source: python/trunk/Lib/ftplib.py@ 606

Last change on this file since 606 was 391, checked in by dmik, 11 years ago

python: Merge vendor 2.7.6 to trunk.

  • Property svn:eol-style set to native
File size: 36.1 KB
Line 
1"""An FTP client class and some helper functions.
2
3Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds
4
5Example:
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 anonymous@
10'230 Guest login ok, access restrictions apply.'
11>>> ftp.retrlines('LIST') # list directory contents
12total 9
13drwxr-xr-x 8 root wheel 1024 Jan 3 1994 .
14drwxr-xr-x 8 root wheel 1024 Jan 3 1994 ..
15drwxr-xr-x 2 root wheel 1024 Jan 3 1994 bin
16drwxr-xr-x 2 root wheel 1024 Jan 3 1994 etc
17d-wxrwxr-x 2 ftp wheel 1024 Sep 5 13:43 incoming
18drwxr-xr-x 2 root wheel 1024 Nov 17 1993 lib
19drwxr-xr-x 6 1094 wheel 1024 Sep 13 19:07 pub
20drwxr-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
27A nice test that reveals some of the network dialogue would be:
28python 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# Modified by Phil Schwartz to add storbinary and storlines callbacks.
36# Modified by Giampaolo Rodola' to add TLS support.
37#
38
39import os
40import sys
41
42# Import SOCKS module if it exists, else standard socket module socket
43try:
44 import SOCKS; socket = SOCKS; del SOCKS # import SOCKS as socket
45 from socket import getfqdn; socket.getfqdn = getfqdn; del getfqdn
46except ImportError:
47 import socket
48from socket import _GLOBAL_DEFAULT_TIMEOUT
49
50__all__ = ["FTP","Netrc"]
51
52# Magic number from <socket.h>
53MSG_OOB = 0x1 # Process data out of band
54
55
56# The standard FTP server control port
57FTP_PORT = 21
58# The sizehint parameter passed to readline() calls
59MAXLINE = 8192
60
61
62# Exception raised when an error or invalid response is received
63class Error(Exception): pass
64class error_reply(Error): pass # unexpected [123]xx reply
65class error_temp(Error): pass # 4xx errors
66class error_perm(Error): pass # 5xx errors
67class error_proto(Error): pass # response does not begin with [1-5]
68
69
70# All exceptions (hopefully) that may be raised here and that aren't
71# (always) programming errors on our side
72all_errors = (Error, IOError, EOFError)
73
74
75# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
76CRLF = '\r\n'
77
78# The class itself
79class FTP:
80
81 '''An FTP client class.
82
83 To create a connection, call the class using these arguments:
84 host, user, passwd, acct, timeout
85
86 The first four arguments are all strings, and have default value ''.
87 timeout must be numeric and defaults to None if not passed,
88 meaning that no timeout will be set on any ftp socket(s)
89 If a timeout is passed, then this is now the default timeout for all ftp
90 socket operations for this instance.
91
92 Then use self.connect() with optional host and port argument.
93
94 To download a file, use ftp.retrlines('RETR ' + filename),
95 or ftp.retrbinary() with slightly different arguments.
96 To upload a file, use ftp.storlines() or ftp.storbinary(),
97 which have an open file as argument (see their definitions
98 below for details).
99 The download/upload functions first issue appropriate TYPE
100 and PORT or PASV commands.
101'''
102
103 debugging = 0
104 host = ''
105 port = FTP_PORT
106 maxline = MAXLINE
107 sock = None
108 file = None
109 welcome = None
110 passiveserver = 1
111
112 # Initialization method (called by class instantiation).
113 # Initialize host to localhost, port to standard ftp port
114 # Optional arguments are host (for connect()),
115 # and user, passwd, acct (for login())
116 def __init__(self, host='', user='', passwd='', acct='',
117 timeout=_GLOBAL_DEFAULT_TIMEOUT):
118 self.timeout = timeout
119 if host:
120 self.connect(host)
121 if user:
122 self.login(user, passwd, acct)
123
124 def connect(self, host='', port=0, timeout=-999):
125 '''Connect to host. Arguments are:
126 - host: hostname to connect to (string, default previous host)
127 - port: port to connect to (integer, default previous port)
128 '''
129 if host != '':
130 self.host = host
131 if port > 0:
132 self.port = port
133 if timeout != -999:
134 self.timeout = timeout
135 self.sock = socket.create_connection((self.host, self.port), self.timeout)
136 self.af = self.sock.family
137 self.file = self.sock.makefile('rb')
138 self.welcome = self.getresp()
139 return self.welcome
140
141 def getwelcome(self):
142 '''Get the welcome message from the server.
143 (this is read and squirreled away by connect())'''
144 if self.debugging:
145 print '*welcome*', self.sanitize(self.welcome)
146 return self.welcome
147
148 def set_debuglevel(self, level):
149 '''Set the debugging level.
150 The required argument level means:
151 0: no debugging output (default)
152 1: print commands and responses but not body text etc.
153 2: also print raw lines read and sent before stripping CR/LF'''
154 self.debugging = level
155 debug = set_debuglevel
156
157 def set_pasv(self, val):
158 '''Use passive or active mode for data transfers.
159 With a false argument, use the normal PORT mode,
160 With a true argument, use the PASV command.'''
161 self.passiveserver = val
162
163 # Internal: "sanitize" a string for printing
164 def sanitize(self, s):
165 if s[:5] == 'pass ' or s[:5] == 'PASS ':
166 i = len(s)
167 while i > 5 and s[i-1] in '\r\n':
168 i = i-1
169 s = s[:5] + '*'*(i-5) + s[i:]
170 return repr(s)
171
172 # Internal: send one line to the server, appending CRLF
173 def putline(self, line):
174 line = line + CRLF
175 if self.debugging > 1: print '*put*', self.sanitize(line)
176 self.sock.sendall(line)
177
178 # Internal: send one command to the server (through putline())
179 def putcmd(self, line):
180 if self.debugging: print '*cmd*', self.sanitize(line)
181 self.putline(line)
182
183 # Internal: return one line from the server, stripping CRLF.
184 # Raise EOFError if the connection is closed
185 def getline(self):
186 line = self.file.readline(self.maxline + 1)
187 if len(line) > self.maxline:
188 raise Error("got more than %d bytes" % self.maxline)
189 if self.debugging > 1:
190 print '*get*', self.sanitize(line)
191 if not line: raise EOFError
192 if line[-2:] == CRLF: line = line[:-2]
193 elif line[-1:] in CRLF: line = line[:-1]
194 return line
195
196 # Internal: get a response from the server, which may possibly
197 # consist of multiple lines. Return a single string with no
198 # trailing CRLF. If the response consists of multiple lines,
199 # these are separated by '\n' characters in the string
200 def getmultiline(self):
201 line = self.getline()
202 if line[3:4] == '-':
203 code = line[:3]
204 while 1:
205 nextline = self.getline()
206 line = line + ('\n' + nextline)
207 if nextline[:3] == code and \
208 nextline[3:4] != '-':
209 break
210 return line
211
212 # Internal: get a response from the server.
213 # Raise various errors if the response indicates an error
214 def getresp(self):
215 resp = self.getmultiline()
216 if self.debugging: print '*resp*', self.sanitize(resp)
217 self.lastresp = resp[:3]
218 c = resp[:1]
219 if c in ('1', '2', '3'):
220 return resp
221 if c == '4':
222 raise error_temp, resp
223 if c == '5':
224 raise error_perm, resp
225 raise error_proto, resp
226
227 def voidresp(self):
228 """Expect a response beginning with '2'."""
229 resp = self.getresp()
230 if resp[:1] != '2':
231 raise error_reply, resp
232 return resp
233
234 def abort(self):
235 '''Abort a file transfer. Uses out-of-band data.
236 This does not follow the procedure from the RFC to send Telnet
237 IP and Synch; that doesn't seem to work with the servers I've
238 tried. Instead, just send the ABOR command as OOB data.'''
239 line = 'ABOR' + CRLF
240 if self.debugging > 1: print '*put urgent*', self.sanitize(line)
241 self.sock.sendall(line, MSG_OOB)
242 resp = self.getmultiline()
243 if resp[:3] not in ('426', '225', '226'):
244 raise error_proto, resp
245
246 def sendcmd(self, cmd):
247 '''Send a command and return the response.'''
248 self.putcmd(cmd)
249 return self.getresp()
250
251 def voidcmd(self, cmd):
252 """Send a command and expect a response beginning with '2'."""
253 self.putcmd(cmd)
254 return self.voidresp()
255
256 def sendport(self, host, port):
257 '''Send a PORT command with the current host and the given
258 port number.
259 '''
260 hbytes = host.split('.')
261 pbytes = [repr(port//256), repr(port%256)]
262 bytes = hbytes + pbytes
263 cmd = 'PORT ' + ','.join(bytes)
264 return self.voidcmd(cmd)
265
266 def sendeprt(self, host, port):
267 '''Send a EPRT command with the current host and the given port number.'''
268 af = 0
269 if self.af == socket.AF_INET:
270 af = 1
271 if self.af == socket.AF_INET6:
272 af = 2
273 if af == 0:
274 raise error_proto, 'unsupported address family'
275 fields = ['', repr(af), host, repr(port), '']
276 cmd = 'EPRT ' + '|'.join(fields)
277 return self.voidcmd(cmd)
278
279 def makeport(self):
280 '''Create a new socket and send a PORT command for it.'''
281 err = None
282 sock = None
283 for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
284 af, socktype, proto, canonname, sa = res
285 try:
286 sock = socket.socket(af, socktype, proto)
287 sock.bind(sa)
288 except socket.error, err:
289 if sock:
290 sock.close()
291 sock = None
292 continue
293 break
294 if sock is None:
295 if err is not None:
296 raise err
297 else:
298 raise socket.error("getaddrinfo returns an empty list")
299 sock.listen(1)
300 port = sock.getsockname()[1] # Get proper port
301 host = self.sock.getsockname()[0] # Get proper host
302 if self.af == socket.AF_INET:
303 resp = self.sendport(host, port)
304 else:
305 resp = self.sendeprt(host, port)
306 if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
307 sock.settimeout(self.timeout)
308 return sock
309
310 def makepasv(self):
311 if self.af == socket.AF_INET:
312 host, port = parse227(self.sendcmd('PASV'))
313 else:
314 host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
315 return host, port
316
317 def ntransfercmd(self, cmd, rest=None):
318 """Initiate a transfer over the data connection.
319
320 If the transfer is active, send a port command and the
321 transfer command, and accept the connection. If the server is
322 passive, send a pasv command, connect to it, and start the
323 transfer command. Either way, return the socket for the
324 connection and the expected size of the transfer. The
325 expected size may be None if it could not be determined.
326
327 Optional `rest' argument can be a string that is sent as the
328 argument to a REST command. This is essentially a server
329 marker used to tell the server to skip over any data up to the
330 given marker.
331 """
332 size = None
333 if self.passiveserver:
334 host, port = self.makepasv()
335 conn = socket.create_connection((host, port), self.timeout)
336 try:
337 if rest is not None:
338 self.sendcmd("REST %s" % rest)
339 resp = self.sendcmd(cmd)
340 # Some servers apparently send a 200 reply to
341 # a LIST or STOR command, before the 150 reply
342 # (and way before the 226 reply). This seems to
343 # be in violation of the protocol (which only allows
344 # 1xx or error messages for LIST), so we just discard
345 # this response.
346 if resp[0] == '2':
347 resp = self.getresp()
348 if resp[0] != '1':
349 raise error_reply, resp
350 except:
351 conn.close()
352 raise
353 else:
354 sock = self.makeport()
355 try:
356 if rest is not None:
357 self.sendcmd("REST %s" % rest)
358 resp = self.sendcmd(cmd)
359 # See above.
360 if resp[0] == '2':
361 resp = self.getresp()
362 if resp[0] != '1':
363 raise error_reply, resp
364 conn, sockaddr = sock.accept()
365 if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
366 conn.settimeout(self.timeout)
367 finally:
368 sock.close()
369 if resp[:3] == '150':
370 # this is conditional in case we received a 125
371 size = parse150(resp)
372 return conn, size
373
374 def transfercmd(self, cmd, rest=None):
375 """Like ntransfercmd() but returns only the socket."""
376 return self.ntransfercmd(cmd, rest)[0]
377
378 def login(self, user = '', passwd = '', acct = ''):
379 '''Login, default anonymous.'''
380 if not user: user = 'anonymous'
381 if not passwd: passwd = ''
382 if not acct: acct = ''
383 if user == 'anonymous' and passwd in ('', '-'):
384 # If there is no anonymous ftp password specified
385 # then we'll just use anonymous@
386 # We don't send any other thing because:
387 # - We want to remain anonymous
388 # - We want to stop SPAM
389 # - We don't want to let ftp sites to discriminate by the user,
390 # host or country.
391 passwd = passwd + 'anonymous@'
392 resp = self.sendcmd('USER ' + user)
393 if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
394 if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
395 if resp[0] != '2':
396 raise error_reply, resp
397 return resp
398
399 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
400 """Retrieve data in binary mode. A new port is created for you.
401
402 Args:
403 cmd: A RETR command.
404 callback: A single parameter callable to be called on each
405 block of data read.
406 blocksize: The maximum number of bytes to read from the
407 socket at one time. [default: 8192]
408 rest: Passed to transfercmd(). [default: None]
409
410 Returns:
411 The response code.
412 """
413 self.voidcmd('TYPE I')
414 conn = self.transfercmd(cmd, rest)
415 while 1:
416 data = conn.recv(blocksize)
417 if not data:
418 break
419 callback(data)
420 conn.close()
421 return self.voidresp()
422
423 def retrlines(self, cmd, callback = None):
424 """Retrieve data in line mode. A new port is created for you.
425
426 Args:
427 cmd: A RETR, LIST, NLST, or MLSD command.
428 callback: An optional single parameter callable that is called
429 for each line with the trailing CRLF stripped.
430 [default: print_line()]
431
432 Returns:
433 The response code.
434 """
435 if callback is None: callback = print_line
436 resp = self.sendcmd('TYPE A')
437 conn = self.transfercmd(cmd)
438 fp = conn.makefile('rb')
439 while 1:
440 line = fp.readline(self.maxline + 1)
441 if len(line) > self.maxline:
442 raise Error("got more than %d bytes" % self.maxline)
443 if self.debugging > 2: print '*retr*', repr(line)
444 if not line:
445 break
446 if line[-2:] == CRLF:
447 line = line[:-2]
448 elif line[-1:] == '\n':
449 line = line[:-1]
450 callback(line)
451 fp.close()
452 conn.close()
453 return self.voidresp()
454
455 def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
456 """Store a file in binary mode. A new port is created for you.
457
458 Args:
459 cmd: A STOR command.
460 fp: A file-like object with a read(num_bytes) method.
461 blocksize: The maximum data size to read from fp and send over
462 the connection at once. [default: 8192]
463 callback: An optional single parameter callable that is called on
464 each block of data after it is sent. [default: None]
465 rest: Passed to transfercmd(). [default: None]
466
467 Returns:
468 The response code.
469 """
470 self.voidcmd('TYPE I')
471 conn = self.transfercmd(cmd, rest)
472 while 1:
473 buf = fp.read(blocksize)
474 if not buf: break
475 conn.sendall(buf)
476 if callback: callback(buf)
477 conn.close()
478 return self.voidresp()
479
480 def storlines(self, cmd, fp, callback=None):
481 """Store a file in line mode. A new port is created for you.
482
483 Args:
484 cmd: A STOR command.
485 fp: A file-like object with a readline() method.
486 callback: An optional single parameter callable that is called on
487 each line after it is sent. [default: None]
488
489 Returns:
490 The response code.
491 """
492 self.voidcmd('TYPE A')
493 conn = self.transfercmd(cmd)
494 while 1:
495 buf = fp.readline(self.maxline + 1)
496 if len(buf) > self.maxline:
497 raise Error("got more than %d bytes" % self.maxline)
498 if not buf: break
499 if buf[-2:] != CRLF:
500 if buf[-1] in CRLF: buf = buf[:-1]
501 buf = buf + CRLF
502 conn.sendall(buf)
503 if callback: callback(buf)
504 conn.close()
505 return self.voidresp()
506
507 def acct(self, password):
508 '''Send new account name.'''
509 cmd = 'ACCT ' + password
510 return self.voidcmd(cmd)
511
512 def nlst(self, *args):
513 '''Return a list of files in a given directory (default the current).'''
514 cmd = 'NLST'
515 for arg in args:
516 cmd = cmd + (' ' + arg)
517 files = []
518 self.retrlines(cmd, files.append)
519 return files
520
521 def dir(self, *args):
522 '''List a directory in long form.
523 By default list current directory to stdout.
524 Optional last argument is callback function; all
525 non-empty arguments before it are concatenated to the
526 LIST command. (This *should* only be used for a pathname.)'''
527 cmd = 'LIST'
528 func = None
529 if args[-1:] and type(args[-1]) != type(''):
530 args, func = args[:-1], args[-1]
531 for arg in args:
532 if arg:
533 cmd = cmd + (' ' + arg)
534 self.retrlines(cmd, func)
535
536 def rename(self, fromname, toname):
537 '''Rename a file.'''
538 resp = self.sendcmd('RNFR ' + fromname)
539 if resp[0] != '3':
540 raise error_reply, resp
541 return self.voidcmd('RNTO ' + toname)
542
543 def delete(self, filename):
544 '''Delete a file.'''
545 resp = self.sendcmd('DELE ' + filename)
546 if resp[:3] in ('250', '200'):
547 return resp
548 else:
549 raise error_reply, resp
550
551 def cwd(self, dirname):
552 '''Change to a directory.'''
553 if dirname == '..':
554 try:
555 return self.voidcmd('CDUP')
556 except error_perm, msg:
557 if msg.args[0][:3] != '500':
558 raise
559 elif dirname == '':
560 dirname = '.' # does nothing, but could return error
561 cmd = 'CWD ' + dirname
562 return self.voidcmd(cmd)
563
564 def size(self, filename):
565 '''Retrieve the size of a file.'''
566 # The SIZE command is defined in RFC-3659
567 resp = self.sendcmd('SIZE ' + filename)
568 if resp[:3] == '213':
569 s = resp[3:].strip()
570 try:
571 return int(s)
572 except (OverflowError, ValueError):
573 return long(s)
574
575 def mkd(self, dirname):
576 '''Make a directory, return its full pathname.'''
577 resp = self.sendcmd('MKD ' + dirname)
578 return parse257(resp)
579
580 def rmd(self, dirname):
581 '''Remove a directory.'''
582 return self.voidcmd('RMD ' + dirname)
583
584 def pwd(self):
585 '''Return current working directory.'''
586 resp = self.sendcmd('PWD')
587 return parse257(resp)
588
589 def quit(self):
590 '''Quit, and close the connection.'''
591 resp = self.voidcmd('QUIT')
592 self.close()
593 return resp
594
595 def close(self):
596 '''Close the connection without assuming anything about it.'''
597 if self.file is not None:
598 self.file.close()
599 if self.sock is not None:
600 self.sock.close()
601 self.file = self.sock = None
602
603try:
604 import ssl
605except ImportError:
606 pass
607else:
608 class FTP_TLS(FTP):
609 '''A FTP subclass which adds TLS support to FTP as described
610 in RFC-4217.
611
612 Connect as usual to port 21 implicitly securing the FTP control
613 connection before authenticating.
614
615 Securing the data connection requires user to explicitly ask
616 for it by calling prot_p() method.
617
618 Usage example:
619 >>> from ftplib import FTP_TLS
620 >>> ftps = FTP_TLS('ftp.python.org')
621 >>> ftps.login() # login anonymously previously securing control channel
622 '230 Guest login ok, access restrictions apply.'
623 >>> ftps.prot_p() # switch to secure data connection
624 '200 Protection level set to P'
625 >>> ftps.retrlines('LIST') # list directory content securely
626 total 9
627 drwxr-xr-x 8 root wheel 1024 Jan 3 1994 .
628 drwxr-xr-x 8 root wheel 1024 Jan 3 1994 ..
629 drwxr-xr-x 2 root wheel 1024 Jan 3 1994 bin
630 drwxr-xr-x 2 root wheel 1024 Jan 3 1994 etc
631 d-wxrwxr-x 2 ftp wheel 1024 Sep 5 13:43 incoming
632 drwxr-xr-x 2 root wheel 1024 Nov 17 1993 lib
633 drwxr-xr-x 6 1094 wheel 1024 Sep 13 19:07 pub
634 drwxr-xr-x 3 root wheel 1024 Jan 3 1994 usr
635 -rw-r--r-- 1 root root 312 Aug 1 1994 welcome.msg
636 '226 Transfer complete.'
637 >>> ftps.quit()
638 '221 Goodbye.'
639 >>>
640 '''
641 ssl_version = ssl.PROTOCOL_TLSv1
642
643 def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
644 certfile=None, timeout=_GLOBAL_DEFAULT_TIMEOUT):
645 self.keyfile = keyfile
646 self.certfile = certfile
647 self._prot_p = False
648 FTP.__init__(self, host, user, passwd, acct, timeout)
649
650 def login(self, user='', passwd='', acct='', secure=True):
651 if secure and not isinstance(self.sock, ssl.SSLSocket):
652 self.auth()
653 return FTP.login(self, user, passwd, acct)
654
655 def auth(self):
656 '''Set up secure control connection by using TLS/SSL.'''
657 if isinstance(self.sock, ssl.SSLSocket):
658 raise ValueError("Already using TLS")
659 if self.ssl_version == ssl.PROTOCOL_TLSv1:
660 resp = self.voidcmd('AUTH TLS')
661 else:
662 resp = self.voidcmd('AUTH SSL')
663 self.sock = ssl.wrap_socket(self.sock, self.keyfile, self.certfile,
664 ssl_version=self.ssl_version)
665 self.file = self.sock.makefile(mode='rb')
666 return resp
667
668 def prot_p(self):
669 '''Set up secure data connection.'''
670 # PROT defines whether or not the data channel is to be protected.
671 # Though RFC-2228 defines four possible protection levels,
672 # RFC-4217 only recommends two, Clear and Private.
673 # Clear (PROT C) means that no security is to be used on the
674 # data-channel, Private (PROT P) means that the data-channel
675 # should be protected by TLS.
676 # PBSZ command MUST still be issued, but must have a parameter of
677 # '0' to indicate that no buffering is taking place and the data
678 # connection should not be encapsulated.
679 self.voidcmd('PBSZ 0')
680 resp = self.voidcmd('PROT P')
681 self._prot_p = True
682 return resp
683
684 def prot_c(self):
685 '''Set up clear text data connection.'''
686 resp = self.voidcmd('PROT C')
687 self._prot_p = False
688 return resp
689
690 # --- Overridden FTP methods
691
692 def ntransfercmd(self, cmd, rest=None):
693 conn, size = FTP.ntransfercmd(self, cmd, rest)
694 if self._prot_p:
695 conn = ssl.wrap_socket(conn, self.keyfile, self.certfile,
696 ssl_version=self.ssl_version)
697 return conn, size
698
699 def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
700 self.voidcmd('TYPE I')
701 conn = self.transfercmd(cmd, rest)
702 try:
703 while 1:
704 data = conn.recv(blocksize)
705 if not data:
706 break
707 callback(data)
708 # shutdown ssl layer
709 if isinstance(conn, ssl.SSLSocket):
710 conn.unwrap()
711 finally:
712 conn.close()
713 return self.voidresp()
714
715 def retrlines(self, cmd, callback = None):
716 if callback is None: callback = print_line
717 resp = self.sendcmd('TYPE A')
718 conn = self.transfercmd(cmd)
719 fp = conn.makefile('rb')
720 try:
721 while 1:
722 line = fp.readline(self.maxline + 1)
723 if len(line) > self.maxline:
724 raise Error("got more than %d bytes" % self.maxline)
725 if self.debugging > 2: print '*retr*', repr(line)
726 if not line:
727 break
728 if line[-2:] == CRLF:
729 line = line[:-2]
730 elif line[-1:] == '\n':
731 line = line[:-1]
732 callback(line)
733 # shutdown ssl layer
734 if isinstance(conn, ssl.SSLSocket):
735 conn.unwrap()
736 finally:
737 fp.close()
738 conn.close()
739 return self.voidresp()
740
741 def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
742 self.voidcmd('TYPE I')
743 conn = self.transfercmd(cmd, rest)
744 try:
745 while 1:
746 buf = fp.read(blocksize)
747 if not buf: break
748 conn.sendall(buf)
749 if callback: callback(buf)
750 # shutdown ssl layer
751 if isinstance(conn, ssl.SSLSocket):
752 conn.unwrap()
753 finally:
754 conn.close()
755 return self.voidresp()
756
757 def storlines(self, cmd, fp, callback=None):
758 self.voidcmd('TYPE A')
759 conn = self.transfercmd(cmd)
760 try:
761 while 1:
762 buf = fp.readline(self.maxline + 1)
763 if len(buf) > self.maxline:
764 raise Error("got more than %d bytes" % self.maxline)
765 if not buf: break
766 if buf[-2:] != CRLF:
767 if buf[-1] in CRLF: buf = buf[:-1]
768 buf = buf + CRLF
769 conn.sendall(buf)
770 if callback: callback(buf)
771 # shutdown ssl layer
772 if isinstance(conn, ssl.SSLSocket):
773 conn.unwrap()
774 finally:
775 conn.close()
776 return self.voidresp()
777
778 __all__.append('FTP_TLS')
779 all_errors = (Error, IOError, EOFError, ssl.SSLError)
780
781
782_150_re = None
783
784def parse150(resp):
785 '''Parse the '150' response for a RETR request.
786 Returns the expected transfer size or None; size is not guaranteed to
787 be present in the 150 message.
788 '''
789 if resp[:3] != '150':
790 raise error_reply, resp
791 global _150_re
792 if _150_re is None:
793 import re
794 _150_re = re.compile("150 .* \((\d+) bytes\)", re.IGNORECASE)
795 m = _150_re.match(resp)
796 if not m:
797 return None
798 s = m.group(1)
799 try:
800 return int(s)
801 except (OverflowError, ValueError):
802 return long(s)
803
804
805_227_re = None
806
807def parse227(resp):
808 '''Parse the '227' response for a PASV request.
809 Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
810 Return ('host.addr.as.numbers', port#) tuple.'''
811
812 if resp[:3] != '227':
813 raise error_reply, resp
814 global _227_re
815 if _227_re is None:
816 import re
817 _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)')
818 m = _227_re.search(resp)
819 if not m:
820 raise error_proto, resp
821 numbers = m.groups()
822 host = '.'.join(numbers[:4])
823 port = (int(numbers[4]) << 8) + int(numbers[5])
824 return host, port
825
826
827def parse229(resp, peer):
828 '''Parse the '229' response for a EPSV request.
829 Raises error_proto if it does not contain '(|||port|)'
830 Return ('host.addr.as.numbers', port#) tuple.'''
831
832 if resp[:3] != '229':
833 raise error_reply, resp
834 left = resp.find('(')
835 if left < 0: raise error_proto, resp
836 right = resp.find(')', left + 1)
837 if right < 0:
838 raise error_proto, resp # should contain '(|||port|)'
839 if resp[left + 1] != resp[right - 1]:
840 raise error_proto, resp
841 parts = resp[left + 1:right].split(resp[left+1])
842 if len(parts) != 5:
843 raise error_proto, resp
844 host = peer[0]
845 port = int(parts[3])
846 return host, port
847
848
849def parse257(resp):
850 '''Parse the '257' response for a MKD or PWD request.
851 This is a response to a MKD or PWD request: a directory name.
852 Returns the directoryname in the 257 reply.'''
853
854 if resp[:3] != '257':
855 raise error_reply, resp
856 if resp[3:5] != ' "':
857 return '' # Not compliant to RFC 959, but UNIX ftpd does this
858 dirname = ''
859 i = 5
860 n = len(resp)
861 while i < n:
862 c = resp[i]
863 i = i+1
864 if c == '"':
865 if i >= n or resp[i] != '"':
866 break
867 i = i+1
868 dirname = dirname + c
869 return dirname
870
871
872def print_line(line):
873 '''Default retrlines callback to print a line.'''
874 print line
875
876
877def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
878 '''Copy file from one FTP-instance to another.'''
879 if not targetname: targetname = sourcename
880 type = 'TYPE ' + type
881 source.voidcmd(type)
882 target.voidcmd(type)
883 sourcehost, sourceport = parse227(source.sendcmd('PASV'))
884 target.sendport(sourcehost, sourceport)
885 # RFC 959: the user must "listen" [...] BEFORE sending the
886 # transfer request.
887 # So: STOR before RETR, because here the target is a "user".
888 treply = target.sendcmd('STOR ' + targetname)
889 if treply[:3] not in ('125', '150'): raise error_proto # RFC 959
890 sreply = source.sendcmd('RETR ' + sourcename)
891 if sreply[:3] not in ('125', '150'): raise error_proto # RFC 959
892 source.voidresp()
893 target.voidresp()
894
895
896class Netrc:
897 """Class to parse & provide access to 'netrc' format files.
898
899 See the netrc(4) man page for information on the file format.
900
901 WARNING: This class is obsolete -- use module netrc instead.
902
903 """
904 __defuser = None
905 __defpasswd = None
906 __defacct = None
907
908 def __init__(self, filename=None):
909 if filename is None:
910 if "HOME" in os.environ:
911 filename = os.path.join(os.environ["HOME"],
912 ".netrc")
913 else:
914 raise IOError, \
915 "specify file to load or set $HOME"
916 self.__hosts = {}
917 self.__macros = {}
918 fp = open(filename, "r")
919 in_macro = 0
920 while 1:
921 line = fp.readline(self.maxline + 1)
922 if len(line) > self.maxline:
923 raise Error("got more than %d bytes" % self.maxline)
924 if not line: break
925 if in_macro and line.strip():
926 macro_lines.append(line)
927 continue
928 elif in_macro:
929 self.__macros[macro_name] = tuple(macro_lines)
930 in_macro = 0
931 words = line.split()
932 host = user = passwd = acct = None
933 default = 0
934 i = 0
935 while i < len(words):
936 w1 = words[i]
937 if i+1 < len(words):
938 w2 = words[i + 1]
939 else:
940 w2 = None
941 if w1 == 'default':
942 default = 1
943 elif w1 == 'machine' and w2:
944 host = w2.lower()
945 i = i + 1
946 elif w1 == 'login' and w2:
947 user = w2
948 i = i + 1
949 elif w1 == 'password' and w2:
950 passwd = w2
951 i = i + 1
952 elif w1 == 'account' and w2:
953 acct = w2
954 i = i + 1
955 elif w1 == 'macdef' and w2:
956 macro_name = w2
957 macro_lines = []
958 in_macro = 1
959 break
960 i = i + 1
961 if default:
962 self.__defuser = user or self.__defuser
963 self.__defpasswd = passwd or self.__defpasswd
964 self.__defacct = acct or self.__defacct
965 if host:
966 if host in self.__hosts:
967 ouser, opasswd, oacct = \
968 self.__hosts[host]
969 user = user or ouser
970 passwd = passwd or opasswd
971 acct = acct or oacct
972 self.__hosts[host] = user, passwd, acct
973 fp.close()
974
975 def get_hosts(self):
976 """Return a list of hosts mentioned in the .netrc file."""
977 return self.__hosts.keys()
978
979 def get_account(self, host):
980 """Returns login information for the named host.
981
982 The return value is a triple containing userid,
983 password, and the accounting field.
984
985 """
986 host = host.lower()
987 user = passwd = acct = None
988 if host in self.__hosts:
989 user, passwd, acct = self.__hosts[host]
990 user = user or self.__defuser
991 passwd = passwd or self.__defpasswd
992 acct = acct or self.__defacct
993 return user, passwd, acct
994
995 def get_macros(self):
996 """Return a list of all defined macro names."""
997 return self.__macros.keys()
998
999 def get_macro(self, macro):
1000 """Return a sequence of lines which define a named macro."""
1001 return self.__macros[macro]
1002
1003
1004
1005def test():
1006 '''Test program.
1007 Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
1008
1009 -d dir
1010 -l list
1011 -p password
1012 '''
1013
1014 if len(sys.argv) < 2:
1015 print test.__doc__
1016 sys.exit(0)
1017
1018 debugging = 0
1019 rcfile = None
1020 while sys.argv[1] == '-d':
1021 debugging = debugging+1
1022 del sys.argv[1]
1023 if sys.argv[1][:2] == '-r':
1024 # get name of alternate ~/.netrc file:
1025 rcfile = sys.argv[1][2:]
1026 del sys.argv[1]
1027 host = sys.argv[1]
1028 ftp = FTP(host)
1029 ftp.set_debuglevel(debugging)
1030 userid = passwd = acct = ''
1031 try:
1032 netrc = Netrc(rcfile)
1033 except IOError:
1034 if rcfile is not None:
1035 sys.stderr.write("Could not open account file"
1036 " -- using anonymous login.")
1037 else:
1038 try:
1039 userid, passwd, acct = netrc.get_account(host)
1040 except KeyError:
1041 # no account for host
1042 sys.stderr.write(
1043 "No account -- using anonymous login.")
1044 ftp.login(userid, passwd, acct)
1045 for file in sys.argv[2:]:
1046 if file[:2] == '-l':
1047 ftp.dir(file[2:])
1048 elif file[:2] == '-d':
1049 cmd = 'CWD'
1050 if file[2:]: cmd = cmd + ' ' + file[2:]
1051 resp = ftp.sendcmd(cmd)
1052 elif file == '-p':
1053 ftp.set_pasv(not ftp.passiveserver)
1054 else:
1055 ftp.retrbinary('RETR ' + file, \
1056 sys.stdout.write, 1024)
1057 ftp.quit()
1058
1059
1060if __name__ == '__main__':
1061 test()
Note: See TracBrowser for help on using the repository browser.