Browse Source

Merge pull request #525 from inkhey/fix/mail_fetcher_dont_rely_on_seen/unseen_flag

Bastien Sevajol 6 years ago
parent
commit
8723863dc2
No account linked to committer's email
1 changed files with 30 additions and 15 deletions
  1. 30 15
      tracim/tracim/lib/email_fetcher.py

+ 30 - 15
tracim/tracim/lib/email_fetcher.py View File

26
 CONTENT_TYPE_TEXT_PLAIN = 'text/plain'
26
 CONTENT_TYPE_TEXT_PLAIN = 'text/plain'
27
 CONTENT_TYPE_TEXT_HTML = 'text/html'
27
 CONTENT_TYPE_TEXT_HTML = 'text/html'
28
 
28
 
29
-IMAP_SEEN_FLAG = imapclient.SEEN
30
 IMAP_CHECKED_FLAG = imapclient.FLAGGED
29
 IMAP_CHECKED_FLAG = imapclient.FLAGGED
30
+IMAP_SEEN_FLAG = imapclient.SEEN
31
 
31
 
32
 MAIL_FETCHER_FILELOCK_TIMEOUT = 10
32
 MAIL_FETCHER_FILELOCK_TIMEOUT = 10
33
 MAIL_FETCHER_CONNECTION_TIMEOUT = 60*3
33
 MAIL_FETCHER_CONNECTION_TIMEOUT = 60*3
152
         return None
152
         return None
153
 
153
 
154
 
154
 
155
+class BadIMAPFetchResponse(Exception):
156
+    pass
157
+
158
+
155
 class MailFetcher(object):
159
 class MailFetcher(object):
156
     def __init__(
160
     def __init__(
157
         self,
161
         self,
291
             # TODO - G.M - 10-01-2017 - Support imapclient exceptions
295
             # TODO - G.M - 10-01-2017 - Support imapclient exceptions
292
             # when Imapclient stable will be 2.0+
296
             # when Imapclient stable will be 2.0+
293
 
297
 
298
+            except BadIMAPFetchResponse as e:
299
+                log = 'Imap Fetch command return bad response.' \
300
+                      'Is someone else connected to the mailbox ?: ' \
301
+                      '{}'
302
+                logger.error(self, log.format(e.__str__()))
294
             # Others
303
             # Others
295
             except Exception as e:
304
             except Exception as e:
296
                 log = 'Mail Fetcher error {}'
305
                 log = 'Mail Fetcher error {}'
342
         """
351
         """
343
         messages = []
352
         messages = []
344
 
353
 
345
-        logger.debug(self, 'Fetch unseen messages')
346
-        uids = imapc.search(['UNSEEN'])
347
-        logger.debug(self, 'Found {} unseen mails'.format(
348
-            len(uids),
349
-        ))
350
-        imapc.add_flags(uids, IMAP_SEEN_FLAG)
351
-        logger.debug(self, 'Temporary Flag {} mails as seen'.format(
354
+        logger.debug(self, 'Fetch unflagged messages')
355
+        uids = imapc.search(['UNFLAGGED'])
356
+        logger.debug(self, 'Found {} unflagged mails'.format(
352
             len(uids),
357
             len(uids),
353
         ))
358
         ))
354
         for msgid, data in imapc.fetch(uids, ['BODY.PEEK[]']).items():
359
         for msgid, data in imapc.fetch(uids, ['BODY.PEEK[]']).items():
359
             logger.debug(self, 'Fetch mail "{}"'.format(
364
             logger.debug(self, 'Fetch mail "{}"'.format(
360
                 msgid,
365
                 msgid,
361
             ))
366
             ))
362
-            msg = message_from_bytes(data[b'BODY[]'])
367
+
368
+            try:
369
+                msg = message_from_bytes(data[b'BODY[]'])
370
+            except KeyError as e:
371
+                # INFO - G.M - 12-01-2018 - Fetch may return events response
372
+                # In some specific case, fetch command may return events
373
+                # response unrelated to fetch request.
374
+                # This should happen only when someone-else use the mailbox
375
+                # at the same time of the fetcher.
376
+                # see https://github.com/mjs/imapclient/issues/334
377
+                except_msg = 'fetch response : {}'.format(str(data))
378
+                raise BadIMAPFetchResponse(except_msg) from e
379
+
363
             msg_container = MessageContainer(msg, msgid)
380
             msg_container = MessageContainer(msg, msgid)
364
             messages.append(msg_container)
381
             messages.append(msg_container)
382
+
365
         return messages
383
         return messages
366
 
384
 
367
     def _notify_tracim(
385
     def _notify_tracim(
383
         #  if no from address for example) and catch it here
401
         #  if no from address for example) and catch it here
384
         while mails:
402
         while mails:
385
             mail = mails.pop()
403
             mail = mails.pop()
386
-            body =  mail.get_body(
404
+            body = mail.get_body(
387
                 use_html_parsing=self.use_html_parsing,
405
                 use_html_parsing=self.use_html_parsing,
388
                 use_txt_parsing=self.use_txt_parsing,
406
                 use_txt_parsing=self.use_txt_parsing,
389
             )
407
             )
421
                         str(r.status_code),
439
                         str(r.status_code),
422
                         details,
440
                         details,
423
                     ))
441
                     ))
424
-                # Flag all correctly checked mail, unseen the others
442
+                # Flag all correctly checked mail
425
                 if r.status_code in [200, 204, 400]:
443
                 if r.status_code in [200, 204, 400]:
426
                     imapc.add_flags((mail.uid,), IMAP_CHECKED_FLAG)
444
                     imapc.add_flags((mail.uid,), IMAP_CHECKED_FLAG)
427
-                else:
428
-                    imapc.remove_flags((mail.uid,), IMAP_SEEN_FLAG)
445
+                    imapc.add_flags((mail.uid,), IMAP_SEEN_FLAG)
429
             # TODO - G.M - Verify exception correctly works
446
             # TODO - G.M - Verify exception correctly works
430
             except requests.exceptions.Timeout as e:
447
             except requests.exceptions.Timeout as e:
431
                 log = 'Timeout error to transmit fetched mail to tracim : {}'
448
                 log = 'Timeout error to transmit fetched mail to tracim : {}'
432
                 logger.error(self, log.format(str(e)))
449
                 logger.error(self, log.format(str(e)))
433
-                imapc.remove_flags((mail.uid,), IMAP_SEEN_FLAG)
434
             except requests.exceptions.RequestException as e:
450
             except requests.exceptions.RequestException as e:
435
                 log = 'Fail to transmit fetched mail to tracim : {}'
451
                 log = 'Fail to transmit fetched mail to tracim : {}'
436
                 logger.error(self, log.format(str(e)))
452
                 logger.error(self, log.format(str(e)))
437
-                imapc.remove_flags((mail.uid,), IMAP_SEEN_FLAG)