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 '[' & ']' 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