maui icon indicating copy to clipboard operation
maui copied to clipboard

Fix e-mail URI escaping recipients

Open Cheesebaron opened this issue 1 year ago • 12 comments

Description of Change

Escaping the recipient e-mail addresses, leads to the character @ being escaped and creates an invalid URL.

So [email protected] becomes john%40myhost.com which isn't containing a valid e-mail.

On iOS this fails when translating the URL to NSUrl.

Issues Fixed

Fixes #12010

Cheesebaron avatar Feb 16 '23 15:02 Cheesebaron

Hey there @Cheesebaron! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

ghost avatar Feb 16 '23 15:02 ghost

Hold your horses, just reading RFC 2368, there are even more issues with this method for creating the URI. I guess no one ever tested it?

Everything is delimited using ? this is wrong. Only the first parameter should be formatted like that.

Cheesebaron avatar Feb 16 '23 15:02 Cheesebaron

I've changed the implementation, with inspiration from https://codereview.stackexchange.com/a/129600, to properly generate a RFC 2368 mailto URL.

So previously there were several issues.

  • To, CC, BCC where all URL escaped, which is not correct if they are not part of a parameter, so only if you use to=someone%40somewhere.net you can escape, but if using raw mailto:someone%40somewhere.net it won't work
  • The To recipients were delimited incorrectly for the format chosen

This has now been corrected and proper URLs are emitted. Perhaps someone should add some implementation to the e-mail tests ;)

Cheesebaron avatar Feb 16 '23 15:02 Cheesebaron

Also find it a bit weird why MailMessage is not used instead of rolling your own? Any particular reason for this?

Cheesebaron avatar Feb 16 '23 15:02 Cheesebaron

Maybe because this code was legacy ? Coming from Essentials?

Any thoughts @mattleibow ?

rmarinho avatar Feb 16 '23 17:02 rmarinho

/azp run

jfversluis avatar Feb 20 '23 18:02 jfversluis

Azure Pipelines successfully started running 2 pipeline(s).

azure-pipelines[bot] avatar Feb 20 '23 18:02 azure-pipelines[bot]

@Cheesebaron just an FYI we didn't forget about this one! Matt had some off time and trying to figure out the history here if there was a good reason for it. Otherwise I think this is a great improvement. Thank you for that! I'll make sure we get to it shortly :)

jfversluis avatar Feb 28 '23 15:02 jfversluis

@Cheesebaron just an FYI we didn't forget about this one! Matt had some off time and trying to figure out the history here if there was a good reason for it. Otherwise I think this is a great improvement. Thank you for that! I'll make sure we get to it shortly :)

Yep no worries. I have a workaround in my App for this which works, so I am not in a rush to get this in.

Cheesebaron avatar Feb 28 '23 15:02 Cheesebaron

The lack of MailMessage may have been a result of UWP... But also, does that support the construct of parameters to a uri? And then it may also pull in related code making the linker less effective.

But, this code appears to only be used on iOS, so if this looks good if it works.

mattleibow avatar Feb 28 '23 16:02 mattleibow

Also:

Perhaps someone should add some implementation to the e-mail tests

Thanks for starting the work on the tests! ;)

mattleibow avatar Feb 28 '23 16:02 mattleibow

Thanks for starting the work on the tests! ;)

🙈

I was having trouble even building the solution, but got that to work yesterday. I can have a look at adding some of those. There are no tests at all for any of this e-mail stuff to begin with 😂

Cheesebaron avatar Mar 01 '23 08:03 Cheesebaron

Azure Pipelines successfully started running 2 pipeline(s).

azure-pipelines[bot] avatar Mar 03 '23 14:03 azure-pipelines[bot]

Amazing!

There are no tests at all for any of this e-mail stuff

No idea what you mean! 😉 All I see are wonderful tests in this PR 😉 Perfect example of a PR and tests to go along with it!

Thanks a big bunch!

mattleibow avatar Mar 03 '23 16:03 mattleibow

Is there a way to work around this until the fix is released? I now realize that I've been seeing this crash in AppCenter because people needing help weren't able to contact me...

BioTurboNick avatar Mar 10 '23 00:03 BioTurboNick

Yeah, you can do anything you want in your code. To work around this I made a small service like so:

public class IosEmailService : IEmailService
{
    public Task SendMessage(EmailMessage message)
    {
        if (MFMailComposeViewController.CanSendMail)
            return Email.ComposeAsync(message);

        return ComposeWithUrl(message);
    }

    private static Task ComposeWithUrl(EmailMessage message)
    {
        var url = GetMailToUri(message);
        var nsurl = NSUrl.FromString(url);
        return Launcher.Default.OpenAsync(nsurl!);
    }

    private static string GetMailToUri(EmailMessage message) =>
        "mailto:?" + string.Join("&", Parameters(message));

    private static IEnumerable<string> Parameters(EmailMessage message)
    {
        if (message.To?.Any() == true)
            yield return "to=" + Recipients(message.To);

        if (message.Cc?.Any() == true)
            yield return "cc=" + Recipients(message.Cc);

        if (message.Bcc?.Any() == true)
            yield return "bcc=" + Recipients(message.Bcc);

        if (!string.IsNullOrWhiteSpace(message.Subject))
            yield return "subject=" + Uri.EscapeDataString(message.Subject);

        if (!string.IsNullOrWhiteSpace(message.Body))
            yield return "body=" + Uri.EscapeDataString(message.Body);
    }

    private static string Recipients(IEnumerable<string> addresses) =>
        string.Join(",", addresses.Select(Uri.EscapeDataString));
}

Calling SendMessage on my own service instead of calling MAUI Essentials directly.

Cheesebaron avatar Mar 10 '23 08:03 Cheesebaron

Hi @Cheesebaron , could you show us what's on your Email.ComposeAsync(message) on line 6 of your code above? Who is that Emailclass?

ederbond avatar Mar 29 '23 20:03 ederbond

Hi @Cheesebaron , could you show us what's on your Email.ComposeAsync(message) on line 6 of your code above? Who is that Emailclass?

That is just MAUI Essentials

Cheesebaron avatar Mar 30 '23 06:03 Cheesebaron

Backport it to .NET 7.

ederbond avatar Mar 30 '23 16:03 ederbond

/backport to net7.0

hartez avatar May 24 '23 15:05 hartez

Started backporting to net7.0: https://github.com/dotnet/maui/actions/runs/5070650052

github-actions[bot] avatar May 24 '23 15:05 github-actions[bot]