xref: /trunk/main/scripting/source/pyprov/mailmerge.py (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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()
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 == '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            msgattachment.add_header('Content-Disposition', 'attachment', \
209                filename=attachment.ReadableName)
210            msg.attach(msgattachment)
211
212        uniquer = {}
213        for key in recipients:
214            uniquer[key] = True
215        if len(ccrecipients):
216            for key in ccrecipients:
217                uniquer[key] = True
218        if len(bccrecipients):
219            for key in bccrecipients:
220                uniquer[key] = True
221        truerecipients = uniquer.keys()
222
223        if dbg:
224            print >> sys.stderr, "PyMailSMPTService recipients are", truerecipients
225
226        self.server.sendmail(sendermail, truerecipients, msg.as_string())
227
228class PyMailIMAPService(unohelper.Base, XMailService):
229    def __init__( self, ctx ):
230        self.ctx = ctx
231        self.listeners = []
232        self.supportedtypes = ('Insecure', 'Ssl')
233        self.server = None
234        self.connectioncontext = None
235        if dbg:
236            print >> sys.stderr, "PyMailIMAPService init"
237    def addConnectionListener(self, xListener):
238        if dbg:
239            print >> sys.stderr, "PyMailIMAPService addConnectionListener"
240        self.listeners.append(xListener)
241    def removeConnectionListener(self, xListener):
242        if dbg:
243            print >> sys.stderr, "PyMailIMAPService removeConnectionListener"
244        self.listeners.remove(xListener)
245    def getSupportedConnectionTypes(self):
246        if dbg:
247            print >> sys.stderr, "PyMailIMAPService getSupportedConnectionTypes"
248        return self.supportedtypes
249    def connect(self, xConnectionContext, xAuthenticator):
250        if dbg:
251            print >> sys.stderr, "PyMailIMAPService connect"
252
253        self.connectioncontext = xConnectionContext
254        server = xConnectionContext.getValueByName("ServerName")
255        if dbg:
256            print >> sys.stderr, server
257        port = xConnectionContext.getValueByName("Port")
258        if dbg:
259            print >> sys.stderr, port
260        connectiontype = xConnectionContext.getValueByName("ConnectionType")
261        if dbg:
262            print >> sys.stderr, connectiontype
263        print >> sys.stderr, "BEFORE"
264        if connectiontype == 'Ssl':
265            self.server = imaplib.IMAP4_SSL(server, port)
266        else:
267            self.server = imaplib.IMAP4(server, port)
268        print >> sys.stderr, "AFTER"
269
270        user = xAuthenticator.getUserName().encode('ascii')
271        password = xAuthenticator.getPassword().encode('ascii')
272        if user != '':
273            if dbg:
274                print >> sys.stderr, 'Logging in, username of', user
275            self.server.login(user, password)
276
277        for listener in self.listeners:
278            listener.connected(self.notify)
279    def disconnect(self):
280        if dbg:
281            print >> sys.stderr, "PyMailIMAPService disconnect"
282        if self.server:
283            self.server.logout()
284            self.server = None
285        for listener in self.listeners:
286            listener.disconnected(self.notify)
287    def isConnected(self):
288        if dbg:
289            print >> sys.stderr, "PyMailIMAPService isConnected"
290        return self.server != None
291    def getCurrentConnectionContext(self):
292        if dbg:
293            print >> sys.stderr, "PyMailIMAPService getCurrentConnectionContext"
294        return self.connectioncontext
295
296class PyMailPOP3Service(unohelper.Base, XMailService):
297    def __init__( self, ctx ):
298        self.ctx = ctx
299        self.listeners = []
300        self.supportedtypes = ('Insecure', 'Ssl')
301        self.server = None
302        self.connectioncontext = None
303        if dbg:
304            print >> sys.stderr, "PyMailPOP3Service init"
305    def addConnectionListener(self, xListener):
306        if dbg:
307            print >> sys.stderr, "PyMailPOP3Service addConnectionListener"
308        self.listeners.append(xListener)
309    def removeConnectionListener(self, xListener):
310        if dbg:
311            print >> sys.stderr, "PyMailPOP3Service removeConnectionListener"
312        self.listeners.remove(xListener)
313    def getSupportedConnectionTypes(self):
314        if dbg:
315            print >> sys.stderr, "PyMailPOP3Service getSupportedConnectionTypes"
316        return self.supportedtypes
317    def connect(self, xConnectionContext, xAuthenticator):
318        if dbg:
319            print >> sys.stderr, "PyMailPOP3Service connect"
320
321        self.connectioncontext = xConnectionContext
322        server = xConnectionContext.getValueByName("ServerName")
323        if dbg:
324            print >> sys.stderr, server
325        port = xConnectionContext.getValueByName("Port")
326        if dbg:
327            print >> sys.stderr, port
328        connectiontype = xConnectionContext.getValueByName("ConnectionType")
329        if dbg:
330            print >> sys.stderr, connectiontype
331        print >> sys.stderr, "BEFORE"
332        if connectiontype == 'Ssl':
333            self.server = poplib.POP3_SSL(server, port)
334        else:
335            self.server = poplib.POP3(server, port)
336        print >> sys.stderr, "AFTER"
337
338        user = xAuthenticator.getUserName().encode('ascii')
339        password = xAuthenticator.getPassword().encode('ascii')
340        if dbg:
341            print >> sys.stderr, 'Logging in, username of', user
342        self.server.user(user)
343        self.server.pass_(user, password)
344
345        for listener in self.listeners:
346            listener.connected(self.notify)
347    def disconnect(self):
348        if dbg:
349            print >> sys.stderr, "PyMailPOP3Service disconnect"
350        if self.server:
351            self.server.quit()
352            self.server = None
353        for listener in self.listeners:
354            listener.disconnected(self.notify)
355    def isConnected(self):
356        if dbg:
357            print >> sys.stderr, "PyMailPOP3Service isConnected"
358        return self.server != None
359    def getCurrentConnectionContext(self):
360        if dbg:
361            print >> sys.stderr, "PyMailPOP3Service getCurrentConnectionContext"
362        return self.connectioncontext
363
364class PyMailServiceProvider(unohelper.Base, XMailServiceProvider):
365    def __init__( self, ctx ):
366        if dbg:
367            print >> sys.stderr, "PyMailServiceProvider init"
368        self.ctx = ctx
369    def create(self, aType):
370        if dbg:
371            print >> sys.stderr, "PyMailServiceProvider create with", aType
372        if aType == SMTP:
373            return PyMailSMTPService(self.ctx);
374        elif aType == POP3:
375            return PyMailPOP3Service(self.ctx);
376        elif aType == IMAP:
377            return PyMailIMAPService(self.ctx);
378        else:
379            print >> sys.stderr, "PyMailServiceProvider, unknown TYPE", aType
380
381class PyMailMessage(unohelper.Base, XMailMessage):
382    def __init__( self, ctx, sTo='', sFrom='', Subject='', Body=None, aMailAttachment=None ):
383        if dbg:
384            print >> sys.stderr, "PyMailMessage init"
385        self.ctx = ctx
386
387        self.recipients = sTo,
388        self.ccrecipients = ()
389        self.bccrecipients = ()
390        self.aMailAttachments = ()
391        if aMailAttachment != None:
392            self.aMailAttachments = aMailAttachment,
393
394        self.SenderName, self.SenderAddress = parseaddr(sFrom)
395        self.ReplyToAddress = sFrom
396        self.Subject = Subject
397        self.Body = Body
398        if dbg:
399            print >> sys.stderr, "post PyMailMessage init"
400    def addRecipient( self, recipient ):
401        if dbg:
402            print >> sys.stderr, "PyMailMessage.addRecipient", recipient
403        self.recipients = self.recipients, recipient
404    def addCcRecipient( self, ccrecipient ):
405        if dbg:
406            print >> sys.stderr, "PyMailMessage.addCcRecipient", ccrecipient
407        self.ccrecipients = self.ccrecipients, ccrecipient
408    def addBccRecipient( self, bccrecipient ):
409        if dbg:
410            print >> sys.stderr, "PyMailMessage.addBccRecipient", bccrecipient
411        self.bccrecipients = self.bccrecipients, bccrecipient
412    def getRecipients( self ):
413        if dbg:
414            print >> sys.stderr, "PyMailMessage.getRecipients", self.recipients
415        return self.recipients
416    def getCcRecipients( self ):
417        if dbg:
418            print >> sys.stderr, "PyMailMessage.getCcRecipients", self.ccrecipients
419        return self.ccrecipients
420    def getBccRecipients( self ):
421        if dbg:
422            print >> sys.stderr, "PyMailMessage.getBccRecipients", self.bccrecipients
423        return self.bccrecipients
424    def addAttachment( self, aMailAttachment ):
425        if dbg:
426            print >> sys.stderr, "PyMailMessage.addAttachment"
427        self.aMailAttachments = self.aMailAttachments, aMailAttachment
428    def getAttachments( self ):
429        if dbg:
430            print >> sys.stderr, "PyMailMessage.getAttachments"
431        return self.aMailAttachments
432
433# pythonloader looks for a static g_ImplementationHelper variable
434g_ImplementationHelper = unohelper.ImplementationHelper()
435g_ImplementationHelper.addImplementation( \
436    PyMailServiceProvider, "org.openoffice.pyuno.MailServiceProvider",
437        ("com.sun.star.mail.MailServiceProvider",),)
438g_ImplementationHelper.addImplementation( \
439    PyMailMessage, "org.openoffice.pyuno.MailMessage",
440        ("com.sun.star.mail.MailMessage",),)
441