Ticket #2565: callmonitor.module

File callmonitor.module, 19.9 kB (added by sasargen, 8 months ago)

Patched to encrypt file paths in html output and function arguments.

Line 
1 <?php
2
3 /**
4  * @file
5  * Functions for the interface to the call monitor recordings
6  */
7
8 /**
9   * Class for Callmonitor
10   */
11 class Callmonitor {
12
13   /*
14    * rank (for prioritizing modules)
15    */
16   function rank() {
17
18     $rank = 2;
19     return $rank;
20   }
21
22   /*
23    * init
24    */
25   function init() {
26   }
27
28   /*
29    * Adds menu item to nav menu
30    *
31    * @param $args
32    *   Common arguments
33    */
34   function navMenu($args) {
35
36     $ret .= "<p><small><small><a href='" . $_SESSION['ARI_ROOT'] . "?m=Callmonitor&f=display'>" . _("Call Monitor") . "</a></small></small></p><br>";
37
38     return $ret;
39   }
40
41   /*
42    * Acts on the selected call monitor recordings in the method indicated by the action and updates page
43    *
44    * @param $args
45    *   Common arguments
46    */
47   function recAction($args) {
48
49     // args
50     $m = getArgument($args,'m');
51     $a = getArgument($args,'a');
52     $q = getArgument($args,'q');
53     $start = getArgument($args,'start');
54     $span = getArgument($args,'span');
55     $order = getArgument($args,'order');
56     $sort = getArgument($args,'sort');
57     $duration_filter = getArgument($args,'duration_filter');
58
59     // get files
60     $files = array();
61     foreach($_REQUEST as $key => $value) {
62       if (preg_match('/selected/',$key)) {
63         array_push($files, $value);
64       }
65     }
66
67     if ($a=='delete') {
68       $this->deleteRecData($files);
69     }
70
71     if ($a=='ignore') {
72
73       $start = 0;
74
75       setcookie("ari_duration_filter", $duration_filter, time()+365*24*60*60);
76     }
77
78     // redirect to see updated page
79     $ret .= "
80       <head>
81         <script>
82         <!--
83           window.location = \"" . $_SESSION['ARI_ROOT'] . "?m=" . $m . "&q=" . $q . "&start=" . $start . "&span=" . $span . "&order=" . $order . "&sort=" . $sort . "&duration_filter=" . $duration_filter . "\"
84         // -->
85         </script>
86       </head>";
87
88     return $ret;
89   }
90
91   /*
92    * Displays stats page
93    *
94    * @param $args
95    *   Common arguments
96    */
97   function display($args) {
98
99     global $ASTERISK_CALLMONITOR_PATH;
100     global $CALLMONITOR_ALLOW_DELETE;
101     global $AJAX_PAGE_REFRESH_ENABLE;
102     global $ARI_CRYPT_PASSWORD;
103
104     $display = new DisplaySearch();
105     $crypt = new Crypt();
106
107     // get the search string
108     $m = getArgument($args,'m');
109     $f = getArgument($args,'f');
110     $q = getArgument($args,'q');
111     $start = getArgument($args,'start');
112     $span = getArgument($args,'span');
113     $order = getArgument($args,'order');
114     $sort = getArgument($args,'sort');
115     $duration_filter = getArgument($args,'duration_filter');
116
117     $start = $start=='' ? 0 : $start;
118     $span = $span=='' ? 15 : $span;
119     $order = $order=='' ? 'calldate' : $order;
120     $sort = $sort=='' ? 'desc' : $sort;
121
122     $displayname = $_SESSION['ari_user']['displayname'];
123     $extension = $_SESSION['ari_user']['extension'];
124
125     // get data
126     $record_count = $this->getCdrCount($q,$duration_filter);
127     $data = $this->getCdrData($q,$duration_filter,$start,$span,$order,$sort);
128
129     // get the call monitor recording files
130     $paths = split(';',$ASTERISK_CALLMONITOR_PATH);
131     foreach($paths as $key => $path) {
132       if (!is_dir($path)) {
133         $_SESSION['ari_error'] .= sprintf(_("Path is not a directory: %s"),$path) . "<br>";
134       }
135     }
136     $recordings = $this->getRecordings($ASTERISK_CALLMONITOR_PATH,$data);
137
138     // build controls
139     if ($CALLMONITOR_ALLOW_DELETE) {
140       $controls .= "
141         <button class='infobar' type='submit' onclick=\"document.callmonitor_form.a.value='delete'\">
142         " . _("delete") . "
143         </button>
144         &nbsp;";
145     }
146
147     $controls .= "
148       <small>" . _("duration") . "</small>
149       <input name='duration_filter' type='text' size=4 maxlength=8 value='" . $_COOKIE['ari_duration_filter'] . "'>
150       <button class='infobar' type='submit' onclick=\"document.callmonitor_form.a.value='ignore'\">
151       " . _("ignore") . "
152       </button>";
153
154     // table header
155     if ($CALLMONITOR_ALLOW_DELETE) {
156       $recording_delete_header = "<th></th>";
157     }
158
159     $fields[0]['field'] = "calldate";
160     $fields[0]['text'] = _("Date");
161     $fields[1]['field'] = "calldate";
162     $fields[1]['text'] = _("Time");
163     $fields[2]['field'] = "clid";
164     $fields[2]['text'] = _("Caller ID");
165     $fields[3]['field'] = "src";
166     $fields[3]['text'] = _("Source");
167     $fields[4]['field'] = "dst";
168     $fields[4]['text'] = _("Destination");
169     $fields[5]['field'] = "dcontext";
170     $fields[5]['text'] = _("Context");
171     $fields[6]['field'] = "duration";
172     $fields[6]['text'] = _("Duration");
173
174     $i = 0;
175     while ($fields[$i]) {
176
177       $field = $fields[$i]['field'];
178       $text = $fields[$i]['text'];
179       if ($order==$field) {
180         if ($sort=='asc') {
181           $currentSort = 'desc';
182           $arrowImg = "<img src='theme/images/arrow-asc.gif' alt='sort'>";
183         }
184         else {
185           $currentSort = 'asc';
186           $arrowImg = "<img src='theme/images/arrow-desc.gif' alt='sort'>";
187         }   
188
189         if ($i==1) {
190           $arrowImg = '';
191         }   
192       }
193       else {
194         $arrowImg = '';
195         $currentSort = 'desc';
196       }
197
198       $unicode_q = urlencode($q);
199       $recording_header .= "<th><a href=" .  $_SESSION['ARI_ROOT'] . "?m=" . $m . "&f=" . $f . "&q=" . $unicode_q . "&order=" . $field . "&sort=" . $currentSort . ">" . $text . $arrowImg . "</a></th>";
200
201       $i++;
202     }
203     $recording_header .= "<th>" . _("Monitor") . "</th>";
204
205     // table body
206     foreach($data as $key=>$value) {
207
208       // recording file
209       $recording = $recordings[$value['uniqueid'] . $value['calldate']];
210       $recordingCrypt = $crypt->encrypt($recording,$ARI_CRYPT_PASSWORD);
211
212       // date and time
213       $buf = split(' ', $value[calldate]);
214       $date = $buf[0];
215       $time = $buf[1];
216
217       // recording delete checkbox
218       if ($CALLMONITOR_ALLOW_DELETE) {
219         $recording_delete_checkbox = "<td class='checkbox'><input type=checkbox name='selected" . ++$i . "' value=" . $recordingCrypt . "></td>";
220       }
221
222       $recordingLink = '';
223       if (is_file($recordings[$value['uniqueid'] . $value['calldate']])) {
224         $recordingLink = "<a href='#' onClick=\"javascript:popUp('misc/recording_popup.php?recording=" . $recordingCrypt . "&date=" . $date . "&time=" . $time . "'); return false;\">" . _("play") . "</a>";
225       }
226    
227       $recording_body .= "<tr>
228                        " . $recording_delete_checkbox . "
229                        <td width=70>" . $date . "</td>
230                        <td>" . $time . "</td>
231                        <td>" . $value[clid] . "</td>
232                        <td>" . $value[src] . "</td>
233                        <td>" . $value[dst] . "</td>
234                        <td>" . $value[dcontext] . "</td>
235                        <td width=90>" . $value[duration] . " sec</td>
236                        <td>" . $recordingLink . "</td>
237                      </tr>";
238     }
239     if (!count($data)) {
240       $recording_body .= "<tr></tr>";
241     }
242
243     // options
244     $url_opts = array();
245     $url_opts['sort'] = $sort;
246     $url_opts['order'] = $order;
247     $url_opts['duration_filter'] = $duration_filter;
248
249     // build page content
250     $ret .= checkErrorMessage();
251
252     // ajax page refresh script
253     if ($AJAX_PAGE_REFRESH_ENABLE) {
254  //     $ret .= ajaxRefreshScript($args);
255     }
256
257     // header
258     if ($_SESSION['ari_user']['admin_callmonitor']) {
259       $header_text = _("Call Monitor");
260     } else {
261       $header_text = sprintf(_("Call Monitor for %s (%s)"),$displayname,$extension);
262     }
263     $ret .= $display->displayHeaderText($header_text);
264     $ret .= $display->displaySearchBlock('left',$m,$q,$url_opts,true);
265
266     // start form
267     if ($CALLMONITOR_ALLOW_DELETE) {
268
269       $ret .= "
270         <form  name='callmonitor_form' action='" . $_SESSION['ARI_ROOT'] . "' method='GET'>
271           <input type=hidden name=m value=" . $m . ">   
272           <input type=hidden name=f value=recAction>
273           <input type=hidden name=a value=''>
274           <input type=hidden name=q value=" . $q . ">
275           <input type=hidden name=start value=" . $start . ">
276           <input type=hidden name=span value=" . $span . ">
277           <input type=hidden name=order value=" . $order . ">
278           <input type=hidden name=sort value=" . $sort . ">";
279     }
280
281     $ret .= $display->displayInfoBarBlock($controls,$q,$start,$span,$record_count);
282
283     // javascript for popup and message actions
284     $ret .= "
285       <SCRIPT LANGUAGE='JavaScript'>
286       <!-- Begin
287       function popUp(URL) {
288         eval(\"page = window.open(URL, 'play', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=1,width=324,height=110');\");
289       }
290
291       function checkAll(form,set) {
292         var elem = 0;
293         var i = 0;
294         while (elem = form.elements[i]) {
295           if (set) {
296             elem.checked = true;
297           } else {
298             elem.checked = false;
299           }
300           i++;
301         }
302         return true;
303       }
304       // End -->
305       </script>";
306
307     // call monitor delete recording controls
308     if ($CALLMONITOR_ALLOW_DELETE) {
309       $ret .= "
310         <table>
311           <tr>
312             <td>
313               <small>" . _("select") . ": </small>
314               <small><a href='' OnClick=\"checkAll(document.callmonitor_form,true); return false;\">" . _("all") . "</a></small>
315               <small><a href='' OnClick=\"checkAll(document.callmonitor_form,false); return false;\">" . _("none") . "</a></small>
316             </td>
317           </tr>
318         </table>";
319     }
320     else {
321       $ret .= "<br>";
322     }
323
324     // table
325     $ret .= "
326       <table class='callmonitor'>
327         <tr>
328           " . $recording_delete_header . "
329           " . $recording_header . "
330         </tr>
331         " . $recording_body . "
332       </table>";
333
334     $start = getArgument($args,'start');
335     $span = getArgument($args,'span');
336     $order = getArgument($args,'order');
337     $sort = getArgument($args,'sort');
338
339     // end form
340     if ($CALLMONITOR_ALLOW_DELETE) {
341       $ret .= "</form>";
342     }
343
344     $ret .= $display->displaySearchBlock('center',$m,$q,$url_opts,false);
345     $ret .= $display->displayNavigationBlock($m,$q,$url_opts,$start,$span,$record_count);
346
347     return $ret;
348   }
349
350   /*
351    * Checks for a recording file
352    *
353    * @param $asterisk_callmonitor_path
354    *   path call monitor recording directory on the asterisk server
355    * @param $data
356    *   current call monitor recordings on the asterisk server
357    * @return $recording
358    *   returns an array of $recording file names if found
359    */
360   function getRecordings($asterisk_callmonitor_path,$data) {
361
362     global $CALLMONITOR_ONLY_EXACT_MATCHING;
363     global $CALLMONITOR_AGGRESSIVE_MATCHING;
364
365     $recordings = array();
366
367     $extension = $_SESSION['ari_user']['extension'];
368
369     $paths = split(';',$asterisk_callmonitor_path);
370     foreach($paths as $key => $path) {
371       $paths[$key] = fixPathSlash($paths[$key]);
372     }
373
374     $files = array();
375     if (!$CALLMONITOR_ONLY_EXACT_MATCHING) {
376       $filter = '';
377       $recursiveMax = 6;
378       $recursiveCount = 0;
379       foreach($paths as $key => $path) {
380         $path_files = getFiles($path,$filter,$recursiveMax,$recursiveCount);
381         if ($path_files) {
382           $files = array_merge($files,$path_files);
383         }
384       }
385       rsort($files);
386     }
387
388     foreach($data as $data_key => $data_value) {
389
390       $recording='';
391
392       $calldate = $data_value['calldate'];
393       $duration = $data_value['duration'];
394       $lastdata = $data_value['lastdata'];
395       $uniqueid = $data_value['uniqueid'];
396       $userfield = $data_value['userfield'];
397
398       // timestamps
399       $st = trim(strtotime($calldate));
400       $et = trim(strtotime($calldate) + $duration);   // for on-demand call recordings
401
402       // unique file key
403       if ($uniqueid) {
404         $buf = preg_replace('/\-|\:/', '', $calldate);
405         $calldate_key = preg_replace('/\s+/', '-', $buf);
406         $unique_file_key = $calldate_key . "-" . $uniqueid;
407       }
408       if ($unique_file_key=='') {
409         $buf = preg_split("/\|/", $lastdata);
410         $unique_file_key = $buf[1];
411       }
412
413       $recordingLink = '';
414       foreach($paths as $callmonitor_key => $path) {
415
416         // try to find an exact match using the uniqueid
417         if (isset($uniqueid)) {
418
419           $check_files = array();
420           array_push($check_files,$path . $uniqueid . ".WAV");
421           array_push($check_files,$path . $uniqueid . ".wav");
422           array_push($check_files,$path . $uniqueid . ".gsm");
423
424           array_push($check_files,$path . $unique_file_key . ".WAV");
425           array_push($check_files,$path . $unique_file_key . ".wav");
426           array_push($check_files,$path . $unique_file_key . ".gsm");
427
428           array_push($check_files,$path . "g" . $extension . "-" . $unique_file_key . ".WAV");
429           array_push($check_files,$path . "g" . $extension . "-" . $unique_file_key . ".wav");
430           array_push($check_files,$path . "g" . $extension . "-" . $unique_file_key . ".gsm");
431
432           array_push($check_files,$path . "q" . $extension . "-" . $unique_file_key . ".WAV");
433           array_push($check_files,$path . "q" . $extension . "-" . $unique_file_key . ".wav");
434           array_push($check_files,$path . "q" . $extension . "-" . $unique_file_key . ".gsm");
435
436           array_push($check_files,$path . "OUT" . $extension . "-" . $unique_file_key . ".WAV");
437           array_push($check_files,$path . "OUT" . $extension . "-" . $unique_file_key . ".wav");
438           array_push($check_files,$path . "OUT" . $extension . "-" . $unique_file_key . ".gsm");
439
440           array_push($check_files,$path . $userfield);
441
442           // try to match
443           foreach($check_files as $check_file) {
444             if (is_file($check_file)) {
445               $recording = $check_file;
446               break;
447             }
448           }
449         }
450
451         // if found do not need to check the rest of the paths
452         if ($recording!='') {
453           break;
454         }
455       }
456
457       // get all the callmonitor recordings on server and try to find a non-exact match for this log entry
458       if (!$CALLMONITOR_ONLY_EXACT_MATCHING) {
459
460         // try to find a file using the uniqueid
461         if (!$recording) {
462
463           // try and match the unique id
464           if (!$recording) {
465             foreach($files as $key => $path) {
466               if (strlen($uniqueid)>1 && strpos($path,$uniqueid)!==FALSE) {
467                 $recording = $path;
468                 $files[$key] = '';  // remove it from the recording files so it will not be matched twice
469                 break;
470               }
471             }
472           }
473         }
474
475         // try and match a file using the calldate (if no unique number from database)
476         if (!$recording) {
477
478           foreach($files as $key => $path) {
479             $parts = split("-", $path);
480             if (strlen($st)>1 &&
481                    (strpos($path,$st)!==FALSE) ||
482                    (strpos($path,"auto")!==FALSE && $parts[1] >= $st && $parts[1] <= $et)) {
483               $recording = $path;
484               $files[$key] = '';  // remove it from the recording files so it will not be matched twice
485               break;
486             }
487           }
488         }
489
490         if ($CALLMONITOR_AGGRESSIVE_MATCHING) {
491
492           // one last stab at finding a recording by adding one or two seconds to the call time
493           if (!$recording) {
494             $st_1 = trim($st+1);
495             $st_2 = trim($st+2);
496             $et_1 = trim($et+1);
497             $et_2 = trim($et+2);
498             foreach($files as $key => $path) {
499               $split = explode("-", $path);
500               if (strlen($st)>1
501                     && ((strpos($path,$st_1)!==FALSE) ||
502                         (strpos($path,$st_2)!==FALSE) ||
503                         (strpos($path,"auto")!==FALSE && $parts[1] >= $st_1 && $parts[1] <= $et_1) ||
504                         (strpos($path,"auto")!==FALSE && $parts[1] >= $st_2 && $parts[1] <= $et_2))) {
505                 $recording = $path;
506                 $files[$key] = '';  // remove it from the recording files so it will not be matched twice
507                 break;
508               }
509             }
510           }
511         }
512       }
513
514       // add to array to be returned
515       if ($recording) {
516         $recordings[$uniqueid . $calldate] = $recording;
517       }
518     }
519
520     return $recordings;
521   }
522
523   /*
524    * Deletes selected call monitor recordings
525    *
526    * @param $files
527    *   Array of files to delete
528    */
529   function deleteRecData($files) {
530
531     global $ARI_CRYPT_PASSWORD;
532
533     $crypt = new Crypt();
534
535     foreach($files as $key => $fileCrypt) {
536       $file = $crypt->decrypt($fileCrypt,$ARI_CRYPT_PASSWORD);
537       if (is_writable($file)) {
538         unlink($file);
539       } else {
540         $_SESSION['ari_error'] = _("Only deletes recording files, not cdr log");
541       }
542     }
543   }
544
545   /*
546    * Gets cdr record count
547    *
548    * @param $q
549    *   query text
550    */
551   function getSearchText($q,$duration_filter) {
552
553     // search text
554     if ($q!='*' && $q!=NULL) {
555       $searchText .= "WHERE ";
556       $tok = strtok($q," \n\t");
557       while ($tok) {
558         $searchText .= " (calldate regexp '" . $tok . "'
559                          OR clid regexp '" . $tok . "'
560                          OR src regexp '" . $tok . "'
561                          OR dst regexp '" . $tok . "'
562                          OR dstchannel regexp '" . $tok . "'
563                          OR dcontext regexp '" . $tok . "'
564                          OR duration regexp '" . $tok . "'
565                          OR disposition regexp '" . $tok . "'
566                          OR uniqueid regexp '" . $tok . "'
567                          OR userfield regexp '" . $tok . "'
568                        )";
569         $tok = strtok(" \n\t");
570         if ($tok) {
571           $searchText .= " AND";
572         }
573       }
574     }
575
576     // duration_filter
577     if ($duration_filter) {
578       if (!$searchText) {
579         $searchText .= "WHERE ";
580       } else {
581         $searchText .= "AND ";
582       }
583       $searchText .= "duration>" . $duration_filter . " ";
584     }
585
586     // admin
587     if (!$_SESSION['ari_user']['admin_callmonitor']) {
588       if (!$searchText) {
589         $searchText .= "WHERE ";
590       } else {
591         $searchText .= "AND ";
592       }
593
594       // allow entries to be viewed with users extension
595       $searchText .= "(src = '" . $_SESSION['ari_user']['extension'] . "'
596                       OR dst = '" . $_SESSION['ari_user']['extension'] . "'
597
598                                         OR channel LIKE 'IAX2/" . $_SESSION['ari_user']['extension'] ."-%'
599                                         OR dstchannel LIKE 'IAX2/" . $_SESSION['ari_user']['extension'] ."-%'
600
601                       OR channel LIKE 'SIP/" . $_SESSION['ari_user']['extension'] ."-%'
602                       OR dstchannel LIKE 'SIP/" . $_SESSION['ari_user']['extension'] ."-%')";
603
604       // allow entries to be viewed with users outbound CID
605       if (isset($_SESSION['ari_user']['outboundCID']) && trim($_SESSION['ari_user']['outboundCID']) != '') {
606         $searchText .= "OR (src = '" . $_SESSION['ari_user']['outboundCID'] . "'
607                         OR dst = '" . $_SESSION['ari_user']['outboundCID'] . "')";
608       }
609     }
610
611     return $searchText;
612   }
613
614   /*
615    * Gets cdr record count
616    *
617    * @param $q
618    *   query text
619    * @return $count
620    *   Number of cdr records counted
621    */
622   function getCdrCount($q,$duration_filter) {
623
624     global $ASTERISKCDR_DBTABLE;
625
626     $searchText = $this->getSearchText($q,$duration_filter);
627
628     $dbh = $_SESSION['dbh_cdr'];
629     $sql = "SELECT count(*)
630             FROM " . $ASTERISKCDR_DBTABLE . "
631             " . $searchText;
632
633     $result = $dbh->getAll($sql);
634     if (DB::isError($result)) {
635       $_SESSION['ari_error'] = $result->getMessage();
636       return;
637     }
638     $count = $result[0][0];
639
640     return $count;
641   }
642
643   /*
644    * Gets cdr data
645    *
646    * @param $q
647    *   query text
648    * @param $start
649    *   start record
650    * @param $span
651    *   number of records to return
652    * @return $data
653    *   cdr data to be returned
654    */
655   function getCdrData($q,$duration_filter,$start,$span,$order,$sort) {
656
657     global $ASTERISKCDR_DBTABLE;
658
659     $data = array();
660
661     $searchText = $this->getSearchText($q,$duration_filter);
662
663     $dbh = $_SESSION['dbh_cdr'];
664     $sql = "SELECT *
665             FROM " . $ASTERISKCDR_DBTABLE . "
666             " . $searchText . "
667             ORDER BY " . $order . " " . $sort . "
668             LIMIT " . $start . "," . $span;
669     $result = $dbh->getAll($sql,DB_FETCHMODE_ASSOC);
670     if (DB::isError($result)) {
671       $_SESSION['ari_error'] = $result->getMessage();
672       return;
673     }
674     $data = $result;
675
676     return $data;
677   }
678
679
680 }
681
682
683 ?>
Donate



Support
Download
Develop
Forums
News
Documentation
Paid Support
About

Paid Ads