tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / scripting / source / pyprov / mailmerge.py
blob13e23e64965010ddfb98d880e47113ae6be8ee0d
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
18 # to implement com::sun::star::mail::XMailServiceProvider
19 # and
20 # to implement com.sun.star.mail.XMailMessage
22 from com.sun.star.mail import XMailServiceProvider
23 from com.sun.star.mail import XMailService
24 from com.sun.star.mail import XSmtpService
25 from com.sun.star.mail import XMailMessage
26 from com.sun.star.mail.MailServiceType import SMTP
27 from com.sun.star.mail.MailServiceType import POP3
28 from com.sun.star.mail.MailServiceType import IMAP
29 from com.sun.star.lang import IllegalArgumentException
30 from com.sun.star.lang import EventObject
31 from com.sun.star.lang import XServiceInfo
33 from email.mime.base import MIMEBase
34 from email.message import Message
35 from email.charset import Charset
36 from email.charset import QP
37 from email.encoders import encode_base64
38 from email.header import Header
39 from email.mime.multipart import MIMEMultipart
40 from email.utils import formatdate
41 from email.utils import parseaddr
42 from socket import _GLOBAL_DEFAULT_TIMEOUT
44 import sys
45 import ssl
46 import smtplib
47 import imaplib
48 import poplib
50 dbg = False
52 # pythonloader looks for a static g_ImplementationHelper variable
53 g_ImplementationHelper = unohelper.ImplementationHelper()
54 g_providerImplName = "org.openoffice.pyuno.MailServiceProvider"
55 g_messageImplName = "org.openoffice.pyuno.MailMessage"
58 def prepareTLSContext(xComponent, xContext, isTLSRequested):
59 xConfigProvider = xContext.ServiceManager.createInstance(
60 "com.sun.star.configuration.ConfigurationProvider"
62 prop = uno.createUnoStruct("com.sun.star.beans.PropertyValue")
63 prop.Name = "nodepath"
64 prop.Value = "/org.openoffice.Office.Security/Net"
65 xSettings = xConfigProvider.createInstanceWithArguments(
66 "com.sun.star.configuration.ConfigurationAccess", (prop,)
68 isAllowedInsecure = xSettings.getByName("AllowInsecureProtocols")
69 tlscontext = None
70 if isTLSRequested:
71 if dbg:
72 print("SSL config: " + str(ssl.get_default_verify_paths()), file=sys.stderr)
73 tlscontext = ssl.create_default_context()
74 # SSLv2/v3 is already disabled by default.
75 # This check does not work, because OpenSSL 3 defines SSL_OP_NO_SSLv2
76 # as 0, so even though _ssl__SSLContext_impl() tries to set it,
77 # getting the value from SSL_CTX_get_options() doesn't lead to setting
78 # the python-level flag.
79 # assert (tlscontext.options & ssl.Options.OP_NO_SSLv2) != 0
80 assert (tlscontext.options & ssl.Options.OP_NO_SSLv3) != 0
81 if not (isAllowedInsecure):
82 if not (isTLSRequested):
83 if dbg:
84 print(
85 "mailmerge.py: insecure connection not allowed by configuration",
86 file=sys.stderr,
88 raise IllegalArgumentException(
89 "insecure connection not allowed by configuration", xComponent, 1
91 tlscontext.options |= ssl.Options.OP_NO_TLSv1 | ssl.Options.OP_NO_TLSv1_1
92 return tlscontext
95 class PyMailSMTPService(unohelper.Base, XSmtpService):
96 def __init__(self, ctx):
97 self.ctx = ctx
98 self.listeners = []
99 self.supportedtypes = ("Insecure", "Ssl")
100 self.server = None
101 self.connectioncontext = None
102 self.notify = EventObject(self)
103 if dbg:
104 print("PyMailSMTPService init", file=sys.stderr)
105 print("python version is: " + sys.version, file=sys.stderr)
107 def addConnectionListener(self, xListener):
108 if dbg:
109 print("PyMailSMTPService addConnectionListener", file=sys.stderr)
110 self.listeners.append(xListener)
112 def removeConnectionListener(self, xListener):
113 if dbg:
114 print("PyMailSMTPService removeConnectionListener", file=sys.stderr)
115 self.listeners.remove(xListener)
117 def getSupportedConnectionTypes(self):
118 if dbg:
119 print("PyMailSMTPService getSupportedConnectionTypes", file=sys.stderr)
120 return self.supportedtypes
122 def connect(self, xConnectionContext, xAuthenticator):
123 self.connectioncontext = xConnectionContext
124 if dbg:
125 print("PyMailSMTPService connect", file=sys.stderr)
126 server = xConnectionContext.getValueByName("ServerName").strip()
127 if dbg:
128 print("ServerName: " + server, file=sys.stderr)
129 port = int(xConnectionContext.getValueByName("Port"))
130 if dbg:
131 print("Port: " + str(port), file=sys.stderr)
132 tout = xConnectionContext.getValueByName("Timeout")
133 if dbg:
134 print(isinstance(tout, int), file=sys.stderr)
135 if not isinstance(tout, int):
136 tout = _GLOBAL_DEFAULT_TIMEOUT
137 if dbg:
138 print("Timeout: " + str(tout), file=sys.stderr)
139 connectiontype = xConnectionContext.getValueByName("ConnectionType")
140 if dbg:
141 print("ConnectionType: " + connectiontype, file=sys.stderr)
142 tlscontext = prepareTLSContext(
143 self, self.ctx, connectiontype.upper() == "SSL" or port == 465
145 if port == 465:
146 self.server = smtplib.SMTP_SSL(
147 server, port, timeout=tout, context=tlscontext
149 else:
150 self.server = smtplib.SMTP(server, port, timeout=tout)
152 if dbg:
153 self.server.set_debuglevel(1)
155 if connectiontype.upper() == "SSL" and port != 465:
156 # STRIPTLS: smtplib raises an exception if result is not 220
157 self.server.starttls(context=tlscontext)
159 user = xAuthenticator.getUserName()
160 password = xAuthenticator.getPassword()
161 if user != "":
162 if dbg:
163 print("Logging in, username of: " + user, file=sys.stderr)
164 self.server.login(user, password)
166 for listener in self.listeners:
167 listener.connected(self.notify)
169 def disconnect(self):
170 if dbg:
171 print("PyMailSMTPService disconnect", file=sys.stderr)
172 if self.server:
173 self.server.quit()
174 self.server = None
175 for listener in self.listeners:
176 listener.disconnected(self.notify)
178 def isConnected(self):
179 if dbg:
180 print("PyMailSMTPService isConnected", file=sys.stderr)
181 return self.server is not None
183 def getCurrentConnectionContext(self):
184 if dbg:
185 print("PyMailSMTPService getCurrentConnectionContext", file=sys.stderr)
186 return self.connectioncontext
188 def sendMailMessage(self, xMailMessage):
189 COMMASPACE = ", "
191 if dbg:
192 print("PyMailSMTPService sendMailMessage", file=sys.stderr)
193 recipients = xMailMessage.getRecipients()
194 sendermail = xMailMessage.SenderAddress
195 sendername = xMailMessage.SenderName
196 subject = xMailMessage.Subject
197 ccrecipients = xMailMessage.getCcRecipients()
198 bccrecipients = xMailMessage.getBccRecipients()
199 if dbg:
200 print("PyMailSMTPService subject: " + subject, file=sys.stderr)
201 print("PyMailSMTPService from: " + sendername, file=sys.stderr)
202 print("PyMailSMTPService from: " + sendermail, file=sys.stderr)
203 print("PyMailSMTPService send to: %s" % (recipients,), file=sys.stderr)
205 attachments = xMailMessage.getAttachments()
207 textmsg = Message()
209 content = xMailMessage.Body
210 flavors = content.getTransferDataFlavors()
211 if dbg:
212 print(
213 "PyMailSMTPService flavors len: %d" % (len(flavors),), file=sys.stderr
216 # Use first flavor that's sane for an email body
217 for flavor in flavors:
218 if (
219 flavor.MimeType.find("text/html") != -1
220 or flavor.MimeType.find("text/plain") != -1
222 if dbg:
223 print(
224 "PyMailSMTPService mimetype is: " + flavor.MimeType,
225 file=sys.stderr,
227 textbody = content.getTransferData(flavor)
229 if len(textbody):
230 mimeEncoding = re.sub(
231 "charset=.*", "charset=UTF-8", flavor.MimeType
233 if mimeEncoding.find("charset=UTF-8") == -1:
234 mimeEncoding = mimeEncoding + "; charset=UTF-8"
235 textmsg["Content-Type"] = mimeEncoding
236 textmsg["MIME-Version"] = "1.0"
238 try:
239 # it's a string, get it as utf-8 bytes
240 textbody = textbody.encode("utf-8")
241 except Exception:
242 # it's a bytesequence, get raw bytes
243 textbody = textbody.value
244 textbody = textbody.decode("utf-8")
245 c = Charset("utf-8")
246 c.body_encoding = QP
247 textmsg.set_payload(textbody, c)
249 break
251 if len(attachments):
252 msg = MIMEMultipart()
253 msg.epilogue = ""
254 msg.attach(textmsg)
255 else:
256 msg = textmsg
258 hdr = Header(sendername, "utf-8")
259 hdr.append("<" + sendermail + ">", "us-ascii")
260 msg["Subject"] = subject
261 msg["From"] = hdr
262 msg["To"] = COMMASPACE.join(recipients)
263 if len(ccrecipients):
264 msg["Cc"] = COMMASPACE.join(ccrecipients)
265 if xMailMessage.ReplyToAddress != "":
266 msg["Reply-To"] = xMailMessage.ReplyToAddress
268 mailerstring = "LibreOffice via Caolan's mailmerge component"
269 try:
270 ctx = uno.getComponentContext()
271 aConfigProvider = ctx.ServiceManager.createInstance(
272 "com.sun.star.configuration.ConfigurationProvider"
274 prop = uno.createUnoStruct("com.sun.star.beans.PropertyValue")
275 prop.Name = "nodepath"
276 prop.Value = "/org.openoffice.Setup/Product"
277 aSettings = aConfigProvider.createInstanceWithArguments(
278 "com.sun.star.configuration.ConfigurationAccess", (prop,)
280 mailerstring = (
281 aSettings.getByName("ooName")
282 + " "
283 + aSettings.getByName("ooSetupVersion")
284 + " via Caolan's mailmerge component"
286 except Exception:
287 pass
289 msg["X-Mailer"] = mailerstring
290 msg["Date"] = formatdate(localtime=True)
292 for attachment in attachments:
293 content = attachment.Data
294 flavors = content.getTransferDataFlavors()
295 flavor = flavors[0]
296 ctype = flavor.MimeType
297 maintype, subtype = ctype.split("/", 1)
298 msgattachment = MIMEBase(maintype, subtype)
299 data = content.getTransferData(flavor)
300 msgattachment.set_payload(data.value)
301 encode_base64(msgattachment)
302 fname = attachment.ReadableName
303 try:
304 msgattachment.add_header(
305 "Content-Disposition", "attachment", filename=fname
307 except Exception:
308 msgattachment.add_header(
309 "Content-Disposition", "attachment", filename=("utf-8", "", fname)
311 if dbg:
312 print(
313 ("PyMailSMTPService attachmentheader: ", str(msgattachment)),
314 file=sys.stderr,
317 msg.attach(msgattachment)
319 uniquer = {}
320 for key in recipients:
321 uniquer[key] = True
322 if len(ccrecipients):
323 for key in ccrecipients:
324 uniquer[key] = True
325 if len(bccrecipients):
326 for key in bccrecipients:
327 uniquer[key] = True
328 truerecipients = uniquer.keys()
330 if dbg:
331 print(
332 ("PyMailSMTPService recipients are: ", truerecipients), file=sys.stderr
335 self.server.sendmail(sendermail, truerecipients, msg.as_string())
338 class PyMailIMAPService(unohelper.Base, XMailService):
339 def __init__(self, ctx):
340 self.ctx = ctx
341 self.listeners = []
342 self.supportedtypes = ("Insecure", "Ssl")
343 self.server = None
344 self.connectioncontext = None
345 self.notify = EventObject(self)
346 if dbg:
347 print("PyMailIMAPService init", file=sys.stderr)
349 def addConnectionListener(self, xListener):
350 if dbg:
351 print("PyMailIMAPService addConnectionListener", file=sys.stderr)
352 self.listeners.append(xListener)
354 def removeConnectionListener(self, xListener):
355 if dbg:
356 print("PyMailIMAPService removeConnectionListener", file=sys.stderr)
357 self.listeners.remove(xListener)
359 def getSupportedConnectionTypes(self):
360 if dbg:
361 print("PyMailIMAPService getSupportedConnectionTypes", file=sys.stderr)
362 return self.supportedtypes
364 def connect(self, xConnectionContext, xAuthenticator):
365 if dbg:
366 print("PyMailIMAPService connect", file=sys.stderr)
368 self.connectioncontext = xConnectionContext
369 server = xConnectionContext.getValueByName("ServerName")
370 if dbg:
371 print(server, file=sys.stderr)
372 port = int(xConnectionContext.getValueByName("Port"))
373 if dbg:
374 print(port, file=sys.stderr)
375 connectiontype = xConnectionContext.getValueByName("ConnectionType")
376 if dbg:
377 print(connectiontype, file=sys.stderr)
378 tlscontext = prepareTLSContext(self, self.ctx, connectiontype.upper() == "SSL")
379 print("BEFORE", file=sys.stderr)
380 if connectiontype.upper() == "SSL":
381 self.server = imaplib.IMAP4_SSL(server, port, ssl_context=tlscontext)
382 else:
383 self.server = imaplib.IMAP4(server, port)
384 print("AFTER", file=sys.stderr)
386 user = xAuthenticator.getUserName()
387 password = xAuthenticator.getPassword()
388 if user != "":
389 if dbg:
390 print("Logging in, username of: " + user, file=sys.stderr)
391 self.server.login(user, password)
393 for listener in self.listeners:
394 listener.connected(self.notify)
396 def disconnect(self):
397 if dbg:
398 print("PyMailIMAPService disconnect", file=sys.stderr)
399 if self.server:
400 self.server.logout()
401 self.server = None
402 for listener in self.listeners:
403 listener.disconnected(self.notify)
405 def isConnected(self):
406 if dbg:
407 print("PyMailIMAPService isConnected", file=sys.stderr)
408 return self.server is not None
410 def getCurrentConnectionContext(self):
411 if dbg:
412 print("PyMailIMAPService getCurrentConnectionContext", file=sys.stderr)
413 return self.connectioncontext
416 class PyMailPOP3Service(unohelper.Base, XMailService):
417 def __init__(self, ctx):
418 self.ctx = ctx
419 self.listeners = []
420 self.supportedtypes = ("Insecure", "Ssl")
421 self.server = None
422 self.connectioncontext = None
423 self.notify = EventObject(self)
424 if dbg:
425 print("PyMailPOP3Service init", file=sys.stderr)
427 def addConnectionListener(self, xListener):
428 if dbg:
429 print("PyMailPOP3Service addConnectionListener", file=sys.stderr)
430 self.listeners.append(xListener)
432 def removeConnectionListener(self, xListener):
433 if dbg:
434 print("PyMailPOP3Service removeConnectionListener", file=sys.stderr)
435 self.listeners.remove(xListener)
437 def getSupportedConnectionTypes(self):
438 if dbg:
439 print("PyMailPOP3Service getSupportedConnectionTypes", file=sys.stderr)
440 return self.supportedtypes
442 def connect(self, xConnectionContext, xAuthenticator):
443 if dbg:
444 print("PyMailPOP3Service connect", file=sys.stderr)
446 self.connectioncontext = xConnectionContext
447 server = xConnectionContext.getValueByName("ServerName")
448 if dbg:
449 print(server, file=sys.stderr)
450 port = int(xConnectionContext.getValueByName("Port"))
451 if dbg:
452 print(port, file=sys.stderr)
453 connectiontype = xConnectionContext.getValueByName("ConnectionType")
454 if dbg:
455 print(connectiontype, file=sys.stderr)
456 tlscontext = prepareTLSContext(self, self.ctx, connectiontype.upper() == "SSL")
457 print("BEFORE", file=sys.stderr)
458 if connectiontype.upper() == "SSL":
459 self.server = poplib.POP3_SSL(server, port, context=tlscontext)
460 else:
461 tout = xConnectionContext.getValueByName("Timeout")
462 if dbg:
463 print(isinstance(tout, int), file=sys.stderr)
464 if not isinstance(tout, int):
465 tout = _GLOBAL_DEFAULT_TIMEOUT
466 if dbg:
467 print("Timeout: " + str(tout), file=sys.stderr)
468 self.server = poplib.POP3(server, port, timeout=tout)
469 print("AFTER", file=sys.stderr)
471 user = xAuthenticator.getUserName()
472 password = xAuthenticator.getPassword()
473 if dbg:
474 print("Logging in, username of: " + user, file=sys.stderr)
475 self.server.user(user)
476 self.server.pass_(password)
478 for listener in self.listeners:
479 listener.connected(self.notify)
481 def disconnect(self):
482 if dbg:
483 print("PyMailPOP3Service disconnect", file=sys.stderr)
484 if self.server:
485 self.server.quit()
486 self.server = None
487 for listener in self.listeners:
488 listener.disconnected(self.notify)
490 def isConnected(self):
491 if dbg:
492 print("PyMailPOP3Service isConnected", file=sys.stderr)
493 return self.server is not None
495 def getCurrentConnectionContext(self):
496 if dbg:
497 print("PyMailPOP3Service getCurrentConnectionContext", file=sys.stderr)
498 return self.connectioncontext
501 class PyMailServiceProvider(unohelper.Base, XMailServiceProvider, XServiceInfo):
502 def __init__(self, ctx):
503 if dbg:
504 print("PyMailServiceProvider init", file=sys.stderr)
505 self.ctx = ctx
507 def create(self, aType):
508 if dbg:
509 print("PyMailServiceProvider create with", aType, file=sys.stderr)
510 if aType == SMTP:
511 return PyMailSMTPService(self.ctx)
512 elif aType == POP3:
513 return PyMailPOP3Service(self.ctx)
514 elif aType == IMAP:
515 return PyMailIMAPService(self.ctx)
516 else:
517 print("PyMailServiceProvider, unknown TYPE " + aType, file=sys.stderr)
519 def getImplementationName(self):
520 return g_providerImplName
522 def supportsService(self, ServiceName):
523 return g_ImplementationHelper.supportsService(g_providerImplName, ServiceName)
525 def getSupportedServiceNames(self):
526 return g_ImplementationHelper.getSupportedServiceNames(g_providerImplName)
529 class PyMailMessage(unohelper.Base, XMailMessage):
530 def __init__(
531 self, ctx, sTo="", sFrom="", Subject="", Body=None, aMailAttachment=None
533 if dbg:
534 print("PyMailMessage init", file=sys.stderr)
535 self.ctx = ctx
537 self.recipients = [sTo]
538 self.ccrecipients = []
539 self.bccrecipients = []
540 self.aMailAttachments = []
541 if aMailAttachment is not None:
542 self.aMailAttachments.append(aMailAttachment)
544 self.SenderName, self.SenderAddress = parseaddr(sFrom)
545 self.ReplyToAddress = sFrom
546 self.Subject = Subject
547 self.Body = Body
548 if dbg:
549 print("post PyMailMessage init", file=sys.stderr)
551 def addRecipient(self, recipient):
552 if dbg:
553 print("PyMailMessage.addRecipient: " + recipient, file=sys.stderr)
554 self.recipients.append(recipient)
556 def addCcRecipient(self, ccrecipient):
557 if dbg:
558 print("PyMailMessage.addCcRecipient: " + ccrecipient, file=sys.stderr)
559 self.ccrecipients.append(ccrecipient)
561 def addBccRecipient(self, bccrecipient):
562 if dbg:
563 print("PyMailMessage.addBccRecipient: " + bccrecipient, file=sys.stderr)
564 self.bccrecipients.append(bccrecipient)
566 def getRecipients(self):
567 if dbg:
568 print(
569 "PyMailMessage.getRecipients: " + str(self.recipients), file=sys.stderr
571 return tuple(self.recipients)
573 def getCcRecipients(self):
574 if dbg:
575 print(
576 "PyMailMessage.getCcRecipients: " + str(self.ccrecipients),
577 file=sys.stderr,
579 return tuple(self.ccrecipients)
581 def getBccRecipients(self):
582 if dbg:
583 print(
584 "PyMailMessage.getBccRecipients: " + str(self.bccrecipients),
585 file=sys.stderr,
587 return tuple(self.bccrecipients)
589 def addAttachment(self, aMailAttachment):
590 if dbg:
591 print("PyMailMessage.addAttachment", file=sys.stderr)
592 self.aMailAttachments.append(aMailAttachment)
594 def getAttachments(self):
595 if dbg:
596 print("PyMailMessage.getAttachments", file=sys.stderr)
597 return tuple(self.aMailAttachments)
599 def getImplementationName(self):
600 return g_messageImplName
602 def supportsService(self, ServiceName):
603 return g_ImplementationHelper.supportsService(g_messageImplName, ServiceName)
605 def getSupportedServiceNames(self):
606 return g_ImplementationHelper.getSupportedServiceNames(g_messageImplName)
609 g_ImplementationHelper.addImplementation(
610 PyMailServiceProvider,
611 g_providerImplName,
612 ("com.sun.star.mail.MailServiceProvider",),
614 g_ImplementationHelper.addImplementation(
615 PyMailMessage,
616 g_messageImplName,
617 ("com.sun.star.mail.MailMessage",),
620 # vim: set shiftwidth=4 softtabstop=4 expandtab: