#!/usr/bin/env perl # # Copyright (c) 2020-2023 Lorenzo Salvadore # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. use strict; use warnings; use Getopt::Std; # ------------------------------------------------------- # Global variables declaration # ------------------------------------------------------- # Variables used to compute the time coordinates of the call (see below) # # They can be computed based on present date or given on the command line # through options (see --help). my $day; my $month; my $year; # Time coordinates of the call # # - $quarter is the number of the quarter for which we are sending the # calls. # - $urgency_tag indicates the urgency with which we are requesting the # reports. It will be included in the subject of the calling mail. It # can be empty, [2 WEEKS LEFT REMINDER] or [LAST OFFICIAL REMINDER]. my $quarter; my $urgency_tag; # Variables related to the contacts of the last status reports # # $year_last, $month_last_start, %month_last_stop and $quarter_last are # the year, the initial month, the last month and the number of the # quarter of the last quarterly status reports. They are used to compute # $quarter_last_directory, the directory where the reports for last # quarter can be found. my $year_last; my $month_last_start; my $month_last_stop; my $quarter_last; my $quarter_last_directory; # Variables related to the calls mail we are sending # # - $subject is the subject of the mail we send. # - $send_command is the command we run to send the mail. # - @bcc_recipients and @cc_recipients are the array of all the addresses # we want to put in the BCC and CC field respectively. The To field # contains freebsd-status-calls@FreeBSD.org. my $subject; my $send_command; my @bcc_recipients; my @cc_recipients; # Other variables # - %quarter_specific_recipients is a hash that contains lists of # addresses. The addresses of each list should be contacted only for the # quarter specified by the list's the keys. # - %template_substitutions is a hash that specifies for each quarter how # the variabes in the call.template file should be substituted. # - %options is a hash used for registering option. See --help to list all # available options. my %quarter_specific_recipients; my %template_substitutions; my %options; # ------------------------------------------------------- # Variables initialization # ------------------------------------------------------- @bcc_recipients = (); @cc_recipients = ( 'freebsd-current@FreeBSD.org', 'freebsd-hackers@FreeBSD.org', 'devsummit@FreeBSD.org' ); $quarter_specific_recipients{1} = [ 'secretary@asiabsdcon.org' ]; $quarter_specific_recipients{2} = [ 'info@bsdcan.org', 'soc-students@FreeBSD.org', 'soc-mentors@FreeBSD.org' ]; $quarter_specific_recipients{3} = [ 'soc-students@FreeBSD.org', 'soc-mentors@FreeBSD.org' ]; $quarter_specific_recipients{4} = []; $template_substitutions{1}{'%%START%%'} = 'January'; $template_substitutions{1}{'%%STOP%%'} = 'March'; $template_substitutions{1}{'%%START_NUM%%'} = '01'; $template_substitutions{1}{'%%STOP_NUM%%'} = '03'; $template_substitutions{1}{'%%DEADLINE%%'} = 'March, 31st'; $template_substitutions{2}{'%%START%%'} = 'April'; $template_substitutions{2}{'%%STOP%%'} = 'June'; $template_substitutions{2}{'%%START_NUM%%'} = '04'; $template_substitutions{2}{'%%STOP_NUM%%'} = '06'; $template_substitutions{2}{'%%DEADLINE%%'} = 'June, 30th'; $template_substitutions{3}{'%%START%%'} = 'July'; $template_substitutions{3}{'%%STOP%%'} = 'September'; $template_substitutions{3}{'%%START_NUM%%'} = '07'; $template_substitutions{3}{'%%STOP_NUM%%'} = '09'; $template_substitutions{3}{'%%DEADLINE%%'} = 'September, 30th'; $template_substitutions{4}{'%%START%%'} = 'October'; $template_substitutions{4}{'%%STOP%%'} = 'December'; $template_substitutions{4}{'%%DEADLINE%%'} = 'December, 31st'; $template_substitutions{4}{'%%START_NUM%%'} = '10'; $template_substitutions{4}{'%%STOP_NUM%%'} = '12'; $main::VERSION = "[not versioned]"; $Getopt::Std::STANDARD_HELP_VERSION = 1; # ------------------------------------------------------- # Subroutines definition # ------------------------------------------------------- sub main::HELP_MESSAGE { print <= 1970 (I think you are unlikely to send calls before the Unix epoch. And I am writing it in 2020.) -t Testing flag. Set it it you want to test the script without actually send mails. -s signature Name to use for signing the status reports calls mail. Example: ./sendcalls -d 31 -m 2 -y 2000 -s 'Lorenzo Salvadore' (Yes, you can send calls even on inexistent days such as 2020 February, 31st.) EOT exit 1; } # ------------------------------------------------------- # Execution starts here # ------------------------------------------------------- getopts('d:m:y:s:t', \%options); main::HELP_MESSAGE if(not $options{'s'}); # ------------------------------------------------------- # Compute time coordinates (see global variables declaration) # ------------------------------------------------------- (undef, undef, undef, $day, $month, $year, undef, undef, undef) = localtime(); $year = $year + 1900; $day = $options{'d'} if($options{'d'}); $month = $options{'m'} - 1 if($options{'m'}); $year = $options{'y'} if($options{'y'}); die "Choosen date does not seem plausibile: year is $year, month is $month and day is $day" if( $day < 1 or $day > 31 or $month < 1 or $month > 12 or $year < 1970 ); if($day < 14) { $urgency_tag = ''; } elsif($day < 23) { $urgency_tag = '[2 WEEKS LEFT REMINDER] '; } else { $urgency_tag = '[LAST OFFICIAL REMINDER] '; } $quarter = int($month / 3) + 1; # ------------------------------------------------------- # Compute @bcc_recipients and @cc_recipients # ------------------------------------------------------- $year_last = $year; $month_last_start = sprintf("%02d",int((($month - 3) % 12) / 3) * 3 + 1); $month_last_stop = sprintf("%02d",$month_last_start + 2); $quarter_last = $quarter - 1; if($quarter_last == 0) { $year_last = $year_last - 1; $quarter_last = 4; } $quarter_last_directory = '../../website/content/en/status/report-'. $year_last. '-'. $month_last_start. '-'. $year_last. '-'. $month_last_stop; foreach(`ls $quarter_last_directory`) { $_ =~ tr/\n//d; open(quarterly_report, '<', $quarter_last_directory.'/'.$_) or die "Could not open $quarter_last_directory/$_: $!"; while() { if($_ =~ m/^Contact:.*(<.*@.*>)/) { my $address = $1; $address =~ tr/<>//d; push @bcc_recipients, $address if(not $address =~ m/\@FreeBSD.org$/i); } } close(quarterly_report); } { my %tmp = map {$_ => 0} @bcc_recipients; @bcc_recipients = keys %tmp; } push @cc_recipients, @{ $quarter_specific_recipients{$quarter} }; # ------------------------------------------------------- # Compute missing %template_substitutions elements # ------------------------------------------------------- $template_substitutions{$quarter}{'%%QUARTER%%'} = $quarter; $template_substitutions{$quarter}{'%%YEAR%%'} = $year; $template_substitutions{$quarter}{'%%SIGNATURE%%'} = $options{'s'}; $template_substitutions{$quarter}{'%%DEADLINE%%'} = $template_substitutions{$quarter}{'%%DEADLINE%%'}.' '.$year; # ------------------------------------------------------- # Generate mail text # ------------------------------------------------------- open(call_template, '<', 'call.txt.template') or die "Could not open call.txt.template: $!"; open(call_mail, '>', 'call.txt') or die "Could not open call.txt: $!"; while() { my $line = $_; $line =~ s/$_/$template_substitutions{$quarter}{$_}/g foreach(keys %{ $template_substitutions{$quarter} }); print call_mail $line; } close(call_template); close(call_mail); # ------------------------------------------------------- # Compute $subject and $send_command # ------------------------------------------------------- $subject = $urgency_tag."Call for ".$year."Q".$quarter." status reports"; $send_command = "cat call.txt | mail -s \"".$subject."\""; # @bcc_recipients should never be empty as we have reports with mail # contacts every quarter, but we test it anyway: we do not want to # assume that this will be true forever $send_command = $send_command.' -b '.(join ',', @bcc_recipients) if(@bcc_recipients); # @cc_recipients should never be empty as we initialize it not empty # and never remove addresses from there, but we test it anyway: we do # not want to assume that this will be true forever $send_command = $send_command.' -c '.(join ',', @cc_recipients) if(@cc_recipients); $send_command = $send_command.' freebsd-status-calls@FreeBSD.org'; # ------------------------------------------------------- # Send mail or show testing information # ------------------------------------------------------- if($options{'t'}) { print <; close(call_mail); } else { system $send_command; } # ------------------------------------------------------- # Clean environment # ------------------------------------------------------- unlink "call.txt";