使用Bugzilla,你肯定會遇到的坑。
阿新 • • 發佈:2018-04-14
bugzilla smtp ssl mail perl
最近和幾個朋友一起做用戶態協議棧開源項目 NtyTcp ,純業余愛好,個人情懷。剛剛開始做有好多的Bug,故搭建了一個Bugzilla,用來上傳Bug的。
有幾個開發朋友想在 bug.ntytcp.com提交bug,不能註冊,因為不能發郵件。至於為什麽不能發送郵件?因為阿裏雲把25端口封了,使用smtp不能發送。
使用465端口發送,發現發不出來,又找不到原因。硬著頭皮比Bugzilla的源碼看了一下。可恨的是自己並沒有寫過perl代碼。
於是用了兩天時間連學帶看就把bugzilla的源碼看了一遍。然後把bugzilla郵件發送的部分重寫了一下。先把bugzilla郵件發送部分貼出來。
bugzilla/mailer.pm
sub MessageToMTA { my ($msg, $send_now) = (@_); my $method = Bugzilla->params->{'mail_delivery_method'}; return if $method eq 'None'; if (Bugzilla->params->{'use_mailer_queue'} && ! $send_now && ! Bugzilla->dbh->bz_in_transaction() ) { Bugzilla->job_queue->insert('send_mail', { msg => $msg }); return; } my $dbh = Bugzilla->dbh; my $email = ref($msg) ? $msg : Bugzilla::MIME->new($msg); # If we're called from within a transaction, we don't want to send the # email immediately, in case the transaction is rolled back. Instead we # insert it into the mail_staging table, and bz_commit_transaction calls # send_staged_mail() after the transaction is committed. if (! $send_now && $dbh->bz_in_transaction()) { # The e-mail string may contain tainted values. my $string = $email->as_string; trick_taint($string); my $sth = $dbh->prepare("INSERT INTO mail_staging (message) VALUES (?)"); $sth->bind_param(1, $string, $dbh->BLOB_TYPE); $sth->execute; return; } my $from = $email->header('From'); my $hostname; my $transport; if ($method eq "Sendmail") { if (ON_WINDOWS) { $transport = Bugzilla::Sender::Transport::Sendmail->new({ sendmail => SENDMAIL_EXE }); } else { $transport = Bugzilla::Sender::Transport::Sendmail->new(); } } else { # Sendmail will automatically append our hostname to the From # address, but other mailers won't. my $urlbase = Bugzilla->params->{'urlbase'}; $urlbase =~ m|//([^:/]+)[:/]?|; $hostname = $1 || 'localhost'; $from .= "\@$hostname" if $from !~ /@/; $email->header_set('From', $from); # Sendmail adds a Date: header also, but others may not. if (!defined $email->header('Date')) { $email->header_set('Date', time2str("%a, %d %b %Y %T %z", time())); } } if ($method eq "SMTP") { my ($host, $port) = split(/:/, Bugzilla->params->{'smtpserver'}, 2); $transport = Bugzilla->request_cache->{smtp} //= Email::Sender::Transport::SMTP::Persistent->new({ host => $host, defined($port) ? (port => $port) : (), sasl_username => Bugzilla->params->{'smtp_username'}, sasl_password => Bugzilla->params->{'smtp_password'}, helo => $hostname, ssl => Bugzilla->params->{'smtp_ssl'}, debug => Bugzilla->params->{'smtp_debug'} }); } Bugzilla::Hook::process('mailer_before_send', { email => $email }); return if $email->header('to') eq ''; if ($method eq "Test") { my $filename = bz_locations()->{'datadir'} . '/mailer.testfile'; open TESTFILE, '>>', $filename; # From - <date> is required to be a valid mbox file. print TESTFILE "\n\nFrom - " . $email->header('Date') . "\n" . $email->as_string; close TESTFILE; } else { # This is useful for Sendmail, so we put it out here. local $ENV{PATH} = SENDMAIL_PATH; eval { sendmail($email, { transport => $transport }) }; if ($@) { ThrowCodeError('mail_send_error', { msg => $@->message, mail => $email }); } } }
使用的sendmail($email, {transport=> $transport}), 發送。由於系統的sendmail沒有配置好,發送不出來。
自己寫了一版單獨用perl發送郵件的。
#!/usr/bin/perl use Net::SMTP::SSL; use MIME::Base64; use MIME::Lite; my $msg = MIME::Lite->new( From=>'[email protected]', To=>'[email protected]', Subject=>'First Email', Data=>'nihao', Type=>'text/html' ); $account="[email protected]"; $password="your password"; $smtp = Net::SMTP::SSL->new( Host => 'smtp.host.com', Port => 465, Timeout => 120, Debug=>1 ); # connect to an SMTP server die "Couldn't open connection: $!" if (!defined $smtp ); #$smtp->auth($account,$password); $smtp->datasend("AUTH LOGIN\r\n"); $smtp->datasend(encode_base64('[email protected]')); # username $smtp->datasend(encode_base64('your password')); # password $smtp->mail('[email protected]'); $smtp->to('[email protected]'); $smtp->data(); $smtp->datasend($msg->as_string()); $smtp->datasend("Blah\r\n\r\n"); $smtp->dataend();
相信從代碼風格來看,就是第一次寫perl 。^_^ 。 ^_^
這版是可以發送郵件的。
但是跟bugzilla的mailer.pm 發送差別很大。沒辦法整合。
於是又換了一種寫法。
#!/usr/bin/perl use strict; use warnings; use Email::Sender::Simple qw(sendmail); use Email::Sender::Transport::SMTP (); use Email::Simple (); use Email::Simple::Creator (); use Email::Sender::Transport::SMTP::TLS; use Email::Sender::Transport::SMTP::Persistent; use Email::Sender::Transport::SMTPS; use Net::SMTP::SSL; use MIME::Base64; my $smtpserver = 'smtp.host.com'; my $smtpport = 465; my $smtpuser = '[email protected]'; my $smtppassword = 'your password'; my $smtpto = '[email protected]'; my $hostname = 'localhost.localdomain'; my $transport = Email::Sender::Transport::SMTP::Persistent->new({ host => $smtpserver, defined($smtpport) ? (port => $smtpport) : (), sasl_username => $smtpuser, sasl_password => $smtpuser, helo => $hostname, ssl => 1, debug => 1 }); my $email = Email::Simple->create( header => [ To => $smtpto, From => $smtpuser, Subject => 'Hi!', ], body => "This is my message\n", ); #try { #eval {sendmail($email, { transport => $transport }) }; #}catch { #die "Error sending email: $_"; my $smtp = Net::SMTP::SSL->new( Host => $smtpserver, Port => $smtpport, Timeout => 120, Debug => 1 ); die "Couldn't open connection: $!" if (!defined $smtp ); #$smtp->auth(Bugzilla->params->{'smtp_username'}, Bugzilla->params->{'smtp_password'}); $smtp->datasend("AUTH LOGIN\n"); $smtp->datasend(encode_base64($smtpuser)); $smtp->datasend(encode_base64($smtppassword)); $smtp->mail($smtpuser); $smtp->to($smtpto); $smtp->data(); $smtp->datasend($email->as_string()); $smtp->datasend("\r\n\r\n"); $smtp->dataend(); $smtp->quit();
這個代碼的風格就像那麽回事了,就寫過perl代碼的人了 。^_^。^_^
將mailer.pm 的代碼整合。
sub MessageToMTA { my ($msg, $send_now) = (@_); my $method = Bugzilla->params->{'mail_delivery_method'}; return if $method eq 'None'; if (Bugzilla->params->{'use_mailer_queue'} && ! $send_now && ! Bugzilla->dbh->bz_in_transaction() ) { Bugzilla->job_queue->insert('send_mail', { msg => $msg }); return; } my $dbh = Bugzilla->dbh; my $email = ref($msg) ? $msg : Bugzilla::MIME->new($msg); # If we're called from within a transaction, we don't want to send the # email immediately, in case the transaction is rolled back. Instead we # insert it into the mail_staging table, and bz_commit_transaction calls # send_staged_mail() after the transaction is committed. if (! $send_now && $dbh->bz_in_transaction()) { # The e-mail string may contain tainted values. my $string = $email->as_string; trick_taint($string); my $sth = $dbh->prepare("INSERT INTO mail_staging (message) VALUES (?)"); $sth->bind_param(1, $string, $dbh->BLOB_TYPE); $sth->execute; return; } my $from = $email->header('From'); my $hostname; my $transport; if ($method eq "Sendmail") { if (ON_WINDOWS) { $transport = Bugzilla::Sender::Transport::Sendmail->new({ sendmail => SENDMAIL_EXE }); } else { $transport = Bugzilla::Sender::Transport::Sendmail->new(); } } else { # Sendmail will automatically append our hostname to the From # address, but other mailers won't. my $urlbase = Bugzilla->params->{'urlbase'}; $urlbase =~ m|//([^:/]+)[:/]?|; $hostname = $1 || 'localhost'; $from .= "\@$hostname" if $from !~ /@/; $email->header_set('From', $from); # Sendmail adds a Date: header also, but others may not. if (!defined $email->header('Date')) { $email->header_set('Date', time2str("%a, %d %b %Y %T %z", time())); } } my ($host, $port) = split(/:/, Bugzilla->params->{'smtpserver'}, 2); if ($method eq "SMTP") { # my ($host, $port) = split(/:/, Bugzilla->params->{'smtpserver'}, 2); $transport = Bugzilla->request_cache->{smtp} //= Email::Sender::Transport::SMTP::Persistent->new({ host => $host, defined($port) ? (port => $port) : (), sasl_username => Bugzilla->params->{'smtp_username'}, sasl_password => Bugzilla->params->{'smtp_password'}, helo => $hostname, ssl => Bugzilla->params->{'smtp_ssl'}, debug => Bugzilla->params->{'smtp_debug'} }); } Bugzilla::Hook::process('mailer_before_send', { email => $email }); return if $email->header('to') eq ''; if ($method eq "Test") { my $filename = bz_locations()->{'datadir'} . '/mailer.testfile'; open TESTFILE, '>>', $filename; # From - <date> is required to be a valid mbox file. print TESTFILE "\n\nFrom - " . $email->header('Date') . "\n" . $email->as_string; close TESTFILE; } else { # This is useful for Sendmail, so we put it out here. # local $ENV{PATH} = SENDMAIL_PATH; # eval { sendmail($email, { transport => $transport }) }; # if ($@) { # ThrowCodeError('mail_send_error', { msg => $@->message, mail => $email }); # } my $smtp = Net::SMTP::SSL->new( Host => $host, Port => $port, Timeout => 120, Debug => 1 ); die "Couldn't open connection: $!" if (!defined $smtp ); #$smtp->auth(Bugzilla->params->{'smtp_username'}, Bugzilla->params->{'smtp_password'}); $smtp->datasend("AUTH LOGIN\n"); $smtp->datasend(encode_base64(Bugzilla->params->{'smtp_username'})); $smtp->datasend(encode_base64(Bugzilla->params->{'smtp_password'})); $smtp->mail(Bugzilla->params->{'smtp_username'}); $smtp->to($email->header('to')); $smtp->data(); $smtp->datasend($email->as_string()); $smtp->datasend("\r\n\r\n"); $smtp->dataend(); $smtp->quit(); } }
主要是將郵件發送方式修改了。
local $ENV{PATH} = SENDMAIL_PATH; eval { sendmail($email, { transport => $transport }) }; if ($@) { ThrowCodeError('mail_send_error', { msg => $@->message, mail => $email }); }
換成了
my $smtp = Net::SMTP::SSL->new( Host => $host, Port => $port, Timeout => 120, Debug => 1 ); die "Couldn't open connection: $!" if (!defined $smtp ); #$smtp->auth(Bugzilla->params->{'smtp_username'}, Bugzilla->params->{'smtp_password'}); $smtp->datasend("AUTH LOGIN\n"); $smtp->datasend(encode_base64(Bugzilla->params->{'smtp_username'})); $smtp->datasend(encode_base64(Bugzilla->params->{'smtp_password'})); $smtp->mail(Bugzilla->params->{'smtp_username'}); $smtp->to($email->header('to')); $smtp->data(); $smtp->datasend($email->as_string()); $smtp->datasend("\r\n\r\n"); $smtp->dataend(); $smtp->quit();
bugzilla的代碼還是寫的很人性化的,沒有寫過perl代碼的,一看都能知道個大概。向Bugzilla的作者致敬。
至於郵件發送的流程,大家可以參照rfc822,不要隨便相信網上的代碼。
使用Bugzilla,你肯定會遇到的坑。