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
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 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.
85 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
90 The download/upload functions first issue appropriate TYPE
91 and PORT or PASV commands.
102 # 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())
106 def __init__(self
, host
='', user
='', passwd
='', acct
=''):
109 if user
: self
.login(user
, passwd
, acct
)
111 def connect(self
, host
= '', port
= 0):
112 '''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
117 self
.passiveserver
= 0
118 msg
= "getaddrinfo returns an empty list"
119 for res
in socket
.getaddrinfo(self
.host
, self
.port
, 0, socket
.SOCK_STREAM
):
120 af
, socktype
, proto
, canonname
, sa
= res
122 self
.sock
= socket
.socket(af
, socktype
, proto
)
123 self
.sock
.connect(sa
)
124 except socket
.error
, msg
:
130 raise socket
.error
, msg
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
)
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]
213 raise error_temp
, resp
215 raise error_perm
, resp
217 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
.send(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
= [`port
/256`
, `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
= ['', `af`
, host
, `port`
, '']
269 cmd
= 'EPRT ' + string
.joinfields(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"
275 for res
in socket
.getaddrinfo(None, 0, self
.af
, socket
.SOCK_STREAM
, 0, socket
.AI_PASSIVE
):
276 af
, socktype
, proto
, canonname
, sa
= res
278 sock
= socket
.socket(af
, socktype
, proto
)
280 except socket
.error
, msg
:
286 raise socket
.error
, msg
288 port
= sock
.getsockname()[1] # Get proper port
289 host
= self
.sock
.getsockname()[0] # Get proper host
290 if self
.af
== socket
.AF_INET
:
291 resp
= self
.sendport(host
, port
)
293 resp
= self
.sendeprt(host
, port
)
297 if self
.af
== socket
.AF_INET
:
298 host
, port
= parse227(self
.sendcmd('PASV'))
300 host
, port
= parse229(self
.sendcmd('EPSV'), self
.sock
.getpeername())
303 def ntransfercmd(self
, cmd
, rest
=None):
304 """Initiate a transfer over the data connection.
306 If the transfer is active, send a port command and the
307 transfer command, and accept the connection. If the server is
308 passive, send a pasv command, connect to it, and start the
309 transfer command. Either way, return the socket for the
310 connection and the expected size of the transfer. The
311 expected size may be None if it could not be determined.
313 Optional `rest' argument can be a string that is sent as the
314 argument to a RESTART command. This is essentially a server
315 marker used to tell the server to skip over any data up to the
319 if self
.passiveserver
:
320 host
, port
= self
.makepasv()
321 af
, socktype
, proto
, canon
, sa
= socket
.getaddrinfo(host
, port
, 0, socket
.SOCK_STREAM
)[0]
322 conn
= socket
.socket(af
, socktype
, proto
)
325 self
.sendcmd("REST %s" % rest
)
326 resp
= self
.sendcmd(cmd
)
328 raise error_reply
, resp
330 sock
= self
.makeport()
332 self
.sendcmd("REST %s" % rest
)
333 resp
= self
.sendcmd(cmd
)
335 raise error_reply
, resp
336 conn
, sockaddr
= sock
.accept()
337 if resp
[:3] == '150':
338 # this is conditional in case we received a 125
339 size
= parse150(resp
)
342 def transfercmd(self
, cmd
, rest
=None):
343 """Like nstransfercmd() but returns only the socket."""
344 return self
.ntransfercmd(cmd
, rest
)[0]
346 def login(self
, user
= '', passwd
= '', acct
= ''):
347 '''Login, default anonymous.'''
348 if not user
: user
= 'anonymous'
349 if not passwd
: passwd
= ''
350 if not acct
: acct
= ''
351 if user
== 'anonymous' and passwd
in ('', '-'):
352 # get fully qualified domain name of local host
353 thishost
= socket
.getfqdn()
355 if os
.environ
.has_key('LOGNAME'):
356 realuser
= os
.environ
['LOGNAME']
357 elif os
.environ
.has_key('USER'):
358 realuser
= os
.environ
['USER']
360 realuser
= 'anonymous'
361 except AttributeError:
362 # Not all systems have os.environ....
363 realuser
= 'anonymous'
364 passwd
= passwd
+ realuser
+ '@' + thishost
365 resp
= self
.sendcmd('USER ' + user
)
366 if resp
[0] == '3': resp
= self
.sendcmd('PASS ' + passwd
)
367 if resp
[0] == '3': resp
= self
.sendcmd('ACCT ' + acct
)
369 raise error_reply
, resp
372 def retrbinary(self
, cmd
, callback
, blocksize
=8192, rest
=None):
373 """Retrieve data in binary mode.
375 `cmd' is a RETR command. `callback' is a callback function is
376 called for each block. No more than `blocksize' number of
377 bytes will be read from the socket. Optional `rest' is passed
380 A new port is created for you. Return the response code.
382 self
.voidcmd('TYPE I')
383 conn
= self
.transfercmd(cmd
, rest
)
385 data
= conn
.recv(blocksize
)
390 return self
.voidresp()
392 def retrlines(self
, cmd
, callback
= None):
393 '''Retrieve data in line mode.
394 The argument is a RETR or LIST command.
395 The callback function (2nd argument) is called for each line,
396 with trailing CRLF stripped. This creates a new port for you.
397 print_line() is the default callback.'''
398 if not callback
: callback
= print_line
399 resp
= self
.sendcmd('TYPE A')
400 conn
= self
.transfercmd(cmd
)
401 fp
= conn
.makefile('rb')
404 if self
.debugging
> 2: print '*retr*', `line`
407 if line
[-2:] == CRLF
:
409 elif line
[-1:] == '\n':
414 return self
.voidresp()
416 def storbinary(self
, cmd
, fp
, blocksize
=8192):
417 '''Store a file in binary mode.'''
418 self
.voidcmd('TYPE I')
419 conn
= self
.transfercmd(cmd
)
421 buf
= fp
.read(blocksize
)
425 return self
.voidresp()
427 def storlines(self
, cmd
, fp
):
428 '''Store a file in line mode.'''
429 self
.voidcmd('TYPE A')
430 conn
= self
.transfercmd(cmd
)
435 if buf
[-1] in CRLF
: buf
= buf
[:-1]
439 return self
.voidresp()
441 def acct(self
, password
):
442 '''Send new account name.'''
443 cmd
= 'ACCT ' + password
444 return self
.voidcmd(cmd
)
446 def nlst(self
, *args
):
447 '''Return a list of files in a given directory (default the current).'''
450 cmd
= cmd
+ (' ' + arg
)
452 self
.retrlines(cmd
, files
.append
)
455 def dir(self
, *args
):
456 '''List a directory in long form.
457 By default list current directory to stdout.
458 Optional last argument is callback function; all
459 non-empty arguments before it are concatenated to the
460 LIST command. (This *should* only be used for a pathname.)'''
463 if args
[-1:] and type(args
[-1]) != type(''):
464 args
, func
= args
[:-1], args
[-1]
467 cmd
= cmd
+ (' ' + arg
)
468 self
.retrlines(cmd
, func
)
470 def rename(self
, fromname
, toname
):
472 resp
= self
.sendcmd('RNFR ' + fromname
)
474 raise error_reply
, resp
475 return self
.voidcmd('RNTO ' + toname
)
477 def delete(self
, filename
):
479 resp
= self
.sendcmd('DELE ' + filename
)
480 if resp
[:3] in ('250', '200'):
482 elif resp
[:1] == '5':
483 raise error_perm
, resp
485 raise error_reply
, resp
487 def cwd(self
, dirname
):
488 '''Change to a directory.'''
491 return self
.voidcmd('CDUP')
492 except error_perm
, msg
:
494 raise error_perm
, msg
496 dirname
= '.' # does nothing, but could return error
497 cmd
= 'CWD ' + dirname
498 return self
.voidcmd(cmd
)
500 def size(self
, filename
):
501 '''Retrieve the size of a file.'''
502 # Note that the RFC doesn't say anything about 'SIZE'
503 resp
= self
.sendcmd('SIZE ' + filename
)
504 if resp
[:3] == '213':
505 return int(resp
[3:].strip())
507 def mkd(self
, dirname
):
508 '''Make a directory, return its full pathname.'''
509 resp
= self
.sendcmd('MKD ' + dirname
)
510 return parse257(resp
)
512 def rmd(self
, dirname
):
513 '''Remove a directory.'''
514 return self
.voidcmd('RMD ' + dirname
)
517 '''Return current working directory.'''
518 resp
= self
.sendcmd('PWD')
519 return parse257(resp
)
522 '''Quit, and close the connection.'''
523 resp
= self
.voidcmd('QUIT')
528 '''Close the connection without assuming anything about it.'''
532 self
.file = self
.sock
= None
538 '''Parse the '150' response for a RETR request.
539 Returns the expected transfer size or None; size is not guaranteed to
540 be present in the 150 message.
542 if resp
[:3] != '150':
543 raise error_reply
, resp
547 _150_re
= re
.compile("150 .* \((\d+) bytes\)", re
.IGNORECASE
)
548 m
= _150_re
.match(resp
)
550 return int(m
.group(1))
557 '''Parse the '227' response for a PASV request.
558 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
559 Return ('host.addr.as.numbers', port#) tuple.'''
561 if resp
[:3] != '227':
562 raise error_reply
, resp
566 _227_re
= re
.compile(r
'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
567 m
= _227_re
.search(resp
)
569 raise error_proto
, resp
571 host
= '.'.join(numbers
[:4])
572 port
= (int(numbers
[4]) << 8) + int(numbers
[5])
576 def parse229(resp
, peer
):
577 '''Parse the '229' response for a EPSV request.
578 Raises error_proto if it does not contain '(|||port|)'
579 Return ('host.addr.as.numbers', port#) tuple.'''
581 if resp
[:3] <> '229':
582 raise error_reply
, resp
583 left
= string
.find(resp
, '(')
584 if left
< 0: raise error_proto
, resp
585 right
= string
.find(resp
, ')', left
+ 1)
587 raise error_proto
, resp
# should contain '(|||port|)'
588 if resp
[left
+ 1] <> resp
[right
- 1]:
589 raise error_proto
, resp
590 parts
= string
.split(resp
[left
+ 1:right
], resp
[left
+1])
592 raise error_proto
, resp
594 port
= string
.atoi(parts
[3])
599 '''Parse the '257' response for a MKD or PWD request.
600 This is a response to a MKD or PWD request: a directory name.
601 Returns the directoryname in the 257 reply.'''
603 if resp
[:3] != '257':
604 raise error_reply
, resp
605 if resp
[3:5] != ' "':
606 return '' # Not compliant to RFC 959, but UNIX ftpd does this
614 if i
>= n
or resp
[i
] != '"':
617 dirname
= dirname
+ c
621 def print_line(line
):
622 '''Default retrlines callback to print a line.'''
626 def ftpcp(source
, sourcename
, target
, targetname
= '', type = 'I'):
627 '''Copy file from one FTP-instance to another.'''
628 if not targetname
: targetname
= sourcename
629 type = 'TYPE ' + type
632 sourcehost
, sourceport
= parse227(source
.sendcmd('PASV'))
633 target
.sendport(sourcehost
, sourceport
)
634 # RFC 959: the user must "listen" [...] BEFORE sending the
636 # So: STOR before RETR, because here the target is a "user".
637 treply
= target
.sendcmd('STOR ' + targetname
)
638 if treply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
639 sreply
= source
.sendcmd('RETR ' + sourcename
)
640 if sreply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
646 """Class to parse & provide access to 'netrc' format files.
648 See the netrc(4) man page for information on the file format.
650 WARNING: This class is obsolete -- use module netrc instead.
657 def __init__(self
, filename
=None):
659 if os
.environ
.has_key("HOME"):
660 filename
= os
.path
.join(os
.environ
["HOME"],
664 "specify file to load or set $HOME"
667 fp
= open(filename
, "r")
672 if in_macro
and line
.strip():
673 macro_lines
.append(line
)
676 self
.__macros
[macro_name
] = tuple(macro_lines
)
679 host
= user
= passwd
= acct
= None
682 while i
< len(words
):
690 elif w1
== 'machine' and w2
:
693 elif w1
== 'login' and w2
:
696 elif w1
== 'password' and w2
:
699 elif w1
== 'account' and w2
:
702 elif w1
== 'macdef' and w2
:
709 self
.__defuser
= user
or self
.__defuser
710 self
.__defpasswd
= passwd
or self
.__defpasswd
711 self
.__defacct
= acct
or self
.__defacct
713 if self
.__hosts
.has_key(host
):
714 ouser
, opasswd
, oacct
= \
717 passwd
= passwd
or opasswd
719 self
.__hosts
[host
] = user
, passwd
, acct
723 """Return a list of hosts mentioned in the .netrc file."""
724 return self
.__hosts
.keys()
726 def get_account(self
, host
):
727 """Returns login information for the named host.
729 The return value is a triple containing userid,
730 password, and the accounting field.
734 user
= passwd
= acct
= None
735 if self
.__hosts
.has_key(host
):
736 user
, passwd
, acct
= self
.__hosts
[host
]
737 user
= user
or self
.__defuser
738 passwd
= passwd
or self
.__defpasswd
739 acct
= acct
or self
.__defacct
740 return user
, passwd
, acct
742 def get_macros(self
):
743 """Return a list of all defined macro names."""
744 return self
.__macros
.keys()
746 def get_macro(self
, macro
):
747 """Return a sequence of lines which define a named macro."""
748 return self
.__macros
[macro
]
754 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
758 while sys
.argv
[1] == '-d':
759 debugging
= debugging
+1
761 if sys
.argv
[1][:2] == '-r':
762 # get name of alternate ~/.netrc file:
763 rcfile
= sys
.argv
[1][2:]
767 ftp
.set_debuglevel(debugging
)
768 userid
= passwd
= acct
= ''
770 netrc
= Netrc(rcfile
)
772 if rcfile
is not None:
773 sys
.stderr
.write("Could not open account file"
774 " -- using anonymous login.")
777 userid
, passwd
, acct
= netrc
.get_account(host
)
779 # no account for host
781 "No account -- using anonymous login.")
782 ftp
.login(userid
, passwd
, acct
)
783 for file in sys
.argv
[2:]:
786 elif file[:2] == '-d':
788 if file[2:]: cmd
= cmd
+ ' ' + file[2:]
789 resp
= ftp
.sendcmd(cmd
)
791 ftp
.set_pasv(not ftp
.passiveserver
)
793 ftp
.retrbinary('RETR ' + file, \
794 sys
.stdout
.write
, 1024)
798 if __name__
== '__main__':