001: /**
002: * $Id: FullFtpClient.java,v 1.19 2006/10/31 12:52:05 ss150821 Exp $
003: * Copyright 2002 Sun Microsystems, Inc. All
004: * rights reserved. Use of this product is subject
005: * to license terms. Federal Acquisitions:
006: * Commercial Software -- Government Users
007: * Subject to Standard License Terms and
008: * Conditions.
009: *
010: * Sun, Sun Microsystems, the Sun logo, and Sun ONE
011: * are trademarks or registered trademarks of Sun Microsystems,
012: * Inc. in the United States and other countries.
013: */package com.sun.portal.netfile.servlet.java2;
014:
015: /**
016: * This class implements a FTP client.
017: *
018: * @version 1.0, 12/12/2001
019: * @author Suresh Yellamaraju and Ali Baqri
020: */
021:
022: import java.io.BufferedInputStream;
023: import java.io.FileNotFoundException;
024: import java.io.IOException;
025: import java.io.OutputStreamWriter;
026: import java.net.InetAddress;
027: import java.net.ServerSocket;
028: import java.net.Socket;
029: import java.net.UnknownHostException;
030: import java.util.Enumeration;
031: import java.util.StringTokenizer;
032: import java.util.Vector;
033: import java.util.logging.Level;
034: import java.util.logging.Logger;
035:
036: import sun.net.TelnetInputStream;
037: import sun.net.TelnetOutputStream;
038: import sun.net.TransferProtocolClient;
039:
040: import com.sun.portal.log.common.PortalLogger;
041:
042: public class FullFtpClient extends TransferProtocolClient {
043:
044: private static final String LOOPBACK_IP = "127.0.0.1";
045: private String machineName = null;
046: private static final String HOST_NOT_FOUND = "Server Address could not be resolved";
047: private static final String HOST_NO_MATCH = "Server IP Address does not match an network address of iPS";
048:
049: public static final int FTP_PORT = 21;
050:
051: static int FTP_SUCCESS = 1;
052: static int FTP_TRY_AGAIN = 2;
053: static int FTP_ERROR = 3;
054:
055: /* socket for data transfer */
056: private Socket sock = null;
057:
058: private boolean replyPending = false;
059: private boolean binaryMode = true;
060:
061: /* user name for login */
062: String user = null;
063:
064: /* password for login */
065: String password = null;
066:
067: /* last command issued */
068: String command;
069:
070: /* The last reply code from the ftp daemon. */
071: int lastReplyCode;
072:
073: /* Welcome message from the server, if any. */
074: public String welcomeMsg;
075:
076: /* Machine encoding to use with the server */
077: public String machine_encoding = "UTF8";
078:
079: /* OutputStreamWriter to server */
080: OutputStreamWriter opsw_server;
081:
082: private static Logger logger = PortalLogger
083: .getLogger(FullFtpClient.class);
084:
085: private void setDebug() {
086: }
087:
088: /* New FullFtpClient connected to host <i>host</i>. */
089: public FullFtpClient(String host, String machine_encoding)
090: throws IOException {
091: this (host, FTP_PORT, machine_encoding);
092: }
093:
094: /* Create an uninitialized FullFTP client. */
095: public FullFtpClient(String host, int port, String machine_encoding)
096: throws IOException {
097: super ();
098: setDebug();
099: this .machine_encoding = machine_encoding;
100: openServerOnPort(host, port);
101: }
102:
103: /* Move up one directory in the ftp file system */
104: public void cdup() throws IOException {
105: issueCommandCheck("CDUP");
106: }
107:
108: /* Create a new directory named s in the ftp file system */
109: public void mkdir(String s) throws IOException {
110: issueCommandCheck("MKD " + s);
111: }
112:
113: /* Delete the specified directory from the ftp file system */
114: public void rmdir(String s) throws IOException {
115: issueCommandCheck("RMD " + s);
116: }
117:
118: /* Delete the file s from the ftp file system */
119: public void delete(String s) throws IOException {
120: issueCommandCheck("DELE " + s);
121: }
122:
123: /* Rename a file from - to */
124: public void rename(String old_name, String new_name)
125: throws IOException {
126: issueCommandCheck("RNFR " + old_name);
127: issueCommandCheck("RNTO " + new_name);
128: }
129:
130: /* Get the name of the present working directory on the ftp file system */
131: public String pwd() throws IOException {
132: issueCommandCheck("PWD");
133: StringBuffer result = new StringBuffer();
134: for (Enumeration e = serverResponse.elements(); e
135: .hasMoreElements();) {
136: result.append((String) e.nextElement());
137: }
138: return result.toString();
139:
140: }
141:
142: /* PUT a file to the FTP server */
143: public TelnetOutputStream put(String filename) throws IOException {
144: sock = openDataConnection("STOR " + filename);
145:
146: return new TelnetOutputStream(sock.getOutputStream(),
147: binaryMode);
148: }
149:
150: /* APPEND to a file on the FTP server */
151: public TelnetOutputStream append(String filename)
152: throws IOException {
153: sock = openDataConnection("APPE " + filename);
154:
155: return new TelnetOutputStream(sock.getOutputStream(),
156: binaryMode);
157: }
158:
159: /* LIST files on a remote FTP server */
160: public TelnetInputStream list() throws IOException {
161: sock = openDataConnection("LIST");
162:
163: return new TelnetInputStream(sock.getInputStream(), binaryMode);
164: }
165:
166: /* CD to a specific directory on a remote FTP server */
167: public void cd(String remoteDirectory) throws IOException {
168: issueCommandCheck("CWD " + remoteDirectory);
169: }
170:
171: /** Set transfer type to 'I' */
172: public void binary() throws IOException {
173: issueCommandCheck("TYPE I");
174: binaryMode = true;
175: }
176:
177: /** Set transfer type to 'A' */
178: public void ascii() throws IOException {
179: issueCommandCheck("TYPE A");
180: binaryMode = false;
181: }
182:
183: /* Bug 4452946 */
184: public sun.net.TelnetInputStream nlist() throws java.io.IOException {
185: java.net.Socket s = openDataConnection("NLST -a");
186:
187: return new sun.net.TelnetInputStream(s.getInputStream(), false);
188: }
189:
190: public sun.net.TelnetInputStream list(boolean list_al)
191: throws java.io.IOException {
192: java.net.Socket s = openDataConnection("LIST -al");
193:
194: return new sun.net.TelnetInputStream(s.getInputStream(), false);
195: }
196:
197: public sun.net.TelnetInputStream list(String filename)
198: throws java.io.IOException {
199: java.net.Socket s = openDataConnection("LIST -al " + filename);
200:
201: return new sun.net.TelnetInputStream(s.getInputStream(), false);
202: }
203:
204: /* Issue QUIT command to the FTP server and close the connection. */
205: public void quit() throws IOException {
206: if (serverIsOpen()) {
207: issueCommand("QUIT");
208: this .closeServer();
209: }
210: }
211:
212: /* Login user to a host with username and password */
213: public void login(String user, String password) throws IOException {
214:
215: if (!serverIsOpen())
216: throw new IOException("not connected to host");
217: this .user = user;
218: this .password = password;
219: if (issueCommand("USER " + user) == FTP_ERROR)
220: throw new IOException("user");
221: if (password != null
222: && issueCommand("PASS " + password) == FTP_ERROR)
223: throw new IOException("password");
224: String l;
225: for (int i = 0; i < serverResponse.size(); i++) {
226: l = (String) serverResponse.elementAt(i);
227: if (l != null) {
228: if (l.charAt(3) != '-') {
229: break;
230: }
231: // get rid of the "230-" prefix
232: l = l.substring(4);
233: if (welcomeMsg == null) {
234: welcomeMsg = l;
235: } else {
236: welcomeMsg += l;
237: }
238: }
239: }
240: }
241:
242: /* GET a file from the FTP server */
243: public TelnetInputStream get(String filename) throws IOException {
244: try {
245: sock = openDataConnection("RETR " + filename);
246: } catch (FileNotFoundException fileException) {
247: StringTokenizer t = new StringTokenizer(filename, "/");
248: String pathElement = null;
249:
250: while (t.hasMoreElements()) {
251: pathElement = t.nextToken();
252:
253: if (!t.hasMoreElements()) {
254: /* This is the file component. Look it up now. */
255: break;
256: }
257: try {
258: cd(pathElement);
259: } catch (IOException e) {
260: /* Giving up. */
261: throw fileException;
262: }
263: }
264: if (pathElement != null) {
265: sock = openDataConnection("RETR " + pathElement);
266: } else {
267: throw fileException;
268: }
269: }
270:
271: return new TelnetInputStream(sock.getInputStream(), binaryMode);
272: }
273:
274: //over riding the base class to fix the multihomed portal server
275: protected Socket openDataConnection(String s) throws IOException {
276: //these two defined in the super class( but not available here) hence defineing again
277: int FTP_ERROR = 3;
278: int numOtherNames = 0, count = 0;
279: Socket dataSocket = null;
280: ServerSocket dataServerSock = null;
281: InetAddress theAddress = null;
282: int foundFlag = 0;
283: byte abyte0[] = null;
284: String interfaceIP_touse = null;
285: String ftpServerName = null;
286: Vector ipaddresses = null;
287: java.net.InetAddress otherName = null;
288:
289: ftpServerName = (String) this .getMachineToAccess();
290:
291: if (ftpServerName != null) {
292: try {
293: interfaceIP_touse = (String) NetFileServlet
294: .getInterface(ftpServerName);
295: } catch (NullPointerException npe) {
296: writeDebug("Interface to use for "
297: + ftpServerName
298: + " not cached. Interface determination started.");
299: }
300: /**
301: * Interface to use with the given server name not available
302: * So call findInterface to determine interface to use with this given server name
303: */
304: if (interfaceIP_touse != null) {
305: dataSocket = this .openCachedDataConnection(s,
306: interfaceIP_touse);
307: return dataSocket;
308: }
309: }
310:
311: ipaddresses = NetFileServlet.getInterfaceIPs();
312: if (ipaddresses == null) {
313: IOException ftpprotocolexception1 = new IOException(
314: "NO INTERFACES");
315: throw ftpprotocolexception1;
316: }
317:
318: /**
319: * Get the interfaceIP to be used.
320: * If the name of the FTP Server matches including the subnet with the interface's IP,
321: * then call openCachedDataConnection with that interface's IP.
322: *
323: * If Name of the FTP Server cannot be resolved, then go ahead and try on all interfaces sequentially.
324: *
325: * If IP of the FTP Server does not match upto and including the subnet, then once again go ahead
326: * and try on all interfaces sequentially.
327: *
328: * In sequential access, the best case could be that FTP Server is reached on first interface and
329: * the worst case, the FTP Server is reached through the last interface(timeout likely).
330: */
331: interfaceIP_touse = this .findInterface(ftpServerName,
332: ipaddresses);
333: if (interfaceIP_touse.equalsIgnoreCase(HOST_NO_MATCH)) {
334: writeDebug(ftpServerName
335: + " does not match any subnetwork of iPS. Going ahead and trying on all interfaces");
336: numOtherNames = ipaddresses.size();
337: } else if (interfaceIP_touse.equalsIgnoreCase(HOST_NOT_FOUND)) {
338: writeDebug(ftpServerName
339: + " could not be resolved/found. Going ahead and trying on all interfaces");
340: numOtherNames = ipaddresses.size();
341: } else {
342: numOtherNames = 1;
343: dataSocket = this .openCachedDataConnection(s,
344: interfaceIP_touse);
345: /**
346: * For future use, set the name of server as key and the interface IP to use as value.
347: */
348: NetFileServlet.setInterface(ftpServerName,
349: interfaceIP_touse);
350: return dataSocket;
351: }
352:
353: writeDebug("Number of Interfaces available: " + numOtherNames);
354:
355: int iteration = 0;
356: Vector ipaddresses1 = (Vector) ipaddresses.clone();
357: ipaddresses1.remove(LOOPBACK_IP);
358:
359: do {
360: otherName = java.net.InetAddress
361: .getByName((String) ipaddresses1.get(iteration));
362: theAddress = otherName;
363: abyte0 = theAddress.getAddress();
364: boolean boundFlag = false;
365: /**
366: * Keep Trying till you bind the socket on a port.
367: * unlikely that the socket will never find a port to bind on to - hence the loop till bound.
368: */
369: while (!boundFlag) {
370: try {
371: dataServerSock = new ServerSocket(0, 1, theAddress);
372: foundFlag = 1;
373: boundFlag = true;
374: } catch (java.net.BindException be) {
375: writeDebug("Could not bind on HostAddress "
376: + theAddress.getHostAddress()
377: + ". Address is " + theAddress.getAddress());
378: foundFlag = 0;
379: }
380: }
381:
382: String s1 = "PORT ";
383: /**
384: * PORT h1,h2,h3,h4,p1,p2
385: * First the Host, then the Port
386: */
387: for (int i = 0; i < abyte0.length; i++)
388: s1 = s1 + (abyte0[i] & 0xff) + ",";
389:
390: s1 = s1 + (dataServerSock.getLocalPort() >>> 8 & 0xff)
391: + "," + (dataServerSock.getLocalPort() & 0xff);
392: writeDebug("Port Command Issued: " + s1);
393: writeDebug("Host Name Socket is listening on: "
394: + dataServerSock.getInetAddress().getHostName());
395: writeDebug("Port Socket is listening on: "
396: + dataServerSock.getLocalPort());
397: writeDebug("IP Address socket is listening on: "
398: + dataServerSock.getInetAddress().getHostAddress());
399:
400: //throwing IOException instead of FtpProtocolException as that is not public
401: if (issueCommand(s1) == FTP_ERROR) {
402: writeDebug("Port Command Failed");
403: IOException ftpprotocolexception = new IOException(
404: "PORT");
405: if (dataServerSock != null)
406: dataServerSock.close();
407: foundFlag = FTP_ERROR;
408: throw ftpprotocolexception;
409: }
410: if (issueCommand(s) == FTP_ERROR) {
411: writeDebug("Data Command sent. Output data from FTP Server not received.");
412: /**
413: * The FTP Server could not reach the data socket and port on which we are listening.
414: * So try and listen on another IP and port for output.
415: */
416: numOtherNames--;
417: foundFlag = 0;
418: if ((numOtherNames == 0) && (foundFlag != 1)) {
419: IOException ftpprotocolexception = new IOException(
420: "HOST");
421: writeDebug(ftpprotocolexception.getMessage());
422: if (dataServerSock != null)
423: dataServerSock.close();
424: }
425: } else {
426: writeDebug("Output Data has arrived. So accepting it");
427: /**
428: * Data is to be received. So accept it and close the data socket.
429: * Also set the foundFlag to 1
430: */
431: dataSocket = dataServerSock.accept();
432: dataServerSock.close();
433: /**
434: * For future use, set the name of server as key and the interface IP to use as value.
435: */
436: NetFileServlet.setInterface((String) this
437: .getMachineToAccess(), (String) ipaddresses1
438: .get(iteration++));
439: foundFlag = 1;
440: }
441: } while ((foundFlag == 0) && (numOtherNames != 0));
442: return dataSocket;
443: }
444:
445: protected Socket openCachedDataConnection(String s,
446: String interface_touse) throws IOException {
447: int FTP_ERROR = 3;
448: boolean boundFlag = false;
449: java.net.ServerSocket dataServerSock = null;
450: Socket dataSocket = null;
451: byte[] abyte0 = null;
452:
453: java.net.InetAddress theAddress = java.net.InetAddress
454: .getByName(interface_touse);
455: abyte0 = theAddress.getAddress();
456: /**
457: * Keep Trying till you bind the socket on a port.
458: * unlikely that the socket will never find a port to bind on to - hence the loop till bound.
459: */
460: while (!boundFlag) {
461: try {
462: dataServerSock = new ServerSocket(0, 1, theAddress);
463: boundFlag = true;
464: } catch (java.net.BindException be) {
465: writeDebug("Could not bind on HostAddress "
466: + theAddress.getHostAddress()
467: + ". Address is " + theAddress.getAddress());
468: }
469: }
470:
471: String s1 = "PORT ";
472: /**
473: * PORT h1,h2,h3,h4,p1,p2
474: * First the Host, then the Port
475: */
476: for (int i = 0; i < abyte0.length; i++)
477: s1 = s1 + (abyte0[i] & 0xff) + ",";
478: s1 = s1 + (dataServerSock.getLocalPort() >>> 8 & 0xff) + ","
479: + (dataServerSock.getLocalPort() & 0xff);
480:
481: if (issueCommand(s1) == FTP_ERROR) {
482: writeDebug("Port Command Failed");
483: IOException ftpprotocolexception = new IOException("PORT");
484: if (dataServerSock != null)
485: dataServerSock.close();
486: throw ftpprotocolexception;
487: }
488: if (issueCommand(s) == FTP_ERROR) {
489: writeDebug("Data Command sent. Output data from FTP Server not received.");
490: /**
491: * The FTP Server could not reach the data socket and port on which we are listening.
492: * So try and listen on another interface IP and port for output.
493: */
494: IOException ftpprotocolexception = new IOException("HOST");
495: if (dataServerSock != null)
496: dataServerSock.close();
497: throw ftpprotocolexception;
498: } else {
499: writeDebug("Data has arrived. So accepting it");
500: /**
501: * Data is to be received. So accept it and close the data socket.
502: * Also set the foundFlag to 1
503: */
504: dataSocket = dataServerSock.accept();
505: dataServerSock.close();
506: }
507: return dataSocket;
508: }
509:
510: public void setMachineToAccess(String machName) {
511: machineName = machName;
512: }
513:
514: public Object getMachineToAccess() {
515: return machineName;
516: }
517:
518: /**
519: * Determines an interface on which the given FTP Server can be reached
520: * If the FTP Server name cannot be resolved by any of its names,
521: * then HOST_NOT_FOUND is returned.
522: * The first address that matches the network address of any of the interfaces is used
523: */
524:
525: protected String findInterface(String nameOfServer,
526: Vector interfaceIPs) {
527: InetAddress[] inetAddr = null;
528: try {
529: inetAddr = InetAddress.getAllByName(nameOfServer);
530: } catch (java.net.UnknownHostException uhe) {
531: writeDebug("Unknown host", uhe);
532: return HOST_NOT_FOUND;
533: }
534: if (inetAddr == null)
535: return HOST_NOT_FOUND;
536: for (int interfacesCount = 0; interfacesCount < (interfaceIPs
537: .size() - 1); interfacesCount++) {
538: for (int serversCount = inetAddr.length; serversCount > 0; serversCount--) {
539: String serverIPAddr = (String) inetAddr[serversCount - 1]
540: .getHostAddress();
541: String interfaceIPAddr = (String) interfaceIPs
542: .get(interfacesCount);
543:
544: if (serverIPAddr.indexOf(":") != -1
545: || interfaceIPAddr.indexOf(":") != -1) {
546: writeDebug("IPv6 Addresses not supported");
547: return HOST_NO_MATCH;
548: }
549:
550: if (serverIPAddr.substring(0,
551: serverIPAddr.lastIndexOf("."))
552: .equalsIgnoreCase(
553: interfaceIPAddr.substring(0,
554: interfaceIPAddr
555: .lastIndexOf(".")))) {
556: return interfaceIPAddr;
557: }
558:
559: }
560: }
561: return HOST_NO_MATCH;
562: }
563:
564: protected int issueCommand(String cmd) throws IOException {
565: command = cmd;
566:
567: int reply;
568:
569: if (replyPending) {
570: if (readReply() == FTP_ERROR)
571: // logger.severe("Error reading FTP pending reply\n");
572: logger.severe("PSSRNF_CSPNSJ2030");
573: }
574: replyPending = false;
575: do {
576: sendServer(cmd + "\r\n");
577: reply = readReply();
578: if (cmd.indexOf("PASS") < 0) {
579: // logger.info("Reply Code received is " + lastReplyCode + " for " + cmd);
580: Object[] params1 = { new Integer(lastReplyCode),
581: " for ", cmd };
582: logger.log(Level.INFO, "PSSRNF_CSPNSJ2031", params1);
583: } else {
584: // logger.info("Reply Code received is " + lastReplyCode + " for PASS");
585: Object[] params2 = { new Integer(lastReplyCode),
586: " for PASS" };
587: logger.log(Level.INFO, "PSSRNF_CSPNSJ2032", params2);
588: }
589: } while (reply == FTP_TRY_AGAIN);
590: return reply;
591: }
592:
593: protected void issueCommandCheck(String cmd) throws IOException {
594: if (issueCommand(cmd) != FTP_SUCCESS)
595: throw new IOException(cmd);
596: }
597:
598: protected int readReply() throws IOException {
599: lastReplyCode = readServerResponse();
600:
601: switch (lastReplyCode / 100) {
602: case 1:
603: replyPending = true;
604:
605: case 2:
606: case 3:
607: return FTP_SUCCESS;
608:
609: case 5:
610: if (lastReplyCode == 530) {
611: if (user == null) {
612: throw new IOException("Not logged in");
613: }
614: return FTP_ERROR;
615: }
616: if (lastReplyCode == 550) {
617: throw new FileNotFoundException(command + ": "
618: + getResponseString());
619: }
620: if (lastReplyCode == 553) {
621: throw new FileNotFoundException(command + ": "
622: + getResponseString());
623: }
624: }
625:
626: /* this statement is not reached */
627: return FTP_ERROR;
628: }
629:
630: /* open a FTP connection to host <i>host</i>. */
631: public void openServer(String host) throws IOException {
632: int port = FTP_PORT;
633:
634: openServer(host, port);
635: }
636:
637: /* open a FTP connection to host <i>host</i> on port <i>port</i>. */
638: public void openServerOnPort(String host, int port)
639: throws IOException {
640: this .openServer(host, port);
641: if (readReply() == FTP_ERROR)
642: throw new IOException("Welcome message");
643: }
644:
645: public void openServer(String server, int port) throws IOException,
646: UnknownHostException {
647: //writeDebug("Opening server connection on:"+server+":"+port);
648: try {
649: if (serverSocket != null) {
650: quit();
651: }
652: serverSocket = doConnect(server, port);
653: opsw_server = new OutputStreamWriter(serverSocket
654: .getOutputStream(), machine_encoding);
655: serverInput = new BufferedInputStream(serverSocket
656: .getInputStream());
657: } catch (Exception e) {
658: writeDebug("Exception in opening connection to FTP server",
659: e);
660: if (e instanceof IOException) {
661: throw (IOException) e;
662: }
663: if (e instanceof UnknownHostException) {
664: throw (UnknownHostException) e;
665: }
666: }
667: }
668:
669: public void closeDataConnection() {
670: try {
671: if (sock != null)
672: sock.close();
673: } catch (Exception e) {
674: }
675: sock = null;
676: }
677:
678: public void closeServer() {
679: if (!serverIsOpen()) {
680: return;
681: }
682: try {
683: if (opsw_server != null)
684: opsw_server.close();
685: } catch (Exception e) {
686: }
687: try {
688: if (serverInput != null)
689: serverInput.close();
690: } catch (Exception e) {
691: }
692: try {
693: if (serverSocket != null)
694: serverSocket.close();
695: } catch (Exception e) {
696: }
697:
698: closeDataConnection();
699:
700: serverSocket = null;
701: serverInput = null;
702: opsw_server = null;
703:
704: writeDebug("Connection to FTP server closed");
705: }
706:
707: /* Sends command <i>cmd</i> to the server. */
708: public void sendServer(String cmd) {
709: try {
710: opsw_server.write(cmd);
711: opsw_server.flush();
712: } catch (Exception e) {
713: writeDebug("Exception in sending command to the server", e);
714: }
715:
716: }
717:
718: protected void writeDebug(String szMsg) {
719: writeDebug(szMsg, null);
720: }
721:
722: protected void writeDebug(String szMsg, Exception e) {
723: if (e != null) {
724: // logger.log(Level.INFO, szMsg, e);
725: logger.log(Level.INFO, "PSSRNF_CSPNSJ2033");
726: } else {
727: // logger.info(szMsg);
728: logger.info("PSSRNF_CSPNSJ2034");
729: }
730: }
731:
732: protected void writeErrorDebug(String szError, Exception e) {
733: if (e != null)
734: // logger.log(Level.SEVERE, szError, e);
735: logger.log(Level.SEVERE, "PSSRNF_CSPNSJ2035");
736: else
737: // logger.severe(szError);
738: logger.severe("PSSRNF_CSPNSJ2036");
739: }
740:
741: }
|