safe_asterisk: Cleanup and debian compatibility.
[asterisk/asterisk.git] / contrib / scripts / vmail.cgi
1 #!/usr/bin/perl
2 #
3 # Web based Voicemail for Asterisk
4 #
5 # Copyright (C) 2002, Linux Support Services, Inc.
6 #
7 # Distributed under the terms of the GNU General Public License
8 #
9 # Written by Mark Spencer <markster@linux-support.net>
10 #
11 # (icky, I know....  if you know better perl please help!)
12 #
13 #
14 # Synchronization added by GDS Partners (www.gdspartners.com)
15 #                        Stojan Sljivic (stojan.sljivic@gdspartners.com)
16 #
17 use CGI qw/:standard/;
18 use Carp::Heavy;
19 use CGI::Carp qw(fatalsToBrowser);
20 use DBI;
21 use Fcntl qw ( O_WRONLY O_CREAT O_EXCL );
22 use Time::HiRes qw ( usleep );
23
24 $context=""; # Define here your by default context (so you dont need to put voicemail@context in the login)
25
26 @validfolders = ( "INBOX", "Old", "Work", "Family", "Friends", "Cust1", "Cust2", "Cust3", "Cust4", "Cust5" );
27
28 %formats = (
29         "wav" => {
30                 name => "Uncompressed WAV",
31                 mime => "audio/x-wav",
32                 pref => 1
33         },
34         "WAV" => {
35                 name => "GSM Compressed WAV",
36                 mime => "audio/x-wav",
37                 pref => 2
38         },
39         "gsm" => {
40                 name => "Raw GSM Audio",
41                 mime => "audio/x-gsm",
42                 pref => 3
43         }
44 );
45
46 $astpath = "/_asterisk";
47
48 $stdcontainerstart = "<table align=center width=600><tr><td>\n";
49 $footer = "<hr><font size=-1><a href=\"http://www.asterisk.org\">The Asterisk Open Source PBX</a> Copyright 2004-2008, <a href=\"http://www.digium.com\">Digium, Inc.</a></a>";
50 $stdcontainerend = "</td></tr><tr><td align=right>$footer</td></tr></table>\n";
51
52 sub lock_path($) {
53
54         my($path) = @_;
55         my $rand;
56         my $rfile;
57         my $start;
58         my $res;
59         
60         $rand = rand 99999999;  
61         $rfile = "$path/.lock-$rand";
62         
63         sysopen(RFILE, $rfile, O_WRONLY | O_CREAT | O_EXCL, 0666) or return -1;
64         close(RFILE);
65         
66         $res = link($rfile, "$path/.lock");
67         $start = time;
68         if ($res == 0) {
69         while (($res == 0) && (time - $start <= 5)) {
70                 $res = link($rfile, "$path/.lock");
71                 usleep(1);
72         }
73         }
74         unlink($rfile);
75         
76         if ($res == 0) {
77                 return -1;
78         } else {
79                 return 0;
80         }
81 }
82
83 sub unlock_path($) {
84
85         my($path) = @_;
86         
87         unlink("$path/.lock");
88 }
89
90 sub untaint($) {
91
92         my($data) = @_;
93         
94         if ($data =~ /^([-\@\w.]+)$/) {
95                 $data = $1;
96         } else {
97                 die "Security violation.";
98         }
99         
100         return $data;
101 }
102
103 sub login_screen($) {
104         print header;
105         my ($message) = @_;
106         print <<_EOH;
107
108 <TITLE>Asterisk Web-Voicemail</TITLE>
109 <BODY BGCOLOR="white">
110 $stdcontainerstart
111 <FORM METHOD="post">
112 <input type=hidden name="action" value="login">
113 <table align=center>
114 <tr><td valign=top align=center rowspan=6><img align=center src="$astpath/animlogo.gif"></td></tr>
115 <tr><td align=center colspan=2><font size=+2>Comedian Mail Login</font></td></tr>
116 <tr><td align=center colspan=2><font size=+1>$message</font></td></tr>
117 <tr><td>Mailbox:</td><td><input type=text name="mailbox"></td></tr>
118 <tr><td>Password:</td><td><input type=password name="password"></td></tr>
119 <tr><td align=right colspan=2><input value="Login" type=submit></td></tr>
120 <input type=hidden name="context" value="$context">
121 </table>
122 </FORM>
123 $stdcontainerend
124 </BODY>\n
125 _EOH
126
127 }
128
129 sub check_login($$)
130 {
131         local ($filename, $startcat) = @_;
132         local ($mbox, $context) = split(/\@/, param('mailbox'));
133         local $pass = param('password');
134         local $category = $startcat;
135         local @fields;
136         local $tmp;
137         local (*VMAIL);
138         if (!$category) {
139                 $category = "general";
140         }
141         if (!$context) {
142                 $context = param('context');
143         }
144         if (!$context) {
145                 $context = "default";
146         }
147         if (!$filename) {
148                 $filename = "/etc/asterisk/voicemail.conf";
149         }
150 #       print header;
151 #       print "Including <h2>$filename</h2> while in <h2>$category</h2>...\n";
152         open(VMAIL, "<$filename") || die("Bleh, no $filename");
153         while(<VMAIL>) {
154                 chomp;
155                 if (/include\s\"([^\"]+)\"$/) {
156                         ($tmp, $category) = &check_login("/etc/asterisk/$1", $category);
157                         if (length($tmp)) {
158 #                               print "Got '$tmp'\n";
159                                 return ($tmp, $category);
160                         }
161                 } elsif (/\[(.*)\]/) {
162                         $category = $1;
163                 } elsif ($category eq "general") {
164                         if (/([^\s]+)\s*\=\s*(.*)/) {
165                                 if ($1 eq "dbname") {
166                                         $dbname = $2;
167                                 } elsif ($1 eq "dbpass") {
168                                         $dbpass = $2;
169                                 } elsif ($1 eq "dbhost") {
170                                         $dbhost = $2;
171                                 } elsif ($1 eq "dbuser") {
172                                         $dbuser = $2;
173                                 }
174                         }
175                         if ($dbname and $dbpass and $dbhost and $dbuser) {
176
177                                 # db variables are present.  Use db for authentication.
178                                 my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost",$dbuser,$dbpass);
179                                 my $sth = $dbh->prepare(qq{select fullname,context from voicemail where mailbox='$mbox' and password='$pass' and context='$context'});
180                                 $sth->execute();
181                                 if (($fullname, $category) = $sth->fetchrow_array()) {
182                                         return ($fullname ? $fullname : "Extension $mbox in $context",$category);
183                                 }
184                         }
185                 } elsif (($category ne "general") && ($category ne "zonemessages")) { 
186                         if (/([^\s]+)\s*\=\>?\s*(.*)/) {
187                                 @fields = split(/\,\s*/, $2);
188 #                               print "<p>Mailbox is $1\n";
189                                 if (($mbox eq $1) && (($pass eq $fields[0]) || ("-${pass}" eq $fields[0])) && ($context eq $category)) {
190                                         return ($fields[1] ? $fields[1] : "Extension $mbox in $context", $category);
191                                 }
192                         }
193                 }
194         }
195         close(VMAIL);
196         return check_login_users();
197 }
198
199 sub check_login_users {
200         my ($mbox, $context) = split(/\@/, param('mailbox'));
201         my $pass = param('password');
202         my ($found, $fullname) = (0, "");
203         open VMAIL, "</etc/asterisk/users.conf";
204         while (<VMAIL>) {
205                 chomp;
206                 if (m/\[(.*)\]/) {
207                         if ($1 eq $mbox) {
208                                 $found = 1;
209                         } elsif ($found == 2) {
210                                 close VMAIL;
211                                 return (($fullname ? $fullname : "Extension $mbox in $context"), $context);
212                         } else {
213                                 $found = 0;
214                         }
215                 } elsif ($found) {
216                         my ($var, $value) = split /\s*=\s*/, $_, 2;
217                         if ($var eq 'vmsecret' and $value eq $pass) {
218                                 $found = 2;
219                         } elsif ($var eq 'fullname') {
220                                 $fullname = $value;
221                                 if ($found == 2) {
222                                         close VMAIL;
223                                         return ($fullname, $context);
224                                 }
225                         }
226                 }
227         }
228         close VMAIL;
229         return ("", "");
230 }
231
232 sub validmailbox($$$$)
233 {
234         local ($context, $mbox, $filename, $startcat) = @_;
235         local $category = $startcat;
236         local @fields;
237         local (*VMAIL);
238         if (!$context) {
239                 $context = param('context');
240         }
241         if (!$context) {
242                 $context = "default";
243         }
244         if (!$filename) {
245                 $filename = "/etc/asterisk/voicemail.conf";
246         }
247         if (!$category) {
248                 $category = "general";
249         }
250         open(VMAIL, "<$filename") || die("Bleh, no $filename");
251         while (<VMAIL>) {
252                 chomp;
253                 if (/include\s\"([^\"]+)\"$/) {
254                         ($tmp, $category) = &validmailbox($mbox, $context, "/etc/asterisk/$1");
255                         if ($tmp) {
256                                 return ($tmp, $category);
257                         }
258                 } elsif (/\[(.*)\]/) {
259                         $category = $1;
260                 } elsif ($category eq "general") {
261                         if (/([^\s]+)\s*\=\s*(.*)/) {
262                                 if ($1 eq "dbname") {
263                                         $dbname = $2;
264                                 } elsif ($1 eq "dbpass") {
265                                         $dbpass = $2;
266                                 } elsif ($1 eq "dbhost") {
267                                         $dbhost = $2;
268                                 } elsif ($1 eq "dbuser") {
269                                         $dbuser = $2;
270                                 }
271                         }
272                         if ($dbname and $dbpass and $dbhost and $dbuser) {
273
274                                 # db variables are present.  Use db for authentication.
275                                 my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost",$dbuser,$dbpass);
276                                 my $sth = $dbh->prepare(qq{select fullname,context from voicemail where mailbox='$mbox' and password='$pass' and context='$context'});
277                                 $sth->execute();
278                                 if (($fullname, $context) = $sth->fetchrow_array()) {
279                                         return ($fullname ? $fullname : "unknown", $category);
280                                 }
281                         }
282                 } elsif (($category ne "general") && ($category ne "zonemessages") && ($category eq $context)) {
283                         if (/([^\s]+)\s*\=\>?\s*(.*)/) {
284                                 @fields = split(/\,\s*/, $2);
285                                 if (($mbox eq $1) && ($context eq $category)) {
286                                         return ($fields[2] ? $fields[2] : "unknown", $category);
287                                 }
288                         }
289                 }
290         }
291         return ("", $category);
292 }
293
294 sub mailbox_options()
295 {
296         local($context, $current, $filename, $category) = @_;
297         local (*VMAIL);
298         local $tmp2;
299         local $tmp;
300         if (!$filename) {
301                 $filename = "/etc/asterisk/voicemail.conf";
302         }
303         if (!$category) {
304                 $category = "general";
305         }
306 #       print header;
307 #       print "Including <h2>$filename</h2> while in <h2>$category</h2>...\n";
308         open(VMAIL, "<$filename") || die("Bleh, no voicemail.conf");
309         while(<VMAIL>) {
310                 chomp;
311                 s/\;.*$//;
312                 if (/include\s\"([^\"]+)\"$/) {
313                         ($tmp2, $category) = &mailbox_options($context, $current, "/etc/asterisk/$1", $category);
314 #                       print "Got '$tmp2'...\n";
315                         $tmp .= $tmp2;
316                 } elsif (/\[(.*)\]/) {
317                         $category = $1;
318                 } elsif ($category eq "general") {
319                         if (/([^\s]+)\s*\=\s*(.*)/) {
320                                 if ($1 eq "dbname") {
321                                         $dbname = $2;
322                                 } elsif ($1 eq "dbpass") {
323                                         $dbpass = $2;
324                                 } elsif ($1 eq "dbhost") {
325                                         $dbhost = $2;
326                                 } elsif ($1 eq "dbuser") {
327                                         $dbuser = $2;
328                                 }
329                         }
330                         if ($dbname and $dbpass and $dbhost and $dbuser) {
331
332                                 # db variables are present.  Use db for authentication.
333                                 my $dbh = DBI->connect("DBI:mysql:$dbname:$dbhost",$dbuser,$dbpass);
334                                 my $sth = $dbh->prepare(qq{select mailbox,fullname,context from voicemail where context='$context' order by mailbox});
335                                 $sth->execute();
336                                 while (($mailbox, $fullname, $category) = $sth->fetchrow_array()) {
337                                         $text = $mailbox;
338                                         if ($fullname) {
339                                                 $text .= " (".$fullname.")";
340                                         }
341                                         if ($mailbox eq $current) {
342                                                 $tmp .= "<OPTION SELECTED>$text</OPTION>\n";
343                                         } else {
344                                                 $tmp .= "<OPTION>$text</OPTION>\n";
345                                         }
346                                 }
347                                 return ($tmp, $category);
348                         }
349                 } elsif (($category ne "general") && ($category ne "zonemessages")) {
350                         if (/([^\s]+)\s*\=\>?\s*(.*)/) {
351                                 @fields = split(/\,\s*/, $2);
352                                 $text = "$1";
353                                 if ($fields[1]) {
354                                         $text .= " ($fields[1])";
355                                 }
356                                 if ($1 eq $current) {
357                                         $tmp .= "<OPTION SELECTED>$text</OPTION>\n";
358                                 } else {
359                                         $tmp .= "<OPTION>$text</OPTION>\n";
360                                 }
361                                 
362                         }
363                 }
364         }
365         close(VMAIL);
366         return ($tmp, $category);
367 }
368
369 sub mailbox_list()
370 {
371         local ($name, $context, $current) = @_;
372         local $tmp;
373         local $text;
374         local $tmp;
375         local $opts;
376         if (!$context) {
377                 $context = "default";
378         }
379         $tmp = "<SELECT name=\"$name\">\n";
380         ($opts) = &mailbox_options($context, $current);
381         $tmp .= $opts;
382         $tmp .= "</SELECT>\n";
383         
384 }
385
386 sub msgcount() 
387 {
388         my ($context, $mailbox, $folder) = @_;
389         my $path = "/var/spool/asterisk/voicemail/$context/$mailbox/$folder";
390         if (opendir(DIR, $path)) {
391                 my @msgs = grep(/^msg....\.txt$/, readdir(DIR));
392                 closedir(DIR);
393                 return sprintf "%d", $#msgs + 1;
394         }
395         return "0";
396 }
397
398 sub msgcountstr()
399 {
400         my ($context, $mailbox, $folder) = @_;
401         my $count = &msgcount($context, $mailbox, $folder);
402         if ($count > 1) {
403                 "$count messages";
404         } elsif ($count > 0) {
405                 "$count message";
406         } else {
407                 "no messages";
408         }
409 }
410 sub messages()
411 {
412         my ($context, $mailbox, $folder) = @_;
413         my $path = "/var/spool/asterisk/voicemail/$context/$mailbox/$folder";
414         if (opendir(DIR, $path)) {
415                 my @msgs = sort grep(/^msg....\.txt$/, readdir(DIR));
416                 closedir(DIR);
417                 return map { s/^msg(....)\.txt$/$1/; $_ } @msgs;
418         }
419         return ();
420 }
421
422 sub getcookie()
423 {
424         my ($var) = @_;
425         return cookie($var);
426 }
427
428 sub makecookie()
429 {
430         my ($format) = @_;
431         cookie(-name => "format", -value =>["$format"], -expires=>"+1y");
432 }
433
434 sub getfields()
435 {
436         my ($context, $mailbox, $folder, $msg) = @_;
437         my $fields;
438         if (open(MSG, "</var/spool/asterisk/voicemail/$context/$mailbox/$folder/msg${msg}.txt")) {
439                 while(<MSG>) {
440                         s/\#.*$//g;
441                         if (/^(\w+)\s*\=\s*(.*)$/) {
442                                 $fields->{$1} = $2;
443                         }
444                 }
445                 close(MSG);
446                 $fields->{'msgid'} = $msg;
447         } else { print "<BR>Unable to open '$msg' in '$mailbox', '$folder'\n<B>"; }
448         $fields;
449 }
450
451 sub message_prefs()
452 {
453         my ($nextaction, $msgid) = @_;
454         my $folder = param('folder');
455         my $mbox = param('mailbox');
456         my $context = param('context');
457         my $passwd = param('password');
458         my $format = param('format');
459         if (!$format) {
460                 $format = &getcookie('format');
461         }
462         print header;
463         print <<_EOH;
464
465 <TITLE>Asterisk Web-Voicemail: Preferences</TITLE>
466 <BODY BGCOLOR="white">
467 $stdcontainerstart
468 <FORM METHOD="post">
469 <table width=100% align=center>
470 <tr><td align=right colspan=3><font size=+2>Web Voicemail Preferences</font></td></tr>
471 <tr><td align=left><font size=+1>Preferred&nbsp;Audio&nbsp;Format:</font></td><td colspan=2></td></tr>
472 _EOH
473
474 foreach $fmt (sort { $formats{$a}->{'pref'} <=> $formats{$b}->{'pref'} } keys %formats) {
475         my $clicked = "checked" if $fmt eq $format;
476         print "<tr><td></td><td align=left><input type=radio name=\"format\" $clicked value=\"$fmt\"></td><td width=100%>&nbsp;$formats{$fmt}->{name}</td></tr>\n";
477 }
478
479 print <<_EOH;
480 <tr><td align=right colspan=3><input type=submit value="save settings..."></td></tr>
481 </table>
482 <input type=hidden name="action" value="$nextaction">
483 <input type=hidden name="folder" value="$folder">
484 <input type=hidden name="mailbox" value="$mbox">
485 <input type=hidden name="context" value="$context">
486 <input type=hidden name="password" value="$passwd">
487 <input type=hidden name="msgid" value="$msgid">
488 $stdcontainerend
489 </BODY>\n
490 _EOH
491
492 }
493
494 sub message_play()
495 {
496         my ($message, $msgid) = @_;
497         my $folder = param('folder');
498         my ($mbox, $context) = split(/\@/, param('mailbox'));
499         my $passwd = param('password');
500         my $format = param('format');
501         
502         my $fields;
503         if (!$context) {
504                 $context = param('context');
505         }
506         if (!$context) {
507                 $context = "default";
508         }
509         
510         my $folders = &folder_list('newfolder', $context, $mbox, $folder);
511         my $mailboxes = &mailbox_list('forwardto', $context, $mbox);
512         if (!$format) {
513                 $format = &getcookie('format');
514         }
515         if (!$format) {
516                 &message_prefs("play", $msgid);
517         } else {
518                 print header(-cookie => &makecookie($format));
519                 $fields = &getfields($context, $mbox, $folder, $msgid);
520                 if (!$fields) {
521                         print "<BR>Bah!\n";
522                         return;
523                 }
524                 my $duration = $fields->{'duration'};
525                 if ($duration) {
526                         $duration = sprintf "%d:%02d", $duration/60, $duration % 60; 
527                 } else {
528                         $duration = "<i>Unknown</i>";
529                 }
530                 print <<_EOH;
531         
532 <TITLE>Asterisk Web-Voicemail: $folder Message $msgid</TITLE>
533 <BODY BGCOLOR="white">
534 $stdcontainerstart
535 <FORM METHOD="post">
536 <table width=100% align=center>
537 <tr><td align=right colspan=3><font size=+1>$folder Message $msgid</font></td></tr>
538 _EOH
539
540                 print <<_EOH;
541 <tr><td align=center colspan=3>
542 <table>
543         <tr><td colspan=2 align=center><font size=+1>$folder <b>$msgid</b></font></td></tr>
544         <tr><td><b>Message:</b></td><td>$msgid</td></tr>\n
545         <tr><td><b>Mailbox:</b></td><td>$mbox\@$context</td></tr>\n
546         <tr><td><b>Folder:</b></td><td>$folder</td></tr>\n
547         <tr><td><b>From:</b></td><td>$fields->{callerid}</td></tr>\n
548         <tr><td><b>Duration:</b></td><td>$duration</td></tr>\n
549         <tr><td><b>Original Date:</b></td><td>$fields->{origdate}</td></tr>\n
550         <tr><td><b>Original Mailbox:</b></td><td>$fields->{origmailbox}</td></tr>\n
551         <tr><td><b>Caller Channel:</b></td><td>$fields->{callerchan}</td></tr>\n
552         <tr><td align=center colspan=2>
553         <input name="action" type=submit value="index">&nbsp;
554         <input name="action" type=submit value="delete ">&nbsp;
555         <input name="action" type=submit value="forward to -> ">&nbsp;
556         $mailboxes&nbsp;
557         <input name="action" type=submit value="save to ->">
558         $folders&nbsp;
559         <input name="action" type=submit value="play ">
560         <input name="action" type=submit value="download">
561 </td></tr>
562 <tr><td colspan=2 align=center>
563 <embed width=400 height=40 src="vmail.cgi?action=audio&folder=$folder&mailbox=$mbox&context=$context&password=$passwd&msgid=$msgid&format=$format&dontcasheme=$$.$format" autostart=yes loop=false></embed>
564 </td></tr></table>
565 </td></tr>
566 </table>
567 <input type=hidden name="folder" value="$folder">
568 <input type=hidden name="mailbox" value="$mbox">
569 <input type=hidden name="context" value="$context">
570 <input type=hidden name="password" value="$passwd">
571 <input type=hidden name="msgid" value="$msgid">
572 $stdcontainerend
573 </BODY>\n
574 _EOH
575         }
576 }
577
578 sub message_audio()
579 {
580         my ($forcedownload) = @_;
581         my $folder = &untaint(param('folder'));
582         my $msgid = &untaint(param('msgid'));
583         my $mailbox = &untaint(param('mailbox'));
584         my $context = &untaint(param('context'));
585         my $format = param('format');
586         if (!$format) {
587                 $format = &getcookie('format');
588         }
589         &untaint($format);
590
591         my $path = "/var/spool/asterisk/voicemail/$context/$mailbox/$folder/msg${msgid}.$format";
592
593         $msgid =~ /^\d\d\d\d$/ || die("Msgid Liar ($msgid)!");
594         grep(/^${format}$/, keys %formats) || die("Format Liar ($format)!");
595
596         # Mailbox and folder are already verified
597         if (open(AUDIO, "<$path")) {
598                 $size = -s $path;
599                 $|=1;
600                 if ($forcedownload) {
601                         print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size, -attachment => "msg${msgid}.$format");
602                 } else {                
603                         print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size);
604                 }
605                 
606                 while(($amt = sysread(AUDIO, $data, 4096)) > 0) {
607                         syswrite(STDOUT, $data, $amt);
608                 }
609                 close(AUDIO);
610         } else {
611                 die("Hrm, can't seem to open $path\n");
612         }
613 }
614
615 sub message_index() 
616 {
617         my ($folder, $message) = @_;
618         my ($mbox, $context) = split(/\@/, param('mailbox'));
619         my $passwd = param('password');
620         my $message2;
621         my $msgcount;   
622         my $hasmsg;
623         my ($newmessages, $oldmessages);
624         my $format = param('format');
625         if (!$format) {
626                 $format = &getcookie('format');
627         }
628         if (!$context) {
629                 $context = param('context');
630         }
631         if (!$context) {
632                 $context = "default";
633         }
634         if ($folder) {
635                 $msgcount = &msgcountstr($context, $mbox, $folder);
636                 $message2 = "&nbsp;&nbsp;&nbsp;Folder '$folder' has " . &msgcountstr($context, $mbox, $folder);
637         } else {
638                 $newmessages = &msgcount($context, $mbox, "INBOX");
639                 $oldmessages = &msgcount($context, $mbox, "Old");
640                 if (($newmessages > 0) || ($oldmessages < 1)) {
641                         $folder = "INBOX";
642                 } else {
643                         $folder = "Old";
644                 }
645                 $message2 = "You have";
646                 if ($newmessages > 0) {
647                         $message2 .= " <b>$newmessages</b> NEW";
648                         if ($oldmessages > 0) {
649                                 $message2 .= "and <b>$oldmessages</b> OLD";
650                                 if ($oldmessages != 1) {
651                                         $message2 .= " messages.";
652                                 } else {
653                                         $message2 .= "message.";
654                                 }
655                         } else {
656                                 if ($newmessages != 1) {
657                                         $message2 .= " messages.";
658                                 } else {
659                                         $message2 .= " message.";
660                                 }
661                         }
662                 } else {
663                         if ($oldmessages > 0) {
664                                 $message2 .= " <b>$oldmessages</b> OLD";
665                                 if ($oldmessages != 1) {
666                                         $message2 .= " messages.";
667                                 } else {
668                                         $message2 .= " message.";
669                                 }
670                         } else {
671                                 $message2 .= " <b>no</b> messages.";
672                         }
673                 }
674         }
675         
676         my $folders = &folder_list('newfolder', $context, $mbox, $folder);
677         my $cfolders = &folder_list('changefolder', $context, $mbox, $folder);
678         my $mailboxes = &mailbox_list('forwardto', $context, $mbox);
679         print header(-cookie => &makecookie($format));
680         print <<_EOH;
681
682 <TITLE>Asterisk Web-Voicemail: $mbox\@$context $folder</TITLE>
683 <BODY BGCOLOR="white">
684 $stdcontainerstart
685 <FORM METHOD="post">
686 <table width=100% align=center>
687 <tr><td align=center colspan=2><font size=+2><I>$message</I></font></td></tr>
688 <tr><td align=right colspan=2><font size=+1><b>$folder</b> Messages</font> <input type=submit name="action" value="change to ->">$cfolders</td></tr>
689 <tr><td align=left colspan=2><font size=+1>$message2</font></td></tr>
690 </table>
691 <table width=100% align=center cellpadding=0 cellspacing=0>
692 _EOH
693
694 print "<tr><td>&nbsp;Msg</td><td>&nbsp;From</td><td>&nbsp;Duration</td><td>&nbsp;Date</td><td>&nbsp;</td></tr>\n";
695 print "<tr><td><hr></td><td><hr></td><td><hr></td><td><hr></td><td></td></tr>\n";
696 foreach $msg (&messages($context, $mbox, $folder)) {
697
698         $fields = &getfields($context, $mbox, $folder, $msg);
699         $duration = $fields->{'duration'};
700         if ($duration) {
701                 $duration = sprintf "%d:%02d", $duration / 60, $duration % 60;
702         } else {
703                 $duration = "<i>Unknown</i>";
704         }
705         $hasmsg++;
706         print "<tr><td><input type=checkbox name=\"msgselect\" value=\"$msg\">&nbsp;<b>$msg</b></td><td>$fields->{'callerid'}</td><td>$duration</td><td>$fields->{'origdate'}</td><td><input name='play$msg' alt=\"Play message $msg\" border=0 type=image align=left src=\"$astpath/play.gif\"></td></tr>\n";
707
708 }
709 if (!$hasmsg) {
710         print "<tr><td colspan=4 align=center><P><b><i>No messages</i></b><P></td></tr>";
711 }
712
713 print <<_EOH;
714 </table>
715 <table width=100% align=center>
716 <tr><td align=right colspan=2>
717         <input type="submit" name="action" value="refresh">&nbsp;
718 _EOH
719
720 if ($hasmsg) {
721 print <<_EOH;
722         <input type="submit" name="action" value="delete">&nbsp;
723         <input type="submit" name="action" value="save to ->">
724         $folders&nbsp;
725         <input type="submit" name="action" value="forward to ->">
726         $mailboxes
727 _EOH
728 }
729
730 print <<_EOH;
731 </td></tr>
732 <tr><td align=right colspan=2>
733         <input type="submit" name="action" value="preferences">
734         <input type="submit" name="action" value="logout">
735 </td></tr>
736 </table>
737 <input type=hidden name="folder" value="$folder">
738 <input type=hidden name="mailbox" value="$mbox">
739 <input type=hidden name="context" value="$context">
740 <input type=hidden name="password" value="$passwd">
741 </FORM>
742 $stdcontainerend
743 </BODY>\n
744 _EOH
745 }
746
747 sub validfolder()
748 {
749         my ($folder) = @_;
750         return grep(/^$folder$/, @validfolders);
751 }
752
753 sub folder_list()
754 {
755         my ($name, $context, $mbox, $selected) = @_;
756         my $f;
757         my $count;
758         my $tmp = "<SELECT name=\"$name\">\n";
759         foreach $f (@validfolders) {
760                 $count =  &msgcount($context, $mbox, $f);
761                 if ($f eq $selected) {
762                         $tmp .= "<OPTION SELECTED>$f ($count)</OPTION>\n";
763                 } else {
764                         $tmp .= "<OPTION>$f ($count)</OPTION>\n";
765                 }
766         }
767         $tmp .= "</SELECT>";
768 }
769
770 sub message_rename()
771 {
772         my ($context, $mbox, $oldfolder, $old, $newfolder, $new) = @_;
773         my ($oldfile, $newfile);
774         return if ($old eq $new) && ($oldfolder eq $newfolder);
775
776         if ($context =~ /^(\w+)$/) {
777                 $context = $1;
778         } else {
779                 die("Invalid Context<BR>\n");
780         }
781         
782         if ($mbox =~ /^(\w+)$/) {
783                 $mbox = $1;
784         } else {
785                 die ("Invalid mailbox<BR>\n");
786         }
787         
788         if ($oldfolder =~ /^(\w+)$/) {
789                 $oldfolder = $1;
790         } else {
791                 die("Invalid old folder<BR>\n");
792         }
793         
794         if ($newfolder =~ /^(\w+)$/) {
795                 $newfolder = $1;
796         } else {
797                 die("Invalid new folder ($newfolder)<BR>\n");
798         }
799         
800         if ($old =~ /^(\d\d\d\d)$/) {
801                 $old = $1;
802         } else {
803                 die("Invalid old Message<BR>\n");
804         }
805         
806         if ($new =~ /^(\d\d\d\d)$/) {
807                 $new = $1;
808         } else {
809                 die("Invalid old Message<BR>\n");
810         }
811         
812         my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$newfolder";
813         $path =~ /^(.*)$/;
814         $path = $1;
815         mkdir $path, 0770;
816         $path = "/var/spool/asterisk/voicemail/$context/$mbox/$oldfolder";
817         opendir(DIR, $path) || die("Unable to open directory\n");
818         my @files = grep /^msg${old}\.\w+$/, readdir(DIR);
819         closedir(DIR);
820         foreach $oldfile (@files) {
821                 my $tmp = $oldfile;
822                 if ($tmp =~ /^(msg${old}.\w+)$/) {
823                         $tmp = $1;
824                         $oldfile = $path . "/$tmp";
825                         $tmp =~ s/msg${old}/msg${new}/;
826                         $newfile = "/var/spool/asterisk/voicemail/$context/$mbox/$newfolder/$tmp";
827 #                       print "Renaming $oldfile to $newfile<BR>\n";
828                         rename($oldfile, $newfile);
829                 }
830         }
831 }
832
833 sub file_copy()
834 {
835         my ($orig, $new) = @_;
836         my $res;
837         my $data;
838         $orig =~ /^(.*)$/;
839         $orig = $1;
840         $new =~ /^(.*)$/;
841         $new = $1;
842         open(IN, "<$orig") || die("Unable to open '$orig'\n");
843         open(OUT, ">$new") || DIE("Unable to open '$new'\n");
844         while(($res = sysread(IN, $data, 4096)) > 0) {
845                 syswrite(OUT, $data, $res);
846         }
847         close(OUT);
848         close(IN);
849 }
850
851 sub message_copy()
852 {
853         my ($context, $mbox, $newmbox, $oldfolder, $old, $new) = @_;
854         my ($oldfile, $newfile);
855         return if ($mbox eq $newmbox);
856         
857         if ($mbox =~ /^(\w+)$/) {
858                 $mbox = $1;
859         } else {
860                 die ("Invalid mailbox<BR>\n");
861         }
862
863         if ($newmbox =~ /^(\w+)$/) {
864                 $newmbox = $1;
865         } else {
866                 die ("Invalid new mailbox<BR>\n");
867         }
868         
869         if ($oldfolder =~ /^(\w+)$/) {
870                 $oldfolder = $1;
871         } else {
872                 die("Invalid old folder<BR>\n");
873         }
874         
875         if ($old =~ /^(\d\d\d\d)$/) {
876                 $old = $1;
877         } else {
878                 die("Invalid old Message<BR>\n");
879         }
880         
881         if ($new =~ /^(\d\d\d\d)$/) {
882                 $new = $1;
883         } else {
884                 die("Invalid old Message<BR>\n");
885         }
886         
887         my $path = "/var/spool/asterisk/voicemail/$context/$newmbox";
888         $path =~ /^(.*)$/;
889         $path = $1;
890         mkdir $path, 0770;
891         $path = "/var/spool/asterisk/voicemail/$context/$newmbox/INBOX";
892         $path =~ /^(.*)$/;
893         $path = $1;
894         mkdir $path, 0770;
895         $path = "/var/spool/asterisk/voicemail/$context/$mbox/$oldfolder";
896         opendir(DIR, $path) || die("Unable to open directory\n");
897         my @files = grep /^msg${old}\.\w+$/, readdir(DIR);
898         closedir(DIR);
899         foreach $oldfile (@files) {
900                 my $tmp = $oldfile;
901                 if ($tmp =~ /^(msg${old}.\w+)$/) {
902                         $tmp = $1;
903                         $oldfile = $path . "/$tmp";
904                         $tmp =~ s/msg${old}/msg${new}/;
905                         $newfile = "/var/spool/asterisk/voicemail/$context/$newmbox/INBOX/$tmp";
906 #                       print "Copying $oldfile to $newfile<BR>\n";
907                         &file_copy($oldfile, $newfile);
908                 }
909         }
910 }
911
912 sub message_delete()
913 {
914         my ($context, $mbox, $folder, $msg) = @_;
915         if ($mbox =~ /^(\w+)$/) {
916                 $mbox = $1;
917         } else {
918                 die ("Invalid mailbox<BR>\n");
919         }
920         if ($context =~ /^(\w+)$/) {
921                 $context = $1;
922         } else {
923                 die ("Invalid context<BR>\n");
924         }
925         if ($folder =~ /^(\w+)$/) {
926                 $folder = $1;
927         } else {
928                 die("Invalid folder<BR>\n");
929         }
930         if ($msg =~ /^(\d\d\d\d)$/) {
931                 $msg = $1;
932         } else {
933                 die("Invalid Message<BR>\n");
934         }
935         my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$folder";
936         opendir(DIR, $path) || die("Unable to open directory\n");
937         my @files = grep /^msg${msg}\.\w+$/, readdir(DIR);
938         closedir(DIR);
939         foreach $oldfile (@files) {
940                 if ($oldfile =~ /^(msg${msg}.\w+)$/) {
941                         $oldfile = $path . "/$1";
942 #                       print "Deleting $oldfile<BR>\n";
943                         unlink($oldfile);
944                 }
945         }
946 }
947
948 sub message_forward()
949 {
950         my ($toindex, @msgs) = @_;
951         my $folder = param('folder');
952         my ($mbox, $context) = split(/\@/, param('mailbox'));
953         my $newmbox = param('forwardto');
954         my $msg;
955         my $msgcount;
956         if (!$context) {
957                 $context = param('context');
958         }
959         if (!$context) {
960                 $context = "default";
961         }
962         $newmbox =~ s/(\w+)(\s+.*)?$/$1/;
963         if (!&validmailbox($context, $newmbox)) {
964                 die("Bah! Not a valid mailbox '$newmbox'\n");
965                 return "";
966         }
967         
968         my $txt;
969         $context = &untaint($context);
970         $newmbox = &untaint($newmbox);
971         my $path = "/var/spool/asterisk/voicemail/$context/$newmbox/INBOX";
972         if ($msgs[0]) {
973                 if (&lock_path($path) == 0) {
974                         $msgcount = &msgcount($context, $newmbox, "INBOX");
975                         
976                         if ($newmbox ne $mbox) {
977         #                       print header;
978                                 foreach $msg (@msgs) {
979         #                               print "Forwarding $msg from $mbox to $newmbox<BR>\n";
980                                         &message_copy($context, $mbox, $newmbox, $folder, $msg, sprintf "%04d", $msgcount);
981                                         $msgcount++;
982                                 }
983                                 $txt = "Forwarded messages " . join(', ', @msgs) . "to $newmbox";
984                         } else {
985                                 $txt = "Can't forward messages to yourself!\n";
986                         }
987                         &unlock_path($path); 
988                 } else {
989                         $txt = "Cannot forward messages: Unable to lock path.\n";
990                 }
991         } else {
992                 $txt = "Please Select Message(s) for this action.\n";
993         }
994         if ($toindex) {
995                 &message_index($folder, $txt);
996         } else {
997                 &message_play($txt, $msgs[0]);
998         }
999 }
1000
1001 sub message_delete_or_move()
1002 {
1003         my ($toindex, $del, @msgs) = @_;
1004         my $txt;
1005         my $path;
1006         my ($y, $x);
1007         my $folder = param('folder');
1008         my $newfolder = param('newfolder') unless $del;
1009         $newfolder =~ s/^(\w+)\s+.*$/$1/;
1010         my ($mbox, $context) = split(/\@/, param('mailbox'));
1011         if (!$context) {
1012                 $context = param('context');
1013         }
1014         if (!$context) {
1015                 $context = "default";
1016         }
1017         my $passwd = param('password');
1018         $context = &untaint($context);
1019         $mbox = &untaint($mbox);
1020         $folder = &untaint($folder);
1021         $path = "/var/spool/asterisk/voicemail/$context/$mbox/$folder";
1022         if ($msgs[0]) {
1023                 if (&lock_path($path) == 0) {
1024                         my $msgcount = &msgcount($context, $mbox, $folder);
1025                         my $omsgcount = &msgcount($context, $mbox, $newfolder) if $newfolder;
1026                 #       print header;
1027                         if ($newfolder ne $folder) {
1028                                 $y = 0;
1029                                 for ($x=0;$x<$msgcount;$x++) {
1030                                         my $msg = sprintf "%04d", $x;
1031                                         my $newmsg = sprintf "%04d", $y;
1032                                         if (grep(/^$msg$/, @msgs)) {
1033                                                 if ($newfolder) {
1034                                                         &message_rename($context, $mbox, $folder, $msg, $newfolder, sprintf "%04d", $omsgcount);
1035                                                         $omsgcount++;
1036                                                 } else {
1037                                                         &message_delete($context, $mbox, $folder, $msg);
1038                                                 }
1039                                         } else {
1040                                                 &message_rename($context, $mbox, $folder, $msg, $folder, $newmsg);
1041                                                 $y++;
1042                                         }
1043                                 }
1044                                 if ($del) {
1045                                         $txt = "Deleted messages "  . join (', ', @msgs);
1046                                 } else {
1047                                         $txt = "Moved messages "  . join (', ', @msgs) . " to $newfolder";
1048                                 }
1049                         } else {
1050                                 $txt = "Can't move a message to the same folder they're in already";
1051                         }
1052                         &unlock_path($path);
1053                 } else {
1054                         $txt = "Cannot move/delete messages: Unable to lock path.\n";
1055                 }
1056         } else {
1057                 $txt = "Please Select Message(s) for this action.\n";
1058         }
1059         # Not as many messages now
1060         $msgcount--;
1061         if ($toindex || ($msgs[0] >= $msgcount)) {
1062                 &message_index($folder, $txt);  
1063         } else {
1064                 &message_play($txt, $msgs[0]);
1065         }
1066 }
1067
1068 if (param()) {
1069         my $folder = param('folder');
1070         my $changefolder = param('changefolder');
1071         $changefolder =~ s/(\w+)\s+.*$/$1/;
1072         
1073         my $newfolder = param('newfolder');
1074         $newfolder =~ s/^(\w+)\s+.*$/$1/;
1075         if ($newfolder && !&validfolder($newfolder)) {
1076                 print header;
1077                 die("Bah! new folder '$newfolder' isn't a folder.");
1078         }
1079         $action = param('action');
1080         $msgid = param('msgid');
1081         if (!$action) {
1082                 my ($tmp) = grep /^play\d\d\d\d\.x$/, param;
1083                 if ($tmp =~ /^play(\d\d\d\d)/) {
1084                         $msgid = $1;
1085                         $action = "play";
1086                 } else {
1087                         print header;
1088                         print "No message?<BR>\n";
1089                         return;
1090                 }
1091         }
1092         @msgs = param('msgselect');
1093         @msgs = ($msgid) unless @msgs;
1094         {
1095                 ($mailbox) = &check_login();
1096                 if (length($mailbox)) {
1097                         if ($action eq 'login') {
1098                                 &message_index($folder, "Welcome, $mailbox");
1099                         } elsif (($action eq 'refresh') || ($action eq 'index')) {
1100                                 &message_index($folder, "Welcome, $mailbox");
1101                         } elsif ($action eq 'change to ->') {
1102                                 if (&validfolder($changefolder)) {
1103                                         $folder = $changefolder;
1104                                         &message_index($folder, "Welcome, $mailbox");
1105                                 } else {
1106                                         die("Bah!  Not a valid change to folder '$changefolder'\n");
1107                                 }
1108                         } elsif ($action eq 'play') {
1109                                 &message_play("$mailbox $folder $msgid", $msgid);
1110                         } elsif ($action eq 'preferences') {
1111                                 &message_prefs("refresh", $msgid);
1112                         } elsif ($action eq 'download') {
1113                                 &message_audio(1);
1114                         } elsif ($action eq 'play ') {
1115                                 &message_audio(0);
1116                         } elsif ($action eq 'audio') {
1117                                 &message_audio(0);
1118                         } elsif ($action eq 'delete') {
1119                                 &message_delete_or_move(1, 1, @msgs);
1120                         } elsif ($action eq 'delete ') {
1121                                 &message_delete_or_move(0, 1, @msgs);
1122                         } elsif ($action eq 'forward to ->') {
1123                                 &message_forward(1, @msgs);
1124                         } elsif ($action eq 'forward to -> ') {
1125                                 &message_forward(0, @msgs);
1126                         } elsif ($action eq 'save to ->') {
1127                                 &message_delete_or_move(1, 0, @msgs);
1128                         } elsif ($action eq 'save to -> ') {
1129                                 &message_delete_or_move(0, 0, @msgs);
1130                         } elsif ($action eq 'logout') {
1131                                 &login_screen("Logged out!\n");
1132                         }
1133                 } else {
1134                         sleep(1);
1135                         &login_screen("Login Incorrect!\n");
1136                 }
1137         }
1138 } else {
1139         &login_screen("\&nbsp;");
1140 }