Issue #7264: Fix a possible deadlock when deallocating thread-local objects
[python.git] / Lib / ftplib.py
blob222e77d654d2ea1db8012bea9f5f4548e70cc132
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'
75 # The class itself
76 class FTP:
78 '''An FTP client class.
80 To create a connection, call the class using these arguments:
81 host, user, passwd, acct, timeout
83 The first four arguments are all strings, and have default value ''.
84 timeout must be numeric and defaults to None if not passed,
85 meaning that no timeout will be set on any ftp socket(s)
86 If a timeout is passed, then this is now the default timeout for all ftp
87 socket operations for this instance.
89 Then use self.connect() with optional host and port argument.
91 To download a file, use ftp.retrlines('RETR ' + filename),
92 or ftp.retrbinary() with slightly different arguments.
93 To upload a file, use ftp.storlines() or ftp.storbinary(),
94 which have an open file as argument (see their definitions
95 below for details).
96 The download/upload functions first issue appropriate TYPE
97 and PORT or PASV commands.
98 '''
100 debugging = 0
101 host = ''
102 port = FTP_PORT
103 sock = None
104 file = None
105 welcome = None
106 passiveserver = 1
108 # Initialization method (called by class instantiation).
109 # Initialize host to localhost, port to standard ftp port
110 # Optional arguments are host (for connect()),
111 # and user, passwd, acct (for login())
112 def __init__(self, host='', user='', passwd='', acct='',
113 timeout=_GLOBAL_DEFAULT_TIMEOUT):
114 self.timeout = timeout
115 if host:
116 self.connect(host)
117 if user:
118 self.login(user, passwd, acct)
120 def connect(self, host='', port=0, timeout=-999):
121 '''Connect to host. Arguments are:
122 - host: hostname to connect to (string, default previous host)
123 - port: port to connect to (integer, default previous port)
125 if host != '':
126 self.host = host
127 if port > 0:
128 self.port = port
129 if timeout != -999:
130 self.timeout = timeout
131 self.sock = socket.create_connection((self.host, self.port), self.timeout)
132 self.af = self.sock.family
133 self.file = self.sock.makefile('rb')
134 self.welcome = self.getresp()
135 return self.welcome
137 def getwelcome(self):
138 '''Get the welcome message from the server.
139 (this is read and squirreled away by connect())'''
140 if self.debugging:
141 print '*welcome*', self.sanitize(self.welcome)
142 return self.welcome
144 def set_debuglevel(self, level):
145 '''Set the debugging level.
146 The required argument level means:
147 0: no debugging output (default)
148 1: print commands and responses but not body text etc.
149 2: also print raw lines read and sent before stripping CR/LF'''
150 self.debugging = level
151 debug = set_debuglevel
153 def set_pasv(self, val):
154 '''Use passive or active mode for data transfers.
155 With a false argument, use the normal PORT mode,
156 With a true argument, use the PASV command.'''
157 self.passiveserver = val
159 # Internal: "sanitize" a string for printing
160 def sanitize(self, s):
161 if s[:5] == 'pass ' or s[:5] == 'PASS ':
162 i = len(s)
163 while i > 5 and s[i-1] in '\r\n':
164 i = i-1
165 s = s[:5] + '*'*(i-5) + s[i:]
166 return repr(s)
168 # Internal: send one line to the server, appending CRLF
169 def putline(self, line):
170 line = line + CRLF
171 if self.debugging > 1: print '*put*', self.sanitize(line)
172 self.sock.sendall(line)
174 # Internal: send one command to the server (through putline())
175 def putcmd(self, line):
176 if self.debugging: print '*cmd*', self.sanitize(line)
177 self.putline(line)
179 # Internal: return one line from the server, stripping CRLF.
180 # Raise EOFError if the connection is closed
181 def getline(self):
182 line = self.file.readline()
183 if self.debugging > 1:
184 print '*get*', self.sanitize(line)
185 if not line: raise EOFError
186 if line[-2:] == CRLF: line = line[:-2]
187 elif line[-1:] in CRLF: line = line[:-1]
188 return line
190 # Internal: get a response from the server, which may possibly
191 # consist of multiple lines. Return a single string with no
192 # trailing CRLF. If the response consists of multiple lines,
193 # these are separated by '\n' characters in the string
194 def getmultiline(self):
195 line = self.getline()
196 if line[3:4] == '-':
197 code = line[:3]
198 while 1:
199 nextline = self.getline()
200 line = line + ('\n' + nextline)
201 if nextline[:3] == code and \
202 nextline[3:4] != '-':
203 break
204 return line
206 # Internal: get a response from the server.
207 # Raise various errors if the response indicates an error
208 def getresp(self):
209 resp = self.getmultiline()
210 if self.debugging: print '*resp*', self.sanitize(resp)
211 self.lastresp = resp[:3]
212 c = resp[:1]
213 if c in ('1', '2', '3'):
214 return resp
215 if c == '4':
216 raise error_temp, resp
217 if c == '5':
218 raise error_perm, resp
219 raise error_proto, resp
221 def voidresp(self):
222 """Expect a response beginning with '2'."""
223 resp = self.getresp()
224 if resp[:1] != '2':
225 raise error_reply, resp
226 return resp
228 def abort(self):
229 '''Abort a file transfer. Uses out-of-band data.
230 This does not follow the procedure from the RFC to send Telnet
231 IP and Synch; that doesn't seem to work with the servers I've
232 tried. Instead, just send the ABOR command as OOB data.'''
233 line = 'ABOR' + CRLF
234 if self.debugging > 1: print '*put urgent*', self.sanitize(line)
235 self.sock.sendall(line, MSG_OOB)
236 resp = self.getmultiline()
237 if resp[:3] not in ('426', '226'):
238 raise error_proto, resp
240 def sendcmd(self, cmd):
241 '''Send a command and return the response.'''
242 self.putcmd(cmd)
243 return self.getresp()
245 def voidcmd(self, cmd):
246 """Send a command and expect a response beginning with '2'."""
247 self.putcmd(cmd)
248 return self.voidresp()
250 def sendport(self, host, port):
251 '''Send a PORT command with the current host and the given
252 port number.
254 hbytes = host.split('.')
255 pbytes = [repr(port//256), repr(port%256)]
256 bytes = hbytes + pbytes
257 cmd = 'PORT ' + ','.join(bytes)
258 return self.voidcmd(cmd)
260 def sendeprt(self, host, port):
261 '''Send a EPRT command with the current host and the given port number.'''
262 af = 0
263 if self.af == socket.AF_INET:
264 af = 1
265 if self.af == socket.AF_INET6:
266 af = 2
267 if af == 0:
268 raise error_proto, 'unsupported address family'
269 fields = ['', repr(af), host, repr(port), '']
270 cmd = 'EPRT ' + '|'.join(fields)
271 return self.voidcmd(cmd)
273 def makeport(self):
274 '''Create a new socket and send a PORT command for it.'''
275 msg = "getaddrinfo returns an empty list"
276 sock = None
277 for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
278 af, socktype, proto, canonname, sa = res
279 try:
280 sock = socket.socket(af, socktype, proto)
281 sock.bind(sa)
282 except socket.error, msg:
283 if sock:
284 sock.close()
285 sock = None
286 continue
287 break
288 if not sock:
289 raise socket.error, msg
290 sock.listen(1)
291 port = sock.getsockname()[1] # Get proper port
292 host = self.sock.getsockname()[0] # Get proper host
293 if self.af == socket.AF_INET:
294 resp = self.sendport(host, port)
295 else:
296 resp = self.sendeprt(host, port)
297 return sock
299 def makepasv(self):
300 if self.af == socket.AF_INET:
301 host, port = parse227(self.sendcmd('PASV'))
302 else:
303 host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
304 return host, port
306 def ntransfercmd(self, cmd, rest=None):
307 """Initiate a transfer over the data connection.
309 If the transfer is active, send a port command and the
310 transfer command, and accept the connection. If the server is
311 passive, send a pasv command, connect to it, and start the
312 transfer command. Either way, return the socket for the
313 connection and the expected size of the transfer. The
314 expected size may be None if it could not be determined.
316 Optional `rest' argument can be a string that is sent as the
317 argument to a REST command. This is essentially a server
318 marker used to tell the server to skip over any data up to the
319 given marker.
321 size = None
322 if self.passiveserver:
323 host, port = self.makepasv()
324 conn = socket.create_connection((host, port), self.timeout)
325 if rest is not None:
326 self.sendcmd("REST %s" % rest)
327 resp = self.sendcmd(cmd)
328 # Some servers apparently send a 200 reply to
329 # a LIST or STOR command, before the 150 reply
330 # (and way before the 226 reply). This seems to
331 # be in violation of the protocol (which only allows
332 # 1xx or error messages for LIST), so we just discard
333 # this response.
334 if resp[0] == '2':
335 resp = self.getresp()
336 if resp[0] != '1':
337 raise error_reply, resp
338 else:
339 sock = self.makeport()
340 if rest is not None:
341 self.sendcmd("REST %s" % rest)
342 resp = self.sendcmd(cmd)
343 # See above.
344 if resp[0] == '2':
345 resp = self.getresp()
346 if resp[0] != '1':
347 raise error_reply, resp
348 conn, sockaddr = sock.accept()
349 if resp[:3] == '150':
350 # this is conditional in case we received a 125
351 size = parse150(resp)
352 return conn, size
354 def transfercmd(self, cmd, rest=None):
355 """Like ntransfercmd() but returns only the socket."""
356 return self.ntransfercmd(cmd, rest)[0]
358 def login(self, user = '', passwd = '', acct = ''):
359 '''Login, default anonymous.'''
360 if not user: user = 'anonymous'
361 if not passwd: passwd = ''
362 if not acct: acct = ''
363 if user == 'anonymous' and passwd in ('', '-'):
364 # If there is no anonymous ftp password specified
365 # then we'll just use anonymous@
366 # We don't send any other thing because:
367 # - We want to remain anonymous
368 # - We want to stop SPAM
369 # - We don't want to let ftp sites to discriminate by the user,
370 # host or country.
371 passwd = passwd + 'anonymous@'
372 resp = self.sendcmd('USER ' + user)
373 if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
374 if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
375 if resp[0] != '2':
376 raise error_reply, resp
377 return resp
379 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
380 """Retrieve data in binary mode. A new port is created for you.
382 Args:
383 cmd: A RETR command.
384 callback: A single parameter callable to be called on each
385 block of data read.
386 blocksize: The maximum number of bytes to read from the
387 socket at one time. [default: 8192]
388 rest: Passed to transfercmd(). [default: None]
390 Returns:
391 The response code.
393 self.voidcmd('TYPE I')
394 conn = self.transfercmd(cmd, rest)
395 while 1:
396 data = conn.recv(blocksize)
397 if not data:
398 break
399 callback(data)
400 conn.close()
401 return self.voidresp()
403 def retrlines(self, cmd, callback = None):
404 """Retrieve data in line mode. A new port is created for you.
406 Args:
407 cmd: A RETR, LIST, NLST, or MLSD command.
408 callback: An optional single parameter callable that is called
409 for each line with the trailing CRLF stripped.
410 [default: print_line()]
412 Returns:
413 The response code.
415 if callback is None: callback = print_line
416 resp = self.sendcmd('TYPE A')
417 conn = self.transfercmd(cmd)
418 fp = conn.makefile('rb')
419 while 1:
420 line = fp.readline()
421 if self.debugging > 2: print '*retr*', repr(line)
422 if not line:
423 break
424 if line[-2:] == CRLF:
425 line = line[:-2]
426 elif line[-1:] == '\n':
427 line = line[:-1]
428 callback(line)
429 fp.close()
430 conn.close()
431 return self.voidresp()
433 def storbinary(self, cmd, fp, blocksize=8192, callback=None):
434 """Store a file in binary mode. A new port is created for you.
436 Args:
437 cmd: A STOR command.
438 fp: A file-like object with a read(num_bytes) method.
439 blocksize: The maximum data size to read from fp and send over
440 the connection at once. [default: 8192]
441 callback: An optional single parameter callable that is called on
442 on each block of data after it is sent. [default: None]
444 Returns:
445 The response code.
447 self.voidcmd('TYPE I')
448 conn = self.transfercmd(cmd)
449 while 1:
450 buf = fp.read(blocksize)
451 if not buf: break
452 conn.sendall(buf)
453 if callback: callback(buf)
454 conn.close()
455 return self.voidresp()
457 def storlines(self, cmd, fp, callback=None):
458 """Store a file in line mode. A new port is created for you.
460 Args:
461 cmd: A STOR command.
462 fp: A file-like object with a readline() method.
463 callback: An optional single parameter callable that is called on
464 on each line after it is sent. [default: None]
466 Returns:
467 The response code.
469 self.voidcmd('TYPE A')
470 conn = self.transfercmd(cmd)
471 while 1:
472 buf = fp.readline()
473 if not buf: break
474 if buf[-2:] != CRLF:
475 if buf[-1] in CRLF: buf = buf[:-1]
476 buf = buf + CRLF
477 conn.sendall(buf)
478 if callback: callback(buf)
479 conn.close()
480 return self.voidresp()
482 def acct(self, password):
483 '''Send new account name.'''
484 cmd = 'ACCT ' + password
485 return self.voidcmd(cmd)
487 def nlst(self, *args):
488 '''Return a list of files in a given directory (default the current).'''
489 cmd = 'NLST'
490 for arg in args:
491 cmd = cmd + (' ' + arg)
492 files = []
493 self.retrlines(cmd, files.append)
494 return files
496 def dir(self, *args):
497 '''List a directory in long form.
498 By default list current directory to stdout.
499 Optional last argument is callback function; all
500 non-empty arguments before it are concatenated to the
501 LIST command. (This *should* only be used for a pathname.)'''
502 cmd = 'LIST'
503 func = None
504 if args[-1:] and type(args[-1]) != type(''):
505 args, func = args[:-1], args[-1]
506 for arg in args:
507 if arg:
508 cmd = cmd + (' ' + arg)
509 self.retrlines(cmd, func)
511 def rename(self, fromname, toname):
512 '''Rename a file.'''
513 resp = self.sendcmd('RNFR ' + fromname)
514 if resp[0] != '3':
515 raise error_reply, resp
516 return self.voidcmd('RNTO ' + toname)
518 def delete(self, filename):
519 '''Delete a file.'''
520 resp = self.sendcmd('DELE ' + filename)
521 if resp[:3] in ('250', '200'):
522 return resp
523 else:
524 raise error_reply, resp
526 def cwd(self, dirname):
527 '''Change to a directory.'''
528 if dirname == '..':
529 try:
530 return self.voidcmd('CDUP')
531 except error_perm, msg:
532 if msg.args[0][:3] != '500':
533 raise
534 elif dirname == '':
535 dirname = '.' # does nothing, but could return error
536 cmd = 'CWD ' + dirname
537 return self.voidcmd(cmd)
539 def size(self, filename):
540 '''Retrieve the size of a file.'''
541 # The SIZE command is defined in RFC-3659
542 resp = self.sendcmd('SIZE ' + filename)
543 if resp[:3] == '213':
544 s = resp[3:].strip()
545 try:
546 return int(s)
547 except (OverflowError, ValueError):
548 return long(s)
550 def mkd(self, dirname):
551 '''Make a directory, return its full pathname.'''
552 resp = self.sendcmd('MKD ' + dirname)
553 return parse257(resp)
555 def rmd(self, dirname):
556 '''Remove a directory.'''
557 return self.voidcmd('RMD ' + dirname)
559 def pwd(self):
560 '''Return current working directory.'''
561 resp = self.sendcmd('PWD')
562 return parse257(resp)
564 def quit(self):
565 '''Quit, and close the connection.'''
566 resp = self.voidcmd('QUIT')
567 self.close()
568 return resp
570 def close(self):
571 '''Close the connection without assuming anything about it.'''
572 if self.file:
573 self.file.close()
574 self.sock.close()
575 self.file = self.sock = None
578 _150_re = None
580 def parse150(resp):
581 '''Parse the '150' response for a RETR request.
582 Returns the expected transfer size or None; size is not guaranteed to
583 be present in the 150 message.
585 if resp[:3] != '150':
586 raise error_reply, resp
587 global _150_re
588 if _150_re is None:
589 import re
590 _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE)
591 m = _150_re.match(resp)
592 if not m:
593 return None
594 s = m.group(1)
595 try:
596 return int(s)
597 except (OverflowError, ValueError):
598 return long(s)
601 _227_re = None
603 def parse227(resp):
604 '''Parse the '227' response for a PASV request.
605 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
606 Return ('host.addr.as.numbers', port#) tuple.'''
608 if resp[:3] != '227':
609 raise error_reply, resp
610 global _227_re
611 if _227_re is None:
612 import re
613 _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
614 m = _227_re.search(resp)
615 if not m:
616 raise error_proto, resp
617 numbers = m.groups()
618 host = '.'.join(numbers[:4])
619 port = (int(numbers[4]) << 8) + int(numbers[5])
620 return host, port
623 def parse229(resp, peer):
624 '''Parse the '229' response for a EPSV request.
625 Raises error_proto if it does not contain '(|||port|)'
626 Return ('host.addr.as.numbers', port#) tuple.'''
628 if resp[:3] != '229':
629 raise error_reply, resp
630 left = resp.find('(')
631 if left < 0: raise error_proto, resp
632 right = resp.find(')', left + 1)
633 if right < 0:
634 raise error_proto, resp # should contain '(|||port|)'
635 if resp[left + 1] != resp[right - 1]:
636 raise error_proto, resp
637 parts = resp[left + 1:right].split(resp[left+1])
638 if len(parts) != 5:
639 raise error_proto, resp
640 host = peer[0]
641 port = int(parts[3])
642 return host, port
645 def parse257(resp):
646 '''Parse the '257' response for a MKD or PWD request.
647 This is a response to a MKD or PWD request: a directory name.
648 Returns the directoryname in the 257 reply.'''
650 if resp[:3] != '257':
651 raise error_reply, resp
652 if resp[3:5] != ' "':
653 return '' # Not compliant to RFC 959, but UNIX ftpd does this
654 dirname = ''
655 i = 5
656 n = len(resp)
657 while i < n:
658 c = resp[i]
659 i = i+1
660 if c == '"':
661 if i >= n or resp[i] != '"':
662 break
663 i = i+1
664 dirname = dirname + c
665 return dirname
668 def print_line(line):
669 '''Default retrlines callback to print a line.'''
670 print line
673 def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
674 '''Copy file from one FTP-instance to another.'''
675 if not targetname: targetname = sourcename
676 type = 'TYPE ' + type
677 source.voidcmd(type)
678 target.voidcmd(type)
679 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
680 target.sendport(sourcehost, sourceport)
681 # RFC 959: the user must "listen" [...] BEFORE sending the
682 # transfer request.
683 # So: STOR before RETR, because here the target is a "user".
684 treply = target.sendcmd('STOR ' + targetname)
685 if treply[:3] not in ('125', '150'): raise error_proto # RFC 959
686 sreply = source.sendcmd('RETR ' + sourcename)
687 if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959
688 source.voidresp()
689 target.voidresp()
692 class Netrc:
693 """Class to parse & provide access to 'netrc' format files.
695 See the netrc(4) man page for information on the file format.
697 WARNING: This class is obsolete -- use module netrc instead.
700 __defuser = None
701 __defpasswd = None
702 __defacct = None
704 def __init__(self, filename=None):
705 if filename is None:
706 if "HOME" in os.environ:
707 filename = os.path.join(os.environ["HOME"],
708 ".netrc")
709 else:
710 raise IOError, \
711 "specify file to load or set $HOME"
712 self.__hosts = {}
713 self.__macros = {}
714 fp = open(filename, "r")
715 in_macro = 0
716 while 1:
717 line = fp.readline()
718 if not line: break
719 if in_macro and line.strip():
720 macro_lines.append(line)
721 continue
722 elif in_macro:
723 self.__macros[macro_name] = tuple(macro_lines)
724 in_macro = 0
725 words = line.split()
726 host = user = passwd = acct = None
727 default = 0
728 i = 0
729 while i < len(words):
730 w1 = words[i]
731 if i+1 < len(words):
732 w2 = words[i + 1]
733 else:
734 w2 = None
735 if w1 == 'default':
736 default = 1
737 elif w1 == 'machine' and w2:
738 host = w2.lower()
739 i = i + 1
740 elif w1 == 'login' and w2:
741 user = w2
742 i = i + 1
743 elif w1 == 'password' and w2:
744 passwd = w2
745 i = i + 1
746 elif w1 == 'account' and w2:
747 acct = w2
748 i = i + 1
749 elif w1 == 'macdef' and w2:
750 macro_name = w2
751 macro_lines = []
752 in_macro = 1
753 break
754 i = i + 1
755 if default:
756 self.__defuser = user or self.__defuser
757 self.__defpasswd = passwd or self.__defpasswd
758 self.__defacct = acct or self.__defacct
759 if host:
760 if host in self.__hosts:
761 ouser, opasswd, oacct = \
762 self.__hosts[host]
763 user = user or ouser
764 passwd = passwd or opasswd
765 acct = acct or oacct
766 self.__hosts[host] = user, passwd, acct
767 fp.close()
769 def get_hosts(self):
770 """Return a list of hosts mentioned in the .netrc file."""
771 return self.__hosts.keys()
773 def get_account(self, host):
774 """Returns login information for the named host.
776 The return value is a triple containing userid,
777 password, and the accounting field.
780 host = host.lower()
781 user = passwd = acct = None
782 if host in self.__hosts:
783 user, passwd, acct = self.__hosts[host]
784 user = user or self.__defuser
785 passwd = passwd or self.__defpasswd
786 acct = acct or self.__defacct
787 return user, passwd, acct
789 def get_macros(self):
790 """Return a list of all defined macro names."""
791 return self.__macros.keys()
793 def get_macro(self, macro):
794 """Return a sequence of lines which define a named macro."""
795 return self.__macros[macro]
799 def test():
800 '''Test program.
801 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
803 -d dir
804 -l list
805 -p password
808 if len(sys.argv) < 2:
809 print test.__doc__
810 sys.exit(0)
812 debugging = 0
813 rcfile = None
814 while sys.argv[1] == '-d':
815 debugging = debugging+1
816 del sys.argv[1]
817 if sys.argv[1][:2] == '-r':
818 # get name of alternate ~/.netrc file:
819 rcfile = sys.argv[1][2:]
820 del sys.argv[1]
821 host = sys.argv[1]
822 ftp = FTP(host)
823 ftp.set_debuglevel(debugging)
824 userid = passwd = acct = ''
825 try:
826 netrc = Netrc(rcfile)
827 except IOError:
828 if rcfile is not None:
829 sys.stderr.write("Could not open account file"
830 " -- using anonymous login.")
831 else:
832 try:
833 userid, passwd, acct = netrc.get_account(host)
834 except KeyError:
835 # no account for host
836 sys.stderr.write(
837 "No account -- using anonymous login.")
838 ftp.login(userid, passwd, acct)
839 for file in sys.argv[2:]:
840 if file[:2] == '-l':
841 ftp.dir(file[2:])
842 elif file[:2] == '-d':
843 cmd = 'CWD'
844 if file[2:]: cmd = cmd + ' ' + file[2:]
845 resp = ftp.sendcmd(cmd)
846 elif file == '-p':
847 ftp.set_pasv(not ftp.passiveserver)
848 else:
849 ftp.retrbinary('RETR ' + file, \
850 sys.stdout.write, 1024)
851 ftp.quit()
854 if __name__ == '__main__':
855 test()