cyu
10/8/2011 - 3:48 PM

Importing Mail to GMail With GML - http://blog.sourcebender.com/2006/07/11/importing-mail-to-gmail-with-gml.html

#!/usr/bin/env python
#
# This is a modified version of the Gmail Loader application which can be
#   here: http://www.marklyon.org/gmail/download.htm
#
# CHANGES:
#   * Changed the default SMTP server to smtp.gmail.com (the original one seems
#     to be no longer active).
#   * Added support for SMTP authentication.
#   * Added port number argument for GMail SMTP server.
#   * Added support SSL (TLS) authentication for GMail SMTP server.
#
#
# Copyright (C) 2004 Mark H. Lyon <mark@marklyon.org>
#
# This file is the Mbox & Maildir to Gmail Loader (GML).
#
# Mbox & Maildir to Gmail Loader (GML) 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 2 of
# the License, or (at your option) any later version.
#
# GML 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 GML; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# This entire header must remain intact in any distributed copy.

# Origional development thread at Ars Technica:
# http://episteme.arstechnica.com/eve/ubb.x?a=tpc&s=50009562&f=6330927813&m=108000474631
#
# Version 0.1 - 15 Jun 04 16:28 Supports Mbox
# Version 0.2 - 15 Jun 04 18:48 Implementing Magus` suggestion for Maildir
# Version 0.3 - 16 Jun 04 16:17 Implement Rold Gold suggestion for counters
# Version 0.4 - 17 Jun 04 13:15 Add support for changing SMTP server at command line
# Version 0.5 - 25 Jun 04 16:00 Implemented GUI and added support for "Sent" mail
#                               submitted to me by Steven Scott <progoth@gmail.com>
#                               Special Thanks to: Buddy Fortner, Brian Hodgin,
#                               Jamie Zawinski, The Youngblood Brass Band, Diet Pepsi,
#                               and many others for helping in their own way.
#
#                               Some of the GUI widgets come from Pmw, which can be
#                               downloaded at http://pmw.sourceforge.net/
# Version .5b - 30 Jun 04 13:00 Update to solve the missing os import bug reported
#                               by users trying maildir.


import os, sys, mailbox, smtplib, time, Pmw, webbrowser, tkFileDialog, re
from Tkinter import *
#from webbrowser import open_new
#from Pmw import ComboBox, ScrolledText, logicalfont
#from tkFileDialog import askopenfilename


class GmlWin:

   def __init__(self, parent=None):

      self.version = '0.5 - 25 Jun 04 16:00'

      # Let's get everything initilized
      self.root = Tk()
      self.boxtype = ''            # Stores the current box type
      self.rcvorsn = ''  	   # Stores the current recieve or send status
      self.mb      = ''            # The opened mailbox
      self.msg     = ''            # The Message
      self.document= ''            # The Message Body
      self.needath = IntVar()      # 0 no authentication,  1 use authentication
      self.mailbox = ''            # Path to message store
      self.recipnt = ''            # Gmail account to forward to
      self.smtpsvr = ''            # SMTP Server to use
      self.smtpusr = ''            # SMTP Username, required if needath = 1
      self.smtppwd = ''            # SMTP Password, required if needath = 1
      self.running = 0		   # Flag for great justice (0 clear, 1 running, 2 error)
      self.count   = [0,0,0]       # Counters

      self.boxtyps = ('mBox (Netscape, Mozilla, Thunderbird)', 'mBox (Less Strict - Solves Some Problems)',
                      'MailDir (Qmail, others)', 'MMDF (Mutt, SCO Unix)', 'MH (NMH Message System)',
                      'Babyl (Emacs RMAIL)')
      self.rcvtyps = ('Mail I Recieved (Goes to Inbox)', 'Mail I Sent (Goes to Sent Mail)')

      self.main_screen()
      self.root.title('Google Mail Loader')
      mainloop()

   def main_screen(self):

      # Setup Step Labels
      Label(self.root, text="Setup SMTP Server Settings", font=("Helvetica", 10, 'bold'), relief=GROOVE) \
         .grid(	row=0, 	column=0, columnspan=5, sticky=W+E, padx=5, pady=5)
      Label(self.root, text="Configure Your Email File",  font=("Helvetica", 10, 'bold'), relief=GROOVE) \
         .grid(	row=8, 	column=0, columnspan=5, sticky=W+E, padx=5, pady=5)
      Label(self.root, text="Enter Your GMail Address",   font=("Helvetica", 10, 'bold'), relief=GROOVE) \
         .grid(	row=13, column=0, columnspan=5, sticky=W+E, padx=5, pady=5)

      # SMTP Settings
      Label(self.root, text="SMTP Server:").grid(row=1, column=0, sticky=E)
      self.smtpsvr = Entry(self.root); self.smtpsvr.insert(0, 'smtp.gmail.com');
      self.smtpsvr.grid(row=1, column=1, columnspan=3, sticky=W+E)
      Checkbutton(self.root, text="Requires Authentication", variable=self.needath, command=self.NeedAuth) \
         .grid(row=2, column=1, columnspan=3, sticky=W)
      Label(self.root, text="Username:").grid(row=3, column=0, sticky=E)
      Label(self.root, text="Password:").grid(row=4, column=0, sticky=E)
      self.smtpusr = Entry(self.root, state=DISABLED);
      self.smtpusr.grid(row=3, column=1, columnspan=3, sticky=W+E)
      self.smtppwd = Entry(self.root, state=DISABLED);
      self.smtppwd.grid(row=4, column=1, columnspan=3, sticky=W+E)
      Label(self.root, text=" ").grid(row=5, column=0, sticky=E)

      # Message Store Settings
      self.mailbox = Entry(self.root); self.mailbox.grid(row=9, column=0, columnspan=3, sticky=W+E)
      Button(self.root, text="Find", command=self.FindFile).grid(row=9, column=3, sticky=W+E)
      self.boxtype = Pmw.ComboBox(self.root, label_text = 'File Type:', labelpos = 'nw',
                                  scrolledlist_items = self.boxtyps)
      self.boxtype.selectitem(self.boxtyps[0])
      self.boxtype.grid(row=10, column=0, columnspan=4, sticky=W+E)
      self.rcvorsn = Pmw.ComboBox(self.root, label_text = 'Message Type:', labelpos = 'nw',
                                  scrolledlist_items = self.rcvtyps)
      self.rcvorsn.selectitem(self.rcvtyps[0])
      self.rcvorsn.grid(row=11, column=0, columnspan=4, sticky=W+E)
      Label(self.root, text=" ").grid(row=12, column=0, sticky=E)

      # GMail Account Information
      self.recipnt = Entry(self.root); self.recipnt.insert(0, '@gmail.com');
      self.recipnt.grid(row=14, column=0, columnspan=4, sticky=W+E)
      Label(self.root, text=" ").grid(row=15, column=0, sticky=E)

      # Execute and About
      Button(self.root, text="Send To GMail", command=self.execute) \
         .grid(row=16, column=0, columnspan=2, sticky=W+E)
      Button(self.root, text="Save Log", command=self.saveText) \
         .grid(row=16, column=2, sticky=E)
      Button(self.root, text="About", command=self.openAbout) \
         .grid(row=16, column=3, sticky=E)

      # Output Window
      self.st = Pmw.ScrolledText(self.root, borderframe = 1,
                                 usehullsize = 1, hull_width = 375,
                                 hull_height = 375, text_padx = 10,
                                 text_pady = 10, text_wrap='word',
      		                 hscrollmode='dynamic', vscrollmode='static',
      		                 text_font = Pmw.logicalfont(name='Fixed', size=12))
      self.st.grid(row=0, column=6, sticky=NW, rowspan=17, padx=10, pady=5)

      self.writeHeader()
      self.writeInstructions()


    # Tests to see if the SMTP Authentication option's status,
    # and enables/disables the text boxes accordingly
   def NeedAuth(self):
      if self.needath.get():
         self.smtpusr.config( state = NORMAL )
         self.smtppwd.config( state = NORMAL )
      else:
         self.smtpusr.config( state = DISABLED )
         self.smtppwd.config( state = DISABLED )

    # Test for a correct file using the 'Find' button
    # and populates the text box
   def FindFile(self):
      FileName = tkFileDialog.askopenfilename()
      if (FileName==None or FileName==""):
         return
      self.mailbox.delete(0, END)
      self.mailbox.insert(0, FileName)

    # Opens a new browser window directed at the gml page.
   def openAbout(self):
      webbrowser.open_new('http://www.marklyon.org/gmail')

    # The header and credits
   def writeHeader(self):
      self.st.clear()
      self.Disp("Gmail Loader (GML)")
      self.Disp("By Mark Lyon <mark@marklyon.org>")
      self.Disp("Version: " , self.version)
      self.Disp()

    # Some simple usage information
   def writeInstructions(self):
      self.Disp("To use, complete the options to the left of this message:")
      self.Disp("1) Most users can connect to Google's SMTP server, but you may " \
                "specify your own SMTP server if you desire.")
      self.Disp("2) Locate your email file.  If you are using a MailDir, locate " \
                "one file inside your MailDir.")
      self.Disp("3) Choose the correct file type.  There are two versions of " \
                "mBox.  Use the 'Less Strict' if you have problems with the other.")
      self.Disp("4) Select where you would like your messages to go in Gmail, " \
                "either 'Inbox' or 'Sent Mail'.")
      self.Disp("5) Enter your complete GMail address, and click 'Send To GMail'.")


    # Write to the window or console
   def Disp(self, *message):
      for txt in message:
        self.st.appendtext(txt)
      self.st.appendtext('\n')
      self.root.update()

    # Save the text in the window
   def saveText(self):
      try:
         filename = tkFileDialog.asksaveasfilename()
         if filename == None: return
         self.st.exportfile(filename)
      except:
         self.Disp()
         self.Disp("*** UNABLE TO SAVE LOG FILE.  ERROR FOLLOWS.")
         self.appendTrace()


    # Since we'd like a little more information about errors, append that bad boy.
   def appendTrace(self):
      self.Disp("Error Type: " , sys.exc_info()[0])
      self.Disp("Error Val : " , sys.exc_info()[1])

    # Oprn the mailbox (external function)
   def selectBox(self):
      try:
         self._selectBox()
         self.Disp()
      except:
         self.Disp("*** UNABLE TO OPEN MESSAGE FILE.  ERROR FOLLOWS.")
         self.appendTrace()

    # Open the mailbox (internal function)
   def _selectBox(self):
       # mBox Strict
      if self.boxtype.get() == self.boxtyps[0]:
         self.mb = mailbox.UnixMailbox (file(self.mailbox.get(),'r'))
         self.Disp(self.boxtype.get(), " at location " ,self.mailbox.get(), " Opened Successfully.")
       # mBox Loose
      elif self.boxtype.get() == self.boxtyps[1]:
         self.mb = mailbox.PortableUnixMailbox(file(self.mailbox.get(),'r'))
         self.Disp(self.boxtype.get(), " at location ", self.mailbox.get(), " Opened Successfully.")
       # MailDir
      elif self.boxtype.get() == self.boxtyps[2]:
         self.mb = mailbox.Maildir(os.path.dirname(self.mailbox.get()))
         self.Disp(self.boxtype.get(), " at location ",os.path.dirname(self.mailbox.get()), " Opened Successfully.")
       # MMDF
      elif self.boxtype.get() == self.boxtyps[3]:
         self.mb = mailbox.MmdfMailbox(file(self.mailbox.get(),'r'))
         self.Disp(self.boxtype.get(), " at location ", self.mailbox.get(), " Opened Successfully.")
       # MH
      elif self.boxtype.get() == self.boxtyps[4]:
         self.mb = mailbox.MHMailbox(file(self.mailbox.get(),'r'))
         self.Disp(self.boxtype.get(), " at location ", self.mailbox.get(), " Opened Successfully.")
       # Babyl
      elif self.boxtype.get() == self.boxtyps[5]:
         self.mb = mailbox.BabylMailbox(file(self.mailbox.get(),'r'))
         self.Disp(self.boxtype.get(), " at location ", self.mailbox.get(), " Opened Successfully.")
       #Unknown File Type
      else:
         self.Disp("*** I don't know about that file type.")
         self.running = 2

    # Feed the Beast (external)
   def process(self):
      try:
         self._process()
      except:
         self.Disp("*** CANNOT READ MESSAGES.  ERROR FOLLOWS.")
         self.appendTrace()

    # Feed the Beast (internal)
   def _process(self):
      self.msg = self.mb.next()
      while self.msg is not None:
         self.processDocument()
         self.msg = self.mb.next()
      self.finishUp()

    # Read Document and then Send It (external)
   def processDocument(self):
      try:
         self._processDocument()
      except:
         self.count[2] = self.count[2] + 1
         self.Disp("*** ", self.count[2] ," MESSAGE READ FAILED, MAY BE MALFORMED.  ERROR FOLLOWS.")
         self.appendTrace()

    # Read Document and then Send It (internal)
   def _processDocument(self):
      self.document = self.msg.fp.read()
      if self.document is not None:
         self.sendMessage()

    # Send a Message (external)
   def sendMessage(self):
      try:
         self._sendMessage()
         self.count[0] = self.count[0] + 1
         self.Disp(self.count[0] ," Forwarded a message from: ", self.msg.getaddr('From')[1])
      except:
         self.count[1] = self.count[1] + 1
         self.Disp("*** ", self.count[1] ," ERROR SENDING MESSAGE FROM: ", self.msg.getaddr('From')[1])
         self.Disp("*** UNABLE TO CONNECT TO SERVER OR SEND MESSAGE. ERROR FOLLOWS.")
         self.appendTrace()

    # Send a Message (Internal)
   def _sendMessage(self):
      time.sleep(2)

      if self.smtpsvr.get() == 'smtp.gmail.com':
        server = smtplib.SMTP(self.smtpsvr.get(), 587)
      else:
        server = smtplib.SMTP(self.smtpsvr.get())

      server.set_debuglevel(1)

      if self.smtpsvr.get() == 'smtp.gmail.com':
        server.ehlo()
        server.starttls()

      if self.needath.get():
         server.ehlo()
         server.login(self.smtpusr.get(), self.smtppwd.get())
      
      if self.rcvorsn.get() == self.rcvtyps[1]:
         fullmsg = re.sub(r'From: .*', 'From: %s' % self.recipnt.get(),
                          self.msg.__str__( ) + '\x0a' + self.document, 1 )
         server.sendmail(self.recipnt.get(), self.recipnt.get(), fullmsg)
      else:
         fullmsg = self.msg.__str__( ) + '\x0a' + self.document
         server.sendmail(self.msg.getaddr('From')[1], self.recipnt.get(), fullmsg)
      server.quit()

    # Prints the stat information
   def finishUp(self):
      self.Disp()
      self.Disp("Done. Stats: ", self.count[0] ," success ", self.count[1] ," error ", self.count[2] ," skipped.")
      self.running = 0
      self.count = [0,0,0]

    # Performs some last minute testing.
   def execute(self):

      if self.running: return
      else:            self.running = 1

      self.writeHeader()

      if not self.mailbox.get():
         self.Disp("*** You didn't specify a message store.")
         self.running = 2
      if not self.recipnt.get():
         self.Disp("*** You didn't specify a recipient.")
         self.running = 2
      if not self.smtpsvr.get():
         self.Disp("*** You didn't specify a SMTP server.")
         self.running = 2
      if self.needath.get():
         if not self.smtpusr.get():
            self.Disp("*** You didn't specify a SMTP username.")
            self.running = 2
         if not self.smtppwd.get():
            self.Disp("*** You didn't specify a SMTP password.")
            self.running = 2


      self.selectBox()
      self.process()

      if self.running == 2:
         self.running = 0
         self.Disp()
         self.writeInstructions()
         return

      print "Sending messages from", self.mailbox.get(), "to", self.recipnt.get(),
      if self.boxtype.get() == self.boxtyps[2]: print "in Maildir Format",
      else:                  		  print "in mBox Format",
      if self.rcvorsn.get() == self.rcvtyps[1]: print "for sent messages (sender will be rewritten)",
      else:                               print "for recieved messages (sender not rewritten)",
      if self.needath.get(): print "from", self.smtpsvr.get(), "using the username", self.smtpusr.get(), "and password", self.smtppwd.get() + "."
      else:                  print "from", self.smtpsvr.get(), "without authentication."

      # Sometimes you want to see what's in a combo box.  Do this.
      #print self.boxtype.get()


if __name__ == '__main__':

   GmlWin()