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()
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 == '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=attachment.ReadableName)
210			msg.attach(msgattachment)
211
212		uniquer = {}
213		for key in recipients:
214			uniquer[key] = True
215		if len(ccrecipients):
216			for key in ccrecipients:
217				uniquer[key] = True
218		if len(bccrecipients):
219			for key in bccrecipients:
220				uniquer[key] = True
221		truerecipients = uniquer.keys()
222
223		if dbg:
224			print >> sys.stderr, "PyMailSMPTService recipients are", truerecipients
225
226		self.server.sendmail(sendermail, truerecipients, msg.as_string())
227
228class PyMailIMAPService(unohelper.Base, XMailService):
229	def __init__( self, ctx ):
230		self.ctx = ctx
231		self.listeners = []
232		self.supportedtypes = ('Insecure', 'Ssl')
233		self.server = None
234		self.connectioncontext = None
235		if dbg:
236			print >> sys.stderr, "PyMailIMAPService init"
237	def addConnectionListener(self, xListener):
238		if dbg:
239			print >> sys.stderr, "PyMailIMAPService addConnectionListener"
240		self.listeners.append(xListener)
241	def removeConnectionListener(self, xListener):
242		if dbg:
243			print >> sys.stderr, "PyMailIMAPService removeConnectionListener"
244		self.listeners.remove(xListener)
245	def getSupportedConnectionTypes(self):
246		if dbg:
247			print >> sys.stderr, "PyMailIMAPService getSupportedConnectionTypes"
248		return self.supportedtypes
249	def connect(self, xConnectionContext, xAuthenticator):
250		if dbg:
251			print >> sys.stderr, "PyMailIMAPService connect"
252
253		self.connectioncontext = xConnectionContext
254		server = xConnectionContext.getValueByName("ServerName")
255		if dbg:
256			print >> sys.stderr, server
257		port = xConnectionContext.getValueByName("Port")
258		if dbg:
259			print >> sys.stderr, port
260		connectiontype = xConnectionContext.getValueByName("ConnectionType")
261		if dbg:
262			print >> sys.stderr, connectiontype
263		print >> sys.stderr, "BEFORE"
264		if connectiontype == 'Ssl':
265			self.server = imaplib.IMAP4_SSL(server, port)
266		else:
267			self.server = imaplib.IMAP4(server, port)
268		print >> sys.stderr, "AFTER"
269
270		user = xAuthenticator.getUserName().encode('ascii')
271		password = xAuthenticator.getPassword().encode('ascii')
272		if user != '':
273			if dbg:
274				print >> sys.stderr, 'Logging in, username of', user
275			self.server.login(user, password)
276
277		for listener in self.listeners:
278			listener.connected(self.notify)
279	def disconnect(self):
280		if dbg:
281			print >> sys.stderr, "PyMailIMAPService disconnect"
282		if self.server:
283			self.server.logout()
284			self.server = None
285		for listener in self.listeners:
286			listener.disconnected(self.notify)
287	def isConnected(self):
288		if dbg:
289			print >> sys.stderr, "PyMailIMAPService isConnected"
290		return self.server != None
291	def getCurrentConnectionContext(self):
292		if dbg:
293			print >> sys.stderr, "PyMailIMAPService getCurrentConnectionContext"
294		return self.connectioncontext
295
296class PyMailPOP3Service(unohelper.Base, XMailService):
297	def __init__( self, ctx ):
298		self.ctx = ctx
299		self.listeners = []
300		self.supportedtypes = ('Insecure', 'Ssl')
301		self.server = None
302		self.connectioncontext = None
303		if dbg:
304			print >> sys.stderr, "PyMailPOP3Service init"
305	def addConnectionListener(self, xListener):
306		if dbg:
307			print >> sys.stderr, "PyMailPOP3Service addConnectionListener"
308		self.listeners.append(xListener)
309	def removeConnectionListener(self, xListener):
310		if dbg:
311			print >> sys.stderr, "PyMailPOP3Service removeConnectionListener"
312		self.listeners.remove(xListener)
313	def getSupportedConnectionTypes(self):
314		if dbg:
315			print >> sys.stderr, "PyMailPOP3Service getSupportedConnectionTypes"
316		return self.supportedtypes
317	def connect(self, xConnectionContext, xAuthenticator):
318		if dbg:
319			print >> sys.stderr, "PyMailPOP3Service connect"
320
321		self.connectioncontext = xConnectionContext
322		server = xConnectionContext.getValueByName("ServerName")
323		if dbg:
324			print >> sys.stderr, server
325		port = xConnectionContext.getValueByName("Port")
326		if dbg:
327			print >> sys.stderr, port
328		connectiontype = xConnectionContext.getValueByName("ConnectionType")
329		if dbg:
330			print >> sys.stderr, connectiontype
331		print >> sys.stderr, "BEFORE"
332		if connectiontype == 'Ssl':
333			self.server = poplib.POP3_SSL(server, port)
334		else:
335			self.server = poplib.POP3(server, port)
336		print >> sys.stderr, "AFTER"
337
338		user = xAuthenticator.getUserName().encode('ascii')
339		password = xAuthenticator.getPassword().encode('ascii')
340		if dbg:
341			print >> sys.stderr, 'Logging in, username of', user
342		self.server.user(user)
343		self.server.pass_(user, password)
344
345		for listener in self.listeners:
346			listener.connected(self.notify)
347	def disconnect(self):
348		if dbg:
349			print >> sys.stderr, "PyMailPOP3Service disconnect"
350		if self.server:
351			self.server.quit()
352			self.server = None
353		for listener in self.listeners:
354			listener.disconnected(self.notify)
355	def isConnected(self):
356		if dbg:
357			print >> sys.stderr, "PyMailPOP3Service isConnected"
358		return self.server != None
359	def getCurrentConnectionContext(self):
360		if dbg:
361			print >> sys.stderr, "PyMailPOP3Service getCurrentConnectionContext"
362		return self.connectioncontext
363
364class PyMailServiceProvider(unohelper.Base, XMailServiceProvider):
365	def __init__( self, ctx ):
366		if dbg:
367			print >> sys.stderr, "PyMailServiceProvider init"
368		self.ctx = ctx
369	def create(self, aType):
370		if dbg:
371			print >> sys.stderr, "PyMailServiceProvider create with", aType
372		if aType == SMTP:
373			return PyMailSMTPService(self.ctx);
374		elif aType == POP3:
375			return PyMailPOP3Service(self.ctx);
376		elif aType == IMAP:
377			return PyMailIMAPService(self.ctx);
378		else:
379			print >> sys.stderr, "PyMailServiceProvider, unknown TYPE", aType
380
381class PyMailMessage(unohelper.Base, XMailMessage):
382	def __init__( self, ctx, sTo='', sFrom='', Subject='', Body=None, aMailAttachment=None ):
383		if dbg:
384			print >> sys.stderr, "PyMailMessage init"
385		self.ctx = ctx
386
387		self.recipients = sTo,
388		self.ccrecipients = ()
389		self.bccrecipients = ()
390		self.aMailAttachments = ()
391		if aMailAttachment != None:
392			self.aMailAttachments = aMailAttachment,
393
394		self.SenderName, self.SenderAddress = parseaddr(sFrom)
395		self.ReplyToAddress = sFrom
396		self.Subject = Subject
397		self.Body = Body
398		if dbg:
399			print >> sys.stderr, "post PyMailMessage init"
400	def addRecipient( self, recipient ):
401		if dbg:
402			print >> sys.stderr, "PyMailMessage.addRecipient", recipient
403		self.recipients = self.recipients, recipient
404	def addCcRecipient( self, ccrecipient ):
405		if dbg:
406			print >> sys.stderr, "PyMailMessage.addCcRecipient", ccrecipient
407		self.ccrecipients = self.ccrecipients, ccrecipient
408	def addBccRecipient( self, bccrecipient ):
409		if dbg:
410			print >> sys.stderr, "PyMailMessage.addBccRecipient", bccrecipient
411		self.bccrecipients = self.bccrecipients, bccrecipient
412	def getRecipients( self ):
413		if dbg:
414			print >> sys.stderr, "PyMailMessage.getRecipients", self.recipients
415		return self.recipients
416	def getCcRecipients( self ):
417		if dbg:
418			print >> sys.stderr, "PyMailMessage.getCcRecipients", self.ccrecipients
419		return self.ccrecipients
420	def getBccRecipients( self ):
421		if dbg:
422			print >> sys.stderr, "PyMailMessage.getBccRecipients", self.bccrecipients
423		return self.bccrecipients
424	def addAttachment( self, aMailAttachment ):
425		if dbg:
426			print >> sys.stderr, "PyMailMessage.addAttachment"
427		self.aMailAttachments = self.aMailAttachments, aMailAttachment
428	def getAttachments( self ):
429		if dbg:
430			print >> sys.stderr, "PyMailMessage.getAttachments"
431		return self.aMailAttachments
432
433# pythonloader looks for a static g_ImplementationHelper variable
434g_ImplementationHelper = unohelper.ImplementationHelper()
435g_ImplementationHelper.addImplementation( \
436	PyMailServiceProvider, "org.openoffice.pyuno.MailServiceProvider",
437		("com.sun.star.mail.MailServiceProvider",),)
438g_ImplementationHelper.addImplementation( \
439	PyMailMessage, "org.openoffice.pyuno.MailMessage",
440		("com.sun.star.mail.MailMessage",),)
441