Coverage Report

Created: 2026-10-15 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/plugins/ua_config_json.c
Line
Count
Source
1
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
2
 * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
3
 *
4
 *    Copyright 2023 (c) Fraunhofer IOSB (Author: Noel Graf)
5
 *    Copyright 2025 (c) o6 Automation GmbH (Author: Julius Pfrommer)
6
 */
7
8
#include <open62541/plugin/log.h>
9
#include <open62541/server_config_file_based.h>
10
#include "cj5.h"
11
#include "open62541/server_config_default.h"
12
#ifdef UA_ENABLE_ENCRYPTION
13
#include "open62541/plugin/certificategroup_default.h"
14
#endif
15
16
182
#define MAX_TOKENS 1024
17
18
typedef struct {
19
    const char *json;
20
    const cj5_token *tokens;
21
    size_t tokensSize;
22
    size_t index;
23
    UA_Byte depth;
24
    cj5_result result;
25
26
    UA_Logger *logging;
27
} ParsingCtx;
28
29
static UA_ByteString
30
40.8k
getJsonPart(cj5_token tok, const char *json) {
31
40.8k
    UA_ByteString bs;
32
40.8k
    UA_ByteString_init(&bs);
33
40.8k
    if(tok.type == CJ5_TOKEN_STRING) {
34
2.58k
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start - 1);
35
2.58k
        bs.length = (tok.end - tok.start) + 3;
36
2.58k
        return bs;
37
38.2k
    } else {
38
38.2k
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start);
39
38.2k
        bs.length = (tok.end - tok.start) + 1;
40
38.2k
        return bs;
41
38.2k
    }
42
40.8k
}
43
44
/* Advances ctx->index and returns the next token, or a zeroed size-0
45
 * sentinel once the stream is exhausted. A field's declared child count
46
 * can desync from the actual token count, so this stays in bounds of
47
 * `tokens[MAX_TOKENS]` even then. */
48
static cj5_token
49
256k
nextToken(ParsingCtx *ctx) {
50
256k
    if(!ctx->tokens || ctx->index + 1 >= ctx->tokensSize) {
51
230k
        ctx->index = ctx->tokensSize;
52
230k
        cj5_token empty;
53
230k
        memset(&empty, 0, sizeof(empty));
54
230k
        return empty;
55
230k
    }
56
26.2k
    ctx->index++;
57
26.2k
    return ctx->tokens[ctx->index];
58
256k
}
59
60
/* Forward declarations*/
61
#define PARSE_JSON(TYPE) static UA_StatusCode                   \
62
    TYPE##_parseJson(ParsingCtx *ctx, void *configField, size_t *configFieldSize)
63
64
typedef UA_StatusCode
65
(*parseJsonSignature)(ParsingCtx *ctx, void *configField, size_t *configFieldSize);
66
67
#ifdef UA_ENABLE_ENCRYPTION
68
static UA_ByteString
69
loadCertificateFile(const char *const path);
70
#endif
71
72
/* The DataType "kind" is an internal type classification. It is used to
73
 * dispatch handling to the correct routines. */
74
#define UA_SERVERCONFIGFIELDKINDS 25
75
typedef enum {
76
    /* Basic Types */
77
    UA_SERVERCONFIGFIELD_INT64 = 0,
78
    UA_SERVERCONFIGFIELD_UINT16,
79
    UA_SERVERCONFIGFIELD_UINT32,
80
    UA_SERVERCONFIGFIELD_UINT64,
81
    UA_SERVERCONFIGFIELD_STRING,
82
    UA_SERVERCONFIGFIELD_LOCALIZEDTEXT,
83
    UA_SERVERCONFIGFIELD_DOUBLE,
84
    UA_SERVERCONFIGFIELD_BOOLEAN,
85
    UA_SERVERCONFIGFIELD_DURATION,
86
    UA_SERVERCONFIGFIELD_DURATIONRANGE,
87
    UA_SERVERCONFIGFIELD_UINT32RANGE,
88
89
    /* Advanced Types */
90
    UA_SERVERCONFIGFIELD_BUILDINFO,
91
    UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION,
92
    UA_SERVERCONFIGFIELD_STRINGARRAY,
93
    UA_SERVERCONFIGFIELD_UINT32ARRAY,
94
    UA_SERVERCONFIGFIELD_DATETIME,
95
    UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION,
96
    UA_SERVERCONFIGFIELD_TCPCONFIGURATION,
97
    UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION,
98
    UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION,
99
    UA_SERVERCONFIGFIELD_MDNSCONFIGURATION,
100
    UA_SERVERCONFIGFIELD_SECURITYPOLICIES,
101
    UA_SERVERCONFIGFIELD_SECURITYPKI,
102
103
    /* Enumerations */
104
    UA_SERVERCONFIGFIELD_APPLICATIONTYPE,
105
    UA_SERVERCONFIGFIELD_RULEHANDLING
106
} UA_ServerConfigFieldKind;
107
108
extern const parseJsonSignature parseJsonJumpTable[UA_SERVERCONFIGFIELDKINDS];
109
110
/*----------------------Basic Types------------------------*/
111
0
PARSE_JSON(Int64Field) {
112
0
    cj5_token tok = nextToken(ctx);
113
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
114
0
    UA_Int64 out;
115
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_INT64], NULL);
116
0
    if(retval != UA_STATUSCODE_GOOD)
117
0
        return retval;
118
0
    UA_Int64 *field = (UA_Int64*)configField;
119
0
    *field = out;
120
0
    return retval;
121
0
}
122
93
PARSE_JSON(UInt16Field) {
123
93
    cj5_token tok = nextToken(ctx);
124
93
    UA_ByteString buf = getJsonPart(tok, ctx->json);
125
93
    UA_UInt16 out;
126
93
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT16], NULL);
127
93
    if(retval != UA_STATUSCODE_GOOD)
128
72
        return retval;
129
21
    UA_UInt16 *field = (UA_UInt16*)configField;
130
21
    *field = out;
131
21
    return retval;
132
93
}
133
372
PARSE_JSON(UInt32Field) {
134
372
    cj5_token tok = nextToken(ctx);
135
372
    UA_ByteString buf = getJsonPart(tok, ctx->json);
136
372
    UA_UInt32 out;
137
372
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
138
372
    if(retval != UA_STATUSCODE_GOOD)
139
95
        return retval;
140
277
    UA_UInt32 *field = (UA_UInt32*)configField;
141
277
    *field = out;
142
277
    return retval;
143
372
}
144
24
PARSE_JSON(UInt64Field) {
145
24
    cj5_token tok = nextToken(ctx);
146
24
    UA_ByteString buf = getJsonPart(tok, ctx->json);
147
24
    UA_UInt64 out;
148
24
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT64], NULL);
149
24
    if(retval != UA_STATUSCODE_GOOD)
150
14
        return retval;
151
10
    UA_UInt64 *field = (UA_UInt64*)configField;
152
10
    *field = out;
153
10
    return retval;
154
24
}
155
37.6k
PARSE_JSON(StringField) {
156
37.6k
    cj5_token tok = nextToken(ctx);
157
37.6k
    UA_ByteString buf = getJsonPart(tok, ctx->json);
158
37.6k
    UA_String out;
159
37.6k
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_STRING], NULL);
160
37.6k
    if(retval != UA_STATUSCODE_GOOD)
161
37.2k
        return retval;
162
386
    UA_String *field = (UA_String*)configField;
163
386
    if(field != NULL) {
164
386
        UA_String_clear(field);
165
386
        *field = out;
166
386
    }
167
386
    return retval;
168
37.6k
}
169
52
PARSE_JSON(LocalizedTextField) {
170
    /*
171
     applicationName: {
172
        locale: "de-DE",
173
        text: "Test text"
174
    }
175
     */
176
52
    cj5_token tok = nextToken(ctx);
177
52
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
178
52
    UA_String locale = {.length = 0, .data = NULL};
179
52
    UA_String text = {.length = 0, .data = NULL};
180
460
    for(size_t j = tok.size/2; j > 0; j--) {
181
408
        tok = nextToken(ctx);
182
408
        switch (tok.type) {
183
249
        case CJ5_TOKEN_STRING: {
184
249
            char *field = (char*)UA_malloc(tok.size + 1);
185
249
            unsigned int str_len = 0;
186
249
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field, &str_len);
187
188
249
            tok = nextToken(ctx);
189
249
            UA_ByteString buf = getJsonPart(tok, ctx->json);
190
249
            if(strcmp(field, "locale") == 0)
191
0
                retval |= UA_decodeJson(&buf, &locale, &UA_TYPES[UA_TYPES_STRING], NULL);
192
249
            else if(strcmp(field, "text") == 0)
193
0
                retval |= UA_decodeJson(&buf, &text, &UA_TYPES[UA_TYPES_STRING], NULL);
194
249
            else {
195
249
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
196
249
            }
197
249
            UA_free(field);
198
249
            break;
199
0
        }
200
159
        default:
201
159
            break;
202
408
        }
203
408
    }
204
52
    UA_LocalizedText out;
205
52
    out.locale = locale;
206
52
    out.text = text;
207
52
    if(retval != UA_STATUSCODE_GOOD)
208
0
        return retval;
209
52
    UA_LocalizedText *field = (UA_LocalizedText*)configField;
210
52
    if(field != NULL) {
211
52
        UA_LocalizedText_clear(field);
212
52
        *field = out;
213
52
    }
214
52
    return retval;
215
52
}
216
298
PARSE_JSON(DoubleField) {
217
298
    cj5_token tok = nextToken(ctx);
218
298
    UA_ByteString buf = getJsonPart(tok, ctx->json);
219
298
    UA_Double out;
220
298
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DOUBLE], NULL);
221
298
    if(retval != UA_STATUSCODE_GOOD)
222
42
        return retval;
223
256
    UA_Double *field = (UA_Double *)configField;
224
256
    *field = out;
225
256
    return retval;
226
298
}
227
21
PARSE_JSON(BooleanField) {
228
21
    cj5_token tok = nextToken(ctx);
229
21
    UA_ByteString buf = getJsonPart(tok, ctx->json);
230
21
    UA_Boolean out;
231
21
    if(tok.type != CJ5_TOKEN_BOOL) {
232
21
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Value of type bool expected.");
233
21
        return UA_STATUSCODE_BADTYPEMISMATCH;
234
21
    }
235
0
    UA_String val = UA_STRING("true");
236
0
    if(UA_String_equal(&val, &buf)) {
237
0
        out = true;
238
0
    }else {
239
0
        out = false;
240
0
    }
241
    /* set server config field */
242
0
    UA_Boolean *field = (UA_Boolean *)configField;
243
0
    *field = out;
244
0
    return UA_STATUSCODE_GOOD;
245
21
}
246
129
PARSE_JSON(DurationField) {
247
129
    UA_Double double_value;
248
129
    UA_StatusCode retval = DoubleField_parseJson(ctx, &double_value, NULL);
249
129
    if(retval != UA_STATUSCODE_GOOD)
250
14
        return retval;
251
115
    UA_Duration *field = (UA_Duration*)configField;
252
115
    *field = (UA_Duration)double_value;
253
115
    return retval;
254
129
}
255
PARSE_JSON(DurationRangeField) {
256
    UA_DurationRange *field = (UA_DurationRange*)configField;
257
    cj5_token tok = nextToken(ctx);
258
    for(size_t j = tok.size/2; j > 0; j--) {
259
        tok = nextToken(ctx);
260
        switch (tok.type) {
261
        case CJ5_TOKEN_STRING: {
262
            char *field_str = (char*)UA_malloc(tok.size + 1);
263
            unsigned int str_len = 0;
264
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
265
            if(strcmp(field_str, "min") == 0)
266
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATION](ctx, &field->min, NULL);
267
            else if(strcmp(field_str, "max") == 0)
268
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATION](ctx, &field->max, NULL);
269
            else {
270
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
271
            }
272
            UA_free(field_str);
273
            break;
274
        }
275
        default:
276
            break;
277
        }
278
    }
279
    return UA_STATUSCODE_GOOD;
280
}
281
PARSE_JSON(UInt32RangeField) {
282
    UA_UInt32Range *field = (UA_UInt32Range*)configField;
283
    cj5_token tok = nextToken(ctx);
284
    for(size_t j = tok.size/2; j > 0; j--) {
285
        tok = nextToken(ctx);
286
        switch (tok.type) {
287
        case CJ5_TOKEN_STRING: {
288
            char *field_str = (char*)UA_malloc(tok.size + 1);
289
            unsigned int str_len = 0;
290
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
291
            if(strcmp(field_str, "min") == 0)
292
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &field->min, NULL);
293
            else if(strcmp(field_str, "max") == 0)
294
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &field->max, NULL);
295
            else {
296
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
297
            }
298
            UA_free(field_str);
299
            break;
300
        }
301
        default:
302
            break;
303
        }
304
    }
305
    return UA_STATUSCODE_GOOD;
306
}
307
308
/*----------------------Advanced Types------------------------*/
309
0
PARSE_JSON(BuildInfo) {
310
0
    UA_BuildInfo *field = (UA_BuildInfo*)configField;
311
0
    cj5_token tok = nextToken(ctx);
312
0
    for(size_t j = tok.size/2; j > 0; j--) {
313
0
        tok = nextToken(ctx);
314
0
        switch (tok.type) {
315
0
        case CJ5_TOKEN_STRING: {
316
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
317
0
            unsigned int str_len = 0;
318
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
319
0
            if(strcmp(field_str, "productUri") == 0)
320
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productUri, NULL);
321
0
            else if(strcmp(field_str, "manufacturerName") == 0)
322
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->manufacturerName, NULL);
323
0
            else if(strcmp(field_str, "productName") == 0)
324
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productName, NULL);
325
0
            else if(strcmp(field_str, "softwareVersion") == 0)
326
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->softwareVersion, NULL);
327
0
            else if(strcmp(field_str, "buildNumber") == 0)
328
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->buildNumber, NULL);
329
0
            else if(strcmp(field_str, "buildDate") == 0)
330
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DATETIME](ctx, &field->buildDate, NULL);
331
0
            else {
332
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
333
0
            }
334
0
            UA_free(field_str);
335
0
            break;
336
0
        }
337
0
        default:
338
0
            break;
339
0
        }
340
0
    }
341
0
    return UA_STATUSCODE_GOOD;
342
0
}
343
0
PARSE_JSON(ApplicationDescriptionField) {
344
0
    UA_ApplicationDescription *field = (UA_ApplicationDescription*)configField;
345
0
    cj5_token tok = nextToken(ctx);
346
0
    for(size_t j = tok.size/2; j > 0; j--) {
347
0
        tok = nextToken(ctx);
348
0
        switch (tok.type) {
349
0
        case CJ5_TOKEN_STRING: {
350
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
351
0
            unsigned int str_len = 0;
352
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
353
0
            if(strcmp(field_str, "applicationUri") == 0)
354
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->applicationUri, NULL);
355
0
            else if(strcmp(field_str, "productUri") == 0)
356
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productUri, NULL);
357
0
            else if(strcmp(field_str, "applicationName") == 0)
358
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_LOCALIZEDTEXT](ctx, &field->applicationName, NULL);
359
0
            else if(strcmp(field_str, "applicationType") == 0)
360
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONTYPE](ctx, &field->applicationType, NULL);
361
0
            else if(strcmp(field_str, "gatewayServerUri") == 0)
362
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->gatewayServerUri, NULL);
363
0
            else if(strcmp(field_str, "discoveryProfileUri") == 0)
364
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->discoveryProfileUri, NULL);
365
0
            else if(strcmp(field_str, "discoveryUrls") == 0)
366
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &field->discoveryUrls, &field->discoveryUrlsSize);
367
0
            else {
368
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
369
0
            }
370
0
            UA_free(field_str);
371
0
            break;
372
0
        }
373
0
        default:
374
0
            break;
375
0
        }
376
0
    }
377
0
    return UA_STATUSCODE_GOOD;
378
0
}
379
19
PARSE_JSON(StringArrayField) {
380
19
    if(configFieldSize == NULL) {
381
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Pointer to the array size is not set.");
382
0
        return UA_STATUSCODE_BADARGUMENTSMISSING;
383
0
    }
384
19
    cj5_token tok = nextToken(ctx);
385
19
    UA_String *stringArray = (UA_String*)UA_malloc(sizeof(UA_String) * tok.size);
386
19
    size_t stringArraySize = 0;
387
37.1k
    for(size_t j = tok.size; j > 0; j--) {
388
37.1k
        UA_String out = {.length = 0, .data = NULL};
389
37.1k
        parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
390
37.1k
        UA_String_copy(&out, &stringArray[stringArraySize++]);
391
37.1k
        UA_String_clear(&out);
392
37.1k
    }
393
    /* Add to the config */
394
19
    UA_String **field = (UA_String**)configField;
395
19
    if(*configFieldSize > 0) {
396
19
        UA_Array_delete(*field, *configFieldSize,
397
19
                        &UA_TYPES[UA_TYPES_STRING]);
398
19
        *field = NULL;
399
19
        *configFieldSize = 0;
400
19
    }
401
19
    UA_StatusCode retval =
402
19
        UA_Array_copy(stringArray, stringArraySize,
403
19
                      (void**)field, &UA_TYPES[UA_TYPES_STRING]);
404
19
    *configFieldSize = stringArraySize;
405
406
    /* Clean up */
407
19
    UA_Array_delete(stringArray, stringArraySize, &UA_TYPES[UA_TYPES_STRING]);
408
19
    return retval;
409
19
}
410
0
PARSE_JSON(UInt32ArrayField) {
411
0
    if(configFieldSize == NULL) {
412
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Pointer to the array size is not set.");
413
0
        return UA_STATUSCODE_BADARGUMENTSMISSING;
414
0
    }
415
0
    cj5_token tok = nextToken(ctx);
416
0
    UA_UInt32 *numberArray = (UA_UInt32*)UA_malloc(sizeof(UA_UInt32) * tok.size);
417
0
    size_t numberArraySize = 0;
418
0
    for(size_t j = tok.size; j > 0; j--) {
419
0
        UA_UInt32 value;
420
0
        UA_StatusCode retval = UInt32Field_parseJson(ctx, &value, NULL);
421
0
        if(retval != UA_STATUSCODE_GOOD)
422
0
            continue;
423
0
        numberArray[numberArraySize++] = value;
424
0
    }
425
    /* Add to the config */
426
0
    UA_UInt32 **field = (UA_UInt32**)configField;
427
0
    if(*configFieldSize > 0) {
428
0
        UA_Array_delete(*field, *configFieldSize,
429
0
                        &UA_TYPES[UA_TYPES_UINT32]);
430
0
        *field = NULL;
431
0
        *configFieldSize = 0;
432
0
    }
433
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
434
0
    if(numberArraySize > 0) {
435
0
        retval = UA_Array_copy(numberArray, numberArraySize,
436
0
                          (void **)field, &UA_TYPES[UA_TYPES_UINT32]);
437
0
        *configFieldSize = numberArraySize;
438
0
    }
439
    /* Clean up */
440
0
    UA_Array_delete(numberArray, numberArraySize, &UA_TYPES[UA_TYPES_UINT32]);
441
0
    return retval;
442
0
}
443
1
PARSE_JSON(DateTimeField) {
444
1
    cj5_token tok = nextToken(ctx);
445
1
    UA_ByteString buf = getJsonPart(tok, ctx->json);
446
1
    UA_DateTime out;
447
1
    UA_DateTime_init(&out);
448
1
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DATETIME], NULL);
449
1
    if(retval != UA_STATUSCODE_GOOD)
450
1
        return retval;
451
0
    UA_DateTime *field = (UA_DateTime*)configField;
452
0
    *field = out;
453
0
    return retval;
454
1
}
455
456
8
PARSE_JSON(MdnsConfigurationField) {
457
8
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
458
8
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
459
8
    cj5_token tok = nextToken(ctx);
460
29
    for(size_t j = tok.size/2; j > 0; j--) {
461
21
        tok = nextToken(ctx);
462
21
        switch (tok.type) {
463
7
        case CJ5_TOKEN_STRING: {
464
7
            char *field_str = (char*)UA_malloc(tok.size + 1);
465
7
            unsigned int str_len = 0;
466
7
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
467
7
            if(strcmp(field_str, "mdnsServerName") == 0)
468
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsConfig.mdnsServerName, NULL);
469
7
            else if(strcmp(field_str, "serverCapabilities") == 0)
470
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &config->mdnsConfig.serverCapabilities, &config->mdnsConfig.serverCapabilitiesSize);
471
7
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_MDNSD
472
7
            else if(strcmp(field_str, "mdnsInterfaceIP") == 0)
473
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsInterfaceIP, NULL);
474
            /* mdnsIpAddressList and mdnsIpAddressListSize are only available if UA_HAS_GETIFADDR is not defined: */
475
# if !defined(UA_HAS_GETIFADDR)
476
            else if(strcmp(field_str, "mdnsIpAddressList") == 0)
477
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32ARRAY](ctx, &config->mdnsIpAddressList, &config->mdnsIpAddressListSize);
478
# endif
479
7
#endif
480
7
            else {
481
7
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
482
7
            }
483
7
            UA_free(field_str);
484
7
            break;
485
0
        }
486
14
        default:
487
14
        break;
488
21
        }
489
21
    }
490
8
#endif
491
8
    return UA_STATUSCODE_GOOD;
492
8
}
493
494
7
PARSE_JSON(SubscriptionConfigurationField) {
495
7
#ifdef UA_ENABLE_SUBSCRIPTIONS
496
7
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
497
7
    cj5_token tok = nextToken(ctx);
498
25.1k
    for(size_t j = tok.size/2; j > 0; j--) {
499
25.1k
        tok = nextToken(ctx);
500
25.1k
        switch (tok.type) {
501
23
        case CJ5_TOKEN_STRING: {
502
23
            char *field_str = (char*)UA_malloc(tok.size + 1);
503
23
            unsigned int str_len = 0;
504
23
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
505
23
            if(strcmp(field_str, "maxSubscriptions") == 0)
506
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxSubscriptions, NULL);
507
23
            else if(strcmp(field_str, "maxSubscriptionsPerSession") == 0)
508
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxSubscriptionsPerSession, NULL);
509
23
            else if(strcmp(field_str, "publishingIntervalLimits") == 0)
510
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATIONRANGE](ctx, &config->publishingIntervalLimits, NULL);
511
23
            else if(strcmp(field_str, "lifeTimeCountLimits") == 0)
512
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->lifeTimeCountLimits, NULL);
513
23
            else if(strcmp(field_str, "keepAliveCountLimits") == 0)
514
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->keepAliveCountLimits, NULL);
515
23
            else if(strcmp(field_str, "maxNotificationsPerPublish") == 0)
516
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxNotificationsPerPublish, NULL);
517
23
            else if(strcmp(field_str, "enableRetransmissionQueue") == 0)
518
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->enableRetransmissionQueue, NULL);
519
23
            else if(strcmp(field_str, "maxRetransmissionQueueSize") == 0)
520
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxRetransmissionQueueSize, NULL);
521
23
# ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
522
23
            else if(strcmp(field_str, "maxEventsPerNode") == 0)
523
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxEventsPerNode, NULL);
524
23
# endif
525
23
            else if(strcmp(field_str, "maxMonitoredItems") == 0)
526
1
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxMonitoredItems, NULL);
527
22
            else if(strcmp(field_str, "maxMonitoredItemsPerSubscription") == 0)
528
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxMonitoredItemsPerSubscription, NULL);
529
22
            else if(strcmp(field_str, "samplingIntervalLimits") == 0)
530
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DURATIONRANGE](ctx, &config->samplingIntervalLimits, NULL);
531
22
            else if(strcmp(field_str, "queueSizeLimits") == 0)
532
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32RANGE](ctx, &config->queueSizeLimits, NULL);
533
22
            else if(strcmp(field_str, "maxPublishReqPerSession") == 0)
534
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxPublishReqPerSession, NULL);
535
22
            else {
536
22
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
537
22
            }
538
23
            UA_free(field_str);
539
23
            break;
540
0
        }
541
25.0k
        default:
542
25.0k
            break;
543
25.1k
        }
544
25.1k
    }
545
7
#endif
546
7
    return UA_STATUSCODE_GOOD;
547
7
}
548
549
1
PARSE_JSON(TcpConfigurationField) {
550
1
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
551
1
    cj5_token tok = nextToken(ctx);
552
3.35k
    for(size_t j = tok.size/2; j > 0; j--) {
553
3.35k
        tok = nextToken(ctx);
554
3.35k
        switch (tok.type) {
555
0
        case CJ5_TOKEN_STRING: {
556
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
557
0
            unsigned int str_len = 0;
558
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
559
0
            if(strcmp(field_str, "tcpBufSize") == 0)
560
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpBufSize, NULL);
561
0
            else if(strcmp(field_str, "tcpMaxMsgSize") == 0)
562
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxMsgSize, NULL);
563
0
            else if(strcmp(field_str, "tcpMaxChunks") == 0)
564
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxChunks, NULL);
565
0
            else {
566
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
567
0
            }
568
0
            UA_free(field_str);
569
0
            break;
570
0
        }
571
3.35k
        default:
572
3.35k
            break;
573
3.35k
        }
574
3.35k
    }
575
1
    return UA_STATUSCODE_GOOD;
576
1
}
577
578
7
PARSE_JSON(PubsubConfigurationField) {
579
7
#ifdef UA_ENABLE_PUBSUB
580
7
    UA_PubSubConfiguration *field = (UA_PubSubConfiguration*)configField;
581
7
    cj5_token tok = nextToken(ctx);
582
135k
    for(size_t j = tok.size/2; j > 0; j--) {
583
135k
        tok = nextToken(ctx);
584
135k
        switch (tok.type) {
585
2
        case CJ5_TOKEN_STRING: {
586
2
            char *field_str = (char*)UA_malloc(tok.size + 1);
587
2
            unsigned int str_len = 0;
588
2
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
589
2
            if(strcmp(field_str, "enableDeltaFrames") == 0)
590
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableDeltaFrames, NULL);
591
2
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
592
2
            else if(strcmp(field_str, "enableInformationModelMethods") == 0)
593
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableInformationModelMethods, NULL);
594
2
#endif
595
2
            else {
596
2
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
597
2
            }
598
2
            UA_free(field_str);
599
2
            break;
600
0
        }
601
135k
        default:
602
135k
            break;
603
135k
        }
604
135k
    }
605
7
#endif
606
7
    return UA_STATUSCODE_GOOD;
607
7
}
608
609
137
PARSE_JSON(HistorizingConfigurationField) {
610
137
#ifdef UA_ENABLE_HISTORIZING
611
137
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
612
137
    cj5_token tok = nextToken(ctx);
613
2.39k
    for(size_t j = tok.size/2; j > 0; j--) {
614
2.25k
        tok = nextToken(ctx);
615
2.25k
        switch (tok.type) {
616
136
        case CJ5_TOKEN_STRING: {
617
136
            char *field_str = (char*)UA_malloc(tok.size + 1);
618
136
            unsigned int str_len = 0;
619
136
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
620
136
            if(strcmp(field_str, "accessHistoryDataCapability") == 0)
621
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryDataCapability, NULL);
622
136
            else if(strcmp(field_str, "maxReturnDataValues") == 0)
623
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnDataValues, NULL);
624
136
            else if(strcmp(field_str, "accessHistoryEventsCapability") == 0)
625
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryEventsCapability, NULL);
626
136
            else if(strcmp(field_str, "maxReturnEventValues") == 0)
627
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnEventValues, NULL);
628
136
            else if(strcmp(field_str, "insertDataCapability") == 0)
629
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertDataCapability, NULL);
630
136
            else if(strcmp(field_str, "insertEventCapability") == 0)
631
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertEventCapability, NULL);
632
136
            else if(strcmp(field_str, "insertAnnotationsCapability") == 0)
633
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertAnnotationsCapability, NULL);
634
136
            else if(strcmp(field_str, "replaceDataCapability") == 0)
635
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceDataCapability, NULL);
636
136
            else if(strcmp(field_str, "replaceEventCapability") == 0)
637
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceEventCapability, NULL);
638
136
            else if(strcmp(field_str, "updateDataCapability") == 0)
639
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateDataCapability, NULL);
640
136
            else if(strcmp(field_str, "updateEventCapability") == 0)
641
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateEventCapability, NULL);
642
136
            else if(strcmp(field_str, "deleteRawCapability") == 0)
643
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteRawCapability, NULL);
644
136
            else if(strcmp(field_str, "deleteEventCapability") == 0)
645
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteEventCapability, NULL);
646
136
            else if(strcmp(field_str, "deleteAtTimeDataCapability") == 0)
647
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteAtTimeDataCapability, NULL);
648
136
            else {
649
136
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
650
136
            }
651
136
            UA_free(field_str);
652
136
            break;
653
0
        }
654
2.11k
        default:
655
2.11k
            break;
656
2.25k
        }
657
2.25k
    }
658
137
#endif
659
137
    return UA_STATUSCODE_GOOD;
660
137
}
661
662
15
PARSE_JSON(SecurityPolciesField) {
663
15
#ifdef UA_ENABLE_ENCRYPTION
664
15
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
665
666
15
    UA_String noneuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
667
15
    UA_String basic128Rsa15uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
668
15
    UA_String basic256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256");
669
15
    UA_String basic256Sha256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
670
15
    UA_String aes128sha256rsaoaepuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
671
15
    UA_String aes256sha256rsapssuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss");
672
673
15
    cj5_token tok = nextToken(ctx);
674
15
    for(size_t j = tok.size; j > 0; j--) {
675
676
15
        UA_String policy = {.length = 0, .data = NULL};
677
15
        UA_ByteString certificate = {.length = 0, .data = NULL};
678
15
        UA_ByteString privateKey = {.length = 0, .data = NULL};
679
680
15
        tok = nextToken(ctx);
681
8.71k
        for(size_t i = tok.size / 2; i > 0; i--) {
682
8.70k
            tok = nextToken(ctx);
683
8.70k
            switch(tok.type) {
684
1.18k
            case CJ5_TOKEN_STRING: {
685
1.18k
                char *field_str = (char *)UA_malloc(tok.size + 1);
686
1.18k
                unsigned int str_len = 0;
687
1.18k
                cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
688
1.18k
                if(strcmp(field_str, "certificate") == 0) {
689
0
                    UA_String out = {.length = 0, .data = NULL};
690
0
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
691
692
0
                    if(out.length > 0) {
693
0
                        char *certfile = (char *)UA_malloc(out.length + 1);
694
0
                        memcpy(certfile, out.data, out.length);
695
0
                        certfile[out.length] = '\0';
696
0
                        certificate = loadCertificateFile(certfile);
697
0
                        UA_String_clear(&out);
698
0
                        UA_free(certfile);
699
0
                    }
700
1.18k
                } else if(strcmp(field_str, "privateKey") == 0) {
701
148
                    UA_String out = {.length = 0, .data = NULL};
702
148
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
703
704
148
                    if(out.length > 0) {
705
0
                        char *keyfile = (char *)UA_malloc(out.length + 1);
706
0
                        memcpy(keyfile, out.data, out.length);
707
0
                        keyfile[out.length] = '\0';
708
0
                        privateKey = loadCertificateFile(keyfile);
709
0
                        UA_String_clear(&out);
710
0
                        UA_free(keyfile);
711
0
                    }
712
1.04k
                } else if(strcmp(field_str, "policy") == 0) {
713
64
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &policy, NULL);
714
977
                } else {
715
977
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
716
977
                }
717
1.18k
                UA_free(field_str);
718
1.18k
                break;
719
0
            }
720
7.51k
            default:
721
7.51k
                break;
722
8.70k
            }
723
8.70k
        }
724
725
15
        if(certificate.length == 0 || privateKey.length == 0) {
726
15
            UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
727
15
                           "Certificate and PrivateKey must be set for every policy.");
728
15
            if(policy.length > 0)
729
0
                UA_String_clear(&policy);
730
15
            if(certificate.length > 0)
731
0
                UA_ByteString_clear(&certificate);
732
15
            if(privateKey.length > 0)
733
0
                UA_ByteString_clear(&privateKey);
734
15
            return UA_STATUSCODE_BADINTERNALERROR;
735
15
        }
736
0
        UA_StatusCode retval = UA_STATUSCODE_GOOD;
737
0
        if(UA_String_equal(&policy, &noneuri)) {
738
            /* Nothing to do! */
739
0
        } else if(UA_String_equal(&policy, &basic128Rsa15uri)) {
740
0
            retval = UA_ServerConfig_addSecurityPolicyBasic128Rsa15(config, &certificate, &privateKey);
741
0
            if(retval != UA_STATUSCODE_GOOD) {
742
0
                UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
743
0
                               "Could not add SecurityPolicy#Basic128Rsa15 with error code %s",
744
0
                               UA_StatusCode_name(retval));
745
0
            }
746
0
        } else if(UA_String_equal(&policy, &basic256uri)) {
747
0
            retval = UA_ServerConfig_addSecurityPolicyBasic256(config, &certificate, &privateKey);
748
0
            if(retval != UA_STATUSCODE_GOOD) {
749
0
                UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
750
0
                               "Could not add SecurityPolicy#Basic256 with error code %s",
751
0
                               UA_StatusCode_name(retval));
752
0
            }
753
0
        } else if(UA_String_equal(&policy, &basic256Sha256uri)) {
754
0
            retval = UA_ServerConfig_addSecurityPolicyBasic256Sha256(config, &certificate, &privateKey);
755
0
            if(retval != UA_STATUSCODE_GOOD) {
756
0
                UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
757
0
                               "Could not add SecurityPolicy#Basic256Sha256 with error code %s",
758
0
                               UA_StatusCode_name(retval));
759
0
            }
760
0
        } else if(UA_String_equal(&policy, &aes128sha256rsaoaepuri)) {
761
0
            retval = UA_ServerConfig_addSecurityPolicyAes128Sha256RsaOaep(config, &certificate, &privateKey);
762
0
            if(retval != UA_STATUSCODE_GOOD) {
763
0
                UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
764
0
                               "Could not add SecurityPolicy#Aes128Sha256RsaOaep with error code %s",
765
0
                               UA_StatusCode_name(retval));
766
0
            }
767
0
        } else if(UA_String_equal(&policy, &aes256sha256rsapssuri)) {
768
0
             retval = UA_ServerConfig_addSecurityPolicyAes256Sha256RsaPss(config, &certificate, &privateKey);
769
0
             if(retval != UA_STATUSCODE_GOOD) {
770
0
                 UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
771
0
                                "Could not add SecurityPolicy#Aes256Sha256RsaPss with error code %s",
772
0
                               UA_StatusCode_name(retval));
773
0
            }
774
0
        } else {
775
0
            UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown Security Policy.");
776
0
        }
777
778
        /* Add all Endpoints */
779
0
        UA_ServerConfig_addAllEndpoints(config);
780
781
0
        if(policy.length > 0)
782
0
            UA_String_clear(&policy);
783
0
        if(certificate.length > 0)
784
0
            UA_ByteString_clear(&certificate);
785
0
        if(privateKey.length > 0)
786
0
            UA_ByteString_clear(&privateKey);
787
0
    }
788
0
#endif
789
0
    return UA_STATUSCODE_GOOD;
790
15
}
791
792
2.15k
PARSE_JSON(SecurityPkiField) {
793
2.15k
#ifdef UA_ENABLE_ENCRYPTION
794
2.15k
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
795
2.15k
    UA_String pkiFolder = {.length = 0, .data = NULL};
796
797
2.15k
    cj5_token tok = nextToken(ctx);
798
2.15k
    UA_ByteString buf = getJsonPart(tok, ctx->json);
799
2.15k
    UA_StatusCode retval = UA_decodeJson(&buf, &pkiFolder, &UA_TYPES[UA_TYPES_STRING], NULL);
800
2.15k
    if(retval != UA_STATUSCODE_GOOD)
801
12
        return retval;
802
803
2.13k
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || defined(__APPLE__) || defined(__OpenBSD__)
804
    /* Set up the parameters for the filestore certificate store */
805
2.13k
    UA_KeyValuePair params[2];
806
2.13k
    size_t paramsSize = 2;
807
808
2.13k
    params[0].key = UA_QUALIFIEDNAME(0, "max-trust-listsize");
809
2.13k
    UA_Variant_setScalar(&params[0].value, &config->maxTrustListSize, &UA_TYPES[UA_TYPES_UINT32]);
810
2.13k
    params[1].key = UA_QUALIFIEDNAME(0, "max-rejected-listsize");
811
2.13k
    UA_Variant_setScalar(&params[1].value, &config->maxRejectedListSize, &UA_TYPES[UA_TYPES_UINT32]);
812
813
2.13k
    UA_KeyValueMap paramsMap;
814
2.13k
    paramsMap.map = params;
815
2.13k
    paramsMap.mapSize = paramsSize;
816
817
    /* set server config field */
818
2.13k
    UA_NodeId defaultApplicationGroup =
819
2.13k
           UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
820
2.13k
    retval = UA_CertificateGroup_Filestore(&config->secureChannelPKI, &defaultApplicationGroup,
821
2.13k
                                           pkiFolder, config->logging, &paramsMap);
822
2.13k
    if(retval != UA_STATUSCODE_GOOD) {
823
50
        UA_String_clear(&pkiFolder);
824
50
        return retval;
825
50
    }
826
827
2.08k
    UA_NodeId defaultUserTokenGroup =
828
2.08k
            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
829
2.08k
    retval = UA_CertificateGroup_Filestore(&config->sessionPKI, &defaultUserTokenGroup,
830
2.08k
                                            pkiFolder, config->logging, &paramsMap);
831
2.08k
    if(retval != UA_STATUSCODE_GOOD) {
832
5
        UA_String_clear(&pkiFolder);
833
5
        return retval;
834
5
    }
835
836
    /* Clean up */
837
2.08k
    UA_String_clear(&pkiFolder);
838
#else
839
    (void)config;
840
    UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
841
                   "pkiFolder is not supported on this platform. "
842
                   "Trusted clients will not be verified.");
843
#endif
844
#else
845
    UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
846
                   "pkiFolder is set in the config but UA_ENABLE_ENCRYPTION "
847
                   "is not enabled. Trusted clients will not be verified.");
848
#endif
849
2.08k
    return UA_STATUSCODE_GOOD;
850
2.08k
}
851
852
/*----------------------Enumerations------------------------*/
853
0
PARSE_JSON(ApplicationTypeField) {
854
0
    UA_UInt32 enum_value;
855
0
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
856
0
    if(retval != UA_STATUSCODE_GOOD)
857
0
        return retval;
858
0
    UA_ApplicationType *field = (UA_ApplicationType*)configField;
859
0
    *field = (UA_ApplicationType)enum_value;
860
0
    return retval;
861
0
}
862
57
PARSE_JSON(RuleHandlingField) {
863
57
    UA_UInt32 enum_value;
864
57
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
865
57
    if(retval != UA_STATUSCODE_GOOD)
866
43
        return retval;
867
14
    UA_RuleHandling *field = (UA_RuleHandling*)configField;
868
14
    *field = (UA_RuleHandling)enum_value;
869
14
    return retval;
870
57
}
871
872
const parseJsonSignature parseJsonJumpTable[UA_SERVERCONFIGFIELDKINDS] = {
873
    /* Basic Types */
874
    (parseJsonSignature)Int64Field_parseJson,
875
    (parseJsonSignature)UInt16Field_parseJson,
876
    (parseJsonSignature)UInt32Field_parseJson,
877
    (parseJsonSignature)UInt64Field_parseJson,
878
    (parseJsonSignature)StringField_parseJson,
879
    (parseJsonSignature)LocalizedTextField_parseJson,
880
    (parseJsonSignature)DoubleField_parseJson,
881
    (parseJsonSignature)BooleanField_parseJson,
882
    (parseJsonSignature)DurationField_parseJson,
883
    (parseJsonSignature)DurationRangeField_parseJson,
884
    (parseJsonSignature)UInt32RangeField_parseJson,
885
886
    /* Advanced Types */
887
    (parseJsonSignature)BuildInfo_parseJson,
888
    (parseJsonSignature)ApplicationDescriptionField_parseJson,
889
    (parseJsonSignature)StringArrayField_parseJson,
890
    (parseJsonSignature)UInt32ArrayField_parseJson,
891
    (parseJsonSignature)DateTimeField_parseJson,
892
    (parseJsonSignature)SubscriptionConfigurationField_parseJson,
893
    (parseJsonSignature)TcpConfigurationField_parseJson,
894
    (parseJsonSignature)PubsubConfigurationField_parseJson,
895
    (parseJsonSignature)HistorizingConfigurationField_parseJson,
896
    (parseJsonSignature)MdnsConfigurationField_parseJson,
897
    (parseJsonSignature)SecurityPolciesField_parseJson,
898
    (parseJsonSignature)SecurityPkiField_parseJson,
899
900
    /* Enumerations */
901
    (parseJsonSignature)ApplicationTypeField_parseJson,
902
    (parseJsonSignature)RuleHandlingField_parseJson,
903
};
904
905
/* Skips unknown item (simple, object or array) in config file. 
906
* Unknown items may happen if we don't support some features. 
907
* E.g. if  UA_ENABLE_ENCRYPTION is not defined and config file 
908
* contains "securityPolicies" entry.
909
*/
910
static void
911
63
skipUnknownItem(ParsingCtx* ctx) {
912
63
    unsigned int end = ctx->tokens[ctx->index].end;
913
119
    do {
914
119
        ctx->index++;
915
119
    } while (ctx->index < ctx->tokensSize &&
916
96
        ctx->tokens[ctx->index].start < end);
917
63
}
918
919
static UA_StatusCode
920
182
parseJSONConfig(UA_ServerConfig *config, UA_ByteString json_config) {
921
    // Parsing json config
922
182
    const char *json = (const char*)json_config.data;
923
182
    cj5_token tokens[MAX_TOKENS];
924
182
    cj5_result r = cj5_parse(json, (unsigned int)json_config.length, tokens, MAX_TOKENS, NULL);
925
926
    /* Validate the parse result: must succeed and produce a root object
927
     * with at least one key-value pair (i.e. >= 2 tokens). */
928
182
    if(r.error != CJ5_ERROR_NONE || r.num_tokens < 2 ||
929
83
       r.tokens[0].type != CJ5_TOKEN_OBJECT)
930
99
        return UA_STATUSCODE_BADDECODINGERROR;
931
932
83
    ParsingCtx ctx;
933
83
    ctx.json = json;
934
83
    ctx.result = r;
935
83
    ctx.tokens = r.tokens;
936
83
    ctx.tokensSize = r.num_tokens;
937
83
    ctx.index = 1; // The first token is ignored because it is known and not needed.
938
939
83
    ctx.logging = config->logging;
940
941
    /* Buffer for the field name */
942
83
    char field[256];
943
944
83
    size_t serverConfigSize = 0;
945
83
    if(ctx.tokens)
946
83
        serverConfigSize = (ctx.tokens[ctx.index-1].size/2);
947
83
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
948
660
    for (size_t j = serverConfigSize; j > 0 && ctx.index < ctx.tokensSize; j--) {
949
598
        cj5_token tok = ctx.tokens[ctx.index];
950
598
        switch (tok.type) {
951
590
            case CJ5_TOKEN_STRING: {
952
590
                if(tok.size >= 255) {
953
27
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
954
27
                                   "Configuration field name too long");
955
27
                    continue;
956
27
                }
957
563
                unsigned int str_len = 0;
958
563
                cj5_error_code res = cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
959
563
                if(res != CJ5_ERROR_NONE) {
960
300
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
961
300
                                   "Configuration field name not a valid string");
962
300
                    continue;
963
300
                }
964
263
                if(strcmp(field, "buildInfo") == 0)
965
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BUILDINFO](&ctx, &config->buildInfo, NULL);
966
263
                else if(strcmp(field, "applicationDescription") == 0)
967
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION](&ctx, &config->applicationDescription, NULL);
968
263
                else if(strcmp(field, "shutdownDelay") == 0)
969
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->shutdownDelay, NULL);
970
263
                else if(strcmp(field, "verifyRequestTimestamp") == 0)
971
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->verifyRequestTimestamp, NULL);
972
263
                else if(strcmp(field, "allowEmptyVariables") == 0)
973
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->allowEmptyVariables, NULL);
974
263
                else if(strcmp(field, "serverUrls") == 0)
975
19
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](&ctx, &config->serverUrls, &config->serverUrlsSize);
976
244
                else if(strcmp(field, "tcpEnabled") == 0)
977
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->tcpEnabled, NULL);
978
244
                else if(strcmp(field, "tcp") == 0)
979
1
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_TCPCONFIGURATION](&ctx, config, NULL);
980
243
                else if(strcmp(field, "securityPolicyNoneDiscoveryOnly") == 0)
981
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->securityPolicyNoneDiscoveryOnly, NULL);
982
243
                else if(strcmp(field, "modellingRulesOnInstances") == 0)
983
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->modellingRulesOnInstances, NULL);
984
243
                else if(strcmp(field, "maxSecureChannels") == 0)
985
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSecureChannels, NULL);
986
243
                else if(strcmp(field, "maxSecurityTokenLifetime") == 0)
987
3
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxSecurityTokenLifetime, NULL);
988
240
                else if(strcmp(field, "maxSessions") == 0)
989
3
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSessions, NULL);
990
237
                else if(strcmp(field, "maxSessionTimeout") == 0)
991
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->maxSessionTimeout, NULL);
992
237
                else if(strcmp(field, "maxNodesPerRead") == 0)
993
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRead, NULL);
994
237
                else if(strcmp(field, "maxNodesPerWrite") == 0)
995
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerWrite, NULL);
996
237
                else if(strcmp(field, "maxNodesPerMethodCall") == 0)
997
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerMethodCall, NULL);
998
237
                else if(strcmp(field, "maxNodesPerBrowse") == 0)
999
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerBrowse, NULL);
1000
237
                else if(strcmp(field, "maxNodesPerRegisterNodes") == 0)
1001
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRegisterNodes, NULL);
1002
237
                else if(strcmp(field, "maxNodesPerTranslateBrowsePathsToNodeIds") == 0)
1003
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerTranslateBrowsePathsToNodeIds, NULL);
1004
237
                else if(strcmp(field, "maxNodesPerNodeManagement") == 0)
1005
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerNodeManagement, NULL);
1006
237
                else if(strcmp(field, "maxMonitoredItemsPerCall") == 0)
1007
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxMonitoredItemsPerCall, NULL);
1008
237
                else if(strcmp(field, "maxReferencesPerNode") == 0)
1009
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxReferencesPerNode, NULL);
1010
237
                else if(strcmp(field, "reverseReconnectInterval") == 0)
1011
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->reverseReconnectInterval, NULL);
1012
1013
237
#if UA_MULTITHREADING >= 100
1014
237
                else if(strcmp(field, "asyncOperationTimeout") == 0)
1015
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->asyncOperationTimeout, NULL);
1016
237
                else if(strcmp(field, "maxAsyncOperationQueueSize") == 0)
1017
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT64](&ctx, &config->maxAsyncOperationQueueSize, NULL);
1018
237
#endif
1019
1020
237
#ifdef UA_ENABLE_DISCOVERY
1021
237
                else if(strcmp(field, "discoveryCleanupTimeout") == 0)
1022
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->discoveryCleanupTimeout, NULL);
1023
237
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
1024
237
                else if(strcmp(field, "mdnsEnabled") == 0)
1025
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->mdnsEnabled, NULL);
1026
237
                else if(strcmp(field, "mdns") == 0)
1027
8
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_MDNSCONFIGURATION](&ctx, config, NULL);
1028
229
#endif
1029
229
#endif
1030
1031
229
#ifdef UA_ENABLE_SUBSCRIPTIONS
1032
229
                else if(strcmp(field, "subscriptionsEnabled") == 0)
1033
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->subscriptionsEnabled, NULL);
1034
229
                else if(strcmp(field, "subscriptions") == 0)
1035
7
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION](&ctx, config, NULL);
1036
222
# endif
1037
1038
222
#ifdef UA_ENABLE_HISTORIZING
1039
222
                else if(strcmp(field, "historizingEnabled") == 0)
1040
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->historizingEnabled, NULL);
1041
222
                else if(strcmp(field, "historizing") == 0)
1042
137
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION](&ctx, config, NULL);
1043
85
#endif
1044
1045
85
#ifdef UA_ENABLE_PUBSUB
1046
85
                else if(strcmp(field, "pubsubEnabled") == 0)
1047
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->pubsubEnabled, NULL);
1048
85
                else if(strcmp(field, "pubsub") == 0)
1049
7
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION](&ctx, &config->pubSubConfig, NULL);
1050
78
#endif
1051
78
#ifdef UA_ENABLE_ENCRYPTION
1052
78
                else if(strcmp(field, "securityPolicies") == 0)
1053
15
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPOLICIES](&ctx, config, NULL);
1054
63
                else if(strcmp(field, "pkiFolder") == 0)
1055
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPKI](&ctx, config, NULL);
1056
63
#endif
1057
63
                else {
1058
63
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1059
63
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled.", field);
1060
                    /* skip the name of item */
1061
63
                    ++ctx.index;
1062
                    /* skip value of unknown item */
1063
63
                    skipUnknownItem(&ctx);
1064
                    /* after skipUnknownItem() ctx->index points to the name of the following item.
1065
                       We must decrement index in oder following increment will
1066
                       still set index to the right position (name of the following item) */
1067
63
                    --ctx.index;
1068
63
                }
1069
263
                if(retval != UA_STATUSCODE_GOOD) {
1070
21
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1071
21
                                 "An error occurred while parsing the configuration field %s", field);
1072
21
                    return retval;
1073
21
                }
1074
242
                break;
1075
263
            }
1076
242
            default:
1077
8
                break;
1078
598
        }
1079
250
        ctx.index += 1;
1080
250
    }
1081
62
    return retval;
1082
83
}
1083
1084
UA_Server *
1085
6.61k
UA_Server_newFromFile(const UA_ByteString json_config) {
1086
6.61k
    UA_ServerConfig config;
1087
6.61k
    memset(&config, 0, sizeof(UA_ServerConfig));
1088
6.61k
    UA_StatusCode res = UA_ServerConfig_setDefault(&config);
1089
6.61k
    res |= parseJSONConfig(&config, json_config);
1090
6.61k
    if(res != UA_STATUSCODE_GOOD) {
1091
1.13k
        UA_ServerConfig_clear(&config);
1092
1.13k
        return NULL;
1093
1.13k
    }
1094
5.48k
    return UA_Server_newWithConfig(&config);
1095
6.61k
}
1096
1097
UA_StatusCode
1098
0
UA_ServerConfig_updateFromFile(UA_ServerConfig *config, const UA_ByteString json_config) {
1099
0
    UA_StatusCode res = parseJSONConfig(config, json_config);
1100
0
    return res;
1101
0
}
1102
1103
#ifdef UA_ENABLE_ENCRYPTION
1104
static UA_ByteString
1105
loadCertificateFile(const char *const path) {
1106
    UA_ByteString fileContents = UA_BYTESTRING_NULL;
1107
1108
    /* Open the file */
1109
    FILE *fp = fopen(path, "rb");
1110
    if(!fp) {
1111
        errno = 0; /* We read errno also from the tcp layer... */
1112
        return fileContents;
1113
    }
1114
1115
    /* Get the file length, allocate the data and read */
1116
    if(fseek(fp, 0, SEEK_END) != 0) {
1117
        fclose(fp);
1118
        errno = 0;
1119
        return fileContents;
1120
    }
1121
1122
    long length = ftell(fp);
1123
    if(length < 0) {
1124
        fclose(fp);
1125
        errno = 0;
1126
        return fileContents;
1127
    }
1128
1129
    fileContents.length = (size_t)length;
1130
    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
1131
    if(fileContents.data) {
1132
        if(fseek(fp, 0, SEEK_SET) != 0) {
1133
            fclose(fp);
1134
            UA_ByteString_clear(&fileContents);
1135
            errno = 0;
1136
            return fileContents;
1137
        }
1138
        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
1139
        if(read != fileContents.length)
1140
            UA_ByteString_clear(&fileContents);
1141
    } else {
1142
        fileContents.length = 0;
1143
    }
1144
    fclose(fp);
1145
1146
    return fileContents;
1147
}
1148
#endif