Vega strike Python Modules doc  0.5.1
Documentation of the " Modules " folder of Vega strike
 All Data Structures Namespaces Files Functions Variables
ftplib.py
Go to the documentation of this file.
1 """An FTP client class and some helper functions.
2 
3 Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds
4 
5 Example:
6 
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 >>>
26 
27 A nice test that reveals some of the network dialogue would be:
28 python ftplib.py -d localhost -l -p -l
29 """
30 
31 #
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 #
36 
37 import os
38 import sys
39 import string
40 
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 
48 __all__ = ["FTP","Netrc"]
49 
50 # Magic number from <socket.h>
51 MSG_OOB = 0x1 # Process data out of band
52 
53 
54 # The standard FTP server control port
55 FTP_PORT = 21
56 
57 
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]
64 
65 
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)
69 
70 
71 # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
72 CRLF = '\r\n'
73 
74 
75 # The class itself
76 class FTP:
77 
78  '''An FTP client class.
79 
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.
84 
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 '''
93 
94  debugging = 0
95  host = ''
96  port = FTP_PORT
97  sock = None
98  file = None
99  welcome = None
100  passiveserver = 1
101 
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)
110 
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
135 
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
142 
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
151 
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
157 
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`
166 
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)
172 
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)
177 
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
188 
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
204 
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
219 
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
226 
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
238 
239  def sendcmd(self, cmd):
240  '''Send a command and return the response.'''
241  self.putcmd(cmd)
242  return self.getresp()
243 
244  def voidcmd(self, cmd):
245  """Send a command and expect a response beginning with '2'."""
246  self.putcmd(cmd)
247  return self.voidresp()
248 
249  def sendport(self, host, port):
250  '''Send a PORT command with the current host and the given
251  port number.
252  '''
253  hbytes = host.split('.')
254  pbytes = [`port/256`, `port%256`]
255  bytes = hbytes + pbytes
256  cmd = 'PORT ' + ','.join(bytes)
257  return self.voidcmd(cmd)
258 
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)
271 
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
297 
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
304 
305  def ntransfercmd(self, cmd, rest=None):
306  """Initiate a transfer over the data connection.
307 
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.
314 
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.
319  """
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
343 
344  def transfercmd(self, cmd, rest=None):
345  """Like ntransfercmd() but returns only the socket."""
346  return self.ntransfercmd(cmd, rest)[0]
347 
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
373 
374  def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
375  """Retrieve data in binary mode.
376 
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().
381 
382  A new port is created for you. Return the response code.
383  """
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()
393 
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()
417 
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()
428 
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()
442 
443  def acct(self, password):
444  '''Send new account name.'''
445  cmd = 'ACCT ' + password
446  return self.voidcmd(cmd)
447 
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
456 
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)
471 
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)
478 
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
488 
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)
501 
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)
512 
513  def mkd(self, dirname):
514  '''Make a directory, return its full pathname.'''
515  resp = self.sendcmd('MKD ' + dirname)
516  return parse257(resp)
517 
518  def rmd(self, dirname):
519  '''Remove a directory.'''
520  return self.voidcmd('RMD ' + dirname)
521 
522  def pwd(self):
523  '''Return current working directory.'''
524  resp = self.sendcmd('PWD')
525  return parse257(resp)
526 
527  def quit(self):
528  '''Quit, and close the connection.'''
529  resp = self.voidcmd('QUIT')
530  self.close()
531  return resp
532 
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
539 
540 
541 _150_re = None
542 
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.
547  '''
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)
562 
563 
564 _227_re = None
565 
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.'''
570 
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
584 
585 
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.'''
590 
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
606 
607 
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.'''
612 
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
629 
630 
631 def print_line(line):
632  '''Default retrlines callback to print a line.'''
633  print line
634 
635 
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()
653 
654 
655 class Netrc:
656  """Class to parse & provide access to 'netrc' format files.
657 
658  See the netrc(4) man page for information on the file format.
659 
660  WARNING: This class is obsolete -- use module netrc instead.
661 
662  """
663  __defuser = None
664  __defpasswd = None
665  __defacct = None
666 
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()
731 
732  def get_hosts(self):
733  """Return a list of hosts mentioned in the .netrc file."""
734  return self.__hosts.keys()
735 
736  def get_account(self, host):
737  """Returns login information for the named host.
738 
739  The return value is a triple containing userid,
740  password, and the accounting field.
741 
742  """
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
751 
752  def get_macros(self):
753  """Return a list of all defined macro names."""
754  return self.__macros.keys()
755 
756  def get_macro(self, macro):
757  """Return a sequence of lines which define a named macro."""
758  return self.__macros[macro]
759 
760 
761 
762 def test():
763  '''Test program.
764  Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...'''
765 
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()
806 
807 
808 if __name__ == '__main__':
809  test()