From 59cb2e20c7e1e61794d16d7c4a90174853c54a7c Mon Sep 17 00:00:00 2001 From: Reuben Castelino Date: Mon, 1 Sep 2014 12:34:03 -0700 Subject: [PATCH 1/7] edited gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 96778bd..ea28487 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ tmtags ## PROJECT::GENERAL *.pyc +build/ +dist/ +gmail.egg-info/ ## PROJECT::SPECIFIC *.rdb From 2f69ec538119fc2c74dd5c42715775c2e93557bd Mon Sep 17 00:00:00 2001 From: Reuben Castelino Date: Mon, 1 Sep 2014 13:31:13 -0700 Subject: [PATCH 2/7] first attempt, most changes include automatic change using 2to3, next commit will be my manual changes --- gmail/gmail.py | 14 +++++++------- gmail/mailbox.py | 8 ++++---- gmail/message.py | 14 +++++++------- gmail/utf.py | 6 +++--- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/gmail/gmail.py b/gmail/gmail.py index 7fb09e1..4c2de01 100644 --- a/gmail/gmail.py +++ b/gmail/gmail.py @@ -1,9 +1,9 @@ import re import imaplib -from mailbox import Mailbox -from utf import encode as encode_utf7, decode as decode_utf7 -from exceptions import * +from .mailbox import Mailbox +from .utf import encode as encode_utf7, decode as decode_utf7 +from .exceptions import * class Gmail(): # GMail IMAP defaults @@ -53,7 +53,7 @@ def fetch_mailboxes(self): response, mailbox_list = self.imap.list() if response == 'OK': for mailbox in mailbox_list: - mailbox_name = mailbox.split('"/"')[-1].replace('"', '').strip() + mailbox_name = mailbox.decode().split('"/"')[-1].replace('"', '').strip() mailbox = Mailbox(self) mailbox.external_name = mailbox_name self.mailboxes[mailbox_name] = mailbox @@ -147,9 +147,9 @@ def copy(self, uid, to_mailbox, from_mailbox=None): self.imap.uid('COPY', uid, to_mailbox) def fetch_multiple_messages(self, messages): - fetch_str = ','.join(messages.keys()) + fetch_str = ','.join(list(messages.keys())) response, results = self.imap.uid('FETCH', fetch_str, '(BODY.PEEK[] FLAGS X-GM-THRID X-GM-MSGID X-GM-LABELS)') - for index in xrange(len(results) - 1): + for index in range(len(results) - 1): raw_message = results[index] if re.search(r'UID (\d+)', raw_message[0]): uid = re.search(r'UID (\d+)', raw_message[0]).groups(1)[0] @@ -159,7 +159,7 @@ def fetch_multiple_messages(self, messages): def labels(self, require_unicode=False): - keys = self.mailboxes.keys() + keys = list(self.mailboxes.keys()) if require_unicode: keys = [decode_utf7(key) for key in keys] return keys diff --git a/gmail/mailbox.py b/gmail/mailbox.py index 0368034..aac7f7c 100644 --- a/gmail/mailbox.py +++ b/gmail/mailbox.py @@ -1,5 +1,5 @@ -from message import Message -from utf import encode as encode_utf7, decode as decode_utf7 +from .message import Message +from .utf import encode as encode_utf7, decode as decode_utf7 class Mailbox(): @@ -60,7 +60,7 @@ def mail(self, prefetch=False, **kwargs): # print search response, data = self.gmail.imap.uid('SEARCH', *search) if response == 'OK': - uids = filter(None, data[0].split(' ')) # filter out empty strings + uids = [_f for _f in data[0].decode().split(' ') if _f] # filter out empty strings for uid in uids: if not self.messages.get(uid): @@ -91,7 +91,7 @@ def threads(self, prefetch=False, **kwargs): if prefetch: fetch_str = ','.join(uids) response, results = self.gmail.imap.uid('FETCH', fetch_str, '(BODY.PEEK[] FLAGS X-GM-THRID X-GM-MSGID X-GM-LABELS)') - for index in xrange(len(results) - 1): + for index in range(len(results) - 1): raw_message = results[index] if re.search(r'UID (\d+)', raw_message[0]): uid = re.search(r'UID (\d+)', raw_message[0]).groups(1)[0] diff --git a/gmail/message.py b/gmail/message.py index 9813ce4..821b5f3 100644 --- a/gmail/message.py +++ b/gmail/message.py @@ -113,7 +113,7 @@ def archive(self): def parse_headers(self, message): hdrs = {} - for hdr in message.keys(): + for hdr in list(message.keys()): hdrs[hdr] = message[hdr] return hdrs @@ -122,22 +122,22 @@ def parse_flags(self, headers): # flags = re.search(r'FLAGS \(([^\)]*)\)', headers).groups(1)[0].split(' ') def parse_labels(self, headers): + headers = headers.decode() if re.search(r'X-GM-LABELS \(([^\)]+)\)', headers): labels = re.search(r'X-GM-LABELS \(([^\)]+)\)', headers).groups(1)[0].split(' ') - return map(lambda l: l.replace('"', '').decode("string_escape"), labels) + return [l.replace('"', '').decode("string_escape") for l in labels] else: return list() def parse_subject(self, encoded_subject): dh = decode_header(encoded_subject) - default_charset = 'ASCII' - return ''.join([ unicode(t[0], t[1] or default_charset) for t in dh ]) + return ''.join(t[0] for t in dh) def parse(self, raw_message): raw_headers = raw_message[0] raw_email = raw_message[1] - self.message = email.message_from_string(raw_email) + self.message = email.message_from_string(raw_email.decode()) self.headers = self.parse_headers(self.message) self.to = self.message['to'] @@ -170,7 +170,7 @@ def parse(self, raw_message): # Parse attachments into attachment objects array for this message self.attachments = [ Attachment(attachment) for attachment in self.message._payload - if not isinstance(attachment, basestring) and attachment.get('Content-Disposition') is not None + if not isinstance(attachment, str) and attachment.get('Content-Disposition') is not None ] @@ -210,7 +210,7 @@ def fetch_thread(self): self.gmail.use_mailbox(original_mailbox.name) # combine and sort sent and received messages - return sorted(dict(received_messages.items() + sent_messages.items()).values(), key=lambda m: m.sent_at) + return sorted(list(dict(list(received_messages.items()) + list(sent_messages.items())).values()), key=lambda m: m.sent_at) class Attachment: diff --git a/gmail/utf.py b/gmail/utf.py index c953e73..8efc230 100644 --- a/gmail/utf.py +++ b/gmail/utf.py @@ -22,8 +22,8 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -text_type = unicode -binary_type = str +text_type = str +binary_type = bytes PRINTABLE = set(range(0x20, 0x26)) | set(range(0x27, 0x7f)) @@ -96,4 +96,4 @@ def modified_utf7(s): def modified_deutf7(s): s_utf7 = '+' + s.replace(',', '/') + '-' # encode to latin-1: '+AP8-' => b'+AP8-', decode from utf-7 => '\xff' - return s_utf7.encode('latin-1').decode('utf-7') \ No newline at end of file + return s_utf7.encode('latin-1').decode('utf-7') From 4ea0816293e86a256bdd82b59eec5b4cc4c94943 Mon Sep 17 00:00:00 2001 From: Reuben Castelino Date: Mon, 1 Sep 2014 13:58:42 -0700 Subject: [PATCH 3/7] last manual fix seems to have gotten most of the obvious bugs --- gmail/message.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gmail/message.py b/gmail/message.py index 821b5f3..c93fb9b 100644 --- a/gmail/message.py +++ b/gmail/message.py @@ -122,10 +122,9 @@ def parse_flags(self, headers): # flags = re.search(r'FLAGS \(([^\)]*)\)', headers).groups(1)[0].split(' ') def parse_labels(self, headers): - headers = headers.decode() if re.search(r'X-GM-LABELS \(([^\)]+)\)', headers): labels = re.search(r'X-GM-LABELS \(([^\)]+)\)', headers).groups(1)[0].split(' ') - return [l.replace('"', '').decode("string_escape") for l in labels] + return [l.replace('"', '').encode().decode("unicode_escape") for l in labels] else: return list() @@ -134,7 +133,7 @@ def parse_subject(self, encoded_subject): return ''.join(t[0] for t in dh) def parse(self, raw_message): - raw_headers = raw_message[0] + raw_headers = raw_message[0].decode() raw_email = raw_message[1] self.message = email.message_from_string(raw_email.decode()) @@ -157,7 +156,7 @@ def parse(self, raw_message): self.sent_at = datetime.datetime.fromtimestamp(time.mktime(email.utils.parsedate_tz(self.message['date'])[:9])) - self.flags = self.parse_flags(raw_headers) + self.flags = self.parse_flags(raw_headers.encode()) self.labels = self.parse_labels(raw_headers) From 887f4a9111cd358c7684fefbe613ddb417d24c6e Mon Sep 17 00:00:00 2001 From: Reuben Castelino Date: Fri, 15 Dec 2017 00:17:42 -0800 Subject: [PATCH 4/7] updated readme to show python3 change --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index aae4856..16d3c67 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Heavily inspired by [Kriss "nu7hatch" Kowalik's GMail for Ruby library](https:// * [Charlie Guo](https://github.com/charlierguo) +Edited by me to work with python3 + ## Installation For now, installation is manual (`pip` support not yet implemented) and the only requirement is to use Python 2 (2.7+ to be precise): From da8673950d39b13ebc8523531439a61da1e6c265 Mon Sep 17 00:00:00 2001 From: Alex Riina Date: Wed, 28 Nov 2018 15:54:58 -0500 Subject: [PATCH 5/7] decode parts of subject according to specified encoding The `encoded subject` is a string like `=?ISO-8859-1?Q?a?= =?Latin-1?Q?b?=` which wraps pairs each chunk with its encoding. decode_headers returns the chunks with their encodings so instead of ignoring the encodings or treating them all as utf-8 (https://github.com/projectdelphai/gmail/pull/1), decode them with the specified encoding. --- gmail/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gmail/message.py b/gmail/message.py index c93fb9b..7096364 100644 --- a/gmail/message.py +++ b/gmail/message.py @@ -130,7 +130,7 @@ def parse_labels(self, headers): def parse_subject(self, encoded_subject): dh = decode_header(encoded_subject) - return ''.join(t[0] for t in dh) + return ''.join(value.decode(encoding) for value, encoding in dh) def parse(self, raw_message): raw_headers = raw_message[0].decode() From 36075c226f535973b6af65940aa321bfbb561cf9 Mon Sep 17 00:00:00 2001 From: Alex Riina Date: Wed, 28 Nov 2018 16:24:32 -0500 Subject: [PATCH 6/7] handle subject strings without encodings that are returned as strings --- gmail/message.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gmail/message.py b/gmail/message.py index 7096364..a0dacb9 100644 --- a/gmail/message.py +++ b/gmail/message.py @@ -130,7 +130,11 @@ def parse_labels(self, headers): def parse_subject(self, encoded_subject): dh = decode_header(encoded_subject) - return ''.join(value.decode(encoding) for value, encoding in dh) + + # dh is a list that has (str, None) and (bytes, encoding) objects + return ''.join( + value.decode(encoding) if encoding else value + for value, encoding in dh) def parse(self, raw_message): raw_headers = raw_message[0].decode() From faa675c9d215fc76d8312409908ffd0c7370ae01 Mon Sep 17 00:00:00 2001 From: Reuben Castelino Date: Thu, 29 Nov 2018 17:42:34 -0800 Subject: [PATCH 7/7] updated README to specify difference between fork --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 16d3c67..b27936a 100644 --- a/README.md +++ b/README.md @@ -8,17 +8,16 @@ __This library is still under development, so please forgive some of the rough e Heavily inspired by [Kriss "nu7hatch" Kowalik's GMail for Ruby library](https://github.com/nu7hatch/gmail) -## Author +## Contributors -* [Charlie Guo](https://github.com/charlierguo) - -Edited by me to work with python3 +* [Charlie Guo](https://github.com/charlierguo) - Author of the original python2 package +* [Reuben Castelino](https://github.com/projectdelphai) - Edited by me to work with python3 ## Installation -For now, installation is manual (`pip` support not yet implemented) and the only requirement is to use Python 2 (2.7+ to be precise): +For now, installation is manual (`pip` support not yet implemented) and the only requirement is to use Python 3 - git clone git://github.com/charlierguo/gmail.git + git clone git://github.com/projectdelphai/gmail.git ## Features