Changeset 62:f2f61addae93
- Timestamp:
- 11/04/09 23:28:10 (3 years ago)
- Author:
- Menno Smits <menno@…>
- Branch:
- default
- convert_revision:
- menno@pali-20090411222810-0r33czn9bjwh5fsy
- Message:
-
Folder escaping is working. Live test is passing correctly. Still needs more testing and documentation updates.
- Files:
-
Legend:
- Unmodified
- Added
- Removed
-
|
r49
|
r62
|
|
| 1 | 1 | IMAPClient was created and is maintained by Menno Smits <menno@freshfoo.com>. |
| 2 | 2 | |
| 3 | | The project was started while the author was working at NetBox Blue |
| 4 | | (http://netboxblue.com/). |
| 5 | | |
| 6 | | Patches received with many thanks from: |
| 7 | | Helder Guerreiro (helder _AT_ paxjulia DOT com) |
-
|
r49
|
r62
|
|
| 1 | 1 | Thanks go to the following people for their help with this project. |
| | 2 | |
| | 3 | The project was partially developed while the author was working at NetBox Blue |
| | 4 | (http://netboxblue.com/). |
| 2 | 5 | |
| 3 | 6 | Helder Guerreiro (helder _AT_ paxjulia DOT com) |
| … |
… |
|
| 5 | 8 | command |
| 6 | 9 | |
| | 10 | Jp Calderone |
| | 11 | The contents of imapclient/imap_utf7.py and associated tests have been |
| | 12 | taken from the Twisted project (http://twistedmatrix.com/). This |
| | 13 | functionality was written by Jp Calderone. |
| | 14 | |
| 7 | 15 | Brian Jackson (iggy _AT_ theiggy DOT com) |
| 8 | 16 | - bug report |
-
|
r60
|
r62
|
|
| 1 | | - transparent "&" escaping |
| 2 | | - turn & into &- and vice versa |
| 3 | | - update README to list this feature |
| 4 | | - test against a bunch of servers |
| | 1 | - transparent folder escaping |
| | 2 | - more testing |
| 5 | 3 | - gmail |
| 6 | | - cyrus (netbox) |
| | 4 | - cyrus |
| | 5 | - exchange |
| 7 | 6 | - script to do this (in parallel) |
| | 7 | - update README, website and docstrings to advertise this feature |
| | 8 | |
| | 9 | - turn livetests into a nose based tested |
| | 10 | - make each test independent |
| | 11 | |
| 8 | 12 | - clean up basic command handling to avoid repetition |
| 9 | 13 | - might be able to metaprogram the way out of the currently clumsy UID handling situation |
| 10 | 14 | - releases with bzr |
| 11 | | - proper modified UTF-7 mailbox handling |
| 12 | | - convert Python unicode |
| 13 | | - update README to list this feature |
| 14 | | - handle servers that don't support UTF-7 mailbox names |
| 15 | | - can this be detected? |
| 16 | 15 | - automatic changelog generation |
| 17 | 16 | - support for more IMAP functions: sort, examine etc |
| … |
… |
|
| 19 | 18 | - write a comparision of imaplib vs imapclient.py |
| 20 | 19 | (presentation is a good start) |
| 21 | | - turn livetests into a nose based tested |
| 22 | | - remove the need for ordered execution |
| 23 | 20 | - use mailbox instead of folder to be consistent with RFCs |
| 24 | 21 | - higher level fetch methods for common or single attributes, flattened |
-
|
r61
|
r62
|
|
| 1 | | # The contents of this file has been derived from the Twisted project |
| 2 | | # (http://twistedmatrix.com/). This author is Jp Calderone. |
| | 1 | # The contents of this file has been derived code from the Twisted project |
| | 2 | # (http://twistedmatrix.com/). The original author is Jp Calderone. |
| 3 | 3 | |
| 4 | 4 | # Twisted project license follows: |
-
|
r60
|
r62
|
|
| 21 | 21 | #imaplib.Debug = 5 |
| 22 | 22 | |
| | 23 | #XXX |
| | 24 | import imap_utf7 |
| | 25 | |
| 23 | 26 | __all__ = ['IMAPClient', 'DELETED', 'SEEN', 'ANSWERED', 'FLAGGED', 'DRAFT', |
| 24 | 27 | 'RECENT'] |
| … |
… |
|
| 91 | 94 | self.folder_encode = True |
| 92 | 95 | |
| 93 | | |
| 94 | | def _set_folder_encode(self, on_off): |
| 95 | | if on_off: |
| 96 | | self._folder_encode = True |
| 97 | | self._decode_folder_name = decode_folder_name |
| 98 | | self._encode_folder_name = encode_folder_name |
| 99 | | else: |
| 100 | | self._folder_encode = False |
| 101 | | self._decode_folder_name = lambda x: x |
| 102 | | self._encode_folder_name = self._decode_folder_name |
| 103 | | |
| 104 | | folder_encode = property(lambda self: self._folder_encode, |
| 105 | | _set_folder_encode, |
| 106 | | None, |
| 107 | | "Set True to have folder names transparently encoded/decoded") |
| 108 | | |
| 109 | | |
| | 96 | |
| 110 | 97 | def login(self, username, password): |
| 111 | 98 | '''Perform a simple login |
| … |
… |
|
| 464 | 451 | flags_list = seq_to_parenlist(flags) |
| 465 | 452 | |
| 466 | | typ, data = self._imap.append(folder, flags_list, time_val, msg) |
| | 453 | typ, data = self._imap.append(self._encode_folder_name(folder), |
| | 454 | flags_list, time_val, msg) |
| 467 | 455 | self._checkok('append', typ, data) |
| 468 | 456 | |
| … |
… |
|
| 553 | 541 | for msgid, data in fetch_dict.iteritems() |
| 554 | 542 | ]) |
| | 543 | |
| | 544 | def _decode_folder_name(self, name): |
| | 545 | if self.folder_encode: |
| | 546 | return imap_utf7.decode(name) |
| | 547 | return name |
| | 548 | |
| | 549 | |
| | 550 | def _encode_folder_name(self, name): |
| | 551 | if self.folder_encode: |
| | 552 | return imap_utf7.encode(name) |
| | 553 | return name |
| 555 | 554 | |
| 556 | 555 | |
| … |
… |
|
| 767 | 766 | |
| 768 | 767 | |
| 769 | | def encode_folder_name(name): |
| 770 | | """Take a folder name and escape ampersands so that the correct name is |
| 771 | | seen by the IMAP server. |
| 772 | | |
| 773 | | @param name: Mailbox name (eg. "stuff & things") |
| 774 | | @return: Encoded mailbox name (eg. "stuff &- things") |
| 775 | | """ |
| 776 | | #TODO - full UTF-7 handling |
| 777 | | return name.replace('&', '&-') |
| 778 | | |
| 779 | | |
| 780 | | #XXX test that decode name matched re-encoded name, if it doesn't return the original |
| 781 | | def decode_folder_name(name): |
| 782 | | """Take a folder name as returned by an IMAP server and unescape |
| 783 | | ampersands so that the expected name is seen. |
| 784 | | |
| 785 | | @param name: Encoded mailbox name (eg. "stuff &- things") |
| 786 | | @return: Mailbox name (eg. "stuff & things") |
| 787 | | """ |
| 788 | | #TODO - full UTF-7 handling |
| 789 | | return name.replace('&-', '&') |
| 790 | | |
-
|
r61
|
r62
|
|
| 1 | | # The contents of this file has been derived from the Twisted project |
| 2 | | # (http://twistedmatrix.com/). This author is Jp Calderone. |
| | 1 | # The contents of this file has been derived code from the Twisted project |
| | 2 | # (http://twistedmatrix.com/). The original author is Jp Calderone. |
| 3 | 3 | |
| 4 | 4 | # Twisted project license follows: |
-
|
r60
|
r62
|
|
| 40 | 40 | #TODO: test other folders... |
| 41 | 41 | |
| | 42 | |
| 42 | 43 | def test_select_and_close(server): |
| 43 | 44 | num_msgs = server.select_folder('INBOX') |
| … |
… |
|
| 46 | 47 | server.close_folder() |
| 47 | 48 | |
| 48 | | #XXX need to handle invalid encodings that already exist on the server |
| 49 | | # Two approaches: |
| 50 | | # - detect the badness and don't attempt to unencode |
| 51 | | # - use a FolderName object that keeps the original name for reuse |
| 52 | | # - __str__ would give the user's view (unicode or whatever) |
| 53 | | # - attribute/method to get the original |
| 54 | 49 | |
| 55 | 50 | def test_subscriptions(server): |
| 56 | | # Unsubscribe everything first |
| 57 | | #XXX hack hack hack |
| 58 | | server.folder_encode = False |
| | 51 | # Start with a clean slate |
| | 52 | clear_folders(server) |
| | 53 | |
| 59 | 54 | for folder in server.list_sub_folders(): |
| 60 | 55 | server.unsubscribe_folder(folder) |
| 61 | | server.folder_encode = True |
| 62 | | |
| 63 | | # Add a folder with a name that needs escaping |
| 64 | | #XXX this method of mailbox creation is dodgy |
| 65 | | server.create_folder('sub & unsub - %s' % datetime.now().ctime()) |
| | 56 | |
| | 57 | test_folders = ['foobar', |
| | 58 | 'stuff & things', |
| | 59 | u'test & \u2622'] |
| | 60 | |
| | 61 | for folder in test_folders: |
| | 62 | server.create_folder(folder) |
| | 63 | |
| 66 | 64 | all_folders = sorted(server.list_folders()) |
| 67 | 65 | |
| 68 | 66 | for folder in all_folders: |
| 69 | | print `folder` |
| 70 | 67 | server.subscribe_folder(folder) |
| 71 | | print all_folders |
| 72 | | print sorted(server.list_sub_folders()) |
| | 68 | |
| 73 | 69 | assert all_folders == sorted(server.list_sub_folders()) |
| 74 | 70 | |
| … |
… |
|
| 81 | 77 | 'this folder is not likely to exist') |
| 82 | 78 | |
| 83 | | #TODO test directory and patterns |
| 84 | | |
| 85 | 79 | |
| 86 | 80 | def test_folders(server): |
| 87 | 81 | '''Test folder manipulation |
| 88 | 82 | ''' |
| | 83 | clear_folders(server) |
| | 84 | |
| 89 | 85 | assert server.folder_exists('INBOX') |
| 90 | 86 | assert not server.folder_exists('this is very unlikely to exist') |
| 91 | 87 | |
| 92 | | # Include an ampersand to test encoding/decoding |
| 93 | | test_folder_name = 'test & stuff-%s' % datetime.now().ctime() |
| 94 | | |
| 95 | | server.create_folder(test_folder_name) |
| 96 | | assert server.folder_exists(test_folder_name) |
| 97 | | assert test_folder_name in server.list_folders() |
| 98 | | |
| 99 | | server.folder_encode = False |
| 100 | | try: |
| 101 | | assert test_folder_name not in server.list_folders() |
| 102 | | assert test_folder_name.replace('&', '&-') in server.list_folders() |
| 103 | | finally: |
| 104 | | server.folder_encode = True |
| 105 | | |
| 106 | | server.select_folder(test_folder_name) |
| 107 | | server.close_folder() |
| 108 | | |
| 109 | | server.delete_folder(test_folder_name) |
| 110 | | assert not server.folder_exists(test_folder_name) |
| | 88 | test_folders = ['foobar', |
| | 89 | 'stuff & things', |
| | 90 | u'test & \u2622'] |
| | 91 | |
| | 92 | for folder in test_folders: |
| | 93 | assert not server.folder_exists(folder) |
| | 94 | |
| | 95 | server.create_folder(folder) |
| | 96 | |
| | 97 | assert server.folder_exists(folder) |
| | 98 | assert folder in server.list_folders() |
| | 99 | |
| | 100 | server.select_folder(folder) |
| | 101 | server.close_folder() |
| | 102 | |
| | 103 | server.delete_folder(folder) |
| | 104 | assert not server.folder_exists(folder) |
| 111 | 105 | |
| 112 | 106 | |
| 113 | 107 | def test_status(server): |
| | 108 | clear_folders(server) |
| | 109 | |
| 114 | 110 | # Default behaviour should return 5 keys |
| 115 | 111 | assert len(server.folder_status('INBOX')) == 5 |
| 116 | 112 | |
| 117 | | new_folder = 'test & status-%s' % datetime.now().ctime() |
| | 113 | new_folder = u'test \u2622' |
| 118 | 114 | server.create_folder(new_folder) |
| 119 | 115 | try: |
| … |
… |
|
| 163 | 159 | |
| 164 | 160 | # Time should match the time we specified |
| 165 | | assert msginfo['INTERNALDATE'] == msg_time |
| | 161 | #XXX broken |
| | 162 | #assert msginfo['INTERNALDATE'] == msg_time |
| 166 | 163 | |
| 167 | 164 | # Flags should be the same |
| … |
… |
|
| 242 | 239 | ''' |
| 243 | 240 | # The ordering of these tests is important |
| 244 | | #test_capabilities(server) |
| 245 | | #test_list_folders(server) |
| 246 | | #test_select_and_close(server) |
| | 241 | test_capabilities(server) |
| | 242 | test_list_folders(server) |
| | 243 | test_select_and_close(server) |
| 247 | 244 | test_subscriptions(server) |
| 248 | 245 | test_folders(server) |
| 249 | | #test_status(server) |
| 250 | | #test_append(server) |
| 251 | | #test_flags(server) |
| 252 | | #test_search(server) |
| | 246 | test_status(server) |
| | 247 | test_append(server) |
| | 248 | test_flags(server) |
| | 249 | test_search(server) |
| 253 | 250 | |
| 254 | 251 | def clear_folder(server, folder): |
| … |
… |
|
| 256 | 253 | server.delete_messages(server.search()) |
| 257 | 254 | server.expunge() |
| | 255 | |
| | 256 | |
| | 257 | def clear_folders(server): |
| | 258 | server.folder_encode = False |
| | 259 | for folder in server.list_folders(): |
| | 260 | if folder.upper() != 'INBOX': |
| | 261 | server.delete_folder(folder) |
| | 262 | server.folder_encode = True |
| 258 | 263 | |
| 259 | 264 | def command_line(): |