Skip to content

Commit

Permalink
Handle null flags (Gmail)
Browse files Browse the repository at this point in the history
  • Loading branch information
FlyingDiver committed Oct 10, 2018
1 parent 73b1cea commit 2f39911
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 12 deletions.
2 changes: 1 addition & 1 deletion BetterEmail.indigoPlugin/Contents/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>PluginVersion</key>
<string>7.2.3</string>
<string>7.2.4</string>
<key>ServerApiVersion</key>
<string>2.0</string>
<key>IwsApiVersion</key>
Expand Down
15 changes: 10 additions & 5 deletions BetterEmail.indigoPlugin/Contents/Server Plugin/IMAPServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ def poll(self):
self.checkMsgs()

# close the connection and log out
self.device.updateStateOnServer(key="serverStatus", value="Success")
self.device.updateStateImageOnServer(indigo.kStateImageSel.SensorOn)
self.connection.close()
self.connection.logout()
self.logger.debug(u"\tLogged out from IMAP server: " + self.device.name)
Expand Down Expand Up @@ -132,6 +130,8 @@ def connectIMAP(self):
self.connection.login(self.imapProps['serverLogin'], self.imapProps['serverPassword'])
self.logger.debug(self.device.name + u": Doing select(\"INBOX\")")
self.connection.select("INBOX")
self.device.updateStateOnServer(key="serverStatus", value="Success")
self.device.updateStateImageOnServer(indigo.kStateImageSel.SensorOn)
resp, data = self.connection.list()
if resp == 'OK':
self.logger.threaddebug(u"{}: Mailbox list:".format(self.device.name))
Expand All @@ -143,6 +143,8 @@ def connectIMAP(self):

except Exception, e:
self.logger.error(self.device.name + ': Error connecting to IMAP server: ' + str(e))
self.device.updateStateOnServer(key="serverStatus", value="Failure")
self.device.updateStateImageOnServer(indigo.kStateImageSel.SensorOff)
indigo.activePlugin.connErrorTriggerCheck(self.device)
raise

Expand Down Expand Up @@ -185,7 +187,10 @@ def checkMsgs(self):
if not self.imapProps['checkSeen']: # only check for IndigoProcessed flag if we're not processing all messages
try:
typ, resp = self.connection.fetch(messageNum, '(FLAGS)')
if "$IndigoProcessed" in resp[0]:
self.logger.threaddebug(u"{}: Message # {} Flags = '{}'".format(self.device.name, messageNum, resp))
if not resp:
self.logger.debug(self.device.name + u"%s: Message # %s has no Flags. Processing anyway." % (self.device.name, messageNum))
elif "$IndigoProcessed" in resp[0]:
self.logger.debug(self.device.name + u"%s: Message # %s already seen, skipping..." % (self.device.name, messageNum))
continue
except Exception, e:
Expand Down Expand Up @@ -253,7 +258,7 @@ def checkMsgs(self):

try:
if message.is_multipart():
self.logger.threaddebug(self.device.name + 'checkMsgs: Decoding multipart message')
self.logger.threaddebug(self.device.name + u": checkMsgs: Decoding multipart message")
for part in message.walk():
type = part.get_content_type()
self.logger.threaddebug('\tfound type: %s' % type)
Expand Down Expand Up @@ -287,7 +292,7 @@ def checkMsgs(self):
{'key':'messageText', 'value':messageText},
{'key':'lastMessage', 'value':messageID}
]
self.logger.threaddebug('checkMsgs: Updating states on server: %s' % str(stateList))
# self.logger.threaddebug('checkMsgs: Updating states on server: %s' % str(stateList))
self.device.updateStatesOnServer(stateList)

indigo.activePlugin.triggerCheck(self.device)
Expand Down
60 changes: 54 additions & 6 deletions BetterEmail.indigoPlugin/Contents/Server Plugin/imaplib2.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
"Internaldate2Time", "ParseFlags", "Time2Internaldate",
"Mon2num", "MonthNames", "InternalDate")

__version__ = "2.55"
__version__ = "2.57"
__release__ = "2"
__revision__ = "55"
__revision__ = "57"
__credits__ = """
Authentication code contributed by Donn Cave <[email protected]> June 1998.
String method conversion by ESR, February 2001.
Expand Down Expand Up @@ -109,6 +109,7 @@
'CREATE': ((AUTH, SELECTED), True),
'DELETE': ((AUTH, SELECTED), True),
'DELETEACL': ((AUTH, SELECTED), True),
'ENABLE': ((AUTH,), False),
'EXAMINE': ((AUTH, SELECTED), False),
'EXPUNGE': ((SELECTED,), True),
'FETCH': ((SELECTED,), True),
Expand Down Expand Up @@ -300,17 +301,18 @@ class abort(error): pass # Service errors - close and retry
class readonly(abort): pass # Mailbox status changed to READ-ONLY


# These must be encoded according to utf8 setting in _mode_xxx():
_literal = br'.*{(?P<size>\d+)}$'
_untagged_status = br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?'

continuation_cre = re.compile(r'\+( (?P<data>.*))?')
literal_cre = re.compile(r'.*{(?P<size>\d+)}$')
mapCRLF_cre = re.compile(r'\r\n|\r|\n')
# Need to quote "atom-specials" :-
# "(" / ")" / "{" / SP / 0x00 - 0x1f / 0x7f / "%" / "*" / DQUOTE / "\" / "]"
# so match not the inverse set
mustquote_cre = re.compile(r"[^!#$&'+,./0-9:;<=>?@A-Z\[^_`a-z|}~-]")
response_code_cre = re.compile(r'\[(?P<type>[A-Z-]+)( (?P<data>[^\]]*))?\]')
# sequence_set_cre = re.compile(r"^[0-9]+(:([0-9]+|\*))?(,[0-9]+(:([0-9]+|\*))?)*$")
untagged_response_cre = re.compile(r'\* (?P<type>[A-Z-]+)( (?P<data>.*))?')
untagged_status_cre = re.compile(r'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?')


def __init__(self, host=None, port=None, debug=None, debug_file=None, identifier=None, timeout=None, debug_buf_lvl=None):
Expand Down Expand Up @@ -342,6 +344,8 @@ def __init__(self, host=None, port=None, debug=None, debug_file=None, identifier
+ self.tagpre
+ r'\d+) (?P<type>[A-Z]+) (?P<data>.*)')

self._mode_ascii() # Only option in py2

if __debug__: self._init_debug(debug, debug_file, debug_buf_lvl)

self.resp_timeout = timeout # Timeout waiting for command response
Expand Down Expand Up @@ -428,6 +432,28 @@ def __getattr__(self, attr):
raise AttributeError("Unknown IMAP4 command: '%s'" % attr)


def _mode_ascii(self):
self.utf8_enabled = False
self._encoding = 'ascii'
if bytes != str:
self.literal_cre = re.compile(self._literal, re.ASCII)
self.untagged_status_cre = re.compile(self._untagged_status, re.ASCII)
else:
self.literal_cre = re.compile(self._literal)
self.untagged_status_cre = re.compile(self._untagged_status)


def _mode_utf8(self):
self.utf8_enabled = True
self._encoding = 'utf-8'
if bytes != str:
self.literal_cre = re.compile(self._literal)
self.untagged_status_cre = re.compile(self._untagged_status)
else:
self.literal_cre = re.compile(self._literal, re.UNICODE)
self.untagged_status_cre = re.compile(self._untagged_status, re.UNICODE)



# Overridable methods

Expand Down Expand Up @@ -676,7 +702,10 @@ def append(self, mailbox, flags, date_time, message, **kw):
date_time = Time2Internaldate(date_time)
else:
date_time = None
self.literal = self.mapCRLF_cre.sub(CRLF, message)
literal = self.mapCRLF_cre.sub(CRLF, message)
if self.utf8_enabled:
literal = b'UTF8 (' + literal + b')'
self.literal = literal
try:
return self._simple_command(name, mailbox, flags, date_time, **kw)
finally:
Expand Down Expand Up @@ -774,6 +803,19 @@ def deleteacl(self, mailbox, who, **kw):
return self._simple_command('DELETEACL', mailbox, who, **kw)


def enable(self, capability):
"""Send an RFC5161 enable string to the server.
(typ, [data]) = <intance>.enable(capability)
"""
if 'ENABLE' not in self.capabilities:
raise self.error("Server does not support ENABLE")
typ, data = self._simple_command('ENABLE', capability)
if typ == 'OK' and 'UTF8=ACCEPT' in capability.upper():
self._mode_utf8()
return typ, data


def examine(self, mailbox='INBOX', **kw):
"""(typ, [data]) = examine(mailbox='INBOX')
Select a mailbox for READ-ONLY access. (Flushes all untagged responses.)
Expand Down Expand Up @@ -1025,11 +1067,14 @@ def rename(self, oldmailbox, newmailbox, **kw):
def search(self, charset, *criteria, **kw):
"""(typ, [data]) = search(charset, criterion, ...)
Search mailbox for matching messages.
If UTF8 is enabled, charset MUST be None.
'data' is space separated list of matching message numbers."""

name = 'SEARCH'
kw['untagged_response'] = name
if charset:
if self.utf8_enabled:
raise self.error("Non-None charset not valid in UTF8 mode")
return self._simple_command(name, 'CHARSET', charset, *criteria, **kw)
return self._simple_command(name, *criteria, **kw)

Expand Down Expand Up @@ -1412,6 +1457,9 @@ def _command(self, name, *args, **kw):
if not ok:
break

if data == 'go ahead': # Apparently not uncommon broken IMAP4 server response to AUTHENTICATE command
data = ''

# Send literal

if literator is not None:
Expand Down

0 comments on commit 2f39911

Please sign in to comment.