#!/usr/bin/perl
#==============================================================================
#
# Name: postcard.cgi (Postcard Direct)
#
# Author: Peter Sundstrom (peters@ginini.com.au)
#
# Created: Feb 1999
#
# Source: http://www.ginini.com/software/postcard-direct/
#
# Description: Emails a postcard directly to the recipient.
#
# Copyright: (c)1999-2000 Peter Sundstrom.
# All rights reserved.
#
# See http://www.ginini.com/software/postcard-direct/
# for licence details.
#
#==============================================================================
use CGI::Carp qw(carpout fatalsToBrowser);
#------------------------------------------------------------------------
# YOU MUST SET THE FOLLOWING OPTIONS
#------------------------------------------------------------------------
BEGIN {
# Full directory path containing the "Postcard Direct" essential files,
# like the help file, midi files and configuration files.
$PostcardRoot='/hotel/ole-hansen/WWW/postcard-direct';
# You shouldn't need to modify the next three settings
# Full directory path where the configuration file/s are kept
$ConfigDir="$PostcardRoot/config";
# Name of the default configuration filename if none is specified;
$DefaultCfg="$ConfigDir/default.cfg";
# Full directory path to the additional perl modules
$Modules="$PostcardRoot/modules";
}
#------------------------------------------------------------------------
# END OF CONFIGURABLE OPTIONS
#------------------------------------------------------------------------
$Version='4.2.4';
use File::Basename;
use lib "$Modules";
Error("You need perl 5.004 or greater for this script to run") if ($] < 5.004);
# URL to this script
$CGI=$ENV{'SCRIPT_NAME'};
undef %Data;
$Method = $ENV{'REQUEST_METHOD'};
if ($Method eq 'GET') {
$Query = $ENV{'QUERY_STRING'};
}
else {
read(STDIN,$Query,$ENV{'CONTENT_LENGTH'});
}
Error("Called without any parameters. Need to specify a postcard image") if (! "$Query");
foreach (split(/[&;]/, $Query)) {
s/\+/ /g;
($key, $value) = split('=', $_);
$key =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
$value =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
$Data{$key} = $value;
}
#
# Unbuffer output
#
$|=1;
#
# Check to see what configuration to use
#
$Config="$Data{config}";
if ($Config) {
Error("Invalid Configuration file format. No paths allowed.") if ($Config !~ m#^([\w.-]+)$#);
Error("Configuration file $ConfigDir/$Config does not exist") if ( ! -f "$ConfigDir/$Config");
require "$ConfigDir/$Config";
}
else {
Error("Default configuration file $DefaultCfg does not exist") if (! -f "$DefaultCfg");
require "$DefaultCfg";
$Config=basename("$DefaultCfg");
}
#
# Check to see if the script is being called from a valid location
#
AntiLeech() if $AntiLeech;
#
# Set appropriate URL's
#
($BasePath=$PostcardRoot) =~ s!$TopLevel!!;
$BaseURL= "$WebRoot$BasePath";
$Help="$BaseURL/help.html";
$MidiURL="$BaseURL/" . basename($MidiDir);
#
# Set default design if none is specified
#
$Data{design}='default.design' unless $Data{design};
#
# Check what action has been specified
#
if ($Data{'send'} or $Data{'send.x'}) {
CheckBadData();
SendPostcard();
}
elsif ($Data{'preview'} or $Data{'preview.x'}) {
CheckBadData();
PreviewPostcard();
}
else {
DisplayForm();
}
#--------------------------------------------------------------------
sub DisplayForm {
Error("No postcard image or object specified") unless ($Data{image} or $Data{object});
RemoteSiteAllowed($Data{image}) if ($Data{image} =~ /http:/i);
RemoteSiteAllowed($Data{object}) if ($Data{object} =~ /http:/i);
open(FORM,"$Form") or Error("Can not open postcard form template $Form", $!);
if ($Data{image} and $Data{image} !~ /http:/i) {
$ImagePath=ImageLocation("$Data{image}");
Error("Postcard image not found
URL: $Data{image} Directory path: $ImagePath") if (! -f $ImagePath);
}
elsif ($Data{object} and $Data{object} !~ /http:/i) {
$ObjectPath=ImageLocation("$Data{object}");
Error("Object not found
URL: $Data{object} Directory path: $ObjectPath") if (! -f $ObjectPath);
}
undef $Output;
while (
EOF
}
#-----------------------------------------------------------------------------
# Lock a file
sub LockFile {
my $FH=shift;
my $Status=0;
my $Tries=0;
while ($Status != 0) {
$Status = flock($FH,2);
($Tries == 4) && last;
$Status && sleep(1);
$Tries++;
}
}
#-----------------------------------------------------------------------------
# Unlock a file
sub UnlockFile {
my $FH=shift;
flock($FH,8);
}
#-----------------------------------------------------------------------------
# Display any input errors from the form
sub InputError {
my $Text=shift;
open (INPUTERROR,"$InputError") or Error("Can not open $InputError", $!);
print "Content-type: text/html\n\n";
while () {
next if (/^#/);
s/%MESSAGE%/$Text/g;
s//$Text/ig;
s!%BACK%!
!g;
s!!
!ig;
print;
}
close(INPUTERROR);
exit;
}
#-----------------------------------------------------------------------------
# Check for possible security hacks
sub CheckBadData {
Error("Invalid design name. No paths allowed.",$Data{design}) if ($Data{design} !~ m#^([\w.-]+)$#);
Error("Invalid midi name. No paths allowed",$Data{midi}) if ($Data{midi} !~ m#^([\w.-]+)$# and $Data{midi});
}
#-----------------------------------------------------------------------------
# Anti-leech check. This check, along with all other anti-leech CGI methods
# that rely on the referer are flawed, but people request it, so hey here it is.
sub AntiLeech {
my $Referer=$ENV{'HTTP_REFERER'};
Error("No referer set",$ENV{'REMOTE_ADDR'}) unless $Referer;
foreach (@RefererList) {
return 1 if ($Referer =~ /$_/i);
}
Error("This script can only be run from a valid site",$Referer);
}
#-----------------------------------------------------------------------------
# Display any errors using the template
sub Error {
my ($Text,$Errmsg)=@_;
print "Content-type: text/html\n\n";
ErrorStandard("$Text","$Errmsg") if (! -f $Error);
open (ERROR,"$Error") or ErrorStandard("$Text");
while () {
next if (/^#/);
s/%MESSAGE%/$Text/g;
s/%ERROR%/$Errmsg/g;
s//$Errmsg/g;
s//$Text/ig;
s/%VERSION%/$Version/g;
s//$Version/ig;
print;
}
close(ERROR);
exit;
}
#-----------------------------------------------------------------------------
# Foolproof way to display errors if the error template doesn't exist.
sub ErrorStandard {
my ($Text,$Errmsg)=@_;
require Cwd;
Cwd->import();
my $Dir=cwd();
print <
Postcard Direct Error
$Text
Diagnostics
Error Message: $Errmsg
Full Directory path to this script: $Dir
Postcard Direct Version: $Version
Perl Version: $]
Server Type: $ENV{'SERVER_SOFTWARE'}
HTML
exit;
}
#-----------------------------------------------------------------------------
# Check for a valid email address format. Adapted from Tom Christianson
# ckaddr script
#
sub CheckAddress {
my $address=shift;
return "Incomplete email address" if ($address !~ /\@./);
for ($address) {
s/^-+//;
tr/A-Z/a-z/;
}
($user, $domain) = split /\@/, $address;
return "$Result" if ($Result=rfc822($address));
if ($StrictEmailCheck) {
return "$Result" if ($Result=CheckUser($user));
return "$Result" if ($Result=CheckDomain($domain));
}
}
#-----------------------------------------------------------------------------
sub CheckUser {
my $user=shift;
return("Username: contains only a single character") if length($user) == 1;
study $user;
return("Username: duplicate letters") if $user =~ /(\w)\1{3,}/;
return("Username: contains whitespace") if $user =~ /\s/;
return("Username: contains invalid characters") if $user =~ /[;,\/#^*]/;
return("Username: contains duplicate letters") if $user =~ /^(.)\1+$/;
return("Username: contains no valid characters") unless $user =~ /[a-z0-9]/;
return("Username: backspace") if $user =~ /[\010\177]/;
$letters = "qwertyuiopasdfghjklzxcvbnmmnbvcxzlkjhgfrdsapoiuytrewq";
return("Username: contains consecutive letters") if
length($user) > 2 &&
( index($letters, $user) != -1
||
($user =~ /^(\w+)\1$/ && length($1) > 2
&& index($letters, $1) != -1)
);
}
#-----------------------------------------------------------------------------
sub CheckDomain {
my $domain=shift;
return("incomplete domain name") unless index($domain, '.') >= 0;
study $domain;
return("Domain name: contains whitespace") if $domain =~ /\s/;
return("Domain name: contains invalid characters") if $domain =~ /[;,\/#^*]/;
return("Domain name: must contain letters") unless $domain =~ /[a-z]/;
return("Domain name: contains backspace") if $domain =~ /[\010\177]/;
}
#-----------------------------------------------------------------------------
# Adapted from Sendmail.pm routine by Milivoj Ivkovic
sub Sendmail {
# Error 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 Sender email address invalid
use Socket;
my ($fromaddr, $to, $smtp, $message) = @_;
$to =~ /(<.*>)/;
$to = $1;
$fromaddr =~ /(<.*>)/;
$fromaddr = $1;
$message =~ s/^\./\.\./gm; # handle . as first character
$message =~ s/\r\n/\n/g; # handle line ending
$message =~ s/\n/\r\n/g; # handle line ending
$smtp =~ s/^\s+//g; # remove spaces around $smtp
$smtp =~ s/\s+$//g;
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\r\n";
$_ = ;
if (/^[45]/)
{
close(MAIL);
return(-5,$_);
}
print MAIL "mail from: $fromaddr\r\n";
$_ = ;
if (/^[45]/)
{
close(MAIL);
return(-8,$_);
}
print MAIL "rcpt to: $to\r\n";
$_ = ;
if (/^[45]/)
{
close(MAIL);
return(-6,$_);
}
print MAIL "data\r\n";
$_ = ;
if (/^[45]/)
{
close MAIL;
return(-5,$_);
}
print MAIL "$message";
print MAIL "\r\n.\r\n";
$_ = ;
if (/^[45]/)
{
close(MAIL);
return(-7,$_);
}
print MAIL "quit\r\n";
$_ = ;
close(MAIL);
return(1);
}
#-----------------------------------------------------------------------------
sub rfc822 {
# rfc822 -- check whether address is valid rfc 822 address
# tchrist@perl.com
#
# pattern developed in program by jfriedl;
# see "Mastering Regular Expressions" from ORA for details
my $address = shift;
local $_;
undef $RFC822PAT;
while () {
chomp;
$RFC822PAT .= $_;
}
return($address =~ /^$RFC822PAT$/o ? 0 : "Invalid email address format");
}
# don't touch this stuff down here or you'll break the rfc822 matcher.
__END__
(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n
\015()]|\\[^\x80-\xff])*\))*\))*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\
xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"(?:[^\\\x80-\xff\n\015"
]|\\[^\x80-\xff])*")(?:(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xf
f]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*\.(?:[\040\t]|\((?:[
^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\
xff])*\))*\))*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;
:".\\\[\]\000-\037\x80-\xff])|"(?:[^\\\x80-\xff\n\015"]|\\[^\x80-\xff])*"))
*(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\
n\015()]|\\[^\x80-\xff])*\))*\))*@(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\
\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*(?:[^(\04
0)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-
\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])(?:(?:[\040\t]|\((?
:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80
-\xff])*\))*\))*\.(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\(
(?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*(?:[^(\040)<>@,;:".\\\[\]
\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\
\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\]))*|(?:[^(\040)<>@,;:".\\\[\]\000-\0
37\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"(?:[^\\\x80-\xf
f\n\015"]|\\[^\x80-\xff])*")(?:[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\03
7]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\
\[^\x80-\xff])*\))*\)|"(?:[^\\\x80-\xff\n\015"]|\\[^\x80-\xff])*")*<(?:[\04
0\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]
|\\[^\x80-\xff])*\))*\))*(?:@(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x
80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*(?:[^(\040)<>@
,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]
)|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])(?:(?:[\040\t]|\((?:[^\\
\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff
])*\))*\))*\.(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^
\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*(?:[^(\040)<>@,;:".\\\[\]\000-
\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-
\xff\n\015\[\]]|\\[^\x80-\xff])*\]))*(?:(?:[\040\t]|\((?:[^\\\x80-\xff\n\01
5()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*,(?
:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\0
15()]|\\[^\x80-\xff])*\))*\))*@(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^
\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*(?:[^(\040)<
>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xf
f])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])(?:(?:[\040\t]|\((?:[^
\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\x
ff])*\))*\))*\.(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:
[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*(?:[^(\040)<>@,;:".\\\[\]\00
0-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x8
0-\xff\n\015\[\]]|\\[^\x80-\xff])*\]))*)*:(?:[\040\t]|\((?:[^\\\x80-\xff\n\
015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*)
?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000
-\037\x80-\xff])|"(?:[^\\\x80-\xff\n\015"]|\\[^\x80-\xff])*")(?:(?:[\040\t]
|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[
^\x80-\xff])*\))*\))*\.(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xf
f]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*(?:[^(\040)<>@,;:".\
\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"(?:
[^\\\x80-\xff\n\015"]|\\[^\x80-\xff])*"))*(?:[\040\t]|\((?:[^\\\x80-\xff\n\
015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*@
(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n
\015()]|\\[^\x80-\xff])*\))*\))*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff
]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\
]]|\\[^\x80-\xff])*\])(?:(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\
xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*\.(?:[\040\t]|\((?
:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80
-\xff])*\))*\))*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@
,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff
])*\]))*(?:[\040\t]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff]|\((?:[^\\\x8
0-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*>)(?:[\040\t]|\((?:[^\\\x80-\xff\n\
015()]|\\[^\x80-\xff]|\((?:[^\\\x80-\xff\n\015()]|\\[^\x80-\xff])*\))*\))*