This program looks really stupid, but it makes it possible for me to
do something in less than a minute (namely, send out all the email
I've written) that would take many minutes with, say, a webmail
interface at an internet cafe.

Here in Peru, there are lots of internet cafes (mostly 1 sol per hour
or 50 centimos per 15 minutes --- a sol is about 30 cents, so this is
half or a third of the price in Ecuador), but I don't trust the
machines in them to have access to my accounts.  But I can't hook up
Beatrice's laptop to their network, since the Ethernet port is broken,
and it would be somewhat imprudent anyway to go around flashing a
$1000 brand new laptop in public places like that.

So I wrote this CGI script, which lets me upload a batch of mail with
a one-time password in the first line of the file.  It pipes it to a
mail-batch-sending program I previously wrote (which I posted here on
kragen-hacks years ago).  It took me a while to get it working because
I forgot that $/ wasn't per-filehandle in Perl the way $| is.  My bug
was clearly explained in the first ten paragraphs of perldoc perlvar,
but it took me about half an hour to debug, since the web server was
on the other end of a flaky wireless connection, and Beatrice's Mac
doesn't come with a web server.

I keep the mail passwords in plaintext because I'm not worried that
someone who has read access to the files on my server will want to
steal passwords that let them send email from it (and prevent me from
doing same).

I picked Perl because I'm more familiar with CGI.pm than cgi.py, and I
knew it could handle file uploads, but in retrospect this probably
would have been simpler in Python, for the following reasons: 
- cgi.py's interface for getting uploaded file contents was what I
  went to 7 lines of trouble to duplicate here, which doesn't sound
  bad except that one of them had a bug that took me half an hour to
  track down due to Perl's gratuitous use of a global variable
- 8 different lines here have code to explicitly raise an exception if
  an I/O operation fails, which is what I wanted and happens to be the
  default behavior in Python

On the other hand, this Mac has the standard library documentation for
Perl (where it is embedded in the modules) but not Python (although it
does have what little documentation is embedded, which rarely
suffices), and I would have had to hand-code the HTML.

#!/usr/bin/perl -w
# vim:tw=80:sw=4:ai:et:ts=4
use strict;
use CGI qw(:standard);
my $password_filename = '/home/kragen/mail-passwords';
my $mailmsgpy = "/home/kragen/mailmsg.py";
$|++;

sub get_uploaded_file {
    my ($name) = @_;
    my $param = param($name);
    no strict 'refs';
    local $/;
    return scalar <$param>;
}

sub read_file_chomped {
    my ($fname) = @_;
    open INFILE, "<$fname" or die "Can't open $fname: $!";
    my @lines = <INFILE>;
    close INFILE;
    chomp @lines;
    return @lines;
}

sub write_file_chomped {
    my ($fname, @contents) = @_;
    open OUTFILE, ">$fname.new" or die "Can't open $fname.new: $!";
    print OUTFILE map { $_, "\n" } @contents or 
        die "Can't write to $fname.new: $!";
    close OUTFILE or die "Can't close $fname.new: $!";
    rename "$fname.new", $fname or die "Can't rename $fname.new to $fname: $!";
}

sub main {
    if (request_method eq 'GET') {
        print header, start_html("upload file"),
            h1('upload file'),
            start_multipart_form,
            filefield('file'),
            submit, end_form, end_html;
    } elsif (request_method eq 'POST') {
        my $data = get_uploaded_file('file');
        my $password;
        $password = $1 if $data =~ s/(.*)\n//;
        my @passwords = read_file_chomped $password_filename;
        unless (grep { $_ eq $password } @passwords) {
            print header, start_html('bad password'),
                h1('bad password'),
                p("Nope, I don't see " . escapeHTML($password)),
                end_html;
            return;
        }
        write_file_chomped($password_filename, 
                           grep { $_ ne $password } @passwords);
        print header, start_html('sending email'), "<pre>\n";
        open MAIL, "|$mailmsgpy send" or die "Can't pipe: $!";
        print MAIL $data or die "Can't write to pipe: $!";
        close MAIL or die "Can't close pipe: $!";
        print "</pre>", p("done"), end_html;
    } else {
        die "Bad request method";
    }
}
main;

Reply via email to