Search code examples
c#gmail-apiemail-headersmimekitbase64url

MimeMessage causing recipient address required error in Gmail Apis possibly due base64url encoding


Edit 1: After playing around further it appears that the space is not the culprit but possibly the base64url encode method is. I extracted the Raw string for both plain text and mime message and compared them in the the online tool. In case of plain text they both are same but in the case of mime message they are completely different however if I decode them back to string they are identical. I don't know a whole lot about encoding decoding so any help will be appreciated. Edit end.

I am trying to compose email in c# using mimekit and send it with google.apis.gmail.v1. but it throws error "Recipient address required[400]. Here is the code.

var mailMessage = new MailMessage
            {
                Subject = "test subject",
                Body = " <h1>test body</h1>"
            };
            mailMessage.IsBodyHtml = true;
            mailMessage.To.Add("testemail");

            MimeMessage mm = MimeMessage.CreateFromMailMessage(mailMessage);
            var gmailMessage = new Google.Apis.Gmail.v1.Data.Message
            {
                Raw = Base64UrlEncode(mm.ToString())
            };
            service.Users.Messages.Send(gmailMessage, "me").execute();
            // Encoding method
             public static string Base64UrlEncode(string text)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(text);

            return Convert.ToBase64String(bytes)
                .Replace('+', '-')
                .Replace('/', '_')
                .Replace("=", "");
        }

The above code throws error however if I compose email using plain text like below:

string plainText = "To:testemail\r\n" +
                        "Subject: test subject\r\n" +
                        "Content-Type: text/html; charset=us-ascii\r\n\r\n" +
                        "<h1>Test body<h1>";

if I encode and pass this plain text to Raw property of gmail message this works flawlessly. to debug the issue I decoded the plain text and mime message using an online tool and the only difference I notice is space before To header value like this:

// plain text:
To:testemail

//mime message:
To: testemail

notice the space after "To:". To confirm my suspicion I removed the space from mime message and encoded it again using online tool and passed the encoded string to Raw property and it works without error. So figured a hacky solution and tried removing the space using regex like this:

            var corrected = Regex.Replace(mm.ToString(), @"To: ", "To:");

But for some reason that doesn't work which doesn't make sense to me. According to the documentation Raw property takes RFC 2822 formatted and base64url encoded string and from what I could find RFC 2822 doesn't allow space after the header type. Any help will be appreciated :) For reference here is both strings decoded from raw one works and other doesn't;

//plain text that works:
To:[email protected]
Subject: Test subject
Content-Type: text/html; charset=us-ascii

<h1>Test body <h1>

// mime message that throw error
To: [email protected]
Subject: test message
Date: Date
MIME-Version: 1.0
Content-Type: text/html; charset=us-ascii

<h1>test body</h1>


Solution

  • The correct solution is something more like this:

    var mailMessage = new MailMessage
    {
        Subject = "test subject",
        Body = " <h1>test body</h1>"
    };
    mailMessage.IsBodyHtml = true;
    mailMessage.To.Add("testemail");
    
    MimeMessage mm = MimeMessage.CreateFromMailMessage(mailMessage);
    byte[] rawMimeData;
    using (var memory = new MemoryStream ()) {
        mm.WriteTo(memory);
        rawMimeData = memory.ToArray();
    }
    
    var gmailMessage = new Google.Apis.Gmail.v1.Data.Message
    {
        Raw = Base64UrlEncode(rawMimeData)
    };
    
    service.Users.Messages.Send(gmailMessage, "me").execute();
    
    // Encoding method
    public static string Base64UrlEncode(byte[] bytes)
    {
        return Convert.ToBase64String(bytes)
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");
    }