Changeset 103

Show
Ignore:
Timestamp:
2002-03-29 21:47:11 (7 years ago)
Author:
schwa
Message:
Module: added skeleton for MockSocket (returned by transfercmd).
Module: renamed FTP class to MockSession.
Module: added class FailOnLoginSession.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/_mock_ftplib.py

    r102 r103  
    3030# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
    3131 
    32 # $Id: _mock_ftplib.py,v 1.3 2002/03/29 18:50:33 schwa Exp $ 
     32# $Id: _mock_ftplib.py,v 1.4 2002/03/29 21:47:11 schwa Exp $ 
    3333 
    3434""" 
     
    4242import ftplib 
    4343 
    44 class FTP(ftplib.FTP): 
     44try: 
     45    import cStringIO as StringIO 
     46except ImportError: 
     47    import StringIO 
     48 
     49 
     50class MockSocket: 
     51    def __init__(self, mock_socket_file_contents=''): 
     52        self.mock_socket_file_contents = mock_socket_file_contents 
     53 
     54    def makefile(self, mode): 
     55        return StringIO.StringIO(self.mock_socket_file_contents) 
     56 
     57 
     58class MockSession: 
    4559    """ 
    4660    Mock implementation of ftplib.FTP . For information on mock 
    4761    objects see http://www.mockobjects.com/ . 
    4862    """ 
    49     debugging = 0 
     63 
     64    # taken from ftplib.FTP 
    5065    host = '' 
    51     port = ftplib.FTP_PORT 
    5266    sock = None 
    5367    file = None 
    5468    welcome = None 
    5569    passiveserver = 1 
     70 
     71    # mock object settings 
     72    voidresp_raise = None 
     73    voidresp_result = None 
     74    sendcmd_result = None 
     75    voidcmd_exception = None 
     76    voidcmd_result = None 
     77    mock_socket_file_contents = '' 
     78    login_raise = None 
     79    login_result = None 
    5680 
    5781    # Initialization method (called by class instantiation). 
     
    7094        if host: self.host = host 
    7195        if port: self.port = port 
    72         self.passiveserver = 0 
    73         msg = "getaddrinfo returns an empty list" 
    74         for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): 
    75             af, socktype, proto, canonname, sa = res 
    76             try: 
    77                 self.sock = socket.socket(af, socktype, proto) 
    78                 self.sock.connect(sa) 
    79             except socket.error, msg: 
    80                 if self.sock: 
    81                     self.sock.close() 
    82                 self.sock = None 
    83                 continue 
    84             break 
    85         if not self.sock: 
    86             raise socket.error, msg 
    87         self.af = af 
    88         self.file = self.sock.makefile('rb') 
    89         self.welcome = self.getresp() 
     96        self.passiveserver = 1 
     97        self.file = StringIO.StringIO() 
     98        self.welcome = 'Welcome to the MockSession class! ;-)' 
    9099        return self.welcome 
    91100 
     
    93102        """Get the welcome message from the server. 
    94103        (this is read and squirreled away by connect())""" 
    95         if self.debugging: 
    96             print '*welcome*', self.sanitize(self.welcome) 
    97104        return self.welcome 
    98  
    99     def set_debuglevel(self, level): 
    100         """Set the debugging level. 
    101         The required argument level means: 
    102         0: no debugging output (default) 
    103         1: print commands and responses but not body text etc. 
    104         2: also print raw lines read and sent before stripping CR/LF""" 
    105         self.debugging = level 
    106     debug = set_debuglevel 
    107105 
    108106    def set_pasv(self, val): 
     
    112110        self.passiveserver = val 
    113111 
    114     # Internal: "sanitize" a string for printing 
    115     def sanitize(self, s): 
    116         if s[:5] == 'pass ' or s[:5] == 'PASS ': 
    117             i = len(s) 
    118             while i > 5 and s[i-1] in '\r\n': 
    119                 i = i-1 
    120             s = s[:5] + '*'*(i-5) + s[i:] 
    121         return `s` 
    122  
    123     # Internal: send one line to the server, appending CRLF 
    124     def putline(self, line): 
    125         line = line + CRLF 
    126         if self.debugging > 1: print '*put*', self.sanitize(line) 
    127         self.sock.send(line) 
    128  
    129     # Internal: send one command to the server (through putline()) 
    130     def putcmd(self, line): 
    131         if self.debugging: print '*cmd*', self.sanitize(line) 
    132         self.putline(line) 
    133  
    134     # Internal: return one line from the server, stripping CRLF. 
    135     # Raise EOFError if the connection is closed 
    136     def getline(self): 
    137         line = self.file.readline() 
    138         if self.debugging > 1: 
    139             print '*get*', self.sanitize(line) 
    140         if not line: raise EOFError 
    141         if line[-2:] == CRLF: line = line[:-2] 
    142         elif line[-1:] in CRLF: line = line[:-1] 
    143         return line 
    144  
    145     # Internal: get a response from the server, which may possibly 
    146     # consist of multiple lines.  Return a single string with no 
    147     # trailing CRLF.  If the response consists of multiple lines, 
    148     # these are separated by '\n' characters in the string 
    149     def getmultiline(self): 
    150         line = self.getline() 
    151         if line[3:4] == '-': 
    152             code = line[:3] 
    153             while 1: 
    154                 nextline = self.getline() 
    155                 line = line + ('\n' + nextline) 
    156                 if nextline[:3] == code and \ 
    157                         nextline[3:4] != '-': 
    158                     break 
    159         return line 
    160  
    161     # Internal: get a response from the server. 
    162     # Raise various errors if the response indicates an error 
    163     def getresp(self): 
    164         resp = self.getmultiline() 
    165         if self.debugging: print '*resp*', self.sanitize(resp) 
    166         self.lastresp = resp[:3] 
    167         c = resp[:1] 
    168         if c == '4': 
    169             raise error_temp, resp 
    170         if c == '5': 
    171             raise error_perm, resp 
    172         if c not in '123': 
    173             raise error_proto, resp 
    174         return resp 
    175  
    176112    def voidresp(self): 
    177113        """Expect a response beginning with '2'.""" 
    178         resp = self.getresp() 
    179         if resp[0] != '2': 
    180             raise error_reply, resp 
    181         return resp 
    182  
    183     def abort(self): 
    184         """Abort a file transfer.  Uses out-of-band data. 
    185         This does not follow the procedure from the RFC to send Telnet 
    186         IP and Synch; that doesn't seem to work with the servers I've 
    187         tried.  Instead, just send the ABOR command as OOB data.""" 
    188         line = 'ABOR' + CRLF 
    189         if self.debugging > 1: print '*put urgent*', self.sanitize(line) 
    190         self.sock.send(line, MSG_OOB) 
    191         resp = self.getmultiline() 
    192         if resp[:3] not in ('426', '226'): 
    193             raise error_proto, resp 
     114        if self.voidresp_raise: 
     115            raise ftplib.error_reply, resp 
     116        return self.voidresp_result 
    194117 
    195118    def sendcmd(self, cmd): 
    196119        """Send a command and return the response.""" 
    197         self.putcmd(cmd) 
    198         return self.getresp() 
     120        return self.sendcmd_result 
    199121 
    200122    def voidcmd(self, cmd): 
    201123        """Send a command and expect a response beginning with '2'.""" 
    202         self.putcmd(cmd) 
    203         return self.voidresp() 
    204  
    205     def sendport(self, host, port): 
    206         """Send a PORT command with the current host and the given 
    207         port number. 
    208         """ 
    209         hbytes = host.split('.') 
    210         pbytes = [`port/256`, `port%256`] 
    211         bytes = hbytes + pbytes 
    212         cmd = 'PORT ' + ','.join(bytes) 
    213         return self.voidcmd(cmd) 
    214  
    215     def sendeprt(self, host, port): 
    216         """Send a EPRT command with the current host and the given port number.""" 
    217         af = 0 
    218         if self.af == socket.AF_INET: 
    219             af = 1 
    220         if self.af == socket.AF_INET6: 
    221             af = 2 
    222         if af == 0: 
    223             raise error_proto, 'unsupported address family' 
    224         fields = ['', `af`, host, `port`, ''] 
    225         cmd = 'EPRT ' + string.joinfields(fields, '|') 
    226         return self.voidcmd(cmd) 
    227  
    228     def makeport(self): 
    229         """Create a new socket and send a PORT command for it.""" 
    230         msg = "getaddrinfo returns an empty list" 
    231         sock = None 
    232         for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): 
    233             af, socktype, proto, canonname, sa = res 
    234             try: 
    235                 sock = socket.socket(af, socktype, proto) 
    236                 sock.bind(sa) 
    237             except socket.error, msg: 
    238                 if sock: 
    239                     sock.close() 
    240                 sock = None 
    241                 continue 
    242             break 
    243         if not sock: 
    244             raise socket.error, msg 
    245         sock.listen(1) 
    246         port = sock.getsockname()[1] # Get proper port 
    247         host = self.sock.getsockname()[0] # Get proper host 
    248         if self.af == socket.AF_INET: 
    249             resp = self.sendport(host, port) 
    250         else: 
    251             resp = self.sendeprt(host, port) 
    252         return sock 
    253  
    254     def makepasv(self): 
    255         if self.af == socket.AF_INET: 
    256             host, port = parse227(self.sendcmd('PASV')) 
    257         else: 
    258             host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername()) 
    259         return host, port 
     124        if self.voidcmd_exception is not None: 
     125            raise self.voidcmd_exception 
     126        return self.voidcmd_result 
    260127 
    261128    def ntransfercmd(self, cmd, rest=None): 
     
    274141        given marker. 
    275142        """ 
    276         size = None 
    277         if self.passiveserver: 
    278             host, port = self.makepasv() 
    279             af, socktype, proto, canon, sa = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)[0] 
    280             conn = socket.socket(af, socktype, proto) 
    281             conn.connect(sa) 
    282             if rest is not None: 
    283                 self.sendcmd("REST %s" % rest) 
    284             resp = self.sendcmd(cmd) 
    285             if resp[0] != '1': 
    286                 raise error_reply, resp 
    287         else: 
    288             sock = self.makeport() 
    289             if rest is not None: 
    290                 self.sendcmd("REST %s" % rest) 
    291             resp = self.sendcmd(cmd) 
    292             if resp[0] != '1': 
    293                 raise error_reply, resp 
    294             conn, sockaddr = sock.accept() 
    295         if resp[:3] == '150': 
    296             # this is conditional in case we received a 125 
    297             size = parse150(resp) 
     143        conn = MockSocket(self.mock_socket_file_contents) 
     144        size = self.ntransfercmd_size 
    298145        return conn, size 
    299146 
     
    302149        return self.ntransfercmd(cmd, rest)[0] 
    303150 
    304     def login(self, user = '', passwd = '', acct = ''): 
     151    def login(self, user='', passwd='', acct=''): 
    305152        """Login, default anonymous.""" 
    306         if not user: user = 'anonymous' 
    307         if not passwd: passwd = '' 
    308         if not acct: acct = '' 
    309         if user == 'anonymous' and passwd in ('', '-'): 
    310             # get fully qualified domain name of local host 
    311             thishost = socket.getfqdn() 
    312             try: 
    313                 if os.environ.has_key('LOGNAME'): 
    314                     realuser = os.environ['LOGNAME'] 
    315                 elif os.environ.has_key('USER'): 
    316                     realuser = os.environ['USER'] 
    317                 else: 
    318                     realuser = 'anonymous' 
    319             except AttributeError: 
    320                 # Not all systems have os.environ.... 
    321                 realuser = 'anonymous' 
    322             passwd = passwd + realuser + '@' + thishost 
    323         resp = self.sendcmd('USER ' + user) 
    324         if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd) 
    325         if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct) 
    326         if resp[0] != '2': 
    327             raise error_reply, resp 
    328         return resp 
     153        if self.login_raise: 
     154            raise ftplib.error_reply, self.login_result 
     155        return self.login_result 
    329156 
    330157    def retrbinary(self, cmd, callback, blocksize=8192, rest=None): 
     
    493320            self.sock.close() 
    494321            self.file = self.sock = None 
     322 
     323 
     324class FailOnLoginSession: 
     325    def __init__(self, host='', user='', password=''): 
     326        raise ftplib.error_perm 
     327