app/django/core/mail.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
    69 class BadHeaderError(ValueError):
    69 class BadHeaderError(ValueError):
    70     pass
    70     pass
    71 
    71 
    72 def forbid_multi_line_headers(name, val):
    72 def forbid_multi_line_headers(name, val):
    73     """Forbids multi-line headers, to prevent header injection."""
    73     """Forbids multi-line headers, to prevent header injection."""
       
    74     val = force_unicode(val)
    74     if '\n' in val or '\r' in val:
    75     if '\n' in val or '\r' in val:
    75         raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
    76         raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
    76     try:
    77     try:
    77         val = force_unicode(val).encode('ascii')
    78         val = val.encode('ascii')
    78     except UnicodeEncodeError:
    79     except UnicodeEncodeError:
    79         if name.lower() in ('to', 'from', 'cc'):
    80         if name.lower() in ('to', 'from', 'cc'):
    80             result = []
    81             result = []
    81             for item in val.split(', '):
    82             for item in val.split(', '):
    82                 nm, addr = parseaddr(item)
    83                 nm, addr = parseaddr(item)
    83                 nm = str(Header(nm, settings.DEFAULT_CHARSET))
    84                 nm = str(Header(nm, settings.DEFAULT_CHARSET))
    84                 result.append(formataddr((nm, str(addr))))
    85                 result.append(formataddr((nm, str(addr))))
    85             val = ', '.join(result)
    86             val = ', '.join(result)
    86         else:
    87         else:
    87             val = Header(force_unicode(val), settings.DEFAULT_CHARSET)
    88             val = Header(val, settings.DEFAULT_CHARSET)
       
    89     else:
       
    90         if name.lower() == 'subject':
       
    91             val = Header(val)
    88     return name, val
    92     return name, val
    89 
    93 
    90 class SafeMIMEText(MIMEText):
    94 class SafeMIMEText(MIMEText):
    91     def __setitem__(self, name, val):
    95     def __setitem__(self, name, val):
    92         name, val = forbid_multi_line_headers(name, val)
    96         name, val = forbid_multi_line_headers(name, val)
   172             self.close()
   176             self.close()
   173         return num_sent
   177         return num_sent
   174 
   178 
   175     def _send(self, email_message):
   179     def _send(self, email_message):
   176         """A helper method that does the actual sending."""
   180         """A helper method that does the actual sending."""
   177         if not email_message.to:
   181         if not email_message.recipients():
   178             return False
   182             return False
   179         try:
   183         try:
   180             self.connection.sendmail(email_message.from_email,
   184             self.connection.sendmail(email_message.from_email,
   181                     email_message.recipients(),
   185                     email_message.recipients(),
   182                     email_message.message().as_string())
   186                     email_message.message().as_string())
   203         All strings used to create the message can be unicode strings (or UTF-8
   207         All strings used to create the message can be unicode strings (or UTF-8
   204         bytestrings). The SafeMIMEText class will handle any necessary encoding
   208         bytestrings). The SafeMIMEText class will handle any necessary encoding
   205         conversions.
   209         conversions.
   206         """
   210         """
   207         if to:
   211         if to:
       
   212             assert not isinstance(to, basestring), '"to" argument must be a list or tuple'
   208             self.to = list(to)
   213             self.to = list(to)
   209         else:
   214         else:
   210             self.to = []
   215             self.to = []
   211         if bcc:
   216         if bcc:
       
   217             assert not isinstance(bcc, basestring), '"bcc" argument must be a list or tuple'
   212             self.bcc = list(bcc)
   218             self.bcc = list(bcc)
   213         else:
   219         else:
   214             self.bcc = []
   220             self.bcc = []
   215         self.from_email = from_email or settings.DEFAULT_FROM_EMAIL
   221         self.from_email = from_email or settings.DEFAULT_FROM_EMAIL
   216         self.subject = subject
   222         self.subject = subject
   239                 else:
   245                 else:
   240                     msg.attach(self._create_attachment(*attachment))
   246                     msg.attach(self._create_attachment(*attachment))
   241         msg['Subject'] = self.subject
   247         msg['Subject'] = self.subject
   242         msg['From'] = self.from_email
   248         msg['From'] = self.from_email
   243         msg['To'] = ', '.join(self.to)
   249         msg['To'] = ', '.join(self.to)
   244         msg['Date'] = formatdate()
   250 
   245         msg['Message-ID'] = make_msgid()
   251         # Email header names are case-insensitive (RFC 2045), so we have to
       
   252         # accommodate that when doing comparisons.
       
   253         header_names = [key.lower() for key in self.extra_headers]
       
   254         if 'date' not in header_names:
       
   255             msg['Date'] = formatdate()
       
   256         if 'message-id' not in header_names:
       
   257             msg['Message-ID'] = make_msgid()
   246         for name, value in self.extra_headers.items():
   258         for name, value in self.extra_headers.items():
   247             msg[name] = value
   259             msg[name] = value
   248         return msg
   260         return msg
   249 
   261 
   250     def recipients(self):
   262     def recipients(self):