3 # Web based Voicemail for Asterisk
5 # Copyright (C) 2002, Linux Support Services, Inc.
7 # Distributed under the terms of the GNU General Public License
9 # Written by Mark Spencer <markster@linux-support.net>
11 # (icky, I know.... if you know better perl please help!)
14 use CGI qw/:standard/;
15 use CGI::Carp qw(fatalsToBrowser);
17 @validfolders = ( "INBOX", "Old", "Work", "Family", "Friends", "Cust1", "Cust2", "Cust3", "Cust4", "Cust5" );
21 name => "Uncompressed WAV",
22 mime => "audio/x-wav",
26 name => "GSM Compressed WAV",
27 mime => "audio/x-wav",
31 name => "Raw GSM Audio",
32 mime => "audio/x-gsm",
37 $astpath = "/_asterisk";
39 $stdcontainerstart = "<table align=center width=600><tr><td>\n";
40 $footer = "<hr><font size=-1><a href=\"http://www.asterisk.org\">The Asterisk Open Source PBX</a> Copyright 2002, <a href=\"http://www.digium.com\">Digium, Inc.</a></a>";
41 $stdcontainerend = "</td></tr><tr><td align=right>$footer</td></tr></table>\n";
48 <TITLE>Asterisk Web-Voicemail</TITLE>
49 <BODY BGCOLOR="white">
52 <input type=hidden name="action" value="login">
54 <tr><td valign=top align=center rowspan=6><img align=center src="$astpath/animlogo.gif"></td></tr>
55 <tr><td align=center colspan=2><font size=+2>Commedian Mail Login</font></td></tr>
56 <tr><td align=center colspan=2><font size=+1>$message</font></td></tr>
57 <tr><td>Mailbox:</td><td><input type=text name="mailbox"></td></tr>
58 <tr><td>Password:</td><td><input type=password name="password"></td></tr>
59 <tr><td align=right colspan=2><input value="Login" type=submit></td></tr>
70 my ($mbox, $context) = split(/\@/, param('mailbox'));
71 my $pass = param('password');
72 my $category = "general";
75 $context = param('context');
80 open(VMAIL, "</etc/asterisk/voicemail.conf") || die("Bleh, no voicemail.conf");
85 } elsif ($category ne "general") {
86 if (/([^\s]+)\s*\=\>?\s*(.*)/) {
87 @fields = split(/\,\s*/, $2);
88 if (($mbox eq $1) && ($pass eq $fields[0]) && ($context eq $category)) {
89 return $fields[1] ? $fields[1] : "Extension $mbox in $context";
98 my ($context, $mbox) = @_;
99 my $category = "general";
101 open(VMAIL, "</etc/asterisk/voicemail.conf") || die("Bleh, no voicemail.conf");
106 } elsif (($category ne "general") && ($category eq $context)) {
107 if (/([^\s]+)\s*\=\>?\s*(.*)/) {
108 @fields = split(/\,\s*/, $2);
110 return $fields[2] ? $fields[2] : "unknown";
119 my ($name, $context, $current) = @_;
122 $tmp = "<SELECT name=\"$name\">\n";
123 open(VMAIL, "</etc/asterisk/voicemail.conf") || die("Bleh, no voicemail.conf");
129 } elsif ($category ne "general") {
130 if (/([^\s]+)\s*\=\>?\s*(.*)/) {
131 @fields = split(/\,\s*/, $2);
134 $text .= " ($fields[1])";
136 if ($1 eq $current) {
137 $tmp .= "<OPTION SELECTED>$text</OPTION>\n";
139 $tmp .= "<OPTION>$text</OPTION>\n";
142 if (($mbox eq $1) && ($pass eq $fields[0])) {
148 $tmp .= "</SELECT>\n";
154 my ($context, $mailbox, $folder) = @_;
155 my $path = "/var/spool/asterisk/voicemail/$context/$mailbox/$folder";
156 if (opendir(DIR, $path)) {
157 my @msgs = grep(/^msg....\.txt$/, readdir(DIR));
159 return sprintf "%d", $#msgs + 1;
166 my ($context, $mailbox, $folder) = @_;
167 my $count = &msgcount($context, $mailbox, $folder);
170 } elsif ($count > 0) {
178 my ($context, $mailbox, $folder) = @_;
179 my $path = "/var/spool/asterisk/voicemail/$context/$mailbox/$folder";
180 if (opendir(DIR, $path)) {
181 my @msgs = sort grep(/^msg....\.txt$/, readdir(DIR));
183 return map { s/^msg(....)\.txt$/$1/; $_ } @msgs;
197 cookie(-name => "format", -value =>["$format"], -expires=>"+1y");
202 my ($context, $mailbox, $folder, $msg) = @_;
204 if (open(MSG, "</var/spool/asterisk/voicemail/$context/$mailbox/$folder/msg${msg}.txt")) {
207 if (/^(\w+)\s*\=\s*(.*)$/) {
212 $fields->{'msgid'} = $msg;
213 } else { print "<BR>Unable to open '$msg' in '$mailbox', '$folder'\n<B>"; }
219 my ($nextaction, $msgid) = @_;
220 my $folder = param('folder');
221 my $mbox = param('mailbox');
222 my $passwd = param('password');
223 my $format = param('format');
225 $format = &getcookie('format');
230 <TITLE>Asterisk Web-Voicemail: Preferences</TITLE>
231 <BODY BGCOLOR="white">
234 <table width=100% align=center>
235 <tr><td align=right colspan=3><font size=+2>Web Voicemail Preferences</font></td></tr>
236 <tr><td align=left><font size=+1>Preferred Audio Format:</font></td><td colspan=2></td></tr>
239 foreach $fmt (sort { $formats{$a}->{'pref'} <=> $formats{$b}->{'pref'} } keys %formats) {
240 my $clicked = "checked" if $fmt eq $format;
241 print "<tr><td></td><td align=left><input type=radio name=\"format\" $clicked value=\"$fmt\"></td><td width=100%> $formats{$fmt}->{name}</td></tr>\n";
245 <tr><td align=right colspan=3><input type=submit value="save settings..."></td></tr>
247 <input type=hidden name="action" value="$nextaction">
248 <input type=hidden name="folder" value="$folder">
249 <input type=hidden name="mailbox" value="$mbox">
250 <input type=hidden name="context" value="$context">
251 <input type=hidden name="password" value="$passwd">
252 <input type=hidden name="msgid" value="$msgid">
261 my ($message, $msgid) = @_;
262 my $folder = param('folder');
263 my ($mbox, $context) = split(/\@/, param('mailbox'));
264 my $passwd = param('password');
265 my $format = param('format');
269 $context = param('context');
272 $context = "default";
275 my $folders = &folder_list('newfolder', $context, $mbox, $folder);
276 my $mailboxes = &mailbox_list('forwardto', $context, $mbox);
278 $format = &getcookie('format');
281 &message_prefs("play", $msgid);
283 print header(-cookie => &makecookie($format));
284 $fields = &getfields($context, $mbox, $folder, $msgid);
289 my $duration = $fields->{'duration'};
291 $duration = sprintf "%d:%02d", $duration/60, $duration % 60;
293 $duration = "<i>Unknown</i>";
297 <TITLE>Asterisk Web-Voicemail: $folder Message $msgid</TITLE>
298 <BODY BGCOLOR="white">
301 <table width=100% align=center>
302 <tr><td align=right colspan=3><font size=+1>$folder Message $msgid</font></td></tr>
306 <tr><td align=center colspan=3>
308 <tr><td colspan=2 align=center><font size=+1>$folder <b>$msgid</b></font></td></tr>
309 <tr><td><b>Message:</b></td><td>$msgid</td></tr>\n
310 <tr><td><b>Mailbox:</b></td><td>$mbox</td></tr>\n
311 <tr><td><b>Folder:</b></td><td>$folder</td></tr>\n
312 <tr><td><b>From:</b></td><td>$fields->{callerid}</td></tr>\n
313 <tr><td><b>Duration:</b></td><td>$duration</td></tr>\n
314 <tr><td><b>Original Date:</b></td><td>$fields->{origdate}</td></tr>\n
315 <tr><td><b>Original Mailbox:</b></td><td>$fields->{origmailbox}</td></tr>\n
316 <tr><td><b>Caller Channel:</b></td><td>$fields->{callerchan}</td></tr>\n
317 <tr><td align=center colspan=2>
318 <input name="action" type=submit value="index">
319 <input name="action" type=submit value="delete ">
320 <input name="action" type=submit value="forward to -> ">
322 <input name="action" type=submit value="save to ->">
324 <input name="action" type=submit value="play ">
325 <input name="action" type=submit value="download">
327 <tr><td colspan=2 align=center>
328 <embed width=400 height=40 src="vmail.cgi?action=audio&folder=$folder&mailbox=$mbox&password=$passwd&msgid=$msgid&format=$format&dontcasheme=$$.$format" autostart=yes loop=false></embed>
332 <input type=hidden name="folder" value="$folder">
333 <input type=hidden name="mailbox" value="$mbox">
334 <input type=hidden name="context" value="$context">
335 <input type=hidden name="password" value="$passwd">
336 <input type=hidden name="msgid" value="$msgid">
345 my ($forcedownload) = @_;
346 my $folder = param('folder');
347 my $msgid = param('msgid');
348 my $mailbox = param('mailbox');
349 my $format = param('format');
351 $format = &getcookie('format');
353 my $path = "/var/spool/asterisk/vm/$mailbox/$folder/msg${msgid}.$format";
355 $msgid =~ /^\d\d\d\d$/ || die("Msgid Liar ($msgid)!");
356 grep(/^${format}$/, keys %formats) || die("Format Liar ($format)!");
358 # Mailbox and folder are already verified
359 if (open(AUDIO, "<$path")) {
362 if ($forcedownload) {
363 print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size, -attachment => "msg${msgid}.$format");
365 print header(-type=>$formats{$format}->{'mime'}, -Content_length => $size);
368 while(($amt = sysread(AUDIO, $data, 4096)) > 0) {
369 syswrite(STDOUT, $data, $amt);
373 die("Hrm, can't seem to open $path\n");
379 my ($folder, $message) = @_;
380 my ($mbox, $context) = split(/\@/, param('mailbox'));
381 my $passwd = param('password');
385 my $newmessages, $oldmessages;
386 my $format = param('format');
388 $format = &getcookie('format');
391 $context = param('context');
394 $context = "default";
397 $msgcount = &msgcountstr($context, $mbox, $folder);
398 $message2 = " Folder '$folder' has " . &msgcountstr($context, $mbox, $folder);
400 $newmessages = &msgcount($context, $mbox, "INBOX");
401 $oldmessages = &msgcount($context, $mbox, "Old");
402 if (($newmessages > 0) || ($oldmessages < 1)) {
407 $message2 = "You have";
408 if ($newmessages > 0) {
409 $message2 .= " <b>$newmessages</b> NEW";
410 if ($oldmessages > 0) {
411 $message2 .= "and <b>$oldmessages</b> OLD";
412 if ($oldmessages != 1) {
413 $message2 .= " messages.";
415 $message2 .= "message.";
418 if ($newmessages != 1) {
419 $message2 .= " messages.";
421 $message2 .= " message.";
425 if ($oldmessages > 0) {
426 $message2 .= " <b>$oldmessages</b> OLD";
427 if ($oldmessages != 1) {
428 $message2 .= " messages.";
430 $message2 .= " message.";
433 $message2 .= " <b>no</b> messages.";
438 my $folders = &folder_list('newfolder', $context, $mbox, $folder);
439 my $cfolders = &folder_list('changefolder', $context, $mbox, $folder);
440 my $mailboxes = &mailbox_list('forwardto', $context, $mbox);
441 print header(-cookie => &makecookie($format));
444 <TITLE>Asterisk Web-Voicemail: $mbox $folder</TITLE>
445 <BODY BGCOLOR="white">
448 <table width=100% align=center>
449 <tr><td align=center colspan=2><font size=+2><I>$message</I></font></td></tr>
450 <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>
451 <tr><td align=left colspan=2><font size=+1>$message2</font></td></tr>
453 <table width=100% align=center cellpadding=0 cellspacing=0>
456 print "<tr><td> Msg</td><td> From</td><td> Duration</td><td> Date</td><td> </td></tr>\n";
457 print "<tr><td><hr></td><td><hr></td><td><hr></td><td><hr></td><td></td></tr>\n";
458 foreach $msg (&messages($context, $mbox, $folder)) {
460 $fields = &getfields($context, $mbox, $folder, $msg);
461 $duration = $fields->{'duration'};
463 $duration = sprintf "%d:%02d", $duration / 60, $duration % 60;
465 $duration = "<i>Unknown</i>";
468 print "<tr><td><input type=checkbox name=\"msgselect\" value=\"$msg\"> <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";
472 print "<tr><td colspan=4 align=center><P><b><i>No messages</i></b><P></td></tr>";
477 <table width=100% align=center>
478 <tr><td align=right colspan=2>
479 <input type="submit" name="action" value="refresh">
484 <input type="submit" name="action" value="delete">
485 <input type="submit" name="action" value="save to ->">
487 <input type="submit" name="action" value="forward to ->">
494 <tr><td align=right colspan=2>
495 <input type="submit" name="action" value="preferences">
496 <input type="submit" name="action" value="logout">
499 <input type=hidden name="folder" value="$folder">
500 <input type=hidden name="mailbox" value="$mbox">
501 <input type=hidden name="context" value="$context">
502 <input type=hidden name="password" value="$passwd">
512 return grep(/^$folder$/, @validfolders);
517 my ($name, $context, $mbox, $selected) = @_;
520 my $tmp = "<SELECT name=\"$name\">\n";
521 foreach $f (@validfolders) {
522 $count = &msgcount($context, $mbox, $f);
523 if ($f eq $selected) {
524 $tmp .= "<OPTION SELECTED>$f ($count)</OPTION>\n";
526 $tmp .= "<OPTION>$f ($count)</OPTION>\n";
534 my ($context, $mbox, $oldfolder, $old, $newfolder, $new) = @_;
535 my $oldfile, $newfile;
536 return if ($old eq $new) && ($oldfolder eq $newfolder);
538 if ($mbox =~ /^(\w+)$/) {
541 die ("Invalid mailbox<BR>\n");
544 if ($oldfolder =~ /^(\w+)$/) {
547 die("Invalid old folder<BR>\n");
550 if ($newfolder =~ /^(\w+)$/) {
553 die("Invalid new folder ($newfolder)<BR>\n");
556 if ($old =~ /^(\d\d\d\d)$/) {
559 die("Invalid old Message<BR>\n");
562 if ($new =~ /^(\d\d\d\d)$/) {
565 die("Invalid old Message<BR>\n");
568 my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$newfolder";
570 my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$oldfolder";
571 opendir(DIR, $path) || die("Unable to open directory\n");
572 my @files = grep /^msg${old}\.\w+$/, readdir(DIR);
574 foreach $oldfile (@files) {
576 if ($tmp =~ /^(msg${old}.\w+)$/) {
578 $oldfile = $path . "/$tmp";
579 $tmp =~ s/msg${old}/msg${new}/;
580 $newfile = "/var/spool/asterisk/voicemail/$context/$mbox/$newfolder/$tmp";
581 # print "Renaming $oldfile to $newfile<BR>\n";
582 rename($oldfile, $newfile);
589 my ($orig, $new) = @_;
592 open(IN, "<$orig") || die("Unable to open '$orig'\n");
593 open(OUT, ">$new") || DIE("Unable to open '$new'\n");
594 while(($res = sysread(IN, $data, 4096)) > 0) {
595 syswrite(OUT, $data, $res);
603 my ($mbox, $oldfolder, $old, $newmbox, $new) = @_;
604 my $oldfile, $newfile;
605 return if ($mbox eq $newmbox);
607 if ($mbox =~ /^(\w+)$/) {
610 die ("Invalid mailbox<BR>\n");
613 if ($newmbox =~ /^(\w+)$/) {
616 die ("Invalid new mailbox<BR>\n");
619 if ($oldfolder =~ /^(\w+)$/) {
622 die("Invalid old folder<BR>\n");
625 if ($old =~ /^(\d\d\d\d)$/) {
628 die("Invalid old Message<BR>\n");
631 if ($new =~ /^(\d\d\d\d)$/) {
634 die("Invalid old Message<BR>\n");
637 my $path = "/var/spool/asterisk/vm/$newmbox";
639 my $path = "/var/spool/asterisk/vm/$newmbox/INBOX";
641 my $path = "/var/spool/asterisk/vm/$mbox/$oldfolder";
642 opendir(DIR, $path) || die("Unable to open directory\n");
643 my @files = grep /^msg${old}\.\w+$/, readdir(DIR);
645 foreach $oldfile (@files) {
647 if ($tmp =~ /^(msg${old}.\w+)$/) {
649 $oldfile = $path . "/$tmp";
650 $tmp =~ s/msg${old}/msg${new}/;
651 $newfile = "/var/spool/asterisk/vm/$newmbox/INBOX/$tmp";
652 # print "Copying $oldfile to $newfile<BR>\n";
653 &file_copy($oldfile, $newfile);
660 my ($context, $mbox, $folder, $msg) = @_;
661 if ($mbox =~ /^(\w+)$/) {
664 die ("Invalid mailbox<BR>\n");
666 if ($context =~ /^(\w+)$/) {
669 die ("Invalid context<BR>\n");
671 if ($folder =~ /^(\w+)$/) {
674 die("Invalid folder<BR>\n");
676 if ($msg =~ /^(\d\d\d\d)$/) {
679 die("Invalid Message<BR>\n");
681 my $path = "/var/spool/asterisk/voicemail/$context/$mbox/$folder";
682 opendir(DIR, $path) || die("Unable to open directory\n");
683 my @files = grep /^msg${msg}\.\w+$/, readdir(DIR);
685 foreach $oldfile (@files) {
686 if ($oldfile =~ /^(msg${msg}.\w+)$/) {
687 $oldfile = $path . "/$1";
688 # print "Deleting $oldfile<BR>\n";
694 sub message_forward()
696 my ($toindex, @msgs) = @_;
697 my $folder = param('folder');
698 my ($mbox, $context) = split(/\@/, param('mailbox'));
699 my $newmbox = param('forwardto');
703 $context = param('context');
706 $context = "default";
708 $newmbox =~ s/(\w+)(\s+.*)?$/$1/;
709 if (!&validmailbox($context, $newmbox)) {
710 die("Bah! Not a valid mailbox '$newmbox'\n");
713 $msgcount = &msgcount($context, $newmbox, "INBOX");
715 if ($newmbox ne $mbox) {
717 foreach $msg (@msgs) {
718 # print "Forwarding $msg from $mbox to $newmbox<BR>\n";
719 &message_copy($context, $mbox, $folder, $msg, $newmbox, sprintf "%04d", $msgcount);
722 $txt = "Forwarded messages " . join(', ', @msgs) . "to $newmbox";
724 $txt = "Can't forward messages to yourself!\n";
727 &message_index($folder, $txt);
729 &message_play($txt, $msgs[0]);
733 sub message_delete_or_move()
735 my ($toindex, $del, @msgs) = @_;
739 my $folder = param('folder');
740 my $newfolder = param('newfolder') unless $del;
741 $newfolder =~ s/^(\w+)\s+.*$/$1/;
742 my ($mbox, $context) = split(/\@/, param('mailbox'));
744 $context = param('context');
747 $context = "default";
749 my $passwd = param('password');
750 my $msgcount = &msgcount($context, $mbox, $folder);
751 my $omsgcount = &msgcount($context, $mbox, $newfolder) if $newfolder;
753 if ($newfolder ne $folder) {
755 for ($x=0;$x<$msgcount;$x++) {
756 my $msg = sprintf "%04d", $x;
757 my $newmsg = sprintf "%04d", $y;
758 if (grep(/^$msg$/, @msgs)) {
760 &message_rename($context, $mbox, $folder, $msg, $newfolder, sprintf "%04d", $omsgcount);
763 &message_delete($context, $mbox, $folder, $msg);
766 &message_rename($context, $mbox, $folder, $msg, $folder, $newmsg);
771 $txt = "Deleted messages " . join (', ', @msgs);
773 $txt = "Moved messages " . join (', ', @msgs) . " to $newfolder";
776 $txt = "Can't move a message to the same folder they're in already";
778 # Not as many messages now
780 if ($toindex || ($msgs[0] >= $msgcount)) {
781 &message_index($folder, $txt);
783 &message_play($txt, $msgs[0]);
788 my $folder = param('folder');
789 my $changefolder = param('changefolder');
790 $changefolder =~ s/(\w+)\s+.*$/$1/;
792 my $newfolder = param('newfolder');
793 $newfolder =~ s/^(\w+)\s+.*$/$1/;
794 if ($newfolder && !&validfolder($newfolder)) {
796 die("Bah! new folder '$newfolder' isn't a folder.");
798 $action = param('action');
799 $msgid = param('msgid');
801 my ($tmp) = grep /^play\d\d\d\d\.x$/, param;
802 if ($tmp =~ /^play(\d\d\d\d)/) {
807 print "No message?<BR>\n";
811 @msgs = param('msgselect');
812 @msgs = ($msgid) unless @msgs;
814 $mailbox = check_login();
816 if ($action eq 'login') {
817 &message_index($folder, "Welcome, $mailbox");
818 } elsif (($action eq 'refresh') || ($action eq 'index')) {
819 &message_index($folder, "Welcome, $mailbox");
820 } elsif ($action eq 'change to ->') {
821 if (&validfolder($changefolder)) {
822 $folder = $changefolder;
823 &message_index($folder, "Welcome, $mailbox");
825 die("Bah! Not a valid change to folder '$changefolder'\n");
827 } elsif ($action eq 'play') {
828 &message_play("$mailbox $folder $msgid", $msgid);
829 } elsif ($action eq 'preferences') {
830 &message_prefs("refresh", $msgid);
831 } elsif ($action eq 'download') {
833 } elsif ($action eq 'play ') {
835 } elsif ($action eq 'audio') {
837 } elsif ($action eq 'delete') {
838 &message_delete_or_move(1, 1, @msgs);
839 } elsif ($action eq 'delete ') {
840 &message_delete_or_move(0, 1, @msgs);
841 } elsif ($action eq 'forward to ->') {
842 &message_forward(1, @msgs);
843 } elsif ($action eq 'forward to -> ') {
844 &message_forward(0, @msgs);
845 } elsif ($action eq 'save to ->') {
846 &message_delete_or_move(1, 0, @msgs);
847 } elsif ($action eq 'save to -> ') {
848 &message_delete_or_move(0, 0, @msgs);
849 } elsif ($action eq 'logout') {
850 &login_screen("Logged out!\n");
854 &login_screen("Login Incorrect!\n");
858 &login_screen("\ ");