Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / scripting / source / pyprov / mailmerge.py
blob6bd80430d147939fb4d2c52a61a78f1a0671361a
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 import unohelper
15 import uno
16 import re
17 import os
18 import encodings.idna
20 #to implement com::sun::star::mail::XMailServiceProvider
21 #and
22 #to implement com.sun.star.mail.XMailMessage
24 from com.sun.star.mail import XMailServiceProvider
25 from com.sun.star.mail import XMailService
26 from com.sun.star.mail import XSmtpService
27 from com.sun.star.mail import XConnectionListener
28 from com.sun.star.mail import XAuthenticator
29 from com.sun.star.mail import XMailMessage
30 from com.sun.star.mail.MailServiceType import SMTP
31 from com.sun.star.mail.MailServiceType import POP3
32 from com.sun.star.mail.MailServiceType import IMAP
33 from com.sun.star.uno import XCurrentContext
34 from com.sun.star.lang import IllegalArgumentException
35 from com.sun.star.lang import EventObject
36 from com.sun.star.lang import XServiceInfo
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, ssl, smtplib, imaplib, poplib
51 dbg = False
53 # pythonloader looks for a static g_ImplementationHelper variable
54 g_ImplementationHelper = unohelper.ImplementationHelper()
55 g_providerImplName = "org.openoffice.pyuno.MailServiceProvider"
56 g_messageImplName = "org.openoffice.pyuno.MailMessage"
58 class 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
64 self.connectioncontext = 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)
77 def getSupportedConnectionTypes(self):
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)
103 if dbg:
104 self.server.set_debuglevel(1)
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()
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)
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
135 def getCurrentConnectionContext(self):
136 if dbg:
137 print("PyMailSMTPService getCurrentConnectionContext", file=sys.stderr)
138 return self.connectioncontext
139 def sendMailMessage(self, xMailMessage):
140 COMMASPACE = ', '
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)
156 attachments = xMailMessage.getAttachments()
158 textmsg = Message()
160 content = xMailMessage.Body
161 flavors = content.getTransferDataFlavors()
162 if dbg:
163 print("PyMailSMTPService flavors len: %d" % (len(flavors),), file=sys.stderr)
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)
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'
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)
190 break
192 if (len(attachments)):
193 msg = MIMEMultipart()
194 msg.epilogue = ''
195 msg.attach(textmsg)
196 else:
197 msg = textmsg
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
209 mailerstring = "LibreOffice via Caolan's mailmerge component"
210 try:
211 ctx = uno.getComponentContext()
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
223 msg['X-Mailer'] = mailerstring
224 msg['Date'] = formatdate(localtime=True)
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)
246 msg.attach(msgattachment)
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()
259 if dbg:
260 print(("PyMailSMTPService recipients are: ", truerecipients), file=sys.stderr)
262 self.server.sendmail(sendermail, truerecipients, msg.as_string())
264 class PyMailIMAPService(unohelper.Base, XMailService):
265 def __init__( self, ctx ):
266 self.ctx = ctx
267 self.listeners = []
268 self.supportedtypes = ('Insecure', 'Ssl')
269 self.server = None
270 self.connectioncontext = 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)
282 def getSupportedConnectionTypes(self):
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)
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)
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)
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
328 def getCurrentConnectionContext(self):
329 if dbg:
330 print("PyMailIMAPService getCurrentConnectionContext", file=sys.stderr)
331 return self.connectioncontext
333 class PyMailPOP3Service(unohelper.Base, XMailService):
334 def __init__( self, ctx ):
335 self.ctx = ctx
336 self.listeners = []
337 self.supportedtypes = ('Insecure', 'Ssl')
338 self.server = None
339 self.connectioncontext = 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)
351 def getSupportedConnectionTypes(self):
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)
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)
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)
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
404 def getCurrentConnectionContext(self):
405 if dbg:
406 print("PyMailPOP3Service getCurrentConnectionContext", file=sys.stderr)
407 return self.connectioncontext
409 class 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)
426 def getImplementationName(self):
427 return g_providerImplName
429 def supportsService(self, ServiceName):
430 return g_ImplementationHelper.supportsService(g_providerImplName, ServiceName)
432 def getSupportedServiceNames(self):
433 return g_ImplementationHelper.getSupportedServiceNames(g_providerImplName)
435 class 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
441 self.recipients = [sTo]
442 self.ccrecipients = []
443 self.bccrecipients = []
444 self.aMailAttachments = []
445 if aMailAttachment != None:
446 self.aMailAttachments.append(aMailAttachment)
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)
487 def getImplementationName(self):
488 return g_messageImplName
490 def supportsService(self, ServiceName):
491 return g_ImplementationHelper.supportsService(g_messageImplName, ServiceName)
493 def getSupportedServiceNames(self):
494 return g_ImplementationHelper.getSupportedServiceNames(g_messageImplName)
496 g_ImplementationHelper.addImplementation( \
497 PyMailServiceProvider, g_providerImplName,
498 ("com.sun.star.mail.MailServiceProvider",),)
499 g_ImplementationHelper.addImplementation( \
500 PyMailMessage, g_messageImplName,
501 ("com.sun.star.mail.MailMessage",),)
503 # vim: set shiftwidth=4 softtabstop=4 expandtab: