This commit was manufactured by cvs2svn to create tag 'r221c2'.
[python/dscho.git] / Lib / ftplib.py
blob01895fd66862f089a15621577fae662d87d7e2eb
1 """An FTP client class and some helper functions.
3 Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds
5 Example:
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
12 total 9
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.'
23 >>> ftp.quit()
24 '221 Goodbye.'
25 >>>
27 A nice test that reveals some of the network dialogue would be:
28 python ftplib.py -d localhost -l -p -l
29 """
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.
37 import os
38 import sys
39 import string
41 # Import SOCKS module if it exists, else standard socket module socket
42 try:
43 import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket
44 from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn
45 except ImportError:
46 import socket
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
55 FTP_PORT = 21
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)
72 CRLF = '\r\n'
75 # The class itself
76 class FTP:
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
89 below for details).
90 The download/upload functions first issue appropriate TYPE
91 and PORT or PASV commands.
92 '''
94 debugging = 0
95 host = ''
96 port = FTP_PORT
97 sock = None
98 file = None
99 welcome = None
100 passiveserver = 1
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=''):
107 if host:
108 self.connect(host)
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 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
120 try:
121 self.sock = socket.socket(af, socktype, proto)
122 self.sock.connect(sa)
123 except socket.error, msg:
124 if self.sock:
125 self.sock.close()
126 self.sock = None
127 continue
128 break
129 if not self.sock:
130 raise socket.error, msg
131 self.af = af
132 self.file = self.sock.makefile('rb')
133 self.welcome = self.getresp()
134 return self.welcome
136 def getwelcome(self):
137 '''Get the welcome message from the server.
138 (this is read and squirreled away by connect())'''
139 if self.debugging:
140 print '*welcome*', self.sanitize(self.welcome)
141 return 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 ':
161 i = len(s)
162 while i > 5 and s[i-1] in '\r\n':
163 i = i-1
164 s = s[:5] + '*'*(i-5) + s[i:]
165 return `s`
167 # Internal: send one line to the server, appending CRLF
168 def putline(self, line):
169 line = line + CRLF
170 if self.debugging > 1: print '*put*', self.sanitize(line)
171 self.sock.sendall(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)
176 self.putline(line)
178 # Internal: return one line from the server, stripping CRLF.
179 # Raise EOFError if the connection is closed
180 def getline(self):
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]
187 return line
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()
195 if line[3:4] == '-':
196 code = line[:3]
197 while 1:
198 nextline = self.getline()
199 line = line + ('\n' + nextline)
200 if nextline[:3] == code and \
201 nextline[3:4] != '-':
202 break
203 return line
205 # Internal: get a response from the server.
206 # Raise various errors if the response indicates an error
207 def getresp(self):
208 resp = self.getmultiline()
209 if self.debugging: print '*resp*', self.sanitize(resp)
210 self.lastresp = resp[:3]
211 c = resp[:1]
212 if c == '4':
213 raise error_temp, resp
214 if c == '5':
215 raise error_perm, resp
216 if c not in '123':
217 raise error_proto, resp
218 return resp
220 def voidresp(self):
221 """Expect a response beginning with '2'."""
222 resp = self.getresp()
223 if resp[0] != '2':
224 raise error_reply, resp
225 return resp
227 def abort(self):
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.'''
232 line = 'ABOR' + CRLF
233 if self.debugging > 1: print '*put urgent*', self.sanitize(line)
234 self.sock.sendall(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.'''
241 self.putcmd(cmd)
242 return self.getresp()
244 def voidcmd(self, cmd):
245 """Send a command and expect a response beginning with '2'."""
246 self.putcmd(cmd)
247 return self.voidresp()
249 def sendport(self, host, port):
250 '''Send a PORT command with the current host and the given
251 port number.
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.'''
261 af = 0
262 if self.af == socket.AF_INET:
263 af = 1
264 if self.af == socket.AF_INET6:
265 af = 2
266 if af == 0:
267 raise error_proto, 'unsupported address family'
268 fields = ['', `af`, host, `port`, '']
269 cmd = 'EPRT ' + string.joinfields(fields, '|')
270 return self.voidcmd(cmd)
272 def makeport(self):
273 '''Create a new socket and send a PORT command for it.'''
274 msg = "getaddrinfo returns an empty list"
275 sock = None
276 for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
277 af, socktype, proto, canonname, sa = res
278 try:
279 sock = socket.socket(af, socktype, proto)
280 sock.bind(sa)
281 except socket.error, msg:
282 if sock:
283 sock.close()
284 sock = None
285 continue
286 break
287 if not sock:
288 raise socket.error, msg
289 sock.listen(1)
290 port = sock.getsockname()[1] # Get proper port
291 host = self.sock.getsockname()[0] # Get proper host
292 if self.af == socket.AF_INET:
293 resp = self.sendport(host, port)
294 else:
295 resp = self.sendeprt(host, port)
296 return sock
298 def makepasv(self):
299 if self.af == socket.AF_INET:
300 host, port = parse227(self.sendcmd('PASV'))
301 else:
302 host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
303 return host, port
305 def ntransfercmd(self, cmd, rest=None):
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
318 given marker.
320 size = None
321 if self.passiveserver:
322 host, port = self.makepasv()
323 af, socktype, proto, canon, sa = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0]
324 conn = socket.socket(af, socktype, proto)
325 conn.connect(sa)
326 if rest is not None:
327 self.sendcmd("REST %s" % rest)
328 resp = self.sendcmd(cmd)
329 if resp[0] != '1':
330 raise error_reply, resp
331 else:
332 sock = self.makeport()
333 if rest is not None:
334 self.sendcmd("REST %s" % rest)
335 resp = self.sendcmd(cmd)
336 if resp[0] != '1':
337 raise error_reply, resp
338 conn, sockaddr = sock.accept()
339 if resp[:3] == '150':
340 # this is conditional in case we received a 125
341 size = parse150(resp)
342 return conn, size
344 def transfercmd(self, cmd, rest=None):
345 """Like ntransfercmd() but returns only the socket."""
346 return self.ntransfercmd(cmd, rest)[0]
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 ('', '-'):
354 # get fully qualified domain name of local host
355 thishost = socket.getfqdn()
356 try:
357 if os.environ.has_key('LOGNAME'):
358 realuser = os.environ['LOGNAME']
359 elif os.environ.has_key('USER'):
360 realuser = os.environ['USER']
361 else:
362 realuser = 'anonymous'
363 except AttributeError:
364 # Not all systems have os.environ....
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)
370 if resp[0] != '2':
371 raise error_reply, resp
372 return 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
380 to transfercmd().
382 A new port is created for you. Return the response code.
384 self.voidcmd('TYPE I')
385 conn = self.transfercmd(cmd, rest)
386 while 1:
387 data = conn.recv(blocksize)
388 if not data:
389 break
390 callback(data)
391 conn.close()
392 return self.voidresp()
394 def retrlines(self, cmd, callback = None):
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
401 resp = self.sendcmd('TYPE A')
402 conn = self.transfercmd(cmd)
403 fp = conn.makefile('rb')
404 while 1:
405 line = fp.readline()
406 if self.debugging > 2: print '*retr*', `line`
407 if not line:
408 break
409 if line[-2:] == CRLF:
410 line = line[:-2]
411 elif line[-1:] == '\n':
412 line = line[:-1]
413 callback(line)
414 fp.close()
415 conn.close()
416 return self.voidresp()
418 def storbinary(self, cmd, fp, blocksize=8192):
419 '''Store a file in binary mode.'''
420 self.voidcmd('TYPE I')
421 conn = self.transfercmd(cmd)
422 while 1:
423 buf = fp.read(blocksize)
424 if not buf: break
425 conn.sendall(buf)
426 conn.close()
427 return self.voidresp()
429 def storlines(self, cmd, fp):
430 '''Store a file in line mode.'''
431 self.voidcmd('TYPE A')
432 conn = self.transfercmd(cmd)
433 while 1:
434 buf = fp.readline()
435 if not buf: break
436 if buf[-2:] != CRLF:
437 if buf[-1] in CRLF: buf = buf[:-1]
438 buf = buf + CRLF
439 conn.sendall(buf)
440 conn.close()
441 return self.voidresp()
443 def acct(self, password):
444 '''Send new account name.'''
445 cmd = 'ACCT ' + password
446 return self.voidcmd(cmd)
448 def nlst(self, *args):
449 '''Return a list of files in a given directory (default the current).'''
450 cmd = 'NLST'
451 for arg in args:
452 cmd = cmd + (' ' + arg)
453 files = []
454 self.retrlines(cmd, files.append)
455 return files
457 def dir(self, *args):
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.)'''
463 cmd = 'LIST'
464 func = None
465 if args[-1:] and type(args[-1]) != type(''):
466 args, func = args[:-1], args[-1]
467 for arg in args:
468 if arg:
469 cmd = cmd + (' ' + arg)
470 self.retrlines(cmd, func)
472 def rename(self, fromname, toname):
473 '''Rename a file.'''
474 resp = self.sendcmd('RNFR ' + fromname)
475 if resp[0] != '3':
476 raise error_reply, resp
477 return self.voidcmd('RNTO ' + toname)
479 def delete(self, filename):
480 '''Delete a file.'''
481 resp = self.sendcmd('DELE ' + filename)
482 if resp[:3] in ('250', '200'):
483 return resp
484 elif resp[:1] == '5':
485 raise error_perm, resp
486 else:
487 raise error_reply, resp
489 def cwd(self, dirname):
490 '''Change to a directory.'''
491 if dirname == '..':
492 try:
493 return self.voidcmd('CDUP')
494 except error_perm, msg:
495 if msg.args[0][:3] != '500':
496 raise
497 elif dirname == '':
498 dirname = '.' # does nothing, but could return error
499 cmd = 'CWD ' + dirname
500 return self.voidcmd(cmd)
502 def size(self, filename):
503 '''Retrieve the size of a file.'''
504 # Note that the RFC doesn't say anything about 'SIZE'
505 resp = self.sendcmd('SIZE ' + filename)
506 if resp[:3] == '213':
507 s = resp[3:].strip()
508 try:
509 return int(s)
510 except (OverflowError, ValueError):
511 return long(s)
513 def mkd(self, dirname):
514 '''Make a directory, return its full pathname.'''
515 resp = self.sendcmd('MKD ' + dirname)
516 return parse257(resp)
518 def rmd(self, dirname):
519 '''Remove a directory.'''
520 return self.voidcmd('RMD ' + dirname)
522 def pwd(self):
523 '''Return current working directory.'''
524 resp = self.sendcmd('PWD')
525 return parse257(resp)
527 def quit(self):
528 '''Quit, and close the connection.'''
529 resp = self.voidcmd('QUIT')
530 self.close()
531 return resp
533 def close(self):
534 '''Close the connection without assuming anything about it.'''
535 if self.file:
536 self.file.close()
537 self.sock.close()
538 self.file = self.sock = None
541 _150_re = None
543 def parse150(resp):
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
550 global _150_re
551 if _150_re is None:
552 import re
553 _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE)
554 m = _150_re.match(resp)
555 if not m:
556 return None
557 s = m.group(1)
558 try:
559 return int(s)
560 except (OverflowError, ValueError):
561 return long(s)
564 _227_re = None
566 def parse227(resp):
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
573 global _227_re
574 if _227_re is None:
575 import re
576 _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
577 m = _227_re.search(resp)
578 if not m:
579 raise error_proto, resp
580 numbers = m.groups()
581 host = '.'.join(numbers[:4])
582 port = (int(numbers[4]) << 8) + int(numbers[5])
583 return host, port
586 def parse229(resp, peer):
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
593 left = string.find(resp, '(')
594 if left < 0: raise error_proto, resp
595 right = string.find(resp, ')', left + 1)
596 if right < 0:
597 raise error_proto, resp # should contain '(|||port|)'
598 if resp[left + 1] <> resp[right - 1]:
599 raise error_proto, resp
600 parts = string.split(resp[left + 1:right], resp[left+1])
601 if len(parts) <> 5:
602 raise error_proto, resp
603 host = peer[0]
604 port = string.atoi(parts[3])
605 return host, port
608 def parse257(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] != ' "':
616 return '' # Not compliant to RFC 959, but UNIX ftpd does this
617 dirname = ''
618 i = 5
619 n = len(resp)
620 while i < n:
621 c = resp[i]
622 i = i+1
623 if c == '"':
624 if i >= n or resp[i] != '"':
625 break
626 i = i+1
627 dirname = dirname + c
628 return dirname
631 def print_line(line):
632 '''Default retrlines callback to print a line.'''
633 print 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
640 source.voidcmd(type)
641 target.voidcmd(type)
642 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
643 target.sendport(sourcehost, sourceport)
644 # RFC 959: the user must "listen" [...] BEFORE sending the
645 # transfer request.
646 # So: STOR before RETR, because here the target is a "user".
647 treply = target.sendcmd('STOR ' + targetname)
648 if treply[:3] not in ('125', '150'): raise error_proto # RFC 959
649 sreply = source.sendcmd('RETR ' + sourcename)
650 if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959
651 source.voidresp()
652 target.voidresp()
655 class Netrc:
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.
663 __defuser = None
664 __defpasswd = None
665 __defacct = None
667 def __init__(self, filename=None):
668 if not filename:
669 if os.environ.has_key("HOME"):
670 filename = os.path.join(os.environ["HOME"],
671 ".netrc")
672 else:
673 raise IOError, \
674 "specify file to load or set $HOME"
675 self.__hosts = {}
676 self.__macros = {}
677 fp = open(filename, "r")
678 in_macro = 0
679 while 1:
680 line = fp.readline()
681 if not line: break
682 if in_macro and line.strip():
683 macro_lines.append(line)
684 continue
685 elif in_macro:
686 self.__macros[macro_name] = tuple(macro_lines)
687 in_macro = 0
688 words = line.split()
689 host = user = passwd = acct = None
690 default = 0
691 i = 0
692 while i < len(words):
693 w1 = words[i]
694 if i+1 < len(words):
695 w2 = words[i + 1]
696 else:
697 w2 = None
698 if w1 == 'default':
699 default = 1
700 elif w1 == 'machine' and w2:
701 host = w2.lower()
702 i = i + 1
703 elif w1 == 'login' and w2:
704 user = w2
705 i = i + 1
706 elif w1 == 'password' and w2:
707 passwd = w2
708 i = i + 1
709 elif w1 == 'account' and w2:
710 acct = w2
711 i = i + 1
712 elif w1 == 'macdef' and w2:
713 macro_name = w2
714 macro_lines = []
715 in_macro = 1
716 break
717 i = i + 1
718 if default:
719 self.__defuser = user or self.__defuser
720 self.__defpasswd = passwd or self.__defpasswd
721 self.__defacct = acct or self.__defacct
722 if host:
723 if self.__hosts.has_key(host):
724 ouser, opasswd, oacct = \
725 self.__hosts[host]
726 user = user or ouser
727 passwd = passwd or opasswd
728 acct = acct or oacct
729 self.__hosts[host] = user, passwd, acct
730 fp.close()
732 def get_hosts(self):
733 """Return a list of hosts mentioned in the .netrc file."""
734 return self.__hosts.keys()
736 def get_account(self, host):
737 """Returns login information for the named host.
739 The return value is a triple containing userid,
740 password, and the accounting field.
743 host = host.lower()
744 user = passwd = acct = None
745 if self.__hosts.has_key(host):
746 user, passwd, acct = self.__hosts[host]
747 user = user or self.__defuser
748 passwd = passwd or self.__defpasswd
749 acct = acct or self.__defacct
750 return user, passwd, acct
752 def get_macros(self):
753 """Return a list of all defined macro names."""
754 return self.__macros.keys()
756 def get_macro(self, macro):
757 """Return a sequence of lines which define a named macro."""
758 return self.__macros[macro]
762 def test():
763 '''Test program.
764 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
766 debugging = 0
767 rcfile = None
768 while sys.argv[1] == '-d':
769 debugging = debugging+1
770 del sys.argv[1]
771 if sys.argv[1][:2] == '-r':
772 # get name of alternate ~/.netrc file:
773 rcfile = sys.argv[1][2:]
774 del sys.argv[1]
775 host = sys.argv[1]
776 ftp = FTP(host)
777 ftp.set_debuglevel(debugging)
778 userid = passwd = acct = ''
779 try:
780 netrc = Netrc(rcfile)
781 except IOError:
782 if rcfile is not None:
783 sys.stderr.write("Could not open account file"
784 " -- using anonymous login.")
785 else:
786 try:
787 userid, passwd, acct = netrc.get_account(host)
788 except KeyError:
789 # no account for host
790 sys.stderr.write(
791 "No account -- using anonymous login.")
792 ftp.login(userid, passwd, acct)
793 for file in sys.argv[2:]:
794 if file[:2] == '-l':
795 ftp.dir(file[2:])
796 elif file[:2] == '-d':
797 cmd = 'CWD'
798 if file[2:]: cmd = cmd + ' ' + file[2:]
799 resp = ftp.sendcmd(cmd)
800 elif file == '-p':
801 ftp.set_pasv(not ftp.passiveserver)
802 else:
803 ftp.retrbinary('RETR ' + file, \
804 sys.stdout.write, 1024)
805 ftp.quit()
808 if __name__ == '__main__':
809 test()