home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_601 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  27.8 KB  |  970 lines

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