Changeset - d95ef6587bca
[Not reviewed]
beta
0 4 0
Marcin Kuzminski - 13 years ago 2012-06-13 23:44:32
marcin@python-works.com
fixes #481 rhodecode emails are sent without Date header
- small code cleanup in mailing
4 files changed with 17 insertions and 7 deletions:
0 comments (0 inline, 0 general)
docs/changelog.rst
Show inline comments
 
@@ -29,48 +29,49 @@ news
 
  Now based on pygrack & gitweb
 
- Improved RSS/ATOM feeds. Discoverable by browsers using proper headers, and 
 
  reformated based on user suggestions. Additional rss/atom feeds for user
 
  journal
 
- various i18n improvements
 
- #478 permissions overview for admin in user edit view
 

	
 
fixes
 
+++++
 

	
 
- improved translations
 
- fixes issue #455 Creating an archive generates an exception on Windows
 
- fixes #448 Download ZIP archive keeps file in /tmp open and results 
 
  in out of disk space
 
- fixes issue #454 Search results under Windows include proceeding
 
  backslash
 
- fixed issue #450. Rhodecode no longer will crash when bad revision is
 
  present in journal data.
 
- fix for issue #417, git execution was broken on windows for certain
 
  commands.
 
- fixed #413. Don't disable .git directory for bare repos on deleting
 
- fixed issue #459. Changed the way of obtaining logger in reindex task.
 
- fixed #453 added ID field in whoosh SCHEMA that solves the issue of
 
  reindexing modified files
 
- fixes #481 rhodecode emails are sent without Date header 
 

	
 
1.3.6 (**2012-05-17**)
 
----------------------
 

	
 
news
 
++++
 

	
 
- chinese traditional translation
 
- changed setup-app into setup-rhodecode and added arguments for auto-setup 
 
  mode that doesn't need user interaction 
 

	
 
fixes
 
+++++
 

	
 
- fixed no scm found warning
 
- fixed __future__ import error on rcextensions
 
- made simplejson required lib for speedup on JSON encoding
 
- fixes #449 bad regex could get more than revisions from parsing history
 
- don't clear DB session when CELERY_EAGER is turned ON
 

	
 
1.3.5 (**2012-05-10**)
 
----------------------
 

	
 
news
rhodecode/lib/rcmail/message.py
Show inline comments
 
from rhodecode.lib.rcmail.response import MailResponse
 

	
 
from rhodecode.lib.rcmail.exceptions import BadHeaders
 
from rhodecode.lib.rcmail.exceptions import InvalidMessage
 

	
 

	
 
class Attachment(object):
 
    """
 
    Encapsulates file attachment information.
 

	
 
    :param filename: filename of attachment
 
    :param content_type: file mimetype
 
    :param data: the raw file data, either as string or file obj
 
    :param disposition: content-disposition (if any)
 
    """
 

	
 
    def __init__(self,
 
                 filename=None,
 
                 content_type=None,
 
                 data=None,
 
                 disposition=None):
 

	
 
        self.filename = filename
 
        self.content_type = content_type
 
        self.disposition = disposition or 'attachment'
 
        self._data = data
 

	
 
    @property
 
    def data(self):
 
        if isinstance(self._data, basestring):
 
@@ -113,55 +114,55 @@ class Message(object):
 

	
 
        return response
 

	
 
    def is_bad_headers(self):
 
        """
 
        Checks for bad headers i.e. newlines in subject, sender or recipients.
 
        """
 

	
 
        headers = [self.subject, self.sender]
 
        headers += list(self.send_to)
 
        headers += self.extra_headers.values()
 

	
 
        for val in headers:
 
            for c in '\r\n':
 
                if c in val:
 
                    return True
 
        return False
 

	
 
    def validate(self):
 
        """
 
        Checks if message is valid and raises appropriate exception.
 
        """
 

	
 
        if not self.recipients:
 
            raise InvalidMessage, "No recipients have been added"
 
            raise InvalidMessage("No recipients have been added")
 

	
 
        if not self.body and not self.html:
 
            raise InvalidMessage, "No body has been set"
 
            raise InvalidMessage("No body has been set")
 

	
 
        if not self.sender:
 
            raise InvalidMessage, "No sender address has been set"
 
            raise InvalidMessage("No sender address has been set")
 

	
 
        if self.is_bad_headers():
 
            raise BadHeaders
 

	
 
    def add_recipient(self, recipient):
 
        """
 
        Adds another recipient to the message.
 

	
 
        :param recipient: email address of recipient.
 
        """
 

	
 
        self.recipients.append(recipient)
 

	
 
    def add_cc(self, recipient):
 
        """
 
        Adds an email address to the CC list.
 

	
 
        :param recipient: email address of recipient.
 
        """
 

	
 
        self.cc.append(recipient)
 

	
 
    def add_bcc(self, recipient):
 
        """
rhodecode/lib/rcmail/response.py
Show inline comments
 
@@ -343,107 +343,111 @@ def to_message(mail, separator="; "):
 
                            "%r (Python ERROR: %s" %
 
                            (ctype, params, exc.message))
 

	
 
    for k in mail.keys():
 
        if k in ADDRESS_HEADERS_WHITELIST:
 
            out[k.encode('ascii')] = header_to_mime_encoding(
 
                                         mail[k],
 
                                         not_email=False,
 
                                         separator=separator
 
                                     )
 
        else:
 
            out[k.encode('ascii')] = header_to_mime_encoding(
 
                                         mail[k],
 
                                         not_email=True
 
                                    )
 

	
 
    out.extract_payload(mail)
 

	
 
    # go through the children
 
    for part in mail.parts:
 
        out.attach(to_message(part))
 

	
 
    return out
 

	
 

	
 
class MIMEPart(MIMEBase):
 
    """
 
    A reimplementation of nearly everything in email.mime to be more useful
 
    for actually attaching things.  Rather than one class for every type of
 
    thing you'd encode, there's just this one, and it figures out how to
 
    encode what you ask it.
 
    """
 
    def __init__(self, type, **params):
 
        self.maintype, self.subtype = type.split('/')
 
        MIMEBase.__init__(self, self.maintype, self.subtype, **params)
 

	
 
    def add_text(self, content):
 
        # this is text, so encode it in canonical form
 
        try:
 
            encoded = content.encode('ascii')
 
            charset = 'ascii'
 
        except UnicodeError:
 
            encoded = content.encode('utf-8')
 
            charset = 'utf-8'
 

	
 
        self.set_payload(encoded, charset=charset)
 

	
 
    def extract_payload(self, mail):
 
        if mail.body == None: return  # only None, '' is still ok
 
        if mail.body == None:
 
            return  # only None, '' is still ok
 

	
 
        ctype, ctype_params = mail.content_encoding['Content-Type']
 
        cdisp, cdisp_params = mail.content_encoding['Content-Disposition']
 

	
 
        assert ctype, ("Extract payload requires that mail.content_encoding "
 
                       "have a valid Content-Type.")
 

	
 
        if ctype.startswith("text/"):
 
            self.add_text(mail.body)
 
        else:
 
            if cdisp:
 
                # replicate the content-disposition settings
 
                self.add_header('Content-Disposition', cdisp, **cdisp_params)
 

	
 
            self.set_payload(mail.body)
 
            encoders.encode_base64(self)
 

	
 
    def __repr__(self):
 
        return "<MIMEPart '%s/%s': %r, %r, multipart=%r>" % (
 
            self.subtype,
 
            self.maintype,
 
            self['Content-Type'],
 
            self['Content-Disposition'],
 
            self.is_multipart())
 

	
 

	
 
def header_to_mime_encoding(value, not_email=False, separator=", "):
 
    if not value: return ""
 
    if not value:
 
        return ""
 

	
 
    encoder = Charset(DEFAULT_ENCODING)
 
    if type(value) == list:
 
        return separator.join(properly_encode_header(
 
            v, encoder, not_email) for v in value)
 
    else:
 
        return properly_encode_header(value, encoder, not_email)
 

	
 

	
 
def properly_encode_header(value, encoder, not_email):
 
    """
 
    The only thing special (weird) about this function is that it tries
 
    to do a fast check to see if the header value has an email address in
 
    it.  Since random headers could have an email address, and email addresses
 
    have weird special formatting rules, we have to check for it.
 

	
 
    Normally this works fine, but in Librelist, we need to "obfuscate" email
 
    addresses by changing the '@' to '-AT-'.  This is where
 
    VALUE_IS_EMAIL_ADDRESS exists.  It's a simple lambda returning True/False
 
    to check if a header value has an email address.  If you need to make this
 
    check different, then change this.
 
    """
 
    try:
 
        return value.encode("ascii")
 
    except UnicodeEncodeError:
 
        if not_email is False and VALUE_IS_EMAIL_ADDRESS(value):
 
            # this could have an email address, make sure we don't screw it up
 
            name, address = parseaddr(value)
 
            return '"%s" <%s>' % (
 
                encoder.header_encode(name.encode("utf-8")), address)
 

	
 
        return encoder.header_encode(value.encode("utf-8"))
rhodecode/lib/rcmail/smtp_mailer.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
"""
 
    rhodecode.lib.rcmail.smtp_mailer
 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
    Simple smtp mailer used in RhodeCode
 

	
 
    :created_on: Sep 13, 2010
 
    :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
 
    :license: GPLv3, see COPYING for more details.
 
"""
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import time
 
import logging
 
import smtplib
 
from socket import sslerror
 
from email.utils import formatdate
 
from rhodecode.lib.rcmail.message import Message
 

	
 

	
 
class SmtpMailer(object):
 
    """SMTP mailer class
 

	
 
    mailer = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth
 
                        mail_port, ssl, tls)
 
    mailer.send(recipients, subject, body, attachment_files)
 

	
 
    :param recipients might be a list of string or single string
 
    :param attachment_files is a dict of {filename:location}
 
        it tries to guess the mimetype and attach the file
 

	
 
    """
 

	
 
    def __init__(self, mail_from, user, passwd, mail_server, smtp_auth=None,
 
                 mail_port=None, ssl=False, tls=False, debug=False):
 

	
 
        self.mail_from = mail_from
 
        self.mail_server = mail_server
 
        self.mail_port = mail_port
 
        self.user = user
 
        self.passwd = passwd
 
        self.ssl = ssl
 
        self.tls = tls
 
        self.debug = debug
 
        self.auth = smtp_auth
 

	
 
    def send(self, recipients=[], subject='', body='', html='',
 
             attachment_files=None):
 

	
 
        if isinstance(recipients, basestring):
 
            recipients = [recipients]
 
        headers = {
 
            'Date': formatdate(time.time())
 
        }
 
        msg = Message(subject, recipients, body, html, self.mail_from,
 
                      recipients_separator=", ")
 
                      recipients_separator=", ", extra_headers=headers)
 
        raw_msg = msg.to_message()
 

	
 
        if self.ssl:
 
            smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
 
        else:
 
            smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
 

	
 
        if self.tls:
 
            smtp_serv.ehlo()
 
            smtp_serv.starttls()
 

	
 
        if self.debug:
 
            smtp_serv.set_debuglevel(1)
 

	
 
        smtp_serv.ehlo()
 
        if self.auth:
 
            smtp_serv.esmtp_features["auth"] = self.auth
 

	
 
        # if server requires authorization you must provide login and password
 
        # but only if we have them
 
        if self.user and self.passwd:
 
            smtp_serv.login(self.user, self.passwd)
 

	
 
        smtp_serv.sendmail(msg.sender, msg.send_to, raw_msg.as_string())
0 comments (0 inline, 0 general)