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