| Guido van Rossum | 4b8c6ea | 2000-02-04 15:39:30 +0000 | [diff] [blame] | 1 | """An FTP client class and some helper functions. | 
 | 2 |  | 
| Barry Warsaw | 100d81e | 2000-09-01 06:09:23 +0000 | [diff] [blame] | 3 | Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 4 |  | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 5 | Example: | 
 | 6 |  | 
 | 7 | >>> from ftplib import FTP | 
 | 8 | >>> ftp = FTP('ftp.python.org') # connect to host, default port | 
| Guido van Rossum | 24a6434 | 2001-12-28 20:54:55 +0000 | [diff] [blame] | 9 | >>> ftp.login() # default, i.e.: user anonymous, passwd anonymous@ | 
| Guido van Rossum | 2f3941d | 1997-10-07 14:49:56 +0000 | [diff] [blame] | 10 | '230 Guest login ok, access restrictions apply.' | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 11 | >>> ftp.retrlines('LIST') # list directory contents | 
 | 12 | total 9 | 
 | 13 | drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 . | 
 | 14 | drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 .. | 
 | 15 | drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 bin | 
 | 16 | drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 etc | 
 | 17 | d-wxrwxr-x   2 ftp      wheel        1024 Sep  5 13:43 incoming | 
 | 18 | drwxr-xr-x   2 root     wheel        1024 Nov 17  1993 lib | 
 | 19 | drwxr-xr-x   6 1094     wheel        1024 Sep 13 19:07 pub | 
 | 20 | drwxr-xr-x   3 root     wheel        1024 Jan  3  1994 usr | 
 | 21 | -rw-r--r--   1 root     root          312 Aug  1  1994 welcome.msg | 
| Guido van Rossum | 2f3941d | 1997-10-07 14:49:56 +0000 | [diff] [blame] | 22 | '226 Transfer complete.' | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 23 | >>> ftp.quit() | 
| Guido van Rossum | 2f3941d | 1997-10-07 14:49:56 +0000 | [diff] [blame] | 24 | '221 Goodbye.' | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 25 | >>> | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 26 |  | 
 | 27 | A nice test that reveals some of the network dialogue would be: | 
 | 28 | python ftplib.py -d localhost -l -p -l | 
| Guido van Rossum | 4b8c6ea | 2000-02-04 15:39:30 +0000 | [diff] [blame] | 29 | """ | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 30 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 31 | # | 
| Guido van Rossum | 98d9fd3 | 2000-02-28 15:12:25 +0000 | [diff] [blame] | 32 | # Changes and improvements suggested by Steve Majewski. | 
 | 33 | # Modified by Jack to work on the mac. | 
 | 34 | # Modified by Siebren to support docstrings and PASV. | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 35 | # Modified by Phil Schwartz to add storbinary and storlines callbacks. | 
| Guido van Rossum | 98d9fd3 | 2000-02-28 15:12:25 +0000 | [diff] [blame] | 36 | # | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 37 |  | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 38 | import os | 
 | 39 | import sys | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 40 |  | 
| Guido van Rossum | b6775db | 1994-08-01 11:34:53 +0000 | [diff] [blame] | 41 | # Import SOCKS module if it exists, else standard socket module socket | 
 | 42 | try: | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 43 |     import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket | 
 | 44 |     from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn | 
| Guido van Rossum | b6775db | 1994-08-01 11:34:53 +0000 | [diff] [blame] | 45 | except ImportError: | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 46 |     import socket | 
| Facundo Batista | 4f1b1ed | 2008-05-29 16:39:26 +0000 | [diff] [blame] | 47 | from socket import _GLOBAL_DEFAULT_TIMEOUT | 
| Guido van Rossum | b6775db | 1994-08-01 11:34:53 +0000 | [diff] [blame] | 48 |  | 
| Skip Montanaro | eccd02a | 2001-01-20 23:34:12 +0000 | [diff] [blame] | 49 | __all__ = ["FTP","Netrc"] | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 50 |  | 
| Guido van Rossum | d316607 | 1993-05-24 14:16:22 +0000 | [diff] [blame] | 51 | # Magic number from <socket.h> | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 52 | MSG_OOB = 0x1                           # Process data out of band | 
| Guido van Rossum | d316607 | 1993-05-24 14:16:22 +0000 | [diff] [blame] | 53 |  | 
 | 54 |  | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 55 | # The standard FTP server control port | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 56 | FTP_PORT = 21 | 
| Barry Warsaw | d6fddf3 | 2013-09-25 09:36:58 -0400 | [diff] [blame] | 57 | # The sizehint parameter passed to readline() calls | 
 | 58 | MAXLINE = 8192 | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 59 |  | 
 | 60 |  | 
| Guido van Rossum | 2197479 | 1992-11-06 13:34:17 +0000 | [diff] [blame] | 61 | # Exception raised when an error or invalid response is received | 
| Fred Drake | 227b120 | 2000-08-17 05:06:49 +0000 | [diff] [blame] | 62 | class Error(Exception): pass | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 63 | class error_reply(Error): pass          # unexpected [123]xx reply | 
 | 64 | class error_temp(Error): pass           # 4xx errors | 
 | 65 | class error_perm(Error): pass           # 5xx errors | 
 | 66 | class error_proto(Error): pass          # response does not begin with [1-5] | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 67 |  | 
 | 68 |  | 
| Guido van Rossum | 2197479 | 1992-11-06 13:34:17 +0000 | [diff] [blame] | 69 | # All exceptions (hopefully) that may be raised here and that aren't | 
 | 70 | # (always) programming errors on our side | 
| Gregory P. Smith | e6c0303 | 2008-04-12 22:24:04 +0000 | [diff] [blame] | 71 | all_errors = (Error, IOError, EOFError) | 
| Guido van Rossum | 2197479 | 1992-11-06 13:34:17 +0000 | [diff] [blame] | 72 |  | 
 | 73 |  | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 74 | # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF) | 
 | 75 | CRLF = '\r\n' | 
 | 76 |  | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 77 | # The class itself | 
 | 78 | class FTP: | 
 | 79 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 80 |     '''An FTP client class. | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 81 |  | 
| Facundo Batista | 3f10099 | 2007-03-26 20:56:09 +0000 | [diff] [blame] | 82 |     To create a connection, call the class using these arguments: | 
 | 83 |             host, user, passwd, acct, timeout | 
 | 84 |  | 
 | 85 |     The first four arguments are all strings, and have default value ''. | 
 | 86 |     timeout must be numeric and defaults to None if not passed, | 
 | 87 |     meaning that no timeout will be set on any ftp socket(s) | 
 | 88 |     If a timeout is passed, then this is now the default timeout for all ftp | 
 | 89 |     socket operations for this instance. | 
| Neal Norwitz | 0d4c06e | 2007-04-25 06:30:05 +0000 | [diff] [blame] | 90 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 91 |     Then use self.connect() with optional host and port argument. | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 92 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 93 |     To download a file, use ftp.retrlines('RETR ' + filename), | 
 | 94 |     or ftp.retrbinary() with slightly different arguments. | 
 | 95 |     To upload a file, use ftp.storlines() or ftp.storbinary(), | 
 | 96 |     which have an open file as argument (see their definitions | 
 | 97 |     below for details). | 
 | 98 |     The download/upload functions first issue appropriate TYPE | 
 | 99 |     and PORT or PASV commands. | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 100 | ''' | 
 | 101 |  | 
| Fred Drake | 9c98a42 | 2001-02-28 21:46:37 +0000 | [diff] [blame] | 102 |     debugging = 0 | 
 | 103 |     host = '' | 
 | 104 |     port = FTP_PORT | 
| Barry Warsaw | d6fddf3 | 2013-09-25 09:36:58 -0400 | [diff] [blame] | 105 |     maxline = MAXLINE | 
| Fred Drake | 9c98a42 | 2001-02-28 21:46:37 +0000 | [diff] [blame] | 106 |     sock = None | 
 | 107 |     file = None | 
 | 108 |     welcome = None | 
 | 109 |     passiveserver = 1 | 
 | 110 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 111 |     # Initialization method (called by class instantiation). | 
 | 112 |     # Initialize host to localhost, port to standard ftp port | 
 | 113 |     # Optional arguments are host (for connect()), | 
 | 114 |     # and user, passwd, acct (for login()) | 
| Facundo Batista | 4f1b1ed | 2008-05-29 16:39:26 +0000 | [diff] [blame] | 115 |     def __init__(self, host='', user='', passwd='', acct='', | 
 | 116 |                  timeout=_GLOBAL_DEFAULT_TIMEOUT): | 
| Facundo Batista | 3f10099 | 2007-03-26 20:56:09 +0000 | [diff] [blame] | 117 |         self.timeout = timeout | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 118 |         if host: | 
| Fred Drake | 9c98a42 | 2001-02-28 21:46:37 +0000 | [diff] [blame] | 119 |             self.connect(host) | 
| Neal Norwitz | 0d4c06e | 2007-04-25 06:30:05 +0000 | [diff] [blame] | 120 |             if user: | 
| Facundo Batista | 3f10099 | 2007-03-26 20:56:09 +0000 | [diff] [blame] | 121 |                 self.login(user, passwd, acct) | 
| Guido van Rossum | 52fc1f6 | 1993-06-17 12:38:10 +0000 | [diff] [blame] | 122 |  | 
| Facundo Batista | 4f1b1ed | 2008-05-29 16:39:26 +0000 | [diff] [blame] | 123 |     def connect(self, host='', port=0, timeout=-999): | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 124 |         '''Connect to host.  Arguments are: | 
| Facundo Batista | 3f10099 | 2007-03-26 20:56:09 +0000 | [diff] [blame] | 125 |          - host: hostname to connect to (string, default previous host) | 
 | 126 |          - port: port to connect to (integer, default previous port) | 
 | 127 |         ''' | 
 | 128 |         if host != '': | 
 | 129 |             self.host = host | 
 | 130 |         if port > 0: | 
 | 131 |             self.port = port | 
| Facundo Batista | 4f1b1ed | 2008-05-29 16:39:26 +0000 | [diff] [blame] | 132 |         if timeout != -999: | 
| Facundo Batista | 93c3368 | 2007-03-30 13:00:35 +0000 | [diff] [blame] | 133 |             self.timeout = timeout | 
| Facundo Batista | 3f10099 | 2007-03-26 20:56:09 +0000 | [diff] [blame] | 134 |         self.sock = socket.create_connection((self.host, self.port), self.timeout) | 
 | 135 |         self.af = self.sock.family | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 136 |         self.file = self.sock.makefile('rb') | 
 | 137 |         self.welcome = self.getresp() | 
 | 138 |         return self.welcome | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 139 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 140 |     def getwelcome(self): | 
 | 141 |         '''Get the welcome message from the server. | 
 | 142 |         (this is read and squirreled away by connect())''' | 
 | 143 |         if self.debugging: | 
 | 144 |             print '*welcome*', self.sanitize(self.welcome) | 
 | 145 |         return self.welcome | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 146 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 147 |     def set_debuglevel(self, level): | 
 | 148 |         '''Set the debugging level. | 
 | 149 |         The required argument level means: | 
 | 150 |         0: no debugging output (default) | 
 | 151 |         1: print commands and responses but not body text etc. | 
 | 152 |         2: also print raw lines read and sent before stripping CR/LF''' | 
 | 153 |         self.debugging = level | 
 | 154 |     debug = set_debuglevel | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 155 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 156 |     def set_pasv(self, val): | 
 | 157 |         '''Use passive or active mode for data transfers. | 
 | 158 |         With a false argument, use the normal PORT mode, | 
 | 159 |         With a true argument, use the PASV command.''' | 
 | 160 |         self.passiveserver = val | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 161 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 162 |     # Internal: "sanitize" a string for printing | 
 | 163 |     def sanitize(self, s): | 
 | 164 |         if s[:5] == 'pass ' or s[:5] == 'PASS ': | 
 | 165 |             i = len(s) | 
 | 166 |             while i > 5 and s[i-1] in '\r\n': | 
 | 167 |                 i = i-1 | 
 | 168 |             s = s[:5] + '*'*(i-5) + s[i:] | 
| Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 169 |         return repr(s) | 
| Guido van Rossum | ebaf104 | 1995-05-05 15:54:14 +0000 | [diff] [blame] | 170 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 171 |     # Internal: send one line to the server, appending CRLF | 
 | 172 |     def putline(self, line): | 
 | 173 |         line = line + CRLF | 
 | 174 |         if self.debugging > 1: print '*put*', self.sanitize(line) | 
| Martin v. Löwis | e12454f | 2002-02-16 23:06:19 +0000 | [diff] [blame] | 175 |         self.sock.sendall(line) | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 176 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 177 |     # Internal: send one command to the server (through putline()) | 
 | 178 |     def putcmd(self, line): | 
 | 179 |         if self.debugging: print '*cmd*', self.sanitize(line) | 
 | 180 |         self.putline(line) | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 181 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 182 |     # Internal: return one line from the server, stripping CRLF. | 
 | 183 |     # Raise EOFError if the connection is closed | 
 | 184 |     def getline(self): | 
| Barry Warsaw | d6fddf3 | 2013-09-25 09:36:58 -0400 | [diff] [blame] | 185 |         line = self.file.readline(self.maxline + 1) | 
 | 186 |         if len(line) > self.maxline: | 
 | 187 |             raise Error("got more than %d bytes" % self.maxline) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 188 |         if self.debugging > 1: | 
 | 189 |             print '*get*', self.sanitize(line) | 
 | 190 |         if not line: raise EOFError | 
 | 191 |         if line[-2:] == CRLF: line = line[:-2] | 
 | 192 |         elif line[-1:] in CRLF: line = line[:-1] | 
 | 193 |         return line | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 194 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 195 |     # Internal: get a response from the server, which may possibly | 
 | 196 |     # consist of multiple lines.  Return a single string with no | 
 | 197 |     # trailing CRLF.  If the response consists of multiple lines, | 
 | 198 |     # these are separated by '\n' characters in the string | 
 | 199 |     def getmultiline(self): | 
 | 200 |         line = self.getline() | 
 | 201 |         if line[3:4] == '-': | 
 | 202 |             code = line[:3] | 
 | 203 |             while 1: | 
 | 204 |                 nextline = self.getline() | 
 | 205 |                 line = line + ('\n' + nextline) | 
 | 206 |                 if nextline[:3] == code and \ | 
 | 207 |                         nextline[3:4] != '-': | 
 | 208 |                     break | 
 | 209 |         return line | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 210 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 211 |     # Internal: get a response from the server. | 
 | 212 |     # Raise various errors if the response indicates an error | 
 | 213 |     def getresp(self): | 
 | 214 |         resp = self.getmultiline() | 
 | 215 |         if self.debugging: print '*resp*', self.sanitize(resp) | 
 | 216 |         self.lastresp = resp[:3] | 
 | 217 |         c = resp[:1] | 
| Raymond Hettinger | c88a6c7 | 2005-04-05 04:31:09 +0000 | [diff] [blame] | 218 |         if c in ('1', '2', '3'): | 
 | 219 |             return resp | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 220 |         if c == '4': | 
 | 221 |             raise error_temp, resp | 
 | 222 |         if c == '5': | 
 | 223 |             raise error_perm, resp | 
| Raymond Hettinger | c88a6c7 | 2005-04-05 04:31:09 +0000 | [diff] [blame] | 224 |         raise error_proto, resp | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 225 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 226 |     def voidresp(self): | 
 | 227 |         """Expect a response beginning with '2'.""" | 
 | 228 |         resp = self.getresp() | 
| Georg Brandl | f7a1efc | 2009-04-05 10:51:10 +0000 | [diff] [blame] | 229 |         if resp[:1] != '2': | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 230 |             raise error_reply, resp | 
 | 231 |         return resp | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 232 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 233 |     def abort(self): | 
 | 234 |         '''Abort a file transfer.  Uses out-of-band data. | 
 | 235 |         This does not follow the procedure from the RFC to send Telnet | 
 | 236 |         IP and Synch; that doesn't seem to work with the servers I've | 
 | 237 |         tried.  Instead, just send the ABOR command as OOB data.''' | 
 | 238 |         line = 'ABOR' + CRLF | 
 | 239 |         if self.debugging > 1: print '*put urgent*', self.sanitize(line) | 
| Martin v. Löwis | e12454f | 2002-02-16 23:06:19 +0000 | [diff] [blame] | 240 |         self.sock.sendall(line, MSG_OOB) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 241 |         resp = self.getmultiline() | 
| Giampaolo Rodolà | fa1520a | 2010-04-18 13:13:54 +0000 | [diff] [blame] | 242 |         if resp[:3] not in ('426', '225', '226'): | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 243 |             raise error_proto, resp | 
| Guido van Rossum | d316607 | 1993-05-24 14:16:22 +0000 | [diff] [blame] | 244 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 245 |     def sendcmd(self, cmd): | 
 | 246 |         '''Send a command and return the response.''' | 
 | 247 |         self.putcmd(cmd) | 
 | 248 |         return self.getresp() | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 249 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 250 |     def voidcmd(self, cmd): | 
 | 251 |         """Send a command and expect a response beginning with '2'.""" | 
 | 252 |         self.putcmd(cmd) | 
 | 253 |         return self.voidresp() | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 254 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 255 |     def sendport(self, host, port): | 
 | 256 |         '''Send a PORT command with the current host and the given | 
 | 257 |         port number. | 
 | 258 |         ''' | 
| Eric S. Raymond | c95bf69 | 2001-02-09 10:06:47 +0000 | [diff] [blame] | 259 |         hbytes = host.split('.') | 
| Benjamin Peterson | dee0b17 | 2008-09-27 22:08:12 +0000 | [diff] [blame] | 260 |         pbytes = [repr(port//256), repr(port%256)] | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 261 |         bytes = hbytes + pbytes | 
| Eric S. Raymond | c95bf69 | 2001-02-09 10:06:47 +0000 | [diff] [blame] | 262 |         cmd = 'PORT ' + ','.join(bytes) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 263 |         return self.voidcmd(cmd) | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 264 |  | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 265 |     def sendeprt(self, host, port): | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 266 |         '''Send a EPRT command with the current host and the given port number.''' | 
 | 267 |         af = 0 | 
 | 268 |         if self.af == socket.AF_INET: | 
 | 269 |             af = 1 | 
 | 270 |         if self.af == socket.AF_INET6: | 
 | 271 |             af = 2 | 
 | 272 |         if af == 0: | 
 | 273 |             raise error_proto, 'unsupported address family' | 
| Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 274 |         fields = ['', repr(af), host, repr(port), ''] | 
| Neal Norwitz | 7ce734c | 2002-05-31 14:13:04 +0000 | [diff] [blame] | 275 |         cmd = 'EPRT ' + '|'.join(fields) | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 276 |         return self.voidcmd(cmd) | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 277 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 278 |     def makeport(self): | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 279 |         '''Create a new socket and send a PORT command for it.''' | 
| Martin v. Löwis | 2ad2569 | 2001-07-31 08:40:21 +0000 | [diff] [blame] | 280 |         msg = "getaddrinfo returns an empty list" | 
| Martin v. Löwis | 322c0d1 | 2001-10-07 08:53:32 +0000 | [diff] [blame] | 281 |         sock = None | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 282 |         for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): | 
 | 283 |             af, socktype, proto, canonname, sa = res | 
 | 284 |             try: | 
 | 285 |                 sock = socket.socket(af, socktype, proto) | 
 | 286 |                 sock.bind(sa) | 
 | 287 |             except socket.error, msg: | 
| Martin v. Löwis | 322c0d1 | 2001-10-07 08:53:32 +0000 | [diff] [blame] | 288 |                 if sock: | 
 | 289 |                     sock.close() | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 290 |                 sock = None | 
 | 291 |                 continue | 
 | 292 |             break | 
 | 293 |         if not sock: | 
 | 294 |             raise socket.error, msg | 
 | 295 |         sock.listen(1) | 
 | 296 |         port = sock.getsockname()[1] # Get proper port | 
 | 297 |         host = self.sock.getsockname()[0] # Get proper host | 
 | 298 |         if self.af == socket.AF_INET: | 
 | 299 |             resp = self.sendport(host, port) | 
 | 300 |         else: | 
 | 301 |             resp = self.sendeprt(host, port) | 
| Giampaolo Rodolà | a2eb7f6 | 2010-04-19 21:56:45 +0000 | [diff] [blame] | 302 |         if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT: | 
 | 303 |             sock.settimeout(self.timeout) | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 304 |         return sock | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 305 |  | 
 | 306 |     def makepasv(self): | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 307 |         if self.af == socket.AF_INET: | 
 | 308 |             host, port = parse227(self.sendcmd('PASV')) | 
 | 309 |         else: | 
 | 310 |             host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername()) | 
 | 311 |         return host, port | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 312 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 313 |     def ntransfercmd(self, cmd, rest=None): | 
 | 314 |         """Initiate a transfer over the data connection. | 
| Barry Warsaw | 100d81e | 2000-09-01 06:09:23 +0000 | [diff] [blame] | 315 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 316 |         If the transfer is active, send a port command and the | 
 | 317 |         transfer command, and accept the connection.  If the server is | 
 | 318 |         passive, send a pasv command, connect to it, and start the | 
 | 319 |         transfer command.  Either way, return the socket for the | 
 | 320 |         connection and the expected size of the transfer.  The | 
 | 321 |         expected size may be None if it could not be determined. | 
| Barry Warsaw | 100d81e | 2000-09-01 06:09:23 +0000 | [diff] [blame] | 322 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 323 |         Optional `rest' argument can be a string that is sent as the | 
| Gregory P. Smith | 2230bcf | 2008-01-22 23:15:34 +0000 | [diff] [blame] | 324 |         argument to a REST command.  This is essentially a server | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 325 |         marker used to tell the server to skip over any data up to the | 
 | 326 |         given marker. | 
 | 327 |         """ | 
 | 328 |         size = None | 
 | 329 |         if self.passiveserver: | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 330 |             host, port = self.makepasv() | 
| Facundo Batista | 9249312 | 2007-06-06 15:13:37 +0000 | [diff] [blame] | 331 |             conn = socket.create_connection((host, port), self.timeout) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 332 |             if rest is not None: | 
 | 333 |                 self.sendcmd("REST %s" % rest) | 
 | 334 |             resp = self.sendcmd(cmd) | 
| Martin v. Löwis | 36cbc08 | 2006-11-12 18:48:13 +0000 | [diff] [blame] | 335 |             # Some servers apparently send a 200 reply to | 
 | 336 |             # a LIST or STOR command, before the 150 reply | 
 | 337 |             # (and way before the 226 reply). This seems to | 
 | 338 |             # be in violation of the protocol (which only allows | 
 | 339 |             # 1xx or error messages for LIST), so we just discard | 
 | 340 |             # this response. | 
 | 341 |             if resp[0] == '2': | 
| Tim Peters | f733abb | 2007-01-30 03:03:46 +0000 | [diff] [blame] | 342 |                 resp = self.getresp() | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 343 |             if resp[0] != '1': | 
 | 344 |                 raise error_reply, resp | 
 | 345 |         else: | 
 | 346 |             sock = self.makeport() | 
 | 347 |             if rest is not None: | 
 | 348 |                 self.sendcmd("REST %s" % rest) | 
 | 349 |             resp = self.sendcmd(cmd) | 
| Martin v. Löwis | 36cbc08 | 2006-11-12 18:48:13 +0000 | [diff] [blame] | 350 |             # See above. | 
 | 351 |             if resp[0] == '2': | 
| Tim Peters | f733abb | 2007-01-30 03:03:46 +0000 | [diff] [blame] | 352 |                 resp = self.getresp() | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 353 |             if resp[0] != '1': | 
 | 354 |                 raise error_reply, resp | 
 | 355 |             conn, sockaddr = sock.accept() | 
| Giampaolo Rodolà | a2eb7f6 | 2010-04-19 21:56:45 +0000 | [diff] [blame] | 356 |             if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT: | 
 | 357 |                 conn.settimeout(self.timeout) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 358 |         if resp[:3] == '150': | 
 | 359 |             # this is conditional in case we received a 125 | 
 | 360 |             size = parse150(resp) | 
 | 361 |         return conn, size | 
| Fred Drake | 4de02d9 | 1997-01-10 18:26:09 +0000 | [diff] [blame] | 362 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 363 |     def transfercmd(self, cmd, rest=None): | 
| Guido van Rossum | b6aca6a | 2001-10-16 19:45:52 +0000 | [diff] [blame] | 364 |         """Like ntransfercmd() but returns only the socket.""" | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 365 |         return self.ntransfercmd(cmd, rest)[0] | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 366 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 367 |     def login(self, user = '', passwd = '', acct = ''): | 
 | 368 |         '''Login, default anonymous.''' | 
 | 369 |         if not user: user = 'anonymous' | 
 | 370 |         if not passwd: passwd = '' | 
 | 371 |         if not acct: acct = '' | 
 | 372 |         if user == 'anonymous' and passwd in ('', '-'): | 
| Tim Peters | e441860 | 2002-02-16 07:34:19 +0000 | [diff] [blame] | 373 |             # If there is no anonymous ftp password specified | 
 | 374 |             # then we'll just use anonymous@ | 
 | 375 |             # We don't send any other thing because: | 
 | 376 |             # - We want to remain anonymous | 
 | 377 |             # - We want to stop SPAM | 
 | 378 |             # - We don't want to let ftp sites to discriminate by the user, | 
 | 379 |             #   host or country. | 
| Guido van Rossum | c33e077 | 2001-12-28 20:54:28 +0000 | [diff] [blame] | 380 |             passwd = passwd + 'anonymous@' | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 381 |         resp = self.sendcmd('USER ' + user) | 
 | 382 |         if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd) | 
 | 383 |         if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct) | 
 | 384 |         if resp[0] != '2': | 
 | 385 |             raise error_reply, resp | 
 | 386 |         return resp | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 387 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 388 |     def retrbinary(self, cmd, callback, blocksize=8192, rest=None): | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 389 |         """Retrieve data in binary mode.  A new port is created for you. | 
| Barry Warsaw | 100d81e | 2000-09-01 06:09:23 +0000 | [diff] [blame] | 390 |  | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 391 |         Args: | 
 | 392 |           cmd: A RETR command. | 
 | 393 |           callback: A single parameter callable to be called on each | 
 | 394 |                     block of data read. | 
 | 395 |           blocksize: The maximum number of bytes to read from the | 
 | 396 |                      socket at one time.  [default: 8192] | 
 | 397 |           rest: Passed to transfercmd().  [default: None] | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 398 |  | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 399 |         Returns: | 
 | 400 |           The response code. | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 401 |         """ | 
 | 402 |         self.voidcmd('TYPE I') | 
 | 403 |         conn = self.transfercmd(cmd, rest) | 
 | 404 |         while 1: | 
 | 405 |             data = conn.recv(blocksize) | 
 | 406 |             if not data: | 
 | 407 |                 break | 
 | 408 |             callback(data) | 
 | 409 |         conn.close() | 
 | 410 |         return self.voidresp() | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 411 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 412 |     def retrlines(self, cmd, callback = None): | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 413 |         """Retrieve data in line mode.  A new port is created for you. | 
 | 414 |  | 
 | 415 |         Args: | 
| Gregory P. Smith | 2230bcf | 2008-01-22 23:15:34 +0000 | [diff] [blame] | 416 |           cmd: A RETR, LIST, NLST, or MLSD command. | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 417 |           callback: An optional single parameter callable that is called | 
 | 418 |                     for each line with the trailing CRLF stripped. | 
 | 419 |                     [default: print_line()] | 
 | 420 |  | 
 | 421 |         Returns: | 
 | 422 |           The response code. | 
 | 423 |         """ | 
| Raymond Hettinger | e874fc3 | 2002-05-12 05:53:51 +0000 | [diff] [blame] | 424 |         if callback is None: callback = print_line | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 425 |         resp = self.sendcmd('TYPE A') | 
 | 426 |         conn = self.transfercmd(cmd) | 
 | 427 |         fp = conn.makefile('rb') | 
 | 428 |         while 1: | 
| Barry Warsaw | d6fddf3 | 2013-09-25 09:36:58 -0400 | [diff] [blame] | 429 |             line = fp.readline(self.maxline + 1) | 
 | 430 |             if len(line) > self.maxline: | 
 | 431 |                 raise Error("got more than %d bytes" % self.maxline) | 
| Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 432 |             if self.debugging > 2: print '*retr*', repr(line) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 433 |             if not line: | 
 | 434 |                 break | 
 | 435 |             if line[-2:] == CRLF: | 
 | 436 |                 line = line[:-2] | 
 | 437 |             elif line[-1:] == '\n': | 
 | 438 |                 line = line[:-1] | 
 | 439 |             callback(line) | 
 | 440 |         fp.close() | 
 | 441 |         conn.close() | 
 | 442 |         return self.voidresp() | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 443 |  | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 444 |     def storbinary(self, cmd, fp, blocksize=8192, callback=None): | 
 | 445 |         """Store a file in binary mode.  A new port is created for you. | 
 | 446 |  | 
 | 447 |         Args: | 
 | 448 |           cmd: A STOR command. | 
 | 449 |           fp: A file-like object with a read(num_bytes) method. | 
 | 450 |           blocksize: The maximum data size to read from fp and send over | 
 | 451 |                      the connection at once.  [default: 8192] | 
 | 452 |           callback: An optional single parameter callable that is called on | 
 | 453 |                     on each block of data after it is sent.  [default: None] | 
 | 454 |  | 
 | 455 |         Returns: | 
 | 456 |           The response code. | 
 | 457 |         """ | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 458 |         self.voidcmd('TYPE I') | 
 | 459 |         conn = self.transfercmd(cmd) | 
 | 460 |         while 1: | 
 | 461 |             buf = fp.read(blocksize) | 
 | 462 |             if not buf: break | 
| Martin v. Löwis | e12454f | 2002-02-16 23:06:19 +0000 | [diff] [blame] | 463 |             conn.sendall(buf) | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 464 |             if callback: callback(buf) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 465 |         conn.close() | 
 | 466 |         return self.voidresp() | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 467 |  | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 468 |     def storlines(self, cmd, fp, callback=None): | 
 | 469 |         """Store a file in line mode.  A new port is created for you. | 
 | 470 |  | 
 | 471 |         Args: | 
 | 472 |           cmd: A STOR command. | 
 | 473 |           fp: A file-like object with a readline() method. | 
 | 474 |           callback: An optional single parameter callable that is called on | 
 | 475 |                     on each line after it is sent.  [default: None] | 
 | 476 |  | 
 | 477 |         Returns: | 
 | 478 |           The response code. | 
 | 479 |         """ | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 480 |         self.voidcmd('TYPE A') | 
 | 481 |         conn = self.transfercmd(cmd) | 
 | 482 |         while 1: | 
| Barry Warsaw | d6fddf3 | 2013-09-25 09:36:58 -0400 | [diff] [blame] | 483 |             buf = fp.readline(self.maxline + 1) | 
 | 484 |             if len(buf) > self.maxline: | 
 | 485 |                 raise Error("got more than %d bytes" % self.maxline) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 486 |             if not buf: break | 
 | 487 |             if buf[-2:] != CRLF: | 
 | 488 |                 if buf[-1] in CRLF: buf = buf[:-1] | 
 | 489 |                 buf = buf + CRLF | 
| Martin v. Löwis | e12454f | 2002-02-16 23:06:19 +0000 | [diff] [blame] | 490 |             conn.sendall(buf) | 
| Gregory P. Smith | c64386b | 2008-01-22 00:19:41 +0000 | [diff] [blame] | 491 |             if callback: callback(buf) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 492 |         conn.close() | 
 | 493 |         return self.voidresp() | 
| Guido van Rossum | 0eaa74b | 1996-01-25 18:37:21 +0000 | [diff] [blame] | 494 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 495 |     def acct(self, password): | 
 | 496 |         '''Send new account name.''' | 
 | 497 |         cmd = 'ACCT ' + password | 
 | 498 |         return self.voidcmd(cmd) | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 499 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 500 |     def nlst(self, *args): | 
 | 501 |         '''Return a list of files in a given directory (default the current).''' | 
 | 502 |         cmd = 'NLST' | 
 | 503 |         for arg in args: | 
 | 504 |             cmd = cmd + (' ' + arg) | 
 | 505 |         files = [] | 
 | 506 |         self.retrlines(cmd, files.append) | 
 | 507 |         return files | 
| Guido van Rossum | ae3b3a3 | 1993-11-30 13:43:54 +0000 | [diff] [blame] | 508 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 509 |     def dir(self, *args): | 
 | 510 |         '''List a directory in long form. | 
 | 511 |         By default list current directory to stdout. | 
 | 512 |         Optional last argument is callback function; all | 
 | 513 |         non-empty arguments before it are concatenated to the | 
 | 514 |         LIST command.  (This *should* only be used for a pathname.)''' | 
 | 515 |         cmd = 'LIST' | 
 | 516 |         func = None | 
 | 517 |         if args[-1:] and type(args[-1]) != type(''): | 
 | 518 |             args, func = args[:-1], args[-1] | 
 | 519 |         for arg in args: | 
 | 520 |             if arg: | 
 | 521 |                 cmd = cmd + (' ' + arg) | 
 | 522 |         self.retrlines(cmd, func) | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 523 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 524 |     def rename(self, fromname, toname): | 
 | 525 |         '''Rename a file.''' | 
 | 526 |         resp = self.sendcmd('RNFR ' + fromname) | 
 | 527 |         if resp[0] != '3': | 
 | 528 |             raise error_reply, resp | 
 | 529 |         return self.voidcmd('RNTO ' + toname) | 
| Guido van Rossum | a61bdeb | 1995-10-11 17:36:31 +0000 | [diff] [blame] | 530 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 531 |     def delete(self, filename): | 
 | 532 |         '''Delete a file.''' | 
 | 533 |         resp = self.sendcmd('DELE ' + filename) | 
 | 534 |         if resp[:3] in ('250', '200'): | 
 | 535 |             return resp | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 536 |         else: | 
 | 537 |             raise error_reply, resp | 
| Guido van Rossum | 02cf582 | 1993-05-17 08:00:02 +0000 | [diff] [blame] | 538 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 539 |     def cwd(self, dirname): | 
 | 540 |         '''Change to a directory.''' | 
 | 541 |         if dirname == '..': | 
 | 542 |             try: | 
 | 543 |                 return self.voidcmd('CDUP') | 
 | 544 |             except error_perm, msg: | 
| Martin v. Löwis | b525511 | 2002-03-10 15:59:58 +0000 | [diff] [blame] | 545 |                 if msg.args[0][:3] != '500': | 
 | 546 |                     raise | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 547 |         elif dirname == '': | 
 | 548 |             dirname = '.'  # does nothing, but could return error | 
 | 549 |         cmd = 'CWD ' + dirname | 
 | 550 |         return self.voidcmd(cmd) | 
| Guido van Rossum | 17ed1ae | 1993-06-01 13:21:04 +0000 | [diff] [blame] | 551 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 552 |     def size(self, filename): | 
 | 553 |         '''Retrieve the size of a file.''' | 
| Gregory P. Smith | 2230bcf | 2008-01-22 23:15:34 +0000 | [diff] [blame] | 554 |         # The SIZE command is defined in RFC-3659 | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 555 |         resp = self.sendcmd('SIZE ' + filename) | 
 | 556 |         if resp[:3] == '213': | 
| Guido van Rossum | b6aca6a | 2001-10-16 19:45:52 +0000 | [diff] [blame] | 557 |             s = resp[3:].strip() | 
 | 558 |             try: | 
 | 559 |                 return int(s) | 
| Guido van Rossum | 1f74cb3 | 2001-10-17 17:21:47 +0000 | [diff] [blame] | 560 |             except (OverflowError, ValueError): | 
| Guido van Rossum | b6aca6a | 2001-10-16 19:45:52 +0000 | [diff] [blame] | 561 |                 return long(s) | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 562 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 563 |     def mkd(self, dirname): | 
 | 564 |         '''Make a directory, return its full pathname.''' | 
 | 565 |         resp = self.sendcmd('MKD ' + dirname) | 
 | 566 |         return parse257(resp) | 
| Guido van Rossum | 9824509 | 1998-02-19 21:15:44 +0000 | [diff] [blame] | 567 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 568 |     def rmd(self, dirname): | 
 | 569 |         '''Remove a directory.''' | 
 | 570 |         return self.voidcmd('RMD ' + dirname) | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 571 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 572 |     def pwd(self): | 
 | 573 |         '''Return current working directory.''' | 
 | 574 |         resp = self.sendcmd('PWD') | 
 | 575 |         return parse257(resp) | 
| Guido van Rossum | 17ed1ae | 1993-06-01 13:21:04 +0000 | [diff] [blame] | 576 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 577 |     def quit(self): | 
 | 578 |         '''Quit, and close the connection.''' | 
 | 579 |         resp = self.voidcmd('QUIT') | 
 | 580 |         self.close() | 
 | 581 |         return resp | 
 | 582 |  | 
 | 583 |     def close(self): | 
 | 584 |         '''Close the connection without assuming anything about it.''' | 
| Fred Drake | 9c98a42 | 2001-02-28 21:46:37 +0000 | [diff] [blame] | 585 |         if self.file: | 
 | 586 |             self.file.close() | 
 | 587 |             self.sock.close() | 
 | 588 |             self.file = self.sock = None | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 589 |  | 
 | 590 |  | 
| Guido van Rossum | acfb82a | 1997-10-22 20:49:52 +0000 | [diff] [blame] | 591 | _150_re = None | 
| Fred Drake | 4de02d9 | 1997-01-10 18:26:09 +0000 | [diff] [blame] | 592 |  | 
 | 593 | def parse150(resp): | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 594 |     '''Parse the '150' response for a RETR request. | 
 | 595 |     Returns the expected transfer size or None; size is not guaranteed to | 
 | 596 |     be present in the 150 message. | 
 | 597 |     ''' | 
 | 598 |     if resp[:3] != '150': | 
 | 599 |         raise error_reply, resp | 
 | 600 |     global _150_re | 
 | 601 |     if _150_re is None: | 
 | 602 |         import re | 
 | 603 |         _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE) | 
 | 604 |     m = _150_re.match(resp) | 
| Guido van Rossum | b6aca6a | 2001-10-16 19:45:52 +0000 | [diff] [blame] | 605 |     if not m: | 
 | 606 |         return None | 
 | 607 |     s = m.group(1) | 
 | 608 |     try: | 
 | 609 |         return int(s) | 
| Guido van Rossum | 1f74cb3 | 2001-10-17 17:21:47 +0000 | [diff] [blame] | 610 |     except (OverflowError, ValueError): | 
| Guido van Rossum | b6aca6a | 2001-10-16 19:45:52 +0000 | [diff] [blame] | 611 |         return long(s) | 
| Fred Drake | 4de02d9 | 1997-01-10 18:26:09 +0000 | [diff] [blame] | 612 |  | 
 | 613 |  | 
| Guido van Rossum | 70297d3 | 2001-08-17 17:24:29 +0000 | [diff] [blame] | 614 | _227_re = None | 
 | 615 |  | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 616 | def parse227(resp): | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 617 |     '''Parse the '227' response for a PASV request. | 
 | 618 |     Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)' | 
 | 619 |     Return ('host.addr.as.numbers', port#) tuple.''' | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 620 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 621 |     if resp[:3] != '227': | 
 | 622 |         raise error_reply, resp | 
| Guido van Rossum | 70297d3 | 2001-08-17 17:24:29 +0000 | [diff] [blame] | 623 |     global _227_re | 
 | 624 |     if _227_re is None: | 
 | 625 |         import re | 
 | 626 |         _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)') | 
 | 627 |     m = _227_re.search(resp) | 
 | 628 |     if not m: | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 629 |         raise error_proto, resp | 
| Guido van Rossum | 70297d3 | 2001-08-17 17:24:29 +0000 | [diff] [blame] | 630 |     numbers = m.groups() | 
| Eric S. Raymond | c95bf69 | 2001-02-09 10:06:47 +0000 | [diff] [blame] | 631 |     host = '.'.join(numbers[:4]) | 
 | 632 |     port = (int(numbers[4]) << 8) + int(numbers[5]) | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 633 |     return host, port | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 634 |  | 
 | 635 |  | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 636 | def parse229(resp, peer): | 
 | 637 |     '''Parse the '229' response for a EPSV request. | 
 | 638 |     Raises error_proto if it does not contain '(|||port|)' | 
 | 639 |     Return ('host.addr.as.numbers', port#) tuple.''' | 
 | 640 |  | 
| Raymond Hettinger | c88a6c7 | 2005-04-05 04:31:09 +0000 | [diff] [blame] | 641 |     if resp[:3] != '229': | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 642 |         raise error_reply, resp | 
| Neal Norwitz | 7ce734c | 2002-05-31 14:13:04 +0000 | [diff] [blame] | 643 |     left = resp.find('(') | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 644 |     if left < 0: raise error_proto, resp | 
| Neal Norwitz | 7ce734c | 2002-05-31 14:13:04 +0000 | [diff] [blame] | 645 |     right = resp.find(')', left + 1) | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 646 |     if right < 0: | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 647 |         raise error_proto, resp # should contain '(|||port|)' | 
| Raymond Hettinger | c88a6c7 | 2005-04-05 04:31:09 +0000 | [diff] [blame] | 648 |     if resp[left + 1] != resp[right - 1]: | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 649 |         raise error_proto, resp | 
| Walter Dörwald | a401ae4 | 2002-06-03 10:41:45 +0000 | [diff] [blame] | 650 |     parts = resp[left + 1:right].split(resp[left+1]) | 
| Raymond Hettinger | c88a6c7 | 2005-04-05 04:31:09 +0000 | [diff] [blame] | 651 |     if len(parts) != 5: | 
| Martin v. Löwis | 4eb5940 | 2001-07-26 13:37:33 +0000 | [diff] [blame] | 652 |         raise error_proto, resp | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 653 |     host = peer[0] | 
| Neal Norwitz | 7ce734c | 2002-05-31 14:13:04 +0000 | [diff] [blame] | 654 |     port = int(parts[3]) | 
| Martin v. Löwis | a43c2f8 | 2001-07-24 20:34:08 +0000 | [diff] [blame] | 655 |     return host, port | 
 | 656 |  | 
 | 657 |  | 
| Guido van Rossum | c567c60 | 1992-11-05 22:22:37 +0000 | [diff] [blame] | 658 | def parse257(resp): | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 659 |     '''Parse the '257' response for a MKD or PWD request. | 
 | 660 |     This is a response to a MKD or PWD request: a directory name. | 
 | 661 |     Returns the directoryname in the 257 reply.''' | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 662 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 663 |     if resp[:3] != '257': | 
 | 664 |         raise error_reply, resp | 
 | 665 |     if resp[3:5] != ' "': | 
 | 666 |         return '' # Not compliant to RFC 959, but UNIX ftpd does this | 
 | 667 |     dirname = '' | 
 | 668 |     i = 5 | 
 | 669 |     n = len(resp) | 
 | 670 |     while i < n: | 
 | 671 |         c = resp[i] | 
 | 672 |         i = i+1 | 
 | 673 |         if c == '"': | 
 | 674 |             if i >= n or resp[i] != '"': | 
 | 675 |                 break | 
 | 676 |             i = i+1 | 
 | 677 |         dirname = dirname + c | 
 | 678 |     return dirname | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 679 |  | 
| Guido van Rossum | 2f3941d | 1997-10-07 14:49:56 +0000 | [diff] [blame] | 680 |  | 
| Guido van Rossum | ae3b3a3 | 1993-11-30 13:43:54 +0000 | [diff] [blame] | 681 | def print_line(line): | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 682 |     '''Default retrlines callback to print a line.''' | 
 | 683 |     print line | 
| Guido van Rossum | ae3b3a3 | 1993-11-30 13:43:54 +0000 | [diff] [blame] | 684 |  | 
| Guido van Rossum | 2f3941d | 1997-10-07 14:49:56 +0000 | [diff] [blame] | 685 |  | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 686 | def ftpcp(source, sourcename, target, targetname = '', type = 'I'): | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 687 |     '''Copy file from one FTP-instance to another.''' | 
 | 688 |     if not targetname: targetname = sourcename | 
 | 689 |     type = 'TYPE ' + type | 
 | 690 |     source.voidcmd(type) | 
 | 691 |     target.voidcmd(type) | 
 | 692 |     sourcehost, sourceport = parse227(source.sendcmd('PASV')) | 
 | 693 |     target.sendport(sourcehost, sourceport) | 
 | 694 |     # RFC 959: the user must "listen" [...] BEFORE sending the | 
 | 695 |     # transfer request. | 
 | 696 |     # So: STOR before RETR, because here the target is a "user". | 
 | 697 |     treply = target.sendcmd('STOR ' + targetname) | 
 | 698 |     if treply[:3] not in ('125', '150'): raise error_proto  # RFC 959 | 
 | 699 |     sreply = source.sendcmd('RETR ' + sourcename) | 
 | 700 |     if sreply[:3] not in ('125', '150'): raise error_proto  # RFC 959 | 
 | 701 |     source.voidresp() | 
 | 702 |     target.voidresp() | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 703 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 704 |  | 
| Guido van Rossum | 56d1e3a | 1997-03-14 04:16:54 +0000 | [diff] [blame] | 705 | class Netrc: | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 706 |     """Class to parse & provide access to 'netrc' format files. | 
| Fred Drake | 475d51d | 1997-06-24 22:02:54 +0000 | [diff] [blame] | 707 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 708 |     See the netrc(4) man page for information on the file format. | 
| Guido van Rossum | 56d1e3a | 1997-03-14 04:16:54 +0000 | [diff] [blame] | 709 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 710 |     WARNING: This class is obsolete -- use module netrc instead. | 
| Guido van Rossum | c822a45 | 1998-12-22 16:49:16 +0000 | [diff] [blame] | 711 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 712 |     """ | 
 | 713 |     __defuser = None | 
 | 714 |     __defpasswd = None | 
 | 715 |     __defacct = None | 
| Guido van Rossum | 56d1e3a | 1997-03-14 04:16:54 +0000 | [diff] [blame] | 716 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 717 |     def __init__(self, filename=None): | 
| Raymond Hettinger | 094662a | 2002-06-01 01:29:16 +0000 | [diff] [blame] | 718 |         if filename is None: | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 719 |             if "HOME" in os.environ: | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 720 |                 filename = os.path.join(os.environ["HOME"], | 
 | 721 |                                         ".netrc") | 
 | 722 |             else: | 
 | 723 |                 raise IOError, \ | 
 | 724 |                       "specify file to load or set $HOME" | 
 | 725 |         self.__hosts = {} | 
 | 726 |         self.__macros = {} | 
 | 727 |         fp = open(filename, "r") | 
 | 728 |         in_macro = 0 | 
 | 729 |         while 1: | 
 | 730 |             line = fp.readline() | 
 | 731 |             if not line: break | 
| Eric S. Raymond | c95bf69 | 2001-02-09 10:06:47 +0000 | [diff] [blame] | 732 |             if in_macro and line.strip(): | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 733 |                 macro_lines.append(line) | 
 | 734 |                 continue | 
 | 735 |             elif in_macro: | 
 | 736 |                 self.__macros[macro_name] = tuple(macro_lines) | 
 | 737 |                 in_macro = 0 | 
| Eric S. Raymond | c95bf69 | 2001-02-09 10:06:47 +0000 | [diff] [blame] | 738 |             words = line.split() | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 739 |             host = user = passwd = acct = None | 
 | 740 |             default = 0 | 
 | 741 |             i = 0 | 
 | 742 |             while i < len(words): | 
 | 743 |                 w1 = words[i] | 
 | 744 |                 if i+1 < len(words): | 
 | 745 |                     w2 = words[i + 1] | 
 | 746 |                 else: | 
 | 747 |                     w2 = None | 
 | 748 |                 if w1 == 'default': | 
 | 749 |                     default = 1 | 
 | 750 |                 elif w1 == 'machine' and w2: | 
| Eric S. Raymond | c95bf69 | 2001-02-09 10:06:47 +0000 | [diff] [blame] | 751 |                     host = w2.lower() | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 752 |                     i = i + 1 | 
 | 753 |                 elif w1 == 'login' and w2: | 
 | 754 |                     user = w2 | 
 | 755 |                     i = i + 1 | 
 | 756 |                 elif w1 == 'password' and w2: | 
 | 757 |                     passwd = w2 | 
 | 758 |                     i = i + 1 | 
 | 759 |                 elif w1 == 'account' and w2: | 
 | 760 |                     acct = w2 | 
 | 761 |                     i = i + 1 | 
 | 762 |                 elif w1 == 'macdef' and w2: | 
 | 763 |                     macro_name = w2 | 
 | 764 |                     macro_lines = [] | 
 | 765 |                     in_macro = 1 | 
 | 766 |                     break | 
 | 767 |                 i = i + 1 | 
 | 768 |             if default: | 
 | 769 |                 self.__defuser = user or self.__defuser | 
 | 770 |                 self.__defpasswd = passwd or self.__defpasswd | 
 | 771 |                 self.__defacct = acct or self.__defacct | 
 | 772 |             if host: | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 773 |                 if host in self.__hosts: | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 774 |                     ouser, opasswd, oacct = \ | 
 | 775 |                            self.__hosts[host] | 
 | 776 |                     user = user or ouser | 
 | 777 |                     passwd = passwd or opasswd | 
 | 778 |                     acct = acct or oacct | 
 | 779 |                 self.__hosts[host] = user, passwd, acct | 
 | 780 |         fp.close() | 
| Guido van Rossum | 56d1e3a | 1997-03-14 04:16:54 +0000 | [diff] [blame] | 781 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 782 |     def get_hosts(self): | 
 | 783 |         """Return a list of hosts mentioned in the .netrc file.""" | 
 | 784 |         return self.__hosts.keys() | 
| Guido van Rossum | 8ca8420 | 1998-03-26 20:56:10 +0000 | [diff] [blame] | 785 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 786 |     def get_account(self, host): | 
 | 787 |         """Returns login information for the named host. | 
| Guido van Rossum | 8ca8420 | 1998-03-26 20:56:10 +0000 | [diff] [blame] | 788 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 789 |         The return value is a triple containing userid, | 
 | 790 |         password, and the accounting field. | 
| Guido van Rossum | 8ca8420 | 1998-03-26 20:56:10 +0000 | [diff] [blame] | 791 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 792 |         """ | 
| Eric S. Raymond | c95bf69 | 2001-02-09 10:06:47 +0000 | [diff] [blame] | 793 |         host = host.lower() | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 794 |         user = passwd = acct = None | 
| Raymond Hettinger | 54f0222 | 2002-06-01 14:18:47 +0000 | [diff] [blame] | 795 |         if host in self.__hosts: | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 796 |             user, passwd, acct = self.__hosts[host] | 
 | 797 |         user = user or self.__defuser | 
 | 798 |         passwd = passwd or self.__defpasswd | 
 | 799 |         acct = acct or self.__defacct | 
 | 800 |         return user, passwd, acct | 
| Guido van Rossum | 8ca8420 | 1998-03-26 20:56:10 +0000 | [diff] [blame] | 801 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 802 |     def get_macros(self): | 
 | 803 |         """Return a list of all defined macro names.""" | 
 | 804 |         return self.__macros.keys() | 
| Guido van Rossum | 8ca8420 | 1998-03-26 20:56:10 +0000 | [diff] [blame] | 805 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 806 |     def get_macro(self, macro): | 
 | 807 |         """Return a sequence of lines which define a named macro.""" | 
 | 808 |         return self.__macros[macro] | 
| Guido van Rossum | 56d1e3a | 1997-03-14 04:16:54 +0000 | [diff] [blame] | 809 |  | 
| Fred Drake | 475d51d | 1997-06-24 22:02:54 +0000 | [diff] [blame] | 810 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 811 |  | 
| Guido van Rossum | 1115ab2 | 1992-11-04 15:51:30 +0000 | [diff] [blame] | 812 | def test(): | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 813 |     '''Test program. | 
| Raymond Hettinger | c88a6c7 | 2005-04-05 04:31:09 +0000 | [diff] [blame] | 814 |     Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ... | 
 | 815 |  | 
 | 816 |     -d dir | 
 | 817 |     -l list | 
 | 818 |     -p password | 
 | 819 |     ''' | 
 | 820 |  | 
 | 821 |     if len(sys.argv) < 2: | 
 | 822 |         print test.__doc__ | 
 | 823 |         sys.exit(0) | 
| Guido van Rossum | d2560b0 | 1996-05-28 23:41:25 +0000 | [diff] [blame] | 824 |  | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 825 |     debugging = 0 | 
 | 826 |     rcfile = None | 
 | 827 |     while sys.argv[1] == '-d': | 
 | 828 |         debugging = debugging+1 | 
 | 829 |         del sys.argv[1] | 
 | 830 |     if sys.argv[1][:2] == '-r': | 
 | 831 |         # get name of alternate ~/.netrc file: | 
 | 832 |         rcfile = sys.argv[1][2:] | 
 | 833 |         del sys.argv[1] | 
 | 834 |     host = sys.argv[1] | 
 | 835 |     ftp = FTP(host) | 
 | 836 |     ftp.set_debuglevel(debugging) | 
 | 837 |     userid = passwd = acct = '' | 
 | 838 |     try: | 
 | 839 |         netrc = Netrc(rcfile) | 
 | 840 |     except IOError: | 
 | 841 |         if rcfile is not None: | 
 | 842 |             sys.stderr.write("Could not open account file" | 
 | 843 |                              " -- using anonymous login.") | 
 | 844 |     else: | 
 | 845 |         try: | 
 | 846 |             userid, passwd, acct = netrc.get_account(host) | 
 | 847 |         except KeyError: | 
 | 848 |             # no account for host | 
 | 849 |             sys.stderr.write( | 
 | 850 |                     "No account -- using anonymous login.") | 
 | 851 |     ftp.login(userid, passwd, acct) | 
 | 852 |     for file in sys.argv[2:]: | 
 | 853 |         if file[:2] == '-l': | 
 | 854 |             ftp.dir(file[2:]) | 
 | 855 |         elif file[:2] == '-d': | 
 | 856 |             cmd = 'CWD' | 
 | 857 |             if file[2:]: cmd = cmd + ' ' + file[2:] | 
 | 858 |             resp = ftp.sendcmd(cmd) | 
 | 859 |         elif file == '-p': | 
 | 860 |             ftp.set_pasv(not ftp.passiveserver) | 
 | 861 |         else: | 
 | 862 |             ftp.retrbinary('RETR ' + file, \ | 
 | 863 |                            sys.stdout.write, 1024) | 
 | 864 |     ftp.quit() | 
| Guido van Rossum | 221ec0b | 1995-08-04 04:39:30 +0000 | [diff] [blame] | 865 |  | 
 | 866 |  | 
 | 867 | if __name__ == '__main__': | 
| Tim Peters | 88869f9 | 2001-01-14 23:36:06 +0000 | [diff] [blame] | 868 |     test() |