1 /*!
2 * \file sccp_device.c
3 * \brief SCCP Device Class
4 * \author Sergio Chersovani <mlists [at] c-net.it>
5 * \note Reworked, but based on chan_sccp code.
6 * The original chan_sccp driver that was made by Zozo which itself was derived from the chan_skinny driver.
7 * Modified by Jan Czmok and Julien Goodwin
8 * \note This program is free software and may be modified and distributed under the terms of the GNU Public License.
9 * See the LICENSE file at the top of the source tree.
10 *
11 * \remarks
12 * Purpose: SCCP Device
13 * When to use: Only methods directly related to sccp devices should be stored in this source file.
14 * Relations: SCCP Device -> SCCP DeviceLine -> SCCP Line
15 * SCCP Line -> SCCP ButtonConfig -> SCCP Device
16 */
17
18 #include "config.h"
19 #include "common.h"
20 #include "sccp_channel.h"
21 #include "sccp_actions.h"
22 #include "sccp_config.h"
23 #include "sccp_device.h"
24 #include "sccp_feature.h"
25 #include "sccp_line.h"
26 #include "sccp_linedevice.h"
27 #include "sccp_session.h"
28 #include "sccp_indicate.h"
29 #include "sccp_utils.h"
30 #include "sccp_atomic.h"
31 //#include "sccp_devstate.h"
32 #include "sccp_featureParkingLot.h"
33 #include "sccp_labels.h"
34
35 SCCP_FILE_VERSION(__FILE__, "");
36
37 #ifdef HAVE_PBX_ACL_H // AST_SENSE_ALLOW
38 # include <asterisk/acl.h>
39 #endif
40 #if defined(CS_AST_HAS_EVENT) && defined(HAVE_PBX_EVENT_H) // ast_event_subscribe
41 # include <asterisk/event.h>
42 #endif
43 #if HAVE_ICONV
44 #include <iconv.h>
45 int sccp_device_createiconv(devicePtr d);
46 void sccp_device_destroyiconv(devicePtr d);
47 #endif
48
49 int __sccp_device_destroy(const void *ptr);
50 void sccp_device_removeFromGlobals(devicePtr device);
51
52 /*!
53 * \brief Private Device Data Structure
54 */
55 struct sccp_private_device_data {
56 sccp_mutex_t lock;
57
58 sccp_accessorystate_t accessoryStatus[SCCP_ACCESSORY_SENTINEL + 1];
59 sccp_devicestate_t deviceState; /*!< Device State */
60
61 skinny_registrationstate_t registrationState;
62
63 #if HAVE_ICONV
64 iconv_t iconv;
65 sccp_mutex_t iconv_lock;
66 #endif
67 };
68
69 #define sccp_private_lock(x) sccp_mutex_lock(&((struct sccp_private_device_data * const)(x))->lock) /* discard const */
70 #define sccp_private_unlock(x) sccp_mutex_unlock(&((struct sccp_private_device_data * const)(x))->lock) /* discard const */
71
72 /* indicate definition */
73 static void sccp_device_indicate_onhook(constDevicePtr device, const uint8_t lineInstance, uint32_t callid);
74 static void sccp_device_indicate_offhook(constDevicePtr device, sccp_linedevice_t * ld, uint32_t callid);
75 static void sccp_device_indicate_dialing(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_calltype_t calltype, sccp_callinfo_t * const callinfo, char dialedNumber[SCCP_MAX_EXTENSION]);
76 static void sccp_device_indicate_proceed(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_calltype_t calltype, sccp_callinfo_t * const callinfo);
77 static void sccp_device_indicate_connected(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_calltype_t calltype, sccp_callinfo_t * const callinfo);
78 static void sccp_device_old_callhistory(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_callHistoryDisposition_t disposition);
79 static void sccp_device_new_callhistory(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_callHistoryDisposition_t disposition);
80
81 static void sccp_device_indicate_onhook_remote(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid);
82 static void sccp_device_indicate_offhook_remote(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid);
83 static void sccp_device_indicate_connected_remote(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, skinny_callinfo_visibility_t visibility);
84 static void sccp_device_old_indicate_remoteHold(constDevicePtr device, uint8_t lineInstance, uint32_t callid, skinny_callpriority_t callpriority, skinny_callinfo_visibility_t visibility);
85 static void sccp_device_new_indicate_remoteHold(constDevicePtr device, uint8_t lineInstance, uint32_t callid, skinny_callpriority_t callpriority, skinny_callinfo_visibility_t visibility);
86
87 void sccp_dev_postregistration(devicePtr data);
88
89 /* end indicate */
90 static sccp_push_result_t sccp_device_pushURL(constDevicePtr device, const char *url, uint8_t priority, skinny_tone_t tone);
sccp_device_pushURLNotSupported(constDevicePtr device,const char * url,uint8_t priority,skinny_tone_t tone)91 static sccp_push_result_t sccp_device_pushURLNotSupported(constDevicePtr device, const char *url, uint8_t priority, skinny_tone_t tone)
92 {
93 return SCCP_PUSH_RESULT_NOT_SUPPORTED;
94 }
95
96 static sccp_push_result_t sccp_device_pushTextMessage(constDevicePtr device, const char *messageText, const char *from, uint8_t priority, skinny_tone_t tone);
sccp_device_pushTextMessageNotSupported(constDevicePtr device,const char * messageText,const char * from,uint8_t priority,skinny_tone_t tone)97 static sccp_push_result_t sccp_device_pushTextMessageNotSupported(constDevicePtr device, const char *messageText, const char *from, uint8_t priority, skinny_tone_t tone)
98 {
99 return SCCP_PUSH_RESULT_NOT_SUPPORTED;
100 }
101
102 static const struct sccp_device_indication_cb sccp_device_indication_newerDevices = {
103 .remoteHold = sccp_device_new_indicate_remoteHold,
104 .remoteOffhook = sccp_device_indicate_offhook_remote,
105 .remoteOnhook = sccp_device_indicate_onhook_remote,
106 .remoteConnected = sccp_device_indicate_connected_remote,
107 .offhook = sccp_device_indicate_offhook,
108 .onhook = sccp_device_indicate_onhook,
109 .dialing = sccp_device_indicate_dialing,
110 .proceed = sccp_device_indicate_proceed,
111 .connected = sccp_device_indicate_connected,
112 .callhistory = sccp_device_new_callhistory,
113 };
114
115 static const struct sccp_device_indication_cb sccp_device_indication_olderDevices = {
116 .remoteHold = sccp_device_old_indicate_remoteHold,
117 .remoteOffhook = sccp_device_indicate_offhook_remote,
118 .remoteOnhook = sccp_device_indicate_onhook_remote,
119 .remoteConnected = sccp_device_indicate_connected_remote,
120 .offhook = sccp_device_indicate_offhook,
121 .onhook = sccp_device_indicate_onhook,
122 .dialing = sccp_device_indicate_dialing,
123 .proceed = sccp_device_indicate_proceed,
124 .connected = sccp_device_indicate_connected,
125 .callhistory = sccp_device_old_callhistory,
126 };
127
sccp_device_checkACLTrue(constDevicePtr device)128 static boolean_t sccp_device_checkACLTrue(constDevicePtr device)
129 {
130 return TRUE;
131 }
132
sccp_device_trueResult(void)133 static boolean_t sccp_device_trueResult(void)
134 {
135 return TRUE;
136 }
137
sccp_device_falseResult(void)138 static boolean_t sccp_device_falseResult(void)
139 {
140 return FALSE;
141 }
142
sccp_device_setBackgroundImageNotSupported(constDevicePtr device,const char * url,const char * tn)143 static void sccp_device_setBackgroundImageNotSupported(constDevicePtr device, const char *url, const char *tn)
144 {
145 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: does not support Background Image\n", device->id);
146 }
147
sccp_device_setBackgroundImage(constDevicePtr device,const char * url,const char * tn)148 static void sccp_device_setBackgroundImage(constDevicePtr device, const char *url, const char *tn)
149 {
150 if (!url || strncasecmp("http://", url, strlen("http://")) != 0) {
151 pbx_log(LOG_WARNING, "SCCP: '%s' needs to be a valid http url\n", url ? url : "--");
152 return;
153 }
154
155 char xmlStr[StationMaxXMLMessage] = { 0 };
156 unsigned int transactionID = sccp_random();
157
158 snprintf(xmlStr, sizeof(xmlStr), "<setBackground><background><image>%s</image><icon>%s</icon></background></setBackground>\n", url, tn);
159
160 device->protocol->sendUserToDeviceDataVersionMessage(device, APPID_BACKGROUND, 0, 0, transactionID, xmlStr, 0);
161 sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_3 "%s: set background:%s via transaction:%d\n", device->id, url, transactionID);
162 }
163
sccp_device_displayBackgroundImagePreviewNotSupported(constDevicePtr device,const char * url)164 static void sccp_device_displayBackgroundImagePreviewNotSupported(constDevicePtr device, const char *url)
165 {
166 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: does not support Background Image\n", device->id);
167 }
168
sccp_device_displayBackgroundImagePreview(constDevicePtr device,const char * url)169 static void sccp_device_displayBackgroundImagePreview(constDevicePtr device, const char *url)
170 {
171 if (!url || strncmp("http://", url, strlen("http://")) != 0) {
172 pbx_log(LOG_WARNING, "SCCP: '%s' needs to be a valid http url\n", url ? url : "--");
173 return;
174 }
175 char xmlStr[StationMaxXMLMessage] = {0};
176 unsigned int transactionID = sccp_random();
177
178 snprintf(xmlStr, sizeof(xmlStr), "<setBackgroundPreview><image>%s</image></setBackgroundPreview>", url);
179
180 device->protocol->sendUserToDeviceDataVersionMessage(device, APPID_BACKGROUND, 0, 0, transactionID, xmlStr, 0);
181 sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_3 "%s: display background:%s via transaction:%d\n", device->id, url, transactionID);
182 }
183
sccp_device_retrieveDeviceCapabilities(constDevicePtr device)184 static void sccp_device_retrieveDeviceCapabilities(constDevicePtr device)
185 {
186 char *xmlStr = "<getDeviceCaps></getDeviceCaps>";
187 unsigned int transactionID = sccp_random();
188
189 device->protocol->sendUserToDeviceDataVersionMessage(device, APPID_DEVICECAPABILITIES, 1, 0, transactionID, xmlStr, 2);
190 sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_3 "%s: asking for device capabilities via transaction:%d\n", device->id, transactionID);
191 }
192
sccp_device_setRingtoneNotSupported(constDevicePtr device,const char * url)193 static void sccp_device_setRingtoneNotSupported(constDevicePtr device, const char *url)
194 {
195 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: does not support setting ringtone\n", device->id);
196 }
197
sccp_device_setRingtone(constDevicePtr device,const char * url)198 static void sccp_device_setRingtone(constDevicePtr device, const char *url)
199 {
200 if (!url || strncmp("http://", url, strlen("http://")) != 0) {
201 pbx_log(LOG_WARNING, "SCCP: '%s' needs to be a valid http url\n", url ? url : "--");
202 return;
203 }
204
205 char xmlStr[StationMaxXMLMessage] = {0};
206 unsigned int transactionID = sccp_random();
207
208 snprintf(xmlStr, sizeof(xmlStr), "<setRingTone><ringTone>%s</ringTone></setRingTone>", url);
209
210 device->protocol->sendUserToDeviceDataVersionMessage(device, APPID_RINGTONE, 0, 0, transactionID, xmlStr, 0);
211 sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_3 "%s: set ringtone:%s via transaction:%d\n", device->id, url, transactionID);
212 }
213
sccp_device_getDtfmMode(constDevicePtr device)214 static sccp_dtmfmode_t sccp_device_getDtfmMode(constDevicePtr device)
215 {
216 sccp_dtmfmode_t res = device->dtmfmode;
217
218 if (device->dtmfmode == SCCP_DTMFMODE_AUTO) {
219 if (device->device_features.phoneFeatures[2] & SKINNY_PHONE_FEATURES2_RFC2833) {
220 res = SCCP_DTMFMODE_RFC2833;
221 } else {
222 res = SCCP_DTMFMODE_SKINNY;
223 }
224 }
225 return res;
226 }
227
sccp_device_copyStr2Locale_UTF8(constDevicePtr d,char * dst,ICONV_CONST char * src,size_t dst_size)228 static void sccp_device_copyStr2Locale_UTF8(constDevicePtr d, char *dst, ICONV_CONST char *src, size_t dst_size)
229 {
230 if (!dst || !src) {
231 return;
232 }
233 sccp_copy_string(dst, src, dst_size);
234 }
235
236 #if HAVE_ICONV
sccp_device_createiconv(devicePtr d)237 int sccp_device_createiconv(devicePtr d)
238 {
239 d->privateData->iconv = iconv_open(d->iconvcodepage, "UTF-8");
240 if (d->privateData->iconv == (iconv_t) -1) {
241 pbx_log(LOG_ERROR, "SCCP:conversion from 'UTF-8' to '%s' not available.\n", d->iconvcodepage);
242 return 0;
243 }
244 pbx_mutex_init(&d->privateData->iconv_lock);
245 return 1;
246 }
sccp_device_destroyiconv(devicePtr d)247 void sccp_device_destroyiconv(devicePtr d)
248 {
249 if (d->privateData->iconv != (iconv_t) -1) {
250 pbx_mutex_destroy(&d->privateData->iconv_lock);
251 iconv_close(d->privateData->iconv);
252 d->privateData->iconv = (iconv_t) -1;
253 }
254 }
255
sccp_device_convUtf8toLatin1(constDevicePtr d,ICONV_CONST char * utf8str,char * buf,size_t len)256 static boolean_t sccp_device_convUtf8toLatin1(constDevicePtr d, ICONV_CONST char *utf8str, char *buf, size_t len)
257 {
258 if (d->privateData->iconv == (iconv_t) -1) {
259 // fallback to plain string copy
260 sccp_copy_string(buf, utf8str, len);
261 return TRUE;
262 }
263 size_t incount = 0;
264
265 size_t outcount = len;
266 incount = sccp_strlen(utf8str);
267 if (incount) {
268 pbx_mutex_lock(&d->privateData->iconv_lock);
269 if (iconv(d->privateData->iconv, &utf8str, &incount, &buf, &outcount) == (size_t) -1) {
270 if (errno == E2BIG) {
271 pbx_log(LOG_WARNING, "SCCP: Iconv: output buffer too small.\n");
272 } else if (errno == EILSEQ) {
273 pbx_log(LOG_WARNING, "SCCP: Iconv: illegal character.\n");
274 } else if (errno == EINVAL) {
275 pbx_log(LOG_WARNING, "SCCP: Iconv: incomplete character sequence.\n");
276 } else {
277 pbx_log(LOG_WARNING, "SCCP: Iconv: error %d: %s.\n", errno, strerror(errno));
278 }
279 }
280 pbx_mutex_unlock(&d->privateData->iconv_lock);
281 }
282 return TRUE;
283 }
284
sccp_device_copyStr2Locale_Convert(constDevicePtr d,char * dst,ICONV_CONST char * src,size_t dst_size)285 static void sccp_device_copyStr2Locale_Convert(constDevicePtr d, char *dst, ICONV_CONST char *src, size_t dst_size)
286 {
287 if (!dst || !src) {
288 return;
289 }
290 char *buf = (char *)sccp_alloca(dst_size);
291 size_t buf_len = dst_size;
292 memset(buf, 0, dst_size);
293 if (sccp_device_convUtf8toLatin1(d, src, buf, buf_len)) {
294 sccp_copy_string(dst, buf, dst_size);
295 return;
296 }
297 }
298 #endif
299
300 /*
301 static void sccp_device_startStream(const sccp_device_t *device, const char *address, uint32_t port){
302 pbx_str_t *xmlStr = pbx_str_alloca(DEFAULT_PBX_STR_BUFFERSIZE);
303 unsigned int transactionID = sccp_random();
304 pbx_str_append(&xmlStr, 0, "<startMedia>");
305 pbx_str_append(&xmlStr, 0, "<mediaStream>");
306 //pbx_str_append(&xmlStr, 0, "<onStopped></onStopped>"); url
307 pbx_str_append(&xmlStr, 0, "<receiveVolume>50</receiveVolume>"); // 0-100
308 pbx_str_append(&xmlStr, 0, "<type>audio</type>"); // send|receive|sendReceive
309 pbx_str_append(&xmlStr, 0, "<mode>sendReceive</mode>"); // send|receive|sendReceive
310 pbx_str_append(&xmlStr, 0, "<codec>Wideband</codec>"); // "G.711" "G.722" "G.723" "G.728" "G.729" "GSM" "Wideband" "iLBC"
311 pbx_str_append(&xmlStr, 0, "<address>");
312 pbx_str_append(&xmlStr, 0, address);
313 pbx_str_append(&xmlStr, 0, "</address>");
314 pbx_str_append(&xmlStr, 0, "<port>20480</port>");
315 pbx_str_append(&xmlStr, 0, "</mediaStream>");
316 pbx_str_append(&xmlStr, 0, "</startMedia>\n\0");
317
318 device->protocol->sendUserToDeviceDataVersionMessage(device, APPID_STREAM, 0, 0, transactionID, pbx_str_buffer(xmlStr), 0);
319 }
320 */
321
322 /*!
323 * \brief Check device ipaddress against the ip ACL (permit/deny and permithosts entries)
324 */
sccp_device_checkACL(constDevicePtr device)325 static boolean_t sccp_device_checkACL(constDevicePtr device)
326 {
327 struct sockaddr_storage sas = { 0 };
328 boolean_t matchesACL = FALSE;
329
330 if (!device || !device->session) {
331 return FALSE;
332 }
333
334 /* get current socket information */
335 sccp_session_getSas(device->session, &sas);
336
337 /* no permit deny information */
338 if (!device->ha) {
339 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: no deny/permit information for this device, allow all connections\n", device->id);
340 return TRUE;
341 }
342
343 if (sccp_apply_ha(device->ha, &sas) != AST_SENSE_ALLOW) {
344 // checking permithosts
345 struct ast_str *ha_buf = pbx_str_alloca(DEFAULT_PBX_STR_BUFFERSIZE);
346
347 sccp_print_ha(ha_buf, DEFAULT_PBX_STR_BUFFERSIZE, GLOB(ha));
348
349 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: not allowed by deny/permit list (%s). Checking permithost list...\n", device->id, pbx_str_buffer(ha_buf));
350
351 /*! \todo check permithosts with IPv6 */
352 /*
353 struct ast_hostent ahp;
354 struct hostent *hp;
355 sccp_hostname_t *permithost;
356
357 uint8_t i = 0;
358
359 SCCP_LIST_TRAVERSE_SAFE_BEGIN(&device->permithosts, permithost, list) {
360 if ((hp = pbx_gethostbyname(permithost->name, &ahp))) {
361 for (i = 0; NULL != hp->h_addr_list[i]; i++) { // walk resulting ip address
362 if (sin.sin_addr.s_addr == (*(struct in_addr *) hp->h_addr_list[i]).s_addr) {
363 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: permithost = %s match found.\n", device->id, permithost->name);
364 matchesACL = TRUE;
365 continue;
366 }
367 }
368 } else {
369 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Invalid address resolution for permithost = %s (skipping permithost).\n", device->id, permithost->name);
370 }
371 }
372 SCCP_LIST_TRAVERSE_SAFE_END;
373 */
374 } else {
375 matchesACL = TRUE;
376 }
377 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: checkACL returning %s\n", device->id, matchesACL ? "TRUE" : "FALSE");
378 return matchesACL;
379 }
380
381 /*!
382 * \brief run before reload is start on devices
383 * \note See \ref sccp_config_reload
384 *
385 * \callgraph
386 * \callergraph
387 */
sccp_device_pre_reload(void)388 void sccp_device_pre_reload(void)
389 {
390 sccp_device_t *d = NULL;
391 sccp_buttonconfig_t *config = NULL;
392
393 SCCP_RWLIST_RDLOCK(&GLOB(devices));
394 SCCP_RWLIST_TRAVERSE(&GLOB(devices), d, list) {
395 sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Setting Device to Pending Delete=1\n", d->id);
396 #ifdef CS_SCCP_REALTIME
397 if (!d->realtime) { /* don't want to reset realtime devices, if they have not changed */
398 d->pendingDelete = 1;
399 }
400 #endif
401 d->pendingUpdate = 0;
402
403 /* clear softkeyset */
404 d->softkeyset = NULL;
405 d->softKeyConfiguration.modes = NULL;
406 d->softKeyConfiguration.size = 0;
407 d->isAnonymous=FALSE;
408
409 SCCP_LIST_LOCK(&d->buttonconfig);
410 SCCP_LIST_TRAVERSE(&d->buttonconfig, config, list) {
411 sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_4 "%s: Setting Button at Index:%d to pendingDelete\n", d->id, config->index);
412 config->pendingDelete = 1;
413 config->pendingUpdate = 0;
414 }
415 SCCP_LIST_UNLOCK(&d->buttonconfig);
416 d->softkeyset = NULL;
417 d->softKeyConfiguration.modes = 0;
418 d->softKeyConfiguration.size = 0;
419 }
420 SCCP_RWLIST_UNLOCK(&GLOB(devices));
421 }
422
423 /*!
424 * \brief Check Device Update Status
425 * \note See \ref sccp_config_reload
426 * \param device SCCP Device
427 * \return Result as Boolean
428 *
429 * \callgraph
430 * \callergraph
431 */
sccp_device_check_update(devicePtr device)432 boolean_t sccp_device_check_update(devicePtr device)
433 {
434 AUTO_RELEASE(sccp_device_t, d , device ? sccp_device_retain(device) : NULL);
435 boolean_t res = FALSE;
436
437 if (d) {
438 //sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_2 "%s (check_update) pendingUpdate: %s, pendingDelete: %s\n", d->id, d->pendingUpdate ? "TRUE" : "FALSE", d->pendingDelete ? "TRUE" : "FALSE");
439 if ((d->pendingUpdate || d->pendingDelete)) {
440 do {
441 if (sccp_device_numberOfChannels(d) > 0) {
442 //sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "device: %s check_update, openchannel: %d -> device restart pending.\n", d->id, sccp_device_numberOfChannels(d));
443 break;
444 }
445
446 sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_1 "Device %s needs to be reset because of a change in sccp.conf (Update:%d, Delete:%d)\n", d->id, d->pendingUpdate, d->pendingDelete);
447
448 d->pendingUpdate = 0;
449 sccp_dev_clean_restart(d, (d->pendingDelete) ? TRUE : FALSE);
450 res = TRUE;
451 } while (0);
452 }
453 }
454 return res;
455 }
456
457 /*!
458 * \brief run after the new device config is loaded during the reload process
459 * \note See \ref sccp_config_reload
460 *
461 * \callgraph
462 * \callergraph
463 *
464 */
sccp_device_post_reload(void)465 void sccp_device_post_reload(void)
466 {
467 sccp_device_t *d = NULL;
468 sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_1 "SCCP: (post_reload)\n");
469
470 SCCP_RWLIST_TRAVERSE_SAFE_BEGIN(&GLOB(devices), d, list) {
471 if (!d->pendingDelete && !d->pendingUpdate) {
472 continue;
473 }
474 /* Because of the previous check, the only reason that the device hasn't
475 * been updated will be because it is currently engaged in a call.
476 */
477 if (!sccp_device_check_update(d)) {
478 sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "Device %s will receive reset after current call is completed\n", d->id);
479 }
480 // make sure preferences only contains the codecs that this device is capable of
481 sccp_codec_reduceSet(d->preferences.audio , d->capabilities.audio);
482 sccp_codec_reduceSet(d->preferences.video , d->capabilities.video);
483 /* should we re-check the device after hangup ? */
484 }
485 SCCP_LIST_TRAVERSE_SAFE_END;
486 }
487
488 /* ====================================================================================================== start getters / setters for privateData */
sccp_device_getAccessoryStatus(constDevicePtr d,const sccp_accessory_t accessory)489 const sccp_accessorystate_t sccp_device_getAccessoryStatus(constDevicePtr d, const sccp_accessory_t accessory)
490 {
491 pbx_assert(d != NULL && d->privateData != NULL);
492 sccp_private_lock(d->privateData);
493 sccp_accessorystate_t accessoryStatus = d->privateData->accessoryStatus[accessory];
494 sccp_private_unlock(d->privateData);
495 return accessoryStatus;
496 }
497
sccp_device_getActiveAccessory(constDevicePtr d)498 const sccp_accessory_t sccp_device_getActiveAccessory(constDevicePtr d)
499 {
500 sccp_accessory_t res = SCCP_ACCESSORY_NONE;
501 pbx_assert(d != NULL && d->privateData != NULL);
502 sccp_accessory_t accessory = SCCP_ACCESSORY_NONE;
503 sccp_private_lock(d->privateData);
504 for (accessory = SCCP_ACCESSORY_NONE ; accessory < SCCP_ACCESSORY_SENTINEL; enum_incr(accessory)) {
505 if (d->privateData->accessoryStatus[accessory] == SCCP_ACCESSORYSTATE_OFFHOOK) {
506 res = accessory;
507 break;
508 }
509 }
510 sccp_private_unlock(d->privateData);
511 return res;
512 }
513
sccp_device_setAccessoryStatus(constDevicePtr d,const sccp_accessory_t accessory,const sccp_accessorystate_t state)514 int sccp_device_setAccessoryStatus(constDevicePtr d, const sccp_accessory_t accessory, const sccp_accessorystate_t state)
515 {
516 pbx_assert(d != NULL && d->privateData != NULL);
517 pbx_assert(accessory > SCCP_ACCESSORY_NONE && accessory < SCCP_ACCESSORY_SENTINEL && state > SCCP_ACCESSORYSTATE_NONE && state < SCCP_ACCESSORYSTATE_SENTINEL);
518 int changed = 0;
519
520 sccp_private_lock(d->privateData);
521 if (state != d->privateData->accessoryStatus[accessory]) {
522 d->privateData->accessoryStatus[accessory] = state;
523 if (state == SCCP_ACCESSORYSTATE_ONHOOK) {
524 sccp_dev_cleardisplaynotify(d);
525 sccp_dev_check_displayprompt(d);
526 }
527 changed=1;
528 }
529 sccp_private_unlock(d->privateData);
530 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Accessory '%s' is '%s'\n", d->id, sccp_accessory2str(accessory), sccp_accessorystate2str(state));
531 return changed;
532 }
533
sccp_device_getDeviceState(constDevicePtr d)534 const sccp_devicestate_t sccp_device_getDeviceState(constDevicePtr d)
535 {
536 pbx_assert(d != NULL && d->privateData != NULL);
537
538 sccp_devicestate_t state = SCCP_DEVICESTATE_SENTINEL;
539
540 sccp_private_lock(d->privateData);
541 state = d->privateData->deviceState;
542 sccp_private_unlock(d->privateData);
543
544 return state;
545 }
546
sccp_device_setDeviceState(constDevicePtr d,const sccp_devicestate_t state)547 int sccp_device_setDeviceState(constDevicePtr d, const sccp_devicestate_t state)
548 {
549 pbx_assert(d != NULL && d->privateData != NULL);
550 int changed = 0;
551
552 sccp_private_lock(d->privateData);
553 if (state != d->privateData->deviceState) {
554 d->privateData->deviceState = state;
555 changed=1;
556 }
557 sccp_private_unlock(d->privateData);
558
559 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Device State is '%s'\n", d->id, sccp_devicestate2str(state));
560 return changed;
561 }
562
sccp_device_getRegistrationState(constDevicePtr d)563 const skinny_registrationstate_t sccp_device_getRegistrationState(constDevicePtr d)
564 {
565 pbx_assert(d != NULL && d->privateData != NULL);
566
567 skinny_registrationstate_t state = SKINNY_REGISTRATIONSTATE_SENTINEL;
568
569 sccp_private_lock(d->privateData);
570 state = d->privateData->registrationState;
571 sccp_private_unlock(d->privateData);
572
573 return state;
574 }
575
sccp_device_setRegistrationState(constDevicePtr d,const skinny_registrationstate_t state)576 int sccp_device_setRegistrationState(constDevicePtr d, const skinny_registrationstate_t state)
577 {
578 pbx_assert(d != NULL);
579 if (isPointerDead(d) || !d->privateData) {
580 return 0;;
581 }
582
583 int changed = 0;
584
585 if (!isPointerDead(d->privateData)) {
586 sccp_private_lock(d->privateData);
587 if (state != d->privateData->registrationState) {
588 d->privateData->registrationState = state;
589 changed=1;
590 }
591 sccp_private_unlock(d->privateData);
592 }
593
594 #ifdef CS_AST_HAS_STASIS_ENDPOINT
595 if (iPbx.endpoint_online && iPbx.endpoint_offline) {
596 if (SKINNY_DEVICE_RS_OK == state) {
597 struct sockaddr_storage ourip = { 0 };
598 sccp_session_getOurIP(d->session, &ourip, 0);
599 iPbx.endpoint_online(d->endpoint, sccp_netsock_stringify(&ourip));
600 } else if (SKINNY_DEVICE_RS_NONE == state) {
601 iPbx.endpoint_offline(d->endpoint, "Unreachable");
602 } else {
603 iPbx.endpoint_offline(d->endpoint, "expired");
604 }
605 }
606 #endif
607
608 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Registration State is '%s'\n", d->id, skinny_registrationstate2str(state));
609 return changed;
610 }
611
612 /* ======================================================================================================== end getters / setters for privateData */
613
614 /*!
615 * \brief create a device and adding default values.
616 * \return retained device with default/global values
617 *
618 * \callgraph
619 * \callergraph
620 */
sccp_device_create(const char * id)621 devicePtr sccp_device_create(const char * id)
622 {
623 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "SCCP: Create Device\n");
624 struct sccp_private_device_data * private_data = NULL;
625
626 sccp_device_t *d = (sccp_device_t *) sccp_refcount_object_alloc(sizeof(sccp_device_t), SCCP_REF_DEVICE, id, __sccp_device_destroy);
627
628 if (!d) {
629 pbx_log(LOG_ERROR, "Unable to allocate memory for a device\n");
630 return NULL;
631 }
632
633 //memset(d, 0, sizeof(sccp_device_t));
634 private_data = (sccp_private_device_data_t *)sccp_calloc(sizeof *private_data, 1);
635 if (!private_data) {
636 pbx_log(LOG_ERROR, "%s: No memory to allocate device private data\n", id);
637 sccp_device_release(&d); /* explicit release */
638 return NULL;
639 }
640 d->privateData = private_data;
641 d->privateData->registrationState = SKINNY_DEVICE_RS_NONE;
642 sccp_mutex_init(&d->privateData->lock);
643
644 sccp_copy_string(d->id, id, sizeof(d->id));
645 SCCP_LIST_HEAD_INIT(&d->buttonconfig);
646 SCCP_LIST_HEAD_INIT(&d->selectedChannels);
647 SCCP_LIST_HEAD_INIT(&d->addons);
648 #ifdef CS_AST_HAS_STASIS_ENDPOINT
649 if (iPbx.endpoint_create) {
650 d->endpoint = iPbx.endpoint_create("SCCP", id);
651 }
652 #endif
653 memset(&d->softKeyConfiguration.activeMask, 0xFF, sizeof d->softKeyConfiguration.activeMask);
654 memset(d->call_statistics, 0, ((sizeof *d->call_statistics) * 2));
655
656 // d->softKeyConfiguration.modes = (softkey_modes *) SoftKeyModes;
657 // d->softKeyConfiguration.size = ARRAY_LEN(SoftKeyModes);
658 sccp_device_setDeviceState(d, SCCP_DEVICESTATE_ONHOOK);
659 d->postregistration_thread = AST_PTHREADT_STOP;
660 d->defaultLineInstance = SCCP_FIRST_LINEINSTANCE;
661
662 // set minimum protocol levels
663 d->protocolversion = SCCP_DRIVER_SUPPORTED_PROTOCOL_LOW;
664 d->protocol = sccp_protocol_getDeviceProtocol(d, SCCP_PROTOCOL);
665
666 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "Init MessageStack\n");
667
668 /* initialize messageStack */
669 #ifndef SCCP_ATOMIC
670 pbx_mutex_init(&d->messageStack.lock);
671 sccp_mutex_lock(&d->messageStack.lock);
672 #endif
673 for(uint8_t i = 0; i < ARRAY_LEN(d->messageStack.messages); i++) {
674 d->messageStack.messages[i] = NULL;
675 }
676 #ifndef SCCP_ATOMIC
677 sccp_mutex_unlock(&d->messageStack.lock);
678 #endif
679 #if HAVE_ICONV
680 d->privateData->iconv = (iconv_t) -1;
681 #endif
682
683 // /* disable videomode and join softkey for all softkeysets */
684 /*
685 for (i = 0; i < KEYMODE_ONHOOKSTEALABLE; i++) {
686 sccp_softkey_setSoftkeyState(d, i, SKINNY_LBL_VIDEO_MODE, FALSE);
687 sccp_softkey_setSoftkeyState(d, i, SKINNY_LBL_JOIN, FALSE);
688 }
689 */
690
691 d->pushURL = sccp_device_pushURLNotSupported;
692 d->pushTextMessage = sccp_device_pushTextMessageNotSupported;
693 d->checkACL = sccp_device_checkACL;
694 d->useHookFlash = sccp_device_falseResult;
695 d->hasDisplayPrompt = sccp_device_trueResult;
696 d->hasLabelLimitedDisplayPrompt = sccp_device_falseResult;
697 d->hasEnhancedIconMenuSupport = sccp_device_falseResult;
698 d->hasMWILight = sccp_device_trueResult;
699 d->setBackgroundImage = sccp_device_setBackgroundImageNotSupported;
700 d->displayBackgroundImagePreview = sccp_device_displayBackgroundImagePreviewNotSupported;
701 d->retrieveDeviceCapabilities = sccp_device_retrieveDeviceCapabilities;
702 d->setRingTone = sccp_device_setRingtoneNotSupported;
703 d->getDtmfMode = sccp_device_getDtfmMode;
704 d->copyStr2Locale = sccp_device_copyStr2Locale_UTF8;
705 d->keepalive = d->keepaliveinterval = d->keepalive ? d->keepalive : GLOB(keepalive);
706 d->mwiUpdateRequired = TRUE;
707
708 d->pendingUpdate = 0;
709 d->pendingDelete = 0;
710 return d;
711 }
712
713 /*!
714 * \brief create an anonymous device and adding default values.
715 * \return retained device with default/global values
716 *
717 * \callgraph
718 * \callergraph
719 */
sccp_device_createAnonymous(const char * name)720 devicePtr sccp_device_createAnonymous(const char * name)
721 {
722 sccp_device_t *d = sccp_device_create(name);
723
724 if (!d) {
725 pbx_log(LOG_ERROR, "SCCP: sccp_device_create(%s) failed", name);
726 return NULL;
727 }
728
729 d->realtime = TRUE;
730 d->isAnonymous = TRUE;
731 d->checkACL = sccp_device_checkACLTrue;
732 return d;
733 }
734
__saveLastDialedNumberToDatabase(constDevicePtr device)735 static void __saveLastDialedNumberToDatabase(constDevicePtr device)
736 {
737 char family[25];
738 snprintf(family, sizeof(family), "SCCP/%s", device->id);
739 if (!sccp_strlen_zero(device->redialInformation.number)) {
740 char buffer[SCCP_MAX_EXTENSION+16] = "\0";
741 snprintf (buffer, sizeof(buffer), "%s;lineInstance=%d", device->redialInformation.number, device->redialInformation.lineInstance);
742 iPbx.feature_addToDatabase(family, "lastDialedNumber", buffer);
743 } else {
744 iPbx.feature_removeFromDatabase(family, "lastDialedNumber");
745 }
746 }
747
sccp_device_setLastNumberDialed(devicePtr device,const char * lastNumberDialed,const sccp_linedevice_t * ld)748 void sccp_device_setLastNumberDialed(devicePtr device, const char * lastNumberDialed, const sccp_linedevice_t * ld)
749 {
750 boolean_t ResetNoneLineInstance = FALSE;
751 boolean_t redial_active = FALSE;
752 boolean_t update_database = FALSE;
753
754 if (device->useRedialMenu) {
755 return;
756 }
757
758 if (lastNumberDialed && !sccp_strlen_zero(lastNumberDialed)) {
759 if (sccp_strlen_zero(device->redialInformation.number)) {
760 ResetNoneLineInstance = TRUE;
761 }
762 if(!sccp_strequals(device->redialInformation.number, lastNumberDialed) || device->redialInformation.lineInstance != ld->lineInstance) {
763 sccp_log(DEBUGCAT_DEVICE) (VERBOSE_PREFIX_3 "%s: Update last number dialed to %s.\n", DEV_ID_LOG(device), lastNumberDialed);
764 sccp_copy_string(device->redialInformation.number, lastNumberDialed, sizeof(device->redialInformation.number));
765 device->redialInformation.lineInstance = ld->lineInstance;
766 update_database = TRUE;
767 }
768 redial_active = TRUE;
769 } else {
770 if (!sccp_strlen_zero(device->redialInformation.number) || device->redialInformation.lineInstance != 0) {
771 sccp_log(DEBUGCAT_DEVICE) (VERBOSE_PREFIX_3 "%s: Clear last number dialed.\n", DEV_ID_LOG(device));
772 sccp_copy_string(device->redialInformation.number, "", sizeof(device->redialInformation.number));
773 device->redialInformation.lineInstance = 0;
774 update_database = TRUE;
775 }
776 }
777 sccp_softkey_setSoftkeyState(device, KEYMODE_ONHOOK, SKINNY_LBL_REDIAL, redial_active);
778 sccp_softkey_setSoftkeyState(device, KEYMODE_OFFHOOK, SKINNY_LBL_REDIAL, redial_active);
779 sccp_softkey_setSoftkeyState(device, KEYMODE_OFFHOOKFEAT, SKINNY_LBL_REDIAL, redial_active);
780 sccp_softkey_setSoftkeyState(device, KEYMODE_ONHOOKSTEALABLE, SKINNY_LBL_REDIAL, redial_active);
781 if (ResetNoneLineInstance) {
782 sccp_dev_set_keyset(device, 0, 0, KEYMODE_ONHOOK);
783 }
784 if (update_database) {
785 __saveLastDialedNumberToDatabase(device);
786 }
787 }
788
789 /*!
790 * \brief set type of Indicate protocol by device type
791 */
sccp_device_preregistration(devicePtr device)792 void sccp_device_preregistration(devicePtr device)
793 {
794 if (!device) {
795 return;
796 }
797 /*! \todo use device->device_features to detect devices capabilities, instead of hardcoded list of devices */
798 switch (device->skinny_type) {
799 // case SKINNY_DEVICETYPE_CISCO7912:
800 // case SKINNY_DEVICETYPE_CISCO7920:
801 // case SKINNY_DEVICETYPE_CISCO7935:
802 // case SKINNY_DEVICETYPE_CISCO7936:
803 // case SKINNY_DEVICETYPE_CISCO7937:
804 // case SKINNY_DEVICETYPE_CISCO7940:
805 // case SKINNY_DEVICETYPE_NOKIA_E_SERIES:
806 // case SKINNY_DEVICETYPE_NOKIA_ICC:
807 // case SKINNY_DEVICETYPE_SPA_303G:
808 // case SKINNY_DEVICETYPE_SPA_502G:
809 // case SKINNY_DEVICETYPE_SPA_504G:
810 // case SKINNY_DEVICETYPE_SPA_508G:
811 // case SKINNY_DEVICETYPE_SPA_509G:
812 // case SKINNY_DEVICETYPE_SPA_512G:
813 // case SKINNY_DEVICETYPE_SPA_514G:
814 // case SKINNY_DEVICETYPE_SPA_521S:
815 // case SKINNY_DEVICETYPE_SPA_524SG:
816 // case SKINNY_DEVICETYPE_SPA_525G:
817 // case SKINNY_DEVICETYPE_SPA_525G2:
818 case SKINNY_DEVICETYPE_CISCO7906: /* added since 2019-10-18 */
819 case SKINNY_DEVICETYPE_CISCO7911: /* added since 2019-10-18 */
820 case SKINNY_DEVICETYPE_CISCO7931: /* added since 2019-10-18 */
821 case SKINNY_DEVICETYPE_CISCO7941:
822 case SKINNY_DEVICETYPE_CISCO7941GE:
823 case SKINNY_DEVICETYPE_CISCO7942:
824 case SKINNY_DEVICETYPE_CISCO7945:
825 case SKINNY_DEVICETYPE_CISCO7921:
826 case SKINNY_DEVICETYPE_CISCO7925:
827 case SKINNY_DEVICETYPE_CISCO7926:
828 case SKINNY_DEVICETYPE_CISCO7961:
829 case SKINNY_DEVICETYPE_CISCO7961GE:
830 case SKINNY_DEVICETYPE_CISCO7962:
831 case SKINNY_DEVICETYPE_CISCO7965:
832 case SKINNY_DEVICETYPE_CISCO7970:
833 case SKINNY_DEVICETYPE_CISCO7971:
834 case SKINNY_DEVICETYPE_CISCO7975:
835 case SKINNY_DEVICETYPE_CISCO7985:
836 case SKINNY_DEVICETYPE_CISCO_IP_COMMUNICATOR:
837 case SKINNY_DEVICETYPE_CISCO6901: /* added since 2019-10-18 */
838 case SKINNY_DEVICETYPE_CISCO6911: /* added since 2019-10-18 */
839 case SKINNY_DEVICETYPE_CISCO6921: /* added since 2019-10-18 */
840 case SKINNY_DEVICETYPE_CISCO6941: /* added since 2019-10-18 */
841 case SKINNY_DEVICETYPE_CISCO6945: /* added since 2019-10-18 */
842 case SKINNY_DEVICETYPE_CISCO6961: /* added since 2019-10-18 */
843 case SKINNY_DEVICETYPE_CISCO8941: /* added since 2019-10-18 */
844 case SKINNY_DEVICETYPE_CISCO8945: /* added since 2019-10-18 */
845 device->indicate = &sccp_device_indication_newerDevices;
846 break;
847 default:
848 device->indicate = &sccp_device_indication_olderDevices;
849 break;
850 }
851 #if HAVE_ICONV
852 if (!(device->device_features.phoneFeatures[1] & SKINNY_PHONE_FEATURES1_UTF8)) {
853 sccp_device_createiconv(device);
854 device->copyStr2Locale = sccp_device_copyStr2Locale_Convert;
855 }
856 #endif
857 }
858
859 /*!
860 * \brief Add a device to the global sccp_device list
861 * \param device SCCP Device
862 * \return SCCP Device
863 *
864 * \note needs to be called with a retained device
865 * \note adds a retained device to the list (refcount + 1)
866 */
sccp_device_addToGlobals(constDevicePtr device)867 void sccp_device_addToGlobals(constDevicePtr device)
868 {
869 if (!device) {
870 pbx_log(LOG_ERROR, "Adding null to the global device list is not allowed!\n");
871 return;
872 }
873 sccp_device_t *d = sccp_device_retain(device);
874 if (d) {
875 SCCP_RWLIST_WRLOCK(&GLOB(devices));
876 SCCP_RWLIST_INSERT_SORTALPHA(&GLOB(devices), d, list, id);
877 SCCP_RWLIST_UNLOCK(&GLOB(devices));
878 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "Added device '%s' to Glob(devices)\n", d->id);
879 }
880 }
881
882 /*!
883 * \brief Removes a device from the global sccp_device list
884 * \param device SCCP Device
885 * \return device or NULL
886 *
887 * \note needs to be called with a retained device
888 * \note removes the retained device withing the list (refcount - 1)
889 */
sccp_device_removeFromGlobals(devicePtr device)890 void sccp_device_removeFromGlobals(devicePtr device)
891 {
892 if (!device) {
893 pbx_log(LOG_ERROR, "Removing null from the global device list is not allowed!\n");
894 return;
895 }
896 sccp_device_t * d = NULL;
897
898 SCCP_RWLIST_WRLOCK(&GLOB(devices));
899 d = SCCP_RWLIST_REMOVE(&GLOB(devices), device, list);
900 SCCP_RWLIST_UNLOCK(&GLOB(devices));
901
902 if(d) {
903 sccp_log((DEBUGCAT_CORE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "Removed device '%s' from Glob(devices)\n", DEV_ID_LOG(device));
904 sccp_device_release(&d); /* explicit release of device after removing from list */
905 }
906 }
907
sccp_addon_build_buttontemplate(constDevicePtr d,sccp_addon_t * addon,btnlist * btn,uint8_t btn_index)908 static uint8_t sccp_addon_build_buttontemplate(constDevicePtr d, sccp_addon_t *addon, btnlist * btn, uint8_t btn_index)
909 {
910 uint8_t i = 0;
911 uint8_t start_point = btn_index;
912 skinny_devicetype_t type = addon->type;
913
914 sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_BUTTONTEMPLATE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Building button template %s(%d)\n", d->id, skinny_devicetype2str(type), type);
915
916 switch (type) {
917 case SKINNY_DEVICETYPE_CISCO_ADDON_7914:
918 for (i = 0; i < 14; i++) {
919 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
920 }
921 break;
922 case SKINNY_DEVICETYPE_CISCO_ADDON_7915_12BUTTON:
923 case SKINNY_DEVICETYPE_CISCO_ADDON_7916_12BUTTON:
924 for (i = 0; i < 12; i++) {
925 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
926 }
927 break;
928 case SKINNY_DEVICETYPE_CISCO_ADDON_7915_24BUTTON:
929 case SKINNY_DEVICETYPE_CISCO_ADDON_7916_24BUTTON:
930 for (i = 0; i < 24; i++) {
931 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
932 }
933 break;
934 case SKINNY_DEVICETYPE_CISCO_ADDON_SPA500S:
935 case SKINNY_DEVICETYPE_CISCO_ADDON_SPA500DS:
936 case SKINNY_DEVICETYPE_CISCO_ADDON_SPA932DS:
937 for (i = 0; i < 32; i++) {
938 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
939 }
940 break;
941 default:
942 pbx_log(LOG_WARNING, "%s: Unknown addon device type '%d' found.\n", d->id, type);
943 break;
944 }
945 for (i = start_point; i < btn_index; i++) {
946 btn[i].devicetype = type;
947 }
948
949 sccp_log(DEBUGCAT_DEVICE)(VERBOSE_PREFIX_3 "%s: Allocated %d Addon Buttons.\n", d->id, btn_index - start_point);
950 return btn_index;
951 }
952
953 /*!
954 * \brief Create a template of Buttons as Definition for a Phonetype (d->skinny_type)
955 * \param d device
956 * \param btn buttonlist
957 */
sccp_dev_build_buttontemplate(devicePtr d,btnlist * btn)958 uint8_t sccp_dev_build_buttontemplate(devicePtr d, btnlist * btn)
959 {
960 uint8_t i = 0;
961 uint8_t btn_index=0;
962 skinny_devicetype_t type = d->skinny_type;
963
964 sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_BUTTONTEMPLATE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Building button template %s(%d), user config %s\n", d->id, skinny_devicetype2str(type), type, d->config_type);
965
966 switch (type) {
967 case SKINNY_DEVICETYPE_30SPPLUS:
968 case SKINNY_DEVICETYPE_30VIP:
969 /* 13 rows, 2 columns */
970 for (i = 0; i < 4; i++) {
971 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
972 }
973 /* Column 2 */
974 btn[btn_index++].type = SKINNY_BUTTONTYPE_LASTNUMBERREDIAL;
975 btn[btn_index++].type = SKINNY_BUTTONTYPE_VOICEMAIL;
976 btn[btn_index++].type = SKINNY_BUTTONTYPE_CALLPARK;
977 btn[btn_index++].type = SKINNY_BUTTONTYPE_FORWARDALL;
978 btn[btn_index++].type = SKINNY_BUTTONTYPE_CONFERENCE;
979 for (i = 0; i < 4; i++) {
980 btn[btn_index++].type = SKINNY_BUTTONTYPE_UNDEFINED;
981 }
982 for (i = 0; i < 13; i++) {
983 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
984 }
985 d->hasMWILight = sccp_device_falseResult;
986 break;
987 case SKINNY_DEVICETYPE_12SPPLUS:
988 case SKINNY_DEVICETYPE_12SP:
989 case SKINNY_DEVICETYPE_12:
990 /* 6 rows, 2 columns */
991 for (i = 0; i < 2; i++) {
992 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
993 }
994 for (i = 0; i < 4; i++) {
995 btn[btn_index++].type = SCCP_BUTTONTYPE_SPEEDDIAL;
996 }
997 btn[btn_index++].type = SKINNY_BUTTONTYPE_HOLD;
998 btn[btn_index++].type = SKINNY_BUTTONTYPE_LASTNUMBERREDIAL;
999 btn[btn_index++].type = SKINNY_BUTTONTYPE_TRANSFER;
1000 btn[btn_index++].type = SKINNY_BUTTONTYPE_FORWARDALL;
1001 btn[btn_index++].type = SKINNY_BUTTONTYPE_CALLPARK;
1002 btn[btn_index++].type = SKINNY_BUTTONTYPE_VOICEMAIL;
1003 d->hasMWILight = sccp_device_falseResult;
1004 break;
1005 case SKINNY_DEVICETYPE_CISCO7902:
1006 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1007 btn[btn_index++].type = SKINNY_BUTTONTYPE_HOLD;
1008 btn[btn_index++].type = SKINNY_BUTTONTYPE_TRANSFER;
1009 btn[btn_index++].type = SKINNY_BUTTONTYPE_DISPLAY;
1010 btn[btn_index++].type = SKINNY_BUTTONTYPE_VOICEMAIL;
1011 btn[btn_index++].type = SKINNY_BUTTONTYPE_CONFERENCE;
1012 btn[btn_index++].type = SKINNY_BUTTONTYPE_FORWARDALL;
1013 for (i = 0; i < 4; i++) {
1014 btn[btn_index++].type = SCCP_BUTTONTYPE_SPEEDDIAL;
1015 }
1016 btn[btn_index++].type = SKINNY_BUTTONTYPE_LASTNUMBERREDIAL;
1017 break;
1018 case SKINNY_DEVICETYPE_CISCO7910:
1019 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1020 btn[btn_index++].type = SKINNY_BUTTONTYPE_HOLD;
1021 btn[btn_index++].type = SKINNY_BUTTONTYPE_TRANSFER;
1022 btn[btn_index++].type = SKINNY_BUTTONTYPE_DISPLAY;
1023 btn[btn_index++].type = SKINNY_BUTTONTYPE_VOICEMAIL;
1024 btn[btn_index++].type = SKINNY_BUTTONTYPE_CONFERENCE;
1025 btn[btn_index++].type = SKINNY_BUTTONTYPE_FORWARDALL;
1026 for (i = 0; i < 2; i++) {
1027 btn[btn_index++].type = SCCP_BUTTONTYPE_SPEEDDIAL;
1028 }
1029 btn[btn_index++].type = SKINNY_BUTTONTYPE_LASTNUMBERREDIAL;
1030 break;
1031 case SKINNY_DEVICETYPE_CISCO7906:
1032 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1033 btn[btn_index++].type = SKINNY_BUTTONTYPE_HOLD;
1034 for (i = 0; i < 9; i++) {
1035 btn[btn_index++].type = SCCP_BUTTONTYPE_SPEEDDIAL;
1036 }
1037 d->useHookFlash = sccp_device_trueResult;
1038 break;
1039 case SKINNY_DEVICETYPE_CISCO7911:
1040 case SKINNY_DEVICETYPE_CISCO7905:
1041 case SKINNY_DEVICETYPE_CISCO7912:
1042 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1043 btn[btn_index++].type = SKINNY_BUTTONTYPE_HOLD;
1044 for (i = 0; i < 9; i++) {
1045 btn[btn_index++].type = SCCP_BUTTONTYPE_SPEEDDIAL;
1046 }
1047 d->hasEnhancedIconMenuSupport = sccp_device_trueResult;
1048 d->useHookFlash = sccp_device_trueResult;
1049 break;
1050 case SKINNY_DEVICETYPE_CISCO7920:
1051 // only four lines are displayed, but scrolling down shows two additional ones
1052 for(i = 0; i < 6; i++) {
1053 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1054 }
1055 break;
1056 case SKINNY_DEVICETYPE_CISCO7931:
1057 for (i = 0; i < 20; i++) {
1058 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1059 }
1060 btn[btn_index].type = SKINNY_BUTTONTYPE_MESSAGES; btn[btn_index].instance = 21; btn_index++;
1061 btn[btn_index].type = SKINNY_BUTTONTYPE_DIRECTORY; btn[btn_index].instance = 22; btn_index++;
1062 btn[btn_index].type = SKINNY_BUTTONTYPE_HEADSET; btn[btn_index].instance = 23; btn_index++;
1063 btn[btn_index].type = SKINNY_BUTTONTYPE_APPLICATION; btn[btn_index].instance = 24; btn_index++;
1064 d->hasEnhancedIconMenuSupport = sccp_device_trueResult;
1065 break;
1066 case SKINNY_DEVICETYPE_CISCO7935:
1067 case SKINNY_DEVICETYPE_CISCO7936:
1068 case SKINNY_DEVICETYPE_CISCO7937:
1069 for (i = 0; i < 2; i++) {
1070 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1071 }
1072 break;
1073 case SKINNY_DEVICETYPE_CISCO7921:
1074 case SKINNY_DEVICETYPE_CISCO7925:
1075 case SKINNY_DEVICETYPE_CISCO7926:
1076 for (i = 0; i < 6; i++) {
1077 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1078 }
1079 break;
1080 case SKINNY_DEVICETYPE_CISCO7940:
1081 d->pushTextMessage = sccp_device_pushTextMessage;
1082 d->pushURL = sccp_device_pushURL;
1083 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1084 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1085 break;
1086 case SKINNY_DEVICETYPE_CISCO7960:
1087 d->pushTextMessage = sccp_device_pushTextMessage;
1088 d->pushURL = sccp_device_pushURL;
1089 for (i = 6; i > 0; i--) {
1090 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1091 }
1092 break;
1093 case SKINNY_DEVICETYPE_CISCO7941:
1094 case SKINNY_DEVICETYPE_CISCO7941GE:
1095 case SKINNY_DEVICETYPE_CISCO7942:
1096 case SKINNY_DEVICETYPE_CISCO7945:
1097 /* add text message support */
1098 d->pushTextMessage = sccp_device_pushTextMessage;
1099 d->pushURL = sccp_device_pushURL;
1100 d->setBackgroundImage = sccp_device_setBackgroundImage;
1101 d->displayBackgroundImagePreview = sccp_device_displayBackgroundImagePreview;
1102 d->setRingTone = sccp_device_setRingtone;
1103 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1104 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1105 break;
1106 case SKINNY_DEVICETYPE_CISCO7961:
1107 case SKINNY_DEVICETYPE_CISCO7961GE:
1108 case SKINNY_DEVICETYPE_CISCO7962:
1109 case SKINNY_DEVICETYPE_CISCO7965:
1110 /* add text message support */
1111 d->pushTextMessage = sccp_device_pushTextMessage;
1112 d->pushURL = sccp_device_pushURL;
1113 d->setBackgroundImage = sccp_device_setBackgroundImage;
1114 d->displayBackgroundImagePreview = sccp_device_displayBackgroundImagePreview;
1115 d->setRingTone = sccp_device_setRingtone;
1116 d->hasEnhancedIconMenuSupport = sccp_device_trueResult;
1117 for (i = 6; i > 0; i--) {
1118 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1119 }
1120 break;
1121 case SKINNY_DEVICETYPE_CISCO7970:
1122 case SKINNY_DEVICETYPE_CISCO7971:
1123 case SKINNY_DEVICETYPE_CISCO7975:
1124 /* the nokia icc client identifies it self as SKINNY_DEVICETYPE_CISCO7970, but it can only have one line */
1125 if (!strcasecmp(d->config_type, "nokia-icc")) { // this is for nokia icc legacy support (Old releases) -FS
1126 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1127 } else {
1128 for (i = 8; i > 0; i--) {
1129 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1130 }
1131
1132 /* add text message support */
1133 d->pushTextMessage = sccp_device_pushTextMessage;
1134 d->pushURL = sccp_device_pushURL;
1135 d->setBackgroundImage = sccp_device_setBackgroundImage;
1136 d->displayBackgroundImagePreview = sccp_device_displayBackgroundImagePreview;
1137 d->setRingTone = sccp_device_setRingtone;
1138 d->hasEnhancedIconMenuSupport = sccp_device_trueResult;
1139 }
1140 /* TEMP */
1141 d->hasMWILight = sccp_device_falseResult;
1142 break;
1143 case SKINNY_DEVICETYPE_CISCO_IP_COMMUNICATOR:
1144 /* the nokia icc client identifies it self as SKINNY_DEVICETYPE_CISCO7970, but it can only have one line */
1145 if (!strcasecmp(d->config_type, "nokia-icc")) { // this is for nokia icc legacy support (Old releases) -FS
1146 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1147 } else {
1148 for (i = 8; i > 0; i--) {
1149 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1150 }
1151
1152 /* add text message support */
1153 d->pushTextMessage = sccp_device_pushTextMessage;
1154 d->pushURL = sccp_device_pushURL;
1155 d->setBackgroundImage = sccp_device_setBackgroundImage;
1156 d->displayBackgroundImagePreview = sccp_device_displayBackgroundImagePreview;
1157 d->setRingTone = sccp_device_setRingtone;
1158 }
1159 break;
1160 case SKINNY_DEVICETYPE_CISCO7985:
1161 #ifdef CS_SCCP_VIDEO
1162 sccp_softkey_setSoftkeyState(d, KEYMODE_CONNTRANS, SKINNY_LBL_VIDEO_MODE, TRUE);
1163 #endif
1164 for (i = 0; i < 1; i++) {
1165 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1166 }
1167 break;
1168 case SKINNY_DEVICETYPE_NOKIA_ICC:
1169 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1170 d->useHookFlash = sccp_device_trueResult;
1171 break;
1172 case SKINNY_DEVICETYPE_NOKIA_E_SERIES:
1173 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1174 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1175 for (i = 0; i < 5; i++) {
1176 btn[btn_index++].type = SCCP_BUTTONTYPE_SPEEDDIAL;
1177 }
1178 break;
1179 case SKINNY_DEVICETYPE_VGC:
1180 case SKINNY_DEVICETYPE_ANALOG_GATEWAY:
1181 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1182 d->hasDisplayPrompt = sccp_device_falseResult;
1183 d->useHookFlash = sccp_device_trueResult;
1184 d->hasMWILight = sccp_device_falseResult;
1185 break;
1186 case SKINNY_DEVICETYPE_ATA188:
1187 case SKINNY_DEVICETYPE_ATA186:
1188 //case SKINNY_DEVICETYPE_ATA188:
1189 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1190 for (i = 0; i < 4; i++) {
1191 btn[btn_index++].type = SCCP_BUTTONTYPE_SPEEDDIAL;
1192 }
1193 d->hasDisplayPrompt = sccp_device_falseResult;
1194 d->useHookFlash = sccp_device_trueResult;
1195 d->hasMWILight = sccp_device_falseResult;
1196 break;
1197 case SKINNY_DEVICETYPE_CISCO8941:
1198 case SKINNY_DEVICETYPE_CISCO8945:
1199 #ifdef CS_SCCP_VIDEO
1200 sccp_softkey_setSoftkeyState(d, KEYMODE_CONNTRANS, SKINNY_LBL_VIDEO_MODE, TRUE);
1201 #endif
1202 d->pushTextMessage = sccp_device_pushTextMessage;
1203 d->pushURL = sccp_device_pushURL;
1204 d->setBackgroundImage = sccp_device_setBackgroundImage;
1205 d->displayBackgroundImagePreview = sccp_device_displayBackgroundImagePreview;
1206 d->setRingTone = sccp_device_setRingtone;
1207 d->hasDisplayPrompt = sccp_device_falseResult;
1208 d->hasLabelLimitedDisplayPrompt = sccp_device_trueResult;
1209 d->dndmode = SCCP_DNDMODE_REJECT;
1210
1211 for (i = 0; i < 10; i++) { // 4 visible, 6 in dropdown
1212 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1213 }
1214 /*
1215 for (i = 5; i <= 10; i++) {
1216 btn[btn_index++].type = SCCP_BUTTONTYPE_SPEEDDIAL;
1217 }
1218 */
1219 btn[btn_index++].type = SKINNY_BUTTONTYPE_CONFERENCE;
1220 btn[btn_index++].type = SKINNY_BUTTONTYPE_HOLD;
1221 btn[btn_index++].type = SKINNY_BUTTONTYPE_TRANSFER;
1222 btn[btn_index++].type = SKINNY_BUTTONTYPE_LASTNUMBERREDIAL;
1223 break;
1224
1225 case SKINNY_DEVICETYPE_SPA_502G:
1226 case SKINNY_DEVICETYPE_SPA_512G:
1227 case SKINNY_DEVICETYPE_SPA_521S:
1228 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1229 break;
1230 case SKINNY_DEVICETYPE_SPA_303G:
1231 for (i = 0; i < 3; i++) {
1232 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1233 }
1234 break;
1235 case SKINNY_DEVICETYPE_SPA_504G:
1236 case SKINNY_DEVICETYPE_SPA_514G:
1237 case SKINNY_DEVICETYPE_SPA_524SG:
1238 for (i = 0; i < 4; i++) {
1239 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1240 }
1241 break;
1242 case SKINNY_DEVICETYPE_SPA_508G:
1243 for (i = 0; i < 8; i++) {
1244 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1245 }
1246 btn[btn_index++].type = SKINNY_BUTTONTYPE_VOICEMAIL;
1247 btn[btn_index++].type = SKINNY_BUTTONTYPE_HOLD;
1248 break;
1249 case SKINNY_DEVICETYPE_SPA_509G:
1250 for (i = 0; i < 12; i++) {
1251 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1252 }
1253 btn[btn_index++].type = SKINNY_BUTTONTYPE_VOICEMAIL;
1254 btn[btn_index++].type = SKINNY_BUTTONTYPE_HOLD;
1255 break;
1256 case SKINNY_DEVICETYPE_SPA_525G2:
1257 for (i = 0; i < 8; i++) {
1258 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1259 }
1260 break;
1261 case SKINNY_DEVICETYPE_CISCO6901:
1262 d->useHookFlash = sccp_device_trueResult;
1263 d->hasLabelLimitedDisplayPrompt = sccp_device_trueResult;
1264 //d->dndmode = SCCP_DNDMODE_REJECT;
1265 //d->hasDisplayPrompt = sccp_device_falseResult;
1266 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1267 break;
1268 case SKINNY_DEVICETYPE_CISCO6911:
1269 d->hasDisplayPrompt = sccp_device_falseResult;
1270 //d->dndmode = SCCP_DNDMODE_REJECT;
1271 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1272 break;
1273 case SKINNY_DEVICETYPE_CISCO6921:
1274 //d->hasDisplayPrompt = sccp_device_falseResult;
1275 d->hasLabelLimitedDisplayPrompt = sccp_device_trueResult;
1276 //d->dndmode = SCCP_DNDMODE_REJECT;
1277 for (i = 0; i < 2; i++) {
1278 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1279 }
1280 for (i = 0; i < 6; i++) {
1281 btn[btn_index++].type = SCCP_BUTTONTYPE_SPEEDDIAL;
1282 }
1283 //btn[btn_index++].type = SKINNY_BUTTONTYPE_CONFERENCE;
1284 //btn[btn_index++].type = SKINNY_BUTTONTYPE_HOLD;
1285 //btn[btn_index++].type = SKINNY_BUTTONTYPE_TRANSFER;
1286 break;
1287 case SKINNY_DEVICETYPE_CISCO6941:
1288 case SKINNY_DEVICETYPE_CISCO6945:
1289 for (i = 0; i < 4; i++) {
1290 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1291 }
1292 //d->hasDisplayPrompt = sccp_device_falseResult;
1293 d->hasLabelLimitedDisplayPrompt = sccp_device_trueResult;
1294 //d->dndmode = SCCP_DNDMODE_REJECT;
1295 break;
1296 case SKINNY_DEVICETYPE_CISCO6961:
1297 for (i = 0; i < 12; i++) {
1298 btn[btn_index++].type = SCCP_BUTTONTYPE_MULTI;
1299 }
1300 //d->hasDisplayPrompt = sccp_device_falseResult;
1301 d->hasLabelLimitedDisplayPrompt = sccp_device_trueResult;
1302 //d->dndmode = SCCP_DNDMODE_REJECT;
1303 break;
1304 default:
1305 pbx_log(LOG_WARNING, "Unknown device type '%d' found.\n", d->skinny_type);
1306 /* at least one line */
1307 btn[btn_index++].type = SCCP_BUTTONTYPE_LINE;
1308 break;
1309 }
1310 for (i = 0; i < btn_index; i++) {
1311 btn[i].devicetype = type;
1312 }
1313 sccp_log(DEBUGCAT_DEVICE)(VERBOSE_PREFIX_3 "%s: Allocated %d Device buttons.\n", d->id, btn_index);
1314
1315 sccp_addon_t *cur = NULL;
1316 SCCP_LIST_LOCK(&d->addons);
1317 SCCP_LIST_TRAVERSE(&d->addons, cur, list) {
1318 btn_index = sccp_addon_build_buttontemplate(d, cur, btn, btn_index);
1319 }
1320 SCCP_LIST_UNLOCK(&d->addons);
1321
1322 if (d->skinny_type < 6 || sccp_strcaseequals(d->config_type, "kirk")) {
1323 d->hasDisplayPrompt = sccp_device_falseResult;
1324 d->hasMWILight = sccp_device_falseResult;
1325 }
1326
1327 // fill the rest with abbreviated dial buttons
1328 for (i = btn_index; i< StationMaxButtonTemplateSize; i++) {
1329 btn[i].type = SCCP_BUTTONTYPE_ABBRDIAL;
1330 btn[i].devicetype = type;
1331 }
1332 sccp_log(DEBUGCAT_DEVICE)(VERBOSE_PREFIX_3 "%s: Allocated %d Abbreviate Dial Buttons.\n", d->id, StationMaxButtonTemplateSize - btn_index);
1333
1334 return btn_index;
1335 }
1336
1337 /*!
1338 * \brief Send SCCP Message to Device
1339 * \param d SCCP Device
1340 * \param msg SCCP Message
1341 * \return Status as int
1342 *
1343 * \callgraph
1344 */
sccp_dev_send(constDevicePtr d,sccp_msg_t * msg)1345 int sccp_dev_send(constDevicePtr d, sccp_msg_t * msg)
1346 {
1347 int result = -1;
1348
1349 if (d && d->session && msg) {
1350 sccp_log((DEBUGCAT_MESSAGE))(VERBOSE_PREFIX_3 "%s: >> Send message %s\n", d->id, msginfo2str(letohl(msg->header.lel_messageId)));
1351 result = sccp_session_send(d, msg);
1352 } else {
1353 sccp_free(msg);
1354 }
1355 return result;
1356 }
1357
1358 /*!
1359 * \brief Send an SCCP message to a device
1360 * \param d SCCP Device
1361 * \param t SCCP Message
1362 *
1363 * \callgraph
1364 * \callergraph
1365 */
sccp_dev_sendmsg(constDevicePtr d,sccp_mid_t t)1366 void sccp_dev_sendmsg(constDevicePtr d, sccp_mid_t t)
1367 {
1368 if (d) {
1369 sccp_session_sendmsg(d, t);
1370 }
1371 }
1372
1373 /*!
1374 * \brief Register a Device
1375 * \param d SCCP Device
1376 * \param state Registration State as skinny_registrationstate_t
1377 *
1378 * \note adds a retained device to the event.deviceRegistered.device
1379 */
sccp_dev_set_registered(devicePtr d,skinny_registrationstate_t state)1380 void sccp_dev_set_registered(devicePtr d, skinny_registrationstate_t state)
1381 {
1382 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: (sccp_dev_set_registered) Setting Registered Status for Device from %s to %s\n", DEV_ID_LOG(d), skinny_registrationstate2str(sccp_device_getRegistrationState(d)), skinny_registrationstate2str(state));
1383
1384 if (!sccp_device_setRegistrationState(d, state)) {
1385 return;
1386 }
1387
1388 /* Handle registration completion. */
1389 if (state == SKINNY_DEVICE_RS_OK) {
1390 if (!d->linesRegistered) {
1391 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Device does not support RegisterAvailableLinesMessage, force this\n", DEV_ID_LOG(d));
1392 sccp_handle_AvailableLines(d->session, d, NULL);
1393 }
1394 sccp_dev_postregistration(d);
1395 } else if (state == SKINNY_DEVICE_RS_PROGRESS) {
1396 sccp_event_t *event = sccp_event_allocate(SCCP_EVENT_DEVICE_PREREGISTERED);
1397 if (event) {
1398 event->deviceRegistered.device = sccp_device_retain(d);
1399 sccp_event_fire(event);
1400 }
1401 }
1402 d->registrationTime = time(0);
1403 }
1404
1405 /*!
1406 * \brief Sets the SCCP Device's SoftKey Mode Specified by opt
1407 * \param d SCCP Device
1408 * \param lineInstance LineInstance as uint8_t
1409 * \param callid Call ID as uint8_t
1410 * \param softKeySetIndex SoftKeySet Index
1411 * \todo Disable DirTrfr by Default
1412 */
1413 //void sccp_dev_set_keyset(constDevicePtr d, uint8_t lineInstance, uint32_t callid, uint8_t softKeySetIndex)
sccp_dev_set_keyset(constDevicePtr d,uint8_t lineInstance,uint32_t callid,skinny_keymode_t softKeySetIndex)1414 void sccp_dev_set_keyset(constDevicePtr d, uint8_t lineInstance, uint32_t callid, skinny_keymode_t softKeySetIndex)
1415 {
1416 sccp_msg_t *msg = NULL;
1417
1418 if (!d) {
1419 return;
1420 }
1421 if (!d->softkeysupport) {
1422 return; /* the device does not support softkeys */
1423 }
1424 /* 69XX Exception SoftKeySet Mapping */
1425 if (d->skinny_type == SKINNY_DEVICETYPE_CISCO6901 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6911 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6921 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6941 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6945 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6961) {
1426
1427 /* 69XX does not like CONNCONF, so let's not set that and keep CONNECTED instead */
1428 /* while transfer in progress, they like OFFHOOKFEAT, after when we have connected to the destination we need to set CONNTRANS */
1429 /*! \todo Discuss if this behaviour should not be the general case for all devices */
1430 if (d->transfer && d->transferChannels.transferee) {
1431 /* first stage transfer */
1432 if (softKeySetIndex == KEYMODE_OFFHOOK && !d->transferChannels.transferer) {
1433 softKeySetIndex = KEYMODE_OFFHOOKFEAT;
1434 }
1435 /* second stage transfer (blind or not) */
1436 if ((softKeySetIndex == KEYMODE_RINGOUT || softKeySetIndex == KEYMODE_CONNECTED) && d->transferChannels.transferer) {
1437 softKeySetIndex = KEYMODE_CONNTRANS;
1438 }
1439 }
1440 } else {
1441 /*let's replace the CONNECTED with the transfer / conference states when allowed on this device */
1442 if (softKeySetIndex == KEYMODE_CONNECTED) {
1443 softKeySetIndex = (
1444 #if CS_SCCP_CONFERENCE
1445 (d->conference) ? KEYMODE_CONNCONF :
1446 #endif
1447 (d->transfer) ? KEYMODE_CONNTRANS : KEYMODE_CONNECTED
1448 );
1449 }
1450 }
1451 REQ(msg, SelectSoftKeysMessage);
1452 if (!msg) {
1453 return;
1454 }
1455 msg->data.SelectSoftKeysMessage.lel_lineInstance = htolel(lineInstance);
1456 msg->data.SelectSoftKeysMessage.lel_callReference = htolel(callid);
1457 msg->data.SelectSoftKeysMessage.lel_softKeySetIndex = htolel(softKeySetIndex);
1458
1459 if (softKeySetIndex == KEYMODE_ONHOOK || softKeySetIndex == KEYMODE_OFFHOOK || softKeySetIndex == KEYMODE_OFFHOOKFEAT) {
1460 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_REDIAL, (sccp_strlen_zero(d->redialInformation.number) && !d->useRedialMenu) ? FALSE : TRUE);
1461 }
1462 #if CS_SCCP_CONFERENCE
1463 if (d->allow_conference) {
1464 if (d->conference) {
1465 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_CONFRN, FALSE);
1466 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_JOIN, TRUE);
1467 } else {
1468 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_CONFRN, TRUE);
1469 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_JOIN, FALSE);
1470 }
1471 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_CONFLIST, TRUE);
1472 } else {
1473 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_CONFRN, FALSE);
1474 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_CONFLIST, FALSE);
1475 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_JOIN, FALSE);
1476 }
1477 #endif
1478
1479 /* deactivate monitor softkey for all states excl. connected -MC */
1480 if (softKeySetIndex != KEYMODE_CONNTRANS && softKeySetIndex != KEYMODE_CONNECTED && softKeySetIndex != KEYMODE_EMPTY) {
1481 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_MONITOR, FALSE);
1482 }
1483 if (softKeySetIndex == KEYMODE_RINGOUT) {
1484 if (d->transfer && d->transferChannels.transferer) {
1485 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_TRANSFER, TRUE);
1486 } else {
1487 sccp_softkey_setSoftkeyState((sccp_device_t *) d, softKeySetIndex, SKINNY_LBL_TRANSFER, FALSE);
1488 }
1489 }
1490 //msg->data.SelectSoftKeysMessage.les_validKeyMask = 0xFFFFFFFF; /* htolel(65535); */
1491 msg->data.SelectSoftKeysMessage.les_validKeyMask = htolel(d->softKeyConfiguration.activeMask[softKeySetIndex]);
1492
1493 sccp_log((DEBUGCAT_SOFTKEY + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Set softkeyset to %s(%d) on line %d and call %d\n", d->id, skinny_keymode2str(softKeySetIndex), softKeySetIndex, lineInstance, callid);
1494 sccp_log((DEBUGCAT_SOFTKEY + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: validKeyMask %u\n", d->id, msg->data.SelectSoftKeysMessage.les_validKeyMask);
1495 sccp_dev_send(d, msg);
1496 }
1497
1498 /*!
1499 * \brief Set Ringer on Device
1500 * \param d SCCP Device
1501 * \param opt Option as uint8_t
1502 * \param lineInstance LineInstance as uint32_t
1503 * \param callid Call ID as uint32_t
1504 */
sccp_dev_set_ringer(constDevicePtr d,skinny_ringtype_t ringtype,skinny_ringduration_t duration,uint8_t lineInstance,uint32_t callid)1505 void sccp_dev_set_ringer(constDevicePtr d, skinny_ringtype_t ringtype, skinny_ringduration_t duration, uint8_t lineInstance, uint32_t callid)
1506 {
1507 sccp_msg_t *msg = NULL;
1508
1509 REQ(msg, SetRingerMessage);
1510 if (!msg) {
1511 return;
1512 }
1513 msg->data.SetRingerMessage.lel_ringMode = htolel(ringtype);
1514 /* Note that for distinctive ringing to work with the higher protocol versions
1515 the following actually needs to be set to 1 as the original comment says.
1516 Curiously, the variable is not set to 1 ... */
1517 msg->data.SetRingerMessage.lel_ringDuration = htolel(duration); /* Normal:1 / Single:2 */
1518 msg->data.SetRingerMessage.lel_lineInstance = htolel(lineInstance);
1519 msg->data.SetRingerMessage.lel_callReference = htolel(callid);
1520 sccp_dev_send(d, msg);
1521 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Send ringer mode %s(%d) on device\n", DEV_ID_LOG(d), skinny_ringtype2str(ringtype), ringtype);
1522 }
1523
1524 /*!
1525 * \brief Set Speaker Status on Device
1526 * \param d SCCP Device
1527 * \param mode Speaker Mode as uint8_t
1528 */
sccp_dev_set_speaker(constDevicePtr d,uint8_t mode)1529 void sccp_dev_set_speaker(constDevicePtr d, uint8_t mode)
1530 {
1531 sccp_msg_t *msg = NULL;
1532
1533 if (!d || !d->session) {
1534 return;
1535 }
1536 REQ(msg, SetSpeakerModeMessage);
1537 if (!msg) {
1538 return;
1539 }
1540 msg->data.SetSpeakerModeMessage.lel_speakerMode = htolel(mode);
1541 sccp_dev_send(d, msg);
1542 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Send speaker mode '%s'\n", d->id, (mode == SKINNY_STATIONSPEAKER_ON ? "on" : (mode == SKINNY_STATIONSPEAKER_OFF ? "off" : "unknown")));
1543 }
1544
1545 /*!
1546 * \brief Set HookFlash Detect
1547 * \param d SCCP Device
1548 */
sccp_dev_setHookFlashDetect(constDevicePtr d)1549 static void sccp_dev_setHookFlashDetect(constDevicePtr d)
1550 {
1551 sccp_msg_t *msg = NULL;
1552
1553 if (!d || !d->session || !d->protocol || !d->useHookFlash()) {
1554 return; /* only for old phones */
1555 }
1556 REQ(msg, SetHookFlashDetectMessage);
1557 if (!msg) {
1558 return;
1559 }
1560 sccp_dev_send(d, msg);
1561 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Enabled HookFlashDetect\n", d->id);
1562 }
1563
1564 /*!
1565 * \brief Set Microphone Status on Device
1566 * \param d SCCP Device
1567 * \param mode Microphone Mode as uint8_t
1568 */
sccp_dev_set_microphone(devicePtr d,uint8_t mode)1569 void sccp_dev_set_microphone(devicePtr d, uint8_t mode)
1570 {
1571 sccp_msg_t *msg = NULL;
1572
1573 if (!d || !d->session) {
1574 return;
1575 }
1576 REQ(msg, SetMicroModeMessage);
1577 if (!msg) {
1578 return;
1579 }
1580 msg->data.SetMicroModeMessage.lel_micMode = htolel(mode);
1581 sccp_dev_send(d, msg);
1582 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Send microphone mode '%s'\n", d->id, (mode == SKINNY_STATIONMIC_ON ? "on" : (mode == SKINNY_STATIONMIC_OFF ? "off" : "unknown")));
1583 }
1584
1585 /*!
1586 * \brief Set Call Plane to Active on Line on Device
1587 * \param device SCCP Device
1588 * \param lineInstance lineInstance as unint8_t
1589 * \param status Status as int
1590 * \todo What does this function do exactly (ActivateCallPlaneMessage) ?
1591 *
1592 * \callgraph
1593 * \callergraph
1594 */
sccp_dev_set_cplane(constDevicePtr device,uint8_t lineInstance,int status)1595 void sccp_dev_set_cplane(constDevicePtr device, uint8_t lineInstance, int status)
1596 {
1597 sccp_msg_t *msg = NULL;
1598
1599 if (!device) {
1600 return;
1601 }
1602 REQ(msg, ActivateCallPlaneMessage);
1603 if (!msg) {
1604 return;
1605 }
1606 if (status) {
1607 msg->data.ActivateCallPlaneMessage.lel_lineInstance = htolel(lineInstance);
1608 }
1609 sccp_dev_send(device, msg);
1610
1611 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Send activate call plane on line %d\n", device->id, (status) ? lineInstance : 0);
1612 }
1613
1614 /*!
1615 * \brief Set Call Plane to In-Active on Line on Device
1616 * \param d device
1617 * \todo What does this function do exactly (DeactivateCallPlaneMessage) ?
1618 *
1619 * \callgraph
1620 * \callergraph
1621 */
sccp_dev_deactivate_cplane(constDevicePtr d)1622 void sccp_dev_deactivate_cplane(constDevicePtr d)
1623 {
1624 if (!d) {
1625 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "Null device for deactivate callplane\n");
1626 return;
1627 }
1628
1629 sccp_dev_sendmsg(d, DeactivateCallPlaneMessage);
1630 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Send deactivate call plane\n", d->id);
1631 }
1632
1633 /*!
1634 * \brief Send Start Tone to Device
1635 * \param d SCCP Device
1636 * \param tone Tone as uint8_t
1637 * \param lineInstance LineInstance as uint8_t
1638 * \param callid Call ID as uint32_t
1639 * \param direction Direction as skinny_toneDirection_t
1640 */
sccp_dev_starttone(constDevicePtr d,skinny_tone_t tone,uint8_t lineInstance,uint32_t callid,skinny_toneDirection_t direction)1641 void sccp_dev_starttone(constDevicePtr d, skinny_tone_t tone, uint8_t lineInstance, uint32_t callid, skinny_toneDirection_t direction)
1642 {
1643 sccp_msg_t *msg = NULL;
1644
1645 if (!d) {
1646 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "Null device for device starttone\n");
1647 return;
1648 }
1649
1650 REQ(msg, StartToneMessage);
1651 if (!msg) {
1652 return;
1653 }
1654 msg->data.StartToneMessage.lel_tone = htolel(tone);
1655 msg->data.StartToneMessage.lel_toneDirection = htolel(direction);
1656 msg->data.StartToneMessage.lel_lineInstance = htolel(lineInstance);
1657 msg->data.StartToneMessage.lel_callReference = htolel(callid);
1658
1659 sccp_dev_send(d, msg);
1660 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Sending tone %s (%d) on line %d with callid %d (direction: %s)\n", d->id, skinny_tone2str(tone), tone, lineInstance, callid, skinny_toneDirection2str(direction));
1661 }
1662
1663 /*!
1664 * \brief Send Stop Tone to Device
1665 * \param d SCCP Device
1666 * \param lineInstance LineInstance as uint8_t
1667 * \param callid Call ID as uint32_t
1668 */
sccp_dev_stoptone(constDevicePtr d,uint8_t lineInstance,uint32_t callid)1669 void sccp_dev_stoptone(constDevicePtr d, uint8_t lineInstance, uint32_t callid)
1670 {
1671 sccp_msg_t *msg = NULL;
1672
1673 if (!d || !d->session) {
1674 return;
1675 }
1676 REQ(msg, StopToneMessage);
1677 if (!msg) {
1678 return;
1679 }
1680 msg->data.StopToneMessage.lel_lineInstance = htolel(lineInstance);
1681 msg->data.StopToneMessage.lel_callReference = htolel(callid);
1682 if (d->protocolversion >= 11) {
1683 msg->data.StopToneMessage.lel_tone = htolel(SKINNY_TONE_SILENCE);
1684 }
1685 sccp_dev_send(d, msg);
1686 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Stop tone on line %d with callid %d\n", d->id, lineInstance, callid);
1687 }
1688
1689 /*!
1690 * \brief Set Message on Display Prompt of Device
1691 * \param d SCCP Device
1692 * \param msg Msg as char
1693 * \param timeout Timeout as int
1694 * \param storedb Store in the pbx database
1695 * \param beep Beep on device when message is received
1696 *
1697 * \callgraph
1698 * \callergraph
1699 */
sccp_dev_set_message(devicePtr d,const char * msg,const int timeout,const boolean_t storedb,const boolean_t beep)1700 void sccp_dev_set_message(devicePtr d, const char *msg, const int timeout, const boolean_t storedb, const boolean_t beep)
1701 {
1702 if (storedb) {
1703 char msgtimeout[10];
1704
1705 snprintf(msgtimeout, sizeof(msgtimeout), "%d", timeout);
1706 iPbx.feature_addToDatabase("SCCP/message", "timeout", pbx_strdup(msgtimeout));
1707 iPbx.feature_addToDatabase("SCCP/message", "text", msg);
1708 }
1709
1710 if (timeout) {
1711 if (d->skinny_type == SKINNY_DEVICETYPE_CISCO6901 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6921 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6941 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6945 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6961) {
1712 sccp_dev_displayprompt(d, 0, 0, msg, timeout);
1713 } else {
1714 sccp_dev_displayprinotify(d, msg, SCCP_MESSAGE_PRIORITY_TIMEOUT, timeout);
1715 }
1716 } else {
1717 sccp_device_addMessageToStack(d, SCCP_MESSAGE_PRIORITY_IDLE, msg);
1718 }
1719 if (beep) {
1720 sccp_dev_starttone(d, SKINNY_TONE_ZIPZIP, 0, 0, SKINNY_TONEDIRECTION_USER);
1721 }
1722 }
1723
1724 /*!
1725 * \brief Clear Message from Display Prompt of Device
1726 * \param d SCCP Device
1727 * \param cleardb Clear from the pbx database
1728 *
1729 * \callgraph
1730 * \callergraph
1731 */
sccp_dev_clear_message(devicePtr d,const boolean_t cleardb)1732 void sccp_dev_clear_message(devicePtr d, const boolean_t cleardb)
1733 {
1734 if (cleardb) {
1735 iPbx.feature_removeTreeFromDatabase("SCCP/message", "timeout");
1736 iPbx.feature_removeTreeFromDatabase("SCCP/message", "text");
1737 }
1738
1739 sccp_device_clearMessageFromStack(d, SCCP_MESSAGE_PRIORITY_IDLE);
1740 if (d->skinny_type == SKINNY_DEVICETYPE_CISCO6901 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6921 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6941 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6945 || d->skinny_type == SKINNY_DEVICETYPE_CISCO6961) {
1741 sccp_dev_clearprompt(d, 0, 0);
1742 } else {
1743 sccp_dev_cleardisplayprinotify(d, SCCP_MESSAGE_PRIORITY_TIMEOUT);
1744 }
1745 }
1746
1747 /*!
1748 * \brief Send Clear Prompt to Device
1749 * \param d SCCP Device
1750 * \param lineInstance LineInstance as uint8_t
1751 * \param callid Call ID uint32_t
1752 *
1753 * \callgraph
1754 * \callergraph
1755 */
sccp_dev_clearprompt(constDevicePtr d,const uint8_t lineInstance,const uint32_t callid)1756 void sccp_dev_clearprompt(constDevicePtr d, const uint8_t lineInstance, const uint32_t callid)
1757 {
1758 sccp_msg_t *msg = NULL;
1759
1760 if (!d || !d->session || !d->protocol || (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
1761 return; /* only for telecaster and new phones */
1762 }
1763 REQ(msg, ClearPromptStatusMessage);
1764 if (!msg) {
1765 return;
1766 }
1767 msg->data.ClearPromptStatusMessage.lel_callReference = htolel(callid);
1768 msg->data.ClearPromptStatusMessage.lel_lineInstance = htolel(lineInstance);
1769 sccp_dev_send(d, msg);
1770 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Clear the status prompt on line %d and callid %d\n", d->id, lineInstance, callid);
1771 }
1772
1773 /*!
1774 * \brief Send Display Prompt to Device
1775 * \param d SCCP Device
1776 * \param lineInstance Line instance as uint8_t
1777 * \param callid Call ID uint32_t
1778 * \param msg Msg as char
1779 * \param timeout Timeout as int
1780 * \param file Source File
1781 * \param lineno Source Line
1782 * \param pretty_function CB Function to Print
1783 *
1784 * \callgraph
1785 * \callergraph
1786 */
1787 //void sccp_dev_displayprompt(devicePtr d, uint8_t line, uint32_t callid, char *msg, int timeout)
sccp_dev_displayprompt_debug(constDevicePtr d,const uint8_t lineInstance,const uint32_t callid,const char * msg,const int timeout,const char * file,int lineno,const char * pretty_function)1788 void sccp_dev_displayprompt_debug(constDevicePtr d, const uint8_t lineInstance, const uint32_t callid, const char *msg, const int timeout, const char *file, int lineno, const char *pretty_function)
1789 {
1790 #if DEBUG
1791 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: ( %s:%d:%s ) sccp_dev_displayprompt '%s' for line %d (%d)\n", DEV_ID_LOG(d), file, lineno, pretty_function, msg, lineInstance, timeout);
1792 #endif
1793 if (!d || !d->session || !d->protocol || (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
1794 return;
1795 }
1796 d->protocol->displayPrompt(d, lineInstance, callid, timeout, msg);
1797 }
1798
1799 /*!
1800 * \brief Send Clear Display to Device
1801 * \param d SCCP Device
1802 *
1803 * \callgraph
1804 * \callergraph
1805 *
1806 * \note: message is not known by all devices, we should figure out which do and which don't, for now, we are not using this message anymore
1807 * JVM: Startup Module Loader|cip.sccp.CcApi:? - alarm( GENERAL_ALARM ):Invalid SCCP message! : ID :9a: MessageFactory.createMessage failed, length = 0 - close connection and alarm in future
1808 */
sccp_dev_cleardisplay(constDevicePtr d)1809 void sccp_dev_cleardisplay(constDevicePtr d)
1810 {
1811 //if (!d || !d->session || !d->protocol || (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
1812 // return;
1813 //}
1814 //sccp_dev_sendmsg(d, ClearDisplay);
1815 //sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Clear the display\n", d->id);
1816 }
1817
1818 #if UNUSEDCODE // 2015-11-01
1819 /*!
1820 * \brief Send Display to Device
1821 * \param d SCCP Device
1822 * \param msgstr Msg as char
1823 * \param file Source File
1824 * \param lineno Source Line
1825 * \param pretty_function CB Function to Print
1826 *
1827 * \callgraph
1828 * \callergraph
1829 */
sccp_dev_display_debug(constDevicePtr d,const char * msgstr,const char * file,const int lineno,const char * pretty_function)1830 void sccp_dev_display_debug(constDevicePtr d, const char *msgstr, const char *file, const int lineno, const char *pretty_function)
1831 {
1832 #if DEBUG
1833 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: ( %s:%d:%s ) sccp_dev_display '%s'\n", DEV_ID_LOG(d), file, lineno, pretty_function, msgstr);
1834 #endif
1835 sccp_msg_t *msg = NULL;
1836
1837 if (!d || !d->session || !d->protocol || (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
1838 if (!d || !d->session || !d->protocol || !d->hasDisplayPrompt()) {
1839 return;
1840 }
1841 if (!msgstr || sccp_strlen_zero(msgstr)) {
1842 return;
1843 }
1844 REQ(msg, DisplayTextMessage);
1845 if (!msg) {
1846 return;
1847 }
1848 sccp_copy_string(msg->data.DisplayTextMessage.displayMessage, msgstr, sizeof(msg->data.DisplayTextMessage.displayMessage));
1849
1850 sccp_dev_send(d, msg);
1851 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Display text\n", d->id);
1852 }
1853 #endif
1854
1855 /*!
1856 * \brief Send Clear Display Notification to Device
1857 *
1858 * \param d SCCP Device
1859 *
1860 * \callgraph
1861 * \callergraph
1862 */
1863 void sccp_dev_cleardisplaynotify(constDevicePtr d)
1864 {
1865 if (!d || !d->session || !d->protocol || (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
1866 return; /* only for telecaster and new phones */
1867 }
1868 sccp_dev_sendmsg(d, ClearNotifyMessage);
1869 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_MESSAGE)) (VERBOSE_PREFIX_3 "%s: Clear the display notify message\n", d->id);
1870 }
1871
1872 /*!
1873 * \brief Send Display Notification to Device
1874 * \param d SCCP Device
1875 * \param msg Msg as char
1876 * \param timeout Timeout as uint8_t
1877 * \param file Source File
1878 * \param lineno Source Line
1879 * \param pretty_function CB Function to Print
1880 *
1881 * \callgraph
1882 * \callergraph
1883 */
1884 //void sccp_dev_displaynotify(devicePtr d, char *msg, uint32_t timeout)
1885 void sccp_dev_displaynotify_debug(constDevicePtr d, const char *msg, uint8_t timeout, const char *file, const int lineno, const char *pretty_function)
1886 {
1887 // #if DEBUG
1888 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: ( %s:%d:%s ) sccp_dev_displaynotify '%s' (%d)\n", DEV_ID_LOG(d), file, lineno, pretty_function, msg, timeout);
1889 // #endif
1890 if (!d || !d->session || !d->protocol || (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
1891 return;
1892 }
1893 if (!msg || sccp_strlen_zero(msg)) {
1894 return;
1895 }
1896 d->protocol->displayNotify(d, timeout, msg);
1897 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Display notify with timeout %d\n", d->id, timeout);
1898 }
1899
1900 /*!
1901 * \brief Send Clear Display Notification to Device
1902 * \param d SCCP Device
1903 * \param priority Priority as uint8_t
1904 *
1905 * \callgraph
1906 * \callergraph
1907 */
1908 void sccp_dev_cleardisplayprinotify(constDevicePtr d, const uint8_t priority)
1909 {
1910 sccp_msg_t *msg = NULL;
1911 if (!d || !d->session || !d->protocol || (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
1912 return;
1913 }
1914 REQ(msg, ClearPriNotifyMessage);
1915 msg->data.ClearPriNotifyMessage.lel_priority = htolel(priority);
1916
1917 sccp_dev_send(d, msg);
1918 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Clear the display priority notify message\n", d->id);
1919 }
1920
1921 /*!
1922 * \brief Send Display Priority Notification to Device
1923 * \param d SCCP Device
1924 * \param msg Msg as char
1925 * \param priority Priority as uint8_t
1926 * \param timeout Timeout as uint8_t
1927 * \param file Source File
1928 * \param lineno Source Line
1929 * \param pretty_function CB Function to Print
1930 *
1931 * \callgraph
1932 * \callergraph
1933 */
1934 //void sccp_dev_displayprinotify(devicePtr d, char *msg, uint32_t priority, uint32_t timeout)
1935 void sccp_dev_displayprinotify_debug(constDevicePtr d, const char *msg, const sccp_message_priority_t priority, const uint8_t timeout, const char *file, const int lineno, const char *pretty_function)
1936 {
1937 if (!d || !d->session || !d->protocol || (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
1938 return;
1939 }
1940 if (!msg || sccp_strlen_zero(msg)) {
1941 sccp_dev_cleardisplayprinotify(d, priority);
1942 return;
1943 }
1944 d->protocol->displayPriNotify(d, priority, timeout, msg);
1945 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Display notify with timeout %d and priority %d\n", d->id, timeout, priority);
1946 }
1947
1948 /*!
1949 * \brief Find SpeedDial by Index
1950 * \param d SCCP Device
1951 * \param instance Instance as uint8_t
1952 * \param withHint With Hint as boolean_t
1953 * \param k SCCP Speeddial (Returned by Ref)
1954 * \return Void
1955 *
1956 */
1957 void sccp_dev_speed_find_byindex(constDevicePtr d, const uint16_t instance, boolean_t withHint, sccp_speed_t * const k)
1958 {
1959 sccp_buttonconfig_t *config = NULL;
1960
1961 if (!d || !d->session || instance == 0) {
1962 return;
1963 }
1964 memset(k, 0, sizeof(sccp_speed_t));
1965 sccp_copy_string(k->name, "unknown speeddial", sizeof(k->name));
1966
1967 SCCP_LIST_LOCK(&(((devicePtr)d)->buttonconfig));
1968 SCCP_LIST_TRAVERSE(&d->buttonconfig, config, list) {
1969 if (config->type == SPEEDDIAL && config->instance == instance) {
1970 /* we are searching for hinted speeddials */
1971 if (TRUE == withHint && !sccp_strlen_zero(config->button.speeddial.hint)) {
1972 k->valid = TRUE;
1973 k->instance = instance;
1974 k->type = SCCP_BUTTONTYPE_SPEEDDIAL;
1975 sccp_copy_string(k->name, config->label, sizeof(k->name));
1976 sccp_copy_string(k->ext, config->button.speeddial.ext, sizeof(k->ext));
1977 sccp_copy_string(k->hint, config->button.speeddial.hint, sizeof(k->hint));
1978
1979 } else if(FALSE == withHint && sccp_strlen_zero(config->button.speeddial.hint)) {
1980 k->valid = TRUE;
1981 k->instance = instance;
1982 k->type = SCCP_BUTTONTYPE_SPEEDDIAL;
1983 sccp_copy_string(k->name, config->label, sizeof(k->name));
1984 sccp_copy_string(k->ext, config->button.speeddial.ext, sizeof(k->ext));
1985 }
1986 }
1987 }
1988 SCCP_LIST_UNLOCK(&(((devicePtr)d)->buttonconfig));
1989 }
1990
1991 /*!
1992 * \brief Send Get Activeline to Device
1993 * \param device SCCP Device
1994 * \return Retained SCCP Line
1995 *
1996 * \warning
1997 * - device->buttonconfig is not locked
1998 * \return_ref d->currentLine
1999 */
2000 linePtr sccp_dev_getActiveLine(constDevicePtr device)
2001 {
2002 sccp_buttonconfig_t * buttonconfig = NULL;
2003
2004 if (!device || !device->session) {
2005 return NULL;
2006 }
2007 if (device->currentLine) {
2008 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: The active line is %s\n", device->id, device->currentLine->name);
2009 return sccp_line_retain(device->currentLine);
2010 }
2011 // else try to set an new currentLine
2012
2013 /*! \todo Does this actually make sense. traversing the buttonconfig and then finding a line, potentially doing this multiple times */
2014 devicePtr d = (sccp_device_t * const) device; // need non-const device
2015 SCCP_LIST_TRAVERSE(&device->buttonconfig, buttonconfig, list) {
2016 if (buttonconfig->type == LINE && !d->currentLine) {
2017 if ((d->currentLine = sccp_line_find_byname(buttonconfig->button.line.name, FALSE))) { // update device->currentLine, returns retained line
2018 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: Forcing the active line to %s from NULL\n", d->id, d->currentLine->name);
2019 return sccp_line_retain(d->currentLine); // returning retained
2020 }
2021 }
2022 }
2023
2024 // failed to find or set a currentLine
2025 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: No lines\n", device->id);
2026 return NULL; // never reached
2027 }
2028
2029 /*!
2030 * \brief Set Activeline to Device
2031 * \param device SCCP Device
2032 * \param l SCCP Line
2033 */
2034 // void sccp_dev_setActiveLine(devicePtr device, constLinePtr l)
2035 void __sccp_dev_setActiveLine(devicePtr device, constLinePtr l, const char *file, uint32_t line, const char *func)
2036 {
2037 if (!device || !device->session) {
2038 return;
2039 }
2040 //sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_1 "%s (setActiveLine) %s func called by %s:%s:%s\n", DEV_ID_LOG(device), (l) ? l->name : "<null>", line);
2041 sccp_line_refreplace(&device->currentLine, l);
2042
2043 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: Set the active line %s\n", device->id, l ? l->name : "(NULL)");
2044 }
2045
2046 /*!
2047 * \brief Get Active Channel
2048 * \param device SCCP Device
2049 * \return SCCP Channel
2050 */
2051 channelPtr sccp_device_getActiveChannel(constDevicePtr device)
2052 {
2053 sccp_channel_t *channel = NULL;
2054
2055 if (!device) {
2056 return NULL;
2057 }
2058
2059 sccp_log((DEBUGCAT_CHANNEL + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Getting the active channel on device.\n", device->id);
2060
2061 if (device->active_channel && (channel = sccp_channel_retain(device->active_channel))) {
2062 if (channel && channel->state == SCCP_CHANNELSTATE_DOWN) {
2063 sccp_log((DEBUGCAT_CHANNEL + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: 'active channel': %s on device is DOWN apparently. Returning NULL\n", device->id, channel->designator);
2064 sccp_channel_release(&channel); /* explicit release, when not returning channel because it's DOWN */
2065 }
2066 } else {
2067 sccp_log((DEBUGCAT_CHANNEL + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: No active channel on device.\n", device->id);
2068 }
2069
2070 return channel;
2071 }
2072
2073 /*!
2074 * \brief Set SCCP Channel to Active
2075 * \param d SCCP Device
2076 * \param channel SCCP Channel
2077 */
2078 //void sccp_device_setActiveChannel(constDevicePtr d, constChannelPtr channel)
2079 void __sccp_device_setActiveChannel(constDevicePtr d, constChannelPtr channel, const char *file, uint32_t line, const char *func)
2080 {
2081 //sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_1 "%s (setActiveChannel) %s func called by %s:%s:%s\n", DEV_ID_LOG(d), (channel) ? channel->designator : "<null>", line);
2082 AUTO_RELEASE(sccp_device_t, device , sccp_device_retain(d));
2083
2084 if(device && device->active_channel != channel) {
2085 sccp_log((DEBUGCAT_CHANNEL + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Set the active channel %d on device\n", DEV_ID_LOG(d), (channel) ? channel->callid : 0);
2086 if (device->active_channel && device->active_channel->line) {
2087 device->active_channel->line->statistic.numberOfActiveChannels--;
2088 }
2089 if (!channel) {
2090 sccp_dev_setActiveLine(device, NULL);
2091 }
2092 sccp_channel_refreplace(&device->active_channel, channel);
2093 if (device->active_channel) {
2094 sccp_dev_setActiveLine(device, device->active_channel->line);
2095 if (device->active_channel->line) {
2096 device->active_channel->line->statistic.numberOfActiveChannels++;
2097 }
2098 }
2099 }
2100 }
2101
2102 /*!
2103 * \brief Reschedule Display Prompt Check
2104 * \param d SCCP Device
2105 *
2106 * \todo We have to decide on a standardized implementation of displayprompt to be used
2107 * For DND/Cfwd/Message/Voicemail/Private Status for Devices and Individual Lines
2108 * If necessary devicetypes could be deviced into 3-4 groups depending on their capability for displaying status the best way
2109 *
2110 * \callgraph
2111 * \callergraph
2112 */
2113 void sccp_dev_check_displayprompt(constDevicePtr d)
2114 {
2115 //sccp_log((DEBUGCAT_CORE + DEBUGCAT_DEVICE + DEBUGCAT_MESSAGE)) (VERBOSE_PREFIX_1 "%s: (sccp_dev_check_displayprompt)\n", DEV_ID_LOG(d));
2116 if (!d || !d->session || !d->protocol || (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
2117 return;
2118 }
2119 boolean_t message_set = FALSE;
2120
2121 sccp_dev_clearprompt(d, 0, 0);
2122 #ifndef SCCP_ATOMIC
2123 devicePtr device = (devicePtr) d; /* discard const */
2124 sccp_mutex_lock(&device->messageStack.lock);
2125 #endif
2126 for(int i = SCCP_MESSAGE_PRIORITY_SENTINEL - 1; i >= 0; i--) {
2127 if (d->messageStack.messages[i] != NULL && !sccp_strlen_zero(d->messageStack.messages[i])) {
2128 sccp_dev_displayprompt(d, 0, 0, d->messageStack.messages[i], 0);
2129 message_set = TRUE;
2130 break;
2131 }
2132 }
2133 #ifndef SCCP_ATOMIC
2134 sccp_mutex_unlock(&device->messageStack.lock);
2135 #endif
2136 if (!message_set) {
2137 sccp_dev_displayprompt(d, 0, 0, SKINNY_DISP_YOUR_CURRENT_OPTIONS, 0);
2138 sccp_dev_set_keyset(d, 0, 0, KEYMODE_ONHOOK); /* this is for redial softkey */
2139 }
2140 sccp_log_and((DEBUGCAT_DEVICE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "%s: Finish DisplayPrompt\n", d->id);
2141 }
2142
2143 /*!
2144 * \brief Send forward status to a line on a device
2145 * \param l SCCP Line
2146 * \param lineInstance lineInstance as uint8_t
2147 * \param device SCCP Device
2148 *
2149 * \todo integration this function correctly into check sccp_dev_check_displayprompt
2150 *
2151 * \callgraph
2152 * \callergraph
2153 */
2154 void sccp_dev_forward_status(constLinePtr l, uint8_t lineInstance, constDevicePtr device)
2155 {
2156 #ifndef ASTDB_FAMILY_KEY_LEN
2157 #define ASTDB_FAMILY_KEY_LEN 100
2158 #endif
2159 #ifndef ASTDB_RESULT_LEN
2160 #define ASTDB_RESULT_LEN 80
2161 #endif
2162 if (!l || !device || !device->session) {
2163 return;
2164 }
2165 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: Send Forward Status. Line: %s\n", device->id, l->name);
2166
2167 //! \todo check for forward status during registration -MC
2168 //! \todo Needs to be revised. Does not make sense to call sccp_handle_AvailableLines from here
2169 if (sccp_device_getRegistrationState(device) != SKINNY_DEVICE_RS_OK) {
2170 if (!device->linesRegistered) {
2171 AUTO_RELEASE(sccp_device_t, d , sccp_device_retain(device));
2172 if (d) {
2173 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Device does not support RegisterAvailableLinesMessage, forcing this\n", DEV_ID_LOG(device));
2174 sccp_handle_AvailableLines(d->session, d, NULL);
2175 d->linesRegistered = TRUE;
2176 }
2177 }
2178 }
2179
2180 AUTO_RELEASE(sccp_linedevice_t, ld, sccp_linedevice_find(device, l));
2181 if(ld) {
2182 device->protocol->sendCallForwardStatus(device, ld);
2183 char buffer[256];
2184 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_LINE))(VERBOSE_PREFIX_3 "%s: Sent Forward Status (%s). Line: %s (%d)\n", device->id, sccp_linedevice_get_cfwd_string(ld, buffer, sizeof(buffer)), l->name,
2185 ld->lineInstance);
2186 } else {
2187 pbx_log(LOG_NOTICE, "%s: Device does not have line configured (no ld found)\n", DEV_ID_LOG(device));
2188 }
2189 }
2190
2191 /*!
2192 * \brief Handle Post Device Registration
2193 * \param data Data
2194 *
2195 * \callgraph
2196 * \callergraph
2197 *
2198 * \note adds a retained device to the event.deviceRegistered.device
2199 */
2200 void sccp_dev_postregistration(devicePtr d)
2201 {
2202
2203 #ifndef ASTDB_FAMILY_KEY_LEN
2204 #define ASTDB_FAMILY_KEY_LEN 100
2205 #endif
2206 #ifndef ASTDB_RESULT_LEN
2207 #define ASTDB_RESULT_LEN 256
2208 #endif
2209 char family[ASTDB_FAMILY_KEY_LEN] = { 0 };
2210 char buffer[ASTDB_RESULT_LEN] = { 0 };
2211 int instance = 0;
2212
2213 if (!d) {
2214 return;
2215 }
2216 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "%s: Device registered; performing post registration tasks...\n", d->id);
2217
2218 // Post event to interested listeners (hints, mwi) that device was registered.
2219 sccp_event_t *event = sccp_event_allocate(SCCP_EVENT_DEVICE_REGISTERED);
2220 if (event) {
2221 event->deviceRegistered.device = sccp_device_retain(d);
2222 sccp_event_fire(event);
2223 }
2224
2225 if (iPbx.feature_getFromDatabase) {
2226 /* read last line/device states from db */
2227 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Getting Database Settings...\n", d->id);
2228 for (instance = SCCP_FIRST_LINEINSTANCE; instance < d->lineButtons.size; instance++) {
2229 if (d->lineButtons.instance[instance]) {
2230 AUTO_RELEASE(sccp_linedevice_t, ld, sccp_linedevice_retain(d->lineButtons.instance[instance]));
2231 snprintf(family, sizeof(family), "SCCP/%s/%s", d->id, ld->line->name);
2232 for(uint x = SCCP_CFWD_ALL; x < SCCP_CFWD_SENTINEL; x++) {
2233 char cfwdstr[15] = "";
2234 snprintf(cfwdstr, 14, "cfwd%s", sccp_cfwd2str((sccp_cfwd_t)x));
2235 if(iPbx.feature_getFromDatabase(family, cfwdstr, buffer, sizeof(buffer)) && strcmp(buffer, "") != 0) {
2236 ld->cfwd[x].enabled = TRUE;
2237 sccp_copy_string(ld->cfwd[x].number, buffer, sizeof(ld->cfwd[x].number));
2238 sccp_feat_changed(d, ld, sccp_cfwd2feature((sccp_cfwd_t)x));
2239 }
2240 }
2241 }
2242 }
2243
2244 /* System Message */
2245 if (iPbx.feature_getFromDatabase("SCCP/message", "text", buffer, sizeof(buffer))) {
2246 char timebuffer[ASTDB_RESULT_LEN];
2247 int timeout = 0;
2248 if (!sccp_strlen_zero(buffer)) {
2249 if (iPbx.feature_getFromDatabase("SCCP/message", "timeout", timebuffer, sizeof(timebuffer))) {
2250 sscanf(timebuffer, "%i", &timeout);
2251 }
2252 sccp_dev_set_message(d, buffer, timeout, FALSE, FALSE);
2253 }
2254 }
2255
2256 snprintf(family, sizeof(family), "SCCP/%s", d->id);
2257 if(iPbx.feature_getFromDatabase(family, "dnd", buffer, sizeof(buffer)) && strcmp(buffer, "") != 0) {
2258 d->dndFeature.status = sccp_dndmode_str2val(buffer);
2259 sccp_feat_changed(d, NULL, SCCP_FEATURE_DND);
2260 }
2261
2262 if(iPbx.feature_getFromDatabase(family, "privacy", buffer, sizeof(buffer)) && strcmp(buffer, "") != 0) {
2263 sscanf(buffer,"%d", &d->privacyFeature.status);
2264 sccp_feat_changed(d, NULL, SCCP_FEATURE_PRIVACY);
2265 }
2266
2267 if(iPbx.feature_getFromDatabase(family, "monitor", buffer, sizeof(buffer)) && strcmp(buffer, "") != 0) {
2268 sccp_feat_monitor(d, NULL, 0, NULL);
2269 sccp_feat_changed(d, NULL, SCCP_FEATURE_MONITOR);
2270 }
2271
2272 char lastNumber[SCCP_MAX_EXTENSION] = "";
2273 if (iPbx.feature_getFromDatabase(family, "lastDialedNumber", buffer, sizeof(buffer))) {
2274 sscanf(buffer,"%79[^;];lineInstance=%d", lastNumber, &instance);
2275 AUTO_RELEASE(sccp_linedevice_t, ld, sccp_linedevice_findByLineinstance(d, instance));
2276 if(ld) {
2277 sccp_device_setLastNumberDialed(d, lastNumber, ld);
2278 }
2279 }
2280 }
2281 if (d->backgroundImage && !sccp_strlen_zero(d->backgroundImage)) {
2282 d->setBackgroundImage(d, d->backgroundImage, d->backgroundTN ? d->backgroundTN : d->backgroundImage);
2283 }
2284
2285 if (d->ringtone && !sccp_strlen_zero(d->ringtone)) {
2286 d->setRingTone(d, d->ringtone);
2287 }
2288
2289 if (d->useRedialMenu && (!d->hasDisplayPrompt() && !d->hasLabelLimitedDisplayPrompt())) {
2290 pbx_log(LOG_NOTICE, "%s: useRedialMenu is currently not supported on this devicetype. Reverting to old style redial\n", d->id);
2291 d->useRedialMenu = FALSE;
2292 }
2293
2294 for (instance = SCCP_FIRST_LINEINSTANCE; instance < d->lineButtons.size; instance++) {
2295 if (d->lineButtons.instance[instance]) {
2296 AUTO_RELEASE(sccp_linedevice_t, ld, sccp_linedevice_retain(d->lineButtons.instance[instance]));
2297 if(ld) {
2298 sccp_linedevice_indicateMWI(ld);
2299 }
2300 }
2301 }
2302 sccp_device_setMWI(d);
2303 sccp_dev_check_displayprompt(d);
2304
2305 #ifdef CS_SCCP_PARK
2306 sccp_buttonconfig_t *config = NULL;
2307 SCCP_LIST_LOCK(&d->buttonconfig);
2308 SCCP_LIST_TRAVERSE(&d->buttonconfig, config, list) {
2309 if (config->type == FEATURE && config->button.feature.id ==SCCP_FEATURE_PARKINGLOT) {
2310 if(iParkingLot.attachObserver && iParkingLot.attachObserver(d, config)) {
2311 iParkingLot.notifyDevice(d, config);
2312 }
2313 }
2314 }
2315 SCCP_LIST_UNLOCK(&d->buttonconfig);
2316 #endif
2317 if (d->useHookFlash()) {
2318 sccp_dev_setHookFlashDetect(d);
2319 }
2320 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Post registration process... done!\n", d->id);
2321 }
2322
2323 static void sccp_buttonconfig_destroy(sccp_buttonconfig_t *buttonconfig)
2324 {
2325 if (!buttonconfig) {
2326 return;
2327 }
2328 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "SCCP: (buttonconfig_destroy) destroying index:%d, type:%s (%d), pendingDelete:%s, pendingUpdate:%s\n",
2329 buttonconfig->index, sccp_config_buttontype2str(buttonconfig->type), buttonconfig->type, buttonconfig->pendingDelete ? "True" : "False", buttonconfig->pendingUpdate ? "True" : "False");
2330 if (buttonconfig->label) {
2331 sccp_free(buttonconfig->label);
2332 }
2333 switch(buttonconfig->type) {
2334 case LINE:
2335 if (buttonconfig->button.line.name) {
2336 sccp_free(buttonconfig->button.line.name);
2337 }
2338 if (buttonconfig->button.line.subscriptionId) {
2339 sccp_free(buttonconfig->button.line.subscriptionId);
2340 }
2341 if (buttonconfig->button.line.options) {
2342 sccp_free(buttonconfig->button.line.options);
2343 }
2344 break;
2345 case SPEEDDIAL:
2346 if (buttonconfig->button.speeddial.ext) {
2347 sccp_free(buttonconfig->button.speeddial.ext);
2348 }
2349 if (buttonconfig->button.speeddial.hint) {
2350 sccp_free(buttonconfig->button.speeddial.hint);
2351 }
2352 break;
2353 case SERVICE:
2354 if (buttonconfig->button.service.url) {
2355 sccp_free(buttonconfig->button.service.url);
2356 }
2357 break;
2358 case FEATURE:
2359 if (buttonconfig->button.feature.options) {
2360 sccp_free(buttonconfig->button.feature.options);
2361 }
2362 if(buttonconfig->button.feature.args) {
2363 sccp_free(buttonconfig->button.feature.args);
2364 }
2365 break;
2366 case EMPTY:
2367 case SCCP_CONFIG_BUTTONTYPE_SENTINEL:
2368 break;
2369 }
2370 sccp_free(buttonconfig);
2371 buttonconfig = NULL;
2372 }
2373
2374
2375
2376 /*!
2377 * \brief Clean Device
2378 *
2379 * clean up memory allocated by the device.
2380 * if destroy is true, device will be removed from global device list
2381 *
2382 * \param device SCCP Device
2383 * \param remove_from_global as boolean_t
2384 * \param cleanupTime Clean-up Time as uint8
2385 *
2386 * \callgraph
2387 * \callergraph
2388 *
2389 * \note adds a retained device to the event.deviceRegistered.device
2390 */
2391 void _sccp_dev_clean(devicePtr device, boolean_t remove_from_global, boolean_t restart_device)
2392 {
2393 AUTO_RELEASE(sccp_device_t, d , sccp_device_retain(device));
2394 sccp_buttonconfig_t *config = NULL;
2395 sccp_selectedchannel_t *selectedChannel = NULL;
2396 sccp_channel_t *c = NULL;
2397 int i = 0;
2398
2399 if(d) {
2400 sccp_log((DEBUGCAT_CORE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_1 "SCCP: Clean Device %s, remove from global:%s, restart_device:%s\n", d->id, remove_from_global ? "Yes" : "No", restart_device ? "Yes" : "No");
2401 sccp_device_setRegistrationState(d, SKINNY_DEVICE_RS_CLEANING);
2402 if (remove_from_global) {
2403 d->id[0] = 'X';
2404 d->id[1] = 'X';
2405 d->id[2] = 'X';
2406 sccp_device_removeFromGlobals(d);
2407 }
2408
2409 d->linesRegistered = FALSE;
2410 __saveLastDialedNumberToDatabase(d);
2411
2412 if (d->active_channel) {
2413 sccp_device_setActiveChannel(d, NULL);
2414 }
2415
2416 if (d->currentLine) {
2417 sccp_dev_setActiveLine(d, NULL);
2418 }
2419 /* hang up open channels and remove device from line */
2420 SCCP_LIST_LOCK(&d->buttonconfig);
2421 SCCP_LIST_TRAVERSE(&d->buttonconfig, config, list) {
2422 if (config->type == LINE) {
2423 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_2 "%s: checking buttonconfig index:%d, type:%s (%d) to see if there are any connected lines/channels\n",
2424 d->id, config->index, sccp_config_buttontype2str(config->type), config->type);
2425 AUTO_RELEASE(sccp_line_t, line , sccp_line_find_byname(config->button.line.name, FALSE));
2426
2427 if (!line) {
2428 continue;
2429 }
2430 SCCP_LIST_LOCK(&line->channels);
2431 SCCP_LIST_TRAVERSE_BACKWARDS_SAFE_BEGIN(&line->channels, c, list) {
2432 AUTO_RELEASE(sccp_channel_t, channel, sccp_channel_retain(c));
2433 if (channel) {
2434 AUTO_RELEASE(sccp_device_t, tmpDevice, sccp_channel_getDevice(channel));
2435 if (tmpDevice && tmpDevice == d) {
2436 pbx_log(LOG_WARNING, "SCCP: Hangup open channel on line %s device %s\n", line->name, d->id);
2437 sccp_channel_endcall(channel);
2438 }
2439 }
2440 }
2441 SCCP_LIST_TRAVERSE_BACKWARDS_SAFE_END;
2442 SCCP_LIST_UNLOCK(&line->channels);
2443
2444 /* remove devices from line */
2445 sccp_log((DEBUGCAT_CORE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_2 "SCCP: Remove Line %s from device %s\n", line->name, d->id);
2446 sccp_linedevice_remove(d, line);
2447 #ifdef CS_SCCP_PARK
2448 } else if (iParkingLot.detachObserver && config->type == FEATURE && config->button.feature.id ==SCCP_FEATURE_PARKINGLOT) {
2449 sccp_log((DEBUGCAT_DEVICE))(VERBOSE_PREFIX_2 "%s: checking buttonconfig index:%d, type:%s (%d) to see if there are any observed parkinglots\n", d->id, config->index,
2450 sccp_config_buttontype2str(config->type), config->type);
2451 iParkingLot.detachObserver(d, config);
2452 #endif
2453 }
2454 }
2455 SCCP_LIST_TRAVERSE_SAFE_BEGIN(&d->buttonconfig, config, list) {
2456 sccp_log_and((DEBUGCAT_DEVICE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_2 "%s: checking buttonconfig for pendingDelete (index:%d, type:%s (%d), pendingDelete:%s, pendingUpdate:%s)\n",
2457 d->id, config->index, sccp_config_buttontype2str(config->type), config->type, config->pendingDelete ? "True" : "False", config->pendingUpdate ? "True" : "False");
2458 config->instance = 0; /* reset button configuration to rebuild template on register */
2459 if (config->pendingDelete) {
2460 SCCP_LIST_REMOVE_CURRENT(list);
2461 sccp_buttonconfig_destroy(config);
2462 }
2463 }
2464 SCCP_LIST_TRAVERSE_SAFE_END;
2465 SCCP_LIST_UNLOCK(&d->buttonconfig);
2466
2467 sccp_log((DEBUGCAT_CORE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_2 "SCCP: Unregister Device %s\n", d->id);
2468
2469 sccp_event_t *event = sccp_event_allocate(SCCP_EVENT_DEVICE_UNREGISTERED);
2470 if (event) {
2471 event->deviceRegistered.device = sccp_device_retain(d);
2472 sccp_event_fire(event);
2473 }
2474
2475 if (SCCP_NAT_AUTO == d->nat || SCCP_NAT_AUTO_OFF == d->nat || SCCP_NAT_AUTO_ON == d->nat) {
2476 d->nat = SCCP_NAT_AUTO;
2477 }
2478
2479 /* cleanup statistics */
2480 memset(&d->configurationStatistic, 0, sizeof(d->configurationStatistic));
2481
2482 d->status.token = SCCP_TOKEN_STATE_NOTOKEN;
2483 d->registrationTime = time(0);
2484
2485 /* removing addons */
2486 if (remove_from_global) {
2487 sccp_addons_clear(d);
2488 }
2489
2490 /* removing selected channels */
2491 SCCP_LIST_LOCK(&d->selectedChannels);
2492 while ((selectedChannel = SCCP_LIST_REMOVE_HEAD(&d->selectedChannels, list))) {
2493 sccp_channel_release(&selectedChannel->channel);
2494 sccp_free(selectedChannel);
2495 }
2496 SCCP_LIST_UNLOCK(&d->selectedChannels);
2497
2498 /* release line references, refcounted in btnList */
2499 if (d->buttonTemplate) {
2500 btnlist *btn = d->buttonTemplate;
2501
2502 for (i = 0; i < StationMaxButtonTemplateSize; i++) {
2503 if ((btn[i].type == SKINNY_BUTTONTYPE_LINE) && btn[i].ptr) {
2504 sccp_line_t * tmp = btn[i].ptr; /* implicit cast without type change */
2505 sccp_line_release(&tmp);
2506 btn[i].ptr = NULL;
2507 }
2508 }
2509 sccp_free(d->buttonTemplate);
2510 d->buttonTemplate = NULL;
2511 }
2512
2513 if (device->lineButtons.size) {
2514 sccp_linedevice_deleteButtonsArray(d);
2515 }
2516 sccp_session_t *s = d->session;
2517 if (s) {
2518 if (restart_device) {
2519 sccp_device_sendReset(d, SKINNY_RESETTYPE_RESTART);
2520 //sccp_safe_sleep(100);
2521 }
2522 sccp_session_releaseDevice(s);
2523 d->session = NULL;
2524 sccp_session_stopthread(s, SKINNY_DEVICE_RS_NONE);
2525 }
2526 sccp_device_setRegistrationState(d, SKINNY_DEVICE_RS_NONE);
2527 /*
2528 #if CS_REFCOUNT_DEBUG
2529 if (remove_from_global) {
2530 pbx_str_t *buf = pbx_str_create(DEFAULT_PBX_STR_BUFFERSIZE);
2531 sccp_refcount_gen_report(device, &buf);
2532 pbx_log(LOG_NOTICE, "%s (device_clean) (realtime: %s)\nrefcount_report:\n%s\n", d->id, d && d->realtime ? "yes" : "no", pbx_str_buffer(buf));
2533 sccp_free(buf);
2534 }
2535 #endif
2536 */
2537 }
2538 }
2539
2540 /*!
2541 * \brief Free a Device as scheduled command
2542 * \param ptr SCCP Device Pointer
2543 * \return success as int
2544 *
2545 * \callgraph
2546 * \callergraph
2547 *
2548 * \called_from_asterisk
2549 *
2550 */
2551 int __sccp_device_destroy(const void *ptr)
2552 {
2553 sccp_device_t *d = (sccp_device_t *) ptr;
2554
2555 if (!d) {
2556 pbx_log(LOG_ERROR, "SCCP: Trying to destroy non-existend device\n");
2557 return -1;
2558 }
2559
2560 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_1 "%s: Destroying Device\n", d->id);
2561
2562 // cleanup dynamic allocated during sccp_config (i.e. STRINGPTR)
2563 sccp_config_cleanup_dynamically_allocated_memory(d, SCCP_CONFIG_DEVICE_SEGMENT);
2564
2565 // clean button config (only generated on read config, so do not remove during device clean)
2566 {
2567 sccp_buttonconfig_t *config = NULL;
2568 SCCP_LIST_LOCK(&d->buttonconfig);
2569 while ((config = SCCP_LIST_REMOVE_HEAD(&d->buttonconfig, list))) {
2570 sccp_buttonconfig_destroy(config);
2571 }
2572 SCCP_LIST_UNLOCK(&d->buttonconfig);
2573 if (!SCCP_LIST_EMPTY(&d->buttonconfig)) {
2574 pbx_log(LOG_WARNING, "%s: (device_destroy) there are connected buttonconfigs left during device destroy\n", d->id);
2575 }
2576 SCCP_LIST_HEAD_DESTROY(&d->buttonconfig);
2577 }
2578
2579 // clean permithosts
2580 {
2581 sccp_hostname_t *permithost = NULL;
2582 SCCP_LIST_LOCK(&d->permithosts);
2583 while ((permithost = SCCP_LIST_REMOVE_HEAD(&d->permithosts, list))) {
2584 if (permithost) {
2585 sccp_free(permithost);
2586 }
2587 }
2588 SCCP_LIST_UNLOCK(&d->permithosts);
2589 if (!SCCP_LIST_EMPTY(&d->permithosts)) {
2590 pbx_log(LOG_WARNING, "%s: (device_destroy) there are connected permithosts left during device destroy\n", d->id);
2591 }
2592 SCCP_LIST_HEAD_DESTROY(&d->permithosts);
2593 }
2594
2595 // clean selected channels
2596 {
2597 sccp_selectedchannel_t *selectedChannel = NULL;
2598 SCCP_LIST_LOCK(&d->selectedChannels);
2599 while ((selectedChannel = SCCP_LIST_REMOVE_HEAD(&d->selectedChannels, list))) {
2600 sccp_channel_release(&selectedChannel->channel);
2601 sccp_free(selectedChannel);
2602 }
2603 SCCP_LIST_UNLOCK(&d->selectedChannels);
2604 if (!SCCP_LIST_EMPTY(&d->selectedChannels)) {
2605 pbx_log(LOG_WARNING, "%s: (device_destroy) there are connected selectedChannels left during device destroy\n", d->id);
2606 }
2607 SCCP_LIST_HEAD_DESTROY(&d->selectedChannels);
2608 }
2609
2610 // cleanup ha
2611 if (d->ha) {
2612 sccp_free_ha(d->ha);
2613 d->ha = NULL;
2614 }
2615
2616 // cleanup message stack
2617 {
2618 #ifndef SCCP_ATOMIC
2619 sccp_mutex_lock(&d->messageStack.lock);
2620 #endif
2621 for(uint i = 0; i < SCCP_MESSAGE_PRIORITY_SENTINEL; i++) {
2622 if (d->messageStack.messages[i] != NULL) {
2623 sccp_free(d->messageStack.messages[i]);
2624 }
2625 }
2626 #ifndef SCCP_ATOMIC
2627 sccp_mutex_unlock(&d->messageStack.lock);
2628 pbx_mutex_destroy(&d->messageStack.lock);
2629 #endif
2630 }
2631
2632 // cleanup variables
2633 if (d->variables) {
2634 pbx_variables_destroy(d->variables);
2635 d->variables = NULL;
2636 }
2637
2638 // cleanup privateData
2639 if (d->privateData) {
2640 #if HAVE_ICONV
2641 if (d->privateData->iconv != (iconv_t) -1) {
2642 sccp_device_destroyiconv(d);
2643 }
2644 #endif
2645 sccp_mutex_destroy(&d->privateData->lock);
2646 sccp_free(d->privateData);
2647 }
2648
2649 #ifdef CS_AST_HAS_STASIS_ENDPOINT
2650 if(iPbx.endpoint_shutdown && d->endpoint) {
2651 iPbx.endpoint_shutdown(&d->endpoint);
2652 }
2653 #endif
2654
2655 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Device Destroyed\n", d->id);
2656 return 0;
2657 }
2658
2659 /*!
2660 * \brief is Video Support on a Device
2661 * \param device SCCP Device
2662 * \return result as boolean_t
2663 */
2664 boolean_t sccp_device_isVideoSupported(constDevicePtr device)
2665 {
2666 boolean_t res = FALSE;
2667 #ifdef CS_SCCP_VIDEO
2668 if (device->capabilities.video[0] != SKINNY_CODEC_NONE) {
2669 res = TRUE;
2670 }
2671 sccp_log((DEBUGCAT_CODEC)) (VERBOSE_PREFIX_3 "%s: video support %s\n", device->id, res ? "true" : "false");
2672 #endif
2673 return res;
2674 }
2675
2676 /*!
2677 * \brief Find ServiceURL by index
2678 * \param device SCCP Device
2679 * \param instance Instance as uint8_t
2680 * \return SCCP Service
2681 *
2682 */
2683 sccp_buttonconfig_t *sccp_dev_serviceURL_find_byindex(devicePtr device, uint16_t instance)
2684 {
2685 sccp_buttonconfig_t *config = NULL;
2686
2687 if (!device || !device->session) {
2688 return NULL;
2689 }
2690 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_BUTTONTEMPLATE)) (VERBOSE_PREFIX_3 "%s: searching for service with instance %d\n", device->id, instance);
2691 SCCP_LIST_LOCK(&device->buttonconfig);
2692 SCCP_LIST_TRAVERSE(&device->buttonconfig, config, list) {
2693 sccp_log_and((DEBUGCAT_DEVICE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "%s: instance: %d buttontype: %d\n", device->id, config->instance, config->type);
2694
2695 if (config->type == SERVICE && config->instance == instance) {
2696 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_BUTTONTEMPLATE)) (VERBOSE_PREFIX_3 "%s: found service: %s\n", device->id, config->label);
2697 break;
2698 }
2699 }
2700 SCCP_LIST_UNLOCK(&device->buttonconfig);
2701
2702 return config;
2703 }
2704
2705 /*!
2706 * \brief Send Reset to a Device
2707 * \param d SCCP Device
2708 * \param reset_type as int
2709 * \return Status as int
2710 */
2711 int sccp_device_sendReset(devicePtr d, skinny_resetType_t reset_type)
2712 {
2713 sccp_msg_t *msg = NULL;
2714
2715 if (!d) {
2716 return 0;
2717 }
2718
2719 REQ(msg, Reset);
2720 if (!msg) {
2721 return 0;
2722 }
2723
2724 msg->data.Reset.lel_resetType = htolel(reset_type);
2725 sccp_session_send(d, msg);
2726
2727 d->pendingUpdate = 0;
2728 return 1;
2729 }
2730
2731 /*!
2732 * \brief Send Call State to Device
2733 * \param d SCCP Device
2734 * \param instance Instance as int
2735 * \param callid Call ID as int
2736 * \param state Call State as int
2737 * \param precedence_level precedence_level as skinny_callpriority_t
2738 * \param visibility Visibility as skinny_callinfo_visibility_t
2739 *
2740 * \callgraph
2741 * \callergraph
2742 */
2743 void sccp_device_sendcallstate(constDevicePtr d, uint8_t instance, uint32_t callid, skinny_callstate_t state, skinny_callpriority_t precedence_level, skinny_callinfo_visibility_t visibility)
2744 {
2745 sccp_msg_t *msg = NULL;
2746
2747 if (!d) {
2748 return;
2749 }
2750 REQ(msg, CallStateMessage);
2751 if (!msg) {
2752 return;
2753 }
2754 msg->data.CallStateMessage.lel_callState = htolel(state);
2755 msg->data.CallStateMessage.lel_lineInstance = htolel(instance);
2756 msg->data.CallStateMessage.lel_callReference = htolel(callid);
2757 msg->data.CallStateMessage.lel_visibility = htolel(visibility);
2758 msg->data.CallStateMessage.precedence.lel_level = htolel(precedence_level);
2759 msg->data.CallStateMessage.precedence.lel_domain = htolel(0);
2760 sccp_dev_send(d, msg);
2761 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Send and Set the call state %s(%d) on call %d (visibility:%s)\n", d->id, skinny_callstate2str(state), state, callid, skinny_callinfo_visibility2str(visibility));
2762 }
2763
2764 /*!
2765 * \brief Send Call History Disposition
2766 *
2767 * \note Only works on a limitted set of devices and firmware revisions (more research needed).
2768 */
2769 void sccp_device_sendCallHistoryDisposition(constDevicePtr d, uint8_t lineInstance, uint32_t callid, skinny_callHistoryDisposition_t disposition)
2770 {
2771 sccp_msg_t *msg = NULL;
2772 if (!d) {
2773 return;
2774 }
2775 REQ(msg, CallHistoryDispositionMessage);
2776 if (!msg) {
2777 return;
2778 }
2779 msg->data.CallHistoryDispositionMessage.lel_disposition = htolel(disposition);
2780 msg->data.CallHistoryDispositionMessage.lel_lineInstance = htolel(lineInstance);
2781 msg->data.CallHistoryDispositionMessage.lel_callReference = htolel(callid);
2782 sccp_dev_send(d, msg);
2783 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Send Call History Disposition:%s on call %d\n", d->id, skinny_callHistoryDisposition2str(disposition), callid);
2784 }
2785
2786 /*!
2787 * \brief Get the number of channels that the device owns
2788 * \param device sccp device
2789 * \note device should be locked by parent functions
2790 *
2791 * \warning
2792 * - device-buttonconfig is not always locked
2793 */
2794 uint8_t sccp_device_numberOfChannels(constDevicePtr device)
2795 {
2796 sccp_buttonconfig_t *config = NULL;
2797 sccp_channel_t *c = NULL;
2798 uint8_t numberOfChannels = 0;
2799
2800 if (!device) {
2801 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "device is null\n");
2802 return 0;
2803 }
2804
2805 SCCP_LIST_TRAVERSE(&device->buttonconfig, config, list) {
2806 if (config->type == LINE) {
2807 AUTO_RELEASE(sccp_line_t, l , sccp_line_find_byname(config->button.line.name, FALSE));
2808
2809 if (!l) {
2810 continue;
2811 }
2812 SCCP_LIST_LOCK(&l->channels);
2813 SCCP_LIST_TRAVERSE(&l->channels, c, list) {
2814 AUTO_RELEASE(sccp_device_t, tmpDevice , sccp_channel_getDevice(c));
2815
2816 if (tmpDevice == device) {
2817 numberOfChannels++;
2818 }
2819 }
2820 SCCP_LIST_UNLOCK(&l->channels);
2821 }
2822 }
2823
2824 return numberOfChannels;
2825 }
2826
2827 /*!
2828 * \brief Send DTMF Tone as KeyPadButton to SCCP Device
2829 */
2830 void sccp_dev_keypadbutton(devicePtr d, char digit, uint8_t line, uint32_t callid)
2831 {
2832 sccp_msg_t *msg = NULL;
2833
2834 if (!d || !d->session) {
2835 return;
2836 }
2837 if (digit == '*') {
2838 digit = 0xe; /* See the definition of tone_list in chan_protocol.h for more info */
2839 } else if (digit == '#') {
2840 digit = 0xf;
2841 } else if (digit == '0') {
2842 digit = 0xa; /* 0 is not 0 for cisco :-) */
2843 } else {
2844 digit -= '0';
2845 }
2846
2847 if (digit > 16) {
2848 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: SCCP phones can't play this type of dtmf. Sending it inband\n", d->id);
2849 return;
2850 }
2851
2852 REQ(msg, KeypadButtonMessage);
2853 if (!msg) {
2854 return;
2855 }
2856 msg->data.KeypadButtonMessage.lel_kpButton = htolel(digit);
2857 msg->data.KeypadButtonMessage.lel_lineInstance = htolel(line);
2858 msg->data.KeypadButtonMessage.lel_callReference = htolel(callid);
2859
2860 sccp_dev_send(d, msg);
2861
2862 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: (sccp_dev_keypadbutton) Sending keypad '%02X'\n", DEV_ID_LOG(d), digit);
2863 }
2864
2865 /* Local Device Indications */
2866 static void sccp_device_indicate_onhook(constDevicePtr device, const uint8_t lineInstance, uint32_t callid)
2867 {
2868 sccp_dev_stoptone(device, lineInstance, callid);
2869 sccp_device_setLamp(device, SKINNY_STIMULUS_LINE, lineInstance, SKINNY_LAMP_OFF);
2870 sccp_dev_clearprompt(device, lineInstance, callid);
2871
2872 sccp_device_sendcallstate(device, lineInstance, callid, SKINNY_CALLSTATE_ONHOOK, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
2873 sccp_dev_set_keyset(device, 0, 0, KEYMODE_ONHOOK); /* reset the keyset of the base instance instead of current lineInstance + callid*/
2874 if (device->session) {
2875 sccp_handle_time_date_req(device->session, (sccp_device_t *) device, NULL); /** we need datetime on hangup for 7936 */
2876 }
2877
2878 sccp_device_clearMessageFromStack((sccp_device_t *) device, SCCP_MESSAGE_PRIORITY_PRIVACY);
2879 if (device->active_channel && device->active_channel->callid == callid) {
2880 sccp_dev_set_speaker(device, SKINNY_STATIONSPEAKER_OFF);
2881 }
2882 sccp_dev_set_ringer(device, SKINNY_RINGTYPE_OFF, SKINNY_RINGDURATION_NORMAL, lineInstance, callid);
2883 }
2884 /* currently unused and out of sync with sccp_indications.c */
2885 static void sccp_device_indicate_offhook(constDevicePtr device, sccp_linedevice_t * ld, uint32_t callid)
2886 {
2887 sccp_dev_set_speaker(device, SKINNY_STATIONSPEAKER_ON);
2888 if (device->dndFeature.status == SCCP_DNDMODE_OFF && device->monitorFeature.status == SCCP_FEATURE_MONITOR_STATE_DISABLED) {
2889 sccp_device_sendcallstate(device, ld->lineInstance, callid, SKINNY_CALLSTATE_OFFHOOK, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
2890 } else {
2891 sccp_device_sendcallstate(device, ld->lineInstance, callid, SKINNY_CALLSTATE_CALLREMOTEMULTILINE, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
2892 }
2893 sccp_dev_set_cplane(device, ld->lineInstance, 1);
2894 sccp_dev_displayprompt(device, ld->lineInstance, callid, SKINNY_DISP_ENTER_NUMBER, GLOB(digittimeout));
2895 sccp_dev_set_keyset(device, ld->lineInstance, callid, KEYMODE_OFFHOOK);
2896 sccp_dev_starttone(device, ld->line->initial_dialtone_tone, ld->lineInstance, callid, SKINNY_TONEDIRECTION_USER);
2897 }
2898
2899 static void sccp_device_indicate_dialing(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_calltype_t calltype, sccp_callinfo_t * const callinfo, char dialedNumber[SCCP_MAX_EXTENSION])
2900 {
2901 sccp_dev_stoptone(device, lineInstance, callid);
2902 sccp_device_setLamp(device, SKINNY_STIMULUS_LINE, lineInstance, SKINNY_LAMP_BLINK);
2903 iCallInfo.Setter(callinfo, SCCP_CALLINFO_CALLEDPARTY_NUMBER, dialedNumber, SCCP_CALLINFO_KEY_SENTINEL);
2904 iCallInfo.Send(callinfo, callid, calltype, lineInstance, device, FALSE);
2905
2906 if (device->protocol && device->protocol->sendDialedNumber) {
2907 device->protocol->sendDialedNumber(device, lineInstance, callid, dialedNumber);
2908 }
2909 sccp_device_sendcallstate(device, lineInstance, callid, SKINNY_CALLSTATE_PROCEED, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
2910 }
2911
2912 static void sccp_device_indicate_proceed(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_calltype_t calltype, sccp_callinfo_t * const callinfo)
2913 {
2914 sccp_dev_stoptone(device, lineInstance, callid);
2915 sccp_device_sendcallstate(device, lineInstance, callid, SKINNY_CALLSTATE_PROCEED, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
2916 iCallInfo.Send(callinfo, callid, calltype, lineInstance, device, FALSE);
2917 sccp_dev_displayprompt(device, lineInstance, callid, SKINNY_DISP_CALL_PROCEED, GLOB(digittimeout));
2918 }
2919
2920 static void sccp_device_indicate_connected(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_calltype_t calltype, sccp_callinfo_t * const callinfo)
2921 {
2922 sccp_dev_set_ringer(device, SKINNY_RINGTYPE_OFF, SKINNY_RINGDURATION_NORMAL, lineInstance, callid);
2923 sccp_dev_set_speaker(device, SKINNY_STATIONSPEAKER_ON);
2924 sccp_dev_stoptone(device, lineInstance, callid);
2925 sccp_device_setLamp(device, SKINNY_STIMULUS_LINE, lineInstance, SKINNY_LAMP_ON);
2926 sccp_device_sendcallstate(device, lineInstance, callid, SKINNY_CALLSTATE_CONNECTED, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
2927 iCallInfo.Send(callinfo, callid, calltype, lineInstance, device, TRUE);
2928 sccp_dev_set_cplane(device, lineInstance, 1);
2929 sccp_dev_displayprompt(device, lineInstance, callid, SKINNY_DISP_CONNECTED, GLOB(digittimeout));
2930 }
2931
2932 static void sccp_device_old_callhistory(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_callHistoryDisposition_t disposition)
2933 {
2934 skinny_callstate_t state=SKINNY_CALLSTATE_CONNECTED;
2935 skinny_callinfo_visibility_t visibility=SKINNY_CALLINFO_VISIBILITY_HIDDEN;
2936 sccp_log((DEBUGCAT_CALLINFO)) (VERBOSE_PREFIX_3 "%s: callhistory: entry of callid:%d on lineInstace:%d, disposition:%s\n", device->id, callid, lineInstance, skinny_callHistoryDisposition2str(disposition));
2937 switch(disposition) {
2938 case SKINNY_CALL_HISTORY_DISPOSITION_RECEIVED_CALLS:
2939 state=SKINNY_CALLSTATE_CONNECTED;
2940 visibility=SKINNY_CALLINFO_VISIBILITY_COLLAPSED;
2941 break;
2942 case SKINNY_CALL_HISTORY_DISPOSITION_MISSED_CALLS:
2943 state=SKINNY_CALLSTATE_RINGIN;
2944 visibility=SKINNY_CALLINFO_VISIBILITY_COLLAPSED;
2945 break;
2946 case SKINNY_CALL_HISTORY_DISPOSITION_IGNORE:
2947 case SKINNY_CALL_HISTORY_DISPOSITION_PLACED_CALLS:
2948 case SKINNY_CALL_HISTORY_DISPOSITION_UNKNOWN:
2949 case SKINNY_CALLHISTORYDISPOSITION_SENTINEL:
2950 state=SKINNY_CALLSTATE_CONNECTED;
2951 visibility=SKINNY_CALLINFO_VISIBILITY_HIDDEN;
2952 break;
2953 }
2954 sccp_device_sendcallstate(device, lineInstance, callid, state, SKINNY_CALLPRIORITY_LOW, visibility);
2955 }
2956
2957 static void sccp_device_new_callhistory(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, const skinny_callHistoryDisposition_t disposition)
2958 {
2959 sccp_log((DEBUGCAT_CALLINFO)) (VERBOSE_PREFIX_3 "%s: callhistory: entry of callid:%d on lineInstace:%d, disposition:%s\n", device->id, callid, lineInstance, skinny_callHistoryDisposition2str(disposition));
2960 sccp_device_sendCallHistoryDisposition(device, lineInstance, callid, disposition);
2961 }
2962 /** End Local Device Indications **/
2963
2964 /* Remote Device Indications */
2965 static void sccp_device_indicate_onhook_remote(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid)
2966 {
2967 sccp_device_setLamp(device, SKINNY_STIMULUS_LINE, lineInstance, SKINNY_LAMP_OFF);
2968 sccp_dev_cleardisplaynotify(device);
2969 sccp_dev_clearprompt(device, lineInstance, callid);
2970 sccp_dev_set_ringer(device, SKINNY_RINGTYPE_OFF, SKINNY_RINGDURATION_NORMAL, lineInstance, callid);
2971 sccp_device_sendcallstate(device, lineInstance, callid, SKINNY_CALLSTATE_ONHOOK, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
2972 sccp_dev_set_keyset(device, lineInstance, callid, KEYMODE_ONHOOK);
2973 sccp_dev_set_cplane(device, lineInstance, 0);
2974 sccp_dev_set_keyset(device, lineInstance, callid, KEYMODE_ONHOOK);
2975 if (device->session) {
2976 sccp_handle_time_date_req(device->session, (sccp_device_t *) device, NULL); /** we need datetime on hangup for 7936 */
2977 }
2978 }
2979
2980 static void sccp_device_indicate_offhook_remote(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid)
2981 {
2982 sccp_device_sendcallstate(device, lineInstance, callid, SKINNY_CALLSTATE_OFFHOOK, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
2983 sccp_dev_set_keyset(device, lineInstance, callid, KEYMODE_OFFHOOK);
2984 }
2985
2986
2987 static void sccp_device_indicate_connected_remote(constDevicePtr device, const uint8_t lineInstance, const uint32_t callid, skinny_callinfo_visibility_t visibility)
2988 {
2989 sccp_dev_set_ringer(device, SKINNY_RINGTYPE_OFF, SKINNY_RINGDURATION_NORMAL, lineInstance, callid);
2990 sccp_dev_clearprompt(device, lineInstance, callid);
2991 sccp_device_setLamp(device, SKINNY_STIMULUS_LINE, lineInstance, SKINNY_LAMP_ON);
2992 sccp_device_sendcallstate(device, lineInstance, callid, SKINNY_CALLSTATE_CALLREMOTEMULTILINE, SKINNY_CALLPRIORITY_LOW, visibility);
2993 sccp_dev_set_keyset(device, lineInstance, callid, KEYMODE_ONHOOKSTEALABLE);
2994 }
2995
2996 /*!
2997 * \brief Indicate to device that remote side has been put on hold (old).
2998 */
2999 static void sccp_device_old_indicate_remoteHold(constDevicePtr device, uint8_t lineInstance, uint32_t callid, skinny_callpriority_t callpriority, skinny_callinfo_visibility_t visibility)
3000 {
3001 sccp_device_sendcallstate(device, lineInstance, callid, SKINNY_CALLSTATE_HOLD, callpriority, visibility);
3002 sccp_dev_set_keyset(device, lineInstance, callid, KEYMODE_ONHOLD);
3003 sccp_dev_displayprompt(device, lineInstance, callid, SKINNY_DISP_HOLD, GLOB(digittimeout));
3004 }
3005
3006 /*!
3007 * \brief Indicate to device that remote side has been put on hold (new).
3008 */
3009 static void sccp_device_new_indicate_remoteHold(constDevicePtr device, uint8_t lineInstance, uint32_t callid, skinny_callpriority_t callpriority, skinny_callinfo_visibility_t visibility)
3010 {
3011 sccp_device_sendcallstate(device, lineInstance, callid, SKINNY_CALLSTATE_HOLDRED, callpriority, visibility);
3012 sccp_dev_set_keyset(device, lineInstance, callid, KEYMODE_ONHOLD);
3013 sccp_dev_displayprompt(device, lineInstance, callid, SKINNY_DISP_HOLD, GLOB(digittimeout));
3014 }
3015 /** End Remote Device Indications **/
3016
3017 /*!
3018 * \brief Add message to the MessageStack to be shown on the Status Line of the SCCP Device
3019 */
3020 void sccp_device_addMessageToStack(devicePtr device, const uint8_t priority, const char *message)
3021 {
3022 // sccp_log((DEBUGCAT_CORE + DEBUGCAT_DEVICE + DEBUGCAT_MESSAGE)) (VERBOSE_PREFIX_1 "%s: (sccp_device_addMessageToStack), '%s' at priority %d \n", DEV_ID_LOG(device), message, priority);
3023 if (ARRAY_LEN(device->messageStack.messages) <= priority) {
3024 return;
3025 }
3026 char * newValue = NULL;
3027 char * oldValue = NULL;
3028
3029 newValue = pbx_strdup(message);
3030
3031 do {
3032 oldValue = device->messageStack.messages[priority];
3033 } while (!CAS_PTR(&device->messageStack.messages[priority], oldValue, newValue, &device->messageStack.lock));
3034
3035 if (oldValue) {
3036 sccp_free(oldValue);
3037 }
3038 sccp_dev_check_displayprompt(device);
3039 }
3040
3041 /*!
3042 * \brief Remove a message from the MessageStack to be shown on the Status Line of the SCCP Device
3043 */
3044 void sccp_device_clearMessageFromStack(devicePtr device, const uint8_t priority)
3045 {
3046 if (ARRAY_LEN(device->messageStack.messages) <= priority) {
3047 return;
3048 }
3049
3050 char * newValue = NULL;
3051 char * oldValue = NULL;
3052
3053 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_4 "%s: clear message stack %d\n", DEV_ID_LOG(device), priority);
3054
3055 do {
3056 oldValue = device->messageStack.messages[priority];
3057 } while (!CAS_PTR(&device->messageStack.messages[priority], oldValue, newValue, &device->messageStack.lock));
3058
3059 if (oldValue) {
3060 sccp_free(oldValue);
3061 sccp_dev_check_displayprompt(device);
3062 }
3063 }
3064
3065 /*!
3066 * \brief Handle Feature Change Event for persistent feature storage
3067 * \param event SCCP Event
3068 *
3069 * \callgraph
3070 * \callergraph
3071 *
3072 * \warning
3073 * - device->buttonconfig is not always locked
3074 * - line->devices is not always locked
3075 */
3076 void sccp_device_featureChangedDisplay(const sccp_event_t * event)
3077 {
3078 sccp_linedevice_t * ld = NULL;
3079 sccp_device_t * device = NULL;
3080
3081 char tmp[256] = { 0 };
3082 size_t len = sizeof(tmp);
3083 char *s = tmp;
3084
3085 if (!event || !(device = event->featureChanged.device)) {
3086 return;
3087 }
3088 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_EVENT + DEBUGCAT_FEATURE)) (VERBOSE_PREFIX_3 "%s: Received Feature Change Event: %s(%d)\n", DEV_ID_LOG(device), sccp_feature_type2str(event->featureChanged.featureType), event->featureChanged.featureType);
3089 switch (event->featureChanged.featureType) {
3090 case SCCP_FEATURE_CFWDNONE:
3091 sccp_device_clearMessageFromStack(device, SCCP_MESSAGE_PRIORITY_CFWD);
3092 break;
3093 case SCCP_FEATURE_CFWDBUSY:
3094 case SCCP_FEATURE_CFWDALL:
3095 case SCCP_FEATURE_CFWDNOANSWER:
3096 if((ld = event->featureChanged.optional_linedevice)) {
3097 linePtr line = ld->line;
3098 uint8_t instance = ld->lineInstance;
3099
3100 sccp_dev_forward_status(line, instance, device);
3101 for(uint x = SCCP_CFWD_ALL; x < SCCP_CFWD_SENTINEL; x++) {
3102 if(ld->cfwd[x].enabled) {
3103 sccp_cfwd_t cfwd_type = (sccp_cfwd_t)x;
3104 if(sccp_strlen(line->cid_num) + sccp_strlen(ld->cfwd[x].number) > 15) {
3105 pbx_build_string(&s, &len, "%s:%s", sccp_cfwd2disp(cfwd_type), ld->cfwd[x].number);
3106 } else {
3107 pbx_build_string(&s, &len, "%s:%s %s %s", sccp_cfwd2disp(cfwd_type), line->cid_num, SKINNY_DISP_FORWARDED_TO, ld->cfwd[x].number);
3108 }
3109 }
3110 }
3111 }
3112 if (!sccp_strlen_zero(tmp)) {
3113 sccp_device_addMessageToStack(device, SCCP_MESSAGE_PRIORITY_CFWD, tmp);
3114 } else {
3115 sccp_device_clearMessageFromStack(device, SCCP_MESSAGE_PRIORITY_CFWD);
3116 }
3117 break;
3118 case SCCP_FEATURE_DND:
3119 if(device->hasLabelLimitedDisplayPrompt()) {
3120 sccp_dev_displayprompt(device, 0, 0, SKINNY_DISP_YOUR_CURRENT_OPTIONS, 0);
3121 }
3122 if (device->dndFeature.status) {
3123 char dndmsg[StationMaxDisplayNotifySize];
3124 if (!device->dndmode) { // running in try state/cycle mode
3125 if (device->dndFeature.status == SCCP_DNDMODE_SILENT) {
3126 snprintf(dndmsg, sizeof(dndmsg), SKINNY_DISP_DND " (" SKINNY_DISP_SILENT ")");
3127 } else {
3128 snprintf(dndmsg, sizeof(dndmsg), SKINNY_DISP_DND " (" SKINNY_DISP_BUSY ")");
3129 }
3130 } else {
3131 snprintf(dndmsg, sizeof(dndmsg), SKINNY_DISP_DO_NOT_DISTURB_IS_ACTIVE);
3132 }
3133 if (device->hasLabelLimitedDisplayPrompt() && device->hasDisplayPrompt()) { // 69xx series
3134 sccp_device_addMessageToStack(device, SCCP_MESSAGE_PRIORITY_DND, SKINNY_DISP_DO_NOT_DISTURB_IS_ACTIVE);
3135 if (!device->dndmode) { // popup with precise state
3136 sccp_dev_displaynotify(device, dndmsg, 3);
3137 }
3138 } else { // 79xx and 89xx series
3139 sccp_device_addMessageToStack(device, SCCP_MESSAGE_PRIORITY_DND, dndmsg);
3140 }
3141 } else {
3142 sccp_device_clearMessageFromStack(device, SCCP_MESSAGE_PRIORITY_DND);
3143 }
3144 break;
3145 case SCCP_FEATURE_PRIVACY:
3146 if (TRUE == device->privacyFeature.status) {
3147 sccp_device_addMessageToStack(device, SCCP_MESSAGE_PRIORITY_PRIVACY, SKINNY_DISP_PRIVATE);
3148 } else {
3149 sccp_device_clearMessageFromStack(device, SCCP_MESSAGE_PRIORITY_PRIVACY);
3150 }
3151 break;
3152 case SCCP_FEATURE_MONITOR:
3153 if (device->monitorFeature.status & (SCCP_FEATURE_MONITOR_STATE_REQUESTED | SCCP_FEATURE_MONITOR_STATE_ACTIVE)) {
3154 //sccp_device_addMessageToStack(device, SCCP_MESSAGE_PRIORITY_MONITOR, SKINNY_DISP_RECORDING);
3155 sccp_dev_set_message(device, SKINNY_DISP_RECORDING, SCCP_DISPLAYSTATUS_TIMEOUT, FALSE, FALSE);
3156 } else if (device->monitorFeature.status & SCCP_FEATURE_MONITOR_STATE_REQUESTED) {
3157 sccp_device_addMessageToStack(device, SCCP_MESSAGE_PRIORITY_MONITOR, SKINNY_DISP_RECORDING_AWAITING_CALL_TO_BE_ACTIVE);
3158 } else {
3159 sccp_device_clearMessageFromStack(device, SCCP_MESSAGE_PRIORITY_MONITOR);
3160 }
3161 break;
3162 case SCCP_FEATURE_PARKINGLOT:
3163 break;
3164 default:
3165 return;
3166 }
3167
3168 }
3169
3170 /*!
3171 * \brief Push a URL to an SCCP device
3172 */
3173 static sccp_push_result_t sccp_device_pushURL(constDevicePtr device, const char *url, uint8_t priority, skinny_tone_t tone)
3174 {
3175 const char *xmlFormat = "<CiscoIPPhoneExecute><ExecuteItem Priority=\"0\" URL=\"%s\"/></CiscoIPPhoneExecute>";
3176 size_t msg_length = strlen(xmlFormat) + sccp_strlen(url) - 2 /* for %s */ + 1 /* for terminator */ ;
3177 unsigned int transactionID = sccp_random();
3178
3179 if (sccp_strlen(url) > 256) {
3180 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: (pushURL) url is to long (max 256 char).\n", DEV_ID_LOG(device));
3181 return SCCP_PUSH_RESULT_FAIL;
3182 }
3183 char xmlData[msg_length];
3184
3185 snprintf(xmlData, msg_length, xmlFormat, url);
3186 device->protocol->sendUserToDeviceDataVersionMessage(device, APPID_PUSH, 0, 1, transactionID, xmlData, priority);
3187 if (SKINNY_TONE_SILENCE != tone) {
3188 sccp_dev_starttone(device, tone, 0, 0, SKINNY_TONEDIRECTION_USER);
3189 }
3190 return SCCP_PUSH_RESULT_SUCCESS;
3191 }
3192
3193 /*!
3194 * \brief Push a Text Message to an SCCP device
3195 *
3196 * \note
3197 * title field can be max 32 characters long
3198 * protocolversion < 17 allows for maximum of 1024 characters in the text block / maximum 2000 characted in overall message
3199 * protocolversion > 17 allows variable sized messages up to 4000 char in the text block (using multiple messages if necessary)
3200 */
3201 static sccp_push_result_t sccp_device_pushTextMessage(constDevicePtr device, const char *messageText, const char *from, uint8_t priority, skinny_tone_t tone)
3202 {
3203 const char *xmlFormat = "<CiscoIPPhoneText>%s<Text>%s</Text></CiscoIPPhoneText>";
3204 size_t msg_length = strlen(xmlFormat) + sccp_strlen(messageText) - 4 /* for the %s' */ + 1 /* for terminator */ ;
3205 unsigned int transactionID = sccp_random();
3206
3207 if (sccp_strlen(from) > 32) {
3208 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: (pushTextMessage) from is to long (max 32 char).\n", DEV_ID_LOG(device));
3209 return SCCP_PUSH_RESULT_FAIL;
3210 }
3211
3212 if ((device->protocolversion < 17 && 1024 > msg_length) || sccp_strlen(messageText) > 4000) {
3213 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: (pushTextMessage) messageText is to long.\n", DEV_ID_LOG(device));
3214 return SCCP_PUSH_RESULT_FAIL;
3215 }
3216
3217 const char *xmlTitleFormat = "<Title>%s</Title>";
3218 size_t title_length = strlen(xmlTitleFormat) + sccp_strlen(from) - 2 /* for the %s */ + 1 /* for terminator */ ;
3219 char title[title_length];
3220
3221 if (!sccp_strlen_zero(from)) {
3222 msg_length += title_length;
3223 snprintf(title, title_length, xmlTitleFormat, from);
3224 }
3225
3226 char xmlData[msg_length];
3227
3228 snprintf(xmlData, msg_length, xmlFormat, title, messageText);
3229 device->protocol->sendUserToDeviceDataVersionMessage(device, APPID_PUSH, 0, 1, transactionID, xmlData, priority);
3230
3231 if (SKINNY_TONE_SILENCE != tone) {
3232 sccp_dev_starttone(device, tone, 0, 0, SKINNY_TONEDIRECTION_USER);
3233 }
3234 return SCCP_PUSH_RESULT_SUCCESS;
3235 }
3236
3237 /*=================================================================================== FIND FUNCTIONS ==============*/
3238
3239 /*!
3240 * \brief Find Device by Line Index
3241 * \param d SCCP Device
3242 * \param lineName Line Name as char
3243 * \return Status as int
3244 * \note device should be locked by parent fuction
3245 *
3246 * \warning
3247 * - device->buttonconfig is not always locked
3248 */
3249 uint8_t __PURE__ sccp_device_find_index_for_line(constDevicePtr d, const char *lineName)
3250 {
3251 for(uint8_t instance = SCCP_FIRST_LINEINSTANCE; instance < d->lineButtons.size; instance++) {
3252 if (d->lineButtons.instance[instance] && d->lineButtons.instance[instance]->line && !strcasecmp(d->lineButtons.instance[instance]->line->name, lineName)) {
3253 return instance;
3254 }
3255 }
3256 return 0;
3257 }
3258
3259 gcc_inline int16_t sccp_device_buttonIndex2lineInstance(constDevicePtr d, uint16_t buttonIndex)
3260 {
3261 if (buttonIndex > 0 && buttonIndex < StationMaxButtonTemplateSize && d->buttonTemplate[buttonIndex - 1].instance) {
3262 return d->buttonTemplate[buttonIndex - 1].instance;
3263 }
3264 pbx_log(LOG_ERROR, "%s: buttonIndex2lineInstance for buttonIndex:%d failed!\n", d->id, buttonIndex);
3265 return -1;
3266 }
3267
3268 /*!
3269 * \brief Find Device by ID
3270 *
3271 * \callgraph
3272 * \callergraph
3273 *
3274 * \param id Device ID (SEP.....)
3275 * \param useRealtime Use RealTime as Boolean
3276 * \return SCCP Device - can bee null if device is not found
3277 */
3278 devicePtr sccp_device_find_byid(const char * id, boolean_t useRealtime)
3279 {
3280 sccp_device_t *d = NULL;
3281
3282 if (sccp_strlen_zero(id)) {
3283 sccp_log((DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "SCCP: Not allowed to search for device with name ''\n");
3284 return NULL;
3285 }
3286
3287 SCCP_RWLIST_RDLOCK(&GLOB(devices));
3288 d = SCCP_RWLIST_FIND(&GLOB(devices), sccp_device_t, tmpd, list, (sccp_strcaseequals(tmpd->id, id)), TRUE, __FILE__, __LINE__, __PRETTY_FUNCTION__);
3289 SCCP_RWLIST_UNLOCK(&GLOB(devices));
3290
3291 #ifdef CS_SCCP_REALTIME
3292 if (!d && useRealtime) {
3293 d = sccp_device_find_realtime_byid(id);
3294 }
3295 #endif
3296
3297 return d;
3298 }
3299
3300 #ifdef CS_SCCP_REALTIME
3301 /*!
3302 * \brief Find Device via RealTime
3303 *
3304 * \callgraph
3305 * \callergraph
3306 */
3307 #if DEBUG
3308 /*!
3309 * \param name Device ID (hostname)
3310 * \param filename Debug FileName
3311 * \param lineno Debug LineNumber
3312 * \param func Debug Function Name
3313 * \return SCCP Device - can bee null if device is not found
3314 */
3315 devicePtr __sccp_device_find_realtime(const char * name, const char * filename, int lineno, const char * func)
3316 # else
3317 /*!
3318 * \param name Device ID (hostname)
3319 * \return SCCP Device - can bee null if device is not found
3320 */
3321 devicePtr sccp_device_find_realtime(const char * name)
3322 # endif
3323 {
3324 sccp_device_t *d = NULL;
3325 PBX_VARIABLE_TYPE *v = NULL, *variable = NULL;
3326
3327 if (sccp_strlen_zero(GLOB(realtimedevicetable)) || sccp_strlen_zero(name)) {
3328 return NULL;
3329 }
3330 if ((variable = pbx_load_realtime(GLOB(realtimedevicetable), "name", name, NULL))) {
3331 v = variable;
3332 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_REALTIME)) (VERBOSE_PREFIX_3 "SCCP: Device '%s' found in realtime table '%s'\n", name, GLOB(realtimedevicetable));
3333
3334 d = sccp_device_create(name); /** create new device */
3335 if (!d) {
3336 pbx_log(LOG_ERROR, "SCCP: Unable to build realtime device '%s'\n", name);
3337 return NULL;
3338 }
3339 // sccp_copy_string(d->id, name, sizeof(d->id));
3340
3341 sccp_config_applyDeviceConfiguration(d, v); /** load configuration and set defaults */
3342
3343 // sccp_config_restoreDeviceFeatureStatus(d); /** load device status from database */
3344
3345 sccp_device_addToGlobals(d); /** add to device to global device list */
3346
3347 d->realtime = TRUE; /** set device as realtime device */
3348 pbx_variables_destroy(v);
3349
3350 return d;
3351 }
3352
3353 sccp_log((DEBUGCAT_DEVICE + DEBUGCAT_REALTIME)) (VERBOSE_PREFIX_3 "SCCP: Device '%s' not found in realtime table '%s'\n", name, GLOB(realtimedevicetable));
3354 return NULL;
3355 }
3356 #endif
3357
3358 void sccp_device_setLamp(constDevicePtr device, skinny_stimulus_t stimulus, uint8_t instance, skinny_lampmode_t mode)
3359 {
3360 sccp_msg_t *msg = NULL;
3361
3362 REQ(msg, SetLampMessage);
3363
3364 if (msg) {
3365 msg->data.SetLampMessage.lel_stimulus = htolel(stimulus);
3366 msg->data.SetLampMessage.lel_stimulusInstance = instance;
3367 msg->data.SetLampMessage.lel_lampMode = htolel(mode);
3368 sccp_dev_send(device, msg);
3369 }
3370 }
3371
3372 void sccp_device_setMWI(devicePtr device)
3373 {
3374 device->voicemailStatistic.newmsgs = 0;
3375 device->voicemailStatistic.oldmsgs = 0;
3376 for (uint8_t instance = SCCP_FIRST_LINEINSTANCE; instance < device->lineButtons.size; instance++) {
3377 if(device->lineButtons.instance[instance]) {
3378 linePtr l = device->lineButtons.instance[instance]->line;
3379 device->voicemailStatistic.newmsgs += l->voicemailStatistic.newmsgs;
3380 device->voicemailStatistic.oldmsgs += l->voicemailStatistic.oldmsgs;
3381 }
3382 }
3383 sccp_log((DEBUGCAT_MWI)) (VERBOSE_PREFIX_3 "%s: (sccp_device_setMWI), newmsgs:%d, oldmsgs:%d\n", device->id, device->voicemailStatistic.newmsgs, device->voicemailStatistic.oldmsgs);
3384 device->mwiUpdateRequired = TRUE;
3385 sccp_device_indicateMWI(device);
3386 }
3387
3388 /*!
3389 * Temporarily suppress MWI output during call
3390 */
3391 void sccp_device_suppressMWI(devicePtr device)
3392 {
3393 if (!device->mwioncall) {
3394 sccp_log((DEBUGCAT_MWI)) (VERBOSE_PREFIX_3 "%s: (sccp_device_suppressMWI)\n", device->id);
3395 device->mwiUpdateRequired = TRUE;
3396 sccp_device_setLamp(device, SKINNY_STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
3397 }
3398 }
3399
3400 void sccp_device_indicateMWI(devicePtr device)
3401 {
3402 sccp_log((DEBUGCAT_MWI)) (VERBOSE_PREFIX_3 "%s: (sccp_device_indicateMWI) indication update required:%s\n", device->id, device->mwiUpdateRequired ? "yes" : "no");
3403 if (device->mwiUpdateRequired) {
3404 sccp_log((DEBUGCAT_MWI)) (VERBOSE_PREFIX_3 "%s: (sccp_device_indicateMWI) Set main voicemail lamp:%s\n", device->id,
3405 device->voicemailStatistic.newmsgs ? "on" : "off");
3406 sccp_device_setLamp(device, SKINNY_STIMULUS_VOICEMAIL, 0, device->voicemailStatistic.newmsgs ? device->mwilamp : SKINNY_LAMP_OFF);
3407
3408 if (device->voicemailStatistic.newmsgs || device->voicemailStatistic.oldmsgs) {
3409 sccp_log((DEBUGCAT_MWI)) (VERBOSE_PREFIX_3 "%s: (sccp_device_indicateMWI) Set Have Voicemail on Display\n", device->id);
3410 char buffer[StationMaxDisplayTextSize];
3411 snprintf(buffer, StationMaxDisplayTextSize, "%s: (%u/%u)", SKINNY_DISP_YOU_HAVE_VOICEMAIL, device->voicemailStatistic.newmsgs, device->voicemailStatistic.oldmsgs);
3412 sccp_device_addMessageToStack(device, SCCP_MESSAGE_PRIORITY_VOICEMAIL, buffer);
3413 } else {
3414 sccp_log((DEBUGCAT_MWI)) (VERBOSE_PREFIX_3 "%s: (sccp_device_indicateMWI) Remove Have Voicemail from Display\n", device->id);
3415 sccp_device_clearMessageFromStack(device, SCCP_MESSAGE_PRIORITY_VOICEMAIL);
3416 }
3417 }
3418 }
3419 // kate: indent-width 4; replace-tabs off; indent-mode cstyle; auto-insert-doxygen on; line-numbers on; tab-indents on; keep-extra-spaces off; auto-brackets on;
3420