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.3 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 // Prepares the connection for the data transfer. 1540 FTPDataTransferConnectionProvider provider; 1541 // Active or passive? 1542 if (passive) { 1543 provider = openPassiveDataTransferChannel(); 1544 } else { 1545 provider = openActiveDataTransferChannel(); 1546 } 1547 // ASCII, please! 1548 communication.sendFTPCommand("TYPE A"); 1549 FTPReply r = communication.readFTPReply(); 1550 if (!r.isSuccessCode()) { 1551 provider.dispose(); 1552 throw new FTPException(r); 1553 } 1554 // Send the list command. 1555 String command = "LIST"; 1556 if (fileSpec != null && fileSpec.length() > 0) { 1557 command += " " + fileSpec; 1558 } 1559 communication.sendFTPCommand(command); 1560 FTPConnection dtConnection; 1561 try { 1562 dtConnection = provider.openDataTransferConnection(); 1563 r = communication.readFTPReply(); 1564 if (r.getCode() != 150 && r.getCode() != 125) { 1565 throw new FTPException(r); 1566 } 1567 } finally { 1568 provider.dispose(); 1569 } 1570 // Change the operation status. 1571 synchronized (abortLock) { 1572 ongoingDataTransfer = true; 1573 aborted = false; 1574 } 1575 // Fetch the list from the data transfer connection. 1576 ArrayList lines = new ArrayList(); 1577 NVTASCIIReader dataReader = null; 1578 try { 1579 // Opens the data transfer connection. 1580 dataTransferInputStream = dtConnection.getInputStream(); 1581 // Let's do it! 1582 dataReader = new NVTASCIIReader(dataTransferInputStream, 1583 pickCharset()); 1584 String line; 1585 while ((line = dataReader.readLine()) != null) { 1586 if (line.length() > 0) { 1587 lines.add(line); 1588 } 1589 } 1590 } catch (IOException e) { 1591 synchronized (abortLock) { 1592 if (aborted) { 1593 throw new FTPAbortedException(); 1594 } else { 1595 throw new FTPDataTransferException( 1596 "I/O error in data transfer", e); 1597 } 1598 } 1599 } finally { 1600 if (dataReader != null) { 1601 try { 1602 dataReader.close(); 1603 } catch (Throwable t) { 1604 ; 1605 } 1606 } 1607 try { 1608 dtConnection.close(); 1609 } catch (Throwable t) { 1610 ; 1611 } 1612 // Consume the result reply of the transfer. 1613 communication.readFTPReply(); 1614 // Set to null the instance-level input stream. 1615 dataTransferInputStream = null; 1616 // Change the operation status. 1617 synchronized (abortLock) { 1618 ongoingDataTransfer = false; 1619 aborted = false; 1620 } 1621 } 1622 // Build an array of lines. 1623 int size = lines.size(); 1624 String[] list = new String[size]; 1625 for (int i = 0; i < size; i++) { 1626 list[i] = (String) lines.get(i); 1627 } 1628 // Parse the list. 1629 FTPFile[] ret = null; 1630 if (parser == null) { 1631 // Try to parse the list with every parser available. 1632 for (Iterator i = listParsers.iterator(); i.hasNext();) { 1633 FTPListParser aux = (FTPListParser) i.next(); 1634 try { 1635 // Let's try! 1636 ret = aux.parse(list); 1637 // This parser smells good! 1638 parser = aux; 1639 // Leave the loop. 1640 break; 1641 } catch (FTPListParseException e) { 1642 // Let's try the next one. 1643 continue; 1644 } 1645 } 1646 } else { 1647 ret = parser.parse(list); 1648 } 1649 if (ret == null) { 1650 // None of the parsers can handle the list response. 1651 throw new FTPListParseException(); 1652 } else { 1653 // Return the parsed list. 1654 return ret; 1655 } 1656 } 1657 } 1658 1659 /** 1660 * This method lists the entries of the current working directory parsing 1661 * the reply to a FTP LIST command. 1662 * 1663 * The response to the LIST command is parsed through the FTPListParser 1664 * objects registered on the client. The distribution of ftp4j contains some 1665 * standard parsers already registered on every FTPClient object created. If 1666 * they don't work in your case (a FTPListParseException is thrown), you can 1667 * build your own parser implementing the FTPListParser interface and add it 1668 * to the client by calling its addListParser() method. 1669 * 1670 * Calling this method blocks the current thread until the operation is 1671 * completed. The operation could be interrupted by another thread calling 1672 * abortCurrentDataTransfer(). The list() method will break with a 1673 * FTPAbortedException. 1674 * 1675 * @return The list of the files (and directories) in the current working 1676 * directory. 1677 * @throws IllegalStateException 1678 * If the client is not connected or not authenticated. 1679 * @throws IOException 1680 * If an I/O error occurs. 1681 * @throws FTPIllegalReplyException 1682 * If the server replies in an illegal way. 1683 * @throws FTPException 1684 * If the operation fails. 1685 * @throws FTPDataTransferException 1686 * If a I/O occurs in the data transfer connection. If you 1687 * receive this exception the transfer failed, but the main 1688 * connection with the remote FTP server is in theory still 1689 * working. 1690 * @throws FTPAbortedException 1691 * If operation is aborted by another thread. 1692 * @throws FTPListParseException 1693 * If none of the registered parsers can handle the response 1694 * sent by the server. 1695 * @see FTPListParser 1696 * @see FTPClient#addListParser(FTPListParser) 1697 * @see FTPClient#getListParsers() 1698 * @see FTPClient#abortCurrentDataTransfer(boolean) 1699 * @see FTPClient#listNames() 1700 */ 1701 public FTPFile[] list() throws IllegalStateException, IOException, 1702 FTPIllegalReplyException, FTPException, FTPDataTransferException, 1703 FTPAbortedException, FTPListParseException { 1704 return list(null); 1705 } 1706 1707 /** 1708 * This method lists the entries of the current working directory with a FTP 1709 * NLST command. 1710 * 1711 * The response consists in an array of string, each one reporting the name 1712 * of a file or a directory placed in the current working directory. For a 1713 * more detailed directory listing procedure look at the list() method. 1714 * 1715 * Calling this method blocks the current thread until the operation is 1716 * completed. The operation could be interrupted by another thread calling 1717 * abortCurrentDataTransfer(). The listNames() method will break with a 1718 * FTPAbortedException. 1719 * 1720 * @return The list of the files (and directories) in the current working 1721 * directory. 1722 * @throws IllegalStateException 1723 * If the client is not connected or not authenticated. 1724 * @throws IOException 1725 * If an I/O error occurs. 1726 * @throws FTPIllegalReplyException 1727 * If the server replies in an illegal way. 1728 * @throws FTPException 1729 * If the operation fails. 1730 * @throws FTPDataTransferException 1731 * If a I/O occurs in the data transfer connection. If you 1732 * receive this exception the transfer failed, but the main 1733 * connection with the remote FTP server is in theory still 1734 * working. 1735 * @throws FTPAbortedException 1736 * If operation is aborted by another thread. 1737 * @throws FTPListParseException 1738 * If none of the registered parsers can handle the response 1739 * sent by the server. 1740 * @see FTPClient#abortCurrentDataTransfer(boolean) 1741 * @see FTPClient#list() 1742 */ 1743 public String[] listNames() throws IllegalStateException, IOException, 1744 FTPIllegalReplyException, FTPException, FTPDataTransferException, 1745 FTPAbortedException, FTPListParseException { 1746 synchronized (lock) { 1747 // Is this client connected? 1748 if (!connected) { 1749 throw new IllegalStateException("Client not connected"); 1750 } 1751 // Is this client authenticated? 1752 if (!authenticated) { 1753 throw new IllegalStateException("Client not authenticated"); 1754 } 1755 // Prepares the connection for the data transfer. 1756 FTPDataTransferConnectionProvider provider; 1757 // Active or passive? 1758 if (passive) { 1759 provider = openPassiveDataTransferChannel(); 1760 } else { 1761 provider = openActiveDataTransferChannel(); 1762 } 1763 // ASCII, please! 1764 communication.sendFTPCommand("TYPE A"); 1765 FTPReply r = communication.readFTPReply(); 1766 if (!r.isSuccessCode()) { 1767 provider.dispose(); 1768 throw new FTPException(r); 1769 } 1770 // Send the NLST command. 1771 communication.sendFTPCommand("NLST"); 1772 FTPConnection dtConnection; 1773 try { 1774 dtConnection = provider.openDataTransferConnection(); 1775 r = communication.readFTPReply(); 1776 if (r.getCode() != 150 && r.getCode() != 125) { 1777 throw new FTPException(r); 1778 } 1779 } finally { 1780 provider.dispose(); 1781 } 1782 // Change the operation status. 1783 synchronized (abortLock) { 1784 ongoingDataTransfer = true; 1785 aborted = false; 1786 } 1787 // Fetch the list from the data transfer connection. 1788 ArrayList lines = new ArrayList(); 1789 NVTASCIIReader dataReader = null; 1790 try { 1791 // Opens the data transfer connection. 1792 dataTransferInputStream = dtConnection.getInputStream(); 1793 // Let's do it! 1794 dataReader = new NVTASCIIReader(dataTransferInputStream, 1795 pickCharset()); 1796 String line; 1797 while ((line = dataReader.readLine()) != null) { 1798 if (line.length() > 0) { 1799 lines.add(line); 1800 } 1801 } 1802 } catch (IOException e) { 1803 synchronized (abortLock) { 1804 if (aborted) { 1805 throw new FTPAbortedException(); 1806 } else { 1807 throw new FTPDataTransferException( 1808 "I/O error in data transfer", e); 1809 } 1810 } 1811 } finally { 1812 if (dataReader != null) { 1813 try { 1814 dataReader.close(); 1815 } catch (Throwable t) { 1816 ; 1817 } 1818 } 1819 try { 1820 dtConnection.close(); 1821 } catch (Throwable t) { 1822 ; 1823 } 1824 // Consume the result reply of the transfer. 1825 communication.readFTPReply(); 1826 // Set to null the instance-level input stream. 1827 dataTransferInputStream = null; 1828 // Change the operation status. 1829 synchronized (abortLock) { 1830 ongoingDataTransfer = false; 1831 aborted = false; 1832 } 1833 } 1834 // Build an array. 1835 int size = lines.size(); 1836 String[] list = new String[size]; 1837 for (int i = 0; i < size; i++) { 1838 list[i] = (String) lines.get(i); 1839 } 1840 return list; 1841 } 1842 } 1843 1844 /** 1845 * This method uploads a file to the remote server. 1846 * 1847 * Calling this method blocks the current thread until the operation is 1848 * completed. The operation could be interrupted by another thread calling 1849 * abortCurrentDataTransfer(). The method will break with a 1850 * FTPAbortedException. 1851 * 1852 * @param file 1853 * The file to upload. 1854 * @throws IllegalStateException 1855 * If the client is not connected or not authenticated. 1856 * @throws FileNotFoundException 1857 * If the supplied file cannot be found. 1858 * @throws IOException 1859 * If an I/O error occurs. 1860 * @throws FTPIllegalReplyException 1861 * If the server replies in an illegal way. 1862 * @throws FTPException 1863 * If the operation fails. 1864 * @throws FTPDataTransferException 1865 * If a I/O occurs in the data transfer connection. If you 1866 * receive this exception the transfer failed, but the main 1867 * connection with the remote FTP server is in theory still 1868 * working. 1869 * @throws FTPAbortedException 1870 * If operation is aborted by another thread. 1871 * @see FTPClient#abortCurrentDataTransfer(boolean) 1872 */ 1873 public void upload(File file) throws IllegalStateException, 1874 FileNotFoundException, IOException, FTPIllegalReplyException, 1875 FTPException, FTPDataTransferException, FTPAbortedException { 1876 upload(file, 0, null); 1877 } 1878 1879 /** 1880 * This method uploads a file to the remote server. 1881 * 1882 * Calling this method blocks the current thread until the operation is 1883 * completed. The operation could be interrupted by another thread calling 1884 * abortCurrentDataTransfer(). The method will break with a 1885 * FTPAbortedException. 1886 * 1887 * @param file 1888 * The file to upload. 1889 * @param listener 1890 * The listener for the operation. Could be null. 1891 * @throws IllegalStateException 1892 * If the client is not connected or not authenticated. 1893 * @throws FileNotFoundException 1894 * If the supplied file cannot be found. 1895 * @throws IOException 1896 * If an I/O error occurs. 1897 * @throws FTPIllegalReplyException 1898 * If the server replies in an illegal way. 1899 * @throws FTPException 1900 * If the operation fails. 1901 * @throws FTPDataTransferException 1902 * If a I/O occurs in the data transfer connection. If you 1903 * receive this exception the transfer failed, but the main 1904 * connection with the remote FTP server is in theory still 1905 * working. 1906 * @throws FTPAbortedException 1907 * If operation is aborted by another thread. 1908 * @see FTPClient#abortCurrentDataTransfer(boolean) 1909 */ 1910 public void upload(File file, FTPDataTransferListener listener) 1911 throws IllegalStateException, FileNotFoundException, IOException, 1912 FTPIllegalReplyException, FTPException, FTPDataTransferException, 1913 FTPAbortedException { 1914 upload(file, 0, listener); 1915 } 1916 1917 /** 1918 * This method resumes an upload of a file to the remote server. 1919 * 1920 * Calling this method blocks the current thread until the operation is 1921 * completed. The operation could be interrupted by another thread calling 1922 * abortCurrentDataTransfer(). The method will break with a 1923 * FTPAbortedException. 1924 * 1925 * @param file 1926 * The file to upload. 1927 * @param restartAt 1928 * The restart point (number of bytes already uploaded). 1929 * @throws IllegalStateException 1930 * If the client is not connected or not authenticated. 1931 * @throws FileNotFoundException 1932 * If the supplied file cannot be found. 1933 * @throws IOException 1934 * If an I/O error occurs. 1935 * @throws FTPIllegalReplyException 1936 * If the server replies in an illegal way. 1937 * @throws FTPException 1938 * If the operation fails. 1939 * @throws FTPDataTransferException 1940 * If a I/O occurs in the data transfer connection. If you 1941 * receive this exception the transfer failed, but the main 1942 * connection with the remote FTP server is in theory still 1943 * working. 1944 * @throws FTPAbortedException 1945 * If operation is aborted by another thread. 1946 * @see FTPClient#abortCurrentDataTransfer(boolean) 1947 */ 1948 public void upload(File file, long restartAt) throws IllegalStateException, 1949 FileNotFoundException, IOException, FTPIllegalReplyException, 1950 FTPException, FTPDataTransferException, FTPAbortedException { 1951 upload(file, restartAt, null); 1952 } 1953 1954 /** 1955 * This method resumes an upload of a file to the remote server. 1956 * 1957 * Calling this method blocks the current thread until the operation is 1958 * completed. The operation could be interrupted by another thread calling 1959 * abortCurrentDataTransfer(). The method will break with a 1960 * FTPAbortedException. 1961 * 1962 * @param file 1963 * The file to upload. 1964 * @param restartAt 1965 * The restart point (number of bytes already uploaded). 1966 * @param listener 1967 * The listener for the operation. Could be null. 1968 * @throws IllegalStateException 1969 * If the client is not connected or not authenticated. 1970 * @throws FileNotFoundException 1971 * If the supplied file cannot be found. 1972 * @throws IOException 1973 * If an I/O error occurs. 1974 * @throws FTPIllegalReplyException 1975 * If the server replies in an illegal way. 1976 * @throws FTPException 1977 * If the operation fails. 1978 * @throws FTPDataTransferException 1979 * If a I/O occurs in the data transfer connection. If you 1980 * receive this exception the transfer failed, but the main 1981 * connection with the remote FTP server is in theory still 1982 * working. 1983 * @throws FTPAbortedException 1984 * If operation is aborted by another thread. 1985 * @see FTPClient#abortCurrentDataTransfer(boolean) 1986 */ 1987 public void upload(File file, long restartAt, 1988 FTPDataTransferListener listener) throws IllegalStateException, 1989 FileNotFoundException, IOException, FTPIllegalReplyException, 1990 FTPException, FTPDataTransferException, FTPAbortedException { 1991 if (!file.exists()) { 1992 throw new FileNotFoundException(file.getAbsolutePath()); 1993 } 1994 InputStream inputStream = null; 1995 try { 1996 inputStream = new FileInputStream(file); 1997 } catch (IOException e) { 1998 throw new FTPDataTransferException(e); 1999 } 2000 try { 2001 upload(file.getName(), inputStream, restartAt, restartAt, file 2002 .length(), listener); 2003 } catch (IllegalStateException e) { 2004 throw e; 2005 } catch (IOException e) { 2006 throw e; 2007 } catch (FTPIllegalReplyException e) { 2008 throw e; 2009 } catch (FTPException e) { 2010 throw e; 2011 } catch (FTPDataTransferException e) { 2012 throw e; 2013 } catch (FTPAbortedException e) { 2014 throw e; 2015 } finally { 2016 if (inputStream != null) { 2017 try { 2018 inputStream.close(); 2019 } catch (Throwable t) { 2020 ; 2021 } 2022 } 2023 } 2024 } 2025 2026 /** 2027 * This method resumes an upload to the remote server. 2028 * 2029 * Note that no byte is skipped in the given inputStream. The restartAt 2030 * value is, in this case, an information for the remote server. 2031 * 2032 * Calling this method blocks the current thread until the operation is 2033 * completed. The operation could be interrupted by another thread calling 2034 * abortCurrentDataTransfer(). The method will break with a 2035 * FTPAbortedException. 2036 * 2037 * @param fileName 2038 * The name of the remote file. 2039 * @param inputStream 2040 * The source of data. 2041 * @param restartAt 2042 * The restart point (number of bytes already uploaded). 2043 * @param streamOffset 2044 * The offset to skip in the stream. 2045 * @param streamLength 2046 * The length of the data of the stream to upload. 2047 * @param listener 2048 * The listener for the operation. Could be null. 2049 * @throws IllegalStateException 2050 * If the client is not connected or not authenticated. 2051 * @throws IOException 2052 * If an I/O error occurs. 2053 * @throws FTPIllegalReplyException 2054 * If the server replies in an illegal way. 2055 * @throws FTPException 2056 * If the operation fails. 2057 * @throws FTPDataTransferException 2058 * If a I/O occurs in the data transfer connection. If you 2059 * receive this exception the transfer failed, but the main 2060 * connection with the remote FTP server is in theory still 2061 * working. 2062 * @throws FTPAbortedException 2063 * If operation is aborted by another thread. 2064 * @see FTPClient#abortCurrentDataTransfer(boolean) 2065 */ 2066 public void upload(String fileName, InputStream inputStream, 2067 long restartAt, long streamOffset, long streamLength, 2068 FTPDataTransferListener listener) throws IllegalStateException, 2069 IOException, FTPIllegalReplyException, FTPException, 2070 FTPDataTransferException, FTPAbortedException { 2071 synchronized (lock) { 2072 // Is this client connected? 2073 if (!connected) { 2074 throw new IllegalStateException("Client not connected"); 2075 } 2076 // Is this client authenticated? 2077 if (!authenticated) { 2078 throw new IllegalStateException("Client not authenticated"); 2079 } 2080 // Prepares the connection for the data transfer. 2081 FTPDataTransferConnectionProvider provider; 2082 // Active or passive? 2083 if (passive) { 2084 provider = openPassiveDataTransferChannel(); 2085 } else { 2086 provider = openActiveDataTransferChannel(); 2087 } 2088 2089 // Select the type of contents. 2090 int tp = type; 2091 if (tp == TYPE_AUTO) { 2092 tp = detectType(fileName); 2093 } 2094 if (tp == TYPE_TEXTUAL) { 2095 communication.sendFTPCommand("TYPE A"); 2096 } else if (tp == TYPE_BINARY) { 2097 communication.sendFTPCommand("TYPE I"); 2098 } 2099 FTPReply r = communication.readFTPReply(); 2100 if (!r.isSuccessCode()) { 2101 throw new FTPException(r); 2102 } 2103 // Send the REST command. 2104 communication.sendFTPCommand("REST " + restartAt); 2105 r = communication.readFTPReply(); 2106 if (r.getCode() != 350) { 2107 provider.dispose(); 2108 throw new FTPException(r); 2109 } 2110 // Send the STOR command. 2111 communication.sendFTPCommand("STOR " + fileName); 2112 FTPConnection dtConnection; 2113 try { 2114 dtConnection = provider.openDataTransferConnection(); 2115 r = communication.readFTPReply(); 2116 if (r.getCode() != 150 && r.getCode() != 125) { 2117 throw new FTPException(r); 2118 } 2119 } finally { 2120 provider.dispose(); 2121 } 2122 // Change the operation status. 2123 synchronized (abortLock) { 2124 ongoingDataTransfer = true; 2125 aborted = false; 2126 } 2127 // Prepare and start a NOOPer thread. 2128 NOOPer nooper = new NOOPer(); 2129 nooper.start(); 2130 // Upload the stream. 2131 long done = 0; 2132 try { 2133 // Skips. 2134 inputStream.skip(streamOffset); 2135 // Opens the data transfer connection. 2136 dataTransferInputStream = dtConnection.getInputStream(); 2137 // Listeners. 2138 if (listener != null) { 2139 listener.started(); 2140 } 2141 // Let's do it! 2142 if (tp == TYPE_TEXTUAL) { 2143 Reader reader = new InputStreamReader(inputStream); 2144 Writer writer = new OutputStreamWriter( 2145 dataTransferOutputStream, "UTF-8"); 2146 char[] buffer = new char[1024]; 2147 while (done < streamLength) { 2148 int l = reader.read(buffer, 0, (int) Math.min( 2149 buffer.length, streamLength - done)); 2150 if (l == -1) { 2151 throw new IOException("End of stream reached"); 2152 } else { 2153 writer.write(buffer, 0, l); 2154 done += l; 2155 if (listener != null) { 2156 listener.transferred(l); 2157 } 2158 } 2159 } 2160 } else if (tp == TYPE_BINARY) { 2161 byte[] buffer = new byte[1024]; 2162 while (done < streamLength) { 2163 int l = inputStream.read(buffer, 0, (int) Math.min( 2164 buffer.length, streamLength - done)); 2165 if (l == -1) { 2166 throw new IOException("End of stream reached"); 2167 } else { 2168 dataTransferOutputStream.write(buffer, 0, l); 2169 done += l; 2170 if (listener != null) { 2171 listener.transferred(l); 2172 } 2173 } 2174 } 2175 } 2176 } catch (IOException e) { 2177 synchronized (abortLock) { 2178 if (aborted) { 2179 if (listener != null) { 2180 listener.aborted(); 2181 } 2182 throw new FTPAbortedException(); 2183 } else { 2184 if (listener != null) { 2185 listener.failed(); 2186 } 2187 throw new FTPDataTransferException( 2188 "I/O error in data transfer", e); 2189 } 2190 } 2191 } finally { 2192 // Stop nooping. 2193 nooper.interrupt(); 2194 for (;;) { 2195 try { 2196 nooper.join(); 2197 break; 2198 } catch (InterruptedException e) { 2199 continue; 2200 } 2201 } 2202 // Closing stream and data connection. 2203 if (dataTransferOutputStream != null) { 2204 try { 2205 dataTransferOutputStream.close(); 2206 } catch (Throwable t) { 2207 ; 2208 } 2209 } 2210 try { 2211 dtConnection.close(); 2212 } catch (Throwable t) { 2213 ; 2214 } 2215 // Set to null the instance-level input stream. 2216 dataTransferOutputStream = null; 2217 // Consume the result reply of the transfer. 2218 communication.readFTPReply(); 2219 // Change the operation status. 2220 synchronized (abortLock) { 2221 ongoingDataTransfer = false; 2222 aborted = false; 2223 } 2224 } 2225 if (listener != null) { 2226 listener.completed(); 2227 } 2228 } 2229 } 2230 2231 /** 2232 * This method downloads a remote file from the server to a local file. 2233 * 2234 * Calling this method blocks the current thread until the operation is 2235 * completed. The operation could be interrupted by another thread calling 2236 * abortCurrentDataTransfer(). The method will break with a 2237 * FTPAbortedException. 2238 * 2239 * @param remoteFileName 2240 * The name of the file to download. 2241 * @param localFile 2242 * The local file. 2243 * @throws IllegalStateException 2244 * If the client is not connected or not authenticated. 2245 * @throws FileNotFoundException 2246 * If the supplied file cannot be found. 2247 * @throws IOException 2248 * If an I/O error occurs. 2249 * @throws FTPIllegalReplyException 2250 * If the server replies in an illegal way. 2251 * @throws FTPException 2252 * If the operation fails. 2253 * @throws FTPDataTransferException 2254 * If a I/O occurs in the data transfer connection. If you 2255 * receive this exception the transfer failed, but the main 2256 * connection with the remote FTP server is in theory still 2257 * working. 2258 * @throws FTPAbortedException 2259 * If operation is aborted by another thread. 2260 * @see FTPClient#abortCurrentDataTransfer(boolean) 2261 */ 2262 public void download(String remoteFileName, File localFile) 2263 throws IllegalStateException, FileNotFoundException, IOException, 2264 FTPIllegalReplyException, FTPException, FTPDataTransferException, 2265 FTPAbortedException { 2266 download(remoteFileName, localFile, 0, null); 2267 } 2268 2269 /** 2270 * This method downloads a remote file from the server to a local file. 2271 * 2272 * Calling this method blocks the current thread until the operation is 2273 * completed. The operation could be interrupted by another thread calling 2274 * abortCurrentDataTransfer(). The method will break with a 2275 * FTPAbortedException. 2276 * 2277 * @param remoteFileName 2278 * The name of the file to download. 2279 * @param localFile 2280 * The local file. 2281 * @param listener 2282 * The listener for the operation. Could be null. 2283 * @throws IllegalStateException 2284 * If the client is not connected or not authenticated. 2285 * @throws FileNotFoundException 2286 * If the supplied file cannot be found. 2287 * @throws IOException 2288 * If an I/O error occurs. 2289 * @throws FTPIllegalReplyException 2290 * If the server replies in an illegal way. 2291 * @throws FTPException 2292 * If the operation fails. 2293 * @throws FTPDataTransferException 2294 * If a I/O occurs in the data transfer connection. If you 2295 * receive this exception the transfer failed, but the main 2296 * connection with the remote FTP server is in theory still 2297 * working. 2298 * @throws FTPAbortedException 2299 * If operation is aborted by another thread. 2300 * @see FTPClient#abortCurrentDataTransfer(boolean) 2301 */ 2302 public void download(String remoteFileName, File localFile, 2303 FTPDataTransferListener listener) throws IllegalStateException, 2304 FileNotFoundException, IOException, FTPIllegalReplyException, 2305 FTPException, FTPDataTransferException, FTPAbortedException { 2306 download(remoteFileName, localFile, 0, listener); 2307 } 2308 2309 /** 2310 * This method resumes a download operation from the remote server to a 2311 * local file. 2312 * 2313 * Calling this method blocks the current thread until the operation is 2314 * completed. The operation could be interrupted by another thread calling 2315 * abortCurrentDataTransfer(). The method will break with a 2316 * FTPAbortedException. 2317 * 2318 * @param remoteFileName 2319 * The name of the file to download. 2320 * @param localFile 2321 * The local file. 2322 * @param restartAt 2323 * The restart point (number of bytes already downloaded). 2324 * @throws IllegalStateException 2325 * If the client is not connected or not authenticated. 2326 * @throws FileNotFoundException 2327 * If the supplied file cannot be found. 2328 * @throws IOException 2329 * If an I/O error occurs. 2330 * @throws FTPIllegalReplyException 2331 * If the server replies in an illegal way. 2332 * @throws FTPException 2333 * If the operation fails. 2334 * @throws FTPDataTransferException 2335 * If a I/O occurs in the data transfer connection. If you 2336 * receive this exception the transfer failed, but the main 2337 * connection with the remote FTP server is in theory still 2338 * working. 2339 * @throws FTPAbortedException 2340 * If operation is aborted by another thread. 2341 * @see FTPClient#abortCurrentDataTransfer(boolean) 2342 */ 2343 public void download(String remoteFileName, File localFile, long restartAt) 2344 throws IllegalStateException, FileNotFoundException, IOException, 2345 FTPIllegalReplyException, FTPException, FTPDataTransferException, 2346 FTPAbortedException { 2347 download(remoteFileName, localFile, restartAt, null); 2348 } 2349 2350 /** 2351 * This method resumes a download operation from the remote server to a 2352 * local file. 2353 * 2354 * Calling this method blocks the current thread until the operation is 2355 * completed. The operation could be interrupted by another thread calling 2356 * abortCurrentDataTransfer(). The method will break with a 2357 * FTPAbortedException. 2358 * 2359 * @param remoteFileName 2360 * The name of the file to download. 2361 * @param localFile 2362 * The local file. 2363 * @param restartAt 2364 * The restart point (number of bytes already downloaded). 2365 * @param listener 2366 * The listener for the operation. Could be null. 2367 * @throws IllegalStateException 2368 * If the client is not connected or not authenticated. 2369 * @throws FileNotFoundException 2370 * If the supplied file cannot be found. 2371 * @throws IOException 2372 * If an I/O error occurs. 2373 * @throws FTPIllegalReplyException 2374 * If the server replies in an illegal way. 2375 * @throws FTPException 2376 * If the operation fails. 2377 * @throws FTPDataTransferException 2378 * If a I/O occurs in the data transfer connection. If you 2379 * receive this exception the transfer failed, but the main 2380 * connection with the remote FTP server is in theory still 2381 * working. 2382 * @throws FTPAbortedException 2383 * If operation is aborted by another thread. 2384 * @see FTPClient#abortCurrentDataTransfer(boolean) 2385 */ 2386 public void download(String remoteFileName, File localFile, long restartAt, 2387 FTPDataTransferListener listener) throws IllegalStateException, 2388 FileNotFoundException, IOException, FTPIllegalReplyException, 2389 FTPException, FTPDataTransferException, FTPAbortedException { 2390 OutputStream outputStream = null; 2391 try { 2392 outputStream = new FileOutputStream(localFile, true); 2393 } catch (IOException e) { 2394 throw new FTPDataTransferException(e); 2395 } 2396 try { 2397 download(remoteFileName, outputStream, restartAt, listener); 2398 } catch (IllegalStateException e) { 2399 throw e; 2400 } catch (IOException e) { 2401 throw e; 2402 } catch (FTPIllegalReplyException e) { 2403 throw e; 2404 } catch (FTPException e) { 2405 throw e; 2406 } catch (FTPDataTransferException e) { 2407 throw e; 2408 } catch (FTPAbortedException e) { 2409 throw e; 2410 } finally { 2411 if (outputStream != null) { 2412 try { 2413 outputStream.close(); 2414 } catch (Throwable t) { 2415 ; 2416 } 2417 } 2418 } 2419 } 2420 2421 /** 2422 * This method resumes a download operation from the remote server. 2423 * 2424 * Calling this method blocks the current thread until the operation is 2425 * completed. The operation could be interrupted by another thread calling 2426 * abortCurrentDataTransfer(). The method will break with a 2427 * FTPAbortedException. 2428 * 2429 * @param fileName 2430 * The name of the remote file. 2431 * @param outputStream 2432 * The destination stream of data read during the download. 2433 * @param restartAt 2434 * The restart point (number of bytes already downloaded). 2435 * @param listener 2436 * The listener for the operation. Could be null. 2437 * @throws IllegalStateException 2438 * If the client is not connected or not authenticated. 2439 * @throws IOException 2440 * If an I/O error occurs. 2441 * @throws FTPIllegalReplyException 2442 * If the server replies in an illegal way. 2443 * @throws FTPException 2444 * If the operation fails. 2445 * @throws FTPDataTransferException 2446 * If a I/O occurs in the data transfer connection. If you 2447 * receive this exception the transfer failed, but the main 2448 * connection with the remote FTP server is in theory still 2449 * working. 2450 * @throws FTPAbortedException 2451 * If operation is aborted by another thread. 2452 * @see FTPClient#abortCurrentDataTransfer(boolean) 2453 */ 2454 public void download(String fileName, OutputStream outputStream, 2455 long restartAt, FTPDataTransferListener listener) 2456 throws IllegalStateException, IOException, 2457 FTPIllegalReplyException, FTPException, FTPDataTransferException, 2458 FTPAbortedException { 2459 synchronized (lock) { 2460 // Is this client connected? 2461 if (!connected) { 2462 throw new IllegalStateException("Client not connected"); 2463 } 2464 // Is this client authenticated? 2465 if (!authenticated) { 2466 throw new IllegalStateException("Client not authenticated"); 2467 } 2468 // Prepares the connection for the data transfer. 2469 FTPDataTransferConnectionProvider provider; 2470 // Active or passive? 2471 if (passive) { 2472 provider = openPassiveDataTransferChannel(); 2473 } else { 2474 provider = openActiveDataTransferChannel(); 2475 } 2476 // Select the type of contents. 2477 int tp = type; 2478 if (tp == TYPE_AUTO) { 2479 tp = detectType(fileName); 2480 } 2481 if (tp == TYPE_TEXTUAL) { 2482 communication.sendFTPCommand("TYPE A"); 2483 } else if (tp == TYPE_BINARY) { 2484 communication.sendFTPCommand("TYPE I"); 2485 } 2486 FTPReply r = communication.readFTPReply(); 2487 if (!r.isSuccessCode()) { 2488 throw new FTPException(r); 2489 } 2490 // Send the REST command. 2491 communication.sendFTPCommand("REST " + restartAt); 2492 r = communication.readFTPReply(); 2493 if (r.getCode() != 350) { 2494 provider.dispose(); 2495 throw new FTPException(r); 2496 } 2497 // Send the RETR command. 2498 communication.sendFTPCommand("RETR " + fileName); 2499 FTPConnection dtConnection; 2500 try { 2501 dtConnection = provider.openDataTransferConnection(); 2502 r = communication.readFTPReply(); 2503 if (r.getCode() != 150 && r.getCode() != 125) { 2504 throw new FTPException(r); 2505 } 2506 } finally { 2507 provider.dispose(); 2508 } 2509 // Change the operation status. 2510 synchronized (abortLock) { 2511 ongoingDataTransfer = true; 2512 aborted = false; 2513 } 2514 // Prepare and start a NOOP thread. 2515 NOOPer nooper = new NOOPer(); 2516 nooper.start(); 2517 // Download the stream. 2518 try { 2519 // Opens the data transfer connection. 2520 dataTransferInputStream = dtConnection.getInputStream(); 2521 // Listeners. 2522 if (listener != null) { 2523 listener.started(); 2524 } 2525 // Let's do it! 2526 if (tp == TYPE_TEXTUAL) { 2527 Reader reader = new InputStreamReader( 2528 dataTransferInputStream, "UTF-8"); 2529 Writer writer = new OutputStreamWriter(outputStream); 2530 char[] buffer = new char[1024]; 2531 int l; 2532 while ((l = reader.read(buffer, 0, buffer.length)) != -1) { 2533 writer.write(buffer, 0, l); 2534 if (listener != null) { 2535 listener.transferred(l); 2536 } 2537 } 2538 } else if (tp == TYPE_BINARY) { 2539 byte[] buffer = new byte[1024]; 2540 int l; 2541 while ((l = dataTransferInputStream.read(buffer, 0, 2542 buffer.length)) != -1) { 2543 outputStream.write(buffer, 0, l); 2544 if (listener != null) { 2545 listener.transferred(l); 2546 } 2547 } 2548 } 2549 } catch (IOException e) { 2550 synchronized (abortLock) { 2551 if (aborted) { 2552 if (listener != null) { 2553 listener.aborted(); 2554 } 2555 throw new FTPAbortedException(); 2556 } else { 2557 if (listener != null) { 2558 listener.failed(); 2559 } 2560 throw new FTPDataTransferException( 2561 "I/O error in data transfer", e); 2562 } 2563 } 2564 } finally { 2565 // Stop nooping. 2566 nooper.interrupt(); 2567 for (;;) { 2568 try { 2569 nooper.join(); 2570 break; 2571 } catch (InterruptedException e) { 2572 continue; 2573 } 2574 } 2575 // Closing stream and data connection. 2576 if (dataTransferInputStream != null) { 2577 try { 2578 dataTransferInputStream.close(); 2579 } catch (Throwable t) { 2580 ; 2581 } 2582 } 2583 try { 2584 dtConnection.close(); 2585 } catch (Throwable t) { 2586 ; 2587 } 2588 // Set to null the instance-level input stream. 2589 dataTransferInputStream = null; 2590 // Consume the result reply of the transfer. 2591 communication.readFTPReply(); 2592 // Change the operation status. 2593 synchronized (abortLock) { 2594 ongoingDataTransfer = false; 2595 aborted = false; 2596 } 2597 } 2598 if (listener != null) { 2599 listener.completed(); 2600 } 2601 } 2602 } 2603 2604 /** 2605 * This method detects the type for a file transfer. 2606 */ 2607 private int detectType(String fileName) throws IOException, 2608 FTPIllegalReplyException, FTPException { 2609 int start = fileName.lastIndexOf('.') + 1; 2610 int stop = fileName.length(); 2611 if (start > 0 && start < stop - 1) { 2612 String ext = fileName.substring(start, stop); 2613 ext = ext.toLowerCase(); 2614 if (textualExtensionRecognizer.isTextualExt(ext)) { 2615 return TYPE_TEXTUAL; 2616 } else { 2617 return TYPE_BINARY; 2618 } 2619 } else { 2620 return TYPE_BINARY; 2621 } 2622 } 2623 2624 /** 2625 * This method opens a data transfer channel in active mode. 2626 */ 2627 private FTPDataTransferConnectionProvider openActiveDataTransferChannel() 2628 throws IOException, FTPIllegalReplyException, FTPException, 2629 FTPDataTransferException { 2630 // Create a FTPDataTransferServer object. 2631 FTPDataTransferServer server = new FTPDataTransferServer(); 2632 int port = server.getPort(); 2633 int p1 = port >>> 8; 2634 int p2 = port & 0xff; 2635 int[] addr = pickLocalAddress(); 2636 // Send the port command. 2637 communication.sendFTPCommand("PORT " + addr[0] + "," + addr[1] + "," 2638 + addr[2] + "," + addr[3] + "," + p1 + "," + p2); 2639 FTPReply r = communication.readFTPReply(); 2640 if (!r.isSuccessCode()) { 2641 // Disposes. 2642 server.dispose(); 2643 // Closes the already open connection (if any). 2644 try { 2645 FTPConnection aux = server.openDataTransferConnection(); 2646 aux.close(); 2647 } catch (Throwable t) { 2648 ; 2649 } 2650 // Throws the exception. 2651 throw new FTPException(r); 2652 } 2653 return server; 2654 } 2655 2656 /** 2657 * This method opens a data transfer channel in passive mode. 2658 */ 2659 private FTPDataTransferConnectionProvider openPassiveDataTransferChannel() 2660 throws IOException, FTPIllegalReplyException, FTPException, 2661 FTPDataTransferException { 2662 // Send the PASV command. 2663 communication.sendFTPCommand("PASV"); 2664 // Read the reply. 2665 FTPReply r = communication.readFTPReply(); 2666 if (!r.isSuccessCode()) { 2667 throw new FTPException(r); 2668 } 2669 // Use a regexp to extract the remote address and port. 2670 String addressAndPort = null; 2671 String[] messages = r.getMessages(); 2672 for (int i = 0; i < messages.length; i++) { 2673 Matcher m = PASV_PATTERN.matcher(messages[i]); 2674 if (m.find()) { 2675 int start = m.start(); 2676 int end = m.end(); 2677 addressAndPort = messages[i].substring(start, end); 2678 break; 2679 } 2680 } 2681 if (addressAndPort == null) { 2682 // The remote server has not sent the coordinates for the 2683 // data transfer connection. 2684 throw new FTPIllegalReplyException(); 2685 } 2686 // Parse the string extracted from the reply. 2687 StringTokenizer st = new StringTokenizer(addressAndPort, ","); 2688 int b1 = Integer.parseInt(st.nextToken()); 2689 int b2 = Integer.parseInt(st.nextToken()); 2690 int b3 = Integer.parseInt(st.nextToken()); 2691 int b4 = Integer.parseInt(st.nextToken()); 2692 int p1 = Integer.parseInt(st.nextToken()); 2693 int p2 = Integer.parseInt(st.nextToken()); 2694 final InetAddress remoteAddress = InetAddress.getByAddress(new byte[] { 2695 (byte) b1, (byte) b2, (byte) b3, (byte) b4 }); 2696 final int remotePort = (p1 << 8) | p2; 2697 FTPDataTransferConnectionProvider provider = new FTPDataTransferConnectionProvider() { 2698 2699 public FTPConnection openDataTransferConnection() 2700 throws FTPDataTransferException { 2701 // Establish the connection. 2702 FTPConnection dtConnection; 2703 try { 2704 dtConnection = connector.connectForDataTransferChannel( 2705 remoteAddress.getHostAddress(), remotePort); 2706 } catch (IOException e) { 2707 throw new FTPDataTransferException( 2708 "Cannot connect to the remote server", e); 2709 } 2710 return dtConnection; 2711 } 2712 2713 public void dispose() { 2714 // nothing to do 2715 } 2716 2717 }; 2718 return provider; 2719 } 2720 2721 /** 2722 * If there's any ongoing data transfer operation, this method aborts it. 2723 * 2724 * @param sendAborCommand 2725 * If true the client will negotiate the abort procedure with the 2726 * server, through the standard FTP ABOR command. Otherwise the 2727 * open data transfer connection will be closed without any 2728 * advise has sent to the server. 2729 * @throws IOException 2730 * If the ABOR command cannot be sent due to any I/O error. This 2731 * could happen only if force is false. 2732 * @throws FTPIllegalReplyException 2733 * If the server reply to the ABOR command is illegal. This 2734 * could happen only if force is false. 2735 */ 2736 public void abortCurrentDataTransfer(boolean sendAborCommand) 2737 throws IOException, FTPIllegalReplyException { 2738 synchronized (abortLock) { 2739 if (ongoingDataTransfer && !aborted) { 2740 if (sendAborCommand) { 2741 communication.sendFTPCommand("ABOR"); 2742 communication.readFTPReply(); 2743 } 2744 if (dataTransferInputStream != null) { 2745 try { 2746 dataTransferInputStream.close(); 2747 } catch (Throwable t) { 2748 ; 2749 } 2750 } 2751 if (dataTransferOutputStream != null) { 2752 try { 2753 dataTransferOutputStream.close(); 2754 } catch (Throwable t) { 2755 ; 2756 } 2757 } 2758 aborted = true; 2759 } 2760 } 2761 } 2762 2763 /** 2764 * Returns the name of the charset that should be used in textual 2765 * transmissions. 2766 * 2767 * @return The name of the charset that should be used in textual 2768 * transmissions. 2769 */ 2770 private String pickCharset() { 2771 if (charset != null) { 2772 return charset; 2773 } else if (utf8Supported) { 2774 return "UTF-8"; 2775 } else { 2776 return System.getProperty("file.encoding"); 2777 } 2778 } 2779 2780 /** 2781 * Picks the local address for an active data transfer operation. 2782 * 2783 * @return The local address as a 4 integer values array. 2784 * @throws IOException 2785 * If an unexpected I/O error occurs while trying to resolve the 2786 * local address. 2787 */ 2788 private int[] pickLocalAddress() throws IOException { 2789 // Forced address? 2790 int[] ret = pickForcedLocalAddress(); 2791 // Auto-detect? 2792 if (ret == null) { 2793 ret = pickAutoDetectedLocalAddress(); 2794 } 2795 // Returns. 2796 return ret; 2797 } 2798 2799 /** 2800 * If a local address for active data transfers has been supplied through 2801 * the {@link FTPKeys#ACTIVE_DT_HOST_ADDRESS}, it returns it as a 4 elements 2802 * integer array; otherwise it returns null. 2803 * 2804 * @return The forced local address, or null. 2805 */ 2806 private int[] pickForcedLocalAddress() { 2807 int[] ret = null; 2808 String aux = System.getProperty(FTPKeys.ACTIVE_DT_HOST_ADDRESS); 2809 if (aux != null) { 2810 boolean valid = false; 2811 StringTokenizer st = new StringTokenizer(aux, "."); 2812 if (st.countTokens() == 4) { 2813 valid = true; 2814 int[] arr = new int[4]; 2815 for (int i = 0; i < 4; i++) { 2816 String tk = st.nextToken(); 2817 try { 2818 arr[i] = Integer.parseInt(tk); 2819 } catch (NumberFormatException e) { 2820 arr[i] = -1; 2821 } 2822 if (arr[i] < 0 || arr[i] > 255) { 2823 valid = false; 2824 break; 2825 } 2826 } 2827 if (valid) { 2828 ret = arr; 2829 } 2830 } 2831 if (!valid) { 2832 // warning to the developer 2833 System.err.println("WARNING: invalid value \"" + aux 2834 + "\" for the " + FTPKeys.ACTIVE_DT_HOST_ADDRESS 2835 + " system property. The value should " 2836 + "be in the x.x.x.x form."); 2837 } 2838 } 2839 return ret; 2840 } 2841 2842 /** 2843 * Auto-detects the local network address, and returns it in the form of a 4 2844 * elements integer array. 2845 * 2846 * @return The detected local address. 2847 * @throws IOException 2848 * If an unexpected I/O error occurs while trying to resolve the 2849 * local address. 2850 */ 2851 private int[] pickAutoDetectedLocalAddress() throws IOException { 2852 InetAddress addressObj = InetAddress.getLocalHost(); 2853 byte[] addr = addressObj.getAddress(); 2854 int b1 = addr[0] & 0xff; 2855 int b2 = addr[1] & 0xff; 2856 int b3 = addr[2] & 0xff; 2857 int b4 = addr[3] & 0xff; 2858 int[] ret = { b1, b2, b3, b4 }; 2859 return ret; 2860 } 2861 2862 public String toString() { 2863 synchronized (lock) { 2864 StringBuffer buffer = new StringBuffer(); 2865 buffer.append(getClass().getName()); 2866 buffer.append(" [connected="); 2867 buffer.append(connected); 2868 if (connected) { 2869 buffer.append(", host="); 2870 buffer.append(host); 2871 buffer.append(", port="); 2872 buffer.append(port); 2873 } 2874 buffer.append(", authenticated="); 2875 buffer.append(authenticated); 2876 if (authenticated) { 2877 buffer.append(", username="); 2878 buffer.append(username); 2879 buffer.append(", password="); 2880 StringBuffer buffer2 = new StringBuffer(); 2881 for (int i = 0; i < password.length(); i++) { 2882 buffer2.append('*'); 2883 } 2884 buffer.append(buffer2); 2885 } 2886 buffer.append(", transfer mode="); 2887 buffer.append(passive ? "passive" : "active"); 2888 buffer.append(", transfer type="); 2889 switch (type) { 2890 case TYPE_AUTO: 2891 buffer.append("auto"); 2892 break; 2893 case TYPE_BINARY: 2894 buffer.append("binary"); 2895 break; 2896 case TYPE_TEXTUAL: 2897 buffer.append("textual"); 2898 break; 2899 } 2900 buffer.append(", connector="); 2901 buffer.append(connector); 2902 buffer.append(", textualExtensionRecognizer="); 2903 buffer.append(textualExtensionRecognizer); 2904 FTPListParser[] listParsers = getListParsers(); 2905 if (listParsers.length > 0) { 2906 buffer.append(", listParsers="); 2907 for (int i = 0; i < listParsers.length; i++) { 2908 if (i > 0) { 2909 buffer.append(", "); 2910 } 2911 buffer.append(listParsers[i]); 2912 } 2913 } 2914 FTPCommunicationListener[] communicationListeners = getCommunicationListeners(); 2915 if (communicationListeners.length > 0) { 2916 buffer.append(", communicationListeners="); 2917 for (int i = 0; i < communicationListeners.length; i++) { 2918 if (i > 0) { 2919 buffer.append(", "); 2920 } 2921 buffer.append(communicationListeners[i]); 2922 } 2923 } 2924 buffer.append("]"); 2925 return buffer.toString(); 2926 } 2927 } 2928 2929 /** 2930 * A NOOPer thread used during long data trasnfers. 2931 */ 2932 private class NOOPer extends Thread { 2933 2934 public void run() { 2935 long delay; 2936 try { 2937 String aux = System 2938 .getProperty(FTPKeys.DT_AUTO_NOOP_DELAY, "0"); 2939 delay = Long.parseLong(aux); 2940 } catch (NumberFormatException e) { 2941 delay = 0; 2942 } 2943 if (delay > 0) { 2944 while (!Thread.interrupted()) { 2945 // Sleep. 2946 try { 2947 Thread.sleep(delay); 2948 } catch (InterruptedException e) { 2949 break; 2950 } 2951 // Send NOOP. 2952 try { 2953 communication.sendFTPCommand("NOOP"); 2954 communication.readFTPReply(); 2955 } catch (Throwable t) { 2956 ; 2957 } 2958 } 2959 } 2960 } 2961 2962 } 2963 2964 }