diff options
author | Simon Rettberg | 2014-06-03 16:47:36 +0200 |
---|---|---|
committer | Simon Rettberg | 2014-06-03 16:47:36 +0200 |
commit | 32dc5354e2916387a2c62eadae0a4568023f1151 (patch) | |
tree | 7fd9a0173d6073e86d1d48e545646b1bc8c1a5eb /src/main/java | |
download | tmlite-bwlp-32dc5354e2916387a2c62eadae0a4568023f1151.tar.gz tmlite-bwlp-32dc5354e2916387a2c62eadae0a4568023f1151.tar.xz tmlite-bwlp-32dc5354e2916387a2c62eadae0a4568023f1151.zip |
Initial commit
Diffstat (limited to 'src/main/java')
17 files changed, 1634 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/satserver/util/Archive.java b/src/main/java/org/openslx/satserver/util/Archive.java new file mode 100644 index 0000000..dcc1380 --- /dev/null +++ b/src/main/java/org/openslx/satserver/util/Archive.java @@ -0,0 +1,217 @@ +package org.openslx.satserver.util; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Calendar; +import java.util.GregorianCalendar; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.ArchiveStreamFactory; +import org.apache.commons.compress.archivers.ar.ArArchiveEntry; +import org.apache.commons.compress.archivers.cpio.CpioArchiveEntry; +import org.apache.commons.compress.archivers.dump.DumpArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.compressors.CompressorStreamFactory; +import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; +import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; +import org.apache.commons.io.FileUtils; + +public class Archive +{ + + /** + * Create input stream from given archive file. The archive format will be guessed + * from the firt couple of bytes in the file, which should work for most archive + * formats (see apache commons-compress docs for detailed information) + * + * @param fileName archive to open for reading + * @return ArchiveInputStream stream to read archive from + * @throws FileNotFoundException + * @throws ArchiveException + * @throws IllegalArgumentException + */ + public static ArchiveInputStream getArchiveInputStream( String fileName ) + throws FileNotFoundException, ArchiveException, IllegalArgumentException + { + // Get an input stream for the archive handler + // 1) Get input stream from file + InputStream fileStream = new BufferedInputStream( new FileInputStream( fileName ) ); + // 2) Might be compressed, so try to get a decompressed stream + InputStream archiveStream; + try { + archiveStream = new BufferedInputStream( new CompressorStreamFactory().createCompressorInputStream( fileStream ) ); + } catch ( Exception e ) { + // Might be uncompressed archive, like tar - just try file input stream + archiveStream = fileStream; + } + // 3) Try to create archive input stream - if it succeeds we can read the archive :) + return new ArchiveStreamFactory().createArchiveInputStream( archiveStream ); + } + + /** + * Create tar archive with the given name. Optional compression method is + * determined by the file extension. + * + * @param outputFile name of archive to create; can be relative or absolute path + * @return {@link TarArchiveOutputStream} to write archive entries to + * @throws IOException + */ + @SuppressWarnings( "resource" ) + public static TarArchiveOutputStream createTarArchive( String outputFile ) throws IOException + { + OutputStream outputStream = new BufferedOutputStream( new FileOutputStream( outputFile ) ); + OutputStream compressedOutputStream; + if ( outputFile.endsWith( ".tar.gz" ) || outputFile.endsWith( ".tgz" ) ) { + compressedOutputStream = new BufferedOutputStream( new GzipCompressorOutputStream( outputStream ) ); + } else if ( outputFile.endsWith( ".tar.bz2" ) || outputFile.endsWith( ".tbz" ) ) { + compressedOutputStream = new BufferedOutputStream( new BZip2CompressorOutputStream( outputStream ) ); + } else if ( outputFile.endsWith( ".tar.xz" ) || outputFile.endsWith( ".txz" ) ) { + compressedOutputStream = new BufferedOutputStream( new XZCompressorOutputStream( outputStream ) ); + } else if ( outputFile.endsWith( ".tar.Z" ) || outputFile.endsWith( ".tar.z" ) || outputFile.endsWith( ".tz" ) ) { + compressedOutputStream = new BufferedOutputStream( new XZCompressorOutputStream( outputStream ) ); + } else if ( outputFile.endsWith( ".tar.lzma" ) ) { + compressedOutputStream = new BufferedOutputStream( new XZCompressorOutputStream( outputStream ) ); + } else if ( outputFile.endsWith( ".tar" ) ) { + compressedOutputStream = outputStream; + } else { + outputStream.close(); + return null; + } + try { + return new TarArchiveOutputStream( compressedOutputStream ); + } catch ( Throwable t ) { + compressedOutputStream.close(); + return null; + } + } + + public static TarArchiveEntry createTarArchiveEntry( ArchiveEntry inEntry, int defaultUser, int defaultGroup, int defaultDirMode, int defaultFileMode ) + { + if ( inEntry instanceof TarArchiveEntry ) { + // Source is tar - easy + return (TarArchiveEntry)inEntry; + } + final TarArchiveEntry outEntry = new TarArchiveEntry( inEntry.getName() ); + outEntry.setSize( inEntry.getSize() ); + if ( inEntry instanceof ArArchiveEntry ) { + // Source is ar - has most of the stuff tar supports; transform + outEntry.setIds( ( (ArArchiveEntry)inEntry ).getUserId(), ( (ArArchiveEntry)inEntry ).getGroupId() ); + outEntry.setMode( ( (ArArchiveEntry)inEntry ).getMode() ); + outEntry.setModTime( ( (ArArchiveEntry)inEntry ).getLastModifiedDate() ); + } else if ( inEntry instanceof DumpArchiveEntry ) { + // Source is dump (whatever the fuck that is) - has most of the stuff tar supports; transform + outEntry.setIds( ( (DumpArchiveEntry)inEntry ).getUserId(), ( (DumpArchiveEntry)inEntry ).getGroupId() ); + outEntry.setMode( ( (DumpArchiveEntry)inEntry ).getMode() ); + outEntry.setModTime( ( (DumpArchiveEntry)inEntry ).getLastModifiedDate() ); + } else if ( inEntry instanceof CpioArchiveEntry ) { + // Source is cpio - has most of the stuff tar supports; transform + outEntry.setIds( (int) ( (CpioArchiveEntry)inEntry ).getUID(), (int) ( (CpioArchiveEntry)inEntry ).getGID() ); + outEntry.setMode( (int) ( ( (CpioArchiveEntry)inEntry ).getMode() & 07777 ) ); + outEntry.setModTime( ( (CpioArchiveEntry)inEntry ).getLastModifiedDate() ); + } else if ( inEntry instanceof ZipArchiveEntry ) { + // Source is ZIP - might not have unix stuff + outEntry.setIds( defaultUser, defaultGroup ); + int mode = ( (ZipArchiveEntry)inEntry ).getUnixMode(); + if ( mode != 0 ) { // Has unix permissions + outEntry.setMode( mode ); + } else if ( inEntry.isDirectory() ) { // Use default passed in + outEntry.setMode( defaultDirMode ); + } else { // Use default passed in + outEntry.setMode( defaultFileMode ); + } + outEntry.setModTime( ( (ZipArchiveEntry)inEntry ).getLastModifiedDate() ); + } else { + outEntry.setIds( defaultUser, defaultGroup ); + if ( inEntry.isDirectory() ) { + outEntry.setMode( defaultDirMode ); + } else { + outEntry.setMode( defaultFileMode ); + Calendar cal = GregorianCalendar.getInstance(); + cal.set( 2014, 4, 2, 13, 57 ); + outEntry.setModTime( cal.getTime() ); + } + } + return outEntry; + } + + public static boolean tarAddFile( TarArchiveOutputStream tar, String inArchiveFileName, File sourceFile, int mode ) + { + if ( !sourceFile.exists() || !sourceFile.isFile() ) + return false; + InputStream in; + try { + in = new FileInputStream( sourceFile ); + } catch ( Exception e ) { + e.printStackTrace(); + return false; + } + TarArchiveEntry entry = new TarArchiveEntry( inArchiveFileName ); + long size = FileUtils.sizeOf( sourceFile ); + entry.setIds( 0, 0 ); + entry.setMode( mode ); + entry.setSize( size ); + try { + tar.putArchiveEntry( entry ); + Util.streamCopy( in, tar, size ); + tar.closeArchiveEntry(); + } catch ( IOException e ) { + e.printStackTrace(); + return false; + } finally { + Util.multiClose( in ); + } + return true; + } + + public static boolean tarCreateFileFromString( TarArchiveOutputStream tar, String fileName, String contents, int mode ) + { + return tarCreateFileFromString( tar, fileName, contents.getBytes( StandardCharsets.UTF_8 ), mode ); + } + + public static boolean tarCreateFileFromString( TarArchiveOutputStream tar, String fileName, byte[] contents, int mode ) + { + TarArchiveEntry entry = new TarArchiveEntry( fileName ); + entry.setIds( 0, 0 ); + entry.setMode( mode ); + entry.setSize( contents.length ); + try { + tar.putArchiveEntry( entry ); + tar.write( contents ); + tar.closeArchiveEntry(); + } catch ( IOException e ) { + e.printStackTrace(); + return false; + } + return true; + } + + public static boolean tarCreateSymlink( TarArchiveOutputStream tar, String target, String linkName ) + { + TarArchiveEntry entry = new TarArchiveEntry( linkName, TarArchiveEntry.LF_SYMLINK ); + entry.setIds( 0, 0 ); + entry.setMode( 0777 ); + entry.setLinkName( target ); + try { + tar.putArchiveEntry( entry ); + tar.closeArchiveEntry(); + } catch ( IOException e ) { + e.printStackTrace(); + return false; + } + return true; + } + +} diff --git a/src/main/java/org/openslx/satserver/util/Constants.java b/src/main/java/org/openslx/satserver/util/Constants.java new file mode 100644 index 0000000..b8394f0 --- /dev/null +++ b/src/main/java/org/openslx/satserver/util/Constants.java @@ -0,0 +1,8 @@ +package org.openslx.satserver.util; + +public class Constants +{ + + public static final String BASEDIR = System.getProperty( "user.dir" ); + +} diff --git a/src/main/java/org/openslx/satserver/util/Util.java b/src/main/java/org/openslx/satserver/util/Util.java new file mode 100644 index 0000000..a6d354a --- /dev/null +++ b/src/main/java/org/openslx/satserver/util/Util.java @@ -0,0 +1,66 @@ +package org.openslx.satserver.util; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class Util +{ + + /** + * Check if the given string starts with any of the additionally passed strings. + * Ugly boilerplate. + * + * @param stringToCheck a string we want to check the starting of + * @param compareTo list of strings we compare stringToCheck to + * @return true if stringToCheck starts with any of the strings in compareTo + */ + public static boolean startsWith( String stringToCheck, String... compareTo ) + { + for ( String check : compareTo ) { + if ( stringToCheck.startsWith( check ) ) + return true; + } + return false; + } + + /** + * Close all given Closables. Can handle null references. + * @param streams one or more closables/streams + */ + public static void multiClose( Closeable... streams ) + { + if ( streams == null ) + return; + for ( Closeable stream : streams ) { + if ( stream != null ) { + try { + stream.close(); + } catch ( IOException e ) { + // Ignore - nothing meaningful to do + } + } + } + } + + public static boolean streamCopy( InputStream in, OutputStream out, long bytes ) + { + byte buffer[] = new byte[ 7900 ]; + while ( bytes > 0 ) { + try { + int ret = in.read( buffer, 0, (int) ( bytes > buffer.length ? buffer.length : bytes ) ); + if ( ret == -1 ) + return false; + bytes -= ret; + out.write( buffer, 0, ret ); + } catch ( IOException e ) { + e.printStackTrace(); + return false; + } + } + return true; + } + + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java new file mode 100644 index 0000000..c0534c7 --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/CompileIPxe.java @@ -0,0 +1,83 @@ +package org.openslx.taskmanager.tasks; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.FileUtils; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class CompileIPxe extends AbstractTask +{ + + @Expose + private String ip = null; + @Expose + private String defaultentry = null; + @Expose + private int timeout = 0; + @Expose + private String custom = null; + + private Output status = new Output(); + + @Override + protected boolean initTask() + { + this.setStatusObject( this.status ); + if ( this.ip == null ) { + status.error = "No IP address set."; + return false; + } + if ( this.defaultentry == null ) + this.defaultentry = "net"; + if ( this.custom == null ) + this.custom = ""; + return true; + } + + @Override + protected boolean execute() + { + // Prepare menu + String template; + try { + template = FileUtils.readFileToString( new File( "./data/pxemenu.template" ), StandardCharsets.UTF_8 ); + } catch ( IOException e ) { + status.error = e.toString(); + return false; + } + // Substitutions + template = template.replaceAll( "%ip%", this.ip ); + template = template.replaceAll( "%timeout%", Integer.toString( this.timeout * 10 ) ); + template = template.replaceAll( "%totaltimeout%", Integer.toString( this.timeout * 40 ) ); + template = template.replaceAll( "%default%", this.defaultentry ); + template = template.replaceAll( "%custom%", this.custom ); + // Default selection net + if ( this.defaultentry.equals( "net" ) ) + template = template.replaceAll( "%default-net%", "MENU DEFAULT" ); + else + template = template.replaceAll( "%default-net%", "" ); + // Default selection hdd + if ( this.defaultentry.equals( "hdd" ) ) + template = template.replaceAll( "%default-hdd%", "MENU DEFAULT" ); + else + template = template.replaceAll( "%default-hdd%", "" ); + // Write out + try { + FileUtils.writeStringToFile( new File( "/srv/openslx/tftp/pxelinux.cfg/default" ), template, StandardCharsets.UTF_8 ); + } catch ( IOException e ) { + status.error = e.toString(); + return false; + } + return true; + } + + class Output + { + protected String error = null; + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/CreateAdConfig.java b/src/main/java/org/openslx/taskmanager/tasks/CreateAdConfig.java new file mode 100644 index 0000000..71ef249 --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/CreateAdConfig.java @@ -0,0 +1,161 @@ +package org.openslx.taskmanager.tasks; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.io.FileUtils; +import org.openslx.satserver.util.Archive; +import org.openslx.satserver.util.Util; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class CreateAdConfig extends AbstractTask +{ + @Expose + private int moduleid = 0; + @Expose + private String filename = null; + @Expose + private String server = null; + @Expose + private String searchbase = null; + @Expose + private String binddn = null; + @Expose + private String bindpw = null; + @Expose + private String proxyip = null; + @Expose + private int proxyport = 0; + @Expose + private String home = null; + + private Output status = new Output(); + + @Override + protected boolean initTask() + { + // TODO: Check path is allowed + this.setStatusObject( this.status ); + if ( filename == null || server == null || searchbase == null || binddn == null || bindpw == null || proxyip == null || proxyport == 0 || moduleid == 0 ) { + status.error = "Missing argument to task"; + return false; + } + if ( this.home == null ) + this.home = ""; + return true; + } + + @Override + protected boolean execute() + { + TarArchiveOutputStream outArchive = null; + try { + // ldadp config + String ldadpConf = String.format( + "[%s]\n" + + "binddn=%s\n" + + "bindpw=%s\n" + + "base=%s\n" + + "port=%s\n" + + "home=%s\n" + + "\n", + this.server, + this.binddn, + this.bindpw, + this.searchbase, + this.proxyport, + this.home ); + String fileName = "/opt/ldadp/configs/" + this.moduleid + ".cfg"; + try { + Files.deleteIfExists( Paths.get( filename ) ); + } catch ( IOException e1 ) { + } + try { + FileUtils.writeStringToFile( new File( fileName ), ldadpConf, StandardCharsets.UTF_8 ); + } catch ( IOException e ) { + status.error = e.toString(); + return false; + } + try { + outArchive = Archive.createTarArchive( this.filename ); + } catch ( IOException e ) { + status.error = "Could not create archive at " + this.filename; + return false; + } + // Generic ldap config + String ldapConf = String + .format( + "URI ldap://%s:%d/\n" + + "BASE %s\n" + + "BIND_TIMELIMIT 10\n" + + "TIMELIMIT 30\n" + + "nss_base_passwd %s\n" + + "nss_base_group %s\n" + + "nss_initgroups_ignoreusers avahi,avahi-autoipd,backup,bin,colord,daemon,dnsmasq,games,gnats,hplip,irc,kernoops,libuuid,lightdm,list,lp,mail,man,messagebus,news,proxy,pulse,root,rtkit,saned,speech-dispatcher,sshd,sync,sys,syslog,usbmux,uucp,whoopsie,www-data\n", + this.proxyip, this.proxyport, + this.searchbase, + this.searchbase, + this.searchbase + ); + // nslcd config + String nslcdConf = String + .format( + "URI ldap://%s:%d/\n" + + "BASE %s\n" + + "BIND_TIMELIMIT 10\n" + + "TIMELIMIT 30\n" + + "scope sub\n" + + "nss_initgroups_ignoreusers avahi,avahi-autoipd,backup,bin,colord,daemon,dnsmasq,games,gnats,hplip,irc,kernoops,libuuid,lightdm,list,lp,mail,man,messagebus,news,proxy,pulse,root,rtkit,saned,speech-dispatcher,sshd,sync,sys,syslog,usbmux,uucp,whoopsie,www-data\n", + this.proxyip, this.proxyport, + this.searchbase, + this.searchbase, + this.searchbase + ); + // nsswitch.conf with ldap enabled + if ( !Archive.tarAddFile( outArchive, "/etc/nsswitch.conf", new File( "./data/ad/nsswitch.conf" ), 0644 ) ) { + status.error = "Could not add nsswitch.conf to module"; + return false; + } + // All the pam.d common-XXXX files + for ( String file : new String[] { "common-auth", "common-account", "common-session", "common-session-noninteractive", "common-password" } ) { + if ( !Archive.tarAddFile( outArchive, "/etc/pam.d/" + file, new File( "./data/ad/" + file ), 0644 ) ) { + status.error = "Could not add " + file + " to module"; + return false; + } + } + // Home if present + if ( this.home.length() != 0 + && !Archive.tarAddFile( outArchive, "/opt/openslx/scripts/pam_script_mount_persistent", new File( "./data/ad/mountscript" ), 0644 ) ) { + status.error = "Could not add mount script to module"; + return false; + } + boolean ret = Archive.tarCreateFileFromString( outArchive, "/etc/ldap.conf", ldapConf, 0644 ) + && Archive.tarCreateFileFromString( outArchive, "/etc/nslcd.conf", nslcdConf, 0644 ) + && Archive.tarCreateSymlink( outArchive, "/etc/ldap.conf", "/etc/ldap/ldap.conf" ) + && Archive.tarCreateSymlink( outArchive, "/etc/ldap.conf", "/etc/openldap/ldap.conf" ) + && Archive.tarCreateSymlink( outArchive, "../nslcd.service", "/etc/systemd/system/basic.target.wants/nslcd.service" ); + if ( !ret ) { + status.error = "Could not add ldap configs to module"; + } + return ret; + } finally { + Util.multiClose( outArchive ); + } + } + + /** + * Output - contains additional status data of this task + */ + @SuppressWarnings( "unused" ) + private static class Output + { + protected String error = null; + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/DeleteFile.java b/src/main/java/org/openslx/taskmanager/tasks/DeleteFile.java new file mode 100644 index 0000000..8c0f9a1 --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/DeleteFile.java @@ -0,0 +1,57 @@ +package org.openslx.taskmanager.tasks; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.apache.commons.io.FilenameUtils; +import org.openslx.satserver.util.Util; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class DeleteFile extends AbstractTask +{ + + protected static final String[] ALLOWED_DIRS = + { "/tmp/", "/opt/openslx/configs/" }; + + @Expose + private String file = null; + + private Output status = new Output(); + + @Override + protected boolean initTask() + { + this.setStatusObject( status ); + this.file = FilenameUtils.normalize( this.file ); + if ( this.file == null || !Util.startsWith( this.file, ALLOWED_DIRS ) ) { + status.error = "File not in allowed directory"; + return false; + } + return true; + } + + @Override + protected boolean execute() + { + try { + Files.deleteIfExists( Paths.get( this.file ) ); + } catch ( IOException e1 ) { + status.error = e1.toString(); + return false; + } + return true; + } + + /** + * Output - contains additional status data of this task + */ + @SuppressWarnings( "unused" ) + private static class Output + { + protected String error = null; + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/DownloadFile.java b/src/main/java/org/openslx/taskmanager/tasks/DownloadFile.java new file mode 100644 index 0000000..d901c9f --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/DownloadFile.java @@ -0,0 +1,91 @@ +package org.openslx.taskmanager.tasks; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.openslx.satserver.util.Util; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class DownloadFile extends AbstractTask +{ + + @Expose + private String url = null; + @Expose + private String destination = null; + + private Output status = new Output(); + + private static final String[] ALLOWED_DIRS = + { "/srv/openslx/www/boot/" }; + + @Override + protected boolean initTask() + { + this.setStatusObject( status ); + if ( this.url == null ) { + status.error = "No URL given."; + return false; + } + this.destination = FilenameUtils.normalize( this.destination ); + if ( this.destination == null || !Util.startsWith( this.destination, ALLOWED_DIRS ) || this.destination.endsWith( "/" ) ) { + status.error = "File not in allowed directory"; + return false; + } + return true; + } + + @Override + protected boolean execute() + { + URLConnection connection = null; + BufferedInputStream in = null; + FileOutputStream fout = null; + try { + File dest = new File( this.destination ); + FileUtils.forceMkdir( new File( dest.getParent() ) ); + FileUtils.deleteQuietly( dest ); + connection = new URL( this.url ).openConnection(); + in = new BufferedInputStream( connection.getInputStream() ); + fout = new FileOutputStream( dest ); + status.size = connection.getContentLengthLong(); + + final byte data[] = new byte[ 9000 ]; + int count; + while ( ( count = in.read( data, 0, data.length ) ) != -1 ) { + fout.write( data, 0, count ); + status.complete += count; + if ( status.size > 0 ) + status.progress = (int) ( 100l * status.complete / status.size ); + } + return true; + } catch ( IOException e ) { + status.error = "Download error: " + e.toString(); + FileUtils.deleteQuietly( new File( this.destination ) ); + return false; + } finally { + Util.multiClose( in, fout ); + } + } + + /** + * Output - contains additional status data of this task + */ + @SuppressWarnings( "unused" ) + private static class Output + { + protected String error = null; + protected long size = -1; + protected long complete = 0; + protected int progress = 0; + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/DownloadText.java b/src/main/java/org/openslx/taskmanager/tasks/DownloadText.java new file mode 100644 index 0000000..11b30cf --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/DownloadText.java @@ -0,0 +1,82 @@ +package org.openslx.taskmanager.tasks; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; + +import org.openslx.satserver.util.Util; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class DownloadText extends AbstractTask +{ + + @Expose + private String url = null; + + private Output status = new Output(); + + private static final long MAX_SIZE = 10000; + + @Override + protected boolean initTask() + { + this.setStatusObject( status ); + if ( this.url == null ) { + status.error = "No URL given."; + return false; + } + return true; + } + + @Override + protected boolean execute() + { + URLConnection connection = null; + BufferedInputStream in = null; + try { + connection = new URL( this.url ).openConnection(); + in = new BufferedInputStream( connection.getInputStream() ); + status.size = connection.getContentLength(); + if ( status.size > MAX_SIZE ) { + status.error = "Remote file too large: " + status.size + " bytes!"; + return false; + } + StringBuilder sb = new StringBuilder( Math.max( 8, status.size ) ); + + final byte data[] = new byte[ 9000 ]; + int count; + while ( ( count = in.read( data, 0, data.length ) ) != -1 ) { + sb.append( new String( data, 0, count, StandardCharsets.UTF_8 ) ); + status.complete += count; + if ( status.complete > MAX_SIZE ) { + status.error = "Remote file too large: > " + status.size + " bytes!"; + return false; + } + } + status.content = sb.toString(); + return true; + } catch ( IOException e ) { + status.error = "Download error: " + e.toString(); + return false; + } finally { + Util.multiClose( in ); + } + } + + /** + * Output - contains additional status data of this task + */ + @SuppressWarnings( "unused" ) + private static class Output + { + protected String error = null; + protected String content = null; + protected int size = -1; + protected int complete = 0; + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/DummyTask.java b/src/main/java/org/openslx/taskmanager/tasks/DummyTask.java new file mode 100644 index 0000000..9524f2f --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/DummyTask.java @@ -0,0 +1,58 @@ +package org.openslx.taskmanager.tasks; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; + +import org.apache.log4j.Logger; +import org.openslx.taskmanager.api.AbstractTask; + +public class DummyTask extends AbstractTask +{ + + private static final Logger log = Logger.getLogger( DummyTask.class ); + + private DummyTaskStatus status = new DummyTaskStatus(); + + @Override + protected boolean initTask() + { + log.debug( "Initialized DummyTask with ID " + this.getId() ); + return true; + } + + @Override + protected boolean execute() + { + setStatusObject( status ); + Process process = null; + try { + // Create process + process = Runtime.getRuntime().exec( "dummy", null, new File("/") ); + + // Read its output + BufferedReader reader = new BufferedReader( new InputStreamReader( process.getInputStream() ) ); + String line; + while ( ( line = reader.readLine() ) != null ) { + line = reader.readLine(); + if ( line.matches( "^\\d+%$" ) ) + this.status.progress = Integer.parseInt( line.substring( 0, line.length() - 1 ) ); + } + } catch ( IOException e ) { + log.warn( "Process of task " + this.getId() + " died." ); + } finally { + if ( process != null ) + process.destroy(); + } + return this.status.progress == 100; + } + + class DummyTaskStatus + { + + protected int progress = 0; + + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/LdadpLauncher.java b/src/main/java/org/openslx/taskmanager/tasks/LdadpLauncher.java new file mode 100644 index 0000000..123ab1a --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/LdadpLauncher.java @@ -0,0 +1,79 @@ +package org.openslx.taskmanager.tasks; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.openslx.satserver.util.Constants; +import org.openslx.taskmanager.api.SystemCommandTask; + +import com.google.gson.annotations.Expose; + +public class LdadpLauncher extends SystemCommandTask +{ + @Expose + private int[] ids = null; + + private Output status = new Output(); + + @Override + protected boolean initTask() + { + this.setStatusObject( this.status ); + if ( ids == null ) { + status.addMessage( "No ids passed to task." ); + return false; + } + return true; + } + + @Override + protected String[] initCommandLine() + { + List<String> args = new ArrayList<>(); + args.addAll( Arrays.asList( new String[] { + "/usr/bin/sudo", + "-n", + "-u", "ldadp", Constants.BASEDIR + "/scripts/ldadp-launcher", + "/opt/ldadp", + "--" + } ) ); + for ( int i = 0; i < ids.length; ++i ) { + args.add( Integer.toString( this.ids[i] ) ); + } + return args.toArray( new String[ args.size() ] ); + } + + @Override + protected boolean processEnded( int exitCode ) + { + return exitCode == 0; + } + + @Override + protected void processStdOut( String line ) + { + status.addMessage( line ); + } + + @Override + protected void processStdErr( String line ) + { + status.addMessage( line ); + } + + class Output + { + private String messages = null; + + private void addMessage( String str ) + { + if ( messages == null ) { + messages = str; + } else { + messages += "\n" + str; + } + } + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/LdapSearch.java b/src/main/java/org/openslx/taskmanager/tasks/LdapSearch.java new file mode 100644 index 0000000..f861b6a --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/LdapSearch.java @@ -0,0 +1,122 @@ +package org.openslx.taskmanager.tasks; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Random; + +import org.apache.commons.io.FileUtils; +import org.openslx.taskmanager.api.SystemCommandTask; + +import com.google.gson.annotations.Expose; + +public class LdapSearch extends SystemCommandTask +{ + + @Expose + private String server = null; + @Expose + private String searchbase = null; + @Expose + private String binddn = null; + @Expose + private String bindpw = null; + + private String fifo = null; + + private volatile int userCount = 0; + + private Output status = new Output(); + + @Override + protected boolean initTask() + { + this.setStatusObject( this.status ); + if ( this.server == null || this.searchbase == null || this.binddn == null ) { + status.messages = "Missing parameter"; + return false; + } + return true; + } + + @Override + protected String[] initCommandLine() + { + if ( this.bindpw == null ) + this.bindpw = ""; + this.fifo = String.format( "/tmp/bwlp-%s-%s.ldap", System.currentTimeMillis(), new Random().nextInt() ); + File pwFile = new File( this.fifo ); + FileUtils.deleteQuietly( pwFile ); + try { + pwFile.createNewFile(); + pwFile.setReadable( false, false ); + pwFile.setReadable( true, true ); + FileUtils.writeStringToFile( pwFile, this.bindpw, StandardCharsets.UTF_8 ); + } catch ( IOException e ) { + FileUtils.deleteQuietly( pwFile ); + status.messages = e.toString(); + return null; + } + status.addMessage( "Trying to find 4 random AD users to verify everything is all right..." ); + + return new String[] { + "ldapsearch", + "-x", // Simple auth + "-LLL", // No additional stuff + "-y", this.fifo, // Password from file + "-H", "ldap://" + this.server + ":3268/", // Host + "-b", this.searchbase, // SB + "-D", this.binddn, // DN + "-l", "4", // Time limit in seconds + "-z", "4", // Max number of results + "-o", "ldif-wrap=no", // Turn off retarded line wrapping done by ldapsearch + "(&(objectClass=user)(objectClass=person)(sAMAccountName=*))", + "sAMAccountName" // Only one attribute + }; + } + + @Override + protected boolean processEnded( int exitCode ) + { + FileUtils.deleteQuietly( new File( this.fifo ) ); + if ( exitCode == 4 ) // Means size limit exceeded, ignore + exitCode = 0; + if ( exitCode != 0 ) + status.addMessage( "Exit code is " + exitCode ); + if ( exitCode == 0 && this.userCount < 4 ) + status.addMessage( "Found less than 4 users. Are you sure you got the right credentials." ); + return this.userCount >= 4; + } + + @Override + protected void processStdOut( String line ) + { + if ( line.contains( "sAMAccountName: " ) ) { + status.addMessage( "Found AD user " + line.substring( 16 ) + " :-)" ); + this.userCount++; + } + } + + @Override + protected void processStdErr( String line ) + { + if ( line.contains( "Size limit exceeded" ) ) + return; + status.addMessage( "Error: " + line ); + } + + class Output + { + private String messages = null; + + private void addMessage( String str ) + { + if ( messages == null ) { + messages = str; + } else { + messages += "\n" + str; + } + } + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/LinkConfigTgz.java b/src/main/java/org/openslx/taskmanager/tasks/LinkConfigTgz.java new file mode 100644 index 0000000..686cb9b --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/LinkConfigTgz.java @@ -0,0 +1,61 @@ +package org.openslx.taskmanager.tasks; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.apache.commons.io.FilenameUtils; +import org.openslx.satserver.util.Util; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class LinkConfigTgz extends AbstractTask +{ + + protected static final String[] ALLOWED_DIRS = + { "/tmp/", "/opt/openslx/configs/" }; + + @Expose + private String destination = null; + + private Output status = new Output(); + + @Override + protected boolean initTask() + { + this.setStatusObject( status ); + this.destination = FilenameUtils.normalize( this.destination ); + if ( this.destination == null || !Util.startsWith( this.destination, ALLOWED_DIRS ) ) { + status.error = "File not in allowed directory"; + return false; + } + return true; + } + + @Override + protected boolean execute() + { + try { + Files.deleteIfExists( Paths.get( "/srv/openslx/www/boot/default/config.tgz" ) ); + } catch ( IOException e1 ) { + } + try { + Files.createSymbolicLink( Paths.get( "/srv/openslx/www/boot/default/config.tgz" ), Paths.get( this.destination ) ); + } catch ( IOException e ) { + status.error = e.toString(); + return false; + } + return true; + } + + /** + * Output - contains additional status data of this task + */ + @SuppressWarnings( "unused" ) + private static class Output + { + protected String error = null; + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/ListArchive.java b/src/main/java/org/openslx/taskmanager/tasks/ListArchive.java new file mode 100644 index 0000000..33bb83a --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/ListArchive.java @@ -0,0 +1,116 @@ +package org.openslx.taskmanager.tasks; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.io.FilenameUtils; +import org.openslx.satserver.util.Archive; +import org.openslx.satserver.util.Util; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class ListArchive extends AbstractTask +{ + + @Expose + private String file; + + /* + * Own vars/constants not being deserialized + */ + + protected static final String[] ALLOWED_DIRS = + { "/tmp/", "/opt/openslx/" }; + + private Output status = new Output(); + + /* + * Code + */ + + @Override + protected boolean execute() + { + ArchiveInputStream archive; + try { + archive = Archive.getArchiveInputStream( this.file ); + } catch ( FileNotFoundException e1 ) { + status.error = e1.getMessage(); + status.errorCode = Output.ErrorCode.NOT_FOUND; + return false; + } catch ( IllegalArgumentException e1 ) { + status.error = e1.getMessage(); + status.errorCode = Output.ErrorCode.UNKNOWN_ERROR; + return false; + } catch ( ArchiveException e1 ) { + status.error = e1.getMessage(); + status.errorCode = Output.ErrorCode.UNKNOWN_FORMAT; + return false; + } + + status.entries = new ArrayList<>(); + + ArchiveEntry archiveEntry; + try { + // Iterate over every entry + while ( ( archiveEntry = archive.getNextEntry() ) != null ) { + if ( !archive.canReadEntryData( archiveEntry ) ) + continue; + Output.Entry entry = new Output.Entry(); + entry.name = archiveEntry.getName(); + entry.isdir = archiveEntry.isDirectory(); + entry.size = archiveEntry.getSize(); + status.entries.add( entry ); + } + } catch ( IOException e ) { + return false; + } + return true; + } + + @Override + protected boolean initTask() + { + this.setStatusObject( this.status ); + if ( this.file == null || this.file.length() == 0 || this.file.charAt( 0 ) != '/' ) { + status.error = "Need absolute path."; + status.errorCode = Output.ErrorCode.INVALID_DIRECTORY; + return false; + } + this.file = FilenameUtils.normalize( this.file ); + if ( this.file == null || !Util.startsWith( this.file, ALLOWED_DIRS ) ) { + status.error = "File not in allowed directory"; + status.errorCode = Output.ErrorCode.INVALID_DIRECTORY; + return false; + } + return true; + } + + @SuppressWarnings( "unused" ) + private static class Output + { + protected String error = null; + + protected enum ErrorCode + { + NOT_FOUND, UNKNOWN_FORMAT, UNKNOWN_ERROR, INVALID_DIRECTORY + }; + + protected ErrorCode errorCode = null; + protected List<Entry> entries = null; + + public static class Entry + { + protected String name = null; + protected boolean isdir = false; + protected long size = -1; + } + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/LocalAddressesList.java b/src/main/java/org/openslx/taskmanager/tasks/LocalAddressesList.java new file mode 100644 index 0000000..2bfb20e --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/LocalAddressesList.java @@ -0,0 +1,76 @@ +package org.openslx.taskmanager.tasks; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import org.apache.log4j.Logger; +import org.openslx.taskmanager.api.AbstractTask; + +public class LocalAddressesList extends AbstractTask +{ + private static final Logger LOG = Logger.getLogger( LocalAddressesList.class ); + + private Output status = new Output(); + + @Override + protected boolean initTask() + { + return true; + } + + @Override + protected boolean execute() + { + this.setStatusObject( status ); + List<Output.Entry> list = new ArrayList<>(); + try { + for ( Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { + NetworkInterface intf = en.nextElement(); + for ( Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) { + InetAddress addr = enumIpAddr.nextElement(); + Output.Entry entry = new Output.Entry(); + entry.iface = intf.getName(); + entry.ip = addr.getHostAddress(); + if ( addr instanceof Inet4Address ) { + entry.type = "ipv4"; + } else if ( addr instanceof Inet6Address ) { + entry.type = "ipv6"; + } else { + entry.type = addr.getClass().getSimpleName(); + } + list.add( entry ); + } + } + } catch ( SocketException e ) { + LOG.info( " (error retrieving network interface list)" ); + this.status.error = "Error retrieving network interface list (" + e.getMessage() + ")"; + return false; + } + this.status.addresses = list; + return true; + } + + /** + * Output - contains additional status data of this task + */ + @SuppressWarnings( "unused" ) + private static class Output + { + protected String error = null; + protected List<Entry> addresses = null; + + protected static class Entry + { + protected String ip = null; + protected String iface = null; + protected String type = null; + } + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/LsTask.java b/src/main/java/org/openslx/taskmanager/tasks/LsTask.java new file mode 100644 index 0000000..2e01167 --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/LsTask.java @@ -0,0 +1,80 @@ +package org.openslx.taskmanager.tasks; + +import java.util.ArrayList; +import java.util.List; + +import org.openslx.taskmanager.api.SystemCommandTask; + +import com.google.gson.annotations.Expose; + +public class LsTask extends SystemCommandTask +{ + + /* + * Serialize + */ + @Expose + private String directory = null; + + /* + * Private, no serialization + */ + private Output status = new Output(); + private List<String> files = new ArrayList<>(); + + @Override + protected boolean initTask() + { + if ( directory == null ) { + status.error ="Missing directory argument"; + return false; + } + return true; + } + + @Override + protected String[] initCommandLine() + { + return new String[] { "/bin/ls", directory }; + } + + @Override + protected void processStarted() + { + setStatusObject( status ); + } + + @Override + protected boolean processEnded( int exitCode ) + { + if ( exitCode != 0 ) { + status.files = null; + return false; + } + status.files = this.files; + return true; + } + + @Override + protected void processStdOut( String line ) + { + this.files.add( line ); + } + + @Override + protected void processStdErr( String line ) + { + status.error = line; + } + + /** + * Output - contains additional status data of this task + */ + @SuppressWarnings( "unused" ) + private static class Output + { + protected String error = null; + protected List<String> files = null; + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/MountVmStore.java b/src/main/java/org/openslx/taskmanager/tasks/MountVmStore.java new file mode 100644 index 0000000..a77387d --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/MountVmStore.java @@ -0,0 +1,84 @@ +package org.openslx.taskmanager.tasks; + +import org.openslx.satserver.util.Constants; +import org.openslx.taskmanager.api.SystemCommandTask; + +import com.google.gson.annotations.Expose; + +public class MountVmStore extends SystemCommandTask +{ + @Expose + private String address = null; + @Expose + private String type = null; + @Expose + private String username = null; + @Expose + private String password = null; + + private Output status = new Output(); + + @Override + protected boolean initTask() + { + this.setStatusObject( this.status ); + if ( this.address == null || this.type == null ) { + status.addMessage( "Address or type not given." ); + return false; + } + if ( this.username == null ) + this.username = ""; + if ( this.password == null ) + this.password = ""; + return true; + } + + @Override + protected String[] initCommandLine() + { + return new String[] { + "/usr/bin/sudo", + "-n", + "-u", "root", Constants.BASEDIR + "/scripts/mount-store", + "images", + this.address, + this.username, + this.password + }; + } + + @Override + protected boolean processEnded( int exitCode ) + { + if ( exitCode != 0 ) + status.addMessage( "Failed with exit code " + exitCode ); + return exitCode == 0; + } + + @Override + protected void processStdOut( String line ) + { + status.addMessage( line ); + } + + @Override + protected void processStdErr( String line ) + { + status.addMessage( line ); + } + + class Output + { + private String messages = null; + + private void addMessage( String str ) + { + if ( messages == null ) { + messages = str; + } else { + messages += "\n" + str; + } + } + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/RecompressArchive.java b/src/main/java/org/openslx/taskmanager/tasks/RecompressArchive.java new file mode 100644 index 0000000..d428f03 --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/RecompressArchive.java @@ -0,0 +1,193 @@ +package org.openslx.taskmanager.tasks; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.openslx.satserver.util.Archive; +import org.openslx.satserver.util.Util; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class RecompressArchive extends AbstractTask +{ + + @Expose + private String[] inputFiles; + + @Expose + private String outputFile; + + /* + * Own vars/constants not being deserialized + */ + + protected static final String[] ALLOWED_DIRS = + { "/tmp/", "/opt/openslx/configs/" }; + + private Output status = new Output(); + + /* + * Code + */ + + @Override + protected boolean execute() + { + if ( execute2() ) { + return true; + } + FileUtils.deleteQuietly( new File( this.outputFile ) ); + return false; + } + + private boolean execute2() + { + // Open output file archive + TarArchiveOutputStream outArchive = null; + try { + try { + FileUtils.forceMkdir( new File( new File( this.outputFile ).getParent() ) ); + outArchive = Archive.createTarArchive( this.outputFile ); + } catch ( IOException e2 ) { + status.error = e2.getMessage(); + status.errorCode = Output.ErrorCode.WRITE_FAILED; + return false; + } + + // Open input file archives, one by one + for ( final String inputFile : this.inputFiles ) { + ArchiveInputStream inArchive = null; + try { + try { + inArchive = Archive.getArchiveInputStream( inputFile ); + } catch ( FileNotFoundException e1 ) { + status.error = "(" + inputFile + ") " + e1.getMessage(); + status.errorCode = Output.ErrorCode.NOT_FOUND; + return false; + } catch ( IllegalArgumentException e1 ) { + status.error = "(" + inputFile + ") " + e1.getMessage(); + status.errorCode = Output.ErrorCode.UNKNOWN_ERROR; + return false; + } catch ( ArchiveException e1 ) { + status.error = "(" + inputFile + ") " + e1.getMessage(); + status.errorCode = Output.ErrorCode.UNKNOWN_FORMAT; + return false; + } + // It's open + ArchiveEntry inEntry; + + // Remember all files added, so we can issue warnings for duplicate entries + Set<String> entries = new HashSet<>(); + // Iterate over every entry + while ( ( inEntry = inArchive.getNextEntry() ) != null ) { + if ( !inArchive.canReadEntryData( inEntry ) ) + continue; // Skip unreadable entries + // Construct TarArchiveEntry - we want unix stuff like uid/gid, links, file/dir mode, so try to get from source archive + TarArchiveEntry outEntry = Archive.createTarArchiveEntry( inEntry, 0, 0, 0755, 0644 ); + // Ask lib if the entry can be written + if ( !outArchive.canWriteEntryData( outEntry ) ) { + status.warnings.add( "Commons-compress says entry '" + outEntry.getName() + "' cannot be written." ); + continue; + } + // Dupcheck + if ( entries.contains( outEntry.getName() ) ) { + status.warnings.add( "Duplicate entry: " + outEntry.getName() ); + } else { + entries.add( outEntry.getName() ); + } + // Actually add entry now + outArchive.putArchiveEntry( outEntry ); + if ( !Util.streamCopy( inArchive, outArchive, outEntry.getSize() ) ) { + status.error = "(" + inputFile + ") Could not copy entry '" + inEntry.getName() + "' to destination archive."; + status.errorCode = Output.ErrorCode.WRITE_FAILED; + return false; + } + outArchive.closeArchiveEntry(); + } // End entry + } catch ( IOException e ) { + return false; + } finally { + Util.multiClose( inArchive ); + } + } // End of loop over input archives + + return true; + } finally { + Util.multiClose( outArchive ); + } + } + + @Override + protected boolean initTask() + { + this.setStatusObject( this.status ); + // Input files + if ( this.inputFiles == null ) { + status.error = "Input: No archives given"; + status.errorCode = Output.ErrorCode.UNKNOWN_ERROR; + return false; + } + for ( int i = 0; i < this.inputFiles.length; ++i ) { + String inputFile = this.inputFiles[i]; + if ( inputFile == null || inputFile.length() == 0 || inputFile.charAt( 0 ) != '/' ) { + status.error = "Input: Need absolute path (got: " + inputFile + ")"; + status.errorCode = Output.ErrorCode.INVALID_DIRECTORY; + return false; + } + inputFile = FilenameUtils.normalize( inputFile ); + if ( inputFile == null || !Util.startsWith( inputFile, ALLOWED_DIRS ) ) { + status.error = "Input: File not in allowed directory"; + status.errorCode = Output.ErrorCode.INVALID_DIRECTORY; + return false; + } + this.inputFiles[i] = inputFile; + } + // Output file + if ( this.outputFile == null || this.outputFile.length() == 0 || this.outputFile.charAt( 0 ) != '/' ) { + status.error = "Output: Need absolute path."; + status.errorCode = Output.ErrorCode.INVALID_DIRECTORY; + return false; + } + this.outputFile = FilenameUtils.normalize( this.outputFile ); + if ( !Util.startsWith( this.outputFile, ALLOWED_DIRS ) ) { + status.error = "Output: File not in allowed directory"; + status.errorCode = Output.ErrorCode.INVALID_DIRECTORY; + return false; + } + return true; + } + + @SuppressWarnings( "unused" ) + private static class Output + { + protected String error = null; + + protected enum ErrorCode + { + NOT_FOUND, UNKNOWN_FORMAT, UNKNOWN_ERROR, INVALID_DIRECTORY, WRITE_FAILED + }; + + protected ErrorCode errorCode = null; + protected List<String> warnings = null; + + public static class Entry + { + protected String name = null; + protected boolean isdir = false; + protected long size = -1; + } + } + +} |