blob: 885b5cdfa2d69c1a18186300d8bf754e7fa1d7ff [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
118 for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
119 af, socktype, proto, canonname, sa = res
120 try:
121 self.sock = socket.socket(af, socktype, proto)
122 self.sock.connect(sa)
123 except socket.error, msg:
124 self.sock.close()
125 self.sock = None
126 continue
127 break
128 if not self.sock:
129 raise socket.error, msg
130 self.af = af
131 self.file = self.sock.makefile('rb')
132 self.welcome = self.getresp()
133 return self.welcome
Guido van Rossum1115ab21992-11-04 15:51:30 +0000134
Tim Peters88869f92001-01-14 23:36:06 +0000135 def getwelcome(self):
136 '''Get the welcome message from the server.
137 (this is read and squirreled away by connect())'''
138 if self.debugging:
139 print '*welcome*', self.sanitize(self.welcome)
140 return self.welcome
Guido van Rossum1115ab21992-11-04 15:51:30 +0000141
Tim Peters88869f92001-01-14 23:36:06 +0000142 def set_debuglevel(self, level):
143 '''Set the debugging level.
144 The required argument level means:
145 0: no debugging output (default)
146 1: print commands and responses but not body text etc.
147 2: also print raw lines read and sent before stripping CR/LF'''
148 self.debugging = level
149 debug = set_debuglevel
Guido van Rossum1115ab21992-11-04 15:51:30 +0000150
Tim Peters88869f92001-01-14 23:36:06 +0000151 def set_pasv(self, val):
152 '''Use passive or active mode for data transfers.
153 With a false argument, use the normal PORT mode,
154 With a true argument, use the PASV command.'''
155 self.passiveserver = val
Guido van Rossumd2560b01996-05-28 23:41:25 +0000156
Tim Peters88869f92001-01-14 23:36:06 +0000157 # Internal: "sanitize" a string for printing
158 def sanitize(self, s):
159 if s[:5] == 'pass ' or s[:5] == 'PASS ':
160 i = len(s)
161 while i > 5 and s[i-1] in '\r\n':
162 i = i-1
163 s = s[:5] + '*'*(i-5) + s[i:]
164 return `s`
Guido van Rossumebaf1041995-05-05 15:54:14 +0000165
Tim Peters88869f92001-01-14 23:36:06 +0000166 # Internal: send one line to the server, appending CRLF
167 def putline(self, line):
168 line = line + CRLF
169 if self.debugging > 1: print '*put*', self.sanitize(line)
170 self.sock.send(line)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000171
Tim Peters88869f92001-01-14 23:36:06 +0000172 # Internal: send one command to the server (through putline())
173 def putcmd(self, line):
174 if self.debugging: print '*cmd*', self.sanitize(line)
175 self.putline(line)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000176
Tim Peters88869f92001-01-14 23:36:06 +0000177 # Internal: return one line from the server, stripping CRLF.
178 # Raise EOFError if the connection is closed
179 def getline(self):
180 line = self.file.readline()
181 if self.debugging > 1:
182 print '*get*', self.sanitize(line)
183 if not line: raise EOFError
184 if line[-2:] == CRLF: line = line[:-2]
185 elif line[-1:] in CRLF: line = line[:-1]
186 return line
Guido van Rossum1115ab21992-11-04 15:51:30 +0000187
Tim Peters88869f92001-01-14 23:36:06 +0000188 # Internal: get a response from the server, which may possibly
189 # consist of multiple lines. Return a single string with no
190 # trailing CRLF. If the response consists of multiple lines,
191 # these are separated by '\n' characters in the string
192 def getmultiline(self):
193 line = self.getline()
194 if line[3:4] == '-':
195 code = line[:3]
196 while 1:
197 nextline = self.getline()
198 line = line + ('\n' + nextline)
199 if nextline[:3] == code and \
200 nextline[3:4] != '-':
201 break
202 return line
Guido van Rossum1115ab21992-11-04 15:51:30 +0000203
Tim Peters88869f92001-01-14 23:36:06 +0000204 # Internal: get a response from the server.
205 # Raise various errors if the response indicates an error
206 def getresp(self):
207 resp = self.getmultiline()
208 if self.debugging: print '*resp*', self.sanitize(resp)
209 self.lastresp = resp[:3]
210 c = resp[:1]
211 if c == '4':
212 raise error_temp, resp
213 if c == '5':
214 raise error_perm, resp
215 if c not in '123':
216 raise error_proto, resp
217 return resp
Guido van Rossum1115ab21992-11-04 15:51:30 +0000218
Tim Peters88869f92001-01-14 23:36:06 +0000219 def voidresp(self):
220 """Expect a response beginning with '2'."""
221 resp = self.getresp()
222 if resp[0] != '2':
223 raise error_reply, resp
224 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000225
Tim Peters88869f92001-01-14 23:36:06 +0000226 def abort(self):
227 '''Abort a file transfer. Uses out-of-band data.
228 This does not follow the procedure from the RFC to send Telnet
229 IP and Synch; that doesn't seem to work with the servers I've
230 tried. Instead, just send the ABOR command as OOB data.'''
231 line = 'ABOR' + CRLF
232 if self.debugging > 1: print '*put urgent*', self.sanitize(line)
233 self.sock.send(line, MSG_OOB)
234 resp = self.getmultiline()
235 if resp[:3] not in ('426', '226'):
236 raise error_proto, resp
Guido van Rossumd3166071993-05-24 14:16:22 +0000237
Tim Peters88869f92001-01-14 23:36:06 +0000238 def sendcmd(self, cmd):
239 '''Send a command and return the response.'''
240 self.putcmd(cmd)
241 return self.getresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000242
Tim Peters88869f92001-01-14 23:36:06 +0000243 def voidcmd(self, cmd):
244 """Send a command and expect a response beginning with '2'."""
245 self.putcmd(cmd)
246 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000247
Tim Peters88869f92001-01-14 23:36:06 +0000248 def sendport(self, host, port):
249 '''Send a PORT command with the current host and the given
250 port number.
251 '''
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000252 hbytes = host.split('.')
Tim Peters88869f92001-01-14 23:36:06 +0000253 pbytes = [`port/256`, `port%256`]
254 bytes = hbytes + pbytes
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000255 cmd = 'PORT ' + ','.join(bytes)
Tim Peters88869f92001-01-14 23:36:06 +0000256 return self.voidcmd(cmd)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000257
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000258 def sendeprt(self, host, port):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000259 '''Send a EPRT command with the current host and the given port number.'''
260 af = 0
261 if self.af == socket.AF_INET:
262 af = 1
263 if self.af == socket.AF_INET6:
264 af = 2
265 if af == 0:
266 raise error_proto, 'unsupported address family'
267 fields = ['', `af`, host, `port`, '']
268 cmd = 'EPRT ' + string.joinfields(fields, '|')
269 return self.voidcmd(cmd)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000270
Tim Peters88869f92001-01-14 23:36:06 +0000271 def makeport(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000272 '''Create a new socket and send a PORT command for it.'''
273 for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
274 af, socktype, proto, canonname, sa = res
275 try:
276 sock = socket.socket(af, socktype, proto)
277 sock.bind(sa)
278 except socket.error, msg:
279 sock.close()
280 sock = None
281 continue
282 break
283 if not sock:
284 raise socket.error, msg
285 sock.listen(1)
286 port = sock.getsockname()[1] # Get proper port
287 host = self.sock.getsockname()[0] # Get proper host
288 if self.af == socket.AF_INET:
289 resp = self.sendport(host, port)
290 else:
291 resp = self.sendeprt(host, port)
292 return sock
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000293
294 def makepasv(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000295 if self.af == socket.AF_INET:
296 host, port = parse227(self.sendcmd('PASV'))
297 else:
298 host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
299 return host, port
Guido van Rossum1115ab21992-11-04 15:51:30 +0000300
Tim Peters88869f92001-01-14 23:36:06 +0000301 def ntransfercmd(self, cmd, rest=None):
302 """Initiate a transfer over the data connection.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000303
Tim Peters88869f92001-01-14 23:36:06 +0000304 If the transfer is active, send a port command and the
305 transfer command, and accept the connection. If the server is
306 passive, send a pasv command, connect to it, and start the
307 transfer command. Either way, return the socket for the
308 connection and the expected size of the transfer. The
309 expected size may be None if it could not be determined.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000310
Tim Peters88869f92001-01-14 23:36:06 +0000311 Optional `rest' argument can be a string that is sent as the
312 argument to a RESTART command. This is essentially a server
313 marker used to tell the server to skip over any data up to the
314 given marker.
315 """
316 size = None
317 if self.passiveserver:
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000318 host, port = self.makepasv()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000319 af, socktype, proto, canon, sa = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0]
320 conn = socket.socket(af, socktype, proto)
321 conn.connect(sa)
Tim Peters88869f92001-01-14 23:36:06 +0000322 if rest is not None:
323 self.sendcmd("REST %s" % rest)
324 resp = self.sendcmd(cmd)
325 if resp[0] != '1':
326 raise error_reply, resp
327 else:
328 sock = self.makeport()
329 if rest is not None:
330 self.sendcmd("REST %s" % rest)
331 resp = self.sendcmd(cmd)
332 if resp[0] != '1':
333 raise error_reply, resp
334 conn, sockaddr = sock.accept()
335 if resp[:3] == '150':
336 # this is conditional in case we received a 125
337 size = parse150(resp)
338 return conn, size
Fred Drake4de02d91997-01-10 18:26:09 +0000339
Tim Peters88869f92001-01-14 23:36:06 +0000340 def transfercmd(self, cmd, rest=None):
341 """Like nstransfercmd() but returns only the socket."""
342 return self.ntransfercmd(cmd, rest)[0]
Guido van Rossumc567c601992-11-05 22:22:37 +0000343
Tim Peters88869f92001-01-14 23:36:06 +0000344 def login(self, user = '', passwd = '', acct = ''):
345 '''Login, default anonymous.'''
346 if not user: user = 'anonymous'
347 if not passwd: passwd = ''
348 if not acct: acct = ''
349 if user == 'anonymous' and passwd in ('', '-'):
350 # get fully qualified domain name of local host
351 thishost = socket.getfqdn()
352 try:
353 if os.environ.has_key('LOGNAME'):
354 realuser = os.environ['LOGNAME']
355 elif os.environ.has_key('USER'):
356 realuser = os.environ['USER']
357 else:
358 realuser = 'anonymous'
359 except AttributeError:
360 # Not all systems have os.environ....
361 realuser = 'anonymous'
362 passwd = passwd + realuser + '@' + thishost
363 resp = self.sendcmd('USER ' + user)
364 if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
365 if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
366 if resp[0] != '2':
367 raise error_reply, resp
368 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000369
Tim Peters88869f92001-01-14 23:36:06 +0000370 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
371 """Retrieve data in binary mode.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000372
Tim Peters88869f92001-01-14 23:36:06 +0000373 `cmd' is a RETR command. `callback' is a callback function is
374 called for each block. No more than `blocksize' number of
375 bytes will be read from the socket. Optional `rest' is passed
376 to transfercmd().
Guido van Rossum1115ab21992-11-04 15:51:30 +0000377
Tim Peters88869f92001-01-14 23:36:06 +0000378 A new port is created for you. Return the response code.
379 """
380 self.voidcmd('TYPE I')
381 conn = self.transfercmd(cmd, rest)
382 while 1:
383 data = conn.recv(blocksize)
384 if not data:
385 break
386 callback(data)
387 conn.close()
388 return self.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000389
Tim Peters88869f92001-01-14 23:36:06 +0000390 def retrlines(self, cmd, callback = None):
391 '''Retrieve data in line mode.
392 The argument is a RETR or LIST command.
393 The callback function (2nd argument) is called for each line,
394 with trailing CRLF stripped. This creates a new port for you.
395 print_line() is the default callback.'''
396 if not callback: callback = print_line
397 resp = self.sendcmd('TYPE A')
398 conn = self.transfercmd(cmd)
399 fp = conn.makefile('rb')
400 while 1:
401 line = fp.readline()
402 if self.debugging > 2: print '*retr*', `line`
403 if not line:
404 break
405 if line[-2:] == CRLF:
406 line = line[:-2]
407 elif line[-1:] == '\n':
408 line = line[:-1]
409 callback(line)
410 fp.close()
411 conn.close()
412 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000413
Guido van Rossum4ac83472001-02-15 13:50:36 +0000414 def storbinary(self, cmd, fp, blocksize=8192):
Tim Peters88869f92001-01-14 23:36:06 +0000415 '''Store a file in binary mode.'''
416 self.voidcmd('TYPE I')
417 conn = self.transfercmd(cmd)
418 while 1:
419 buf = fp.read(blocksize)
420 if not buf: break
421 conn.send(buf)
422 conn.close()
423 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000424
Tim Peters88869f92001-01-14 23:36:06 +0000425 def storlines(self, cmd, fp):
426 '''Store a file in line mode.'''
427 self.voidcmd('TYPE A')
428 conn = self.transfercmd(cmd)
429 while 1:
430 buf = fp.readline()
431 if not buf: break
432 if buf[-2:] != CRLF:
433 if buf[-1] in CRLF: buf = buf[:-1]
434 buf = buf + CRLF
435 conn.send(buf)
436 conn.close()
437 return self.voidresp()
Guido van Rossum0eaa74b1996-01-25 18:37:21 +0000438
Tim Peters88869f92001-01-14 23:36:06 +0000439 def acct(self, password):
440 '''Send new account name.'''
441 cmd = 'ACCT ' + password
442 return self.voidcmd(cmd)
Guido van Rossumc567c601992-11-05 22:22:37 +0000443
Tim Peters88869f92001-01-14 23:36:06 +0000444 def nlst(self, *args):
445 '''Return a list of files in a given directory (default the current).'''
446 cmd = 'NLST'
447 for arg in args:
448 cmd = cmd + (' ' + arg)
449 files = []
450 self.retrlines(cmd, files.append)
451 return files
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000452
Tim Peters88869f92001-01-14 23:36:06 +0000453 def dir(self, *args):
454 '''List a directory in long form.
455 By default list current directory to stdout.
456 Optional last argument is callback function; all
457 non-empty arguments before it are concatenated to the
458 LIST command. (This *should* only be used for a pathname.)'''
459 cmd = 'LIST'
460 func = None
461 if args[-1:] and type(args[-1]) != type(''):
462 args, func = args[:-1], args[-1]
463 for arg in args:
464 if arg:
465 cmd = cmd + (' ' + arg)
466 self.retrlines(cmd, func)
Guido van Rossumc567c601992-11-05 22:22:37 +0000467
Tim Peters88869f92001-01-14 23:36:06 +0000468 def rename(self, fromname, toname):
469 '''Rename a file.'''
470 resp = self.sendcmd('RNFR ' + fromname)
471 if resp[0] != '3':
472 raise error_reply, resp
473 return self.voidcmd('RNTO ' + toname)
Guido van Rossuma61bdeb1995-10-11 17:36:31 +0000474
Tim Peters88869f92001-01-14 23:36:06 +0000475 def delete(self, filename):
476 '''Delete a file.'''
477 resp = self.sendcmd('DELE ' + filename)
478 if resp[:3] in ('250', '200'):
479 return resp
480 elif resp[:1] == '5':
481 raise error_perm, resp
482 else:
483 raise error_reply, resp
Guido van Rossum02cf5821993-05-17 08:00:02 +0000484
Tim Peters88869f92001-01-14 23:36:06 +0000485 def cwd(self, dirname):
486 '''Change to a directory.'''
487 if dirname == '..':
488 try:
489 return self.voidcmd('CDUP')
490 except error_perm, msg:
491 if msg[:3] != '500':
492 raise error_perm, msg
493 elif dirname == '':
494 dirname = '.' # does nothing, but could return error
495 cmd = 'CWD ' + dirname
496 return self.voidcmd(cmd)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000497
Tim Peters88869f92001-01-14 23:36:06 +0000498 def size(self, filename):
499 '''Retrieve the size of a file.'''
500 # Note that the RFC doesn't say anything about 'SIZE'
501 resp = self.sendcmd('SIZE ' + filename)
502 if resp[:3] == '213':
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000503 return int(resp[3:].strip())
Guido van Rossumc567c601992-11-05 22:22:37 +0000504
Tim Peters88869f92001-01-14 23:36:06 +0000505 def mkd(self, dirname):
506 '''Make a directory, return its full pathname.'''
507 resp = self.sendcmd('MKD ' + dirname)
508 return parse257(resp)
Guido van Rossum98245091998-02-19 21:15:44 +0000509
Tim Peters88869f92001-01-14 23:36:06 +0000510 def rmd(self, dirname):
511 '''Remove a directory.'''
512 return self.voidcmd('RMD ' + dirname)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000513
Tim Peters88869f92001-01-14 23:36:06 +0000514 def pwd(self):
515 '''Return current working directory.'''
516 resp = self.sendcmd('PWD')
517 return parse257(resp)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000518
Tim Peters88869f92001-01-14 23:36:06 +0000519 def quit(self):
520 '''Quit, and close the connection.'''
521 resp = self.voidcmd('QUIT')
522 self.close()
523 return resp
524
525 def close(self):
526 '''Close the connection without assuming anything about it.'''
Fred Drake9c98a422001-02-28 21:46:37 +0000527 if self.file:
528 self.file.close()
529 self.sock.close()
530 self.file = self.sock = None
Guido van Rossumc567c601992-11-05 22:22:37 +0000531
532
Guido van Rossumacfb82a1997-10-22 20:49:52 +0000533_150_re = None
Fred Drake4de02d91997-01-10 18:26:09 +0000534
535def parse150(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000536 '''Parse the '150' response for a RETR request.
537 Returns the expected transfer size or None; size is not guaranteed to
538 be present in the 150 message.
539 '''
540 if resp[:3] != '150':
541 raise error_reply, resp
542 global _150_re
543 if _150_re is None:
544 import re
545 _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE)
546 m = _150_re.match(resp)
547 if m:
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000548 return int(m.group(1))
Tim Peters88869f92001-01-14 23:36:06 +0000549 return None
Fred Drake4de02d91997-01-10 18:26:09 +0000550
551
Guido van Rossumd2560b01996-05-28 23:41:25 +0000552def parse227(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000553 '''Parse the '227' response for a PASV request.
554 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
555 Return ('host.addr.as.numbers', port#) tuple.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000556
Tim Peters88869f92001-01-14 23:36:06 +0000557 if resp[:3] != '227':
558 raise error_reply, resp
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000559 left = resp.find('(')
Tim Peters88869f92001-01-14 23:36:06 +0000560 if left < 0: raise error_proto, resp
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000561 right = resp.find(')', left + 1)
Tim Peters88869f92001-01-14 23:36:06 +0000562 if right < 0:
563 raise error_proto, resp # should contain '(h1,h2,h3,h4,p1,p2)'
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000564 numbers = resp[left+1:right].split(',')
Tim Peters88869f92001-01-14 23:36:06 +0000565 if len(numbers) != 6:
566 raise error_proto, resp
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000567 host = '.'.join(numbers[:4])
568 port = (int(numbers[4]) << 8) + int(numbers[5])
Tim Peters88869f92001-01-14 23:36:06 +0000569 return host, port
Guido van Rossumd2560b01996-05-28 23:41:25 +0000570
571
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000572def parse229(resp, peer):
573 '''Parse the '229' response for a EPSV request.
574 Raises error_proto if it does not contain '(|||port|)'
575 Return ('host.addr.as.numbers', port#) tuple.'''
576
577 if resp[:3] <> '229':
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000578 raise error_reply, resp
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000579 left = string.find(resp, '(')
580 if left < 0: raise error_proto, resp
581 right = string.find(resp, ')', left + 1)
582 if right < 0:
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000583 raise error_proto, resp # should contain '(|||port|)'
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000584 if resp[left + 1] <> resp[right - 1]:
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000585 raise error_proto, resp
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000586 parts = string.split(resp[left + 1:right], resp[left+1])
587 if len(parts) <> 5:
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000588 raise error_proto, resp
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000589 host = peer[0]
590 port = string.atoi(parts[3])
591 return host, port
592
593
Guido van Rossumc567c601992-11-05 22:22:37 +0000594def parse257(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000595 '''Parse the '257' response for a MKD or PWD request.
596 This is a response to a MKD or PWD request: a directory name.
597 Returns the directoryname in the 257 reply.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000598
Tim Peters88869f92001-01-14 23:36:06 +0000599 if resp[:3] != '257':
600 raise error_reply, resp
601 if resp[3:5] != ' "':
602 return '' # Not compliant to RFC 959, but UNIX ftpd does this
603 dirname = ''
604 i = 5
605 n = len(resp)
606 while i < n:
607 c = resp[i]
608 i = i+1
609 if c == '"':
610 if i >= n or resp[i] != '"':
611 break
612 i = i+1
613 dirname = dirname + c
614 return dirname
Guido van Rossum1115ab21992-11-04 15:51:30 +0000615
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000616
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000617def print_line(line):
Tim Peters88869f92001-01-14 23:36:06 +0000618 '''Default retrlines callback to print a line.'''
619 print line
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000620
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000621
Guido van Rossumd2560b01996-05-28 23:41:25 +0000622def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
Tim Peters88869f92001-01-14 23:36:06 +0000623 '''Copy file from one FTP-instance to another.'''
624 if not targetname: targetname = sourcename
625 type = 'TYPE ' + type
626 source.voidcmd(type)
627 target.voidcmd(type)
628 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
629 target.sendport(sourcehost, sourceport)
630 # RFC 959: the user must "listen" [...] BEFORE sending the
631 # transfer request.
632 # So: STOR before RETR, because here the target is a "user".
633 treply = target.sendcmd('STOR ' + targetname)
634 if treply[:3] not in ('125', '150'): raise error_proto # RFC 959
635 sreply = source.sendcmd('RETR ' + sourcename)
636 if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959
637 source.voidresp()
638 target.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000639
Tim Peters88869f92001-01-14 23:36:06 +0000640
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000641class Netrc:
Tim Peters88869f92001-01-14 23:36:06 +0000642 """Class to parse & provide access to 'netrc' format files.
Fred Drake475d51d1997-06-24 22:02:54 +0000643
Tim Peters88869f92001-01-14 23:36:06 +0000644 See the netrc(4) man page for information on the file format.
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000645
Tim Peters88869f92001-01-14 23:36:06 +0000646 WARNING: This class is obsolete -- use module netrc instead.
Guido van Rossumc822a451998-12-22 16:49:16 +0000647
Tim Peters88869f92001-01-14 23:36:06 +0000648 """
649 __defuser = None
650 __defpasswd = None
651 __defacct = None
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000652
Tim Peters88869f92001-01-14 23:36:06 +0000653 def __init__(self, filename=None):
654 if not filename:
655 if os.environ.has_key("HOME"):
656 filename = os.path.join(os.environ["HOME"],
657 ".netrc")
658 else:
659 raise IOError, \
660 "specify file to load or set $HOME"
661 self.__hosts = {}
662 self.__macros = {}
663 fp = open(filename, "r")
664 in_macro = 0
665 while 1:
666 line = fp.readline()
667 if not line: break
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000668 if in_macro and line.strip():
Tim Peters88869f92001-01-14 23:36:06 +0000669 macro_lines.append(line)
670 continue
671 elif in_macro:
672 self.__macros[macro_name] = tuple(macro_lines)
673 in_macro = 0
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000674 words = line.split()
Tim Peters88869f92001-01-14 23:36:06 +0000675 host = user = passwd = acct = None
676 default = 0
677 i = 0
678 while i < len(words):
679 w1 = words[i]
680 if i+1 < len(words):
681 w2 = words[i + 1]
682 else:
683 w2 = None
684 if w1 == 'default':
685 default = 1
686 elif w1 == 'machine' and w2:
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000687 host = w2.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000688 i = i + 1
689 elif w1 == 'login' and w2:
690 user = w2
691 i = i + 1
692 elif w1 == 'password' and w2:
693 passwd = w2
694 i = i + 1
695 elif w1 == 'account' and w2:
696 acct = w2
697 i = i + 1
698 elif w1 == 'macdef' and w2:
699 macro_name = w2
700 macro_lines = []
701 in_macro = 1
702 break
703 i = i + 1
704 if default:
705 self.__defuser = user or self.__defuser
706 self.__defpasswd = passwd or self.__defpasswd
707 self.__defacct = acct or self.__defacct
708 if host:
709 if self.__hosts.has_key(host):
710 ouser, opasswd, oacct = \
711 self.__hosts[host]
712 user = user or ouser
713 passwd = passwd or opasswd
714 acct = acct or oacct
715 self.__hosts[host] = user, passwd, acct
716 fp.close()
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000717
Tim Peters88869f92001-01-14 23:36:06 +0000718 def get_hosts(self):
719 """Return a list of hosts mentioned in the .netrc file."""
720 return self.__hosts.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000721
Tim Peters88869f92001-01-14 23:36:06 +0000722 def get_account(self, host):
723 """Returns login information for the named host.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000724
Tim Peters88869f92001-01-14 23:36:06 +0000725 The return value is a triple containing userid,
726 password, and the accounting field.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000727
Tim Peters88869f92001-01-14 23:36:06 +0000728 """
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000729 host = host.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000730 user = passwd = acct = None
731 if self.__hosts.has_key(host):
732 user, passwd, acct = self.__hosts[host]
733 user = user or self.__defuser
734 passwd = passwd or self.__defpasswd
735 acct = acct or self.__defacct
736 return user, passwd, acct
Guido van Rossum8ca84201998-03-26 20:56:10 +0000737
Tim Peters88869f92001-01-14 23:36:06 +0000738 def get_macros(self):
739 """Return a list of all defined macro names."""
740 return self.__macros.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000741
Tim Peters88869f92001-01-14 23:36:06 +0000742 def get_macro(self, macro):
743 """Return a sequence of lines which define a named macro."""
744 return self.__macros[macro]
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000745
Fred Drake475d51d1997-06-24 22:02:54 +0000746
Tim Peters88869f92001-01-14 23:36:06 +0000747
Guido van Rossum1115ab21992-11-04 15:51:30 +0000748def test():
Tim Peters88869f92001-01-14 23:36:06 +0000749 '''Test program.
750 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000751
Tim Peters88869f92001-01-14 23:36:06 +0000752 debugging = 0
753 rcfile = None
754 while sys.argv[1] == '-d':
755 debugging = debugging+1
756 del sys.argv[1]
757 if sys.argv[1][:2] == '-r':
758 # get name of alternate ~/.netrc file:
759 rcfile = sys.argv[1][2:]
760 del sys.argv[1]
761 host = sys.argv[1]
762 ftp = FTP(host)
763 ftp.set_debuglevel(debugging)
764 userid = passwd = acct = ''
765 try:
766 netrc = Netrc(rcfile)
767 except IOError:
768 if rcfile is not None:
769 sys.stderr.write("Could not open account file"
770 " -- using anonymous login.")
771 else:
772 try:
773 userid, passwd, acct = netrc.get_account(host)
774 except KeyError:
775 # no account for host
776 sys.stderr.write(
777 "No account -- using anonymous login.")
778 ftp.login(userid, passwd, acct)
779 for file in sys.argv[2:]:
780 if file[:2] == '-l':
781 ftp.dir(file[2:])
782 elif file[:2] == '-d':
783 cmd = 'CWD'
784 if file[2:]: cmd = cmd + ' ' + file[2:]
785 resp = ftp.sendcmd(cmd)
786 elif file == '-p':
787 ftp.set_pasv(not ftp.passiveserver)
788 else:
789 ftp.retrbinary('RETR ' + file, \
790 sys.stdout.write, 1024)
791 ftp.quit()
Guido van Rossum221ec0b1995-08-04 04:39:30 +0000792
793
794if __name__ == '__main__':
Tim Peters88869f92001-01-14 23:36:06 +0000795 test()