Coverage Report

Created: 2026-10-05 06:46

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
173
#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
42.2k
getJsonPart(cj5_token tok, const char *json) {
31
42.2k
    UA_ByteString bs;
32
42.2k
    UA_ByteString_init(&bs);
33
42.2k
    if(tok.type == CJ5_TOKEN_STRING) {
34
3.74k
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start - 1);
35
3.74k
        bs.length = (tok.end - tok.start) + 3;
36
3.74k
        return bs;
37
38.4k
    } else {
38
38.4k
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start);
39
38.4k
        bs.length = (tok.end - tok.start) + 1;
40
38.4k
        return bs;
41
38.4k
    }
42
42.2k
}
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
458k
nextToken(ParsingCtx *ctx) {
50
458k
    if(!ctx->tokens || ctx->index + 1 >= ctx->tokensSize) {
51
428k
        ctx->index = ctx->tokensSize;
52
428k
        cj5_token empty;
53
428k
        memset(&empty, 0, sizeof(empty));
54
428k
        return empty;
55
428k
    }
56
29.3k
    ctx->index++;
57
29.3k
    return ctx->tokens[ctx->index];
58
458k
}
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
124
PARSE_JSON(UInt16Field) {
123
124
    cj5_token tok = nextToken(ctx);
124
124
    UA_ByteString buf = getJsonPart(tok, ctx->json);
125
124
    UA_UInt16 out;
126
124
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT16], NULL);
127
124
    if(retval != UA_STATUSCODE_GOOD)
128
102
        return retval;
129
22
    UA_UInt16 *field = (UA_UInt16*)configField;
130
22
    *field = out;
131
22
    return retval;
132
124
}
133
519
PARSE_JSON(UInt32Field) {
134
519
    cj5_token tok = nextToken(ctx);
135
519
    UA_ByteString buf = getJsonPart(tok, ctx->json);
136
519
    UA_UInt32 out;
137
519
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
138
519
    if(retval != UA_STATUSCODE_GOOD)
139
118
        return retval;
140
401
    UA_UInt32 *field = (UA_UInt32*)configField;
141
401
    *field = out;
142
401
    return retval;
143
519
}
144
28
PARSE_JSON(UInt64Field) {
145
28
    cj5_token tok = nextToken(ctx);
146
28
    UA_ByteString buf = getJsonPart(tok, ctx->json);
147
28
    UA_UInt64 out;
148
28
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT64], NULL);
149
28
    if(retval != UA_STATUSCODE_GOOD)
150
19
        return retval;
151
9
    UA_UInt64 *field = (UA_UInt64*)configField;
152
9
    *field = out;
153
9
    return retval;
154
28
}
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.1k
        return retval;
162
460
    UA_String *field = (UA_String*)configField;
163
460
    if(field != NULL) {
164
460
        UA_String_clear(field);
165
460
        *field = out;
166
460
    }
167
460
    return retval;
168
37.6k
}
169
61
PARSE_JSON(LocalizedTextField) {
170
    /*
171
     applicationName: {
172
        locale: "de-DE",
173
        text: "Test text"
174
    }
175
     */
176
61
    cj5_token tok = nextToken(ctx);
177
61
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
178
61
    UA_String locale = {.length = 0, .data = NULL};
179
61
    UA_String text = {.length = 0, .data = NULL};
180
625
    for(size_t j = tok.size/2; j > 0; j--) {
181
564
        tok = nextToken(ctx);
182
564
        switch (tok.type) {
183
356
        case CJ5_TOKEN_STRING: {
184
356
            char *field = (char*)UA_malloc(tok.size + 1);
185
356
            unsigned int str_len = 0;
186
356
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field, &str_len);
187
188
356
            tok = nextToken(ctx);
189
356
            UA_ByteString buf = getJsonPart(tok, ctx->json);
190
356
            if(strcmp(field, "locale") == 0)
191
0
                retval |= UA_decodeJson(&buf, &locale, &UA_TYPES[UA_TYPES_STRING], NULL);
192
356
            else if(strcmp(field, "text") == 0)
193
0
                retval |= UA_decodeJson(&buf, &text, &UA_TYPES[UA_TYPES_STRING], NULL);
194
356
            else {
195
356
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
196
356
            }
197
356
            UA_free(field);
198
356
            break;
199
0
        }
200
208
        default:
201
208
            break;
202
564
        }
203
564
    }
204
61
    UA_LocalizedText out;
205
61
    out.locale = locale;
206
61
    out.text = text;
207
61
    if(retval != UA_STATUSCODE_GOOD)
208
0
        return retval;
209
61
    UA_LocalizedText *field = (UA_LocalizedText*)configField;
210
61
    if(field != NULL) {
211
61
        UA_LocalizedText_clear(field);
212
61
        *field = out;
213
61
    }
214
61
    return retval;
215
61
}
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
54
        return retval;
223
244
    UA_Double *field = (UA_Double *)configField;
224
244
    *field = out;
225
244
    return retval;
226
298
}
227
20
PARSE_JSON(BooleanField) {
228
20
    cj5_token tok = nextToken(ctx);
229
20
    UA_ByteString buf = getJsonPart(tok, ctx->json);
230
20
    UA_Boolean out;
231
20
    if(tok.type != CJ5_TOKEN_BOOL) {
232
20
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Value of type bool expected.");
233
20
        return UA_STATUSCODE_BADTYPEMISMATCH;
234
20
    }
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
20
}
246
148
PARSE_JSON(DurationField) {
247
148
    UA_Double double_value;
248
148
    UA_StatusCode retval = DoubleField_parseJson(ctx, &double_value, NULL);
249
148
    if(retval != UA_STATUSCODE_GOOD)
250
18
        return retval;
251
130
    UA_Duration *field = (UA_Duration*)configField;
252
130
    *field = (UA_Duration)double_value;
253
130
    return retval;
254
148
}
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
20
PARSE_JSON(StringArrayField) {
380
20
    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
20
    cj5_token tok = nextToken(ctx);
385
20
    UA_String *stringArray = (UA_String*)UA_malloc(sizeof(UA_String) * tok.size);
386
20
    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
20
    UA_String **field = (UA_String**)configField;
395
20
    if(*configFieldSize > 0) {
396
20
        UA_Array_delete(*field, *configFieldSize,
397
20
                        &UA_TYPES[UA_TYPES_STRING]);
398
20
        *field = NULL;
399
20
        *configFieldSize = 0;
400
20
    }
401
20
    UA_StatusCode retval =
402
20
        UA_Array_copy(stringArray, stringArraySize,
403
20
                      (void**)field, &UA_TYPES[UA_TYPES_STRING]);
404
20
    *configFieldSize = stringArraySize;
405
406
    /* Clean up */
407
20
    UA_Array_delete(stringArray, stringArraySize, &UA_TYPES[UA_TYPES_STRING]);
408
20
    return retval;
409
20
}
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
12
PARSE_JSON(MdnsConfigurationField) {
457
12
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
458
12
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
459
12
    cj5_token tok = nextToken(ctx);
460
37
    for(size_t j = tok.size/2; j > 0; j--) {
461
25
        tok = nextToken(ctx);
462
25
        switch (tok.type) {
463
11
        case CJ5_TOKEN_STRING: {
464
11
            char *field_str = (char*)UA_malloc(tok.size + 1);
465
11
            unsigned int str_len = 0;
466
11
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
467
11
            if(strcmp(field_str, "mdnsServerName") == 0)
468
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsConfig.mdnsServerName, NULL);
469
11
            else if(strcmp(field_str, "serverCapabilities") == 0)
470
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &config->mdnsConfig.serverCapabilities, &config->mdnsConfig.serverCapabilitiesSize);
471
11
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_MDNSD
472
11
            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
11
#endif
480
11
            else {
481
11
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
482
11
            }
483
11
            UA_free(field_str);
484
11
            break;
485
0
        }
486
14
        default:
487
14
        break;
488
25
        }
489
25
    }
490
12
#endif
491
12
    return UA_STATUSCODE_GOOD;
492
12
}
493
494
6
PARSE_JSON(SubscriptionConfigurationField) {
495
6
#ifdef UA_ENABLE_SUBSCRIPTIONS
496
6
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
497
6
    cj5_token tok = nextToken(ctx);
498
24.3k
    for(size_t j = tok.size/2; j > 0; j--) {
499
24.3k
        tok = nextToken(ctx);
500
24.3k
        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
24.3k
        default:
542
24.3k
            break;
543
24.3k
        }
544
24.3k
    }
545
6
#endif
546
6
    return UA_STATUSCODE_GOOD;
547
6
}
548
549
6
PARSE_JSON(TcpConfigurationField) {
550
6
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
551
6
    cj5_token tok = nextToken(ctx);
552
84.5k
    for(size_t j = tok.size/2; j > 0; j--) {
553
84.5k
        tok = nextToken(ctx);
554
84.5k
        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
84.5k
        default:
572
84.5k
            break;
573
84.5k
        }
574
84.5k
    }
575
6
    return UA_STATUSCODE_GOOD;
576
6
}
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
192k
    for(size_t j = tok.size/2; j > 0; j--) {
583
192k
        tok = nextToken(ctx);
584
192k
        switch (tok.type) {
585
5
        case CJ5_TOKEN_STRING: {
586
5
            char *field_str = (char*)UA_malloc(tok.size + 1);
587
5
            unsigned int str_len = 0;
588
5
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
589
5
            if(strcmp(field_str, "enableDeltaFrames") == 0)
590
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableDeltaFrames, NULL);
591
5
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
592
5
            else if(strcmp(field_str, "enableInformationModelMethods") == 0)
593
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableInformationModelMethods, NULL);
594
5
#endif
595
5
            else {
596
5
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
597
5
            }
598
5
            UA_free(field_str);
599
5
            break;
600
0
        }
601
192k
        default:
602
192k
            break;
603
192k
        }
604
192k
    }
605
7
#endif
606
7
    return UA_STATUSCODE_GOOD;
607
7
}
608
609
136
PARSE_JSON(HistorizingConfigurationField) {
610
136
#ifdef UA_ENABLE_HISTORIZING
611
136
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
612
136
    cj5_token tok = nextToken(ctx);
613
408
    for(size_t j = tok.size/2; j > 0; j--) {
614
272
        tok = nextToken(ctx);
615
272
        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
136
        default:
655
136
            break;
656
272
        }
657
272
    }
658
136
#endif
659
136
    return UA_STATUSCODE_GOOD;
660
136
}
661
662
14
PARSE_JSON(SecurityPolciesField) {
663
14
#ifdef UA_ENABLE_ENCRYPTION
664
14
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
665
666
14
    UA_String noneuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
667
14
    UA_String basic128Rsa15uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
668
14
    UA_String basic256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256");
669
14
    UA_String basic256Sha256uri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
670
14
    UA_String aes128sha256rsaoaepuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
671
14
    UA_String aes256sha256rsapssuri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss");
672
673
14
    cj5_token tok = nextToken(ctx);
674
14
    for(size_t j = tok.size; j > 0; j--) {
675
676
14
        UA_String policy = {.length = 0, .data = NULL};
677
14
        UA_ByteString certificate = {.length = 0, .data = NULL};
678
14
        UA_ByteString privateKey = {.length = 0, .data = NULL};
679
680
14
        tok = nextToken(ctx);
681
68.0k
        for(size_t i = tok.size / 2; i > 0; i--) {
682
68.0k
            tok = nextToken(ctx);
683
68.0k
            switch(tok.type) {
684
700
            case CJ5_TOKEN_STRING: {
685
700
                char *field_str = (char *)UA_malloc(tok.size + 1);
686
700
                unsigned int str_len = 0;
687
700
                cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
688
700
                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
700
                } else if(strcmp(field_str, "privateKey") == 0) {
701
58
                    UA_String out = {.length = 0, .data = NULL};
702
58
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &out, NULL);
703
704
58
                    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
642
                } else if(strcmp(field_str, "policy") == 0) {
713
69
                    parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &policy, NULL);
714
573
                } else {
715
573
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
716
573
                }
717
700
                UA_free(field_str);
718
700
                break;
719
0
            }
720
67.3k
            default:
721
67.3k
                break;
722
68.0k
            }
723
68.0k
        }
724
725
14
        if(certificate.length == 0 || privateKey.length == 0) {
726
14
            UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
727
14
                           "Certificate and PrivateKey must be set for every policy.");
728
14
            if(policy.length > 0)
729
0
                UA_String_clear(&policy);
730
14
            if(certificate.length > 0)
731
0
                UA_ByteString_clear(&certificate);
732
14
            if(privateKey.length > 0)
733
0
                UA_ByteString_clear(&privateKey);
734
14
            return UA_STATUSCODE_BADINTERNALERROR;
735
14
        }
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
14
}
791
792
3.23k
PARSE_JSON(SecurityPkiField) {
793
3.23k
#ifdef UA_ENABLE_ENCRYPTION
794
3.23k
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
795
3.23k
    UA_String pkiFolder = {.length = 0, .data = NULL};
796
797
3.23k
    cj5_token tok = nextToken(ctx);
798
3.23k
    UA_ByteString buf = getJsonPart(tok, ctx->json);
799
3.23k
    UA_StatusCode retval = UA_decodeJson(&buf, &pkiFolder, &UA_TYPES[UA_TYPES_STRING], NULL);
800
3.23k
    if(retval != UA_STATUSCODE_GOOD)
801
18
        return retval;
802
803
3.21k
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || defined(__APPLE__) || defined(__OpenBSD__)
804
    /* Set up the parameters for the filestore certificate store */
805
3.21k
    UA_KeyValuePair params[2];
806
3.21k
    size_t paramsSize = 2;
807
808
3.21k
    params[0].key = UA_QUALIFIEDNAME(0, "max-trust-listsize");
809
3.21k
    UA_Variant_setScalar(&params[0].value, &config->maxTrustListSize, &UA_TYPES[UA_TYPES_UINT32]);
810
3.21k
    params[1].key = UA_QUALIFIEDNAME(0, "max-rejected-listsize");
811
3.21k
    UA_Variant_setScalar(&params[1].value, &config->maxRejectedListSize, &UA_TYPES[UA_TYPES_UINT32]);
812
813
3.21k
    UA_KeyValueMap paramsMap;
814
3.21k
    paramsMap.map = params;
815
3.21k
    paramsMap.mapSize = paramsSize;
816
817
    /* set server config field */
818
3.21k
    UA_NodeId defaultApplicationGroup =
819
3.21k
           UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
820
3.21k
    retval = UA_CertificateGroup_Filestore(&config->secureChannelPKI, &defaultApplicationGroup,
821
3.21k
                                           pkiFolder, config->logging, &paramsMap);
822
3.21k
    if(retval != UA_STATUSCODE_GOOD) {
823
58
        UA_String_clear(&pkiFolder);
824
58
        return retval;
825
58
    }
826
827
3.15k
    UA_NodeId defaultUserTokenGroup =
828
3.15k
            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
829
3.15k
    retval = UA_CertificateGroup_Filestore(&config->sessionPKI, &defaultUserTokenGroup,
830
3.15k
                                            pkiFolder, config->logging, &paramsMap);
831
3.15k
    if(retval != UA_STATUSCODE_GOOD) {
832
5
        UA_String_clear(&pkiFolder);
833
5
        return retval;
834
5
    }
835
836
    /* Clean up */
837
3.15k
    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
3.15k
    return UA_STATUSCODE_GOOD;
850
3.15k
}
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
67
PARSE_JSON(RuleHandlingField) {
863
67
    UA_UInt32 enum_value;
864
67
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
865
67
    if(retval != UA_STATUSCODE_GOOD)
866
53
        return retval;
867
14
    UA_RuleHandling *field = (UA_RuleHandling*)configField;
868
14
    *field = (UA_RuleHandling)enum_value;
869
14
    return retval;
870
67
}
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
66
skipUnknownItem(ParsingCtx* ctx) {
912
66
    unsigned int end = ctx->tokens[ctx->index].end;
913
150
    do {
914
150
        ctx->index++;
915
150
    } while (ctx->index < ctx->tokensSize &&
916
125
        ctx->tokens[ctx->index].start < end);
917
66
}
918
919
static UA_StatusCode
920
173
parseJSONConfig(UA_ServerConfig *config, UA_ByteString json_config) {
921
    // Parsing json config
922
173
    const char *json = (const char*)json_config.data;
923
173
    cj5_token tokens[MAX_TOKENS];
924
173
    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
173
    if(r.error != CJ5_ERROR_NONE || r.num_tokens < 2 ||
929
83
       r.tokens[0].type != CJ5_TOKEN_OBJECT)
930
90
        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
880
    for (size_t j = serverConfigSize; j > 0 && ctx.index < ctx.tokensSize; j--) {
949
821
        cj5_token tok = ctx.tokens[ctx.index];
950
821
        switch (tok.type) {
951
809
            case CJ5_TOKEN_STRING: {
952
809
                if(tok.size >= 255) {
953
12
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
954
12
                                   "Configuration field name too long");
955
12
                    continue;
956
12
                }
957
797
                unsigned int str_len = 0;
958
797
                cj5_error_code res = cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
959
797
                if(res != CJ5_ERROR_NONE) {
960
520
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
961
520
                                   "Configuration field name not a valid string");
962
520
                    continue;
963
520
                }
964
277
                if(strcmp(field, "buildInfo") == 0)
965
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BUILDINFO](&ctx, &config->buildInfo, NULL);
966
277
                else if(strcmp(field, "applicationDescription") == 0)
967
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION](&ctx, &config->applicationDescription, NULL);
968
277
                else if(strcmp(field, "shutdownDelay") == 0)
969
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->shutdownDelay, NULL);
970
277
                else if(strcmp(field, "verifyRequestTimestamp") == 0)
971
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->verifyRequestTimestamp, NULL);
972
277
                else if(strcmp(field, "allowEmptyVariables") == 0)
973
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->allowEmptyVariables, NULL);
974
277
                else if(strcmp(field, "serverUrls") == 0)
975
20
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](&ctx, &config->serverUrls, &config->serverUrlsSize);
976
257
                else if(strcmp(field, "tcpEnabled") == 0)
977
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->tcpEnabled, NULL);
978
257
                else if(strcmp(field, "tcp") == 0)
979
6
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_TCPCONFIGURATION](&ctx, config, NULL);
980
251
                else if(strcmp(field, "securityPolicyNoneDiscoveryOnly") == 0)
981
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->securityPolicyNoneDiscoveryOnly, NULL);
982
251
                else if(strcmp(field, "modellingRulesOnInstances") == 0)
983
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->modellingRulesOnInstances, NULL);
984
251
                else if(strcmp(field, "maxSecureChannels") == 0)
985
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSecureChannels, NULL);
986
251
                else if(strcmp(field, "maxSecurityTokenLifetime") == 0)
987
4
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxSecurityTokenLifetime, NULL);
988
247
                else if(strcmp(field, "maxSessions") == 0)
989
5
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSessions, NULL);
990
242
                else if(strcmp(field, "maxSessionTimeout") == 0)
991
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->maxSessionTimeout, NULL);
992
242
                else if(strcmp(field, "maxNodesPerRead") == 0)
993
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRead, NULL);
994
242
                else if(strcmp(field, "maxNodesPerWrite") == 0)
995
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerWrite, NULL);
996
242
                else if(strcmp(field, "maxNodesPerMethodCall") == 0)
997
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerMethodCall, NULL);
998
242
                else if(strcmp(field, "maxNodesPerBrowse") == 0)
999
1
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerBrowse, NULL);
1000
241
                else if(strcmp(field, "maxNodesPerRegisterNodes") == 0)
1001
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRegisterNodes, NULL);
1002
241
                else if(strcmp(field, "maxNodesPerTranslateBrowsePathsToNodeIds") == 0)
1003
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerTranslateBrowsePathsToNodeIds, NULL);
1004
241
                else if(strcmp(field, "maxNodesPerNodeManagement") == 0)
1005
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerNodeManagement, NULL);
1006
241
                else if(strcmp(field, "maxMonitoredItemsPerCall") == 0)
1007
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxMonitoredItemsPerCall, NULL);
1008
241
                else if(strcmp(field, "maxReferencesPerNode") == 0)
1009
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxReferencesPerNode, NULL);
1010
241
                else if(strcmp(field, "reverseReconnectInterval") == 0)
1011
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->reverseReconnectInterval, NULL);
1012
1013
241
#if UA_MULTITHREADING >= 100
1014
241
                else if(strcmp(field, "asyncOperationTimeout") == 0)
1015
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->asyncOperationTimeout, NULL);
1016
241
                else if(strcmp(field, "maxAsyncOperationQueueSize") == 0)
1017
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT64](&ctx, &config->maxAsyncOperationQueueSize, NULL);
1018
241
#endif
1019
1020
241
#ifdef UA_ENABLE_DISCOVERY
1021
241
                else if(strcmp(field, "discoveryCleanupTimeout") == 0)
1022
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->discoveryCleanupTimeout, NULL);
1023
241
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
1024
241
                else if(strcmp(field, "mdnsEnabled") == 0)
1025
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->mdnsEnabled, NULL);
1026
241
                else if(strcmp(field, "mdns") == 0)
1027
12
                    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
6
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION](&ctx, config, NULL);
1036
223
# endif
1037
1038
223
#ifdef UA_ENABLE_HISTORIZING
1039
223
                else if(strcmp(field, "historizingEnabled") == 0)
1040
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->historizingEnabled, NULL);
1041
223
                else if(strcmp(field, "historizing") == 0)
1042
136
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION](&ctx, config, NULL);
1043
87
#endif
1044
1045
87
#ifdef UA_ENABLE_PUBSUB
1046
87
                else if(strcmp(field, "pubsubEnabled") == 0)
1047
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->pubsubEnabled, NULL);
1048
87
                else if(strcmp(field, "pubsub") == 0)
1049
7
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION](&ctx, &config->pubSubConfig, NULL);
1050
80
#endif
1051
80
#ifdef UA_ENABLE_ENCRYPTION
1052
80
                else if(strcmp(field, "securityPolicies") == 0)
1053
14
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPOLICIES](&ctx, config, NULL);
1054
66
                else if(strcmp(field, "pkiFolder") == 0)
1055
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPKI](&ctx, config, NULL);
1056
66
#endif
1057
66
                else {
1058
66
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1059
66
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled.", field);
1060
                    /* skip the name of item */
1061
66
                    ++ctx.index;
1062
                    /* skip value of unknown item */
1063
66
                    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
66
                    --ctx.index;
1068
66
                }
1069
277
                if(retval != UA_STATUSCODE_GOOD) {
1070
24
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1071
24
                                 "An error occurred while parsing the configuration field %s", field);
1072
24
                    return retval;
1073
24
                }
1074
253
                break;
1075
277
            }
1076
253
            default:
1077
12
                break;
1078
821
        }
1079
265
        ctx.index += 1;
1080
265
    }
1081
59
    return retval;
1082
83
}
1083
1084
UA_Server *
1085
7.52k
UA_Server_newFromFile(const UA_ByteString json_config) {
1086
7.52k
    UA_ServerConfig config;
1087
7.52k
    memset(&config, 0, sizeof(UA_ServerConfig));
1088
7.52k
    UA_StatusCode res = UA_ServerConfig_setDefault(&config);
1089
7.52k
    res |= parseJSONConfig(&config, json_config);
1090
7.52k
    if(res != UA_STATUSCODE_GOOD) {
1091
1.25k
        UA_ServerConfig_clear(&config);
1092
1.25k
        return NULL;
1093
1.25k
    }
1094
6.26k
    return UA_Server_newWithConfig(&config);
1095
7.52k
}
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