1 /*!
2 * \file sccp_featureParkingLot.c
3 * \brief SCCP ParkingLot Class
4 * \author Diederik de Groot <ddegroot [at] users.sf.net>
5 * \date 2015-Sept-16
6 * \note This program is free software and may be modified and distributed under the terms of the GNU Public License.
7 * See the LICENSE file at the top of the source tree.
8 *
9 * $date$
10 * $revision$
11 */
12 #include "config.h"
13 #include "common.h"
14
15 SCCP_FILE_VERSION(__FILE__, "");
16
17 #include "sccp_featureParkingLot.h"
18
19 #ifdef CS_SCCP_PARK
20
21 #include "sccp_utils.h"
22 #include "sccp_vector.h"
23 #include "sccp_device.h"
24 #include "sccp_line.h"
25 # include "sccp_linedevice.h"
26 # include "sccp_channel.h"
27 # include "sccp_feature.h"
28 # include "sccp_labels.h"
29
30 static const uint32_t appID = APPID_VISUALPARKINGLOT;
31
32 #ifdef HAVE_PBX_APP_H
33 # include <asterisk/app.h>
34 #endif
35
36 /* asterisk-11 */
37 /*
38 Event: ParkedCall
39 Privilege: call,all
40 Timestamp: 1460205775.670404
41 Exten: 701
42 Channel: SCCP/10041-0000000e
43 Parkinglot: default
44 From: SCCP/10011-0000000f
45 Timeout: 45
46 CallerIDNum: 10041
47 CallerIDName: PHONE4
48 ConnectedLineNum: <unknown>
49 ConnectedLineName: <unknown>
50 Uniqueid: 1460205775.48
51
52 Event: UnParkedCall
53 Privilege: call,all
54 Timestamp: 1460205785.934545
55 Exten: 701
56 Channel: SCCP/10041-0000000e
57 Parkinglot: default
58 From: SCCP/10031-00000010
59 CallerIDNum: 10041
60 CallerIDName: PHONE4
61 ConnectedLineNum: <unknown>
62 ConnectedLineName: <unknown>
63 Uniqueid: 1460205775.48
64
65 Event: ParkedCallGiveUp
66 Privilege: call,all
67 Timestamp: 1460819185.922496
68 Exten: 701
69 Channel: SCCP/10041-00000001
70 Parkinglot: default
71 CallerIDNum: 10041
72 CallerIDName: PHONE4
73 ConnectedLineNum: 10011
74 ConnectedLineName: Diederik-Phone1
75 UniqueID: 1460819174.54
76
77 Event: ParkedCallTimeOut
78 Privilege: call,all
79 Timestamp: 1460974082.683646
80 Exten: 701
81 Channel: SCCP/10011-00000003
82 Parkinglot: default
83 CallerIDNum: 10011
84 CallerIDName: Diederik-Phone1
85 ConnectedLineNum: 10031
86 ConnectedLineName: Diederik-Phone3
87 UniqueID: 1460974037.17
88 */
89
90 /* asterisk-13 */
91 /*
92 Event: ParkedCall
93 Privilege: call,all
94 SequenceNumber: 118
95 File: parking/parking_manager.c
96 Line: 676
97 Func: parked_call_message_response
98 ParkeeChannel: SCCP/10011-00000001
99 ParkeeChannelState: 6
100 ParkeeChannelStateDesc: Up
101 ParkeeCallerIDNum: 10011
102 ParkeeCallerIDName: Diederik-Phone1
103 ParkeeConnectedLineNum: <unknown>
104 ParkeeConnectedLineName: <unknown>
105 ParkeeLanguage: en
106 ParkeeAccountCode: 10011
107 ParkeeContext: internal
108 ParkeeExten: 10031
109 ParkeePriority: 3
110 ParkeeUniqueid: 1461160476.0
111 ParkeeLinkedid: 1461160476.0
112 ParkerDialString: SCCP/10031
113 Parkinglot: default
114 ParkingSpace: 701
115 ParkingTimeout: 45
116 ParkingDuration: 0
117
118 UnParkedCall
119 Privilege: call,all
120 SequenceNumber: 1091
121 File: parking/parking_manager.c
122 Line: 676
123 Func: parked_call_message_response
124 ParkeeChannel: SCCP/10011-00000001
125 ParkeeChannelState: 6
126 ParkeeChannelStateDesc: Up
127 ParkeeCallerIDNum: 10011
128 ParkeeCallerIDName: Diederik-Phone1
129 ParkeeConnectedLineNum: <unknown>
130 ParkeeConnectedLineName: <unknown>
131 ParkeeLanguage: en
132 ParkeeAccountCode: 10011
133 ParkeeContext: internal
134 ParkeeExten: 10031
135 ParkeePriority: 3
136 ParkeeUniqueid: 1461161791.29
137 ParkeeLinkedid: 1461161791.29
138 RetrieverChannel: SCCP/10041-00000003
139 RetrieverChannelState: 6
140 RetrieverChannelStateDesc: Up
141 RetrieverCallerIDNum: 10041
142 RetrieverCallerIDName: PHONE4
143 RetrieverConnectedLineNum: <unknown>
144 RetrieverConnectedLineName: <unknown>
145 RetrieverLanguage: en
146 RetrieverAccountCode: 79005
147 RetrieverContext: internal
148 RetrieverExten: 701
149 RetrieverPriority: 1
150 RetrieverUniqueid: 1461161803.31
151 RetrieverLinkedid: 1461161803.31
152 ParkerDialString: SCCP/10031
153 Parkinglot: default
154 ParkingSpace: 701
155 ParkingTimeout: 35
156 ParkingDuration: 10
157
158 Event: ParkedCallGiveUp
159 Privilege: call,all
160 SequenceNumber: 142
161 File: parking/parking_manager.c
162 Line: 676
163 Func: parked_call_message_response
164 ParkeeChannel: SCCP/10011-00000001
165 ParkeeChannelState: 6
166 ParkeeChannelStateDesc: Up
167 ParkeeCallerIDNum: 10011
168 ParkeeCallerIDName: Diederik-Phone1
169 ParkeeConnectedLineNum: <unknown>
170 ParkeeConnectedLineName: <unknown>
171 ParkeeLanguage: en
172 ParkeeAccountCode: 10011
173 ParkeeContext: internal
174 ParkeeExten: 10031
175 ParkeePriority: 3
176 ParkeeUniqueid: 1461160476.0
177 ParkeeLinkedid: 1461160476.0
178 ParkerDialString: SCCP/10031
179 Parkinglot: default
180 ParkingSpace: 701
181 ParkingTimeout: 36
182 ParkingDuration: 9
183
184 Event: ParkedCallTimeOut
185 Privilege: call,all
186 SequenceNumber: 427
187 File: parking/parking_manager.c
188 Line: 676
189 Func: parked_call_message_response
190 ParkeeChannel: SCCP/10011-00000007
191 ParkeeChannelState: 6
192 ParkeeChannelStateDesc: Up
193 ParkeeCallerIDNum: 10011
194 ParkeeCallerIDName: Diederik-Phone1
195 ParkeeConnectedLineNum: <unknown>
196 ParkeeConnectedLineName: <unknown>
197 ParkeeLanguage: en
198 ParkeeAccountCode: 10011
199 ParkeeContext: park-dial
200 ParkeeExten: SCCP_10031
201 ParkeePriority: 1
202 ParkeeUniqueid: 1461160740.12
203 ParkeeLinkedid: 1461160740.12
204 ParkerDialString: SCCP/10031
205 Parkinglot: default
206 ParkingSpace: 701
207 ParkingTimeout: 0
208 ParkingDuration: 45
209 */
210
211 /* forward declarations */
212 struct parkinglot;
213 typedef struct parkinglot sccp_parkinglot_t;
214 static void notifyLocked(sccp_parkinglot_t *pl);
215
216 typedef struct plslot plslot_t;
217 typedef struct plobserver plobserver_t;
218
219 /* private variables */
220 struct plslot {
221 int slot;
222 const char *exten;
223 const char *from;
224 const char *channel;
225 const char *callerid_num;
226 const char *callerid_name;
227 const char *connectedline_num;
228 const char *connectedline_name;
229 };
230
231 struct plobserver {
232 sccp_device_t * device;
233 uint8_t instance;
234 uint8_t transactionId;
235 };
236
237 struct parkinglot {
238 pbx_mutex_t lock;
239 char *context;
240 SCCP_VECTOR(, plobserver_t) observers;
241 SCCP_VECTOR(, plslot_t) slots;
242 SCCP_RWLIST_ENTRY(sccp_parkinglot_t) list;
243 };
244
245 #define ICONSTATE_NEW_ON 0x020303 // option:closed, color=yellow, flashspeed=slow
246 #define ICONSTATE_NEW_OFF 0x010000 // option:open, color=off, flashspeed=None
247 #define ICONSTATE_OLD_ON 1 // option:closed
248 #define ICONSTATE_OLD_OFF 0 // option:open
249
250 /* private functions */
251 //#define sccp_parkinglot_lock(x) ({sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_2 "%s:%d:requestinglock:%p\n",__PRETTY_FUNCTION__,__LINE__,x);pbx_mutex_lock(&((sccp_parkinglot_t * const)(x))->lock);sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_2 "%s:%d:locked:%p\n",__PRETTY_FUNCTION__,__LINE__,x);}) // discard const
252 //#define sccp_parkinglot_unlock(x) ({sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_2 "%s:%d:unlock:%p\n",__PRETTY_FUNCTION__,__LINE__,x);pbx_mutex_unlock(&((sccp_parkinglot_t * const)(x))->lock);}) // discard const
253 #define sccp_parkinglot_lock(x) ({pbx_mutex_lock(&((sccp_parkinglot_t * const)(x))->lock);}) // discard const
254 #define sccp_parkinglot_unlock(x) ({pbx_mutex_unlock(&((sccp_parkinglot_t * const)(x))->lock);}) // discard const
255
256 SCCP_RWLIST_HEAD(sccp_parkinglot_vector, sccp_parkinglot_t) parkinglots;
257 #define OBSERVER_CB_CMP(elem, value) ((elem).device == (value).device && (elem).instance == (value).instance)
258 #define SLOT_CB_CMP(elem, value) ((elem).slot == (value))
259
260 #define SLOT_CLEANUP(elem) \
261 if ((elem).exten) {sccp_free((elem).exten);} \
262 if ((elem).from) {sccp_free((elem).from);} \
263 if ((elem).channel) {sccp_free((elem).channel);} \
264 if ((elem).callerid_num) {sccp_free((elem).callerid_num);} \
265 if ((elem).callerid_name) {sccp_free((elem).callerid_name);} \
266 if ((elem).connectedline_num) {sccp_free((elem).connectedline_num);} \
267 if ((elem).connectedline_name) {sccp_free((elem).connectedline_name);}
268
269
270 /* exported functions */
addParkinglot(const char * parkinglot)271 static sccp_parkinglot_t * addParkinglot(const char *parkinglot)
272 {
273 pbx_assert(parkinglot != NULL);
274
275 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "SCCP: (addParkinglot) %s\n", parkinglot);
276 sccp_parkinglot_t *pl = (sccp_parkinglot_t *) sccp_calloc(sizeof(sccp_parkinglot_t), 1);
277
278 pl->context = pbx_strdup(parkinglot);
279 pbx_mutex_init(&pl->lock);
280 SCCP_VECTOR_INIT(&pl->observers,1);
281 SCCP_VECTOR_INIT(&pl->slots,1);
282
283 SCCP_RWLIST_WRLOCK(&parkinglots);
284 SCCP_RWLIST_INSERT_HEAD(&parkinglots, pl, list);
285 SCCP_RWLIST_UNLOCK(&parkinglots);
286 return pl;
287 }
288
removeParkinglot(sccp_parkinglot_t * pl)289 static int removeParkinglot(sccp_parkinglot_t *pl)
290 {
291 pbx_assert(pl != NULL && pl != NULL);
292
293 int res = FALSE;
294 sccp_parkinglot_t *removed = NULL;
295 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "SCCP: (removeParkinglot) %s\n", pl->context);
296
297 SCCP_RWLIST_WRLOCK(&parkinglots);
298 removed = SCCP_RWLIST_REMOVE(&parkinglots, pl, list);
299 SCCP_RWLIST_UNLOCK(&parkinglots);
300 sccp_parkinglot_unlock(pl);
301
302 if (removed) {
303 if (removed->context) {
304 sccp_free(removed->context);
305 }
306 SCCP_VECTOR_RESET(&removed->observers, SCCP_VECTOR_ELEM_CLEANUP_NOOP);
307 SCCP_VECTOR_FREE(&removed->observers);
308 SCCP_VECTOR_RESET(&removed->slots, SLOT_CLEANUP);
309 SCCP_VECTOR_FREE(&removed->slots);
310 pbx_mutex_destroy(&removed->lock);
311 sccp_free(removed);
312 res = TRUE;
313 }
314 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "SCCP: (removeParkinglot) done\n");
315 return res;
316 }
317
318 /* returns locked pl */
findParkinglotByContext(const char * parkinglot)319 static sccp_parkinglot_t * const findParkinglotByContext(const char *parkinglot)
320 {
321 pbx_assert(parkinglot != NULL);
322
323 sccp_parkinglot_t *pl = NULL;
324 SCCP_RWLIST_RDLOCK(&parkinglots);
325 SCCP_RWLIST_TRAVERSE(&parkinglots, pl, list) {
326 sccp_parkinglot_lock(pl);
327 if (sccp_strcaseequals(pl->context, parkinglot)) {
328 //sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "SCCP: (findParkinglotByContext) found match:%s\n", pl->context);
329 // returning parkinglot locked
330 break;
331 }
332 sccp_parkinglot_unlock(pl);
333 }
334 SCCP_RWLIST_UNLOCK(&parkinglots);
335 return pl;
336 }
337
338 /* returns locked pl */
findCreateParkinglot(const char * parkinglot,boolean_t create)339 static sccp_parkinglot_t * const findCreateParkinglot(const char *parkinglot, boolean_t create)
340 {
341 pbx_assert(parkinglot != NULL);
342
343 //sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "SCCP: (findCreateParkinglot) %s (create:%s)\n", parkinglot, create ? "TRUE" : "FALSE");
344 sccp_parkinglot_t *pl = findParkinglotByContext(parkinglot);
345 if (!pl && create) {
346 if (!(pl = addParkinglot(parkinglot))) {
347 //pbx_log(LOG_NOTICE, "SCCP: (findCreateParkinglot) Could not add ParkingLot: %s\n", parkinglot);
348 return NULL;
349 }
350 //sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "SCCP: (findCreateParkinglot) New %s Created\n", parkinglot);
351 sccp_parkinglot_lock(pl);
352 }
353 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "SCCP: (findCreateParkinglot) Found:%s \n", pl ? "TRUE" : "FALSE");
354 return pl;
355 }
356
357 // observer
attachObserver(sccp_device_t * device,const sccp_buttonconfig_t * const buttonConfig)358 static int attachObserver(sccp_device_t * device, const sccp_buttonconfig_t * const buttonConfig)
359 {
360 pbx_assert(device != NULL && buttonConfig != NULL);
361 int res = FALSE;
362
363 if(!sccp_strlen_zero(buttonConfig->button.feature.options)) {
364 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (attachObserver) device:%s at instance:%d\n", buttonConfig->button.feature.options, device->id, buttonConfig->instance);
365 RAII(sccp_parkinglot_t *, pl, findCreateParkinglot(buttonConfig->button.feature.options, TRUE), sccp_parkinglot_unlock);
366 if (pl) {
367 plobserver_t observer = {
368 .device = device,
369 .instance = buttonConfig->instance,
370 .transactionId = 0,
371 };
372
373 /* upgrade to wrlock */
374 if (SCCP_VECTOR_APPEND(&pl->observers, observer) == 0) {
375 res = TRUE;
376 }
377 }
378 }
379 return res;
380 }
381
detachObserver(sccp_device_t * device,const sccp_buttonconfig_t * const buttonConfig)382 static int detachObserver(sccp_device_t * device, const sccp_buttonconfig_t * const buttonConfig)
383 {
384 pbx_assert(device != NULL && buttonConfig != NULL);
385 int res = FALSE;
386
387 if(!sccp_strlen_zero(buttonConfig->button.feature.options)) {
388 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (detachObserver) device:%s at instance:%d\n", buttonConfig->button.feature.options, device->id, buttonConfig->instance);
389 sccp_parkinglot_t * pl = findCreateParkinglot(buttonConfig->button.feature.options, FALSE); /* don't use RAII, removeParkinglot unlocks and destroys the lock */
390 if (pl) {
391 plobserver_t cmp = {
392 .device = device,
393 .instance = buttonConfig->instance,
394 };
395 if (SCCP_VECTOR_REMOVE_CMP_UNORDERED(&pl->observers, cmp, OBSERVER_CB_CMP, SCCP_VECTOR_ELEM_CLEANUP_NOOP) == 0) {
396 res = TRUE;
397 }
398 if (SCCP_VECTOR_SIZE(&pl->observers) == 0) {
399 removeParkinglot(pl); // will destroy pl and unlock pl in the process
400 } else {
401 sccp_parkinglot_unlock(pl);
402 }
403 }
404 }
405 return res;
406 }
407
getParkingLotCXML(sccp_parkinglot_t * pl,int protocolversion,uint8_t instance,uint32_t transactionId,char ** const outbuf)408 static char * const getParkingLotCXML(sccp_parkinglot_t *pl, int protocolversion, uint8_t instance, uint32_t transactionId, char **const outbuf)
409 {
410 pbx_assert(pl != NULL && outbuf != NULL);
411
412 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (getParkingLotCXML) with version:%d\n", pl->context, protocolversion);
413 *outbuf = NULL;
414 if (SCCP_VECTOR_SIZE(&pl->slots)) {
415 pbx_str_t *buf = ast_str_create(DEFAULT_PBX_STR_BUFFERSIZE);
416 pbx_str_append(&buf, 0, "<?xml version=\"1.0\"?>");
417 if (protocolversion < 15) {
418 pbx_str_append(&buf, 0, "<CiscoIPPhoneMenu>");
419 } else {
420 pbx_str_append(&buf, 0, "<CiscoIPPhoneMenu appId='%d' onAppClosed='%d'>", appID, appID);
421 }
422 pbx_str_append(&buf, 0, "<Title>Parked Calls</Title>");
423 pbx_str_append(&buf, 0, "<Prompt>Choose a ParkingLot Slot</Prompt>");
424 for(uint8_t idx = 0; idx < SCCP_VECTOR_SIZE(&pl->slots); idx++) {
425 plslot_t *slot = SCCP_VECTOR_GET_ADDR(&pl->slots, idx);
426 pbx_str_append(&buf, 0, "<MenuItem>");
427 const char *connected_line = !sccp_strcaseequals(slot->connectedline_name, "<unknown>") ? slot->connectedline_name : slot->from;
428 if (!sccp_strcaseequals(slot->callerid_name, "<unknown>")) {
429 pbx_str_append(&buf, 0, "<Name>%s (%s) by %s</Name>", slot->callerid_name, slot->callerid_num, connected_line);
430 } else {
431 pbx_str_append(&buf, 0, "<Name>%s by %s</Name>", slot->callerid_num, connected_line);
432 }
433 pbx_str_append(&buf, 0, "<URL>UserCallData:%d:%d:%d:%d:%s/%s</URL>", appID, instance, 0, transactionId, pl->context, slot->exten);
434 pbx_str_append(&buf, 0, "</MenuItem>");
435 }
436 pbx_str_append(&buf, 0, "<SoftKeyItem>");
437 pbx_str_append(&buf, 0, "<Name>Dial</Name>");
438 pbx_str_append(&buf, 0, "<Position>1</Position>");
439 pbx_str_append(&buf, 0, "<URL>UserDataSoftKey:Select:%d:DIAL/%d</URL>", appID, transactionId);
440 pbx_str_append(&buf, 0, "</SoftKeyItem>\n");
441 pbx_str_append(&buf, 0, "<SoftKeyItem>");
442 pbx_str_append(&buf, 0, "<Name>Exit</Name>");
443 pbx_str_append(&buf, 0, "<Position>3</Position>");
444 pbx_str_append(&buf, 0, "<URL>UserDataSoftKey:Select:%d:EXIT/%d</URL>", appID, transactionId);
445 pbx_str_append(&buf, 0, "</SoftKeyItem>\n");
446
447 pbx_str_append(&buf, 0, "</CiscoIPPhoneMenu>");
448 *outbuf = pbx_strdup(pbx_str_buffer(buf));
449 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (getParkingLotCXML) with version:%d, result:\n[%s]\n", pl->context, protocolversion, *outbuf);
450 sccp_free(buf);
451 }
452 return *outbuf;
453 }
454
__showVisualParkingLot(sccp_parkinglot_t * pl,constDevicePtr d,plobserver_t * observer)455 static void __showVisualParkingLot(sccp_parkinglot_t *pl, constDevicePtr d, plobserver_t * observer)
456 {
457 pbx_assert(pl != NULL && d != NULL && observer != NULL);
458 uint32_t transactionId = sccp_random();
459 char * xmlStr = NULL;
460
461 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (showVisualParkingLot) showing on device:%s, instance:%d\n", pl->context, observer->device->id, observer->instance);
462 if ((xmlStr = getParkingLotCXML(pl, d->protocolversion, observer->instance, transactionId, &xmlStr))) {
463 sccp_parkinglot_unlock(pl);
464 d->protocol->sendUserToDeviceDataVersionMessage(d, appID, 0, 0, transactionId, xmlStr, 0);
465 sccp_free(xmlStr);
466 sccp_parkinglot_lock(pl);
467 } else {
468 sccp_parkinglot_unlock(pl);
469 sccp_dev_displayprinotify(d, SKINNY_DISP_CANNOT_RETRIEVE_PARKED_CALL, SCCP_MESSAGE_PRIORITY_TIMEOUT, 5);
470 sccp_parkinglot_lock(pl);
471 }
472 observer->transactionId = transactionId;
473 }
474
__hideVisualParkingLot(sccp_parkinglot_t * pl,constDevicePtr d,plobserver_t * observer)475 static void __hideVisualParkingLot(sccp_parkinglot_t *pl, constDevicePtr d, plobserver_t *observer)
476 {
477 pbx_assert(pl != NULL && observer != NULL);
478
479 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (hideVisualParkingLot) device:%s, instance:%d\n", pl->context, d->id, observer->instance);
480 uint32_t transactionId = observer->transactionId;
481 sccp_parkinglot_unlock(pl);
482
483 char xmlStr[DEFAULT_PBX_STR_BUFFERSIZE];
484 if (d->protocolversion < 15) {
485 snprintf(xmlStr, DEFAULT_PBX_STR_BUFFERSIZE, "<CiscoIPPhoneExecute><ExecuteItem Priority=\"0\" URL=\"Init:Services\"/></CiscoIPPhoneExecute>");
486 } else {
487 snprintf(xmlStr, DEFAULT_PBX_STR_BUFFERSIZE, "<CiscoIPPhoneExecute><ExecuteItem Priority=\"0\" URL=\"App:Close:%d\"/></CiscoIPPhoneExecute>", appID);
488 }
489 d->protocol->sendUserToDeviceDataVersionMessage(observer->device, appID, 0, 0, transactionId, xmlStr, 0);
490
491 sccp_parkinglot_lock(pl);
492 observer->transactionId = 0;
493 }
494
hideVisualParkingLot(const char * parkinglot,constDevicePtr d,uint8_t instance)495 static void hideVisualParkingLot(const char *parkinglot, constDevicePtr d, uint8_t instance)
496 {
497 pbx_assert(parkinglot != NULL && d != NULL);
498
499 RAII(sccp_parkinglot_t *, pl, findCreateParkinglot(parkinglot, TRUE), sccp_parkinglot_unlock);
500 if (pl) {
501 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (hideVisualParkingLot) device:%s, instance:%d, size:%d\n", parkinglot, d->id, instance, (int)SCCP_VECTOR_SIZE(&pl->observers));
502 for(uint8_t idx = 0; idx < SCCP_VECTOR_SIZE(&pl->observers); idx++) {
503 plobserver_t *observer = SCCP_VECTOR_GET_ADDR(&pl->observers, idx);
504 if (observer->device == d && observer->instance == instance) {
505 __hideVisualParkingLot(pl, d, observer);
506 }
507 }
508 }
509 }
510
_notifyHelper(plobserver_t * observer,sccp_parkinglot_t * pl,constDevicePtr device)511 static void _notifyHelper(plobserver_t *observer, sccp_parkinglot_t *pl, constDevicePtr device)
512 {
513 uint32_t iconstate = 0;
514 sccp_buttonconfig_t *config = NULL;
515 int numslots = SCCP_VECTOR_SIZE(&pl->slots);
516 if (device->protocolversion < 15) {
517 sccp_device_setLamp(device, SKINNY_STIMULUS_PARKINGLOT, 0, numslots ? SKINNY_LAMP_ON : SKINNY_LAMP_OFF);
518 iconstate = numslots ? ICONSTATE_OLD_ON : ICONSTATE_OLD_OFF;
519 } else {
520 iconstate = numslots ? ICONSTATE_NEW_ON : ICONSTATE_NEW_OFF;
521 }
522
523 // change button state
524 SCCP_LIST_LOCK(&device->buttonconfig);
525 SCCP_LIST_TRAVERSE(&device->buttonconfig, config, list) {
526 if (config->type == FEATURE && config->instance == observer->instance) {
527 config->button.feature.status = iconstate;
528 }
529 }
530 SCCP_LIST_UNLOCK(&device->buttonconfig);
531
532 // update already displayed visual parkinglot window
533 if (observer->transactionId) {
534 if (numslots > 0 && !device->active_channel) {
535 __showVisualParkingLot(pl, device, observer);
536 } else {
537 __hideVisualParkingLot(pl, device, observer);
538 }
539 }
540 sccp_feat_changed(device, NULL, SCCP_FEATURE_PARKINGLOT);
541 }
542
notifyDevice(constDevicePtr device,const sccp_buttonconfig_t * const buttonConfig)543 static void notifyDevice(constDevicePtr device, const sccp_buttonconfig_t * const buttonConfig)
544 {
545 pbx_assert(device != NULL && buttonConfig != NULL);
546 uint8_t idx = 0;
547 //uint32_t iconstate = 0;
548 plobserver_t *observer = NULL;
549
550 if(!sccp_strlen_zero(buttonConfig->button.feature.options)) {
551 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (notifyDevice) notifyDevice:%s\n", buttonConfig->button.feature.options, device->id);
552 RAII(sccp_parkinglot_t *, pl, findCreateParkinglot(buttonConfig->button.feature.options, TRUE), sccp_parkinglot_unlock);
553 if (pl) {
554 for (idx = 0; idx < SCCP_VECTOR_SIZE(&pl->observers); idx++) {
555 observer = SCCP_VECTOR_GET_ADDR(&pl->observers, idx);
556 if (observer && observer->device == device) {
557 _notifyHelper(observer, pl, device);
558 }
559 }
560 }
561 }
562 }
563
notifyLocked(sccp_parkinglot_t * pl)564 static void notifyLocked(sccp_parkinglot_t *pl)
565 {
566 pbx_assert(pl != NULL);
567
568 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (notify)\n", pl->context);
569 uint8_t idx = 0;
570 plobserver_t *observer = NULL;
571
572 for (idx = 0; idx < SCCP_VECTOR_SIZE(&pl->observers); idx++) {
573 observer = SCCP_VECTOR_GET_ADDR(&pl->observers, idx);
574 if (observer) {
575 AUTO_RELEASE(sccp_device_t, device , sccp_device_retain(observer->device));
576 if (device) {
577 _notifyHelper(observer, pl, device);
578 }
579 }
580 }
581 }
582
583 // slot
addSlot(const char * parkinglot,int slot,struct message * m)584 static int addSlot(const char *parkinglot, int slot, struct message *m)
585 {
586 pbx_assert(parkinglot != NULL && m != NULL);
587
588 int res = FALSE;
589
590 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (addSlot) adding to slot:%d\n", parkinglot, slot);
591
592 RAII(sccp_parkinglot_t *, pl, findCreateParkinglot(parkinglot, TRUE), sccp_parkinglot_unlock);
593 if (pl) {
594 if (SCCP_VECTOR_GET_CMP(&pl->slots, slot, SLOT_CB_CMP) == NULL) {
595 plslot_t new_slot = {
596 .slot = slot,
597 .exten = pbx_strdup(astman_get_header(m, PARKING_SLOT)),
598 .from = pbx_strdup(astman_get_header(m, PARKING_FROM)),
599 .channel = pbx_strdup(astman_get_header(m, PARKING_PREFIX "Channel")),
600 .callerid_num = pbx_strdup(astman_get_header(m, PARKING_PREFIX "CallerIDNum")),
601 .callerid_name = pbx_strdup(astman_get_header(m, PARKING_PREFIX "CallerIDName")),
602 .connectedline_num = pbx_strdup(astman_get_header(m, PARKING_PREFIX "ConnectedLineNum")),
603 .connectedline_name = pbx_strdup(astman_get_header(m, PARKING_PREFIX "ConnectedLineName")),
604 };
605 if (SCCP_VECTOR_APPEND(&pl->slots, new_slot) == 0) {
606 notifyLocked(pl);
607 res = TRUE;
608 }
609 } else {
610 notifyLocked(pl);
611 }
612 } else {
613 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "SCCP: (addSlot) ParkingLot:%s is not being observed\n", parkinglot);
614 }
615 return res;
616 }
617
removeSlot(const char * parkinglot,int slot)618 static int removeSlot(const char *parkinglot, int slot)
619 {
620 pbx_assert(parkinglot != NULL);
621
622 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (removeSlot) removing slot:%d\n", parkinglot, slot);
623 int res = FALSE;
624
625 RAII(sccp_parkinglot_t *, pl, findCreateParkinglot(parkinglot, TRUE), sccp_parkinglot_unlock);
626 if (pl) {
627 if (SCCP_VECTOR_REMOVE_CMP_UNORDERED(&pl->slots, slot, SLOT_CB_CMP, SLOT_CLEANUP) == 0) {
628 notifyLocked(pl);
629 res = TRUE;
630 }
631 } else {
632 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "SCCP: (removeSlot) ParkingLot:%s is not being observed\n", parkinglot);
633 }
634 return !res;
635 }
636
637 /*
638 * Handle Park Feature Button Press
639 * -If we have an active call -> pressing the park feature key, will park that call
640 * -If we are not on an active call:
641 * - If there is 0 parked calls: Display Status Message, "No parked calls'
642 * - If there is 1 parked call: Unpark that call immediatly
643 * - If there is more than 1 parked call: display the visual parking lot representation.
644 */
handleButtonPress(constDevicePtr d,const sccp_buttonconfig_t * const buttonConfig)645 static void handleButtonPress(constDevicePtr d, const sccp_buttonconfig_t * const buttonConfig)
646 {
647 pbx_assert(d != NULL && buttonConfig != NULL);
648 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (handleButtonPress) options:%s, instance:%d\n", d->id, buttonConfig->button.feature.options, buttonConfig->instance);
649
650 AUTO_RELEASE(sccp_channel_t, channel , sccp_device_getActiveChannel(d));
651 if (channel && channel->state != SCCP_CHANNELSTATE_OFFHOOK && channel->state != SCCP_CHANNELSTATE_HOLD) {
652 sccp_channel_park(channel);
653 } else if(!sccp_strlen_zero(buttonConfig->button.feature.options)) {
654 RAII(sccp_parkinglot_t *, pl, findCreateParkinglot(buttonConfig->button.feature.options, TRUE), sccp_parkinglot_unlock);
655 if (pl) {
656 if (SCCP_VECTOR_SIZE(&pl->slots) == 0) {
657 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (handleButtonPress) 0 slot occupied. Show statusBar message\n", buttonConfig->button.feature.options);
658 sccp_dev_displayprinotify(d, SKINNY_DISP_CANNOT_RETRIEVE_PARKED_CALL, SCCP_MESSAGE_PRIORITY_TIMEOUT, 5);
659 } else {
660 if(sccp_strcaseequals(buttonConfig->button.feature.args, "RetrieveSingle") && SCCP_VECTOR_SIZE(&pl->slots) == 1) {
661 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (handleButtonPress) 1 slot occupied -> Unpark Call Immediately\n", buttonConfig->button.feature.options);
662 plslot_t *slot = SCCP_VECTOR_GET_ADDR(&pl->slots, 0);
663 if (slot) {
664 AUTO_RELEASE(sccp_line_t, line , channel ? sccp_line_retain(channel->line) : d->currentLine ? sccp_dev_getActiveLine(d) : sccp_line_find_byid(d, d->defaultLineInstance));
665 AUTO_RELEASE(sccp_channel_t, new_channel,
666 sccp_channel_newcall(line, d, slot->exten, SKINNY_CALLTYPE_OUTBOUND, NULL, NULL)); // implicit release
667 }
668 } else {
669 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (handleButtonPress) multiple slots occupied -> Show Visual ParkingLot\n", buttonConfig->button.feature.options);
670 uint8_t idx = 0;
671 for (idx = 0; idx < SCCP_VECTOR_SIZE(&pl->observers); idx++) {
672 plobserver_t *observer = SCCP_VECTOR_GET_ADDR(&pl->observers, idx);
673 if(observer->device == d && observer->instance == buttonConfig->instance) {
674 __showVisualParkingLot(pl, d, observer);
675 }
676 }
677 }
678 }
679 }
680 }
681 }
682
handleDevice2User(const char * parkinglot,constDevicePtr d,const char * slot_exten,uint8_t instance,uint32_t transactionId)683 static void handleDevice2User(const char *parkinglot, constDevicePtr d, const char *slot_exten, uint8_t instance, uint32_t transactionId)
684 {
685 pbx_assert(d != NULL);
686 sccp_log(DEBUGCAT_PARKINGLOT)(VERBOSE_PREFIX_1 "%s: (handleDevice2Usewr) instance:%d, transactionId:%d\n", d->id, instance, transactionId);
687
688 if (d->dtu_softkey.action && d->dtu_softkey.transactionID == transactionId) {
689 if (sccp_strequals(d->dtu_softkey.action, "DIAL")) {
690 AUTO_RELEASE(sccp_line_t, line , d->currentLine ? sccp_dev_getActiveLine(d) : sccp_line_find_byid(d, d->defaultLineInstance));
691 AUTO_RELEASE(sccp_channel_t, new_channel, sccp_channel_newcall(line, d, slot_exten, SKINNY_CALLTYPE_OUTBOUND, NULL, NULL)); // implicit release
692 } else if (sccp_strequals(d->dtu_softkey.action, "EXIT")) {
693 hideVisualParkingLot(parkinglot, d, instance);
694 }
695 }
696 }
697 /* Assign to interface */
698 const ParkingLotInterface iParkingLot = {
699 .attachObserver = attachObserver,
700 .detachObserver = detachObserver,
701 .addSlot = addSlot,
702 .removeSlot = removeSlot,
703 .handleButtonPress = handleButtonPress,
704 .handleDevice2User = handleDevice2User,
705 .notifyDevice = notifyDevice,
706 };
707 #else
708 const ParkingLotInterface iParkingLot = { 0 };
709 #endif
710