/*****************************************************************************
* Copyright (c) 2006, 2007 g-Eclipse Consortium
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Initial development of the original code was made for the
* g-Eclipse project founded by European Union
* project number: FP6-IST-034327 http://www.geclipse.eu/
*
* Contributors:
* Ariel Garcia - initial implementation
*****************************************************************************/
package eu.geclipse.core.util;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.net.URI;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import eu.geclipse.core.internal.Activator;
/**
* This class is an extension of java.io.File providing creation of
* files and folders with secure permissions, if supported by the
* underlying OS! Sadly there is no way to manage file/folder permissions
* in Java <= 5 or in Eclipse itself. This class should go away with
* the use of Java 6...
*
* @author agarcia
* @see java.io.File
*/
public class SecureFile extends File {
private static final String SECURE_PERMISSIONS_FAILED
= "Failed to set secure permissions: "; //$NON-NLS-1$
private static final String WARNING_FILE_NOT_DELETED
= ". Warning: insecure file could not be deleted."; //$NON-NLS-1$
/** Declare some serialVersionUID */
private static final long serialVersionUID = 101010123456789010L;
/**
* Creates a new <code>SecureFile</code> instance by converting the
* given pathname string into an abstract pathname. Never forget to
* actually _create_ the file with one of the create*File() methods,
* or call setSecure() if the file already exists, before writing
* data inside with a FileOutputStream!
*
* @see java.io.File#File(String)
* @param pathname A pathname string
*/
public SecureFile( final String pathname ) {
super( pathname );
}
/**
* Creates a new <code>SecureFile</code> instance from a parent
* pathname string and a child pathname string. Never forget to
* actually _create_ the file with one of the create*File() methods,
* or call setSecure() if the file already exists, before writing
* data inside with a FileOutputStream!
*
* @see java.io.File#File(String, String)
* @param parent The parent abstract pathname
* @param child The child pathname string
*/
public SecureFile( final String parent, final String child ) {
super( parent, child );
}
/**
* Creates a new <code>SecureFile</code> instance from a parent abstract
* pathname and a child pathname string. Never forget to
* actually _create_ the file with one of the create*File() methods,
* or call setSecure() if the file already exists, before writing
* data inside with a FileOutputStream!
*
* @see java.io.File#File(File, String)
* @param parent The parent abstract pathname
* @param child The child pathname string
*/
public SecureFile( final File parent, final String child ) {
super( parent, child );
}
/**
* Creates a new <code>SecureFile</code> instance by converting the
* given URI into an abstract pathname. Never forget to
* actually _create_ the file with one of the create*File() methods,
* or call setSecure() if the file already exists, before writing
* data inside with a FileOutputStream!
*
* @see java.io.File#File(URI)
* @param uri A URI
*/
public SecureFile( final URI uri ) {
super( uri );
}
/**
* Private method for setting the secure permissions
*/
private static boolean protect( final String path ) {
int retVal = 0;
// TODO: implement this for Windows. This is insecure, but keeps
// the current behaviour until it is implemented!
if ( System.getProperty( "os.name" ).contains( "Windows" ) ) { //$NON-NLS-1$ //$NON-NLS-2$
return true;
}
/*
* UNIX specific, using a library call could be more efficient but
* less portable, at least exec'ing chmod should work on all unixes...
*/
String mode = "600"; //$NON-NLS-1$
SecureFile f = new SecureFile( path );
if ( f.isDirectory() ) {
mode = "700"; //$NON-NLS-1$
}
String[] cmd = { "chmod", mode, path }; //$NON-NLS-1$
String[] env = { "PATH=/bin;/usr/bin;/usr/local/bin" }; //$NON-NLS-1$
Runtime rt = Runtime.getRuntime();
Process proc = null;
try {
proc = rt.exec( cmd, env );
} catch ( Exception ex ) {
String msg = SECURE_PERMISSIONS_FAILED
+ "Exception running chmod"; //$NON-NLS-1$
IStatus status = new Status( IStatus.WARNING,
Activator.PLUGIN_ID, IStatus.OK,
msg,
ex );
Activator.logStatus( status );
return false;
}
// Output?
InputStream eStr = proc.getErrorStream();
BufferedReader eB = new BufferedReader( new InputStreamReader( eStr ) );
InputStream oStr = proc.getInputStream();
BufferedReader oB = new BufferedReader( new InputStreamReader( oStr ) );
// Error?
try {
retVal = proc.waitFor();
} catch ( InterruptedException ie ) {
// Something went wrong
retVal = 1;
String msg = SECURE_PERMISSIONS_FAILED
+ "Exception waiting for chmod to finish"; //$NON-NLS-1$
IStatus status = new Status( IStatus.WARNING,
Activator.PLUGIN_ID, IStatus.OK,
msg,
ie );
Activator.logStatus( status );
}
if ( retVal != 0 ) {
String line = null;
String msg = "chmod command returned non-zero value: "; //$NON-NLS-1$
try {
while ( (line = eB.readLine()) != null ) {
msg = msg.concat( line + " -- " ); //$NON-NLS-1$
}
} catch ( IOException ioe ) {
//
}
IStatus status = new Status( IStatus.WARNING,
Activator.PLUGIN_ID,
SECURE_PERMISSIONS_FAILED + msg );
Activator.logStatus( status );
try {
eB.close();
oB.close();
} catch ( IOException ioe ) {
// Nothing we can do now here
}
return false;
}
try {
eB.close();
oB.close();
} catch ( IOException ioe ) {
// Nothing we can do now here
}
return true;
}
/**
* Set secure permissions on the existing file named by this abstract pathname.
*
* @return <code>true</code> if the named file could be set to secure
* permissions; <code>false</code> if the file didn't exist
* @throws IOException if setting the permissions failed
*/
public boolean setSecure() throws IOException {
String filePath = getPath();
boolean ret = protect( filePath );
if ( ret != true ) {
throw new IOException( SECURE_PERMISSIONS_FAILED + filePath );
}
return ret;
}
/**
* Tests whether the file named by this abstract pathname has secure
* permissions.
*
* @return <code>true</code> if the named file exists and has secure
* permissions; <code>false</code> otherwise
*/
public boolean isSecure() {
// String filePath = getPath();
// TODO: implement this. Right now we just return false, so
// setSecure() will be called in any case.
return false;
}
/**
* Atomically creates a new, empty, protected file named by this abstract pathname if
* and only if a file with this name does not yet exist.
*
* @return <code>true</code> if the named file does not exist and was
* successfully created with secure permissions; <code>false</code>
* if the named file already exists
* @throws IOException if setting the permissions failed
*/
@Override
public boolean createNewFile() throws IOException {
if ( super.createNewFile() == false ) {
return false;
}
String filePath = getPath();
boolean ret = protect( filePath );
if ( ret != true ) {
// CreateNewFile succeeded, but setting the secure permissions didn't...
File dfile = new File( filePath );
String msg = ""; //$NON-NLS-1$
if ( ! dfile.delete() ) {
msg = WARNING_FILE_NOT_DELETED;
}
// TODO: check this method's policy regarding return value and
// exception throwing in the superclass...
throw new IOException( SECURE_PERMISSIONS_FAILED + filePath + msg );
}
return ret;
}
/**
* Creates a protected empty file in the default temporary-file directory,
* using the given prefix and suffix to generate its name. Invoking this
* method is equivalent to invoking
* <code>{@link #createTempFile(java.lang.String, java.lang.String, java.io.File)
* createTempFile(prefix, suffix, null)}</code>.
*
* @see java.io.File#createTempFile(String, String)
* @return An abstract pathname denoting a new empty protected file
*/
public static SecureFile createTempFile( final String prefix,
final String suffix )
throws IOException
{
return createTempFile(prefix, suffix, null);
}
/**
* Creates a protected empty file in the specified directory, using the
* given prefix and suffix to generate its name.
*
* @see java.io.File#createTempFile(String, String, File)
* @return An abstract pathname denoting a new empty protected file
*/
public static SecureFile createTempFile( final String prefix,
final String suffix,
final File directory )
throws IOException
{
File file = File.createTempFile(prefix, suffix, directory);
String filePath = file.getPath();
SecureFile secFile = new SecureFile( filePath );
if ( protect( filePath ) != true ) {
throw new IOException( SECURE_PERMISSIONS_FAILED + filePath);
}
return secFile;
}
/**
* Renames the file denoted by this abstract pathname.
*
* @see java.io.File#renameTo(File)
* @return <code>true</code> if and only if the renaming succeeded;
* <code>false</code> otherwise
*/
@Override
public boolean renameTo( final File dest ) {
return super.renameTo( dest ) && protect( getPath() );
}
/**
* Creates the secure directory named by this abstract pathname.
*
* @see java.io.File#mkdir()
* @return <code>true</code> if and only if the directory was
* created with secure permissions; <code>false</code> otherwise
*/
@Override
public boolean mkdir() {
return super.mkdir() && protect( getPath() );
}
/**
* Creates the secure directory named by this abstract pathname,
* including any necessary but nonexistent parent directories.
*
* @see java.io.File#mkdirs()
* @return <code>true</code> if and only if the directory was created
* with secure permissions, along with all necessary parent
* directories; <code>false</code> otherwise
*/
@Override
public boolean mkdirs() {
return super.mkdirs() && protect( getPath() );
}
}