1 /*!
2  * \file        sccp_config.c
3  * \brief       SCCP Config 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  * \note        To find out more about the reload function see \ref sccp_config_reload
11  * \remarks     Only methods directly related to chan-sccp configuration should be stored in this source file.
12  *
13  */
14 
15 /*!
16  * \section sccp_config Loading sccp.conf/realtime configuration implementation
17  *
18  * \subsection sccp_config_reload How was the new cli command "sccp reload" implemented
19  *
20  * sccp_cli.c
21  * - new implementation of cli reload command
22  *  - checks if no other reload command is currently running
23  *  - starts loading global settings from sccp.conf (sccp_config_general)
24  *  - starts loading devices and lines from sccp.conf(sccp_config_readDevicesLines)
25  *  .
26  * .
27  *
28  * sccp_config.c
29  * - modified sccp_config_general
30  *
31  * - modified sccp_config_readDevicesLines
32  *  - sets pendingDelete for
33  *    - devices (via sccp_device_pre_reload),
34  *    - lines (via sccp_line_pre_reload)
35  *    - softkey (via sccp_softkey_pre_reload)
36  *    .
37  *  - calls sccp_config_buildDevice as usual
38  *    - calls sccp_config_buildDevice as usual
39  *      - find device
40  *      - or create new device
41  *      - parses sccp.conf for device
42  *      - set defaults for device if necessary using the default from globals using the same parameter name
43  *      - set pendingUpdate on device for parameters marked with SCCP_CONFIG_NEEDDEVICERESET (remove pendingDelete)
44  *      .
45  *    - calls sccp_config_buildLine as usual
46  *      - find line
47  *      - or create new line
48  *      - parses sccp.conf for line
49  *      - set defaults for line if necessary using the default from globals using the same parameter name
50  *      - set pendingUpdate on line for parameters marked with SCCP_CONFIG_NEEDDEVICERESET (remove pendingDelete)
51  *      .
52  *    - calls sccp_config_softKeySet as usual ***
53  *      - find softKeySet
54  *      - or create new softKeySet
55  *      - parses sccp.conf for softKeySet
56  *      - set pendingUpdate on softKetSet for parameters marked with SCCP_CONFIG_NEEDDEVICERESET (remove pendingDelete)
57  *      .
58  *    .
59  *  - checks pendingDelete and pendingUpdate for
60  *    - skip when call in progress
61  *    - devices (via sccp_device_post_reload),
62  *      - resets GLOB(device) if pendingUpdate
63  *      - removes GLOB(devices) with pendingDelete
64  *      .
65  *    - lines (via sccp_line_post_reload)
66  *      - resets GLOB(lines) if pendingUpdate
67  *      - removes GLOB(lines) with pendingDelete
68  *      .
69  *    - softkey (via sccp_softkey_post_reload) ***
70  *      - resets GLOB(softkeyset) if pendingUpdate ***
71  *      - removes GLOB(softkeyset) with pendingDelete ***
72  *      .
73  *    .
74  *  .
75  * channel.c
76  * - sccp_channel_endcall ***
77  *   - reset device if still device->pendingUpdate,line->pendingUpdate or softkeyset->pendingUpdate
78  *   .
79  * .
80  *
81  * lines marked with "***" still need be implemented
82  *
83  */
84 
85 /*** DOCUMENTATION
86 	<manager name="SCCPConfigMetaData" language="en_US">
87 		<synopsis>Retrieve config metadata in json format</synopsis>
88 		<syntax>
89 			<xi:include href="../core-en_US.xml" parse="xml"
90 				xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])"/>
91 			<parameter name="segment" required="false">
92 				<para>The name of the segment you are interest, leaving it empty will list the segments available.</para>
93 				<enumlist>
94 					<enum name="general"/>
95 					<enum name="device"/>
96 					<enum name="line"/>
97 					<enum name="softkey"/>
98 				</enumlist>
99 			</parameter>
100 			<parameter name="ResultFormat">
101 				<para/>
102 				<enumlist>
103 					<enum name="list">
104 						<para>Will return Output will SCCPConfigMetaData managerevent</para>
105 					</enum>
106 					<enum name="command">
107 						<para>Will return json formatted string presented in the 'DataType' field.</para>
108 					</enum>
109 				</enumlist>
110 			</parameter>
111 		</syntax>
112 		<description>
113 			<para>Fetch configuration metadata</para>
114 		</description>
115 		<see-also>
116 			<ref type="managerEvent">SCCPConfigMetaData</ref>
117 			<ref type="managerEvent">SCCPConfigMetaDataComplete</ref>
118 		</see-also>
119 		<responses>
120 			<list-elements>
121 				<managerEvent language="en_US" name="SCCPConfigMetaData">
122 					<managerEventInstance class="EVENT_FLAG_COMMAND">
123 						<synopsis>Detailed field information for the requested segment.</synopsis>
124 						<synopsis>One</synopsis>
125 						<syntax>
126 							<parameter name="Name">
127 								<para>Name of the field.</para>
128 							</parameter>
129 							<parameter name="Type">
130 								<para>Name of the field.</para>
131 							</parameter>
132 							<parameter name="Size">
133 								<para>Size of the field</para>
134 							</parameter>
135 							<parameter name="Flags">
136 								<para/>
137 								<enumlist>
138 									<enum name="BOOLEAN"/>
139 									<enum name="INT"/>
140 									<enum name="UNSIGNED INT"/>
141 									<enum name="STRINGPTR">
142 										<para>Null terminated string.</para>
143 									</enum>
144 									<enum name="STRING">
145 										<para>Fixed length string.</para>
146 									</enum>
147 									<enum name="PARSER">
148 										<para>Custom parser/generator.</para>
149 									</enum>
150 									<enum name="CHAR">
151 										<para>Single Character.</para>
152 									</enum>
153 									<enum name="ENUM">
154 										<para>When 'ENUM' is used the list entry will also contain the <replaceable>Possible Values</replaceable> field</para>
155 									</enum>
156 								</enumlist>
157 							</parameter>
158 							<parameter name="Possible Values" required="false">
159 								<para>Contains a string of possible values, enclosed in '[' &amp; ']' and separated by ','.</para>
160 								<para>Only included for entries that have type:ENUM.</para>
161 							</parameter>
162 							<parameter name="DefaultValue">
163 								<para>Default value of the field.</para>
164 							</parameter>
165 							<parameter name="Description">
166 								<para>A detailed user description of the field.</para>
167 							</parameter>
168 							<xi:include href="../core-en_US.xml" parse="xml"
169 								xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])"/>
170 						</syntax>
171 						<description>
172 							<para>Detailed field information for the requested segment.</para>
173 						</description>
174 						<see-also>
175 							<ref type="managerEvent">SCCPConfigMetaDataComplete</ref>
176 						</see-also>
177 					</managerEventInstance>
178 				</managerEvent>
179 			</list-elements>
180 			<managerEvent language="en_US" name="SCCPConfigMetaDataComplete">
181 				<managerEventInstance class="EVENT_FLAG_AGENT">
182 					<synopsis>Final response event in a series of events to the Agents AMI action.</synopsis>
183 					<syntax>
184 						<xi:include href="../core-en_US.xml" parse="xml"
185 							xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])"/>
186 					</syntax>
187 					<see-also>
188 						<ref type="manager">SCCPConfigMetaData</ref>
189 					</see-also>
190 				</managerEventInstance>
191 			</managerEvent>
192 		</responses>
193 	</manager>
194 ***/
195 #include "config.h"
196 #include "common.h"
197 #include "sccp_config.h"
198 #include "sccp_device.h"
199 #include "sccp_featureButton.h"
200 #include "sccp_line.h"
201 #include "sccp_linedevice.h"
202 #include "sccp_mwi.h"
203 #include "sccp_session.h"
204 #include "sccp_utils.h"
205 #include "sccp_labels.h"
206 #include "revision.h"
207 
208 SCCP_FILE_VERSION(__FILE__, "");
209 
210 #include <asterisk/paths.h>
211 #if defined(CS_AST_HAS_EVENT) && defined(HAVE_PBX_EVENT_H) && (defined(CS_DEVICESTATE) || defined(CS_CACHEABLE_DEVICESTATE))	// ast_event_subscribe
212 #  include <asterisk/event.h>
213 #endif
214 
215 #ifdef HAVE_PBX_APP_H
216 #	include <asterisk/app.h>                                        // AST_DECLARE_APP_ARGS / AST_STANDARD_APP_ARGS
217 #endif
218 
219 #ifndef offsetof
220 #if defined(__GNUC__) && __GNUC__ > 3
221 #define offsetof(type, member)  __builtin_offsetof (type, member)
222 #else
223 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
224 #endif
225 #endif
226 #ifndef offsetof
227 #endif
228 #define offsize(TYPE, MEMBER) sizeof(((TYPE *)0)->MEMBER)
229 #define G_OBJ_REF(x) offsize(struct sccp_global_vars,x), offsetof(struct sccp_global_vars,x)
230 #define D_OBJ_REF(x) offsize(struct sccp_device,x), offsetof(struct sccp_device,x)
231 #define L_OBJ_REF(x) offsize(struct sccp_line,x), offsetof(struct sccp_line,x)
232 #define S_OBJ_REF(x) offsize(struct softKeySetConfiguration,x), offsetof(struct softKeySetConfiguration,x)
233 #define H_OBJ_REF(x) offsize(struct sccp_hotline,x), offsetof(struct sccp_hotline,x)
234 //#define BITMASK(b) (1 << ((b) % CHAR_BIT))
235 //#define BITSLOT(b) ((b) / CHAR_BIT)
236 //#define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b))
237 //#define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b))
238 //#define BITTOGGLE(a, b) ((a)[BITSLOT(b)] ^= BITMASK(b))
239 
240 /*!
241  * \brief Enum for Config Option Types
242  */
243 enum SCCPConfigOptionType {
244 	/* clang-format off */
245 	SCCP_CONFIG_DATATYPE_BOOLEAN			= 1 << 0,
246 	SCCP_CONFIG_DATATYPE_INT			= 1 << 1,
247 	SCCP_CONFIG_DATATYPE_UINT			= 1 << 2,
248 	SCCP_CONFIG_DATATYPE_STRING			= 1 << 3,
249 	SCCP_CONFIG_DATATYPE_PARSER			= 1 << 4,
250 	SCCP_CONFIG_DATATYPE_STRINGPTR			= 1 << 5,		/* string pointer */
251 	SCCP_CONFIG_DATATYPE_CHAR			= 1 << 6,
252 	SCCP_CONFIG_DATATYPE_ENUM			= 1 << 7,
253 	/* clang-format on */
254 };
255 
256 /*!
257  * \brief Enum for Config Option Flags
258  */
259 enum SCCPConfigOptionFlag {
260 	/* clang-format off */
261 	SCCP_CONFIG_FLAG_IGNORE 			= 1 << 0,		/*< ignore parameter */
262 	SCCP_CONFIG_FLAG_NONE	 			= 1 << 1,		/*< ignore parameter */
263 	SCCP_CONFIG_FLAG_DEPRECATED			= 1 << 2,		/*< parameter is deprecated and should not be used anymore, warn user and still set variable */
264 	SCCP_CONFIG_FLAG_OBSOLETE			= 1 << 3,		/*< parameter is now obsolete warn user and skip */
265 	SCCP_CONFIG_FLAG_CHANGED			= 1 << 4,		/*< parameter implementation has changed, warn user */
266 	SCCP_CONFIG_FLAG_REQUIRED			= 1 << 5,		/*< parameter is required */
267 	SCCP_CONFIG_FLAG_GET_DEVICE_DEFAULT		= 1 << 6,		/*< retrieve default value from device */
268 	SCCP_CONFIG_FLAG_GET_GLOBAL_DEFAULT		= 1 << 7,		/*< retrieve default value from global */
269 	SCCP_CONFIG_FLAG_MULTI_ENTRY			= 1 << 8,		/*< multi entries allowed */
270 	/* clang-format on */
271 };
272 
273 /*!
274  * \brief SCCP Config Option Struct
275  */
276 typedef struct SCCPConfigOption {
277 /* clang-format off */
278 	const char *name;							/*!< Configuration Parameter Name */
279 	const size_t size;							/*!< The offsize of the element in the structure where the option value is stored */
280 	const int offset;							/*!< The offset relative to the context structure where the option value is stored. */
281 	int type; 								/*!< bitfield of enum SCCPConfigOptionType */
282 	sccp_value_changed_t (*converter_f)(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment); /*!< Conversion function */
283 	sccp_enum_str2intval_t str2intval;
284         sccp_enum_all_entries_t all_entries;
285         const char *parsername;
286 	int flags;								/*!< bitfield of enum SCCPConfigOptionFlag */
287 	sccp_configurationchange_t change;					/*!< Does a change of this value needs a device restart */
288 	const char *defaultValue;						/*!< Default value */
289 	const char *description;						/*!< Configuration description (config file) or warning message for deprecated or obsolete values */
290 /* clang-format on */
291 } SCCPConfigOption;
292 
293 //converter function prototypes
294 sccp_value_changed_t sccp_config_parse_codec_preferences(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
295 sccp_value_changed_t sccp_config_parse_mailbox(void * const dest, const size_t size, PBX_VARIABLE_TYPE * vroot, const sccp_config_segment_t segment);
296 sccp_value_changed_t sccp_config_parse_tos(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
297 sccp_value_changed_t sccp_config_parse_cos(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
298 sccp_value_changed_t sccp_config_parse_amaflags(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
299 sccp_value_changed_t sccp_config_parse_secondaryDialtoneDigits(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
300 sccp_value_changed_t sccp_config_parse_variables(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
301 sccp_value_changed_t sccp_config_parse_group(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
302 sccp_value_changed_t sccp_config_parse_deny_permit(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
303 sccp_value_changed_t sccp_config_parse_button(void * const dest, const size_t size, PBX_VARIABLE_TYPE * vars, const sccp_config_segment_t segment);
304 sccp_value_changed_t sccp_config_parse_permithosts(void * const dest, const size_t size, PBX_VARIABLE_TYPE * vroot, const sccp_config_segment_t segment);
305 sccp_value_changed_t sccp_config_parse_addons(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
306 sccp_value_changed_t sccp_config_parse_privacyFeature(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
307 sccp_value_changed_t sccp_config_parse_debug(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
308 sccp_value_changed_t sccp_config_parse_ipaddress(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
309 sccp_value_changed_t sccp_config_parse_port(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
310 sccp_value_changed_t sccp_config_parse_context(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
311 sccp_value_changed_t sccp_config_parse_hotline_context(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
312 sccp_value_changed_t sccp_config_parse_hotline_exten(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
313 sccp_value_changed_t sccp_config_parse_hotline_label(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
314 sccp_value_changed_t sccp_config_parse_jbflags_enable(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
315 sccp_value_changed_t sccp_config_parse_jbflags_force(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
316 sccp_value_changed_t sccp_config_parse_jbflags_log(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
317 sccp_value_changed_t sccp_config_parse_jbflags_maxsize(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
318 sccp_value_changed_t sccp_config_parse_jbflags_impl(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
319 sccp_value_changed_t sccp_config_parse_jbflags_jbresyncthreshold(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
320 sccp_value_changed_t sccp_config_checkButton(sccp_buttonconfig_list_t *buttonconfigList, int buttonindex, sccp_config_buttontype_t type, const char *name, const char *options, const char *args);
321 sccp_value_changed_t sccp_config_parse_webdir(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
322 sccp_value_changed_t sccp_config_parse_earlyrtp(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment);
323 
324 #include "sccp_config_entries.hh"
325 
326 /*!
327  * \brief SCCP Config Option Struct
328  */
329 typedef struct SCCPConfigSegment {
330 	const char *name;
331 	const SCCPConfigOption *config;
332 	long unsigned int config_size;
333 	const sccp_config_segment_t segment;
334 } SCCPConfigSegment;
335 
336 /*!
337  * \brief SCCP Config Option Struct Initialization
338  */
339 static const SCCPConfigSegment sccpConfigSegments[] = {
340 	{"general", sccpGlobalConfigOptions, ARRAY_LEN(sccpGlobalConfigOptions), SCCP_CONFIG_GLOBAL_SEGMENT},
341 	{"device", sccpDeviceConfigOptions, ARRAY_LEN(sccpDeviceConfigOptions), SCCP_CONFIG_DEVICE_SEGMENT},
342 	{"line", sccpLineConfigOptions, ARRAY_LEN(sccpLineConfigOptions), SCCP_CONFIG_LINE_SEGMENT},
343 	{"softkey", sccpSoftKeyConfigOptions, ARRAY_LEN(sccpSoftKeyConfigOptions), SCCP_CONFIG_SOFTKEY_SEGMENT},
344 };
345 
346 /*!
347  * \brief Find of SCCP Config Options
348  */
349 //static const SCCPConfigOption *sccp_find_segment(const sccp_config_segment_t segment)
sccp_find_segment(const sccp_config_segment_t segment)350 static const SCCPConfigSegment *sccp_find_segment(const sccp_config_segment_t segment)
351 {
352 	uint8_t i = 0;
353 
354 	for (i = 0; i < ARRAY_LEN(sccpConfigSegments); i++) {
355 		if (sccpConfigSegments[i].segment == segment) {
356 			return &sccpConfigSegments[i];
357 		}
358 	}
359 	return NULL;
360 }
361 
362 /*!
363  * \brief Find of SCCP Config Options
364  */
sccp_find_config(const sccp_config_segment_t segment,const char * name)365 static const SCCPConfigOption *sccp_find_config(const sccp_config_segment_t segment, const char *name)
366 {
367 	const SCCPConfigSegment *sccpConfigSegment = sccp_find_segment(segment);
368 	const SCCPConfigOption *config = sccpConfigSegment->config;
369 
370 	char delims[] = "|";
371 	char * token = NULL;
372 	char * tokenrest = NULL;
373 	char * config_name = NULL;
374 
375 	for (long unsigned int i = 0; i < sccpConfigSegment->config_size; i++) {
376 		if (strstr(config[i].name, delims) != NULL) {
377 			config_name = pbx_strdup(config[i].name);
378 			token = strtok_r(config_name, delims, &tokenrest);
379 			while (token != NULL) {
380 				if(strcasecmp(token, name) == 0) {
381 					sccp_free(config_name);
382 					return &config[i];
383 				}
384 				token = strtok_r(NULL, delims, &tokenrest);
385 			}
386 			sccp_free(config_name);
387 		}
388 		if(strcasecmp(config[i].name, name) == 0) {
389 			return &config[i];
390 		}
391 	}
392 
393 	return NULL;
394 }
395 
396 /* Create new variable structure for Multi Entry Parameters */
createVariableSetForMultiEntryParameters(PBX_VARIABLE_TYPE * cat_root,const char * configOptionName,PBX_VARIABLE_TYPE * out)397 static PBX_VARIABLE_TYPE *createVariableSetForMultiEntryParameters(PBX_VARIABLE_TYPE * cat_root, const char *configOptionName, PBX_VARIABLE_TYPE * out)
398 {
399 	PBX_VARIABLE_TYPE *v = cat_root;
400 	PBX_VARIABLE_TYPE *tmp = NULL;
401 	char delims[] = "|";
402 	char option_name[strlen(configOptionName) + 2];
403 	char * token = NULL;
404 	char * tokenrest = NULL;
405 
406 	snprintf(option_name, sizeof(option_name), "%s%s", configOptionName, delims);
407 	token = strtok_r(option_name, delims, &tokenrest);
408 	while (token != NULL) {
409 		sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Token %s/%s\n", option_name, token);
410 		for (v = cat_root; v; v = v->next) {
411 			if(strcasecmp(token, v->name) == 0) {
412 				if (!tmp) {
413 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Create new variable set (%s=%s)\n", v->name, v->value);
414 					if (!(out = pbx_variable_new(v->name, v->value, ""))) {
415 						pbx_log(LOG_ERROR, "SCCP: (sccp_config) Error while creating new var structure\n");
416 						goto EXIT;
417 					}
418 					tmp = out;
419 				} else {
420 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Add to variable set (%s=%s)\n", v->name, v->value);
421 					if (!(tmp->next = pbx_variable_new(v->name, v->value, ""))) {
422 						pbx_log(LOG_ERROR, "SCCP: (sccp_config) Error while creating new var structure\n");
423 						pbx_variables_destroy(out);
424 						goto EXIT;
425 					}
426 					tmp = tmp->next;
427 				}
428 			}
429 		}
430 		token = strtok_r(NULL, delims, &tokenrest);
431 	}
432 EXIT:
433 	return out;
434 }
435 
createVariableSetForTokenizedDefault(const char * configOptionName,const char * defaultValue,PBX_VARIABLE_TYPE * out)436 static PBX_VARIABLE_TYPE *createVariableSetForTokenizedDefault(const char *configOptionName, const char *defaultValue, PBX_VARIABLE_TYPE * out)
437 {
438 	PBX_VARIABLE_TYPE *tmp = NULL;
439 
440 	char delims[] = "|";
441 	char *option_name_tokens = pbx_strdupa(configOptionName);
442 	char *option_value_tokens = pbx_strdupa(defaultValue);
443 	char * option_name_tokens_saveptr = NULL;
444 	char * option_value_tokens_saveptr = NULL;
445 
446 	char *option_name = strtok_r(option_name_tokens, "|", &option_name_tokens_saveptr);
447 	char *option_value = strtok_r(option_value_tokens, "|", &option_value_tokens_saveptr);
448 
449 	while (option_name != NULL && option_value != NULL) {
450 		sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Token %s/%s\n", option_name, option_value);
451 		if (!tmp) {
452 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Create new variable set (%s=%s)\n", option_name, option_value);
453 			if (!(out = pbx_variable_new(option_name, option_value, ""))) {
454 				pbx_log(LOG_ERROR, "SCCP: (sccp_config) Error while creating new var structure\n");
455 				goto EXIT;
456 			}
457 			tmp = out;
458 		} else {
459 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Add to variable set (%s=%s)\n", option_name, option_value);
460 			if (!(tmp->next = pbx_variable_new(option_name, option_value, ""))) {
461 				pbx_log(LOG_ERROR, "SCCP: (sccp_config) Error while creating new var structure\n");
462 				pbx_variables_destroy(out);
463 				goto EXIT;
464 			}
465 			tmp = tmp->next;
466 		}
467 		option_name = strtok_r(NULL, delims, &option_name_tokens_saveptr);
468 		option_value = strtok_r(NULL, delims, &option_value_tokens_saveptr);
469 	}
470 EXIT:
471 	return out;
472 }
473 
474 /*!
475  * \brief Parse SCCP Config Option Value
476  *
477  * \todo add errormsg return to sccpConfigOption->converter_f: so we can have a fixed format the returned errors to the user
478  */
sccp_config_object_setValue(void * const obj,PBX_VARIABLE_TYPE * cat_root,const char * name,const char * value,int lineno,const sccp_config_segment_t segment,boolean_t * SetEntries,boolean_t default_run)479 static sccp_configurationchange_t sccp_config_object_setValue(void * const obj, PBX_VARIABLE_TYPE * cat_root, const char * name, const char * value, int lineno, const sccp_config_segment_t segment, boolean_t * SetEntries,
480 							      boolean_t default_run)
481 {
482 	const SCCPConfigSegment *sccpConfigSegment = sccp_find_segment(segment);
483 	const SCCPConfigOption *sccpConfigOption = sccp_find_config(segment, name);
484 	void * dst = NULL;
485 	enum SCCPConfigOptionType type = 0;  /* enum wrapper */
486 	enum SCCPConfigOptionFlag flags = 0; /* enum wrapper */
487 	const char * deprecated_obsolete_url = "https://github.com/chan-sccp/chan-sccp/wiki/Deprecated---Obsoleted-Parameters";
488 
489 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;						/* indicates config value is changed or not */
490 	sccp_configurationchange_t changes = SCCP_CONFIG_NOUPDATENEEDED;
491 
492 	sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_3 "SCCP: parsing %s parameter: %s %s%s%s (line: %d)\n", sccpConfigSegment->name, name, value ? "= '" : "", value ? value : "", value ? "' " : "", lineno);
493 
494 	short int int8num = 0;
495 	int int16num = 0;
496 	long int int32num = 0;
497 	long long int int64num = 0;
498 	short unsigned int uint8num = 0;
499 	unsigned int uint16num = 0;
500 	long unsigned int uint32num = 0;
501 	long long unsigned int uint64num = 0;
502 	boolean_t boolean = 0;
503 	char * str = NULL;
504 	char oldChar = 0;
505 	char * tmp_value = NULL;
506 
507 	if (!sccpConfigOption) {
508 		if (strlen(name) == 0 || name[0] != '_') {							/* skip generating error, when column name starts with '_' */
509 			pbx_log(LOG_WARNING, "SCCP: Unknown param at %s:%d:%s='%s'\n", sccpConfigSegment->name, lineno, name, value);
510 		}
511 		return SCCP_CONFIG_NOUPDATENEEDED;
512 	}
513 
514 	dst = ((uint8_t *) obj) + sccpConfigOption->offset;
515 	type = (enum SCCPConfigOptionType) sccpConfigOption->type;
516 	flags = (enum SCCPConfigOptionFlag) sccpConfigOption->flags;
517 
518 	// check if already set during first pass (multi_entry)
519 	if (SetEntries != NULL && ((flags & SCCP_CONFIG_FLAG_MULTI_ENTRY) == SCCP_CONFIG_FLAG_MULTI_ENTRY)) {
520 		for (long unsigned int y = 0; y < sccpConfigSegment->config_size; y++) {
521 			if (sccpConfigOption->offset == sccpConfigSegment->config[y].offset) {
522 				if (SetEntries[y] == TRUE) {
523 					sccp_log_and ((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "SCCP: (sccp_config_object_setValue) Set Entry[%lu] = TRUE for MultiEntry %s -> SKIPPING\n", y,
524 											  sccpConfigSegment->config[y].name);
525 					return SCCP_CONFIG_NOUPDATENEEDED;
526 				}
527 			}
528 		}
529 	}
530 
531 	if ((flags & SCCP_CONFIG_FLAG_IGNORE) == SCCP_CONFIG_FLAG_IGNORE) {
532 		//sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_2 "SCCP: config parameter %s='%s' in line %d ignored\n", name, value, lineno);
533 		return SCCP_CONFIG_NOUPDATENEEDED;
534 	} if ((flags & SCCP_CONFIG_FLAG_CHANGED) == SCCP_CONFIG_FLAG_CHANGED && !default_run) {
535 		pbx_log(LOG_NOTICE, "SCCP: changed config param at %s='%s' in line %d\n - %s -> please check sccp.conf file\n", name, value, lineno, sccpConfigOption->description);
536 	} else if ((flags & SCCP_CONFIG_FLAG_DEPRECATED) == SCCP_CONFIG_FLAG_DEPRECATED && lineno > 0 && !default_run) {
537 		pbx_log(LOG_WARNING, "SCCP: deprecated config param at %s='%s' in line %d\n - %s -> using old implementation.\nSee:%s\n", name, value, lineno, sccpConfigOption->description, deprecated_obsolete_url);
538 	} else if ((flags & SCCP_CONFIG_FLAG_OBSOLETE) == SCCP_CONFIG_FLAG_OBSOLETE && lineno > 0 && !default_run) {
539 		pbx_log(LOG_ERROR, "SCCP: obsolete config param at %s='%s' in line %d\n - %s -> param skipped\nSee: %s\n", name, value, lineno, sccpConfigOption->description, deprecated_obsolete_url);
540 		return SCCP_CONFIG_NOUPDATENEEDED;
541 	} else if ((flags & SCCP_CONFIG_FLAG_REQUIRED) == SCCP_CONFIG_FLAG_REQUIRED) {
542 		if (NULL == value) {
543 			pbx_log(LOG_WARNING, "SCCP: required config param at %s='<NULL>' - %s\n", name, sccpConfigOption->description);
544 			return SCCP_CONFIG_WARNING;
545 		}
546 	}
547 
548 	/* rewrite to check change at the end */
549 	switch (type) {
550 		case SCCP_CONFIG_DATATYPE_CHAR:
551 			oldChar = *(char *) dst;
552 
553 			if (!sccp_strlen_zero(value)) {
554 				if (oldChar != value[0]) {
555 					changed = SCCP_CONFIG_CHANGE_CHANGED;
556 					*(char *) dst = value[0];
557 				}
558 			} else {
559 				if (oldChar != '\0') {
560 					changed = SCCP_CONFIG_CHANGE_CHANGED;
561 					*(char *) dst = '\0';
562 				}
563 			}
564 			break;
565 
566 		case SCCP_CONFIG_DATATYPE_STRING:
567 			str = (char *) dst;
568 
569 			if (!sccp_strlen_zero(value)) {
570 				if (sccp_strlen(value) > sccpConfigOption->size - 1) {
571 					pbx_log(LOG_NOTICE, "SCCP: config parameter %s:%s value '%s' is too long, only using the first %d characters\n", sccpConfigSegment->name, name, value, (int) sccpConfigOption->size - 1);
572 				}
573 				if(strncasecmp(str, value, sccpConfigOption->size - 1) != 0) {
574 					if (GLOB(reload_in_progress)) {
575 						sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "SCCP: config parameter %s '%s' != '%s'\n", name, str, value);
576 					}
577 					changed = SCCP_CONFIG_CHANGE_CHANGED;
578 					pbx_copy_string((char *)dst, value, sccpConfigOption->size);
579 				}
580 			} else if (!sccp_strlen_zero(str)) {
581 				changed = SCCP_CONFIG_CHANGE_CHANGED;
582 				pbx_copy_string((char *)dst, "", sccpConfigOption->size);
583 			}
584 			break;
585 
586 		case SCCP_CONFIG_DATATYPE_STRINGPTR:
587 			changed = SCCP_CONFIG_CHANGE_NOCHANGE;
588 			str = *(char **) dst;
589 
590 			if (!sccp_strequals(str, value)) {
591 				// pbx_log(LOG_NOTICE, "SCCP: Adding %s='%s' -> '%s'\n", name, str, value);
592 				changed = SCCP_CONFIG_CHANGE_CHANGED;
593 			}
594 			if (SCCP_CONFIG_CHANGE_CHANGED == changed) {
595 				if(*(void **)dst) {
596 					sccp_free(*(void **)dst);
597 				}
598 				if (value) {
599 					*(void **) dst = pbx_strdup(value);
600 				} else {
601 					*(void **) dst = NULL;
602 				}
603 			}
604 			break;
605 
606 		case SCCP_CONFIG_DATATYPE_INT:
607 			if (sccp_strlen_zero(value)) {
608 				tmp_value = pbx_strdupa("0");
609 			} else {
610 				tmp_value = pbx_strdupa(value);
611 			}
612 
613 			switch (sccpConfigOption->size) {
614 				case 1:
615 					if (sscanf(tmp_value, "%hd", &int8num) == 1) {
616 						if ((*(int8_t *) dst) != int8num) {
617 							*(int8_t *) dst = int8num;
618 							changed = SCCP_CONFIG_CHANGE_CHANGED;
619 						}
620 					}
621 					break;
622 				case 2:
623 					if (sscanf(tmp_value, "%d", &int16num) == 1) {
624 						if ((*(int16_t *) dst) != int16num) {
625 							*(int16_t *) dst = int16num;
626 							changed = SCCP_CONFIG_CHANGE_CHANGED;
627 						}
628 					}
629 					break;
630 				case 4:
631 					if (sscanf(tmp_value, "%ld", &int32num) == 1) {
632 						if ((*(int32_t *) dst) != int32num) {
633 							*(int32_t *) dst = int32num;
634 							changed = SCCP_CONFIG_CHANGE_CHANGED;
635 						}
636 					}
637 					break;
638 				case 8:
639 					if (sscanf(tmp_value, "%lld", &int64num) == 1) {
640 						if ((*(int64_t *) dst) != int64num) {
641 							*(int64_t *) dst = int64num;
642 							changed = SCCP_CONFIG_CHANGE_CHANGED;
643 						}
644 					}
645 					break;
646 			}
647 			break;
648 		case SCCP_CONFIG_DATATYPE_UINT:
649 			if (sccp_strlen_zero(value)) {
650 				tmp_value = pbx_strdupa("0");
651 			} else {
652 				tmp_value = pbx_strdupa(value);
653 			}
654 			switch (sccpConfigOption->size) {
655 				case 1:
656 					if ((!strncmp("0x", tmp_value, 2) && sscanf(tmp_value, "%hx", &uint8num)) || (sscanf(tmp_value, "%hu", &uint8num) == 1)) {
657 						if ((*(uint8_t *) dst) != uint8num) {
658 							*(uint8_t *) dst = uint8num;
659 							changed = SCCP_CONFIG_CHANGE_CHANGED;
660 						}
661 					}
662 					break;
663 				case 2:
664 					if ((!strncmp("0x", tmp_value, 2) && sscanf(tmp_value, "%ux", &uint16num)) || (sscanf(tmp_value, "%u", &uint16num) == 1)) {
665 						if ((*(uint16_t *) dst) != uint16num) {
666 							*(uint16_t *) dst = uint16num;
667 							changed = SCCP_CONFIG_CHANGE_CHANGED;
668 						}
669 					}
670 					break;
671 				case 4:
672 					if ((!strncmp("0x", tmp_value, 2) && sscanf(tmp_value, "%lx", &uint32num)) || (sscanf(tmp_value, "%lu", &uint32num) == 1)) {
673 						if ((*(uint32_t *) dst) != uint32num) {
674 							*(uint32_t *) dst = uint32num;
675 							changed = SCCP_CONFIG_CHANGE_CHANGED;
676 						}
677 					}
678 					break;
679 				case 8:
680 					if ((!strncmp("0x", tmp_value, 2) && sscanf(tmp_value, "%llx", &uint64num)) || (sscanf(tmp_value, "%llu", &uint64num) == 1)) {
681 						if ((*(uint64_t *) dst) != uint64num) {
682 							*(uint64_t *) dst = uint64num;
683 							changed = SCCP_CONFIG_CHANGE_CHANGED;
684 						}
685 					}
686 					break;
687 			}
688 			break;
689 
690 		case SCCP_CONFIG_DATATYPE_BOOLEAN:
691 			if (sccp_strlen_zero(value)) {
692 				boolean = FALSE;
693 			} else {
694 				if (sccp_true(value)) {
695 					boolean = TRUE;
696 				} else if (!sccp_true(value)) {
697 					boolean = FALSE;
698 				} else {
699 					pbx_log(LOG_NOTICE, "SCCP: Invalid value '%s' for [%s]->%s. Allowed: [TRUE/ON/YES or FALSE/OFF/NO]\n", value, sccpConfigSegment->name, name);
700 					changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
701 					break;
702 				}
703 			}
704 
705 			if (*(boolean_t *) dst != boolean) {
706 				*(boolean_t *) dst = boolean;
707 				changed = SCCP_CONFIG_CHANGE_CHANGED;
708 			}
709 			break;
710 
711 		case SCCP_CONFIG_DATATYPE_PARSER:
712 			{
713 				/* MULTI_ENTRY can only be parsed by a specific datatype_parser at this moment */
714 				if (sccpConfigOption->converter_f) {
715 					PBX_VARIABLE_TYPE *new_var = NULL;
716 
717 					if (cat_root) {								/* convert cat_root or referral to new PBX_VARIABLE_TYPE */
718 						new_var = createVariableSetForMultiEntryParameters(cat_root, sccpConfigOption->name, new_var);
719 					} else {
720 						if (strstr(value, "|")) {					/* convert multi-token default value to PBX_VARIABLE_TYPE */
721 							new_var = createVariableSetForTokenizedDefault(name, value, new_var);
722 						} else {							/* convert simple single value -> PBX_VARIABLE_TYPE */
723 							new_var = ast_variable_new(name, value, "");
724 						}
725 					}
726 					if (new_var) {
727 						changed = sccpConfigOption->converter_f(dst, sccpConfigOption->size, new_var, segment);
728 						pbx_variables_destroy(new_var);
729 					}
730 				}
731 			}
732 			break;
733 		case SCCP_CONFIG_DATATYPE_ENUM:
734 			{
735 				int enumValue = -1;
736 				if (!sccp_strlen_zero(value)) {
737 					const char *all_entries = sccpConfigOption->all_entries();
738 					if (!strncasecmp(value, "On,Yes,True,Off,No,False", strlen(value))) {
739 						if (sccp_true(value)) {
740 							if (strcasestr(all_entries, "On")) {
741 								enumValue = sccpConfigOption->str2intval("On");
742 							} else if (strcasestr(all_entries, "Yes")) {
743 								enumValue = sccpConfigOption->str2intval("Yes");
744 							} else if (strcasestr(all_entries, "True")) {
745 								enumValue = sccpConfigOption->str2intval("True");
746 							}
747 						} else if (!sccp_true(value)) {
748 							if (strcasestr(all_entries, "Off")) {
749 								enumValue = sccpConfigOption->str2intval("Off");
750 							} else if (strcasestr(all_entries, "No")) {
751 								enumValue = sccpConfigOption->str2intval("No");
752 							} else if (strcasestr(all_entries, "False")) {
753 								enumValue = sccpConfigOption->str2intval("False");
754 							}
755 						}
756 					} else if (!strncmp("0x", value, 2) && sscanf(value, "%x", &enumValue)) {
757 						sccp_log(DEBUGCAT_HIGH) ("SCCP: Parse Other Value: %s -> %d\n", value, enumValue);
758 					} else if (sscanf(value, "%d", &enumValue)) {
759 						sccp_log(DEBUGCAT_HIGH) ("SCCP: Parse Other Value: %s -> %d\n", value, enumValue);
760 					} else if ((enumValue = sccpConfigOption->str2intval(value)) != -1) {
761 						sccp_log(DEBUGCAT_HIGH) ("SCCP: Parse Other Value: %s -> %d\n", value, enumValue);
762 					}
763 					if (enumValue != -1) {
764 			                        switch (sccpConfigOption->size) {
765 						case 1:
766 							if (*(int8_t *) dst != (int8_t)enumValue) {
767 								*(int8_t *) dst = (int8_t)enumValue;
768 								changed = SCCP_CONFIG_CHANGE_CHANGED;
769 							}
770 							break;
771 						case 2:
772 							if ((*(uint16_t *) dst) != (int16_t)enumValue) {
773 								*(uint16_t *) dst = (int16_t)enumValue;
774 								changed = SCCP_CONFIG_CHANGE_CHANGED;
775 							}
776 							break;
777 						default:
778 							if (*(int *) dst != enumValue) {
779 								*(int *) dst = enumValue;
780 								changed = SCCP_CONFIG_CHANGE_CHANGED;
781 							}
782 							break;
783 						}
784 					} else {
785 						pbx_log(LOG_NOTICE, "SCCP: Invalid value '%s' for [%s]->%s. Allowed: [%s]\n", value, sccpConfigSegment->name, name, sccpConfigOption->all_entries());
786 						changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
787 					}
788 					break;
789 				}
790 				pbx_log(LOG_WARNING, "SCCP: [%s]=>%s cannot be ''. Allowed: [%s]\n", sccpConfigSegment->name, name, sccpConfigOption->all_entries());
791 				changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
792 			}
793 			break;
794 	}
795 
796 	if (SCCP_CONFIG_CHANGE_CHANGED == changed) {
797 		if (GLOB(reload_in_progress)) {
798 			sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "SCCP: config parameter %s='%s' in line %d changed. %s\n", name, value, lineno, SCCP_CONFIG_NEEDDEVICERESET == sccpConfigOption->change ? "(causes device reset)" : "");
799 		}
800 		changes = sccpConfigOption->change;
801 	}
802 
803 	if ((SCCP_CONFIG_CHANGE_INVALIDVALUE != changed && SCCP_CONFIG_CHANGE_ERROR != changed) ||
804 		((flags & SCCP_CONFIG_FLAG_MULTI_ENTRY) == SCCP_CONFIG_FLAG_MULTI_ENTRY)		/* Multi_Entry could give back invalid for one of it's values */
805 	) {
806 		/* if SetEntries is provided lookup the first offset of the struct variable we have set and note the index in SetEntries by changing the boolean_t to TRUE */
807 		if (SetEntries != NULL) {
808 			for (long unsigned int x = 0; x < sccpConfigSegment->config_size; x++) {
809 				if (sccpConfigOption->offset == sccpConfigSegment->config[x].offset) {
810 					sccp_log_and ((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "SCCP: (sccp_config_object_setValue) Set Entry[%lu] = TRUE for %s\n", x, sccpConfigSegment->config[x].name);
811 					SetEntries[x] = TRUE;
812 				}
813 			}
814 		}
815 	}
816 	if (SCCP_CONFIG_CHANGE_INVALIDVALUE == changed && !default_run) {
817 		pbx_log(LOG_NOTICE, "SCCP: Option Description: %s\n", sccpConfigOption->description);
818 	}
819 	if (SCCP_CONFIG_CHANGE_ERROR == changed) {
820 		pbx_log(LOG_NOTICE, "SCCP: Exception/Error during parsing of: %s\n", sccpConfigOption->description);
821 	}
822 	return changes;
823 }
824 
825 /*!
826  * \brief Set SCCP obj defaults from predecessor (device / global)
827  *
828  * check if we can find the param name in the segment specified and retrieving its value or default value
829  * copy the string from the defaultSegment and run through the converter again
830  */
sccp_config_set_defaults(void * const obj,const sccp_config_segment_t segment,boolean_t * SetEntries)831 static void sccp_config_set_defaults(void * const obj, const sccp_config_segment_t segment, boolean_t * SetEntries)
832 {
833 	if (!GLOB(cfg)) {
834 		pbx_log(LOG_NOTICE, "GLOB(cfg) not available. Skip loading default setting.\n");
835 		return;
836 	}
837 	/* destination segment */
838 	const SCCPConfigSegment *sccpConfigSegment = sccp_find_segment(segment);
839 
840 	/* destination/source config */
841 	const SCCPConfigOption *sccpDstConfig = sccpConfigSegment->config;
842 	const SCCPConfigOption * sccpDefaultConfigOption = NULL;
843 	sccp_device_t * referral_device = NULL; /* need to find a way to find the default device to copy */
844 	char *referral_cat = "";
845 	sccp_config_segment_t search_segment_type = 0;
846 	boolean_t referralValueFound = FALSE;
847 
848 	// already Set
849 	boolean_t skip = FALSE;
850 
851 	/* find the defaultValue, first check the reference, if no reference is specified, us the local defaultValue */
852 	for (long unsigned int cur_elem = 0; cur_elem < sccpConfigSegment->config_size; cur_elem++) {
853 		/* Lookup the first offset of the struct variable we want to set default for, find the corresponding entry in the SetEntries array and check the boolean flag, skip if true */
854 		skip = FALSE;
855 		for (long unsigned int skip_elem = 0; skip_elem < sccpConfigSegment->config_size; skip_elem++) {
856 			//if (sccpDstConfig[cur_elem].offset == sccpConfigSegment->config[skip_elem].offset && (SetEntries[skip_elem] || sccpConfigSegment->config[cur_elem].flags & (SCCP_CONFIG_FLAG_DEPRECATED | SCCP_CONFIG_FLAG_OBSOLETE))) {
857 			if (sccpDstConfig[cur_elem].offset == sccpConfigSegment->config[skip_elem].offset && (SetEntries[skip_elem] || sccpConfigSegment->config[cur_elem].flags & (SCCP_CONFIG_FLAG_OBSOLETE))) {
858 				sccp_log_and ((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_2 "SCCP: (sccp_config_set_defaults) skip setting default (SetEntry[%lu] = TRUE for %s)\n", skip_elem,
859 										  sccpConfigSegment->config[skip_elem].name);
860 				skip = TRUE;
861 				break;
862 			}
863 		}
864 		if (skip) {											// skip default value if already set
865 			continue;
866 		}
867 		int flags = sccpDstConfig[cur_elem].flags;
868 		int type = sccpDstConfig[cur_elem].type;
869 
870 		if (((flags & SCCP_CONFIG_FLAG_OBSOLETE) != SCCP_CONFIG_FLAG_OBSOLETE)) {			// has not been set already and is not obsolete
871 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_1 "parsing %s parameter %s looking for defaultValue (flags: %d, type: %d)\n", sccpConfigSegment->name, sccpDstConfig[cur_elem].name, flags, type);
872 
873 			/* check if referring to another segment, or ourself */
874 			if ((flags & SCCP_CONFIG_FLAG_GET_DEVICE_DEFAULT) == SCCP_CONFIG_FLAG_GET_DEVICE_DEFAULT) {	/* get default value from device */
875 				referral_device = &(*(sccp_device_t *) obj);
876 				referral_cat = referral_device->id;
877 				search_segment_type = SCCP_CONFIG_DEVICE_SEGMENT;
878 
879 			} else if ((flags & SCCP_CONFIG_FLAG_GET_GLOBAL_DEFAULT) == SCCP_CONFIG_FLAG_GET_GLOBAL_DEFAULT) {	/* get default value from global */
880 				referral_cat = "general";
881 				search_segment_type = SCCP_CONFIG_GLOBAL_SEGMENT;
882 
883 			} else {										/* get default value from our own segment */
884 				referral_cat = NULL;
885 				search_segment_type = segment;
886 			}
887 
888 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_2 "config parameter:'%s' defaultValue %s lookup %s%s\n", sccpDstConfig[cur_elem].name, referral_cat ? "referred" : "direct", referral_cat ? "via " : "", referral_cat ? referral_cat : "");
889 
890 			/* check to see if there is a default value to be found in the config file within referred segment */
891 			if (referral_cat) {
892 				/* tokenize all option parameters */
893 				PBX_VARIABLE_TYPE * v = NULL;
894 				PBX_VARIABLE_TYPE * cat_root = NULL;
895 				char option_tokens[sccp_strlen(sccpDstConfig[cur_elem].name) + 2];
896 				referralValueFound = FALSE;
897 
898 				/* tokenparsing */
899 				snprintf(option_tokens, sizeof(option_tokens), "%s|", sccpDstConfig[cur_elem].name);
900 				char *option_tokens_saveptr = NULL;
901 				char *option_name = strtok_r(option_tokens, "|", &option_tokens_saveptr);
902 				do {
903 					/* search for the default values in the referred segment, if found break so we can pass on the cat_root */
904 					for (cat_root = v = ast_variable_browse(GLOB(cfg), referral_cat); v; v = v->next) {
905 						if (sccp_strcaseequals((const char *) option_name, v->name)) {
906 							sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_2 "Found name:'%s', value:'%s', use referred config-file value from segment '%s'\n", option_name, v->value, referral_cat);
907 							referralValueFound = TRUE;
908 							break;
909 						}
910 					}
911 				} while ((option_name = strtok_r(NULL, "|", &option_tokens_saveptr)) != NULL);
912 
913 				if (referralValueFound && v) {							/* if referred to other segment and a value was found, pass the newly found cat_root directly to setValue */
914 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Refer default value lookup for parameter:'%s' through '%s' segment\n", sccpDstConfig[cur_elem].name, referral_cat);
915 					sccp_config_object_setValue(obj, cat_root, sccpDstConfig[cur_elem].name, v->value, __LINE__, segment, SetEntries, TRUE);
916 					continue;
917 				} else {									/* if referred but no default value was found, pass on the defaultValue of the referred segment in raw string form (including tokens) */
918 					sccpDefaultConfigOption = sccp_find_config(search_segment_type, sccpDstConfig[cur_elem].name);
919 					if (sccpDefaultConfigOption && !sccp_strlen_zero(sccpDefaultConfigOption->defaultValue)) {
920 						sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Set parameter '%s' to segment default, being:'%s'\n", sccpDstConfig[cur_elem].name, sccpDstConfig[cur_elem].defaultValue);
921 						sccp_config_object_setValue(obj, NULL, sccpDstConfig[cur_elem].name, sccpDefaultConfigOption->defaultValue, __LINE__, segment, SetEntries, TRUE);
922 						continue;
923 					}
924 				}
925 
926 			} else if (!sccp_strlen_zero(sccpDstConfig[cur_elem].defaultValue)) {			/* Non-Referral, pass defaultValue on in raw string format (including tokens) */
927 				sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Set parameter '%s' to own default, being:'%s'\n", sccpDstConfig[cur_elem].name, sccpDstConfig[cur_elem].defaultValue);
928 				sccp_config_object_setValue(obj, NULL, sccpDstConfig[cur_elem].name, sccpDstConfig[cur_elem].defaultValue, __LINE__, segment, SetEntries, TRUE);
929 				continue;
930 			}
931 
932 			if (type == SCCP_CONFIG_DATATYPE_STRINGPTR || type==SCCP_CONFIG_DATATYPE_PARSER) {	/* If nothing was found, clear variable, incase of a STRINGPTR */
933 				sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Clearing parameter %s\n", sccpDstConfig[cur_elem].name);
934 				sccp_config_object_setValue(obj, NULL, sccpDstConfig[cur_elem].name, "", __LINE__, segment, SetEntries, TRUE);
935 			}
936 		}
937 	}
938 }
939 
940 /*
941  * \brief automatically free all STRINGPTR objects allocated during config parsing and object initialization
942  *
943  * \note This makes it easier to change character arrays to stringptr in structs, without having to think about cleaning up after them.
944  *
945  */
sccp_config_cleanup_dynamically_allocated_memory(void * const obj,const sccp_config_segment_t segment)946 void sccp_config_cleanup_dynamically_allocated_memory(void * const obj, const sccp_config_segment_t segment)
947 {
948 	const SCCPConfigSegment *sccpConfigSegment = sccp_find_segment(segment);
949 	const SCCPConfigOption *sccpConfigOption = sccpConfigSegment->config;
950 	void * dst = NULL;
951 	char * str = NULL;
952 
953 	for (long unsigned int i = 0; i < sccpConfigSegment->config_size; i++) {
954 		if (sccpConfigOption[i].type == SCCP_CONFIG_DATATYPE_STRINGPTR) {
955 			dst = ((uint8_t *) obj) + sccpConfigOption[i].offset;
956 			str = *(char **) dst;
957 			if (str) {
958 				// pbx_log(LOG_NOTICE, "SCCP: Freeing %s='%s'\n", sccpConfigOption[i].name, str);
959 				sccp_free(str);
960 				str = NULL;
961 			}
962 		}
963 	}
964 }
965 
966 /*!
967  * \brief Config Converter/Parser for Bind Address
968  *
969  * \note not multi_entry
970  */
sccp_config_parse_ipaddress(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)971 sccp_value_changed_t sccp_config_parse_ipaddress(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
972 {
973 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
974 	char *value = pbx_strdupa(v->value);
975 
976 #if ASTERISK_VERSION_GROUP == 106
977 	if (sccp_strequals(value, "::")) {
978 		pbx_log(LOG_ERROR, "Asterisk 1.6, does not support ipv6, '::' has been replaced with '0.0.0.0'\n");
979 		value = pbx_strdupa("::");
980 	}
981 #endif
982 	if (sccp_strlen_zero(value)) {
983 		value = pbx_strdupa("0.0.0.0");
984 	}
985 	struct sockaddr_storage bindaddr_prev = (*(struct sockaddr_storage *) dest);
986 	struct sockaddr_storage bindaddr_new = { 0, };
987 
988 	if (!sccp_sockaddr_storage_parse(&bindaddr_new, value, PARSE_PORT_FORBID)) {
989 		pbx_log(LOG_WARNING, "Invalid IP address: %s\n", value);
990 		changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
991 	} else {
992 		if (sccp_netsock_cmp_addr(&bindaddr_prev, &bindaddr_new)) {					// 0 = equal
993 			memcpy(&(*(struct sockaddr_storage *) dest), &bindaddr_new, sizeof(bindaddr_new));
994 			changed = SCCP_CONFIG_CHANGE_CHANGED;
995 		}
996 	}
997 	return changed;
998 }
999 
1000 /*!
1001  * \brief Config Converter/Parser for Port
1002  *
1003  * \note not multi_entry
1004  */
sccp_config_parse_port(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1005 sccp_value_changed_t sccp_config_parse_port(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1006 {
1007 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1008 	char *value = pbx_strdupa(v->value);
1009 
1010 	int new_port = 0;
1011 	struct sockaddr_storage bindaddr_storage_prev = (*(struct sockaddr_storage *) dest);
1012 
1013 	if (sscanf(value, "%i", &new_port) == 1) {
1014 		if (bindaddr_storage_prev.ss_family == AF_INET) {
1015 			struct sockaddr_in bindaddr_prev = (*(struct sockaddr_in *) dest);
1016 
1017 			if (bindaddr_prev.sin_port != 0) {
1018 				if (bindaddr_prev.sin_port != htons(new_port)) {
1019 					(*(struct sockaddr_in *) dest).sin_port = htons(new_port);
1020 					changed = SCCP_CONFIG_CHANGE_CHANGED;
1021 				}
1022 			} else {
1023 				(*(struct sockaddr_in *) dest).sin_port = htons(new_port);
1024 				changed = SCCP_CONFIG_CHANGE_CHANGED;
1025 			}
1026 		} else if (bindaddr_storage_prev.ss_family == AF_INET6) {
1027 			struct sockaddr_in6 bindaddr_prev = (*(struct sockaddr_in6 *) dest);
1028 
1029 			if (bindaddr_prev.sin6_port != 0) {
1030 				if (bindaddr_prev.sin6_port != htons(new_port)) {
1031 					(*(struct sockaddr_in6 *) dest).sin6_port = htons(new_port);
1032 					changed = SCCP_CONFIG_CHANGE_CHANGED;
1033 				}
1034 			} else {
1035 				(*(struct sockaddr_in6 *) dest).sin6_port = htons(new_port);
1036 				changed = SCCP_CONFIG_CHANGE_CHANGED;
1037 			}
1038 		} else {
1039 			pbx_log(LOG_WARNING, "Invalid address in bindaddr to set port to '%s'\n", value);
1040 			changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1041 		}
1042 	} else {
1043 		pbx_log(LOG_WARNING, "Invalid port number '%s'\n", value);
1044 		changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1045 	}
1046 
1047 	return changed;
1048 }
1049 
1050 /*!
1051  * \brief Config Converter/Parser for privacyFeature
1052  *
1053  * \todo malloc/calloc of privacyFeature necessary ?
1054  *
1055  * \note not multi_entry
1056  */
sccp_config_parse_privacyFeature(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1057 sccp_value_changed_t sccp_config_parse_privacyFeature(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1058 {
1059 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1060 	char *value = pbx_strdupa(v->value);
1061 	sccp_featureConfiguration_t privacyFeature = {0};
1062 
1063 	if (sccp_strcaseequals(value, "full")) {
1064 		privacyFeature.status = ~0;
1065 		privacyFeature.enabled = TRUE;
1066 	} else if (sccp_true(value) || !sccp_true(value)) {
1067 		privacyFeature.status = 0;
1068 		privacyFeature.enabled = sccp_true(value);
1069 	} else {
1070 		pbx_log(LOG_WARNING, "Invalid privacy value, should be 'full', 'on' or 'off'\n");
1071 		return SCCP_CONFIG_CHANGE_INVALIDVALUE;
1072 	}
1073 
1074 	if (privacyFeature.status != (*(sccp_featureConfiguration_t *) dest).status || privacyFeature.enabled != (*(sccp_featureConfiguration_t *) dest).enabled) {
1075 		//*(sccp_featureConfiguration_t *) dest = privacyFeature;
1076 		memcpy(dest, &privacyFeature, sizeof(sccp_featureConfiguration_t));
1077 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1078 	}
1079 	return changed;
1080 }
1081 
1082 /*!
1083  * \brief Config Converter/Parser for mwilamp
1084  *
1085  * \note not multi_entry
1086  */
1087 /*
1088    sccp_value_changed_t sccp_config_parse_mwilamp(void *const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1089    {
1090    sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1091    char *value = pbx_strdupa(v->value);
1092    skinny_lampmode_t mwilamp = SKINNY_LAMP_OFF;
1093 
1094    if (sccp_strcaseequals(value, "wink")) {
1095    mwilamp = SKINNY_LAMP_WINK;
1096    } else if (sccp_strcaseequals(value, "flash")) {
1097    mwilamp = SKINNY_LAMP_FLASH;
1098    } else if (sccp_strcaseequals(value, "blink")) {
1099    mwilamp = SKINNY_LAMP_BLINK;
1100    } else if (!sccp_true(value)) {
1101    mwilamp = SKINNY_LAMP_OFF;
1102    } else if (sccp_true(value)) {
1103    mwilamp = SKINNY_LAMP_ON;
1104    } else {
1105    pbx_log(LOG_WARNING, "Invalid mwilamp value, should be one of 'off', 'on', 'wink', 'flash' or 'blink'\n");
1106    changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1107    }
1108 
1109    if (*(skinny_lampmode_t *) dest != mwilamp) {
1110    *(skinny_lampmode_t *) dest = mwilamp;
1111    changed = SCCP_CONFIG_CHANGE_CHANGED;
1112    }
1113    return changed;
1114    }
1115  */
1116 
1117 /*!
1118  * \brief Config Converter/Parser for Tos Value
1119  *
1120  * \note not multi_entry
1121  */
sccp_config_parse_tos(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1122 sccp_value_changed_t sccp_config_parse_tos(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1123 {
1124 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1125 	char *value = pbx_strdupa(v->value);
1126 	uint8_t tos = 0;
1127 
1128 	if (pbx_str2tos(value, &tos)) {
1129 		/* value is tos */
1130 	} else if (sscanf(value, "%" SCNu8, &tos) == 1) {
1131 		tos = tos & 0xff;
1132 	} else if (sccp_strcaseequals(value, "lowdelay")) {
1133 		tos = IPTOS_LOWDELAY;
1134 	} else if (sccp_strcaseequals(value, "throughput")) {
1135 		tos = IPTOS_THROUGHPUT;
1136 	} else if (sccp_strcaseequals(value, "reliability")) {
1137 		tos = IPTOS_RELIABILITY;
1138 
1139 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
1140 	} else if (sccp_strcaseequals(value, "mincost")) {
1141 		tos = IPTOS_MINCOST;
1142 #endif
1143 	} else if (sccp_strcaseequals(value, "none")) {
1144 		tos = 0;
1145 	} else {
1146 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
1147 		changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1148 #else
1149 		changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1150 #endif
1151 		tos = 0x68 & 0xff;
1152 	}
1153 
1154 	if ((*(uint8_t *) dest) != tos) {
1155 		*(uint8_t *) dest = tos;
1156 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1157 	}
1158 	return changed;
1159 }
1160 
1161 /*!
1162  * \brief Config Converter/Parser for Cos Value
1163  *
1164  * \note not multi_entry
1165  */
sccp_config_parse_cos(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1166 sccp_value_changed_t sccp_config_parse_cos(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1167 {
1168 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1169 	char *value = pbx_strdupa(v->value);
1170 	uint8_t cos = 0;
1171 
1172 	if (pbx_str2cos(value, &cos)) {
1173 		/* value is tos */
1174 	} else if (sscanf(value, "%" SCNu8, &cos) == 1) {
1175 		if (cos > 7) {
1176 			pbx_log(LOG_WARNING, "Invalid cos %d value, refer to QoS documentation\n", cos);
1177 			return SCCP_CONFIG_CHANGE_INVALIDVALUE;
1178 		}
1179 	}
1180 
1181 	if ((*(uint8_t *) dest) != cos) {
1182 		*(uint8_t *) dest = cos;
1183 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1184 	}
1185 
1186 	return changed;
1187 }
1188 
1189 /*!
1190  * \brief Config Converter/Parser for AmaFlags Value
1191  *
1192  * \note not multi_entry
1193  */
sccp_config_parse_amaflags(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1194 sccp_value_changed_t sccp_config_parse_amaflags(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1195 {
1196 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1197 	char *value = pbx_strdupa(v->value);
1198 	int amaflags = 0;
1199 
1200 	if (!sccp_strlen_zero(value)) {
1201 		amaflags = pbx_channel_string2amaflag(value);
1202 		if ((*(int *) dest) != amaflags) {
1203 			changed = SCCP_CONFIG_CHANGE_CHANGED;
1204 			*(int *) dest = amaflags;
1205 		}
1206 	}
1207 	return changed;
1208 }
1209 
1210 /*!
1211  * \brief Config Converter/Parser for Secondary Dialtone Digits
1212  *
1213  * \note not multi_entry
1214  */
sccp_config_parse_secondaryDialtoneDigits(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1215 sccp_value_changed_t sccp_config_parse_secondaryDialtoneDigits(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1216 {
1217 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1218 	char *value = pbx_strdupa(v->value);
1219 	char *str = (char *) dest;
1220 
1221 	if (sccp_strlen(value) <= 9) {
1222 		if (!sccp_strcaseequals(str, value)) {
1223 			sccp_copy_string(str, value, 9);
1224 			changed = SCCP_CONFIG_CHANGE_CHANGED;
1225 		}
1226 	} else {
1227 		changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1228 	}
1229 
1230 	return changed;
1231 }
1232 
1233 /*!
1234  * \brief Config Converter/Parser for Callgroup/Pickupgroup Values
1235  *
1236  * \todo check changes to make the function more generic
1237  *
1238  * \note not multi_entry
1239  */
sccp_config_parse_group(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1240 sccp_value_changed_t sccp_config_parse_group(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1241 {
1242 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1243 	char *value = pbx_strdupa(v->value);
1244 
1245 	char * piece = NULL;
1246 	char * c = NULL;
1247 	int start = 0;
1248 
1249 	int finish = 0;
1250 
1251 	int x = 0;
1252 	sccp_group_t group = 0;
1253 
1254 	if (!sccp_strlen_zero(value)) {
1255 		c = pbx_strdupa(value);
1256 
1257 		while ((piece = strsep(&c, ","))) {
1258 			if (sscanf(piece, "%30d-%30d", &start, &finish) == 2) {
1259 				/* Range */
1260 			} else if (sscanf(piece, "%30d", &start)) {
1261 				/* Just one */
1262 				finish = start;
1263 			} else {
1264 				pbx_log(LOG_ERROR, "Syntax error parsing group configuration '%s' at '%s'. Ignoring.\n", value, piece);
1265 				continue;
1266 			}
1267 			for (x = start; x <= finish; x++) {
1268 				if ((x > 63) || (x < 0)) {
1269 					pbx_log(LOG_WARNING, "Ignoring invalid group %d (maximum group is 63)\n", x);
1270 				} else {
1271 					group |= ((ast_group_t) 1 << x);
1272 				}
1273 			}
1274 		}
1275 	}
1276 #if defined(HAVE_UNALIGNED_BUSERROR)										// for example sparc64
1277 	sccp_group_t group_orig = 0;
1278 
1279 	memcpy(&group_orig, dest, sizeof(sccp_group_t));
1280 	if (group_orig != group) {
1281 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1282 		memcpy(dest, &group, sizeof(sccp_group_t));
1283 	}
1284 #else
1285 	if ((*(sccp_group_t *) dest) != group) {
1286 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1287 		*(sccp_group_t *) dest = group;
1288 	}
1289 #endif
1290 	return changed;
1291 }
1292 
1293 /*!
1294  * \brief Config Converter/Parser for Context
1295  *
1296  * \note not multi_entry
1297  */
sccp_config_parse_context(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1298 sccp_value_changed_t sccp_config_parse_context(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1299 {
1300 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1301 	if (v->value && !sccp_strlen_zero(v->value)) {
1302 		char *value = pbx_strdupa(v->value);
1303 		char *str = (char *) dest;
1304 		if (!sccp_strcaseequals(str, value)) {
1305 			changed = SCCP_CONFIG_CHANGE_CHANGED;
1306 			sccp_copy_string((char *)dest, value, size);
1307 			//if (!sccp_strlen_zero(value) && !pbx_context_find((const char *) dest)) {
1308 			//	pbx_log(LOG_WARNING, "The context '%s' you specified might not be available in the dialplan. Please check the sccp.conf\n", (char *) dest);
1309 			//}
1310 		} else {
1311 			changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1312 		}
1313 	}
1314 	return changed;
1315 }
1316 
1317 /*!
1318  * \brief Config Converter/Parser for Hotline Context
1319  *
1320  * \note not multi_entry
1321  */
sccp_config_parse_hotline_context(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1322 sccp_value_changed_t sccp_config_parse_hotline_context(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1323 {
1324 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1325 	char *value = pbx_strdupa(v->value);
1326 	sccp_hotline_t *hotline = *(sccp_hotline_t **) dest;
1327 
1328 	if (hotline->line && !sccp_strcaseequals(hotline->line->context, value)) {
1329 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1330 		if (hotline->line->context) {
1331 			sccp_free(hotline->line->context);
1332 		}
1333 		hotline->line->context = pbx_strdup(value);
1334 	}
1335 	return changed;
1336 }
1337 
1338 /*!
1339  * \brief Config Converter/Parser for Hotline Extension
1340  *
1341  * \note not multi_entry
1342  */
sccp_config_parse_hotline_exten(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1343 sccp_value_changed_t sccp_config_parse_hotline_exten(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1344 {
1345 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1346 	char *value = pbx_strdupa(v->value);
1347 	sccp_hotline_t *hotline = *(sccp_hotline_t **) dest;
1348 
1349 	if (!sccp_strcaseequals(hotline->exten, value)) {
1350 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1351 		pbx_copy_string(hotline->exten, value, SCCP_MAX_EXTENSION);
1352 		if (hotline->line) {
1353 			if (hotline->line->adhocNumber) {
1354 				sccp_free(hotline->line->adhocNumber);
1355 			}
1356 			hotline->line->adhocNumber = pbx_strdup(value);
1357 		}
1358 	}
1359 	return changed;
1360 }
1361 
1362 /*!
1363  * \brief Config Converter/Parser for Hotline Extension
1364  *
1365  * \note not multi_entry
1366  */
sccp_config_parse_hotline_label(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1367 sccp_value_changed_t sccp_config_parse_hotline_label(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1368 {
1369 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1370 	char *value = pbx_strdupa(v->value);
1371 	sccp_hotline_t *hotline = *(sccp_hotline_t **) dest;
1372 
1373 	if (hotline->line && !sccp_strcaseequals(hotline->line->label, value)) {
1374 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1375 		if (hotline->line->label) {
1376 			sccp_free(hotline->line->label);
1377 		}
1378 		hotline->line->label = pbx_strdup(value);
1379 	}
1380 	return changed;
1381 }
1382 
1383 /*!
1384  * \brief Config Converter/Parser for Jitter Buffer Flags
1385  *
1386  * \note not multi_entry
1387  */
sccp_config_parse_jbflags(void * const dest,const char * value,const unsigned int flag)1388 static sccp_value_changed_t sccp_config_parse_jbflags(void * const dest, const char * value, const unsigned int flag)
1389 {
1390 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1391 
1392 	struct ast_jb_conf *jb = *(struct ast_jb_conf **) dest;
1393 
1394 	if (pbx_test_flag(jb, flag) != (unsigned) sccp_true(value)) {
1395 		pbx_set2_flag(jb, sccp_true(value), flag);
1396 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1397 	}
1398 	return changed;
1399 }
1400 
sccp_config_parse_jbflags_enable(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1401 sccp_value_changed_t sccp_config_parse_jbflags_enable(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1402 {
1403 	char *value = pbx_strdupa(v->value);
1404 
1405 	return sccp_config_parse_jbflags(dest, value, AST_JB_ENABLED);
1406 }
1407 
sccp_config_parse_jbflags_force(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1408 sccp_value_changed_t sccp_config_parse_jbflags_force(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1409 {
1410 	char *value = pbx_strdupa(v->value);
1411 
1412 	return sccp_config_parse_jbflags(dest, value, AST_JB_FORCED);
1413 }
1414 
sccp_config_parse_jbflags_log(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1415 sccp_value_changed_t sccp_config_parse_jbflags_log(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1416 {
1417 	char *value = pbx_strdupa(v->value);
1418 
1419 	return sccp_config_parse_jbflags(dest, value, AST_JB_LOG);
1420 }
1421 
sccp_config_parse_jbflags_maxsize(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1422 sccp_value_changed_t sccp_config_parse_jbflags_maxsize(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1423 {
1424 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1425 	int value = sccp_atoi(v->value, strlen(v->value));
1426 	struct ast_jb_conf *jb = *(struct ast_jb_conf **) dest;
1427 
1428 	if (jb->max_size != value) {
1429 		jb->max_size = value;
1430 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1431 	}
1432 	return changed;
1433 }
1434 
sccp_config_parse_jbflags_jbresyncthreshold(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1435 sccp_value_changed_t sccp_config_parse_jbflags_jbresyncthreshold(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1436 {
1437 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1438 	int value = sccp_atoi(v->value, strlen(v->value));
1439 	struct ast_jb_conf *jb = *(struct ast_jb_conf **) dest;
1440 
1441 	if (jb->resync_threshold != value) {
1442 		jb->resync_threshold = value;
1443 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1444 	}
1445 	return changed;
1446 }
1447 
sccp_config_parse_jbflags_impl(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1448 sccp_value_changed_t sccp_config_parse_jbflags_impl(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1449 {
1450 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1451 	char * value = pbx_strdupa(v->value);
1452 	struct ast_jb_conf *jb = *(struct ast_jb_conf **) dest;
1453 
1454 	if (!sccp_strcaseequals(jb->impl,value)) {
1455 		sccp_copy_string(jb->impl, value, sizeof jb->impl);
1456 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1457 	}
1458 	return changed;
1459 }
1460 
1461 /*!
1462  * \brief Config Converter/Parser for WebDir
1463  */
sccp_config_parse_webdir(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1464 sccp_value_changed_t sccp_config_parse_webdir(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1465 {
1466 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1467 	char *value = pbx_strdupa(v->value);
1468 	char *webdir = (char *) dest;
1469 	char new_webdir[PATH_MAX] = "";
1470 
1471 	if (sccp_strlen_zero(value)) {
1472 	        snprintf(new_webdir, sizeof(new_webdir), "%s/%s", ast_config_AST_DATA_DIR, "static-http/");
1473 	} else {
1474                 snprintf(new_webdir, sizeof(new_webdir), "%s", value);
1475 	}
1476 
1477         if (!sccp_strcaseequals(new_webdir, webdir)) {
1478 	        if (access(new_webdir, F_OK ) != -1) {
1479                         changed = SCCP_CONFIG_CHANGE_CHANGED;
1480                         pbx_copy_string(webdir, new_webdir, size);
1481 	        } else {
1482 			pbx_log(LOG_WARNING, "The webdir '%s' specified could not be found.\n", new_webdir);
1483 			pbx_copy_string(webdir, "", size);
1484 	                changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1485 	        }
1486         } else {
1487                 changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1488         }
1489 	return changed;
1490 }
1491 
1492 /*!
1493  * \brief Config Converter/Parser for Debug
1494  *
1495  * \note multi_entry
1496  */
sccp_config_parse_debug(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1497 sccp_value_changed_t sccp_config_parse_debug(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1498 {
1499 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1500 	uint32_t debug_new = 0;
1501 	char * debug_arr[1] = { 0 };
1502 
1503 	for (; v; v = v->next) {
1504 		debug_arr[0] = pbx_strdup (v->value);
1505 		debug_new = sccp_parse_debugline(debug_arr, 0, 1, debug_new);
1506 		sccp_free (debug_arr[0]);
1507 	}
1508 	if (*(uint32_t *) dest != debug_new) {
1509 		*(uint32_t *) dest = debug_new;
1510 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1511 	}
1512 	return changed;
1513 }
1514 
1515 /*!
1516  * \brief Config Converter/Parser for Earlyrtp
1517  *
1518  * temporary "any string" to boolean for earlyrtp
1519  */
sccp_config_parse_earlyrtp(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1520 sccp_value_changed_t sccp_config_parse_earlyrtp(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1521 {
1522 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1523 	boolean_t old = *(boolean_t *)dest;
1524 	boolean_t new = !sccp_false(v->value);
1525 
1526 	if(sccp_strcaseequals(v->value, "none")) {
1527 		new = FALSE;
1528 	}
1529 
1530 	if(new != old) {
1531 		*(boolean_t *)dest = new;
1532 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1533 	}
1534 	return changed;
1535 }
1536 
1537 /*!
1538  * \brief Config Converter/Parser for Codec Preferences
1539  *
1540  * \todo need check to see if preferred_codecs has changed
1541  * \todo do we need to reduce the preferences by the pbx -> skinny codec mapping ?
1542  *
1543  *
1544  * \note multi_entry
1545  */
sccp_config_parse_codec_preferences(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1546 sccp_value_changed_t sccp_config_parse_codec_preferences(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1547 {
1548 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1549 	skinny_capabilities_t *prefs = (skinny_capabilities_t *) dest;
1550 	skinny_codec_t new_codecs[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONE};
1551 	int errors = 0;
1552 
1553 	for (; v; v = v->next) {
1554 		sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) ("sccp_config_parse_codec preference: name: %s, value:%s\n", v->name, v->value);
1555 		if (sccp_strcaseequals(v->name, "disallow")) {
1556 			errors += sccp_codec_parseAllowDisallow(new_codecs, v->value, 0);
1557 		} else if (sccp_strcaseequals(v->name, "allow")) {
1558 			errors += sccp_codec_parseAllowDisallow(new_codecs, v->value, 1);
1559 		} else {
1560 			errors += 1;
1561 		}
1562 	}
1563 
1564 	skinny_codec_t audio_prefs[SKINNY_MAX_CAPABILITIES] = {SKINNY_CODEC_NONE};
1565 	sccp_get_codecs_bytype(new_codecs, audio_prefs, SKINNY_CODEC_TYPE_AUDIO);
1566 #if CS_SCCP_VIDEO
1567 	skinny_codec_t video_prefs[SKINNY_MAX_CAPABILITIES] = {SKINNY_CODEC_NONE};
1568 	sccp_get_codecs_bytype(new_codecs, video_prefs, SKINNY_CODEC_TYPE_VIDEO);
1569 #endif
1570 	if (errors) {
1571 		pbx_log(LOG_NOTICE, "SCCP: (parse_codec preference) Error occured during parsing of the disallowed / allowed codecs\n");
1572 		changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1573 	} else {
1574 		if(memcmp(prefs->audio, audio_prefs, sizeof prefs->audio) != 0) {
1575 			memcpy(prefs->audio, audio_prefs, sizeof prefs->audio);
1576 			changed = SCCP_CONFIG_CHANGE_CHANGED;
1577 		}
1578 #if CS_SCCP_VIDEO
1579 		if(memcmp(prefs->video, video_prefs, sizeof prefs->video) != 0) {
1580 			memcpy(prefs->video, video_prefs, sizeof prefs->video);
1581 			changed = SCCP_CONFIG_CHANGE_CHANGED;
1582 		}
1583 #endif
1584 	}
1585 	return changed;
1586 
1587 }
1588 
1589 /*!
1590  * \brief Config Converter/Parser for Deny IP
1591  *
1592  * \todo need check to see if ha has changed
1593  *
1594  * \note multi_entry
1595  */
sccp_config_parse_deny_permit(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1596 sccp_value_changed_t sccp_config_parse_deny_permit(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1597 {
1598 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1599 	int error = 0;
1600 	int errors = 0;
1601 
1602 	struct sccp_ha *prev_ha = *(struct sccp_ha **) dest;
1603 	struct sccp_ha *ha = NULL;
1604 
1605 	for (; v; v = v->next) {
1606 		//sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) ("sccp_config_parse_deny_permit: name: %s, value:%s\n", v->name, v->value);
1607 		if (sccp_strcaseequals(v->name, "deny")) {
1608 			ha = sccp_append_ha("deny", v->value, ha, &error);
1609 			//sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Deny: %s\n", v->value);
1610 		} else if (sccp_strcaseequals(v->name, "permit") || sccp_strcaseequals(v->name, "localnet")) {
1611 			if (sccp_strcaseequals(v->value, "internal")) {
1612 				ha = sccp_append_ha("permit", "127.0.0.0/255.0.0.0", ha, &error);
1613 				errors |= error;
1614 				ha = sccp_append_ha("permit", "10.0.0.0/255.0.0.0", ha, &error);
1615 				errors |= error;
1616 				ha = sccp_append_ha("permit", "172.16.0.0/255.224.0.0", ha, &error);
1617 				errors |= error;
1618 				ha = sccp_append_ha("permit", "192.168.0.0/255.255.0.0", ha, &error);
1619 			} else {
1620 				ha = sccp_append_ha("permit", v->value, ha, &error);
1621 			}
1622 			//sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Permit: %s\n", v->value);
1623 		}
1624 		errors |= error;
1625 	}
1626 	if (!error) {
1627 		// compare ha elements
1628 		struct ast_str *ha_buf = pbx_str_alloca(DEFAULT_PBX_STR_BUFFERSIZE);
1629 		struct ast_str *prev_ha_buf = pbx_str_alloca(DEFAULT_PBX_STR_BUFFERSIZE);
1630 		if (ha_buf && prev_ha_buf) {
1631 			sccp_print_ha(ha_buf, DEFAULT_PBX_STR_BUFFERSIZE, ha);
1632 			sccp_print_ha(prev_ha_buf, DEFAULT_PBX_STR_BUFFERSIZE, prev_ha);
1633 			if (!sccp_strequals(pbx_str_buffer(ha_buf), pbx_str_buffer(prev_ha_buf))) {
1634 				//sccp_log_and(DEBUGCAT_CONFIG + DEBUGCAT_HIGH) ("hal: %s\nprev_ha: %s\n", pbx_str_buffer(ha_buf), pbx_str_buffer(prev_ha_buf));
1635 				if (prev_ha) {
1636 					sccp_free_ha(prev_ha);
1637 				}
1638 				*(struct sccp_ha **) dest = ha;
1639 				changed = SCCP_CONFIG_CHANGE_CHANGED;
1640 				ha = NULL;					// passed on to dest, will not be freed at exit
1641 			}
1642 		} else {
1643 			pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
1644 			changed = SCCP_CONFIG_CHANGE_ERROR;
1645 		}
1646 	} else {
1647 		sccp_log(DEBUGCAT_CONFIG) (VERBOSE_PREFIX_3 "SCCP: (sccp_config_parse_deny_permit) Invalid\n");
1648 		changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1649 	}
1650 
1651 	/* cleanup resources when not passed on to dest */
1652 	if (ha) {
1653 		sccp_free_ha(ha);
1654 	}
1655 	//sccp_log(DEBUGCAT_CONFIG) (VERBOSE_PREFIX_3 "SCCP: (sccp_config_parse_deny_permit) Return: %d\n", changed);
1656 	return changed;
1657 }
1658 
1659 /*!
1660  * \brief Config Converter/Parser for Permit Hosts
1661  *
1662  * \todo maybe add new DATATYPE_LIST to add string param value to LIST_HEAD to make a generic parser
1663  * \todo need check to see if  has changed
1664  *
1665  * \note multi_entry
1666  * \note order is irrelevant
1667  */
sccp_config_parse_permithosts(void * const dest,const size_t size,PBX_VARIABLE_TYPE * vroot,const sccp_config_segment_t segment)1668 sccp_value_changed_t sccp_config_parse_permithosts(void * const dest, const size_t size, PBX_VARIABLE_TYPE * vroot, const sccp_config_segment_t segment)
1669 {
1670 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1671 	sccp_hostname_t *permithost = NULL;
1672 
1673 	SCCP_LIST_HEAD (hostname, sccp_hostname_t) * permithostList = (struct hostname *)dest;
1674 
1675 	PBX_VARIABLE_TYPE *v = NULL;
1676 	int listCount = SCCP_LIST_GETSIZE(permithostList);
1677 	int varCount = 0;
1678 	int found = 0;
1679 
1680 	for (v = vroot; v; v = v->next) {
1681 		SCCP_LIST_TRAVERSE(permithostList, permithost, list) {
1682 			if (sccp_strcaseequals(permithost->name, v->value)) {					// variable found
1683 				found++;
1684 				break;
1685 			}
1686 		}
1687 		varCount++;
1688 	}
1689 	if (listCount != varCount || listCount != found) {							// build new list
1690 		while ((permithost = SCCP_LIST_REMOVE_HEAD(permithostList, list))) {				// clear list
1691 			sccp_free(permithost);
1692 		}
1693 		for (v = vroot; v; v = v->next) {
1694 			if (!(permithost = (sccp_hostname_t *)sccp_calloc(1, sizeof(sccp_hostname_t)))) {
1695 				pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
1696 				return SCCP_CONFIG_CHANGE_ERROR;
1697 			}
1698 			sccp_copy_string(permithost->name, v->value, sizeof(permithost->name));
1699 			SCCP_LIST_INSERT_TAIL(permithostList, permithost, list);
1700 		}
1701 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1702 	}
1703 	return changed;
1704 }
1705 
addonstr2enum(const char * addonstr)1706 static skinny_devicetype_t addonstr2enum(const char *addonstr)
1707 {
1708 	if (sccp_strcaseequals(addonstr, "7914")) {
1709 		return SKINNY_DEVICETYPE_CISCO_ADDON_7914;
1710 	}
1711 	if (sccp_strcaseequals(addonstr, "7915")) {
1712 		return SKINNY_DEVICETYPE_CISCO_ADDON_7915_24BUTTON;
1713 	}
1714 	if (sccp_strcaseequals(addonstr, "7916")) {
1715 		return SKINNY_DEVICETYPE_CISCO_ADDON_7916_24BUTTON;
1716 	}
1717 	if (sccp_strcaseequals(addonstr, "500S")) {
1718 		return SKINNY_DEVICETYPE_CISCO_ADDON_SPA500S;
1719 	}
1720 	if (sccp_strcaseequals(addonstr, "500DS")) {
1721 		return SKINNY_DEVICETYPE_CISCO_ADDON_SPA500DS;
1722 	}
1723 	if (sccp_strcaseequals(addonstr, "932DS")) {
1724 		return SKINNY_DEVICETYPE_CISCO_ADDON_SPA932DS;
1725 	}
1726 	sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "SCCP: Unknown addon type (%s)\n", addonstr);
1727 	return SKINNY_DEVICETYPE_SENTINEL;
1728 }
1729 
1730 /*!
1731  * \brief Config Converter/Parser for addons
1732  *
1733  * \todo make more generic
1734  * \todo cleanup original implementation in sccp_utils.c
1735  *
1736  * \note multi_entry
1737  */
sccp_config_parse_addons(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1738 sccp_value_changed_t sccp_config_parse_addons(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1739 {
1740 	unsigned int changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1741 	sccp_addon_t *addon = NULL;
1742 	skinny_devicetype_t addon_type = 0;
1743 
1744 	SCCP_LIST_HEAD (addon, sccp_addon_t) * addonList = (struct addon *)dest;
1745 
1746 	SCCP_LIST_TRAVERSE_SAFE_BEGIN(addonList, addon, list) {
1747 		if (v) {
1748 			if (!sccp_strlen_zero(v->value)) {
1749 				if((addon_type = addonstr2enum(v->value)) && addon_type != SKINNY_DEVICETYPE_SENTINEL) {
1750 					if (addon->type != addon_type) {					/* change/update */
1751 						sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH))("change addon: %s(%d) => %s(%d)\n", skinny_devicetype2str(addon->type), addon->type, skinny_devicetype2str(addon_type),
1752 												addon_type);
1753 						addon->type = addon_type;
1754 						changed |= SCCP_CONFIG_CHANGE_CHANGED;
1755 					}
1756 				} else {
1757 					pbx_log(LOG_ERROR, "unknown addon type: %s, skipped\n", v->value);
1758 					changed |= SCCP_CONFIG_CHANGE_INVALIDVALUE;
1759 				}
1760 			}
1761 			v = v->next;
1762 		} else {											/* removal */
1763 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) ("remove addon: %d\n", addon->type);
1764 			SCCP_LIST_REMOVE_CURRENT(list);
1765 			sccp_free(addon);
1766 			changed |= SCCP_CONFIG_CHANGE_CHANGED;
1767 		}
1768 	}
1769 	SCCP_LIST_TRAVERSE_SAFE_END;
1770 
1771 	int addon_counter = 0;
1772 
1773 	for (; v; v = v->next) {										/* addition */
1774 		if (2 > addon_counter++) {
1775 			if (!sccp_strlen_zero(v->value)) {
1776 				if((addon_type = addonstr2enum(v->value)) && addon_type != SKINNY_DEVICETYPE_SENTINEL) {
1777 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH))("add new addon: %s(%d)\n", skinny_devicetype2str(addon_type), addon_type);
1778 					if (!(addon = (sccp_addon_t *)sccp_calloc(1, sizeof(sccp_addon_t)))) {
1779 						pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
1780 						return SCCP_CONFIG_CHANGE_ERROR;
1781 					}
1782 					addon->type = addon_type;
1783 					SCCP_LIST_INSERT_TAIL(addonList, addon, list);
1784 					changed |= SCCP_CONFIG_CHANGE_CHANGED;
1785 				} else {
1786 					pbx_log(LOG_ERROR, "unknown addon type: %s, skipped\n", v->value);
1787 					changed |= SCCP_CONFIG_CHANGE_INVALIDVALUE;
1788 				}
1789 			}
1790 		} else {
1791 			pbx_log(LOG_ERROR, "SCCP: maximum number(2) of addon's has been reached\n");
1792 			changed |= SCCP_CONFIG_CHANGE_INVALIDVALUE;
1793 		}
1794 	}
1795 	return (sccp_value_changed_t)changed;
1796 }
1797 
1798 /*!
1799  * \brief Config Converter/Parser for Mailbox Value
1800  *
1801  * \note multi_entry
1802  * \note order is irrelevant
1803  */
sccp_config_parse_mailbox(void * const dest,const size_t size,PBX_VARIABLE_TYPE * vroot,const sccp_config_segment_t segment)1804 sccp_value_changed_t sccp_config_parse_mailbox(void * const dest, const size_t size, PBX_VARIABLE_TYPE * vroot, const sccp_config_segment_t segment)
1805 {
1806 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1807 	sccp_mailbox_t *mailbox = NULL;
1808 
1809 	SCCP_LIST_HEAD (mailbox, sccp_mailbox_t) * mailboxList = (struct mailbox *)dest;
1810 
1811 	PBX_VARIABLE_TYPE *v = NULL;
1812 	int varCount = 0;
1813 	int listCount = 0;
1814 
1815 	listCount = mailboxList->size;
1816 	int notfound = 0;
1817 
1818 	for (v = vroot; v; v = v->next) {
1819 		if (!sccp_strlen_zero(v->value)) {
1820 			varCount++;
1821 		}
1822 	}
1823 
1824 	if (varCount == listCount) {										// list length equal
1825 		SCCP_LIST_TRAVERSE(mailboxList, mailbox, list) {
1826 			for (v = vroot; v; v = v->next) {
1827 				if (!sccp_strlen_zero(v->value)) {
1828 					char uniqueid[SCCP_MAX_MAILBOX_UNIQUEID];
1829 					snprintf(uniqueid, sizeof(uniqueid), "%s%s", v->value, !strstr(v->value, "@") ? "@default" : "");
1830 					if (sccp_strcaseequals(mailbox->uniqueid, uniqueid)) {
1831 						continue;
1832 					}
1833 					notfound += 1;
1834 				}
1835 			}
1836 		}
1837 	}
1838 	if (varCount != listCount || notfound) {								// build new list
1839 		while ((mailbox = SCCP_LIST_REMOVE_HEAD(mailboxList, list))) {					// clear list
1840 			sccp_free(mailbox);
1841 		}
1842 		for (v = vroot; v; v = v->next) {								// create new list
1843 			if (!sccp_strlen_zero(v->value)) {
1844 				sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "add new mailbox: '%s'\n", v->value);
1845 				if (!(mailbox = (sccp_mailbox_t *)sccp_calloc(1, sizeof(sccp_mailbox_t)))) {
1846 					pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
1847 					return SCCP_CONFIG_CHANGE_ERROR;
1848 				}
1849 				snprintf(mailbox->uniqueid, sizeof(mailbox->uniqueid), "%s%s", v->value, !strstr(v->value, "@") ? "@default" : "");
1850 				SCCP_LIST_INSERT_TAIL(mailboxList, mailbox, list);
1851 			}
1852 		}
1853 		changed = SCCP_CONFIG_CHANGE_CHANGED;
1854 	}
1855 	return changed;
1856 }
1857 
1858 /*!
1859  * \brief Config Converter/Parser for PBX Variables
1860  *
1861  * \note multi_entry
1862  */
sccp_config_parse_variables(void * const dest,const size_t size,PBX_VARIABLE_TYPE * v,const sccp_config_segment_t segment)1863 sccp_value_changed_t sccp_config_parse_variables(void * const dest, const size_t size, PBX_VARIABLE_TYPE * v, const sccp_config_segment_t segment)
1864 {
1865 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1866 
1867 	PBX_VARIABLE_TYPE *variableList = *(PBX_VARIABLE_TYPE **) dest;
1868 
1869 	if (variableList) {
1870 		pbx_variables_destroy(variableList);								/* destroy old list */
1871 		variableList = NULL;
1872 	}
1873 	PBX_VARIABLE_TYPE *variable = variableList;
1874 	char * var_name = NULL;
1875 	char * var_value = NULL;
1876 
1877 	for (; v; v = v->next) {										/* create new list */
1878 		var_name = pbx_strdup (v->value);
1879 		var_value = NULL;
1880 		if ((var_value = strchr(var_name, '='))) {
1881 			*var_value++ = '\0';
1882 		}
1883 		if (!sccp_strlen_zero(var_name) && !sccp_strlen_zero(var_value)) {
1884 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) ("add new variable: %s=%s\n", var_name, var_value);
1885 			if (!variable) {
1886 				if (!(variableList = pbx_variable_new(var_name, var_value, ""))) {
1887 					pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
1888 					variableList = NULL;
1889 					break;
1890 				}
1891 				variable = variableList;
1892 			} else {
1893 				if (!(variable->next = pbx_variable_new(var_name, var_value, ""))) {
1894 					pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
1895 					pbx_variables_destroy(variableList);
1896 					variableList = NULL;
1897 					break;
1898 				}
1899 				variable = variable->next;
1900 			}
1901 		}
1902 		sccp_free (var_name);
1903 	}
1904 	*(PBX_VARIABLE_TYPE **) dest = variableList;
1905 
1906 	return changed;
1907 }
1908 
1909 /*!
1910  * \brief Config Converter/Parser for Buttons
1911  *
1912  * \todo Build a check to see if the button has changed
1913  *
1914  * \note multi_entry
1915  */
sccp_config_parse_button(void * const dest,const size_t size,PBX_VARIABLE_TYPE * vars,const sccp_config_segment_t segment)1916 sccp_value_changed_t sccp_config_parse_button(void * const dest, const size_t size, PBX_VARIABLE_TYPE * vars, const sccp_config_segment_t segment)
1917 {
1918 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_CHANGED;
1919 
1920 	/* parse all button definitions in one pass */
1921 	char * buttonType = NULL;
1922 
1923 	char * buttonName = NULL;
1924 
1925 	char * buttonOption = NULL;
1926 
1927 	char * buttonArgs = NULL;
1928 	char k_button[256];
1929 	char * splitter = NULL;
1930 	sccp_config_buttontype_t type = EMPTY;									/* default to empty */
1931 	uint buttonindex = 0;
1932 
1933 	sccp_buttonconfig_list_t *buttonconfigList = (sccp_buttonconfig_list_t *)dest;
1934 	sccp_buttonconfig_t *config = NULL;
1935 	PBX_VARIABLE_TYPE * first_var = vars;
1936 	PBX_VARIABLE_TYPE * v = NULL;
1937 
1938 	/* temp current buttonconfiglist status*/
1939 	{
1940 		SCCP_LIST_LOCK(buttonconfigList);
1941 		sccp_log_and((DEBUGCAT_DEVICE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_2 "buttonconfig status before check\n");
1942 		SCCP_LIST_TRAVERSE(buttonconfigList, config, list) {
1943 			sccp_log_and((DEBUGCAT_DEVICE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "index:%d, type:%-10.10s (%d), pendingDelete:%s, pendingUpdate:%s\n",
1944 			config->index, sccp_config_buttontype2str(config->type), config->type, config->pendingDelete ? "True" : "False", config->pendingUpdate ? "True" : "False");
1945 		}
1946 		SCCP_LIST_UNLOCK(buttonconfigList);
1947 	}
1948 	/* temp */
1949 
1950 	if (GLOB(reload_in_progress)) {
1951 		changed = SCCP_CONFIG_CHANGE_NOCHANGE;
1952 		sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "SCCP: Checking Button Config\n");
1953 		/* check if the number of buttons got reduced */
1954 		for (v = first_var; v && !sccp_strlen_zero(v->value); v = v->next) {				/* check buttons against currently loaded set*/
1955 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Checking button: %s\n", v->value);
1956 			sccp_copy_string(k_button, v->value, sizeof(k_button));
1957 			splitter = k_button;
1958 			buttonType = strsep(&splitter, ",");
1959 			buttonName = strsep(&splitter, ",");
1960 			buttonOption = strsep(&splitter, ",");
1961 			buttonArgs = splitter;
1962 
1963 			type = sccp_config_buttontype_str2val(buttonType);
1964 			if (type == SCCP_CONFIG_BUTTONTYPE_SENTINEL) {
1965 				pbx_log(LOG_WARNING, "Unknown button type '%s'.\n", buttonType);
1966 				changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
1967 				type = EMPTY;
1968 			}
1969 			if ((changed = sccp_config_checkButton(buttonconfigList, buttonindex, type, buttonName ? pbx_strip(buttonName) : NULL, buttonOption ? pbx_strip(buttonOption) : NULL, buttonArgs ? pbx_strip(buttonArgs) : NULL))) {
1970 				sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Button: %s changed. Giving up on checking buttonchanges, reloading all of them.\n", v->value);
1971 				break;
1972 			}
1973 			buttonindex++;
1974 		}
1975 		if (!changed && SCCP_LIST_GETSIZE(buttonconfigList) != buttonindex) {
1976 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Number of Buttons changed (%d != %d). Reloading all of them.\n", SCCP_LIST_GETSIZE(buttonconfigList), buttonindex);
1977 			changed = SCCP_CONFIG_CHANGE_CHANGED;
1978 		}
1979 		/* Clear/Set pendingDelete and PendingUpdate if button has changed or not
1980 		 *
1981 		 * \note Moved here from device_post_reload so that we will know the new state before line_post_reload.
1982 		 * That way adding/removind a line while accidentally keeping the button config for that line still works.
1983 		 */
1984 		if (!changed) {
1985 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Nothing changed, clear the pendingDelete and pendingUpdate settings\n");
1986 			SCCP_LIST_LOCK(buttonconfigList);
1987 			SCCP_LIST_TRAVERSE(buttonconfigList, config, list) {
1988 				config->pendingDelete = 0;
1989 				config->pendingUpdate = 0;
1990 			}
1991 			SCCP_LIST_UNLOCK(buttonconfigList);
1992 		}
1993 	}
1994 	/* temp current buttonconfiglist status*/
1995 	{
1996 		SCCP_LIST_LOCK(buttonconfigList);
1997 		sccp_log_and((DEBUGCAT_DEVICE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_2 "buttonconfig status after check\n");
1998 		SCCP_LIST_TRAVERSE(buttonconfigList, config, list) {
1999 			sccp_log_and((DEBUGCAT_DEVICE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "index:%d, type:%-10.10s (%d), pendingDelete:%s, pendingUpdate:%s\n",
2000 				config->index, sccp_config_buttontype2str(config->type), config->type, config->pendingDelete ? "True" : "False", config->pendingUpdate ? "True" : "False");
2001 		}
2002 		SCCP_LIST_UNLOCK(buttonconfigList);
2003 	}
2004 	/* temp */
2005 	if (changed) {
2006 		buttonindex = 0;										/* buttonconfig has changed. Load all buttons as new ones */
2007 		sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "Any Previous ButtonConfig will be discared during post-process\n");
2008 		for (v = first_var; v && !sccp_strlen_zero(v->value); v = v->next) {
2009 			sccp_copy_string(k_button, v->value, sizeof(k_button));
2010 			splitter = k_button;
2011 			buttonType = strsep(&splitter, ",");
2012 			buttonName = strsep(&splitter, ",");
2013 			buttonOption = strsep(&splitter, ",");
2014 			buttonArgs = splitter;
2015 
2016 			type = sccp_config_buttontype_str2val(buttonType);
2017 			if (type == SCCP_CONFIG_BUTTONTYPE_SENTINEL) {
2018 				pbx_log(LOG_WARNING, "Unknown button type '%s'. Will insert an Empty button instead.\n", buttonType);
2019 				changed = SCCP_CONFIG_CHANGE_INVALIDVALUE;
2020 				type = EMPTY;
2021 			}
2022 			sccp_config_addButton(buttonconfigList, buttonindex, type, buttonName ? pbx_strip(buttonName) : NULL, buttonOption ? pbx_strip(buttonOption) : NULL, buttonArgs ? pbx_strip(buttonArgs) : NULL);
2023 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Added button: %s\n", v->value);
2024 			buttonindex++;
2025 		}
2026 	}
2027 
2028 	/* temp current buttonconfiglist status*/
2029 	{
2030 		SCCP_LIST_LOCK(buttonconfigList);
2031 		sccp_log_and((DEBUGCAT_DEVICE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_2 "buttonconfig status after adding new buttons\n");
2032 		SCCP_LIST_TRAVERSE(buttonconfigList, config, list) {
2033 			sccp_log_and((DEBUGCAT_DEVICE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "index:%d, type:%-10.10s (%d), pendingDelete:%s, pendingUpdate:%s\n",
2034 				config->index, sccp_config_buttontype2str(config->type), config->type, config->pendingDelete ? "True" : "False", config->pendingUpdate ? "True" : "False");
2035 		}
2036 		SCCP_LIST_UNLOCK(buttonconfigList);
2037 	}
2038 	/* temp */
2039 
2040 	/* return changed status */
2041 	if (GLOB(reload_in_progress)) {
2042 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_3 "buttonconfig: %s\n", changed ? "changed" : "remained the same");
2043 	}
2044 
2045 	return changed;
2046 }
2047 
2048 /* end dyn config */
2049 /*!
2050  * \brief check a Button against the current ButtonConfig on a device
2051  *
2052  * \param buttonconfigList pointer to the device->buttonconfig list
2053  * \param buttonindex button index
2054  * \param type type of button
2055  * \param name name
2056  * \param options options
2057  * \param args args
2058  *
2059  * \callgraph
2060  * \callergraph
2061  *
2062  * \todo Build a check to see if the button has changed
2063  */
sccp_config_checkButton(sccp_buttonconfig_list_t * buttonconfigList,int buttonindex,sccp_config_buttontype_t type,const char * name,const char * options,const char * args)2064 sccp_value_changed_t sccp_config_checkButton(sccp_buttonconfig_list_t *buttonconfigList, int buttonindex, sccp_config_buttontype_t type, const char *name, const char *options, const char *args)
2065 {
2066 	sccp_buttonconfig_t *config = NULL;
2067 	char * parse;
2068 	AST_DECLARE_APP_ARGS (elems, AST_APP_ARG (option); AST_APP_ARG (arg););
2069 	if (args && !sccp_strlen_zero (args)) {
2070 		parse = pbx_strdupa (args);
2071 		AST_STANDARD_APP_ARGS (elems, parse);
2072 	}
2073 
2074 	// boolean_t is_new = FALSE;
2075 	sccp_value_changed_t changed = SCCP_CONFIG_CHANGE_NOCHANGE;
2076 
2077 	SCCP_LIST_LOCK(buttonconfigList);
2078 	SCCP_LIST_TRAVERSE(buttonconfigList, config, list) {
2079 		if (config->index == buttonindex) {
2080 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Found Button index at %d:%d\n", config->index, buttonindex);
2081 			break;
2082 		}
2083 	}
2084 	SCCP_LIST_UNLOCK(buttonconfigList);
2085 
2086 	changed = SCCP_CONFIG_CHANGE_CHANGED;
2087 	if (config) {
2088 		switch (type) {
2089 			case LINE:
2090 			{
2091 				char extension[SCCP_MAX_EXTENSION];
2092 				sccp_subscription_id_t subscriptionId;
2093 				int parseRes = sccp_parseComposedId(name, 80, &subscriptionId, extension);
2094 				if (parseRes) {
2095 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: ComposedId extension: %s, subscriptionId[number:%s, name:%s, label:%s, aux:%s]\n", extension, subscriptionId.number, subscriptionId.name, subscriptionId.label, subscriptionId.aux);
2096 					if (LINE == config->type &&
2097 						sccp_strequals(config->label, name) &&
2098 						sccp_strequals(config->button.line.name, extension) &&
2099 						((!config->button.line.subscriptionId && parseRes == 1) ||
2100 						(config->button.line.subscriptionId &&
2101 							(
2102 								sccp_strcaseequals(config->button.line.subscriptionId->number, subscriptionId.number) &&
2103 								sccp_strequals(config->button.line.subscriptionId->name, subscriptionId.name) &&
2104 								sccp_strequals(config->button.line.subscriptionId->label, subscriptionId.label) &&
2105 								sccp_strequals(config->button.line.subscriptionId->aux, subscriptionId.aux)
2106 							)
2107 						))
2108 					) {
2109 						if (!options || sccp_strequals(config->button.line.options, options)) {
2110 							sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Line Button Definition remained the same\n");
2111 							changed = SCCP_CONFIG_CHANGE_NOCHANGE;
2112 						} else {
2113 							sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "options: %s <-> %s  (%d)\n", config->button.line.options, options, sccp_strequals(config->button.line.options, options));
2114 						}
2115 					}
2116 				} else {
2117 					pbx_log(LOG_WARNING, "SCCP: button definition:'%s' could not be parsed\n", name);
2118 				}
2119 				break;
2120 			}
2121 			case SPEEDDIAL:
2122 				/* \todo check if values change */
2123 				if (SPEEDDIAL == config->type && sccp_strequals(config->label, name) && sccp_strequals(config->button.speeddial.ext, options)) {
2124 					if (!args || sccp_strequals(config->button.speeddial.hint, args)) {
2125 						sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Speeddial Button Definition remained the same\n");
2126 						changed = SCCP_CONFIG_CHANGE_NOCHANGE;
2127 					}
2128 				}
2129 				break;
2130 			case SERVICE:
2131 				if (SERVICE == config->type && sccp_strequals(config->label, name) && sccp_strequals(config->button.service.url, options)) {
2132 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Service Button Definition remained the same\n");
2133 					changed = SCCP_CONFIG_CHANGE_NOCHANGE;
2134 				}
2135 				break;
2136 			case FEATURE:
2137 				if (	FEATURE == config->type
2138 					&& buttonindex == config->index
2139 					&& sccp_strequals(config->label, name)
2140 					&& config->button.feature.id == sccp_feature_type_str2val(options)
2141 				) {
2142 					char *default_option = "";
2143 					char *default_arg = "";
2144 					char combined_args[512] = "";
2145 					char combined_current[512] = "";
2146 					snprintf(combined_current, sizeof(combined_current), "%s, %s",
2147 						config->button.feature.options ? config->button.feature.options : "",
2148 						config->button.feature.args ? config->button.feature.args : "");
2149 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Feature Button Definition:%s,%s -> %s,%s\n", config->button.feature.options, config->button.feature.args, elems.option, elems.arg);
2150 					if(SCCP_FEATURE_PARKINGLOT == config->button.feature.id) {
2151 						default_option = "default";
2152 						default_arg = "RetrieveSingle";
2153 					} else
2154 #ifdef CS_DEVSTATE_FEATURE
2155 					if(SCCP_FEATURE_DEVSTATE == config->button.feature.id) {
2156 						default_option = config->label;
2157 						default_arg = "00001|10012|22321";
2158 					}
2159 #endif
2160 					snprintf(combined_args, sizeof(combined_args), "%s, %s", elems.option ? elems.option : default_option, elems.arg ? elems.arg : default_arg);
2161 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH))(VERBOSE_PREFIX_4 "old:'%s' / new:'%s'\n", combined_current, combined_args);
2162 					if (
2163 						(sccp_strequals(combined_current, combined_args))
2164 					) {
2165 						sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Feature Button Definition remained the same\n");
2166 						changed = SCCP_CONFIG_CHANGE_NOCHANGE;
2167 						break;
2168 					}
2169 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Feature Button Definition changed\n");
2170 				}
2171 				break;
2172 			case EMPTY:
2173 				if (EMPTY == config->type) {
2174 					sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Button Definition remained the same\n");
2175 					changed = SCCP_CONFIG_CHANGE_NOCHANGE;
2176 				}
2177 				break;
2178 			default:
2179 				sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_4 "SCCP: Unknown ButtonType: %d\n", type);
2180 				break;
2181 		}
2182 	}
2183 	if (changed) {
2184 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_4 "SCCP: ButtonTemplate has changed\n");
2185 	} else {
2186 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_4 "SCCP: ButtonTemplate remained the same\n");
2187 	}
2188 	return changed;
2189 }
2190 
2191 
2192 /*!
2193  * \brief add a Button to a device
2194  *
2195  * \param buttonconfigList pointer to the device->buttonconfig list
2196  * \param buttonindex button index
2197  * \param type type of button
2198  * \param name name
2199  * \param options options
2200  * \param args args
2201  *
2202  * \callgraph
2203  * \callergraph
2204  *
2205  * \todo Build a check to see if the button has changed
2206  */
sccp_config_addButton(sccp_buttonconfig_list_t * buttonconfigList,int buttonindex,sccp_config_buttontype_t type,const char * name,const char * options,const char * args)2207 sccp_value_changed_t sccp_config_addButton(sccp_buttonconfig_list_t *buttonconfigList, int buttonindex, sccp_config_buttontype_t type, const char *name, const char *options, const char *args)
2208 {
2209 	sccp_buttonconfig_t *config = NULL;
2210 
2211 	char * parse;
2212 	AST_DECLARE_APP_ARGS (elems, AST_APP_ARG (option); AST_APP_ARG (arg););
2213 	if (args && !sccp_strlen_zero (args)) {
2214 		parse = pbx_strdupa (args);
2215 		AST_STANDARD_APP_ARGS (elems, parse);
2216 	}
2217 
2218 	sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Loading New Button Config\n");
2219 	/*
2220 	if (!sccp_config_buttontype_exists(type)) {
2221 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_4 "SCCP: Unknown ButtonType. Skipping\n");
2222 		return SCCP_CONFIG_CHANGE_INVALIDVALUE;
2223 	}
2224 	*/
2225 
2226 	SCCP_LIST_LOCK(buttonconfigList);
2227 	if (!(config = (sccp_buttonconfig_t *)sccp_calloc(1, sizeof(sccp_buttonconfig_t)))) {
2228 		pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
2229 		return SCCP_CONFIG_CHANGE_ERROR;
2230 	}
2231 	config->index = buttonindex;
2232 	config->type = type;
2233 	sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "New %s Button '%s' at : %d:%d\n", sccp_config_buttontype2str(type), name, buttonindex, config->index);
2234 	SCCP_LIST_INSERT_TAIL(buttonconfigList, config, list);
2235 	SCCP_LIST_UNLOCK(buttonconfigList);
2236 
2237 	/* replace faulty button declarations with an empty button */
2238 	if (type != EMPTY && (sccp_strlen_zero(name) || (type != LINE && !options))) {
2239 		sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_1 "SCCP: Faulty %s Button Configuration found at buttonindex: %d, name: %s, options: %s, args: %s. Substituted with  EMPTY button\n", sccp_config_buttontype2str(type), config->index, name, options, args);
2240 		type = EMPTY;
2241 	}
2242 
2243 	switch (type) {
2244 		case LINE:
2245 		{
2246 			char extension[SCCP_MAX_EXTENSION];
2247 			sccp_subscription_id_t * subscriptionId = (sccp_subscription_id_t *)sccp_calloc(1, sizeof(sccp_subscription_id_t));
2248 			if (!subscriptionId) {
2249 				pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
2250 				return SCCP_CONFIG_CHANGE_INVALIDVALUE;
2251  			}
2252 			if (sccp_parseComposedId(name, 80, subscriptionId, extension)) {;
2253 				sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Line Button Definition\n");
2254 				sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: ComposedId extension: %s, subscriptionId[number:%s, name:%s, label:%s, aux:%s]\n", extension, subscriptionId->number, subscriptionId->name, subscriptionId->label, subscriptionId->aux);
2255 				config->type = LINE;
2256 				config->label = pbx_strdup(name);
2257 				config->button.line.name = pbx_strdup(extension);
2258 
2259 				if (!sccp_strlen_zero(subscriptionId->number) || !sccp_strlen_zero(subscriptionId->name) || !sccp_strlen_zero(subscriptionId->label) || !sccp_strlen_zero(subscriptionId->aux)) {
2260 					config->button.line.subscriptionId = subscriptionId;
2261 				} else {
2262 					sccp_free(subscriptionId);
2263 				}
2264 			} else {
2265 				pbx_log(LOG_WARNING, "SCCP: button definition:'%s' could not be parsed\n", name);
2266 				sccp_free(subscriptionId);
2267 				return SCCP_CONFIG_CHANGE_INVALIDVALUE;
2268 			}
2269 			if (options) {
2270 				config->button.line.options = pbx_strdup(options);
2271 			} else {
2272 				config->button.line.options = NULL;
2273 			}
2274 			break;
2275 		}
2276 		case SPEEDDIAL:
2277 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Speeddial Button Definition\n");
2278 			config->type = SPEEDDIAL;
2279 			config->label = pbx_strdup(name);
2280 			config->button.speeddial.ext = pbx_strdup(options);
2281 			if (args) {
2282 				config->button.speeddial.hint = pbx_strdup(args);
2283 			} else {
2284 				config->button.speeddial.hint = NULL;
2285 			}
2286 			break;
2287 		case SERVICE:
2288 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Service Button Definition\n");
2289 			config->type = SERVICE;
2290 			config->label = pbx_strdup(name);
2291 			config->button.service.url = pbx_strdup(options);
2292 			break;
2293 		case FEATURE:
2294 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Feature Button Definition\n");
2295 			sccp_log_and((DEBUGCAT_FEATURE + DEBUGCAT_FEATURE_BUTTON + DEBUGCAT_BUTTONTEMPLATE)) (VERBOSE_PREFIX_4 "featureID: %s\n", options);
2296 			config->type = FEATURE;
2297 			config->label = pbx_strdup(name);
2298 			config->button.feature.id = sccp_feature_type_str2val(options);
2299 
2300 			config->button.feature.options = NULL;
2301 			config->button.feature.args = NULL;
2302 
2303 			if(SCCP_FEATURE_PARKINGLOT == config->button.feature.id) {
2304 				if(elems.option && !sccp_strlen_zero(elems.option)) {
2305 					config->button.feature.options = pbx_strdup(elems.option);
2306 				} else {
2307 					config->button.feature.options = pbx_strdup("default");
2308 				}
2309 				if(elems.arg && !sccp_strlen_zero(elems.arg)) {
2310 					config->button.feature.args = pbx_strdup(elems.arg);
2311 				} else {
2312 					config->button.feature.args = pbx_strdup("RetrieveSingle");
2313 				}
2314 			} else
2315 #ifdef CS_DEVSTATE_FEATURE
2316 			if(SCCP_FEATURE_DEVSTATE == config->button.feature.id) {
2317 				// Value1:State:0:UNKNOWN|1:NOT_INUSE|2:INUSE|3:BUSY|4:INVALID|5:UNAVAILABLE|6:RINGING|7:RINGINUSE|8:ONHOLD
2318 				// Value2:RythmCode:0-1:off,2:on,3-7:different_speeds,
2319 				// Value3:ColorCode:0:off,1:green,2:red,3:orange
2320 				// Value4:IconCode:0:off,1:open,2:closed,3:box
2321 				// Value5:NextState:0:UNKNOWN|1:NOT_INUSE|2:INUSE|3:BUSY|4:INVALID|5:UNAVAILABLE|6:RINGING|7:RINGINUSE|8:ONHOLD
2322 
2323 				// char *args_str = "devstname,[{"s":"NOT_INUSE","c":"off","r":"off","i":"open","n":"INUSE"},{"s":"INUSE","c":"orange","r":"slow","i":"closed","n":NOT_INUSE}]"
2324 				// char *args_str = "devstname,[{"s":1,"c":0,"r":0,"i":1,"n":2},{"s":2,"c":3,"r":2,"i":2,"n":1}]"
2325 				// char *args_str = "devstname,10012|22321"
2326 				if(elems.option && !sccp_strlen_zero(elems.option)) {
2327 					config->button.feature.options = pbx_strdup(elems.option);
2328 				} else {
2329 					config->button.feature.options = pbx_strdup(config->label);
2330 				}
2331 				if(elems.arg && !sccp_strlen_zero(elems.arg)) {
2332 					config->button.feature.args = pbx_strdup(elems.arg);
2333 				} else {
2334 					config->button.feature.args = pbx_strdup("00001|10012|22321");
2335 				}
2336 			} else
2337 #endif
2338 			{
2339 				if(elems.option && !sccp_strlen_zero(elems.option)) {
2340 					config->button.feature.options = pbx_strdup(elems.option);
2341 				}
2342 				if(elems.arg && !sccp_strlen_zero(elems.arg)) {
2343 					config->button.feature.args = pbx_strdup(elems.arg);
2344 				}
2345 			}
2346 			sccp_log_and((DEBUGCAT_FEATURE + DEBUGCAT_FEATURE_BUTTON + DEBUGCAT_BUTTONTEMPLATE))(VERBOSE_PREFIX_4 "Configured feature button:%d with featureID: %s args: %s\n", config->instance,
2347 													 config->button.feature.options, config->button.feature.args);
2348 			break;
2349 		case EMPTY:
2350 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Empty Button Definition\n");
2351 			config->type = EMPTY;
2352 			config->label = NULL;
2353 			break;
2354 		default:
2355 			sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCP: Unknown Button Type:%d\n", type);
2356 			config->type = EMPTY;
2357 			config->label = NULL;
2358 			break;
2359 	}
2360 	return SCCP_CONFIG_CHANGE_CHANGED;
2361 }
2362 
2363 /*!
2364  * \brief Build Line
2365  * \param l SCCP Line
2366  * \param v Asterisk Variable
2367  * \param lineName Name of line as char
2368  * \param isRealtime is Realtime as Boolean
2369  *
2370  * \callgraph
2371  * \callergraph
2372  *
2373  */
sccp_config_buildLine(sccp_line_t * l,PBX_VARIABLE_TYPE * v,boolean_t isRealtime)2374 static void sccp_config_buildLine(sccp_line_t * l, PBX_VARIABLE_TYPE * v, boolean_t isRealtime)
2375 {
2376 	sccp_configurationchange_t res = sccp_config_applyLineConfiguration(l, v);
2377 	if(!l) {
2378 		pbx_log(LOG_ERROR, "SCCP: (sccp_config_buildLine) called without valid line ptr\n");
2379 		return;
2380 	}
2381 
2382 #ifdef CS_SCCP_REALTIME
2383 	l->realtime = isRealtime;
2384 #endif
2385 	// if (GLOB(reload_in_progress) && res == SCCP_CONFIG_NEEDDEVICERESET && l && l->pendingDelete) {
2386 	if (GLOB(reload_in_progress) && res == SCCP_CONFIG_NEEDDEVICERESET) {
2387 		sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_1 "%s: major line changes detected, device reset required -> pendingUpdate=1\n", l->name);
2388 		l->pendingUpdate = 1;
2389 	} else {
2390 		l->pendingUpdate = 0;
2391 	}
2392 	sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "%s: Removing pendingDelete\n", l->name);
2393 	l->pendingDelete = 0;
2394 }
2395 
2396 /*!
2397  * \brief Build Device
2398  * \param d SCCP Device
2399  * \param variable Asterisk Variable
2400  * \param deviceName Name of device as char
2401  * \param isRealtime is Realtime as Boolean
2402  *
2403  * \callgraph
2404  * \callergraph
2405  *
2406  */
sccp_config_buildDevice(sccp_device_t * d,PBX_VARIABLE_TYPE * variable,boolean_t isRealtime)2407 static void sccp_config_buildDevice(sccp_device_t * d, PBX_VARIABLE_TYPE * variable, boolean_t isRealtime)
2408 {
2409 	PBX_VARIABLE_TYPE *v = variable;
2410 	if(!d) {
2411 		pbx_log(LOG_ERROR, "SCCP: (sccp_config_buildDevice) called without valid device ptr\n");
2412 		return;
2413 	}
2414 
2415 	/* apply configuration */
2416 	sccp_configurationchange_t res = sccp_config_applyDeviceConfiguration(d, v);
2417 
2418 #ifdef CS_SCCP_REALTIME
2419 	d->realtime = isRealtime;
2420 #endif
2421 	if (GLOB(reload_in_progress) && res == SCCP_CONFIG_NEEDDEVICERESET && d) {
2422 		sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_1 "%s: major changes for device detected, device reset required -> pendingUpdate=1\n", d->id);
2423 		d->pendingUpdate = 1;
2424 	} else {
2425 		d->pendingUpdate = 0;
2426 	}
2427 	d->pendingDelete = 0;
2428 }
2429 
2430 /*!
2431  * \brief Get Configured Line from Asterisk Variable
2432  * \param v Asterisk Variable
2433  * \return Configured SCCP Line
2434  * \note also used by realtime functionality to line device from Asterisk Variable
2435  *
2436  * \callgraph
2437  * \callergraph
2438  *
2439  */
sccp_config_applyGlobalConfiguration(PBX_VARIABLE_TYPE * v)2440 sccp_configurationchange_t sccp_config_applyGlobalConfiguration(PBX_VARIABLE_TYPE * v)
2441 {
2442 	unsigned int  res = SCCP_CONFIG_NOUPDATENEEDED;
2443 	boolean_t SetEntries[ARRAY_LEN(sccpGlobalConfigOptions)] = { FALSE };
2444 	PBX_VARIABLE_TYPE *cat_root = v;
2445 
2446 	for (; v; v = v->next) {
2447 		res |= sccp_config_object_setValue(sccp_globals, cat_root, v->name, v->value, v->lineno, SCCP_CONFIG_GLOBAL_SEGMENT, SetEntries, FALSE);
2448 	}
2449 	if (res) {
2450 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Update Needed (%d)\n", res);
2451 	}
2452 	sccp_config_set_defaults(sccp_globals, SCCP_CONFIG_GLOBAL_SEGMENT, SetEntries);
2453 
2454 	if (GLOB(keepalive) < SCCP_MIN_KEEPALIVE) {
2455 		GLOB(keepalive) = SCCP_MIN_KEEPALIVE;
2456 	}
2457 
2458 	return (sccp_configurationchange_t)res;
2459 }
2460 
2461 /*!
2462  * \brief Add 'default' softkeyset
2463  */
sccp_config_add_default_softkeyset(void)2464 static void sccp_config_add_default_softkeyset(void)
2465 {
2466 	// create tempory "default" variable set to create "default" softkeyset, if not defined in sccp.conf
2467 	PBX_VARIABLE_TYPE * softkeyset_root = NULL;
2468 	PBX_VARIABLE_TYPE * tmp = NULL;
2469 	uint cur_elem = 0;
2470 	const SCCPConfigOption *sccpConfigOption = sccpSoftKeyConfigOptions;
2471 	for (cur_elem = 0; cur_elem < ARRAY_LEN(sccpSoftKeyConfigOptions); cur_elem++) {
2472 		if (sccpConfigOption[cur_elem].defaultValue != NULL) {
2473 			if (!softkeyset_root) {
2474 				softkeyset_root = pbx_variable_new(sccpConfigOption[cur_elem].name, sccpConfigOption[cur_elem].defaultValue, "");
2475 				tmp = softkeyset_root;
2476 			} else {
2477 				tmp->next = pbx_variable_new(sccpConfigOption[cur_elem].name, sccpConfigOption[cur_elem].defaultValue, "");
2478 				tmp = tmp->next;
2479 			}
2480 		}
2481 	}
2482 	//pbx_log(LOG_NOTICE, "Adding 'default' softkeyset\n");
2483 	sccp_config_softKeySet(softkeyset_root, "default");
2484 	pbx_variables_destroy(softkeyset_root);
2485 }
2486 
2487 /*!
2488  * \brief Parse sccp.conf and Create General Configuration
2489  * \param readingtype SCCP Reading Type
2490  *
2491  * \callgraph
2492  * \callergraph
2493  */
sccp_config_general(sccp_readingtype_t readingtype)2494 boolean_t sccp_config_general(sccp_readingtype_t readingtype)
2495 {
2496 	PBX_VARIABLE_TYPE * v = NULL;
2497 
2498 	/* Cleanup for reload */
2499 	if (!GLOB(cfg)) {
2500 		pbx_log(LOG_WARNING, "Unable to load config file sccp.conf, SCCP disabled\n");
2501 		return FALSE;
2502 	}
2503 
2504 	/* read the general section */
2505 	v = ast_variable_browse(GLOB(cfg), "general");
2506 	if (!v) {
2507 		pbx_log(LOG_WARNING, "Missing [general] section, SCCP disabled\n");
2508 		return FALSE;
2509 	}
2510 	// sccp_config_set_defaults(sccp_globals, SCCP_CONFIG_GLOBAL_SEGMENT);
2511 
2512 	/* setup bindaddress */
2513 	if (!sccp_netsock_getPort(&GLOB(bindaddr))) {
2514 		struct sockaddr_in *in = (struct sockaddr_in *) &GLOB(bindaddr);
2515 
2516 		in->sin_port = ntohs(DEFAULT_SCCP_PORT);
2517 		GLOB(bindaddr).ss_family = AF_INET;
2518 	}
2519 #ifdef HAVE_OPENSSL
2520 	if(!sccp_netsock_getPort(&GLOB(secbindaddr))) {
2521 		struct sockaddr_in * in = (struct sockaddr_in *)&GLOB(secbindaddr);
2522 
2523 		in->sin_port = ntohs(DEFAULT_SCCP_SECURE_PORT);
2524 		GLOB(secbindaddr).ss_family = AF_INET;
2525 	}
2526 #endif
2527 
2528 	sccp_configurationchange_t res = sccp_config_applyGlobalConfiguration(v);
2529 
2530 	/* setup bind-port */
2531 	if (!sccp_netsock_getPort(&GLOB(bindaddr))) {
2532 		sccp_netsock_setPort(&GLOB(bindaddr), DEFAULT_SCCP_PORT);
2533 	}
2534 
2535 	if (GLOB(reload_in_progress) && res == SCCP_CONFIG_NEEDDEVICERESET) {
2536 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_1 "SCCP: major changes detected in globals, reset required -> pendingUpdate=1\n");
2537 		GLOB(pendingUpdate) = 1;
2538 	} else {
2539 		GLOB(pendingUpdate) = 0;
2540 	}
2541 
2542 	if (GLOB(regcontext)) {
2543                 /* setup regcontext */
2544 		char newcontexts[SCCP_MAX_CONTEXT]="";
2545 		char oldcontexts[SCCP_MAX_CONTEXT]="";
2546 		char * stringp = NULL;
2547 
2548 		char * context = NULL;
2549 
2550 		char * oldregcontext = NULL;
2551 
2552 		sccp_copy_string(newcontexts, GLOB(regcontext), sizeof(newcontexts));
2553 		//memcpy(newcontexts, GLOB(regcontext), sizeof(newcontexts));
2554 		stringp = newcontexts;
2555 
2556 		sccp_copy_string(oldcontexts, GLOB(used_context), sizeof(oldcontexts));				// Initialize copy of current regcontext for later use in removing stale contexts
2557 		//memcpy(oldcontexts, GLOB(used_context), sizeof(oldcontexts));					// Initialize copy of current regcontext for later use in removing stale contexts
2558 		oldregcontext = oldcontexts;
2559 
2560 		cleanup_stale_contexts(stringp, oldregcontext);							// Let's remove any contexts that are no longer defined in regcontext
2561 
2562 		while ((context = strsep(&stringp, "&"))) {							// Create contexts if they don't exist already
2563 			sccp_copy_string(GLOB(used_context), context, sizeof(GLOB(used_context)));
2564 			pbx_context_find_or_create(NULL, NULL, context, "SCCP");
2565 		}
2566 	}
2567 	if (GLOB(externhost)) {
2568 		sccp_netsock_flush_externhost();
2569 	}
2570 
2571 	return TRUE;
2572 }
2573 
2574 
2575 /*!
2576  * \brief Cleanup Stale Contexts (regcontext)
2577  * \param new New Context as Character
2578  * \param old Old Context as Character
2579  */
cleanup_stale_contexts(char * new_contexts,char * old_contexts)2580 void cleanup_stale_contexts(char *new_contexts, char *old_contexts)
2581 {
2582 	char * oldcontext = NULL;
2583 
2584 	char * newcontext = NULL;
2585 
2586 	char * stalecontext = NULL;
2587 
2588 	char * stringp = NULL;
2589 
2590 	char newlist[SCCP_MAX_CONTEXT];
2591 
2592 	while ((oldcontext = strsep(&old_contexts, "&"))) {
2593 		stalecontext = NULL;
2594 		sccp_copy_string(newlist, new_contexts, sizeof(newlist));
2595 		stringp = newlist;
2596 		while ((newcontext = strsep(&stringp, "&"))) {
2597 			if (sccp_strequals(newcontext, oldcontext)) {
2598 				/* This is not the context you're looking for */
2599 				stalecontext = NULL;
2600 				break;
2601 			} else {
2602 				stalecontext = oldcontext;
2603 			}
2604 
2605 		}
2606 		if (stalecontext) {
2607 			ast_context_destroy(ast_context_find(stalecontext), "SCCP");
2608 		}
2609 	}
2610 }
2611 
2612 /*!
2613  * \brief Read Lines from the Config File
2614  *
2615  * \param readingtype as SCCP Reading Type
2616  * \since 10.01.2008 - branche V3
2617  * \author Marcello Ceschia
2618  *
2619  * \callgraph
2620  * \callergraph
2621  *
2622  */
sccp_config_readDevicesLines(sccp_readingtype_t readingtype)2623 boolean_t sccp_config_readDevicesLines(sccp_readingtype_t readingtype)
2624 {
2625 	// struct ast_config *cfg = NULL;
2626 
2627 	char *cat = NULL;
2628 	PBX_VARIABLE_TYPE *v = NULL;
2629 	uint8_t device_count = 0;
2630 	uint8_t line_count = 0;
2631 	sccp_device_t *d = NULL;
2632 
2633 	sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_1 "Loading Devices and Lines from config\n");
2634 
2635 	sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_1 "Checking Reading Type:%s (%d)\n", readingtype == 0 ? "Module load" : "Reload", readingtype);
2636 	if (readingtype == SCCP_CONFIG_READRELOAD) {
2637 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Device Pre Reload\n");
2638 		sccp_device_pre_reload();
2639 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Line Pre Reload\n");
2640 		sccp_line_pre_reload();
2641 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Softkey Pre Reload\n");
2642 		sccp_softkey_pre_reload();
2643 	}
2644 
2645 	if (!GLOB(cfg)) {
2646 		pbx_log(LOG_NOTICE, "SCCP: (sccp_config_readDevicesLines) Unable to load config file sccp.conf, SCCP disabled\n");
2647 		return FALSE;
2648 	}
2649 
2650 	while ((cat = pbx_category_browse(GLOB(cfg), cat))) {
2651 		const char * utype = NULL;
2652 
2653 		if (!strcasecmp(cat, "general")) {
2654 			continue;
2655 		}
2656 		utype = pbx_variable_retrieve(GLOB(cfg), cat, "type");
2657 		sccp_log_and((DEBUGCAT_CONFIG + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_2 "SCCP: (sccp_config_readDevicesLines) Reading Section Of Type %s\n", utype);
2658 
2659 		if (!utype) {
2660 			pbx_log(LOG_WARNING, "Section '%s' is missing a type parameter\n", cat);
2661 			continue;
2662 		} else if (!strcasecmp(utype, "device")) {
2663 			// check minimum requirements for a device
2664 			sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Parsing device [%s]\n", cat);
2665 			v = ast_variable_browse(GLOB(cfg), cat);
2666 
2667 			// Try to find out if we have the device already on file.
2668 			// However, do not look into realtime, since
2669 			// we might have been asked to create a device for realtime addition,
2670 			// thus causing an infinite loop / recursion.
2671 			AUTO_RELEASE(sccp_device_t, device, sccp_device_find_byid(cat, FALSE));
2672 			sccp_nat_t nat = SCCP_NAT_AUTO;
2673 
2674 			/* create new device with default values */
2675 			if (!device) {
2676 				device = sccp_device_create(cat) /*ref_replace*/;
2677 				if(!device) {
2678 					return FALSE;
2679 				}
2680 				// sccp_copy_string(d->id, cat, sizeof(d->id));         /* set device name */
2681 				sccp_device_addToGlobals(device);
2682 				device_count++;
2683 			} else {
2684 				if (device->pendingDelete) {
2685 					nat = device->nat;
2686 					device->pendingDelete = 0;
2687 				}
2688 			}
2689 			sccp_config_buildDevice(device, v, FALSE);
2690 			sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_3 "found device %d: %s\n", device_count, cat);
2691 			/* load saved settings from ast db */
2692 			// sccp_config_restoreDeviceFeatureStatus(device);
2693 
2694 			/* restore current nat status, if device does not get restarted */
2695 			if (0 == device->pendingDelete && sccp_device_getRegistrationState(device) != SKINNY_DEVICE_RS_NONE) {
2696 				if (SCCP_NAT_AUTO == device->nat && (SCCP_NAT_AUTO == nat || SCCP_NAT_AUTO_OFF == nat || SCCP_NAT_AUTO_ON == nat)) {
2697 					device->nat = nat;
2698 				}
2699 			}
2700 		} else if (!strcasecmp(utype, "line")) {
2701 			/* check minimum requirements for a line */
2702 			sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Parsing line [%s]\n", cat);
2703 
2704 			if ((!(!sccp_strlen_zero(pbx_variable_retrieve(GLOB(cfg), cat, "label"))) && (!sccp_strlen_zero(pbx_variable_retrieve(GLOB(cfg), cat, "cid_name"))) && (!sccp_strlen_zero(pbx_variable_retrieve(GLOB(cfg), cat, "cid_num"))))) {
2705 				pbx_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sccp.conf");
2706 				continue;
2707 			}
2708 			line_count++;
2709 
2710 			v = ast_variable_browse(GLOB(cfg), cat);
2711 			AUTO_RELEASE(sccp_line_t, l , sccp_line_find_byname(cat, FALSE));
2712 
2713 			/* check if we have this line already */
2714 			if (l) {
2715 				sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_3 "found line %d: %s, do update\n", line_count, cat);
2716 				sccp_config_buildLine(l, v, FALSE);
2717 			} else if((l = sccp_line_create(cat)) /*ref_replace*/) {
2718 				sccp_config_buildLine(l, v, FALSE);
2719 				sccp_line_addToGlobals(l);						/* may find another line instance create by another thread, in that case the newly created line is going to be dropped when l is released */
2720 			} else {
2721 				return FALSE;
2722 			}
2723 		} else if (!strcasecmp(utype, "softkeyset")) {
2724 			sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "parsing softkey [%s]\n", cat);
2725 			if (sccp_strcaseequals(cat, "default")) {
2726 				pbx_log(LOG_WARNING, "SCCP: (sccp_config_readDevicesLines) The 'default' softkeyset cannot be overriden, please use another name\n");
2727 			} else {
2728 				v = ast_variable_browse(GLOB(cfg), cat);
2729 				sccp_config_softKeySet(v, cat);
2730 			}
2731 		} else {
2732 			pbx_log(LOG_WARNING, "SCCP: (sccp_config_readDevicesLines) UNKNOWN SECTION / UTYPE, type: %s\n", utype);
2733 		}
2734 	}
2735 	sccp_config_add_default_softkeyset();
2736 
2737 #ifdef CS_SCCP_REALTIME
2738 	/* reload realtime lines */
2739 	sccp_configurationchange_t res = SCCP_CONFIG_NOUPDATENEEDED;
2740 	PBX_VARIABLE_TYPE *rv = NULL;
2741 
2742 	sccp_line_t *l = NULL;
2743 	SCCP_RWLIST_RDLOCK(&GLOB(lines));
2744 	SCCP_RWLIST_TRAVERSE(&GLOB(lines), l, list) {
2745 		AUTO_RELEASE(sccp_line_t, line , sccp_line_retain(l));
2746 		if (line) {
2747 			do {
2748 				if (line->realtime == TRUE && line != GLOB(hotline)->line) {
2749 					sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_3 "%s: reload realtime line\n", line->name);
2750 					rv = pbx_load_realtime(GLOB(realtimelinetable), "name", line->name, NULL);
2751 					/* we did not find this line, mark it for deletion */
2752 					if (!rv) {
2753 						sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_3 "%s: realtime line not found - set pendingDelete=1\n", line->name);
2754 						line->pendingDelete = 1;
2755 						break;
2756 					}
2757 					line->pendingDelete = 0;
2758 
2759 					res = sccp_config_applyLineConfiguration(line, rv);
2760 					/* check if we did some changes that needs a device update */
2761 					if (GLOB(reload_in_progress) && res & SCCP_CONFIG_NEEDDEVICERESET) {
2762 						line->pendingUpdate = 1;
2763 					} else {
2764 						line->pendingUpdate = 0;
2765 					}
2766 					pbx_variables_destroy(rv);
2767 				}
2768 			} while (0);
2769 		}
2770 	}
2771 	SCCP_RWLIST_UNLOCK(&GLOB(lines));
2772 	/* finished realtime line reload */
2773 
2774 	SCCP_RWLIST_RDLOCK(&GLOB(devices));
2775 	SCCP_RWLIST_TRAVERSE(&GLOB(devices), d, list) {
2776 		AUTO_RELEASE(sccp_device_t, device , sccp_device_retain(d));
2777 		if (device) {
2778 			do {
2779 				if (device->realtime == TRUE) {
2780 					sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_3 "%s: reload realtime line\n", device->id);
2781 					rv = pbx_load_realtime(GLOB(realtimedevicetable), "name", device->id, NULL);
2782 					/* we did not find this line, mark it for deletion */
2783 					if (!rv) {
2784 						sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_3 "%s: realtime device not found - set pendingDelete=1\n", device->id);
2785 						device->pendingDelete = 1;
2786 						break;
2787 					}
2788 					device->pendingDelete = 0;
2789 
2790 					res = sccp_config_applyDeviceConfiguration(device, rv);
2791 					/* check if we did some changes that needs a device update */
2792 					if (GLOB(reload_in_progress) && res & SCCP_CONFIG_NEEDDEVICERESET) {
2793 						device->pendingUpdate = 1;
2794 					} else {
2795 						device->pendingUpdate = 0;
2796 					}
2797 					pbx_variables_destroy(rv);
2798 				}
2799 			} while (0);
2800 		}
2801 	}
2802 	SCCP_RWLIST_UNLOCK(&GLOB(devices));
2803 #endif
2804 
2805 	if (GLOB(reload_in_progress) && GLOB(pendingUpdate)) {
2806 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Global param changed needing restart ->  Restart all device\n");
2807 
2808 		SCCP_RWLIST_RDLOCK(&GLOB(devices));
2809 		SCCP_RWLIST_TRAVERSE(&GLOB(devices), d, list) {
2810 			if (d->realtime) {
2811 				d->pendingDelete = 1;
2812 			} else if (!d->pendingDelete && !d->pendingUpdate) {
2813 				d->pendingUpdate = 1;
2814 			}
2815 		}
2816 		SCCP_RWLIST_UNLOCK(&GLOB(devices));
2817 	} else {
2818 		GLOB(pendingUpdate) = 0;
2819 	}
2820 	GLOB(pendingUpdate) = 0;
2821 
2822 	sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_1 "Checking Reading Type\n");
2823 	if (readingtype == SCCP_CONFIG_READRELOAD) {
2824 		/* IMPORTANT: The line_post_reload function may change the pendingUpdate field of
2825 		 * devices, so it's really important to call it *before* calling device_post_real().
2826 		 */
2827 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Line Post Reload\n");
2828 		sccp_line_post_reload();
2829 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Device Post Reload\n");
2830 		sccp_device_post_reload();
2831 		sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "Softkey Post Reload\n");
2832 		sccp_softkey_post_reload();
2833 	}
2834 	return TRUE;
2835 }
2836 
2837 /*!
2838  * \brief Get Configured Line from Asterisk Variable
2839  * \param l SCCP Line
2840  * \param v Asterisk Variable
2841  * \return Configured SCCP Line
2842  * \note also used by realtime functionality to line device from Asterisk Variable
2843  *
2844  * \callgraph
2845  * \callergraph
2846  *
2847  */
sccp_config_applyLineConfiguration(linePtr l,PBX_VARIABLE_TYPE * v)2848 sccp_configurationchange_t sccp_config_applyLineConfiguration(linePtr l, PBX_VARIABLE_TYPE * v)
2849 {
2850 	unsigned int res = SCCP_CONFIG_NOUPDATENEEDED;
2851 	boolean_t SetEntries[ARRAY_LEN(sccpLineConfigOptions)] = { FALSE };
2852 	PBX_VARIABLE_TYPE *cat_root = v;
2853 	if(!l) {
2854 		pbx_log(LOG_ERROR, "SCCP: (sccp_config_applyLineConfiguration) called without valid line ptr\n");
2855 		return SCCP_CONFIG_ERROR;
2856 	}
2857 
2858 	for (; v; v = v->next) {
2859 		res |= sccp_config_object_setValue(l, cat_root, v->name, v->value, v->lineno, SCCP_CONFIG_LINE_SEGMENT, SetEntries, FALSE);
2860 	}
2861 
2862 	l->preferences_set_on_line_level = (l->preferences.audio[0] != SKINNY_CODEC_NONE) ? TRUE: FALSE;
2863 
2864 	sccp_config_set_defaults(l, SCCP_CONFIG_LINE_SEGMENT, SetEntries);
2865 
2866 	if (sccp_strlen_zero(l->id)) {
2867 		snprintf(l->id, sizeof(l->id), "%04d", SCCP_LIST_GETSIZE(&GLOB(lines)));
2868 	}
2869 
2870 	return (sccp_configurationchange_t)res;
2871 }
2872 
2873 /*!
2874  * \brief Apply Device Configuration from Asterisk Variable
2875  * \param d SCCP Device
2876  * \param v Asterisk Variable
2877  * \return Configured SCCP Device
2878  * \note also used by realtime functionality to line device from Asterisk Variable
2879  * \todo this function should be called sccp_config_applyDeviceConfiguration
2880  *
2881  * \callgraph
2882  * \callergraph
2883  *
2884  */
sccp_config_applyDeviceConfiguration(devicePtr d,PBX_VARIABLE_TYPE * v)2885 sccp_configurationchange_t sccp_config_applyDeviceConfiguration(devicePtr d, PBX_VARIABLE_TYPE * v)
2886 {
2887 	unsigned int res = SCCP_CONFIG_NOUPDATENEEDED;
2888 	boolean_t SetEntries[ARRAY_LEN(sccpDeviceConfigOptions)] = { FALSE };
2889 	PBX_VARIABLE_TYPE *cat_root = v;
2890 	if(!d) {
2891 		pbx_log(LOG_ERROR, "SCCP: (sccp_config_applyDeviceConfiguration) called without valid device ptr\n");
2892 		return SCCP_CONFIG_ERROR;
2893 	}
2894 
2895 	if (d->pendingDelete) {
2896 		sccp_dev_clean_restart(d, FALSE);
2897 	}
2898 	for (; v; v = v->next) {
2899 		res |= sccp_config_object_setValue(d, cat_root, v->name, v->value, v->lineno, SCCP_CONFIG_DEVICE_SEGMENT, SetEntries, FALSE);
2900 	}
2901 
2902 	sccp_config_set_defaults(d, SCCP_CONFIG_DEVICE_SEGMENT, SetEntries);
2903 
2904 	if (d->keepalive < SCCP_MIN_KEEPALIVE) {
2905 		d->keepalive = SCCP_MIN_KEEPALIVE;
2906 	}
2907 	return (sccp_configurationchange_t)res;
2908 }
2909 
2910 /*!
2911  * \brief Find the Correct Config File
2912  * \return Asterisk Config Object as ast_config
2913  */
sccp_config_getConfig(boolean_t force,const char * const filename)2914 sccp_config_file_status_t sccp_config_getConfig(boolean_t force, const char * const filename)
2915 {
2916 	// struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS & CONFIG_FLAG_FILEUNCHANGED };
2917 	sccp_config_file_status_t res = 0;
2918 	char * newfilename = "sccp.conf";
2919 	struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
2920 	if (force) {
2921 		if (GLOB(cfg)) {
2922 			pbx_config_destroy(GLOB(cfg));
2923 			GLOB(cfg) = NULL;
2924 		}
2925 		pbx_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
2926 	}
2927 
2928 	if(filename && !sccp_strlen_zero(filename)) {
2929 		newfilename = (char *)filename;
2930 	} else if(GLOB(config_file_name) && !sccp_strlen_zero(GLOB(config_file_name))) {
2931 		newfilename = pbx_strdupa(GLOB(config_file_name));
2932 	}
2933 
2934 	GLOB(cfg) = pbx_config_load(newfilename, "chan_sccp", config_flags);
2935 	if (GLOB(cfg) == CONFIG_STATUS_FILEMISSING) {
2936 		pbx_log(LOG_ERROR, "Config file '%s' not found, aborting (re)load.\n", newfilename);
2937 		GLOB(cfg) = NULL;
2938 		res = CONFIG_STATUS_FILE_NOT_FOUND;
2939 		goto FUNC_EXIT;
2940 	} else if (GLOB(cfg) == CONFIG_STATUS_FILEINVALID) {
2941 		pbx_log(LOG_ERROR, "Config file '%s' specified is not a valid config file, aborting (re)load.\n", newfilename);
2942 		GLOB(cfg) = NULL;
2943 		res = CONFIG_STATUS_FILE_INVALID;
2944 		goto FUNC_EXIT;
2945 	} else if (GLOB(cfg) == CONFIG_STATUS_FILEUNCHANGED) {
2946 		// ugly solution, but we always need to have a valid config file loaded.
2947 		pbx_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
2948 		GLOB(cfg) = pbx_config_load(newfilename, "chan_sccp", config_flags);
2949 		if (!force) {
2950 			sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_3 "Config file '%s' has not changed, aborting (re)load.\n", newfilename);
2951 			res = CONFIG_STATUS_FILE_NOT_CHANGED;
2952 			goto FUNC_EXIT;
2953 		} else {
2954 			sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_3 "Config file '%s' has not changed, force requested, forcing reload.\n", newfilename);
2955 		}
2956 	}
2957 	if (GLOB(cfg)) {
2958 		if (ast_variable_browse(GLOB(cfg), "devices")) {						/* Warn user when old entries exist in sccp.conf */
2959 			pbx_log(LOG_ERROR,
2960 				"\n\n --> You are using an old configuration format, please update '%s'!!\n --> Loading of module chan_sccp with current sccp.conf has terminated\n --> Check "
2961 				"https://github.com/chan-sccp/chan-sccp/wiki/How-to-setup-the-chan_sccp-Module for more information.\n\n",
2962 				newfilename);
2963 			pbx_config_destroy(GLOB(cfg));
2964 			GLOB(cfg) = NULL;
2965 			res = CONFIG_STATUS_FILE_OLD;
2966 			goto FUNC_EXIT;
2967 		} else if (!ast_variable_browse(GLOB(cfg), "general")) {
2968 			pbx_log(LOG_ERROR, "Missing [general] section, SCCP disabled\n");
2969 			pbx_config_destroy(GLOB(cfg));
2970 			GLOB(cfg) = NULL;
2971 			res = CONFIG_STATUS_FILE_NOT_SCCP;
2972 			goto FUNC_EXIT;
2973 		}
2974 	} else {
2975 		pbx_log(LOG_ERROR, "Missing Glob(cfg)\n");
2976 		GLOB(cfg) = NULL;
2977 		res = CONFIG_STATUS_FILE_NOT_FOUND;
2978 		goto FUNC_EXIT;
2979 	}
2980 	sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_3 "Config file '%s' loaded.\n", newfilename);
2981 	res = CONFIG_STATUS_FILE_OK;
2982 FUNC_EXIT:
2983 	if (GLOB(config_file_name)) {sccp_free(GLOB(config_file_name));}
2984 	GLOB(config_file_name) = pbx_strdup(newfilename);
2985 	return res;
2986 }
2987 
2988 /*!
2989  * \brief Soft Key Str to Label Mapping
2990  */
2991 static const struct softkeyConfigurationTemplate {
2992 	const char configVar[16];										/*!< Config Variable as Character */
2993 	const int softkey;											/*!< Softkey as Int */
2994 } softKeyTemplate[] = {
2995 	/* clang-format off */
2996 	{"redial", 			SKINNY_LBL_REDIAL},
2997 	{"newcall", 			SKINNY_LBL_NEWCALL},
2998 	{"cfwdall", 			SKINNY_LBL_CFWDALL},
2999 	{"cfwdbusy", 			SKINNY_LBL_CFWDBUSY},
3000 	{"cfwdnoanswer",		SKINNY_LBL_CFWDNOANSWER},
3001 	{"dnd", 			SKINNY_LBL_DND},
3002 	{"hold", 			SKINNY_LBL_HOLD},
3003 	{"endcall", 			SKINNY_LBL_ENDCALL},
3004 	{"idivert", 			SKINNY_LBL_IDIVERT},
3005 	{"resume", 			SKINNY_LBL_RESUME},
3006 	{"newcall", 			SKINNY_LBL_NEWCALL},
3007 	{"transfer", 			SKINNY_LBL_TRANSFER},
3008 	{"answer", 			SKINNY_LBL_ANSWER},
3009 	{"transvm", 			SKINNY_LBL_TRNSFVM},
3010 	{"private", 			SKINNY_LBL_PRIVATE},
3011 	{"meetme", 			SKINNY_LBL_MEETME},
3012 	{"barge", 			SKINNY_LBL_BARGE},
3013 	{"back", 			SKINNY_LBL_BACKSPACE},
3014 	{"intrcpt", 			SKINNY_LBL_INTRCPT},
3015 	{"monitor", 			SKINNY_LBL_MONITOR},
3016 	{"dial", 			SKINNY_LBL_DIAL},
3017 #ifndef CS_ADV_FEATURES
3018 	{"callback",			SKINNY_LBL_CALLBACK},
3019 	{"trnsfvm",			SKINNY_LBL_TRNSFVM},
3020 	{"cbarge", 			SKINNY_LBL_CBARGE},
3021 #endif
3022 #ifdef CS_SCCP_VIDEO
3023 	{"vidmode", 			SKINNY_LBL_VIDEO_MODE},
3024 #else
3025 	{"vidmode", 			-1},
3026 #endif
3027 #ifdef CS_SCCP_PICKUP
3028 	{"pickup", 			SKINNY_LBL_PICKUP},
3029 	{"gpickup", 			SKINNY_LBL_GPICKUP},
3030 #else
3031 	{"pickup", 			-1},
3032 	{"gpickup", 			-1},
3033 #endif
3034 #ifdef CS_SCCP_PARK
3035 	{"park", 			SKINNY_LBL_PARK},
3036 #else
3037 	{"park", 			-1},
3038 #endif
3039 #ifdef CS_SCCP_DIRTRFR
3040 	{"select", 			SKINNY_LBL_SELECT},
3041 	{"dirtrfr", 			SKINNY_LBL_DIRTRFR},
3042 #else
3043 	{"select", 			-1},
3044 	{"dirtrfr", 			-1},
3045 #endif
3046 #ifdef CS_SCCP_CONFERENCE
3047 	{"conf", 			SKINNY_LBL_CONFRN},
3048 	{"confrn",			SKINNY_LBL_CONFRN},
3049 	{"join", 			SKINNY_LBL_JOIN},
3050 	{"conflist", 			SKINNY_LBL_CONFLIST},
3051 #else
3052 	{"conf", 			-1},
3053 	{"confrn",			-1},
3054 	{"join",			-1},
3055 	{"conflist", 			-1},
3056 #endif
3057 	{"empty", 			SKINNY_LBL_EMPTY},
3058 //	{"info", 			SKINNY_LBL_INFO},
3059 	/* clang-format on */
3060 
3061 };
3062 
3063 /*!
3064  * \brief Get softkey label as int
3065  * \param key configuration value
3066  * \return SoftKey label as int of SKINNY_LBL_*. returns an empty button if nothing matched
3067  */
sccp_config_getSoftkeyLbl(char * key)3068 static int sccp_config_getSoftkeyLbl(char *key)
3069 {
3070 	size_t i = 0;
3071 	for (i = 0; i < ARRAY_LEN(softKeyTemplate); i++) {
3072 		if (sccp_strcaseequals(softKeyTemplate[i].configVar, key)) {
3073 			return softKeyTemplate[i].softkey;
3074 		}
3075 	}
3076 	sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_3 "softkeybutton: %s not defined\n", key);
3077 	return SKINNY_LBL_EMPTY;
3078 }
3079 
3080 /*!
3081  * \brief Read a single SoftKeyMode (configuration values)
3082  * \param softkeyset SoftKeySet
3083  * \param data configuration values
3084  * \return number of parsed softkeys
3085  */
sccp_config_readSoftKeySet(uint8_t * softkeyset,const char * data)3086 static uint8_t sccp_config_readSoftKeySet(uint8_t * softkeyset, const char * data)
3087 {
3088 	if (!data) {
3089 		return 0;
3090 	}
3091 	int i = 0;
3092 
3093 	int j = 0;
3094 	int softkey = 0;
3095 
3096 	char * labels = pbx_strdupa(data);
3097 	char * labelrest = NULL;
3098 	char delims[] = ",";
3099 
3100 	char * label = strtok_r(labels, delims, &labelrest);
3101 	while(label) {
3102 		label = pbx_strip(label);
3103 		if ((softkey = sccp_config_getSoftkeyLbl(label)) != -1 && (i + 1) < StationMaxSoftKeySetDefinition) {
3104 			softkeyset[i++] = softkey;
3105 		}
3106 		label = strtok_r(NULL, delims, &labelrest);
3107 	}
3108 	/*! \todo we used calloc to create this structure, so setting EMPTY should not be required here */
3109 	for (j = i; j < StationMaxSoftKeySetDefinition; j++) {
3110 		softkeyset[j] = SKINNY_LBL_EMPTY;
3111 	}
3112 	return i;
3113 }
3114 
3115 /*!
3116  * \brief Read a SoftKey configuration context
3117  * \param variable list of configuration variables
3118  * \param name name of this configuration (context)
3119  *
3120  * \callgraph
3121  * \callergraph
3122  *
3123  */
sccp_config_softKeySet(PBX_VARIABLE_TYPE * variable,const char * name)3124 void sccp_config_softKeySet(PBX_VARIABLE_TYPE * variable, const char *name)
3125 {
3126 	int keySetSize = 0;
3127 	sccp_softKeySetConfiguration_t *softKeySetConfiguration = NULL;
3128 	skinny_keymode_t keyMode = SKINNY_KEYMODE_SENTINEL;
3129 
3130 	sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_SOFTKEY)) (VERBOSE_PREFIX_3 "start reading softkeyset: %s\n", name);
3131 
3132 	SCCP_LIST_LOCK(&softKeySetConfig);
3133 	SCCP_LIST_TRAVERSE(&softKeySetConfig, softKeySetConfiguration, list) {
3134 		if (sccp_strcaseequals(softKeySetConfiguration->name, name)) {
3135 			//sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_SOFTKEY)) (VERBOSE_PREFIX_3 "Softkeyset: %s already defined\n", name);
3136 			break;
3137 		}
3138 	}
3139 	SCCP_LIST_UNLOCK(&softKeySetConfig);
3140 
3141 	if (!softKeySetConfiguration) {
3142 		//sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_SOFTKEY)) (VERBOSE_PREFIX_3 "Adding Softkeyset: %s\n", name);
3143 		softKeySetConfiguration = (sccp_softKeySetConfiguration_t *)sccp_calloc(1, sizeof(sccp_softKeySetConfiguration_t));
3144 		memset(softKeySetConfiguration, 0, sizeof(sccp_softKeySetConfiguration_t));
3145 
3146 		sccp_copy_string(softKeySetConfiguration->name, name, sizeof(sccp_softKeySetConfiguration_t));
3147 		softKeySetConfiguration->numberOfSoftKeySets = 0;
3148 		softKeySetConfiguration->softkeyCbMap = NULL;							// defaults to static softkeyMapCb
3149 
3150 		/* add new softkexSet to list */
3151 		SCCP_LIST_LOCK(&softKeySetConfig);
3152 		SCCP_LIST_INSERT_HEAD(&softKeySetConfig, softKeySetConfiguration, list);
3153 		SCCP_LIST_UNLOCK(&softKeySetConfig);
3154 	}
3155 
3156 	while (variable) {
3157 		keyMode = SKINNY_KEYMODE_SENTINEL;
3158 		sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_SOFTKEY)) (VERBOSE_PREFIX_3 "softkeyset: %s = %s\n", variable->name, variable->value);
3159 		if (sccp_strcaseequals(variable->name, "uriaction")) {
3160 			sccp_log(DEBUGCAT_CONFIG) (VERBOSE_PREFIX_3 "SCCP: UriAction softkey (%s) found\n", variable->value);
3161 			if (!softKeySetConfiguration->softkeyCbMap) {
3162 				softKeySetConfiguration->softkeyCbMap = sccp_softkeyMap_copyStaticallyMapped();
3163 			}
3164 
3165 			char * uriactionstr = pbx_strdup (variable->value);
3166 			char *event = strsep(&uriactionstr, ",");
3167 			if (event && !sccp_strlen_zero(uriactionstr)) {
3168 				sccp_softkeyMap_replaceCallBackByUriAction(softKeySetConfiguration->softkeyCbMap, labelstr2int(event), uriactionstr);
3169 			} else {
3170 				sccp_log(DEBUGCAT_CONFIG) (VERBOSE_PREFIX_3 "SCCP: UriAction softkey (%s) not found, or no uris (%s) specified\n", event, uriactionstr);
3171 			}
3172 			sccp_free (uriactionstr);
3173 		} else if (sccp_strcaseequals(variable->name, "onhook")) {
3174 			keyMode = KEYMODE_ONHOOK;
3175 		} else if (sccp_strcaseequals(variable->name, "connected")) {
3176 			keyMode = KEYMODE_CONNECTED;
3177 		} else if (sccp_strcaseequals(variable->name, "onhold")) {
3178 			keyMode = KEYMODE_ONHOLD;
3179 		} else if (sccp_strcaseequals(variable->name, "ringin")) {
3180 			keyMode = KEYMODE_RINGIN;
3181 		} else if (sccp_strcaseequals(variable->name, "offhook")) {
3182 			keyMode = KEYMODE_OFFHOOK;
3183 		} else if (sccp_strcaseequals(variable->name, "conntrans")) {
3184 			keyMode = KEYMODE_CONNTRANS;
3185 		} else if (sccp_strcaseequals(variable->name, "digitsfoll")) {
3186 			keyMode = KEYMODE_DIGITSFOLL;
3187 		} else if (sccp_strcaseequals(variable->name, "connconf")) {
3188 			keyMode = KEYMODE_CONNCONF;
3189 		} else if (sccp_strcaseequals(variable->name, "ringout")) {
3190 			keyMode = KEYMODE_RINGOUT;
3191 		} else if (sccp_strcaseequals(variable->name, "offhookfeat")) {
3192 			keyMode = KEYMODE_OFFHOOKFEAT;
3193 		} else if (sccp_strcaseequals(variable->name, "inusehint") || sccp_strcaseequals(variable->name, "onhint")) {
3194 			keyMode = KEYMODE_INUSEHINT;
3195 		} else if (sccp_strcaseequals(variable->name, "onhookstealable") || sccp_strcaseequals(variable->name, "onstealable")) {
3196 			keyMode = KEYMODE_ONHOOKSTEALABLE;
3197 		} else if (sccp_strcaseequals(variable->name, "holdconf")) {
3198 			keyMode = KEYMODE_HOLDCONF;
3199 		}
3200 
3201 		if (keyMode != SKINNY_KEYMODE_SENTINEL) {
3202 			if (softKeySetConfiguration->numberOfSoftKeySets < (keyMode + 1)) {
3203 				softKeySetConfiguration->numberOfSoftKeySets = keyMode + 1;
3204 			}
3205 
3206 			/* cleanup old value */
3207 			if (softKeySetConfiguration->modes[keyMode].ptr) {
3208 				//sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_SOFTKEY)) (VERBOSE_PREFIX_3 "KeyMode(%d) Ptr already defined in Softkeyset: %s. Freeing...\n", keyMode, name);
3209 				sccp_free(softKeySetConfiguration->modes[keyMode].ptr);
3210 			}
3211 
3212 			/* add new value */
3213 			uint8_t *softkeyset = (uint8_t *)sccp_calloc(StationMaxSoftKeySetDefinition, sizeof(uint8_t));
3214 			keySetSize = sccp_config_readSoftKeySet(softkeyset, variable->value);
3215 			//sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_SOFTKEY)) (VERBOSE_PREFIX_3 "Adding KeyMode(%d), with Size(%d), prt(%p) to Softkeyset: %s\n", keyMode, keySetSize, softkeyset, name);
3216 			if (keySetSize > 0) {
3217 				softKeySetConfiguration->modes[keyMode].id = keyMode;
3218 				softKeySetConfiguration->modes[keyMode].ptr = softkeyset;
3219 				softKeySetConfiguration->modes[keyMode].count = keySetSize;
3220 			} else {
3221 				softKeySetConfiguration->modes[keyMode].id = keyMode;
3222 				softKeySetConfiguration->modes[keyMode].ptr = NULL;
3223 				softKeySetConfiguration->modes[keyMode].count = 0;
3224 				sccp_free(softkeyset);
3225 			}
3226 		}
3227 
3228 		variable = variable->next;
3229 	}
3230 
3231 }
3232 
3233 
3234 /* generate json output from now on */
sccp_manager_config_metadata(struct mansession * s,const struct message * m)3235 int sccp_manager_config_metadata(struct mansession *s, const struct message *m)
3236 {
3237 	const SCCPConfigSegment *sccpConfigSegment = NULL;
3238 	int total = 0;
3239 	uint i = 0;
3240 	const char *id = astman_get_header(m, "ActionID");
3241 	const char *req_segment = astman_get_header(m, "Segment");
3242 	const char *req_resultformat = astman_get_header(m, "ResultFormat");
3243 	uint comma = 0;
3244 
3245 	if (sccp_strlen_zero(req_segment)) {										// return all segments
3246 		int sccp_config_revision = 0;
3247 
3248 		sscanf(SCCP_CONFIG_REVISION, "$" "Revision: %i" "$", &sccp_config_revision);
3249 
3250 		if (sccp_strcaseequals(req_resultformat, "list")) {
3251 			astman_send_listack(s, m, "SCCPConfigMetaData Follows", "Start");
3252 			astman_append(s, "Event: SCCPConfigMetaData\r\n");
3253 		} else if (sccp_strcaseequals(req_resultformat, "command")) {
3254 			astman_append(s, "Response: Follows\r\n");
3255 			astman_append(s, "Priviledge: Command\r\n");
3256 		} else {
3257 			astman_append(s, "Response: Success\r\n");
3258 		}
3259 		if (!ast_strlen_zero(id)) {
3260 			astman_append(s, "ActionID: %s\r\n", id);
3261 		}
3262 
3263 		astman_append(s, "JSON: {");
3264 		astman_append(s, "\"Name\":\"Chan-sccp-b\",");
3265 		astman_append(s, "\"Version\":\"%s\",", SCCP_VERSION);
3266 #if defined(VCS_BRANCH) && defined(VCS_NUM) && defined(VCS_TAG) && defined(VCS_TAG) && defined(VCS_TYPE)
3267 		astman_append(s, "\"Branch\":\"%s\",", VCS_BRANCH);
3268 		astman_append(s, "\"RevisionHash\":\"%s\",", VCS_SHORT_HASH);
3269 		astman_append(s, "\"RevisionNum\":\"%d\",", VCS_NUM);
3270 		astman_append(s, "\"Tag\":\"%s\",", VCS_TAG);
3271 		astman_append(s, "\"VersioningType\":\"%s\",", VCS_TYPE);
3272 #else
3273 		astman_append(s, "\"Branch\":\"%s\",", SCCP_BRANCH);
3274 		astman_append(s, "\"RevisionHash\":\"%s\",", SCCP_REVISION);
3275 		astman_append(s, "\"RevisionNum\":\"%d\",", 0);
3276 		astman_append(s, "\"Tag\":\"%s\",", "");
3277 		astman_append(s, "\"VersioningType\":\"%s\",", "archive");
3278 #endif
3279 		astman_append(s, "\"ConfigRevision\":\"%d\",", sccp_config_revision);
3280 		char *conf_enabled_array[] = {
3281 #ifdef CS_SCCP_PARK
3282 			"park",
3283 #endif
3284 #ifdef CS_SCCP_PICKUP
3285 			"pickup",
3286 #endif
3287 #ifdef CS_SCCP_REALTIME
3288 			"realtime",
3289 #endif
3290 #ifdef CS_SCCP_VIDEO
3291 			"video",
3292 #endif
3293 #ifdef CS_SCCP_CONFERENCE
3294 			"conferenence",
3295 #endif
3296 #ifdef CS_SCCP_DIRTRFR
3297 			"dirtrfr",
3298 #endif
3299 #ifdef CS_SCCP_FEATURE_MONITOR
3300 			"feature_monitor",
3301 #endif
3302 #ifdef CS_SCCP_FUNCTIONS
3303 			"functions",
3304 #endif
3305 #ifdef CS_MANAGER_EVENTS
3306 			"manager_events",
3307 #endif
3308 #ifdef CS_DEVICESTATE
3309 			"devicestate",
3310 #endif
3311 #ifdef CS_DEVSTATE_FEATURE
3312 			"devstate_feature",
3313 #endif
3314 #ifdef CS_DYNAMIC_SPEEDDIAL
3315 			"dynamic_speeddial",
3316 #endif
3317 #ifdef CS_DYNAMIC_SPEEDDIAL_CID
3318 			"dynamic_speeddial_cid",
3319 #endif
3320 #ifdef CS_EXPERIMENTAL
3321 			"experimental",
3322 #endif
3323 #ifdef DEBUG
3324 			"debug",
3325 #endif
3326 		};
3327 		comma = 0;
3328 		astman_append(s, "\"ConfigureEnabled\": [");
3329 		for (i = 0; i < ARRAY_LEN(conf_enabled_array); i++) {
3330 			astman_append(s, "%s\"%s\"", comma ? "," : "",conf_enabled_array[i]);
3331 			comma = 1;
3332 		}
3333 		astman_append(s, "],");
3334 
3335 		comma = 0;
3336 		astman_append(s, "\"Segments\":[");
3337 		for (i = 0; i < ARRAY_LEN(sccpConfigSegments); i++) {
3338 			astman_append(s, "%s", comma ? "," : "");
3339 			astman_append(s, "\"%s\"", sccpConfigSegments[i].name);
3340 			comma = 1;
3341 		}
3342 		astman_append(s, "]}\r\n");
3343 		total++;
3344 		if (sccp_strcaseequals(req_resultformat, "list")) {
3345 			astman_append(s,
3346 				"\r\nEvent: SCCPConfigMetaDataComplete\r\n"
3347 				"EventList: Complete\r\n"
3348 				"ListItems: %d\r\n\r\n", total);
3349 		} else if (sccp_strcaseequals(req_resultformat, "command")) {
3350 			astman_append(s, "--END COMMAND--\r\n");
3351 		}
3352 		astman_append(s, "\r\n");
3353 	} else {												// return metadata for option in segmnet
3354 		/*
3355 		   JSON:
3356 		   {
3357 		   "Segment": "general",
3358 		   "Options": [
3359 		   {
3360 		   Option: config->name,
3361 		   Type: ....,
3362 		   Flags : [Required, Deprecated, Obsolete, MultiEntry, RestartRequiredOnUpdate],
3363 		   DefaultValue: ...,
3364 		   Description: ....
3365 		   },
3366 		   {
3367 		   ...
3368 		   }
3369 		   ]
3370 
3371 		   }
3372 		 */
3373 		for (i = 0; i < ARRAY_LEN(sccpConfigSegments); i++) {
3374 			if (sccp_strcaseequals(sccpConfigSegments[i].name, req_segment)) {
3375 				sccpConfigSegment = &sccpConfigSegments[i];
3376 				const SCCPConfigOption *config = sccpConfigSegment->config;
3377 
3378 				if (sccp_strcaseequals(req_resultformat, "list")) {
3379 					astman_send_listack(s, m, "SCCPConfigMetaData Follows", "Start");
3380 					astman_append(s, "Event: SCCPConfigMetaData\r\n");
3381 				} else if (sccp_strcaseequals(req_resultformat, "command")) {
3382 					astman_append(s, "Response: Follows\r\n");
3383 				} else {
3384 					astman_append(s, "Response: Success\r\n");
3385 				}
3386 				if (!ast_strlen_zero(id)) {
3387 					astman_append(s, "ActionID: %s\r\n", id);
3388 				}
3389 				astman_append(s, "JSON: {");
3390 				astman_append(s, "\"Segment\":\"%s\",", sccpConfigSegment->name);
3391 				astman_append(s, "\"Options\":[");
3392 				comma = 0;
3393 
3394 				for (long unsigned int cur_elem = 0; cur_elem < sccpConfigSegment->config_size; cur_elem++) {
3395 					if ((config[cur_elem].flags & SCCP_CONFIG_FLAG_IGNORE) != SCCP_CONFIG_FLAG_IGNORE) {
3396 						astman_append(s, "%s", comma ? "," : "");
3397 						astman_append(s, "{");
3398 
3399 						{
3400 							astman_append(s, "\"Name\":\"%s\",", config[cur_elem].name);
3401 
3402 							switch (config[cur_elem].type) {
3403 								case SCCP_CONFIG_DATATYPE_BOOLEAN:
3404 									astman_append(s, "\"Type\":\"BOOLEAN\",");
3405 									astman_append(s, "\"Size\":%d", (int) config[cur_elem].size - 1);
3406 									break;
3407 								case SCCP_CONFIG_DATATYPE_INT:
3408 									astman_append(s, "\"Type\":\"INT\",");
3409 									astman_append(s, "\"Size\":%d", (int) config[cur_elem].size - 1);
3410 									break;
3411 								case SCCP_CONFIG_DATATYPE_UINT:
3412 									astman_append(s, "\"Type\":\"UNSIGNED INT\",");
3413 									astman_append(s, "\"Size\":%d", (int) config[cur_elem].size - 1);
3414 									break;
3415 								case SCCP_CONFIG_DATATYPE_STRINGPTR:
3416 									astman_append(s, "\"Type\":\" STRING\",");
3417 									astman_append(s, "\"Size\":0");
3418 									break;
3419 								case SCCP_CONFIG_DATATYPE_STRING:
3420 									astman_append(s, "\"Type\":\"STRING\",");
3421 									astman_append(s, "\"Size\":%d", (int) config[cur_elem].size - 1);
3422 									break;
3423 								case SCCP_CONFIG_DATATYPE_PARSER:
3424 									astman_append(s, "\"Type\":\"PARSER\",");
3425 									astman_append(s, "\"Size\":0,");
3426 									astman_append(s, "\"Parser\":\"%s\"", config[cur_elem].parsername);
3427 									break;
3428 								case SCCP_CONFIG_DATATYPE_CHAR:
3429 									astman_append(s, "\"Type\":\"CHAR\",");
3430 									astman_append(s, "\"Size\":1");
3431 									break;
3432 								case SCCP_CONFIG_DATATYPE_ENUM:
3433 									astman_append(s, "\"Type\":\"ENUM\",");
3434 									astman_append(s, "\"Size\":%d,", (int) config[cur_elem].size - 1);
3435 									char * all_entries = pbx_strdup (config[cur_elem].all_entries());
3436 									char *possible_entry = "";
3437 
3438 									int subcomma = 0;
3439 									astman_append(s, "\"Possible Values\": [");
3440 									while (all_entries && (possible_entry = strsep(&all_entries, ","))) {
3441 										astman_append(s, "%s\"%s\"", subcomma ? "," : "", possible_entry);
3442 										subcomma = 1;
3443 									}
3444 									astman_append(s, "]");
3445 									sccp_free (all_entries);
3446 									break;
3447 							}
3448 							astman_append(s, ",");
3449 
3450 							if ((config[cur_elem].flags & (SCCP_CONFIG_FLAG_REQUIRED | SCCP_CONFIG_FLAG_DEPRECATED | SCCP_CONFIG_FLAG_OBSOLETE | SCCP_CONFIG_FLAG_MULTI_ENTRY)) > 0 || (config[cur_elem].change & SCCP_CONFIG_NEEDDEVICERESET) == SCCP_CONFIG_NEEDDEVICERESET) {
3451 								astman_append(s, "\"Flags\":[");
3452 								{
3453 									int comma1 = 0;
3454 
3455 									if ((config[cur_elem].flags & SCCP_CONFIG_FLAG_REQUIRED) == SCCP_CONFIG_FLAG_REQUIRED) {
3456 										astman_append(s, "\"Required\"");
3457 										comma1 = 1;
3458 									}
3459 									if ((config[cur_elem].flags & SCCP_CONFIG_FLAG_DEPRECATED) == SCCP_CONFIG_FLAG_DEPRECATED) {
3460 										astman_append(s, "%s", comma1 ? "," : "");
3461 										astman_append(s, "\"Deprecated\"");
3462 										comma1 = 1;
3463 									}
3464 									if ((config[cur_elem].flags & SCCP_CONFIG_FLAG_OBSOLETE) == SCCP_CONFIG_FLAG_OBSOLETE) {
3465 										astman_append(s, "%s", comma1 ? "," : "");
3466 										astman_append(s, "\"Obsolete\"");
3467 										comma1 = 1;
3468 									}
3469 									if ((config[cur_elem].flags & SCCP_CONFIG_FLAG_MULTI_ENTRY) == SCCP_CONFIG_FLAG_MULTI_ENTRY) {
3470 										astman_append(s, "%s", comma1 ? "," : "");
3471 										astman_append(s, "\"MultiEntry\"");
3472 										comma1 = 1;
3473 									}
3474 									if ((config[cur_elem].change & SCCP_CONFIG_NEEDDEVICERESET) == SCCP_CONFIG_NEEDDEVICERESET) {
3475 										astman_append(s, "%s", comma1 ? "," : "");
3476 										astman_append(s, "\"RestartRequiredOnUpdate\"");
3477 										comma1 = 1;
3478 									}
3479 								}
3480 								astman_append(s, "],");
3481 							}
3482 
3483 							astman_append(s, "\"DefaultValue\":\"%s\"", config[cur_elem].defaultValue);
3484 
3485 							if (!sccp_strlen_zero(config[cur_elem].description)) {
3486 								char * description = pbx_strdup (config[cur_elem].description);
3487 								char *description_part = "";
3488 								int comma2 = 0;
3489 
3490 								astman_append(s, ",\"Description\": [");
3491 								while(description && (description_part = strsep(&description, "\n")) && !sccp_strlen_zero(description_part)) {
3492 									astman_append(s, "%s\"%s\"", comma2++ ? "," : "", description_part);
3493 								}
3494 								astman_append(s, "]");
3495 								sccp_free (description);
3496 							}
3497 						}
3498 						astman_append(s, "}");
3499 						comma = 1;
3500 					}
3501 				}
3502 				astman_append(s, "]}\r\n");
3503 				total++;
3504 				if (sccp_strcaseequals(req_resultformat, "list")) {
3505 					astman_append(s,
3506 						"\r\nEvent: SCCPConfigMetaDataComplete\r\n"
3507 						"EventList: Complete\r\n"
3508 						"ListItems: %d\r\n\r\n", total);
3509 				} else if (sccp_strcaseequals(req_resultformat, "command")) {
3510 					astman_append(s, "--END COMMAND--\r\n"
3511 							 "DataType: JSON\r\n"
3512 							 "Priviledge: Command\r\n"
3513 					);
3514 				}
3515 				astman_append(s, "\r\n");
3516 			}
3517 		}
3518 	}
3519 	return 0;
3520 }
3521 
_config_generate_wiki(char * filename)3522 static int _config_generate_wiki(char * filename)
3523 {
3524 	const SCCPConfigSegment * sccpConfigSegment = NULL;
3525 	const SCCPConfigOption * config = NULL;
3526 	long unsigned int sccp_option = 0;
3527 	long unsigned int segment = 0;
3528 	char * description = "";
3529 	char * description_part = "";
3530 	char fn[PATH_MAX];
3531 
3532 	snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
3533 	pbx_log(LOG_NOTICE, "Creating new wiki file '%s'\n", fn);
3534 
3535 	int fd = open (fn, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
3536 	if (fd == -1) {
3537 		pbx_log(LOG_WARNING, "Error creating new config file: %s\n", strerror(errno));
3538 		return -1;
3539 	}
3540 	FILE * f = fdopen(fd, "w+");
3541 	if (!f) {
3542 		pbx_log(LOG_WARNING, "Error opening new wiki file: %s\n", strerror(errno));
3543 		close (fd);
3544 		return -2;
3545 	}
3546 
3547 	char date[256] = "";
3548 	struct ast_tm tm;
3549 	struct timeval now = ast_tvnow();
3550 	ast_strftime (date, sizeof (date), "%b %e %T", ast_localtime (&now, &tm, NULL));
3551 
3552 	fprintf(f, "*sccp.conf options*\n\n");
3553 	for(segment = SCCP_CONFIG_GLOBAL_SEGMENT; segment <= SCCP_CONFIG_SOFTKEY_SEGMENT; segment++) {
3554 		sccpConfigSegment = sccp_find_segment((sccp_config_segment_t)segment);
3555 		fprintf(f, "\n**[%s] section**\n\n", sccpConfigSegment->name);
3556 		fprintf(f, "<table>\n");
3557 		fprintf(f, "<tr><td><b>parameter</b></td><td><b>default</b></td><td><b>format</b></td><td><b>required</b></td><td><b>status</b></td></tr>\n");
3558 		fprintf(f, "<tr><td></td><td colspan='4'><b>description</b></td></tr>\n");
3559 		config = sccpConfigSegment->config;
3560 		for(sccp_option = 0; sccp_option < sccpConfigSegment->config_size; sccp_option++) {
3561 			// if ((config[sccp_option].flags & (SCCP_CONFIG_FLAG_IGNORE | SCCP_CONFIG_FLAG_DEPRECATED | SCCP_CONFIG_FLAG_OBSOLETE)) == 0) {
3562 			sccp_log((DEBUGCAT_CONFIG))(VERBOSE_PREFIX_2 "adding name: %s, default_value: %s\n", config[sccp_option].name, config[sccp_option].defaultValue);
3563 			if(!sccp_strlen_zero(config[sccp_option].name)) {
3564 				char delims[] = "|";
3565 				char * option_name_tokens = pbx_strdup (config[sccp_option].name);
3566 				char * option_value_tokens = NULL;
3567 				if(!sccp_strlen_zero(config[sccp_option].defaultValue)) {
3568 					option_value_tokens = pbx_strdup (config[sccp_option].defaultValue);
3569 				} else {
3570 					option_value_tokens = pbx_strdup ("\"\"");
3571 				}
3572 				char * option_name_tokens_saveptr = NULL;
3573 				char * option_value_tokens_saveptr = NULL;
3574 				char * option_name = strtok_r(option_name_tokens, delims, &option_name_tokens_saveptr);
3575 				char * option_value = strtok_r(option_value_tokens, delims, &option_value_tokens_saveptr);
3576 				while(option_name != NULL) {
3577 					fprintf(f, "<tr class=option_row id='%s'>\n", option_name);
3578 					fprintf(f, "<td class='name'>%s</td><td class='default_value'>%s</td>\n", option_name, option_value);
3579 					option_name = strtok_r(NULL, delims, &option_name_tokens_saveptr);
3580 					option_value = strtok_r(NULL, delims, &option_value_tokens_saveptr);
3581 					switch(config[sccp_option].type) {
3582 						case SCCP_CONFIG_DATATYPE_STRING:
3583 							fprintf(f, "<td class='format'>max length:%d</td>\n", (int)config[sccp_option].size - 1);
3584 							break;
3585 						case SCCP_CONFIG_DATATYPE_ENUM: {
3586 							char * all_entries = pbx_strdup (config[sccp_option].all_entries());
3587 							char * possible_entry = "";
3588 							int subcomma = 0;
3589 
3590 							fprintf(f, "<td class='format'><small>potential values:[");
3591 							while(all_entries && (possible_entry = strsep(&all_entries, ","))) {
3592 								fprintf(f, "%s%s", subcomma ? ", " : "", possible_entry);
3593 								subcomma = 1;
3594 							}
3595 							fprintf(f, "]</small></td>\n");
3596 							sccp_free (all_entries);
3597 						} break;
3598 						default:
3599 							fprintf(f, "<td class='format'>-</td>\n");
3600 							break;
3601 					}
3602 					fprintf(f, "<td class='required'>%s</td>\n", (config[sccp_option].flags & SCCP_CONFIG_FLAG_REQUIRED) == SCCP_CONFIG_FLAG_REQUIRED ? "yes" : "-");
3603 					fprintf(f, "<td class='status'>%s</td> ",
3604 						(config[sccp_option].flags & SCCP_CONFIG_FLAG_DEPRECATED) == SCCP_CONFIG_FLAG_DEPRECATED
3605 						    ? "deprecated"
3606 						    : (config[sccp_option].flags & SCCP_CONFIG_FLAG_OBSOLETE) == SCCP_CONFIG_FLAG_OBSOLETE ? "obsolete" : "-");
3607 					fprintf(f, "</tr>\n");
3608 					if(!sccp_strlen_zero(config[sccp_option].description)) {
3609 						fprintf(f, "<tr class='descr_row'><td></td>\n");
3610 						fprintf(f, "<td class='description' id='%s' colspan='4'><small>", config[sccp_option].name);
3611 						description = pbx_strdup (config[sccp_option].description);
3612 						while((description_part = strsep(&description, "\n"))) {
3613 							if(!sccp_strlen_zero(description_part)) {
3614 								fprintf(f, "%s.<br>", description_part);
3615 							}
3616 						}
3617 						if(description_part) {
3618 							sccp_free(description_part);
3619 						}
3620 						fprintf(f, "</small></td>\n");
3621 						sccp_free (description);
3622 					}
3623 				}
3624 				fprintf(f, "</tr>\n");
3625 				sccp_free (option_name_tokens);
3626 				sccp_free (option_value_tokens);
3627 			} else {
3628 				pbx_log(LOG_ERROR, "Error creating new variable structure for %s='%s'\n", config[sccp_option].name, config[sccp_option].defaultValue);
3629 				fclose(f);
3630 				close (fd);
3631 				return 2;
3632 			}
3633 		}
3634 		fprintf(f, "</table><br>\n");
3635 	}
3636 	fclose(f);
3637 	close (fd);
3638 	pbx_log (LOG_NOTICE, "Created new wiki file '%s'\n", fn);
3639 
3640 	return 0;
3641 };
3642 
3643 /*!
3644  * \brief Generate default sccp.conf file
3645  * \param filename Filename
3646  * \param configType Config Type:
3647  *- 0 = templated + registered devices
3648  *- 1 = required params only
3649  *- 2 = all params
3650  * \todo Add functionality
3651  */
sccp_config_generate(char * filename,int configType)3652 int sccp_config_generate(char *filename, int configType)
3653 {
3654 	if(configType == 3) {
3655 		return _config_generate_wiki(filename);
3656 	}
3657 	const SCCPConfigSegment * sccpConfigSegment = NULL;
3658 	const SCCPConfigOption * config = NULL;
3659 	long unsigned int sccp_option = 0;
3660 	long unsigned int segment = 0;
3661 	char *description = "";
3662 	char *description_part = "";
3663 	char name_and_value[100] = "";
3664 	char size_str[15] = "";
3665 	int linelen = 0;
3666 	struct ast_str * extra_info = pbx_str_alloca(DEFAULT_PBX_STR_BUFFERSIZE * 3);
3667 
3668 	char fn[PATH_MAX];
3669 
3670 	snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
3671 	pbx_log(LOG_NOTICE, "Creating new config file '%s'\n", fn);
3672 
3673 	int fd = open (fn, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
3674 	if (fd == -1) {
3675 		pbx_log (LOG_WARNING, "Error creating new config file: %s\n", strerror (errno));
3676 		return -1;
3677 	}
3678 	FILE * f = fdopen(fd, "w+");
3679 	if (!f) {
3680 		pbx_log(LOG_WARNING, "Error opening new config file: %s\n", strerror(errno));
3681 		close (fd);
3682 		return -2;
3683 	}
3684 
3685 	char date[256] = "";
3686 	struct ast_tm tm;
3687 	struct timeval now = ast_tvnow();
3688 	ast_strftime (date, sizeof (date), "%b %e %T", ast_localtime (&now, &tm, NULL));
3689 
3690 	fprintf(f, ";!\n");
3691 	fprintf(f, ";! Automatically generated configuration file\n");
3692 	fprintf(f, ";! Filename: %s (%s)\n", filename, fn);
3693 	fprintf(f, ";! Generator: sccp config generate\n");
3694 	fprintf(f, ";! Creation Date: %s", date);
3695 	fprintf(f, ";!\n");
3696 	fprintf(f, "\n");
3697 	fprintf(f, ";!\n");
3698 	fprintf(f, ";! This file is only provided to show a possible parameters and their defaults.\n");
3699 	fprintf(f, ";! Please do not use this file as a starting point for your sccp.conf file !\n");
3700 	fprintf(f, ";! Parameters that have blanks or empty strings are just there as a placeholder, not to show a valid value.\n");
3701 	fprintf(f, ";!\n");
3702 	fprintf(f, "\n");
3703 
3704 	for (segment = SCCP_CONFIG_GLOBAL_SEGMENT; segment <= SCCP_CONFIG_SOFTKEY_SEGMENT; segment++) {
3705 		sccpConfigSegment = sccp_find_segment((sccp_config_segment_t)segment);
3706 		if (configType == 0 && (segment == SCCP_CONFIG_DEVICE_SEGMENT || segment == SCCP_CONFIG_LINE_SEGMENT)) {
3707 			sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "adding [%s] template section\n", sccpConfigSegment->name);
3708 			fprintf(f, "\n;\n; %s section\n;\n[default_%s](!)\n", sccpConfigSegment->name, sccpConfigSegment->name);
3709 		} else if (configType == 0 && segment == SCCP_CONFIG_SOFTKEY_SEGMENT) {
3710 			sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "adding [%s] section\n", sccpConfigSegment->name);
3711 			fprintf(f, "\n;\n; %s section\n;\n;[mysoftkeyset]\n", sccpConfigSegment->name);
3712 		} else {
3713 			sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "adding [%s] section\n", sccpConfigSegment->name);
3714 			fprintf(f, "\n;\n; %s section\n;\n[%s]\n", sccpConfigSegment->name, sccpConfigSegment->name);
3715 		}
3716 
3717 		config = sccpConfigSegment->config;
3718 		for (sccp_option = 0; sccp_option < sccpConfigSegment->config_size; sccp_option++) {
3719 			//if ((config[sccp_option].flags & SCCP_CONFIG_FLAG_IGNORE & SCCP_CONFIG_FLAG_DEPRECATED & SCCP_CONFIG_FLAG_OBSOLETE) == 0) {
3720 			if ((config[sccp_option].flags & (SCCP_CONFIG_FLAG_IGNORE | SCCP_CONFIG_FLAG_DEPRECATED | SCCP_CONFIG_FLAG_OBSOLETE)) == 0) {
3721 				sccp_log((DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_2 "adding name: %s, default_value: %s\n", config[sccp_option].name, config[sccp_option].defaultValue);
3722 
3723 				if (!sccp_strlen_zero(config[sccp_option].name)) {
3724 					if (!sccp_strlen_zero(config[sccp_option].defaultValue)			// non empty
3725 					    || (configType != 2 && ((config[sccp_option].flags & SCCP_CONFIG_FLAG_REQUIRED) != SCCP_CONFIG_FLAG_REQUIRED && sccp_strlen_zero(config[sccp_option].defaultValue)))	// empty but required
3726 					    ) {
3727 					    	if (strstr(config[sccp_option].name, "|")) {
3728 					    		char delims[] = "|";
3729 							char * option_name_tokens = pbx_strdup (config[sccp_option].name);
3730 							char * option_value_tokens = NULL;
3731 							if(!sccp_strlen_zero(config[sccp_option].defaultValue)) {
3732 								option_value_tokens = pbx_strdup (config[sccp_option].defaultValue);
3733 							} else {
3734 								option_value_tokens = pbx_strdup ("\"\"");
3735 							}
3736 							char * option_name_tokens_saveptr = NULL;
3737 							char * option_value_tokens_saveptr = NULL;
3738 							char * option_name = strtok_r(option_name_tokens, delims, &option_name_tokens_saveptr);
3739 							char *option_value = strtok_r(option_value_tokens, delims, &option_value_tokens_saveptr);
3740 					    		while (option_name != NULL) {
3741 								snprintf(name_and_value, sizeof(name_and_value), "%s = %s", option_name, option_value ? option_value : "\"\"");
3742 								fprintf(f, "%s", name_and_value);
3743 								option_name = strtok_r(NULL, delims, &option_name_tokens_saveptr);
3744 								option_value = strtok_r(NULL, delims, &option_value_tokens_saveptr);
3745 								if (option_name) {
3746 									fprintf(f, "\n");
3747 								}
3748 							}
3749 							sccp_free (option_name_tokens);
3750 							sccp_free (option_value_tokens);
3751 						} else {
3752 							snprintf(name_and_value, sizeof(name_and_value), "%s%s = %s", !sccp_strlen_zero(config[sccp_option].defaultValue) ? ";" : "", config[sccp_option].name, sccp_strlen_zero(config[sccp_option].defaultValue) ? "\"\"" : config[sccp_option].defaultValue);
3753 							fprintf(f, "%s", name_and_value);
3754 					    	}
3755 						linelen = (int) strlen(name_and_value);
3756 						switch (config[sccp_option].type) {
3757 							case SCCP_CONFIG_DATATYPE_STRING:
3758 								snprintf(size_str, sizeof(size_str), "(SIZE: %d) ", (int) config[sccp_option].size - 1);
3759 								break;
3760 							case SCCP_CONFIG_DATATYPE_ENUM:
3761 								{
3762 								char * all_entries = pbx_strdup (config[sccp_option].all_entries());
3763 								char * possible_entry = "";
3764 								int subcomma = 0;
3765 
3766 								pbx_str_append (&extra_info, 0, "(POSSIBLE VALUES: [");
3767 								while (all_entries && (possible_entry = strsep (&all_entries, ","))) {
3768 									pbx_str_append (&extra_info, 0, "%s\"%s\"", subcomma ? "," : "", possible_entry);
3769 									subcomma = 1;
3770 									}
3771 									pbx_str_append(&extra_info, 0, "])");
3772 									sccp_free (all_entries);
3773 								}
3774 								size_str[0] = '\0';
3775 								break;
3776 							default:
3777 								size_str[0] = '\0';
3778 								break;
3779 						}
3780 						fprintf(f, "%*.s ; %s%s%s%s%s", 81 - linelen, " ",
3781 							((config[sccp_option].flags & SCCP_CONFIG_FLAG_REQUIRED) == SCCP_CONFIG_FLAG_REQUIRED) ? "(REQUIRED) " : "",
3782 							((config[sccp_option].flags & SCCP_CONFIG_FLAG_MULTI_ENTRY) == SCCP_CONFIG_FLAG_MULTI_ENTRY) ? "(MULTI-ENTRY) " : "",
3783 							((config[sccp_option].flags & SCCP_CONFIG_FLAG_DEPRECATED) == SCCP_CONFIG_FLAG_DEPRECATED) ? "(DEPRECATED) " : "",
3784 							((config[sccp_option].flags & SCCP_CONFIG_FLAG_OBSOLETE) == SCCP_CONFIG_FLAG_OBSOLETE) ? "(DEPRECATED) " : "",
3785 							size_str
3786 							);
3787 						if (!sccp_strlen_zero(config[sccp_option].description)) {
3788 							description = pbx_strdup (config[sccp_option].description);
3789 							while ((description_part = strsep(&description, "\n"))) {
3790 								if (!sccp_strlen_zero(description_part)) {
3791 									if (linelen) {
3792 										fprintf(f, "%s\n", description_part);
3793 									} else {
3794 										fprintf(f, "%*.s ; %s\n", 81, " ", description_part);
3795 									}
3796 									linelen = 0;
3797 								}
3798 							}
3799 							if (description_part) {
3800 								sccp_free(description_part);
3801 							}
3802 							sccp_free (description);
3803 						} else {
3804 							fprintf(f, "\n");
3805 						}
3806 						if (ast_str_strlen(extra_info)) {
3807 							fprintf(f, "%*.s ; %s\n", 81, " ", pbx_str_buffer(extra_info));
3808 							ast_str_reset(extra_info);
3809 						}
3810 					}
3811 				} else {
3812 					pbx_log(LOG_ERROR, "Error creating new variable structure for %s='%s'\n", config[sccp_option].name, config[sccp_option].defaultValue);
3813 					fclose(f);
3814 					close (fd);
3815 					return 2;
3816 				}
3817 			}
3818 		}
3819 		sccp_log((DEBUGCAT_CONFIG)) ("\n");
3820 	}
3821 	fclose(f);
3822 	close (fd);
3823 	pbx_log (LOG_NOTICE, "Created new config file '%s'\n", fn);
3824 
3825 	return 0;
3826 };
3827 
3828 #if CS_TEST_FRAMEWORK
3829 #include <asterisk/test.h>
AST_TEST_DEFINE(sccp_config_base_functions)3830 AST_TEST_DEFINE(sccp_config_base_functions)
3831 {
3832 	switch(cmd) {
3833 		case TEST_INIT:
3834 			info->name = "base_functions";
3835 			info->category = "/channels/chan_sccp/config/";
3836 			info->summary = "chan-sccp-b config test";
3837 			info->description = "chan-sccp-b config tests";
3838 			return AST_TEST_NOT_RUN;
3839 		case TEST_EXECUTE:
3840 			break;
3841 	}
3842 
3843 	pbx_test_status_update(test, "Executing chan-sccp-b config tests...\n");
3844 
3845 	pbx_test_status_update(test, "sccp_find_segment...\n");
3846 	pbx_test_validate(test, sccp_find_segment(SCCP_CONFIG_GLOBAL_SEGMENT) == &sccpConfigSegments[0]);
3847 
3848 	pbx_test_status_update(test, "sccp_fine_config...\n");
3849 	const SCCPConfigSegment *sccpConfigSegment = sccp_find_segment(SCCP_CONFIG_GLOBAL_SEGMENT);
3850 	pbx_test_validate(test, sccp_find_config(SCCP_CONFIG_GLOBAL_SEGMENT, "debug") == &sccpConfigSegment->config[0]);
3851 	pbx_test_validate(test, sccp_find_config(SCCP_CONFIG_GLOBAL_SEGMENT, "port") == &sccpConfigSegment->config[6]);
3852 
3853 	return AST_TEST_PASS;
3854 }
3855 
AST_TEST_DEFINE(sccp_config_multientry)3856 AST_TEST_DEFINE(sccp_config_multientry)
3857 {
3858 	switch(cmd) {
3859 		case TEST_INIT:
3860 			info->name = "MultiEntryParameters";
3861 			info->category = "/channels/chan_sccp/config/";
3862 			info->summary = "chan-sccp-b config test";
3863 			info->description = "chan-sccp-b config tests";
3864 			return AST_TEST_NOT_RUN;
3865 		case TEST_EXECUTE:
3866 			break;
3867 	}
3868 
3869 	pbx_test_status_update(test, "createVariableSetForMultiEntryParameters...\n");
3870 	PBX_VARIABLE_TYPE *varset = NULL, *v = NULL,*root = NULL;
3871 	root = ast_variable_new("disallow", "0.0.0.0/0.0.0.0", "");
3872 	root->next = ast_variable_new("allow", "10.10.10.0/255.255.255.0", "");
3873 
3874 	v = varset = createVariableSetForMultiEntryParameters(root, "disallow|allow", varset);
3875 	pbx_test_validate(test, v != NULL);
3876 	pbx_test_status_update(test, "Test disallow == 0.0.0.0/0.0.0.0\n");
3877 	pbx_test_validate(test, (!strcasecmp((const char *) "disallow", v->name) && !strcasecmp((const char *) "0.0.0.0/0.0.0.0", v->value)));
3878 	v = v->next;
3879 	pbx_test_validate(test, v != NULL);
3880 	pbx_test_status_update(test, "Test allow == 10.10.10.10/255.255.255.255\n");
3881 	pbx_test_validate(test, (!strcasecmp((const char *) "allow", v->name) && !strcasecmp((const char *) "10.10.10.0/255.255.255.0", v->value)));
3882 	v = v->next;
3883 	pbx_test_validate(test, v == NULL);
3884 
3885 	pbx_variables_destroy(varset);
3886 	pbx_variables_destroy(root);
3887 
3888 	return AST_TEST_PASS;
3889 }
3890 
AST_TEST_DEFINE(sccp_config_tokenized_default)3891 AST_TEST_DEFINE(sccp_config_tokenized_default)
3892 {
3893 	switch(cmd) {
3894 		case TEST_INIT:
3895 			info->name = "TokenizedDefault";
3896 			info->category = "/channels/chan_sccp/config/";
3897 			info->summary = "chan-sccp-b config test";
3898 			info->description = "chan-sccp-b config tests";
3899 			return AST_TEST_NOT_RUN;
3900 		case TEST_EXECUTE:
3901 			break;
3902 	}
3903 
3904 	pbx_test_status_update(test, "createVariableSetForTokenizedDefault...\n");
3905 	PBX_VARIABLE_TYPE *varset = NULL, *v = NULL;
3906 	v = varset = createVariableSetForTokenizedDefault("disallow|allow", "0.0.0.0/0.0.0.0|10.10.10.0/255.255.255.0", NULL);
3907 
3908 	pbx_test_validate(test, v != NULL);
3909 	pbx_test_status_update(test, "Test disallow == 0.0.0.0\n");
3910 	pbx_test_validate(test, (!strcasecmp((const char *) "disallow", v->name) && !strcasecmp((const char *) "0.0.0.0/0.0.0.0", v->value)));
3911 	v = v->next;
3912 	pbx_test_validate(test, v != NULL);
3913 	pbx_test_status_update(test, "Test allow == 10.10.10.0/255.255.255.0\n");
3914 	pbx_test_validate(test, (!strcasecmp((const char *) "allow", v->name) && !strcasecmp((const char *) "10.10.10.0/255.255.255.0", v->value)));
3915 
3916 	pbx_variables_destroy(varset);
3917 
3918 	return AST_TEST_PASS;
3919 }
3920 
3921 /*
3922 AST_TEST_DEFINE(sccp_config_setValue)
3923 {
3924 	switch(cmd) {
3925 		case TEST_INIT:
3926 			info->name = "setValue";
3927 			info->category = "/channels/chan_sccp/config/";
3928 			info->summary = "chan-sccp-b config test";
3929 			info->description = "chan-sccp-b config tests";
3930 			return AST_TEST_NOT_RUN;
3931 		case TEST_EXECUTE:
3932 			break;
3933 	}
3934 
3935 	//pbx_test_status_update(test, "sccp_config_object_setValue...\n");
3936 	//static sccp_configurationchange_t sccp_config_object_setValue(void *obj, PBX_VARIABLE_TYPE * cat_root, const char *name, const char *value, int lineno, const sccp_config_segment_t segment, boolean_t *SetEntries, FALSE)
3937 
3938 	return AST_TEST_PASS;
3939 }
3940 
3941 AST_TEST_DEFINE(sccp_config_setDefault)
3942 {
3943 	switch(cmd) {
3944 		case TEST_INIT:
3945 			info->name = "setDefault";
3946 			info->category = "/channels/chan_sccp/config/";
3947 			info->summary = "chan-sccp-b config test";
3948 			info->description = "chan-sccp-b config tests";
3949 			return AST_TEST_NOT_RUN;
3950 		case TEST_EXECUTE:
3951 			break;
3952 	}
3953 
3954 	//pbx_test_status_update(test, "sccp_config_set_defaults...\n");
3955 	//static void sccp_config_set_defaults(void *obj, const sccp_config_segment_t segment, boolean_t *SetEntries)
3956 
3957 	return AST_TEST_PASS;
3958 }
3959 */
3960 
sccp_register_tests(void)3961 static void __attribute__((constructor)) sccp_register_tests(void)
3962 {
3963 	AST_TEST_REGISTER(sccp_config_base_functions);
3964 	AST_TEST_REGISTER(sccp_config_multientry);
3965 	AST_TEST_REGISTER(sccp_config_tokenized_default);
3966 	//AST_TEST_REGISTER(sccp_config_setValue);
3967 	//AST_TEST_REGISTER(sccp_config_setDefault);
3968 }
3969 
sccp_unregister_tests(void)3970 static void __attribute__((destructor)) sccp_unregister_tests(void)
3971 {
3972 	AST_TEST_UNREGISTER(sccp_config_base_functions);
3973 	AST_TEST_UNREGISTER(sccp_config_multientry);
3974 	AST_TEST_UNREGISTER(sccp_config_tokenized_default);
3975 	//AST_TEST_UNREGISTER(sccp_config_setValue);
3976 	//AST_TEST_UNREGISTER(sccp_config_setDefault);
3977 }
3978 #endif
3979 
3980 // kate: indent-width 8; replace-tabs off; indent-mode cstyle; auto-insert-doxygen on; line-numbers on; tab-indents on; keep-extra-spaces off; auto-brackets off;
3981