Example #1
0
def send_email(sender, recipient, subject, body, host='localhost', port='25', username=None, password=None, header_charset='UTF-8'):
 
    for body_charset in 'US-ASCII', 'ISO-8859-1', 'UTF-8':
        try:
            body.encode(body_charset)
        except UnicodeError:
            pass
        else:
            break

    sender_name, sender_addr = parseaddr(sender)
    recipient_name, recipient_addr = parseaddr(recipient)

    sender_name = str(Header(unicode(sender_name), header_charset))
    recipient_name = str(Header(unicode(recipient_name), header_charset))

    sender_addr = sender_addr.encode('ascii')
    recipient_addr = recipient_addr.encode('ascii')

    msg = MIMEText(body.encode(body_charset), 'plain', body_charset)
    msg['From'] = formataddr((sender_name, sender_addr))
    msg['To'] = formataddr((recipient_name, recipient_addr))
    msg['Subject'] = Header(unicode(subject), header_charset)

    smtp = SMTP('{host}:{port}'.format(host=host, port=port))
    smtp.starttls()
    if username and password:
        smtp.login(username,password)
    smtp.sendmail(sender, recipient, msg.as_string())
    smtp.quit()
Example #2
0
    def send(self):
        body = self.template.render(**self.vars)

        header_charset = 'ISO-8859-1'
        body_charset = 'UTF-8'

        sender_name, sender_addr = parseaddr(self.sender)
        recipient_name, recipient_addr = parseaddr(self.recipient)

        # We must always pass Unicode strings to Header, otherwise it will
        # use RFC 2047 encoding even on plain ASCII strings.
        sender_name = str(Header(unicode(sender_name), header_charset))
        recipient_name = str(Header(unicode(recipient_name), header_charset))

        # Make sure email addresses do not contain non-ASCII characters
        sender_addr = sender_addr.encode('ascii')
        recipient_addr = recipient_addr.encode('ascii')

        # Create the message ('plain' stands for Content-Type: text/plain)
        msg = MIMEText(body, 'plain', body_charset)
        msg['From'] = formataddr((sender_name, sender_addr))
        msg['To'] = formataddr((recipient_name, recipient_addr))
        msg['Subject'] = Header(unicode(self.subject), header_charset)

        with SMTP() as smtp:
            smtp.connect(self.smtp)
            smtp.sendmail(self.sender, self.recipient, msg.as_string())
Example #3
0
def sendEmail(server, port, login, password, sender, recipient, subject, body):
    """Send e-mail function with unicode support"""
    headerCharset = "ISO-8859-1"

    for bodyCharset in ("US-ASCII", "ISO-8859-1", "UTF-8"):
        try:
            body.encode(bodyCharset)

        except UnicodeError:
            pass

        else:
            break

    senderName, senderAddr = parseaddr(sender)
    recipientName, recipientAddr = parseaddr(recipient)

    senderName = str(Header(unicode(senderName), headerCharset))
    recipientName = str(Header(unicode(recipientName), headerCharset))

    senderAddr = senderAddr.encode("ascii")
    recipientAddr = recipientAddr.encode("ascii")

    msg = MIMEText(body.encode(bodyCharset), "html", bodyCharset)
    msg["From"] = formataddr((senderName, senderAddr))
    msg["To"] = formataddr((recipientName, recipientAddr))
    msg["Subject"] = Header(unicode(subject), headerCharset)

    server = smtplib.SMTP(server, port)
    server.login(login, password)
    server.sendmail(sender, recipient, msg.as_string())
    server.quit()
Example #4
0
def send_email(sender, recipient, subject, body):
   from smtplib import SMTP
   from email.MIMEText import MIMEText
   from email.Header import Header
   from email.Utils import parseaddr, formataddr
   # Header class is smart enough to try US-ASCII, then the charset we
   # provide, then fall back to UTF-8.
   header_charset = 'ISO-8859-1'
   # We must choose the body charset manually
   for body_charset in 'UTF-8', 'ISO-8859-1', 'US-ASCII'  :
      try:
         body.encode(body_charset)
      except UnicodeError:
         pass
      else:
         break
   # Split real name (which is optional) and email address parts
   sender_name, sender_addr = parseaddr(sender)
   recipient_name, recipient_addr = parseaddr(recipient)
   # We must always pass Unicode strings to Header, otherwise it will
   # use RFC 2047 encoding even on plain ASCII strings.
   sender_name = str(Header(unicode(sender_name), header_charset))
   recipient_name = str(Header(unicode(recipient_name), header_charset))
   # Make sure email addresses do not contain non-ASCII characters
   sender_addr = sender_addr.encode('ascii')
   recipient_addr = recipient_addr.encode('ascii')
   # Create the message ('plain' stands for Content-Type: text/plain)
   msg = MIMEText(body.encode(body_charset), 'plain', body_charset)
   msg['From'] = formataddr((sender_name, sender_addr))
   msg['To'] = formataddr((recipient_name, recipient_addr))
   msg['Subject'] = Header(unicode(subject), header_charset)
   # Send the message via SMTP to localhost:25
   smtp = SMTP("localhost")
   smtp.sendmail(sender, recipient, msg.as_string())
   smtp.quit()
Example #5
0
File: mail.py Project: mysoftpro/pc
def send_email(recipient, subject, body, sender=u'Компьютерный магазин <*****@*****.**>'):
    """Send an email.

    All arguments should be Unicode strings (plain ASCII works as well).

    Only the real name part of sender and recipient addresses may contain
    non-ASCII characters.

    The email will be properly MIME encoded and delivered though SMTP to
    localhost port 25.  This is easy to change if you want something different.

    The charset of the email will be the first one out of US-ASCII, ISO-8859-1
    and UTF-8 that can represent all the characters occurring in the email.
    """

    # Header class is smart enough to try US-ASCII, then the charset we
    # provide, then fall back to UTF-8.
    header_charset = 'ISO-8859-1'

    # We must choose the body charset manually
    for body_charset in 'US-ASCII', 'ISO-8859-1', 'UTF-8':
	try:
	    body.encode(body_charset)
	except UnicodeError:
	    pass
	else:
	    break

    # Split real name (which is optional) and email address parts
    sender_name, sender_addr = parseaddr(sender)
    recipient_name, recipient_addr = parseaddr(recipient)

    # We must always pass Unicode strings to Header, otherwise it will
    # use RFC 2047 encoding even on plain ASCII strings.
    sender_name = str(Header(unicode(sender_name), header_charset))
    recipient_name = str(Header(unicode(recipient_name), header_charset))

    # Make sure email addresses do not contain non-ASCII characters
    sender_addr = sender_addr.encode('ascii')
    recipient_addr = recipient_addr.encode('ascii')

    # Create the message ('plain' stands for Content-Type: text/plain)
    #msg = MIMEText(body.encode(body_charset), 'plain', body_charset)
    msg = MIMEMultipart('alternative')
    part = MIMEText(body.encode(body_charset), 'html', body_charset)
    msg.attach(part)

    msg['From'] = formataddr((sender_name, sender_addr))
    msg['To'] = formataddr((recipient_name, recipient_addr))
    msg['Subject'] = Header(unicode(subject), body_charset)

    msg = StringIO(str(msg))
    d = defer.Deferred()
    factory = ESMTPSenderFactory(sender_addr, mailpassword, sender_addr, recipient, msg, d, requireTransportSecurity=False)
    reactor.connectTCP('smtp.yandex.ru', 587, factory)
    return d
Example #6
0
def send_email(sender, recipient, subject, body, content_type="plain"):
    """Send an email.

    All arguments should be Unicode strings (plain ASCII works as well).

    Only the real name part of sender and recipient addresses may contain
    non-ASCII characters.

    The email will be properly MIME encoded and delivered though SMTP to
    localhost port 25.  This is easy to change if you want something different.

    The charset of the email will be the first one out of US-ASCII, ISO-8859-1
    and UTF-8 that can represent all the characters occurring in the email.
    """

    # Header class is smart enough to try US-ASCII, then the charset we
    # provide, then fall back to UTF-8.
    header_charset = "ISO-8859-1"

    # We must choose the body charset manually
    for body_charset in "US-ASCII", "ISO-8859-1", "UTF-8":
        try:
            body.encode(body_charset)
        except UnicodeError:
            pass
        else:
            break

    # Split real name (which is optional) and email address parts
    sender_name, sender_addr = parseaddr(sender)
    recipient_name, recipient_addr = parseaddr(recipient)

    # We must always pass Unicode strings to Header, otherwise it will
    # use RFC 2047 encoding even on plain ASCII strings.
    sender_name = str(Header(unicode(sender_name), header_charset))
    recipient_name = str(Header(unicode(recipient_name), header_charset))

    # Make sure email addresses do not contain non-ASCII characters
    sender_addr = sender_addr.encode("ascii")
    recipient_addr = recipient_addr.encode("ascii")

    # Create the message ('plain' stands for Content-Type: text/plain)
    msg = MIMEText(body.encode(body_charset), content_type, body_charset)
    msg["From"] = formataddr((sender_name, sender_addr))
    msg["To"] = formataddr((recipient_name, recipient_addr))
    msg["Subject"] = Header(unicode(subject), header_charset)

    try:
        # Send the message via SMTP to localhost:25
        smtp = SMTP("127.0.0.1")
        smtp.ehlo()
        smtp.sendmail(sender_addr, recipient, msg.as_string())
        smtp.quit()
    except Exception as ex:
        pass
def get_maildata(sender, recipient, subject, body):
    # for python<2.7
    # source: http://mg.pov.lt/blog/unicode-emails-in-python
    sender_name, sender_addr = parseaddr(sender)
    recipient_name, recipient_addr = parseaddr(recipient)
    sender_name = str(Header(unicode(sender_name), 'utf8'))
    recipient_name = str(Header(unicode(recipient_name), 'utf8'))
    sender_addr = sender_addr.encode('ascii')
    recipient_addr = recipient_addr.encode('ascii')
    msg = MIMEText(body.encode('utf8'), 'html', 'utf8')
    msg['From'] = formataddr((sender_name, sender_addr))
    msg['To'] = formataddr((recipient_name, recipient_addr))
    msg['Subject'] = Header(unicode(subject), 'utf8')
    return msg.as_string()
Example #8
0
    def __init__(self,
                 context,
                 message,
                 ):
        """
        Extract the bits of interest from an RFC2822 message string.

        This perhaps should do the isJunk test up front to avoid
        unnecessary resource usage.
        """
        BLATHER('mailin.py processing incoming message:\n%s' % message)
        #BLATHER('mailin.py processing incoming message')
        self.context = context
        self.original = message
        self.msg = email.message_from_string(self.original)
        self.date = self.msg['Date']
        self.subject = re.sub(r'\n',r'',self.msg.get('Subject',''))
        self.realSubject = re.sub(r'.*?\[.*?\] ?(.*)',r'\1',self.subject)
        self.messageid = self.msg.get('Message-id','')
        self.inreplyto = self.msg.get('In-reply-to','')
        self.From = self.msg.get('From')
        self.FromRealName = parseaddr(self.From)[0]
        self.FromEmail    = parseaddr(self.From)[1]
        self.FromUserName = (self.FromRealName or
                             re.sub(r'@.*$',r'',self.FromEmail))
        self.sender = self.msg.get('Sender')
        self.senderEmail = (self.sender and
                            parseaddr(self.sender)[1]) or None
        tos = self.msg.get_all('to', [])
        ccs = self.msg.get_all('cc', [])
        resent_tos = self.msg.get_all('resent-to', [])
        resent_ccs = self.msg.get_all('resent-cc', [])
        self.recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)

        # mailing list support
        # XXX x-beenthere is mailman-specific - need to support ezmlm & others here
        #self.xbeenthere = (self.msg.get('X-BeenThere') or
        #                   re.search(r'[^\s<]+@[^\s>]+',self.msg.get('Delivered-To')).group())
        # ..Type Error - configured ezmlm to provide beenthere instead (?)
        self.xbeenthere = self.msg.get('X-BeenThere')

        # raises an exception if there's no text part
        try:
            plaintextpart = typed_subpart_iterator(self.msg,
                                                   'text',
                                                   'plain').next().get_payload(decode=1)
        except StopIteration:
            plaintextpart = ''
        self.body = self.cleanupBody(plaintextpart)
Example #9
0
    def sendmail(self, toaddr, subject, body, cc=[], bcc=[]):

        if not self.server:
            self.connect()

        logging.info( 'Send mail to %s' % toaddr )

        for body_charset in 'US-ASCII', 'ISO-8859-1', 'UTF-8':
            try:
                body.encode(body_charset)
            except UnicodeError:
                pass
            else:
                break

        from_name, from_addr = parseaddr( self.fromaddr )
        to_name  , to_addr   = parseaddr( toaddr )

        from_name = str(Header(unicode(from_name), self.header_charset))
        to_name = str(Header(unicode(to_name), self.header_charset))

        from_addr = from_addr.encode('ascii')
        to_addr = to_addr.encode('ascii')

        if from_addr == to_addr:
            logging.info( 'Send mail to myself is not allowed now.')
            return

        email_format = 'html' if self.HTML else 'plain'
            
        msg = MIMEText( body.encode(body_charset), email_format, body_charset )
        msg['From'] = formataddr((from_name, from_addr))
        msg['To'] = formataddr((to_name, to_addr))

        if cc:
            msg['CC'] = ', '.join([ self._formataddr(x) for x in cc ])

        if bcc:
            msg['BCC'] = ', '.join([ self._formataddr(x) for x in bcc ])

        msg['Subject'] = Header(unicode(subject), self.header_charset)
        msg['date'] = time.strftime('%a, %d %b %Y %H:%M:%S %z')

        try:
            self.server.sendmail( self.fromaddr, [toaddr] + cc + bcc, msg.as_string() )
        except Exception, e:
            logging.error( 'Send mail from %s:%s to %s failed: %s' % (
                    self.host, self.port, toaddr, e) )
Example #10
0
 def parse_from(self,from_name):
     nick,email=parseaddr(from_name)
     if not nick:
         nick=email
     if not nick and not email:
         nick,email = old_parse_from(from_name)
     return nick,email
Example #11
0
def get_or_create_contact(contact_email):
    name, email = contact_email
    email=email.lower()
    names = []
    defaults={}
    if not name:
        name = extract_name(email)
    if name:
        names = name.split(' ')
    
    _name, email = parseaddr(email)
    
    if len(names) > 1:
        defaults['first_name'] = names[0]
        defaults['last_name'] = ' '.join(names[1:])
    
    try:
        contact = Contact.objects.get(email__iexact=email)
    except Contact.DoesNotExist:
        contact = Contact(email=email)
        contact.save()
    
    if not contact.user and User.objects.filter(email__iexact=email).exists():
        contact.user = User.objects.filter(email__iexact=email)[0]
        contact.save()
    
    if not contact.first_name and 'first_name' in defaults:
        contact.first_name = defaults.get('first_name')[:100]
    if not contact.last_name and 'last_name' in defaults:
        contact.last_name = defaults.get('last_name')[:100]
    contact.save()

    return contact
	def fetch_current_subscribers(self):
		"""
		Fetch the current list of subscribers by calling out to the majordomo
		server and scrape the result of the 'who-short' command.
		"""

		f = urlopen("https://%s/mj/mj_wwwadm?passw=%s&list=%s&func=who-short" %
			(self.mjhost, self.listpwd, self.listname))
		s = f.read()
		f.close()

		# Ugly screen-scraping regexp hack
		resub = re.compile('list administration<br>\s+</p>\s+<pre>([^<]+)</pre>')
		m = resub.findall(s)
		if len(m) != 1:
			if s.find("<!-- Majordomo who_none format file -->") > 0:
				# Nobody on the list yet
				return set()
			raise Exception("Could not find list of subscribers")

		# Deal with those HTML entities that are set by the majordomo server.
		s = m[0].replace('&lt;','<').replace('&gt;','>').replace('&quot;','"')

		# Parse all email addresses to make sure we can deal with both the
		# "*****@*****.**" and "Joe User <*****@*****.**>" formats. Return
		# the address part only, as a unique set.
		return set([parseaddr(a)[1] for a in re.split('[\r\n]+',s) if a])
Example #13
0
    def add_members (self, save=False):
        """Add any email addressses that are provided in the create form."""

        if self.welcome == '':
            text = ('Welcome to %s. Visit the List Server to ' +
            'manage your subscriptions') % self.ln
        else:
            text = self.welcome

        for key in self.cgidata.keys():
            if re.match('^lc_member_', key):
                fn, em = parseaddr(self.cgival(key).lower().strip())
                userdesc = UserDesc(em, fn, mm_cfg.SSO_STOCK_USER_PWD, False)
                try:
                    self.ml.ApprovedAddMember(userdesc, True, text,
                                              whence='SSO List Creation Time')
                    syslog('sso',
                           'Successfully added %s to list: %s' % (em,
                                                                  self.ln))
                except Errors.MMAlreadyAMember:
                    ## FIXME: Need to find some way of communicating this
                    ## case to the user. As thisi s a new list, this can only
                    ## happen if the same address is given by the admin... hm
                    syslog('sso',
                           '%s already a member of listL %s' % (em, self.ln))

        if save:
            self.ml.Save()
Example #14
0
    def _decompose_addr(self, addr):
        addr = addr.replace("[", "").replace("]", "")
        (name, email) = parseaddr(addr)

        # The eMail parser cannot handle Surname, Name <*****@*****.**> properly.
        # Provide a fixup hack for this case
        if (name == "" or email.count("@") == 0):
            m = re.search(self.fixup_emailPattern, addr)
            if m:
                name = m.group(1)
                email = m.group(2)
                m2 = re.search(self.commaNamePattern, name)
                if m2:
                    # Replace "Surname, Name" by "Name Surname"
                    name = "{0} {1}".format(m2.group(2), m2.group(1))

#                print "Fixup for addr {0} required -> ({1}/{2})".format(addr, name, email)
            else:
                # In this case, no eMail address was specified.
#                print("Fixup for email required, but FAILED for {0}".format(addr))
                name = addr
                rand_str = "".join(random.choice(string.ascii_lowercase + string.digits)
                                   for i in range(10))
                email = "could.not.resolve@"+rand_str

        email = email.lower()

        name = self._cleanName(name)
        email = self._cleanName(email)

        return (name, email)
Example #15
0
    def edit_members (self):
        oldm = self.ml.getRegularMemberKeys()
        newm = []
        for key in self.cgidata.keys():
            if re.match('^lc_member_', key):
                fn, em = parseaddr(self.cgival(key).lower())
                newm.append(em)

        remove = [x for x in oldm if not x in newm]
        insert = [x for x in newm if not x in oldm]

        syslog('sso', 'Will remove %s from list %s' % (remove, self.ln))
        syslog('sso', 'Will add %s to list %s' % (insert, self.ln))

        for em in remove:
            self.ml.ApprovedDeleteMember(em, whence='SSO Web Interface')

        for em in insert:
            userdesc = UserDesc(em, '', mm_cfg.SSO_STOCK_USER_PWD, False)
            try:
                self.ml.ApprovedAddMember(userdesc, True, self.welcome,
                                          whence='SSO List Edit')
                syslog('sso',
                       'Successfully added %s to list: %s' % (em,
                                                              self.ln))
            except Errors.MMAlreadyAMember:
                syslog('sso',
                       'request_edit: %s already a member of list %s' % (em,
                                                                         self.ln))
def process(msg):
    # Yahoo! bounces seem to have a known subject value and something called
    # an x-uidl: header, the value of which seems unimportant.
    sender = parseaddr(msg.get('from', '').lower())[1] or ''
    if not sender.startswith('mailer-daemon@yahoo'):
        return None
    addrs = []
    # simple state machine
    #     0 == nothing seen
    #     1 == tag line seen
    state = 0
    for line in email.Iterators.body_line_iterator(msg):
        line = line.strip()
        if state == 0 and tcre.match(line):
            state = 1
        elif state == 1:
            mo = acre.match(line)
            if mo:
                addrs.append(mo.group('addr'))
                continue
            mo = ecre.match(line)
            if mo:
                # we're at the end of the error response
                break
    return addrs
Example #17
0
 def check_from(self, arguments,
                all_numeric=re.compile('[0-9][0-9]+$')):
     assert not arguments, arguments
     from email.Utils import parseaddr
     text = self.message.get('From')
     pair = text and parseaddr(text)
     if not pair or not pair[1]:
         self.checker.reject("Missing From.")
         return
     text = pair[1].lower()
     if not text:
         self.checker.reject("Empty From.")
         return
     fields = text.split('@')
     if len(fields) != 2:
         self.checker.reject("Invalid From `%s'." % text)
         return
     user, domain = fields
     if all_numeric.match(user):
         self.checker.reject("All numeric user `%s'." % user)
     fields = domain.split('.')
     if len(fields) < 2:
         self.checker.reject("Domain `%s' not fully qualified." % domain)
         return
     if domain == 'hotmail.com':
         if not self.message.get('X-Originating-IP'):
             self.checker.reject("Forged From `hotmail.com'.")
Example #18
0
def verp_probe(mlist, msg):
    bmailbox, bdomain = Utils.ParseEmail(mlist.GetBouncesEmail())
    # Sadly not every MTA bounces VERP messages correctly, or consistently.
    # Fall back to Delivered-To: (Postfix), Envelope-To: (Exim) and
    # Apparently-To:, and then short-circuit if we still don't have anything
    # to work with.  Note that there can be multiple Delivered-To: headers so
    # we need to search them all (and we don't worry about false positives for
    # forwarded email, because only one should match VERP_REGEXP).
    vals = []
    for header in ('to', 'delivered-to', 'envelope-to', 'apparently-to'):
        vals.extend(msg.get_all(header, []))
    for field in vals:
        to = parseaddr(field)[1]
        if not to:
            continue                          # empty header
        mo = re.search(mm_cfg.VERP_PROBE_REGEXP, to)
        if not mo:
            continue                          # no match of regexp
        try:
            if bmailbox <> mo.group('bounces'):
                continue                      # not a bounce to our list
            # Extract the token and see if there's an entry
            token = mo.group('token')
            data = mlist.pend_confirm(token, expunge=False)
            if data is not None:
                return token
        except IndexError:
            syslog(
                'error',
                "VERP_PROBE_REGEXP doesn't yield the right match groups: %s",
                mm_cfg.VERP_PROBE_REGEXP)
    return None
Example #19
0
 def check_blacklists(self, blacklists,
                      parsed_flag=[]):
     # Extract the From domain.
     text = self.message.get('From')
     if text is None:
         return
     from email.Utils import parseaddr
     pair = parseaddr(text)
     if not pair or not pair[1]:
         return
     fields = pair[1].lower().split('@')
     if len(fields) != 2:
         return
     user, domain = fields
     # Get a list of IP addresses.
     from . import DNS
     if not parsed_flag:
         DNS.ParseResolvConf()
         parsed_flag.append(None)
     numeric_ips = []
     for answer in DNS.Request(domain, qtype='a').req().answers:
         numeric_ips.append(answer['data'])
     if not numeric_ips:
         if not DNS.Request(domain, qtype='mx').req().answers:
             self.checker.reject("Domain `%s' is not resolved." % domain)
     # Validate IP addresses against blacklists.
     for numeric_ip in numeric_ips:
         fragments = numeric_ip.split('.')
         fragments.reverse()
         for blacklist in blacklists:
             if '.' not in blacklist:
                 blacklist += '.mail-abuse.org'
             domain = '.'.join(fragments) + '.' + blacklist
             if DNS.Request(domain, qtype='a').req().answers:
                 self.checker.reject("Listed in `%s'." % blacklist)
Example #20
0
    def addUser(self, login, name = None, last_name = None, email = None, 
            passwd = None, sha_passwd = None, note = None):

        if not email:
            raise ScalakError("You have to supply user email.")


        # Some not really nice mailman hacks
        # Need to import Mailman code 
        sys.path.insert(0, self._mailman_dir)
        sitedir = os.path.join(sys.prefix, 'lib', 'python'+sys.version[:3],
                'site-packages')
        sys.path.append(sitedir)
        from Mailman import mm_cfg
        from Mailman import MailList
        from Mailman import Utils
        from Mailman import Errors
        from Mailman import Message
        from Mailman import i18n
        from email.Utils import parseaddr

        class UserDesc: pass

        if not Utils.list_exists(self.listname):
            raise ScalakError("No such mailing list")

        mlist = MailList.MailList(self.listname)
        usr = UserDesc()
        usr.fullname, usr.address = parseaddr(email)
        usr.digest = 0
        try:
            mlist.ApprovedAddMember(usr, 0, 0)
            mlist.Save()
        finally:
            mlist.Unlock()
Example #21
0
def get_sender(header):
    replyto = header.get("Reply-To")
    if replyto is None:
        replyto = header.get("From")
        if replyto is None: return None
    x = parseaddr(replyto)
    return x[1]
Example #22
0
def encode_rfc2822_address_header(header_text):
    """If ``header_text`` contains non-ASCII characters,
       attempts to locate patterns of the form
       ``"Name" <address@domain>`` and replace the
       ``"Name"`` portion by the RFC2047-encoded
       version, preserving the address part untouched.
    """
    header_text_utf8 = tools.ustr(header_text).encode('utf-8')
    header_text_ascii = try_coerce_ascii(header_text_utf8)
    if header_text_ascii:
        return header_text_ascii

    name, email = parseaddr(header_text_utf8)
    if not name:
      return email

    # non-ASCII characters are present, attempt to
    # replace all "Name" patterns with the RFC2047-
    # encoded version
    name_encoded = str(Header(name, 'utf-8'))
    header_text_utf8 = "%s <%s>" % (name_encoded, email)
    # try again after encoding
    header_text_ascii = try_coerce_ascii(header_text_utf8)
    if header_text_ascii:
        return header_text_ascii
    # fallback to extracting pure addresses only, which could
    # still cause a failure downstream if the actual addresses
    # contain non-ASCII characters
    return COMMASPACE.join(extract_rfc2822_addresses(header_text_utf8))
Example #23
0
 def sender(self):
     """ get the email address of the sender
     """
     sender = utils.get_header(self.msg(), 'Resent-From')
     if not sender:
         sender = utils.get_header(self.msg(), 'From')
     (sender_name, sender_address) = parseaddr(sender)
     return sender_address
Example #24
0
def create_email(sender, recipient, bcc, subject, body):
    """Create an email message.

    All arguments should be Unicode strings (plain ASCII works as well).

    Only the real name part of sender and recipient addresses may contain
    non-ASCII characters.

    The charset of the email will be the UTF-8.
    """

    header_charset = 'UTF-8'
    body_charset = 'UTF-8'

    body.encode(body_charset)

    # Split real name (which is optional) and email address parts
    sender_name, sender_addr = parseaddr(sender)
    recipient_name, recipient_addr = parseaddr(recipient)

    # We must always pass Unicode strings to Header, otherwise it will
    # use RFC 2047 encoding even on plain ASCII strings.
    sender_name = str(Header(unicode(sender_name), header_charset))
    recipient_name = str(Header(unicode(recipient_name), header_charset))

    # Make sure email addresses do not contain non-ASCII characters
    sender_addr = sender_addr.encode('ascii')
    recipient_addr = recipient_addr.encode('ascii')

    bcc_pairs = []
    for bcc_mail in bcc:
        bcc_name, bcc_addr = parseaddr(bcc_mail)
        bcc_name = str(Header(unicode(bcc_name), header_charset))
        bcc_addr = bcc_addr.encode('ascii')
        bcc_pairs.append((bcc_name, bcc_addr))

    # Create the message ('plain' stands for Content-Type: text/plain)
    msg = MIMEText(body.encode(body_charset), 'plain', body_charset)
    msg['From'] = formataddr((sender_name, sender_addr))
    msg['To'] = formataddr((recipient_name, recipient_addr))
    msg['Bcc'] = ', '.join(formataddr((bcc_name, bcc_addr))
                                     for bcc_name, bcc_addr
                                     in bcc_pairs)
    msg['Subject'] = Header(unicode(subject), header_charset)

    return msg
Example #25
0
    def get_owners (self):
        ret = []
        for key in self.cgidata.keys():
            if re.match('^lc_owner', key):
                fn, em = parseaddr(self.cgival(key).lower().strip())
                ret.append(em)

        return ret
Example #26
0
def determine_sender(mail, action='reply'):
    """
    Inspect a given mail to reply/forward/bounce and find the most appropriate
    account to act from and construct a suitable From-Header to use.

    :param mail: the email to inspect
    :type mail: `email.message.Message`
    :param action: intended use case: one of "reply", "forward" or "bounce"
    :type action: str
    """
    assert action in ['reply', 'forward', 'bounce']
    realname = None
    address = None

    # get accounts
    my_accounts = settings.get_accounts()
    assert my_accounts, 'no accounts set!'

    # extract list of recipients to check for my address
    rec_to = filter(lambda x: x, mail.get('To', '').split(','))
    rec_cc = filter(lambda x: x, mail.get('Cc', '').split(','))
    delivered_to = mail.get('Delivered-To', None)
    recipients = rec_to + rec_cc
    if delivered_to is not None:
        recipients.append(delivered_to)

    logging.debug('recipients: %s' % recipients)
    # pick the most important account that has an address in recipients
    # and use that accounts realname and the found recipient address
    for account in my_accounts:
        acc_addresses = account.get_addresses()
        for alias in acc_addresses:
            if realname is not None:
                break
            regex = re.compile(alias)
            for rec in recipients:
                seen_name, seen_address = parseaddr(rec)
                if regex.match(seen_address):
                    logging.debug("match!: '%s' '%s'" % (seen_address, alias))
                    if settings.get(action + '_force_realname'):
                        realname = account.realname
                    else:
                        realname = seen_name
                    if settings.get(action + '_force_address'):
                        address = account.address
                    else:
                        address = seen_address

    # revert to default account if nothing found
    if realname is None:
        account = my_accounts[0]
        realname = account.realname
        address = account.address
    logging.debug('using realname: "%s"' % realname)
    logging.debug('using address: %s' % address)

    from_value = address if realname == '' else '%s <%s>' % (realname, address)
    return from_value, account
Example #27
0
def encode_emailaddress(address):
    """
    Encode an email address as ASCII using the Encoded-Word standard.
    Needed to work around http://code.djangoproject.com/ticket/11144
    """
    try: return address.encode('ascii')
    except UnicodeEncodeError: pass
    nm, addr = parseaddr(address)
    return formataddr( (str(Header(nm, settings.DEFAULT_CHARSET)), addr) )
Example #28
0
    def _split_address(address):
        """Split an username + email address into its parts.

        This takes "Joe Foo <*****@*****.**>" and returns "Joe Foo",
        "*****@*****.**".
        :param address: A combined username
        :return: (username, email)
        """
        return parseaddr(address)
Example #29
0
File: mail.py Project: algby/ietfdb
def send_smtp(msg, bcc=None):
    '''
    Send a Message via SMTP, based on the django email server settings.
    The destination list will be taken from the To:/Cc: headers in the
    Message.  The From address will be used if present or will default
    to the django setting DEFAULT_FROM_EMAIL

    If someone has set test_mode=True, then append the msg to
    the outbox.
    '''
    add_headers(msg)
    (fname, frm) = parseaddr(msg.get('From'))
    addrlist = msg.get_all('To') + msg.get_all('Cc', [])
    if bcc:
        addrlist += [bcc]
    to = [addr for name, addr in getaddresses(addrlist) if ( addr != '' and not addr.startswith('unknown-email-') )]
    if not to:
        log("No addressees for email from '%s', subject '%s'.  Nothing sent." % (frm, msg.get('Subject', '[no subject]')))
    else:
        if test_mode:
            outbox.append(msg)
        server = None
        try:
            server = smtplib.SMTP()
            #log("SMTP server: %s" % repr(server))
            #if settings.DEBUG:
            #    server.set_debuglevel(1)
            conn_code, conn_msg = server.connect(SMTP_ADDR['ip4'], SMTP_ADDR['port'])
            #log("SMTP connect: code: %s; msg: %s" % (conn_code, conn_msg))
            if settings.EMAIL_HOST_USER and settings.EMAIL_HOST_PASSWORD:
                server.ehlo()
                if 'starttls' not in server.esmtp_features:
                    raise ImproperlyConfigured('password configured but starttls not supported')
                (retval, retmsg) = server.starttls()
                if retval != 220:
                    raise ImproperlyConfigured('password configured but tls failed: %d %s' % ( retval, retmsg ))
                # Send a new EHLO, since without TLS the server might not
                # advertise the AUTH capability.
                server.ehlo()
                server.login(settings.EMAIL_HOST_USER, settings.EMAIL_HOST_PASSWORD)
            unhandled = server.sendmail(frm, to, msg.as_string())
            if unhandled != {}:
                raise SMTPSomeRefusedRecipients(message="%d addresses were refused"%len(unhandled),original_msg=msg,refusals=unhandled)
        except Exception as e:
            # need to improve log message
            log("Exception while trying to send email from '%s' to %s subject '%s'" % (frm, to, msg.get('Subject', '[no subject]')))
            if isinstance(e, smtplib.SMTPException):
                e.original_msg=msg
                raise 
            else:
                raise smtplib.SMTPException({'really': sys.exc_info()[0], 'value': sys.exc_info()[1], 'tb': traceback.format_tb(sys.exc_info()[2])})
        finally:
            try:
                server.quit()
            except smtplib.SMTPServerDisconnected:
                pass
        log("sent email from '%s' to %s subject '%s'" % (frm, to, msg.get('Subject', '[no subject]')))
Example #30
0
def normAddressList(txt, assumedCharset=_7BIT_CHARSET):
    items = []
    if txt:
        if type(txt) == _UNICODE_TYPE or type(txt) == _STR_TYPE:
            txt, assumedCharset = safelyEncode(txt, assumedCharset)
            txt = txt.replace(';', ',')
            items = [formataddr(parseaddr(x.strip())) for x in txt.split(',') if x.strip()]
        else:
            assert(type(txt) == type([]))
            items = txt
            newItems = []
            for addr in items:
                txt, assumedCharset = safelyEncode(txt, assumedCharset)
                newItems.append(formataddr(parseaddr(txt)))
            items = newItems
        items = _getUnique(items)
        items.sort()
    return items, assumedCharset
Example #31
0
def send_email(toaddr, subject, body, cc = [], bcc = []):

    global email_from, smtp_server, smtp_port, \
        email_username, email_pass

    # TODO: something check
    if not smtp_server:
        return False

    fromaddr = email_from

    # Header class is smart enough to try US-ASCII, then the charset we
    # provide, then fall back to UTF-8.
    header_charset = 'ISO-8859-1'

    # We must choose the body charset manually
    for body_charset in 'US-ASCII', 'ISO-8859-1', 'UTF-8':
        try:
            body.encode(body_charset)
        except UnicodeError:
            pass
        else:
            break

    from_name, from_addr = parseaddr(fromaddr)
    to_name, to_addr = parseaddr(toaddr)

    from_name = str(Header(unicode(from_name), header_charset))
    to_name = str(Header(unicode(to_name), header_charset))

    from_addr = from_addr.encode('ascii')
    to_addr = to_addr.encode('ascii')

    msg = MIMEText(body.encode(body_charset), 'plain', body_charset)
    msg['From'] = formataddr((from_name, from_addr))
    msg['To'] = formataddr((to_name, to_addr))

    if cc:
        L = []
        for x in cc:
            x_name, x_addr = parseaddr(x)
            L.append( formataddr((x_name, x_addr)) )
        msg['CC'] = ', '.join(L)

    if bcc:
        L = []
        for x in bcc:
            x_name, x_addr = parseaddr(x)
            L.append( formataddr((x_name, x_addr)) )
        msg['BCC'] = ', '.join(L)

    msg['Subject'] = Header(unicode(subject), header_charset)
    msg['date']=time.strftime('%a, %d %b %Y %H:%M:%S %z')

    toaddrs = [toaddr] + cc + bcc

    smtp = SMTP(smtp_server, smtp_port)

    #smtp.ehlo()

    if smtp_server.endswith('gmail.com'):
        smtp.starttls()

    #smtp.ehlo()

    if not ( smtp_server.endswith('127.0.0.1') or
             smtp_server.endswith('localhost') ):
        smtp.login(email_username, email_pass)

    smtp.sendmail(fromaddr, toaddrs, msg.as_string())
    smtp.quit()
    return True
Example #32
0
def send(sender,
         recipient,
         subject,
         body,
         contenttype,
         extraheaders=None,
         smtpserver=None):
    """Send an email.

	All arguments should be Unicode strings (plain ASCII works as well).

	Only the real name part of sender and recipient addresses may contain
	non-ASCII characters.

	The email will be properly MIME encoded and delivered though SMTP to
	localhost port 25.  This is easy to change if you want something different.

	The charset of the email will be the first one out of the list
	that can represent all the characters occurring in the email.
	"""

    # Header class is smart enough to try US-ASCII, then the charset we
    # provide, then fall back to UTF-8.
    header_charset = 'ISO-8859-1'

    # We must choose the body charset manually
    for body_charset in CHARSET_LIST:
        try:
            body.encode(body_charset)
        except (UnicodeError, LookupError):
            pass
        else:
            break

    # Split real name (which is optional) and email address parts
    sender_name, sender_addr = parseaddr(sender)
    recipient_name, recipient_addr = parseaddr(recipient)

    # We must always pass Unicode strings to Header, otherwise it will
    # use RFC 2047 encoding even on plain ASCII strings.
    sender_name = str(Header(unicode(sender_name), header_charset))
    recipient_name = str(Header(unicode(recipient_name), header_charset))

    # Make sure email addresses do not contain non-ASCII characters
    sender_addr = sender_addr.encode('ascii')
    recipient_addr = recipient_addr.encode('ascii')

    # Create the message ('plain' stands for Content-Type: text/plain)
    msg = MIMEText(body.encode(body_charset), contenttype, body_charset)
    msg['To'] = formataddr((recipient_name, recipient_addr))
    msg['Subject'] = Header(unicode(subject), header_charset)
    for hdr in extraheaders.keys():
        try:
            msg[hdr] = Header(unicode(extraheaders[hdr], header_charset))
        except:
            msg[hdr] = Header(extraheaders[hdr])

    fromhdr = formataddr((sender_name, sender_addr))
    msg['From'] = fromhdr

    msg_as_string = msg.as_string()
    #DEPRECATED 	if QP_REQUIRED:
    #DEPRECATED 		ins, outs = SIO(msg_as_string), SIO()
    #DEPRECATED 		mimify.mimify(ins, outs)
    #DEPRECATED 		msg_as_string = outs.getvalue()

    if VERBOSE: print 'Sending:', unu(subject)

    if SMTP_SEND:
        if not smtpserver:
            import smtplib

            try:
                if SMTP_SSL:
                    smtpserver = smtplib.SMTP_SSL()
                else:
                    smtpserver = smtplib.SMTP()
                smtpserver.connect(SMTP_SERVER)
            except KeyboardInterrupt:
                raise
            except Exception, e:
                print >> warn, ""
                print >> warn, (
                    'Fatal error: could not connect to mail server "%s"' %
                    SMTP_SERVER)
                print >> warn, (
                    'Check your config.py file to confirm that SMTP_SERVER and other mail server settings are configured properly'
                )
                if hasattr(e, 'reason'):
                    print >> warn, "Reason:", e.reason
                sys.exit(1)

            if AUTHREQUIRED:
                try:
                    smtpserver.ehlo()
                    if not SMTP_SSL: smtpserver.starttls()
                    smtpserver.ehlo()
                    smtpserver.login(SMTP_USER, SMTP_PASS)
                except KeyboardInterrupt:
                    raise
                except Exception, e:
                    print >> warn, ""
                    print >> warn, (
                        'Fatal error: could not authenticate with mail server "%s" as user "%s"'
                        % (SMTP_SERVER, SMTP_USER))
                    print >> warn, (
                        'Check your config.py file to confirm that SMTP_SERVER and other mail server settings are configured properly'
                    )
                    if hasattr(e, 'reason'):
                        print >> warn, "Reason:", e.reason
                    sys.exit(1)
Example #33
0
     print >> outfile, '[<b><a href=".">PEP Index</a></b>]'
 if pep:
     try:
         print >> outfile, ('[<b><a href="pep-%04d.txt">PEP Source</a>'
                            '</b>]' % int(pep))
     except ValueError, error:
         print >> sys.stderr, ('ValueError (invalid PEP number): %s' %
                               error)
 print >> outfile, '</td></tr></table>'
 print >> outfile, '<div class="header">\n<table border="0">'
 for k, v in header:
     if k.lower() in ('author', 'discussions-to'):
         mailtos = []
         for part in re.split(',\s*', v):
             if '@' in part:
                 realname, addr = parseaddr(part)
                 if k.lower() == 'discussions-to':
                     m = linkemail(addr, pep)
                 else:
                     m = fixemail(addr, pep)
                 mailtos.append('%s &lt;%s&gt;' % (realname, m))
             elif part.startswith('http:'):
                 mailtos.append('<a href="%s">%s</a>' % (part, part))
             else:
                 mailtos.append(part)
         v = COMMASPACE.join(mailtos)
     elif k.lower() in ('replaces', 'replaced-by', 'requires'):
         otherpeps = ''
         for otherpep in re.split(',?\s+', v):
             otherpep = int(otherpep)
             otherpeps += '<a href="pep-%04d.html">%i</a> ' % (otherpep,
Example #34
0
def fixfile(inpath, input_lines, outfile):
    try:
        from email.Utils import parseaddr
    except ImportError:
        from email.utils import parseaddr
    basename = os.path.basename(inpath)
    infile = iter(input_lines)
    # convert plaintext pep to minimal XHTML markup
    print(DTD, file=outfile)
    print('<html>', file=outfile)
    print(COMMENT, file=outfile)
    print('<head>', file=outfile)
    # head
    header = []
    pep = ""
    title = ""
    for line in infile:
        if not line.strip():
            break
        if line[0].strip():
            if ":" not in line:
                break
            key, value = line.split(":", 1)
            value = value.strip()
            header.append((key, value))
        else:
            # continuation line
            key, value = header[-1]
            value = value + line
            header[-1] = key, value
        if key.lower() == "title":
            title = value
        elif key.lower() == "pep":
            pep = value
    if pep:
        title = "PEP " + pep + " -- " + title
    if title:
        print('  <title>%s</title>' % escape(title), file=outfile)
    r = random.choice(list(range(64)))
    print(('  <link rel="STYLESHEET" href="style.css" type="text/css" />\n'
           '</head>\n'
           '<body bgcolor="white">\n'
           '<table class="navigation" cellpadding="0" cellspacing="0"\n'
           '       width="100%%" border="0">\n'
           '<tr><td class="navicon" width="150" height="35">\n'
           '<a href="../" title="Python Home Page">\n'
           '<img src="../pics/PyBanner%03d.gif" alt="[Python]"\n'
           ' border="0" width="150" height="35" /></a></td>\n'
           '<td class="textlinks" align="left">\n'
           '[<b><a href="../">Python Home</a></b>]' % r),
          file=outfile)
    if basename != 'pep-0000.txt':
        print('[<b><a href=".">PEP Index</a></b>]', file=outfile)
    if pep:
        try:
            print(('[<b><a href="pep-%04d.txt">PEP Source</a>'
                   '</b>]' % int(pep)),
                  file=outfile)
        except ValueError as error:
            print(('ValueError (invalid PEP number): %s' % error),
                  file=sys.stderr)
    print('</td></tr></table>', file=outfile)
    print('<div class="header">\n<table border="0">', file=outfile)
    for k, v in header:
        if k.lower() in ('author', 'bdfl-delegate', 'discussions-to'):
            mailtos = []
            for part in re.split(',\s*', v):
                if '@' in part:
                    realname, addr = parseaddr(part)
                    if k.lower() == 'discussions-to':
                        m = linkemail(addr, pep)
                    else:
                        m = fixemail(addr, pep)
                    mailtos.append('%s &lt;%s&gt;' % (realname, m))
                elif part.startswith('http:'):
                    mailtos.append('<a href="%s">%s</a>' % (part, part))
                else:
                    mailtos.append(part)
            v = COMMASPACE.join(mailtos)
        elif k.lower() in ('replaces', 'replaced-by', 'requires'):
            otherpeps = ''
            for otherpep in re.split(',?\s+', v):
                otherpep = int(otherpep)
                otherpeps += '<a href="pep-%04d.html">%i</a> ' % (otherpep,
                                                                  otherpep)
            v = otherpeps
        elif k.lower() in ('last-modified', ):
            date = v or time.strftime('%d-%b-%Y',
                                      time.localtime(os.stat(inpath)[8]))
            if date.startswith('$' 'Date: ') and date.endswith(' $'):
                date = date[6:-2]
            if basename == 'pep-0000.txt':
                v = date
            else:
                try:
                    url = PEPCVSURL % int(pep)
                    v = '<a href="%s">%s</a> ' % (url, escape(date))
                except ValueError as error:
                    v = date
        elif k.lower() in ('content-type', ):
            url = PEPURL % 9
            pep_type = v or 'text/plain'
            v = '<a href="%s">%s</a> ' % (url, escape(pep_type))
        elif k.lower() == 'version':
            if v.startswith('$' 'Revision: ') and v.endswith(' $'):
                v = escape(v[11:-2])
        else:
            v = escape(v)
        print('  <tr><th>%s:&nbsp;</th><td>%s</td></tr>' \
              % (escape(k), v), file=outfile)
    print('</table>', file=outfile)
    print('</div>', file=outfile)
    print('<hr />', file=outfile)
    print('<div class="content">', file=outfile)
    need_pre = 1
    for line in infile:
        if line[0] == '\f':
            continue
        if line.strip() == LOCALVARS:
            break
        if line[0].strip():
            if not need_pre:
                print('</pre>', file=outfile)
            print('<h3>%s</h3>' % line.strip(), file=outfile)
            need_pre = 1
        elif not line.strip() and need_pre:
            continue
        else:
            # PEP 0 has some special treatment
            if basename == 'pep-0000.txt':
                parts = line.split()
                if len(parts) > 1 and re.match(r'\s*\d{1,4}', parts[1]):
                    # This is a PEP summary line, which we need to hyperlink
                    url = PEPURL % int(parts[1])
                    if need_pre:
                        print('<pre>', file=outfile)
                        need_pre = 0
                    print(re.sub(parts[1],
                                 '<a href="%s">%s</a>' % (url, parts[1]), line,
                                 1),
                          end=' ',
                          file=outfile)
                    continue
                elif parts and '@' in parts[-1]:
                    # This is a pep email address line, so filter it.
                    url = fixemail(parts[-1], pep)
                    if need_pre:
                        print('<pre>', file=outfile)
                        need_pre = 0
                    print(re.sub(parts[-1], url, line, 1),
                          end=' ',
                          file=outfile)
                    continue
            line = fixpat.sub(lambda x, c=inpath: fixanchor(c, x), line)
            if need_pre:
                print('<pre>', file=outfile)
                need_pre = 0
            outfile.write(line)
    if not need_pre:
        print('</pre>', file=outfile)
    print('</div>', file=outfile)
    print('</body>', file=outfile)
    print('</html>', file=outfile)
Example #35
0
def send_email(sender, recipient, subject, body, body_plain="", noreply=False):
    """Send an email.

    All arguments should be Unicode strings (plain ASCII works as well).

    Only the real name part of sender and recipient addresses may contain
    non-ASCII characters.

    The email will be properly MIME encoded and delivered though SMTP.

    The charset of the email will be the first one out of US-ASCII, ISO-8859-1
    and UTF-8 that can represent all the characters occurring in the email.
    """

    # Header class is smart enough to try US-ASCII, then the charset we
    # provide, then fall back to UTF-8.
    header_charset = 'ISO-8859-1'

    # We must choose the body charset manually
    for body_charset in 'US-ASCII', 'ISO-8859-1', 'UTF-8':
        try:
            body.encode(body_charset)
        except UnicodeError:
            pass
        else:
            break

    # Split real name (which is optional) and email address parts
    sender_name, sender_addr = parseaddr(sender)
    recipient_name, recipient_addr = parseaddr(recipient)

    # We must always pass Unicode strings to Header, otherwise it will
    # use RFC 2047 encoding even on plain ASCII strings.
    sender_name = str(Header(unicode(sender_name), header_charset))
    recipient_name = str(Header(unicode(recipient_name), header_charset))

    # Make sure email addresses do not contain non-ASCII characters
    sender_addr = sender_addr.encode('ascii')
    recipient_addr = recipient_addr.encode('ascii')

    # Create the message
    msg = MIMEMultipart('alternative')
    html_part = MIMEText(body.encode(body_charset), 'html', body_charset)
    plain_part = MIMEText(body_plain.encode(body_charset), 'plain',
                          body_charset)
    msg.attach(plain_part)
    msg.attach(html_part)
    msg['From'] = formataddr((sender_name, sender_addr))
    msg['To'] = formataddr((recipient_name, recipient_addr))
    msg['Subject'] = Header(unicode(subject), header_charset)

    # Send the message via SMTP via one of the configured smtp servers
    # If all servers fail, throw an error
    sent = False
    for smtp_server in tickee.settings.SMTP_SERVERS:
        try:
            smtp = SMTP(smtp_server.get('host'), smtp_server.get('port'))
            smtp.starttls()
            smtp.login(smtp_server.get('username'),
                       smtp_server.get('password'))
            smtp.sendmail(sender, recipient, msg.as_string())
            smtp.quit()
        except Exception as e:
            tlogger.error("failed sending mail to '%s' using smtp '%s'" %
                          (recipient, smtp_server.get('name')))
            continue  # continue to the next smtp server
        else:
            sent = True
            break  # stop trying to send the mail
    if not sent:
        raise CoreError('Failed sending email.')
Example #36
0
    def send_email(self, backend, mail):
        domain = self.config.get('domain')
        recipient = self.config.get('recipient')

        reply_id = ''
        if mail.parent:
            reply_id = u'<%s.%s@%s>' % (backend.name, mail.parent.full_id,
                                        domain)
        subject = mail.title
        sender = u'"%s" <%s@%s>' % (mail.sender.replace('"', '""') if
                                    mail.sender else '', backend.name, domain)

        # assume that .date is an UTC datetime
        date = formatdate(time.mktime(utc2local(mail.date).timetuple()),
                          localtime=True)
        msg_id = u'<%s.%s@%s>' % (backend.name, mail.full_id, domain)

        if self.config.get('html') and mail.flags & mail.IS_HTML:
            body = mail.content
            content_type = 'html'
        else:
            if mail.flags & mail.IS_HTML:
                body = html2text(mail.content)
            else:
                body = mail.content
            content_type = 'plain'

        if body is None:
            body = ''

        if mail.signature:
            if self.config.get('html') and mail.flags & mail.IS_HTML:
                body += u'<p>-- <br />%s</p>' % mail.signature
            else:
                body += u'\n\n-- \n'
                if mail.flags & mail.IS_HTML:
                    body += html2text(mail.signature)
                else:
                    body += mail.signature

        # Header class is smart enough to try US-ASCII, then the charset we
        # provide, then fall back to UTF-8.
        header_charset = 'ISO-8859-1'

        # We must choose the body charset manually
        for body_charset in 'US-ASCII', 'ISO-8859-1', 'UTF-8':
            try:
                body.encode(body_charset)
            except UnicodeError:
                pass
            else:
                break

        # Split real name (which is optional) and email address parts
        sender_name, sender_addr = parseaddr(sender)
        recipient_name, recipient_addr = parseaddr(recipient)

        # We must always pass Unicode strings to Header, otherwise it will
        # use RFC 2047 encoding even on plain ASCII strings.
        sender_name = str(Header(unicode(sender_name), header_charset))
        recipient_name = str(Header(unicode(recipient_name), header_charset))

        # Make sure email addresses do not contain non-ASCII characters
        sender_addr = sender_addr.encode('ascii')
        recipient_addr = recipient_addr.encode('ascii')

        # Create the message ('plain' stands for Content-Type: text/plain)
        msg = MIMEText(body.encode(body_charset), content_type, body_charset)
        msg['From'] = formataddr((sender_name, sender_addr))
        msg['To'] = formataddr((recipient_name, recipient_addr))
        msg['Subject'] = Header(unicode(subject), header_charset)
        msg['Message-Id'] = msg_id
        msg['Date'] = date
        if reply_id:
            msg['In-Reply-To'] = reply_id

        self.logger.info('Send mail from <%s> to <%s>' % (sender, recipient))
        if len(self.config.get('pipe')) > 0:
            p = subprocess.Popen(self.config.get('pipe'),
                                 shell=True,
                                 stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
            p.stdin.write(msg.as_string())
            p.stdin.close()
            if p.wait() != 0:
                self.logger.error('Unable to deliver mail: %s' %
                                  p.stdout.read().strip())
                return False
        else:
            # Send the message via SMTP to localhost:25
            try:
                smtp = SMTP(self.config.get('smtp'))
                smtp.sendmail(sender, recipient, msg.as_string())
            except Exception as e:
                self.logger.error('Unable to deliver mail: %s' % e)
                return False
            else:
                smtp.quit()

        return True
Example #37
0
def fixfile(inpath, input_lines, outfile):
    from email.Utils import parseaddr
    basename = os.path.basename(inpath)
    infile = iter(input_lines)
    # convert plaintext psep to minimal XHTML markup
    print >> outfile, DTD
    print >> outfile, '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'
    print >> outfile, COMMENT
    print >> outfile, '<head>'
    # head
    header = []
    psep = ""
    title = ""
    for line in infile:
        if not line.strip():
            break
        if line[0].strip():
            if ":" not in line:
                break
            key, value = line.split(":", 1)
            value = value.strip()
            header.append((key, value))
        else:
            # continuation line
            key, value = header[-1]
            value = value + line
            header[-1] = key, value
        if key.lower() == "title":
            title = value
        elif key.lower() == "psep":
            psep = value
    if psep:
        title = "PSEP " + psep + " -- " + title
    if title:
        print >> outfile, '  <title>%s</title>' % cgi.escape(title)
    r = random.choice(range(64))
    print >> outfile, (
        '  <link rel="STYLESHEET" href="psep.css" type="text/css" />\n'
        '</head>\n'
        '<body>\n'
        '<div id="header">\n'
        '<a href="http://www.pyside.org/" title="Python Home Page">\n'
        '<img src="http://www.pyside.org/wp-content/themes/openbossa/images/logo.png" alt="[PySide]"\n'
        'border="0" width="199" height="102" /></a>\n'
        '[<b><a href="http://www.pyside.org/">PySide Home</a></b>]\n'
        '</div>\n'
        '<div id="content">\n')
    if basename <> 'psep-0000.txt':
        print >> outfile, '[<b><a href=".">PSEP Index</a></b>]'
    print >> outfile, '<table border="0">'
    for k, v in header:
        if k.lower() in ('author', 'discussions-to'):
            mailtos = []
            for part in re.split(',\s*', v):
                if '@' in part:
                    realname, addr = parseaddr(part)
                    if k.lower() == 'discussions-to':
                        m = linkemail(addr, psep)
                    else:
                        m = fixemail(addr, psep)
                    mailtos.append('%s &lt;%s&gt;' % (realname, m))
                elif part.startswith('http:'):
                    mailtos.append(
                        '<a href="%s">%s</a>' % (part, part))
                else:
                    mailtos.append(part)
            v = COMMASPACE.join(mailtos)
        elif k.lower() in ('replaces', 'replaced-by', 'requires'):
            otherpseps = ''
            for otherpsep in re.split(',?\s+', v):
                otherpsep = int(otherpsep)
                otherpseps += '<a href="psep-%04d.html">%i</a> ' % (otherpsep,
                                                                  otherpsep)
            v = otherpseps
        elif k.lower() in ('last-modified',):
            date = time.strftime('%d-%b-%Y',
                                      time.localtime(os.stat(inpath)[8]))
            if int(psep) != 0:
                url = PSEPCVSURL % int(psep)
                v = '<a href="%s">%s</a> ' % (url, cgi.escape(date))
            else:
                v = date
        elif k.lower() in ('content-type',):
            url = PSEPURL % 9
            psep_type = v or 'text/plain'
            v = '<a href="%s">%s</a> ' % (url, cgi.escape(psep_type))
        else:
            v = cgi.escape(v)
        print >> outfile, '  <tr class="field"><th class="field-name">%s:&nbsp;</th><td class="field-body">%s</td></tr>' \
              % (cgi.escape(k), v)
    print >> outfile, '</table>'
    print >> outfile, '<hr />'
    need_pre = 1
    for line in infile:
        if line[0] == '\f':
            continue
        if line.strip() == LOCALVARS:
            break
        if line[0].strip():
            if not need_pre:
                print >> outfile, '</pre>'
            print >> outfile, '<h3>%s</h3>' % line.strip()
            need_pre = 1
        elif not line.strip() and need_pre:
            continue
        else:
            # PSEP 0 has some special treatment
            if basename == 'psep-0000.txt':
                parts = line.split()
                if len(parts) > 1 and re.match(r'\s*\d{1,4}', parts[1]):
                    # This is a PSEP summary line, which we need to hyperlink
                    url = PSEPURL % int(parts[1])
                    if need_pre:
                        print >> outfile, '<pre>'
                        need_pre = 0
                    print >> outfile, re.sub(
                        parts[1],
                        '<a href="%s">%s</a>' % (url, parts[1]),
                        line, 1),
                    continue
                elif parts and '@' in parts[-1]:
                    # This is a psep email address line, so filter it.
                    url = fixemail(parts[-1], psep)
                    if need_pre:
                        print >> outfile, '<pre>'
                        need_pre = 0
                    print >> outfile, re.sub(
                        parts[-1], url, line, 1),
                    continue
            line = fixpat.sub(lambda x, c=inpath: fixanchor(c, x), line)
            if need_pre:
                print >> outfile, '<pre>'
                need_pre = 0
            outfile.write(line)
    if not need_pre:
        print >> outfile, '</pre>'
    print >> outfile, '</div>'
    print >> outfile, '</body>'
    print >> outfile, '</html>'
Example #38
0
 def getBugReporter(self, remote_bug):
     """See ISupportsBugImport."""
     debian_bug = self._findBug(remote_bug)
     reporter_name, reporter_email = parseaddr(debian_bug.originator)
     return reporter_name, reporter_email
Example #39
0
    def parse(self, m, prefix=None):
        """Parse mail sent by FreshCVS"""

        # FreshCVS sets From: to "user CVS <user>", but the <> part may be
        # modified by the MTA (to include a local domain)
        name, addr = parseaddr(m["from"])
        if not name:
            return None  # no From means this message isn't from FreshCVS
        cvs = name.find(" CVS")
        if cvs == -1:
            return None  # this message isn't from FreshCVS
        who = name[:cvs]

        # we take the time of receipt as the time of checkin. Not correct,
        # but it avoids the out-of-order-changes issue. See the comment in
        # parseSyncmail about using the 'Date:' header
        when = util.now()

        files = []
        comments = ""
        isdir = 0
        lines = list(body_line_iterator(m))
        while lines:
            line = lines.pop(0)
            if line == "Modified files:\n":
                break
        while lines:
            line = lines.pop(0)
            if line == "\n":
                break
            line = line.rstrip("\n")
            linebits = line.split(None, 1)
            file = linebits[0]
            if prefix:
                # insist that the file start with the prefix: FreshCVS sends
                # changes we don't care about too
                if file.startswith(prefix):
                    file = file[len(prefix):]
                else:
                    continue
            if len(linebits) == 1:
                isdir = 1
            elif linebits[1] == "0 0":
                isdir = 1
            files.append(file)
        while lines:
            line = lines.pop(0)
            if line == "Log message:\n":
                break
        # message is terminated by "ViewCVS links:" or "Index:..." (patch)
        while lines:
            line = lines.pop(0)
            if line == "ViewCVS links:\n":
                break
            if line.find("Index: ") == 0:
                break
            comments += line
        comments = comments.rstrip() + "\n"

        if not files:
            return None

        change = changes.Change(who, files, comments, isdir, when=when)

        return change
Example #40
0
    def parse(self, m, prefix=None):
        """Parse messages sent by the 'buildbot-cvs-mail' program.
        """
        # The mail is sent from the person doing the checkin. Assume that the
        # local username is enough to identify them (this assumes a one-server
        # cvs-over-rsh environment rather than the server-dirs-shared-over-NFS
        # model)
        name, addr = parseaddr(m["from"])
        if not addr:
            return None  # no From means this message isn't from buildbot-cvs-mail
        at = addr.find("@")
        if at == -1:
            author = addr  # might still be useful
        else:
            author = addr[:at]

        # CVS accecpts RFC822 dates. buildbot-cvs-mail adds the date as
        # part of the mail header, so use that.
        # This assumes cvs is being access via ssh or pserver, so the time
        # will be the CVS server's time.

        # calculate a "revision" based on that timestamp, or the current time
        # if we're unable to parse the date.
        log.msg('Processing CVS mail')
        dateTuple = parsedate_tz(m["date"])
        if dateTuple == None:
            when = util.now()
        else:
            when = mktime_tz(dateTuple)

        theTime = datetime.datetime.utcfromtimestamp(float(when))
        rev = theTime.strftime('%Y-%m-%d %H:%M:%S')

        catRE = re.compile('^Category:\s*(\S.*)')
        cvsRE = re.compile('^CVSROOT:\s*(\S.*)')
        cvsmodeRE = re.compile('^Cvsmode:\s*(\S.*)')
        filesRE = re.compile('^Files:\s*(\S.*)')
        modRE = re.compile('^Module:\s*(\S.*)')
        pathRE = re.compile('^Path:\s*(\S.*)')
        projRE = re.compile('^Project:\s*(\S.*)')
        singleFileRE = re.compile('(.*) (NONE|\d(\.|\d)+) (NONE|\d(\.|\d)+)')
        tagRE = re.compile('^\s+Tag:\s*(\S.*)')
        updateRE = re.compile('^Update of:\s*(\S.*)')
        comments = ""
        branch = None
        cvsroot = None
        fileList = None
        files = []
        isdir = 0
        path = None
        project = None

        lines = list(body_line_iterator(m))
        while lines:
            line = lines.pop(0)
            m = catRE.match(line)
            if m:
                category = m.group(1)
                continue
            m = cvsRE.match(line)
            if m:
                cvsroot = m.group(1)
                continue
            m = cvsmodeRE.match(line)
            if m:
                cvsmode = m.group(1)
                continue
            m = filesRE.match(line)
            if m:
                fileList = m.group(1)
                continue
            m = modRE.match(line)
            if m:
                # We don't actually use this
                #module = m.group(1)
                continue
            m = pathRE.match(line)
            if m:
                path = m.group(1)
                continue
            m = projRE.match(line)
            if m:
                project = m.group(1)
                continue
            m = tagRE.match(line)
            if m:
                branch = m.group(1)
                continue
            m = updateRE.match(line)
            if m:
                # We don't actually use this
                #updateof = m.group(1)
                continue
            if line == "Log Message:\n":
                break

        # CVS 1.11 lists files as:
        #   repo/path file,old-version,new-version file2,old-version,new-version
        # Version 1.12 lists files as:
        #   file1 old-version new-version file2 old-version new-version
        #
        # files consists of tuples of 'file-name old-version new-version'
        # The versions are either dotted-decimal version numbers, ie 1.1
        # or NONE. New files are of the form 'NONE NUMBER', while removed
        # files are 'NUMBER NONE'. 'NONE' is a literal string
        # Parsing this instead of files list in 'Added File:' etc
        # makes it possible to handle files with embedded spaces, though
        # it could fail if the filename was 'bad 1.1 1.2'
        # For cvs version 1.11, we expect
        #  my_module new_file.c,NONE,1.1
        #  my_module removed.txt,1.2,NONE
        #  my_module modified_file.c,1.1,1.2
        # While cvs version 1.12 gives us
        #  new_file.c NONE 1.1
        #  removed.txt 1.2 NONE
        #  modified_file.c 1.1,1.2

        if fileList is None:
            log.msg('CVSMaildirSource Mail with no files. Ignoring')
            return None  # We don't have any files. Email not from CVS

        if cvsmode == '1.11':
            # Please, no repo paths with spaces!
            m = re.search('([^ ]*) ', fileList)
            if m:
                path = m.group(1)
            else:
                log.msg(
                    'CVSMaildirSource can\'t get path from file list. Ignoring mail'
                )
                return
            fileList = fileList[len(path):].strip()
            singleFileRE = re.compile(
                '(.+?),(NONE|(?:\d+\.(?:\d+\.\d+\.)*\d+)),(NONE|(?:\d+\.(?:\d+\.\d+\.)*\d+))(?: |$)'
            )
        elif cvsmode == '1.12':
            singleFileRE = re.compile(
                '(.+?) (NONE|(?:\d+\.(?:\d+\.\d+\.)*\d+)) (NONE|(?:\d+\.(?:\d+\.\d+\.)*\d+))(?: |$)'
            )
            if path is None:
                raise ValueError(
                    'CVSMaildirSource cvs 1.12 require path. Check cvs loginfo config'
                )
        else:
            raise ValueError('Expected cvsmode 1.11 or 1.12. got: %s' %
                             cvsmode)

        log.msg("CVSMaildirSource processing filelist: %s" % fileList)
        while (fileList):
            m = singleFileRE.match(fileList)
            if m:
                curFile = path + '/' + m.group(1)
                files.append(curFile)
                fileList = fileList[m.end():]
            else:
                log.msg('CVSMaildirSource no files matched regex. Ignoring')
                return None  # bail - we couldn't parse the files that changed
        # Now get comments
        while lines:
            line = lines.pop(0)
            comments += line

        comments = comments.rstrip() + "\n"
        if comments == '\n':
            comments = None
        return ('cvs',
                dict(author=author,
                     files=files,
                     comments=comments,
                     isdir=isdir,
                     when=when,
                     branch=branch,
                     revision=rev,
                     category=category,
                     repository=cvsroot,
                     project=project,
                     properties=self.properties))
Example #41
0
    def parse(self, m, prefix=None):
        """Parse messages sent by the svn 'commit-email.pl' trigger.
        """

        # The mail is sent from the person doing the checkin. Assume that the
        # local username is enough to identify them (this assumes a one-server
        # cvs-over-rsh environment rather than the server-dirs-shared-over-NFS
        # model)
        name, addr = parseaddr(m["from"])
        if not addr:
            return None  # no From means this message isn't from svn
        at = addr.find("@")
        if at == -1:
            author = addr  # might still be useful
        else:
            author = addr[:at]

        # we take the time of receipt as the time of checkin. Not correct (it
        # depends upon the email latency), but it avoids the
        # out-of-order-changes issue. Also syncmail doesn't give us anything
        # better to work with, unless you count pulling the v1-vs-v2
        # timestamp out of the diffs, which would be ugly. TODO: Pulling the
        # 'Date:' header from the mail is a possibility, and
        # email.Utils.parsedate_tz may be useful. It should be configurable,
        # however, because there are a lot of broken clocks out there.
        when = util.now()

        files = []
        comments = ""
        lines = list(body_line_iterator(m))
        rev = None
        while lines:
            line = lines.pop(0)

            # "Author: jmason"
            match = re.search(r"^Author: (\S+)", line)
            if match:
                author = match.group(1)

            # "New Revision: 105955"
            match = re.search(r"^New Revision: (\d+)", line)
            if match:
                rev = match.group(1)

            # possible TODO: use "Date: ..." data here instead of time of
            # commit message receipt, above. however, this timestamp is
            # specified *without* a timezone, in the server's local TZ, so to
            # be accurate buildbot would need a config setting to specify the
            # source server's expected TZ setting! messy.

            # this stanza ends with the "Log:"
            if (line == "Log:\n"):
                break

        # commit message is terminated by the file-listing section
        while lines:
            line = lines.pop(0)
            if (line == "Modified:\n" or line == "Added:\n"
                    or line == "Removed:\n"):
                break
            comments += line
        comments = comments.rstrip() + "\n"

        while lines:
            line = lines.pop(0)
            if line == "\n":
                break
            if line.find("Modified:\n") == 0:
                continue  # ignore this line
            if line.find("Added:\n") == 0:
                continue  # ignore this line
            if line.find("Removed:\n") == 0:
                continue  # ignore this line
            line = line.strip()

            thesefiles = line.split(" ")
            for f in thesefiles:
                if prefix:
                    # insist that the file start with the prefix: we may get
                    # changes we don't care about too
                    if f.startswith(prefix):
                        f = f[len(prefix):]
                    else:
                        log.msg("ignored file from svn commit: prefix '%s' "
                                "does not match filename '%s'" % (prefix, f))
                        continue

                # TODO: figure out how new directories are described, set
                # .isdir
                files.append(f)

        if not files:
            log.msg("no matching files found, ignoring commit")
            return None

        return ('svn',
                dict(author=author,
                     files=files,
                     comments=comments,
                     when=when,
                     revision=rev))
Example #42
0
def process(mlist, msg, msgdata):
    # Set the "X-Ack: no" header if noack flag is set.
    if msgdata.get('noack'):
        change_header('X-Ack', 'no', mlist, msg, msgdata)
    # Because we're going to modify various important headers in the email
    # message, we want to save some of the information in the msgdata
    # dictionary for later.  Specifically, the sender header will get waxed,
    # but we need it for the Acknowledge module later.
    # We may have already saved it; if so, don't clobber it here.
    if 'original_sender' not in msgdata:
        msgdata['original_sender'] = msg.get_sender()
    # VirginRunner sets _fasttrack for internally crafted messages.
    fasttrack = msgdata.get('_fasttrack')
    if not msgdata.get('isdigest') and not fasttrack:
        try:
            prefix_subject(mlist, msg, msgdata)
        except (UnicodeError, ValueError):
            # TK: Sometimes subject header is not MIME encoded for 8bit
            # simply abort prefixing.
            pass
    # Mark message so we know we've been here, but leave any existing
    # X-BeenThere's intact.
    change_header('X-BeenThere',
                  mlist.GetListEmail(),
                  mlist,
                  msg,
                  msgdata,
                  delete=False)
    # Add Precedence: and other useful headers.  None of these are standard
    # and finding information on some of them are fairly difficult.  Some are
    # just common practice, and we'll add more here as they become necessary.
    # Good places to look are:
    #
    # http://www.dsv.su.se/~jpalme/ietf/jp-ietf-home.html
    # http://www.faqs.org/rfcs/rfc2076.html
    #
    # None of these headers are added if they already exist.  BAW: some
    # consider the advertising of this a security breach.  I.e. if there are
    # known exploits in a particular version of Mailman and we know a site is
    # using such an old version, they may be vulnerable.  It's too easy to
    # edit the code to add a configuration variable to handle this.
    change_header('X-Mailman-Version',
                  mm_cfg.VERSION,
                  mlist,
                  msg,
                  msgdata,
                  repl=False)
    # We set "Precedence: list" because this is the recommendation from the
    # sendmail docs, the most authoritative source of this header's semantics.
    change_header('Precedence', 'list', mlist, msg, msgdata, repl=False)
    # Do we change the from so the list takes ownership of the email
    if (msgdata.get('from_is_list') or mlist.from_is_list) and not fasttrack:
        # Be as robust as possible here.
        faddrs = getaddresses(msg.get_all('from', []))
        # Strip the nulls and bad emails.
        faddrs = [x for x in faddrs if x[1].find('@') > 0]
        if len(faddrs) == 1:
            realname, email = o_from = faddrs[0]
        else:
            # No From: or multiple addresses.  Just punt and take
            # the get_sender result.
            realname = ''
            email = msgdata['original_sender']
            o_from = (realname, email)
        if not realname:
            if mlist.isMember(email):
                realname = mlist.getMemberName(email) or email
            else:
                realname = email
        # Remove domain from realname if it looks like an email address
        realname = re.sub(r'@([^ .]+\.)+[^ .]+$', '---', realname)
        # Make a display name and RFC 2047 encode it if necessary.  This is
        # difficult and kludgy. If the realname came from From: it should be
        # ascii or RFC 2047 encoded. If it came from the list, it should be
        # in the charset of the list's preferred language or possibly unicode.
        # if it's from the email address, it should be ascii. In any case,
        # make it a unicode.
        if isinstance(realname, unicode):
            urn = realname
        else:
            rn, cs = ch_oneline(realname)
            urn = unicode(rn, cs, errors='replace')
        # likewise, the list's real_name which should be ascii, but use the
        # charset of the list's preferred_language which should be a superset.
        lcs = Utils.GetCharSet(mlist.preferred_language)
        ulrn = unicode(mlist.real_name, lcs, errors='replace')
        # get translated 'via' with dummy replacements
        realname = '%(realname)s'
        lrn = '%(lrn)s'
        # We want the i18n context to be the list's preferred_language.  It
        # could be the poster's.
        otrans = i18n.get_translation()
        i18n.set_language(mlist.preferred_language)
        via = _('%(realname)s via %(lrn)s')
        i18n.set_translation(otrans)
        uvia = unicode(via, lcs, errors='replace')
        # Replace the dummy replacements.
        uvia = re.sub(u'%\(lrn\)s', ulrn, re.sub(u'%\(realname\)s', urn, uvia))
        # And get an RFC 2047 encoded header string.
        dn = str(Header(uvia, lcs))
        change_header('From', formataddr((dn, mlist.GetListEmail())), mlist,
                      msg, msgdata)
    else:
        # Use this as a flag
        o_from = None
    # Reply-To: munging.  Do not do this if the message is "fast tracked",
    # meaning it is internally crafted and delivered to a specific user.  BAW:
    # Yuck, I really hate this feature but I've caved under the sheer pressure
    # of the (very vocal) folks want it.  OTOH, RFC 2822 allows Reply-To: to
    # be a list of addresses, so instead of replacing the original, simply
    # augment it.  RFC 2822 allows max one Reply-To: header so collapse them
    # if we're adding a value, otherwise don't touch it.  (Should we collapse
    # in all cases?)
    # MAS: We need to do some things with the original From: if we've munged
    # it for DMARC mitigation.  We have goals for this process which are
    # not completely compatible, so we do the best we can.  Our goals are:
    # 1) as long as the list is not anonymous, the original From: address
    #    should be obviously exposed, i.e. not just in a header that MUAs
    #    don't display.
    # 2) the original From: address should not be in a comment or display
    #    name in the new From: because it is claimed that multiple domains
    #    in any fields in From: are indicative of spamminess.  This means
    #    it should be in Reply-To: or Cc:.
    # 3) the behavior of an MUA doing a 'reply' or 'reply all' should be
    #    consistent regardless of whether or not the From: is munged.
    # Goal 3) implies sometimes the original From: should be in Reply-To:
    # and sometimes in Cc:, and even so, this goal won't be achieved in
    # all cases with all MUAs.  In cases of conflict, the above ordering of
    # goals is priority order.

    if not fasttrack:
        # A convenience function, requires nested scopes.  pair is (name, addr)
        new = []
        d = {}

        def add(pair):
            lcaddr = pair[1].lower()
            if d.has_key(lcaddr):
                return
            d[lcaddr] = pair
            new.append(pair)

        # List admin wants an explicit Reply-To: added
        if mlist.reply_goes_to_list == 2:
            add(parseaddr(mlist.reply_to_address))
        # If we're not first stripping existing Reply-To: then we need to add
        # the original Reply-To:'s to the list we're building up.  In both
        # cases we'll zap the existing field because RFC 2822 says max one is
        # allowed.
        o_rt = False
        if not mlist.first_strip_reply_to:
            orig = msg.get_all('reply-to', [])
            for pair in getaddresses(orig):
                # There's an original Reply-To: and we're not removing it.
                add(pair)
                o_rt = True
        # We also need to put the old From: in Reply-To: in all cases where
        # it is not going in Cc:.  This is when reply_goes_to_list == 0 and
        # either there was no original Reply-To: or we stripped it.
        # However, if there was an original Reply-To:, unstripped, and it
        # contained the original From: address we need to flag that it's
        # there so we don't add the original From: to Cc:
        if o_from and mlist.reply_goes_to_list == 0:
            if o_rt:
                if d.has_key(o_from[1].lower()):
                    # Original From: address is in original Reply-To:.
                    # Pretend we added it.
                    o_from = None
            else:
                add(o_from)
                # Flag that we added it.
                o_from = None
        # Set Reply-To: header to point back to this list.  Add this last
        # because some folks think that some MUAs make it easier to delete
        # addresses from the right than from the left.
        if mlist.reply_goes_to_list == 1:
            i18ndesc = uheader(mlist, mlist.description, 'Reply-To')
            add((str(i18ndesc), mlist.GetListEmail()))
        # Don't put Reply-To: back if there's nothing to add!
        if new:
            # Preserve order
            change_header('Reply-To',
                          COMMASPACE.join([formataddr(pair) for pair in new]),
                          mlist, msg, msgdata)
        else:
            del msg['reply-to']
        # The To field normally contains the list posting address.  However
        # when messages are fully personalized, that header will get
        # overwritten with the address of the recipient.  We need to get the
        # posting address in one of the recipient headers or they won't be
        # able to reply back to the list.  It's possible the posting address
        # was munged into the Reply-To header, but if not, we'll add it to a
        # Cc header.  BAW: should we force it into a Reply-To header in the
        # above code?
        # Also skip Cc if this is an anonymous list as list posting address
        # is already in From and Reply-To in this case.
        # We do add the Cc in cases where From: header munging is being done
        # because even though the list address is in From:, the Reply-To:
        # poster will override it. Brain dead MUAs may then address the list
        # twice on a 'reply all', but reasonable MUAs should do the right
        # thing.  We also add the original From: to Cc: if it wasn't added
        # to Reply-To:
        add_list = (mlist.personalize == 2 and mlist.reply_goes_to_list <> 1
                    and not mlist.anonymous_list)
        if add_list or o_from:
            # Watch out for existing Cc headers, merge, and remove dups.  Note
            # that RFC 2822 says only zero or one Cc header is allowed.
            new = []
            d = {}
            # If we're adding the original From:, add it first.
            if o_from:
                add(o_from)
            # AvoidDuplicates may have set a new Cc: in msgdata.add_header,
            # so check that.
            if (msgdata.has_key('add_header')
                    and msgdata['add_header'].has_key('Cc')):
                for pair in getaddresses([msgdata['add_header']['Cc']]):
                    add(pair)
            else:
                for pair in getaddresses(msg.get_all('cc', [])):
                    add(pair)
            if add_list:
                i18ndesc = uheader(mlist, mlist.description, 'Cc')
                add((str(i18ndesc), mlist.GetListEmail()))
            change_header('Cc',
                          COMMASPACE.join([formataddr(pair) for pair in new]),
                          mlist, msg, msgdata)
    # Add list-specific headers as defined in RFC 2369 and RFC 2919, but only
    # if the message is being crafted for a specific list (e.g. not for the
    # password reminders).
    #
    # BAW: Some people really hate the List-* headers.  It seems that the free
    # version of Eudora (possibly on for some platforms) does not hide these
    # headers by default, pissing off their users.  Too bad.  Fix the MUAs.
    if msgdata.get('_nolist') or not mlist.include_rfc2369_headers:
        return
    # This will act like an email address for purposes of formataddr()
    listid = '%s.%s' % (mlist.internal_name(), mlist.host_name)
    cset = Utils.GetCharSet(mlist.preferred_language)
    if mlist.description:
        # Don't wrap the header since here we just want to get it properly RFC
        # 2047 encoded.
        i18ndesc = uheader(mlist, mlist.description, 'List-Id', maxlinelen=998)
        listid_h = formataddr((str(i18ndesc), listid))
    else:
        # without desc we need to ensure the MUST brackets
        listid_h = '<%s>' % listid
    # We always add a List-ID: header.
    change_header('List-Id', listid_h, mlist, msg, msgdata)
    # For internally crafted messages, we also add a (nonstandard),
    # "X-List-Administrivia: yes" header.  For all others (i.e. those coming
    # from list posts), we add a bunch of other RFC 2369 headers.
    requestaddr = mlist.GetRequestEmail()
    subfieldfmt = '<%s>, <mailto:%s?subject=%ssubscribe>'
    listinfo = mlist.GetScriptURL('listinfo', absolute=1)
    useropts = mlist.GetScriptURL('options', absolute=1)
    headers = {}
    if msgdata.get('reduced_list_headers'):
        headers['X-List-Administrivia'] = 'yes'
    else:
        headers.update({
            'List-Help':
            '<mailto:%s?subject=help>' % requestaddr,
            'List-Unsubscribe':
            subfieldfmt % (useropts, requestaddr, 'un'),
            'List-Subscribe':
            subfieldfmt % (listinfo, requestaddr, ''),
        })
        # List-Post: is controlled by a separate attribute
        if mlist.include_list_post_header:
            headers['List-Post'] = '<mailto:%s>' % mlist.GetListEmail()
        # Add this header if we're archiving
        if mlist.archive:
            archiveurl = mlist.GetBaseArchiveURL()
            headers['List-Archive'] = '<%s>' % archiveurl
    # First we delete any pre-existing headers because the RFC permits only
    # one copy of each, and we want to be sure it's ours.
    for h, v in headers.items():
        # Wrap these lines if they are too long.  78 character width probably
        # shouldn't be hardcoded, but is at least text-MUA friendly.  The
        # adding of 2 is for the colon-space separator.
        if len(h) + 2 + len(v) > 78:
            v = CONTINUATION.join(v.split(', '))
        change_header(h, v, mlist, msg, msgdata)
Example #43
0
def getRawAddress(address):
    name, addr = parseaddr(address)
    return addr
Example #44
0
def ticket_from_message(message, queue, quiet):
    # 'message' must be an RFC822 formatted message.
    msg = message
    message = email.message_from_string(msg)
    subject = message.get('subject', _('Created from e-mail'))
    subject = decode_mail_headers(decodeUnknown(message.get_charset(), subject))
    subject = subject.replace("Re: ", "").replace("Fw: ", "").replace("RE: ", "").replace("FW: ", "").replace("Automatic reply: ", "").strip()

    sender = message.get('from', _('Unknown Sender'))
    sender = decode_mail_headers(decodeUnknown(message.get_charset(), sender))

    sender_email = parseaddr(sender)[1]

    body_plain, body_html = '', ''

    for ignore in IgnoreEmail.objects.filter(Q(queues=queue) | Q(queues__isnull=True)):
        if ignore.test(sender_email):
            if ignore.keep_in_mailbox:
                # By returning 'False' the message will be kept in the mailbox,
                # and the 'True' will cause the message to be deleted.
                return False
            return True

    matchobj = re.match(r".*\["+queue.slug+"-(?P<id>\d+)\]", subject)
    if matchobj:
        # This is a reply or forward.
        ticket = matchobj.group('id')
    else:
        ticket = None

    counter = 0
    files = []

    for part in message.walk():
        if part.get_content_maintype() == 'multipart':
            continue

        name = part.get_param("name")
        if name:
            name = collapse_rfc2231_value(name)

        if part.get_content_maintype() == 'text' and name == None:
            if part.get_content_subtype() == 'plain':
                body_plain = EmailReplyParser.parse_reply(decodeUnknown(part.get_content_charset(), part.get_payload(decode=True)))
            else:
                body_html = part.get_payload(decode=True)
                try:
                    # strip html tags
                    body_plain = striptags(body_html)
                except DjangoUnicodeDecodeError as e:
                    charset = chardet.detect(body_html)['encoding']
                    body_plain = striptags(unicode(body_html, charset))
                # remove extra new lines
                body_plain, n = re.subn(r'[\r\n]+', r'\n', body_plain)
                # remove extra spaces
                body_plain, n = re.subn(r'\s+$', '', body_plain, flags=re.M)
                body_plain = unescape(body_plain)
        else:
            if not name:
                ext = mimetypes.guess_extension(part.get_content_type())
                name = "part-%i%s" % (counter, ext)

            files.append({
                'filename': name,
                'content': part.get_payload(decode=True),
                'type': part.get_content_type()},
                )

        counter += 1

    if body_plain:
        body = body_plain
        if body_html:
            body += '\n\n'
            body += _('***Note that HTML tags are stripped out. Please see attachment email_html_body.html for the full html content.')
    else:
        body = _('No plain-text email body available. Please see attachment email_html_body.html.')

    if body_html:
        files.append({
            'filename': _("email_html_body.html"),
            'content': body_html,
            'type': 'text/html',
        })

    now = timezone.now()

    if ticket:
        try:
            t = Ticket.objects.get(id=ticket)
            new = False
        except Ticket.DoesNotExist:
            ticket = None

    priority = 3

    smtp_priority = message.get('priority', '')
    smtp_importance = message.get('importance', '')

    high_priority_types = ('high', 'important', '1', 'urgent')

    if smtp_priority in high_priority_types or smtp_importance in high_priority_types:
        priority = 2

    if ticket == None:
        t = Ticket(
            title=subject,
            queue=queue,
            submitter_email=sender_email,
            created=now,
            description=body,
            priority=priority,
        )
        t.save()
        new = True
        update = ''

    elif t.status == Ticket.CLOSED_STATUS:
        t.status = Ticket.REOPENED_STATUS
        t.save()

    f = FollowUp(
        ticket = t,
        title = _('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}),
        date = timezone.now(),
        public = True,
        comment = body,
    )

    if t.status == Ticket.REOPENED_STATUS:
        f.new_status = Ticket.REOPENED_STATUS
        f.title = _('Ticket Re-Opened by E-Mail Received from %(sender_email)s' % {'sender_email': sender_email})
    
    f.save()

    if not quiet:
        print (" [%s-%s] %s" % (t.queue.slug, t.id, t.title,)).encode('ascii', 'replace')

    for file in files:
        if file['content']:
            filename = file['filename'].encode('ascii', 'replace').replace(' ', '_')
            filename = re.sub('[^a-zA-Z0-9._-]+', '', filename)
            a = Attachment(
                followup=f,
                filename=filename,
                mime_type=file['type'],
                size=len(file['content']),
                )
            a.file.save(filename, ContentFile(file['content']), save=False)
            a.save()
            if not quiet:
                print "    - %s" % filename


    context = safe_template_context(t)

    if new:

        if sender_email:
            send_templated_mail(
                'newticket_submitter',
                context,
                recipients=sender_email,
                sender=queue.from_address,
                fail_silently=True,
                )

        if queue.new_ticket_cc:
            send_templated_mail(
                'newticket_cc',
                context,
                recipients=queue.new_ticket_cc,
                sender=queue.from_address,
                fail_silently=True,
                )

        if queue.updated_ticket_cc and queue.updated_ticket_cc != queue.new_ticket_cc:
            send_templated_mail(
                'newticket_cc',
                context,
                recipients=queue.updated_ticket_cc,
                sender=queue.from_address,
                fail_silently=True,
                )

    else:
        context.update(comment=f.comment)

        if t.status == Ticket.REOPENED_STATUS:
            update = _(' (Reopened)')
        else:
            update = _(' (Updated)')

        if t.assigned_to:
            send_templated_mail(
                'updated_owner',
                context,
                recipients=t.assigned_to.email,
                sender=queue.from_address,
                fail_silently=True,
                )

        if queue.updated_ticket_cc:
            send_templated_mail(
                'updated_cc',
                context,
                recipients=queue.updated_ticket_cc,
                sender=queue.from_address,
                fail_silently=True,
                )

    return t
Example #45
0
 def _formataddr(self, addr):
     x_name, x_addr = parseaddr( addr )
     x_name = str(Header(unicode(x_name), self.header_charset))
     x_addr.encode('ascii')
     return formataddr((x_name, x_addr))
Example #46
0
def send_email(sender,
               cc_recipients,
               bcc_recipients,
               subject,
               body,
               attachments=None):
    """Send an email.

    All arguments should be Unicode strings (plain ASCII works as well).

    Only the real name part of sender and recipient addresses may contain
    non-ASCII characters.

    The email will be properly MIME encoded and delivered though SMTP to
    172.17.0.1.  This is easy to change if you want something different.

    The charset of the email will be the first one out of US-ASCII, ISO-8859-1
    and UTF-8 that can represent all the characters occurring in the email.
    """

    # combined recipients
    recipients = cc_recipients + bcc_recipients

    # Header class is smart enough to try US-ASCII, then the charset we
    # provide, then fall back to UTF-8.
    header_charset = 'ISO-8859-1'

    # We must choose the body charset manually
    for body_charset in 'US-ASCII', 'ISO-8859-1', 'UTF-8':
        try:
            body.encode(body_charset)
        except UnicodeError:
            pass
        else:
            break

    # Split real name (which is optional) and email address parts
    sender_name, sender_addr = parseaddr(sender)
    parsed_cc_recipients = [parseaddr(rec) for rec in cc_recipients]
    parsed_bcc_recipients = [parseaddr(rec) for rec in bcc_recipients]
    #recipient_name, recipient_addr = parseaddr(recipient)

    # We must always pass Unicode strings to Header, otherwise it will
    # use RFC 2047 encoding even on plain ASCII strings.
    sender_name = str(Header(str(sender_name), header_charset))
    unicode_parsed_cc_recipients = []
    for recipient_name, recipient_addr in parsed_cc_recipients:
        recipient_name = str(Header(str(recipient_name), header_charset))
        # Make sure email addresses do not contain non-ASCII characters
        recipient_addr = recipient_addr.encode('ascii')
        unicode_parsed_cc_recipients.append((recipient_name, recipient_addr))
    unicode_parsed_bcc_recipients = []
    for recipient_name, recipient_addr in parsed_bcc_recipients:
        recipient_name = str(Header(str(recipient_name), header_charset))
        # Make sure email addresses do not contain non-ASCII characters
        recipient_addr = recipient_addr.encode('ascii')
        unicode_parsed_bcc_recipients.append((recipient_name, recipient_addr))

    # Make sure email addresses do not contain non-ASCII characters
    sender_addr = sender_addr.encode('ascii')

    # Create the message ('plain' stands for Content-Type: text/plain)
    msg = MIMEMultipart()
    msg['CC'] = COMMASPACE.join([
        formataddr((recipient_name, recipient_addr))
        for recipient_name, recipient_addr in unicode_parsed_cc_recipients
    ])
    msg['BCC'] = COMMASPACE.join([
        formataddr((recipient_name, recipient_addr))
        for recipient_name, recipient_addr in unicode_parsed_bcc_recipients
    ])
    msg['Subject'] = Header(str(subject), header_charset)
    msg['FROM'] = "*****@*****.**"
    msg.attach(MIMEText(body.encode(body_charset), 'plain', body_charset))

    # Add attachments
    if isinstance(attachments, dict):
        for fname in attachments:
            part = MIMEBase('application', "octet-stream")
            part.set_payload(attachments[fname])
            Encoders.encode_base64(part)
            part.add_header('Content-Disposition',
                            'attachment; filename="%s"' % fname)
            msg.attach(part)

    # Send the message via SMTP to docker host
    smtp_url = "smtp://127.0.0.1:25"
    logger.info("smtp_url : %s" % smtp_url)
    smtp = SMTP("127.0.0.1")
    smtp.sendmail(sender, recipients, msg.as_string())
    smtp.quit()
    def parse(self, m, prefix):
        """parse email sent by cradek's EMC CVS Script 1.0"""

        #log.msg("got an email!")
        #log.msg(m)

        # the From: should look like this:  EMC CVS server <*****@*****.**>
        name, addr = parseaddr(m["from"]);
        expected_name = "EMC CVS server"
        expected_addr = "*****@*****.**"
        if name != expected_name:
            log.msg("email is from \"%s\", expected \"%s\"" % (name, expected_name))
            return None
        if addr != expected_addr:
            log.msg("email is from \"%s\", expected \"%s\"" % (addr, expected_addr))
            return None

        # we take the time of receipt as the time of checkin. Not correct,
        # but it avoids the out-of-order-changes issue. See the comment in
        # parseSyncmail about using the 'Date:' header
        when = util.now()

        #
        # the commit notification email looks like this:
        #
        # <message>
        #  <generator>
        #   <name>EMC CIA script</name>
        #   <version>1.0</version>
        #  </generator>
        #  <source>
        #   <project>EMC</project>
        #   <module>emc2/docs/src/gui</module>
        #   <branch>TRUNK</branch>
        #  </source>
        #  <body>
        #   <commit>
        #    <files><file>axis.lyx</file></files>
        #    <author>bigjohnt</author>
        #    <log>minor edit
        #    </log>
        #   </commit>
        #  </body>
        # </message>

        files = []
        module = ""
        branch = ""
        comments = ""
        author = ""
        isdir = 0
        lines = list(body_line_iterator(m))

        while lines:
            line = lines.pop(0)
            #log.msg("thinking about line \"%s\"" % line)

            if re.search(r"\<module\>emc2", line):
                module = "emc2"
                continue

            match = re.search(r"\<branch\>([^<]+)", line)
            if match:
                branch = match.group(1)
                continue

            match = re.search(r"\<author\>([^\<]+)\<", line)
            if match:
                author = match.group(1)
                continue

            match = re.search(r"\<files\>(.+)\<\/files\>", line)
            if match:
                f = match.group(1)
                # f begins and ends with the separator, so the split result begins and ends with an empty string
                new_files = re.split(r"(?:</?file>)+", f)
                while new_files:
                    new_file = new_files.pop(0)
                    if new_file:
                        files.append(new_file)

                continue

            match = re.search(r"\<log\>(.*)", line)
            if match:
                comments += match.group(1)
                while lines:
                    line = lines.pop(0)
                    if re.search(r"\<\/log\>", line):
                        break
                    comments += line
                continue

        if module != "emc2":
            log.msg("email was not for the emc2 module, ignoring");
            return None

        if not branch:
            log.msg("email contained no branch");
            return None

        if not files:
            log.msg("email contained no files");
            return None

        if not author:
            log.msg("email contained no author");
            return None

        log.msg("accepting change from email:");
        log.msg("    branch = %s" % branch);
        log.msg("    author = %s" % author);
        log.msg("    files = %s" % files);
        log.msg("    comments = %s" % comments);

        # the "branch" value gets used by Buildbot as a CVS Tag, and for TRUNK the proper Tag is None
        if branch == "TRUNK":
            branch = None

        return changes.Change(author, files, comments, when=when, branch=branch)
Example #48
0
             mail_to, repr(ex), str(ex))
         logger(errstr)
         pass
 else:
     # could not use smtplib interface on linux without authorization
     # use sendmail interface
     tmp_file = None
     tmp_file_attach = None
     try:
         if mswindows:
             cmd = [
                 os.path.join(plesk_config.get('PRODUCT_ROOT_D'),
                              "admin", "bin", "plesk_sendmail.exe")
             ]
             cmd.append("--send")
             (mail_from_name, mail_from_addr) = parseaddr(mail_from)
             cmd.append("--from=" + str(Header(mail_from_addr)))
             cmd.append("--from-name=" + str(Header(mail_from_name)))
             cmd.append("--to=" + str(Header(mail_to)))
             cmd.append("--to-name=" + str(Header(mail_to)))
             if self.__reply_to:
                 (reply_to_name,
                  reply_to_addr) = parseaddr(self.__reply_to)
                 cmd.append("--reply-to=" + str(Header(reply_to_addr)))
             cmd.append("--subject=" + str(Header(subject)))
             tmp_file = os.path.join(
                 pmm_config.tmp_directory(),
                 "pmmcli_daemon_mail_" + str(random.randint(0, 1000)))
             with codecs.open(tmp_file, 'w', encoding='utf-8') as f:
                 f.write(body)
             logger("tmp_file %s" % tmp_file)
Example #49
0
def getActualAddr(addr):
    # "Foo Bar <*****@*****.**>" -> "*****@*****.**"
    return parseaddr(addr)[1].lower()
Example #50
0
def safe_mailaddr(addr):

    name, addr = parseaddr(addr)
    return formataddr((str(Header(unicode(name),
                                  header_charset)), addr.encode('ascii')))
Example #51
0
def process(res, args):
    mlist = res.mlist
    address = None
    password = None
    ok = False
    full = False
    if mlist.private_roster == 0:
        # Public rosters
        if args:
            if len(args) == 1:
                if mlist.Authenticate((mm_cfg.AuthListModerator,
                                       mm_cfg.AuthListAdmin),
                                      args[0]):
                    full = True
                else:
                    usage(res)
                    return STOP
            else:
                usage(res)
                return STOP
        ok = True
    elif mlist.private_roster == 1:
        # List members only
        if len(args) == 1:
            password = args[0]
            realname, address = parseaddr(res.msg['from'])
        elif len(args) == 2 and args[1].startswith('address='):
            password = args[0]
            address = args[1][8:]
        else:
            usage(res)
            return STOP
        if mlist.isMember(address) and mlist.Authenticate(
            (mm_cfg.AuthUser,
             mm_cfg.AuthListModerator,
             mm_cfg.AuthListAdmin),
            password, address):
            # Then
            ok = True
        if mlist.Authenticate(
            (mm_cfg.AuthListModerator,
             mm_cfg.AuthListAdmin),
            password):
            # Then
            ok = full = True
    else:
        # Admin only
        if len(args) <> 1:
            usage(res)
            return STOP
        if mlist.Authenticate((mm_cfg.AuthListModerator,
                               mm_cfg.AuthListAdmin),
                              args[0]):
            ok = full = True
    if not ok:
        res.results.append(
            _('You are not allowed to retrieve the list membership.'))
        return STOP
    # It's okay for this person to see the list membership
    dmembers = mlist.getDigestMemberKeys()
    rmembers = mlist.getRegularMemberKeys()
    if not dmembers and not rmembers:
        res.results.append(_('This list has no members.'))
        return
    # Convenience function
    def addmembers(members):
        for member in members:
            if not full and mlist.getMemberOption(member,
                                           mm_cfg.ConcealSubscription):
                continue
            realname = mlist.getMemberName(member)
            if realname:
                res.results.append('    %s (%s)' % (member, realname))
            else:
                res.results.append('    %s' % member)
    if rmembers:
        res.results.append(_('Non-digest (regular) members:'))
        addmembers(rmembers)
    if dmembers:
        res.results.append(_('Digest members:'))
        addmembers(dmembers)
Example #52
0
def send(sender, recipient, subject, body, contenttype, datetime, extraheaders=None, mailserver=None, folder=None):
    """Send an email.
    
    All arguments should be Unicode strings (plain ASCII works as well).
    
    Only the real name part of sender and recipient addresses may contain
    non-ASCII characters.
    
    The email will be properly MIME encoded and delivered though SMTP to
    localhost port 25.  This is easy to change if you want something different.
    
    The charset of the email will be the first one out of the list
    that can represent all the characters occurring in the email.
    """

    # Header class is smart enough to try US-ASCII, then the charset we
    # provide, then fall back to UTF-8.
    header_charset = 'ISO-8859-1'
    
    # We must choose the body charset manually
    for body_charset in CHARSET_LIST:
        try:
            body.encode(body_charset)
        except (UnicodeError, LookupError):
            pass
        else:
            break

    # Split real name (which is optional) and email address parts
    sender_name, sender_addr = parseaddr(sender)
    recipient_name, recipient_addr = parseaddr(recipient)
    
    # We must always pass Unicode strings to Header, otherwise it will
    # use RFC 2047 encoding even on plain ASCII strings.
    sender_name = str(Header(unicode(sender_name), header_charset))
    recipient_name = str(Header(unicode(recipient_name), header_charset))
    
    # Make sure email addresses do not contain non-ASCII characters
    sender_addr = sender_addr.encode('ascii')
    recipient_addr = recipient_addr.encode('ascii')
    
    # Create the message ('plain' stands for Content-Type: text/plain)
    msg = MIMEText(body.encode(body_charset), contenttype, body_charset)
    if IMAP_OVERRIDE_TO:
        msg['To'] = IMAP_OVERRIDE_TO
    else:
        msg['To'] = formataddr((recipient_name, recipient_addr))
    msg['Subject'] = Header(unicode(subject), header_charset)
    for hdr in extraheaders.keys():
        try:
            msg[hdr] = Header(unicode(extraheaders[hdr], header_charset))
        except:
            msg[hdr] = Header(extraheaders[hdr])
        
    fromhdr = formataddr((sender_name, sender_addr))
    if IMAP_MUNGE_FROM:
        msg['From'] = extraheaders['X-MUNGED-FROM']
    else:
        msg['From'] = fromhdr

    msg_as_string = msg.as_string()

    if IMAP_SEND:
        if not mailserver:
            try:
                (host,port) = IMAP_SERVER.split(':',1)
            except ValueError:
                host = IMAP_SERVER
                port = 993 if IMAP_SSL else 143
            try:
                if IMAP_SSL:
                    mailserver = imaplib.IMAP4_SSL(host, port)
                else:
                    mailserver = imaplib.IMAP4(host, port)
                # speed up interactions on TCP connections using small packets
                mailserver.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
                mailserver.login(IMAP_USER, IMAP_PASS)
            except KeyboardInterrupt:
                raise
            except Exception, e:
                print >>warn, ""
                print >>warn, ('Fatal error: could not connect to mail server "%s"' % IMAP_SERVER)
                print >>warn, ('Check your config.py file to confirm that IMAP_SERVER and other mail server settings are configured properly')
                if hasattr(e, 'reason'):
                    print >>warn, "Reason:", e.reason
                sys.exit(1)
        if not folder:
            folder = DEFAULT_IMAP_FOLDER
        #mailserver.debug = 4
        if mailserver.select(folder)[0] == 'NO':
            print >>warn, ("%s does not exist, creating" % folder)
            mailserver.create(folder)
            mailserver.subscribe(folder)
        mailserver.append(folder,'',imaplib.Time2Internaldate(datetime), msg_as_string)
        return mailserver
Example #53
0
def process(res, args):
    mlist = res.mlist
    digest = None
    password = None
    address = None
    realname = None
    # Parse the args
    argnum = 0
    for arg in args:
        if arg.lower().startswith('address='):
            address = arg[8:]
        elif argnum == 0:
            password = arg
        elif argnum == 1:
            if arg.lower() not in ('digest', 'nodigest'):
                res.results.append(_('Bad digest specifier: %(arg)s'))
                return STOP
            if arg.lower() == 'digest':
                digest = 1
            else:
                digest = 0
        else:
            res.results.append(_('Usage:'))
            res.results.append(gethelp(mlist))
            return STOP
        argnum += 1
    # Fix the password/digest issue
    if (digest is None and password
            and password.lower() in ('digest', 'nodigest')):
        if password.lower() == 'digest':
            digest = 1
        else:
            digest = 0
        password = None
    # Fill in empty defaults
    if digest is None:
        digest = mlist.digest_is_default
    if password is None:
        password = Utils.MakeRandomPassword()
    if address is None:
        realname, address = parseaddr(res.msg['from'])
        if not address:
            # Fall back to the sender address
            address = res.msg.get_sender()
        if not address:
            res.results.append(_('No valid address found to subscribe'))
            return STOP
        # Watch for encoded names
        try:
            h = make_header(decode_header(realname))
            # BAW: in Python 2.2, use just unicode(h)
            realname = h.__unicode__()
        except UnicodeError:
            realname = u''
        # Coerce to byte string if uh contains only ascii
        try:
            realname = realname.encode('us-ascii')
        except UnicodeError:
            pass
    # Create the UserDesc record and do a non-approved subscription
    listowner = mlist.GetOwnerEmail()
    userdesc = UserDesc(address, realname, password, digest)
    remote = res.msg.get_sender()
    try:
        mlist.AddMember(userdesc, remote)
    except Errors.MembershipIsBanned:
        res.results.append(
            _("""\
The email address you supplied is banned from this mailing list.
If you think this restriction is erroneous, please contact the list
owners at %(listowner)s."""))
        return STOP
    except Errors.MMBadEmailError:
        res.results.append(
            _("""\
Mailman won't accept the given email address as a valid address.
(E.g. it must have an @ in it.)"""))
        return STOP
    except Errors.MMHostileAddress:
        res.results.append(
            _("""\
Your subscription is not allowed because
the email address you gave is insecure."""))
        return STOP
    except Errors.MMAlreadyAMember:
        res.results.append(_('You are already subscribed!'))
        return STOP
    except Errors.MMCantDigestError:
        res.results.append(
            _('No one can subscribe to the digest of this list!'))
        return STOP
    except Errors.MMMustDigestError:
        res.results.append(_('This list only supports digest subscriptions!'))
        return STOP
    except Errors.MMSubscribeNeedsConfirmation:
        # We don't need to respond /and/ send a confirmation message.
        res.respond = 0
    except Errors.MMNeedApproval:
        res.results.append(
            _("""\
Your subscription request has been forwarded to the list administrator
at %(listowner)s for review."""))
    else:
        # Everything is a-ok
        res.results.append(_('Subscription request succeeded.'))
Example #54
0
    def parse(self, m, prefix=None):
        """Parse messages sent by the 'syncmail' program, as suggested by the
        sourceforge.net CVS Admin documentation. Syncmail is maintained at
        syncmail.sf.net .
        """
        # pretty much the same as freshcvs mail, not surprising since CVS is
        # the one creating most of the text

        # The mail is sent from the person doing the checkin. Assume that the
        # local username is enough to identify them (this assumes a one-server
        # cvs-over-rsh environment rather than the server-dirs-shared-over-NFS
        # model)
        name, addr = parseaddr(m["from"])
        if not addr:
            return None  # no From means this message isn't from FreshCVS
        at = addr.find("@")
        if at == -1:
            who = addr  # might still be useful
        else:
            who = addr[:at]

        # we take the time of receipt as the time of checkin. Not correct (it
        # depends upon the email latency), but it avoids the
        # out-of-order-changes issue. Also syncmail doesn't give us anything
        # better to work with, unless you count pulling the v1-vs-v2
        # timestamp out of the diffs, which would be ugly. TODO: Pulling the
        # 'Date:' header from the mail is a possibility, and
        # email.Utils.parsedate_tz may be useful. It should be configurable,
        # however, because there are a lot of broken clocks out there.
        when = util.now()

        subject = m["subject"]
        # syncmail puts the repository-relative directory in the subject:
        # mprefix + "%(dir)s %(file)s,%(oldversion)s,%(newversion)s", where
        # 'mprefix' is something that could be added by a mailing list
        # manager.
        # this is the only reasonable way to determine the directory name
        space = subject.find(" ")
        if space != -1:
            directory = subject[:space]
        else:
            directory = subject

        files = []
        comments = ""
        isdir = 0
        branch = None

        lines = list(body_line_iterator(m))
        while lines:
            line = lines.pop(0)

            if (line == "Modified Files:\n" or line == "Added Files:\n"
                    or line == "Removed Files:\n"):
                break

        while lines:
            line = lines.pop(0)
            if line == "\n":
                break
            if line == "Log Message:\n":
                lines.insert(0, line)
                break
            line = line.lstrip()
            line = line.rstrip()
            # note: syncmail will send one email per directory involved in a
            # commit, with multiple files if they were in the same directory.
            # Unlike freshCVS, it makes no attempt to collect all related
            # commits into a single message.

            # note: syncmail will report a Tag underneath the ... Files: line
            # e.g.:       Tag: BRANCH-DEVEL

            if line.startswith('Tag:'):
                branch = line.split(' ')[-1].rstrip()
                continue

            thesefiles = line.split(" ")
            for f in thesefiles:
                f = directory + "/" + f
                if prefix:
                    # insist that the file start with the prefix: we may get
                    # changes we don't care about too
                    if f.startswith(prefix):
                        f = f[len(prefix):]
                    else:
                        continue
                        break
                # TODO: figure out how new directories are described, set
                # .isdir
                files.append(f)

        if not files:
            return None

        while lines:
            line = lines.pop(0)
            if line == "Log Message:\n":
                break
        # message is terminated by "Index:..." (patch) or "--- NEW FILE.."
        # or "--- filename DELETED ---". Sigh.
        while lines:
            line = lines.pop(0)
            if line.find("Index: ") == 0:
                break
            if re.search(r"^--- NEW FILE", line):
                break
            if re.search(r" DELETED ---$", line):
                break
            comments += line
        comments = comments.rstrip() + "\n"

        change = changes.Change(who,
                                files,
                                comments,
                                isdir,
                                when=when,
                                branch=branch)

        return change
Example #55
0
def process(res, args):
    mlist = res.mlist
    address = None
    if not args:
        # They just want to get their existing password
        realname, address = parseaddr(res.msg['from'])
        if mlist.isMember(address):
            password = mlist.getMemberPassword(address)
            res.results.append(_('Your password is: %(password)s'))
            # Prohibit multiple password retrievals.
            return STOP
        else:
            listname = mlist.real_name
            res.results.append(
                _('You are not a member of the %(listname)s mailing list'))
            return STOP
    elif len(args) == 1 and args[0].startswith('address='):
        # They want their password, but they're posting from a different
        # address.  We /must/ return the password to the subscribed address.
        address = args[0][8:]
        res.returnaddr = address
        if mlist.isMember(address):
            password = mlist.getMemberPassword(address)
            res.results.append(_('Your password is: %(password)s'))
            # Prohibit multiple password retrievals.
            return STOP
        else:
            listname = mlist.real_name
            res.results.append(
                _('You are not a member of the %(listname)s mailing list'))
            return STOP
    elif len(args) == 2:
        # They are changing their password
        oldpasswd = args[0]
        newpasswd = args[1]
        realname, address = parseaddr(res.msg['from'])
        if mlist.isMember(address):
            if mlist.Authenticate((mm_cfg.AuthUser, mm_cfg.AuthListAdmin),
                                  oldpasswd, address):
                mlist.setMemberPassword(address, newpasswd)
                res.results.append(_('Password successfully changed.'))
            else:
                res.results.append(
                    _("""\
You did not give the correct old password, so your password has not been
changed.  Use the no argument version of the password command to retrieve your
current password, then try again."""))
                res.results.append(_('\nUsage:'))
                res.results.append(gethelp(mlist))
                return STOP
        else:
            listname = mlist.real_name
            res.results.append(
                _('You are not a member of the %(listname)s mailing list'))
            return STOP
    elif len(args) == 3 and args[2].startswith('address='):
        # They want to change their password, and they're sending this from a
        # different address than what they're subscribed with.  Be sure the
        # response goes to the subscribed address.
        oldpasswd = args[0]
        newpasswd = args[1]
        address = args[2][8:]
        res.returnaddr = address
        if mlist.isMember(address):
            if mlist.Authenticate((mm_cfg.AuthUser, mm_cfg.AuthListAdmin),
                                  oldpasswd, address):
                mlist.setMemberPassword(address, newpasswd)
                res.results.append(_('Password successfully changed.'))
            else:
                res.results.append(
                    _("""\
You did not give the correct old password, so your password has not been
changed.  Use the no argument version of the password command to retrieve your
current password, then try again."""))
                res.results.append(_('\nUsage:'))
                res.results.append(gethelp(mlist))
                return STOP
        else:
            listname = mlist.real_name
            res.results.append(
                _('You are not a member of the %(listname)s mailing list'))
            return STOP
Example #56
0
    def apply(self, ui):
        # get message to forward if not given in constructor
        if not self.message:
            self.message = ui.current_buffer.get_selected_message()
        mail = self.message.get_email()

        # set body text
        name, address = self.message.get_author()
        timestamp = self.message.get_date()
        qf = settings.get_hook('reply_prefix')
        if qf:
            quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman)
        else:
            quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp)
        mailcontent = quotestring
        quotehook = settings.get_hook('text_quote')
        if quotehook:
            mailcontent += quotehook(self.message.accumulate_body())
        else:
            quote_prefix = settings.get('quote_prefix')
            for line in self.message.accumulate_body().splitlines():
                mailcontent += quote_prefix + line + '\n'

        envelope = Envelope(bodytext=mailcontent)

        # copy subject
        subject = decode_header(mail.get('Subject', ''))
        reply_subject_hook = settings.get_hook('reply_subject')
        if reply_subject_hook:
            subject = reply_subject_hook(subject)
        else:
            rsp = settings.get('reply_subject_prefix')
            if not subject.lower().startswith(('re:', rsp.lower())):
                subject = rsp + subject
        envelope.add('Subject', subject)

        # set From-header and sending account
        try:
            from_header, account = determine_sender(mail, 'reply')
        except AssertionError as e:
            ui.notify(e.message, priority='error')
            return
        envelope.add('From', from_header)

        # set To
        sender = mail['Reply-To'] or mail['From']
        my_addresses = settings.get_addresses()
        sender_address = parseaddr(sender)[1]
        cc = ''

        # check if reply is to self sent message
        if sender_address in my_addresses:
            recipients = [mail['To']]
            emsg = 'Replying to own message, set recipients to: %s' \
                % recipients
            logging.debug(emsg)
        else:
            recipients = [sender]

        if self.groupreply:
            # make sure that our own address is not included
            # if the message was self-sent, then our address is not included
            MFT = mail.get_all('Mail-Followup-To', [])
            followupto = self.clear_my_address(my_addresses, MFT)
            if followupto and settings.get('honor_followup_to'):
                logging.debug('honor followup to: %s', followupto)
                recipients = [followupto]
                # since Mail-Followup-To was set, ignore the Cc header
            else:
                if sender != mail['From']:
                    recipients.append(mail['From'])

                # append To addresses if not replying to self sent message
                if sender_address not in my_addresses:
                    cleared = self.clear_my_address(my_addresses,
                                                    mail.get_all('To', []))
                    recipients.append(cleared)

                # copy cc for group-replies
                if 'Cc' in mail:
                    cc = self.clear_my_address(my_addresses,
                                               mail.get_all('Cc', []))
                    envelope.add('Cc', decode_header(cc))

        to = ', '.join(recipients)
        logging.debug('reply to: %s' % to)
        envelope.add('To', decode_header(to))

        # if any of the recipients is a mailinglist that we are subscribed to,
        # set Mail-Followup-To header so that duplicates are avoided
        if settings.get('followup_to'):
            # to and cc are already cleared of our own address
            allrecipients = [to] + [cc]
            lists = settings.get('mailinglists')
            # check if any recipient address matches a known mailing list
            if any([addr in lists for n, addr in getaddresses(allrecipients)]):
                followupto = ', '.join(allrecipients)
                logging.debug('mail followup to: %s' % followupto)
                envelope.add('Mail-Followup-To', decode_header(followupto))

        # set In-Reply-To header
        envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id())

        # set References header
        old_references = mail.get('References', '')
        if old_references:
            old_references = old_references.split()
            references = old_references[-8:]
            if len(old_references) > 8:
                references = old_references[:1] + references
            references.append('<%s>' % self.message.get_message_id())
            envelope.add('References', ' '.join(references))
        else:
            envelope.add('References', '<%s>' % self.message.get_message_id())

        # continue to compose
        ui.apply_command(
            ComposeCommand(envelope=envelope, spawn=self.force_spawn))
Example #57
0
class MaildirRunner(Runner):
    # This class is much different than most runners because it pulls files
    # of a different format than what scripts/post and friends leaves.  The
    # files this runner reads are just single message files as dropped into
    # the directory by the MTA.  This runner will read the file, and enqueue
    # it in the expected qfiles directory for normal processing.
    def __init__(self, slice=None, numslices=1):
        # Don't call the base class constructor, but build enough of the
        # underlying attributes to use the base class's implementation.
        self._stop = 0
        self._dir = os.path.join(mm_cfg.MAILDIR_DIR, 'new')
        self._cur = os.path.join(mm_cfg.MAILDIR_DIR, 'cur')
        self._parser = Parser(Message)

    def _oneloop(self):
        # Refresh this each time through the list.  BAW: could be too
        # expensive.
        listnames = Utils.list_names()
        # Cruise through all the files currently in the new/ directory
        try:
            files = os.listdir(self._dir)
        except OSError, e:
            if e.errno <> errno.ENOENT: raise
            # Nothing's been delivered yet
            return 0
        for file in files:
            srcname = os.path.join(self._dir, file)
            dstname = os.path.join(self._cur, file + ':1,P')
            xdstname = os.path.join(self._cur, file + ':1,X')
            try:
                os.rename(srcname, dstname)
            except OSError, e:
                if e.errno == errno.ENOENT:
                    # Some other MaildirRunner beat us to it
                    continue
                syslog('error', 'Could not rename maildir file: %s', srcname)
                raise
            # Now open, read, parse, and enqueue this message
            try:
                fp = open(dstname)
                try:
                    msg = self._parser.parse(fp)
                finally:
                    fp.close()
                # Now we need to figure out which queue of which list this
                # message was destined for.  See verp_bounce() in
                # BounceRunner.py for why we do things this way.
                vals = []
                for header in ('delivered-to', 'envelope-to', 'apparently-to'):
                    vals.extend(msg.get_all(header, []))
                for field in vals:
                    to = parseaddr(field)[1]
                    if not to:
                        continue
                    mo = lre.match(to)
                    if not mo:
                        # This isn't an address we care about
                        continue
                    listname, subq = mo.group('listname', 'subq')
                    if listname in listnames:
                        break
                else:
                    # As far as we can tell, this message isn't destined for
                    # any list on the system.  What to do?
                    syslog('error', 'Message apparently not for any list: %s',
                           xdstname)
                    os.rename(dstname, xdstname)
                    continue
                # BAW: blech, hardcoded
                msgdata = {'listname': listname}
                # -admin is deprecated
                if subq in ('bounces', 'admin'):
                    queue = get_switchboard(mm_cfg.BOUNCEQUEUE_DIR)
                elif subq == 'confirm':
                    msgdata['toconfirm'] = 1
                    queue = get_switchboard(mm_cfg.CMDQUEUE_DIR)
                elif subq in ('join', 'subscribe'):
                    msgdata['tojoin'] = 1
                    queue = get_switchboard(mm_cfg.CMDQUEUE_DIR)
                elif subq in ('leave', 'unsubscribe'):
                    msgdata['toleave'] = 1
                    queue = get_switchboard(mm_cfg.CMDQUEUE_DIR)
                elif subq == 'owner':
                    msgdata.update({
                        'toowner': 1,
                        'envsender': Utils.get_site_email(extra='bounces'),
                        'pipeline': mm_cfg.OWNER_PIPELINE,
                        })
                    queue = get_switchboard(mm_cfg.INQUEUE_DIR)
                elif subq is None:
                    msgdata['tolist'] = 1
                    queue = get_switchboard(mm_cfg.INQUEUE_DIR)
                elif subq == 'request':
                    msgdata['torequest'] = 1
                    queue = get_switchboard(mm_cfg.CMDQUEUE_DIR)
                else:
                    syslog('error', 'Unknown sub-queue: %s', subq)
                    os.rename(dstname, xdstname)
                    continue
                queue.enqueue(msg, msgdata)
                os.unlink(dstname)
            except Exception, e:
                os.rename(dstname, xdstname)
                syslog('error', str(e))
Example #58
0
def fixfile(inpath, input_lines, outfile):
    from email.Utils import parseaddr
    basename = os.path.basename(inpath)
    infile = iter(input_lines)
    # head
    header = []
    pep = ""
    title = ""
    for line in infile:
        if not line.strip():
            break
        if line[0].strip():
            if ":" not in line:
                break
            key, value = line.split(":", 1)
            value = value.strip()
            header.append((key, value))
        else:
            # continuation line
            key, value = header[-1]
            value = value + line
            header[-1] = key, value
        if key.lower() == "title":
            title = value
        elif key.lower() == "pep":
            pep = value

    if pep:
        title = "PEP " + pep + " -- " + title
    r = random.choice(range(64))
    print >> outfile, COMMENT
    print >> outfile, '<div class="header">\n<table border="0" class="rfc2822">'
    for k, v in header:
        if k.lower() in ('author', 'discussions-to'):
            mailtos = []
            for part in re.split(',\s*', v):
                if '@' in part:
                    realname, addr = parseaddr(part)
                    if k.lower() == 'discussions-to':
                        m = linkemail(addr, pep)
                    else:
                        m = fixemail(addr, pep)
                    mailtos.append('%s &lt;%s&gt;' % (realname, m))
                elif part.startswith('http:'):
                    mailtos.append(
                        '<a href="%s">%s</a>' % (part, part))
                else:
                    mailtos.append(part)
            v = COMMASPACE.join(mailtos)
        elif k.lower() in ('replaces', 'replaced-by', 'requires'):
            otherpeps = ''
            for otherpep in re.split(',?\s+', v):
                otherpep = int(otherpep)
                otherpeps += PEPANCHOR % (otherpep, otherpep)
            v = otherpeps
        elif k.lower() in ('last-modified',):
            date = v or time.strftime('%Y-%m-%d',
                                      time.localtime(os.stat(inpath)[8]))
            if date.startswith('$' 'Date: ') and date.endswith(' $'):
                date = date[6:-2]
            try:
                url = PEPCVSURL % int(pep)
                v = '<a href="%s">%s</a> ' % (url, cgi.escape(date))
            except ValueError, error:
                v = date
        elif k.lower() == 'content-type':
            url = PEPURL % 9
            pep_type = v or 'text/plain'
            v = '<a href="%s">%s</a> ' % (url, cgi.escape(pep_type))
Example #59
0
    def process_message(self, peer, mailfrom, rcpttos, data):
        try:
            mailfrom = parseaddr(mailfrom)[1]
            logger.debug('Receiving message from: %s:%d' % peer)
            logger.debug('Message addressed from: %s' % mailfrom)
            logger.debug('Message addressed to: %s' % str(rcpttos))

            msg = email.message_from_string(data)
            subject = ''
            for encoded_string, charset in decode_header(msg.get('Subject')):
                try:
                    if charset is not None:
                        subject += encoded_string.decode(charset)
                    else:
                        subject += encoded_string
                except:
                    logger.exception(
                        'Error reading part of subject: %s charset %s' %
                        (encoded_string, charset))

            logger.debug('Subject: %s' % subject)

            text_parts = []
            html_parts = []
            attachments = {}

            #logger.debug('Headers: %s' % msg.items())

            # YOU CAN DO SOME SECURITY CONTROLS HERE
            #if (not mailfrom.endswith("@hankenfeld.at") or
            #    not msg.get('Mail-Header') == 'expected value'):
            #    raise Exception("Email not trusted")

            # loop on the email parts
            for part in msg.walk():
                if part.get_content_maintype() == 'multipart':
                    continue

                c_type = part.get_content_type()
                c_disp = part.get('Content-Disposition')

                # text parts will be appended to text_parts
                if c_type == 'text/plain' and c_disp == None:
                    text_parts.append(part.get_payload(decode=True).strip())
                # ignore html part
                elif c_type == 'text/html':
                    html_parts.append(part.get_payload(decode=True).strip())
                # attachments will be sent as files in the POST request
                else:
                    filename = part.get_filename()
                    filecontent = part.get_payload(decode=True)
                    if filecontent is not None:
                        if filename is None:
                            filename = 'untitled'
                        attachments['file%d' %
                                    len(attachments)] = (filename, filecontent)

            body = '\n'.join(text_parts)
            htmlbody = '\n'.join(html_parts)

        except:
            logger.exception('Error reading incoming email')
        else:
            # this data will be sent as POST data
            edata = {
                'subject': subject,
                'body': body,
                'htmlbody': htmlbody,
                'from': mailfrom,
                'attachments': []
            }
            savedata = {
                'sender_ip': peer[0],
                'from': mailfrom,
                'rcpts': rcpttos,
                'raw': data,
                'parsed': edata
            }

            filenamebase = str(int(round(time.time() * 1000)))

            for em in rcpttos:
                em = em.lower()
                if not re.match(r"[^@\s]+@[^@\s]+\.[a-zA-Z0-9]+$", em):
                    logger.exception('Invalid recipient: %s' % em)
                    continue

                domain = em.split('@')[1]
                if (DISCARD_UNKNOWN and not domain in DOMAINS):
                    logger.info('Discarding email for unknown domain: %s' %
                                domain)
                    continue

                if not os.path.exists("../data/" + em):
                    os.mkdir("../data/" + em, 0o755)

                #same attachments if any
                for att in attachments:
                    if not os.path.exists("../data/" + em + "/attachments"):
                        os.mkdir("../data/" + em + "/attachments", 0o755)
                    attd = attachments[att]
                    file = open(
                        "../data/" + em + "/attachments/" + filenamebase +
                        "-" + attd[0], 'wb')
                    file.write(attd[1])
                    file.close()
                    edata["attachments"].append(filenamebase + "-" + attd[0])

                # save actual json data
                with open("../data/" + em + "/" + filenamebase + ".json",
                          "w") as outfile:
                    json.dump(savedata, outfile)

            #print edata
            cleanup()
        return
Example #60
0
    def sendNotificationEmail(self, addresses, subject, rstText):
        """
        Send a notification email to the list of addresses

        XXX Note to self [maurits]: look at this blog post from Marius
        Gedminas, titled "Sending Unicode emails in Python":
        http://mg.pov.lt/blog/unicode-emails-in-python.html
        """

        if not self.getSendNotificationEmailsTo() or not addresses:
            return

        portal_url = getToolByName(self, 'portal_url')
        plone_utils = getToolByName(self, 'plone_utils')

        portal = portal_url.getPortalObject()
        mailHost = plone_utils.getMailHost()
        charset = portal.getProperty('email_charset', '')
        if not charset:
            charset = plone_utils.getSiteEncoding()
        from_address = portal.getProperty('email_from_address', '')

        if not from_address:
            log('Cannot send notification email: email sender address not set')
            return
        from_name = portal.getProperty('email_from_name', '')
        mfrom = formataddr((from_name, from_address))
        if parseaddr(mfrom)[1] != from_address:
            # formataddr probably got confused by special characters.
            mfrom = from_address

        email_msg = MIMEMultipart('alternative')
        email_msg.epilogue = ''

        # Translate the body text
        ts = getGlobalTranslationService()
        rstText = ts.translate('Poi', rstText, context=self)
        # We must choose the body charset manually
        for body_charset in 'US-ASCII', charset, 'UTF-8':
            try:
                rstText = rstText.encode(body_charset)
            except UnicodeError:
                pass
            else:
                break

        textPart = MIMEText(rstText, 'plain', body_charset)
        email_msg.attach(textPart)
        htmlPart = MIMEText(renderHTML(rstText, charset=body_charset), 'html',
                            body_charset)
        email_msg.attach(htmlPart)

        # Make the subject unicode and translate it too.
        subject = safe_unicode(subject, charset)
        subject = ts.translate('Poi', subject, context=self)
        for address in addresses:
            address = safe_unicode(address, charset)
            if not address:
                # E-mail address may not be known, for example in case
                # of LDAP users.  See:
                # http://plone.org/products/poi/issues/213
                continue
            try:
                # Note that charset is only used for the headers, not
                # for the body text as that is a Message/MIMEText already.
                mailHost.secureSend(message=email_msg,
                                    mto=address,
                                    mfrom=mfrom,
                                    subject=subject,
                                    charset=charset)
            except (socket.error, SMTPException), exc:
                log_exc(('Could not send email from %s to %s regarding issue '
                         'in tracker %s\ntext is:\n%s\n') %
                        (mfrom, address, self.absolute_url(), email_msg))
                log_exc("Reason: %s: %r" % (exc.__class__.__name__, str(exc)))
            except: