Coverage Report

Created: 2026-10-15 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open62541_15/src/server/ua_subscription_event.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 2018 (c) Ari Breitkreuz, fortiss GmbH
6
 *    Copyright 2020 (c) Christian von Arnim, ISW University of Stuttgart (for VDW and umati)
7
 *    Copyright 2021 (c) Fraunhofer IOSB (Author: Andreas Ebner)
8
 *    Copyright 2022, 2025 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
9
 */
10
11
#include <open62541/types.h>
12
#include "ua_server_internal.h"
13
#include "ua_subscription.h"
14
15
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
16
17
static UA_StatusCode
18
readSAOfromEventInstance(UA_FilterEvalContext *ctx,
19
                         const UA_SimpleAttributeOperand *sao,
20
0
                         UA_Variant *value) {
21
    /* EventInstance in the information model defined? */
22
0
    const UA_NodeId *ei = ctx->ed.eventInstance;
23
0
    if(!ei)
24
0
        return UA_STATUSCODE_BADINVALIDARGUMENT;
25
26
    /* Prepare the ReadValueId */
27
0
    UA_ReadValueId rvi;
28
0
    UA_ReadValueId_init(&rvi);
29
0
    rvi.indexRange = sao->indexRange;
30
0
    rvi.attributeId = sao->attributeId;
31
32
    /* Read the value */
33
0
    UA_DataValue v;
34
0
    if(sao->browsePathSize == 0) {
35
        /* If this list (browsePath) is empty, the Node is the instance of the
36
         * TypeDefinition. (Part 4, 7.4.4.5) */
37
0
        rvi.nodeId = *ei;
38
39
        /* A Condition is an indirection. Look up the target node. */
40
        /* TODO: check for Branches! One Condition could have multiple Branches */
41
0
        UA_NodeId conditionTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_CONDITIONTYPE);
42
0
        if(UA_NodeId_equal(&sao->typeDefinitionId, &conditionTypeId)) {
43
#ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS
44
            UA_StatusCode res = UA_getConditionId(ctx->server, ctx->ed.eventInstance, &rvi.nodeId);
45
            UA_CHECK_STATUS(res, return res);
46
#else
47
0
            return UA_STATUSCODE_BADNOTSUPPORTED;
48
0
#endif
49
0
        }
50
51
0
        v = readWithSession(ctx->server, ctx->session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
52
0
    } else {
53
        /* Resolve the browse path, starting from the event-source (and not the
54
         * typeDefinitionId). */
55
0
        UA_BrowsePathResult bpr =
56
0
            browseSimplifiedBrowsePath(ctx->server, *ei, sao->browsePathSize, sao->browsePath);
57
0
        if(bpr.targetsSize == 0 && bpr.statusCode == UA_STATUSCODE_GOOD)
58
0
            bpr.statusCode = UA_STATUSCODE_BADNOTFOUND;
59
0
        if(bpr.statusCode != UA_STATUSCODE_GOOD) {
60
0
            UA_StatusCode res = bpr.statusCode;
61
0
            UA_BrowsePathResult_clear(&bpr);
62
0
            return res;
63
0
        }
64
65
        /* Use the first match */
66
0
        rvi.nodeId = bpr.targets[0].targetId.nodeId;
67
0
        v = readWithSession(ctx->server, ctx->session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
68
0
        UA_BrowsePathResult_clear(&bpr);
69
0
    }
70
71
    /* Validate the result */
72
0
    if(v.status != UA_STATUSCODE_GOOD) {
73
0
        UA_Variant_clear(&v.value);
74
0
        return v.status;
75
0
    }
76
0
    if(!v.hasValue)
77
0
        return UA_STATUSCODE_BADNODATAAVAILABLE;
78
79
    /* Move the result to the output */
80
0
    *value = v.value;
81
0
    return UA_STATUSCODE_GOOD;
82
0
}
83
84
/***************/
85
/* Filter Eval */
86
/***************/
87
88
/* Names of the mandatory event properties (in the BaseEventType) */
89
#define MANDATORY_EVENT_PROPERTIES_COUNT 8
90
static UA_String mandatoryEventProperties[MANDATORY_EVENT_PROPERTIES_COUNT] = {
91
    UA_STRING_STATIC("/EventId"),
92
    UA_STRING_STATIC("/EventType"),
93
    UA_STRING_STATIC("/SourceNode"),
94
    UA_STRING_STATIC("/SourceName"),
95
    UA_STRING_STATIC("/Time"),
96
    UA_STRING_STATIC("/ReceiveTime"),
97
    UA_STRING_STATIC("/Message"),
98
    UA_STRING_STATIC("/Severity")
99
};
100
101
void
102
0
UA_FilterEvalContext_init(UA_FilterEvalContext *ctx) {
103
0
    memset(ctx, 0, sizeof(UA_FilterEvalContext));
104
0
}
105
106
void
107
0
UA_FilterEvalContext_reset(UA_FilterEvalContext *ctx) {
108
0
    UA_KeyValueMap_clear(&ctx->fieldCache);
109
110
    /* The data of the EventId is either in the eventIdBuf or in the fieldCache
111
     * or in the ed. So no memory to free here. Keep the EventId if it points to
112
     * the random-id bytes for the next evaluation. */
113
0
    if(ctx->eventId.data != ctx->eventIdBuf)
114
0
        UA_ByteString_init(&ctx->eventId);
115
0
}
116
117
UA_StatusCode
118
0
cacheEventId(UA_FilterEvalContext *ctx) {
119
    /* Already cached */
120
0
    if(ctx->eventId.length > 0)
121
0
        return UA_STATUSCODE_GOOD;
122
123
    /* Resolve from the Key-Value Map - This does not make a deep copy */
124
0
    const UA_EventDescription *ed = &ctx->ed;
125
0
    if(ed && ed->eventFields) {
126
0
        const UA_Variant *found =
127
0
            UA_KeyValueMap_get(ed->eventFields, UA_QUALIFIEDNAME(0, "/EventId"));
128
0
        if(found) {
129
0
            if(!UA_Variant_hasScalarType(found, &UA_TYPES[UA_TYPES_BYTESTRING]))
130
0
                return UA_STATUSCODE_BADTYPEMISMATCH;
131
0
            ctx->eventId = *(UA_ByteString*)found->data; /* shallow copy */
132
0
            return UA_STATUSCODE_GOOD;
133
0
        }
134
0
    }
135
136
    /* Resolve from the EventInstance. But only if a useful identifier
137
     * (ByteString, length > 0) has been set in the information model. */
138
0
    static UA_QualifiedName eventIdName = {0, UA_STRING_STATIC("EventId")};
139
0
    UA_SimpleAttributeOperand sao;
140
0
    UA_SimpleAttributeOperand_init(&sao);
141
0
    sao.attributeId = UA_ATTRIBUTEID_VALUE;
142
0
    sao.browsePath = &eventIdName;
143
0
    sao.browsePathSize = 1;
144
145
0
    UA_Variant val;
146
0
    UA_Variant_init(&val);
147
0
    UA_StatusCode res = readSAOfromEventInstance(ctx, &sao, &val);
148
0
    if(res == UA_STATUSCODE_GOOD) {
149
0
        UA_Boolean isIdString = UA_Variant_hasScalarType(&val, &UA_TYPES[UA_TYPES_BYTESTRING]);
150
0
        UA_assert(!isIdString || val.data != NULL); /* pacify a clang-analyzer warning */
151
0
        if(isIdString && ((UA_ByteString*)val.data)->length > 0) {
152
0
            res = UA_KeyValueMap_set(&ctx->fieldCache,
153
0
                                     UA_QUALIFIEDNAME(0, "/EventId"), &val);
154
0
            UA_Variant_clear(&val);
155
0
            if(res != UA_STATUSCODE_GOOD)
156
0
                return res;
157
0
            const UA_Variant *cached =
158
0
                UA_KeyValueMap_get(&ctx->fieldCache,
159
0
                                   UA_QUALIFIEDNAME(0, "/EventId"));
160
0
            ctx->eventId = *(UA_ByteString*)cached->data; /* shallow copy */
161
0
            return UA_STATUSCODE_GOOD;
162
0
        }
163
164
        /* Continue to create a random ByteString */
165
0
        UA_Variant_clear(&val);
166
0
    }
167
168
    /* If nothing is explicitly defined, create a random 16-byte ByteString */
169
0
    UA_UInt32 *ids = (UA_UInt32*)ctx->eventIdBuf;
170
0
    ids[0] = UA_UInt32_random();
171
0
    ids[1] = UA_UInt32_random();
172
0
    ids[2] = UA_UInt32_random();
173
0
    ids[3] = UA_UInt32_random();
174
0
    ctx->eventId = (UA_ByteString){16, ctx->eventIdBuf};
175
0
    return UA_STATUSCODE_GOOD;
176
0
}
177
178
/* The SAO string is not always in the "normal form" that is the output of
179
 * UA_SimpleAttributeOperand_print. For example, we can have an implicit #Value
180
 * postfix or the path-browsenames prefixed with 0: for ns0. Also, for the
181
 * map-keys as QualifiedNames, we use the key-nsindex as the default nsindex for
182
 * the path.
183
 *
184
 * In order to compare with a non-normal-form SAO string, we decode and compare
185
 * the SAO directly. This is slower, so we try the normal form string-compare
186
 * first. */
187
static UA_Variant *
188
getEventFieldNonNormalForm(UA_Server *server, const UA_KeyValueMap *eventFields,
189
0
                           const UA_SimpleAttributeOperand *sao) {
190
0
    if(!eventFields)
191
0
        return NULL;
192
193
0
    UA_SimpleAttributeOperand keySao;
194
0
    for(size_t i = 0; i < eventFields->mapSize; i++) {
195
0
        UA_KeyValuePair *kvp = &eventFields->map[i];
196
0
        UA_StatusCode res =
197
0
            sao_parseWithDefaultNsIdx(&keySao, kvp->key.name,
198
0
                                      kvp->key.namespaceIndex);
199
0
        if(res != UA_STATUSCODE_GOOD)
200
0
            continue;
201
0
        UA_Boolean found = UA_SimpleAttributeOperand_equal(sao, &keySao);
202
0
        UA_SimpleAttributeOperand_clear(&keySao);
203
0
        if(found)
204
0
            return &kvp->value;
205
0
    }
206
0
    return NULL;
207
0
}
208
209
/* Can return an in-situ value. Check for UA_VARIANT_DATA_NODELETE. */
210
UA_StatusCode
211
resolveSAO(UA_FilterEvalContext *ctx, const UA_SimpleAttributeOperand *sao,
212
0
           UA_Variant *out) {
213
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
214
0
    const UA_EventDescription *ed = &ctx->ed;
215
0
    const UA_Variant *found;
216
0
    UA_Byte pathBuf[512];
217
0
    UA_QualifiedName pathString = {0, {512, pathBuf}};
218
0
    static UA_NodeId baseEventTypeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEEVENTTYPE}};
219
220
    /* Copy into a mutable SAO */
221
0
    UA_SimpleAttributeOperand tmp_sao = *sao;
222
223
    /* Do the lookup without the index-range.
224
     * The index-range from the original sao is used later. */
225
0
    tmp_sao.indexRange = UA_STRING_NULL;
226
227
    /* Initially use the BaseEventTypeId if not defined explicitly.
228
     * If this does not resolve, we try again with i=0 (goto search again). */
229
0
    if(UA_NodeId_isNull(&tmp_sao.typeDefinitionId))
230
0
        tmp_sao.typeDefinitionId = baseEventTypeId;
231
232
0
 search_again:
233
0
    res = UA_SimpleAttributeOperand_print(&tmp_sao, &pathString.name);
234
0
    if(res != UA_STATUSCODE_GOOD)
235
0
        return res;
236
237
    /* Special Case: EventId (generated only once per Event, ignores the IndexRange) */
238
0
    if(UA_String_equal(&pathString.name, &mandatoryEventProperties[0])) {
239
0
        res = cacheEventId(ctx);
240
0
        if(res != UA_STATUSCODE_GOOD)
241
0
            return res;
242
0
        UA_Variant_setScalar(out, &ctx->eventId, &UA_TYPES[UA_TYPES_BYTESTRING]);
243
0
        out->storageType = UA_VARIANT_DATA_NODELETE;
244
0
        return UA_STATUSCODE_GOOD;
245
0
    }
246
247
    /* Source 1: Use the SAO-string to look up from the user-defined key-value
248
     * map (and from the cache) */
249
0
    found = UA_KeyValueMap_get(ed->eventFields, pathString);
250
0
    if(!found)
251
0
        found = UA_KeyValueMap_get(&ctx->fieldCache, pathString);
252
0
    if(!found)
253
0
        found = getEventFieldNonNormalForm(ctx->server, ed->eventFields, &tmp_sao);
254
0
    if(found) {
255
0
        if(sao->indexRange.length == 0) {
256
0
            *out = *found;
257
0
            out->storageType = UA_VARIANT_DATA_NODELETE;
258
0
        } else {
259
0
            UA_NumericRange range;
260
0
            res = UA_NumericRange_parse(&range, sao->indexRange);
261
0
            if(res != UA_STATUSCODE_GOOD)
262
0
               return res;
263
0
            res = UA_Variant_copyRange(found, out, range);
264
0
            UA_free(range.dimensions);
265
0
        }
266
0
        return res;
267
0
    }
268
269
    /* Not found. Print the SAO again with BaseEventTypeId as the
270
     * TypeDefinitionId and try again. This removes any TypeDefinitionId prefix
271
     * from the printed SAO-string. */
272
0
    if(!UA_NodeId_equal(&tmp_sao.typeDefinitionId, &baseEventTypeId)) {
273
0
        tmp_sao.typeDefinitionId = baseEventTypeId;
274
0
        goto search_again;
275
0
    }
276
277
    /* Source 2: Read from the information model */
278
0
    res = readSAOfromEventInstance(ctx, sao, out);
279
0
    if(res == UA_STATUSCODE_GOOD)
280
0
        return UA_STATUSCODE_GOOD;
281
282
    /* Source 3: Use a default for the mandatory fields of the BaseEventType.
283
     * Here we ignore the IndexRange. */
284
0
    UA_String_init(&tmp_sao.indexRange); /* No index range allowed */
285
0
    if(UA_String_equal(&pathString.name, &mandatoryEventProperties[1])) {
286
        /* EventType */
287
0
        return UA_Variant_setScalarCopy(out, &ed->eventType, &UA_TYPES[UA_TYPES_NODEID]);
288
0
    } else if(UA_String_equal(&pathString.name, &mandatoryEventProperties[2])) {
289
        /* SourceNode */
290
0
        return UA_Variant_setScalarCopy(out, &ed->sourceNode, &UA_TYPES[UA_TYPES_NODEID]);
291
0
    } else if(UA_String_equal(&pathString.name, &mandatoryEventProperties[3])) {
292
        /* SourceName. Read the DisplayName from the information model. This
293
         * uses the locale of the session. */
294
0
        UA_ReadValueId rvi;
295
0
        UA_ReadValueId_init(&rvi);
296
0
        rvi.nodeId = ed->sourceNode;
297
0
        rvi.attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
298
0
        UA_DataValue dv = readWithSession(ctx->server, ctx->session, &rvi,
299
0
                                          UA_TIMESTAMPSTORETURN_NEITHER);
300
0
        if(dv.status != UA_STATUSCODE_GOOD) {
301
0
            res = dv.status;
302
0
            UA_DataValue_clear(&dv);
303
0
            return res;
304
0
        }
305
0
        if(dv.value.type != &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]) {
306
0
            UA_DataValue_clear(&dv);
307
0
            return UA_STATUSCODE_BADINTERNALERROR;
308
0
        }
309
0
        UA_LocalizedText *displayName = (UA_LocalizedText*)dv.value.data;
310
0
        res = UA_Variant_setScalarCopy(out, &displayName->text, &UA_TYPES[UA_TYPES_STRING]);
311
0
        UA_DataValue_clear(&dv);
312
0
        return res;
313
0
    } else if(UA_String_equal(&pathString.name, &mandatoryEventProperties[4]) ||
314
0
              UA_String_equal(&pathString.name, &mandatoryEventProperties[5])) {
315
        /* Time / ReceiveTime */
316
0
        UA_EventLoop *el = ctx->server->config.eventLoop;
317
0
        UA_DateTime rcvTime = el->dateTime_now(el);
318
0
        return UA_Variant_setScalarCopy(out, &rcvTime, &UA_TYPES[UA_TYPES_DATETIME]);
319
0
    } else if(UA_String_equal(&pathString.name, &mandatoryEventProperties[6])) {
320
        /* Message */
321
0
        return UA_Variant_setScalarCopy(out, &ed->message, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
322
0
    } else if(UA_String_equal(&pathString.name, &mandatoryEventProperties[7])) {
323
        /* Severity */
324
0
        return UA_Variant_setScalarCopy(out, &ed->severity, &UA_TYPES[UA_TYPES_UINT16]);
325
0
    }
326
327
    /* Not found, return an empty Variant */
328
0
    return UA_STATUSCODE_GOOD;
329
0
}
330
331
/***************************/
332
/* Where-Clause Evaluation */
333
/***************************/
334
335
/* Ternary Logic
336
 * -------------
337
 * Similar to SQL, OPC UA Queries use the K3 - Strong Kleene Logic that
338
 * considers ternary values true/false/null (unknown). Most operators resolve to
339
 * a ternary value. Some operators can resolve to a literal value (e.g. CAST). */
340
341
typedef enum {
342
    UA_TERNARY_FALSE = -1,
343
    UA_TERNARY_NULL = 0,
344
    UA_TERNARY_TRUE = 1
345
} UA_Ternary;
346
347
static UA_Ternary
348
0
UA_Ternary_and(UA_Ternary first, UA_Ternary second) {
349
0
    if(first == UA_TERNARY_FALSE || second == UA_TERNARY_FALSE)
350
0
        return UA_TERNARY_FALSE;
351
0
    if(first == UA_TERNARY_NULL || second == UA_TERNARY_NULL)
352
0
        return UA_TERNARY_NULL;
353
0
    return UA_TERNARY_TRUE;
354
0
}
355
356
static UA_Ternary
357
0
UA_Ternary_or(UA_Ternary first, UA_Ternary second) {
358
0
    if(first == UA_TERNARY_TRUE || second == UA_TERNARY_TRUE)
359
0
        return UA_TERNARY_TRUE;
360
0
    if(first == UA_TERNARY_NULL || second == UA_TERNARY_NULL)
361
0
        return UA_TERNARY_NULL;
362
0
    return UA_TERNARY_FALSE;
363
0
}
364
365
/* Part 4: The NOT operator always evaluates to NULL if applied to a NULL
366
 * operand. We simply swap true/false. */
367
static UA_Ternary
368
0
UA_Ternary_not(UA_Ternary v) {
369
0
    return (UA_Ternary)((int)v * -1);
370
0
}
371
372
0
static UA_Ternary v2t(const UA_Variant *v) {
373
0
    if(UA_Variant_isEmpty(v) || !UA_Variant_hasScalarType(v, &UA_TYPES[UA_TYPES_BOOLEAN]))
374
0
        return UA_TERNARY_NULL;
375
0
    UA_Boolean b = *(UA_Boolean*)v->data;
376
0
    return (b) ? UA_TERNARY_TRUE : UA_TERNARY_FALSE;
377
0
}
378
379
static const UA_Boolean bFalse = false;
380
static const UA_Boolean bTrue  = true;
381
382
0
static UA_Variant t2v(UA_Ternary t) {
383
0
    UA_Variant v;
384
0
    UA_Variant_init(&v);
385
0
    switch(t) {
386
0
    case UA_TERNARY_FALSE:
387
0
        UA_Variant_setScalar(&v, (void*)(uintptr_t)&bFalse, &UA_TYPES[UA_TYPES_BOOLEAN]);
388
0
        break;
389
0
    case UA_TERNARY_TRUE:
390
0
        UA_Variant_setScalar(&v, (void*)(uintptr_t)&bTrue, &UA_TYPES[UA_TYPES_BOOLEAN]);
391
0
        break;
392
0
    default:
393
0
        return v;
394
0
    }
395
0
    v.storageType = UA_VARIANT_DATA_NODELETE;
396
0
    return v;
397
0
}
398
399
/* Type Casting Rules
400
 * ------------------
401
 * For comparison operations values from different datatypes can be implicitly
402
 * or explicitly cast. The standard defines rules to selected the target type
403
 * for casting and when implicit casts are possible. */
404
405
/* Defined in Part 4 Table 123 "Data Precedence Rules". Implicit casting is
406
 * always to the type of lower precedence value. */
407
static UA_Byte typePrecedence[UA_DATATYPEKINDS] = {
408
    12, 10, 11, 8, 9, 5, 6, 3, 4, 2, 1, 14, 255, 13, 255, 255, 16,
409
    15, 7, 18, 17, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
410
};
411
412
/* Type conversion table from the standard.
413
 * 0 -> Same Type, 1 -> Implicit Cast, 2 -> Only explicit Cast, -1 -> cast invalid */
414
static UA_SByte convertLookup[21][21] = {
415
    { 0, 1,-1,-1, 1,-1, 1,-1, 1, 1, 1,-1, 1,-1, 2,-1,-1, 1, 1, 1,-1},
416
    { 2, 0,-1,-1, 1,-1, 1,-1, 1, 1, 1,-1, 1,-1, 2,-1,-1, 1, 1, 1,-1},
417
    {-1,-1, 0,-1,-1,-1,-1, 2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
418
    {-1,-1,-1, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 2,-1,-1,-1,-1,-1,-1},
419
    { 2, 2,-1,-1, 0,-1, 2,-1, 2, 2, 2,-1, 2,-1, 2,-1,-1, 2, 2, 2,-1},
420
    {-1,-1,-1,-1,-1, 0,-1,-1,-1,-1,-1, 2,-1,-1, 1,-1,-1,-1,-1,-1,-1},
421
    { 2, 2,-1,-1, 1,-1, 0,-1, 2, 2, 2,-1, 2,-1, 2,-1,-1, 2, 2, 2,-1},
422
    {-1,-1, 2,-1,-1,-1,-1, 0,-1,-1,-1,-1,-1,-1, 2,-1,-1,-1,-1,-1,-1},
423
    { 2, 2,-1,-1, 1,-1, 1,-1, 0, 1, 1,-1, 2,-1, 2,-1,-1, 2, 1, 1,-1},
424
    { 2, 2,-1,-1, 1,-1, 1,-1, 2, 0, 1,-1, 2, 2, 2,-1,-1, 2, 2, 1,-1},
425
    { 2, 2,-1,-1, 1,-1, 1,-1, 2, 2, 0,-1, 2, 2, 2,-1,-1, 2, 2, 2,-1},
426
    {-1,-1,-1,-1,-1, 1,-1,-1,-1,-1,-1, 0,-1,-1, 1,-1,-1,-1,-1,-1,-1},
427
    { 2, 2,-1,-1, 1,-1, 1,-1, 1, 1, 1,-1, 0,-1, 2,-1,-1, 1, 1, 1,-1},
428
    {-1,-1,-1,-1,-1,-1,-1,-1,-1, 1, 1,-1,-1, 0,-1,-1,-1, 2, 1, 1,-1},
429
    { 1, 1,-1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1,-1, 0, 2, 2, 1, 1, 1,-1},
430
    {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1, 0,-1,-1,-1,-1,-1},
431
    {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 1, 1, 0,-1,-1,-1,-1},
432
    { 2, 2,-1,-1, 1,-1, 1,-1, 1, 1, 1,-1, 2, 1, 2,-1,-1, 0, 1, 1,-1},
433
    { 2, 2,-1,-1, 1,-1, 1,-1, 2, 1, 1,-1, 2, 2, 2,-1,-1, 2, 0, 1,-1},
434
    { 2, 2,-1,-1, 1,-1, 1,-1, 2, 2, 1,-1, 2, 2, 2,-1,-1, 2, 2, 0,-1},
435
    {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0}
436
};
437
438
/* This array maps the index of the DataType-Kind to the index of the type
439
 * conversion matrix (255 -> not contained in the matrix) */
440
static UA_Byte convertLookupIndex[UA_DATATYPEKINDS] = {
441
    0,  12,  1,  8, 17,  9, 18, 10, 19,  6, 4,  14,  3,  7,  2,
442
    20, 11,  5, 13, 16, 15,255,255,255,255,255,255,255,255,255,255
443
};
444
445
/* Returns the target type for implicit cassting or NULL if no implicit casting
446
 * is possible */
447
static const UA_DataType *
448
0
implicitCastTargetType(const UA_DataType *t1, const UA_DataType *t2) {
449
0
    if(!t1 || t1 == t2)
450
0
        return t1;
451
452
    /* Get the type precedence. Return if no implicit casting is possible. */
453
0
    UA_Byte p1 = typePrecedence[t1->typeKind];
454
0
    UA_Byte p2 = typePrecedence[t2->typeKind];
455
0
    if(p1 == UA_BYTE_MAX || p2 == UA_BYTE_MAX)
456
0
        return NULL;
457
458
    /* Which is the target type based on the precedence rules? */
459
0
    const UA_DataType *targetType = (p1 < p2) ? t1 : t2;
460
0
    const UA_DataType *sourceType = (p1 < p2) ? t2 : t1;
461
462
    /* Lookup the casting rule */
463
0
    UA_Byte sourceIndex = convertLookupIndex[sourceType->typeKind];
464
0
    UA_Byte targetIndex = convertLookupIndex[targetType->typeKind];
465
0
    if(sourceIndex == UA_BYTE_MAX || targetIndex == UA_BYTE_MAX)
466
0
        return NULL;
467
0
    UA_SByte castingRule = convertLookup[sourceIndex][targetIndex];
468
469
    /* Is implicit casting allowed? */
470
0
    if(castingRule != 0 && castingRule != 1)
471
0
        return NULL;
472
0
    return targetType;
473
0
}
474
475
/* Cast Numerical
476
 * --------------
477
 * To reduce the number of cases we first "normalize" to either of
478
 * Int64/UInt64/Double. Then cast from there to the target type. This works for
479
 * all numericals (not only the implicit conversion).
480
 *
481
 * Numerical conversion rules from the standard:
482
 *
483
 * - If the conversion fails the result is a null value.
484
 * - Floating point values are rounded by adding 0.5 and truncating when they
485
 *   are converted to integer values.
486
 * - Converting a value that is outside the range of the target type causes a
487
 *   conversion error. */
488
489
#define UA_CAST_SIGNED(t, T)                                           \
490
0
    if(i < (UA_Int64)T##_MIN || i > (UA_Int64)T##_MAX) {               \
491
0
        UA_free(data);                                                 \
492
0
        return;                                                        \
493
0
    }                                                                  \
494
0
    *(t*)data = (t)i;                                                  \
495
0
    do { } while(0)
496
497
#define UA_CAST_UNSIGNED(t, T)                                         \
498
0
    if(u > T##_MAX) {                                                  \
499
0
        UA_free(data);                                                 \
500
0
        return;                                                        \
501
0
    }                                                                  \
502
0
    *(t*)data = (t)u;                                                  \
503
0
    do { } while(0)
504
505
#define UA_CAST_FLOAT(t, T)                                            \
506
0
    if(f + 0.5 < (UA_Double)T##_MIN || f + 0.5 > (UA_Double)T##_MAX) { \
507
0
        UA_free(data);                                                 \
508
0
        return;                                                        \
509
0
    }                                                                  \
510
0
    *(t*)data = (t)(f + 0.5);                                          \
511
0
    do { } while(0)
512
513
/* We can cast between any numerical type. So this can be reused for explicit casting. */
514
static void
515
0
castNumerical(const UA_Variant *in, const UA_DataType *type, UA_Variant *out) {
516
0
    UA_assert(UA_Variant_isScalar(in));
517
0
    UA_Variant_init(out); /* Set to null value */
518
519
0
    UA_Int64  i = 0;
520
0
    UA_UInt64 u = 0;
521
0
    UA_Double f = 0.0;
522
523
0
    const UA_DataTypeKind ink = (UA_DataTypeKind)in->type->typeKind;
524
0
    switch(ink) {
525
0
    case UA_DATATYPEKIND_SBYTE:  i = *(UA_SByte*)in->data; break;
526
0
    case UA_DATATYPEKIND_INT16:  i = *(UA_Int16*)in->data; break;
527
0
    case UA_DATATYPEKIND_INT32:  i = *(UA_Int32*)in->data; break;
528
0
    case UA_DATATYPEKIND_INT64:  i = *(UA_Int64*)in->data; break;
529
0
    case UA_DATATYPEKIND_BYTE: /* or */
530
0
    case UA_DATATYPEKIND_BOOLEAN: u = *(UA_Byte*)in->data; break;
531
0
    case UA_DATATYPEKIND_UINT16:  u = *(UA_UInt16*)in->data; break;
532
0
    case UA_DATATYPEKIND_UINT32: /* or */
533
0
    case UA_DATATYPEKIND_STATUSCODE: u = *(UA_UInt32*)in->data; break;
534
0
    case UA_DATATYPEKIND_UINT64: u = *(UA_UInt64*)in->data; break;
535
0
    case UA_DATATYPEKIND_FLOAT:  f = *(UA_Float*)in->data; break;
536
0
    case UA_DATATYPEKIND_DOUBLE: f = *(UA_Double*)in->data; break;
537
0
    default: return;
538
0
    }
539
540
0
    void *data = UA_new(type);
541
0
    if(!data)
542
0
        return;
543
544
0
    if(ink == UA_DATATYPEKIND_SBYTE || ink == UA_DATATYPEKIND_INT16 ||
545
0
       ink == UA_DATATYPEKIND_INT32 || ink == UA_DATATYPEKIND_INT64) {
546
        /* Cast from signed */
547
0
        switch(type->typeKind) {
548
0
        case UA_DATATYPEKIND_SBYTE:  UA_CAST_SIGNED(UA_SByte, UA_SBYTE); break;
549
0
        case UA_DATATYPEKIND_INT16:  UA_CAST_SIGNED(UA_Int16, UA_INT16); break;
550
0
        case UA_DATATYPEKIND_INT32:  UA_CAST_SIGNED(UA_Int32, UA_INT32); break;
551
0
        case UA_DATATYPEKIND_INT64:  *(UA_Int64*)data = i; break;
552
0
        case UA_DATATYPEKIND_BYTE:   UA_CAST_SIGNED(UA_Byte, UA_BYTE); break;
553
0
        case UA_DATATYPEKIND_UINT16: UA_CAST_SIGNED(UA_UInt16, UA_UINT16); break;
554
0
        case UA_DATATYPEKIND_UINT32: UA_CAST_SIGNED(UA_UInt32, UA_UINT32); break;
555
0
        case UA_DATATYPEKIND_UINT64: UA_CAST_SIGNED(UA_UInt64, UA_UINT64); break;
556
0
        case UA_DATATYPEKIND_FLOAT:  *(UA_Float*)data = (UA_Float)i; break;
557
0
        case UA_DATATYPEKIND_DOUBLE: *(UA_Double*)data = (UA_Double)i; break;
558
0
        default:
559
0
            UA_free(data);
560
0
            return;
561
0
        }
562
0
    } else if(ink == UA_DATATYPEKIND_BYTE   || ink == UA_DATATYPEKIND_UINT16 ||
563
0
              ink == UA_DATATYPEKIND_UINT32 || ink == UA_DATATYPEKIND_UINT64) {
564
        /* Cast from unsigned */
565
0
        switch(type->typeKind) {
566
0
        case UA_DATATYPEKIND_SBYTE:  UA_CAST_UNSIGNED(UA_SByte, UA_SBYTE); break;
567
0
        case UA_DATATYPEKIND_INT16:  UA_CAST_UNSIGNED(UA_Int16, UA_INT16); break;
568
0
        case UA_DATATYPEKIND_INT32:  UA_CAST_UNSIGNED(UA_Int32, UA_INT32); break;
569
0
        case UA_DATATYPEKIND_INT64:  UA_CAST_UNSIGNED(UA_Int64, UA_INT64); break;
570
0
        case UA_DATATYPEKIND_BYTE:   UA_CAST_UNSIGNED(UA_Byte, UA_BYTE); break;
571
0
        case UA_DATATYPEKIND_UINT16: UA_CAST_UNSIGNED(UA_UInt16, UA_UINT16); break;
572
0
        case UA_DATATYPEKIND_UINT32: UA_CAST_UNSIGNED(UA_UInt32, UA_UINT32); break;
573
0
        case UA_DATATYPEKIND_UINT64: *(UA_UInt64*)data = u; break;
574
0
        case UA_DATATYPEKIND_FLOAT:  *(UA_Float*)data = (UA_Float)u; break;
575
0
        case UA_DATATYPEKIND_DOUBLE: *(UA_Double*)data = (UA_Double)u; break;
576
0
        default:
577
0
            UA_free(data);
578
0
            return;
579
0
        }
580
0
    } else {
581
        /* Cast from float */
582
0
        if(f != f) {
583
            /* NaN cannot be cast */
584
0
            UA_free(data);
585
0
            return;
586
0
        }
587
0
        switch(type->typeKind) {
588
0
        case UA_DATATYPEKIND_SBYTE:  UA_CAST_FLOAT(UA_SByte, UA_SBYTE); break;
589
0
        case UA_DATATYPEKIND_INT16:  UA_CAST_FLOAT(UA_Int16, UA_INT16); break;
590
0
        case UA_DATATYPEKIND_INT32:  UA_CAST_FLOAT(UA_Int32, UA_INT32); break;
591
0
        case UA_DATATYPEKIND_INT64:  UA_CAST_FLOAT(UA_Int64, UA_INT64); break;
592
0
        case UA_DATATYPEKIND_BYTE:   UA_CAST_FLOAT(UA_Byte, UA_BYTE); break;
593
0
        case UA_DATATYPEKIND_UINT16: UA_CAST_FLOAT(UA_UInt16, UA_UINT16); break;
594
0
        case UA_DATATYPEKIND_UINT32: UA_CAST_FLOAT(UA_UInt32, UA_UINT32); break;
595
0
        case UA_DATATYPEKIND_UINT64: UA_CAST_FLOAT(UA_UInt64, UA_UINT64); break;
596
0
        case UA_DATATYPEKIND_FLOAT:  *(UA_Float*)data = (UA_Float)f; break;
597
0
        case UA_DATATYPEKIND_DOUBLE: *(UA_Double*)data = (UA_Double)f; break;
598
0
        default:
599
0
            UA_free(data);
600
0
            return;
601
0
        }
602
0
    }
603
604
0
    UA_Variant_setScalar(out, data, type);
605
0
}
606
607
/* Implicit Casting
608
 * ---------------- */
609
610
0
static UA_INLINE UA_Byte uppercase(UA_Byte in) { return in | 32; }
611
612
static UA_StatusCode
613
0
castImplicitFromString(const UA_Variant *in, const UA_DataType *outType, UA_Variant *out) {
614
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
615
616
0
    if(outType == &UA_TYPES[UA_TYPES_BOOLEAN]) {
617
        /* String -> Boolean
618
         *
619
         * Part 4 says: String values containing "true", "false", "1" or "0"
620
         * can be converted to Boolean values. Other string values cause a
621
         * conversion error. In this case Strings are case-insensitive. */
622
0
        UA_Boolean b;
623
0
        const UA_String *inStr = (const UA_String*)in->data;
624
0
        if(inStr->length == 1 && inStr->data[0] == '0') {
625
0
            b = false;
626
0
        } else if(inStr->length == 1 && inStr->data[0] == '1') {
627
0
            b = true;
628
0
        } else if(inStr->length == 4 &&
629
0
                  uppercase(inStr->data[0])== 'T' && uppercase(inStr->data[1])== 'R' &&
630
0
                  uppercase(inStr->data[2])== 'U' && uppercase(inStr->data[3])== 'E') {
631
0
            b = true;
632
0
        } else if(inStr->length == 5              && uppercase(inStr->data[0])== 'F' &&
633
0
                  uppercase(inStr->data[1])== 'A' && uppercase(inStr->data[2])== 'L' &&
634
0
                  uppercase(inStr->data[3])== 'S' && uppercase(inStr->data[4])== 'E') {
635
0
            b = false;
636
0
        } else {
637
0
            return UA_STATUSCODE_BADTYPEMISMATCH;
638
0
        }
639
0
        return UA_Variant_setScalarCopy(out, &b, outType);
640
0
    } else if(outType == &UA_TYPES[UA_TYPES_GUID]) {
641
        /* String -> Guid */
642
0
        UA_Guid guid;
643
0
        res = UA_Guid_parse(&guid, *(UA_String*)in->data);
644
0
        if(res != UA_STATUSCODE_GOOD)
645
0
            return res;
646
0
        return UA_Variant_setScalarCopy(out, &guid, outType);
647
0
    }
648
649
0
#ifdef UA_ENABLE_JSON_ENCODING
650
    /* String -> Numerical, uses the JSON decoding */
651
0
    else if(UA_DataType_isNumeric(outType)) {
652
0
        void *outData = UA_new(outType);
653
0
        if(!outData)
654
0
            return UA_STATUSCODE_BADOUTOFMEMORY;
655
0
        res = UA_decodeJson((const UA_ByteString*)in->data, outData, outType, NULL);
656
0
        if(res != UA_STATUSCODE_GOOD) {
657
0
            UA_free(outData);
658
0
            return res;
659
0
        }
660
0
        UA_Variant_setScalar(out, outData, outType);
661
0
        return UA_STATUSCODE_GOOD;
662
0
    }
663
0
#endif
664
665
    /* No implicit casting possible */
666
0
    return UA_STATUSCODE_BADTYPEMISMATCH;
667
0
}
668
669
static UA_StatusCode
670
0
castImplicit(const UA_Variant *in, const UA_DataType *outType, UA_Variant *out) {
671
    /* Of the input is empty, casting results in a NULL value */
672
0
    if(UA_Variant_isEmpty(in)) {
673
0
        UA_Variant_init(out);
674
0
        return UA_STATUSCODE_GOOD;
675
0
    }
676
677
    /* TODO: We only support scalar values for now */
678
0
    if(!UA_Variant_isScalar(in))
679
0
        return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED;
680
681
    /* No casting necessary */
682
0
    if(in->type == outType) {
683
0
        *out = *in;
684
0
        out->storageType = UA_VARIANT_DATA_NODELETE;
685
0
        return UA_STATUSCODE_GOOD;
686
0
    }
687
688
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
689
0
    switch(in->type->typeKind) {
690
0
    case UA_DATATYPEKIND_EXPANDEDNODEID: {
691
        /* ExpandedNodeId -> String */
692
0
        if(outType != &UA_TYPES[UA_TYPES_STRING])
693
0
            break;
694
0
        UA_String *outStr = UA_String_new();
695
0
        if(!outStr)
696
0
            return UA_STATUSCODE_BADOUTOFMEMORY;
697
0
        res = UA_ExpandedNodeId_print((const UA_ExpandedNodeId*)in->data, outStr);
698
0
        if(res != UA_STATUSCODE_GOOD) {
699
0
            UA_free(outStr);
700
0
            break;
701
0
        }
702
0
        UA_Variant_setScalar(out, outStr, outType);
703
0
        break;
704
0
    }
705
706
0
    case UA_DATATYPEKIND_NODEID: {
707
0
        if(outType == &UA_TYPES[UA_TYPES_STRING]) {
708
            /* NodeId -> String */
709
0
            UA_String *outStr = UA_String_new();
710
0
            if(!outStr)
711
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
712
0
            res = UA_NodeId_print((const UA_NodeId*)in->data, outStr);
713
0
            if(res != UA_STATUSCODE_GOOD) {
714
0
                UA_free(outStr);
715
0
                break;
716
0
            }
717
0
            UA_Variant_setScalar(out, outStr, outType);
718
0
        } else if(outType == &UA_TYPES[UA_TYPES_EXPANDEDNODEID]) {
719
            /* NodeId -> ExpandedNodeId */
720
0
            UA_ExpandedNodeId *eid = UA_ExpandedNodeId_new();
721
0
            if(!eid)
722
0
                return UA_STATUSCODE_BADOUTOFMEMORY;
723
0
            res = UA_NodeId_copy((const UA_NodeId*)in->data, &eid->nodeId);
724
0
            if(res != UA_STATUSCODE_GOOD) {
725
0
                UA_free(eid);
726
0
                break;
727
0
            }
728
0
            UA_Variant_setScalar(out, eid, outType);
729
0
        }
730
0
        break;
731
0
    }
732
733
0
    case UA_DATATYPEKIND_STRING:
734
0
        res = castImplicitFromString(in, outType, out);
735
0
        break;
736
737
0
    case UA_DATATYPEKIND_LOCALIZEDTEXT: {
738
0
        if(outType != &UA_TYPES[UA_TYPES_STRING])
739
0
            break;
740
        /* LocalizedText -> String */
741
0
        UA_LocalizedText *inLT = (UA_LocalizedText*)in->data;
742
0
        res = UA_Variant_setScalarCopy(out, &inLT->text, outType);
743
0
        break;
744
0
    }
745
746
0
    case UA_DATATYPEKIND_QUALIFIEDNAME: {
747
0
        UA_QualifiedName *inQN = (UA_QualifiedName*)in->data;
748
0
        if(outType == &UA_TYPES[UA_TYPES_STRING]) {
749
            /* QualifiedName -> String */
750
0
            res = UA_Variant_setScalarCopy(out, &inQN->name, outType);
751
0
        } else if(outType == &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]) {
752
            /* QualifiedName -> LocalizedText */
753
0
            UA_LocalizedText lt;
754
0
            lt.text = inQN->name;
755
0
            lt.locale = UA_STRING_NULL;
756
0
            res = UA_Variant_setScalarCopy(out, &lt, outType);
757
0
        }
758
0
        break;
759
0
    }
760
761
0
    default:
762
        /* Try casting between numericals (also works for Boolean and StatusCode
763
         * input). The conversion can fail if the limits of the output type are
764
         * exceeded and then results in a NULL value. */
765
0
        castNumerical(in, outType, out);
766
0
    }
767
768
0
    return res;
769
0
}
770
771
/* Filter Evaluation
772
 * ----------------- */
773
774
static UA_StatusCode
775
0
resolveOperand(UA_FilterEvalContext *ctx, UA_ExtensionObject *op, UA_Variant *out) {
776
0
    if(op->encoding != UA_EXTENSIONOBJECT_DECODED &&
777
0
       op->encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE)
778
0
        return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED;
779
780
    /* Result of an operator that was evaluated prior */
781
0
    if(op->content.decoded.type == &UA_TYPES[UA_TYPES_ELEMENTOPERAND]) {
782
0
        UA_ElementOperand *eo = (UA_ElementOperand*)op->content.decoded.data;
783
0
        *out = ctx->operatorResults[eo->index];
784
0
        out->storageType = UA_VARIANT_DATA_NODELETE;
785
0
        return UA_STATUSCODE_GOOD;
786
0
    }
787
788
    /* Literal value */
789
0
    if(op->content.decoded.type == &UA_TYPES[UA_TYPES_LITERALOPERAND]) {
790
0
        UA_LiteralOperand *lo = (UA_LiteralOperand*)op->content.decoded.data;
791
0
        *out = lo->value;
792
0
        out->storageType = UA_VARIANT_DATA_NODELETE;
793
0
        return UA_STATUSCODE_GOOD;
794
0
    }
795
796
    /* SimpleAttributeOperand with a BrowsePath */
797
0
    if(op->content.decoded.type == &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]) {
798
0
        UA_SimpleAttributeOperand *sao =
799
0
            (UA_SimpleAttributeOperand*)op->content.decoded.data;
800
0
        return resolveSAO(ctx, sao, out);
801
0
    }
802
803
0
    return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED;
804
0
}
805
806
static UA_StatusCode
807
0
ofTypeOperator(UA_FilterEvalContext *ctx, size_t index) {
808
0
    const UA_ContentFilterElement *elm = &ctx->filter.whereClause.elements[index];
809
0
    UA_assert(elm->filterOperandsSize == 1);
810
811
    /* Get the operand. Must be a literal NodeId */
812
0
    UA_Variant *op0 = &ctx->operandStack[ctx->top++];
813
0
    UA_StatusCode res = resolveOperand(ctx, &elm->filterOperands[0], op0);
814
0
    if(res != UA_STATUSCODE_GOOD || !UA_Variant_hasScalarType(op0, &UA_TYPES[UA_TYPES_NODEID]))
815
0
        return UA_STATUSCODE_BADFILTEROPERANDINVALID;
816
0
    const UA_NodeId *operandTypeId = (const UA_NodeId *)op0->data;
817
818
    /* Read the /EventType event field */
819
0
    static UA_QualifiedName eventTypeName = {0, UA_STRING_STATIC("EventType")};
820
0
    UA_SimpleAttributeOperand sao;
821
0
    UA_SimpleAttributeOperand_init(&sao);
822
0
    sao.attributeId = UA_ATTRIBUTEID_VALUE;
823
0
    sao.browsePath = &eventTypeName;
824
0
    sao.browsePathSize = 1;
825
0
    UA_Variant eventType;
826
0
    UA_Variant_init(&eventType);
827
0
    res = resolveSAO(ctx, &sao, &eventType);
828
0
    if(res != UA_STATUSCODE_GOOD)
829
0
        return res;
830
0
    if(!UA_Variant_hasScalarType(&eventType, &UA_TYPES[UA_TYPES_NODEID])) {
831
0
        UA_Variant_clear(&eventType);
832
0
        return UA_STATUSCODE_BADINTERNALERROR;
833
0
    }
834
835
    /* Is the /EventType a subtype of the requested type? */
836
0
    UA_Boolean ofType =
837
0
        isNodeInTree_singleRef(ctx->server, (UA_NodeId*)eventType.data, operandTypeId,
838
0
                               UA_REFERENCETYPEINDEX_HASSUBTYPE);
839
0
    ctx->operatorResults[index] = t2v(ofType ? UA_TERNARY_TRUE : UA_TERNARY_FALSE);
840
0
    UA_Variant_clear(&eventType);
841
0
    return UA_STATUSCODE_GOOD;
842
0
}
843
844
static UA_StatusCode
845
0
andOperator(UA_FilterEvalContext *ctx, size_t index) {
846
0
    const UA_ContentFilterElement *elm = &ctx->filter.whereClause.elements[index];
847
0
    UA_assert(elm->filterOperandsSize == 2);
848
0
    UA_Variant *op0 = &ctx->operandStack[ctx->top++];
849
0
    UA_StatusCode res = resolveOperand(ctx, &elm->filterOperands[0], op0);
850
0
    UA_CHECK_STATUS(res, return res);
851
0
    UA_Variant *op1 = &ctx->operandStack[ctx->top++];
852
0
    res = resolveOperand(ctx, &elm->filterOperands[1], op1);
853
0
    UA_CHECK_STATUS(res, return res);
854
0
    ctx->operatorResults[index] = t2v(UA_Ternary_and(v2t(op0), v2t(op1)));
855
0
    return UA_STATUSCODE_GOOD;
856
0
}
857
858
static UA_StatusCode
859
0
orOperator(UA_FilterEvalContext *ctx, size_t index) {
860
0
    const UA_ContentFilterElement *elm = &ctx->filter.whereClause.elements[index];
861
0
    UA_assert(elm->filterOperandsSize == 2);
862
0
    UA_Variant *op0 = &ctx->operandStack[ctx->top++];
863
0
    UA_StatusCode res = resolveOperand(ctx, &elm->filterOperands[0], op0);
864
0
    UA_CHECK_STATUS(res, return res);
865
0
    UA_Variant *op1 = &ctx->operandStack[ctx->top++];
866
0
    res = resolveOperand(ctx, &elm->filterOperands[1], op1);
867
0
    UA_CHECK_STATUS(res, return res);
868
0
    ctx->operatorResults[index] = t2v(UA_Ternary_or(v2t(op0), v2t(op1)));
869
0
    return UA_STATUSCODE_GOOD;
870
0
}
871
872
static UA_StatusCode
873
0
notOperator(UA_FilterEvalContext *ctx, size_t index) {
874
0
    const UA_ContentFilterElement *elm = &ctx->filter.whereClause.elements[index];
875
0
    UA_assert(elm->filterOperandsSize == 1);
876
0
    UA_Variant *op0 = &ctx->operandStack[ctx->top++];
877
0
    UA_StatusCode res = resolveOperand(ctx, &elm->filterOperands[0], op0);
878
0
    UA_CHECK_STATUS(res, return res);
879
0
    ctx->operatorResults[index] = t2v(UA_Ternary_not(v2t(op0)));
880
0
    return UA_STATUSCODE_GOOD;
881
0
}
882
883
/* Resolves the operands and casts them implicitly to the same type. The result
884
 * is set at &ctx->operandStack[ctx->top] (for the initial value of top). */
885
static UA_StatusCode
886
0
castResolveOperands(UA_FilterEvalContext *ctx, size_t index, UA_Boolean setError) {
887
    /* Enough space on the operand stack left? */
888
0
    const UA_ContentFilterElement *elm = &ctx->filter.whereClause.elements[index];
889
0
    if(ctx->top + elm->filterOperandsSize > UA_EVENTFILTER_MAXOPERANDS)
890
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
891
892
    /* Resolve all operands */
893
0
    UA_assert(ctx->top == 0); /* Assume the operand stack is empty */
894
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
895
0
    for(size_t i = 0; i < elm->filterOperandsSize; i++) {
896
0
        res = resolveOperand(ctx, &elm->filterOperands[i], &ctx->operandStack[ctx->top++]);
897
0
        UA_CHECK_STATUS(res, return res);
898
0
    }
899
0
    UA_assert(ctx->top > 0); /* Assume the operand stack is no longer empty */
900
901
    /* Get the datatype for casting */
902
0
    const UA_DataType *targetType = ctx->operandStack[0].type;
903
0
    for(size_t pos = 1; pos < ctx->top; pos++) {
904
0
        if(targetType)
905
0
            targetType = implicitCastTargetType(targetType, ctx->operandStack[pos].type);
906
0
        if(!targetType)
907
0
            return UA_STATUSCODE_BADFILTEROPERANDINVALID;
908
0
    }
909
910
    /* Cast the operands. Put the result in the same location on the operand stack. */
911
0
    for(size_t pos = 0; pos < ctx->top; pos++) {
912
0
        UA_Variant orig = ctx->operandStack[pos];
913
0
        res = castImplicit(&orig, targetType, &ctx->operandStack[pos]);
914
0
        if(res != UA_STATUSCODE_GOOD)
915
0
            return res;
916
0
        if(ctx->operandStack[pos].data == orig.data) {
917
            /* Reuse the storage type of the original data if the variant is
918
             * identical or only the type has changed */
919
0
            ctx->operandStack[pos].storageType = orig.storageType;
920
0
        } else {
921
0
            UA_Variant_clear(&orig); /* Fresh allocation of the cast variant. Clean up. */
922
0
        }
923
0
    }
924
925
0
    return UA_STATUSCODE_GOOD;
926
0
}
927
928
static UA_StatusCode
929
0
compareOperator(UA_FilterEvalContext *ctx, size_t index, UA_FilterOperator op) {
930
0
    UA_assert(ctx->filter.whereClause.elements[index].filterOperandsSize == 2);
931
932
    /* Resolve and cast the operands. A failed casting results in FALSE. Note
933
     * that operands could cast to NULL. */
934
0
    UA_assert(ctx->top == 0); /* Assume the operand stack is empty */
935
0
    UA_StatusCode res = castResolveOperands(ctx, index, false);
936
0
    if(res != UA_STATUSCODE_GOOD || !ctx->operandStack[0].type ||
937
0
       ctx->operandStack[0].type != ctx->operandStack[1].type) {
938
0
        ctx->operatorResults[index] = t2v(UA_TERNARY_FALSE);
939
0
        return UA_STATUSCODE_GOOD;
940
0
    }
941
0
    UA_assert(ctx->top == 2); /* Assume the operand stack is no longer empty */
942
943
    /* The equals operator is always possible. For the other comparisons it has
944
     * to be an ordered type: Numerical, Boolean, StatusCode or DateTime. */
945
0
    const UA_DataType *type = ctx->operandStack[0].type;
946
0
    if(op != UA_FILTEROPERATOR_EQUALS && !UA_DataType_isNumeric(type) &&
947
0
       type->typeKind != UA_DATATYPEKIND_BOOLEAN &&
948
0
       type->typeKind != UA_DATATYPEKIND_STATUSCODE &&
949
0
       type->typeKind != UA_DATATYPEKIND_DATETIME)
950
0
        return UA_STATUSCODE_BADFILTEROPERANDINVALID;
951
952
    /* Compute the order */
953
0
    UA_Order eq = UA_order(ctx->operandStack[0].data, ctx->operandStack[1].data, type);
954
0
    UA_Ternary operatorResult = UA_TERNARY_FALSE;
955
0
    switch(op) {
956
0
    case UA_FILTEROPERATOR_EQUALS:
957
0
    default:
958
0
        if(eq == UA_ORDER_EQ)
959
0
            operatorResult = UA_TERNARY_TRUE;
960
0
        break;
961
0
    case UA_FILTEROPERATOR_GREATERTHAN:
962
0
        if(eq == UA_ORDER_MORE)
963
0
            operatorResult = UA_TERNARY_TRUE;
964
0
        break;
965
0
    case UA_FILTEROPERATOR_LESSTHAN:
966
0
        if(eq == UA_ORDER_LESS)
967
0
            operatorResult = UA_TERNARY_TRUE;
968
0
        break;
969
0
    case UA_FILTEROPERATOR_GREATERTHANOREQUAL:
970
0
        if(eq == UA_ORDER_MORE || eq == UA_ORDER_EQ)
971
0
            operatorResult = UA_TERNARY_TRUE;
972
0
        break;
973
0
    case UA_FILTEROPERATOR_LESSTHANOREQUAL:
974
0
        if(eq == UA_ORDER_LESS || eq == UA_ORDER_EQ)
975
0
            operatorResult = UA_TERNARY_TRUE;
976
0
        break;
977
0
    }
978
979
    /* Set result as a literal value */
980
0
    ctx->operatorResults[index] = t2v(operatorResult);
981
0
    return UA_STATUSCODE_GOOD;
982
0
}
983
984
static UA_StatusCode
985
0
equalsOperator(UA_FilterEvalContext *ctx, size_t index) {
986
0
    return compareOperator(ctx, index, UA_FILTEROPERATOR_EQUALS);
987
0
}
988
989
static UA_StatusCode
990
0
gtOperator(UA_FilterEvalContext *ctx, size_t index) {
991
0
    return compareOperator(ctx, index, UA_FILTEROPERATOR_GREATERTHAN);
992
0
}
993
994
static UA_StatusCode
995
0
ltOperator(UA_FilterEvalContext *ctx, size_t index) {
996
0
    return compareOperator(ctx, index, UA_FILTEROPERATOR_LESSTHAN);
997
0
}
998
999
static UA_StatusCode
1000
0
gteOperator(UA_FilterEvalContext *ctx, size_t index) {
1001
0
    return compareOperator(ctx, index, UA_FILTEROPERATOR_GREATERTHANOREQUAL);
1002
0
}
1003
1004
static UA_StatusCode
1005
0
lteOperator(UA_FilterEvalContext *ctx, size_t index) {
1006
0
    return compareOperator(ctx, index, UA_FILTEROPERATOR_LESSTHANOREQUAL);
1007
0
}
1008
1009
static UA_StatusCode
1010
0
bitwiseOperator(UA_FilterEvalContext *ctx, size_t index, UA_FilterOperator op) {
1011
0
    UA_assert(ctx->filter.whereClause.elements[index].filterOperandsSize == 2);
1012
1013
    /* Resolve and cast the operands. Note that operands could cast to NULL. */
1014
0
    UA_assert(ctx->top == 0); /* Assume the operand stack is empty */
1015
0
    UA_StatusCode res = castResolveOperands(ctx, index, true);
1016
0
    UA_CHECK_STATUS(res, return res);
1017
0
    UA_assert(ctx->top == 2); /* Assume we have two elements */
1018
1019
    /* Operands can cast to NULL */
1020
0
    const UA_DataType *type = ctx->operandStack[0].type;
1021
0
    if(!type || !UA_DataType_isNumeric(type) ||
1022
0
       ctx->operandStack[0].type != ctx->operandStack[1].type)
1023
0
        return UA_STATUSCODE_BADTYPEMISMATCH;
1024
1025
    /* Copy the casted literal to the result */
1026
0
    res = UA_Variant_copy(&ctx->operandStack[0], &ctx->operatorResults[index]);
1027
0
    UA_CHECK_STATUS(res, return res);
1028
1029
    /* Do the bitwise operation on the result data */
1030
0
    UA_Byte *bytesOut = (UA_Byte*)ctx->operatorResults[index].data;
1031
0
    const UA_Byte *bytes2 = (const UA_Byte*)ctx->operandStack[1].data;
1032
0
    for(size_t i = 0; i < type->memSize; i++) {
1033
0
        if(op == UA_FILTEROPERATOR_BITWISEAND)
1034
0
            bytesOut[i] = bytesOut[i] & bytes2[i];
1035
0
        else
1036
0
            bytesOut[i] = bytesOut[i] | bytes2[i];
1037
0
    }
1038
0
    return UA_STATUSCODE_GOOD;
1039
0
}
1040
1041
static UA_StatusCode
1042
0
bitwiseAndOperator(UA_FilterEvalContext *ctx, size_t index) {
1043
0
    return bitwiseOperator(ctx, index, UA_FILTEROPERATOR_BITWISEAND);
1044
0
}
1045
1046
static UA_StatusCode
1047
0
bitwiseOrOperator(UA_FilterEvalContext *ctx, size_t index) {
1048
0
    return bitwiseOperator(ctx, index, UA_FILTEROPERATOR_BITWISEOR);
1049
0
}
1050
1051
static UA_StatusCode
1052
0
betweenOperator(UA_FilterEvalContext *ctx, size_t index) {
1053
0
    UA_assert(ctx->filter.whereClause.elements[index].filterOperandsSize == 3);
1054
1055
    /* If no implicit conversion is available and the operands are of different
1056
     * types, the particular result is FALSE. */
1057
0
    UA_assert(ctx->top == 0); /* Assume the operand stack is empty */
1058
0
    UA_StatusCode res = castResolveOperands(ctx, index, false);
1059
0
    if(res != UA_STATUSCODE_GOOD) {
1060
0
        ctx->operatorResults[index] = t2v(UA_TERNARY_FALSE);
1061
0
        return UA_STATUSCODE_GOOD;
1062
0
    }
1063
0
    UA_assert(ctx->top == 3); /* Assume we have three elements */
1064
1065
    /* The casting can result in NULL values or a non-numerical type */
1066
0
    const UA_DataType *type = ctx->operandStack[0].type;
1067
0
    if(!type || !UA_DataType_isNumeric(type) ||
1068
0
       type != ctx->operandStack[1].type || type != ctx->operandStack[2].type)
1069
0
        return UA_STATUSCODE_BADTYPEMISMATCH;
1070
1071
0
    UA_Order o1 = UA_order(ctx->operandStack[0].data, ctx->operandStack[1].data, type);
1072
0
    UA_Order o2 = UA_order(ctx->operandStack[0].data, ctx->operandStack[2].data, type);
1073
0
    UA_Ternary comp = ((o1 == UA_ORDER_MORE || o1 == UA_ORDER_EQ) &&
1074
0
                       (o2 == UA_ORDER_LESS || o2 == UA_ORDER_EQ)) ?
1075
0
        UA_TERNARY_TRUE : UA_TERNARY_FALSE;
1076
1077
0
    ctx->operatorResults[index] = t2v(comp);
1078
0
    return UA_STATUSCODE_GOOD;
1079
0
}
1080
1081
static UA_StatusCode
1082
0
inListOperator(UA_FilterEvalContext *ctx, size_t index) {
1083
0
    const UA_ContentFilterElement *elm = &ctx->filter.whereClause.elements[index];
1084
0
    UA_assert(elm->filterOperandsSize >= 2);
1085
0
    UA_Boolean found = false;
1086
0
    UA_Variant *op0 = &ctx->operandStack[ctx->top++];
1087
0
    UA_Variant *op1 = &ctx->operandStack[ctx->top++];
1088
0
    UA_StatusCode res = resolveOperand(ctx, &elm->filterOperands[0], op0);
1089
0
    UA_CHECK_STATUS(res, return res);
1090
0
    for(size_t i = 1; i < elm->filterOperandsSize && !found; i++) {
1091
0
        res = resolveOperand(ctx, &elm->filterOperands[i], op1);
1092
0
        if(res != UA_STATUSCODE_GOOD)
1093
0
            continue;
1094
0
        if(UA_Variant_equal(op0, op1))
1095
0
            found = true;
1096
0
        UA_Variant_clear(op1);
1097
0
    }
1098
0
    ctx->operatorResults[index] = t2v((found) ? UA_TERNARY_TRUE: UA_TERNARY_FALSE);
1099
0
    return UA_STATUSCODE_GOOD;
1100
0
}
1101
1102
static UA_StatusCode
1103
0
isNullOperator(UA_FilterEvalContext *ctx, size_t index) {
1104
0
    const UA_ContentFilterElement *elm = &ctx->filter.whereClause.elements[index];
1105
0
    UA_assert(elm->filterOperandsSize == 1);
1106
0
    UA_Variant *op0 = &ctx->operandStack[ctx->top++];
1107
0
    UA_StatusCode res = resolveOperand(ctx, &elm->filterOperands[0], op0);
1108
0
    UA_CHECK_STATUS(res, return res);
1109
0
    ctx->operatorResults[index] =
1110
0
        t2v(UA_Variant_isEmpty(op0) ? UA_TERNARY_TRUE : UA_TERNARY_FALSE);
1111
0
    return UA_STATUSCODE_GOOD;
1112
0
}
1113
1114
static UA_StatusCode
1115
0
notImplementedOperator(UA_FilterEvalContext *ctx, size_t index) {
1116
0
    return UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED;
1117
0
}
1118
1119
typedef struct {
1120
    UA_StatusCode (*operatorMethod)(UA_FilterEvalContext *ctx, size_t index);
1121
    UA_Byte minOperatorCount;
1122
    UA_Byte maxOperatorCount;
1123
} UA_FilterOperatorJumptableElement;
1124
1125
static const UA_FilterOperatorJumptableElement operatorJumptable[18] = {
1126
    {equalsOperator, 2, 2},
1127
    {isNullOperator, 1, 1},
1128
    {gtOperator, 2, 2},
1129
    {ltOperator, 2, 2},
1130
    {gteOperator, 2, 2},
1131
    {lteOperator, 2, 2},
1132
    {notImplementedOperator, 0, UA_EVENTFILTER_MAXOPERANDS}, /* like */
1133
    {notOperator, 1, 1},
1134
    {betweenOperator, 3, 3},
1135
    {inListOperator, 2, UA_EVENTFILTER_MAXOPERANDS},
1136
    {andOperator, 2, 2},
1137
    {orOperator, 2, 2},
1138
    {notImplementedOperator, 0, UA_EVENTFILTER_MAXOPERANDS}, /* cast */
1139
    {notImplementedOperator, 0, UA_EVENTFILTER_MAXOPERANDS}, /* in view */
1140
    {ofTypeOperator, 1, 1},
1141
    {notImplementedOperator, 0, UA_EVENTFILTER_MAXOPERANDS}, /* related to */
1142
    {bitwiseAndOperator, 2, 2},
1143
    {bitwiseOrOperator, 2, 2}
1144
};
1145
1146
/* Evaluate content filter, exported only for unit testing */
1147
UA_StatusCode
1148
0
evaluateWhereClause(UA_FilterEvalContext *ctx) {
1149
0
    UA_LOCK_ASSERT(&ctx->server->serviceMutex);
1150
1151
    /* An empty filter always succeeds */
1152
0
    if(ctx->filter.whereClause.elementsSize == 0)
1153
0
        return UA_STATUSCODE_GOOD;
1154
1155
    /* Information-model filters may bypass creation-time validation. */
1156
0
    for(size_t i = 0; i < ctx->filter.whereClause.elementsSize; i++) {
1157
0
        if((UA_UInt32)ctx->filter.whereClause.elements[i].filterOperator >
1158
0
           UA_FILTEROPERATOR_BITWISEOR)
1159
0
            return UA_STATUSCODE_BADEVENTFILTERINVALID;
1160
0
    }
1161
1162
    /* Pacify some compilers by initializing the first result */
1163
0
    UA_assert(ctx->top == 0);
1164
0
    UA_Variant_init(&ctx->operatorResults[0]);
1165
1166
    /* Evaluate the filter. Iterate backwards over the filter elements and
1167
     * resolve each. This ensures that all element-index operands point to an
1168
     * evaluated element. */
1169
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1170
0
    const UA_ContentFilter *cf = &ctx->filter.whereClause;
1171
0
    for(size_t i = cf->elementsSize - 1; i < cf->elementsSize; i--) {
1172
0
        UA_ContentFilterElement *cfe = &cf->elements[i];
1173
0
        res = operatorJumptable[cfe->filterOperator].operatorMethod(ctx, i);
1174
        /* Clean up the operand stack */
1175
0
        for(size_t j = 0; j < ctx->top; j++)
1176
0
            UA_Variant_clear(&ctx->operandStack[j]);
1177
0
        ctx->top = 0;
1178
0
        if(res != UA_STATUSCODE_GOOD)
1179
0
            break;
1180
0
    }
1181
1182
    /* The filter matches if the operator at the first position evaluates to TRUE */
1183
0
    if(res == UA_STATUSCODE_GOOD && v2t(&ctx->operatorResults[0]) != UA_TERNARY_TRUE)
1184
0
        res = UA_STATUSCODE_BADNOMATCH;
1185
1186
    /* Clean up the results stack */
1187
0
    for(size_t i = 0; i < cf->elementsSize; i++)
1188
0
        UA_Variant_clear(&ctx->operatorResults[i]);
1189
0
    return res;
1190
0
}
1191
1192
/*****************************************/
1193
/* Validation of Filters during Creation */
1194
/*****************************************/
1195
1196
/* Initial select clause validation. The following checks are currently performed:
1197
 * - Check if the TypeDefinitionId or BrowsePath of any clause is NULL
1198
 * - Check if the EventType is a subtype of BaseEventType
1199
 * - Check if the AttributeId is valid
1200
 * - Check if the BrowsePath contains null
1201
 * - Check if the IndexRange is defined and if it is parseable
1202
 * - Check if the AttributeId is value when an IndexRange is defined */
1203
UA_StatusCode
1204
UA_SimpleAttributeOperandValidation(UA_Server *server,
1205
0
                                    const UA_SimpleAttributeOperand *sao) {
1206
    /* TypeDefinition is not NULL? */
1207
0
    if(UA_NodeId_isNull(&sao->typeDefinitionId))
1208
0
        return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
1209
1210
    /* EventType is a subtype of BaseEventType? */
1211
0
    UA_NodeId baseEventTypeId = UA_NS0ID(BASEEVENTTYPE);
1212
0
    if(!isNodeInTree_singleRef(server, &sao->typeDefinitionId, &baseEventTypeId,
1213
0
                               UA_REFERENCETYPEINDEX_HASSUBTYPE))
1214
0
        return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
1215
1216
    /* AttributeId is valid ? */
1217
0
    if(sao->attributeId == 0 || sao->attributeId >= 28)
1218
0
        return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
1219
1220
    /* If the BrowsePath is empty, the Node is the instance of the
1221
     * TypeDefinition. (Part 4, 7.4.4.5) */
1222
0
    if(sao->browsePathSize > 0) {
1223
        /* BrowsePath contains empty BrowseNames? */
1224
0
        for(size_t j = 0; j < sao->browsePathSize; ++j) {
1225
0
            if(UA_QualifiedName_isNull(&sao->browsePath[j]))
1226
0
                return UA_STATUSCODE_BADBROWSENAMEINVALID;
1227
0
        }
1228
1229
        /* Part 4: If the SimpleAttributeOperand is used in an EventFilter and
1230
         * the typeDefinitionId is BaseEventType the Server shall evaluate the
1231
         * browsePath without considering the typeDefinitionId. */
1232
0
        if(!UA_NodeId_equal(&baseEventTypeId, &sao->typeDefinitionId)) {
1233
            /* Part 4: The TypeDefinitionNode restricts the operand to instances
1234
             * of the TypeDefinitionNode or one of its subtypes.
1235
             * First get the list of subtypes from event type (including the
1236
             * event type itself). */
1237
0
            UA_ReferenceTypeSet reftypes_interface =
1238
0
                UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE);
1239
0
            UA_ExpandedNodeId *childTypeNodes = NULL;
1240
0
            size_t childTypeNodesSize = 0;
1241
0
            UA_StatusCode res =
1242
0
                browseRecursive(server, 1, &sao->typeDefinitionId, UA_BROWSEDIRECTION_FORWARD,
1243
0
                                &reftypes_interface, UA_NODECLASS_OBJECTTYPE, true,
1244
0
                                &childTypeNodesSize, &childTypeNodes);
1245
0
            if(res != UA_STATUSCODE_GOOD)
1246
0
                return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
1247
1248
            /* Is the browse path valid for one of the event types? */
1249
0
            UA_Boolean subTypeContainField = false;
1250
0
            for(size_t j = 0; j < childTypeNodesSize && !subTypeContainField; j++) {
1251
0
                UA_BrowsePathResult bpr =
1252
0
                    browseSimplifiedBrowsePath(server, childTypeNodes[j].nodeId,
1253
0
                                               sao->browsePathSize, sao->browsePath);
1254
1255
0
                if(bpr.statusCode == UA_STATUSCODE_GOOD && bpr.targetsSize > 0)
1256
0
                    subTypeContainField = true;
1257
0
                UA_BrowsePathResult_clear(&bpr);
1258
0
            }
1259
1260
            /* Clean up and return an error if not found */
1261
0
            UA_Array_delete(childTypeNodes, childTypeNodesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
1262
0
            if(!subTypeContainField)
1263
0
                return UA_STATUSCODE_BADNODEIDUNKNOWN;
1264
0
        }
1265
0
    }
1266
1267
    /* IndexRange is defined ? */
1268
0
    if(!UA_String_isEmpty(&sao->indexRange)) {
1269
0
        UA_NumericRange numericRange = UA_NUMERICRANGE("");
1270
0
        if(UA_NumericRange_parse(&numericRange, sao->indexRange) != UA_STATUSCODE_GOOD)
1271
0
            return UA_STATUSCODE_BADINDEXRANGEINVALID;
1272
0
        UA_free(numericRange.dimensions);
1273
1274
        /* AttributeId has to be a value if an IndexRange is defined */
1275
0
        if(sao->attributeId != UA_ATTRIBUTEID_VALUE)
1276
0
            return UA_STATUSCODE_BADTYPEMISMATCH;
1277
0
    }
1278
1279
0
    return UA_STATUSCODE_GOOD;
1280
0
}
1281
1282
/* Initial content filter (where clause) check. Current checks:
1283
 * - Number of operands for each (supported) operator
1284
 * - ElementOperands point forward only */
1285
UA_ContentFilterElementResult
1286
UA_ContentFilterElementValidation(UA_Server *server, size_t operatorIndex,
1287
                                  size_t operatorsCount,
1288
0
                                  const UA_ContentFilterElement *ef) {
1289
    /* Initialize the result structure */
1290
0
    UA_ContentFilterElementResult er;
1291
0
    UA_ContentFilterElementResult_init(&er);
1292
0
    er.operandStatusCodes = (UA_StatusCode *)
1293
0
        UA_Array_new(ef->filterOperandsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
1294
0
    if(!er.operandStatusCodes) {
1295
0
        er.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
1296
0
        return er;
1297
0
    }
1298
0
    er.operandStatusCodesSize = ef->filterOperandsSize;
1299
1300
    /* Is the operator in the defined range? Test this before the following
1301
     * jumptable lookup. */
1302
0
    if(ef->filterOperator < 0 || ef->filterOperator > UA_FILTEROPERATOR_BITWISEOR) {
1303
0
        er.statusCode = UA_STATUSCODE_BADEVENTFILTERINVALID;
1304
0
        return er;
1305
0
    }
1306
1307
    /* Number of operands supported for the operator? */
1308
0
    if(ef->filterOperandsSize < operatorJumptable[ef->filterOperator].minOperatorCount ||
1309
0
       ef->filterOperandsSize > operatorJumptable[ef->filterOperator].maxOperatorCount) {
1310
0
        er.statusCode = UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH;
1311
0
        return er;
1312
0
    }
1313
1314
    /* Generic validation of the operands */
1315
0
    for(size_t i = 0; i < ef->filterOperandsSize; i++) {
1316
        /* Must be a decoded ExtensionObject */
1317
0
        UA_ExtensionObject *op = &ef->filterOperands[i];
1318
0
        if(op->encoding != UA_EXTENSIONOBJECT_DECODED &&
1319
0
           op->encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) {
1320
0
            er.operandStatusCodes[i] = UA_STATUSCODE_BADFILTEROPERANDINVALID;
1321
0
            er.statusCode = UA_STATUSCODE_BADFILTEROPERANDINVALID;
1322
0
            return er;
1323
0
        }
1324
1325
        /* Supported type and conforming to the rules? */
1326
0
        if(op->content.decoded.type == &UA_TYPES[UA_TYPES_ELEMENTOPERAND]) {
1327
            /* Part 4, 7.4.4.2 defines conditions for Element Operands: An index
1328
             * is considered valid if its value is greater than the element
1329
             * index it is part of and it does not Reference a non-existent
1330
             * element. Clients shall construct filters in this way to avoid
1331
             * circular and invalid References. */
1332
0
            UA_ElementOperand *eo = (UA_ElementOperand *)op->content.decoded.data;
1333
0
            if(eo->index <= operatorIndex || eo->index >= operatorsCount) {
1334
0
                er.operandStatusCodes[i] = UA_STATUSCODE_BADINDEXRANGEINVALID;
1335
0
                er.statusCode = UA_STATUSCODE_BADINDEXRANGEINVALID;
1336
0
                return er;
1337
0
            }
1338
0
        } else if(op->content.decoded.type == &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND]) {
1339
0
            er.operandStatusCodes[i] = UA_STATUSCODE_BADNOTSUPPORTED;
1340
0
            er.statusCode = UA_STATUSCODE_BADFILTERNOTALLOWED;
1341
0
            return er;
1342
0
        } else if(op->content.decoded.type != &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND] &&
1343
0
                  op->content.decoded.type != &UA_TYPES[UA_TYPES_LITERALOPERAND]) {
1344
0
            er.operandStatusCodes[i] = UA_STATUSCODE_BADFILTEROPERANDINVALID;
1345
0
            er.statusCode = UA_STATUSCODE_BADFILTEROPERANDINVALID;
1346
0
            return er;
1347
0
        }
1348
0
    }
1349
1350
    /* Specific validations for different operators */
1351
0
    switch(ef->filterOperator) {
1352
0
        case UA_FILTEROPERATOR_INVIEW:
1353
0
        case UA_FILTEROPERATOR_RELATEDTO:
1354
            /* Not allowed for event WhereClause according to 7.17.3 in Part 4 */
1355
0
            er.statusCode = UA_STATUSCODE_BADEVENTFILTERINVALID;
1356
0
            break;
1357
1358
0
        case UA_FILTEROPERATOR_EQUALS:
1359
0
        case UA_FILTEROPERATOR_GREATERTHAN:
1360
0
        case UA_FILTEROPERATOR_LESSTHAN:
1361
0
        case UA_FILTEROPERATOR_GREATERTHANOREQUAL:
1362
0
        case UA_FILTEROPERATOR_LESSTHANOREQUAL:
1363
0
        case UA_FILTEROPERATOR_BITWISEAND:
1364
0
        case UA_FILTEROPERATOR_BITWISEOR:
1365
0
        case UA_FILTEROPERATOR_BETWEEN:
1366
0
        case UA_FILTEROPERATOR_INLIST:
1367
0
        case UA_FILTEROPERATOR_AND:
1368
0
        case UA_FILTEROPERATOR_OR:
1369
0
        case UA_FILTEROPERATOR_ISNULL:
1370
0
        case UA_FILTEROPERATOR_NOT:
1371
0
            break;
1372
1373
0
        case UA_FILTEROPERATOR_OFTYPE: {
1374
            /* Make sure the operand is a NodeId literal */
1375
0
            UA_ExtensionObject *o = &ef->filterOperands[0];
1376
0
            UA_LiteralOperand *lo = (UA_LiteralOperand *)o->content.decoded.data;
1377
0
            if(o->content.decoded.type != &UA_TYPES[UA_TYPES_LITERALOPERAND] ||
1378
0
               !UA_Variant_hasScalarType(&lo->value, &UA_TYPES[UA_TYPES_NODEID])) {
1379
0
                er.operandStatusCodes[0] = UA_STATUSCODE_BADFILTEROPERANDINVALID;
1380
0
                er.statusCode = UA_STATUSCODE_BADFILTEROPERANDINVALID;
1381
0
                break;
1382
0
            }
1383
1384
            /* Make sure the operand is a subtype of BaseEventType */
1385
0
            UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
1386
0
            if(!isNodeInTree_singleRef(server, (UA_NodeId *)lo->value.data, &baseEventTypeId,
1387
0
                                       UA_REFERENCETYPEINDEX_HASSUBTYPE)) {
1388
0
                er.operandStatusCodes[0] = UA_STATUSCODE_BADFILTEROPERANDINVALID;
1389
0
                er.statusCode = UA_STATUSCODE_BADFILTEROPERANDINVALID;
1390
0
                break;
1391
0
            }
1392
0
            break;
1393
0
        }
1394
1395
0
        case UA_FILTEROPERATOR_LIKE:
1396
0
        case UA_FILTEROPERATOR_CAST:
1397
0
            er.statusCode = UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED;
1398
0
            break;
1399
1400
0
        default:
1401
0
            er.statusCode = UA_STATUSCODE_BADFILTEROPERATORINVALID;
1402
0
            break;
1403
0
    }
1404
0
    return er;
1405
0
}
1406
1407
/*************************/
1408
/* Create Event Instance */
1409
/*************************/
1410
1411
UA_StatusCode
1412
0
evaluateSelectClause(UA_FilterEvalContext *ctx, UA_EventFieldList *efl) {
1413
    /* Nothing to do */
1414
0
    if(ctx->filter.selectClausesSize == 0)
1415
0
        return UA_STATUSCODE_GOOD;
1416
1417
    /* Allocate the event fields list */
1418
0
    efl->eventFields = (UA_Variant*)
1419
0
        UA_calloc(ctx->filter.selectClausesSize, sizeof(UA_Variant));
1420
0
    if(!efl->eventFields)
1421
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1422
0
    efl->eventFieldsSize = ctx->filter.selectClausesSize;
1423
1424
    /* Resolve the select clauses */
1425
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1426
0
    for(size_t i = 0; i < ctx->filter.selectClausesSize; i++) {
1427
0
        const UA_SimpleAttributeOperand *sao = &ctx->filter.selectClauses[i];
1428
0
        UA_Variant *field = &efl->eventFields[i];
1429
0
        res |= resolveSAO(ctx, sao, field);
1430
1431
        /* Ensure a deep copy */
1432
0
        if(field->storageType == UA_VARIANT_DATA_NODELETE) {
1433
0
            UA_Variant tmp_val;
1434
0
            UA_StatusCode res_ign = UA_Variant_copy(field, &tmp_val);
1435
0
            (void)res_ign; /* Ignore the result - returns an empty variant if copying fails */
1436
0
            *field = tmp_val;
1437
0
        }
1438
0
    }
1439
1440
0
    return res;
1441
0
}
1442
1443
/* Filters an event according to the filter specified by mon and then adds it to
1444
 * mons notification queue */
1445
static UA_StatusCode
1446
0
UA_MonitoredItem_addEvent(UA_MonitoredItem *mon, UA_FilterEvalContext *ctx) {
1447
    /* Evaluate the where clause */
1448
0
    UA_StatusCode res = evaluateWhereClause(ctx);
1449
0
    if(res != UA_STATUSCODE_GOOD) {
1450
0
        if(res == UA_STATUSCODE_BADNOMATCH)
1451
0
            res = UA_STATUSCODE_GOOD;
1452
0
        return res;
1453
0
    }
1454
1455
    /* Allocate memory for the notification */
1456
0
    UA_Notification *n = UA_Notification_new();
1457
0
    if(!n)
1458
0
        return UA_STATUSCODE_BADOUTOFMEMORY;
1459
1460
    /* Evaluate the select clause to get the event fields */
1461
0
    res = evaluateSelectClause(ctx, &n->data.event);
1462
0
    if(res != UA_STATUSCODE_GOOD) {
1463
0
        UA_EventFieldList_clear(&n->data.event);
1464
0
        UA_free(n);
1465
0
        return res;
1466
0
    }
1467
1468
    /* Finalize and enqueue the notification */
1469
0
    n->data.event.clientHandle = mon->parameters.clientHandle;
1470
0
    n->mon = mon;
1471
0
    UA_Notification_enqueueAndTrigger(ctx->server, n);
1472
0
    return UA_STATUSCODE_GOOD;
1473
0
}
1474
1475
#ifdef UA_ENABLE_HISTORIZING
1476
static void
1477
setHistoricalEvent(UA_Server *server, const UA_NodeId *emitNode,
1478
0
                   const UA_EventDescription *ed) {
1479
0
    UA_Variant historicalEventFilterValue;
1480
0
    UA_Variant_init(&historicalEventFilterValue);
1481
1482
    /* A HistoricalEventNode that has event history available will provide this property */
1483
0
    UA_StatusCode retval =
1484
0
        readObjectProperty(server, *emitNode,
1485
0
                           UA_QUALIFIEDNAME(0, "HistoricalEventFilter"),
1486
0
                           &historicalEventFilterValue);
1487
0
    if(retval != UA_STATUSCODE_GOOD) {
1488
0
        if(retval != UA_STATUSCODE_BADNOMATCH)
1489
0
            UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
1490
0
                           "Cannot read the HistoricalEventFilter property of a "
1491
0
                           "listening node. StatusCode %s",
1492
0
                           UA_StatusCode_name(retval));
1493
0
        return;
1494
0
    }
1495
1496
    /* If found then check if HistoricalEventFilter property has a valid value */
1497
0
    if(!UA_Variant_hasScalarType(&historicalEventFilterValue,
1498
0
                                 &UA_TYPES[UA_TYPES_EVENTFILTER])) {
1499
0
        UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
1500
0
                       "HistoricalEventFilter property of a listening node "
1501
0
                       "does not have a valid value");
1502
0
        UA_Variant_clear(&historicalEventFilterValue);
1503
0
        return;
1504
0
    }
1505
0
    UA_EventFilter *ef = (UA_EventFilter*)historicalEventFilterValue.data;
1506
1507
0
    UA_FilterEvalContext ctx;
1508
0
    UA_FilterEvalContext_init(&ctx);
1509
0
    ctx.server = server;
1510
0
    ctx.session = &server->adminSession;
1511
0
    ctx.filter = *ef;
1512
0
    ctx.ed = *ed;
1513
1514
    /* Evaluate the where clause */
1515
0
    UA_StatusCode res = evaluateWhereClause(&ctx);
1516
0
    if(res != UA_STATUSCODE_GOOD) {
1517
0
        UA_FilterEvalContext_reset(&ctx);
1518
0
        UA_Variant_clear(&historicalEventFilterValue);
1519
0
        return;
1520
0
    }
1521
1522
    /* Get the event fields for the select clause */
1523
0
    UA_EventFieldList efl;
1524
0
    UA_EventFieldList_init(&efl);
1525
0
    res = evaluateSelectClause(&ctx, &efl);
1526
1527
    /* Call the history database backend */
1528
0
    if(UA_LIKELY(res == UA_STATUSCODE_GOOD)) {
1529
0
        server->config.historyDatabase.
1530
0
            setEvent(server, server->config.historyDatabase.context,
1531
0
                     &ed->sourceNode, emitNode, ef, &efl);
1532
0
    }
1533
1534
0
    UA_FilterEvalContext_reset(&ctx);
1535
0
    UA_Variant_clear(&historicalEventFilterValue);
1536
0
    UA_EventFieldList_clear(&efl);
1537
0
}
1538
#endif
1539
1540
0
#define EMIT_REFS_ROOT_COUNT 4
1541
static const UA_NodeId emitReferencesRoots[EMIT_REFS_ROOT_COUNT] =
1542
    {{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}},
1543
     {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}},
1544
     {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASEVENTSOURCE}},
1545
     {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASNOTIFIER}}};
1546
1547
UA_StatusCode
1548
createEvent(UA_Server *server, const UA_EventDescription *ed,
1549
0
            UA_ByteString *outEventId) {
1550
0
    UA_StatusCode res = UA_STATUSCODE_GOOD;
1551
0
    UA_LOCK_ASSERT(&server->serviceMutex);
1552
1553
    /* MonitoredItem can only be filtered if the Subscription is defined.
1554
     * Subscription can only be filtered if a Session is defined. */
1555
0
    if((ed->subscriptionId && !ed->sessionId) ||
1556
0
       (ed->monitoredItemId && !ed->subscriptionId)) {
1557
0
        UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
1558
0
                       "Event shall be created with a filter on Subscription (or MonitoredItem), "
1559
0
                       "but no Session (or Subscription) is defined");
1560
0
        return UA_STATUSCODE_BADINTERNALERROR;
1561
0
    }
1562
1563
0
    UA_LOG_DEBUG(server->config.logging, UA_LOGCATEGORY_SERVER,
1564
0
                 "Events: An event of severity %u is emitted by node %N",
1565
0
                 ed->severity, ed->sourceNode);
1566
1567
#ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS
1568
    UA_Boolean isCallerAC = false;
1569
    if(isConditionOrBranch(server, &ed->eventType, &ed->sourceNode, &isCallerAC) &&
1570
       !isCallerAC) {
1571
        UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
1572
                       "Condition Events: Please use A&C API to trigger Condition Events 0x%08X",
1573
                       UA_STATUSCODE_BADINVALIDARGUMENT);
1574
        return UA_STATUSCODE_BADINVALIDARGUMENT;
1575
    }
1576
#endif /* UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS */
1577
1578
    /* The user must ensure that only nodes from the Objects folder (and Views)
1579
     * emit events. The following commented code could enforce this, but is
1580
     * computationally expensive. */
1581
1582
    /* static const UA_NodeId objectsFolderId = */
1583
    /*     {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_OBJECTSFOLDER}}; */
1584
    /* static const UA_NodeId isInFolderReferences[2] = */
1585
    /*     {{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}}, */
1586
    /*      {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}}; */
1587
    /* UA_ReferenceTypeSet refTypes; */
1588
    /* UA_ReferenceTypeSet_init(&refTypes); */
1589
    /* for(int i = 0; i < 2; ++i) { */
1590
    /*     UA_ReferenceTypeSet tmpRefTypes; */
1591
    /*     res = referenceTypeIndices(server, &isInFolderReferences[i], &tmpRefTypes, true); */
1592
    /*     if(res != UA_STATUSCODE_GOOD) { */
1593
    /*         UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER, */
1594
    /*                        "Events: Could not create the list of references and their subtypes " */
1595
    /*                        "with StatusCode %s", UA_StatusCode_name(res)); */
1596
    /*     } */
1597
    /*     refTypes = UA_ReferenceTypeSet_union(refTypes, tmpRefTypes); */
1598
    /* } */
1599
    /* if(!isNodeInTree(server, &origin, &objectsFolderId, &refTypes)) { */
1600
    /*     UA_LOG_ERROR(server->config.logging, UA_LOGCATEGORY_USERLAND, */
1601
    /*                  "Node for event must be in ObjectsFolder!"); */
1602
    /*     return UA_STATUSCODE_BADINVALIDARGUMENT; */
1603
    /* } */
1604
1605
    /* Get all ReferenceTypes over which the events propagate */
1606
0
    UA_ReferenceTypeSet emitRefTypes;
1607
0
    UA_ReferenceTypeSet_init(&emitRefTypes);
1608
0
    for(size_t i = 0; i < EMIT_REFS_ROOT_COUNT; i++) {
1609
0
        UA_ReferenceTypeSet tmpRefTypes;
1610
0
        res = referenceTypeIndices(server, &emitReferencesRoots[i], &tmpRefTypes, true);
1611
0
        if(res != UA_STATUSCODE_GOOD) {
1612
0
            UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
1613
0
                           "Events: Could not create the list of references for event "
1614
0
                           "propagation with StatusCode %s", UA_StatusCode_name(res));
1615
0
            return res;
1616
0
        }
1617
0
        emitRefTypes = UA_ReferenceTypeSet_union(emitRefTypes, tmpRefTypes);
1618
0
    }
1619
1620
    /* Get the list of nodes in the hierarchy that emits the event. Add the
1621
     * server node to the list of nodes from which the event is emitted. The
1622
     * server node emits all events.
1623
     *
1624
     * Part 3, 7.17: In particular, the root notifier of a Server, the Server
1625
     * Object defined in Part 5, is always capable of supplying all Events from
1626
     * a Server and as such has implied HasEventSource References to every event
1627
     * source in a Server. */
1628
0
    UA_NodeId emitStartNodes[2];
1629
0
    emitStartNodes[0] = ed->sourceNode;
1630
0
    emitStartNodes[1] = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
1631
1632
0
    UA_ExpandedNodeId *emitNodes = NULL;
1633
0
    size_t emitNodesSize = 0;
1634
0
    res = browseRecursive(server, 2, emitStartNodes, UA_BROWSEDIRECTION_INVERSE,
1635
0
                          &emitRefTypes, UA_NODECLASS_UNSPECIFIED, true,
1636
0
                          &emitNodesSize, &emitNodes);
1637
0
    if(res != UA_STATUSCODE_GOOD) {
1638
0
        UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
1639
0
                       "Events: Could not create the list of nodes listening on the "
1640
0
                       "event with StatusCode %s", UA_StatusCode_name(res));
1641
0
        return res;
1642
0
    }
1643
1644
    /* Set up the eval context. */
1645
0
    UA_FilterEvalContext ctx;
1646
0
    UA_FilterEvalContext_init(&ctx);
1647
0
    ctx.server = server;
1648
0
    ctx.ed = *ed;
1649
    /* ctx.session is set below for each MonitoredItem */
1650
    /* ctx.filter is set below for each MonitoredItem */
1651
1652
    /* Create / resolve the EventId and copy into outEventId */
1653
0
    if(outEventId) {
1654
0
        ctx.session = &server->adminSession;
1655
0
        res = cacheEventId(&ctx);
1656
0
        if(UA_LIKELY(res == UA_STATUSCODE_GOOD))
1657
0
            res = UA_ByteString_copy(&ctx.eventId, outEventId);
1658
0
        UA_FilterEvalContext_reset(&ctx);
1659
0
        if(res != UA_STATUSCODE_GOOD)
1660
0
            return res;
1661
0
    }
1662
1663
    /* Loop over all nodes that emit this event instance */
1664
0
    for(size_t i = 0; i < emitNodesSize; i++) {
1665
        /* We can only emit on local nodes */
1666
0
        if(!UA_ExpandedNodeId_isLocal(&emitNodes[i]))
1667
0
            continue;
1668
1669
        /* Get the node */
1670
0
        const UA_Node *node = UA_NODESTORE_GET(server, &emitNodes[i].nodeId);
1671
0
        if(!node)
1672
0
            continue;
1673
1674
        /* Only consider objects */
1675
0
        if(node->head.nodeClass != UA_NODECLASS_OBJECT) {
1676
0
            UA_NODESTORE_RELEASE(server, node);
1677
0
            continue;
1678
0
        }
1679
1680
        /* Iterate over all MonitoredItems registered in the node  */
1681
0
        UA_MonitoredItem *mon;
1682
0
        LIST_FOREACH(mon, (const UA_MonitoredItemList*)&node->head.monitoredItems,
1683
0
                     sampling.nodeListEntry) {
1684
            /* Is this an Event-MonitoredItem? */
1685
0
            if(mon->itemToMonitor.attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER)
1686
0
                continue;
1687
1688
            /* Filter on the Session. If a subscription is not attached to a
1689
             * session, then this filter never matches. */
1690
0
            UA_Subscription *sub = mon->subscription;
1691
0
            if(ed->sessionId && (!sub->session || !UA_NodeId_equal(ed->sessionId, &sub->session->sessionId)))
1692
0
                continue;
1693
1694
            /* Filter on the SubscriptionId */
1695
0
            if(ed->subscriptionId && *ed->subscriptionId != sub->subscriptionId)
1696
0
                continue;
1697
1698
            /* Filter on the MonitoredItemId */
1699
0
            if(ed->monitoredItemId && *ed->monitoredItemId != mon->monitoredItemId)
1700
0
                continue;
1701
1702
            /* Get the EventFilter from the MonitoredItem */
1703
0
            if(!UA_ExtensionObject_hasDecodedType(&mon->parameters.filter,
1704
0
                                                  &UA_TYPES[UA_TYPES_EVENTFILTER])) {
1705
0
                UA_LOG_ERROR_SUBSCRIPTION(server->config.logging, mon->subscription,
1706
0
                                          "MonitoredItem %" PRIi32 " | "
1707
0
                                          "The filter must be an EventFilter", mon->monitoredItemId);
1708
0
                continue;
1709
0
            }
1710
0
            ctx.filter = *(UA_EventFilter*)mon->parameters.filter.content.decoded.data;
1711
1712
            /* Do not evaluate event fields for detached subscriptions.
1713
             * Using adminSession would bypass per-session access control. */
1714
0
            if(!sub->session)
1715
0
                continue;
1716
0
            ctx.session = sub->session;
1717
1718
            /* Evaluate the where-clause and create a notification */
1719
0
            res = UA_MonitoredItem_addEvent(mon, &ctx);
1720
0
            UA_FilterEvalContext_reset(&ctx);
1721
0
            if(res != UA_STATUSCODE_GOOD) {
1722
0
                UA_LOG_WARNING(server->config.logging, UA_LOGCATEGORY_SERVER,
1723
0
                               "Events: Could not add the event to a listening "
1724
0
                               "node with StatusCode %s", UA_StatusCode_name(res));
1725
0
                res = UA_STATUSCODE_GOOD; /* Only log problems with individual emit nodes */
1726
0
            }
1727
0
        }
1728
1729
0
        UA_NODESTORE_RELEASE(server, node);
1730
1731
        /* Add event entry in the historical database */
1732
0
#ifdef UA_ENABLE_HISTORIZING
1733
0
        if(server->config.historyDatabase.setEvent)
1734
0
            setHistoricalEvent(server, &emitNodes[i].nodeId, ed);
1735
0
#endif
1736
0
    }
1737
1738
    /* Clean up and return */
1739
0
    if(outEventId && res != UA_STATUSCODE_GOOD)
1740
0
        UA_ByteString_clear(outEventId);
1741
0
    UA_Array_delete(emitNodes, emitNodesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
1742
0
    return res;
1743
0
}
1744
1745
UA_StatusCode
1746
UA_Server_createEvent(UA_Server *server, const UA_NodeId sourceNode,
1747
                      const UA_NodeId eventType, UA_UInt16 severity,
1748
                      const UA_LocalizedText message,
1749
                      const UA_KeyValueMap *eventFields,
1750
                      const UA_NodeId *eventInstance,
1751
0
                      UA_ByteString *outEventId) {
1752
0
    UA_EventDescription ed = {0};
1753
0
    ed.sourceNode = sourceNode;
1754
0
    ed.eventType = eventType;
1755
0
    ed.severity = severity;
1756
0
    ed.message = message;
1757
0
    ed.eventFields = eventFields;
1758
0
    ed.eventInstance = eventInstance;
1759
1760
0
    lockServer(server);
1761
0
    UA_StatusCode res = createEvent(server, &ed, outEventId);
1762
0
    unlockServer(server);
1763
0
    return res;
1764
0
}
1765
1766
UA_StatusCode UA_EXPORT UA_THREADSAFE
1767
UA_Server_createEventEx(UA_Server *server,
1768
                        const UA_EventDescription *ed,
1769
0
                        UA_ByteString *outEventId) {
1770
0
    lockServer(server);
1771
0
    UA_StatusCode res = createEvent(server, ed, outEventId);
1772
0
    unlockServer(server);
1773
0
    return res;
1774
0
}
1775
1776
#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */