////////////////////////////////////////// // split here for Linlyn.java // // At last! Java code to read/write files on the server from an applet! // This is the famous Linlyn code. // // Use: // compile this file, and have your applet call it as below. // // to upload a file: // Linlyn ftp = new Linlyn( <servername>, <user>, <password> ); // ftp.upload( <directory>, <filename>, <contents of file> ); // // to download a file: // Linlyn ftp = new Linlyn( <servername>, <user>, <password> ); // String contents = ftp.download( <directory>, <filename> ); // // the default is ASCII transfer, an overloaded method does bin. // // All parameters and return values are Strings. E.g. // Linlyn ftp = new Linlyn( "rtfm.mit.edu", "anonymous", "linden@" ); // String contents = ftp.download( // "/pub/usenet-by-group/comp.lang.java.programmer" // "Java_Programmers_FAQ" ); // // [the actual values above are not generally valid, substitute // your own server for your first attempt, see note 1.] // // Notes: // 1. Usual applet security rules apply: you can only get a file // from the server that served the applet. // 2. The applet server must also be an FTP server. This is NOT true // for some ISPs, such as best.com. They have separate FTP and // http machines. This code may work on such a setup if you put // the classfiles into the ftp area, and in the HTML file say: // <applet codebase="ftp:///home/linden/ftp" code="t.class" // 3. This code does not break Java security. // It uses FTP to transfer files. If the author of the applet // has FTP disabled you are out of luck. // It breaks regular system security however, as it publishes // (effectively) your ftp password. Only use on an Intranet and // with authorization. // 4. Compiling this causes some deprecation warnings. We wanted to // stick with code that would work in JDK 1.0 browsers. // 5. Each upload or download creates, uses, and terminates a new // ftp session. This is intended for low volume transfer, such // as the ever popular high-score files. // 6. Look at the source for the methods for binary transfers. // // 7. On the Windows platform (particularly an NT ftp server // accessed through the IE5 browser) we have noticed a Microsoft // bug. Sometimes the file methods: STOR, DELE, APPE, etc. "stall". // The file operation starts, but for some reason never completes. // // The workaround is to nudge the buggy Microsoft server by sending // a "NOOP" command after each file operation, e.g. // ... // ftpSendCmd("STOR "+file); // ftpSendCmd("NOOP"); // ... // Your mileage may vary, just passing on a tip. // // 8. FTP is specified in RFC 959 and 1123. // It is based on the telnet protocol which is RFC 854. // There are more FTP RFC's such as 1579, 1635, 1639 and 2228, // if you are interested in FTP development. // You can find RFCs online in many places, including // http://www.ietf.org // http://www.faqs.org/rfcs/rfc959.html // ftp://ftp.isi.edu/in-notes/rfc959.txt // // Version 1.0 May 6 1998. // Version 1.1 May 20 1998. -- added a debugging flag // Version 1.1a May 26 1998. -- fixed the ASCII/BIN flag inversion // Version 1.1b May 29 1998. -- added the security warning. // Version 2.0 Jul 1, 1998. -- Updated to parse multi-string responses // a la RFC 959 // Version 2.1 Aug 5, 1998. -- Updated to work with VMS ftp servers // VMS does not send either a ")" OR a ")." // terminating the IP number, port sequence // in response to PASV. // Version 2.1a Aug 6, 1998 -- more than one line as a "hello" message. // (tvalesky@patriot.net) // Version 2.2 Sep 22 1998 -- added a flush() in ftpSendCmd. // Version 2.2a Dec 09 1998 -- added comments on compiling w/o deprecation // Version 2.2b May 05 1999 -- added dos.close() to upload // Version 2.2c May 19 1999 -- moved the dos.close line. // Version 2.2c May 23 1999 -- updated comment on license // Version 2.2d Jul 22 1999 -- fixed for weird "XITAMI" servers // Version 3.0 Aug 18 1999 -- added "append to file" feature // Version 3.1 Jan 23 2000 -- submit and finish one request at a time. // Stall further requests, and swallow // multiple responses from server // // Authors: // Robert Lynch // Peter van der Linden (Author of "Just Java" book). // // Support: // Unsupported: That's why we give you the source. // Help may be available on time & materials basis only. // You can get copious debug information by changing to // DEBUG=true below and recompiling. // // Copyright 1998,1999,2000 Robert Lynch, Peter van der Linden // This work is distributed under the ARTISTIC LICENSE listed at the // end of this file. // We (the benevolent and philathropic authors) // hereinafter referred to as "We, the benevolent and philathropic authors" // don't intend to make any money off this, and // this code may be used for commercial purposes without charges or // license fees, provided that this source code is included with the // distribution. // // Those using the code do so at their own risk and the authors // are not responsible for any costs, loss, or damage which may // thereby be incurred. package er.extensions.foundation; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.util.NoSuchElementException; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Can upload and download files from ftp servers */ public class ERXLinlyn { private static final Logger log = LoggerFactory.getLogger(ERXLinlyn.class); // FOR DEBUGGING: set the variable to "true" private boolean DEBUG = false; // constructor needs servername, username and passwd public ERXLinlyn(String server, String user, String pass) { this(server, DEFAULT_CNTRL_PORT, user, pass); } public ERXLinlyn(String server, int portNum, String user, String pass) { try { ftpConnect(server, portNum); ftpLogin(user, pass); } catch(IOException ioe) {ioe.printStackTrace();} } /** * Returns a list of files and associated attributes in a given directory. * There is no FTP-server independent way to retrieve only the file name * portion of the result, but calling * substring(lastIndexOf(' ')+1) * on each element in the array will work so long as there are no spaces in filenames. */ public String[] listFiles(String dir) throws IOException { ftpSetTransferType(false); dsock = ftpGetDataSock(); InputStream is = dsock.getInputStream(); ftpSendCmd("LIST "+dir); String contents = getAsString(is); log.debug(contents); String[] files = contents.split("\n"); return files; } public String download(String dir, String file) throws IOException { return download(dir, file, true); } public String download(String dir, String file, boolean asc) throws IOException { return download(dir, file, asc, true); } public String download(String dir, String file, boolean asc, boolean keepAlive) throws IOException { ftpSetDir(dir); ftpSetTransferType(asc); dsock = ftpGetDataSock(); InputStream is = dsock.getInputStream(); ftpSendCmd("RETR "+file); String contents = getAsString(is); if (!keepAlive) { ftpLogout(); } return contents; } public void append(String dir, String file, String what, boolean asc) throws IOException { ftpSetDir(dir); ftpSetTransferType(asc); dsock = ftpGetDataSock(); OutputStream os = dsock.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); ftpSendCmd("APPE "+file); dos.writeBytes(what); dos.flush(); dos.close(); ftpLogout(); } public void upload(String dir, String file, String what) throws IOException { upload(dir, file, what, true); } public void upload(String dir, String file, byte[] bytes) throws IOException { upload(dir, file, bytes, true); } public void upload(String dir, String file, byte[] bytes, boolean keepAlive) throws IOException { ftpSetDir(dir); ftpSetTransferType(false); dsock = ftpGetDataSock(); OutputStream os = dsock.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); ftpSendCmd("STOR "+file); dos.write(bytes); dos.flush(); dos.close(); if (!keepAlive) { ftpLogout(); } } public void upload(String dir, String file, String what, boolean asc) throws IOException { upload(dir, file, what, asc, true); } public void upload(String dir, String file, String what, boolean asc, boolean keepAlive) throws IOException { ftpSetDir(dir); ftpSetTransferType(asc); dsock = ftpGetDataSock(); OutputStream os = dsock.getOutputStream(); DataOutputStream dos = new DataOutputStream(os); ftpSendCmd("STOR "+file); dos.writeBytes(what); dos.flush(); dos.close(); if (!keepAlive) { ftpLogout(); } } /** * Removes a file from a directory on the server. * @return true if the file was deleted (response 250), false otherwise. */ public boolean deleteFile(String dir, String file) throws IOException { return deleteFile(dir, file, true); } public boolean deleteFile(String dir, String file, boolean keepAlive) throws IOException { ftpSetDir(dir); String response = ftpSendCmd("DELE "+file); if (!keepAlive) { ftpLogout(); } if (response.startsWith("250")) { return true; } return false; } ///////////////// private fields //////////////////// private boolean pauser = false; // it's a hack. We're going to // stall (refuse further requests) till we get a reply back // from server for the current request. private String getAsString(InputStream is) { int c=0; int doubler = 65536; char lineBuffer[]=new char[doubler], buf[]=lineBuffer; int room= buf.length, offset=0; try { loop: while (true) { // read chars into a buffer which grows as needed switch (c = is.read() ) { case -1: break loop; default: if (--room < 0) { log.debug("Growing array by: {}", doubler); buf = new char[offset + doubler]; doubler *= 2; // double the size of the array each time it grows. room = buf.length - offset - 1; System.arraycopy(lineBuffer, 0, buf, 0, offset); lineBuffer = buf; } buf[offset++] = (char) c; break; } } } catch(IOException ioe) {ioe.printStackTrace();} if ((c == -1) && (offset == 0)) { return null; } return String.copyValueOf(buf, 0, offset); } private void ftpConnect(String server, int portNum) throws IOException { // Set up socket, control streams, connect to ftp server // Open socket to server control port (default is 21) csock = new Socket(server, portNum); // Open control streams InputStream cis = csock.getInputStream(); dcis = new BufferedReader(new InputStreamReader(cis)); OutputStream cos = csock.getOutputStream(); pos = new PrintWriter(cos, true); // set auto flush true. // See if server is alive or dead... String numerals = responseHandler(null); if(numerals.substring(0,3).equals("220")) { // ftp server alive // System.out.println("Connected to ftp server"); } else { System.err.println("Error connecting to ftp server."); } } private void ftpLogin(String user, String pass) throws IOException { ftpSendCmd("USER "+user); ftpSendCmd("PASS "+pass); } private void ftpSetDir(String dir) throws IOException { // cwd to dir ftpSendCmd("CWD "+dir); } private void ftpSetTransferType(boolean asc) throws IOException { // set file transfer type String ftype = (asc? "A" : "I"); ftpSendCmd("TYPE "+ftype); } private Socket ftpGetDataSock() throws IOException { // Go to PASV mode, capture server reply, parse for socket setup // V2.1: generalized port parsing, allows more server variations String reply = ftpSendCmd("PASV"); // New technique: just find numbers before and after ","! StringTokenizer st = new StringTokenizer(reply, ","); String[] parts = new String[6]; // parts, incl. some garbage int i = 0; // put tokens into String array while(st.hasMoreElements()) { // stick pieces of host, port in String array try { parts[i] = st.nextToken(); i++; } catch(NoSuchElementException nope){nope.printStackTrace();} } // end getting parts of host, port // Get rid of everything before first "," except digits String[] possNum = new String[3]; for(int j = 0; j < 3; j++) { // Get 3 characters, inverse order, check if digit/character possNum[j] = parts[0].substring(parts[0].length() - (j + 1), parts[0].length() - j); // next: digit or character? if(!Character.isDigit(possNum[j].charAt(0))) possNum[j] = ""; } parts[0] = possNum[2] + possNum[1] + possNum[0]; // Get only the digits after the last "," String[] porties = new String[3]; for(int k = 0; k < 3; k++) { // Get 3 characters, in order, check if digit/character // May be less than 3 characters if((k + 1) <= parts[5].length()) porties[k] = parts[5].substring(k, k + 1); else porties[k] = "FOOBAR"; // definitely not a digit! // next: digit or character? if(!Character.isDigit(porties[k].charAt(0))) porties[k] = ""; } // Have to do this one in order, not inverse order parts[5] = porties[0] + porties[1] + porties[2]; // Get dotted quad IP number first String ip = parts[0]+"."+parts[1]+"."+parts[2]+"."+parts[3]; // Determine port int port = -1; try { // Get first part of port, shift by 8 bits. int big = Integer.parseInt(parts[4]) << 8; int small = Integer.parseInt(parts[5]); port = big + small; // port number } catch(NumberFormatException nfe) {nfe.printStackTrace();} if((ip != null) && (port != -1)) dsock = new Socket(ip, port); else throw new IOException(); return dsock; } private String ftpSendCmd(String cmd) throws IOException { // This sends a dialog string to the server, returns reply // V2.0 Updated to parse multi-string responses a la RFC 959 // Prints out only last response string of the lot. if (pauser) // i.e. we already issued a request, and are // waiting for server to reply to it. { if (dcis != null) { String discard = dcis.readLine(); // will block here // preventing this further client request until server // responds to the already outstanding one. if (DEBUG) { System.out.println("keeping handler in sync"+ " by discarding next response: "); System.out.println(discard); } pauser = false; } } pos.print(cmd + "\r\n" ); pos.flush(); String response = responseHandler(cmd); log.debug("command = {}", cmd); log.debug("response = {}", response); return response; } // new method to read multi-line responses // responseHandler: takes a String command or null and returns // just the last line of a possibly multi-line response private String responseHandler(String cmd) throws IOException { // handle more than one line returned String reply = responseParser(dcis.readLine()); String numerals = reply.substring(0, 3); String hyph_test = reply.substring(3, 4); String next = null; if(hyph_test.equals("-")) { // Create "tester", marks end of multi-line output String tester = numerals + " "; boolean done = false; while(!done) { // read lines til finds last line next = dcis.readLine(); // Read "over" blank line responses while (next.equals("") || next.equals(" ")) { next = dcis.readLine(); } // If next starts with "tester", we're done if(next.substring(0,4).equals(tester)) done = true; } if(DEBUG) if(cmd != null) System.out.println("Response to: "+cmd+" was: "+next); else System.out.println("Response was: "+next); return next; } else // "if (hyph_test.equals("-")) not true" if(DEBUG) if(cmd != null) System.out.println("Response to: "+cmd+" was: "+reply); else System.out.println("Response was: "+reply); return reply; } // responseParser: check first digit of first line of response // and take action based on it; set up to read an extra line // if the response starts with "1" private String responseParser(String resp) throws IOException { // Check first digit of resp, take appropriate action. String digit1 = resp.substring(0, 1); if(digit1.equals("1")) { // server to act, then give response if(DEBUG) System.out.println("in 1 handler"); // set pauser pauser = true; return resp; } else if(digit1.equals("2")) { // do usual handling if(DEBUG) System.out.println("in 2 handler"); // reset pauser pauser = false; return resp; } else if(digit1.equals("3") || digit1.equals("4") || digit1.equals("5")) { // do usual handling if(DEBUG) System.out.println("in 3-4-5 handler"); return resp; } else { // not covered, so return null return null; } } private void ftpLogout() {// logout, close streams try { if(DEBUG) System.out.println("sending BYE"); pos.print("BYE" + "\r\n" ); pos.flush(); pos.close(); dcis.close(); csock.close(); dsock.close(); } catch(IOException ioe) {ioe.printStackTrace();} } private static final int DEFAULT_CNTRL_PORT = 21; private Socket csock = null; private Socket dsock = null; private BufferedReader dcis; private PrintWriter pos; } ////////////////////////////////////////////////////////////////////// // // The "Artistic License" (from a KDE module) // // Preamble // // The intent of this document is to state the conditions under which this // software may be copied, such that the Copyright Holder maintains some // semblance of artistic control over the development of the software, // while giving the users of the software the right to use and distribute // the software in a more-or-less customary fashion, plus the right to make // reasonable modifications. // // Definitions: // // "Package" refers to the collection of files distributed by the // Copyright Holder, and derivatives of that collection of files // created through textual modification. // // "Standard Version" refers to such a Package if it has not been // modified, or has been modified in accordance with the wishes // of the Copyright Holder as specified below. // // "Copyright Holder" is whoever is named in the copyright or // copyrights for the package. // // "You" is you, if you're thinking about copying or distributing // this Package. // // "Reasonable copying fee" is whatever you can justify on the // basis of media cost, duplication charges, time of people involved, // and so on. (You will not be required to justify it to the // Copyright Holder, but only to the computing community at large // as a market that must bear the fee.) // // "Freely Available" means that no fee is charged for the item // itself, though there may be fees involved in handling the item. // It also means that recipients of the item may redistribute it // under the same conditions they received it. // // 1. You may make and give away verbatim copies of the source form of the // Standard Version of this Package without restriction, provided that you // duplicate all of the original copyright notices and associated disclaimers. // // 2. You may apply bug fixes, portability fixes and other modifications // derived from the Public Domain or from the Copyright Holder. A Package // modified in such a way shall still be considered the Standard Version. // // 3. You may otherwise modify your copy of this Package in any way, provided // that you insert a prominent notice in each changed file stating how and // when you changed that file, and provided that you do at least ONE of the // following: // // a) place your modifications in the Public Domain or otherwise make them // Freely Available, such as by posting said modifications to Usenet or // an equivalent medium, or placing the modifications on a major archive // site such as uunet.uu.net, or by allowing the Copyright Holder to include // your modifications in the Standard Version of the Package. // // b) use the modified Package only within your corporation or organization. // // c) rename any non-standard executables so the names do not conflict // with standard executables, which must also be provided, and provide // a separate manual page for each non-standard executable that clearly // documents how it differs from the Standard Version. // // d) make other distribution arrangements with the Copyright Holder. // // 4. You may distribute the programs of this Package in object code or // executable form, provided that you do at least ONE of the following: // // a) distribute a Standard Version of the executables and library files, // together with instructions (in the manual page or equivalent) on where // to get the Standard Version. // // b) accompany the distribution with the machine-readable source of // the Package with your modifications. // // c) give non-standard executables non-standard names, and clearly // document the differences in manual pages (or equivalent), together // with instructions on where to get the Standard Version. // // d) make other distribution arrangements with the Copyright Holder. // // 5. You may charge a reasonable copying fee for any distribution of this // Package. You may charge any fee you choose for support of this // Package. You may not charge a fee for this Package itself. However, // you may distribute this Package in aggregate with other (possibly // commercial) programs as part of a larger (possibly commercial) software // distribution provided that you do not advertise this Package as a // product of your own. You may embed this Package's interpreter within // an executable of yours (by linking); this shall be construed as a mere // form of aggregation, provided that the complete Standard Version of the // interpreter is so embedded. // // 6. The scripts and library files supplied as input to or produced as // output from the programs of this Package do not automatically fall // under the copyright of this Package, but belong to whomever generated // them, and may be sold commercially, and may be aggregated with this // Package. If such scripts or library files are aggregated with this // Package via the so-called "undump" or "unexec" methods of producing a // binary executable image, then distribution of such an image shall // neither be construed as a distribution of this Package nor shall it // fall under the restrictions of Paragraphs 3 and 4, provided that you do // not represent such an executable image as a Standard Version of this // Package. // // 7. C subroutines (or comparably compiled subroutines in other // languages) supplied by you and linked into this Package in order to // emulate subroutines and variables of the language defined by this // Package shall not be considered part of this Package, but are the // equivalent of input as in Paragraph 6, provided these subroutines do // not change the language in any way that would cause it to fail the // regression tests for the language. // // 8. Aggregation of this Package with a commercial distribution is always // permitted provided that the use of this Package is embedded; that is, // when no overt attempt is made to make this Package's interfaces visible // to the end user of the commercial distribution. Such use shall not be // construed as a distribution of this Package. // // 9. The name of the Copyright Holder may not be used to endorse or // promote products derived from this software without specific prior // written permission. // // 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED // WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // 11. You must not be Microsoft, or currently be employed by any // company that is partly or wholly owned by Microsoft, if there are // any companies left in the software industry for which this is true. // // The End