#!/usr/bin/perl # Copyright 2000-2003 Fred Steinberg, Brown Bear Software # Check for and send Email Reminders, and Email Subscriptions use strict; #### USER CONFIGURATION #### BEGIN {$Defines::calendar_root = '.';} my %defaults = (baseURL => 'http://your.domain.com/cgi-bin/Calcium.pl', defaultDays => 3); #### END CONFIGURATION #### use lib $Defines::calendar_root; use lib "$Defines::calendar_root/upgrades"; # Read lines from the Calcium Reminder file until we get to the current # date/time. For each Reminder, send the reminding email. # Also send Subscription email, but only once a day. # Pass no args to run just once (typically via cron). (my $usage = <<'END_USAGE') =~ s/^ //gm; Usage: SendReminders [OPTIONS] Email reminder script for Calcium web calendar Options: -i daemon mode; # of minutes to wait between runs -r integer; # of repetitions (-i also required) -q quiet - don't warn on errors or when sending mail -v verbose - be noisy -f process Subscriptions, even if already done today -n don't actually send any mail (for testing) -d
deliver all mail to
(for testing) (currently only for Subscriptions, not Reminders) -b URL of Calcium script, for links in email E.g. SendReminders # run once, and stop. Recommended usage. SendReminders -i 15 # run forever as a dameon, wake up every 15 minutes to send mail SendReminders -vn # don't actually send any mail; just print what's happening to stderr SendReminders -vd me@foo.com # redirect all mail to me@foo.com, with verbose output to console. For testing; mail does _not_ get sent to actual addressee SendReminders -b http://foo.com/cgi-bin/Calcium.pl END_USAGE # ' (for emacs syntax highlighting) use Calendar::Mail::MailReminder; use Getopt::Std; my %opts; getopts ("qvnfb:d:i:r:", \%opts) or die "$usage\n"; my $numReps = $opts{r}; my $interval = $opts{i}; my $quiet = !$opts{q}; my $verbose = $opts{v}; my $deliverTo = $opts{d}; my $dryRun = $opts{n}; my $forceSubs = $opts{f}; my $baseURL = $opts{b} || $defaults{baseURL}; my $error; if (exists $opts{i}) { $error = "interval must be positive integer" unless ($interval and $interval =~ /^\d+$/); } if (exists $opts{r}) { $error = "number of repetitions must be positive integer" unless ($numReps and $numReps =~ /^\d+$/); } $error = "must specify interval for number reps > 1" if (defined ($numReps) and $numReps > 1 and !$interval); die "Error: $error\n\n$usage\n" if ($error); my $warnOnMail = $opts{q}; my $defaultDays = $defaults{defaultDays}; if ($verbose) { warn "-n specified; no mail will be sent.\n\n" if $dryRun; warn "-d specified; all mail will be sent to $deliverTo\n\n" if (!$dryRun and $deliverTo); } my $forkIt = ($interval and ($^O !~ /mswin32/i)); # if interval specified, fork and let the child run forever. (not on Windows) if ($forkIt) { my $child; $child = fork; die "fork failed: $!\n" unless defined $child; exit if $child; die "fork failed: $!\n" unless defined $child; require POSIX; POSIX::setsid() or die "Can't create new session: $!\n"; # ignore SIGHUP; when session leader exits, its kids get HUPped. my $old_hup = $SIG{HUP}; $SIG{HUP} = 'IGNORE'; # fork again, to ensure no controlling terminal $child = fork; die "fork failed: $!\n" unless defined $child; exit if $child; close STDIN; close STDOUT; close STDERR unless $verbose; # die if the main script tells us to in its own special way $SIG{WINCH} = sub { exit }; # and pay attention to HUPs now $SIG{HUP} = 'DEFAULT'; } do { my $pid = $$; my $isForkedChild; # if in daemon mode, fork and let the child do the work and exit, # to ensure things are cleaned up (Except on Windows.) if ($forkIt) { my $child; $SIG{CHLD} = 'IGNORE'; $child = fork; die "fork failed in doReminders: $!\n" unless defined $child; $isForkedChild++ if (!$child); } if ($isForkedChild or !$forkIt) { _doReminders ($pid); _doSubscriptions(); # no-op, except for once a day } exit if ($isForkedChild); $numReps-- if defined $numReps; if ($interval and (!defined $numReps or ($numReps > 0))) { warn "sleeping for $interval minutes...\n" if $verbose; sleep ($interval * 60); } } while ($interval and (!defined $numReps or $numReps)); warn "$0 done at " . localtime (time) . "\n" if $verbose; 0; # ---------- END ---------- sub _doReminders { my $parentPID = shift; MailReminder->open ('readwrite'); # open and lock my $time = time; MailReminder->setLastProcessed ($time, $parentPID); my @reminders = MailReminder->getUpTo ($time); warn "Found ", scalar(@reminders) . " reminders to process\n" if ($verbose); my (@deleteThese, $sentCount, $errors, $tooOld); foreach (@reminders) { # if Reminder is over 1 day old, we don't bother sending it $_->sendIt (earliest => $time-60*60*24, dryRun => $dryRun, deliverTo => $deliverTo, verbose => $verbose); unshift @deleteThese, $_; $sentCount++ if ($_->sendStatus =~ /sent/); $errors++ if ($_->sendStatus =~ /error/); $tooOld++ if ($_->sendStatus =~ /tooOld/); } MailReminder->delete (@deleteThese) unless ($dryRun); MailReminder->close; # release the lock $sentCount ||= 0; if ($verbose or !$quiet) { my $remind = ('reminder' . ($sentCount == 1 ? '' : 's')); warn "Calcium Mail Reminder sent " . $sentCount . " $remind at " . scalar localtime ($time) . "\n" if ($sentCount); warn "There were $errors errors\n" if $errors; warn "$tooOld reminders were too old to bother with\n" if $tooOld; warn "\n"; } } # Do Mail Subscriptions if we haven't already today sub _doSubscriptions { my $lastSubRun = MailReminder->getLastSubscriptionRun; my @then = localtime ($lastSubRun || 0); my $rightThisInstant = time; my @now = localtime ($rightThisInstant); if ($forceSubs or $then[5] < $now[5] # new year or $then[4] < $now[4] # new month or $then[3] < $now[3]) { # new day my $forced = $forceSubs ? '(forced)' : ''; warn "Processing subscriptions $forced\n" if ($verbose); require Calendar::Mail::MailSubscription; my $msub = MailSubscription->new (baseURL => $baseURL, defaultDays => $defaultDays, verbose => $verbose, dryRun => $dryRun, deliverTo => $deliverTo); $msub->sendForToday; MailReminder->setLastSubscriptionRun ($rightThisInstant); } else { warn "Subscriptions ignored; already processed today.\n" if ($verbose); } }