Example #1
0
def send_simple_mail(sender, receiver, subject, msgtxt, attachments=None, bcc=None, sendername=None, receivername=None):
	# attachment format, each is a tuple of (name, mimetype,contents)
	# content should be *binary* and not base64 encoded, since we need to
	# use the base64 routines from the email library to get a properly
	# formatted output message
	msg = MIMEMultipart()
	msg['Subject'] = subject
	msg['To'] = _encoded_email_header(receivername, receiver)
	msg['From'] = _encoded_email_header(sendername, sender)
	msg['Date'] = formatdate(localtime=True)

	msg.attach(MIMEText(msgtxt, _charset='utf-8'))

	if attachments:
		for filename, contenttype, content in attachments:
			main,sub = contenttype.split('/')
			part = MIMENonMultipart(main,sub)
			part.set_payload(content)
			part.add_header('Content-Disposition', 'attachment; filename="%s"' % filename)
			encoders.encode_base64(part)
			msg.attach(part)


	# Just write it to the queue, so it will be transactionally rolled back
	QueuedMail(sender=sender, receiver=receiver, fullmsg=msg.as_string()).save()
	# Any bcc is just entered as a separate email
	if bcc:
		QueuedMail(sender=sender, receiver=bcc, fullmsg=msg.as_string()).save()
    def addAttachment(self, attachment, filename, mimetype=None):
        if not mimetype:
            mimetype = mimetypes.guess_type(filename)[0]
        if not mimetype:
            raise Exception("could not determine MIME type for ", filename)
        if "/" in mimetype:
            major, minor = mimetype.split("/")
        else:
            major = mimetype
            minor = None
        if not self.hasAttachments:
            body = self.msg.set_payload()
            newMsg = MIMEMultipart()
            newMsg.attach(MIMEText(body))

            for header, value in self.msg.items():
                newMsg[header] = value

            self.msg = newMsg
            self.hasAttachments = True
        subMessage = MIMENonMultipart(major, minor, name=filename)
        subMessage.set_payload(attachment)

        if major == "text":
            encoder = Encoders.encode_quopri
        else:
            encoder = Encoders.encode_base64
        encoder(subMessage)
        self.msg.attach(subMessage)
Example #3
0
 def __init__(self, _audiodata, _subtype = None, _encoder = encoders.encode_base64, **_params):
     """Create an audio/* type MIME document.
     
     _audiodata is a string containing the raw audio data.  If this data
     can be decoded by the standard Python `sndhdr' module, then the
     subtype will be automatically included in the Content-Type header.
     Otherwise, you can specify  the specific audio subtype via the
     _subtype parameter.  If _subtype is not given, and no subtype can be
     guessed, a TypeError is raised.
     
     _encoder is a function which will perform the actual encoding for
     transport of the image data.  It takes one argument, which is this
     Image instance.  It should use get_payload() and set_payload() to
     change the payload to the encoded form.  It should also add any
     Content-Transfer-Encoding or other headers to the message as
     necessary.  The default encoding is Base64.
     
     Any additional keyword arguments are passed to the base class
     constructor, which turns them into parameters on the Content-Type
     header.
     """
     if _subtype is None:
         _subtype = _whatsnd(_audiodata)
     if _subtype is None:
         raise TypeError('Could not find audio MIME subtype')
     MIMENonMultipart.__init__(self, 'audio', _subtype, **_params)
     self.set_payload(_audiodata)
     _encoder(self)
     return
Example #4
0
 def __init__(self, message):
     MIMENonMultipart.__init__(self, 'text', 'plain',
                               charset='utf-8')
     utf8qp = email.charset.Charset('utf-8')
     utf8qp.body_encoding = email.charset.QP
     payload = message.get_payload(decode=True)
     self.set_payload(payload, charset=utf8qp)
Example #5
0
    def as_message(self, escape_addresses=True):
        # http://wordeology.com/computer/how-to-send-good-unicode-email-with-python.html
        # http://stackoverflow.com/questions/31714221/how-to-send-an-email-with-quoted
        # http://stackoverflow.com/questions/9403265/how-do-i-use-python/9509718#9509718
        charset = Charset('utf-8')
        charset.header_encoding = QP
        charset.body_encoding = QP
        msg = MIMEMultipart()

        # Headers
        unixfrom = "From %s %s" % (
            self.sender.address, self.archived_date.strftime("%c"))
        header_from = self.sender.address
        if self.sender.name and self.sender.name != self.sender.address:
            header_from = "%s <%s>" % (self.sender.name, header_from)
        header_to = self.mailinglist.name
        if escape_addresses:
            header_from = header_from.replace("@", " at ")
            header_to = header_to.replace("@", " at ")
            unixfrom = unixfrom.replace("@", " at ")
        msg.set_unixfrom(unixfrom)
        headers = (
            ("From", header_from),
            ("To", header_to),
            ("Subject", self.subject),
            )
        for header_name, header_value in headers:
            if not header_value:
                continue
            try:
                msg[header_name] = header_value.encode('ascii')
            except UnicodeEncodeError:
                msg[header_name] = Header(
                    header_value.encode('utf-8'), charset).encode()
        tz = get_fixed_timezone(self.timezone)
        header_date = self.date.astimezone(tz).replace(microsecond=0)
        # Date format: http://tools.ietf.org/html/rfc5322#section-3.3
        msg["Date"] = header_date.strftime("%a, %d %b %Y %H:%M:%S %z")
        msg["Message-ID"] = "<%s>" % self.message_id
        if self.in_reply_to:
            msg["In-Reply-To"] = self.in_reply_to

        # Body
        content = self.ADDRESS_REPLACE_RE.sub(r"\1(a)\2", self.content)
        # Don't use MIMEText, it won't encode to quoted-printable
        textpart = MIMENonMultipart("text", "plain", charset='utf-8')
        textpart.set_payload(content, charset=charset)
        msg.attach(textpart)

        # Attachments
        for attachment in self.attachments.order_by("counter"):
            mimetype = attachment.content_type.split('/', 1)
            part = MIMEBase(mimetype[0], mimetype[1])
            part.set_payload(attachment.content)
            encode_base64(part)
            part.add_header('Content-Disposition', 'attachment',
                            filename=attachment.name)
            msg.attach(part)

        return msg
Example #6
0
  def _execute(self, http, order, requests):
    """Serialize batch request, send to server, process response.

    Args:
      http: httplib2.Http, an http object to be used to make the request with.
      order: list, list of request ids in the order they were added to the
        batch.
      request: list, list of request objects to send.

    Raises:
      httplib2.Error if a transport error has occured.
      apiclient.errors.BatchError if the response is the wrong format.
    """
    message = MIMEMultipart('mixed')
    # Message should not write out it's own headers.
    setattr(message, '_write_headers', lambda self: None)

    # Add all the individual requests.
    for request_id in order:
      request = requests[request_id]

      msg = MIMENonMultipart('application', 'http')
      msg['Content-Transfer-Encoding'] = 'binary'
      msg['Content-ID'] = self._id_to_header(request_id)

      body = self._serialize_request(request)
      msg.set_payload(body)
      message.attach(msg)

    body = message.as_string()

    headers = {}
    headers['content-type'] = ('multipart/mixed; '
                               'boundary="%s"') % message.get_boundary()

    resp, content = http.request(self._batch_uri, 'POST', body=body,
                                 headers=headers)

    if resp.status >= 300:
      raise HttpError(resp, content, self._batch_uri)

    # Now break out the individual responses and store each one.
    boundary, _ = content.split(None, 1)

    # Prepend with a content-type header so FeedParser can handle it.
    header = 'content-type: %s\r\n\r\n' % resp['content-type']
    for_parser = header + content

    parser = FeedParser()
    parser.feed(for_parser)
    mime_response = parser.close()

    if not mime_response.is_multipart():
      raise BatchError("Response not in multipart/mixed format.", resp,
          content)

    for part in mime_response.get_payload():
      request_id = self._header_to_id(part['Content-ID'])
      headers, content = self._deserialize_response(part.get_payload())
      self._responses[request_id] = (headers, content)
Example #7
0
    def post(self, request):
        context = self.get_context_data()

        form, user = context['form'], request.user
        if not form.is_valid():
            return self.get(request)

        s = smtplib.SMTP(settings.EMAIL_HOST, settings.EMAIL_PORT)
        s.starttls()
        if settings.EMAIL_HOST_USER:
            s.login(settings.EMAIL_HOST_USER, settings.EMAIL_HOST_PASSWORD)

        sender = "%s %s (via OxPoints Editor) <%s>" % (user.first_name, user.last_name, user.email)
        recipients = ['%s <%s>' % manager for manager in settings.MANAGERS]

        msg = MIMEMultipart()
        msg['Subject'] = form.cleaned_data['subject']
        msg['From'] = sender
        msg['To'] = ', '.join(recipients)
        msg.attach(MIMEText(form.cleaned_data['message'], 'plain'))

        if request.FILES.get('related_file'):
            related_file = request.FILES['related_file']
            attachment = MIMENonMultipart(*related_file.content_type.split('/', 1))
            attachment.add_header('Content-Disposition', 'attachment', filename=related_file.name)
            attachment.set_payload(related_file.read())
            msg.attach(attachment)

        s.sendmail(sender, recipients, msg.as_string())

        return redirect(reverse('core:request') + '?sent=true')
Example #8
0
    def __init__(self, _text, _subtype='plain', _charset=None):
        """Create a text/* type MIME document.

        _text is the string for this message object.

        _subtype is the MIME sub content type, defaulting to "plain".

        _charset is the character set parameter added to the Content-Type
        header.  This defaults to "us-ascii".  Note that as a side-effect, the
        Content-Transfer-Encoding header will also be set.
        """

        # If no _charset was specified, check to see if there are non-ascii
        # characters present. If not, use 'us-ascii', otherwise use utf-8.
        # XXX: This can be removed once #7304 is fixed.
        if _charset is None:
            try:
                _text.encode('us-ascii')
                _charset = 'us-ascii'
            except UnicodeEncodeError:
                _charset = 'utf-8'

        MIMENonMultipart.__init__(self, 'text', _subtype,
                                  **{'charset': _charset})

        self.set_payload(_text, _charset)
 def __init__(self, _text, _subtype="plain", _charset="utf-8"):
     if not isinstance(_charset, Charset):
         _charset = Charset(_charset)
     if isinstance(_text, unicode):
         _text = _text.encode(_charset.input_charset)
     MIMENonMultipart.__init__(self, "text", _subtype, **{"charset": _charset.input_charset})
     self.set_payload(_text, _charset)
Example #10
0
def sendmail(text, sender, subject, archive=None):
    if not c.has_option('commitmsg', 'destination'):
        return

    if c.has_option('commitmsg', 'replyto'):
        pieces = []
        for p in c.get('commitmsg', 'replyto').split(','):
            pp = p.strip()
            if pp == '$committer':
                if sender:
                    pieces.append(sender.strip())
                # Don't add fallback sender as committer
            else:
                pieces.append(pp)
        replyto = ', '.join(pieces)
    else:
        replyto = None

    if not sender:
        # No sender specified, so use fallback
        sender = cfg.get('commitmsg', 'fallbacksender')

    (sender_name, sender_address) = email.utils.parseaddr(sender)

    if c.has_option('commitmsg', 'forcesenderaddr'):
        sender_address = c.get('commitmsg', 'forcesenderaddr')

    if sender_name:
        fullsender = "{0} <{1}>".format(sender_name, sender_address)
    else:
        fullsender = sender_address

    for m in c.get('commitmsg', 'destination').split(','):
        msg = MIMEMultipart()
        msg['From'] = fullsender
        msg['To'] = m
        msg['Subject'] = subject
        if replyto:
            msg['Reply-To'] = replyto

        # Don't specify utf8 when doing debugging, because that will encode the output
        # as base64 which is completely useless on the console...
        if debug == 1:
            msg.attach(MIMEText(text))
        else:
            msg.attach(MIMEText(text, _charset='utf-8'))

        if archive:
            part = MIMENonMultipart('application', 'x-gzip')
            part.set_payload(archive)
            part.add_header('Content-Disposition', 'attachment; filename="archive.tar.gz"')
            encoders.encode_base64(part)
            msg.attach(part)

        allmail.append({
            'sender': sender_address,
            'to': m,
            'msg': msg,
        })
Example #11
0
 def __init__(self, _imagedata, _subtype = None, _encoder = encoders.encode_base64, **_params):
     if _subtype is None:
         _subtype = imghdr.what(None, _imagedata)
     if _subtype is None:
         raise TypeError('Could not guess image MIME subtype')
     MIMENonMultipart.__init__(self, 'image', _subtype, **_params)
     self.set_payload(_imagedata)
     _encoder(self)
Example #12
0
 def __init__(self, _text, _subtype='plain', _charset='utf-8'):
     if not isinstance(_charset, Charset):
             _charset = Charset(_charset)
     if isinstance(_text,unicode):
         _text = _text.encode(_charset.input_charset)
     MIMENonMultipart.__init__(self, 'text', _subtype,
                                    **{'charset': _charset.input_charset})
     self.set_payload(_text, _charset)
Example #13
0
 def __init__(self, _audiodata, _subtype = None, _encoder = encoders.encode_base64, **_params):
     if _subtype is None:
         _subtype = _whatsnd(_audiodata)
     if _subtype is None:
         raise TypeError('Could not find audio MIME subtype')
     MIMENonMultipart.__init__(self, 'audio', _subtype, **_params)
     self.set_payload(_audiodata)
     _encoder(self)
Example #14
0
  def _send_batch_request(self, requests):
    """Sends a batch of requests to the server and processes the HTTP responses.

    Args:
      requests: List of GoogleComputeEngineBase.API_REQUEST named tuples. Must
        contain <= MAX_BATCH_SIZE elements.

    Raises:
      ValueError: If requests has more than MAX_BATCH_SIZE elements.

    Returns:
      List of GoogleComputeEngineBase.BATCH_RESPONSE named tuples, one for
      each element of request parameter.
    """
    if len(requests) > MAX_BATCH_SIZE:
      raise ValueError('Too many requests provided'
                       '(maximum is {0})'.format(MAX_BATCH_SIZE))

    batch = _BatchApiRequest()
    base = urlparse.urlsplit(self.base_url)
    base_path = base.path.rstrip('/')
    for i, request in enumerate(requests):
      msg = MIMENonMultipart('application', 'http')
      msg.add_header('Content-ID', '<{0}>'.format(i))
      msg.set_payload(self._serialize_batch_api_request(base_path, request))
      batch.attach(msg)

    batch_string = batch.as_string()
    content_type = 'multipart/mixed; boundary="{0}"'.format(
        batch.get_boundary())

    url = urlparse.urlunsplit((base.scheme, base.netloc, 'batch',
                               self._create_url_query(None), None))
    response, data = self._send_request(url, 'POST', batch_string, content_type)

    if response.status >= 300:
      error = gce.GceError(
          message=response.reason, status=response.status)
      return [error] * len(requests)  # Return all errors.
    elif not data:
      error = gce.GceError(
          message='Server returned no data', status=response.status)
      return [error] * len(requests)  # Return all errors.

    # Process successful response.
    data = 'content-type: {0}\r\n\r\n'.format(response['content-type']) + data
    parser = FeedParser()
    parser.feed(data)
    response = parser.close()

    responses = []
    for part in response.get_payload():
      responses.append((
          int(RESPONSE_ID_REGEX.match(part['Content-ID']).group(1)),
          self._parse_batch_api_response(part.get_payload())))

    responses.sort(key=lambda r: r[0])
    return [r[1] for r in responses]
Example #15
0
 def _add_attachments(self, mime):
     for attachment in getattr(self, '_attachments', []):
         major, sub = attachment['content-type'].split('/')
         attachment_mime = MIMENonMultipart(major, sub)
         base64_attachment_file = binascii.b2a_base64(attachment['raw'])
         attachment_mime.set_payload(base64_attachment_file)
         attachment_mime['Content-Disposition'] = 'attachment; filename="%s"' % attachment['name']
         attachment_mime['Content-Transfer-Encoding'] = 'base64'
         mime.attach(attachment_mime)
Example #16
0
    def __init__(self, text, charset='utf-8'):
        """
        Based on email.mime.text.MIMEText but adds format=flowed
        to the content type.  Also defaults to UTF-8.
        """

        MIMENonMultipart.__init__(self, 'text', 'plain',
                                  charset=charset, format='flowed')
        self.set_payload(text, charset)
Example #17
0
 def __init__(self, _text, _subtype='plain', _charset=None):
     if _charset is None:
         try:
             _text.encode('us-ascii')
             _charset = 'us-ascii'
         except UnicodeEncodeError:
             _charset = 'utf-8'
     MIMENonMultipart.__init__(self, 'text', _subtype, **{'charset': _charset})
     self.set_payload(_text, _charset)
    def test_raw_with_attachment_data(self):
        input_mail = InputMail.from_dict(with_attachment_mail_dict(), from_address='pixelated@org')

        attachment = MIMENonMultipart('text', 'plain', Content_Disposition='attachment; filename=ayoyo.txt')
        attachment.set_payload('Hello World')
        mail = MIMEMultipart()
        mail.attach(attachment)

        part_one = 'Content-Type: text/plain\nMIME-Version: 1.0\nContent-Disposition: attachment; filename="ayoyo.txt"\nContent-Transfer-Encoding: base64\n\n'
        part_two = 'Content-Type: text/html\nMIME-Version: 1.0\nContent-Disposition: attachment; filename="hello.html"\nContent-Transfer-Encoding: base64\n\n'

        self.assertRegexpMatches(input_mail.raw, part_one)
        self.assertRegexpMatches(input_mail.raw, part_two)
Example #19
0
 def __init__(self, _text, _subtype = 'plain', _charset = 'us-ascii'):
     """Create a text/* type MIME document.
     
     _text is the string for this message object.
     
     _subtype is the MIME sub content type, defaulting to "plain".
     
     _charset is the character set parameter added to the Content-Type
     header.  This defaults to "us-ascii".  Note that as a side-effect, the
     Content-Transfer-Encoding header will also be set.
     """
     MIMENonMultipart.__init__(self, 'text', _subtype, **{'charset': _charset})
     self.set_payload(_text, _charset)
Example #20
0
    def upload(self, filename):
        name = os.path.basename(filename)
        size = os.path.getsize(filename)
        cype = guess_type(name)
        if not cype or not cype[0]:
            cype = "text/plain"
        else:
            cype = cype[0]

        url = ("https://www.googleapis.com/upload/drive/v2/files?"
               "uploadType=multipart")

        message = MIMEMultipart('mixed')
        # Message should not write out it's own headers.
        setattr(message, '_write_headers', lambda self: None)

        msg = MIMENonMultipart('application', 'json; charset=UTF-8')
        msg.set_payload(json.dumps({"title": name,  #.replace("'", r"\'"),
                                    "mimeType": cype}))
        message.attach(msg)

        msg = MIMENonMultipart(*cype.split('/'))
        msg.add_header("Content-Transfer-Encoding", "binary")
        msg.set_payload(open(filename).read())
        message.attach(msg)
        
        body = message.as_string()
        bd = message.get_boundary()
        hd = {"Content-Type": 'multipart/related; boundary="%s"' % bd}
        res = self.auth_http("POST", url, body, hd)
        ret = res.read()
        pprint(ret)
        return 
    def _attachment_to_cdoc(self, content, content_type, encoder=encoders.encode_base64):
        major, sub = content_type.split('/')
        attachment = MIMENonMultipart(major, sub)
        attachment.set_payload(content)
        encoder(attachment)
        attachment.add_header('Content-Disposition', 'attachment', filename='does_not_matter.txt')

        pseudo_mail = MIMEMultipart()
        pseudo_mail.attach(attachment)

        tmp_mail = SoledadMailAdaptor().get_msg_from_string(MessageClass=Message, raw_msg=pseudo_mail.as_string())

        cdoc = tmp_mail.get_wrapper().cdocs[1]
        return cdoc
Example #22
0
 def _generate_MIME_part(key, content, keytype, headers):
     if not keytype:
         try:
             content.encode("ascii")
             keytype = ("text", "plain")
         except UnicodeError:
             keytype = ("application", "octet-stream")
     submsg = MIMENonMultipart(*keytype)
     content_headers = {'name': key}
     if headers:
         content_headers.update(headers)
     submsg.add_header("Content-disposition", "form-data",
                       **content_headers)
     submsg.set_payload(content)
     return submsg
Example #23
0
 def __init__(self, _msg, _subtype = 'rfc822'):
     """Create a message/* type MIME document.
     
     _msg is a message object and must be an instance of Message, or a
     derived class of Message, otherwise a TypeError is raised.
     
     Optional _subtype defines the subtype of the contained message.  The
     default is "rfc822" (this is defined by the MIME standard, even though
     the term "rfc822" is technically outdated by RFC 2822).
     """
     MIMENonMultipart.__init__(self, 'message', _subtype)
     if not isinstance(_msg, message.Message):
         raise TypeError('Argument is not an instance of Message')
     message.Message.attach(self, _msg)
     self.set_default_type('message/rfc822')
Example #24
0
  def _serialize_request(self, request):
    """Convert an HttpRequest object into a string.

    Args:
      request: HttpRequest, the request to serialize.

    Returns:
      The request as a string in application/http format.
    """
    # Construct status line
    parsed = urlparse.urlparse(request.uri)
    request_line = urlparse.urlunparse(
        (None, None, parsed.path, parsed.params, parsed.query, None)
        )
    status_line = request.method + ' ' + request_line + ' HTTP/1.1\n'
    major, minor = request.headers.get('content-type', 'application/json').split('/')
    msg = MIMENonMultipart(major, minor)
    headers = request.headers.copy()

    if request.http is not None and hasattr(request.http.request,
        'credentials'):
      request.http.request.credentials.apply(headers)

    # MIMENonMultipart adds its own Content-Type header.
    if 'content-type' in headers:
      del headers['content-type']

    for key, value in headers.iteritems():
      msg[key] = value
    msg['Host'] = parsed.netloc
    msg.set_unixfrom(None)

    if request.body is not None:
      msg.set_payload(request.body)
      msg['content-length'] = str(len(request.body))

    # Serialize the mime message.
    fp = StringIO.StringIO()
    # maxheaderlen=0 means don't line wrap headers.
    g = Generator(fp, maxheaderlen=0)
    g.flatten(msg, unixfrom=False)
    body = fp.getvalue()

    # Strip off the \n\n that the MIME lib tacks onto the end of the payload.
    if request.body is None:
      body = body[:-2]

    return status_line.encode('utf-8') + body
Example #25
0
    def __init__(self, _msg, _subtype='rfc822'):
        """Create a message/* type MIME document.

        _msg is a message object and must be an instance of Message, or a
        derived class of Message, otherwise a TypeError is raised.

        Optional _subtype defines the subtype of the contained message.  The
        default is "rfc822" (this is defined by the MIME standard, even though
        the term "rfc822" is technically outdated by RFC 2822).
        """
        MIMENonMultipart.__init__(self, 'message', _subtype)
        if not isinstance(_msg, message.Message):
            raise TypeError('Argument is not an instance of Message')
        # It's convenient to use this base class method.  We need to do it
        # this way or we'll get an exception
        message.Message.attach(self, _msg)
        # And be sure our default type is set correctly
        self.set_default_type('message/rfc822')
Example #26
0
    def mbox(self):
        from email.mime.nonmultipart import MIMENonMultipart
        from email.encoders import encode_7or8bit

        body = ''
        if self.description:
            body += self.description.content + '\n'
        body += self.content

        mbox = MIMENonMultipart('text', 'plain', charset='utf-8')

        mbox['Subject'] = ": ".join([t.name for t in self.tags] + [self.name.strip().capitalize()])
        mbox['From'] = '%s <%s>' % (self.submitter.name, self.submitter.email)
        mbox['Message-Id'] = self.msgid

        mbox.set_payload(body.encode('utf-8'))
        encode_7or8bit(mbox)

        return mbox.as_string()
Example #27
0
def attachment_to_message(attachment):
    mtype, stype = attachment.content_type.split('/')
    if attachment.filename:
        msg = MIMENonMultipart(mtype, stype, name=attachment.filename)
    else:
        msg = MIMENonMultipart(mtype, stype)
    if attachment.disposition:
        if attachment.filename:
            msg.add_header('Content-Dispsition', attachment.disposition,
                    filename=attachment.filename)
        else:
            msg.add_header('Content-Disposition', attachment.disposition)
    payload = attachment.data
    ctenc = attachment.transfer_encoding
    if ctenc:
        payload = encode_string(ctenc, payload)
        msg.add_header('Content-Transfer-Encoding', ctenc)
    msg.set_payload(payload, attachment.charset)
    return msg
Example #28
0
    def prepare_email_message(self):
        """
        Returns a django ``EmailMessage`` or ``EmailMultiAlternatives`` object,
        depending on whether html_message is empty.
        """
        if self.template is not None:
            engine = get_template_engine()
            subject = engine.from_string(self.template.subject).render(self.context)
            plaintext_message = engine.from_string(self.template.content).render(self.context)
            multipart_template = engine.from_string(self.template.html_content)
            html_message = multipart_template.render(self.context)

        else:
            subject = smart_text(self.subject)
            plaintext_message = self.message
            multipart_template = None
            html_message = self.html_message

        connection = connections[self.backend_alias or 'default']

        if html_message:
            if plaintext_message:
                msg = EmailMultiAlternatives(
                    subject=subject, body=plaintext_message, from_email=self.from_email,
                    to=self.to, bcc=self.bcc, cc=self.cc,
                    headers=self.headers, connection=connection)
                msg.attach_alternative(html_message, "text/html")
            else:
                msg = EmailMultiAlternatives(
                    subject=subject, body=html_message, from_email=self.from_email,
                    to=self.to, bcc=self.bcc, cc=self.cc,
                    headers=self.headers, connection=connection)
                msg.content_subtype = 'html'
            if hasattr(multipart_template, 'attach_related'):
                multipart_template.attach_related(msg)

        else:
            msg = EmailMessage(
                subject=subject, body=plaintext_message, from_email=self.from_email,
                to=self.to, bcc=self.bcc, cc=self.cc,
                headers=self.headers, connection=connection)

        for attachment in self.attachments.all():
            if attachment.headers:
                mime_part = MIMENonMultipart(*attachment.mimetype.split('/'))
                mime_part.set_payload(attachment.file.read())
                for key, val in attachment.headers.items():
                    try:
                        mime_part.replace_header(key, val)
                    except KeyError:
                        mime_part.add_header(key, val)
                msg.attach(mime_part)
            else:
                msg.attach(attachment.name, attachment.file.read(), mimetype=attachment.mimetype or None)
            attachment.file.close()

        self._cached_email_message = msg
        return msg
Example #29
0
    def mbox(self):
        from email.mime.nonmultipart import MIMENonMultipart
        from email.encoders import encode_7or8bit

        body = ''
        if self.comments[0].msgid == self.msgid:
            body += self.comments[0].content + '\n'
        body += self.content

        mbox = MIMENonMultipart('text', 'plain', charset='utf-8')

        mbox['Subject'] = self.name
        mbox['From'] = '%s <%s>' % (self.submitter.name, self.submitter.email)
        mbox['Message-Id'] = self.msgid

        mbox.set_payload(body.encode('utf-8'))
        encode_7or8bit(mbox)

        return mbox.as_string()
Example #30
0
    def upload(self, filename):
        name = os.path.basename(filename)
        entry = self.get_info(name)
        if entry:
            self._delete(entry)
        #print "after check"
        size = os.path.getsize(filename)
        cype = guess_type(name)
        if not cype or not cype[0]:
            cype = "text/plain"
        else:
            cype = cype[0]

        url = "https://api.box.com/2.0/files/content"

        message = MIMEMultipart('mixed')
        # Message should not write out it's own headers.
        setattr(message, '_write_headers', lambda self: None)

        msg = MIMENonMultipart(*cype.split('/'))
        msg.add_header('Content-Disposition',
                       'form-data; name="f"; filename="%s"' % name)
        del msg["MIME-Version"]
        msg.set_payload(open(filename).read())
        message.attach(msg)

        msg = MIMENonMultipart("text", "plain")
        msg.add_header('Content-Disposition', 'form-data; name="folder_id"')
        del msg["Content-Type"]
        del msg["MIME-Version"]
        msg.set_payload('0')
        message.attach(msg)

        body = message.as_string()
        #print body
        #return
        bd = message.get_boundary()
        hd = {"Content-Type": "multipart/form-data; boundary=%s" % bd}
        res = self.auth_http("POST", url, body, hd)
        ret = res.read()

        print(json.loads(ret)["entries"][0]['name'])
Example #31
0
  def _serialize_request(self, request):
    """Convert an HttpRequest object into a string.

    Args:
      request: HttpRequest, the request to serialize.

    Returns:
      The request as a string in application/http format.
    """
    # Construct status line
    parsed = urlparse.urlparse(request.uri)
    request_line = urlparse.urlunparse(
        (None, None, parsed.path, parsed.params, parsed.query, None)
        )
    status_line = request.method + ' ' + request_line + ' HTTP/1.1\n'
    major, minor = request.headers.get('content-type', 'application/json').split('/')
    msg = MIMENonMultipart(major, minor)
    headers = request.headers.copy()

    if request.http is not None and hasattr(request.http.request,
        'credentials'):
      request.http.request.credentials.apply(headers)

    # MIMENonMultipart adds its own Content-Type header.
    if 'content-type' in headers:
      del headers['content-type']

    for key, value in headers.iteritems():
      msg[key] = value
    msg['Host'] = parsed.netloc
    msg.set_unixfrom(None)

    if request.body is not None:
      msg.set_payload(request.body)
      msg['content-length'] = str(len(request.body))

    # Serialize the mime message.
    fp = StringIO.StringIO()
    # maxheaderlen=0 means don't line wrap headers.
    g = Generator(fp, maxheaderlen=0)
    g.flatten(msg, unixfrom=False)
    body = fp.getvalue()

    # Strip off the \n\n that the MIME lib tacks onto the end of the payload.
    if request.body is None:
      body = body[:-2]

    return status_line.encode('utf-8') + body
Example #32
0
    def patch_to_attachment(self, patch, index):
        # patches don't come with an encoding.  If the patch is valid utf-8,
        # we'll attach it as MIMEText; otherwise, it gets attached as a binary
        # file.  This will suit the vast majority of cases, since utf8 is by
        # far the most common encoding.
        if not isinstance(patch[1], types.UnicodeType):
            try:
                    unicode = patch[1].decode('utf8')
            except UnicodeDecodeError:
                unicode = None
        else:
            unicode = patch[1]

        if unicode:
            a = MIMEText(unicode.encode(ENCODING), _charset=ENCODING)
        else:
            # MIMEApplication is not present in Python-2.4 :(
            a = MIMENonMultipart('application', 'octet-stream')
            a.set_payload(patch[1])
        a.add_header('Content-Disposition', "attachment",
                     filename="source patch " + str(index))
        return a
Example #33
0
    def email_files(self):
        if os.getcwd() != self.filePathStorage:
            os.chdir(self.filePathStorage)

        try:
            # Changeable from the json object
            to_address = self.adr1 + ',' + self.adr2 + ',' + self.adr3
            message = self.message
            subject = self.subject + " @" + str(
                self.current_date)  # Added the date here since I needed it.
            files = [self.fileName1, self.fileName2, self.fileName3]

            server = smtplib.SMTP(str(self.host) + ":" + str(self.port))
            server.starttls()
            server.login(self.host_usr, self.host_psw)

            # Setup the email header details
            msg = MIMEMultipart(From=self.host_usr,
                                To=self.adr1 + ',' + self.adr2 + ',' +
                                self.adr3,
                                Date=formatdate(localtime=True),
                                Subject=subject)

            # Setup backend email details
            msg['Subject'] = subject
            msg['To'] = self.adr1 + ',' + self.adr2 + ',' + self.adr3
            msg.attach(MIMEText(message.replace('\\n', '\n')))

            # Loop to attach all files in the files array
            for f in files:
                attachment = MIMENonMultipart('text', 'csv', charset='utf-8')
                attachment.set_payload(open(f, "rb").read())
                encoders.encode_base64(attachment)
                attachment.add_header('Content-Disposition',
                                      'attachment',
                                      filename=f)
                msg.attach(attachment)

            server.sendmail(self.host_usr, to_address, msg.as_string())
        except smtplib.SMTPRecipientsRefused as e:
            print("Invalid address - {to_address}".format(
                to_address=self.adr1 + ',' + self.adr2 + ',' + self.adr3))
        finally:
            print('Done')
            if server:
                server.quit()
Example #34
0
    def createMessage(self, recipients):
        msg = MIMEMultipart()
        msg['Subject'] = yamlLoad['SendInfo']['Subject']
        msg['From'] = Sender
        msg['To'] = recipients

        if args.v:
            print(
                colored("Delivering to " + recipients + ", from: " + sender,
                        "yellow"))

        data = MIMEText(yamlLoad['SendInfo']['Body'], 'html')
        msg.attach(data)

        if args.v:
            print(colored("Message to be delivered: \n", "yellow"))
            print(colored(data, "white", "on_blue"))

        if args.p:
            try:
                payloadPath = args.p
                fp = open(payloadPath, 'rb')
                attach = MIMENonMultipart('application', 'exe')
                payload = base64.b64encode(fp.read()).decode('ascii')
                attach.set_payload(payload)
                attach['Content-Transfer-Encoding'] = 'base64'
                nameFile = yamlLoad['SendInfo']['Attachment']
                attach.add_header('Content-Disposition',
                                  'attachment',
                                  filename=nameFile)
                msg.attach(attach)
                fp.close()

                if args.v:
                    print(
                        colored("Name of attachment to be sent: " + nameFile,
                                "yellow"))

            except:
                print(colored("Incorrect file path \n", "yellow"))

        if args.v:
            print(colored("Message created successfuly \n", "green"))

        message = msg.as_string()
        return message
Example #35
0
    def get_content_block(self, f):
        binding_map = {
            '.xlsx': ('application',
                      'vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
            '.xls': ('application', 'vnd.ms-excel'),
            '.csv': ('text', 'csv'),
            '.json': ('application', 'json'),
            '.syslog': ('text', 'syslog')
        }

        # Check the file extension to see if it is a supported type
        name_part, ext_part = os.path.splitext(f.name)

        if ext_part.lower() == '.xml':
            cb = tm11.ContentBlock(tm11.ContentBinding(t.CB_STIX_XML_101),
                                   f.read())
        else:
            binding_tuple = binding_map.get(ext_part.lower(), None)
            if not binding_tuple:
                logger.error(
                    'File extension not supported: %s. Supported extensions: %s'
                    % (ext_part, binding_map.keys()))
                return

            # Read the file and create a MIME message for it
            maintype = binding_tuple[0]
            subtype = binding_tuple[
                1]  # Note: This is MIME subtype, not TAXII subtype
            mime_msg = MIMENonMultipart(maintype, subtype)
            mime_msg.add_header('Content-Disposition',
                                'attachment',
                                filename=f.name)
            mime_msg.set_payload(f.read())
            encode_base64(mime_msg)

            cb = tm11.ContentBlock('%s/%s' % (maintype, subtype),
                                   mime_msg.as_string())

        return cb
Example #36
0
  def _serialize_request(self, request):
    """Convert an HttpRequest object into a string.

    Args:
      request: HttpRequest, the request to serialize.

    Returns:
      The request as a string in application/http format.
    """
    # Construct status line
    parsed = urlparse.urlparse(request.uri)
    request_line = urlparse.urlunparse(
        (None, None, parsed.path, parsed.params, parsed.query, None)
        )
    status_line = request.method + ' ' + request_line + ' HTTP/1.1\n'
    major, minor = request.headers.get('content-type', 'application/json').split('/')
    msg = MIMENonMultipart(major, minor)
    headers = request.headers.copy()

    # MIMENonMultipart adds its own Content-Type header.
    if 'content-type' in headers:
      del headers['content-type']

    for key, value in headers.iteritems():
      msg[key] = value
    msg['Host'] = parsed.netloc
    msg.set_unixfrom(None)

    if request.body is not None:
      msg.set_payload(request.body)
      msg['content-length'] = str(len(request.body))

    body = msg.as_string(False)
    # Strip off the \n\n that the MIME lib tacks onto the end of the payload.
    if request.body is None:
      body = body[:-2]

    return status_line.encode('utf-8') + body
Example #37
0
 def get_mime(cls, report, record, language):
     pool = Pool()
     Report = pool.get(report.report_name, type='report')
     with Transaction().set_context(language=language):
         ext, content, _, title = Report.execute([record.id], {
             'action_id': report.id,
         })
     name = '%s.%s' % (title, ext)
     mimetype, _ = mimetypes.guess_type(name)
     if mimetype:
         msg = MIMENonMultipart(*mimetype.split('/'))
         msg.set_payload(content)
         encode_base64(msg)
     else:
         msg = MIMEApplication(content)
     if not isinstance(name, str):
         name = name.encode('utf-8')
     if not isinstance(language, str):
         language = language.encode('utf-8')
     msg.add_header('Content-Disposition',
                    'attachment',
                    filename=('utf-8', language, name))
     return msg
Example #38
0
File: util.py Project: dbonne/hamn
def send_simple_mail(sender,
                     receiver,
                     subject,
                     msgtxt,
                     attachments=None,
                     bcc=None,
                     sendername=None,
                     receivername=None):
    # attachment format, each is a tuple of (name, mimetype,contents)
    # content should be *binary* and not base64 encoded, since we need to
    # use the base64 routines from the email library to get a properly
    # formatted output message
    msg = MIMEMultipart()
    msg['Subject'] = subject
    if receivername:
        msg['To'] = u'{0} <{1}>'.format(receivername, receiver)
    else:
        msg['To'] = receiver
    if sendername:
        msg['From'] = u'{0} <{1}>'.format(sendername, sender)
    else:
        msg['From'] = sender
    msg['Date'] = formatdate(localtime=True)

    msg.attach(MIMEText(msgtxt, _charset='utf-8'))

    if attachments:
        for filename, contenttype, content in attachments:
            main, sub = contenttype.split('/')
            part = MIMENonMultipart(main, sub)
            part.set_payload(content)
            part.add_header('Content-Disposition',
                            'attachment; filename="%s"' % filename)
            encoders.encode_base64(part)
            msg.attach(part)

    # Just write it to the queue, so it will be transactionally rolled back
    QueuedMail(sender=sender, receiver=receiver,
               fullmsg=msg.as_string()).save()
    # Any bcc is just entered as a separate email
    if bcc:
        QueuedMail(sender=sender, receiver=bcc, fullmsg=msg.as_string()).save()
Example #39
0
    def _attachment_to_cdoc(self,
                            content,
                            content_type,
                            encoder=encoders.encode_base64):
        major, sub = content_type.split('/')
        attachment = MIMENonMultipart(major, sub)
        attachment.set_payload(content)
        encoder(attachment)
        attachment.add_header('Content-Disposition',
                              'attachment',
                              filename='does_not_matter.txt')

        pseudo_mail = MIMEMultipart()
        pseudo_mail.attach(attachment)

        tmp_mail = SoledadMailAdaptor().get_msg_from_string(
            MessageClass=Message, raw_msg=pseudo_mail.as_string())

        cdoc = tmp_mail.get_wrapper().cdocs[1]
        return cdoc
Example #40
0
    async def _serialize_request(self, request):
        """
        Convert an HttpRequest object into a string.

        Args:
          request: HttpRequest, the request to serialize.

        Returns:
          The request as a string in application/http format.
        """
        parsed = urlparse(request.uri)
        request_line = urlunparse(
            ("", "", parsed.path, parsed.params, parsed.query, ""))
        status_line = request.method + " " + request_line + " HTTP/1.1\n"
        major, minor = request.headers.get("content-type",
                                           "application/json").split("/")
        msg = MIMENonMultipart(major, minor)
        headers = request.headers.copy()

        # MIMENonMultipart adds its own Content-Type header.
        if "content-type" in headers:
            del headers["content-type"]

        for key, value in headers.items():
            msg[key] = value
        msg["Host"] = parsed.netloc
        msg.set_unixfrom(None)

        if request.body is not None:
            msg.set_payload(request.body)
            msg["content-length"] = str(len(request.body))

        # Serialize the mime message.
        fp = StringIO()
        # maxheaderlen=0 means don't line wrap headers.
        g = Generator(fp, maxheaderlen=0)
        g.flatten(msg, unixfrom=False)
        body = fp.getvalue()

        return status_line + body
def sendPDF(fileName, email):
    smtp_ssl_host = 'smtp.gmail.com'
    smtp_ssl_port = 465
    username = '******'
    password = '******'
    sender = '*****@*****.**'
    targets = [email]

    msg = MIMEMultipart()
    msg['Subject'] = 'Study Plan'
    msg['From'] = sender
    msg['To'] = ', '.join(targets)

    #Source for PDF send: https://bugs.python.org/issue9040
    try:
        fp = open(fileName, 'rb')
        attach = MIMENonMultipart('application', 'pdf')
        payload = base64.b64encode(fp.read()).decode('ascii')
        attach.set_payload(payload)
        attach['Content-Transfer-Encoding'] = 'base64'
        fp.close()
        attach.add_header('Content-Disposition',
                          'attachment',
                          filename='file.pdf')
        msg.attach(attach)
        #End of found fix

        server = smtplib.SMTP_SSL(smtp_ssl_host, smtp_ssl_port)
        server.login(username, password)

        server.sendmail(sender, targets, msg.as_string())

        server.quit()
        return "Sent study plan to: " + email
    except:
        print 'error/exception'
Example #42
0
def craft_message(headers, body, attachments, embeddeds, mbx, is_html):
    """This function handles the creation of a Python
	email.message object from the headers and body lists created
	during the main loop."""

    global edir

    attachments_ok = False
    embeddedcids = []

    if body:
        msg_text = ''.join(body)
    else:
        msg_text = ''

    # there's no point honoring 'multipart' if we don't have any
    # attachments or embeddeds to attach, so we'll pay most
    # attention to whether we have any attachments or embeddeds
    # defined for this message

    if attachments or embeddeds:
        is_multipart = True
    else:
        is_multipart = False

    message = None

    contenttype = headers.getValue('Content-Type:')

    if contenttype and re_html.search(contenttype):
        is_html = True

    if not contenttype:
        msattach = headers.getValue('X-MS-Attachment:')

        if msattach:
            message = MIMEMultipart()
            attachments_ok = "Dunno"
            attachments_contenttype = "Still Dunno"
        else:
            if is_html:
                message = MIMENonMultipart('text', 'html')
            else:
                message = MIMENonMultipart('text', 'plain')
            attachments_ok = False
            attachments_contenttype = False
            print "T",
    elif re_rfc822.search(contenttype):
        print "[",
        message = MIMEMessage(
            craft_message(*extract_pieces(body, -1, mbx, True)))
        print "]",
    elif not is_multipart:
        mimetype = re_single_contenttype.search(contenttype)

        if mimetype:
            main = mimetype.group(1)
            sub = mimetype.group(2)

            if main != 'multipart':
                message = MIMENonMultipart(main, sub)
                attachments_ok = False
                attachments_contenttype = False
                print "X",
            else:
                # I've seen some messages in Eudora
                # mailboxes that label themselves as
                # multitype/related without having any
                # attachments ever declared in the
                # Eudora box.
                #
                # By passing here, we allow the next
                # clause to go ahead and create an
                # appropriate MIMENonMultipart

                pass

        if not message:
            if is_html:
                message = MIMENonMultipart('text', 'html')
            else:
                message = MIMENonMultipart('text', 'plain')
            attachments_ok = False
            attachments_contenttype = False
    else:
        subtype = re_multi_contenttype.search(contenttype)
        if subtype:
            message = MIMEMultipart(_subtype=subtype.group(1))
            attachments_ok = subtype.group(1)
            attachments_contenttype = contenttype
            print "Y",
        else:
            message = MIMEMultipart()
            print "Z",
            attachments_ok = "Dunno"
            attachments_contenttype = "Still Dunno"

    # Need to add support here for processing embeddeds

    if embeddeds:
        if not isinstance(message, MIMEMultipart):
            print "\n\n==================================================\n"
            print "Found surprise multipart for embeddeds!\n"

            message = MIMEMultipart(_subtype='related')
        else:
            print "\n\n==================================================\n"
            print "Found embeddeds in multipart!\n"

        p = EudoraHTMLParser()

        try:
            p.feed(msg_text)
            cids = p.get_cids()
        except HTMLParseError:
            # okay, we've got unparseable HTML here.
            # Let's just use a quick regexp to see if we can make sense of this.

            cids = []

            for match in re_cids_finder.finditer(msg_text):
                cids.append("cid:" + match.group(1))

        if not len(cids) == len(embeddeds):
            print "cids / embeddeds mismatch!"
            print
            print mbx

            for piece in ['To:', 'From:', 'Subject:', 'Date:']:
                if headers.getValue(piece):
                    print piece + " " + headers.getValue(piece)[:80]
            print

        print "\tcid\t\t\t\t\t\t\tembedded"

        i = 0
        while i < len(cids) or i < len(embeddeds):
            if i < len(cids):
                print "%d.\t%s" % (i, cids[i]),
                print "\t" * (6 - (len(cids[i]) // 8)),

            else:
                print "%d.\t" % (i, ),
                print "\t\t\t\t\t\t",
            if i < len(embeddeds):
                print embeddeds[i],

                if edir and os.path.exists(edir + os.sep + embeddeds[i]):
                    print " *"
                else:
                    print " !"
            else:
                print

            i = i + 1

        cidi = 0
        embeddedi = 0
        cidsmatched = set()
        while cidi < len(cids) or embeddedi < len(embeddeds):
            if cidi < len(cids) and embeddedi < len(embeddeds):
                if cids[cidi].startswith('cid:'):
                    actualcid = cids[cidi][4:]
                else:
                    actualcid = cids[cidi]

                # the document might have several img
                # references to the same cid.. we
                # don't want to try to mate up
                # multiple inline files in that case

                if actualcid in cidsmatched:
                    cidi = cidi + 1
                else:
                    cidsmatched.add(actualcid)
                    embeddedcids.append((actualcid, embeddeds[embeddedi]))
                    embeddedi = embeddedi + 1
                    cidi = cidi + 1
            elif embeddedi < len(embeddeds):
                embeddedcids.append((None, embeddeds[embeddedi]))
                embeddedi = embeddedi + 1
            else:
                # we have more cids than
                # embeddeds, keep looping
                # through
                cidi = cidi + 1

        print "\n\nAttaching inline components:"

        for c, f in embeddedcids:
            print "%s\t%s" % (c, f)

        print "\n==================================================\n"

    if attachments:
        if not isinstance(message, MIMEMultipart):
            #print "\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"
            #print "Forcing surprise multipart!\n"
            #print "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"

            message = MIMEMultipart()

    # bind the headers into our message

    set_headers(message, headers)

    try:
        # rfc822 MIMEMessage objects handle payload management
        # on constructions, we must not try to do it here.

        if not isinstance(message, MIMEMessage):
            if not isinstance(message, MIMEMultipart):
                message.set_payload(msg_text)
            elif is_html:
                message.attach(MIMEText(msg_text, _subtype='html'))
            else:
                message.attach(MIMEText(msg_text))
    except Exception, e:
        print "\nHEY HEY HEY message = " + str(msg_text) + "\n"
        print "Type of message's payload is " + str(type(
            message.get_payload())) + "\n"
        if isinstance(message.get_payload(), list):
            print "Size of message's payload list is " + str(
                len(message.get_payload())) + "\n"
            print ")))))))))))))))))))) First part"
            print str(message.get_payload()[0])
            print ">>>>>>>>>>>>>>>>>>>> Second part"
            print str(message.get_payload()[1])

        print "attachments_contenttype is (%s)" % (attachments_contenttype, )
        print "attachments_ok is (%s)" % (attachments_ok, )

        if attachments:
            print "Yeah, attachments were found: %d" % (len(attachments), )

        print "EXCEPTION " + str(e) + "\n"
        traceback.print_exc(file=sys.stdout)
    def method(self, **kwargs):
        # Don't bother with doc string, it will be over-written by createMethod.

        for name in six.iterkeys(kwargs):
            if name not in parameters.argmap:
                raise TypeError('Got an unexpected keyword argument "%s"' %
                                name)

        # Remove args that have a value of None.
        keys = list(kwargs.keys())
        for name in keys:
            if kwargs[name] is None:
                del kwargs[name]

        for name in parameters.required_params:
            if name not in kwargs:
                # temporary workaround for non-paging methods incorrectly requiring
                # page token parameter (cf. drive.changes.watch vs. drive.changes.list)
                if name not in _PAGE_TOKEN_NAMES or _findPageTokenName(
                        _methodProperties(methodDesc, schema, 'response')):
                    raise TypeError('Missing required parameter "%s"' % name)

        for name, regex in six.iteritems(parameters.pattern_params):
            if name in kwargs:
                if isinstance(kwargs[name], six.string_types):
                    pvalues = [kwargs[name]]
                else:
                    pvalues = kwargs[name]
                for pvalue in pvalues:
                    if re.match(regex, pvalue) is None:
                        raise TypeError(
                            'Parameter "%s" value "%s" does not match the pattern "%s"'
                            % (name, pvalue, regex))

        for name, enums in six.iteritems(parameters.enum_params):
            if name in kwargs:
                # We need to handle the case of a repeated enum
                # name differently, since we want to handle both
                # arg='value' and arg=['value1', 'value2']
                if (name in parameters.repeated_params
                        and not isinstance(kwargs[name], six.string_types)):
                    values = kwargs[name]
                else:
                    values = [kwargs[name]]
                for value in values:
                    if value not in enums:
                        raise TypeError(
                            'Parameter "%s" value "%s" is not an allowed value in "%s"'
                            % (name, value, str(enums)))

        actual_query_params = {}
        actual_path_params = {}
        for key, value in six.iteritems(kwargs):
            to_type = parameters.param_types.get(key, 'string')
            # For repeated parameters we cast each member of the list.
            if key in parameters.repeated_params and type(value) == type([]):
                cast_value = [_cast(x, to_type) for x in value]
            else:
                cast_value = _cast(value, to_type)
            if key in parameters.query_params:
                actual_query_params[parameters.argmap[key]] = cast_value
            if key in parameters.path_params:
                actual_path_params[parameters.argmap[key]] = cast_value
        body_value = kwargs.get('body', None)
        media_filename = kwargs.get('media_body', None)
        media_mime_type = kwargs.get('media_mime_type', None)

        if self._developerKey:
            actual_query_params['key'] = self._developerKey

        model = self._model
        if methodName.endswith('_media'):
            model = MediaModel()
        elif 'response' not in methodDesc:
            model = RawModel()

        headers = {}
        headers, params, query, body = model.request(headers,
                                                     actual_path_params,
                                                     actual_query_params,
                                                     body_value)

        expanded_url = uritemplate.expand(pathUrl, params)
        url = _urljoin(self._baseUrl, expanded_url + query)

        resumable = None
        multipart_boundary = ''

        if media_filename:
            # Ensure we end up with a valid MediaUpload object.
            if isinstance(media_filename, six.string_types):
                if media_mime_type is None:
                    logger.warning(
                        'media_mime_type argument not specified: trying to auto-detect for %s',
                        media_filename)
                    media_mime_type, _ = mimetypes.guess_type(media_filename)
                if media_mime_type is None:
                    raise UnknownFileType(media_filename)
                if not mimeparse.best_match([media_mime_type],
                                            ','.join(accept)):
                    raise UnacceptableMimeTypeError(media_mime_type)
                media_upload = MediaFileUpload(media_filename,
                                               mimetype=media_mime_type)
            elif isinstance(media_filename, MediaUpload):
                media_upload = media_filename
            else:
                raise TypeError('media_filename must be str or MediaUpload.')

            # Check the maxSize
            if media_upload.size(
            ) is not None and media_upload.size() > maxSize > 0:
                raise MediaUploadSizeError("Media larger than: %s" % maxSize)

            # Use the media path uri for media uploads
            expanded_url = uritemplate.expand(mediaPathUrl, params)
            url = _urljoin(self._baseUrl, expanded_url + query)
            if media_upload.resumable():
                url = _add_query_parameter(url, 'uploadType', 'resumable')

            if media_upload.resumable():
                # This is all we need to do for resumable, if the body exists it gets
                # sent in the first request, otherwise an empty body is sent.
                resumable = media_upload
            else:
                # A non-resumable upload
                if body is None:
                    # This is a simple media upload
                    headers['content-type'] = media_upload.mimetype()
                    body = media_upload.getbytes(0, media_upload.size())
                    url = _add_query_parameter(url, 'uploadType', 'media')
                else:
                    # This is a multipart/related upload.
                    msgRoot = MIMEMultipart('related')
                    # msgRoot should not write out it's own headers
                    setattr(msgRoot, '_write_headers', lambda self: None)

                    # attach the body as one part
                    msg = MIMENonMultipart(*headers['content-type'].split('/'))
                    msg.set_payload(body)
                    msgRoot.attach(msg)

                    # attach the media as the second part
                    msg = MIMENonMultipart(*media_upload.mimetype().split('/'))
                    msg['Content-Transfer-Encoding'] = 'binary'

                    payload = media_upload.getbytes(0, media_upload.size())
                    msg.set_payload(payload)
                    msgRoot.attach(msg)
                    # encode the body: note that we can't use `as_string`, because
                    # it plays games with `From ` lines.
                    fp = BytesIO()
                    g = _BytesGenerator(fp, mangle_from_=False)
                    g.flatten(msgRoot, unixfrom=False)
                    body = fp.getvalue()

                    multipart_boundary = msgRoot.get_boundary()
                    headers['content-type'] = (
                        'multipart/related; '
                        'boundary="%s"') % multipart_boundary
                    url = _add_query_parameter(url, 'uploadType', 'multipart')

        logger.info('URL being requested: %s %s' % (httpMethod, url))
        return self._requestBuilder(self._http,
                                    model.response,
                                    url,
                                    method=httpMethod,
                                    body=body,
                                    headers=headers,
                                    methodId=methodId,
                                    resumable=resumable)
Example #44
0
def build_email(mail_to,
                subject,
                body_text,
                mail_from='*****@*****.**',
                reply_to=None,
                attachments=None,
                mime_headers=None):
    """
    Flexible Multipart MIME message builder.

    Implements message building using the "email" Python standard library
    https://docs.python.org/2/library/email-examples.html

    while following recommendations from
    https://stackoverflow.com/questions/3902455/smtp-multipart-alternative-vs-multipart-mixed

    .. seealso::

        - http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52243
        - http://mail.python.org/pipermail/tutor/1999-June/000259.html
        - http://www.bigbold.com/snippets/posts/show/2038
    """
    attachments = attachments or {}
    mime_headers = mime_headers or {}

    # ------------------------------------------
    #                envelope
    # ------------------------------------------

    message = MIMEMultipart('mixed')
    # TODO: Add text for non MIME-aware MUAs
    #message.preamble = 'You will not see this in a MIME-aware mail reader.\n'

    # Address headers
    address_headers = {
        'To': fix_addresslist(AddressList(mail_to)),
        'From': AddressList(mail_from),
        'Reply-To': AddressList(reply_to),
    }

    # Subject header
    mime_headers.update({'Subject': Header(s=subject, charset='utf-8')})

    # Add address headers
    for key, item in address_headers.items():
        if isinstance(item, AddressList):

            # v1
            #for address in format_addresslist(item):
            #    message[key] = address

            # v2
            value = ', '.join(format_addresslist(item))
            if value:
                message[key] = value

    # Add more headers
    for key, value in mime_headers.items():
        #message.add_header(key, value)
        if value:
            message[key] = value

    # ------------------------------------------
    #              email body
    # ------------------------------------------
    body_message = MIMEMultipart('alternative')

    # Start off with a text/plain part
    # https://stackoverflow.com/questions/3902455/smtp-multipart-alternative-vs-multipart-mixed
    body_part1 = MIMEText(body_text, _subtype='plain', _charset='utf-8')
    #body_part1.set_payload(body_text)
    #body_part1.set_charset('utf-8')
    body_message.attach(body_part1)
    message.attach(body_message)

    # TODO: Add a text/html part
    #body_part2 = MIMEText(body_html, 'html')

    # ------------------------------------------
    #            multipart attachments
    # ------------------------------------------
    # from https://docs.python.org/2/library/email-examples.html
    for filename, payload in attachments.items():

        # Guess the content type based on the file's extension.  Encoding
        # will be ignored, although we should check for simple things like
        # gzip'd or compressed files.
        ctype, encoding = mimetypes.guess_type(filename, strict=False)
        #print('ctype, encoding:', ctype, encoding)

        if ctype is None or encoding is not None:
            # No guess could be made, or the file is encoded (compressed), so
            # use a generic bag-of-bits type.
            ctype = 'application/octet-stream'
        maintype, subtype = ctype.split('/', 1)
        #print('maintype, subtype:', maintype, subtype)

        # Create proper MIME part by maintype
        if maintype == 'application' and subtype in ['xml', 'json']:
            part = MIMENonMultipart(maintype, subtype, charset='utf-8')
            part.set_payload(payload.encode('utf-8'), 'utf-8')

        elif maintype == 'text':
            part = MIMEText(payload.encode('utf-8'),
                            _subtype=subtype,
                            _charset='utf-8')
            #part.set_charset('utf-8')

        elif maintype == 'image':
            part = MIMEImage(payload, _subtype=subtype)
        elif maintype == 'audio':
            part = MIMEAudio(payload, _subtype=subtype)
        else:
            part = MIMEBase(maintype, subtype)
            part.set_payload(payload)
            # Encode the payload using Base64 (Content-Transfer-Encoding)
            encoders.encode_base64(part)

        #part = MIMEBase(maintype, subtype, _charset='utf-8')
        # replace forward slashes by dashes
        filename_attachment = filename.lstrip('/\\').replace('/', '-')
        part.add_header('Content-Disposition',
                        'attachment',
                        filename=filename_attachment.encode('utf-8'))
        #part.set_payload(payload.encode('utf-8'))
        #part.set_charset('utf-8')

        # Encode the payload using Base64 (Content-Transfer-Encoding)
        #encoders.encode_base64(part)

        # Add part to multipart message
        message.attach(part)

    payload = message.as_string()

    return payload
Example #45
0
    def send(cls,
             to='',
             cc='',
             bcc='',
             subject='',
             body='',
             files=None,
             record=None,
             reports=None,
             attachments=None):
        pool = Pool()
        User = pool.get('res.user')
        ActionReport = pool.get('ir.action.report')
        Attachment = pool.get('ir.attachment')
        transaction = Transaction()
        user = User(transaction.user)

        Model = pool.get(record[0])
        record = Model(record[1])

        body_html = HTML_EMAIL % {
            'subject': subject,
            'body': body,
            'signature': user.signature or '',
        }
        content = MIMEMultipart('alternative')
        if html2text:
            body_text = HTML_EMAIL % {
                'subject': subject,
                'body': body,
                'signature': '',
            }
            converter = html2text.HTML2Text()
            body_text = converter.handle(body_text)
            if user.signature:
                body_text += '\n-- \n' + converter.handle(user.signature)
            part = MIMEText(body_text, 'plain', _charset='utf-8')
            content.attach(part)
        part = MIMEText(body_html, 'html', _charset='utf-8')
        content.attach(part)
        if files or reports or attachments:
            msg = MIMEMultipart('mixed')
            msg.attach(content)
            if files is None:
                files = []
            else:
                files = list(files)

            for report_id in (reports or []):
                report = ActionReport(report_id)
                Report = pool.get(report.report_name, type='report')
                ext, content, _, title = Report.execute([record.id], {
                    'action_id': report.id,
                })
                name = '%s.%s' % (title, ext)
                if isinstance(content, str):
                    content = content.encode('utf-8')
                files.append((name, content))
            if attachments:
                files += [(a.name, a.data)
                          for a in Attachment.browse(attachments)]
            for name, data in files:
                mimetype, _ = mimetypes.guess_type(name)
                if mimetype:
                    attachment = MIMENonMultipart(*mimetype.split('/'))
                    attachment.set_payload(data)
                    encode_base64(attachment)
                else:
                    attachment = MIMEApplication(data)
                attachment.add_header('Content-Disposition',
                                      'attachment',
                                      filename=('utf-8', '', name))
                msg.attach(attachment)
        else:
            msg = content
        from_ = config.get('email', 'from')
        set_from_header(msg, from_, user.email or from_)
        msg['To'] = ', '.join(formataddr(a) for a in getaddresses([to]))
        msg['Cc'] = ', '.join(formataddr(a) for a in getaddresses([cc]))
        msg['Subject'] = Header(subject, 'utf-8')

        to_addrs = list(
            filter(
                None,
                map(str.strip,
                    _get_emails(to) + _get_emails(cc) + _get_emails(bcc))))
        sendmail_transactional(from_,
                               to_addrs,
                               msg,
                               datamanager=SMTPDataManager(strict=True))

        email = cls(recipients=to,
                    recipients_secondary=cc,
                    recipients_hidden=bcc,
                    addresses=[{
                        'address': a
                    } for a in to_addrs],
                    subject=subject,
                    body=body,
                    resource=record)
        email.save()
        with Transaction().set_context(_check_access=False):
            attachments_ = []
            for name, data in files:
                attachments_.append(
                    Attachment(resource=email, name=name, data=data))
            Attachment.save(attachments_)
        return email
Example #46
0
def to_message(base):
    """
    Given a MailBase, this will construct a MIME part that is canonicalized for
    use with the Python email API.
    """
    ctype, ctparams = base.get_content_type()

    if not ctype:
        if base.parts:
            ctype = 'multipart/mixed'
        else:
            ctype = 'text/plain'

    maintype, subtype = ctype.split('/')
    is_text = maintype == 'text'
    is_multipart = maintype == 'multipart'

    if base.parts and not is_multipart:
        raise RuntimeError(
            'Content type should be multipart, not %r' % ctype
            )

    body = base.get_body()
    ctenc = base.get_transfer_encoding()
    charset = ctparams.get('charset')

    if is_multipart:
        out = MIMEMultipart(subtype, **ctparams)
    else:
        out = MIMENonMultipart(maintype, subtype, **ctparams)
        if ctenc:
            out['Content-Transfer-Encoding'] = ctenc
        if isinstance(body, text_type):
            if not charset:
                if is_text:
                    charset, _ = best_charset(body)
                else:
                    charset = 'utf-8'
            if PY2:
                body = body.encode(charset)
            else:
                body = body.encode(charset, 'surrogateescape')
        if body is not None:
            if ctenc:
                body = transfer_encode(ctenc, body)
            if not PY2:
                body = body.decode(charset or 'ascii', 'replace')
        out.set_payload(body, charset)

    for k in base.keys(): # returned sorted
        value = base[k]
        if not value:
            continue
        out[k] = value

    cdisp, cdisp_params = base.get_content_disposition()

    if cdisp:
        out.add_header('Content-Disposition', cdisp, **cdisp_params)

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

    return out
Example #47
0
        def mock_send_request(path, method, body, content_type):
            self.assertEqual('POST', method)
            self.assertEqual('https://www.googleapis.com/batch', path)
            match = re.match('multipart/mixed; boundary="([^\"]+)"',
                             content_type)
            self.assertTrue(match)
            parts = body.split('--{0}'.format(match.group(1)))
            self.assertEqual(gce_base.MAX_BATCH_SIZE + 2, len(parts))
            self.assertEqual('', parts[0])
            self.assertEqual('--', parts[-1])
            parts = parts[1:-1]

            responses = []
            for part in parts:
                headers, payload = part.split('\n\n', 1)
                headers = parse_headers(headers)
                self.assertEqual('application/http', headers['Content-Type'])
                content_id = headers['Content-ID']
                self.assertTrue(
                    content_id.startswith('<') and content_id.endswith('>'))
                content_id = content_id[1:-1]

                http_headers = payload.split('\n\n', 1)[0]
                split = http_headers.split(
                    '\n', 1)  # Try to split off the http command
                http_request = split[0]
                if len(split) > 1:
                    headers = parse_headers(split[1])

                verb, path = http_request.split(' ')
                self.assertEqual('GET', verb)

                name = re.match('.*/([^/]+)', path).group(1)
                payload = '{{ "kind": "compute#instance", "name": "{0}" }}'.format(
                    name)

                msg = MIMENonMultipart('application', 'http')
                msg.add_header('Content-ID',
                               '<response-{0}>'.format(content_id))
                msg.set_payload(
                    'HTTP/1.1 200 OK\n'
                    'Content-Type: application/json; charset=UTF-8\n'
                    'Content-Length: {0}\n\n'
                    '{1}'.format(len(payload), payload))
                responses.append(msg)

            random.shuffle(responses)
            response = gce_base._BatchApiRequest()
            for r in responses:
                response.attach(r)

            response_string = response.as_string()
            boundary = response.get_boundary()
            response = httplib2.Response({
                'content-type':
                'multipart/mixed; boundary="{0}"'.format(boundary),
                'status':
                200,
                'reason':
                'OK'
            })

            return response, response_string
Example #48
0
    def _process_msg(self, base_msg, append_msg):
        def find_ctype(payload):
            return handlers.type_from_starts_with(payload)

        for part in base_msg.walk():
            if is_skippable(part):
                continue

            ctype = None
            ctype_orig = part.get_content_type()
            payload = util.fully_decoded_payload(part)
            was_compressed = False

            # When the message states it is of a gzipped content type ensure
            # that we attempt to decode said payload so that the decompressed
            # data can be examined (instead of the compressed data).
            if ctype_orig in DECOMP_TYPES:
                try:
                    payload = util.decomp_gzip(payload, quiet=False)
                    # At this point we don't know what the content-type is
                    # since we just decompressed it.
                    ctype_orig = None
                    was_compressed = True
                except util.DecompressionError as e:
                    LOG.warning(
                        "Failed decompressing payload from %s of"
                        " length %s due to: %s", ctype_orig, len(payload), e)
                    continue

            # Attempt to figure out the payloads content-type
            if not ctype_orig:
                ctype_orig = UNDEF_TYPE
            if ctype_orig in TYPE_NEEDED:
                ctype = find_ctype(payload)
            if ctype is None:
                ctype = ctype_orig

            # In the case where the data was compressed, we want to make sure
            # that we create a new message that contains the found content
            # type with the uncompressed content since later traversals of the
            # messages will expect a part not compressed.
            if was_compressed:
                maintype, subtype = ctype.split("/", 1)
                n_part = MIMENonMultipart(maintype, subtype)
                n_part.set_payload(payload)
                # Copy various headers from the old part to the new one,
                # but don't include all the headers since some are not useful
                # after decoding and decompression.
                if part.get_filename():
                    _set_filename(n_part, part.get_filename())
                for h in ('Launch-Index', ):
                    if h in part:
                        _replace_header(n_part, h, str(part[h]))
                part = n_part

            if ctype != ctype_orig:
                _replace_header(part, CONTENT_TYPE, ctype)

            if ctype in INCLUDE_TYPES:
                self._do_include(payload, append_msg)
                continue

            if ctype in ARCHIVE_TYPES:
                self._explode_archive(payload, append_msg)
                continue

            # TODO(harlowja): Should this be happening, shouldn't
            # the part header be modified and not the base?
            _replace_header(base_msg, CONTENT_TYPE, ctype)

            self._attach_part(append_msg, part)
Example #49
0
    async def execute(self, access_token=None):
        if access_token is None:
            if self.access_token is None:
                raise ValueError("Access token is not specified")
            access_token = self.access_token
        else:
            self.access_token = access_token
        """Validated credentials before calling this coroutine."""
        message = MIMEMultipart("mixed")
        # Message should not write out it's own headers.
        setattr(message, "_write_headers", lambda arg: None)

        for rid, request in enumerate(self.requests):
            msg_part = MIMENonMultipart("application", "http")
            msg_part["Content-Transfer-Encoding"] = "binary"
            msg_part["Content-ID"] = self._id_to_header(str(rid))

            body = await asyncio.create_task(self._serialize_request(request))
            msg_part.set_payload(body)
            message.attach(msg_part)

        fp = StringIO()
        g = Generator(fp, mangle_from_=False)
        g.flatten(message, unixfrom=False)
        body = fp.getvalue()

        headers = {}
        headers[
            "content-type"] = f'multipart/mixed; boundary="{message.get_boundary()}"'
        headers['authorization'] = access_token

        LOG.debug("Sending the batch request...")
        p1 = time.perf_counter()
        backoff = 1
        while True:
            try:
                async with aiohttp.ClientSession() as session:
                    async with session.post(url=self._batch_uri,
                                            data=body,
                                            headers=headers) as response:
                        status = response.status
                        if 200 <= status < 300:
                            content = await response.text(encoding='utf-8')
                        elif status == 403 or status == 429:
                            LOG.warning(
                                f"Rate limit exceeded while sending the batch request"
                                f"(403={status==403,}, 429={status==429}), waiting {backoff} seconds."
                            )
                            await asyncio.sleep(backoff)
                            backoff *= 2
                            if backoff > 32:
                                data = await response.text(encoding='utf-8')
                                LOG.error(
                                    f"Repeated rate limit errors. Last error: {data}"
                                )
                                raise BatchError(f"{data}")
                        elif status == 401:
                            LOG.warning(
                                "BatchApiRequest.execute: 401 error encountered. Refreshing the token..."
                            )
                            headers[
                                'authorization'] = await asyncio.create_task(
                                    get_cached_token(GMAIL_TOKEN_ID))
                        else:
                            data = await response.text(encoding='utf-8')
                            LOG.error(
                                f"Unhandled error in BatchApiRequest.execute. Error: {data}"
                            )
                            raise BatchError(f"{data}")
                p2 = time.perf_counter()
                LOG.info(f"Batch response fetched in : {p2 - p1} seconds.")
            except aiohttp.ClientConnectionError as err:
                if backoff > 32:
                    LOG.error(
                        "Failed to send the batch request. Batch endpoint is unreachable and "
                        "back-off is greater than 32 seconds.")
                    raise
                LOG.warning(
                    f"Batch endpoint is unreachable, waiting {backoff} seconds."
                )
                await asyncio.sleep(backoff)
                backoff *= 2
            else:
                break

        await self.handle_response(response, content)
        if len(self.requests) > 0:
            LOG.warning(
                f"{len(self.requests)} tasks FAILED, calling execute again.")
            await self.execute()
        return self.completed_responses
Example #50
0
        def method(self, **kwargs):
            for name in kwargs.iterkeys():
                if name not in argmap:
                    raise TypeError('Got an unexpected keyword argument "%s"' %
                                    name)

            for name in required_params:
                if name not in kwargs:
                    raise TypeError('Missing required parameter "%s"' % name)

            for name, regex in pattern_params.iteritems():
                if name in kwargs:
                    if isinstance(kwargs[name], basestring):
                        pvalues = [kwargs[name]]
                    else:
                        pvalues = kwargs[name]
                    for pvalue in pvalues:
                        if re.match(regex, pvalue) is None:
                            raise TypeError(
                                'Parameter "%s" value "%s" does not match the pattern "%s"'
                                % (name, pvalue, regex))

            for name, enums in enum_params.iteritems():
                if name in kwargs:
                    if kwargs[name] not in enums:
                        raise TypeError(
                            'Parameter "%s" value "%s" is not an allowed value in "%s"'
                            % (name, kwargs[name], str(enums)))

            actual_query_params = {}
            actual_path_params = {}
            for key, value in kwargs.iteritems():
                to_type = param_type.get(key, 'string')
                # For repeated parameters we cast each member of the list.
                if key in repeated_params and type(value) == type([]):
                    cast_value = [_cast(x, to_type) for x in value]
                else:
                    cast_value = _cast(value, to_type)
                if key in query_params:
                    actual_query_params[argmap[key]] = cast_value
                if key in path_params:
                    actual_path_params[argmap[key]] = cast_value
            body_value = kwargs.get('body', None)
            media_filename = kwargs.get('media_body', None)

            if self._developerKey:
                actual_query_params['key'] = self._developerKey

            headers = {}
            headers, params, query, body = self._model.request(
                headers, actual_path_params, actual_query_params, body_value)

            expanded_url = uritemplate.expand(pathUrl, params)
            url = urlparse.urljoin(self._baseUrl, expanded_url + query)

            resumable = None
            multipart_boundary = ''

            if media_filename:
                # Convert a simple filename into a MediaUpload object.
                if isinstance(media_filename, basestring):
                    (media_mime_type,
                     encoding) = mimetypes.guess_type(media_filename)
                    if media_mime_type is None:
                        raise UnknownFileType(media_filename)
                    if not mimeparse.best_match([media_mime_type],
                                                ','.join(accept)):
                        raise UnacceptableMimeTypeError(media_mime_type)
                    media_upload = MediaFileUpload(media_filename,
                                                   media_mime_type)
                elif isinstance(media_filename, MediaUpload):
                    media_upload = media_filename
                else:
                    raise TypeError(
                        'media_filename must be str or MediaUpload. Got %s' %
                        type(media_upload))

                if media_upload.resumable():
                    resumable = media_upload

                # Check the maxSize
                if maxSize > 0 and media_upload.size() > maxSize:
                    raise MediaUploadSizeError("Media larger than: %s" %
                                               maxSize)

                # Use the media path uri for media uploads
                if media_upload.resumable():
                    expanded_url = uritemplate.expand(mediaResumablePathUrl,
                                                      params)
                else:
                    expanded_url = uritemplate.expand(mediaPathUrl, params)
                url = urlparse.urljoin(self._baseUrl, expanded_url + query)

                if body is None:
                    # This is a simple media upload
                    headers['content-type'] = media_upload.mimetype()
                    expanded_url = uritemplate.expand(mediaResumablePathUrl,
                                                      params)
                    if not media_upload.resumable():
                        body = media_upload.getbytes(0, media_upload.size())
                else:
                    # This is a multipart/related upload.
                    msgRoot = MIMEMultipart('related')
                    # msgRoot should not write out it's own headers
                    setattr(msgRoot, '_write_headers', lambda self: None)

                    # attach the body as one part
                    msg = MIMENonMultipart(*headers['content-type'].split('/'))
                    msg.set_payload(body)
                    msgRoot.attach(msg)

                    # attach the media as the second part
                    msg = MIMENonMultipart(*media_upload.mimetype().split('/'))
                    msg['Content-Transfer-Encoding'] = 'binary'

                    if media_upload.resumable():
                        # This is a multipart resumable upload, where a multipart payload
                        # looks like this:
                        #
                        #  --===============1678050750164843052==
                        #  Content-Type: application/json
                        #  MIME-Version: 1.0
                        #
                        #  {'foo': 'bar'}
                        #  --===============1678050750164843052==
                        #  Content-Type: image/png
                        #  MIME-Version: 1.0
                        #  Content-Transfer-Encoding: binary
                        #
                        #  <BINARY STUFF>
                        #  --===============1678050750164843052==--
                        #
                        # In the case of resumable multipart media uploads, the <BINARY
                        # STUFF> is large and will be spread across multiple PUTs.  What we
                        # do here is compose the multipart message with a random payload in
                        # place of <BINARY STUFF> and then split the resulting content into
                        # two pieces, text before <BINARY STUFF> and text after <BINARY
                        # STUFF>. The text after <BINARY STUFF> is the multipart boundary.
                        # In apiclient.http the HttpRequest will send the text before
                        # <BINARY STUFF>, then send the actual binary media in chunks, and
                        # then will send the multipart delimeter.

                        payload = hex(random.getrandbits(300))
                        msg.set_payload(payload)
                        msgRoot.attach(msg)
                        body = msgRoot.as_string()
                        body, _ = body.split(payload)
                        resumable = media_upload
                    else:
                        payload = media_upload.getbytes(0, media_upload.size())
                        msg.set_payload(payload)
                        msgRoot.attach(msg)
                        body = msgRoot.as_string()

                    multipart_boundary = msgRoot.get_boundary()
                    headers['content-type'] = (
                        'multipart/related; '
                        'boundary="%s"') % multipart_boundary

            logging.info('URL being requested: %s' % url)
            return self._requestBuilder(self._http,
                                        self._model.response,
                                        url,
                                        method=httpMethod,
                                        body=body,
                                        headers=headers,
                                        methodId=methodId,
                                        resumable=resumable)
Example #51
0
    def execute(self, http=None):
        """Execute all the requests as a single batched HTTP request.

    Args:
      http: httplib2.Http, an http object to be used in place of the one the
        HttpRequest request object was constructed with.  If one isn't supplied
        then use a http object from the requests in this batch.

    Returns:
      None

    Raises:
      apiclient.errors.HttpError if the response was not a 2xx.
      httplib2.Error if a transport error has occured.
    """
        if http is None:
            for request_id in self._order:
                request, callback = self._requests[request_id]
                if request is not None:
                    http = request.http
                    break
        if http is None:
            raise ValueError("Missing a valid http object.")

        msgRoot = MIMEMultipart('mixed')
        # msgRoot should not write out it's own headers
        setattr(msgRoot, '_write_headers', lambda self: None)

        # Add all the individual requests.
        for request_id in self._order:
            request, callback = self._requests[request_id]

            msg = MIMENonMultipart('application', 'http')
            msg['Content-Transfer-Encoding'] = 'binary'
            msg['Content-ID'] = self._id_to_header(request_id)

            body = self._serialize_request(request)
            msg.set_payload(body)
            msgRoot.attach(msg)

        body = msgRoot.as_string()

        headers = {}
        headers['content-type'] = ('multipart/mixed; '
                                   'boundary="%s"') % msgRoot.get_boundary()

        resp, content = http.request(self._batch_uri,
                                     'POST',
                                     body=body,
                                     headers=headers)

        if resp.status >= 300:
            raise HttpError(resp, content, self._batch_uri)

        # Now break up the response and process each one with the correct postproc
        # and trigger the right callbacks.
        boundary, _ = content.split(None, 1)

        # Prepend with a content-type header so FeedParser can handle it.
        header = 'Content-Type: %s\r\n\r\n' % resp['content-type']
        content = header + content

        parser = FeedParser()
        parser.feed(content)
        respRoot = parser.close()

        if not respRoot.is_multipart():
            raise BatchError("Response not in multipart/mixed format.")

        parts = respRoot.get_payload()
        for part in parts:
            request_id = self._header_to_id(part['Content-ID'])

            headers, content = self._deserialize_response(part.get_payload())

            # TODO(jcgregorio) Remove this temporary hack once the server stops
            # gzipping individual response bodies.
            if content[0] != '{':
                gzipped_content = content
                content = gzip.GzipFile(
                    fileobj=StringIO.StringIO(gzipped_content)).read()

            request, cb = self._requests[request_id]
            postproc = request.postproc
            response = postproc(resp, content)
            if cb is not None:
                cb(request_id, response)
            if self._callback is not None:
                self._callback(request_id, response)
Example #52
0
    def _send_batch_request(self, requests):
        """Sends a batch of requests to the server and processes the HTTP responses.

    Args:
      requests: List of GoogleComputeEngineBase.API_REQUEST named tuples. Must
        contain <= MAX_BATCH_SIZE elements.

    Raises:
      ValueError: If requests has more than MAX_BATCH_SIZE elements.

    Returns:
      List of GoogleComputeEngineBase.BATCH_RESPONSE named tuples, one for
      each element of request parameter.
    """
        if len(requests) > MAX_BATCH_SIZE:
            raise ValueError('Too many requests provided'
                             '(maximum is {0})'.format(MAX_BATCH_SIZE))

        batch = _BatchApiRequest()
        base = urlparse.urlsplit(self.base_url)
        base_path = base.path.rstrip('/')
        for i, request in enumerate(requests):
            msg = MIMENonMultipart('application', 'http')
            msg.add_header('Content-ID', '<{0}>'.format(i))
            msg.set_payload(
                self._serialize_batch_api_request(base_path, request))
            batch.attach(msg)

        batch_string = batch.as_string()
        content_type = 'multipart/mixed; boundary="{0}"'.format(
            batch.get_boundary())

        url = urlparse.urlunsplit((base.scheme, base.netloc, 'batch',
                                   self._create_url_query(None), None))
        response, data = self._send_request(url, 'POST', batch_string,
                                            content_type)

        if response.status >= 300:
            error = gce.GceError(message=response.reason,
                                 status=response.status)
            return [error] * len(requests)  # Return all errors.
        elif not data:
            error = gce.GceError(message='Server returned no data',
                                 status=response.status)
            return [error] * len(requests)  # Return all errors.

        # Process successful response.
        data = 'content-type: {0}\r\n\r\n'.format(
            response['content-type']) + data
        parser = FeedParser()
        parser.feed(data)
        response = parser.close()

        responses = []
        for part in response.get_payload():
            responses.append(
                (int(RESPONSE_ID_REGEX.match(part['Content-ID']).group(1)),
                 self._parse_batch_api_response(part.get_payload())))

        responses.sort(key=lambda r: r[0])
        return [r[1] for r in responses]
Example #53
0
    NEW_PURPOSE_REGISTER: {
        'subject': _('Your new account on %(domain)s'),
    },
    NEW_PURPOSE_SET_PASSWORD: {
        'subject': _('Reset the password for your %(domain)s account'),
    },
    NEW_PURPOSE_SET_EMAIL: {
        'subject': _l('Confirm the email address for your %(domain)s account'),
    },
    NEW_PURPOSE_DELETE: {
        'subject': _l('Delete your account on %(domain)s'),
    },
}

log = logging.getLogger(__name__)
pgp_version = MIMENonMultipart('application', 'pgp-encrypted')
pgp_version.add_header('Content-Description',
                       'PGP/MIME version identification')
pgp_version.set_payload('Version: 1\n')


@python_2_unicode_compatible
class RegistrationUser(XmppBackendUser):
    # NOTE: MySQL only allows a 255 character limit
    jid = models.CharField(max_length=255, unique=True, verbose_name='JID')
    email = models.EmailField(null=True, blank=True)
    gpg_fingerprint = models.CharField(max_length=40, null=True, blank=True)

    # when the account was first registered
    registered = models.DateTimeField(auto_now_add=True)
    registration_method = models.SmallIntegerField(
Example #54
0
    def as_message(self, escape_addresses=True):
        # http://wordeology.com/computer/how-to-send-good-unicode-email-with-python.html
        # http://stackoverflow.com/questions/31714221/how-to-send-an-email-with-quoted
        # http://stackoverflow.com/questions/9403265/how-do-i-use-python/9509718#9509718
        charset = Charset('utf-8')
        charset.header_encoding = QP
        charset.body_encoding = QP
        msg = MIMEMultipart()

        # Headers
        unixfrom = "From %s %s" % (self.sender.address,
                                   self.archived_date.strftime("%c"))
        header_from = self.sender.address
        if self.sender_name and self.sender_name != self.sender.address:
            header_from = "%s <%s>" % (self.sender_name, header_from)
        header_to = self.mailinglist.name
        if escape_addresses:
            header_from = header_from.replace("@", " at ")
            header_to = header_to.replace("@", " at ")
            unixfrom = unixfrom.replace("@", " at ")
        msg.set_unixfrom(unixfrom)
        headers = (
            ("From", header_from),
            ("To", header_to),
            ("Subject", self.subject),
        )
        for header_name, header_value in headers:
            if not header_value:
                continue
            try:
                msg[header_name] = header_value.encode('ascii')
            except UnicodeEncodeError:
                msg[header_name] = Header(header_value.encode('utf-8'),
                                          charset).encode()
        tz = get_fixed_timezone(self.timezone)
        header_date = self.date.astimezone(tz).replace(microsecond=0)
        # Date format: http://tools.ietf.org/html/rfc5322#section-3.3
        msg["Date"] = header_date.strftime("%a, %d %b %Y %H:%M:%S %z")
        msg["Message-ID"] = "<%s>" % self.message_id
        if self.in_reply_to:
            msg["In-Reply-To"] = self.in_reply_to

        # Body
        content = self.ADDRESS_REPLACE_RE.sub(r"\1(a)\2", self.content)
        # Don't use MIMEText, it won't encode to quoted-printable
        textpart = MIMENonMultipart("text", "plain", charset='utf-8')
        textpart.set_payload(content, charset=charset)
        msg.attach(textpart)

        # Attachments
        for attachment in self.attachments.order_by("counter"):
            mimetype = attachment.content_type.split('/', 1)
            part = MIMEBase(mimetype[0], mimetype[1])
            part.set_payload(attachment.content)
            encode_base64(part)
            part.add_header('Content-Disposition',
                            'attachment',
                            filename=attachment.name)
            msg.attach(part)

        return msg
Example #55
0
    def prepare_email_message(self):
        """
        Returns a django ``EmailMessage`` or ``EmailMultiAlternatives`` object,
        depending on whether html_message is empty.
        """
        if get_override_recipients():
            self.to = get_override_recipients()

        if self.template is not None:
            engine = get_template_engine()
            subject = engine.from_string(self.template.subject).render(
                self.context)
            plaintext_message = engine.from_string(
                self.template.content).render(self.context)
            multipart_template = engine.from_string(self.template.html_content)
            html_message = multipart_template.render(self.context)

        else:
            subject = smart_str(self.subject)
            plaintext_message = self.message
            multipart_template = None
            html_message = self.html_message

        connection = connections[self.backend_alias or 'default']
        if isinstance(self.headers, dict) or self.expires_at:
            headers = dict(self.headers or {})
            if self.expires_at:
                headers.update({
                    'Expires':
                    self.expires_at.strftime("%a, %-d %b %H:%M:%S %z")
                })
        else:
            headers = None

        if html_message:
            if plaintext_message:
                msg = EmailMultiAlternatives(subject=subject,
                                             body=plaintext_message,
                                             from_email=self.from_email,
                                             to=self.to,
                                             bcc=self.bcc,
                                             cc=self.cc,
                                             headers=headers,
                                             connection=connection)
                msg.attach_alternative(html_message, "text/html")
            else:
                msg = EmailMultiAlternatives(subject=subject,
                                             body=html_message,
                                             from_email=self.from_email,
                                             to=self.to,
                                             bcc=self.bcc,
                                             cc=self.cc,
                                             headers=headers,
                                             connection=connection)
                msg.content_subtype = 'html'
            if hasattr(multipart_template, 'attach_related'):
                multipart_template.attach_related(msg)

        else:
            msg = EmailMessage(subject=subject,
                               body=plaintext_message,
                               from_email=self.from_email,
                               to=self.to,
                               bcc=self.bcc,
                               cc=self.cc,
                               headers=headers,
                               connection=connection)

        for attachment in self.attachments.all():
            if attachment.headers:
                mime_part = MIMENonMultipart(*attachment.mimetype.split('/'))
                mime_part.set_payload(attachment.file.read())
                for key, val in attachment.headers.items():
                    try:
                        mime_part.replace_header(key, val)
                    except KeyError:
                        mime_part.add_header(key, val)
                msg.attach(mime_part)
            else:
                msg.attach(attachment.name,
                           attachment.file.read(),
                           mimetype=attachment.mimetype or None)
            attachment.file.close()

        self._cached_email_message = msg
        return msg
Example #56
0
 def __init__(self, _text):
     MIMENonMultipart.__init__(self, 'text', 'plain',
                               **{'charset': self.patch_charset})
     self.set_payload(_text.encode(self.patch_charset))
     encode_7or8bit(self)
Example #57
0
    def method(self, **kwargs):
      for name in six.iterkeys(kwargs):
        if name not in argmap:
          raise TypeError('Got an unexpected keyword argument "%s"' % name)

      for name in required_params:
        if name not in kwargs:
          raise TypeError('Missing required parameter "%s"' % name)

      for name, regex in six.iteritems(pattern_params):
        if name in kwargs:
          if isinstance(kwargs[name], six.string_types):
            pvalues = [kwargs[name]]
          else:
            pvalues = kwargs[name]
          for pvalue in pvalues:
            if re.match(regex, pvalue) is None:
              raise TypeError(
                  'Parameter "%s" value "%s" does not match the pattern "%s"' %
                  (name, pvalue, regex))

      for name, enums in six.iteritems(enum_params):
        if name in kwargs:
          if kwargs[name] not in enums:
            raise TypeError(
                'Parameter "%s" value "%s" is not an allowed value in "%s"' %
                (name, kwargs[name], str(enums)))

      actual_query_params = {}
      actual_path_params = {}
      for key, value in six.iteritems(kwargs):
        to_type = param_type.get(key, 'string')
        # For repeated parameters we cast each member of the list.
        if key in repeated_params and type(value) == type([]):
          cast_value = [_cast(x, to_type) for x in value]
        else:
          cast_value = _cast(value, to_type)
        if key in query_params:
          actual_query_params[argmap[key]] = cast_value
        if key in path_params:
          actual_path_params[argmap[key]] = cast_value
      body_value = kwargs.get('body', None)
      media_filename = kwargs.get('media_body', None)

      if self._developerKey:
        actual_query_params['key'] = self._developerKey

      model = self._model
      # If there is no schema for the response then presume a binary blob.
      if 'response' not in methodDesc:
        model = RawModel()

      headers = {}
      headers, params, query, body = model.request(headers,
          actual_path_params, actual_query_params, body_value)

      expanded_url = uritemplate.expand(pathUrl, params)
      url = six.moves.urllib.parse.urljoin(self._baseUrl, expanded_url + query)

      resumable = None
      multipart_boundary = ''

      if media_filename:
        # Ensure we end up with a valid MediaUpload object.
        if isinstance(media_filename, six.string_types):
          (media_mime_type, encoding) = mimetypes.guess_type(media_filename)
          if media_mime_type is None:
            raise UnknownFileType(media_filename)
          if not mimeparse.best_match([media_mime_type], ','.join(accept)):
            raise UnacceptableMimeTypeError(media_mime_type)
          media_upload = MediaFileUpload(media_filename, media_mime_type)
        elif isinstance(media_filename, MediaUpload):
          media_upload = media_filename
        else:
          raise TypeError('media_filename must be str or MediaUpload.')

        # Check the maxSize
        if maxSize > 0 and media_upload.size() > maxSize:
          raise MediaUploadSizeError("Media larger than: %s" % maxSize)

        # Use the media path uri for media uploads
        if media_upload.resumable():
          expanded_url = uritemplate.expand(mediaResumablePathUrl, params)
        else:
          expanded_url = uritemplate.expand(mediaPathUrl, params)
        url = six.moves.urllib.parse.urljoin(self._baseUrl, expanded_url + query)

        if media_upload.resumable():
          # This is all we need to do for resumable, if the body exists it gets
          # sent in the first request, otherwise an empty body is sent.
          resumable = media_upload
        else:
          # A non-resumable upload
          if body is None:
            # This is a simple media upload
            headers['content-type'] = media_upload.mimetype()
            body = media_upload.getbytes(0, media_upload.size())
          else:
            # This is a multipart/related upload.
            msgRoot = MIMEMultipart('related')
            # msgRoot should not write out it's own headers
            setattr(msgRoot, '_write_headers', lambda self: None)

            # attach the body as one part
            msg = MIMENonMultipart(*headers['content-type'].split('/'))
            msg.set_payload(body)
            msgRoot.attach(msg)

            # attach the media as the second part
            msg = MIMENonMultipart(*media_upload.mimetype().split('/'))
            msg['Content-Transfer-Encoding'] = 'binary'

            payload = media_upload.getbytes(0, media_upload.size())
            msg.set_payload(payload)
            msgRoot.attach(msg)
            body = msgRoot.as_string()

            multipart_boundary = msgRoot.get_boundary()
            headers['content-type'] = ('multipart/related; '
                                       'boundary="%s"') % multipart_boundary

      logging.info('URL being requested: %s' % url)
      return self._requestBuilder(self._http,
                                  model.response,
                                  url,
                                  method=httpMethod,
                                  body=body,
                                  headers=headers,
                                  methodId=methodId,
                                  resumable=resumable)
Example #58
0
    def _execute(self, http, order, requests):
        """Serialize batch request, send to server, process response.

    Args:
      http: httplib2.Http, an http object to be used to make the request with.
      order: list, list of request ids in the order they were added to the
        batch.
      request: list, list of request objects to send.

    Raises:
      httplib2.Error if a transport error has occured.
      apiclient.errors.BatchError if the response is the wrong format.
    """
        message = MIMEMultipart('mixed')
        # Message should not write out it's own headers.
        setattr(message, '_write_headers', lambda self: None)

        # Add all the individual requests.
        for request_id in order:
            request = requests[request_id]

            msg = MIMENonMultipart('application', 'http')
            msg['Content-Transfer-Encoding'] = 'binary'
            msg['Content-ID'] = self._id_to_header(request_id)

            body = self._serialize_request(request)
            msg.set_payload(body)
            message.attach(msg)

        body = message.as_string()

        headers = {}
        headers['content-type'] = ('multipart/mixed; '
                                   'boundary="%s"') % message.get_boundary()

        resp, content = http.request(self._batch_uri,
                                     'POST',
                                     body=body,
                                     headers=headers)

        if resp.status >= 300:
            raise HttpError(resp, content, self._batch_uri)

        # Now break out the individual responses and store each one.
        boundary, _ = content.split(None, 1)

        # Prepend with a content-type header so FeedParser can handle it.
        header = 'content-type: %s\r\n\r\n' % resp['content-type']
        for_parser = header + content

        parser = FeedParser()
        parser.feed(for_parser)
        mime_response = parser.close()

        if not mime_response.is_multipart():
            raise BatchError("Response not in multipart/mixed format.", resp,
                             content)

        for part in mime_response.get_payload():
            request_id = self._header_to_id(part['Content-ID'])
            headers, content = self._deserialize_response(part.get_payload())
            self._responses[request_id] = (headers, content)
Example #59
0
def send_email(smtp, recipients, cc, subject, content, csv_reports):
    """Sends an email with attachment.
    Refer to https://gist.github.com/BietteMaxime/f75ae41f7b4557274a9f

    Args:
        smtp: A dictionary containing smtp info:
            - smtp_url
            - smtp_auth_username # optional
            - smtp_auth_password # optional
            - smtp_from
        recipients: To whom to send the email.
        cc: To whom to cc the email.
        subject: Email subject.
        content: Email body content
        csv_reports: List of dictionaries containing "filename", "data" to
            construct CSV attachments.

    Returns:
        None
    """
    if not isinstance(smtp, dict):
        logger.warning("smtp is not a dictionary. Skip.")
        return

    sender = smtp.get("smtp_from", None)
    smtp_url = smtp.get("smtp_url", None)
    smtp_auth_username = smtp.get("smtp_auth_username", None)
    smtp_auth_password = smtp.get("smtp_auth_password", None)
    if sender is None or smtp_url is None:
        logger.warning("Some fields in smtp %s is None. Skip.", smtp)
        return

    # Create message container - the correct MIME type is multipart/mixed
    # to allow attachment.
    full_email = MIMEMultipart("mixed")
    full_email["Subject"] = subject
    full_email["From"] = sender
    full_email["To"] = ", ".join(recipients)
    full_email["CC"] = ", ".join(cc)

    # Create the body of the message (a plain-text version).
    content = content.encode(ENCODING)
    content = MIMEText(content, "plain", _charset=ENCODING)
    full_email.attach(content)

    # Create the attachment of the message in text/csv.
    for report in csv_reports:
        attachment = MIMENonMultipart("text", "csv", charset=ENCODING)
        attachment.add_header("Content-Disposition",
                              "attachment",
                              filename=report["filename"])
        cs = Charset(ENCODING)
        cs.body_encoding = BASE64
        attachment.set_payload(report["data"].encode(ENCODING), charset=cs)
        full_email.attach(attachment)

    try:
        with smtplib.SMTP(smtp_url) as server:
            if smtp_auth_username is not None and smtp_auth_password is not None:
                server.starttls()
                server.login(smtp_auth_username, smtp_auth_password)

            receivers = recipients + cc
            server.sendmail(sender, receivers, full_email.as_string())
            logger.info("Successfully sent email to %s and cc %s",
                        ", ".join(recipients), ", ".join(cc))
    except smtplib.SMTPAuthenticationError:
        logger.warning("The server didn\'t accept the user\\password "
                       "combination.")
    except smtplib.SMTPServerDisconnected:
        logger.warning("Server unexpectedly disconnected")
    except smtplib.SMTPException as e:
        logger.exception("SMTP error occurred: %s", e)
Example #60
0
        def method(self, **kwargs):
            for name in kwargs.iterkeys():
                if name not in argmap:
                    raise TypeError('Got an unexpected keyword argument "%s"' %
                                    name)

            for name in required_params:
                if name not in kwargs:
                    raise TypeError('Missing required parameter "%s"' % name)

            for name, regex in pattern_params.iteritems():
                if name in kwargs:
                    if re.match(regex, kwargs[name]) is None:
                        raise TypeError(
                            'Parameter "%s" value "%s" does not match the pattern "%s"'
                            % (name, kwargs[name], regex))

            for name, enums in enum_params.iteritems():
                if name in kwargs:
                    if kwargs[name] not in enums:
                        raise TypeError(
                            'Parameter "%s" value "%s" is not an allowed value in "%s"'
                            % (name, kwargs[name], str(enums)))

            actual_query_params = {}
            actual_path_params = {}
            for key, value in kwargs.iteritems():
                to_type = param_type.get(key, 'string')
                # For repeated parameters we cast each member of the list.
                if key in repeated_params and type(value) == type([]):
                    cast_value = [_cast(x, to_type) for x in value]
                else:
                    cast_value = _cast(value, to_type)
                if key in query_params:
                    actual_query_params[argmap[key]] = cast_value
                if key in path_params:
                    actual_path_params[argmap[key]] = cast_value
            body_value = kwargs.get('body', None)
            media_filename = kwargs.get('media_body', None)

            if self._developerKey:
                actual_query_params['key'] = self._developerKey

            headers = {}
            headers, params, query, body = self._model.request(
                headers, actual_path_params, actual_query_params, body_value)

            expanded_url = uritemplate.expand(pathUrl, params)
            url = urlparse.urljoin(self._baseUrl, expanded_url + query)

            if media_filename:
                (media_mime_type,
                 encoding) = mimetypes.guess_type(media_filename)
                if media_mime_type is None:
                    raise UnknownFileType(media_filename)
                if not mimeparse.best_match([media_mime_type],
                                            ','.join(accept)):
                    raise UnacceptableMimeTypeError(media_mime_type)

                # Check the maxSize
                if maxSize > 0 and os.path.getsize(media_filename) > maxSize:
                    raise MediaUploadSizeError(media_filename)

                # Use the media path uri for media uploads
                expanded_url = uritemplate.expand(mediaPathUrl, params)
                url = urlparse.urljoin(self._baseUrl, expanded_url + query)

                if body is None:
                    headers['content-type'] = media_mime_type
                    # make the body the contents of the file
                    f = file(media_filename, 'rb')
                    body = f.read()
                    f.close()
                else:
                    msgRoot = MIMEMultipart('related')
                    # msgRoot should not write out it's own headers
                    setattr(msgRoot, '_write_headers', lambda self: None)

                    # attach the body as one part
                    msg = MIMENonMultipart(*headers['content-type'].split('/'))
                    msg.set_payload(body)
                    msgRoot.attach(msg)

                    # attach the media as the second part
                    msg = MIMENonMultipart(*media_mime_type.split('/'))
                    msg['Content-Transfer-Encoding'] = 'binary'

                    f = file(media_filename, 'rb')
                    msg.set_payload(f.read())
                    f.close()
                    msgRoot.attach(msg)

                    body = msgRoot.as_string()

                    # must appear after the call to as_string() to get the right boundary
                    headers['content-type'] = (
                        'multipart/related; '
                        'boundary="%s"') % msgRoot.get_boundary()

            logging.info('URL being requested: %s' % url)
            return self._requestBuilder(self._http,
                                        self._model.response,
                                        url,
                                        method=httpMethod,
                                        body=body,
                                        headers=headers,
                                        methodId=methodId)