Coverage Report

Created: 2026-07-16 06:23

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
114
#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
177
getJsonPart(cj5_token tok, const char *json) {
31
177
    UA_ByteString bs;
32
177
    UA_ByteString_init(&bs);
33
177
    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
177
    } else {
38
177
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start);
39
177
        bs.length = (tok.end - tok.start) + 1;
40
177
        return bs;
41
177
    }
42
177
}
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
45
PARSE_JSON(UInt16Field) {
107
45
    cj5_token tok = ctx->tokens[++ctx->index];
108
45
    UA_ByteString buf = getJsonPart(tok, ctx->json);
109
45
    UA_UInt16 out;
110
45
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT16], NULL);
111
45
    if(retval != UA_STATUSCODE_GOOD)
112
35
        return retval;
113
10
    UA_UInt16 *field = (UA_UInt16*)configField;
114
10
    *field = out;
115
10
    return retval;
116
45
}
117
71
PARSE_JSON(UInt32Field) {
118
71
    cj5_token tok = ctx->tokens[++ctx->index];
119
71
    UA_ByteString buf = getJsonPart(tok, ctx->json);
120
71
    UA_UInt32 out;
121
71
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
122
71
    if(retval != UA_STATUSCODE_GOOD)
123
46
        return retval;
124
25
    UA_UInt32 *field = (UA_UInt32*)configField;
125
25
    *field = out;
126
25
    return retval;
127
71
}
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
25
        return retval;
135
27
    UA_UInt64 *field = (UA_UInt64*)configField;
136
27
    *field = out;
137
27
    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
5
PARSE_JSON(DoubleField) {
201
5
    cj5_token tok = ctx->tokens[++ctx->index];
202
5
    UA_ByteString buf = getJsonPart(tok, ctx->json);
203
5
    UA_Double out;
204
5
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DOUBLE], NULL);
205
5
    if(retval != UA_STATUSCODE_GOOD)
206
3
        return retval;
207
2
    UA_Double *field = (UA_Double *)configField;
208
2
    *field = out;
209
2
    return retval;
210
5
}
211
4
PARSE_JSON(BooleanField) {
212
4
    cj5_token tok = ctx->tokens[++ctx->index];
213
4
    UA_ByteString buf = getJsonPart(tok, ctx->json);
214
4
    UA_Boolean out;
215
4
    if(tok.type != CJ5_TOKEN_BOOL) {
216
4
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Value of type bool expected.");
217
4
        return UA_STATUSCODE_BADTYPEMISMATCH;
218
4
    }
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
4
}
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
0
PARSE_JSON(BuildInfo) {
294
0
    UA_BuildInfo *field = (UA_BuildInfo*)configField;
295
0
    cj5_token tok = ctx->tokens[++ctx->index];
296
0
    for(size_t j = tok.size/2; j > 0; j--) {
297
0
        tok = ctx->tokens[++ctx->index];
298
0
        switch (tok.type) {
299
0
        case CJ5_TOKEN_STRING: {
300
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
301
0
            unsigned int str_len = 0;
302
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
303
0
            if(strcmp(field_str, "productUri") == 0)
304
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productUri, NULL);
305
0
            else if(strcmp(field_str, "manufacturerName") == 0)
306
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->manufacturerName, NULL);
307
0
            else if(strcmp(field_str, "productName") == 0)
308
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->productName, NULL);
309
0
            else if(strcmp(field_str, "softwareVersion") == 0)
310
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->softwareVersion, NULL);
311
0
            else if(strcmp(field_str, "buildNumber") == 0)
312
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &field->buildNumber, NULL);
313
0
            else if(strcmp(field_str, "buildDate") == 0)
314
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_DATETIME](ctx, &field->buildDate, NULL);
315
0
            else {
316
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
317
0
            }
318
0
            UA_free(field_str);
319
0
            break;
320
0
        }
321
0
        default:
322
0
            break;
323
0
        }
324
0
    }
325
0
    return UA_STATUSCODE_GOOD;
326
0
}
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
36
PARSE_JSON(MdnsConfigurationField) {
441
36
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
442
36
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
443
36
    cj5_token tok = ctx->tokens[++ctx->index];
444
473
    for(size_t j = tok.size/2; j > 0; j--) {
445
437
        tok = ctx->tokens[++ctx->index];
446
437
        switch (tok.type) {
447
214
        case CJ5_TOKEN_STRING: {
448
214
            char *field_str = (char*)UA_malloc(tok.size + 1);
449
214
            unsigned int str_len = 0;
450
214
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
451
214
            if(strcmp(field_str, "mdnsServerName") == 0)
452
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRING](ctx, &config->mdnsConfig.mdnsServerName, NULL);
453
214
            else if(strcmp(field_str, "serverCapabilities") == 0)
454
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](ctx, &config->mdnsConfig.serverCapabilities, &config->mdnsConfig.serverCapabilitiesSize);
455
214
#ifdef UA_ENABLE_DISCOVERY_MULTICAST_MDNSD
456
214
            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
214
#endif
464
214
            else {
465
214
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
466
214
            }
467
214
            UA_free(field_str);
468
214
            break;
469
0
        }
470
223
        default:
471
223
        break;
472
437
        }
473
437
    }
474
36
#endif
475
36
    return UA_STATUSCODE_GOOD;
476
36
}
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
0
PARSE_JSON(TcpConfigurationField) {
534
0
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
535
0
    cj5_token tok = ctx->tokens[++ctx->index];
536
0
    for(size_t j = tok.size/2; j > 0; j--) {
537
0
        tok = ctx->tokens[++ctx->index];
538
0
        switch (tok.type) {
539
0
        case CJ5_TOKEN_STRING: {
540
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
541
0
            unsigned int str_len = 0;
542
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
543
0
            if(strcmp(field_str, "tcpBufSize") == 0)
544
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpBufSize, NULL);
545
0
            else if(strcmp(field_str, "tcpMaxMsgSize") == 0)
546
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxMsgSize, NULL);
547
0
            else if(strcmp(field_str, "tcpMaxChunks") == 0)
548
0
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](ctx, &config->tcpMaxChunks, NULL);
549
0
            else {
550
0
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
551
0
            }
552
0
            UA_free(field_str);
553
0
            break;
554
0
        }
555
0
        default:
556
0
            break;
557
0
        }
558
0
    }
559
0
    return UA_STATUSCODE_GOOD;
560
0
}
561
562
PARSE_JSON(PubsubConfigurationField) {
563
#ifdef UA_ENABLE_PUBSUB
564
    UA_PubSubConfiguration *field = (UA_PubSubConfiguration*)configField;
565
    cj5_token tok = ctx->tokens[++ctx->index];
566
    for(size_t j = tok.size/2; j > 0; j--) {
567
        tok = ctx->tokens[++ctx->index];
568
        switch (tok.type) {
569
        case CJ5_TOKEN_STRING: {
570
            char *field_str = (char*)UA_malloc(tok.size + 1);
571
            unsigned int str_len = 0;
572
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
573
            if(strcmp(field_str, "enableDeltaFrames") == 0)
574
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableDeltaFrames, NULL);
575
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
576
            else if(strcmp(field_str, "enableInformationModelMethods") == 0)
577
                parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](ctx, &field->enableInformationModelMethods, NULL);
578
#endif
579
            else {
580
                UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name.");
581
            }
582
            UA_free(field_str);
583
            break;
584
        }
585
        default:
586
            break;
587
        }
588
    }
589
#endif
590
    return UA_STATUSCODE_GOOD;
591
}
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
56
PARSE_JSON(RuleHandlingField) {
847
56
    UA_UInt32 enum_value;
848
56
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
849
56
    if(retval != UA_STATUSCODE_GOOD)
850
34
        return retval;
851
22
    UA_RuleHandling *field = (UA_RuleHandling*)configField;
852
22
    *field = (UA_RuleHandling)enum_value;
853
22
    return retval;
854
56
}
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
2
skipUnknownItem(ParsingCtx* ctx) {
896
2
    unsigned int end = ctx->tokens[ctx->index].end;
897
2
    do {
898
2
        ctx->index++;
899
2
    } while (ctx->index < ctx->tokensSize &&
900
0
        ctx->tokens[ctx->index].start < end);
901
2
}
902
903
static UA_StatusCode
904
114
parseJSONConfig(UA_ServerConfig *config, UA_ByteString json_config) {
905
    // Parsing json config
906
114
    const char *json = (const char*)json_config.data;
907
114
    cj5_token tokens[MAX_TOKENS];
908
114
    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
114
    if(r.error != CJ5_ERROR_NONE || r.num_tokens < 2 ||
913
15
       r.tokens[0].type != CJ5_TOKEN_OBJECT)
914
99
        return UA_STATUSCODE_BADDECODINGERROR;
915
916
15
    ParsingCtx ctx;
917
15
    ctx.json = json;
918
15
    ctx.result = r;
919
15
    ctx.tokens = r.tokens;
920
15
    ctx.tokensSize = r.num_tokens;
921
15
    ctx.index = 1; // The first token is ignored because it is known and not needed.
922
923
15
    ctx.logging = config->logging;
924
925
    /* Buffer for the field name */
926
15
    char field[256];
927
928
15
    size_t serverConfigSize = 0;
929
15
    if(ctx.tokens)
930
15
        serverConfigSize = (ctx.tokens[ctx.index-1].size/2);
931
15
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
932
411
    for (size_t j = serverConfigSize; j > 0; j--) {
933
396
        cj5_token tok = ctx.tokens[ctx.index];
934
396
        switch (tok.type) {
935
182
            case CJ5_TOKEN_STRING: {
936
182
                if(tok.size >= 255) {
937
144
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
938
144
                                   "Configuration field name too long");
939
144
                    continue;
940
144
                }
941
38
                unsigned int str_len = 0;
942
38
                cj5_error_code res = cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
943
38
                if(res != CJ5_ERROR_NONE) {
944
0
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
945
0
                                   "Configuration field name not a valid string");
946
0
                    continue;
947
0
                }
948
38
                if(strcmp(field, "buildInfo") == 0)
949
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BUILDINFO](&ctx, &config->buildInfo, NULL);
950
38
                else if(strcmp(field, "applicationDescription") == 0)
951
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_APPLICATIONDESCRIPTION](&ctx, &config->applicationDescription, NULL);
952
38
                else if(strcmp(field, "shutdownDelay") == 0)
953
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->shutdownDelay, NULL);
954
38
                else if(strcmp(field, "verifyRequestTimestamp") == 0)
955
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->verifyRequestTimestamp, NULL);
956
38
                else if(strcmp(field, "allowEmptyVariables") == 0)
957
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_RULEHANDLING](&ctx, &config->allowEmptyVariables, NULL);
958
38
                else if(strcmp(field, "serverUrls") == 0)
959
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_STRINGARRAY](&ctx, &config->serverUrls, &config->serverUrlsSize);
960
38
                else if(strcmp(field, "tcpEnabled") == 0)
961
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->tcpEnabled, NULL);
962
38
                else if(strcmp(field, "tcp") == 0)
963
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_TCPCONFIGURATION](&ctx, config, NULL);
964
38
                else if(strcmp(field, "securityPolicyNoneDiscoveryOnly") == 0)
965
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->securityPolicyNoneDiscoveryOnly, NULL);
966
38
                else if(strcmp(field, "modellingRulesOnInstances") == 0)
967
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->modellingRulesOnInstances, NULL);
968
38
                else if(strcmp(field, "maxSecureChannels") == 0)
969
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSecureChannels, NULL);
970
38
                else if(strcmp(field, "maxSecurityTokenLifetime") == 0)
971
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxSecurityTokenLifetime, NULL);
972
38
                else if(strcmp(field, "maxSessions") == 0)
973
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT16](&ctx, &config->maxSessions, NULL);
974
38
                else if(strcmp(field, "maxSessionTimeout") == 0)
975
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->maxSessionTimeout, NULL);
976
38
                else if(strcmp(field, "maxNodesPerRead") == 0)
977
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRead, NULL);
978
38
                else if(strcmp(field, "maxNodesPerWrite") == 0)
979
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerWrite, NULL);
980
38
                else if(strcmp(field, "maxNodesPerMethodCall") == 0)
981
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerMethodCall, NULL);
982
38
                else if(strcmp(field, "maxNodesPerBrowse") == 0)
983
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerBrowse, NULL);
984
38
                else if(strcmp(field, "maxNodesPerRegisterNodes") == 0)
985
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerRegisterNodes, NULL);
986
38
                else if(strcmp(field, "maxNodesPerTranslateBrowsePathsToNodeIds") == 0)
987
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerTranslateBrowsePathsToNodeIds, NULL);
988
38
                else if(strcmp(field, "maxNodesPerNodeManagement") == 0)
989
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxNodesPerNodeManagement, NULL);
990
38
                else if(strcmp(field, "maxMonitoredItemsPerCall") == 0)
991
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxMonitoredItemsPerCall, NULL);
992
38
                else if(strcmp(field, "maxReferencesPerNode") == 0)
993
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->maxReferencesPerNode, NULL);
994
38
                else if(strcmp(field, "reverseReconnectInterval") == 0)
995
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->reverseReconnectInterval, NULL);
996
997
38
#if UA_MULTITHREADING >= 100
998
38
                else if(strcmp(field, "asyncOperationTimeout") == 0)
999
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_DOUBLE](&ctx, &config->asyncOperationTimeout, NULL);
1000
38
                else if(strcmp(field, "maxAsyncOperationQueueSize") == 0)
1001
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT64](&ctx, &config->maxAsyncOperationQueueSize, NULL);
1002
38
#endif
1003
1004
38
#ifdef UA_ENABLE_DISCOVERY
1005
38
                else if(strcmp(field, "discoveryCleanupTimeout") == 0)
1006
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_UINT32](&ctx, &config->discoveryCleanupTimeout, NULL);
1007
38
#ifdef UA_ENABLE_DISCOVERY_MULTICAST
1008
38
                else if(strcmp(field, "mdnsEnabled") == 0)
1009
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->mdnsEnabled, NULL);
1010
38
                else if(strcmp(field, "mdns") == 0)
1011
36
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_MDNSCONFIGURATION](&ctx, config, NULL);
1012
2
#endif
1013
2
#endif
1014
1015
2
#ifdef UA_ENABLE_SUBSCRIPTIONS
1016
2
                else if(strcmp(field, "subscriptionsEnabled") == 0)
1017
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->subscriptionsEnabled, NULL);
1018
2
                else if(strcmp(field, "subscriptions") == 0)
1019
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SUBSCRIPTIONCONFIGURATION](&ctx, config, NULL);
1020
2
# endif
1021
1022
2
#ifdef UA_ENABLE_HISTORIZING
1023
2
                else if(strcmp(field, "historizingEnabled") == 0)
1024
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->historizingEnabled, NULL);
1025
2
                else if(strcmp(field, "historizing") == 0)
1026
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_HISTORIZINGCONFIGURATION](&ctx, config, NULL);
1027
2
#endif
1028
1029
2
#ifdef UA_ENABLE_PUBSUB
1030
2
                else if(strcmp(field, "pubsubEnabled") == 0)
1031
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_BOOLEAN](&ctx, &config->pubsubEnabled, NULL);
1032
2
                else if(strcmp(field, "pubsub") == 0)
1033
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_PUBSUBCONFIGURATION](&ctx, &config->pubSubConfig, NULL);
1034
2
#endif
1035
2
#ifdef UA_ENABLE_ENCRYPTION
1036
2
                else if(strcmp(field, "securityPolicies") == 0)
1037
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPOLICIES](&ctx, config, NULL);
1038
2
                else if(strcmp(field, "pkiFolder") == 0)
1039
0
                    retval = parseJsonJumpTable[UA_SERVERCONFIGFIELD_SECURITYPKI](&ctx, config, NULL);
1040
2
#endif
1041
2
                else {
1042
2
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1043
2
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled.", field);
1044
                    /* skip the name of item */
1045
2
                    ++ctx.index;
1046
                    /* skip value of unknown item */
1047
2
                    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
2
                    --ctx.index;
1052
2
                }
1053
38
                if(retval != UA_STATUSCODE_GOOD) {
1054
0
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1055
0
                                 "An error occurred while parsing the configuration field %s", field);
1056
0
                    return retval;
1057
0
                }
1058
38
                break;
1059
38
            }
1060
214
            default:
1061
214
                break;
1062
396
        }
1063
252
        ctx.index += 1;
1064
252
    }
1065
15
    return retval;
1066
15
}
1067
1068
UA_Server *
1069
3.19k
UA_Server_newFromFile(const UA_ByteString json_config) {
1070
3.19k
    UA_ServerConfig config;
1071
3.19k
    memset(&config, 0, sizeof(UA_ServerConfig));
1072
3.19k
    UA_StatusCode res = UA_ServerConfig_setDefault(&config);
1073
3.19k
    res |= parseJSONConfig(&config, json_config);
1074
3.19k
    if(res != UA_STATUSCODE_GOOD) {
1075
948
        UA_ServerConfig_clear(&config);
1076
948
        return NULL;
1077
948
    }
1078
2.24k
    return UA_Server_newWithConfig(&config);
1079
3.19k
}
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