Coverage Report

Created: 2026-06-30 06:45

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
1.95k
#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
221
getJsonPart(cj5_token tok, const char *json) {
31
221
    UA_ByteString bs;
32
221
    UA_ByteString_init(&bs);
33
221
    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
221
    } else {
38
221
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start);
39
221
        bs.length = (tok.end - tok.start) + 1;
40
221
        return bs;
41
221
    }
42
221
}
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
13
PARSE_JSON(UInt16Field) {
107
13
    cj5_token tok = ctx->tokens[++ctx->index];
108
13
    UA_ByteString buf = getJsonPart(tok, ctx->json);
109
13
    UA_UInt16 out;
110
13
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT16], NULL);
111
13
    if(retval != UA_STATUSCODE_GOOD)
112
12
        return retval;
113
1
    UA_UInt16 *field = (UA_UInt16*)configField;
114
1
    *field = out;
115
1
    return retval;
116
13
}
117
147
PARSE_JSON(UInt32Field) {
118
147
    cj5_token tok = ctx->tokens[++ctx->index];
119
147
    UA_ByteString buf = getJsonPart(tok, ctx->json);
120
147
    UA_UInt32 out;
121
147
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
122
147
    if(retval != UA_STATUSCODE_GOOD)
123
101
        return retval;
124
46
    UA_UInt32 *field = (UA_UInt32*)configField;
125
46
    *field = out;
126
46
    return retval;
127
147
}
128
52
PARSE_JSON(UInt64Field) {
129
52
    cj5_token tok = ctx->tokens[++ctx->index];
130
52
    UA_ByteString buf = getJsonPart(tok, ctx->json);
131
52
    UA_UInt64 out;
132
52
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT64], NULL);
133
52
    if(retval != UA_STATUSCODE_GOOD)
134
28
        return retval;
135
24
    UA_UInt64 *field = (UA_UInt64*)configField;
136
24
    *field = out;
137
24
    return retval;
138
52
}
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
3
PARSE_JSON(DoubleField) {
201
3
    cj5_token tok = ctx->tokens[++ctx->index];
202
3
    UA_ByteString buf = getJsonPart(tok, ctx->json);
203
3
    UA_Double out;
204
3
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DOUBLE], NULL);
205
3
    if(retval != UA_STATUSCODE_GOOD)
206
2
        return retval;
207
1
    UA_Double *field = (UA_Double *)configField;
208
1
    *field = out;
209
1
    return retval;
210
3
}
211
6
PARSE_JSON(BooleanField) {
212
6
    cj5_token tok = ctx->tokens[++ctx->index];
213
6
    UA_ByteString buf = getJsonPart(tok, ctx->json);
214
6
    UA_Boolean out;
215
6
    if(tok.type != CJ5_TOKEN_BOOL) {
216
6
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Value of type bool expected.");
217
6
        return UA_STATUSCODE_BADTYPEMISMATCH;
218
6
    }
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
6
}
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.42k
    for(size_t j = tok.size/2; j > 0; j--) {
297
1.40k
        tok = ctx->tokens[++ctx->index];
298
1.40k
        switch (tok.type) {
299
5
        case CJ5_TOKEN_STRING: {
300
5
            char *field_str = (char*)UA_malloc(tok.size + 1);
301
5
            unsigned int str_len = 0;
302
5
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
303
5
            if(strcmp(field_str, "productUri") == 0)
304
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productUri, NULL);
305
5
            else if(strcmp(field_str, "manufacturerName") == 0)
306
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->manufacturerName, NULL);
307
5
            else if(strcmp(field_str, "productName") == 0)
308
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productName, NULL);
309
5
            else if(strcmp(field_str, "softwareVersion") == 0)
310
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->softwareVersion, NULL);
311
5
            else if(strcmp(field_str, "buildNumber") == 0)
312
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->buildNumber, NULL);
313
5
            else if(strcmp(field_str, "buildDate") == 0)
314
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DATETIME](ctx, &field->buildDate, NULL);
315
5
            else {
316
5
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
317
5
            }
318
5
            UA_free(field_str);
319
5
            break;
320
0
        }
321
1.40k
        default:
322
1.40k
            break;
323
1.40k
        }
324
1.40k
    }
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
25
PARSE_JSON(MdnsConfigurationField) {
441
25
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
442
25
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
443
25
    cj5_token tok = ctx->tokens[++ctx->index];
444
165
    for(size_t j = tok.size/2; j > 0; j--) {
445
140
        tok = ctx->tokens[++ctx->index];
446
140
        switch (tok.type) {
447
14
        case CJ5_TOKEN_STRING: {
448
14
            char *field_str = (char*)UA_malloc(tok.size + 1);
449
14
            unsigned int str_len = 0;
450
14
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
451
14
            if(strcmp(field_str, "mdnsServerName") == 0)
452
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsConfig.mdnsServerName, NULL);
453
14
            else if(strcmp(field_str, "serverCapabilities") == 0)
454
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &config->mdnsConfig.serverCapabilities, &config->mdnsConfig.serverCapabilitiesSize);
455
14
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_MDNSD
456
14
            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
14
#endif
464
14
            else {
465
14
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
466
14
            }
467
14
            UA_free(field_str);
468
14
            break;
469
0
        }
470
126
        default:
471
126
        break;
472
140
        }
473
140
    }
474
25
#endif
475
25
    return UA_STATUSCODE_GOOD;
476
25
}
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
27
PARSE_JSON(TcpConfigurationField) {
534
27
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
535
27
    cj5_token tok = ctx->tokens[++ctx->index];
536
907
    for(size_t j = tok.size/2; j > 0; j--) {
537
880
        tok = ctx->tokens[++ctx->index];
538
880
        switch (tok.type) {
539
53
        case CJ5_TOKEN_STRING: {
540
53
            char *field_str = (char*)UA_malloc(tok.size + 1);
541
53
            unsigned int str_len = 0;
542
53
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
543
53
            if(strcmp(field_str, "tcpBufSize") == 0)
544
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpBufSize, NULL);
545
53
            else if(strcmp(field_str, "tcpMaxMsgSize") == 0)
546
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxMsgSize, NULL);
547
53
            else if(strcmp(field_str, "tcpMaxChunks") == 0)
548
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxChunks, NULL);
549
53
            else {
550
53
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
551
53
            }
552
53
            UA_free(field_str);
553
53
            break;
554
0
        }
555
827
        default:
556
827
            break;
557
880
        }
558
880
    }
559
27
    return UA_STATUSCODE_GOOD;
560
27
}
561
562
4
PARSE_JSON(PubsubConfigurationField) {
563
4
#ifdef UA_ENABLE_PUBSUB
564
4
    UA_PubSubConfiguration *field = (UA_PubSubConfiguration*)configField;
565
4
    cj5_token tok = ctx->tokens[++ctx->index];
566
13
    for(size_t j = tok.size/2; j > 0; j--) {
567
9
        tok = ctx->tokens[++ctx->index];
568
9
        switch (tok.type) {
569
0
        case CJ5_TOKEN_STRING: {
570
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
571
0
            unsigned int str_len = 0;
572
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
573
0
            if(strcmp(field_str, "enableDeltaFrames") == 0)
574
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableDeltaFrames, NULL);
575
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
576
0
            else if(strcmp(field_str, "enableInformationModelMethods") == 0)
577
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableInformationModelMethods, NULL);
578
0
#endif
579
0
            else {
580
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
581
0
            }
582
0
            UA_free(field_str);
583
0
            break;
584
0
        }
585
9
        default:
586
9
            break;
587
9
        }
588
9
    }
589
4
#endif
590
4
    return UA_STATUSCODE_GOOD;
591
4
}
592
593
0
PARSE_JSON(HistorizingConfigurationField) {
594
0
#ifdef UA_ENABLE_HISTORIZING
595
0
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
596
0
    cj5_token tok = ctx->tokens[++ctx->index];
597
0
    for(size_t j = tok.size/2; j > 0; j--) {
598
0
        tok = ctx->tokens[++ctx->index];
599
0
        switch (tok.type) {
600
0
        case CJ5_TOKEN_STRING: {
601
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
602
0
            unsigned int str_len = 0;
603
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
604
0
            if(strcmp(field_str, "accessHistoryDataCapability") == 0)
605
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryDataCapability, NULL);
606
0
            else if(strcmp(field_str, "maxReturnDataValues") == 0)
607
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnDataValues, NULL);
608
0
            else if(strcmp(field_str, "accessHistoryEventsCapability") == 0)
609
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->accessHistoryEventsCapability, NULL);
610
0
            else if(strcmp(field_str, "maxReturnEventValues") == 0)
611
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->maxReturnEventValues, NULL);
612
0
            else if(strcmp(field_str, "insertDataCapability") == 0)
613
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertDataCapability, NULL);
614
0
            else if(strcmp(field_str, "insertEventCapability") == 0)
615
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertEventCapability, NULL);
616
0
            else if(strcmp(field_str, "insertAnnotationsCapability") == 0)
617
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->insertAnnotationsCapability, NULL);
618
0
            else if(strcmp(field_str, "replaceDataCapability") == 0)
619
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceDataCapability, NULL);
620
0
            else if(strcmp(field_str, "replaceEventCapability") == 0)
621
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->replaceEventCapability, NULL);
622
0
            else if(strcmp(field_str, "updateDataCapability") == 0)
623
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateDataCapability, NULL);
624
0
            else if(strcmp(field_str, "updateEventCapability") == 0)
625
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->updateEventCapability, NULL);
626
0
            else if(strcmp(field_str, "deleteRawCapability") == 0)
627
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteRawCapability, NULL);
628
0
            else if(strcmp(field_str, "deleteEventCapability") == 0)
629
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteEventCapability, NULL);
630
0
            else if(strcmp(field_str, "deleteAtTimeDataCapability") == 0)
631
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &config->deleteAtTimeDataCapability, NULL);
632
0
            else {
633
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
634
0
            }
635
0
            UA_free(field_str);
636
0
            break;
637
0
        }
638
0
        default:
639
0
            break;
640
0
        }
641
0
    }
642
0
#endif
643
0
    return UA_STATUSCODE_GOOD;
644
0
}
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) || defined(__APPLE__) || defined(__OpenBSD__)
788
    /* Set up the parameters for the filestore certificate store */
789
0
    UA_KeyValuePair params[2];
790
0
    size_t paramsSize = 2;
791
792
0
    params[0].key = UA_QUALIFIEDNAME(0, "max-trust-listsize");
793
0
    UA_Variant_setScalar(&params[0].value, &config->maxTrustListSize, &UA_TYPES[UA_TYPES_UINT32]);
794
0
    params[1].key = UA_QUALIFIEDNAME(0, "max-rejected-listsize");
795
0
    UA_Variant_setScalar(&params[1].value, &config->maxRejectedListSize, &UA_TYPES[UA_TYPES_UINT32]);
796
797
0
    UA_KeyValueMap paramsMap;
798
0
    paramsMap.map = params;
799
0
    paramsMap.mapSize = paramsSize;
800
801
    /* set server config field */
802
0
    UA_NodeId defaultApplicationGroup =
803
0
           UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
804
0
    retval = UA_CertificateGroup_Filestore(&config->secureChannelPKI, &defaultApplicationGroup,
805
0
                                           pkiFolder, config->logging, &paramsMap);
806
0
    if(retval != UA_STATUSCODE_GOOD) {
807
0
        UA_String_clear(&pkiFolder);
808
0
        return retval;
809
0
    }
810
811
0
    UA_NodeId defaultUserTokenGroup =
812
0
            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
813
0
    retval = UA_CertificateGroup_Filestore(&config->sessionPKI, &defaultUserTokenGroup,
814
0
                                            pkiFolder, config->logging, &paramsMap);
815
0
    if(retval != UA_STATUSCODE_GOOD) {
816
0
        UA_String_clear(&pkiFolder);
817
0
        return retval;
818
0
    }
819
820
    /* Clean up */
821
0
    UA_String_clear(&pkiFolder);
822
#else
823
    (void)config;
824
    UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
825
                   "pkiFolder is not supported on this platform. "
826
                   "Trusted clients will not be verified.");
827
#endif
828
#else
829
    UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
830
                   "pkiFolder is set in the config but UA_ENABLE_ENCRYPTION "
831
                   "is not enabled. Trusted clients will not be verified.");
832
#endif
833
0
    return UA_STATUSCODE_GOOD;
834
0
}
835
836
/*----------------------Enumerations------------------------*/
837
0
PARSE_JSON(ApplicationTypeField) {
838
0
    UA_UInt32 enum_value;
839
0
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
840
0
    if(retval != UA_STATUSCODE_GOOD)
841
0
        return retval;
842
0
    UA_ApplicationType *field = (UA_ApplicationType*)configField;
843
0
    *field = (UA_ApplicationType)enum_value;
844
0
    return retval;
845
0
}
846
61
PARSE_JSON(RuleHandlingField) {
847
61
    UA_UInt32 enum_value;
848
61
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
849
61
    if(retval != UA_STATUSCODE_GOOD)
850
31
        return retval;
851
30
    UA_RuleHandling *field = (UA_RuleHandling*)configField;
852
30
    *field = (UA_RuleHandling)enum_value;
853
30
    return retval;
854
61
}
855
856
const parseJsonSignature parseJsonJumpTable[UA_SERVERCONFIGFIELDKINDS] = {
857
    /* Basic Types */
858
    (parseJsonSignature)Int64Field_parseJson,
859
    (parseJsonSignature)UInt16Field_parseJson,
860
    (parseJsonSignature)UInt32Field_parseJson,
861
    (parseJsonSignature)UInt64Field_parseJson,
862
    (parseJsonSignature)StringField_parseJson,
863
    (parseJsonSignature)LocalizedTextField_parseJson,
864
    (parseJsonSignature)DoubleField_parseJson,
865
    (parseJsonSignature)BooleanField_parseJson,
866
    (parseJsonSignature)DurationField_parseJson,
867
    (parseJsonSignature)DurationRangeField_parseJson,
868
    (parseJsonSignature)UInt32RangeField_parseJson,
869
870
    /* Advanced Types */
871
    (parseJsonSignature)BuildInfo_parseJson,
872
    (parseJsonSignature)ApplicationDescriptionField_parseJson,
873
    (parseJsonSignature)StringArrayField_parseJson,
874
    (parseJsonSignature)UInt32ArrayField_parseJson,
875
    (parseJsonSignature)DateTimeField_parseJson,
876
    (parseJsonSignature)SubscriptionConfigurationField_parseJson,
877
    (parseJsonSignature)TcpConfigurationField_parseJson,
878
    (parseJsonSignature)PubsubConfigurationField_parseJson,
879
    (parseJsonSignature)HistorizingConfigurationField_parseJson,
880
    (parseJsonSignature)MdnsConfigurationField_parseJson,
881
    (parseJsonSignature)SecurityPolciesField_parseJson,
882
    (parseJsonSignature)SecurityPkiField_parseJson,
883
884
    /* Enumerations */
885
    (parseJsonSignature)ApplicationTypeField_parseJson,
886
    (parseJsonSignature)RuleHandlingField_parseJson,
887
};
888
889
/* Skips unknown item (simple, object or array) in config file. 
890
* Unknown items may happen if we don't support some features. 
891
* E.g. if  UA_ENABLE_ENCRYPTION is not defined and config file 
892
* contains "securityPolicies" entry.
893
*/
894
static void
895
1.21k
skipUnknownItem(ParsingCtx* ctx) {
896
1.21k
    unsigned int end = ctx->tokens[ctx->index].end;
897
6.11k
    do {
898
6.11k
        ctx->index++;
899
6.11k
    } while (ctx->index < ctx->tokensSize &&
900
5.27k
        ctx->tokens[ctx->index].start < end);
901
1.21k
}
902
903
static UA_StatusCode
904
1.95k
parseJSONConfig(UA_ServerConfig *config, UA_ByteString json_config) {
905
    // Parsing json config
906
1.95k
    const char *json = (const char*)json_config.data;
907
1.95k
    cj5_token tokens[MAX_TOKENS];
908
1.95k
    cj5_result r = cj5_parse(json, (unsigned int)json_config.length, tokens, MAX_TOKENS, NULL);
909
910
    /* Validate the parse result: must succeed and produce a root object
911
     * with at least one key-value pair (i.e. >= 2 tokens). */
912
1.95k
    if(r.error != CJ5_ERROR_NONE || r.num_tokens < 2 ||
913
1.22k
       r.tokens[0].type != CJ5_TOKEN_OBJECT)
914
735
        return UA_STATUSCODE_BADDECODINGERROR;
915
916
1.22k
    ParsingCtx ctx;
917
1.22k
    ctx.json = json;
918
1.22k
    ctx.result = r;
919
1.22k
    ctx.tokens = r.tokens;
920
1.22k
    ctx.tokensSize = r.num_tokens;
921
1.22k
    ctx.index = 1; // The first token is ignored because it is known and not needed.
922
923
1.22k
    ctx.logging = config->logging;
924
925
    /* Buffer for the field name */
926
1.22k
    char field[256];
927
928
1.22k
    size_t serverConfigSize = 0;
929
1.22k
    if(ctx.tokens)
930
1.22k
        serverConfigSize = (ctx.tokens[ctx.index-1].size/2);
931
1.22k
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
932
8.34k
    for (size_t j = serverConfigSize; j > 0; j--) {
933
7.17k
        cj5_token tok = ctx.tokens[ctx.index];
934
7.17k
        switch (tok.type) {
935
7.15k
            case CJ5_TOKEN_STRING: {
936
7.15k
                if(tok.size >= 255) {
937
282
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
938
282
                                   "Configuration field name too long");
939
282
                    continue;
940
282
                }
941
6.86k
                unsigned int str_len = 0;
942
6.86k
                cj5_error_code res = cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
943
6.86k
                if(res != CJ5_ERROR_NONE) {
944
5.52k
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
945
5.52k
                                   "Configuration field name not a valid string");
946
5.52k
                    continue;
947
5.52k
                }
948
1.34k
                if(strcmp(field, "buildInfo") == 0)
949
20
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BUILDINFO](&ctx, &config->buildInfo, NULL);
950
1.32k
                else if(strcmp(field, "applicationDescription") == 0)
951
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION](&ctx, &config->applicationDescription, NULL);
952
1.32k
                else if(strcmp(field, "shutdownDelay") == 0)
953
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->shutdownDelay, NULL);
954
1.32k
                else if(strcmp(field, "verifyRequestTimestamp") == 0)
955
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->verifyRequestTimestamp, NULL);
956
1.32k
                else if(strcmp(field, "allowEmptyVariables") == 0)
957
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->allowEmptyVariables, NULL);
958
1.32k
                else if(strcmp(field, "serverUrls") == 0)
959
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](&ctx, &config->serverUrls, &config->serverUrlsSize);
960
1.32k
                else if(strcmp(field, "tcpEnabled") == 0)
961
1
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->tcpEnabled, NULL);
962
1.32k
                else if(strcmp(field, "tcp") == 0)
963
27
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_TCPCONFIGURATION](&ctx, config, NULL);
964
1.29k
                else if(strcmp(field, "securityPolicyNoneDiscoveryOnly") == 0)
965
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->securityPolicyNoneDiscoveryOnly, NULL);
966
1.29k
                else if(strcmp(field, "modellingRulesOnInstances") == 0)
967
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->modellingRulesOnInstances, NULL);
968
1.29k
                else if(strcmp(field, "maxSecureChannels") == 0)
969
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSecureChannels, NULL);
970
1.29k
                else if(strcmp(field, "maxSecurityTokenLifetime") == 0)
971
57
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxSecurityTokenLifetime, NULL);
972
1.24k
                else if(strcmp(field, "maxSessions") == 0)
973
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSessions, NULL);
974
1.24k
                else if(strcmp(field, "maxSessionTimeout") == 0)
975
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->maxSessionTimeout, NULL);
976
1.24k
                else if(strcmp(field, "maxNodesPerRead") == 0)
977
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRead, NULL);
978
1.24k
                else if(strcmp(field, "maxNodesPerWrite") == 0)
979
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerWrite, NULL);
980
1.24k
                else if(strcmp(field, "maxNodesPerMethodCall") == 0)
981
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerMethodCall, NULL);
982
1.24k
                else if(strcmp(field, "maxNodesPerBrowse") == 0)
983
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerBrowse, NULL);
984
1.24k
                else if(strcmp(field, "maxNodesPerRegisterNodes") == 0)
985
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRegisterNodes, NULL);
986
1.24k
                else if(strcmp(field, "maxNodesPerTranslateBrowsePathsToNodeIds") == 0)
987
1
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerTranslateBrowsePathsToNodeIds, NULL);
988
1.24k
                else if(strcmp(field, "maxNodesPerNodeManagement") == 0)
989
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerNodeManagement, NULL);
990
1.24k
                else if(strcmp(field, "maxMonitoredItemsPerCall") == 0)
991
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxMonitoredItemsPerCall, NULL);
992
1.24k
                else if(strcmp(field, "maxReferencesPerNode") == 0)
993
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxReferencesPerNode, NULL);
994
1.24k
                else if(strcmp(field, "reverseReconnectInterval") == 0)
995
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->reverseReconnectInterval, NULL);
996
997
1.24k
#if UA_MULTITHREADING >= 100
998
1.24k
                else if(strcmp(field, "asyncOperationTimeout") == 0)
999
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->asyncOperationTimeout, NULL);
1000
1.24k
                else if(strcmp(field, "maxAsyncOperationQueueSize") == 0)
1001
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT64](&ctx, &config->maxAsyncOperationQueueSize, NULL);
1002
1.24k
#endif
1003
1004
1.24k
#ifdef UA_ENABLE_DISCOVERY
1005
1.24k
                else if(strcmp(field, "discoveryCleanupTimeout") == 0)
1006
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->discoveryCleanupTimeout, NULL);
1007
1.24k
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
1008
1.24k
                else if(strcmp(field, "mdnsEnabled") == 0)
1009
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->mdnsEnabled, NULL);
1010
1.24k
                else if(strcmp(field, "mdns") == 0)
1011
25
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_MDNSCONFIGURATION](&ctx, config, NULL);
1012
1.21k
#endif
1013
1.21k
#endif
1014
1015
1.21k
#ifdef UA_ENABLE_SUBSCRIPTIONS
1016
1.21k
                else if(strcmp(field, "subscriptionsEnabled") == 0)
1017
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->subscriptionsEnabled, NULL);
1018
1.21k
                else if(strcmp(field, "subscriptions") == 0)
1019
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION](&ctx, config, NULL);
1020
1.21k
# endif
1021
1022
1.21k
#ifdef UA_ENABLE_HISTORIZING
1023
1.21k
                else if(strcmp(field, "historizingEnabled") == 0)
1024
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->historizingEnabled, NULL);
1025
1.21k
                else if(strcmp(field, "historizing") == 0)
1026
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION](&ctx, config, NULL);
1027
1.21k
#endif
1028
1029
1.21k
#ifdef UA_ENABLE_PUBSUB
1030
1.21k
                else if(strcmp(field, "pubsubEnabled") == 0)
1031
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->pubsubEnabled, NULL);
1032
1.21k
                else if(strcmp(field, "pubsub") == 0)
1033
4
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION](&ctx, &config->pubSubConfig, NULL);
1034
1.21k
#endif
1035
1.21k
#ifdef UA_ENABLE_ENCRYPTION
1036
1.21k
                else if(strcmp(field, "securityPolicies") == 0)
1037
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPOLICIES](&ctx, config, NULL);
1038
1.21k
                else if(strcmp(field, "pkiFolder") == 0)
1039
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPKI](&ctx, config, NULL);
1040
1.21k
#endif
1041
1.21k
                else {
1042
1.21k
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1043
1.21k
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled.", field);
1044
                    /* skip the name of item */
1045
1.21k
                    ++ctx.index;
1046
                    /* skip value of unknown item */
1047
1.21k
                    skipUnknownItem(&ctx);
1048
                    /* after skipUnknownItem() ctx->index points to the name of the following item.
1049
                       We must decrement index in oder following increment will
1050
                       still set index to the right position (name of the following item) */
1051
1.21k
                    --ctx.index;
1052
1.21k
                }
1053
1.34k
                if(retval != UA_STATUSCODE_GOOD) {
1054
47
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1055
47
                                 "An error occurred while parsing the configuration field %s", field);
1056
47
                    return retval;
1057
47
                }
1058
1.30k
                break;
1059
1.34k
            }
1060
1.30k
            default:
1061
22
                break;
1062
7.17k
        }
1063
1.32k
        ctx.index += 1;
1064
1.32k
    }
1065
1.17k
    return retval;
1066
1.22k
}
1067
1068
UA_Server *
1069
4.92k
UA_Server_newFromFile(const UA_ByteString json_config) {
1070
4.92k
    UA_ServerConfig config;
1071
4.92k
    memset(&config, 0, sizeof(UA_ServerConfig));
1072
4.92k
    UA_StatusCode res = UA_ServerConfig_setDefault(&config);
1073
4.92k
    res |= parseJSONConfig(&config, json_config);
1074
4.92k
    if(res != UA_STATUSCODE_GOOD) {
1075
1.62k
        UA_ServerConfig_clear(&config);
1076
1.62k
        return NULL;
1077
1.62k
    }
1078
3.30k
    return UA_Server_newWithConfig(&config);
1079
4.92k
}
1080
1081
UA_StatusCode
1082
0
UA_ServerConfig_updateFromFile(UA_ServerConfig *config, const UA_ByteString json_config) {
1083
0
    UA_StatusCode res = parseJSONConfig(config, json_config);
1084
0
    return res;
1085
0
}
1086
1087
#ifdef UA_ENABLE_ENCRYPTION
1088
static UA_ByteString
1089
0
loadCertificateFile(const char *const path) {
1090
0
    UA_ByteString fileContents = UA_BYTESTRING_NULL;
1091
1092
    /* Open the file */
1093
0
    FILE *fp = fopen(path, "rb");
1094
0
    if(!fp) {
1095
0
        errno = 0; /* We read errno also from the tcp layer... */
1096
0
        return fileContents;
1097
0
    }
1098
1099
    /* Get the file length, allocate the data and read */
1100
0
    if(fseek(fp, 0, SEEK_END) != 0) {
1101
0
        fclose(fp);
1102
0
        errno = 0;
1103
0
        return fileContents;
1104
0
    }
1105
1106
0
    long length = ftell(fp);
1107
0
    if(length < 0) {
1108
0
        fclose(fp);
1109
0
        errno = 0;
1110
0
        return fileContents;
1111
0
    }
1112
1113
0
    fileContents.length = (size_t)length;
1114
0
    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
1115
0
    if(fileContents.data) {
1116
0
        if(fseek(fp, 0, SEEK_SET) != 0) {
1117
0
            fclose(fp);
1118
0
            UA_ByteString_clear(&fileContents);
1119
0
            errno = 0;
1120
0
            return fileContents;
1121
0
        }
1122
0
        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
1123
0
        if(read != fileContents.length)
1124
0
            UA_ByteString_clear(&fileContents);
1125
0
    } else {
1126
0
        fileContents.length = 0;
1127
0
    }
1128
0
    fclose(fp);
1129
1130
0
    return fileContents;
1131
0
}
1132
#endif