Łukasz Stelmach <l.stelmach@xxxxxxxxxxx> writes: > Enable sending patches to NNTP servers (Usenet, Gmane). > --- > > The patch implements support for sending messages to groups on NNTP serviers. Cute. A Perl guru might want to encapsulate the differences between $smtp and $nntp codepaths into two Perl modules, but it looks like a good starting point. > The patch does not (attempts not to) change the default behavior i.e. to use sendmail > and/or Net::SMTP. To use NNTP one needs to configure the server (see the help message) > and protocol ("--protocol nntp"). Then after giving --newsgroups the > message will be sent via NNTP. Like this one: > > perl git-send-email.perl --protocol nntp --newsgroups gmane.comp.version-control.git --nntp-server news.gmane.org 0001-send-email-support-NNTP.patch > > git-send-email.perl | 156 +++++++++++++++++++++++++++++++++++++++++---------- > 1 file changed, 125 insertions(+), 31 deletions(-) > > diff --git a/git-send-email.perl b/git-send-email.perl > index bd13cc8..0356635 100755 > --- a/git-send-email.perl > +++ b/git-send-email.perl > @@ -54,12 +54,14 @@ git send-email [options] <file | directory | rev-list options > > --[no-]bcc <str> * Email Bcc: > --subject <str> * Email "Subject:" > --in-reply-to <str> * Email "In-Reply-To:" > + --newsgroups <str> * NNTP Newsgroups: > --[no-]annotate * Review each patch that will be sent in an editor. > --compose * Open an editor for introduction. > --compose-encoding <str> * Encoding to assume for introduction. > --8bit-encoding <str> * Encoding to assume 8bit mails if undeclared > > Sending: > + --protocol <str> * 'smtp' or 'nntp'. Default 'smtp'. > --envelope-sender <str> * Email envelope sender. > --smtp-server <str:int> * Outgoing SMTP server to use. The port > is optional. Default 'localhost'. > @@ -71,6 +73,12 @@ git send-email [options] <file | directory | rev-list options > > --smtp-ssl * Deprecated. Use '--smtp-encryption ssl'. > --smtp-domain <str> * The domain name sent to HELO/EHLO handshake > --smtp-debug <0|1> * Disable, enable Net::SMTP debug. > + --nntp-server <str:int> * Outgoing NNTP server to use. The port > + is optional. > + --nntp-server-port <int> * Outgoing NNTP server port. > + --nntp-user <str> * Username for NNTP AUTHINFO. > + --nntp-pass <str> * Password for NNTP AUTHINFO. > + --nntp-debug <0|1> * Disable, enable Net::NNTP debug. > > Automating: > --identity <str> * Use the sendemail.<id> options. > @@ -138,12 +146,14 @@ sub format_2822_time { > my $have_email_valid = eval { require Email::Valid; 1 }; > my $have_mail_address = eval { require Mail::Address; 1 }; > my $smtp; > +my $nntp; > my $auth; > > # Variables we fill in automatically, or via prompting: > my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh, > $initial_reply_to,$initial_subject,@files, > - $author,$sender,$smtp_authpass,$annotate,$compose,$time); > + $author,$sender,$smtp_authpass,$annotate,$compose,$time, > + @initial_newsgroups, @newsgroups); > > my $envelope_sender; > > @@ -192,7 +202,9 @@ sub do_edit { > > # Variables with corresponding config settings > my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc); > -my ($to_cmd, $cc_cmd); > +my ($to_cmd, $cc_cmd, $newsgroups_cmd); > +my ($email_protocol) = 'smtp'; > +my ($nntp_server, $nntp_server_port, $nntp_authuser, $nntp_authpass); > my ($smtp_server, $smtp_server_port, @smtp_server_options); > my ($smtp_authuser, $smtp_encryption); > my ($identity, $aliasfiletype, @alias_files, $smtp_domain); > @@ -202,6 +214,7 @@ my ($auto_8bit_encoding); > my ($compose_encoding); > > my ($debug_net_smtp) = 0; # Net::SMTP, see send_message() > +my ($debug_net_nntp) = 0; # Net::NNTP, see send_message() > > my $not_set_by_user = "true but not set by the user"; > > @@ -235,6 +248,13 @@ my %config_settings = ( > "from" => \$sender, > "assume8bitencoding" => \$auto_8bit_encoding, > "composeencoding" => \$compose_encoding, > + "nntpserver" => \$nntp_server, > + "nntpserverport" => \$nntp_server_port, > + "nntpuser" => \$nntp_authuser, > + "nntppass" => \$nntp_authpass, > + "protocol" => \$email_protocol, > + "newsgroups" => \@initial_newsgroups, > + "newsgroupscmd" => \$newsgroups_cmd, > ); > > my %config_path_settings = ( > @@ -291,6 +311,7 @@ my $rc = GetOptions("h" => \$help, > "to-cmd=s" => \$to_cmd, > "no-to" => \$no_to, > "cc=s" => \@initial_cc, > + "cc-cmd=s" => \$cc_cmd, > "no-cc" => \$no_cc, > "bcc=s" => \@bcclist, > "no-bcc" => \$no_bcc, > @@ -304,11 +325,18 @@ my $rc = GetOptions("h" => \$help, > "smtp-encryption=s" => \$smtp_encryption, > "smtp-debug:i" => \$debug_net_smtp, > "smtp-domain:s" => \$smtp_domain, > + "newsgroups=s" => \@initial_newsgroups, > + "newsgroups-cmd" => \$newsgroups_cmd, > + "nntp-server=s" => \$nntp_server, > + "nntp-server-port=s" => \$nntp_server_port, > + "nntp-user=s" => \$nntp_authuser, > + "nntp-pass:s" => \$nntp_authpass, > + "nntp-debug:i" => \$debug_net_nntp, > + "protocol=s" => \$email_protocol, > "identity=s" => \$identity, > "annotate!" => \$annotate, > "compose" => \$compose, > "quiet" => \$quiet, > - "cc-cmd=s" => \$cc_cmd, > "suppress-from!" => \$suppress_from, > "suppress-cc=s" => \@suppress_cc, > "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc, > @@ -390,6 +418,24 @@ foreach my $setting (values %config_bool_settings) { > ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]})); > } > > +unless ($email_protocol eq 'smtp' || $email_protocol eq 'nntp') { > + die "Unsupported protocol: $email_protocol"; > +} > + > +# Transport specific setup > +my ($email_authuser, $email_authpass); > +if ($email_protocol eq 'nntp') { > + $email_authuser = $nntp_authuser; > + $email_authuser = $nntp_authuser; > + @initial_to = @initial_cc = @bcclist = (); > + $to_cmd = $cc_cmd = undef; > + $no_cc = $no_bcc = 1; > +} else { > + $email_authuser = $smtp_authuser; > + $email_authpass = $smtp_authpass; > + $newsgroups_cmd = undef; > +} > + > # 'default' encryption is none -- this only prevents a warning > $smtp_encryption = '' unless (defined $smtp_encryption); > > @@ -668,8 +714,8 @@ EOT > } elsif (/^From:\s*(.+)\s*$/i) { > $sender = $1; > next; > - } elsif (/^(?:To|Cc|Bcc):/i) { > - print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n"; > + } elsif (/^(?:To|Cc|Bcc|Newsgroup):/i) { > + print "To/Cc/Bcc/Newsgroup fields are not interpreted yet, they have been ignored\n"; > next; > } > print $c2 $_; > @@ -761,12 +807,21 @@ if (!defined $sender) { > } > > my $prompting = 0; > -if (!@initial_to && !defined $to_cmd) { > + > +if ($email_protocol eq 'smtp' && !@initial_to && !defined $to_cmd) { > my $to = ask("Who should the emails be sent to (if any)? ", > default => "", > valid_re => qr/\@.*\./, confirm_only => 1); > push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later > $prompting++; > +} elsif ($email_protocol eq 'nntp' && > + !@initial_newsgroups && > + !defined $newsgroups_cmd) { > + my $newsgroup = ask("Which newsgroups should the message be sent to (if any)? ", > + default => "", > + valid_re => qr/[\x20-\x7f]+/, confirm_only => 1); > + push @initial_newsgroups, $newsgroup if defined $newsgroup; # sanitized/validated later > + $prompting++; > } > > sub expand_aliases { > @@ -802,7 +857,7 @@ if (defined $initial_reply_to) { > $initial_reply_to = "<$initial_reply_to>" if $initial_reply_to ne ''; > } > > -if (!defined $smtp_server) { > +if ($email_protocol eq 'smtp' && !defined $smtp_server) { > foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) { > if (-x $_) { > $smtp_server = $_; > @@ -1048,41 +1103,56 @@ sub maildomain { > return maildomain_net() || maildomain_mta() || 'localhost.localdomain'; > } > > -sub smtp_host_string { > - if (defined $smtp_server_port) { > - return "$smtp_server:$smtp_server_port"; > +sub email_host_string { > + if ($email_protocol eq 'nntp') { > + if (defined $nntp_server_port) { > + return "$nntp_server:$nntp_server_port"; > + } else { > + return $nntp_server; > + } > + > } else { > - return $smtp_server; > + if (defined $smtp_server_port) { > + return "$smtp_server:$smtp_server_port"; > + } else { > + return $smtp_server; > + } > } > } > > # Returns 1 if authentication succeeded or was not necessary > # (smtp_user was not specified), and 0 otherwise. > > -sub smtp_auth_maybe { > - if (!defined $smtp_authuser || $auth) { > +sub email_auth_maybe { > + if (!defined $email_authuser || $auth) { > return 1; > } > > # Workaround AUTH PLAIN/LOGIN interaction defect > # with Authen::SASL::Cyrus > - eval { > - require Authen::SASL; > - Authen::SASL->import(qw(Perl)); > - }; > + if ($email_protocol eq 'smtp') { > + eval { > + require Authen::SASL; > + Authen::SASL->import(qw(Perl)); > + }; > + } > > # TODO: Authentication may fail not because credentials were > # invalid but due to other reasons, in which we should not > # reject credentials. > $auth = Git::credential({ > - 'protocol' => 'smtp', > - 'host' => smtp_host_string(), > - 'username' => $smtp_authuser, > + 'protocol' => $email_protocol, > + 'host' => email_host_string(), > + 'username' => $email_authuser, > # if there's no password, "git credential fill" will > # give us one, otherwise it'll just pass this one. > - 'password' => $smtp_authpass > + 'password' => $email_authpass > }, sub { > my $cred = shift; > + if ($email_protocol eq 'nntp') { > + return !!$nntp->authinfo($cred->{'username'}, > + $cred->{'password'}); > + } > return !!$smtp->auth($cred->{'username'}, $cred->{'password'}); > }); > > @@ -1099,7 +1169,7 @@ sub send_message { > not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients > } > @cc); > - my $to = join (",\n\t", @recipients); > + my $to = join (",\n\t", (($email_protocol eq 'nntp') ? @newsgroups : @recipients)); > @recipients = unique_email_list(@recipients,@cc,@bcclist); > @recipients = (map { extract_valid_address_or_die($_) } @recipients); > my $date = format_2822_time($time++); > @@ -1117,12 +1187,17 @@ sub send_message { > make_message_id() unless defined($message_id); > > my $header = "From: $sanitized_sender > -To: $to${ccline} > Subject: $subject > Date: $date > Message-Id: $message_id > X-Mailer: git-send-email $gitversion > "; > + if ($email_protocol eq 'nntp') { > + $header = "Newsgroups: $to\n" . $header; > + } else { > + $header = "To: $to${ccline}\n" . $header; > + } > + > if ($reply_to) { > > $header .= "In-Reply-To: $reply_to\n"; > @@ -1174,6 +1249,18 @@ X-Mailer: git-send-email $gitversion > > if ($dry_run) { > # We don't want to send the email. > + } elsif ($email_protocol eq 'nntp') { > + if (!defined $nntp_server) { > + die "The requires NNTP server is not properly defined." > + } > + require Net::NNTP; > + $nntp = Net::NNTP->new(email_host_string(), > + Debug => $debug_net_nntp); > + email_auth_maybe or die $nntp->message; > + $nntp->post or die $nntp->message; > + $nntp->datasend("$header\n$message") or die $nntp->message; > + $nntp->dataend() or die $nntp->message; > + $nntp->code eq "240" or die "Failed to send $subject\n".$nntp->message; > } elsif ($smtp_server =~ m#^/#) { > my $pid = open my $sm, '|-'; > defined $pid or die $!; > @@ -1195,11 +1282,10 @@ X-Mailer: git-send-email $gitversion > $smtp ||= Net::SMTP::SSL->new($smtp_server, > Hello => $smtp_domain, > Port => $smtp_server_port); > - } > - else { > + } else { > require Net::SMTP; > $smtp_domain ||= maildomain(); > - $smtp ||= Net::SMTP->new(smtp_host_string(), > + $smtp ||= Net::SMTP->new(email_host_string(), > Hello => $smtp_domain, > Debug => $debug_net_smtp); > if ($smtp_encryption eq 'tls' && $smtp) { > @@ -1227,7 +1313,7 @@ X-Mailer: git-send-email $gitversion > defined $smtp_server_port ? " port=$smtp_server_port" : ""; > } > > - smtp_auth_maybe or die $smtp->message; > + email_auth_maybe or die $smtp->message; > > $smtp->mail( $raw_from ) or die $smtp->message; > $smtp->to( @recipients ) or die $smtp->message; > @@ -1240,7 +1326,9 @@ X-Mailer: git-send-email $gitversion > printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject); > } else { > print (($dry_run ? "Dry-" : "")."OK. Log says:\n"); > - if ($smtp_server !~ m#^/#) { > + if ($email_protocol eq 'nntp') { > + print "Server: $nntp_server\n"; > + } elsif ($smtp_server !~ m#^/#) { > print "Server: $smtp_server\n"; > print "MAIL FROM:<$raw_from>\n"; > foreach my $entry (@recipients) { > @@ -1250,9 +1338,10 @@ X-Mailer: git-send-email $gitversion > print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n"; > } > print $header, "\n"; > - if ($smtp) { > - print "Result: ", $smtp->code, ' ', > - ($smtp->message =~ /\n([^\n]+\n)$/s), "\n"; > + my $transport = $nntp || $smtp; > + if ($transport) { > + print "Result: ", $transport->code, ' ', > + ($transport->message =~ /\n([^\n]+\n)$/s), "\n"; > } else { > print "Result: OK\n"; > } > @@ -1383,6 +1472,9 @@ foreach my $t (@files) { > } > close $fh; > > + push @newsgroups, recipients_cmd("newsgroups-cmd", "newsgroups", > + $newsgroups_cmd, $t) > + if defined $newsgroups_cmd; > push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t) > if defined $to_cmd; > push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t) > @@ -1430,6 +1522,7 @@ foreach my $t (@files) { > @to = validate_address_list(sanitize_address_list(@to)); > @cc = validate_address_list(sanitize_address_list(@cc)); > > + @newsgroups = (@initial_newsgroups, @newsgroups); > @to = (@initial_to, @to); > @cc = (@initial_cc, @cc); > > @@ -1479,6 +1572,7 @@ sub cleanup_compose_files { > } > > $smtp->quit if $smtp; > +$nntp->quit if $nntp; > > sub unique_email_list { > my %seen; -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html