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