Coverage Report

Created: 2026-05-30 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/util/ua_util.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
 *
5
 *    Copyright 2014, 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
6
 *    Copyright 2014 (c) Florian Palm
7
 *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
8
 */
9
10
/* If UA_ENABLE_INLINABLE_EXPORT is enabled, then this file is the compilation
11
 * unit for the generated code from UA_INLINABLE definitions. */
12
#define UA_INLINABLE_IMPL 1
13
14
#include <open62541/types.h>
15
#include <open62541/server.h>
16
#include <open62541/util.h>
17
#include <open62541/common.h>
18
// Not used in this translation unit, but exposes symbols that are used in other translation units
19
#include <open62541/server_config_default.h>
20
#include <open62541/transport_generated.h>
21
22
#include "ua_util_internal.h"
23
#include "pcg_basic.h"
24
#include "base64.h"
25
#include "itoa.h"
26
27
#if defined(UA_ARCHITECTURE_WIN32)
28
#include <wtypes.h>
29
#include <winbase.h>
30
#endif
31
32
#include "../../deps/parse_num.h"
33
#include "../../deps/libc_time.h"
34
35
char *securityModeNames[4] = {"Invalid", "None", "Sign", "SignAndEncrypt"};
36
37
static const char * attributeIdNames[28] = {
38
    "Invalid", "NodeId", "NodeClass", "BrowseName", "DisplayName", "Description",
39
    "WriteMask", "UserWriteMask", "IsAbstract", "Symmetric", "InverseName",
40
    "ContainsNoLoops", "EventNotifier", "Value", "DataType", "ValueRank",
41
    "ArrayDimensions", "AccessLevel", "UserAccessLevel", "MinimumSamplingInterval",
42
    "Historizing", "Executable", "UserExecutable", "DataTypeDefinition",
43
    "RolePermissions", "UserRolePermissions", "AccessRestrictions", "AccessLevelEx"
44
};
45
46
const char *
47
0
UA_AttributeId_name(UA_AttributeId attrId) {
48
0
    if(attrId < 0 || attrId > UA_ATTRIBUTEID_ACCESSLEVELEX)
49
0
        return attributeIdNames[0];
50
0
    return attributeIdNames[attrId];
51
0
}
52
53
/* OR-ing 32 goes from upper-case to lower-case */
54
UA_AttributeId
55
4.06k
UA_AttributeId_fromName(const UA_String name) {
56
22.0k
    for(size_t i = 0; i < 28; i++) {
57
21.5k
        if(strlen(attributeIdNames[i]) != name.length)
58
17.9k
            continue;
59
24.9k
        for(size_t j = 0; j < name.length; j++) {
60
21.4k
            if((attributeIdNames[i][j] | 32) != (name.data[j] | 32))
61
120
                goto next;
62
21.4k
        }
63
3.54k
        return (UA_AttributeId)i;
64
120
    next:
65
120
        continue;
66
3.66k
    }
67
516
    return UA_ATTRIBUTEID_INVALID;
68
4.06k
}
69
70
static UA_DataTypeKind
71
1.07k
typeEquivalence(const UA_DataType *t) {
72
1.07k
    UA_DataTypeKind k = (UA_DataTypeKind)t->typeKind;
73
1.07k
    if(k == UA_DATATYPEKIND_ENUM)
74
0
        return UA_DATATYPEKIND_INT32;
75
1.07k
    return k;
76
1.07k
}
77
78
void
79
536
adjustType(UA_Variant *value, const UA_DataType *targetType) {
80
    /* If the value is empty, there is nothing we can do here */
81
536
    const UA_DataType *type = value->type;
82
536
    if(!type || !targetType)
83
0
        return;
84
85
    /* A string is written to a byte array. the valuerank and array dimensions
86
     * are checked later */
87
536
    if(targetType == &UA_TYPES[UA_TYPES_BYTE] &&
88
0
       type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
89
0
       UA_Variant_isScalar(value)) {
90
0
        UA_ByteString *str = (UA_ByteString*)value->data;
91
0
        value->type = &UA_TYPES[UA_TYPES_BYTE];
92
0
        value->arrayLength = str->length;
93
0
        value->data = str->data;
94
0
        return;
95
0
    }
96
97
    /* An enum was sent as an int32, or an opaque type as a bytestring. This
98
     * is detected with the typeKind indicating the "true" datatype. */
99
536
    UA_DataTypeKind te1 = typeEquivalence(targetType);
100
536
    UA_DataTypeKind te2 = typeEquivalence(type);
101
536
    if(te1 == te2 && te1 <= UA_DATATYPEKIND_ENUM) {
102
536
        value->type = targetType;
103
536
        return;
104
536
    }
105
106
    /* Add more possible type adjustments here. What are they? */
107
536
}
108
109
size_t
110
1.47M
UA_readNumberWithBase(const UA_Byte *buf, size_t buflen, UA_UInt32 *number, UA_Byte base) {
111
1.47M
    UA_assert(buf);
112
1.47M
    UA_assert(number);
113
1.47M
    u32 n = 0;
114
1.47M
    size_t progress = 0;
115
    /* read numbers until the end or a non-number character appears */
116
26.7M
    while(progress < buflen) {
117
25.3M
        u8 c = buf[progress];
118
25.3M
        if(c >= '0' && c <= '9' && c <= '0' + (base-1))
119
25.2M
           n = (n * base) + c - '0';
120
18.6k
        else if(base > 9 && c >= 'a' && c <= 'z' && c <= 'a' + (base-11))
121
11.6k
           n = (n * base) + c-'a' + 10;
122
7.01k
        else if(base > 9 && c >= 'A' && c <= 'Z' && c <= 'A' + (base-11))
123
5.39k
           n = (n * base) + c-'A' + 10;
124
1.62k
        else
125
1.62k
           break;
126
25.3M
        ++progress;
127
25.3M
    }
128
1.47M
    *number = n;
129
1.47M
    return progress;
130
1.47M
}
131
132
size_t
133
1.45M
UA_readNumber(const UA_Byte *buf, size_t buflen, UA_UInt32 *number) {
134
1.45M
    return UA_readNumberWithBase(buf, buflen, number, 10);
135
1.45M
}
136
137
7.74k
#define UA_SCHEMAS_SIZE 4
138
2.56k
#define UA_ETH_SCHEMA_INDEX 2
139
140
static const char* schemas[UA_SCHEMAS_SIZE] = {
141
    "opc.tcp://", "opc.udp://", "opc.eth://", "opc.mqtt://"
142
};
143
144
UA_StatusCode
145
UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
146
3.17k
                    UA_UInt16 *outPort, UA_String *outPath) {
147
    /* Url must begin with "opc.tcp://" or opc.udp:// (if pubsub enabled) */
148
3.17k
    if(endpointUrl->length < 11) {
149
13
        return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
150
13
    }
151
152
    /* Which type of schema is this? */
153
3.15k
    unsigned schemaType = 0;
154
4.58k
    for(; schemaType < UA_SCHEMAS_SIZE; schemaType++) {
155
4.39k
        if(strncmp((char*)endpointUrl->data,
156
4.39k
                   schemas[schemaType],
157
4.39k
                   strlen(schemas[schemaType])) == 0)
158
2.96k
            break;
159
4.39k
    }
160
3.15k
    if(schemaType == UA_SCHEMAS_SIZE)
161
189
        return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
162
163
    /* Forward the current position until the first colon or slash */
164
2.96k
    size_t start = strlen(schemas[schemaType]);
165
2.96k
    size_t curr = start;
166
2.96k
    UA_Boolean ipv6 = false;
167
2.96k
    if(endpointUrl->length > curr && endpointUrl->data[curr] == '[') {
168
        /* IPv6: opc.tcp://[2001:0db8:85a3::8a2e:0370:7334]:1234/path */
169
14.0M
        for(; curr < endpointUrl->length; ++curr) {
170
14.0M
            if(endpointUrl->data[curr] == ']')
171
173
                break;
172
14.0M
        }
173
219
        if(curr == endpointUrl->length)
174
46
            return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
175
173
        curr++;
176
173
        ipv6 = true;
177
2.74k
    } else {
178
        /* IPv4 or hostname: opc.tcp://something.something:1234/path */
179
3.00M
        for(; curr < endpointUrl->length; ++curr) {
180
3.00M
            if(endpointUrl->data[curr] == ':' || endpointUrl->data[curr] == '/')
181
2.63k
                break;
182
3.00M
        }
183
2.74k
    }
184
185
    /* Set the hostname */
186
2.92k
    if(ipv6) {
187
        /* Skip the ipv6 '[]' container for getaddrinfo() later */
188
173
        outHostname->data = &endpointUrl->data[start+1];
189
173
        outHostname->length = curr - (start+2);
190
2.74k
    } else {
191
2.74k
        outHostname->data = &endpointUrl->data[start];
192
2.74k
        outHostname->length = curr - start;
193
2.74k
    }
194
195
    /* Empty string? */
196
2.92k
    if(outHostname->length == 0)
197
2.57k
        outHostname->data = NULL;
198
199
    /* Already at the end */
200
2.92k
    if(curr == endpointUrl->length)
201
168
        return UA_STATUSCODE_GOOD;
202
203
    /* Set the port - and for ETH set the VID.PCP postfix in the outpath string.
204
     * We have to parse that externally. */
205
2.75k
    if(endpointUrl->data[curr] == ':') {
206
2.56k
        if(++curr == endpointUrl->length)
207
4
            return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
208
209
        /* ETH schema */
210
2.56k
        if(schemaType == UA_ETH_SCHEMA_INDEX) {
211
2
            if(outPath != NULL) {
212
2
                outPath->data = &endpointUrl->data[curr];
213
2
                outPath->length = endpointUrl->length - curr;
214
2
            }
215
2
            return UA_STATUSCODE_GOOD;
216
2
        }
217
218
2.55k
        u32 largeNum;
219
2.55k
        size_t progress = UA_readNumber(&endpointUrl->data[curr],
220
2.55k
                                        endpointUrl->length - curr, &largeNum);
221
2.55k
        if(progress == 0 || largeNum > 65535)
222
199
            return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
223
        /* Test if the end of a valid port was reached */
224
2.36k
        curr += progress;
225
2.36k
        if(curr == endpointUrl->length || endpointUrl->data[curr] == '/')
226
2.28k
            *outPort = (u16)largeNum;
227
2.36k
        if(curr == endpointUrl->length)
228
2.24k
            return UA_STATUSCODE_GOOD;
229
2.36k
    }
230
231
    /* Set the path */
232
302
    UA_assert(curr < endpointUrl->length);
233
302
    if(endpointUrl->data[curr] != '/')
234
157
        return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
235
145
    if(++curr == endpointUrl->length)
236
17
        return UA_STATUSCODE_GOOD;
237
128
    if(outPath != NULL) {
238
128
        outPath->data = &endpointUrl->data[curr];
239
128
        outPath->length = endpointUrl->length - curr;
240
241
        /* Remove trailing slash from the path */
242
128
        if(endpointUrl->data[endpointUrl->length - 1] == '/')
243
18
            outPath->length--;
244
245
        /* Empty string? */
246
128
        if(outPath->length == 0)
247
16
            outPath->data = NULL;
248
128
    }
249
250
128
    return UA_STATUSCODE_GOOD;
251
145
}
252
253
UA_StatusCode
254
UA_parseEndpointUrlEthernet(const UA_String *endpointUrl, UA_String *target,
255
738
                            UA_UInt16 *vid, UA_Byte *pcp) {
256
    /* Url must begin with "opc.eth://" */
257
738
    if(endpointUrl->length < 11) {
258
13
        return UA_STATUSCODE_BADINTERNALERROR;
259
13
    }
260
725
    if(strncmp((char*) endpointUrl->data, "opc.eth://", 10) != 0) {
261
177
        return UA_STATUSCODE_BADINTERNALERROR;
262
177
    }
263
264
    /* Where does the host address end? */
265
548
    size_t curr = 10;
266
1.24M
    for(; curr < endpointUrl->length; ++curr) {
267
1.24M
        if(endpointUrl->data[curr] == ':') {
268
513
           break;
269
513
        }
270
1.24M
    }
271
272
    /* set host address */
273
548
    target->data = &endpointUrl->data[10];
274
548
    target->length = curr - 10;
275
548
    if(curr == endpointUrl->length) {
276
35
        return UA_STATUSCODE_GOOD;
277
35
    }
278
279
    /* Set VLAN */
280
513
    u32 value = 0;
281
513
    curr++;  /* skip ':' */
282
513
    size_t progress = UA_readNumber(&endpointUrl->data[curr],
283
513
                                    endpointUrl->length - curr, &value);
284
513
    if(progress == 0 || value > 4096) {
285
176
        return UA_STATUSCODE_BADINTERNALERROR;
286
176
    }
287
337
    curr += progress;
288
337
    if(curr == endpointUrl->length || endpointUrl->data[curr] == '.') {
289
261
        *vid = (UA_UInt16) value;
290
261
    }
291
337
    if(curr == endpointUrl->length) {
292
65
        return UA_STATUSCODE_GOOD;
293
65
    }
294
295
    /* Set priority */
296
272
    if(endpointUrl->data[curr] != '.') {
297
76
        return UA_STATUSCODE_BADINTERNALERROR;
298
76
    }
299
196
    curr++;  /* skip '.' */
300
196
    progress = UA_readNumber(&endpointUrl->data[curr],
301
196
                             endpointUrl->length - curr, &value);
302
196
    if(progress == 0 || value > 7) {
303
131
        return UA_STATUSCODE_BADINTERNALERROR;
304
131
    }
305
65
    curr += progress;
306
65
    if(curr != endpointUrl->length) {
307
56
        return UA_STATUSCODE_BADINTERNALERROR;
308
56
    }
309
9
    *pcp = (UA_Byte) value;
310
311
9
    return UA_STATUSCODE_GOOD;
312
65
}
313
314
UA_StatusCode
315
UA_ByteString_toBase64(const UA_ByteString *byteString,
316
0
                       UA_String *str) {
317
0
    UA_String_init(str);
318
0
    if(!byteString || !byteString->data || byteString->length == 0)
319
0
        return UA_STATUSCODE_GOOD;
320
321
0
    str->data = (UA_Byte*)
322
0
        UA_base64(byteString->data, byteString->length, &str->length);
323
0
    if(!str->data)
324
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
325
326
0
    return UA_STATUSCODE_GOOD;
327
0
}
328
329
UA_StatusCode
330
UA_ByteString_fromBase64(UA_ByteString *bs,
331
0
                         const UA_String *input) {
332
0
    UA_ByteString_init(bs);
333
0
    if(input->length == 0)
334
0
        return UA_STATUSCODE_GOOD;
335
0
    bs->data = UA_unbase64((const unsigned char*)input->data,
336
0
                           input->length, &bs->length);
337
    /* TODO: Differentiate between encoding and memory errors */
338
0
    if(!bs->data)
339
0
        return UA_STATUSCODE_BADINTERNALERROR;
340
0
    return UA_STATUSCODE_GOOD;
341
0
}
342
343
static u8
344
229k
printNum(i32 n, char *pos, u8 min_digits) {
345
229k
    char digits[10];
346
229k
    u8 len = 0;
347
    /* Handle negative values */
348
229k
    if(n < 0) {
349
6.13k
        pos[len++] = '-';
350
6.13k
        n = -n;
351
6.13k
    }
352
353
    /* Extract the digits */
354
229k
    u8 i = 0;
355
816k
    for(; i < min_digits || n > 0; i++) {
356
587k
        digits[i] = (char)((n % 10) + '0');
357
587k
        n /= 10;
358
587k
    }
359
360
    /* Print in reverse order and return */
361
816k
    for(; i > 0; i--)
362
587k
        pos[len++] = digits[i-1];
363
229k
    return len;
364
229k
}
365
366
25.5k
#define UA_DATETIME_LENGTH 40
367
368
UA_StatusCode
369
25.5k
encodeDateTime(const UA_DateTime dt, UA_String *output) {
370
25.5k
    char buffer[UA_DATETIME_LENGTH];
371
25.5k
    char *pos = buffer;
372
373
25.5k
    if(output->length > 0) {
374
25.5k
        if(output->length < UA_DATETIME_LENGTH)
375
0
            return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED;
376
25.5k
        pos = (char*)output->data;
377
25.5k
    }
378
379
    /* Format: -yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z' is used. max 31 bytes.
380
     * Note the optional minus for negative years. */
381
25.5k
    UA_DateTimeStruct tSt = UA_DateTime_toStruct(dt);
382
25.5k
    pos += printNum(tSt.year, pos, 4);
383
25.5k
    *(pos++) = '-';
384
25.5k
    pos += printNum(tSt.month, pos, 2);
385
25.5k
    *(pos++) = '-';
386
25.5k
    pos += printNum(tSt.day, pos, 2);
387
25.5k
    *(pos++) = 'T';
388
25.5k
    pos += printNum(tSt.hour, pos, 2);
389
25.5k
    *(pos++) = ':';
390
25.5k
    pos += printNum(tSt.min, pos, 2);
391
25.5k
    *(pos++) = ':';
392
25.5k
    pos += printNum(tSt.sec, pos, 2);
393
25.5k
    *(pos++) = '.';
394
25.5k
    pos += printNum(tSt.milliSec, pos, 3);
395
25.5k
    pos += printNum(tSt.microSec, pos, 3);
396
25.5k
    pos += printNum(tSt.nanoSec, pos, 3);
397
398
    /* Remove trailing zeros */
399
25.5k
    pos--;
400
243k
    while(*pos == '0')
401
218k
        pos--;
402
25.5k
    if(*pos == '.')
403
23.1k
        pos--;
404
405
25.5k
    pos++;
406
25.5k
    *(pos++) = 'Z';
407
408
25.5k
    if(output->length > 0) {
409
25.5k
        output->length = (size_t)(pos - (char*)output->data);
410
25.5k
    } else {
411
0
        UA_String str = {(size_t)(pos - buffer), (UA_Byte*)buffer};
412
0
        return UA_String_copy(&str, output);
413
0
    }
414
415
25.5k
    return UA_STATUSCODE_GOOD;
416
25.5k
}
417
418
/* Key Value Map */
419
420
const UA_KeyValueMap UA_KEYVALUEMAP_NULL = {0, NULL};
421
422
UA_KeyValueMap *
423
0
UA_KeyValueMap_new(void) {
424
0
    return (UA_KeyValueMap*)UA_calloc(1, sizeof(UA_KeyValueMap));
425
0
}
426
427
UA_StatusCode
428
UA_KeyValueMap_set(UA_KeyValueMap *map,
429
                   const UA_QualifiedName key,
430
0
                   const UA_Variant *value) {
431
0
    if(map == NULL || value == NULL)
432
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
433
434
    /* Key exists already */
435
0
    const UA_Variant *v = UA_KeyValueMap_get(map, key);
436
0
    if(v) {
437
0
        UA_Variant copyV;
438
0
        UA_StatusCode res = UA_Variant_copy(value, &copyV);
439
0
        if(res != UA_STATUSCODE_GOOD)
440
0
            return res;
441
0
        UA_Variant *target = (UA_Variant*)(uintptr_t)v;
442
0
        UA_Variant_clear(target);
443
0
        *target = copyV;
444
0
        return UA_STATUSCODE_GOOD;
445
0
    }
446
447
    /* Append to the array */
448
0
    UA_KeyValuePair pair;
449
0
    pair.key = key;
450
0
    pair.value = *value;
451
0
    return UA_Array_appendCopy((void**)&map->map, &map->mapSize, &pair,
452
0
                               &UA_TYPES[UA_TYPES_KEYVALUEPAIR]);
453
0
}
454
455
UA_EXPORT UA_StatusCode
456
UA_KeyValueMap_setShallow(UA_KeyValueMap *map,
457
                          const UA_QualifiedName key,
458
0
                          UA_Variant *value) {
459
0
    if(map == NULL || value == NULL)
460
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
461
462
    /* Key exists already */
463
0
    UA_Variant *target;
464
0
    const UA_Variant *v = UA_KeyValueMap_get(map, key);
465
0
    if(v) {
466
0
        target = (UA_Variant*)(uintptr_t)v;
467
0
        UA_Variant_clear(target);
468
0
    } else {
469
        /* Append to the array */
470
0
        UA_KeyValuePair pair;
471
0
        pair.key = key;
472
0
        UA_Variant_init(&pair.value);
473
0
        UA_StatusCode res =
474
0
            UA_Array_appendCopy((void**)&map->map, &map->mapSize, &pair,
475
0
                                &UA_TYPES[UA_TYPES_KEYVALUEPAIR]);
476
0
        if(res != UA_STATUSCODE_GOOD)
477
0
            return res;
478
0
        target = &map->map[map->mapSize-1].value;
479
0
    }
480
481
0
    *target = *value;
482
0
    target->storageType = UA_VARIANT_DATA_NODELETE;
483
0
    return UA_STATUSCODE_GOOD;
484
0
}
485
486
UA_StatusCode
487
UA_KeyValueMap_setScalar(UA_KeyValueMap *map,
488
                         const UA_QualifiedName key,
489
                         const void * UA_RESTRICT p,
490
0
                         const UA_DataType *type) {
491
0
    if(p == NULL || type == NULL)
492
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
493
0
    UA_Variant v;
494
0
    UA_Variant_init(&v);
495
0
    v.type = type;
496
0
    v.arrayLength = 0;
497
0
    v.data = (void*)(uintptr_t)p;
498
0
    return UA_KeyValueMap_set(map, key, &v);
499
0
}
500
501
UA_StatusCode
502
UA_KeyValueMap_setScalarShallow(UA_KeyValueMap *map, const UA_QualifiedName key,
503
0
                                void *UA_RESTRICT p, const UA_DataType *type) {
504
0
    if(p == NULL || type == NULL)
505
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
506
0
    UA_Variant v;
507
0
    UA_Variant_init(&v);
508
0
    v.type = type;
509
0
    v.arrayLength = 0;
510
0
    v.data = p;
511
0
    return UA_KeyValueMap_setShallow(map, key, &v);
512
0
}
513
514
const UA_Variant *
515
UA_KeyValueMap_get(const UA_KeyValueMap *map,
516
150k
                   const UA_QualifiedName key) {
517
150k
    if(!map)
518
0
        return NULL;
519
225k
    for(size_t i = 0; i < map->mapSize; i++) {
520
91.2k
        if(map->map[i].key.namespaceIndex == key.namespaceIndex &&
521
91.2k
           UA_String_equal(&map->map[i].key.name, &key.name))
522
16.2k
            return &map->map[i].value;
523
524
91.2k
    }
525
134k
    return NULL;
526
150k
}
527
528
UA_Boolean
529
0
UA_KeyValueMap_isEmpty(const UA_KeyValueMap *map) {
530
0
    if(!map)
531
0
        return true;
532
0
    return map->mapSize == 0;
533
0
}
534
535
const void *
536
UA_KeyValueMap_getScalar(const UA_KeyValueMap *map,
537
                         const UA_QualifiedName key,
538
85.2k
                         const UA_DataType *type) {
539
85.2k
    const UA_Variant *v = UA_KeyValueMap_get(map, key);
540
85.2k
    if(!v || !UA_Variant_hasScalarType(v, type))
541
76.4k
        return NULL;
542
8.74k
    return v->data;
543
85.2k
}
544
545
void
546
53.2k
UA_KeyValueMap_clear(UA_KeyValueMap *map) {
547
53.2k
    if(!map)
548
0
        return;
549
53.2k
    if(map->mapSize > 0) {
550
0
        UA_Array_delete(map->map, map->mapSize, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]);
551
0
        map->mapSize = 0;
552
0
    }
553
53.2k
}
554
555
void
556
0
UA_KeyValueMap_delete(UA_KeyValueMap *map) {
557
0
    UA_KeyValueMap_clear(map);
558
0
    UA_free(map);
559
0
}
560
561
UA_StatusCode
562
UA_KeyValueMap_remove(UA_KeyValueMap *map,
563
0
                      const UA_QualifiedName key) {
564
0
    if(!map)
565
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
566
567
0
    UA_KeyValuePair *m = map->map;
568
0
    size_t s = map->mapSize;
569
0
    size_t i = 0;
570
0
    for(; i < s; i++) {
571
0
        if(m[i].key.namespaceIndex == key.namespaceIndex &&
572
0
           UA_String_equal(&m[i].key.name, &key.name))
573
0
            break;
574
0
    }
575
0
    if(i == s)
576
0
        return UA_STATUSCODE_BADNOTFOUND;
577
578
    /* Clean the slot and move the last entry to fill the slot */
579
0
    UA_KeyValuePair_clear(&m[i]);
580
0
    if(s > 1 && i < s - 1) {
581
0
        m[i] = m[s-1];
582
0
        UA_KeyValuePair_init(&m[s-1]);
583
0
    }
584
585
    /* In case resize fails, keep the longer original array around. Resize never
586
     * fails when reducing the size to zero. */
587
0
    UA_StatusCode res =
588
0
        UA_Array_resize((void**)&map->map, &map->mapSize, map->mapSize - 1,
589
0
                          &UA_TYPES[UA_TYPES_KEYVALUEPAIR]);
590
    /* Adjust map->mapSize only when UA_Array_resize() failed. On success, the
591
     * value has already been decremented by UA_Array_resize(). */
592
0
    if(res != UA_STATUSCODE_GOOD)
593
0
        map->mapSize--;
594
0
    return UA_STATUSCODE_GOOD;
595
0
}
596
597
UA_StatusCode
598
4.01k
UA_KeyValueMap_copy(const UA_KeyValueMap *src, UA_KeyValueMap *dst) {
599
4.01k
    if(!dst)
600
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
601
4.01k
    if(!src) {
602
0
        dst->map = NULL;
603
0
        dst->mapSize = 0;
604
0
        return UA_STATUSCODE_GOOD;
605
0
    }
606
4.01k
    UA_StatusCode res = UA_Array_copy(src->map, src->mapSize, (void**)&dst->map,
607
4.01k
                                      &UA_TYPES[UA_TYPES_KEYVALUEPAIR]);
608
4.01k
    if(res == UA_STATUSCODE_GOOD)
609
4.01k
        dst->mapSize = src->mapSize;
610
4.01k
    return res;
611
4.01k
}
612
613
UA_Boolean
614
0
UA_KeyValueMap_contains(const UA_KeyValueMap *map, const UA_QualifiedName key) {
615
0
    if(!map)
616
0
        return false;
617
0
    for(size_t i = 0; i < map->mapSize; ++i) {
618
0
        if(UA_QualifiedName_equal(&map->map[i].key, &key))
619
0
            return true;
620
0
    }
621
0
    return false;
622
0
}
623
624
UA_StatusCode
625
0
UA_KeyValueMap_merge(UA_KeyValueMap *lhs, const UA_KeyValueMap *rhs) {
626
0
    if(!lhs)
627
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
628
0
    if(!rhs)
629
0
        return UA_STATUSCODE_GOOD;
630
631
0
    UA_KeyValueMap merge;
632
0
    UA_StatusCode res = UA_KeyValueMap_copy(lhs, &merge);
633
0
    if(res != UA_STATUSCODE_GOOD)
634
0
        return res;
635
636
0
    for(size_t i = 0; i < rhs->mapSize; ++i) {
637
0
        res = UA_KeyValueMap_set(&merge, rhs->map[i].key, &rhs->map[i].value);
638
0
        if(res != UA_STATUSCODE_GOOD) {
639
0
            UA_KeyValueMap_clear(&merge);
640
0
            return res;
641
0
        }
642
0
    }
643
644
0
    UA_KeyValueMap_clear(lhs);
645
0
    *lhs = merge;
646
0
    return UA_STATUSCODE_GOOD;
647
0
}
648
649
/***************************/
650
/* Random Number Generator */
651
/***************************/
652
653
/* TODO is this safe for multithreading? */
654
static pcg32_random_t UA_rng = PCG32_INITIALIZER;
655
656
void
657
7.29k
UA_random_seed(u64 seed) {
658
7.29k
    pcg32_srandom_r(&UA_rng, seed, (u64)UA_DateTime_now());
659
7.29k
}
660
661
void
662
0
UA_random_seed_deterministic(UA_UInt64 seed) {
663
0
    pcg32_srandom_r(&UA_rng, seed, 0);
664
0
}
665
666
u32
667
0
UA_UInt32_random(void) {
668
0
    return (u32)pcg32_random_r(&UA_rng);
669
0
}
670
671
UA_Guid
672
7.29k
UA_Guid_random(void) {
673
7.29k
    UA_Guid result;
674
7.29k
    result.data1 = (u32)pcg32_random_r(&UA_rng);
675
7.29k
    u32 r = (u32)pcg32_random_r(&UA_rng);
676
7.29k
    result.data2 = (u16) r;
677
7.29k
    result.data3 = (u16) (r >> 16);
678
7.29k
    r = (u32)pcg32_random_r(&UA_rng);
679
7.29k
    result.data4[0] = (u8)r;
680
7.29k
    result.data4[1] = (u8)(r >> 4);
681
7.29k
    result.data4[2] = (u8)(r >> 8);
682
7.29k
    result.data4[3] = (u8)(r >> 12);
683
7.29k
    r = (u32)pcg32_random_r(&UA_rng);
684
7.29k
    result.data4[4] = (u8)r;
685
7.29k
    result.data4[5] = (u8)(r >> 4);
686
7.29k
    result.data4[6] = (u8)(r >> 8);
687
7.29k
    result.data4[7] = (u8)(r >> 12);
688
7.29k
    return result;
689
7.29k
}
690
691
/********************/
692
/* Malloc Singleton */
693
/********************/
694
695
#ifdef UA_ENABLE_MALLOC_SINGLETON
696
# include <stdlib.h>
697
UA_EXPORT UA_THREAD_LOCAL void * (*UA_mallocSingleton)(size_t size) = malloc;
698
UA_EXPORT UA_THREAD_LOCAL void (*UA_freeSingleton)(void *ptr) = free;
699
UA_EXPORT UA_THREAD_LOCAL void * (*UA_callocSingleton)(size_t nelem, size_t elsize) = calloc;
700
UA_EXPORT UA_THREAD_LOCAL void * (*UA_reallocSingleton)(void *ptr, size_t size) = realloc;
701
#endif
702
703
/************************/
704
/* ReferenceType Lookup */
705
/************************/
706
707
typedef struct {
708
    UA_String browseName;
709
    UA_UInt32 identifier;
710
} RefTypeName;
711
712
87.0k
#define KNOWNREFTYPES 17
713
static const RefTypeName knownRefTypes[KNOWNREFTYPES] = {
714
    {UA_STRING_STATIC("References"), UA_NS0ID_REFERENCES},
715
    {UA_STRING_STATIC("HierachicalReferences"), UA_NS0ID_HIERARCHICALREFERENCES},
716
    {UA_STRING_STATIC("NonHierachicalReferences"), UA_NS0ID_NONHIERARCHICALREFERENCES},
717
    {UA_STRING_STATIC("HasChild"), UA_NS0ID_HASCHILD},
718
    {UA_STRING_STATIC("Aggregates"), UA_NS0ID_AGGREGATES},
719
    {UA_STRING_STATIC("HasComponent"), UA_NS0ID_HASCOMPONENT},
720
    {UA_STRING_STATIC("HasProperty"), UA_NS0ID_HASPROPERTY},
721
    {UA_STRING_STATIC("HasOrderedComponent"), UA_NS0ID_HASORDEREDCOMPONENT},
722
    {UA_STRING_STATIC("HasSubtype"), UA_NS0ID_HASSUBTYPE},
723
    {UA_STRING_STATIC("Organizes"), UA_NS0ID_ORGANIZES},
724
    {UA_STRING_STATIC("HasModellingRule"), UA_NS0ID_HASMODELLINGRULE},
725
    {UA_STRING_STATIC("HasTypeDefinition"), UA_NS0ID_HASTYPEDEFINITION},
726
    {UA_STRING_STATIC("HasEncoding"), UA_NS0ID_HASENCODING},
727
    {UA_STRING_STATIC("GeneratesEvent"), UA_NS0ID_GENERATESEVENT},
728
    {UA_STRING_STATIC("AlwaysGeneratesEvent"), UA_NS0ID_ALWAYSGENERATESEVENT},
729
    {UA_STRING_STATIC("HasEventSource"), UA_NS0ID_HASEVENTSOURCE},
730
    {UA_STRING_STATIC("HasNotifier"), UA_NS0ID_HASNOTIFIER}
731
};
732
733
UA_StatusCode
734
3.69k
lookupRefType(UA_Server *server, UA_QualifiedName *qn, UA_NodeId *outRefTypeId) {
735
    /* Check well-known ReferenceTypes first */
736
3.69k
    if(qn->namespaceIndex == 0) {
737
37.1k
        for(size_t i = 0; i < KNOWNREFTYPES; i++) {
738
35.8k
            if(UA_String_equal(&qn->name, &knownRefTypes[i].browseName)) {
739
2.32k
                *outRefTypeId = UA_NODEID_NUMERIC(0, knownRefTypes[i].identifier);
740
2.32k
                return UA_STATUSCODE_GOOD;
741
2.32k
            }
742
35.8k
        }
743
3.64k
    }
744
745
    /* Browse the information model. Return the first results if the browse name
746
     * in the hierarchy is ambiguous. */
747
1.37k
    if(server) {
748
0
        UA_BrowseDescription bd;
749
0
        UA_BrowseDescription_init(&bd);
750
0
        bd.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCES);
751
0
        bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
752
0
        bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
753
0
        bd.nodeClassMask = UA_NODECLASS_REFERENCETYPE;
754
755
0
        size_t resultsSize = 0;
756
0
        UA_ExpandedNodeId *results = NULL;
757
0
        UA_StatusCode res =
758
0
            UA_Server_browseRecursive(server, &bd, &resultsSize, &results);
759
0
        if(res != UA_STATUSCODE_GOOD)
760
0
            return res;
761
0
        for(size_t i = 0; i < resultsSize; i++) {
762
0
            UA_QualifiedName bn;
763
0
            UA_Server_readBrowseName(server, results[i].nodeId, &bn);
764
0
            if(UA_QualifiedName_equal(qn, &bn)) {
765
0
                UA_QualifiedName_clear(&bn);
766
0
                *outRefTypeId = results[i].nodeId;
767
0
                UA_NodeId_clear(&results[i].nodeId);
768
0
                UA_Array_delete(results, resultsSize, &UA_TYPES[UA_TYPES_NODEID]);
769
0
                return UA_STATUSCODE_GOOD;
770
0
            }
771
0
            UA_QualifiedName_clear(&bn);
772
0
        }
773
774
0
        UA_Array_delete(results, resultsSize, &UA_TYPES[UA_TYPES_NODEID]);
775
0
    }
776
777
1.37k
    return UA_STATUSCODE_BADNOTFOUND;
778
1.37k
}
779
780
UA_StatusCode
781
17.9k
getRefTypeBrowseName(const UA_NodeId *refTypeId, UA_String *outBN) {
782
    /* Canonical name known? */
783
17.9k
    if(refTypeId->namespaceIndex == 0 &&
784
16.7k
       refTypeId->identifierType == UA_NODEIDTYPE_NUMERIC) {
785
49.8k
        for(size_t i = 0; i < KNOWNREFTYPES; i++) {
786
48.1k
            if(refTypeId->identifier.numeric != knownRefTypes[i].identifier)
787
45.1k
                continue;
788
2.96k
            memcpy(outBN->data, knownRefTypes[i].browseName.data, knownRefTypes[i].browseName.length);
789
2.96k
            outBN->length = knownRefTypes[i].browseName.length;
790
2.96k
            return UA_STATUSCODE_GOOD;
791
48.1k
        }
792
4.66k
    }
793
794
    /* Print the NodeId */
795
15.0k
    return UA_NodeId_print(refTypeId, outBN);
796
17.9k
}
797
798
/************************/
799
/* Printing and Parsing */
800
/************************/
801
802
static UA_INLINE UA_Boolean
803
23.1M
isHex(u8 c) {
804
23.1M
    return ((c >= 'a' && c <= 'f') ||
805
21.7M
            (c >= 'A' && c <= 'F') ||
806
21.6M
            (c >= '0' && c <= '9'));
807
23.1M
}
808
809
UA_StatusCode
810
1.69M
UA_String_unescape(UA_String *str, UA_Boolean copyEscape, UA_Escaping esc) {
811
1.69M
    if(esc == UA_ESCAPING_NONE)
812
844k
        return UA_STATUSCODE_GOOD;
813
814
    /* Does the string need escaping? */
815
846k
    UA_String tmp;
816
846k
    status res = UA_STATUSCODE_GOOD;
817
846k
    u8 *pos = str->data;
818
846k
    u8 *end = str->data + str->length;
819
846k
    u8 escape_char = (esc == UA_ESCAPING_PERCENT ||
820
820k
                      esc == UA_ESCAPING_PERCENT_EXTENDED) ? '%' : '&';
821
39.9M
    for(; pos < end; pos++) {
822
39.1M
        if(*pos == escape_char)
823
13.9k
            goto escape;
824
39.1M
    }
825
826
832k
    return UA_STATUSCODE_GOOD;
827
828
13.9k
 escape:
829
13.9k
    if(copyEscape) {
830
0
        res = UA_String_copy(str, &tmp);
831
0
        if(res != UA_STATUSCODE_GOOD)
832
0
            return res;
833
0
        pos = tmp.data;
834
0
        end = tmp.data + tmp.length;
835
0
    }
836
837
13.9k
    u8 byte = 0;
838
13.9k
    u8 *writepos = pos;
839
840
13.9k
    res = UA_STATUSCODE_BADDECODINGERROR;
841
13.9k
    if(esc == UA_ESCAPING_PERCENT ||
842
13.5k
       esc == UA_ESCAPING_PERCENT_EXTENDED) {
843
        /* Percent-Escaping */
844
17.0M
        for(; pos < end; pos++) {
845
16.9M
            if(*pos == '%') {
846
11.5M
                if(pos + 2 >= end || !isHex(pos[1]) || !isHex(pos[2]))
847
608
                    goto out;
848
11.5M
                if(pos[1] >= 'a')
849
3.11k
                    byte = pos[1] - ('a' - 10);
850
11.5M
                else if(pos[1] >= 'A')
851
4.85k
                    byte = pos[1] - ('A' - 10);
852
11.5M
                else
853
11.5M
                    byte = pos[1] - '0';
854
11.5M
                byte <<= 4;
855
856
11.5M
                if(pos[2] >= 'a')
857
1.46M
                    byte += (u8)(pos[2] - ('a' - 10));
858
10.1M
                else if(pos[2] >= 'A')
859
2.63k
                    byte += (u8)(pos[2] - ('A' - 10));
860
10.1M
                else
861
10.1M
                    byte += (u8)(pos[2] - '0');
862
863
11.5M
                pos += 2;
864
11.5M
                *writepos++ = byte;
865
11.5M
                continue;
866
11.5M
            }
867
5.41M
            *writepos++ = *pos;
868
5.41M
        }
869
13.5k
    } else {
870
        /* And-Escaping */
871
5.39M
        for(; pos < end; pos++) {
872
5.39M
            if(*pos == '&') {
873
9.20k
                pos++;
874
9.20k
                if(pos == end)
875
0
                    goto out;
876
9.20k
            }
877
5.39M
            *writepos++ = *pos;
878
5.39M
        }
879
409
    }
880
13.3k
    res = UA_STATUSCODE_GOOD;
881
882
13.9k
 out:
883
13.9k
    if(copyEscape) {
884
0
        tmp.length = (size_t)(writepos - tmp.data);
885
0
        if(tmp.length == 0)
886
0
            UA_String_clear(&tmp);
887
0
        if(res == UA_STATUSCODE_GOOD)
888
0
            *str = tmp;
889
0
        else
890
0
            UA_String_clear(&tmp);
891
13.9k
    } else if(res == UA_STATUSCODE_GOOD) {
892
13.3k
        str->length = (size_t)(writepos - str->data);
893
13.3k
    }
894
13.9k
    return res;
895
13.3k
}
896
897
static const u8 hexchars[16] =
898
    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
899
900
size_t
901
242k
UA_String_escapedSize(const UA_String s, UA_Escaping esc) {
902
    /* Find out the overhead from escaping */
903
242k
    size_t overhead = 0;
904
152M
    for(size_t j = 0; j < s.length; j++) {
905
152M
        if(esc == UA_ESCAPING_AND_EXTENDED)
906
0
            overhead += isReservedAndExtended(s.data[j]);
907
152M
        else if(esc == UA_ESCAPING_AND)
908
9.74M
            overhead += isReservedAnd(s.data[j]);
909
142M
        else if(esc == UA_ESCAPING_PERCENT)
910
228k
            overhead += (isReservedPercent(s.data[j]) ? 2 : 0);
911
142M
        else /* if(esc == UA_ESCAPING_PERCENT_EXTENDED) */
912
142M
            overhead += (isReservedPercentExtended(s.data[j]) ? 2 : 0);
913
152M
    }
914
915
242k
    return s.length + overhead;
916
242k
}
917
918
size_t
919
239k
UA_String_escapeInsert(u8 *pos, const UA_String s2, UA_Escaping esc) {
920
239k
    u8 *begin = pos;
921
922
239k
    if(esc == UA_ESCAPING_NONE) {
923
70.8M
        for(size_t j = 0; j < s2.length; j++)
924
70.8M
            *pos++ = s2.data[j];
925
207k
    } else if(esc == UA_ESCAPING_PERCENT || esc == UA_ESCAPING_PERCENT_EXTENDED) {
926
31.8M
        for(size_t j = 0; j < s2.length; j++) {
927
31.7M
            UA_Boolean reserved = (esc == UA_ESCAPING_PERCENT_EXTENDED) ?
928
31.4M
                isReservedPercentExtended(s2.data[j]) : isReservedPercent(s2.data[j]);
929
31.7M
            if(UA_LIKELY(!reserved)) {
930
8.57M
                *pos++ = s2.data[j];
931
23.1M
            } else {
932
23.1M
                *pos++ = '%';
933
23.1M
                *pos++ = hexchars[s2.data[j] >> 4];
934
23.1M
                *pos++ = hexchars[s2.data[j] & 0x0f];
935
23.1M
            }
936
31.7M
        }
937
176k
    } else {
938
9.78M
        for(size_t j = 0; j < s2.length; j++) {
939
9.74M
            UA_Boolean reserved = (esc == UA_ESCAPING_AND_EXTENDED) ?
940
9.74M
                isReservedAndExtended(s2.data[j]) : isReservedAnd(s2.data[j]);
941
9.74M
            if(reserved)
942
221k
                *pos++ = '&';
943
9.74M
            *pos++ = s2.data[j];
944
9.74M
        }
945
31.2k
    }
946
947
239k
    return (size_t)(pos - begin);
948
239k
}
949
950
UA_StatusCode
951
206k
UA_String_escapeAppend(UA_String *s, const UA_String s2, UA_Escaping esc) {
952
206k
    if(esc == UA_ESCAPING_NONE)
953
0
        return UA_String_append(s, s2);
954
955
    /* Allocate memory for the additional escaped string */
956
206k
    size_t escapedLength = UA_String_escapedSize(s2, esc);
957
206k
    UA_Byte *buf = (UA_Byte*)
958
206k
        UA_realloc(s->data, s->length + s2.length + escapedLength);
959
206k
    if(!buf)
960
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
961
962
    /* Escape and insert at the end */
963
206k
    s->data = buf;
964
206k
    UA_String_escapeInsert(s->data + s->length, s2, esc);
965
206k
    s->length += escapedLength;
966
206k
    return UA_STATUSCODE_GOOD;
967
206k
}
968
969
static UA_StatusCode
970
4.91k
moveTmpToOut(UA_String *tmp, UA_String *out) {
971
    /* Output has zero length */
972
4.91k
    if(tmp->length == 0) {
973
376
        UA_assert(tmp->data == NULL);
974
376
        if(out->data == NULL)
975
376
            out->data = (UA_Byte*)UA_EMPTY_ARRAY_SENTINEL;
976
376
        out->length = 0;
977
376
        return UA_STATUSCODE_GOOD;
978
376
    }
979
980
    /* No output buffer provided, return the tmp buffer */
981
4.53k
    if(out->length == 0) {
982
4.53k
        *out = *tmp;
983
4.53k
        return UA_STATUSCODE_GOOD;
984
4.53k
    }
985
986
    /* The provided buffer is too short */
987
0
    if(out->length < tmp->length) {
988
0
        UA_String_clear(tmp);
989
0
        return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED;
990
0
    }
991
992
    /* Copy output to the provided buffer */
993
0
    memcpy(out->data, tmp->data, tmp->length);
994
0
    out->length = tmp->length;
995
0
    UA_String_clear(tmp);
996
0
    return UA_STATUSCODE_GOOD;
997
0
}
998
999
static const UA_NodeId hierarchicalRefs =
1000
    {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HIERARCHICALREFERENCES}};
1001
static const UA_NodeId aggregatesRefs =
1002
    {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_AGGREGATES}};
1003
static const UA_NodeId objectsFolder =
1004
    {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_OBJECTSFOLDER}};
1005
1006
static UA_StatusCode
1007
2.82k
printRelativePath(const UA_RelativePath *rp, UA_String *out, UA_Escaping esc) {
1008
2.82k
    UA_String tmp = UA_STRING_NULL;
1009
2.82k
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1010
193k
    for(size_t i = 0; i < rp->elementsSize && res == UA_STATUSCODE_GOOD; i++) {
1011
        /* Print the reference type */
1012
190k
        UA_RelativePathElement *elm = &rp->elements[i];
1013
190k
        if(UA_NodeId_equal(&hierarchicalRefs, &elm->referenceTypeId) &&
1014
169k
           !elm->isInverse && elm->includeSubtypes) {
1015
168k
            res |= UA_String_append(&tmp, UA_STRING("/"));
1016
168k
        } else if(esc == UA_ESCAPING_AND &&
1017
11.4k
                  UA_NodeId_equal(&aggregatesRefs, &elm->referenceTypeId) &&
1018
7.44k
                  !elm->isInverse && elm->includeSubtypes) {
1019
6.94k
            res |= UA_String_append(&tmp, UA_STRING("."));
1020
15.5k
        } else {
1021
15.5k
            res |= UA_String_append(&tmp, UA_STRING("<"));
1022
15.5k
            if(!elm->includeSubtypes)
1023
1.52k
                res |= UA_String_append(&tmp, UA_STRING("#"));
1024
15.5k
            if(elm->isInverse)
1025
1.41k
                res |= UA_String_append(&tmp, UA_STRING("!"));
1026
15.5k
            if(res != UA_STATUSCODE_GOOD)
1027
0
                break;
1028
1029
15.5k
            UA_Byte bnBuf[512];
1030
15.5k
            UA_String bnBufStr = {512, bnBuf};
1031
15.5k
            res = getRefTypeBrowseName(&elm->referenceTypeId, &bnBufStr);
1032
15.5k
            if(res != UA_STATUSCODE_GOOD) {
1033
2.43k
                UA_String_init(&bnBufStr);
1034
2.43k
                res = getRefTypeBrowseName(&elm->referenceTypeId, &bnBufStr);
1035
2.43k
                if(res != UA_STATUSCODE_GOOD)
1036
0
                    break;
1037
2.43k
            }
1038
15.5k
            res |= UA_String_escapeAppend(&tmp, bnBufStr, esc);
1039
15.5k
            res |= UA_String_append(&tmp, UA_STRING(">"));
1040
15.5k
            if(bnBufStr.data != bnBuf)
1041
2.43k
                UA_String_clear(&bnBufStr);
1042
15.5k
        }
1043
1044
        /* Print the qualified name */
1045
190k
        UA_QualifiedName *qn = &elm->targetName;
1046
190k
        if(qn->namespaceIndex > 0) {
1047
1.56k
            char nsStr[8]; /* Enough for a uint16 */
1048
1.56k
            itoaUnsigned(qn->namespaceIndex, nsStr, 10);
1049
1.56k
            res |= UA_String_append(&tmp, UA_STRING(nsStr));
1050
1.56k
            res |= UA_String_append(&tmp, UA_STRING(":"));
1051
1.56k
        }
1052
190k
        res |= UA_String_escapeAppend(&tmp, qn->name, esc);
1053
190k
    }
1054
1055
    /* Encoding failed, clean up */
1056
2.82k
    if(res != UA_STATUSCODE_GOOD) {
1057
0
        UA_String_clear(&tmp);
1058
0
        return res;
1059
0
    }
1060
1061
2.82k
    return moveTmpToOut(&tmp, out);
1062
2.82k
}
1063
1064
UA_StatusCode
1065
739
UA_RelativePath_print(const UA_RelativePath *rp, UA_String *out) {
1066
739
    return printRelativePath(rp, out, UA_ESCAPING_AND);
1067
739
}
1068
1069
static UA_NodeId baseEventTypeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEEVENTTYPE}};
1070
1071
#ifdef UA_TYPES_SIMPLEATTRIBUTEOPERAND
1072
1073
UA_StatusCode
1074
0
UA_SimpleAttributeOperand_print(const UA_SimpleAttributeOperand *sao, UA_String *out) {
1075
0
    UA_RelativePathElement rpe;
1076
0
    UA_String tmp = UA_STRING_NULL;
1077
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1078
1079
    /* Print the TypeDefinitionId */
1080
0
    if(!UA_NodeId_equal(&baseEventTypeId, &sao->typeDefinitionId)) {
1081
0
        UA_Byte nodeIdBuf[512];
1082
0
        UA_String nodeIdBufStr = {512, nodeIdBuf};
1083
0
        res |= nodeId_printEscape(&sao->typeDefinitionId, &nodeIdBufStr,
1084
0
                                  NULL, UA_ESCAPING_PERCENT);
1085
0
        res |= UA_String_append(&tmp, nodeIdBufStr);
1086
0
    }
1087
1088
    /* Print the BrowsePath */
1089
0
    UA_RelativePathElement_init(&rpe);
1090
0
    rpe.includeSubtypes = true;
1091
0
    rpe.referenceTypeId = hierarchicalRefs;
1092
0
    UA_RelativePath rp = {1, &rpe};
1093
0
    for(size_t i = 0; i < sao->browsePathSize; i++) {
1094
0
        UA_String rpstr = UA_STRING_NULL;
1095
0
        UA_assert(rpstr.data == NULL && rpstr.length == 0); /* pacify clang scan-build */
1096
0
        rpe.targetName = sao->browsePath[i];
1097
0
        res |= printRelativePath(&rp, &rpstr, UA_ESCAPING_PERCENT);
1098
0
        res |= UA_String_append(&tmp, rpstr);
1099
0
        UA_String_clear(&rpstr);
1100
0
    }
1101
1102
    /* Print the attribute name */
1103
0
    if(sao->attributeId != UA_ATTRIBUTEID_VALUE) {
1104
0
        const char *attrName = UA_AttributeId_name((UA_AttributeId)sao->attributeId);
1105
0
        res |= UA_String_append(&tmp, UA_STRING("#"));
1106
0
        res |= UA_String_append(&tmp, UA_STRING((char*)(uintptr_t)attrName));
1107
0
    }
1108
1109
    /* Print the IndexRange */
1110
0
    if(sao->indexRange.length > 0) {
1111
0
        res |= UA_String_append(&tmp, UA_STRING("["));
1112
0
        res |= UA_String_append(&tmp, sao->indexRange);
1113
0
        res |= UA_String_append(&tmp, UA_STRING("]"));
1114
0
    }
1115
1116
    /* Encoding failed, clean up */
1117
0
    if(res != UA_STATUSCODE_GOOD) {
1118
0
        UA_String_clear(&tmp);
1119
0
        return res;
1120
0
    }
1121
1122
0
    return moveTmpToOut(&tmp, out);
1123
0
}
1124
1125
#endif /* UA_TYPES_SIMPLEATTRIBUTEOPERAND */
1126
1127
#ifdef UA_TYPES_ATTRIBUTEOPERAND
1128
1129
UA_StatusCode
1130
UA_AttributeOperand_print(const UA_AttributeOperand *ao,
1131
2.08k
                          UA_String *out) {
1132
2.08k
    UA_String tmp = UA_STRING_NULL;
1133
2.08k
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1134
1135
    /* Print the TypeDefinitionId */
1136
2.08k
    if(!UA_NodeId_equal(&objectsFolder, &ao->nodeId)) {
1137
544
        UA_Byte nodeIdBuf[512];
1138
544
        UA_String nodeIdBufStr = {512, nodeIdBuf};
1139
544
        res |= nodeId_printEscape(&ao->nodeId, &nodeIdBufStr,
1140
544
                                  NULL, UA_ESCAPING_PERCENT_EXTENDED);
1141
544
        res |= UA_String_append(&tmp, nodeIdBufStr);
1142
544
    }
1143
1144
    /* Print the BrowsePath */
1145
2.08k
    UA_String rpstr = UA_STRING_NULL;
1146
2.08k
    UA_assert(rpstr.data == NULL && rpstr.length == 0); /* pacify clang scan-build */
1147
2.08k
    res |= printRelativePath(&ao->browsePath, &rpstr, UA_ESCAPING_PERCENT_EXTENDED);
1148
2.08k
    res |= UA_String_append(&tmp, rpstr);
1149
2.08k
    UA_String_clear(&rpstr);
1150
1151
    /* Print the attribute name */
1152
2.08k
    if(ao->attributeId != UA_ATTRIBUTEID_VALUE) {
1153
0
        const char *attrName= UA_AttributeId_name((UA_AttributeId)ao->attributeId);
1154
0
        res |= UA_String_append(&tmp, UA_STRING("#"));
1155
0
        res |= UA_String_append(&tmp, UA_STRING((char*)(uintptr_t)attrName));
1156
0
    }
1157
1158
    /* Print the IndexRange */
1159
2.08k
    if(ao->indexRange.length > 0) {
1160
80
        res |= UA_String_append(&tmp, UA_STRING("["));
1161
80
        res |= UA_String_append(&tmp, ao->indexRange);
1162
80
        res |= UA_String_append(&tmp, UA_STRING("]"));
1163
80
    }
1164
1165
    /* Encoding failed, clean up */
1166
2.08k
    if(res != UA_STATUSCODE_GOOD) {
1167
0
        UA_String_clear(&tmp);
1168
0
        return res;
1169
0
    }
1170
1171
2.08k
    return moveTmpToOut(&tmp, out);
1172
2.08k
}
1173
1174
#endif /* UA_TYPES_ATTRIBUTEOPERAND */
1175
1176
UA_StatusCode
1177
0
UA_ReadValueId_print(const UA_ReadValueId *rvi, UA_String *out) {
1178
0
    UA_String tmp = UA_STRING_NULL;
1179
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1180
1181
    /* Print the TypeDefinitionId */
1182
0
    if(!UA_NodeId_equal(&UA_NODEID_NULL, &rvi->nodeId)) {
1183
0
        UA_Byte nodeIdBuf[512];
1184
0
        UA_String nodeIdBufStr = {512, nodeIdBuf};
1185
0
        res |= nodeId_printEscape(&rvi->nodeId, &nodeIdBufStr,
1186
0
                                  NULL, UA_ESCAPING_PERCENT);
1187
0
        res |= UA_String_append(&tmp, nodeIdBufStr);
1188
0
    }
1189
1190
    /* Print the attribute name */
1191
0
    if(rvi->attributeId != UA_ATTRIBUTEID_VALUE) {
1192
0
        const char *attrName= UA_AttributeId_name((UA_AttributeId)rvi->attributeId);
1193
0
        res |= UA_String_append(&tmp, UA_STRING("#"));
1194
0
        res |= UA_String_append(&tmp, UA_STRING((char*)(uintptr_t)attrName));
1195
0
    }
1196
1197
    /* Print the IndexRange */
1198
0
    if(rvi->indexRange.length > 0) {
1199
0
        res |= UA_String_append(&tmp, UA_STRING("["));
1200
0
        res |= UA_String_append(&tmp, rvi->indexRange);
1201
0
        res |= UA_String_append(&tmp, UA_STRING("]"));
1202
0
    }
1203
1204
    /* Encoding failed, clean up */
1205
0
    if(res != UA_STATUSCODE_GOOD) {
1206
0
        UA_String_clear(&tmp);
1207
0
        return res;
1208
0
    }
1209
1210
0
    return moveTmpToOut(&tmp, out);
1211
0
}
1212
1213
/************************/
1214
/* Cryptography Helpers */
1215
/************************/
1216
1217
UA_ByteString
1218
0
getLeafCertificate(UA_ByteString chain) {
1219
    /* Detect DER encoded X.509 v3 certificate. If the DER detection fails,
1220
     * return the entire chain.
1221
     *
1222
     * The OPC UA standard requires this to be DER. But we also allow other
1223
     * formats like PEM. Afterwards it depends on the crypto backend to parse
1224
     * it. mbedTLS and OpenSSL detect the format automatically. */
1225
0
    if(chain.length < 4 || chain.data[0] != 0x30 || chain.data[1] != 0x82)
1226
0
        return chain;
1227
1228
    /* The certificate length is encoded in the next 2 bytes. */
1229
0
    size_t leafLen = 4; /* Magic numbers + length bytes */
1230
0
    leafLen += (size_t)(((uint16_t)chain.data[2]) << 8);
1231
0
    leafLen += chain.data[3];
1232
1233
    /* Consistency check */
1234
0
    if(leafLen > chain.length)
1235
0
        return UA_BYTESTRING_NULL;
1236
1237
    /* Adjust the length and return */
1238
0
    chain.length = leafLen;
1239
0
    return chain;
1240
0
}
1241
1242
UA_Boolean
1243
0
UA_constantTimeEqual(const void *ptr1, const void *ptr2, size_t length) {
1244
0
    volatile const UA_Byte *a = (volatile const UA_Byte *)ptr1;
1245
0
    volatile const UA_Byte *b = (volatile const UA_Byte *)ptr2;
1246
0
    volatile UA_Byte c = 0;
1247
0
    for(size_t i = 0; i < length; ++i) {
1248
0
        UA_Byte x = a[i], y = b[i];
1249
0
        c = c | (x ^ y);
1250
0
    }
1251
0
    return !c;
1252
0
}
1253
1254
void
1255
0
UA_ByteString_memZero(UA_ByteString *bs) {
1256
#if defined(__STDC_LIB_EXT1__)
1257
   memset_s(bs->data, bs->length, 0, bs->length);
1258
#elif defined(UA_ARCHITECTURE_WIN32)
1259
   SecureZeroMemory(bs->data, bs->length);
1260
#else
1261
0
   volatile unsigned char *volatile ptr =
1262
0
       (volatile unsigned char *)bs->data;
1263
0
   size_t i = 0;
1264
0
   size_t maxLen = bs->length;
1265
0
   while(i < maxLen) {
1266
0
       ptr[i++] = 0;
1267
0
   }
1268
0
#endif
1269
0
}
1270
1271
UA_Boolean
1272
UA_TrustListDataType_contains(const UA_TrustListDataType *trustList,
1273
                              const UA_ByteString *certificate,
1274
0
                              UA_TrustListMasks specifiedList) {
1275
0
    if(!trustList || !certificate)
1276
0
        return false;
1277
1278
0
    if(specifiedList & UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES) {
1279
0
        for(size_t i = 0; i < trustList->trustedCertificatesSize; i++) {
1280
0
            if(UA_ByteString_equal(certificate, &trustList->trustedCertificates[i]))
1281
0
                return true;
1282
0
        }
1283
0
    }
1284
0
    if(specifiedList & UA_TRUSTLISTMASKS_TRUSTEDCRLS) {
1285
0
        for(size_t i = 0; i < trustList->trustedCrlsSize; i++) {
1286
0
            if(UA_ByteString_equal(certificate, &trustList->trustedCrls[i]))
1287
0
                return true;
1288
0
        }
1289
0
    }
1290
0
    if(specifiedList & UA_TRUSTLISTMASKS_ISSUERCERTIFICATES) {
1291
0
        for(size_t i = 0; i < trustList->issuerCertificatesSize; i++) {
1292
0
            if(UA_ByteString_equal(certificate, &trustList->issuerCertificates[i]))
1293
0
                return true;
1294
0
        }
1295
0
    }
1296
0
    if(specifiedList & UA_TRUSTLISTMASKS_ISSUERCRLS) {
1297
0
        for(size_t i = 0; i < trustList->issuerCrlsSize; i++) {
1298
0
            if(UA_ByteString_equal(certificate, &trustList->issuerCrls[i]))
1299
0
                return true;
1300
0
        }
1301
0
    }
1302
1303
0
    return false;
1304
0
}
1305
1306
UA_StatusCode
1307
0
UA_TrustListDataType_add(const UA_TrustListDataType *src, UA_TrustListDataType *dst) {
1308
0
    if(!dst)
1309
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1310
0
    if(!src) {
1311
0
        return UA_STATUSCODE_GOOD;
1312
0
    }
1313
1314
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1315
1316
0
    if(src->specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES) {
1317
0
        if(dst->trustedCertificates == NULL)
1318
0
            dst->trustedCertificates = (UA_ByteString *)UA_Array_new(0, &UA_TYPES[UA_TYPES_BYTESTRING]);
1319
0
        for(size_t i = 0; i < src->trustedCertificatesSize; i++) {
1320
0
            if(UA_TrustListDataType_contains(dst, &src->trustedCertificates[i],
1321
0
                                             UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES)) {
1322
0
                continue;
1323
0
            }
1324
0
            retval = UA_Array_appendCopy((void**)&dst->trustedCertificates, &dst->trustedCertificatesSize,
1325
0
                                      &src->trustedCertificates[i], &UA_TYPES[UA_TYPES_BYTESTRING]);
1326
0
            if(retval != UA_STATUSCODE_GOOD) {
1327
0
                return retval;
1328
0
            }
1329
0
        }
1330
0
        dst->specifiedLists |= UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES;
1331
0
    }
1332
0
    if(src->specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCRLS) {
1333
0
        if(dst->trustedCrls == NULL)
1334
0
            dst->trustedCrls = (UA_ByteString *)UA_Array_new(0, &UA_TYPES[UA_TYPES_BYTESTRING]);
1335
0
        for(size_t i = 0; i < src->trustedCrlsSize; i++) {
1336
0
            if(UA_TrustListDataType_contains(dst, &src->trustedCrls[i],
1337
0
                                             UA_TRUSTLISTMASKS_TRUSTEDCRLS)) {
1338
0
                continue;
1339
0
            }
1340
0
            retval = UA_Array_appendCopy((void**)&dst->trustedCrls, &dst->trustedCrlsSize,
1341
0
                                      &src->trustedCrls[i], &UA_TYPES[UA_TYPES_BYTESTRING]);
1342
0
            if(retval != UA_STATUSCODE_GOOD) {
1343
0
                return retval;
1344
0
            }
1345
0
        }
1346
0
        dst->specifiedLists |= UA_TRUSTLISTMASKS_TRUSTEDCRLS;
1347
0
    }
1348
0
    if(src->specifiedLists & UA_TRUSTLISTMASKS_ISSUERCERTIFICATES) {
1349
0
        if(dst->issuerCertificates == NULL)
1350
0
            dst->issuerCertificates = (UA_ByteString *)UA_Array_new(0, &UA_TYPES[UA_TYPES_BYTESTRING]);
1351
0
        for(size_t i = 0; i < src->issuerCertificatesSize; i++) {
1352
0
            if(UA_TrustListDataType_contains(dst, &src->issuerCertificates[i],
1353
0
                                            UA_TRUSTLISTMASKS_ISSUERCERTIFICATES)) {
1354
0
                continue;
1355
0
            }
1356
0
            retval = UA_Array_appendCopy((void**)&dst->issuerCertificates, &dst->issuerCertificatesSize,
1357
0
                                      &src->issuerCertificates[i], &UA_TYPES[UA_TYPES_BYTESTRING]);
1358
0
            if(retval != UA_STATUSCODE_GOOD) {
1359
0
                return retval;
1360
0
            }
1361
0
        }
1362
0
        dst->specifiedLists |= UA_TRUSTLISTMASKS_ISSUERCERTIFICATES;
1363
0
    }
1364
0
    if(src->specifiedLists & UA_TRUSTLISTMASKS_ISSUERCRLS) {
1365
0
        if(dst->issuerCrls == NULL)
1366
0
            dst->issuerCrls = (UA_ByteString *)UA_Array_new(0, &UA_TYPES[UA_TYPES_BYTESTRING]);
1367
0
        for(size_t i = 0; i < src->issuerCrlsSize; i++) {
1368
0
            if(UA_TrustListDataType_contains(dst, &src->issuerCrls[i],
1369
0
                                            UA_TRUSTLISTMASKS_ISSUERCRLS)) {
1370
0
                continue;
1371
0
            }
1372
0
            retval = UA_Array_appendCopy((void**)&dst->issuerCrls, &dst->issuerCrlsSize,
1373
0
                                      &src->issuerCrls[i], &UA_TYPES[UA_TYPES_BYTESTRING]);
1374
0
            if(retval != UA_STATUSCODE_GOOD) {
1375
0
                return retval;
1376
0
            }
1377
0
        }
1378
0
        dst->specifiedLists |= UA_TRUSTLISTMASKS_ISSUERCRLS;
1379
0
    }
1380
1381
0
    return retval;
1382
0
}
1383
1384
UA_StatusCode
1385
0
UA_TrustListDataType_set(const UA_TrustListDataType *src, UA_TrustListDataType *dst) {
1386
0
    if(src->specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCERTIFICATES) {
1387
0
        UA_Array_delete(dst->trustedCertificates, dst->trustedCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1388
0
        dst->trustedCertificates = NULL;
1389
0
        dst->trustedCertificatesSize = 0;
1390
0
    }
1391
0
    if(src->specifiedLists & UA_TRUSTLISTMASKS_TRUSTEDCRLS) {
1392
0
        UA_Array_delete(dst->trustedCrls, dst->trustedCrlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1393
0
        dst->trustedCrls = NULL;
1394
0
        dst->trustedCrlsSize = 0;
1395
0
    }
1396
0
    if(src->specifiedLists & UA_TRUSTLISTMASKS_ISSUERCERTIFICATES) {
1397
0
        UA_Array_delete(dst->issuerCertificates, dst->issuerCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1398
0
        dst->issuerCertificates = NULL;
1399
0
        dst->issuerCertificatesSize = 0;
1400
0
    }
1401
0
    if(src->specifiedLists & UA_TRUSTLISTMASKS_ISSUERCRLS) {
1402
0
        UA_Array_delete(dst->issuerCrls, dst->issuerCrlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1403
0
        dst->issuerCrls = NULL;
1404
0
        dst->issuerCrlsSize = 0;
1405
0
    }
1406
0
    return UA_TrustListDataType_add(src, dst);
1407
0
}
1408
1409
UA_StatusCode
1410
0
UA_TrustListDataType_remove(const UA_TrustListDataType *src, UA_TrustListDataType *dst) {
1411
0
    if(!dst)
1412
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1413
0
    if(!src) {
1414
0
        return UA_STATUSCODE_GOOD;
1415
0
    }
1416
1417
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1418
1419
    /* remove trusted certificates */
1420
0
    if(dst->trustedCertificatesSize > 0 && src->trustedCertificatesSize > 0) {
1421
0
        UA_ByteString *newList = (UA_ByteString*)UA_calloc(dst->trustedCertificatesSize, sizeof(UA_ByteString));
1422
0
        size_t newListSize = 0;
1423
0
        size_t oldListSize = dst->trustedCertificatesSize;
1424
0
        UA_Boolean isContained = false;
1425
0
        for(size_t i = 0; i < dst->trustedCertificatesSize; i++) {
1426
0
            for(size_t j = 0; j < src->trustedCertificatesSize; j++) {
1427
0
                if(UA_ByteString_equal(&dst->trustedCertificates[i], &src->trustedCertificates[j]))
1428
0
                    isContained = true;
1429
0
            }
1430
0
            if(!isContained) {
1431
0
                UA_ByteString_copy(&dst->trustedCertificates[i], &newList[newListSize]);
1432
0
                newListSize += 1;
1433
0
            }
1434
0
            isContained = false;
1435
0
        }
1436
0
        if(newListSize < dst->trustedCertificatesSize) {
1437
0
            if(newListSize == 0) {
1438
0
                UA_free(newList);
1439
0
                newList = NULL;
1440
0
            } else {
1441
0
                retval = UA_Array_resize((void**)&newList, &oldListSize, newListSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1442
0
                if(retval != UA_STATUSCODE_GOOD) {
1443
0
                    UA_free(newList);
1444
0
                    return retval;
1445
0
                }
1446
0
            }
1447
0
        }
1448
0
        UA_Array_delete(dst->trustedCertificates, dst->trustedCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1449
0
        dst->trustedCertificatesSize = 0;
1450
0
        dst->trustedCertificates = newList;
1451
0
        dst->trustedCertificatesSize = newListSize;
1452
0
    }
1453
1454
    /* remove issuer certificates */
1455
0
    if(dst->issuerCertificatesSize > 0 && src->issuerCertificatesSize > 0) {
1456
0
        UA_ByteString *newList = (UA_ByteString*)UA_calloc(dst->issuerCertificatesSize, sizeof(UA_ByteString));
1457
0
        size_t newListSize = 0;
1458
0
        size_t oldListSize = dst->issuerCertificatesSize;
1459
0
        UA_Boolean isContained = false;
1460
0
        for(size_t i = 0; i < dst->issuerCertificatesSize; i++) {
1461
0
            for(size_t j = 0; j < src->issuerCertificatesSize; j++) {
1462
0
                if(UA_ByteString_equal(&dst->issuerCertificates[i], &src->issuerCertificates[j]))
1463
0
                    isContained = true;
1464
0
            }
1465
0
            if(!isContained) {
1466
0
                UA_ByteString_copy(&dst->issuerCertificates[i], &newList[newListSize]);
1467
0
                newListSize += 1;
1468
0
            }
1469
0
            isContained = false;
1470
0
        }
1471
0
        if(newListSize < dst->issuerCertificatesSize) {
1472
0
            if(newListSize == 0) {
1473
0
                UA_free(newList);
1474
0
                newList = NULL;
1475
0
            } else {
1476
0
                retval = UA_Array_resize((void**)&newList, &oldListSize, newListSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1477
0
                if(retval != UA_STATUSCODE_GOOD) {
1478
0
                    UA_free(newList);
1479
0
                    return retval;
1480
0
                }
1481
0
            }
1482
0
        }
1483
0
        UA_Array_delete(dst->issuerCertificates, dst->issuerCertificatesSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1484
0
        dst->issuerCertificatesSize = 0;
1485
0
        dst->issuerCertificates = newList;
1486
0
        dst->issuerCertificatesSize = newListSize;
1487
0
    }
1488
1489
    /* remove trusted crls */
1490
0
    if(dst->trustedCrlsSize > 0 && src->trustedCrlsSize > 0) {
1491
0
        UA_ByteString *newList = (UA_ByteString*)UA_calloc(dst->trustedCrlsSize, sizeof(UA_ByteString));
1492
0
        size_t newListSize = 0;
1493
0
        size_t oldListSize = dst->trustedCrlsSize;
1494
0
        UA_Boolean isContained = false;
1495
0
        for(size_t i = 0; i < dst->trustedCrlsSize; i++) {
1496
0
            for(size_t j = 0; j < src->trustedCrlsSize; j++) {
1497
0
                if(UA_ByteString_equal(&dst->trustedCrls[i], &src->trustedCrls[j]))
1498
0
                    isContained = true;
1499
0
            }
1500
0
            if(!isContained) {
1501
0
                UA_ByteString_copy(&dst->trustedCrls[i], &newList[newListSize]);
1502
0
                newListSize += 1;
1503
0
            }
1504
0
            isContained = false;
1505
0
        }
1506
0
        if(newListSize < dst->trustedCrlsSize) {
1507
0
            if(newListSize == 0) {
1508
0
                UA_free(newList);
1509
0
                newList = NULL;
1510
0
            } else {
1511
0
                retval = UA_Array_resize((void**)&newList, &oldListSize, newListSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1512
0
                if(retval != UA_STATUSCODE_GOOD) {
1513
0
                    UA_free(newList);
1514
0
                    return retval;
1515
0
                }
1516
0
            }
1517
0
        }
1518
0
        UA_Array_delete(dst->trustedCrls, dst->trustedCrlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1519
0
        dst->trustedCrlsSize = 0;
1520
0
        dst->trustedCrls = newList;
1521
0
        dst->trustedCrlsSize = newListSize;
1522
0
    }
1523
1524
    /* remove issuer crls */
1525
0
    if(dst->issuerCrlsSize > 0 && src->issuerCrlsSize > 0) {
1526
0
        UA_ByteString *newList = (UA_ByteString*)UA_calloc(dst->issuerCrlsSize, sizeof(UA_ByteString));
1527
0
        size_t newListSize = 0;
1528
0
        size_t oldListSize = dst->issuerCrlsSize;
1529
0
        UA_Boolean isContained = false;
1530
0
        for(size_t i = 0; i < dst->issuerCrlsSize; i++) {
1531
0
            for(size_t j = 0; j < src->issuerCrlsSize; j++) {
1532
0
                if(UA_ByteString_equal(&dst->issuerCrls[i], &src->issuerCrls[j]))
1533
0
                    isContained = true;
1534
0
            }
1535
0
            if(!isContained) {
1536
0
                UA_ByteString_copy(&dst->issuerCrls[i], &newList[newListSize]);
1537
0
                newListSize += 1;
1538
0
            }
1539
0
            isContained = false;
1540
0
        }
1541
0
        if(newListSize < dst->issuerCrlsSize) {
1542
0
            if(newListSize == 0) {
1543
0
                UA_free(newList);
1544
0
                newList = NULL;
1545
0
            } else {
1546
0
                retval = UA_Array_resize((void**)&newList, &oldListSize, newListSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1547
0
                if(retval != UA_STATUSCODE_GOOD) {
1548
0
                    UA_free(newList);
1549
0
                    return retval;
1550
0
                }
1551
0
            }
1552
0
        }
1553
0
        UA_Array_delete(dst->issuerCrls, dst->issuerCrlsSize, &UA_TYPES[UA_TYPES_BYTESTRING]);
1554
0
        dst->issuerCrlsSize = 0;
1555
0
        dst->issuerCrls = newList;
1556
0
        dst->issuerCrlsSize = newListSize;
1557
0
    }
1558
1559
0
    return retval;
1560
0
}
1561
1562
UA_UInt32
1563
0
UA_TrustListDataType_getSize(const UA_TrustListDataType *trustList) {
1564
0
    UA_UInt32 size = 0;
1565
0
    for(size_t i = 0; i < trustList->trustedCertificatesSize; i++) {
1566
0
        size += (UA_UInt32)trustList->trustedCertificates[i].length;
1567
0
    }
1568
0
    for(size_t i = 0; i < trustList->trustedCrlsSize; i++) {
1569
0
        size += (UA_UInt32)trustList->trustedCrls[i].length;
1570
0
    }
1571
0
    for(size_t i = 0; i < trustList->issuerCertificatesSize; i++) {
1572
0
        size += (UA_UInt32)trustList->issuerCertificates[i].length;
1573
0
    }
1574
0
    for(size_t i = 0; i < trustList->issuerCrlsSize; i++) {
1575
0
        size += (UA_UInt32)trustList->issuerCrls[i].length;
1576
0
    }
1577
0
    return size;
1578
0
}