6738b82fc88a631d41932b156e53724a0535d606
[staff.git] / shift.cgi
1 #!/usr/bin/perl -w
2 #!/usr/bin/perl -Tw
3 # (Text::Template can't do -T, but no user input is used dangerously)
4 #
5 # $Id: shift.cgi,v 1.5 2002-07-10 01:08:49 ivan Exp $
6 #
7 # Copyright (C) 2000 Adam Gould
8 # Copyright (C) 2000 Michal Migurski
9 # Copyright (C) 2000 Ivan Kohler
10 # All rights reserved.
11 #
12 # This program is free software; you can redistribute it and/or modify it under
13 # the same terms as Perl itself.
14
15 ###
16 # user-servicable parts
17 ###
18
19 $template_file = '/var/www/www.420.am/staff/table.html';
20 $data_directory = '/var/www/www.420.am/staff/data';
21 #$mail_smtpserver = 'localhost'; # set blank to disable
22 $mail_smtpserver = ''; # set blank to disable
23 $mail_from = 'ivan-misconfigured-shift-from@420.am';
24 $mail_subject = 'Your shift has been replaced';
25 @mail_cc = (
26   'ivan-misconfigured-shift-cc@420.am',
27   'ivan-misconfigured-shift-cc2@420.am'
28 );
29 $mail_footer = <<END;
30
31 Sorry, I don't have any more information.  The person who installed the 
32 staff sheet didn't customize this message.
33
34 END
35
36 ###
37 # end of user-servicable parts
38 ###
39
40 use strict;
41 use vars qw( $template_file $data_directory $mail_smtpserver $mail_from
42              $mail_subject @mail_cc $mail_footer
43              $cgi $template %shifthash %warning @messages $magic );
44 use subs qw( form inputbox );
45 use Fcntl ":flock";
46 use CGI 2.15;
47 use CGI::Carp qw(fatalsToBrowser);
48 use Text::Template;
49 use Mail::Internet;
50 use Mail::Header;
51 use Date::Format;
52
53 $cgi = new CGI;
54
55 $template = new Text::Template (
56   TYPE   => 'FILE',
57   SOURCE => $template_file,
58 ) or die "Can't create template for $template_file: $Text::Template::ERROR";
59
60 # fill in new data if provided
61 %warning = ();
62 @messages = ();
63 if ( $cgi->param() ) {
64
65   # kludge - grep for inputbox("field") in template to find valid form fields
66   # (DON'T get them from form submission - that's insecure!)
67   open (TEMPLATE_FILE,"<$template_file")
68     or die "Can't open template for $template_file: $!";
69   my @form_fields =
70     map { /inputbox\s*\(\s*([\'\"])(.*)\1\s*\)/; $2 }
71       #grep { /inputbox\s*\(\s*([\'\"])(.*)\1\s*\)/ }
72       grep { /inputbox\s*\(\s*([\'\"])(.*)\1\s*\);?\s*\}/ }
73         <TEMPLATE_FILE>;
74   close TEMPLATE_FILE;
75
76   #changed fields
77   #foreach $_ ( @form_fields ) {
78   #  warn "${_}_old undefined!" unless defined $cgi->param($_. '_old');
79   #  warn "${_}_new undefined!" unless defined $cgi->param($_. '_new');
80   #}
81   my @diff_fields =
82     grep { $cgi->param($_. '_old') ne $cgi->param($_. '_new') } @form_fields;
83
84   if ( @diff_fields ) {
85
86     local $SIG{HUP} = 'IGNORE';
87     local $SIG{INT} = 'IGNORE';
88     local $SIG{QUIT} = 'IGNORE';
89     local $SIG{TERM} = 'IGNORE';
90     local $SIG{TSTP} = 'IGNORE';
91     local $SIG{PIPE} = 'IGNORE';
92
93     #open(LOCKFILE,">>$data_directory/.lock")
94     open(LOCKFILE,"+<$data_directory/.lock")
95       or open(LOCKFILE,">>$data_directory/.lock")
96       or die "Can't open $data_directory/.lock: $!";
97     flock(LOCKFILE,LOCK_EX); #blocks until we have the lock
98     seek(LOCKFILE, 0, 0);
99     print LOCKFILE "$$     \n"; #superfluous
100
101     get_data();
102
103     foreach my $field ( @diff_fields ) {
104       $shifthash{$field}='' unless defined $shifthash{$field};
105       if ( $shifthash{$field} eq $cgi->param($field. '_old') ) {
106       if ( $cgi->param($field. "_new") =~
107              /^\s*(\w[\w\s\.\'\-]{0,99}<?\s{0,9}(\w[\w\-\.\+]{0,99}\@(([\w\.\-]{1,99}\.){1,99}\w{1,99}))\s{0,9}>?)\s*$/
108            || $cgi->param($field. "_new") =~ /^\s*()$/
109         ) {
110           my $new = $1;
111           open(FILE,">$data_directory/.new.$field")
112             or die "Can't open file $data_directory/$field: $!";
113           print FILE $new;
114           close FILE;
115           rename "$data_directory/.new.$field", "$data_directory/$field";
116           $warning{$field} = '';
117           if (
118             $mail_smtpserver
119             && $shifthash{$field} =~ /\b(\w[\w\-\.\+]*\@(([\w\.\-]+\.)+\w+))\b/
120           ) {
121             my $to = $1;
122             my $header = Mail::Header->new( [
123               "From: $mail_from",
124               "To: $to",
125               "Cc: ". join(", ", @mail_cc),
126               "Sender: $mail_from",
127               "Reply-To: $mail_from",
128               "Date: ". time2str("%a, %d %b %Y %X %z", time),
129               "Subject: $mail_subject",
130             ] );
131             my $msg = Mail::Internet->new(
132               'Header' => $header,
133               'Body'   => [ map "$_\n",
134                 "Hi,",
135                 "",
136                 "The \"$field\" shift you signed up for has been changed to",
137                 '"'. $new. '"',
138                 "",
139                 split("\n", $mail_footer),
140               ],
141             );
142             #send later - don't want to block on smtp while we have the lock
143             push @messages, $msg;
144           }
145         } else {
146           $warning{$field} = 
147             "WARNING: you tried to sign up for <B>$field</B>, but your entry ".
148             "<B>\"". $cgi->param($field. '_new').
149             "</B>\" does not contain a valid email address."
150           ;
151         }
152       } elsif ( $shifthash{$field} eq $cgi->param($field. '_new') ) {
153         #somebody else made the same change (or you hit reload); no need to warn
154         $warning{$field} = '';
155       } else {
156         $warning{$field} =
157           "WARNING: you tried to change <B>$field</B> from \"<B>".
158           $cgi->param($field. '_old').
159           "</B>\" to \"<B>".
160           $cgi->param($field. "_new").
161           "</B>\", but in the meantime someone changed it to: "
162         ;
163       }
164     }
165
166     flock(LOCKFILE,LOCK_UN);
167     close LOCKFILE;
168
169   }
170
171 }
172
173 get_data();
174
175 my $text = $template->fill_in()
176   or die "Can't fill in template for $template_file: $Text::Template::ERROR";
177
178 print $cgi->header, $text;
179
180 $ENV{SMTPHOSTS} = $mail_smtpserver;
181 $ENV{MAILADDRESS} = $mail_from;
182 foreach my $msg ( @messages ) {
183   $msg->smtpsend;
184 }
185
186 # subroutines
187
188 sub get_data {
189   opendir DATA_DIR, $data_directory
190     or die "Can't open directory $data_directory: $!";
191   %shifthash = map {
192     open(FILE, "<$data_directory/$_")
193       or die "Can't open file $data_directory/$_: $!";
194     my $value = scalar(<FILE>) || '';
195     close FILE;
196     chomp $value;
197     ( $_ => $value );
198   } grep { ! /^\.{1,2}(lock)?$/ } readdir(DATA_DIR);
199   closedir DATA_DIR;
200 }
201
202 # subroutines for the template
203
204 sub form {
205  $magic = defined $cgi->param('__MAGIC') ? $cgi->param('__MAGIC') : '';
206  $cgi->delete_all();
207  $cgi->start_form();
208 }
209
210 sub inputbox {
211   my $field = shift;
212   return $shifthash{$field} || "&nbsp;"
213     if $magic eq 'print';
214   $shifthash{$field}='' unless defined $shifthash{$field};
215   $warning{$field}='' unless defined $warning{$field};
216   #"$field ".
217   $cgi->hidden(
218     -name    => $field. '_old',
219     -default => $shifthash{$field},
220     -force   => 1,
221   ).
222   $warning{$field}.
223   $cgi->textfield(
224     -name    => $field. '_new',
225     -default => $shifthash{$field},
226     -force   => 1,
227     -size    => 15,
228   );
229 }
230
231 sub warnings {
232   join "<BR>", map {
233     "$warning{$_}<b>$shifthash{$_}</b>"
234   } grep {
235     $warning{$_}
236   } keys %warning;
237 }
238