コード例 #1
0
    def savemessageflags(self, uid, flags):
        oldfilename = self.messagelist[uid]['filename']
        dir_prefix, newname = os.path.split(oldfilename)
        tmpdir = os.path.join(self.getfullname(), 'tmp')
        if 'S' in flags:
            # If a message has been seen, it goes into the cur
            # directory.  CR debian#152482
            dir_prefix = 'cur'
        else:
            dir_prefix = 'new'

        infostr = self.infosep
        infomatch = re.search('(' + self.infosep + '.*)$', newname)
        if infomatch:  # If the info string is present..
            infostr = infomatch.group(1)
            newname = newname.split(
                self.infosep)[0]  # Strip off the info string.
        infostr = re.sub('2,[A-Z]*', '', infostr)
        infostr += '2,' + ''.join(sorted(flags))
        newname += infostr

        newfilename = os.path.join(dir_prefix, newname)
        if (newfilename != oldfilename):
            try:
                os.rename(os.path.join(self.getfullname(), oldfilename),
                          os.path.join(self.getfullname(), newfilename))
            except OSError, e:
                raise OfflineImapError(
                    "Can't rename file '%s' to '%s': %s" %
                    (oldfilename, newfilename, e[1]),
                    OfflineImapError.ERROR.FOLDER)

            self.messagelist[uid]['flags'] = flags
            self.messagelist[uid]['filename'] = newfilename
コード例 #2
0
ファイル: Maildir.py プロジェクト: wosc/offlineimap
    def change_message_uid(self, uid, new_uid):
        """Change the message from existing uid to new_uid

        This will not update the statusfolder UID, you need to do that yourself.
        :param new_uid: (optional) If given, the old UID will be changed
                        to a new UID. The Maildir backend can implement this as
                        an efficient rename.
        """

        if not uid in self.messagelist:
            raise OfflineImapError(
                "Cannot change unknown Maildir UID %s" % uid,
                OfflineImapError.ERROR.MESSAGE)
        if uid == new_uid: return

        oldfilename = self.messagelist[uid]['filename']
        dir_prefix, filename = os.path.split(oldfilename)
        flags = self.getmessageflags(uid)
        # TODO: we aren't keeping the prefix timestamp so we don't honor the
        # filename_use_mail_timestamp configuration option.
        newfilename = os.path.join(dir_prefix,
                                   self.new_message_filename(new_uid, flags))
        os.rename(os.path.join(self.getfullname(), oldfilename),
                  os.path.join(self.getfullname(), newfilename))
        self.messagelist[new_uid] = self.messagelist[uid]
        self.messagelist[new_uid]['filename'] = newfilename
        del self.messagelist[uid]
コード例 #3
0
    def makefolder(self, foldername):
        """Create a folder on the IMAP server

        This will not update the list cached in :meth:`getfolders`. You
        will need to invoke :meth:`forgetfolders` to force new caching
        when you are done creating folders yourself.

        :param foldername: Full path of the folder to be created."""

        if foldername is '':
            return

        if self.getreference():
            foldername = self.getreference() + self.getsep() + foldername
        if not foldername:  # Create top level folder as folder separator.
            foldername = self.getsep()
        self.ui.makefolder(self, foldername)
        if self.account.dryrun:
            return
        imapobj = self.imapserver.acquireconnection()
        try:
            result = imapobj.create(foldername)
            if result[0] != 'OK':
                raise OfflineImapError(
                    "Folder '%s'[%s] could not be created. "
                    "Server responded: %s" % (foldername, self, str(result)),
                    OfflineImapError.ERROR.FOLDER)
        finally:
            self.imapserver.releaseconnection(imapobj)
コード例 #4
0
ファイル: IMAP.py プロジェクト: imbilltucker/offlineimap3
    def get_auth_mechanisms(self):
        """
        Get the AUTH mechanisms. We have (ranged from the strongest to weakest)
        these methods: "GSSAPI", "XOAUTH2", "CRAM-MD5", "PLAIN", "LOGIN"

        Returns: The supported AUTH Methods

        """
        supported = ["GSSAPI", "XOAUTH2", "CRAM-MD5", "PLAIN", "LOGIN"]
        # Mechanisms are ranged from the strongest to the
        # weakest ones.
        # TODO: we need DIGEST-MD5, it must come before CRAM-MD5
        # due to the chosen-plaintext resistance.
        default = ["GSSAPI", "XOAUTH2", "CRAM-MD5", "PLAIN", "LOGIN"]

        mechs = self.getconflist('auth_mechanisms', r',\s*', default)

        for mech in mechs:
            if mech not in supported:
                raise OfflineImapError(
                    "Repository %s: " % self +
                    "unknown authentication mechanism '%s'" % mech,
                    OfflineImapError.ERROR.REPO)

        self.ui.debug('imap', "Using authentication mechanisms %s" % mechs)
        return mechs
コード例 #5
0
ファイル: IMAP.py プロジェクト: imbilltucker/offlineimap3
    def makefolder_single(self, foldername):
        """
        Create a IMAP folder.

        Args:
            foldername: Folder's name to create

        Returns: None

        """
        self.ui.makefolder(self, foldername)
        if self.account.dryrun:
            return
        imapobj = self.imapserver.acquireconnection()
        try:
            if self.account.utf_8_support:
                foldername = imaputil.utf8_IMAP(foldername)

            result = imapobj.create(foldername)
            if result[0] != 'OK':
                msg = "Folder '%s'[%s] could not be created. "\
                      "Server responded: %s" % (foldername, self, str(result))
                raise OfflineImapError(msg, OfflineImapError.ERROR.FOLDER)
        finally:
            self.imapserver.releaseconnection(imapobj)
コード例 #6
0
ファイル: IMAP.py プロジェクト: dimpase/offlineimap
    def _fetch_from_imap(self, imapobj, uids, query, retry_num=1):
        fails_left = retry_num # retry on dropped connection
        while fails_left:
            try:
                imapobj.select(self.getfullname(), readonly = True)
                res_type, data = imapobj.uid('fetch', uids, query)
                fails_left = 0
            except imapobj.abort as e:
                # Release dropped connection, and get a new one
                self.imapserver.releaseconnection(imapobj, True)
                imapobj = self.imapserver.acquireconnection()
                self.ui.error(e, exc_info()[2])
                fails_left -= 1
                if not fails_left:
                    raise e
        if data == [None] or res_type != 'OK':
            #IMAP server says bad request or UID does not exist
            severity = OfflineImapError.ERROR.MESSAGE
            reason = "IMAP server '%s' failed to fetch messages UID '%s'."\
                "Server responded: %s %s" % (self.getrepository(), uids,
                                             res_type, data)
            if data == [None]:
                #IMAP server did not find a message with this UID
                reason = "IMAP server '%s' does not have a message "\
                    "with UID '%s'" % (self.getrepository(), uids)
            raise OfflineImapError(reason, severity)

        return data
コード例 #7
0
ファイル: Maildir.py プロジェクト: pilou-/offlineimap
    def change_message_uid(self, uid, new_uid):
        """Change the message from existing uid to new_uid

        This will not update the statusfolder UID, you need to do that yourself.
        :param new_uid: (optional) If given, the old UID will be changed
                        to a new UID. The Maildir backend can implement this as
                        an efficient rename.
        """

        if not uid in self.messagelist:
            raise OfflineImapError("Cannot change unknown Maildir UID %s" %
                                   uid)
        if uid == new_uid: return

        oldfilename = self.messagelist[uid]['filename']
        dir_prefix, filename = os.path.split(oldfilename)
        flags = self.getmessageflags(uid)
        content = self.getmessage(uid)
        rtime = emailutil.get_message_date(content)
        newfilename = os.path.join(
            dir_prefix, self.new_message_filename(new_uid, flags, rtime=rtime))
        os.rename(os.path.join(self.getfullname(), oldfilename),
                  os.path.join(self.getfullname(), newfilename))
        self.messagelist[new_uid] = self.messagelist[uid]
        self.messagelist[new_uid]['filename'] = newfilename
        del self.messagelist[uid]
コード例 #8
0
    def makefolder(self, foldername):
        """Create a folder on the IMAP server

        This will not update the list cached in :meth:`getfolders`. You
        will need to invoke :meth:`forgetfolders` to force new caching
        when you are done creating folders yourself.

        :param foldername: Full path of the folder to be created."""
        #TODO: IMHO this existing commented out code is correct and
        #should be enabled, but this would change the behavior for
        #existing configurations who have a 'reference' set on a Mapped
        #IMAP server....:
        #if self.getreference() != '""':
        #    newname = self.getreference() + self.getsep() + foldername
        #else:
        #    newname = foldername
        imapobj = self.imapserver.acquireconnection()
        try:
            self.ui._msg("Creating new IMAP folder '%s' on server %s" %\
                              (foldername, self))
            result = imapobj.create(foldername)
            if result[0] != 'OK':
                raise OfflineImapError("Folder '%s'[%s] could not be created. "
                                       "Server responded: %s" % \
                                           (foldername, self, str(result)),
                                       OfflineImapError.ERROR.FOLDER)
        finally:
            self.imapserver.releaseconnection(imapobj)
コード例 #9
0
ファイル: UIDMaps.py プロジェクト: limansky/offlineimap3
    def change_message_uid(self, ruid, new_ruid):
        """Change the message from existing ruid to new_ruid

        :param new_uid: The old remote UID will be changed to a new
            UID. The UIDMaps case handles this efficiently by simply
            changing the mappings file."""

        if ruid not in self.r2l:
            raise OfflineImapError("Cannot change unknown Maildir UID %s" %
                                   ruid, OfflineImapError.ERROR.MESSAGE)
        if ruid == new_ruid:
            return  # sanity check shortcut

        with self.maplock:
            luid = self.r2l[ruid]
            self.l2r[luid] = new_ruid
            del self.r2l[ruid]
            self.r2l[new_ruid] = luid
            # TODO: diskl2r|r2l are a pain to sync and should be done away with
            # diskl2r only contains positive UIDs, so wrap in ifs.
            if luid > 0:
                self.diskl2r[luid] = new_ruid
            if ruid > 0:
                del self.diskr2l[ruid]
            if new_ruid > 0:
                self.diskr2l[new_ruid] = luid
            self._savemaps()
コード例 #10
0
ファイル: Maildir.py プロジェクト: frioux/offlineimap
    def migratefmd5(self, dryrun=False):
        """Migrate FMD5 hashes from versions prior to 6.3.5

        :param dryrun: Run in dry run mode
        :type fix: Boolean
        :return: None
        """
        oldfmd5 = md5(self.name).hexdigest()
        msglist = self._scanfolder()
        for mkey, mvalue in msglist.iteritems():
            filename = os.path.join(self.getfullname(), mvalue['filename'])
            match = re.search("FMD5=([a-fA-F0-9]+)", filename)
            if match is None:
                self.ui.debug(
                    "maildir",
                    "File `%s' doesn't have an FMD5 assigned" % filename)
            elif match.group(1) == oldfmd5:
                self.ui.info("Migrating file `%s' to FMD5 `%s'" %
                             (filename, self._foldermd5))
                if not dryrun:
                    newfilename = filename.replace("FMD5=" + match.group(1),
                                                   "FMD5=" + self._foldermd5)
                    try:
                        os.rename(filename, newfilename)
                    except OSError as e:
                        raise OfflineImapError(
                            "Can't rename file '%s' to '%s': %s" %
                            (filename, newfilename, e[1]),
                            OfflineImapError.ERROR.FOLDER), None, exc_info()[2]
            elif match.group(1) != self._foldermd5:
                self.ui.warn(("Inconsistent FMD5 for file `%s':"
                              " Neither `%s' nor `%s' found") %
                             (filename, oldfmd5, self._foldermd5))
コード例 #11
0
    def savemessagelabels(self, uid, labels, ignorelabels=set()):
        """Change a message's labels to `labels`.

        Note that this function does not check against dryrun settings,
        so you need to ensure that it is never called in a dryrun mode."""

        filename = self.messagelist[uid]['filename']
        filepath = os.path.join(self.getfullname(), filename)

        file = open(filepath, 'rt')
        content = file.read()
        file.close()

        oldlabels = set()
        for hstr in self.getmessageheaderlist(content, self.labelsheader):
            oldlabels.update(
                imaputil.labels_from_header(self.labelsheader, hstr))

        labels = labels - ignorelabels
        ignoredlabels = oldlabels & ignorelabels
        oldlabels = oldlabels - ignorelabels

        # Nothing to change.
        if labels == oldlabels:
            return

        # Change labels into content.
        labels_str = imaputil.format_labels_string(
            self.labelsheader, sorted(labels | ignoredlabels))

        # First remove old labels header, and then add the new one.
        content = self.deletemessageheaders(content, self.labelsheader)
        content = self.addmessageheader(content, '\n', self.labelsheader,
                                        labels_str)

        mtime = int(os.stat(filepath).st_mtime)

        # Write file with new labels to a unique file in tmp.
        messagename = self.new_message_filename(uid, set())
        tmpname = self.save_to_tmp_file(messagename, content)
        tmppath = os.path.join(self.getfullname(), tmpname)

        # Move to actual location.
        try:
            os.rename(tmppath, filepath)
        except OSError as e:
            six.reraise(
                OfflineImapError,
                OfflineImapError(
                    "Can't rename file '%s' to '%s': %s" %
                    (tmppath, filepath, e[1]), OfflineImapError.ERROR.FOLDER),
                exc_info()[2])

        # If utime_from_header=true, we don't want to change the mtime.
        if self._utime_from_header and mtime:
            os.utime(filepath, (mtime, mtime))

        # save the new mtime and labels
        self.messagelist[uid]['mtime'] = int(os.stat(filepath).st_mtime)
        self.messagelist[uid]['labels'] = labels
コード例 #12
0
 def open(self, host=None, port=None):
     if not self.ca_certs and not self._fingerprint:
         raise OfflineImapError("No CA certificates " + \
           "and no server fingerprints configured.  " + \
           "You must configure at least something, otherwise " + \
           "having SSL helps nothing.", OfflineImapError.ERROR.REPO)
     super(WrappedIMAP4_SSL, self).open(host, port)
     if self._fingerprint:
         # compare fingerprints
         fingerprint = sha1(self.sock.getpeercert(True)).hexdigest()
         if fingerprint not in self._fingerprint:
             raise OfflineImapError("Server SSL fingerprint '%s' " % fingerprint + \
                   "for hostname '%s' " % host + \
                   "does not match configured fingerprint(s) %s.  " % self._fingerprint + \
                   "Please verify and set 'cert_fingerprint' accordingly " + \
                   "if not set yet.", OfflineImapError.ERROR.REPO)
コード例 #13
0
class UsefulIMAPMixIn(object):
    def getselectedfolder(self):
        if self.state == 'SELECTED':
            return self.mailbox
        return None

    def select(self, mailbox='INBOX', readonly=False, force = 0):
        """Selects a mailbox on the IMAP server

        :returns: 'OK' on success, nothing if the folder was already
        selected or raises an :exc:`OfflineImapError`"""
        if self.getselectedfolder() == mailbox and self.is_readonly == readonly \
                and not force:
            # No change; return.
            return
        # Wipe out all old responses, to maintain semantics with old imaplib2
        del self.untagged_responses[:]
        try:
            result = super(UsefulIMAPMixIn, self).select(mailbox, readonly)
        except self.abort, e:
            # self.abort is raised when we are supposed to retry
            errstr = "Server '%s' closed connection, error on SELECT '%s'. Ser"\
                "ver said: %s" % (self.host, mailbox, e.args[0])
            severity = OfflineImapError.ERROR.FOLDER_RETRY
            raise OfflineImapError(errstr, severity)
        if result[0] != 'OK':
            #in case of error, bail out with OfflineImapError
            errstr = "Error SELECTing mailbox '%s', server reply:\n%s" %\
                (mailbox, result)
            severity = OfflineImapError.ERROR.FOLDER
            raise OfflineImapError(errstr, severity)
        return result
コード例 #14
0
ファイル: IMAP.py プロジェクト: cycomanic/offlineimap
    def change_message_uid(self, uid, new_uid):
        """Change the message from existing uid to new_uid

        If the backend supports it. IMAP does not and will throw errors."""
        raise OfflineImapError(
            'IMAP backend cannot change a messages UID from '
            '%d to %d' % (uid, new_uid), OfflineImapError.ERROR.MESSAGE)
コード例 #15
0
ファイル: IMAP.py プロジェクト: wosc/offlineimap
    def __savemessage_searchforheader(self, imapobj, headername, headervalue):
        self.ui.debug('imap', '__savemessage_searchforheader called for %s: %s'%
            (headername, headervalue))
        # Now find the UID it got.
        headervalue = imapobj._quote(headervalue)
        try:
            matchinguids = imapobj.uid('search', 'HEADER',
                headername, headervalue)[1][0]
        except imapobj.error as err:
            # IMAP server doesn't implement search or had a problem.
            self.ui.debug('imap', "__savemessage_searchforheader: got IMAP "
                "error '%s' while attempting to UID SEARCH for message with "
                "header %s"% (err, headername))
            return 0
        self.ui.debug('imap', "__savemessage_searchforheader got initial "
                              "matchinguids: " + repr(matchinguids))

        if matchinguids == '':
            self.ui.debug('imap', "__savemessage_searchforheader: UID SEARCH "
                "for message with header %s yielded no results"% headername)
            return 0

        matchinguids = matchinguids.split(' ')
        self.ui.debug('imap', '__savemessage_searchforheader: matchinguids now '
            + repr(matchinguids))
        if len(matchinguids) != 1 or matchinguids[0] is None:
            raise OfflineImapError(
                "While attempting to find UID for message with "
                "header %s, got wrong-sized matchinguids of %s"%
                (headername, str(matchinguids)),
                OfflineImapError.ERROR.MESSAGE
            )
        return int(matchinguids[0])
コード例 #16
0
    def cachemessagelist(self):
        if not self.synclabels:
            return super(GmailFolder, self).cachemessagelist()

        self.messagelist = {}

        self.ui.collectingdata(None, self)
        imapobj = self.imapserver.acquireconnection()
        try:
            msgsToFetch = self._msgs_to_fetch(imapobj)
            if not msgsToFetch:
                return  # No messages to sync

            # Get the flags and UIDs for these. single-quotes prevent
            # imaplib2 from quoting the sequence.
            #
            # NB: msgsToFetch are sequential numbers, not UID's
            res_type, response = imapobj.fetch("'%s'" % msgsToFetch,
                                               '(FLAGS X-GM-LABELS UID)')
            if res_type != 'OK':
                raise OfflineImapError("FETCHING UIDs in folder [%s]%s failed. " % \
                  (self.getrepository(), self) + \
                  "Server responded '[%s] %s'" % \
                  (res_type, response), OfflineImapError.ERROR.FOLDER), \
                  None, exc_info()[2]
        finally:
            self.imapserver.releaseconnection(imapobj)

        for messagestr in response:
            # looks like: '1 (FLAGS (\\Seen Old) X-GM-LABELS (\\Inbox \\Favorites) UID 4807)' or None if no msg
            # Discard initial message number.
            if messagestr == None:
                continue
            messagestr = messagestr.split(' ', 1)[1]
            options = imaputil.flags2hash(messagestr)
            if not 'UID' in options:
                self.ui.warn('No UID in message with options %s' %\
                                          str(options),
                                          minor = 1)
            else:
                uid = long(options['UID'])
                self.messagelist[uid] = self.msglist_item_initializer(uid)
                flags = imaputil.flagsimap2maildir(options['FLAGS'])
                m = re.search('\(([^\)]*)\)', options['X-GM-LABELS'])
                if m:
                    labels = set([
                        imaputil.dequote(lb)
                        for lb in imaputil.imapsplit(m.group(1))
                    ])
                else:
                    labels = set()
                labels = labels - self.ignorelabels
                rtime = imaplibutil.Internaldate2epoch(messagestr)
                self.messagelist[uid] = {
                    'uid': uid,
                    'flags': flags,
                    'labels': labels,
                    'time': rtime
                }
コード例 #17
0
ファイル: IMAP.py プロジェクト: musoke/offlineimap
 def getmaxage(self):
     if self.config.getdefault("Account %s" % self.accountname, "maxage",
                               None):
         six.reraise(
             OfflineImapError,
             OfflineImapError("maxage is not supported on IMAP-IMAP sync",
                              OfflineImapError.ERROR.REPO),
             exc_info()[2])
コード例 #18
0
 def _uidlist(self, mapping, items):
     try:
         return [mapping[x] for x in items]
     except KeyError as e:
         raise OfflineImapError("Could not find UID for msg '{0}' (f:'{1}'."
             " This is usually a bad thing and should be reported on the ma"
             "iling list.".format(e.args[0], self),
             OfflineImapError.ERROR.MESSAGE), None, exc_info()[2]
コード例 #19
0
    def __xoauth2handler(self, response):
        if self.oauth2_access_token is None:
            if self.oauth2_request_url is None:
                raise OfflineImapError("No remote oauth2_request_url for "
                    "repository '%s' specified."%
                    self, OfflineImapError.ERROR.REPO)

            # Generate new access token.
            params = {}
            params['client_id'] = self.oauth2_client_id
            params['client_secret'] = self.oauth2_client_secret
            params['refresh_token'] = self.oauth2_refresh_token
            params['grant_type'] = 'refresh_token'

            self.ui.debug('imap', 'xoauth2handler: url "%s"'%
                self.oauth2_request_url)
            self.ui.debug('imap', 'xoauth2handler: params "%s"'% params)

            original_socket = socket.socket
            socket.socket = self.authproxied_socket
            try:
                response = urllib.urlopen(
                    self.oauth2_request_url, urllib.urlencode(params)).read()
            except Exception as e:
                try:
                    msg = "%s (configuration is: %s)"% (e, str(params))
                except Exception as eparams:
                    msg = "%s [cannot display configuration: %s]"% (e, eparams)
                six.reraise(type(e), type(e)(msg), exc_info()[2])
            finally:
                socket.socket = original_socket

            resp = json.loads(response)
            self.ui.debug('imap', 'xoauth2handler: response "%s"'% resp)
            if u'error' in resp:
                raise OfflineImapError("xoauth2handler got: %s"% resp,
                    OfflineImapError.ERROR.REPO)
            self.oauth2_access_token = resp['access_token']

        self.ui.debug('imap', 'xoauth2handler: access_token "%s"'%
            self.oauth2_access_token)
        auth_string = 'user=%s\1auth=Bearer %s\1\1'% (
            self.username, self.oauth2_access_token)
        #auth_string = base64.b64encode(auth_string)
        self.ui.debug('imap', 'xoauth2handler: returning "%s"'% auth_string)
        return auth_string
コード例 #20
0
    def getsslcacertfile(self):
        """Determines CA bundle.

        Returns path to the CA bundle.  It is explicitely specified or
        requested via "OS-DEFAULT" value (and we will search known
        locations for the current OS and distribution). If it is not
        specified, we will search it in the known locations.

        If search route, via "OS-DEFAULT" or because is not specified,
        yields nothing, we will throw an exception to make our callers
        distinguish between not specified value and non-existent
        default CA bundle.

        It is also an error to specify non-existent file via configuration:
        it will error out later, but, perhaps, with less verbose explanation,
        so we will also throw an exception.  It is consistent with
        the above behaviour, so any explicitely-requested configuration
        that doesn't result in an existing file will give an exception.
        """
        xforms = [os.path.expanduser, os.path.expandvars, os.path.abspath]
        cacertfile = self.getconf_xform('sslcacertfile', xforms, None)
        # Can't use above cacertfile because of abspath.
        conf_sslacertfile = self.getconf('sslcacertfile', None)
        if conf_sslacertfile == "OS-DEFAULT" or \
                conf_sslacertfile is None or \
                conf_sslacertfile == '':
            cacertfile = get_os_sslcertfile()
            if cacertfile is None:
                searchpath = get_os_sslcertfile_searchpath()
                if searchpath:
                    reason = "Default CA bundle was requested, " \
                             "but no existing locations available.  " \
                             "Tried %s." % (", ".join(searchpath))
                else:
                    reason = "Default CA bundle was requested, " \
                             "but OfflineIMAP doesn't know any for your " \
                             "current operating system."
                raise OfflineImapError(reason, OfflineImapError.ERROR.REPO)
        if cacertfile is None:
            return None
        if not os.path.isfile(cacertfile):
            reason = "CA certfile for repository '%s' couldn't be found.  " \
                     "No such file: '%s'" % (self.name, cacertfile)
            raise OfflineImapError(reason, OfflineImapError.ERROR.REPO)
        return cacertfile
コード例 #21
0
 def __start_tls(self, imapobj):
     if 'STARTTLS' in imapobj.capabilities and not self.usessl:
         self.ui.debug('imap', 'Using STARTTLS connection')
         try:
             imapobj.starttls()
         except imapobj.error as e:
             raise OfflineImapError("Failed to start "
                 "TLS connection: %s"% str(e),
                 OfflineImapError.ERROR.REPO, None, exc_info()[2])
コード例 #22
0
ファイル: IMAP.py プロジェクト: thekix/offlineimap3
        def search(search_conditions):
            """Actually request the server with the specified conditions.

            Returns: range(s) for messages or None if no messages
            are to be fetched."""
            try:
                res_type, res_data = imapobj.search(None, search_conditions)
                if res_type != 'OK':
                    msg = "SEARCH in folder [%s]%s failed. " \
                          "Search string was '%s'. " \
                          "Server responded '[%s] %s'" % \
                          (self.getrepository(), self, search_cond,
                           res_type, res_data)
                    raise OfflineImapError(msg, OfflineImapError.ERROR.FOLDER)
            except Exception as e:
                msg = "SEARCH in folder [%s]%s failed. "\
                      "Search string was '%s'. Error: %s" % \
                      (self.getrepository(), self, search_cond, str(e))
                raise OfflineImapError(msg, OfflineImapError.ERROR.FOLDER)

            """
            In Py2, with IMAP, imaplib2 returned a list of one element string.
              ['1, 2, 3, ...'] -> in Py3 is [b'1 2 3,...']
            In Py2, with Davmail, imaplib2 returned a list of strings.
              ['1', '2', '3', ...] -> in Py3 should be [b'1', b'2', b'3',...]

            In my tests with Py3, I get a list with one element: [b'1 2 3 ...']
            Then I convert the values to string and I get ['1 2 3 ...']

            With Davmail, it should be [b'1', b'2', b'3',...]
            When I convert the values to string, I get ['1', '2', '3',...]
            """
            res_data = [x.decode('utf-8') for x in res_data]

            # Then, I can do the check in the same way than Python 2
            # with string comparison:
            if len(res_data) > 0 and (' ' in res_data[0] or res_data[0] == ''):
                res_data = res_data[0].split()
            # Some servers are broken.
            if 0 in res_data:
                self.ui.warn("server returned UID with 0; ignoring.")
                res_data.remove(0)
            return res_data
コード例 #23
0
 def __authn_login(self, imapobj):
     # Use LOGIN command, unless LOGINDISABLED is advertized
     # (per RFC 2595)
     if 'LOGINDISABLED' in imapobj.capabilities:
         raise OfflineImapError("IMAP LOGIN is "
                                "disabled by server.  Need to use SSL?",
                                OfflineImapError.ERROR.REPO)
     else:
         self.__loginauth(imapobj)
         return True
コード例 #24
0
ファイル: Maildir.py プロジェクト: jishac/offlineimap3
    def getmessage(self, uid):
        """Returns an email message object."""

        filename = self.messagelist[uid]['filename']
        filepath = os.path.join(self.getfullname(), filename)
        fd = open(filepath, 'rb')
        _fd_bytes = fd.read()
        fd.close()
        try:
            retval = self.parser['8bit'].parsebytes(_fd_bytes)
        except:
            err = exc_info()
            msg_id = self._extract_message_id(_fd_bytes)[0].decode(
                'ascii', errors='surrogateescape')
            raise OfflineImapError(
                "Exception parsing message with ID ({}) from file ({}).\n {}: {}"
                .format(msg_id, filename, err[0].__name__,
                        err[1]), OfflineImapError.ERROR.MESSAGE)
        if len(retval.defects) > 0:
            # We don't automatically apply fixes as to attempt to preserve the original message
            self.ui.warn("UID {} has defects: {}".format(uid, retval.defects))
            if any(
                    isinstance(defect, NoBoundaryInMultipartDefect)
                    for defect in retval.defects):
                # (Hopefully) Rare defect from a broken client where multipart boundary is
                # not properly quoted.  Attempt to solve by fixing the boundary and parsing
                self.ui.warn(" ... applying multipart boundary fix.")
                retval = self.parser['8bit'].parsebytes(
                    self._quote_boundary_fix(_fd_bytes))
            try:
                # See if the defects after fixes are preventing us from obtaining bytes
                _ = retval.as_bytes(policy=self.policy['8bit'])
            except UnicodeEncodeError as err:
                # Unknown issue which is causing failure of as_bytes()
                msg_id = self.getmessageheader(retval, "message-id")
                if msg_id is None:
                    msg_id = '<unknown-message-id>'
                raise OfflineImapError(
                    "UID {} ({}) has defects preventing it from being processed!\n  {}: {}"
                    .format(uid, msg_id,
                            type(err).__name__,
                            err), OfflineImapError.ERROR.MESSAGE)
        return retval
コード例 #25
0
ファイル: IMAP.py プロジェクト: dimpase/offlineimap
 def _store_to_imap(self, imapobj, uid, field, data):
     imapobj.select(self.getfullname())
     res_type, retdata = imapobj.uid('store', uid, field, data)
     if res_type != 'OK':
         severity = OfflineImapError.ERROR.MESSAGE
         reason = "IMAP server '%s' failed to store %s for message UID '%d'."\
                  "Server responded: %s %s" % (self.getrepository(), field, uid,
                                               res_type, retdata)
         raise OfflineImapError(reason, severity)
     return retdata[0]
コード例 #26
0
    def cachemessagelist(self, min_date=None, min_uid=None):
        self.ui.loadmessagelist(self.repository, self)
        self.dropmessagelistcache()

        imapobj = self.imapserver.acquireconnection()
        try:
            msgsToFetch = self._msgs_to_fetch(imapobj,
                                              min_date=min_date,
                                              min_uid=min_uid)
            if not msgsToFetch:
                return  # No messages to sync.

            # Get the flags and UIDs for these. single-quotes prevent
            # imaplib2 from quoting the sequence.
            fetch_msg = "%s" % msgsToFetch
            self.ui.debug(
                'imap', "calling imaplib2 fetch command: %s %s" %
                (fetch_msg, '(FLAGS UID INTERNALDATE)'))
            res_type, response = imapobj.fetch(fetch_msg,
                                               '(FLAGS UID INTERNALDATE)')
            if res_type != 'OK':
                raise OfflineImapError(
                    "FETCHING UIDs in folder [%s]%s failed. "
                    "Server responded '[%s] %s'" %
                    (self.getrepository(), self, res_type, response),
                    OfflineImapError.ERROR.FOLDER)
        finally:
            self.imapserver.releaseconnection(imapobj)

        for messagestr in response:
            # Looks like: '1 (FLAGS (\\Seen Old) UID 4807)' or None if no msg.
            # Discard initial message number.
            if messagestr is None:
                continue
            messagestr = messagestr.decode('utf-8').split(' ', 1)[1]
            options = imaputil.flags2hash(messagestr)
            if 'UID' not in options:
                self.ui.warn('No UID in message with options %s' %
                             str(options),
                             minor=1)
            else:
                uid = int(options['UID'])
                self.messagelist[uid] = self.msglist_item_initializer(uid)
                flags = imaputil.flagsimap2maildir(options['FLAGS'])
                keywords = imaputil.flagsimap2keywords(options['FLAGS'])
                rtime = imaplibutil.Internaldate2epoch(
                    messagestr.encode('utf-8'))
                self.messagelist[uid] = {
                    'uid': uid,
                    'flags': flags,
                    'time': rtime,
                    'keywords': keywords
                }
        self.ui.messagelistloaded(self.repository, self,
                                  self.getmessagecount())
コード例 #27
0
ファイル: imaplibutil.py プロジェクト: paranoidsp/offlineimap
 def open(self, host=None, port=None):
     super(WrappedIMAP4_SSL, self).open(host, port)
     if (self._fingerprint or not self.ca_certs):
         # compare fingerprints
         fingerprint = sha1(self.sock.getpeercert(True)).hexdigest()
         if fingerprint != self._fingerprint:
             raise OfflineImapError(
                 "Server SSL fingerprint '%s' for hostnam"
                 "e '%s' does not match configured fingerprint. Please ver"
                 "ify and set 'cert_fingerprint' accordingly if not set ye"
                 "t." % (fingerprint, host), OfflineImapError.ERROR.REPO)
コード例 #28
0
ファイル: Maildir.py プロジェクト: sbambach/offlineimap
    def savemessage(self, uid, content, flags, rtime):
        """Writes a new message, with the specified uid.

        See folder/Base for detail. Note that savemessage() does not
        check against dryrun settings, so you need to ensure that
        savemessage is never called in a dryrun mode."""
        # This function only ever saves to tmp/,
        # but it calls savemessageflags() to actually save to cur/ or new/.
        self.ui.savemessage('maildir', uid, flags, self)
        if uid < 0:
            # We cannot assign a new uid.
            return uid

        if uid in self.messagelist:
            # We already have it, just update flags.
            self.savemessageflags(uid, flags)
            return uid

        # Otherwise, save the message in tmp/ and then call savemessageflags()
        # to give it a permanent home.
        tmpdir = os.path.join(self.getfullname(), 'tmp')
        messagename = self.new_message_filename(uid, flags)
        # open file and write it out
        try:
            fd = os.open(os.path.join(tmpdir, messagename),
                         os.O_EXCL | os.O_CREAT | os.O_WRONLY, 0o666)
        except OSError as e:
            if e.errno == 17:
                #FILE EXISTS ALREADY
                severity = OfflineImapError.ERROR.MESSAGE
                raise OfflineImapError("Unique filename %s already existing." %\
                                           messagename, severity)
            else:
                raise

        file = os.fdopen(fd, 'wt')
        file.write(content)
        # Make sure the data hits the disk
        file.flush()
        if self.dofsync:
            os.fsync(fd)
        file.close()

        if rtime != None:
            os.utime(os.path.join(tmpdir, messagename), (rtime, rtime))

        self.messagelist[uid] = {
            'flags': flags,
            'filename': os.path.join('tmp', messagename)
        }
        # savemessageflags moves msg to 'cur' or 'new' as appropriate
        self.savemessageflags(uid, flags)
        self.ui.debug('maildir', 'savemessage: returning uid %d' % uid)
        return uid
コード例 #29
0
ファイル: IMAP.py プロジェクト: uliska/offlineimap
    def deletefolder(self, foldername):
        """Delete a folder on the IMAP server."""

        imapobj = self.imapserver.acquireconnection()
        try:
            result = imapobj.delete(foldername)
            if result[0] != 'OK':
                raise OfflineImapError("Folder '%s'[%s] could not be deleted. "
                    "Server responded: %s"% (foldername, self, str(result)),
                    OfflineImapError.ERROR.FOLDER)
        finally:
            self.imapserver.releaseconnection(imapobj)
コード例 #30
0
    def __init__(self, reposname, account):
        """Initialize a GmailRepository object."""
        # Enforce SSL usage
        account.getconfig().set('Repository ' + reposname, 'ssl', 'yes')
        IMAPRepository.__init__(self, reposname, account)

        if self.account.getconfboolean('synclabels', 0) and \
              self.account.getconf('status_backend', 'plain') != 'sqlite':
            raise OfflineImapError(
                "The Gmail repository needs the sqlite backend to sync labels.\n"
                "To enable it add 'status_backend = sqlite' in the account section",
                OfflineImapError.ERROR.REPO)