1# Caolan McNamara caolanm@redhat.com
2# a simple email mailmerge component
3
4# manual installation for hackers, not necessary for users
5# cp mailmerge.py /usr/lib/openoffice.org2.0/program
6# cd /usr/lib/openoffice.org2.0/program
7# ./unopkg add --shared mailmerge.py
8# edit ~/.openoffice.org2/user/registry/data/org/openoffice/Office/Writer.xcu
9# and change EMailSupported to as follows...
10#  <prop oor:name="EMailSupported" oor:type="xs:boolean">
11#   <value>true</value>
12#  </prop>
13
14import unohelper
15import uno
16import re
17
18#to implement com::sun::star::mail::XMailServiceProvider
19#and
20#to implement com.sun.star.mail.XMailMessage
21
22from com.sun.star.mail import XMailServiceProvider
23from com.sun.star.mail import XMailService
24from com.sun.star.mail import XSmtpService
25from com.sun.star.mail import XConnectionListener
26from com.sun.star.mail import XAuthenticator
27from com.sun.star.mail import XMailMessage
28from com.sun.star.mail.MailServiceType import SMTP
29from com.sun.star.mail.MailServiceType import POP3
30from com.sun.star.mail.MailServiceType import IMAP
31from com.sun.star.uno import XCurrentContext
32from com.sun.star.lang import IllegalArgumentException
33from com.sun.star.lang import EventObject
34from com.sun.star.mail import SendMailMessageFailedException
35
36from email.MIMEBase import MIMEBase
37from email.Message import Message
38from email import Encoders
39from email.Header import Header
40from email.MIMEMultipart import MIMEMultipart
41from email.Utils import formatdate
42from email.Utils import parseaddr
43
44import sys, smtplib, imaplib, poplib
45
46dbg = False
47
48class PyMailSMTPService(unohelper.Base, XSmtpService):
49	def __init__( self, ctx ):
50		self.ctx = ctx
51		self.listeners = []
52		self.supportedtypes = ('Insecure', 'Ssl')
53		self.server = None
54		self.connectioncontext = None
55		self.notify = EventObject(self)
56		if dbg:
57			print >> sys.stderr, "PyMailSMPTService init"
58	def addConnectionListener(self, xListener):
59		if dbg:
60			print >> sys.stderr, "PyMailSMPTService addConnectionListener"
61		self.listeners.append(xListener)
62	def removeConnectionListener(self, xListener):
63		if dbg:
64			print >> sys.stderr, "PyMailSMPTService removeConnectionListener"
65		self.listeners.remove(xListener)
66	def getSupportedConnectionTypes(self):
67		if dbg:
68			print >> sys.stderr, "PyMailSMPTService getSupportedConnectionTypes"
69		return self.supportedtypes
70	def connect(self, xConnectionContext, xAuthenticator):
71		self.connectioncontext = xConnectionContext
72		if dbg:
73			print >> sys.stderr, "PyMailSMPTService connect"
74		server = xConnectionContext.getValueByName("ServerName")
75		if dbg:
76			print >> sys.stderr, server
77		port = xConnectionContext.getValueByName("Port")
78		if dbg:
79			print >> sys.stderr, port
80		self.server = smtplib.SMTP(server, port)
81		if dbg:
82			self.server.set_debuglevel(1)
83		connectiontype = xConnectionContext.getValueByName("ConnectionType")
84		if dbg:
85			print >> sys.stderr, connectiontype
86		if connectiontype.upper() == 'SSL':
87			self.server.ehlo()
88			self.server.starttls()
89			self.server.ehlo()
90
91		user = xAuthenticator.getUserName().encode('ascii')
92		password = xAuthenticator.getPassword().encode('ascii')
93		if user != '':
94			if dbg:
95				print >> sys.stderr, 'Logging in, username of', user
96			self.server.login(user, password)
97
98		for listener in self.listeners:
99			listener.connected(self.notify)
100	def disconnect(self):
101		if dbg:
102			print >> sys.stderr, "PyMailSMPTService disconnect"
103		if self.server:
104			self.server.quit()
105			self.server = None
106		for listener in self.listeners:
107			listener.disconnected(self.notify)
108	def isConnected(self):
109		if dbg:
110			print >> sys.stderr, "PyMailSMPTService isConnected"
111		return self.server != None
112	def getCurrentConnectionContext(self):
113		if dbg:
114			print >> sys.stderr, "PyMailSMPTService getCurrentConnectionContext"
115		return self.connectioncontext
116	def sendMailMessage(self, xMailMessage):
117		COMMASPACE = ', '
118
119		if dbg:
120			print >> sys.stderr, "PyMailSMPTService sendMailMessage"
121		recipients = xMailMessage.getRecipients()
122		sendermail = xMailMessage.SenderAddress
123		sendername = xMailMessage.SenderName
124		subject = xMailMessage.Subject
125		ccrecipients = xMailMessage.getCcRecipients()
126		bccrecipients = xMailMessage.getBccRecipients()
127		if dbg:
128			print >> sys.stderr, "PyMailSMPTService subject", subject
129			print >> sys.stderr, "PyMailSMPTService from", sendername.encode('utf-8')
130			print >> sys.stderr, "PyMailSMTPService from", sendermail
131			print >> sys.stderr, "PyMailSMPTService send to", recipients
132
133		attachments = xMailMessage.getAttachments()
134
135		textmsg = Message()
136
137		content = xMailMessage.Body
138		flavors = content.getTransferDataFlavors()
139		if dbg:
140			print >> sys.stderr, "PyMailSMPTService flavors len", len(flavors)
141
142		#Use first flavor that's sane for an email body
143		for flavor in flavors:
144			if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find('text/plain') != -1:
145				if dbg:
146					print >> sys.stderr, "PyMailSMPTService mimetype is", flavor.MimeType
147				textbody = content.getTransferData(flavor)
148				try:
149					textbody = textbody.value
150				except:
151					pass
152				textbody = textbody.encode('utf-8')
153
154				if len(textbody):
155					mimeEncoding = re.sub("charset=.*", "charset=UTF-8", flavor.MimeType)
156					if mimeEncoding.find('charset=UTF-8') == -1:
157						mimeEncoding = mimeEncoding + "; charset=UTF-8"
158					textmsg['Content-Type'] = mimeEncoding
159					textmsg['MIME-Version'] = '1.0'
160					textmsg.set_payload(textbody)
161
162				break
163
164		if (len(attachments)):
165			msg = MIMEMultipart()
166			msg.epilogue = ''
167			msg.attach(textmsg)
168		else:
169			msg = textmsg
170
171		hdr = Header(sendername, 'utf-8')
172		hdr.append('<'+sendermail+'>','us-ascii')
173		msg['Subject'] = subject
174		msg['From'] = hdr
175		msg['To'] = COMMASPACE.join(recipients)
176		if len(ccrecipients):
177			msg['Cc'] = COMMASPACE.join(ccrecipients)
178		if xMailMessage.ReplyToAddress != '':
179			msg['Reply-To'] = xMailMessage.ReplyToAddress
180
181		mailerstring = "OpenOffice.org 2.0 via Caolan's mailmerge component"
182		try:
183			ctx = uno.getComponentContext()
184			aConfigProvider = ctx.ServiceManager.createInstance("com.sun.star.configuration.ConfigurationProvider")
185			prop = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
186			prop.Name = "nodepath"
187			prop.Value = "/org.openoffice.Setup/Product"
188			aSettings = aConfigProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess",
189				(prop,))
190			mailerstring = aSettings.getByName("ooName") + " " + \
191				aSettings.getByName("ooSetupVersion") + " via Caolan's mailmerge component"
192		except:
193			pass
194
195		msg['X-Mailer'] = mailerstring
196		msg['Date'] = formatdate(localtime=True)
197
198		for attachment in attachments:
199			content = attachment.Data
200			flavors = content.getTransferDataFlavors()
201			flavor = flavors[0]
202			ctype = flavor.MimeType
203			maintype, subtype = ctype.split('/', 1)
204			msgattachment = MIMEBase(maintype, subtype)
205			data = content.getTransferData(flavor)
206			msgattachment.set_payload(data)
207			Encoders.encode_base64(msgattachment)
208			msgattachment.add_header('Content-Disposition', 'attachment', \
209				filename=('utf-8','',attachment.ReadableName.encode('utf-8')))
210			msg.attach(msgattachment)
211
212
213		uniquer = {}
214		for key in recipients:
215			uniquer[key] = True
216		if len(ccrecipients):
217			for key in ccrecipients:
218				uniquer[key] = True
219		if len(bccrecipients):
220			for key in bccrecipients:
221				uniquer[key] = True
222		truerecipients = uniquer.keys()
223
224		if dbg:
225			print >> sys.stderr, "PyMailSMPTService recipients are", truerecipients
226
227		self.server.sendmail(sendermail, truerecipients, msg.as_string())
228
229class PyMailIMAPService(unohelper.Base, XMailService):
230	def __init__( self, ctx ):
231		self.ctx = ctx
232		self.listeners = []
233		self.supportedtypes = ('Insecure', 'Ssl')
234		self.server = None
235		self.connectioncontext = None
236		self.notify = EventObject(self)
237		if dbg:
238			print >> sys.stderr, "PyMailIMAPService init"
239	def addConnectionListener(self, xListener):
240		if dbg:
241			print >> sys.stderr, "PyMailIMAPService addConnectionListener"
242		self.listeners.append(xListener)
243	def removeConnectionListener(self, xListener):
244		if dbg:
245			print >> sys.stderr, "PyMailIMAPService removeConnectionListener"
246		self.listeners.remove(xListener)
247	def getSupportedConnectionTypes(self):
248		if dbg:
249			print >> sys.stderr, "PyMailIMAPService getSupportedConnectionTypes"
250		return self.supportedtypes
251	def connect(self, xConnectionContext, xAuthenticator):
252		if dbg:
253			print >> sys.stderr, "PyMailIMAPService connect"
254
255		self.connectioncontext = xConnectionContext
256		server = xConnectionContext.getValueByName("ServerName")
257		if dbg:
258			print >> sys.stderr, server
259		port = xConnectionContext.getValueByName("Port")
260		if dbg:
261			print >> sys.stderr, port
262		connectiontype = xConnectionContext.getValueByName("ConnectionType")
263		if dbg:
264			print >> sys.stderr, connectiontype
265		print >> sys.stderr, "BEFORE"
266		if connectiontype.upper() == 'SSL':
267			self.server = imaplib.IMAP4_SSL(server, port)
268		else:
269			self.server = imaplib.IMAP4(server, port)
270		print >> sys.stderr, "AFTER"
271
272		user = xAuthenticator.getUserName().encode('ascii')
273		password = xAuthenticator.getPassword().encode('ascii')
274		if user != '':
275			if dbg:
276				print >> sys.stderr, 'Logging in, username of', user
277			self.server.login(user, password)
278
279		for listener in self.listeners:
280			listener.connected(self.notify)
281	def disconnect(self):
282		if dbg:
283			print >> sys.stderr, "PyMailIMAPService disconnect"
284		if self.server:
285			self.server.logout()
286			self.server = None
287		for listener in self.listeners:
288			listener.disconnected(self.notify)
289	def isConnected(self):
290		if dbg:
291			print >> sys.stderr, "PyMailIMAPService isConnected"
292		return self.server != None
293	def getCurrentConnectionContext(self):
294		if dbg:
295			print >> sys.stderr, "PyMailIMAPService getCurrentConnectionContext"
296		return self.connectioncontext
297
298class PyMailPOP3Service(unohelper.Base, XMailService):
299	def __init__( self, ctx ):
300		self.ctx = ctx
301		self.listeners = []
302		self.supportedtypes = ('Insecure', 'Ssl')
303		self.server = None
304		self.connectioncontext = None
305		self.notify = EventObject(self)
306		if dbg:
307			print >> sys.stderr, "PyMailPOP3Service init"
308	def addConnectionListener(self, xListener):
309		if dbg:
310			print >> sys.stderr, "PyMailPOP3Service addConnectionListener"
311		self.listeners.append(xListener)
312	def removeConnectionListener(self, xListener):
313		if dbg:
314			print >> sys.stderr, "PyMailPOP3Service removeConnectionListener"
315		self.listeners.remove(xListener)
316	def getSupportedConnectionTypes(self):
317		if dbg:
318			print >> sys.stderr, "PyMailPOP3Service getSupportedConnectionTypes"
319		return self.supportedtypes
320	def connect(self, xConnectionContext, xAuthenticator):
321		if dbg:
322			print >> sys.stderr, "PyMailPOP3Service connect"
323
324		self.connectioncontext = xConnectionContext
325		server = xConnectionContext.getValueByName("ServerName")
326		if dbg:
327			print >> sys.stderr, server
328		port = xConnectionContext.getValueByName("Port")
329		if dbg:
330			print >> sys.stderr, port
331		connectiontype = xConnectionContext.getValueByName("ConnectionType")
332		if dbg:
333			print >> sys.stderr, connectiontype
334		print >> sys.stderr, "BEFORE"
335		if connectiontype.upper() == 'SSL':
336			self.server = poplib.POP3_SSL(server, port)
337		else:
338			self.server = poplib.POP3(server, port)
339		print >> sys.stderr, "AFTER"
340
341		user = xAuthenticator.getUserName().encode('ascii')
342		password = xAuthenticator.getPassword().encode('ascii')
343		if dbg:
344			print >> sys.stderr, 'Logging in, username of', user
345		self.server.user(user)
346		self.server.pass_(password)
347
348		for listener in self.listeners:
349			listener.connected(self.notify)
350	def disconnect(self):
351		if dbg:
352			print >> sys.stderr, "PyMailPOP3Service disconnect"
353		if self.server:
354			self.server.quit()
355			self.server = None
356		for listener in self.listeners:
357			listener.disconnected(self.notify)
358	def isConnected(self):
359		if dbg:
360			print >> sys.stderr, "PyMailPOP3Service isConnected"
361		return self.server != None
362	def getCurrentConnectionContext(self):
363		if dbg:
364			print >> sys.stderr, "PyMailPOP3Service getCurrentConnectionContext"
365		return self.connectioncontext
366
367class PyMailServiceProvider(unohelper.Base, XMailServiceProvider):
368	def __init__( self, ctx ):
369		if dbg:
370			print >> sys.stderr, "PyMailServiceProvider init"
371		self.ctx = ctx
372	def create(self, aType):
373		if dbg:
374			print >> sys.stderr, "PyMailServiceProvider create with", aType
375		if aType == SMTP:
376			return PyMailSMTPService(self.ctx);
377		elif aType == POP3:
378			return PyMailPOP3Service(self.ctx);
379		elif aType == IMAP:
380			return PyMailIMAPService(self.ctx);
381		else:
382			print >> sys.stderr, "PyMailServiceProvider, unknown TYPE", aType
383
384class PyMailMessage(unohelper.Base, XMailMessage):
385	def __init__( self, ctx, sTo='', sFrom='', Subject='', Body=None, aMailAttachment=None ):
386		if dbg:
387			print >> sys.stderr, "PyMailMessage init"
388		self.ctx = ctx
389
390		self.recipients = [sTo]
391		self.ccrecipients = []
392		self.bccrecipients = []
393		self.aMailAttachments = []
394		if aMailAttachment != None:
395			self.aMailAttachments.append(aMailAttachment)
396
397		self.SenderName, self.SenderAddress = parseaddr(sFrom)
398		self.ReplyToAddress = sFrom
399		self.Subject = Subject
400		self.Body = Body
401		if dbg:
402			print >> sys.stderr, "post PyMailMessage init"
403	def addRecipient( self, recipient ):
404		if dbg:
405			print >> sys.stderr, "PyMailMessage.addRecipient", recipient
406		self.recipients.append(recipient)
407	def addCcRecipient( self, ccrecipient ):
408		if dbg:
409			print >> sys.stderr, "PyMailMessage.addCcRecipient", ccrecipient
410		self.ccrecipients.append(ccrecipient)
411	def addBccRecipient( self, bccrecipient ):
412		if dbg:
413			print >> sys.stderr, "PyMailMessage.addBccRecipient", bccrecipient
414		self.bccrecipients.append(bccrecipient)
415	def getRecipients( self ):
416		if dbg:
417			print >> sys.stderr, "PyMailMessage.getRecipients", self.recipients
418		return tuple(self.recipients)
419	def getCcRecipients( self ):
420		if dbg:
421			print >> sys.stderr, "PyMailMessage.getCcRecipients", self.ccrecipients
422		return tuple(self.ccrecipients)
423	def getBccRecipients( self ):
424		if dbg:
425			print >> sys.stderr, "PyMailMessage.getBccRecipients", self.bccrecipients
426		return tuple(self.bccrecipients)
427	def addAttachment( self, aMailAttachment ):
428		if dbg:
429			print >> sys.stderr, "PyMailMessage.addAttachment"
430		self.aMailAttachments.append(aMailAttachment)
431	def getAttachments( self ):
432		if dbg:
433			print >> sys.stderr, "PyMailMessage.getAttachments"
434		return tuple(self.aMailAttachments)
435
436
437# pythonloader looks for a static g_ImplementationHelper variable
438g_ImplementationHelper = unohelper.ImplementationHelper()
439g_ImplementationHelper.addImplementation( \
440	PyMailServiceProvider, "org.openoffice.pyuno.MailServiceProvider",
441		("com.sun.star.mail.MailServiceProvider",),)
442g_ImplementationHelper.addImplementation( \
443	PyMailMessage, "org.openoffice.pyuno.MailMessage",
444		("com.sun.star.mail.MailMessage",),)
445