Skip to content

Commit

Permalink
Merge pull request openwebwork#2451 from dlglin/SMTP-Auth
Browse files Browse the repository at this point in the history
Add username and password option for SMTP authentication
  • Loading branch information
Alex-Jordan authored Aug 6, 2024
2 parents bf11f98 + ea523cc commit 73f2cae
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 77 deletions.
10 changes: 10 additions & 0 deletions conf/defaults.config
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ include("VERSION"); # get WW version
# Mail Settings
################################################################################

# $generic_sender_name will be used as the "From:" name on all feedback emails
# sent without a defined user.
$generic_sender_name = '';

# The following variables will override the "From:" address in messages sent by
# the named feature.
$feedback_sender_email = ''; # For student feedback
$instructor_sender_email = ''; # For instructors emailing students
$jitar_sender_email = ''; # For notifications of incomplete JiTaR sets

# By default, feedback is sent to all users who have permission to
# receive_feedback in a course. If this list is non-empty, feedback is also sent
# to the addresses specified here.
Expand Down
18 changes: 18 additions & 0 deletions conf/localOverrides.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@
# Additional mail settings in defaults.config can be overridden here
################################################################################

# $generic_sender_name will be used as the "From:" name on all feedback emails
# sent without a defined user.

$generic_sender_name = '';

# The following variables will override the "From:" address in messages sent by
# the named feature.
# When one of these is set, all messages sent by that feature will use the
# supplied email address as the "From:" address, and the address of the relevant
# user will be set as the "Reply-to:" address for the message.
# These may be required if you have provided an SMTP username and password in
# site.conf. They can also be used to improve email verification and avoid
# messages getting filtered as spam.

$feedback_sender_email = ''; # For student feedback
$instructor_sender_email = ''; # For instructors emailing students
$jitar_sender_email = ''; # For notifications of incomplete JiTaR sets

# By default, feedback is sent to all users who have permission to
# receive_feedback in a course. If this list is non-empty, feedback is also sent
# to the addresses specified here.
Expand Down
51 changes: 28 additions & 23 deletions conf/site.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -242,26 +242,16 @@ $webwork_courses_dir = "/opt/webwork/courses"; # a typical place to put course d
# The following directives need to be configured in order for your webwork
# server to be able to send mail.

# Mail sent by the PG system and the mail merge and feedback modules will be
# sent via this SMTP server. localhost may work if your server is capable
# of sending email, otherwise type the name of your School's outgoing email
# server.
# Mail sent by the mail merge and feedback modules will be sent via this SMTP
# server. localhost may work if your server is capable of sending email,
# otherwise type the name of your School's outgoing email server.
$mail{smtpServer} = ''; # e.g. 'mail.yourschool.edu' or 'localhost'

# When connecting to the above server, WeBWorK will send this address in the
# MAIL FROM command. This has nothing to do with the "From" address on the mail
# message. It can really be anything, but some mail servers require it contain
# a valid mail domain, or at least be well-formed.
$mail{smtpSender} = ''; # e.g. '[email protected]'
# Be sure to use single quotes for the address or the @ sign will be interpreted as an array.

$mail{set_return_path} = ''; #sets the return_path to the From: field (sender's email address)
# The return path is used to send error messages about bounced emails
# "noreply\@$mail{smtpServer}" discards error messages,
# using $mail{smtpSender} would deliver error messages to that address.
# The default setting should be adjusted for local domain
# Leaving the return path blank triggers the default which results in Return-Path being set to the email of the sender.
#

# Seconds to wait before timing out when connecting to the SMTP server.
# the default is 120 seconds.
Expand All @@ -270,32 +260,47 @@ $mail{set_return_path} = ''; #sets the return_path to the From: field (sender's

$mail{smtpTimeout} = 30;


# TLS is a method for providing secure connections to the smtp server.
# https://en.wikipedia.org/wiki/Transport_Layer_Security
# At some sites coordinating the certificates properly is tricky
# Set this value to 0 to avoid checking certificates.
# Set it to 0 to trouble shoot an inability to verify certificates with the smtp server
# Allowed values: 'starttls', 'ssl', 'maybestarttls', 0
# Values of 'maybestarttls' and 0 are insecure and are not recommended for
# production environments, except where the smtp server is localhost.

$mail{tls_allowed} = 0;

#$tls_allowed=0; #old method -- this variable no longer works.
# Extra settings for SSL/TLS
# You may need to use this setting if your SMTP server uses a self-signed certificate.
# SSL_verify_mode => 0 is not recommended for production environments for security
# reasons. See https://metacpan.org/pod/IO::Socket::SSL#Common-Usage-Errors

#$mail{smtpSSLOptions} = {SSL_verify_mode => 0};

# errors of the form
# unable to establish SMTP connection to smtp-gw.rochester.edu port 465
# indicate that there is a mismatch between the port number and the use of ssl
# use port 25 when ssl is off and use port 465 when ssl is on (tls_allowed=1)
# "unable to establish SMTP connection to smtp-gw.rochester.edu port 465"
# indicate that there may be a mismatch between the port number and the use of ssl.
# Many mail servers use port 25 when ssl is off, use port 465 when ssl is on (tls_allowed='ssl'),
# and use port 587 when starttls is used (tls_allowed='starttls').


# Set the SMTP port manually. Typically this does not need to be done it will use
# port 25 if no SSL is on and 465 if ssl is on
# Set the SMTP port manually. Typically this does not need to be done. It will use
# port 25 if insecure, and 465 if ssl is on

#$mail{smtpPort} = 25;

# Debugging tutorial for sending email using ssl/tls
# https://maulwuff.de/research/ssl-debugging.html

# SMTP Authentication
# If your SMTP server requires authentication you can provide the username and password
# for the account on the mail server.
# If you set these credentials, you may need to define the variables $feedback_sender_email,
# $instructor_sender_email and $jitar_sender_email in localOverrides.conf, as some SMTP
# servers require the "From:" address of outgoing emails to match this username. Setting
# those sender variables will then put the user's email address in the "Reply-to:" field.

#$mail{smtpUsername} = '';
#$mail{smtpPassword} = '';

# Set maxAttachmentSize to the maximum number of megabytes to allow for the size of
# files attached to feedback emails. Note that this should be set to match the
# limitations of the email server chosen above, and should be set to a value greater
Expand Down
3 changes: 2 additions & 1 deletion courses.dist/modelCourse/templates/email/welcome.msg
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## template for a Welcome message to be emailed to class (delete this line)
## Note that the From: address will be replaced by the email address of the account
## from which the message is sent.
From: [email protected] (Jan Teacher)
Reply-To: [email protected]
Subject: online homework for Math 123
Message:
Hi $FN,
Expand Down
11 changes: 8 additions & 3 deletions lib/Mojolicious/WeBWorK/Tasks/SendInstructorEmail.pm
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,14 @@ sub mail_message_to_recipients ($job, $ce, $db, $mail_data) {
$mail_data->{merge_data}
);

my $email =
Email::Stuffer->to($user_record->email_address)->from($mail_data->{from})->subject($mail_data->{subject})
->text_body($msg)->header('X-Remote-Host' => $mail_data->{remote_host});
my $email = Email::Stuffer->to($user_record->email_address)->subject($mail_data->{subject})->text_body($msg)
->header('X-Remote-Host' => $mail_data->{remote_host});
if ($ce->{instructor_sender_email}) {
$email->from($mail_data->{from_name} . ' <' . $ce->{instructor_sender_email} . '>')
->reply_to($mail_data->{from});
} else {
$email->from($mail_data->{from});
}

eval {
$email->send_or_die({
Expand Down
10 changes: 7 additions & 3 deletions lib/WeBWorK/ContentGenerator/Feedback.pm
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,14 @@ $emailableURL
$msg .= "***** Data about the environment: *****\n\n" . Dumper($ce) . "\n\n";
}

my $email =
Email::Stuffer->to(join(',', @recipients))->from($sender)->subject($subject)->text_body($msg)
my $from_name = $user ? $user->full_name : $ce->{generic_sender_name};
my $email = Email::Stuffer->to(join(',', @recipients))->subject($subject)->text_body($msg)
->header('X-Remote-Host' => $remote_host);

if ($ce->{feedback_sender_email}) {
$email->from("$from_name <$ce->{feedback_sender_email}>")->reply_to($sender);
} else {
$email->from($sender);
}
# Extra headers
$email->header('X-WeBWorK-Route', $route) if defined $route;
$email->header('X-WeBWorK-Course', $courseID) if defined $courseID;
Expand Down
48 changes: 12 additions & 36 deletions lib/WeBWorK/ContentGenerator/Instructor/SendMail.pm
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ sub initialize ($c) {

# Store data
$c->{defaultPreviewUser} = $ur;
$c->{defaultFrom} = $ur->rfc822_mailbox;
$c->{defaultReply} = $ur->rfc822_mailbox;
$c->{from} = $ur->rfc822_mailbox;
$c->{from_name} = $ur->full_name;
$c->{defaultSubject} = $c->stash('courseID') . ' notice';
$c->{merge_file} = $mergefile // '';

Expand Down Expand Up @@ -196,13 +196,11 @@ sub initialize ($c) {
}

# Get inputs
my ($from, $replyTo, $r_text, $subject);
my ($r_text, $subject);
if ($input_source eq 'file') {
if ($input_file) {
($from, $replyTo, $subject, $r_text) = $c->read_input_file("$emailDirectory/$input_file");
($subject, $r_text) = $c->read_input_file("$emailDirectory/$input_file");
} else {
$from = $c->{defaultFrom};
$replyTo = $c->{defaultReply};
$subject = $c->{defaultSubject};

# If action is openMessage and no file was found, then 'None' was selected.
Expand All @@ -212,16 +210,12 @@ sub initialize ($c) {
$c->param('savefilename', 'default.msg') if $c->param('savefilename');
}
}
$c->param('from', $from) if $from;
$c->param('replyTo', $replyTo) if $replyTo;
$c->param('subject', $subject) if $subject;
$c->param('body', $$r_text) if $r_text;
} elsif ($input_source eq 'form') {
# read info from the form
# bail if there is no message body

$from = $c->param('from');
$replyTo = $c->param('replyTo');
$subject = $c->param('subject');
my $body = $c->param('body');
# Sanity check: body must contain non-white space when previewing message.
Expand All @@ -233,8 +227,6 @@ sub initialize ($c) {
my $remote_host = $c->tx->remote_address || "UNKNOWN";

# Store data
$c->{from} = $from;
$c->{replyTo} = $replyTo;
$c->{subject} = $subject;
$c->{remote_host} = $remote_host;
$c->{r_text} = $r_text;
Expand Down Expand Up @@ -280,8 +272,7 @@ sub initialize ($c) {
$temp_body =~ s/\r\n/\n/g;
$temp_body = join(
"\n",
"From: $from",
"Reply-To: $replyTo",
"From: $c->{from}",
"Subject: $subject",
"Content-Type: text/plain; charset=UTF-8",
"Message:",
Expand Down Expand Up @@ -315,15 +306,6 @@ sub initialize ($c) {
return;
}

# verify format of Reply-to address (zero or more valid rfc2822/ref5322 addresses)
if (defined $c->{replyTo} and $c->{replyTo} ne "") {
my @parsed_replyto_addrs = Email::Address::XS->parse($c->{replyTo});
unless (@parsed_replyto_addrs > 0) {
$c->addbadmessage($c->maketext("Invalid Reply-to address."));
return;
}
}

# Check that recipients have been selected.
unless (@{ $c->{ra_send_to} }) {
$c->addbadmessage(
Expand Down Expand Up @@ -353,7 +335,7 @@ sub initialize ($c) {
text => ${ $c->{r_text} // \'' },
merge_data => $c->{rh_merge_data},
from => $c->{from},
defaultFrom => $c->{defaultFrom},
from_name => $c->{from_name},
remote_host => $c->{remote_host},
} ],
{ notes => { courseID => $c->stash('courseID') } }
Expand Down Expand Up @@ -393,10 +375,9 @@ sub print_preview ($c) {
# Note that this escaping is done in the Mojolicious template automatically.
$msg = join(
"",
"To: ", $c->{preview_user}->email_address, "\n",
"From: ", $c->{from}, "\n",
"Reply-To: ", $c->{replyTo}, "\n",
"Subject: ", $c->{subject}, "\n",
"To: ", $c->{preview_user}->email_address, "\n",
"From: ", $c->{from}, "\n",
"Subject: ", $c->{subject}, "\n",
# In a real mails we would UTF-8 encode the message and give the Content-Type header. For the preview which is
# displayed as html, just add the header, but do NOT use Encode::encode("UTF-8",$msg).
"Content-Type: text/plain; charset=UTF-8\n\n",
Expand Down Expand Up @@ -435,7 +416,7 @@ sub saveMessageFile ($c, $body, $msgFileName) {
sub read_input_file ($c, $filePath) {
my ($text, @text);
my $header = '';
my ($subject, $from, $replyTo);
my $subject;

open my $FILE, "<:encoding(UTF-8)", $filePath
or do { $c->addbadmessage($c->maketext(q{Can't open [_1]}, $filePath)); return };
Expand All @@ -445,17 +426,12 @@ sub read_input_file ($c, $filePath) {
$text = join('', <$FILE>);
close $FILE;

$text =~ s/^\s*//; # remove initial white space if any.
$header =~ /^From:\s(.*)$/m;
$from = $1 || $c->{defaultFrom};

$header =~ /^Reply-To:\s(.*)$/m;
$replyTo = $1 || $c->{defaultReply};
$text =~ s/^\s*//; # remove initial white space if any.

$header =~ /^Subject:\s(.*)$/m;
$subject = $1 || $c->{defaultSubject};

return ($from, $replyTo, $subject, \$text);
return ($subject, \$text);
}

sub get_message_file_names ($c) {
Expand Down
5 changes: 4 additions & 1 deletion lib/WeBWorK/Utils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,10 @@ sub createEmailSenderTransportSMTP ($ce) {
return Email::Sender::Transport::SMTP->new({
host => $ce->{mail}{smtpServer},
ssl => $ce->{mail}{tls_allowed} // 0,
defined $ce->{mail}->{smtpPort} ? (port => $ce->{mail}{smtpPort}) : (),
defined $ce->{mail}->{smtpPort} ? (port => $ce->{mail}{smtpPort}) : (),
defined $ce->{mail}->{smtpUsername} ? (sasl_username => $ce->{mail}{smtpUsername}) : (),
defined $ce->{mail}->{smtpPassword} ? (sasl_password => $ce->{mail}{smtpPassword}) : (),
defined $ce->{mail}->{smtpSSLOptions} ? (ssl_options => $ce->{mail}{smtpSSLOptions}) : (),
timeout => $ce->{mail}{smtpTimeout},
});
}
Expand Down
7 changes: 6 additions & 1 deletion lib/WeBWorK/Utils/ProblemProcessing.pm
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,12 @@ Recitation: $recitation
Comment: $comment
/;

my $email = Email::Stuffer->to(join(',', @recipients))->from($sender)->subject($subject)->text_body($msg);
my $email = Email::Stuffer->to(join(',', @recipients))->subject($subject)->text_body($msg);
if ($ce->{jitar_sender_email}) {
$email->from("$full_name <$ce->{jitar_sender_email}>")->reply_to($sender);
} else {
$email->from($sender);
}

# Extra headers
$email->header('X-WeBWorK-Course: ', $courseID) if defined $courseID;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,7 @@
<%= label_for from => maketext('From:'),
class => 'col-sm-3 col-form-label col-form-label-sm' =%>
<div class="col-sm-9">
<%= text_field from => $c->{from}, id => 'from',
class => 'form-control form-control-sm' =%>
</div>
</div>
<div class="row mb-1">
<%= label_for replyTo => maketext('Reply-To:'),
class => 'col-sm-3 col-form-label col-form-label-sm' =%>
<div class="col-sm-9">
<%= text_field replyTo => $c->{replyTo}, id => 'replyTo',
<%= text_field from => $c->{from}, id => 'from', readonly => undef, disabled => undef,
class => 'form-control form-control-sm' =%>
</div>
</div>
Expand Down

0 comments on commit 73f2cae

Please sign in to comment.