ftplib.py 34.4 KB
Newer Older
1 2
"""An FTP client class and some helper functions.

3
Based on RFC 959: File Transfer Protocol (FTP), by J. Postel and J. Reynolds
4 5 6 7 8

Example:

>>> from ftplib import FTP
>>> 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 12 13 14 15 16 17 18 19 20 21
>>> ftp.retrlines('LIST') # list directory contents
total 9
drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 .
drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 ..
drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 bin
drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 etc
d-wxrwxr-x   2 ftp      wheel        1024 Sep  5 13:43 incoming
drwxr-xr-x   2 root     wheel        1024 Nov 17  1993 lib
drwxr-xr-x   6 1094     wheel        1024 Sep 13 19:07 pub
drwxr-xr-x   3 root     wheel        1024 Jan  3  1994 usr
-rw-r--r--   1 root     root          312 Aug  1  1994 welcome.msg
22
'226 Transfer complete.'
23
>>> ftp.quit()
24
'221 Goodbye.'
Tim Peters's avatar
Tim Peters committed
25
>>>
26 27 28

A nice test that reveals some of the network dialogue would be:
python ftplib.py -d localhost -l -p -l
29
"""
30

Tim Peters's avatar
Tim Peters committed
31
#
32 33 34
# Changes and improvements suggested by Steve Majewski.
# Modified by Jack to work on the mac.
# 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

Guido van Rossum's avatar
Guido van Rossum committed
39
import sys
40
import socket
Georg Brandl's avatar
Georg Brandl committed
41
from socket import _GLOBAL_DEFAULT_TIMEOUT
42

43 44
__all__ = ["FTP", "error_reply", "error_temp", "error_perm", "error_proto",
           "all_errors"]
Guido van Rossum's avatar
Guido van Rossum committed
45

46
# Magic number from <socket.h>
Tim Peters's avatar
Tim Peters committed
47
MSG_OOB = 0x1                           # Process data out of band
48 49


50
# The standard FTP server control port
Guido van Rossum's avatar
Guido van Rossum committed
51
FTP_PORT = 21
52 53
# The sizehint parameter passed to readline() calls
MAXLINE = 8192
Guido van Rossum's avatar
Guido van Rossum committed
54 55


56
# Exception raised when an error or invalid response is received
57
class Error(Exception): pass
Tim Peters's avatar
Tim Peters committed
58 59 60 61
class error_reply(Error): pass          # unexpected [123]xx reply
class error_temp(Error): pass           # 4xx errors
class error_perm(Error): pass           # 5xx errors
class error_proto(Error): pass          # response does not begin with [1-5]
Guido van Rossum's avatar
Guido van Rossum committed
62 63


64 65
# All exceptions (hopefully) that may be raised here and that aren't
# (always) programming errors on our side
66
all_errors = (Error, OSError, EOFError)
67 68


Guido van Rossum's avatar
Guido van Rossum committed
69 70
# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
CRLF = '\r\n'
71
B_CRLF = b'\r\n'
Guido van Rossum's avatar
Guido van Rossum committed
72 73 74 75

# The class itself
class FTP:

Tim Peters's avatar
Tim Peters committed
76
    '''An FTP client class.
77

78 79 80 81 82 83 84 85 86
    To create a connection, call the class using these arguments:
            host, user, passwd, acct, timeout

    The first four arguments are all strings, and have default value ''.
    timeout must be numeric and defaults to None if not passed,
    meaning that no timeout will be set on any ftp socket(s)
    If a timeout is passed, then this is now the default timeout for all ftp
    socket operations for this instance.

Tim Peters's avatar
Tim Peters committed
87
    Then use self.connect() with optional host and port argument.
88

Tim Peters's avatar
Tim Peters committed
89 90 91 92 93 94 95
    To download a file, use ftp.retrlines('RETR ' + filename),
    or ftp.retrbinary() with slightly different arguments.
    To upload a file, use ftp.storlines() or ftp.storbinary(),
    which have an open file as argument (see their definitions
    below for details).
    The download/upload functions first issue appropriate TYPE
    and PORT or PASV commands.
96
    '''
97

98 99 100
    debugging = 0
    host = ''
    port = FTP_PORT
101
    maxline = MAXLINE
102 103 104 105
    sock = None
    file = None
    welcome = None
    passiveserver = 1
106
    encoding = "latin-1"
107

Tim Peters's avatar
Tim Peters committed
108 109 110 111
    # Initialization method (called by class instantiation).
    # Initialize host to localhost, port to standard ftp port
    # Optional arguments are host (for connect()),
    # and user, passwd, acct (for login())
Georg Brandl's avatar
Georg Brandl committed
112
    def __init__(self, host='', user='', passwd='', acct='',
113 114
                 timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None):
        self.source_address = source_address
115
        self.timeout = timeout
Tim Peters's avatar
Tim Peters committed
116
        if host:
117
            self.connect(host)
118 119
            if user:
                self.login(user, passwd, acct)
Tim Peters's avatar
Tim Peters committed
120

121 122 123 124 125 126 127 128
    def __enter__(self):
        return self

    # Context management protocol: try to quit() if active
    def __exit__(self, *args):
        if self.sock is not None:
            try:
                self.quit()
129
            except (OSError, EOFError):
130 131 132 133 134
                pass
            finally:
                if self.sock is not None:
                    self.close()

135
    def connect(self, host='', port=0, timeout=-999, source_address=None):
136
        '''Connect to host.  Arguments are:
137 138
         - host: hostname to connect to (string, default previous host)
         - port: port to connect to (integer, default previous port)
139
         - timeout: the timeout to set against the ftp socket(s)
140 141
         - source_address: a 2-tuple (host, port) for the socket to bind
           to as its source address before connecting.
142 143 144 145 146
        '''
        if host != '':
            self.host = host
        if port > 0:
            self.port = port
Georg Brandl's avatar
Georg Brandl committed
147
        if timeout != -999:
148
            self.timeout = timeout
149 150 151 152
        if source_address is not None:
            self.source_address = source_address
        self.sock = socket.create_connection((self.host, self.port), self.timeout,
                                             source_address=self.source_address)
153
        self.af = self.sock.family
154
        self.file = self.sock.makefile('r', encoding=self.encoding)
155 156
        self.welcome = self.getresp()
        return self.welcome
Tim Peters's avatar
Tim Peters committed
157 158 159 160 161

    def getwelcome(self):
        '''Get the welcome message from the server.
        (this is read and squirreled away by connect())'''
        if self.debugging:
162
            print('*welcome*', self.sanitize(self.welcome))
Tim Peters's avatar
Tim Peters committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
        return self.welcome

    def set_debuglevel(self, level):
        '''Set the debugging level.
        The required argument level means:
        0: no debugging output (default)
        1: print commands and responses but not body text etc.
        2: also print raw lines read and sent before stripping CR/LF'''
        self.debugging = level
    debug = set_debuglevel

    def set_pasv(self, val):
        '''Use passive or active mode for data transfers.
        With a false argument, use the normal PORT mode,
        With a true argument, use the PASV command.'''
        self.passiveserver = val

    # Internal: "sanitize" a string for printing
    def sanitize(self, s):
182 183
        if s[:5] in {'pass ', 'PASS '}:
            i = len(s.rstrip('\r\n'))
Tim Peters's avatar
Tim Peters committed
184
            s = s[:5] + '*'*(i-5) + s[i:]
185
        return repr(s)
Tim Peters's avatar
Tim Peters committed
186 187 188

    # Internal: send one line to the server, appending CRLF
    def putline(self, line):
189 190
        if '\r' in line or '\n' in line:
            raise ValueError('an illegal newline character should not be contained')
Tim Peters's avatar
Tim Peters committed
191
        line = line + CRLF
192 193
        if self.debugging > 1:
            print('*put*', self.sanitize(line))
194
        self.sock.sendall(line.encode(self.encoding))
Tim Peters's avatar
Tim Peters committed
195 196 197

    # Internal: send one command to the server (through putline())
    def putcmd(self, line):
198
        if self.debugging: print('*cmd*', self.sanitize(line))
Tim Peters's avatar
Tim Peters committed
199 200 201 202 203
        self.putline(line)

    # Internal: return one line from the server, stripping CRLF.
    # Raise EOFError if the connection is closed
    def getline(self):
204 205 206
        line = self.file.readline(self.maxline + 1)
        if len(line) > self.maxline:
            raise Error("got more than %d bytes" % self.maxline)
Tim Peters's avatar
Tim Peters committed
207
        if self.debugging > 1:
208
            print('*get*', self.sanitize(line))
209 210 211 212 213 214
        if not line:
            raise EOFError
        if line[-2:] == CRLF:
            line = line[:-2]
        elif line[-1:] in CRLF:
            line = line[:-1]
Tim Peters's avatar
Tim Peters committed
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
        return line

    # Internal: get a response from the server, which may possibly
    # consist of multiple lines.  Return a single string with no
    # trailing CRLF.  If the response consists of multiple lines,
    # these are separated by '\n' characters in the string
    def getmultiline(self):
        line = self.getline()
        if line[3:4] == '-':
            code = line[:3]
            while 1:
                nextline = self.getline()
                line = line + ('\n' + nextline)
                if nextline[:3] == code and \
                        nextline[3:4] != '-':
                    break
        return line

    # Internal: get a response from the server.
    # Raise various errors if the response indicates an error
    def getresp(self):
        resp = self.getmultiline()
237 238
        if self.debugging:
            print('*resp*', self.sanitize(resp))
Tim Peters's avatar
Tim Peters committed
239 240
        self.lastresp = resp[:3]
        c = resp[:1]
241
        if c in {'1', '2', '3'}:
242
            return resp
Tim Peters's avatar
Tim Peters committed
243
        if c == '4':
244
            raise error_temp(resp)
Tim Peters's avatar
Tim Peters committed
245
        if c == '5':
246 247
            raise error_perm(resp)
        raise error_proto(resp)
Tim Peters's avatar
Tim Peters committed
248 249 250 251

    def voidresp(self):
        """Expect a response beginning with '2'."""
        resp = self.getresp()
Benjamin Peterson's avatar
Benjamin Peterson committed
252
        if resp[:1] != '2':
253
            raise error_reply(resp)
Tim Peters's avatar
Tim Peters committed
254 255 256 257 258 259 260
        return resp

    def abort(self):
        '''Abort a file transfer.  Uses out-of-band data.
        This does not follow the procedure from the RFC to send Telnet
        IP and Synch; that doesn't seem to work with the servers I've
        tried.  Instead, just send the ABOR command as OOB data.'''
261
        line = b'ABOR' + B_CRLF
262 263
        if self.debugging > 1:
            print('*put urgent*', self.sanitize(line))
264
        self.sock.sendall(line, MSG_OOB)
Tim Peters's avatar
Tim Peters committed
265
        resp = self.getmultiline()
266
        if resp[:3] not in {'426', '225', '226'}:
267
            raise error_proto(resp)
268
        return resp
Tim Peters's avatar
Tim Peters committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283

    def sendcmd(self, cmd):
        '''Send a command and return the response.'''
        self.putcmd(cmd)
        return self.getresp()

    def voidcmd(self, cmd):
        """Send a command and expect a response beginning with '2'."""
        self.putcmd(cmd)
        return self.voidresp()

    def sendport(self, host, port):
        '''Send a PORT command with the current host and the given
        port number.
        '''
284
        hbytes = host.split('.')
285
        pbytes = [repr(port//256), repr(port%256)]
Tim Peters's avatar
Tim Peters committed
286
        bytes = hbytes + pbytes
287
        cmd = 'PORT ' + ','.join(bytes)
Tim Peters's avatar
Tim Peters committed
288 289
        return self.voidcmd(cmd)

290
    def sendeprt(self, host, port):
291
        '''Send an EPRT command with the current host and the given port number.'''
292 293 294 295 296 297
        af = 0
        if self.af == socket.AF_INET:
            af = 1
        if self.af == socket.AF_INET6:
            af = 2
        if af == 0:
298
            raise error_proto('unsupported address family')
299
        fields = ['', repr(af), host, repr(port), '']
300
        cmd = 'EPRT ' + '|'.join(fields)
301
        return self.voidcmd(cmd)
302

Tim Peters's avatar
Tim Peters committed
303
    def makeport(self):
304
        '''Create a new socket and send a PORT command for it.'''
305
        err = None
306
        sock = None
307 308 309 310 311
        for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
            af, socktype, proto, canonname, sa = res
            try:
                sock = socket.socket(af, socktype, proto)
                sock.bind(sa)
312
            except OSError as _:
313
                err = _
314 315
                if sock:
                    sock.close()
316 317 318
                sock = None
                continue
            break
319 320 321 322
        if sock is None:
            if err is not None:
                raise err
            else:
323
                raise OSError("getaddrinfo returns an empty list")
324 325 326 327 328 329 330
        sock.listen(1)
        port = sock.getsockname()[1] # Get proper port
        host = self.sock.getsockname()[0] # Get proper host
        if self.af == socket.AF_INET:
            resp = self.sendport(host, port)
        else:
            resp = self.sendeprt(host, port)
331 332
        if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
            sock.settimeout(self.timeout)
333
        return sock
334 335

    def makepasv(self):
336 337 338 339 340
        if self.af == socket.AF_INET:
            host, port = parse227(self.sendcmd('PASV'))
        else:
            host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
        return host, port
Tim Peters's avatar
Tim Peters committed
341 342 343 344 345 346 347 348 349 350 351 352

    def ntransfercmd(self, cmd, rest=None):
        """Initiate a transfer over the data connection.

        If the transfer is active, send a port command and the
        transfer command, and accept the connection.  If the server is
        passive, send a pasv command, connect to it, and start the
        transfer command.  Either way, return the socket for the
        connection and the expected size of the transfer.  The
        expected size may be None if it could not be determined.

        Optional `rest' argument can be a string that is sent as the
353
        argument to a REST command.  This is essentially a server
Tim Peters's avatar
Tim Peters committed
354 355 356 357 358
        marker used to tell the server to skip over any data up to the
        given marker.
        """
        size = None
        if self.passiveserver:
359
            host, port = self.makepasv()
360 361
            conn = socket.create_connection((host, port), self.timeout,
                                            source_address=self.source_address)
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
            try:
                if rest is not None:
                    self.sendcmd("REST %s" % rest)
                resp = self.sendcmd(cmd)
                # Some servers apparently send a 200 reply to
                # a LIST or STOR command, before the 150 reply
                # (and way before the 226 reply). This seems to
                # be in violation of the protocol (which only allows
                # 1xx or error messages for LIST), so we just discard
                # this response.
                if resp[0] == '2':
                    resp = self.getresp()
                if resp[0] != '1':
                    raise error_reply(resp)
            except:
                conn.close()
                raise
Tim Peters's avatar
Tim Peters committed
379
        else:
380
            with self.makeport() as sock:
381 382 383 384 385 386 387 388 389 390 391
                if rest is not None:
                    self.sendcmd("REST %s" % rest)
                resp = self.sendcmd(cmd)
                # See above.
                if resp[0] == '2':
                    resp = self.getresp()
                if resp[0] != '1':
                    raise error_reply(resp)
                conn, sockaddr = sock.accept()
                if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
                    conn.settimeout(self.timeout)
Tim Peters's avatar
Tim Peters committed
392 393 394 395 396 397
        if resp[:3] == '150':
            # this is conditional in case we received a 125
            size = parse150(resp)
        return conn, size

    def transfercmd(self, cmd, rest=None):
398
        """Like ntransfercmd() but returns only the socket."""
Tim Peters's avatar
Tim Peters committed
399 400 401 402
        return self.ntransfercmd(cmd, rest)[0]

    def login(self, user = '', passwd = '', acct = ''):
        '''Login, default anonymous.'''
403 404 405 406 407 408
        if not user:
            user = 'anonymous'
        if not passwd:
            passwd = ''
        if not acct:
            acct = ''
409
        if user == 'anonymous' and passwd in {'', '-'}:
Tim Peters's avatar
Tim Peters committed
410 411 412 413 414 415 416
            # If there is no anonymous ftp password specified
            # then we'll just use anonymous@
            # We don't send any other thing because:
            # - We want to remain anonymous
            # - We want to stop SPAM
            # - We don't want to let ftp sites to discriminate by the user,
            #   host or country.
417
            passwd = passwd + 'anonymous@'
Tim Peters's avatar
Tim Peters committed
418
        resp = self.sendcmd('USER ' + user)
419 420 421 422
        if resp[0] == '3':
            resp = self.sendcmd('PASS ' + passwd)
        if resp[0] == '3':
            resp = self.sendcmd('ACCT ' + acct)
Tim Peters's avatar
Tim Peters committed
423
        if resp[0] != '2':
424
            raise error_reply(resp)
Tim Peters's avatar
Tim Peters committed
425 426 427
        return resp

    def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
428 429 430 431 432 433 434 435 436 437 438 439
        """Retrieve data in binary mode.  A new port is created for you.

        Args:
          cmd: A RETR command.
          callback: A single parameter callable to be called on each
                    block of data read.
          blocksize: The maximum number of bytes to read from the
                     socket at one time.  [default: 8192]
          rest: Passed to transfercmd().  [default: None]

        Returns:
          The response code.
Tim Peters's avatar
Tim Peters committed
440 441
        """
        self.voidcmd('TYPE I')
442 443 444 445 446 447
        with self.transfercmd(cmd, rest) as conn:
            while 1:
                data = conn.recv(blocksize)
                if not data:
                    break
                callback(data)
448
            # shutdown ssl layer
449
            if _SSLSocket is not None and isinstance(conn, _SSLSocket):
450
                conn.unwrap()
Tim Peters's avatar
Tim Peters committed
451 452 453
        return self.voidresp()

    def retrlines(self, cmd, callback = None):
454 455 456
        """Retrieve data in line mode.  A new port is created for you.

        Args:
457
          cmd: A RETR, LIST, or NLST command.
458 459 460 461 462 463 464
          callback: An optional single parameter callable that is called
                    for each line with the trailing CRLF stripped.
                    [default: print_line()]

        Returns:
          The response code.
        """
465 466
        if callback is None:
            callback = print_line
Tim Peters's avatar
Tim Peters committed
467
        resp = self.sendcmd('TYPE A')
468 469 470
        with self.transfercmd(cmd) as conn, \
                 conn.makefile('r', encoding=self.encoding) as fp:
            while 1:
471 472 473
                line = fp.readline(self.maxline + 1)
                if len(line) > self.maxline:
                    raise Error("got more than %d bytes" % self.maxline)
474 475
                if self.debugging > 2:
                    print('*retr*', repr(line))
476 477 478 479 480 481 482
                if not line:
                    break
                if line[-2:] == CRLF:
                    line = line[:-2]
                elif line[-1:] == '\n':
                    line = line[:-1]
                callback(line)
483
            # shutdown ssl layer
484
            if _SSLSocket is not None and isinstance(conn, _SSLSocket):
485
                conn.unwrap()
Tim Peters's avatar
Tim Peters committed
486 487
        return self.voidresp()

488
    def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
489 490 491 492 493 494 495 496
        """Store a file in binary mode.  A new port is created for you.

        Args:
          cmd: A STOR command.
          fp: A file-like object with a read(num_bytes) method.
          blocksize: The maximum data size to read from fp and send over
                     the connection at once.  [default: 8192]
          callback: An optional single parameter callable that is called on
497
                    each block of data after it is sent.  [default: None]
498
          rest: Passed to transfercmd().  [default: None]
499 500 501 502

        Returns:
          The response code.
        """
Tim Peters's avatar
Tim Peters committed
503
        self.voidcmd('TYPE I')
504 505 506
        with self.transfercmd(cmd, rest) as conn:
            while 1:
                buf = fp.read(blocksize)
507 508
                if not buf:
                    break
509
                conn.sendall(buf)
510 511
                if callback:
                    callback(buf)
512
            # shutdown ssl layer
513
            if _SSLSocket is not None and isinstance(conn, _SSLSocket):
514
                conn.unwrap()
Tim Peters's avatar
Tim Peters committed
515 516
        return self.voidresp()

517 518 519 520 521 522 523
    def storlines(self, cmd, fp, callback=None):
        """Store a file in line mode.  A new port is created for you.

        Args:
          cmd: A STOR command.
          fp: A file-like object with a readline() method.
          callback: An optional single parameter callable that is called on
524
                    each line after it is sent.  [default: None]
525 526 527 528

        Returns:
          The response code.
        """
Tim Peters's avatar
Tim Peters committed
529
        self.voidcmd('TYPE A')
530 531
        with self.transfercmd(cmd) as conn:
            while 1:
532 533 534
                buf = fp.readline(self.maxline + 1)
                if len(buf) > self.maxline:
                    raise Error("got more than %d bytes" % self.maxline)
535 536
                if not buf:
                    break
537 538 539 540
                if buf[-2:] != B_CRLF:
                    if buf[-1] in B_CRLF: buf = buf[:-1]
                    buf = buf + B_CRLF
                conn.sendall(buf)
541 542
                if callback:
                    callback(buf)
543
            # shutdown ssl layer
544
            if _SSLSocket is not None and isinstance(conn, _SSLSocket):
545
                conn.unwrap()
Tim Peters's avatar
Tim Peters committed
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
        return self.voidresp()

    def acct(self, password):
        '''Send new account name.'''
        cmd = 'ACCT ' + password
        return self.voidcmd(cmd)

    def nlst(self, *args):
        '''Return a list of files in a given directory (default the current).'''
        cmd = 'NLST'
        for arg in args:
            cmd = cmd + (' ' + arg)
        files = []
        self.retrlines(cmd, files.append)
        return files

    def dir(self, *args):
        '''List a directory in long form.
        By default list current directory to stdout.
        Optional last argument is callback function; all
        non-empty arguments before it are concatenated to the
        LIST command.  (This *should* only be used for a pathname.)'''
        cmd = 'LIST'
        func = None
        if args[-1:] and type(args[-1]) != type(''):
            args, func = args[:-1], args[-1]
        for arg in args:
            if arg:
                cmd = cmd + (' ' + arg)
        self.retrlines(cmd, func)

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
    def mlsd(self, path="", facts=[]):
        '''List a directory in a standardized format by using MLSD
        command (RFC-3659). If path is omitted the current directory
        is assumed. "facts" is a list of strings representing the type
        of information desired (e.g. ["type", "size", "perm"]).

        Return a generator object yielding a tuple of two elements
        for every file found in path.
        First element is the file name, the second one is a dictionary
        including a variable number of "facts" depending on the server
        and whether "facts" argument has been provided.
        '''
        if facts:
            self.sendcmd("OPTS MLST " + ";".join(facts) + ";")
        if path:
            cmd = "MLSD %s" % path
        else:
            cmd = "MLSD"
        lines = []
        self.retrlines(cmd, lines.append)
        for line in lines:
            facts_found, _, name = line.rstrip(CRLF).partition(' ')
            entry = {}
            for fact in facts_found[:-1].split(";"):
                key, _, value = fact.partition("=")
                entry[key.lower()] = value
            yield (name, entry)

Tim Peters's avatar
Tim Peters committed
605 606 607 608
    def rename(self, fromname, toname):
        '''Rename a file.'''
        resp = self.sendcmd('RNFR ' + fromname)
        if resp[0] != '3':
609
            raise error_reply(resp)
Tim Peters's avatar
Tim Peters committed
610 611 612 613 614
        return self.voidcmd('RNTO ' + toname)

    def delete(self, filename):
        '''Delete a file.'''
        resp = self.sendcmd('DELE ' + filename)
615
        if resp[:3] in {'250', '200'}:
Tim Peters's avatar
Tim Peters committed
616 617
            return resp
        else:
618
            raise error_reply(resp)
Tim Peters's avatar
Tim Peters committed
619 620 621 622 623 624

    def cwd(self, dirname):
        '''Change to a directory.'''
        if dirname == '..':
            try:
                return self.voidcmd('CDUP')
625
            except error_perm as msg:
626 627
                if msg.args[0][:3] != '500':
                    raise
Tim Peters's avatar
Tim Peters committed
628 629 630 631 632 633 634
        elif dirname == '':
            dirname = '.'  # does nothing, but could return error
        cmd = 'CWD ' + dirname
        return self.voidcmd(cmd)

    def size(self, filename):
        '''Retrieve the size of a file.'''
635
        # The SIZE command is defined in RFC-3659
Tim Peters's avatar
Tim Peters committed
636 637
        resp = self.sendcmd('SIZE ' + filename)
        if resp[:3] == '213':
638
            s = resp[3:].strip()
639
            return int(s)
Tim Peters's avatar
Tim Peters committed
640 641 642

    def mkd(self, dirname):
        '''Make a directory, return its full pathname.'''
643 644 645 646 647
        resp = self.voidcmd('MKD ' + dirname)
        # fix around non-compliant implementations such as IIS shipped
        # with Windows server 2003
        if not resp.startswith('257'):
            return ''
Tim Peters's avatar
Tim Peters committed
648 649 650 651 652 653 654 655
        return parse257(resp)

    def rmd(self, dirname):
        '''Remove a directory.'''
        return self.voidcmd('RMD ' + dirname)

    def pwd(self):
        '''Return current working directory.'''
656 657 658 659 660
        resp = self.voidcmd('PWD')
        # fix around non-compliant implementations such as IIS shipped
        # with Windows server 2003
        if not resp.startswith('257'):
            return ''
Tim Peters's avatar
Tim Peters committed
661 662 663 664 665 666 667 668 669 670
        return parse257(resp)

    def quit(self):
        '''Quit, and close the connection.'''
        resp = self.voidcmd('QUIT')
        self.close()
        return resp

    def close(self):
        '''Close the connection without assuming anything about it.'''
671 672 673 674 675 676 677 678 679 680
        try:
            file = self.file
            self.file = None
            if file is not None:
                file.close()
        finally:
            sock = self.sock
            self.sock = None
            if sock is not None:
                sock.close()
681

682 683
try:
    import ssl
684
except ImportError:
685
    _SSLSocket = None
686
else:
687 688
    _SSLSocket = ssl.SSLSocket

689 690 691 692 693 694 695 696 697 698 699 700 701
    class FTP_TLS(FTP):
        '''A FTP subclass which adds TLS support to FTP as described
        in RFC-4217.

        Connect as usual to port 21 implicitly securing the FTP control
        connection before authenticating.

        Securing the data connection requires user to explicitly ask
        for it by calling prot_p() method.

        Usage example:
        >>> from ftplib import FTP_TLS
        >>> ftps = FTP_TLS('ftp.python.org')
Ezio Melotti's avatar
Ezio Melotti committed
702
        >>> ftps.login()  # login anonymously previously securing control channel
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
        '230 Guest login ok, access restrictions apply.'
        >>> ftps.prot_p()  # switch to secure data connection
        '200 Protection level set to P'
        >>> ftps.retrlines('LIST')  # list directory content securely
        total 9
        drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 .
        drwxr-xr-x   8 root     wheel        1024 Jan  3  1994 ..
        drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 bin
        drwxr-xr-x   2 root     wheel        1024 Jan  3  1994 etc
        d-wxrwxr-x   2 ftp      wheel        1024 Sep  5 13:43 incoming
        drwxr-xr-x   2 root     wheel        1024 Nov 17  1993 lib
        drwxr-xr-x   6 1094     wheel        1024 Sep 13 19:07 pub
        drwxr-xr-x   3 root     wheel        1024 Jan  3  1994 usr
        -rw-r--r--   1 root     root          312 Aug  1  1994 welcome.msg
        '226 Transfer complete.'
        >>> ftps.quit()
        '221 Goodbye.'
        >>>
        '''
722
        ssl_version = ssl.PROTOCOL_TLS_CLIENT
723 724

        def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
725
                     certfile=None, context=None,
726
                     timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None):
727 728 729 730 731 732
            if context is not None and keyfile is not None:
                raise ValueError("context and keyfile arguments are mutually "
                                 "exclusive")
            if context is not None and certfile is not None:
                raise ValueError("context and certfile arguments are mutually "
                                 "exclusive")
733 734 735 736
            if keyfile is not None or certfile is not None:
                import warnings
                warnings.warn("keyfile and certfile are deprecated, use a"
                              "custom context instead", DeprecationWarning, 2)
737 738
            self.keyfile = keyfile
            self.certfile = certfile
739 740 741 742
            if context is None:
                context = ssl._create_stdlib_context(self.ssl_version,
                                                     certfile=certfile,
                                                     keyfile=keyfile)
743
            self.context = context
744
            self._prot_p = False
745
            FTP.__init__(self, host, user, passwd, acct, timeout, source_address)
746 747 748 749 750 751 752 753 754 755

        def login(self, user='', passwd='', acct='', secure=True):
            if secure and not isinstance(self.sock, ssl.SSLSocket):
                self.auth()
            return FTP.login(self, user, passwd, acct)

        def auth(self):
            '''Set up secure control connection by using TLS/SSL.'''
            if isinstance(self.sock, ssl.SSLSocket):
                raise ValueError("Already using TLS")
756
            if self.ssl_version >= ssl.PROTOCOL_TLS:
757 758 759
                resp = self.voidcmd('AUTH TLS')
            else:
                resp = self.voidcmd('AUTH SSL')
760
            self.sock = self.context.wrap_socket(self.sock,
761
                                                 server_hostname=self.host)
762 763 764
            self.file = self.sock.makefile(mode='r', encoding=self.encoding)
            return resp

765 766 767 768 769 770 771 772
        def ccc(self):
            '''Switch back to a clear-text control connection.'''
            if not isinstance(self.sock, ssl.SSLSocket):
                raise ValueError("not using TLS")
            resp = self.voidcmd('CCC')
            self.sock = self.sock.unwrap()
            return resp

773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
        def prot_p(self):
            '''Set up secure data connection.'''
            # PROT defines whether or not the data channel is to be protected.
            # Though RFC-2228 defines four possible protection levels,
            # RFC-4217 only recommends two, Clear and Private.
            # Clear (PROT C) means that no security is to be used on the
            # data-channel, Private (PROT P) means that the data-channel
            # should be protected by TLS.
            # PBSZ command MUST still be issued, but must have a parameter of
            # '0' to indicate that no buffering is taking place and the data
            # connection should not be encapsulated.
            self.voidcmd('PBSZ 0')
            resp = self.voidcmd('PROT P')
            self._prot_p = True
            return resp

        def prot_c(self):
            '''Set up clear text data connection.'''
            resp = self.voidcmd('PROT C')
            self._prot_p = False
            return resp

        # --- Overridden FTP methods

        def ntransfercmd(self, cmd, rest=None):
            conn, size = FTP.ntransfercmd(self, cmd, rest)
            if self._prot_p:
800
                conn = self.context.wrap_socket(conn,
801
                                                server_hostname=self.host)
802 803
            return conn, size

Giampaolo Rodola''s avatar
Giampaolo Rodola' committed
804 805 806 807 808 809 810 811 812
        def abort(self):
            # overridden as we can't pass MSG_OOB flag to sendall()
            line = b'ABOR' + B_CRLF
            self.sock.sendall(line)
            resp = self.getmultiline()
            if resp[:3] not in {'426', '225', '226'}:
                raise error_proto(resp)
            return resp

813
    __all__.append('FTP_TLS')
814
    all_errors = (Error, OSError, EOFError, ssl.SSLError)
815 816


817
_150_re = None
818 819

def parse150(resp):
Tim Peters's avatar
Tim Peters committed
820 821 822 823 824
    '''Parse the '150' response for a RETR request.
    Returns the expected transfer size or None; size is not guaranteed to
    be present in the 150 message.
    '''
    if resp[:3] != '150':
825
        raise error_reply(resp)
Tim Peters's avatar
Tim Peters committed
826 827 828
    global _150_re
    if _150_re is None:
        import re
829
        _150_re = re.compile(
830
            r"150 .* \((\d+) bytes\)", re.IGNORECASE | re.ASCII)
Tim Peters's avatar
Tim Peters committed
831
    m = _150_re.match(resp)
832 833
    if not m:
        return None
834
    return int(m.group(1))
835 836


837 838
_227_re = None

839
def parse227(resp):
Tim Peters's avatar
Tim Peters committed
840 841 842 843 844
    '''Parse the '227' response for a PASV request.
    Raises error_proto if it does not contain '(h1,h2,h3,h4,p1,p2)'
    Return ('host.addr.as.numbers', port#) tuple.'''

    if resp[:3] != '227':
845
        raise error_reply(resp)
846 847 848
    global _227_re
    if _227_re is None:
        import re
849
        _227_re = re.compile(r'(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)', re.ASCII)
850 851
    m = _227_re.search(resp)
    if not m:
852
        raise error_proto(resp)
853
    numbers = m.groups()
854 855
    host = '.'.join(numbers[:4])
    port = (int(numbers[4]) << 8) + int(numbers[5])
Tim Peters's avatar
Tim Peters committed
856
    return host, port
857 858


859
def parse229(resp, peer):
860
    '''Parse the '229' response for an EPSV request.
861 862 863
    Raises error_proto if it does not contain '(|||port|)'
    Return ('host.addr.as.numbers', port#) tuple.'''

864
    if resp[:3] != '229':
865
        raise error_reply(resp)
866
    left = resp.find('(')
867
    if left < 0: raise error_proto(resp)
868
    right = resp.find(')', left + 1)
869
    if right < 0:
870
        raise error_proto(resp) # should contain '(|||port|)'
871
    if resp[left + 1] != resp[right - 1]:
872
        raise error_proto(resp)
873
    parts = resp[left + 1:right].split(resp[left+1])
874
    if len(parts) != 5:
875
        raise error_proto(resp)
876
    host = peer[0]
877
    port = int(parts[3])
878 879 880
    return host, port


881
def parse257(resp):
Tim Peters's avatar
Tim Peters committed
882 883 884 885 886
    '''Parse the '257' response for a MKD or PWD request.
    This is a response to a MKD or PWD request: a directory name.
    Returns the directoryname in the 257 reply.'''

    if resp[:3] != '257':
887
        raise error_reply(resp)
Tim Peters's avatar
Tim Peters committed
888 889 890 891 892 893 894 895 896 897 898 899 900 901
    if resp[3:5] != ' "':
        return '' # Not compliant to RFC 959, but UNIX ftpd does this
    dirname = ''
    i = 5
    n = len(resp)
    while i < n:
        c = resp[i]
        i = i+1
        if c == '"':
            if i >= n or resp[i] != '"':
                break
            i = i+1
        dirname = dirname + c
    return dirname
Guido van Rossum's avatar
Guido van Rossum committed
902

903

904
def print_line(line):
Tim Peters's avatar
Tim Peters committed
905
    '''Default retrlines callback to print a line.'''
906
    print(line)
907

908

909
def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
Tim Peters's avatar
Tim Peters committed
910
    '''Copy file from one FTP-instance to another.'''
911 912
    if not targetname:
        targetname = sourcename
Tim Peters's avatar
Tim Peters committed
913 914 915 916 917 918 919 920 921
    type = 'TYPE ' + type
    source.voidcmd(type)
    target.voidcmd(type)
    sourcehost, sourceport = parse227(source.sendcmd('PASV'))
    target.sendport(sourcehost, sourceport)
    # RFC 959: the user must "listen" [...] BEFORE sending the
    # transfer request.
    # So: STOR before RETR, because here the target is a "user".
    treply = target.sendcmd('STOR ' + targetname)
922 923
    if treply[:3] not in {'125', '150'}:
        raise error_proto  # RFC 959
Tim Peters's avatar
Tim Peters committed
924
    sreply = source.sendcmd('RETR ' + sourcename)
925 926
    if sreply[:3] not in {'125', '150'}:
        raise error_proto  # RFC 959
Tim Peters's avatar
Tim Peters committed
927 928 929 930
    source.voidresp()
    target.voidresp()


Guido van Rossum's avatar
Guido van Rossum committed
931
def test():
Tim Peters's avatar
Tim Peters committed
932
    '''Test program.
933 934 935 936 937 938 939 940
    Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...

    -d dir
    -l list
    -p password
    '''

    if len(sys.argv) < 2:
941
        print(test.__doc__)
942
        sys.exit(0)
Tim Peters's avatar
Tim Peters committed
943

944 945
    import netrc

Tim Peters's avatar
Tim Peters committed
946 947 948 949 950 951 952 953 954 955 956 957 958 959
    debugging = 0
    rcfile = None
    while sys.argv[1] == '-d':
        debugging = debugging+1
        del sys.argv[1]
    if sys.argv[1][:2] == '-r':
        # get name of alternate ~/.netrc file:
        rcfile = sys.argv[1][2:]
        del sys.argv[1]
    host = sys.argv[1]
    ftp = FTP(host)
    ftp.set_debuglevel(debugging)
    userid = passwd = acct = ''
    try:
960
        netrcobj = netrc.netrc(rcfile)
961
    except OSError:
Tim Peters's avatar
Tim Peters committed
962 963 964 965 966
        if rcfile is not None:
            sys.stderr.write("Could not open account file"
                             " -- using anonymous login.")
    else:
        try:
967
            userid, acct, passwd = netrcobj.authenticators(host)
Tim Peters's avatar
Tim Peters committed
968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985
        except KeyError:
            # no account for host
            sys.stderr.write(
                    "No account -- using anonymous login.")
    ftp.login(userid, passwd, acct)
    for file in sys.argv[2:]:
        if file[:2] == '-l':
            ftp.dir(file[2:])
        elif file[:2] == '-d':
            cmd = 'CWD'
            if file[2:]: cmd = cmd + ' ' + file[2:]
            resp = ftp.sendcmd(cmd)
        elif file == '-p':
            ftp.set_pasv(not ftp.passiveserver)
        else:
            ftp.retrbinary('RETR ' + file, \
                           sys.stdout.write, 1024)
    ftp.quit()
986 987 988


if __name__ == '__main__':
Tim Peters's avatar
Tim Peters committed
989
    test()