Coverage Report

Created: 2026-08-13 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541/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
 *    Copyright 2026 (c) o6 Automation GmbH (Author: Moritz Bruder)
7
 */
8
9
#include <open62541/plugin/log.h>
10
#include <open62541/server.h>
11
#include <open62541/client.h>
12
#include "cj5.h"
13
#include "open62541/server_config_default.h"
14
#include "open62541/client_config_default.h"
15
#include "open62541/plugin/securitypolicy_default.h"
16
#ifdef UA_ENABLE_ENCRYPTION
17
#include "open62541/plugin/certificategroup_default.h"
18
#endif
19
#if defined(UA_ENABLE_ENCRYPTION) || defined(UA_ENABLE_LWS)
20
#include <stdio.h>
21
#include <errno.h>
22
#endif
23
24
0
#define MAX_TOKENS 1024
25
26
#define LOG_UNKNOWN_FIELD(ctx, field) \
27
0
    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Unknown field name '%s'.", field)
28
29
typedef struct {
30
    const char *json;
31
    unsigned int index;
32
    UA_Byte depth;
33
    cj5_result result;
34
35
    UA_Logger *logging;
36
} ParsingCtx;
37
38
static UA_ByteString
39
0
getJsonPart(cj5_token tok, const char *json) {
40
0
    UA_ByteString bs;
41
0
    UA_ByteString_init(&bs);
42
0
    if(tok.type == CJ5_TOKEN_STRING) {
43
0
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start - 1);
44
0
        bs.length = (tok.end - tok.start) + 3;
45
0
        return bs;
46
0
    } else {
47
0
        bs.data = (UA_Byte*)(uintptr_t)(json + tok.start);
48
0
        bs.length = (tok.end - tok.start) + 1;
49
0
        return bs;
50
0
    }
51
0
}
52
53
/* Advance to the next token without reading beyond the parser output. A
54
 * malformed child count can otherwise desynchronize the token walk. */
55
static cj5_token
56
0
nextToken(ParsingCtx *ctx) {
57
0
    if(!ctx->result.tokens || ctx->index + 1 >= ctx->result.num_tokens) {
58
0
        ctx->index = ctx->result.num_tokens;
59
0
        cj5_token empty;
60
0
        memset(&empty, 0, sizeof(empty));
61
0
        return empty;
62
0
    }
63
0
    ctx->index++;
64
0
    return ctx->result.tokens[ctx->index];
65
0
}
66
67
/* Forward declarations*/
68
#define PARSE_JSON(TYPE) static UA_StatusCode                   \
69
    TYPE##_parseJson(ParsingCtx *ctx, void *configField, size_t *configFieldSize)
70
71
typedef UA_StatusCode
72
(*parseJsonSignature)(ParsingCtx *ctx, void *configField, size_t *configFieldSize);
73
74
#if defined(UA_ENABLE_ENCRYPTION) || defined(UA_ENABLE_LWS)
75
static UA_ByteString
76
loadCertificateFile(const char *const path);
77
#endif
78
79
/*----------------------Basic Types------------------------*/
80
#if 0
81
PARSE_JSON(Int64Field) {
82
    cj5_token tok = nextToken(ctx);
83
    UA_ByteString buf = getJsonPart(tok, ctx->json);
84
    UA_Int64 out;
85
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_INT64], NULL);
86
    if(retval != UA_STATUSCODE_GOOD)
87
        return retval;
88
    UA_Int64 *field = (UA_Int64*)configField;
89
    *field = out;
90
    return retval;
91
}
92
#endif
93
0
PARSE_JSON(ByteField) {
94
0
    cj5_token tok = nextToken(ctx);
95
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
96
0
    UA_Byte out;
97
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_BYTE], NULL);
98
0
    if(retval != UA_STATUSCODE_GOOD)
99
0
        return retval;
100
0
    UA_Byte *field = (UA_Byte*)configField;
101
0
    *field = out;
102
0
    return retval;
103
0
}
104
0
PARSE_JSON(UInt16Field) {
105
0
    cj5_token tok = nextToken(ctx);
106
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
107
0
    UA_UInt16 out;
108
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT16], NULL);
109
0
    if(retval != UA_STATUSCODE_GOOD)
110
0
        return retval;
111
0
    UA_UInt16 *field = (UA_UInt16*)configField;
112
0
    *field = out;
113
0
    return retval;
114
0
}
115
0
PARSE_JSON(UInt32Field) {
116
0
    cj5_token tok = nextToken(ctx);
117
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
118
0
    UA_UInt32 out;
119
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
120
0
    if(retval != UA_STATUSCODE_GOOD)
121
0
        return retval;
122
0
    UA_UInt32 *field = (UA_UInt32*)configField;
123
0
    *field = out;
124
0
    return retval;
125
0
}
126
0
PARSE_JSON(UInt64Field) {
127
0
    cj5_token tok = nextToken(ctx);
128
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
129
0
    UA_UInt64 out;
130
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_UINT64], NULL);
131
0
    if(retval != UA_STATUSCODE_GOOD)
132
0
        return retval;
133
0
    UA_UInt64 *field = (UA_UInt64*)configField;
134
0
    *field = out;
135
0
    return retval;
136
0
}
137
0
PARSE_JSON(Int32Field) {
138
0
    cj5_token tok = nextToken(ctx);
139
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
140
0
    UA_Int32 out;
141
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_INT32], NULL);
142
0
    if(retval != UA_STATUSCODE_GOOD)
143
0
        return retval;
144
0
    UA_Int32 *field = (UA_Int32*)configField;
145
0
    *field = out;
146
0
    return retval;
147
0
}
148
0
PARSE_JSON(StringField) {
149
0
    cj5_token tok = nextToken(ctx);
150
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
151
0
    UA_String out;
152
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_STRING], NULL);
153
0
    if(retval != UA_STATUSCODE_GOOD)
154
0
        return retval;
155
0
    UA_String *field = (UA_String*)configField;
156
0
    if(field != NULL) {
157
0
        UA_String_clear(field);
158
0
        *field = out;
159
0
    }
160
0
    return retval;
161
0
}
162
0
PARSE_JSON(ByteStringField) {
163
0
    cj5_token tok = nextToken(ctx);
164
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
165
0
    UA_ByteString out;
166
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_BYTESTRING], NULL);
167
0
    if(retval != UA_STATUSCODE_GOOD)
168
0
        return retval;
169
0
    UA_ByteString *field = (UA_ByteString*)configField;
170
0
    *field = out;
171
0
    return retval;
172
0
}
173
0
PARSE_JSON(LocalizedTextField) {
174
    /*
175
     applicationName: {
176
        locale: "de-DE",
177
        text: "Test text"
178
    }
179
     */
180
0
    cj5_token tok = nextToken(ctx);
181
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
182
0
    UA_String locale = {.length = 0, .data = NULL};
183
0
    UA_String text = {.length = 0, .data = NULL};
184
0
    for(size_t j = tok.size/2; j > 0; j--) {
185
0
        tok = nextToken(ctx);
186
0
        switch (tok.type) {
187
0
        case CJ5_TOKEN_STRING: {
188
0
            char *field = (char*)UA_malloc(tok.size + 1);
189
0
            unsigned int str_len = 0;
190
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field, &str_len);
191
192
0
            tok = nextToken(ctx);
193
0
            UA_ByteString buf = getJsonPart(tok, ctx->json);
194
0
            if(strcmp(field, "locale") == 0)
195
0
                retval |= UA_decodeJson(&buf, &locale, &UA_TYPES[UA_TYPES_STRING], NULL);
196
0
            else if(strcmp(field, "text") == 0)
197
0
                retval |= UA_decodeJson(&buf, &text, &UA_TYPES[UA_TYPES_STRING], NULL);
198
0
            else {
199
0
                LOG_UNKNOWN_FIELD(ctx, field);
200
0
            }
201
0
            UA_free(field);
202
0
            break;
203
0
        }
204
0
        default:
205
0
            break;
206
0
        }
207
0
    }
208
0
    UA_LocalizedText out;
209
0
    out.locale = locale;
210
0
    out.text = text;
211
0
    if(retval != UA_STATUSCODE_GOOD)
212
0
        return retval;
213
0
    UA_LocalizedText *field = (UA_LocalizedText*)configField;
214
0
    if(field != NULL) {
215
0
        UA_LocalizedText_clear(field);
216
0
        *field = out;
217
0
    }
218
0
    return retval;
219
0
}
220
0
PARSE_JSON(DoubleField) {
221
0
    cj5_token tok = nextToken(ctx);
222
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
223
0
    UA_Double out;
224
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DOUBLE], NULL);
225
0
    if(retval != UA_STATUSCODE_GOOD)
226
0
        return retval;
227
0
    UA_Double *field = (UA_Double *)configField;
228
0
    *field = out;
229
0
    return retval;
230
0
}
231
0
PARSE_JSON(BooleanField) {
232
0
    cj5_token tok = nextToken(ctx);
233
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
234
0
    UA_Boolean out;
235
0
    if(tok.type != CJ5_TOKEN_BOOL) {
236
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Value of type bool expected.");
237
0
        return UA_STATUSCODE_BADTYPEMISMATCH;
238
0
    }
239
0
    const UA_String val = UA_STRING_STATIC("true");
240
0
    if(UA_String_equal(&val, &buf)) {
241
0
        out = true;
242
0
    }else {
243
0
        out = false;
244
0
    }
245
    /* set server config field */
246
0
    UA_Boolean *field = (UA_Boolean *)configField;
247
0
    *field = out;
248
0
    return UA_STATUSCODE_GOOD;
249
0
}
250
#ifdef UA_ENABLE_SUBSCRIPTIONS
251
0
PARSE_JSON(DurationField) {
252
0
    UA_Double double_value;
253
0
    UA_StatusCode retval = DoubleField_parseJson(ctx, &double_value, NULL);
254
0
    if(retval != UA_STATUSCODE_GOOD)
255
0
        return retval;
256
0
    UA_Duration *field = (UA_Duration*)configField;
257
0
    *field = (UA_Duration)double_value;
258
0
    return retval;
259
0
}
260
0
PARSE_JSON(DurationRangeField) {
261
0
    UA_DurationRange *field = (UA_DurationRange*)configField;
262
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
263
0
    cj5_token tok = nextToken(ctx);
264
0
    for(size_t j = tok.size/2; j > 0; j--) {
265
0
        tok = nextToken(ctx);
266
0
        switch (tok.type) {
267
0
        case CJ5_TOKEN_STRING: {
268
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
269
0
            unsigned int str_len = 0;
270
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
271
0
            if(strcmp(field_str, "min") == 0)
272
0
                retval = DurationField_parseJson(ctx, &field->min, NULL);
273
0
            else if(strcmp(field_str, "max") == 0)
274
0
                retval = DurationField_parseJson(ctx, &field->max, NULL);
275
0
            else {
276
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
277
0
            }
278
0
            UA_free(field_str);
279
0
            if(retval != UA_STATUSCODE_GOOD) {
280
0
                return retval;
281
0
            }
282
0
            break;
283
0
        }
284
0
        default:
285
0
            break;
286
0
        }
287
0
    }
288
0
    return UA_STATUSCODE_GOOD;
289
0
}
290
0
PARSE_JSON(UInt32RangeField) {
291
0
    UA_UInt32Range *field = (UA_UInt32Range*)configField;
292
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
293
0
    cj5_token tok = nextToken(ctx);
294
0
    for(size_t j = tok.size/2; j > 0; j--) {
295
0
        tok = nextToken(ctx);
296
0
        switch (tok.type) {
297
0
        case CJ5_TOKEN_STRING: {
298
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
299
0
            unsigned int str_len = 0;
300
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
301
0
            if(strcmp(field_str, "min") == 0)
302
0
                retval = UInt32Field_parseJson(ctx, &field->min, NULL);
303
0
            else if(strcmp(field_str, "max") == 0)
304
0
                retval = UInt32Field_parseJson(ctx, &field->max, NULL);
305
0
            else {
306
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
307
0
            }
308
0
            UA_free(field_str);
309
0
            if(retval != UA_STATUSCODE_GOOD) {
310
0
                return retval;
311
0
            }
312
0
            break;
313
0
        }
314
0
        default:
315
0
            break;
316
0
        }
317
0
    }
318
0
    return UA_STATUSCODE_GOOD;
319
0
}
320
#endif
321
322
/*----------------------Advanced Types------------------------*/
323
0
PARSE_JSON(StringArrayField) {
324
0
    if(configFieldSize == NULL) {
325
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Pointer to the array size is not set.");
326
0
        return UA_STATUSCODE_BADARGUMENTSMISSING;
327
0
    }
328
0
    cj5_token tok = nextToken(ctx);
329
0
    UA_String *stringArray = (UA_String*)UA_malloc(sizeof(UA_String) * tok.size);
330
0
    size_t stringArraySize = 0;
331
0
    for(size_t j = tok.size; j > 0; j--) {
332
0
        UA_String out = {.length = 0, .data = NULL};
333
0
        UA_StatusCode retval = StringField_parseJson(ctx, &out, NULL);
334
0
        if(retval != UA_STATUSCODE_GOOD) {
335
0
            UA_String_clear(&out);
336
0
            UA_Array_delete(stringArray, stringArraySize, &UA_TYPES[UA_TYPES_STRING]);
337
0
            return retval;
338
0
        }
339
0
        UA_String_copy(&out, &stringArray[stringArraySize++]);
340
0
        UA_String_clear(&out);
341
0
    }
342
    /* Add to the config */
343
0
    UA_String **field = (UA_String**)configField;
344
0
    if(*configFieldSize > 0) {
345
0
        UA_Array_delete(*field, *configFieldSize,
346
0
                        &UA_TYPES[UA_TYPES_STRING]);
347
0
        *field = NULL;
348
0
        *configFieldSize = 0;
349
0
    }
350
0
    UA_StatusCode retval =
351
0
        UA_Array_copy(stringArray, stringArraySize,
352
0
                      (void**)field, &UA_TYPES[UA_TYPES_STRING]);
353
0
    *configFieldSize = stringArraySize;
354
355
    /* Clean up */
356
0
    UA_Array_delete(stringArray, stringArraySize, &UA_TYPES[UA_TYPES_STRING]);
357
0
    return retval;
358
0
}
359
0
PARSE_JSON(DateTimeField) {
360
0
    cj5_token tok = nextToken(ctx);
361
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
362
0
    UA_DateTime out;
363
0
    UA_DateTime_init(&out);
364
0
    UA_StatusCode retval = UA_decodeJson(&buf, &out, &UA_TYPES[UA_TYPES_DATETIME], NULL);
365
0
    if(retval != UA_STATUSCODE_GOOD)
366
0
        return retval;
367
0
    UA_DateTime *field = (UA_DateTime*)configField;
368
0
    *field = out;
369
0
    return retval;
370
0
}
371
0
PARSE_JSON(BuildInfo) {
372
0
    UA_BuildInfo *field = (UA_BuildInfo*)configField;
373
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
374
0
    cj5_token tok = nextToken(ctx);
375
0
    for(size_t j = tok.size/2; j > 0; j--) {
376
0
        tok = nextToken(ctx);
377
0
        switch (tok.type) {
378
0
        case CJ5_TOKEN_STRING: {
379
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
380
0
            unsigned int str_len = 0;
381
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
382
0
            if(strcmp(field_str, "productUri") == 0)
383
0
                retval = StringField_parseJson(ctx, &field->productUri, NULL);
384
0
            else if(strcmp(field_str, "manufacturerName") == 0)
385
0
                retval = StringField_parseJson(ctx, &field->manufacturerName, NULL);
386
0
            else if(strcmp(field_str, "productName") == 0)
387
0
                retval = StringField_parseJson(ctx, &field->productName, NULL);
388
0
            else if(strcmp(field_str, "softwareVersion") == 0)
389
0
                retval = StringField_parseJson(ctx, &field->softwareVersion, NULL);
390
0
            else if(strcmp(field_str, "buildNumber") == 0)
391
0
                retval = StringField_parseJson(ctx, &field->buildNumber, NULL);
392
0
            else if(strcmp(field_str, "buildDate") == 0)
393
0
                retval = DateTimeField_parseJson(ctx, &field->buildDate, NULL);
394
0
            else {
395
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
396
0
            }
397
0
            UA_free(field_str);
398
0
            if(retval != UA_STATUSCODE_GOOD) {
399
0
                return retval;
400
0
            }
401
0
            break;
402
0
        }
403
0
        default:
404
0
            break;
405
0
        }
406
0
    }
407
0
    return UA_STATUSCODE_GOOD;
408
0
}
409
410
0
PARSE_JSON(ApplicationTypeField) {
411
0
    cj5_token tok = nextToken(ctx);
412
0
    UA_ByteString rawToken = getJsonPart(tok, ctx->json);
413
0
    UA_ApplicationType *field = (UA_ApplicationType*)configField;
414
0
    char *fieldStr = (char*)UA_malloc(tok.size + 1);
415
0
    unsigned int strLen = 0;
416
417
0
    if(cj5_get_str(&ctx->result, (unsigned int)ctx->index, fieldStr, &strLen) == CJ5_ERROR_NONE) {
418
0
        if(strcmp("Client", fieldStr) == 0)
419
0
            *field = UA_APPLICATIONTYPE_CLIENT;
420
0
        else if(strcmp("Server", fieldStr) == 0)
421
0
            *field = UA_APPLICATIONTYPE_SERVER;
422
0
        else if(strcmp("ClientAndServer", fieldStr) == 0)
423
0
            *field = UA_APPLICATIONTYPE_CLIENTANDSERVER;
424
0
        else if(strcmp("DiscoveryServer", fieldStr) == 0)
425
0
            *field = UA_APPLICATIONTYPE_DISCOVERYSERVER;
426
0
        else {
427
0
            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
428
0
                        "Unknown ApplicationType '%s'", fieldStr);
429
0
            UA_free(fieldStr);
430
0
            return UA_STATUSCODE_BAD;
431
0
        }
432
0
        UA_free(fieldStr);
433
0
        return UA_STATUSCODE_GOOD;
434
0
    }
435
0
    UA_free(fieldStr);
436
437
    /* Try numeric fallback */
438
0
    UA_UInt32 enumValue;
439
0
    UA_StatusCode retval = UA_decodeJson(&rawToken, &enumValue, &UA_TYPES[UA_TYPES_UINT32], NULL);
440
0
    if(retval != UA_STATUSCODE_GOOD) {
441
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
442
0
                    "Unknown ApplicationType '%S'", rawToken);
443
0
        return retval;
444
0
    }
445
0
    *field = (UA_ApplicationType)enumValue;
446
0
    return UA_STATUSCODE_GOOD;
447
0
}
448
449
typedef enum {
450
    GENERICAPPLICATIONTYPE_CLIENT,
451
    GENERICAPPLICATIONTYPE_SERVER,
452
    GENERICAPPLICATIONTYPE_ANY
453
} GenericApplicationType;
454
455
static UA_StatusCode
456
GenericApplicationDescriptionField_parseJson(ParsingCtx *ctx, void *configField, size_t *configFieldSize, GenericApplicationType type)
457
0
{
458
0
    UA_ApplicationDescription *field = (UA_ApplicationDescription*)configField;
459
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
460
0
    cj5_token tok = nextToken(ctx);
461
0
    for(size_t j = tok.size/2; j > 0; j--) {
462
0
        tok = nextToken(ctx);
463
0
        switch (tok.type) {
464
0
        case CJ5_TOKEN_STRING: {
465
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
466
0
            unsigned int str_len = 0;
467
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
468
0
            if(strcmp(field_str, "applicationUri") == 0)
469
0
                retval = StringField_parseJson(ctx, &field->applicationUri, NULL);
470
0
            else if(strcmp(field_str, "productUri") == 0)
471
0
                retval = StringField_parseJson(ctx, &field->productUri, NULL);
472
0
            else if(strcmp(field_str, "applicationName") == 0)
473
0
                retval = LocalizedTextField_parseJson(ctx, &field->applicationName, NULL);
474
0
            else if(strcmp(field_str, "applicationType") == 0 &&
475
0
                    type != GENERICAPPLICATIONTYPE_CLIENT) {
476
0
                retval = ApplicationTypeField_parseJson(ctx, &field->applicationType, NULL);
477
0
            }
478
0
            else if(strcmp(field_str, "gatewayServerUri") == 0 &&
479
0
                    type != GENERICAPPLICATIONTYPE_CLIENT)
480
0
                retval = StringField_parseJson(ctx, &field->gatewayServerUri, NULL);
481
0
            else if(strcmp(field_str, "discoveryProfileUri") == 0 &&
482
0
                    type != GENERICAPPLICATIONTYPE_CLIENT)
483
0
                retval = StringField_parseJson(ctx, &field->discoveryProfileUri, NULL);
484
0
            else if(strcmp(field_str, "discoveryUrls") == 0)
485
0
                retval = StringArrayField_parseJson(ctx, &field->discoveryUrls, &field->discoveryUrlsSize);
486
0
            else {
487
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
488
0
            }
489
0
            UA_free(field_str);
490
0
            if(retval != UA_STATUSCODE_GOOD) {
491
0
                return retval;
492
0
            }
493
0
            break;
494
0
        }
495
0
        default:
496
0
            break;
497
0
        }
498
0
    }
499
0
    if(type == GENERICAPPLICATIONTYPE_CLIENT) {
500
0
        field->applicationType = UA_APPLICATIONTYPE_CLIENT;
501
0
        field->discoveryUrlsSize = 0;
502
0
        field->discoveryUrls = NULL;
503
0
    }
504
0
    return UA_STATUSCODE_GOOD;
505
0
}
506
507
0
PARSE_JSON(MessageSecurityMode) {
508
0
    size_t index = ++ctx->index;
509
0
    cj5_token tok = ctx->result.tokens[index];
510
0
    UA_ByteString rawToken = getJsonPart(tok, ctx->json);
511
0
    UA_MessageSecurityMode *field = (UA_MessageSecurityMode*)configField;
512
0
    char *fieldStr = (char*)UA_malloc(tok.size + 1);
513
0
    unsigned int strLen = 0;
514
515
0
    if(cj5_get_str(&ctx->result, (unsigned int)ctx->index, fieldStr, &strLen) == CJ5_ERROR_NONE) {
516
0
        if(strcmp("Invalid", fieldStr) == 0)
517
0
            *field = UA_MESSAGESECURITYMODE_INVALID;
518
0
        else if(strcmp("None", fieldStr) == 0)
519
0
            *field = UA_MESSAGESECURITYMODE_NONE;
520
0
        else if(strcmp("Sign", fieldStr) == 0)
521
0
            *field = UA_MESSAGESECURITYMODE_SIGN;
522
0
        else if(strcmp("SignAndEncrypt", fieldStr) == 0)
523
0
            *field = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT;
524
0
        else {
525
0
            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
526
0
                         "Unknown MessageSecurityMode '%.*s'", (int)strLen, fieldStr);
527
0
            UA_free(fieldStr);
528
0
            return UA_STATUSCODE_BAD;
529
0
        }
530
0
        UA_free(fieldStr);
531
0
        return UA_STATUSCODE_GOOD;
532
0
    }
533
0
    UA_free(fieldStr);
534
    /* Try numeric fallback */
535
0
    UA_UInt32 enumValue;
536
0
    UA_StatusCode retval = UA_decodeJson(&rawToken, &enumValue, &UA_TYPES[UA_TYPES_UINT32], NULL);
537
0
    if(retval == UA_STATUSCODE_GOOD) {
538
0
        *field = (UA_MessageSecurityMode)enumValue;
539
0
        return UA_STATUSCODE_GOOD;
540
0
    }
541
0
    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
542
0
                    "Unknown MessageSecurityMode '%S'", rawToken);
543
0
    return UA_STATUSCODE_BAD;
544
0
}
545
546
#ifdef UA_ENABLE_SUBSCRIPTIONS
547
0
PARSE_JSON(SubscriptionConfigurationField) {
548
0
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
549
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
550
0
    cj5_token tok = nextToken(ctx);
551
0
    for(size_t j = tok.size/2; j > 0; j--) {
552
0
        tok = nextToken(ctx);
553
0
        switch (tok.type) {
554
0
        case CJ5_TOKEN_STRING: {
555
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
556
0
            unsigned int str_len = 0;
557
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
558
0
            if(strcmp(field_str, "maxSubscriptions") == 0)
559
0
                retval = UInt32Field_parseJson(ctx, &config->maxSubscriptions, NULL);
560
0
            else if(strcmp(field_str, "maxSubscriptionsPerSession") == 0)
561
0
                retval = UInt32Field_parseJson(ctx, &config->maxSubscriptionsPerSession, NULL);
562
0
            else if(strcmp(field_str, "publishingIntervalLimits") == 0)
563
0
                retval = DurationRangeField_parseJson(ctx, &config->publishingIntervalLimits, NULL);
564
0
            else if(strcmp(field_str, "lifeTimeCountLimits") == 0)
565
0
                retval = UInt32RangeField_parseJson(ctx, &config->lifeTimeCountLimits, NULL);
566
0
            else if(strcmp(field_str, "keepAliveCountLimits") == 0)
567
0
                retval = UInt32RangeField_parseJson(ctx, &config->keepAliveCountLimits, NULL);
568
0
            else if(strcmp(field_str, "maxNotificationsPerPublish") == 0)
569
0
                retval = UInt32Field_parseJson(ctx, &config->maxNotificationsPerPublish, NULL);
570
0
            else if(strcmp(field_str, "enableRetransmissionQueue") == 0)
571
0
                retval = BooleanField_parseJson(ctx, &config->enableRetransmissionQueue, NULL);
572
0
            else if(strcmp(field_str, "maxRetransmissionQueueSize") == 0)
573
0
                retval = UInt32Field_parseJson(ctx, &config->maxRetransmissionQueueSize, NULL);
574
0
# ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
575
0
            else if(strcmp(field_str, "maxEventsPerNode") == 0)
576
0
                retval = UInt32Field_parseJson(ctx, &config->maxEventsPerNode, NULL);
577
0
# endif
578
0
            else if(strcmp(field_str, "maxMonitoredItems") == 0)
579
0
                retval = UInt32Field_parseJson(ctx, &config->maxMonitoredItems, NULL);
580
0
            else if(strcmp(field_str, "maxMonitoredItemsPerSubscription") == 0)
581
0
                retval = UInt32Field_parseJson(ctx, &config->maxMonitoredItemsPerSubscription, NULL);
582
0
            else if(strcmp(field_str, "samplingIntervalLimits") == 0)
583
0
                retval = DurationRangeField_parseJson(ctx, &config->samplingIntervalLimits, NULL);
584
0
            else if(strcmp(field_str, "queueSizeLimits") == 0)
585
0
                retval = UInt32RangeField_parseJson(ctx, &config->queueSizeLimits, NULL);
586
0
            else if(strcmp(field_str, "maxPublishReqPerSession") == 0)
587
0
                retval = UInt32Field_parseJson(ctx, &config->maxPublishReqPerSession, NULL);
588
0
            else {
589
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
590
0
            }
591
0
            UA_free(field_str);
592
0
            if(retval != UA_STATUSCODE_GOOD) {
593
0
                return retval;
594
0
            }
595
0
            break;
596
0
        }
597
0
        default:
598
0
            break;
599
0
        }
600
0
    }
601
0
    return UA_STATUSCODE_GOOD;
602
0
}
603
#endif
604
605
0
PARSE_JSON(TcpConfigurationField) {
606
0
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
607
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
608
0
    cj5_token tok = nextToken(ctx);
609
0
    for(size_t j = tok.size/2; j > 0; j--) {
610
0
        tok = nextToken(ctx);
611
0
        switch (tok.type) {
612
0
        case CJ5_TOKEN_STRING: {
613
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
614
0
            unsigned int str_len = 0;
615
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
616
0
            if(strcmp(field_str, "tcpBufSize") == 0)
617
0
                retval = UInt32Field_parseJson(ctx, &config->tcpBufSize, NULL);
618
0
            else if(strcmp(field_str, "tcpMaxMsgSize") == 0)
619
0
                retval = UInt32Field_parseJson(ctx, &config->tcpMaxMsgSize, NULL);
620
0
            else if(strcmp(field_str, "tcpMaxChunks") == 0)
621
0
                retval = UInt32Field_parseJson(ctx, &config->tcpMaxChunks, NULL);
622
0
            else {
623
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
624
0
            }
625
0
            UA_free(field_str);
626
0
            if(retval != UA_STATUSCODE_GOOD) {
627
0
                return retval;
628
0
            }
629
0
            break;
630
0
        }
631
0
        default:
632
0
            break;
633
0
        }
634
0
    }
635
0
    return UA_STATUSCODE_GOOD;
636
0
}
637
638
#ifdef UA_ENABLE_PUBSUB
639
0
PARSE_JSON(PubsubConfigurationField) {
640
0
    UA_PubSubConfiguration *field = (UA_PubSubConfiguration*)configField;
641
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
642
0
    cj5_token tok = nextToken(ctx);
643
0
    for(size_t j = tok.size/2; j > 0; j--) {
644
0
        tok = nextToken(ctx);
645
0
        switch (tok.type) {
646
0
        case CJ5_TOKEN_STRING: {
647
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
648
0
            unsigned int str_len = 0;
649
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
650
0
            if(strcmp(field_str, "enableDeltaFrames") == 0)
651
0
                retval = BooleanField_parseJson(ctx, &field->enableDeltaFrames, NULL);
652
0
#ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
653
0
            else if(strcmp(field_str, "enableInformationModelMethods") == 0)
654
0
                retval = BooleanField_parseJson(ctx, &field->enableInformationModelMethods, NULL);
655
0
#endif
656
0
            else {
657
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
658
0
            }
659
0
            UA_free(field_str);
660
0
            if(retval != UA_STATUSCODE_GOOD) {
661
0
                return retval;
662
0
            }
663
0
            break;
664
0
        }
665
0
        default:
666
0
            break;
667
0
        }
668
0
    }
669
0
    return UA_STATUSCODE_GOOD;
670
0
}
671
#endif
672
673
#ifdef UA_ENABLE_HISTORIZING
674
0
PARSE_JSON(HistorizingConfigurationField) {
675
0
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
676
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
677
0
    cj5_token tok = nextToken(ctx);
678
0
    for(size_t j = tok.size/2; j > 0; j--) {
679
0
        tok = nextToken(ctx);
680
0
        switch (tok.type) {
681
0
        case CJ5_TOKEN_STRING: {
682
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
683
0
            unsigned int str_len = 0;
684
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
685
0
            if(strcmp(field_str, "accessHistoryDataCapability") == 0)
686
0
                retval = BooleanField_parseJson(ctx, &config->accessHistoryDataCapability, NULL);
687
0
            else if(strcmp(field_str, "maxReturnDataValues") == 0)
688
0
                retval = UInt32Field_parseJson(ctx, &config->maxReturnDataValues, NULL);
689
0
            else if(strcmp(field_str, "accessHistoryEventsCapability") == 0)
690
0
                retval = BooleanField_parseJson(ctx, &config->accessHistoryEventsCapability, NULL);
691
0
            else if(strcmp(field_str, "maxReturnEventValues") == 0)
692
0
                retval = UInt32Field_parseJson(ctx, &config->maxReturnEventValues, NULL);
693
0
            else if(strcmp(field_str, "insertDataCapability") == 0)
694
0
                retval = BooleanField_parseJson(ctx, &config->insertDataCapability, NULL);
695
0
            else if(strcmp(field_str, "insertEventCapability") == 0)
696
0
                retval = BooleanField_parseJson(ctx, &config->insertEventCapability, NULL);
697
0
            else if(strcmp(field_str, "insertAnnotationsCapability") == 0)
698
0
                retval = BooleanField_parseJson(ctx, &config->insertAnnotationsCapability, NULL);
699
0
            else if(strcmp(field_str, "replaceDataCapability") == 0)
700
0
                retval = BooleanField_parseJson(ctx, &config->replaceDataCapability, NULL);
701
0
            else if(strcmp(field_str, "replaceEventCapability") == 0)
702
0
                retval = BooleanField_parseJson(ctx, &config->replaceEventCapability, NULL);
703
0
            else if(strcmp(field_str, "updateDataCapability") == 0)
704
0
                retval = BooleanField_parseJson(ctx, &config->updateDataCapability, NULL);
705
0
            else if(strcmp(field_str, "updateEventCapability") == 0)
706
0
                retval = BooleanField_parseJson(ctx, &config->updateEventCapability, NULL);
707
0
            else if(strcmp(field_str, "deleteRawCapability") == 0)
708
0
                retval = BooleanField_parseJson(ctx, &config->deleteRawCapability, NULL);
709
0
            else if(strcmp(field_str, "deleteEventCapability") == 0)
710
0
                retval = BooleanField_parseJson(ctx, &config->deleteEventCapability, NULL);
711
0
            else if(strcmp(field_str, "deleteAtTimeDataCapability") == 0)
712
0
                retval = BooleanField_parseJson(ctx, &config->deleteAtTimeDataCapability, NULL);
713
0
            else {
714
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
715
0
            }
716
0
            UA_free(field_str);
717
0
            if(retval != UA_STATUSCODE_GOOD) {
718
0
                return retval;
719
0
            }
720
0
            break;
721
0
        }
722
0
        default:
723
0
            break;
724
0
        }
725
0
    }
726
0
    return UA_STATUSCODE_GOOD;
727
0
}
728
#endif
729
730
static
731
UA_StatusCode
732
UA_SecurityPolicy_initByUri(UA_SecurityPolicy *policy,
733
                            UA_ApplicationType applicationType,
734
                            const UA_String *policyUri,
735
                            const UA_ByteString *localCertificate,
736
                            const UA_ByteString *localPrivateKey,
737
0
                            const UA_Logger *logger) {
738
0
    if(!policy || !policyUri)
739
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
740
741
0
    UA_ByteString certificate = UA_BYTESTRING_NULL;
742
0
    if(localCertificate)
743
0
    certificate = *localCertificate;
744
0
#ifdef UA_ENABLE_ENCRYPTION
745
0
    UA_ByteString privateKey = UA_BYTESTRING_NULL;
746
0
    if(localPrivateKey)
747
0
        privateKey = *localPrivateKey;
748
0
#endif
749
750
0
    if(UA_String_equal(policyUri, &UA_SECURITY_POLICY_NONE_URI))
751
0
        return UA_SecurityPolicy_None(policy, certificate, logger);
752
753
0
#ifdef UA_ENABLE_ENCRYPTION
754
0
    static const UA_String basic128Rsa15Uri =
755
0
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15");
756
0
    static const UA_String basic256Uri =
757
0
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Basic256");
758
0
    static const UA_String basic256Sha256Uri =
759
0
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
760
0
    static const UA_String aes128Sha256RsaOaepUri =
761
0
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
762
0
    static const UA_String aes256Sha256RsaPssUri =
763
0
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss");
764
0
#if defined(UA_ENABLE_ENCRYPTION_OPENSSL)
765
0
    static const UA_String eccNistP256Uri =
766
0
        UA_STRING_STATIC("http://opcfoundation.org/UA/SecurityPolicy#EccNistP256");
767
0
#endif
768
769
0
    if(UA_String_equal(policyUri, &basic128Rsa15Uri))
770
0
        return UA_SecurityPolicy_Basic128Rsa15(policy, certificate, privateKey, logger);
771
0
    if(UA_String_equal(policyUri, &basic256Uri))
772
0
        return UA_SecurityPolicy_Basic256(policy, certificate, privateKey, logger);
773
0
    if(UA_String_equal(policyUri, &basic256Sha256Uri))
774
0
        return UA_SecurityPolicy_Basic256Sha256(policy, certificate, privateKey, logger);
775
0
    if(UA_String_equal(policyUri, &aes128Sha256RsaOaepUri))
776
0
        return UA_SecurityPolicy_Aes128Sha256RsaOaep(policy, certificate, privateKey, logger);
777
0
    if(UA_String_equal(policyUri, &aes256Sha256RsaPssUri))
778
0
        return UA_SecurityPolicy_Aes256Sha256RsaPss(policy, certificate, privateKey, logger);
779
0
#if defined(UA_ENABLE_ENCRYPTION_OPENSSL)
780
0
    if(UA_String_equal(policyUri, &eccNistP256Uri))
781
0
        return UA_SecurityPolicy_EccNistP256(policy, applicationType, certificate,
782
0
                                             privateKey, logger);
783
0
#endif
784
0
#endif
785
786
0
    return UA_STATUSCODE_BADNOTSUPPORTED;
787
0
}
788
789
0
PARSE_JSON(CertificateFileField) {
790
0
    UA_ByteString *certificate = (UA_ByteString*)configField;
791
0
    UA_ByteString_init(certificate);
792
0
    UA_String filename = {.length = 0, .data = NULL};
793
794
0
    UA_StatusCode retval = StringField_parseJson(ctx, &filename, NULL);
795
0
    if(retval == UA_STATUSCODE_GOOD) {
796
0
        if (filename.length > 0) {
797
0
#if defined(UA_ENABLE_ENCRYPTION) || defined(UA_ENABLE_LWS)
798
0
            char *certfile = (char *)UA_malloc(filename.length + 1);
799
0
            memcpy(certfile, filename.data, filename.length);
800
0
            certfile[filename.length] = '\0';
801
0
            *certificate = loadCertificateFile((char const *)certfile);
802
0
            UA_free(certfile);
803
0
#endif
804
0
            UA_ByteString_clear(&filename);
805
0
        } else {
806
0
            UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
807
0
                            "Certificate file path is empty.");
808
0
            retval = UA_STATUSCODE_BADINTERNALERROR;
809
0
        }
810
0
    }
811
0
    return retval;
812
0
}
813
814
#ifdef UA_ENABLE_LWS
815
PARSE_JSON(WebSocketConfigurationField) {
816
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
817
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
818
    cj5_token tok = nextToken(ctx);
819
    for(size_t j = tok.size / 2; j > 0; j--) {
820
        tok = nextToken(ctx);
821
        if(tok.type != CJ5_TOKEN_STRING)
822
            continue;
823
824
        char *field = (char*)UA_malloc(tok.size + 1);
825
        if(!field)
826
            return UA_STATUSCODE_BADOUTOFMEMORY;
827
        unsigned int strLen = 0;
828
        cj5_get_str(&ctx->result, (unsigned int)ctx->index, field, &strLen);
829
830
        if(strcmp(field, "webSocketBufSize") == 0) {
831
            retval = UInt32Field_parseJson(ctx, &config->webSocketBufSize, NULL);
832
        } else if(strcmp(field, "webSocketMaxMsgSize") == 0) {
833
            retval = UInt32Field_parseJson(ctx, &config->webSocketMaxMsgSize, NULL);
834
        } else if(strcmp(field, "webSocketMaxChunks") == 0) {
835
            retval = UInt32Field_parseJson(ctx, &config->webSocketMaxChunks, NULL);
836
        } else if(strcmp(field, "webSocketMaxQueueSize") == 0) {
837
            retval = UInt32Field_parseJson(ctx, &config->webSocketMaxQueueSize, NULL);
838
        } else if(strcmp(field, "certificate") == 0) {
839
            UA_ByteString_clear(&config->webSocketCertificate);
840
            retval = CertificateFileField_parseJson(
841
                ctx, &config->webSocketCertificate, NULL);
842
        } else if(strcmp(field, "privateKey") == 0) {
843
            UA_ByteString_clear(&config->webSocketPrivateKey);
844
            retval = CertificateFileField_parseJson(
845
                ctx, &config->webSocketPrivateKey, NULL);
846
        } else if(strcmp(field, "privateKeyPassword") == 0) {
847
            UA_String_clear(&config->webSocketPrivateKeyPassword);
848
            retval = StringField_parseJson(
849
                ctx, &config->webSocketPrivateKeyPassword, NULL);
850
        } else {
851
            LOG_UNKNOWN_FIELD(ctx, field);
852
        }
853
        UA_free(field);
854
        if(retval != UA_STATUSCODE_GOOD)
855
            return retval;
856
    }
857
    return UA_STATUSCODE_GOOD;
858
}
859
#endif
860
861
static UA_StatusCode
862
SecurityPolicyField_parseJson(ParsingCtx *ctx, UA_SecurityPolicy *field,
863
                              UA_ApplicationType applicationType,
864
0
                              const UA_Logger *logger) {
865
0
    UA_String policy = {.length = 0, .data = NULL};
866
0
    UA_ByteString certificate = {.length = 0, .data = NULL};
867
0
    UA_ByteString privateKey = {.length = 0, .data = NULL};
868
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
869
870
0
    cj5_token tok = nextToken(ctx);
871
0
    for(size_t i = tok.size / 2; i > 0 && retval == UA_STATUSCODE_GOOD; i--) {
872
0
        tok = nextToken(ctx);
873
0
        switch(tok.type) {
874
0
        case CJ5_TOKEN_STRING: {
875
0
            char *field_str = (char *)UA_malloc(tok.size + 1);
876
0
            unsigned int str_len = 0;
877
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
878
0
            if(strcmp(field_str, "certificate") == 0) {
879
0
                retval = CertificateFileField_parseJson(ctx, &certificate, NULL);
880
0
            } else if(strcmp(field_str, "privateKey") == 0) {
881
0
                retval = CertificateFileField_parseJson(ctx, &privateKey, NULL);
882
0
            } else if(strcmp(field_str, "policy") == 0) {
883
0
                retval = StringField_parseJson(ctx, &policy, NULL);
884
0
            } else {
885
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
886
0
            }
887
0
            UA_free(field_str);
888
0
            break;
889
0
        }
890
0
        default:
891
0
            break;
892
0
        }
893
0
    }
894
895
0
    if(retval == UA_STATUSCODE_GOOD) {
896
0
        retval = UA_SecurityPolicy_initByUri(field, applicationType, &policy,
897
0
                                             &certificate, &privateKey, logger);
898
0
    }
899
900
0
    if(policy.length > 0)
901
0
        UA_String_clear(&policy);
902
0
    if(certificate.length > 0)
903
0
        UA_ByteString_clear(&certificate);
904
0
    if(privateKey.length > 0)
905
0
        UA_ByteString_clear(&privateKey);
906
907
0
    return retval;
908
0
}
909
910
static UA_StatusCode
911
SecurityPoliciesField_parseJson(ParsingCtx *ctx, void *configField, size_t *configFieldSize, UA_ApplicationType applicationType,
912
0
                                const UA_Logger *logger) {
913
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
914
0
    UA_SecurityPolicy **securityPoliciesField = (UA_SecurityPolicy**)configField;
915
916
0
    cj5_token tok = nextToken(ctx);
917
0
    for(size_t j = tok.size; j > 0; j--) {
918
0
        UA_SecurityPolicy *tmp = (UA_SecurityPolicy*)
919
0
            UA_realloc(*securityPoliciesField, sizeof(UA_SecurityPolicy) * (*configFieldSize + 1));
920
0
        if(!tmp) {
921
0
            retval = UA_STATUSCODE_BADOUTOFMEMORY;
922
0
            break;
923
0
        }
924
0
        *securityPoliciesField = tmp;
925
0
        retval = SecurityPolicyField_parseJson(ctx, &tmp[*configFieldSize],
926
0
                                               applicationType, logger);
927
0
        if(retval != UA_STATUSCODE_GOOD) {
928
0
            if(*configFieldSize == 0) {
929
0
                UA_free(*securityPoliciesField);
930
0
                *securityPoliciesField = NULL;
931
0
            }
932
0
            break;
933
0
        }
934
0
        (*configFieldSize)++;
935
0
    }
936
0
    return retval;
937
0
}
938
939
#ifdef UA_ENABLE_ENCRYPTION
940
0
PARSE_JSON(SecurityPkiField) {
941
0
    UA_ServerConfig *config = (UA_ServerConfig*)configField;
942
0
    UA_String pkiFolder = {.length = 0, .data = NULL};
943
944
0
    cj5_token tok = nextToken(ctx);
945
0
    UA_ByteString buf = getJsonPart(tok, ctx->json);
946
0
    UA_StatusCode retval = UA_decodeJson(&buf, &pkiFolder, &UA_TYPES[UA_TYPES_STRING], NULL);
947
0
    if(retval != UA_STATUSCODE_GOOD)
948
0
        return retval;
949
950
0
#if defined(__linux__) || defined(UA_ARCHITECTURE_WIN32) || defined(__APPLE__) || defined(__OpenBSD__)
951
    /* Set up the parameters for the filestore certificate store */
952
0
    UA_KeyValuePair params[2];
953
0
    size_t paramsSize = 2;
954
955
0
    params[0].key = UA_QUALIFIEDNAME(0, "max-trust-listsize");
956
0
    UA_Variant_setScalar(&params[0].value, &config->maxTrustListSize, &UA_TYPES[UA_TYPES_UINT32]);
957
0
    params[1].key = UA_QUALIFIEDNAME(0, "max-rejected-listsize");
958
0
    UA_Variant_setScalar(&params[1].value, &config->maxRejectedListSize, &UA_TYPES[UA_TYPES_UINT32]);
959
960
0
    UA_KeyValueMap paramsMap;
961
0
    paramsMap.map = params;
962
0
    paramsMap.mapSize = paramsSize;
963
964
    /* set server config field */
965
0
    UA_NodeId defaultApplicationGroup =
966
0
           UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTAPPLICATIONGROUP);
967
0
    retval = UA_CertificateGroup_Filestore(&config->secureChannelPKI, &defaultApplicationGroup,
968
0
                                           pkiFolder, config->logging, &paramsMap);
969
0
    if(retval != UA_STATUSCODE_GOOD) {
970
0
        UA_String_clear(&pkiFolder);
971
0
        return retval;
972
0
    }
973
974
0
    UA_NodeId defaultUserTokenGroup =
975
0
            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERCONFIGURATION_CERTIFICATEGROUPS_DEFAULTUSERTOKENGROUP);
976
0
    retval = UA_CertificateGroup_Filestore(&config->sessionPKI, &defaultUserTokenGroup,
977
0
                                            pkiFolder, config->logging, &paramsMap);
978
0
    if(retval != UA_STATUSCODE_GOOD) {
979
0
        UA_String_clear(&pkiFolder);
980
0
        return retval;
981
0
    }
982
983
    /* Clean up */
984
0
    UA_String_clear(&pkiFolder);
985
#else
986
    (void)config;
987
    UA_LOG_WARNING(ctx->logging, UA_LOGCATEGORY_APPLICATION,
988
                   "pkiFolder is not supported on this platform. "
989
                   "Trusted clients will not be verified.");
990
#endif
991
0
    return UA_STATUSCODE_GOOD;
992
0
}
993
#endif
994
995
0
PARSE_JSON(RuleHandlingField) {
996
0
    UA_UInt32 enum_value;
997
0
    UA_StatusCode retval = UInt32Field_parseJson(ctx, &enum_value, NULL);
998
0
    if(retval != UA_STATUSCODE_GOOD)
999
0
        return retval;
1000
0
    UA_RuleHandling *field = (UA_RuleHandling*)configField;
1001
0
    *field = (UA_RuleHandling)enum_value;
1002
0
    return retval;
1003
0
}
1004
1005
/* Skips unknown item (simple, object or array) in config file.
1006
* Unknown items may happen if we don't support some features.
1007
* E.g. if  UA_ENABLE_ENCRYPTION is not defined and config file
1008
* contains "securityPolicies" entry.
1009
*/
1010
static void
1011
0
skipUnknownItem(ParsingCtx* ctx) {
1012
0
    cj5_skip(&ctx->result, &ctx->index);
1013
0
}
1014
1015
static UA_StatusCode
1016
0
parseJSONServerConfig(UA_ServerConfig *config, UA_ByteString json_config) {
1017
    // Parsing json config
1018
0
    const char *json = (const char*)json_config.data;
1019
0
    cj5_token tokens[MAX_TOKENS];
1020
0
    cj5_result r = cj5_parse(json, (unsigned int)json_config.length, tokens, MAX_TOKENS, NULL);
1021
1022
    /* Validate the parse result: must succeed and produce a root object
1023
     * with at least one key-value pair (i.e. >= 2 tokens). */
1024
0
    if(r.error != CJ5_ERROR_NONE || r.num_tokens < 2 ||
1025
0
       r.tokens[0].type != CJ5_TOKEN_OBJECT)
1026
0
        return UA_STATUSCODE_BADDECODINGERROR;
1027
1028
0
    ParsingCtx ctx;
1029
0
    ctx.json = json;
1030
0
    ctx.result = r;
1031
0
    ctx.index = 1; // The first token is ignored because it is known and not needed.
1032
1033
0
    ctx.logging = config->logging;
1034
1035
    /* Buffer for the field name */
1036
0
    char field[256];
1037
1038
0
    size_t serverConfigSize = 0;
1039
0
    if(ctx.result.tokens)
1040
0
        serverConfigSize = (ctx.result.tokens[ctx.index-1].size/2);
1041
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1042
0
    for (size_t j = serverConfigSize; j > 0 && ctx.index < ctx.result.num_tokens; j--) {
1043
0
        cj5_token tok = ctx.result.tokens[ctx.index];
1044
0
        switch (tok.type) {
1045
0
            case CJ5_TOKEN_STRING: {
1046
0
                if(tok.size >= 255) {
1047
0
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1048
0
                                   "Configuration field name too long");
1049
0
                    continue;
1050
0
                }
1051
0
                unsigned int str_len = 0;
1052
0
                cj5_error_code res = cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
1053
0
                if(res != CJ5_ERROR_NONE) {
1054
0
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1055
0
                                   "Configuration field name not a valid string");
1056
0
                    continue;
1057
0
                }
1058
0
                if(strcmp(field, "buildInfo") == 0)
1059
0
                    retval = BuildInfo_parseJson(&ctx, &config->buildInfo, NULL);
1060
0
                else if(strcmp(field, "applicationDescription") == 0)
1061
0
                    retval = GenericApplicationDescriptionField_parseJson(&ctx, &config->applicationDescription, NULL, GENERICAPPLICATIONTYPE_SERVER);
1062
0
                else if(strcmp(field, "shutdownDelay") == 0)
1063
0
                    retval = DoubleField_parseJson(&ctx, &config->shutdownDelay, NULL);
1064
0
                else if(strcmp(field, "verifyRequestTimestamp") == 0)
1065
0
                    retval = RuleHandlingField_parseJson(&ctx, &config->verifyRequestTimestamp, NULL);
1066
0
                else if(strcmp(field, "allowEmptyVariables") == 0)
1067
0
                    retval = RuleHandlingField_parseJson(&ctx, &config->allowEmptyVariables, NULL);
1068
0
                else if(strcmp(field, "certificateEkuRule") == 0)
1069
0
                    retval = RuleHandlingField_parseJson(&ctx, &config->certificateEkuRule, NULL);
1070
0
                else if(strcmp(field, "serverUrls") == 0)
1071
0
                    retval = StringArrayField_parseJson(&ctx, &config->serverUrls, &config->serverUrlsSize);
1072
0
                else if(strcmp(field, "tcpEnabled") == 0)
1073
0
                    retval = BooleanField_parseJson(&ctx, &config->tcpEnabled, NULL);
1074
0
                else if(strcmp(field, "tcp") == 0)
1075
0
                    retval = TcpConfigurationField_parseJson(&ctx, config, NULL);
1076
#ifdef UA_ENABLE_LWS
1077
                else if(strcmp(field, "webSocketEnabled") == 0)
1078
                    retval = BooleanField_parseJson(&ctx, &config->webSocketEnabled, NULL);
1079
                else if(strcmp(field, "webSocket") == 0)
1080
                    retval = WebSocketConfigurationField_parseJson(&ctx, config, NULL);
1081
#endif
1082
0
                else if(strcmp(field, "securityPolicyNoneDiscoveryOnly") == 0)
1083
0
                    retval = BooleanField_parseJson(&ctx, &config->securityPolicyNoneDiscoveryOnly, NULL);
1084
0
                else if(strcmp(field, "modellingRulesOnInstances") == 0)
1085
0
                    retval = BooleanField_parseJson(&ctx, &config->modellingRulesOnInstances, NULL);
1086
0
                else if(strcmp(field, "copyMethodsOnInstances") == 0)
1087
0
                    retval = BooleanField_parseJson(&ctx, &config->copyMethodsOnInstances, NULL);
1088
0
                else if(strcmp(field, "maxSecureChannels") == 0)
1089
0
                    retval = UInt16Field_parseJson(&ctx, &config->maxSecureChannels, NULL);
1090
0
                else if(strcmp(field, "maxSecurityTokenLifetime") == 0)
1091
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxSecurityTokenLifetime, NULL);
1092
0
                else if(strcmp(field, "maxSessions") == 0)
1093
0
                    retval = UInt16Field_parseJson(&ctx, &config->maxSessions, NULL);
1094
0
                else if(strcmp(field, "maxSessionTimeout") == 0)
1095
0
                    retval = DoubleField_parseJson(&ctx, &config->maxSessionTimeout, NULL);
1096
0
                else if(strcmp(field, "maxNodesPerRead") == 0)
1097
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerRead, NULL);
1098
0
                else if(strcmp(field, "maxNodesPerWrite") == 0)
1099
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerWrite, NULL);
1100
0
                else if(strcmp(field, "maxNodesPerMethodCall") == 0)
1101
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerMethodCall, NULL);
1102
0
                else if(strcmp(field, "maxNodesPerBrowse") == 0)
1103
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerBrowse, NULL);
1104
0
                else if(strcmp(field, "maxNodesPerRegisterNodes") == 0)
1105
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerRegisterNodes, NULL);
1106
0
                else if(strcmp(field, "maxNodesPerTranslateBrowsePathsToNodeIds") == 0)
1107
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerTranslateBrowsePathsToNodeIds, NULL);
1108
0
                else if(strcmp(field, "maxNodesPerNodeManagement") == 0)
1109
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxNodesPerNodeManagement, NULL);
1110
0
                else if(strcmp(field, "maxMonitoredItemsPerCall") == 0)
1111
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxMonitoredItemsPerCall, NULL);
1112
0
                else if(strcmp(field, "maxReferencesPerNode") == 0)
1113
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxReferencesPerNode, NULL);
1114
0
                else if(strcmp(field, "reverseReconnectInterval") == 0)
1115
0
                    retval = UInt32Field_parseJson(&ctx, &config->reverseReconnectInterval, NULL);
1116
1117
0
#if UA_MULTITHREADING >= 100
1118
0
                else if(strcmp(field, "asyncOperationTimeout") == 0)
1119
0
                    retval = DoubleField_parseJson(&ctx, &config->asyncOperationTimeout, NULL);
1120
0
                else if(strcmp(field, "maxAsyncOperationQueueSize") == 0)
1121
0
                    retval = UInt64Field_parseJson(&ctx, &config->maxAsyncOperationQueueSize, NULL);
1122
0
#endif
1123
1124
0
#ifdef UA_ENABLE_DISCOVERY
1125
0
                else if(strcmp(field, "registeredServersEnabled") == 0)
1126
0
                    retval = BooleanField_parseJson(&ctx, &config->registeredServersEnabled, NULL);
1127
0
                else if(strcmp(field, "registeredServerCleanupTimeout") == 0)
1128
0
                    retval = UInt32Field_parseJson(&ctx, &config->registeredServerCleanupTimeout, NULL);
1129
0
                else if(strcmp(field, "serversOnNetworkEnabled") == 0)
1130
0
                    retval = BooleanField_parseJson(&ctx, &config->serversOnNetworkEnabled, NULL);
1131
0
#endif
1132
1133
0
#ifdef UA_ENABLE_SUBSCRIPTIONS
1134
0
                else if(strcmp(field, "subscriptionsEnabled") == 0)
1135
0
                    retval = BooleanField_parseJson(&ctx, &config->subscriptionsEnabled, NULL);
1136
0
                else if(strcmp(field, "subscriptions") == 0)
1137
0
                    retval = SubscriptionConfigurationField_parseJson(&ctx, config, NULL);
1138
0
# endif
1139
1140
0
#ifdef UA_ENABLE_HISTORIZING
1141
0
                else if(strcmp(field, "historizingEnabled") == 0)
1142
0
                    retval = BooleanField_parseJson(&ctx, &config->historizingEnabled, NULL);
1143
0
                else if(strcmp(field, "historizing") == 0)
1144
0
                    retval = HistorizingConfigurationField_parseJson(&ctx, config, NULL);
1145
0
#endif
1146
1147
0
#ifdef UA_ENABLE_PUBSUB
1148
0
                else if(strcmp(field, "pubsubEnabled") == 0)
1149
0
                    retval = BooleanField_parseJson(&ctx, &config->pubsubEnabled, NULL);
1150
0
                else if(strcmp(field, "pubsub") == 0)
1151
0
                    retval = PubsubConfigurationField_parseJson(&ctx, &config->pubSubConfig, NULL);
1152
0
#endif
1153
0
#ifdef UA_ENABLE_ENCRYPTION
1154
0
                else if(strcmp(field, "securityPolicies") == 0)
1155
0
                    retval = SecurityPoliciesField_parseJson(&ctx, &config->securityPolicies, &config->securityPoliciesSize, UA_APPLICATIONTYPE_SERVER, config->logging);
1156
0
                else if(strcmp(field, "pkiFolder") == 0)
1157
0
                    retval = SecurityPkiField_parseJson(&ctx, config, NULL);
1158
0
#endif
1159
0
                else {
1160
0
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1161
0
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled.", field);
1162
                    /* skip the name of item */
1163
0
                    ++ctx.index;
1164
                    /* skip value of unknown item */
1165
0
                    skipUnknownItem(&ctx);
1166
                    /* after skipUnknownItem() ctx->index points to the name of the following item.
1167
                       We must decrement index in oder following increment will
1168
                       still set index to the right position (name of the following item) */
1169
0
                    --ctx.index;
1170
0
                }
1171
0
                if(retval != UA_STATUSCODE_GOOD) {
1172
0
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1173
0
                                 "An error occurred while parsing the configuration field %s", field);
1174
0
                    return retval;
1175
0
                }
1176
0
                break;
1177
0
            }
1178
0
            default:
1179
0
                break;
1180
0
        }
1181
0
        ctx.index += 1;
1182
0
    }
1183
0
    return retval;
1184
0
}
1185
1186
UA_Server *
1187
0
UA_Server_newFromFile(const UA_ByteString jsonConfig) {
1188
0
    UA_ServerConfig config;
1189
0
    UA_StatusCode res = UA_ServerConfig_loadFromFile(&config, jsonConfig);
1190
0
    if(res != UA_STATUSCODE_GOOD)
1191
0
        return NULL;
1192
0
    return UA_Server_newWithConfig(&config);
1193
0
}
1194
1195
UA_StatusCode
1196
0
UA_ServerConfig_loadFromFile(UA_ServerConfig *config, const UA_ByteString jsonConfig) {
1197
0
    memset(config, 0, sizeof(UA_ServerConfig));
1198
0
    UA_StatusCode res = UA_ServerConfig_setDefault(config);
1199
0
    if (res == UA_STATUSCODE_GOOD) {
1200
0
        res = parseJSONServerConfig(config, jsonConfig);
1201
0
        if (UA_StatusCode_isBad(res)) {
1202
0
            UA_ServerConfig_clear(config);
1203
0
        }
1204
0
    }
1205
0
    return res;
1206
0
}
1207
1208
0
PARSE_JSON(ConnectionConfig) {
1209
0
    UA_ConnectionConfig *field = (UA_ConnectionConfig*)configField;
1210
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1211
0
    cj5_token tok = nextToken(ctx);
1212
0
    for(size_t j = tok.size/2; j > 0; j--) {
1213
0
        tok = nextToken(ctx);
1214
0
        switch (tok.type) {
1215
0
        case CJ5_TOKEN_STRING: {
1216
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
1217
0
            unsigned int str_len = 0;
1218
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
1219
0
            if(strcmp(field_str, "protocolVersion") == 0)
1220
0
                retval = UInt32Field_parseJson(ctx, &field->protocolVersion, NULL);
1221
0
            else if(strcmp(field_str, "recvBufferSize") == 0)
1222
0
                retval = UInt32Field_parseJson(ctx, &field->recvBufferSize, NULL);
1223
0
            else if(strcmp(field_str, "sendBufferSize") == 0)
1224
0
                retval = UInt32Field_parseJson(ctx, &field->sendBufferSize, NULL);
1225
0
            else if(strcmp(field_str, "localMaxMessageSize") == 0)
1226
0
                retval = UInt32Field_parseJson(ctx, &field->localMaxMessageSize, NULL);
1227
0
            else if(strcmp(field_str, "remoteMaxMessageSize") == 0)
1228
0
                retval = UInt32Field_parseJson(ctx, &field->remoteMaxMessageSize, NULL);
1229
0
            else if(strcmp(field_str, "localMaxChunkCount") == 0)
1230
0
                retval = UInt32Field_parseJson(ctx, &field->localMaxChunkCount, NULL);
1231
0
            else if(strcmp(field_str, "remoteMaxChunkCount") == 0)
1232
0
                retval = UInt32Field_parseJson(ctx, &field->remoteMaxChunkCount, NULL);
1233
0
            else {
1234
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
1235
0
            }
1236
0
            UA_free(field_str);
1237
0
            if(retval != UA_STATUSCODE_GOOD) {
1238
0
                return retval;
1239
0
            }
1240
0
            break;
1241
0
        }
1242
0
        default:
1243
0
            break;
1244
0
        }
1245
0
    }
1246
0
    return UA_STATUSCODE_GOOD;
1247
0
}
1248
1249
0
PARSE_JSON(UserTokenType) {
1250
0
    cj5_token tok = nextToken(ctx);
1251
0
    UA_ByteString rawToken = getJsonPart(tok, ctx->json);
1252
0
    UA_UserTokenType *field = (UA_UserTokenType*)configField;
1253
0
    char *fieldStr = (char*)UA_malloc(tok.size + 1);
1254
0
    unsigned int strLen = 0;
1255
1256
0
    if(cj5_get_str(&ctx->result, (unsigned int)ctx->index, fieldStr, &strLen) == CJ5_ERROR_NONE) {
1257
0
        if(strcmp("Anonymous", fieldStr) == 0)
1258
0
            *field = UA_USERTOKENTYPE_ANONYMOUS;
1259
0
        else if(strcmp("UserName", fieldStr) == 0)
1260
0
            *field = UA_USERTOKENTYPE_USERNAME;
1261
0
        else if(strcmp("Certificate", fieldStr) == 0)
1262
0
            *field = UA_USERTOKENTYPE_CERTIFICATE;
1263
0
        else if(strcmp("IssuedToken", fieldStr) == 0)
1264
0
            *field = UA_USERTOKENTYPE_ISSUEDTOKEN;
1265
0
        else {
1266
0
            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1267
0
                        "Unknown UserTokenType '%s'", fieldStr);
1268
0
            UA_free(fieldStr);
1269
0
            return UA_STATUSCODE_BAD;
1270
0
        }
1271
0
        UA_free(fieldStr);
1272
0
        return UA_STATUSCODE_GOOD;
1273
0
    }
1274
0
    UA_free(fieldStr);
1275
1276
    /* Try numeric fallback */
1277
0
    UA_UInt32 enumValue;
1278
0
    UA_StatusCode retval = UA_decodeJson(&rawToken, &enumValue, &UA_TYPES[UA_TYPES_UINT32], NULL);
1279
0
    if(retval != UA_STATUSCODE_GOOD) {
1280
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1281
0
                    "Unknown UserTokenType '%S'", rawToken);
1282
0
        return retval;
1283
0
    }
1284
0
    *field = (UA_UserTokenType)enumValue;
1285
0
    return UA_STATUSCODE_GOOD;
1286
0
}
1287
1288
/* Parse the userIdentityToken field. This creates an ExtensionObject containing
1289
 * one of three token types: Anonymous, Username, or Certificate.
1290
 * The type can be explicitly specified via the "type" field, or inferred by
1291
 * the presence of other fields. */
1292
0
PARSE_JSON(UserIdentityToken) {
1293
0
    UA_ExtensionObject *field = (UA_ExtensionObject*)configField;
1294
0
    cj5_token tok = nextToken(ctx);
1295
1296
    /* Variables to track which fields are encountered */
1297
0
    UA_Int32 detectedType = -1; /* -1 indicates no type detected yet */
1298
0
    UA_Boolean typeExplicitlySet = false;
1299
1300
    /* Temporary storage for token fields */
1301
0
    UA_AnonymousIdentityToken anonToken;
1302
0
    UA_UserNameIdentityToken userNameToken;
1303
0
    UA_X509IdentityToken certToken;
1304
1305
0
    UA_AnonymousIdentityToken_init(&anonToken);
1306
0
    UA_UserNameIdentityToken_init(&userNameToken);
1307
0
    UA_X509IdentityToken_init(&certToken);
1308
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1309
1310
0
    for(size_t j = tok.size/2; j > 0 && retval == UA_STATUSCODE_GOOD; j--) {
1311
0
        tok = nextToken(ctx);
1312
0
        switch (tok.type) {
1313
0
        case CJ5_TOKEN_STRING: {
1314
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
1315
0
            unsigned int str_len = 0;
1316
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
1317
1318
0
            if(strcmp(field_str, "type") == 0) {
1319
1320
0
                if (detectedType != -1) {
1321
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1322
0
                                 "Inconsistent type information in userIdentityToken. Type was "
1323
0
                                 "already detected as %d, but 'type' field is set to a different "
1324
0
                                 "value.",
1325
0
                                 detectedType);
1326
0
                    retval = UA_STATUSCODE_BAD;
1327
0
                }
1328
1329
0
                if (retval == UA_STATUSCODE_GOOD) {
1330
                    /* Parse the type field */
1331
0
                    cj5_token typeToken = nextToken(ctx);
1332
0
                    char *typeStr = (char*)UA_malloc(typeToken.size + 1);
1333
0
                    unsigned int typeLen = 0;
1334
1335
0
                    if(cj5_get_str(&ctx->result, (unsigned int)ctx->index, typeStr, &typeLen) == CJ5_ERROR_NONE) {
1336
0
                        if(strcmp("Anonymous", typeStr) == 0) {
1337
0
                            detectedType = UA_USERTOKENTYPE_ANONYMOUS;
1338
0
                            typeExplicitlySet = true;
1339
0
                        } else if(strcmp("UserName", typeStr) == 0) {
1340
0
                            detectedType = UA_USERTOKENTYPE_USERNAME;
1341
0
                            typeExplicitlySet = true;
1342
0
                        } else if(strcmp("Certificate", typeStr) == 0) {
1343
0
                            detectedType = UA_USERTOKENTYPE_CERTIFICATE;
1344
0
                            typeExplicitlySet = true;
1345
0
                        } else {
1346
0
                            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1347
0
                                        "Unknown userIdentityToken type '%s'", typeStr);
1348
0
                            retval = UA_STATUSCODE_BAD;
1349
0
                        }
1350
0
                    } else {
1351
0
                        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1352
0
                                    "Failed to parse userIdentityToken type field");
1353
0
                        retval = UA_STATUSCODE_BAD;
1354
0
                    }
1355
0
                    UA_free(typeStr);
1356
0
                }
1357
0
            }
1358
0
            else if(strcmp(field_str, "userName") == 0) {
1359
                /* This field locks in UserName token type */
1360
0
                if(!typeExplicitlySet && detectedType == -1) {
1361
0
                    detectedType = UA_USERTOKENTYPE_USERNAME;
1362
0
                }
1363
0
                if(detectedType != UA_USERTOKENTYPE_USERNAME) {
1364
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1365
0
                                "Field 'userName' can only be used with UserName token type");
1366
0
                    retval = UA_STATUSCODE_BAD;
1367
0
                }
1368
0
                else {
1369
0
                    retval = StringField_parseJson(ctx, &userNameToken.userName, NULL);
1370
0
                }
1371
0
            }
1372
0
            else if(strcmp(field_str, "password") == 0) {
1373
                /* This field locks in UserName token type */
1374
0
                if(!typeExplicitlySet && detectedType == -1) {
1375
0
                    detectedType = UA_USERTOKENTYPE_USERNAME;
1376
0
                }
1377
0
                if(detectedType != UA_USERTOKENTYPE_USERNAME) {
1378
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1379
0
                                "Field 'password' can only be used with UserName token type");
1380
0
                    retval = UA_STATUSCODE_BAD;
1381
0
                } else {
1382
0
                    retval = ByteStringField_parseJson(ctx, &userNameToken.password, NULL);
1383
0
                }
1384
0
            }
1385
0
            else if(strcmp(field_str, "encryptionAlgorithm") == 0) {
1386
                /* This field can be used with UserName token type */
1387
0
                if(!typeExplicitlySet && detectedType == -1) {
1388
0
                    detectedType = UA_USERTOKENTYPE_USERNAME;
1389
0
                }
1390
0
                if(detectedType != UA_USERTOKENTYPE_USERNAME) {
1391
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1392
0
                                "Field 'encryptionAlgorithm' can only be used with UserName token type");
1393
0
                    retval = UA_STATUSCODE_BAD;
1394
0
                }
1395
0
                else {
1396
0
                    retval = StringField_parseJson(ctx, &userNameToken.encryptionAlgorithm, NULL);
1397
0
                }
1398
0
            }
1399
0
            else if(strcmp(field_str, "certificateData") == 0) {
1400
                /* This field locks in Certificate token type */
1401
0
                if(!typeExplicitlySet && detectedType == -1) {
1402
0
                    detectedType = UA_USERTOKENTYPE_CERTIFICATE;
1403
0
                }
1404
0
                if(detectedType != UA_USERTOKENTYPE_CERTIFICATE) {
1405
0
                    UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1406
0
                                "Field 'certificateData' can only be used with Certificate token type");
1407
0
                    retval = UA_STATUSCODE_BAD;
1408
0
                }
1409
0
                else {
1410
0
                    retval = CertificateFileField_parseJson(ctx, &certToken.certificateData, NULL);
1411
0
                }
1412
0
            }
1413
0
            else {
1414
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
1415
0
            }
1416
0
            UA_free(field_str);
1417
0
            break;
1418
0
        }
1419
0
        default:
1420
0
            break;
1421
0
        }
1422
0
    }
1423
1424
0
    if(retval == UA_STATUSCODE_GOOD) {
1425
        /* If no type was detected, default to Anonymous */
1426
0
        if(detectedType == -1) {
1427
0
            detectedType = UA_USERTOKENTYPE_ANONYMOUS;
1428
0
        }
1429
1430
        /* Create the ExtensionObject with the appropriate token type */
1431
0
        if(detectedType == UA_USERTOKENTYPE_ANONYMOUS) {
1432
0
            retval = UA_ExtensionObject_setValueCopy(field, &anonToken,
1433
0
                                                    &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN]);
1434
0
        } else if(detectedType == UA_USERTOKENTYPE_USERNAME) {
1435
0
            retval = UA_ExtensionObject_setValueCopy(field, &userNameToken,
1436
0
                                                    &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]);
1437
0
        } else if(detectedType == UA_USERTOKENTYPE_CERTIFICATE) {
1438
0
            retval = UA_ExtensionObject_setValueCopy(field, &certToken,
1439
0
                                                    &UA_TYPES[UA_TYPES_X509IDENTITYTOKEN]);
1440
0
        } else {
1441
0
            UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1442
0
                        "Invalid userIdentityToken type");
1443
0
            retval = UA_STATUSCODE_BAD;
1444
0
        }
1445
0
    }
1446
1447
0
    UA_AnonymousIdentityToken_clear(&anonToken);
1448
0
    UA_UserNameIdentityToken_clear(&userNameToken);
1449
0
    UA_X509IdentityToken_clear(&certToken);
1450
0
    return retval;
1451
0
}
1452
1453
0
PARSE_JSON(UserTokenPolicy) {
1454
0
    UA_Boolean issuedTokenTypeFieldsUsed = false;
1455
1456
0
    UA_UserTokenPolicy *field = (UA_UserTokenPolicy*)configField;
1457
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1458
0
    cj5_token tok = nextToken(ctx);
1459
0
    for(size_t j = tok.size/2; j > 0; j--) {
1460
0
        tok = nextToken(ctx);
1461
0
        switch (tok.type) {
1462
0
        case CJ5_TOKEN_STRING: {
1463
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
1464
0
            unsigned int str_len = 0;
1465
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
1466
0
            if(strcmp(field_str, "policyId") == 0)
1467
0
                retval = StringField_parseJson(ctx, &field->policyId, NULL);
1468
0
            else if(strcmp(field_str, "tokenType") == 0)
1469
0
                retval = UserTokenType_parseJson(ctx, &field->tokenType, NULL);
1470
0
            else if(strcmp(field_str, "issuedTokenType") == 0) {
1471
0
                issuedTokenTypeFieldsUsed = true;
1472
0
                retval = StringField_parseJson(ctx, &field->issuedTokenType, NULL);
1473
0
            }
1474
0
            else if(strcmp(field_str, "issuerEndpointUrl") == 0) {
1475
0
                issuedTokenTypeFieldsUsed = true;
1476
0
                retval = StringField_parseJson(ctx, &field->issuerEndpointUrl, NULL);
1477
0
            }
1478
0
            else if(strcmp(field_str, "securityPolicyUri") == 0)
1479
0
                retval = StringField_parseJson(ctx, &field->securityPolicyUri, NULL);
1480
0
            else {
1481
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
1482
0
            }
1483
0
            UA_free(field_str);
1484
0
            if(retval != UA_STATUSCODE_GOOD) {
1485
0
                return retval;
1486
0
            }
1487
0
            break;
1488
0
        }
1489
0
        default:
1490
0
            break;
1491
0
        }
1492
0
    }
1493
0
    if(issuedTokenTypeFieldsUsed && field->tokenType != UA_USERTOKENTYPE_ISSUEDTOKEN) {
1494
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION,
1495
0
                    "Fields 'issuedTokenType' and 'issuerEndpointUrl' can only be used if tokenType is 'IssuedToken'.");
1496
0
        return UA_STATUSCODE_BAD;
1497
0
    }
1498
1499
0
    return UA_STATUSCODE_GOOD;
1500
0
}
1501
1502
0
PARSE_JSON(UserTokenPolicyArrayField) {
1503
0
    if(configFieldSize == NULL) {
1504
0
        UA_LOG_ERROR(ctx->logging, UA_LOGCATEGORY_APPLICATION, "Pointer to the array size is not set.");
1505
0
        return UA_STATUSCODE_BADARGUMENTSMISSING;
1506
0
    }
1507
0
    cj5_token tok = nextToken(ctx);
1508
0
    UA_UserTokenPolicy *policyArray = (UA_UserTokenPolicy*)UA_malloc(sizeof(UA_UserTokenPolicy) * tok.size);
1509
0
    size_t policyArraySize = 0;
1510
0
    for(size_t j = tok.size; j > 0; j--) {
1511
        /* initialize element to zeros so clear functions are safe */
1512
0
        memset(&policyArray[policyArraySize], 0, sizeof(UA_UserTokenPolicy));
1513
0
        UA_StatusCode retval = UserTokenPolicy_parseJson(ctx, &policyArray[policyArraySize], NULL);
1514
0
        if(retval != UA_STATUSCODE_GOOD) {
1515
0
            UA_Array_delete(policyArray, policyArraySize, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
1516
0
            return retval;
1517
0
        }
1518
0
        policyArraySize++;
1519
0
    }
1520
    /* Add to the config */
1521
0
    UA_UserTokenPolicy **field = (UA_UserTokenPolicy**)configField;
1522
0
    if(*configFieldSize > 0) {
1523
0
        UA_Array_delete(*field, *configFieldSize,
1524
0
                        &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
1525
0
        *field = NULL;
1526
0
        *configFieldSize = 0;
1527
0
    }
1528
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1529
0
    if(policyArraySize > 0) {
1530
0
        retval = UA_Array_copy(policyArray, policyArraySize,
1531
0
                               (void **)field, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
1532
0
        *configFieldSize = policyArraySize;
1533
0
    }
1534
1535
    /* Clean up */
1536
0
    UA_Array_delete(policyArray, policyArraySize, &UA_TYPES[UA_TYPES_USERTOKENPOLICY]);
1537
0
    return retval;
1538
0
}
1539
1540
1541
0
PARSE_JSON(EndpointDescription) {
1542
0
    UA_EndpointDescription *field = (UA_EndpointDescription*)configField;
1543
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1544
0
    cj5_token tok = nextToken(ctx);
1545
0
    for(size_t j = tok.size/2; j > 0; j--) {
1546
0
        tok = nextToken(ctx);
1547
0
        switch (tok.type) {
1548
0
        case CJ5_TOKEN_STRING: {
1549
0
            char *field_str = (char*)UA_malloc(tok.size + 1);
1550
0
            unsigned int str_len = 0;
1551
0
            cj5_get_str(&ctx->result, (unsigned int)ctx->index, field_str, &str_len);
1552
0
            if(strcmp(field_str, "endpointUrl") == 0)
1553
0
                retval = StringField_parseJson(ctx, &field->endpointUrl, NULL);
1554
0
            else if(strcmp(field_str, "server") == 0)
1555
0
                retval = GenericApplicationDescriptionField_parseJson(ctx, &field->server, NULL, GENERICAPPLICATIONTYPE_ANY);
1556
0
            else if(strcmp(field_str, "serverCertificate") == 0)
1557
0
                retval = CertificateFileField_parseJson(ctx, &field->serverCertificate, NULL);
1558
0
            else if(strcmp(field_str, "securityMode") == 0)
1559
0
                retval = MessageSecurityMode_parseJson(ctx, &field->securityMode, NULL);
1560
0
            else if(strcmp(field_str, "securityPolicyUri") == 0)
1561
0
                retval = StringField_parseJson(ctx, &field->securityPolicyUri, NULL);
1562
0
            else if(strcmp(field_str, "userIdentityTokens") == 0)
1563
0
                retval = UserTokenPolicyArrayField_parseJson(ctx, &field->userIdentityTokens, &field->userIdentityTokensSize);
1564
0
            else if(strcmp(field_str, "transportProfileUri") == 0)
1565
0
                retval = StringField_parseJson(ctx, &field->transportProfileUri, NULL);
1566
0
            else if(strcmp(field_str, "securityLevel") == 0)
1567
0
                retval = ByteField_parseJson(ctx, &field->securityLevel, NULL);
1568
0
            else {
1569
0
                LOG_UNKNOWN_FIELD(ctx, field_str);
1570
0
            }
1571
0
            UA_free(field_str);
1572
0
            if(retval != UA_STATUSCODE_GOOD) {
1573
0
                return retval;
1574
0
            }
1575
0
            break;
1576
0
        }
1577
0
        default:
1578
0
            break;
1579
0
        }
1580
0
    }
1581
0
    return UA_STATUSCODE_GOOD;
1582
0
}
1583
1584
static UA_StatusCode
1585
0
parseJSONClientConfig(UA_ClientConfig *config, UA_ByteString json_config) {
1586
    // Parsing json config
1587
0
    const char *json = (const char*)json_config.data;
1588
0
    cj5_token tokens[MAX_TOKENS];
1589
0
    cj5_result r = cj5_parse(json, (unsigned int)json_config.length, tokens, MAX_TOKENS, NULL);
1590
1591
0
    if(r.error != CJ5_ERROR_NONE || r.num_tokens < 2 ||
1592
0
       r.tokens[0].type != CJ5_TOKEN_OBJECT)
1593
0
        return UA_STATUSCODE_BADDECODINGERROR;
1594
1595
0
    ParsingCtx ctx;
1596
0
    ctx.json = json;
1597
0
    ctx.result = r;
1598
0
    ctx.index = 1; // The first token is ignored because it is known and not needed.
1599
1600
0
    ctx.logging = config->logging;
1601
1602
0
    size_t clientConfigSize = 0;
1603
0
    if(ctx.result.tokens)
1604
0
        clientConfigSize = (ctx.result.tokens[ctx.index-1].size/2);
1605
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1606
0
    for (size_t j = clientConfigSize; j > 0 && ctx.index < ctx.result.num_tokens; j--) {
1607
0
        cj5_token tok = ctx.result.tokens[ctx.index];
1608
0
        switch (tok.type) {
1609
0
            case CJ5_TOKEN_STRING: {
1610
0
                char *field = (char*)UA_malloc(tok.size + 1);
1611
0
                unsigned int str_len = 0;
1612
0
                cj5_get_str(&ctx.result, (unsigned int)ctx.index, field, &str_len);
1613
0
                if(strcmp(field, "timeout") == 0)
1614
0
                    retval = Int32Field_parseJson(&ctx, &config->timeout, NULL);
1615
0
                else if(strcmp(field, "applicationDescription") == 0)
1616
0
                    retval = GenericApplicationDescriptionField_parseJson(&ctx, &config->clientDescription, NULL, GENERICAPPLICATIONTYPE_CLIENT);
1617
0
                else if(strcmp(field, "endpointUrl") == 0)
1618
0
                    retval = StringField_parseJson(&ctx, &config->endpointUrl, NULL);
1619
0
                else if (strcmp(field, "userIdentityToken") == 0)
1620
0
                    retval = UserIdentityToken_parseJson(&ctx, &config->userIdentityToken, NULL);
1621
0
                else if(strcmp(field, "sessionName") == 0)
1622
0
                    retval = StringField_parseJson(&ctx, &config->sessionName, NULL);
1623
0
                else if(strcmp(field, "sessionLocaleIds") == 0)
1624
                    /* UA_LocaleId is an alias of UA_String */
1625
0
                    retval = StringArrayField_parseJson(&ctx, &config->sessionLocaleIds, &config->sessionLocaleIdsSize);
1626
0
                else if(strcmp(field, "noSession") == 0)
1627
0
                    retval = BooleanField_parseJson(&ctx, &config->noSession, NULL);
1628
0
                else if(strcmp(field, "noReconnect") == 0)
1629
0
                    retval = BooleanField_parseJson(&ctx, &config->noReconnect, NULL);
1630
0
                else if(strcmp(field, "noNewSession") == 0)
1631
0
                    retval = BooleanField_parseJson(&ctx, &config->noNewSession, NULL);
1632
0
                else if(strcmp(field, "secureChannelLifeTime") == 0)
1633
0
                    retval = UInt32Field_parseJson(&ctx, &config->secureChannelLifeTime, NULL);
1634
0
                else if(strcmp(field, "requestedSessionTimeout") == 0)
1635
0
                    retval = UInt32Field_parseJson(&ctx, &config->requestedSessionTimeout, NULL);
1636
0
                else if(strcmp(field, "localConnectionConfig") == 0)
1637
0
                    retval = ConnectionConfig_parseJson(&ctx, &config->localConnectionConfig, NULL);
1638
0
                else if(strcmp(field, "connectivityCheckInterval") == 0)
1639
0
                    retval = UInt32Field_parseJson(&ctx, &config->connectivityCheckInterval, NULL);
1640
0
                else if(strcmp(field, "maxAsyncServiceCalls") == 0)
1641
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxAsyncServiceCalls, NULL);
1642
0
                else if(strcmp(field, "asyncServiceCallRule") == 0)
1643
0
                    retval = RuleHandlingField_parseJson(&ctx, &config->asyncServiceCallRule, NULL);
1644
0
                else if(strcmp(field, "certificateEkuRule") == 0)
1645
0
                    retval = RuleHandlingField_parseJson(&ctx, &config->certificateEkuRule, NULL);
1646
0
                else if(strcmp(field, "tcpReuseAddr") == 0)
1647
0
                    retval = BooleanField_parseJson(&ctx, &config->tcpReuseAddr, NULL);
1648
#ifdef UA_ENABLE_LWS
1649
                else if(strcmp(field, "webSocketMaxQueueSize") == 0)
1650
                    retval = UInt32Field_parseJson(
1651
                        &ctx, &config->webSocketMaxQueueSize, NULL);
1652
                else if(strcmp(field, "webSocketCaCertificate") == 0) {
1653
                    UA_ByteString_clear(&config->webSocketCaCertificate);
1654
                    retval = CertificateFileField_parseJson(
1655
                        &ctx, &config->webSocketCaCertificate, NULL);
1656
                }
1657
#endif
1658
0
                else if(strcmp(field, "endpoint") == 0)
1659
0
                    retval = EndpointDescription_parseJson(&ctx, &config->endpoint, NULL);
1660
0
                else if(strcmp(field, "userTokenPolicy") == 0)
1661
0
                    retval = UserTokenPolicy_parseJson(&ctx, &config->userTokenPolicy, NULL);
1662
0
                else if(strcmp(field, "applicationUri") == 0)
1663
0
                    retval = StringField_parseJson(&ctx, &config->applicationUri, NULL);
1664
0
                else if(strcmp(field, "securityMode") == 0)
1665
0
                    retval = MessageSecurityMode_parseJson(&ctx, &config->securityMode, NULL);
1666
0
                else if(strcmp(field, "securityPolicyUri") == 0)
1667
0
                    retval = StringField_parseJson(&ctx, &config->securityPolicyUri, NULL);
1668
0
                else if(strcmp(field, "authSecurityPolicyUri") == 0)
1669
0
                    retval = StringField_parseJson(&ctx, &config->authSecurityPolicyUri, NULL);
1670
0
                else if(strcmp(field, "securityPolicies") == 0)
1671
0
                    retval = SecurityPoliciesField_parseJson(&ctx, &config->securityPolicies, &config->securityPoliciesSize, UA_APPLICATIONTYPE_CLIENT, config->logging);
1672
0
                else if(strcmp(field, "authSecurityPolicies") == 0)
1673
0
                    retval = SecurityPoliciesField_parseJson(&ctx, &config->authSecurityPolicies, &config->authSecurityPoliciesSize, UA_APPLICATIONTYPE_CLIENT, config->logging);
1674
0
                else if(strcmp(field, "allowNonePolicyPassword") == 0)
1675
0
                    retval = BooleanField_parseJson(&ctx, &config->allowNonePolicyPassword, NULL);
1676
0
#ifdef UA_ENABLE_ENCRYPTION
1677
0
                else if(strcmp(field, "maxTrustListSize") == 0)
1678
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxTrustListSize, NULL);
1679
0
                else if(strcmp(field, "maxRejectedListSize") == 0)
1680
0
                    retval = UInt32Field_parseJson(&ctx, &config->maxRejectedListSize, NULL);
1681
0
#endif
1682
0
                else if(strcmp(field, "namespaces") == 0)
1683
0
                    retval = StringArrayField_parseJson(&ctx, &config->namespaces, &config->namespacesSize);
1684
0
                else if(strcmp(field, "outStandingPublishRequests") == 0)
1685
0
                    retval = UInt16Field_parseJson(&ctx, &config->outStandingPublishRequests, NULL);
1686
0
                else {
1687
0
                    UA_LOG_WARNING(ctx.logging, UA_LOGCATEGORY_APPLICATION,
1688
0
                                   "Field name '%s' unknown or misspelled. Maybe the feature is not enabled either.", field);
1689
                    /* skip the name of item */
1690
0
                    ++ctx.index;
1691
                    /* skip value of unknown item */
1692
0
                    skipUnknownItem(&ctx);
1693
                    /* after skipUnknownItem() ctx->index points to the name of the following item.
1694
                       We must decrement index in oder following increment will
1695
                       still set index to the right position (name of the following item) */
1696
0
                    --ctx.index;
1697
0
                }
1698
0
                UA_free(field);
1699
0
                if(retval != UA_STATUSCODE_GOOD) {
1700
0
                    UA_LOG_ERROR(ctx.logging, UA_LOGCATEGORY_APPLICATION, "An error occurred while parsing the configuration file.");
1701
0
                    return retval;
1702
0
                }
1703
0
                break;
1704
0
            }
1705
0
            default:
1706
0
                break;
1707
0
        }
1708
0
        ctx.index += 1;
1709
0
    }
1710
0
    return retval;
1711
0
}
1712
1713
UA_Client *
1714
UA_Client_newFromFile(const UA_ByteString jsonConfig)
1715
0
{
1716
0
    UA_ClientConfig config;
1717
0
    UA_StatusCode res = UA_ClientConfig_loadFromFile(&config, jsonConfig);
1718
0
    if(res != UA_STATUSCODE_GOOD)
1719
0
        return NULL;
1720
0
    return UA_Client_newWithConfig(&config);
1721
0
}
1722
1723
UA_StatusCode
1724
UA_ClientConfig_loadFromFile(UA_ClientConfig *config, const UA_ByteString jsonConfig)
1725
0
{
1726
0
    memset(config, 0, sizeof(UA_ClientConfig));
1727
0
    UA_StatusCode res = UA_ClientConfig_setDefault(config);
1728
0
    if (res == UA_STATUSCODE_GOOD) {
1729
0
        res = parseJSONClientConfig(config, jsonConfig);
1730
0
        if (UA_StatusCode_isBad(res)) {
1731
0
            UA_ClientConfig_clear(config);
1732
0
        }
1733
0
    }
1734
0
    return res;
1735
0
}
1736
1737
#if defined(UA_ENABLE_ENCRYPTION) || defined(UA_ENABLE_LWS)
1738
static UA_ByteString
1739
0
loadCertificateFile(const char *const path) {
1740
0
    UA_ByteString fileContents = UA_BYTESTRING_NULL;
1741
1742
    /* Open the file */
1743
0
    FILE *fp = fopen(path, "rb");
1744
0
    if(!fp) {
1745
0
        errno = 0; /* We read errno also from the tcp layer... */
1746
0
        return fileContents;
1747
0
    }
1748
1749
    /* Get the file length, allocate the data and read */
1750
0
    if(fseek(fp, 0, SEEK_END) != 0) {
1751
0
        fclose(fp);
1752
0
        errno = 0;
1753
0
        return fileContents;
1754
0
    }
1755
1756
0
    long length = ftell(fp);
1757
0
    if(length < 0) {
1758
0
        fclose(fp);
1759
0
        errno = 0;
1760
0
        return fileContents;
1761
0
    }
1762
1763
0
    fileContents.length = (size_t)length;
1764
0
    fileContents.data = (UA_Byte *)UA_malloc(fileContents.length * sizeof(UA_Byte));
1765
0
    if(fileContents.data) {
1766
0
        if(fseek(fp, 0, SEEK_SET) != 0) {
1767
0
            fclose(fp);
1768
0
            UA_ByteString_clear(&fileContents);
1769
0
            errno = 0;
1770
0
            return fileContents;
1771
0
        }
1772
0
        size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
1773
0
        if(read != fileContents.length)
1774
0
            UA_ByteString_clear(&fileContents);
1775
0
    } else {
1776
0
        fileContents.length = 0;
1777
0
    }
1778
0
    fclose(fp);
1779
1780
0
    return fileContents;
1781
0
}
1782
#endif