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.
94 # Initialization method (called by class instantiation).
95 # Initialize host to localhost, port to standard ftp port
96 # Optional arguments are host (for connect()),
97 # and user, passwd, acct (for login())
98 def __init__(self
, host
= '', user
= '', passwd
= '', acct
= ''):
99 # Initialize the instance to something mostly harmless
108 resp
= self
.connect(host
)
109 if user
: resp
= 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
= 1
118 self
.sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
119 self
.sock
.connect((self
.host
, self
.port
))
120 self
.file = self
.sock
.makefile('rb')
121 self
.welcome
= self
.getresp()
124 def getwelcome(self
):
125 '''Get the welcome message from the server.
126 (this is read and squirreled away by connect())'''
128 print '*welcome*', self
.sanitize(self
.welcome
)
131 def set_debuglevel(self
, level
):
132 '''Set the debugging level.
133 The required argument level means:
134 0: no debugging output (default)
135 1: print commands and responses but not body text etc.
136 2: also print raw lines read and sent before stripping CR/LF'''
137 self
.debugging
= level
138 debug
= set_debuglevel
140 def set_pasv(self
, val
):
141 '''Use passive or active mode for data transfers.
142 With a false argument, use the normal PORT mode,
143 With a true argument, use the PASV command.'''
144 self
.passiveserver
= val
146 # Internal: "sanitize" a string for printing
147 def sanitize(self
, s
):
148 if s
[:5] == 'pass ' or s
[:5] == 'PASS ':
150 while i
> 5 and s
[i
-1] in '\r\n':
152 s
= s
[:5] + '*'*(i
-5) + s
[i
:]
155 # Internal: send one line to the server, appending CRLF
156 def putline(self
, line
):
158 if self
.debugging
> 1: print '*put*', self
.sanitize(line
)
161 # Internal: send one command to the server (through putline())
162 def putcmd(self
, line
):
163 if self
.debugging
: print '*cmd*', self
.sanitize(line
)
166 # Internal: return one line from the server, stripping CRLF.
167 # Raise EOFError if the connection is closed
169 line
= self
.file.readline()
170 if self
.debugging
> 1:
171 print '*get*', self
.sanitize(line
)
172 if not line
: raise EOFError
173 if line
[-2:] == CRLF
: line
= line
[:-2]
174 elif line
[-1:] in CRLF
: line
= line
[:-1]
177 # Internal: get a response from the server, which may possibly
178 # consist of multiple lines. Return a single string with no
179 # trailing CRLF. If the response consists of multiple lines,
180 # these are separated by '\n' characters in the string
181 def getmultiline(self
):
182 line
= self
.getline()
186 nextline
= self
.getline()
187 line
= line
+ ('\n' + nextline
)
188 if nextline
[:3] == code
and \
189 nextline
[3:4] != '-':
193 # Internal: get a response from the server.
194 # Raise various errors if the response indicates an error
196 resp
= self
.getmultiline()
197 if self
.debugging
: print '*resp*', self
.sanitize(resp
)
198 self
.lastresp
= resp
[:3]
201 raise error_temp
, resp
203 raise error_perm
, resp
205 raise error_proto
, resp
209 """Expect a response beginning with '2'."""
210 resp
= self
.getresp()
212 raise error_reply
, resp
216 '''Abort a file transfer. Uses out-of-band data.
217 This does not follow the procedure from the RFC to send Telnet
218 IP and Synch; that doesn't seem to work with the servers I've
219 tried. Instead, just send the ABOR command as OOB data.'''
221 if self
.debugging
> 1: print '*put urgent*', self
.sanitize(line
)
222 self
.sock
.send(line
, MSG_OOB
)
223 resp
= self
.getmultiline()
224 if resp
[:3] not in ('426', '226'):
225 raise error_proto
, resp
227 def sendcmd(self
, cmd
):
228 '''Send a command and return the response.'''
230 return self
.getresp()
232 def voidcmd(self
, cmd
):
233 """Send a command and expect a response beginning with '2'."""
235 return self
.voidresp()
237 def sendport(self
, host
, port
):
238 '''Send a PORT command with the current host and the given
241 hbytes
= host
.split('.')
242 pbytes
= [`port
/256`
, `port
%256`
]
243 bytes
= hbytes
+ pbytes
244 cmd
= 'PORT ' + ','.join(bytes
)
245 return self
.voidcmd(cmd
)
248 '''Create a new socket and send a PORT command for it.'''
250 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
253 dummyhost
, port
= sock
.getsockname() # Get proper port
254 host
, dummyport
= self
.sock
.getsockname() # Get proper host
255 resp
= self
.sendport(host
, port
)
258 def ntransfercmd(self
, cmd
, rest
=None):
259 """Initiate a transfer over the data connection.
261 If the transfer is active, send a port command and the
262 transfer command, and accept the connection. If the server is
263 passive, send a pasv command, connect to it, and start the
264 transfer command. Either way, return the socket for the
265 connection and the expected size of the transfer. The
266 expected size may be None if it could not be determined.
268 Optional `rest' argument can be a string that is sent as the
269 argument to a RESTART command. This is essentially a server
270 marker used to tell the server to skip over any data up to the
274 if self
.passiveserver
:
275 host
, port
= parse227(self
.sendcmd('PASV'))
276 conn
=socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
277 conn
.connect((host
, port
))
279 self
.sendcmd("REST %s" % rest
)
280 resp
= self
.sendcmd(cmd
)
282 raise error_reply
, resp
284 sock
= self
.makeport()
286 self
.sendcmd("REST %s" % rest
)
287 resp
= self
.sendcmd(cmd
)
289 raise error_reply
, resp
290 conn
, sockaddr
= sock
.accept()
291 if resp
[:3] == '150':
292 # this is conditional in case we received a 125
293 size
= parse150(resp
)
296 def transfercmd(self
, cmd
, rest
=None):
297 """Like nstransfercmd() but returns only the socket."""
298 return self
.ntransfercmd(cmd
, rest
)[0]
300 def login(self
, user
= '', passwd
= '', acct
= ''):
301 '''Login, default anonymous.'''
302 if not user
: user
= 'anonymous'
303 if not passwd
: passwd
= ''
304 if not acct
: acct
= ''
305 if user
== 'anonymous' and passwd
in ('', '-'):
306 # get fully qualified domain name of local host
307 thishost
= socket
.getfqdn()
309 if os
.environ
.has_key('LOGNAME'):
310 realuser
= os
.environ
['LOGNAME']
311 elif os
.environ
.has_key('USER'):
312 realuser
= os
.environ
['USER']
314 realuser
= 'anonymous'
315 except AttributeError:
316 # Not all systems have os.environ....
317 realuser
= 'anonymous'
318 passwd
= passwd
+ realuser
+ '@' + thishost
319 resp
= self
.sendcmd('USER ' + user
)
320 if resp
[0] == '3': resp
= self
.sendcmd('PASS ' + passwd
)
321 if resp
[0] == '3': resp
= self
.sendcmd('ACCT ' + acct
)
323 raise error_reply
, resp
326 def retrbinary(self
, cmd
, callback
, blocksize
=8192, rest
=None):
327 """Retrieve data in binary mode.
329 `cmd' is a RETR command. `callback' is a callback function is
330 called for each block. No more than `blocksize' number of
331 bytes will be read from the socket. Optional `rest' is passed
334 A new port is created for you. Return the response code.
336 self
.voidcmd('TYPE I')
337 conn
= self
.transfercmd(cmd
, rest
)
339 data
= conn
.recv(blocksize
)
344 return self
.voidresp()
346 def retrlines(self
, cmd
, callback
= None):
347 '''Retrieve data in line mode.
348 The argument is a RETR or LIST command.
349 The callback function (2nd argument) is called for each line,
350 with trailing CRLF stripped. This creates a new port for you.
351 print_line() is the default callback.'''
352 if not callback
: callback
= print_line
353 resp
= self
.sendcmd('TYPE A')
354 conn
= self
.transfercmd(cmd
)
355 fp
= conn
.makefile('rb')
358 if self
.debugging
> 2: print '*retr*', `line`
361 if line
[-2:] == CRLF
:
363 elif line
[-1:] == '\n':
368 return self
.voidresp()
370 def storbinary(self
, cmd
, fp
, blocksize
=8192):
371 '''Store a file in binary mode.'''
372 self
.voidcmd('TYPE I')
373 conn
= self
.transfercmd(cmd
)
375 buf
= fp
.read(blocksize
)
379 return self
.voidresp()
381 def storlines(self
, cmd
, fp
):
382 '''Store a file in line mode.'''
383 self
.voidcmd('TYPE A')
384 conn
= self
.transfercmd(cmd
)
389 if buf
[-1] in CRLF
: buf
= buf
[:-1]
393 return self
.voidresp()
395 def acct(self
, password
):
396 '''Send new account name.'''
397 cmd
= 'ACCT ' + password
398 return self
.voidcmd(cmd
)
400 def nlst(self
, *args
):
401 '''Return a list of files in a given directory (default the current).'''
404 cmd
= cmd
+ (' ' + arg
)
406 self
.retrlines(cmd
, files
.append
)
409 def dir(self
, *args
):
410 '''List a directory in long form.
411 By default list current directory to stdout.
412 Optional last argument is callback function; all
413 non-empty arguments before it are concatenated to the
414 LIST command. (This *should* only be used for a pathname.)'''
417 if args
[-1:] and type(args
[-1]) != type(''):
418 args
, func
= args
[:-1], args
[-1]
421 cmd
= cmd
+ (' ' + arg
)
422 self
.retrlines(cmd
, func
)
424 def rename(self
, fromname
, toname
):
426 resp
= self
.sendcmd('RNFR ' + fromname
)
428 raise error_reply
, resp
429 return self
.voidcmd('RNTO ' + toname
)
431 def delete(self
, filename
):
433 resp
= self
.sendcmd('DELE ' + filename
)
434 if resp
[:3] in ('250', '200'):
436 elif resp
[:1] == '5':
437 raise error_perm
, resp
439 raise error_reply
, resp
441 def cwd(self
, dirname
):
442 '''Change to a directory.'''
445 return self
.voidcmd('CDUP')
446 except error_perm
, msg
:
448 raise error_perm
, msg
450 dirname
= '.' # does nothing, but could return error
451 cmd
= 'CWD ' + dirname
452 return self
.voidcmd(cmd
)
454 def size(self
, filename
):
455 '''Retrieve the size of a file.'''
456 # Note that the RFC doesn't say anything about 'SIZE'
457 resp
= self
.sendcmd('SIZE ' + filename
)
458 if resp
[:3] == '213':
459 return int(resp
[3:].strip())
461 def mkd(self
, dirname
):
462 '''Make a directory, return its full pathname.'''
463 resp
= self
.sendcmd('MKD ' + dirname
)
464 return parse257(resp
)
466 def rmd(self
, dirname
):
467 '''Remove a directory.'''
468 return self
.voidcmd('RMD ' + dirname
)
471 '''Return current working directory.'''
472 resp
= self
.sendcmd('PWD')
473 return parse257(resp
)
476 '''Quit, and close the connection.'''
477 resp
= self
.voidcmd('QUIT')
482 '''Close the connection without assuming anything about it.'''
485 del self
.file, self
.sock
491 '''Parse the '150' response for a RETR request.
492 Returns the expected transfer size or None; size is not guaranteed to
493 be present in the 150 message.
495 if resp
[:3] != '150':
496 raise error_reply
, resp
500 _150_re
= re
.compile("150 .* \((\d+) bytes\)", re
.IGNORECASE
)
501 m
= _150_re
.match(resp
)
503 return int(m
.group(1))
508 '''Parse the '227' response for a PASV request.
509 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
510 Return ('host.addr.as.numbers', port#) tuple.'''
512 if resp
[:3] != '227':
513 raise error_reply
, resp
514 left
= resp
.find('(')
515 if left
< 0: raise error_proto
, resp
516 right
= resp
.find(')', left
+ 1)
518 raise error_proto
, resp
# should contain '(h1,h2,h3,h4,p1,p2)'
519 numbers
= resp
[left
+1:right
].split(',')
520 if len(numbers
) != 6:
521 raise error_proto
, resp
522 host
= '.'.join(numbers
[:4])
523 port
= (int(numbers
[4]) << 8) + int(numbers
[5])
528 '''Parse the '257' response for a MKD or PWD request.
529 This is a response to a MKD or PWD request: a directory name.
530 Returns the directoryname in the 257 reply.'''
532 if resp
[:3] != '257':
533 raise error_reply
, resp
534 if resp
[3:5] != ' "':
535 return '' # Not compliant to RFC 959, but UNIX ftpd does this
543 if i
>= n
or resp
[i
] != '"':
546 dirname
= dirname
+ c
550 def print_line(line
):
551 '''Default retrlines callback to print a line.'''
555 def ftpcp(source
, sourcename
, target
, targetname
= '', type = 'I'):
556 '''Copy file from one FTP-instance to another.'''
557 if not targetname
: targetname
= sourcename
558 type = 'TYPE ' + type
561 sourcehost
, sourceport
= parse227(source
.sendcmd('PASV'))
562 target
.sendport(sourcehost
, sourceport
)
563 # RFC 959: the user must "listen" [...] BEFORE sending the
565 # So: STOR before RETR, because here the target is a "user".
566 treply
= target
.sendcmd('STOR ' + targetname
)
567 if treply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
568 sreply
= source
.sendcmd('RETR ' + sourcename
)
569 if sreply
[:3] not in ('125', '150'): raise error_proto
# RFC 959
575 """Class to parse & provide access to 'netrc' format files.
577 See the netrc(4) man page for information on the file format.
579 WARNING: This class is obsolete -- use module netrc instead.
586 def __init__(self
, filename
=None):
588 if os
.environ
.has_key("HOME"):
589 filename
= os
.path
.join(os
.environ
["HOME"],
593 "specify file to load or set $HOME"
596 fp
= open(filename
, "r")
601 if in_macro
and line
.strip():
602 macro_lines
.append(line
)
605 self
.__macros
[macro_name
] = tuple(macro_lines
)
608 host
= user
= passwd
= acct
= None
611 while i
< len(words
):
619 elif w1
== 'machine' and w2
:
622 elif w1
== 'login' and w2
:
625 elif w1
== 'password' and w2
:
628 elif w1
== 'account' and w2
:
631 elif w1
== 'macdef' and w2
:
638 self
.__defuser
= user
or self
.__defuser
639 self
.__defpasswd
= passwd
or self
.__defpasswd
640 self
.__defacct
= acct
or self
.__defacct
642 if self
.__hosts
.has_key(host
):
643 ouser
, opasswd
, oacct
= \
646 passwd
= passwd
or opasswd
648 self
.__hosts
[host
] = user
, passwd
, acct
652 """Return a list of hosts mentioned in the .netrc file."""
653 return self
.__hosts
.keys()
655 def get_account(self
, host
):
656 """Returns login information for the named host.
658 The return value is a triple containing userid,
659 password, and the accounting field.
663 user
= passwd
= acct
= None
664 if self
.__hosts
.has_key(host
):
665 user
, passwd
, acct
= self
.__hosts
[host
]
666 user
= user
or self
.__defuser
667 passwd
= passwd
or self
.__defpasswd
668 acct
= acct
or self
.__defacct
669 return user
, passwd
, acct
671 def get_macros(self
):
672 """Return a list of all defined macro names."""
673 return self
.__macros
.keys()
675 def get_macro(self
, macro
):
676 """Return a sequence of lines which define a named macro."""
677 return self
.__macros
[macro
]
683 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
687 while sys
.argv
[1] == '-d':
688 debugging
= debugging
+1
690 if sys
.argv
[1][:2] == '-r':
691 # get name of alternate ~/.netrc file:
692 rcfile
= sys
.argv
[1][2:]
696 ftp
.set_debuglevel(debugging
)
697 userid
= passwd
= acct
= ''
699 netrc
= Netrc(rcfile
)
701 if rcfile
is not None:
702 sys
.stderr
.write("Could not open account file"
703 " -- using anonymous login.")
706 userid
, passwd
, acct
= netrc
.get_account(host
)
708 # no account for host
710 "No account -- using anonymous login.")
711 ftp
.login(userid
, passwd
, acct
)
712 for file in sys
.argv
[2:]:
715 elif file[:2] == '-d':
717 if file[2:]: cmd
= cmd
+ ' ' + file[2:]
718 resp
= ftp
.sendcmd(cmd
)
720 ftp
.set_pasv(not ftp
.passiveserver
)
722 ftp
.retrbinary('RETR ' + file, \
723 sys
.stdout
.write
, 1024)
727 if __name__
== '__main__':