001 /* 002 * ftp4j - A pure Java FTP client library 003 * 004 * Copyright (C) 2008 Carlo Pelliccia (www.sauronsoftware.it) 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 018 */ 019 package it.sauronsoftware.ftp4j; 020 021 import it.sauronsoftware.ftp4j.connectors.DirectConnector; 022 import it.sauronsoftware.ftp4j.extrecognizers.DefaultTextualExtensionRecognizer; 023 import it.sauronsoftware.ftp4j.extrecognizers.ParametricTextualExtensionRecognizer; 024 import it.sauronsoftware.ftp4j.listparsers.DOSListParser; 025 import it.sauronsoftware.ftp4j.listparsers.EPLFListParser; 026 import it.sauronsoftware.ftp4j.listparsers.NetWareListParser; 027 import it.sauronsoftware.ftp4j.listparsers.UnixListParser; 028 029 import java.io.File; 030 import java.io.FileInputStream; 031 import java.io.FileNotFoundException; 032 import java.io.FileOutputStream; 033 import java.io.IOException; 034 import java.io.InputStream; 035 import java.io.InputStreamReader; 036 import java.io.OutputStream; 037 import java.io.OutputStreamWriter; 038 import java.io.Reader; 039 import java.io.Writer; 040 import java.net.InetAddress; 041 import java.text.DateFormat; 042 import java.text.ParseException; 043 import java.text.SimpleDateFormat; 044 import java.util.ArrayList; 045 import java.util.Date; 046 import java.util.Iterator; 047 import java.util.StringTokenizer; 048 import java.util.regex.Matcher; 049 import java.util.regex.Pattern; 050 051 /** 052 * This class implements a FTP client. 053 * 054 * You can use an instance of this class to connect to a remote FTP site and do 055 * FTP operations like directory listing, file upload and download, resume a 056 * broken upload/download and so on. 057 * 058 * The common flow is: create the object, connect to a remote FTP site with the 059 * connect() method, authenticate with login(), do anything you need with the 060 * contents of the remote site, quit the site with disconnect(). 061 * 062 * A FTPClient object can handle a connection per time. Once you have used and 063 * disconnected a FTPClient object you can use it again to connect another FTP 064 * server. 065 * 066 * @author Carlo Pelliccia 067 * @version 1.1 068 */ 069 public class FTPClient { 070 071 /** 072 * The constant for the AUTO file transfer type. It lets the client pick 073 * between textual and binary types, depending on the extension of the file 074 * exchanged through a textual extension recognizer. 075 */ 076 public static final int TYPE_AUTO = 0; 077 078 /** 079 * The constant for the TEXTUAL file transfer type. It means that the data 080 * sent or received is treated as textual information. This implies charset 081 * conversion during the transfer. 082 */ 083 public static final int TYPE_TEXTUAL = 1; 084 085 /** 086 * The constant for the BINARY file transfer type. It means that the data 087 * sent or received is treated as a binary stream. The data is taken "as 088 * is", without any charset conversion. 089 */ 090 public static final int TYPE_BINARY = 2; 091 092 /** 093 * The DateFormat object used to parse the reply to a MDTM command. 094 */ 095 private static final DateFormat MDTM_DATE_FORMAT = new SimpleDateFormat( 096 "yyyyMMddHHmmss"); 097 098 /** 099 * The RegExp Pattern object used to parse the reply to a PASV command. 100 */ 101 private static final Pattern PASV_PATTERN = Pattern 102 .compile("\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}"); 103 104 /** 105 * The RegExp Pattern object used to parse the reply to a PWD command. 106 */ 107 private static final Pattern PWD_PATTERN = Pattern.compile("\"/.*\""); 108 109 /** 110 * The connector used to connect the remote host. 111 */ 112 private FTPConnector connector = new DirectConnector(); 113 114 /** 115 * The FTPCommunicationListener objects registered on the client. 116 */ 117 private ArrayList communicationListeners = new ArrayList(); 118 119 /** 120 * The FTPListParser objects registered on the client. 121 */ 122 private ArrayList listParsers = new ArrayList(); 123 124 /** 125 * The textual extension recognizer used by the client. 126 */ 127 private FTPTextualExtensionRecognizer textualExtensionRecognizer = DefaultTextualExtensionRecognizer 128 .getInstance(); 129 130 /** 131 * The FTPListParser used successfully during previous connection-scope list 132 * operations. 133 */ 134 private FTPListParser parser = null; 135 136 /** 137 * If the client is connected, it reports the remote host name or address. 138 */ 139 private String host = null; 140 141 /** 142 * If the client is connected, it reports the remote port number. 143 */ 144 private int port = 0; 145 146 /** 147 * If the client is authenticated, it reports the authentication username. 148 */ 149 private String username; 150 151 /** 152 * If the client is authenticated, it reports the authentication password. 153 */ 154 private String password; 155 156 /** 157 * The flag reporting the connection status. 158 */ 159 private boolean connected = false; 160 161 /** 162 * The flag reporting the authentication status. 163 */ 164 private boolean authenticated = false; 165 166 /** 167 * The flag for the passive FTP data transfer mode. Default value is true, 168 * cause it's usually the preferred FTP operating mode. 169 */ 170 private boolean passive = true; 171 172 /** 173 * The type of the data transfer contents (auto, textual, binary). The value 174 * should be one of TYPE_AUTO, TYPE_TEXTUAL and TYPE_BINARY constants. 175 * Default value is TYPE_AUTO. 176 */ 177 private int type = TYPE_AUTO; 178 179 /** 180 * The name of the charset used to establish textual communications. If not 181 * null the client will use always the given charset. If null the client 182 * tries to auto-detect the server charset. If this attempt fails the client 183 * will use the machine current charset. 184 */ 185 private String charset = null; 186 187 /** 188 * A flag used to mark whether the connected server supports UTF-8 pathnames 189 * encoding. 190 */ 191 private boolean utf8Supported = false; 192 193 /** 194 * This flag reports if there's any ongoing abortable data transfer 195 * operation. Its value should be accessed only under the eye of the 196 * abortLock synchronization object. 197 */ 198 private boolean ongoingDataTransfer = false; 199 200 /** 201 * The InputStream used for data transfer operations. 202 */ 203 private InputStream dataTransferInputStream = null; 204 205 /** 206 * The OutputStream used for data transfer operations. 207 */ 208 private OutputStream dataTransferOutputStream = null; 209 210 /** 211 * This flag turns to true when any data transfer stream is closed due to an 212 * abort suggestion. 213 */ 214 private boolean aborted = false; 215 216 /** 217 * Lock object used for synchronization. 218 */ 219 private Object lock = new Object(); 220 221 /** 222 * Lock object used for synchronization in abort operations. 223 */ 224 private Object abortLock = new Object(); 225 226 /** 227 * The communication channel established with the server. 228 */ 229 private FTPCommunicationChannel communication = null; 230 231 /** 232 * Builds and initializes the client. 233 */ 234 public FTPClient() { 235 // The built-in parsers. 236 addListParser(new UnixListParser()); 237 addListParser(new DOSListParser()); 238 addListParser(new EPLFListParser()); 239 addListParser(new NetWareListParser()); 240 } 241 242 /** 243 * This method returns the connector used to connect the remote host. 244 * 245 * @return The connector used to connect the remote host. 246 */ 247 public FTPConnector getConnector() { 248 return connector; 249 } 250 251 /** 252 * This method sets the connector used to connect the remote host. 253 * 254 * Default one is a 255 * it.sauronsoftware.ftp4j.connectors.direct.DirectConnector instance. 256 * 257 * @param connector 258 * The connector used to connect the remote host. 259 * @see DirectConnector 260 */ 261 public void setConnector(FTPConnector connector) { 262 this.connector = connector; 263 } 264 265 /** 266 * This method enables/disables the use of the passive mode. 267 * 268 * @param passive 269 * If true the passive mode is enabled. 270 */ 271 public void setPassive(boolean passive) { 272 synchronized (lock) { 273 this.passive = passive; 274 } 275 } 276 277 /** 278 * This methods sets how to treat the contents during a file transfer. 279 * 280 * The type supplied should be one of TYPE_AUTO, TYPE_TEXTUAL or TYPE_BINARY 281 * constants. Default value is TYPE_AUTO. 282 * 283 * TYPE_TEXTUAL means that the data sent or received is treated as textual 284 * information. This implies charset conversion during the transfer. 285 * 286 * TYPE_BINARY means that the data sent or received is treated as a binary 287 * stream. The data is taken "as is", without any charset conversion. 288 * 289 * TYPE_AUTO lets the client pick between textual and binary types, 290 * depending on the extension of the file exchanged, using a 291 * FTPTextualExtensionRecognizer instance, which could be set through the 292 * setTextualExtensionRecognizer method. The default recognizer is an 293 * instance of {@link DefaultTextualExtensionRecognizer}. 294 * 295 * @param type 296 * The type. 297 * @throws IllegalArgumentException 298 * If the supplied type is not valid. 299 * @see FTPClient#setTextualExtensionRecognizer(FTPTextualExtensionRecognizer) 300 * @see DefaultTextualExtensionRecognizer 301 */ 302 public void setType(int type) throws IllegalArgumentException { 303 if (type != TYPE_AUTO && type != TYPE_BINARY && type != TYPE_TEXTUAL) { 304 throw new IllegalArgumentException("Invalid type"); 305 } 306 synchronized (lock) { 307 this.type = type; 308 } 309 } 310 311 /** 312 * This method returns the value suggesting how the client encode and decode 313 * the contents during a data transfer. 314 * 315 * @return The type as a numeric value. The value could be compared to the 316 * constants TYPE_AUTO, TYPE_BINARY and TYPE_TEXTUAL. 317 */ 318 public int getType() { 319 synchronized (lock) { 320 return type; 321 } 322 } 323 324 /** 325 * Returns the name of the charset used to establish textual communications. 326 * If not null the client will use always the given charset. If null the 327 * client tries to auto-detect the server charset. If this attempt fails the 328 * client will use the machine current charset. 329 * 330 * @return The name of the charset used to establish textual communications. 331 * @since 1.1 332 */ 333 public String getCharset() { 334 synchronized (lock) { 335 return charset; 336 } 337 } 338 339 /** 340 * Sets the name of the charset used to establish textual communications. If 341 * not null the client will use always the given charset. If null the client 342 * tries to auto-detect the server charset. If this attempt fails the client 343 * will use the machine current charset. 344 * 345 * @param charset 346 * The name of the charset used to establish textual 347 * communications. 348 * @since 1.1 349 */ 350 public void setCharset(String charset) { 351 synchronized (lock) { 352 this.charset = charset; 353 if (connected) { 354 try { 355 communication.changeCharset(pickCharset()); 356 } catch (IOException e) { 357 e.printStackTrace(); 358 } 359 } 360 } 361 } 362 363 /** 364 * This method returns the textual extension recognizer used by the client. 365 * 366 * Default one is {@link DefaultTextualExtensionRecognizer}. 367 * 368 * @return The textual extension recognizer used by the client. 369 * @see DefaultTextualExtensionRecognizer 370 */ 371 public FTPTextualExtensionRecognizer getTextualExtensionRecognizer() { 372 synchronized (lock) { 373 return textualExtensionRecognizer; 374 } 375 } 376 377 /** 378 * This method sets the textual extension recognizer used by the client. 379 * 380 * The default one is {@link DefaultTextualExtensionRecognizer}. 381 * 382 * You can plug your own by implementing the 383 * {@link FTPTextualExtensionRecognizer} interface. For your convenience the 384 * ftp4j gives you another FTPTextualExtensionRecognizer implementation, 385 * which is {@link ParametricTextualExtensionRecognizer}. 386 * 387 * @param textualExtensionRecognizer 388 * The textual extension recognizer used by the client. 389 * @see DefaultTextualExtensionRecognizer 390 * @see ParametricTextualExtensionRecognizer 391 */ 392 public void setTextualExtensionRecognizer( 393 FTPTextualExtensionRecognizer textualExtensionRecognizer) { 394 synchronized (lock) { 395 this.textualExtensionRecognizer = textualExtensionRecognizer; 396 } 397 } 398 399 /** 400 * This method tests if this client is authenticated. 401 * 402 * @return true if this client is authenticated, false otherwise. 403 */ 404 public boolean isAuthenticated() { 405 synchronized (lock) { 406 return authenticated; 407 } 408 } 409 410 /** 411 * This method tests if this client is connected to a remote FTP server. 412 * 413 * @return true if this client is connected to a remote FTP server, false 414 * otherwise. 415 */ 416 public boolean isConnected() { 417 synchronized (lock) { 418 return connected; 419 } 420 } 421 422 /** 423 * This method tests if this client works in passive FTP mode. 424 * 425 * @return true if this client is configured to work in passive FTP mode. 426 */ 427 public boolean isPassive() { 428 synchronized (lock) { 429 return passive; 430 } 431 } 432 433 /** 434 * If the client is connected, it reports the remote host name or address. 435 * 436 * @return The remote host name or address. 437 */ 438 public String getHost() { 439 synchronized (lock) { 440 return host; 441 } 442 } 443 444 /** 445 * If the client is connected, it reports the remote port number. 446 * 447 * @return The remote port number. 448 */ 449 public int getPort() { 450 synchronized (lock) { 451 return port; 452 } 453 } 454 455 /** 456 * If the client is authenticated, it reports the authentication password. 457 * 458 * @return The authentication password. 459 */ 460 public String getPassword() { 461 synchronized (lock) { 462 return password; 463 } 464 } 465 466 /** 467 * If the client is authenticated, it reports the authentication username. 468 * 469 * @return The authentication username. 470 */ 471 public String getUsername() { 472 synchronized (lock) { 473 return username; 474 } 475 } 476 477 /** 478 * This method adds a FTPCommunicationListener to the object. 479 * 480 * @param listener 481 * The listener. 482 */ 483 public void addCommunicationListener(FTPCommunicationListener listener) { 484 synchronized (lock) { 485 communicationListeners.add(listener); 486 if (communication != null) { 487 communication.addCommunicationListener(listener); 488 } 489 } 490 } 491 492 /** 493 * This method removes a FTPCommunicationListener previously added to the 494 * object. 495 * 496 * @param listener 497 * The listener to be removed. 498 */ 499 public void removeCommunicationListener(FTPCommunicationListener listener) { 500 synchronized (lock) { 501 communicationListeners.remove(listener); 502 if (communication != null) { 503 communication.removeCommunicationListener(listener); 504 } 505 } 506 } 507 508 /** 509 * This method returns a list with all the {@link FTPCommunicationListener} 510 * used by the client. 511 * 512 * @return A list with all the FTPCommunicationListener used by the client. 513 */ 514 public FTPCommunicationListener[] getCommunicationListeners() { 515 synchronized (lock) { 516 int size = communicationListeners.size(); 517 FTPCommunicationListener[] ret = new FTPCommunicationListener[size]; 518 for (int i = 0; i < size; i++) { 519 ret[i] = (FTPCommunicationListener) communicationListeners 520 .get(i); 521 } 522 return ret; 523 } 524 } 525 526 /** 527 * This method adds a {@link FTPListParser} to the object. 528 * 529 * @param listParser 530 * The list parser. 531 */ 532 public void addListParser(FTPListParser listParser) { 533 synchronized (lock) { 534 listParsers.add(listParser); 535 } 536 } 537 538 /** 539 * This method removes a {@link FTPListParser} previously added to the 540 * object. 541 * 542 * @param listParser 543 * The list parser to be removed. 544 */ 545 public void removeListParser(FTPListParser listParser) { 546 synchronized (lock) { 547 listParsers.remove(listParser); 548 } 549 } 550 551 /** 552 * This method returns a list with all the {@link FTPListParser} used by the 553 * client. 554 * 555 * @return A list with all the FTPListParsers used by the client. 556 */ 557 public FTPListParser[] getListParsers() { 558 synchronized (lock) { 559 int size = listParsers.size(); 560 FTPListParser[] ret = new FTPListParser[size]; 561 for (int i = 0; i < size; i++) { 562 ret[i] = (FTPListParser) listParsers.get(i); 563 } 564 return ret; 565 } 566 } 567 568 /** 569 * This method connects the client to the remote FTP host, using the default 570 * port value 21. 571 * 572 * @param host 573 * The hostname of the remote server. 574 * @return The server welcome message, one line per array element. 575 * @throws IllegalStateException 576 * If the client is already connected to a remote host. 577 * @throws IOException 578 * If an I/O occurs. 579 * @throws FTPIllegalReplyException 580 * If the server replies in an illegal way. 581 * @throws FTPException 582 * If the server refuses the connection. 583 */ 584 public String[] connect(String host) throws IllegalStateException, 585 IOException, FTPIllegalReplyException, FTPException { 586 return connect(host, 21); 587 } 588 589 /** 590 * This method connects the client to the remote FTP host. 591 * 592 * @param host 593 * The host name or address of the remote server. 594 * @param port 595 * The port listened by the remote server. 596 * @return The server welcome message, one line per array element. 597 * @throws IllegalStateException 598 * If the client is already connected to a remote host. 599 * @throws IOException 600 * If an I/O occurs. 601 * @throws FTPIllegalReplyException 602 * If the server replies in an illegal way. 603 * @throws FTPException 604 * If the server refuses the connection. 605 */ 606 public String[] connect(String host, int port) 607 throws IllegalStateException, IOException, 608 FTPIllegalReplyException, FTPException { 609 synchronized (lock) { 610 // Is this client already connected to any host? 611 if (connected) { 612 throw new IllegalStateException("Client already connected to " 613 + host + " on port " + port); 614 } 615 // Ok, it's connection time. Let's try! 616 FTPConnection connection = null; 617 try { 618 // Open the connection. 619 connection = connector.connectForCommunicationChannel(host, 620 port); 621 // Open the communication channel. 622 communication = new FTPCommunicationChannel(connection, 623 pickCharset()); 624 for (Iterator i = communicationListeners.iterator(); i 625 .hasNext();) { 626 communication 627 .addCommunicationListener((FTPCommunicationListener) i 628 .next()); 629 } 630 // Welcome message. 631 FTPReply wm = communication.readFTPReply(); 632 // Does this reply mean "ok"? 633 if (!wm.isSuccessCode()) { 634 // Mmmmm... it seems no! 635 throw new FTPException(wm); 636 } 637 // Flag this object as connected to the remote host. 638 connected = true; 639 authenticated = false; 640 parser = null; 641 this.host = host; 642 this.port = port; 643 this.username = null; 644 this.password = null; 645 this.utf8Supported = false; 646 return wm.getMessages(); 647 } catch (IOException e) { 648 // D'oh! 649 throw e; 650 } finally { 651 // If connection has failed... 652 if (!connected) { 653 if (connection != null) { 654 // Close the connection, 'cause it should be open. 655 try { 656 connection.close(); 657 } catch (Throwable t) { 658 ; 659 } 660 } 661 } 662 } 663 } 664 } 665 666 /** 667 * This method disconnects from the remote server, optionally performing the 668 * QUIT procedure. 669 * 670 * @param sendQuitCommand 671 * If true the QUIT procedure with the server will be performed, 672 * otherwise the connection is abruptly closed by the client 673 * without sending any advice to the server. 674 * @throws IllegalStateException 675 * If the client is not connected to a remote host. 676 * @throws IOException 677 * If an I/O occurs (can be thrown only if sendQuitCommand is 678 * true). 679 * @throws FTPIllegalReplyException 680 * If the server replies in an illegal way (can be thrown only 681 * if sendQuitCommand is true). 682 * @throws FTPException 683 * If the server refuses the QUIT command (can be thrown only if 684 * sendQuitCommand is true). 685 */ 686 public void disconnect(boolean sendQuitCommand) 687 throws IllegalStateException, IOException, 688 FTPIllegalReplyException, FTPException { 689 synchronized (lock) { 690 // Is this client connected? 691 if (!connected) { 692 throw new IllegalStateException("Client not connected"); 693 } 694 if (sendQuitCommand) { 695 // Call the QUIT command. 696 communication.sendFTPCommand("QUIT"); 697 FTPReply r = communication.readFTPReply(); 698 if (!r.isSuccessCode()) { 699 throw new FTPException(r); 700 } 701 } 702 // Close the communication. 703 communication.close(); 704 communication = null; 705 // Reset the connection flag. 706 connected = false; 707 } 708 } 709 710 /** 711 * This method causes the communication channel to be abruptly closed. Use 712 * it carefully, since this one is not thread-safe. It is given as an 713 * "emergency brake" to close the control connection when it is blocked. A 714 * thread-safe solution for the same purpose is a call to disconnect(false). 715 * 716 * @see FTPClient#disconnect(boolean) 717 */ 718 public void abruptlyCloseCommunication() { 719 // Close the communication. 720 communication.close(); 721 communication = null; 722 // Reset the connection flag. 723 connected = false; 724 } 725 726 /** 727 * This method authenticates the user against the server. 728 * 729 * @param username 730 * The username. 731 * @param password 732 * The password (if none set it to null). 733 * @throws IllegalStateException 734 * If the client is not connected. Call the connect() method 735 * before! 736 * @throws IOException 737 * If an I/O error occurs. 738 * @throws FTPIllegalReplyException 739 * If the server replies in an illegal way. 740 * @throws FTPException 741 * If login fails. 742 */ 743 public void login(String username, String password) 744 throws IllegalStateException, IOException, 745 FTPIllegalReplyException, FTPException { 746 login(username, password, null); 747 } 748 749 /** 750 * This method authenticates the user against the server. 751 * 752 * @param username 753 * The username. 754 * @param password 755 * The password (if none set it to null). 756 * @param account 757 * The account (if none set it to null). Be careful: some servers 758 * don't implement this feature. 759 * @throws IllegalStateException 760 * If the client is not connected. Call the connect() method 761 * before! 762 * @throws IOException 763 * If an I/O error occurs. 764 * @throws FTPIllegalReplyException 765 * If the server replies in an illegal way. 766 * @throws FTPException 767 * If login fails. 768 */ 769 public void login(String username, String password, String account) 770 throws IllegalStateException, IOException, 771 FTPIllegalReplyException, FTPException { 772 synchronized (lock) { 773 // Is this client connected? 774 if (!connected) { 775 throw new IllegalStateException("Client not connected"); 776 } 777 // Reset the authentication flag. 778 authenticated = false; 779 // Usefull flags. 780 boolean passwordRequired; 781 boolean accountRequired; 782 // Send the user and read the reply. 783 communication.sendFTPCommand("USER " + username); 784 FTPReply r = communication.readFTPReply(); 785 switch (r.getCode()) { 786 case 230: 787 // Password and account aren't required. 788 passwordRequired = false; 789 accountRequired = false; 790 break; 791 case 331: 792 // Password is required. 793 passwordRequired = true; 794 // Account... maybe! More information later... 795 accountRequired = false; 796 break; 797 case 332: 798 // Password is not required, but account is required. 799 passwordRequired = false; 800 accountRequired = true; 801 default: 802 // User validation failed. 803 throw new FTPException(r); 804 } 805 // Password. 806 if (passwordRequired) { 807 if (password == null) { 808 throw new FTPException(331); 809 } 810 // Send the password. 811 communication.sendFTPCommand("PASS " + password); 812 r = communication.readFTPReply(); 813 switch (r.getCode()) { 814 case 230: 815 // Account is not required. 816 accountRequired = false; 817 break; 818 case 332: 819 // Account is required. 820 accountRequired = true; 821 break; 822 default: 823 // Authentication failed. 824 throw new FTPException(r); 825 } 826 } 827 // Account. 828 if (accountRequired) { 829 if (account == null) { 830 throw new FTPException(332); 831 } 832 // Send the account. 833 communication.sendFTPCommand("ACCT " + account); 834 r = communication.readFTPReply(); 835 switch (r.getCode()) { 836 case 230: 837 // Well done! 838 break; 839 default: 840 // Something goes wrong. 841 throw new FTPException(r); 842 } 843 } 844 // Well, if this point is reached the client could consider itself 845 // as authenticated. 846 this.authenticated = true; 847 this.username = username; 848 this.password = password; 849 } 850 // Post-login operations. 851 postLoginOperations(); 852 } 853 854 /** 855 * Performs some post-login operations, such trying to detect server support 856 * for utf8. 857 * 858 * @throws IllegalStateException 859 * If the client is not connected. Call the connect() method 860 * before! 861 * @throws IOException 862 * If an I/O error occurs. 863 * @throws FTPIllegalReplyException 864 * If the server replies in an illegal way. 865 * @throws FTPException 866 * If login fails. 867 */ 868 private void postLoginOperations() throws IllegalStateException, 869 IOException, FTPIllegalReplyException, FTPException { 870 synchronized (lock) { 871 utf8Supported = false; 872 communication.sendFTPCommand("FEAT"); 873 FTPReply r = communication.readFTPReply(); 874 if (r.getCode() == 211) { 875 String[] lines = r.getMessages(); 876 for (int i = 1; i < lines.length - 1; i++) { 877 String feat = lines[i].trim(); 878 // UTF8 supported? 879 if ("UTF8".equalsIgnoreCase(feat)) { 880 utf8Supported = true; 881 communication.changeCharset("UTF-8"); 882 } 883 } 884 } 885 } 886 } 887 888 /** 889 * This method performs a logout operation for the current user, leaving the 890 * connection open, thus it can be used to start a new user session. Be 891 * careful with this: some FTP servers don't implement this feature, even 892 * though it is a standard FTP one. 893 * 894 * @throws IllegalStateException 895 * If the client is not connected or not authenticated. 896 * @throws IOException 897 * If an I/O error occurs. 898 * @throws FTPIllegalReplyException 899 * If the server replies in an illegal way. 900 * @throws FTPException 901 * If the operation fails. 902 */ 903 public void logout() throws IllegalStateException, IOException, 904 FTPIllegalReplyException, FTPException { 905 synchronized (lock) { 906 // Is this client connected? 907 if (!connected) { 908 throw new IllegalStateException("Client not connected"); 909 } 910 // Is this client authenticated? 911 if (!authenticated) { 912 throw new IllegalStateException("Client not authenticated"); 913 } 914 // Send the REIN command. 915 communication.sendFTPCommand("REIN"); 916 FTPReply r = communication.readFTPReply(); 917 if (!r.isSuccessCode()) { 918 throw new FTPException(r); 919 } else { 920 // Ok. Not authenticated, now. 921 authenticated = false; 922 username = null; 923 password = null; 924 } 925 } 926 } 927 928 /** 929 * This method performs a "noop" operation with the server. 930 * 931 * @throws IllegalStateException 932 * If the client is not connected or not authenticated. 933 * @throws IOException 934 * If an I/O error occurs. 935 * @throws FTPIllegalReplyException 936 * If the server replies in an illegal way. 937 * @throws FTPException 938 * If login fails. 939 */ 940 public void noop() throws IllegalStateException, IOException, 941 FTPIllegalReplyException, FTPException { 942 synchronized (lock) { 943 // Is this client connected? 944 if (!connected) { 945 throw new IllegalStateException("Client not connected"); 946 } 947 // Is this client authenticated? 948 if (!authenticated) { 949 throw new IllegalStateException("Client not authenticated"); 950 } 951 // Send the noop. 952 communication.sendFTPCommand("NOOP"); 953 FTPReply r = communication.readFTPReply(); 954 if (!r.isSuccessCode()) { 955 throw new FTPException(r); 956 } 957 } 958 } 959 960 /** 961 * This method sends a custom command to the server. Don't use this method 962 * to send standard commands already supported by the client: this should 963 * cause unexpected results. 964 * 965 * @param command 966 * The command line. 967 * @return The reply supplied by the server, parsed and served in an object 968 * way mode. 969 * @throws IllegalStateException 970 * If this client is not connected. 971 * @throws IOException 972 * If a I/O error occurs. 973 * @throws FTPIllegalReplyException 974 * If the server replies in an illegal way. 975 */ 976 public FTPReply sendCustomCommand(String command) 977 throws IllegalStateException, IOException, FTPIllegalReplyException { 978 synchronized (lock) { 979 // Is this client connected? 980 if (!connected) { 981 throw new IllegalStateException("Client not connected"); 982 } 983 // Send the command and return the reply. 984 communication.sendFTPCommand(command); 985 return communication.readFTPReply(); 986 } 987 } 988 989 /** 990 * This method sends a SITE specific command to the server. 991 * 992 * @param command 993 * The site command. 994 * @return The reply supplied by the server, parsed and served in an object 995 * way mode. 996 * @throws IllegalStateException 997 * If this client is not connected. 998 * @throws IOException 999 * If a I/O error occurs. 1000 * @throws FTPIllegalReplyException 1001 * If the server replies in an illegal way. 1002 */ 1003 public FTPReply sendSiteCommand(String command) 1004 throws IllegalStateException, IOException, FTPIllegalReplyException { 1005 synchronized (lock) { 1006 // Is this client connected? 1007 if (!connected) { 1008 throw new IllegalStateException("Client not connected"); 1009 } 1010 // Send the command and return the reply. 1011 communication.sendFTPCommand("SITE " + command); 1012 return communication.readFTPReply(); 1013 } 1014 } 1015 1016 /** 1017 * Call this method to switch the user current account. Be careful with 1018 * this: some FTP servers don't implement this feature, even though it is a 1019 * standard FTP one. 1020 * 1021 * @param account 1022 * The account. 1023 * @throws IllegalStateException 1024 * If the client is not connected or not authenticated. 1025 * @throws IOException 1026 * If an I/O error occurs. 1027 * @throws FTPIllegalReplyException 1028 * If the server replies in an illegal way. 1029 * @throws FTPException 1030 * If login fails. 1031 */ 1032 public void changeAccount(String account) throws IllegalStateException, 1033 IOException, FTPIllegalReplyException, FTPException { 1034 synchronized (lock) { 1035 // Is this client connected? 1036 if (!connected) { 1037 throw new IllegalStateException("Client not connected"); 1038 } 1039 // Is this client authenticated? 1040 if (!authenticated) { 1041 throw new IllegalStateException("Client not authenticated"); 1042 } 1043 // Send the ACCT command. 1044 communication.sendFTPCommand("ACCT " + account); 1045 FTPReply r = communication.readFTPReply(); 1046 if (!r.isSuccessCode()) { 1047 throw new FTPException(r); 1048 } 1049 } 1050 } 1051 1052 /** 1053 * This method asks and returns the current working directory. 1054 * 1055 * @return path The path to the current working directory. 1056 * @throws IllegalStateException 1057 * If the client is not connected or not authenticated. 1058 * @throws IOException 1059 * If an I/O error occurs. 1060 * @throws FTPIllegalReplyException 1061 * If the server replies in an illegal way. 1062 * @throws FTPException 1063 * If the operation fails. 1064 */ 1065 public String currentDirectory() throws IllegalStateException, IOException, 1066 FTPIllegalReplyException, FTPException { 1067 synchronized (lock) { 1068 // Is this client connected? 1069 if (!connected) { 1070 throw new IllegalStateException("Client not connected"); 1071 } 1072 // Is this client authenticated? 1073 if (!authenticated) { 1074 throw new IllegalStateException("Client not authenticated"); 1075 } 1076 // Send the PWD command. 1077 communication.sendFTPCommand("PWD"); 1078 FTPReply r = communication.readFTPReply(); 1079 if (!r.isSuccessCode()) { 1080 throw new FTPException(r); 1081 } 1082 // Parse the response. 1083 String[] messages = r.getMessages(); 1084 if (messages.length != 1) { 1085 throw new FTPIllegalReplyException(); 1086 } 1087 Matcher m = PWD_PATTERN.matcher(messages[0]); 1088 if (m.find()) { 1089 return messages[0].substring(m.start() + 1, m.end() - 1); 1090 } else { 1091 throw new FTPIllegalReplyException(); 1092 } 1093 } 1094 } 1095 1096 /** 1097 * This method changes the current working directory. 1098 * 1099 * @param path 1100 * The path to the new working directory. 1101 * @throws IllegalStateException 1102 * If the client is not connected or not authenticated. 1103 * @throws IOException 1104 * If an I/O error occurs. 1105 * @throws FTPIllegalReplyException 1106 * If the server replies in an illegal way. 1107 * @throws FTPException 1108 * If the operation fails. 1109 */ 1110 public void changeDirectory(String path) throws IllegalStateException, 1111 IOException, FTPIllegalReplyException, FTPException { 1112 synchronized (lock) { 1113 // Is this client connected? 1114 if (!connected) { 1115 throw new IllegalStateException("Client not connected"); 1116 } 1117 // Is this client authenticated? 1118 if (!authenticated) { 1119 throw new IllegalStateException("Client not authenticated"); 1120 } 1121 // Send the CWD command. 1122 communication.sendFTPCommand("CWD " + path); 1123 FTPReply r = communication.readFTPReply(); 1124 if (!r.isSuccessCode()) { 1125 throw new FTPException(r); 1126 } 1127 } 1128 } 1129 1130 /** 1131 * This method changes the current working directory to the parent one. 1132 * 1133 * @throws IllegalStateException 1134 * If the client is not connected or not authenticated. 1135 * @throws IOException 1136 * If an I/O error occurs. 1137 * @throws FTPIllegalReplyException 1138 * If the server replies in an illegal way. 1139 * @throws FTPException 1140 * If the operation fails. 1141 */ 1142 public void changeDirectoryUp() throws IllegalStateException, IOException, 1143 FTPIllegalReplyException, FTPException { 1144 synchronized (lock) { 1145 // Is this client connected? 1146 if (!connected) { 1147 throw new IllegalStateException("Client not connected"); 1148 } 1149 // Is this client authenticated? 1150 if (!authenticated) { 1151 throw new IllegalStateException("Client not authenticated"); 1152 } 1153 // Send the CWD command. 1154 communication.sendFTPCommand("CDUP"); 1155 FTPReply r = communication.readFTPReply(); 1156 if (!r.isSuccessCode()) { 1157 throw new FTPException(r); 1158 } 1159 } 1160 } 1161 1162 /** 1163 * This method asks and returns the last modification date of a file or 1164 * directory. 1165 * 1166 * @param path 1167 * The path to the file or the directory. 1168 * @return The file/directory last modification date. 1169 * @throws IllegalStateException 1170 * If the client is not connected or not authenticated. 1171 * @throws IOException 1172 * If an I/O error occurs. 1173 * @throws FTPIllegalReplyException 1174 * If the server replies in an illegal way. 1175 * @throws FTPException 1176 * If the operation fails. 1177 */ 1178 public Date modifiedDate(String path) throws IllegalStateException, 1179 IOException, FTPIllegalReplyException, FTPException { 1180 synchronized (lock) { 1181 // Is this client connected? 1182 if (!connected) { 1183 throw new IllegalStateException("Client not connected"); 1184 } 1185 // Is this client authenticated? 1186 if (!authenticated) { 1187 throw new IllegalStateException("Client not authenticated"); 1188 } 1189 // Send the MDTM command. 1190 communication.sendFTPCommand("MDTM " + path); 1191 FTPReply r = communication.readFTPReply(); 1192 if (!r.isSuccessCode()) { 1193 throw new FTPException(r); 1194 } 1195 String[] messages = r.getMessages(); 1196 if (messages.length != 1) { 1197 throw new FTPIllegalReplyException(); 1198 } else { 1199 try { 1200 return MDTM_DATE_FORMAT.parse(messages[0]); 1201 } catch (ParseException e) { 1202 throw new FTPIllegalReplyException(); 1203 } 1204 } 1205 } 1206 } 1207 1208 /** 1209 * This method asks and returns a file size in bytes. 1210 * 1211 * @param path 1212 * The path to the file. 1213 * @return The file size in bytes. 1214 * @throws IllegalStateException 1215 * If the client is not connected or not authenticated. 1216 * @throws IOException 1217 * If an I/O error occurs. 1218 * @throws FTPIllegalReplyException 1219 * If the server replies in an illegal way. 1220 * @throws FTPException 1221 * If the operation fails. 1222 */ 1223 public long fileSize(String path) throws IllegalStateException, 1224 IOException, FTPIllegalReplyException, FTPException { 1225 synchronized (lock) { 1226 // Is this client connected? 1227 if (!connected) { 1228 throw new IllegalStateException("Client not connected"); 1229 } 1230 // Is this client authenticated? 1231 if (!authenticated) { 1232 throw new IllegalStateException("Client not authenticated"); 1233 } 1234 // Send the SIZE command. 1235 communication.sendFTPCommand("SIZE " + path); 1236 FTPReply r = communication.readFTPReply(); 1237 if (!r.isSuccessCode()) { 1238 throw new FTPException(r); 1239 } 1240 String[] messages = r.getMessages(); 1241 if (messages.length != 1) { 1242 throw new FTPIllegalReplyException(); 1243 } else { 1244 try { 1245 return Long.parseLong(messages[0]); 1246 } catch (Throwable t) { 1247 throw new FTPIllegalReplyException(); 1248 } 1249 } 1250 } 1251 } 1252 1253 /** 1254 * This method renames a remote file or directory. It can also be used to 1255 * move a file or a directory. 1256 * 1257 * In example: 1258 * 1259 * <pre> 1260 * client.rename("oldname", "newname"); // This one renames 1261 * </pre> 1262 * 1263 * <pre> 1264 * client.rename("the/old/path/oldname", "/a/new/path/newname"); // This one moves 1265 * </pre> 1266 * 1267 * @param oldPath 1268 * The current path of the file (or directory). 1269 * @param newPath 1270 * The new path for the file (or directory). 1271 * @throws IllegalStateException 1272 * If the client is not connected or not authenticated. 1273 * @throws IOException 1274 * If an I/O error occurs. 1275 * @throws FTPIllegalReplyException 1276 * If the server replies in an illegal way. 1277 * @throws FTPException 1278 * If the operation fails. 1279 */ 1280 public void rename(String oldPath, String newPath) 1281 throws IllegalStateException, IOException, 1282 FTPIllegalReplyException, FTPException { 1283 synchronized (lock) { 1284 // Is this client connected? 1285 if (!connected) { 1286 throw new IllegalStateException("Client not connected"); 1287 } 1288 // Is this client authenticated? 1289 if (!authenticated) { 1290 throw new IllegalStateException("Client not authenticated"); 1291 } 1292 // Send the RNFR command. 1293 communication.sendFTPCommand("RNFR " + oldPath); 1294 FTPReply r = communication.readFTPReply(); 1295 if (r.getCode() != 350) { 1296 throw new FTPException(r); 1297 } 1298 // Send the RNFR command. 1299 communication.sendFTPCommand("RNTO " + newPath); 1300 r = communication.readFTPReply(); 1301 if (!r.isSuccessCode()) { 1302 throw new FTPException(r); 1303 } 1304 } 1305 } 1306 1307 /** 1308 * This method deletes a remote file. 1309 * 1310 * @param path 1311 * The path to the file. 1312 * @throws IllegalStateException 1313 * If the client is not connected or not authenticated. 1314 * @throws IOException 1315 * If an I/O error occurs. 1316 * @throws FTPIllegalReplyException 1317 * If the server replies in an illegal way. 1318 * @throws FTPException 1319 * If the operation fails. 1320 */ 1321 public void deleteFile(String path) throws IllegalStateException, 1322 IOException, FTPIllegalReplyException, FTPException { 1323 synchronized (lock) { 1324 // Is this client connected? 1325 if (!connected) { 1326 throw new IllegalStateException("Client not connected"); 1327 } 1328 // Is this client authenticated? 1329 if (!authenticated) { 1330 throw new IllegalStateException("Client not authenticated"); 1331 } 1332 // Send the DELE command. 1333 communication.sendFTPCommand("DELE " + path); 1334 FTPReply r = communication.readFTPReply(); 1335 if (!r.isSuccessCode()) { 1336 throw new FTPException(r); 1337 } 1338 } 1339 } 1340 1341 /** 1342 * This method deletes a remote directory. 1343 * 1344 * @param path 1345 * The path to the directory. 1346 * @throws IllegalStateException 1347 * If the client is not connected or not authenticated. 1348 * @throws IOException 1349 * If an I/O error occurs. 1350 * @throws FTPIllegalReplyException 1351 * If the server replies in an illegal way. 1352 * @throws FTPException 1353 * If the operation fails. 1354 */ 1355 public void deleteDirectory(String path) throws IllegalStateException, 1356 IOException, FTPIllegalReplyException, FTPException { 1357 synchronized (lock) { 1358 // Is this client connected? 1359 if (!connected) { 1360 throw new IllegalStateException("Client not connected"); 1361 } 1362 // Is this client authenticated? 1363 if (!authenticated) { 1364 throw new IllegalStateException("Client not authenticated"); 1365 } 1366 // Send the RMD command. 1367 communication.sendFTPCommand("RMD " + path); 1368 FTPReply r = communication.readFTPReply(); 1369 if (!r.isSuccessCode()) { 1370 throw new FTPException(r); 1371 } 1372 } 1373 } 1374 1375 /** 1376 * This method creates a new remote directory in the current working one. 1377 * 1378 * @param directoryName 1379 * The name of the new directory. 1380 * @throws IllegalStateException 1381 * If the client is not connected or not authenticated. 1382 * @throws IOException 1383 * If an I/O error occurs. 1384 * @throws FTPIllegalReplyException 1385 * If the server replies in an illegal way. 1386 * @throws FTPException 1387 * If the operation fails. 1388 */ 1389 public void createDirectory(String directoryName) 1390 throws IllegalStateException, IOException, 1391 FTPIllegalReplyException, FTPException { 1392 synchronized (lock) { 1393 // Is this client connected? 1394 if (!connected) { 1395 throw new IllegalStateException("Client not connected"); 1396 } 1397 // Is this client authenticated? 1398 if (!authenticated) { 1399 throw new IllegalStateException("Client not authenticated"); 1400 } 1401 // Send the MKD command. 1402 communication.sendFTPCommand("MKD " + directoryName); 1403 FTPReply r = communication.readFTPReply(); 1404 if (!r.isSuccessCode()) { 1405 throw new FTPException(r); 1406 } 1407 } 1408 } 1409 1410 /** 1411 * This method calls the HELP command on the remote server, returning a list 1412 * of lines with the help contents. 1413 * 1414 * @return The help contents, splitted by line. 1415 * @throws IllegalStateException 1416 * If the client is not connected or not authenticated. 1417 * @throws IOException 1418 * If an I/O error occurs. 1419 * @throws FTPIllegalReplyException 1420 * If the server replies in an illegal way. 1421 * @throws FTPException 1422 * If the operation fails. 1423 */ 1424 public String[] help() throws IllegalStateException, IOException, 1425 FTPIllegalReplyException, FTPException { 1426 synchronized (lock) { 1427 // Is this client connected? 1428 if (!connected) { 1429 throw new IllegalStateException("Client not connected"); 1430 } 1431 // Is this client authenticated? 1432 if (!authenticated) { 1433 throw new IllegalStateException("Client not authenticated"); 1434 } 1435 // Send the HELP command. 1436 communication.sendFTPCommand("HELP"); 1437 FTPReply r = communication.readFTPReply(); 1438 if (!r.isSuccessCode()) { 1439 throw new FTPException(r); 1440 } 1441 return r.getMessages(); 1442 } 1443 } 1444 1445 /** 1446 * This method returns the remote server status, as the result of a FTP STAT 1447 * command. 1448 * 1449 * @return The remote server status, splitted by line. 1450 * @throws IllegalStateException 1451 * If the client is not connected or not authenticated. 1452 * @throws IOException 1453 * If an I/O error occurs. 1454 * @throws FTPIllegalReplyException 1455 * If the server replies in an illegal way. 1456 * @throws FTPException 1457 * If the operation fails. 1458 */ 1459 public String[] serverStatus() throws IllegalStateException, IOException, 1460 FTPIllegalReplyException, FTPException { 1461 synchronized (lock) { 1462 // Is this client connected? 1463 if (!connected) { 1464 throw new IllegalStateException("Client not connected"); 1465 } 1466 // Is this client authenticated? 1467 if (!authenticated) { 1468 throw new IllegalStateException("Client not authenticated"); 1469 } 1470 // Send the STAT command. 1471 communication.sendFTPCommand("STAT"); 1472 FTPReply r = communication.readFTPReply(); 1473 if (!r.isSuccessCode()) { 1474 throw new FTPException(r); 1475 } 1476 return r.getMessages(); 1477 } 1478 } 1479 1480 /** 1481 * This method lists the entries of the current working directory parsing 1482 * the reply to a FTP LIST command. 1483 * 1484 * The response to the LIST command is parsed through the FTPListParser 1485 * objects registered on the client. The distribution of ftp4j contains some 1486 * standard parsers already registered on every FTPClient object created. If 1487 * they don't work in your case (a FTPListParseException is thrown), you can 1488 * build your own parser implementing the FTPListParser interface and add it 1489 * to the client by calling its addListParser() method. 1490 * 1491 * Calling this method blocks the current thread until the operation is 1492 * completed. The operation could be interrupted by another thread calling 1493 * abortCurrentDataTransfer(). The list() method will break with a 1494 * FTPAbortedException. 1495 * 1496 * @param fileSpec 1497 * A file filter string. Depending on the server implementation, 1498 * wildcard characters could be accepted. 1499 * @return The list of the files (and directories) in the current working 1500 * directory. 1501 * @throws IllegalStateException 1502 * If the client is not connected or not authenticated. 1503 * @throws IOException 1504 * If an I/O error occurs. 1505 * @throws FTPIllegalReplyException 1506 * If the server replies in an illegal way. 1507 * @throws FTPException 1508 * If the operation fails. 1509 * @throws FTPDataTransferException 1510 * If a I/O occurs in the data transfer connection. If you 1511 * receive this exception the transfer failed, but the main 1512 * connection with the remote FTP server is in theory still 1513 * working. 1514 * @throws FTPAbortedException 1515 * If operation is aborted by another thread. 1516 * @throws FTPListParseException 1517 * If none of the registered parsers can handle the response 1518 * sent by the server. 1519 * @see FTPListParser 1520 * @see FTPClient#addListParser(FTPListParser) 1521 * @see FTPClient#getListParsers() 1522 * @see FTPClient#abortCurrentDataTransfer(boolean) 1523 * @see FTPClient#listNames() 1524 * @since 1.2 1525 */ 1526 public FTPFile[] list(String fileSpec) throws IllegalStateException, 1527 IOException, FTPIllegalReplyException, FTPException, 1528 FTPDataTransferException, FTPAbortedException, 1529 FTPListParseException { 1530 synchronized (lock) { 1531 // Is this client connected? 1532 if (!connected) { 1533 throw new IllegalStateException("Client not connected"); 1534 } 1535 // Is this client authenticated? 1536 if (!authenticated) { 1537 throw new IllegalStateException("Client not authenticated"); 1538 } 1539 // The connection for the data transfer. 1540 FTPConnection dtConnection; 1541 // Active or passive? 1542 if (passive) { 1543 dtConnection = openPassiveDataTransferChannel(); 1544 } else { 1545 dtConnection = openActiveDataTransferChannel(); 1546 } 1547 // ASCII, please! 1548 communication.sendFTPCommand("TYPE A"); 1549 FTPReply r = communication.readFTPReply(); 1550 if (!r.isSuccessCode()) { 1551 try { 1552 dtConnection.close(); 1553 } catch (Throwable t) { 1554 ; 1555 } 1556 throw new FTPException(r); 1557 } 1558 // Send the list command. 1559 String command = "LIST"; 1560 if (fileSpec != null && fileSpec.length() > 0) { 1561 command += " " + fileSpec; 1562 } 1563 communication.sendFTPCommand(command); 1564 r = communication.readFTPReply(); 1565 if (r.getCode() != 150 && r.getCode() != 125) { 1566 try { 1567 dtConnection.close(); 1568 } catch (Throwable t) { 1569 ; 1570 } 1571 throw new FTPException(r); 1572 } 1573 // Change the operation status. 1574 synchronized (abortLock) { 1575 ongoingDataTransfer = true; 1576 aborted = false; 1577 } 1578 dataTransferInputStream = dtConnection.getInputStream(); 1579 // Fetch the list from the data transfer connection. 1580 ArrayList lines = new ArrayList(); 1581 NVTASCIIReader dataReader = null; 1582 try { 1583 dataReader = new NVTASCIIReader(dataTransferInputStream, 1584 pickCharset()); 1585 String line; 1586 while ((line = dataReader.readLine()) != null) { 1587 if (line.length() > 0) { 1588 lines.add(line); 1589 } 1590 } 1591 } catch (IOException e) { 1592 synchronized (abortLock) { 1593 if (aborted) { 1594 throw new FTPAbortedException(); 1595 } else { 1596 throw new FTPDataTransferException( 1597 "I/O error in data transfer", e); 1598 } 1599 } 1600 } finally { 1601 if (dataReader != null) { 1602 try { 1603 dataReader.close(); 1604 } catch (Throwable t) { 1605 ; 1606 } 1607 } 1608 try { 1609 dtConnection.close(); 1610 } catch (Throwable t) { 1611 ; 1612 } 1613 // Consume the result reply of the transfer. 1614 communication.readFTPReply(); 1615 // Set to null the instance-level input stream. 1616 dataTransferInputStream = null; 1617 // Change the operation status. 1618 synchronized (abortLock) { 1619 ongoingDataTransfer = false; 1620 aborted = false; 1621 } 1622 } 1623 // Build an array of lines. 1624 int size = lines.size(); 1625 String[] list = new String[size]; 1626 for (int i = 0; i < size; i++) { 1627 list[i] = (String) lines.get(i); 1628 } 1629 // Parse the list. 1630 FTPFile[] ret = null; 1631 if (parser == null) { 1632 // Try to parse the list with every parser available. 1633 for (Iterator i = listParsers.iterator(); i.hasNext();) { 1634 FTPListParser aux = (FTPListParser) i.next(); 1635 try { 1636 // Let's try! 1637 ret = aux.parse(list); 1638 // This parser smells good! 1639 parser = aux; 1640 // Leave the loop. 1641 break; 1642 } catch (FTPListParseException e) { 1643 // Let's try the next one. 1644 continue; 1645 } 1646 } 1647 } else { 1648 ret = parser.parse(list); 1649 } 1650 if (ret == null) { 1651 // None of the parsers can handle the list response. 1652 throw new FTPListParseException(); 1653 } else { 1654 // Return the parsed list. 1655 return ret; 1656 } 1657 } 1658 } 1659 1660 /** 1661 * This method lists the entries of the current working directory parsing 1662 * the reply to a FTP LIST command. 1663 * 1664 * The response to the LIST command is parsed through the FTPListParser 1665 * objects registered on the client. The distribution of ftp4j contains some 1666 * standard parsers already registered on every FTPClient object created. If 1667 * they don't work in your case (a FTPListParseException is thrown), you can 1668 * build your own parser implementing the FTPListParser interface and add it 1669 * to the client by calling its addListParser() method. 1670 * 1671 * Calling this method blocks the current thread until the operation is 1672 * completed. The operation could be interrupted by another thread calling 1673 * abortCurrentDataTransfer(). The list() method will break with a 1674 * FTPAbortedException. 1675 * 1676 * @return The list of the files (and directories) in the current working 1677 * directory. 1678 * @throws IllegalStateException 1679 * If the client is not connected or not authenticated. 1680 * @throws IOException 1681 * If an I/O error occurs. 1682 * @throws FTPIllegalReplyException 1683 * If the server replies in an illegal way. 1684 * @throws FTPException 1685 * If the operation fails. 1686 * @throws FTPDataTransferException 1687 * If a I/O occurs in the data transfer connection. If you 1688 * receive this exception the transfer failed, but the main 1689 * connection with the remote FTP server is in theory still 1690 * working. 1691 * @throws FTPAbortedException 1692 * If operation is aborted by another thread. 1693 * @throws FTPListParseException 1694 * If none of the registered parsers can handle the response 1695 * sent by the server. 1696 * @see FTPListParser 1697 * @see FTPClient#addListParser(FTPListParser) 1698 * @see FTPClient#getListParsers() 1699 * @see FTPClient#abortCurrentDataTransfer(boolean) 1700 * @see FTPClient#listNames() 1701 */ 1702 public FTPFile[] list() throws IllegalStateException, IOException, 1703 FTPIllegalReplyException, FTPException, FTPDataTransferException, 1704 FTPAbortedException, FTPListParseException { 1705 return list(null); 1706 } 1707 1708 /** 1709 * This method lists the entries of the current working directory with a FTP 1710 * NLST command. 1711 * 1712 * The response consists in an array of string, each one reporting the name 1713 * of a file or a directory placed in the current working directory. For a 1714 * more detailed directory listing procedure look at the list() method. 1715 * 1716 * Calling this method blocks the current thread until the operation is 1717 * completed. The operation could be interrupted by another thread calling 1718 * abortCurrentDataTransfer(). The listNames() method will break with a 1719 * FTPAbortedException. 1720 * 1721 * @return The list of the files (and directories) in the current working 1722 * directory. 1723 * @throws IllegalStateException 1724 * If the client is not connected or not authenticated. 1725 * @throws IOException 1726 * If an I/O error occurs. 1727 * @throws FTPIllegalReplyException 1728 * If the server replies in an illegal way. 1729 * @throws FTPException 1730 * If the operation fails. 1731 * @throws FTPDataTransferException 1732 * If a I/O occurs in the data transfer connection. If you 1733 * receive this exception the transfer failed, but the main 1734 * connection with the remote FTP server is in theory still 1735 * working. 1736 * @throws FTPAbortedException 1737 * If operation is aborted by another thread. 1738 * @throws FTPListParseException 1739 * If none of the registered parsers can handle the response 1740 * sent by the server. 1741 * @see FTPClient#abortCurrentDataTransfer(boolean) 1742 * @see FTPClient#list() 1743 */ 1744 public String[] listNames() throws IllegalStateException, IOException, 1745 FTPIllegalReplyException, FTPException, FTPDataTransferException, 1746 FTPAbortedException, FTPListParseException { 1747 synchronized (lock) { 1748 // Is this client connected? 1749 if (!connected) { 1750 throw new IllegalStateException("Client not connected"); 1751 } 1752 // Is this client authenticated? 1753 if (!authenticated) { 1754 throw new IllegalStateException("Client not authenticated"); 1755 } 1756 // The connection for the data transfer. 1757 FTPConnection dtConnection; 1758 // Active or passive? 1759 if (passive) { 1760 dtConnection = openPassiveDataTransferChannel(); 1761 } else { 1762 dtConnection = openActiveDataTransferChannel(); 1763 } 1764 // ASCII, please! 1765 communication.sendFTPCommand("TYPE A"); 1766 FTPReply r = communication.readFTPReply(); 1767 if (!r.isSuccessCode()) { 1768 try { 1769 dtConnection.close(); 1770 } catch (Throwable t) { 1771 ; 1772 } 1773 throw new FTPException(r); 1774 } 1775 // Send the NLST command. 1776 communication.sendFTPCommand("NLST"); 1777 r = communication.readFTPReply(); 1778 if (r.getCode() != 150 && r.getCode() != 125) { 1779 try { 1780 dtConnection.close(); 1781 } catch (Throwable t) { 1782 ; 1783 } 1784 throw new FTPException(r); 1785 } 1786 // Change the operation status. 1787 synchronized (abortLock) { 1788 ongoingDataTransfer = true; 1789 aborted = false; 1790 } 1791 dataTransferInputStream = dtConnection.getInputStream(); 1792 // Fetch the list from the data transfer connection. 1793 ArrayList lines = new ArrayList(); 1794 NVTASCIIReader dataReader = null; 1795 try { 1796 dataReader = new NVTASCIIReader(dataTransferInputStream, 1797 pickCharset()); 1798 String line; 1799 while ((line = dataReader.readLine()) != null) { 1800 if (line.length() > 0) { 1801 lines.add(line); 1802 } 1803 } 1804 } catch (IOException e) { 1805 synchronized (abortLock) { 1806 if (aborted) { 1807 throw new FTPAbortedException(); 1808 } else { 1809 throw new FTPDataTransferException( 1810 "I/O error in data transfer", e); 1811 } 1812 } 1813 } finally { 1814 if (dataReader != null) { 1815 try { 1816 dataReader.close(); 1817 } catch (Throwable t) { 1818 ; 1819 } 1820 } 1821 try { 1822 dtConnection.close(); 1823 } catch (Throwable t) { 1824 ; 1825 } 1826 // Consume the result reply of the transfer. 1827 communication.readFTPReply(); 1828 // Set to null the instance-level input stream. 1829 dataTransferInputStream = null; 1830 // Change the operation status. 1831 synchronized (abortLock) { 1832 ongoingDataTransfer = false; 1833 aborted = false; 1834 } 1835 } 1836 // Build an array. 1837 int size = lines.size(); 1838 String[] list = new String[size]; 1839 for (int i = 0; i < size; i++) { 1840 list[i] = (String) lines.get(i); 1841 } 1842 return list; 1843 } 1844 } 1845 1846 /** 1847 * This method uploads a file to the remote server. 1848 * 1849 * Calling this method blocks the current thread until the operation is 1850 * completed. The operation could be interrupted by another thread calling 1851 * abortCurrentDataTransfer(). The method will break with a 1852 * FTPAbortedException. 1853 * 1854 * @param file 1855 * The file to upload. 1856 * @throws IllegalStateException 1857 * If the client is not connected or not authenticated. 1858 * @throws FileNotFoundException 1859 * If the supplied file cannot be found. 1860 * @throws IOException 1861 * If an I/O error occurs. 1862 * @throws FTPIllegalReplyException 1863 * If the server replies in an illegal way. 1864 * @throws FTPException 1865 * If the operation fails. 1866 * @throws FTPDataTransferException 1867 * If a I/O occurs in the data transfer connection. If you 1868 * receive this exception the transfer failed, but the main 1869 * connection with the remote FTP server is in theory still 1870 * working. 1871 * @throws FTPAbortedException 1872 * If operation is aborted by another thread. 1873 * @see FTPClient#abortCurrentDataTransfer(boolean) 1874 */ 1875 public void upload(File file) throws IllegalStateException, 1876 FileNotFoundException, IOException, FTPIllegalReplyException, 1877 FTPException, FTPDataTransferException, FTPAbortedException { 1878 upload(file, 0, null); 1879 } 1880 1881 /** 1882 * This method uploads a file to the remote server. 1883 * 1884 * Calling this method blocks the current thread until the operation is 1885 * completed. The operation could be interrupted by another thread calling 1886 * abortCurrentDataTransfer(). The method will break with a 1887 * FTPAbortedException. 1888 * 1889 * @param file 1890 * The file to upload. 1891 * @param listener 1892 * The listener for the operation. Could be null. 1893 * @throws IllegalStateException 1894 * If the client is not connected or not authenticated. 1895 * @throws FileNotFoundException 1896 * If the supplied file cannot be found. 1897 * @throws IOException 1898 * If an I/O error occurs. 1899 * @throws FTPIllegalReplyException 1900 * If the server replies in an illegal way. 1901 * @throws FTPException 1902 * If the operation fails. 1903 * @throws FTPDataTransferException 1904 * If a I/O occurs in the data transfer connection. If you 1905 * receive this exception the transfer failed, but the main 1906 * connection with the remote FTP server is in theory still 1907 * working. 1908 * @throws FTPAbortedException 1909 * If operation is aborted by another thread. 1910 * @see FTPClient#abortCurrentDataTransfer(boolean) 1911 */ 1912 public void upload(File file, FTPDataTransferListener listener) 1913 throws IllegalStateException, FileNotFoundException, IOException, 1914 FTPIllegalReplyException, FTPException, FTPDataTransferException, 1915 FTPAbortedException { 1916 upload(file, 0, listener); 1917 } 1918 1919 /** 1920 * This method resumes an upload of a file to the remote server. 1921 * 1922 * Calling this method blocks the current thread until the operation is 1923 * completed. The operation could be interrupted by another thread calling 1924 * abortCurrentDataTransfer(). The method will break with a 1925 * FTPAbortedException. 1926 * 1927 * @param file 1928 * The file to upload. 1929 * @param restartAt 1930 * The restart point (number of bytes already uploaded). 1931 * @throws IllegalStateException 1932 * If the client is not connected or not authenticated. 1933 * @throws FileNotFoundException 1934 * If the supplied file cannot be found. 1935 * @throws IOException 1936 * If an I/O error occurs. 1937 * @throws FTPIllegalReplyException 1938 * If the server replies in an illegal way. 1939 * @throws FTPException 1940 * If the operation fails. 1941 * @throws FTPDataTransferException 1942 * If a I/O occurs in the data transfer connection. If you 1943 * receive this exception the transfer failed, but the main 1944 * connection with the remote FTP server is in theory still 1945 * working. 1946 * @throws FTPAbortedException 1947 * If operation is aborted by another thread. 1948 * @see FTPClient#abortCurrentDataTransfer(boolean) 1949 */ 1950 public void upload(File file, long restartAt) throws IllegalStateException, 1951 FileNotFoundException, IOException, FTPIllegalReplyException, 1952 FTPException, FTPDataTransferException, FTPAbortedException { 1953 upload(file, restartAt, null); 1954 } 1955 1956 /** 1957 * This method resumes an upload of a file to the remote server. 1958 * 1959 * Calling this method blocks the current thread until the operation is 1960 * completed. The operation could be interrupted by another thread calling 1961 * abortCurrentDataTransfer(). The method will break with a 1962 * FTPAbortedException. 1963 * 1964 * @param file 1965 * The file to upload. 1966 * @param restartAt 1967 * The restart point (number of bytes already uploaded). 1968 * @param listener 1969 * The listener for the operation. Could be null. 1970 * @throws IllegalStateException 1971 * If the client is not connected or not authenticated. 1972 * @throws FileNotFoundException 1973 * If the supplied file cannot be found. 1974 * @throws IOException 1975 * If an I/O error occurs. 1976 * @throws FTPIllegalReplyException 1977 * If the server replies in an illegal way. 1978 * @throws FTPException 1979 * If the operation fails. 1980 * @throws FTPDataTransferException 1981 * If a I/O occurs in the data transfer connection. If you 1982 * receive this exception the transfer failed, but the main 1983 * connection with the remote FTP server is in theory still 1984 * working. 1985 * @throws FTPAbortedException 1986 * If operation is aborted by another thread. 1987 * @see FTPClient#abortCurrentDataTransfer(boolean) 1988 */ 1989 public void upload(File file, long restartAt, 1990 FTPDataTransferListener listener) throws IllegalStateException, 1991 FileNotFoundException, IOException, FTPIllegalReplyException, 1992 FTPException, FTPDataTransferException, FTPAbortedException { 1993 if (!file.exists()) { 1994 throw new FileNotFoundException(file.getAbsolutePath()); 1995 } 1996 InputStream inputStream = null; 1997 try { 1998 inputStream = new FileInputStream(file); 1999 } catch (IOException e) { 2000 throw new FTPDataTransferException(e); 2001 } 2002 try { 2003 upload(file.getName(), inputStream, restartAt, restartAt, file 2004 .length(), listener); 2005 } catch (IllegalStateException e) { 2006 throw e; 2007 } catch (IOException e) { 2008 throw e; 2009 } catch (FTPIllegalReplyException e) { 2010 throw e; 2011 } catch (FTPException e) { 2012 throw e; 2013 } catch (FTPDataTransferException e) { 2014 throw e; 2015 } catch (FTPAbortedException e) { 2016 throw e; 2017 } finally { 2018 if (inputStream != null) { 2019 try { 2020 inputStream.close(); 2021 } catch (Throwable t) { 2022 ; 2023 } 2024 } 2025 } 2026 } 2027 2028 /** 2029 * This method resumes an upload to the remote server. 2030 * 2031 * Note that no byte is skipped in the given inputStream. The restartAt 2032 * value is, in this case, an information for the remote server. 2033 * 2034 * Calling this method blocks the current thread until the operation is 2035 * completed. The operation could be interrupted by another thread calling 2036 * abortCurrentDataTransfer(). The method will break with a 2037 * FTPAbortedException. 2038 * 2039 * @param fileName 2040 * The name of the remote file. 2041 * @param inputStream 2042 * The source of data. 2043 * @param restartAt 2044 * The restart point (number of bytes already uploaded). 2045 * @param streamOffset 2046 * The offset to skip in the stream. 2047 * @param streamLength 2048 * The length of the data of the stream to upload. 2049 * @param listener 2050 * The listener for the operation. Could be null. 2051 * @throws IllegalStateException 2052 * If the client is not connected or not authenticated. 2053 * @throws IOException 2054 * If an I/O error occurs. 2055 * @throws FTPIllegalReplyException 2056 * If the server replies in an illegal way. 2057 * @throws FTPException 2058 * If the operation fails. 2059 * @throws FTPDataTransferException 2060 * If a I/O occurs in the data transfer connection. If you 2061 * receive this exception the transfer failed, but the main 2062 * connection with the remote FTP server is in theory still 2063 * working. 2064 * @throws FTPAbortedException 2065 * If operation is aborted by another thread. 2066 * @see FTPClient#abortCurrentDataTransfer(boolean) 2067 */ 2068 public void upload(String fileName, InputStream inputStream, 2069 long restartAt, long streamOffset, long streamLength, 2070 FTPDataTransferListener listener) throws IllegalStateException, 2071 IOException, FTPIllegalReplyException, FTPException, 2072 FTPDataTransferException, FTPAbortedException { 2073 synchronized (lock) { 2074 // Is this client connected? 2075 if (!connected) { 2076 throw new IllegalStateException("Client not connected"); 2077 } 2078 // Is this client authenticated? 2079 if (!authenticated) { 2080 throw new IllegalStateException("Client not authenticated"); 2081 } 2082 // The connection for the data transfer. 2083 FTPConnection dtConnection; 2084 // Active or passive? 2085 if (passive) { 2086 dtConnection = openPassiveDataTransferChannel(); 2087 } else { 2088 dtConnection = openActiveDataTransferChannel(); 2089 } 2090 // Select the type of contents. 2091 int tp = type; 2092 if (tp == TYPE_AUTO) { 2093 tp = detectType(fileName); 2094 } 2095 if (tp == TYPE_TEXTUAL) { 2096 communication.sendFTPCommand("TYPE A"); 2097 } else if (tp == TYPE_BINARY) { 2098 communication.sendFTPCommand("TYPE I"); 2099 } 2100 FTPReply r = communication.readFTPReply(); 2101 if (!r.isSuccessCode()) { 2102 throw new FTPException(r); 2103 } 2104 // Send the REST command. 2105 communication.sendFTPCommand("REST " + restartAt); 2106 r = communication.readFTPReply(); 2107 if (r.getCode() != 350) { 2108 try { 2109 dtConnection.close(); 2110 } catch (Throwable t) { 2111 ; 2112 } 2113 throw new FTPException(r); 2114 } 2115 // Send the STOR command. 2116 communication.sendFTPCommand("STOR " + fileName); 2117 r = communication.readFTPReply(); 2118 if (r.getCode() != 150 && r.getCode() != 125) { 2119 try { 2120 dtConnection.close(); 2121 } catch (Throwable t) { 2122 ; 2123 } 2124 throw new FTPException(r); 2125 } 2126 // Change the operation status. 2127 synchronized (abortLock) { 2128 ongoingDataTransfer = true; 2129 aborted = false; 2130 } 2131 dataTransferOutputStream = dtConnection.getOutputStream(); 2132 // Prepare and start a NOOP thread. 2133 Runnable nooper = new Runnable() { 2134 public void run() { 2135 while (!Thread.interrupted()) { 2136 // Sleep. 2137 try { 2138 Thread.sleep(10000); 2139 } catch (InterruptedException e) { 2140 break; 2141 } 2142 // Send NOOP. 2143 try { 2144 communication.sendFTPCommand("NOOP"); 2145 communication.readFTPReply(); 2146 } catch (Throwable t) { 2147 ; 2148 } 2149 } 2150 } 2151 }; 2152 Thread nooperThread = new Thread(nooper); 2153 nooperThread.start(); 2154 // Upload the stream. 2155 long done = 0; 2156 try { 2157 inputStream.skip(streamOffset); 2158 if (listener != null) { 2159 listener.started(); 2160 } 2161 if (tp == TYPE_TEXTUAL) { 2162 Reader reader = new InputStreamReader(inputStream); 2163 Writer writer = new OutputStreamWriter( 2164 dataTransferOutputStream, "UTF-8"); 2165 char[] buffer = new char[1024]; 2166 while (done < streamLength) { 2167 int l = reader.read(buffer, 0, (int) Math.min( 2168 buffer.length, streamLength - done)); 2169 if (l == -1) { 2170 throw new IOException("End of stream reached"); 2171 } else { 2172 writer.write(buffer, 0, l); 2173 done += l; 2174 if (listener != null) { 2175 listener.transferred(l); 2176 } 2177 } 2178 } 2179 } else if (tp == TYPE_BINARY) { 2180 byte[] buffer = new byte[1024]; 2181 while (done < streamLength) { 2182 int l = inputStream.read(buffer, 0, (int) Math.min( 2183 buffer.length, streamLength - done)); 2184 if (l == -1) { 2185 throw new IOException("End of stream reached"); 2186 } else { 2187 dataTransferOutputStream.write(buffer, 0, l); 2188 done += l; 2189 if (listener != null) { 2190 listener.transferred(l); 2191 } 2192 } 2193 } 2194 } 2195 } catch (IOException e) { 2196 synchronized (abortLock) { 2197 if (aborted) { 2198 if (listener != null) { 2199 listener.aborted(); 2200 } 2201 throw new FTPAbortedException(); 2202 } else { 2203 if (listener != null) { 2204 listener.failed(); 2205 } 2206 throw new FTPDataTransferException( 2207 "I/O error in data transfer", e); 2208 } 2209 } 2210 } finally { 2211 // Stop nooping. 2212 nooperThread.interrupt(); 2213 for (;;) { 2214 try { 2215 nooperThread.join(); 2216 break; 2217 } catch (InterruptedException e) { 2218 continue; 2219 } 2220 } 2221 // Closing stream and data connection. 2222 if (dataTransferOutputStream != null) { 2223 try { 2224 dataTransferOutputStream.close(); 2225 } catch (Throwable t) { 2226 ; 2227 } 2228 } 2229 try { 2230 dtConnection.close(); 2231 } catch (Throwable t) { 2232 ; 2233 } 2234 // Set to null the instance-level input stream. 2235 dataTransferOutputStream = null; 2236 // Consume the result reply of the transfer. 2237 communication.readFTPReply(); 2238 // Change the operation status. 2239 synchronized (abortLock) { 2240 ongoingDataTransfer = false; 2241 aborted = false; 2242 } 2243 } 2244 if (listener != null) { 2245 listener.completed(); 2246 } 2247 } 2248 } 2249 2250 /** 2251 * This method downloads a remote file from the server to a local file. 2252 * 2253 * Calling this method blocks the current thread until the operation is 2254 * completed. The operation could be interrupted by another thread calling 2255 * abortCurrentDataTransfer(). The method will break with a 2256 * FTPAbortedException. 2257 * 2258 * @param remoteFileName 2259 * The name of the file to download. 2260 * @param localFile 2261 * The local file. 2262 * @throws IllegalStateException 2263 * If the client is not connected or not authenticated. 2264 * @throws FileNotFoundException 2265 * If the supplied file cannot be found. 2266 * @throws IOException 2267 * If an I/O error occurs. 2268 * @throws FTPIllegalReplyException 2269 * If the server replies in an illegal way. 2270 * @throws FTPException 2271 * If the operation fails. 2272 * @throws FTPDataTransferException 2273 * If a I/O occurs in the data transfer connection. If you 2274 * receive this exception the transfer failed, but the main 2275 * connection with the remote FTP server is in theory still 2276 * working. 2277 * @throws FTPAbortedException 2278 * If operation is aborted by another thread. 2279 * @see FTPClient#abortCurrentDataTransfer(boolean) 2280 */ 2281 public void download(String remoteFileName, File localFile) 2282 throws IllegalStateException, FileNotFoundException, IOException, 2283 FTPIllegalReplyException, FTPException, FTPDataTransferException, 2284 FTPAbortedException { 2285 download(remoteFileName, localFile, 0, null); 2286 } 2287 2288 /** 2289 * This method downloads a remote file from the server to a local file. 2290 * 2291 * Calling this method blocks the current thread until the operation is 2292 * completed. The operation could be interrupted by another thread calling 2293 * abortCurrentDataTransfer(). The method will break with a 2294 * FTPAbortedException. 2295 * 2296 * @param remoteFileName 2297 * The name of the file to download. 2298 * @param localFile 2299 * The local file. 2300 * @param listener 2301 * The listener for the operation. Could be null. 2302 * @throws IllegalStateException 2303 * If the client is not connected or not authenticated. 2304 * @throws FileNotFoundException 2305 * If the supplied file cannot be found. 2306 * @throws IOException 2307 * If an I/O error occurs. 2308 * @throws FTPIllegalReplyException 2309 * If the server replies in an illegal way. 2310 * @throws FTPException 2311 * If the operation fails. 2312 * @throws FTPDataTransferException 2313 * If a I/O occurs in the data transfer connection. If you 2314 * receive this exception the transfer failed, but the main 2315 * connection with the remote FTP server is in theory still 2316 * working. 2317 * @throws FTPAbortedException 2318 * If operation is aborted by another thread. 2319 * @see FTPClient#abortCurrentDataTransfer(boolean) 2320 */ 2321 public void download(String remoteFileName, File localFile, 2322 FTPDataTransferListener listener) throws IllegalStateException, 2323 FileNotFoundException, IOException, FTPIllegalReplyException, 2324 FTPException, FTPDataTransferException, FTPAbortedException { 2325 download(remoteFileName, localFile, 0, listener); 2326 } 2327 2328 /** 2329 * This method resumes a download operation from the remote server to a 2330 * local file. 2331 * 2332 * Calling this method blocks the current thread until the operation is 2333 * completed. The operation could be interrupted by another thread calling 2334 * abortCurrentDataTransfer(). The method will break with a 2335 * FTPAbortedException. 2336 * 2337 * @param remoteFileName 2338 * The name of the file to download. 2339 * @param localFile 2340 * The local file. 2341 * @param restartAt 2342 * The restart point (number of bytes already downloaded). 2343 * @throws IllegalStateException 2344 * If the client is not connected or not authenticated. 2345 * @throws FileNotFoundException 2346 * If the supplied file cannot be found. 2347 * @throws IOException 2348 * If an I/O error occurs. 2349 * @throws FTPIllegalReplyException 2350 * If the server replies in an illegal way. 2351 * @throws FTPException 2352 * If the operation fails. 2353 * @throws FTPDataTransferException 2354 * If a I/O occurs in the data transfer connection. If you 2355 * receive this exception the transfer failed, but the main 2356 * connection with the remote FTP server is in theory still 2357 * working. 2358 * @throws FTPAbortedException 2359 * If operation is aborted by another thread. 2360 * @see FTPClient#abortCurrentDataTransfer(boolean) 2361 */ 2362 public void download(String remoteFileName, File localFile, long restartAt) 2363 throws IllegalStateException, FileNotFoundException, IOException, 2364 FTPIllegalReplyException, FTPException, FTPDataTransferException, 2365 FTPAbortedException { 2366 download(remoteFileName, localFile, restartAt, null); 2367 } 2368 2369 /** 2370 * This method resumes a download operation from the remote server to a 2371 * local file. 2372 * 2373 * Calling this method blocks the current thread until the operation is 2374 * completed. The operation could be interrupted by another thread calling 2375 * abortCurrentDataTransfer(). The method will break with a 2376 * FTPAbortedException. 2377 * 2378 * @param remoteFileName 2379 * The name of the file to download. 2380 * @param localFile 2381 * The local file. 2382 * @param restartAt 2383 * The restart point (number of bytes already downloaded). 2384 * @param listener 2385 * The listener for the operation. Could be null. 2386 * @throws IllegalStateException 2387 * If the client is not connected or not authenticated. 2388 * @throws FileNotFoundException 2389 * If the supplied file cannot be found. 2390 * @throws IOException 2391 * If an I/O error occurs. 2392 * @throws FTPIllegalReplyException 2393 * If the server replies in an illegal way. 2394 * @throws FTPException 2395 * If the operation fails. 2396 * @throws FTPDataTransferException 2397 * If a I/O occurs in the data transfer connection. If you 2398 * receive this exception the transfer failed, but the main 2399 * connection with the remote FTP server is in theory still 2400 * working. 2401 * @throws FTPAbortedException 2402 * If operation is aborted by another thread. 2403 * @see FTPClient#abortCurrentDataTransfer(boolean) 2404 */ 2405 public void download(String remoteFileName, File localFile, long restartAt, 2406 FTPDataTransferListener listener) throws IllegalStateException, 2407 FileNotFoundException, IOException, FTPIllegalReplyException, 2408 FTPException, FTPDataTransferException, FTPAbortedException { 2409 OutputStream outputStream = null; 2410 try { 2411 outputStream = new FileOutputStream(localFile, true); 2412 } catch (IOException e) { 2413 throw new FTPDataTransferException(e); 2414 } 2415 try { 2416 download(remoteFileName, outputStream, restartAt, listener); 2417 } catch (IllegalStateException e) { 2418 throw e; 2419 } catch (IOException e) { 2420 throw e; 2421 } catch (FTPIllegalReplyException e) { 2422 throw e; 2423 } catch (FTPException e) { 2424 throw e; 2425 } catch (FTPDataTransferException e) { 2426 throw e; 2427 } catch (FTPAbortedException e) { 2428 throw e; 2429 } finally { 2430 if (outputStream != null) { 2431 try { 2432 outputStream.close(); 2433 } catch (Throwable t) { 2434 ; 2435 } 2436 } 2437 } 2438 } 2439 2440 /** 2441 * This method resumes a download operation from the remote server. 2442 * 2443 * Calling this method blocks the current thread until the operation is 2444 * completed. The operation could be interrupted by another thread calling 2445 * abortCurrentDataTransfer(). The method will break with a 2446 * FTPAbortedException. 2447 * 2448 * @param fileName 2449 * The name of the remote file. 2450 * @param outputStream 2451 * The destination stream of data read during the download. 2452 * @param restartAt 2453 * The restart point (number of bytes already downloaded). 2454 * @param listener 2455 * The listener for the operation. Could be null. 2456 * @throws IllegalStateException 2457 * If the client is not connected or not authenticated. 2458 * @throws IOException 2459 * If an I/O error occurs. 2460 * @throws FTPIllegalReplyException 2461 * If the server replies in an illegal way. 2462 * @throws FTPException 2463 * If the operation fails. 2464 * @throws FTPDataTransferException 2465 * If a I/O occurs in the data transfer connection. If you 2466 * receive this exception the transfer failed, but the main 2467 * connection with the remote FTP server is in theory still 2468 * working. 2469 * @throws FTPAbortedException 2470 * If operation is aborted by another thread. 2471 * @see FTPClient#abortCurrentDataTransfer(boolean) 2472 */ 2473 public void download(String fileName, OutputStream outputStream, 2474 long restartAt, FTPDataTransferListener listener) 2475 throws IllegalStateException, IOException, 2476 FTPIllegalReplyException, FTPException, FTPDataTransferException, 2477 FTPAbortedException { 2478 synchronized (lock) { 2479 // Is this client connected? 2480 if (!connected) { 2481 throw new IllegalStateException("Client not connected"); 2482 } 2483 // Is this client authenticated? 2484 if (!authenticated) { 2485 throw new IllegalStateException("Client not authenticated"); 2486 } 2487 // The connection for the data transfer. 2488 FTPConnection dtConnection; 2489 // Active or passive? 2490 if (passive) { 2491 dtConnection = openPassiveDataTransferChannel(); 2492 } else { 2493 dtConnection = openActiveDataTransferChannel(); 2494 } 2495 // Select the type of contents. 2496 int tp = type; 2497 if (tp == TYPE_AUTO) { 2498 tp = detectType(fileName); 2499 } 2500 if (tp == TYPE_TEXTUAL) { 2501 communication.sendFTPCommand("TYPE A"); 2502 } else if (tp == TYPE_BINARY) { 2503 communication.sendFTPCommand("TYPE I"); 2504 } 2505 FTPReply r = communication.readFTPReply(); 2506 if (!r.isSuccessCode()) { 2507 throw new FTPException(r); 2508 } 2509 // Send the REST command. 2510 communication.sendFTPCommand("REST " + restartAt); 2511 r = communication.readFTPReply(); 2512 if (r.getCode() != 350) { 2513 try { 2514 dtConnection.close(); 2515 } catch (Throwable t) { 2516 ; 2517 } 2518 throw new FTPException(r); 2519 } 2520 // Send the RETR command. 2521 communication.sendFTPCommand("RETR " + fileName); 2522 r = communication.readFTPReply(); 2523 if (r.getCode() != 150 && r.getCode() != 125) { 2524 try { 2525 dtConnection.close(); 2526 } catch (Throwable t) { 2527 ; 2528 } 2529 throw new FTPException(r); 2530 } 2531 // Change the operation status. 2532 synchronized (abortLock) { 2533 ongoingDataTransfer = true; 2534 aborted = false; 2535 } 2536 dataTransferInputStream = dtConnection.getInputStream(); 2537 // Prepare and start a NOOP thread. 2538 Runnable nooper = new Runnable() { 2539 public void run() { 2540 while (!Thread.interrupted()) { 2541 // Sleep. 2542 try { 2543 Thread.sleep(10000); 2544 } catch (InterruptedException e) { 2545 break; 2546 } 2547 // Send NOOP. 2548 try { 2549 communication.sendFTPCommand("NOOP"); 2550 communication.readFTPReply(); 2551 } catch (Throwable t) { 2552 ; 2553 } 2554 } 2555 } 2556 }; 2557 Thread nooperThread = new Thread(nooper); 2558 nooperThread.start(); 2559 // Download the stream. 2560 try { 2561 if (listener != null) { 2562 listener.started(); 2563 } 2564 if (tp == TYPE_TEXTUAL) { 2565 Reader reader = new InputStreamReader( 2566 dataTransferInputStream, "UTF-8"); 2567 Writer writer = new OutputStreamWriter(outputStream); 2568 char[] buffer = new char[1024]; 2569 int l; 2570 while ((l = reader.read(buffer, 0, buffer.length)) != -1) { 2571 writer.write(buffer, 0, l); 2572 if (listener != null) { 2573 listener.transferred(l); 2574 } 2575 } 2576 } else if (tp == TYPE_BINARY) { 2577 byte[] buffer = new byte[1024]; 2578 int l; 2579 while ((l = dataTransferInputStream.read(buffer, 0, 2580 buffer.length)) != -1) { 2581 outputStream.write(buffer, 0, l); 2582 if (listener != null) { 2583 listener.transferred(l); 2584 } 2585 } 2586 } 2587 } catch (IOException e) { 2588 synchronized (abortLock) { 2589 if (aborted) { 2590 if (listener != null) { 2591 listener.aborted(); 2592 } 2593 throw new FTPAbortedException(); 2594 } else { 2595 if (listener != null) { 2596 listener.failed(); 2597 } 2598 throw new FTPDataTransferException( 2599 "I/O error in data transfer", e); 2600 } 2601 } 2602 } finally { 2603 // Stop nooping. 2604 nooperThread.interrupt(); 2605 for (;;) { 2606 try { 2607 nooperThread.join(); 2608 break; 2609 } catch (InterruptedException e) { 2610 continue; 2611 } 2612 } 2613 // Closing stream and data connection. 2614 if (dataTransferInputStream != null) { 2615 try { 2616 dataTransferInputStream.close(); 2617 } catch (Throwable t) { 2618 ; 2619 } 2620 } 2621 try { 2622 dtConnection.close(); 2623 } catch (Throwable t) { 2624 ; 2625 } 2626 // Set to null the instance-level input stream. 2627 dataTransferInputStream = null; 2628 // Consume the result reply of the transfer. 2629 communication.readFTPReply(); 2630 // Change the operation status. 2631 synchronized (abortLock) { 2632 ongoingDataTransfer = false; 2633 aborted = false; 2634 } 2635 } 2636 if (listener != null) { 2637 listener.completed(); 2638 } 2639 } 2640 } 2641 2642 /** 2643 * This method detects the type for a file transfer. 2644 */ 2645 private int detectType(String fileName) throws IOException, 2646 FTPIllegalReplyException, FTPException { 2647 int start = fileName.lastIndexOf('.'); 2648 int stop = fileName.length(); 2649 if (start > 0 && start < stop - 1) { 2650 String ext = fileName.substring(start, stop); 2651 ext = ext.toLowerCase(); 2652 if (textualExtensionRecognizer.isTextualExt(ext)) { 2653 return TYPE_TEXTUAL; 2654 } else { 2655 return TYPE_BINARY; 2656 } 2657 } else { 2658 return TYPE_BINARY; 2659 } 2660 } 2661 2662 /** 2663 * This method opens a data transfer channel in active mode. 2664 */ 2665 private FTPConnection openActiveDataTransferChannel() throws IOException, 2666 FTPIllegalReplyException, FTPException, FTPDataTransferException { 2667 // Create a FTPDataTransferServer object. 2668 FTPDataTransferServer server = new FTPDataTransferServer(); 2669 int port = server.getPort(); 2670 int p1 = port >>> 8; 2671 int p2 = port & 0xff; 2672 byte[] addr = InetAddress.getLocalHost().getAddress(); 2673 int b1 = addr[0] & 0xff; 2674 int b2 = addr[1] & 0xff; 2675 int b3 = addr[2] & 0xff; 2676 int b4 = addr[3] & 0xff; 2677 // Send the port command. 2678 communication.sendFTPCommand("PORT " + b1 + "," + b2 + "," + b3 + "," 2679 + b4 + "," + p1 + "," + p2); 2680 FTPReply r = communication.readFTPReply(); 2681 if (!r.isSuccessCode()) { 2682 server.close(); 2683 throw new FTPException(r); 2684 } 2685 return server.getConnection(); 2686 } 2687 2688 /** 2689 * This method opens a data transfer channel in passive mode. 2690 */ 2691 private FTPConnection openPassiveDataTransferChannel() throws IOException, 2692 FTPIllegalReplyException, FTPException, FTPDataTransferException { 2693 // Send the PASV command. 2694 communication.sendFTPCommand("PASV"); 2695 // Read the reply. 2696 FTPReply r = communication.readFTPReply(); 2697 if (!r.isSuccessCode()) { 2698 throw new FTPException(r); 2699 } 2700 // Use a regexp to extract the remote address and port. 2701 String addressAndPort = null; 2702 String[] messages = r.getMessages(); 2703 for (int i = 0; i < messages.length; i++) { 2704 Matcher m = PASV_PATTERN.matcher(messages[i]); 2705 if (m.find()) { 2706 int start = m.start(); 2707 int end = m.end(); 2708 addressAndPort = messages[i].substring(start, end); 2709 break; 2710 } 2711 } 2712 if (addressAndPort == null) { 2713 // The remote server has not sent the coordinates for the 2714 // data transfer connection. 2715 throw new FTPIllegalReplyException(); 2716 } 2717 // Parse the string extracted from the reply. 2718 StringTokenizer st = new StringTokenizer(addressAndPort, ","); 2719 int b1 = Integer.parseInt(st.nextToken()); 2720 int b2 = Integer.parseInt(st.nextToken()); 2721 int b3 = Integer.parseInt(st.nextToken()); 2722 int b4 = Integer.parseInt(st.nextToken()); 2723 int p1 = Integer.parseInt(st.nextToken()); 2724 int p2 = Integer.parseInt(st.nextToken()); 2725 InetAddress remoteAddress = InetAddress.getByAddress(new byte[] { 2726 (byte) b1, (byte) b2, (byte) b3, (byte) b4 }); 2727 int remotePort = (p1 << 8) | p2; 2728 // Establish the connection. 2729 final FTPConnection dtConnection; 2730 try { 2731 dtConnection = connector.connectForDataTransferChannel( 2732 remoteAddress.getHostAddress(), remotePort); 2733 } catch (IOException e) { 2734 throw new FTPDataTransferException( 2735 "Cannot connect to the remote server", e); 2736 } 2737 return dtConnection; 2738 } 2739 2740 /** 2741 * If there's any ongoing data transfer operation, this method aborts it. 2742 * 2743 * @param sendAborCommand 2744 * If true the client will negotiate the abort procedure with the 2745 * server, through the standard FTP ABOR command. Otherwise the 2746 * open data transfer connection will be closed without any 2747 * advise has sent to the server. 2748 * @throws IOException 2749 * If the ABOR command cannot be sent due to any I/O error. This 2750 * could happen only if force is false. 2751 * @throws FTPIllegalReplyException 2752 * If the server reply to the ABOR command is illegal. This 2753 * could happen only if force is false. 2754 */ 2755 public void abortCurrentDataTransfer(boolean sendAborCommand) 2756 throws IOException, FTPIllegalReplyException { 2757 synchronized (abortLock) { 2758 if (ongoingDataTransfer && !aborted) { 2759 if (sendAborCommand) { 2760 communication.sendFTPCommand("ABOR"); 2761 communication.readFTPReply(); 2762 } 2763 if (dataTransferInputStream != null) { 2764 try { 2765 dataTransferInputStream.close(); 2766 } catch (Throwable t) { 2767 ; 2768 } 2769 } 2770 if (dataTransferOutputStream != null) { 2771 try { 2772 dataTransferOutputStream.close(); 2773 } catch (Throwable t) { 2774 ; 2775 } 2776 } 2777 aborted = true; 2778 } 2779 } 2780 } 2781 2782 /** 2783 * Returns the name of the charset that should be used in textual 2784 * transmissions. 2785 * 2786 * @return The name of the charset that should be used in textual 2787 * transmissions. 2788 */ 2789 private String pickCharset() { 2790 if (charset != null) { 2791 return charset; 2792 } else if (utf8Supported) { 2793 return "UTF-8"; 2794 } else { 2795 return System.getProperty("file.encoding"); 2796 } 2797 } 2798 2799 public String toString() { 2800 synchronized (lock) { 2801 StringBuffer buffer = new StringBuffer(); 2802 buffer.append(getClass().getName()); 2803 buffer.append(" [connected="); 2804 buffer.append(connected); 2805 if (connected) { 2806 buffer.append(", host="); 2807 buffer.append(host); 2808 buffer.append(", port="); 2809 buffer.append(port); 2810 } 2811 buffer.append(", authenticated="); 2812 buffer.append(authenticated); 2813 if (authenticated) { 2814 buffer.append(", username="); 2815 buffer.append(username); 2816 buffer.append(", password="); 2817 StringBuffer buffer2 = new StringBuffer(); 2818 for (int i = 0; i < password.length(); i++) { 2819 buffer2.append('*'); 2820 } 2821 buffer.append(buffer2); 2822 } 2823 buffer.append(", transfer mode="); 2824 buffer.append(passive ? "passive" : "active"); 2825 buffer.append(", transfer type="); 2826 switch (type) { 2827 case TYPE_AUTO: 2828 buffer.append("auto"); 2829 break; 2830 case TYPE_BINARY: 2831 buffer.append("binary"); 2832 break; 2833 case TYPE_TEXTUAL: 2834 buffer.append("textual"); 2835 break; 2836 } 2837 buffer.append(", connector="); 2838 buffer.append(connector); 2839 buffer.append(", textualExtensionRecognizer="); 2840 buffer.append(textualExtensionRecognizer); 2841 FTPListParser[] listParsers = getListParsers(); 2842 if (listParsers.length > 0) { 2843 buffer.append(", listParsers="); 2844 for (int i = 0; i < listParsers.length; i++) { 2845 if (i > 0) { 2846 buffer.append(", "); 2847 } 2848 buffer.append(listParsers[i]); 2849 } 2850 } 2851 FTPCommunicationListener[] communicationListeners = getCommunicationListeners(); 2852 if (communicationListeners.length > 0) { 2853 buffer.append(", communicationListeners="); 2854 for (int i = 0; i < communicationListeners.length; i++) { 2855 if (i > 0) { 2856 buffer.append(", "); 2857 } 2858 buffer.append(communicationListeners[i]); 2859 } 2860 } 2861 buffer.append("]"); 2862 return buffer.toString(); 2863 } 2864 } 2865 2866 }