]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/c-apps/oils_utils.c
Whitespace. gah.
[working/Evergreen.git] / Open-ILS / src / c-apps / oils_utils.c
1 #include <ctype.h>
2 #include <limits.h>
3 #include "openils/oils_utils.h"
4 #include "openils/oils_idl.h"
5
6 osrfHash* oilsInitIDL(const char* idl_filename) {
7
8         char* freeable_filename = NULL;
9         const char* filename;
10
11         if(idl_filename)
12                 filename = idl_filename;
13         else {
14                 freeable_filename = osrf_settings_host_value("/IDL");
15                 filename = freeable_filename;
16         }
17
18         if (!filename) {
19                 osrfLogError(OSRF_LOG_MARK, "No settings config for '/IDL'");
20                 return NULL;
21         }
22
23         osrfLogInfo(OSRF_LOG_MARK, "Parsing IDL %s", filename);
24
25         if (!oilsIDLInit( filename )) {
26                 osrfLogError(OSRF_LOG_MARK, "Problem loading IDL file [%s]!", filename);
27                 if(freeable_filename)
28                         free(freeable_filename);
29                 return NULL;
30         }
31
32         if(freeable_filename)
33                 free(freeable_filename);
34         return oilsIDL();
35 }
36
37 /**
38         @brief Return a const string with the value of a specified column in a row object.
39         @param object Pointer to the row object.
40         @param field Name of the column.
41         @return Pointer to a const string representing the value of the specified column,
42                 or NULL in case of error.
43
44         The row object must be a JSON_ARRAY with a classname.  The column value must be a
45         JSON_STRING or a JSON_NUMBER.  Any other object type results in a return of NULL.
46
47         The return value points into the internal contents of the row object, which
48         retains ownership.
49 */
50 const char* oilsFMGetStringConst( const jsonObject* object, const char* field ) {
51         return jsonObjectGetString(oilsFMGetObject( object, field ));
52 }
53
54 /**
55         @brief Return a string with the value of a specified column in a row object.
56         @param object Pointer to the row object.
57         @param field Name of the column.
58         @return Pointer to a newly allocated string representing the value of the specified column,
59                 or NULL in case of error.
60
61         The row object must be a JSON_ARRAY with a classname.  The column value must be a
62         JSON_STRING or a JSON_NUMBER.  Any other object type results in a return of NULL.
63
64         The calling code is responsible for freeing the returned string by calling free().
65  */
66 char* oilsFMGetString( const jsonObject* object, const char* field ) {
67         return jsonObjectToSimpleString(oilsFMGetObject( object, field ));
68 }
69
70 /**
71         @brief Return a pointer to the value of a specified column in a row object.
72         @param object Pointer to the row object.
73         @param field Name of the column.
74         @return Pointer to the jsonObject representing the value of the specified column, or NULL
75                 in case of error.
76
77         The row object must be a JSON_ARRAY with a classname.
78
79         The return value may point to a JSON_NULL, JSON_STRING, JSON_NUMBER, or JSON_ARRAY.  It
80         points into the internal contents of the row object, which retains ownership.
81 */
82 const jsonObject* oilsFMGetObject( const jsonObject* object, const char* field ) {
83         if(!(object && field)) return NULL;
84         if( object->type != JSON_ARRAY || !object->classname ) return NULL;
85         int pos = fm_ntop(object->classname, field);
86         if( pos > -1 )
87                 return jsonObjectGetIndex( object, pos );
88         return NULL;
89 }
90
91
92 int oilsFMSetString( jsonObject* object, const char* field, const char* string ) {
93         if(!(object && field && string)) return -1;
94         osrfLogInternal(OSRF_LOG_MARK, "oilsFMSetString(): Collecing position for field %s", field);
95         int pos = fm_ntop(object->classname, field);
96         if( pos > -1 ) {
97                 osrfLogInternal(OSRF_LOG_MARK, "oilsFMSetString(): Setting string "
98                                 "%s at field %s [position %d]", string, field, pos );
99                 jsonObjectSetIndex( object, pos, jsonNewObject(string) );
100                 return 0;
101         }
102         return -1;
103 }
104
105
106 int oilsUtilsIsDBTrue( const char* val ) {
107         if( val && strcasecmp(val, "f") && strcmp(val, "0") ) return 1;
108         return 0;
109 }
110
111
112 long oilsFMGetObjectId( const jsonObject* obj ) {
113         long id = -1;
114         if(!obj) return id;
115         char* ids = oilsFMGetString( obj, "id" );
116         if(ids) {
117                 id = atol(ids);
118                 free(ids);
119         }
120         return id;
121 }
122
123
124 oilsEvent* oilsUtilsCheckPerms( int userid, int orgid, char* permissions[], int size ) {
125         if (!permissions) return NULL;
126         int i;
127         oilsEvent* evt = NULL;
128
129         // Find the root org unit, i.e. the one with no parent.
130         // Assumption: there is only one org unit with no parent.
131         if (orgid == -1) {
132                 jsonObject* where_clause = jsonParse( "{\"parent_ou\":null}" );
133                 jsonObject* org = oilsUtilsQuickReq(
134                         "open-ils.cstore",
135                         "open-ils.cstore.direct.actor.org_unit.search",
136                         where_clause
137                 );
138                 jsonObjectFree( where_clause );
139
140                 orgid = (int)jsonObjectGetNumber( oilsFMGetObject( org, "id" ) );
141
142                 jsonObjectFree(org);
143         }
144
145         for( i = 0; i < size && permissions[i]; i++ ) {
146
147                 char* perm = permissions[i];
148                 jsonObject* params = jsonParseFmt("[%d, \"%s\", %d]", userid, perm, orgid);
149                 jsonObject* o = oilsUtilsQuickReq( "open-ils.storage",
150                         "open-ils.storage.permission.user_has_perm", params );
151
152                 char* r = jsonObjectToSimpleString(o);
153
154                 if(r && !strcmp(r, "0"))
155                         evt = oilsNewEvent3( OSRF_LOG_MARK, OILS_EVENT_PERM_FAILURE, perm, orgid );
156
157                 jsonObjectFree(params);
158                 jsonObjectFree(o);
159                 free(r);
160
161                 if(evt)
162                         break;
163         }
164
165         return evt;
166 }
167
168 /**
169         @brief Perform a remote procedure call.
170         @param service The name of the service to invoke.
171         @param method The name of the method to call.
172         @param params The parameters to be passed to the method, if any.
173         @return A copy of whatever the method returns as a result, or a JSON_NULL if the method
174         doesn't return anything.
175
176         If the @a params parameter points to a JSON_ARRAY, pass each element of the array
177         as a separate parameter.  If it points to any other kind of jsonObject, pass it as a
178         single parameter.  If it is NULL, pass no parameters.
179
180         The calling code is responsible for freeing the returned object by calling jsonObjectFree().
181 */
182 jsonObject* oilsUtilsQuickReq( const char* service, const char* method,
183                 const jsonObject* params ) {
184         if(!(service && method)) return NULL;
185
186         osrfLogDebug(OSRF_LOG_MARK, "oilsUtilsQuickReq(): %s - %s", service, method );
187
188         // Open an application session with the service, and send the request
189         osrfAppSession* session = osrfAppSessionClientInit( service );
190         int reqid = osrfAppSessionSendRequest( session, params, method, 1 );
191
192         // Get the response
193         osrfMessage* omsg = osrfAppSessionRequestRecv( session, reqid, 60 );
194         jsonObject* result = jsonObjectClone( osrfMessageGetResult(omsg) );
195
196         // Clean up
197         osrfMessageFree(omsg);
198         osrfAppSessionFree(session);
199         return result;
200 }
201
202 /**
203         @brief Call a method of the open-ils.storage service.
204         @param method Name of the method.
205         @param params Parameters to be passed to the method, if any.
206         @return A copy of whatever the method returns as a result, or a JSON_NULL if the method
207         doesn't return anything.
208
209         If the @a params parameter points to a JSON_ARRAY, pass each element of the array
210         as a separate parameter.  If it points to any other kind of jsonObject, pass it as a
211         single parameter.  If it is NULL, pass no parameters.
212
213         The calling code is responsible for freeing the returned object by calling jsonObjectFree().
214 */
215 jsonObject* oilsUtilsStorageReq( const char* method, const jsonObject* params ) {
216         return oilsUtilsQuickReq( "open-ils.storage", method, params );
217 }
218
219 /**
220         @brief Call a method of the open-ils.cstore service.
221         @param method Name of the method.
222         @param params Parameters to be passed to the method, if any.
223         @return A copy of whatever the method returns as a result, or a JSON_NULL if the method
224         doesn't return anything.
225
226         If the @a params parameter points to a JSON_ARRAY, pass each element of the array
227         as a separate parameter.  If it points to any other kind of jsonObject, pass it as a
228         single parameter.  If it is NULL, pass no parameters.
229
230         The calling code is responsible for freeing the returned object by calling jsonObjectFree().
231 */
232 jsonObject* oilsUtilsCStoreReq( const char* method, const jsonObject* params ) {
233         return oilsUtilsQuickReq("open-ils.cstore", method, params);
234 }
235
236
237
238 /**
239         @brief Given a username, fetch the corresponding row from the actor.usr table, if any.
240         @param name The username for which to search.
241         @return A Fieldmapper object for the relevant row in the actor.usr table, if it exists;
242         or a JSON_NULL if it doesn't.
243
244         The calling code is responsible for freeing the returned object by calling jsonObjectFree().
245 */
246 jsonObject* oilsUtilsFetchUserByUsername( const char* name ) {
247         if(!name) return NULL;
248         jsonObject* params = jsonParseFmt("{\"usrname\":\"%s\"}", name);
249         jsonObject* user = oilsUtilsQuickReq(
250                 "open-ils.cstore", "open-ils.cstore.direct.actor.user.search", params );
251
252         jsonObjectFree(params);
253         long id = oilsFMGetObjectId(user);
254         osrfLogDebug(OSRF_LOG_MARK, "Fetched user %s:%ld", name, id);
255         return user;
256 }
257
258 /**
259         @brief Given a barcode, fetch the corresponding row from the actor.usr table, if any.
260         @param name The barcode for which to search.
261         @return A Fieldmapper object for the relevant row in the actor.usr table, if it exists;
262         or a JSON_NULL if it doesn't.
263
264         Look up the barcode in actor.card.  Follow a foreign key from there to get a row in
265         actor.usr.
266
267         The calling code is responsible for freeing the returned object by calling jsonObjectFree().
268 */
269 jsonObject* oilsUtilsFetchUserByBarcode(const char* barcode) {
270         if(!barcode) return NULL;
271
272         osrfLogInfo(OSRF_LOG_MARK, "Fetching user by barcode %s", barcode);
273
274         jsonObject* params = jsonParseFmt("{\"barcode\":\"%s\"}", barcode);
275         jsonObject* card = oilsUtilsQuickReq(
276                 "open-ils.cstore", "open-ils.cstore.direct.actor.card.search", params );
277         jsonObjectFree(params);
278
279         if(!card)
280                 return NULL;   // No such card
281
282         // Get the user's id as a double
283         char* usr = oilsFMGetString(card, "usr");
284         jsonObjectFree(card);
285         if(!usr)
286                 return NULL;   // No user id (shouldn't happen)
287         double iusr = strtod(usr, NULL);
288         free(usr);
289
290         // Look up the user in actor.usr
291         params = jsonParseFmt("[%f]", iusr);
292         jsonObject* user = oilsUtilsQuickReq(
293                 "open-ils.cstore", "open-ils.cstore.direct.actor.user.retrieve", params);
294
295         jsonObjectFree(params);
296         return user;
297 }
298
299 char* oilsUtilsFetchOrgSetting( int orgid, const char* setting ) {
300         if(!setting) return NULL;
301
302         jsonObject* params = jsonParseFmt("[%d, \"%s\"]", orgid, setting );
303
304         jsonObject* set = oilsUtilsQuickReq(
305                 "open-ils.actor",
306                 "open-ils.actor.ou_setting.ancestor_default", params);
307
308         char* value = jsonObjectToSimpleString( jsonObjectGetKeyConst( set, "value" ));
309         jsonObjectFree(params);
310         jsonObjectFree(set);
311         osrfLogDebug(OSRF_LOG_MARK, "Fetched org [%d] setting: %s => %s", orgid, setting, value);
312         return value;
313 }
314
315
316
317 char* oilsUtilsLogin( const char* uname, const char* passwd, const char* type, int orgId ) {
318         if(!(uname && passwd)) return NULL;
319
320         osrfLogDebug(OSRF_LOG_MARK, "Logging in with username %s", uname );
321         char* token = NULL;
322
323         jsonObject* params = jsonParseFmt("[\"%s\"]", uname);
324
325         jsonObject* o = oilsUtilsQuickReq(
326                 "open-ils.auth", "open-ils.auth.authenticate.init", params );
327
328         const char* seed = jsonObjectGetString(o);
329         char* passhash = md5sum(passwd);
330         char buf[256];
331         snprintf(buf, sizeof(buf), "%s%s", seed, passhash);
332         char* fullhash = md5sum(buf);
333
334         jsonObjectFree(o);
335         jsonObjectFree(params);
336         free(passhash);
337
338         params = jsonParseFmt( "[\"%s\", \"%s\", \"%s\", \"%d\"]", uname, fullhash, type, orgId );
339         o = oilsUtilsQuickReq( "open-ils.auth",
340                 "open-ils.auth.authenticate.complete", params );
341
342         if(o) {
343                 const char* tok = jsonObjectGetString(
344                         jsonObjectGetKeyConst( jsonObjectGetKey( o,"payload" ), "authtoken" ));
345                 if( tok )
346                         token = strdup( tok );
347         }
348
349         free(fullhash);
350         jsonObjectFree(params);
351         jsonObjectFree(o);
352
353         return token;
354 }
355
356
357 jsonObject* oilsUtilsFetchWorkstation( long id ) {
358         jsonObject* p = jsonParseFmt("[%ld]", id);
359         jsonObject* r = oilsUtilsQuickReq(
360                 "open-ils.storage",
361                 "open-ils.storage.direct.actor.workstation.retrieve", p );
362         jsonObjectFree(p);
363         return r;
364 }
365
366 jsonObject* oilsUtilsFetchWorkstationByName( const char* name ) {
367         jsonObject* p = jsonParseFmt("{\"name\":\"%s\"}", name);
368         jsonObject* r = oilsUtilsCStoreReq(
369                 "open-ils.cstore.direct.actor.workstation.search", p);
370         jsonObjectFree(p);
371         return r;
372 }
373
374 /**
375         @brief Convert a string to a number representing a time interval in seconds.
376         @param interval Pointer to string, e.g. "420" or "2 weeks".
377         @return If successful, the number of seconds that the string represents; otherwise -1.
378
379         If the string is all digits (apart from any leading or trailing white space), convert
380         it directly.  Otherwise pass it to PostgreSQL for translation.
381
382         The result is the same as if we were to pass every string to PostgreSQL, except that,
383         depending on the value of LONG_MAX, we return values for some strings that represent
384         intervals too long for PostgreSQL to represent (i.e. more than 2147483647 seconds).
385
386         WARNING: a valid interval of -1 second will be indistinguishable from an error.  If
387         such an interval is a plausible possibility, don't use this function.
388 */
389 long oilsUtilsIntervalToSeconds( const char* s ) {
390
391         if( !s ) {
392                 osrfLogWarning( OSRF_LOG_MARK, "String to be converted is NULL" );
393                 return -1;
394         }
395
396         // Skip leading white space
397         while( isspace( (unsigned char) *s ))
398                 ++s;
399
400         if( '\0' == *s ) {
401                 osrfLogWarning( OSRF_LOG_MARK, "String to be converted is empty or all white space" );
402                 return -1;
403         }
404
405         // See if the string is a raw number, i.e. all digits
406         // (apart from any leading or trailing white space)
407
408         const char* p = s;   // For traversing and examining the remaining string
409         if( isdigit( (unsigned char) *p )) {
410                 // Looks like a number so far...skip over the digits
411                 do {
412                         ++p;
413                 } while( isdigit( (unsigned char) *p ));
414                 // Skip over any following white space
415                 while( isspace( (unsigned char) *p ))
416                         ++p;
417                 if( '\0' == *p ) {
418                         // This string is a raw number.  Convert it directly.
419                         long n = strtol( s, NULL, 10 );
420                         if( LONG_MAX == n ) {
421                                 // numeric overflow
422                                 osrfLogWarning( OSRF_LOG_MARK,
423                                         "String \"%s\"represents a number too big for a long", s );
424                                 return -1;
425                         } else
426                                 return n;
427                 }
428         }
429
430         // If we get to this point, the string is not all digits.  Pass it to PostgreSQL.
431
432         // Build the query
433         jsonObject* query_obj = jsonParseFmt(
434                 "{\"from\":[\"config.interval_to_seconds\",\"%s\"]}", s );
435
436         // Execute the query
437         jsonObject* result = oilsUtilsCStoreReq(
438                 "open-ils.cstore.json_query", query_obj );
439         jsonObjectFree( query_obj );
440
441         // Get the results
442         const jsonObject* seconds_obj = jsonObjectGetKeyConst( result, "config.interval_to_seconds" );
443         long seconds = -1;
444         if( seconds_obj && JSON_NUMBER == seconds_obj->type )
445                 seconds = (long) jsonObjectGetNumber( seconds_obj );
446         else
447                 osrfLogError( OSRF_LOG_MARK,
448                         "Error calling json_query to convert \"%s\" to seconds", s );
449
450         jsonObjectFree( result );
451         return seconds;
452 }