Beispiel #1
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 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
    def method(self, **kwargs):
        # Don't bother with doc string, it will be over-written by createMethod.

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

        # Remove args that have a value of None.
        keys = 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:
                raise TypeError('Missing required parameter "%s"' % name)

        for name, regex in parameters.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 parameters.enum_params.iteritems():
            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], basestring)):
                    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 kwargs.iteritems():
            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)

        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 = urlparse.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, 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,
                                               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 maxSize > 0 and media_upload.size() > maxSize:
                raise MediaUploadSizeError("Media larger than: %s" % maxSize)

            # Use the media path uri for media uploads
            expanded_url = uritemplate.expand(mediaPathUrl, params)
            url = urlparse.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 = StringIO.StringIO()
                    g = Generator(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)
Beispiel #3
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:
                    error_message = ("Failed decompressing payload from {} of"
                                     " length {} due to: {}".format(
                                         ctype_orig, len(payload), e))
                    _handle_error(error_message, e)
                    continue

            # Attempt to figure out the payloads content-type
            if not ctype_orig:
                ctype_orig = UNDEF_TYPE
            # There are known cases where mime-type text/x-shellscript included
            # non shell-script content that was user-data instead.  It is safe
            # to check the true MIME type for x-shellscript type since all
            # shellscript payloads must have a #! header.  The other MIME types
            # that cloud-init supports do not have the same guarantee.
            if ctype_orig in TYPE_NEEDED + ['text/x-shellscript']:
                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)
Beispiel #4
0
    def send_file(self,
                  filename,
                  addresses,
                  original=None,
                  mime_type='text/plain',
                  rotate_date=None,
                  charset=None):
        '''
        Mails the file with the given file name as an attachement
        to the given recipient(s).

        Raises a LogRotateMailerError on harder errors.

        @param filename:  The file name of the file to send (the existing,
                          rotated and maybe compressed logfile).
        @type filename:   str
        @param addresses: A list of tuples of a pair in the form
                          of the return value of email.utils.parseaddr()
        @type addresses:  list
        @param original:  The file name of the original (unrotated) logfile for
                          informational purposes.
                          If not given, filename is used instead.
        @type original:   str or None
        @param mime_type: MIME type (content type) of the original logfile,
                          defaults to 'text/plain'
        @type mime_type:  str
        @param rotate_date: datetime object of rotation, defaults to now()
        @type rotate_date:  datetime or None
        @param charset: character set of (uncompreesed) logfile, if the
                        mime_type is 'text/plain', defaults to 'utf-8'
        @type charset:  str or None

        @return: success of sending
        @rtype:  bool
        '''

        _ = self.t.lgettext

        if not os.path.exists(filename):
            msg = _("File '%s' doesn't exists.") % (filename)
            self.logger.error(msg)
            return False

        if not os.path.isfile(filename):
            msg = _("File '%s' is not a regular file.") % (filename)
            self.logger.warning(msg)
            return False

        basename = os.path.basename(filename)
        if not original:
            original = os.path.abspath(filename)

        if not rotate_date:
            rotate_date = datetime.now()

        to_list = []
        for address in addresses:
            to_list.append(address[1])

        msg = (_("Sending mail with attached file '%(file)s' to: %(rcpt)s") % {
            'file':
            basename,
            'rcpt':
            ', '.join(
                map(lambda x:
                    ('"' + email.utils.formataddr(x) + '"'), addresses))
        })
        self.logger.debug(msg)

        mail_container = MIMEMultipart()
        mail_container['Date'] = email.utils.formatdate()
        mail_container['X-Mailer'] = ("pylogrotate version %s" %
                                      (self.mailer_version))
        mail_container['From'] = self.from_address
        mail_container['To'] = ', '.join(
            map(lambda x: email.utils.formataddr(x), addresses))
        mail_container['Subject'] = ("Rotated logfile '%s'" % (filename))
        mail_container.preamble = (
            'You will not see this in a MIME-aware mail reader.\n')

        # Generate Text of the first part of mail body
        mailtext = "Rotated Logfile:\n\n"
        mailtext += "\t - " + filename + "\n"
        mailtext += "\t   (" + original + ")\n"
        mailtext += "\n"
        mailtext += "Date of rotation: " + rotate_date.isoformat(' ')
        mailtext += "\n"
        mailtext = _encodestring(mailtext, quotetabs=False)
        mail_part = MIMENonMultipart('text',
                                     'plain',
                                     charset=sys.getdefaultencoding())
        mail_part.set_payload(mailtext)
        mail_part['Content-Transfer-Encoding'] = 'quoted-printable'
        mail_container.attach(mail_part)

        ctype, encoding = mimetypes.guess_type(filename)
        if self.verbose > 3:
            msg = (_("Guessed content-type: '%(ctype)s' " +
                     "and encoding '%(encoding)s'.") % {
                         'ctype': ctype,
                         'encoding': encoding
                     })
            self.logger.debug(msg)

        if encoding:
            if encoding == 'gzip':
                ctype = 'application/x-gzip'
            elif encoding == 'bzip2':
                ctype = 'application/x-bzip2'
            else:
                ctype = 'application/octet-stream'

        if not ctype:
            ctype = mime_type

        maintype, subtype = ctype.split('/', 1)
        fp = open(filename, 'rb')
        mail_part = MIMEBase(maintype, subtype)
        mail_part.set_payload(fp.read())
        fp.close()
        if maintype == 'text':
            msgtext = mail_part.get_payload()
            msgtext = _encodestring(msgtext, quotetabs=False)
            mail_part.set_payload(msgtext)
            mail_part['Content-Transfer-Encoding'] = 'quoted-printable'
        else:
            encoders.encode_base64(mail_part)
        mail_part.add_header('Content-Disposition',
                             'attachment',
                             filename=basename)
        mail_container.attach(mail_part)

        composed = mail_container.as_string()
        if self.verbose > 4:
            msg = _("Generated E-mail:") + "\n" + composed
            self.logger.debug(msg)

        if (not self.use_smtp) and self.sendmail:
            return self._send_per_sendmail(composed)
        else:
            return self._send_per_smtp(composed, to_list)

        return True
Beispiel #5
0
def build_email(mail_to,
                subject,
                body_text,
                mail_from=u'*****@*****.**',
                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({u'Subject': Header(s=subject, charset='utf-8')})

    # Add address headers
    for key, item in address_headers.iteritems():
        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.iteritems():
        #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.iteritems():

        # 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
Beispiel #6
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
        msg['From'] = from_ = config.get('email', 'from')
        if user.email:
            if user.name:
                user_email = formataddr((user.name, user.email))
            else:
                user_email = user.email
            msg['Behalf-Of'] = user_email
            msg['Reply-To'] = user_email
        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
Beispiel #7
0
    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,
        )