Mon, 28 Jan 2013 17:37:18 +0100
Correct socket error reporting improvement with IPv6 portable code,
after helpful recommendation by Saúl Ibarra Corretgé on OSips devlist.
1 #! @l_prefix@/bin/perl
2 #
3 # wakeup.agi 1.1
4 #
5 # A wakeup agi script for Asterisk
6 #
7 # Copyright (C) 2007
8 #
9 # Jonas Arndt <jonas_arndt@comcast.net>
10 #
11 # This program is free software, distributed under the terms of the
12 # GNU General Public License v2.
13 #
14 #
15 # This program is free software: you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License as published by
17 # the Free Software Foundation, either version 3 of the License, or
18 # (at your option) any later version.
19 #
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License
26 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #
29 use strict;
30 use Time::Local;
31 $|=1;
32 # Setup some variables
33 my %AGI; my $DEBUG=0;
34 # Some constants
35 my $OUTDIR="@l_prefix@/var/asterisk/spool/outgoing";
36 my $WAKEDIR="@l_prefix@/var/asterisk/spool/tmp";
37 my $debugfile="@l_prefix@/var/asterisk/spool/tmp/wakeup.log";
38 my $DEBUGOUT = "filehandle";
39 my $CALL = "filehandle";
40 my $TOUCH = "/usr/bin/touch";
42 ############ check_result ##########
43 # Use this to check the result of #
44 # a sent command #
45 # I pretty much stole this from #
46 # the regular agi-test.agi #
47 ####################################
48 sub checkresult {
49 my ($res) = @_;
50 my $retval;
51 chomp $res;
52 if ($res =~ /^200/) {
53 $res =~ /result=(-?\d+)/;
54 if (!length($1)) {
55 print DEBUGOUT "FAIL ($res)\n";
56 exit(1);
57 } elsif ($DEBUG=1) {
58 print DEBUGOUT "PASS ($1)\n";
59 }
60 } else {
61 print STDERR "FAIL (unexpected result '$res')\n";
62 exit(1);
63 }
64 }
66 ############ send_file ####
67 # Use this to send a wave #
68 # file on the channel #
69 ###########################
70 sub send_file {
71 my ($myfile) = @_;
72 chomp($myfile);
73 if ($DEBUG == 1 ) {
74 print DEBUGOUT "Sending stream $myfile \n";
75 }
76 print "STREAM FILE $myfile \"0123456789\"\n";
77 my $result = <STDIN>;
78 &checkresult($result);
79 $result =~ /result=(-?\d+)/;
80 return $1;
81 }
83 ############ hangup ###############
84 # Use this to hang up a channel #
85 ####################################
86 sub hangup {
87 if ($DEBUG == 1 ) {
88 print DEBUGOUT "Hanging up \n";
89 }
90 print "HANGUP \"\" \n";
91 my $result = <STDIN>;
92 &checkresult($result);
93 }
95 ############ say_number ############
96 # Use this to say a number #
97 # over the channel #
98 # #
99 ####################################
100 sub say_number {
101 my ($mynumber) = @_;
102 chomp($mynumber);
103 if ($DEBUG == 1 ) {
104 print DEBUGOUT "Saying number $mynumber \n";
105 }
106 print "SAY NUMBER $mynumber \"0123456789\"\n";
107 my $result = <STDIN>;
108 &checkresult($result);
109 $result =~ /result=(-?\d+)/;
110 return $1;
111 }
113 ############ say_digits ###################
114 # Use this to say digits over the channel #
115 ###########################################
116 sub say_digits {
117 my ($mynumber) = @_;
118 chomp($mynumber);
119 if ($DEBUG == 1 ) {
120 print DEBUGOUT "Saying digits $mynumber \n";
121 }
122 print "SAY DIGITS $mynumber \"0123456789\"\n";
123 my $result = <STDIN>;
124 &checkresult($result);
125 }
127 ######## get_choice ############
128 # Use this to receive a DTMF #
129 # choice from the channel #
130 ################################
131 sub get_choice {
132 if ($DEBUG == 1 ) {
133 print DEBUGOUT "Getting choice \n";
134 }
135 print "WAIT FOR DIGIT 15000\n";
136 my $result = <STDIN>;
137 &checkresult($result);
138 $result =~ /result=(-?\d+)/;
139 return $1;
140 }
142 ###### answer ######
143 # Anser the channel #
144 #####################
145 sub answer {
146 if ($DEBUG == 1 ) {
147 print DEBUGOUT "Answering the channel \n";
148 }
149 print "ANSWER\n";
150 my $result = <STDIN>;
151 &checkresult($result);
152 $result =~ /result=(-?\d+)/;
153 return $1;
154 }
156 ######## get_data ##################
157 # Feed with (file, maxnumbers) #
158 # where file is the sound file #
159 # to be played and maxnumbers is #
160 # the maximum amount of digits to #
161 # allow in the answer #
162 ####################################
163 sub get_data {
164 my @mydata = @_;
165 my $myfile = $mydata[0];
166 my $mymax = $mydata[1];
167 if ($DEBUG == 1 ) {
168 print DEBUGOUT "Getting data \n";
169 }
170 print "GET DATA $myfile 15000 $mymax \n";
171 my $result = <STDIN>;
172 &checkresult($result);
173 $result =~ /result=(-?\d+)/;
174 return $1;
175 }
177 ###### check_outstanding_calls #####
178 # Are there any outstanding wakeup #
179 # calls for this extensions? #
180 # Pass the extension to the #
181 # function. The function returns #
182 # a list of files #
183 ####################################
184 sub check_outstanding_calls {
185 my $myext = @_;
186 #opendir DIR, $WAKEDIR;
187 opendir DIR, $OUTDIR;
188 my @files = grep {/$myext/} readdir(DIR);
189 closedir DIR;
190 return @files;
191 }
193 ######## get_extension #############
194 # Receive the AIG variable and #
195 # return the extension #
196 ####################################
197 sub get_extension {
198 my (@aig) = @_;
199 if ($aig[11] == '') {
200 print STDERR "No extension found in function get_exension \n";
201 return "FAIL";
202 }
203 my $myext = $aig[11];
204 return $myext;
205 }
207 ######## get_context ###############
208 # Receive the AIG variable and #
209 # return the context #
210 ####################################
211 sub get_context {
212 my (@aig) = @_;
213 if ($aig[8] == '') {
214 print STDERR "No extension found in function get_exension \n";
215 return "FAIL";
216 }
217 my $mycont = $aig[8];
218 return $mycont;
219 }
221 ########### get_clid ###############
222 # Receive the AIG variable and #
223 # return the clid #
224 ####################################
225 sub get_clid {
226 my (@aig) = @_;
227 if ($aig[1] == '') {
228 print STDERR "No clid found in function get_clid \n";
229 return "FAIL";
230 }
231 my $myext = $aig[1];
232 return $myext;
233 }
235 ############### init_agi ##################
236 # Use this to initialize the AGI variable #
237 ###########################################
238 sub init_agi {
239 while(<STDIN>) {
240 chomp;
241 last unless length($_);
242 if (/^agi_(\w+)\:\s+(.*)$/) {
243 $AGI{$1} = $2;
244 }
245 }
246 }
248 ######### ascii2num ##########
249 # Removes 48 to get a number #
250 # out of the asciss return #
251 ##############################
252 sub ascii2num {
253 my ($asc) = @_;
254 my $ret;
255 $ret = $asc - 48;
256 return $ret;
257 }
259 ############# Welcome ##############
260 # This is the welcome menu #
261 ####################################
262 sub welcome {
263 my $ret = 0;
264 $ret = &send_file("welcome");
265 if ($ret == 0) {
266 $ret = &send_file("for-wakeup-call");
267 }
268 if ($ret == 0) {
269 $ret = &send_file("press-1");
270 }
271 if ($ret == 0) {
272 $ret = &send_file("for-a-list-of");
273 }
274 if ($ret == 0) {
275 $ret = &send_file("or");
276 }
277 if ($ret == 0) {
278 $ret = &send_file("to-cancel-wakeup");
279 }
280 if ($ret != 0) {
281 $ret = &ascii2num($ret);
282 }
283 if ($ret == 0) {
284 $ret = &get_data("press-2",1);
285 }
286 if ($ret == 1) {
287 $ret = &schedule_new();
288 } elsif ($ret == 2) {
289 &manage_calls();
290 } else {
291 $ret = &send_file("goodbye");
292 }
293 }
295 ######### manage_calls #############
296 # This is what is called if you #
297 # want to manage already scheduled #
298 # wakeup calls #
299 ####################################
300 sub manage_calls {
301 my $checker = "false";
302 my @calls;
303 my $del;
304 #my $ret;
305 my $hours;
306 my $minutes;
307 # Send out a welcome message and ask for return
308 @calls = &check_outstanding_calls($AGI{callerid});
309 if ($#calls + 1 == 0) {
310 $del = &send_file("not-rqsted-wakeup");
311 $del = &send_file("goodbye");
312 } else {
313 foreach (@calls) {
314 $del = 0;
315 my $wakefile = $_;
316 my @wakeup = split /\./, $_;
317 my $time = $wakeup[0];
318 $_ = $time;
319 /(^[0-9][0-9])/;
320 my $hours = $1;
321 /^[0-9][0-9]([0-9][0-9])/;
322 my $minutes = $1;
323 $del = &send_file("rqsted-wakeup-for");
324 if ($del == 0) {
325 $del = &say_number($hours);
326 }
327 if ($del == 0) {
328 if ($minutes >= 10 ) {
329 $del = &say_number($minutes);
330 } elsif ($minutes > 0 && $minutes < 10) {
331 $del = &send_file("digits/oh");
332 $del = &say_number($minutes);
333 }
334 }
335 if ($del == 0) {
336 $del = &send_file("digits/oclock");
337 }
338 if ($del == 0) {
339 $del = &send_file("to-cancel-wakeup");
340 }
341 if ($del == 0) {
342 $del = &send_file("press-1");
343 }
344 if ($del == 0) {
345 $del = &send_file("otherwise-press");
346 }
347 if ($del != 0) {
348 $del = &ascii2num($del);
349 }
350 if ($del == 0) {
351 $del = &get_data("digits/2",1);
352 }
353 if ($del == 1) {
354 my @sysargs = ("rm", "-f", "$WAKEDIR/$wakefile", "$OUTDIR/$wakefile");
355 system(@sysargs) == 0
356 or die "system @sysargs failed: $?";
357 $del = &send_file("cancelled");
358 }
359 }
360 $del = &send_file("goodbye");
361 }
362 }
364 ######## schedule_new ##########
365 # This is the menu to schedule #
366 # a new wakeup call #
367 ################################
368 sub schedule_new {
369 my $checker = "false";
370 my $ret_var;
371 my $ret_dummy = 0;
372 my $time;
373 my $perm;
374 my $file;
375 my $calltype;
376 my $extension;
377 my $context;
378 my $hours;
379 my $minutes;
380 if ($DEBUG == 1 ) {
381 print DEBUGOUT "From schedule_new\n";
382 }
383 while ( $checker eq "false" ) {
384 $ret_var = &send_file("to-rqst-wakeup-call");
385 if ($ret_var != 0) {
386 my $tmp = &get_data("silence/1",3);
387 $ret_var = &ascii2num($ret_var);
388 $ret_var = $ret_var . $tmp;
389 } else {
390 $ret_var = &get_data("enter-a-time",4);
391 }
392 # if ($ret_var < 1300 && $ret_var >= 0100) {
393 # my $pm = &get_data("1-for-am-2-for-pm",1);
394 # if ($pm == 2 && $ret_var <= 1159) {
395 # $ret_var = $ret_var + 1200;
396 # $checker = "true";
397 # } elsif ($pm == 1 && $ret_var > 1159) {
398 # $ret_var = $ret_var - 1200;
399 # # Fix the zero
400 # $ret_var = "00" . $ret_var;
401 # $checker = "true";
402 # } else {
403 # $checker = "true";
404 # }
405 # } elsif ($ret_var > 2359) {
406 # $ret_dummy = &send_file("please-try-again");
407 # } else {
408 # $checker = "true";
409 # }
410 if ($ret_var > 2359) {
411 $ret_dummy = &send_file("please-try-again");
412 } else {
413 $checker = "true";
414 }
415 }
416 $perm = 0;
417 $perm = &send_file("wakeup-for-one-time");
418 if ($perm == 0) {
419 $perm = &send_file("press-1");
420 }
421 if ($perm == 0) {
422 $perm = &send_file("for-a-daily-wakeup-call");
423 }
424 if ($perm != 0) {
425 $perm = &ascii2num($perm);
426 }
427 if ($perm == 0) {
428 $perm = $perm = &get_data("press-2",1);
429 }
430 # Open the file and populate it with data
431 $extension = $AGI{callerid};
432 $context = $AGI{context};
433 if ($perm == 2) {
434 $file = "$WAKEDIR/$ret_var.perm.1.$extension.call";
435 $calltype = "perm";
436 open (CALL, '>', $file) or die "Cannot open call file for write :$!";
437 } else {
438 $file = "$WAKEDIR/$ret_var.temp.1.$extension.call";
439 $calltype = "temp";
440 open (CALL, '>', $file) or die "Cannot open call file for write :$!";
441 }
442 my $myprint = "channel: Local" . "/" . $extension . "@" . $context . "\n";
443 print CALL $myprint;
444 print CALL "maxretries: 3\n";
445 print CALL "retrytime: 60\n";
446 print CALL "waittime: 60\n";
447 print CALL "callerid: \"AsterPBX Weckruf\" <$AGI{extension}>\n";
448 print CALL "application: AGI\n";
449 print CALL "data: wakeup|$ret_var.$calltype.1.$extension.call\n";
450 close ($CALL);
451 # Now touch the file
452 # Get the time variable
453 $time = get_time_string($ret_var);
454 my @command = ("$TOUCH", "-t", "$time", "${file}");
455 system(@command) == 0
456 or die "system @command failed: $?";
457 # Move it to the OUT directory
458 my @command = ("mv", "${file}", "${OUTDIR}/");
459 system(@command) == 0
460 or die "system @command failed: $?";
462 # Stream out the wakeup
463 $_ = $ret_var;
464 /(^[0-9][0-9])/;
465 my $hours = $1;
466 /^[0-9][0-9]([0-9][0-9])/;
467 my $minutes = $1;
468 $ret_dummy = &send_file("rqsted-wakeup-for");
469 $ret_dummy = &say_number($hours);
470 if ($minutes >= 10 ) {
471 &say_number($minutes);
472 } elsif ($minutes > 0 && $minutes < 10) {
473 &send_file("digits/oh");
474 &say_number($minutes);
475 }
476 $ret_dummy = &send_file("digits/oclock");
477 $ret_dummy = &send_file("goodbye");
478 return $ret_var;
479 }
481 ######## get_time_string ###########
482 # This will return the time string #
483 # when inputing a string like hhmi #
484 ####################################
485 sub get_time_string {
486 my ($intime) = @_;
487 my $minutes = substr($intime, 2, 4);
488 my $hours = substr($intime, 0, 2);
489 my $tmpepoch;
490 my $day;
491 my $month;
492 my $ret_val;
493 my $epoch = time();
494 my @timedata = localtime($epoch);
495 # Insert the minutes and hours from input
496 $timedata[1] = $minutes;
497 $timedata[2] = $hours;
498 # Get tmpepoch
499 $tmpepoch = timelocal(@timedata);
500 #Now compare them
501 if ($tmpepoch < $epoch) { # Means it is tomorrow
502 $tmpepoch += 86400; # Add 24 hours
503 }
504 # Now get the new timedata
505 my @timedata = localtime($tmpepoch);
506 $minutes = $timedata[1];
507 $hours = $timedata[2];
508 $day = $timedata[3];
509 $month = $timedata[4] + 1;
510 #Correct the "First hour after midnight" problem
511 if ($minutes < 10) {
512 $minutes = "0" . $minutes;
513 }
514 if ($hours < 10) {
515 $hours = "0" . $hours;
516 }
517 if ($day < 10) {
518 $day = "0" . $day;
519 }
520 if ($month < 10) {
521 $month = "0" . $month;
522 }
523 $ret_val = $month . $day . $hours . $minutes;
524 return $ret_val;
525 }
527 ############ new_time ################
528 # This will return the time string #
529 # with a time set 10 minute into #
530 # the future. The string is MMDDhhmi #
531 ######################################
532 sub new_time {
533 my ($input) = @_;
534 my @timedata;
535 my $minutes;
536 my $hours;
537 my $day;
538 my $month;
539 my $ret_val;
540 my $epoc = time();
541 if ($input eq "10m") {
542 # add 10 minutes
543 $epoc += 600;
544 #$epoc += 120; #just for debugs
545 } else {
546 # add 24 hours
547 $epoc += 86400;
548 }
549 @timedata = localtime($epoc);
550 $minutes = $timedata[1];
551 $hours = $timedata[2];
552 $day = $timedata[3];
553 $month = $timedata[4] + 1;
554 #Correct the "First hour after midnight" problem
555 if ($minutes < 10) {
556 $minutes = "0" . $minutes;
557 }
558 if ($hours < 10) {
559 $hours = "0" . $hours;
560 }
561 if ($day < 10) {
562 $day = "0" . $day;
563 }
564 if ($month < 10) {
565 $month = "0" . $month;
566 }
567 $ret_val = $month . $day . $hours . $minutes;
568 return $ret_val;
569 }
571 ########### snooze ##########
572 # This is the menu to snooze #
573 # the wakeup call #
574 ##############################
575 sub snooze {
576 my ($oldfile) = @_;
577 my $newfile;
578 my $extension;
579 my $context;
580 my @filestore = split (/\./, $oldfile);
581 my @permstore = split (/\./, $oldfile);
582 my $time;
583 my $ret_var = 0;
584 my $ret_dummy;
585 my $myprint;
586 # Answer the channel
587 &answer();
588 # Is this a reoccuring call, then add 24h
589 if ($permstore[1] eq "perm") {
590 $permstore[2] += 1; #Just to get a new file name
591 $newfile = join(".",@permstore);
592 $extension = $AGI{extension};
593 $context = $AGI{context};
594 # Open the file
595 open (CALL, '>', "${WAKEDIR}/${newfile}") or die "Cannot open call file for write :$!";
596 $myprint = "channel: Local" . "/" . $extension . "@" . $context . "\n";
597 print CALL $myprint;
598 print CALL "maxretries: 3\n";
599 print CALL "retrytime: 60\n";
600 print CALL "waittime: 60\n";
601 print CALL "callerid: \"AsterPBX Weckruf\" <$AGI{callerid}>\n";
602 print CALL "application: AGI\n";
603 print CALL "data: wakeup|$newfile\n";
604 close ($CALL);
605 # Get a time 24h from now
606 $time = &new_time("24h");
607 # Touch the file with the new time
608 my @command = ("$TOUCH", "-t", "$time", "${WAKEDIR}/${newfile}");
609 system(@command) == 0
610 or die "system @command failed: $?";
611 # Now move it
612 my @command = ("mv", "${WAKEDIR}/${newfile}", "${OUTDIR}/${newfile}");
613 system(@command) == 0
614 or die "system @command failed: $?";
615 }
616 #Replace the file name time with snooze
617 $filestore[1] = "snooze";
618 # Also add 10 minutes to the name
619 $time = new_time("10m");
620 $filestore[0] = substr($time, 4, 8);
621 # Get the new file name
622 $newfile = join(".",@filestore);
623 $ret_var = &send_file("this-is-yr-wakeup-call");
624 if ($ret_var == 0 ) {
625 $ret_var = &send_file("to-confirm-wakeup");
626 }
627 if ($ret_var == 0 ) {
628 $ret_var = &send_file("press-1");
629 }
630 if ($ret_var == 0 ) {
631 $ret_var = &send_file("to-snooze-for");
632 }
633 if ($ret_var == 0 ) {
634 $ret_var = &send_file("digits/10");
635 }
636 if ($ret_var == 0 ) {
637 $ret_var = &send_file("minutes");
638 }
639 if ($ret_var != 0 ) {
640 $ret_var = &ascii2num($ret_var);
641 }
642 if ($ret_var == 0 ) {
643 $ret_var = &get_data("press-2",1);
644 }
645 if ($ret_var == 2 ) {
646 # Populate some variables
647 $time = &new_time("10m");
648 $extension = $AGI{extension};
649 $context = $AGI{context};
650 # Open the file
651 open (CALL, '>', "${WAKEDIR}/${newfile}") or die "Cannot open call file for write :$!";
652 $myprint = "channel: Local" . "/" . $extension . "@" . $context . "\n";
653 print CALL $myprint;
654 print CALL "maxretries: 3\n";
655 print CALL "retrytime: 60\n";
656 print CALL "waittime: 60\n";
657 print CALL "callerid: \"AsterPBX Weckruf\" <$AGI{callerid}>\n";
658 print CALL "application: AGI\n";
659 print CALL "data: wakeup|$newfile\n";
660 close ($CALL);
661 # Touch the file with the new time
662 my @command = ("$TOUCH", "-t", "$time", "${WAKEDIR}/${newfile}");
663 system(@command) == 0
664 or die "system @command failed: $?";
665 # Now move it
666 my @command = ("mv", "${WAKEDIR}/${newfile}", "${OUTDIR}/${newfile}");
667 system(@command) == 0
668 or die "system @command failed: $?";
669 $ret_dummy = &send_file("goodbye");
671 } elsif ($ret_var == 1) {
672 $ret_dummy = &send_file("goodbye");
673 } else {
674 $ret_dummy = &send_file("goodbye");
675 }
677 # Stream out the wakeup
678 return 0;
679 }
681 ######## main program ########
682 # Here goes the main program #
683 ##############################
684 my $numargs = $#ARGV + 1;
685 if ($DEBUG == 1) {
686 open (DEBUGOUT, '>', $debugfile) or die "Cannot open $debugfile for write :$!";
687 }
689 # Start by reading in the stuff Asterisk is sending
690 &init_agi(); # Comment out in case of debug outside Asterisk
692 # If DEBUG is set, dump the AGI variable
693 if ($DEBUG == 1) {
694 foreach my $i (sort keys %AGI) {
695 print DEBUGOUT " -- $i = $AGI{$i}\n";
696 }
697 }
699 if ( $numargs == 0 ) {
700 &welcome();
701 &hangup();
702 exit(0);
703 } elsif ( $ARGV[0] eq "move" ) {
704 &move();
705 &hangup();
706 exit(0);
707 } else {
708 &snooze($ARGV[0]);
709 &hangup();
710 exit(0);
711 }
713 if ($DEBUG ==1) {
714 close $DEBUGOUT;
715 }