Automatisches Build-Script
authordelaco <delaco@ea5c0bd1-69bd-2848-81d8-4f18e57aeed8>
Mon, 31 Jul 2006 17:54:27 +0000 (17:54 +0000)
committerdelaco <delaco@ea5c0bd1-69bd-2848-81d8-4f18e57aeed8>
Mon, 31 Jul 2006 17:54:27 +0000 (17:54 +0000)
Proxy-ACL gefixt.

git-svn-id: http://svn.ipfire.org/svn/ipfire/trunk@235 ea5c0bd1-69bd-2848-81d8-4f18e57aeed8

doc/packages-list.txt
html/cgi-bin/proxy.cgi
make.sh
tools/sendEmail [new file with mode: 0755]

index 6a402ac..38eba85 100644 (file)
   * inetutils-1.4.2
   * ipac-ng-1.31
   * ipaddr-1.2
+  * ipfire-logs-2006-07-31-12:30.tgz
+  * ipfire-logs-2006-07-31-12:48.tgz
+  * ipfire-logs-2006-07-31-12:51.tgz
+  * ipfire-logs-2006-07-31-12:53.tgz
+  * ipfire-logs-2006-07-31-12:55.tgz
+  * ipfire-logs-2006-07-31-13:06.tgz
+  * ipfire-logs-2006-07-31-17:33.tgz
   * iproute2-2.4.7-now-ss010824
   * iptables-1.3.5
   * iptstate-1.4
   * razor-agents-2.81
   * rp-pppoe-3.5
   * rrdtool-1.0.49
-  * samba-3.0.22
   * samba-3.0.23a
   * sata300_sataii150_tx_series_linux2.4_open_source_1.00.0.15
   * screen-4.0.2
index 269ce36..22d7757 100644 (file)
@@ -2913,7 +2913,7 @@ acl Safe_ports port 777 # multiling http
 acl Safe_ports port 800 # Squids port (for icons)
 
 acl IPCop_http  port 81
-acl IPCop_https port 445
+acl IPCop_https port 444
 acl IPCop_ips              dst $netsettings{'GREEN_ADDRESS'}
 acl IPCop_networks         src "$acl_src_subnets"
 acl IPCop_green_network    src $netsettings{'GREEN_NETADDRESS'}/$netsettings{'GREEN_NETMASK'}
diff --git a/make.sh b/make.sh
index e2baff5..d774d4d 100644 (file)
--- a/make.sh
+++ b/make.sh
   MAX_RETRIES=3                        # prefetch/check loop
   KVER=`grep --max-count=1 VER lfs/linux | awk '{ print $3 }'`
   MACHINE=`uname -m`
+  SVN_REVISION=`svn info | grep Revision | cut -c 11-`
 
   # Setzen des IPFire Builds
   if [ -e ./.svn ]; then
     FIREBUILD=`cat .svn/entries |sed -n 's/^[ \t]*revision=\"// p' | sed -n 's/\".*$// p'`
-#    VERSION="$VERSION (Build:$FIREBUILD)"
   fi
 
   # Debian specific settings
   DIR_CHK=$BASEDIR/cache/check
   mkdir $BASEDIR/log/ 2>/dev/null
 
+  if [ -f .config ]; then
+       . .config
+  fi
+
   if [ 'x86_64' = $MACHINE -o 'i686' = $MACHINE -o 'i586' = $MACHINE -o 'i486' = $MACHINE -o 'i386' = $MACHINE ]; then
        echo "`date -u '+%b %e %T'`: Machine is ix86 (or equivalent)" >> $LOGFILE
        MACHINE=i386
@@ -890,6 +894,11 @@ ipfirepackages() {
   rm -rf  $BASEDIR/build/install/packages/*
 }
 
+update_logs() {
+       tar cfz log/ipfire-logs-`date +'%Y-%m-%d-%H:%M'`.tgz log/_build.*
+       rm -f log/_build.*
+}
+
 # See what we're supposed to do
 case "$1" in 
 build)
@@ -1121,7 +1130,7 @@ gettoolchain)
 svn)
        case "$2" in
          update|up)
-               clear
+               clear
                echo -n "Load the latest source files..."
                svn update >> $PWD/log/_build.svn.update.log
                if [ $? -eq 0 ]; then
@@ -1138,6 +1147,7 @@ svn)
                        echo ".Fail!"
                        exit 1
                fi
+               exit 0
          ;;
          commit|ci)
                clear
@@ -1186,19 +1196,104 @@ svn)
          ;;
        esac
        ;;
-sync)
-       echo -e "Syncing Cache to FTP:"
-       if [ -f .pass ]; then
-               PASS="`cat .pass`"
+make-config)
+       echo -e "This is for creating your configuration..."
+       echo -e "ATTENTION: Even your password will be shown when typed!"
+       echo -e "We will need some input:"
+       echo -e ""
+       echo -n "FTP-DOMAIN FOR THE ISO: "
+       read IPFIRE_FTP_URL_EXT
+       echo -n "PATH FOR $IPFIRE_FTP_URL_EXT: "
+       read IPFIRE_FTP_PATH_EXT
+       echo -n "USERNAME FOR $IPFIRE_FTP_URL_EXT: "
+       read IPFIRE_FTP_USER_EXT
+       echo -n "PASSWORD FOR $IPFIRE_FTP_URL_EXT: "
+       read IPFIRE_FTP_PASS_EXT
+       echo ""
+       echo "(You can leave this empty if the cache-server is the same as your iso-server.)"
+       echo -n "FTP-DOMAIN FOR THE CACHE: "
+       read IPFIRE_FTP_URL_INT
+       echo -n "PATH FOR $IPFIRE_FTP_URL_INT: "
+       read IPFIRE_FTP_PATH_INT
+       if [ $IPFIRE_FTP_URL_INT ]; then
+               echo -n "USERNAME FOR $IPFIRE_FTP_URL_INT: "
+               read IPFIRE_FTP_USER_INT
+               echo -n "PASSWORD FOR $IPFIRE_FTP_URL_INT: "
+               read IPFIRE_FTP_PASS_INT
+       else
+               IPFIRE_FTP_URL_INT=$IPFIRE_FTP_URL_EXT
+               IPFIRE_FTP_USER_INT=$IPFIRE_FTP_USER_EXT
+               IPFIRE_FTP_PASS_INT=$IPFIRE_FTP_PASS_EXT
+               echo "USERNAME FOR $IPFIRE_FTP_URL_INT: $IPFIRE_FTP_USER_INT"
+               echo "PASSWORD FOR $IPFIRE_FTP_URL_INT: $IPFIRE_FTP_PASS_INT"
+       fi
+       echo ""
+       echo "(You can leave this empty if the pak-server is the same as your iso-server.)"
+       echo -n "FTP-DOMAIN FOR THE PAKS: "
+       read IPFIRE_FTP_URL_PAK
+       echo -n "PATH FOR $IPFIRE_FTP_URL_PAK: "
+       read IPFIRE_FTP_PATH_PAK
+       if [ $IPFIRE_FTP_URL_PAK ]; then
+               echo -n "USERNAME FOR $IPFIRE_FTP_URL_PAK: "
+               read IPFIRE_FTP_USER_PAK
+               echo -n "PASSWORD FOR $IPFIRE_FTP_URL_PAK: "
+               read IPFIRE_FTP_PASS_PAK
        else
-               echo -ne "Password for mirror.ipfire.org: "; read PASS
+               IPFIRE_FTP_URL_PAK=$IPFIRE_FTP_URL_EXT
+               IPFIRE_FTP_USER_PAK=$IPFIRE_FTP_USER_EXT
+               IPFIRE_FTP_PASS_PAK=$IPFIRE_FTP_PASS_EXT
+               echo "USERNAME FOR $IPFIRE_FTP_URL_PAK: $IPFIRE_FTP_USER_PAK"
+               echo "PASSWORD FOR $IPFIRE_FTP_URL_PAK: $IPFIRE_FTP_PASS_PAK"
        fi
+       echo ""
+       echo -e "ONE OR MORE EMAIL ADDRESS(ES) TO WHICH THE REPORTS WILL BE SENT"
+       echo -e "(seperated by comma)"
+       read IPFIRE_MAIL_REPORT
+       echo -n "EMAIL FROM: "
+       read IPFIRE_MAIL_FROM
+       echo -n "EMAIL SERVER: "
+       read IPFIRE_MAIL_SERVER
+       echo -n "LOGIN TO MAIL SERVER: "
+       read IPFIRE_MAIL_USER
+       echo -n "MAIL PASSWORD: "
+       read IPFIRE_MAIL_PASS
+       echo -n "Saving..."
+       for i in `seq 20`; do
+               sleep 0.1; echo -n "."
+       done
+       echo ".Finished!"
+       cat <<END > .config
+### ISO server
+IPFIRE_FTP_URL_EXT=$IPFIRE_FTP_URL_EXT
+IPFIRE_FTP_PATH_EXT=$IPFIRE_FTP_PATH_EXT
+IPFIRE_FTP_USER_EXT=$IPFIRE_FTP_USER_EXT
+IPFIRE_FTP_PASS_EXT=$IPFIRE_FTP_PASS_EXT
+### cache server
+IPFIRE_FTP_URL_INT=$IPFIRE_FTP_URL_INT
+IPFIRE_FTP_PATH_INT=$IPFIRE_FTP_PATH_INT
+IPFIRE_FTP_USER_INT=$IPFIRE_FTP_USER_INT
+IPFIRE_FTP_PASS_INT=$IPFIRE_FTP_PASS_INT
+### paks server
+IPFIRE_FTP_URL_PAK=$IPFIRE_FTP_URL_PAK
+IPFIRE_FTP_PATH_PAK=$IPFIRE_FTP_PATH_PAK
+IPFIRE_FTP_USER_PAK=$IPFIRE_FTP_USER_PAK
+IPFIRE_FTP_PASS_PAK=$IPFIRE_FTP_PASS_PAK
+### mail reports
+IPFIRE_MAIL_REPORT=$IPFIRE_MAIL_REPORT
+IPFIRE_MAIL_FROM=$IPFIRE_MAIL_FROM
+IPFIRE_MAIL_SERVER=$IPFIRE_MAIL_SERVER
+IPFIRE_MAIL_USER=$IPFIRE_MAIL_USER
+IPFIRE_MAIL_PASS=$IPFIRE_MAIL_PASS
+END
+       ;;
+sync)
+       echo -e "Syncing cache to ftp:"
        rm -f doc/packages-to-remove-from-ftp
-       ncftpls -u web3 -p $PASS ftp://mirror.ipfire.org/html/source-packages/source/ > ftplist
+       ncftpls -u $IPFIRE_FTP_USER_INT -p $IPFIRE_FTP_PASS_INT ftp://$IPFIRE_FTP_URL_INT$IPFIRE_FTP_PATH_INT/ > ftplist
        for i in `ls -w1 cache/`; do
                grep $i ftplist
                if [ "$?" -ne "0" ]; then
-                       ncftpput -u web3 -p $PASS mirror.ipfire.org /html/source-packages/source cache/$i
+                       ncftpput -u $IPFIRE_FTP_USER_INT -p $IPFIRE_FTP_PASS_INT $IPFIRE_FTP_URL_INT $IPFIRE_FTP_PATH_INT/ cache/$i
                        if [ "$?" -eq "0" ]; then
                                echo -e "$i was successfully uploaded to the ftp server."
                        else
@@ -1214,42 +1309,35 @@ sync)
        done
        rm -f ftplist
        ;;
-pub-iso)
-       echo -e "Upload the ISO to the beta-mirror!"
-       if [ -f .pass ]; then
-               PASS="`cat .pass`"
-       else
-               echo -ne "Password for mirror.ipfire.org: "; read PASS
-       fi
-       ncftpls -u web3 -p $PASS ftp://mirror.ipfire.org/html/source-packages/beta/ | grep `svn info | grep Revision | cut -c 11-`
-       if [ "$?" -eq "1" ]; then
-                       cp $BASEDIR/ipfire-install-1.4.i386.iso $BASEDIR/ipfire-install-1.4.i386-r`svn info | grep Revision | cut -c 11-`.iso
-                       md5sum ipfire-install-1.4.i386-r`svn info | grep Revision | cut -c 11-`.iso > ipfire-install-1.4.i386-r`svn info | grep Revision | cut -c 11-`.iso.md5
-                       ncftpput -u web3 -p $PASS mirror.ipfire.org /html/source-packages/beta/ ipfire-install-1.4.i386-r`svn info | grep Revision | cut -c 11-`.iso
-                       ncftpput -u web3 -p $PASS mirror.ipfire.org /html/source-packages/beta/ ipfire-install-1.4.i386-r`svn info | grep Revision | cut -c 11-`.iso.md5
-                       if [ "$?" -eq "0" ]; then
-                               echo -e "The ISO of Revision `svn info | grep Revision | cut -c 11-` was successfully uploaded to the ftp server."
-                       else
-                               echo -e "There was an error while uploading the ISO to the ftp server."
-                       fi
-       else
-               echo -e "File with name ipfire-install-1.4.i386-r`svn info | grep Revision | cut -c 11-`.iso already exists on the ftp server!"
-       fi
-       rm -f ipfire-install-1.4.i386-r`svn info | grep Revision | cut -c 11-`.iso{,.md5}
-       ;;
-pub-paks)
-       echo -e "Upload the packages to the beta-mirror!"
-       if [ -f .pass ]; then
-               PASS="`cat .pass`"
-       else
-               echo -ne "Password for mirror.ipfire.org: "; read PASS
-       fi
-       ncftpput -z -u web3 -p $PASS mirror.ipfire.org /html/source-packages/packages/ packages/*
-       if [ "$?" -eq "0" ]; then
-               echo -e "The packages were successfully uploaded to the ftp server."
-       else
-               echo -e "There was an error while uploading the packages to the ftp server."
-       fi
+upload)
+       case "$2" in
+         iso)
+               echo -e "Uploading the iso to $IPFIRE_FTP_URL_EXT."
+               ncftpls -u $IPFIRE_FTP_USER_EXT -p $IPFIRE_FTP_PASS_EXT ftp://$IPFIRE_FTP_URL_EXT$IPFIRE_FTP_PATH_EXT/ | grep $SVN_REVISION
+               if [ "$?" -eq "1" ]; then
+                               cp $BASEDIR/ipfire-install-$VERSION.i386.iso $BASEDIR/ipfire-install-$VERSION.i386-r`svn info | grep Revision | cut -c 11-`.iso
+                               md5sum ipfire-install-$VERSION.i386-r$SVN_REVISION.iso > ipfire-install-$VERSION.i386-r$SVN_REVISION.iso.md5
+                               ncftpput -u $IPFIRE_FTP_USER_EXT -p $IPFIRE_FTP_PASS_EXT $IPFIRE_FTP_URL_EXT $IPFIRE_FTP_PATH_EXT/ ipfire-install-$VERSION.i386-r$SVN_REVISION.iso
+                               ncftpput -u $IPFIRE_FTP_USER_EXT -p $IPFIRE_FTP_PASS_EXT $IPFIRE_FTP_URL_EXT $IPFIRE_FTP_PATH_EXT/ ipfire-install-$VERSION.i386-r$SVN_REVISION.iso.md5
+                               if [ "$?" -eq "0" ]; then
+                                       echo -e "The ISO of Revision $SVN_REVISION was successfully uploaded to the ftp server."
+                               else
+                                       echo -e "There was an error while uploading the iso to the ftp server."
+                               fi
+               else
+                       echo -e "File with name ipfire-install-$VERSION.i386-r$SVN_REVISION.iso already exists on the ftp server!"
+               fi
+               rm -f ipfire-install-$VERSION.i386-r$SVN_REVISION.iso{,.md5}
+               ;;
+         paks)
+               ncftpput -z -u $IPFIRE_FTP_USER_PAK -p $IPFIRE_FTP_PASS_PAK $IPFIRE_FTP_URL_PAK $IPFIRE_FTP_PATH_PAK/ packages/*
+               if [ "$?" -eq "0" ]; then
+                       echo -e "The packages were successfully uploaded to the ftp server."
+               else
+                       echo -e "There was an error while uploading the packages to the ftp server."
+               fi
+         ;;
+       esac
        ;;
 build-only)
        rm -f $BASEDIR/log/$2*
@@ -1262,6 +1350,155 @@ build-silent)
        echo "Build started... This will take a while!"
        echo "You can see the status with 'screen -x ipfire'."
        ;;
+mail)
+       chmod 755 tools/sendEmail
+       ATTACHMENT=/tmp/ipfire-build-logs-R$SVN_REVISION.tar.gz
+       if [ "$2" = "ERROR" ]; then
+               SUBJECT="ERROR: IPFIRE-BUILD R$SVN_REVISION on `hostname`"
+               echo "ERROR: $0 build!"
+               cat <<END > /tmp/ipfire_mail_body
+When I was building IPFire on `hostname`, I have found an ERROR!
+Here you can see the logs and detect the reason for this error.
+
+Best Regards
+Your IPFire-Build-Script
+END
+       fi
+       if [ "$2" = "SUCCESS" ]; then
+               SUBJECT="SUCCESS: IPFIRE-BUILD R$SVN_REVISION on `hostname`"
+               cat <<END > /tmp/ipfire_mail_body
+Building IPFire on `hostname` in Revision $SVN_REVISION was successfull!
+You can find the ISO on your ftp server.
+
+Statistics:
+-----------
+Started:       $IPFIRE_START_TIME
+Finished:      `date`
+
+Best Regards
+Your IPFire-Build-Script
+END
+       fi
+       if [ "$2" = "SVNUPDATE" ]; then
+               SUBJECT="SVNUPDATE: IPFIRE-BUILD R$SVN_REVISION on `hostname`"
+               echo "ERROR: $0 svn up!"
+               cat <<END > /tmp/ipfire_mail_body
+When I was downloading the latest svn source,
+I have found an ERROR!
+Here you can see the logs and detect the reason for this error.
+
+Best Regards
+Your IPFire-Build-Script
+END
+       fi
+       if [ "$2" = "PREFETCH" ]; then
+               SUBJECT="PREFETCH: IPFIRE-BUILD R$SVN_REVISION on `hostname`"
+               echo "ERROR: $0 prefetch!"
+               cat <<END > /tmp/ipfire_mail_body
+When I was downloading the source packages,
+I have found an ERROR!
+Here you can see the logs and detect the reason for this error.
+
+Best Regards
+Your IPFire-Build-Script
+END
+       fi
+
+       if [ "$2" = "ISO" ]; then
+               SUBJECT="ISO: IPFIRE-BUILD R$SVN_REVISION on `hostname`"
+               echo "ERROR: $0 upload iso!"
+               cat <<END > /tmp/ipfire_mail_body
+When I was uploading the iso image,
+I have found an ERROR!
+Here you can see the logs and detect the reason for this error.
+
+Best Regards
+Your IPFire-Build-Script
+END
+       fi
+
+       if [ "$2" = "PAKS" ]; then
+               SUBJECT="PAKS: IPFIRE-BUILD R$SVN_REVISION on `hostname`"
+               echo "ERROR: $0 upload paks!"
+               cat <<END > /tmp/ipfire_mail_body
+When I was uploading the packages,
+I have found an ERROR!
+Here you can see the logs and detect the reason for this error.
+
+Best Regards
+Your IPFire-Build-Script
+END
+       fi
+
+       tar cfz $ATTACHMENT log/_build*
+       cat <<END >> /tmp/ipfire_mail_body
+
+Here is a summary... The full logs are in the attachment.
+---------------------------------------------------------
+
+`tail log/_*`
+END
+       cat /tmp/ipfire_mail_body | tools/sendEmail -q \
+               -f $IPFIRE_MAIL_FROM \
+               -t $IPFIRE_MAIL_REPORT \
+               -u $SUBJECT \
+               -s $IPFIRE_MAIL_SERVER:25 \
+               -xu $IPFIRE_MAIL_USER \
+               -xp $IPFIRE_MAIL_PASS \
+               -l log/_build.mail.log \
+               -a $ATTACHMENT # -v
+       rm -f /tmp/ipfire_mail_body $ATTACHMENT
+       ;;
+unattended)
+       ### This is our procedure that will compile the IPFire by herself...
+       echo "### UPDATE LOGS"
+       update_logs
+       echo "### SAVING TIME"
+       export IPFIRE_START_TIME=`date`
+
+       echo "### RUNNING SVN-UPDATE"
+       $0 svn update > /dev/null
+       if [ $? -ne 0 ]; then
+               $0 mail SVNUPDATE
+               exit 1
+       fi
+       chmod 755 $0
+
+       echo "### RUNNING PREFETCH"
+       $0 prefetch | grep -q "md5 difference"
+       if [ $? -eq 0 ]; then
+               $0 mail PREFETCH
+               exit 1
+       fi
+
+       echo "### RUNNING BUILD"
+       $0 build
+       if [ $? -ne 0 ]; then
+               $0 mail ERROR
+               exit 1
+       fi
+
+       echo "### UPLOADING ISO"
+       $0 upload iso
+       if [ $? -ne 0 ]; then
+               $0 mail ISO
+               exit 1
+       fi
+       
+       echo "### UPLOADING PAKS"
+       $0 upload paks
+       if [ $? -ne 0 ]; then
+               $0 mail PAKS
+               exit 1
+       fi
+
+       echo "### SUCCESS!"
+       $0 mail SUCCESS
+       ;;
+batch)
+       screen -dmS batch $0 unattended
+       exit 0
+       ;;
 *)
        clear
        svn info
@@ -1281,7 +1518,7 @@ build-silent)
                        sleep 0.1; echo -n "."
                done
                echo ".Ready!"
-               sleep 0.5
+               sleep 0.3
                screen -x ipfire
                ;;
        "IPFIRE: Clean")
diff --git a/tools/sendEmail b/tools/sendEmail
new file mode 100755 (executable)
index 0000000..2f2d143
--- /dev/null
@@ -0,0 +1,1873 @@
+#!/usr/bin/perl -w
+##############################################################################
+## sendEmail
+## Written by: Brandon Zehm <caspian@dotconf.net>
+## 
+## License:
+##  sendEmail (hereafter referred to as "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.
+##  Note that when redistributing modified versions of this source code, you
+##  must ensure that this disclaimer and the above coder's names are included
+##  VERBATIM in the modified code.
+##  
+## Disclaimer:
+##  This program is provided with no warranty of any kind, either expressed or
+##  implied.  It is the responsibility of the user (you) to fully research and
+##  comprehend the usage of this program.  As with any tool, it can be misused,
+##  either intentionally (you're a vandal) or unintentionally (you're a moron).
+##  THE AUTHOR(S) IS(ARE) NOT RESPONSIBLE FOR ANYTHING YOU DO WITH THIS PROGRAM
+##  or anything that happens because of your use (or misuse) of this program,
+##  including but not limited to anything you, your lawyers, or anyone else
+##  can dream up.  And now, a relevant quote directly from the GPL:
+##    
+## NO WARRANTY
+## 
+##  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+##  FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+##  OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+##  PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+##  OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+##  TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+##  PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+##  REPAIR OR CORRECTION.
+##    
+##############################################################################
+use strict;
+use IO::Socket;
+
+
+
+########################
+##  Global Variables  ##
+########################
+
+my %conf = (
+    ## General
+    "programName"          => $0,                                  ## The name of this program
+    "version"              => '1.52',                              ## The version of this program
+    "authorName"           => 'Brandon Zehm',                      ## Author's Name
+    "authorEmail"          => 'caspian@dotconf.net',               ## Author's Email Address
+    "timezone"             => '+0000 (GMT)',                       ## We always use +0000 for the time zone
+    "hostname"             => 'localhost',                         ## Used in printmsg() for all output, and in SMTP EHLO.
+    "debug"                => 0,                                   ## Default debug level
+    "error"                => '',                                  ## Error messages will often be stored here
+    
+    ## Logging
+    "debug"                => 0,
+    "stdout"               => 1,
+    "logging"              => 0,                                   ## If this is true the printmsg function prints to the log file
+    "logFile"              => '',                                  ## If this is specified (form the command line via -l) this file will be used for logging.
+    
+    ## Network
+    "server"               => 'localhost',                         ## Default SMTP server
+    "port"                 => 25,                                  ## Default port
+    "alarm"                => '',                                  ## Default timeout for connects and reads, this gets set from $opt{'timeout'}
+    
+    ## Email
+    "delimiter"            => "----MIME delimiter for sendEmail-"  ## MIME Delimiter
+                              . rand(1000000),                     ## Add some randomness to the delimiter
+    "Message-ID"           => rand(1000000) . "-sendEmail",        ## Message-ID for email header
+    "authUsername"         => '',                                  ## Username used in SMTP Auth
+    "authPassword"         => '',                                  ## Password used in SMTP Auth
+    
+);
+
+
+## This hash stores the options passed on the command line via the -o option.
+my %opt = (
+    ## Addressing
+    "reply-to"             => '',                                  ## Reply-To field
+    
+    ## Message
+    "message-file"         => '',                                  ## File to read message body from
+    "message-header"       => '',                                  ## Additional email header line
+    "message-format"       => 'normal',                            ## If "raw" is specified the message is sent unmodified
+    "message-charset"      => 'iso-8859-1',                        ## Message character-set
+    
+    ## Network
+    "timeout"              => 60,                                  ## Default timeout for connects and reads, this is copied to $conf{'alarm'} later.
+    
+);
+
+## More variables used later in the program
+my $CRLF        = "\015\012";
+my $subject     = '';
+my $message     = '';
+my $from        = '';
+my @to          = ();
+my @cc          = ();
+my @bcc         = ();
+my @attachments = ();
+my @attachments_names = ();
+
+## For printing colors to the console
+my ${colorRed}    = "\033[31;1m";
+my ${colorGreen}  = "\033[32;1m";
+my ${colorCyan}   = "\033[36;1m";
+my ${colorWhite}  = "\033[37;1m";
+my ${colorNormal} = "\033[m";
+my ${colorBold}   = "\033[1m";
+my ${colorNoBold} = "\033[0m";
+
+## Don't use shell escape codes on Windows systems
+if ($^O =~ /win/i) {
+    ${colorRed}    = "";
+    ${colorGreen}  = "";
+    ${colorCyan}   = "";
+    ${colorWhite}  = "";
+    ${colorNormal} = "";
+    ${colorBold}   = "";
+    ${colorNoBold} = "";
+}
+
+
+
+
+
+#############################
+##
+##      MAIN PROGRAM
+##
+#############################
+
+
+## Initialize
+initialize();
+
+## Process Command Line
+processCommandLine();
+$conf{'alarm'} = $opt{'timeout'};
+
+## Abort program after $conf{'alarm'} seconds to avoid infinite hangs
+alarm($conf{'alarm'}) if ($^O !~ /win/i);  ## alarm() doesn't work in win32
+
+
+
+
+###################################################
+##  Read $message from STDIN if -m was not used  ##
+###################################################
+
+if (!($message)) {
+    ## Read message body from a file specified with -o message-file=
+    if ($opt{'message-file'}) {
+        if (! -e $opt{'message-file'}) {
+            printmsg("ERROR => Message body file specified [$opt{'message-file'}] does not exist!", 0);
+            printmsg("HINT => 1) check spelling of your file; 2) fully qualify the path; 3) doubble quote it", 1);
+            quit("", 1);
+        }
+        if (! -r $opt{'message-file'}) {
+            printmsg("ERROR => Message body file specified can not be read due to restricted permissions!", 0);
+            printmsg("HINT => Check permissions on file specified to ensure it can be read", 1);
+            quit("", 1);
+        }
+        if (!open(MFILE, "< " . $opt{'message-file'})) {
+            printmsg("ERROR => Error opening message body file [$opt{'message-file'}]: $!", 0);
+            quit("", 1);
+        }
+        while (<MFILE>) {
+            $message .= $_;
+        }
+        close(MFILE);
+    }
+    
+    ## Read message body from STDIN
+    else {
+        alarm($conf{'alarm'}) if ($^O !~ /win/i);  ## alarm() doesn't work in win32
+        if ($conf{'stdout'}) {
+            print "Reading message body from STDIN because the '-m' option was not used.\n";
+            print "If you are manually typing in a message:\n";
+            print "  - First line must be received within $conf{'alarm'} seconds.\n" if ($^O !~ /win/i);
+            print "  - End manual input with a CTRL-D on its own line.\n\n" if ($^O !~ /win/i);
+            print "  - End manual input with a CTRL-Z on its own line.\n\n" if ($^O =~ /win/i);
+        }
+        while (<STDIN>) {                 ## Read STDIN into $message
+            $message .= $_;
+            alarm(0) if ($^O !~ /win/i);  ## Disable the alarm since at least one line was received
+        }
+        printmsg("Message input complete.", 0);
+    }
+}
+
+## Replace bare LF's with CRLF's (\012 should always have \015 with it)
+$message =~ s/(\015)?(\012|$)/\015\012/g;
+
+## Replace bare CR's with CRLF's (\015 should always have \012 with it)
+$message =~ s/(\015)(\012|$)?/\015\012/g;
+
+## Check message for bare periods and encode them
+$message =~ s/(^|$CRLF)(\.{1})($CRLF|$)/$1.$2$3/g;
+
+## Get the current date for the email header
+my ($sec,$min,$hour,$mday,$mon,$year,$day) = gmtime();
+$year += 1900; $mon = return_month($mon); $day = return_day($day);
+my $date = sprintf("%s, %s %s %d %.2d:%.2d:%.2d %s",$day, $mday, $mon, $year, $hour, $min, $sec, $conf{'timezone'});
+
+
+
+
+##################################
+##  Connect to the SMTP server  ##
+##################################
+printmsg("DEBUG => Connecting to $conf{'server'}:$conf{'port'}", 1);
+$SIG{'ALRM'} = sub { 
+    printmsg("ERROR => Timeout while connecting to $conf{'server'}:$conf{'port'}  There was no response after $conf{'alarm'} seconds.", 0); 
+    printmsg("HINT => Try specifying a different mail relay with the -s option.", 1);
+    quit("", 1);
+};
+alarm($conf{'alarm'}) if ($^O !~ /win/i);  ## alarm() doesn't work in win32;
+my $SERVER = IO::Socket::INET->new( PeerAddr  => $conf{'server'},
+                                    PeerPort  => $conf{'port'},
+                                    Proto     => 'tcp',
+                                    Autoflush => 1,
+                                    timeout   => $conf{'alarm'},
+);
+alarm(0) if ($^O !~ /win/i);  ## alarm() doesn't work in win32;
+
+## Make sure we got connected
+if ( (!$SERVER) or (!$SERVER->opened()) ) {
+    printmsg("ERROR => Connection attempt to $conf{'server'}:$conf{'port'} failed: $@", 0);
+    printmsg("HINT => Try specifying a different mail relay with the -s option.", 1);
+    quit("", 1);
+}
+
+
+
+
+
+
+#########################
+##  Do the SMTP Dance  ##
+#########################
+
+## Read initial greeting to make sure we're talking to a live SMTP server
+if (SMTPchat()) { quit($conf{'error'}, 1); }
+
+## EHLO
+if (SMTPchat('EHLO ' . $conf{'hostname'}))   { 
+    printmsg($conf{'error'}, 0);
+    printmsg("NOTICE => EHLO command failed, attempting HELO instead");
+    if (SMTPchat('HELO ' . $conf{'hostname'}))   { quit($conf{'error'}, 1); }
+    if ( $conf{'authUsername'} and $conf{'authPassword'} ) {
+        printmsg("WARNING => The mail server does not support ESMTP or SMTP AUTH!");
+    }
+}
+else {
+    ## Do SMTP Auth if required
+    if ( $conf{'authUsername'} and $conf{'authPassword'} ) {
+        if (SMTPchat('AUTH LOGIN'))              { quit($conf{'error'}, 1); }
+        if (SMTPchat($conf{'authUsername'}))     { quit($conf{'error'}, 1); }
+        if (SMTPchat($conf{'authPassword'}))     { quit($conf{'error'}, 1); }
+    }
+}
+
+## MAIL FROM
+if (SMTPchat('MAIL FROM:<' .(returnAddressParts($from))[1]. '>')) { quit($conf{'error'}, 1); }
+
+## RCPT TO
+my $oneRcptAccepted = 0;
+foreach my $rcpt (@to, @cc, @bcc) {
+    my ($name, $address) = returnAddressParts($rcpt);
+    if (SMTPchat('RCPT TO:<' . $address . '>')) {
+        printmsg("WARNING => The recipient <$address> was rejected by the mail server, error follows:", 0);
+        $conf{'error'} =~ s/^ERROR/WARNING/o;
+        printmsg($conf{'error'}, 0);
+    }
+    elsif ($oneRcptAccepted == 0) {
+        $oneRcptAccepted = 1;
+    }
+}
+## If no recipients were accepted we need to exit with an error.
+if ($oneRcptAccepted == 0) {
+    quit("ERROR => Exiting. No recipients were accepted for delivery by the mail server.", 1);
+}
+
+## DATA
+if (SMTPchat('DATA')) { quit($conf{'error'}, 1); }
+
+
+###############################
+##  Build and send the body  ##
+###############################
+printmsg("INFO => Sending message body",1);
+
+## If the message-format is raw just send the message as-is.
+if ($opt{'message-format'} =~ /^raw$/i) {
+    print $SERVER $message;
+}
+
+## If the message-format isn't raw, then build and send the message,
+else {
+    
+    ## Message-ID: <MessageID>
+    print $SERVER 'Message-ID: <' . $conf{'Message-ID'} . '@' . $conf{'hostname'} . '>' . $CRLF;
+    
+    ## From: "Name" <address@domain.com> (the pointless test below is just to keep scoping correct)
+    if ($from) {
+        my ($name, $address) = returnAddressParts($from);
+        print $SERVER 'From: "' . $name . '" <' . $address . '>' . $CRLF;
+    }
+    
+    ## Reply-To: 
+    if ($opt{'reply-to'}) {
+        my ($name, $address) = returnAddressParts($opt{'reply-to'});
+        print $SERVER 'Reply-To: "' . $name . '" <' . $address . '>' . $CRLF;
+    }
+    
+    ## To: "Name" <address@domain.com>
+    if (scalar(@to) > 0) {
+        print $SERVER "To:";
+        for (my $a = 0; $a < scalar(@to); $a++) {
+            my $msg = "";
+            
+            my ($name, $address) = returnAddressParts($to[$a]);
+            $msg = " \"$name\" <$address>";
+            
+            ## If we're not on the last address add a comma to the end of the line.
+            if (($a + 1) != scalar(@to)) {
+                $msg .= ",";
+            }
+            
+            print $SERVER $msg . $CRLF;
+        }
+    }
+    ## We always want a To: line so if the only recipients were bcc'd they don't see who it was sent to
+    else {
+        print $SERVER "To: \"Undisclosed Recipients\" <>$CRLF";
+    }
+    
+    if (scalar(@cc) > 0) {
+        print $SERVER "Cc:";
+        for (my $a = 0; $a < scalar(@cc); $a++) {
+            my $msg = "";
+            
+            my ($name, $address) = returnAddressParts($cc[$a]);
+            $msg = " \"$name\" <$address>";
+            
+            ## If we're not on the last address add a comma to the end of the line.
+            if (($a + 1) != scalar(@cc)) {
+                $msg .= ",";
+            }
+            
+            print $SERVER $msg . $CRLF;
+        }
+    }
+    
+    print $SERVER 'Subject: ' . $subject . $CRLF;                   ## Subject
+    print $SERVER 'Date: ' . $date . $CRLF;                         ## Date
+    print $SERVER 'X-Mailer: sendEmail-'.$conf{'version'}.$CRLF;    ## X-Mailer
+    
+    ## Send an additional message header line if specified
+    if ($opt{'message-header'}) {
+        print $SERVER $opt{'message-header'} . $CRLF;
+    }
+    
+    ## Encode all messages with MIME.
+    print $SERVER "MIME-Version: 1.0$CRLF";
+    print $SERVER "Content-Type: multipart/mixed; boundary=\"$conf{'delimiter'}\"$CRLF";
+    print $SERVER "$CRLF";
+    print $SERVER "This is a multi-part message in MIME format. To properly display this message you need a MIME-Version 1.0 compliant Email program.$CRLF";
+    print $SERVER "$CRLF";
+    
+    
+    ## Send message body
+    print $SERVER "--$conf{'delimiter'}$CRLF";
+    ## If the message contains HTML change the Content-Type
+    if ($message =~ /^\s*<html>/i) {
+        printmsg("Message is in HTML format", 1);
+        print $SERVER "Content-Type: text/html;$CRLF";
+    }
+    ## Otherwise it's a normal text email
+    else {
+        print $SERVER "Content-Type: text/plain;$CRLF";
+    }
+    print $SERVER "        charset=\"" . $opt{'message-charset'} . "\"$CRLF";
+    print $SERVER "Content-Transfer-Encoding: 7bit$CRLF";
+    print $SERVER $CRLF;
+    print $SERVER $message;
+    
+    
+    
+    ## Send Attachemnts
+    if ($attachments[0]) {
+        ## Disable the alarm so people on modems can send big attachments
+        alarm(0) if ($^O !~ /win/i);  ## alarm() doesn't work in win32
+        
+        ## Send the attachments
+        foreach my $filename (@attachments) {
+            ## This is check 2, we already checked this above, but just in case...
+            if ( ! -f $filename ) {
+                printmsg("ERROR => The file [$filename] doesn't exist!  Email will be sent, but without that attachment.", 0);
+            }
+            elsif ( ! -r $filename ) {
+                printmsg("ERROR => Couldn't open the file [$filename] for reading: $!   Email will be sent, but without that attachment.", 0);
+            }
+            else {
+                printmsg("DEBUG => Sending the attachment [$filename]", 1);
+                send_attachment($filename);
+            }
+        }
+    }
+    
+    
+    ## End the mime encoded message
+    print $SERVER "$CRLF--$conf{'delimiter'}--$CRLF";  
+}
+
+
+## Tell the server we are done sending the email
+print $SERVER "$CRLF.$CRLF";
+if (SMTPchat()) { quit($conf{'error'}, 1); }
+
+
+
+####################
+#  We are done!!!  #
+####################
+
+## Disconnect from the server
+if (SMTPchat('QUIT')) { quit($conf{'error'}, 1); }
+close $SERVER;
+
+
+
+
+
+
+#######################################
+##  Generate exit message/log entry  ##
+#######################################
+
+if ($conf{'debug'} or $conf{'logging'}) {
+    printmsg("Generating a detailed exit message", 3);
+    
+    ## Put the message together
+    my $output = "Email was sent successfully!  From: <" . (returnAddressParts($from))[1] . "> ";
+    
+    if (scalar(@to) > 0) {
+        $output .= "To: ";
+        for ($a = 0; $a < scalar(@to); $a++) {
+            $output .= "<" . (returnAddressParts($to[$a]))[1] . "> ";
+        }
+    }
+    if (scalar(@cc) > 0) {
+        $output .= "Cc: ";
+        for ($a = 0; $a < scalar(@cc); $a++) {
+            $output .= "<" . (returnAddressParts($cc[$a]))[1] . "> ";
+        }
+    }
+    if (scalar(@bcc) > 0) {
+        $output .= "Bcc: ";
+        for ($a = 0; $a < scalar(@bcc); $a++) {
+            $output .= "<" . (returnAddressParts($bcc[$a]))[1] . "> ";
+        }
+    }
+    $output .= "Subject: [$subject] " if ($subject);
+    if (scalar(@attachments_names) > 0) { 
+        $output .= "Attachment(s): ";
+        foreach(@attachments_names) {
+            $output .= "[$_] ";
+        }
+    }
+    $output .= "Server: [$conf{'server'}:$conf{'port'}]";
+    
+    
+######################
+#  Exit the program  #
+######################
+    
+    ## Print / Log the detailed message
+    quit($output, 0);
+}
+else {
+    ## Or the standard message
+    quit("Email was sent successfully!", 0);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+###############################################################################################
+##  Function: initialize ()
+##  
+##  Does all the script startup jibberish.
+##  
+###############################################################################################
+sub initialize {
+
+    ## Set STDOUT to flush immediatly after each print  
+    $| = 1;
+    
+    ## Intercept signals
+    $SIG{'QUIT'}  = sub { quit("EXITING: Received SIG$_[0]", 1); };
+    $SIG{'INT'}   = sub { quit("EXITING: Received SIG$_[0]", 1); };
+    $SIG{'KILL'}  = sub { quit("EXITING: Received SIG$_[0]", 1); };
+    $SIG{'TERM'}  = sub { quit("EXITING: Received SIG$_[0]", 1); };
+  
+    ## ALARM and HUP signals are not supported in Win32
+    unless ($^O =~ /win/i) {
+        $SIG{'HUP'}   = sub { quit("EXITING: Received SIG$_[0]", 1); };
+        $SIG{'ALRM'}  = sub { quit("EXITING: Received SIG$_[0]", 1); };
+    }
+    
+    ## Fixup $conf{'programName'}
+    $conf{'programName'} =~ s/(.)*[\/,\\]//;
+    $0 = $conf{'programName'} . " " . join(" ", @ARGV);
+    
+    ## Fixup $conf{'hostname'}
+    if ($conf{'hostname'} eq 'localhost') {
+        $conf{'hostname'} = "";
+        
+        if ($ENV{'HOSTNAME'}) {
+            $conf{'hostname'} = lc($ENV{'HOSTNAME'});
+        }
+        elsif ($ENV{'COMPUTERNAME'}) {
+            $conf{'hostname'} = lc($ENV{'COMPUTERNAME'});
+        }
+        else {
+            ## Try the hostname module
+            use Sys::Hostname;
+            $conf{'hostname'} = lc(hostname());
+        }
+        
+        ## Assign a name of "localhost" if it can't find anything else.
+        if (!$conf{'hostname'}) {
+            $conf{'hostname'} = 'localhost';
+        }
+        
+        $conf{'hostname'} =~ s/\..*$//;  ## Remove domain name if it's present
+    }
+    
+    return(1);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+###############################################################################################
+##  Function: processCommandLine ()
+##  
+##  Processes command line storing important data in global vars (usually %conf)
+##  
+###############################################################################################
+sub processCommandLine {
+    
+    
+    ############################
+    ##  Process command line  ##
+    ############################
+    
+    my @ARGS = @ARGV;  ## This is so later we can re-parse the command line args later if we need to
+    my $numargv = @ARGS;
+    help() unless ($numargv);
+    my $counter = 0;
+    
+    for ($counter = 0; $counter < $numargv; $counter++) {
+  
+        if ($ARGS[$counter] =~ /^-h$/i) {                    ## Help ##
+            help();
+        }
+        
+        elsif ($ARGS[$counter] eq "") {                      ## Ignore null arguments
+            ## Do nothing
+        }
+        
+        elsif ($ARGS[$counter] =~ /^--help/) {               ## Topical Help ##
+            $counter++;
+            if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) {
+                helpTopic($ARGS[$counter]);
+            }
+            else {
+                help();
+            }
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-o$/i) {                 ## Options specified with -o ##
+            $counter++;
+            ## Loop through each option passed after the -o
+            while ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) {
+                
+                if ($ARGS[$counter] !~ /(\S+)=(\S.*)/) {
+                    printmsg("WARNING => Name/Value pair [$ARGS[$counter]] is not properly formatted", 0);
+                    printmsg("WARNING => Arguments proceeding -o should be in the form of \"name=value\"", 0);
+                }
+                else {
+                    if (exists($opt{$1})) {
+                        $opt{$1} = $2;
+                        printmsg("DEBUG => Assigned \$opt{} key/value: $1 => $2", 3);
+                    }
+                    else {
+                        printmsg("WARNING => Name/Value pair [$ARGS[$counter]] will be ignored: unknown key [$1]", 0);
+                        printmsg("HINT => Try the --help option to find valid command line arguments", 1);
+                    }
+                }
+                $counter++;
+            }   $counter--;
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-f$/) {                  ## From ##
+            $counter++;
+            if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { $from = $ARGS[$counter]; }
+            else { printmsg("WARNING => The argument after -f was not an email address!", 0); $counter--; }
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-t$/) {                  ## To ##
+            $counter++;
+            while ($ARGS[$counter] && ($ARGS[$counter] !~ /^-/)) {
+                if ($ARGS[$counter] =~ /[;,]/) {
+                    push (@to, split(/[;,]/, $ARGS[$counter]));
+                }
+                else {
+                    push (@to,$ARGS[$counter]);
+                }
+                $counter++;
+            }   $counter--;
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-cc$/) {                 ## Cc ##
+            $counter++;
+            while ($ARGS[$counter] && ($ARGS[$counter] !~ /^-/)) {
+                if ($ARGS[$counter] =~ /[;,]/) {
+                    push (@cc, split(/[;,]/, $ARGS[$counter]));
+                }
+                else {
+                    push (@cc,$ARGS[$counter]);
+                }
+                $counter++;
+            }   $counter--;
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-bcc$/) {                ## Bcc ##
+            $counter++;
+            while ($ARGS[$counter] && ($ARGS[$counter] !~ /^-/)) {
+                if ($ARGS[$counter] =~ /[;,]/) {
+                    push (@bcc, split(/[;,]/, $ARGS[$counter]));
+                }
+                else {
+                    push (@bcc,$ARGS[$counter]);
+                }
+                $counter++;
+            }   $counter--;
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-m$/) {                  ## Message ##
+            $counter++;
+            $message = "";
+            while ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) {
+                if ($message) { $message .= " "; }
+                $message .= $ARGS[$counter];
+                $counter++;
+            }   $counter--;
+            
+            ## Replace '\n' with $CRLF.
+            ## This allows newlines with messages sent on the command line
+            $message =~ s/\\n/$CRLF/g;
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-u$/) {                  ## Subject ##
+            $counter++;
+            $subject = "";
+            while ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) {
+                if ($subject) { $subject .= " "; }
+                $subject .= $ARGS[$counter];
+                $counter++;
+            }   $counter--;
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-s$/) {                  ## Server ##
+            $counter++;
+            if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) {
+                $conf{'server'} = $ARGS[$counter];
+                if ($conf{'server'} =~ /:/) {                ## Port ##
+                    ($conf{'server'},$conf{'port'}) = split(":",$conf{'server'});
+                }
+            }
+            else { printmsg("WARNING - The argument after -s was not the server!", 0); $counter--; }
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-a$/) {                  ## Attachments ##
+            $counter++;
+            while ($ARGS[$counter] && ($ARGS[$counter] !~ /^-/)) {
+                push (@attachments,$ARGS[$counter]);
+                $counter++;
+            }   $counter--;
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-xu$/) {                  ## AuthSMTP Username ##
+            $counter++;
+            if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) {
+               $conf{'authUsername'} = $ARGS[$counter];
+               my $tmp = substr(pack('u', $conf{'authUsername'}), 1);   ## Convert the string to uuencoded text
+               chop($tmp);
+               $tmp =~ tr|` -_|AA-Za-z0-9+/|;                           ## Translate from uuencode to base64
+               $conf{'authUsername'} = $tmp;
+            }
+            else { 
+                printmsg("WARNING => The argument after -xu was not valid username!", 0);
+                $counter--;
+            }
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-xp$/) {                  ## AuthSMTP Password ##
+            $counter++;
+            if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) {
+               $conf{'authPassword'} = $ARGS[$counter];
+               my $tmp = substr(pack('u', $conf{'authPassword'}), 1);   ## Convert the binary to uuencoded text
+               chop($tmp);
+               $tmp =~ tr|` -_|AA-Za-z0-9+/|;                           ## Translate from uuencode to base64
+               $conf{'authPassword'} = $tmp;
+            }
+            else {
+                printmsg("WARNING => The argument after -xp was not valid password!", 0);
+                $counter--;
+            }
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-l$/) {                  ## Logging ##
+            $counter++;
+            $conf{'logging'} = 1;
+            if ($ARGS[$counter] && $ARGS[$counter] !~ /^-/) { $conf{'logFile'} = $ARGS[$counter]; }
+            else { printmsg("WARNING - The argument after -l was not the log file!", 0); $counter--; }
+        }
+        
+        elsif ($ARGS[$counter] =~ s/^-v+//i) {               ## Verbosity ##
+            my $tmp = (length($&) - 1);
+            $conf{'debug'} += $tmp;
+        }
+        
+        elsif ($ARGS[$counter] =~ /^-q$/) {                  ## Quiet ##
+            $conf{'stdout'} = 0;
+        }
+        
+        else {
+            printmsg("Error: \"$ARGS[$counter]\" is not a recognized option!", 0);
+            help();
+        }
+        
+    }
+
+
+
+
+
+
+    
+    
+    ###################################################
+    ##  Verify required variables are set correctly  ##
+    ###################################################
+
+    if (!$conf{'server'}) {
+        $conf{'server'} = 'localhost';
+    }
+    if (!$conf{'port'}) {
+        $conf{'port'} = 25;
+    }
+    if (!$from) {
+        quit("ERROR => You must specify a 'from' field!  Try --help.", 1);
+    }
+    if ( ((scalar(@to)) + (scalar(@cc)) + (scalar(@bcc))) <= 0) {
+        quit("ERROR => You must specify at least one recipient via -t, -cc, or -bcc", 1);
+    }
+    
+    ## Make sure email addresses look OK.
+    foreach my $addr (@to, @cc, @bcc, $from, $opt{'reply-to'}) {
+        if ($addr) {
+            if (!returnAddressParts($addr)) {
+                printmsg("ERROR => Can't use improperly formatted email address: $addr", 0);
+                printmsg("HINT => Try viewing the extended help on addressing with \"--help addressing\"", 1);
+                quit("", 1);
+            }
+        }
+    }
+    
+    ## Make sure all attachments exist.
+    foreach my $file (@attachments) {
+        if ( (! -f $file) or (! -r $file) ) {
+            printmsg("ERROR => The attachment [$file] doesn't exist!", 0);
+            printmsg("HINT => Try specifying the full path to the file or reading extended help with \"--help message\"", 1);
+            quit("", 1);
+        }
+    }
+    
+    if ($conf{'logging'} and (!$conf{'logFile'})) {
+        quit("ERROR => You used -l to enable logging but didn't specify a log file!", 1);
+    }    
+    
+    if ( $conf{'authUsername'} ) {
+        if (!$conf{'authPassword'}) {
+            quit ("ERROR => You must supply both a username and a password to use SMTP auth.",1);
+        }
+    }
+    
+    ## Return 0 errors
+    return(0);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## getline($socketRef)
+sub getline {
+    my ($socketRef) = @_;
+    local ($/) = "\r\n";
+    return $$socketRef->getline;
+}
+
+
+
+
+## Receive a (multiline?) SMTP response from ($socketRef)
+sub getResponse {
+    my ($socketRef) = @_;
+    my ($tmp, $reply);
+    local ($/) = "\r\n";
+    return undef unless defined($tmp = getline($socketRef));
+    return("getResponse() socket is not open") unless ($$socketRef->opened);
+    ## Keep reading lines if it's a multi-line response
+    while ($tmp =~ /^\d{3}-/o) {
+        $reply .= $tmp;
+        return undef unless defined($tmp = getline($socketRef));
+    }
+    $reply .= $tmp;
+    $reply =~ s/\r?\n$//o;
+    return $reply;
+}
+
+
+
+
+###############################################################################################
+##  Function:    SMTPchat ( [string $command] )
+##
+##  Description: Sends $command to the SMTP server (on SERVER) and awaits a successfull
+##               reply form the server.  If the server returns an error, or does not reply
+##               within $conf{'alarm'} seconds an error is generated.
+##               NOTE: $command is optional, if no command is specified then nothing will
+##               be sent to the server, but a valid response is still required from the server.
+##
+##  Input:       [$command]          A (optional) valid SMTP command (ex. "HELO")
+##  
+##  
+##  Output:      Returns zero on success, or non-zero on error.  
+##               Error messages will be stored in $conf{'error'}
+##               
+##  
+##  Example:     SMTPchat ("HELO mail.isp.net");
+###############################################################################################
+sub SMTPchat {
+    my ($command) = @_;
+    
+    printmsg("INFO => Sending: \t$command", 1) if ($command);
+    
+    ## Send our command
+    print $SERVER "$command$CRLF" if ($command);
+    
+    ## Read a response from the server
+    $SIG{'ALRM'} = sub { $conf{'error'} = "alarm"; $SERVER->close(); };
+    alarm($conf{'alarm'}) if ($^O !~ /win/i);  ## alarm() doesn't work in win32;
+    my $result = getResponse(\$SERVER); 
+    alarm(0) if ($^O !~ /win/i);  ## alarm() doesn't work in win32;
+    
+    ## Generate an alert if we timed out
+    if ($conf{'error'} eq "alarm") {
+        $conf{'error'} = "ERROR => Timeout while reading from $conf{'server'}:$conf{'port'} There was no response after $conf{'alarm'} seconds.";
+        return(1);
+    }
+    
+    ## Make sure the server actually responded
+    if (!$result) {
+        $conf{'error'} = "ERROR => $conf{'server'}:$conf{'port'} returned a zero byte response to our query.";
+        return(2);
+    }
+    
+    ## Validate the response
+    if (evalSMTPresponse($result)) {
+        ## conf{'error'} will already be set here
+        return(2);
+    }
+    
+    ## Print the success messsage
+    printmsg($conf{'error'}, 1);
+    
+    ## Return Success
+    return(0);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+###############################################################################################
+##  Function:    evalSMTPresponse (string $message )
+##
+##  Description: Searches $message for either an  SMTP success or error code, and returns
+##               0 on success, and the actual error code on error.
+##               
+##
+##  Input:       $message          Data received from a SMTP server (ex. "220 
+##                                
+##  
+##  Output:      Returns zero on success, or non-zero on error.  
+##               Error messages will be stored in $conf{'error'}
+##               
+##  
+##  Example:     SMTPchat ("HELO mail.isp.net");
+###############################################################################################
+sub evalSMTPresponse {
+    my ($message) = @_;
+    
+    ## Validate input
+    if (!$message) { 
+        $conf{'error'} = "ERROR => No message was passed to evalSMTPresponse().  What happened?";
+        return(1)
+    }
+    
+    printmsg("DEBUG => evalSMTPresponse() - Checking for SMTP success or error status in the message: $message ", 3);
+    
+    ## Look for a SMTP success code
+    if ($message =~ /^([23]\d\d)/) {
+        printmsg("DEBUG => evalSMTPresponse() - Found SMTP success code: $1", 2);
+        $conf{'error'} = "SUCCESS => Received: \t$message";
+        return(0);
+    }
+    
+    ## Look for a SMTP error code
+    if ($message =~ /^([45]\d\d)/) {
+        printmsg("DEBUG => evalSMTPresponse() - Found SMTP error code: $1", 2);
+        $conf{'error'} = "ERROR => Received: \t$message";
+        return($1);
+    }
+    
+    ## If no SMTP codes were found return an error of 1
+    $conf{'error'} = "ERROR => Received a message with no success or error code. The message received was: $message";
+    return(2);
+    
+}
+
+
+
+
+
+
+
+
+
+
+#########################################################
+# SUB: &return_month(0,1,etc)
+#  returns the name of the month that corrosponds
+#  with the number.  returns 0 on error.
+#########################################################
+sub return_month {
+    my $x = $_[0];
+    if ($x == 0)  { return 'Jan'; }
+    if ($x == 1)  { return 'Feb'; }
+    if ($x == 2)  { return 'Mar'; }
+    if ($x == 3)  { return 'Apr'; }
+    if ($x == 4)  { return 'May'; }
+    if ($x == 5)  { return 'Jun'; }
+    if ($x == 6)  { return 'Jul'; }
+    if ($x == 7)  { return 'Aug'; }
+    if ($x == 8)  { return 'Sep'; }
+    if ($x == 9)  { return 'Oct'; }
+    if ($x == 10) { return 'Nov'; }
+    if ($x == 11) { return 'Dec'; }
+    return (0);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#########################################################
+# SUB: &return_day(0,1,etc)
+#  returns the name of the day that corrosponds
+#  with the number.  returns 0 on error.
+#########################################################
+sub return_day {
+    my $x = $_[0];
+    if ($x == 0)  { return 'Sun'; }
+    if ($x == 1)  { return 'Mon'; }
+    if ($x == 2)  { return 'Tue'; }
+    if ($x == 3)  { return 'Wed'; }
+    if ($x == 4)  { return 'Thu'; }
+    if ($x == 5)  { return 'Fri'; }
+    if ($x == 6)  { return 'Sat'; }
+    return (0);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+###############################################################################################
+##  Function:    returnAddressParts(string $address)
+##
+##  Description: Returns a two element array containing the "Name" and "Address" parts of 
+##               an email address.
+##  
+## Example:      "Brandon Zehm <caspian@dotconf.net>"
+##               would return: ("Brandon Zehm", "caspian@dotconf.net");
+## 
+##               "caspian@dotconf.net"
+##               would return: ("caspian@dotconf.net", "caspian@dotconf.net")
+###############################################################################################
+sub returnAddressParts {
+    my $input = $_[0];
+    my $name = "";
+    my $address = "";
+    
+    ## Make sure to fail if it looks totally invalid
+    if ($input !~ /(\S+\@\S+)/) {
+        $conf{'error'} = "ERROR => The address [$input] doesn't look like a valid email address, ignoring it";
+        return(undef());
+    }
+    
+    ## Check 1, should find addresses like: "Brandon Zehm <caspian@dotconf.net>"
+    elsif ($input =~ /^\s*(\S(.*\S)?)\s*<(\S+\@\S+)>/o) {
+        ($name, $address) = ($1, $3);
+    }
+    
+    ## Otherwise if that failed, just get the address: <caspian@dotconf.net>
+    elsif ($input =~ /<(\S+\@\S+)>/o) {
+        $name = $address = $1;
+    }
+    
+    ## Or maybe it was formatted this way: caspian@dotconf.net
+    elsif ($input =~ /(\S+\@\S+)/o) {
+        $name = $address = $1;
+    }
+    
+    ## Something stupid happened, just return an error.
+    unless ($name and $address) {
+        printmsg("ERROR => Couldn't parse the address: $input", 0);
+        printmsg("HINT => If you think this should work, consider reporting this as a bug to $conf{'authorEmail'}", 1);
+        return(undef());
+    }
+    
+    ## Make sure there aren't invalid characters in the address, and return it.
+    my $ctrl        = '\000-\037';
+    my $nonASCII    = '\x80-\xff';
+    if ($address =~ /[<> ,;:"'\[\]\\$ctrl$nonASCII]/) {
+        printmsg("WARNING => The address [$address] seems to contain invalid characters: continuing anyway", 0);
+    }
+    return($name, $address);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#########################################################
+# SUB: send_attachment("/path/filename")
+# Sends the mime headers and base64 encoded file
+# to the email server.
+#########################################################
+sub send_attachment {
+    my ($filename) = @_;                             ## Get filename passed
+    my (@fields, $y, $filename_name, $encoding,      ## Local variables
+        @attachlines, $content_type);
+    my $bin = 1;
+    
+    @fields = split(/\/|\\/, $filename);             ## Get the actual filename without the path  
+    $filename_name = pop(@fields);       
+    push @attachments_names, $filename_name;         ## FIXME: This is only used later for putting in the log file
+    
+    ##########################
+    ## Autodetect Mime Type ##
+    ##########################
+    
+    @fields = split(/\./, $filename_name);
+    $encoding = $fields[$#fields];
+    
+    if ($encoding =~ /txt|text|log|conf|^c$|cpp|^h$|inc|m3u/i) {   $content_type = 'text/plain';                      }
+    elsif ($encoding =~ /html|htm|shtml|shtm|asp|php|cfm/i) {      $content_type = 'text/html';                       }
+    elsif ($encoding =~ /sh$/i) {                                  $content_type = 'application/x-sh';                }
+    elsif ($encoding =~ /tcl/i) {                                  $content_type = 'application/x-tcl';               }
+    elsif ($encoding =~ /pl$/i) {                                  $content_type = 'application/x-perl';              }
+    elsif ($encoding =~ /js$/i) {                                  $content_type = 'application/x-javascript';        }
+    elsif ($encoding =~ /man/i) {                                  $content_type = 'application/x-troff-man';         }
+    elsif ($encoding =~ /gif/i) {                                  $content_type = 'image/gif';                       }
+    elsif ($encoding =~ /jpg|jpeg|jpe|jfif|pjpeg|pjp/i) {          $content_type = 'image/jpeg';                      }
+    elsif ($encoding =~ /tif|tiff/i) {                             $content_type = 'image/tiff';                      }
+    elsif ($encoding =~ /xpm/i) {                                  $content_type = 'image/x-xpixmap';                 }
+    elsif ($encoding =~ /bmp/i) {                                  $content_type = 'image/x-MS-bmp';                  }
+    elsif ($encoding =~ /pcd/i) {                                  $content_type = 'image/x-photo-cd';                }
+    elsif ($encoding =~ /png/i) {                                  $content_type = 'image/png';                       }
+    elsif ($encoding =~ /aif|aiff/i) {                             $content_type = 'audio/x-aiff';                    }
+    elsif ($encoding =~ /wav/i) {                                  $content_type = 'audio/x-wav';                     }
+    elsif ($encoding =~ /mp2|mp3|mpa/i) {                          $content_type = 'audio/x-mpeg';                    }
+    elsif ($encoding =~ /ra$|ram/i) {                              $content_type = 'audio/x-pn-realaudio';            }
+    elsif ($encoding =~ /mpeg|mpg/i) {                             $content_type = 'video/mpeg';                      }
+    elsif ($encoding =~ /mov|qt$/i) {                              $content_type = 'video/quicktime';                 }
+    elsif ($encoding =~ /avi/i) {                                  $content_type = 'video/x-msvideo';                 }
+    elsif ($encoding =~ /zip/i) {                                  $content_type = 'application/x-zip-compressed';    }
+    elsif ($encoding =~ /tar/i) {                                  $content_type = 'application/x-tar';               }
+    elsif ($encoding =~ /jar/i) {                                  $content_type = 'application/java-archive';        }
+    elsif ($encoding =~ /exe|bin/i) {                              $content_type = 'application/octet-stream';        }
+    elsif ($encoding =~ /ppt|pot|ppa|pps|pwz/i) {                  $content_type = 'application/vnd.ms-powerpoint';   }
+    elsif ($encoding =~ /mdb|mda|mde/i) {                          $content_type = 'application/vnd.ms-access';       }
+    elsif ($encoding =~ /xls|xlt|xlm|xld|xla|xlc|xlw|xll/i) {      $content_type = 'application/vnd.ms-excel';        }
+    elsif ($encoding =~ /doc|dot/i) {                              $content_type = 'application/msword';              }
+    elsif ($encoding =~ /rtf/i) {                                  $content_type = 'application/rtf';                 }
+    elsif ($encoding =~ /pdf/i) {                                  $content_type = 'application/pdf';                 }
+    elsif ($encoding =~ /tex/i) {                                  $content_type = 'application/x-tex';               }
+    elsif ($encoding =~ /latex/i) {                                $content_type = 'application/x-latex';             }
+    elsif ($encoding =~ /vcf/i) {                                  $content_type = 'application/x-vcard';             }
+    else { $content_type = 'application/octet-stream';  }
+  
+  
+  ############################
+  ## Process the attachment ##
+  ############################
+    
+    #####################################
+    ## Generate and print MIME headers ##
+    #####################################
+    
+    $y  = "$CRLF--$conf{'delimiter'}$CRLF";
+    $y .= "Content-Type: $content_type;$CRLF";
+    $y .= "        name=\"$filename_name\"$CRLF";
+    $y .= "Content-Transfer-Encoding: base64$CRLF";
+    $y .= "Content-Disposition: attachment; filename=\"$filename_name\"$CRLF";
+    $y .= "$CRLF";
+    print $SERVER $y;
+    
+    
+    ###########################################################
+    ## Convert the file to base64 and print it to the server ##
+    ###########################################################
+    
+    open (FILETOATTACH, $filename) || do { 
+        printmsg("ERROR => Opening the file [$filename] for attachment failed with the error: $!", 0);
+        return(1);
+    };
+    binmode(FILETOATTACH);                 ## Hack to make Win32 work
+    
+    my $res = "";
+    my $tmp = "";
+    my $base64 = "";
+    while (<FILETOATTACH>) {               ## Read a line from the (binary) file
+        $res .= $_;
+        
+        ###################################
+        ## Convert binary data to base64 ##
+        ###################################
+        while ($res =~ s/(.{45})//s) {         ## Get 45 bytes from the binary string
+            $tmp = substr(pack('u', $&), 1);   ## Convert the binary to uuencoded text
+            chop($tmp);
+            $tmp =~ tr|` -_|AA-Za-z0-9+/|;     ## Translate from uuencode to base64
+            $base64 .= $tmp;
+        }
+        
+        ################################
+        ## Print chunks to the server ##
+        ################################
+        while ($base64 =~ s/(.{76})//s) {
+            print $SERVER "$1$CRLF";
+        }
+      
+    }
+    
+    ###################################
+    ## Encode and send the leftovers ##
+    ###################################
+    my $padding = "";
+    if ( ($res) and (length($res) >= 1) ) {
+        $padding = (3 - length($res) % 3) % 3;  ## Set flag if binary data isn't divisible by 3
+        $res = substr(pack('u', $res), 1);         ## Convert the binary to uuencoded text
+        chop($res);
+        $res =~ tr|` -_|AA-Za-z0-9+/|;             ## Translate from uuencode to base64
+    }
+    
+    ############################
+    ## Fix padding at the end ##
+    ############################
+    $res = $base64 . $res;                                  ## Get left overs from above
+    $res =~ s/.{$padding}$/'=' x $padding/e if $padding;    ## Fix the end padding if flag (from above) is set
+    if ($res) {
+        while ($res =~ s/(.{1,76})//s) {                        ## Send it to the email server.
+            print $SERVER "$1$CRLF";
+        }
+    }
+    
+    close (FILETOATTACH) || do {
+        printmsg("ERROR - Closing the filehandle for file [$filename] failed with the error: $!", 0);
+        return(2);
+    };
+    
+    ## Return 0 errors
+    return(0);
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+###############################################################################################
+##  Function:    printmsg (string $message, int $level)
+##
+##  Description: Handles all messages - printing them to the screen only if the messages
+##               $level is >= the global debug level.  If $conf{'logFile'} is defined it
+##               will also log the message to that file.
+##
+##  Input:       $message          A message to be printed, logged, etc.
+##               $level            The debug level of the message. If
+##                                 not defined 0 will be assumed.  0 is
+##                                 considered a normal message, 1 and 
+##                                 higher is considered a debug message.
+##  
+##  Output:      Prints to STDOUT
+##
+##  Assumptions: $conf{'hostname'} should be the name of the computer we're running on.
+##               $conf{'stdout'} should be set to 1 if you want to print to stdout
+##               $conf{'logFile'} should be a full path to a log file if you want that
+##               $conf{'syslog'} should be 1 if you want to syslog, the syslog() function
+##               written by Brandon Zehm should be present.
+##               $conf{'debug'} should be an integer between 0 and 10.
+##
+##  Example:     printmsg("WARNING: We believe in generic error messages... NOT!", 0);
+###############################################################################################
+sub printmsg {
+    ## Assign incoming parameters to variables
+    my ( $message, $level ) = @_;
+    
+    ## Make sure input is sane
+    $level = 0 if (!defined($level));
+    $message =~ s/\s+$//sgo;
+    $message =~ s/\r?\n/, /sgo;
+    
+    ## Continue only if the debug level of the program is >= message debug level.
+    if ($conf{'debug'} >= $level) {
+        
+        ## Get the date in the format: Dec  3 11:14:04
+        my ($sec, $min, $hour, $mday, $mon) = localtime();
+        $mon = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')[$mon];
+        my $date = sprintf("%s %02d %02d:%02d:%02d", $mon, $mday, $hour, $min, $sec);
+        
+        ## Print to STDOUT always if debugging is enabled, or if conf{stdout} is true.
+        if ( ($conf{'debug'} >= 1) or ($conf{'stdout'} == 1) ) {
+            print "$date $conf{'hostname'} $conf{'programName'}\[$$\]: $message\n";
+        }
+        
+        ## Print to the log file if $conf{'logging'} is true
+        if ($conf{'logFile'}) {
+            if (openLogFile($conf{'logFile'})) { $conf{'logFile'} = ""; printmsg("ERROR => Opening the file [$conf{'logFile'}] for appending returned the error: $!", 1); }
+            print LOGFILE "$date $conf{'hostname'} $conf{'programName'}\[$$\]: $message\n";
+        }
+        
+    }
+    
+    ## Return 0 errors
+    return(0);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+###############################################################################################
+## FUNCTION:    
+##   openLogFile ( $filename )
+## 
+## 
+## DESCRIPTION: 
+##   Opens the file $filename and attaches it to the filehandle "LOGFILE".  Returns 0 on success
+##   and non-zero on failure.  Error codes are listed below, and the error message gets set in
+##   global variable $!.
+##   
+##   
+## Example: 
+##   openFile ("/var/log/sendEmail.log");
+##
+###############################################################################################
+sub openLogFile {
+    ## Get the incoming filename
+    my $filename = $_[0];
+    
+    ## Make sure our file exists, and if the file doesn't exist then create it
+    if ( ! -f $filename ) {
+        print STDERR "NOTICE: The log file [$filename] does not exist.  Creating it now with mode [0600].\n" if ($conf{'stdout'});
+        open (LOGFILE, ">>$filename");
+        close LOGFILE;
+        chmod (0600, $filename);
+    }
+    
+    ## Now open the file and attach it to a filehandle
+    open (LOGFILE,">>$filename") or return (1);
+    
+    ## Put the file into non-buffering mode
+    select LOGFILE;
+    $| = 1;
+    select STDOUT;
+    
+    ## Return success
+    return(0);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+###############################################################################################
+##  Function:    quit (string $message, int $errorLevel)
+##  
+##  Description: Exits the program, optionally printing $message.  It 
+##               returns an exit error level of $errorLevel to the 
+##               system  (0 means no errors, and is assumed if empty.)
+##
+##  Example:     quit("Exiting program normally", 0);
+###############################################################################################
+sub quit {
+    my %incoming = ();
+    (
+      $incoming{'message'},
+      $incoming{'errorLevel'}
+    ) = @_;
+    $incoming{'errorLevel'} = 0 if (!defined($incoming{'errorLevel'}));
+    
+    ## Print exit message
+    if ($incoming{'message'}) { 
+        printmsg($incoming{'message'}, 0);
+    }
+    
+    ## Exit
+    exit($incoming{'errorLevel'});
+}
+
+
+
+
+
+
+
+
+
+
+
+
+###############################################################################################
+## Function:    help ()
+##
+## Description: For all those newbies ;) 
+##              Prints a help message and exits the program.
+## 
+###############################################################################################
+sub help {
+exit(1) if (!$conf{'stdout'});
+print <<EOM;
+
+${colorBold}$conf{'programName'}-$conf{'version'} by $conf{'authorName'} <$conf{'authorEmail'}>${colorNoBold}
+
+Synopsis:  $conf{'programName'} -f ADDRESS [options]
+  
+  ${colorRed}Required:${colorNormal}
+    -f ADDRESS                from (sender) email address
+    * At least one recipient required via -t, -cc, or -bcc
+    * Message body required via -m, STDIN, or -o message-file=FILE
+    
+  ${colorGreen}Common:${colorNormal}
+    -t ADDRESS [ADDR ...]     to email address(es)
+    -u SUBJECT                message subject
+    -m MESSAGE                message body
+    -s SERVER[:PORT]          smtp mail relay, default is $conf{'server'}:$conf{'port'}
+    
+  ${colorGreen}Optional:${colorNormal}
+    -a   FILE [FILE ...]      file attachment(s)
+    -cc  ADDRESS [ADDR ...]   cc  email address(es)
+    -bcc ADDRESS [ADDR ...]   bcc email address(es)
+    
+  ${colorGreen}Paranormal:${colorNormal}
+    -xu USERNAME              authentication user (for SMTP authentication)
+    -xp PASSWORD              authentication password (for SMTP authentication)
+    -l  LOGFILE               log to the specified file
+    -v                        verbosity, use multiple times for greater effect
+    -q                        be quiet (no stdout output)
+    -o NAME=VALUE             see extended help topic "misc" for details
+  
+  ${colorGreen}Help:${colorNormal}
+    --help TOPIC              The following extended help topics are available:
+        addressing            explain addressing and related options
+        message               explain message body input and related options
+        misc                  explain -xu, -xp, and others
+        networking            explain -s, etc
+        output                explain logging and other output options
+
+EOM
+exit(1);
+}
+
+
+
+
+
+
+
+
+
+###############################################################################################
+## Function:    helpTopic ($topic)
+##
+## Description: For all those newbies ;) 
+##              Prints a help message and exits the program.
+## 
+###############################################################################################
+sub helpTopic {
+    exit(1) if (!$conf{'stdout'});
+    my ($topic) = @_;
+
+    CASE: {
+
+
+
+
+## ADDRESSING
+        ($topic eq 'addressing') && do {
+            print <<EOM;
+
+${colorBold}ADDRESSING DOCUMENTATION${colorNormal}
+
+${colorGreen}Addressing Options${colorNormal}
+Options related to addressing:
+    -f   ADDRESS
+    -t   ADDRESS [ADDRESS ...]
+    -cc  ADDRESS [ADDRESS ...]
+    -bcc ADDRESS [ADDRESS ...]
+    -o   reply-to=ADDRESS
+    
+-f ADDRESS
+    This required option specifies who the email is from, I.E. the sender's
+    email address.
+    
+-t ADDRESS [ADDRESS ...]
+    This option specifies the primary recipient(s).  At least one recipient
+    address must be specified via the -t, -cc. or -bcc options.
+
+-cc ADDRESS [ADDRESS ...]
+    This option specifies the "carbon copy" recipient(s).  At least one 
+    recipient address must be specified via the -t, -cc. or -bcc options.
+
+-bcc ADDRESS [ADDRESS ...]
+    This option specifies the "blind carbon copy" recipient(s).  At least
+    one recipient address must be specified via the -t, -cc. or -bcc options.
+
+-o reply-to=ADDRESS
+    This option specifies that an optional "Reply-To" address should be
+    written in the email's headers.
+    
+
+${colorGreen}Email Address Syntax${colorNormal}
+Email addresses may be specified in one of two ways:
+    Full Name:     "John Doe <john.doe\@gmail.com>"
+    Just Address:  "john.doe\@gmail.com"
+
+The "Full Name" method is useful if you want a name, rather than a plain
+email address, to be displayed in the recipient's From, To, or Cc fields
+when they view the message.
+    
+
+${colorGreen}Multiple Recipients${colorNormal}
+The -t, -cc, and -bcc options each accept multiple addresses.  They may be
+specified by separating them by either a white space, comma, or semi-colon
+separated list.  You may also specify the -t, -cc, and -bcc options multiple
+times, each occurance will append the new recipients to the respective list.
+
+Examples:
+(I used "-t" in these examples, but it can be "-cc" or "-bcc" as well)
+
+  * Space separated list:
+    -t jane.doe\@yahoo.com "John Doe <john.doe\@gmail.com>"
+    
+  * Semi-colon separated list:
+    -t "jane.doe\@yahoo.com; John Doe <john.doe\@gmail.com>"
+  * Comma separated list:
+    -t "jane.doe\@yahoo.com, John Doe <john.doe\@gmail.com>"
+  
+  * Multiple -t, -cc, or -bcc options:
+    -t "jane.doe\@yahoo.com" -t "John Doe <john.doe\@gmail.com>"
+  
+
+EOM
+            last CASE;
+        };
+
+
+
+
+
+
+## MESSAGE
+        ($topic eq 'message') && do {
+            print <<EOM;
+
+${colorBold}MESSAGE DOCUMENTATION${colorNormal}
+
+${colorGreen}Message Options${colorNormal}
+Options related to the message:
+    -u  SUBJECT
+    -m  MESSAGE
+    -o  message-file=FILE
+    -o  message-header=EMAIL HEADER
+    -o  message-format=raw
+    -o  message-charset=CHARSET
+    
+-u SUBJECT
+    This option allows you to specify the subject for your email message.
+    It is not required (anymore) that the subject be quoted, although it 
+    is recommended.  The subject will be read until an argument starting
+    with a hyphen (-) is found.  
+    Examples:
+      -u "Contact information while on vacation"
+      -u New Microsoft vulnerability discovered
+
+-m MESSAGE
+    This option is one of three methods that allow you to specify the message
+    body for your email.  The message may be specified on the command line
+    with this -m option, read from a file with the -o message-file=FILE
+    option, or read from STDIN if neither of these options are present.
+    
+    It is not required (anymore) that the message be quoted, although it is
+    recommended.  The message will be read until an argument starting with a
+    hyphen (-) is found.
+    Examples:
+      -m "See you in South Beach, Hawaii.  -Todd"
+      -m Please ensure that you upgrade your systems right away
+    
+    Multi-line message bodies may be specified with the -m option by putting
+    a "\\n" into the message.  Example:
+      -m "This is line 1.\\nAnd this is line 2."
+    
+    HTML messages are supported, simply begin your message with "<html>" and
+    sendEmail will properly label the mime header so MUAs properly render
+    the message.  
+
+-o message-file=FILE
+    This option is one of three methods that allow you to specify the message
+    body for your email.  To use this option simply specify a text file
+    containing the body of your email message. Examples:
+      -o message-file=/root/message.txt
+      -o message-file="C:\\Program Files\\output.txt"
+
+-o message-header=EMAIL HEADER
+    This option allows you to specify an additional single line to insert
+    into the email headers.  Do not use this unless you know what you are
+    doing!
+    Example: To scare a Microsoft Outlook user you may want to try this:
+     -o message-header="X-Message-Flag: This message contains illegal content"
+
+-o message-format=raw
+    This option instructs sendEmail to assume the message is already a
+    complete email message.  SendEmail will not generate any headers and will
+    transmit the message as-is to the remote SMTP server.  Due to the nature
+    of this option the following command line options will be ignored when this
+    one is used:
+      -u SUBJECT
+      -o message-header=EMAIL HEADER
+      -o message-charset=CHARSET
+      -a ATTACHMENT
+      
+-o message-charset=CHARSET
+    This option allows you to specify the character-set for the message body.
+    The default is iso-8859-1.
+      
+
+${colorGreen}The Message Body${colorNormal}
+The message body may be specified in one of three ways:
+ 1) Via the -m MESSAGE command line option.
+    Example:
+      -m "This is the message body"
+      
+ 2) By putting the message body in a file and using the -o message-file=FILE
+    command line option.
+    Example:
+      -o message-file=/root/message.txt
+      
+ 3) By piping the message body to sendEmail when nither of the above command
+    line options were specified.
+    Example:
+      grep "ERROR" /var/log/messages | sendEmail -t you\@domain.com ...
+
+If the message body begins with "<html>" then the message will be treated as
+an HTML message and the MIME headers will be written so that a HTML capable
+email client will display the message in it's HTML form.
+Any of the above methods may be used with the -o message-format=raw option 
+to deliver an already complete email message.
+
+
+EOM
+            last CASE;
+        };
+        
+
+
+
+
+
+## MISC
+        ($topic eq 'misc') && do {
+            print <<EOM;
+
+${colorBold}MISC DOCUMENTATION${colorNormal}
+
+${colorGreen}Misc Options${colorNormal}
+Options that don't fit anywhere else:
+    -xu  USERNAME
+    -xp  PASSWORD
+    -a   ATTACHMENT
+    -o   timeout=SECONDS
+    
+-xu  USERNAME
+    This option, in conjunction with the -xp option, allows you to specify
+    a username and password to be used with SMTP servers requiring
+    authentication (via SMTP AUTH.)
+
+-xp  PASSWORD
+    This option, in conjunction with the -xu option, allows you to specify
+    a username and password to be used with SMTP servers requiring
+    authentication (via SMTP AUTH.)
+
+-a   ATTACHMENT [ATTACHMENT]
+    This option allows you to attach any number of files to your email
+    message.
+    
+-o   timeout=SECONDS    
+    This option sets the timeout value in seconds used for all network reads,
+    writes, and a few other things.
+
+    
+${colorGreen}The Complete -o Parameter List${colorNormal}
+    -o message-file=FILE
+    -o message-header=EMAIL HEADER
+    -o message-format=raw
+    -o message-charset=CHARSET
+    -o reply-to=ADDRESS
+    -o timeout=SECONDS
+
+        
+EOM
+            last CASE;
+        };
+        
+
+
+
+
+
+## NETWORKING
+        ($topic eq 'networking') && do {
+            print <<EOM;
+
+${colorBold}NETWORKING DOCUMENTATION${colorNormal}
+
+${colorGreen}Networking Options${colorNormal}
+Options related to networking:
+    -s   SERVER[:PORT]
+    -o   timeout=SECONDS
+    
+-s SERVER[:PORT]
+    This option allows you to specify the SMTP server sendEmail should
+    connect to to deliver your email message to.  If this option is not
+    specified sendEmail will try to connect to localhost:25 to deliver
+    the message.  THIS IS MOST LIKELY NOT WHAT YOU WANT, AND WILL LIKELY
+    FAIL unless you have a email server (commonly known as an MTA) running
+    on your computer!
+    Typically you will need to specify your company or ISP's email server.
+    For example, if you use CableOne you will need to specify:
+       -s mail.cableone.net
+    If you have your own email server running on port 300 you would
+    probably use an option like this:
+       -s myserver.mydomain.com:300
+    
+-o timeout=SECONDS    
+    This option sets the timeout value in seconds used for all network reads,
+    writes, and a few other things.
+
+    
+EOM
+            last CASE;
+        };
+        
+        
+        
+        
+        
+        
+## OUTPUTO
+        ($topic eq 'output') && do {
+            print <<EOM;
+
+${colorBold}OUTPUT DOCUMENTATION${colorNormal}
+
+${colorGreen}Output Options${colorNormal}
+Options related to output:
+    -l LOGFILE
+    -v
+    -q
+    
+-l LOGFILE
+    This option allows you to specify a log file to append to.  Every message
+    that is displayed to STDOUT is also written to the log file.  This may be
+    used in conjunction with -q and -v.
+
+-q
+    This option tells sendEmail to disable printing to STDOUT.  In other
+    words nothing will be printed to the console.  This does not affect the
+    behavior of the -l or -v options.
+    
+-v
+    This option allows you to increase the debug level of sendEmail.  You may
+    either use this option more than once, or specify more than one v at a
+    time to obtain a debug level higher than one.  Examples:
+        Specifies a debug level of 1:  -v
+        Specifies a debug level of 2:  -vv
+        Specifies a debug level of 2:  -v -v
+    A debug level of one is recommended when doing any sort of debugging.  
+    At that level you will see the entire SMTP transaction (except the
+    body of the email message), and hints will be displayed for most
+    warnings and errors.  The highest debug level is three.
+
+    
+EOM
+            last CASE;
+        };
+        
+        ## Unknown option selected!
+        quit("ERROR => The help topic specified is not valid!", 1);
+    };
+    
+exit(1);
+}
+
+