|  | @@ -22,9 +22,16 @@ CONTENT_TYPE_TEXT_PLAIN = 'text/plain'
 | 
	
		
			
			| 22 | 22 |  CONTENT_TYPE_TEXT_HTML = 'text/html'
 | 
	
		
			
			| 23 | 23 |  
 | 
	
		
			
			| 24 | 24 |  
 | 
	
		
			
			|  | 25 | +class MessageContainer(object):
 | 
	
		
			
			|  | 26 | +    def __init__(self, message: Message, uid: int) -> None:
 | 
	
		
			
			|  | 27 | +        self.message = message
 | 
	
		
			
			|  | 28 | +        self.uid = uid
 | 
	
		
			
			|  | 29 | +
 | 
	
		
			
			|  | 30 | +
 | 
	
		
			
			| 25 | 31 |  class DecodedMail(object):
 | 
	
		
			
			| 26 |  | -    def __init__(self, message: Message) -> None:
 | 
	
		
			
			|  | 32 | +    def __init__(self, message: Message, uid: int=None) -> None:
 | 
	
		
			
			| 27 | 33 |          self._message = message
 | 
	
		
			
			|  | 34 | +        self.uid = uid
 | 
	
		
			
			| 28 | 35 |  
 | 
	
		
			
			| 29 | 36 |      def _decode_header(self, header_title: str) -> typing.Optional[str]:
 | 
	
		
			
			| 30 | 37 |          # FIXME : Handle exception
 | 
	
	
		
			
			|  | @@ -186,10 +193,8 @@ class MailFetcher(object):
 | 
	
		
			
			| 186 | 193 |              try:
 | 
	
		
			
			| 187 | 194 |                  self._connect()
 | 
	
		
			
			| 188 | 195 |                  messages = self._fetch()
 | 
	
		
			
			| 189 |  | -                # TODO - G.M -  2017-11-22 retry sending unsended mail
 | 
	
		
			
			| 190 |  | -                # These mails are return by _notify_tracim, flag them with "unseen" # nopep8
 | 
	
		
			
			| 191 |  | -                # or store them until new _notify_tracim call
 | 
	
		
			
			| 192 |  | -                cleaned_mails = [DecodedMail(msg) for msg in messages]
 | 
	
		
			
			|  | 196 | +                cleaned_mails = [DecodedMail(m.message, m.uid)
 | 
	
		
			
			|  | 197 | +                                 for m in messages]
 | 
	
		
			
			| 193 | 198 |                  self._notify_tracim(cleaned_mails)
 | 
	
		
			
			| 194 | 199 |                  self._disconnect()
 | 
	
		
			
			| 195 | 200 |              except Exception as e:
 | 
	
	
		
			
			|  | @@ -237,7 +242,7 @@ class MailFetcher(object):
 | 
	
		
			
			| 237 | 242 |              self._connection.logout()
 | 
	
		
			
			| 238 | 243 |              self._connection = None
 | 
	
		
			
			| 239 | 244 |  
 | 
	
		
			
			| 240 |  | -    def _fetch(self) -> typing.List[Message]:
 | 
	
		
			
			|  | 245 | +    def _fetch(self) -> typing.List[MessageContainer]:
 | 
	
		
			
			| 241 | 246 |          """
 | 
	
		
			
			| 242 | 247 |          Get news message from mailbox
 | 
	
		
			
			| 243 | 248 |          :return: list of new mails
 | 
	
	
		
			
			|  | @@ -266,21 +271,22 @@ class MailFetcher(object):
 | 
	
		
			
			| 266 | 271 |                  logger.debug(self, 'Found {} unseen mails'.format(
 | 
	
		
			
			| 267 | 272 |                      len(data[0].split()),
 | 
	
		
			
			| 268 | 273 |                  ))
 | 
	
		
			
			| 269 |  | -                for num in data[0].split():
 | 
	
		
			
			| 270 |  | -                    # INFO - G.M - 2017-11-23 - Fetch (RFC288) to retrieve all
 | 
	
		
			
			| 271 |  | -                    # complete mails see example : https://docs.python.org/fr/3.5/library/imaplib.html#imap4-example .  # nopep8
 | 
	
		
			
			| 272 |  | -                    # Be careful, This method remove also mails from Unseen
 | 
	
		
			
			| 273 |  | -                    # mails
 | 
	
		
			
			|  | 274 | +                for uid in data[0].split():
 | 
	
		
			
			|  | 275 | +                    # INFO - G.M - 2017-12-08 - Fetch BODY.PEEK[]
 | 
	
		
			
			|  | 276 | +                    # Retrieve all mail(body and header) but don't set mail
 | 
	
		
			
			|  | 277 | +                    # as seen because of PEEK
 | 
	
		
			
			|  | 278 | +                    # see rfc3501
 | 
	
		
			
			| 274 | 279 |                      logger.debug(self, 'Fetch mail "{}"'.format(
 | 
	
		
			
			| 275 |  | -                        num,
 | 
	
		
			
			|  | 280 | +                        uid,
 | 
	
		
			
			| 276 | 281 |                      ))
 | 
	
		
			
			| 277 |  | -                    rv, data = self._connection.fetch(num, '(RFC822)')
 | 
	
		
			
			|  | 282 | +                    rv, data = self._connection.fetch(uid, 'BODY.PEEK[]')
 | 
	
		
			
			| 278 | 283 |                      logger.debug(self, 'Response status {}'.format(
 | 
	
		
			
			| 279 | 284 |                          rv,
 | 
	
		
			
			| 280 | 285 |                      ))
 | 
	
		
			
			| 281 | 286 |                      if rv == 'OK':
 | 
	
		
			
			| 282 | 287 |                          msg = message_from_bytes(data[0][1])
 | 
	
		
			
			| 283 |  | -                        messages.append(msg)
 | 
	
		
			
			|  | 288 | +                        msg_container = MessageContainer(msg, uid)
 | 
	
		
			
			|  | 289 | +                        messages.append(msg_container)
 | 
	
		
			
			| 284 | 290 |                      else:
 | 
	
		
			
			| 285 | 291 |                          log = 'IMAP : Unable to get mail : {}'
 | 
	
		
			
			| 286 | 292 |                          logger.error(self, log.format(str(rv)))
 | 
	
	
		
			
			|  | @@ -335,6 +341,8 @@ class MailFetcher(object):
 | 
	
		
			
			| 335 | 341 |                          str(r.status_code),
 | 
	
		
			
			| 336 | 342 |                          details,
 | 
	
		
			
			| 337 | 343 |                      ))
 | 
	
		
			
			|  | 344 | +                else:
 | 
	
		
			
			|  | 345 | +                    self._set_flag(mail.uid)
 | 
	
		
			
			| 338 | 346 |              # TODO - G.M - Verify exception correctly works
 | 
	
		
			
			| 339 | 347 |              except requests.exceptions.Timeout as e:
 | 
	
		
			
			| 340 | 348 |                  log = 'Timeout error to transmit fetched mail to tracim : {}'
 | 
	
	
		
			
			|  | @@ -345,3 +353,17 @@ class MailFetcher(object):
 | 
	
		
			
			| 345 | 353 |                  logger.error(self, log.format(str(e)))
 | 
	
		
			
			| 346 | 354 |  
 | 
	
		
			
			| 347 | 355 |          return unsended_mails
 | 
	
		
			
			|  | 356 | +
 | 
	
		
			
			|  | 357 | +    def _set_flag(self, uid):
 | 
	
		
			
			|  | 358 | +        assert uid is not None
 | 
	
		
			
			|  | 359 | +        rv, data = self._connection.store(
 | 
	
		
			
			|  | 360 | +            uid,
 | 
	
		
			
			|  | 361 | +            '+FLAGS',
 | 
	
		
			
			|  | 362 | +            '\\Seen'
 | 
	
		
			
			|  | 363 | +        )
 | 
	
		
			
			|  | 364 | +        if rv == 'OK':
 | 
	
		
			
			|  | 365 | +            log = 'Message {} set as seen.'.format(uid)
 | 
	
		
			
			|  | 366 | +            logger.debug(self, log)
 | 
	
		
			
			|  | 367 | +        else:
 | 
	
		
			
			|  | 368 | +            log = 'Can not set Message {} as seen : {}'.format(uid, rv)
 | 
	
		
			
			|  | 369 | +            logger.error(self, log)
 |