blob: ea91c1707c137dfcd6185facfc7e37786dfcb4cd [file] [log] [blame]
Guido van Rossum4b8c6ea2000-02-04 15:39:30 +00001"""An FTP client class and some helper functions.
2
Barry Warsaw100d81e2000-09-01 06:09:23 +00003Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds
Guido van Rossum1115ab21992-11-04 15:51:30 +00004
Guido van Rossumd2560b01996-05-28 23:41:25 +00005Example:
6
7>>> from ftplib import FTP
8>>> ftp = FTP('ftp.python.org') # connect to host, default port
Guido van Rossum24a64342001-12-28 20:54:55 +00009>>> ftp.login() # default, i.e.: user anonymous, passwd anonymous@
Guido van Rossum2f3941d1997-10-07 14:49:56 +000010'230 Guest login ok, access restrictions apply.'
Guido van Rossumd2560b01996-05-28 23:41:25 +000011>>> ftp.retrlines('LIST') # list directory contents
12total 9
13drwxr-xr-x 8 root wheel 1024 Jan 3 1994 .
14drwxr-xr-x 8 root wheel 1024 Jan 3 1994 ..
15drwxr-xr-x 2 root wheel 1024 Jan 3 1994 bin
16drwxr-xr-x 2 root wheel 1024 Jan 3 1994 etc
17d-wxrwxr-x 2 ftp wheel 1024 Sep 5 13:43 incoming
18drwxr-xr-x 2 root wheel 1024 Nov 17 1993 lib
19drwxr-xr-x 6 1094 wheel 1024 Sep 13 19:07 pub
20drwxr-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 Rossum2f3941d1997-10-07 14:49:56 +000022'226 Transfer complete.'
Guido van Rossumd2560b01996-05-28 23:41:25 +000023>>> ftp.quit()
Guido van Rossum2f3941d1997-10-07 14:49:56 +000024'221 Goodbye.'
Tim Peters88869f92001-01-14 23:36:06 +000025>>>
Guido van Rossumd2560b01996-05-28 23:41:25 +000026
27A nice test that reveals some of the network dialogue would be:
28python ftplib.py -d localhost -l -p -l
Guido van Rossum4b8c6ea2000-02-04 15:39:30 +000029"""
Guido van Rossumc567c601992-11-05 22:22:37 +000030
Tim Peters88869f92001-01-14 23:36:06 +000031#
Guido van Rossum98d9fd32000-02-28 15:12:25 +000032# 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.
Christian Heimes1af737c2008-01-23 08:24:23 +000035# Modified by Phil Schwartz to add storbinary and storlines callbacks.
Guido van Rossum98d9fd32000-02-28 15:12:25 +000036#
Guido van Rossumc567c601992-11-05 22:22:37 +000037
Guido van Rossum1115ab21992-11-04 15:51:30 +000038import os
39import sys
Guido van Rossum1115ab21992-11-04 15:51:30 +000040
Guido van Rossumb6775db1994-08-01 11:34:53 +000041# Import SOCKS module if it exists, else standard socket module socket
42try:
Tim Peters88869f92001-01-14 23:36:06 +000043 import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket
44 from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn
Guido van Rossumb6775db1994-08-01 11:34:53 +000045except ImportError:
Tim Peters88869f92001-01-14 23:36:06 +000046 import socket
Georg Brandlf78e02b2008-06-10 17:40:04 +000047from socket import _GLOBAL_DEFAULT_TIMEOUT
Guido van Rossumb6775db1994-08-01 11:34:53 +000048
Skip Montanaroeccd02a2001-01-20 23:34:12 +000049__all__ = ["FTP","Netrc"]
Guido van Rossum1115ab21992-11-04 15:51:30 +000050
Guido van Rossumd3166071993-05-24 14:16:22 +000051# Magic number from <socket.h>
Tim Peters88869f92001-01-14 23:36:06 +000052MSG_OOB = 0x1 # Process data out of band
Guido van Rossumd3166071993-05-24 14:16:22 +000053
54
Guido van Rossumc567c601992-11-05 22:22:37 +000055# The standard FTP server control port
Guido van Rossum1115ab21992-11-04 15:51:30 +000056FTP_PORT = 21
Guido van Rossum1115ab21992-11-04 15:51:30 +000057
58
Guido van Rossum21974791992-11-06 13:34:17 +000059# Exception raised when an error or invalid response is received
Fred Drake227b1202000-08-17 05:06:49 +000060class Error(Exception): pass
Tim Peters88869f92001-01-14 23:36:06 +000061class error_reply(Error): pass # unexpected [123]xx reply
62class error_temp(Error): pass # 4xx errors
63class error_perm(Error): pass # 5xx errors
64class error_proto(Error): pass # response does not begin with [1-5]
Guido van Rossum1115ab21992-11-04 15:51:30 +000065
66
Guido van Rossum21974791992-11-06 13:34:17 +000067# All exceptions (hopefully) that may be raised here and that aren't
68# (always) programming errors on our side
Christian Heimes33fe8092008-04-13 13:53:33 +000069all_errors = (Error, IOError, EOFError)
Guido van Rossum21974791992-11-06 13:34:17 +000070
71
Guido van Rossum1115ab21992-11-04 15:51:30 +000072# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
73CRLF = '\r\n'
Benjamin Petersonbe17a112008-09-27 21:49:47 +000074B_CRLF = b'\r\n'
Guido van Rossum1115ab21992-11-04 15:51:30 +000075
Guido van Rossum1115ab21992-11-04 15:51:30 +000076# The class itself
77class FTP:
78
Tim Peters88869f92001-01-14 23:36:06 +000079 '''An FTP client class.
Guido van Rossumd2560b01996-05-28 23:41:25 +000080
Guido van Rossumd8faa362007-04-27 19:54:29 +000081 To create a connection, call the class using these arguments:
82 host, user, passwd, acct, timeout
83
84 The first four arguments are all strings, and have default value ''.
85 timeout must be numeric and defaults to None if not passed,
86 meaning that no timeout will be set on any ftp socket(s)
87 If a timeout is passed, then this is now the default timeout for all ftp
88 socket operations for this instance.
89
Tim Peters88869f92001-01-14 23:36:06 +000090 Then use self.connect() with optional host and port argument.
Guido van Rossumd2560b01996-05-28 23:41:25 +000091
Tim Peters88869f92001-01-14 23:36:06 +000092 To download a file, use ftp.retrlines('RETR ' + filename),
93 or ftp.retrbinary() with slightly different arguments.
94 To upload a file, use ftp.storlines() or ftp.storbinary(),
95 which have an open file as argument (see their definitions
96 below for details).
97 The download/upload functions first issue appropriate TYPE
98 and PORT or PASV commands.
Guido van Rossum52b89762007-07-17 20:45:57 +000099 '''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000100
Fred Drake9c98a422001-02-28 21:46:37 +0000101 debugging = 0
102 host = ''
103 port = FTP_PORT
104 sock = None
105 file = None
106 welcome = None
107 passiveserver = 1
Brett Cannon6733d702007-10-08 19:48:15 +0000108 encoding = "latin1"
Fred Drake9c98a422001-02-28 21:46:37 +0000109
Tim Peters88869f92001-01-14 23:36:06 +0000110 # Initialization method (called by class instantiation).
111 # Initialize host to localhost, port to standard ftp port
112 # Optional arguments are host (for connect()),
113 # and user, passwd, acct (for login())
Georg Brandlf78e02b2008-06-10 17:40:04 +0000114 def __init__(self, host='', user='', passwd='', acct='',
115 timeout=_GLOBAL_DEFAULT_TIMEOUT):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000116 self.timeout = timeout
Tim Peters88869f92001-01-14 23:36:06 +0000117 if host:
Fred Drake9c98a422001-02-28 21:46:37 +0000118 self.connect(host)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000119 if user:
120 self.login(user, passwd, acct)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000121
Georg Brandlf78e02b2008-06-10 17:40:04 +0000122 def connect(self, host='', port=0, timeout=-999):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000123 '''Connect to host. Arguments are:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000124 - host: hostname to connect to (string, default previous host)
125 - port: port to connect to (integer, default previous port)
126 '''
127 if host != '':
128 self.host = host
129 if port > 0:
130 self.port = port
Georg Brandlf78e02b2008-06-10 17:40:04 +0000131 if timeout != -999:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000132 self.timeout = timeout
133 self.sock = socket.create_connection((self.host, self.port), self.timeout)
134 self.af = self.sock.family
Guido van Rossum52b89762007-07-17 20:45:57 +0000135 self.file = self.sock.makefile('r', encoding=self.encoding)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000136 self.welcome = self.getresp()
137 return self.welcome
Guido van Rossum1115ab21992-11-04 15:51:30 +0000138
Tim Peters88869f92001-01-14 23:36:06 +0000139 def getwelcome(self):
140 '''Get the welcome message from the server.
141 (this is read and squirreled away by connect())'''
142 if self.debugging:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000143 print('*welcome*', self.sanitize(self.welcome))
Tim Peters88869f92001-01-14 23:36:06 +0000144 return self.welcome
Guido van Rossum1115ab21992-11-04 15:51:30 +0000145
Tim Peters88869f92001-01-14 23:36:06 +0000146 def set_debuglevel(self, level):
147 '''Set the debugging level.
148 The required argument level means:
149 0: no debugging output (default)
150 1: print commands and responses but not body text etc.
151 2: also print raw lines read and sent before stripping CR/LF'''
152 self.debugging = level
153 debug = set_debuglevel
Guido van Rossum1115ab21992-11-04 15:51:30 +0000154
Tim Peters88869f92001-01-14 23:36:06 +0000155 def set_pasv(self, val):
156 '''Use passive or active mode for data transfers.
157 With a false argument, use the normal PORT mode,
158 With a true argument, use the PASV command.'''
159 self.passiveserver = val
Guido van Rossumd2560b01996-05-28 23:41:25 +0000160
Tim Peters88869f92001-01-14 23:36:06 +0000161 # Internal: "sanitize" a string for printing
162 def sanitize(self, s):
163 if s[:5] == 'pass ' or s[:5] == 'PASS ':
164 i = len(s)
165 while i > 5 and s[i-1] in '\r\n':
166 i = i-1
167 s = s[:5] + '*'*(i-5) + s[i:]
Walter Dörwald70a6b492004-02-12 17:35:32 +0000168 return repr(s)
Guido van Rossumebaf1041995-05-05 15:54:14 +0000169
Tim Peters88869f92001-01-14 23:36:06 +0000170 # Internal: send one line to the server, appending CRLF
171 def putline(self, line):
172 line = line + CRLF
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000173 if self.debugging > 1: print('*put*', self.sanitize(line))
Guido van Rossum52b89762007-07-17 20:45:57 +0000174 self.sock.sendall(line.encode(self.encoding))
Guido van Rossum1115ab21992-11-04 15:51:30 +0000175
Tim Peters88869f92001-01-14 23:36:06 +0000176 # Internal: send one command to the server (through putline())
177 def putcmd(self, line):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000178 if self.debugging: print('*cmd*', self.sanitize(line))
Tim Peters88869f92001-01-14 23:36:06 +0000179 self.putline(line)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000180
Tim Peters88869f92001-01-14 23:36:06 +0000181 # Internal: return one line from the server, stripping CRLF.
182 # Raise EOFError if the connection is closed
183 def getline(self):
184 line = self.file.readline()
185 if self.debugging > 1:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000186 print('*get*', self.sanitize(line))
Tim Peters88869f92001-01-14 23:36:06 +0000187 if not line: raise EOFError
188 if line[-2:] == CRLF: line = line[:-2]
189 elif line[-1:] in CRLF: line = line[:-1]
190 return line
Guido van Rossum1115ab21992-11-04 15:51:30 +0000191
Tim Peters88869f92001-01-14 23:36:06 +0000192 # Internal: get a response from the server, which may possibly
193 # consist of multiple lines. Return a single string with no
194 # trailing CRLF. If the response consists of multiple lines,
195 # these are separated by '\n' characters in the string
196 def getmultiline(self):
197 line = self.getline()
198 if line[3:4] == '-':
199 code = line[:3]
200 while 1:
201 nextline = self.getline()
202 line = line + ('\n' + nextline)
203 if nextline[:3] == code and \
204 nextline[3:4] != '-':
205 break
206 return line
Guido van Rossum1115ab21992-11-04 15:51:30 +0000207
Tim Peters88869f92001-01-14 23:36:06 +0000208 # Internal: get a response from the server.
209 # Raise various errors if the response indicates an error
210 def getresp(self):
211 resp = self.getmultiline()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000212 if self.debugging: print('*resp*', self.sanitize(resp))
Tim Peters88869f92001-01-14 23:36:06 +0000213 self.lastresp = resp[:3]
214 c = resp[:1]
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000215 if c in ('1', '2', '3'):
216 return resp
Tim Peters88869f92001-01-14 23:36:06 +0000217 if c == '4':
Collin Winterce36ad82007-08-30 01:19:48 +0000218 raise error_temp(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000219 if c == '5':
Collin Winterce36ad82007-08-30 01:19:48 +0000220 raise error_perm(resp)
221 raise error_proto(resp)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000222
Tim Peters88869f92001-01-14 23:36:06 +0000223 def voidresp(self):
224 """Expect a response beginning with '2'."""
225 resp = self.getresp()
Benjamin Petersond23f8222009-04-05 19:13:16 +0000226 if resp[:1] != '2':
Collin Winterce36ad82007-08-30 01:19:48 +0000227 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000228 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000229
Tim Peters88869f92001-01-14 23:36:06 +0000230 def abort(self):
231 '''Abort a file transfer. Uses out-of-band data.
232 This does not follow the procedure from the RFC to send Telnet
233 IP and Synch; that doesn't seem to work with the servers I've
234 tried. Instead, just send the ABOR command as OOB data.'''
235 line = 'ABOR' + CRLF
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000236 if self.debugging > 1: print('*put urgent*', self.sanitize(line))
Martin v. Löwise12454f2002-02-16 23:06:19 +0000237 self.sock.sendall(line, MSG_OOB)
Tim Peters88869f92001-01-14 23:36:06 +0000238 resp = self.getmultiline()
Giampaolo Rodolà22357e12010-04-18 13:23:21 +0000239 if resp[:3] not in ('426', '225', '226'):
Collin Winterce36ad82007-08-30 01:19:48 +0000240 raise error_proto(resp)
Guido van Rossumd3166071993-05-24 14:16:22 +0000241
Tim Peters88869f92001-01-14 23:36:06 +0000242 def sendcmd(self, cmd):
243 '''Send a command and return the response.'''
244 self.putcmd(cmd)
245 return self.getresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000246
Tim Peters88869f92001-01-14 23:36:06 +0000247 def voidcmd(self, cmd):
248 """Send a command and expect a response beginning with '2'."""
249 self.putcmd(cmd)
250 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000251
Tim Peters88869f92001-01-14 23:36:06 +0000252 def sendport(self, host, port):
253 '''Send a PORT command with the current host and the given
254 port number.
255 '''
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000256 hbytes = host.split('.')
Benjamin Peterson3a53fbb2008-09-27 22:04:16 +0000257 pbytes = [repr(port//256), repr(port%256)]
Tim Peters88869f92001-01-14 23:36:06 +0000258 bytes = hbytes + pbytes
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000259 cmd = 'PORT ' + ','.join(bytes)
Tim Peters88869f92001-01-14 23:36:06 +0000260 return self.voidcmd(cmd)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000261
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000262 def sendeprt(self, host, port):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000263 '''Send a EPRT command with the current host and the given port number.'''
264 af = 0
265 if self.af == socket.AF_INET:
266 af = 1
267 if self.af == socket.AF_INET6:
268 af = 2
269 if af == 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000270 raise error_proto('unsupported address family')
Walter Dörwald70a6b492004-02-12 17:35:32 +0000271 fields = ['', repr(af), host, repr(port), '']
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000272 cmd = 'EPRT ' + '|'.join(fields)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000273 return self.voidcmd(cmd)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000274
Tim Peters88869f92001-01-14 23:36:06 +0000275 def makeport(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000276 '''Create a new socket and send a PORT command for it.'''
Martin v. Löwis2ad25692001-07-31 08:40:21 +0000277 msg = "getaddrinfo returns an empty list"
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000278 sock = None
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000279 for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
280 af, socktype, proto, canonname, sa = res
281 try:
282 sock = socket.socket(af, socktype, proto)
283 sock.bind(sa)
Guido van Rossumb940e112007-01-10 16:19:56 +0000284 except socket.error as msg:
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000285 if sock:
286 sock.close()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000287 sock = None
288 continue
289 break
290 if not sock:
Collin Winterce36ad82007-08-30 01:19:48 +0000291 raise socket.error(msg)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000292 sock.listen(1)
293 port = sock.getsockname()[1] # Get proper port
294 host = self.sock.getsockname()[0] # Get proper host
295 if self.af == socket.AF_INET:
296 resp = self.sendport(host, port)
297 else:
298 resp = self.sendeprt(host, port)
Giampaolo Rodolàf5279402010-04-19 22:10:56 +0000299 if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
300 sock.settimeout(self.timeout)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000301 return sock
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000302
303 def makepasv(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000304 if self.af == socket.AF_INET:
305 host, port = parse227(self.sendcmd('PASV'))
306 else:
307 host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
308 return host, port
Guido van Rossum1115ab21992-11-04 15:51:30 +0000309
Tim Peters88869f92001-01-14 23:36:06 +0000310 def ntransfercmd(self, cmd, rest=None):
311 """Initiate a transfer over the data connection.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000312
Tim Peters88869f92001-01-14 23:36:06 +0000313 If the transfer is active, send a port command and the
314 transfer command, and accept the connection. If the server is
315 passive, send a pasv command, connect to it, and start the
316 transfer command. Either way, return the socket for the
317 connection and the expected size of the transfer. The
318 expected size may be None if it could not be determined.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000319
Tim Peters88869f92001-01-14 23:36:06 +0000320 Optional `rest' argument can be a string that is sent as the
Christian Heimes1af737c2008-01-23 08:24:23 +0000321 argument to a REST command. This is essentially a server
Tim Peters88869f92001-01-14 23:36:06 +0000322 marker used to tell the server to skip over any data up to the
323 given marker.
324 """
325 size = None
326 if self.passiveserver:
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000327 host, port = self.makepasv()
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000328 conn = socket.create_connection((host, port), self.timeout)
Tim Peters88869f92001-01-14 23:36:06 +0000329 if rest is not None:
330 self.sendcmd("REST %s" % rest)
331 resp = self.sendcmd(cmd)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000332 # Some servers apparently send a 200 reply to
333 # a LIST or STOR command, before the 150 reply
334 # (and way before the 226 reply). This seems to
335 # be in violation of the protocol (which only allows
336 # 1xx or error messages for LIST), so we just discard
337 # this response.
338 if resp[0] == '2':
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000339 resp = self.getresp()
Tim Peters88869f92001-01-14 23:36:06 +0000340 if resp[0] != '1':
Collin Winterce36ad82007-08-30 01:19:48 +0000341 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000342 else:
343 sock = self.makeport()
344 if rest is not None:
345 self.sendcmd("REST %s" % rest)
346 resp = self.sendcmd(cmd)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000347 # See above.
348 if resp[0] == '2':
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000349 resp = self.getresp()
Tim Peters88869f92001-01-14 23:36:06 +0000350 if resp[0] != '1':
Collin Winterce36ad82007-08-30 01:19:48 +0000351 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000352 conn, sockaddr = sock.accept()
Giampaolo Rodolàf5279402010-04-19 22:10:56 +0000353 if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
354 conn.settimeout(self.timeout)
Tim Peters88869f92001-01-14 23:36:06 +0000355 if resp[:3] == '150':
356 # this is conditional in case we received a 125
357 size = parse150(resp)
358 return conn, size
Fred Drake4de02d91997-01-10 18:26:09 +0000359
Tim Peters88869f92001-01-14 23:36:06 +0000360 def transfercmd(self, cmd, rest=None):
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000361 """Like ntransfercmd() but returns only the socket."""
Tim Peters88869f92001-01-14 23:36:06 +0000362 return self.ntransfercmd(cmd, rest)[0]
Guido van Rossumc567c601992-11-05 22:22:37 +0000363
Tim Peters88869f92001-01-14 23:36:06 +0000364 def login(self, user = '', passwd = '', acct = ''):
365 '''Login, default anonymous.'''
366 if not user: user = 'anonymous'
367 if not passwd: passwd = ''
368 if not acct: acct = ''
369 if user == 'anonymous' and passwd in ('', '-'):
Tim Peterse4418602002-02-16 07:34:19 +0000370 # If there is no anonymous ftp password specified
371 # then we'll just use anonymous@
372 # We don't send any other thing because:
373 # - We want to remain anonymous
374 # - We want to stop SPAM
375 # - We don't want to let ftp sites to discriminate by the user,
376 # host or country.
Guido van Rossumc33e0772001-12-28 20:54:28 +0000377 passwd = passwd + 'anonymous@'
Tim Peters88869f92001-01-14 23:36:06 +0000378 resp = self.sendcmd('USER ' + user)
379 if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
380 if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
381 if resp[0] != '2':
Collin Winterce36ad82007-08-30 01:19:48 +0000382 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000383 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000384
Tim Peters88869f92001-01-14 23:36:06 +0000385 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
Christian Heimes1af737c2008-01-23 08:24:23 +0000386 """Retrieve data in binary mode. A new port is created for you.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000387
Christian Heimes1af737c2008-01-23 08:24:23 +0000388 Args:
389 cmd: A RETR command.
390 callback: A single parameter callable to be called on each
391 block of data read.
392 blocksize: The maximum number of bytes to read from the
393 socket at one time. [default: 8192]
394 rest: Passed to transfercmd(). [default: None]
Guido van Rossum1115ab21992-11-04 15:51:30 +0000395
Christian Heimes1af737c2008-01-23 08:24:23 +0000396 Returns:
397 The response code.
Tim Peters88869f92001-01-14 23:36:06 +0000398 """
399 self.voidcmd('TYPE I')
400 conn = self.transfercmd(cmd, rest)
401 while 1:
402 data = conn.recv(blocksize)
403 if not data:
404 break
405 callback(data)
406 conn.close()
407 return self.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000408
Tim Peters88869f92001-01-14 23:36:06 +0000409 def retrlines(self, cmd, callback = None):
Christian Heimes1af737c2008-01-23 08:24:23 +0000410 """Retrieve data in line mode. A new port is created for you.
411
412 Args:
413 cmd: A RETR, LIST, NLST, or MLSD command.
414 callback: An optional single parameter callable that is called
415 for each line with the trailing CRLF stripped.
416 [default: print_line()]
417
418 Returns:
419 The response code.
420 """
Raymond Hettingere874fc32002-05-12 05:53:51 +0000421 if callback is None: callback = print_line
Tim Peters88869f92001-01-14 23:36:06 +0000422 resp = self.sendcmd('TYPE A')
423 conn = self.transfercmd(cmd)
Guido van Rossum52b89762007-07-17 20:45:57 +0000424 fp = conn.makefile('r', encoding=self.encoding)
Tim Peters88869f92001-01-14 23:36:06 +0000425 while 1:
426 line = fp.readline()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000427 if self.debugging > 2: print('*retr*', repr(line))
Tim Peters88869f92001-01-14 23:36:06 +0000428 if not line:
429 break
430 if line[-2:] == CRLF:
431 line = line[:-2]
432 elif line[-1:] == '\n':
433 line = line[:-1]
434 callback(line)
435 fp.close()
436 conn.close()
437 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000438
Christian Heimes1af737c2008-01-23 08:24:23 +0000439 def storbinary(self, cmd, fp, blocksize=8192, callback=None):
440 """Store a file in binary mode. A new port is created for you.
441
442 Args:
443 cmd: A STOR command.
444 fp: A file-like object with a read(num_bytes) method.
445 blocksize: The maximum data size to read from fp and send over
446 the connection at once. [default: 8192]
447 callback: An optional single parameter callable that is called on
448 on each block of data after it is sent. [default: None]
449
450 Returns:
451 The response code.
452 """
Tim Peters88869f92001-01-14 23:36:06 +0000453 self.voidcmd('TYPE I')
454 conn = self.transfercmd(cmd)
455 while 1:
456 buf = fp.read(blocksize)
457 if not buf: break
Martin v. Löwise12454f2002-02-16 23:06:19 +0000458 conn.sendall(buf)
Christian Heimes1af737c2008-01-23 08:24:23 +0000459 if callback: callback(buf)
Tim Peters88869f92001-01-14 23:36:06 +0000460 conn.close()
461 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000462
Christian Heimes1af737c2008-01-23 08:24:23 +0000463 def storlines(self, cmd, fp, callback=None):
464 """Store a file in line mode. A new port is created for you.
465
466 Args:
467 cmd: A STOR command.
468 fp: A file-like object with a readline() method.
469 callback: An optional single parameter callable that is called on
470 on each line after it is sent. [default: None]
471
472 Returns:
473 The response code.
474 """
Tim Peters88869f92001-01-14 23:36:06 +0000475 self.voidcmd('TYPE A')
476 conn = self.transfercmd(cmd)
477 while 1:
478 buf = fp.readline()
479 if not buf: break
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000480 if buf[-2:] != B_CRLF:
481 if buf[-1] in B_CRLF: buf = buf[:-1]
482 buf = buf + B_CRLF
Martin v. Löwise12454f2002-02-16 23:06:19 +0000483 conn.sendall(buf)
Christian Heimes1af737c2008-01-23 08:24:23 +0000484 if callback: callback(buf)
Tim Peters88869f92001-01-14 23:36:06 +0000485 conn.close()
486 return self.voidresp()
Guido van Rossum0eaa74b1996-01-25 18:37:21 +0000487
Tim Peters88869f92001-01-14 23:36:06 +0000488 def acct(self, password):
489 '''Send new account name.'''
490 cmd = 'ACCT ' + password
491 return self.voidcmd(cmd)
Guido van Rossumc567c601992-11-05 22:22:37 +0000492
Tim Peters88869f92001-01-14 23:36:06 +0000493 def nlst(self, *args):
494 '''Return a list of files in a given directory (default the current).'''
495 cmd = 'NLST'
496 for arg in args:
497 cmd = cmd + (' ' + arg)
498 files = []
499 self.retrlines(cmd, files.append)
500 return files
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000501
Tim Peters88869f92001-01-14 23:36:06 +0000502 def dir(self, *args):
503 '''List a directory in long form.
504 By default list current directory to stdout.
505 Optional last argument is callback function; all
506 non-empty arguments before it are concatenated to the
507 LIST command. (This *should* only be used for a pathname.)'''
508 cmd = 'LIST'
509 func = None
510 if args[-1:] and type(args[-1]) != type(''):
511 args, func = args[:-1], args[-1]
512 for arg in args:
513 if arg:
514 cmd = cmd + (' ' + arg)
515 self.retrlines(cmd, func)
Guido van Rossumc567c601992-11-05 22:22:37 +0000516
Tim Peters88869f92001-01-14 23:36:06 +0000517 def rename(self, fromname, toname):
518 '''Rename a file.'''
519 resp = self.sendcmd('RNFR ' + fromname)
520 if resp[0] != '3':
Collin Winterce36ad82007-08-30 01:19:48 +0000521 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000522 return self.voidcmd('RNTO ' + toname)
Guido van Rossuma61bdeb1995-10-11 17:36:31 +0000523
Tim Peters88869f92001-01-14 23:36:06 +0000524 def delete(self, filename):
525 '''Delete a file.'''
526 resp = self.sendcmd('DELE ' + filename)
527 if resp[:3] in ('250', '200'):
528 return resp
Tim Peters88869f92001-01-14 23:36:06 +0000529 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000530 raise error_reply(resp)
Guido van Rossum02cf5821993-05-17 08:00:02 +0000531
Tim Peters88869f92001-01-14 23:36:06 +0000532 def cwd(self, dirname):
533 '''Change to a directory.'''
534 if dirname == '..':
535 try:
536 return self.voidcmd('CDUP')
Guido van Rossumb940e112007-01-10 16:19:56 +0000537 except error_perm as msg:
Martin v. Löwisb5255112002-03-10 15:59:58 +0000538 if msg.args[0][:3] != '500':
539 raise
Tim Peters88869f92001-01-14 23:36:06 +0000540 elif dirname == '':
541 dirname = '.' # does nothing, but could return error
542 cmd = 'CWD ' + dirname
543 return self.voidcmd(cmd)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000544
Tim Peters88869f92001-01-14 23:36:06 +0000545 def size(self, filename):
546 '''Retrieve the size of a file.'''
Christian Heimes1af737c2008-01-23 08:24:23 +0000547 # The SIZE command is defined in RFC-3659
Tim Peters88869f92001-01-14 23:36:06 +0000548 resp = self.sendcmd('SIZE ' + filename)
549 if resp[:3] == '213':
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000550 s = resp[3:].strip()
551 try:
552 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000553 except (OverflowError, ValueError):
Guido van Rossume2a383d2007-01-15 16:59:06 +0000554 return int(s)
Guido van Rossumc567c601992-11-05 22:22:37 +0000555
Tim Peters88869f92001-01-14 23:36:06 +0000556 def mkd(self, dirname):
557 '''Make a directory, return its full pathname.'''
558 resp = self.sendcmd('MKD ' + dirname)
559 return parse257(resp)
Guido van Rossum98245091998-02-19 21:15:44 +0000560
Tim Peters88869f92001-01-14 23:36:06 +0000561 def rmd(self, dirname):
562 '''Remove a directory.'''
563 return self.voidcmd('RMD ' + dirname)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000564
Tim Peters88869f92001-01-14 23:36:06 +0000565 def pwd(self):
566 '''Return current working directory.'''
567 resp = self.sendcmd('PWD')
568 return parse257(resp)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000569
Tim Peters88869f92001-01-14 23:36:06 +0000570 def quit(self):
571 '''Quit, and close the connection.'''
572 resp = self.voidcmd('QUIT')
573 self.close()
574 return resp
575
576 def close(self):
577 '''Close the connection without assuming anything about it.'''
Fred Drake9c98a422001-02-28 21:46:37 +0000578 if self.file:
579 self.file.close()
580 self.sock.close()
581 self.file = self.sock = None
Guido van Rossumc567c601992-11-05 22:22:37 +0000582
583
Guido van Rossumacfb82a1997-10-22 20:49:52 +0000584_150_re = None
Fred Drake4de02d91997-01-10 18:26:09 +0000585
586def parse150(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000587 '''Parse the '150' response for a RETR request.
588 Returns the expected transfer size or None; size is not guaranteed to
589 be present in the 150 message.
590 '''
591 if resp[:3] != '150':
Collin Winterce36ad82007-08-30 01:19:48 +0000592 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000593 global _150_re
594 if _150_re is None:
595 import re
Antoine Pitroufd036452008-08-19 17:56:33 +0000596 _150_re = re.compile(
597 "150 .* \((\d+) bytes\)", re.IGNORECASE | re.ASCII)
Tim Peters88869f92001-01-14 23:36:06 +0000598 m = _150_re.match(resp)
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000599 if not m:
600 return None
601 s = m.group(1)
602 try:
603 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000604 except (OverflowError, ValueError):
Guido van Rossume2a383d2007-01-15 16:59:06 +0000605 return int(s)
Fred Drake4de02d91997-01-10 18:26:09 +0000606
607
Guido van Rossum70297d32001-08-17 17:24:29 +0000608_227_re = None
609
Guido van Rossumd2560b01996-05-28 23:41:25 +0000610def parse227(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000611 '''Parse the '227' response for a PASV request.
612 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
613 Return ('host.addr.as.numbers', port#) tuple.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000614
Tim Peters88869f92001-01-14 23:36:06 +0000615 if resp[:3] != '227':
Collin Winterce36ad82007-08-30 01:19:48 +0000616 raise error_reply(resp)
Guido van Rossum70297d32001-08-17 17:24:29 +0000617 global _227_re
618 if _227_re is None:
619 import re
Antoine Pitroufd036452008-08-19 17:56:33 +0000620 _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)', re.ASCII)
Guido van Rossum70297d32001-08-17 17:24:29 +0000621 m = _227_re.search(resp)
622 if not m:
Collin Winterce36ad82007-08-30 01:19:48 +0000623 raise error_proto(resp)
Guido van Rossum70297d32001-08-17 17:24:29 +0000624 numbers = m.groups()
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000625 host = '.'.join(numbers[:4])
626 port = (int(numbers[4]) << 8) + int(numbers[5])
Tim Peters88869f92001-01-14 23:36:06 +0000627 return host, port
Guido van Rossumd2560b01996-05-28 23:41:25 +0000628
629
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000630def parse229(resp, peer):
631 '''Parse the '229' response for a EPSV request.
632 Raises error_proto if it does not contain '(|||port|)'
633 Return ('host.addr.as.numbers', port#) tuple.'''
634
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000635 if resp[:3] != '229':
Collin Winterce36ad82007-08-30 01:19:48 +0000636 raise error_reply(resp)
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000637 left = resp.find('(')
Collin Winterce36ad82007-08-30 01:19:48 +0000638 if left < 0: raise error_proto(resp)
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000639 right = resp.find(')', left + 1)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000640 if right < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000641 raise error_proto(resp) # should contain '(|||port|)'
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000642 if resp[left + 1] != resp[right - 1]:
Collin Winterce36ad82007-08-30 01:19:48 +0000643 raise error_proto(resp)
Walter Dörwalda401ae42002-06-03 10:41:45 +0000644 parts = resp[left + 1:right].split(resp[left+1])
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000645 if len(parts) != 5:
Collin Winterce36ad82007-08-30 01:19:48 +0000646 raise error_proto(resp)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000647 host = peer[0]
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000648 port = int(parts[3])
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000649 return host, port
650
651
Guido van Rossumc567c601992-11-05 22:22:37 +0000652def parse257(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000653 '''Parse the '257' response for a MKD or PWD request.
654 This is a response to a MKD or PWD request: a directory name.
655 Returns the directoryname in the 257 reply.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000656
Tim Peters88869f92001-01-14 23:36:06 +0000657 if resp[:3] != '257':
Collin Winterce36ad82007-08-30 01:19:48 +0000658 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000659 if resp[3:5] != ' "':
660 return '' # Not compliant to RFC 959, but UNIX ftpd does this
661 dirname = ''
662 i = 5
663 n = len(resp)
664 while i < n:
665 c = resp[i]
666 i = i+1
667 if c == '"':
668 if i >= n or resp[i] != '"':
669 break
670 i = i+1
671 dirname = dirname + c
672 return dirname
Guido van Rossum1115ab21992-11-04 15:51:30 +0000673
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000674
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000675def print_line(line):
Tim Peters88869f92001-01-14 23:36:06 +0000676 '''Default retrlines callback to print a line.'''
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000677 print(line)
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000678
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000679
Guido van Rossumd2560b01996-05-28 23:41:25 +0000680def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
Tim Peters88869f92001-01-14 23:36:06 +0000681 '''Copy file from one FTP-instance to another.'''
682 if not targetname: targetname = sourcename
683 type = 'TYPE ' + type
684 source.voidcmd(type)
685 target.voidcmd(type)
686 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
687 target.sendport(sourcehost, sourceport)
688 # RFC 959: the user must "listen" [...] BEFORE sending the
689 # transfer request.
690 # So: STOR before RETR, because here the target is a "user".
691 treply = target.sendcmd('STOR ' + targetname)
692 if treply[:3] not in ('125', '150'): raise error_proto # RFC 959
693 sreply = source.sendcmd('RETR ' + sourcename)
694 if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959
695 source.voidresp()
696 target.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000697
Tim Peters88869f92001-01-14 23:36:06 +0000698
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000699class Netrc:
Tim Peters88869f92001-01-14 23:36:06 +0000700 """Class to parse & provide access to 'netrc' format files.
Fred Drake475d51d1997-06-24 22:02:54 +0000701
Tim Peters88869f92001-01-14 23:36:06 +0000702 See the netrc(4) man page for information on the file format.
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000703
Tim Peters88869f92001-01-14 23:36:06 +0000704 WARNING: This class is obsolete -- use module netrc instead.
Guido van Rossumc822a451998-12-22 16:49:16 +0000705
Tim Peters88869f92001-01-14 23:36:06 +0000706 """
707 __defuser = None
708 __defpasswd = None
709 __defacct = None
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000710
Tim Peters88869f92001-01-14 23:36:06 +0000711 def __init__(self, filename=None):
Raymond Hettinger094662a2002-06-01 01:29:16 +0000712 if filename is None:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000713 if "HOME" in os.environ:
Tim Peters88869f92001-01-14 23:36:06 +0000714 filename = os.path.join(os.environ["HOME"],
715 ".netrc")
716 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000717 raise IOError("specify file to load or set $HOME")
Tim Peters88869f92001-01-14 23:36:06 +0000718 self.__hosts = {}
719 self.__macros = {}
720 fp = open(filename, "r")
721 in_macro = 0
722 while 1:
723 line = fp.readline()
724 if not line: break
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000725 if in_macro and line.strip():
Tim Peters88869f92001-01-14 23:36:06 +0000726 macro_lines.append(line)
727 continue
728 elif in_macro:
729 self.__macros[macro_name] = tuple(macro_lines)
730 in_macro = 0
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000731 words = line.split()
Tim Peters88869f92001-01-14 23:36:06 +0000732 host = user = passwd = acct = None
733 default = 0
734 i = 0
735 while i < len(words):
736 w1 = words[i]
737 if i+1 < len(words):
738 w2 = words[i + 1]
739 else:
740 w2 = None
741 if w1 == 'default':
742 default = 1
743 elif w1 == 'machine' and w2:
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000744 host = w2.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000745 i = i + 1
746 elif w1 == 'login' and w2:
747 user = w2
748 i = i + 1
749 elif w1 == 'password' and w2:
750 passwd = w2
751 i = i + 1
752 elif w1 == 'account' and w2:
753 acct = w2
754 i = i + 1
755 elif w1 == 'macdef' and w2:
756 macro_name = w2
757 macro_lines = []
758 in_macro = 1
759 break
760 i = i + 1
761 if default:
762 self.__defuser = user or self.__defuser
763 self.__defpasswd = passwd or self.__defpasswd
764 self.__defacct = acct or self.__defacct
765 if host:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000766 if host in self.__hosts:
Tim Peters88869f92001-01-14 23:36:06 +0000767 ouser, opasswd, oacct = \
768 self.__hosts[host]
769 user = user or ouser
770 passwd = passwd or opasswd
771 acct = acct or oacct
772 self.__hosts[host] = user, passwd, acct
773 fp.close()
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000774
Tim Peters88869f92001-01-14 23:36:06 +0000775 def get_hosts(self):
776 """Return a list of hosts mentioned in the .netrc file."""
777 return self.__hosts.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000778
Tim Peters88869f92001-01-14 23:36:06 +0000779 def get_account(self, host):
780 """Returns login information for the named host.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000781
Tim Peters88869f92001-01-14 23:36:06 +0000782 The return value is a triple containing userid,
783 password, and the accounting field.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000784
Tim Peters88869f92001-01-14 23:36:06 +0000785 """
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000786 host = host.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000787 user = passwd = acct = None
Raymond Hettinger54f02222002-06-01 14:18:47 +0000788 if host in self.__hosts:
Tim Peters88869f92001-01-14 23:36:06 +0000789 user, passwd, acct = self.__hosts[host]
790 user = user or self.__defuser
791 passwd = passwd or self.__defpasswd
792 acct = acct or self.__defacct
793 return user, passwd, acct
Guido van Rossum8ca84201998-03-26 20:56:10 +0000794
Tim Peters88869f92001-01-14 23:36:06 +0000795 def get_macros(self):
796 """Return a list of all defined macro names."""
797 return self.__macros.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000798
Tim Peters88869f92001-01-14 23:36:06 +0000799 def get_macro(self, macro):
800 """Return a sequence of lines which define a named macro."""
801 return self.__macros[macro]
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000802
Fred Drake475d51d1997-06-24 22:02:54 +0000803
Tim Peters88869f92001-01-14 23:36:06 +0000804
Guido van Rossum1115ab21992-11-04 15:51:30 +0000805def test():
Tim Peters88869f92001-01-14 23:36:06 +0000806 '''Test program.
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000807 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
808
809 -d dir
810 -l list
811 -p password
812 '''
813
814 if len(sys.argv) < 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000815 print(test.__doc__)
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000816 sys.exit(0)
Guido van Rossumd2560b01996-05-28 23:41:25 +0000817
Tim Peters88869f92001-01-14 23:36:06 +0000818 debugging = 0
819 rcfile = None
820 while sys.argv[1] == '-d':
821 debugging = debugging+1
822 del sys.argv[1]
823 if sys.argv[1][:2] == '-r':
824 # get name of alternate ~/.netrc file:
825 rcfile = sys.argv[1][2:]
826 del sys.argv[1]
827 host = sys.argv[1]
828 ftp = FTP(host)
829 ftp.set_debuglevel(debugging)
830 userid = passwd = acct = ''
831 try:
832 netrc = Netrc(rcfile)
833 except IOError:
834 if rcfile is not None:
835 sys.stderr.write("Could not open account file"
836 " -- using anonymous login.")
837 else:
838 try:
839 userid, passwd, acct = netrc.get_account(host)
840 except KeyError:
841 # no account for host
842 sys.stderr.write(
843 "No account -- using anonymous login.")
844 ftp.login(userid, passwd, acct)
845 for file in sys.argv[2:]:
846 if file[:2] == '-l':
847 ftp.dir(file[2:])
848 elif file[:2] == '-d':
849 cmd = 'CWD'
850 if file[2:]: cmd = cmd + ' ' + file[2:]
851 resp = ftp.sendcmd(cmd)
852 elif file == '-p':
853 ftp.set_pasv(not ftp.passiveserver)
854 else:
855 ftp.retrbinary('RETR ' + file, \
856 sys.stdout.write, 1024)
857 ftp.quit()
Guido van Rossum221ec0b1995-08-04 04:39:30 +0000858
859
860if __name__ == '__main__':
Tim Peters88869f92001-01-14 23:36:06 +0000861 test()