thirdparty/google_appengine/google/appengine/api/mail.py
changeset 2273 e4cb9c53db3e
parent 297 35211afcd563
child 2864 2e0b0af889be
equal deleted inserted replaced
2272:26491ee91e33 2273:e4cb9c53db3e
    26 
    26 
    27 
    27 
    28 from email import MIMEBase
    28 from email import MIMEBase
    29 from email import MIMEMultipart
    29 from email import MIMEMultipart
    30 from email import MIMEText
    30 from email import MIMEText
    31 import mimetypes
       
    32 import types
    31 import types
    33 
    32 
    34 from google.appengine.api import api_base_pb
    33 from google.appengine.api import api_base_pb
    35 from google.appengine.api import apiproxy_stub_map
    34 from google.appengine.api import apiproxy_stub_map
    36 from google.appengine.api import mail_service_pb
    35 from google.appengine.api import mail_service_pb
    49   mail_service_pb.MailServiceError.INVALID_ATTACHMENT_TYPE:
    48   mail_service_pb.MailServiceError.INVALID_ATTACHMENT_TYPE:
    50     InvalidAttachmentTypeError,
    49     InvalidAttachmentTypeError,
    51 }
    50 }
    52 
    51 
    53 
    52 
    54 EXTENSION_WHITELIST = set([
    53 EXTENSION_MIME_MAP = {
    55   'bmp',
    54   'asc': 'text/plain',
    56   'css',
    55   'bmp': 'image/x-ms-bmp',
    57   'csv',
    56   'css': 'text/css',
    58   'gif',
    57   'csv': 'text/csv',
    59   'html', 'htm',
    58   'diff': 'text/plain',
    60   'jpeg', 'jpg', 'jpe',
    59   'gif': 'image/gif',
    61   'pdf',
    60   'htm': 'text/html',
    62   'png',
    61   'html': 'text/html',
    63   'rss',
    62   'ics': 'text/calendar',
    64   'text', 'txt', 'asc', 'diff', 'pot',
    63   'jpe': 'image/jpeg',
    65   'tiff', 'tif',
    64   'jpeg': 'image/jpeg',
    66   'wbmp',
    65   'jpg': 'image/jpeg',
    67 ])
    66   'pdf': 'application/pdf',
       
    67   'png': 'image/png',
       
    68   'pot': 'text/plain',
       
    69   'rss': 'text/rss+xml',
       
    70   'text': 'text/plain',
       
    71   'tif': 'image/tiff',
       
    72   'tiff': 'image/tiff',
       
    73   'txt': 'text/plain',
       
    74   'vcf': 'text/directory',
       
    75   'wbmp': 'image/vnd.wap.wbmp',
       
    76 }
       
    77 
       
    78 EXTENSION_WHITELIST = frozenset(EXTENSION_MIME_MAP.iterkeys())
    68 
    79 
    69 
    80 
    70 def invalid_email_reason(email_address, field):
    81 def invalid_email_reason(email_address, field):
    71   """Determine reason why email is invalid
    82   """Determine reason why email is invalid
    72 
    83 
   232   message.send(make_sync_call)
   243   message.send(make_sync_call)
   233 
   244 
   234 SendMailToAdmins = send_mail_to_admins
   245 SendMailToAdmins = send_mail_to_admins
   235 
   246 
   236 
   247 
       
   248 def _GetMimeType(file_name):
       
   249   """Determine mime-type from file name.
       
   250 
       
   251   Parses file name and determines mime-type based on extension map.
       
   252 
       
   253   This method is not part of the public API and should not be used by
       
   254   applications.
       
   255 
       
   256   Args:
       
   257     file_name: File to determine extension for.
       
   258 
       
   259   Returns:
       
   260     Mime-type associated with file extension.
       
   261 
       
   262   Raises:
       
   263     InvalidAttachmentTypeError when the file name of an attachment.
       
   264   """
       
   265   extension_index = file_name.rfind('.')
       
   266   if extension_index == -1:
       
   267     raise InvalidAttachmentTypeError(
       
   268         "File '%s' does not have an extension" % file_name)
       
   269   extension = file_name[extension_index + 1:]
       
   270   mime_type = EXTENSION_MIME_MAP.get(extension, None)
       
   271   if mime_type is None:
       
   272     raise InvalidAttachmentTypeError(
       
   273         "Extension '%s' is not supported." % extension)
       
   274   return mime_type
       
   275 
       
   276 
   237 def mail_message_to_mime_message(protocol_message):
   277 def mail_message_to_mime_message(protocol_message):
   238   """Generate a MIMEMultitype message from protocol buffer.
   278   """Generate a MIMEMultitype message from protocol buffer.
   239 
   279 
   240   Generates a complete MIME multi-part email object from a MailMessage
   280   Generates a complete MIME multi-part email object from a MailMessage
   241   protocol buffer.  The body fields are sent as individual alternatives
   281   protocol buffer.  The body fields are sent as individual alternatives
   247   Args:
   287   Args:
   248     message: Message PB to convert to MIMEMultitype.
   288     message: Message PB to convert to MIMEMultitype.
   249 
   289 
   250   Returns:
   290   Returns:
   251     MIMEMultitype representing the provided MailMessage.
   291     MIMEMultitype representing the provided MailMessage.
       
   292 
       
   293   Raises:
       
   294     InvalidAttachmentTypeError when the file name of an attachment
   252   """
   295   """
   253   parts = []
   296   parts = []
   254   if protocol_message.has_textbody():
   297   if protocol_message.has_textbody():
   255     parts.append(MIMEText.MIMEText(protocol_message.textbody()))
   298     parts.append(MIMEText.MIMEText(protocol_message.textbody()))
   256   if protocol_message.has_htmlbody():
   299   if protocol_message.has_htmlbody():
   262   else:
   305   else:
   263     payload = [MIMEMultipart.MIMEMultipart('alternative', _subparts=parts)]
   306     payload = [MIMEMultipart.MIMEMultipart('alternative', _subparts=parts)]
   264 
   307 
   265   result = MIMEMultipart.MIMEMultipart(_subparts=payload)
   308   result = MIMEMultipart.MIMEMultipart(_subparts=payload)
   266   for attachment in protocol_message.attachment_list():
   309   for attachment in protocol_message.attachment_list():
   267     mime_type, encoding = mimetypes.guess_type(attachment.filename())
   310     file_name = attachment.filename()
   268     assert mime_type is not None
   311     mime_type = _GetMimeType(file_name)
   269     maintype, subtype = mime_type.split('/')
   312     maintype, subtype = mime_type.split('/')
   270     mime_attachment = MIMEBase.MIMEBase(maintype, subtype)
   313     mime_attachment = MIMEBase.MIMEBase(maintype, subtype)
   271     mime_attachment.add_header('Content-Disposition',
   314     mime_attachment.add_header('Content-Disposition',
   272                                'attachment',
   315                                'attachment',
   273                                filename=attachment.filename())
   316                                filename=attachment.filename())
   274     mime_attachment.set_charset(encoding)
       
   275     mime_attachment.set_payload(attachment.data())
   317     mime_attachment.set_payload(attachment.data())
   276     result.attach(mime_attachment)
   318     result.attach(mime_attachment)
   277 
   319 
   278   if protocol_message.to_size():
   320   if protocol_message.to_size():
   279     result['To'] = ', '.join(protocol_message.to_list())
   321     result['To'] = ', '.join(protocol_message.to_list())
   281     result['Cc'] = ', '.join(protocol_message.cc_list())
   323     result['Cc'] = ', '.join(protocol_message.cc_list())
   282   if protocol_message.bcc_size():
   324   if protocol_message.bcc_size():
   283     result['Bcc'] = ', '.join(protocol_message.bcc_list())
   325     result['Bcc'] = ', '.join(protocol_message.bcc_list())
   284 
   326 
   285   result['From'] = protocol_message.sender()
   327   result['From'] = protocol_message.sender()
   286   result['ReplyTo'] = protocol_message.replyto()
   328   result['Reply-To'] = protocol_message.replyto()
   287   result['Subject'] = protocol_message.subject()
   329   result['Subject'] = protocol_message.subject()
   288 
   330 
   289   return result
   331   return result
   290 
   332 
   291 MailMessageToMIMEMessage = mail_message_to_mime_message
   333 MailMessageToMIMEMessage = mail_message_to_mime_message
   374     if not hasattr(self, 'subject'):
   416     if not hasattr(self, 'subject'):
   375       raise MissingSubjectError()
   417       raise MissingSubjectError()
   376     if not hasattr(self, 'body') and not hasattr(self, 'html'):
   418     if not hasattr(self, 'body') and not hasattr(self, 'html'):
   377       raise MissingBodyError()
   419       raise MissingBodyError()
   378     if hasattr(self, 'attachments'):
   420     if hasattr(self, 'attachments'):
   379       for filename, data in _attachment_sequence(self.attachments):
   421       for file_name, data in _attachment_sequence(self.attachments):
   380         split_filename = filename.split('.')
   422         _GetMimeType(file_name)
   381         if len(split_filename) < 2:
       
   382           raise InvalidAttachmentTypeError()
       
   383         if split_filename[-1] not in EXTENSION_WHITELIST:
       
   384           raise InvalidAttachmentTypeError()
       
   385         mime_type, encoding = mimetypes.guess_type(filename)
       
   386         if mime_type is None:
       
   387           raise InvalidAttachmentTypeError()
       
   388 
   423 
   389   def CheckInitialized(self):
   424   def CheckInitialized(self):
   390     self.check_initialized()
   425     self.check_initialized()
   391 
   426 
   392   def is_initialized(self):
   427   def is_initialized(self):