blob: af213f30313ff7f7d154c7fbd024e468d6648ebd [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.'''
Giampaolo Rodola'0b5c21f2011-05-07 19:03:47 +0200235 line = b'ABOR' + B_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)
Giampaolo Rodola'0b5c21f2011-05-07 19:03:47 +0200241 return resp
Guido van Rossumd3166071993-05-24 14:16:22 +0000242
Tim Peters88869f92001-01-14 23:36:06 +0000243 def sendcmd(self, cmd):
244 '''Send a command and return the response.'''
245 self.putcmd(cmd)
246 return self.getresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000247
Tim Peters88869f92001-01-14 23:36:06 +0000248 def voidcmd(self, cmd):
249 """Send a command and expect a response beginning with '2'."""
250 self.putcmd(cmd)
251 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000252
Tim Peters88869f92001-01-14 23:36:06 +0000253 def sendport(self, host, port):
254 '''Send a PORT command with the current host and the given
255 port number.
256 '''
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000257 hbytes = host.split('.')
Benjamin Peterson3a53fbb2008-09-27 22:04:16 +0000258 pbytes = [repr(port//256), repr(port%256)]
Tim Peters88869f92001-01-14 23:36:06 +0000259 bytes = hbytes + pbytes
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000260 cmd = 'PORT ' + ','.join(bytes)
Tim Peters88869f92001-01-14 23:36:06 +0000261 return self.voidcmd(cmd)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000262
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000263 def sendeprt(self, host, port):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000264 '''Send a EPRT command with the current host and the given port number.'''
265 af = 0
266 if self.af == socket.AF_INET:
267 af = 1
268 if self.af == socket.AF_INET6:
269 af = 2
270 if af == 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000271 raise error_proto('unsupported address family')
Walter Dörwald70a6b492004-02-12 17:35:32 +0000272 fields = ['', repr(af), host, repr(port), '']
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000273 cmd = 'EPRT ' + '|'.join(fields)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000274 return self.voidcmd(cmd)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000275
Tim Peters88869f92001-01-14 23:36:06 +0000276 def makeport(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000277 '''Create a new socket and send a PORT command for it.'''
Martin v. Löwis2ad25692001-07-31 08:40:21 +0000278 msg = "getaddrinfo returns an empty list"
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000279 sock = None
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000280 for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
281 af, socktype, proto, canonname, sa = res
282 try:
283 sock = socket.socket(af, socktype, proto)
284 sock.bind(sa)
Guido van Rossumb940e112007-01-10 16:19:56 +0000285 except socket.error as msg:
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000286 if sock:
287 sock.close()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000288 sock = None
289 continue
290 break
291 if not sock:
Collin Winterce36ad82007-08-30 01:19:48 +0000292 raise socket.error(msg)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000293 sock.listen(1)
294 port = sock.getsockname()[1] # Get proper port
295 host = self.sock.getsockname()[0] # Get proper host
296 if self.af == socket.AF_INET:
297 resp = self.sendport(host, port)
298 else:
299 resp = self.sendeprt(host, port)
Giampaolo Rodolàf5279402010-04-19 22:10:56 +0000300 if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
301 sock.settimeout(self.timeout)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000302 return sock
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000303
304 def makepasv(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000305 if self.af == socket.AF_INET:
306 host, port = parse227(self.sendcmd('PASV'))
307 else:
308 host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
309 return host, port
Guido van Rossum1115ab21992-11-04 15:51:30 +0000310
Tim Peters88869f92001-01-14 23:36:06 +0000311 def ntransfercmd(self, cmd, rest=None):
312 """Initiate a transfer over the data connection.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000313
Tim Peters88869f92001-01-14 23:36:06 +0000314 If the transfer is active, send a port command and the
315 transfer command, and accept the connection. If the server is
316 passive, send a pasv command, connect to it, and start the
317 transfer command. Either way, return the socket for the
318 connection and the expected size of the transfer. The
319 expected size may be None if it could not be determined.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000320
Tim Peters88869f92001-01-14 23:36:06 +0000321 Optional `rest' argument can be a string that is sent as the
Christian Heimes1af737c2008-01-23 08:24:23 +0000322 argument to a REST command. This is essentially a server
Tim Peters88869f92001-01-14 23:36:06 +0000323 marker used to tell the server to skip over any data up to the
324 given marker.
325 """
326 size = None
327 if self.passiveserver:
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000328 host, port = self.makepasv()
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000329 conn = socket.create_connection((host, port), self.timeout)
Tim Peters88869f92001-01-14 23:36:06 +0000330 if rest is not None:
331 self.sendcmd("REST %s" % rest)
332 resp = self.sendcmd(cmd)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000333 # Some servers apparently send a 200 reply to
334 # a LIST or STOR command, before the 150 reply
335 # (and way before the 226 reply). This seems to
336 # be in violation of the protocol (which only allows
337 # 1xx or error messages for LIST), so we just discard
338 # this response.
339 if resp[0] == '2':
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000340 resp = self.getresp()
Tim Peters88869f92001-01-14 23:36:06 +0000341 if resp[0] != '1':
Collin Winterce36ad82007-08-30 01:19:48 +0000342 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000343 else:
344 sock = self.makeport()
345 if rest is not None:
346 self.sendcmd("REST %s" % rest)
347 resp = self.sendcmd(cmd)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000348 # See above.
349 if resp[0] == '2':
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000350 resp = self.getresp()
Tim Peters88869f92001-01-14 23:36:06 +0000351 if resp[0] != '1':
Collin Winterce36ad82007-08-30 01:19:48 +0000352 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000353 conn, sockaddr = sock.accept()
Giampaolo Rodolàf5279402010-04-19 22:10:56 +0000354 if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
355 conn.settimeout(self.timeout)
Tim Peters88869f92001-01-14 23:36:06 +0000356 if resp[:3] == '150':
357 # this is conditional in case we received a 125
358 size = parse150(resp)
359 return conn, size
Fred Drake4de02d91997-01-10 18:26:09 +0000360
Tim Peters88869f92001-01-14 23:36:06 +0000361 def transfercmd(self, cmd, rest=None):
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000362 """Like ntransfercmd() but returns only the socket."""
Tim Peters88869f92001-01-14 23:36:06 +0000363 return self.ntransfercmd(cmd, rest)[0]
Guido van Rossumc567c601992-11-05 22:22:37 +0000364
Tim Peters88869f92001-01-14 23:36:06 +0000365 def login(self, user = '', passwd = '', acct = ''):
366 '''Login, default anonymous.'''
367 if not user: user = 'anonymous'
368 if not passwd: passwd = ''
369 if not acct: acct = ''
370 if user == 'anonymous' and passwd in ('', '-'):
Tim Peterse4418602002-02-16 07:34:19 +0000371 # If there is no anonymous ftp password specified
372 # then we'll just use anonymous@
373 # We don't send any other thing because:
374 # - We want to remain anonymous
375 # - We want to stop SPAM
376 # - We don't want to let ftp sites to discriminate by the user,
377 # host or country.
Guido van Rossumc33e0772001-12-28 20:54:28 +0000378 passwd = passwd + 'anonymous@'
Tim Peters88869f92001-01-14 23:36:06 +0000379 resp = self.sendcmd('USER ' + user)
380 if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
381 if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
382 if resp[0] != '2':
Collin Winterce36ad82007-08-30 01:19:48 +0000383 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000384 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000385
Tim Peters88869f92001-01-14 23:36:06 +0000386 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
Christian Heimes1af737c2008-01-23 08:24:23 +0000387 """Retrieve data in binary mode. A new port is created for you.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000388
Christian Heimes1af737c2008-01-23 08:24:23 +0000389 Args:
390 cmd: A RETR command.
391 callback: A single parameter callable to be called on each
392 block of data read.
393 blocksize: The maximum number of bytes to read from the
394 socket at one time. [default: 8192]
395 rest: Passed to transfercmd(). [default: None]
Guido van Rossum1115ab21992-11-04 15:51:30 +0000396
Christian Heimes1af737c2008-01-23 08:24:23 +0000397 Returns:
398 The response code.
Tim Peters88869f92001-01-14 23:36:06 +0000399 """
400 self.voidcmd('TYPE I')
401 conn = self.transfercmd(cmd, rest)
402 while 1:
403 data = conn.recv(blocksize)
404 if not data:
405 break
406 callback(data)
407 conn.close()
408 return self.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000409
Tim Peters88869f92001-01-14 23:36:06 +0000410 def retrlines(self, cmd, callback = None):
Christian Heimes1af737c2008-01-23 08:24:23 +0000411 """Retrieve data in line mode. A new port is created for you.
412
413 Args:
414 cmd: A RETR, LIST, NLST, or MLSD command.
415 callback: An optional single parameter callable that is called
416 for each line with the trailing CRLF stripped.
417 [default: print_line()]
418
419 Returns:
420 The response code.
421 """
Raymond Hettingere874fc32002-05-12 05:53:51 +0000422 if callback is None: callback = print_line
Tim Peters88869f92001-01-14 23:36:06 +0000423 resp = self.sendcmd('TYPE A')
424 conn = self.transfercmd(cmd)
Guido van Rossum52b89762007-07-17 20:45:57 +0000425 fp = conn.makefile('r', encoding=self.encoding)
Tim Peters88869f92001-01-14 23:36:06 +0000426 while 1:
427 line = fp.readline()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000428 if self.debugging > 2: print('*retr*', repr(line))
Tim Peters88869f92001-01-14 23:36:06 +0000429 if not line:
430 break
431 if line[-2:] == CRLF:
432 line = line[:-2]
433 elif line[-1:] == '\n':
434 line = line[:-1]
435 callback(line)
436 fp.close()
437 conn.close()
438 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000439
Christian Heimes1af737c2008-01-23 08:24:23 +0000440 def storbinary(self, cmd, fp, blocksize=8192, callback=None):
441 """Store a file in binary mode. A new port is created for you.
442
443 Args:
444 cmd: A STOR command.
445 fp: A file-like object with a read(num_bytes) method.
446 blocksize: The maximum data size to read from fp and send over
447 the connection at once. [default: 8192]
448 callback: An optional single parameter callable that is called on
449 on each block of data after it is sent. [default: None]
450
451 Returns:
452 The response code.
453 """
Tim Peters88869f92001-01-14 23:36:06 +0000454 self.voidcmd('TYPE I')
455 conn = self.transfercmd(cmd)
456 while 1:
457 buf = fp.read(blocksize)
458 if not buf: break
Martin v. Löwise12454f2002-02-16 23:06:19 +0000459 conn.sendall(buf)
Christian Heimes1af737c2008-01-23 08:24:23 +0000460 if callback: callback(buf)
Tim Peters88869f92001-01-14 23:36:06 +0000461 conn.close()
462 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000463
Christian Heimes1af737c2008-01-23 08:24:23 +0000464 def storlines(self, cmd, fp, callback=None):
465 """Store a file in line mode. A new port is created for you.
466
467 Args:
468 cmd: A STOR command.
469 fp: A file-like object with a readline() method.
470 callback: An optional single parameter callable that is called on
471 on each line after it is sent. [default: None]
472
473 Returns:
474 The response code.
475 """
Tim Peters88869f92001-01-14 23:36:06 +0000476 self.voidcmd('TYPE A')
477 conn = self.transfercmd(cmd)
478 while 1:
479 buf = fp.readline()
480 if not buf: break
Benjamin Petersonbe17a112008-09-27 21:49:47 +0000481 if buf[-2:] != B_CRLF:
482 if buf[-1] in B_CRLF: buf = buf[:-1]
483 buf = buf + B_CRLF
Martin v. Löwise12454f2002-02-16 23:06:19 +0000484 conn.sendall(buf)
Christian Heimes1af737c2008-01-23 08:24:23 +0000485 if callback: callback(buf)
Tim Peters88869f92001-01-14 23:36:06 +0000486 conn.close()
487 return self.voidresp()
Guido van Rossum0eaa74b1996-01-25 18:37:21 +0000488
Tim Peters88869f92001-01-14 23:36:06 +0000489 def acct(self, password):
490 '''Send new account name.'''
491 cmd = 'ACCT ' + password
492 return self.voidcmd(cmd)
Guido van Rossumc567c601992-11-05 22:22:37 +0000493
Tim Peters88869f92001-01-14 23:36:06 +0000494 def nlst(self, *args):
495 '''Return a list of files in a given directory (default the current).'''
496 cmd = 'NLST'
497 for arg in args:
498 cmd = cmd + (' ' + arg)
499 files = []
500 self.retrlines(cmd, files.append)
501 return files
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000502
Tim Peters88869f92001-01-14 23:36:06 +0000503 def dir(self, *args):
504 '''List a directory in long form.
505 By default list current directory to stdout.
506 Optional last argument is callback function; all
507 non-empty arguments before it are concatenated to the
508 LIST command. (This *should* only be used for a pathname.)'''
509 cmd = 'LIST'
510 func = None
511 if args[-1:] and type(args[-1]) != type(''):
512 args, func = args[:-1], args[-1]
513 for arg in args:
514 if arg:
515 cmd = cmd + (' ' + arg)
516 self.retrlines(cmd, func)
Guido van Rossumc567c601992-11-05 22:22:37 +0000517
Tim Peters88869f92001-01-14 23:36:06 +0000518 def rename(self, fromname, toname):
519 '''Rename a file.'''
520 resp = self.sendcmd('RNFR ' + fromname)
521 if resp[0] != '3':
Collin Winterce36ad82007-08-30 01:19:48 +0000522 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000523 return self.voidcmd('RNTO ' + toname)
Guido van Rossuma61bdeb1995-10-11 17:36:31 +0000524
Tim Peters88869f92001-01-14 23:36:06 +0000525 def delete(self, filename):
526 '''Delete a file.'''
527 resp = self.sendcmd('DELE ' + filename)
528 if resp[:3] in ('250', '200'):
529 return resp
Tim Peters88869f92001-01-14 23:36:06 +0000530 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000531 raise error_reply(resp)
Guido van Rossum02cf5821993-05-17 08:00:02 +0000532
Tim Peters88869f92001-01-14 23:36:06 +0000533 def cwd(self, dirname):
534 '''Change to a directory.'''
535 if dirname == '..':
536 try:
537 return self.voidcmd('CDUP')
Guido van Rossumb940e112007-01-10 16:19:56 +0000538 except error_perm as msg:
Martin v. Löwisb5255112002-03-10 15:59:58 +0000539 if msg.args[0][:3] != '500':
540 raise
Tim Peters88869f92001-01-14 23:36:06 +0000541 elif dirname == '':
542 dirname = '.' # does nothing, but could return error
543 cmd = 'CWD ' + dirname
544 return self.voidcmd(cmd)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000545
Tim Peters88869f92001-01-14 23:36:06 +0000546 def size(self, filename):
547 '''Retrieve the size of a file.'''
Christian Heimes1af737c2008-01-23 08:24:23 +0000548 # The SIZE command is defined in RFC-3659
Tim Peters88869f92001-01-14 23:36:06 +0000549 resp = self.sendcmd('SIZE ' + filename)
550 if resp[:3] == '213':
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000551 s = resp[3:].strip()
552 try:
553 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000554 except (OverflowError, ValueError):
Guido van Rossume2a383d2007-01-15 16:59:06 +0000555 return int(s)
Guido van Rossumc567c601992-11-05 22:22:37 +0000556
Tim Peters88869f92001-01-14 23:36:06 +0000557 def mkd(self, dirname):
558 '''Make a directory, return its full pathname.'''
559 resp = self.sendcmd('MKD ' + dirname)
560 return parse257(resp)
Guido van Rossum98245091998-02-19 21:15:44 +0000561
Tim Peters88869f92001-01-14 23:36:06 +0000562 def rmd(self, dirname):
563 '''Remove a directory.'''
564 return self.voidcmd('RMD ' + dirname)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000565
Tim Peters88869f92001-01-14 23:36:06 +0000566 def pwd(self):
567 '''Return current working directory.'''
568 resp = self.sendcmd('PWD')
569 return parse257(resp)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000570
Tim Peters88869f92001-01-14 23:36:06 +0000571 def quit(self):
572 '''Quit, and close the connection.'''
573 resp = self.voidcmd('QUIT')
574 self.close()
575 return resp
576
577 def close(self):
578 '''Close the connection without assuming anything about it.'''
Fred Drake9c98a422001-02-28 21:46:37 +0000579 if self.file:
580 self.file.close()
581 self.sock.close()
582 self.file = self.sock = None
Guido van Rossumc567c601992-11-05 22:22:37 +0000583
584
Guido van Rossumacfb82a1997-10-22 20:49:52 +0000585_150_re = None
Fred Drake4de02d91997-01-10 18:26:09 +0000586
587def parse150(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000588 '''Parse the '150' response for a RETR request.
589 Returns the expected transfer size or None; size is not guaranteed to
590 be present in the 150 message.
591 '''
592 if resp[:3] != '150':
Collin Winterce36ad82007-08-30 01:19:48 +0000593 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000594 global _150_re
595 if _150_re is None:
596 import re
Antoine Pitroufd036452008-08-19 17:56:33 +0000597 _150_re = re.compile(
598 "150 .* \((\d+) bytes\)", re.IGNORECASE | re.ASCII)
Tim Peters88869f92001-01-14 23:36:06 +0000599 m = _150_re.match(resp)
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000600 if not m:
601 return None
602 s = m.group(1)
603 try:
604 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000605 except (OverflowError, ValueError):
Guido van Rossume2a383d2007-01-15 16:59:06 +0000606 return int(s)
Fred Drake4de02d91997-01-10 18:26:09 +0000607
608
Guido van Rossum70297d32001-08-17 17:24:29 +0000609_227_re = None
610
Guido van Rossumd2560b01996-05-28 23:41:25 +0000611def parse227(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000612 '''Parse the '227' response for a PASV request.
613 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
614 Return ('host.addr.as.numbers', port#) tuple.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000615
Tim Peters88869f92001-01-14 23:36:06 +0000616 if resp[:3] != '227':
Collin Winterce36ad82007-08-30 01:19:48 +0000617 raise error_reply(resp)
Guido van Rossum70297d32001-08-17 17:24:29 +0000618 global _227_re
619 if _227_re is None:
620 import re
Antoine Pitroufd036452008-08-19 17:56:33 +0000621 _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)', re.ASCII)
Guido van Rossum70297d32001-08-17 17:24:29 +0000622 m = _227_re.search(resp)
623 if not m:
Collin Winterce36ad82007-08-30 01:19:48 +0000624 raise error_proto(resp)
Guido van Rossum70297d32001-08-17 17:24:29 +0000625 numbers = m.groups()
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000626 host = '.'.join(numbers[:4])
627 port = (int(numbers[4]) << 8) + int(numbers[5])
Tim Peters88869f92001-01-14 23:36:06 +0000628 return host, port
Guido van Rossumd2560b01996-05-28 23:41:25 +0000629
630
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000631def parse229(resp, peer):
632 '''Parse the '229' response for a EPSV request.
633 Raises error_proto if it does not contain '(|||port|)'
634 Return ('host.addr.as.numbers', port#) tuple.'''
635
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000636 if resp[:3] != '229':
Collin Winterce36ad82007-08-30 01:19:48 +0000637 raise error_reply(resp)
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000638 left = resp.find('(')
Collin Winterce36ad82007-08-30 01:19:48 +0000639 if left < 0: raise error_proto(resp)
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000640 right = resp.find(')', left + 1)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000641 if right < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000642 raise error_proto(resp) # should contain '(|||port|)'
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000643 if resp[left + 1] != resp[right - 1]:
Collin Winterce36ad82007-08-30 01:19:48 +0000644 raise error_proto(resp)
Walter Dörwalda401ae42002-06-03 10:41:45 +0000645 parts = resp[left + 1:right].split(resp[left+1])
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000646 if len(parts) != 5:
Collin Winterce36ad82007-08-30 01:19:48 +0000647 raise error_proto(resp)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000648 host = peer[0]
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000649 port = int(parts[3])
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000650 return host, port
651
652
Guido van Rossumc567c601992-11-05 22:22:37 +0000653def parse257(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000654 '''Parse the '257' response for a MKD or PWD request.
655 This is a response to a MKD or PWD request: a directory name.
656 Returns the directoryname in the 257 reply.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000657
Tim Peters88869f92001-01-14 23:36:06 +0000658 if resp[:3] != '257':
Collin Winterce36ad82007-08-30 01:19:48 +0000659 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000660 if resp[3:5] != ' "':
661 return '' # Not compliant to RFC 959, but UNIX ftpd does this
662 dirname = ''
663 i = 5
664 n = len(resp)
665 while i < n:
666 c = resp[i]
667 i = i+1
668 if c == '"':
669 if i >= n or resp[i] != '"':
670 break
671 i = i+1
672 dirname = dirname + c
673 return dirname
Guido van Rossum1115ab21992-11-04 15:51:30 +0000674
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000675
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000676def print_line(line):
Tim Peters88869f92001-01-14 23:36:06 +0000677 '''Default retrlines callback to print a line.'''
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000678 print(line)
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000679
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000680
Guido van Rossumd2560b01996-05-28 23:41:25 +0000681def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
Tim Peters88869f92001-01-14 23:36:06 +0000682 '''Copy file from one FTP-instance to another.'''
683 if not targetname: targetname = sourcename
684 type = 'TYPE ' + type
685 source.voidcmd(type)
686 target.voidcmd(type)
687 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
688 target.sendport(sourcehost, sourceport)
689 # RFC 959: the user must "listen" [...] BEFORE sending the
690 # transfer request.
691 # So: STOR before RETR, because here the target is a "user".
692 treply = target.sendcmd('STOR ' + targetname)
693 if treply[:3] not in ('125', '150'): raise error_proto # RFC 959
694 sreply = source.sendcmd('RETR ' + sourcename)
695 if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959
696 source.voidresp()
697 target.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000698
Tim Peters88869f92001-01-14 23:36:06 +0000699
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000700class Netrc:
Tim Peters88869f92001-01-14 23:36:06 +0000701 """Class to parse & provide access to 'netrc' format files.
Fred Drake475d51d1997-06-24 22:02:54 +0000702
Tim Peters88869f92001-01-14 23:36:06 +0000703 See the netrc(4) man page for information on the file format.
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000704
Tim Peters88869f92001-01-14 23:36:06 +0000705 WARNING: This class is obsolete -- use module netrc instead.
Guido van Rossumc822a451998-12-22 16:49:16 +0000706
Tim Peters88869f92001-01-14 23:36:06 +0000707 """
708 __defuser = None
709 __defpasswd = None
710 __defacct = None
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000711
Tim Peters88869f92001-01-14 23:36:06 +0000712 def __init__(self, filename=None):
Raymond Hettinger094662a2002-06-01 01:29:16 +0000713 if filename is None:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000714 if "HOME" in os.environ:
Tim Peters88869f92001-01-14 23:36:06 +0000715 filename = os.path.join(os.environ["HOME"],
716 ".netrc")
717 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000718 raise IOError("specify file to load or set $HOME")
Tim Peters88869f92001-01-14 23:36:06 +0000719 self.__hosts = {}
720 self.__macros = {}
721 fp = open(filename, "r")
722 in_macro = 0
723 while 1:
724 line = fp.readline()
725 if not line: break
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000726 if in_macro and line.strip():
Tim Peters88869f92001-01-14 23:36:06 +0000727 macro_lines.append(line)
728 continue
729 elif in_macro:
730 self.__macros[macro_name] = tuple(macro_lines)
731 in_macro = 0
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000732 words = line.split()
Tim Peters88869f92001-01-14 23:36:06 +0000733 host = user = passwd = acct = None
734 default = 0
735 i = 0
736 while i < len(words):
737 w1 = words[i]
738 if i+1 < len(words):
739 w2 = words[i + 1]
740 else:
741 w2 = None
742 if w1 == 'default':
743 default = 1
744 elif w1 == 'machine' and w2:
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000745 host = w2.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000746 i = i + 1
747 elif w1 == 'login' and w2:
748 user = w2
749 i = i + 1
750 elif w1 == 'password' and w2:
751 passwd = w2
752 i = i + 1
753 elif w1 == 'account' and w2:
754 acct = w2
755 i = i + 1
756 elif w1 == 'macdef' and w2:
757 macro_name = w2
758 macro_lines = []
759 in_macro = 1
760 break
761 i = i + 1
762 if default:
763 self.__defuser = user or self.__defuser
764 self.__defpasswd = passwd or self.__defpasswd
765 self.__defacct = acct or self.__defacct
766 if host:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000767 if host in self.__hosts:
Tim Peters88869f92001-01-14 23:36:06 +0000768 ouser, opasswd, oacct = \
769 self.__hosts[host]
770 user = user or ouser
771 passwd = passwd or opasswd
772 acct = acct or oacct
773 self.__hosts[host] = user, passwd, acct
774 fp.close()
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000775
Tim Peters88869f92001-01-14 23:36:06 +0000776 def get_hosts(self):
777 """Return a list of hosts mentioned in the .netrc file."""
778 return self.__hosts.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000779
Tim Peters88869f92001-01-14 23:36:06 +0000780 def get_account(self, host):
781 """Returns login information for the named host.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000782
Tim Peters88869f92001-01-14 23:36:06 +0000783 The return value is a triple containing userid,
784 password, and the accounting field.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000785
Tim Peters88869f92001-01-14 23:36:06 +0000786 """
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000787 host = host.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000788 user = passwd = acct = None
Raymond Hettinger54f02222002-06-01 14:18:47 +0000789 if host in self.__hosts:
Tim Peters88869f92001-01-14 23:36:06 +0000790 user, passwd, acct = self.__hosts[host]
791 user = user or self.__defuser
792 passwd = passwd or self.__defpasswd
793 acct = acct or self.__defacct
794 return user, passwd, acct
Guido van Rossum8ca84201998-03-26 20:56:10 +0000795
Tim Peters88869f92001-01-14 23:36:06 +0000796 def get_macros(self):
797 """Return a list of all defined macro names."""
798 return self.__macros.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000799
Tim Peters88869f92001-01-14 23:36:06 +0000800 def get_macro(self, macro):
801 """Return a sequence of lines which define a named macro."""
802 return self.__macros[macro]
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000803
Fred Drake475d51d1997-06-24 22:02:54 +0000804
Tim Peters88869f92001-01-14 23:36:06 +0000805
Guido van Rossum1115ab21992-11-04 15:51:30 +0000806def test():
Tim Peters88869f92001-01-14 23:36:06 +0000807 '''Test program.
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000808 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
809
810 -d dir
811 -l list
812 -p password
813 '''
814
815 if len(sys.argv) < 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000816 print(test.__doc__)
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000817 sys.exit(0)
Guido van Rossumd2560b01996-05-28 23:41:25 +0000818
Tim Peters88869f92001-01-14 23:36:06 +0000819 debugging = 0
820 rcfile = None
821 while sys.argv[1] == '-d':
822 debugging = debugging+1
823 del sys.argv[1]
824 if sys.argv[1][:2] == '-r':
825 # get name of alternate ~/.netrc file:
826 rcfile = sys.argv[1][2:]
827 del sys.argv[1]
828 host = sys.argv[1]
829 ftp = FTP(host)
830 ftp.set_debuglevel(debugging)
831 userid = passwd = acct = ''
832 try:
833 netrc = Netrc(rcfile)
834 except IOError:
835 if rcfile is not None:
836 sys.stderr.write("Could not open account file"
837 " -- using anonymous login.")
838 else:
839 try:
840 userid, passwd, acct = netrc.get_account(host)
841 except KeyError:
842 # no account for host
843 sys.stderr.write(
844 "No account -- using anonymous login.")
845 ftp.login(userid, passwd, acct)
846 for file in sys.argv[2:]:
847 if file[:2] == '-l':
848 ftp.dir(file[2:])
849 elif file[:2] == '-d':
850 cmd = 'CWD'
851 if file[2:]: cmd = cmd + ' ' + file[2:]
852 resp = ftp.sendcmd(cmd)
853 elif file == '-p':
854 ftp.set_pasv(not ftp.passiveserver)
855 else:
856 ftp.retrbinary('RETR ' + file, \
857 sys.stdout.write, 1024)
858 ftp.quit()
Guido van Rossum221ec0b1995-08-04 04:39:30 +0000859
860
861if __name__ == '__main__':
Tim Peters88869f92001-01-14 23:36:06 +0000862 test()