LP1779158 Angular7 and ng-lint updates
[Evergreen.git] / Open-ILS / src / c-apps / oils_event.c
1 #include "openils/oils_event.h"
2 #include <libxml/parser.h>
3 #include <libxml/tree.h>
4 #include "opensrf/osrf_settings.h"
5
6 const char default_lang[] = "en-US";
7
8 static void _oilsEventParseEvents();
9 static const char* lookup_desc( const char* lang, const char* code );
10
11 // The following two osrfHashes are created when we
12 // create the first osrfEvent, and are never freed.
13
14 /**
15         @brief Lookup store mapping event names to event numbers.
16
17         - Key: textcode from the events config file.
18         - Data: numeric code (as a string) from the events config file.
19 */
20 static osrfHash* _oilsEventEvents = NULL;
21
22 /**
23         @brief Lookup store mapping event numbers to descriptive text.
24
25         - Key: numeric code (as a string) of the event.
26         - Data: another layer of lookup, as follows:
27                 - Key: numeric code (as a string) of the event.
28                 - Data: text message describing the event.
29 */
30 static osrfHash* _oilsEventDescriptions = NULL;
31
32 /**
33         @brief Allocate and initialize a new oilsEvent.
34         @param file The name of the source file where oilsNewEvent is called.
35         @param line The line number in the source code where oilsNewEvent is called.
36         @param event A name or label for the event.
37         @return Pointer to the newly allocated oilsEvent.
38
39         The first two parameters are normally passed as the OSRF_LOG_MARK macro.
40
41         The calling code is responsible for freeing the oilsEvent by calling oilsEventFree().
42 */
43 oilsEvent* oilsNewEvent( const char* file, int line, const char* event ) {
44         if(!event) return NULL;
45
46         osrfLogInfo(OSRF_LOG_MARK, "Creating new event: %s", event);
47
48         if(!_oilsEventEvents)
49                 _oilsEventParseEvents();
50
51         oilsEvent* evt = safe_malloc( sizeof(oilsEvent) );
52         evt->event = strdup(event);
53         evt->perm = NULL;
54         evt->permloc = -1;
55         evt->payload = NULL;
56         evt->json = NULL;
57
58         if(file)
59                 evt->file = strdup(file);
60         else
61                 evt->file = strdup( "" );
62
63         evt->line = line;
64         return evt;
65 }
66
67 /**
68         @brief Allocate and initialize a new oilsEvent with a payload.
69         @param file The name of the source file where oilsNewEvent is called.
70         @param line The line number in the source code where oilsNewEvent is called.
71         @param event A name or label for the event.
72         @param payload The payload, of which a copy will be incorporated into the oilsEvent.
73         @return Pointer to the newly allocated oilsEvent.
74
75         The first two parameters are normally passed as the OSRF_LOG_MARK macro.
76
77         The calling code is responsible for freeing the oilsEvent by calling oilsEventFree().
78 */
79 oilsEvent* oilsNewEvent2( const char* file, int line, const char* event,
80                 const jsonObject* payload ) {
81         oilsEvent* evt = oilsNewEvent(file, line, event);
82
83         if(payload)
84                 evt->payload = jsonObjectClone(payload);
85
86         return evt;
87 }
88
89 /**
90         @brief Create a new oilsEvent with a permission and a permission location.
91         @param file The name of the source file where oilsNewEvent is called.
92         @param line The line number in the source code where oilsNewEvent is called.
93         @param event A name or label for the event.
94         @param perm The permission name.
95         @param permloc The permission location.
96         @return Pointer to the newly allocated oilsEvent.
97
98         The first two parameters are normally passed as the OSRF_LOG_MARK macro.
99
100         The calling code is responsible for freeing the oilsEvent by calling oilsEventFree().
101 */
102 oilsEvent* oilsNewEvent3( const char* file, int line, const char* event,
103                 const char* perm, int permloc ) {
104         oilsEvent* evt = oilsNewEvent(file, line, event);
105         if(perm) {
106                 evt->perm = strdup(perm);
107                 evt->permloc = permloc;
108         }
109         return evt;
110 }
111
112 /**
113         @brief Create a new oilsEvent with a permission and a permission location.
114         @param file The name of the source file where oilsNewEvent is called.
115         @param line The line number in the source code where oilsNewEvent is called.
116         @param event A name or label for the event.
117         @param perm The permission name.
118         @param permloc The permission location.
119         @param payload Pointer to the payload.
120         @return Pointer to the newly allocated oilsEvent.
121
122         The first two parameters are normally passed as the OSRF_LOG_MARK macro.
123
124         The calling code is responsible for freeing the oilsEvent by calling oilsEventFree().
125 */
126 oilsEvent* oilsNewEvent4( const char* file, int line, const char* event,
127                 const char* perm, int permloc, const jsonObject* payload ) {
128         oilsEvent* evt = oilsNewEvent3( file, line, event, perm, permloc );
129
130         if(payload)
131                 evt->payload = jsonObjectClone(payload);
132
133         return evt;
134 }
135
136 /**
137         @brief Set the permission and permission location of an oilsEvent.
138         @param event Pointer the oilsEvent whose permission and permission location are to be set.
139         @param perm The permission name.
140         @param permloc The permission location.
141 */
142 void oilsEventSetPermission( oilsEvent* event, const char* perm, int permloc ) {
143         if(!(event && perm)) return;
144
145         if(event->perm)
146                 free(event->perm);
147
148         event->perm = strdup(perm);
149         event->permloc = permloc;
150 }
151
152 /**
153         @brief Install a payload in an oilsEvent.
154         @param event The oilsEvent in which the payload is to be installed.
155         @param payload The payload, a copy of which will be installed in the oilsEvent.
156
157         If @a payload is NULL, install a JSON_NULL as the payload.
158 */
159 void oilsEventSetPayload( oilsEvent* event, const jsonObject* payload ) {
160         if(!(event && payload)) return;
161
162         if(event->payload)
163                 jsonObjectFree(event->payload);
164
165         event->payload = jsonObjectClone(payload);
166 }
167
168 /**
169         @brief Free an OilsEvent.
170         @param event Pointer to the oilsEvent to be freed.
171 */
172 void oilsEventFree( oilsEvent* event ) {
173         if(!event) return;
174         free(event->event);
175         free(event->perm);
176         free(event->file);
177
178         // If present, the jsonObject to which event->json will include a pointer to
179         // event->payload.  Hence we must avoid trying to free the payload twice.
180         if(event->json)
181                 jsonObjectFree(event->json);
182         else
183                 jsonObjectFree(event->payload);
184
185         free(event);
186 }
187
188 /**
189         @brief Package the contents of an oilsEvent into a jsonObject.
190         @param event Pointer to the oilsEvent whose contents are to be packaged.
191         @return Pointer to the newly created jsonObject if successful, or NULL if not.
192
193         The jsonObject will include a textual description of the event, as loaded from the
194         events file.  Although the events file may include text in multiple languages,
195         oilsEventToJSON() uses only those marked as "en-US".
196
197         A pointer to the resulting jsonObject will be stored in the oilsEvent.  Hence the calling
198         code should @em not free the returned jsonObject directly.  It will be freed by
199         oilsEventFree().
200 */
201 jsonObject* oilsEventToJSON( oilsEvent* event ) {
202         if(!event) return NULL;
203
204         char* code = osrfHashGet( _oilsEventEvents, event->event );
205         if(!code) {
206                 osrfLogError(OSRF_LOG_MARK, "No such event name: %s", event->event );
207                 return NULL;
208         }
209
210         // Look up the text message corresponding the code, preferably in the right language.
211         const char* lang = osrf_message_get_last_locale();
212         const char* desc = lookup_desc( lang, code );
213         if( !desc && strcmp( lang, default_lang ) )    // No luck?
214                 desc = lookup_desc( default_lang, code );  // Try the default language
215
216         if( !desc )
217                 desc = "";  // Not found?  Default to an empty string.
218
219         jsonObject* json = jsonNewObject(NULL);
220         jsonObjectSetKey( json, "ilsevent", jsonNewNumberObject(atoi(code)) );
221         jsonObjectSetKey( json, "textcode", jsonNewObject(event->event) );
222         jsonObjectSetKey( json, "desc", jsonNewObject(desc) );
223         jsonObjectSetKey( json, "pid", jsonNewNumberObject(getpid()) );
224
225         char buf[256] = "";
226         snprintf(buf, sizeof(buf), "%s:%d", event->file, event->line);
227         jsonObjectSetKey( json, "stacktrace", jsonNewObject(buf) );
228
229         if(event->perm)
230                 jsonObjectSetKey( json, "ilsperm", jsonNewObject(event->perm) );
231
232         if(event->permloc != -1)
233                 jsonObjectSetKey( json, "ilspermloc", jsonNewNumberObject(event->permloc) );
234
235         if(event->payload)
236                 jsonObjectSetKey( json, "payload", event->payload );
237
238         if(event->json)
239                 jsonObjectFree(event->json);
240
241         event->json = json;
242         return json;
243 }
244
245 /**
246         @brief Lookup up the descriptive text, in a given language, for a given event code.
247         @param lang The language (a.k.a. locale) of the desired message.
248         @param code The numeric code for the event, as a string.
249         return The corresponding descriptive text if found, or NULL if not.
250
251         The lookup has two stages.  First we look up the language, and then within that
252         language we look up the code.
253 */
254 static const char* lookup_desc( const char* lang, const char* code ) {
255         // Search for the right language
256         const char* desc = NULL;
257         osrfHash* lang_hash = osrfHashGet( _oilsEventDescriptions, lang );
258         if( lang_hash ) {
259                 // Within that language, search for the right message
260                 osrfLogDebug( OSRF_LOG_MARK, "Loaded event lang hash for %s", lang );
261                 desc = osrfHashGet( lang_hash, code );
262         }
263
264         if( desc )
265                 osrfLogDebug( OSRF_LOG_MARK, "Found event description %s", desc );
266         else
267                 osrfLogDebug( OSRF_LOG_MARK, "Event description not found for code %s", code );
268
269         return desc;
270 }
271
272 /**
273         @brief Parse and load the events file.
274
275         Get the name of the events file from previously loaded settings.  Open it and load
276         it into an xmlDoc.  Based on the contents of the xmlDoc, build two osrfHashes: one to
277         map event names to event numbers, and another to map event numbers to descriptive
278         text messages (actually one such hash for each supported language).
279 */
280 static void _oilsEventParseEvents() {
281
282         char* xml = osrf_settings_host_value("/ils_events");
283
284         if(!xml) {
285                 osrfLogError(OSRF_LOG_MARK, "Unable to find ILS Events file: %s", xml);
286                 return;
287         }
288
289         xmlDocPtr doc = xmlParseFile(xml);
290         free(xml);
291         int success = 0;
292         _oilsEventEvents = osrfNewHash();
293         _oilsEventDescriptions = osrfNewHash();
294
295         if( doc ) {
296                 xmlNodePtr root = xmlDocGetRootElement(doc);
297                 if( root ) {
298                         xmlNodePtr child = root->children;
299                         while( child ) {
300                                 if( !strcmp((char*) child->name, "event") ) {
301                                         xmlChar* code = xmlGetProp( child, BAD_CAST "code");
302                                         xmlChar* textcode = xmlGetProp( child, BAD_CAST "textcode");
303                                         if( code && textcode ) {
304                                                 osrfHashSet( _oilsEventEvents, code, (char*) textcode );
305                                                 success = 1;
306                                         }
307
308                                         /* here we collect all of the <desc> nodes on the event
309                                          * element and store them based on the xml:lang attribute
310                                          */
311                                         xmlNodePtr desc = child->children;
312                                         while(desc) {
313                                                 if( !strcmp((char*) desc->name, "desc") ) {
314                                                         xmlChar* lang = xmlGetProp( desc, BAD_CAST "lang");
315                                                         if(lang) {
316                                                                 osrfLogInternal( OSRF_LOG_MARK,
317                                                                         "Loaded event lang: %s", (char*) lang );
318                                                                 osrfHash* langHash = osrfHashGet(
319                                                                         _oilsEventDescriptions, (char*) lang);
320                                                                 if(!langHash) {
321                                                                         langHash = osrfNewHash();
322                                                                         osrfHashSet(_oilsEventDescriptions, langHash, (char*) lang);
323                                                                 }
324                                                                 char* content;
325                                                                 if( desc->children
326                                                                         && (content = (char*) desc->children->content) ) {
327                                                                         osrfLogInternal( OSRF_LOG_MARK,
328                                                                                 "Loaded event desc: %s", content);
329                                                                         osrfHashSet( langHash, content, (char*) code );
330                                                                 }
331                                                         }
332                                                 }
333                                                 desc = desc->next;
334                                         }
335                                 }
336                                 child = child->next;
337                         }
338                 }
339         }
340
341         if(!success)
342                 osrfLogError(OSRF_LOG_MARK, " ! Unable to parse events file: %s", xml );
343 }