#!/usr/bin/python
# -*- coding: UTF-8 -*-

# Evolution IMAP cache to MBOX
#
# Copyright (C) 2011  Fabien LOISON <http://www.flogisoft.com/>
#
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://sam.zoy.org/wtfpl/COPYING for more details.


#This script converts the Evolution IMAP cache into an MBOX folder. It
#can be useful if you remove an IMAP folder by accident. The status
#(read, unread, important, deleted,...) stored in the .ev-summary
#file will be lost.

#This script is not finished, so it can't convert all messages.
#In my case, it converted successfully 98.09% of the mails.

#If you have any question, you can contact me here:
# http://www.flogisoft.com/contact/

#PS: Yes I know that the code is ugly...


INPUT = "./INBOX/"     #FIXME: Change this
OUTPUT = "out.mbox"    #FIXME: Change this


import os
import sys
import glob
import re


MAILS = {}


def _get_file_content(filename):
    path = os.path.join(INPUT, filename)
    file_ = open(path, "r")
    content = file_.read()
    file_.close()
    return content


def _find_boundary(header):
    multi = False
    for line in header.split("\n"):
        if not multi:
            if re.match(r"^content-type:\s*multipart/(alternative|mixed|related).*;\s*boundary=\"?([^\"]+)(\"|\r|$|;)", line, re.IGNORECASE):
                return re.match(r"^content-type:\s*multipart/(alternative|mixed|related).*;\s*boundary=\"?([^\"]+)(\"|\r|$|;)", line, re.IGNORECASE).group(2)
            elif re.match(r"^content-type:\s*multipart/(alternative|mixed|related)", line, re.IGNORECASE):
                multi = True
        else:
            if re.match(r"^\s+(.*)boundary=\"?([^\"]+)(\"|\r|$|;)", line, re.IGNORECASE):
                return re.match(r"^\s+(.*)boundary=\"?([^\"]+)(\"|\r|$|;)", line, re.IGNORECASE).group(2)
            elif re.match("^\s+", line):
                continue
            else:
                multi = False
    return ""


def _build_multipart_mail(mail):
    header = ""
    boundary = ""
    parts = []
    mimes = []
    subparts = []
    submimes = []
    subboundary = ""

    #index
    for file_ in MAILS[mail]['files']:
        if file_ == "%s.HEADER" % mail:
            header = file_
        elif re.match(r"^[0-9]+\.[0-9]+\.MIME$", file_):
            mimes.append(file_)
        elif re.match(r"^[0-9]+\.[0-9]+$", file_):
            parts.append(file_)
        elif re.match(r"^[0-9]+\.[0-9]+\.[0-9]+\.MIME$", file_):
            submimes.append(file_)
        elif re.match(r"^[0-9]+\.[0-9]+\.[0-9]+$", file_):
            subparts.append(file_)

    #Check if we handle all parts
    if len(MAILS[mail]['files']) != len(parts) + len(mimes) + len(subparts) + len(submimes) + 1:
        return False

    #Build mail
    header = _get_file_content(header)
    boundary = _find_boundary(header)
    if boundary == "":
        return False
    #
    MAILS[mail]['mail'] = "X-Evolution-Imap2MBox-MailID: %s\n" % mail
    MAILS[mail]['mail'] += "X-Evolution-Imap2MBox-MailType: MultiPart\n"
    MAILS[mail]['mail'] += header
    mimes.sort()
    submimes.sort()
    for mime in mimes:
        if mime[:-5] in parts:
            MAILS[mail]['mail'] += "\n--%s\n" % boundary # XXX
            MAILS[mail]['mail'] += _get_file_content(mime)
            MAILS[mail]['mail'] += _get_file_content(mime[:-5])
        else:
            subboundary = _find_boundary(_get_file_content(mime))
            if subboundary == "":
                MAILS[mail]['mail'] = ""
                return False
            MAILS[mail]['mail'] += "\n--%s\n" % boundary # XXX
            MAILS[mail]['mail'] += _get_file_content(mime)
            subnumb = re.match(r"^[0-9]+\.([0-9]+)\.MIME$", mime).group(1)
            for submime in submimes:
                if re.match("^[0-9]+\.%s\.[0-9]+\.MIME$" % subnumb, submime):
                    MAILS[mail]['mail'] += "\n--%s\n" % subboundary
                    MAILS[mail]['mail'] += _get_file_content(submime)
                    MAILS[mail]['mail'] += _get_file_content(submime[:-5])
            MAILS[mail]['mail'] += "\n--%s--\n" % subboundary
    MAILS[mail]['mail'] += "\n--%s--\n" % boundary
    return True


def get_mail_list():
    print("==== Make the mail index ====")
    files = os.listdir(INPUT)
    for file_ in files:
        sys.stdout.write("\r\033[K  > Scanning '%s'..." % file_)
        sys.stdout.flush()
        mailnumb = file_.split(".")[0]
        if mailnumb.isdigit():
            if not mailnumb in MAILS:
                MAILS[mailnumb] = {
                        'files': [file_],
                        'mail': "",
                        }
            else:
                MAILS[mailnumb]['files'].append(file_)
        else:
            print("\r\033[K  * '%s' skipped..." % file_)

    print("\r\033[K\n  > %i mails found.\n\n" % len(MAILS))


def build_mails():
    print("==== Build the mails ====")
    count = 0
    for mail in MAILS:
        sys.stdout.write("\r\033[K  > Building mail #%s..." % mail)
        sys.stdout.flush()

        #ID.
        if len(MAILS[mail]['files']) == 1 and MAILS[mail]['files'][0] == "%s." % mail: #ID.
            MAILS[mail]['mail'] = "X-Evolution-Imap2MBox-MailID: %s\n" % mail
            MAILS[mail]['mail'] += "X-Evolution-Imap2MBox-MailType: ID.\n"
            MAILS[mail]['mail'] += _get_file_content("%s." % mail)
            count += 1

        #ID. ID.HEADER
        elif len(MAILS[mail]['files']) == 2 and "%s." % mail in MAILS[mail]['files'] and "%s.HEADER" % mail in MAILS[mail]['files']:
            MAILS[mail]['mail'] = "X-Evolution-Imap2MBox-MailID: %s\n" % mail
            MAILS[mail]['mail'] += "X-Evolution-Imap2MBox-MailType: ID., ID.HEADER\n"
            MAILS[mail]['mail'] += _get_file_content("%s." % mail)
            count += 1

        #ID.1 ID.HEADER
        elif len(MAILS[mail]['files']) == 2 and "%s.1" % mail in MAILS[mail]['files'] and "%s.HEADER" % mail in MAILS[mail]['files']:
            MAILS[mail]['mail'] = "X-Evolution-Imap2MBox-MailID: %s\n" % mail
            MAILS[mail]['mail'] += "X-Evolution-Imap2MBox-MailType: ID.1, ID.HEADER\n"
            MAILS[mail]['mail'] += _get_file_content("%s.HEADER" % mail)
            MAILS[mail]['mail'] += _get_file_content("%s.1" % mail)
            count += 1

        #ID.HEADER ID.TEXT
        elif len(MAILS[mail]['files']) == 2 and "%s.TEXT" % mail in MAILS[mail]['files'] and "%s.HEADER" % mail in MAILS[mail]['files']:
            MAILS[mail]['mail'] = "X-Evolution-Imap2MBox-MailID: %s\n" % mail
            MAILS[mail]['mail'] += "X-Evolution-Imap2MBox-MailType: ID.HEADER, ID.TEXT\n"
            MAILS[mail]['mail'] += _get_file_content("%s.HEADER" % mail)
            MAILS[mail]['mail'] += _get_file_content("%s.TEXT" % mail)
            count += 1

        #Multi Part... Ouch !
        else:
            if _build_multipart_mail(mail):
                count += 1
            else:
                print("\r\033[K  * Mail #%s skipped..." % mail) #Oh no ! :(

    print("\r\033[K\n  > %i/%i mails builded.\n\n" % (count, len(MAILS)))


def write_mbox():
    print("==== Writing the mails ====")
    count = 0
    mbox = open(OUTPUT, "w")
    for mail in MAILS:
        sys.stdout.write("\r\033[K  > Writing mail #%s..." % mail)
        sys.stdout.flush()

        if MAILS[mail]['mail'] != "":
            mbox.write("From -\n")
            mbox.write(MAILS[mail]['mail'])
            mbox.write("\n")
            count += 1
        else:
            pass
            print("\r\033[K  * Mail #%s skipped..." % mail)

    mbox.close()
    print("\r\033[K\n  > %i/%i mails written.\n\n" % (count, len(MAILS)))


get_mail_list()
build_mails()
write_mbox()


