KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > armedbear > j > FtpSession


1 /*
2  * FtpSession.java
3  *
4  * Copyright (C) 1998-2003 Peter Graves
5  * $Id: FtpSession.java,v 1.4 2003/05/25 13:43:17 piso Exp $
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */

21
22 package org.armedbear.j;
23
24 import java.io.BufferedReader JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.InputStreamReader JavaDoc;
28 import java.io.OutputStream JavaDoc;
29 import java.io.OutputStreamWriter JavaDoc;
30 import java.io.StringReader JavaDoc;
31 import java.net.InetAddress JavaDoc;
32 import java.net.ServerSocket JavaDoc;
33 import java.net.Socket JavaDoc;
34 import java.net.SocketException JavaDoc;
35 import java.util.Random JavaDoc;
36 import java.util.StringTokenizer JavaDoc;
37 import java.util.Vector JavaDoc;
38 import javax.swing.SwingUtilities JavaDoc;
39
40 public class FtpSession implements Constants
41 {
42     private static final boolean echo = true;
43     private static final Vector JavaDoc sessionList = new Vector JavaDoc();
44
45     private static CleanupThread cleanupThread;
46
47     private String JavaDoc host;
48     private int port;
49     private String JavaDoc user;
50     private String JavaDoc password;
51     private String JavaDoc loginDirectory;
52     private String JavaDoc currentDirectory;
53     private Socket JavaDoc controlSocket;
54     private BufferedReader JavaDoc controlIn;
55     private OutputStreamWriter JavaDoc controlOut;
56     private ServerSocket JavaDoc serverSocket;
57     private Socket JavaDoc dataSocket;
58     private InputStream JavaDoc dataIn;
59     private OutputStream JavaDoc dataOut;
60     private boolean connected;
61     private boolean usePassiveMode = true;
62     private String JavaDoc errorText;
63     private ProgressNotifier progressNotifier;
64     private boolean locked;
65
66     private FtpSession()
67     {
68         register(this);
69     }
70
71     private FtpSession(Login login, int port)
72     {
73         host = login.host;
74         user = login.user;
75         password = login.password;
76         this.port = port;
77         usePassiveMode =
78             Editor.preferences().getBooleanProperty(Property.FTP_USE_PASSIVE_MODE);
79         register(this);
80     }
81
82     private static synchronized void register(FtpSession session)
83     {
84         sessionList.add(session);
85         if (cleanupThread == null) {
86             cleanupThread = new CleanupThread(cleanupRunnable);
87             cleanupThread.start();
88         }
89     }
90
91     private static synchronized void unregister(FtpSession session)
92     {
93         if (!sessionList.contains(session))
94             Debug.bug();
95         sessionList.remove(session);
96     }
97
98     protected Object JavaDoc clone()
99     {
100         FtpSession session = new FtpSession();
101         session.host = host;
102         session.user = user;
103         session.password = password;
104         session.port = port;
105         session.usePassiveMode = usePassiveMode;
106         return session;
107     }
108
109     public final String JavaDoc getHost()
110     {
111         return host;
112     }
113
114     public final String JavaDoc getErrorText()
115     {
116         return errorText;
117     }
118
119     public final String JavaDoc getLoginDirectory()
120     {
121         return loginDirectory;
122     }
123
124     public final void setProgressNotifier(ProgressNotifier progressNotifier)
125     {
126         this.progressNotifier = progressNotifier;
127     }
128
129     public final boolean isLocked()
130     {
131         return locked;
132     }
133
134     private synchronized boolean lock()
135     {
136         if (locked)
137             return false;
138         locked = true;
139         return true;
140     }
141
142     public synchronized void unlock()
143     {
144         if (locked) {
145             progressNotifier = null;
146             locked = false;
147         } else
148             Debug.bug("FtpSession.unlock session was not locked");
149     }
150
151     private boolean changeDirectory(String JavaDoc dirname)
152     {
153         if (dirname.equals(currentDirectory))
154             return true;
155         command("CWD " + dirname);
156         String JavaDoc s= getReplyString();
157         int code = getCode(s);
158         if (code == 421) {
159             connect();
160             if (connected) {
161                 command("CWD " + dirname);
162                 s = getReplyString();
163             }
164             else
165                 return false;
166         }
167         if (getCode(s) == 250) {
168             currentDirectory = dirname;
169             return true;
170         }
171         return false;
172     }
173
174     public boolean isDirectory(String JavaDoc remotePath)
175     {
176         if (changeDirectory(remotePath))
177             return true;
178
179         return false;
180     }
181
182     public boolean isFile(String JavaDoc remotePath)
183     {
184         command("SIZE " + remotePath);
185         String JavaDoc s = getReplyString();
186         int code = getCode(s);
187         if (code == 213)
188             return true;
189         if (code == 500) {
190             // "SIZE" command was not recognized.
191
String JavaDoc listing = getDirectoryListingForFile(remotePath);
192             if (listing != null && listing.length() > 0) {
193                 char c = listing.charAt(0);
194                 if (c == '-' || c == 'l')
195                     return true;
196             }
197         }
198         return false;
199     }
200
201     public boolean exists(String JavaDoc remotePath)
202     {
203         if (isDirectory(remotePath))
204             return true;
205         else
206             return isFile(remotePath);
207     }
208
209     private long getFileSize(String JavaDoc remotePath)
210     {
211         long fileSize = -1;
212         command("SIZE " + remotePath);
213         String JavaDoc s = getReplyString();
214         if (getCode(s) == 213) {
215             s = s.substring(3).trim();
216             try {
217                 fileSize = Long.parseLong(s);
218             }
219             catch (NumberFormatException JavaDoc e) {
220                 Log.error(e);
221             }
222         }
223         return fileSize;
224     }
225
226     boolean deleteFile(String JavaDoc remotePath)
227     {
228         command("DELE " + remotePath);
229         return getReply() == 250;
230     }
231
232     boolean removeDirectory(String JavaDoc remotePath)
233     {
234         command("RMD " + remotePath);
235         return getReply() == 250;
236     }
237
238     public boolean chmod(FtpFile file, int permissions)
239     {
240         if (permissions != 0) {
241             FastStringBuffer sb = new FastStringBuffer("SITE CHMOD ");
242             sb.append(Integer.toString(permissions, 8));
243             sb.append(' ');
244             sb.append(file.canonicalPath());
245             command(sb.toString());
246             return getReply() == 200;
247         }
248         else
249             return false;
250     }
251
252     public int getPermissions(FtpFile file)
253     {
254         int permissions = 0;
255         String JavaDoc listing = getDirectoryListingForFile(file.canonicalPath());
256         if (listing != null) {
257             String JavaDoc s = listing.substring(1, 10);
258             Log.debug("s = |" + s + "|");
259             if (s.length() == 9) {
260                 if (s.charAt(0) == 'r')
261                     permissions += 0400;
262                 if (s.charAt(1) == 'w')
263                     permissions += 0200;
264                 if (s.charAt(2) == 'x')
265                     permissions += 0100;
266                 if (s.charAt(3) == 'r')
267                     permissions += 040;
268                 if (s.charAt(4) == 'w')
269                     permissions += 020;
270                 if (s.charAt(5) == 'x')
271                     permissions += 010;
272                 if (s.charAt(6) == 'r')
273                     permissions += 4;
274                 if (s.charAt(7) == 'w')
275                     permissions += 2;
276                 if (s.charAt(8) == 'x')
277                     permissions += 1;
278             }
279         }
280         return permissions;
281     }
282
283     int getFileStatus(String JavaDoc filename)
284     {
285         if (!connected) {
286             connect();
287             if (!connected)
288                 return -1;
289         }
290         if (changeDirectory(filename))
291             return 2; // It's a directory.
292
// Not a directory.
293
if (!openDataSocket())
294             return -1;
295         int status = -1; // Not found or unknown status.
296
command("LIST " + filename);
297         int code = getReply();
298         // 125 Data connection already open, transfer starting
299
// 150 Opening ASCII mode data connection for file list
300
// 226 Transfer complete
301
if (code == 125 || code == 150 || code == 226) {
302             String JavaDoc s = getData();
303             closeDataSocket();
304             // Look for "drwxrwxrwx"...
305
if (s != null && s.length() > 10) {
306                 char c = s.charAt(0);
307                 if (c == 'd')
308                     status = 2; // Shouldn't happen, since changeDirectory failed.
309
else if (c == '-')
310                     status = 1;
311                 if (status != -1) {
312                     // Sanity checking.
313
boolean valid = true;
314                     c = s.charAt(1);
315                     if (c != 'r' && c != 'w' && c != '-')
316                         valid = false;
317                     else {
318                         c = s.charAt(2);
319                         if (c != 'r' && c != 'w' && c != '-')
320                             valid = false;
321                     }
322                     if (!valid)
323                         status = -1;
324                 }
325             }
326             // 125 Data connection already open, transfer starting
327
// 150 Opening ASCII mode data connection for file list
328
// 226 Transfer complete
329
if (code == 125 || code == 150)
330                 getReply(226);
331         }
332         return status;
333     }
334
335     public String JavaDoc getDirectoryListing(File file)
336     {
337         if (!(file instanceof FtpFile)) {
338             Debug.bug();
339             return null;
340         }
341         Debug.assertTrue(isLocked());
342         String JavaDoc listing = DirectoryCache.getDirectoryCache().getListing(file);
343         if (listing == null) {
344             listing = getDirectoryListing(file.canonicalPath());
345             if (listing != null)
346                 DirectoryCache.getDirectoryCache().put(file, listing);
347         }
348         return listing;
349     }
350
351     public String JavaDoc getDirectoryListing(String JavaDoc dirname)
352     {
353         Debug.assertTrue(isLocked());
354         String JavaDoc listing = null;
355         if (verifyConnected()) {
356             if (changeDirectory(dirname)) {
357                 if (openDataSocket()) {
358                     command("LIST -la");
359                     int code = getReply();
360
361                     // 125 Data connection already open, transfer starting
362
// 150 Opening ASCII mode data connection for file list
363
// 226 Transfer complete
364
if (code == 125 || code == 150 || code == 226)
365                         listing = getData();
366
367                     closeDataSocket();
368                     if (code == 125 || code == 150)
369                         getReply(226);
370                 }
371             }
372         }
373         return listing;
374     }
375
376     public String JavaDoc getDirectoryListingForFile(String JavaDoc filename)
377     {
378         String JavaDoc listing = null;
379         if (connected) {
380             if (openDataSocket()) {
381                 command("LIST -la " + filename);
382                 int code = getReply();
383
384                 // 125 Data connection already open, transfer starting
385
// 150 Opening ASCII mode data connection for file list
386
// 226 Transfer complete
387
if (code == 125 || code == 150 || code == 226)
388                     listing = getData();
389
390                 closeDataSocket();
391                 if (code == 125 || code == 150)
392                     getReply(226);
393             }
394         }
395
396         // With at least some FTP servers (ProFTP 1.2.0pre9) the listing
397
// will be empty if any part of the filename contains any space
398
// characters, although CWD works fine.
399

400         // If that happens we CWD into the parent directory, list all the
401
// files, and pick out the line of interest with a regular expression.
402

403         // We check the listing again to make sure the file hasn't changed
404
// on the server when we go to write out a modified version.
405

406         if (listing == null || listing.length() == 0) {
407             int index = filename.lastIndexOf('/');
408             if (index >= 0) {
409                 String JavaDoc parent = index == 0 ? "/" : filename.substring(0, index);
410                 String JavaDoc name = filename.substring(index + 1);
411                 String JavaDoc parentListing = getDirectoryListing(parent);
412                 if (parentListing != null) {
413                     BufferedReader JavaDoc reader =
414                         new BufferedReader JavaDoc(new StringReader JavaDoc(parentListing));
415                     String JavaDoc entry;
416                     try {
417                         while ((entry = reader.readLine()) != null) {
418                             if (name.equals(DirectoryEntry.getName(entry))) {
419                                 // Found it!
420
listing = entry;
421                                 break;
422                             }
423                         }
424                     }
425                     catch (IOException JavaDoc e) {
426                         Log.error(e);
427                     }
428                 }
429             }
430         }
431
432         Log.debug("getDirectoryListingForFile |" + listing + "|");
433         return listing;
434     }
435
436     public int put(File localFile, File remoteFile, long fileSize, boolean saveInPlace)
437     {
438         boolean succeeded = false;
439         boolean cancelled = false;
440         String JavaDoc tempName = null;
441
442         // Change directory to prove parent directory exists.
443
if (changeDirectory(remoteFile.getParent())) {
444             OutputStream JavaDoc out = null;
445             if (saveInPlace) {
446                 out = getOutputStreamForFile(remoteFile.canonicalPath());
447             } else {
448                 // It would be nice to use STOU here, but most servers don't
449
// implement it.
450
tempName = getUniqueName(remoteFile.getParentFile());
451                 Log.debug("tempName = |" + tempName + "|");
452                 if (tempName != null)
453                     out = getOutputStreamForFile(tempName);
454             }
455             if (out != null) {
456                 try {
457                     InputStream JavaDoc in = localFile.getInputStream();
458                     byte[] bytes = new byte[16384];
459                     long totalBytes = 0;
460                     int bytesRead;
461                     if (progressNotifier != null)
462                         progressNotifier.progressStart();
463                     while((bytesRead = in.read(bytes)) > 0) {
464                         out.write(bytes, 0, bytesRead);
465                         totalBytes += bytesRead;
466                         if (progressNotifier != null) {
467                             if (progressNotifier.cancelled()) {
468                                 cancelled = true;
469                                 break;
470                             }
471                             progressNotifier.progress("Sent ", totalBytes, fileSize);
472                         }
473                         // Slow things down for debugging, maybe.
474
Debug.throttle();
475                     }
476                     if (progressNotifier != null)
477                         progressNotifier.progressStop();
478                     out.flush();
479                     out.close();
480                     closeDataSocket();
481                     in.close();
482                     succeeded = getReply(226);
483                     if (cancelled && tempName != null) {
484                         command("DELE " + tempName);
485                         getReplyString(); // Ignore reply.
486
}
487                 }
488                 catch (Exception JavaDoc e) {
489                     Log.error(e);
490                 }
491             }
492         }
493
494         if (succeeded && !cancelled && tempName != null) {
495             do {
496                 if (progressNotifier != null)
497                     progressNotifier.setText("Renaming temporary file");
498                 command("RNFR " + tempName);
499                 if (!getReply(350)) {
500                     succeeded = false;
501                     break;
502                 }
503                 command("RNTO " + remoteFile.canonicalPath());
504                 if (!getReply(250))
505                     succeeded = false;
506                 break;
507             } while (false);
508         }
509
510         if (progressNotifier != null) {
511             if (cancelled)
512                 progressNotifier.setText("Transfer cancelled");
513             else if (succeeded)
514                 progressNotifier.setText("Transfer completed");
515             else
516                 progressNotifier.setText("Transfer failed");
517         }
518
519         if (cancelled)
520             return CANCELLED;
521         else if (succeeded)
522             return SUCCESS;
523         else
524             return ERROR;
525     }
526
527     private static Random JavaDoc random;
528
529     private String JavaDoc getUniqueName(File dir)
530     {
531         long now = System.currentTimeMillis();
532         if (random == null) {
533             // Use uptime as seed.
534
random = new Random JavaDoc(now - Editor.getStartTimeMillis());
535         }
536         long n = now + Math.abs(random.nextLong() % now);
537         for (int i = 0; i < 100; i++) {
538             File file = File.getInstance(dir, String.valueOf(n));
539             String JavaDoc name = file.canonicalPath();
540             if (!exists(name)) {
541                 Log.debug("unique name = |" + name + "|");
542                 return name;
543             }
544             n += Math.abs(random.nextLong() % now);
545         }
546         return null;
547     }
548
549     public int get(File remoteFile, File localFile, long fileSize)
550     {
551         boolean succeeded = false;
552         boolean cancelled = false;
553         if (fileSize == 0) {
554             fileSize = getFileSize(remoteFile.canonicalPath());
555             if (fileSize < 0) // Error.
556
fileSize = 0;
557         }
558         InputStream JavaDoc in = getInputStreamForFile(remoteFile.canonicalPath());
559         if (in == null)
560             return ERROR;
561         try {
562             OutputStream JavaDoc out = localFile.getOutputStream();
563             byte[] bytes = new byte[16384];
564             long totalBytes = 0;
565
566             if (progressNotifier != null)
567                 progressNotifier.progressStart();
568
569             while (true) {
570                 int bytesRead = 0;
571                 try {
572                     // We may get an exception here if user cancels.
573
bytesRead = in.read(bytes);
574                 }
575                 catch (Exception JavaDoc e) {
576                     if (progressNotifier == null || !progressNotifier.cancelled())
577                         Log.error(e);
578                     else
579                         // Exception is expected.
580
Log.debug("FtpSession.get cancelled");
581                 }
582                 if (bytesRead <= 0)
583                     break;
584                 out.write(bytes, 0, bytesRead);
585                 totalBytes += bytesRead;
586                 if (progressNotifier != null) {
587                     if (progressNotifier.cancelled()) {
588                         cancelled = true;
589                         break;
590                     }
591                     progressNotifier.progress("Received ", totalBytes, fileSize);
592                 }
593                 // Slow things down for debugging, maybe.
594
Debug.throttle();
595             }
596             if (progressNotifier != null)
597                 progressNotifier.progressStop();
598             // If the user cancels, just close the data connection.
599
// A bit rude, but it seems to work.
600
out.flush();
601             out.close();
602             in.close();
603             closeDataSocket();
604             if (cancelled)
605                 localFile.delete();
606             succeeded = getReply(226);
607         }
608         catch (SocketException JavaDoc e) {
609             if (cancelled)
610                 ; // Exception is expected in this case.
611
else
612                 Log.error(e);
613         }
614         catch (Exception JavaDoc e) {
615             Log.error(e);
616         }
617         if (progressNotifier != null) {
618             if (cancelled)
619                 progressNotifier.setText("Transfer cancelled");
620             else if (succeeded)
621                 progressNotifier.setText("Transfer completed");
622             else
623                 progressNotifier.setText("Transfer failed");
624         }
625         if (cancelled)
626             return CANCELLED;
627         else if (succeeded)
628             return SUCCESS;
629         else
630             return ERROR;
631     }
632
633     public synchronized boolean verifyConnected()
634     {
635         if (connected) {
636             command("NOOP");
637             if (getReply() == 421) {
638                 Log.debug("verifyConnected calling connect");
639                 connect();
640             }
641         } else
642             connect();
643         if (connected && progressNotifier != null)
644             progressNotifier.setText("Connected to " + host);
645         return connected;
646     }
647
648     private synchronized void connect()
649     {
650         if (progressNotifier != null)
651             progressNotifier.setText("Connecting to " + host);
652         Log.debug("connecting to " + host);
653
654         connected = false;
655         loginDirectory = null;
656         currentDirectory = null;
657         errorText = null;
658
659         SocketConnection sc = new SocketConnection(host, port, 30000, 200, progressNotifier);
660         controlSocket = sc.connect();
661         if (controlSocket == null) {
662             errorText = sc.getErrorText();
663             return;
664         }
665
666         try {
667             controlIn =
668                 new BufferedReader JavaDoc(new InputStreamReader JavaDoc(controlSocket.getInputStream()));
669             controlOut = new OutputStreamWriter JavaDoc(controlSocket.getOutputStream());
670         }
671         catch (IOException JavaDoc e) {
672             Log.error(e);
673             disconnect();
674             return;
675         }
676         getReplyString();
677         command("USER " + user);
678         int code = getReply();
679         if (code == 331) {
680             // Password required.
681
if (password != null) {
682                 command("PASS " + password);
683                 code = getReply();
684                 if (code == 530) {
685                     errorText = "Login incorrect";
686                     user = null;
687                     password = null;
688                 }
689             }
690         }
691         if (code != 230) {
692             if (errorText == null || errorText.length() == 0) {
693                 if (lastReply != null)
694                     errorText = lastReply.substring(3).trim();
695                 if (errorText == null || errorText.length() == 0)
696                     errorText = "Unable to connect to " + host;
697             }
698             disconnect();
699             return;
700         }
701
702         // Determine login directory.
703
command("PWD");
704         String JavaDoc s = getReplyString();
705         int begin = s.indexOf('"');
706         if (begin >= 0) {
707             int end = s.indexOf('"', ++begin);
708             if (end >= 0)
709                 loginDirectory = currentDirectory = s.substring(begin, end);
710         }
711
712         command("TYPE I");
713         code = getReply();
714         if (code != 200) {
715             Log.error("connect didn't get 200");
716             disconnect();
717             return;
718         }
719
720         connected = true;
721         Log.debug("connected!");
722     }
723
724     private void command(String JavaDoc s)
725     {
726         boolean reconnect = false;
727         if (echo)
728             Log.debug("==> " + (s.startsWith("PASS ") ? "PASS" : s));
729         try {
730             controlOut.write(s + "\r\n");
731             controlOut.flush();
732             return;
733         }
734         catch (Exception JavaDoc e) {
735             Log.error("exception command " + s);
736             reconnect = true;
737         }
738         if (reconnect) {
739             // Exception may mean we were disconnected by remote host because
740
// of inactivity. Try to reconnect.
741
Log.debug("trying to reconnect...");
742             connect();
743             if (connected) {
744                 if (echo)
745                     Log.debug("==> " + (s.startsWith("PASS ") ? "PASS" : s));
746                 try {
747                     controlOut.write(s + "\r\n");
748                     controlOut.flush();
749                     return;
750                 }
751                 catch (Exception JavaDoc e) {
752                     Log.error("2nd exception command " + s);
753                     reconnect = true;
754                 }
755             }
756         }
757     }
758
759     private String JavaDoc lastReply;
760
761     // Returns final line of reply.
762
private String JavaDoc getReplyString()
763     {
764         String JavaDoc s = null;
765         try {
766             do {
767                 s = controlIn.readLine();
768                 if (echo && s != null)
769                     Log.debug("<== " + s);
770             } while (s != null && !isEndOfReply(s));
771         }
772         catch (Exception JavaDoc e) {}
773         lastReply = s;
774         return s;
775     }
776
777     private static boolean isEndOfReply(String JavaDoc s)
778     {
779         // If it's not the last line of the reply, the 4th char will be '-'.
780
if (s != null &&
781             s.length() >= 4 &&
782             s.charAt(3) == ' ' &&
783             Character.isDigit(s.charAt(0)) &&
784             Character.isDigit(s.charAt(1)) &&
785             Character.isDigit(s.charAt(2)))
786         {
787             return true;
788         }
789
790         return false;
791     }
792
793     private boolean getReply(int required)
794     {
795         String JavaDoc s = getReplyString();
796         if (s == null)
797             return false;
798         return getCode(s) == required;
799     }
800
801     private int getReply()
802     {
803         String JavaDoc s = getReplyString();
804         if (s == null)
805             return 421;
806         return getCode(s);
807     }
808
809     private static int getCode(String JavaDoc reply)
810     {
811         int code = -1;
812         if (reply != null) {
813             // We do sanity checking when we get the reply string, so we don't
814
// need it here.
815
try {
816                 code = Integer.parseInt(reply.substring(0, 3));
817             }
818             catch (NumberFormatException JavaDoc e) {
819                 Log.error(e);
820             }
821         }
822         return code;
823     }
824
825     private String JavaDoc getData()
826     {
827         if (!usePassiveMode)
828             acceptConnectionFromServer();
829         byte[] buf = new byte[16384];
830         FastStringBuffer sb = new FastStringBuffer();
831         try {
832             while (true) {
833                 int bytesRead = dataIn.read(buf); // Blocks.
834
if (bytesRead < 0)
835                     break;
836                 sb.append(new String JavaDoc(buf, 0, bytesRead));
837             }
838         }
839         catch (IOException JavaDoc e) {
840             Log.error(e);
841         }
842         return sb.toString();
843     }
844
845     private void closeDataSocket()
846     {
847         if (dataSocket != null) {
848             if (dataIn != null) {
849                 try {
850                     dataIn.close();
851                 }
852                 catch (IOException JavaDoc e) {
853                     Log.error(e);
854                 }
855             }
856             if (dataOut != null) {
857                 try {
858                     dataOut.close();
859                 }
860                 catch (IOException JavaDoc e) {
861                     Log.error(e);
862                 }
863             }
864             try {
865                 dataSocket.close();
866             }
867             catch (IOException JavaDoc e){
868                 Log.error(e);
869             }
870             dataSocket = null;
871             dataIn = null;
872             dataOut = null;
873         }
874     }
875
876     private boolean openDataSocket()
877     {
878         if (!connected) {
879             connect();
880             if (!connected)
881                 return false;
882         }
883         closeDataSocket();
884         if (usePassiveMode) {
885             command("PASV");
886             String JavaDoc reply;
887             while (true){
888                 reply = getReplyString();
889                 if (reply == null) {
890                     Log.error("openDataSocket null reply to PASV");
891                     return false;
892                 }
893                 int code = getCode(reply);
894                 if (code >= 400)
895                     return false; // Error.
896
if (code == 227)
897                     break; // The one we want.
898
// Otherwise the reply we received is not a reply to the PASV command.
899
// Loop back and try again.
900
}
901             int begin = reply.indexOf('(');
902             if (begin < 0)
903                 return false;
904             int end = reply.indexOf(')');
905             if (end < 0)
906                 return false;
907             String JavaDoc s = reply.substring(begin+1, end);
908             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(s, ",", false);
909             if (st.countTokens() != 6)
910                 return false;
911             String JavaDoc[] addr = new String JavaDoc[4];
912             for (int i = 0; i < 4; i++)
913                 addr[i] = st.nextToken();
914             String JavaDoc address = addr[0] + "." + addr[1] + "." + addr[2] + "." + addr[3];
915             try {
916                 int hibyte = Integer.parseInt(st.nextToken());
917                 int lobyte = Integer.parseInt(st.nextToken());
918                 int dataPort = hibyte * 256 + lobyte;
919                 Log.debug("opening data socket");
920                 dataSocket = new Socket JavaDoc(address, dataPort);
921                 dataIn = dataSocket.getInputStream();
922                 dataOut = dataSocket.getOutputStream();
923             }
924             catch (NumberFormatException JavaDoc e) {
925                 Log.error(e);
926             }
927             catch (IOException JavaDoc ex) {
928                 Log.error(ex);
929             }
930             return dataSocket != null;
931         } else {
932             // We're not using passive mode.
933
try {
934                 InetAddress JavaDoc localHost = controlSocket.getLocalAddress();
935                 serverSocket = new ServerSocket JavaDoc(0);
936                 int dataPort = serverSocket.getLocalPort();
937                 int lobyte = (dataPort & 0x00ff);
938                 int hibyte = ((dataPort & 0xff00) >> 8);
939                 byte[] addrBytes = localHost.getAddress();
940                 int[] addr = new int[4];
941                 for (int i = 0; i < 4; i++) {
942                     addr[i] = addrBytes[i];
943                     if (addr[i] < 0)
944                         addr[i] += 256;
945                 }
946                 String JavaDoc s = "PORT " + addr[0] + "," + addr[1] + "," + addr[2] + "," + addr[3] + "," + hibyte + "," + lobyte;
947                 command(s);
948                 return getReply(200);
949             }
950             catch (Exception JavaDoc e) {
951                 Log.error(e);
952             }
953             return false;
954         }
955     }
956
957     private void acceptConnectionFromServer()
958     {
959         try {
960             dataSocket = serverSocket.accept();
961             if (dataSocket != null) {
962                 dataIn = dataSocket.getInputStream();
963                 dataOut = dataSocket.getOutputStream();
964             }
965         }
966         catch (Exception JavaDoc e) {
967             Log.error(e);
968         }
969     }
970
971     private InputStream JavaDoc getInputStreamForFile(String JavaDoc filename)
972     {
973         if (openDataSocket()) {
974             command("RETR " + filename);
975             int code = getCode(getReplyString());
976             if (code > -1 && code < 400) {
977                 if (!usePassiveMode)
978                     acceptConnectionFromServer();
979                 return dataIn;
980             }
981         }
982         return null;
983     }
984
985     private OutputStream JavaDoc getOutputStreamForFile(String JavaDoc filename)
986     {
987         if (filename == null)
988             return null;
989         if (openDataSocket()) {
990             command("STOR " + filename);
991             int code = getCode(getReplyString());
992             if (code > -1 && code < 400) {
993                 if (!usePassiveMode)
994                     acceptConnectionFromServer();
995                 return dataOut;
996             }
997             if (code == 550 || code == 553)
998                 errorText = "Access denied";
999         }
1000        return null;
1001    }
1002
1003    private static synchronized void cleanup()
1004    {
1005        // Walk buffer list in event dispatch thread.
1006
if (!SwingUtilities.isEventDispatchThread()) {
1007            Debug.bug();
1008            return;
1009        }
1010        if (sessionList.size() == 0)
1011            return; // Nothing to do.
1012
for (int i = sessionList.size(); i-- > 0;) {
1013            FtpSession session = (FtpSession) sessionList.get(i);
1014            String JavaDoc host = session.getHost();
1015            boolean inUse = false;
1016            for (BufferIterator it = new BufferIterator(); it.hasNext();) {
1017                Buffer buf = it.nextBuffer();
1018                if (buf.getFile() instanceof FtpFile) {
1019                    if (host.equals(buf.getFile().getHostName())) {
1020                         inUse = true;
1021                         break;
1022                    }
1023                }
1024            }
1025            if (!inUse) {
1026                session.close();
1027                unregister(session);
1028            }
1029        }
1030        if (sessionList.size() == 0) {
1031            if (cleanupThread != null) {
1032                cleanupThread.cancel();
1033                cleanupThread = null;
1034            }
1035        }
1036    }
1037
1038    private static final Runnable JavaDoc cleanupRunnable = new Runnable JavaDoc() {
1039        public void run()
1040        {
1041            cleanup();
1042        }
1043    };
1044
1045    public synchronized void disconnect()
1046    {
1047        Log.debug("disconnect");
1048        if (controlSocket != null) {
1049            Log.debug("closing control socket...");
1050            try {
1051                controlSocket.close();
1052            }
1053            catch (IOException JavaDoc e) {
1054                Log.error(e);
1055            }
1056            controlSocket = null;
1057            controlIn = null;
1058            controlOut = null;
1059        }
1060        if (dataSocket != null) {
1061            Log.debug("closing data socket...");
1062            try {
1063                dataSocket.close();
1064            }
1065            catch (IOException JavaDoc e) {
1066                Log.error(e);
1067            }
1068            dataSocket = null;
1069            dataIn = null;
1070            dataOut = null;
1071        }
1072        connected = false;
1073    }
1074
1075    private void close()
1076    {
1077        Log.debug("FtpSession.close");
1078        if (connected) {
1079            final Editor editor = Editor.currentEditor();
1080            editor.setWaitCursor();
1081            Runnable JavaDoc r = new Runnable JavaDoc() {
1082                public void run()
1083                {
1084                    try {
1085                        if (echo)
1086                            Log.debug("==> QUIT");
1087                        controlOut.write("QUIT\r\n");
1088                        controlOut.flush();
1089                        getReply();
1090                    }
1091                    catch (IOException JavaDoc e) {}
1092                }
1093            };
1094            Thread JavaDoc t = new Thread JavaDoc(r);
1095            t.start();
1096            try {
1097                t.join(3000);
1098            }
1099            catch (InterruptedException JavaDoc e) {
1100                Log.error(e);
1101            }
1102            if (t.isAlive()) {
1103                Log.debug("stopping QUIT thread");
1104                t.stop();
1105            }
1106            disconnect();
1107            editor.setDefaultCursor();
1108        }
1109        Log.debug("leaving close");
1110    }
1111
1112    public static synchronized FtpSession getSession(FtpFile file)
1113    {
1114        if (file == null)
1115            return null;
1116        Login login = new Login(file.getHostName(), file.getUserName(), file.getPassword());
1117        if (login == null)
1118            return null; // Invalid URL.
1119
// Try to find an idle session for the required host.
1120
FtpSession session = lockSession(login.host, file.getPort());
1121        if (session != null) {
1122            if (session.checkLogin())
1123                return session;
1124
1125            session.unlock();
1126            return null;
1127        }
1128        // No idle session for this host. Try to find a session to clone.
1129
session = findSession(login.host, file.getPort());
1130        if (session != null) {
1131            session = (FtpSession) session.clone();
1132            if (session.lock()) {
1133                if (session.checkLogin())
1134                    return session;
1135                session.unlock();
1136            }
1137            return null;
1138        }
1139        if (login.user == null || login.password == null) {
1140            // The URL lacked either a user name or a password or both.
1141
final Editor editor = Editor.currentEditor();
1142            if (login.user == null) {
1143                // No user name in URL. We'll take the first entry in ~/.netrc
1144
// for the host in question.
1145
Login maybe = Netrc.getLogin(login.host);
1146                if (maybe != null) {
1147                    login.user = maybe.user;
1148                    login.password = maybe.password;
1149                }
1150                if (login.user == null) {
1151                    // Nothing relevant in ~/.netrc. Ask the user.
1152
login.user = InputDialog.showInputDialog(editor, "Login:", "Login on " + login.host);
1153                    editor.repaintNow();
1154                }
1155            }
1156            if (login.user == null)
1157                return null; // User cancelled.
1158
if (login.user.length() == 0)
1159                login.user = "anonymous";
1160            if (login.password == null) {
1161                // We have host and user name but no password.
1162
login.password = Netrc.getPassword(login.host, login.user);
1163                if (login.password == null) {
1164                    if (login.user.equals("anonymous"))
1165                        login.password = Editor.preferences().getStringProperty(Property.FTP_ANONYMOUS_PASSWORD);
1166                    if (login.password == null) {
1167                        // Ask the user.
1168
login.password = PasswordDialog.showPasswordDialog(editor, "Password:", "Password");
1169                        editor.repaintNow();
1170                    }
1171                }
1172            }
1173            // If we don't have a password, give up (it can be blank, but not
1174
// null).
1175
if (login.password == null)
1176                return null;
1177        }
1178        // Final sanity check.
1179
if (login.user.length() == 0)
1180            return null;
1181        // At this point we have non-empty strings for host and user name, and
1182
// a non-null password.
1183
session = new FtpSession(login, file.getPort());
1184        if (session.lock())
1185            return session;
1186        return null;
1187    }
1188
1189    // Make sure the login is comnplete. Get the user to enter the username
1190
// and/or password if missing. Don't look in .netrc or preferences; we may
1191
// be here because the information in .netrc or preferences didn't work.
1192
private boolean checkLogin()
1193    {
1194        final Editor editor = Editor.currentEditor();
1195        if (user == null) {
1196            // Nothing relevant in ~/.netrc. Ask the user.
1197
user = InputDialog.showInputDialog(editor, "Login:", "Login on " + host);
1198            editor.repaintNow();
1199            if (user == null)
1200                return false; // User cancelled.
1201
}
1202        if (user.length() == 0)
1203            user = "anonymous";
1204        if (password == null) {
1205            // Ask the user.
1206
password = PasswordDialog.showPasswordDialog(editor, "Password:", "Password");
1207            editor.repaintNow();
1208            // If we don't have a password, give up.
1209
if (password == null)
1210                return false;
1211        }
1212        // Final sanity check.
1213
if (user.length() == 0)
1214            return false;
1215        return true;
1216    }
1217
1218    private static FtpSession lockSession(String JavaDoc host, int port)
1219    {
1220        for (int i = 0; i < sessionList.size(); i++) {
1221            FtpSession session = (FtpSession) sessionList.get(i);
1222            if (session.host.equals(host) && session.port == port) {
1223                if (session.lock())
1224                    return session;
1225            }
1226        }
1227        return null;
1228    }
1229
1230    private static FtpSession findSession(String JavaDoc host, int port)
1231    {
1232        for (int i = 0; i < sessionList.size(); i++) {
1233            FtpSession session = (FtpSession) sessionList.get(i);
1234            if (session.host.equals(host) && session.port == port)
1235                return session;
1236        }
1237        return null;
1238    }
1239}
1240
Popular Tags