Changeset 116:a975e50ceb32
- Timestamp:
- 11/01/10 19:14:38 (2 years ago)
- Author:
- Menno Smits <menno@…>
- Branch:
- default
- Parents:
- 115:7aa870b75d11 (diff), 98:bd244d16fcea (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
- Message:
-
Merged from trunk
- Files:
-
Legend:
- Unmodified
- Added
- Removed
-
|
r115
|
r116
|
|
| 11 | 11 | import imap_utf7 |
| 12 | 12 | from fixed_offset import FixedOffset |
| | 13 | |
| 13 | 14 | |
| 14 | 15 | __all__ = ['IMAPClient', 'DELETED', 'SEEN', 'ANSWERED', 'FLAGGED', 'DRAFT', |
| … |
… |
|
| 27 | 28 | |
| 28 | 29 | class IMAPClient(object): |
| 29 | | ''' |
| | 30 | """ |
| 30 | 31 | A Pythonic, easy-to-use IMAP client class. |
| 31 | 32 | |
| … |
… |
|
| 59 | 60 | These are aliases for the imaplib.IMAP4 exceptions of the same name. Socket |
| 60 | 61 | errors may also be raised in the case of network errors. |
| 61 | | ''' |
| | 62 | """ |
| 62 | 63 | |
| 63 | 64 | Error = imaplib.IMAP4.error |
| … |
… |
|
| 66 | 67 | |
| 67 | 68 | re_sep = re.compile('^\(\("[^"]*" "([^"]+)"\)\)') |
| 68 | | re_folder = re.compile('\([^)]*\) "[^"]+" "?([^"]+)"?') |
| | 69 | # re_folder = re.compile('\([^)]*\) "[^"]+" "?([^"]+)"?') |
| | 70 | re_folder = re.compile(r'\([^)]*\) "[^"]+" (?P<qqq>"?)(?P<folder>.+)(?P=qqq)') |
| 69 | 71 | re_status = re.compile(r'^\s*"?(?P<folder>[^"]+)"?\s+' |
| 70 | 72 | r'\((?P<status_items>.*)\)$') |
| 71 | 73 | |
| 72 | 74 | def __init__(self, host, port=None, use_uid=True, ssl=False): |
| 73 | | '''Initialise object instance and connect to the remote IMAP server. |
| | 75 | """Initialise object instance and connect to the remote IMAP server. |
| 74 | 76 | |
| 75 | 77 | @param host: The IMAP server address/hostname to connect to. |
| … |
… |
|
| 77 | 79 | @param use_uid: Should message UIDs be used (default is True). |
| 78 | 80 | @param ssl: Make an SSL connection (default is False) |
| 79 | | ''' |
| | 81 | """ |
| 80 | 82 | if ssl: |
| 81 | 83 | ImapClass = imaplib.IMAP4_SSL |
| … |
… |
|
| 94 | 96 | |
| 95 | 97 | def login(self, username, password): |
| 96 | | '''Perform a simple login |
| 97 | | ''' |
| | 98 | """Perform a simple login |
| | 99 | """ |
| 98 | 100 | typ, data = self._imap.login(username, password) |
| 99 | 101 | self._checkok('login', typ, data) |
| … |
… |
|
| 102 | 104 | |
| 103 | 105 | def logout(self): |
| 104 | | '''Perform a logout |
| 105 | | ''' |
| | 106 | """Perform a logout |
| | 107 | """ |
| 106 | 108 | typ, data = self._imap.logout() |
| 107 | 109 | self._checkbye('logout', typ, data) |
| … |
… |
|
| 110 | 112 | |
| 111 | 113 | def capabilities(self): |
| 112 | | '''Returns the server capability list |
| 113 | | ''' |
| | 114 | """Returns the server capability list |
| | 115 | """ |
| 114 | 116 | return self._imap.capabilities |
| 115 | 117 | |
| 116 | 118 | |
| 117 | 119 | def has_capability(self, capability): |
| 118 | | '''Checks if the server has the given capability. |
| | 120 | """Checks if the server has the given capability. |
| 119 | 121 | |
| 120 | 122 | @param capability: capability to test (eg 'SORT') |
| 121 | | ''' |
| | 123 | """ |
| 122 | 124 | # FIXME: this will not detect capabilities that are backwards |
| 123 | 125 | # compatible with the current level. For instance the SORT |
| … |
… |
|
| 131 | 133 | |
| 132 | 134 | def get_folder_delimiter(self): |
| 133 | | '''Determine the folder separator used by the IMAP server. |
| | 135 | """Determine the folder separator used by the IMAP server. |
| 134 | 136 | |
| 135 | 137 | @return: The folder separator. |
| 136 | 138 | @rtype: string |
| 137 | | ''' |
| | 139 | """ |
| 138 | 140 | typ, data = self._imap.namespace() |
| 139 | 141 | self._checkok('namespace', typ, data) |
| … |
… |
|
| 147 | 149 | |
| 148 | 150 | def list_folders(self, directory="", pattern="*"): |
| 149 | | '''Get a listing of folders on the server. |
| | 151 | """Get a listing of folders on the server. |
| 150 | 152 | |
| 151 | 153 | The default behaviour (no args) will list all folders for the logged in |
| … |
… |
|
| 159 | 161 | decoding). If the folder_encode attribute is False, no decoding |
| 160 | 162 | will be performed and only ordinary strings will be returned. |
| 161 | | ''' |
| | 163 | """ |
| 162 | 164 | typ, data = self._imap.list(directory, pattern) |
| 163 | 165 | self._checkok('list', typ, data) |
| 164 | | |
| | 166 | return self._proc_folder_list(data) |
| | 167 | |
| | 168 | |
| | 169 | def list_sub_folders(self, directory="", pattern="*"): |
| | 170 | """Get a listing of subscribed folders on the server. |
| | 171 | |
| | 172 | The default behaviour (no args) will list all subscribed folders for the |
| | 173 | logged in user. |
| | 174 | |
| | 175 | @param directory: The base directory to look for folders from. |
| | 176 | @param pattern: A pattern to match against folder names. Only folder |
| | 177 | names matching this pattern will be returned. Wildcards accepted. |
| | 178 | @return: A list of folder names. As per the return of list_folders(). |
| | 179 | """ |
| | 180 | typ, data = self._imap.lsub(directory, pattern) |
| | 181 | self._checkok('lsub', typ, data) |
| | 182 | return self._proc_folder_list(data) |
| | 183 | |
| | 184 | |
| | 185 | def _proc_folder_list(self, folder_data): |
| 165 | 186 | folders = [] |
| 166 | | for line in data: |
| | 187 | for line in folder_data: |
| 167 | 188 | #TODO can the FetchParser code be adapted for use here? |
| 168 | 189 | folder_text = None |
| 169 | 190 | if isinstance(line, tuple): |
| 170 | 191 | folder_text = line[-1] |
| 171 | | else: |
| | 192 | elif line: |
| 172 | 193 | match = self.re_folder.match(line) |
| 173 | 194 | if match: |
| 174 | | folder_text = match.group(1) |
| | 195 | folder_text = match.group('folder') |
| | 196 | folder_text = folder_text.replace(r'\"', '"') |
| | 197 | folder_text = folder_text.replace(r'\\', '\\') |
| 175 | 198 | if folder_text is not None: |
| 176 | 199 | folders.append(self._decode_folder_name(folder_text)) |
| 177 | 200 | return folders |
| 178 | | |
| 179 | | |
| 180 | | def list_sub_folders(self, directory="", pattern="*"): |
| 181 | | '''Get a listing of subscribed folders on the server. |
| 182 | | |
| 183 | | The default behaviour (no args) will list all subscribed folders for the |
| 184 | | logged in user. |
| 185 | | |
| 186 | | @param directory: The base directory to look for folders from. |
| 187 | | @param pattern: A pattern to match against folder names. Only folder |
| 188 | | names matching this pattern will be returned. Wildcards accepted. |
| 189 | | @return: A list of folder names. As per the return of list_folders(). |
| 190 | | ''' |
| 191 | | typ, data = self._imap.lsub(directory, pattern) |
| 192 | | self._checkok('lsub', typ, data) |
| 193 | | |
| 194 | | folders = [] |
| 195 | | for line in data: |
| 196 | | if line: |
| 197 | | m = self.re_folder.match(line) |
| 198 | | if m: |
| 199 | | folders.append(self._decode_folder_name(m.group(1))) |
| 200 | | return folders |
| 201 | | |
| | 201 | |
| 202 | 202 | |
| 203 | 203 | def select_folder(self, folder): |
| 204 | | '''Select the current folder on the server. Future calls to methods |
| | 204 | """Select the current folder on the server. Future calls to methods |
| 205 | 205 | such as search and fetch will act on the selected folder. |
| 206 | 206 | |
| … |
… |
|
| 208 | 208 | @return: Number of messages in the folder. |
| 209 | 209 | @rtype: long int |
| 210 | | ''' |
| | 210 | """ |
| 211 | 211 | typ, data = self._imap.select(self._encode_folder_name(folder)) |
| 212 | 212 | self._checkok('select', typ, data) |
| … |
… |
|
| 215 | 215 | |
| 216 | 216 | def folder_status(self, folder, what=None): |
| 217 | | '''Requests the status from folder. |
| | 217 | """Requests the status from folder. |
| 218 | 218 | |
| 219 | 219 | @param folder: The folder name. |
| … |
… |
|
| 223 | 223 | the items specified in the what parameter. |
| 224 | 224 | @rtype: dict |
| 225 | | ''' |
| | 225 | """ |
| 226 | 226 | if what is None: |
| 227 | 227 | what = ('MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN') |
| … |
… |
|
| 247 | 247 | |
| 248 | 248 | def close_folder(self): |
| 249 | | '''Close the currently selected folder. |
| | 249 | """Close the currently selected folder. |
| 250 | 250 | |
| 251 | 251 | @return: Server response. |
| 252 | | ''' |
| | 252 | """ |
| 253 | 253 | typ, data = self._imap.close() |
| 254 | 254 | self._checkok('close', typ, data) |
| … |
… |
|
| 257 | 257 | |
| 258 | 258 | def create_folder(self, folder): |
| 259 | | '''Create a new folder on the server. |
| | 259 | """Create a new folder on the server. |
| 260 | 260 | |
| 261 | 261 | @param folder: The folder name. |
| 262 | 262 | @return: Server response. |
| 263 | | ''' |
| | 263 | """ |
| 264 | 264 | typ, data = self._imap.create(self._encode_folder_name(folder)) |
| 265 | 265 | self._checkok('create', typ, data) |
| … |
… |
|
| 268 | 268 | |
| 269 | 269 | def delete_folder(self, folder): |
| 270 | | '''Delete a new folder on the server. |
| | 270 | """Delete a new folder on the server. |
| 271 | 271 | |
| 272 | 272 | @param folder: Folder name to delete. |
| 273 | 273 | @return: Server response. |
| 274 | | ''' |
| | 274 | """ |
| 275 | 275 | typ, data = self._imap.delete(self._encode_folder_name(folder)) |
| 276 | 276 | self._checkok('delete', typ, data) |
| … |
… |
|
| 279 | 279 | |
| 280 | 280 | def folder_exists(self, folder): |
| 281 | | '''Determine if a folder exists on the server. |
| | 281 | """Determine if a folder exists on the server. |
| 282 | 282 | |
| 283 | 283 | @param folder: Full folder name to look for. |
| 284 | 284 | @return: True if the folder exists. False otherwise. |
| 285 | | ''' |
| | 285 | """ |
| 286 | 286 | typ, data = self._imap.list('', self._encode_folder_name(folder)) |
| 287 | 287 | self._checkok('list', typ, data) |
| … |
… |
|
| 290 | 290 | |
| 291 | 291 | def subscribe_folder(self, folder): |
| 292 | | '''Subscribe to a folder. |
| | 292 | """Subscribe to a folder. |
| 293 | 293 | |
| 294 | 294 | @param folder: Folder name to subscribe to. |
| 295 | 295 | @return: Server response message. |
| 296 | | ''' |
| | 296 | """ |
| 297 | 297 | typ, data = self._imap.subscribe(self._encode_folder_name(folder)) |
| 298 | 298 | self._checkok('subscribe', typ, data) |
| … |
… |
|
| 301 | 301 | |
| 302 | 302 | def unsubscribe_folder(self, folder): |
| 303 | | '''Unsubscribe a folder. |
| | 303 | """Unsubscribe a folder. |
| 304 | 304 | |
| 305 | 305 | @param folder: Folder name to unsubscribe. |
| 306 | 306 | @return: Server response message. |
| 307 | | ''' |
| | 307 | """ |
| 308 | 308 | typ, data = self._imap.unsubscribe(self._encode_folder_name(folder)) |
| 309 | 309 | self._checkok('unsubscribe', typ, data) |
| … |
… |
|
| 336 | 336 | |
| 337 | 337 | def sort(self, sort_criteria, criteria='ALL', charset='UTF-8' ): |
| 338 | | '''Returns a list of messages sorted by sort_criteria. |
| | 338 | """Returns a list of messages sorted by sort_criteria. |
| 339 | 339 | |
| 340 | 340 | Note that this is an extension to the IMAP4: |
| 341 | 341 | http://www.ietf.org/internet-drafts/draft-ietf-imapext-sort-19.txt |
| 342 | | ''' |
| | 342 | """ |
| 343 | 343 | if not criteria: |
| 344 | 344 | raise ValueError('no criteria specified') |
| … |
… |
|
| 365 | 365 | |
| 366 | 366 | def get_flags(self, messages): |
| 367 | | '''Return the flags set for messages |
| | 367 | """Return the flags set for messages |
| 368 | 368 | |
| 369 | 369 | @param messages: Message IDs to check flags for |
| 370 | 370 | @return: As for add_f |
| 371 | 371 | { msgid1: [flag1, flag2, ... ], } |
| 372 | | ''' |
| | 372 | """ |
| 373 | 373 | response = self.fetch(messages, ['FLAGS']) |
| 374 | 374 | return self._flatten_dict(response) |
| … |
… |
|
| 376 | 376 | |
| 377 | 377 | def add_flags(self, messages, flags): |
| 378 | | '''Add one or more flags to messages |
| | 378 | """Add one or more flags to messages |
| 379 | 379 | |
| 380 | 380 | @param messages: Message IDs to add flags to |
| … |
… |
|
| 382 | 382 | @return: The flags set for each message ID as a dictionary |
| 383 | 383 | { msgid1: [flag1, flag2, ... ], } |
| 384 | | ''' |
| | 384 | """ |
| 385 | 385 | return self._store('+FLAGS', messages, flags) |
| 386 | 386 | |
| 387 | 387 | |
| 388 | 388 | def remove_flags(self, messages, flags): |
| 389 | | '''Remove one or more flags from messages |
| | 389 | """Remove one or more flags from messages |
| 390 | 390 | |
| 391 | 391 | @param messages: Message IDs to remove flags from |
| 392 | 392 | @param flags: Sequence of flags to remove |
| 393 | 393 | @return: As for get_flags. |
| 394 | | ''' |
| | 394 | """ |
| 395 | 395 | return self._store('-FLAGS', messages, flags) |
| 396 | 396 | |
| 397 | 397 | |
| 398 | 398 | def set_flags(self, messages, flags): |
| 399 | | '''Set the flags for messages |
| | 399 | """Set the flags for messages |
| 400 | 400 | |
| 401 | 401 | @param messages: Message IDs to set flags for |
| 402 | 402 | @param flags: Sequence of flags to set |
| 403 | 403 | @return: As for get_flags. |
| 404 | | ''' |
| | 404 | """ |
| 405 | 405 | return self._store('FLAGS', messages, flags) |
| 406 | 406 | |
| 407 | 407 | |
| 408 | 408 | def delete_messages(self, messages): |
| 409 | | '''Short-hand method for deleting one or more messages |
| | 409 | """Short-hand method for deleting one or more messages |
| 410 | 410 | |
| 411 | 411 | @param messages: Message IDs to mark for deletion. |
| 412 | 412 | @return: Same as for get_flags. |
| 413 | | ''' |
| | 413 | """ |
| 414 | 414 | return self.add_flags(messages, DELETED) |
| 415 | 415 | |
| 416 | 416 | |
| 417 | 417 | def fetch(self, messages, parts): |
| 418 | | '''Retrieve selected data items for one or more messages. |
| | 418 | """Retrieve selected data items for one or more messages. |
| 419 | 419 | |
| 420 | 420 | @param messages: Message IDs to fetch. |
| … |
… |
|
| 424 | 424 | INTERNALDATE parts will be returned as datetime objects converted |
| 425 | 425 | to the local machine's time zone. |
| 426 | | ''' |
| | 426 | """ |
| 427 | 427 | if not messages: |
| 428 | 428 | return {} |
| … |
… |
|
| 462 | 462 | |
| 463 | 463 | def append(self, folder, msg, flags=(), msg_time=None): |
| 464 | | '''Append a message to a folder |
| | 464 | """Append a message to a folder |
| 465 | 465 | |
| 466 | 466 | @param folder: Folder name to append to. |
| … |
… |
|
| 475 | 475 | @return: The append response returned by the server. |
| 476 | 476 | @rtype: str |
| 477 | | ''' |
| | 477 | """ |
| 478 | 478 | if msg_time: |
| 479 | 479 | time_val = '"%s"' % datetime_to_imap(msg_time) |
| … |
… |
|
| 497 | 497 | |
| 498 | 498 | def getacl(self, folder): |
| 499 | | '''Get the ACL for a folder |
| | 499 | """Get the ACL for a folder |
| 500 | 500 | |
| 501 | 501 | @param folder: Folder name to get the ACL for. |
| 502 | 502 | @return: A list of (who, acl) tuples |
| 503 | | ''' |
| | 503 | """ |
| 504 | 504 | typ, data = self._imap.getacl(folder) |
| 505 | 505 | self._checkok('getacl', typ, data) |
| … |
… |
|
| 515 | 515 | |
| 516 | 516 | def setacl(self, folder, who, what): |
| 517 | | '''Set an ACL for a folder |
| | 517 | """Set an ACL for a folder |
| 518 | 518 | |
| 519 | 519 | @param folder: Folder name to set an ACL for. |
| … |
… |
|
| 521 | 521 | @param what: A string describing the ACL. Set to '' to remove an ACL. |
| 522 | 522 | @return: Server response string. |
| 523 | | ''' |
| | 523 | """ |
| 524 | 524 | typ, data = self._imap.setacl(folder, who, what) |
| 525 | 525 | self._checkok('setacl', typ, data) |
| … |
… |
|
| 528 | 528 | |
| 529 | 529 | def _check_resp(self, expected, command, typ, data): |
| 530 | | '''Check command responses for errors. |
| | 530 | """Check command responses for errors. |
| 531 | 531 | |
| 532 | 532 | @raise: Error if a command failed. |
| 533 | | ''' |
| | 533 | """ |
| 534 | 534 | if typ != expected: |
| 535 | 535 | raise self.Error('%s failed: %r' % (command, data[0])) |
| … |
… |
|
| 545 | 545 | |
| 546 | 546 | def _store(self, cmd, messages, flags): |
| 547 | | '''Worker function for flag manipulation functions |
| | 547 | """Worker function for flag manipulation functions |
| 548 | 548 | |
| 549 | 549 | @param cmd: STORE command to use (eg. '+FLAGS') |
| … |
… |
|
| 552 | 552 | @return: The flags set for each message ID as a dictionary |
| 553 | 553 | { msgid1: [flag1, flag2, ... ], } |
| 554 | | ''' |
| | 554 | """ |
| 555 | 555 | if not messages: |
| 556 | 556 | return {} |
| … |
… |
|
| 587 | 587 | |
| 588 | 588 | class FetchParser(object): |
| 589 | | ''' |
| | 589 | """ |
| 590 | 590 | Parse an IMAP FETCH response and convert the return values to useful Python |
| 591 | 591 | values. |
| 592 | | ''' |
| | 592 | """ |
| 593 | 593 | |
| 594 | 594 | def parse(self, response): |
| … |
… |
|
| 623 | 623 | |
| 624 | 624 | data = data.lstrip() |
| | 625 | msgid = None |
| 625 | 626 | if data[0].isdigit(): |
| 626 | 627 | # Get message ID |
| … |
… |
|
| 630 | 631 | assert data.startswith('('), data |
| 631 | 632 | data = data[1:] |
| 632 | | if data.endswith(')'): |
| 633 | | data = data[:-1] |
| 634 | | |
| 635 | | else: |
| 636 | | msgid = None |
| | 633 | |
| | 634 | if data.endswith(')'): |
| | 635 | data = data[:-1] |
| 637 | 636 | |
| 638 | 637 | for name, item in FetchTokeniser().process_pairs(data): |
| … |
… |
|
| 658 | 657 | |
| 659 | 658 | def do_INTERNALDATE(self, arg): |
| 660 | | '''Process an INTERNALDATE response |
| | 659 | """Process an INTERNALDATE response |
| 661 | 660 | |
| 662 | 661 | @param arg: A quoted IMAP INTERNALDATE string |
| 663 | 662 | (eg. " 9-Feb-2007 17:08:08 +0000") |
| 664 | 663 | @return: datetime.datetime instance for the given time (in UTC) |
| 665 | | ''' |
| | 664 | """ |
| 666 | 665 | arg = 'INTERNALDATE "%s"' % arg |
| 667 | 666 | mo = imaplib.InternalDate.match(arg) |
| … |
… |
|
| 693 | 692 | |
| 694 | 693 | class FetchTokeniser(object): |
| 695 | | ''' |
| | 694 | """ |
| 696 | 695 | General response tokenizer and converter |
| 697 | | ''' |
| | 696 | """ |
| 698 | 697 | |
| 699 | 698 | QUOTED_STRING = '(?:".*?")' |
| … |
… |
|
| 715 | 714 | |
| 716 | 715 | def process_pairs(self, s): |
| 717 | | '''Break up and convert a string of FETCH response pairs |
| | 716 | """Break up and convert a string of FETCH response pairs |
| 718 | 717 | |
| 719 | 718 | @param s: FETCH response string eg. "FOO 12 BAH (1 abc def "foo bar")" |
| 720 | 719 | @return: Tokenised and converted input return as (name, data) pairs. |
| 721 | | ''' |
| | 720 | """ |
| 722 | 721 | out = [] |
| 723 | 722 | for m in strict_finditer(self.PAIR_RE, s): |
| … |
… |
|
| 727 | 726 | |
| 728 | 727 | def process_list(self, s): |
| 729 | | '''Break up and convert a string of data items |
| | 728 | """Break up and convert a string of data items |
| 730 | 729 | |
| 731 | 730 | @param s: FETCH response string eg. "(1 abc def "foo bar")" |
| 732 | 731 | @return: A list of converted items. |
| 733 | | ''' |
| | 732 | """ |
| 734 | 733 | if s == '': |
| 735 | 734 | return [] |
| … |
… |
|
| 755 | 754 | |
| 756 | 755 | class Literal(object): |
| 757 | | ''' |
| | 756 | """ |
| 758 | 757 | Simple class to represent a literal token in the fetch response |
| 759 | 758 | (eg. "{21}") |
| 760 | | ''' |
| | 759 | """ |
| 761 | 760 | |
| 762 | 761 | def __init__(self, length): |
| … |
… |
|
| 771 | 770 | |
| 772 | 771 | def strict_finditer(regex, s): |
| 773 | | '''Like re.finditer except the regex must match from exactly where the |
| | 772 | """Like re.finditer except the regex must match from exactly where the |
| 774 | 773 | previous match ended and all the entire input must be matched. |
| 775 | | ''' |
| | 774 | """ |
| 776 | 775 | i = 0 |
| 777 | 776 | matched = False |
| … |
… |
|
| 791 | 790 | |
| 792 | 791 | def messages_to_str(messages): |
| 793 | | '''Convert a sequence of messages ids or a single message id into an |
| | 792 | """Convert a sequence of messages ids or a single message id into an |
| 794 | 793 | message ID list for use with IMAP commands. |
| 795 | 794 | |
| … |
… |
|
| 797 | 796 | (eg. [1,4,5,7,8]) |
| 798 | 797 | @return: Message list string (eg. "1,4,5,6,8") |
| 799 | | ''' |
| | 798 | """ |
| 800 | 799 | if isinstance(messages, (str, int, long)): |
| 801 | 800 | messages = (messages,) |
| … |
… |
|
| 806 | 805 | |
| 807 | 806 | def seq_to_parenlist(flags): |
| 808 | | '''Convert a sequence into parenthised list for use with IMAP commands |
| | 807 | """Convert a sequence into parenthised list for use with IMAP commands |
| 809 | 808 | |
| 810 | 809 | @param flags: Sequence to process (eg. ['abc', 'def']) |
| 811 | 810 | @return: IMAP parenthenised list (eg. '(abc def)') |
| 812 | | ''' |
| | 811 | """ |
| 813 | 812 | if isinstance(flags, str): |
| 814 | 813 | flags = (flags,) |
| … |
… |
|
| 819 | 818 | |
| 820 | 819 | def datetime_to_imap(dt): |
| 821 | | '''Convert a datetime instance to a IMAP datetime string |
| | 820 | """Convert a datetime instance to a IMAP datetime string |
| 822 | 821 | |
| 823 | 822 | If timezone information is missing the current system timezone is used. |
| 824 | | ''' |
| | 823 | """ |
| 825 | 824 | if not dt.tzinfo: |
| 826 | 825 | dt = dt.replace(tzinfo=FixedOffset.for_system()) |
-
|
r96
|
r116
|
|
| 15 | 15 | __all__ = ['IMAPClient', 'DELETED', 'SEEN', 'ANSWERED', 'FLAGGED', 'DRAFT', |
| 16 | 16 | 'RECENT'] |
| | 17 | |
| | 18 | from response_parser import parse_fetch_response |
| 17 | 19 | |
| 18 | 20 | # System flags |
| … |
… |
|
| 327 | 329 | |
| 328 | 330 | self._checkok('search', typ, data) |
| | 331 | if data == [None]: # no untagged responses... |
| | 332 | return [] |
| 329 | 333 | |
| 330 | 334 | return [ long(i) for i in data[0].split() ] |
| … |
… |
|
| 436 | 440 | return parser(data) |
| 437 | 441 | |
| | 442 | def altfetch(self, messages, parts): |
| | 443 | if not messages: |
| | 444 | return {} |
| | 445 | |
| | 446 | msg_list = messages_to_str(messages) |
| | 447 | parts_list = seq_to_parenlist([p.upper() for p in parts]) |
| | 448 | |
| | 449 | if self.use_uid: |
| | 450 | tag = self._imap._command('UID', 'FETCH', msg_list, parts_list) |
| | 451 | else: |
| | 452 | tag = self._imap._command('FETCH', msg_list, parts_list) |
| | 453 | typ, data = self._imap._command_complete('FETCH', tag) |
| | 454 | self._checkok('fetch', typ, data) |
| | 455 | typ, data = self._imap._untagged_response(typ, data, 'FETCH') |
| | 456 | # appears to be a special case - no 'untagged' responses (ie, no |
| | 457 | # folders) results in [None] |
| | 458 | if data == [None]: |
| | 459 | return {} |
| | 460 | |
| | 461 | return parse_fetch_response(data) |
| 438 | 462 | |
| 439 | 463 | def append(self, folder, msg, flags=(), msg_time=None): |
| … |
… |
|
| 800 | 824 | if not dt.tzinfo: |
| 801 | 825 | dt = dt.replace(tzinfo=FixedOffset.for_system()) |
| 802 | | |
| 803 | 826 | return dt.strftime("%d-%b-%Y %H:%M:%S %z") |
| 804 | | |
| 805 | | |
| | 827 | |
| | 828 | |