#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2013 Remy van Elst # # This program 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. # This program 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 this program. If not, see . import imaplib import email import mailbox from email.header import decode_header from email.utils import parsedate import time import re from math import ceil from random import choice import os import base64 import cgi import sys import shutil import errno import datetime import fileinput import ConfigParser from quopri import decodestring import getpass # places where the config could be located config_file_paths = [ './nopriv.ini', './.nopriv.ini', '~/.config/nopriv.ini', '/opt/local/etc/nopriv.ini', '/etc/nopriv.ini' ] config = ConfigParser.RawConfigParser() found = False for conf_file in config_file_paths: if os.path.isfile(conf_file): config.read(conf_file) found = True break if found == False: message = "No config file found. Expected places: %s" % \ ("\n".join(config_file_paths), ) raise Exception(message) IMAPSERVER = config.get('nopriv', 'imap_server') IMAPLOGIN = config.get('nopriv', 'imap_user') IMAPPASSWORD = config.get('nopriv', 'imap_password') if IMAPPASSWORD == "": IMAPPASSWORD = getpass.getpass() IMAPFOLDER_ORIG = [ folder.strip() for folder in \ config.get('nopriv', 'imap_folder').split(',') \ if folder.strip() != "" ] yes_flags = ['true', 1, '1', 'True', 'yes', 'y', 'on'] ssl = False try: ssl_value = config.get('nopriv', 'ssl') if ssl_value in yes_flags: ssl = True except: pass incremental_backup = False try: incremental_value = config.get('nopriv', 'incremental_backup') if incremental_value in yes_flags: incremental_backup = True except: pass offline = False try: offline_value = config.get('nopriv', 'offline') if offline_value in yes_flags: offline = True except: pass enable_html = True CreateMailDir = True messages_per_overview_page = 50 inc_location = "inc" def connectToImapMailbox(IMAPSERVER, IMAPLOGIN, IMAPPASSWORD): if ssl is True: mail = imaplib.IMAP4_SSL(IMAPSERVER) if ssl is False: mail = imaplib.IMAP4(IMAPSERVER) mail.login(IMAPLOGIN, IMAPPASSWORD) return mail maildir = 'NoPrivMaildir' def returnHeader(title, inclocation=inc_location): response = """ %s
""" % (title, inclocation) return response def returnFooter(): response = """
""" return response lastfolder = "" def printQuote(): quotes = ['Come on, shut off that damn alarm and I promise I\'ll never violate you again.', 'I\'ve become romantically involved with a hologram. If that\'s possible.', 'Listen to me very carefully because I\'m only going to say this once. Coffee - black.', 'Computer, prepare to eject the warp core - authorization Torres omega five nine three!', 'The procedure is quite simple. I\'ll drill an opening into your skull percisely two milimeters in diameter and then use a neuralyte probe to extract a sample of your parietal lobe weighing approximately one gram'] return choice(quotes) class DecodeError(Exception): pass def decode_string(string): for charset in ("utf-8", 'latin-1', 'iso-8859-1', 'us-ascii', 'windows-1252','us-ascii'): try: return cgi.escape(unicode(string, charset)).encode('ascii', 'xmlcharrefreplace') except Exception: continue raise DecodeError("Could not decode string") attCount = 0 lastAttName = "" att_count = 0 last_att_filename = "" def saveToMaildir(msg, mailFolder): global lastfolder global maildir mbox = mailbox.Maildir(maildir, factory=mailbox.MaildirMessage, create=True) folder = mbox.add_folder(mailFolder) folder.lock() try: message_key = folder.add(msg) folder.flush() maildir_message = folder.get_message(message_key) try: message_date_epoch = time.mktime(parsedate(decode_header(maildir_message.get("Date"))[0][0])) except TypeError as typeerror: message_date_epoch = time.mktime([2000, 1, 1, 1, 1, 1, 1, 1, 0]) maildir_message.set_date(message_date_epoch) maildir_message.add_flag("s") finally: folder.unlock() folder.close() mbox.close() def saveMostRecentMailID(mail_id, email_address, folder, filename = "nopriv.txt"): match = False for line in fileinput.input(filename, inplace = 1): if line.split(":")[0] == folder and line.split(":")[1] == email_address and len(line) > 3: line = folder + ":" + email_address + ":" + str(mail_id) match = True if len(line) > 3 and line != "\n": print(line) fileinput.close() if match == False: with open(os.path.join(filename), 'a') as progress_file: progress_file.write(folder + ":" + email_address + ":" + str(mail_id)) progress_file.close() def getLastMailID(folder, email_address, filename = "nopriv.txt"): if not os.path.exists(filename): with open(os.path.join(filename), 'w') as progress_file: progress_file.write(folder + ":" + email_address + ":1") progress_file.close() match = False with open(os.path.join(filename), 'r') as progress_file: for line in progress_file: if len(line) > 3: latest_mailid = line.split(":")[2] email_addres_from_file = line.split(":")[1] folder_name = line.split(":")[0] if folder_name == folder and email_addres_from_file == email_address: progress_file.close() return latest_mailid return 0 progress_file.close() def get_messages_to_local_maildir(mailFolder, mail, startid = 1): global IMAPLOGIN mail.select(mailFolder, readonly=True) try: typ, mdata = mail.search(None, "ALL") except Exception as imaperror: print("Error in IMAP Query: %s." % imaperror) print("Does the imap folder \"%s\" exists?" % mailFolder) return total_messages_in_mailbox = len(mdata[0].split()) last_mail_id = 0 try: last_mail_id = mdata[0].split()[-1] except Exception: pass folder_most_recent_id = getLastMailID(mailFolder, IMAPLOGIN) if folder_most_recent_id > 2 and incremental_backup == True: if not int(folder_most_recent_id) == 1: startid = int(folder_most_recent_id) + 1 if startid == 0: startid = 1 for message_id in range(int(startid), int(total_messages_in_mailbox + 1)): result, data = mail.fetch(message_id , "(RFC822)") raw_email = data[0][1] print('Saving message %s.' % (message_id)) maildir_folder = mailFolder.replace("/", ".") saveToMaildir(raw_email, maildir_folder) if incremental_backup == True: saveMostRecentMailID(message_id, IMAPLOGIN, mailFolder) def returnIndexPage(): global IMAPFOLDER global IMAPLOGIN global IMAPSERVER global ssl global offline now = datetime.datetime.now() with open("index.html", "w") as indexFile: indexFile.write(returnHeader("Email Backup Overview Page")) indexFile.write("
\n") indexFile.write("

Folders

\n") indexFile.write(returnMenu("", index=True, vertical = True, activeItem="index")) indexFile.write("
\n") indexFile.write("
\n") indexFile.write("

Information

\n") indexFile.write("

This is your email backup. You've made it with ") indexFile.write("NoPriv.py from Raymii.org.
\n") indexFile.write("On the right you have the folders you wanted to backup.\n") indexFile.write("Click one to get the overview of that folder.
\n") indexFile.write("

\n
\n

\n") indexFile.write("Here is the information you gave me:
\n") indexFile.write("IMAP Server: " + IMAPSERVER + "
\n") indexFile.write("Username: " + IMAPLOGIN + "
\n") indexFile.write("Date of backup: " + str(now) + "
\n") indexFile.write("Folders to backup:
\n

    \n") for folder in IMAPFOLDER: indexFile.write("\t
  • " + folder + "
  • \n") indexFile.write("
\n") indexFile.write("
Available Folders:
") if not offline: indexFile.write(returnImapFolders(available=True, selected=False, html=True)) if ssl: indexFile.write("And, you've got a good mail provider, they support SSL and your backup was made over SSL.
\n") else: indexFile.write("No encrption was used when getting the emails.
\n") indexFile.write("Thats all folks, have a nice day!

\n") indexFile.write("
") indexFile.write(returnFooter()) indexFile.close() def allFolders(IMAPFOLDER_ORIG, mail): response = [] if len(IMAPFOLDER_ORIG) == 1 and IMAPFOLDER_ORIG[0] == "NoPriv_All": maillist = mail.list() for imapFolder in sorted(maillist[1]): imapFolder = re.sub(r"(?i)\(.*\)", "", imapFolder, flags=re.DOTALL) imapFolder = re.sub(r"(?i)\".\"", "", imapFolder, flags=re.DOTALL) imapFolder = re.sub(r"(?i)\"", "", imapFolder, flags=re.DOTALL) imapFolder = imapFolder.strip() response.append(imapFolder) else: response = IMAPFOLDER_ORIG return response def returnImapFolders(available=True, selected=True, html=False): response = "" if available: if not html: response += "Available IMAP4 folders:\n" maillist = mail.list() for ifo in sorted(maillist[1]): ifo = re.sub(r"(?i)\(.*\)", "", ifo, flags=re.DOTALL) ifo = re.sub(r"(?i)\".\"", "", ifo, flags=re.DOTALL) ifo = re.sub(r"(?i)\"", "", ifo, flags=re.DOTALL) if html: response += "- %s
\n" % ifo else: response += "- %s \n" % ifo response += "\n" if selected: if html: response += "Selected folders:
\n" else: response += "Selected folders:\n" for sfo in IMAPFOLDER: if html: response += "- %s
\n" % sfo else: response += "- %s \n" % sfo if html: response += "
\n" else: response += "\n" return response def returnMenu(folderImIn, inDate = False, index = False, vertical = False, activeItem = ""): global IMAPFOLDER folder_number = folderImIn.split('/') current_folder = folder_number folder_number = len(folder_number) dotdotslash = "" if vertical: response = '