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