1 #include "openils/oils_event.h"
2 #include <libxml/parser.h>
3 #include <libxml/tree.h>
4 #include "opensrf/osrf_settings.h"
6 const char default_lang[] = "en-US";
8 static void _oilsEventParseEvents();
9 static const char* lookup_desc( const char* lang, const char* code );
11 // The following two osrfHashes are created when we
12 // create the first osrfEvent, and are never freed.
15 @brief Lookup store mapping event names to event numbers.
17 - Key: textcode from the events config file.
18 - Data: numeric code (as a string) from the events config file.
20 static osrfHash* _oilsEventEvents = NULL;
23 @brief Lookup store mapping event numbers to descriptive text.
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.
30 static osrfHash* _oilsEventDescriptions = NULL;
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.
39 The first two parameters are normally passed as the OSRF_LOG_MARK macro.
41 The calling code is responsible for freeing the oilsEvent by calling oilsEventFree().
43 oilsEvent* oilsNewEvent( const char* file, int line, const char* event ) {
44 if(!event) return NULL;
46 osrfLogInfo(OSRF_LOG_MARK, "Creating new event: %s", event);
49 _oilsEventParseEvents();
51 oilsEvent* evt = safe_malloc( sizeof(oilsEvent) );
52 evt->event = strdup(event);
59 evt->file = strdup(file);
61 evt->file = strdup( "" );
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.
75 The first two parameters are normally passed as the OSRF_LOG_MARK macro.
77 The calling code is responsible for freeing the oilsEvent by calling oilsEventFree().
79 oilsEvent* oilsNewEvent2( const char* file, int line, const char* event,
80 const jsonObject* payload ) {
81 oilsEvent* evt = oilsNewEvent(file, line, event);
84 evt->payload = jsonObjectClone(payload);
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.
98 The first two parameters are normally passed as the OSRF_LOG_MARK macro.
100 The calling code is responsible for freeing the oilsEvent by calling oilsEventFree().
102 oilsEvent* oilsNewEvent3( const char* file, int line, const char* event,
103 const char* perm, int permloc ) {
104 oilsEvent* evt = oilsNewEvent(file, line, event);
106 evt->perm = strdup(perm);
107 evt->permloc = permloc;
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.
122 The first two parameters are normally passed as the OSRF_LOG_MARK macro.
124 The calling code is responsible for freeing the oilsEvent by calling oilsEventFree().
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 );
131 evt->payload = jsonObjectClone(payload);
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.
142 void oilsEventSetPermission( oilsEvent* event, const char* perm, int permloc ) {
143 if(!(event && perm)) return;
148 event->perm = strdup(perm);
149 event->permloc = permloc;
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.
157 If @a payload is NULL, install a JSON_NULL as the payload.
159 void oilsEventSetPayload( oilsEvent* event, const jsonObject* payload ) {
160 if(!(event && payload)) return;
163 jsonObjectFree(event->payload);
165 event->payload = jsonObjectClone(payload);
169 @brief Free an OilsEvent.
170 @param event Pointer to the oilsEvent to be freed.
172 void oilsEventFree( oilsEvent* event ) {
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.
181 jsonObjectFree(event->json);
183 jsonObjectFree(event->payload);
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.
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".
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
201 jsonObject* oilsEventToJSON( oilsEvent* event ) {
202 if(!event) return NULL;
204 char* code = osrfHashGet( _oilsEventEvents, event->event );
206 osrfLogError(OSRF_LOG_MARK, "No such event name: %s", event->event );
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
217 desc = ""; // Not found? Default to an empty string.
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()) );
226 snprintf(buf, sizeof(buf), "%s:%d", event->file, event->line);
227 jsonObjectSetKey( json, "stacktrace", jsonNewObject(buf) );
230 jsonObjectSetKey( json, "ilsperm", jsonNewObject(event->perm) );
232 if(event->permloc != -1)
233 jsonObjectSetKey( json, "ilspermloc", jsonNewNumberObject(event->permloc) );
236 jsonObjectSetKey( json, "payload", event->payload );
239 jsonObjectFree(event->json);
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.
251 The lookup has two stages. First we look up the language, and then within that
252 language we look up the code.
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 );
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 );
265 osrfLogDebug( OSRF_LOG_MARK, "Found event description %s", desc );
267 osrfLogDebug( OSRF_LOG_MARK, "Event description not found for code %s", code );
273 @brief Parse and load the events file.
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).
280 static void _oilsEventParseEvents() {
282 char* xml = osrf_settings_host_value("/ils_events");
285 osrfLogError(OSRF_LOG_MARK, "Unable to find ILS Events file: %s", xml);
289 xmlDocPtr doc = xmlParseFile(xml);
292 _oilsEventEvents = osrfNewHash();
293 _oilsEventDescriptions = osrfNewHash();
296 xmlNodePtr root = xmlDocGetRootElement(doc);
298 xmlNodePtr child = root->children;
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 );
308 /* here we collect all of the <desc> nodes on the event
309 * element and store them based on the xml:lang attribute
311 xmlNodePtr desc = child->children;
313 if( !strcmp((char*) desc->name, "desc") ) {
314 xmlChar* lang = xmlGetProp( desc, BAD_CAST "lang");
316 osrfLogInternal( OSRF_LOG_MARK,
317 "Loaded event lang: %s", (char*) lang );
318 osrfHash* langHash = osrfHashGet(
319 _oilsEventDescriptions, (char*) lang);
321 langHash = osrfNewHash();
322 osrfHashSet(_oilsEventDescriptions, langHash, (char*) lang);
326 && (content = (char*) desc->children->content) ) {
327 osrfLogInternal( OSRF_LOG_MARK,
328 "Loaded event desc: %s", content);
329 osrfHashSet( langHash, content, (char*) code );
342 osrfLogError(OSRF_LOG_MARK, " ! Unable to parse events file: %s", xml );