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