blob: 52458a68870adf85c58dfcf3701bea9ca52ff3c5 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.net.ftp;
27
28import java.util.StringTokenizer;
29import java.util.regex.*;
30import java.io.*;
31import java.net.*;
32import sun.net.TransferProtocolClient;
33import sun.net.TelnetInputStream;
34import sun.net.TelnetOutputStream;
35import sun.misc.RegexpPool;
36import java.security.AccessController;
37import java.security.PrivilegedAction;
38
39/**
40 * This class implements the FTP client.
41 *
42 * @author Jonathan Payne
43 */
44
45public class FtpClient extends TransferProtocolClient {
46 public static final int FTP_PORT = 21;
47
48 static int FTP_SUCCESS = 1;
49 static int FTP_TRY_AGAIN = 2;
50 static int FTP_ERROR = 3;
51
52 /** remember the ftp server name because we may need it */
53 private String serverName = null;
54
55 /** socket for data transfer */
56 private boolean replyPending = false;
57 private boolean binaryMode = false;
58 private boolean loggedIn = false;
59
60 /** regexp pool of hosts for which we should connect directly, not Proxy
61 * these are intialized from a property.
62 */
63 private static RegexpPool nonProxyHostsPool = null;
64
65 /** The string soucre of nonProxyHostsPool
66 */
67 private static String nonProxyHostsSource = null;
68
69 /** last command issued */
70 String command;
71
72 /** The last reply code from the ftp daemon. */
73 int lastReplyCode;
74
75 /** Welcome message from the server, if any. */
76 public String welcomeMsg;
77
78
79 /* these methods are used to determine whether ftp urls are sent to */
80 /* an http server instead of using a direct connection to the */
81 /* host. They aren't used directly here. */
82 /**
83 * @return if the networking layer should send ftp connections through
84 * a proxy
85 */
86 public static boolean getUseFtpProxy() {
87 // if the ftp.proxyHost is set, use it!
88 return (getFtpProxyHost() != null);
89 }
90
91 /**
92 * @return the host to use, or null if none has been specified
93 */
94 public static String getFtpProxyHost() {
95 return java.security.AccessController.doPrivileged(
96 new java.security.PrivilegedAction<String>() {
97 public String run() {
98 String result = System.getProperty("ftp.proxyHost");
99 if (result == null) {
100 result = System.getProperty("ftpProxyHost");
101 }
102 if (result == null) {
103 // as a last resort we use the general one if ftp.useProxy
104 // is true
105 if (Boolean.getBoolean("ftp.useProxy")) {
106 result = System.getProperty("proxyHost");
107 }
108 }
109 return result;
110 }
111 });
112 }
113
114 /**
115 * @return the proxy port to use. Will default reasonably if not set.
116 */
117 public static int getFtpProxyPort() {
118 final int result[] = {80};
119 java.security.AccessController.doPrivileged(
120 new java.security.PrivilegedAction() {
121 public Object run() {
122
123 String tmp = System.getProperty("ftp.proxyPort");
124 if (tmp == null) {
125 // for compatibility with 1.0.2
126 tmp = System.getProperty("ftpProxyPort");
127 }
128 if (tmp == null) {
129 // as a last resort we use the general one if ftp.useProxy
130 // is true
131 if (Boolean.getBoolean("ftp.useProxy")) {
132 tmp = System.getProperty("proxyPort");
133 }
134 }
135 if (tmp != null) {
136 result[0] = Integer.parseInt(tmp);
137 }
138 return null;
139 }
140 });
141 return result[0];
142 }
143
144 public static boolean matchNonProxyHosts(String host) {
145 synchronized (FtpClient.class) {
146 String rawList = java.security.AccessController.doPrivileged(
147 new sun.security.action.GetPropertyAction("ftp.nonProxyHosts"));
148 if (rawList == null) {
149 nonProxyHostsPool = null;
150 } else {
151 if (!rawList.equals(nonProxyHostsSource)) {
152 RegexpPool pool = new RegexpPool();
153 StringTokenizer st = new StringTokenizer(rawList, "|", false);
154 try {
155 while (st.hasMoreTokens()) {
156 pool.add(st.nextToken().toLowerCase(), Boolean.TRUE);
157 }
158 } catch (sun.misc.REException ex) {
159 System.err.println("Error in http.nonProxyHosts system property: " + ex);
160 }
161 nonProxyHostsPool = pool;
162 }
163 }
164 nonProxyHostsSource = rawList;
165 }
166
167 if (nonProxyHostsPool == null) {
168 return false;
169 }
170
171 if (nonProxyHostsPool.match(host) != null) {
172 return true;
173 } else {
174 return false;
175 }
176 }
177
178 /**
179 * issue the QUIT command to the FTP server and close the connection.
180 *
181 * @exception FtpProtocolException if an error occured
182 */
183 public void closeServer() throws IOException {
184 if (serverIsOpen()) {
185 issueCommand("QUIT");
186 super.closeServer();
187 }
188 }
189
190 /**
191 * Send a command to the FTP server.
192 *
193 * @param cmd String containing the command
194 * @return reply code
195 *
196 * @exception FtpProtocolException if an error occured
197 */
198 protected int issueCommand(String cmd) throws IOException {
199 command = cmd;
200
201 int reply;
202
203 while (replyPending) {
204 replyPending = false;
205 if (readReply() == FTP_ERROR)
206 throw new FtpProtocolException("Error reading FTP pending reply\n");
207 }
208 do {
209 sendServer(cmd + "\r\n");
210 reply = readReply();
211 } while (reply == FTP_TRY_AGAIN);
212 return reply;
213 }
214
215 /**
216 * Send a command to the FTP server and check for success.
217 *
218 * @param cmd String containing the command
219 *
220 * @exception FtpProtocolException if an error occured
221 */
222 protected void issueCommandCheck(String cmd) throws IOException {
223 if (issueCommand(cmd) != FTP_SUCCESS)
224 throw new FtpProtocolException(cmd + ":" + getResponseString());
225 }
226
227 /**
228 * Read the reply from the FTP server.
229 *
230 * @return FTP_SUCCESS or FTP_ERROR depending on success
231 * @exception FtpProtocolException if an error occured
232 */
233 protected int readReply() throws IOException {
234 lastReplyCode = readServerResponse();
235
236 switch (lastReplyCode / 100) {
237 case 1:
238 replyPending = true;
239 /* falls into ... */
240
241 case 2:
242 case 3:
243 return FTP_SUCCESS;
244
245 case 5:
246 if (lastReplyCode == 530) {
247 if (!loggedIn) {
248 throw new FtpLoginException("Not logged in");
249 }
250 return FTP_ERROR;
251 }
252 if (lastReplyCode == 550) {
253 throw new FileNotFoundException(command + ": " + getResponseString());
254 }
255 }
256
257 /* this statement is not reached */
258 return FTP_ERROR;
259 }
260
261 /**
262 * Tries to open a Data Connection in "PASSIVE" mode by issuing a EPSV or
263 * PASV command then opening a Socket to the specified address & port
264 *
265 * @return the opened socket
266 * @exception FtpProtocolException if an error occurs when issuing the
267 * PASV command to the ftp server.
268 */
269 protected Socket openPassiveDataConnection() throws IOException {
270 String serverAnswer;
271 int port;
272 InetSocketAddress dest = null;
273
274 /**
275 * Here is the idea:
276 *
277 * - First we want to try the new (and IPv6 compatible) EPSV command
278 * But since we want to be nice with NAT software, we'll issue the
279 * EPSV ALL cmd first.
280 * EPSV is documented in RFC2428
281 * - If EPSV fails, then we fall back to the older, yet OK PASV command
282 * - If PASV fails as well, then we throw an exception and the calling method
283 * will have to try the EPRT or PORT command
284 */
285 if (issueCommand("EPSV ALL") == FTP_SUCCESS) {
286 // We can safely use EPSV commands
287 if (issueCommand("EPSV") == FTP_ERROR)
288 throw new FtpProtocolException("EPSV Failed: " + getResponseString());
289 serverAnswer = getResponseString();
290
291 // The response string from a EPSV command will contain the port number
292 // the format will be :
293 // 229 Entering Extended Passive Mode (|||58210|)
294 //
295 // So we'll use the regular expresions package to parse the output.
296
297 Pattern p = Pattern.compile("^229 .* \\(\\|\\|\\|(\\d+)\\|\\)");
298 Matcher m = p.matcher(serverAnswer);
299 if (! m.find())
300 throw new FtpProtocolException("EPSV failed : " + serverAnswer);
301 // Yay! Let's extract the port number
302 String s = m.group(1);
303 port = Integer.parseInt(s);
304 InetAddress add = serverSocket.getInetAddress();
305 if (add != null) {
306 dest = new InetSocketAddress(add, port);
307 } else {
308 // This means we used an Unresolved address to connect in
309 // the first place. Most likely because the proxy is doing
310 // the name resolution for us, so let's keep using unresolved
311 // address.
312 dest = InetSocketAddress.createUnresolved(serverName, port);
313 }
314 } else {
315 // EPSV ALL failed, so Let's try the regular PASV cmd
316 if (issueCommand("PASV") == FTP_ERROR)
317 throw new FtpProtocolException("PASV failed: " + getResponseString());
318 serverAnswer = getResponseString();
319
320 // Let's parse the response String to get the IP & port to connect to
321 // the String should be in the following format :
322 //
323 // 227 Entering Passive Mode (A1,A2,A3,A4,p1,p2)
324 //
325 // Note that the two parenthesis are optional
326 //
327 // The IP address is A1.A2.A3.A4 and the port is p1 * 256 + p2
328 //
329 // The regular expression is a bit more complex this time, because the
330 // parenthesis are optionals and we have to use 3 groups.
331
332 Pattern p = Pattern.compile("227 .* \\(?(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)?");
333 Matcher m = p.matcher(serverAnswer);
334 if (! m.find())
335 throw new FtpProtocolException("PASV failed : " + serverAnswer);
336 // Get port number out of group 2 & 3
337 port = Integer.parseInt(m.group(3)) + (Integer.parseInt(m.group(2)) << 8);
338 // IP address is simple
339 String s = m.group(1).replace(',','.');
340 dest = new InetSocketAddress(s, port);
341 }
342 // Got everything, let's open the socket!
343 Socket s;
344 if (proxy != null) {
345 if (proxy.type() == Proxy.Type.SOCKS) {
346 s = (Socket) AccessController.doPrivileged(
347 new PrivilegedAction() {
348 public Object run() {
349 return new Socket(proxy);
350 }});
351 } else
352 s = new Socket(Proxy.NO_PROXY);
353 } else
354 s = new Socket();
355 if (connectTimeout >= 0) {
356 s.connect(dest, connectTimeout);
357 } else {
358 if (defaultConnectTimeout > 0) {
359 s.connect(dest, defaultConnectTimeout);
360 } else {
361 s.connect(dest);
362 }
363 }
364 if (readTimeout >= 0)
365 s.setSoTimeout(readTimeout);
366 else
367 if (defaultSoTimeout > 0) {
368 s.setSoTimeout(defaultSoTimeout);
369 }
370 return s;
371 }
372
373 /**
374 * Tries to open a Data Connection with the server. It will first try a passive
375 * mode connection, then, if it fails, a more traditional PORT command
376 *
377 * @param cmd the command to execute (RETR, STOR, etc...)
378 * @return the opened socket
379 *
380 * @exception FtpProtocolException if an error occurs when issuing the
381 * PORT command to the ftp server.
382 */
383 protected Socket openDataConnection(String cmd) throws IOException {
384 ServerSocket portSocket;
385 Socket clientSocket = null;
386 String portCmd;
387 InetAddress myAddress;
388 IOException e;
389
390 // Let's try passive mode first
391 try {
392 clientSocket = openPassiveDataConnection();
393 } catch (IOException ex) {
394 clientSocket = null;
395 }
396 if (clientSocket != null) {
397 // We did get a clientSocket, so the passive mode worked
398 // Let's issue the command (GET, DIR, ...)
399 try {
400 if (issueCommand(cmd) == FTP_ERROR) {
401 clientSocket.close();
402 throw new FtpProtocolException(getResponseString());
403 } else
404 return clientSocket;
405 } catch (IOException ioe) {
406 clientSocket.close();
407 throw ioe;
408 }
409 }
410
411 assert(clientSocket == null);
412
413 // Passive mode failed, let's fall back to the good old "PORT"
414
415 if (proxy != null && proxy.type() == Proxy.Type.SOCKS) {
416 // We're behind a firewall and the passive mode fail,
417 // since we can't accept a connection through SOCKS (yet)
418 // throw an exception
419 throw new FtpProtocolException("Passive mode failed");
420 } else
421 portSocket = new ServerSocket(0, 1);
422 try {
423 myAddress = portSocket.getInetAddress();
424 if (myAddress.isAnyLocalAddress())
425 myAddress = getLocalAddress();
426 // Let's try the new, IPv6 compatible EPRT command
427 // See RFC2428 for specifics
428 // Some FTP servers (like the one on Solaris) are bugged, they
429 // will accept the EPRT command but then, the subsequent command
430 // (e.g. RETR) will fail, so we have to check BOTH results (the
431 // EPRT cmd then the actual command) to decide wether we should
432 // fall back on the older PORT command.
433 portCmd = "EPRT |" +
434 ((myAddress instanceof Inet6Address) ? "2" : "1") + "|" +
435 myAddress.getHostAddress() +"|" +
436 portSocket.getLocalPort()+"|";
437 if (issueCommand(portCmd) == FTP_ERROR ||
438 issueCommand(cmd) == FTP_ERROR) {
439 // The EPRT command failed, let's fall back to good old PORT
440 portCmd = "PORT ";
441 byte[] addr = myAddress.getAddress();
442
443 /* append host addr */
444 for (int i = 0; i < addr.length; i++) {
445 portCmd = portCmd + (addr[i] & 0xFF) + ",";
446 }
447
448 /* append port number */
449 portCmd = portCmd + ((portSocket.getLocalPort() >>> 8) & 0xff) + ","
450 + (portSocket.getLocalPort() & 0xff);
451 if (issueCommand(portCmd) == FTP_ERROR) {
452 e = new FtpProtocolException("PORT :" + getResponseString());
453 throw e;
454 }
455 if (issueCommand(cmd) == FTP_ERROR) {
456 e = new FtpProtocolException(cmd + ":" + getResponseString());
457 throw e;
458 }
459 }
460 // Either the EPRT or the PORT command was successful
461 // Let's create the client socket
462 if (connectTimeout >= 0) {
463 portSocket.setSoTimeout(connectTimeout);
464 } else {
465 if (defaultConnectTimeout > 0)
466 portSocket.setSoTimeout(defaultConnectTimeout);
467 }
468 clientSocket = portSocket.accept();
469 if (readTimeout >= 0)
470 clientSocket.setSoTimeout(readTimeout);
471 else {
472 if (defaultSoTimeout > 0)
473 clientSocket.setSoTimeout(defaultSoTimeout);
474 }
475 } finally {
476 portSocket.close();
477 }
478
479 return clientSocket;
480 }
481
482 /* public methods */
483
484 /**
485 * Open a FTP connection to host <i>host</i>.
486 *
487 * @param host The hostname of the ftp server
488 *
489 * @exception FtpProtocolException if connection fails
490 */
491 public void openServer(String host) throws IOException {
492 openServer(host, FTP_PORT);
493 }
494
495 /**
496 * Open a FTP connection to host <i>host</i> on port <i>port</i>.
497 *
498 * @param host the hostname of the ftp server
499 * @param port the port to connect to (usually 21)
500 *
501 * @exception FtpProtocolException if connection fails
502 */
503 public void openServer(String host, int port) throws IOException {
504 this.serverName = host;
505 super.openServer(host, port);
506 if (readReply() == FTP_ERROR)
507 throw new FtpProtocolException("Welcome message: " +
508 getResponseString());
509 }
510
511
512 /**
513 * login user to a host with username <i>user</i> and password
514 * <i>password</i>
515 *
516 * @param user Username to use at login
517 * @param password Password to use at login or null of none is needed
518 *
519 * @exception FtpLoginException if login is unsuccesful
520 */
521 public void login(String user, String password) throws IOException {
522 if (!serverIsOpen())
523 throw new FtpLoginException("not connected to host");
524 if (user == null || user.length() == 0)
525 return;
526 if (issueCommand("USER " + user) == FTP_ERROR)
527 throw new FtpLoginException("user " + user + " : " + getResponseString());
528 /*
529 * Checks for "331 User name okay, need password." answer
530 */
531
532 if (lastReplyCode == 331)
533 if ((password == null) || (password.length() == 0) ||
534 (issueCommand("PASS " + password) == FTP_ERROR))
535 throw new FtpLoginException("password: " + getResponseString());
536
537 // keep the welcome message around so we can
538 // put it in the resulting HTML page.
539 String l;
540 StringBuffer sb = new StringBuffer();
541 for (int i = 0; i < serverResponse.size(); i++) {
542 l = (String)serverResponse.elementAt(i);
543 if (l != null) {
544 if (l.length() >= 4 && l.startsWith("230")) {
545 // get rid of the "230-" prefix
546 l = l.substring(4);
547 }
548 sb.append(l);
549 }
550 }
551 welcomeMsg = sb.toString();
552 loggedIn = true;
553 }
554
555 /**
556 * GET a file from the FTP server
557 *
558 * @param filename name of the file to retrieve
559 * @return the <code>InputStream</code> to read the file from
560 *
561 * @exception FileNotFoundException if the file can't be opened
562 */
563 public TelnetInputStream get(String filename) throws IOException {
564 Socket s;
565
566 try {
567 s = openDataConnection("RETR " + filename);
568 } catch (FileNotFoundException fileException) {
569 /* Well, "/" might not be the file delimitor for this
570 particular ftp server, so let's try a series of
571 "cd" commands to get to the right place. */
572 /* But don't try this if there are no '/' in the path */
573 if (filename.indexOf('/') == -1)
574 throw fileException;
575
576 StringTokenizer t = new StringTokenizer(filename, "/");
577 String pathElement = null;
578
579 while (t.hasMoreElements()) {
580 pathElement = t.nextToken();
581
582 if (!t.hasMoreElements()) {
583 /* This is the file component. Look it up now. */
584 break;
585 }
586 try {
587 cd(pathElement);
588 } catch (FtpProtocolException e) {
589 /* Giving up. */
590 throw fileException;
591 }
592 }
593 if (pathElement != null) {
594 s = openDataConnection("RETR " + pathElement);
595 } else {
596 throw fileException;
597 }
598 }
599
600 return new TelnetInputStream(s.getInputStream(), binaryMode);
601 }
602
603 /**
604 * PUT a file to the FTP server
605 *
606 * @param filename name of the file to store
607 * @return the <code>OutputStream</code> to write the file to
608 *
609 */
610 public TelnetOutputStream put(String filename) throws IOException {
611 Socket s = openDataConnection("STOR " + filename);
612 TelnetOutputStream out = new TelnetOutputStream(s.getOutputStream(), binaryMode);
613 if (!binaryMode)
614 out.setStickyCRLF(true);
615 return out;
616 }
617
618 /**
619 * Append to a file on the FTP server
620 *
621 * @param filename name of the file to append to
622 * @return the <code>OutputStream</code> to write the file to
623 *
624 */
625 public TelnetOutputStream append(String filename) throws IOException {
626 Socket s = openDataConnection("APPE " + filename);
627 TelnetOutputStream out = new TelnetOutputStream(s.getOutputStream(), binaryMode);
628 if (!binaryMode)
629 out.setStickyCRLF(true);
630
631 return out;
632 }
633
634 /**
635 * LIST files in the current directory on a remote FTP server
636 *
637 * @return the <code>InputStream</code> to read the list from
638 *
639 */
640 public TelnetInputStream list() throws IOException {
641 Socket s = openDataConnection("LIST");
642
643 return new TelnetInputStream(s.getInputStream(), binaryMode);
644 }
645
646 /**
647 * List (NLST) file names on a remote FTP server
648 *
649 * @param path pathname to the directory to list, null for current
650 * directory
651 * @return the <code>InputStream</code> to read the list from
652 * @exception <code>FtpProtocolException</code>
653 */
654 public TelnetInputStream nameList(String path) throws IOException {
655 Socket s;
656
657 if (path != null)
658 s = openDataConnection("NLST " + path);
659 else
660 s = openDataConnection("NLST");
661 return new TelnetInputStream(s.getInputStream(), binaryMode);
662 }
663
664 /**
665 * CD to a specific directory on a remote FTP server
666 *
667 * @param remoteDirectory path of the directory to CD to
668 *
669 * @exception <code>FtpProtocolException</code>
670 */
671 public void cd(String remoteDirectory) throws IOException {
672 if (remoteDirectory == null ||
673 "".equals(remoteDirectory))
674 return;
675 issueCommandCheck("CWD " + remoteDirectory);
676 }
677
678 /**
679 * CD to the parent directory on a remote FTP server
680 *
681 */
682 public void cdUp() throws IOException {
683 issueCommandCheck("CDUP");
684 }
685
686 /**
687 * Print working directory of remote FTP server
688 *
689 * @exception FtpProtocolException if the command fails
690 */
691 public String pwd() throws IOException {
692 String answ;
693
694 issueCommandCheck("PWD");
695 /*
696 * answer will be of the following format :
697 *
698 * 257 "/" is current directory.
699 */
700 answ = getResponseString();
701 if (!answ.startsWith("257"))
702 throw new FtpProtocolException("PWD failed. " + answ);
703 return answ.substring(5, answ.lastIndexOf('"'));
704 }
705
706 /**
707 * Set transfer type to 'I'
708 *
709 * @exception FtpProtocolException if the command fails
710 */
711 public void binary() throws IOException {
712 issueCommandCheck("TYPE I");
713 binaryMode = true;
714 }
715
716 /**
717 * Set transfer type to 'A'
718 *
719 * @exception FtpProtocolException if the command fails
720 */
721 public void ascii() throws IOException {
722 issueCommandCheck("TYPE A");
723 binaryMode = false;
724 }
725
726 /**
727 * Rename a file on the ftp server
728 *
729 * @exception FtpProtocolException if the command fails
730 */
731 public void rename(String from, String to) throws IOException {
732 issueCommandCheck("RNFR " + from);
733 issueCommandCheck("RNTO " + to);
734 }
735
736 /**
737 * Get the "System string" from the FTP server
738 *
739 * @exception FtpProtocolException if it fails
740 */
741 public String system() throws IOException {
742 String answ;
743 issueCommandCheck("SYST");
744 answ = getResponseString();
745 if (!answ.startsWith("215"))
746 throw new FtpProtocolException("SYST failed." + answ);
747 return answ.substring(4); // Skip "215 "
748 }
749
750 /**
751 * Send a No-operation command. It's usefull for testing the connection status
752 *
753 * @exception FtpProtocolException if the command fails
754 */
755 public void noop() throws IOException {
756 issueCommandCheck("NOOP");
757 }
758
759 /**
760 * Reinitialize the USER parameters on the FTp server
761 *
762 * @exception FtpProtocolException if the command fails
763 */
764 public void reInit() throws IOException {
765 issueCommandCheck("REIN");
766 loggedIn = false;
767 }
768
769 /**
770 * New FTP client connected to host <i>host</i>.
771 *
772 * @param host Hostname of the FTP server
773 *
774 * @exception FtpProtocolException if the connection fails
775 */
776 public FtpClient(String host) throws IOException {
777 super();
778 openServer(host, FTP_PORT);
779 }
780
781 /**
782 * New FTP client connected to host <i>host</i>, port <i>port</i>.
783 *
784 * @param host Hostname of the FTP server
785 * @param port port number to connect to (usually 21)
786 *
787 * @exception FtpProtocolException if the connection fails
788 */
789 public FtpClient(String host, int port) throws IOException {
790 super();
791 openServer(host, port);
792 }
793
794 /** Create an uninitialized FTP client. */
795 public FtpClient() {}
796
797 public FtpClient(Proxy p) {
798 proxy = p;
799 }
800
801 protected void finalize() throws IOException {
802 /**
803 * Do not call the "normal" closeServer() as we want finalization
804 * to be as efficient as possible
805 */
806 if (serverIsOpen())
807 super.closeServer();
808 }
809
810}