#!/usr/bin/perl
#--------------------------------------------------------------------------------#
# PROGRAM: gmailsmtp.pl                                                          #
#          This program was designed to act as a proxy SMTP server on a          #
#          linux system that relays its outbound mail through a gmail            #
#          account via HTTPS.                                                    #
#                                                                                #
# Copyright (C) 2005 Frederik Vanrenterghem                                      #
#                                                                                #
# This program is free software; you can redistribute it and/or                  #
# modify it under the terms of the GNU General Public License                    #
# as published by the Free Software Foundation; either version 2                 #
# of the License, or (at your option) any later version.                         #
#                                                                                #
# This program is distributed in the hope that it will be useful,                #
# but WITHOUT ANY WARRANTY; without even the implied warranty of                 #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                  #
# GNU General Public License for more details.                                   #
#                                                                                #
# You should have received a copy of the GNU General Public License              #
# along with this program; if not, write to the Free Software                    #
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.#
#                                                                                #
# CREATED: 2005/05/21 by Frederik Vanrenterghem <frederik@vanrenterghem.biz>     #
#                                                                                #
# VERSION: 0.1.0                                                                 #
#                                                                                #
# CREDITS: James C. Specht, Jr. <james.specht@gmail.com>                         #
#            For his gsr.pl v.0.8.0, which supplied the majority                 #
#            of the code for v.0.1.0                                             #
#                                                                                #
#--------------------------------------------------------------------------------#

use Mail::Webmail::Gmail;
use Net::SMTP::Server;
use Net::SMTP::Server::Client;
use IO::Socket::INET;
use IO::Socket::SSL;
use Net::SSLeay;
use Digest::HMAC_MD5 qw(hmac_md5_hex);
use MIME::Base64 qw(encode_base64 decode_base64);
use Getopt::Long;
use Term::ReadKey;
use Proc::Daemon;
use Sys::Syslog;

$SIG{CHLD} = 'IGNORE';

my ($configfile,$ehlo_ok,$from,@to,$msgsrc,$dohelp,$sock,$code,$text,$more, 
    %features,$server,$conn,$verbose,$version,$daemon,$relayfile,@relayIP,
    $pidfile,$client);

$configfile="/usr/local/etc/gmailsmtp.conf";
$relayfile="/usr/local/etc/gmailsmtp.relay";
$pidfile="/usr/local/var/run/gmailsmtp.pid";
$verbose=0;
$daemon=0;
$version="0.1.0";

# Get command line options.
GetOptions ('config-file=s' => \$configfile,
            'relay-file=s' => \$relayfile,
            'verbose:1' => \$verbose,
            'daemon:1' => \$daemon,
            'help:1' => \$dohelp );

require "$configfile";

if ($dohelp == 1 ) {&usage();}

# Force this process out as a daemon.
if ($daemon >= 1) { 
  Proc::Daemon::Init; 
  # Since we are a daemon process log to syslog facility.
  openlog('gmailsmtp','pid','mail'); 
  syslog('info','gmailsmtp.pl daemon process loading');
  $verbose=0;
  # Write our pid out.
  open(FPID, ">$pidfile");
  print FPID "$$";
  close(FPID);
} 

$ehlo_ok = 1;

if ($gmailserver =~ /^(.*):(.*)$/) {
  $gmailserver = $1;
  $gmailport = $2;
}

if ($daemon >= 1) 
  { syslog('info',"Gmail relay set to $gmailserver:$gmailport"); }

if (!defined ($gmailusername)) { 
  dienice ("Missing \$gmailusername in $configfile!"); 
}

syslog('info',"Gmail user account is $gmailusername") if ($daemon >= 1);

if ($gmailpassword eq "") {
  # We are a daemon so we can't asked for the gmail account password!
  if ($daemon >= 1) {
    syslog('info',"Missing \$gmailpassword in $configile!");
    syslog('info','Aborting startup!');
    exit;
  }
  printf ("Enter password for %s : ", $gmailusername);
  # Set echo off.
  ReadMode (2);
  $gmailpassword = <>;
  # Restore echo.
  ReadMode (0);
  printf ("\n");

  exit if (! defined ($gmailpassword));
  chop ($gmailpassword);
}

# Build the relayIP table
BuildRelayIPTable();

&StartSMTPServer();
closelog();
exit();

sub StartSMTPServer 
{
  my $i, $IPaddr, $size, $nrcpt;

  $server = new Net::SMTP::Server($localserver,$localport) ||
    dienice ("Unable to start SMTP Server on $localserver:$localport!"); 

  while($conn = $server->accept()) {
    printf("\nNew connection!\n\n") if ($verbose >= 1);
    
    my $pid = fork();

    if ($pid) {undef($conn);} # this is the parent process
    dienice ("Can't create child process!") unless defined $pid; 
    unless($pid) { # $pid == 0 means we're the child

      $client = new Net::SMTP::Server::Client($conn) ||
        dienice ("Unable to start SMTP client connection!"); 

      # Capture the IP address of our client.
      $IPaddr = $client->{SOCK}->peerhost();

      syslog('info',"Incoming connection from [$IPaddr]") if ($daemon >= 1);
      printf("\nConnection from [$IPaddr]\n") if ($verbose >= 1);

      # Do we allow this IP to relay through us?
      if (AllowRelay($IPaddr) == 0) {
        warnnice("Connection refused to [$IPaddr]");
      } 
          
      if ($client->process) {
        if ($verbose >= 1 ) {
          printf("MAIL FROM: $client->{FROM}\n");
          for ($i=0; $i <= $#client->{TO}; $i++) {
            printf("RCPT TO: $client->{TO}[i]\n");
          }
          printf("DATA:\n\n$client->{MSG}\n");
        }
        # Now relay the msg on.
        $from   = $client->{FROM};
        for ($i=0; $i <= $#client->{TO}; $i++) 
          { $to[$i] = $client->{TO}[$i];}
        $msgsrc = $client->{MSG}; 

        if ($daemon >= 1) {
          $size = length($msgsrc);
          $nrcpt = $#to + 1;
          syslog('info',"from=$from, size=$size, nrcpt=$nrcpt");
          for ($i=0; $i <= $#to; $i++) 
            { syslog('info',"to=$to[$i]"); }
        }

        printf("Relay via GMail account $gmailusername...\n") if ($verbose >= 1);
        if ($daemon >= 1) 
          { syslog('info',"relay via GMail account $gmailusername"); }

        &RelayOurMSG();
        #$client->shutdown;
        exit 1;
      }
    }
    undef $client;
  }
}

sub RelayOurMSG
{
###nieuw vanaf hier
if ($verbose >= 1) 
        {printf("Connecting to GMail...\n");}
    my ( $gmail ) = Mail::Webmail::Gmail->new(
            username => $gmailusername, password => $gmailpassword, proxy_name=> $proxy_name, );

if ($verbose >= 1) 
        {printf("Connection to Gmail established.\n");}	

if ($verbose >= 1) 
        {printf("Attempting to send message.\n");}
        
	my $msgid = $gmail->send_message
    ( to => $to[0],
      subject => time(), 
      msgbody => $msgsrc );
    print "Msgid: $msgid\n";
    if ( $msgid ) {
        if ( $gmail->error() ) {
        	if ($verbose >= 1) 
        		{printf("We ran into a problem. Attempting to display the problem...\n");}
            print $gmail->error_msg();
        } else {
            ### Create new label ###
            if ($verbose >= 1) 
        		{printf("Creating new label\n");}
            my $test_label = "tl_" . time();
            $gmail->edit_labels( label => $test_label, action => 'create' );
            if ( $gmail->error() ) {
                print $gmail->error_msg();
            } else {
                ### Add this label to our new message ###
                $gmail->edit_labels( label => $test_label, action => 'add', 'msgid' => $msgid );
                if ( $gmail->error() ) {
                    print $gmail->error_msg();
                } else {
                    print "Added label: $test_label to message $msgid\n";
                }
            }
        }
    }
###tot hier

  # Good bye...
    if ($verbose >= 1) 
        {printf("Message sent succesfully.\n");} 
    if ($daemon >= 1) {
      syslog('info',"Message relay successful");
    }
}

# This is the main SMTP "engine".
sub run_smtp
{


  # We can do a relay-test now if a recipient was set.
  if ($#to >= 0) {
    if (!defined ($from)) {
      $from = "<$gmailusername>";
      if ($verbose >= 1) 
        {printf("From: address not set.  Using $gmailusername.\n");}
      if ($daemon >= 1)
        {syslog('info',"From: address not set. Using $gmailusername.");}
    }
    &send_line ($sock, "MAIL FROM: %s\n", $from);
    ($code, $text, $more) = &get_line ($sock);
    if ($code != 250) { warnnice ("MAIL FROM failed: '$code $text'\n"); }

    my $i;
    for ($i=0; $i <= $#to; $i++) {
      &send_line ($sock, "RCPT TO: %s\n", $to[$i]);
      ($code, $text, $more) = &get_line ($sock);
      if ($code != 250) {
        warnnice ("RCPT TO ".$to[$i]." "."failed: '$code $text'\n");
      }
    }
  }

  # Wow, we should even send something!
  if (defined ($msgsrc))
  {
    &send_line ($sock, "DATA\n");
    ($code, $text, $more) = &get_line ($sock);
    if ($code != 354) { warnnice ("DATA failed: '$code $text'\n"); }

    if ($verbose >= 1) {
      printf("Sending the following.\n\n");
      printf("$msgsrc\n");
    }

    $sock->printf ("$msgsrc\r\n.\r\n");

    ($code, $text, $more) = &get_line ($sock);
    if ($code != 250) { warnnice ("DATA not sent: '$code $text'\n"); }
  }

  # Perfect. Everything succeeded!
  return 1;
}

# Get one line of response from the server.
sub get_line ($)
{
  my $sock = shift;
  my ($code, $sep, $text) = ($sock->getline() =~ /(\d+)(.)([^\r]*)/);
  my $more;
  if ($sep eq "-") { $more = 1; } else { $more = 0; }
  printf ("[%d] '%s'\n", $code, $text) if ($verbose >= 1);
  return ($code, $text, $more);
}

# Send one line back to the server
sub send_line ($@)
{
  my $socket = shift;
  my @args = @_;
  
  printf ("> ") if ($verbose >= 1);
  printf (@args) if ($verbose >= 1);
  $socket->printf (@args);
}

# Helper function to encode CRAM-MD5 challenge
sub encode_cram_md5 ($$$)
{
  my ($ticket64, $username, $password) = @_;
  my $ticket = decode_base64($ticket64) or
     dienice ("Unable to decode Base64 encoded string '$ticket64'");

  my $password_md5 = hmac_md5_hex($ticket, $password);
  return encode_base64 ("$username $password_md5", "");
}

# Store all server's ESMTP features to a hash.
sub say_hello ($$$$)
{
  my ($sock, $ehlo_ok, $gmailhello, $featref) = @_;
  my ($feat, $param);
  my $hello_cmd = $ehlo_ok ? "EHLO" : "HELO";

  &send_line ($sock, "$hello_cmd $gmailhello\n");
  my ($code, $text, $more) = &get_line ($sock);

  if ($code != 250) { warnnice ("$hello_cmd failed: '$code $text'\n"); }

  # Empty the hash
  %{$featref} = ();

  ($feat, $param) = ($text =~ /^(\w+)[= ]*(.*)$/);
  $featref->{$feat} = $param;

  # Load all features presented by the server into the hash
  while ($more == 1) {
    ($code, $text, $more) = &get_line ($sock);
    ($feat, $param) = ($text =~ /^(\w+)[= ]*(.*)$/);
    $featref->{$feat} = $param;
  }

  return 1;
}

sub warnnice {
  my ($txt) = @_;

  if ( $daemon >= 1 ) { 
    syslog('info', $txt); 
    syslog('info', 'Connection terminated!'); 
  }
  else { 
    printf("\n$txt\n"); 
    printf("Connection terminated!\n"); 
  }
  if ($sock) {
    printf("Shutdown!!!!!\n");
    &send_line ($sock, "QUIT\n");
  }
  if ($client) { $client->shutdown;}
  exit 1;
}

sub dienice {
  my ($txt) = @_;

  if ( $daemon >= 1 ) { 
    syslog('info', $txt);
    syslog('info',"Aborting");
  }
  else { 
    printf("\n$txt\n"); 
    printf("Aborting\n\n");
  }
  if ($client) {$client->shutdown;}
  exit 1;
}

sub AllowRelay($) {

  my $IP = shift;
  my $i;

  for ($i=0; $i <= $#relayIP; $i++) {
    if ($IP =~ /^$relayIP[$i]/) { return "1";}
  }

  # Did not find a match.
  return "0";  
}

sub BuildRelayIPTable {

  my ($i);

  $i=0;

  if ($verbose >= 1) { printf("Loading relay table from $relayfile\n");}
  if ($daemon >= 1) { syslog('info',"Loading relay table from $relayfile");}

  open(INFILE, "$relayfile") || dienice("Cannot open relay file $relayfile!");
  while($line = <INFILE>) {
    $line =~ s/#.*//;            # ignore comments by erasing them
    next if $line =~ /^(\s)*$/;  # skip blank lines
    chomp($line);
    if ($verbose >= 1) { printf("Adding [$line] to relay table\n");}
    if ($daemon >= 1) {syslog('info',"Adding [$line] to relay table");}

    $relayIP[$i++] = $line;
  }
  close(INFILE);
}

sub usage ()
{
  printf ("\nA simple SMTP server that relays local email through a gmail account via HTTPS.

Author: Frederik Vanrenterghem <frederik\@vanrenterghem.biz>
        http://vanrenterghem.biz/

Version: $version

Usage: gmailsmtp.pl [--options]

        --config-file=          The location of the gmailsmtp.conf file.
                                (default: /usr/local/etc/gmailsmtp.conf)
        --relay-file=           The location of the gmailsmtp.relay file.
                                (default: /usr/local/etc/gmailsmtp.relay)
        --verbose		Be verbose instead of silent.
        --daemon		Start as daemon process.
        --help                  Shows you this output.           

Current Options:

	\$gmailserver	=	[$gmailserver]
	\$gmailport	=	[$gmailport]
	\$gmailusername	=	[$gmailusername]\n");

  if ($gmailpassword ne "") {
    printf ("	\$gmailpassword	=	[is set]\n");
  } else {
    printf ("	\$gmailpassword	=	[is NOT set]\n");
  } 

  printf ("	\$gmailhello	=	[$gmailhello]
	\$localserver	=	[$localserver]
	\$localport	=	[$localport]
");
        exit (0);
}

 