int osrfMathRun( osrfMethodContext* ctx ) { if( osrfMethodVerifyContext( ctx ) ) { osrfLogError( OSRF_LOG_MARK, "Invalid method context" ); return -1; } /* collect the request params */ const jsonObject* x = jsonObjectGetIndex(ctx->params, 0); const jsonObject* y = jsonObjectGetIndex(ctx->params, 1); if( x && y ) { /* pull out the params as strings since they may be either strings or numbers depending on the client */ char* a = jsonObjectToSimpleString(x); char* b = jsonObjectToSimpleString(y); if( a && b ) { osrfLogActivity( OSRF_LOG_MARK, "Running opensrf.math %s [ %s : %s ]", ctx->method->name, a, b ); /* construct a new params object to send to dbmath */ jsonObject* newParams = jsonParseFmt( "[ %s, %s ]", a, b ); free(a); free(b); /* connect to db math */ osrfAppSession* ses = osrfAppSessionClientInit("opensrf.dbmath"); /* forcing an explicit connect allows us to talk to one worker backend * regardless of "stateful" config settings for the server * This buys us nothing here since we're only sending one request... * */ /*osrfAppSessionConnect(ses);*/ /* dbmath uses the same method names that math does */ int req_id = osrfAppSessionSendRequest( ses, newParams, ctx->method->name, 1 ); osrfMessage* omsg = osrfAppSessionRequestRecv( ses, req_id, 60 ); jsonObjectFree(newParams); if(omsg) { /* return dbmath's response to the user */ osrfAppRespondComplete( ctx, osrfMessageGetResult(omsg) ); osrfMessageFree(omsg); osrfAppSessionFree(ses); return 0; } osrfAppSessionFree(ses); } else { if(a) free(a); if(b) free(b); } } return -1; }
/* Adds the authentication token to the user cache. The timeout for the auth token is based on the type of login as well as (if type=='opac') the org location id. Returns the event that should be returned to the user. Event must be freed */ static oilsEvent* oilsAuthHandleLoginOK( jsonObject* userObj, const char* uname, const char* type, int orgloc, const char* workstation ) { oilsEvent* response; long timeout; char* wsorg = jsonObjectToSimpleString(oilsFMGetObject(userObj, "ws_ou")); if(wsorg) { /* if there is a workstation, use it for the timeout */ osrfLogDebug( OSRF_LOG_MARK, "Auth session trying workstation id %d for auth timeout", atoi(wsorg)); timeout = oilsAuthGetTimeout( userObj, type, atoi(wsorg) ); free(wsorg); } else { osrfLogDebug( OSRF_LOG_MARK, "Auth session trying org from param [%d] for auth timeout", orgloc ); timeout = oilsAuthGetTimeout( userObj, type, orgloc ); } osrfLogDebug(OSRF_LOG_MARK, "Auth session timeout for %s: %ld", uname, timeout ); char* string = va_list_to_string( "%d.%ld.%s", (long) getpid(), time(NULL), uname ); char* authToken = md5sum(string); char* authKey = va_list_to_string( "%s%s", OILS_AUTH_CACHE_PRFX, authToken ); const char* ws = (workstation) ? workstation : ""; osrfLogActivity(OSRF_LOG_MARK, "successful login: username=%s, authtoken=%s, workstation=%s", uname, authToken, ws ); oilsFMSetString( userObj, "passwd", "" ); jsonObject* cacheObj = jsonParseFmt( "{\"authtime\": %ld}", timeout ); jsonObjectSetKey( cacheObj, "userobj", jsonObjectClone(userObj)); if( !strcmp( type, OILS_AUTH_PERSIST )) { // Add entries for endtime and reset_interval, so that we can gracefully // extend the session a bit if the user is active toward the end of the // timeout originally specified. time_t endtime = time( NULL ) + timeout; jsonObjectSetKey( cacheObj, "endtime", jsonNewNumberObject( (double) endtime ) ); // Reset interval is hard-coded for now, but if we ever want to make it // configurable, this is the place to do it: jsonObjectSetKey( cacheObj, "reset_interval", jsonNewNumberObject( (double) DEFAULT_RESET_INTERVAL )); } osrfCachePutObject( authKey, cacheObj, (time_t) timeout ); jsonObjectFree(cacheObj); osrfLogInternal(OSRF_LOG_MARK, "oilsAuthHandleLoginOK(): Placed user object into cache"); jsonObject* payload = jsonParseFmt( "{ \"authtoken\": \"%s\", \"authtime\": %ld }", authToken, timeout ); response = oilsNewEvent2( OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload ); free(string); free(authToken); free(authKey); jsonObjectFree(payload); return response; }
/* Adds the authentication token to the user cache. The timeout for the auth token is based on the type of login as well as (if type=='opac') the org location id. Returns the event that should be returned to the user. Event must be freed */ static oilsEvent* oilsAuthHandleLoginOK( osrfMethodContext* ctx, jsonObject* userObj, const char* uname, const char* type, int orgloc, const char* workstation ) { oilsEvent* response = NULL; jsonObject* params = jsonNewObject(NULL); jsonObjectSetKey(params, "user_id", jsonNewNumberObject(oilsFMGetObjectId(userObj))); jsonObjectSetKey(params,"org_unit", jsonNewNumberObject(orgloc)); jsonObjectSetKey(params, "login_type", jsonNewObject(type)); if (workstation) jsonObjectSetKey(params, "workstation", jsonNewObject(workstation)); jsonObject* authEvt = oilsUtilsQuickReqCtx( ctx, "open-ils.auth_internal", "open-ils.auth_internal.session.create", params); jsonObjectFree(params); if (authEvt) { response = oilsNewEvent2( OSRF_LOG_MARK, jsonObjectGetString(jsonObjectGetKey(authEvt, "textcode")), jsonObjectGetKey(authEvt, "payload") // cloned within Event ); osrfLogActivity(OSRF_LOG_MARK, "successful login: username=%s, authtoken=%s, workstation=%s", uname, jsonObjectGetString( jsonObjectGetKeyConst( jsonObjectGetKeyConst(authEvt, "payload"), "authtoken" ) ), workstation ? workstation : "" ); jsonObjectFree(authEvt); } else { osrfLogError(OSRF_LOG_MARK, "Error caching auth session in open-ils.auth_internal"); } return response; }
/** * Parses the request body, logs any REQUEST messages to the activity log, * stamps the translator ingress on each message, and returns the updated * messages as a JSON string. */ static char* osrfHttpTranslatorParseRequest(osrfHttpTranslator* trans) { osrfMessage* msg; osrfMessage* msgList[MAX_MSGS_PER_PACKET]; int numMsgs = osrf_message_deserialize(trans->body, msgList, MAX_MSGS_PER_PACKET); osrfLogDebug(OSRF_LOG_MARK, "parsed %d opensrf messages in this packet", numMsgs); if(numMsgs == 0) return NULL; // log request messages to the activity log int i; for(i = 0; i < numMsgs; i++) { msg = msgList[i]; osrfMessageSetIngress(msg, TRANSLATOR_INGRESS); switch(msg->m_type) { case REQUEST: { const jsonObject* params = msg->_params; growing_buffer* act = buffer_init(128); char* method = msg->method_name; buffer_fadd(act, "[%s] [%s] %s %s", trans->remoteHost, "", trans->service, method); const jsonObject* obj = NULL; int i = 0; const char* str; int redactParams = 0; while( (str = osrfStringArrayGetString(log_protect_arr, i++)) ) { //osrfLogInternal(OSRF_LOG_MARK, "Checking for log protection [%s]", str); if(!strncmp(method, str, strlen(str))) { redactParams = 1; break; } } if(redactParams) { OSRF_BUFFER_ADD(act, " **PARAMS REDACTED**"); } else { i = 0; while((obj = jsonObjectGetIndex(params, i++))) { str = jsonObjectToJSON(obj); if( i == 1 ) OSRF_BUFFER_ADD(act, " "); else OSRF_BUFFER_ADD(act, ", "); OSRF_BUFFER_ADD(act, str); free(str); } } osrfLogActivity(OSRF_LOG_MARK, "%s", act->buf); buffer_free(act); break; } case CONNECT: trans->connecting = 1; if (numMsgs == 1) trans->connectOnly = 1; break; case DISCONNECT: trans->disconnecting = 1; if (numMsgs == 1) trans->disconnectOnly = 1; break; case RESULT: osrfLogWarning( OSRF_LOG_MARK, "Unexpected RESULT message received" ); break; case STATUS: osrfLogWarning( OSRF_LOG_MARK, "Unexpected STATUS message received" ); break; default: osrfLogWarning( OSRF_LOG_MARK, "Invalid message type %d received", msg->m_type ); break; } } char* jsonString = osrfMessageSerializeBatch(msgList, numMsgs); for(i = 0; i < numMsgs; i++) { osrfMessageFree(msgList[i]); } return jsonString; }
static int osrf_json_gateway_method_handler (request_rec *r) { /* make sure we're needed first thing*/ if (strcmp(r->handler, MODULE_NAME )) return DECLINED; osrf_json_gateway_dir_config* dir_conf = ap_get_module_config(r->per_dir_config, &osrf_json_gateway_module); /* provide 2 different JSON parsers and serializers to support legacy JSON */ jsonObject* (*parseJSONFunc) (const char*) = legacy_jsonParseString; char* (*jsonToStringFunc) (const jsonObject*) = legacy_jsonObjectToJSON; if(dir_conf->legacyJSON) { ap_log_rerror( APLOG_MARK, APLOG_DEBUG, 0, r, "Using legacy JSON"); } else { parseJSONFunc = jsonParse; jsonToStringFunc = jsonObjectToJSON; } osrfLogDebug(OSRF_LOG_MARK, "osrf gateway: entered request handler"); /* verify we are connected */ if( !bootstrapped || !osrfSystemGetTransportClient()) { ap_log_rerror( APLOG_MARK, APLOG_ERR, 0, r, "Cannot process request " "because the OpenSRF JSON gateway has not been bootstrapped..."); usleep( 100000 ); /* 100 milliseconds */ exit(1); } osrfLogSetAppname("osrf_json_gw"); char* osrf_locale = NULL; char* param_locale = NULL; /* locale for this call */ char* service = NULL; /* service to connect to */ char* method = NULL; /* method to perform */ char* format = NULL; /* method to perform */ char* a_l = NULL; /* request api level */ char* input_format = NULL; /* POST data format, defaults to 'format' */ int isXML = 0; int api_level = 1; r->allowed |= (AP_METHOD_BIT << M_GET); r->allowed |= (AP_METHOD_BIT << M_POST); osrfLogDebug(OSRF_LOG_MARK, "osrf gateway: parsing URL params"); osrfStringArray* mparams = NULL; osrfStringArray* params = apacheParseParms(r); /* free me */ param_locale = apacheGetFirstParamValue( params, "locale" ); service = apacheGetFirstParamValue( params, "service" ); method = apacheGetFirstParamValue( params, "method" ); format = apacheGetFirstParamValue( params, "format" ); input_format = apacheGetFirstParamValue( params, "input_format" ); a_l = apacheGetFirstParamValue( params, "api_level" ); mparams = apacheGetParamValues( params, "param" ); /* free me */ if(format == NULL) format = strdup( "json" ); if(input_format == NULL) input_format = strdup( format ); /* set the user defined timeout value */ int timeout = 60; char* tout = apacheGetFirstParamValue( params, "timeout" ); /* request timeout in seconds */ if( tout ) { timeout = atoi(tout); osrfLogDebug(OSRF_LOG_MARK, "Client supplied timeout of %d", timeout); free( tout ); } if (a_l) { api_level = atoi(a_l); free( a_l ); } if (!strcasecmp(format, "xml")) { isXML = 1; ap_set_content_type(r, "application/xml"); } else { ap_set_content_type(r, "text/plain"); } free( format ); int ret = OK; /* ----------------------------------------------------------------- */ /* Grab the requested locale using the Accept-Language header*/ if ( !param_locale ) { if ( apr_table_get(r->headers_in, "X-OpenSRF-Language") ) { param_locale = strdup( apr_table_get(r->headers_in, "X-OpenSRF-Language") ); } else if ( apr_table_get(r->headers_in, "Accept-Language") ) { param_locale = strdup( apr_table_get(r->headers_in, "Accept-Language") ); } } if (param_locale) { growing_buffer* osrf_locale_buf = buffer_init(16); if (index(param_locale, ',')) { int ind = index(param_locale, ',') - param_locale; int i; for ( i = 0; i < ind && i < 128; i++ ) buffer_add_char( osrf_locale_buf, param_locale[i] ); } else { buffer_add( osrf_locale_buf, param_locale ); } free(param_locale); osrf_locale = buffer_release( osrf_locale_buf ); } else { osrf_locale = strdup( osrf_json_default_locale ); } /* ----------------------------------------------------------------- */ if(!(service && method)) { osrfLogError(OSRF_LOG_MARK, "Service [%s] not found or not allowed", service); ret = HTTP_NOT_FOUND; } else { /* This will log all heaers to the apache error log const apr_array_header_t* arr = apr_table_elts(r->headers_in); const void* ptr; while( (ptr = apr_array_pop(arr)) ) { apr_table_entry_t* e = (apr_table_entry_t*) ptr; fprintf(stderr, "Table entry: %s : %s\n", e->key, e->val ); } fflush(stderr); */ osrfAppSession* session = osrfAppSessionClientInit(service); osrf_app_session_set_locale(session, osrf_locale); double starttime = get_timestamp_millis(); int req_id = -1; if(!strcasecmp(input_format, "json")) { jsonObject * arr = jsonNewObject(NULL); const char* str; int i = 0; while( (str = osrfStringArrayGetString(mparams, i++)) ) jsonObjectPush(arr, parseJSONFunc(str)); req_id = osrfAppSessionSendRequest( session, arr, method, api_level ); jsonObjectFree(arr); } else { /** * If we receive XML method params, convert each param to a JSON object * and pass the array of JSON object params to the method */ if(!strcasecmp(input_format, "xml")) { jsonObject* jsonParams = jsonNewObject(NULL); const char* str; int i = 0; while( (str = osrfStringArrayGetString(mparams, i++)) ) { jsonObjectPush(jsonParams, jsonXMLToJSONObject(str)); } req_id = osrfAppSessionSendRequest( session, jsonParams, method, api_level ); jsonObjectFree(jsonParams); } } if( req_id == -1 ) { osrfLogError(OSRF_LOG_MARK, "I am unable to communicate with opensrf..going away..."); osrfAppSessionFree(session); /* we don't want to spawn an intense re-forking storm * if there is no jabber server.. so give it some time before we die */ usleep( 100000 ); /* 100 milliseconds */ exit(1); } /* ----------------------------------------------------------------- */ /* log all requests to the activity log */ const char* authtoken = apr_table_get(r->headers_in, "X-OILS-Authtoken"); if(!authtoken) authtoken = ""; growing_buffer* act = buffer_init(128); buffer_fadd(act, "[%s] [%s] [%s] %s %s", r->connection->remote_ip, authtoken, osrf_locale, service, method ); const char* str; int i = 0; while( (str = osrfStringArrayGetString(mparams, i++)) ) { if( i == 1 ) { OSRF_BUFFER_ADD(act, " "); OSRF_BUFFER_ADD(act, str); } else { OSRF_BUFFER_ADD(act, ", "); OSRF_BUFFER_ADD(act, str); } } osrfLogActivity( OSRF_LOG_MARK, act->buf ); buffer_free(act); /* ----------------------------------------------------------------- */ osrfMessage* omsg = NULL; int statuscode = 200; /* kick off the object */ if (isXML) ap_rputs( "<response xmlns=\"http://opensrf.org/-/namespaces/gateway/v1\"><payload>", r ); else ap_rputs("{\"payload\":[", r); int morethan1 = 0; char* statusname = NULL; char* statustext = NULL; char* output = NULL; while((omsg = osrfAppSessionRequestRecv( session, req_id, timeout ))) { statuscode = omsg->status_code; const jsonObject* res; if( ( res = osrfMessageGetResult(omsg)) ) { if (isXML) { output = jsonObjectToXML( res ); } else { output = jsonToStringFunc( res ); if( morethan1 ) ap_rputs(",", r); /* comma between JSON array items */ } ap_rputs(output, r); free(output); morethan1 = 1; } else { if( statuscode > 299 ) { /* the request returned a low level error */ statusname = omsg->status_name ? strdup(omsg->status_name) : strdup("Unknown Error"); statustext = omsg->status_text ? strdup(omsg->status_text) : strdup("No Error Message"); osrfLogError( OSRF_LOG_MARK, "Gateway received error: %s", statustext ); } } osrfMessageFree(omsg); if(statusname) break; } double duration = get_timestamp_millis() - starttime; osrfLogDebug(OSRF_LOG_MARK, "gateway request took %f seconds", duration); if (isXML) ap_rputs("</payload>", r); else ap_rputs("]",r); /* finish off the payload array */ if(statusname) { /* add a debug field if the request died */ ap_log_rerror( APLOG_MARK, APLOG_INFO, 0, r, "OpenSRF JSON Request returned error: %s -> %s", statusname, statustext ); int l = strlen(statusname) + strlen(statustext) + 32; char buf[l]; if (isXML) snprintf( buf, sizeof(buf), "<debug>\"%s : %s\"</debug>", statusname, statustext ); else { char bb[l]; snprintf(bb, sizeof(bb), "%s : %s", statusname, statustext); jsonObject* tmp = jsonNewObject(bb); char* j = jsonToStringFunc(tmp); snprintf( buf, sizeof(buf), ",\"debug\": %s", j); free(j); jsonObjectFree(tmp); } ap_rputs(buf, r); free(statusname); free(statustext); } /* insert the status code */ char buf[32]; if (isXML) snprintf(buf, sizeof(buf), "<status>%d</status>", statuscode ); else snprintf(buf, sizeof(buf), ",\"status\":%d", statuscode ); ap_rputs( buf, r ); if (isXML) ap_rputs("</response>", r); else ap_rputs( "}", r ); /* finish off the object */ osrfAppSessionFree(session); } osrfLogInfo(OSRF_LOG_MARK, "Completed processing service=%s, method=%s", service, method); osrfStringArrayFree(params); osrfStringArrayFree(mparams); free( osrf_locale ); free( input_format ); free( method ); free( service ); osrfLogDebug(OSRF_LOG_MARK, "Gateway served %d requests", ++numserved); osrfLogClearXid(); return ret; }
static char* extract_inbound_messages( const request_rec *r, const char* service, const char* thread, const char* recipient, const jsonObject *osrf_msg) { int i; int num_msgs = osrf_msg->size; osrfMessage* msg; osrfMessage* msg_list[num_msgs]; // here we do an extra json round-trip to get the data // in a form osrf_message_deserialize can understand // TODO: consider a version of osrf_message_init which can // accept a jsonObject* instead of a JSON string. char *osrf_msg_json = jsonObjectToJSON(osrf_msg); osrf_message_deserialize(osrf_msg_json, msg_list, num_msgs); free(osrf_msg_json); // should we require the caller to always pass the service? if (service == NULL) service = ""; for(i = 0; i < num_msgs; i++) { msg = msg_list[i]; osrfMessageSetIngress(msg, WEBSOCKET_TRANSLATOR_INGRESS); switch(msg->m_type) { case REQUEST: { const jsonObject* params = msg->_params; growing_buffer* act = buffer_init(128); char* method = msg->method_name; buffer_fadd(act, "[%s] [%s] %s %s", get_client_ip(r), "", service, method); const jsonObject* obj = NULL; int i = 0; const char* str; int redactParams = 0; while( (str = osrfStringArrayGetString(log_protect_arr, i++)) ) { if(!strncmp(method, str, strlen(str))) { redactParams = 1; break; } } if(redactParams) { OSRF_BUFFER_ADD(act, " **PARAMS REDACTED**"); } else { i = 0; while((obj = jsonObjectGetIndex(params, i++))) { char* str = jsonObjectToJSON(obj); if( i == 1 ) OSRF_BUFFER_ADD(act, " "); else OSRF_BUFFER_ADD(act, ", "); OSRF_BUFFER_ADD(act, str); free(str); } } osrfLogActivity(OSRF_LOG_MARK, "%s", act->buf); buffer_free(act); requests_in_flight++; break; } case DISCONNECT: clear_cached_recipient(thread); break; } } char* finalMsg = osrfMessageSerializeBatch(msg_list, num_msgs); // clean up our messages for(i = 0; i < num_msgs; i++) osrfMessageFree(msg_list[i]); return finalMsg; }