Esempio n. 1
0
 def test_06_flagsmaildir2imap(self):
     """Test imaputil.flagsmaildir2imap()"""
     res = imaputil.flagsmaildir2imap(set(b'DR'))
     self.assertEqual(res, b'(\\Answered \\Draft)')
     # test all possible flags
     res = imaputil.flagsmaildir2imap(set(b'SRFTD'))
     self.assertEqual(res, b'(\\Answered \\Deleted \\Draft \\Flagged \\Seen)')
 def test_06_flagsmaildir2imap(self):
     """Test imaputil.flagsmaildir2imap()"""
     res = imaputil.flagsmaildir2imap(set(b'DR'))
     self.assertEqual(res, b'(\\Answered \\Draft)')
     # test all possible flags
     res = imaputil.flagsmaildir2imap(set(b'SRFTD'))
     self.assertEqual(res, b'(\\Answered \\Deleted \\Draft \\Flagged \\Seen)')
Esempio n. 3
0
    def savemessageflags(self, uid, flags):
        """Change a message's flags to `flags`.

        Note that this function does not check against dryrun settings,
        so you need to ensure that it is never called in a
        dryrun mode."""
        imapobj = self.imapserver.acquireconnection()
        try:
            try:
                imapobj.select(self.getfullname())
            except imapobj.readonly:
                self.ui.flagstoreadonly(self, [uid], flags)
                return
            result = imapobj.uid('store', '%d' % uid, 'FLAGS',
                                 imaputil.flagsmaildir2imap(flags))
            assert result[0] == 'OK', 'Error with store: ' + '. '.join(
                result[1])
        finally:
            self.imapserver.releaseconnection(imapobj)
        result = result[1][0]
        if not result:
            self.messagelist[uid]['flags'] = flags
        else:
            flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
            self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
Esempio n. 4
0
    def processmessagesflags(self, operation, uidlist, flags):
        # XXX: the imapobj.myrights(...) calls dies with an error
        # report from Gmail server stating that IMAP command
        # 'MYRIGHTS' is not implemented.  So, this
        # `processmessagesflags` is just a copy from `IMAPFolder`,
        # with the references to `imapobj.myrights()` deleted This
        # shouldn't hurt, however, Gmail users always have full
        # control over all their mailboxes (apparently).
        if len(uidlist) > 101:
            # Hack for those IMAP ervers with a limited line length
            self.processmessagesflags(operation, uidlist[:100], flags)
            self.processmessagesflags(operation, uidlist[100:], flags)
            return
        
        imapobj = self.imapserver.acquireconnection()
        try:
            imapobj.select(self.getfullname())
            r = imapobj.uid('store',
                            imaputil.listjoin(uidlist),
                            operation + 'FLAGS',
                            imaputil.flagsmaildir2imap(flags))
            assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
            r = r[1]
        finally:
            self.imapserver.releaseconnection(imapobj)

        needupdate = copy(uidlist)
        for result in r:
            if result == None:
                # Compensate for servers that don't return anything from
                # STORE.
                continue
            attributehash = imaputil.flags2hash(imaputil.imapsplit(result)[1])
            if not ('UID' in attributehash and 'FLAGS' in attributehash):
                # Compensate for servers that don't return a UID attribute.
                continue
            flags = attributehash['FLAGS']
            uid = long(attributehash['UID'])
            self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
            try:
                needupdate.remove(uid)
            except ValueError:          # Let it slide if it's not in the list
                pass
        for uid in needupdate:
            if operation == '+':
                for flag in flags:
                    if not flag in self.messagelist[uid]['flags']:
                        self.messagelist[uid]['flags'].append(flag)
                    self.messagelist[uid]['flags'].sort()
            elif operation == '-':
                for flag in flags:
                    if flag in self.messagelist[uid]['flags']:
                        self.messagelist[uid]['flags'].remove(flag)
Esempio n. 5
0
    def processmessagesflags(self, operation, uidlist, flags):
        # XXX: the imapobj.myrights(...) calls dies with an error
        # report from Gmail server stating that IMAP command
        # 'MYRIGHTS' is not implemented.  So, this
        # `processmessagesflags` is just a copy from `IMAPFolder`,
        # with the references to `imapobj.myrights()` deleted This
        # shouldn't hurt, however, Gmail users always have full
        # control over all their mailboxes (apparently).
        if len(uidlist) > 101:
            # Hack for those IMAP ervers with a limited line length
            self.processmessagesflags(operation, uidlist[:100], flags)
            self.processmessagesflags(operation, uidlist[100:], flags)
            return
        
        imapobj = self.imapserver.acquireconnection()
        try:
            imapobj.select(self.getfullname())
            r = imapobj.uid('store',
                            imaputil.listjoin(uidlist),
                            operation + 'FLAGS',
                            imaputil.flagsmaildir2imap(flags))
            assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
            r = r[1]
        finally:
            self.imapserver.releaseconnection(imapobj)

        needupdate = copy(uidlist)
        for result in r:
            if result == None:
                # Compensate for servers that don't return anything from
                # STORE.
                continue
            attributehash = imaputil.flags2hash(imaputil.imapsplit(result)[1])
            if not ('UID' in attributehash and 'FLAGS' in attributehash):
                # Compensate for servers that don't return a UID attribute.
                continue
            flags = attributehash['FLAGS']
            uid = long(attributehash['UID'])
            self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
            try:
                needupdate.remove(uid)
            except ValueError:          # Let it slide if it's not in the list
                pass
        for uid in needupdate:
            if operation == '+':
                for flag in flags:
                    if not flag in self.messagelist[uid]['flags']:
                        self.messagelist[uid]['flags'].append(flag)
                    self.messagelist[uid]['flags'].sort()
            elif operation == '-':
                for flag in flags:
                    if flag in self.messagelist[uid]['flags']:
                        self.messagelist[uid]['flags'].remove(flag)
Esempio n. 6
0
 def processmessagesflags(self, operation, uidlist, flags):
     if len(uidlist) > 101:
         # Hack for those IMAP ervers with a limited line length
         self.processmessagesflags(operation, uidlist[:100], flags)
         self.processmessagesflags(operation, uidlist[100:], flags)
         return
     
     imapobj = self.imapserver.acquireconnection()
     try:
         try:
             imapobj.select(self.getfullname())
         except imapobj.readonly:
             UIBase.getglobalui().flagstoreadonly(self, uidlist, flags)
             return
         r = imapobj.uid('store',
                         imaputil.listjoin(uidlist),
                         operation + 'FLAGS',
                         imaputil.flagsmaildir2imap(flags))
         assert r[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
         r = r[1]
     finally:
         self.imapserver.releaseconnection(imapobj)
     # Some IMAP servers do not always return a result.  Therefore,
     # only update the ones that it talks about, and manually fix
     # the others.
     needupdate = copy(uidlist)
     for result in r:
         if result == None:
             # Compensate for servers that don't return anything from
             # STORE.
             continue
         attributehash = imaputil.flags2hash(imaputil.imapsplit(result)[1])
         if not ('UID' in attributehash and 'FLAGS' in attributehash):
             # Compensate for servers that don't return a UID attribute.
             continue
         lflags = attributehash['FLAGS']
         uid = long(attributehash['UID'])
         self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(lflags)
         try:
             needupdate.remove(uid)
         except ValueError:          # Let it slide if it's not in the list
             pass
     for uid in needupdate:
         if operation == '+':
             for flag in flags:
                 if not flag in self.messagelist[uid]['flags']:
                     self.messagelist[uid]['flags'].append(flag)
                 self.messagelist[uid]['flags'].sort()
         elif operation == '-':
             for flag in flags:
                 if flag in self.messagelist[uid]['flags']:
                     self.messagelist[uid]['flags'].remove(flag)
Esempio n. 7
0
    def _scanfolder(self):
        """Cache the message list from a Maildir.

        Maildir flags are: R (replied) S (seen) T (trashed) D (draft) F
        (flagged).
        :returns: dict that can be used as self.messagelist"""
        maxage = self.config.getdefaultint("Account " + self.accountname,
                                           "maxage", None)
        maxsize = self.config.getdefaultint("Account " + self.accountname,
                                            "maxsize", None)
        retval = {}
        files = []
        nouidcounter = -1  # Messages without UIDs get negative UIDs.
        for dirannex in ['new', 'cur']:
            fulldirname = os.path.join(self.getfullname(), dirannex)
            files.extend(
                (dirannex, filename) for filename in os.listdir(fulldirname))

        for dirannex, filename in files:
            # We store just dirannex and filename, ie 'cur/123...'
            filepath = os.path.join(dirannex, filename)
            # check maxage/maxsize if this message should be considered
            if maxage and not self._iswithinmaxage(filename, maxage):
                continue
            if maxsize and (os.path.getsize(
                    os.path.join(self.getfullname(), filepath)) > maxsize):
                continue

            (prefix, uid, fmd5, maildirflags) = self._parse_filename(filename)
            if uid is None:  # assign negative uid to upload it.
                uid = nouidcounter
                nouidcounter -= 1
            else:  # It comes from our folder.
                uidmatch = re_uidmatch.search(filename)
                uid = None
                if not uidmatch:
                    uid = nouidcounter
                    nouidcounter -= 1
                else:
                    uid = long(uidmatch.group(1))
            # 'filename' is 'dirannex/filename', e.g. cur/123,U=1,FMD5=1:2,S
            retval[uid] = {
                'flags': imaputil.flagsmaildir2imap(maildirflags),
                'filename': filepath
            }
        return retval
Esempio n. 8
0
    def processmessagesflags(self, operation, uidlist, flags):
        if len(uidlist) > 101:
            # Hack for those IMAP ervers with a limited line length
            self.processmessagesflags(operation, uidlist[:100], flags)
            self.processmessagesflags(operation, uidlist[100:], flags)
            return

        imapobj = self.imapserver.acquireconnection()
        try:
            try:
                imapobj.select(self.getfullname())
            except imapobj.readonly:
                self.ui.flagstoreadonly(self, uidlist, flags)
                return
            r = imapobj.uid(
                "store", imaputil.uid_sequence(uidlist), operation + "FLAGS", imaputil.flagsmaildir2imap(flags)
            )
            assert r[0] == "OK", "Error with store: " + ". ".join(r[1])
            r = r[1]
        finally:
            self.imapserver.releaseconnection(imapobj)
        # Some IMAP servers do not always return a result.  Therefore,
        # only update the ones that it talks about, and manually fix
        # the others.
        needupdate = list(uidlist)
        for result in r:
            if result == None:
                # Compensate for servers that don't return anything from
                # STORE.
                continue
            attributehash = imaputil.flags2hash(imaputil.imapsplit(result)[1])
            if not ("UID" in attributehash and "FLAGS" in attributehash):
                # Compensate for servers that don't return a UID attribute.
                continue
            flagstr = attributehash["FLAGS"]
            uid = long(attributehash["UID"])
            self.messagelist[uid]["flags"] = imaputil.flagsimap2maildir(flagstr)
            try:
                needupdate.remove(uid)
            except ValueError:  # Let it slide if it's not in the list
                pass
        for uid in needupdate:
            if operation == "+":
                self.messagelist[uid]["flags"] |= flags
            elif operation == "-":
                self.messagelist[uid]["flags"] -= flags
Esempio n. 9
0
 def savemessageflags(self, uid, flags):
     imapobj = self.imapserver.acquireconnection()
     try:
         try:
             imapobj.select(self.getfullname())
         except imapobj.readonly:
             self.ui.flagstoreadonly(self, [uid], flags)
             return
         result = imapobj.uid("store", "%d" % uid, "FLAGS", imaputil.flagsmaildir2imap(flags))
         assert result[0] == "OK", "Error with store: " + ". ".join(result[1])
     finally:
         self.imapserver.releaseconnection(imapobj)
     result = result[1][0]
     if not result:
         self.messagelist[uid]["flags"] = flags
     else:
         flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])["FLAGS"]
         self.messagelist[uid]["flags"] = imaputil.flagsimap2maildir(flags)
Esempio n. 10
0
    def _scanfolder(self):
        """Cache the message list from a Maildir.

        Maildir flags are: R (replied) S (seen) T (trashed) D (draft) F
        (flagged).
        :returns: dict that can be used as self.messagelist"""
        maxage = self.config.getdefaultint("Account " + self.accountname,
                                           "maxage", None)
        maxsize = self.config.getdefaultint("Account " + self.accountname,
                                            "maxsize", None)
        retval = {}
        files = []
        nouidcounter = -1          # Messages without UIDs get negative UIDs.
        for dirannex in ['new', 'cur']:
            fulldirname = os.path.join(self.getfullname(), dirannex)
            files.extend((dirannex, filename) for
                         filename in os.listdir(fulldirname))

        for dirannex, filename in files:
            # We store just dirannex and filename, ie 'cur/123...'
            filepath = os.path.join(dirannex, filename)
            # check maxage/maxsize if this message should be considered
            if maxage and not self._iswithinmaxage(filename, maxage):
                continue
            if maxsize and (os.path.getsize(os.path.join(
                        self.getfullname(), filepath)) > maxsize):
                continue

            (prefix, uid, fmd5, maildirflags) = self._parse_filename(filename)
            if uid is None: # assign negative uid to upload it.
                uid = nouidcounter
                nouidcounter -= 1
            else:                       # It comes from our folder.
                uidmatch = re_uidmatch.search(filename)
                uid = None
                if not uidmatch:
                    uid = nouidcounter
                    nouidcounter -= 1
                else:
                    uid = long(uidmatch.group(1))
            # 'filename' is 'dirannex/filename', e.g. cur/123,U=1,FMD5=1:2,S
            retval[uid] = {'flags': imaputil.flagsmaildir2imap(maildirflags),
                           'filename': filepath}
        return retval
Esempio n. 11
0
 def __processmessagesflags_real(self, operation, uidlist, flags):
     imapobj = self.imapserver.acquireconnection()
     try:
         try:
             imapobj.select(self.getfullname())
         except imapobj.readonly:
             self.ui.flagstoreadonly(self, uidlist, flags)
             return
         response = imapobj.uid('store', imaputil.uid_sequence(uidlist),
                                operation + 'FLAGS',
                                imaputil.flagsmaildir2imap(flags))
         if response[0] != 'OK':
             raise OfflineImapError(
                 'Error with store: %s' % '. '.join(response[1]),
                 OfflineImapError.ERROR.MESSAGE)
         response = response[1]
     finally:
         self.imapserver.releaseconnection(imapobj)
     # Some IMAP servers do not always return a result.  Therefore,
     # only update the ones that it talks about, and manually fix
     # the others.
     needupdate = list(uidlist)
     for result in response:
         if result is None:
             # Compensate for servers that don't return anything from
             # STORE.
             continue
         attributehash = imaputil.flags2hash(imaputil.imapsplit(result)[1])
         if not ('UID' in attributehash and 'FLAGS' in attributehash):
             # Compensate for servers that don't return a UID attribute.
             continue
         flagstr = attributehash['FLAGS']
         uid = int(attributehash['UID'])
         self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(
             flagstr)
         try:
             needupdate.remove(uid)
         except ValueError:  # Let it slide if it's not in the list.
             pass
     for uid in needupdate:
         if operation == '+':
             self.messagelist[uid]['flags'] |= flags
         elif operation == '-':
             self.messagelist[uid]['flags'] -= flags
Esempio n. 12
0
 def __processmessagesflags_real(self, operation, uidlist, flags):
     imapobj = self.imapserver.acquireconnection()
     try:
         try:
             imapobj.select(self.getfullname())
         except imapobj.readonly:
             self.ui.flagstoreadonly(self, uidlist, flags)
             return
         response = imapobj.uid('store',
             imaputil.uid_sequence(uidlist), operation + 'FLAGS',
                 imaputil.flagsmaildir2imap(flags))
         if response[0] != 'OK':
             raise OfflineImapError(
                 'Error with store: %s'% '. '.join(response[1]),
                 OfflineImapError.ERROR.MESSAGE)
         response = response[1]
     finally:
         self.imapserver.releaseconnection(imapobj)
     # Some IMAP servers do not always return a result.  Therefore,
     # only update the ones that it talks about, and manually fix
     # the others.
     needupdate = list(uidlist)
     for result in response:
         if result is None:
             # Compensate for servers that don't return anything from
             # STORE.
             continue
         attributehash = imaputil.flags2hash(imaputil.imapsplit(result)[1])
         if not ('UID' in attributehash and 'FLAGS' in attributehash):
             # Compensate for servers that don't return a UID attribute.
             continue
         flagstr = attributehash['FLAGS']
         uid = int(attributehash['UID'])
         self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flagstr)
         try:
             needupdate.remove(uid)
         except ValueError:  # Let it slide if it's not in the list.
             pass
     for uid in needupdate:
         if operation == '+':
             self.messagelist[uid]['flags'] |= flags
         elif operation == '-':
             self.messagelist[uid]['flags'] -= flags
Esempio n. 13
0
 def savemessageflags(self, uid, flags):
     imapobj = self.imapserver.acquireconnection()
     try:
         try:
             imapobj.select(self.getfullname())
         except imapobj.readonly:
             self.ui.flagstoreadonly(self, [uid], flags)
             return
         result = imapobj.uid('store', '%d' % uid, 'FLAGS',
                              imaputil.flagsmaildir2imap(flags))
         assert result[0] == 'OK', 'Error with store: ' + '. '.join(result[1])
     finally:
         self.imapserver.releaseconnection(imapobj)
     result = result[1][0]
     if not result:
         self.messagelist[uid]['flags'] = flags
     else:
         flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
         self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
Esempio n. 14
0
 def savemessageflags(self, uid, flags):
     imapobj = self.imapserver.acquireconnection()
     try:
         try:
             imapobj.select(self.getfullname())
         except imapobj.readonly:
             self.ui.flagstoreadonly(self, [uid], flags)
             return
         result = imapobj.uid('store', '%d' % uid, 'FLAGS',
                              imaputil.flagsmaildir2imap(flags))
         assert result[0] == 'OK', 'Error with store: ' + '. '.join(r[1])
     finally:
         self.imapserver.releaseconnection(imapobj)
     result = result[1][0]
     if not result:
         self.messagelist[uid]['flags'] = flags
     else:
         flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
         self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
Esempio n. 15
0
    def savemessageflags(self, uid, flags):
        """Change a message's flags to `flags`.

        Note that this function does not check against dryrun settings,
        so you need to ensure that it is never called in a
        dryrun mode."""
        imapobj = self.imapserver.acquireconnection()
        try:
            result = self._store_to_imap(imapobj, str(uid), 'FLAGS',
                                         imaputil.flagsmaildir2imap(flags))
        except imapobj.readonly:
            self.ui.flagstoreadonly(self, [uid], flags)
            return
        finally:
            self.imapserver.releaseconnection(imapobj)

        if not result:
            self.messagelist[uid]['flags'] = flags
        else:
            flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
            self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
Esempio n. 16
0
    def savemessageflags(self, uid, flags):
        """Change a message's flags to `flags`.

        Note that this function does not check against dryrun settings,
        so you need to ensure that it is never called in a
        dryrun mode."""
        imapobj = self.imapserver.acquireconnection()
        try:
            result = self._store_to_imap(imapobj, str(uid), 'FLAGS',
                imaputil.flagsmaildir2imap(flags))
        except imapobj.readonly:
            self.ui.flagstoreadonly(self, [uid], flags)
            return
        finally:
            self.imapserver.releaseconnection(imapobj)

        if not result:
            self.messagelist[uid]['flags'] = flags
        else:
            flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
            self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
Esempio n. 17
0
    def savemessageflags(self, uid, flags):
        """Change a message's flags to `flags`.

        Note that this function does not check against dryrun settings,
        so you need to ensure that it is never called in a
        dryrun mode."""
        imapobj = self.imapserver.acquireconnection()
        try:
            try:
                imapobj.select(self.getfullname())
            except imapobj.readonly:
                self.ui.flagstoreadonly(self, [uid], flags)
                return
            result = imapobj.uid('store', '%d' % uid, 'FLAGS',
                                 imaputil.flagsmaildir2imap(flags))
            assert result[0] == 'OK', 'Error with store: ' + '. '.join(result[1])
        finally:
            self.imapserver.releaseconnection(imapobj)
        result = result[1][0]
        if not result:
            self.messagelist[uid]['flags'] = flags
        else:
            flags = imaputil.flags2hash(imaputil.imapsplit(result)[1])['FLAGS']
            self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flags)
Esempio n. 18
0
    def savemessage(self, uid, content, flags, rtime):
        """Save the message on the Server

        This backend always assigns a new uid, so the uid arg is ignored.

        This function will update the self.messagelist dict to contain
        the new message after sucessfully saving it.

        See folder/Base for details. Note that savemessage() does not
        check against dryrun settings, so you need to ensure that
        savemessage is never called in a dryrun mode.

        :param rtime: A timestamp to be used as the mail date
        :returns: the UID of the new message as assigned by the server. If the
                  message is saved, but it's UID can not be found, it will
                  return 0. If the message can't be written (folder is
                  read-only for example) it will return -1."""

        self.ui.savemessage('imap', uid, flags, self)

        # Already have it, just save modified flags.
        if uid > 0 and self.uidexists(uid):
            self.savemessageflags(uid, flags)
            return uid

        content = self.deletemessageheaders(content, self.filterheaders)

        # Use proper CRLF all over the message.
        content = re.sub("(?<!\r)\n", CRLF, content)

        # Get the date of the message, so we can pass it to the server.
        date = self.__getmessageinternaldate(content, rtime)

        # Message-ID is handy for debugging messages.
        msg_id = self.getmessageheader(content, "message-id")
        if not msg_id:
            msg_id = '[unknown message-id]'

        retry_left = 2  # succeeded in APPENDING?
        imapobj = self.imapserver.acquireconnection()
        # NB: in the finally clause for this try we will release
        # NB: the acquired imapobj, so don't do that twice unless
        # NB: you will put another connection to imapobj.  If you
        # NB: really do need to release connection manually, set
        # NB: imapobj to None.
        try:
            while retry_left:
                # XXX: we can mangle message only once, out of the loop
                # UIDPLUS extension provides us with an APPENDUID response.
                use_uidplus = 'UIDPLUS' in imapobj.capabilities

                if not use_uidplus:
                    # Insert a random unique header that we can fetch later.
                    (headername,
                     headervalue) = self.__generate_randomheader(content)
                    self.ui.debug(
                        'imap', 'savemessage: header is: %s: %s' %
                        (headername, headervalue))
                    content = self.addmessageheader(content, CRLF, headername,
                                                    headervalue)

                if len(content) > 200:
                    dbg_output = "%s...%s" % (content[:150], content[-50:])
                else:
                    dbg_output = content
                self.ui.debug(
                    'imap', "savemessage: date: %s, content: '%s'" %
                    (date, dbg_output))

                try:
                    # Select folder for append and make the box READ-WRITE.
                    imapobj.select(self.getfullname())
                except imapobj.readonly:
                    # readonly exception. Return original uid to notify that
                    # we did not save the message. (see savemessage in Base.py)
                    self.ui.msgtoreadonly(self, uid, content, flags)
                    return uid

                # Do the APPEND.
                try:
                    (typ,
                     dat) = imapobj.append(self.getfullname(),
                                           imaputil.flagsmaildir2imap(flags),
                                           date, content)
                    # This should only catch 'NO' responses since append()
                    # will raise an exception for 'BAD' responses:
                    if typ != 'OK':
                        # For example, Groupwise IMAP server can return something like:
                        #
                        #   NO APPEND The 1500 MB storage limit has been exceeded.
                        #
                        # In this case, we should immediately abort the repository sync
                        # and continue with the next account.
                        msg = \
                            "Saving msg (%s) in folder '%s', repository '%s' failed (abort). " \
                            "Server responded: %s %s\n"% \
                            (msg_id, self, self.getrepository(), typ, dat)
                        raise OfflineImapError(msg,
                                               OfflineImapError.ERROR.REPO)
                    retry_left = 0  # Mark as success.
                except imapobj.abort as e:
                    # Connection has been reset, release connection and retry.
                    retry_left -= 1
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = self.imapserver.acquireconnection()
                    if not retry_left:
                        six.reraise(
                            OfflineImapError,
                            OfflineImapError(
                                "Saving msg (%s) in folder '%s', "
                                "repository '%s' failed (abort). Server responded: %s\n"
                                "Message content was: %s" %
                                (msg_id, self, self.getrepository(), str(e),
                                 dbg_output), OfflineImapError.ERROR.MESSAGE),
                            exc_info()[2])
                    # XXX: is this still needed?
                    self.ui.error(e, exc_info()[2])
                except imapobj.error as e:  # APPEND failed
                    # If the server responds with 'BAD', append()
                    # raise()s directly.  So we catch that too.
                    # drop conn, it might be bad.
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = None
                    six.reraise(
                        OfflineImapError,
                        OfflineImapError(
                            "Saving msg (%s) folder '%s', repo '%s'"
                            "failed (error). Server responded: %s\nMessage content was: "
                            "%s" % (msg_id, self, self.getrepository(), str(e),
                                    dbg_output),
                            OfflineImapError.ERROR.MESSAGE),
                        exc_info()[2])
            # Checkpoint. Let it write out stuff, etc. Eg searches for
            # just uploaded messages won't work if we don't do this.
            (typ, dat) = imapobj.check()
            assert (typ == 'OK')

            # Get the new UID, do we use UIDPLUS?
            if use_uidplus:
                # Get new UID from the APPENDUID response, it could look
                # like OK [APPENDUID 38505 3955] APPEND completed with
                # 38505 bein folder UIDvalidity and 3955 the new UID.
                # note: we would want to use .response() here but that
                # often seems to return [None], even though we have
                # data. TODO
                resp = imapobj._get_untagged_response('APPENDUID')
                if resp == [None] or resp is None:
                    self.ui.warn(
                        "Server supports UIDPLUS but got no APPENDUID "
                        "appending a message.")
                    return 0
                uid = int(resp[-1].split(' ')[1])
                if uid == 0:
                    self.ui.warn(
                        "savemessage: Server supports UIDPLUS, but"
                        " we got no usable uid back. APPENDUID reponse was "
                        "'%s'" % str(resp))
            else:
                # We don't support UIDPLUS.
                uid = self.__savemessage_searchforheader(
                    imapobj, headername, headervalue)
                # See docs for savemessage in Base.py for explanation
                # of this and other return values.
                if uid == 0:
                    self.ui.debug(
                        'imap', 'savemessage: attempt to get new UID '
                        'UID failed. Search headers manually.')
                    uid = self.__savemessage_fetchheaders(
                        imapobj, headername, headervalue)
                    self.ui.warn(
                        'imap', "savemessage: Searching mails for new "
                        "Message-ID failed. Could not determine new UID.")
        finally:
            if imapobj:
                self.imapserver.releaseconnection(imapobj)

        if uid:  # Avoid UID FETCH 0 crash happening later on
            self.messagelist[uid] = self.msglist_item_initializer(uid)
            self.messagelist[uid]['flags'] = flags

        self.ui.debug('imap', 'savemessage: returning new UID %d' % uid)
        return uid
Esempio n. 19
0
    def savemessage(self, uid, content, flags, rtime):
        """Save the message on the Server

        This backend always assigns a new uid, so the uid arg is ignored.

        This function will update the self.messagelist dict to contain
        the new message after sucessfully saving it.

        :param rtime: A timestamp to be used as the mail date
        :returns: the UID of the new message as assigned by the
                  server. If the folder is read-only it will return 0."""
        self.ui.debug('imap', 'savemessage: called')

        # already have it, just save modified flags
        if uid > 0 and uid in self.messagelist:
            self.savemessageflags(uid, flags)
            return uid

        try:
            imapobj = self.imapserver.acquireconnection()

            try:
                imapobj.select(self.getfullname(
                ))  # Needed for search and making the box READ-WRITE
            except imapobj.readonly:
                # readonly exception. Return original uid to notify that
                # we did not save the message. (see savemessage in Base.py)
                self.ui.msgtoreadonly(self, uid, content, flags)
                return uid

            # UIDPLUS extension provides us with an APPENDUID response to our append()
            use_uidplus = 'UIDPLUS' in imapobj.capabilities

            # get the date of the message file, so we can pass it to the server.
            date = self.getmessageinternaldate(content, rtime)
            self.ui.debug('imap', 'savemessage: using date %s' % date)

            content = re.sub("(?<!\r)\n", "\r\n", content)

            if not use_uidplus:
                # insert a random unique header that we can fetch later
                (headername, headervalue) = self.generate_randomheader(content)
                self.ui.debug('imap', 'savemessage: new headers are: %s: %s' % \
                             (headername, headervalue))
                content = self.savemessage_addheader(content, headername,
                                                     headervalue)
            self.ui.debug('imap', 'savemessage: content is: ' + repr(content))

            # TODO: - append could raise a ValueError if the date is not in
            #         valid format...?
            (typ, dat) = imapobj.append(self.getfullname(),
                                        imaputil.flagsmaildir2imap(flags),
                                        date, content)
            assert (typ == 'OK')

            # Checkpoint.  Let it write out the messages, etc.
            (typ, dat) = imapobj.check()
            assert (typ == 'OK')

            # get the new UID. Test for APPENDUID response even if the
            # server claims to not support it, as e.g. Gmail does :-(
            if use_uidplus or imapobj._get_untagged_response(
                    'APPENDUID', True):
                # get the new UID from the APPENDUID response, it could look like
                # OK [APPENDUID 38505 3955] APPEND completed
                # with 38505 bein folder UIDvalidity and 3955 the new UID
                if not imapobj._get_untagged_response('APPENDUID', True):
                    self.ui.warn(
                        "Server supports UIDPLUS but got no APPENDUID "
                        "appending a message.")
                    return 0
                uid = long(
                    imapobj._get_untagged_response('APPENDUID',
                                                   True)[-1].split(' ')[1])

            else:
                # we don't support UIDPLUS
                uid = self.savemessage_searchforheader(imapobj, headername,
                                                       headervalue)
                # See docs for savemessage in Base.py for explanation of this and other return values
                if uid == 0:
                    self.ui.debug(
                        'imap',
                        'savemessage: first attempt to get new UID failed.  Going to run a NOOP and try again.'
                    )
                    assert (imapobj.noop()[0] == 'OK')
                    uid = self.savemessage_searchforheader(
                        imapobj, headername, headervalue)

        finally:
            self.imapserver.releaseconnection(imapobj)

        if uid:  # avoid UID FETCH 0 crash happening later on
            self.messagelist[uid] = {'uid': uid, 'flags': flags}

        self.ui.debug('imap', 'savemessage: returning new UID %d' % uid)
        return uid
Esempio n. 20
0
    def savemessage(self, uid, content, flags, rtime):
        """Save the message on the Server

        This backend always assigns a new uid, so the uid arg is ignored.

        This function will update the self.messagelist dict to contain
        the new message after sucessfully saving it.

        See folder/Base for details. Note that savemessage() does not
        check against dryrun settings, so you need to ensure that
        savemessage is never called in a dryrun mode.

        :param rtime: A timestamp to be used as the mail date
        :returns: the UID of the new message as assigned by the server. If the
                  message is saved, but it's UID can not be found, it will
                  return 0. If the message can't be written (folder is
                  read-only for example) it will return -1."""
        self.ui.savemessage('imap', uid, flags, self)

        # already have it, just save modified flags
        if uid > 0 and self.uidexists(uid):
            self.savemessageflags(uid, flags)
            return uid

        # Remove headers in filterheaders
        if len(self.filterheaders) > 0:
            insertionpoint = content.find("\n\n")
            leader = content[0:insertionpoint]
            trailer = content[insertionpoint:]

            for header in self.filterheaders:
                leader = re.sub('^%s:.*$\n?' % header, '', leader, flags = re.MULTILINE)

            content = leader + trailer

        retry_left = 2 # succeeded in APPENDING?
        imapobj = self.imapserver.acquireconnection()
        try:
            while retry_left:
                # UIDPLUS extension provides us with an APPENDUID response.
                use_uidplus = 'UIDPLUS' in imapobj.capabilities

                # get the date of the message, so we can pass it to the server.
                date = self.getmessageinternaldate(content, rtime)

                if not use_uidplus:
                    # insert a random unique header that we can fetch later
                    (headername, headervalue) = self.generate_randomheader(
                                                    content)
                    self.ui.debug('imap', 'savemessage: header is: %s: %s' %\
                                      (headername, headervalue))
                    content = self.savemessage_addheader(content, headername,
                                                         headervalue)

                content = re.sub("(?<!\r)\n", "\r\n", content)

                if len(content)>200:
                    dbg_output = "%s...%s" % (content[:150], content[-50:])
                else:
                    dbg_output = content
                self.ui.debug('imap', "savemessage: date: %s, content: '%s'" %
                             (date, dbg_output))

                try:
                    # Select folder for append and make the box READ-WRITE
                    imapobj.select(self.getfullname())
                except imapobj.readonly:
                    # readonly exception. Return original uid to notify that
                    # we did not save the message. (see savemessage in Base.py)
                    self.ui.msgtoreadonly(self, uid, content, flags)
                    return uid

                #Do the APPEND
                try:
                    (typ, dat) = imapobj.append(self.getfullname(),
                                       imaputil.flagsmaildir2imap(flags),
                                       date, content)
                    retry_left = 0                # Mark as success
                except imapobj.abort as e:
                    # connection has been reset, release connection and retry.
                    retry_left -= 1
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = self.imapserver.acquireconnection()
                    if not retry_left:
                        raise OfflineImapError("Saving msg in folder '%s', "
                              "repository '%s' failed (abort). Server reponded: %s\n"
                              "Message content was: %s" %
                              (self, self.getrepository(), str(e), dbg_output),
                                               OfflineImapError.ERROR.MESSAGE)
                    self.ui.error(e, exc_info()[2])
                except imapobj.error as e: # APPEND failed
                    # If the server responds with 'BAD', append()
                    # raise()s directly.  So we catch that too.
                    # drop conn, it might be bad.
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = None
                    raise OfflineImapError("Saving msg folder '%s', repo '%s'"
                        "failed (error). Server reponded: %s\nMessage content was: "
                        "%s" % (self, self.getrepository(), str(e), dbg_output),
                                           OfflineImapError.ERROR.MESSAGE)
            # Checkpoint. Let it write out stuff, etc. Eg searches for
            # just uploaded messages won't work if we don't do this.
            (typ,dat) = imapobj.check()
            assert(typ == 'OK')

            # get the new UID, do we use UIDPLUS?
            if use_uidplus:
                # get new UID from the APPENDUID response, it could look
                # like OK [APPENDUID 38505 3955] APPEND completed with
                # 38505 bein folder UIDvalidity and 3955 the new UID.
                # note: we would want to use .response() here but that
                # often seems to return [None], even though we have
                # data. TODO
                resp = imapobj._get_untagged_response('APPENDUID')
                if resp == [None] or resp is None:
                    self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
                                 "appending a message.")
                    return 0
                uid = long(resp[-1].split(' ')[1])
                if uid == 0:
                    self.ui.warn("savemessage: Server supports UIDPLUS, but"
                            " we got no usable uid back. APPENDUID reponse was "
                            "'%s'" % str(resp))
            else:
                # we don't support UIDPLUS
                uid = self.savemessage_searchforheader(imapobj, headername,
                                                       headervalue)
                # See docs for savemessage in Base.py for explanation
                # of this and other return values
                if uid == 0:
                    self.ui.debug('imap', 'savemessage: attempt to get new UID '
                        'UID failed. Search headers manually.')
                    uid = self.savemessage_fetchheaders(imapobj, headername,
                                                        headervalue)
                    self.ui.warn('imap', "savemessage: Searching mails for new "
                        "Message-ID failed. Could not determine new UID.")
        finally:
            self.imapserver.releaseconnection(imapobj)

        if uid: # avoid UID FETCH 0 crash happening later on
            self.messagelist[uid] = {'uid': uid, 'flags': flags}

        self.ui.debug('imap', 'savemessage: returning new UID %d' % uid)
        return uid
Esempio n. 21
0
    def savemessage(self, uid, content, flags, rtime):
        """Save the message on the Server

        This backend always assigns a new uid, so the uid arg is ignored.

        This function will update the self.messagelist dict to contain
        the new message after sucessfully saving it.

        :param rtime: A timestamp to be used as the mail date
        :returns: the UID of the new message as assigned by the server. If the
                  message is saved, but it's UID can not be found, it will
                  return 0. If the message can't be written (folder is
                  read-only for example) it will return -1."""
        self.ui.debug("imap", "savemessage: called")

        # already have it, just save modified flags
        if uid > 0 and self.uidexists(uid):
            self.savemessageflags(uid, flags)
            return uid

        imapobj = self.imapserver.acquireconnection()
        try:
            success = False  # succeeded in APPENDING?
            while not success:

                # UIDPLUS extension provides us with an APPENDUID response.
                use_uidplus = "UIDPLUS" in imapobj.capabilities

                # get the date of the message, so we can pass it to the server.
                date = self.getmessageinternaldate(content, rtime)
                content = re.sub("(?<!\r)\n", "\r\n", content)

                if not use_uidplus:
                    # insert a random unique header that we can fetch later
                    (headername, headervalue) = self.generate_randomheader(content)
                    self.ui.debug("imap", "savemessage: header is: %s: %s" % (headername, headervalue))
                    content = self.savemessage_addheader(content, headername, headervalue)
                if len(content) > 200:
                    dbg_output = "%s...%s" % (content[:150], content[-50:])
                else:
                    dbg_output = content
                self.ui.debug("imap", "savemessage: date: %s, content: '%s'" % (date, dbg_output))

                try:
                    # Select folder for append and make the box READ-WRITE
                    imapobj.select(self.getfullname())
                except imapobj.readonly:
                    # readonly exception. Return original uid to notify that
                    # we did not save the message. (see savemessage in Base.py)
                    self.ui.msgtoreadonly(self, uid, content, flags)
                    return uid

                # Do the APPEND
                try:
                    (typ, dat) = imapobj.append(self.getfullname(), imaputil.flagsmaildir2imap(flags), date, content)
                    success = True
                except imapobj.abort, e:
                    # connection has been reset, release connection and retry.
                    self.ui.error(e, exc_info()[2])
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = self.imapserver.acquireconnection()
                except imapobj.error, e:
                    # If the server responds with 'BAD', append() raise()s directly.
                    # So we need to prepare a response ourselves.
                    typ, dat = "BAD", str(e)
                    if typ != "OK":  # APPEND failed
                        raise OfflineImapError(
                            "Saving msg in folder '%s', repository "
                            "'%s' failed. Server reponded; %s %s\nMessage content was:"
                            " %s" % (self, self.getrepository(), typ, dat, dbg_output),
                            OfflineImapError.ERROR.MESSAGE,
                        )
Esempio n. 22
0
    def savemessage(self, uid, content, flags, rtime):
        """Save the message on the Server

        This backend always assigns a new uid, so the uid arg is ignored.

        This function will update the self.messagelist dict to contain
        the new message after sucessfully saving it.

        :param rtime: A timestamp to be used as the mail date
        :returns: the UID of the new message as assigned by the server. If the
                  message is saved, but it's UID can not be found, it will
                  return 0. If the message can't be written (folder is
                  read-only for example) it will return -1."""
        self.ui.debug('imap', 'savemessage: called')

        # already have it, just save modified flags
        if uid > 0 and self.uidexists(uid):
            self.savemessageflags(uid, flags)
            return uid

        retry_left = 2  # succeeded in APPENDING?
        imapobj = self.imapserver.acquireconnection()
        try:
            while retry_left:
                # UIDPLUS extension provides us with an APPENDUID response.
                use_uidplus = 'UIDPLUS' in imapobj.capabilities

                # get the date of the message, so we can pass it to the server.
                date = self.getmessageinternaldate(content, rtime)
                content = re.sub("(?<!\r)\n", "\r\n", content)

                if not use_uidplus:
                    # insert a random unique header that we can fetch later
                    (headername,
                     headervalue) = self.generate_randomheader(content)
                    self.ui.debug('imap', 'savemessage: header is: %s: %s' %\
                                      (headername, headervalue))
                    content = self.savemessage_addheader(
                        content, headername, headervalue)
                if len(content) > 200:
                    dbg_output = "%s...%s" % (content[:150], content[-50:])
                else:
                    dbg_output = content
                self.ui.debug(
                    'imap', "savemessage: date: %s, content: '%s'" %
                    (date, dbg_output))

                try:
                    # Select folder for append and make the box READ-WRITE
                    imapobj.select(self.getfullname())
                except imapobj.readonly:
                    # readonly exception. Return original uid to notify that
                    # we did not save the message. (see savemessage in Base.py)
                    self.ui.msgtoreadonly(self, uid, content, flags)
                    return uid

                #Do the APPEND
                try:
                    (typ,
                     dat) = imapobj.append(self.getfullname(),
                                           imaputil.flagsmaildir2imap(flags),
                                           date, content)
                    retry_left = 0  # Mark as success
                except imapobj.abort, e:
                    # connection has been reset, release connection and retry.
                    retry_left -= 1
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = self.imapserver.acquireconnection()
                    if not retry_left:
                        raise OfflineImapError(
                            "Saving msg in folder '%s', "
                            "repository '%s' failed (abort). Server reponded: %s\n"
                            "Message content was: %s" %
                            (self, self.getrepository(), str(e), dbg_output),
                            OfflineImapError.ERROR.MESSAGE)
                    self.ui.error(e, exc_info()[2])
                except imapobj.error, e:  # APPEND failed
                    # If the server responds with 'BAD', append()
                    # raise()s directly.  So we catch that too.
                    # drop conn, it might be bad.
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = None
                    raise OfflineImapError(
                        "Saving msg folder '%s', repo '%s'"
                        "failed (error). Server reponded: %s\nMessage content was: "
                        "%s" %
                        (self, self.getrepository(), str(e), dbg_output),
                        OfflineImapError.ERROR.MESSAGE)
Esempio n. 23
0
    def savemessage(self, uid, content, flags, rtime):
        """Save the message on the Server

        This backend always assigns a new uid, so the uid arg is ignored.

        This function will update the self.messagelist dict to contain
        the new message after sucessfully saving it.

        :param rtime: A timestamp to be used as the mail date
        :returns: the UID of the new message as assigned by the
                  server. If the folder is read-only it will return 0."""
        self.ui.debug('imap', 'savemessage: called')

        # already have it, just save modified flags
        if uid > 0 and self.uidexists(uid):
            self.savemessageflags(uid, flags)
            return uid

        try:
            imapobj = self.imapserver.acquireconnection()

            try:
                imapobj.select(self.getfullname()) # Needed for search and making the box READ-WRITE
            except imapobj.readonly:
                # readonly exception. Return original uid to notify that
                # we did not save the message. (see savemessage in Base.py)
                self.ui.msgtoreadonly(self, uid, content, flags)
                return uid

            # UIDPLUS extension provides us with an APPENDUID response to our append()
            use_uidplus = 'UIDPLUS' in imapobj.capabilities

            # get the date of the message file, so we can pass it to the server.
            date = self.getmessageinternaldate(content, rtime)
            self.ui.debug('imap', 'savemessage: using date %s' % date)
    
            content = re.sub("(?<!\r)\n", "\r\n", content)
    
            if not use_uidplus:
                # insert a random unique header that we can fetch later
                (headername, headervalue) = self.generate_randomheader(content)
                self.ui.debug('imap', 'savemessage: new headers are: %s: %s' % \
                             (headername, headervalue))
                content = self.savemessage_addheader(content, headername,
                                                     headervalue)    
            self.ui.debug('imap', 'savemessage: content is: ' + repr(content))

            # TODO: - append could raise a ValueError if the date is not in
            #         valid format...?
            (typ,dat) = imapobj.append(self.getfullname(),
                                       imaputil.flagsmaildir2imap(flags),
                                       date, content)
            assert(typ == 'OK')

            # Checkpoint.  Let it write out the messages, etc.
            (typ,dat) = imapobj.check()
            assert(typ == 'OK')

            # get the new UID. Test for APPENDUID response even if the
            # server claims to not support it, as e.g. Gmail does :-(
            if use_uidplus or imapobj._get_untagged_response('APPENDUID', True):
                # get the new UID from the APPENDUID response, it could look like
                # OK [APPENDUID 38505 3955] APPEND completed
                # with 38505 bein folder UIDvalidity and 3955 the new UID
                if not imapobj._get_untagged_response('APPENDUID', True):
                    self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
                                 "appending a message.")
                    return 0
                uid = long(imapobj._get_untagged_response('APPENDUID', True)[-1].split(' ')[1])

            else:
                # we don't support UIDPLUS
                uid = self.savemessage_searchforheader(imapobj, headername,
                                                       headervalue)
                # See docs for savemessage in Base.py for explanation of this and other return values
                if uid == 0:
                    self.ui.debug('imap', 'savemessage: first attempt to get new UID failed.  Going to run a NOOP and try again.')
                    assert(imapobj.noop()[0] == 'OK')
                    uid = self.savemessage_searchforheader(imapobj, headername,
                                                       headervalue)

        finally:
            self.imapserver.releaseconnection(imapobj)

        if uid: # avoid UID FETCH 0 crash happening later on
            self.messagelist[uid] = {'uid': uid, 'flags': flags}

        self.ui.debug('imap', 'savemessage: returning new UID %d' % uid)
        return uid
Esempio n. 24
0
    def savemessage(self, uid, content, flags, rtime):
        """Save the message on the Server

        This backend always assigns a new uid, so the uid arg is ignored.

        This function will update the self.messagelist dict to contain
        the new message after sucessfully saving it.

        See folder/Base for details. Note that savemessage() does not
        check against dryrun settings, so you need to ensure that
        savemessage is never called in a dryrun mode.

        :param rtime: A timestamp to be used as the mail date
        :returns: the UID of the new message as assigned by the server. If the
                  message is saved, but it's UID can not be found, it will
                  return 0. If the message can't be written (folder is
                  read-only for example) it will return -1."""

        self.ui.savemessage('imap', uid, flags, self)

        # Already have it, just save modified flags.
        if uid > 0 and self.uidexists(uid):
            self.savemessageflags(uid, flags)
            return uid

        content = self.deletemessageheaders(content, self.filterheaders)

        # Use proper CRLF all over the message.
        content = re.sub("(?<!\r)\n", CRLF, content)

        # Get the date of the message, so we can pass it to the server.
        date = self.__getmessageinternaldate(content, rtime)

        # Message-ID is handy for debugging messages.
        msg_id = self.getmessageheader(content, "message-id")
        if not msg_id:
            msg_id = '[unknown message-id]'

        retry_left = 2 # succeeded in APPENDING?
        imapobj = self.imapserver.acquireconnection()
        # NB: in the finally clause for this try we will release
        # NB: the acquired imapobj, so don't do that twice unless
        # NB: you will put another connection to imapobj.  If you
        # NB: really do need to release connection manually, set
        # NB: imapobj to None.
        try:
            while retry_left:
                # XXX: we can mangle message only once, out of the loop
                # UIDPLUS extension provides us with an APPENDUID response.
                use_uidplus = 'UIDPLUS' in imapobj.capabilities

                if not use_uidplus:
                    # Insert a random unique header that we can fetch later.
                    (headername, headervalue) = self.__generate_randomheader(
                        content)
                    self.ui.debug('imap', 'savemessage: header is: %s: %s'%
                        (headername, headervalue))
                    content = self.addmessageheader(content, CRLF, headername, headervalue)

                if len(content)>200:
                    dbg_output = "%s...%s"% (content[:150], content[-50:])
                else:
                    dbg_output = content
                self.ui.debug('imap', "savemessage: date: %s, content: '%s'"%
                    (date, dbg_output))

                try:
                    # Select folder for append and make the box READ-WRITE.
                    imapobj.select(self.getfullname())
                except imapobj.readonly:
                    # readonly exception. Return original uid to notify that
                    # we did not save the message. (see savemessage in Base.py)
                    self.ui.msgtoreadonly(self, uid, content, flags)
                    return uid

                # Do the APPEND.
                try:
                    (typ, dat) = imapobj.append(self.getfullname(),
                        imaputil.flagsmaildir2imap(flags), date, content)
                    # This should only catch 'NO' responses since append()
                    # will raise an exception for 'BAD' responses:
                    if typ != 'OK':
                        # For example, Groupwise IMAP server can return something like:
                        #
                        #   NO APPEND The 1500 MB storage limit has been exceeded.
                        #
                        # In this case, we should immediately abort the repository sync
                        # and continue with the next account.
                        msg = \
                            "Saving msg (%s) in folder '%s', repository '%s' failed (abort). " \
                            "Server responded: %s %s\n"% \
                            (msg_id, self, self.getrepository(), typ, dat)
                        raise OfflineImapError(msg, OfflineImapError.ERROR.REPO)
                    retry_left = 0 # Mark as success.
                except imapobj.abort as e:
                    # Connection has been reset, release connection and retry.
                    retry_left -= 1
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = self.imapserver.acquireconnection()
                    if not retry_left:
                        six.reraise(OfflineImapError,
                                    OfflineImapError("Saving msg (%s) in folder '%s', "
                                        "repository '%s' failed (abort). Server responded: %s\n"
                                        "Message content was: %s"%
                                        (msg_id, self, self.getrepository(), str(e), dbg_output),
                                        OfflineImapError.ERROR.MESSAGE),
                                    exc_info()[2])
                    # XXX: is this still needed?
                    self.ui.error(e, exc_info()[2])
                except imapobj.error as e: # APPEND failed
                    # If the server responds with 'BAD', append()
                    # raise()s directly.  So we catch that too.
                    # drop conn, it might be bad.
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = None
                    six.reraise(OfflineImapError,
                                OfflineImapError("Saving msg (%s) folder '%s', repo '%s'"
                                    "failed (error). Server responded: %s\nMessage content was: "
                                    "%s"% (msg_id, self, self.getrepository(), str(e), dbg_output),
                                    OfflineImapError.ERROR.MESSAGE),
                                exc_info()[2])
            # Checkpoint. Let it write out stuff, etc. Eg searches for
            # just uploaded messages won't work if we don't do this.
            (typ,dat) = imapobj.check()
            assert(typ == 'OK')

            # Get the new UID, do we use UIDPLUS?
            if use_uidplus:
                # Get new UID from the APPENDUID response, it could look
                # like OK [APPENDUID 38505 3955] APPEND completed with
                # 38505 bein folder UIDvalidity and 3955 the new UID.
                # note: we would want to use .response() here but that
                # often seems to return [None], even though we have
                # data. TODO
                resp = imapobj._get_untagged_response('APPENDUID')
                if resp == [None] or resp is None:
                    self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
                        "appending a message.")
                    return 0
                uid = int(resp[-1].split(' ')[1])
                if uid == 0:
                    self.ui.warn("savemessage: Server supports UIDPLUS, but"
                        " we got no usable uid back. APPENDUID reponse was "
                        "'%s'"% str(resp))
            else:
                # We don't support UIDPLUS.
                uid = self.__savemessage_searchforheader(imapobj, headername,
                    headervalue)
                # See docs for savemessage in Base.py for explanation
                # of this and other return values.
                if uid == 0:
                    self.ui.debug('imap', 'savemessage: attempt to get new UID '
                        'UID failed. Search headers manually.')
                    uid = self.__savemessage_fetchheaders(imapobj, headername,
                        headervalue)
                    self.ui.warn('imap', "savemessage: Searching mails for new "
                        "Message-ID failed. Could not determine new UID.")
        finally:
            if imapobj: self.imapserver.releaseconnection(imapobj)

        if uid: # Avoid UID FETCH 0 crash happening later on
            self.messagelist[uid] = self.msglist_item_initializer(uid)
            self.messagelist[uid]['flags'] = flags

        self.ui.debug('imap', 'savemessage: returning new UID %d'% uid)
        return uid
Esempio n. 25
0
    def savemessage(self, uid, content, flags, rtime):
        imapobj = self.imapserver.acquireconnection()
        ui = UIBase.getglobalui()
        ui.debug('imap', 'savemessage: called')
        try:
            try:
                imapobj.select(self.getfullname()) # Needed for search
            except imapobj.readonly:
                ui.msgtoreadonly(self, uid, content, flags)
                # Return indicating message taken, but no UID assigned.
                # Fudge it.
                return 0
            
            # This backend always assigns a new uid, so the uid arg is ignored.
            # In order to get the new uid, we need to save off the message ID.

            message = rfc822.Message(StringIO(content))
            datetuple_msg = rfc822.parsedate(message.getheader('Date'))
            # Will be None if missing or not in a valid format.

            # If time isn't known
            if rtime == None and datetuple_msg == None:
                datetuple = time.localtime()
            elif rtime == None:
                datetuple = datetuple_msg
            else:
                datetuple = time.localtime(rtime)

            try:
                if datetuple[0] < 1981:
                    raise ValueError

                # Check for invalid date
                datetuple_check = time.localtime(time.mktime(datetuple))
                if datetuple[:2] != datetuple_check[:2]:
                    raise ValueError

                # This could raise a value error if it's not a valid format.
                date = imaplib.Time2Internaldate(datetuple) 
            except (ValueError, OverflowError):
                # Argh, sometimes it's a valid format but year is 0102
                # or something.  Argh.  It seems that Time2Internaldate
                # will rause a ValueError if the year is 0102 but not 1902,
                # but some IMAP servers nonetheless choke on 1902.
                date = imaplib.Time2Internaldate(time.localtime())

            ui.debug('imap', 'savemessage: using date ' + str(date))
            content = re.sub("(?<!\r)\n", "\r\n", content)
            ui.debug('imap', 'savemessage: initial content is: ' + repr(content))

            (headername, headervalue) = self.savemessage_getnewheader(content)
            ui.debug('imap', 'savemessage: new headers are: %s: %s' % \
                     (headername, headervalue))
            content = self.savemessage_addheader(content, headername,
                                                 headervalue)
            ui.debug('imap', 'savemessage: new content is: ' + repr(content))
            ui.debug('imap', 'savemessage: new content length is ' + \
                     str(len(content)))

            assert(imapobj.append(self.getfullname(),
                                       imaputil.flagsmaildir2imap(flags),
                                       date, content)[0] == 'OK')

            # Checkpoint.  Let it write out the messages, etc.
            assert(imapobj.check()[0] == 'OK')

            # Keep trying until we get the UID.
            ui.debug('imap', 'savemessage: first attempt to get new UID')
            uid = self.savemessage_searchforheader(imapobj, headername,
                                                   headervalue)
            # See docs for savemessage in Base.py for explanation of this and other return values
            if uid <= 0:
                ui.debug('imap', 'savemessage: first attempt to get new UID failed.  Going to run a NOOP and try again.')
                assert(imapobj.noop()[0] == 'OK')
                uid = self.savemessage_searchforheader(imapobj, headername,
                                                       headervalue)
        finally:
            self.imapserver.releaseconnection(imapobj)

        if uid: # avoid UID FETCH 0 crash happening later on
            self.messagelist[uid] = {'uid': uid, 'flags': flags}

        ui.debug('imap', 'savemessage: returning %d' % uid)
        return uid
Esempio n. 26
0
    def savemessage(self, uid, content, flags, rtime):
        """Save the message on the Server

        This backend always assigns a new uid, so the uid arg is ignored.

        This function will update the self.messagelist dict to contain
        the new message after sucessfully saving it.

        See folder/Base for details. Note that savemessage() does not
        check against dryrun settings, so you need to ensure that
        savemessage is never called in a dryrun mode.

        :param rtime: A timestamp to be used as the mail date
        :returns: the UID of the new message as assigned by the server. If the
                  message is saved, but it's UID can not be found, it will
                  return 0. If the message can't be written (folder is
                  read-only for example) it will return -1."""
        self.ui.savemessage('imap', uid, flags, self)

        # already have it, just save modified flags
        if uid > 0 and self.uidexists(uid):
            self.savemessageflags(uid, flags)
            return uid

        retry_left = 2 # succeeded in APPENDING?
        imapobj = self.imapserver.acquireconnection()
        try:
            while retry_left:
                # UIDPLUS extension provides us with an APPENDUID response.
                use_uidplus = 'UIDPLUS' in imapobj.capabilities

                # get the date of the message, so we can pass it to the server.
                date = self.getmessageinternaldate(content, rtime)
                content = re.sub("(?<!\r)\n", "\r\n", content)

                if not use_uidplus:
                    # insert a random unique header that we can fetch later
                    (headername, headervalue) = self.generate_randomheader(
                                                    content)
                    self.ui.debug('imap', 'savemessage: header is: %s: %s' %\
                                      (headername, headervalue))
                    content = self.savemessage_addheader(content, headername,
                                                         headervalue)    
                if len(content)>200:
                    dbg_output = "%s...%s" % (content[:150], content[-50:])
                else:
                    dbg_output = content
                self.ui.debug('imap', "savemessage: date: %s, content: '%s'" %
                             (date, dbg_output))

                try:
                    # Select folder for append and make the box READ-WRITE
                    imapobj.select(self.getfullname())
                except imapobj.readonly:
                    # readonly exception. Return original uid to notify that
                    # we did not save the message. (see savemessage in Base.py)
                    self.ui.msgtoreadonly(self, uid, content, flags)
                    return uid

                #Do the APPEND
                try:
                    (typ, dat) = imapobj.append(self.getfullname(),
                                       imaputil.flagsmaildir2imap(flags),
                                       date, content)
                    retry_left = 0                # Mark as success
                except imapobj.abort as e:
                    # connection has been reset, release connection and retry.
                    retry_left -= 1
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = self.imapserver.acquireconnection()
                    if not retry_left:
                        raise OfflineImapError("Saving msg in folder '%s', "
                              "repository '%s' failed (abort). Server reponded: %s\n"
                              "Message content was: %s" %
                              (self, self.getrepository(), str(e), dbg_output),
                                               OfflineImapError.ERROR.MESSAGE)
                    self.ui.error(e, exc_info()[2])
                except imapobj.error as e: # APPEND failed
                    # If the server responds with 'BAD', append()
                    # raise()s directly.  So we catch that too.
                    # drop conn, it might be bad.
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = None
                    raise OfflineImapError("Saving msg folder '%s', repo '%s'"
                        "failed (error). Server reponded: %s\nMessage content was: "
                        "%s" % (self, self.getrepository(), str(e), dbg_output),
                                           OfflineImapError.ERROR.MESSAGE)
            # Checkpoint. Let it write out stuff, etc. Eg searches for
            # just uploaded messages won't work if we don't do this.
            (typ,dat) = imapobj.check()
            assert(typ == 'OK')

            # get the new UID, do we use UIDPLUS?
            if use_uidplus:
                # get new UID from the APPENDUID response, it could look
                # like OK [APPENDUID 38505 3955] APPEND completed with
                # 38505 bein folder UIDvalidity and 3955 the new UID.
                # note: we would want to use .response() here but that
                # often seems to return [None], even though we have
                # data. TODO
                resp = imapobj._get_untagged_response('APPENDUID')
                if resp == [None] or resp is None:
                    self.ui.warn("Server supports UIDPLUS but got no APPENDUID "
                                 "appending a message.")
                    return 0
                uid = long(resp[-1].split(' ')[1])
                if uid == 0:
                    self.ui.warn("savemessage: Server supports UIDPLUS, but"
                            " we got no usable uid back. APPENDUID reponse was "
                            "'%s'" % str(resp))
            else:
                # we don't support UIDPLUS
                uid = self.savemessage_searchforheader(imapobj, headername,
                                                       headervalue)
                # See docs for savemessage in Base.py for explanation
                # of this and other return values
                if uid == 0:
                    self.ui.debug('imap', 'savemessage: attempt to get new UID '
                        'UID failed. Search headers manually.')
                    uid = self.savemessage_fetchheaders(imapobj, headername,
                                                        headervalue)
                    self.ui.warn('imap', "savemessage: Searching mails for new "
                        "Message-ID failed. Could not determine new UID.")
        finally:
            self.imapserver.releaseconnection(imapobj)

        if uid: # avoid UID FETCH 0 crash happening later on
            self.messagelist[uid] = {'uid': uid, 'flags': flags}

        self.ui.debug('imap', 'savemessage: returning new UID %d' % uid)
        return uid
Esempio n. 27
0
    def savemessage(self, uid, content, flags, rtime):
        """Save the message on the Server

        This backend always assigns a new uid, so the uid arg is ignored.

        This function will update the self.messagelist dict to contain
        the new message after sucessfully saving it.

        :param rtime: A timestamp to be used as the mail date
        :returns: the UID of the new message as assigned by the server. If the
                  message is saved, but it's UID can not be found, it will
                  return 0. If the message can't be written (folder is
                  read-only for example) it will return -1."""
        self.ui.debug('imap', 'savemessage: called')

        # already have it, just save modified flags
        if uid > 0 and self.uidexists(uid):
            self.savemessageflags(uid, flags)
            return uid

        retry_left = 2 # succeeded in APPENDING?
        imapobj = self.imapserver.acquireconnection()
        try:
            while retry_left:
                # UIDPLUS extension provides us with an APPENDUID response.
                use_uidplus = 'UIDPLUS' in imapobj.capabilities

                # get the date of the message, so we can pass it to the server.
                date = self.getmessageinternaldate(content, rtime)
                content = re.sub("(?<!\r)\n", "\r\n", content)

                if not use_uidplus:
                    # insert a random unique header that we can fetch later
                    (headername, headervalue) = self.generate_randomheader(
                                                    content)
                    self.ui.debug('imap', 'savemessage: header is: %s: %s' %\
                                      (headername, headervalue))
                    content = self.savemessage_addheader(content, headername,
                                                         headervalue)    
                if len(content)>200:
                    dbg_output = "%s...%s" % (content[:150], content[-50:])
                else:
                    dbg_output = content
                self.ui.debug('imap', "savemessage: date: %s, content: '%s'" %
                             (date, dbg_output))

                try:
                    # Select folder for append and make the box READ-WRITE
                    imapobj.select(self.getfullname())
                except imapobj.readonly:
                    # readonly exception. Return original uid to notify that
                    # we did not save the message. (see savemessage in Base.py)
                    self.ui.msgtoreadonly(self, uid, content, flags)
                    return uid

                # Clean out existing APPENDUID responses and do APPEND
                try:
                    imapobj.response('APPENDUID') # flush APPENDUID responses
                    typ, dat = imapobj.append(self.getfullname(),
                                       imaputil.flagsmaildir2imap(flags),
                                       date, content)
                    retry_left = 0                # Mark as success
                except imapobj.abort, e:
                    # connection has been reset, release connection and retry.
                    retry_left -= 1
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = self.imapserver.acquireconnection()
                    if not retry_left:
                        raise OfflineImapError("Saving msg in folder '%s', "
                              "repository '%s' failed (abort). Server reponded: %s\n"
                              "Message content was: %s" %
                              (self, self.getrepository(), str(e), dbg_output),
                                               OfflineImapError.ERROR.MESSAGE)
                    self.ui.error(e, exc_info()[2])
                except imapobj.error, e: # APPEND failed
                    # If the server responds with 'BAD', append()
                    # raise()s directly.  So we catch that too.
                    # drop conn, it might be bad.
                    self.imapserver.releaseconnection(imapobj, True)
                    imapobj = None
                    raise OfflineImapError("Saving msg folder '%s', repo '%s'"
                        "failed (error). Server reponded: %s\nMessage content was: "
                        "%s" % (self, self.getrepository(), str(e), dbg_output),
                                           OfflineImapError.ERROR.MESSAGE)