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