blob: 592f5d29d2caa6be47b70248f7b16f9d74d69e02 [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
9>>> ftp.login() # default, i.e.: user anonymous, passwd user@hostname
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.
35#
Guido van Rossumc567c601992-11-05 22:22:37 +000036
Guido van Rossum1115ab21992-11-04 15:51:30 +000037import os
38import sys
Guido van Rossum1115ab21992-11-04 15:51:30 +000039import string
40
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
Tim Peters88869f92001-01-14 23:36:06 +000080 To create a connection, call the class using these argument:
81 host, user, passwd, acct
82 These are all strings, and have default value ''.
83 Then use self.connect() with optional host and port argument.
Guido van Rossumd2560b01996-05-28 23:41:25 +000084
Tim Peters88869f92001-01-14 23:36:06 +000085 To download a file, use ftp.retrlines('RETR ' + filename),
86 or ftp.retrbinary() with slightly different arguments.
87 To upload a file, use ftp.storlines() or ftp.storbinary(),
88 which have an open file as argument (see their definitions
89 below for details).
90 The download/upload functions first issue appropriate TYPE
91 and PORT or PASV commands.
Guido van Rossumd2560b01996-05-28 23:41:25 +000092'''
93
Fred Drake9c98a422001-02-28 21:46:37 +000094 debugging = 0
95 host = ''
96 port = FTP_PORT
97 sock = None
98 file = None
99 welcome = None
100 passiveserver = 1
101
Tim Peters88869f92001-01-14 23:36:06 +0000102 # Initialization method (called by class instantiation).
103 # Initialize host to localhost, port to standard ftp port
104 # Optional arguments are host (for connect()),
105 # and user, passwd, acct (for login())
Fred Drake9c98a422001-02-28 21:46:37 +0000106 def __init__(self, host='', user='', passwd='', acct=''):
Tim Peters88869f92001-01-14 23:36:06 +0000107 if host:
Fred Drake9c98a422001-02-28 21:46:37 +0000108 self.connect(host)
109 if user: self.login(user, passwd, acct)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000110
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000111 def connect(self, host = '', port = 0):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000112 '''Connect to host. Arguments are:
113 - host: hostname to connect to (string, default previous host)
114 - port: port to connect to (integer, default previous port)'''
115 if host: self.host = host
116 if port: self.port = port
117 self.passiveserver = 0
Martin v. Löwis2ad25692001-07-31 08:40:21 +0000118 msg = "getaddrinfo returns an empty list"
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000119 for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
120 af, socktype, proto, canonname, sa = res
121 try:
122 self.sock = socket.socket(af, socktype, proto)
123 self.sock.connect(sa)
124 except socket.error, msg:
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000125 if self.sock:
126 self.sock.close()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000127 self.sock = None
128 continue
129 break
130 if not self.sock:
131 raise socket.error, msg
132 self.af = af
133 self.file = self.sock.makefile('rb')
134 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:
141 print '*welcome*', self.sanitize(self.welcome)
142 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:]
166 return `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
171 if self.debugging > 1: print '*put*', self.sanitize(line)
172 self.sock.send(line)
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):
176 if self.debugging: print '*cmd*', self.sanitize(line)
177 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:
184 print '*get*', self.sanitize(line)
185 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()
210 if self.debugging: print '*resp*', self.sanitize(resp)
211 self.lastresp = resp[:3]
212 c = resp[:1]
213 if c == '4':
214 raise error_temp, resp
215 if c == '5':
216 raise error_perm, resp
217 if c not in '123':
218 raise error_proto, resp
219 return 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':
225 raise error_reply, resp
226 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
234 if self.debugging > 1: print '*put urgent*', self.sanitize(line)
235 self.sock.send(line, MSG_OOB)
236 resp = self.getmultiline()
237 if resp[:3] not in ('426', '226'):
238 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('.')
Tim Peters88869f92001-01-14 23:36:06 +0000255 pbytes = [`port/256`, `port%256`]
256 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:
268 raise error_proto, 'unsupported address family'
269 fields = ['', `af`, host, `port`, '']
270 cmd = 'EPRT ' + string.joinfields(fields, '|')
271 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)
282 except socket.error, 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:
289 raise socket.error, msg
290 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
317 argument to a RESTART command. This is essentially a server
318 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()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000324 af, socktype, proto, canon, sa = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0]
325 conn = socket.socket(af, socktype, proto)
326 conn.connect(sa)
Tim Peters88869f92001-01-14 23:36:06 +0000327 if rest is not None:
328 self.sendcmd("REST %s" % rest)
329 resp = self.sendcmd(cmd)
330 if resp[0] != '1':
331 raise error_reply, resp
332 else:
333 sock = self.makeport()
334 if rest is not None:
335 self.sendcmd("REST %s" % rest)
336 resp = self.sendcmd(cmd)
337 if resp[0] != '1':
338 raise error_reply, resp
339 conn, sockaddr = sock.accept()
340 if resp[:3] == '150':
341 # this is conditional in case we received a 125
342 size = parse150(resp)
343 return conn, size
Fred Drake4de02d91997-01-10 18:26:09 +0000344
Tim Peters88869f92001-01-14 23:36:06 +0000345 def transfercmd(self, cmd, rest=None):
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000346 """Like ntransfercmd() but returns only the socket."""
Tim Peters88869f92001-01-14 23:36:06 +0000347 return self.ntransfercmd(cmd, rest)[0]
Guido van Rossumc567c601992-11-05 22:22:37 +0000348
Tim Peters88869f92001-01-14 23:36:06 +0000349 def login(self, user = '', passwd = '', acct = ''):
350 '''Login, default anonymous.'''
351 if not user: user = 'anonymous'
352 if not passwd: passwd = ''
353 if not acct: acct = ''
354 if user == 'anonymous' and passwd in ('', '-'):
355 # get fully qualified domain name of local host
356 thishost = socket.getfqdn()
357 try:
358 if os.environ.has_key('LOGNAME'):
359 realuser = os.environ['LOGNAME']
360 elif os.environ.has_key('USER'):
361 realuser = os.environ['USER']
362 else:
363 realuser = 'anonymous'
364 except AttributeError:
365 # Not all systems have os.environ....
366 realuser = 'anonymous'
367 passwd = passwd + realuser + '@' + thishost
368 resp = self.sendcmd('USER ' + user)
369 if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
370 if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
371 if resp[0] != '2':
372 raise error_reply, resp
373 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000374
Tim Peters88869f92001-01-14 23:36:06 +0000375 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
376 """Retrieve data in binary mode.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000377
Tim Peters88869f92001-01-14 23:36:06 +0000378 `cmd' is a RETR command. `callback' is a callback function is
379 called for each block. No more than `blocksize' number of
380 bytes will be read from the socket. Optional `rest' is passed
381 to transfercmd().
Guido van Rossum1115ab21992-11-04 15:51:30 +0000382
Tim Peters88869f92001-01-14 23:36:06 +0000383 A new port is created for you. Return the response code.
384 """
385 self.voidcmd('TYPE I')
386 conn = self.transfercmd(cmd, rest)
387 while 1:
388 data = conn.recv(blocksize)
389 if not data:
390 break
391 callback(data)
392 conn.close()
393 return self.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000394
Tim Peters88869f92001-01-14 23:36:06 +0000395 def retrlines(self, cmd, callback = None):
396 '''Retrieve data in line mode.
397 The argument is a RETR or LIST command.
398 The callback function (2nd argument) is called for each line,
399 with trailing CRLF stripped. This creates a new port for you.
400 print_line() is the default callback.'''
401 if not callback: callback = print_line
402 resp = self.sendcmd('TYPE A')
403 conn = self.transfercmd(cmd)
404 fp = conn.makefile('rb')
405 while 1:
406 line = fp.readline()
407 if self.debugging > 2: print '*retr*', `line`
408 if not line:
409 break
410 if line[-2:] == CRLF:
411 line = line[:-2]
412 elif line[-1:] == '\n':
413 line = line[:-1]
414 callback(line)
415 fp.close()
416 conn.close()
417 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000418
Guido van Rossum4ac83472001-02-15 13:50:36 +0000419 def storbinary(self, cmd, fp, blocksize=8192):
Tim Peters88869f92001-01-14 23:36:06 +0000420 '''Store a file in binary mode.'''
421 self.voidcmd('TYPE I')
422 conn = self.transfercmd(cmd)
423 while 1:
424 buf = fp.read(blocksize)
425 if not buf: break
426 conn.send(buf)
427 conn.close()
428 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000429
Tim Peters88869f92001-01-14 23:36:06 +0000430 def storlines(self, cmd, fp):
431 '''Store a file in line mode.'''
432 self.voidcmd('TYPE A')
433 conn = self.transfercmd(cmd)
434 while 1:
435 buf = fp.readline()
436 if not buf: break
437 if buf[-2:] != CRLF:
438 if buf[-1] in CRLF: buf = buf[:-1]
439 buf = buf + CRLF
440 conn.send(buf)
441 conn.close()
442 return self.voidresp()
Guido van Rossum0eaa74b1996-01-25 18:37:21 +0000443
Tim Peters88869f92001-01-14 23:36:06 +0000444 def acct(self, password):
445 '''Send new account name.'''
446 cmd = 'ACCT ' + password
447 return self.voidcmd(cmd)
Guido van Rossumc567c601992-11-05 22:22:37 +0000448
Tim Peters88869f92001-01-14 23:36:06 +0000449 def nlst(self, *args):
450 '''Return a list of files in a given directory (default the current).'''
451 cmd = 'NLST'
452 for arg in args:
453 cmd = cmd + (' ' + arg)
454 files = []
455 self.retrlines(cmd, files.append)
456 return files
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000457
Tim Peters88869f92001-01-14 23:36:06 +0000458 def dir(self, *args):
459 '''List a directory in long form.
460 By default list current directory to stdout.
461 Optional last argument is callback function; all
462 non-empty arguments before it are concatenated to the
463 LIST command. (This *should* only be used for a pathname.)'''
464 cmd = 'LIST'
465 func = None
466 if args[-1:] and type(args[-1]) != type(''):
467 args, func = args[:-1], args[-1]
468 for arg in args:
469 if arg:
470 cmd = cmd + (' ' + arg)
471 self.retrlines(cmd, func)
Guido van Rossumc567c601992-11-05 22:22:37 +0000472
Tim Peters88869f92001-01-14 23:36:06 +0000473 def rename(self, fromname, toname):
474 '''Rename a file.'''
475 resp = self.sendcmd('RNFR ' + fromname)
476 if resp[0] != '3':
477 raise error_reply, resp
478 return self.voidcmd('RNTO ' + toname)
Guido van Rossuma61bdeb1995-10-11 17:36:31 +0000479
Tim Peters88869f92001-01-14 23:36:06 +0000480 def delete(self, filename):
481 '''Delete a file.'''
482 resp = self.sendcmd('DELE ' + filename)
483 if resp[:3] in ('250', '200'):
484 return resp
485 elif resp[:1] == '5':
486 raise error_perm, resp
487 else:
488 raise error_reply, resp
Guido van Rossum02cf5821993-05-17 08:00:02 +0000489
Tim Peters88869f92001-01-14 23:36:06 +0000490 def cwd(self, dirname):
491 '''Change to a directory.'''
492 if dirname == '..':
493 try:
494 return self.voidcmd('CDUP')
495 except error_perm, msg:
496 if msg[:3] != '500':
497 raise error_perm, msg
498 elif dirname == '':
499 dirname = '.' # does nothing, but could return error
500 cmd = 'CWD ' + dirname
501 return self.voidcmd(cmd)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000502
Tim Peters88869f92001-01-14 23:36:06 +0000503 def size(self, filename):
504 '''Retrieve the size of a file.'''
505 # Note that the RFC doesn't say anything about 'SIZE'
506 resp = self.sendcmd('SIZE ' + filename)
507 if resp[:3] == '213':
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000508 s = resp[3:].strip()
509 try:
510 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000511 except (OverflowError, ValueError):
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000512 return long(s)
Guido van Rossumc567c601992-11-05 22:22:37 +0000513
Tim Peters88869f92001-01-14 23:36:06 +0000514 def mkd(self, dirname):
515 '''Make a directory, return its full pathname.'''
516 resp = self.sendcmd('MKD ' + dirname)
517 return parse257(resp)
Guido van Rossum98245091998-02-19 21:15:44 +0000518
Tim Peters88869f92001-01-14 23:36:06 +0000519 def rmd(self, dirname):
520 '''Remove a directory.'''
521 return self.voidcmd('RMD ' + dirname)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000522
Tim Peters88869f92001-01-14 23:36:06 +0000523 def pwd(self):
524 '''Return current working directory.'''
525 resp = self.sendcmd('PWD')
526 return parse257(resp)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000527
Tim Peters88869f92001-01-14 23:36:06 +0000528 def quit(self):
529 '''Quit, and close the connection.'''
530 resp = self.voidcmd('QUIT')
531 self.close()
532 return resp
533
534 def close(self):
535 '''Close the connection without assuming anything about it.'''
Fred Drake9c98a422001-02-28 21:46:37 +0000536 if self.file:
537 self.file.close()
538 self.sock.close()
539 self.file = self.sock = None
Guido van Rossumc567c601992-11-05 22:22:37 +0000540
541
Guido van Rossumacfb82a1997-10-22 20:49:52 +0000542_150_re = None
Fred Drake4de02d91997-01-10 18:26:09 +0000543
544def parse150(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000545 '''Parse the '150' response for a RETR request.
546 Returns the expected transfer size or None; size is not guaranteed to
547 be present in the 150 message.
548 '''
549 if resp[:3] != '150':
550 raise error_reply, resp
551 global _150_re
552 if _150_re is None:
553 import re
554 _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE)
555 m = _150_re.match(resp)
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000556 if not m:
557 return None
558 s = m.group(1)
559 try:
560 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000561 except (OverflowError, ValueError):
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000562 return long(s)
Fred Drake4de02d91997-01-10 18:26:09 +0000563
564
Guido van Rossum70297d32001-08-17 17:24:29 +0000565_227_re = None
566
Guido van Rossumd2560b01996-05-28 23:41:25 +0000567def parse227(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000568 '''Parse the '227' response for a PASV request.
569 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
570 Return ('host.addr.as.numbers', port#) tuple.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000571
Tim Peters88869f92001-01-14 23:36:06 +0000572 if resp[:3] != '227':
573 raise error_reply, resp
Guido van Rossum70297d32001-08-17 17:24:29 +0000574 global _227_re
575 if _227_re is None:
576 import re
577 _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
578 m = _227_re.search(resp)
579 if not m:
Tim Peters88869f92001-01-14 23:36:06 +0000580 raise error_proto, resp
Guido van Rossum70297d32001-08-17 17:24:29 +0000581 numbers = m.groups()
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000582 host = '.'.join(numbers[:4])
583 port = (int(numbers[4]) << 8) + int(numbers[5])
Tim Peters88869f92001-01-14 23:36:06 +0000584 return host, port
Guido van Rossumd2560b01996-05-28 23:41:25 +0000585
586
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000587def parse229(resp, peer):
588 '''Parse the '229' response for a EPSV request.
589 Raises error_proto if it does not contain '(|||port|)'
590 Return ('host.addr.as.numbers', port#) tuple.'''
591
592 if resp[:3] <> '229':
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000593 raise error_reply, resp
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000594 left = string.find(resp, '(')
595 if left < 0: raise error_proto, resp
596 right = string.find(resp, ')', left + 1)
597 if right < 0:
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000598 raise error_proto, resp # should contain '(|||port|)'
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000599 if resp[left + 1] <> resp[right - 1]:
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000600 raise error_proto, resp
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000601 parts = string.split(resp[left + 1:right], resp[left+1])
602 if len(parts) <> 5:
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000603 raise error_proto, resp
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000604 host = peer[0]
605 port = string.atoi(parts[3])
606 return host, port
607
608
Guido van Rossumc567c601992-11-05 22:22:37 +0000609def parse257(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000610 '''Parse the '257' response for a MKD or PWD request.
611 This is a response to a MKD or PWD request: a directory name.
612 Returns the directoryname in the 257 reply.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000613
Tim Peters88869f92001-01-14 23:36:06 +0000614 if resp[:3] != '257':
615 raise error_reply, resp
616 if resp[3:5] != ' "':
617 return '' # Not compliant to RFC 959, but UNIX ftpd does this
618 dirname = ''
619 i = 5
620 n = len(resp)
621 while i < n:
622 c = resp[i]
623 i = i+1
624 if c == '"':
625 if i >= n or resp[i] != '"':
626 break
627 i = i+1
628 dirname = dirname + c
629 return dirname
Guido van Rossum1115ab21992-11-04 15:51:30 +0000630
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000631
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000632def print_line(line):
Tim Peters88869f92001-01-14 23:36:06 +0000633 '''Default retrlines callback to print a line.'''
634 print line
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000635
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000636
Guido van Rossumd2560b01996-05-28 23:41:25 +0000637def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
Tim Peters88869f92001-01-14 23:36:06 +0000638 '''Copy file from one FTP-instance to another.'''
639 if not targetname: targetname = sourcename
640 type = 'TYPE ' + type
641 source.voidcmd(type)
642 target.voidcmd(type)
643 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
644 target.sendport(sourcehost, sourceport)
645 # RFC 959: the user must "listen" [...] BEFORE sending the
646 # transfer request.
647 # So: STOR before RETR, because here the target is a "user".
648 treply = target.sendcmd('STOR ' + targetname)
649 if treply[:3] not in ('125', '150'): raise error_proto # RFC 959
650 sreply = source.sendcmd('RETR ' + sourcename)
651 if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959
652 source.voidresp()
653 target.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000654
Tim Peters88869f92001-01-14 23:36:06 +0000655
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000656class Netrc:
Tim Peters88869f92001-01-14 23:36:06 +0000657 """Class to parse & provide access to 'netrc' format files.
Fred Drake475d51d1997-06-24 22:02:54 +0000658
Tim Peters88869f92001-01-14 23:36:06 +0000659 See the netrc(4) man page for information on the file format.
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000660
Tim Peters88869f92001-01-14 23:36:06 +0000661 WARNING: This class is obsolete -- use module netrc instead.
Guido van Rossumc822a451998-12-22 16:49:16 +0000662
Tim Peters88869f92001-01-14 23:36:06 +0000663 """
664 __defuser = None
665 __defpasswd = None
666 __defacct = None
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000667
Tim Peters88869f92001-01-14 23:36:06 +0000668 def __init__(self, filename=None):
669 if not filename:
670 if os.environ.has_key("HOME"):
671 filename = os.path.join(os.environ["HOME"],
672 ".netrc")
673 else:
674 raise IOError, \
675 "specify file to load or set $HOME"
676 self.__hosts = {}
677 self.__macros = {}
678 fp = open(filename, "r")
679 in_macro = 0
680 while 1:
681 line = fp.readline()
682 if not line: break
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000683 if in_macro and line.strip():
Tim Peters88869f92001-01-14 23:36:06 +0000684 macro_lines.append(line)
685 continue
686 elif in_macro:
687 self.__macros[macro_name] = tuple(macro_lines)
688 in_macro = 0
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000689 words = line.split()
Tim Peters88869f92001-01-14 23:36:06 +0000690 host = user = passwd = acct = None
691 default = 0
692 i = 0
693 while i < len(words):
694 w1 = words[i]
695 if i+1 < len(words):
696 w2 = words[i + 1]
697 else:
698 w2 = None
699 if w1 == 'default':
700 default = 1
701 elif w1 == 'machine' and w2:
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000702 host = w2.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000703 i = i + 1
704 elif w1 == 'login' and w2:
705 user = w2
706 i = i + 1
707 elif w1 == 'password' and w2:
708 passwd = w2
709 i = i + 1
710 elif w1 == 'account' and w2:
711 acct = w2
712 i = i + 1
713 elif w1 == 'macdef' and w2:
714 macro_name = w2
715 macro_lines = []
716 in_macro = 1
717 break
718 i = i + 1
719 if default:
720 self.__defuser = user or self.__defuser
721 self.__defpasswd = passwd or self.__defpasswd
722 self.__defacct = acct or self.__defacct
723 if host:
724 if self.__hosts.has_key(host):
725 ouser, opasswd, oacct = \
726 self.__hosts[host]
727 user = user or ouser
728 passwd = passwd or opasswd
729 acct = acct or oacct
730 self.__hosts[host] = user, passwd, acct
731 fp.close()
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000732
Tim Peters88869f92001-01-14 23:36:06 +0000733 def get_hosts(self):
734 """Return a list of hosts mentioned in the .netrc file."""
735 return self.__hosts.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000736
Tim Peters88869f92001-01-14 23:36:06 +0000737 def get_account(self, host):
738 """Returns login information for the named host.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000739
Tim Peters88869f92001-01-14 23:36:06 +0000740 The return value is a triple containing userid,
741 password, and the accounting field.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000742
Tim Peters88869f92001-01-14 23:36:06 +0000743 """
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000744 host = host.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000745 user = passwd = acct = None
746 if self.__hosts.has_key(host):
747 user, passwd, acct = self.__hosts[host]
748 user = user or self.__defuser
749 passwd = passwd or self.__defpasswd
750 acct = acct or self.__defacct
751 return user, passwd, acct
Guido van Rossum8ca84201998-03-26 20:56:10 +0000752
Tim Peters88869f92001-01-14 23:36:06 +0000753 def get_macros(self):
754 """Return a list of all defined macro names."""
755 return self.__macros.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000756
Tim Peters88869f92001-01-14 23:36:06 +0000757 def get_macro(self, macro):
758 """Return a sequence of lines which define a named macro."""
759 return self.__macros[macro]
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000760
Fred Drake475d51d1997-06-24 22:02:54 +0000761
Tim Peters88869f92001-01-14 23:36:06 +0000762
Guido van Rossum1115ab21992-11-04 15:51:30 +0000763def test():
Tim Peters88869f92001-01-14 23:36:06 +0000764 '''Test program.
765 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000766
Tim Peters88869f92001-01-14 23:36:06 +0000767 debugging = 0
768 rcfile = None
769 while sys.argv[1] == '-d':
770 debugging = debugging+1
771 del sys.argv[1]
772 if sys.argv[1][:2] == '-r':
773 # get name of alternate ~/.netrc file:
774 rcfile = sys.argv[1][2:]
775 del sys.argv[1]
776 host = sys.argv[1]
777 ftp = FTP(host)
778 ftp.set_debuglevel(debugging)
779 userid = passwd = acct = ''
780 try:
781 netrc = Netrc(rcfile)
782 except IOError:
783 if rcfile is not None:
784 sys.stderr.write("Could not open account file"
785 " -- using anonymous login.")
786 else:
787 try:
788 userid, passwd, acct = netrc.get_account(host)
789 except KeyError:
790 # no account for host
791 sys.stderr.write(
792 "No account -- using anonymous login.")
793 ftp.login(userid, passwd, acct)
794 for file in sys.argv[2:]:
795 if file[:2] == '-l':
796 ftp.dir(file[2:])
797 elif file[:2] == '-d':
798 cmd = 'CWD'
799 if file[2:]: cmd = cmd + ' ' + file[2:]
800 resp = ftp.sendcmd(cmd)
801 elif file == '-p':
802 ftp.set_pasv(not ftp.passiveserver)
803 else:
804 ftp.retrbinary('RETR ' + file, \
805 sys.stdout.write, 1024)
806 ftp.quit()
Guido van Rossum221ec0b1995-08-04 04:39:30 +0000807
808
809if __name__ == '__main__':
Tim Peters88869f92001-01-14 23:36:06 +0000810 test()