#!/usr/bin/env python
#
#    imap2mbox.py v1.2.0
#
#    Copyright (C) 2008  Michele <o-zone@zerozone.it> Pinassi
#
#    This software is free software; you can redistribute it and/or
#    modify it under the terms of the GNU Lesser General Public
#    License as published by the Free Software Foundation; either
#    version 2.1 of the License, or (at your option) any later version.
#
#    This software 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
#    Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with this library; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#    1/9/2008 - First public release
#    29/10/2008 (v1.1.0) Added patches from Gregory Boyce <gboyce@badbelly.com> 
#       (OptionParser, SSL, read-only, skip not-matched) and some small 
#       fixes by me
#    18/11/2008 (v1.2.0) New patches from Gregory Boyce <gboyce@badbelly.com> 
#        that adds support for Exchange. Added compatibility with Python 2.3

""" Export your remote IMAP4 folders to local "mbox" file
    imap2mbox simply connects to remote IMAP4 server and recursively saves all 
    directories (and related messages) to mbox files. After, you can use 
    MHonarc to create an HTML archive ready to be burned into CDs ! 
"""

import getpass, imaplib, os, email, sys, re
from optparse import OptionParser

if sys.version_info[0] >= 2 and sys.version_info[1] >= 5:
    # do stuff for Python 2.5
    from email.utils import unquote
else:
    from rfc822 import unquote

from fnmatch import fnmatch

# Parse commandline options
parser = OptionParser()
parser.add_option("-p", "--port", type="int", help="Port to connect to")
parser.add_option("-u", "--username", default=getpass.getuser(), 
        help="Username (defaults to current user)")
parser.add_option("-w", "--password", default=getpass.getpass(), 
        help="Specify Password (less secure)")
parser.add_option("-f", "--folder", default="*", 
        help="Select folders to download (wildcards supported)")
parser.add_option("-s", "--ssl", action="store_true", 
        help="Enable SSL")                  
parser.add_option("-r", "--readonly", action="store_true", 
        help="Do not mark messages as read")                  
parser.add_option("-t", "--test", action="store_true", help="Test Mode")

(options, args) = parser.parse_args()

# Parse list
def parseList(listEntry):
    """ parses a list enty """
    parser = re.compile('(\(.*\)) (".*") (.*)')
    return parser.match(mbox).groups() 

# Grab the servername from the commandline options
if len(args) != 1:
    parser.error("Argument 'server' missing.")
IMAPHOST = args[0]

IMAPUSER = options.username

# Vai !

class progressBar:
    """ a simple progress bar """
    def __init__(self, minValue = 0, maxValue = 10, totalWidth=12):
        """ constructor for progress bar """
        self.progBar = "[]"   # This holds the progress bar string
        self.min = minValue
        self.max = maxValue
        self.span = maxValue - minValue
        self.width = totalWidth
        self.amount = 0       # When amount == max, we are 100% done 
        self.updateAmount(0)  # Build progress bar string

    def updateAmount(self, newAmount = 0):
        """ updates the value of progress bar """
        if newAmount < self.min: 
            newAmount = self.min
        if newAmount > self.max: 
            newAmount = self.max
        self.amount = newAmount

        # Figure out the new percent done, round to an integer
        diffFromMin = float(self.amount - self.min)
        percentDone = (diffFromMin / float(self.span)) * 100.0
        percentDone = round(percentDone)
        percentDone = int(percentDone)

        # Figure out how many hash bars the percentage should be
        allFull = self.width - 2
        numHashes = (percentDone / 100.0) * allFull
        numHashes = int(round(numHashes))

        # build a progress bar with hashes and spaces
        self.progBar = "[" + '#'*numHashes + ' '*(allFull-numHashes) + "]"

        # figure out where to put the percentage, roughly centered
        percentPlace = (len(self.progBar) / 2) - len(str(percentDone)) 
        percentString = str(percentDone) + "%"

        # slice the percentage into the bar
        self.progBar = self.progBar[0:percentPlace] + percentString + \
                self.progBar[percentPlace+len(percentString):]

    def __str__(self):
        return str(self.progBar)

try:
    # If SSL...
    if options.ssl:
        if options.port:
            imap4 = imaplib.IMAP4_SSL(IMAPHOST, options.port)
        else:
            imap4 = imaplib.IMAP4_SSL(IMAPHOST)
    # else normal...
    else:
        if options.port:
            imap4 = imaplib.IMAP4(IMAPHOST, options.port)
        else:
            imap4 = imaplib.IMAP4(IMAPHOST)
    # Let's go !
    imap4.login(IMAPUSER, options.password)
    if options.readonly:
        imap4.select(readonly=True)
    else:
        imap4.select()
except Exception, e:
    print "Error: %r\n"% (e)
    sys.exit()

if options.test:
    ret, mboxes = imap4.list()
    for mbox in mboxes:
        hasChild, dot, mbName = parseList(mbox)
        mbName = unquote(mbName)
        try:
            if options.readonly:
                ret, numMex = imap4.select(mbName, readonly=True)
            else:
                ret, numMex = imap4.select(mbName)
            print "%s has %d messages"% (mbName, int(numMex[0]))
        except Exception, e:
            print "Error: %r\n"% e
            
    sys.exit()

try:
    os.mkdir(IMAPHOST)
except:
    pass
os.chdir(IMAPHOST)
try:
    os.mkdir(IMAPUSER)
except:
    pass
os.chdir(IMAPUSER)
# Inizia ad importare la casella postale
ret, mboxes = imap4.list()
for mbox in mboxes:
    hasChild, dot, mbName = parseList(mbox)
    mbName = unquote(mbName)
    if options.readonly:
        ret, numMex = imap4.select(mbName, readonly=True)
    else:
        ret, numMex = imap4.select(mbName)
    if fnmatch(mbName, options.folder):
        if int(numMex[0]) > 0:
            mbName = re.sub("/", ".", mbName)
            if os.path.isfile("%s.mbox"% (mbName)):
                print "%s.mbox exists. Skipping !"% (mbName)
            else:
                mboxFd = os.open("%s.mbox"% (mbName), os.O_WRONLY | os.O_CREAT)
                numMex = int(numMex[0])
                print "Running on %s (%d messages):"% (mbName, numMex)
                ret, data = imap4.search(None, 'ALL')
                pBar = progressBar(0, numMex, 20)
                for num in data[0].split():
                    # Preleva messaggio
                    ret, data = imap4.fetch(num, '(RFC822)')
                    msgData = email.message_from_string(data[0][1]) 
                    # Scrivi MESSAGGIO
                    os.write(mboxFd, str(msgData))
                    os.write(mboxFd, "\n")
                    print pBar, "\r",
                    pBar.updateAmount(int(num))
                    sys.stdout.flush()
                # Finito !
                print pBar, "\r",
                pBar.updateAmount(numMex)
                sys.stdout.flush()
                print "\n"
                # Chiudi file mbox
                os.close(mboxFd)
        else:
            print "Skip empty mbox %s"% (mbName)
        
# Chiudi tutto !
imap4.close()
imap4.logout()
        
