Coverage Report

Created: 2026-04-06 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/S2OPC/src/ClientServer/services/b2c/util_event.c
Line
Count
Source
1
/*
2
 * Licensed to Systerel under one or more contributor license
3
 * agreements. See the NOTICE file distributed with this work
4
 * for additional information regarding copyright ownership.
5
 * Systerel licenses this file to you under the Apache
6
 * License, Version 2.0 (the "License"); you may not use this
7
 * file except in compliance with the License. You may obtain
8
 * a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
20
#include "util_event.h"
21
22
#include "constants.h"
23
#include "opcua_statuscodes.h"
24
#include "sopc_assert.h"
25
#include "sopc_date_time.h"
26
#include "sopc_macros.h"
27
#include "sopc_mem_alloc.h"
28
#include "sopc_toolkit_config_internal.h"
29
#include "util_b2c.h"
30
#include "util_variant.h"
31
32
static const SOPC_NodeId EventQueueOverflowTypeId = SOPC_NODEID_NS0_NUMERIC(OpcUaId_EventQueueOverflowEventType);
33
static const SOPC_NodeId ServerId = SOPC_NODEID_NS0_NUMERIC(OpcUaId_Server);
34
static const SOPC_NodeId BaseEventTypeId = SOPC_NODEID_NS0_NUMERIC(OpcUaId_BaseEventType);
35
36
static const SOPC_Event* queueOverflowEvent = NULL;
37
38
const SOPC_Server_Event_Types* util_event__get_event_types(void)
39
0
{
40
    // Use first EP as all are pointing to the same server configuration
41
0
    SOPC_Endpoint_Config* endpointConfig = SOPC_ToolkitServer_GetEndpointConfig(1);
42
0
    if (NULL != endpointConfig && NULL != endpointConfig->serverConfigPtr)
43
0
    {
44
0
        SOPC_Server_Config* serverConfig = endpointConfig->serverConfigPtr;
45
0
        return serverConfig->eventTypes;
46
0
    }
47
0
    else
48
0
    {
49
0
        return NULL;
50
0
    }
51
0
}
52
53
bool util_event__alloc_event_field_list(uint32_t clientHandle,
54
                                        int32_t nbSelectClauses,
55
                                        OpcUa_EventFieldList** ppEventFieldList)
56
0
{
57
0
    SOPC_ASSERT(NULL != ppEventFieldList);
58
0
    bool res = false;
59
0
    OpcUa_EventFieldList* eventFieldList = NULL;
60
0
    SOPC_ReturnStatus status =
61
0
        SOPC_EncodeableObject_Create(&OpcUa_EventFieldList_EncodeableType, (void**) &eventFieldList);
62
0
    if (SOPC_STATUS_OK == status)
63
0
    {
64
0
        eventFieldList->ClientHandle = clientHandle;
65
0
        eventFieldList->EventFields = SOPC_Calloc((size_t) nbSelectClauses, sizeof(*eventFieldList->EventFields));
66
0
        eventFieldList->NoOfEventFields = nbSelectClauses;
67
0
        if (NULL != eventFieldList->EventFields)
68
0
        {
69
0
            res = true;
70
0
            *ppEventFieldList = eventFieldList;
71
0
        }
72
0
        else
73
0
        {
74
0
            SOPC_Free(eventFieldList);
75
0
        }
76
0
    }
77
0
    return res;
78
0
}
79
80
static void applyDataValueTsRemoval(SOPC_DataValue* dv, bool removeSrc, bool removeSrv)
81
0
{
82
0
    SOPC_ASSERT(NULL != dv);
83
0
    if (removeSrc)
84
0
    {
85
0
        dv->SourceTimestamp = 0;
86
0
        dv->SourcePicoSeconds = 0;
87
0
    }
88
0
    if (removeSrv)
89
0
    {
90
0
        dv->ServerTimestamp = 0;
91
0
        dv->ServerPicoSeconds = 0;
92
0
    }
93
0
}
94
95
void util_event__set_event_field_list_elt(char** preferredLocalesIds,
96
                                          const constants__t_TimestampsToReturn_i timestampToReturn,
97
                                          bool userAccessGranted,
98
                                          int32_t selectClauseIdx,
99
                                          const SOPC_InternalMonitoredItemFilterCtx* pFilterCtx,
100
                                          const SOPC_Event* pEvent,
101
                                          OpcUa_EventFieldList* pEventFieldList)
102
0
{
103
0
    SOPC_Variant* selectVal = &pEventFieldList->EventFields[selectClauseIdx];
104
105
    /*
106
     * From part 4 (v1.05) §7.22.3:
107
     * For example, the Server shall set the event field to Bad_UserAccessDenied
108
     * if the value is not accessible to the user associated with the Session.
109
     * If a Value Attribute has an uncertain or bad StatusCode associated with it then the Server
110
     * shall provide the StatusCode instead of the Value Attribute.
111
     */
112
    // eventNotifier attribute is considered not accessible, return a bad status instead of value
113
0
    if (!userAccessGranted)
114
0
    {
115
0
        SOPC_Variant_Initialize(selectVal);
116
0
        selectVal->BuiltInTypeId = SOPC_StatusCode_Id;
117
0
        selectVal->Value.Status = OpcUa_BadUserAccessDenied;
118
0
        return;
119
0
    }
120
121
0
    SOPC_ASSERT(NULL != pFilterCtx && !pFilterCtx->isDataFilter);
122
0
    char* qnPath = pFilterCtx->Filter.Event.qnPathStrSelectClauses[selectClauseIdx];
123
0
    SOPC_NumericRange* indexRange = pFilterCtx->Filter.Event.indexRangeSelectClauses[selectClauseIdx];
124
0
    const SOPC_Variant* eventVar = SOPC_Event_GetVariableFromStrPath(pEvent, qnPath);
125
    // Restricts empty browse path for NodeId attribute
126
    // (other browse paths are for Value attribute only which is already checked on creation)
127
0
    if (NULL != eventVar && '\0' == *qnPath &&
128
0
        SOPC_AttributeId_NodeId != pFilterCtx->Filter.Event.eventFilter->SelectClauses[selectClauseIdx].AttributeId)
129
0
    {
130
0
        eventVar = NULL;
131
0
    }
132
133
0
    if (NULL != eventVar)
134
0
    {
135
0
        SOPC_ReturnStatus status =
136
0
            util_variant__copy_and_apply_locales_and_index_range(selectVal, eventVar, preferredLocalesIds, indexRange);
137
138
        /* Apply the TimestampToReturn in case of DataValue built it type:
139
         * "When monitoring Events, this applies only to Event fields that are of type DataValue.
140
         */
141
0
        if (SOPC_STATUS_OK == status && SOPC_DataValue_Id == selectVal->BuiltInTypeId)
142
0
        {
143
0
            bool removeSrc = false;
144
0
            bool removeSrv = false;
145
0
            switch (timestampToReturn)
146
0
            {
147
0
            case constants__e_ttr_source:
148
0
                removeSrv = true;
149
0
                break;
150
0
            case constants__e_ttr_server:
151
0
                removeSrc = true;
152
0
                break;
153
0
            case constants__e_ttr_neither:
154
0
                removeSrc = true;
155
0
                removeSrv = true;
156
0
                break;
157
0
            default:
158
                // Keep both in other cases
159
0
                break;
160
0
            }
161
162
0
            if (removeSrc || removeSrv)
163
0
            {
164
0
                if (SOPC_VariantArrayType_SingleValue == selectVal->ArrayType)
165
0
                {
166
0
                    applyDataValueTsRemoval(selectVal->Value.DataValue, removeSrc, removeSrv);
167
0
                }
168
0
                else
169
0
                {
170
0
                    int32_t length = SOPC_Variant_GetArrayOrMatrixLength(selectVal);
171
0
                    for (int32_t i = 0; i < length; i++)
172
0
                    {
173
0
                        applyDataValueTsRemoval(SOPC_VARIANT_GET_ARRAY_VALUES_PTR(selectVal, DataValue), removeSrc,
174
0
                                                removeSrv);
175
0
                    }
176
0
                }
177
0
            }
178
0
        }
179
0
    } // else: keep NULL variant selectVal content
180
0
}
181
182
static bool is_event_type_compatible_with_select_clause(const OpcUa_SimpleAttributeOperand* selectClause,
183
                                                        const SOPC_NodeId* eventTypeId)
184
0
{
185
0
    bool res = SOPC_NodeId_Equal(&BaseEventTypeId, &selectClause->TypeDefinitionId);
186
0
    if (!res)
187
0
    {
188
0
        res = SOPC_NodeId_Equal(&selectClause->TypeDefinitionId, eventTypeId);
189
0
    }
190
0
    return res;
191
0
}
192
193
bool util_event__alloc_and_fill_event_field_list(const SOPC_InternalMonitoredItemFilterCtx* pFilterCtx,
194
                                                 uint32_t clientHandle,
195
                                                 char** preferredLocalesIds,
196
                                                 const constants__t_TimestampsToReturn_i timestampToReturn,
197
                                                 bool userAccessGranted,
198
                                                 const SOPC_Event* pEvent,
199
                                                 OpcUa_EventFieldList** ppEventFieldList)
200
0
{
201
0
    SOPC_ASSERT(NULL != pFilterCtx);
202
0
    SOPC_ASSERT(NULL != pEvent);
203
0
    SOPC_ASSERT(NULL != ppEventFieldList);
204
0
    SOPC_ASSERT(!pFilterCtx->isDataFilter);
205
0
    int32_t nbSelectClauses = pFilterCtx->Filter.Event.eventFilter->NoOfSelectClauses;
206
0
    bool res = nbSelectClauses > 0;
207
0
    bool setField = false;
208
0
    if (res)
209
0
    {
210
0
        res = util_event__alloc_event_field_list(clientHandle, nbSelectClauses, ppEventFieldList);
211
0
    }
212
0
    if (res)
213
0
    {
214
0
        const SOPC_NodeId* eventTypeId = SOPC_Event_GetEventTypeId(pEvent);
215
0
        for (int32_t i = 0; i < nbSelectClauses; i++)
216
0
        {
217
0
            setField = is_event_type_compatible_with_select_clause(
218
0
                &pFilterCtx->Filter.Event.eventFilter->SelectClauses[i], eventTypeId);
219
0
            if (setField)
220
0
            {
221
0
                util_event__set_event_field_list_elt(preferredLocalesIds, timestampToReturn, userAccessGranted, i,
222
0
                                                     pFilterCtx, pEvent, *ppEventFieldList);
223
0
            }
224
0
        }
225
0
    }
226
227
0
    return res;
228
0
}
229
230
static SOPC_Event* create_queueOverflowEvent(void)
231
0
{
232
0
    if (NULL == queueOverflowEvent)
233
0
    {
234
0
        SOPC_Event* QOfEvent =
235
0
            SOPC_EventManager_CreateEventInstance(util_event__get_event_types(), &EventQueueOverflowTypeId);
236
0
        if (NULL == QOfEvent)
237
0
        {
238
            // Impossible to instantiate the event type
239
0
            return NULL;
240
0
        }
241
        /**
242
         * From part 5 (v1.05) §6.4.34:
243
         * The SourceNode Property for Events of this type shall be assigned to the NodeId of the Server
244
         * Object. The SourceName for Events of this type shall be "Internal/EventQueueOverflow".
245
         */
246
0
        SOPC_ReturnStatus status = SOPC_Event_SetSourceNode(QOfEvent, &ServerId);
247
0
        SOPC_String sourceName;
248
0
        SOPC_String_Initialize(&sourceName);
249
0
        if (SOPC_STATUS_OK == status)
250
0
        {
251
0
            status = SOPC_String_AttachFromCstring(&sourceName, "Internal/EventQueueOverflow");
252
0
        }
253
0
        if (SOPC_STATUS_OK == status)
254
0
        {
255
0
            status = SOPC_Event_SetSourceName(QOfEvent, &sourceName);
256
0
        }
257
0
        if (SOPC_STATUS_OK == status)
258
0
        {
259
0
            queueOverflowEvent = QOfEvent;
260
0
        }
261
0
        else
262
0
        {
263
0
            SOPC_Event_Delete(&QOfEvent);
264
0
        }
265
0
    }
266
267
0
    SOPC_Event* event = SOPC_Event_CreateCopy(queueOverflowEvent, true);
268
0
    if (NULL == event)
269
0
    {
270
0
        return NULL;
271
0
    }
272
273
0
    SOPC_ReturnStatus status = SOPC_Event_SetTime(event, SOPC_Time_GetCurrentTimeUTC());
274
    // We still want to generate the created queueOverflowEvent even if it fails
275
0
    SOPC_UNUSED_RESULT(status);
276
0
    return event;
277
0
}
278
279
bool util_event__gen_event_queue_overflow_notification(const SOPC_InternalMonitoredItemFilterCtx* pFilterCtx,
280
                                                       uint32_t clientHandle,
281
                                                       OpcUa_EventFieldList** ppEventFieldList)
282
0
{
283
0
    SOPC_Event* queueOverflowEventInst = create_queueOverflowEvent();
284
0
    if (NULL == queueOverflowEventInst)
285
0
    {
286
0
        return false;
287
0
    }
288
0
    bool res = util_event__alloc_and_fill_event_field_list(pFilterCtx, clientHandle, NULL, constants__e_ttr_both, true,
289
0
                                                           queueOverflowEventInst, ppEventFieldList);
290
0
    SOPC_Event_Delete(&queueOverflowEventInst);
291
0
    return res;
292
0
}