threads / rfc / 63519

RFC patchsend-mail: add support for Microsoft Graph API

Subject: [PATCH RFC] send-mail: add support for Microsoft Graph API

## tl;dr

5 messages between May 25, 2025 and May 26, 2025. Diffs are folded; open one to read it.

replies: 4people: 3as markdown or json

Aditya Garg· May 25, 2025, 11:06 UTC · lore

Apart from SMTP, Microsoft also provides a REST API, branded as Microsoft Graph for sending mails. Upon testing a bit, I have found a few benefits over SMTP. Firstly, SMTP servers of Microsoft are kinda slow. On an average, initialising the SMTP server even on a fast internet connection takes around 8-10 seconds with send-email. Once initialised, subsequent messages sometimes also face delays, taking around 3-5 seconds per message, and other times they are sent almost instantaneously. Secondly, their SMTP server does not respect the Message-ID specified by the user and replaces it with their own generated string.

Microsoft Graph API solves both these problems. It is extremely fast, taking around 1 second to send a series of 5 patches, and also respects the Message-ID specified by the user.

After this patch, users can use the graph API by having their config as:

[sendemail]
	useMSGraph = true
	MSGraphUser = yourname@outlook.com

The API requires an OAuth2.0 access token for authentication, and users can either generate them manually, or use a credential helper

TODO: I still need to write the documentation for this feature, but I wanted to get the code reviewed first.

PS: This patch is sent using MS Graph. Note the fixed Message-ID ;)
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
 git-send-email.perl | 71 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 67 insertions(+), 4 deletions(-)
Show changes to git-send-email.perl +67 −4
diff --git a/git-send-email.perl b/git-send-email.perl
index b09251c4fc..edb96c7e1a 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -73,7 +73,10 @@ sub usage {
     --no-smtp-auth                 * Disable SMTP authentication. Shorthand for
                                      `--smtp-auth=none`
     --smtp-debug            <0|1>  * Disable, enable Net::SMTP debug.
-
+    --[no-]use-msgraph      <str>  * Use Microsoft Graph API instead of SMTP.
+    --msgraph-user          <str>  * Username in case an email API is used.
+                                     If not specified, the from address is used.
+    --msgraph-token         <str>  * OAuth2.0 access token for the email API.
     --batch-size            <int>  * send max <int> message per connection.
     --relogin-delay         <int>  * delay <int> seconds between two successive login.
                                      This option can only be used with --batch-size
@@ -201,7 +204,7 @@ sub format_2822_time {
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@xh,$envelope_sender,
 	$initial_in_reply_to,$reply_to,$initial_subject,@files,
-	$author,$sender,$smtp_authpass,$annotate,$compose,$time);
+	$author,$sender,$smtp_authpass,$annotate,$compose,$time,$msgraph_token);
 # Things we either get from config, *or* are overridden on the
 # command-line.
 my ($no_cc, $no_to, $no_bcc, $no_identity, $no_header_cmd);
@@ -283,6 +286,7 @@ sub do_edit {
 my ($compose_encoding);
 my ($sendmail_cmd);
 my ($mailmap_file, $mailmap_blob);
+my ($msgraph_user);
 # Variables with corresponding config settings & hardcoded defaults
 my ($debug_net_smtp) = 0;		# Net::SMTP, see send_message()
 my $thread = 1;
@@ -293,6 +297,7 @@ sub do_edit {
 my $target_xfer_encoding = 'auto';
 my $forbid_sendmail_variables = 1;
 my $outlook_id_fix = 'auto';
+my $use_msgraph = 0;
 
 my %config_bool_settings = (
     "thread" => \$thread,
@@ -309,6 +314,7 @@ sub do_edit {
     "forbidsendmailvariables" => \$forbid_sendmail_variables,
     "mailmap" => \$mailmap,
     "outlookidfix" => \$outlook_id_fix,
+    "usemsgraph" => \$use_msgraph,
 );
 
 my %config_settings = (
@@ -337,6 +343,8 @@ sub do_edit {
     "composeencoding" => \$compose_encoding,
     "transferencoding" => \$target_xfer_encoding,
     "sendmailcmd" => \$sendmail_cmd,
+    "msgraphuser" => \$msgraph_user,
+    "msgraphtoken" => \$msgraph_token,
 );
 
 my %config_path_settings = (
@@ -556,6 +564,9 @@ sub config_regexp {
 		    "git-completion-helper" => \$git_completion_helper,
 		    "v=s" => \$reroll_count,
 		    "outlook-id-fix!" => \$outlook_id_fix,
+		    "use-msgraph!" => \$use_msgraph,
+		    "msgraph-user=s" => \$msgraph_user,
+		    "msgraph-token:s" => \$msgraph_token,
 );
 $rc = GetOptions(%options);
 
@@ -1095,7 +1106,7 @@ sub expand_one_alias {
 	$reply_to = sanitize_address($reply_to);
 }
 
-if (!defined $sendmail_cmd && !defined $smtp_server) {
+if (!defined $sendmail_cmd && !defined $smtp_server && !$use_msgraph) {
 	my @sendmail_paths = qw( /usr/sbin/sendmail /usr/lib/sendmail );
 	push @sendmail_paths, map {"$_/sendmail"} split /:/, $ENV{PATH};
 	foreach (@sendmail_paths) {
@@ -1603,6 +1614,7 @@ sub is_outlook {
 sub send_message {
 	my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header();
 	my @recipients = @$recipients_ref;
+	my $msgraph_response_code;
 
 	my @sendmail_parameters = ('-i', @recipients);
 	my $raw_from = $sender;
@@ -1680,6 +1692,52 @@ sub send_message {
 		}
 		print $sm "$header\n$message";
 		close $sm or die $!;
+	} elsif ($use_msgraph) {
+		# Use Microsoft Graph API
+		# https://learn.microsoft.com/en-us/graph/api/user-sendmail
+		my $auth_api;
+		if (!defined $msgraph_user) {
+			$msgraph_user = (defined $smtp_authuser
+				? $smtp_authuser
+				: $raw_from
+			);
+		}
+		require MIME::Base64;
+		require HTTP::Tiny;
+		$auth_api = Git::credential({
+			'protocol' => 'https',
+			'host' => 'graph.microsoft.com',
+			'username' => $msgraph_user,
+			'password' => $msgraph_token,
+		}, sub {
+			my $cred = shift;
+			$msgraph_token = $cred->{'password'};
+		});
+		my $email_url = "https://graph.microsoft.com/v1.0/users/$msgraph_user/sendMail";
+		my $real_message = "$header\n$message\n";
+		# Convert LF to CRLF
+		unless ($real_message =~ /\r\n/) {
+			$real_message =~ s/\r?\n/\r\n/g;
+		}
+		# The API requires the message to be base64 encoded if sending in MIME format.
+		my $encoded_message = MIME::Base64::encode_base64($real_message, "");
+		my $http = HTTP::Tiny->new(
+			default_headers => {
+				'Authorization' => "Bearer $msgraph_token",
+				'Content-Type'  => 'text/plain',
+			},
+		);
+		my $response = $http->post($email_url, {
+			content => $encoded_message,
+		});
+
+
+		if (!$response->{success}) {
+			die sprintf(__("Failed to send email: %s\nResponse: %s"),
+				$response->{status}, $response->{content});
+		} else {
+			$msgraph_response_code = $response->{status};
+		}
 	} else {
 
 		if (!defined $smtp_server) {
@@ -1790,12 +1848,15 @@ sub send_message {
 	} else {
 		print($dry_run ? __("Dry-OK. Log says:") : __("OK. Log says:"));
 		print "\n";
-		if (!defined $sendmail_cmd && !file_name_is_absolute($smtp_server)) {
+		if (!defined $sendmail_cmd && !file_name_is_absolute($smtp_server)
+		   && !$use_msgraph) {
 			print "Server: $smtp_server\n";
 			print "MAIL FROM:<$raw_from>\n";
 			foreach my $entry (@recipients) {
 			    print "RCPT TO:<$entry>\n";
 			}
+		} elsif ($use_msgraph) {
+			print "Email API: Microsoft Graph API\n";
 		} else {
 			my $sm;
 			if (defined $sendmail_cmd) {
@@ -1810,6 +1871,8 @@ sub send_message {
 		if ($smtp) {
 			print __("Result: "), $smtp->code, ' ',
 				($smtp->message =~ /\n([^\n]+\n)$/s);
+		} elsif (defined $msgraph_response_code) {
+			print __("Result: "), $msgraph_response_code;
 		} else {
 			print __("Result: OK");
 		}
-- 
2.49.0
Aditya Garg· May 25, 2025, 11:22 UTC · re: Aditya Garg · lore

Re: [PATCH RFC] send-mail: add support for Microsoft Graph API

On 25/05/25 4:34 pm, Aditya Garg wrote:
Show 24 quoted lines
> Apart from SMTP, Microsoft also provides a REST API, branded as
> Microsoft Graph for sending mails. Upon testing a bit, I have
> found a few benefits over SMTP. Firstly, SMTP servers of Microsoft
> are kinda slow. On an average, initialising the SMTP server even
> on a fast internet connection takes around 8-10 seconds with
> send-email. Once initialised, subsequent messages sometimes also
> face delays, taking around 3-5 seconds per message, and other
> times they are sent almost instantaneously. Secondly, their SMTP
> server does not respect the Message-ID specified by the user and
> replaces it with their own generated string.
> 
> Microsoft Graph API solves both these problems. It is extremely
> fast, taking around 1 second to send a series of 5 patches, and
> also respects the Message-ID specified by the user.
> 
> After this patch, users can use the graph API by having their
> config as:
> 
> [sendemail]
> 	useMSGraph = true
> 	MSGraphUser = yourname@outlook.com
> 
> The API requires an OAuth2.0 access token for authentication, and
> users can either generate them manually, or use a credential helper
Also, if someone is interested in testing, you can use a small credentialhelper for msgraph here:
https://github.com/AdityaGarg8/git-credential-email/tree/msgraph
brian m. carlson· May 25, 2025, 20:24 UTC · re: Aditya Garg · lore

Re: [PATCH RFC] send-mail: add support for Microsoft Graph API

On 2025-05-25 at 11:06:32, Aditya Garg wrote:
Show 14 quoted lines
> Apart from SMTP, Microsoft also provides a REST API, branded as
> Microsoft Graph for sending mails. Upon testing a bit, I have
> found a few benefits over SMTP. Firstly, SMTP servers of Microsoft
> are kinda slow. On an average, initialising the SMTP server even
> on a fast internet connection takes around 8-10 seconds with
> send-email. Once initialised, subsequent messages sometimes also
> face delays, taking around 3-5 seconds per message, and other
> times they are sent almost instantaneously. Secondly, their SMTP
> server does not respect the Message-ID specified by the user and
> replaces it with their own generated string.
> 
> Microsoft Graph API solves both these problems. It is extremely
> fast, taking around 1 second to send a series of 5 patches, and
> also respects the Message-ID specified by the user.

I don't think we should be adding support for proprietary methods of sending mail. There are two standard methods, which are SMTP and the sendmail binary, that are well known, well understood, and widely deployed.

It is well known that Microsoft has invented a lot of proprietary Outlook-specific formats for messages, such as TNEF and MAPI, which we should also not support because they are poorly defined and not specified in an open standard. I would also be opposed to sending mail in formats or using methods that are proprietary to Google, Amazon, or any other provider, since we already have clear specifications on how mail is to be sent that precede this project's existence by some time.

I understand that the Outlook servers may be slow and inefficient as well as modify the Message-ID, but that's ultimately a problem you need to address with them and get them to fix. I should note that I don't have that problem: I run my own Postfix and Dovecot servers which are very speedy, and I feel confident any other provider could do so as well. I also have never had that problem sending mail through Google's SMTP server at work.

If this REST API approach is well defined by an RFC or some other open specification process that the public can participate in and deployed among a large number of providers, including several open source implementations, then we might consider adopting it.

-- 
brian m. carlson (they/them)
Toronto, Ontario, CA
Drew DeVault· May 26, 2025, 01:36 UTC · re: brian m. carlson · lore

Re: [PATCH RFC] send-mail: add support for Microsoft Graph API

I'm inclined to agree that it would be less than ideal to add first-class support for proprietary protocols to git send-email. Some kind of local SMTP => Graph API bridge is probably a better solution, assuming Microsoft won't listen to reason (nor your boss, when you ask them to switch away from Outlook).

Aditya Garg· May 26, 2025, 03:38 UTC · re: brian m. carlson · lore

Re: [PATCH RFC] send-mail: add support for Microsoft Graph API

Show 43 quoted lines
> On 26 May 2025, at 1:55 AM, brian m. carlson <sandals@crustytoothpaste.net> wrote:
> 
> On 2025-05-25 at 11:06:32, Aditya Garg wrote:
>> Apart from SMTP, Microsoft also provides a REST API, branded as
>> Microsoft Graph for sending mails. Upon testing a bit, I have
>> found a few benefits over SMTP. Firstly, SMTP servers of Microsoft
>> are kinda slow. On an average, initialising the SMTP server even
>> on a fast internet connection takes around 8-10 seconds with
>> send-email. Once initialised, subsequent messages sometimes also
>> face delays, taking around 3-5 seconds per message, and other
>> times they are sent almost instantaneously. Secondly, their SMTP
>> server does not respect the Message-ID specified by the user and
>> replaces it with their own generated string.
>> 
>> Microsoft Graph API solves both these problems. It is extremely
>> fast, taking around 1 second to send a series of 5 patches, and
>> also respects the Message-ID specified by the user.
> 
> I don't think we should be adding support for proprietary methods of
> sending mail.  There are two standard methods, which are SMTP and the
> sendmail binary, that are well known, well understood, and widely
> deployed.
> 
> It is well known that Microsoft has invented a lot of proprietary
> Outlook-specific formats for messages, such as TNEF and MAPI, which we
> should also not support because they are poorly defined and not
> specified in an open standard.  I would also be opposed to sending mail
> in formats or using methods that are proprietary to Google, Amazon, or
> any other provider, since we already have clear specifications on how
> mail is to be sent that precede this project's existence by some time.
> 
> I understand that the Outlook servers may be slow and inefficient as
> well as modify the Message-ID, but that's ultimately a problem you need
> to address with them and get them to fix.  I should note that I don't
> have that problem: I run my own Postfix and Dovecot servers which are
> very speedy, and I feel confident any other provider could do so as
> well.  I also have never had that problem sending mail through Google's
> SMTP server at work.
> 
> If this REST API approach is well defined by an RFC or some other open
> specification process that the public can participate in and deployed
> among a large number of providers, including several open source
> implementations, then we might consider adopting it.

Fair, I was thinking proprietary protocols might not be accepted upstream. I think its best to use an SMTP bridge, or use the sendmail-cmd like capabilities of send-email.

← back to recent threads