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
47 from socket
import _GLOBAL_DEFAULT_TIMEOUT
49 __all__
= ["FTP","Netrc"]
51 # Magic number from <socket.h>
52 MSG_OOB
= 0x1 # Process data out of band
55 # The standard FTP server control port
59 # Exception raised when an error or invalid response is received
60 class Error(Exception): pass
61 class error_reply(Error
): pass # unexpected [123]xx reply
62 class error_temp(Error
): pass # 4xx errors
63 class error_perm(Error
): pass # 5xx errors
64 class error_proto(Error
): pass # response does not begin with [1-5]
67 # All exceptions (hopefully) that may be raised here and that aren't
68 # (always) programming errors on our side
69 all_errors
= (Error
, IOError, EOFError)
72 # 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
='',
113 timeout
=_GLOBAL_DEFAULT_TIMEOUT
):
114 self
.timeout
= timeout
118 self
.login(user
, passwd
, acct
)
120 def connect(self
, host
='', port
=0, timeout
=-999):
121 '''Connect to host. Arguments are:
122 - host: hostname to connect to (string, default previous host)
123 - port: port to connect to (integer, default previous port)
130 self
.timeout
= timeout
131 self
.sock
= socket
.create_connection((self
.host
, self
.port
), self
.timeout
)
132 self
.af
= self
.sock
.family
133 self
.file = self
.sock
.makefile('rb')
134 self
.welcome
= self
.getresp()
137 def getwelcome(self
):
138 '''Get the welcome message from the server.
139 (this is read and squirreled away by connect())'''
141 print '*welcome*', self
.sanitize(self
.welcome
)
144 def set_debuglevel(self
, level
):
145 '''Set the debugging level.
146 The required argument level means:
147 0: no debugging output (default)
148 1: print commands and responses but not body text etc.
149 2: also print raw lines read and sent before stripping CR/LF'''
150 self
.debugging
= level
151 debug
= set_debuglevel
153 def set_pasv(self
, val
):
154 '''Use passive or active mode for data transfers.
155 With a false argument, use the normal PORT mode,
156 With a true argument, use the PASV command.'''
157 self
.passiveserver
= val
159 # Internal: "sanitize" a string for printing
160 def sanitize(self
, s
):
161 if s
[:5] == 'pass ' or s
[:5] == 'PASS ':
163 while i
> 5 and s
[i
-1] in '\r\n':
165 s
= s
[:5] + '*'*(i
-5) + s
[i
:]
168 # Internal: send one line to the server, appending CRLF
169 def putline(self
, line
):
171 if self
.debugging
> 1: print '*put*', self
.sanitize(line
)
172 self
.sock
.sendall(line
)
174 # Internal: send one command to the server (through putline())
175 def putcmd(self
, line
):
176 if self
.debugging
: print '*cmd*', self
.sanitize(line
)
179 # Internal: return one line from the server, stripping CRLF.
180 # Raise EOFError if the connection is closed
182 line
= self
.file.readline()
183 if self
.debugging
> 1:
184 print '*get*', self
.sanitize(line
)
185 if not line
: raise EOFError
186 if line
[-2:] == CRLF
: line
= line
[:-2]
187 elif line
[-1:] in CRLF
: line
= line
[:-1]
190 # Internal: get a response from the server, which may possibly
191 # consist of multiple lines. Return a single string with no
192 # trailing CRLF. If the response consists of multiple lines,
193 # these are separated by '\n' characters in the string
194 def getmultiline(self
):
195 line
= self
.getline()
199 nextline
= self
.getline()
200 line
= line
+ ('\n' + nextline
)
201 if nextline
[:3] == code
and \
202 nextline
[3:4] != '-':
206 # Internal: get a response from the server.
207 # Raise various errors if the response indicates an error
209 resp
= self
.getmultiline()
210 if self
.debugging
: print '*resp*', self
.sanitize(resp
)
211 self
.lastresp
= resp
[:3]
213 if c
in ('1', '2', '3'):
216 raise error_temp
, resp
218 raise error_perm
, resp
219 raise error_proto
, resp
222 """Expect a response beginning with '2'."""
223 resp
= self
.getresp()
225 raise error_reply
, resp
229 '''Abort a file transfer. Uses out-of-band data.
230 This does not follow the procedure from the RFC to send Telnet
231 IP and Synch; that doesn't seem to work with the servers I've
232 tried. Instead, just send the ABOR command as OOB data.'''
234 if self
.debugging
> 1: print '*put urgent*', self
.sanitize(line
)
235 self
.sock
.sendall(line
, MSG_OOB
)
236 resp
= self
.getmultiline()
237 if resp
[:3] not in ('426', '226'):
238 raise error_proto
, resp
240 def sendcmd(self
, cmd
):
241 '''Send a command and return the response.'''
243 return self
.getresp()
245 def voidcmd(self
, cmd
):
246 """Send a command and expect a response beginning with '2'."""
248 return self
.voidresp()
250 def sendport(self
, host
, port
):
251 '''Send a PORT command with the current host and the given
254 hbytes
= host
.split('.')
255 pbytes
= [repr(port
//256), repr(port
%256)]
256 bytes
= hbytes
+ pbytes
257 cmd
= 'PORT ' + ','.join(bytes
)
258 return self
.voidcmd(cmd
)
260 def sendeprt(self
, host
, port
):
261 '''Send a EPRT command with the current host and the given port number.'''
263 if self
.af
== socket
.AF_INET
:
265 if self
.af
== socket
.AF_INET6
:
268 raise error_proto
, 'unsupported address family'
269 fields
= ['', repr(af
), host
, repr(port
), '']
270 cmd
= 'EPRT ' + '|'.join(fields
)
271 return self
.voidcmd(cmd
)
274 '''Create a new socket and send a PORT command for it.'''
275 msg
= "getaddrinfo returns an empty list"
277 for res
in socket
.getaddrinfo(None, 0, self
.af
, socket
.SOCK_STREAM
, 0, socket
.AI_PASSIVE
):
278 af
, socktype
, proto
, canonname
, sa
= res
280 sock
= socket
.socket(af
, socktype
, proto
)
282 except socket
.error
, msg
:
289 raise socket
.error
, msg
291 port
= sock
.getsockname()[1] # Get proper port
292 host
= self
.sock
.getsockname()[0] # Get proper host
293 if self
.af
== socket
.AF_INET
:
294 resp
= self
.sendport(host
, port
)
296 resp
= self
.sendeprt(host
, port
)
300 if self
.af
== socket
.AF_INET
:
301 host
, port
= parse227(self
.sendcmd('PASV'))
303 host
, port
= parse229(self
.sendcmd('EPSV'), self
.sock
.getpeername())
306 def ntransfercmd(self
, cmd
, rest
=None):
307 """Initiate a transfer over the data connection.
309 If the transfer is active, send a port command and the
310 transfer command, and accept the connection. If the server is
311 passive, send a pasv command, connect to it, and start the
312 transfer command. Either way, return the socket for the
313 connection and the expected size of the transfer. The
314 expected size may be None if it could not be determined.
316 Optional `rest' argument can be a string that is sent as the
317 argument to a REST command. This is essentially a server
318 marker used to tell the server to skip over any data up to the
322 if self
.passiveserver
:
323 host
, port
= self
.makepasv()
324 conn
= socket
.create_connection((host
, port
), self
.timeout
)
326 self
.sendcmd("REST %s" % rest
)
327 resp
= self
.sendcmd(cmd
)
328 # Some servers apparently send a 200 reply to
329 # a LIST or STOR command, before the 150 reply
330 # (and way before the 226 reply). This seems to
331 # be in violation of the protocol (which only allows
332 # 1xx or error messages for LIST), so we just discard
335 resp
= self
.getresp()
337 raise error_reply
, resp
339 sock
= self
.makeport()
341 self
.sendcmd("REST %s" % rest
)
342 resp
= self
.sendcmd(cmd
)
345 resp
= self
.getresp()
347 raise error_reply
, resp
348 conn
, sockaddr
= sock
.accept()
349 if resp
[:3] == '150':
350 # this is conditional in case we received a 125
351 size
= parse150(resp
)
354 def transfercmd(self
, cmd
, rest
=None):
355 """Like ntransfercmd() but returns only the socket."""
356 return self
.ntransfercmd(cmd
, rest
)[0]
358 def login(self
, user
= '', passwd
= '', acct
= ''):
359 '''Login, default anonymous.'''
360 if not user
: user
= 'anonymous'
361 if not passwd
: passwd
= ''
362 if not acct
: acct
= ''
363 if user
== 'anonymous' and passwd
in ('', '-'):
364 # If there is no anonymous ftp password specified
365 # then we'll just use anonymous@
366 # We don't send any other thing because:
367 # - We want to remain anonymous
368 # - We want to stop SPAM
369 # - We don't want to let ftp sites to discriminate by the user,
371 passwd
= passwd
+ 'anonymous@'
372 resp
= self
.sendcmd('USER ' + user
)
373 if resp
[0] == '3': resp
= self
.sendcmd('PASS ' + passwd
)
374 if resp
[0] == '3': resp
= self
.sendcmd('ACCT ' + acct
)
376 raise error_reply
, resp
379 def retrbinary(self
, cmd
, callback
, blocksize
=8192, rest
=None):
380 """Retrieve data in binary mode. A new port is created for you.
384 callback: A single parameter callable to be called on each
386 blocksize: The maximum number of bytes to read from the
387 socket at one time. [default: 8192]
388 rest: Passed to transfercmd(). [default: None]
393 self
.voidcmd('TYPE I')
394 conn
= self
.transfercmd(cmd
, rest
)
396 data
= conn
.recv(blocksize
)
401 return self
.voidresp()
403 def retrlines(self
, cmd
, callback
= None):
404 """Retrieve data in line mode. A new port is created for you.
407 cmd: A RETR, LIST, NLST, or MLSD command.
408 callback: An optional single parameter callable that is called
409 for each line with the trailing CRLF stripped.
410 [default: print_line()]
415 if callback
is None: callback
= print_line
416 resp
= self
.sendcmd('TYPE A')
417 conn
= self
.transfercmd(cmd
)
418 fp
= conn
.makefile('rb')
421 if self
.debugging
> 2: print '*retr*', repr(line
)
424 if line
[-2:] == CRLF
:
426 elif line
[-1:] == '\n':
431 return self
.voidresp()
433 def storbinary(self
, cmd
, fp
, blocksize
=8192, callback
=None):
434 """Store a file in binary mode. A new port is created for you.
438 fp: A file-like object with a read(num_bytes) method.
439 blocksize: The maximum data size to read from fp and send over
440 the connection at once. [default: 8192]
441 callback: An optional single parameter callable that is called on
442 on each block of data after it is sent. [default: None]
447 self
.voidcmd('TYPE I')
448 conn
= self
.transfercmd(cmd
)
450 buf
= fp
.read(blocksize
)
453 if callback
: callback(buf
)
455 return self
.voidresp()
457 def storlines(self
, cmd
, fp
, callback
=None):
458 """Store a file in line mode. A new port is created for you.
462 fp: A file-like object with a readline() method.
463 callback: An optional single parameter callable that is called on
464 on each line after it is sent. [default: None]
469 self
.voidcmd('TYPE A')
470 conn
= self
.transfercmd(cmd
)
475 if buf
[-1] in CRLF
: buf
= buf
[:-1]
478 if callback
: callback(buf
)
480 return self
.voidresp()
482 def acct(self
, password
):
483 '''Send new account name.'''
484 cmd
= 'ACCT ' + password
485 return self
.voidcmd(cmd
)
487 def nlst(self
, *args
):
488 '''Return a list of files in a given directory (default the current).'''
491 cmd
= cmd
+ (' ' + arg
)
493 self
.retrlines(cmd
, files
.append
)
496 def dir(self
, *args
):
497 '''List a directory in long form.
498 By default list current directory to stdout.
499 Optional last argument is callback function; all
500 non-empty arguments before it are concatenated to the
501 LIST command. (This *should* only be used for a pathname.)'''
504 if args
[-1:] and type(args
[-1]) != type(''):
505 args
, func
= args
[:-1], args
[-1]
508 cmd
= cmd
+ (' ' + arg
)
509 self
.retrlines(cmd
, func
)
511 def rename(self
, fromname
, toname
):
513 resp
= self
.sendcmd('RNFR ' + fromname
)
515 raise error_reply
, resp
516 return self
.voidcmd('RNTO ' + toname
)
518 def delete(self
, filename
):
520 resp
= self
.sendcmd('DELE ' + filename
)
521 if resp
[:3] in ('250', '200'):
523 elif resp
[:1] == '5':
524 raise error_perm
, resp
526 raise error_reply
, resp
528 def cwd(self
, dirname
):
529 '''Change to a directory.'''
532 return self
.voidcmd('CDUP')
533 except error_perm
, msg
:
534 if msg
.args
[0][:3] != '500':
537 dirname
= '.' # does nothing, but could return error
538 cmd
= 'CWD ' + dirname
539 return self
.voidcmd(cmd
)
541 def size(self
, filename
):
542 '''Retrieve the size of a file.'''
543 # The SIZE command is defined in RFC-3659
544 resp
= self
.sendcmd('SIZE ' + filename
)
545 if resp
[:3] == '213':
549 except (OverflowError, ValueError):
552 def mkd(self
, dirname
):
553 '''Make a directory, return its full pathname.'''
554 resp
= self
.sendcmd('MKD ' + dirname
)
555 return parse257(resp
)
557 def rmd(self
, dirname
):
558 '''Remove a directory.'''
559 return self
.voidcmd('RMD ' + dirname
)
562 '''Return current working directory.'''
563 resp
= self
.sendcmd('PWD')
564 return parse257(resp
)
567 '''Quit, and close the connection.'''
568 resp
= self
.voidcmd('QUIT')
573 '''Close the connection without assuming anything about it.'''
577 self
.file = self
.sock
= None
583 '''Parse the '150' response for a RETR request.
584 Returns the expected transfer size or None; size is not guaranteed to
585 be present in the 150 message.
587 if resp
[:3] != '150':
588 raise error_reply
, resp
592 _150_re
= re
.compile("150 .* \((\d+) bytes\)", re
.IGNORECASE
)
593 m
= _150_re
.match(resp
)
599 except (OverflowError, ValueError):
606 '''Parse the '227' response for a PASV request.
607 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
608 Return ('host.addr.as.numbers', port#) tuple.'''
610 if resp
[:3] != '227':
611 raise error_reply
, resp
615 _227_re
= re
.compile(r
'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
616 m
= _227_re
.search(resp
)
618 raise error_proto
, resp
620 host
= '.'.join(numbers
[:4])
621 port
= (int(numbers
[4]) << 8) + int(numbers
[5])
625 def parse229(resp
, peer
):
626 '''Parse the '229' response for a EPSV request.
627 Raises error_proto if it does not contain '(|||port|)'
628 Return ('host.addr.as.numbers', port#) tuple.'''
630 if resp
[:3] != '229':
631 raise error_reply
, resp
632 left
= resp
.find('(')
633 if left
< 0: raise error_proto
, resp
634 right
= resp
.find(')', left
+ 1)
636 raise error_proto
, resp
# should contain '(|||port|)'
637 if resp
[left
+ 1] != resp
[right
- 1]:
638 raise error_proto
, resp
639 parts
= resp
[left
+ 1:right
].split(resp
[left
+1])
641 raise error_proto
, resp
648 '''Parse the '257' response for a MKD or PWD request.
649 This is a response to a MKD or PWD request: a directory name.
650 Returns the directoryname in the 257 reply.'''
652 if resp
[:3] != '257':
653 raise error_reply
, resp
654 if resp
[3:5] != ' "':
655 return '' # Not compliant to RFC 959, but UNIX ftpd does this
663 if i
>= n
or resp
[i
] != '"':
666 dirname
= dirname
+ c
670 def print_line(line
):
671 '''Default retrlines callback to print a line.'''
675 def ftpcp(source
, sourcename
, target
, targetname
= '', type = 'I'):
676 '''Copy file from one FTP-instance to another.'''
677 if not targetname
: targetname
= sourcename
678 type = 'TYPE ' + type
681 sourcehost
, sourceport
= parse227(source
.sendcmd('PASV'))
682 target
.sendport(sourcehost
, sourceport
)
683 # RFC 959: the user must "listen" [...] BEFORE sending the
685 # So: STOR before RETR, because here the target is a "user".
686 treply
= target
.sendcmd('STOR ' + targetname
)
687 if treply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
688 sreply
= source
.sendcmd('RETR ' + sourcename
)
689 if sreply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
695 """Class to parse & provide access to 'netrc' format files.
697 See the netrc(4) man page for information on the file format.
699 WARNING: This class is obsolete -- use module netrc instead.
706 def __init__(self
, filename
=None):
708 if "HOME" in os
.environ
:
709 filename
= os
.path
.join(os
.environ
["HOME"],
713 "specify file to load or set $HOME"
716 fp
= open(filename
, "r")
721 if in_macro
and line
.strip():
722 macro_lines
.append(line
)
725 self
.__macros
[macro_name
] = tuple(macro_lines
)
728 host
= user
= passwd
= acct
= None
731 while i
< len(words
):
739 elif w1
== 'machine' and w2
:
742 elif w1
== 'login' and w2
:
745 elif w1
== 'password' and w2
:
748 elif w1
== 'account' and w2
:
751 elif w1
== 'macdef' and w2
:
758 self
.__defuser
= user
or self
.__defuser
759 self
.__defpasswd
= passwd
or self
.__defpasswd
760 self
.__defacct
= acct
or self
.__defacct
762 if host
in self
.__hosts
:
763 ouser
, opasswd
, oacct
= \
766 passwd
= passwd
or opasswd
768 self
.__hosts
[host
] = user
, passwd
, acct
772 """Return a list of hosts mentioned in the .netrc file."""
773 return self
.__hosts
.keys()
775 def get_account(self
, host
):
776 """Returns login information for the named host.
778 The return value is a triple containing userid,
779 password, and the accounting field.
783 user
= passwd
= acct
= None
784 if host
in self
.__hosts
:
785 user
, passwd
, acct
= self
.__hosts
[host
]
786 user
= user
or self
.__defuser
787 passwd
= passwd
or self
.__defpasswd
788 acct
= acct
or self
.__defacct
789 return user
, passwd
, acct
791 def get_macros(self
):
792 """Return a list of all defined macro names."""
793 return self
.__macros
.keys()
795 def get_macro(self
, macro
):
796 """Return a sequence of lines which define a named macro."""
797 return self
.__macros
[macro
]
803 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
810 if len(sys
.argv
) < 2:
816 while sys
.argv
[1] == '-d':
817 debugging
= debugging
+1
819 if sys
.argv
[1][:2] == '-r':
820 # get name of alternate ~/.netrc file:
821 rcfile
= sys
.argv
[1][2:]
825 ftp
.set_debuglevel(debugging
)
826 userid
= passwd
= acct
= ''
828 netrc
= Netrc(rcfile
)
830 if rcfile
is not None:
831 sys
.stderr
.write("Could not open account file"
832 " -- using anonymous login.")
835 userid
, passwd
, acct
= netrc
.get_account(host
)
837 # no account for host
839 "No account -- using anonymous login.")
840 ftp
.login(userid
, passwd
, acct
)
841 for file in sys
.argv
[2:]:
844 elif file[:2] == '-d':
846 if file[2:]: cmd
= cmd
+ ' ' + file[2:]
847 resp
= ftp
.sendcmd(cmd
)
849 ftp
.set_pasv(not ftp
.passiveserver
)
851 ftp
.retrbinary('RETR ' + file, \
852 sys
.stdout
.write
, 1024)
856 if __name__
== '__main__':