Coverage Report

Created: 2026-05-30 06:22

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