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)
79 '''An FTP client class.
81 To create a connection, call the class using these arguments:
82 host, user, passwd, acct, timeout
84 The first four arguments are all strings, and have default value ''.
85 timeout must be numeric and defaults to None if not passed,
86 meaning that no timeout will be set on any ftp socket(s)
87 If a timeout is passed, then this is now the default timeout for all ftp
88 socket operations for this instance.
90 Then use self.connect() with optional host and port argument.
92 To download a file, use ftp.retrlines('RETR ' + filename),
93 or ftp.retrbinary() with slightly different arguments.
94 To upload a file, use ftp.storlines() or ftp.storbinary(),
95 which have an open file as argument (see their definitions
97 The download/upload functions first issue appropriate TYPE
98 and PORT or PASV commands.
110 # Initialization method (called by class instantiation).
111 # Initialize host to localhost, port to standard ftp port
112 # Optional arguments are host (for connect()),
113 # and user, passwd, acct (for login())
114 def __init__(self
, host
='', user
='', passwd
='', acct
='',
115 timeout
=_GLOBAL_DEFAULT_TIMEOUT
):
116 self
.timeout
= timeout
120 self
.login(user
, passwd
, acct
)
122 def connect(self
, host
='', port
=0, timeout
=-999):
123 '''Connect to host. Arguments are:
124 - host: hostname to connect to (string, default previous host)
125 - port: port to connect to (integer, default previous port)
132 self
.timeout
= timeout
133 self
.sock
= socket
.create_connection((self
.host
, self
.port
), self
.timeout
)
134 self
.af
= self
.sock
.family
135 self
.file = self
.sock
.makefile('r', encoding
=self
.encoding
)
136 self
.welcome
= self
.getresp()
139 def getwelcome(self
):
140 '''Get the welcome message from the server.
141 (this is read and squirreled away by connect())'''
143 print('*welcome*', self
.sanitize(self
.welcome
))
146 def set_debuglevel(self
, level
):
147 '''Set the debugging level.
148 The required argument level means:
149 0: no debugging output (default)
150 1: print commands and responses but not body text etc.
151 2: also print raw lines read and sent before stripping CR/LF'''
152 self
.debugging
= level
153 debug
= set_debuglevel
155 def set_pasv(self
, val
):
156 '''Use passive or active mode for data transfers.
157 With a false argument, use the normal PORT mode,
158 With a true argument, use the PASV command.'''
159 self
.passiveserver
= val
161 # Internal: "sanitize" a string for printing
162 def sanitize(self
, s
):
163 if s
[:5] == 'pass ' or s
[:5] == 'PASS ':
165 while i
> 5 and s
[i
-1] in '\r\n':
167 s
= s
[:5] + '*'*(i
-5) + s
[i
:]
170 # Internal: send one line to the server, appending CRLF
171 def putline(self
, line
):
173 if self
.debugging
> 1: print('*put*', self
.sanitize(line
))
174 self
.sock
.sendall(line
.encode(self
.encoding
))
176 # Internal: send one command to the server (through putline())
177 def putcmd(self
, line
):
178 if self
.debugging
: print('*cmd*', self
.sanitize(line
))
181 # Internal: return one line from the server, stripping CRLF.
182 # Raise EOFError if the connection is closed
184 line
= self
.file.readline()
185 if self
.debugging
> 1:
186 print('*get*', self
.sanitize(line
))
187 if not line
: raise EOFError
188 if line
[-2:] == CRLF
: line
= line
[:-2]
189 elif line
[-1:] in CRLF
: line
= line
[:-1]
192 # Internal: get a response from the server, which may possibly
193 # consist of multiple lines. Return a single string with no
194 # trailing CRLF. If the response consists of multiple lines,
195 # these are separated by '\n' characters in the string
196 def getmultiline(self
):
197 line
= self
.getline()
201 nextline
= self
.getline()
202 line
= line
+ ('\n' + nextline
)
203 if nextline
[:3] == code
and \
204 nextline
[3:4] != '-':
208 # Internal: get a response from the server.
209 # Raise various errors if the response indicates an error
211 resp
= self
.getmultiline()
212 if self
.debugging
: print('*resp*', self
.sanitize(resp
))
213 self
.lastresp
= resp
[:3]
215 if c
in ('1', '2', '3'):
218 raise error_temp(resp
)
220 raise error_perm(resp
)
221 raise error_proto(resp
)
224 """Expect a response beginning with '2'."""
225 resp
= self
.getresp()
227 raise error_reply(resp
)
231 '''Abort a file transfer. Uses out-of-band data.
232 This does not follow the procedure from the RFC to send Telnet
233 IP and Synch; that doesn't seem to work with the servers I've
234 tried. Instead, just send the ABOR command as OOB data.'''
236 if self
.debugging
> 1: print('*put urgent*', self
.sanitize(line
))
237 self
.sock
.sendall(line
, MSG_OOB
)
238 resp
= self
.getmultiline()
239 if resp
[:3] not in ('426', '225', '226'):
240 raise error_proto(resp
)
242 def sendcmd(self
, cmd
):
243 '''Send a command and return the response.'''
245 return self
.getresp()
247 def voidcmd(self
, cmd
):
248 """Send a command and expect a response beginning with '2'."""
250 return self
.voidresp()
252 def sendport(self
, host
, port
):
253 '''Send a PORT command with the current host and the given
256 hbytes
= host
.split('.')
257 pbytes
= [repr(port
//256), repr(port
%256)]
258 bytes
= hbytes
+ pbytes
259 cmd
= 'PORT ' + ','.join(bytes
)
260 return self
.voidcmd(cmd
)
262 def sendeprt(self
, host
, port
):
263 '''Send a EPRT command with the current host and the given port number.'''
265 if self
.af
== socket
.AF_INET
:
267 if self
.af
== socket
.AF_INET6
:
270 raise error_proto('unsupported address family')
271 fields
= ['', repr(af
), host
, repr(port
), '']
272 cmd
= 'EPRT ' + '|'.join(fields
)
273 return self
.voidcmd(cmd
)
276 '''Create a new socket and send a PORT command for it.'''
277 msg
= "getaddrinfo returns an empty list"
279 for res
in socket
.getaddrinfo(None, 0, self
.af
, socket
.SOCK_STREAM
, 0, socket
.AI_PASSIVE
):
280 af
, socktype
, proto
, canonname
, sa
= res
282 sock
= socket
.socket(af
, socktype
, proto
)
284 except socket
.error
as msg
:
291 raise socket
.error(msg
)
293 port
= sock
.getsockname()[1] # Get proper port
294 host
= self
.sock
.getsockname()[0] # Get proper host
295 if self
.af
== socket
.AF_INET
:
296 resp
= self
.sendport(host
, port
)
298 resp
= self
.sendeprt(host
, port
)
299 if self
.timeout
is not _GLOBAL_DEFAULT_TIMEOUT
:
300 sock
.settimeout(self
.timeout
)
304 if self
.af
== socket
.AF_INET
:
305 host
, port
= parse227(self
.sendcmd('PASV'))
307 host
, port
= parse229(self
.sendcmd('EPSV'), self
.sock
.getpeername())
310 def ntransfercmd(self
, cmd
, rest
=None):
311 """Initiate a transfer over the data connection.
313 If the transfer is active, send a port command and the
314 transfer command, and accept the connection. If the server is
315 passive, send a pasv command, connect to it, and start the
316 transfer command. Either way, return the socket for the
317 connection and the expected size of the transfer. The
318 expected size may be None if it could not be determined.
320 Optional `rest' argument can be a string that is sent as the
321 argument to a REST command. This is essentially a server
322 marker used to tell the server to skip over any data up to the
326 if self
.passiveserver
:
327 host
, port
= self
.makepasv()
328 conn
= socket
.create_connection((host
, port
), self
.timeout
)
330 self
.sendcmd("REST %s" % rest
)
331 resp
= self
.sendcmd(cmd
)
332 # Some servers apparently send a 200 reply to
333 # a LIST or STOR command, before the 150 reply
334 # (and way before the 226 reply). This seems to
335 # be in violation of the protocol (which only allows
336 # 1xx or error messages for LIST), so we just discard
339 resp
= self
.getresp()
341 raise error_reply(resp
)
343 sock
= self
.makeport()
345 self
.sendcmd("REST %s" % rest
)
346 resp
= self
.sendcmd(cmd
)
349 resp
= self
.getresp()
351 raise error_reply(resp
)
352 conn
, sockaddr
= sock
.accept()
353 if self
.timeout
is not _GLOBAL_DEFAULT_TIMEOUT
:
354 conn
.settimeout(self
.timeout
)
355 if resp
[:3] == '150':
356 # this is conditional in case we received a 125
357 size
= parse150(resp
)
360 def transfercmd(self
, cmd
, rest
=None):
361 """Like ntransfercmd() but returns only the socket."""
362 return self
.ntransfercmd(cmd
, rest
)[0]
364 def login(self
, user
= '', passwd
= '', acct
= ''):
365 '''Login, default anonymous.'''
366 if not user
: user
= 'anonymous'
367 if not passwd
: passwd
= ''
368 if not acct
: acct
= ''
369 if user
== 'anonymous' and passwd
in ('', '-'):
370 # If there is no anonymous ftp password specified
371 # then we'll just use anonymous@
372 # We don't send any other thing because:
373 # - We want to remain anonymous
374 # - We want to stop SPAM
375 # - We don't want to let ftp sites to discriminate by the user,
377 passwd
= passwd
+ 'anonymous@'
378 resp
= self
.sendcmd('USER ' + user
)
379 if resp
[0] == '3': resp
= self
.sendcmd('PASS ' + passwd
)
380 if resp
[0] == '3': resp
= self
.sendcmd('ACCT ' + acct
)
382 raise error_reply(resp
)
385 def retrbinary(self
, cmd
, callback
, blocksize
=8192, rest
=None):
386 """Retrieve data in binary mode. A new port is created for you.
390 callback: A single parameter callable to be called on each
392 blocksize: The maximum number of bytes to read from the
393 socket at one time. [default: 8192]
394 rest: Passed to transfercmd(). [default: None]
399 self
.voidcmd('TYPE I')
400 conn
= self
.transfercmd(cmd
, rest
)
402 data
= conn
.recv(blocksize
)
407 return self
.voidresp()
409 def retrlines(self
, cmd
, callback
= None):
410 """Retrieve data in line mode. A new port is created for you.
413 cmd: A RETR, LIST, NLST, or MLSD command.
414 callback: An optional single parameter callable that is called
415 for each line with the trailing CRLF stripped.
416 [default: print_line()]
421 if callback
is None: callback
= print_line
422 resp
= self
.sendcmd('TYPE A')
423 conn
= self
.transfercmd(cmd
)
424 fp
= conn
.makefile('r', encoding
=self
.encoding
)
427 if self
.debugging
> 2: print('*retr*', repr(line
))
430 if line
[-2:] == CRLF
:
432 elif line
[-1:] == '\n':
437 return self
.voidresp()
439 def storbinary(self
, cmd
, fp
, blocksize
=8192, callback
=None):
440 """Store a file in binary mode. A new port is created for you.
444 fp: A file-like object with a read(num_bytes) method.
445 blocksize: The maximum data size to read from fp and send over
446 the connection at once. [default: 8192]
447 callback: An optional single parameter callable that is called on
448 on each block of data after it is sent. [default: None]
453 self
.voidcmd('TYPE I')
454 conn
= self
.transfercmd(cmd
)
456 buf
= fp
.read(blocksize
)
459 if callback
: callback(buf
)
461 return self
.voidresp()
463 def storlines(self
, cmd
, fp
, callback
=None):
464 """Store a file in line mode. A new port is created for you.
468 fp: A file-like object with a readline() method.
469 callback: An optional single parameter callable that is called on
470 on each line after it is sent. [default: None]
475 self
.voidcmd('TYPE A')
476 conn
= self
.transfercmd(cmd
)
480 if buf
[-2:] != B_CRLF
:
481 if buf
[-1] in B_CRLF
: buf
= buf
[:-1]
484 if callback
: callback(buf
)
486 return self
.voidresp()
488 def acct(self
, password
):
489 '''Send new account name.'''
490 cmd
= 'ACCT ' + password
491 return self
.voidcmd(cmd
)
493 def nlst(self
, *args
):
494 '''Return a list of files in a given directory (default the current).'''
497 cmd
= cmd
+ (' ' + arg
)
499 self
.retrlines(cmd
, files
.append
)
502 def dir(self
, *args
):
503 '''List a directory in long form.
504 By default list current directory to stdout.
505 Optional last argument is callback function; all
506 non-empty arguments before it are concatenated to the
507 LIST command. (This *should* only be used for a pathname.)'''
510 if args
[-1:] and type(args
[-1]) != type(''):
511 args
, func
= args
[:-1], args
[-1]
514 cmd
= cmd
+ (' ' + arg
)
515 self
.retrlines(cmd
, func
)
517 def rename(self
, fromname
, toname
):
519 resp
= self
.sendcmd('RNFR ' + fromname
)
521 raise error_reply(resp
)
522 return self
.voidcmd('RNTO ' + toname
)
524 def delete(self
, filename
):
526 resp
= self
.sendcmd('DELE ' + filename
)
527 if resp
[:3] in ('250', '200'):
530 raise error_reply(resp
)
532 def cwd(self
, dirname
):
533 '''Change to a directory.'''
536 return self
.voidcmd('CDUP')
537 except error_perm
as msg
:
538 if msg
.args
[0][:3] != '500':
541 dirname
= '.' # does nothing, but could return error
542 cmd
= 'CWD ' + dirname
543 return self
.voidcmd(cmd
)
545 def size(self
, filename
):
546 '''Retrieve the size of a file.'''
547 # The SIZE command is defined in RFC-3659
548 resp
= self
.sendcmd('SIZE ' + filename
)
549 if resp
[:3] == '213':
553 except (OverflowError, ValueError):
556 def mkd(self
, dirname
):
557 '''Make a directory, return its full pathname.'''
558 resp
= self
.sendcmd('MKD ' + dirname
)
559 return parse257(resp
)
561 def rmd(self
, dirname
):
562 '''Remove a directory.'''
563 return self
.voidcmd('RMD ' + dirname
)
566 '''Return current working directory.'''
567 resp
= self
.sendcmd('PWD')
568 return parse257(resp
)
571 '''Quit, and close the connection.'''
572 resp
= self
.voidcmd('QUIT')
577 '''Close the connection without assuming anything about it.'''
581 self
.file = self
.sock
= None
587 '''Parse the '150' response for a RETR request.
588 Returns the expected transfer size or None; size is not guaranteed to
589 be present in the 150 message.
591 if resp
[:3] != '150':
592 raise error_reply(resp
)
596 _150_re
= re
.compile(
597 "150 .* \((\d+) bytes\)", re
.IGNORECASE | re
.ASCII
)
598 m
= _150_re
.match(resp
)
604 except (OverflowError, ValueError):
611 '''Parse the '227' response for a PASV request.
612 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
613 Return ('host.addr.as.numbers', port#) tuple.'''
615 if resp
[:3] != '227':
616 raise error_reply(resp
)
620 _227_re
= re
.compile(r
'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)', re
.ASCII
)
621 m
= _227_re
.search(resp
)
623 raise error_proto(resp
)
625 host
= '.'.join(numbers
[:4])
626 port
= (int(numbers
[4]) << 8) + int(numbers
[5])
630 def parse229(resp
, peer
):
631 '''Parse the '229' response for a EPSV request.
632 Raises error_proto if it does not contain '(|||port|)'
633 Return ('host.addr.as.numbers', port#) tuple.'''
635 if resp
[:3] != '229':
636 raise error_reply(resp
)
637 left
= resp
.find('(')
638 if left
< 0: raise error_proto(resp
)
639 right
= resp
.find(')', left
+ 1)
641 raise error_proto(resp
) # should contain '(|||port|)'
642 if resp
[left
+ 1] != resp
[right
- 1]:
643 raise error_proto(resp
)
644 parts
= resp
[left
+ 1:right
].split(resp
[left
+1])
646 raise error_proto(resp
)
653 '''Parse the '257' response for a MKD or PWD request.
654 This is a response to a MKD or PWD request: a directory name.
655 Returns the directoryname in the 257 reply.'''
657 if resp
[:3] != '257':
658 raise error_reply(resp
)
659 if resp
[3:5] != ' "':
660 return '' # Not compliant to RFC 959, but UNIX ftpd does this
668 if i
>= n
or resp
[i
] != '"':
671 dirname
= dirname
+ c
675 def print_line(line
):
676 '''Default retrlines callback to print a line.'''
680 def ftpcp(source
, sourcename
, target
, targetname
= '', type = 'I'):
681 '''Copy file from one FTP-instance to another.'''
682 if not targetname
: targetname
= sourcename
683 type = 'TYPE ' + type
686 sourcehost
, sourceport
= parse227(source
.sendcmd('PASV'))
687 target
.sendport(sourcehost
, sourceport
)
688 # RFC 959: the user must "listen" [...] BEFORE sending the
690 # So: STOR before RETR, because here the target is a "user".
691 treply
= target
.sendcmd('STOR ' + targetname
)
692 if treply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
693 sreply
= source
.sendcmd('RETR ' + sourcename
)
694 if sreply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
700 """Class to parse & provide access to 'netrc' format files.
702 See the netrc(4) man page for information on the file format.
704 WARNING: This class is obsolete -- use module netrc instead.
711 def __init__(self
, filename
=None):
713 if "HOME" in os
.environ
:
714 filename
= os
.path
.join(os
.environ
["HOME"],
717 raise IOError("specify file to load or set $HOME")
720 fp
= open(filename
, "r")
725 if in_macro
and line
.strip():
726 macro_lines
.append(line
)
729 self
.__macros
[macro_name
] = tuple(macro_lines
)
732 host
= user
= passwd
= acct
= None
735 while i
< len(words
):
743 elif w1
== 'machine' and w2
:
746 elif w1
== 'login' and w2
:
749 elif w1
== 'password' and w2
:
752 elif w1
== 'account' and w2
:
755 elif w1
== 'macdef' and w2
:
762 self
.__defuser
= user
or self
.__defuser
763 self
.__defpasswd
= passwd
or self
.__defpasswd
764 self
.__defacct
= acct
or self
.__defacct
766 if host
in self
.__hosts
:
767 ouser
, opasswd
, oacct
= \
770 passwd
= passwd
or opasswd
772 self
.__hosts
[host
] = user
, passwd
, acct
776 """Return a list of hosts mentioned in the .netrc file."""
777 return self
.__hosts
.keys()
779 def get_account(self
, host
):
780 """Returns login information for the named host.
782 The return value is a triple containing userid,
783 password, and the accounting field.
787 user
= passwd
= acct
= None
788 if host
in self
.__hosts
:
789 user
, passwd
, acct
= self
.__hosts
[host
]
790 user
= user
or self
.__defuser
791 passwd
= passwd
or self
.__defpasswd
792 acct
= acct
or self
.__defacct
793 return user
, passwd
, acct
795 def get_macros(self
):
796 """Return a list of all defined macro names."""
797 return self
.__macros
.keys()
799 def get_macro(self
, macro
):
800 """Return a sequence of lines which define a named macro."""
801 return self
.__macros
[macro
]
807 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
814 if len(sys
.argv
) < 2:
820 while sys
.argv
[1] == '-d':
821 debugging
= debugging
+1
823 if sys
.argv
[1][:2] == '-r':
824 # get name of alternate ~/.netrc file:
825 rcfile
= sys
.argv
[1][2:]
829 ftp
.set_debuglevel(debugging
)
830 userid
= passwd
= acct
= ''
832 netrc
= Netrc(rcfile
)
834 if rcfile
is not None:
835 sys
.stderr
.write("Could not open account file"
836 " -- using anonymous login.")
839 userid
, passwd
, acct
= netrc
.get_account(host
)
841 # no account for host
843 "No account -- using anonymous login.")
844 ftp
.login(userid
, passwd
, acct
)
845 for file in sys
.argv
[2:]:
848 elif file[:2] == '-d':
850 if file[2:]: cmd
= cmd
+ ' ' + file[2:]
851 resp
= ftp
.sendcmd(cmd
)
853 ftp
.set_pasv(not ftp
.passiveserver
)
855 ftp
.retrbinary('RETR ' + file, \
856 sys
.stdout
.write
, 1024)
860 if __name__
== '__main__':