gmail migration python script

sync.py

#!/usr/bin/env python

import cPickle as pickle
import imaplib
import sys

from sexp import scan_sexp

def savetemp(name, value):
    f = open(name, 'w')
    pickle.dump(value, f)
    f.close()

def loadtemp(name):
    try:
        f = open(name, 'r')
        value = pickle.load(f)
        f.close()
        return value
    except:
        return {}

HOST = 'imap.gmail.com'
PORT = 993
SOURCE_USER = 'XXX@gmail.com'
SOURCE_PASS = 'XXX'
DEST_USER = 'XXX@gmail.com'
DEST_PASS = 'XXX'
TEMPFILE = 'temp'

print 'connecting...'
source = imaplib.IMAP4_SSL(HOST, PORT)
print 'logging in...'
source.login(SOURCE_USER, SOURCE_PASS)
print 'connecting...'
dest = imaplib.IMAP4_SSL(HOST, PORT)
print 'logging in...'
dest.login(DEST_USER, DEST_PASS)

labels = {
    '[Gmail]/All Mail': '[Gmail]/All Mail',
}

temp = loadtemp(TEMPFILE)

for slabel, dlabel in labels.items():
    # creating temp for slabel
    if not temp.has_key(slabel):
        temp[slabel] = {'dest_uids': {}, 'dest_info': {}, 'source_uids': {}}

    # printing temp info for slabel
    print 'loaded for %s: "dest_uids": %d, "dest_info": %d, "source_uids": %d' % (slabel,
                                                                                  len(temp[slabel]['dest_uids']),
                                                                                  len(temp[slabel]['dest_info']),
                                                                                  len(temp[slabel]['source_uids']))

    print 'selecting source folder %s...' % slabel
    if source.select(slabel)[0] == 'NO':
        print 'error: select failed'
        continue
    else:
        print 'selecting dest folder %s...' % dlabel
        if dest.select(dlabel)[0] == 'NO':
            print 'dest folder not found; creating...'
            if dest.create(dlabel)[0]=='NO' or dest.select(dlabel)[0]=='NO':
                print 'error: could not create folder'
                continue

        print 'analyzing existing messages...'
        uids = dest.search(None, 'ALL')[1][0].split()
        if uids:
            i = 1
            length = len(uids)
            for uid in uids:
                # progress
                sys.stdout.write('\r%d/%d' % (i, length))
                sys.stdout.flush()
                i += 1

                if not temp[slabel]['dest_uids'].has_key(uid):
                    try:
                        msg_id = scan_sexp(dest.fetch(uid, 'ENVELOPE')[1][0])[1][1][-1]
                        temp[slabel]['dest_uids'][uid] = True
                        temp[slabel]['dest_info'][msg_id] = True
                    except:
                        pass

        print '\nwriting dest_uids and dest_info temp...'
        savetemp(TEMPFILE, temp)

        print 'migrating...'
        uids = source.search(None, 'ALL')[1][0].split()
        if uids:
            i = 1
            length = len(uids)
            for uid in uids:
                # progress
                sys.stdout.write('\r%d/%d' % (i, length))
                sys.stdout.flush()
                i += 1

                if not temp[slabel]['source_uids'].has_key(uid):
                    try:
                        msg_id = scan_sexp(source.fetch(uid, 'ENVELOPE')[1][0])[1][1][-1]
                        if not temp[slabel]['dest_info'].has_key(msg_id):
                            msg = source.fetch(uid, '(RFC822 FLAGS INTERNALDATE)')
                            dest.append(dlabel, \
                                        imaplib.ParseFlags(msg[1][1][:-1]), \
                                        imaplib.Internaldate2tuple(msg[1][1][:-1]), \
                                        msg[1][0][1])
                            temp[slabel]['source_uids'][uid] = True
                        else:
                            temp[slabel]['source_uids'][uid] = True
                    except:
                        pass

        print '\nwriting dest_uids and dest_info temp...'
        savetemp(TEMPFILE, temp)
                    
try:
    source.close()
    dest.close()
except:
    pass

print '\ndone'

sexp.py

# -*- coding: utf-8 -*-

# imaplib2 python module, meant to be a replacement to the python default 
# imaplib module
# Copyright (C) 2008 Helder Guerreiro

## This file is part of imaplib2.
##
## imaplib2 is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## imaplib2 is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with hlimap.  If not, see <http://www.gnu.org/licenses/>.

#
# Helder Guerreiro <helder@paxjulia.com>
#
# $LastChangedDate: 2008-04-22 22:45:55 +0100 (Tue, 22 Apr 2008) $
# $LastChangedRevision: 327 $
# $LastChangedBy: helder $
#

# Global imports
import re, string

# Regexp
literal_re = re.compile(r'^{(\d+)}\r\n')
simple_re = re.compile(r'^([^ ()]+)')
quoted_re = re.compile(r'^"((?:[^"\\]|\\")*?)"')

# Errors
class SError(Exception): pass

def scan_sexp(text):
    '''S-Expression scanner.

    This is a non-recursive version. It uses the lists property of assigning
    only by reference to assemble the s-exp.

    @param text: text to be scanned.
    @type  text: s-exp string

    @return result: s-exp in a python list.
    '''

    # Initialization
    pos = 0
    lenght = len(text)
    current = ''
    result = []
    cur_result = result
    level = [ cur_result ]

    # Scanner
    while pos < lenght:

        # Quoted literal:
        if text[pos] == '"':
            quoted = quoted_re.match(text[pos:])
            if quoted:
                cur_result.append( quoted.groups()[0] )
                pos += quoted.end() - 1

        # Numbered literal:
        elif text[pos] == '{':
            lit = literal_re.match(text[pos:])
            if lit:
                 start = pos+lit.end()
                 end = pos+lit.end()+int(lit.groups()[0])
                 pos = end - 1
                 cur_result.append( text[ start:end ] )

        # Simple literal
        elif text[pos] not in '() ':
            simple = simple_re.match(text[pos:])
            if simple:
                tmp = simple.groups()[0]
                if tmp.isdigit():
                    tmp = int(tmp)
                elif tmp == 'NIL':
                    tmp = None
                cur_result.append( tmp )
                pos += simple.end() - 1

        # Level handling, if we find a '(' we must add another list, if we
        # find a ')' we must return to the previous list.
        elif text[pos] == '(':
            cur_result.append([])
            cur_result = cur_result[-1]
            level.append(cur_result)

        elif text[pos] == ')':
            try:
                cur_result = level[-2]
                del level[-1]
            except IndexError:
                raise SError('Unexpected parenthesis at pos %d' % pos)

        pos += 1

    return result

if __name__ == '__main__':
    from time import time

    count = 1000
    text = '(A NIL {5}\r\n12345 (D E))(F G)'
    text = '266 FETCH (FLAGS (\Seen) UID 31608 INTERNALDATE "30-Jan-2008 02:48:01 +0000" RFC822.SIZE 4509 ENVELOPE ("Tue, 29 Jan 2008 14:00:24 +0000" "Aprenda as tXcnicas e os truques da cozinha mais doce..." (("Ediclube" NIL "ediclube" "sigmathis.info")) (("Ediclube" NIL "ediclube" "sigmathis.info")) ((NIL NIL "ediclube" "sigmathis.info")) ((NIL NIL "helder" "example.com")) NIL NIL NIL "<64360f85d83238281a27b921fd3e7eb3@localhost.localdomain>"))'
    #text = 'AA 12341 NIL (A NIL "asdasd fffff\\"sdasd" {%d}\r\n%s (D E))(F G)' % ( count, '#' * count)
    #text = 'A B (C NIL (D E))(F G)'

    itx = 300
    rit = xrange(itx)

    print 'Test to the s-exp parser:'
    print

    print 'Non Recursive (%d times):' % itx
    a = time()



    for i in rit:
        scan_sexp(text)
    b = time()
    print 1000 * (b-a) / itx, 'ms/iter'
    print itx, ' --> ', 1000 * (b-a) , 'ms'
    print
    print scan_sexp(text)