blob: cf1f32fe724fa4446e7bbbd11df7f78efc88a6cf [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
Martin v. Löwis2ad25692001-07-31 08:40:21 +0000117 msg = "getaddrinfo returns an empty list"
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000118 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:
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000124 if self.sock:
125 self.sock.close()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000126 self.sock = None
127 continue
128 break
129 if not self.sock:
130 raise socket.error, msg
131 self.af = af
132 self.file = self.sock.makefile('rb')
133 self.welcome = self.getresp()
134 return self.welcome
Guido van Rossum1115ab21992-11-04 15:51:30 +0000135
Tim Peters88869f92001-01-14 23:36:06 +0000136 def getwelcome(self):
137 '''Get the welcome message from the server.
138 (this is read and squirreled away by connect())'''
139 if self.debugging:
140 print '*welcome*', self.sanitize(self.welcome)
141 return self.welcome
Guido van Rossum1115ab21992-11-04 15:51:30 +0000142
Tim Peters88869f92001-01-14 23:36:06 +0000143 def set_debuglevel(self, level):
144 '''Set the debugging level.
145 The required argument level means:
146 0: no debugging output (default)
147 1: print commands and responses but not body text etc.
148 2: also print raw lines read and sent before stripping CR/LF'''
149 self.debugging = level
150 debug = set_debuglevel
Guido van Rossum1115ab21992-11-04 15:51:30 +0000151
Tim Peters88869f92001-01-14 23:36:06 +0000152 def set_pasv(self, val):
153 '''Use passive or active mode for data transfers.
154 With a false argument, use the normal PORT mode,
155 With a true argument, use the PASV command.'''
156 self.passiveserver = val
Guido van Rossumd2560b01996-05-28 23:41:25 +0000157
Tim Peters88869f92001-01-14 23:36:06 +0000158 # Internal: "sanitize" a string for printing
159 def sanitize(self, s):
160 if s[:5] == 'pass ' or s[:5] == 'PASS ':
161 i = len(s)
162 while i > 5 and s[i-1] in '\r\n':
163 i = i-1
164 s = s[:5] + '*'*(i-5) + s[i:]
165 return `s`
Guido van Rossumebaf1041995-05-05 15:54:14 +0000166
Tim Peters88869f92001-01-14 23:36:06 +0000167 # Internal: send one line to the server, appending CRLF
168 def putline(self, line):
169 line = line + CRLF
170 if self.debugging > 1: print '*put*', self.sanitize(line)
171 self.sock.send(line)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000172
Tim Peters88869f92001-01-14 23:36:06 +0000173 # Internal: send one command to the server (through putline())
174 def putcmd(self, line):
175 if self.debugging: print '*cmd*', self.sanitize(line)
176 self.putline(line)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000177
Tim Peters88869f92001-01-14 23:36:06 +0000178 # Internal: return one line from the server, stripping CRLF.
179 # Raise EOFError if the connection is closed
180 def getline(self):
181 line = self.file.readline()
182 if self.debugging > 1:
183 print '*get*', self.sanitize(line)
184 if not line: raise EOFError
185 if line[-2:] == CRLF: line = line[:-2]
186 elif line[-1:] in CRLF: line = line[:-1]
187 return line
Guido van Rossum1115ab21992-11-04 15:51:30 +0000188
Tim Peters88869f92001-01-14 23:36:06 +0000189 # Internal: get a response from the server, which may possibly
190 # consist of multiple lines. Return a single string with no
191 # trailing CRLF. If the response consists of multiple lines,
192 # these are separated by '\n' characters in the string
193 def getmultiline(self):
194 line = self.getline()
195 if line[3:4] == '-':
196 code = line[:3]
197 while 1:
198 nextline = self.getline()
199 line = line + ('\n' + nextline)
200 if nextline[:3] == code and \
201 nextline[3:4] != '-':
202 break
203 return line
Guido van Rossum1115ab21992-11-04 15:51:30 +0000204
Tim Peters88869f92001-01-14 23:36:06 +0000205 # Internal: get a response from the server.
206 # Raise various errors if the response indicates an error
207 def getresp(self):
208 resp = self.getmultiline()
209 if self.debugging: print '*resp*', self.sanitize(resp)
210 self.lastresp = resp[:3]
211 c = resp[:1]
212 if c == '4':
213 raise error_temp, resp
214 if c == '5':
215 raise error_perm, resp
216 if c not in '123':
217 raise error_proto, resp
218 return resp
Guido van Rossum1115ab21992-11-04 15:51:30 +0000219
Tim Peters88869f92001-01-14 23:36:06 +0000220 def voidresp(self):
221 """Expect a response beginning with '2'."""
222 resp = self.getresp()
223 if resp[0] != '2':
224 raise error_reply, resp
225 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000226
Tim Peters88869f92001-01-14 23:36:06 +0000227 def abort(self):
228 '''Abort a file transfer. Uses out-of-band data.
229 This does not follow the procedure from the RFC to send Telnet
230 IP and Synch; that doesn't seem to work with the servers I've
231 tried. Instead, just send the ABOR command as OOB data.'''
232 line = 'ABOR' + CRLF
233 if self.debugging > 1: print '*put urgent*', self.sanitize(line)
234 self.sock.send(line, MSG_OOB)
235 resp = self.getmultiline()
236 if resp[:3] not in ('426', '226'):
237 raise error_proto, resp
Guido van Rossumd3166071993-05-24 14:16:22 +0000238
Tim Peters88869f92001-01-14 23:36:06 +0000239 def sendcmd(self, cmd):
240 '''Send a command and return the response.'''
241 self.putcmd(cmd)
242 return self.getresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000243
Tim Peters88869f92001-01-14 23:36:06 +0000244 def voidcmd(self, cmd):
245 """Send a command and expect a response beginning with '2'."""
246 self.putcmd(cmd)
247 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000248
Tim Peters88869f92001-01-14 23:36:06 +0000249 def sendport(self, host, port):
250 '''Send a PORT command with the current host and the given
251 port number.
252 '''
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000253 hbytes = host.split('.')
Tim Peters88869f92001-01-14 23:36:06 +0000254 pbytes = [`port/256`, `port%256`]
255 bytes = hbytes + pbytes
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000256 cmd = 'PORT ' + ','.join(bytes)
Tim Peters88869f92001-01-14 23:36:06 +0000257 return self.voidcmd(cmd)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000258
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000259 def sendeprt(self, host, port):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000260 '''Send a EPRT command with the current host and the given port number.'''
261 af = 0
262 if self.af == socket.AF_INET:
263 af = 1
264 if self.af == socket.AF_INET6:
265 af = 2
266 if af == 0:
267 raise error_proto, 'unsupported address family'
268 fields = ['', `af`, host, `port`, '']
269 cmd = 'EPRT ' + string.joinfields(fields, '|')
270 return self.voidcmd(cmd)
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000271
Tim Peters88869f92001-01-14 23:36:06 +0000272 def makeport(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000273 '''Create a new socket and send a PORT command for it.'''
Martin v. Löwis2ad25692001-07-31 08:40:21 +0000274 msg = "getaddrinfo returns an empty list"
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000275 sock = None
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000276 for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
277 af, socktype, proto, canonname, sa = res
278 try:
279 sock = socket.socket(af, socktype, proto)
280 sock.bind(sa)
281 except socket.error, msg:
Martin v. Löwis322c0d12001-10-07 08:53:32 +0000282 if sock:
283 sock.close()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000284 sock = None
285 continue
286 break
287 if not sock:
288 raise socket.error, msg
289 sock.listen(1)
290 port = sock.getsockname()[1] # Get proper port
291 host = self.sock.getsockname()[0] # Get proper host
292 if self.af == socket.AF_INET:
293 resp = self.sendport(host, port)
294 else:
295 resp = self.sendeprt(host, port)
296 return sock
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000297
298 def makepasv(self):
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000299 if self.af == socket.AF_INET:
300 host, port = parse227(self.sendcmd('PASV'))
301 else:
302 host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
303 return host, port
Guido van Rossum1115ab21992-11-04 15:51:30 +0000304
Tim Peters88869f92001-01-14 23:36:06 +0000305 def ntransfercmd(self, cmd, rest=None):
306 """Initiate a transfer over the data connection.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000307
Tim Peters88869f92001-01-14 23:36:06 +0000308 If the transfer is active, send a port command and the
309 transfer command, and accept the connection. If the server is
310 passive, send a pasv command, connect to it, and start the
311 transfer command. Either way, return the socket for the
312 connection and the expected size of the transfer. The
313 expected size may be None if it could not be determined.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000314
Tim Peters88869f92001-01-14 23:36:06 +0000315 Optional `rest' argument can be a string that is sent as the
316 argument to a RESTART command. This is essentially a server
317 marker used to tell the server to skip over any data up to the
318 given marker.
319 """
320 size = None
321 if self.passiveserver:
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000322 host, port = self.makepasv()
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000323 af, socktype, proto, canon, sa = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0]
324 conn = socket.socket(af, socktype, proto)
325 conn.connect(sa)
Tim Peters88869f92001-01-14 23:36:06 +0000326 if rest is not None:
327 self.sendcmd("REST %s" % rest)
328 resp = self.sendcmd(cmd)
329 if resp[0] != '1':
330 raise error_reply, resp
331 else:
332 sock = self.makeport()
333 if rest is not None:
334 self.sendcmd("REST %s" % rest)
335 resp = self.sendcmd(cmd)
336 if resp[0] != '1':
337 raise error_reply, resp
338 conn, sockaddr = sock.accept()
339 if resp[:3] == '150':
340 # this is conditional in case we received a 125
341 size = parse150(resp)
342 return conn, size
Fred Drake4de02d91997-01-10 18:26:09 +0000343
Tim Peters88869f92001-01-14 23:36:06 +0000344 def transfercmd(self, cmd, rest=None):
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000345 """Like ntransfercmd() but returns only the socket."""
Tim Peters88869f92001-01-14 23:36:06 +0000346 return self.ntransfercmd(cmd, rest)[0]
Guido van Rossumc567c601992-11-05 22:22:37 +0000347
Tim Peters88869f92001-01-14 23:36:06 +0000348 def login(self, user = '', passwd = '', acct = ''):
349 '''Login, default anonymous.'''
350 if not user: user = 'anonymous'
351 if not passwd: passwd = ''
352 if not acct: acct = ''
353 if user == 'anonymous' and passwd in ('', '-'):
Guido van Rossumc33e0772001-12-28 20:54:28 +0000354 # If there is no anonymous ftp password specified
355 # then we'll just use anonymous@
356 # We don't send any other thing because:
357 # - We want to remain anonymous
358 # - We want to stop SPAM
359 # - We don't want to let ftp sites to discriminate by the user,
360 # host or country.
361 passwd = passwd + 'anonymous@'
Tim Peters88869f92001-01-14 23:36:06 +0000362 resp = self.sendcmd('USER ' + user)
363 if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
364 if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
365 if resp[0] != '2':
366 raise error_reply, resp
367 return resp
Guido van Rossumc567c601992-11-05 22:22:37 +0000368
Tim Peters88869f92001-01-14 23:36:06 +0000369 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
370 """Retrieve data in binary mode.
Barry Warsaw100d81e2000-09-01 06:09:23 +0000371
Tim Peters88869f92001-01-14 23:36:06 +0000372 `cmd' is a RETR command. `callback' is a callback function is
373 called for each block. No more than `blocksize' number of
374 bytes will be read from the socket. Optional `rest' is passed
375 to transfercmd().
Guido van Rossum1115ab21992-11-04 15:51:30 +0000376
Tim Peters88869f92001-01-14 23:36:06 +0000377 A new port is created for you. Return the response code.
378 """
379 self.voidcmd('TYPE I')
380 conn = self.transfercmd(cmd, rest)
381 while 1:
382 data = conn.recv(blocksize)
383 if not data:
384 break
385 callback(data)
386 conn.close()
387 return self.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000388
Tim Peters88869f92001-01-14 23:36:06 +0000389 def retrlines(self, cmd, callback = None):
390 '''Retrieve data in line mode.
391 The argument is a RETR or LIST command.
392 The callback function (2nd argument) is called for each line,
393 with trailing CRLF stripped. This creates a new port for you.
394 print_line() is the default callback.'''
395 if not callback: callback = print_line
396 resp = self.sendcmd('TYPE A')
397 conn = self.transfercmd(cmd)
398 fp = conn.makefile('rb')
399 while 1:
400 line = fp.readline()
401 if self.debugging > 2: print '*retr*', `line`
402 if not line:
403 break
404 if line[-2:] == CRLF:
405 line = line[:-2]
406 elif line[-1:] == '\n':
407 line = line[:-1]
408 callback(line)
409 fp.close()
410 conn.close()
411 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000412
Guido van Rossum4ac83472001-02-15 13:50:36 +0000413 def storbinary(self, cmd, fp, blocksize=8192):
Tim Peters88869f92001-01-14 23:36:06 +0000414 '''Store a file in binary mode.'''
415 self.voidcmd('TYPE I')
416 conn = self.transfercmd(cmd)
417 while 1:
418 buf = fp.read(blocksize)
419 if not buf: break
420 conn.send(buf)
421 conn.close()
422 return self.voidresp()
Guido van Rossumc567c601992-11-05 22:22:37 +0000423
Tim Peters88869f92001-01-14 23:36:06 +0000424 def storlines(self, cmd, fp):
425 '''Store a file in line mode.'''
426 self.voidcmd('TYPE A')
427 conn = self.transfercmd(cmd)
428 while 1:
429 buf = fp.readline()
430 if not buf: break
431 if buf[-2:] != CRLF:
432 if buf[-1] in CRLF: buf = buf[:-1]
433 buf = buf + CRLF
434 conn.send(buf)
435 conn.close()
436 return self.voidresp()
Guido van Rossum0eaa74b1996-01-25 18:37:21 +0000437
Tim Peters88869f92001-01-14 23:36:06 +0000438 def acct(self, password):
439 '''Send new account name.'''
440 cmd = 'ACCT ' + password
441 return self.voidcmd(cmd)
Guido van Rossumc567c601992-11-05 22:22:37 +0000442
Tim Peters88869f92001-01-14 23:36:06 +0000443 def nlst(self, *args):
444 '''Return a list of files in a given directory (default the current).'''
445 cmd = 'NLST'
446 for arg in args:
447 cmd = cmd + (' ' + arg)
448 files = []
449 self.retrlines(cmd, files.append)
450 return files
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000451
Tim Peters88869f92001-01-14 23:36:06 +0000452 def dir(self, *args):
453 '''List a directory in long form.
454 By default list current directory to stdout.
455 Optional last argument is callback function; all
456 non-empty arguments before it are concatenated to the
457 LIST command. (This *should* only be used for a pathname.)'''
458 cmd = 'LIST'
459 func = None
460 if args[-1:] and type(args[-1]) != type(''):
461 args, func = args[:-1], args[-1]
462 for arg in args:
463 if arg:
464 cmd = cmd + (' ' + arg)
465 self.retrlines(cmd, func)
Guido van Rossumc567c601992-11-05 22:22:37 +0000466
Tim Peters88869f92001-01-14 23:36:06 +0000467 def rename(self, fromname, toname):
468 '''Rename a file.'''
469 resp = self.sendcmd('RNFR ' + fromname)
470 if resp[0] != '3':
471 raise error_reply, resp
472 return self.voidcmd('RNTO ' + toname)
Guido van Rossuma61bdeb1995-10-11 17:36:31 +0000473
Tim Peters88869f92001-01-14 23:36:06 +0000474 def delete(self, filename):
475 '''Delete a file.'''
476 resp = self.sendcmd('DELE ' + filename)
477 if resp[:3] in ('250', '200'):
478 return resp
479 elif resp[:1] == '5':
480 raise error_perm, resp
481 else:
482 raise error_reply, resp
Guido van Rossum02cf5821993-05-17 08:00:02 +0000483
Tim Peters88869f92001-01-14 23:36:06 +0000484 def cwd(self, dirname):
485 '''Change to a directory.'''
486 if dirname == '..':
487 try:
488 return self.voidcmd('CDUP')
489 except error_perm, msg:
490 if msg[:3] != '500':
491 raise error_perm, msg
492 elif dirname == '':
493 dirname = '.' # does nothing, but could return error
494 cmd = 'CWD ' + dirname
495 return self.voidcmd(cmd)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000496
Tim Peters88869f92001-01-14 23:36:06 +0000497 def size(self, filename):
498 '''Retrieve the size of a file.'''
499 # Note that the RFC doesn't say anything about 'SIZE'
500 resp = self.sendcmd('SIZE ' + filename)
501 if resp[:3] == '213':
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000502 s = resp[3:].strip()
503 try:
504 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000505 except (OverflowError, ValueError):
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000506 return long(s)
Guido van Rossumc567c601992-11-05 22:22:37 +0000507
Tim Peters88869f92001-01-14 23:36:06 +0000508 def mkd(self, dirname):
509 '''Make a directory, return its full pathname.'''
510 resp = self.sendcmd('MKD ' + dirname)
511 return parse257(resp)
Guido van Rossum98245091998-02-19 21:15:44 +0000512
Tim Peters88869f92001-01-14 23:36:06 +0000513 def rmd(self, dirname):
514 '''Remove a directory.'''
515 return self.voidcmd('RMD ' + dirname)
Guido van Rossum1115ab21992-11-04 15:51:30 +0000516
Tim Peters88869f92001-01-14 23:36:06 +0000517 def pwd(self):
518 '''Return current working directory.'''
519 resp = self.sendcmd('PWD')
520 return parse257(resp)
Guido van Rossum17ed1ae1993-06-01 13:21:04 +0000521
Tim Peters88869f92001-01-14 23:36:06 +0000522 def quit(self):
523 '''Quit, and close the connection.'''
524 resp = self.voidcmd('QUIT')
525 self.close()
526 return resp
527
528 def close(self):
529 '''Close the connection without assuming anything about it.'''
Fred Drake9c98a422001-02-28 21:46:37 +0000530 if self.file:
531 self.file.close()
532 self.sock.close()
533 self.file = self.sock = None
Guido van Rossumc567c601992-11-05 22:22:37 +0000534
535
Guido van Rossumacfb82a1997-10-22 20:49:52 +0000536_150_re = None
Fred Drake4de02d91997-01-10 18:26:09 +0000537
538def parse150(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000539 '''Parse the '150' response for a RETR request.
540 Returns the expected transfer size or None; size is not guaranteed to
541 be present in the 150 message.
542 '''
543 if resp[:3] != '150':
544 raise error_reply, resp
545 global _150_re
546 if _150_re is None:
547 import re
548 _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE)
549 m = _150_re.match(resp)
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000550 if not m:
551 return None
552 s = m.group(1)
553 try:
554 return int(s)
Guido van Rossum1f74cb32001-10-17 17:21:47 +0000555 except (OverflowError, ValueError):
Guido van Rossumb6aca6a2001-10-16 19:45:52 +0000556 return long(s)
Fred Drake4de02d91997-01-10 18:26:09 +0000557
558
Guido van Rossum70297d32001-08-17 17:24:29 +0000559_227_re = None
560
Guido van Rossumd2560b01996-05-28 23:41:25 +0000561def parse227(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000562 '''Parse the '227' response for a PASV request.
563 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
564 Return ('host.addr.as.numbers', port#) tuple.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000565
Tim Peters88869f92001-01-14 23:36:06 +0000566 if resp[:3] != '227':
567 raise error_reply, resp
Guido van Rossum70297d32001-08-17 17:24:29 +0000568 global _227_re
569 if _227_re is None:
570 import re
571 _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
572 m = _227_re.search(resp)
573 if not m:
Tim Peters88869f92001-01-14 23:36:06 +0000574 raise error_proto, resp
Guido van Rossum70297d32001-08-17 17:24:29 +0000575 numbers = m.groups()
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000576 host = '.'.join(numbers[:4])
577 port = (int(numbers[4]) << 8) + int(numbers[5])
Tim Peters88869f92001-01-14 23:36:06 +0000578 return host, port
Guido van Rossumd2560b01996-05-28 23:41:25 +0000579
580
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000581def parse229(resp, peer):
582 '''Parse the '229' response for a EPSV request.
583 Raises error_proto if it does not contain '(|||port|)'
584 Return ('host.addr.as.numbers', port#) tuple.'''
585
586 if resp[:3] <> '229':
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000587 raise error_reply, resp
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000588 left = string.find(resp, '(')
589 if left < 0: raise error_proto, resp
590 right = string.find(resp, ')', left + 1)
591 if right < 0:
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000592 raise error_proto, resp # should contain '(|||port|)'
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000593 if resp[left + 1] <> resp[right - 1]:
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000594 raise error_proto, resp
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000595 parts = string.split(resp[left + 1:right], resp[left+1])
596 if len(parts) <> 5:
Martin v. Löwis4eb59402001-07-26 13:37:33 +0000597 raise error_proto, resp
Martin v. Löwisa43c2f82001-07-24 20:34:08 +0000598 host = peer[0]
599 port = string.atoi(parts[3])
600 return host, port
601
602
Guido van Rossumc567c601992-11-05 22:22:37 +0000603def parse257(resp):
Tim Peters88869f92001-01-14 23:36:06 +0000604 '''Parse the '257' response for a MKD or PWD request.
605 This is a response to a MKD or PWD request: a directory name.
606 Returns the directoryname in the 257 reply.'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000607
Tim Peters88869f92001-01-14 23:36:06 +0000608 if resp[:3] != '257':
609 raise error_reply, resp
610 if resp[3:5] != ' "':
611 return '' # Not compliant to RFC 959, but UNIX ftpd does this
612 dirname = ''
613 i = 5
614 n = len(resp)
615 while i < n:
616 c = resp[i]
617 i = i+1
618 if c == '"':
619 if i >= n or resp[i] != '"':
620 break
621 i = i+1
622 dirname = dirname + c
623 return dirname
Guido van Rossum1115ab21992-11-04 15:51:30 +0000624
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000625
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000626def print_line(line):
Tim Peters88869f92001-01-14 23:36:06 +0000627 '''Default retrlines callback to print a line.'''
628 print line
Guido van Rossumae3b3a31993-11-30 13:43:54 +0000629
Guido van Rossum2f3941d1997-10-07 14:49:56 +0000630
Guido van Rossumd2560b01996-05-28 23:41:25 +0000631def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
Tim Peters88869f92001-01-14 23:36:06 +0000632 '''Copy file from one FTP-instance to another.'''
633 if not targetname: targetname = sourcename
634 type = 'TYPE ' + type
635 source.voidcmd(type)
636 target.voidcmd(type)
637 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
638 target.sendport(sourcehost, sourceport)
639 # RFC 959: the user must "listen" [...] BEFORE sending the
640 # transfer request.
641 # So: STOR before RETR, because here the target is a "user".
642 treply = target.sendcmd('STOR ' + targetname)
643 if treply[:3] not in ('125', '150'): raise error_proto # RFC 959
644 sreply = source.sendcmd('RETR ' + sourcename)
645 if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959
646 source.voidresp()
647 target.voidresp()
Guido van Rossum1115ab21992-11-04 15:51:30 +0000648
Tim Peters88869f92001-01-14 23:36:06 +0000649
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000650class Netrc:
Tim Peters88869f92001-01-14 23:36:06 +0000651 """Class to parse & provide access to 'netrc' format files.
Fred Drake475d51d1997-06-24 22:02:54 +0000652
Tim Peters88869f92001-01-14 23:36:06 +0000653 See the netrc(4) man page for information on the file format.
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000654
Tim Peters88869f92001-01-14 23:36:06 +0000655 WARNING: This class is obsolete -- use module netrc instead.
Guido van Rossumc822a451998-12-22 16:49:16 +0000656
Tim Peters88869f92001-01-14 23:36:06 +0000657 """
658 __defuser = None
659 __defpasswd = None
660 __defacct = None
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000661
Tim Peters88869f92001-01-14 23:36:06 +0000662 def __init__(self, filename=None):
663 if not filename:
664 if os.environ.has_key("HOME"):
665 filename = os.path.join(os.environ["HOME"],
666 ".netrc")
667 else:
668 raise IOError, \
669 "specify file to load or set $HOME"
670 self.__hosts = {}
671 self.__macros = {}
672 fp = open(filename, "r")
673 in_macro = 0
674 while 1:
675 line = fp.readline()
676 if not line: break
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000677 if in_macro and line.strip():
Tim Peters88869f92001-01-14 23:36:06 +0000678 macro_lines.append(line)
679 continue
680 elif in_macro:
681 self.__macros[macro_name] = tuple(macro_lines)
682 in_macro = 0
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000683 words = line.split()
Tim Peters88869f92001-01-14 23:36:06 +0000684 host = user = passwd = acct = None
685 default = 0
686 i = 0
687 while i < len(words):
688 w1 = words[i]
689 if i+1 < len(words):
690 w2 = words[i + 1]
691 else:
692 w2 = None
693 if w1 == 'default':
694 default = 1
695 elif w1 == 'machine' and w2:
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000696 host = w2.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000697 i = i + 1
698 elif w1 == 'login' and w2:
699 user = w2
700 i = i + 1
701 elif w1 == 'password' and w2:
702 passwd = w2
703 i = i + 1
704 elif w1 == 'account' and w2:
705 acct = w2
706 i = i + 1
707 elif w1 == 'macdef' and w2:
708 macro_name = w2
709 macro_lines = []
710 in_macro = 1
711 break
712 i = i + 1
713 if default:
714 self.__defuser = user or self.__defuser
715 self.__defpasswd = passwd or self.__defpasswd
716 self.__defacct = acct or self.__defacct
717 if host:
718 if self.__hosts.has_key(host):
719 ouser, opasswd, oacct = \
720 self.__hosts[host]
721 user = user or ouser
722 passwd = passwd or opasswd
723 acct = acct or oacct
724 self.__hosts[host] = user, passwd, acct
725 fp.close()
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000726
Tim Peters88869f92001-01-14 23:36:06 +0000727 def get_hosts(self):
728 """Return a list of hosts mentioned in the .netrc file."""
729 return self.__hosts.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000730
Tim Peters88869f92001-01-14 23:36:06 +0000731 def get_account(self, host):
732 """Returns login information for the named host.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000733
Tim Peters88869f92001-01-14 23:36:06 +0000734 The return value is a triple containing userid,
735 password, and the accounting field.
Guido van Rossum8ca84201998-03-26 20:56:10 +0000736
Tim Peters88869f92001-01-14 23:36:06 +0000737 """
Eric S. Raymondc95bf692001-02-09 10:06:47 +0000738 host = host.lower()
Tim Peters88869f92001-01-14 23:36:06 +0000739 user = passwd = acct = None
740 if self.__hosts.has_key(host):
741 user, passwd, acct = self.__hosts[host]
742 user = user or self.__defuser
743 passwd = passwd or self.__defpasswd
744 acct = acct or self.__defacct
745 return user, passwd, acct
Guido van Rossum8ca84201998-03-26 20:56:10 +0000746
Tim Peters88869f92001-01-14 23:36:06 +0000747 def get_macros(self):
748 """Return a list of all defined macro names."""
749 return self.__macros.keys()
Guido van Rossum8ca84201998-03-26 20:56:10 +0000750
Tim Peters88869f92001-01-14 23:36:06 +0000751 def get_macro(self, macro):
752 """Return a sequence of lines which define a named macro."""
753 return self.__macros[macro]
Guido van Rossum56d1e3a1997-03-14 04:16:54 +0000754
Fred Drake475d51d1997-06-24 22:02:54 +0000755
Tim Peters88869f92001-01-14 23:36:06 +0000756
Guido van Rossum1115ab21992-11-04 15:51:30 +0000757def test():
Tim Peters88869f92001-01-14 23:36:06 +0000758 '''Test program.
759 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
Guido van Rossumd2560b01996-05-28 23:41:25 +0000760
Tim Peters88869f92001-01-14 23:36:06 +0000761 debugging = 0
762 rcfile = None
763 while sys.argv[1] == '-d':
764 debugging = debugging+1
765 del sys.argv[1]
766 if sys.argv[1][:2] == '-r':
767 # get name of alternate ~/.netrc file:
768 rcfile = sys.argv[1][2:]
769 del sys.argv[1]
770 host = sys.argv[1]
771 ftp = FTP(host)
772 ftp.set_debuglevel(debugging)
773 userid = passwd = acct = ''
774 try:
775 netrc = Netrc(rcfile)
776 except IOError:
777 if rcfile is not None:
778 sys.stderr.write("Could not open account file"
779 " -- using anonymous login.")
780 else:
781 try:
782 userid, passwd, acct = netrc.get_account(host)
783 except KeyError:
784 # no account for host
785 sys.stderr.write(
786 "No account -- using anonymous login.")
787 ftp.login(userid, passwd, acct)
788 for file in sys.argv[2:]:
789 if file[:2] == '-l':
790 ftp.dir(file[2:])
791 elif file[:2] == '-d':
792 cmd = 'CWD'
793 if file[2:]: cmd = cmd + ' ' + file[2:]
794 resp = ftp.sendcmd(cmd)
795 elif file == '-p':
796 ftp.set_pasv(not ftp.passiveserver)
797 else:
798 ftp.retrbinary('RETR ' + file, \
799 sys.stdout.write, 1024)
800 ftp.quit()
Guido van Rossum221ec0b1995-08-04 04:39:30 +0000801
802
803if __name__ == '__main__':
Tim Peters88869f92001-01-14 23:36:06 +0000804 test()