3 #include "openils/oils_utils.h"
4 #include "openils/oils_idl.h"
6 osrfHash* oilsInitIDL(const char* idl_filename) {
8 char* freeable_filename = NULL;
12 filename = idl_filename;
14 freeable_filename = osrf_settings_host_value("/IDL");
15 filename = freeable_filename;
19 osrfLogError(OSRF_LOG_MARK, "No settings config for '/IDL'");
23 osrfLogInfo(OSRF_LOG_MARK, "Parsing IDL %s", filename);
25 if (!oilsIDLInit( filename )) {
26 osrfLogError(OSRF_LOG_MARK, "Problem loading IDL file [%s]!", filename);
28 free(freeable_filename);
33 free(freeable_filename);
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.
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.
47 The return value points into the internal contents of the row object, which
50 const char* oilsFMGetStringConst( const jsonObject* object, const char* field ) {
51 return jsonObjectGetString(oilsFMGetObject( object, field ));
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.
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.
64 The calling code is responsible for freeing the returned string by calling free().
66 char* oilsFMGetString( const jsonObject* object, const char* field ) {
67 return jsonObjectToSimpleString(oilsFMGetObject( object, field ));
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
77 The row object must be a JSON_ARRAY with a classname.
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.
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);
87 return jsonObjectGetIndex( object, pos );
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);
97 osrfLogInternal(OSRF_LOG_MARK, "oilsFMSetString(): Setting string "
98 "%s at field %s [position %d]", string, field, pos );
99 jsonObjectSetIndex( object, pos, jsonNewObject(string) );
106 int oilsUtilsIsDBTrue( const char* val ) {
107 if( val && strcasecmp(val, "f") && strcmp(val, "0") ) return 1;
112 long oilsFMGetObjectId( const jsonObject* obj ) {
115 char* ids = oilsFMGetString( obj, "id" );
123 int oilsUtilsTrackUserActivity(osrfMethodContext* ctx, long usr, const char* ewho, const char* ewhat, const char* ehow) {
124 if (!usr && !(ewho || ewhat || ehow)) return 0;
127 jsonObject* params = jsonParseFmt(
128 "{\"from\":[\"actor.insert_usr_activity\", %ld, \"%s\", \"%s\", \"%s\"]}",
130 (NULL == ewho) ? "" : ewho,
131 (NULL == ewhat) ? "" : ewhat,
132 (NULL == ehow) ? "" : ehow
135 osrfAppSession* session = osrfAppSessionClientInit("open-ils.cstore");
136 osrfAppSessionConnect(session);
137 int reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.begin", 1);
138 osrfMessage* omsg = osrfAppSessionRequestRecv(session, reqid, 60);
141 osrfMessageFree(omsg);
142 reqid = osrfAppSessionSendRequest(session, params, "open-ils.cstore.json_query", 1);
143 omsg = osrfAppSessionRequestRecv(session, reqid, 60);
146 const jsonObject* rows = osrfMessageGetResult(omsg);
147 if (rows) rowcount = rows->size;
148 osrfMessageFree(omsg); // frees rows
150 reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.commit", 1);
151 omsg = osrfAppSessionRequestRecv(session, reqid, 60);
152 osrfMessageFree(omsg);
154 reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.rollback", 1);
155 omsg = osrfAppSessionRequestRecv(session, reqid, 60);
156 osrfMessageFree(omsg);
161 osrfAppSessionFree(session); // calls disconnect internally
162 jsonObjectFree(params);
167 static int rootOrgId = 0; // cache the ID of the root org unit.
168 int oilsUtilsGetRootOrgId() {
170 // return the cached value if we have it.
171 if (rootOrgId > 0) return rootOrgId;
173 jsonObject* where_clause = jsonParse("{\"parent_ou\":null}");
174 jsonObject* org = oilsUtilsQuickReq(
176 "open-ils.cstore.direct.actor.org_unit.search",
181 jsonObjectGetNumber(oilsFMGetObject(org, "id"));
183 jsonObjectFree(where_clause);
189 oilsEvent* oilsUtilsCheckPerms( int userid, int orgid, char* permissions[], int size ) {
190 if (!permissions) return NULL;
193 // Check perms against the root org unit if no org unit is provided.
195 orgid = oilsUtilsGetRootOrgId();
197 for( i = 0; i < size && permissions[i]; i++ ) {
198 oilsEvent* evt = NULL;
199 char* perm = permissions[i];
201 jsonObject* params = jsonParseFmt(
202 "{\"from\":[\"permission.usr_has_perm\",\"%d\",\"%s\",\"%d\"]}",
207 jsonObject* result = oilsUtilsCStoreReq(
208 "open-ils.cstore.json_query", params);
210 const jsonObject* hasPermStr =
211 jsonObjectGetKeyConst(result, "permission.usr_has_perm");
213 if (!oilsUtilsIsDBTrue(jsonObjectGetString(hasPermStr))) {
215 OSRF_LOG_MARK, OILS_EVENT_PERM_FAILURE, perm, orgid);
218 jsonObjectFree(params);
219 jsonObjectFree(result);
221 // return first failed permission check.
225 return NULL; // all perm checks succeeded
229 @brief Perform a remote procedure call.
230 @param service The name of the service to invoke.
231 @param method The name of the method to call.
232 @param params The parameters to be passed to the method, if any.
233 @return A copy of whatever the method returns as a result, or a JSON_NULL if the method
234 doesn't return anything.
236 If the @a params parameter points to a JSON_ARRAY, pass each element of the array
237 as a separate parameter. If it points to any other kind of jsonObject, pass it as a
238 single parameter. If it is NULL, pass no parameters.
240 The calling code is responsible for freeing the returned object by calling jsonObjectFree().
242 jsonObject* oilsUtilsQuickReq( const char* service, const char* method,
243 const jsonObject* params ) {
244 if(!(service && method)) return NULL;
246 osrfLogDebug(OSRF_LOG_MARK, "oilsUtilsQuickReq(): %s - %s", service, method );
248 // Open an application session with the service, and send the request
249 osrfAppSession* session = osrfAppSessionClientInit( service );
250 int reqid = osrfAppSessionSendRequest( session, params, method, 1 );
253 osrfMessage* omsg = osrfAppSessionRequestRecv( session, reqid, 60 );
254 jsonObject* result = jsonObjectClone( osrfMessageGetResult(omsg) );
257 osrfMessageFree(omsg);
258 osrfAppSessionFree(session);
263 @brief Perform a remote procedure call, propagating session
265 @param service The name of the service to invoke.
266 @param method The name of the method to call.
267 @param params The parameters to be passed to the method, if any.
268 @return A copy of whatever the method returns as a result, or a JSON_NULL if the method
269 doesn't return anything.
271 If the @a params parameter points to a JSON_ARRAY, pass each element of the array
272 as a separate parameter. If it points to any other kind of jsonObject, pass it as a
273 single parameter. If it is NULL, pass no parameters.
275 The calling code is responsible for freeing the returned object by calling jsonObjectFree().
277 jsonObject* oilsUtilsQuickReqCtx( osrfMethodContext* ctx, const char* service,
278 const char* method, const jsonObject* params ) {
279 if(!(service && method && ctx)) return NULL;
281 osrfLogDebug(OSRF_LOG_MARK, "oilsUtilsQuickReqCtx(): %s - %s (%s)", service, method, ctx->session->session_tz );
283 // Open an application session with the service, and send the request
284 osrfAppSession* session = osrfAppSessionClientInit( service );
285 osrf_app_session_set_tz(session, ctx->session->session_tz);
286 int reqid = osrfAppSessionSendRequest( session, params, method, 1 );
289 osrfMessage* omsg = osrfAppSessionRequestRecv( session, reqid, 60 );
290 jsonObject* result = jsonObjectClone( osrfMessageGetResult(omsg) );
293 osrfMessageFree(omsg);
294 osrfAppSessionFree(session);
299 @brief Call a method of the open-ils.storage service.
300 @param method Name of the method.
301 @param params Parameters to be passed to the method, if any.
302 @return A copy of whatever the method returns as a result, or a JSON_NULL if the method
303 doesn't return anything.
305 If the @a params parameter points to a JSON_ARRAY, pass each element of the array
306 as a separate parameter. If it points to any other kind of jsonObject, pass it as a
307 single parameter. If it is NULL, pass no parameters.
309 The calling code is responsible for freeing the returned object by calling jsonObjectFree().
311 jsonObject* oilsUtilsStorageReq( const char* method, const jsonObject* params ) {
312 return oilsUtilsQuickReq( "open-ils.storage", method, params );
316 @brief Call a method of the open-ils.cstore service.
317 @param method Name of the method.
318 @param params Parameters to be passed to the method, if any.
319 @return A copy of whatever the method returns as a result, or a JSON_NULL if the method
320 doesn't return anything.
322 If the @a params parameter points to a JSON_ARRAY, pass each element of the array
323 as a separate parameter. If it points to any other kind of jsonObject, pass it as a
324 single parameter. If it is NULL, pass no parameters.
326 The calling code is responsible for freeing the returned object by calling jsonObjectFree().
328 jsonObject* oilsUtilsCStoreReq( const char* method, const jsonObject* params ) {
329 return oilsUtilsQuickReq("open-ils.cstore", method, params);
333 @brief Call a method of the open-ils.cstore service, context aware.
334 @param ctx Method context object.
335 @param method Name of the method.
336 @param params Parameters to be passed to the method, if any.
337 @return A copy of whatever the method returns as a result, or a JSON_NULL if the method
338 doesn't return anything.
340 If the @a params parameter points to a JSON_ARRAY, pass each element of the array
341 as a separate parameter. If it points to any other kind of jsonObject, pass it as a
342 single parameter. If it is NULL, pass no parameters.
344 The calling code is responsible for freeing the returned object by calling jsonObjectFree().
346 jsonObject* oilsUtilsCStoreReqCtx( osrfMethodContext* ctx, const char* method, const jsonObject* params ) {
347 return oilsUtilsQuickReqCtx(ctx, "open-ils.cstore", method, params);
353 @brief Given a username, fetch the corresponding row from the actor.usr table, if any.
354 @param name The username for which to search.
355 @return A Fieldmapper object for the relevant row in the actor.usr table, if it exists;
356 or a JSON_NULL if it doesn't.
358 The calling code is responsible for freeing the returned object by calling jsonObjectFree().
360 jsonObject* oilsUtilsFetchUserByUsername( osrfMethodContext* ctx, const char* name ) {
361 if(!name) return NULL;
362 jsonObject* params = jsonParseFmt("{\"usrname\":\"%s\"}", name);
363 jsonObject* user = oilsUtilsQuickReqCtx(
364 ctx, "open-ils.cstore", "open-ils.cstore.direct.actor.user.search", params );
366 jsonObjectFree(params);
367 long id = oilsFMGetObjectId(user);
368 osrfLogDebug(OSRF_LOG_MARK, "Fetched user %s:%ld", name, id);
373 @brief Given a barcode, fetch the corresponding row from the actor.usr table, if any.
374 @param name The barcode for which to search.
375 @return A Fieldmapper object for the relevant row in the actor.usr table, if it exists;
376 or a JSON_NULL if it doesn't.
378 Look up the barcode in actor.card. Follow a foreign key from there to get a row in
381 The calling code is responsible for freeing the returned object by calling jsonObjectFree().
383 jsonObject* oilsUtilsFetchUserByBarcode(osrfMethodContext* ctx, const char* barcode) {
384 if(!barcode) return NULL;
386 osrfLogInfo(OSRF_LOG_MARK, "Fetching user by barcode %s", barcode);
388 jsonObject* params = jsonParseFmt("{\"barcode\":\"%s\"}", barcode);
389 jsonObject* card = oilsUtilsQuickReqCtx(
390 ctx, "open-ils.cstore", "open-ils.cstore.direct.actor.card.search", params );
391 jsonObjectFree(params);
394 return NULL; // No such card
396 // Get the user's id as a long
397 char* usr = oilsFMGetString(card, "usr");
398 jsonObjectFree(card);
400 return NULL; // No user id (shouldn't happen)
401 long iusr = strtol(usr, NULL, 10);
404 // Look up the user in actor.usr
405 params = jsonParseFmt("[%d]", iusr);
406 jsonObject* user = oilsUtilsQuickReqCtx(
407 ctx, "open-ils.cstore", "open-ils.cstore.direct.actor.user.retrieve", params);
409 jsonObjectFree(params);
413 char* oilsUtilsFetchOrgSetting( int orgid, const char* setting ) {
414 if(!setting) return NULL;
416 jsonObject* params = jsonParseFmt("[%d, \"%s\"]", orgid, setting );
418 jsonObject* set = oilsUtilsQuickReq(
420 "open-ils.actor.ou_setting.ancestor_default", params);
422 char* value = jsonObjectToSimpleString( jsonObjectGetKeyConst( set, "value" ));
423 jsonObjectFree(params);
425 osrfLogDebug(OSRF_LOG_MARK, "Fetched org [%d] setting: %s => %s", orgid, setting, value);
431 char* oilsUtilsLogin( const char* uname, const char* passwd, const char* type, int orgId ) {
432 if(!(uname && passwd)) return NULL;
434 osrfLogDebug(OSRF_LOG_MARK, "Logging in with username %s", uname );
437 jsonObject* params = jsonParseFmt("[\"%s\"]", uname);
439 jsonObject* o = oilsUtilsQuickReq(
440 "open-ils.auth", "open-ils.auth.authenticate.init", params );
442 const char* seed = jsonObjectGetString(o);
443 char* passhash = md5sum(passwd);
445 snprintf(buf, sizeof(buf), "%s%s", seed, passhash);
446 char* fullhash = md5sum(buf);
449 jsonObjectFree(params);
452 params = jsonParseFmt( "[\"%s\", \"%s\", \"%s\", \"%d\"]", uname, fullhash, type, orgId );
453 o = oilsUtilsQuickReq( "open-ils.auth",
454 "open-ils.auth.authenticate.complete", params );
457 const char* tok = jsonObjectGetString(
458 jsonObjectGetKeyConst( jsonObjectGetKey( o,"payload" ), "authtoken" ));
460 token = strdup( tok );
464 jsonObjectFree(params);
471 jsonObject* oilsUtilsFetchWorkstation( long id ) {
472 jsonObject* p = jsonParseFmt("[%ld]", id);
473 jsonObject* r = oilsUtilsQuickReq(
475 "open-ils.storage.direct.actor.workstation.retrieve", p );
480 jsonObject* oilsUtilsFetchWorkstationByName( const char* name ) {
481 jsonObject* p = jsonParseFmt("{\"name\":\"%s\"}", name);
482 jsonObject* r = oilsUtilsCStoreReq(
483 "open-ils.cstore.direct.actor.workstation.search", p);
489 @brief Convert a string to a number representing a time interval in seconds.
490 @param interval Pointer to string, e.g. "420" or "2 weeks".
491 @return If successful, the number of seconds that the string represents; otherwise -1.
493 If the string is all digits (apart from any leading or trailing white space), convert
494 it directly. Otherwise pass it to PostgreSQL for translation.
496 The result is the same as if we were to pass every string to PostgreSQL, except that,
497 depending on the value of LONG_MAX, we return values for some strings that represent
498 intervals too long for PostgreSQL to represent (i.e. more than 2147483647 seconds).
500 WARNING: a valid interval of -1 second will be indistinguishable from an error. If
501 such an interval is a plausible possibility, don't use this function.
503 long oilsUtilsIntervalToSeconds( const char* s ) {
506 osrfLogWarning( OSRF_LOG_MARK, "String to be converted is NULL" );
510 // Skip leading white space
511 while( isspace( (unsigned char) *s ))
515 osrfLogWarning( OSRF_LOG_MARK, "String to be converted is empty or all white space" );
519 // See if the string is a raw number, i.e. all digits
520 // (apart from any leading or trailing white space)
522 const char* p = s; // For traversing and examining the remaining string
523 if( isdigit( (unsigned char) *p )) {
524 // Looks like a number so far...skip over the digits
527 } while( isdigit( (unsigned char) *p ));
528 // Skip over any following white space
529 while( isspace( (unsigned char) *p ))
532 // This string is a raw number. Convert it directly.
533 long n = strtol( s, NULL, 10 );
534 if( LONG_MAX == n ) {
536 osrfLogWarning( OSRF_LOG_MARK,
537 "String \"%s\"represents a number too big for a long", s );
544 // If we get to this point, the string is not all digits. Pass it to PostgreSQL.
547 jsonObject* query_obj = jsonParseFmt(
548 "{\"from\":[\"config.interval_to_seconds\",\"%s\"]}", s );
551 jsonObject* result = oilsUtilsCStoreReq(
552 "open-ils.cstore.json_query", query_obj );
553 jsonObjectFree( query_obj );
556 const jsonObject* seconds_obj = jsonObjectGetKeyConst( result, "config.interval_to_seconds" );
558 if( seconds_obj && JSON_NUMBER == seconds_obj->type )
559 seconds = (long) jsonObjectGetNumber( seconds_obj );
561 osrfLogError( OSRF_LOG_MARK,
562 "Error calling json_query to convert \"%s\" to seconds", s );
564 jsonObjectFree( result );