mourningdove/cgi-bin/DW/Controller/Admin/SendMail.pm

348 lines
11 KiB
Perl
Raw Normal View History

2026-05-24 01:03:05 +00:00
#!/usr/bin/perl
#
# DW::Controller::SendMail
#
# Admin page for sending emails from site accounts.
#
# Authors:
# Jen Griffin <kareila@livejournal.com>
#
# Copyright (c) 2012-2015 by Dreamwidth Studios, LLC.
#
# This program is free software; you may redistribute it and/or modify it under
# the same terms as Perl itself. For a copy of the license, please reference
# 'perldoc perlartistic' or 'perldoc perlgpl'.
#
package DW::Controller::Admin::SendMail;
use strict;
use DW::Controller;
use DW::Routing;
use DW::Template;
use DW::Controller::Admin;
use DW::FormErrors;
my @privs = qw(siteadmin:sendmail);
DW::Controller::Admin->register_admin_page(
'/',
path => 'sendmail/',
ml_scope => '/admin/sendmail/index.tt',
privs => \@privs,
);
DW::Routing->register_string( "/admin/sendmail/index", \&index_controller );
DW::Routing->register_string( "/admin/sendmail/lookup", \&lookup_controller );
DW::Routing->register_string( "/admin/sendmail/message", \&message_controller );
# FIXME: add this later
#DW::Routing->register_string( "/admin/sendmail/forms", \&form_controller );
# helper function for lookup and message
my $enhance_row = sub {
my ($row) = @_;
# link to view full message
$row->{msgurl} = LJ::create_url( "/admin/sendmail/message", args => { id => $row->{msgid} } );
# time_sent needs a display version
$row->{time_sent_view} = LJ::time_to_http( $row->{time_sent} );
# build a request URL
if ( $row->{request} ) {
$row->{request_url} =
LJ::create_url( "/support/see_request", args => { id => $row->{request} } );
}
# sendto might be a uid (if it's an email, no work is needed)
if ( $row->{sendto} =~ /^\d+$/ ) {
if ( my $u = LJ::load_userid( $row->{sendto} ) ) {
$row->{sendto} = $u;
}
else {
$row->{sendto} = '[unknown user] ' . $row->{sendto};
}
}
# sentfrom is definitely a uid
if ( my $ru = LJ::load_userid( $row->{remoteid} ) ) {
$row->{remote} = $ru;
}
else {
$row->{remote} = '[unknown user] ' . $row->{remoteid};
}
# add domain name to display site address
$row->{account_view} = $row->{account} . "\@$LJ::DOMAIN";
return $row;
};
sub index_controller {
my ( $ok, $rv ) = controller( privcheck => \@privs, form_auth => 1 );
return $rv unless $ok;
my $remote = $rv->{remote};
my $scope = sub { return '/admin/sendmail/index.tt' . $_[0] };
my $r = DW::Request->get;
my $errors = DW::FormErrors->new;
# form processing
if ( $r->did_post ) {
my $args = $r->post_args;
my $account = LJ::trim( $args->{account} );
my $sendto = LJ::trim( $args->{sendto} );
my $message = LJ::trim( $args->{message} );
my $subject = LJ::trim( $args->{subject} );
my $support_req = LJ::trim( $args->{request} );
my $teamnotes = LJ::trim( $args->{notes} );
my $reqsubj = $args->{reqsubj} ? 1 : 0;
if ($account) {
$errors->add( "account", ".error.badacct" )
unless exists $LJ::SENDMAIL_ACCOUNTS{$account};
}
else {
$errors->add( "account", ".error.noacct" );
}
$errors->add( "subject", ".error.nosubj" ) unless $subject;
$errors->add( "message", ".error.nomsg" ) unless $message;
if ($support_req) {
if ( $support_req =~ /^\d+$/ ) {
$subject = "[\#$support_req] $subject" if $reqsubj;
}
else {
$errors->add( "request", ".error.badreq" );
}
}
my $u;
if ($sendto) {
if ( $sendto =~ /,/ ) { # multiple recipients
$errors->add( "sendto", ".error.nomulti" );
}
else {
# make sure we're sending to either a valid user or something
# that looks reasonably like an email address.
if ( $sendto !~ /^[^@]+@[^.]+\./ ) {
# doesn't look like an email address; do a username lookup
$u = LJ::load_user($sendto);
$errors->add( "sendto", ".error.badrcpt" ) unless defined $u;
$sendto = $u->id; # log userid instead of username
}
}
}
else { # no $sendto
$errors->add( "sendto", ".error.norcpt" );
}
# at this point, we should have good form data; the form can still
# have errors below, but they aren't the fault of the user input
unless ( $errors->exist ) {
# now that we have the data, send the message.
# 1. insert data into siteadmin_email_history table
my $msgid = LJ::alloc_global_counter('N');
my $dbh = LJ::get_db_writer();
$dbh->do(
"INSERT INTO siteadmin_email_history (msgid, remoteid,"
. " time_sent, account, sendto, subject, request, message, "
. " notes) VALUES (?,?,?,?,?,?,?,?,?)",
undef,
$msgid,
$remote->id,
time,
$account,
$sendto,
$subject,
$support_req,
$message,
$teamnotes
) or return error_ml( $scope->('.error.sendfailed') );
# keeping this as error_ml; if we get a DB error things are FUBAR
# 2. construct the message and send it to the user(s)
# (this block adapted from bin/worker/paidstatus)
my $msg = {
from => "$account\@$LJ::DOMAIN",
fromname => $LJ::SITENAME,
subject => $subject,
body => $message,
};
my $sent = 0;
my $send = sub { LJ::send_mail($msg); $sent = 1; };
if ( $u && $u->is_community ) {
# send an email to every maintainer
my $maintus = LJ::load_userids( $u->maintainer_userids );
foreach my $maintu ( values %$maintus ) {
$msg->{to} = $maintu->email_raw;
$send->() if $msg->{to};
}
}
else {
$msg->{to} = $u ? $u->email_raw : $sendto;
$send->() if $msg->{to};
}
# 3. update userlog and return success message
if ($sent) {
$remote->log_event(
'siteadmin_email',
{
account => $account,
msgid => $msgid
}
);
return success_ml(
$scope->('.success.msgtext'),
undef,
[
{
text => LJ::Lang::ml( $scope->('.success.linktext.a') ),
url => '/admin/sendmail'
}
]
);
}
else {
$errors->add( "sendto", ".error.nouseremail" );
}
}
}
# end form processing
# Construct data for dropdown of available email addresses;
# the user should have at least one of these for this page to be useful.
# If the user somehow has sendmail priv but no relevant account priv,
# we print an error in the template in that case.
my @account_menu = ( "", LJ::Lang::ml( $scope->('.select.account.choose') ) );
foreach my $account ( sort keys %LJ::SENDMAIL_ACCOUNTS ) {
my $priv = $LJ::SENDMAIL_ACCOUNTS{$account};
push @account_menu, ( $account, "$account\@$LJ::DOMAIN" )
if $remote->has_priv($priv);
}
$rv->{has_menu} = ( @account_menu > 2 );
$rv->{account_menu} = \@account_menu;
$rv->{errors} = $errors;
$rv->{formdata} = $r->post_args;
return DW::Template->render_template( 'admin/sendmail/index.tt', $rv );
}
sub lookup_controller {
my ( $ok, $rv ) = controller( privcheck => \@privs, form_auth => 1 );
return $rv unless $ok;
my $remote = $rv->{remote};
my $scope = sub { return '/admin/sendmail/lookup.tt' . $_[0] };
my $r = DW::Request->get;
my $errors = DW::FormErrors->new;
if ( $r->did_post ) {
my $args = $r->post_args;
my $account = LJ::trim( $args->{account} );
if ($account) {
if ( my $priv = $LJ::SENDMAIL_ACCOUNTS{$account} ) {
$errors->add( "account", ".error.nopriv", { priv => $priv } )
unless $remote->has_priv($priv);
}
else {
$errors->add( "account", ".error.badacct" );
}
}
else {
$errors->add( "account", ".error.noacct" );
}
unless ( $errors->exist ) {
my $dbr = LJ::get_db_reader();
my $rows = $dbr->selectall_hashref(
"SELECT msgid, time_sent, sendto, subject, request"
. " FROM siteadmin_email_history WHERE account=?",
'msgid', undef, $account
);
die $dbr->errstr if $dbr->err;
my @data = map { $enhance_row->($_) } values %$rows;
$rv->{rows} = [ sort { $b->{time_sent} <=> $a->{time_sent} } @data ];
$rv->{account} = $account;
}
}
# Construct data for dropdown of available email addresses
my @account_menu = ( "", LJ::Lang::ml( $scope->('.select.account.choose') ) );
foreach my $account ( sort keys %LJ::SENDMAIL_ACCOUNTS ) {
my $priv = $LJ::SENDMAIL_ACCOUNTS{$account};
push @account_menu, ( $account, "$account\@$LJ::DOMAIN" )
if $remote->has_priv($priv);
}
$rv->{has_menu} = ( @account_menu > 2 );
$rv->{account_menu} = \@account_menu;
$rv->{errors} = $errors;
$rv->{formdata} = $r->post_args;
return DW::Template->render_template( 'admin/sendmail/lookup.tt', $rv );
}
sub message_controller {
my ( $ok, $rv ) = controller( privcheck => \@privs );
return $rv unless $ok;
my $remote = $rv->{remote};
my $scope = sub { return '/admin/sendmail/lookup.tt' . $_[0] };
my $r = DW::Request->get;
my $args = $r->get_args;
my $msgid = LJ::trim( $args->{id} );
return $r->redirect("/admin/sendmail/lookup")
unless $msgid && $msgid =~ /^\d+$/;
my $dbr = LJ::get_db_reader();
my $row = $dbr->selectrow_hashref( "SELECT * FROM siteadmin_email_history WHERE msgid=?",
undef, $msgid );
die $dbr->errstr if $dbr->err;
return error_ml( $scope->('.error.nomsg') ) unless $row;
my $priv = $LJ::SENDMAIL_ACCOUNTS{ $row->{account} };
if ( $priv && $remote->has_priv($priv) ) {
$rv->{row} = $enhance_row->($row);
}
else {
return error_ml( $scope->('.error.nopriv'), { priv => $priv } );
}
return DW::Template->render_template( 'admin/sendmail/message.tt', $rv );
}
1;