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