Merged revisions 81181 via svnmerge from
[python/dscho.git] / Lib / ftplib.py
blobea91c1707c137dfcd6185facfc7e37786dfcb4cd
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 anonymous@
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.
35 # Modified by Phil Schwartz to add storbinary and storlines callbacks.
38 import os
39 import sys
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
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
56 FTP_PORT = 21
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)
73 CRLF = '\r\n'
74 B_CRLF = b'\r\n'
76 # The class itself
77 class FTP:
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
96 below for details).
97 The download/upload functions first issue appropriate TYPE
98 and PORT or PASV commands.
99 '''
101 debugging = 0
102 host = ''
103 port = FTP_PORT
104 sock = None
105 file = None
106 welcome = None
107 passiveserver = 1
108 encoding = "latin1"
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
117 if host:
118 self.connect(host)
119 if user:
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)
127 if host != '':
128 self.host = host
129 if port > 0:
130 self.port = port
131 if timeout != -999:
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()
137 return self.welcome
139 def getwelcome(self):
140 '''Get the welcome message from the server.
141 (this is read and squirreled away by connect())'''
142 if self.debugging:
143 print('*welcome*', self.sanitize(self.welcome))
144 return 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 ':
164 i = len(s)
165 while i > 5 and s[i-1] in '\r\n':
166 i = i-1
167 s = s[:5] + '*'*(i-5) + s[i:]
168 return repr(s)
170 # Internal: send one line to the server, appending CRLF
171 def putline(self, line):
172 line = line + CRLF
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))
179 self.putline(line)
181 # Internal: return one line from the server, stripping CRLF.
182 # Raise EOFError if the connection is closed
183 def getline(self):
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]
190 return line
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()
198 if line[3:4] == '-':
199 code = line[:3]
200 while 1:
201 nextline = self.getline()
202 line = line + ('\n' + nextline)
203 if nextline[:3] == code and \
204 nextline[3:4] != '-':
205 break
206 return line
208 # Internal: get a response from the server.
209 # Raise various errors if the response indicates an error
210 def getresp(self):
211 resp = self.getmultiline()
212 if self.debugging: print('*resp*', self.sanitize(resp))
213 self.lastresp = resp[:3]
214 c = resp[:1]
215 if c in ('1', '2', '3'):
216 return resp
217 if c == '4':
218 raise error_temp(resp)
219 if c == '5':
220 raise error_perm(resp)
221 raise error_proto(resp)
223 def voidresp(self):
224 """Expect a response beginning with '2'."""
225 resp = self.getresp()
226 if resp[:1] != '2':
227 raise error_reply(resp)
228 return resp
230 def abort(self):
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.'''
235 line = 'ABOR' + CRLF
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.'''
244 self.putcmd(cmd)
245 return self.getresp()
247 def voidcmd(self, cmd):
248 """Send a command and expect a response beginning with '2'."""
249 self.putcmd(cmd)
250 return self.voidresp()
252 def sendport(self, host, port):
253 '''Send a PORT command with the current host and the given
254 port number.
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.'''
264 af = 0
265 if self.af == socket.AF_INET:
266 af = 1
267 if self.af == socket.AF_INET6:
268 af = 2
269 if af == 0:
270 raise error_proto('unsupported address family')
271 fields = ['', repr(af), host, repr(port), '']
272 cmd = 'EPRT ' + '|'.join(fields)
273 return self.voidcmd(cmd)
275 def makeport(self):
276 '''Create a new socket and send a PORT command for it.'''
277 msg = "getaddrinfo returns an empty list"
278 sock = None
279 for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
280 af, socktype, proto, canonname, sa = res
281 try:
282 sock = socket.socket(af, socktype, proto)
283 sock.bind(sa)
284 except socket.error as msg:
285 if sock:
286 sock.close()
287 sock = None
288 continue
289 break
290 if not sock:
291 raise socket.error(msg)
292 sock.listen(1)
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)
297 else:
298 resp = self.sendeprt(host, port)
299 if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
300 sock.settimeout(self.timeout)
301 return sock
303 def makepasv(self):
304 if self.af == socket.AF_INET:
305 host, port = parse227(self.sendcmd('PASV'))
306 else:
307 host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
308 return host, port
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
323 given marker.
325 size = None
326 if self.passiveserver:
327 host, port = self.makepasv()
328 conn = socket.create_connection((host, port), self.timeout)
329 if rest is not None:
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
337 # this response.
338 if resp[0] == '2':
339 resp = self.getresp()
340 if resp[0] != '1':
341 raise error_reply(resp)
342 else:
343 sock = self.makeport()
344 if rest is not None:
345 self.sendcmd("REST %s" % rest)
346 resp = self.sendcmd(cmd)
347 # See above.
348 if resp[0] == '2':
349 resp = self.getresp()
350 if resp[0] != '1':
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)
358 return conn, size
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,
376 # host or country.
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)
381 if resp[0] != '2':
382 raise error_reply(resp)
383 return resp
385 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
386 """Retrieve data in binary mode. A new port is created for you.
388 Args:
389 cmd: A RETR command.
390 callback: A single parameter callable to be called on each
391 block of data read.
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]
396 Returns:
397 The response code.
399 self.voidcmd('TYPE I')
400 conn = self.transfercmd(cmd, rest)
401 while 1:
402 data = conn.recv(blocksize)
403 if not data:
404 break
405 callback(data)
406 conn.close()
407 return self.voidresp()
409 def retrlines(self, cmd, callback = None):
410 """Retrieve data in line mode. A new port is created for you.
412 Args:
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()]
418 Returns:
419 The response code.
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)
425 while 1:
426 line = fp.readline()
427 if self.debugging > 2: print('*retr*', repr(line))
428 if not line:
429 break
430 if line[-2:] == CRLF:
431 line = line[:-2]
432 elif line[-1:] == '\n':
433 line = line[:-1]
434 callback(line)
435 fp.close()
436 conn.close()
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.
442 Args:
443 cmd: A STOR command.
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]
450 Returns:
451 The response code.
453 self.voidcmd('TYPE I')
454 conn = self.transfercmd(cmd)
455 while 1:
456 buf = fp.read(blocksize)
457 if not buf: break
458 conn.sendall(buf)
459 if callback: callback(buf)
460 conn.close()
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.
466 Args:
467 cmd: A STOR command.
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]
472 Returns:
473 The response code.
475 self.voidcmd('TYPE A')
476 conn = self.transfercmd(cmd)
477 while 1:
478 buf = fp.readline()
479 if not buf: break
480 if buf[-2:] != B_CRLF:
481 if buf[-1] in B_CRLF: buf = buf[:-1]
482 buf = buf + B_CRLF
483 conn.sendall(buf)
484 if callback: callback(buf)
485 conn.close()
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).'''
495 cmd = 'NLST'
496 for arg in args:
497 cmd = cmd + (' ' + arg)
498 files = []
499 self.retrlines(cmd, files.append)
500 return files
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.)'''
508 cmd = 'LIST'
509 func = None
510 if args[-1:] and type(args[-1]) != type(''):
511 args, func = args[:-1], args[-1]
512 for arg in args:
513 if arg:
514 cmd = cmd + (' ' + arg)
515 self.retrlines(cmd, func)
517 def rename(self, fromname, toname):
518 '''Rename a file.'''
519 resp = self.sendcmd('RNFR ' + fromname)
520 if resp[0] != '3':
521 raise error_reply(resp)
522 return self.voidcmd('RNTO ' + toname)
524 def delete(self, filename):
525 '''Delete a file.'''
526 resp = self.sendcmd('DELE ' + filename)
527 if resp[:3] in ('250', '200'):
528 return resp
529 else:
530 raise error_reply(resp)
532 def cwd(self, dirname):
533 '''Change to a directory.'''
534 if dirname == '..':
535 try:
536 return self.voidcmd('CDUP')
537 except error_perm as msg:
538 if msg.args[0][:3] != '500':
539 raise
540 elif dirname == '':
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':
550 s = resp[3:].strip()
551 try:
552 return int(s)
553 except (OverflowError, ValueError):
554 return int(s)
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)
565 def pwd(self):
566 '''Return current working directory.'''
567 resp = self.sendcmd('PWD')
568 return parse257(resp)
570 def quit(self):
571 '''Quit, and close the connection.'''
572 resp = self.voidcmd('QUIT')
573 self.close()
574 return resp
576 def close(self):
577 '''Close the connection without assuming anything about it.'''
578 if self.file:
579 self.file.close()
580 self.sock.close()
581 self.file = self.sock = None
584 _150_re = None
586 def parse150(resp):
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)
593 global _150_re
594 if _150_re is None:
595 import re
596 _150_re = re.compile(
597 "150 .* \((\d+) bytes\)", re.IGNORECASE | re.ASCII)
598 m = _150_re.match(resp)
599 if not m:
600 return None
601 s = m.group(1)
602 try:
603 return int(s)
604 except (OverflowError, ValueError):
605 return int(s)
608 _227_re = None
610 def parse227(resp):
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)
617 global _227_re
618 if _227_re is None:
619 import re
620 _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)', re.ASCII)
621 m = _227_re.search(resp)
622 if not m:
623 raise error_proto(resp)
624 numbers = m.groups()
625 host = '.'.join(numbers[:4])
626 port = (int(numbers[4]) << 8) + int(numbers[5])
627 return host, port
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)
640 if right < 0:
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])
645 if len(parts) != 5:
646 raise error_proto(resp)
647 host = peer[0]
648 port = int(parts[3])
649 return host, port
652 def parse257(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
661 dirname = ''
662 i = 5
663 n = len(resp)
664 while i < n:
665 c = resp[i]
666 i = i+1
667 if c == '"':
668 if i >= n or resp[i] != '"':
669 break
670 i = i+1
671 dirname = dirname + c
672 return dirname
675 def print_line(line):
676 '''Default retrlines callback to print a line.'''
677 print(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
684 source.voidcmd(type)
685 target.voidcmd(type)
686 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
687 target.sendport(sourcehost, sourceport)
688 # RFC 959: the user must "listen" [...] BEFORE sending the
689 # transfer request.
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
695 source.voidresp()
696 target.voidresp()
699 class Netrc:
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.
707 __defuser = None
708 __defpasswd = None
709 __defacct = None
711 def __init__(self, filename=None):
712 if filename is None:
713 if "HOME" in os.environ:
714 filename = os.path.join(os.environ["HOME"],
715 ".netrc")
716 else:
717 raise IOError("specify file to load or set $HOME")
718 self.__hosts = {}
719 self.__macros = {}
720 fp = open(filename, "r")
721 in_macro = 0
722 while 1:
723 line = fp.readline()
724 if not line: break
725 if in_macro and line.strip():
726 macro_lines.append(line)
727 continue
728 elif in_macro:
729 self.__macros[macro_name] = tuple(macro_lines)
730 in_macro = 0
731 words = line.split()
732 host = user = passwd = acct = None
733 default = 0
734 i = 0
735 while i < len(words):
736 w1 = words[i]
737 if i+1 < len(words):
738 w2 = words[i + 1]
739 else:
740 w2 = None
741 if w1 == 'default':
742 default = 1
743 elif w1 == 'machine' and w2:
744 host = w2.lower()
745 i = i + 1
746 elif w1 == 'login' and w2:
747 user = w2
748 i = i + 1
749 elif w1 == 'password' and w2:
750 passwd = w2
751 i = i + 1
752 elif w1 == 'account' and w2:
753 acct = w2
754 i = i + 1
755 elif w1 == 'macdef' and w2:
756 macro_name = w2
757 macro_lines = []
758 in_macro = 1
759 break
760 i = i + 1
761 if default:
762 self.__defuser = user or self.__defuser
763 self.__defpasswd = passwd or self.__defpasswd
764 self.__defacct = acct or self.__defacct
765 if host:
766 if host in self.__hosts:
767 ouser, opasswd, oacct = \
768 self.__hosts[host]
769 user = user or ouser
770 passwd = passwd or opasswd
771 acct = acct or oacct
772 self.__hosts[host] = user, passwd, acct
773 fp.close()
775 def get_hosts(self):
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.
786 host = host.lower()
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]
805 def test():
806 '''Test program.
807 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
809 -d dir
810 -l list
811 -p password
814 if len(sys.argv) < 2:
815 print(test.__doc__)
816 sys.exit(0)
818 debugging = 0
819 rcfile = None
820 while sys.argv[1] == '-d':
821 debugging = debugging+1
822 del sys.argv[1]
823 if sys.argv[1][:2] == '-r':
824 # get name of alternate ~/.netrc file:
825 rcfile = sys.argv[1][2:]
826 del sys.argv[1]
827 host = sys.argv[1]
828 ftp = FTP(host)
829 ftp.set_debuglevel(debugging)
830 userid = passwd = acct = ''
831 try:
832 netrc = Netrc(rcfile)
833 except IOError:
834 if rcfile is not None:
835 sys.stderr.write("Could not open account file"
836 " -- using anonymous login.")
837 else:
838 try:
839 userid, passwd, acct = netrc.get_account(host)
840 except KeyError:
841 # no account for host
842 sys.stderr.write(
843 "No account -- using anonymous login.")
844 ftp.login(userid, passwd, acct)
845 for file in sys.argv[2:]:
846 if file[:2] == '-l':
847 ftp.dir(file[2:])
848 elif file[:2] == '-d':
849 cmd = 'CWD'
850 if file[2:]: cmd = cmd + ' ' + file[2:]
851 resp = ftp.sendcmd(cmd)
852 elif file == '-p':
853 ftp.set_pasv(not ftp.passiveserver)
854 else:
855 ftp.retrbinary('RETR ' + file, \
856 sys.stdout.write, 1024)
857 ftp.quit()
860 if __name__ == '__main__':
861 test()