def get_article(self): article="Newsgroups: "+self.newsgroups+"\n" article=article+"From: "+formataddr([self.nick_encoded,self.email])+"\n" article=article+"Subject: "+self.subject_encoded+"\n" if self.references!="": article=article+"References: "+self.references+"\n" self.references="" article=article+"User-Agent: "+self.user_agent+"\n" article=article+"MIME-Version: 1.0\n" article=article+"Content-Type: text/plain; charset="+self.output_charset+"\n" if self.output_charset.lower()=="us-ascii": article=article+"Content-Transfer-Encoding: 7bit\n" else: article=article+"Content-Transfer-Encoding: 8bit\n" if self.generate_mid=="True": mid=make_msgid("XPN") if self.fqdn: left,right=mid.split("@") def clear_fqdn(s,chars): s=s.encode("us-ascii","replace") for char in chars: s=s.replace(char,"") return s mid=left+"@"+clear_fqdn(self.fqdn,"@\\\"<>()[];:,")+">" article=article+"Message-ID: "+mid+"\n" for header in self.custom_headers: article=article+header+"\n" article=article+"\n" article=article.encode("utf-8")+self.body_encoded return article
def send_simple_mail(sender, receiver, subject, msgtxt, attachments=None, usergenerated=False): # attachment format, each is a tuple of (name, mimetype,contents) # content should be *binary* and not base64 encoded, since we need to # use the base64 routines from the email library to get a properly # formatted output message msg = MIMEMultipart() msg['Subject'] = subject msg['To'] = receiver msg['From'] = sender msg['Date'] = formatdate(localtime=True) msg['Message-ID'] = make_msgid() msg.attach(MIMEText(msgtxt, _charset='utf-8')) if attachments: for filename, contenttype, content in attachments: main,sub = contenttype.split('/') part = MIMENonMultipart(main,sub) part.set_payload(content) part.add_header('Content-Disposition', 'attachment; filename="%s"' % filename) encoders.encode_base64(part) msg.attach(part) # Just write it to the queue, so it will be transactionally rolled back QueuedMail(sender=sender, receiver=receiver, fullmsg=msg.as_string(), usergenerated=usergenerated).save()
def fetch(self): """retrieve all messages from IMAP spool limited to 10 for testing""" if self.protocol._pop3user == 'monitor' and self.protocol._pop3pass == 'monitor': id = uuid5(NAMESPACE_DNS, str(time())) self.messages = [ POP3Message(content=monitormessage % (make_msgid(), formatdate(), id, id)) ] return True try: if self._imap == None: if not self.authenticate(): return False r, n = self._imap.select('INBOX') logger.debug(u'IMAP: fetching INBOX %s' % n) n = int(n[0]) if n == 0: return True # limit for now #if n > 10: n = 10 if n == 1: n = 2 for n in range(1, n): logger.debug(u'IMAP: retrieve %s' % n) r, c = self._imap.fetch(n, '(RFC822)') self.protocol.messages.append(POP3Message(content=c[0][1])) return True except Exception, e: logger.error(u'IMAP: fetch: %s' % str(e)) self._imap = None self.state = str(e) return False
def mail_closures(bugs): """Send the list of closed bugs.""" text = "" max_package = 0 max_bug = 0 bugs.sort() for bug_severity, package, bug, title in bugs: if len(package) > max_package: max_package = len(package) if len(str(bug)) > max_bug: max_bug = len(str(bug)) for idx, severity in enumerate(SEVERITY): closures = [ bug for bug in bugs if bug[0] == idx ] if not len(closures): continue text += "%s\n" % severity text += ("-" * len(severity)) + "\n\n" for bug_severity, package, bug, title in closures: text += "%-*s %-*d %s\n" \ % (max_package, package, max_bug, bug, title) text += "\n" message = MIMEText(text) message.add_header("From", "Tanglu Merge-o-Matic <*****@*****.**>") message.add_header("To", "Tanglu Merge-o-Matic <*****@*****.**>") message.add_header("Date", formatdate()) message.add_header("Subject", "Bugs closed in Debian") message.add_header("Message-ID", make_msgid()) send_message(message, RECIPIENTS)
def send(self, fromaddr, toaddrs, message): """See IMailer.""" env_recips = COMMASPACE.join(toaddrs) log = getLogger('lp.services.mail') log.info('Email from %s to %s being stored in mailbox %s', fromaddr, env_recips, self.filename) msg = email.message_from_string(message) # Mimic what MTAs such as Postfix do in transfering the envelope # sender into the Return-Path header. It's okay if the message has # multiple such headers. msg['Return-Path'] = fromaddr # Because it might be useful, copy the envelope recipients into the # RFC 2822 headers too. msg['X-Envelope-To'] = env_recips # Add the Message-ID required by the interface; even though the # interface says that the message text doesn't include such a header, # zap it first just in case. del msg['message-id'] msg['Message-ID'] = message_id = make_msgid() mbox_file = open(self.filename, 'a') try: print >> mbox_file, msg finally: mbox_file.close() if self.mailer is not None: # Forward the message on to the chained mailer, if there is one. # This allows for example, the mboxMailer to be used in the test # suite, which requires that the testMailer eventually gets # called. chained_mailer = getUtility(IMailer, self.mailer) chained_mailer.send(fromaddr, toaddrs, message) return message_id
def _get_detached_message_for_person(self, sender): # Return a signed message that contains a detached signature. body = dedent("""\ This is a multi-line body. Sincerely, Your friendly tester.""") to = self.factory.getUniqueEmailAddress() msg = MIMEMultipart() msg['Message-Id'] = make_msgid('launchpad') msg['Date'] = formatdate() msg['To'] = to msg['From'] = sender.preferredemail.email msg['Subject'] = 'Sample' body_text = MIMEText(body) msg.attach(body_text) # A detached signature is calculated on the entire string content of # the body message part. key = import_secret_test_key() gpghandler = getUtility(IGPGHandler) signature = gpghandler.signContent( canonicalise_line_endings(body_text.as_string()), key.fingerprint, 'test', gpgme.SIG_MODE_DETACH) attachment = Message() attachment.set_payload(signature) attachment['Content-Type'] = 'application/pgp-signature' msg.attach(attachment) self.assertTrue(msg.is_multipart()) return signed_message_from_string(msg.as_string())
def mail(smtp, fromaddr, toaddrs, replytoaddr, subject, body): from email.Header import Header from email.MIMEText import MIMEText msg = MIMEText(body, 'plain') from email.Utils import make_msgid msg['Message-Id'] = make_msgid() msg['Subject'] = Header(subject) msg['From'] = Header(fromaddr) from email.Utils import formatdate msg['Date'] = formatdate() if len(replytoaddr): msg['ReplyTo'] = Header(replytoaddr) if isinstance(toaddrs, basestring): toaddrs = [toaddrs] to = '' for t in toaddrs: if len(to): to += ', ' to += t msg['To'] = Header(to) try: r = smtp.sendmail(fromaddr, toaddrs, msg.as_string(False)) for (name, errormsg) in r.iteritems(): print name, ':', errormsg except smtplib.SMTPRecipientsRefused, obj: print 'ERROR: SMTPRecipientsRefused' for (addr, errormsg) in obj.recipients.iteritems(): print addr, ':', errormsg
def mail_closures(bugs): """Send the list of closed bugs.""" text = "" max_package = 0 max_bug = 0 bugs.sort() for bug_severity, package, bug, title in bugs: if len(package) > max_package: max_package = len(package) if len(str(bug)) > max_bug: max_bug = len(str(bug)) for idx, severity in enumerate(SEVERITY): closures = [bug for bug in bugs if bug[0] == idx] if not len(closures): continue text += "%s\n" % severity text += ("-" * len(severity)) + "\n\n" for bug_severity, package, bug, title in closures: text += "%-*s %-*d %s\n" \ % (max_package, package, max_bug, bug, title) text += "\n" message = MIMEText(text) message.add_header("From", "Tanglu Merge-o-Matic <*****@*****.**>") message.add_header("To", "Tanglu Merge-o-Matic <*****@*****.**>") message.add_header("Date", formatdate()) message.add_header("Subject", "Bugs closed in Debian") message.add_header("Message-ID", make_msgid()) send_message(message, RECIPIENTS)
def mail(smtp, fromaddr, toaddrs, replytoaddr, subject, body): from email.Header import Header from email.MIMEText import MIMEText msg = MIMEText( body, 'plain' ) from email.Utils import make_msgid msg['Message-Id'] = make_msgid() msg['Subject'] = Header(subject) msg['From'] = Header(fromaddr) from email.Utils import formatdate msg['Date'] = formatdate() if len(replytoaddr): msg['ReplyTo'] = Header(replytoaddr) if isinstance(toaddrs, basestring): toaddrs = [toaddrs] to = '' for t in toaddrs: if len(to): to += ', ' to += t msg['To'] = Header(to) try: r = smtp.sendmail( fromaddr, toaddrs, msg.as_string(False) ) for (name, errormsg) in r.iteritems(): print name, ':', errormsg except smtplib.SMTPRecipientsRefused, obj: print 'ERROR: SMTPRecipientsRefused' for (addr, errormsg) in obj.recipients.iteritems(): print addr, ':', errormsg
def emit(self, record): msg = record.getMessage() if not self.send_empty_entries and not msg.strip(): return current_time = self.now() current_hour = current_time.hour if current_hour != self.hour: self.hour = current_hour self.sent = 0 if self.sent == self.flood_level: # send critical error, record = LogRecord(name='flood', level=CRITICAL, pathname='', lineno=0, msg=flood_template % (self.sent, current_time.strftime('%H:%M:%S'), current_hour + 1), args=(), exc_info=None) elif self.flood_level and self.sent > self.flood_level: # do nothing, we've sent too many emails already return self.sent += 1 # actually send the mail, try: msg = self.format(record) if self.template is not None: msg = self.template % msg subtype = self.content_type.split('/')[-1] if isinstance(msg, unicode): email = MIMEText(msg, subtype, self.charset) else: email = MIMEText(msg, subtype) for header, value in self.headers.items(): email[header] = value email['Subject'] = self.getSubject(record) email['From'] = self.fromaddr email['To'] = ', '.join(self.toaddrs) email['X-Mailer'] = x_mailer email['X-Log-Level'] = record.levelname email['Date'] = formatdate() email['Message-ID'] = make_msgid('MailingLogger') smtp = smtplib.SMTP(self.mailhost, self.mailport) secureports = [587] try: port = int(self.mailhost.split(':')[1]) except: port = None if (self.mailport in secureports) or (port in secureports): smtp.starttls() if self.username and self.password: smtp.login(self.username, self.password) smtp.sendmail(self.fromaddr, self.toaddrs, email.as_string()) smtp.quit() except: self.handleError(record)
def createComment(self, owner, subject, content=None, vote=None, review_type=None, parent=None, _date_created=DEFAULT, _notify_listeners=True): """See `IBranchMergeProposal`.""" #:param _date_created: The date the message was created. Provided # only for testing purposes, as it can break # BranchMergeProposal.root_message. review_type = self._normalizeReviewType(review_type) assert owner is not None, 'Merge proposal messages need a sender' parent_message = None if parent is not None: assert parent.branch_merge_proposal == self, \ 'Replies must use the same merge proposal as their parent' parent_message = parent.message if not subject: # Get the subject from the parent if there is one, or use a nice # default. if parent is None: subject = self.title else: subject = parent.message.subject if not subject.startswith('Re: '): subject = 'Re: ' + subject # Avoid circular dependencies. from lp.services.messages.model.message import Message, MessageChunk msgid = make_msgid('codereview') message = Message( parent=parent_message, owner=owner, rfc822msgid=msgid, subject=subject, datecreated=_date_created) MessageChunk(message=message, content=content, sequence=1) return self.createCommentFromMessage( message, vote, review_type, original_email=None, _notify_listeners=_notify_listeners, _validate=False)
def emit(self, record): msg = record.getMessage() if not self.send_empty_entries and not msg.strip(): return current_time = self.now() current_hour = current_time.hour if current_hour != self.hour: self.hour = current_hour self.sent = 0 if self.sent == self.flood_level: # send critical error record = LogRecord( name='flood', level=CRITICAL, pathname='', lineno=0, msg=flood_template % (self.sent, current_time.strftime('%H:%M:%S'), current_hour + 1), args=(), exc_info=None) elif self.flood_level and self.sent > self.flood_level: # do nothing, we've sent too many emails already return self.sent += 1 # actually send the mail try: msg = self.format(record) if self.template is not None: msg = self.template % msg subtype = self.content_type.split('/')[-1] if isinstance(msg, text_type): try: msg = msg.encode('ascii') charset = 'ascii' except UnicodeEncodeError: charset = self.charset email = MIMEText(msg, subtype, charset) else: email = MIMEText(msg, subtype) for header, value in self.headers.items(): email[header] = value email['Subject'] = self.getSubject(record) email['From'] = self.fromaddr email['To'] = ', '.join(self.toaddrs) email['X-Mailer'] = x_mailer email['X-Log-Level'] = record.levelname email['Date'] = formatdate() email['Message-ID'] = make_msgid('MailingLogger') smtp = smtplib.SMTP(self.mailhost, self.mailport) if self.username and self.password: smtp.login(self.username, self.password) smtp.sendmail(self.fromaddr, self.toaddrs, email.as_string()) smtp.quit() except: self.handleError(record)
def add_headers(msg): if not(msg.has_key('Message-ID')): msg['Message-ID'] = make_msgid('idtracker') if not(msg.has_key('Date')): msg['Date'] = formatdate(time.time(), True) if not(msg.has_key('From')): msg['From'] = settings.DEFAULT_FROM_EMAIL return msg
def add_headers(msg): if not (msg.has_key('Message-ID')): msg['Message-ID'] = make_msgid('idtracker') if not (msg.has_key('Date')): msg['Date'] = formatdate(time.time(), True) if not (msg.has_key('From')): msg['From'] = settings.DEFAULT_FROM_EMAIL return msg
def makeArticle(sender, newsgroup, subject, body): article = Message() article["From"] = sender article["Newsgroups"] = newsgroup article["Subject"] = subject article["Message-Id"] = make_msgid() article["Date"] = formatdate() article.set_payload(body) return article.as_string(unixfrom=False)
def sendDeleteNotifications(con, lang, project, category, relativeID, userID): """ Send a notification in case of delete """ cursor = con.cursor() # creating the list of recipients query = """ SELECT mail FROM Client WHERE subscription = True AND allowed = True AND project = %s""" cursor.execute(query, (projectID(con, project), )) recipients = [] for row in cursor.fetchall() : recipients.append(row[0]) # get message content query = """ SELECT name, lastMessageID FROM Entry WHERE relativeID = %s AND category = %s""" categoryCode = {'bug' : 1, 'feature' : 2} cursor.execute(query, (relativeID, categoryCode[category])) row = cursor.fetchone() title = row[0] inReplyTo = row[1] # user caracteristics (the one who deleted the entry) query = """SELECT login, mail FROM Client WHERE id=%s""" cursor.execute(query, (userID, )) row = cursor.fetchone() author = row[0] authorMail = row[1] # load template mytemplate = Template(filename='templates/'+lang+'/mails/delete.mail', output_encoding='utf-8', default_filters=['decode.utf8'], input_encoding='utf-8') text = mytemplate.render(creator = author) category = category[0].upper()+category[1:].lower() h = Header() h.append(u'Re:', 'utf-8') h.append(category, 'utf-8') h.append(u'#'+str(relativeID)+u':', 'utf-8') h.append(title, 'utf-8') # make messageID # no need to save it to the database : the entry will be deleted messageID = make_msgid() mail = {'From' : author + ' <' + authorMail + '>', 'To' : recipients, 'Subject' : h, 'Reply-To' : project + '@projects.naphtaline.net', 'Message-ID' : messageID, 'In-Reply-To' : inReplyTo, 'text' : text, 'files' : []} send_mail(mail)
def send_email(self, email): msg = MIMEText(email['body']) if self.subject: msg['Subject'] = email['subject'] msg['From'] = email['from'] msg['To'] = self.COMMASPACE.join(email['to']) msg['Date'] = formatdate() msg['Message-ID'] = make_msgid() try: self.send_smtp(msg, email['to']) except Exception, e: self.write_stderr("Error sending email: %s\n" % e)
def build_invitation(self, email_from='', email_to='', subject='', email_cc=[], email_bcc=[], reply_to=False, attachments=None, message_id=None, references=None, object_id=False, headers={}, ): email_from = email_from or tools.config.get('email_from') assert email_from, "You must either provide a sender address explicitly or configure "\ "a global sender address in the server configuration or with the "\ "--email-from startup parameter." msg = MIMEMultipart() if not headers: headers = {} if not message_id: if object_id: message_id = tools.generate_tracking_message_id(object_id) else: message_id = make_msgid() msg['Message-Id'] = encode_header(message_id) if references: msg['references'] = encode_header(references) msg['Subject'] = encode_header(subject) msg['From'] = encode_rfc2822_address_header(email_from) del msg['Reply-To'] if reply_to: msg['Reply-To'] = encode_rfc2822_address_header(reply_to) else: msg['Reply-To'] = msg['From'] msg['To'] = encode_rfc2822_address_header(COMMASPACE.join(email_to)) if email_cc: msg['Cc'] = encode_rfc2822_address_header(COMMASPACE.join(email_cc)) if email_bcc: msg['Bcc'] = encode_rfc2822_address_header(COMMASPACE.join(email_bcc)) msg['Date'] = formatdate() for key, value in headers.items(): msg[ustr(key).encode('utf-8')] = encode_header(value) text_to_body_added = False if attachments: #it is assumed for now that only ics file is attached!!! for fname, fcontent in attachments: if not text_to_body_added and fname == 'invite.ics': # Provide message description in body of message only as text for now; need fixes if 'DESCRIPTION:' in fcontent and 'LOCATION' in fcontent.split('DESCRIPTION')[1]: meeting_description_text = fcontent.split('DESCRIPTION:')[1].split('LOCATION')[0] text_converted_to_html = self.plaintext2html(meeting_description_text, tabstop=4) text_utf8 = re.sub(r'\\n', "</p><p>", text_converted_to_html) alternative_part = MIMEMultipart(_subtype="alternative") alternative_part.attach(MIMEText(text_utf8, _charset='utf-8', _subtype='html')) msg.attach(alternative_part) #adding invitation stuff part = MIMEBase('text', 'calendar', charset='utf-8', method='REQUEST') part.set_payload(fcontent) msg.attach(part) return msg
def send_simple_mail(sender, receiver, subject, msgtxt, attachments=None, usergenerated=False, cc=None, replyto=None, sendername=None, receivername=None): # attachment format, each is a tuple of (name, mimetype,contents) # content should be *binary* and not base64 encoded, since we need to # use the base64 routines from the email library to get a properly # formatted output message msg = MIMEMultipart() msg['Subject'] = subject msg['To'] = _encoded_email_header(receivername, receiver) msg['From'] = _encoded_email_header(sendername, sender) if cc: msg['Cc'] = cc if replyto: msg['Reply-To'] = replyto msg['Date'] = formatdate(localtime=True) msg['Message-ID'] = make_msgid() msg.attach(MIMEText(msgtxt, _charset='utf-8')) if attachments: for filename, contenttype, content in attachments: main, sub = contenttype.split('/') part = MIMENonMultipart(main, sub) part.set_payload(content) part.add_header('Content-Disposition', 'attachment; filename="%s"' % filename) encoders.encode_base64(part) msg.attach(part) # Just write it to the queue, so it will be transactionally rolled back QueuedMail(sender=sender, receiver=receiver, fullmsg=msg.as_string(), usergenerated=usergenerated).save() if cc: # Write a second copy for the cc, wihch will be delivered # directly to the recipient. (The sender doesn't parse the # message content to extract cc fields). QueuedMail(sender=sender, receiver=cc, fullmsg=msg.as_string(), usergenerated=usergenerated).save()
def createMessage(self, subject, bug, owner, content=None): """See `IBugMessageSet`.""" msg = Message( parent=bug.initial_message, owner=owner, rfc822msgid=make_msgid('malone'), subject=subject) MessageChunk(message=msg, content=content, sequence=1) bugmsg = BugMessage(bug=bug, message=msg, index=bug.bug_messages.count()) # XXX 2008-05-27 jamesh: # Ensure that BugMessages get flushed in same order as they # are created. Store.of(bugmsg).flush() return bugmsg
def emit(self,record): msg = record.getMessage() if not self.send_empty_entries and not msg.strip(): return for criterion in self.ignore: if criterion(msg): return current_time = now() current_hour = current_time.hour if current_hour != self.hour: self.hour = current_hour self.sent = 0 if self.sent == self.flood_level: # send critical error record = LogRecord( name = 'flood', level = CRITICAL, pathname = '', lineno = 0, msg = flood_template % (self.sent, current_time.strftime('%H:%M:%S'), current_hour+1), args = (), exc_info = None) elif self.flood_level and self.sent > self.flood_level: # do nothing, we've sent too many emails already return self.sent += 1 # actually send the mail try: msg = self.format(record) email = MIMEText(msg) for header,value in self.headers.items(): email[header]=value email['Subject']=self.getSubject(record) email['From']=self.fromaddr email['To']=', '.join(self.toaddrs) email['X-Mailer']=x_mailer email['Date']=formatdate() email['Message-ID']=make_msgid('MailingLogger') smtp = smtplib.SMTP(self.mailhost, self.mailport) if self.username and self.password: smtp.login(self.username,self.password) smtp.sendmail(self.fromaddr, self.toaddrs, email.as_string()) smtp.quit() except: self.handleError(record)
def send_mail(mail_to, smtp_from, subject, smtp_server, smtp_port): ''' Connect to the server once and send all mails from 'mail_to' dictionary. Contains emails as keys and messages to send as values. smtp_to: the sender subject: subject line, common for all mails ''' if not mail_to: logging.debug('No mails to send (send_mail)') return logging.debug("Connecting to the server '%s:%s'", smtp_server, smtp_port) smtp = smtplib.SMTP(smtp_server, smtp_port) logging.debug('Connected.') smtp.set_debuglevel(0) for send_to in mail_to: text = mail_to[send_to] msg_root = MIMEMultipart('related') msg_root['From'] = smtp_from msg_root['To'] = send_to msg_root['Date'] = formatdate(localtime=True) msg_root['Message-ID'] = make_msgid() msg_root['Subject'] = subject msg_root.preamble = 'This is a multi-part message in MIME format.' msg = MIMEMultipart('alternative') msg.set_charset('utf-8') msg_root.attach(msg) # Wrapping text to the simple html header text = '<HTML><BODY><div><pre>' + text + '</pre></div></BODY></HTML>' # Attaching text to the letter msg_text = MIMEText(text.encode('utf-8', 'replace'), 'html', _charset='utf-8') msg.attach(msg_text) email_file_data = msg_root.as_string() smtp.sendmail(smtp_from, send_to, email_file_data) logging.debug("Sent outgoing email to '%s'", send_to) smtp.close()
def send_mail(mail_to, smtp_from, subject, smtp_server, smtp_port): ''' Connect to the server once and send all mails from 'mail_to' dictionary. Contains emails as keys and messages to send as values. smtp_to: the sender subject: subject line, common for all mails ''' if not mail_to: logging.debug('No mails to send (send_mail)') return logging.debug("Connecting to the server '%s:%s'", smtp_server, smtp_port) smtp = smtplib.SMTP(smtp_server, smtp_port) logging.debug('Connected.') smtp.set_debuglevel(0) for send_to in mail_to: text = mail_to[send_to] msg_root = MIMEMultipart('related') msg_root['From'] = smtp_from msg_root['To'] = send_to msg_root['Date'] = formatdate(localtime=True) msg_root['Message-ID'] = make_msgid() msg_root['Subject'] = subject msg_root.preamble = 'This is a multi-part message in MIME format.' msg = MIMEMultipart('alternative') msg.set_charset('utf-8') msg_root.attach(msg) # Wrapping text to the simple html header text = '<HTML><BODY><div><pre>' + text + '</pre></div></BODY></HTML>' # Attaching text to the letter msg_text = MIMEText(text.encode( 'utf-8', 'replace'), 'html', _charset='utf-8') msg.attach(msg_text) email_file_data = msg_root.as_string() smtp.sendmail(smtp_from, send_to, email_file_data) logging.debug("Sent outgoing email to '%s'", send_to) smtp.close()
def new(distro_series_difference, owner, comment): """See `IDistroSeriesDifferenceCommentSource`.""" msgid = make_msgid('distroseriesdifference') message = Message( parent=None, owner=owner, rfc822msgid=msgid, subject=distro_series_difference.title) MessageChunk(message=message, content=comment, sequence=1) store = IMasterStore(DistroSeriesDifferenceComment) dsd_comment = DistroSeriesDifferenceComment() dsd_comment.distro_series_difference = distro_series_difference dsd_comment.message = message comment = store.add(dsd_comment) store.flush() return comment
def new(distro_series_difference, owner, comment): """See `IDistroSeriesDifferenceCommentSource`.""" msgid = make_msgid('distroseriesdifference') message = Message(parent=None, owner=owner, rfc822msgid=msgid, subject=distro_series_difference.title) MessageChunk(message=message, content=comment, sequence=1) store = IMasterStore(DistroSeriesDifferenceComment) dsd_comment = DistroSeriesDifferenceComment() dsd_comment.distro_series_difference = distro_series_difference dsd_comment.message = message comment = store.add(dsd_comment) store.flush() return comment
def fromText(self, subject, content, owner=None, datecreated=None, rfc822msgid=None): """See IMessageSet.""" if datecreated is None: datecreated = UTC_NOW if rfc822msgid is None: rfc822msgid = make_msgid("launchpad") message = Message( subject=subject, rfc822msgid=rfc822msgid, owner=owner, datecreated=datecreated) MessageChunk(message=message, sequence=1, content=content) # XXX 2008-05-27 jamesh: # Ensure that BugMessages get flushed in same order as they # are created. Store.of(message).flush() return message
def createComment(self, owner, subject, content=None, vote=None, review_type=None, parent=None, _date_created=DEFAULT, _notify_listeners=True): """See `IBranchMergeProposal`.""" #:param _date_created: The date the message was created. Provided # only for testing purposes, as it can break # BranchMergeProposal.root_message. review_type = self._normalizeReviewType(review_type) assert owner is not None, 'Merge proposal messages need a sender' parent_message = None if parent is not None: assert parent.branch_merge_proposal == self, \ 'Replies must use the same merge proposal as their parent' parent_message = parent.message if not subject: # Get the subject from the parent if there is one, or use a nice # default. if parent is None: subject = self.title else: subject = parent.message.subject if not subject.startswith('Re: '): subject = 'Re: ' + subject # Avoid circular dependencies. from lp.services.messages.model.message import Message, MessageChunk msgid = make_msgid('codereview') message = Message(parent=parent_message, owner=owner, rfc822msgid=msgid, subject=subject, datecreated=_date_created) MessageChunk(message=message, content=content, sequence=1) return self.createCommentFromMessage( message, vote, review_type, original_email=None, _notify_listeners=_notify_listeners, _validate=False)
def generate_email(self, text): def commatize(item): if isinstance(item, str): item = [item] try: item = [email.lower() for email in item] return ', '.join(item) except TypeError: return None send_to = commatize(self.send_to) cc = commatize(self.cc) msg = MIMEText(text) msg['Subject'] = self.subject msg['From'] = self.send_from msg['To'] = send_to msg['CC'] = cc msg['Date'] = formatdate(localtime=True) msg['Message-ID'] = make_msgid() return msg
def fromText(self, subject, content, owner=None, datecreated=None, rfc822msgid=None): """See IMessageSet.""" if datecreated is None: datecreated = UTC_NOW if rfc822msgid is None: rfc822msgid = make_msgid("launchpad") message = Message(subject=subject, rfc822msgid=rfc822msgid, owner=owner, datecreated=datecreated) MessageChunk(message=message, sequence=1, content=content) # XXX 2008-05-27 jamesh: # Ensure that BugMessages get flushed in same order as they # are created. Store.of(message).flush() return message
def email(msg_to, msg_from, subject, body): import smtplib from email.MIMEText import MIMEText from email.Utils import make_msgid, formatdate if not is_production(): msg_to = "*****@*****.**" msg = MIMEText(body.encode('utf8'), 'plain', 'utf8') msg['To'] = msg_to msg['From'] = msg_from msg['Subject'] = subject msg['Date'] = formatdate(localtime=True) msg['Message-ID'] = make_msgid() s = smtplib.SMTP() s.connect() try: s.sendmail(msg_from, [msg_to], msg.as_string()) except smtplib.SMTPException, smtp: # todo: record bounces so a human can do something log.error("smtp error %s" % repr(smtp))
def mail_diff(distro, last, this, uploader, subscriptions): """Mail a diff out to the subscribers.""" recipients = get_recipients(distro, this["Package"], uploader, subscriptions) if not len(recipients): return if distro == SRC_DISTRO: # Debian uploads always just have a diff subject = "Debian %s %s" % (this["Package"], this["Version"]) intro = MIMEText("""\ This e-mail has been sent due to an upload to Debian, and contains the difference between the new version and the previous one.""") payload = diff_part(distro, this) elif distro != OUR_DISTRO: # Other uploads always just have a diff subject = "%s %s %s" % (distro, this["Package"], this["Version"]) intro = MIMEText("""\ This e-mail has been sent due to an upload to %s, and contains the difference between the new version and the previous one.""" % distro) payload = diff_part(distro, this) elif get_base(this) == this["Version"]: # Never e-mail tanglu uploads without local changes return elif last is None: # Initial tanglu uploads, send the patch subject = "Tanglu (new) %s %s" % (this["Package"], this["Version"]) intro = MIMEText("""\ This e-mail has been sent due to an upload to Tanglu of a new source package which already contains Tanglu changes. It contains the difference between the Tanglu version and the equivalent base version in Debian.""") payload = patch_part(distro, this) elif get_base(last) != get_base(this): # Tanglu changed upstream version, send the patech subject = "Tanglu (new upstream) %s %s"\ % (this["Package"], this["Version"]) intro = MIMEText("""\ This e-mail has been sent due to an upload to Tanglu of a new upstream version which still contains Tanglu changes. It contains the difference between the Tanglu version and the equivalent base version in Debian, note that this difference may include the upstream changes.""") payload = patch_part(distro, this) else: # Tanglu revision, send the diff subject = "Tanglu %s %s" % (this["Package"], this["Version"]) intro = MIMEText("""\ This e-mail has been sent due to an upload to Tanglu that contains Tanglu changes. It contains the difference between the new version and the previous version of the same source package in Tanglu.""") payload = diff_part(distro, this) # Allow patches to be missing (no Debian version) if payload is None: return # Extract the changes file changes_filename = changes_file(distro, this) if os.path.isfile(changes_filename): changes = MIMEText(open(changes_filename).read()) elif os.path.isfile(changes_filename + ".bz2"): changes = MIMEText(bz2.BZ2File(changes_filename + ".bz2").read()) changes.add_header("Content-Disposition", "inline", filename="%s" % os.path.basename(changes_filename)) # Build up the message message = MIMEMultipart() message.add_header("From", "Tanglu Merge-o-Matic <*****@*****.**>") message.add_header("To", "Tanglu Merge-o-Matic <*****@*****.**>") message.add_header("Date", formatdate()) message.add_header("Subject", subject) message.add_header("Message-ID", make_msgid()) message.add_header("X-Your-Mom", "mom.tanglu.org %s" % this["Package"]) message.add_header("X-PTS-Approved", "yes") message.attach(intro) message.attach(changes) message.attach(payload) send_message(message, recipients)
def mainloop(): while 1: # Are there any files in the spool directory? to_be_sent = [] all_files = [] for spool in MAILDROP_SPOOLS: all_files.extend([os.path.join(spool, x) for x in os.listdir(spool)]) # Remove lock files clean_files = [x for x in all_files if not x.endswith('.lck')] # Remove files that were locked by the previously removed lock files clean_files = [x for x in clean_files if not '%s.lck' % x in all_files] # Remove directories to_be_sent = [x for x in clean_files if not os.path.isdir(x)] if len(to_be_sent) > 0: # Open the log file time_stamp = time.strftime('%Y/%m/%d %H:%M:%S') log_file = open(MAILDROP_LOG_FILE, 'a') msg = '\n### Started at %s...' % time_stamp log_file.write(msg) if DEBUG: print msg while len(to_be_sent) > 0: if (MAILDROP_BATCH == 0) or (MAILDROP_BATCH > len(to_be_sent)): batch = len(to_be_sent) else: batch = MAILDROP_BATCH # Send mail try: smtp_server = smtplib.SMTP(SMTP_HOST, SMTP_PORT) #smtp_server.set_debuglevel(1) smtp_server.ehlo() except smtplib.SMTPConnectError: # SMTP server did not respond. Log it and stop processing. time_stamp = time.strftime('%Y/%m/%d %H:%M:%S') err_msg = '!!!!! Connection error at %s' % time_stamp finish_msg = '### Finished at %s' % time_stamp log_file.write(err_msg) if DEBUG: print err_msg log_file.write(finish_msg) if DEBUG: print finish_msg log_file.close() break if MAILDROP_TLS > 0: if (MAILDROP_TLS > 1 and not smtp_server.has_extn('starttls')): # Problem: TLS is required but the server does not # offer it We stop processing here. time_stamp = time.strftime('%Y/%m/%d %H:%M:%S') err_msg = '!!!!! TLS unavailable at %s' % time_stamp finish_msg = '### Finished at %s' % time_stamp log_file.write(err_msg) if DEBUG: print err_msg log_file.write(finish_msg) if DEBUG: print finish_msg log_file.close() break smtp_server.starttls() # We have to say Hello again after starting TLS smtp_server.ehlo() if MAILDROP_LOGIN != '' and MAILDROP_PASSWORD != '': # Login is required to send mail if not smtp_server.has_extn('auth'): # The server does not offer authentication but we want it # We stop processing here. time_stamp = time.strftime('%Y/%m/%d %H:%M:%S') err_msg = '!!!!! Authentication unavailable at %s' % time_stamp finish_msg = '### Finished at %s' % time_stamp log_file.write(err_msg) if DEBUG: print err_msg log_file.write(finish_msg) if DEBUG: print finish_msg log_file.close() break try: smtp_server.login(MAILDROP_LOGIN, MAILDROP_PASSWORD) except smtplib.SMTPAuthenticationError: # Authentication with the given credentials fails. # We stop processing here. time_stamp = time.strftime('%Y/%m/%d %H:%M:%S') err_msg = '!!!!! Authentication failed at %s' % time_stamp finish_msg = '### Finished at %s' % time_stamp log_file.write(err_msg) if DEBUG: print err_msg log_file.write(finish_msg) if DEBUG: print finish_msg log_file.close() break for file_path in to_be_sent[0:batch]: mail_dict = read_mail(file_path) if not mail_dict: continue # Create mail and send it off h_from = mail_dict.get('From') h_to = mail_dict.get('To') h_to_list = [] for item in rfc822.AddressList(h_to): h_to_list.append(item[1]) h_body = mail_dict.get('body') if ADD_MESSAGEID: h_body = 'Message-Id: %s\n%s' % (make_msgid(), h_body) try: smtp_server.sendmail(h_from, h_to_list, h_body) stat = 'OK' os.remove(file_path) except smtplib.SMTPRecipientsRefused, e: stat = 'FATAL: ', str(e) for (addr, error) in e.recipients.items(): if str(error[0]) in FATAL_ERROR_CODES: os.remove(file_path) break except smtplib.SMTPException, e: stat = 'BAD: ', str(e) mail_msg = '\n%s\t %s' % (stat, h_to) log_file.write(mail_msg) if DEBUG: print mail_msg log_file.flush() if WAIT_INTERVAL: time.sleep(WAIT_INTERVAL) to_be_sent = to_be_sent[batch:] try: smtp_server.quit() except smtplib.SMTPServerDisconnected: pass time_stamp = time.strftime('%Y/%m/%d %H:%M:%S') finish_msg = '\n### Finished at %s\n' % time_stamp log_file.write(finish_msg) if DEBUG: print finish_msg log_file.close()
def fullprocess(self, message): """ Add Message ID's """ if 'needs_id' in message.myflags.keys(): message.add_header("Message-Id", make_msgid('procimap') ) return message
def _process_utf8(self,kw): # sort out what encoding we're going to use encoding = kw.get('encoding', self.getProperty('encoding', BaseMailTemplate.default_encoding)) text = self.__class__.__bases__[1].__call__(self,**kw) # ZPT adds newline at the end, but it breaks backward compatibility. # So I remove it. if text and text[-1]=='\n': text = text[:-1] if not self.html() and isinstance(text, unicode): text = text.encode(encoding,'replace') # now turn the result into a MIMEText object msg = BaseMailTemplate.MIMEText( text.replace('\r',''), self.content_type.split('/')[1], encoding ) # sort out what headers and addresses we're going to use headers = {} values = {} # headers from the headers property for header in getattr(self,'headers',()): name,value = header.split(':',1) headers[name]=value # headers from the headers parameter headers_param = kw.get('headers',{}) headers.update(headers_param) # values and some specific headers for key,header in (('mfrom','From'), ('mto','To'), ('mcc','Cc'), ('mbcc','Bcc'), ('subject','Subject')): value = kw.get(key, headers_param.get(header, getattr(self, key, headers.get(header)))) if value is not None: values[key]=value # turn some sequences in coma-seperated strings if isinstance(value, (tuple, list)): value = ', '.join(value) # make sure we have no unicode headers if isinstance(value,unicode): value = value.encode(encoding) if key == 'subject': try: # Try to keep header non encoded value = Header(value.encode("ascii")) except UnicodeDecodeError: value = Header(value, "UTF-8") else: value_list = getaddresses([value]) dest_list = [] for name, email in value_list: try: name = Header(name.encode("ascii")) except UnicodeDecodeError: name = Header(name, "UTF-8") dest_list.append(formataddr((name.encode(), email))) value = ", ".join(dest_list) headers[header]=value # check required values have been supplied errors = [] for param in ('mfrom','mto'): if not values.get(param): errors.append(param) if errors: raise TypeError( 'The following parameters were required by not specified: '+( ', '.join(errors) )) # add date header headers['Date']=BaseMailTemplate.DateTime().rfc822() # add message-id header headers['Message-ID']=make_msgid() # turn headers into an ordered list for predictable header order keys = headers.keys() keys.sort() return msg,values,[(key,headers[key]) for key in keys]
def transport(self, data): if type(data) != type(''): # @todo Add warning that stream is being ignored # @todo Assuming that not string means stream. a = array.array('c') stream_data = data.read(1024) while stream_data: a.fromstring(stream_data) stream_data = data.read(1024) data = a.tostring() headers = {} splitdata = data.split('\r\n\r\n') if len(splitdata) > 1: headerdata = splitdata[0] data = string.join(splitdata[1:], '\r\n\r\n') for header in headerdata.split('\r\n'): splitheader = header.split(':') name = splitheader[0].strip() value = string.join(splitheader[1:], ':').strip() headers[name] = value if self.subtype: text_subtype = self.subtype else: text_subtype = 'plain' if text_subtype == 'plain': default_extension = 'txt' else: default_extension = text_subtype if text_subtype != 'xml' and data[:16].strip()[:5] == '<?xml': msglog.log('broadway', msglog.types.WARN, 'Transporter overriding configured subtype to "xml".') text_subtype = 'xml' default_extension = 'xml' msg = MIMEText(data, _subtype=text_subtype) subject = headers.get('Subject') if subject is None or subject == 'None' or subject == '': subject = self.subject #CSCtg54105 else: if self.subject is not None and self.subject != 'None' and self.subject != '': subject = self.subject + ': ' + subject if subject: msg.add_header('Thread-Topic', subject) msg.add_header('Subject', subject) msg.add_header('From', self.sender) msg.add_header('To', COMMASPACE.join(self._recipients)) date = headers.get('Date', formatdate(time.time(), True)) msg.add_header('Date', date) message_id = make_msgid() if self.as_attachment: # @fixme: Make configurable default_filename = "%s.%s" % (message_id[1:-1].split("@")[0], default_extension) msg.add_header('Content-Disposition', 'attachment', filename=default_filename) msg.set_param('name', default_filename) # @fixme: Make configurable msg.add_header('Content-Class', 'urn:content-classes:message') # @fixme: Make configurable msg.add_header('X-Mailer', 'Mediator SMTP Transport') msg.add_header('Message-ID', message_id) # @fixme: Make configurable msg.add_header('Importance', 'normal') # @fixme: Make configurable msg.add_header('Priority', 'normal') msg.preamble = '' # To guarantee the message ends with a newline msg.epilogue = '' smtp = self.SMTP() if self.debug: smtp.set_debuglevel(self.debug) smtp.connect(self.host, self.port, self.timeout) if self.custom_domain: if not (200 <= smtp.ehlo(self.custom_domain)[0] <= 299): (code, resp) = smtp.helo(self.custom_domain) if not (200 <= code <= 299): raise smtp.SMTPHeloError(code, resp) if self.authenticate: try: smtp.login(self.username, self.password) except smtplib.SMTPAuthenticationError: msglog.log( 'broadway', msglog.types.WARN, 'SMTP Authentication failed.' + ' Invalid username/password.') raise failures = smtp.sendmail(self.sender, self._recipients, msg.as_string()) for recipient in failures.keys(): msglog.log( 'broadway', msglog.types.WARN, 'Error sending mail to %s -> %s' % (recipient, failures[recipient])) smtp.close()
def build_email(self, email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False, attachments=None, message_id=None, references=None, object_id=False, subtype='plain', headers=None, body_alternative=None, subtype_alternative='plain'): """Constructs an RFC2822 email.message.Message object based on the keyword arguments passed, and returns it. :param string email_from: sender email address :param list email_to: list of recipient addresses (to be joined with commas) :param string subject: email subject (no pre-encoding/quoting necessary) :param string body: email body, of the type ``subtype`` (by default, plaintext). If html subtype is used, the message will be automatically converted to plaintext and wrapped in multipart/alternative, unless an explicit ``body_alternative`` version is passed. :param string body_alternative: optional alternative body, of the type specified in ``subtype_alternative`` :param string reply_to: optional value of Reply-To header :param string object_id: optional tracking identifier, to be included in the message-id for recognizing replies. Suggested format for object-id is "res_id-model", e.g. "12345-crm.lead". :param string subtype: optional mime subtype for the text body (usually 'plain' or 'html'), must match the format of the ``body`` parameter. Default is 'plain', making the content part of the mail "text/plain". :param string subtype_alternative: optional mime subtype of ``body_alternative`` (usually 'plain' or 'html'). Default is 'plain'. :param list attachments: list of (filename, filecontents) pairs, where filecontents is a string containing the bytes of the attachment :param list email_cc: optional list of string values for CC header (to be joined with commas) :param list email_bcc: optional list of string values for BCC header (to be joined with commas) :param dict headers: optional map of headers to set on the outgoing mail (may override the other headers, including Subject, Reply-To, Message-Id, etc.) :rtype: email.message.Message (usually MIMEMultipart) :return: the new RFC2822 email message """ email_from = email_from or tools.config.get('email_from') assert email_from, "You must either provide a sender address explicitly or configure "\ "a global sender address in the server configuration or with the "\ "--email-from startup parameter." # Note: we must force all strings to to 8-bit utf-8 when crafting message, # or use encode_header() for headers, which does it automatically. headers = headers or {} # need valid dict later if not email_cc: email_cc = [] if not email_bcc: email_bcc = [] if not body: body = u'' email_body_utf8 = ustr(body).encode('utf-8') email_text_part = MIMEText(email_body_utf8, _subtype=subtype, _charset='utf-8') msg = MIMEMultipart() if not message_id: if object_id: message_id = tools.generate_tracking_message_id(object_id) else: message_id = make_msgid() msg['Message-Id'] = encode_header(message_id) if references: msg['references'] = encode_header(references) msg['Subject'] = encode_header(subject) msg['From'] = encode_rfc2822_address_header(email_from) del msg['Reply-To'] if reply_to: msg['Reply-To'] = encode_rfc2822_address_header(reply_to) else: msg['Reply-To'] = msg['From'] msg['To'] = encode_rfc2822_address_header(COMMASPACE.join(email_to)) if email_cc: msg['Cc'] = encode_rfc2822_address_header(COMMASPACE.join(email_cc)) if email_bcc: msg['Bcc'] = encode_rfc2822_address_header(COMMASPACE.join(email_bcc)) msg['Date'] = formatdate() # Custom headers may override normal headers or provide additional ones for key, value in headers.iteritems(): msg[ustr(key).encode('utf-8')] = encode_header(value) if subtype == 'html' and not body_alternative and html2text: # Always provide alternative text body ourselves if possible. text_utf8 = tools.html2text(email_body_utf8.decode('utf-8')).encode('utf-8') alternative_part = MIMEMultipart(_subtype="alternative") alternative_part.attach(MIMEText(text_utf8, _charset='utf-8', _subtype='plain')) alternative_part.attach(email_text_part) msg.attach(alternative_part) elif body_alternative: # Include both alternatives, as specified, within a multipart/alternative part alternative_part = MIMEMultipart(_subtype="alternative") body_alternative_utf8 = ustr(body_alternative).encode('utf-8') alternative_body_part = MIMEText(body_alternative_utf8, _subtype=subtype_alternative, _charset='utf-8') alternative_part.attach(alternative_body_part) alternative_part.attach(email_text_part) msg.attach(alternative_part) else: msg.attach(email_text_part) if attachments: for (fname, fcontent) in attachments: filename_rfc2047 = encode_header_param(fname) part = MIMEBase('application', "octet-stream") # The default RFC2231 encoding of Message.add_header() works in Thunderbird but not GMail # so we fix it by using RFC2047 encoding for the filename instead. part.set_param('name', filename_rfc2047) part.add_header('Content-Disposition', 'attachment', filename=filename_rfc2047) part.set_payload(fcontent) Encoders.encode_base64(part) msg.attach(part) return msg
def createMessage(self, subject, spec, owner, content=None): """See ISpecificationMessageSet.""" msg = Message( owner=owner, rfc822msgid=make_msgid('blueprint'), subject=subject) MessageChunk(message=msg, content=content, sequence=1) return SpecificationMessage(specification=spec, message=msg)
def sendNewEntryNotifications(con, lang, project, category, id, files) : cursor = con.cursor() # list of recipients query = """ SELECT mail FROM Client WHERE subscription = True AND allowed = True AND project = %s """ cursor.execute(query, (projectID(con, project), )) recipients = [] for row in cursor.fetchall() : recipients.append(row[0]) # content of the message query = """ SELECT Entry.relativeID, Entry.name, Entry.description, Entry.status, Entry.severity, Client.login, Client.mail, extract( day FROM Entry.creationDate), extract( month FROM Entry.creationDate), extract( year FROM Entry.creationDate) FROM Entry INNER JOIN Client ON Entry.creator = Client.id WHERE Entry.id = %s""" cursor.execute(query, (id, )) row = cursor.fetchone() relativeID = int(row[0]) title = row[1] description = unFormatText(row[2]) status = int(row[3]) severity = int(row[4]) creator = row[5] creatorMail = row[6] day = row[7] month = row[8] year = row[9] # creating Message-ID for this entry, and therefore the entry-point for the thread messageID = make_msgid() query = """ UPDATE Entry SET lastMessageID = %s WHERE id = %s""" cursor.execute(query, (messageID,id)) con.commit() mytemplate = Template(filename='templates/'+lang+'/mails/newEntry.mail', output_encoding='utf-8', default_filters=['decode.utf8'], input_encoding='utf-8') text = mytemplate.render( creator = creator, title = title, description = description, files = files, status = status, severity = severity ) category = category[0].upper()+category[1:].lower() h = Header() h.append(category, 'utf-8') h.append(u'#'+str(relativeID)+u':', 'utf-8') h.append(title, 'utf-8') mail = { 'From' : creator + ' <' + creatorMail + '>', 'To' : recipients, 'Subject' : h, 'Reply-To' : project + '@projects.naphtaline.net', 'Message-ID' : messageID, 'In-Reply-To' : '', 'text' : text, 'files' : files, } send_mail(mail)
# Processing tobox = ImapMailbox((toserver, toboxname)) fromboxname = sys.argv[1] frombox = mailbox.mbox(fromboxname) frombox.lock() tobox.lock() i = 1 print "Processing mbox file %s with %s messages" % (fromboxname, len(frombox)) for message in frombox: print "%s" % i if message['Message-Id'] is None: print " WARNING: message has no message-id (mesage ID will be added)" message.add_header("Message-Id", make_msgid('katamon.mbox2imap')) if True: try: tobox.add(message) except ImapNotOkError: print " ERROR: Transaction failed for message %s" % i toserver.reconnect() tobox = ImapMailbox((toserver, toboxname)) time.sleep(5) time.sleep(1) number_in_tobox = len(tobox.get_all_uids()) #time.sleep(1) print " Added Message, %s in mailbox" % number_in_tobox i = i + 1 frombox.close()
def sendNewCommentNotifications(con, lang, project, category, id, content, files) : cursor = con.cursor() # creating the list of recipients query = """ SELECT mail FROM Client WHERE subscription = True AND allowed = True AND project = %s """ cursor.execute(query, (projectID(con, project), )) recipients = [] for row in cursor.fetchall() : recipients.append(row[0]) # creating the message query = """ SELECT Client.login, Client.mail, Comment.entry, extract( day FROM Comment.creationDate), extract( month FROM Comment.creationDate), extract( year FROM Comment.creationDate), Entry.name, Entry.relativeID, Entry.lastMessageID FROM Comment INNER JOIN Entry ON Comment.entry = Entry.id INNER JOIN Client ON Comment.author = Client.id WHERE Comment.id = %s""" cursor.execute(query, (id, )) row = cursor.fetchone() author = row[0] authorMail = row[1] idEntry = int(row[2]) day = int(row[3]) month = int(row[4]) year = int(row[5]) title = row[6] relativeID = int(row[7]) inReplyTo = row[8] # updating Message-ID messageID = make_msgid() query = """ UPDATE Entry SET lastMessageID = %s WHERE id = %s""" cursor.execute(query, (messageID,id)) con.commit() mytemplate = Template(filename='templates/'+lang+'/mails/newcomment.mail', output_encoding='utf-8', default_filters=['decode.utf8'], input_encoding='utf-8') text = mytemplate.render( author = author, comment = content, files = files ) category = category[0].upper()+category[1:].lower() h = Header() h.append(u'Re:', 'utf-8') h.append(category, 'utf-8') h.append(u'#'+str(relativeID)+u':', 'utf-8') h.append(title, 'utf-8') mail = { 'From' : author + ' <' + authorMail + '>', 'To' : recipients, 'Subject' : h, 'Reply-To' : project + '@projects.naphtaline.net', 'Message-ID' : messageID, 'In-Reply-To' : inReplyTo, 'text' : text, 'files' : files, } send_mail(mail)
def notify_action_needed(target, output_dir, report): try: json_mtime = os.path.getmtime(output_dir + '/REPORT.json') except OSError as e: # presumably it only has an old-style REPORT; skip it logger.debug('Skipping package %s: %s', report.source_package, e) return try: if json_mtime < os.path.getmtime(output_dir + '/action_needed.eml'): logger.debug('already sent notification for %s', output_dir) return except OSError: # file is inaccessible, assume re-notification is necessary pass ROOT = config.get('ROOT') MOM_NAME = config.get('MOM_NAME') MOM_EMAIL = config.get('MOM_EMAIL') MOM_URL = config.get('MOM_URL') obs_project = report.obs_project if obs_project is None: # guess something reasonable obs_project = ':'.join((report.left_distro, report.left_suite, report.left_component)) rel_output_dir = output_dir if rel_output_dir.startswith(ROOT): rel_output_dir = rel_output_dir[len(ROOT):] rel_output_dir = rel_output_dir.strip('/') # Some typical example subject lines: # [CONFLICTS] dderivative:alpha:main/curl # [FAILED] dderivative:alpha:main/coreutils # [MERGED] dderivative:alpha:main/robotfindskitten (if not committable) # [NO_BASE] dderivative:alpha:main/hello-debhelper # [SYNC_THEIRS] dderivative:alpha:main/hello (if not committable) subject = '[%s] %s/%s' % (report.result, obs_project, report.source_package) logger.info('%s', subject) message = MIMEMultipart() message.add_header('From', '%s <%s>' % (MOM_NAME, MOM_EMAIL)) message.add_header('To', '%s <%s>' % (MOM_NAME, MOM_EMAIL)) message.add_header('Date', formatdate()) message.add_header('Message-ID', make_msgid()) message.add_header('X-Your-Mom', '%s %s' % (MOM_URL, report.source_package)) message.add_header('Subject', subject) if report.result == MergeResult.CONFLICTS: text = ("""\ This package could not be merged: an automated 3-way merge detected conflicts. Please carry out a manual merge and commit the result to OBS. """) elif report.result == MergeResult.FAILED: text = ("""\ This package could not be merged for some reason. Please carry out a manual merge and commit the result to OBS. """) elif report.result == MergeResult.MERGED: text = ("""\ This package was merged automatically and it seems to have worked. Please check that the result makes sense. """) elif report.result == MergeResult.NO_BASE: text = ("""\ None of the packages' common ancestors could be found in the package pool. This package cannot be merged until you import one. """) elif report.result == MergeResult.SYNC_THEIRS: text = ("""\ The version in the source distribution supersedes our version. Please check that it's OK to update to the newer version. """) if report.bases_not_found: if report.result == MergeResult.NO_BASE: text = (text + """\ The most recent common ancestor was:""") else: text = (text + """ The packages' most recent common ancestor could not be found. This merge was based on an older common ancestor, but you might get a better-quality automatic merge if you import this version into the package pool:""") text += (""" %(base_not_found)s If that version was in Debian or Ubuntu, you might be able to get it from: http://snapshot.debian.org/package/%(source_package)s/ https://launchpad.net/ubuntu/+source/%(source_package)s See the "bases_not_found" list in the attached JSON report for some older versions that might also work. Download the source package with dget(1) or similar, and put it in: %(right_pool_dir)s before the next merge-o-matic run. """ % { 'right_pool_dir': report.right_pool_dir, 'base_not_found': report.bases_not_found[0], 'source_package': report.source_package, }) text = (text + """ Our version in %s: %s/%s Newest common ancestor: %s Their version in %s:%s:%s: %s """ % (obs_project, report.source_package, report.left_version, report.base_version, report.right_distro, report.right_suite, report.right_component, report.right_version)) if report.obs_request_url is not None: text = (text + """ OBS submit request for the proposed version: %s """ % report.obs_request_url) elif report.committed: text = (text + """ This package was successfully committed to %s/%s. """ % (report.committed_to, report.obs_package)) elif report.commit_detail: text = (text + """ A commit to OBS was attempted, but it appears to have failed: %s The merge-o-matic log file might have more details. """ % report.commit_detail) else: text = (text + """ This package was not committed to OBS. """) if report.merged_patch is not None: text = (text + """ You can view the diff from the upstream version to the proposed version here: %s/%s/%s """ % (MOM_URL, rel_output_dir, report.merged_patch)) if report.proposed_patch is not None: text = (text + """ You can view the diff from our current version to the proposed version here: %s/%s/%s """ % (MOM_URL, rel_output_dir, report.proposed_patch)) if report.right_changelog is not None: text = (text + """ The upstream version's changelog is attached. """) dsc = None for x in report.merged_files: if x.endswith('.dsc'): dsc = x if dsc is not None: text = (text + """ Download the proposed source package for testing here: %s/%s/%s """ % (MOM_URL, rel_output_dir, dsc)) if report.merge_failure_tarball is not None: text = (text + """ You can download a tarball containing the failed merge here: %s/%s/%s """ % (MOM_URL, rel_output_dir, report.merge_failure_tarball)) text = (text + """ A detailed merge report in JSON format is attached. More information at: %(MOM_URL)s/%(rel_output_dir)s/REPORT.html Regards, the Merge-o-Matic instance at <%(MOM_URL)s> """ % { 'MOM_URL': MOM_URL, 'rel_output_dir': rel_output_dir, }) message.attach(MIMEText(text)) if report.right_changelog: cl_part = MIMEText( open(output_dir + '/' + report.right_changelog).read()) cl_part.add_header('Content-Disposition', 'inline', filename=report.right_changelog) message.attach(cl_part) json_part = MIMEText(open(output_dir + '/REPORT.json').read()) json_part.add_header('Content-Disposition', 'inline', filename='%s_REPORT.json' % report.source_package) message.attach(json_part) if 'MOM_TEST' in os.environ: return with open(output_dir + '/action_needed.eml.tmp', 'w') as email: email.write(message.as_string()) all_ok = True smtp = SMTP('localhost') for addr in config.get('RECIPIENTS', default=[]): message.replace_header('To', addr) try: smtp.sendmail(MOM_EMAIL, addr, message.as_string()) except Exception: logger.exception('sending to %s failed:', addr) all_ok = False smtp = SMTP('localhost') # If all emails succeeded, if all_ok: os.rename(output_dir + '/action_needed.eml.tmp', output_dir + '/action_needed.eml')
def run(self): """ Main entry point """ # Read command line usage = "%prog [options] <url> <address> [<address2>] [<address...>]" parser = OptionParser(usage=usage, version=self.NAME + ' ' + self.VERSION) parser.add_option("--http-user", dest="http_user", help="Username for HTTP POST authentication") parser.add_option("--http-pass", dest="http_pass", help="Password for HTTP POST authentication") parser.add_option("-s", "--smtp", dest="smtp", help="SMTP server address. Default: localhost", default='localhost') parser.add_option("--smtp-user", dest="smtp_user", help="Username for SMTP authentication") parser.add_option("--smtp-pass", dest="smtp_pass", help="Password for SMTP authentication") parser.add_option("-c", "--cc", dest="cc", help="Carbon Copy recipient") parser.add_option("-f", "--from", dest="sender", help="eMail sender. Default: emailer@localhost", default="emailer@localhost") parser.add_option( "-j", "--subject", dest="subject", help="eMail Subject. Default: MailWWW Autogenerated Mail", default="MailWWW Autogenerated Mail") parser.add_option("-n", "--no-css", dest="nocss", help="Disable embedding of linked Style Sheets", default=False, action="store_true") parser.add_option( "-m", "--multiple", dest="multiple", help= "Send multiple emails: one for each recipient (Cc field is ignored)", default=False, action="store_true") parser.add_option("-v", "--verbose", dest="verbose", help="Show progress information", default=False, action="store_true") (options, args) = parser.parse_args() # Parse mandatory arguments if len(args) < 2: parser.error("unvalid number of arguments") dest = [] i = 0 for a in args: if i == 0: url = a else: dest.append(a) i += 1 # Parse optional arguments http_user = options.http_user http_pass = options.http_pass cc = [] if options.cc: cc.append(options.cc) host = options.smtp port = 25 user = options.smtp_user pwd = options.smtp_pass sender = options.sender subject = options.subject nocss = options.nocss multiple = options.multiple verbose = options.verbose logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO) # Opens URL logging.info('Fetching url %s', url) data = None if http_user or http_pass: # Use POST authentication data = urllib.urlencode({ 'username': http_user, 'password': http_pass, 'login': True }) f = urllib.urlopen(url, data) html = f.read() # Search for meta content-type tag, use this encoding when found encre = re.compile( r'<meta\s+http-equiv=(?:"|\')Content-Type(?:"|\')\s+content=(?:"|\')([^\'"]*)(?:"|\')\s*/>', re.I | re.M) match = encre.search(html) if match: encoding = self.__parseEncoding(match.group(1)) try: html = unicode(html, encoding, errors='replace') except LookupError as e: encoding = self.__parseEncoding(f.headers['content-type']) html = unicode(html, encoding, errors='replace') else: encoding = self.__parseEncoding(f.headers['content-type']) html = unicode(html, encoding, errors='replace') logging.info('Detected charset: %s', encoding) f.close() # Retrieve linked style sheets if not nocss: logging.info('Fetching Style Sheets...') parser = CSSLister(url) parser.feed(html) parser.close() for search, replace in parser.get_replacements(): html = html.replace(search, replace, 1) # Prepare mail msg = MIMEMultipart() msg['Date'] = formatdate(localtime=True) msg['Message-ID'] = make_msgid('emailer') msg['Subject'] = subject msg['From'] = sender if cc and not multiple: msg['Cc'] = ', '.join(cc) msg.preamble = 'This is a milti-part message in MIME format.' txt = MIMEText(html.encode('utf-8'), 'html', 'utf-8') msg.attach(txt) if not multiple: msg['To'] = ', '.join(dest) # Sends message smtp = smtplib.SMTP() smtp.connect(host, port) if user: smtp.login(user, pwd) if multiple: for d in dest: del msg['To'] msg['To'] = d logging.info('Sending mail to: %s', d) smtp.sendmail(sender, d, msg.as_string()) else: logging.info('Sending mail to: %s, Cc: %s', dest, cc) smtp.sendmail(sender, dest + cc, msg.as_string()) smtp.quit()
def _process_utf8(self, kw): # sort out what encoding we're going to use encoding = kw.get( 'encoding', self.getProperty('encoding', BaseMailTemplate.default_encoding)) text = self.__class__.__bases__[1].__call__(self, **kw) # ZPT adds newline at the end, but it breaks backward compatibility. # So I remove it. if text and text[-1] == '\n': text = text[:-1] if not self.html() and isinstance(text, unicode): text = text.encode(encoding, 'replace') # now turn the result into a MIMEText object msg = BaseMailTemplate.MIMEText(text.replace('\r', ''), self.content_type.split('/')[1], encoding) # sort out what headers and addresses we're going to use headers = {} values = {} # headers from the headers property for header in getattr(self, 'headers', ()): name, value = header.split(':', 1) headers[name] = value # headers from the headers parameter headers_param = kw.get('headers', {}) headers.update(headers_param) # values and some specific headers for key, header in (('mfrom', 'From'), ('mto', 'To'), ('mcc', 'Cc'), ('mbcc', 'Bcc'), ('subject', 'Subject')): value = kw.get( key, headers_param.get(header, getattr(self, key, headers.get(header)))) if value is not None: values[key] = value # turn some sequences in coma-seperated strings if isinstance(value, (tuple, list)): value = ', '.join(value) # make sure we have no unicode headers if isinstance(value, unicode): value = value.encode(encoding) if key == 'subject': value = make_header([(value, 'utf-8')]).encode() headers[header] = value # check required values have been supplied errors = [] for param in ('mfrom', 'mto', 'subject'): if not values.get(param): errors.append(param) if errors: raise TypeError( 'The following parameters were required by not specified: ' + (', '.join(errors))) # add date header headers['Date'] = BaseMailTemplate.DateTime().rfc822() # add message-id header headers['Message-ID'] = make_msgid() # turn headers into an ordered list for predictable header order keys = headers.keys() keys.sort() return msg, values, [(key, headers[key]) for key in keys]
def hold(mlist, msg, msgdata, annotation): """Hold the message in both Mailman and Launchpad. `annotation` is an arbitrary string required by the API. """ # Hold the message in Mailman and Launchpad so that it's easier to # resubmit it after approval via the LP u/i. If the team administrator # ends up rejecting the message, it will also be easy to discard it on the # Mailman side. But this way, we don't have to reconstitute the message # from the librarian if it gets approved. However, unlike the standard # Moderate handler, we don't craft all the notification messages about # this hold. We also need to keep track of the message-id (which better # be unique) because that's how we communicate about the message's status. request_id = mlist.HoldMessage(msg, annotation, msgdata) assert mlist.Locked(), ( 'Mailing list should be locked: %s' % mlist.internal_name()) # This is a hack because by default Mailman cannot look up held messages # by message-id. This works because Mailman's persistency layer simply # pickles the MailList object, mostly without regard to a known schema. # # Mapping: message-id -> request-id holds = getattr(mlist, 'held_message_ids', None) if holds is None: holds = mlist.held_message_ids = {} message_id = msg.get('message-id') if message_id is None: msg['Message-ID'] = message_id = make_msgid() if message_id in holds: # No legitimate sender should ever give us a message with a duplicate # message id, so treat this as spam. syslog('vette', 'Discarding duplicate held message-id: %s', message_id) raise Errors.DiscardMessage # Discard messages that claim to be from the list itself because Mailman's # internal handlers did not approve the message before it arrived at this # step--these messages are forgeries. list_address = mlist.getListAddress() for sender in msg.get_senders(): if list_address == sender: syslog('vette', 'Discarding forged message-id: %s', message_id) raise Errors.DiscardMessage # Discard messages without text content since there will be nothing to # moderate. Most of these messages are spam. if is_message_empty(msg): syslog('vette', 'Discarding text-less message-id: %s', message_id) raise Errors.DiscardMessage holds[message_id] = request_id # In addition to Message-ID, the librarian requires a Date header. if 'date' not in msg: msg['Date'] = formatdate() # Store the message in the librarian. proxy = XMLRPCRunner.get_mailing_list_api_proxy() # This will fail if we can't talk to Launchpad. That's okay though # because Mailman's IncomingRunner will re-queue the message and re-start # processing at this handler. proxy.holdMessage(mlist.internal_name(), xmlrpclib.Binary(msg.as_string())) syslog('vette', 'Holding message for LP approval: %s', message_id) # Raise this exception, signaling to the incoming queue runner that it is # done processing this message, and should not send it through any further # handlers. raise Errors.HoldMessage
# Processing tobox = ImapMailbox((toserver, toboxname)) fromboxname = sys.argv[1] frombox = mailbox.mbox(fromboxname) frombox.lock() tobox.lock() i = 1 print "Processing mbox file %s with %s messages" % (fromboxname, len(frombox)) for message in frombox: print "%s" % i if message['Message-Id'] is None: print " WARNING: message has no message-id (mesage ID will be added)" message.add_header("Message-Id", make_msgid('katamon.mbox2imap') ) if True: try: tobox.add(message) except ImapNotOkError: print " ERROR: Transaction failed for message %s" % i toserver.reconnect() tobox = ImapMailbox((toserver, toboxname)) time.sleep(5) time.sleep(1) number_in_tobox = len(tobox.get_all_uids()) #time.sleep(1) print " Added Message, %s in mailbox" % number_in_tobox i = i + 1 frombox.close()
def sendmail(request, to, subject, text, **kw): """ Send a mail to the address(es) in 'to', with the given subject and mail body 'text'. Return a tuple of success or error indicator and message. Set a different "From" address with "mail_from=<email>". @param request: the request object @param to: target email address @param subject: subject of email @param text: email body text @rtype: tuple @return: (is_ok, msg) """ _ = request.getText # should not happen, but who knows ... if not config.mail_smarthost: return ( 0, _( "This wiki is not enabled for mail processing. " "Contact the owner of the wiki, who can either enable " 'email, or remove the "Subscribe" icon.' ), ) mail_from = kw.get("mail_from", config.mail_from) or config.mail_from # Create a text/plain message msg = MIMEText(text, "plain", config.charset) msg["From"] = mail_from msg["To"] = ", ".join(to) msg["Date"] = formatdate() try: # only python >= 2.2.2 has this: from email.Header import Header from email.Utils import make_msgid msg["Message-ID"] = make_msgid() msg["Subject"] = Header(subject, config.charset) except ImportError: # this is not standards compliant, but mostly works msg["Subject"] = subject # no message-id. if you still have py 2.2.1, you like it old and broken try: server = smtplib.SMTP(config.mail_smarthost, config.mail_port, config.domain) try: if config.use_tls: server.ehlo() server.starttls() server.ehlo() # server.set_debuglevel(1) if config.mail_smarthost_auth: user, pwd = config.mail_smarthost_auth server.login(user, pwd) server.sendmail(mail_from, to, msg.as_string()) finally: try: server.quit() except AttributeError: # in case the connection failed, SMTP has no "sock" attribute pass except smtplib.SMTPException, e: return (0, str(e))
def build_invitation( self, email_from='', email_to='', subject='', email_cc=[], email_bcc=[], reply_to=False, attachments=None, message_id=None, references=None, object_id=False, headers={}, ): email_from = email_from or tools.config.get('email_from') assert email_from, "You must either provide a sender address explicitly or configure "\ "a global sender address in the server configuration or with the "\ "--email-from startup parameter." msg = MIMEMultipart() if not headers: headers = {} if not message_id: if object_id: message_id = tools.generate_tracking_message_id(object_id) else: message_id = make_msgid() msg['Message-Id'] = encode_header(message_id) if references: msg['references'] = encode_header(references) msg['Subject'] = encode_header(subject) msg['From'] = encode_rfc2822_address_header(email_from) del msg['Reply-To'] if reply_to: msg['Reply-To'] = encode_rfc2822_address_header(reply_to) else: msg['Reply-To'] = msg['From'] msg['To'] = encode_rfc2822_address_header(COMMASPACE.join(email_to)) if email_cc: msg['Cc'] = encode_rfc2822_address_header( COMMASPACE.join(email_cc)) if email_bcc: msg['Bcc'] = encode_rfc2822_address_header( COMMASPACE.join(email_bcc)) msg['Date'] = formatdate() for key, value in headers.items(): msg[ustr(key).encode('utf-8')] = encode_header(value) text_to_body_added = False if attachments: #it is assumed for now that only ics file is attached!!! for fname, fcontent in attachments: if not text_to_body_added and fname == 'invite.ics': # Provide message description in body of message only as text for now; need fixes if 'DESCRIPTION:' in fcontent and 'LOCATION' in fcontent.split( 'DESCRIPTION')[1]: meeting_description_text = fcontent.split( 'DESCRIPTION:')[1].split('LOCATION')[0] text_converted_to_html = self.plaintext2html( meeting_description_text, tabstop=4) text_utf8 = re.sub(r'\\n', "</p><p>", text_converted_to_html) alternative_part = MIMEMultipart(_subtype="alternative") alternative_part.attach( MIMEText(text_utf8, _charset='utf-8', _subtype='html')) msg.attach(alternative_part) #adding invitation stuff part = MIMEBase('text', 'calendar', charset='utf-8', method='REQUEST') part.set_payload(fcontent) msg.attach(part) return msg
def _process(self, kw): # sort out what encoding we're going to use encoding = kw.get('encoding', self.getProperty('encoding', default_encoding)) text = self.__class__.__bases__[1].__call__(self, **kw) # ZPT adds newline at the end, but it breaks backward compatibility. # So I remove it. if text.endswith('\n'): text = text[:-1] if not self.html() and isinstance(text, unicode): text = text.encode(encoding, 'replace') # now turn the result into a MIMEText object msg = MIMEText(text.replace('\r', ''), self.content_type.split('/')[1], encoding) # sort out what headers and addresses we're going to use headers = {} values = {} # headers from the headers property for header in getattr(self, 'headers', ()): name, value = header.split(':', 1) headers[name] = value # headers from the headers parameter headers_param = kw.get('headers', {}) headers.update(headers_param) # values and some specific headers for key, header in (('mfrom', 'From'), ('mto', 'To'), ('mcc', 'Cc'), ('mbcc', 'Bcc'), ('subject', 'Subject')): value = kw.get( key, headers_param.get(header, getattr(self, key, headers.get(header)))) if value is not None: values[key] = value if key == 'subject': try: # Try to keep header non encoded value = Header(value) except UnicodeDecodeError: value = Header(value, "UTF-8") else: dest_list = [] for name, email in getaddresses( (value, ) if isinstance(value, basestring) else value): try: name = Header(name) except UnicodeDecodeError: name = Header(name, "UTF-8") dest_list.append(formataddr((name.encode(), email))) value = ", ".join(dest_list) headers[header] = value # check required values have been supplied errors = [] for param in ('mfrom', 'mto'): if not values.get(param): errors.append(param) if errors: raise TypeError( 'The following parameters were required by not specified: ' + (', '.join(errors))) # add date header headers['Date'] = DateTime().rfc822() # do not let the MTA to generate the Message-ID: # we want to have it stored in ERP5, for mail threading headers['Message-ID'] = make_msgid() # turn headers into an ordered list for predictable header order keys = headers.keys() keys.sort() return msg, values, [(key, headers[key]) for key in keys]