#!/usr/bin/perl
# Be sure that the line above points to where perl 5 is
# on your system.
##################################################################
# nmmdadmin.cgi: subscription e-mail collector with subscribe
# and unsubscribe features.
# Release 1.1 on 02/03/02
# (C) 1999-2002 BigNoseBird.Com, Inc. This program is freeware and may
# be used at no cost to you (just leave this notice intact).
# Feel free to modify, hack, and play with this script.
# It is provided AS-IS with no warranty of any kind.
# We also cannot assume responsibility for either any programs
# provided here, or for any advice that is given since we have no
# control over what happens after our code or words leave this site.
# Always use prudent judgment in implementing any program- and
# always make a backup first!
# Thanks to James Ryley for some excellent cleanup work!
#
##################################################################
use Socket;
$|=1;
## SECURITY NOTICE # SECURITY NOTICE # SECURITY NOTICE ###########
#
# This script has NO security features built in. Please
# consult the README.TXT file for information on securing
# this script from abuse.
#
##################################################################
#### USER CONFIGURATION SECTION ##################################
# delimiter is the character that divides your e-mail list file.
# $delimiter = "\\t" # tab
$delimiter = "\\|"; # pipe
# $BASEDIR is the full directory path to where you will store your
# mail list (mbz) files and letter (ltr) files. Be certain that
# the script can write to this directory
$BASEDIR="/www/mydomain/cgi-bin/nomodomo";
# $TEMPDIR is the location of the system temporary directory. The
# setting below is fine for all unix systems.
$TEMPDIR="/tmp";
# $SCRIPT_URL is the URL (not path) of this script.
$SCRIPT_URL="/cgi-bin/nomodomo/nmmdadmin.cgi";
# $SMTP_SERVER is the name of the sendmail or SMTP host that will
# send your mail. This script uses the SOCKETS method, and does
# not use sendmail or BLAT.EXE for cross-platform use and ease of
# installation. The value below should work on almost all systems
# that are capable of sending mail. Use your ISP's mail server
# such as mail.xxxyyyzzz.net if your host cannot originate mail.
$SMTP_SERVER="localhost";
# In case you don't have access to SMTP, try commenting out the
# line above with a # mark at the start of the line, and uncomment
# the SENDMAIL below.
# $SEND_MAIL="/usr/lib/sendmail -t";
# $DEFAULT_EMAIL is used as the default 'from' e-mail address
# for your mailings. You can type over this value when sending
# mail.
$DEFAULT_EMAIL="me\@mydomain.com";
##################################################################
&test_dirs;
&setup;
if ($ENV{'REQUEST_METHOD'} ne "POST" && $ENV{'QUERY_STRING'} eq "") {
&query_form; exit;
}
&decode_vars;
if ($ENV{'REQUEST_METHOD'} eq "POST" && $fields{'action'} eq "LIST") {
&get_list; exit;
}
if ($ENV{'REQUEST_METHOD'} eq "POST" && $fields{'action'} eq "SENDMAIL") {
&fire_mail;
exit;
}
if ($ENV{'REQUEST_METHOD'} eq "POST" && $fields{'action'} eq "POSTLETTER") {
&post_letter;
exit;
}
if ($ENV{'REQUEST_METHOD'} eq "POST" && $fields{'action'} eq "EDIT") {
<r_editor;
exit;
}
if ($ENV{'REQUEST_METHOD'} eq "POST" && $fields{'action'} eq "PURGE") {
&purge_names;
exit;
}
&error_report("Called without proper options set");
##################################################################
sub query_form
{
print "Content-type: text/html\n\n";
$fileselect = &get_files("filename","mbz");
$ltrselect = &get_files("lfilename","ltr");
print <<__QUERY_FORM__;
BNB's NoMoDoMo
Welcome to BNB's NoMoDomo Subscription Manager Control
Panel! The forms below will allow you to manage your
mailing lists, create and edit your letters, and send
out mailings.
$cpr
__QUERY_FORM__
}
sub fire_mail
{
if ($fields{'filename'} eq "" or $fields{'lfilename'} eq "") {
&error_report("No letter file or mail list file selected");
}
if ($fields{'from'} eq "" or &valid_address($fields{'from'}) != 1) {
&error_report("The from e-mail is missing or invalid");
}
if (!-e "$BASEDIR/$fields{'filename'}") {
&error_report("unable to find $fields{'filename'} file");
}
if (!-e "$BASEDIR/$fields{'lfilename'}") {
&error_report("unable to find $fields{'filename'} file");
}
$lettext="";
open (INMLTR,"<$BASEDIR/$fields{'lfilename'}");
while ($ir=)
{
chop $ir;
if (($ir eq "") || ($ir eq "\r")){
$ir="\n";
}
$lettext .= $ir;
}
close(INMLTR);
&get_the_lock;
@thelist=();
open (INFA,"<$BASEDIR/$fields{'filename'}");
while ($tl=)
{
chop $tl;
@ems=split(/$delimiter/,$tl);
push(@thelist,$ems[0]);
}
close(INFA);
&drop_the_lock;
print "Content-type: text/html\n\n";
print "
Mail being sent to subscribed members of $fields{'filename'}\n";
print "using letter $fields{'lfilename'}\n\n";
foreach $em (@thelist)
{
$mailresult=&sendmail($fields{from}, $fields{from}, $em, $SMTP_SERVER, $fields{subject}, $lettext);
if ($mailresult eq "1")
{print "$em: SENT\n";}
else
{print "$em: MAIL NOT SENT: $mailcodes{'$mailresult'}\n";}
}
print"\nProcessing completed!\n";
}
##################################################################
sub get_list
{
if (!-w "$BASEDIR/$fields{'filename'}")
{&error_report("Write permission on requested $fields{'filename'} file are not turned on. Try CHMOD 666 $fields{'filename'}");}
print "Content-type: text/html\n\n";
print <<__HEADER__;
__FOOTER__
}
##################################################################
sub get_the_lock
{
$lockfile="$TEMPDIR/subscribe.lck";
local ($endtime);
$endtime = 60;
$endtime = time + $endtime;
while (-e $lockfile && time < $endtime)
{
# Do Nothing
}
open(LOCK_FILE, ">$lockfile");
}
##################################################################
sub drop_the_lock
{
close($lockfile);
unlink($lockfile);
}
##################################################################
sub decode_vars
{
@killist=();
$i=0;
read(STDIN,$temp,$ENV{'CONTENT_LENGTH'});
@pairs=split(/&/,$temp);
foreach $item(@pairs)
{
($key,$content)=split(/=/,$item,2);
$content=~tr/+/ /;
$content=~s/%(..)/pack("c",hex($1))/ge;
$content=~s/\t/ /g;
$fields{$key}=$content;
if ($key eq "filename" && taint_test($fields{'filename'}) != 1) {
&error_report("mail list filename contains illegal characters.");
}
if ($key eq "lfilename" && taint_test($fields{'lfilename'}) != 1) {
&error_report("letter filename contains illegal characters.");
}
if ($key eq "thisname") {
$content=~s/ //g;
push(@killist,$content);
}
}
}
##################################################################
sub setup
{
$cpr=<<__CPR__;
__CPR__
%mailcodes=('1','success',
'-1', 'smtphost unknown',
'-2', 'socket() failed',
'-3', 'connect() failed',
'-4', 'service not available',
'-5', 'unspecified communication error',
'-6', 'local user to unknown on host smtp',
'-7', 'transmission of message failed',
'-8', 'argument to empty');
}
##################################################################
sub purge_names
{
&get_the_lock;
open (RDR,"<$BASEDIR/$fields{'filename'}");
@biglist=;
close(RDR);
open (ODR,"> $BASEDIR/$fields{'filename'}");
foreach $em(@biglist)
{
$skip=0;
chop $em;
@rms=split(/$delimiter/,$em);
foreach $ds (@killist)
{
if ($rms[0] eq $ds)
{$skip=1;break;}
}
if ($skip == 1)
{next;}
print ODR "$em\n";
}
close(ODR);
&drop_the_lock;
print "Location: $SCRIPT_URL\n\n";
}
##################################################################
sub get_files
{
local($style,$exten) = @_;
local(@items, $item);
opendir(MBZDIR, "$BASEDIR");
@items = grep(/$exten/,readdir(MBZDIR));
closedir(MBZDIR);
$fs="\n";
return $fs;
}
##################################################################
sub ltr_editor
{
if (!-w "$BASEDIR/$fields{'lfilename'}")
{&error_report("Write permission on requested $fields{'lfilename'} file are not turned on. Try CHMOD 666 $fields{'lfilename'}");}
print "Content-type: text/html\n\n";
if ( $fields{'newfile'} eq "NO")
{
$lettext="";
open (INLTR,"<$BASEDIR/$fields{'lfilename'}");
while ($ir=)
{ $lettext .= $ir; }
close(INLTR);
$namehide="";
$header="
EDIT LETTER FILE: $fields{'lfilename'}
";
}
else
{
$header ="
CREATE LETTER FILE: ";
$header .= "
";
$header .= "";
}
print <<__HEADER1__;
__HEADER1__
}
##################################################################
sub post_letter
{
if ( $fields{'newfile'} eq "YES")
{$fn="$fields{'lfilename'}.ltr";}
else {$fn=$fields{'lfilename'};}
open (OTLTR,"> $BASEDIR/$fn");
print OTLTR "$fields{'lettext'}";
close (OTLTR);
print "Location: $SCRIPT_URL\n\n";
}
##################################################################
sub taint_test
{
local($testvalue) = @_;
if ($testvalue=~ /^([-\@\w.]+)$/)
{return 1;}
else {return 0;}
}
##################################################################
sub error_report
{
local($errormsg) = @_;
print "Content-type: text/html\n\n";
print <<__ERROR_MESSAGE__;
The following error has occurred:
$errormsg
__ERROR_MESSAGE__
exit;
}
##################################################################
sub test_dirs
{
if (!-w $BASEDIR)
{&error_report("The BASEDIR does not have write permission turned on! Try CHMOD 777 $BASEDIR");}
if (!-w $TEMPDIR)
{&error_report("The BASEDIR does not have write permission turned on! Try CHMOD 777 $TEMPDIR");}
}
###################################################################
###################################################################
sub sendmail {
# error codes below for those who bother to check result codes
# 1 success
# -1 $smtphost unknown
# -2 socket() failed
# -3 connect() failed
# -4 service not available
# -5 unspecified communication error
# -6 local user $to unknown on host $smtp
# -7 transmission of message failed
# -8 argument $to empty
#
# Sample call:
#
# &sendmail($from, $reply, $to, $smtp, $subject, $message );
#
# Note that there are several commands for cleaning up possible bad inputs - if you
# are hard coding things from a library file, so of those are unnecesssary
#
my ($fromaddr, $replyaddr, $to, $smtp, $subject, $message) = @_;
$to =~ s/[ \t]+/, /g; # pack spaces and add comma
$fromaddr =~ s/.*<([^\s]*?)>/$1/; # get from email address
$replyaddr =~ s/.*<([^\s]*?)>/$1/; # get reply email address
$replyaddr =~ s/^([^\s]+).*/$1/; # use first address
$message =~ s/^\./\.\./gm; # handle . as first character
$message =~ s/\r\n/\n/g; # handle line ending
$message =~ s/\n/\r\n/g;
$smtp =~ s/^\s+//g; # remove spaces around $smtp
$smtp =~ s/\s+$//g;
if (!$to)
{
return(-8);
}
if ($SMTP_SERVER ne "")
{
my($proto) = (getprotobyname('tcp'))[2];
my($port) = (getservbyname('smtp', 'tcp'))[2];
my($smtpaddr) = ($smtp =~
/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)
? pack('C4',$1,$2,$3,$4)
: (gethostbyname($smtp))[4];
if (!defined($smtpaddr))
{
return(-1);
}
if (!socket(MAIL, AF_INET, SOCK_STREAM, $proto))
{
return(-2);
}
if (!connect(MAIL, pack('Sna4x8', AF_INET, $port, $smtpaddr)))
{
return(-3);
}
my($oldfh) = select(MAIL);
$| = 1;
select($oldfh);
$_ = ;
if (/^[45]/)
{
close(MAIL);
return(-4);
}
print MAIL "helo $SMTP_SERVER\r\n";
$_ = ;
if (/^[45]/)
{
close(MAIL);
return(-5);
}
print MAIL "mail from: <$fromaddr>\r\n";
$_ = ;
if (/^[45]/)
{
close(MAIL);
return(-5);
}
foreach (split(/, /, $to))
{
print MAIL "rcpt to: <$_>\r\n";
$_ = ;
if (/^[45]/)
{
close(MAIL);
return(-6);
}
}
print MAIL "data\r\n";
$_ = ;
if (/^[45]/)
{
close MAIL;
return(-5);
}
}
if ($SEND_MAIL ne "")
{
open (MAIL,"| $SEND_MAIL");
}
print MAIL "To: $to\n";
print MAIL "From: $fromaddr\n";
print MAIL "Reply-to: $replyaddr\n" if $replyaddr;
print MAIL "X-Mailer: Perl Powered Socket Mailer\n";
print MAIL "Subject: $subject\n\n";
print MAIL "$message";
print MAIL "\n.\n";
if ($SMTP_SERVER ne "")
{
$_ = ;
if (/^[45]/)
{
close(MAIL);
return(-7);
}
print MAIL "quit\r\n";
$_ = ;
}
close(MAIL);
return(1);
}
##################################################################
sub valid_address
{
local($testmail) = @_;
if ($testmail eq "")
{return 0;}
if ($testmail =~ /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/ ||
$testmail !~ /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/)
{ return 0;}
else
{ return 1;}
}