aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2025-04-16 13:54:19 -0700
committerJunio C Hamano <gitster@pobox.com>2025-04-16 13:54:19 -0700
commit4c58159add709464440fa8029b56cf0152b0003a (patch)
tree4a8ceba2ffda43d08240ce1005d19c6ace08ff09
parent01a6e244f951c0372ef541069570bb6bcfefcca3 (diff)
parent1ac402cdf3d5a82d3ba8943a452e84f54f398522 (diff)
downloadgit-4c58159add709464440fa8029b56cf0152b0003a.tar.xz
Merge branch 'zy/send-email-error-handling'
Auth-related (and unrelated) error handling in send-email has been made more robust. * zy/send-email-error-handling: send-email: finer-grained SMTP error handling send-email: capture errors in an eval {} block
-rwxr-xr-xgit-send-email.perl65
1 files changed, 51 insertions, 14 deletions
diff --git a/git-send-email.perl b/git-send-email.perl
index 798d59b84f..1f613fa979 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1419,7 +1419,7 @@ sub smtp_auth_maybe {
die "invalid smtp auth: '${smtp_auth}'";
}
- # TODO: Authentication may fail not because credentials were
+ # Authentication may fail not because credentials were
# invalid but due to other reasons, in which we should not
# reject credentials.
$auth = Git::credential({
@@ -1431,26 +1431,63 @@ sub smtp_auth_maybe {
'password' => $smtp_authpass
}, sub {
my $cred = shift;
+ my $result;
+ my $error;
- if ($smtp_auth) {
- my $sasl = Authen::SASL->new(
- mechanism => $smtp_auth,
- callback => {
- user => $cred->{'username'},
- pass => $cred->{'password'},
- authname => $cred->{'username'},
- }
- );
-
- return !!$smtp->auth($sasl);
- }
+ # catch all SMTP auth error in a unified eval block
+ eval {
+ if ($smtp_auth) {
+ my $sasl = Authen::SASL->new(
+ mechanism => $smtp_auth,
+ callback => {
+ user => $cred->{'username'},
+ pass => $cred->{'password'},
+ authname => $cred->{'username'},
+ }
+ );
+ $result = $smtp->auth($sasl);
+ } else {
+ $result = $smtp->auth($cred->{'username'}, $cred->{'password'});
+ }
+ 1; # ensure true value is returned if no exception is thrown
+ } or do {
+ $error = $@ || 'Unknown error';
+ };
- return !!$smtp->auth($cred->{'username'}, $cred->{'password'});
+ return ($error
+ ? handle_smtp_error($error)
+ : ($result ? 1 : 0));
});
return $auth;
}
+sub handle_smtp_error {
+ my ($error) = @_;
+
+ # Parse SMTP status code from error message in:
+ # https://www.rfc-editor.org/rfc/rfc5321.html
+ if ($error =~ /\b(\d{3})\b/) {
+ my $status_code = $1;
+ if ($status_code =~ /^4/) {
+ # 4yz: Transient Negative Completion reply
+ warn "SMTP transient error (status code $status_code): $error";
+ return 1;
+ } elsif ($status_code =~ /^5/) {
+ # 5yz: Permanent Negative Completion reply
+ warn "SMTP permanent error (status code $status_code): $error";
+ return 0;
+ }
+ # If no recognized status code is found, treat as transient error
+ warn "SMTP unknown error: $error. Treating as transient failure.";
+ return 1;
+ }
+
+ # If no status code is found, treat as transient error
+ warn "SMTP generic error: $error";
+ return 1;
+}
+
sub ssl_verify_params {
eval {
require IO::Socket::SSL;