Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SmtpClient support #14288

Closed
d0pare opened this issue Feb 24, 2015 · 32 comments
Closed

Add SmtpClient support #14288

d0pare opened this issue Feb 24, 2015 · 32 comments
Milestone

Comments

@d0pare
Copy link

d0pare commented Feb 24, 2015

Is there any plans to add System.Net.Mail into aspnetcore50?

@Petermarcu
Copy link
Member

Its not in our current plans but the code is available here if you are interested in porting it to make it work.

@d0pare
Copy link
Author

d0pare commented Feb 25, 2015

Thank you very much, I will try to port.

@d0pare d0pare closed this as completed Feb 25, 2015
@guardrex
Copy link

I was just directed here by @Eilon on dotnet/aspnetcore#467. I have a need for basic SMTP capabilities in CoreCLR, and I was hoping to not need to roll my own TcpClient SMTP functionality. Can you say why it is thought that CoreCLR installs wouldn't need basic E-mail functionality? I thought it was a very popular API.

@guardrex
Copy link

I've posted a UserVoice request for this: Please vote for it here if you are in favor of having System.Net.Mail in CoreFX.

@guardrex
Copy link

Here is a code sample that I gathered from bits off the Net for a client on port 25 to get mail out through a network-based mail server (behind a firewall with the app so that sending unencrypted credentials isn't an issue).

Obviously, I didn't fix it up or anything ... It's not production quality, of course. I'll fix it up later. I just wanted to get this out now in case anyone needs a plan to get started with a little SMTP action.

If someone wants to wrap the stream in an sslStream to show how to make a secure connection, I hope they will post that sample ... and please vote at UserVoice for adding System.Net.Mail to CoreFX (CoreCLR).

using System.Net.Sockets;
using System.Text;
using System.IO;

using (var client = new TcpClient(server, port))
{
    using (var stream = client.GetStream())
    using (var reader = new StreamReader(stream))
    using (var writer = new StreamWriter(stream) { AutoFlush = true })
    {
        writer.WriteLine("HELO " + server);
        outputString += "1: " + reader.ReadLine() + " ";

        writer.WriteLine("AUTH LOGIN");
        outputString += "2: " + reader.ReadLine() + " ";

        string username = "[email protected]";
        var plainTextBytes1 = System.Text.Encoding.UTF8.GetBytes(username);
        string base64Username = System.Convert.ToBase64String(plainTextBytes1);
        writer.WriteLine(base64Username);
        outputString += "3: " + reader.ReadLine() + " ";

        string password = "PASSWORD_FOR_SERVICE_ACCOUNT";
        var plainTextBytes2 = System.Text.Encoding.UTF8.GetBytes(password);
        string base64Password = System.Convert.ToBase64String(plainTextBytes2);
        writer.WriteLine(base64Password);
        outputString += "4: " + reader.ReadLine() + " ";

        writer.WriteLine("MAIL FROM:<[email protected]>");
        outputString += "5: " + reader.ReadLine() + " ";

        writer.WriteLine("RCPT TO:<[email protected]>");
        outputString += "6: " + reader.ReadLine() + " ";

        writer.WriteLine("DATA");
        outputString += "7: " + reader.ReadLine() + " ";

        writer.WriteLine("From: \"Service Account\" <[email protected]>");
        writer.WriteLine("To: \"Somebody\" <[email protected]>");
        writer.WriteLine("Subject: Test message!");
        // Put one blank line after the Subject line, then start the message body.
        writer.WriteLine("");
        // Start the message body here
        writer.WriteLine("Hello Somebody,");
        writer.WriteLine("This is a test message.");
        writer.WriteLine("");
        writer.WriteLine("Candy’s dandy … but liquor is quicker!!");
        // Send a period to denote the end of the message body
        writer.WriteLine(".");
        outputString += "8: " + reader.ReadLine() + " ";

        writer.WriteLine("QUIT");
        outputString += "9: " + reader.ReadLine() + " ";
    }
}

Outputs:

1: 250 Hello.
2: 334 XXXXXXXXX
3: 334 XXXXXXXXX
4: 235 authenticated.
5: 250 OK
6: 250 OK
7: 354 OK, send.
8: 250 Queued (0.032 seconds)
9: 221 goodbye

... so you can do a .StartsWith() to make sure processing at each step went ok.

@Petermarcu
Copy link
Member

I think we can leave this issue open and up for grabs. Its totally reasonable to have this library and the code for it is already licensed appropriately here.

@Petermarcu Petermarcu reopened this Apr 23, 2015
@Petermarcu Petermarcu changed the title SmtpClient support in aspnetcore50. Add SmtpClient support Apr 23, 2015
@Petermarcu
Copy link
Member

@dopare, were you going to try to port this or should we leave it up for grabs for others to pick up?

@d0pare
Copy link
Author

d0pare commented Apr 24, 2015

@Petermarcu I was trying to find the project where I can start to port. But didn't find appropriate one.

@guardrex
Copy link

System.Net.Mail is quite a feat of engineering! However, it's great scope and depth look to make it a real bear to port. I took a look at where Roslyn chokes on just the System.Net.Mail namespace. There are ~2,400 issues, broken down below. It's good to see that string resources and logging knock out >1,000 of those. It's a big question mark how much work it would take to deal with that many external references. That last ~150 "Other" group is a real hodgepodge of little things to address.

Issue Count (# are approx)
String Resources 860
Assembly References 744
Logging 246
ValidationHelper 92
ResourceScope 72
GlobalLog 68
InvokeCallback 32
HeaderCollection 26
LazyAsyncResult 18
TlsStream 14
ConnectionPoolManager 10
SecurityAction 8
PermissionState 6
SecurityPermissionFlag 6
Other ~150

@guardrex
Copy link

And here is a sample for wrapping an SMTP connection into an sslStream for secure mail to places like Gmail. Again, this is just a kind of pseudo-code walk-through of what you must do to make the protocol work. This code will run fine, but it's far from production quality. Hopefully, this will tide CoreCLR folks over until we have something solid to use.

using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.IO;
using System.Net.Security;

const string server = "smtp.gmail.com";
const int port = 587;
using (var client = new TcpClient(server, port)) {
    using (var stream = client.GetStream())
    using (var reader = new StreamReader(stream))
    using (var writer = new StreamWriter(stream) { AutoFlush = true }) {
        outputString += "1: " + reader.ReadLine() + " ";

        writer.WriteLine("HELO " + server);
        outputString += "2: " + reader.ReadLine() + " ";

        writer.WriteLine("STARTTLS");
        outputString += "3: " + reader.ReadLine() + " ";

        using (var sslStream = new SslStream(client.GetStream(), false)) {

            sslStream.AuthenticateAsClient(server);

            using (var secureReader = new StreamReader(sslStream))
            using (var secureWriter = new StreamWriter(sslStream) { AutoFlush = true }) {

                secureWriter.WriteLine("AUTH LOGIN");
                outputString += "4: " + secureReader.ReadLine() + " ";

                string username = "YOUR_GMAIL_FULL_EMAIL_ADDRESS";
                var plainTextBytes1 = System.Text.Encoding.UTF8.GetBytes(username);
                string base64Username = System.Convert.ToBase64String(plainTextBytes1);
                secureWriter.WriteLine(base64Username);
                outputString += "5: " + secureReader.ReadLine() + " ";

                string password = "YOUR_GMAIL_PASSWORD";
                var plainTextBytes2 = System.Text.Encoding.UTF8.GetBytes(password);
                string base64Password = System.Convert.ToBase64String(plainTextBytes2);
                secureWriter.WriteLine(base64Password);
                outputString += "6: " + secureReader.ReadLine() + " ";

                secureWriter.WriteLine("MAIL FROM:<MESSAGE_FROM_EMAIL_ADDRESS>");
                outputString += "7: " + secureReader.ReadLine() + " ";

                secureWriter.WriteLine("RCPT TO:<MESSAGE_TO_EMAIL_ADDRESS>");
                outputString += "8: " + secureReader.ReadLine() + " ";

                secureWriter.WriteLine("DATA");
                outputString += "9: " + secureReader.ReadLine() + " ";

                secureWriter.WriteLine("From: \"MESSAGE_FROM_NAME\" <MESSAGE_FROM_EMAIL_ADDRESS>");
                secureWriter.WriteLine("To: \"MESSAGE_TO_NAME\" <MESSAGE_TO_EMAIL_ADDRESS>");
                secureWriter.WriteLine("EMAIL_SUBJECT");
                // Leave one blank line after the subject
                secureWriter.WriteLine("");
                // Start the message body here
                secureWriter.WriteLine("Hello Luke,");
                secureWriter.WriteLine("");
                secureWriter.WriteLine("Cuz! You gotta try Beck's Sapphire! It ROCKS!");
                secureWriter.WriteLine("");
                secureWriter.WriteLine("Later,");
                secureWriter.WriteLine("");
                secureWriter.WriteLine("Luke");
                // End the message body by sending a period
                secureWriter.WriteLine(".");
                outputString += "10: " + secureReader.ReadLine() + " ";

                secureWriter.WriteLine("QUIT");
                outputString += "11: " + secureReader.ReadLine() + " ";
            }
        }
    }
}

Will output:

1: 220 mx.google.com ESMTP xxxxxxxxxxxxxxxxxxxxx - gsmtp
2: 250 mx.google.com at your service
3: 220 2.0.0 Ready to start TLS
4: 334 xxxxxxxxxxxxxxx
5: 334 xxxxxxxxxxxxxxx
6: 235 2.7.0 Accepted
7: 250 2.1.0 OK xxxxxxxxxxxxxxxxxxx - gsmtp
8: 250 2.1.5 OK xxxxxxxxxxxxxxxxxxx  - gsmtp
9: 354 Go ahead xxxxxxxxxxxxxxxxxxx  - gsmtp
10: 250 2.0.0 OK xxxxxxxxxx xxxxxxxxxxxxxxxxxxx  - gsmtp
11: 221 2.0.0 closing connection xxxxxxxxxxxxxxxxxxx  - gsmtp

@guardrex
Copy link

One final example in case the mail server doesn't support TLS but does support SSL (port 465), such as GoDaddy 🚽 mail servers 💩.

using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.IO;
using System.Net.Security;

const string server = "smtpout.secureserver.net";
const int port = 465;
using (var client = new TcpClient(server, port)) {
    using (var stream = new SslStream(client.GetStream(), false)) {

        stream.AuthenticateAsClient(server);

        using (var reader = new StreamReader(stream))
        using (var writer = new StreamWriter(stream) { AutoFlush = true }) {
            outputString += "1: " + reader.ReadLine() + " ";

            writer.WriteLine("HELO " + server);
            outputString += "2: " + reader.ReadLine() + " ";

            writer.WriteLine("AUTH LOGIN");
            outputString += "3: " + reader.ReadLine() + " ";

            string username = "YOUR_ACCOUNT_EMAIL_ADDRESS";
            var plainTextBytes1 = System.Text.Encoding.UTF8.GetBytes(username);
            string base64Username = System.Convert.ToBase64String(plainTextBytes1);
            writer.WriteLine(base64Username);
            outputString += "4: " + reader.ReadLine() + " ";

            string password = "YOUR_ACCOUNT_PASSWORD";
            var plainTextBytes2 = System.Text.Encoding.UTF8.GetBytes(password);
            string base64Password = System.Convert.ToBase64String(plainTextBytes2);
            writer.WriteLine(base64Password);
            outputString += "5: " + reader.ReadLine() + " ";

            writer.WriteLine("MAIL FROM:<MESSAGE_FROM_EMAIL_ADDRESS>");
            outputString += "6: " + reader.ReadLine() + " ";

            writer.WriteLine("RCPT TO:<MESSAGE_TO_EMAIL_ADDRESS>");
            outputString += "7: " + reader.ReadLine() + " ";

            writer.WriteLine("DATA");
            outputString += "8: " + reader.ReadLine() + " ";

            writer.WriteLine("From: \"MESSAGE_FROM_NAME\" <MESSAGE_FROM_EMAIL_ADDRESS>");
            writer.WriteLine("To: \"MESSAGE_TO_NAME\" <MESSAGE_TO_EMAIL_ADDRESS>");
            writer.WriteLine("EMAIL_SUBJECT");
            // Leave one blank line after the subject
            writer.WriteLine("");
            // Start the message body here
            writer.WriteLine("Hello Luke,");
            writer.WriteLine("");
            writer.WriteLine("Cuz! You gotta try Beck's Sapphire! It ROCKS!");
            writer.WriteLine("");
            writer.WriteLine("Later,");
            writer.WriteLine("");
            writer.WriteLine("Luke");
            // End the message body by sending a period
            writer.WriteLine(".");
            outputString += "9: " + reader.ReadLine() + " ";

            writer.WriteLine("QUIT");
            outputString += "10: " + reader.ReadLine() + " ";
        }
    }
}

Outputs:

1: 220 p3plsmtpa12-09.prod.phx3.secureserver.net ESMTP
2: 250 p3plsmtpa12-09.prod.phx3.secureserver.net hello [xxx.xxx.xxx.xxx], secureserver.net
3: 334 xxxxxxxxxxxxxxx
4: 334 xxxxxxxxxxxxxxx
5: 235 ... authentication succeeded :: xxxxxxxxxxxxxxx
6: 250 <sender@sender_server.com> sender ok
7: 250 <recipient@recipient_server.com> recipient ok
8: 354 enter mail, end with "." on a line by itself
9: 250 xxxxxxxxxxxxxxx mail accepted for delivery
10: 221 p3plsmtpa12-09.prod.phx3.secureserver.net closing connection

@Petermarcu
Copy link
Member

As others have discovered, this may not be as straight forward to port as it may have first seemed. @SidharthNabar, can you speak to any plans that may be evolving around doing an official port of this as the rest of the networking stack moves onto Github?

@akoeplinger
Copy link
Member

I think porting https://github.com/jstedfast/MailKit (or at least the SMTP parts) might be way easier, since it already targets Windows 8.1 Universal, which afaik is a subset of .NET Core.

@SidharthNabar
Copy link

Yep - our team owns the networking APIs within .NET framework and System.Net.Mail is on our roadmap to bring to CoreCLR. Currently, we are working on some higher priority issues (including open-sourcing some other networking APIs that are supported in .NET Core). Unfortunately, we do not have a specific timeline right now.

As others have discovered, it will be challenging to move System.Net.Mail to CoreCLR since it has dependencies on the old managed networking stack (e.g. ServicePointManager) that is not yet supported in .NET Core. @akoeplinger is right - Windows 8.1 Universal .NET surface is a subset of .NET Core at least as far as networking is concerned, and MailKit may be a good short-term mitigation till we get System.Net.Mail to .NET Core.

@guardrex
Copy link

@SidharthNabar Thank you for the update. I'm happy to hear it's on the roadmap. In the meantime, my ugly little SMTP bits that I posted here are working fine and will tide me over.

@jstedfast
Copy link
Member

If anyone has any questions about MailKit, let me know, I'll be happy to answer any questions and provide any assistance that is needed.

@niemyjski
Copy link

@jstedfast I created an issue for this here :) jstedfast/MailKit#212

niemyjski referenced this issue in exceptionless/Exceptionless Jul 7, 2015
…iables #115

@ejsmith This will cover our use case, but can you review this.. There
really isn't a good solution right now as vnext doesn't support this...
https://github.com/dotnet/corefx/issues/1006
@AdmirR
Copy link

AdmirR commented Nov 20, 2015

(Regarding the solution proposed by @guardrex)
Note, some hosts may require a "." ending, instead of the proposed ". QUIT".

                    writer.WriteLine("\r\n.\r\n");
                    outputString += "9: " + reader.ReadLine() + " ";

Came across this issue when (self-hosting) a project of mine, it would send mail fine in a localhost (debugging) environment, but would time out in a production environment.

@guardrex
Copy link

@AdmirR Thanks for adding that note. The code I posted works with Google, GoDaddy, and hMailServer. You're right ... beyond that ... best wishes ... it's probably the Wild West when it comes to mail servers.

This is probably a good time to ask for an update on System.Net.Mail. @Petermarcu Is there any news on how its going with that effort? When we last left off, they said they had plans to do it, but they had no idea when they would get to it.

@guardrex
Copy link

guardrex commented Dec 2, 2015

@SidharthNabar

If possible, you might want to mark this "planned" or such on UserVoice ... just so folks don't get too upset about it being on the backlog for a little while. It might spare you a few death threats. 😃

http://aspnet.uservoice.com/forums/252111-asp-net-vnext/suggestions/7648077-corefx-should-include-system-net-mail

@SidharthNabar
Copy link

This is still on our backlog and is planned to be supported. We are still working on finishing up previously planned namespaces such as System.Net.Sockets, System.Net.Security and others, which System.Net.Mail will likely depend on anyway. If this is something you would be interested in contributing to, please comment here and we'd be happy to engage with you.

Thanks
Sid

@SidharthNabar
Copy link

Thanks @guardrex - I posted a reply on the UserVoice as well :-)

@jstedfast
Copy link
Member

For anyone interested in MailMessage / SmtpClient equivalent support, I've just finished porting MimeKit and MailKit over to CoreCLR.

I'll be cleaning up the code a bit more this weekend and expect to publish new nugets by the end of the weekend.

@guardrex
Copy link

guardrex commented Dec 5, 2015

... and as soon as this new System.Net.Sockets issue is worked out, I'll update the examples here if you just need a quick-and-dirty method.

@joeaudette
Copy link

for anyone who needs to send email on dnxcore50, @jstedfast just got his MailKit/MimeKit projects working jstedfast/MailKit#212

nugets are available now.

If you are as grateful for that news as I am please join me in sending a donation to his pledgie page http://www.pledgie.com/campaigns/29300

@SidharthNabar
Copy link

Thanks @jstedfast - Really appreciate your contribution here! We will continue to keep System.Net.Mail for .NET Core in our plans, but it's really nice to see .NET Core developers unblocked thanks to you :)

@jstedfast
Copy link
Member

Glad to help :)

@guardrex
Copy link

This is an update on using TcpClient and SslStream. I've had trouble talking to GoDaddy servers lately. Some 2012R2 boxes working and others refusing to play nice with GoDaddy servers (SCHANNEL and cipher suite problems perhaps). However, Gmail seems to be working. Here's the latest code for hitting up Gmail for mail forwarding on port 587. Keeping in mind, of course, this isn't production quality. It's just a prototype to show you the ropes. Enjoy!

[EDIT] Note: You may need to go into the Google Account security settings and allow less secure applications to use the account. You may also need to hit this page: https://accounts.google.com/DisplayUnlockCaptcha while logged in to the Google account, which I think disables captcha requirements for the application logging in (I think).

[EDIT] I'm updating the code to correctly fix a problem with the forwarded mail. The prior code I had here didn't have a proper "Reply-To" setup so that when you click to reply, it would fill the Gmail account address into the To field. With the "Reply-To" explicitly set to the original senders name and address, the To field in a message reply will be filled in correctly.

private async Task<string> SendMailOnPort587(
        Visitor visitor,
        string server,
        int port,
        string smtpServerAccountName,
        string smtpServerUsername,
        string smtpServerPassword,
        string destinationEmailAddress,
        string destinationEmailName,
        string destinationSubjectLine)
{
    string outputString = string.Empty;
    try
    {
        using (var client = new TcpClient()) {
            await client.ConnectAsync(server, port);
            using (var stream = client.GetStream())
            using (var reader = new StreamReader(stream))
            using (var writer = new StreamWriter(stream) { AutoFlush = true }) {
                outputString += "1: " + reader.ReadLine() + " ";

                writer.WriteLine("HELO " + server);
                outputString += "2: " + reader.ReadLine() + " ";

                writer.WriteLine("STARTTLS");
                outputString += "3: " + reader.ReadLine() + " ";

                using (var sslStream = new SslStream(client.GetStream(), false)) {

                    await sslStream.AuthenticateAsClientAsync(server);

                    using (var secureReader = new StreamReader(sslStream))
                    using (var secureWriter = new StreamWriter(sslStream) { AutoFlush = true }) {

                        secureWriter.WriteLine("AUTH LOGIN");
                        outputString += "4: " + secureReader.ReadLine() + " ";

                        string username = smtpServerUsername;
                        var plainTextBytes1 = System.Text.Encoding.UTF8.GetBytes(username);
                        string base64Username = System.Convert.ToBase64String(plainTextBytes1);
                        secureWriter.WriteLine(base64Username);
                        outputString += "5: " + secureReader.ReadLine() + " ";

                        string password = smtpServerPassword;
                        var plainTextBytes2 = System.Text.Encoding.UTF8.GetBytes(password);
                        string base64Password = System.Convert.ToBase64String(plainTextBytes2);
                        secureWriter.WriteLine(base64Password);
                        outputString += "6: " + secureReader.ReadLine() + " ";

                        secureWriter.WriteLine("MAIL FROM:<" + smtpServerUsername + ">");
                        outputString += "7: " + secureReader.ReadLine() + " ";

                        secureWriter.WriteLine("RCPT TO:<" + destinationEmailAddress + ">");
                        outputString += "8: " + secureReader.ReadLine() + " ";

                        secureWriter.WriteLine("DATA");
                        outputString += "9: " + secureReader.ReadLine() + " ";

                        secureWriter.WriteLine("From: \"" + smtpServerAccountName + "\" <" + smtpServerUsername + ">");
                        secureWriter.WriteLine("To: \"" + destinationEmailName + "\" <" + destinationEmailAddress + ">");
                        secureWriter.WriteLine("Subject: " + destinationSubjectLine);
                        secureWriter.WriteLine("Reply-To: \"" + visitor.Name + "\" <" + visitor.Email + ">");
                        secureWriter.WriteLine("");
                        secureWriter.WriteLine("Attn: " + visitor.Department);
                        secureWriter.WriteLine("");
                        secureWriter.WriteLine("Name: " + visitor.Name);
                        secureWriter.WriteLine("E-mail: " + visitor.Email);
                        secureWriter.WriteLine("Phone: " + visitor.Phone);
                        secureWriter.WriteLine("");
                        secureWriter.WriteLine(visitor.Message);
                        secureWriter.WriteLine("");
                        secureWriter.WriteLine("VIRUS Warning! DO NOT click links in E-mail messages!");
                        secureWriter.WriteLine("");
                        secureWriter.WriteLine(".");
                        outputString += "10: " + secureReader.ReadLine() + " ";

                        secureWriter.WriteLine("QUIT");
                        outputString += "11: " + secureReader.ReadLine() + " ";
                    }
                }
            }
        }
        serverReply = outputString;
    }
    catch (Exception ex)
    {
        serverReply = serverReply + " Exception: " + ex.Message + " outputString: " + outputString;
    }
    return serverReply;
}

serverReply if all goes well ...

1: 220 smtp.gmail.com ESMTP g81sm28380166pfj.1 - gsmtp 
2: 250 smtp.gmail.com at your service 
3: 220 2.0.0 Ready to start TLS 
4: 334 VXNxcx5hbWUx 
5: 334 xGFzc3dvxmx6 
6: 235 2.7.0 Accepted 
7: 250 2.1.0 OK g81sm28380166pfj.1 - gsmtp 
8: 250 2.1.5 OK g81sm28380166pfj.1 - gsmtp 
9: 354  Go ahead g81sm28380166pfj.1 - gsmtp 
10: 250 2.0.0 OK 1455403723 g81sm28380166pfj.1 - gsmtp 
11: 221 2.0.0 closing connection g81sm28380166pfj.1 - gsmtp 

@LorenzGardner
Copy link

Any update on inclusion of the the namesapce? I 'm guessing it won't make RC2 at this point.

@guardrex
Copy link

guardrex commented May 11, 2016

Correct ... it won't. I did put my extremely hacky barely prototype Email package up. It works well with Gmail servers. It's at least the basis of something that might tide some folks over to MailKit being updated for RC2 or the release of a new System.Net.Mail ... https://github.com/GuardRex/Email Moved to https://github.com/GuardRex/GuardRex.RexMail and https://www.nuget.org/packages/GuardRex.RexMail/

@davidsh
Copy link
Contributor

davidsh commented Sep 18, 2016

Closing this as dotnet/corefx#11792 will track this work.

@Serexx
Copy link

Serexx commented Jun 4, 2017

How anybody could contemplate building a web targeted development framework and consider packaged SMTP support as unnecessary is beyond me. Jus sayin.

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 2.0.0 milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Jan 7, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests