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