| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | """IMAP4 client.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Based on RFC 2060. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | Public class:           IMAP4 | 
					
						
							|  |  |  | Public variable:        Debug | 
					
						
							|  |  |  | Public functions:       Internaldate2tuple | 
					
						
							|  |  |  |                         Int2AP | 
					
						
							|  |  |  |                         ParseFlags | 
					
						
							|  |  |  |                         Time2Internaldate | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											1998-06-25 02:22:16 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-02-28 15:12:25 +00:00
										 |  |  | # Author: Piers Lauder <piers@cs.su.oz.au> December 1997. | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2000-02-28 15:12:25 +00:00
										 |  |  | # Authentication code contributed by Donn Cave <donn@u.washington.edu> June 1998. | 
					
						
							| 
									
										
										
										
											2001-02-09 06:50:21 +00:00
										 |  |  | # String method conversion by ESR, February 2001. | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  | # GET/SETACL contributed by Anthony Baxter <anthony@interlink.com.au> April 2001. | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  | # IMAP4_SSL contributed by Tino Lange <Tino.Lange@isg.de> March 2002. | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  | # GET/SETQUOTA contributed by Andreas Zeidler <az@kreativkombinat.de> June 2002. | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  | # PROXYAUTH contributed by Rick Holbert <holbert.13@osu.edu> November 2002. | 
					
						
							| 
									
										
										
										
											2005-06-01 23:50:52 +00:00
										 |  |  | # GET/SETANNOTATION contributed by Tomas Lindroos <skitta@abo.fi> June 2005. | 
					
						
							| 
									
										
										
										
											2000-02-28 15:12:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-31 10:50:03 +00:00
										 |  |  | __version__ = "2.58" | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  | import binascii, os, random, re, socket, sys, time | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-06-02 00:42:36 +00:00
										 |  |  | __all__ = ["IMAP4", "IMAP4_SSL", "IMAP4_stream", "Internaldate2tuple", | 
					
						
							| 
									
										
										
										
											2001-01-24 04:16:09 +00:00
										 |  |  |            "Int2AP", "ParseFlags", "Time2Internaldate"] | 
					
						
							| 
									
										
										
										
											2001-01-23 15:35:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | #       Globals | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | CRLF = '\r\n' | 
					
						
							|  |  |  | Debug = 0 | 
					
						
							|  |  |  | IMAP4_PORT = 143 | 
					
						
							| 
									
										
										
										
											2002-03-08 09:05:12 +00:00
										 |  |  | IMAP4_SSL_PORT = 993 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | AllowedVersions = ('IMAP4REV1', 'IMAP4')        # Most recent first | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | #       Commands | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | Commands = { | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # name            valid states | 
					
						
							|  |  |  |         'APPEND':       ('AUTH', 'SELECTED'), | 
					
						
							|  |  |  |         'AUTHENTICATE': ('NONAUTH',), | 
					
						
							|  |  |  |         'CAPABILITY':   ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'), | 
					
						
							|  |  |  |         'CHECK':        ('SELECTED',), | 
					
						
							|  |  |  |         'CLOSE':        ('SELECTED',), | 
					
						
							|  |  |  |         'COPY':         ('SELECTED',), | 
					
						
							|  |  |  |         'CREATE':       ('AUTH', 'SELECTED'), | 
					
						
							|  |  |  |         'DELETE':       ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2004-07-27 05:07:19 +00:00
										 |  |  |         'DELETEACL':    ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'EXAMINE':      ('AUTH', 'SELECTED'), | 
					
						
							|  |  |  |         'EXPUNGE':      ('SELECTED',), | 
					
						
							|  |  |  |         'FETCH':        ('SELECTED',), | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         'GETACL':       ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2005-06-01 23:50:52 +00:00
										 |  |  |         'GETANNOTATION':('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  |         'GETQUOTA':     ('AUTH', 'SELECTED'), | 
					
						
							|  |  |  |         'GETQUOTAROOT': ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2004-07-27 05:07:19 +00:00
										 |  |  |         'MYRIGHTS':     ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'LIST':         ('AUTH', 'SELECTED'), | 
					
						
							|  |  |  |         'LOGIN':        ('NONAUTH',), | 
					
						
							|  |  |  |         'LOGOUT':       ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'), | 
					
						
							|  |  |  |         'LSUB':         ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         'NAMESPACE':    ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'NOOP':         ('NONAUTH', 'AUTH', 'SELECTED', 'LOGOUT'), | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |         'PARTIAL':      ('SELECTED',),                                  # NB: obsolete | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |         'PROXYAUTH':    ('AUTH',), | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'RENAME':       ('AUTH', 'SELECTED'), | 
					
						
							|  |  |  |         'SEARCH':       ('SELECTED',), | 
					
						
							|  |  |  |         'SELECT':       ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         'SETACL':       ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2005-06-01 23:50:52 +00:00
										 |  |  |         'SETANNOTATION':('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  |         'SETQUOTA':     ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         'SORT':         ('SELECTED',), | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'STATUS':       ('AUTH', 'SELECTED'), | 
					
						
							|  |  |  |         'STORE':        ('SELECTED',), | 
					
						
							|  |  |  |         'SUBSCRIBE':    ('AUTH', 'SELECTED'), | 
					
						
							| 
									
										
										
										
											2003-11-10 06:44:44 +00:00
										 |  |  |         'THREAD':       ('SELECTED',), | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'UID':          ('SELECTED',), | 
					
						
							|  |  |  |         'UNSUBSCRIBE':  ('AUTH', 'SELECTED'), | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #       Patterns to match server responses | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | Continuation = re.compile(r'\+( (?P<data>.*))?') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | Flags = re.compile(r'.*FLAGS \((?P<flags>[^\)]*)\)') | 
					
						
							|  |  |  | InternalDate = re.compile(r'.*INTERNALDATE "' | 
					
						
							| 
									
										
										
										
											2005-03-02 09:13:45 +00:00
										 |  |  |         r'(?P<day>[ 0123][0-9])-(?P<mon>[A-Z][a-z][a-z])-(?P<year>[0-9][0-9][0-9][0-9])' | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])' | 
					
						
							|  |  |  |         r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])' | 
					
						
							|  |  |  |         r'"') | 
					
						
							| 
									
										
										
											
												Patches by Piers Lauder.
Reasons for patches:
1st patch (15,21):
	version change
2nd patch (66,72):
	This is a patch I found in a Zope product release (quite by accident!).
	It relaxes the conditions for matching a literal. I've looked over the
	logic, and tested it, and it seems sensible.
3rd patch (117,123):
	It appears the quoting matcher was too general, and that the IMAP4
	protocol requires characters like ':' in commands to be unquoted.
	(This is the patch already sent to Guido.)
4th patch (699,705):
	Spelling correction in comment.
5th patch (753,761):
	Another patch from the Zope product. It seems that some IMAP4 servers
	produce unexpected responses in the middle of valid command/response
	sequences. This patch ignores the unexpected responses in this
	situation. (How I wish users would send me bug reports with examples!).
last 2 patches: (1015,1028) (1038,1044):
	Minor improvements to test code.
											
										 
											2000-02-17 17:12:39 +00:00
										 |  |  | Literal = re.compile(r'.*{(?P<size>\d+)}$') | 
					
						
							| 
									
										
										
										
											2003-04-29 23:58:08 +00:00
										 |  |  | MapCRLF = re.compile(r'\r\n|\r|\n') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | Response_code = re.compile(r'\[(?P<type>[A-Z-]+)( (?P<data>[^\]]*))?\]') | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | Untagged_response = re.compile(r'\* (?P<type>[A-Z-]+)( (?P<data>.*))?') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | Untagged_status = re.compile(r'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class IMAP4: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """IMAP4 client class.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Instantiate with: IMAP4([host[, port]]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             host - host's name (default: localhost); | 
					
						
							|  |  |  |             port - port number (default: standard IMAP4 port). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     All IMAP4rev1 commands are supported by methods of the same | 
					
						
							|  |  |  |     name (in lower-case). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     All arguments to commands are converted to strings, except for | 
					
						
							|  |  |  |     AUTHENTICATE, and the last argument to APPEND which is passed as | 
					
						
							|  |  |  |     an IMAP4 literal.  If necessary (the string contains any | 
					
						
							|  |  |  |     non-printing characters or white-space and isn't enclosed with | 
					
						
							|  |  |  |     either parentheses or double quotes) each string is quoted. | 
					
						
							|  |  |  |     However, the 'password' argument to the LOGIN command is always | 
					
						
							|  |  |  |     quoted.  If you want to avoid having an argument string quoted | 
					
						
							|  |  |  |     (eg: the 'flags' argument to STORE) then enclose the string in | 
					
						
							|  |  |  |     parentheses (eg: "(\Deleted)"). | 
					
						
							| 
									
										
										
										
											1998-05-29 13:34:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     Each command returns a tuple: (type, [data, ...]) where 'type' | 
					
						
							|  |  |  |     is usually 'OK' or 'NO', and 'data' is either the text from the | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |     tagged response, or untagged results from command. Each 'data' | 
					
						
							|  |  |  |     is either a string, or a tuple. If a tuple, then the first part | 
					
						
							|  |  |  |     is the header of the response, and the second part contains | 
					
						
							|  |  |  |     the data (ie: 'literal' value). | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     Errors raise the exception class <instance>.error("<reason>"). | 
					
						
							|  |  |  |     IMAP4 server errors raise <instance>.abort("<reason>"), | 
					
						
							|  |  |  |     which is a sub-class of 'error'. Mailbox status changes | 
					
						
							|  |  |  |     from READ-WRITE to READ-ONLY raise the exception class | 
					
						
							|  |  |  |     <instance>.readonly("<reason>"), which is a sub-class of 'abort'. | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     "error" exceptions imply a program error. | 
					
						
							|  |  |  |     "abort" exceptions imply the connection should be reset, and | 
					
						
							|  |  |  |             the command re-tried. | 
					
						
							|  |  |  |     "readonly" exceptions imply the command should be re-tried. | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-06-01 23:50:52 +00:00
										 |  |  |     Note: to use this module, you must read the RFCs pertaining to the | 
					
						
							|  |  |  |     IMAP4 protocol, as the semantics of the arguments to each IMAP4 | 
					
						
							|  |  |  |     command are left to the invoker, not to mention the results. Also, | 
					
						
							|  |  |  |     most IMAP servers implement a sub-set of the commands available here. | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     class error(Exception): pass    # Logical errors - debug required | 
					
						
							|  |  |  |     class abort(error): pass        # Service errors - close and retry | 
					
						
							|  |  |  |     class readonly(abort): pass     # Mailbox status changed to READ-ONLY | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     mustquote = re.compile(r"[^\w!#$%&'*+,.:;<=>?^`|~-]") | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def __init__(self, host = '', port = IMAP4_PORT): | 
					
						
							|  |  |  |         self.debug = Debug | 
					
						
							|  |  |  |         self.state = 'LOGOUT' | 
					
						
							|  |  |  |         self.literal = None             # A literal argument to a command | 
					
						
							|  |  |  |         self.tagged_commands = {}       # Tagged commands awaiting response | 
					
						
							|  |  |  |         self.untagged_responses = {}    # {typ: [data, ...], ...} | 
					
						
							|  |  |  |         self.continuation_response = '' # Last continuation response | 
					
						
							| 
									
										
										
										
											2005-08-31 10:46:29 +00:00
										 |  |  |         self.is_readonly = False        # READ-ONLY desired state | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         self.tagnum = 0 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # Open socket to server. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         self.open(host, port) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # Create unique tag for this session, | 
					
						
							|  |  |  |         # and compile tagged response matcher. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-07-05 04:20:07 +00:00
										 |  |  |         self.tagpre = Int2AP(random.randint(4096, 65535)) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         self.tagre = re.compile(r'(?P<tag>' | 
					
						
							|  |  |  |                         + self.tagpre | 
					
						
							|  |  |  |                         + r'\d+) (?P<type>[A-Z]+) (?P<data>.*)') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # Get server welcome message, | 
					
						
							|  |  |  |         # request and store CAPABILITY response. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if __debug__: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |             self._cmd_log_len = 10 | 
					
						
							|  |  |  |             self._cmd_log_idx = 0 | 
					
						
							|  |  |  |             self._cmd_log = {}           # Last `_cmd_log_len' interactions | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             if self.debug >= 1: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 self._mesg('imaplib version %s' % __version__) | 
					
						
							|  |  |  |                 self._mesg('new IMAP4 connection, tag=%s' % self.tagpre) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         self.welcome = self._get_response() | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if 'PREAUTH' in self.untagged_responses: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             self.state = 'AUTH' | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         elif 'OK' in self.untagged_responses: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             self.state = 'NONAUTH' | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise self.error(self.welcome) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-06-01 23:50:52 +00:00
										 |  |  |         typ, dat = self.capability() | 
					
						
							|  |  |  |         if dat == [None]: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             raise self.error('no CAPABILITY response from server') | 
					
						
							| 
									
										
										
										
											2005-06-01 23:50:52 +00:00
										 |  |  |         self.capabilities = tuple(dat[-1].upper().split()) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if __debug__: | 
					
						
							|  |  |  |             if self.debug >= 3: | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |                 self._mesg('CAPABILITIES: %r' % (self.capabilities,)) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         for version in AllowedVersions: | 
					
						
							|  |  |  |             if not version in self.capabilities: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             self.PROTOCOL_VERSION = version | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											1998-06-25 02:22:16 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         raise self.error('server not IMAP4 compliant') | 
					
						
							| 
									
										
										
										
											1998-04-11 01:22:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def __getattr__(self, attr): | 
					
						
							|  |  |  |         #       Allow UPPERCASE variants of IMAP4 command methods. | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if attr in Commands: | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |             return getattr(self, attr.lower()) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         raise AttributeError("Unknown IMAP4 command: '%s'" % attr) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-09-28 15:34:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |     #       Overridable methods | 
					
						
							| 
									
										
										
										
											1998-09-28 15:34:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-05 22:31:57 +00:00
										 |  |  |     def open(self, host = '', port = IMAP4_PORT): | 
					
						
							|  |  |  |         """Setup connection to remote server on "host:port"
 | 
					
						
							|  |  |  |             (default: localhost:standard IMAP4 port). | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         This connection will be used by the routines: | 
					
						
							|  |  |  |             read, readline, send, shutdown. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2002-06-05 22:31:57 +00:00
										 |  |  |         self.host = host | 
					
						
							|  |  |  |         self.port = port | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | 
					
						
							| 
									
										
										
										
											2002-06-05 22:31:57 +00:00
										 |  |  |         self.sock.connect((host, port)) | 
					
						
							| 
									
										
										
										
											2001-10-15 13:47:08 +00:00
										 |  |  |         self.file = self.sock.makefile('rb') | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |     def read(self, size): | 
					
						
							|  |  |  |         """Read 'size' bytes from remote.""" | 
					
						
							|  |  |  |         return self.file.read(size) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def readline(self): | 
					
						
							|  |  |  |         """Read line from remote.""" | 
					
						
							|  |  |  |         return self.file.readline() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def send(self, data): | 
					
						
							|  |  |  |         """Send data to remote.""" | 
					
						
							| 
									
										
										
										
											2002-02-16 23:06:19 +00:00
										 |  |  |         self.sock.sendall(data) | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |     def shutdown(self): | 
					
						
							|  |  |  |         """Close I/O established in "open".""" | 
					
						
							|  |  |  |         self.file.close() | 
					
						
							|  |  |  |         self.sock.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def socket(self): | 
					
						
							|  |  |  |         """Return socket instance used to connect to IMAP4 server.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         socket = <instance>.socket() | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self.sock | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     #       Utility methods | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def recent(self): | 
					
						
							|  |  |  |         """Return most recent 'RECENT' responses if any exist,
 | 
					
						
							|  |  |  |         else prompt server for an update using the 'NOOP' command. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.recent() | 
					
						
							| 
									
										
										
										
											1998-09-28 15:34:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'data' is None if no new messages, | 
					
						
							|  |  |  |         else list of RECENT responses, most recent last. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'RECENT' | 
					
						
							|  |  |  |         typ, dat = self._untagged_response('OK', [None], name) | 
					
						
							|  |  |  |         if dat[-1]: | 
					
						
							|  |  |  |             return typ, dat | 
					
						
							|  |  |  |         typ, dat = self.noop()  # Prod server for response | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							| 
									
										
										
										
											1998-09-28 15:34:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def response(self, code): | 
					
						
							|  |  |  |         """Return data for response 'code' if received, or None.
 | 
					
						
							| 
									
										
										
										
											1998-09-28 15:34:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         Old value for response 'code' is cleared. | 
					
						
							| 
									
										
										
										
											1998-09-28 15:34:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (code, [data]) = <instance>.response(code) | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2001-02-09 06:50:21 +00:00
										 |  |  |         return self._untagged_response(code, [None], code.upper()) | 
					
						
							| 
									
										
										
										
											1998-09-28 15:34:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     #       IMAP4 commands | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def append(self, mailbox, flags, date_time, message): | 
					
						
							|  |  |  |         """Append message to named mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.append(mailbox, flags, date_time, message) | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 All args except `message' can be None. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'APPEND' | 
					
						
							|  |  |  |         if not mailbox: | 
					
						
							|  |  |  |             mailbox = 'INBOX' | 
					
						
							|  |  |  |         if flags: | 
					
						
							|  |  |  |             if (flags[0],flags[-1]) != ('(',')'): | 
					
						
							|  |  |  |                 flags = '(%s)' % flags | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             flags = None | 
					
						
							|  |  |  |         if date_time: | 
					
						
							|  |  |  |             date_time = Time2Internaldate(date_time) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             date_time = None | 
					
						
							| 
									
										
										
										
											2003-04-29 23:40:59 +00:00
										 |  |  |         self.literal = MapCRLF.sub(CRLF, message) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return self._simple_command(name, mailbox, flags, date_time) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def authenticate(self, mechanism, authobject): | 
					
						
							|  |  |  |         """Authenticate command - requires response processing.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'mechanism' specifies which authentication mechanism is to | 
					
						
							|  |  |  |         be used - it must appear in <instance>.capabilities in the | 
					
						
							|  |  |  |         form AUTH=<mechanism>. | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'authobject' must be a callable object: | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 data = authobject(response) | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         It will be called to process server continuation responses. | 
					
						
							|  |  |  |         It should return data that will be encoded and sent to server. | 
					
						
							|  |  |  |         It should return None if the client abort response '*' should | 
					
						
							|  |  |  |         be sent instead. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2001-02-09 06:50:21 +00:00
										 |  |  |         mech = mechanism.upper() | 
					
						
							| 
									
										
										
										
											2003-06-29 04:21:43 +00:00
										 |  |  |         # XXX: shouldn't this code be removed, not commented out? | 
					
						
							|  |  |  |         #cap = 'AUTH=%s' % mech | 
					
						
							| 
									
										
										
										
											2002-11-24 02:35:35 +00:00
										 |  |  |         #if not cap in self.capabilities:       # Let the server decide! | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |         #    raise self.error("Server doesn't allow %s authentication." % mech) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         self.literal = _Authenticator(authobject).process | 
					
						
							|  |  |  |         typ, dat = self._simple_command('AUTHENTICATE', mech) | 
					
						
							|  |  |  |         if typ != 'OK': | 
					
						
							|  |  |  |             raise self.error(dat[-1]) | 
					
						
							|  |  |  |         self.state = 'AUTH' | 
					
						
							|  |  |  |         return typ, dat | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-06-01 23:50:52 +00:00
										 |  |  |     def capability(self): | 
					
						
							|  |  |  |         """(typ, [data]) = <instance>.capability()
 | 
					
						
							|  |  |  |         Fetch capabilities list from server."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         name = 'CAPABILITY' | 
					
						
							|  |  |  |         typ, dat = self._simple_command(name) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def check(self): | 
					
						
							|  |  |  |         """Checkpoint mailbox on server.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.check() | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._simple_command('CHECK') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def close(self): | 
					
						
							|  |  |  |         """Close currently selected mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 14:20:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         Deleted messages are removed from writable mailbox. | 
					
						
							|  |  |  |         This is the recommended command before 'LOGOUT'. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.close() | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             typ, dat = self._simple_command('CLOSE') | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self.state = 'AUTH' | 
					
						
							|  |  |  |         return typ, dat | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def copy(self, message_set, new_mailbox): | 
					
						
							|  |  |  |         """Copy 'message_set' messages onto end of 'new_mailbox'.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.copy(message_set, new_mailbox) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._simple_command('COPY', message_set, new_mailbox) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def create(self, mailbox): | 
					
						
							|  |  |  |         """Create new mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.create(mailbox) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._simple_command('CREATE', mailbox) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def delete(self, mailbox): | 
					
						
							|  |  |  |         """Delete old mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.delete(mailbox) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._simple_command('DELETE', mailbox) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-27 05:07:19 +00:00
										 |  |  |     def deleteacl(self, mailbox, who): | 
					
						
							|  |  |  |         """Delete the ACLs (remove any rights) set for who on mailbox.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (typ, [data]) = <instance>.deleteacl(mailbox, who) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._simple_command('DELETEACL', mailbox, who) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def expunge(self): | 
					
						
							|  |  |  |         """Permanently remove deleted items from selected mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 14:20:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         Generates 'EXPUNGE' response for each deleted message. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.expunge() | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'data' is list of 'EXPUNGE'd message numbers in order received. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'EXPUNGE' | 
					
						
							|  |  |  |         typ, dat = self._simple_command(name) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def fetch(self, message_set, message_parts): | 
					
						
							|  |  |  |         """Fetch (parts of) messages.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data, ...]) = <instance>.fetch(message_set, message_parts) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'message_parts' should be a string of selected parts | 
					
						
							|  |  |  |         enclosed in parentheses, eg: "(UID BODY[TEXT])". | 
					
						
							| 
									
										
										
										
											2000-05-25 03:25:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'data' are tuples of message part envelope and data. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'FETCH' | 
					
						
							|  |  |  |         typ, dat = self._simple_command(name, message_set, message_parts) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |     def getacl(self, mailbox): | 
					
						
							|  |  |  |         """Get the ACLs for a mailbox.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (typ, [data]) = <instance>.getacl(mailbox) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         typ, dat = self._simple_command('GETACL', mailbox) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, 'ACL') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-06-01 23:50:52 +00:00
										 |  |  |     def getannotation(self, mailbox, entry, attribute): | 
					
						
							|  |  |  |         """(typ, [data]) = <instance>.getannotation(mailbox, entry, attribute)
 | 
					
						
							|  |  |  |         Retrieve ANNOTATIONs."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         typ, dat = self._simple_command('GETANNOTATION', mailbox, entry, attribute) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, 'ANNOTATION') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  |     def getquota(self, root): | 
					
						
							|  |  |  |         """Get the quota root's resource usage and limits.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-17 12:43:20 +00:00
										 |  |  |         Part of the IMAP4 QUOTA extension defined in rfc2087. | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         (typ, [data]) = <instance>.getquota(root) | 
					
						
							| 
									
										
										
										
											2002-06-17 12:43:20 +00:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  |         typ, dat = self._simple_command('GETQUOTA', root) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, 'QUOTA') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getquotaroot(self, mailbox): | 
					
						
							|  |  |  |         """Get the list of quota roots for the named mailbox.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (typ, [[QUOTAROOT responses...], [QUOTA responses]]) = <instance>.getquotaroot(mailbox) | 
					
						
							| 
									
										
										
										
											2002-06-17 12:43:20 +00:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2004-08-10 01:24:54 +00:00
										 |  |  |         typ, dat = self._simple_command('GETQUOTAROOT', mailbox) | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  |         typ, quota = self._untagged_response(typ, dat, 'QUOTA') | 
					
						
							|  |  |  |         typ, quotaroot = self._untagged_response(typ, dat, 'QUOTAROOT') | 
					
						
							| 
									
										
										
										
											2002-06-17 12:43:20 +00:00
										 |  |  |         return typ, [quotaroot, quota] | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def list(self, directory='""', pattern='*'): | 
					
						
							|  |  |  |         """List mailbox names in directory matching pattern.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.list(directory='""', pattern='*') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'data' is list of LIST responses. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'LIST' | 
					
						
							|  |  |  |         typ, dat = self._simple_command(name, directory, pattern) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def login(self, user, password): | 
					
						
							|  |  |  |         """Identify client using plaintext password.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.login(user, password) | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         NB: 'password' will be quoted. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         typ, dat = self._simple_command('LOGIN', user, self._quote(password)) | 
					
						
							|  |  |  |         if typ != 'OK': | 
					
						
							|  |  |  |             raise self.error(dat[-1]) | 
					
						
							|  |  |  |         self.state = 'AUTH' | 
					
						
							|  |  |  |         return typ, dat | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |     def login_cram_md5(self, user, password): | 
					
						
							|  |  |  |         """ Force use of CRAM-MD5 authentication.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (typ, [data]) = <instance>.login_cram_md5(user, password) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.user, self.password = user, password | 
					
						
							|  |  |  |         return self.authenticate('CRAM-MD5', self._CRAM_MD5_AUTH) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _CRAM_MD5_AUTH(self, challenge): | 
					
						
							|  |  |  |         """ Authobject to use with CRAM-MD5 authentication. """ | 
					
						
							|  |  |  |         import hmac | 
					
						
							|  |  |  |         return self.user + " " + hmac.HMAC(self.password, challenge).hexdigest() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def logout(self): | 
					
						
							|  |  |  |         """Shutdown connection to server.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.logout() | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         Returns server 'BYE' response. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.state = 'LOGOUT' | 
					
						
							|  |  |  |         try: typ, dat = self._simple_command('LOGOUT') | 
					
						
							|  |  |  |         except: typ, dat = 'NO', ['%s: %s' % sys.exc_info()[:2]] | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         self.shutdown() | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if 'BYE' in self.untagged_responses: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             return 'BYE', self.untagged_responses['BYE'] | 
					
						
							|  |  |  |         return typ, dat | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def lsub(self, directory='""', pattern='*'): | 
					
						
							|  |  |  |         """List 'subscribed' mailbox names in directory matching pattern.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data, ...]) = <instance>.lsub(directory='""', pattern='*') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'data' are tuples of message part envelope and data. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'LSUB' | 
					
						
							|  |  |  |         typ, dat = self._simple_command(name, directory, pattern) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-27 05:07:19 +00:00
										 |  |  |     def myrights(self, mailbox): | 
					
						
							|  |  |  |         """Show my ACLs for a mailbox (i.e. the rights that I have on mailbox).
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (typ, [data]) = <instance>.myrights(mailbox) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         typ,dat = self._simple_command('MYRIGHTS', mailbox) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, 'MYRIGHTS') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |     def namespace(self): | 
					
						
							|  |  |  |         """ Returns IMAP namespaces ala rfc2342
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (typ, [data, ...]) = <instance>.namespace() | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'NAMESPACE' | 
					
						
							|  |  |  |         typ, dat = self._simple_command(name) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def noop(self): | 
					
						
							|  |  |  |         """Send NOOP command.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |         (typ, [data]) = <instance>.noop() | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         if __debug__: | 
					
						
							|  |  |  |             if self.debug >= 3: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 self._dump_ur(self.untagged_responses) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return self._simple_command('NOOP') | 
					
						
							| 
									
										
										
										
											1998-05-29 13:34:03 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def partial(self, message_num, message_part, start, length): | 
					
						
							|  |  |  |         """Fetch truncated part of a message.
 | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data, ...]) = <instance>.partial(message_num, message_part, start, length) | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'data' is tuple of message part envelope and data. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'PARTIAL' | 
					
						
							|  |  |  |         typ, dat = self._simple_command(name, message_num, message_part, start, length) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, 'FETCH') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |     def proxyauth(self, user): | 
					
						
							|  |  |  |         """Assume authentication as "user".
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Allows an authorised administrator to proxy into any user's | 
					
						
							|  |  |  |         mailbox. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (typ, [data]) = <instance>.proxyauth(user) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         name = 'PROXYAUTH' | 
					
						
							|  |  |  |         return self._simple_command('PROXYAUTH', user) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def rename(self, oldmailbox, newmailbox): | 
					
						
							|  |  |  |         """Rename old mailbox name to new.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |         (typ, [data]) = <instance>.rename(oldmailbox, newmailbox) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         return self._simple_command('RENAME', oldmailbox, newmailbox) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def search(self, charset, *criteria): | 
					
						
							|  |  |  |         """Search mailbox for matching messages.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-03-27 16:59:38 +00:00
										 |  |  |         (typ, [data]) = <instance>.search(charset, criterion, ...) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'data' is space separated list of matching message numbers. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'SEARCH' | 
					
						
							|  |  |  |         if charset: | 
					
						
							| 
									
										
										
										
											2003-02-27 20:14:51 +00:00
										 |  |  |             typ, dat = self._simple_command(name, 'CHARSET', charset, *criteria) | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2003-02-27 20:14:51 +00:00
										 |  |  |             typ, dat = self._simple_command(name, *criteria) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-31 10:46:29 +00:00
										 |  |  |     def select(self, mailbox='INBOX', readonly=False): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         """Select a mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         Flush all untagged responses. | 
					
						
							| 
									
										
										
										
											1998-05-18 14:39:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-08-31 10:46:29 +00:00
										 |  |  |         (typ, [data]) = <instance>.select(mailbox='INBOX', readonly=False) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'data' is count of messages in mailbox ('EXISTS' response). | 
					
						
							| 
									
										
										
										
											2004-08-10 01:24:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Mandated responses are ('FLAGS', 'EXISTS', 'RECENT', 'UIDVALIDITY'), so | 
					
						
							|  |  |  |         other responses should be obtained via <instance>.response('FLAGS') etc. | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         self.untagged_responses = {}    # Flush old responses. | 
					
						
							|  |  |  |         self.is_readonly = readonly | 
					
						
							| 
									
										
										
										
											2005-08-31 10:46:29 +00:00
										 |  |  |         if readonly: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             name = 'EXAMINE' | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             name = 'SELECT' | 
					
						
							|  |  |  |         typ, dat = self._simple_command(name, mailbox) | 
					
						
							|  |  |  |         if typ != 'OK': | 
					
						
							|  |  |  |             self.state = 'AUTH'     # Might have been 'SELECTED' | 
					
						
							|  |  |  |             return typ, dat | 
					
						
							|  |  |  |         self.state = 'SELECTED' | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if 'READ-ONLY' in self.untagged_responses \ | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 and not readonly: | 
					
						
							|  |  |  |             if __debug__: | 
					
						
							|  |  |  |                 if self.debug >= 1: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                     self._dump_ur(self.untagged_responses) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             raise self.readonly('%s is not writable' % mailbox) | 
					
						
							|  |  |  |         return typ, self.untagged_responses.get('EXISTS', [None]) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |     def setacl(self, mailbox, who, what): | 
					
						
							|  |  |  |         """Set a mailbox acl.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-03-25 00:12:21 +00:00
										 |  |  |         (typ, [data]) = <instance>.setacl(mailbox, who, what) | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         return self._simple_command('SETACL', mailbox, who, what) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-06-01 23:50:52 +00:00
										 |  |  |     def setannotation(self, *args): | 
					
						
							|  |  |  |         """(typ, [data]) = <instance>.setannotation(mailbox[, entry, attribute]+)
 | 
					
						
							|  |  |  |         Set ANNOTATIONs."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         typ, dat = self._simple_command('SETANNOTATION', *args) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, 'ANNOTATION') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  |     def setquota(self, root, limits): | 
					
						
							|  |  |  |         """Set the quota root's resource limits.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (typ, [data]) = <instance>.setquota(root, limits) | 
					
						
							| 
									
										
										
										
											2002-06-17 12:43:20 +00:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  |         typ, dat = self._simple_command('SETQUOTA', root, limits) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, 'QUOTA') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |     def sort(self, sort_criteria, charset, *search_criteria): | 
					
						
							|  |  |  |         """IMAP4rev1 extension SORT command.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (typ, [data]) = <instance>.sort(sort_criteria, charset, search_criteria, ...) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'SORT' | 
					
						
							| 
									
										
										
										
											2001-07-21 01:41:30 +00:00
										 |  |  |         #if not name in self.capabilities:      # Let the server decide! | 
					
						
							|  |  |  |         #       raise self.error('unimplemented extension command: %s' % name) | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         if (sort_criteria[0],sort_criteria[-1]) != ('(',')'): | 
					
						
							| 
									
										
										
										
											2001-07-21 01:41:30 +00:00
										 |  |  |             sort_criteria = '(%s)' % sort_criteria | 
					
						
							| 
									
										
										
										
											2003-02-27 20:14:51 +00:00
										 |  |  |         typ, dat = self._simple_command(name, sort_criteria, charset, *search_criteria) | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def status(self, mailbox, names): | 
					
						
							|  |  |  |         """Request named status conditions for mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.status(mailbox, names) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'STATUS' | 
					
						
							| 
									
										
										
										
											2001-07-21 01:41:30 +00:00
										 |  |  |         #if self.PROTOCOL_VERSION == 'IMAP4':   # Let the server decide! | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         #    raise self.error('%s unimplemented in IMAP4 (obtain IMAP4rev1 server, or re-code)' % name) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         typ, dat = self._simple_command(name, mailbox, names) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def store(self, message_set, command, flags): | 
					
						
							|  |  |  |         """Alters flag dispositions for messages in mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.store(message_set, command, flags) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if (flags[0],flags[-1]) != ('(',')'): | 
					
						
							|  |  |  |             flags = '(%s)' % flags  # Avoid quoting the flags | 
					
						
							|  |  |  |         typ, dat = self._simple_command('STORE', message_set, command, flags) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, 'FETCH') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def subscribe(self, mailbox): | 
					
						
							|  |  |  |         """Subscribe to new mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.subscribe(mailbox) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._simple_command('SUBSCRIBE', mailbox) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-11-10 06:44:44 +00:00
										 |  |  |     def thread(self, threading_algorithm, charset, *search_criteria): | 
					
						
							|  |  |  |         """IMAPrev1 extension THREAD command.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         (type, [data]) = <instance>.thread(threading_alogrithm, charset, search_criteria, ...) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         name = 'THREAD' | 
					
						
							|  |  |  |         typ, dat = self._simple_command(name, threading_algorithm, charset, *search_criteria) | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def uid(self, command, *args): | 
					
						
							|  |  |  |         """Execute "command arg ..." with messages identified by UID,
 | 
					
						
							|  |  |  |                 rather than message number. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.uid(command, arg1, arg2, ...) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         Returns response appropriate to 'command'. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2001-02-09 06:50:21 +00:00
										 |  |  |         command = command.upper() | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if not command in Commands: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             raise self.error("Unknown IMAP4 UID command: %s" % command) | 
					
						
							|  |  |  |         if self.state not in Commands[command]: | 
					
						
							|  |  |  |             raise self.error('command %s illegal in state %s' | 
					
						
							|  |  |  |                                     % (command, self.state)) | 
					
						
							|  |  |  |         name = 'UID' | 
					
						
							| 
									
										
										
										
											2003-02-27 20:14:51 +00:00
										 |  |  |         typ, dat = self._simple_command(name, command, *args) | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         if command in ('SEARCH', 'SORT'): | 
					
						
							|  |  |  |             name = command | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             name = 'FETCH' | 
					
						
							|  |  |  |         return self._untagged_response(typ, dat, name) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def unsubscribe(self, mailbox): | 
					
						
							|  |  |  |         """Unsubscribe from old mailbox.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.unsubscribe(mailbox) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._simple_command('UNSUBSCRIBE', mailbox) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def xatom(self, name, *args): | 
					
						
							|  |  |  |         """Allow simple extension commands
 | 
					
						
							|  |  |  |                 notified by server in CAPABILITY response. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         Assumes command is legal in current state. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         (typ, [data]) = <instance>.xatom(name, arg, ...) | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Returns response appropriate to extension command `name'. | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         name = name.upper() | 
					
						
							| 
									
										
										
										
											2001-07-21 01:41:30 +00:00
										 |  |  |         #if not name in self.capabilities:      # Let the server decide! | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         #    raise self.error('unknown extension command: %s' % name) | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if not name in Commands: | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |             Commands[name] = (self.state,) | 
					
						
							| 
									
										
										
										
											2003-02-27 20:14:51 +00:00
										 |  |  |         return self._simple_command(name, *args) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     #       Private methods | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _append_untagged(self, typ, dat): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if dat is None: dat = '' | 
					
						
							|  |  |  |         ur = self.untagged_responses | 
					
						
							|  |  |  |         if __debug__: | 
					
						
							|  |  |  |             if self.debug >= 5: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 self._mesg('untagged_responses[%s] %s += ["%s"]' % | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                         (typ, len(ur.get(typ,'')), dat)) | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if typ in ur: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             ur[typ].append(dat) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             ur[typ] = [dat] | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _check_bye(self): | 
					
						
							|  |  |  |         bye = self.untagged_responses.get('BYE') | 
					
						
							|  |  |  |         if bye: | 
					
						
							|  |  |  |             raise self.abort(bye[-1]) | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _command(self, name, *args): | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if self.state not in Commands[name]: | 
					
						
							|  |  |  |             self.literal = None | 
					
						
							|  |  |  |             raise self.error( | 
					
						
							|  |  |  |             'command %s illegal in state %s' % (name, self.state)) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         for typ in ('OK', 'NO', 'BAD'): | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |             if typ in self.untagged_responses: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 del self.untagged_responses[typ] | 
					
						
							| 
									
										
										
										
											1998-09-28 15:34:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if 'READ-ONLY' in self.untagged_responses \ | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         and not self.is_readonly: | 
					
						
							|  |  |  |             raise self.readonly('mailbox status changed to READ-ONLY') | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         tag = self._new_tag() | 
					
						
							|  |  |  |         data = '%s %s' % (tag, name) | 
					
						
							|  |  |  |         for arg in args: | 
					
						
							|  |  |  |             if arg is None: continue | 
					
						
							|  |  |  |             data = '%s %s' % (data, self._checkquote(arg)) | 
					
						
							| 
									
										
										
										
											1998-05-29 13:34:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         literal = self.literal | 
					
						
							|  |  |  |         if literal is not None: | 
					
						
							|  |  |  |             self.literal = None | 
					
						
							|  |  |  |             if type(literal) is type(self._command): | 
					
						
							|  |  |  |                 literator = literal | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 literator = None | 
					
						
							|  |  |  |                 data = '%s {%s}' % (data, len(literal)) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if __debug__: | 
					
						
							|  |  |  |             if self.debug >= 4: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 self._mesg('> %s' % data) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 self._log('> %s' % data) | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |             self.send('%s%s' % (data, CRLF)) | 
					
						
							|  |  |  |         except (socket.error, OSError), val: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             raise self.abort('socket error: %s' % val) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if literal is None: | 
					
						
							|  |  |  |             return tag | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         while 1: | 
					
						
							|  |  |  |             # Wait for continuation response | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             while self._get_response(): | 
					
						
							|  |  |  |                 if self.tagged_commands[tag]:   # BAD/NO? | 
					
						
							|  |  |  |                     return tag | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             # Send literal | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             if literator: | 
					
						
							|  |  |  |                 literal = literator(self.continuation_response) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             if __debug__: | 
					
						
							|  |  |  |                 if self.debug >= 4: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                     self._mesg('write literal size %s' % len(literal)) | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |                 self.send(literal) | 
					
						
							|  |  |  |                 self.send(CRLF) | 
					
						
							|  |  |  |             except (socket.error, OSError), val: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 raise self.abort('socket error: %s' % val) | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             if not literator: | 
					
						
							|  |  |  |                 break | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return tag | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _command_complete(self, name, tag): | 
					
						
							|  |  |  |         self._check_bye() | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             typ, data = self._get_tagged_response(tag) | 
					
						
							|  |  |  |         except self.abort, val: | 
					
						
							|  |  |  |             raise self.abort('command: %s => %s' % (name, val)) | 
					
						
							|  |  |  |         except self.error, val: | 
					
						
							|  |  |  |             raise self.error('command: %s => %s' % (name, val)) | 
					
						
							|  |  |  |         self._check_bye() | 
					
						
							|  |  |  |         if typ == 'BAD': | 
					
						
							|  |  |  |             raise self.error('%s command error: %s %s' % (name, typ, data)) | 
					
						
							|  |  |  |         return typ, data | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _get_response(self): | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # Read response and store. | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         # Returns None for continuation responses, | 
					
						
							|  |  |  |         # otherwise first response line received. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         resp = self._get_line() | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # Command completion response? | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if self._match(self.tagre, resp): | 
					
						
							|  |  |  |             tag = self.mo.group('tag') | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |             if not tag in self.tagged_commands: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 raise self.abort('unexpected tagged response: %s' % resp) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             typ = self.mo.group('type') | 
					
						
							|  |  |  |             dat = self.mo.group('data') | 
					
						
							|  |  |  |             self.tagged_commands[tag] = (typ, [dat]) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             dat2 = None | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             # '*' (untagged) responses? | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             if not self._match(Untagged_response, resp): | 
					
						
							|  |  |  |                 if self._match(Untagged_status, resp): | 
					
						
							|  |  |  |                     dat2 = self.mo.group('data2') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             if self.mo is None: | 
					
						
							|  |  |  |                 # Only other possibility is '+' (continuation) response... | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 if self._match(Continuation, resp): | 
					
						
							|  |  |  |                     self.continuation_response = self.mo.group('data') | 
					
						
							|  |  |  |                     return None     # NB: indicates continuation | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 raise self.abort("unexpected response: '%s'" % resp) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             typ = self.mo.group('type') | 
					
						
							|  |  |  |             dat = self.mo.group('data') | 
					
						
							|  |  |  |             if dat is None: dat = ''        # Null untagged response | 
					
						
							|  |  |  |             if dat2: dat = dat + ' ' + dat2 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             # Is there a literal to come? | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             while self._match(Literal, dat): | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 # Read literal direct from connection. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-02-09 06:50:21 +00:00
										 |  |  |                 size = int(self.mo.group('size')) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 if __debug__: | 
					
						
							|  |  |  |                     if self.debug >= 4: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                         self._mesg('read literal size %s' % size) | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |                 data = self.read(size) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 # Store response with literal as tuple | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 self._append_untagged(typ, (dat, data)) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 # Read trailer - possibly containing another literal | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 dat = self._get_line() | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             self._append_untagged(typ, dat) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # Bracketed response information? | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if typ in ('OK', 'NO', 'BAD') and self._match(Response_code, dat): | 
					
						
							|  |  |  |             self._append_untagged(self.mo.group('type'), self.mo.group('data')) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if __debug__: | 
					
						
							|  |  |  |             if self.debug >= 1 and typ in ('NO', 'BAD', 'BYE'): | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 self._mesg('%s response: %s' % (typ, dat)) | 
					
						
							| 
									
										
										
										
											1998-09-28 15:34:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return resp | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _get_tagged_response(self, tag): | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         while 1: | 
					
						
							|  |  |  |             result = self.tagged_commands[tag] | 
					
						
							|  |  |  |             if result is not None: | 
					
						
							|  |  |  |                 del self.tagged_commands[tag] | 
					
						
							|  |  |  |                 return result | 
					
						
							| 
									
										
										
											
												Patches by Piers Lauder.
Reasons for patches:
1st patch (15,21):
	version change
2nd patch (66,72):
	This is a patch I found in a Zope product release (quite by accident!).
	It relaxes the conditions for matching a literal. I've looked over the
	logic, and tested it, and it seems sensible.
3rd patch (117,123):
	It appears the quoting matcher was too general, and that the IMAP4
	protocol requires characters like ':' in commands to be unquoted.
	(This is the patch already sent to Guido.)
4th patch (699,705):
	Spelling correction in comment.
5th patch (753,761):
	Another patch from the Zope product. It seems that some IMAP4 servers
	produce unexpected responses in the middle of valid command/response
	sequences. This patch ignores the unexpected responses in this
	situation. (How I wish users would send me bug reports with examples!).
last 2 patches: (1015,1028) (1038,1044):
	Minor improvements to test code.
											
										 
											2000-02-17 17:12:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             # Some have reported "unexpected response" exceptions. | 
					
						
							|  |  |  |             # Note that ignoring them here causes loops. | 
					
						
							|  |  |  |             # Instead, send me details of the unexpected response and | 
					
						
							|  |  |  |             # I'll update the code in `_get_response()'. | 
					
						
							| 
									
										
										
											
												Patches by Piers Lauder.
Reasons for patches:
1st patch (15,21):
	version change
2nd patch (66,72):
	This is a patch I found in a Zope product release (quite by accident!).
	It relaxes the conditions for matching a literal. I've looked over the
	logic, and tested it, and it seems sensible.
3rd patch (117,123):
	It appears the quoting matcher was too general, and that the IMAP4
	protocol requires characters like ':' in commands to be unquoted.
	(This is the patch already sent to Guido.)
4th patch (699,705):
	Spelling correction in comment.
5th patch (753,761):
	Another patch from the Zope product. It seems that some IMAP4 servers
	produce unexpected responses in the middle of valid command/response
	sequences. This patch ignores the unexpected responses in this
	situation. (How I wish users would send me bug reports with examples!).
last 2 patches: (1015,1028) (1038,1044):
	Minor improvements to test code.
											
										 
											2000-02-17 17:12:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 self._get_response() | 
					
						
							|  |  |  |             except self.abort, val: | 
					
						
							|  |  |  |                 if __debug__: | 
					
						
							|  |  |  |                     if self.debug >= 1: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                         self.print_log() | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |                 raise | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _get_line(self): | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |         line = self.readline() | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if not line: | 
					
						
							|  |  |  |             raise self.abort('socket error: EOF') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # Protocol mandates all lines terminated by CRLF | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         line = line[:-2] | 
					
						
							|  |  |  |         if __debug__: | 
					
						
							|  |  |  |             if self.debug >= 4: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 self._mesg('< %s' % line) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 self._log('< %s' % line) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return line | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _match(self, cre, s): | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # Run compiled regular expression match method on 's'. | 
					
						
							|  |  |  |         # Save result, return success. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         self.mo = cre.match(s) | 
					
						
							|  |  |  |         if __debug__: | 
					
						
							|  |  |  |             if self.mo is not None and self.debug >= 5: | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |                 self._mesg("\tmatched r'%s' => %r" % (cre.pattern, self.mo.groups())) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return self.mo is not None | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _new_tag(self): | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         tag = '%s%s' % (self.tagpre, self.tagnum) | 
					
						
							|  |  |  |         self.tagnum = self.tagnum + 1 | 
					
						
							|  |  |  |         self.tagged_commands[tag] = None | 
					
						
							|  |  |  |         return tag | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _checkquote(self, arg): | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         # Must quote command args if non-alphanumeric chars present, | 
					
						
							|  |  |  |         # and not already quoted. | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if type(arg) is not type(''): | 
					
						
							|  |  |  |             return arg | 
					
						
							| 
									
										
										
										
											2004-10-08 04:05:39 +00:00
										 |  |  |         if len(arg) >= 2 and (arg[0],arg[-1]) in (('(',')'),('"','"')): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             return arg | 
					
						
							| 
									
										
										
										
											2004-10-08 04:05:39 +00:00
										 |  |  |         if arg and self.mustquote.search(arg) is None: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             return arg | 
					
						
							|  |  |  |         return self._quote(arg) | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _quote(self, arg): | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-02-09 06:50:21 +00:00
										 |  |  |         arg = arg.replace('\\', '\\\\') | 
					
						
							|  |  |  |         arg = arg.replace('"', '\\"') | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return '"%s"' % arg | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _simple_command(self, name, *args): | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-27 20:14:51 +00:00
										 |  |  |         return self._command_complete(name, self._command(name, *args)) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     def _untagged_response(self, typ, dat, name): | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if typ == 'NO': | 
					
						
							|  |  |  |             return typ, dat | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |         if not name in self.untagged_responses: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             return typ, [None] | 
					
						
							| 
									
										
										
										
											2002-06-30 03:39:14 +00:00
										 |  |  |         data = self.untagged_responses.pop(name) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         if __debug__: | 
					
						
							|  |  |  |             if self.debug >= 5: | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 self._mesg('untagged_responses[%s] => %s' % (name, data)) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return typ, data | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |     if __debug__: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def _mesg(self, s, secs=None): | 
					
						
							|  |  |  |             if secs is None: | 
					
						
							|  |  |  |                 secs = time.time() | 
					
						
							|  |  |  |             tm = time.strftime('%M:%S', time.localtime(secs)) | 
					
						
							|  |  |  |             sys.stderr.write('  %s.%02d %s\n' % (tm, (secs*100)%100, s)) | 
					
						
							|  |  |  |             sys.stderr.flush() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def _dump_ur(self, dict): | 
					
						
							|  |  |  |             # Dump untagged responses (in `dict'). | 
					
						
							|  |  |  |             l = dict.items() | 
					
						
							|  |  |  |             if not l: return | 
					
						
							|  |  |  |             t = '\n\t\t' | 
					
						
							|  |  |  |             l = map(lambda x:'%s: "%s"' % (x[0], x[1][0] and '" "'.join(x[1]) or ''), l) | 
					
						
							|  |  |  |             self._mesg('untagged responses dump:%s%s' % (t, t.join(l))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def _log(self, line): | 
					
						
							|  |  |  |             # Keep log of last `_cmd_log_len' interactions for debugging. | 
					
						
							|  |  |  |             self._cmd_log[self._cmd_log_idx] = (line, time.time()) | 
					
						
							|  |  |  |             self._cmd_log_idx += 1 | 
					
						
							|  |  |  |             if self._cmd_log_idx >= self._cmd_log_len: | 
					
						
							|  |  |  |                 self._cmd_log_idx = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def print_log(self): | 
					
						
							|  |  |  |             self._mesg('last %d IMAP4 interactions:' % len(self._cmd_log)) | 
					
						
							|  |  |  |             i, n = self._cmd_log_idx, self._cmd_log_len | 
					
						
							|  |  |  |             while n: | 
					
						
							|  |  |  |                 try: | 
					
						
							| 
									
										
										
										
											2003-02-27 20:14:51 +00:00
										 |  |  |                     self._mesg(*self._cmd_log[i]) | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |                 except: | 
					
						
							|  |  |  |                     pass | 
					
						
							|  |  |  |                 i += 1 | 
					
						
							|  |  |  |                 if i >= self._cmd_log_len: | 
					
						
							|  |  |  |                     i = 0 | 
					
						
							|  |  |  |                 n -= 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  | class IMAP4_SSL(IMAP4): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """IMAP4 client class over SSL connection
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-03-08 09:05:12 +00:00
										 |  |  |     Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile]]]]) | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             host - host's name (default: localhost); | 
					
						
							|  |  |  |             port - port number (default: standard IMAP4 SSL port). | 
					
						
							|  |  |  |             keyfile - PEM formatted file that contains your private key (default: None); | 
					
						
							|  |  |  |             certfile - PEM formatted certificate chain file (default: None); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for more documentation see the docstring of the parent class IMAP4. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, host = '', port = IMAP4_SSL_PORT, keyfile = None, certfile = None): | 
					
						
							|  |  |  |         self.keyfile = keyfile | 
					
						
							|  |  |  |         self.certfile = certfile | 
					
						
							|  |  |  |         IMAP4.__init__(self, host, port) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-06-05 22:31:57 +00:00
										 |  |  |     def open(self, host = '', port = IMAP4_SSL_PORT): | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  |         """Setup connection to remote server on "host:port".
 | 
					
						
							| 
									
										
										
										
											2002-06-05 22:31:57 +00:00
										 |  |  |             (default: localhost:standard IMAP4 SSL port). | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  |         This connection will be used by the routines: | 
					
						
							|  |  |  |             read, readline, send, shutdown. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2002-06-05 22:31:57 +00:00
										 |  |  |         self.host = host | 
					
						
							|  |  |  |         self.port = port | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  |         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | 
					
						
							| 
									
										
										
										
											2002-06-05 22:31:57 +00:00
										 |  |  |         self.sock.connect((host, port)) | 
					
						
							|  |  |  |         self.sslobj = socket.ssl(self.sock, self.keyfile, self.certfile) | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def read(self, size): | 
					
						
							|  |  |  |         """Read 'size' bytes from remote.""" | 
					
						
							| 
									
										
										
										
											2002-06-23 10:47:13 +00:00
										 |  |  |         # sslobj.read() sometimes returns < size bytes | 
					
						
							| 
									
										
										
										
											2004-05-20 01:16:14 +00:00
										 |  |  |         chunks = [] | 
					
						
							|  |  |  |         read = 0 | 
					
						
							|  |  |  |         while read < size: | 
					
						
							|  |  |  |             data = self.sslobj.read(size-read) | 
					
						
							|  |  |  |             read += len(data) | 
					
						
							| 
									
										
										
										
											2004-05-20 11:32:35 +00:00
										 |  |  |             chunks.append(data) | 
					
						
							| 
									
										
										
										
											2002-06-23 10:47:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-05-20 01:16:14 +00:00
										 |  |  |         return ''.join(chunks) | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def readline(self): | 
					
						
							|  |  |  |         """Read line from remote.""" | 
					
						
							| 
									
										
										
										
											2002-06-23 10:47:13 +00:00
										 |  |  |         # NB: socket.ssl needs a "readline" method, or perhaps a "makefile" method. | 
					
						
							| 
									
										
										
										
											2004-05-20 01:16:14 +00:00
										 |  |  |         line = [] | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  |         while 1: | 
					
						
							|  |  |  |             char = self.sslobj.read(1) | 
					
						
							| 
									
										
										
										
											2004-05-20 01:16:14 +00:00
										 |  |  |             line.append(char) | 
					
						
							|  |  |  |             if char == "\n": return ''.join(line) | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def send(self, data): | 
					
						
							|  |  |  |         """Send data to remote.""" | 
					
						
							| 
									
										
										
										
											2002-06-23 10:47:13 +00:00
										 |  |  |         # NB: socket.ssl needs a "sendall" method to match socket objects. | 
					
						
							|  |  |  |         bytes = len(data) | 
					
						
							|  |  |  |         while bytes > 0: | 
					
						
							|  |  |  |             sent = self.sslobj.write(data) | 
					
						
							|  |  |  |             if sent == bytes: | 
					
						
							|  |  |  |                 break    # avoid copy | 
					
						
							|  |  |  |             data = data[sent:] | 
					
						
							|  |  |  |             bytes = bytes - sent | 
					
						
							| 
									
										
										
										
											2002-03-08 01:53:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def shutdown(self): | 
					
						
							|  |  |  |         """Close I/O established in "open".""" | 
					
						
							|  |  |  |         self.sock.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def socket(self): | 
					
						
							|  |  |  |         """Return socket instance used to connect to IMAP4 server.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         socket = <instance>.socket() | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self.sock | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def ssl(self): | 
					
						
							|  |  |  |         """Return SSLObject instance used to communicate with the IMAP4 server.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ssl = <instance>.socket.ssl() | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self.sslobj | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  | class IMAP4_stream(IMAP4): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """IMAP4 client class over a stream
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Instantiate with: IMAP4_stream(command) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             where "command" is a string that can be passed to os.popen2() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for more documentation see the docstring of the parent class IMAP4. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, command): | 
					
						
							|  |  |  |         self.command = command | 
					
						
							|  |  |  |         IMAP4.__init__(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def open(self, host = None, port = None): | 
					
						
							|  |  |  |         """Setup a stream connection.
 | 
					
						
							|  |  |  |         This connection will be used by the routines: | 
					
						
							|  |  |  |             read, readline, send, shutdown. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self.host = None        # For compatibility with parent class | 
					
						
							|  |  |  |         self.port = None | 
					
						
							|  |  |  |         self.sock = None | 
					
						
							|  |  |  |         self.file = None | 
					
						
							|  |  |  |         self.writefile, self.readfile = os.popen2(self.command) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def read(self, size): | 
					
						
							|  |  |  |         """Read 'size' bytes from remote.""" | 
					
						
							|  |  |  |         return self.readfile.read(size) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def readline(self): | 
					
						
							|  |  |  |         """Read line from remote.""" | 
					
						
							|  |  |  |         return self.readfile.readline() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def send(self, data): | 
					
						
							|  |  |  |         """Send data to remote.""" | 
					
						
							|  |  |  |         self.writefile.write(data) | 
					
						
							|  |  |  |         self.writefile.flush() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def shutdown(self): | 
					
						
							|  |  |  |         """Close I/O established in "open".""" | 
					
						
							|  |  |  |         self.readfile.close() | 
					
						
							|  |  |  |         self.writefile.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | class _Authenticator: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """Private class to provide en/decoding
 | 
					
						
							|  |  |  |             for base64-based authentication conversation. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, mechinst): | 
					
						
							|  |  |  |         self.mech = mechinst    # Callable object to provide/process data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def process(self, data): | 
					
						
							|  |  |  |         ret = self.mech(self.decode(data)) | 
					
						
							|  |  |  |         if ret is None: | 
					
						
							|  |  |  |             return '*'      # Abort conversation | 
					
						
							|  |  |  |         return self.encode(ret) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def encode(self, inp): | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         #  Invoke binascii.b2a_base64 iteratively with | 
					
						
							|  |  |  |         #  short even length buffers, strip the trailing | 
					
						
							|  |  |  |         #  line feed from the result and append.  "Even" | 
					
						
							|  |  |  |         #  means a number that factors to both 6 and 8, | 
					
						
							|  |  |  |         #  so when it gets to the end of the 8-bit input | 
					
						
							|  |  |  |         #  there's no partial 6-bit output. | 
					
						
							|  |  |  |         # | 
					
						
							|  |  |  |         oup = '' | 
					
						
							|  |  |  |         while inp: | 
					
						
							|  |  |  |             if len(inp) > 48: | 
					
						
							|  |  |  |                 t = inp[:48] | 
					
						
							|  |  |  |                 inp = inp[48:] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 t = inp | 
					
						
							|  |  |  |                 inp = '' | 
					
						
							|  |  |  |             e = binascii.b2a_base64(t) | 
					
						
							|  |  |  |             if e: | 
					
						
							|  |  |  |                 oup = oup + e[:-1] | 
					
						
							|  |  |  |         return oup | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def decode(self, inp): | 
					
						
							|  |  |  |         if not inp: | 
					
						
							|  |  |  |             return '' | 
					
						
							|  |  |  |         return binascii.a2b_base64(inp) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-06-18 14:24:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | Mon2num = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12} | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def Internaldate2tuple(resp): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """Convert IMAP4 INTERNALDATE to UT.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     Returns Python time module tuple. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     mo = InternalDate.match(resp) | 
					
						
							|  |  |  |     if not mo: | 
					
						
							|  |  |  |         return None | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     mon = Mon2num[mo.group('mon')] | 
					
						
							|  |  |  |     zonen = mo.group('zonen') | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-02-22 13:24:27 +00:00
										 |  |  |     day = int(mo.group('day')) | 
					
						
							|  |  |  |     year = int(mo.group('year')) | 
					
						
							|  |  |  |     hour = int(mo.group('hour')) | 
					
						
							|  |  |  |     min = int(mo.group('min')) | 
					
						
							|  |  |  |     sec = int(mo.group('sec')) | 
					
						
							|  |  |  |     zoneh = int(mo.group('zoneh')) | 
					
						
							|  |  |  |     zonem = int(mo.group('zonem')) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     # INTERNALDATE timezone must be subtracted to get UT | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     zone = (zoneh*60 + zonem)*60 | 
					
						
							|  |  |  |     if zonen == '-': | 
					
						
							|  |  |  |         zone = -zone | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     tt = (year, mon, day, hour, min, sec, -1, -1, -1) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     utc = time.mktime(tt) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     # Following is necessary because the time module has no 'mkgmtime'. | 
					
						
							|  |  |  |     # 'mktime' assumes arg in local timezone, so adds timezone/altzone. | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     lt = time.localtime(utc) | 
					
						
							|  |  |  |     if time.daylight and lt[-1]: | 
					
						
							|  |  |  |         zone = zone + time.altzone | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         zone = zone + time.timezone | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     return time.localtime(utc - zone) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def Int2AP(num): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """Convert integer to A-P string representation.""" | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     val = ''; AP = 'ABCDEFGHIJKLMNOP' | 
					
						
							|  |  |  |     num = int(abs(num)) | 
					
						
							|  |  |  |     while num: | 
					
						
							|  |  |  |         num, mod = divmod(num, 16) | 
					
						
							|  |  |  |         val = AP[mod] + val | 
					
						
							|  |  |  |     return val | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def ParseFlags(resp): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """Convert IMAP4 flags response to python tuple.""" | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     mo = Flags.match(resp) | 
					
						
							|  |  |  |     if not mo: | 
					
						
							|  |  |  |         return () | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-02-09 06:50:21 +00:00
										 |  |  |     return tuple(mo.group('flags').split()) | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def Time2Internaldate(date_time): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """Convert 'date_time' to IMAP4 INTERNALDATE representation.
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     Return string in form: '"DD-Mmm-YYYY HH:MM:SS +HHMM"' | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-01-05 17:17:09 +00:00
										 |  |  |     if isinstance(date_time, (int, float)): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         tt = time.localtime(date_time) | 
					
						
							| 
									
										
										
										
											2002-01-05 17:17:09 +00:00
										 |  |  |     elif isinstance(date_time, (tuple, time.struct_time)): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         tt = date_time | 
					
						
							| 
									
										
										
										
											2002-06-17 07:07:20 +00:00
										 |  |  |     elif isinstance(date_time, str) and (date_time[0],date_time[-1]) == ('"','"'): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return date_time        # Assume in correct format | 
					
						
							| 
									
										
										
										
											2002-01-05 17:17:09 +00:00
										 |  |  |     else: | 
					
						
							|  |  |  |         raise ValueError("date_time not of a known type") | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     dt = time.strftime("%d-%b-%Y %H:%M:%S", tt) | 
					
						
							|  |  |  |     if dt[0] == '0': | 
					
						
							|  |  |  |         dt = ' ' + dt[1:] | 
					
						
							|  |  |  |     if time.daylight and tt[-1]: | 
					
						
							|  |  |  |         zone = -time.altzone | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         zone = -time.timezone | 
					
						
							| 
									
										
										
										
											2004-09-27 15:29:05 +00:00
										 |  |  |     return '"' + dt + " %+03d%02d" % divmod(zone//60, 60) + '"' | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-12-13 23:27:45 +00:00
										 |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											1998-04-09 13:51:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |     # To test: invoke either as 'python imaplib.py [IMAP4_server_hostname]' | 
					
						
							|  |  |  |     # or 'python imaplib.py -s "rsh IMAP4_server_hostname exec /etc/rimapd"' | 
					
						
							|  |  |  |     # to test the IMAP4_stream class | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-08-13 15:34:41 +00:00
										 |  |  |     import getopt, getpass | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |         optlist, args = getopt.getopt(sys.argv[1:], 'd:s:') | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     except getopt.error, val: | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |         optlist, args = (), () | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |     stream_command = None | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     for opt,val in optlist: | 
					
						
							|  |  |  |         if opt == '-d': | 
					
						
							|  |  |  |             Debug = int(val) | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |         elif opt == '-s': | 
					
						
							|  |  |  |             stream_command = val | 
					
						
							|  |  |  |             if not args: args = (stream_command,) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if not args: args = ('',) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     host = args[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     USER = getpass.getuser() | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |     PASSWD = getpass.getpass("IMAP password for %s on %s: " % (USER, host or "localhost")) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-29 23:40:59 +00:00
										 |  |  |     test_mesg = 'From: %(user)s@localhost%(lf)sSubject: IMAP4 test%(lf)s%(lf)sdata...%(lf)s' % {'user':USER, 'lf':'\n'} | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     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')), | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |     ('fetch', ('1', '(FLAGS INTERNALDATE RFC822)')), | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     ('store', ('1', 'FLAGS', '(\Deleted)')), | 
					
						
							| 
									
										
										
										
											2001-07-20 10:52:06 +00:00
										 |  |  |     ('namespace', ()), | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     ('expunge', ()), | 
					
						
							|  |  |  |     ('recent', ()), | 
					
						
							|  |  |  |     ('close', ()), | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test_seq2 = ( | 
					
						
							|  |  |  |     ('select', ()), | 
					
						
							|  |  |  |     ('response',('UIDVALIDITY',)), | 
					
						
							|  |  |  |     ('uid', ('SEARCH', 'ALL')), | 
					
						
							|  |  |  |     ('response', ('EXISTS',)), | 
					
						
							|  |  |  |     ('append', (None, None, None, test_mesg)), | 
					
						
							|  |  |  |     ('recent', ()), | 
					
						
							|  |  |  |     ('logout', ()), | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def run(cmd, args): | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |         M._mesg('%s %s' % (cmd, args)) | 
					
						
							| 
									
										
										
										
											2003-02-27 20:14:51 +00:00
										 |  |  |         typ, dat = getattr(M, cmd)(*args) | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |         M._mesg('%s => %s %s' % (cmd, typ, dat)) | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |         if typ == 'NO': raise dat[0] | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         return dat | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2002-11-22 05:53:04 +00:00
										 |  |  |         if stream_command: | 
					
						
							|  |  |  |             M = IMAP4_stream(stream_command) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             M = IMAP4(host) | 
					
						
							|  |  |  |         if M.state == 'AUTH': | 
					
						
							| 
									
										
										
										
											2002-11-24 02:35:35 +00:00
										 |  |  |             test_seq1 = test_seq1[1:]   # Login not needed | 
					
						
							| 
									
										
										
										
											2002-02-22 01:15:17 +00:00
										 |  |  |         M._mesg('PROTOCOL_VERSION = %s' % M.PROTOCOL_VERSION) | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |         M._mesg('CAPABILITIES = %r' % (M.capabilities,)) | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for cmd,args in test_seq1: | 
					
						
							|  |  |  |             run(cmd, args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for ml in run('list', ('/tmp/', 'yy%')): | 
					
						
							|  |  |  |             mo = re.match(r'.*"([^"]+)"$', ml) | 
					
						
							|  |  |  |             if mo: path = mo.group(1) | 
					
						
							| 
									
										
										
										
											2001-02-09 06:50:21 +00:00
										 |  |  |             else: path = ml.split()[-1] | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             run('delete', (path,)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for cmd,args in test_seq2: | 
					
						
							|  |  |  |             dat = run(cmd, args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (cmd,args) != ('uid', ('SEARCH', 'ALL')): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-02-09 06:50:21 +00:00
										 |  |  |             uid = dat[-1].split() | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             if not uid: continue | 
					
						
							|  |  |  |             run('uid', ('FETCH', '%s' % uid[-1], | 
					
						
							|  |  |  |                     '(FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER RFC822.TEXT)')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         print '\nAll tests OK.' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     except: | 
					
						
							|  |  |  |         print '\nTests failed.' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not Debug: | 
					
						
							|  |  |  |             print '''
 | 
					
						
							| 
									
										
										
										
											2000-03-28 20:20:53 +00:00
										 |  |  | If you would like to see debugging output, | 
					
						
							|  |  |  | try: %s -d5 | 
					
						
							|  |  |  | ''' % sys.argv[0]
 | 
					
						
							| 
									
										
										
										
											1998-04-11 01:22:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         raise |