home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Calibre / calibre-0.8.18.msi / file_262 / imaplib.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2011-09-09  |  28.0 KB  |  934 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.7)
  3.  
  4. __version__ = '2.58'
  5. import binascii
  6. import errno
  7. import random
  8. import re
  9. import socket
  10. import subprocess
  11. import sys
  12. import time
  13. __all__ = [
  14.     'IMAP4',
  15.     'IMAP4_stream',
  16.     'Internaldate2tuple',
  17.     'Int2AP',
  18.     'ParseFlags',
  19.     'Time2Internaldate']
  20. CRLF = '\r\n'
  21. Debug = 0
  22. IMAP4_PORT = 143
  23. IMAP4_SSL_PORT = 993
  24. AllowedVersions = ('IMAP4REV1', 'IMAP4')
  25. Commands = {
  26.     'APPEND': ('AUTH', 'SELECTED'),
  27.     'AUTHENTICATE': ('NONAUTH',),
  28.     'CAPABILITY': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
  29.     'CHECK': ('SELECTED',),
  30.     'CLOSE': ('SELECTED',),
  31.     'COPY': ('SELECTED',),
  32.     'CREATE': ('AUTH', 'SELECTED'),
  33.     'DELETE': ('AUTH', 'SELECTED'),
  34.     'DELETEACL': ('AUTH', 'SELECTED'),
  35.     'EXAMINE': ('AUTH', 'SELECTED'),
  36.     'EXPUNGE': ('SELECTED',),
  37.     'FETCH': ('SELECTED',),
  38.     'GETACL': ('AUTH', 'SELECTED'),
  39.     'GETANNOTATION': ('AUTH', 'SELECTED'),
  40.     'GETQUOTA': ('AUTH', 'SELECTED'),
  41.     'GETQUOTAROOT': ('AUTH', 'SELECTED'),
  42.     'MYRIGHTS': ('AUTH', 'SELECTED'),
  43.     'LIST': ('AUTH', 'SELECTED'),
  44.     'LOGIN': ('NONAUTH',),
  45.     'LOGOUT': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
  46.     'LSUB': ('AUTH', 'SELECTED'),
  47.     'NAMESPACE': ('AUTH', 'SELECTED'),
  48.     'NOOP': ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'),
  49.     'PARTIAL': ('SELECTED',),
  50.     'PROXYAUTH': ('AUTH',),
  51.     'RENAME': ('AUTH', 'SELECTED'),
  52.     'SEARCH': ('SELECTED',),
  53.     'SELECT': ('AUTH', 'SELECTED'),
  54.     'SETACL': ('AUTH', 'SELECTED'),
  55.     'SETANNOTATION': ('AUTH', 'SELECTED'),
  56.     'SETQUOTA': ('AUTH', 'SELECTED'),
  57.     'SORT': ('SELECTED',),
  58.     'STATUS': ('AUTH', 'SELECTED'),
  59.     'STORE': ('SELECTED',),
  60.     'SUBSCRIBE': ('AUTH', 'SELECTED'),
  61.     'THREAD': ('SELECTED',),
  62.     'UID': ('SELECTED',),
  63.     'UNSUBSCRIBE': ('AUTH', 'SELECTED') }
  64. Continuation = re.compile('\\+( (?P<data>.*))?')
  65. Flags = re.compile('.*FLAGS \\((?P<flags>[^\\)]*)\\)')
  66. InternalDate = re.compile('.*INTERNALDATE "(?P<day>[ 0123][0-9])-(?P<mon>[A-Z][a-z][a-z])-(?P<year>[0-9][0-9][0-9][0-9]) (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9]) (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])"')
  67. Literal = re.compile('.*{(?P<size>\\d+)}$')
  68. MapCRLF = re.compile('\\r\\n|\\r|\\n')
  69. Response_code = re.compile('\\[(?P<type>[A-Z-]+)( (?P<data>[^\\]]*))?\\]')
  70. Untagged_response = re.compile('\\* (?P<type>[A-Z-]+)( (?P<data>.*))?')
  71. Untagged_status = re.compile('\\* (?P<data>\\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?')
  72.  
  73. class IMAP4:
  74.     
  75.     class error(Exception):
  76.         pass
  77.  
  78.     
  79.     class abort(error):
  80.         pass
  81.  
  82.     
  83.     class readonly(abort):
  84.         pass
  85.  
  86.     mustquote = re.compile("[^\\w!#$%&'*+,.:;<=>?^`|~-]")
  87.     
  88.     def __init__(self, host = '', port = IMAP4_PORT):
  89.         self.debug = Debug
  90.         self.state = 'LOGOUT'
  91.         self.literal = None
  92.         self.tagged_commands = { }
  93.         self.untagged_responses = { }
  94.         self.continuation_response = ''
  95.         self.is_readonly = False
  96.         self.tagnum = 0
  97.         self.open(host, port)
  98.         self.tagpre = Int2AP(random.randint(4096, 65535))
  99.         self.tagre = re.compile('(?P<tag>' + self.tagpre + '\\d+) (?P<type>[A-Z]+) (?P<data>.*)')
  100.         self.welcome = self._get_response()
  101.         if 'PREAUTH' in self.untagged_responses:
  102.             self.state = 'AUTH'
  103.         elif 'OK' in self.untagged_responses:
  104.             self.state = 'NONAUTH'
  105.         else:
  106.             raise self.error(self.welcome)
  107.         (typ, dat) = None.capability()
  108.         if dat == [
  109.             None]:
  110.             raise self.error('no CAPABILITY response from server')
  111.         self.capabilities = tuple(dat[-1].upper().split())
  112.         for version in AllowedVersions:
  113.             if version not in self.capabilities:
  114.                 continue
  115.             self.PROTOCOL_VERSION = version
  116.             return None
  117.         
  118.         raise self.error('server not IMAP4 compliant')
  119.  
  120.     
  121.     def __getattr__(self, attr):
  122.         if attr in Commands:
  123.             return getattr(self, attr.lower())
  124.         raise None("Unknown IMAP4 command: '%s'" % attr)
  125.  
  126.     
  127.     def open(self, host = '', port = IMAP4_PORT):
  128.         self.host = host
  129.         self.port = port
  130.         self.sock = socket.create_connection((host, port))
  131.         self.file = self.sock.makefile('rb')
  132.  
  133.     
  134.     def read(self, size):
  135.         return self.file.read(size)
  136.  
  137.     
  138.     def readline(self):
  139.         return self.file.readline()
  140.  
  141.     
  142.     def send(self, data):
  143.         self.sock.sendall(data)
  144.  
  145.     
  146.     def shutdown(self):
  147.         self.file.close()
  148.         
  149.         try:
  150.             self.sock.shutdown(socket.SHUT_RDWR)
  151.         except socket.error:
  152.             e = None
  153.             if e.errno != errno.ENOTCONN:
  154.                 raise 
  155.         finally:
  156.             self.sock.close()
  157.  
  158.  
  159.     
  160.     def socket(self):
  161.         return self.sock
  162.  
  163.     
  164.     def recent(self):
  165.         name = 'RECENT'
  166.         (typ, dat) = self._untagged_response('OK', [
  167.             None], name)
  168.         if dat[-1]:
  169.             return (typ, dat)
  170.         (typ, dat) = None.noop()
  171.         return self._untagged_response(typ, dat, name)
  172.  
  173.     
  174.     def response(self, code):
  175.         return self._untagged_response(code, [
  176.             None], code.upper())
  177.  
  178.     
  179.     def append(self, mailbox, flags, date_time, message):
  180.         name = 'APPEND'
  181.         if not mailbox:
  182.             mailbox = 'INBOX'
  183.         if flags or (flags[0], flags[-1]) != ('(', ')'):
  184.             flags = '(%s)' % flags
  185.         
  186.         flags = None
  187.         if date_time:
  188.             date_time = Time2Internaldate(date_time)
  189.         else:
  190.             date_time = None
  191.         self.literal = MapCRLF.sub(CRLF, message)
  192.         return self._simple_command(name, mailbox, flags, date_time)
  193.  
  194.     
  195.     def authenticate(self, mechanism, authobject):
  196.         mech = mechanism.upper()
  197.         self.literal = _Authenticator(authobject).process
  198.         (typ, dat) = self._simple_command('AUTHENTICATE', mech)
  199.         if typ != 'OK':
  200.             raise self.error(dat[-1])
  201.         self.state = 'AUTH'
  202.         return (typ, dat)
  203.  
  204.     
  205.     def capability(self):
  206.         name = 'CAPABILITY'
  207.         (typ, dat) = self._simple_command(name)
  208.         return self._untagged_response(typ, dat, name)
  209.  
  210.     
  211.     def check(self):
  212.         return self._simple_command('CHECK')
  213.  
  214.     
  215.     def close(self):
  216.         
  217.         try:
  218.             (typ, dat) = self._simple_command('CLOSE')
  219.         finally:
  220.             self.state = 'AUTH'
  221.  
  222.         return (typ, dat)
  223.  
  224.     
  225.     def copy(self, message_set, new_mailbox):
  226.         return self._simple_command('COPY', message_set, new_mailbox)
  227.  
  228.     
  229.     def create(self, mailbox):
  230.         return self._simple_command('CREATE', mailbox)
  231.  
  232.     
  233.     def delete(self, mailbox):
  234.         return self._simple_command('DELETE', mailbox)
  235.  
  236.     
  237.     def deleteacl(self, mailbox, who):
  238.         return self._simple_command('DELETEACL', mailbox, who)
  239.  
  240.     
  241.     def expunge(self):
  242.         name = 'EXPUNGE'
  243.         (typ, dat) = self._simple_command(name)
  244.         return self._untagged_response(typ, dat, name)
  245.  
  246.     
  247.     def fetch(self, message_set, message_parts):
  248.         name = 'FETCH'
  249.         (typ, dat) = self._simple_command(name, message_set, message_parts)
  250.         return self._untagged_response(typ, dat, name)
  251.  
  252.     
  253.     def getacl(self, mailbox):
  254.         (typ, dat) = self._simple_command('GETACL', mailbox)
  255.         return self._untagged_response(typ, dat, 'ACL')
  256.  
  257.     
  258.     def getannotation(self, mailbox, entry, attribute):
  259.         (typ, dat) = self._simple_command('GETANNOTATION', mailbox, entry, attribute)
  260.         return self._untagged_response(typ, dat, 'ANNOTATION')
  261.  
  262.     
  263.     def getquota(self, root):
  264.         (typ, dat) = self._simple_command('GETQUOTA', root)
  265.         return self._untagged_response(typ, dat, 'QUOTA')
  266.  
  267.     
  268.     def getquotaroot(self, mailbox):
  269.         (typ, dat) = self._simple_command('GETQUOTAROOT', mailbox)
  270.         (typ, quota) = self._untagged_response(typ, dat, 'QUOTA')
  271.         (typ, quotaroot) = self._untagged_response(typ, dat, 'QUOTAROOT')
  272.         return (typ, [
  273.             quotaroot,
  274.             quota])
  275.  
  276.     
  277.     def list(self, directory = '""', pattern = '*'):
  278.         name = 'LIST'
  279.         (typ, dat) = self._simple_command(name, directory, pattern)
  280.         return self._untagged_response(typ, dat, name)
  281.  
  282.     
  283.     def login(self, user, password):
  284.         (typ, dat) = self._simple_command('LOGIN', user, self._quote(password))
  285.         if typ != 'OK':
  286.             raise self.error(dat[-1])
  287.         self.state = 'AUTH'
  288.         return (typ, dat)
  289.  
  290.     
  291.     def login_cram_md5(self, user, password):
  292.         self.user = user
  293.         self.password = password
  294.         return self.authenticate('CRAM-MD5', self._CRAM_MD5_AUTH)
  295.  
  296.     
  297.     def _CRAM_MD5_AUTH(self, challenge):
  298.         import hmac
  299.         return self.user + ' ' + hmac.HMAC(self.password, challenge).hexdigest()
  300.  
  301.     
  302.     def logout(self):
  303.         self.state = 'LOGOUT'
  304.         
  305.         try:
  306.             (typ, dat) = self._simple_command('LOGOUT')
  307.         except:
  308.             typ = 'NO'
  309.             dat = [
  310.                 '%s: %s' % sys.exc_info()[:2]]
  311.  
  312.         self.shutdown()
  313.         if 'BYE' in self.untagged_responses:
  314.             return ('BYE', self.untagged_responses['BYE'])
  315.         return (None, dat)
  316.  
  317.     
  318.     def lsub(self, directory = '""', pattern = '*'):
  319.         name = 'LSUB'
  320.         (typ, dat) = self._simple_command(name, directory, pattern)
  321.         return self._untagged_response(typ, dat, name)
  322.  
  323.     
  324.     def myrights(self, mailbox):
  325.         (typ, dat) = self._simple_command('MYRIGHTS', mailbox)
  326.         return self._untagged_response(typ, dat, 'MYRIGHTS')
  327.  
  328.     
  329.     def namespace(self):
  330.         name = 'NAMESPACE'
  331.         (typ, dat) = self._simple_command(name)
  332.         return self._untagged_response(typ, dat, name)
  333.  
  334.     
  335.     def noop(self):
  336.         return self._simple_command('NOOP')
  337.  
  338.     
  339.     def partial(self, message_num, message_part, start, length):
  340.         name = 'PARTIAL'
  341.         (typ, dat) = self._simple_command(name, message_num, message_part, start, length)
  342.         return self._untagged_response(typ, dat, 'FETCH')
  343.  
  344.     
  345.     def proxyauth(self, user):
  346.         name = 'PROXYAUTH'
  347.         return self._simple_command('PROXYAUTH', user)
  348.  
  349.     
  350.     def rename(self, oldmailbox, newmailbox):
  351.         return self._simple_command('RENAME', oldmailbox, newmailbox)
  352.  
  353.     
  354.     def search(self, charset, *criteria):
  355.         name = 'SEARCH'
  356.         if charset:
  357.             (typ, dat) = self._simple_command(name, 'CHARSET', charset, *criteria)
  358.         else:
  359.             (typ, dat) = self._simple_command(name, *criteria)
  360.         return self._untagged_response(typ, dat, name)
  361.  
  362.     
  363.     def select(self, mailbox = 'INBOX', readonly = False):
  364.         self.untagged_responses = { }
  365.         self.is_readonly = readonly
  366.         if readonly:
  367.             name = 'EXAMINE'
  368.         else:
  369.             name = 'SELECT'
  370.         (typ, dat) = self._simple_command(name, mailbox)
  371.         if typ != 'OK':
  372.             self.state = 'AUTH'
  373.             return (typ, dat)
  374.         self.state = None
  375.         if 'READ-ONLY' in self.untagged_responses and not readonly:
  376.             raise self.readonly('%s is not writable' % mailbox)
  377.         return (typ, self.untagged_responses.get('EXISTS', [
  378.             None]))
  379.  
  380.     
  381.     def setacl(self, mailbox, who, what):
  382.         return self._simple_command('SETACL', mailbox, who, what)
  383.  
  384.     
  385.     def setannotation(self, *args):
  386.         (typ, dat) = self._simple_command('SETANNOTATION', *args)
  387.         return self._untagged_response(typ, dat, 'ANNOTATION')
  388.  
  389.     
  390.     def setquota(self, root, limits):
  391.         (typ, dat) = self._simple_command('SETQUOTA', root, limits)
  392.         return self._untagged_response(typ, dat, 'QUOTA')
  393.  
  394.     
  395.     def sort(self, sort_criteria, charset, *search_criteria):
  396.         name = 'SORT'
  397.         if (sort_criteria[0], sort_criteria[-1]) != ('(', ')'):
  398.             sort_criteria = '(%s)' % sort_criteria
  399.         (typ, dat) = self._simple_command(name, sort_criteria, charset, *search_criteria)
  400.         return self._untagged_response(typ, dat, name)
  401.  
  402.     
  403.     def status(self, mailbox, names):
  404.         name = 'STATUS'
  405.         (typ, dat) = self._simple_command(name, mailbox, names)
  406.         return self._untagged_response(typ, dat, name)
  407.  
  408.     
  409.     def store(self, message_set, command, flags):
  410.         if (flags[0], flags[-1]) != ('(', ')'):
  411.             flags = '(%s)' % flags
  412.         (typ, dat) = self._simple_command('STORE', message_set, command, flags)
  413.         return self._untagged_response(typ, dat, 'FETCH')
  414.  
  415.     
  416.     def subscribe(self, mailbox):
  417.         return self._simple_command('SUBSCRIBE', mailbox)
  418.  
  419.     
  420.     def thread(self, threading_algorithm, charset, *search_criteria):
  421.         name = 'THREAD'
  422.         (typ, dat) = self._simple_command(name, threading_algorithm, charset, *search_criteria)
  423.         return self._untagged_response(typ, dat, name)
  424.  
  425.     
  426.     def uid(self, command, *args):
  427.         command = command.upper()
  428.         if command not in Commands:
  429.             raise self.error('Unknown IMAP4 UID command: %s' % command)
  430.         if self.state not in Commands[command]:
  431.             raise self.error('command %s illegal in state %s, only allowed in states %s' % (command, self.state, ', '.join(Commands[command])))
  432.         name = 'UID'
  433.         (typ, dat) = self._simple_command(name, command, *args)
  434.         if command in ('SEARCH', 'SORT', 'THREAD'):
  435.             name = command
  436.         else:
  437.             name = 'FETCH'
  438.         return self._untagged_response(typ, dat, name)
  439.  
  440.     
  441.     def unsubscribe(self, mailbox):
  442.         return self._simple_command('UNSUBSCRIBE', mailbox)
  443.  
  444.     
  445.     def xatom(self, name, *args):
  446.         name = name.upper()
  447.         if name not in Commands:
  448.             Commands[name] = (self.state,)
  449.         return self._simple_command(name, *args)
  450.  
  451.     
  452.     def _append_untagged(self, typ, dat):
  453.         if dat is None:
  454.             dat = ''
  455.         ur = self.untagged_responses
  456.         if typ in ur:
  457.             ur[typ].append(dat)
  458.         else:
  459.             ur[typ] = [
  460.                 dat]
  461.  
  462.     
  463.     def _check_bye(self):
  464.         bye = self.untagged_responses.get('BYE')
  465.         if bye:
  466.             raise self.abort(bye[-1])
  467.  
  468.     
  469.     def _command(self, name, *args):
  470.         if self.state not in Commands[name]:
  471.             self.literal = None
  472.             raise self.error('command %s illegal in state %s, only allowed in states %s' % (name, self.state, ', '.join(Commands[name])))
  473.         for typ in ('OK', 'NO', 'BAD'):
  474.             if typ in self.untagged_responses:
  475.                 del self.untagged_responses[typ]
  476.                 continue
  477.         if 'READ-ONLY' in self.untagged_responses and not (self.is_readonly):
  478.             raise self.readonly('mailbox status changed to READ-ONLY')
  479.         tag = self._new_tag()
  480.         data = '%s %s' % (tag, name)
  481.         for arg in args:
  482.             if arg is None:
  483.                 continue
  484.             data = '%s %s' % (data, self._checkquote(arg))
  485.         
  486.         literal = self.literal
  487.         if literal is not None:
  488.             self.literal = None
  489.             if type(literal) is type(self._command):
  490.                 literator = literal
  491.             else:
  492.                 literator = None
  493.                 data = '%s {%s}' % (data, len(literal))
  494.         
  495.         try:
  496.             self.send('%s%s' % (data, CRLF))
  497.         except (socket.error, OSError):
  498.             val = None
  499.             raise self.abort('socket error: %s' % val)
  500.  
  501.         if literal is None:
  502.             return tag
  503.         while self._get_response():
  504.             if self.tagged_commands[tag]:
  505.                 return tag
  506.         if literator:
  507.             literal = literator(self.continuation_response)
  508.         
  509.         try:
  510.             self.send(literal)
  511.             self.send(CRLF)
  512.         except (socket.error, OSError):
  513.             val = None
  514.             raise self.abort('socket error: %s' % val)
  515.  
  516.         if not literator:
  517.             break
  518.             continue
  519.             continue
  520.             return tag
  521.  
  522.     
  523.     def _command_complete(self, name, tag):
  524.         if name != 'LOGOUT':
  525.             self._check_bye()
  526.         
  527.         try:
  528.             (typ, data) = self._get_tagged_response(tag)
  529.         except self.abort:
  530.             val = None
  531.             raise self.abort('command: %s => %s' % (name, val))
  532.         except self.error:
  533.             val = None
  534.             raise self.error('command: %s => %s' % (name, val))
  535.  
  536.         if name != 'LOGOUT':
  537.             self._check_bye()
  538.         if typ == 'BAD':
  539.             raise self.error('%s command error: %s %s' % (name, typ, data))
  540.         return (typ, data)
  541.  
  542.     
  543.     def _get_response(self):
  544.         resp = self._get_line()
  545.         if self._match(self.tagre, resp):
  546.             tag = self.mo.group('tag')
  547.             if tag not in self.tagged_commands:
  548.                 raise self.abort('unexpected tagged response: %s' % resp)
  549.             typ = self.mo.group('type')
  550.             dat = self.mo.group('data')
  551.             self.tagged_commands[tag] = (typ, [
  552.                 dat])
  553.         else:
  554.             dat2 = None
  555.             if self._match(Untagged_response, resp) and self._match(Untagged_status, resp):
  556.                 dat2 = self.mo.group('data2')
  557.             
  558.         if self.mo is None:
  559.             if self._match(Continuation, resp):
  560.                 self.continuation_response = self.mo.group('data')
  561.                 return None
  562.             raise None.abort("unexpected response: '%s'" % resp)
  563.         typ = self.mo.group('type')
  564.         dat = self.mo.group('data')
  565.         if dat is None:
  566.             dat = ''
  567.         if dat2:
  568.             dat = dat + ' ' + dat2
  569.         while self._match(Literal, dat):
  570.             size = int(self.mo.group('size'))
  571.             data = self.read(size)
  572.             self._append_untagged(typ, (dat, data))
  573.             dat = self._get_line()
  574.         self._append_untagged(typ, dat)
  575.         if typ in ('OK', 'NO', 'BAD') and self._match(Response_code, dat):
  576.             self._append_untagged(self.mo.group('type'), self.mo.group('data'))
  577.         return resp
  578.  
  579.     
  580.     def _get_tagged_response(self, tag):
  581.         while None:
  582.             result = self.tagged_commands[tag]
  583.             if result is not None:
  584.                 del self.tagged_commands[tag]
  585.                 return result
  586.         continue
  587.         except self.abort:
  588.             val = None
  589.             raise 
  590.             continue
  591.         
  592.  
  593.     
  594.     def _get_line(self):
  595.         line = self.readline()
  596.         if not line:
  597.             raise self.abort('socket error: EOF')
  598.         if not line.endswith('\r\n'):
  599.             raise self.abort('socket error: unterminated line')
  600.         line = line[:-2]
  601.         return line
  602.  
  603.     
  604.     def _match(self, cre, s):
  605.         self.mo = cre.match(s)
  606.         return self.mo is not None
  607.  
  608.     
  609.     def _new_tag(self):
  610.         tag = '%s%s' % (self.tagpre, self.tagnum)
  611.         self.tagnum = self.tagnum + 1
  612.         self.tagged_commands[tag] = None
  613.         return tag
  614.  
  615.     
  616.     def _checkquote(self, arg):
  617.         if type(arg) is not type(''):
  618.             return arg
  619.         if None(arg) >= 2 and (arg[0], arg[-1]) in (('(', ')'), ('"', '"')):
  620.             return arg
  621.         if None and self.mustquote.search(arg) is None:
  622.             return arg
  623.         return None._quote(arg)
  624.  
  625.     
  626.     def _quote(self, arg):
  627.         arg = arg.replace('\\', '\\\\')
  628.         arg = arg.replace('"', '\\"')
  629.         return '"%s"' % arg
  630.  
  631.     
  632.     def _simple_command(self, name, *args):
  633.         return self._command_complete(name, self._command(name, *args))
  634.  
  635.     
  636.     def _untagged_response(self, typ, dat, name):
  637.         if typ == 'NO':
  638.             return (typ, dat)
  639.         if None not in self.untagged_responses:
  640.             return (typ, [
  641.                 None])
  642.         data = None.untagged_responses.pop(name)
  643.         return (typ, data)
  644.  
  645.  
  646.  
  647. try:
  648.     import ssl
  649. except ImportError:
  650.     pass
  651.  
  652.  
  653. class IMAP4_SSL(IMAP4):
  654.     
  655.     def __init__(self, host = '', port = IMAP4_SSL_PORT, keyfile = None, certfile = None):
  656.         self.keyfile = keyfile
  657.         self.certfile = certfile
  658.         IMAP4.__init__(self, host, port)
  659.  
  660.     
  661.     def open(self, host = '', port = IMAP4_SSL_PORT):
  662.         self.host = host
  663.         self.port = port
  664.         self.sock = socket.create_connection((host, port))
  665.         self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile)
  666.  
  667.     
  668.     def read(self, size):
  669.         chunks = []
  670.         read = 0
  671.         while read < size:
  672.             data = self.sslobj.read(min(size - read, 16384))
  673.             read += len(data)
  674.             chunks.append(data)
  675.         return ''.join(chunks)
  676.  
  677.     
  678.     def readline(self):
  679.         line = []
  680.         while None:
  681.             char = self.sslobj.read(1)
  682.             if char in ('\n', ''):
  683.                 return ''.join(line)
  684.             return None
  685.  
  686.     
  687.     def send(self, data):
  688.         bytes = len(data)
  689.         while bytes > 0:
  690.             sent = self.sslobj.write(data)
  691.             if sent == bytes:
  692.                 break
  693.             data = data[sent:]
  694.             bytes = bytes - sent
  695.  
  696.     
  697.     def shutdown(self):
  698.         self.sock.close()
  699.  
  700.     
  701.     def socket(self):
  702.         return self.sock
  703.  
  704.     
  705.     def ssl(self):
  706.         return self.sslobj
  707.  
  708.  
  709. __all__.append('IMAP4_SSL')
  710.  
  711. class IMAP4_stream(IMAP4):
  712.     
  713.     def __init__(self, command):
  714.         self.command = command
  715.         IMAP4.__init__(self)
  716.  
  717.     
  718.     def open(self, host = None, port = None):
  719.         self.host = None
  720.         self.port = None
  721.         self.sock = None
  722.         self.file = None
  723.         self.process = subprocess.Popen(self.command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, shell = True, close_fds = True)
  724.         self.writefile = self.process.stdin
  725.         self.readfile = self.process.stdout
  726.  
  727.     
  728.     def read(self, size):
  729.         return self.readfile.read(size)
  730.  
  731.     
  732.     def readline(self):
  733.         return self.readfile.readline()
  734.  
  735.     
  736.     def send(self, data):
  737.         self.writefile.write(data)
  738.         self.writefile.flush()
  739.  
  740.     
  741.     def shutdown(self):
  742.         self.readfile.close()
  743.         self.writefile.close()
  744.         self.process.wait()
  745.  
  746.  
  747.  
  748. class _Authenticator:
  749.     
  750.     def __init__(self, mechinst):
  751.         self.mech = mechinst
  752.  
  753.     
  754.     def process(self, data):
  755.         ret = self.mech(self.decode(data))
  756.         if ret is None:
  757.             return '*'
  758.         return None.encode(ret)
  759.  
  760.     
  761.     def encode(self, inp):
  762.         oup = ''
  763.         while inp:
  764.             if len(inp) > 48:
  765.                 t = inp[:48]
  766.                 inp = inp[48:]
  767.             else:
  768.                 t = inp
  769.                 inp = ''
  770.             e = binascii.b2a_base64(t)
  771.             if e:
  772.                 oup = oup + e[:-1]
  773.                 continue
  774.             return oup
  775.  
  776.     
  777.     def decode(self, inp):
  778.         if not inp:
  779.             return ''
  780.         return None.a2b_base64(inp)
  781.  
  782.  
  783. Mon2num = {
  784.     'Jan': 1,
  785.     'Feb': 2,
  786.     'Mar': 3,
  787.     'Apr': 4,
  788.     'May': 5,
  789.     'Jun': 6,
  790.     'Jul': 7,
  791.     'Aug': 8,
  792.     'Sep': 9,
  793.     'Oct': 10,
  794.     'Nov': 11,
  795.     'Dec': 12 }
  796.  
  797. def Internaldate2tuple(resp):
  798.     mo = InternalDate.match(resp)
  799.     if not mo:
  800.         return None
  801.     mon = None[mo.group('mon')]
  802.     zonen = mo.group('zonen')
  803.     day = int(mo.group('day'))
  804.     year = int(mo.group('year'))
  805.     hour = int(mo.group('hour'))
  806.     min = int(mo.group('min'))
  807.     sec = int(mo.group('sec'))
  808.     zoneh = int(mo.group('zoneh'))
  809.     zonem = int(mo.group('zonem'))
  810.     zone = (zoneh * 60 + zonem) * 60
  811.     if zonen == '-':
  812.         zone = -zone
  813.     tt = (year, mon, day, hour, min, sec, -1, -1, -1)
  814.     utc = time.mktime(tt)
  815.     lt = time.localtime(utc)
  816.     if time.daylight and lt[-1]:
  817.         zone = zone + time.altzone
  818.     else:
  819.         zone = zone + time.timezone
  820.     return time.localtime(utc - zone)
  821.  
  822.  
  823. def Int2AP(num):
  824.     val = ''
  825.     AP = 'ABCDEFGHIJKLMNOP'
  826.     num = int(abs(num))
  827.     while num:
  828.         (num, mod) = divmod(num, 16)
  829.         val = AP[mod] + val
  830.     return val
  831.  
  832.  
  833. def ParseFlags(resp):
  834.     mo = Flags.match(resp)
  835.     if not mo:
  836.         return ()
  837.     return None(mo.group('flags').split())
  838.  
  839.  
  840. def Time2Internaldate(date_time):
  841.     if isinstance(date_time, (int, float)):
  842.         tt = time.localtime(date_time)
  843.     elif isinstance(date_time, (tuple, time.struct_time)):
  844.         tt = date_time
  845.     elif isinstance(date_time, str) and (date_time[0], date_time[-1]) == ('"', '"'):
  846.         return date_time
  847.     raise ValueError('date_time not of a known type')
  848.     dt = time.strftime('%d-%b-%Y %H:%M:%S', tt)
  849.     if dt[0] == '0':
  850.         dt = ' ' + dt[1:]
  851.     if time.daylight and tt[-1]:
  852.         zone = -(time.altzone)
  853.     else:
  854.         zone = -(time.timezone)
  855.     return '"' + dt + ' %+03d%02d' % divmod(zone // 60, 60) + '"'
  856.  
  857. if __name__ == '__main__':
  858.     import getopt
  859.     import getpass
  860.     
  861.     try:
  862.         (optlist, args) = getopt.getopt(sys.argv[1:], 'd:s:')
  863.     except getopt.error:
  864.         val = None
  865.         (optlist, args) = ((), ())
  866.  
  867.     stream_command = None
  868.     for opt, val in optlist:
  869.         if opt == '-d':
  870.             Debug = int(val)
  871.         elif opt == '-s':
  872.             stream_command = val
  873.             if not args:
  874.                 args = (stream_command,)
  875.     
  876.     if not args:
  877.         args = ('',)
  878.     host = args[0]
  879.     USER = getpass.getuser()
  880.     if not host:
  881.         pass
  882.     PASSWD = getpass.getpass('IMAP password for %s on %s: ' % (USER, 'localhost'))
  883.     test_mesg = 'From: %(user)s@localhost%(lf)sSubject: IMAP4 test%(lf)s%(lf)sdata...%(lf)s' % {
  884.         'user': USER,
  885.         'lf': '\n' }
  886.     test_seq1 = (('login', (USER, PASSWD)), ('create', ('/tmp/xxx 1',)), ('rename', ('/tmp/xxx 1', '/tmp/yyy')), ('CREATE', ('/tmp/yyz 2',)), ('append', ('/tmp/yyz 2', None, None, test_mesg)), ('list', ('/tmp', 'yy*')), ('select', ('/tmp/yyz 2',)), ('search', (None, 'SUBJECT', 'test')), ('fetch', ('1', '(FLAGS INTERNALDATE RFC822)')), ('store', ('1', 'FLAGS', '(\\Deleted)')), ('namespace', ()), ('expunge', ()), ('recent', ()), ('close', ()))
  887.     test_seq2 = (('select', ()), ('response', ('UIDVALIDITY',)), ('uid', ('SEARCH', 'ALL')), ('response', ('EXISTS',)), ('append', (None, None, None, test_mesg)), ('recent', ()), ('logout', ()))
  888.     
  889.     def run(cmd, args):
  890.         M._mesg('%s %s' % (cmd, args))
  891.         (typ, dat) = getattr(M, cmd)(*args)
  892.         M._mesg('%s => %s %s' % (cmd, typ, dat))
  893.         if typ == 'NO':
  894.             raise dat[0]
  895.         return dat
  896.  
  897.     
  898.     try:
  899.         if stream_command:
  900.             M = IMAP4_stream(stream_command)
  901.         else:
  902.             M = IMAP4(host)
  903.         if M.state == 'AUTH':
  904.             test_seq1 = test_seq1[1:]
  905.         M._mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION)
  906.         M._mesg('CAPABILITIES = %r' % (M.capabilities,))
  907.         for cmd, args in test_seq1:
  908.             run(cmd, args)
  909.         
  910.         for ml in run('list', ('/tmp/', 'yy%')):
  911.             mo = re.match('.*"([^"]+)"$', ml)
  912.             if mo:
  913.                 path = mo.group(1)
  914.             else:
  915.                 path = ml.split()[-1]
  916.             run('delete', (path,))
  917.         
  918.         for cmd, args in test_seq2:
  919.             dat = run(cmd, args)
  920.             if (cmd, args) != ('uid', ('SEARCH', 'ALL')):
  921.                 continue
  922.             uid = dat[-1].split()
  923.             if not uid:
  924.                 continue
  925.             run('uid', ('FETCH', '%s' % uid[-1], '(FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER RFC822.TEXT)'))
  926.         
  927.         print '\nAll tests OK.'
  928.     except:
  929.         print '\nTests failed.'
  930.         if not Debug:
  931.             print '\nIf you would like to see debugging output,\ntry: %s -d5\n' % sys.argv[0]
  932.         raise 
  933.  
  934.