0

I am trying to implement a mailer API with Serverless Framework and AWS Lambda. Plan is to send the mail details and attachment in a multipart/form-data POST request and then read the file and append it as an attachment.

def handler(event, context):
    try:
        body_file = BytesIO(event['body'].encode('utf-8'))
        form, files = parse_into_field_storage(body_file, event['headers']['Content-Type'], body_file.getbuffer().nbytes)

        mails = get_templated_mails(form, files)

        send_mails(sender, mails)
    except Exception as e:
        print(e)
        traceback.print_exc()
        return { "statusCode": 500, "body": json.dumps({ 'message': "Mailer failed" })}

    return { "statusCode": 200, "body": json.dumps({ 'message': 'Success' })}

The following code reads the multipart/form-data request body and parses it

def parse_into_field_storage(fp, ctype, clength):
    fs = FieldStorage( fp=fp, environ={'REQUEST_METHOD': 'POST'}, headers={'content-type': ctype}, keep_blank_values=True )
    form = {}
    files = {}
    for f in fs.list:
        if f.filename:
            files.setdefault(f.name, []).append(f)
        else:
            form.setdefault(f.name, []).append(f.value)
    if len(files):
        files = files['attachments']
    return form, files

Below is the code to construct the mail, while the mail body looks fine, the attachment somehow is getting corrupted.

def get_templated_mails(form, files):
    mails = []

    sender = get_sender(form)
    template = form['template'][0]
    subject = form['subject'][0]
    template_data = json.loads(form['template_data'][0])
    print(template_data)

    for data in template_data:
        mail = MIMEMultipart()
        mail['To'] = data['to']
        mail['From'] = sender
        mail['Subject'] = populate_data_to_string(data['subject_data'], subject)
        body = MIMEText(populate_data_to_string(data['data'], template), 'html')
        mail.attach(body)
        mail = load_attachments(mail, files)
        mails.append({'raw': base64.b64encode(mail.as_string().encode()).decode()})
    return mails

def load_attachments(mail, files):
    for file in files:
        content_type, encoding = mimetypes.guess_type(file.filename)
        print("Mime type ==============> ", content_type, encoding)

        if content_type is None or encoding is not None:
            content_type = 'application/octet-stream'
        main_type, sub_type = content_type.split('/', 1)
        print(main_type, sub_type)
        if main_type == 'text':
            attachment = MIMEText('r', _subtype=sub_type)
        elif main_type == 'image':
            attachment = MIMEImage('r', _subtype=sub_type)
        elif main_type == 'audio':
            attachment = MIMEAudio('r', _subtype=sub_type)
        else:
            attachment = MIMEBase(main_type, sub_type)

        attachment.set_payload(base64.b64encode(file.fp.getbuffer()))

        filename = file.filename
        attachment.add_header('Content-Disposition', 'attachment', filename=filename)

        mail.attach(attachment)

    return mail

I have tried out a lot of things to fix encoding in load_attachments, but nothing seems to help. I also tried to get the file content after the multipart parsing itself and convert it as a file, looks like its getting corrupted there itself.

What am I doing wrong here? Any clue would be helpful.

stark_1
  • 53
  • 6

0 Answers0