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