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 anonymous@
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.
35 # Modified by Phil Schwartz to add storbinary and storlines callbacks.
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
48 __all__
= ["FTP","Netrc"]
50 # Magic number from <socket.h>
51 MSG_OOB
= 0x1 # Process data out of band
54 # The standard FTP server control port
58 # Exception raised when an error or invalid response is received
59 class Error(Exception): pass
60 class error_reply(Error
): pass # unexpected [123]xx reply
61 class error_temp(Error
): pass # 4xx errors
62 class error_perm(Error
): pass # 5xx errors
63 class error_proto(Error
): pass # response does not begin with [1-5]
66 # All exceptions (hopefully) that may be raised here and that aren't
67 # (always) programming errors on our side
68 all_errors
= (Error
, socket
.error
, IOError, EOFError)
71 # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
78 '''An FTP client class.
80 To create a connection, call the class using these arguments:
81 host, user, passwd, acct, timeout
83 The first four arguments are all strings, and have default value ''.
84 timeout must be numeric and defaults to None if not passed,
85 meaning that no timeout will be set on any ftp socket(s)
86 If a timeout is passed, then this is now the default timeout for all ftp
87 socket operations for this instance.
89 Then use self.connect() with optional host and port argument.
91 To download a file, use ftp.retrlines('RETR ' + filename),
92 or ftp.retrbinary() with slightly different arguments.
93 To upload a file, use ftp.storlines() or ftp.storbinary(),
94 which have an open file as argument (see their definitions
96 The download/upload functions first issue appropriate TYPE
97 and PORT or PASV commands.
108 # Initialization method (called by class instantiation).
109 # Initialize host to localhost, port to standard ftp port
110 # Optional arguments are host (for connect()),
111 # and user, passwd, acct (for login())
112 def __init__(self
, host
='', user
='', passwd
='', acct
='', timeout
=None):
113 self
.timeout
= timeout
117 self
.login(user
, passwd
, acct
)
119 def connect(self
, host
='', port
=0, timeout
=None):
120 '''Connect to host. Arguments are:
121 - host: hostname to connect to (string, default previous host)
122 - port: port to connect to (integer, default previous port)
128 if timeout
is not None:
129 self
.timeout
= timeout
130 self
.sock
= socket
.create_connection((self
.host
, self
.port
), self
.timeout
)
131 self
.af
= self
.sock
.family
132 self
.file = self
.sock
.makefile('rb')
133 self
.welcome
= self
.getresp()
136 def getwelcome(self
):
137 '''Get the welcome message from the server.
138 (this is read and squirreled away by connect())'''
140 print '*welcome*', self
.sanitize(self
.welcome
)
143 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
152 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
158 # Internal: "sanitize" a string for printing
159 def sanitize(self
, s
):
160 if s
[:5] == 'pass ' or s
[:5] == 'PASS ':
162 while i
> 5 and s
[i
-1] in '\r\n':
164 s
= s
[:5] + '*'*(i
-5) + s
[i
:]
167 # Internal: send one line to the server, appending CRLF
168 def putline(self
, line
):
170 if self
.debugging
> 1: print '*put*', self
.sanitize(line
)
171 self
.sock
.sendall(line
)
173 # Internal: send one command to the server (through putline())
174 def putcmd(self
, line
):
175 if self
.debugging
: print '*cmd*', self
.sanitize(line
)
178 # Internal: return one line from the server, stripping CRLF.
179 # Raise EOFError if the connection is closed
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]
189 # 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()
198 nextline
= self
.getline()
199 line
= line
+ ('\n' + nextline
)
200 if nextline
[:3] == code
and \
201 nextline
[3:4] != '-':
205 # Internal: get a response from the server.
206 # Raise various errors if the response indicates an error
208 resp
= self
.getmultiline()
209 if self
.debugging
: print '*resp*', self
.sanitize(resp
)
210 self
.lastresp
= resp
[:3]
212 if c
in ('1', '2', '3'):
215 raise error_temp
, resp
217 raise error_perm
, resp
218 raise error_proto
, resp
221 """Expect a response beginning with '2'."""
222 resp
= self
.getresp()
224 raise error_reply
, resp
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.'''
233 if self
.debugging
> 1: print '*put urgent*', self
.sanitize(line
)
234 self
.sock
.sendall(line
, MSG_OOB
)
235 resp
= self
.getmultiline()
236 if resp
[:3] not in ('426', '226'):
237 raise error_proto
, resp
239 def sendcmd(self
, cmd
):
240 '''Send a command and return the response.'''
242 return self
.getresp()
244 def voidcmd(self
, cmd
):
245 """Send a command and expect a response beginning with '2'."""
247 return self
.voidresp()
249 def sendport(self
, host
, port
):
250 '''Send a PORT command with the current host and the given
253 hbytes
= host
.split('.')
254 pbytes
= [repr(port
/256), repr(port
%256)]
255 bytes
= hbytes
+ pbytes
256 cmd
= 'PORT ' + ','.join(bytes
)
257 return self
.voidcmd(cmd
)
259 def sendeprt(self
, host
, port
):
260 '''Send a EPRT command with the current host and the given port number.'''
262 if self
.af
== socket
.AF_INET
:
264 if self
.af
== socket
.AF_INET6
:
267 raise error_proto
, 'unsupported address family'
268 fields
= ['', repr(af
), host
, repr(port
), '']
269 cmd
= 'EPRT ' + '|'.join(fields
)
270 return self
.voidcmd(cmd
)
273 '''Create a new socket and send a PORT command for it.'''
274 msg
= "getaddrinfo returns an empty list"
276 for res
in socket
.getaddrinfo(None, 0, self
.af
, socket
.SOCK_STREAM
, 0, socket
.AI_PASSIVE
):
277 af
, socktype
, proto
, canonname
, sa
= res
279 sock
= socket
.socket(af
, socktype
, proto
)
281 except socket
.error
, msg
:
288 raise socket
.error
, msg
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
)
295 resp
= self
.sendeprt(host
, port
)
299 if self
.af
== socket
.AF_INET
:
300 host
, port
= parse227(self
.sendcmd('PASV'))
302 host
, port
= parse229(self
.sendcmd('EPSV'), self
.sock
.getpeername())
305 def ntransfercmd(self
, cmd
, rest
=None):
306 """Initiate a transfer over the data connection.
308 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.
315 Optional `rest' argument can be a string that is sent as the
316 argument to a REST command. This is essentially a server
317 marker used to tell the server to skip over any data up to the
321 if self
.passiveserver
:
322 host
, port
= self
.makepasv()
323 conn
= socket
.create_connection((host
, port
), self
.timeout
)
325 self
.sendcmd("REST %s" % rest
)
326 resp
= self
.sendcmd(cmd
)
327 # Some servers apparently send a 200 reply to
328 # a LIST or STOR command, before the 150 reply
329 # (and way before the 226 reply). This seems to
330 # be in violation of the protocol (which only allows
331 # 1xx or error messages for LIST), so we just discard
334 resp
= self
.getresp()
336 raise error_reply
, resp
338 sock
= self
.makeport()
340 self
.sendcmd("REST %s" % rest
)
341 resp
= self
.sendcmd(cmd
)
344 resp
= self
.getresp()
346 raise error_reply
, resp
347 conn
, sockaddr
= sock
.accept()
348 if resp
[:3] == '150':
349 # this is conditional in case we received a 125
350 size
= parse150(resp
)
353 def transfercmd(self
, cmd
, rest
=None):
354 """Like ntransfercmd() but returns only the socket."""
355 return self
.ntransfercmd(cmd
, rest
)[0]
357 def login(self
, user
= '', passwd
= '', acct
= ''):
358 '''Login, default anonymous.'''
359 if not user
: user
= 'anonymous'
360 if not passwd
: passwd
= ''
361 if not acct
: acct
= ''
362 if user
== 'anonymous' and passwd
in ('', '-'):
363 # If there is no anonymous ftp password specified
364 # then we'll just use anonymous@
365 # We don't send any other thing because:
366 # - We want to remain anonymous
367 # - We want to stop SPAM
368 # - We don't want to let ftp sites to discriminate by the user,
370 passwd
= passwd
+ 'anonymous@'
371 resp
= self
.sendcmd('USER ' + user
)
372 if resp
[0] == '3': resp
= self
.sendcmd('PASS ' + passwd
)
373 if resp
[0] == '3': resp
= self
.sendcmd('ACCT ' + acct
)
375 raise error_reply
, resp
378 def retrbinary(self
, cmd
, callback
, blocksize
=8192, rest
=None):
379 """Retrieve data in binary mode. A new port is created for you.
383 callback: A single parameter callable to be called on each
385 blocksize: The maximum number of bytes to read from the
386 socket at one time. [default: 8192]
387 rest: Passed to transfercmd(). [default: None]
392 self
.voidcmd('TYPE I')
393 conn
= self
.transfercmd(cmd
, rest
)
395 data
= conn
.recv(blocksize
)
400 return self
.voidresp()
402 def retrlines(self
, cmd
, callback
= None):
403 """Retrieve data in line mode. A new port is created for you.
406 cmd: A RETR, LIST, NLST, or MLSD command.
407 callback: An optional single parameter callable that is called
408 for each line with the trailing CRLF stripped.
409 [default: print_line()]
414 if callback
is None: callback
= print_line
415 resp
= self
.sendcmd('TYPE A')
416 conn
= self
.transfercmd(cmd
)
417 fp
= conn
.makefile('rb')
420 if self
.debugging
> 2: print '*retr*', repr(line
)
423 if line
[-2:] == CRLF
:
425 elif line
[-1:] == '\n':
430 return self
.voidresp()
432 def storbinary(self
, cmd
, fp
, blocksize
=8192, callback
=None):
433 """Store a file in binary mode. A new port is created for you.
437 fp: A file-like object with a read(num_bytes) method.
438 blocksize: The maximum data size to read from fp and send over
439 the connection at once. [default: 8192]
440 callback: An optional single parameter callable that is called on
441 on each block of data after it is sent. [default: None]
446 self
.voidcmd('TYPE I')
447 conn
= self
.transfercmd(cmd
)
449 buf
= fp
.read(blocksize
)
452 if callback
: callback(buf
)
454 return self
.voidresp()
456 def storlines(self
, cmd
, fp
, callback
=None):
457 """Store a file in line mode. A new port is created for you.
461 fp: A file-like object with a readline() method.
462 callback: An optional single parameter callable that is called on
463 on each line after it is sent. [default: None]
468 self
.voidcmd('TYPE A')
469 conn
= self
.transfercmd(cmd
)
474 if buf
[-1] in CRLF
: buf
= buf
[:-1]
477 if callback
: callback(buf
)
479 return self
.voidresp()
481 def acct(self
, password
):
482 '''Send new account name.'''
483 cmd
= 'ACCT ' + password
484 return self
.voidcmd(cmd
)
486 def nlst(self
, *args
):
487 '''Return a list of files in a given directory (default the current).'''
490 cmd
= cmd
+ (' ' + arg
)
492 self
.retrlines(cmd
, files
.append
)
495 def dir(self
, *args
):
496 '''List a directory in long form.
497 By default list current directory to stdout.
498 Optional last argument is callback function; all
499 non-empty arguments before it are concatenated to the
500 LIST command. (This *should* only be used for a pathname.)'''
503 if args
[-1:] and type(args
[-1]) != type(''):
504 args
, func
= args
[:-1], args
[-1]
507 cmd
= cmd
+ (' ' + arg
)
508 self
.retrlines(cmd
, func
)
510 def rename(self
, fromname
, toname
):
512 resp
= self
.sendcmd('RNFR ' + fromname
)
514 raise error_reply
, resp
515 return self
.voidcmd('RNTO ' + toname
)
517 def delete(self
, filename
):
519 resp
= self
.sendcmd('DELE ' + filename
)
520 if resp
[:3] in ('250', '200'):
522 elif resp
[:1] == '5':
523 raise error_perm
, resp
525 raise error_reply
, resp
527 def cwd(self
, dirname
):
528 '''Change to a directory.'''
531 return self
.voidcmd('CDUP')
532 except error_perm
, msg
:
533 if msg
.args
[0][:3] != '500':
536 dirname
= '.' # does nothing, but could return error
537 cmd
= 'CWD ' + dirname
538 return self
.voidcmd(cmd
)
540 def size(self
, filename
):
541 '''Retrieve the size of a file.'''
542 # The SIZE command is defined in RFC-3659
543 resp
= self
.sendcmd('SIZE ' + filename
)
544 if resp
[:3] == '213':
548 except (OverflowError, ValueError):
551 def mkd(self
, dirname
):
552 '''Make a directory, return its full pathname.'''
553 resp
= self
.sendcmd('MKD ' + dirname
)
554 return parse257(resp
)
556 def rmd(self
, dirname
):
557 '''Remove a directory.'''
558 return self
.voidcmd('RMD ' + dirname
)
561 '''Return current working directory.'''
562 resp
= self
.sendcmd('PWD')
563 return parse257(resp
)
566 '''Quit, and close the connection.'''
567 resp
= self
.voidcmd('QUIT')
572 '''Close the connection without assuming anything about it.'''
576 self
.file = self
.sock
= None
582 '''Parse the '150' response for a RETR request.
583 Returns the expected transfer size or None; size is not guaranteed to
584 be present in the 150 message.
586 if resp
[:3] != '150':
587 raise error_reply
, resp
591 _150_re
= re
.compile("150 .* \((\d+) bytes\)", re
.IGNORECASE
)
592 m
= _150_re
.match(resp
)
598 except (OverflowError, ValueError):
605 '''Parse the '227' response for a PASV request.
606 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
607 Return ('host.addr.as.numbers', port#) tuple.'''
609 if resp
[:3] != '227':
610 raise error_reply
, resp
614 _227_re
= re
.compile(r
'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
615 m
= _227_re
.search(resp
)
617 raise error_proto
, resp
619 host
= '.'.join(numbers
[:4])
620 port
= (int(numbers
[4]) << 8) + int(numbers
[5])
624 def parse229(resp
, peer
):
625 '''Parse the '229' response for a EPSV request.
626 Raises error_proto if it does not contain '(|||port|)'
627 Return ('host.addr.as.numbers', port#) tuple.'''
629 if resp
[:3] != '229':
630 raise error_reply
, resp
631 left
= resp
.find('(')
632 if left
< 0: raise error_proto
, resp
633 right
= resp
.find(')', left
+ 1)
635 raise error_proto
, resp
# should contain '(|||port|)'
636 if resp
[left
+ 1] != resp
[right
- 1]:
637 raise error_proto
, resp
638 parts
= resp
[left
+ 1:right
].split(resp
[left
+1])
640 raise error_proto
, resp
647 '''Parse the '257' response for a MKD or PWD request.
648 This is a response to a MKD or PWD request: a directory name.
649 Returns the directoryname in the 257 reply.'''
651 if resp
[:3] != '257':
652 raise error_reply
, resp
653 if resp
[3:5] != ' "':
654 return '' # Not compliant to RFC 959, but UNIX ftpd does this
662 if i
>= n
or resp
[i
] != '"':
665 dirname
= dirname
+ c
669 def print_line(line
):
670 '''Default retrlines callback to print a line.'''
674 def ftpcp(source
, sourcename
, target
, targetname
= '', type = 'I'):
675 '''Copy file from one FTP-instance to another.'''
676 if not targetname
: targetname
= sourcename
677 type = 'TYPE ' + type
680 sourcehost
, sourceport
= parse227(source
.sendcmd('PASV'))
681 target
.sendport(sourcehost
, sourceport
)
682 # RFC 959: the user must "listen" [...] BEFORE sending the
684 # So: STOR before RETR, because here the target is a "user".
685 treply
= target
.sendcmd('STOR ' + targetname
)
686 if treply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
687 sreply
= source
.sendcmd('RETR ' + sourcename
)
688 if sreply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
694 """Class to parse & provide access to 'netrc' format files.
696 See the netrc(4) man page for information on the file format.
698 WARNING: This class is obsolete -- use module netrc instead.
705 def __init__(self
, filename
=None):
707 if "HOME" in os
.environ
:
708 filename
= os
.path
.join(os
.environ
["HOME"],
712 "specify file to load or set $HOME"
715 fp
= open(filename
, "r")
720 if in_macro
and line
.strip():
721 macro_lines
.append(line
)
724 self
.__macros
[macro_name
] = tuple(macro_lines
)
727 host
= user
= passwd
= acct
= None
730 while i
< len(words
):
738 elif w1
== 'machine' and w2
:
741 elif w1
== 'login' and w2
:
744 elif w1
== 'password' and w2
:
747 elif w1
== 'account' and w2
:
750 elif w1
== 'macdef' and w2
:
757 self
.__defuser
= user
or self
.__defuser
758 self
.__defpasswd
= passwd
or self
.__defpasswd
759 self
.__defacct
= acct
or self
.__defacct
761 if host
in self
.__hosts
:
762 ouser
, opasswd
, oacct
= \
765 passwd
= passwd
or opasswd
767 self
.__hosts
[host
] = user
, passwd
, acct
771 """Return a list of hosts mentioned in the .netrc file."""
772 return self
.__hosts
.keys()
774 def get_account(self
, host
):
775 """Returns login information for the named host.
777 The return value is a triple containing userid,
778 password, and the accounting field.
782 user
= passwd
= acct
= None
783 if host
in self
.__hosts
:
784 user
, passwd
, acct
= self
.__hosts
[host
]
785 user
= user
or self
.__defuser
786 passwd
= passwd
or self
.__defpasswd
787 acct
= acct
or self
.__defacct
788 return user
, passwd
, acct
790 def get_macros(self
):
791 """Return a list of all defined macro names."""
792 return self
.__macros
.keys()
794 def get_macro(self
, macro
):
795 """Return a sequence of lines which define a named macro."""
796 return self
.__macros
[macro
]
802 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
809 if len(sys
.argv
) < 2:
815 while sys
.argv
[1] == '-d':
816 debugging
= debugging
+1
818 if sys
.argv
[1][:2] == '-r':
819 # get name of alternate ~/.netrc file:
820 rcfile
= sys
.argv
[1][2:]
824 ftp
.set_debuglevel(debugging
)
825 userid
= passwd
= acct
= ''
827 netrc
= Netrc(rcfile
)
829 if rcfile
is not None:
830 sys
.stderr
.write("Could not open account file"
831 " -- using anonymous login.")
834 userid
, passwd
, acct
= netrc
.get_account(host
)
836 # no account for host
838 "No account -- using anonymous login.")
839 ftp
.login(userid
, passwd
, acct
)
840 for file in sys
.argv
[2:]:
843 elif file[:2] == '-d':
845 if file[2:]: cmd
= cmd
+ ' ' + file[2:]
846 resp
= ftp
.sendcmd(cmd
)
848 ftp
.set_pasv(not ftp
.passiveserver
)
850 ftp
.retrbinary('RETR ' + file, \
851 sys
.stdout
.write
, 1024)
855 if __name__
== '__main__':