loops/agent/crawl/outlook.py
tschmid e5eeb2d502 Outlook dialog handle added
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2067 fd906abe-77d9-0310-91a1-e0d9ade77398
2007-09-23 18:12:34 +00:00

321 lines
12 KiB
Python

"""
This module reads out information from Microsoft Outlook.
The function loadInbox() reads all Emails of MsOutlook-inbox folder,
optionally it is possible to read the subfolder of the inbox too.
The emails will be returnes as Python MIME objects in a list.
Tobias Schmid 26.07.2007
"""
import win32com.client
import re
from email.mime.multipart import MIMEMultipart
from threading import Thread
# imports for handling the outlook dialog
import ctypes
import win32api, win32process, win32con
from watsup.winGuiAuto import findTopWindow, findControl, findControls, clickButton, \
getComboboxItems, selectComboboxItem, setCheckBox
from twisted.internet.defer import Deferred
from twisted.internet.task import coiterate
from zope.interface import implements
from loops.agent.interfaces import IResource
from loops.agent.crawl.base import CrawlingJob as BaseCrawlingJob
from loops.agent.crawl.base import Metadata
# DEBUG FLAGS
DEBUG = 1
DEBUG_WRITELINE = 1
# some constants
COMMASPACE = ', '
class CrawlingJob(BaseCrawlingJob):
keys = ""
inbox = ""
subfolders = ""
pattern = ""
def collect(self):
self.collected = []
coiterate(self.crawlOutlook()).addCallback(self.finished)
# TODO: addErrback()
self.deferred = Deferred()
return self.deferred
def finished(self, result):
self.deferred.callback(self.collected)
def crawlOutlook(self):
outlookFound = 0
try:
oOutlookApp = \
win32com.client.gencache.EnsureDispatch("Outlook.Application")
outlookFound = 1
except:
print "MSOutlook: unable to load Outlook"
records = []
if not outlookFound:
return
# fetch the params
criteria = self.params
self.keys = criteria.get('keys')
self.inbox = criteria.get('inbox') #boolean
self.subfolders = criteria.get('subfolders') #boolean
self.pattern = criteria.get('pattern')
if self.pattern != '':
self.pattern = re.compile(criteria.get('pattern') or '.*')
else:
self.pattern = None
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> starting'
# try to handle the Outlook dialog
handle = HandleOutlookDialog()
handle.start()
# catch Inbox folder
onMAPI = oOutlookApp.GetNamespace("MAPI")
ofInbox = \
onMAPI.GetDefaultFolder(win32com.client.constants.olFolderInbox)
# fetch the mails of the inbox folder
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> fetch mails of inbox folder'
# fetch mails from inbox
if self.inbox:
self.loadEmail(ofInbox)
# fetch mails of inbox subfolders
if self.subfolders and self.pattern is None:
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> fetch emails of subfolders'
lInboxSubfolders = getattr(ofInbox, 'Folders')
for of in range(lInboxSubfolders.__len__()):
# get a MAPI-folder object and load its emails
self.loadEmail(lInboxSubfolders.Item(of + 1))
# pattern, just read the specified subfolder
elif self.subfolders and self.pattern:
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> fetch emails of specified subfolder'
lInboxSubfolders = getattr(ofInbox, 'Folders')
for of in range(lInboxSubfolders.__len__()):
# get a MAPI-folder object and load its emails
if self.pattern.match(getattr(lInboxSubfolders.Item(of + 1), 'Name')):
self.loadEmail(lInboxSubfolders.Item(of + 1)) #oFolder
if DEBUG:
print 'number of mails in Inbox:', len(ofInbox.Items)
# list of _Folder (subfolder of inbox)
lInboxSubfolders = getattr(ofInbox, 'Folders')
# get Count-Attribute of _Folders class
iInboxSubfoldersCount = getattr(lInboxSubfolders, 'Count')
# the Item-Method of the _Folders class returns a MAPIFolder object
oFolder = lInboxSubfolders.Item(1)
print 'Count of Inbox-SubFolders:', iInboxSubfoldersCount
print 'Inbox sub folders (Folder/Mails):'
for folder in range(iInboxSubfoldersCount):
oFolder = lInboxSubfolders.Item(folder+1)
print getattr(oFolder, 'Name'), '/' , len(getattr(oFolder, 'Items'))
if DEBUG_WRITELINE:
print 'MSOutlook.loadInbox() ===> ending'
yield '1'
def loadEmail(self, oFolder):
# get items of the folder
folderItems = getattr(oFolder, 'Items')
for item in range(len(folderItems)):
mail = folderItems.Item(item+1)
if mail.Class == win32com.client.constants.olMail:
if self.keys is None:
self.keys = []
for key in mail._prop_map_get_:
if isinstance(getattr(mail, key), (int, str, unicode)):
self.keys.append(key)
if DEBUG:
self.keys.sort()
print 'Fiels\n======================================='
for key in self.keys:
print key
record = {}
for key in self.keys:
record[key] = getattr(mail, key)
if DEBUG:
print str(item)
# Create the mime email object
msg = self.createEmailMime(record)
# list with mime objects
self.collected.append((OutlookResource(msg)))
def createEmailMime(self, emails):
# Create the container (outer) email message.
msg = MIMEMultipart()
# subject
msg['Subject'] = emails['Subject'].encode('utf-8')
# sender
if emails.has_key('SenderEmailAddress'):
sender = str(emails['SenderEmailAddress'].encode('utf-8'))
else:
sender = str(emails['SenderName'].encode('utf-8'))
msg['From'] = sender
# CC
#msg['CC'] = str(emails['CC'].encode('utf-8'))
# ReceivedTime
#msg['Date'] = str(emails['ReceivedTime'])
#recipients
recipients = []
if emails.has_key('Recipients'):
for rec in range(emails['Recipients'].__len__()):
recipients.append(getattr(emails['Recipients'].Item(rec+1), 'Address'))
msg['To'] = COMMASPACE.join(recipients)
else:
recipients.append(emails['To'])
msg['To'] = COMMASPACE.join(recipients)
# message
msg.preamble = emails['Body'].encode('utf-8')
return msg
def handleOutlookDialog(self):
"""
This function handles the outlook dialog, which appears if someone
tries to access to MS Outlook.
"""
hwnd = None
while True:
hwnd = ctypes.windll.user32.FindWindowExA(None, hwnd, None, None)
print 'searching....'
if hwnd == None:
break
else:
val = u"\0" * 1024
ctypes.windll.user32.GetWindowTextW(hwnd, val, len(val))
val = val.replace(u"\000", u"")
if val and repr(val) == "u'Microsoft Office Outlook'":
print repr(val)
print '===> MSOutlook dialog box found'
#tid, pid = win32process.GetWindowThreadProcessId(hwnd)
# get a handle
#handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 0, pid)
# terminate the process by the handle
#win32api.TerminateProcess(handle, 0)
# close the handle - thankyou
#win32api.CloseHandle(handle)
# get the Main Control
form = findTopWindow(wantedText='Microsoft Office Outlook')
controls = findControls(form)
# get the check box
checkBox = findControl(form, wantedText='Zugriff')
setCheckBox(checkBox, 1)
# get the combo box
comboBox = findControl(form, wantedClass='ComboBox')
items = getComboboxItems(comboBox)
selectComboboxItem(comboBox, items[3])#'10 Minuten'
# finally get the button and click it
button = findControl(form, wantedText = 'Erteilen')
clickButton(button)
print '-> dialog found and handled'
break
class HandleOutlookDialog(Thread):
def __init_(self):
Thread.__init__(self)
def run(self):
hwnd = None
while True:
hwnd = ctypes.windll.user32.FindWindowExA(None, hwnd, None, None)
#print 'searching....'
if hwnd == None:
break
else:
val = u"\0" * 1024
ctypes.windll.user32.GetWindowTextW(hwnd, val, len(val))
val = val.replace(u"\000", u"")
if val and repr(val) == "u'Microsoft Office Outlook'":
print repr(val)
print '===> MSOutlook dialog box found'
# get the Main Control
form = findTopWindow(wantedText='Microsoft Office Outlook')
#print 'Control', form
controls = findControls(form)
#print 'Controls', str(controls)
# get the check box
checkBox = findControl(form, wantedText='Zugriff')
#print 'CheckBox', checkBox
setCheckBox(checkBox, 1)
# get the combo box
comboBox = findControl(form, wantedClass='ComboBox')
#print 'ComboBox', comboBox
items = getComboboxItems(comboBox)
#print 'ComboBox-Items', str(items)
selectComboboxItem(comboBox, items[1])#'10 Minuten'
# finally get the button and click it
button = findControl(form, wantedText = 'Erteilen')
print 'Erteilen Button', button
clickButton(button)
print '-> dialog found and handled'
break
class OutlookResource(object):
implements(IResource)
def __init__(self, oEmail):
self.oEmail = oEmail
@property
def data(self):
return self.oEmail