// ftpc.java // ------------------------------------- // (C) by Michael Peter Christen; mc@anomic.de // first published on http://www.anomic.de // Frankfurt, Germany, 2002, 2004, 2006 // main implementation finished: 28.05.2002 // last major change: 06.05.2004 // added html generation for directories: 5.9.2006 // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // Using this software in any meaning (reading, learning, copying, compiling, // running) means that you agree that the Author(s) is (are) not responsible // for cost, loss of data or any harm that may be caused directly or indirectly // by usage of this softare or this documentation. The usage of this software // is on your own risk. The installation and usage (starting/running) of this // software may allow other people or application to access your computer and // any attached devices and is highly dependent on the configuration of the // software which must be done by the user of the software; the author(s) is // (are) also not responsible for proper configuration and usage of the // software, even if provoked by documentation provided together with // the software. // // Any changes to this file according to the GPL as documented in the file // gpl.txt aside this file in the shipment you received can be done to the // lines that follows this copyright notice here, but changes must not be // done inside the copyright notive above. A re-distribution must contain // the intact and unchanged copyright notice. // Contributions and changes to the program code must be marked as such. package de.anomic.net; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.RandomAccessFile; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.text.DateFormat; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; import java.util.Properties; import java.util.StringTokenizer; import java.util.TimeZone; import java.util.Vector; import de.anomic.server.serverCore; public class ftpc { private static final String vDATE = "20040506"; private static final String logPrefix = "FTPC: "; private InputStream in; private PrintStream out; private PrintStream err; private boolean glob = true; // glob = false -> filenames are taken literally for mget, .. // for time measurement private static final TimeZone GMTTimeZone = TimeZone.getTimeZone("PST"); // the GMT Time Zone // transfer type private static final char transferType = 'i'; // transfer binary // block size [1K by default] private static final int blockSize = 1024; // client socket for commands private Socket ControlSocket = null; // socket timeout private int ControlSocketTimeout = 300000; // socket for data transactions private ServerSocket DataSocketActive = null; private Socket DataSocketPassive = null; private boolean DataSocketPassiveMode = true; // output and input streams for client control connection private BufferedReader clientInput = null; private DataOutputStream clientOutput = null; // client prompt private String prompt = "ftp [local]>"; String[] cmd; // session parameters File currentPath; String account, password, host, remotemessage, remotegreeting, remotesystem; int port; public ftpc() { this(System.in, System.out, System.err); } public ftpc(java.io.InputStream ins, java.io.PrintStream outs, java.io.PrintStream errs) { try { System.setSecurityManager(new sm()); } catch (java.security.AccessControlException e) { } this.in = ins; this.out = outs; this.err = errs; this.currentPath = new File(System.getProperty("user.dir")); try { this.currentPath = new File(this.currentPath.getCanonicalPath()); } catch (IOException e) {} this.account = null; this.password = null; this.host = null; this.port = -1; this.remotemessage = null; this.remotegreeting = null; this.remotesystem = null; } public void shell(String server) { String command; java.io.PrintWriter pw = null; if (out != null) { pw = new java.io.PrintWriter(out); } try { java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(in)); if (server != null) exec("open " + server, true); while (true) { // prompt if (pw != null) {pw.print(prompt); pw.flush();} // read a line while ((command = stdin.readLine()) == null) if (pw != null) {pw.print(prompt); pw.flush();} // execute if (!exec(command, false)) break; } } catch (Exception e) { err.println(logPrefix + "---- Error - ftp exception: " + e); e.printStackTrace(err); } } public boolean exec(String command, boolean promptIt) { if ((command == null) || (command.length() == 0)) return true; int pos; String com; boolean ret = true; while (command.length() > 0) { pos = command.indexOf(";"); if (pos < 0) pos = command.indexOf("\n"); if (pos < 0) { com = command; command = ""; } else { com = command.substring(0,pos); command = command.substring(pos + 1); } if (promptIt) out.println(logPrefix + prompt + com); cmd = line2args(com); try { ret = (((Boolean) getClass().getMethod( cmd[0].toUpperCase(), new Class[0] ).invoke(this, new Object[0])).booleanValue()); } catch (InvocationTargetException e) { if (e.getMessage() == null) {} else if (ControlSocket == null) { // the error was probably caused because there is no connection err.println(logPrefix + "---- not connected. no effect."); e.printStackTrace(); return ret; } else { err.println(logPrefix + "---- ftp internal exception: target exception " + e); return ret; } } catch (IllegalAccessException e) { err.println(logPrefix + "---- ftp internal exception: wrong access " + e); return ret; } catch (NoSuchMethodException e) { // consider first that the user attempted to execute a java command from // the current path; either local or remote if (ControlSocket == null) { // try a local exec try { javaexec(cmd); } catch (Exception ee) { err.println(logPrefix + "---- Command '" + cmd[0] + "' not supported. Try 'HELP'."); } } else { // try a remote exec exec("java " + com, false); } return ret; } } return ret; } private String[] line2args(String line) { // parse the command line if ((line == null) || (line.length() == 0)) return null; // pre-parse String line1=""; boolean quoted = false; for (int i = 0; i < line.length(); i++) { if (quoted) { if (line.charAt(i) == '"') { quoted = false; } else { line1 = line1 + line.charAt(i); } } else { if (line.charAt(i) == '"') { quoted = true; } else if (line.charAt(i) == ' ') { line1 = line1 + '|'; } else { line1 = line1 + line.charAt(i); } } } // construct StringTokenizer String args[]; StringTokenizer st = new StringTokenizer(line1,"|"); // read tokens from string args = new String[st.countTokens()]; for (int i = 0; st.hasMoreTokens(); i++) { args[i] = st.nextToken(); } st = null; return args; } private static String[] shift(String args[]) { if ((args == null) || (args.length == 0)) return args; else { String[] newArgs = new String[args.length-1]; System.arraycopy(args, 1, newArgs, 0, args.length-1); return newArgs; } } class cl extends ClassLoader { public cl() { super(); } public Class loadClass(String classname, boolean resolve) throws ClassNotFoundException { Class c = findLoadedClass(classname); if (c == null) try { // second try: ask the system c = findSystemClass(classname); } catch (ClassNotFoundException e) { // third try: load myself File f = new File(System.getProperty("user.dir"), classname + ".class"); int length = (int)f.length(); byte[] classbytes = new byte[length]; try { DataInputStream in = new DataInputStream(new FileInputStream(f)); in.readFully(classbytes); in.close(); c = defineClass(classname, classbytes, 0, classbytes.length); } catch (FileNotFoundException ee) { throw new ClassNotFoundException(); } catch (IOException ee) { throw new ClassNotFoundException(); } } if (resolve) resolveClass(c); return c; } } private void javaexec(String[] inArgs) { String obj = inArgs[0]; String[] args = new String[inArgs.length-1]; // remove the object name from the array of arguments System.arraycopy(inArgs, 1, args, 0, inArgs.length-1); // Build the argument list for invoke() method. Object[] argList = new Object[1]; argList[0] = args; Properties pr = System.getProperties(); String origPath = (String) pr.get("java.class.path"); try { // set the user.dir to the actual local path pr.put("user.dir", this.currentPath.toString()); // add the current path to the classpath //pr.put("java.class.path", "" + pr.get("user.dir") + pr.get("path.separator") + origPath); //err.println(logPrefix + "System Properties: " + pr.toString()); System.setProperties(pr); // locate object Class c = (new cl()).loadClass(obj); //Class c = this.getClass().getClassLoader().loadClass(obj); // locate public static main(String[]) method Class[] parameterType = new Class[1]; parameterType[0] = Class.forName("[Ljava.lang.String;"); Method m = c.getMethod("main", parameterType); // invoke object.main() Object result = m.invoke(null, argList); parameterType = null; m = null; // handle result if (result != null) out.println(logPrefix + "returns " + result); // set the local path to the user.dir (which may have changed) this.currentPath = new File((String) pr.get("user.dir")); } catch (ClassNotFoundException e) { // err.println(logPrefix + "---- cannot find class file " + obj + ".class"); // class file does not exist, go silently over it to not show everybody that the // system attempted to load a class file err.println(logPrefix + "---- Command '" + obj + "' not supported. Try 'HELP'."); } catch (NoSuchMethodException e) { err.println(logPrefix + "---- no \"public static main(String args[])\" in " + obj); } catch (InvocationTargetException e) { Throwable orig = e.getTargetException(); if (orig.getMessage() == null) {} else { err.println(logPrefix + "---- Exception from " + obj + ": " + orig.getMessage()); orig.printStackTrace(err); } } catch (IllegalAccessException e) { err.println(logPrefix + "---- Illegal access for " + obj + ": class is probably not declared as public"); e.printStackTrace(err); } catch (NullPointerException e) { err.println(logPrefix + "---- main(String args[]) is not defined as static for " + obj); /* } catch (IOException e) { // class file does not exist, go silently over it to not show everybody that the // system attempted to load a class file err.println(logPrefix + "---- Command '" + obj + "' not supported. Try 'HELP'."); */ } catch (Exception e) { err.println(logPrefix + "---- Exception caught: " + e); e.printStackTrace(err); } // set the classpath to its original definition pr.put("java.class.path", origPath); } // FTP CLIENT COMMANDS ------------------------------------ public boolean ASCII() { if (cmd.length != 1) { err.println(logPrefix + "---- Syntax: ASCII (no parameter)"); return true; } try { literal("TYPE A"); } catch (IOException e) { err.println(logPrefix + "---- Error: ASCII transfer type not supported by server."); } return true; } public boolean BINARY() { if (cmd.length != 1) { err.println(logPrefix + "---- Syntax: BINARY (no parameter)"); return true; } try { literal("TYPE I"); } catch (IOException e) { err.println(logPrefix + "---- Error: BINARY transfer type not supported by server."); } return true; } public boolean BYE() { return QUIT(); } public boolean CD() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: CD "); return true; } if (ControlSocket == null) return LCD(); try { // send cwd command send("CWD " + cmd[1]); String reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); } catch (IOException e) { err.println(logPrefix + "---- Error: change of working directory to path " + cmd[1] + " failed."); } return true; } public boolean CLOSE() { return DISCONNECT(); } private void rmForced(String path) throws IOException { // first try: send DELE command (to delete a file) send("DELE " + path); // read reply String reply1 = receive(); if (Integer.parseInt(reply1.substring(0, 1)) != 2) { // second try: send a RMD command (to delete a directory) send("RMD " + path); // read reply String reply2 = receive(); if (Integer.parseInt(reply2.substring(0, 1)) != 2) { // third try: test if this thing is a directory or file and send appropriate error message if (isFolder(path)) throw new IOException(reply2); else throw new IOException(reply1); } } } public boolean DEL() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: DEL "); return true; } if (ControlSocket == null) return LDEL(); try { rmForced(cmd[1]); } catch (IOException e) { err.println(logPrefix + "---- Error: deletion of file " + cmd[1] + " failed."); } return true; } public boolean RM() { return DEL(); } public boolean DIR() { if (cmd.length > 2) { err.println(logPrefix + "---- Syntax: DIR [|]"); return true; } if (ControlSocket == null) return LDIR(); try { Vector l; if (cmd.length == 2) l = list(cmd[1],false); else l = list(".",false); Enumeration x = l.elements(); out.println(logPrefix + "---- v---v---v---v---v---v---v---v---v---v---v---v---v---v---v---v---v---v---v"); while (x.hasMoreElements()) out.println(logPrefix + (String) x.nextElement()); out.println(logPrefix + "---- ^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^"); } catch (IOException e) { err.println(logPrefix + "---- Error: remote list not available"); } return true; } public boolean DISCONNECT() { try { quit(); out.println(logPrefix + "---- Connection closed."); } catch (IOException e) { err.println(logPrefix + "---- Connection to server lost."); } this.ControlSocket = null; this.DataSocketActive = null; this.DataSocketPassive = null; this.clientInput = null; this.clientOutput = null; this.prompt = "ftp [local]>"; return true; } private String quit() throws IOException { // send delete command send("QUIT"); // read status reply String reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); // cleanup if (ControlSocket != null) { clientOutput.close(); clientInput.close(); ControlSocket.close(); } if (DataSocketActive != null) DataSocketActive.close(); if (DataSocketPassive != null) DataSocketPassive.close(); return reply; } public boolean EXIT() { return QUIT(); } public boolean GET() { if ((cmd.length < 2) || (cmd.length > 3)) { err.println(logPrefix + "---- Syntax: GET []"); return true; } String remote = cmd[1]; //(new File(cmd[1])).getName(); File local; File l; if (cmd.length == 2) { l = new File(remote); if (l.isAbsolute()) local = l; else local = new File(this.currentPath, remote); } else { l = new File(cmd[2]); if (l.isAbsolute()) local = l; else local = new File(this.currentPath, cmd[2]); } if (local.exists()) { err.println(logPrefix + "---- Error: local file " + local.toString() + " already exists."); err.println(logPrefix + " File " + remote + " not retrieved. Local file unchanged."); } else { if (cmd.length == 2) retrieveFilesRecursively(remote, false); else try { get(local.getAbsolutePath(), remote); } catch (IOException e) { err.println(logPrefix + "---- Error: retrieving file " + remote + " failed. (" + e.getMessage() + ")"); } } return true; } private void retrieveFilesRecursively(String remote, boolean delete) { File local; File l = new File(remote); if (l.isAbsolute()) local = l; else local = new File(this.currentPath, remote); try { get(local.getAbsolutePath(), remote); try {if (delete) rmForced(remote);} catch (IOException eee) { err.println(logPrefix + "---- Warning: remote file or path " + remote + " cannot be removed."); } } catch (IOException e) { if (e.getMessage().startsWith("550")) { // maybe it's a "not a plain file" error message", then it can be a folder // test if this exists (then it should be a folder) if (isFolder(remote)) { // copy the whole directory exec("cd \"" + remote + "\";lmkdir \"" + remote + "\";lcd \"" + remote + "\"",true); //exec("mget *",true); try { Enumeration files = list(".",false).elements(); while (files.hasMoreElements()) retrieveFilesRecursively((String) files.nextElement(), delete); } catch (IOException ee) {} exec("cd ..;lcd ..", true); try {if (delete) rmForced(remote);} catch (IOException eee) { err.println(logPrefix + "---- Warning: remote file or path " + remote + " cannot be removed."); } } else { err.println(logPrefix + "---- Error: remote file or path " + remote + " does not exist."); } } else { err.println(logPrefix + "---- Error: retrieving file " + remote + " failed. (" + e.getMessage() + ")"); } } } public boolean isFolder(String path) { try { send("CWD " + path); String reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); send("CWD .."); reply = receive(); return true; } catch (IOException e) { return false; } } public boolean GLOB() { if (cmd.length != 1) { err.println(logPrefix + "---- Syntax: GLOB (no parameter)"); return true; } this.glob = !this.glob; out.println(logPrefix + "---- globbing is now turned " + ((this.glob) ? "ON" : "OFF")); return true; } public boolean HASH() { err.println(logPrefix + "---- no games implemented"); return true; } public boolean JAR() { sun.tools.jar.Main.main(shift(cmd)); return true; } public boolean JJENCODE() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: JJENCODE "); return true; } String path = cmd[1]; File dir = new File(path); File newPath = dir.isAbsolute() ? dir : new File(this.currentPath, path); if (newPath.exists()) { if (newPath.isDirectory()) { // exec("cd \"" + remote + "\";lmkdir \"" + remote + "\";lcd \"" + remote + "\"",true); /* if not exist %1\nul goto :error cd %1 c:\jdk1.2.2\bin\jar -cfM0 ..\%1.jar *.* cd .. c:\jdk1.2.2\bin\jar -cfM %1.jj %1.jar del %1.jar */ String s = ""; String[] l = newPath.list(); for (int i = 0; i < l.length; i++) s = s + " \"" + l[i] + "\""; exec("cd \"" + path + "\";jar -cfM0 ../\"" + path + ".jar\"" + s, true); exec("cd ..;jar -cfM \"" + path + ".jj\" \"" + path + ".jar\"", true); exec("rm \"" + path + ".jar\"", true); } else { err.println(logPrefix + "---- Error: local path " + newPath.toString() + " denotes not to a directory."); } } else { err.println(logPrefix + "---- Error: local path " + newPath.toString() + " does not exist."); } return true; } public boolean JJDECODE() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: JJENCODE "); return true; } String path = cmd[1]; File dir = new File(path); File newPath = dir.isAbsolute() ? dir : new File(this.currentPath, path); File newFolder = new File(newPath.toString() + ".dir"); if (newPath.exists()) { if (!newPath.isDirectory()) { if (!newFolder.mkdir()) { /* if not exist %1.jj goto :error mkdir %1.dir copy %1.jj %1.dir\ > %1.dummy && del %1.dummy cd %1.dir c:\jdk1.2.2\bin\jar -xf %1.jj del %1.jj c:\jdk1.2.2\bin\jar -xf %1.jar del %1.jar cd .. */ exec("mkdir \"" + path + ".dir\"", true); } else { err.println(logPrefix + "---- Error: target dir " + newFolder.toString() + " cannot be created"); } } else { err.println(logPrefix + "---- Error: local path " + newPath.toString() + " must denote to jar/jar file"); } } else { err.println(logPrefix + "---- Error: local path " + newPath.toString() + " does not exist."); } return true; } private static String[] argList2StringArray(String argList) { // command line parser StringTokenizer tokens = new StringTokenizer(argList); String[] args = new String[tokens.countTokens()]; for (int i = 0; tokens.hasMoreTokens(); i++) args[i] = tokens.nextToken(); tokens = null; // free mem return args; } public boolean JOIN(String[] args) { // make sure the specified dest file does not exist String dest_name = args[1]; File dest_file = new File(dest_name); if (dest_file.exists()) { err.println(logPrefix + "join: destination file " + dest_name + " already exists"); return true; } // prepare or search file names of the input files to be joined String source_name; File source_file; int pc = -1; // create new string array with file names // scan first for the files pc = 0; source_name = dest_name + ".000"; String argString = ""; source_file = new File(source_name); while ((source_file.exists()) && (source_file.isFile()) && (source_file.canRead())) { argString = argString + " " + source_name; pc++; source_name = dest_name + (pc < 10 ? ".00"+pc : (pc < 100 ? ".0"+pc : "."+pc)); source_file = new File(source_name); } args = argList2StringArray(argString.substring(1)); // do the join FileOutputStream dest = null; FileInputStream source = null; byte[] buffer; int bytes_read = 0; try { // open output file dest = new FileOutputStream(dest_file); buffer = new byte[1024]; // append all source files for (pc = 0; pc < args.length; pc++) { // open the source file source_name = args[pc]; source_file = new File(source_name); source = new FileInputStream(source_file); // start with the copy of one source file while (true) { bytes_read = source.read(buffer); if (bytes_read == -1) break; dest.write(buffer, 0, bytes_read); } // copy finished. close source file if (source != null) try { source.close(); } catch (IOException e) {} } // close the output file if (dest != null) try { dest.close(); } catch (IOException e) {} // if we come to this point then everything went fine // if the user wanted to delete the source it is save to do so now for (pc = 0; pc < args.length; pc++) { try { if (!(new File(args[pc])).delete()) System.err.println(logPrefix + "join: unable to delete file " + args[pc]); } catch (SecurityException e) { System.err.println(logPrefix + "join: no permission to delete file " + args[pc]); } } } catch (FileNotFoundException e) { } catch (IOException e) { } // clean up finally { // close any opened streams if (dest != null) try { dest.close(); } catch (IOException e) {} if (source != null) try { source.close(); } catch (IOException e) {} // print appropriate message System.err.println(logPrefix + "join created output from " + args.length + " source files"); } return true; } public boolean COPY(String[] args) { File dest_file = new File(args[2]); if (dest_file.exists()) { err.println(logPrefix + "copy: destination file " + args[2] + " already exists"); return true; } int bytes_read = 0; try { // open output file FileOutputStream dest = new FileOutputStream(dest_file); byte[] buffer = new byte[1024]; // open the source file File source_file = new File(args[1]); FileInputStream source = new FileInputStream(source_file); // start with the copy of one source file while (true) { bytes_read = source.read(buffer); if (bytes_read == -1) break; dest.write(buffer, 0, bytes_read); } // copy finished. close source file if (source != null) try { source.close(); } catch (IOException e) {} // close the output file if (dest != null) try { dest.close(); } catch (IOException e) {} } catch (FileNotFoundException e) { } catch (IOException e) { } return true; } public boolean JAVA() { String s = "JAVA"; for (int i = 1; i< cmd.length; i++) s = s + " " + cmd[i]; try { send(s); /*String reply =*/ receive(); } catch (IOException e) {} return true; } public boolean LCD() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: LCD "); return true; } String path = cmd[1]; File dir = new File(path); File newPath = dir.isAbsolute() ? dir : new File(this.currentPath, path); try {newPath = new File(newPath.getCanonicalPath());} catch (IOException e) {} if (newPath.exists()) { if (newPath.isDirectory()) { this.currentPath = newPath; out.println(logPrefix + "---- New local path: " + this.currentPath.toString()); } else { err.println(logPrefix + "---- Error: local path " + newPath.toString() + " denotes not a directory."); } } else { err.println(logPrefix + "---- Error: local path " + newPath.toString() + " does not exist."); } return true; } public boolean LDEL() { return LRM(); } public boolean LDIR() { if (cmd.length != 1) { err.println(logPrefix + "---- Syntax: LDIR (no parameter)"); return true; } String[] name = this.currentPath.list(); for (int n = 0; n < name.length; ++ n) out.println(logPrefix + ls(new File(this.currentPath, name[n]))); return true; } private String ls(File inode) { if ((inode == null) || (!inode.exists())) return ""; String s = ""; if (inode.isDirectory()) s = s + "d"; else if (inode.isFile()) s = s + "-"; //else if (inode.isHidden()) s = s + "h"; else s = s + "?"; if (inode.canRead()) s = s + "r"; else s = s + "-"; if (inode.canWrite()) s = s + "w"; else s = s + "-"; s = s + " " + lenformatted(Long.toString(inode.length()),9); DateFormat df = DateFormat.getDateTimeInstance(); s = s + " " + df.format(new Date(inode.lastModified())); s = s + " " + inode.getName(); if (inode.isDirectory()) s = s + "/"; return s; } private String lenformatted(String s, int l) { l = l - s.length(); while (l > 0) {s = " " + s; l--;} return s; } public boolean LITERAL() { if (cmd.length == 1) { err.println(logPrefix + "---- Syntax: LITERAL [] (see RFC959)"); return true; } String s = ""; for (int i = 1; i < cmd.length; i++) s = s + " " + cmd[i]; try { literal(s.substring(1)); } catch (IOException e) { err.println(logPrefix + "---- Error: Syntax of FTP-command wrong. See RFC959 for details."); } return true; } public boolean LLS() { return LDIR(); } public boolean LMD() { return LMKDIR(); } public boolean LMKDIR() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: LMKDIR "); return true; } File f = new File(this.currentPath, cmd[1]); if (f.exists()) { err.println(logPrefix + "---- Error: local file/folder " + cmd[1] + " already exists"); } else { if (!f.mkdir()) err.println(logPrefix + "---- Error: creation of local folder " + cmd[1] + " failed"); } return true; } public boolean LMV() { if (cmd.length != 3) { err.println(logPrefix + "---- Syntax: LMV "); return true; } File from = new File(cmd[1]); File to = new File(cmd[2]); if (!to.exists()) { if (from.renameTo(to)) { out.println(logPrefix + "---- \"" + from.toString() + "\" renamed to \"" + to.toString() + "\""); } else err.println(logPrefix + "rename failed"); } else err.println(logPrefix + "\"" + to.toString() + "\" already exists"); return true; } public boolean LPWD() { if (cmd.length != 1) { err.println(logPrefix + "---- Syntax: LPWD (no parameter)"); return true; } out.println(logPrefix + "---- Local path: " + this.currentPath.toString()); return true; } public boolean LRD() { return LMKDIR(); } public boolean LRMDIR() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: LRMDIR "); return true; } File f = new File(this.currentPath, cmd[1]); if (!f.exists()) { err.println(logPrefix + "---- Error: local folder " + cmd[1] + " does not exist"); } else { if (!f.delete()) err.println(logPrefix + "---- Error: deletion of local folder " + cmd[1] + " failed"); } return true; } public boolean LRM() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: LRM "); return true; } File f = new File(this.currentPath, cmd[1]); if (!f.exists()) { err.println(logPrefix + "---- Error: local file " + cmd[1] + " does not exist"); } else { if (!f.delete()) err.println(logPrefix + "---- Error: deletion of file " + cmd[1] + " failed"); } return true; } public boolean LS() { if (cmd.length > 2) { err.println(logPrefix + "---- Syntax: LS [|]"); return true; } if (ControlSocket == null) return LLS(); try { Vector l; if (cmd.length == 2) l = list(cmd[1],true); else l = list(".",true); Enumeration x = l.elements(); out.println(logPrefix + "---- v---v---v---v---v---v---v---v---v---v---v---v---v---v---v---v---v---v---v"); while (x.hasMoreElements()) out.println(logPrefix + (String) x.nextElement()); out.println(logPrefix + "---- ^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^---^"); } catch (IOException e) { err.println(logPrefix + "---- Error: remote list not available"); } return true; } private Vector list(String path, boolean extended) throws IOException { // prepare data channel if (DataSocketPassiveMode) createPassiveDataPort(); else createActiveDataPort(); // send command to the control port if (extended) send("LIST " + path); else send("NLST " + path); // read status of the command from the control port String reply = receive(); // get status code int status = Integer.parseInt(reply.substring(0, 1)); // starting data transaction if (status == 1) { Socket data; if (DataSocketPassiveMode) { data = DataSocketPassive; } else { data = DataSocketActive.accept(); } BufferedReader ClientStream = new BufferedReader(new InputStreamReader(data.getInputStream())); // read file system data String line; Vector files = new Vector(); while ((line = ClientStream.readLine()) != null) if (!line.startsWith("total ")) files.addElement(line); // after stream is empty we should get control completion echo //reply = receive(); //boolean success = (Integer.parseInt(reply.substring(0, 1)) == 2); // shutdown connection ClientStream.close(); data.close(); //if (!success) throw new IOException(reply); files.trimToSize(); return files; } else throw new IOException(reply); } public boolean MDIR() { return MKDIR(); } public boolean MKDIR() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: MKDIR "); return true; } if (ControlSocket == null) return LMKDIR(); try { // send mkdir command send("MKD " + cmd[1]); // read reply String reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); } catch (IOException e) { err.println(logPrefix + "---- Error: creation of folder " + cmd[1] + " failed"); } return true; } public boolean MGET() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: MGET "); return true; } try { mget(cmd[1], false); } catch (IOException e) { err.println(logPrefix + "---- Error: mget failed (" + e.getMessage() + ")"); } return true; } private void mget(String pattern, boolean remove) throws IOException { Vector l = list(".",false); Enumeration x = l.elements(); String remote; File local; //int idx; // the search for " " is only for improper lists from the server. this fails if the file name has a " " in it while (x.hasMoreElements()) { remote = (String) x.nextElement(); //idx = remote.lastIndexOf(" "); //if (idx >= 0) remote = remote.substring(idx + 1); if (matches(remote, pattern)) { local = new File(this.currentPath, remote); if (local.exists()) { err.println(logPrefix + "---- Warning: local file " + local.toString() + " overwritten."); local.delete(); } retrieveFilesRecursively(remote, remove); } } } public boolean MOVEDOWN() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: MOVEDOWN "); return true; } try { mget(cmd[1], true); } catch (IOException e) { err.println(logPrefix + "---- Error: movedown failed (" + e.getMessage() + ")"); } return true; } /* public boolean MOVEUP() { } */ public boolean MV() { if (cmd.length != 3) { err.println(logPrefix + "---- Syntax: MV "); return true; } if (ControlSocket == null) return LMV(); try { // send rename commands send("RNFR " + cmd[1]); // read reply String reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); send("RNTO " + cmd[2]); // read reply reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); } catch (IOException e) { err.println(logPrefix + "---- Error: rename of " + cmd[1] + " to " + cmd[2] + " failed."); } return true; } public boolean NOOP() { if (cmd.length != 1) { err.println(logPrefix + "---- Syntax: NOOP (no parameter)"); return true; } try { literal("NOOP"); } catch (IOException e) { err.println(logPrefix + "---- Error: server does not know how to do nothing"); } return true; } public boolean OPEN() { if ((cmd.length < 2) || (cmd.length > 3)) { err.println(logPrefix + "---- Syntax: OPEN []"); return true; } int port = 21; if (cmd.length == 3) { try { port = java.lang.Integer.parseInt(cmd[2]); } catch (NumberFormatException e) {port = 21;} } if (cmd[1].indexOf(":") > 0) { // port is given port = java.lang.Integer.parseInt(cmd[1].substring(cmd[1].indexOf(":") + 1)); cmd[1] = cmd[1].substring(0,cmd[1].indexOf(":")); } try { open(cmd[1], port); out.println(logPrefix + "---- Connection to " + cmd[1] + " established."); prompt = "ftp [" + cmd[1] + "]>"; } catch (IOException e) { err.println(logPrefix + "---- Error: connecting " + cmd[1] + " on port " + port + " failed."); } return true; } private void open(String host, int port) throws IOException { if (ControlSocket != null) exec("close",false); // close any existing connections first ControlSocket = new Socket(host, port); ControlSocket.setSoTimeout(this.ControlSocketTimeout); clientInput = new BufferedReader(new InputStreamReader(ControlSocket.getInputStream())); clientOutput = new DataOutputStream(new BufferedOutputStream(ControlSocket.getOutputStream())); // read and return server message this.host = host; this.port = port; this.remotemessage = receive(); if ((remotemessage != null) && (remotemessage.length() > 3)) remotemessage = remotemessage.substring(4); } public boolean PROMPT() { err.println(logPrefix + "---- prompt is always off"); return true; } public boolean PUT() { if ((cmd.length < 2) || (cmd.length > 3)) { err.println(logPrefix + "---- Syntax: PUT []"); return true; } File local = new File(this.currentPath, cmd[1]); String remote = (cmd.length == 2) ? local.getName() : cmd[2]; if (!local.exists()) { err.println(logPrefix + "---- Error: local file " + local.toString() + " does not exist."); err.println(logPrefix + " Remote file " + remote + " not overwritten."); } else { try { put(local.getAbsolutePath(), remote); } catch (IOException e) { err.println(logPrefix + "---- Error: transmitting file " + local.toString() + " failed."); } } return true; } public boolean PWD() { if (cmd.length > 1) { err.println(logPrefix + "---- Syntax: PWD (no parameter)"); return true; } if (ControlSocket == null) return LPWD(); try { // send pwd command send("PWD"); // read current directory String reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); // parse directory name out of the reply reply = reply.substring(5); reply = reply.substring(0, reply.lastIndexOf('"')); out.println(logPrefix + "---- Current remote path is: " + reply); } catch (IOException e) { err.println(logPrefix + "---- Error: remote path not available"); } return true; } public boolean REMOTEHELP() { if (cmd.length != 1) { err.println(logPrefix + "---- Syntax: REMOTEHELP (no parameter)"); return true; } try { literal("HELP"); } catch (IOException e) { err.println(logPrefix + "---- Error: remote help not supported by server."); } return true; } public boolean RMDIR() { if (cmd.length != 2) { err.println(logPrefix + "---- Syntax: RMDIR "); return true; } if (ControlSocket == null) return LRMDIR(); try { rmForced(cmd[1]); } catch (IOException e) { err.println(logPrefix + "---- Error: deletion of folder " + cmd[1] + " failed."); } return true; } public boolean QUIT() { if (ControlSocket != null) exec("close",false); return false; } public boolean RECV() { return GET(); } public int size(String path) throws IOException { // get the size of a file. If the given path targets to a directory, a -1 is returned // this function is not supported by standard rfc 959. The method is descibed in // http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16.txt // if the method is not supported by the target server, this throws an IOException with the // server response as exception message // send command to the control port send("SIZE " + path); // read status of the command from the control port String reply = receive(); // get status code int status = Integer.parseInt(reply.substring(0, 3)); // starting data transaction if (status == 213) { try { return Integer.parseInt(reply.substring(4)); } catch (NumberFormatException e) { throw new IOException(reply); } } else { throw new IOException(reply); } } public boolean USER() { if (cmd.length != 3) { err.println(logPrefix + "---- Syntax: USER "); return true; } try { login(cmd[1], cmd[2]); out.println(logPrefix + "---- Granted access for user " + cmd[1] + "."); } catch (IOException e) { err.println(logPrefix + "---- Error: authorization of user " + cmd[1] + " failed."); } return true; } public boolean APPEND() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean HELP() { out.println(logPrefix + "---- ftp HELP ----"); out.println(logPrefix + ""); out.println(logPrefix + "This ftp client shell can act as command shell for the local host as well for the"); out.println(logPrefix + "remote host. Commands that point to the local host are preceded by 'L'."); out.println(logPrefix + ""); out.println(logPrefix + "Supported Commands:"); out.println(logPrefix + "ASCII"); out.println(logPrefix + " switch remote server to ASCII transfer mode"); out.println(logPrefix + "BINARY"); out.println(logPrefix + " switch remote server to BINARY transfer mode"); out.println(logPrefix + "BYE"); out.println(logPrefix + " quit the command shell (same as EXIT)"); out.println(logPrefix + "CD "); out.println(logPrefix + " change remote path"); out.println(logPrefix + "CLOSE"); out.println(logPrefix + " close connection to remote host (same as DISCONNECT)"); out.println(logPrefix + "DEL "); out.println(logPrefix + " delete file on remote server (same as RM)"); out.println(logPrefix + "RM "); out.println(logPrefix + " remove file from remote server (same as DEL)"); out.println(logPrefix + "DIR [|] "); out.println(logPrefix + " print file information for remote directory or file"); out.println(logPrefix + "DISCONNECT"); out.println(logPrefix + " disconnect from remote server (same as CLOSE)"); out.println(logPrefix + "EXIT"); out.println(logPrefix + " quit the command shell (same as BYE)"); out.println(logPrefix + "GET []"); out.println(logPrefix + " load from remote server and store it locally,"); out.println(logPrefix + " optionally to . if the is a directory,"); out.println(logPrefix + " then all files in that directory are retrieved,"); out.println(logPrefix + " including recursively all subdirectories."); out.println(logPrefix + "GLOB"); out.println(logPrefix + " toggles globbing: matching with wild cards or not"); out.println(logPrefix + "COPY"); out.println(logPrefix + " copies local files"); out.println(logPrefix + "LCD "); out.println(logPrefix + " local directory change"); out.println(logPrefix + "LDEL "); out.println(logPrefix + " local file delete"); out.println(logPrefix + "LDIR"); out.println(logPrefix + " shows local directory content"); out.println(logPrefix + "LITERAL []"); out.println(logPrefix + " Sends FTP commands as documented in RFC959"); out.println(logPrefix + "LLS"); out.println(logPrefix + " as LDIR"); out.println(logPrefix + "LMD"); out.println(logPrefix + " as LMKDIR"); out.println(logPrefix + "LMV "); out.println(logPrefix + " copies local files"); out.println(logPrefix + "LPWD"); out.println(logPrefix + " prints local path"); out.println(logPrefix + "LRD"); out.println(logPrefix + " as LMKDIR"); out.println(logPrefix + "LRMD "); out.println(logPrefix + " deletes local directory "); out.println(logPrefix + "LRM "); out.println(logPrefix + " deletes local file "); out.println(logPrefix + "LS [|]"); out.println(logPrefix + " prints list of remote directory or information of file "); out.println(logPrefix + "MDIR"); out.println(logPrefix + " as MKDIR"); out.println(logPrefix + "MGET "); out.println(logPrefix + " copies files from remote server that fits into the"); out.println(logPrefix + " pattern to the local path."); out.println(logPrefix + "MOVEDOWN "); out.println(logPrefix + " copies files from remote server as with MGET"); out.println(logPrefix + " and deletes them afterwards on the remote server"); out.println(logPrefix + "MV "); out.println(logPrefix + " moves or renames files on the local host"); out.println(logPrefix + "NOOP"); out.println(logPrefix + " sends the NOOP command to the remote server (which does nothing)"); out.println(logPrefix + " This command is usually used to measure the speed of the remote server."); out.println(logPrefix + "OPEN []"); out.println(logPrefix + " connects the ftp shell to the remote server . Optionally,"); out.println(logPrefix + " a port number can be given, the default port number is 21."); out.println(logPrefix + " Example: OPEN localhost:2121 or OPEN 192.168.0.1 2121"); out.println(logPrefix + "PROMPT"); out.println(logPrefix + " compatibility command, that usually toggles beween prompting on or off."); out.println(logPrefix + " ftp has prompting switched off by default and cannot switched on."); out.println(logPrefix + "PUT []"); out.println(logPrefix + " copies the to the remote server to the current remote path or"); out.println(logPrefix + " optionally to the given path."); out.println(logPrefix + "PWD"); out.println(logPrefix + " prints current path on the remote server."); out.println(logPrefix + "REMOTEHELP"); out.println(logPrefix + " asks the remote server to print the help text of the remote server"); out.println(logPrefix + "RMDIR "); out.println(logPrefix + " removes the directory on the remote server"); out.println(logPrefix + "QUIT"); out.println(logPrefix + " exits the ftp application"); out.println(logPrefix + "RECV"); out.println(logPrefix + " as GET"); out.println(logPrefix + "USER "); out.println(logPrefix + " logs into the remote server with the user "); out.println(logPrefix + " and the password "); out.println(logPrefix + ""); out.println(logPrefix + ""); out.println(logPrefix + "EXAMPLE:"); out.println(logPrefix + "a standard sessions looks like this"); out.println(logPrefix + ">open 192.168.0.1:2121"); out.println(logPrefix + ">user anonymous bob"); out.println(logPrefix + ">pwd"); out.println(logPrefix + ">ls"); out.println(logPrefix + ">....."); out.println(logPrefix + ""); out.println(logPrefix + ""); return true; } public boolean QUOTE() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean BELL() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean MDELETE() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean SEND() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean DEBUG() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean MLS() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean TRACE() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean MPUT() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean TYPE() { err.println(logPrefix + "---- not yet supported"); return true; } public boolean CREATE() { err.println(logPrefix + "---- not yet supported"); return true; } // helper functions private boolean matches(String name, String pattern) { // checks whether the string name matches with the pattern // the pattern may contain characters '*' as wildcard for several // characters (also none) and '?' to match exactly one characters //out.println(logPrefix + "MATCH " + name + " " + pattern); if (!this.glob) return name.equals(pattern); if (pattern.equals("*")) return true; if ((pattern.startsWith("*")) && (pattern.endsWith("*"))) return // avoid recursion deadlock ((matches(name, pattern.substring(1))) || (matches(name, pattern.substring(0, pattern.length() - 1)))); try { int i = pattern.indexOf("?"); if (i >= 0) { if (!(matches(name.substring(0, i), pattern.substring(0, i)))) return false; return (matches(name.substring(i + 1), pattern.substring(i + 1))); } i = pattern.indexOf("*"); if (i >= 0) { if (!(name.substring(0, i).equals(pattern.substring(0, i)))) return false; if (pattern.length() == i + 1) return true; // pattern would be '*' return (matches( reverse(name.substring(i)), reverse(pattern.substring(i + 1)) + "*")); } return name.equals(pattern); } catch (java.lang.StringIndexOutOfBoundsException e) { // this is normal. it's a lazy implementation return false; } } private String reverse(String s) { if (s.length() < 2) return s; return reverse(s.substring(1)) + s.charAt(0); } // protocoll socket commands private void send(String buf) throws IOException { clientOutput.writeBytes(buf); clientOutput.write('\r'); clientOutput.write('\n'); clientOutput.flush(); if (buf.startsWith("PASS")) { if (out != null) out.println(logPrefix + "> PASS ********"); } else { if (out != null) out.println(logPrefix + "> " + buf); } } private String receive() throws IOException { // last reply starts with 3 digit number followed by space String reply; while(true) { reply = clientInput.readLine(); // sanity check if (reply == null) throw new IOException("Server has presumably shut down the connection."); if (out != null) out.println(logPrefix + "< " + reply); //serverResponse.addElement(reply); if (reply.length() >= 4 && Character.isDigit(reply.charAt(0)) && Character.isDigit(reply.charAt(1)) && Character.isDigit(reply.charAt(2)) && (reply.charAt(3) == ' ')) break; // end of reply } // return last reply line return reply; } private void sendTransferType(char type) throws IOException { send("TYPE " + type); String reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); } private void createActiveDataPort() throws IOException { // create data socket and bind it to free port available DataSocketActive = new ServerSocket(0); // get port socket has been bound to int DataPort = DataSocketActive.getLocalPort(); // client ip //InetAddress LocalIp = serverCore.publicIP(); // InetAddress LocalIp = DataSocketActive.getInetAddress().getLocalHost(); // save ip address in high byte order //byte[] Bytes = LocalIp.getAddress(); byte[] Bytes = serverCore.publicIP().getBytes(); // bytes greater than 127 should not be printed as negative short[] Shorts = new short[4]; for (int i = 0; i < 4; i++) { Shorts[i] = Bytes[i]; if (Shorts[i] < 0) Shorts[i] += 256; } // send port command via control socket: // four ip address shorts encoded and two port shorts encoded send("PORT " + //"127,0,0,1," + Shorts[0] + "," + Shorts[1] + "," + Shorts[2] + "," + Shorts[3] + "," + ((DataPort & 0xff00) >> 8) + "," + (DataPort & 0x00ff)); // read status of the command from the control port String reply = receive(); // check status code if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); DataSocketPassiveMode = false; } private void createPassiveDataPort() throws IOException { // send port command via control socket: // four ip address shorts encoded and two port shorts encoded send("PASV"); // read status of the command from the control port String reply = receive(); // check status code if (!(reply.substring(0, 3).equals("227"))) throw new IOException(reply); // parse the status return: address should start at the first number int pos = 4; while ((pos < reply.length()) && ((reply.charAt(pos) < '0') || (reply.charAt(pos) > '9'))) pos++; if (pos >= reply.length()) throw new IOException(reply + " [could not parse return code]"); reply = reply.substring(pos); pos = reply.length() - 1; while ((pos >= 0) && ((reply.charAt(pos) < '0') || (reply.charAt(pos) > '9'))) pos--; if (pos < 0) throw new IOException("[could not parse return code: no numbers]"); reply = reply.substring(0, pos + 1); StringTokenizer st = new StringTokenizer(reply, ","); if (st.countTokens() != 6) throw new IOException("[could not parse return code: wrong number of numbers]"); // set the data host and port int a = Integer.parseInt(st.nextToken()); int b = Integer.parseInt(st.nextToken()); int c = Integer.parseInt(st.nextToken()); int d = Integer.parseInt(st.nextToken()); InetAddress datahost = InetAddress.getByName(a + "." + b + "." + c + "." + d); int high = Integer.parseInt(st.nextToken()); int low = Integer.parseInt(st.nextToken()); if (high < 0 || high > 255 || low < 0 || low > 255) throw new IOException("[could not parse return code: syntax error]"); int dataport = (high << 8) + low; DataSocketPassive = new Socket(datahost, dataport); DataSocketPassiveMode = true; } private void get(String fileDest, String fileName) throws IOException { // store time for statistics long start = Calendar.getInstance(GMTTimeZone).getTime().getTime(); // prepare data channel if (DataSocketPassiveMode) createPassiveDataPort(); else createActiveDataPort(); // set type of the transfer sendTransferType(transferType); // send command to the control port send("RETR " + fileName); // read status of the command from the control port String reply = receive(); // get status code int status = Integer.parseInt(reply.substring(0, 1)); // starting data transaction if (status == 1) { Socket data; if (DataSocketPassiveMode) { data = DataSocketPassive; } else { data = DataSocketActive.accept(); } InputStream ClientStream = data.getInputStream(); // create local file RandomAccessFile outFile; if (fileDest == null) outFile = new RandomAccessFile(fileName, "rw"); else outFile = new RandomAccessFile(fileDest, "rw"); // write remote file to local file byte[] block = new byte[blockSize]; int numRead; long length = 0; while ((numRead = ClientStream.read(block)) != -1) { outFile.write(block, 0, numRead); length = length + numRead; } // after stream is empty we should get control completion echo //reply = receive(); //boolean success = (Integer.parseInt(reply.substring(0, 1)) == 2); // shutdown connection outFile.close(); ClientStream.close(); data.close(); //if (!success) throw new IOException(reply); // write statistics long stop = Calendar.getInstance(GMTTimeZone).getTime().getTime(); out.print("---- downloaded " + ((length < 2048) ? length + " bytes" : ((int) length / 1024) + " kbytes") + " in " + (((stop - start) < 2000) ? (stop - start) + " milliseconds" : (((int) ((stop - start) / 100)) / 10) + " seconds")); if (start == stop) err.println(logPrefix + ""); else out.println(logPrefix + " (" + (length * 1000 / 1024 / (stop - start)) + " kbytes/second)"); } else throw new IOException(reply); } private void put(String fileName, String fileDest) throws IOException { // prepare data channel if (DataSocketPassiveMode) createPassiveDataPort(); else createActiveDataPort(); // set type of the transfer sendTransferType(transferType); // send command to the control port if (fileDest == null) send("STOR " + fileName); else send("STOR " + fileDest); // read status of the command from the control port String reply = receive(); // starting data transaction if (Integer.parseInt(reply.substring(0, 1)) == 1) { // ftp server initiated client connection Socket data; if (DataSocketPassiveMode) { data = DataSocketPassive; } else { data = DataSocketActive.accept(); } OutputStream ClientStream = data.getOutputStream(); // read from local file RandomAccessFile inFile = new RandomAccessFile(fileName, "r"); // write remote file to local file byte[] block = new byte[blockSize]; int numRead; while ((numRead = inFile.read(block)) >= 0) { ClientStream.write(block, 0, numRead); } // shutdown and cleanup inFile.close(); ClientStream.close(); // after stream is empty we should get control completion echo reply = receive(); boolean success = (Integer.parseInt(reply.substring(0, 1)) == 2); // shutdown remote client connection data.close(); if (!success) throw new IOException(reply); } else throw new IOException(reply); } private void login(String account, String password) throws IOException { // send user name send("USER " + account); String reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) == 4) throw new IOException(reply); if (Integer.parseInt(reply.substring(0, 1)) == 2) { this.account = account; this.password = password; this.remotegreeting = reply; return; } // send password send("PASS " + password); reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) != 2) throw new IOException(reply); this.account = account; this.password = password; this.remotegreeting = reply; } public void sys() throws IOException { // send system command send("SYST"); // check completion String systemType = receive(); if (Integer.parseInt(systemType.substring(0, 1)) != 2) throw new IOException(systemType); // exclude status code from reply this.remotesystem = systemType.substring(4); } private void literal(String commandLine) throws IOException { // send the complete line send(commandLine); // read reply String reply = receive(); if (Integer.parseInt(reply.substring(0, 1)) == 5) throw new IOException(reply); } public int getTimeout() { return this.ControlSocketTimeout; } public void setTimeout(int timeout) { this.ControlSocketTimeout = timeout; } class ee extends SecurityException { private static final long serialVersionUID = 1L; private int value = 0; public ee() {} public ee(int value) { super(); this.value = value; } public int value() { return value; } } class sm extends SecurityManager { public void checkCreateClassLoader() { } public void checkAccess(Thread g) { } public void checkAccess(ThreadGroup g) { } public void checkExit(int status) { //System.out.println(logPrefix + "ShellSecurityManager: object called System.exit(" + status + ")"); // signal that someone is trying to terminate the JVM. throw new ee(status); } public void checkExec(String cmd) { } public void checkLink(String lib) { } public void checkRead(FileDescriptor fd) { } public void checkRead(String file) { } public void checkRead(String file, Object context) { } public void checkWrite(FileDescriptor fd) { } public void checkWrite(String file) { } public void checkDelete(String file) { } public void checkConnect(String host, int port) { } public void checkConnect(String host, int port, Object context) { } public void checkListen(int port) { } public void checkAccept(String host, int port) { } public void checkMulticast(InetAddress maddr) { } //public void checkMulticast(InetAddress maddr, byte ttl) { } public void checkPropertiesAccess() { } public void checkPropertyAccess(String key) { } public void checkPropertyAccess(String key, String def) { } public boolean checkTopLevelWindow(Object window) { return true; } public void checkPrintJobAccess() { } public void checkSystemClipboardAccess() { } public void checkAwtEventQueueAccess() { } public void checkPackageAccess(String pkg) { } public void checkPackageDefinition(String pkg) { } public void checkSetFactory() { } public void checkMemberAccess(Class clazz, int which) { } public void checkSecurityAccess(String provider) { } } public static Vector dir(String host, String remotePath, String account, String password, boolean extended) { try { ftpc c = new ftpc(); c.cmd = new String[]{"open", host}; c.OPEN(); c.cmd = new String[]{"user", account, password}; c.USER(); c.cmd = new String[]{"ls"}; Vector v = c.list(remotePath, extended); c.cmd = new String[]{"close"}; c.CLOSE(); c.cmd = new String[]{"exit"}; c.EXIT(); return v; } catch (java.security.AccessControlException e) { return null; } catch (IOException e) { return null; } } public static void dir(String host, String remotePath, String account, String password) { try { ftpc c = new ftpc(); c.exec("open " + host, false); c.exec("user " + account + " " + password, false); c.exec("cd " + remotePath, false); c.exec("ls", true); c.exec("close", false); c.exec("exit", false); } catch (java.security.AccessControlException e) { } } public StringBuffer dirhtml(String remotePath) { // returns a directory listing using an existing connection try { Vector list = list(remotePath, true); if (this.remotesystem == null) sys(); String base = "ftp://" + ((account.equals("anonymous")) ? "" : (account + ":" + password + "@")) + host + ((port == 21) ? "" : (":" + port)) + ((remotePath.charAt(0) == '/') ? "" : "/") + remotePath; return dirhtml(base, this.remotemessage, this.remotegreeting, this.remotesystem, list); } catch (java.security.AccessControlException e) { return null; } catch (IOException e) { return null; } } public static StringBuffer dirhtml(String host, int port, String remotePath, String account, String password) { // opens a new connection and returns a directory listing as html try { ftpc c = new ftpc(System.in, null, System.err); c.open(host, port); c.login(account, password); c.sys(); StringBuffer page = c.dirhtml(remotePath); c.quit(); return page; } catch (java.security.AccessControlException e) { return null; } catch (IOException e) { return null; } } public static StringBuffer dirhtml(String base, String servermessage, String greeting, String system, Vector list) { // this creates the html output from collected strings StringBuffer page = new StringBuffer(1024); String title = "Index of " + base; // find position of filename int filemarker = 999; for (int i = 0; i < list.size(); i++) { filemarker = Math.min(filemarker, ((String) list.elementAt(i)).lastIndexOf(' ')); } filemarker++; page.append("\n"); page.append("\n"); page.append(" " + title + "\n"); page.append(" \n"); page.append(" \n"); page.append("\n"); page.append("

" + title + "

\n"); page.append("

Server \"" + servermessage + "\" responded:\n");
            page.append("  \n");
            page.append(greeting);
            page.append("\n");
            page.append("  

\n"); page.append("
\n"); page.append("
\n");
            for (int i = 0; i < list.size(); i++) {
                String line = (String) list.elementAt(i);
                page.append(line.substring(0, filemarker ));
                page.append(
                        "" +
                        line.substring(filemarker) +
                        "\n");
            }
            page.append("  
\n"); page.append("
\n"); page.append("
System info: \"" + system + "\"
\n"); page.append("\n"); return page; } public static void dirAnonymous(String host, String remotePath) { dir(host, remotePath, "anonymous", "anomic"); } public static void dirAnonymousHtml(String host, int port, String remotePath, String htmloutfile) { StringBuffer page = dirhtml(host, port, remotePath, "anonymous", "anomic"); File file = new File(htmloutfile); FileOutputStream fos; try { fos = new FileOutputStream(file); fos.write((new String(page)).getBytes()); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static String put(String host, File localFile, String remotePath, String remoteName, String account, String password) throws IOException { // returns the log try { ByteArrayOutputStream bout = new ByteArrayOutputStream(); PrintStream out = new PrintStream(bout); ByteArrayOutputStream berr = new ByteArrayOutputStream(); PrintStream err = new PrintStream(berr); ftpc c = new ftpc(System.in, out, err); c.exec("open " + host, false); c.exec("user " + account + " " + password, false); if (remotePath != null) { remotePath = remotePath.replace('\\', '/'); c.exec("cd " + remotePath, false); } c.exec("binary", false); if (localFile.isAbsolute()) { c.exec("lcd \"" + localFile.getParent() + "\"", false); localFile = new File(localFile.getName()); } c.exec("put " + localFile.toString() + ((remoteName.length() == 0) ? "" : (" " + remoteName)), false); c.exec("close", false); c.exec("exit", false); out.close(); err.close(); String outLog = bout.toString(); bout.close(); String errLog = berr.toString(); berr.close(); if (errLog.length() > 0) { throw new IOException ("Ftp put failed:\n" + errLog); } return outLog; } catch (IOException e) { throw e; } } public static void get(String host, String remoteFile, File localPath, String account, String password) { try { ftpc c = new ftpc(); if (remoteFile.length() == 0) remoteFile = "/"; c.exec("open " + host, false); c.exec("user " + account + " " + password, false); c.exec("lcd " + localPath.getAbsolutePath().toString(), false); c.exec("binary", false); c.exec("get " + remoteFile + " " + localPath.getAbsoluteFile().toString(), false); c.exec("close", false); c.exec("exit", false); } catch (java.security.AccessControlException e) { } } public static void getAnonymous(String host, String remoteFile, File localPath) { get(host, remoteFile, localPath, "anonymous", "anomic"); } public static class pt implements Runnable { String host; File localFile; String remotePath; String remoteName; String account; String password; public pt(String h, File l, String rp, String rn, String a, String p) { host = h; localFile = l; remotePath = rp; remoteName = rn; account = a; password = p; } public final void run() { try { put(host, localFile, remotePath, remoteName, account, password); } catch (IOException e) { e.printStackTrace(); } } } public static Thread putAsync(String host, File localFile, String remotePath, String remoteName, String account, String password) { Thread t = new Thread(new pt(host, localFile, remotePath, remoteName, account, password)); t.start(); return t; // return value can be used to determine status of transfer with isAlive() or join() } private static void printHelp() { System.out.println(logPrefix + "ftp help"); System.out.println(logPrefix + "----------"); System.out.println(logPrefix + ""); System.out.println(logPrefix + "The following commands are supported"); System.out.println(logPrefix + "java ftp -- (without arguments) starts the shell. Thy 'help' then for shell commands."); System.out.println(logPrefix + "java ftp [':'] -- starts shell and connects to specified host"); System.out.println(logPrefix + "java ftp -h -- prints this help"); System.out.println(logPrefix + "java ftp -dir [':'] [ ]"); System.out.println(logPrefix + "java ftp -get [':'] [ ]"); System.out.println(logPrefix + "java ftp -put [':'] "); System.out.println(logPrefix + ""); } public static void main(String[] args) { System.out.println(logPrefix + "WELCOME TO THE ANOMIC FTP CLIENT v" + vDATE); System.out.println(logPrefix + "Visit http://www.anomic.de and support shareware!"); System.out.println(logPrefix + "try -h for command line options"); System.out.println(logPrefix + ""); if (args.length == 0) { (new ftpc()).shell(null); } else if (args.length == 1) { if (args[0].equals("-h")) { printHelp(); } else { (new ftpc()).shell(args[0]); } } else if (args.length == 2) { printHelp(); } else if (args.length == 3) { if (args[0].equals("-dir")) { dirAnonymous(args[1], args[2]); } else if (args[0].equals("-htmldir")) { dirAnonymousHtml(args[1], 21, args[2], "dirindex.html"); } else { printHelp(); } } else if (args.length == 4) { if (args[0].equals("-get")) { getAnonymous(args[1], args[2], new File(args[3])); } else { printHelp(); } } else if (args.length == 5) { if (args[0].equals("-dir")) { dir(args[1], args[2], args[3], args[4]); } else { printHelp(); } } else if (args.length == 6) { if (args[0].equals("-get")) { get(args[1], args[2], new File(args[3]), args[4], args[5]); } else if (args[0].equals("-put")) { try { put(args[1], new File(args[2]), args[3], "", args[4], args[5]); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { printHelp(); } } else { printHelp(); } } }