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