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
43 import SOCKS; socket = SOCKS; del SOCKS
44 from socket
import getfqdn; socket.getfqdn = getfqdn; del getfqdn
48 __all__ = [
"FTP",
"Netrc"]
68 all_errors = (Error, socket.error, IOError, EOFError)
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.
106 def __init__(self, host='', user='', passwd='', acct=''):
109 if user: self.
login(user, passwd, acct)
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)'''
116 if port: self.
port = port
117 msg =
"getaddrinfo returns an empty list"
118 for res
in socket.getaddrinfo(self.
host, self.
port, 0, socket.SOCK_STREAM):
119 af, socktype, proto, canonname, sa = res
122 self.sock.connect(sa)
123 except socket.error, msg:
130 raise socket.error, msg
132 self.
file = self.sock.makefile(
'rb')
137 '''Get the welcome message from the server.
138 (this is read and squirreled away by connect())'''
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'''
150 debug = set_debuglevel
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.'''
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:]
171 self.sock.sendall(line)
181 line = self.file.readline()
184 if not line:
raise EOFError
185 if line[-2:] == CRLF: line = line[:-2]
186 elif line[-1:]
in CRLF: line = line[:-1]
199 line = line + (
'\n' + nextline)
200 if nextline[:3] == code
and \
201 nextline[3:4] !=
'-':
213 raise error_temp, resp
215 raise error_perm, resp
217 raise error_proto, resp
221 """Expect a response beginning with '2'."""
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.'''
234 self.sock.sendall(line, MSG_OOB)
236 if resp[:3]
not in (
'426',
'226'):
237 raise error_proto, resp
240 '''Send a command and return the response.'''
245 """Send a command and expect a response beginning with '2'."""
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)
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`,
'']
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
281 except socket.error, msg:
288 raise socket.error, msg
290 port = sock.getsockname()[1]
291 host = self.sock.getsockname()[0]
292 if self.
af == socket.AF_INET:
299 if self.
af == socket.AF_INET:
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 RESTART command. This is essentially a server
317 marker used to tell the server to skip over any data up to the
323 af, socktype, proto, canon, sa = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0]
330 raise error_reply, resp
337 raise error_reply, resp
338 conn, sockaddr = sock.accept()
339 if resp[:3] ==
'150':
345 """Like ntransfercmd() but returns only the socket."""
348 def login(self, user = '', passwd = '', acct = ''):
349 '''Login, default anonymous.'''
350 if not user: user =
'anonymous'
351 if not passwd: passwd =
''
352 if not acct: acct =
''
353 if user ==
'anonymous' and passwd
in (
'',
'-'):
357 if os.environ.has_key(
'LOGNAME'):
358 realuser = os.environ[
'LOGNAME']
359 elif os.environ.has_key(
'USER'):
360 realuser = os.environ[
'USER']
362 realuser =
'anonymous'
363 except AttributeError:
365 realuser =
'anonymous'
366 passwd = passwd + realuser +
'@' + thishost
367 resp = self.
sendcmd(
'USER ' + user)
368 if resp[0] ==
'3': resp = self.
sendcmd(
'PASS ' + passwd)
369 if resp[0] ==
'3': resp = self.
sendcmd(
'ACCT ' + acct)
371 raise error_reply, resp
374 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
375 """Retrieve data in binary mode.
377 `cmd' is a RETR command. `callback' is a callback function is
378 called for each block. No more than `blocksize' number of
379 bytes will be read from the socket. Optional `rest' is passed
382 A new port is created for you. Return the response code.
387 data = conn.recv(blocksize)
395 '''Retrieve data in line mode.
396 The argument is a RETR or LIST command.
397 The callback function (2nd argument) is called for each line,
398 with trailing CRLF stripped. This creates a new port for you.
399 print_line() is the default callback.'''
400 if not callback: callback = print_line
403 fp = conn.makefile(
'rb')
406 if self.
debugging > 2:
print '*retr*', `line`
409 if line[-2:] == CRLF:
411 elif line[-1:] ==
'\n':
419 '''Store a file in binary mode.'''
423 buf = fp.read(blocksize)
430 '''Store a file in line mode.'''
437 if buf[-1]
in CRLF: buf = buf[:-1]
444 '''Send new account name.'''
445 cmd =
'ACCT ' + password
449 '''Return a list of files in a given directory (default the current).'''
452 cmd = cmd + (
' ' + arg)
458 '''List a directory in long form.
459 By default list current directory to stdout.
460 Optional last argument is callback function; all
461 non-empty arguments before it are concatenated to the
462 LIST command. (This *should* only be used for a pathname.)'''
465 if args[-1:]
and type(args[-1]) != type(
''):
466 args, func = args[:-1], args[-1]
469 cmd = cmd + (
' ' + arg)
474 resp = self.
sendcmd(
'RNFR ' + fromname)
476 raise error_reply, resp
477 return self.
voidcmd(
'RNTO ' + toname)
481 resp = self.
sendcmd(
'DELE ' + filename)
482 if resp[:3]
in (
'250',
'200'):
484 elif resp[:1] ==
'5':
485 raise error_perm, resp
487 raise error_reply, resp
490 '''Change to a directory.'''
494 except error_perm, msg:
495 if msg.args[0][:3] !=
'500':
499 cmd =
'CWD ' + dirname
503 '''Retrieve the size of a file.'''
505 resp = self.
sendcmd(
'SIZE ' + filename)
506 if resp[:3] ==
'213':
510 except (OverflowError, ValueError):
514 '''Make a directory, return its full pathname.'''
515 resp = self.
sendcmd(
'MKD ' + dirname)
519 '''Remove a directory.'''
520 return self.
voidcmd(
'RMD ' + dirname)
523 '''Return current working directory.'''
528 '''Quit, and close the connection.'''
534 '''Close the connection without assuming anything about it.'''
544 '''Parse the '150' response for a RETR request.
545 Returns the expected transfer size or None; size is not guaranteed to
546 be present in the 150 message.
548 if resp[:3] !=
'150':
549 raise error_reply, resp
553 _150_re = re.compile(
"150 .* \((\d+) bytes\)", re.IGNORECASE)
554 m = _150_re.match(resp)
560 except (OverflowError, ValueError):
567 '''Parse the '227' response for a PASV request.
568 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
569 Return ('host.addr.as.numbers', port#) tuple.'''
571 if resp[:3] !=
'227':
572 raise error_reply, resp
576 _227_re = re.compile(
r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
577 m = _227_re.search(resp)
579 raise error_proto, resp
581 host =
'.'.
join(numbers[:4])
582 port = (int(numbers[4]) << 8) + int(numbers[5])
587 '''Parse the '229' response for a EPSV request.
588 Raises error_proto if it does not contain '(|||port|)'
589 Return ('host.addr.as.numbers', port#) tuple.'''
591 if resp[:3] <>
'229':
592 raise error_reply, resp
594 if left < 0:
raise error_proto, resp
597 raise error_proto, resp
598 if resp[left + 1] <> resp[right - 1]:
599 raise error_proto, resp
600 parts =
string.split(resp[left + 1:right], resp[left+1])
602 raise error_proto, resp
609 '''Parse the '257' response for a MKD or PWD request.
610 This is a response to a MKD or PWD request: a directory name.
611 Returns the directoryname in the 257 reply.'''
613 if resp[:3] !=
'257':
614 raise error_reply, resp
615 if resp[3:5] !=
' "':
624 if i >= n
or resp[i] !=
'"':
627 dirname = dirname + c
632 '''Default retrlines callback to print a line.'''
636 def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
637 '''Copy file from one FTP-instance to another.'''
638 if not targetname: targetname = sourcename
639 type =
'TYPE ' + type
642 sourcehost, sourceport =
parse227(source.sendcmd(
'PASV'))
643 target.sendport(sourcehost, sourceport)
647 treply = target.sendcmd(
'STOR ' + targetname)
648 if treply[:3]
not in (
'125',
'150'):
raise error_proto
649 sreply = source.sendcmd(
'RETR ' + sourcename)
650 if sreply[:3]
not in (
'125',
'150'):
raise error_proto
656 """Class to parse & provide access to 'netrc' format files.
658 See the netrc(4) man page for information on the file format.
660 WARNING: This class is obsolete -- use module netrc instead.
669 if os.environ.has_key(
"HOME"):
670 filename = os.path.join(os.environ[
"HOME"],
674 "specify file to load or set $HOME"
677 fp =
open(filename,
"r")
682 if in_macro
and line.strip():
683 macro_lines.append(line)
689 host = user = passwd = acct =
None
692 while i < len(words):
700 elif w1 ==
'machine' and w2:
703 elif w1 ==
'login' and w2:
706 elif w1 ==
'password' and w2:
709 elif w1 ==
'account' and w2:
712 elif w1 ==
'macdef' and w2:
723 if self.__hosts.has_key(host):
724 ouser, opasswd, oacct = \
727 passwd = passwd
or opasswd
729 self.
__hosts[host] = user, passwd, acct
733 """Return a list of hosts mentioned in the .netrc file."""
734 return self.__hosts.keys()
737 """Returns login information for the named host.
739 The return value is a triple containing userid,
740 password, and the accounting field.
744 user = passwd = acct =
None
745 if self.__hosts.has_key(host):
746 user, passwd, acct = self.
__hosts[host]
750 return user, passwd, acct
753 """Return a list of all defined macro names."""
754 return self.__macros.keys()
757 """Return a sequence of lines which define a named macro."""
764 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
768 while sys.argv[1] ==
'-d':
769 debugging = debugging+1
771 if sys.argv[1][:2] ==
'-r':
773 rcfile = sys.argv[1][2:]
777 ftp.set_debuglevel(debugging)
778 userid = passwd = acct =
''
780 netrc =
Netrc(rcfile)
782 if rcfile
is not None:
783 sys.stderr.write(
"Could not open account file"
784 " -- using anonymous login.")
787 userid, passwd, acct = netrc.get_account(host)
791 "No account -- using anonymous login.")
792 ftp.login(userid, passwd, acct)
793 for file
in sys.argv[2:]:
796 elif file[:2] ==
'-d':
798 if file[2:]: cmd = cmd +
' ' + file[2:]
799 resp = ftp.sendcmd(cmd)
801 ftp.set_pasv(
not ftp.passiveserver)
803 ftp.retrbinary(
'RETR ' + file, \
804 sys.stdout.write, 1024)
808 if __name__ ==
'__main__':