#!/usr/local/bin/perl # $Id: quotesmailer,v 1.4 2000/09/30 18:25:18 grimaldo Rel $ #----------------------------------------------------------------------- # quotesmailer (c)1999-2000 Dídimo Emilio Grimaldo Tuñón #----------------------------------------------------------------------- # AUTHOR: D. Emilio Grimaldo T. grimaldo@panama.iaehv.nl # DESCRIPTION: # A simple add-on to Quotes that enables to setup a list of # recipients if necessary or just you (mostly). Each of the # major functions have their own list. It hides all the nitty- # greety details and you can place it in a personal crontab # (see crontab(5), crontab(8)): # 00 07 * * Mon-Fri /usr/local/bin/quotesmailer -stocks # 00 08 * * Mon-Fri /usr/local/bin/quotesmailer -stocks -web # 00 12 * * Mon-Fri /usr/local/bin/quotesmailer -money # 30 09 * * Mon-Fri /usr/local/bin/quotesmailer -news # # It uses a blind-copy list (Bcc) for delivery. # OPTIONS: # -h Help # -t me@there Test, pretend to mail to me@there # -stocks Fetch & mail stock quotations # -money " " " currency exchange quotations # -news " " " Linux headlines # -announce Send an announcement (Used for release announcements) # # TEST: # -testwith FILE Instruct Quotes to use FILE as input instead of # establishing a network connection. # PARAMETERS: # none # USAGE: # quotesmailer [-t me@there.com] { -stocks | -news | -money } # quotesmailer -stocks -web -testwith /tmp/mystocks.html # # FRESHMEAT: 913728558 (Distributed only with Quotes) # ********* CONFIGURATION SECTION ********* my $Product = 'QuotesMailer'; my %cfg = ( 'stock-list' => "$ENV{'HOME'}/.quotes-stocks-l", # important 'money-list' => "$ENV{'HOME'}/.quotes-money-l", # important 'news-list' => "$ENV{'HOME'}/.quotes-news-l", # important 'stock-w-list' => "$ENV{'HOME'}/.quotes-stocks-web-l", # important 'quotes-bin' => '/usr/local/bin/quotes -q', # important # 'quotes-bin' => './quotes -q', # important 'mail-bin' => '/usr/sbin/sendmail -t -U', # important 'stock-subj' => "$Product: Stock quotes", 'money-subj' => "$Product: Currency quotes", 'news-subj' => "$Product: Linux news", 'stock-url' => "quotes.nasdaq-amex.com", 'money-url' => "quote.yahoo.com", 'news-url' => "slashdot.org", 'announce-list' => "$ENV{'HOME'}/.quotes-announce-l", 'html-opt' => '-web -swallow' # @CR-001 ); # ********* ********************* ********* # ********* I N C L U D E S ********* use strict; use Getopt::Long; # ********* ********************* ********* # ********* G L O B A L V A R S ********* my $cvsId = '$Revision: 1.4 $'; my @MailHeaders = ( 'From: nobody@localhost', 'X-Agent: Quotes v1.7', "X-Product: $Product v$cvsId" ); my @MailBody; my ($optTest, $optAnnounce, $optHelp, $optStocks, $optMoney, $optNews, $optWeb); my ($optTestWith, $modSubject); # ********* ********************* ********* sub Initialize { $cvsId =~ m/Revision:\s+(\d+\.\d+\.*\d*\.*\d*)/; $cvsId = $1; } sub Help { print "\n\t$Product version $cvsId\n"; print "\tCopyleft (c)1999-2000 D.Emilio Grimaldo T.\n"; print "\tEmail: grimaldo\@panama.iaehv.nl\n"; print "\t-------------------------------------------------\n"; print "\tA Quotation mailer program for Quotes\n"; print "\t-------------------------------------------------\n"; print "\tUsage:\n\t\tquotesmailer [-w] {-money|-stocks|-news}\n"; print "\t\tquotesmailer -announce announcement.txt -subject \"subject\"\n"; exit(0); } sub SetOptions { my $rOpts = shift; my $cmd = "$cfg{'quotes-bin'} "; my $date = localtime(time); my @d; if ($optStocks) { $cmd .= " $cfg{'html-opt'} " if ($optWeb); # @CR-001 $cmd .= " -u $optTestWith " if $optTestWith; # Test only $cmd .= '-names stocks'; $$rOpts{'url'} = $cfg{'stock-url'}; $$rOpts{'subtitle'} = 'Stock Quotes'; push(@MailHeaders, "Subject: $cfg{'stock-subj'}"); push(@MailHeaders, 'Content-Type: text/html') if $optWeb; # PR-001 } if ($optMoney) { $cmd .= 'currency'; $$rOpts{'url'} = $cfg{'money-url'}; $$rOpts{'subtitle'} = 'Currency Exchange'; push(@MailHeaders, "Subject: $cfg{'money-subj'}"); } if ($optNews) { $cmd .= 'slashdot'; $$rOpts{'url'} = $cfg{'news-url'}; $$rOpts{'subtitle'} = 'Slashdot Linux News'; push(@MailHeaders, "Subject: $cfg{'news-subj'}"); } if ($optAnnounce) { push(@MailHeaders, "Subject: $modSubject"); # CR-003 } @d = split(/\s+/, $date); $date = "$d[0], $d[2] $d[1] $d[4] $d[3]"; # Time zone? push(@MailHeaders, "Date: $date"); return $cmd; } sub SetBodyHeader { my $subtitle = shift; if ($optWeb) { $subtitle = " - $subtitle"; push(@MailBody, "

$Product v$cvsId $subtitle


\n"); } else { push(@MailBody, "\t********************************\n"); push(@MailBody, "\t* $Product v$cvsId\n"); push(@MailBody, "\t********************************\n"); } } sub SetBodyFooter { my $rOpts = shift; my $website = 'www.iae.nl/users/grimaldo/OpenSoft/'; my ($chars, $frame, $url); $chars = length("$$rOpts{'url'}/"); $chars = length($website) if ($chars < length($website)); $chars += 9; $frame = ($optWeb ? '
' : "\t" . "*" x $chars . "\n"); push(@MailBody, $frame); if (!$optWeb) { push(@MailBody, "\t* http://$$rOpts{'url'}/\n") unless $optAnnounce; push(@MailBody, "\t* http://$website\n"); } else { push(@MailBody, &QuUrl($$rOpts{'url'}, 'Nasdaq-Amex') . '
') unless $optAnnounce; push(@MailBody, &QuUrl($website, 'OpenSoft') . '

'); } push(@MailBody, "$frame\n"); } sub GetRecipients { my $list; my $cnt = 0; my $ccCnt = 0; my $Cc = 'Bcc: '; # Use blind copies for privacy if ($optTest ne '') { # In test mode we only create a raw mail output but don't # actually send it to the list, only to the test recipient push(@MailHeaders, "To: $optTest"); return; } $list = $cfg{'stock-list'} if ($optStocks); $list = $cfg{'stock-w-list'} if ($optStocks && $optWeb); # @CR-001 $list = $cfg{'money-list'} if ($optMoney); $list = $cfg{'news-list'} if ($optNews); $list = $cfg{'announce-list'} if ($optAnnounce); open(RECIPIENTS,"< $list") || die "$Product::GetRecipients $!\n"; while () { next if (/^\s*$/ || /^\s*#/); next if (!($_ =~ m/\@/)); chomp; $cnt++; if ($cnt == 1) { push(@MailHeaders, "To: $_"); } else { $ccCnt++; if ($ccCnt == 5) { # Only 6 addresses per line $Cc .= ",$_"; $ccCnt = 0; # reset push(@MailHeaders, "$Cc"); $Cc = ''; next; } elsif ($ccCnt == 1) { if ($cnt != 2) { push(@MailHeaders, "$Cc") if (length($Cc)); $Cc = "\t,$_"; # first in a continuation line } else { $Cc .= "$_"; # first in the first Cc line } } else { $Cc .= ",$_"; } } } if (length($Cc) && $cnt > 1) { push(@MailHeaders, "$Cc"); } close(RECIPIENTS); } sub GetData { my $cmd = shift; if ($optAnnounce ne '' && -f $optAnnounce) { open(QUOTES,"< $optAnnounce") || die "$Product::GetData $!\n"; } else { open(QUOTES,"$cmd |") || die "$Product::GetData $!\n"; } while () { push(@MailBody, $_); } close(QUOTES); } sub SendMail { my $i; my $MAIL; if ($optTest eq '') { open (PIPEOUT,"| $cfg{'mail-bin'}") || die "$Product::SendMail $!\n"; $MAIL = \*PIPEOUT; } else { $MAIL = \*STDOUT; } foreach $i (0 .. $#MailHeaders ) { print $MAIL "$MailHeaders[$i]\n"; } print $MAIL "\n"; foreach $i (0 .. $#MailBody ) { print $MAIL "$MailBody[$i]"; } close($MAIL); } sub QuUrl { my $url = shift; my $txt = shift; return "http://$url" if !$optWeb; return "$txt"; } # ***************************************************** # * M A I N * # ***************************************************** my $agent; my %options; &Initialize; &Help if ($#ARGV == -1); &GetOptions('m|money' => \$optMoney, 's|stocks' => \$optStocks, 'n|news' => \$optNews, 'test|t=s' => \$optTest, # -test me@there.com 'testwith=s' => \$optTestWith, # -testwith /tmp/mystocks.html 'announce|a=s' => \$optAnnounce, # -announce notice.txt 'subject=s' => \$modSubject, # Message subject (announce) 'w|web' => \$optWeb, # Use alternate HTML format 'h|help' => \$optHelp ); &Help if ($optHelp); $agent = &SetOptions(\%options); &GetRecipients; &SetBodyHeader($options{'subtitle'}); &GetData($agent); &SetBodyFooter(\%options); &SendMail; # ***************************************************** # * H I S T O R Y * # ***************************************************** # 28-may-99 Date mail header gets Winblows clients confused # 23-oct-99 DEGT CR-001 Support primitive HTML output (Quotes CR-007) # 26-nov-99 DEGT CR-002 Enhanced GetRecipients # 26-nov-99 DEGT PR-001 Enhanced/fixed HTML output # 03-feb-00 DEGT PR-002 Error on every fifth of the copy list # 05-jun-00 DEGT CR-003 Added --subject for use with announce # 28-sep-00 DEGT CR-013 Quotes now uses 'currency' instead of 'money' # 30-sep-00 DEGT v1.4 for Quotes Release v1.7