international numbers have a +
[auerswald-callnotifier.git] / src / callnotifier / CallMonitor.php
1 <?php
2 namespace callnotifier;
3
4 /**
5  * Watches EDSS1 messages for calls. Keeps an internal call state
6  * and notifies loggers of incoming and finished calls.
7  *
8  * Notifications:
9  * - startingCall
10  * - finishedCall
11  */
12 class CallMonitor
13 {
14     protected $currentCalls = array();
15
16     /**
17      * Array of objects that are able to load details for a call
18      *
19      * @var array
20      */
21     protected $detaillers = array();
22
23     public function __construct($config, $log)
24     {
25         $this->config = $config;
26         $this->log = $log;
27     }
28
29     public function addDetailler(CallMonitor_Detailler $detailler)
30     {
31         $this->detaillers[] = $detailler;
32     }
33
34     public function handle(EDSS1_Message $msg)
35     {
36         $callId = $msg->callRef;
37         if (!array_key_exists($callId, $this->currentCalls)) {
38             $this->handleNew($callId, $msg);
39         } else {
40             $this->handleExisting($callId, $msg);
41         }
42     }
43
44     protected function handleNew($callId, EDSS1_Message $msg)
45     {
46         if ($msg->type != EDSS1_Message::SETUP) {
47             return;
48         }
49         $this->currentCalls[$callId] = new CallMonitor_Call();
50         $this->handleSetup($callId, $msg);
51     }
52
53
54     protected function handleSetup($callId, EDSS1_Message $msg)
55     {
56         $call = $this->currentCalls[$callId];
57         $call->start = time();
58         if ($msg->tei == 127) {
59             $call->type = CallMonitor_Call::INCOMING;
60         } else {
61             $call->type = CallMonitor_Call::OUTGOING;
62         }
63
64         $this->handleParams($msg, $call, $callId);
65     }
66
67
68     protected function handleExisting($callId, EDSS1_Message $msg)
69     {
70         $call = $this->currentCalls[$callId];
71
72         switch ($msg->type) {
73         case EDSS1_Message::INFORMATION:
74             $this->handleParams($msg, $call, $callId);
75             break;
76         case EDSS1_Message::ALERTING:
77             if ($call->type == CallMonitor_Call::OUTGOING) {
78                 /**
79                  * There may be two alerts: One from the telephone to the
80                  * switchboard, and one from the switchboard to the target.
81                  *
82                  * The alert from the switchboard to the target call is
83                  * sent first, so we can remove the call from the telephone
84                  * to the switchboard.
85                  */
86                 $bFound = false;
87                 foreach ($this->currentCalls as $otherCallId => $otherCall) {
88                     if ($otherCallId != $callId && $otherCall->to == $call->to) {
89                         $bFound = true;
90                         break;
91                     }
92                 }
93                 if ($bFound) {
94                     unset($this->currentCalls[$otherCallId]);
95                 }
96             }
97             $this->loadCallDetails($call);
98             $this->log->log('startingCall', array('call' => $call));
99             break;
100
101         case EDSS1_Message::RELEASE:
102         case EDSS1_Message::RELEASE_COMPLETE:
103             $call->end = time();
104             $this->log->log('finishedCall', array('call' => $call));
105             unset($this->currentCalls[$callId]);
106             break;
107         }
108     }
109
110     protected function handleParams($msg, $call, $callId)
111     {
112         foreach ($msg->parameters as $param) {
113             switch ($param->type) {
114             case EDSS1_Parameter::CALLING_PARTY_NUMBER:
115                 $call->from = $this->getFullNumber(
116                     $param->number, $param->numberType
117                 );
118                 break;
119             case EDSS1_Parameter::CALLED_PARTY_NUMBER:
120                 $call->to = $this->getFullNumber(
121                     $param->number, $param->numberType
122                 );
123                 if ($call->type == CallMonitor_Call::INCOMING
124                     && $param->numberType != EDSS1_Parameter_Names::NUMBER_SUBSCRIBER
125                 ) {
126                     //only keep incoming calls that arrive at the switchboard,
127                     // not the ones from the switchboard to the telephones
128                     unset($this->currentCalls[$callId]);
129                 }
130                 break;
131             case EDSS1_Parameter::KEYPAD:
132                 if ($call->to === null) {
133                     $call->to = $param->data;
134                 }
135             }
136         }
137     }
138
139
140     protected function getFullNumber($number, $type)
141     {
142         if ($type == EDSS1_Parameter_Names::NUMBER_NATIONAL) {
143             return '0' . $number;
144         } else if ($type == EDSS1_Parameter_Names::NUMBER_INTERNATIONAL) {
145             return '+' . $number;
146         }
147         return $number;
148     }
149
150     /**
151      * Load details for a call, e.g. the name of the calling person
152      * or the area
153      *
154      * @return void
155      */
156     protected function loadCallDetails($call)
157     {
158         foreach ($this->detaillers as $detailler) {
159             $detailler->loadCallDetails($call);
160         }
161     }
162 }
163
164 ?>