bump product version to 4.1.6.2
[LibreOffice.git] / scripting / source / pyprov / mailmerge.py
blob3cfb6d3175ebe4a31f7dd2540a5885ddf4218ecd
1 # Caolan McNamara caolanm@redhat.com
2 # a simple email mailmerge component
4 # manual installation for hackers, not necessary for users
5 # cp mailmerge.py /usr/lib/libreoffice/program
6 # cd /usr/lib/libreoffice/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>
14 from __future__ import print_function
16 import unohelper
17 import uno
18 import re
19 import os
21 #to implement com::sun::star::mail::XMailServiceProvider
22 #and
23 #to implement com.sun.star.mail.XMailMessage
25 from com.sun.star.mail import XMailServiceProvider
26 from com.sun.star.mail import XMailService
27 from com.sun.star.mail import XSmtpService
28 from com.sun.star.mail import XConnectionListener
29 from com.sun.star.mail import XAuthenticator
30 from com.sun.star.mail import XMailMessage
31 from com.sun.star.mail.MailServiceType import SMTP
32 from com.sun.star.mail.MailServiceType import POP3
33 from com.sun.star.mail.MailServiceType import IMAP
34 from com.sun.star.uno import XCurrentContext
35 from com.sun.star.lang import IllegalArgumentException
36 from com.sun.star.lang import EventObject
37 from com.sun.star.mail import SendMailMessageFailedException
39 from email.mime.base import MIMEBase
40 from email.message import Message
41 from email.charset import Charset
42 from email.charset import QP
43 from email.encoders import encode_base64
44 from email.header import Header
45 from email.mime.multipart import MIMEMultipart
46 from email.utils import formatdate
47 from email.utils import parseaddr
48 from socket import _GLOBAL_DEFAULT_TIMEOUT
50 import sys, smtplib, imaplib, poplib
51 dbg = False
53 #no stderr under windows, output to pymailmerge.log
54 #with no buffering
55 if dbg and os.name == 'nt':
56 dbgout = open('pymailmerge.log', 'w', 0)
57 else:
58 dbgout = sys.stderr
60 class PyMailSMTPService(unohelper.Base, XSmtpService):
61 def __init__( self, ctx ):
62 self.ctx = ctx
63 self.listeners = []
64 self.supportedtypes = ('Insecure', 'Ssl')
65 self.server = None
66 self.connectioncontext = None
67 self.notify = EventObject(self)
68 if dbg:
69 print("PyMailSMTPService init", file=dbgout)
70 def addConnectionListener(self, xListener):
71 if dbg:
72 print("PyMailSMTPService addConnectionListener", file=dbgout)
73 self.listeners.append(xListener)
74 def removeConnectionListener(self, xListener):
75 if dbg:
76 print("PyMailSMTPService removeConnectionListener", file=dbgout)
77 self.listeners.remove(xListener)
78 def getSupportedConnectionTypes(self):
79 if dbg:
80 print("PyMailSMTPService getSupportedConnectionTypes", file=dbgout)
81 return self.supportedtypes
82 def connect(self, xConnectionContext, xAuthenticator):
83 self.connectioncontext = xConnectionContext
84 if dbg:
85 print("PyMailSMTPService connect", file=dbgout)
86 server = xConnectionContext.getValueByName("ServerName")
87 if dbg:
88 print("ServerName: " + server, file=dbgout)
89 port = int(xConnectionContext.getValueByName("Port"))
90 if dbg:
91 print("Port: " + str(port), file=dbgout)
92 tout = xConnectionContext.getValueByName("Timeout")
93 if dbg:
94 print(isinstance(tout,int), file=dbgout)
95 if not isinstance(tout,int):
96 tout = _GLOBAL_DEFAULT_TIMEOUT
97 if dbg:
98 print("Timeout: " + str(tout), file=dbgout)
99 self.server = smtplib.SMTP(server, port,timeout=tout)
101 #stderr not available for us under windows, but
102 #set_debuglevel outputs there, and so throw
103 #an exception under windows on debugging mode
104 #with this enabled
105 if dbg and os.name != 'nt':
106 self.server.set_debuglevel(1)
108 connectiontype = xConnectionContext.getValueByName("ConnectionType")
109 if dbg:
110 print("ConnectionType: " + connectiontype, file=dbgout)
111 if connectiontype.upper() == 'SSL':
112 self.server.ehlo()
113 self.server.starttls()
114 self.server.ehlo()
116 user = xAuthenticator.getUserName()
117 password = xAuthenticator.getPassword()
118 if user != '':
119 if sys.version < '3': # fdo#59249 i#105669 Python 2 needs "ascii"
120 user = user.encode('ascii')
121 password = password.encode('ascii')
122 if dbg:
123 print("Logging in, username of: " + user, file=dbgout)
124 self.server.login(user, password)
126 for listener in self.listeners:
127 listener.connected(self.notify)
128 def disconnect(self):
129 if dbg:
130 print("PyMailSMTPService disconnect", file=dbgout)
131 if self.server:
132 self.server.quit()
133 self.server = None
134 for listener in self.listeners:
135 listener.disconnected(self.notify)
136 def isConnected(self):
137 if dbg:
138 print("PyMailSMTPService isConnected", file=dbgout)
139 return self.server != None
140 def getCurrentConnectionContext(self):
141 if dbg:
142 print("PyMailSMTPService getCurrentConnectionContext", file=dbgout)
143 return self.connectioncontext
144 def sendMailMessage(self, xMailMessage):
145 COMMASPACE = ', '
147 if dbg:
148 print("PyMailSMTPService sendMailMessage", file=dbgout)
149 recipients = xMailMessage.getRecipients()
150 sendermail = xMailMessage.SenderAddress
151 sendername = xMailMessage.SenderName
152 subject = xMailMessage.Subject
153 ccrecipients = xMailMessage.getCcRecipients()
154 bccrecipients = xMailMessage.getBccRecipients()
155 if dbg:
156 print("PyMailSMTPService subject: " + subject, file=dbgout)
157 print("PyMailSMTPService from: " + sendername, file=dbgout)
158 print("PyMailSMTPService from: " + sendermail, file=dbgout)
159 print("PyMailSMTPService send to: %s" % (recipients,), file=dbgout)
161 attachments = xMailMessage.getAttachments()
163 textmsg = Message()
165 content = xMailMessage.Body
166 flavors = content.getTransferDataFlavors()
167 if dbg:
168 print("PyMailSMTPService flavors len: %d" % (len(flavors),), file=dbgout)
170 #Use first flavor that's sane for an email body
171 for flavor in flavors:
172 if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find('text/plain') != -1:
173 if dbg:
174 print("PyMailSMTPService mimetype is: " + flavor.MimeType, file=dbgout)
175 textbody = content.getTransferData(flavor)
177 if len(textbody):
178 mimeEncoding = re.sub("charset=.*", "charset=UTF-8", flavor.MimeType)
179 if mimeEncoding.find('charset=UTF-8') == -1:
180 mimeEncoding = mimeEncoding + "; charset=UTF-8"
181 textmsg['Content-Type'] = mimeEncoding
182 textmsg['MIME-Version'] = '1.0'
184 try:
185 #it's a string, get it as utf-8 bytes
186 textbody = textbody.encode('utf-8')
187 except:
188 #it's a bytesequence, get raw bytes
189 textbody = textbody.value
190 if sys.version >= '3':
191 if sys.version_info.minor < 3 or (sys.version_info.minor == 3 and sys.version_info.micro <= 1):
192 #http://stackoverflow.com/questions/9403265/how-do-i-use-python-3-2-email-module-to-send-unicode-messages-encoded-in-utf-8-w
193 #see http://bugs.python.org/16564, etc. basically it now *seems* to be all ok
194 #in python 3.3.2 onwards, but a little busted in 3.3.0
196 textbody = textbody.decode('iso8859-1')
197 else:
198 textbody = textbody.decode('utf-8')
199 c = Charset('utf-8')
200 c.body_encoding = QP
201 textmsg.set_payload(textbody, c)
202 else:
203 textmsg.set_payload(textbody)
205 break
207 if (len(attachments)):
208 msg = MIMEMultipart()
209 msg.epilogue = ''
210 msg.attach(textmsg)
211 else:
212 msg = textmsg
214 hdr = Header(sendername, 'utf-8')
215 hdr.append('<'+sendermail+'>','us-ascii')
216 msg['Subject'] = subject
217 msg['From'] = hdr
218 msg['To'] = COMMASPACE.join(recipients)
219 if len(ccrecipients):
220 msg['Cc'] = COMMASPACE.join(ccrecipients)
221 if xMailMessage.ReplyToAddress != '':
222 msg['Reply-To'] = xMailMessage.ReplyToAddress
224 mailerstring = "LibreOffice via Caolan's mailmerge component"
225 try:
226 ctx = uno.getComponentContext()
227 aConfigProvider = ctx.ServiceManager.createInstance("com.sun.star.configuration.ConfigurationProvider")
228 prop = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
229 prop.Name = "nodepath"
230 prop.Value = "/org.openoffice.Setup/Product"
231 aSettings = aConfigProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess",
232 (prop,))
233 mailerstring = aSettings.getByName("ooName") + " " + \
234 aSettings.getByName("ooSetupVersion") + " via Caolan's mailmerge component"
235 except:
236 pass
238 msg['X-Mailer'] = mailerstring
239 msg['Date'] = formatdate(localtime=True)
241 for attachment in attachments:
242 content = attachment.Data
243 flavors = content.getTransferDataFlavors()
244 flavor = flavors[0]
245 ctype = flavor.MimeType
246 maintype, subtype = ctype.split('/', 1)
247 msgattachment = MIMEBase(maintype, subtype)
248 data = content.getTransferData(flavor)
249 msgattachment.set_payload(data.value)
250 encode_base64(msgattachment)
251 fname = attachment.ReadableName
252 try:
253 msgattachment.add_header('Content-Disposition', 'attachment', \
254 filename=fname)
255 except:
256 msgattachment.add_header('Content-Disposition', 'attachment', \
257 filename=('utf-8','',fname))
258 if dbg:
259 print(("PyMailSMTPService attachmentheader: ", str(msgattachment)), file=dbgout)
261 msg.attach(msgattachment)
263 uniquer = {}
264 for key in recipients:
265 uniquer[key] = True
266 if len(ccrecipients):
267 for key in ccrecipients:
268 uniquer[key] = True
269 if len(bccrecipients):
270 for key in bccrecipients:
271 uniquer[key] = True
272 truerecipients = uniquer.keys()
274 if dbg:
275 print(("PyMailSMTPService recipients are: ", truerecipients), file=dbgout)
277 self.server.sendmail(sendermail, truerecipients, msg.as_string())
279 class PyMailIMAPService(unohelper.Base, XMailService):
280 def __init__( self, ctx ):
281 self.ctx = ctx
282 self.listeners = []
283 self.supportedtypes = ('Insecure', 'Ssl')
284 self.server = None
285 self.connectioncontext = None
286 self.notify = EventObject(self)
287 if dbg:
288 print("PyMailIMAPService init", file=dbgout)
289 def addConnectionListener(self, xListener):
290 if dbg:
291 print("PyMailIMAPService addConnectionListener", file=dbgout)
292 self.listeners.append(xListener)
293 def removeConnectionListener(self, xListener):
294 if dbg:
295 print("PyMailIMAPService removeConnectionListener", file=dbgout)
296 self.listeners.remove(xListener)
297 def getSupportedConnectionTypes(self):
298 if dbg:
299 print("PyMailIMAPService getSupportedConnectionTypes", file=dbgout)
300 return self.supportedtypes
301 def connect(self, xConnectionContext, xAuthenticator):
302 if dbg:
303 print("PyMailIMAPService connect", file=dbgout)
305 self.connectioncontext = xConnectionContext
306 server = xConnectionContext.getValueByName("ServerName")
307 if dbg:
308 print(server, file=dbgout)
309 port = int(xConnectionContext.getValueByName("Port"))
310 if dbg:
311 print(port, file=dbgout)
312 connectiontype = xConnectionContext.getValueByName("ConnectionType")
313 if dbg:
314 print(connectiontype, file=dbgout)
315 print("BEFORE", file=dbgout)
316 if connectiontype.upper() == 'SSL':
317 self.server = imaplib.IMAP4_SSL(server, port)
318 else:
319 self.server = imaplib.IMAP4(server, port)
320 print("AFTER", file=dbgout)
322 user = xAuthenticator.getUserName()
323 password = xAuthenticator.getPassword()
324 if user != '':
325 if sys.version < '3': # fdo#59249 i#105669 Python 2 needs "ascii"
326 user = user.encode('ascii')
327 password = password.encode('ascii')
328 if dbg:
329 print("Logging in, username of: " + user, file=dbgout)
330 self.server.login(user, password)
332 for listener in self.listeners:
333 listener.connected(self.notify)
334 def disconnect(self):
335 if dbg:
336 print("PyMailIMAPService disconnect", file=dbgout)
337 if self.server:
338 self.server.logout()
339 self.server = None
340 for listener in self.listeners:
341 listener.disconnected(self.notify)
342 def isConnected(self):
343 if dbg:
344 print("PyMailIMAPService isConnected", file=dbgout)
345 return self.server != None
346 def getCurrentConnectionContext(self):
347 if dbg:
348 print("PyMailIMAPService getCurrentConnectionContext", file=dbgout)
349 return self.connectioncontext
351 class PyMailPOP3Service(unohelper.Base, XMailService):
352 def __init__( self, ctx ):
353 self.ctx = ctx
354 self.listeners = []
355 self.supportedtypes = ('Insecure', 'Ssl')
356 self.server = None
357 self.connectioncontext = None
358 self.notify = EventObject(self)
359 if dbg:
360 print("PyMailPOP3Service init", file=dbgout)
361 def addConnectionListener(self, xListener):
362 if dbg:
363 print("PyMailPOP3Service addConnectionListener", file=dbgout)
364 self.listeners.append(xListener)
365 def removeConnectionListener(self, xListener):
366 if dbg:
367 print("PyMailPOP3Service removeConnectionListener", file=dbgout)
368 self.listeners.remove(xListener)
369 def getSupportedConnectionTypes(self):
370 if dbg:
371 print("PyMailPOP3Service getSupportedConnectionTypes", file=dbgout)
372 return self.supportedtypes
373 def connect(self, xConnectionContext, xAuthenticator):
374 if dbg:
375 print("PyMailPOP3Service connect", file=dbgout)
377 self.connectioncontext = xConnectionContext
378 server = xConnectionContext.getValueByName("ServerName")
379 if dbg:
380 print(server, file=dbgout)
381 port = int(xConnectionContext.getValueByName("Port"))
382 if dbg:
383 print(port, file=dbgout)
384 connectiontype = xConnectionContext.getValueByName("ConnectionType")
385 if dbg:
386 print(connectiontype, file=dbgout)
387 print("BEFORE", file=dbgout)
388 if connectiontype.upper() == 'SSL':
389 self.server = poplib.POP3_SSL(server, port)
390 else:
391 tout = xConnectionContext.getValueByName("Timeout")
392 if dbg:
393 print(isinstance(tout,int), file=dbgout)
394 if not isinstance(tout,int):
395 tout = _GLOBAL_DEFAULT_TIMEOUT
396 if dbg:
397 print("Timeout: " + str(tout), file=dbgout)
398 self.server = poplib.POP3(server, port, timeout=tout)
399 print("AFTER", file=dbgout)
401 user = xAuthenticator.getUserName()
402 password = xAuthenticator.getPassword()
403 if sys.version < '3': # fdo#59249 i#105669 Python 2 needs "ascii"
404 user = user.encode('ascii')
405 password = password.encode('ascii')
406 if dbg:
407 print("Logging in, username of: " + user, file=dbgout)
408 self.server.user(user)
409 self.server.pass_(password)
411 for listener in self.listeners:
412 listener.connected(self.notify)
413 def disconnect(self):
414 if dbg:
415 print("PyMailPOP3Service disconnect", file=dbgout)
416 if self.server:
417 self.server.quit()
418 self.server = None
419 for listener in self.listeners:
420 listener.disconnected(self.notify)
421 def isConnected(self):
422 if dbg:
423 print("PyMailPOP3Service isConnected", file=dbgout)
424 return self.server != None
425 def getCurrentConnectionContext(self):
426 if dbg:
427 print("PyMailPOP3Service getCurrentConnectionContext", file=dbgout)
428 return self.connectioncontext
430 class PyMailServiceProvider(unohelper.Base, XMailServiceProvider):
431 def __init__( self, ctx ):
432 if dbg:
433 print("PyMailServiceProvider init", file=dbgout)
434 self.ctx = ctx
435 def create(self, aType):
436 if dbg:
437 print("PyMailServiceProvider create with", aType, file=dbgout)
438 if aType == SMTP:
439 return PyMailSMTPService(self.ctx);
440 elif aType == POP3:
441 return PyMailPOP3Service(self.ctx);
442 elif aType == IMAP:
443 return PyMailIMAPService(self.ctx);
444 else:
445 print("PyMailServiceProvider, unknown TYPE " + aType, file=dbgout)
447 class PyMailMessage(unohelper.Base, XMailMessage):
448 def __init__( self, ctx, sTo='', sFrom='', Subject='', Body=None, aMailAttachment=None ):
449 if dbg:
450 print("PyMailMessage init", file=dbgout)
451 self.ctx = ctx
453 self.recipients = [sTo]
454 self.ccrecipients = []
455 self.bccrecipients = []
456 self.aMailAttachments = []
457 if aMailAttachment != None:
458 self.aMailAttachments.append(aMailAttachment)
460 self.SenderName, self.SenderAddress = parseaddr(sFrom)
461 self.ReplyToAddress = sFrom
462 self.Subject = Subject
463 self.Body = Body
464 if dbg:
465 print("post PyMailMessage init", file=dbgout)
466 def addRecipient( self, recipient ):
467 if dbg:
468 print("PyMailMessage.addRecipient: " + recipient, file=dbgout)
469 self.recipients.append(recipient)
470 def addCcRecipient( self, ccrecipient ):
471 if dbg:
472 print("PyMailMessage.addCcRecipient: " + ccrecipient, file=dbgout)
473 self.ccrecipients.append(ccrecipient)
474 def addBccRecipient( self, bccrecipient ):
475 if dbg:
476 print("PyMailMessage.addBccRecipient: " + bccrecipient, file=dbgout)
477 self.bccrecipients.append(bccrecipient)
478 def getRecipients( self ):
479 if dbg:
480 print("PyMailMessage.getRecipients: " + str(self.recipients), file=dbgout)
481 return tuple(self.recipients)
482 def getCcRecipients( self ):
483 if dbg:
484 print("PyMailMessage.getCcRecipients: " + str(self.ccrecipients), file=dbgout)
485 return tuple(self.ccrecipients)
486 def getBccRecipients( self ):
487 if dbg:
488 print("PyMailMessage.getBccRecipients: " + str(self.bccrecipients), file=dbgout)
489 return tuple(self.bccrecipients)
490 def addAttachment( self, aMailAttachment ):
491 if dbg:
492 print("PyMailMessage.addAttachment", file=dbgout)
493 self.aMailAttachments.append(aMailAttachment)
494 def getAttachments( self ):
495 if dbg:
496 print("PyMailMessage.getAttachments", file=dbgout)
497 return tuple(self.aMailAttachments)
499 # pythonloader looks for a static g_ImplementationHelper variable
500 g_ImplementationHelper = unohelper.ImplementationHelper()
501 g_ImplementationHelper.addImplementation( \
502 PyMailServiceProvider, "org.openoffice.pyuno.MailServiceProvider",
503 ("com.sun.star.mail.MailServiceProvider",),)
504 g_ImplementationHelper.addImplementation( \
505 PyMailMessage, "org.openoffice.pyuno.MailMessage",
506 ("com.sun.star.mail.MailMessage",),)