blob: 8813d3c2e624b5ae9cf34c467faccebd9e04018b [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
Guido van Rossumb6775db1994-08-01 11:34:53 +000047
Skip Montanaroeccd02a2001-01-20 23:34:12 +000048__all__ = ["FTP","Netrc"]
Guido van Rossum1115ab21992-11-04 15:51:30 +000049
Guido van Rossumd3166071993-05-24 14:16:22 +000050# Magic number from <socket.h>
Tim Peters88869f92001-01-14 23:36:06 +000051MSG_OOB = 0x1 # Process data out of band
Guido van Rossumd3166071993-05-24 14:16:22 +000052
53
Guido van Rossumc567c601992-11-05 22:22:37 +000054# The standard FTP server control port
Guido van Rossum1115ab21992-11-04 15:51:30 +000055FTP_PORT = 21
Guido van Rossum1115ab21992-11-04 15:51:30 +000056
57
Guido van Rossum21974791992-11-06 13:34:17 +000058# Exception raised when an error or invalid response is received
Fred Drake227b1202000-08-17 05:06:49 +000059class Error(Exception): pass
Tim Peters88869f92001-01-14 23:36:06 +000060class error_reply(Error): pass # unexpected [123]xx reply
61class error_temp(Error): pass # 4xx errors
62class error_perm(Error): pass # 5xx errors
63class error_proto(Error): pass # response does not begin with [1-5]
Guido van Rossum1115ab21992-11-04 15:51:30 +000064
65
Guido van Rossum21974791992-11-06 13:34:17 +000066# All exceptions (hopefully) that may be raised here and that aren't
67# (always) programming errors on our side
Fred Drake227b1202000-08-17 05:06:49 +000068all_errors = (Error, socket.error, IOError, EOFError)
Guido van Rossum21974791992-11-06 13:34:17 +000069
70
Guido van Rossum1115ab21992-11-04 15:51:30 +000071# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
72CRLF = '\r\n'
73
74
Guido van Rossum1115ab21992-11-04 15:51:30 +000075# The class itself
76class FTP:
77
Tim Peters88869f92001-01-14 23:36:06 +000078 '''An FTP client class.
Guido van Rossumd2560b01996-05-28 23:41:25 +000079
Guido van Rossumd8faa362007-04-27 19:54:29 +000080 To create a connection, call the class using these arguments:
81 host, user, passwd, acct, timeout
82
83 The first four arguments are all strings, and have default value ''.
84 timeout must be numeric and defaults to None if not passed,
85 meaning that no timeout will be set on any ftp socket(s)
86 If a timeout is passed, then this is now the default timeout for all ftp
87 socket operations for this instance.
88
Tim Peters88869f92001-01-14 23:36:06 +000089 Then use self.connect() with optional host and port argument.
Guido van Rossumd2560b01996-05-28 23:41:25 +000090
Tim Peters88869f92001-01-14 23:36:06 +000091 To download a file, use ftp.retrlines('RETR ' + filename),
92 or ftp.retrbinary() with slightly different arguments.
93 To upload a file, use ftp.storlines() or ftp.storbinary(),
94 which have an open file as argument (see their definitions
95 below for details).
96 The download/upload functions first issue appropriate TYPE
97 and PORT or PASV commands.
Guido van Rossum52b89762007-07-17 20:45:57 +000098 '''
Guido van Rossumd2560b01996-05-28 23:41:25 +000099
Fred Drake9c98a422001-02-28 21:46:37 +0000100 debugging = 0
101 host = ''
102 port = FTP_PORT
103 sock = None
104 file = None
105 welcome = None
106 passiveserver = 1
Brett Cannon6733d702007-10-08 19:48:15 +0000107 encoding = "latin1"
Fred Drake9c98a422001-02-28 21:46:37 +0000108
Tim Peters88869f92001-01-14 23:36:06 +0000109 # Initialization method (called by class instantiation).
110 # Initialize host to localhost, port to standard ftp port
111 # Optional arguments are host (for connect()),
112 # and user, passwd, acct (for login())
Guido van Rossumd8faa362007-04-27 19:54:29 +0000113 def __init__(self, host='', user='', passwd='', acct='', timeout=None):
114 self.timeout = timeout
Tim Peters88869f92001-01-14 23:36:06 +0000115 if host:
Fred Drake9c98a422001-02-28 21:46:37 +0000116 self.connect(host)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000117 if user:
118 self.login(user, passwd, acct)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000119
Guido van Rossumd8faa362007-04-27 19:54:29 +0000120 def connect(self, host='', port=0, timeout=None):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000121 '''Connect to host. Arguments are:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000122 - host: hostname to connect to (string, default previous host)
123 - port: port to connect to (integer, default previous port)
124 '''
125 if host != '':
126 self.host = host
127 if port > 0:
128 self.port = port
129 if timeout is not None:
130 self.timeout = timeout
131 self.sock = socket.create_connection((self.host, self.port), self.timeout)
132 self.af = self.sock.family
Guido van Rossum52b89762007-07-17 20:45:57 +0000133 self.file = self.sock.makefile('r', encoding=self.encoding)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000134 self.welcome = self.getresp()
135 return self.welcome
Guido van Rossum1115ab21992-11-04 15:51:30 +0000136
Tim Peters88869f92001-01-14 23:36:06 +0000137 def getwelcome(self):
138 '''Get the welcome message from the server.
139 (this is read and squirreled away by connect())'''
140 if self.debugging:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000141 print('*welcome*', self.sanitize(self.welcome))
Tim Peters88869f92001-01-14 23:36:06 +0000142 return self.welcome
Guido van Rossum1115ab21992-11-04 15:51:30 +0000143
Tim Peters88869f92001-01-14 23:36:06 +0000144 def set_debuglevel(self, level):
145 '''Set the debugging level.
146 The required argument level means:
147 0: no debugging output (default)
148 1: print commands and responses but not body text etc.
149 2: also print raw lines read and sent before stripping CR/LF'''
150 self.debugging = level
151 debug = set_debuglevel
Guido van Rossum1115ab21992-11-04 15:51:30 +0000152
Tim Peters88869f92001-01-14 23:36:06 +0000153 def set_pasv(self, val):
154 '''Use passive or active mode for data transfers.
155 With a false argument, use the normal PORT mode,
156 With a true argument, use the PASV command.'''
157 self.passiveserver = val
Guido van Rossumd2560b01996-05-28 23:41:25 +0000158
Tim Peters88869f92001-01-14 23:36:06 +0000159 # Internal: "sanitize" a string for printing
160 def sanitize(self, s):
161 if s[:5] == 'pass ' or s[:5] == 'PASS ':
162 i = len(s)
163 while i > 5 and s[i-1] in '\r\n':
164 i = i-1
165 s = s[:5] + '*'*(i-5) + s[i:]
Walter Dörwald70a6b492004-02-12 17:35:32 +0000166 return repr(s)
Guido van Rossumebaf1041995-05-05 15:54:14 +0000167
Tim Peters88869f92001-01-14 23:36:06 +0000168 # Internal: send one line to the server, appending CRLF
169 def putline(self, line):
170 line = line + CRLF
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000171 if self.debugging > 1: print('*put*', self.sanitize(line))
Guido van Rossum52b89762007-07-17 20:45:57 +0000172 self.sock.sendall(line.encode(self.encoding))
Guido van Rossum1115ab21992-11-04 15:51:30 +0000173
Tim Peters88869f92001-01-14 23:36:06 +0000174 # Internal: send one command to the server (through putline())
175 def putcmd(self, line):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000176 if self.debugging: print('*cmd*', self.sanitize(line))
Tim Peters88869f92001-01-14 23:36:06 +0000177 self.putline(line)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000178
Tim Peters88869f92001-01-14 23:36:06 +0000179 # Internal: return one line from the server, stripping CRLF.
180 # Raise EOFError if the connection is closed
181 def getline(self):
182 line = self.file.readline()
183 if self.debugging > 1:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000184 print('*get*', self.sanitize(line))
Tim Peters88869f92001-01-14 23:36:06 +0000185 if not line: raise EOFError
186 if line[-2:] == CRLF: line = line[:-2]
187 elif line[-1:] in CRLF: line = line[:-1]
188 return line
Guido van Rossum1115ab21992-11-04 15:51:30 +0000189
Tim Peters88869f92001-01-14 23:36:06 +0000190 # Internal: get a response from the server, which may possibly
191 # consist of multiple lines. Return a single string with no
192 # trailing CRLF. If the response consists of multiple lines,
193 # these are separated by '\n' characters in the string
194 def getmultiline(self):
195 line = self.getline()
196 if line[3:4] == '-':
197 code = line[:3]
198 while 1:
199 nextline = self.getline()
200 line = line + ('\n' + nextline)
201 if nextline[:3] == code and \
202 nextline[3:4] != '-':
203 break
204 return line
Guido van Rossum1115ab21992-11-04 15:51:30 +0000205
Tim Peters88869f92001-01-14 23:36:06 +0000206 # Internal: get a response from the server.
207 # Raise various errors if the response indicates an error
208 def getresp(self):
209 resp = self.getmultiline()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000210 if self.debugging: print('*resp*', self.sanitize(resp))
Tim Peters88869f92001-01-14 23:36:06 +0000211 self.lastresp = resp[:3]
212 c = resp[:1]
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000213 if c in ('1', '2', '3'):
214 return resp
Tim Peters88869f92001-01-14 23:36:06 +0000215 if c == '4':
Collin Winterce36ad82007-08-30 01:19:48 +0000216 raise error_temp(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000217 if c == '5':
Collin Winterce36ad82007-08-30 01:19:48 +0000218 raise error_perm(resp)
219 raise error_proto(resp)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000220
Tim Peters88869f92001-01-14 23:36:06 +0000221 def voidresp(self):
222 """Expect a response beginning with '2'."""
223 resp = self.getresp()
224 if resp[0] != '2':
Collin Winterce36ad82007-08-30 01:19:48 +0000225 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000226 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000227
Tim Peters88869f92001-01-14 23:36:06 +0000228 def abort(self):
229 '''Abort a file transfer. Uses out-of-band data.
230 This does not follow the procedure from the RFC to send Telnet
231 IP and Synch; that doesn't seem to work with the servers I've
232 tried. Instead, just send the ABOR command as OOB data.'''
233 line = 'ABOR' + CRLF
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000234 if self.debugging > 1: print('*put urgent*', self.sanitize(line))
Martin v. Löwise12454f2002-02-16 23:06:19 +0000235 self.sock.sendall(line, MSG_OOB)
Tim Peters88869f92001-01-14 23:36:06 +0000236 resp = self.getmultiline()
237 if resp[:3] not in ('426', '226'):
Collin Winterce36ad82007-08-30 01:19:48 +0000238 raise error_proto(resp)
Guido van Rossumd3166071993-05-24 14:16:22 +0000239
Tim Peters88869f92001-01-14 23:36:06 +0000240 def sendcmd(self, cmd):
241 '''Send a command and return the response.'''
242 self.putcmd(cmd)
243 return self.getresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000244
Tim Peters88869f92001-01-14 23:36:06 +0000245 def voidcmd(self, cmd):
246 """Send a command and expect a response beginning with '2'."""
247 self.putcmd(cmd)
248 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000249
Tim Peters88869f92001-01-14 23:36:06 +0000250 def sendport(self, host, port):
251 '''Send a PORT command with the current host and the given
252 port number.
253 '''
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000254 hbytes = host.split('.')
Walter Dörwald70a6b492004-02-12 17:35:32 +0000255 pbytes = [repr(port/256), repr(port%256)]
Tim Peters88869f92001-01-14 23:36:06 +0000256 bytes = hbytes + pbytes
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000257 cmd = 'PORT ' + ','.join(bytes)
Tim Peters88869f92001-01-14 23:36:06 +0000258 return self.voidcmd(cmd)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000259
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000260 def sendeprt(self, host, port):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000261 '''Send a EPRT command with the current host and the given port number.'''
262 af = 0
263 if self.af == socket.AF_INET:
264 af = 1
265 if self.af == socket.AF_INET6:
266 af = 2
267 if af == 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000268 raise error_proto('unsupported address family')
Walter Dörwald70a6b492004-02-12 17:35:32 +0000269 fields = ['', repr(af), host, repr(port), '']
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000270 cmd = 'EPRT ' + '|'.join(fields)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000271 return self.voidcmd(cmd)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000272
Tim Peters88869f92001-01-14 23:36:06 +0000273 def makeport(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000274 '''Create a new socket and send a PORT command for it.'''
Martin v. Löwis2ad25692001-07-31 08:40:21 +0000275 msg = "getaddrinfo returns an empty list"
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000276 sock = None
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000277 for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
278 af, socktype, proto, canonname, sa = res
279 try:
280 sock = socket.socket(af, socktype, proto)
281 sock.bind(sa)
Guido van Rossumb940e112007-01-10 16:19:56 +0000282 except socket.error as msg:
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000283 if sock:
284 sock.close()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000285 sock = None
286 continue
287 break
288 if not sock:
Collin Winterce36ad82007-08-30 01:19:48 +0000289 raise socket.error(msg)
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000290 sock.listen(1)
291 port = sock.getsockname()[1] # Get proper port
292 host = self.sock.getsockname()[0] # Get proper host
293 if self.af == socket.AF_INET:
294 resp = self.sendport(host, port)
295 else:
296 resp = self.sendeprt(host, port)
297 return sock
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000298
299 def makepasv(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000300 if self.af == socket.AF_INET:
301 host, port = parse227(self.sendcmd('PASV'))
302 else:
303 host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
304 return host, port
Guido van Rossum1115ab21992-11-04 15:51:30 +0000305
Tim Peters88869f92001-01-14 23:36:06 +0000306 def ntransfercmd(self, cmd, rest=None):
307 """Initiate a transfer over the data connection.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000308
Tim Peters88869f92001-01-14 23:36:06 +0000309 If the transfer is active, send a port command and the
310 transfer command, and accept the connection. If the server is
311 passive, send a pasv command, connect to it, and start the
312 transfer command. Either way, return the socket for the
313 connection and the expected size of the transfer. The
314 expected size may be None if it could not be determined.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000315
Tim Peters88869f92001-01-14 23:36:06 +0000316 Optional `rest' argument can be a string that is sent as the
Christian Heimes1af737c2008-01-23 08:24:23 +0000317 argument to a REST command. This is essentially a server
Tim Peters88869f92001-01-14 23:36:06 +0000318 marker used to tell the server to skip over any data up to the
319 given marker.
320 """
321 size = None
322 if self.passiveserver:
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000323 host, port = self.makepasv()
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000324 conn = socket.create_connection((host, port), self.timeout)
Tim Peters88869f92001-01-14 23:36:06 +0000325 if rest is not None:
326 self.sendcmd("REST %s" % rest)
327 resp = self.sendcmd(cmd)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000328 # Some servers apparently send a 200 reply to
329 # a LIST or STOR command, before the 150 reply
330 # (and way before the 226 reply). This seems to
331 # be in violation of the protocol (which only allows
332 # 1xx or error messages for LIST), so we just discard
333 # this response.
334 if resp[0] == '2':
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000335 resp = self.getresp()
Tim Peters88869f92001-01-14 23:36:06 +0000336 if resp[0] != '1':
Collin Winterce36ad82007-08-30 01:19:48 +0000337 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000338 else:
339 sock = self.makeport()
340 if rest is not None:
341 self.sendcmd("REST %s" % rest)
342 resp = self.sendcmd(cmd)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000343 # See above.
344 if resp[0] == '2':
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000345 resp = self.getresp()
Tim Peters88869f92001-01-14 23:36:06 +0000346 if resp[0] != '1':
Collin Winterce36ad82007-08-30 01:19:48 +0000347 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000348 conn, sockaddr = sock.accept()
349 if resp[:3] == '150':
350 # this is conditional in case we received a 125
351 size = parse150(resp)
352 return conn, size
Fred Drake4de02d91997-01-10 18:26:09 +0000353
Tim Peters88869f92001-01-14 23:36:06 +0000354 def transfercmd(self, cmd, rest=None):
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000355 """Like ntransfercmd() but returns only the socket."""
Tim Peters88869f92001-01-14 23:36:06 +0000356 return self.ntransfercmd(cmd, rest)[0]
Guido van Rossumc567c601992-11-05 22:22:37 +0000357
Tim Peters88869f92001-01-14 23:36:06 +0000358 def login(self, user = '', passwd = '', acct = ''):
359 '''Login, default anonymous.'''
360 if not user: user = 'anonymous'
361 if not passwd: passwd = ''
362 if not acct: acct = ''
363 if user == 'anonymous' and passwd in ('', '-'):
Tim Peterse4418602002-02-16 07:34:19 +0000364 # If there is no anonymous ftp password specified
365 # then we'll just use anonymous@
366 # We don't send any other thing because:
367 # - We want to remain anonymous
368 # - We want to stop SPAM
369 # - We don't want to let ftp sites to discriminate by the user,
370 # host or country.
Guido van Rossumc33e0772001-12-28 20:54:28 +0000371 passwd = passwd + 'anonymous@'
Tim Peters88869f92001-01-14 23:36:06 +0000372 resp = self.sendcmd('USER ' + user)
373 if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
374 if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
375 if resp[0] != '2':
Collin Winterce36ad82007-08-30 01:19:48 +0000376 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000377 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000378
Tim Peters88869f92001-01-14 23:36:06 +0000379 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
Christian Heimes1af737c2008-01-23 08:24:23 +0000380 """Retrieve data in binary mode. A new port is created for you.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000381
Christian Heimes1af737c2008-01-23 08:24:23 +0000382 Args:
383 cmd: A RETR command.
384 callback: A single parameter callable to be called on each
385 block of data read.
386 blocksize: The maximum number of bytes to read from the
387 socket at one time. [default: 8192]
388 rest: Passed to transfercmd(). [default: None]
Guido van Rossum1115ab21992-11-04 15:51:30 +0000389
Christian Heimes1af737c2008-01-23 08:24:23 +0000390 Returns:
391 The response code.
Tim Peters88869f92001-01-14 23:36:06 +0000392 """
393 self.voidcmd('TYPE I')
394 conn = self.transfercmd(cmd, rest)
395 while 1:
396 data = conn.recv(blocksize)
397 if not data:
398 break
399 callback(data)
400 conn.close()
401 return self.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000402
Tim Peters88869f92001-01-14 23:36:06 +0000403 def retrlines(self, cmd, callback = None):
Christian Heimes1af737c2008-01-23 08:24:23 +0000404 """Retrieve data in line mode. A new port is created for you.
405
406 Args:
407 cmd: A RETR, LIST, NLST, or MLSD command.
408 callback: An optional single parameter callable that is called
409 for each line with the trailing CRLF stripped.
410 [default: print_line()]
411
412 Returns:
413 The response code.
414 """
Raymond Hettingere874fc32002-05-12 05:53:51 +0000415 if callback is None: callback = print_line
Tim Peters88869f92001-01-14 23:36:06 +0000416 resp = self.sendcmd('TYPE A')
417 conn = self.transfercmd(cmd)
Guido van Rossum52b89762007-07-17 20:45:57 +0000418 fp = conn.makefile('r', encoding=self.encoding)
Tim Peters88869f92001-01-14 23:36:06 +0000419 while 1:
420 line = fp.readline()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000421 if self.debugging > 2: print('*retr*', repr(line))
Tim Peters88869f92001-01-14 23:36:06 +0000422 if not line:
423 break
424 if line[-2:] == CRLF:
425 line = line[:-2]
426 elif line[-1:] == '\n':
427 line = line[:-1]
428 callback(line)
429 fp.close()
430 conn.close()
431 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000432
Christian Heimes1af737c2008-01-23 08:24:23 +0000433 def storbinary(self, cmd, fp, blocksize=8192, callback=None):
434 """Store a file in binary mode. A new port is created for you.
435
436 Args:
437 cmd: A STOR command.
438 fp: A file-like object with a read(num_bytes) method.
439 blocksize: The maximum data size to read from fp and send over
440 the connection at once. [default: 8192]
441 callback: An optional single parameter callable that is called on
442 on each block of data after it is sent. [default: None]
443
444 Returns:
445 The response code.
446 """
Tim Peters88869f92001-01-14 23:36:06 +0000447 self.voidcmd('TYPE I')
448 conn = self.transfercmd(cmd)
449 while 1:
450 buf = fp.read(blocksize)
451 if not buf: break
Martin v. Löwise12454f2002-02-16 23:06:19 +0000452 conn.sendall(buf)
Christian Heimes1af737c2008-01-23 08:24:23 +0000453 if callback: callback(buf)
Tim Peters88869f92001-01-14 23:36:06 +0000454 conn.close()
455 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000456
Christian Heimes1af737c2008-01-23 08:24:23 +0000457 def storlines(self, cmd, fp, callback=None):
458 """Store a file in line mode. A new port is created for you.
459
460 Args:
461 cmd: A STOR command.
462 fp: A file-like object with a readline() method.
463 callback: An optional single parameter callable that is called on
464 on each line after it is sent. [default: None]
465
466 Returns:
467 The response code.
468 """
Tim Peters88869f92001-01-14 23:36:06 +0000469 self.voidcmd('TYPE A')
470 conn = self.transfercmd(cmd)
471 while 1:
472 buf = fp.readline()
473 if not buf: break
474 if buf[-2:] != CRLF:
475 if buf[-1] in CRLF: buf = buf[:-1]
476 buf = buf + CRLF
Martin v. Löwise12454f2002-02-16 23:06:19 +0000477 conn.sendall(buf)
Christian Heimes1af737c2008-01-23 08:24:23 +0000478 if callback: callback(buf)
Tim Peters88869f92001-01-14 23:36:06 +0000479 conn.close()
480 return self.voidresp()
Guido van Rossum0eaa74b1996-01-25 18:37:21 +0000481
Tim Peters88869f92001-01-14 23:36:06 +0000482 def acct(self, password):
483 '''Send new account name.'''
484 cmd = 'ACCT ' + password
485 return self.voidcmd(cmd)
Guido van Rossumc567c601992-11-05 22:22:37 +0000486
Tim Peters88869f92001-01-14 23:36:06 +0000487 def nlst(self, *args):
488 '''Return a list of files in a given directory (default the current).'''
489 cmd = 'NLST'
490 for arg in args:
491 cmd = cmd + (' ' + arg)
492 files = []
493 self.retrlines(cmd, files.append)
494 return files
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000495
Tim Peters88869f92001-01-14 23:36:06 +0000496 def dir(self, *args):
497 '''List a directory in long form.
498 By default list current directory to stdout.
499 Optional last argument is callback function; all
500 non-empty arguments before it are concatenated to the
501 LIST command. (This *should* only be used for a pathname.)'''
502 cmd = 'LIST'
503 func = None
504 if args[-1:] and type(args[-1]) != type(''):
505 args, func = args[:-1], args[-1]
506 for arg in args:
507 if arg:
508 cmd = cmd + (' ' + arg)
509 self.retrlines(cmd, func)
Guido van Rossumc567c601992-11-05 22:22:37 +0000510
Tim Peters88869f92001-01-14 23:36:06 +0000511 def rename(self, fromname, toname):
512 '''Rename a file.'''
513 resp = self.sendcmd('RNFR ' + fromname)
514 if resp[0] != '3':
Collin Winterce36ad82007-08-30 01:19:48 +0000515 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000516 return self.voidcmd('RNTO ' + toname)
Guido van Rossuma61bdeb1995-10-11 17:36:31 +0000517
Tim Peters88869f92001-01-14 23:36:06 +0000518 def delete(self, filename):
519 '''Delete a file.'''
520 resp = self.sendcmd('DELE ' + filename)
521 if resp[:3] in ('250', '200'):
522 return resp
523 elif resp[:1] == '5':
Collin Winterce36ad82007-08-30 01:19:48 +0000524 raise error_perm(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000525 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000526 raise error_reply(resp)
Guido van Rossum02cf5821993-05-17 08:00:02 +0000527
Tim Peters88869f92001-01-14 23:36:06 +0000528 def cwd(self, dirname):
529 '''Change to a directory.'''
530 if dirname == '..':
531 try:
532 return self.voidcmd('CDUP')
Guido van Rossumb940e112007-01-10 16:19:56 +0000533 except error_perm as msg:
Martin v. Löwisb5255112002-03-10 15:59:58 +0000534 if msg.args[0][:3] != '500':
535 raise
Tim Peters88869f92001-01-14 23:36:06 +0000536 elif dirname == '':
537 dirname = '.' # does nothing, but could return error
538 cmd = 'CWD ' + dirname
539 return self.voidcmd(cmd)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000540
Tim Peters88869f92001-01-14 23:36:06 +0000541 def size(self, filename):
542 '''Retrieve the size of a file.'''
Christian Heimes1af737c2008-01-23 08:24:23 +0000543 # The SIZE command is defined in RFC-3659
Tim Peters88869f92001-01-14 23:36:06 +0000544 resp = self.sendcmd('SIZE ' + filename)
545 if resp[:3] == '213':
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000546 s = resp[3:].strip()
547 try:
548 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000549 except (OverflowError, ValueError):
Guido van Rossume2a383d2007-01-15 16:59:06 +0000550 return int(s)
Guido van Rossumc567c601992-11-05 22:22:37 +0000551
Tim Peters88869f92001-01-14 23:36:06 +0000552 def mkd(self, dirname):
553 '''Make a directory, return its full pathname.'''
554 resp = self.sendcmd('MKD ' + dirname)
555 return parse257(resp)
Guido van Rossum98245091998-02-19 21:15:44 +0000556
Tim Peters88869f92001-01-14 23:36:06 +0000557 def rmd(self, dirname):
558 '''Remove a directory.'''
559 return self.voidcmd('RMD ' + dirname)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000560
Tim Peters88869f92001-01-14 23:36:06 +0000561 def pwd(self):
562 '''Return current working directory.'''
563 resp = self.sendcmd('PWD')
564 return parse257(resp)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000565
Tim Peters88869f92001-01-14 23:36:06 +0000566 def quit(self):
567 '''Quit, and close the connection.'''
568 resp = self.voidcmd('QUIT')
569 self.close()
570 return resp
571
572 def close(self):
573 '''Close the connection without assuming anything about it.'''
Fred Drake9c98a422001-02-28 21:46:37 +0000574 if self.file:
575 self.file.close()
576 self.sock.close()
577 self.file = self.sock = None
Guido van Rossumc567c601992-11-05 22:22:37 +0000578
579
Guido van Rossumacfb82a1997-10-22 20:49:52 +0000580_150_re = None
Fred Drake4de02d91997-01-10 18:26:09 +0000581
582def parse150(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000583 '''Parse the '150' response for a RETR request.
584 Returns the expected transfer size or None; size is not guaranteed to
585 be present in the 150 message.
586 '''
587 if resp[:3] != '150':
Collin Winterce36ad82007-08-30 01:19:48 +0000588 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000589 global _150_re
590 if _150_re is None:
591 import re
592 _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE)
593 m = _150_re.match(resp)
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000594 if not m:
595 return None
596 s = m.group(1)
597 try:
598 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000599 except (OverflowError, ValueError):
Guido van Rossume2a383d2007-01-15 16:59:06 +0000600 return int(s)
Fred Drake4de02d91997-01-10 18:26:09 +0000601
602
Guido van Rossum70297d32001-08-17 17:24:29 +0000603_227_re = None
604
Guido van Rossumd2560b01996-05-28 23:41:25 +0000605def parse227(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000606 '''Parse the '227' response for a PASV request.
607 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
608 Return ('host.addr.as.numbers', port#) tuple.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000609
Tim Peters88869f92001-01-14 23:36:06 +0000610 if resp[:3] != '227':
Collin Winterce36ad82007-08-30 01:19:48 +0000611 raise error_reply(resp)
Guido van Rossum70297d32001-08-17 17:24:29 +0000612 global _227_re
613 if _227_re is None:
614 import re
615 _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
616 m = _227_re.search(resp)
617 if not m:
Collin Winterce36ad82007-08-30 01:19:48 +0000618 raise error_proto(resp)
Guido van Rossum70297d32001-08-17 17:24:29 +0000619 numbers = m.groups()
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000620 host = '.'.join(numbers[:4])
621 port = (int(numbers[4]) << 8) + int(numbers[5])
Tim Peters88869f92001-01-14 23:36:06 +0000622 return host, port
Guido van Rossumd2560b01996-05-28 23:41:25 +0000623
624
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000625def parse229(resp, peer):
626 '''Parse the '229' response for a EPSV request.
627 Raises error_proto if it does not contain '(|||port|)'
628 Return ('host.addr.as.numbers', port#) tuple.'''
629
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000630 if resp[:3] != '229':
Collin Winterce36ad82007-08-30 01:19:48 +0000631 raise error_reply(resp)
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000632 left = resp.find('(')
Collin Winterce36ad82007-08-30 01:19:48 +0000633 if left < 0: raise error_proto(resp)
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000634 right = resp.find(')', left + 1)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000635 if right < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000636 raise error_proto(resp) # should contain '(|||port|)'
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000637 if resp[left + 1] != resp[right - 1]:
Collin Winterce36ad82007-08-30 01:19:48 +0000638 raise error_proto(resp)
Walter Dörwalda401ae42002-06-03 10:41:45 +0000639 parts = resp[left + 1:right].split(resp[left+1])
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000640 if len(parts) != 5:
Collin Winterce36ad82007-08-30 01:19:48 +0000641 raise error_proto(resp)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000642 host = peer[0]
Neal Norwitz7ce734c2002-05-31 14:13:04 +0000643 port = int(parts[3])
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000644 return host, port
645
646
Guido van Rossumc567c601992-11-05 22:22:37 +0000647def parse257(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000648 '''Parse the '257' response for a MKD or PWD request.
649 This is a response to a MKD or PWD request: a directory name.
650 Returns the directoryname in the 257 reply.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000651
Tim Peters88869f92001-01-14 23:36:06 +0000652 if resp[:3] != '257':
Collin Winterce36ad82007-08-30 01:19:48 +0000653 raise error_reply(resp)
Tim Peters88869f92001-01-14 23:36:06 +0000654 if resp[3:5] != ' "':
655 return '' # Not compliant to RFC 959, but UNIX ftpd does this
656 dirname = ''
657 i = 5
658 n = len(resp)
659 while i < n:
660 c = resp[i]
661 i = i+1
662 if c == '"':
663 if i >= n or resp[i] != '"':
664 break
665 i = i+1
666 dirname = dirname + c
667 return dirname
Guido van Rossum1115ab21992-11-04 15:51:30 +0000668
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000669
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000670def print_line(line):
Tim Peters88869f92001-01-14 23:36:06 +0000671 '''Default retrlines callback to print a line.'''
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000672 print(line)
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000673
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000674
Guido van Rossumd2560b01996-05-28 23:41:25 +0000675def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
Tim Peters88869f92001-01-14 23:36:06 +0000676 '''Copy file from one FTP-instance to another.'''
677 if not targetname: targetname = sourcename
678 type = 'TYPE ' + type
679 source.voidcmd(type)
680 target.voidcmd(type)
681 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
682 target.sendport(sourcehost, sourceport)
683 # RFC 959: the user must "listen" [...] BEFORE sending the
684 # transfer request.
685 # So: STOR before RETR, because here the target is a "user".
686 treply = target.sendcmd('STOR ' + targetname)
687 if treply[:3] not in ('125', '150'): raise error_proto # RFC 959
688 sreply = source.sendcmd('RETR ' + sourcename)
689 if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959
690 source.voidresp()
691 target.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000692
Tim Peters88869f92001-01-14 23:36:06 +0000693
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000694class Netrc:
Tim Peters88869f92001-01-14 23:36:06 +0000695 """Class to parse & provide access to 'netrc' format files.
Fred Drake475d51d1997-06-24 22:02:54 +0000696
Tim Peters88869f92001-01-14 23:36:06 +0000697 See the netrc(4) man page for information on the file format.
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000698
Tim Peters88869f92001-01-14 23:36:06 +0000699 WARNING: This class is obsolete -- use module netrc instead.
Guido van Rossumc822a451998-12-22 16:49:16 +0000700
Tim Peters88869f92001-01-14 23:36:06 +0000701 """
702 __defuser = None
703 __defpasswd = None
704 __defacct = None
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000705
Tim Peters88869f92001-01-14 23:36:06 +0000706 def __init__(self, filename=None):
Raymond Hettinger094662a2002-06-01 01:29:16 +0000707 if filename is None:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000708 if "HOME" in os.environ:
Tim Peters88869f92001-01-14 23:36:06 +0000709 filename = os.path.join(os.environ["HOME"],
710 ".netrc")
711 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000712 raise IOError("specify file to load or set $HOME")
Tim Peters88869f92001-01-14 23:36:06 +0000713 self.__hosts = {}
714 self.__macros = {}
715 fp = open(filename, "r")
716 in_macro = 0
717 while 1:
718 line = fp.readline()
719 if not line: break
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000720 if in_macro and line.strip():
Tim Peters88869f92001-01-14 23:36:06 +0000721 macro_lines.append(line)
722 continue
723 elif in_macro:
724 self.__macros[macro_name] = tuple(macro_lines)
725 in_macro = 0
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000726 words = line.split()
Tim Peters88869f92001-01-14 23:36:06 +0000727 host = user = passwd = acct = None
728 default = 0
729 i = 0
730 while i < len(words):
731 w1 = words[i]
732 if i+1 < len(words):
733 w2 = words[i + 1]
734 else:
735 w2 = None
736 if w1 == 'default':
737 default = 1
738 elif w1 == 'machine' and w2:
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000739 host = w2.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000740 i = i + 1
741 elif w1 == 'login' and w2:
742 user = w2
743 i = i + 1
744 elif w1 == 'password' and w2:
745 passwd = w2
746 i = i + 1
747 elif w1 == 'account' and w2:
748 acct = w2
749 i = i + 1
750 elif w1 == 'macdef' and w2:
751 macro_name = w2
752 macro_lines = []
753 in_macro = 1
754 break
755 i = i + 1
756 if default:
757 self.__defuser = user or self.__defuser
758 self.__defpasswd = passwd or self.__defpasswd
759 self.__defacct = acct or self.__defacct
760 if host:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000761 if host in self.__hosts:
Tim Peters88869f92001-01-14 23:36:06 +0000762 ouser, opasswd, oacct = \
763 self.__hosts[host]
764 user = user or ouser
765 passwd = passwd or opasswd
766 acct = acct or oacct
767 self.__hosts[host] = user, passwd, acct
768 fp.close()
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000769
Tim Peters88869f92001-01-14 23:36:06 +0000770 def get_hosts(self):
771 """Return a list of hosts mentioned in the .netrc file."""
772 return self.__hosts.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000773
Tim Peters88869f92001-01-14 23:36:06 +0000774 def get_account(self, host):
775 """Returns login information for the named host.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000776
Tim Peters88869f92001-01-14 23:36:06 +0000777 The return value is a triple containing userid,
778 password, and the accounting field.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000779
Tim Peters88869f92001-01-14 23:36:06 +0000780 """
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000781 host = host.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000782 user = passwd = acct = None
Raymond Hettinger54f02222002-06-01 14:18:47 +0000783 if host in self.__hosts:
Tim Peters88869f92001-01-14 23:36:06 +0000784 user, passwd, acct = self.__hosts[host]
785 user = user or self.__defuser
786 passwd = passwd or self.__defpasswd
787 acct = acct or self.__defacct
788 return user, passwd, acct
Guido van Rossum8ca84201998-03-26 20:56:10 +0000789
Tim Peters88869f92001-01-14 23:36:06 +0000790 def get_macros(self):
791 """Return a list of all defined macro names."""
792 return self.__macros.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000793
Tim Peters88869f92001-01-14 23:36:06 +0000794 def get_macro(self, macro):
795 """Return a sequence of lines which define a named macro."""
796 return self.__macros[macro]
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000797
Fred Drake475d51d1997-06-24 22:02:54 +0000798
Tim Peters88869f92001-01-14 23:36:06 +0000799
Guido van Rossum1115ab21992-11-04 15:51:30 +0000800def test():
Tim Peters88869f92001-01-14 23:36:06 +0000801 '''Test program.
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000802 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
803
804 -d dir
805 -l list
806 -p password
807 '''
808
809 if len(sys.argv) < 2:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000810 print(test.__doc__)
Raymond Hettingerc88a6c72005-04-05 04:31:09 +0000811 sys.exit(0)
Guido van Rossumd2560b01996-05-28 23:41:25 +0000812
Tim Peters88869f92001-01-14 23:36:06 +0000813 debugging = 0
814 rcfile = None
815 while sys.argv[1] == '-d':
816 debugging = debugging+1
817 del sys.argv[1]
818 if sys.argv[1][:2] == '-r':
819 # get name of alternate ~/.netrc file:
820 rcfile = sys.argv[1][2:]
821 del sys.argv[1]
822 host = sys.argv[1]
823 ftp = FTP(host)
824 ftp.set_debuglevel(debugging)
825 userid = passwd = acct = ''
826 try:
827 netrc = Netrc(rcfile)
828 except IOError:
829 if rcfile is not None:
830 sys.stderr.write("Could not open account file"
831 " -- using anonymous login.")
832 else:
833 try:
834 userid, passwd, acct = netrc.get_account(host)
835 except KeyError:
836 # no account for host
837 sys.stderr.write(
838 "No account -- using anonymous login.")
839 ftp.login(userid, passwd, acct)
840 for file in sys.argv[2:]:
841 if file[:2] == '-l':
842 ftp.dir(file[2:])
843 elif file[:2] == '-d':
844 cmd = 'CWD'
845 if file[2:]: cmd = cmd + ' ' + file[2:]
846 resp = ftp.sendcmd(cmd)
847 elif file == '-p':
848 ftp.set_pasv(not ftp.passiveserver)
849 else:
850 ftp.retrbinary('RETR ' + file, \
851 sys.stdout.write, 1024)
852 ftp.quit()
Guido van Rossum221ec0b1995-08-04 04:39:30 +0000853
854
855if __name__ == '__main__':
Tim Peters88869f92001-01-14 23:36:06 +0000856 test()