Coverage Report

Created: 2026-09-27 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/server/ua_services_nodemanagement.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-2017 (c) Florian Palm
7
 *    Copyright 2015-2016 (c) Sten GrĂ¼ner
8
 *    Copyright 2015-2016 (c) Chris Iatrou
9
 *    Copyright 2015-2016 (c) Oleksiy Vasylyev
10
 *    Copyright 2017 (c) Julian Grothoff
11
 *    Copyright 2016 (c) LEvertz
12
 *    Copyright 2016 (c) Lorenz Haas
13
 *    Copyright 2017 (c) frax2222
14
 *    Copyright 2017-2018 (c) Stefan Profanter, fortiss GmbH
15
 *    Copyright 2017 (c) Christian von Arnim
16
 *    Copyright 2021 (c) Christian von Arnim, ISW University of Stuttgart  (for VDW and umati)
17
 *    Copyright 2017 (c) Henrik Norrman
18
 *    Copyright 2021 (c) Fraunhofer IOSB (Author: Andreas Ebner)
19
 *    Copyright 2026 (c) o6 Automation GmbH (Author: Andreas Ebner)
20
 */
21
22
#include "ua_server_internal.h"
23
#include "ua_services.h"
24
25
/*********************/
26
/* Edit Node Context */
27
/*********************/
28
29
UA_StatusCode
30
UA_Server_getNodeContext(UA_Server *server, UA_NodeId nodeId,
31
0
                         void **nodeContext) {
32
0
    lockServer(server);
33
0
    UA_StatusCode retval = getNodeContext(server, nodeId, nodeContext);
34
0
    unlockServer(server);
35
0
    return retval;
36
0
}
37
38
UA_StatusCode
39
getNodeContext(UA_Server *server, UA_NodeId nodeId,
40
1.42k
               void **nodeContext) {
41
1.42k
    const UA_Node *node = UA_NODESTORE_GET(server, &nodeId);
42
1.42k
    if(!node)
43
60
        return UA_STATUSCODE_BADNODEIDUNKNOWN;
44
1.36k
    *nodeContext = node->head.context;
45
1.36k
    UA_NODESTORE_RELEASE(server, node);
46
1.36k
    return UA_STATUSCODE_GOOD;
47
1.42k
}
48
49
static UA_StatusCode
50
setDeconstructedNode(UA_Server *server, UA_Session *session,
51
773k
                     UA_NodeHead *head, void *context) {
52
773k
    head->constructed = false;
53
773k
    return UA_STATUSCODE_GOOD;
54
773k
}
55
56
static UA_StatusCode
57
setConstructedNodeContext(UA_Server *server, UA_Session *session,
58
17.9M
                          UA_NodeHead *head, void *context) {
59
17.9M
    head->context = context;
60
17.9M
    head->constructed = true;
61
17.9M
    return UA_STATUSCODE_GOOD;
62
17.9M
}
63
64
UA_StatusCode
65
19.4k
setNodeContext(UA_Server *server, UA_NodeId nodeId, void *nodeContext) {
66
19.4k
    UA_Node *node =
67
19.4k
        UA_NODESTORE_GET_EDIT_SELECTIVE(server, &nodeId, UA_NODEATTRIBUTESMASK_NONE,
68
19.4k
                                        UA_REFERENCETYPESET_NONE,
69
19.4k
                                        UA_BROWSEDIRECTION_INVALID);
70
19.4k
    if(!node)
71
19.4k
        return UA_STATUSCODE_BADNODEIDINVALID;
72
0
    node->head.context = nodeContext;
73
0
    UA_NODESTORE_RELEASE(server, node);
74
0
    return UA_STATUSCODE_GOOD;
75
19.4k
}
76
77
UA_StatusCode
78
UA_Server_setNodeContext(UA_Server *server, UA_NodeId nodeId,
79
0
                         void *nodeContext) {
80
0
    lockServer(server);
81
0
    UA_StatusCode retval = setNodeContext(server, nodeId, nodeContext);
82
0
    unlockServer(server);
83
0
    return retval;
84
0
}
85
86
static UA_StatusCode
87
checkSetIsDynamicVariable(UA_Server *server, UA_Session *session,
88
                          const UA_NodeId *nodeId);
89
90
/**********************/
91
/* Consistency Checks */
92
/**********************/
93
94
#define UA_PARENT_REFERENCES_COUNT 2
95
96
static const UA_NodeId parentReferences[UA_PARENT_REFERENCES_COUNT] = {
97
    {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}},
98
    {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HIERARCHICALREFERENCES}}
99
};
100
101
static void
102
logAddNode(const UA_Logger *logger, UA_Session *session,
103
40.2k
           const UA_NodeId *nodeId, const char *msg) {
104
40.2k
    UA_LOG_INFO_SESSION(logger, session, "AddNode (%N): %s", *nodeId, msg);
105
40.2k
}
106
107
/* Check if the requested parent node exists, has the right node class and is
108
 * referenced with an allowed (hierarchical) reference type. For "type" nodes,
109
 * only hasSubType references are allowed. */
110
static UA_StatusCode
111
checkParentReference(UA_Server *server, UA_Session *session, const UA_NodeHead *head,
112
5.97M
                     const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
113
5.97M
    UA_Boolean noParent = UA_NodeId_isNull(parentNodeId) &&
114
77.5k
                          UA_NodeId_isNull(referenceTypeId);
115
116
    /* Objects do not need a parent (e.g. mandatory/optional modellingrules).
117
     * Also, there are some variables which do not have parents, e.g.
118
     * EnumStrings, EnumValues */
119
5.97M
    if((head->nodeClass == UA_NODECLASS_OBJECT ||
120
5.48M
        head->nodeClass == UA_NODECLASS_VARIABLE) &&
121
4.59M
       noParent)
122
77.5k
        return UA_STATUSCODE_GOOD;
123
124
    /* Part 3 requires Methods to be the target of a HasComponent reference.
125
     * Accept detached Methods for compatibility with legacy NodeSets. */
126
5.89M
    if(head->nodeClass == UA_NODECLASS_METHOD && noParent) {
127
0
        UA_LOG_WARNING_SESSION(server->config.logging, session,
128
0
                               "AddNode (%N): The Method is detached (has no parent)",
129
0
                               head->nodeId);
130
0
        return UA_STATUSCODE_GOOD;
131
0
    }
132
133
    /* See if the parent exists */
134
5.89M
    const UA_Node *parent = UA_NODESTORE_GET(server, parentNodeId);
135
5.89M
    if(!parent) {
136
25
        UA_LOG_INFO_SESSION(server->config.logging, session,
137
25
                            "AddNode (%N): Parent node %N not found",
138
25
                            head->nodeId, *parentNodeId);
139
25
        return UA_STATUSCODE_BADPARENTNODEIDINVALID;
140
25
    }
141
142
5.89M
    UA_NodeClass parentNodeClass = parent->head.nodeClass;
143
5.89M
    UA_NODESTORE_RELEASE(server, parent);
144
145
    /* Check the referencetype exists */
146
5.89M
    const UA_Node *referenceType = UA_NODESTORE_GET(server, referenceTypeId);
147
5.89M
    if(!referenceType) {
148
18
        UA_LOG_INFO_SESSION(server->config.logging, session,
149
18
                            "AddNode (%N): Reference type %N to the parent not found",
150
18
                            head->nodeId, *referenceTypeId);
151
18
        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
152
18
    }
153
154
    /* Check if the referencetype is a reference type node */
155
5.89M
    if(referenceType->head.nodeClass != UA_NODECLASS_REFERENCETYPE) {
156
0
        UA_LOG_INFO_SESSION(server->config.logging, session,
157
0
                            "AddNode (%N): Reference type %N to the parent is "
158
0
                            "not a ReferenceTypeNode", head->nodeId, *referenceTypeId);
159
0
        UA_NODESTORE_RELEASE(server, referenceType);
160
0
        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
161
0
    }
162
163
    /* Read the attributes used below before releasing the node */
164
5.89M
    UA_Boolean referenceTypeIsAbstract = referenceType->referenceTypeNode.isAbstract;
165
5.89M
    UA_Byte refTypeIndex = referenceType->referenceTypeNode.referenceTypeIndex;
166
5.89M
    UA_NODESTORE_RELEASE(server, referenceType);
167
5.89M
    if(referenceTypeIsAbstract == true) {
168
0
        UA_LOG_INFO_SESSION(server->config.logging, session,
169
0
                            "AddNode (%N): Reference type %N to the parent is abstract",
170
0
                            head->nodeId, *referenceTypeId);
171
0
        return UA_STATUSCODE_BADREFERENCENOTALLOWED;
172
0
    }
173
174
    /* Check hassubtype relation for type nodes */
175
5.89M
    if(head->nodeClass == UA_NODECLASS_DATATYPE ||
176
5.43M
       head->nodeClass == UA_NODECLASS_VARIABLETYPE ||
177
5.27M
       head->nodeClass == UA_NODECLASS_OBJECTTYPE ||
178
4.92M
       head->nodeClass == UA_NODECLASS_REFERENCETYPE) {
179
        /* Type needs hassubtype reference to the supertype */
180
1.14M
        if(refTypeIndex != UA_REFERENCETYPEINDEX_HASSUBTYPE) {
181
0
            logAddNode(server->config.logging, session, &head->nodeId,
182
0
                       "Type nodes need to have a HasSubType reference to the parent");
183
0
            return UA_STATUSCODE_BADREFERENCENOTALLOWED;
184
0
        }
185
        /* Supertype needs to be of the same node type  */
186
1.14M
        if(parentNodeClass != head->nodeClass) {
187
0
            logAddNode(server->config.logging, session, &head->nodeId,
188
0
                       "Type nodes needs to be of the same node "
189
0
                       "type as their parent");
190
0
            return UA_STATUSCODE_BADPARENTNODEIDINVALID;
191
0
        }
192
1.14M
        return UA_STATUSCODE_GOOD;
193
1.14M
    }
194
195
    /* Test if the referencetype is hierarchical */
196
4.75M
    const UA_NodeId hierarchRefs = UA_NS0ID(HIERARCHICALREFERENCES);
197
4.75M
    if(!isNodeInTree_singleRef(server, referenceTypeId, &hierarchRefs,
198
4.75M
                               UA_REFERENCETYPEINDEX_HASSUBTYPE)) {
199
0
        logAddNode(server->config.logging, session, &head->nodeId,
200
0
                   "Reference type to the parent is not hierarchical");
201
0
        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
202
0
    }
203
204
4.75M
    return UA_STATUSCODE_GOOD;
205
4.75M
}
206
207
/* Only BaseDataType can have empty values. Generate a default value. */
208
static UA_StatusCode
209
0
setDefaultValue(UA_Server *server, const UA_VariableNode *node) {
210
    /* Get the DataType */
211
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
212
0
    const UA_DataType *type = UA_Server_findDataType(server, &node->dataType);
213
0
    if(!type) {
214
        /* No description for the DataType found. It is possible that an
215
         * abstract DataType is used, e.g. UInteger. Browse to see if there is a
216
         * non-abstract subtype that can be used for the default value.
217
         *
218
         * Look up and downwards in the hierarchy. Some data types (e.g.
219
         * UtcTime) are derived from a non-abstract data type. This is then used
220
         * for the actual value, Use the first match. */
221
0
        UA_ReferenceTypeSet refs = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE);
222
0
        UA_ExpandedNodeId *typeCandidates = NULL;
223
0
        size_t typeCandidatesSize = 0;
224
0
        res = browseRecursive(server, 1, &node->dataType,
225
0
                              UA_BROWSEDIRECTION_BOTH, &refs,
226
0
                              UA_NODECLASS_DATATYPE, false,
227
0
                              &typeCandidatesSize, &typeCandidates);
228
0
        if(res != UA_STATUSCODE_GOOD)
229
0
            return res;
230
231
0
        for(size_t i = 0; i < typeCandidatesSize; i++) {
232
            /* Skip BaseDataType (Variant). This is the root of the DataType
233
             * hierarchy. Variables of BaseDataType can be empty. */
234
0
            if(UA_NodeId_equal(&UA_TYPES[UA_TYPES_VARIANT].typeId,
235
0
                               &typeCandidates[i].nodeId))
236
0
                continue;
237
238
            /* Skip ExtensionObject. Even though we can encode the type it is
239
             * abstract and nodes should not contain them as value. */
240
0
            if(UA_NodeId_equal(&UA_TYPES[UA_TYPES_EXTENSIONOBJECT].typeId,
241
0
                               &typeCandidates[i].nodeId))
242
0
                continue;
243
244
            /* Get the type encoding information */
245
0
            type = UA_Server_findDataType(server, &typeCandidates[i].nodeId);
246
0
            if(type)
247
0
                break;
248
0
        }
249
250
0
        UA_Array_delete(typeCandidates, typeCandidatesSize,
251
0
                        &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
252
0
        if(!type)
253
0
            return UA_STATUSCODE_BADTYPEMISMATCH;
254
0
    }
255
256
    /* Set up the value with the default content */
257
0
    UA_Variant val;
258
0
    UA_Variant_init(&val);
259
0
    if(node->valueRank < 0) {
260
        /* Set a scalar */
261
0
        void *data = UA_new(type);
262
0
        if(!data)
263
0
            return UA_STATUSCODE_BADOUTOFMEMORY;
264
0
        UA_Variant_setScalar(&val, data, type);
265
0
    } else {
266
        /* Set an array
267
         *
268
         * https://reference.opcfoundation.org/v104/Core/docs/Part3/5.6.2/#Table13
269
         * specifies ArrayDimensions as follows: This Attribute specifies the
270
         * maximum supported length of each dimension. If the maximum is unknown
271
         * the value shall be 0. The number of elements shall be equal to the
272
         * value of the ValueRank Attribute. This Attribute shall be null if
273
         * ValueRank <= 0.
274
         *
275
         * The (variant) values themselves cannot have ArrayDimensions with a
276
         * dimension length of zero. We however consider that empty arrays
277
         * (null-array or length zero) have implicit array dimensions [0,0,...].
278
         * With the appropriate number of dimensions. So they always match.
279
         *
280
         * We automatically create a null array during the node creation when
281
         * required for the type-checking..
282
         *
283
         * Also see the method 'compatibleValueArrayDimensions' where the
284
         * compatibility of the ArrayDimensions is checked. */
285
0
        UA_Variant_setArray(&val, NULL, 0, type);
286
0
    }
287
288
    /* Write the value */
289
0
    res = writeAttribute(server, &server->adminSession, &node->head.nodeId,
290
0
                         UA_ATTRIBUTEID_VALUE, &val, &UA_TYPES[UA_TYPES_VARIANT]);
291
292
    /* Clean up */
293
0
    UA_Variant_clear(&val);
294
0
    return res;
295
0
}
296
297
static UA_StatusCode
298
typeCheckVariableNode(UA_Server *server, UA_Session *session,
299
                      const UA_VariableNode *node,
300
12.6M
                      const UA_VariableTypeNode *vt) {
301
    /* Check the datatype against the vt */
302
12.6M
    if(!compatibleDataTypes(server, &node->dataType, &vt->dataType)) {
303
0
        logAddNode(server->config.logging, session, &node->head.nodeId,
304
0
                   "The value of is incompatible with "
305
0
                   "the datatype of the VariableType");
306
0
        return UA_STATUSCODE_BADTYPEMISMATCH;
307
0
    }
308
309
    /* Check valueRank against array dimensions */
310
12.6M
    if(!compatibleValueRankArrayDimensions(server, session, node->valueRank,
311
12.6M
                                           node->arrayDimensionsSize)) {
312
0
        logAddNode(server->config.logging, session, &node->head.nodeId,
313
0
                   "The value rank of is incompatible with its array dimensions");
314
0
        return UA_STATUSCODE_BADTYPEMISMATCH;
315
0
    }
316
317
    /* Check valueRank against the vt */
318
12.6M
    if(!compatibleValueRanks(node->valueRank, vt->valueRank)) {
319
0
        logAddNode(server->config.logging, session, &node->head.nodeId,
320
0
                   "The value rank is incompatible "
321
0
                   "with the value rank of the VariableType");
322
0
        return UA_STATUSCODE_BADTYPEMISMATCH;
323
0
    }
324
325
    /* Check array dimensions against the vt */
326
12.6M
    if(!compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions,
327
12.6M
                                  node->arrayDimensionsSize, node->arrayDimensions)) {
328
0
        logAddNode(server->config.logging, session, &node->head.nodeId,
329
0
                   "The array dimensions are incompatible with the "
330
0
                   "array dimensions of the VariableType");
331
0
        return UA_STATUSCODE_BADTYPEMISMATCH;
332
0
    }
333
334
    /* Typecheck the value. Less strict for NS0 -- we adjust nodes after loading
335
     * the generated definitions. */
336
12.6M
    if(server->bootstrapNS0)
337
12.1M
        return UA_STATUSCODE_GOOD;
338
339
    /* The value might come from a datasource, so we perform a
340
     * regular read. */
341
487k
    UA_DataValue value;
342
487k
    UA_DataValue_init(&value);
343
487k
    UA_StatusCode retval = readValueAttribute(server, session, node, &value);
344
487k
    if(retval != UA_STATUSCODE_GOOD)
345
0
        return retval;
346
347
    /* We have a value. Write it back to perform checks and adjustments. */
348
487k
    const char *reason;
349
487k
    if(node->valueSourceType == UA_VALUESOURCETYPE_INTERNAL && value.hasValue) {
350
0
        if(!compatibleValue(server, session, &node->dataType, node->valueRank,
351
0
                            node->arrayDimensionsSize, node->arrayDimensions,
352
0
                            &value.value, NULL, &reason)) {
353
0
            retval = writeAttribute(server, session, &node->head.nodeId,
354
0
                                    UA_ATTRIBUTEID_VALUE, &value.value,
355
0
                                    &UA_TYPES[UA_TYPES_VARIANT]);
356
0
        }
357
0
        UA_DataValue_clear(&value);
358
0
        return retval;
359
0
    }
360
361
    /* Only BaseDataType (Variant) can have empty values */
362
487k
    if(!value.hasValue &&
363
487k
       !UA_NodeId_equal(&node->dataType, &UA_TYPES[UA_TYPES_VARIANT].typeId)) {
364
        /* Warn if that is configured */
365
487k
        if(server->config.allowEmptyVariables != UA_RULEHANDLING_ACCEPT)
366
487k
            UA_LOG_DEBUG_SESSION(server->config.logging, session,
367
487k
                                 "AddNode (%N): The value is empty. "
368
487k
                                 "But this is only allowed for BaseDataType. "
369
487k
                                 "Create a matching default value.",
370
487k
                                 node->head.nodeId);
371
372
        /* Abort if that is configured */
373
487k
        if(server->config.allowEmptyVariables == UA_RULEHANDLING_ABORT)
374
0
            retval = UA_STATUSCODE_BADTYPEMISMATCH;
375
376
        /* Try to generate and write a default value */
377
487k
        if(server->config.allowEmptyVariables == UA_RULEHANDLING_DEFAULT) {
378
0
            retval = setDefaultValue(server, node);
379
0
            if(retval != UA_STATUSCODE_GOOD) {
380
0
                UA_LOG_INFO_SESSION(server->config.logging, session,
381
0
                                    "AddNode (%N): Could not create a default value "
382
0
                                    "with StatusCode %s", node->head.nodeId,
383
0
                                    UA_StatusCode_name(retval));
384
0
            }
385
0
        }
386
487k
        return retval;
387
487k
    }
388
389
    /* Type-check the value */
390
0
    UA_Boolean compatible =
391
0
        compatibleValue(server, session, &node->dataType,
392
0
            node->valueRank, node->arrayDimensionsSize,
393
0
            node->arrayDimensions, &value.value, NULL, &reason);
394
0
    if(!compatible) {
395
0
        UA_LOG_INFO_SESSION(server->config.logging, session,
396
0
                            "AddNode (%N): The VariableNode value has "
397
0
                            "failed the type check with reason %s. ",
398
0
                            node->head.nodeId, reason);
399
0
        retval = UA_STATUSCODE_BADINTERNALERROR;
400
0
    }
401
402
0
    UA_DataValue_clear(&value);
403
0
    return retval;
404
487k
}
405
406
/********************/
407
/* Instantiate Node */
408
/********************/
409
410
static const UA_NodeId baseDataVariableType =
411
    {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEDATAVARIABLETYPE}};
412
static const UA_NodeId baseObjectType =
413
    {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEOBJECTTYPE}};
414
static const UA_NodeId hasTypeDefinition =
415
    {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASTYPEDEFINITION}};
416
417
static UA_Boolean
418
compatibleVariableTypeValue(UA_Server *server, UA_Session *session,
419
                            const UA_VariableNode *node,
420
                            const UA_VariableTypeNode *vt,
421
0
                            const UA_Variant *value) {
422
0
    const UA_NodeId *dataType = &node->dataType;
423
0
    if(UA_NodeId_isNull(dataType))
424
0
        dataType = &vt->dataType;
425
426
0
    size_t arrayDimensionsSize = node->arrayDimensionsSize;
427
0
    const UA_UInt32 *arrayDimensions = node->arrayDimensions;
428
0
    if(arrayDimensionsSize == 0 && vt->arrayDimensionsSize > 0) {
429
0
        arrayDimensionsSize = vt->arrayDimensionsSize;
430
0
        arrayDimensions = vt->arrayDimensions;
431
0
    }
432
433
0
    const char *reason;
434
0
    return compatibleValue(server, session, dataType, node->valueRank,
435
0
                           arrayDimensionsSize, arrayDimensions, value,
436
0
                           NULL, &reason);
437
0
}
438
439
/* Use attributes from the variable type wherever required. Reload the node if
440
 * changes were made. */
441
static UA_StatusCode
442
useVariableTypeAttributes(UA_Server *server, UA_Session *session,
443
                          const UA_VariableNode *node,
444
12.6M
                          const UA_VariableTypeNode *vt) {
445
    /* If no value is set, see if the vt provides one and copy it. This needs to
446
     * be done before copying the datatype from the vt, as setting the datatype
447
     * triggers a typecheck. */
448
12.6M
    UA_DataValue origDv;
449
12.6M
    UA_DataValue_init(&origDv);
450
12.6M
    UA_StatusCode retval = readValueAttribute(server, session, node, &origDv);
451
12.6M
    if(retval != UA_STATUSCODE_GOOD)
452
0
        return retval;
453
454
12.6M
    if(origDv.hasValue && origDv.value.type) {
455
        /* A value is present */
456
1.36M
        UA_DataValue_clear(&origDv);
457
11.2M
    } else {
458
11.2M
        UA_DataValue_clear(&origDv);
459
11.2M
        UA_DataValue v;
460
11.2M
        UA_DataValue_init(&v);
461
11.2M
        retval = readValueAttribute(server, session, (const UA_VariableNode*)vt, &v);
462
11.2M
        if(retval == UA_STATUSCODE_GOOD && v.hasValue &&
463
0
           compatibleVariableTypeValue(server, session, node, vt, &v.value)) {
464
0
            retval = writeAttribute(server, session, &node->head.nodeId,
465
0
                                    UA_ATTRIBUTEID_VALUE, &v.value,
466
0
                                    &UA_TYPES[UA_TYPES_VARIANT]);
467
0
        }
468
11.2M
        UA_DataValue_clear(&v);
469
470
11.2M
        if(retval != UA_STATUSCODE_GOOD) {
471
0
            logAddNode(server->config.logging, session, &node->head.nodeId,
472
0
                       "The default content of the VariableType could "
473
0
                       "not be used. This may happen if the VariableNode "
474
0
                       "makes additional restrictions.");
475
0
            retval = UA_STATUSCODE_GOOD;
476
0
        }
477
11.2M
    }
478
479
    /* If no datatype is given, use the datatype of the vt */
480
12.6M
    if(UA_NodeId_isNull(&node->dataType)) {
481
0
        logAddNode(server->config.logging, session, &node->head.nodeId,
482
0
                   "No datatype given; Copy the datatype attribute "
483
0
                   "from the TypeDefinition");
484
0
        retval = writeAttribute(server, session, &node->head.nodeId,
485
0
                                UA_ATTRIBUTEID_DATATYPE, &vt->dataType,
486
0
                                &UA_TYPES[UA_TYPES_NODEID]);
487
0
        if(retval != UA_STATUSCODE_GOOD)
488
0
            return retval;
489
0
    }
490
491
    /* Use the ArrayDimensions of the vt */
492
12.6M
    if(node->arrayDimensionsSize == 0 && vt->arrayDimensionsSize > 0) {
493
0
        UA_Variant v;
494
0
        UA_Variant_init(&v);
495
0
        UA_Variant_setArray(&v, vt->arrayDimensions, vt->arrayDimensionsSize,
496
0
                            &UA_TYPES[UA_TYPES_UINT32]);
497
0
        retval = writeAttribute(server, session, &node->head.nodeId,
498
0
                                UA_ATTRIBUTEID_ARRAYDIMENSIONS, &v,
499
0
                                &UA_TYPES[UA_TYPES_VARIANT]);
500
0
    }
501
502
12.6M
    return retval;
503
12.6M
}
504
505
struct findChildContext {
506
    UA_Server *server;
507
    UA_QualifiedName browseName;
508
    UA_UInt32 browseNameHash;
509
    UA_NodeClass nodeClassMask;
510
    UA_NodeId *outInstanceNodeId;
511
};
512
513
static void *
514
115M
findChildCallback(void *context, UA_ReferenceTarget *t) {
515
115M
    struct findChildContext *ctx = (struct findChildContext*)context;
516
517
    /* No ExpandedNodeId pointing to another server */
518
115M
    if(!UA_NodePointer_isLocal(t->targetId))
519
0
        return NULL;
520
521
    /* Compare the hash (fast) */
522
115M
    if(t->targetNameHash != ctx->browseNameHash)
523
108M
        return NULL;
524
525
    /* Get the node to compare the full attributes */
526
7.40M
    const UA_Node *refTarget =
527
7.40M
        UA_NODESTORE_GETFROMREF_SELECTIVE(ctx->server, t->targetId,
528
7.40M
                                          UA_NODEATTRIBUTESMASK_BROWSENAME,
529
7.40M
                                          UA_REFERENCETYPESET_NONE,
530
7.40M
                                          UA_BROWSEDIRECTION_INVALID);
531
7.40M
    if(!refTarget)
532
0
        return NULL;
533
534
    /* Compare NodeClass and BrowseName */
535
7.40M
    if(!(refTarget->head.nodeClass & ctx->nodeClassMask) ||
536
7.40M
       !UA_QualifiedName_equal(&ctx->browseName, &refTarget->head.browseName)) {
537
0
        UA_NODESTORE_RELEASE(ctx->server, refTarget);
538
0
        return NULL;
539
0
    }
540
541
7.40M
    UA_NODESTORE_RELEASE(ctx->server, refTarget);
542
543
    /* Copy the child NodeId and return a sentinel value */
544
7.40M
    UA_NodeId origId = UA_NodePointer_toNodeId(t->targetId);
545
7.40M
    UA_StatusCode res = UA_NodeId_copy(&origId, ctx->outInstanceNodeId);
546
7.40M
    return (res == UA_STATUSCODE_GOOD) ? (void*)0x01 : (void*)0x02;
547
7.40M
}
548
549
UA_StatusCode
550
findChildByBrowsename(UA_Server *server, UA_Session *session,
551
                      const UA_NodeId parentId, UA_NodeClass nodeClassMask,
552
                      const UA_Byte refType, const UA_NodeId refTypeId,
553
                      const UA_QualifiedName *browseName,
554
10.4M
                      UA_NodeId *outChildNodeId) {
555
    /* Begin with HasComponent references only. This is the most common case as
556
     * a "fast path". If this fails also look for subtypes of HasComponent. */
557
10.4M
    UA_Boolean subTypes = false;
558
10.4M
    UA_ReferenceTypeSet refTypes = UA_REFTYPESET(refType);
559
560
    /* Setup the context */
561
10.4M
    struct findChildContext ctx;
562
10.4M
    ctx.server = server;
563
10.4M
    ctx.browseName = *browseName;
564
10.4M
    ctx.browseNameHash = UA_QualifiedName_hash(browseName);
565
10.4M
    ctx.nodeClassMask = nodeClassMask;
566
10.4M
    ctx.outInstanceNodeId = outChildNodeId;
567
568
    /* Get the parent node */
569
10.4M
    void *found = NULL;
570
10.4M
    const UA_Node *parent;
571
20.8M
 get_parent:
572
20.8M
    parent = UA_NODESTORE_GET_SELECTIVE(server, &parentId,
573
20.8M
                                        UA_NODEATTRIBUTESMASK_NONE,
574
20.8M
                                        refTypes, UA_BROWSEDIRECTION_FORWARD);
575
20.8M
    if(!parent)
576
0
        return UA_STATUSCODE_BADNODEIDUNKNOWN;
577
578
    /* Loop over the references to find a match */
579
87.0M
    for(size_t i = 0; i < parent->head.referencesSize; i++) {
580
73.6M
        UA_NodeReferenceKind *rk = &parent->head.references[i];
581
73.6M
        if(rk->isInverse)
582
21.0M
            continue;
583
52.5M
        if(!UA_ReferenceTypeSet_contains(&refTypes, rk->referenceTypeIndex))
584
42.8M
            continue;
585
9.71M
        found = UA_NodeReferenceKind_iterate(rk, findChildCallback, &ctx);
586
9.71M
        if(found)
587
7.40M
            break;
588
9.71M
    }
589
590
20.8M
    UA_NODESTORE_RELEASE(server, parent);
591
592
    /* Also consider subtypes of for references*/
593
20.8M
    if(!found && !subTypes) {
594
10.4M
        UA_StatusCode res =
595
10.4M
            referenceTypeIndices(server, &refTypeId, &refTypes, true);
596
10.4M
        if(res != UA_STATUSCODE_GOOD)
597
0
            return res;
598
10.4M
        subTypes = true;
599
10.4M
        goto get_parent;
600
10.4M
    }
601
602
    /* Error */
603
10.4M
    if(found == (void *)0x02)
604
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
605
606
    /* Done */
607
10.4M
    return (found) ? UA_STATUSCODE_GOOD : UA_STATUSCODE_BADNOTFOUND;
608
10.4M
}
609
610
static const UA_ExpandedNodeId mandatoryId =
611
    {{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_MODELLINGRULE_MANDATORY}}, {0, NULL}, 0};
612
613
static UA_Boolean
614
isMandatoryChild(UA_Server *server, UA_Session *session,
615
3.04M
                 const UA_NodeId *childNodeId) {
616
    /* Get the child */
617
3.04M
    const UA_Node *child = UA_NODESTORE_GET(server, childNodeId);
618
3.04M
    if(!child)
619
0
        return false;
620
621
    /* Look for the reference making the child mandatory */
622
3.04M
    UA_Boolean found = false;
623
11.2M
    for(size_t i = 0; i < child->head.referencesSize; ++i) {
624
9.22M
        UA_NodeReferenceKind *rk = &child->head.references[i];
625
9.22M
        if(rk->referenceTypeIndex != UA_REFERENCETYPEINDEX_HASMODELLINGRULE)
626
6.22M
            continue;
627
3.00M
        if(rk->isInverse)
628
0
            continue;
629
630
3.00M
        if(UA_NodeReferenceKind_findTarget(rk, &mandatoryId)) {
631
1.01M
            found = true;
632
1.01M
            break;
633
1.01M
        }
634
3.00M
    }
635
636
3.04M
    UA_NODESTORE_RELEASE(server, child);
637
3.04M
    return found;
638
3.04M
}
639
640
8.30M
#define UA_MAX_NODE_INSTANTIATION_DEPTH 64
641
642
static UA_StatusCode
643
beginChildInstantiation(UA_Server *server, UA_Session *session,
644
                        const UA_NodeId *destinationNodeId,
645
8.30M
                        const UA_NodeId *sourceNodeId) {
646
8.30M
    if(server->nodeInstantiationDepth >= UA_MAX_NODE_INSTANTIATION_DEPTH) {
647
0
        UA_LOG_WARNING_SESSION(server->config.logging, session,
648
0
                               "AddNode (%N): Recursive child instantiation "
649
0
                               "exceeded the maximum depth %u while copying %N",
650
0
                               *destinationNodeId,
651
0
                               UA_MAX_NODE_INSTANTIATION_DEPTH, *sourceNodeId);
652
0
        return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
653
0
    }
654
655
8.30M
    server->nodeInstantiationDepth++;
656
8.30M
    return UA_STATUSCODE_GOOD;
657
8.30M
}
658
659
static void
660
8.30M
endChildInstantiation(UA_Server *server) {
661
8.30M
    UA_assert(server->nodeInstantiationDepth > 0);
662
8.30M
    server->nodeInstantiationDepth--;
663
8.30M
}
664
665
static UA_StatusCode
666
copyAllChildren(UA_Server *server, UA_Session *session,
667
                const UA_NodeId *source, const UA_NodeId *destination);
668
669
static void
670
Operation_addReference(UA_Server *server, UA_Session *session, void *context,
671
                       const UA_AddReferencesItem *item, UA_StatusCode *retval);
672
673
UA_StatusCode
674
addRefWithSession(UA_Server *server, UA_Session *session, const UA_NodeId *sourceId,
675
                  const UA_NodeId *referenceTypeId, const UA_NodeId *targetId,
676
31.6M
                  UA_Boolean forward) {
677
31.6M
    UA_AddReferencesItem ref_item;
678
31.6M
    UA_AddReferencesItem_init(&ref_item);
679
31.6M
    ref_item.sourceNodeId = *sourceId;
680
31.6M
    ref_item.referenceTypeId = *referenceTypeId;
681
31.6M
    ref_item.isForward = forward;
682
31.6M
    ref_item.targetNodeId.nodeId = *targetId;
683
684
31.6M
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
685
31.6M
    Operation_addReference(server, session, NULL, &ref_item, &retval);
686
31.6M
    return retval;
687
31.6M
}
688
689
UA_StatusCode
690
addRef(UA_Server *server, const UA_NodeId sourceId,
691
       const UA_NodeId referenceTypeId, const UA_NodeId targetId,
692
375k
       UA_Boolean forward) {
693
375k
    return addRefWithSession(server, &server->adminSession, &sourceId,
694
375k
                             &referenceTypeId, &targetId, forward);
695
375k
}
696
697
static UA_StatusCode
698
addInterfaceChildren(UA_Server *server, UA_Session *session,
699
1.46M
                     const UA_NodeId *nodeId) {
700
    /* Get the hierarchy of the type and all its supertypes */
701
1.46M
    UA_NodeId *hierarchy = NULL;
702
1.46M
    size_t hierarchySize = 0;
703
1.46M
    UA_StatusCode retval = getAllInterfaces(server, nodeId, &hierarchy, &hierarchySize);
704
1.46M
    if(retval != UA_STATUSCODE_GOOD)
705
0
        return retval;
706
707
    /* Copy members of the type and supertypes (and instantiate them) */
708
1.46M
    for(size_t i = 0; i < hierarchySize; ++i) {
709
0
        retval = copyAllChildren(server, session, &hierarchy[i], nodeId);
710
0
        if(retval != UA_STATUSCODE_GOOD) {
711
0
            UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]);
712
0
            return retval;
713
0
        }
714
0
    }
715
716
1.46M
    for(size_t i = 0; i < hierarchySize; ++i) {
717
0
        UA_NodeId refId = UA_NS0ID(HASINTERFACE);
718
0
        retval = addRef(server, *nodeId, refId, hierarchy[i], true);
719
720
        /* Don't add the original HasInterface reference to ObjectType sub nodes */
721
0
        if(retval == UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED) {
722
0
            retval = UA_STATUSCODE_GOOD;
723
0
        } else if(retval != UA_STATUSCODE_GOOD) {
724
0
            break;
725
0
        }
726
0
    }
727
728
1.46M
    UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]);
729
1.46M
    return retval;
730
1.46M
}
731
732
static UA_StatusCode
733
copyObjectVariableChild(UA_Server *server, UA_Session *session,
734
                        const UA_NodeId *destinationNodeId,
735
397k
                        const UA_ReferenceDescription *rd) {
736
    /* This creates a new logical node from an instance declaration. It is not
737
     * an editable replacement of the source node, so runtime associations such
738
     * as attached MonitoredItems must not be copied. */
739
397k
    const UA_Node *source = UA_NODESTORE_GET(server, &rd->nodeId.nodeId);
740
397k
    if(!source)
741
0
        return UA_STATUSCODE_BADNODEIDUNKNOWN;
742
743
397k
    UA_Node *node = UA_NODESTORE_NEW(server, source->head.nodeClass);
744
397k
    if(!node) {
745
0
        UA_NODESTORE_RELEASE(server, source);
746
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
747
0
    }
748
749
397k
    UA_StatusCode res = UA_Node_copy(source, node);
750
397k
    UA_NODESTORE_RELEASE(server, source);
751
397k
    if(res != UA_STATUSCODE_GOOD) {
752
0
        UA_NODESTORE_DELETE(server, node);
753
0
        return res;
754
0
    }
755
756
    /* Remove the context of the copied node */
757
397k
    node->head.context = NULL;
758
397k
    node->head.constructed = false;
759
760
    /* The value source callbacks are copied by default. But we don't want
761
     * to keep it here. */
762
397k
    if(node->head.nodeClass == UA_NODECLASS_VARIABLE ||
763
345k
       node->head.nodeClass == UA_NODECLASS_VARIABLETYPE) {
764
345k
        if(node->variableNode.valueSourceType == UA_VALUESOURCETYPE_INTERNAL ||
765
345k
           node->variableNode.valueSourceType == UA_VALUESOURCETYPE_EXTERNAL) {
766
345k
            memset(&node->variableNode.valueSource.internal.notifications, 0,
767
345k
                   sizeof(UA_ValueSourceNotifications));
768
345k
        } else {
769
0
            memset(&node->variableNode.valueSource.callback, 0,
770
0
                   sizeof(UA_CallbackValueSource));
771
772
0
        }
773
345k
        node->variableNode.valueSourceType = UA_VALUESOURCETYPE_INTERNAL;
774
345k
    }
775
776
    /* Reset the NodeId (random numeric id will be assigned in the nodestore) */
777
397k
    UA_NodeId_clear(&node->head.nodeId);
778
397k
    node->head.nodeId.namespaceIndex = destinationNodeId->namespaceIndex;
779
397k
    if(server->config.nodeLifecycle &&
780
0
       server->config.nodeLifecycle->generateChildNodeId) {
781
0
        res = server->config.nodeLifecycle->
782
0
            generateChildNodeId(server, &session->sessionId, session->context,
783
0
                                &rd->nodeId.nodeId, destinationNodeId,
784
0
                                &rd->referenceTypeId, &node->head.nodeId);
785
0
        if(res != UA_STATUSCODE_GOOD) {
786
0
            UA_NODESTORE_DELETE(server, node);
787
0
            return res;
788
0
        }
789
0
    }
790
791
    /* Remove references, they are re-created from scratch in
792
     * addnode_finish. For now we keep all the modelling rule references and
793
     * delete all others. */
794
795
    /* TODO: Be more clever in removing references that are re-added during
796
     * addnode_finish. That way, we can call addnode_finish also on children
797
     * that were manually added by the user during addnode_begin and
798
     * addnode_finish. */
799
397k
    const UA_NodeId nodeId_typesFolder = UA_NS0ID(TYPESFOLDER);
800
397k
    const UA_ReferenceTypeSet reftypes_aggregates =
801
397k
        UA_REFTYPESET(UA_REFERENCETYPEINDEX_AGGREGATES);
802
397k
    UA_ReferenceTypeSet reftypes_skipped =
803
397k
        UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASINTERFACE);
804
397k
    if(server->config.modellingRulesOnInstances ||
805
0
       isNodeInTree(server, destinationNodeId,
806
397k
                    &nodeId_typesFolder, &reftypes_aggregates)) {
807
        /* hasModellingRule-reference is required (configured or node in an
808
         * instance declaration) */
809
397k
        UA_ReferenceTypeSet_add(&reftypes_skipped,
810
397k
                                UA_REFERENCETYPEINDEX_HASMODELLINGRULE);
811
397k
    }
812
397k
    UA_Node_deleteReferencesSubset(node, &reftypes_skipped);
813
814
    /* Add the node to the nodestore */
815
397k
    UA_NodeId newNodeId = UA_NODEID_NULL;
816
397k
    res = UA_NODESTORE_INSERT(server, node, &newNodeId);
817
    /* node = NULL; The pointer is no longer valid */
818
397k
    if(res != UA_STATUSCODE_GOOD)
819
0
        return res;
820
821
    /* Add the node references */
822
397k
    res = addNode_addRefs(server, session, &newNodeId, destinationNodeId,
823
397k
                          &rd->referenceTypeId, &rd->typeDefinition.nodeId);
824
397k
    if(res != UA_STATUSCODE_GOOD)
825
10
        goto errout;
826
827
397k
    if(rd->nodeClass == UA_NODECLASS_VARIABLE) {
828
345k
        res = checkSetIsDynamicVariable(server, session, &newNodeId);
829
345k
        if(res != UA_STATUSCODE_GOOD)
830
0
            goto errout;
831
345k
    }
832
833
    /* For the new child, recursively copy the members of the original. No
834
     * typechecking is performed here. Assuming that the original is
835
     * consistent. */
836
397k
    res = copyAllChildren(server, session, &rd->nodeId.nodeId, &newNodeId);
837
397k
    if(res != UA_STATUSCODE_GOOD)
838
0
        goto errout;
839
840
    /* Check if its a dynamic variable, add all type and/or interface
841
     * children and call the constructor */
842
397k
    res = addNode_finish(server, session, &newNodeId);
843
397k
    if(res != UA_STATUSCODE_GOOD)
844
0
        goto errout;
845
846
397k
 errout:
847
    /* Clean up */
848
397k
    if(res != UA_STATUSCODE_GOOD)
849
10
        deleteNode(server, newNodeId, true);
850
397k
    UA_NodeId_clear(&newNodeId);
851
397k
    return res;
852
397k
}
853
854
static UA_StatusCode
855
copyChild(UA_Server *server, UA_Session *session,
856
          const UA_NodeId *destinationNodeId,
857
3.59M
          const UA_ReferenceDescription *rd) {
858
3.59M
    UA_assert(session);
859
3.59M
    UA_LOCK_ASSERT(&server->serviceMutex);
860
861
    /* Is there an existing child with the browsename? */
862
3.59M
    UA_NodeId existingChild = UA_NODEID_NULL;
863
3.59M
    UA_NodeClass childNodeClass = (UA_NodeClass)
864
3.59M
        (UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD);
865
3.59M
    UA_StatusCode retval = findChildByBrowsename(server, session,
866
3.59M
                                                 *destinationNodeId, childNodeClass,
867
3.59M
                                                 UA_REFERENCETYPEINDEX_AGGREGATES,
868
3.59M
                                                 UA_NS0ID(AGGREGATES), &rd->browseName,
869
3.59M
                                                 &existingChild);
870
871
    /* Existing child with that browseName. Deep-copy missing members. */
872
3.59M
    if(retval == UA_STATUSCODE_GOOD) {
873
2.52M
        if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
874
2.48M
           rd->nodeClass == UA_NODECLASS_OBJECT) {
875
2.48M
            retval = beginChildInstantiation(server, session, destinationNodeId,
876
2.48M
                                             &rd->nodeId.nodeId);
877
2.48M
            if(retval == UA_STATUSCODE_GOOD) {
878
2.48M
                retval = copyAllChildren(server, session, &rd->nodeId.nodeId,
879
2.48M
                                         &existingChild);
880
2.48M
                endChildInstantiation(server);
881
2.48M
            }
882
2.48M
        }
883
2.52M
        UA_NodeId_clear(&existingChild);
884
2.52M
        return retval;
885
2.52M
    }
886
887
    /* An error occurred (besides not finding an existing child) */
888
1.07M
    if(retval != UA_STATUSCODE_BADNOTFOUND)
889
0
        return retval;
890
891
    /* Is the child mandatory? If not, ask callback whether child should be instantiated.
892
     * If not, skip. */
893
1.07M
    if(!isMandatoryChild(server, session, &rd->nodeId.nodeId)) {
894
676k
        if(!server->config.nodeLifecycle ||
895
0
           !server->config.nodeLifecycle->createOptionalChild)
896
676k
            return UA_STATUSCODE_GOOD;
897
0
        UA_Boolean createChild = server->config.nodeLifecycle->
898
0
            createOptionalChild(server, &session->sessionId, session->context,
899
0
                                &rd->nodeId.nodeId, destinationNodeId, &rd->referenceTypeId);
900
0
        if(!createChild)
901
0
            return UA_STATUSCODE_GOOD;
902
0
    }
903
904
    /* Child is a method -> create a reference */
905
397k
    if(rd->nodeClass == UA_NODECLASS_METHOD) {
906
0
        UA_AddReferencesItem newItem;
907
0
        UA_AddReferencesItem_init(&newItem);
908
0
        newItem.sourceNodeId = *destinationNodeId;
909
0
        newItem.referenceTypeId = rd->referenceTypeId;
910
0
        newItem.isForward = true;
911
0
        newItem.targetNodeId = rd->nodeId;
912
0
        newItem.targetNodeClass = UA_NODECLASS_METHOD;
913
0
        Operation_addReference(server, session, NULL, &newItem, &retval);
914
0
        return retval;
915
0
    }
916
917
    /* Child is a variable or object */
918
397k
    if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
919
397k
       rd->nodeClass == UA_NODECLASS_OBJECT) {
920
397k
        retval = beginChildInstantiation(server, session, destinationNodeId,
921
397k
                                         &rd->nodeId.nodeId);
922
397k
        if(retval != UA_STATUSCODE_GOOD)
923
0
            return retval;
924
397k
        retval = copyObjectVariableChild(server, session, destinationNodeId, rd);
925
397k
        endChildInstantiation(server);
926
397k
    }
927
928
397k
    return retval;
929
397k
}
930
931
/* Copy any children of Node sourceNodeId to another node destinationNodeId. */
932
static UA_StatusCode
933
copyAllChildren(UA_Server *server, UA_Session *session,
934
36.3M
                const UA_NodeId *source, const UA_NodeId *destination) {
935
    /* Browse to get all children of the source */
936
36.3M
    UA_BrowseDescription bd;
937
36.3M
    UA_BrowseDescription_init(&bd);
938
36.3M
    bd.nodeId = *source;
939
36.3M
    bd.referenceTypeId = UA_NS0ID(AGGREGATES);
940
36.3M
    bd.includeSubtypes = true;
941
36.3M
    bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
942
36.3M
    bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
943
36.3M
    bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS |
944
36.3M
        UA_BROWSERESULTMASK_BROWSENAME | UA_BROWSERESULTMASK_TYPEDEFINITION;
945
946
36.3M
    UA_BrowseResult br;
947
36.3M
    UA_BrowseResult_init(&br);
948
36.3M
    UA_UInt32 maxrefs = 0;
949
36.3M
    Operation_Browse(server, session, &maxrefs, &bd, &br);
950
36.3M
    if(br.statusCode != UA_STATUSCODE_GOOD)
951
0
        return br.statusCode;
952
953
36.3M
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
954
46.8M
    for(size_t i = 0; i < br.referencesSize; ++i) {
955
10.4M
        UA_ReferenceDescription *rd = &br.references[i];
956
10.4M
        retval = copyChild(server, session, destination, rd);
957
10.4M
        if(retval != UA_STATUSCODE_GOOD)
958
1.05k
            break;
959
10.4M
    }
960
961
36.3M
    UA_BrowseResult_clear(&br);
962
36.3M
    return retval;
963
36.3M
}
964
965
static UA_StatusCode
966
addTypeChildren(UA_Server *server, UA_Session *session,
967
13.6M
                const UA_NodeId *nodeId, const UA_NodeId *typeId) {
968
    /* Get the hierarchy of the type and all its supertypes */
969
13.6M
    UA_NodeId *hierarchy = NULL;
970
13.6M
    size_t hierarchySize = 0;
971
13.6M
    UA_StatusCode retval =
972
13.6M
        getTypeAndInterfaceHierarchy(server, typeId, true,
973
13.6M
                                     &hierarchy, &hierarchySize);
974
13.6M
    if(retval != UA_STATUSCODE_GOOD)
975
0
        return retval;
976
977
    /* Copy members of the type and supertypes (and instantiate them) */
978
41.7M
    for(size_t i = 0; i < hierarchySize; ++i) {
979
28.0M
        retval = copyAllChildren(server, session, &hierarchy[i], nodeId);
980
28.0M
        if(retval != UA_STATUSCODE_GOOD)
981
1.05k
            break;
982
28.0M
    }
983
984
13.6M
    UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]);
985
13.6M
    return retval;
986
13.6M
}
987
988
/************/
989
/* Add Node */
990
/************/
991
992
static const UA_NodeId hasSubtype = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}};
993
994
UA_StatusCode
995
addNode_addRefs(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
996
                const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
997
17.8M
                const UA_NodeId *typeDefinitionId) {
998
    /* Get the node */
999
17.8M
    const UA_Node *type = NULL;
1000
17.8M
    const UA_Node *node = UA_NODESTORE_GET(server, nodeId);
1001
17.8M
    if(!node)
1002
0
        return UA_STATUSCODE_BADNODEIDUNKNOWN;
1003
1004
    /* Use the typeDefinition as parent for type-nodes */
1005
17.8M
    const UA_NodeHead *head = &node->head;
1006
17.8M
    if(head->nodeClass == UA_NODECLASS_VARIABLETYPE ||
1007
17.3M
       head->nodeClass == UA_NODECLASS_OBJECTTYPE ||
1008
16.2M
       head->nodeClass == UA_NODECLASS_REFERENCETYPE ||
1009
15.7M
       head->nodeClass == UA_NODECLASS_DATATYPE) {
1010
3.46M
        if(UA_NodeId_equal(referenceTypeId, &UA_NODEID_NULL))
1011
369k
            referenceTypeId = &hasSubtype;
1012
3.46M
        const UA_Node *parentNode = UA_NODESTORE_GET(server, parentNodeId);
1013
3.46M
        if(parentNode) {
1014
3.46M
            if(parentNode->head.nodeClass == head->nodeClass)
1015
3.46M
                typeDefinitionId = parentNodeId;
1016
3.46M
            UA_NODESTORE_RELEASE(server, parentNode);
1017
3.46M
        }
1018
3.46M
    }
1019
1020
17.8M
    UA_StatusCode retval;
1021
    /* Make sure newly created node does not have itself as parent */
1022
17.8M
    if(UA_NodeId_equal(nodeId, parentNodeId)) {
1023
0
        logAddNode(server->config.logging, session, nodeId,
1024
0
                   "A node cannot have itself as parent");
1025
0
        retval = UA_STATUSCODE_BADINVALIDARGUMENT;
1026
0
        goto cleanup;
1027
0
    }
1028
1029
1030
    /* Check parent reference. Objects may have no parent. */
1031
17.8M
    retval = checkParentReference(server, session, head, parentNodeId, referenceTypeId);
1032
17.8M
    if(retval != UA_STATUSCODE_GOOD) {
1033
300
        logAddNode(server->config.logging, session, nodeId,
1034
300
                   "The parent reference is invalid");
1035
300
        goto cleanup;
1036
300
    }
1037
1038
    /* Replace empty typeDefinition with the most permissive default */
1039
17.8M
    if((head->nodeClass == UA_NODECLASS_VARIABLE ||
1040
5.65M
        head->nodeClass == UA_NODECLASS_OBJECT) &&
1041
13.6M
       UA_NodeId_isNull(typeDefinitionId)) {
1042
39.9k
        logAddNode(server->config.logging, session, nodeId,
1043
39.9k
                   "No TypeDefinition. Use the default "
1044
39.9k
                   "TypeDefinition for the Variable/Object");
1045
39.9k
        if(head->nodeClass == UA_NODECLASS_VARIABLE)
1046
1.05k
            typeDefinitionId = &baseDataVariableType;
1047
38.8k
        else
1048
38.8k
            typeDefinitionId = &baseObjectType;
1049
39.9k
    }
1050
1051
    /* Get the node type. There must be a typedefinition for variables, objects
1052
     * and type-nodes. See the above checks. */
1053
17.8M
    if(!UA_NodeId_isNull(typeDefinitionId)) {
1054
        /* Get the type node */
1055
17.1M
        type = UA_NODESTORE_GET(server, typeDefinitionId);
1056
17.1M
        if(!type) {
1057
1.05k
            UA_LOG_INFO_SESSION(server->config.logging, session,
1058
1.05k
                                "AddNode (%N): Node type %N not found ",
1059
1.05k
                                *nodeId, *typeDefinitionId);
1060
1.05k
            retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
1061
1.05k
            goto cleanup;
1062
1.05k
        }
1063
1064
17.1M
        UA_Boolean typeOk = false;
1065
17.1M
        const UA_NodeHead *typeHead = &type->head;
1066
17.1M
        switch(head->nodeClass) {
1067
1.39M
            case UA_NODECLASS_DATATYPE:
1068
1.39M
                typeOk = typeHead->nodeClass == UA_NODECLASS_DATATYPE;
1069
1.39M
                break;
1070
0
            case UA_NODECLASS_METHOD:
1071
0
                typeOk = typeHead->nodeClass == UA_NODECLASS_METHOD;
1072
0
                break;
1073
1.46M
            case UA_NODECLASS_OBJECT:
1074
2.51M
            case UA_NODECLASS_OBJECTTYPE:
1075
2.51M
                typeOk = typeHead->nodeClass == UA_NODECLASS_OBJECTTYPE;
1076
2.51M
                break;
1077
531k
            case UA_NODECLASS_REFERENCETYPE:
1078
531k
                typeOk = typeHead->nodeClass == UA_NODECLASS_REFERENCETYPE;
1079
531k
                break;
1080
12.1M
            case UA_NODECLASS_VARIABLE:
1081
12.6M
            case UA_NODECLASS_VARIABLETYPE:
1082
12.6M
                typeOk = typeHead->nodeClass == UA_NODECLASS_VARIABLETYPE;
1083
12.6M
                break;
1084
0
            case UA_NODECLASS_VIEW:
1085
0
                typeOk = typeHead->nodeClass == UA_NODECLASS_VIEW;
1086
0
                break;
1087
0
            default:
1088
0
                typeOk = false;
1089
17.1M
        }
1090
17.1M
        if(!typeOk) {
1091
0
            logAddNode(server->config.logging, session, nodeId,
1092
0
                       "Type does not match the NodeClass");
1093
0
            retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
1094
0
            goto cleanup;
1095
0
        }
1096
1097
        /* See if the type has the correct node class. For type-nodes, we know
1098
         * that type has the same nodeClass from checkParentReference. */
1099
17.1M
        if(head->nodeClass == UA_NODECLASS_VARIABLE &&
1100
12.1M
           type->variableTypeNode.isAbstract) {
1101
            /* Get subtypes of the parent reference types */
1102
0
            UA_ReferenceTypeSet refTypes1, refTypes2;
1103
0
            retval |= referenceTypeIndices(server, &parentReferences[0], &refTypes1, true);
1104
0
            retval |= referenceTypeIndices(server, &parentReferences[1], &refTypes2, true);
1105
0
            UA_ReferenceTypeSet refTypes = UA_ReferenceTypeSet_union(refTypes1, refTypes2);
1106
0
            if(retval != UA_STATUSCODE_GOOD)
1107
0
                goto cleanup;
1108
1109
            /* Abstract variable is allowed if parent is a children of a
1110
             * base data variable. An abstract variable may be part of an
1111
             * object type which again is below BaseObjectType.
1112
             * When the parent is not set (e.g. generated namespace code where
1113
             * the parent reference is non-hierarchical), we skip the check. */
1114
0
            const UA_NodeId variableTypes = UA_NS0ID(BASEDATAVARIABLETYPE);
1115
0
            const UA_NodeId objectTypes = UA_NS0ID(BASEOBJECTTYPE);
1116
0
            if(!UA_NodeId_isNull(parentNodeId) &&
1117
0
               !isNodeInTree(server, parentNodeId, &variableTypes, &refTypes) &&
1118
0
               !isNodeInTree(server, parentNodeId, &objectTypes, &refTypes)) {
1119
0
                logAddNode(server->config.logging, session, nodeId,
1120
0
                           "Type of variable node must be a "
1121
0
                           "VariableType and cannot be abstract");
1122
0
                retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
1123
0
                goto cleanup;
1124
0
            }
1125
0
        }
1126
1127
17.1M
        if(head->nodeClass == UA_NODECLASS_OBJECT &&
1128
1.46M
           type->objectTypeNode.isAbstract) {
1129
            /* Get subtypes of the parent reference types */
1130
58.3k
            UA_ReferenceTypeSet refTypes1, refTypes2;
1131
58.3k
            retval |= referenceTypeIndices(server, &parentReferences[0], &refTypes1, true);
1132
58.3k
            retval |= referenceTypeIndices(server, &parentReferences[1], &refTypes2, true);
1133
58.3k
            UA_ReferenceTypeSet refTypes = UA_ReferenceTypeSet_union(refTypes1, refTypes2);
1134
58.3k
            if(retval != UA_STATUSCODE_GOOD)
1135
0
                goto cleanup;
1136
1137
1138
            /* Object node created of an abstract ObjectType. Only allowed if
1139
             * within BaseObjectType folder, or if it's an event (subType of
1140
             * BaseEventType), or if the parent is not set (e.g. generated
1141
             * namespace code where the parent reference is
1142
             * non-hierarchical). */
1143
58.3k
            const UA_NodeId objectTypes = UA_NS0ID(BASEOBJECTTYPE);
1144
58.3k
            UA_Boolean isInBaseObjectType =
1145
58.3k
                isNodeInTree(server, parentNodeId, &objectTypes, &refTypes);
1146
1147
58.3k
            if(!isInBaseObjectType && !UA_NodeId_isNull(parentNodeId)) {
1148
0
                logAddNode(server->config.logging, session, nodeId,
1149
0
                           "Type of ObjectNode must be ObjectType and not be abstract");
1150
0
                retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
1151
0
                goto cleanup;
1152
0
            }
1153
58.3k
        }
1154
17.1M
    }
1155
1156
    /* Add reference to the parent */
1157
17.8M
    if(!UA_NodeId_isNull(parentNodeId)) {
1158
17.5M
        if(UA_NodeId_isNull(referenceTypeId)) {
1159
0
            logAddNode(server->config.logging, session, nodeId,
1160
0
                       "Reference to parent cannot be null");
1161
0
            retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
1162
0
            goto cleanup;
1163
0
        }
1164
1165
17.5M
        retval = addRefWithSession(server, session, &head->nodeId, referenceTypeId,
1166
17.5M
                                   parentNodeId, false);
1167
17.5M
        if(retval != UA_STATUSCODE_GOOD) {
1168
0
            logAddNode(server->config.logging, session, nodeId,
1169
0
                       "Adding reference to parent failed");
1170
0
            goto cleanup;
1171
0
        }
1172
17.5M
    }
1173
1174
    /* Add a hasTypeDefinition reference */
1175
17.8M
    if(head->nodeClass == UA_NODECLASS_VARIABLE ||
1176
13.6M
       head->nodeClass == UA_NODECLASS_OBJECT) {
1177
13.6M
        UA_assert(type != NULL); /* see above */
1178
13.6M
        retval = addRefWithSession(server, session, &head->nodeId, &hasTypeDefinition,
1179
13.6M
                                   &type->head.nodeId, true);
1180
13.6M
        if(retval != UA_STATUSCODE_GOOD) {
1181
1
            logAddNode(server->config.logging, session, nodeId,
1182
1
                       "Adding a reference to the type definition failed");
1183
1
        }
1184
13.6M
    }
1185
1186
17.8M
 cleanup:
1187
17.8M
    UA_NODESTORE_RELEASE(server, node);
1188
17.8M
    if(type)
1189
17.1M
        UA_NODESTORE_RELEASE(server, type);
1190
17.8M
    return retval;
1191
17.8M
}
1192
1193
/* Create the node and add it to the nodestore. But don't typecheck and add
1194
 * references so far */
1195
UA_StatusCode
1196
addNode_raw(UA_Server *server, UA_Session *session, void *nodeContext,
1197
16.9M
            const UA_AddNodesItem *item, UA_NodeId *outNewNodeId) {
1198
16.9M
    UA_LOCK_ASSERT(&server->serviceMutex);
1199
1200
    /* Do not check access for server */
1201
16.9M
    if(session != &server->adminSession && server->config.accessControl.allowAddNode) {
1202
24
        if(!server->config.accessControl.
1203
24
           allowAddNode(server, &server->config.accessControl,
1204
24
                        &session->sessionId, session->context, item)) {
1205
0
            return UA_STATUSCODE_BADUSERACCESSDENIED;
1206
0
        }
1207
24
    }
1208
1209
    /* Check the NamespaceIndex */
1210
16.9M
    if(item->requestedNewNodeId.nodeId.namespaceIndex >= server->namespacesSize) {
1211
1
        UA_LOG_INFO_SESSION(server->config.logging, session,
1212
1
                            "AddNode (%N): Namespace invalid",
1213
1
                            item->requestedNewNodeId.nodeId);
1214
1
        return UA_STATUSCODE_BADNODEIDINVALID;
1215
1
    }
1216
1217
16.9M
    if(item->nodeAttributes.encoding != UA_EXTENSIONOBJECT_DECODED &&
1218
16.9M
       item->nodeAttributes.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) {
1219
23
        UA_LOG_INFO_SESSION(server->config.logging, session,
1220
23
                            "AddNode (%N): Node attributes invalid",
1221
23
                            item->requestedNewNodeId.nodeId);
1222
23
        return UA_STATUSCODE_BADINTERNALERROR;
1223
23
    }
1224
1225
    /* Create a Node */
1226
16.9M
    UA_Node *node = UA_NODESTORE_NEW(server, item->nodeClass);
1227
16.9M
    if(!node) {
1228
0
        UA_LOG_INFO_SESSION(server->config.logging, session,
1229
0
                            "AddNode: Could not create a node "
1230
0
                            "in the nodestore");
1231
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1232
0
    }
1233
1234
16.9M
    UA_NodeId tmpOutId = UA_NODEID_NULL;
1235
1236
    /* Fill the node attributes. If an error occurs, the memory in the node is
1237
     * cleaned up inside UA_NODESTORE_DELETE. */
1238
16.9M
    node->head.context = nodeContext;
1239
16.9M
    UA_StatusCode retval =
1240
16.9M
        UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->head.nodeId);
1241
16.9M
    if(retval != UA_STATUSCODE_GOOD)
1242
0
        goto create_error;
1243
1244
16.9M
    retval = UA_QualifiedName_copy(&item->browseName, &node->head.browseName);
1245
16.9M
    if(retval != UA_STATUSCODE_GOOD)
1246
0
        goto create_error;
1247
1248
16.9M
    retval = UA_Node_setAttributes(node, item->nodeAttributes.content.decoded.data,
1249
16.9M
                                   item->nodeAttributes.content.decoded.type);
1250
16.9M
    if(retval != UA_STATUSCODE_GOOD)
1251
0
        goto create_error;
1252
1253
    /* Create a current source timestamp for values that don't have any */
1254
16.9M
    if(node->head.nodeClass == UA_NODECLASS_VARIABLE) {
1255
11.3M
        UA_VariableNode *vn = &node->variableNode;
1256
11.3M
        if(vn->valueSourceType == UA_VALUESOURCETYPE_INTERNAL &&
1257
11.3M
           !vn->valueSource.internal.value.hasSourceTimestamp) {
1258
11.3M
            UA_EventLoop *el = server->config.eventLoop;
1259
11.3M
            vn->valueSource.internal.value.sourceTimestamp = el->dateTime_now(el);
1260
11.3M
            vn->valueSource.internal.value.hasSourceTimestamp = true;
1261
11.3M
        }
1262
11.3M
    }
1263
1264
    /* Add the node to the Nodestore */
1265
16.9M
    if(!outNewNodeId)
1266
116k
        outNewNodeId = &tmpOutId;
1267
16.9M
    retval = UA_NODESTORE_INSERT(server, node, outNewNodeId);
1268
16.9M
    if(retval != UA_STATUSCODE_GOOD) {
1269
0
        UA_LOG_INFO_SESSION(server->config.logging, session,
1270
0
                            "AddNode (%N): Could not add the new node "
1271
0
                            "to the Nodestore with status %s",
1272
0
                            item->requestedNewNodeId.nodeId,
1273
0
                            UA_StatusCode_name(retval));
1274
0
        return retval;
1275
0
    }
1276
1277
16.9M
    if(outNewNodeId == &tmpOutId)
1278
116k
        UA_NodeId_clear(&tmpOutId);
1279
1280
16.9M
    return UA_STATUSCODE_GOOD;
1281
1282
0
create_error:
1283
0
    UA_LOG_INFO_SESSION(server->config.logging, session,
1284
0
                        "AddNode (%N): Could not create node "
1285
0
                        "with status %s", node->head.nodeId,
1286
0
                        UA_StatusCode_name(retval));
1287
0
    UA_NODESTORE_DELETE(server, node);
1288
0
    return retval;
1289
16.9M
}
1290
1291
static UA_StatusCode
1292
findDefaultInstanceBrowseNameNode(UA_Server *server, UA_NodeId startingNode,
1293
8
                                  UA_NodeId *foundId) {
1294
8
    UA_NodeId_init(foundId);
1295
8
    UA_RelativePathElement rpe;
1296
8
    UA_RelativePathElement_init(&rpe);
1297
8
    rpe.referenceTypeId = UA_NS0ID(HASPROPERTY);
1298
8
    rpe.targetName = UA_QUALIFIEDNAME(0, "DefaultInstanceBrowseName");
1299
8
    UA_BrowsePath bp;
1300
8
    UA_BrowsePath_init(&bp);
1301
8
    bp.startingNode = startingNode;
1302
8
    bp.relativePath.elementsSize = 1;
1303
8
    bp.relativePath.elements = &rpe;
1304
8
    UA_BrowsePathResult bpr = translateBrowsePathToNodeIds(server, &bp);
1305
8
    UA_StatusCode retval = bpr.statusCode;
1306
8
    if(retval == UA_STATUSCODE_GOOD && bpr.targetsSize > 0)
1307
0
        retval = UA_NodeId_copy(&bpr.targets[0].targetId.nodeId, foundId);
1308
8
    UA_BrowsePathResult_clear(&bpr);
1309
8
    return retval;
1310
8
}
1311
1312
/* Check if we got a valid browse name for the new node. For object nodes the
1313
 * BrowseName may only be null if the parent type has a
1314
 * 'DefaultInstanceBrowseName' property. */
1315
static UA_StatusCode
1316
16.8M
checkSetBrowseName(UA_Server *server, UA_Session *session, UA_AddNodesItem *item) {
1317
    /* If the object node already has a browse name we are done here. */
1318
16.8M
    if(!UA_QualifiedName_isNull(&item->browseName))
1319
16.8M
        return UA_STATUSCODE_GOOD;
1320
1321
    /* Nodes other than Objects must have a BrowseName */
1322
35
    if(item->nodeClass != UA_NODECLASS_OBJECT)
1323
27
        return UA_STATUSCODE_BADBROWSENAMEINVALID;
1324
1325
    /* At this point we have an object with an empty browse name. Check the type
1326
     * node if it has a DefaultInstanceBrowseName property. */
1327
8
    UA_NodeId defaultBrowseNameNode;
1328
8
    UA_StatusCode retval =
1329
8
        findDefaultInstanceBrowseNameNode(server, item->typeDefinition.nodeId,
1330
8
                                          &defaultBrowseNameNode);
1331
8
    if(retval != UA_STATUSCODE_GOOD)
1332
8
        return UA_STATUSCODE_BADBROWSENAMEINVALID;
1333
1334
0
    UA_Variant defaultBrowseName;
1335
0
    retval = readWithReadValue(server, &defaultBrowseNameNode,
1336
0
                               UA_ATTRIBUTEID_VALUE, &defaultBrowseName);
1337
0
    UA_NodeId_clear(&defaultBrowseNameNode);
1338
0
    if(retval != UA_STATUSCODE_GOOD)
1339
0
        return UA_STATUSCODE_BADBROWSENAMEINVALID;
1340
1341
0
    if(UA_Variant_hasScalarType(&defaultBrowseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME])) {
1342
0
        item->browseName = *(UA_QualifiedName*)defaultBrowseName.data;
1343
0
        UA_QualifiedName_init((UA_QualifiedName*)defaultBrowseName.data);
1344
0
    } else {
1345
0
        retval = UA_STATUSCODE_BADBROWSENAMEINVALID;
1346
0
    }
1347
1348
0
    UA_Variant_clear(&defaultBrowseName);
1349
0
    return retval;
1350
0
}
1351
1352
/* Prepare the node, then add it to the nodestore */
1353
static UA_StatusCode
1354
Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContext,
1355
                        const UA_AddNodesItem *item, const UA_NodeId *parentNodeId,
1356
5.58M
                        const UA_NodeId *referenceTypeId, UA_NodeId *outNewNodeId) {
1357
    /* Create a temporary NodeId if none is returned */
1358
5.58M
    UA_NodeId newId;
1359
5.58M
    if(!outNewNodeId) {
1360
5.39M
        UA_NodeId_init(&newId);
1361
5.39M
        outNewNodeId = &newId;
1362
5.39M
    }
1363
1364
    /* Set the BrowsenName before adding to the Nodestore. The BrowseName is
1365
     * immutable afterwards. */
1366
5.58M
    UA_Boolean noBrowseName = UA_QualifiedName_isNull(&item->browseName);
1367
5.58M
    UA_StatusCode retval =
1368
5.58M
        checkSetBrowseName(server, session, (UA_AddNodesItem*)(uintptr_t)item);
1369
5.58M
    if(retval != UA_STATUSCODE_GOOD)
1370
35
        return retval;
1371
1372
    /* Create the node and add it to the nodestore */
1373
5.57M
    retval = addNode_raw(server, session, nodeContext, item, outNewNodeId);
1374
5.57M
    if(retval != UA_STATUSCODE_GOOD)
1375
24
        goto cleanup;
1376
1377
    /* Typecheck and add references to parent and type definition */
1378
5.57M
    retval = addNode_addRefs(server, session, outNewNodeId, parentNodeId,
1379
5.57M
                             referenceTypeId, &item->typeDefinition.nodeId);
1380
5.57M
    if(retval != UA_STATUSCODE_GOOD)
1381
43
        deleteNode(server, *outNewNodeId, true);
1382
1383
5.57M
    if(outNewNodeId == &newId)
1384
5.39M
        UA_NodeId_clear(&newId);
1385
1386
5.57M
 cleanup:
1387
5.57M
    if(noBrowseName)
1388
0
        UA_QualifiedName_clear((UA_QualifiedName*)(uintptr_t)&item->browseName);
1389
5.57M
    return retval;
1390
5.57M
}
1391
1392
/* Construct children first */
1393
static UA_StatusCode
1394
recursiveCallConstructors(UA_Server *server, UA_Session *session,
1395
17.9M
                          const UA_NodeId *nodeId, const UA_Node *type) {
1396
17.9M
    UA_LOCK_ASSERT(&server->serviceMutex);
1397
1398
    /* Browse the children */
1399
17.9M
    UA_BrowseDescription bd;
1400
17.9M
    UA_BrowseDescription_init(&bd);
1401
17.9M
    bd.nodeId = *nodeId;
1402
17.9M
    bd.referenceTypeId = UA_NS0ID(AGGREGATES);
1403
17.9M
    bd.includeSubtypes = true;
1404
17.9M
    bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
1405
1406
17.9M
    UA_BrowseResult br;
1407
17.9M
    UA_BrowseResult_init(&br);
1408
17.9M
    UA_UInt32 maxrefs = 0;
1409
17.9M
    Operation_Browse(server, session, &maxrefs, &bd, &br);
1410
17.9M
    if(br.statusCode != UA_STATUSCODE_GOOD)
1411
0
        return br.statusCode;
1412
1413
    /* Call the constructor for every unconstructed child node */
1414
17.9M
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1415
31.6M
    for(size_t i = 0; i < br.referencesSize; ++i) {
1416
13.6M
        UA_ReferenceDescription *rd = &br.references[i];
1417
13.6M
        if(!UA_ExpandedNodeId_isLocal(&rd->nodeId))
1418
0
            continue;
1419
13.6M
        const UA_Node *target = UA_NODESTORE_GET(server, &rd->nodeId.nodeId);
1420
13.6M
        if(!target)
1421
0
            continue;
1422
13.6M
        if(target->head.constructed) {
1423
13.6M
            UA_NODESTORE_RELEASE(server, target);
1424
13.6M
            continue;
1425
13.6M
        }
1426
1427
        /* TODO: Do we need all attributes and references here?  */
1428
0
        const UA_Node *targetType = NULL;
1429
0
        if(target->head.nodeClass == UA_NODECLASS_VARIABLE ||
1430
0
           target->head.nodeClass == UA_NODECLASS_OBJECT) {
1431
0
            targetType = getNodeType(server, &target->head, ~(UA_UInt32)0,
1432
0
                                     UA_REFERENCETYPESET_ALL,
1433
0
                                     UA_BROWSEDIRECTION_BOTH);
1434
0
            if(!targetType) {
1435
0
                UA_NODESTORE_RELEASE(server, target);
1436
0
                retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
1437
0
                break;
1438
0
            }
1439
0
        }
1440
1441
0
        UA_NODESTORE_RELEASE(server, target);
1442
0
        retval = recursiveCallConstructors(server, session, &rd->nodeId.nodeId, targetType);
1443
1444
0
        if(targetType)
1445
0
            UA_NODESTORE_RELEASE(server, targetType);
1446
0
        if(retval != UA_STATUSCODE_GOOD)
1447
0
            break;
1448
0
    }
1449
1450
17.9M
    UA_BrowseResult_clear(&br);
1451
1452
    /* If a child could not be constructed or the node is already constructed */
1453
17.9M
    if(retval != UA_STATUSCODE_GOOD)
1454
0
        return retval;
1455
1456
    /* Get the node context */
1457
17.9M
    const UA_Node *node = UA_NODESTORE_GET(server, nodeId);
1458
17.9M
    if(!node)
1459
0
        return UA_STATUSCODE_BADNODEIDUNKNOWN;
1460
17.9M
    void *context = node->head.context;
1461
17.9M
    UA_NodeClass nodeClass = node->head.nodeClass;
1462
17.9M
    UA_NODESTORE_RELEASE(server, node);
1463
1464
    /* Call the global constructor */
1465
17.9M
    if(server->config.nodeLifecycle &&
1466
0
       server->config.nodeLifecycle->constructor) {
1467
0
        retval = server->config.nodeLifecycle->
1468
0
            constructor(server, &session->sessionId,
1469
0
                        session->context, nodeId, &context);
1470
0
        if(retval != UA_STATUSCODE_GOOD)
1471
0
            return retval;
1472
0
    }
1473
1474
    /* Call the local (per-type) constructor */
1475
17.9M
    const UA_NodeTypeLifecycle *lifecycle = NULL;
1476
17.9M
    if(type && nodeClass == UA_NODECLASS_OBJECT)
1477
1.46M
        lifecycle = &type->objectTypeNode.lifecycle;
1478
16.4M
    else if(type && nodeClass == UA_NODECLASS_VARIABLE)
1479
12.1M
        lifecycle = &type->variableTypeNode.lifecycle;
1480
17.9M
    if(lifecycle && lifecycle->constructor) {
1481
0
        retval = lifecycle->constructor(server, &session->sessionId,
1482
0
                                        session->context, &type->head.nodeId,
1483
0
                                        type->head.context, nodeId, &context);
1484
0
        if(retval != UA_STATUSCODE_GOOD)
1485
0
            goto global_destructor;
1486
0
    }
1487
1488
    /* Set the context *and* mark the node as constructed */
1489
17.9M
    retval = editNode(server, &server->adminSession, nodeId,
1490
17.9M
                      0, UA_REFERENCETYPESET_NONE, UA_BROWSEDIRECTION_INVALID,
1491
17.9M
                      (UA_EditNodeCallback)setConstructedNodeContext, context);
1492
17.9M
    if(retval != UA_STATUSCODE_GOOD)
1493
0
        goto local_destructor;
1494
1495
    /* All good, return */
1496
17.9M
    return retval;
1497
1498
    /* Fail. Call the destructors. */
1499
0
  local_destructor:
1500
0
    if(lifecycle && lifecycle->destructor) {
1501
0
        lifecycle->destructor(server, &session->sessionId, session->context,
1502
0
                              &type->head.nodeId, type->head.context, nodeId, &context);
1503
0
    }
1504
1505
0
  global_destructor:
1506
0
    if(server->config.nodeLifecycle && server->config.nodeLifecycle->destructor) {
1507
0
        server->config.nodeLifecycle->destructor(server, &session->sessionId,
1508
0
                                                 session->context, nodeId, context);
1509
0
    }
1510
0
    return retval;
1511
0
}
1512
1513
/* Add new ReferenceType to the subtypes bitfield */
1514
static UA_StatusCode
1515
addReferenceTypeSubtype(UA_Server *server, UA_Session *session,
1516
1.62M
                        UA_Node *node, void *context) {
1517
1.62M
    node->referenceTypeNode.subTypes =
1518
1.62M
        UA_ReferenceTypeSet_union(node->referenceTypeNode.subTypes,
1519
1.62M
                                  *(UA_ReferenceTypeSet*)context);
1520
1.62M
    return UA_STATUSCODE_GOOD;
1521
1.62M
}
1522
1523
static UA_StatusCode
1524
589k
setReferenceTypeSubtypes(UA_Server *server, const UA_ReferenceTypeNode *node) {
1525
    /* Get the ReferenceTypes upwards in the hierarchy */
1526
589k
    size_t parentsSize = 0;
1527
589k
    UA_ExpandedNodeId *parents = NULL;
1528
589k
    UA_ReferenceTypeSet reftypes_subtype = UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE);
1529
589k
    UA_StatusCode res =
1530
589k
        browseRecursive(server, 1, &node->head.nodeId, UA_BROWSEDIRECTION_INVERSE,
1531
589k
                        &reftypes_subtype, UA_NODECLASS_UNSPECIFIED,
1532
589k
                        false, &parentsSize, &parents);
1533
589k
    if(res != UA_STATUSCODE_GOOD)
1534
0
        return res;
1535
1536
    /* Add the ReferenceTypeIndex of this node */
1537
589k
    const UA_ReferenceTypeSet *newRefSet = &node->subTypes;
1538
2.20M
    for(size_t i = 0; i < parentsSize; i++) {
1539
1.62M
        editNode(server, &server->adminSession, &parents[i].nodeId,
1540
1.62M
                 0, UA_REFERENCETYPESET_NONE, UA_BROWSEDIRECTION_INVALID,
1541
1.62M
                 addReferenceTypeSubtype, (void*)(uintptr_t)newRefSet);
1542
1.62M
    }
1543
1544
589k
    UA_Array_delete(parents, parentsSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
1545
589k
    return UA_STATUSCODE_GOOD;
1546
589k
}
1547
1548
static UA_StatusCode
1549
setVariableNodeDynamic(UA_Server *server, const UA_NodeId *nodeId,
1550
97.2k
                       UA_Boolean isDynamic) {
1551
97.2k
    UA_Node *node =
1552
97.2k
        UA_NODESTORE_GET_EDIT_SELECTIVE(server, nodeId, 0, UA_REFERENCETYPESET_NONE,
1553
97.2k
                                        UA_BROWSEDIRECTION_INVALID);
1554
97.2k
    if(!node)
1555
0
        return UA_STATUSCODE_BADNODEIDINVALID;
1556
97.2k
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1557
97.2k
    if(node->head.nodeClass == UA_NODECLASS_VARIABLE)
1558
97.2k
        ((UA_VariableNode*)node)->isDynamic = isDynamic;
1559
0
    else
1560
0
        res = UA_STATUSCODE_BADINTERNALERROR;
1561
97.2k
    UA_NODESTORE_RELEASE(server, node);
1562
97.2k
    return res;
1563
97.2k
}
1564
1565
static UA_StatusCode
1566
checkSetIsDynamicVariable(UA_Server *server, UA_Session *session,
1567
13.0M
                          const UA_NodeId *nodeId) {
1568
    /* Get all hierarchical reference types */
1569
13.0M
    UA_ReferenceTypeSet reftypes_hierarchical;
1570
13.0M
    UA_ReferenceTypeSet_init(&reftypes_hierarchical);
1571
13.0M
    UA_NodeId hierarchicalRefs = UA_NS0ID(HIERARCHICALREFERENCES);
1572
13.0M
    UA_StatusCode res =
1573
13.0M
        referenceTypeIndices(server, &hierarchicalRefs, &reftypes_hierarchical, true);
1574
13.0M
    if(res != UA_STATUSCODE_GOOD)
1575
0
        return res;
1576
1577
    /* Is the variable under the server object? */
1578
13.0M
    UA_NodeId serverNodeId = UA_NS0ID(SERVER);
1579
13.0M
    if(isNodeInTree(server, nodeId, &serverNodeId, &reftypes_hierarchical))
1580
2.47M
        return UA_STATUSCODE_GOOD;
1581
1582
    /* Is the variable in the type hierarchy? */
1583
10.5M
    UA_NodeId typesNodeId = UA_NS0ID(TYPESFOLDER);
1584
10.5M
    if(isNodeInTree(server, nodeId, &typesNodeId, &reftypes_hierarchical))
1585
10.4M
        return UA_STATUSCODE_GOOD;
1586
1587
    /* Is the variable a property of a method node (InputArguments /
1588
     * OutputArguments)? */
1589
97.2k
    UA_BrowseDescription bd;
1590
97.2k
    UA_BrowseDescription_init(&bd);
1591
97.2k
    bd.nodeId = *nodeId;
1592
97.2k
    bd.browseDirection = UA_BROWSEDIRECTION_INVERSE;
1593
97.2k
    bd.referenceTypeId = UA_NS0ID(HASPROPERTY);
1594
97.2k
    bd.includeSubtypes = false;
1595
97.2k
    bd.nodeClassMask = UA_NODECLASS_METHOD;
1596
97.2k
    UA_BrowseResult br;
1597
97.2k
    UA_BrowseResult_init(&br);
1598
97.2k
    UA_UInt32 maxrefs = 0;
1599
97.2k
    Operation_Browse(server, session, &maxrefs, &bd, &br);
1600
97.2k
    UA_Boolean hasParentMethod = (br.referencesSize > 0);
1601
97.2k
    UA_BrowseResult_clear(&br);
1602
97.2k
    if(hasParentMethod)
1603
0
        return UA_STATUSCODE_GOOD;
1604
1605
    /* Set the variable to "dynamic" */
1606
97.2k
    return setVariableNodeDynamic(server, nodeId, true);
1607
97.2k
}
1608
1609
UA_StatusCode
1610
UA_Server_setVariableNodeDynamic(UA_Server *server, const UA_NodeId nodeId,
1611
0
                                 UA_Boolean isDynamic) {
1612
0
    lockServer(server);
1613
0
    UA_StatusCode res = setVariableNodeDynamic(server, &nodeId, isDynamic);
1614
0
    unlockServer(server);
1615
0
    return res;
1616
0
}
1617
1618
/* Children, references, type-checking, constructors. */
1619
UA_StatusCode
1620
17.9M
addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId) {
1621
    /* Get the node */
1622
17.9M
    const UA_Node *type = NULL;
1623
17.9M
    const UA_Node *node = UA_NODESTORE_GET(server, nodeId);
1624
17.9M
    if(!node)
1625
0
        return UA_STATUSCODE_BADNODEIDUNKNOWN;
1626
1627
    /* Set the ReferenceTypesSet of subtypes in the ReferenceTypeNode */
1628
17.9M
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
1629
17.9M
    if(node->head.nodeClass == UA_NODECLASS_REFERENCETYPE) {
1630
589k
        retval = setReferenceTypeSubtypes(server, &node->referenceTypeNode);
1631
589k
        if(retval != UA_STATUSCODE_GOOD)
1632
0
            goto cleanup;
1633
589k
    }
1634
1635
    /* Check NodeClass for 'hasSubtype'. UA_NODECLASS_VARIABLE not allowed
1636
     * to have subtype */
1637
17.9M
    if(node->head.nodeClass == UA_NODECLASS_VARIABLE) {
1638
47.4M
        for(size_t i = 0; i < node->head.referencesSize; i++) {
1639
35.2M
            if(node->head.references[i].referenceTypeIndex ==
1640
35.2M
               UA_REFERENCETYPEINDEX_HASSUBTYPE) {
1641
0
                UA_LOG_INFO_SESSION(server->config.logging, session,
1642
0
                                    "AddNode (%N): Variable not allowed "
1643
0
                                    "to have HasSubType reference",
1644
0
                                    node->head.nodeId);
1645
0
                retval = UA_STATUSCODE_BADREFERENCENOTALLOWED;
1646
0
                goto cleanup;
1647
0
            }
1648
35.2M
        }
1649
12.1M
    }
1650
1651
    /* Set variables to dynamic (source and server timestamps are meaningful) if
1652
     * they fulfill some conditions. (Do this before replacing the default value
1653
     * in a VariableNode. So that the sourceTimestamp is set in the Write service.) */
1654
17.9M
    if(node->head.nodeClass == UA_NODECLASS_VARIABLE) {
1655
12.1M
        retval = checkSetIsDynamicVariable(server, session, nodeId);
1656
12.1M
        if(retval != UA_STATUSCODE_GOOD)
1657
0
            goto cleanup;
1658
12.1M
    }
1659
1660
    /* Get the type node
1661
     * TODO: Do we need all attributes and references here?  */
1662
17.9M
    if(node->head.nodeClass == UA_NODECLASS_VARIABLE ||
1663
5.77M
       node->head.nodeClass == UA_NODECLASS_VARIABLETYPE ||
1664
14.1M
       node->head.nodeClass == UA_NODECLASS_OBJECT) {
1665
14.1M
        type = getNodeType(server, &node->head, ~(UA_UInt32)0,
1666
14.1M
                           UA_REFERENCETYPESET_ALL, UA_BROWSEDIRECTION_BOTH);
1667
14.1M
        if(!type) {
1668
19.4k
            if(server->bootstrapNS0)
1669
19.4k
                goto constructor;
1670
0
            logAddNode(server->config.logging, session, &node->head.nodeId,
1671
0
                       "Node type not found");
1672
0
            retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
1673
0
            goto cleanup;
1674
19.4k
        }
1675
14.1M
    }
1676
1677
    /* Initialize and typecheck the variable */
1678
17.9M
    if(node->head.nodeClass == UA_NODECLASS_VARIABLE ||
1679
12.6M
       node->head.nodeClass == UA_NODECLASS_VARIABLETYPE) {
1680
        /* Use attributes from the type. The value and value constraints are the
1681
         * same for the variable and variabletype attribute structs. */
1682
12.6M
        retval = useVariableTypeAttributes(server, session,
1683
12.6M
                                           &node->variableNode,
1684
12.6M
                                           &type->variableTypeNode);
1685
12.6M
        if(retval != UA_STATUSCODE_GOOD) {
1686
0
            UA_LOG_INFO_SESSION(server->config.logging, session,
1687
0
                                "AddNode (%N): Using attributes for from "
1688
0
                                "the variable type failed with error code %s",
1689
0
                                node->head.nodeId, UA_StatusCode_name(retval));
1690
0
            goto cleanup;
1691
0
        }
1692
1693
        /* Get a new pointer to the node. It might have been updated in the
1694
         * background due to a 'Write' operation inside
1695
         * useVariableTypeAttributes. */
1696
12.6M
        UA_NODESTORE_RELEASE(server, node);
1697
12.6M
        node = UA_NODESTORE_GET(server, nodeId);
1698
12.6M
        if(!node || (node->head.nodeClass != UA_NODECLASS_VARIABLE &&
1699
486k
                     node->head.nodeClass != UA_NODECLASS_VARIABLETYPE)) {
1700
0
            retval = UA_STATUSCODE_BADINTERNALERROR;
1701
0
            goto cleanup;
1702
0
        }
1703
1704
        /* Check if all attributes hold the constraints of the type now. The initial
1705
         * attributes must type-check. The constructor might change the attributes
1706
         * again. Then, the changes are type-checked by the normal write service. */
1707
12.6M
        retval = typeCheckVariableNode(server, session, &node->variableNode,
1708
12.6M
                                       &type->variableTypeNode);
1709
12.6M
        if(retval != UA_STATUSCODE_GOOD) {
1710
0
            UA_LOG_INFO_SESSION(server->config.logging, session,
1711
0
                                "AddNode (%N): Type-checking failed with error code %s",
1712
0
                                node->head.nodeId, UA_StatusCode_name(retval));
1713
0
            goto cleanup;
1714
0
        }
1715
12.6M
    }
1716
1717
    /* Add (mandatory) child nodes from the type definition */
1718
17.9M
    if(node->head.nodeClass == UA_NODECLASS_VARIABLE ||
1719
13.6M
       node->head.nodeClass == UA_NODECLASS_OBJECT) {
1720
13.6M
        retval = addTypeChildren(server, session, nodeId, &type->head.nodeId);
1721
13.6M
        if(retval != UA_STATUSCODE_GOOD) {
1722
1.05k
            UA_LOG_INFO_SESSION(server->config.logging, session,
1723
1.05k
                                "AddNode (%N): Adding child nodes "
1724
1.05k
                                "failed with error code %s",
1725
1.05k
                                node->head.nodeId, UA_StatusCode_name(retval));
1726
1.05k
            goto cleanup;
1727
1.05k
        }
1728
13.6M
    }
1729
1730
    /* Add (mandatory) child nodes from the HasInterface references */
1731
17.9M
    if(node->head.nodeClass == UA_NODECLASS_OBJECT) {
1732
1.46M
        retval = addInterfaceChildren(server, session, nodeId);
1733
1.46M
        if(retval != UA_STATUSCODE_GOOD) {
1734
0
            UA_LOG_INFO_SESSION(server->config.logging, session,
1735
0
                                "AddNode (%N): Adding child nodes "
1736
0
                                "interface failed with error code %s",
1737
0
                                node->head.nodeId, UA_StatusCode_name(retval));
1738
0
            goto cleanup;
1739
0
        }
1740
1.46M
    }
1741
1742
    /* Call the constructor(s) */
1743
17.9M
 constructor:
1744
17.9M
    if(!node->head.constructed)
1745
17.9M
        retval = recursiveCallConstructors(server, session, nodeId, type);
1746
17.9M
    if(retval != UA_STATUSCODE_GOOD) {
1747
0
        UA_LOG_INFO_SESSION(server->config.logging, session,
1748
0
                            "AddNode (%N): Calling the node constructor(s) "
1749
0
                            "failed with error code %s",
1750
0
                            node->head.nodeId, UA_StatusCode_name(retval));
1751
0
    }
1752
1753
17.9M
 cleanup:
1754
17.9M
    UA_NODESTORE_RELEASE(server, node);
1755
17.9M
    if(type)
1756
14.1M
        UA_NODESTORE_RELEASE(server, type);
1757
17.9M
    if(retval != UA_STATUSCODE_GOOD)
1758
1.05k
        deleteNode(server, *nodeId, true);
1759
17.9M
    return retval;
1760
17.9M
}
1761
1762
static void
1763
Operation_addNode(UA_Server *server, UA_Session *session, void *nodeContext,
1764
185k
                  const UA_AddNodesItem *item, UA_AddNodesResult *result) {
1765
185k
    result->statusCode =
1766
185k
        Operation_addNode_begin(server, session, nodeContext,
1767
185k
                                item, &item->parentNodeId.nodeId,
1768
185k
                                &item->referenceTypeId, &result->addedNodeId);
1769
185k
    if(result->statusCode != UA_STATUSCODE_GOOD)
1770
102
        return;
1771
1772
    /* AddNodes_finish */
1773
184k
    result->statusCode = addNode_finish(server, session, &result->addedNodeId);
1774
1775
    /* If finishing failed, the node was deleted */
1776
184k
    if(result->statusCode != UA_STATUSCODE_GOOD)
1777
10
        UA_NodeId_clear(&result->addedNodeId);
1778
184k
}
1779
1780
UA_Boolean
1781
Service_AddNodes(UA_Server *server, UA_Session *session,
1782
                 const UA_AddNodesRequest *request,
1783
20
                 UA_AddNodesResponse *response) {
1784
20
    UA_LOG_DEBUG_SESSION(server->config.logging, session, "Processing AddNodesRequest");
1785
20
    UA_LOCK_ASSERT(&server->serviceMutex);
1786
1787
20
    if(server->config.maxNodesPerNodeManagement != 0 &&
1788
0
       request->nodesToAddSize > server->config.maxNodesPerNodeManagement) {
1789
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
1790
0
        return true;
1791
0
    }
1792
1793
20
    response->responseHeader.serviceResult =
1794
20
        allocProcessServiceOperations(server, session,
1795
20
                                      (UA_ServiceOperation)Operation_addNode, NULL,
1796
20
                                      &request->nodesToAddSize,
1797
20
                                      &UA_TYPES[UA_TYPES_ADDNODESITEM],
1798
20
                                      &response->resultsSize,
1799
20
                                      &UA_TYPES[UA_TYPES_ADDNODESRESULT]);
1800
20
    return true;
1801
20
}
1802
1803
UA_StatusCode
1804
addNode(UA_Server *server, const UA_NodeClass nodeClass, const UA_NodeId requestedNewNodeId,
1805
        const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
1806
        const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
1807
        const void *attr, const UA_DataType *attributeType,
1808
554k
        void *nodeContext, UA_NodeId *outNewNodeId) {
1809
554k
    UA_LOCK_ASSERT(&server->serviceMutex);
1810
1811
    /* Create the AddNodesItem */
1812
554k
    UA_AddNodesItem item;
1813
554k
    UA_AddNodesItem_init(&item);
1814
554k
    item.nodeClass = nodeClass;
1815
554k
    item.requestedNewNodeId.nodeId = requestedNewNodeId;
1816
554k
    item.browseName = browseName;
1817
554k
    item.parentNodeId.nodeId = parentNodeId;
1818
554k
    item.referenceTypeId = referenceTypeId;
1819
554k
    item.typeDefinition.nodeId = typeDefinition;
1820
554k
    UA_ExtensionObject_setValueNoDelete(&item.nodeAttributes,
1821
554k
                                        (void*)(uintptr_t)attr, attributeType);
1822
1823
    /* Call the normal addnodes service */
1824
554k
    UA_AddNodesResult result;
1825
554k
    UA_AddNodesResult_init(&result);
1826
554k
    Operation_addNode(server, &server->adminSession, nodeContext, &item, &result);
1827
554k
    if(outNewNodeId)
1828
0
        *outNewNodeId = result.addedNodeId;
1829
554k
    else
1830
554k
        UA_NodeId_clear(&result.addedNodeId);
1831
554k
    return result.statusCode;
1832
554k
}
1833
1834
static UA_StatusCode
1835
__Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
1836
                    const UA_NodeId *requestedNewNodeId,
1837
                    const UA_NodeId *parentNodeId,
1838
                    const UA_NodeId *referenceTypeId,
1839
                    const UA_QualifiedName browseName,
1840
                    const UA_NodeId *typeDefinition,
1841
                    const UA_NodeAttributes *attr,
1842
                    const UA_DataType *attributeType,
1843
0
                    void *nodeContext, UA_NodeId *outNewNodeId) {
1844
0
    lockServer(server);
1845
0
    UA_StatusCode reval =
1846
0
        addNode(server, nodeClass, *requestedNewNodeId, *parentNodeId,
1847
0
                *referenceTypeId, browseName, *typeDefinition, attr,
1848
0
                attributeType, nodeContext, outNewNodeId);
1849
0
    unlockServer(server);
1850
0
    return reval;
1851
0
}
1852
1853
UA_StatusCode
1854
UA_Server_addVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
1855
                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
1856
                          const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
1857
                          const UA_VariableAttributes attr, void *nodeContext,
1858
0
                          UA_NodeId *outNewNodeId) {
1859
0
    return __Server_addNode(server, UA_NODECLASS_VARIABLE, &requestedNewNodeId,
1860
0
                            &parentNodeId, &referenceTypeId, browseName,
1861
0
                            &typeDefinition, (const UA_NodeAttributes*)&attr,
1862
0
                            &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
1863
0
                            nodeContext, outNewNodeId);
1864
0
}
1865
1866
UA_StatusCode
1867
UA_Server_addVariableTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
1868
                              const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
1869
                              const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
1870
                              const UA_VariableTypeAttributes attr, void *nodeContext,
1871
0
                              UA_NodeId *outNewNodeId) {
1872
0
    return __Server_addNode(server, UA_NODECLASS_VARIABLETYPE,
1873
0
                            &requestedNewNodeId, &parentNodeId, &referenceTypeId,
1874
0
                            browseName, &typeDefinition,
1875
0
                            (const UA_NodeAttributes*)&attr,
1876
0
                            &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES],
1877
0
                            nodeContext, outNewNodeId);
1878
0
}
1879
1880
UA_StatusCode
1881
UA_Server_addObjectNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
1882
                        const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
1883
                        const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
1884
                        const UA_ObjectAttributes attr, void *nodeContext,
1885
0
                        UA_NodeId *outNewNodeId) {
1886
0
    return __Server_addNode(server, UA_NODECLASS_OBJECT, &requestedNewNodeId,
1887
0
                            &parentNodeId, &referenceTypeId, browseName,
1888
0
                            &typeDefinition, (const UA_NodeAttributes*)&attr,
1889
0
                            &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES],
1890
0
                            nodeContext, outNewNodeId);
1891
0
}
1892
1893
UA_StatusCode
1894
UA_Server_addObjectTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
1895
                            const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
1896
                            const UA_QualifiedName browseName, const UA_ObjectTypeAttributes attr,
1897
0
                            void *nodeContext, UA_NodeId *outNewNodeId) {
1898
0
    return __Server_addNode(server, UA_NODECLASS_OBJECTTYPE, &requestedNewNodeId,
1899
0
                            &parentNodeId, &referenceTypeId, browseName,
1900
0
                            &UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
1901
0
                            &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES],
1902
0
                            nodeContext, outNewNodeId);
1903
0
}
1904
1905
UA_StatusCode
1906
UA_Server_addViewNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
1907
                      const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
1908
                      const UA_QualifiedName browseName, const UA_ViewAttributes attr,
1909
0
                      void *nodeContext, UA_NodeId *outNewNodeId) {
1910
0
    return __Server_addNode(server, UA_NODECLASS_VIEW, &requestedNewNodeId,
1911
0
                            &parentNodeId, &referenceTypeId, browseName,
1912
0
                            &UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
1913
0
                            &UA_TYPES[UA_TYPES_VIEWATTRIBUTES],
1914
0
                            nodeContext, outNewNodeId);
1915
0
}
1916
1917
UA_StatusCode
1918
UA_Server_addReferenceTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
1919
                               const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
1920
                               const UA_QualifiedName browseName,
1921
                               const UA_ReferenceTypeAttributes attr, void *nodeContext,
1922
0
                               UA_NodeId *outNewNodeId) {
1923
0
    return __Server_addNode(server, UA_NODECLASS_REFERENCETYPE,
1924
0
                            &requestedNewNodeId, &parentNodeId, &referenceTypeId,
1925
0
                            browseName, &UA_NODEID_NULL,
1926
0
                            (const UA_NodeAttributes*)&attr,
1927
0
                            &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES],
1928
0
                            nodeContext, outNewNodeId);
1929
0
}
1930
1931
UA_StatusCode
1932
UA_Server_addDataTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
1933
                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
1934
                          const UA_QualifiedName browseName, const UA_DataTypeAttributes attr,
1935
0
                          void *nodeContext, UA_NodeId *outNewNodeId) {
1936
0
    return __Server_addNode(server, UA_NODECLASS_DATATYPE, &requestedNewNodeId,
1937
0
                            &parentNodeId, &referenceTypeId, browseName,
1938
0
                            &UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
1939
0
                            &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES],
1940
0
                            nodeContext, outNewNodeId);
1941
0
}
1942
1943
UA_StatusCode
1944
addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
1945
              const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
1946
              const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
1947
              const UA_NodeId typeDefinition, const void *attr,
1948
              const UA_DataType *attributeType, void *nodeContext,
1949
16.2M
              UA_NodeId *outNewNodeId) {
1950
16.2M
    UA_AddNodesItem item;
1951
16.2M
    UA_AddNodesItem_init(&item);
1952
16.2M
    item.nodeClass = nodeClass;
1953
16.2M
    item.requestedNewNodeId.nodeId = requestedNewNodeId;
1954
16.2M
    item.browseName = browseName;
1955
16.2M
    item.typeDefinition.nodeId = typeDefinition;
1956
16.2M
    UA_ExtensionObject_setValueNoDelete(&item.nodeAttributes,
1957
16.2M
                                        (void*)(uintptr_t)attr, attributeType);
1958
16.2M
    return Operation_addNode_begin(server, &server->adminSession, nodeContext, &item,
1959
16.2M
                                   &parentNodeId, &referenceTypeId, outNewNodeId);
1960
16.2M
}
1961
1962
UA_StatusCode
1963
UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
1964
                        const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
1965
                        const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
1966
                        const UA_NodeId typeDefinition, const void *attr,
1967
                        const UA_DataType *attributeType, void *nodeContext,
1968
16.2M
                        UA_NodeId *outNewNodeId) {
1969
16.2M
    lockServer(server);
1970
16.2M
    UA_StatusCode res =
1971
16.2M
        addNode_begin(server, nodeClass, requestedNewNodeId, parentNodeId,
1972
16.2M
                      referenceTypeId, browseName, typeDefinition, attr,
1973
16.2M
                      attributeType, nodeContext, outNewNodeId);
1974
16.2M
    unlockServer(server);
1975
16.2M
    return res;
1976
16.2M
}
1977
1978
UA_StatusCode
1979
5.15M
UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId) {
1980
5.15M
    lockServer(server);
1981
5.15M
    UA_StatusCode retval = addNode_finish(server, &server->adminSession, &nodeId);
1982
5.15M
    unlockServer(server);
1983
5.15M
    return retval;
1984
5.15M
}
1985
1986
/****************/
1987
/* Delete Nodes */
1988
/****************/
1989
1990
static void
1991
Operation_deleteReference(UA_Server *server, UA_Session *session, void *context,
1992
                          const UA_DeleteReferencesItem *item, UA_StatusCode *retval);
1993
1994
struct RemoveIncomingContext {
1995
    UA_Server *server;
1996
    UA_Session *session;
1997
    UA_DeleteReferencesItem *item;
1998
};
1999
2000
static void *
2001
2.04M
removeIncomingReferencesCallback(void *context, UA_ReferenceTarget *t) {
2002
2.04M
    struct RemoveIncomingContext *ctx = (struct RemoveIncomingContext *)context;
2003
2.04M
    if(!UA_NodePointer_isLocal(t->targetId))
2004
0
        return NULL;
2005
2.04M
    UA_StatusCode dummy;
2006
2.04M
    ctx->item->sourceNodeId = UA_NodePointer_toNodeId(t->targetId);
2007
2.04M
    Operation_deleteReference(ctx->server, ctx->session, NULL, ctx->item, &dummy);
2008
2.04M
    return NULL;
2009
2.04M
}
2010
2011
/* Remove references to this node (in the other nodes) */
2012
static void
2013
removeIncomingReferences(UA_Server *server, UA_Session *session,
2014
626k
                         const UA_NodeHead *head) {
2015
626k
    UA_DeleteReferencesItem item;
2016
626k
    UA_DeleteReferencesItem_init(&item);
2017
626k
    item.targetNodeId.nodeId = head->nodeId;
2018
626k
    item.deleteBidirectional = false;
2019
2020
626k
    struct RemoveIncomingContext ctx;
2021
626k
    ctx.server = server;
2022
626k
    ctx.session = session;
2023
626k
    ctx.item = &item;
2024
2025
2.35M
    for(size_t i = 0; i < head->referencesSize; ++i) {
2026
1.73M
        UA_NodeReferenceKind *rk = &head->references[i];
2027
1.73M
        item.isForward = rk->isInverse;
2028
1.73M
        item.referenceTypeId =
2029
1.73M
            *UA_NODESTORE_GETREFERENCETYPEID(server, rk->referenceTypeIndex);
2030
1.73M
        UA_NodeReferenceKind_iterate(rk, removeIncomingReferencesCallback, &ctx);
2031
1.73M
    }
2032
626k
}
2033
2034
static void *
2035
766k
checkTargetInRefTree(void *context, UA_ReferenceTarget *t) {
2036
766k
    RefTree *refTree = (RefTree*)context;
2037
766k
    if(!UA_NodePointer_isLocal(t->targetId))
2038
0
        return NULL;
2039
766k
    UA_NodeId tmpId = UA_NodePointer_toNodeId(t->targetId);
2040
766k
    if(!RefTree_containsNodeId(refTree, &tmpId))
2041
2.57k
        return (void*)0x1;
2042
764k
    return NULL;
2043
766k
}
2044
2045
/* A node is auto-deleted if all its hierarchical parents are being deleted */
2046
static UA_Boolean
2047
hasParentRef(const UA_NodeHead *head, const UA_ReferenceTypeSet *refSet,
2048
762k
             RefTree *refTree) {
2049
2.90M
    for(size_t i = 0; i < head->referencesSize; i++) {
2050
2.14M
        UA_NodeReferenceKind *rk = &head->references[i];
2051
2.14M
        if(!rk->isInverse)
2052
1.36M
            continue;
2053
784k
        if(!UA_ReferenceTypeSet_contains(refSet, rk->referenceTypeIndex))
2054
21.0k
            continue;
2055
763k
        if(UA_NodeReferenceKind_iterate(rk, checkTargetInRefTree, refTree) != NULL)
2056
2.57k
            return true;
2057
763k
    }
2058
759k
    return false;
2059
762k
}
2060
2061
static void
2062
deconstructNodeSet(UA_Server *server, UA_Session *session,
2063
15.4k
                   UA_ReferenceTypeSet *hierarchRefsSet, RefTree *refTree) {
2064
15.4k
    UA_LOCK_ASSERT(&server->serviceMutex);
2065
2066
    /* Deconstruct the nodes based on the RefTree entries, parent nodes first */
2067
789k
    for(size_t i = 0; i < refTree->size; i++) {
2068
773k
        const UA_Node *member = UA_NODESTORE_GET(server, &refTree->targets[i].nodeId);
2069
773k
        if(!member)
2070
0
            continue;
2071
2072
        /* Call the type-level destructor
2073
         * TODO: Do we need all attributes and references here?  */
2074
773k
        void *context = member->head.context; /* No longer needed after this function */
2075
773k
        if(member->head.nodeClass == UA_NODECLASS_OBJECT ||
2076
743k
           member->head.nodeClass == UA_NODECLASS_VARIABLE) {
2077
695k
            const UA_Node *type =
2078
695k
                getNodeType(server, &member->head, ~(UA_UInt32)0,
2079
695k
                            UA_REFERENCETYPESET_ALL, UA_BROWSEDIRECTION_BOTH);
2080
695k
            if(type) {
2081
               /* Get the lifecycle */
2082
612k
               const UA_NodeTypeLifecycle *lifecycle;
2083
612k
               if(member->head.nodeClass == UA_NODECLASS_OBJECT)
2084
28.9k
                  lifecycle = &type->objectTypeNode.lifecycle;
2085
583k
               else
2086
583k
                  lifecycle = &type->variableTypeNode.lifecycle;
2087
2088
               /* Call the destructor */
2089
612k
               if(lifecycle->destructor) {
2090
2.01k
                  lifecycle->destructor(server,
2091
2.01k
                                        &session->sessionId, session->context,
2092
2.01k
                                        &type->head.nodeId, type->head.context,
2093
2.01k
                                        &member->head.nodeId, &context);
2094
2.01k
               }
2095
2096
               /* Release the type node */
2097
612k
               UA_NODESTORE_RELEASE(server, type);
2098
612k
            }
2099
695k
        }
2100
2101
        /* Call the global destructor */
2102
773k
        if(server->config.nodeLifecycle &&
2103
0
           server->config.nodeLifecycle->destructor) {
2104
0
            server->config.nodeLifecycle->destructor(server, &session->sessionId,
2105
0
                                                     session->context,
2106
0
                                                     &member->head.nodeId, context);
2107
0
        }
2108
2109
        /* Release the node. Don't access the node context from here on. */
2110
773k
        UA_NODESTORE_RELEASE(server, member);
2111
2112
        /* Set the constructed flag to false */
2113
773k
        editNode(server, &server->adminSession, &refTree->targets[i].nodeId,
2114
773k
                 0, UA_REFERENCETYPESET_NONE, UA_BROWSEDIRECTION_INVALID,
2115
773k
                 (UA_EditNodeCallback)setDeconstructedNode, NULL);
2116
773k
    }
2117
15.4k
}
2118
2119
struct DeleteChildrenContext {
2120
    UA_Server *server;
2121
    const UA_ReferenceTypeSet *hierarchRefsSet;
2122
    RefTree *refTree;
2123
    UA_StatusCode res;
2124
};
2125
2126
static void *
2127
762k
deleteChildrenCallback(void *context, UA_ReferenceTarget *t) {
2128
762k
    struct DeleteChildrenContext *ctx = (struct DeleteChildrenContext*)context;
2129
2130
    /* Get the child */
2131
762k
    const UA_Node *child = UA_NODESTORE_GETFROMREF(ctx->server, t->targetId);
2132
762k
    if(!child)
2133
245
        return NULL;
2134
2135
    /* Only delete child nodes that have no other parent */
2136
762k
    if(!hasParentRef(&child->head, ctx->hierarchRefsSet, ctx->refTree))
2137
759k
        ctx->res = RefTree_addNodeId(ctx->refTree, &child->head.nodeId, NULL);
2138
2139
762k
    UA_NODESTORE_RELEASE(ctx->server, child);
2140
762k
    return (ctx->res == UA_STATUSCODE_GOOD) ? NULL : (void*)0x01;
2141
762k
}
2142
2143
/* The processNodeLayer function searches all children's of the head node and
2144
 * adds the children node to the RefTree if all incoming references sources are
2145
 * contained in the RefTree (No callback references to this node --> node can be
2146
 * deleted) */
2147
static UA_StatusCode
2148
autoDeleteChildren(UA_Server *server, UA_Session *session, RefTree *refTree,
2149
773k
                   const UA_ReferenceTypeSet *hierarchRefsSet, const UA_NodeHead *head){
2150
773k
    struct DeleteChildrenContext ctx;
2151
773k
    ctx.server = server;
2152
773k
    ctx.hierarchRefsSet = hierarchRefsSet;
2153
773k
    ctx.refTree = refTree;
2154
773k
    ctx.res = UA_STATUSCODE_GOOD;
2155
2156
2.94M
    for(size_t i = 0; i < head->referencesSize; ++i) {
2157
        /* Check if the ReferenceType is hierarchical */
2158
2.17M
        UA_NodeReferenceKind *rk = &head->references[i];
2159
2.17M
        if(!UA_ReferenceTypeSet_contains(hierarchRefsSet, rk->referenceTypeIndex))
2160
1.30M
            continue;
2161
2162
        /* Check if the references are forward (to a child) */
2163
872k
        if(rk->isInverse)
2164
772k
            continue;
2165
2166
        /* Loop over the references */
2167
99.9k
        UA_NodeReferenceKind_iterate(rk, deleteChildrenCallback, &ctx);
2168
99.9k
        if(ctx.res != UA_STATUSCODE_GOOD)
2169
0
            return ctx.res;
2170
99.9k
    }
2171
773k
    return UA_STATUSCODE_GOOD;
2172
773k
}
2173
2174
/* Build up an ordered set (tree) of all nodes that can be deleted. Step through
2175
 * the ordered set in order to avoid recursion. */
2176
static UA_StatusCode
2177
buildDeleteNodeSet(UA_Server *server, UA_Session *session,
2178
                   const UA_ReferenceTypeSet *hierarchRefsSet,
2179
                   const UA_NodeId *initial, UA_Boolean removeTargetRefs,
2180
15.4k
                   RefTree *refTree) {
2181
    /* Add the initial node to delete */
2182
15.4k
    UA_StatusCode res = RefTree_addNodeId(refTree, initial, NULL);
2183
15.4k
    if(res != UA_STATUSCODE_GOOD)
2184
0
        return res;
2185
2186
    /* Find out which hierarchical children should also be deleted. We know
2187
     * there are no "callback" ExpandedNodeId in the RefTree. */
2188
15.4k
    size_t pos = 0;
2189
789k
    while(pos < refTree->size) {
2190
773k
        const UA_Node *member = UA_NODESTORE_GET(server, &refTree->targets[pos].nodeId);
2191
773k
        pos++;
2192
773k
        if(!member)
2193
0
            continue;
2194
773k
        res |= autoDeleteChildren(server, session, refTree, hierarchRefsSet, &member->head);
2195
773k
        UA_NODESTORE_RELEASE(server, member);
2196
773k
    }
2197
15.4k
    return res;
2198
15.4k
}
2199
2200
static void
2201
deleteNodeSet(UA_Server *server, UA_Session *session,
2202
              const UA_ReferenceTypeSet *hierarchRefsSet,
2203
15.4k
              UA_Boolean removeTargetRefs, RefTree *refTree) {
2204
    /* Delete the nodes based on the RefTree entries */
2205
789k
    for(size_t i = refTree->size; i > 0; --i) {
2206
773k
        const UA_Node *member = UA_NODESTORE_GET(server, &refTree->targets[i-1].nodeId);
2207
773k
        if(!member)
2208
0
            continue;
2209
773k
        UA_NODESTORE_RELEASE(server, member);
2210
773k
        if(removeTargetRefs)
2211
626k
            removeIncomingReferences(server, session, &member->head);
2212
773k
        UA_NODESTORE_REMOVE(server, &member->head.nodeId);
2213
773k
    }
2214
15.4k
}
2215
2216
static void
2217
deleteNodeOperation(UA_Server *server, UA_Session *session, void *context,
2218
228k
                    const UA_DeleteNodesItem *item, UA_StatusCode *result) {
2219
228k
    UA_LOCK_ASSERT(&server->serviceMutex);
2220
2221
    /* Do not check access for server */
2222
228k
    if(session != &server->adminSession && server->config.accessControl.allowDeleteNode) {
2223
2.21k
        if(!server->config.accessControl.
2224
2.21k
           allowDeleteNode(server, &server->config.accessControl,
2225
2.21k
                           &session->sessionId, session->context, item)) {
2226
0
            *result = UA_STATUSCODE_BADUSERACCESSDENIED;
2227
0
            return;
2228
0
        }
2229
2.21k
    }
2230
2231
228k
    const UA_Node *node = UA_NODESTORE_GET(server, &item->nodeId);
2232
228k
    if(!node) {
2233
221k
        *result = UA_STATUSCODE_BADNODEIDUNKNOWN;
2234
221k
        return;
2235
221k
    }
2236
2237
6.46k
    if(UA_Node_hasSubTypeOrInstances(&node->head)) {
2238
46
        UA_LOG_INFO_SESSION(server->config.logging, session,
2239
46
                            "DeleteNode (%N): Cannot delete a type node with "
2240
46
                            "active instances or subtypes", node->head.nodeId);
2241
46
        UA_NODESTORE_RELEASE(server, node);
2242
46
        *result = UA_STATUSCODE_BADINTERNALERROR;
2243
46
        return;
2244
46
    }
2245
2246
    /* TODO: Check if the information model consistency is violated */
2247
    /* TODO: Check if the node is a mandatory child of a parent */
2248
2249
    /* Relase the node. Don't access the pointer after this! */
2250
6.42k
    UA_NODESTORE_RELEASE(server, node);
2251
2252
    /* A node can be referenced with hierarchical references from several
2253
     * parents in the information model. (But not in a circular way.) The
2254
     * hierarchical references are checked to see if a node can be deleted.
2255
     * Getting the type hierarchy can fail in case of low RAM. In that case the
2256
     * nodes are always deleted. */
2257
6.42k
    UA_ReferenceTypeSet hierarchRefsSet;
2258
6.42k
    UA_NodeId hr = UA_NS0ID(HIERARCHICALREFERENCES);
2259
6.42k
    *result = referenceTypeIndices(server, &hr, &hierarchRefsSet, true);
2260
6.42k
    if(*result != UA_STATUSCODE_GOOD)
2261
408
        return;
2262
2263
    /* The list of childs is needed for the deconstructing and deleting phase.
2264
     * Within the processNodeLayer we generate a RefTree based set of childs
2265
     * which can be deleted beside the parent node. */
2266
6.01k
    RefTree refTree;
2267
6.01k
    *result = RefTree_init(&refTree);
2268
6.01k
    if(*result != UA_STATUSCODE_GOOD)
2269
0
        return;
2270
6.01k
    *result = buildDeleteNodeSet(server, session, &hierarchRefsSet, &item->nodeId,
2271
6.01k
                                 item->deleteTargetReferences, &refTree);
2272
6.01k
    if(*result != UA_STATUSCODE_GOOD) {
2273
0
        UA_LOG_WARNING_SESSION(server->config.logging, session,
2274
0
                               "DeleteNode: Incomplete lookup of nodes. "
2275
0
                               "Still deleting what we have.");
2276
        /* Continue, so the RefTree is cleaned up. Return the error message
2277
         * anyway. */
2278
0
    }
2279
2280
    /* Deconstruct, then delete, then clean up the set */
2281
6.01k
    deconstructNodeSet(server, session, &hierarchRefsSet, &refTree);
2282
6.01k
    deleteNodeSet(server, session, &hierarchRefsSet,
2283
6.01k
                  item->deleteTargetReferences, &refTree);
2284
6.01k
    RefTree_clear(&refTree);
2285
6.01k
}
2286
2287
UA_Boolean
2288
Service_DeleteNodes(UA_Server *server, UA_Session *session,
2289
                    const UA_DeleteNodesRequest *request,
2290
344
                    UA_DeleteNodesResponse *response) {
2291
344
    UA_LOG_DEBUG_SESSION(server->config.logging, session,
2292
344
                         "Processing DeleteNodesRequest");
2293
344
    UA_LOCK_ASSERT(&server->serviceMutex);
2294
2295
344
    if(server->config.maxNodesPerNodeManagement != 0 &&
2296
0
       request->nodesToDeleteSize > server->config.maxNodesPerNodeManagement) {
2297
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
2298
0
        return true;
2299
0
    }
2300
2301
344
    response->responseHeader.serviceResult =
2302
344
        allocProcessServiceOperations(server, session,
2303
344
                                      (UA_ServiceOperation)deleteNodeOperation,
2304
344
                                      NULL, &request->nodesToDeleteSize,
2305
344
                                      &UA_TYPES[UA_TYPES_DELETENODESITEM],
2306
344
                                      &response->resultsSize,
2307
344
                                      &UA_TYPES[UA_TYPES_STATUSCODE]);
2308
344
    return true;
2309
344
}
2310
2311
UA_StatusCode
2312
UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId,
2313
0
                     UA_Boolean deleteReferences) {
2314
0
    lockServer(server);
2315
0
    UA_StatusCode retval = deleteNode(server, nodeId, deleteReferences);
2316
0
    unlockServer(server);
2317
0
    return retval;
2318
0
}
2319
2320
UA_StatusCode
2321
deleteNode(UA_Server *server, const UA_NodeId nodeId,
2322
225k
           UA_Boolean deleteReferences) {
2323
225k
    UA_LOCK_ASSERT(&server->serviceMutex);
2324
225k
    UA_DeleteNodesItem item;
2325
225k
    item.deleteTargetReferences = deleteReferences;
2326
225k
    item.nodeId = nodeId;
2327
225k
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
2328
225k
    deleteNodeOperation(server, &server->adminSession, NULL, &item, &retval);
2329
225k
    return retval;
2330
225k
}
2331
2332
/******************/
2333
/* Add References */
2334
/******************/
2335
2336
static void
2337
Operation_addReference(UA_Server *server, UA_Session *session, void *context,
2338
14.2M
                       const UA_AddReferencesItem *item, UA_StatusCode *retval) {
2339
14.2M
    (void)context;
2340
14.2M
    UA_assert(session);
2341
14.2M
    UA_LOCK_ASSERT(&server->serviceMutex);
2342
2343
    /* Check access rights */
2344
14.2M
    if(session != &server->adminSession && server->config.accessControl.allowAddReference) {
2345
28
        if (!server->config.accessControl.
2346
28
                allowAddReference(server, &server->config.accessControl,
2347
28
                                  &session->sessionId, session->context, item)) {
2348
0
            *retval = UA_STATUSCODE_BADUSERACCESSDENIED;
2349
0
            return;
2350
0
        }
2351
28
    }
2352
2353
    /* TODO: Currently no expandednodeids are allowed */
2354
14.2M
    if(item->targetServerUri.length > 0) {
2355
1
        *retval = UA_STATUSCODE_BADNOTIMPLEMENTED;
2356
1
        return;
2357
1
    }
2358
2359
    /* Check the ReferenceType and get the index */
2360
14.2M
    const UA_Node *refType = UA_NODESTORE_GET(server, &item->referenceTypeId);
2361
14.2M
    if(!refType) {
2362
1
        UA_LOG_DEBUG_SESSION(server->config.logging, session,
2363
1
                             "Cannot add reference - ReferenceType %N unknown",
2364
1
                             item->referenceTypeId);
2365
1
        *retval = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
2366
1
        return;
2367
1
    }
2368
14.2M
    if(refType->head.nodeClass != UA_NODECLASS_REFERENCETYPE) {
2369
1
        UA_LOG_DEBUG_SESSION(server->config.logging, session,
2370
1
                             "Cannot add reference - ReferenceType %N with wrong NodeClass",
2371
1
                             item->referenceTypeId);
2372
1
        UA_NODESTORE_RELEASE(server, refType);
2373
1
        *retval = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
2374
1
        return;
2375
1
    }
2376
14.2M
    UA_Byte refTypeIndex = refType->referenceTypeNode.referenceTypeIndex;
2377
14.2M
    UA_NODESTORE_RELEASE(server, refType);
2378
2379
    /* Get the source and target node (editable). Include only the BrowseName
2380
     * and the relevant ReferenceType and direction. Don't modify the target
2381
     * node if it lives on a different server. */
2382
14.2M
    UA_Node *targetNode = NULL;
2383
14.2M
    if(UA_ExpandedNodeId_isLocal(&item->targetNodeId)) {
2384
14.2M
        if(UA_NodeId_equal(&item->targetNodeId.nodeId, &item->sourceNodeId)) {
2385
1
            *retval = UA_STATUSCODE_GOOD;
2386
1
            UA_LOG_INFO_SESSION(server->config.logging, session,
2387
1
                                "Cannot add reference - source and target %N are identical",
2388
1
                                item->targetNodeId.nodeId);
2389
1
            return;
2390
1
        }
2391
14.2M
        targetNode =
2392
14.2M
            UA_NODESTORE_GET_EDIT_SELECTIVE(server, &item->targetNodeId.nodeId,
2393
14.2M
                                            UA_NODEATTRIBUTESMASK_BROWSENAME,
2394
14.2M
                                            UA_REFTYPESET(refTypeIndex),
2395
14.2M
                                            (!item->isForward) ?
2396
14.2M
                                            UA_BROWSEDIRECTION_FORWARD : UA_BROWSEDIRECTION_INVERSE);
2397
14.2M
        if(!targetNode) {
2398
1
            UA_LOG_DEBUG_SESSION(server->config.logging, session,
2399
1
                                 "Cannot add reference - target %N does not exist",
2400
1
                                 item->targetNodeId.nodeId);
2401
1
            *retval = UA_STATUSCODE_BADTARGETNODEIDINVALID;
2402
1
            return;
2403
1
        }
2404
14.2M
        if(item->targetNodeClass != UA_NODECLASS_UNSPECIFIED &&
2405
14
           item->targetNodeClass != targetNode->head.nodeClass) {
2406
14
            UA_LOG_DEBUG_SESSION(server->config.logging, session,
2407
14
                                 "Cannot add reference - target %N has NodeClass %u "
2408
14
                                 "but request expects %u",
2409
14
                                 item->targetNodeId.nodeId, (unsigned)targetNode->head.nodeClass,
2410
14
                                 (unsigned)item->targetNodeClass);
2411
14
            UA_NODESTORE_RELEASE(server, targetNode);
2412
14
            *retval = UA_STATUSCODE_BADNODECLASSINVALID;
2413
14
            return;
2414
14
        }
2415
14.2M
    }
2416
2417
14.2M
    UA_Node *sourceNode =
2418
14.2M
        UA_NODESTORE_GET_EDIT_SELECTIVE(server, &item->sourceNodeId,
2419
14.2M
                                        UA_NODEATTRIBUTESMASK_BROWSENAME,
2420
14.2M
                                        UA_REFTYPESET(refTypeIndex),
2421
14.2M
                                        item->isForward ?
2422
14.2M
                                        UA_BROWSEDIRECTION_FORWARD : UA_BROWSEDIRECTION_INVERSE);
2423
14.2M
    if(!sourceNode) {
2424
12.9k
        if(targetNode)
2425
12.9k
            UA_NODESTORE_RELEASE(server, targetNode);
2426
12.9k
        *retval = UA_STATUSCODE_BADSOURCENODEIDINVALID;
2427
12.9k
        return;
2428
12.9k
    }
2429
2430
    /* Add the first direction. Use hash 0 for non-local targets where
2431
     * targetNode is NULL (their browse name is not available locally). */
2432
14.2M
    UA_UInt32 targetNameHash = targetNode ?
2433
14.2M
        UA_QualifiedName_hash(&targetNode->head.browseName) : 0;
2434
14.2M
    *retval = UA_Node_addReference(sourceNode, refTypeIndex, item->isForward,
2435
14.2M
                                   &item->targetNodeId, targetNameHash);
2436
14.2M
    UA_Boolean firstExisted = false;
2437
14.2M
    if(*retval == UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED) {
2438
0
        *retval = UA_STATUSCODE_GOOD;
2439
0
        firstExisted = true;
2440
0
    }
2441
14.2M
    if(*retval != UA_STATUSCODE_GOOD)
2442
0
        goto cleanup;
2443
2444
    /* Add the second direction */
2445
14.2M
    if(targetNode) {
2446
14.2M
        UA_ExpandedNodeId expSourceId;
2447
14.2M
        UA_ExpandedNodeId_init(&expSourceId);
2448
14.2M
        expSourceId.nodeId = item->sourceNodeId;
2449
14.2M
        UA_UInt32 sourceNameHash = UA_QualifiedName_hash(&sourceNode->head.browseName);
2450
14.2M
        *retval = UA_Node_addReference(targetNode, refTypeIndex, !item->isForward,
2451
14.2M
                                       &expSourceId, sourceNameHash);
2452
2453
        /* Second direction existed already */
2454
14.2M
        if(*retval == UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED) {
2455
            /* Set bad result if BOTH directions already existed.
2456
             * But don't delete the reference from source to target. */
2457
0
            if(!firstExisted)
2458
0
                *retval = UA_STATUSCODE_GOOD;
2459
0
            goto cleanup;
2460
0
        }
2461
2462
        /* Remove first direction if the second direction failed */
2463
14.2M
        if(*retval != UA_STATUSCODE_GOOD)
2464
0
            UA_Node_deleteReference(sourceNode, refTypeIndex, item->isForward, &item->targetNodeId);
2465
14.2M
    }
2466
2467
14.2M
 cleanup:
2468
14.2M
    if(targetNode)
2469
14.2M
        UA_NODESTORE_RELEASE(server, targetNode);
2470
14.2M
    UA_NODESTORE_RELEASE(server, sourceNode);
2471
14.2M
}
2472
2473
UA_Boolean
2474
Service_AddReferences(UA_Server *server, UA_Session *session,
2475
                      const UA_AddReferencesRequest *request,
2476
29
                      UA_AddReferencesResponse *response) {
2477
29
    UA_LOG_DEBUG_SESSION(server->config.logging, session,
2478
29
                         "Processing AddReferencesRequest");
2479
29
    UA_LOCK_ASSERT(&server->serviceMutex);
2480
29
    UA_assert(session);
2481
2482
29
    if(server->config.maxNodesPerNodeManagement != 0 &&
2483
0
       request->referencesToAddSize > server->config.maxNodesPerNodeManagement) {
2484
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
2485
0
        return true;
2486
0
    }
2487
2488
29
    response->responseHeader.serviceResult =
2489
29
        allocProcessServiceOperations(server, session,
2490
29
                                      (UA_ServiceOperation)Operation_addReference,
2491
29
                                      NULL, &request->referencesToAddSize,
2492
29
                                      &UA_TYPES[UA_TYPES_ADDREFERENCESITEM],
2493
29
                                      &response->resultsSize,
2494
29
                                      &UA_TYPES[UA_TYPES_STATUSCODE]);
2495
29
    return true;
2496
29
}
2497
2498
UA_StatusCode
2499
UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
2500
                       const UA_NodeId refTypeId,
2501
                       const UA_ExpandedNodeId targetId,
2502
11.0M
                       UA_Boolean isForward) {
2503
11.0M
    UA_AddReferencesItem item;
2504
11.0M
    UA_AddReferencesItem_init(&item);
2505
11.0M
    item.sourceNodeId = sourceId;
2506
11.0M
    item.referenceTypeId = refTypeId;
2507
11.0M
    item.isForward = isForward;
2508
11.0M
    item.targetNodeId = targetId;
2509
2510
11.0M
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
2511
11.0M
    lockServer(server);
2512
11.0M
    Operation_addReference(server, &server->adminSession, NULL, &item, &retval);
2513
11.0M
    unlockServer(server);
2514
11.0M
    return retval;
2515
11.0M
}
2516
2517
/*********************/
2518
/* Delete References */
2519
/*********************/
2520
2521
static void
2522
Operation_deleteReference(UA_Server *server, UA_Session *session, void *context,
2523
863k
                          const UA_DeleteReferencesItem *item, UA_StatusCode *retval) {
2524
863k
    UA_LOCK_ASSERT(&server->serviceMutex);
2525
2526
    /* Do not check access for server */
2527
863k
    if(session != &server->adminSession &&
2528
341k
       server->config.accessControl.allowDeleteReference) {
2529
341k
        if(!server->config.accessControl.
2530
341k
           allowDeleteReference(server, &server->config.accessControl,
2531
341k
                                &session->sessionId, session->context, item)) {
2532
0
            *retval = UA_STATUSCODE_BADUSERACCESSDENIED;
2533
0
            return;
2534
0
        }
2535
341k
    }
2536
2537
    /* Check the ReferenceType and get the RefTypeIndex */
2538
863k
    const UA_Node *refType =
2539
863k
        UA_NODESTORE_GET_SELECTIVE(server, &item->referenceTypeId,
2540
863k
                                   0, UA_REFERENCETYPESET_NONE, UA_BROWSEDIRECTION_INVALID);
2541
863k
    if(!refType) {
2542
77.4k
        *retval = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
2543
77.4k
        return;
2544
77.4k
    }
2545
2546
786k
    if(refType->head.nodeClass != UA_NODECLASS_REFERENCETYPE) {
2547
16
        UA_NODESTORE_RELEASE(server, refType);
2548
16
        *retval = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
2549
16
        return;
2550
16
    }
2551
2552
786k
    UA_Byte refTypeIndex = refType->referenceTypeNode.referenceTypeIndex;
2553
786k
    UA_NODESTORE_RELEASE(server, refType);
2554
2555
    // TODO: Check consistency constraints, remove the references.
2556
2557
    /* Delete the reference in this direction */
2558
786k
    UA_Node *firstNode =
2559
786k
        UA_NODESTORE_GET_EDIT_SELECTIVE(server, &item->sourceNodeId, 0,
2560
786k
                                        UA_REFTYPESET(refTypeIndex),
2561
786k
                                        item->isForward ?
2562
786k
                                        UA_BROWSEDIRECTION_FORWARD : UA_BROWSEDIRECTION_INVERSE);
2563
786k
    if(firstNode) {
2564
781k
        *retval = UA_Node_deleteReference(firstNode, refTypeIndex, item->isForward, &item->targetNodeId);
2565
781k
    } else {
2566
5.18k
        *retval = UA_STATUSCODE_BADNODEIDUNKNOWN;
2567
5.18k
    }
2568
786k
    UA_NODESTORE_RELEASE(server, firstNode);
2569
786k
    if(*retval != UA_STATUSCODE_GOOD)
2570
200k
        return;
2571
2572
586k
    if(!item->deleteBidirectional || item->targetNodeId.serverIndex != 0)
2573
586k
        return;
2574
2575
    /* Remove the reference in the second direction */
2576
0
    if(UA_ExpandedNodeId_isLocal(&item->targetNodeId)) {
2577
0
        UA_ExpandedNodeId target2;
2578
0
        UA_ExpandedNodeId_init(&target2);
2579
0
        target2.nodeId = item->sourceNodeId;
2580
0
        UA_Node *secondNode =
2581
0
            UA_NODESTORE_GET_EDIT_SELECTIVE(server, &item->targetNodeId.nodeId, 0,
2582
0
                                            UA_REFTYPESET(refTypeIndex),
2583
0
                                            (!item->isForward) ?
2584
0
                                            UA_BROWSEDIRECTION_FORWARD : UA_BROWSEDIRECTION_INVERSE);
2585
0
        if(secondNode) {
2586
0
            *retval = UA_Node_deleteReference(secondNode, refTypeIndex, !item->isForward, &target2);
2587
0
            UA_NODESTORE_RELEASE(server, secondNode);
2588
0
        }
2589
0
    }
2590
0
}
2591
2592
UA_Boolean
2593
Service_DeleteReferences(UA_Server *server, UA_Session *session,
2594
                         const UA_DeleteReferencesRequest *request,
2595
11
                         UA_DeleteReferencesResponse *response) {
2596
11
    UA_LOG_DEBUG_SESSION(server->config.logging, session,
2597
11
                         "Processing DeleteReferencesRequest");
2598
11
    UA_LOCK_ASSERT(&server->serviceMutex);
2599
2600
11
    if(server->config.maxNodesPerNodeManagement != 0 &&
2601
0
       request->referencesToDeleteSize > server->config.maxNodesPerNodeManagement) {
2602
0
        response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
2603
0
        return true;
2604
0
    }
2605
2606
11
    response->responseHeader.serviceResult =
2607
11
        allocProcessServiceOperations(server, session,
2608
11
                                      (UA_ServiceOperation)Operation_deleteReference,
2609
11
                                      NULL, &request->referencesToDeleteSize,
2610
11
                                      &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM],
2611
11
                                      &response->resultsSize,
2612
11
                                      &UA_TYPES[UA_TYPES_STATUSCODE]);
2613
11
    return true;
2614
11
}
2615
2616
UA_StatusCode
2617
deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
2618
                const UA_NodeId referenceTypeId, UA_Boolean isForward,
2619
                const UA_ExpandedNodeId targetNodeId,
2620
0
                UA_Boolean deleteBidirectional) {
2621
0
    UA_DeleteReferencesItem item;
2622
0
    item.sourceNodeId = sourceNodeId;
2623
0
    item.referenceTypeId = referenceTypeId;
2624
0
    item.isForward = isForward;
2625
0
    item.targetNodeId = targetNodeId;
2626
0
    item.deleteBidirectional = deleteBidirectional;
2627
2628
0
    UA_StatusCode retval = UA_STATUSCODE_GOOD;
2629
0
    Operation_deleteReference(server, &server->adminSession, NULL, &item, &retval);
2630
0
    return retval;
2631
0
}
2632
2633
UA_StatusCode
2634
UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
2635
                          const UA_NodeId referenceTypeId, UA_Boolean isForward,
2636
                          const UA_ExpandedNodeId targetNodeId,
2637
0
                          UA_Boolean deleteBidirectional) {
2638
0
    lockServer(server);
2639
0
    UA_StatusCode res = deleteReference(server, sourceNodeId, referenceTypeId,
2640
0
                                        isForward, targetNodeId, deleteBidirectional);
2641
0
    unlockServer(server);
2642
0
    return res;
2643
0
}
2644
2645
/*****************************/
2646
/* Set Internal Value Source */
2647
/*****************************/
2648
2649
struct SetInternalValueContext {
2650
    const UA_DataValue *value;
2651
    const UA_ValueSourceNotifications *notifications;
2652
};
2653
2654
static UA_StatusCode
2655
setInternalValueSourceCB(UA_Server *server, UA_Session *session,
2656
0
                         UA_VariableNode *vn, const void *ctx) {
2657
0
    const struct SetInternalValueContext *ivc =
2658
0
        (const struct SetInternalValueContext*)ctx;
2659
2660
    /* Check the node class */
2661
0
    if(vn->head.nodeClass != UA_NODECLASS_VARIABLE)
2662
0
        return UA_STATUSCODE_BADNODECLASSINVALID;
2663
2664
    /* Make a copy of the supplied value */
2665
0
    UA_DataValue val;
2666
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
2667
0
    if(ivc->value) {
2668
0
        res = UA_DataValue_copy(ivc->value, &val);
2669
0
        if(res != UA_STATUSCODE_GOOD)
2670
0
            return res;
2671
0
    }
2672
2673
    /* Replace the previous internal value */
2674
0
    if(vn->valueSourceType == UA_VALUESOURCETYPE_INTERNAL) {
2675
0
        if(ivc->value) {
2676
0
            UA_DataValue_clear(&vn->valueSource.internal.value);
2677
0
            vn->valueSource.internal.value = val;
2678
0
        }
2679
0
    } else {
2680
0
        if(ivc->value)
2681
0
            vn->valueSource.internal.value = val;
2682
0
        else
2683
0
            UA_DataValue_init(&vn->valueSource.internal.value);
2684
0
        vn->valueSourceType = UA_VALUESOURCETYPE_INTERNAL;
2685
0
    }
2686
2687
    /* Set the notification callbacks */
2688
0
    if(ivc->notifications)
2689
0
        vn->valueSource.internal.notifications = *ivc->notifications;
2690
0
    else
2691
0
        memset(&vn->valueSource.internal.notifications, 0,
2692
0
               sizeof(UA_ValueSourceNotifications));
2693
2694
0
    return UA_STATUSCODE_GOOD;
2695
0
}
2696
2697
UA_StatusCode
2698
setVariableNode_internalValueSource(UA_Server *server, const UA_NodeId nodeId,
2699
                                    const UA_DataValue *value,
2700
0
                                    const UA_ValueSourceNotifications *notifications) {
2701
0
    struct SetInternalValueContext ctx;
2702
0
    ctx.value = value;
2703
0
    ctx.notifications = notifications;
2704
0
    return editNode(server, &server->adminSession, &nodeId,
2705
0
                    UA_NODEATTRIBUTESMASK_VALUE, UA_REFERENCETYPESET_NONE,
2706
0
                    UA_BROWSEDIRECTION_INVALID,
2707
0
                    (UA_EditNodeCallback)setInternalValueSourceCB, &ctx);
2708
0
}
2709
2710
UA_StatusCode
2711
UA_Server_setVariableNode_internalValueSource(UA_Server *server, const UA_NodeId nodeId,
2712
                                              const UA_DataValue *value,
2713
0
                                              const UA_ValueSourceNotifications *notifications) {
2714
0
    lockServer(server);
2715
0
    UA_StatusCode res = setVariableNode_internalValueSource(server, nodeId, value, notifications);
2716
0
    unlockServer(server);
2717
0
    return res;
2718
0
}
2719
2720
/*****************************/
2721
/* Set External Value Source */
2722
/*****************************/
2723
2724
struct SetExternalValueContext {
2725
    UA_DataValue **value;
2726
    const UA_ValueSourceNotifications *notifications;
2727
};
2728
2729
static UA_StatusCode
2730
setExternalValueSourceCB(UA_Server *server, UA_Session *session,
2731
0
                         UA_VariableNode *vn, const void *ctx) {
2732
0
    const struct SetExternalValueContext *evc =
2733
0
        (const struct SetExternalValueContext*)ctx;
2734
2735
    /* Check the node class */
2736
0
    if(vn->head.nodeClass != UA_NODECLASS_VARIABLE)
2737
0
        return UA_STATUSCODE_BADNODECLASSINVALID;
2738
2739
    /* Clean the previous internal value */
2740
0
    if(vn->valueSourceType == UA_VALUESOURCETYPE_INTERNAL && evc->value)
2741
0
        UA_DataValue_clear(&vn->valueSource.internal.value);
2742
2743
    /* Set the value */
2744
0
    vn->valueSourceType = UA_VALUESOURCETYPE_EXTERNAL;
2745
0
    vn->valueSource.external.value = evc->value;
2746
0
    if(evc->notifications)
2747
0
        vn->valueSource.external.notifications = *evc->notifications;
2748
0
    else
2749
0
        memset(&vn->valueSource.external.notifications, 0,
2750
0
               sizeof(UA_ValueSourceNotifications));
2751
2752
0
    return UA_STATUSCODE_GOOD;
2753
0
}
2754
2755
UA_StatusCode
2756
UA_Server_setVariableNode_externalValueSource(UA_Server *server, const UA_NodeId nodeId,
2757
                                              UA_DataValue **value,
2758
0
                                              const UA_ValueSourceNotifications *notifications) {
2759
0
    if(!server || !value || !*value)
2760
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
2761
0
    lockServer(server);
2762
0
    struct SetExternalValueContext ctx;
2763
0
    ctx.value = value;
2764
0
    ctx.notifications = notifications;
2765
0
    UA_StatusCode res = editNode(server, &server->adminSession, &nodeId,
2766
0
                                 UA_NODEATTRIBUTESMASK_VALUE, UA_REFERENCETYPESET_NONE,
2767
0
                                 UA_BROWSEDIRECTION_INVALID,
2768
0
                                 (UA_EditNodeCallback)setExternalValueSourceCB, &ctx);
2769
0
    unlockServer(server);
2770
0
    return res;
2771
0
}
2772
2773
/*****************************/
2774
/* Set Callback Value Source */
2775
/*****************************/
2776
2777
static UA_StatusCode
2778
setCallbackValueSourceCB(UA_Server *server, UA_Session *session,
2779
1.26M
                         UA_VariableNode *vn, const void *ctx) {
2780
1.26M
    const UA_CallbackValueSource *evs = (const UA_CallbackValueSource*)ctx;
2781
2782
    /* Check the node class */
2783
1.26M
    if(vn->head.nodeClass != UA_NODECLASS_VARIABLE)
2784
0
        return UA_STATUSCODE_BADNODECLASSINVALID;
2785
2786
    /* Clean up the internal value */
2787
1.26M
    if(vn->valueSourceType == UA_VALUESOURCETYPE_INTERNAL)
2788
1.26M
        UA_DataValue_clear(&vn->valueSource.internal.value);
2789
2790
    /* Replace the value source */
2791
1.26M
    vn->valueSource.callback = *evs;
2792
1.26M
    vn->valueSourceType = UA_VALUESOURCETYPE_CALLBACK;
2793
2794
1.26M
    return UA_STATUSCODE_GOOD;
2795
1.26M
}
2796
2797
UA_StatusCode
2798
setVariableNode_callbackValueSource(UA_Server *server, const UA_NodeId nodeId,
2799
1.28M
                                    const UA_CallbackValueSource evs) {
2800
1.28M
    return editNode(server, &server->adminSession, &nodeId, UA_NODEATTRIBUTESMASK_VALUE,
2801
1.28M
                    UA_REFERENCETYPESET_NONE, UA_BROWSEDIRECTION_INVALID,
2802
1.28M
                    (UA_EditNodeCallback)setCallbackValueSourceCB,
2803
1.28M
                    (void*)(uintptr_t)&evs);
2804
1.28M
}
2805
2806
UA_StatusCode
2807
UA_Server_setVariableNode_callbackValueSource(UA_Server *server,
2808
                                              const UA_NodeId nodeId,
2809
0
                                              const UA_CallbackValueSource evs) {
2810
0
    lockServer(server);
2811
0
    UA_StatusCode res = setVariableNode_callbackValueSource(server, nodeId, evs);
2812
0
    unlockServer(server);
2813
0
    return res;
2814
0
}
2815
2816
UA_StatusCode
2817
UA_Server_addCallbackValueSourceVariableNode(UA_Server *server,
2818
                                             const UA_NodeId requestedNewNodeId,
2819
                                             const UA_NodeId parentNodeId,
2820
                                             const UA_NodeId referenceTypeId,
2821
                                             const UA_QualifiedName browseName,
2822
                                             const UA_NodeId typeDefinition,
2823
                                             const UA_VariableAttributes attr,
2824
                                             const UA_CallbackValueSource evs,
2825
0
                                             void *nodeContext, UA_NodeId *outNewNodeId) {
2826
0
    UA_AddNodesItem item;
2827
0
    UA_AddNodesItem_init(&item);
2828
0
    item.nodeClass = UA_NODECLASS_VARIABLE;
2829
0
    item.requestedNewNodeId.nodeId = requestedNewNodeId;
2830
0
    item.browseName = browseName;
2831
0
    UA_ExpandedNodeId typeDefinitionId;
2832
0
    UA_ExpandedNodeId_init(&typeDefinitionId);
2833
0
    typeDefinitionId.nodeId = typeDefinition;
2834
0
    item.typeDefinition = typeDefinitionId;
2835
0
    UA_ExtensionObject_setValueNoDelete(&item.nodeAttributes, (void*)(uintptr_t)&attr,
2836
0
                                        &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES]);
2837
0
    UA_NodeId newNodeId;
2838
0
    if(!outNewNodeId) {
2839
0
        newNodeId = UA_NODEID_NULL;
2840
0
        outNewNodeId = &newNodeId;
2841
0
    }
2842
2843
0
    lockServer(server);
2844
2845
    /* Create the node and add it to the nodestore */
2846
0
    UA_StatusCode retval = addNode_raw(server, &server->adminSession, nodeContext,
2847
0
                                       &item, outNewNodeId);
2848
0
    if(retval != UA_STATUSCODE_GOOD)
2849
0
        goto cleanup;
2850
2851
    /* Set the value source */
2852
0
    retval = setVariableNode_callbackValueSource(server, *outNewNodeId, evs);
2853
0
    if(retval != UA_STATUSCODE_GOOD)
2854
0
        goto cleanup;
2855
2856
    /* Typecheck and add references to parent and type definition */
2857
0
    retval = addNode_addRefs(server, &server->adminSession, outNewNodeId, &parentNodeId,
2858
0
                             &referenceTypeId, &typeDefinition);
2859
0
    if(retval != UA_STATUSCODE_GOOD)
2860
0
        goto cleanup;
2861
2862
    /* Call the constructors */
2863
0
    retval = addNode_finish(server, &server->adminSession, outNewNodeId);
2864
2865
0
 cleanup:
2866
0
    unlockServer(server);
2867
0
    if(outNewNodeId == &newNodeId)
2868
0
        UA_NodeId_clear(&newNodeId);
2869
2870
0
    return retval;
2871
0
}
2872
2873
/************************************/
2874
/* Special Handling of Method Nodes */
2875
/************************************/
2876
2877
#ifdef UA_ENABLE_METHODCALLS
2878
2879
static const UA_NodeId hasproperty = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASPROPERTY}};
2880
static const UA_NodeId propertytype = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_PROPERTYTYPE}};
2881
2882
static UA_StatusCode
2883
UA_Server_addMethodNodeEx_finish(UA_Server *server, const UA_NodeId nodeId,
2884
                                 UA_MethodCallback method,
2885
                                 const size_t inputArgumentsSize,
2886
                                 const UA_Argument *inputArguments,
2887
                                 const UA_NodeId inputArgumentsRequestedNewNodeId,
2888
                                 UA_NodeId *inputArgumentsOutNewNodeId,
2889
                                 const size_t outputArgumentsSize,
2890
                                 const UA_Argument *outputArguments,
2891
                                 const UA_NodeId outputArgumentsRequestedNewNodeId,
2892
719k
                                 UA_NodeId *outputArgumentsOutNewNodeId) {
2893
    /* Browse to see which argument nodes exist */
2894
719k
    UA_BrowseDescription bd;
2895
719k
    UA_BrowseDescription_init(&bd);
2896
719k
    bd.nodeId = nodeId;
2897
719k
    bd.referenceTypeId = UA_NS0ID(HASPROPERTY);
2898
719k
    bd.includeSubtypes = false;
2899
719k
    bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
2900
719k
    bd.nodeClassMask = UA_NODECLASS_VARIABLE;
2901
719k
    bd.resultMask = UA_BROWSERESULTMASK_BROWSENAME;
2902
2903
719k
    UA_BrowseResult br;
2904
719k
    UA_BrowseResult_init(&br);
2905
719k
    UA_UInt32 maxrefs = 0;
2906
719k
    Operation_Browse(server, &server->adminSession, &maxrefs, &bd, &br);
2907
2908
719k
    UA_StatusCode retval = br.statusCode;
2909
719k
    if(retval != UA_STATUSCODE_GOOD) {
2910
0
        deleteNode(server, nodeId, true);
2911
0
        UA_BrowseResult_clear(&br);
2912
0
        return retval;
2913
0
    }
2914
2915
    /* Filter out the argument nodes */
2916
719k
    UA_NodeId inputArgsId = UA_NODEID_NULL;
2917
719k
    UA_NodeId outputArgsId = UA_NODEID_NULL;
2918
719k
    const UA_QualifiedName inputArgsName = UA_QUALIFIEDNAME(0, "InputArguments");
2919
719k
    const UA_QualifiedName outputArgsName = UA_QUALIFIEDNAME(0, "OutputArguments");
2920
1.71M
    for(size_t i = 0; i < br.referencesSize; i++) {
2921
991k
        UA_ReferenceDescription *rd = &br.references[i];
2922
991k
        if(rd->browseName.namespaceIndex == 0 &&
2923
991k
           UA_String_equal(&rd->browseName.name, &inputArgsName.name))
2924
641k
            inputArgsId = rd->nodeId.nodeId;
2925
349k
        else if(rd->browseName.namespaceIndex == 0 &&
2926
349k
                UA_String_equal(&rd->browseName.name, &outputArgsName.name))
2927
349k
            outputArgsId = rd->nodeId.nodeId;
2928
991k
    }
2929
2930
    /* Add the Input Arguments VariableNode */
2931
719k
    if(inputArgumentsSize > 0 && UA_NodeId_isNull(&inputArgsId)) {
2932
0
        UA_VariableAttributes attr = UA_VariableAttributes_default;
2933
0
        char *name = "InputArguments";
2934
0
        attr.displayName = UA_LOCALIZEDTEXT("", name);
2935
0
        attr.dataType = UA_TYPES[UA_TYPES_ARGUMENT].typeId;
2936
0
        attr.valueRank = UA_VALUERANK_ONE_DIMENSION;
2937
0
        UA_UInt32 inputArgsSize32 = (UA_UInt32)inputArgumentsSize;
2938
0
        attr.arrayDimensions = &inputArgsSize32;
2939
0
        attr.arrayDimensionsSize = 1;
2940
0
        UA_Variant_setArray(&attr.value, (void *)(uintptr_t)inputArguments,
2941
0
                            inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
2942
0
        retval = addNode(server, UA_NODECLASS_VARIABLE, inputArgumentsRequestedNewNodeId,
2943
0
                         nodeId, hasproperty, UA_QUALIFIEDNAME(0, name),
2944
0
                         propertytype, &attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
2945
0
                         NULL, &inputArgsId);
2946
0
        if(retval != UA_STATUSCODE_GOOD)
2947
0
            goto error;
2948
0
    }
2949
2950
    /* Add the Output Arguments VariableNode */
2951
719k
    if(outputArgumentsSize > 0 && UA_NodeId_isNull(&outputArgsId)) {
2952
0
        UA_VariableAttributes attr = UA_VariableAttributes_default;
2953
0
        char *name = "OutputArguments";
2954
0
        attr.displayName = UA_LOCALIZEDTEXT("", name);
2955
0
        attr.dataType = UA_TYPES[UA_TYPES_ARGUMENT].typeId;
2956
0
        attr.valueRank = UA_VALUERANK_ONE_DIMENSION;
2957
0
        UA_UInt32 outputArgsSize32 = (UA_UInt32)outputArgumentsSize;
2958
0
        attr.arrayDimensions = &outputArgsSize32;
2959
0
        attr.arrayDimensionsSize = 1;
2960
0
        UA_Variant_setArray(&attr.value, (void *)(uintptr_t)outputArguments,
2961
0
                            outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
2962
0
        retval = addNode(server, UA_NODECLASS_VARIABLE, outputArgumentsRequestedNewNodeId,
2963
0
                         nodeId, hasproperty, UA_QUALIFIEDNAME(0, name),
2964
0
                         propertytype, &attr, &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES],
2965
0
                         NULL, &outputArgsId);
2966
0
        if(retval != UA_STATUSCODE_GOOD)
2967
0
            goto error;
2968
0
    }
2969
2970
719k
    retval = setMethodNode_callback(server, nodeId, method);
2971
719k
    if(retval != UA_STATUSCODE_GOOD)
2972
0
        goto error;
2973
2974
    /* Call finish to add the parent reference */
2975
719k
    retval = addNode_finish(server, &server->adminSession, &nodeId);
2976
719k
    if(retval != UA_STATUSCODE_GOOD)
2977
0
        goto error;
2978
2979
719k
    if(inputArgumentsOutNewNodeId != NULL) {
2980
0
        UA_NodeId_copy(&inputArgsId, inputArgumentsOutNewNodeId);
2981
0
    }
2982
719k
    if(outputArgumentsOutNewNodeId != NULL) {
2983
0
        UA_NodeId_copy(&outputArgsId, outputArgumentsOutNewNodeId);
2984
0
    }
2985
719k
    UA_BrowseResult_clear(&br);
2986
719k
    return retval;
2987
2988
0
error:
2989
0
    deleteNode(server, nodeId, true);
2990
0
    deleteNode(server, inputArgsId, true);
2991
0
    deleteNode(server, outputArgsId, true);
2992
0
    UA_BrowseResult_clear(&br);
2993
0
    return retval;
2994
719k
}
2995
2996
UA_StatusCode
2997
UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
2998
                               UA_MethodCallback method,
2999
                               size_t inputArgumentsSize,
3000
                               const UA_Argument* inputArguments,
3001
                               size_t outputArgumentsSize,
3002
719k
                               const UA_Argument* outputArguments) {
3003
719k
    lockServer(server);
3004
719k
    UA_StatusCode retval =
3005
719k
        UA_Server_addMethodNodeEx_finish(server, nodeId, method,
3006
719k
                                         inputArgumentsSize, inputArguments,
3007
719k
                                         UA_NODEID_NULL, NULL,
3008
719k
                                         outputArgumentsSize, outputArguments,
3009
719k
                                         UA_NODEID_NULL, NULL);
3010
719k
    unlockServer(server);
3011
719k
    return retval;
3012
719k
}
3013
3014
UA_StatusCode
3015
addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
3016
              const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
3017
              const UA_QualifiedName browseName,
3018
              const UA_MethodAttributes *attr, UA_MethodCallback method,
3019
              size_t inputArgumentsSize, const UA_Argument *inputArguments,
3020
              const UA_NodeId inputArgumentsRequestedNewNodeId,
3021
              UA_NodeId *inputArgumentsOutNewNodeId,
3022
              size_t outputArgumentsSize, const UA_Argument *outputArguments,
3023
              const UA_NodeId outputArgumentsRequestedNewNodeId,
3024
              UA_NodeId *outputArgumentsOutNewNodeId,
3025
0
              void *nodeContext, UA_NodeId *outNewNodeId) {
3026
0
    UA_AddNodesItem item;
3027
0
    UA_AddNodesItem_init(&item);
3028
0
    item.nodeClass = UA_NODECLASS_METHOD;
3029
0
    item.requestedNewNodeId.nodeId = requestedNewNodeId;
3030
0
    item.browseName = browseName;
3031
0
    UA_ExtensionObject_setValueNoDelete(&item.nodeAttributes, (void*)(uintptr_t)attr,
3032
0
                                        &UA_TYPES[UA_TYPES_METHODATTRIBUTES]);
3033
0
    UA_NodeId newId;
3034
0
    if(!outNewNodeId) {
3035
0
        UA_NodeId_init(&newId);
3036
0
        outNewNodeId = &newId;
3037
0
    }
3038
0
    UA_StatusCode retval = Operation_addNode_begin(server, &server->adminSession,
3039
0
                                                   nodeContext, &item, &parentNodeId,
3040
0
                                                   &referenceTypeId, outNewNodeId);
3041
0
    if(retval != UA_STATUSCODE_GOOD)
3042
0
        return retval;
3043
3044
0
    retval = UA_Server_addMethodNodeEx_finish(server, *outNewNodeId, method,
3045
0
                                              inputArgumentsSize, inputArguments,
3046
0
                                              inputArgumentsRequestedNewNodeId,
3047
0
                                              inputArgumentsOutNewNodeId,
3048
0
                                              outputArgumentsSize, outputArguments,
3049
0
                                              outputArgumentsRequestedNewNodeId,
3050
0
                                              outputArgumentsOutNewNodeId);
3051
0
    if(outNewNodeId == &newId)
3052
0
        UA_NodeId_clear(&newId);
3053
0
    return retval;
3054
0
}
3055
3056
UA_StatusCode
3057
UA_Server_addMethodNodeEx(UA_Server *server, const UA_NodeId requestedNewNodeId,
3058
                          const UA_NodeId parentNodeId,
3059
                          const UA_NodeId referenceTypeId,
3060
                          const UA_QualifiedName browseName,
3061
                          const UA_MethodAttributes attr, UA_MethodCallback method,
3062
                          size_t inputArgumentsSize, const UA_Argument *inputArguments,
3063
                          const UA_NodeId inputArgumentsRequestedNewNodeId,
3064
                          UA_NodeId *inputArgumentsOutNewNodeId,
3065
                          size_t outputArgumentsSize, const UA_Argument *outputArguments,
3066
                          const UA_NodeId outputArgumentsRequestedNewNodeId,
3067
                          UA_NodeId *outputArgumentsOutNewNodeId,
3068
0
                          void *nodeContext, UA_NodeId *outNewNodeId) {
3069
0
    lockServer(server);
3070
0
    UA_StatusCode res = addMethodNode(server, requestedNewNodeId,
3071
0
                                      parentNodeId, referenceTypeId,
3072
0
                                      browseName, &attr, method,
3073
0
                                      inputArgumentsSize, inputArguments,
3074
0
                                      inputArgumentsRequestedNewNodeId,
3075
0
                                      inputArgumentsOutNewNodeId,
3076
0
                                      outputArgumentsSize,
3077
0
                                      outputArguments,
3078
0
                                      outputArgumentsRequestedNewNodeId,
3079
0
                                      outputArgumentsOutNewNodeId,
3080
0
                                      nodeContext, outNewNodeId);
3081
0
    unlockServer(server);
3082
0
    return res;
3083
0
}
3084
3085
UA_StatusCode
3086
UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
3087
                        const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
3088
                        const UA_QualifiedName browseName, const UA_MethodAttributes attr,
3089
                        UA_MethodCallback method,
3090
                        size_t inputArgumentsSize, const UA_Argument *inputArguments,
3091
                        size_t outputArgumentsSize, const UA_Argument *outputArguments,
3092
0
                        void *nodeContext, UA_NodeId *outNewNodeId) {
3093
0
    return UA_Server_addMethodNodeEx(server, requestedNewNodeId,  parentNodeId,
3094
0
                                     referenceTypeId, browseName, attr, method,
3095
0
                                     inputArgumentsSize, inputArguments,
3096
0
                                     UA_NODEID_NULL, NULL,
3097
0
                                     outputArgumentsSize, outputArguments,
3098
0
                                     UA_NODEID_NULL, NULL,
3099
0
                                     nodeContext, outNewNodeId);
3100
0
}
3101
3102
static UA_StatusCode
3103
editMethodCallback(UA_Server *server, UA_Session* session,
3104
1.10M
                   UA_Node *node, UA_MethodCallback methodCallback) {
3105
1.10M
    if(node->head.nodeClass != UA_NODECLASS_METHOD)
3106
0
        return UA_STATUSCODE_BADNODECLASSINVALID;
3107
1.10M
    node->methodNode.method = methodCallback;
3108
1.10M
    return UA_STATUSCODE_GOOD;
3109
1.10M
}
3110
3111
UA_StatusCode
3112
setMethodNode_callback(UA_Server *server,
3113
                       const UA_NodeId methodNodeId,
3114
1.10M
                       UA_MethodCallback methodCallback) {
3115
1.10M
    UA_LOCK_ASSERT(&server->serviceMutex);
3116
1.10M
    return editNode(server, &server->adminSession, &methodNodeId,
3117
1.10M
                    0, UA_REFERENCETYPESET_NONE, UA_BROWSEDIRECTION_INVALID,
3118
1.10M
                    (UA_EditNodeCallback)editMethodCallback,
3119
1.10M
                    (void*)(uintptr_t)methodCallback);
3120
1.10M
}
3121
3122
UA_StatusCode
3123
UA_Server_setMethodNodeCallback(UA_Server *server,
3124
                                const UA_NodeId methodNodeId,
3125
0
                                UA_MethodCallback methodCallback) {
3126
0
    lockServer(server);
3127
0
    UA_StatusCode retVal = setMethodNode_callback(server, methodNodeId, methodCallback);
3128
0
    unlockServer(server);
3129
0
    return retVal;
3130
0
}
3131
3132
UA_StatusCode
3133
UA_Server_getMethodNodeCallback(UA_Server *server,
3134
                                const UA_NodeId methodNodeId,
3135
0
                                UA_MethodCallback *outMethodCallback) {
3136
0
    lockServer(server);
3137
0
    const UA_Node *node = UA_NODESTORE_GET(server, &methodNodeId);
3138
0
    if(!node) {
3139
0
        unlockServer(server);
3140
0
        return UA_STATUSCODE_BADNODEIDUNKNOWN;
3141
0
    }
3142
3143
0
    if(node->head.nodeClass != UA_NODECLASS_METHOD) {
3144
0
        UA_NODESTORE_RELEASE(server, node);
3145
0
        unlockServer(server);
3146
0
        return UA_STATUSCODE_BADNODECLASSINVALID;
3147
0
    }
3148
3149
0
    *outMethodCallback = node->methodNode.method;
3150
0
    UA_NODESTORE_RELEASE(server, node);
3151
0
    unlockServer(server);
3152
0
    return UA_STATUSCODE_GOOD;
3153
0
}
3154
3155
#endif
3156
3157
/************************/
3158
/* Lifecycle Management */
3159
/************************/
3160
3161
void UA_EXPORT
3162
UA_Server_setAdminSessionContext(UA_Server *server,
3163
0
                                 void *context) {
3164
0
    server->adminSession.context = context;
3165
0
}
3166
3167
static UA_StatusCode
3168
setNodeTypeLifecycleCallback(UA_Server *server, UA_Session *session,
3169
136k
                             UA_Node *node, UA_NodeTypeLifecycle *lifecycle) {
3170
136k
    if(node->head.nodeClass == UA_NODECLASS_OBJECTTYPE) {
3171
136k
        node->objectTypeNode.lifecycle = *lifecycle;
3172
136k
    } else if(node->head.nodeClass == UA_NODECLASS_VARIABLETYPE) {
3173
0
        node->variableTypeNode.lifecycle = *lifecycle;
3174
0
    } else {
3175
0
        return UA_STATUSCODE_BADNODECLASSINVALID;
3176
0
    }
3177
136k
    return UA_STATUSCODE_GOOD;
3178
136k
}
3179
3180
UA_StatusCode
3181
setNodeTypeLifecycle(UA_Server *server, UA_NodeId nodeId,
3182
136k
                     UA_NodeTypeLifecycle lifecycle) {
3183
136k
    return editNode(server, &server->adminSession, &nodeId, 0,
3184
136k
                    UA_REFERENCETYPESET_NONE, UA_BROWSEDIRECTION_INVALID,
3185
136k
                    (UA_EditNodeCallback)setNodeTypeLifecycleCallback,
3186
136k
                    &lifecycle);
3187
136k
}
3188
3189
UA_StatusCode
3190
UA_Server_setNodeTypeLifecycle(UA_Server *server, UA_NodeId nodeId,
3191
0
                               UA_NodeTypeLifecycle lifecycle) {
3192
0
    lockServer(server);
3193
0
    UA_StatusCode retval = setNodeTypeLifecycle(server, nodeId, lifecycle);
3194
0
    unlockServer(server);
3195
0
    return retval;
3196
0
}