microsoftgraph / msgraph-sdk-go

Microsoft Graph SDK for Go
https://docs.microsoft.com/en-us/graph/sdks/sdks-overview
MIT License
235 stars 36 forks source link

Is there a way to post a raw RFC 5322 message to the create message endpoint? #701

Open dcormier opened 5 months ago

dcormier commented 5 months ago

I have an RFC 5322-formatted message, and I'm trying to POST it to the /users/{id | userPrincipalName}/mailFolders/{id}/messages endpoint.

Per the docs for that API, that is possible when POSTing a request to that endpoint with a content type of text/plain. Specifically, this note in the "Request headers" section for Content-Type (emphasis mine):

Use application/json for a JSON object and text/plain for MIME content.

Combined with this additional note in the "Request body" section:

When specifying the body in MIME format, provide the MIME content with the applicable Internet message headers ("To", "CC", "BCC", "Subject"), all encoded in base64 format in the request body.

I can't find a way to do this with the SDK. Am I missing something, or does the SDK not support this?

I'm able to get as far as this:

client.
    Users().
    ByUserId(userID).
    MailFolders().
    ByMailFolderId("inbox").
    Messages().
    Post(
        ctx,
        // This doesn't compile as it isn't `model.Messageable`.
        // How do I send this?
        base64.StdEncoding.EncodeToString(email),
        nil,
    )

A model.Messaage gets sent as application/json, and can't be built from an RFC 5322 message (as far as I can tell).

rkodev commented 4 months ago

Hi @dcormier, Thanks for trying the MS Graph Go SDK. As of now , v1.44.0 of graph go sdk, it does not support sending RFC 5322-formatted message. You should still be able to parse this message to a 'MessageObject' and send it via the SDK.

Here is some sample code

package main

import (
    "bytes"
    "fmt"
    "log"
    "net/mail"
    "strings"
)

// Message represents an email message.
type Message struct {
    From    string
    To      []string
    Cc      []string
    Bcc     []string
    Subject string
    Date    string
    Body    string
}

func parseRFC5322Message(rfc5322 string) (*Message, error) {
    // Create a reader for the message string
    reader := strings.NewReader(rfc5322)

    // Parse the message using the net/mail package
    msg, err := mail.ReadMessage(reader)
    if err != nil {
        return nil, err
    }

    // Extract the headers
    header := msg.Header

    // Get the values of the headers
    from := header.Get("From")
    to := header.Get("To")
    cc := header.Get("Cc")
    bcc := header.Get("Bcc")
    subject := header.Get("Subject")
    date := header.Get("Date")

    // Read the body
    bodyBuffer := new(bytes.Buffer)
    bodyBuffer.ReadFrom(msg.Body)
    body := bodyBuffer.String()

    // Split the "To", "Cc", and "Bcc" headers into slices
    toList := parseAddressList(to)
    ccList := parseAddressList(cc)
    bccList := parseAddressList(bcc)

    // Create the message model
    message := &Message{
        From:    from,
        To:      toList,
        Cc:      ccList,
        Bcc:     bccList,
        Subject: subject,
        Date:    date,
        Body:    body,
    }

    return message, nil
}

func parseAddressList(addresses string) []string {
    if addresses == "" {
        return []string{}
    }
    // Split the address list by commas and trim spaces
    list := strings.Split(addresses, ",")
    for i := range list {
        list[i] = strings.TrimSpace(list[i])
    }
    return list
}
dcormier commented 4 months ago

An approach like that won't work in this case because of subtle differences between MIME content and JSON representations. There are too many emails that don't adhere strictly to the RFCs. We need byte for byte precision for these emails. We need to be able to post the MIME content directly. We don't want this code to be pointed at for any discrepancies.