1 """An FTP client class and some helper functions.
3 Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds
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
10 '230 Guest login ok, access restrictions apply.'
11 >>> ftp.retrlines('LIST') # list directory contents
13 drwxr-xr-x 8 root wheel 1024 Jan 3 1994 .
14 drwxr-xr-x 8 root wheel 1024 Jan 3 1994 ..
15 drwxr-xr-x 2 root wheel 1024 Jan 3 1994 bin
16 drwxr-xr-x 2 root wheel 1024 Jan 3 1994 etc
17 d-wxrwxr-x 2 ftp wheel 1024 Sep 5 13:43 incoming
18 drwxr-xr-x 2 root wheel 1024 Nov 17 1993 lib
19 drwxr-xr-x 6 1094 wheel 1024 Sep 13 19:07 pub
20 drwxr-xr-x 3 root wheel 1024 Jan 3 1994 usr
21 -rw-r--r-- 1 root root 312 Aug 1 1994 welcome.msg
22 '226 Transfer complete.'
27 A nice test that reveals some of the network dialogue would be:
28 python ftplib.py -d localhost -l -p -l
32 # 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.
41 # Import SOCKS module if it exists, else standard socket module socket
43 import SOCKS
; socket
= SOCKS
; del SOCKS
# import SOCKS as socket
44 from socket
import getfqdn
; socket
.getfqdn
= getfqdn
; del getfqdn
49 # Magic number from <socket.h>
50 MSG_OOB
= 0x1 # Process data out of band
53 # The standard FTP server control port
57 # Exception raised when an error or invalid response is received
58 class Error(Exception): pass
59 class error_reply(Error
): pass # unexpected [123]xx reply
60 class error_temp(Error
): pass # 4xx errors
61 class error_perm(Error
): pass # 5xx errors
62 class error_proto(Error
): pass # response does not begin with [1-5]
65 # All exceptions (hopefully) that may be raised here and that aren't
66 # (always) programming errors on our side
67 all_errors
= (Error
, socket
.error
, IOError, EOFError)
70 # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
77 '''An FTP client class.
79 To create a connection, call the class using these argument:
80 host, user, passwd, acct
81 These are all strings, and have default value ''.
82 Then use self.connect() with optional host and port argument.
84 To download a file, use ftp.retrlines('RETR ' + filename),
85 or ftp.retrbinary() with slightly different arguments.
86 To upload a file, use ftp.storlines() or ftp.storbinary(),
87 which have an open file as argument (see their definitions
89 The download/upload functions first issue appropriate TYPE
90 and PORT or PASV commands.
93 # Initialization method (called by class instantiation).
94 # Initialize host to localhost, port to standard ftp port
95 # Optional arguments are host (for connect()),
96 # and user, passwd, acct (for login())
97 def __init__(self
, host
= '', user
= '', passwd
= '', acct
= ''):
98 # Initialize the instance to something mostly harmless
107 resp
= self
.connect(host
)
108 if user
: resp
= self
.login(user
, passwd
, acct
)
110 def connect(self
, host
= '', port
= 0):
111 '''Connect to host. Arguments are:
112 - host: hostname to connect to (string, default previous host)
113 - port: port to connect to (integer, default previous port)'''
114 if host
: self
.host
= host
115 if port
: self
.port
= port
116 self
.passiveserver
= 0
117 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
118 self
.sock
.connect((self
.host
, self
.port
))
119 self
.file = self
.sock
.makefile('rb')
120 self
.welcome
= self
.getresp()
123 def getwelcome(self
):
124 '''Get the welcome message from the server.
125 (this is read and squirreled away by connect())'''
127 print '*welcome*', self
.sanitize(self
.welcome
)
130 def set_debuglevel(self
, level
):
131 '''Set the debugging level.
132 The required argument level means:
133 0: no debugging output (default)
134 1: print commands and responses but not body text etc.
135 2: also print raw lines read and sent before stripping CR/LF'''
136 self
.debugging
= level
137 debug
= set_debuglevel
139 def set_pasv(self
, val
):
140 '''Use passive or active mode for data transfers.
141 With a false argument, use the normal PORT mode,
142 With a true argument, use the PASV command.'''
143 self
.passiveserver
= val
145 # Internal: "sanitize" a string for printing
146 def sanitize(self
, s
):
147 if s
[:5] == 'pass ' or s
[:5] == 'PASS ':
149 while i
> 5 and s
[i
-1] in '\r\n':
151 s
= s
[:5] + '*'*(i
-5) + s
[i
:]
154 # Internal: send one line to the server, appending CRLF
155 def putline(self
, line
):
157 if self
.debugging
> 1: print '*put*', self
.sanitize(line
)
160 # Internal: send one command to the server (through putline())
161 def putcmd(self
, line
):
162 if self
.debugging
: print '*cmd*', self
.sanitize(line
)
165 # Internal: return one line from the server, stripping CRLF.
166 # Raise EOFError if the connection is closed
168 line
= self
.file.readline()
169 if self
.debugging
> 1:
170 print '*get*', self
.sanitize(line
)
171 if not line
: raise EOFError
172 if line
[-2:] == CRLF
: line
= line
[:-2]
173 elif line
[-1:] in CRLF
: line
= line
[:-1]
176 # Internal: get a response from the server, which may possibly
177 # consist of multiple lines. Return a single string with no
178 # trailing CRLF. If the response consists of multiple lines,
179 # these are separated by '\n' characters in the string
180 def getmultiline(self
):
181 line
= self
.getline()
185 nextline
= self
.getline()
186 line
= line
+ ('\n' + nextline
)
187 if nextline
[:3] == code
and \
188 nextline
[3:4] <> '-':
192 # Internal: get a response from the server.
193 # Raise various errors if the response indicates an error
195 resp
= self
.getmultiline()
196 if self
.debugging
: print '*resp*', self
.sanitize(resp
)
197 self
.lastresp
= resp
[:3]
200 raise error_temp
, resp
202 raise error_perm
, resp
204 raise error_proto
, resp
208 """Expect a response beginning with '2'."""
209 resp
= self
.getresp()
211 raise error_reply
, resp
215 '''Abort a file transfer. Uses out-of-band data.
216 This does not follow the procedure from the RFC to send Telnet
217 IP and Synch; that doesn't seem to work with the servers I've
218 tried. Instead, just send the ABOR command as OOB data.'''
220 if self
.debugging
> 1: print '*put urgent*', self
.sanitize(line
)
221 self
.sock
.send(line
, MSG_OOB
)
222 resp
= self
.getmultiline()
223 if resp
[:3] not in ('426', '226'):
224 raise error_proto
, resp
226 def sendcmd(self
, cmd
):
227 '''Send a command and return the response.'''
229 return self
.getresp()
231 def voidcmd(self
, cmd
):
232 """Send a command and expect a response beginning with '2'."""
234 return self
.voidresp()
236 def sendport(self
, host
, port
):
237 '''Send a PORT command with the current host and the given
240 hbytes
= string
.splitfields(host
, '.')
241 pbytes
= [`port
/256`
, `port
%256`
]
242 bytes
= hbytes
+ pbytes
243 cmd
= 'PORT ' + string
.joinfields(bytes
, ',')
244 return self
.voidcmd(cmd
)
247 '''Create a new socket and send a PORT command for it.'''
249 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
252 dummyhost
, port
= sock
.getsockname() # Get proper port
253 host
, dummyport
= self
.sock
.getsockname() # Get proper host
254 resp
= self
.sendport(host
, port
)
257 def ntransfercmd(self
, cmd
, rest
=None):
258 """Initiate a transfer over the data connection.
260 If the transfer is active, send a port command and the
261 transfer command, and accept the connection. If the server is
262 passive, send a pasv command, connect to it, and start the
263 transfer command. Either way, return the socket for the
264 connection and the expected size of the transfer. The
265 expected size may be None if it could not be determined.
267 Optional `rest' argument can be a string that is sent as the
268 argument to a RESTART command. This is essentially a server
269 marker used to tell the server to skip over any data up to the
273 if self
.passiveserver
:
274 host
, port
= parse227(self
.sendcmd('PASV'))
275 conn
=socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
276 conn
.connect((host
, port
))
278 self
.sendcmd("REST %s" % rest
)
279 resp
= self
.sendcmd(cmd
)
281 raise error_reply
, resp
283 sock
= self
.makeport()
285 self
.sendcmd("REST %s" % rest
)
286 resp
= self
.sendcmd(cmd
)
288 raise error_reply
, resp
289 conn
, sockaddr
= sock
.accept()
290 if resp
[:3] == '150':
291 # this is conditional in case we received a 125
292 size
= parse150(resp
)
295 def transfercmd(self
, cmd
, rest
=None):
296 """Like nstransfercmd() but returns only the socket."""
297 return self
.ntransfercmd(cmd
, rest
)[0]
299 def login(self
, user
= '', passwd
= '', acct
= ''):
300 '''Login, default anonymous.'''
301 if not user
: user
= 'anonymous'
302 if not passwd
: passwd
= ''
303 if not acct
: acct
= ''
304 if user
== 'anonymous' and passwd
in ('', '-'):
305 # get fully qualified domain name of local host
306 thishost
= socket
.getfqdn()
308 if os
.environ
.has_key('LOGNAME'):
309 realuser
= os
.environ
['LOGNAME']
310 elif os
.environ
.has_key('USER'):
311 realuser
= os
.environ
['USER']
313 realuser
= 'anonymous'
314 except AttributeError:
315 # Not all systems have os.environ....
316 realuser
= 'anonymous'
317 passwd
= passwd
+ realuser
+ '@' + thishost
318 resp
= self
.sendcmd('USER ' + user
)
319 if resp
[0] == '3': resp
= self
.sendcmd('PASS ' + passwd
)
320 if resp
[0] == '3': resp
= self
.sendcmd('ACCT ' + acct
)
322 raise error_reply
, resp
325 def retrbinary(self
, cmd
, callback
, blocksize
=8192, rest
=None):
326 """Retrieve data in binary mode.
328 `cmd' is a RETR command. `callback' is a callback function is
329 called for each block. No more than `blocksize' number of
330 bytes will be read from the socket. Optional `rest' is passed
333 A new port is created for you. Return the response code.
335 self
.voidcmd('TYPE I')
336 conn
= self
.transfercmd(cmd
, rest
)
338 data
= conn
.recv(blocksize
)
343 return self
.voidresp()
345 def retrlines(self
, cmd
, callback
= None):
346 '''Retrieve data in line mode.
347 The argument is a RETR or LIST command.
348 The callback function (2nd argument) is called for each line,
349 with trailing CRLF stripped. This creates a new port for you.
350 print_line() is the default callback.'''
351 if not callback
: callback
= print_line
352 resp
= self
.sendcmd('TYPE A')
353 conn
= self
.transfercmd(cmd
)
354 fp
= conn
.makefile('rb')
357 if self
.debugging
> 2: print '*retr*', `line`
360 if line
[-2:] == CRLF
:
362 elif line
[-1:] == '\n':
367 return self
.voidresp()
369 def storbinary(self
, cmd
, fp
, blocksize
):
370 '''Store a file in binary mode.'''
371 self
.voidcmd('TYPE I')
372 conn
= self
.transfercmd(cmd
)
374 buf
= fp
.read(blocksize
)
378 return self
.voidresp()
380 def storlines(self
, cmd
, fp
):
381 '''Store a file in line mode.'''
382 self
.voidcmd('TYPE A')
383 conn
= self
.transfercmd(cmd
)
388 if buf
[-1] in CRLF
: buf
= buf
[:-1]
392 return self
.voidresp()
394 def acct(self
, password
):
395 '''Send new account name.'''
396 cmd
= 'ACCT ' + password
397 return self
.voidcmd(cmd
)
399 def nlst(self
, *args
):
400 '''Return a list of files in a given directory (default the current).'''
403 cmd
= cmd
+ (' ' + arg
)
405 self
.retrlines(cmd
, files
.append
)
408 def dir(self
, *args
):
409 '''List a directory in long form.
410 By default list current directory to stdout.
411 Optional last argument is callback function; all
412 non-empty arguments before it are concatenated to the
413 LIST command. (This *should* only be used for a pathname.)'''
416 if args
[-1:] and type(args
[-1]) != type(''):
417 args
, func
= args
[:-1], args
[-1]
420 cmd
= cmd
+ (' ' + arg
)
421 self
.retrlines(cmd
, func
)
423 def rename(self
, fromname
, toname
):
425 resp
= self
.sendcmd('RNFR ' + fromname
)
427 raise error_reply
, resp
428 return self
.voidcmd('RNTO ' + toname
)
430 def delete(self
, filename
):
432 resp
= self
.sendcmd('DELE ' + filename
)
433 if resp
[:3] in ('250', '200'):
435 elif resp
[:1] == '5':
436 raise error_perm
, resp
438 raise error_reply
, resp
440 def cwd(self
, dirname
):
441 '''Change to a directory.'''
444 return self
.voidcmd('CDUP')
445 except error_perm
, msg
:
447 raise error_perm
, msg
449 dirname
= '.' # does nothing, but could return error
450 cmd
= 'CWD ' + dirname
451 return self
.voidcmd(cmd
)
453 def size(self
, filename
):
454 '''Retrieve the size of a file.'''
455 # Note that the RFC doesn't say anything about 'SIZE'
456 resp
= self
.sendcmd('SIZE ' + filename
)
457 if resp
[:3] == '213':
458 return string
.atoi(string
.strip(resp
[3:]))
460 def mkd(self
, dirname
):
461 '''Make a directory, return its full pathname.'''
462 resp
= self
.sendcmd('MKD ' + dirname
)
463 return parse257(resp
)
465 def rmd(self
, dirname
):
466 '''Remove a directory.'''
467 return self
.voidcmd('RMD ' + dirname
)
470 '''Return current working directory.'''
471 resp
= self
.sendcmd('PWD')
472 return parse257(resp
)
475 '''Quit, and close the connection.'''
476 resp
= self
.voidcmd('QUIT')
481 '''Close the connection without assuming anything about it.'''
484 del self
.file, self
.sock
490 '''Parse the '150' response for a RETR request.
491 Returns the expected transfer size or None; size is not guaranteed to
492 be present in the 150 message.
494 if resp
[:3] != '150':
495 raise error_reply
, resp
499 _150_re
= re
.compile("150 .* \((\d+) bytes\)", re
.IGNORECASE
)
500 m
= _150_re
.match(resp
)
502 return string
.atoi(m
.group(1))
507 '''Parse the '227' response for a PASV request.
508 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
509 Return ('host.addr.as.numbers', port#) tuple.'''
511 if resp
[:3] <> '227':
512 raise error_reply
, resp
513 left
= string
.find(resp
, '(')
514 if left
< 0: raise error_proto
, resp
515 right
= string
.find(resp
, ')', left
+ 1)
517 raise error_proto
, resp
# should contain '(h1,h2,h3,h4,p1,p2)'
518 numbers
= string
.split(resp
[left
+1:right
], ',')
519 if len(numbers
) <> 6:
520 raise error_proto
, resp
521 host
= string
.join(numbers
[:4], '.')
522 port
= (string
.atoi(numbers
[4]) << 8) + string
.atoi(numbers
[5])
527 '''Parse the '257' response for a MKD or PWD request.
528 This is a response to a MKD or PWD request: a directory name.
529 Returns the directoryname in the 257 reply.'''
531 if resp
[:3] <> '257':
532 raise error_reply
, resp
533 if resp
[3:5] <> ' "':
534 return '' # Not compliant to RFC 959, but UNIX ftpd does this
542 if i
>= n
or resp
[i
] <> '"':
545 dirname
= dirname
+ c
549 def print_line(line
):
550 '''Default retrlines callback to print a line.'''
554 def ftpcp(source
, sourcename
, target
, targetname
= '', type = 'I'):
555 '''Copy file from one FTP-instance to another.'''
556 if not targetname
: targetname
= sourcename
557 type = 'TYPE ' + type
560 sourcehost
, sourceport
= parse227(source
.sendcmd('PASV'))
561 target
.sendport(sourcehost
, sourceport
)
562 # RFC 959: the user must "listen" [...] BEFORE sending the
564 # So: STOR before RETR, because here the target is a "user".
565 treply
= target
.sendcmd('STOR ' + targetname
)
566 if treply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
567 sreply
= source
.sendcmd('RETR ' + sourcename
)
568 if sreply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
574 """Class to parse & provide access to 'netrc' format files.
576 See the netrc(4) man page for information on the file format.
578 WARNING: This class is obsolete -- use module netrc instead.
585 def __init__(self
, filename
=None):
587 if os
.environ
.has_key("HOME"):
588 filename
= os
.path
.join(os
.environ
["HOME"],
592 "specify file to load or set $HOME"
595 fp
= open(filename
, "r")
600 if in_macro
and string
.strip(line
):
601 macro_lines
.append(line
)
604 self
.__macros
[macro_name
] = tuple(macro_lines
)
606 words
= string
.split(line
)
607 host
= user
= passwd
= acct
= None
610 while i
< len(words
):
618 elif w1
== 'machine' and w2
:
619 host
= string
.lower(w2
)
621 elif w1
== 'login' and w2
:
624 elif w1
== 'password' and w2
:
627 elif w1
== 'account' and w2
:
630 elif w1
== 'macdef' and w2
:
637 self
.__defuser
= user
or self
.__defuser
638 self
.__defpasswd
= passwd
or self
.__defpasswd
639 self
.__defacct
= acct
or self
.__defacct
641 if self
.__hosts
.has_key(host
):
642 ouser
, opasswd
, oacct
= \
645 passwd
= passwd
or opasswd
647 self
.__hosts
[host
] = user
, passwd
, acct
651 """Return a list of hosts mentioned in the .netrc file."""
652 return self
.__hosts
.keys()
654 def get_account(self
, host
):
655 """Returns login information for the named host.
657 The return value is a triple containing userid,
658 password, and the accounting field.
661 host
= string
.lower(host
)
662 user
= passwd
= acct
= None
663 if self
.__hosts
.has_key(host
):
664 user
, passwd
, acct
= self
.__hosts
[host
]
665 user
= user
or self
.__defuser
666 passwd
= passwd
or self
.__defpasswd
667 acct
= acct
or self
.__defacct
668 return user
, passwd
, acct
670 def get_macros(self
):
671 """Return a list of all defined macro names."""
672 return self
.__macros
.keys()
674 def get_macro(self
, macro
):
675 """Return a sequence of lines which define a named macro."""
676 return self
.__macros
[macro
]
682 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
686 while sys
.argv
[1] == '-d':
687 debugging
= debugging
+1
689 if sys
.argv
[1][:2] == '-r':
690 # get name of alternate ~/.netrc file:
691 rcfile
= sys
.argv
[1][2:]
695 ftp
.set_debuglevel(debugging
)
696 userid
= passwd
= acct
= ''
698 netrc
= Netrc(rcfile
)
700 if rcfile
is not None:
701 sys
.stderr
.write("Could not open account file"
702 " -- using anonymous login.")
705 userid
, passwd
, acct
= netrc
.get_account(host
)
707 # no account for host
709 "No account -- using anonymous login.")
710 ftp
.login(userid
, passwd
, acct
)
711 for file in sys
.argv
[2:]:
714 elif file[:2] == '-d':
716 if file[2:]: cmd
= cmd
+ ' ' + file[2:]
717 resp
= ftp
.sendcmd(cmd
)
719 ftp
.set_pasv(not ftp
.passiveserver
)
721 ftp
.retrbinary('RETR ' + file, \
722 sys
.stdout
.write
, 1024)
726 if __name__
== '__main__':