Пример #1
0
/**
 * Cache the transaction with the JID of the backend process we are talking to
 */
static void osrfHttpTranslatorCacheSession(osrfHttpTranslator* trans, const char* jid) {
    jsonObject* cacheObj = jsonNewObject(NULL);
    jsonObjectSetKey(cacheObj, "ip", jsonNewObject(trans->remoteHost));
    jsonObjectSetKey(cacheObj, "jid", jsonNewObject(jid));
    jsonObjectSetKey(cacheObj, "service", jsonNewObject(trans->service));
    osrfCachePutObject(trans->thread, cacheObj, CACHE_TIME);
}
Пример #2
0
static void startElementHandler(
    void *parser, const xmlChar *name, const xmlChar **atts) {

    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;
    jsonObject* obj;

    char* hint = getXMLAttr(atts, "class_hint");

    if(!strcmp((char*) name, "null")) {
        appendChild(p, jsonNewObject(NULL));
        return;
    }

    if(!strcmp((char*) name, "string")) {
        p->inString = 1;
        return;
    }

    if(!strcmp((char*) name, "element")) {
       osrfListPush(p->keyStack, strdup(getXMLAttr(atts, "key")));
       return;
    }

    if(!strcmp((char*) name, "object")) {
        obj = jsonNewObject(NULL);
        jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
        obj->type = JSON_HASH;
        appendChild(p, obj);
        osrfListPush(p->objStack, obj);
        return;
    }

    if(!strcmp((char*) name, "array")) {
        obj = jsonNewObject(NULL);
        jsonObjectSetClass(obj, hint); /* OK if hint is NULL */
        obj->type = JSON_ARRAY;
        appendChild(p, obj);
        osrfListPush(p->objStack, obj);
        return;
    }


    if(!strcmp((char*) name, "number")) {
        p->inNumber = 1;
        return;
    }

    if(!strcmp((char*) name, "boolean")) {
        obj = jsonNewObject(NULL);
        obj->type = JSON_BOOL;
        char* val = getXMLAttr(atts, "value");
        if(val && !strcmp(val, "true"))
            obj->value.b = 1;
        
        appendChild(p, obj);
        return;
    }
}
Пример #3
0
/**
	@brief Implement the "init" method.
	@param ctx The method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- username
	- nonce : optional login seed (string) provided by the caller which
		is added to the auth init cache to differentiate between logins
		using the same username and thus avoiding cache collisions for
		near-simultaneous logins.

	Return to client: Intermediate authentication seed.

	Combine the username with a timestamp and process ID, and take an md5 hash of the result.
	Store the hash in memcache, with a key based on the username.  Then return the hash to
	the client.

	However: if the username includes one or more embedded blank spaces, return a dummy
	hash without storing anything in memcache.  The dummy will never match a stored hash, so
	any attempt to authenticate with it will fail.
*/
int oilsAuthInit( osrfMethodContext* ctx ) {
	OSRF_METHOD_VERIFY_CONTEXT(ctx);

	char* username  = jsonObjectToSimpleString( jsonObjectGetIndex(ctx->params, 0) );
	const char* nonce = jsonObjectGetString(jsonObjectGetIndex(ctx->params, 1));
	if (!nonce) nonce = "";

	if( username ) {

		jsonObject* resp;

		if( strchr( username, ' ' ) ) {

			// Embedded spaces are not allowed in a username.  Use "x" as a dummy
			// seed.  It will never be a valid seed because 'x' is not a hex digit.
			resp = jsonNewObject( "x" );

		} else {

			// Build a key and a seed; store them in memcache.
			char* key  = va_list_to_string( "%s%s%s", OILS_AUTH_CACHE_PRFX, username, nonce );
			char* countkey = va_list_to_string( "%s%s%s", OILS_AUTH_CACHE_PRFX, username, OILS_AUTH_COUNT_SFFX );
			char* seed = md5sum( "%d.%ld.%s.%s", (int) time(NULL), (long) getpid(), username, nonce );
			jsonObject* countobject = osrfCacheGetObject( countkey );
			if(!countobject) {
				countobject = jsonNewNumberObject( (double) 0 );
			}
			osrfCachePutString( key, seed, _oilsAuthSeedTimeout );
			osrfCachePutObject( countkey, countobject, _oilsAuthBlockTimeout );

			osrfLogDebug( OSRF_LOG_MARK, "oilsAuthInit(): has seed %s and key %s", seed, key );

			// Build a returnable object containing the seed.
			resp = jsonNewObject( seed );

			free( seed );
			free( key );
			free( countkey );
			jsonObjectFree( countobject );
		}

		// Return the seed to the client.
		osrfAppRespondComplete( ctx, resp );

		jsonObjectFree(resp);
		free(username);
		return 0;
	}

	return -1;  // Error: no username parameter
}
Пример #4
0
/*
	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;
}
Пример #5
0
/**
	@brief Run the introspect method for a specified method or group of methods.
	@param ctx Pointer to the method context.
	@return 1 if successful, or if no search target is specified as a parameter; -1 if unable
	to find a pointer to the application.

	Traverse the list of methods, and report on each one whose name starts with the specified
	search target.  In effect, the search target ends with an implicit wild card.
*/
static int osrfAppIntrospect( osrfMethodContext* ctx ) {

	// Get the name of the method to introspect
	const char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
	if( !methodSubstring )
		return 1; /* respond with no methods */

	// Get a pointer to the application
	osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
	if( !app )
		return -1;   // Oops, no application...

	int len = 0;
	osrfHashIterator* itr = osrfNewHashIterator(app->methods);
	osrfMethod* method;

	while( (method = osrfHashIteratorNext(itr)) ) {
		if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
			if( !strncmp( method->name, methodSubstring, len) ) {
				jsonObject* resp = jsonNewObject(NULL);
				_osrfAppSetIntrospectMethod( ctx, method, resp );
				osrfAppRespond(ctx, resp);
				jsonObjectFree(resp);
			}
		}
	}

	osrfHashIteratorFree(itr);
	return 1;
}
Пример #6
0
/**
	@brief Introspect a specified method.
	@param ctx Pointer to the method context.
	@param method Pointer to the osrfMethod for the specified method.
	@param resp Pointer to the jsonObject into which method information will be placed.

	Treating the @a resp object as a JSON_HASH, insert entries for various bits of information
	about the specified method.
*/
static void _osrfAppSetIntrospectMethod( osrfMethodContext* ctx, const osrfMethod* method,
		jsonObject* resp ) {
	if(!(ctx && resp)) return;

	jsonObjectSetKey(resp, "api_name",  jsonNewObject(method->name));
	jsonObjectSetKey(resp, "method",    jsonNewObject(method->symbol));
	jsonObjectSetKey(resp, "service",   jsonNewObject(ctx->session->remote_service));
	jsonObjectSetKey(resp, "notes",     jsonNewObject(method->notes));
	jsonObjectSetKey(resp, "argc",      jsonNewNumberObject(method->argc));

	jsonObjectSetKey(resp, "sysmethod",
			jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
	jsonObjectSetKey(resp, "atomic",
			jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
	jsonObjectSetKey(resp, "cachable",
			jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
}
Пример #7
0
int main( void ) {
	int rc = 0;

	struct timezone tz = { 240, 1 };
	struct timeval begin_timeval;
	struct timeval end_timeval;

	gettimeofday( &begin_timeval, &tz );

	long i;
	jsonObject * pObj = NULL;

	for( i = 10000000; i; --i )
	{
//		pObj = jsonParse( sample_json );
		pObj = jsonNewObject( NULL );
		jsonObject * p1 = jsonNewObject( NULL );
		jsonObject * p2 = jsonNewObject( NULL );
		jsonObjectFree( p1 );
		jsonObjectFree( p2 );
		jsonObjectFree( pObj );
	}

	jsonObjectFreeUnused();
	
	gettimeofday( &end_timeval, &tz );

	struct timeval elapsed = diff_timeval( &begin_timeval, &end_timeval );

	printf( "Elapsed time: %ld seconds, %ld microseconds\n",
			(long) elapsed.tv_sec, (long) elapsed.tv_usec );

	struct rlimit rlim;
	if( getrlimit( RLIMIT_DATA, &rlim ) )
		printf( "Error calling getrlimit\n" );
	else
		printf( "Address space: %lu\n", (unsigned long) rlim.rlim_cur );

#ifdef HAVE_MALLOC_STATS
	malloc_stats();
#else
	fprintf(stderr, "malloc_stats() is not available on your system\n");
#endif
	return rc;
}
Пример #8
0
/**
 * Returns true if the provided password is correct.
 * Turn the password into the nested md5 hash required of migrated
 * passwords, then check the password in the DB.
 */
static int oilsAuthLoginCheckPassword(int user_id, const char* password) {

    growing_buffer* gb = buffer_init(33); // free me 1
    char* salt = oilsAuthGetSalt(user_id); // free me 2
    char* passhash = md5sum(password); // free me 3

    buffer_add(gb, salt); // gb strdup's internally
    buffer_add(gb, passhash);

    free(salt); // free 2
    free(passhash); // free 3

    // salt + md5(password)
    passhash = buffer_release(gb); // free 1 ; free me 4
    char* finalpass = md5sum(passhash); // free me 5

    free(passhash); // free 4

    jsonObject *arr = jsonNewObjectType(JSON_ARRAY);
    jsonObjectPush(arr, jsonNewObject("actor.verify_passwd"));
    jsonObjectPush(arr, jsonNewNumberObject((long) user_id));
    jsonObjectPush(arr, jsonNewObject("main"));
    jsonObjectPush(arr, jsonNewObject(finalpass));
    jsonObject *params = jsonNewObjectType(JSON_HASH); // free me 6
    jsonObjectSetKey(params, "from", arr);

    free(finalpass); // free 5

    jsonObject* verify_obj = // free 
        oilsUtilsCStoreReq("open-ils.cstore.json_query", params);

    jsonObjectFree(params); // free 6

    if (!verify_obj) return 0; // error

    int verified = oilsUtilsIsDBTrue(
        jsonObjectGetString(
            jsonObjectGetKeyConst(verify_obj, "actor.verify_passwd")
        )
    );

    jsonObjectFree(verify_obj);

    return verified;
}
Пример #9
0
/**
	@brief Load a specified query from the database query tables.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query id (key of query.stored_query table)

	Returns: a hash with two entries:
	- "token": A character string serving as a token for future references to the query.
	- "bind_variables" A hash of bind variables; see notes for doParamList().
*/
int doPrepare( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query id from a method parameter
	const jsonObject* query_id_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( query_id_obj->type != JSON_NUMBER ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query id must be a number" );
		return -1;
	}

	int query_id = atoi( jsonObjectGetString( query_id_obj ));
	if( query_id <= 0 ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter: query id must be greater than zero" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Loading query for id # %d", query_id );

	BuildSQLState* state = buildSQLStateNew( dbhandle );
	state->defaults_usable = 1;
	state->values_required = 0;
	StoredQ* query = getStoredQuery( state, query_id );
	if( state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, "Unable to load stored query # %d", query_id );
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to load stored query" );
		if( state->panic ) {
			osrfLogError( OSRF_LOG_MARK, sqlAddMsg( state, 
				"Database connection isn't working" ));
			osrfAppSessionPanic( ctx->session );
		}
		return -1;
	}

	const char* token = save_query( ctx, state, query );

	osrfLogInfo( OSRF_LOG_MARK, "Token for query id # %d is \"%s\"", query_id, token );

	// Build an object to return.  It will be a hash containing the query token and a
	// list of bind variables.
	jsonObject* returned_obj = jsonNewObjectType( JSON_HASH );
	jsonObjectSetKey( returned_obj, "token", jsonNewObject( token ));
	jsonObjectSetKey( returned_obj, "bind_variables",
		oilsBindVarList( state->bindvar_list ));

	osrfAppRespondComplete( ctx, returned_obj );
	return 0;
}
Пример #10
0
int oilsFMSetString( jsonObject* object, const char* field, const char* string ) {
	if(!(object && field && string)) return -1;
	osrfLogInternal(OSRF_LOG_MARK, "oilsFMSetString(): Collecing position for field %s", field);
	int pos = fm_ntop(object->classname, field);
	if( pos > -1 ) {
		osrfLogInternal(OSRF_LOG_MARK, "oilsFMSetString(): Setting string "
				"%s at field %s [position %d]", string, field, pos );
		jsonObjectSetIndex( object, pos, jsonNewObject(string) );
		return 0;
	}
	return -1;
}
Пример #11
0
static void speedTest() {

    /* creates a giant json object, generating JSON strings
     * of subobjects as it goes. */

    int i,k;
    int count = 50;
    char buf[16];
    char* jsonString;

    jsonObject* array;
    jsonObject* dupe;
    jsonObject* hash = jsonNewObject(NULL);

    for(i = 0; i < count; i++) {
        
        snprintf(buf, sizeof(buf), "key_%d", i);

        array = jsonNewObject(NULL);
        for(k = 0; k < count + i; k++) {
            jsonObjectPush(array, jsonNewNumberObject(k));
            jsonObjectPush(array, jsonNewObject(NULL));
            jsonObjectPush(array, jsonNewObjectFmt("str %d-%d", i, k));
        }
        jsonObjectSetKey(hash, buf, array);

        jsonString = jsonObjectToJSON(hash);
        printf("%s\n\n", jsonString);
        dupe = jsonParse(jsonString);

        jsonObjectFree(dupe);
        free(jsonString);
    }

    jsonObjectFree(hash);
}
Пример #12
0
/**
	@brief Collect characters into a number, and create a JSON_NUMBER for it.
	@param parser Pointer to a parser.
	@param firstc The first character in the number.
	@return Pointer to a newly created jsonObject of type JSON_NUMBER, or NULL upon error.

	Collect digits, signs, decimal points, and 'E' or 'e' (for scientific notation) into
	a buffer.  Make sure that the result is numeric.  If it's not numeric by strict JSON
	rules, try to make it numeric by some judicious massaging (we aren't quite as strict
	as the official JSON rules).

	If successful, construct a jsonObject of type JSON_NUMBER containing the resulting
	numeric string.  Otherwise log an error message and return NULL.
*/
static jsonObject* get_number( Parser* parser, char firstc ) {

	if( parser->str_buf )
		buffer_reset( parser->str_buf );
	else
		parser->str_buf = buffer_init( 64 );

	growing_buffer* gb = parser->str_buf;
	OSRF_BUFFER_ADD_CHAR( gb, firstc );

	char c;

	for( ;; ) {
		c = parser_nextc( parser );
		if( isdigit( (unsigned char) c ) ||
			'.' == c ||
			'-' == c ||
			'+' == c ||
			'e' == c ||
			'E' == c ) {
			OSRF_BUFFER_ADD_CHAR( gb, c );
		} else {
			if( ! isspace( (unsigned char) c ) )
				parser_ungetc( parser );
			break;
		}
	}

	char* s = buffer_data( gb );
	if( ! jsonIsNumeric( s ) ) {
		char* temp = jsonScrubNumber( s );
		free( s );
		s = temp;
		if( !s ) {
			report_error( parser, parser->buff[ parser->index - 1 ],
					"Invalid numeric format" );
			return NULL;
		}
	}

	jsonObject* obj = jsonNewObject( NULL );
	obj->type = JSON_NUMBER;
	obj->value.s = s;

	return obj;
}
Пример #13
0
/**
	@brief Run the implement_all method.
	@param ctx Pointer to the method context.
	@return 1 if successful, or -1 if unable to find a pointer to the application.

	Report on all of the methods of the application.
*/
static int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
	osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );

	if(app) {
		osrfHashIterator* itr = osrfNewHashIterator(app->methods);
		osrfMethod* method;
		while( (method = osrfHashIteratorNext(itr)) ) {
			jsonObject* resp = jsonNewObject(NULL);
			_osrfAppSetIntrospectMethod( ctx, method, resp );
			osrfAppRespond(ctx, resp);
			jsonObjectFree(resp);
		}
		osrfHashIteratorFree(itr);
		return 1;
	} else
		return -1;
}
Пример #14
0
int oilsAuthSessionDelete( osrfMethodContext* ctx ) {
	OSRF_METHOD_VERIFY_CONTEXT(ctx);

	const char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
	jsonObject* resp = NULL;

	if( authToken ) {
		osrfLogDebug(OSRF_LOG_MARK, "Removing auth session: %s", authToken );
		char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); /**/
		osrfCacheRemove(key);
		resp = jsonNewObject(authToken); /**/
		free(key);
	}

	osrfAppRespondComplete( ctx, resp );
	jsonObjectFree(resp);
	return 0;
}
Пример #15
0
static void characterHandler(void *parser, const xmlChar *ch, int len) {

    char data[len+1];
    strncpy(data, (char*) ch, len);
    data[len] = '\0';
    osrfXMLGatewayParser* p = (osrfXMLGatewayParser*) parser;

    if(p->inString) {
        appendChild(p, jsonNewObject(data));
        p->inString = 0;
        return;
    }

    if(p->inNumber) {
        appendChild(p, jsonNewNumberObject(atof(data)));
        p->inNumber = 0;
        return;
    }
}
Пример #16
0
/**
	@brief Construct an SQL query, but without executing it.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: A string containing an SQL query..
*/
int doSql( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Returning SQL for token \"%s\"", token );
	if( query->state->error ) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"No valid prepared query available for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "No valid prepared query available" );
		return -1;
	} else if( buildSQL( query->state, query->query )) {
		osrfLogWarning( OSRF_LOG_MARK, sqlAddMsg( query->state,
			"Unable to build SQL statement for query id # %d", query->query->id ));
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Unable to build SQL statement" );
		return -1;
	}

	osrfAppRespondComplete( ctx, jsonNewObject( OSRF_BUFFER_C_STR( query->state->sql )));
	return 0;
}
Пример #17
0
/**
	@brief Get the next JSON node -- be it string, number, hash, or whatever.
	@param parser Pointer to a Parser.
	@param firstc The first character in the part that we're parsing.
	@return Pointer to the next JSON node, or NULL upon error.

	The first character tells us what kind of thing we're parsing next: a string, an array,
	a hash, a number, a boolean, or a null.  Branch accordingly.

	In the case of an array or a hash, this function indirectly calls itself in order to
	parse subordinate nodes.
*/
static jsonObject* get_json_node( Parser* parser, char firstc ) {

	jsonObject* obj = NULL;

	// Branch on the first character
	if( '"' == firstc ) {
		const char* str = get_string( parser );
		if( str ) {
			obj = jsonNewObject( NULL );
			obj->type = JSON_STRING;
			obj->value.s = strdup( str );
		}
	} else if( '[' == firstc ) {
		obj = get_array( parser );
	} else if( '{' == firstc ) {
		if( parser->decode )
			obj = get_decoded_hash( parser );
		else
			obj = get_hash( parser );
	} else if( 'n' == firstc ) {
		obj = get_null( parser );
	} else if( 't' == firstc ) {
		obj = get_true( parser );
	} else if( 'f' == firstc ) {
		obj = get_false( parser );
	}
	else if( isdigit( (unsigned char) firstc ) ||
			 '.' == firstc ||
			 '-' == firstc ||
			 '+' == firstc ||
			 'e' == firstc ||
			 'E' == firstc ) {
		obj = get_number( parser, firstc );
	} else {
		report_error( parser, firstc, "Unexpected character" );
	}

	return obj;
}
Пример #18
0
/**
	@brief Parse the JSON keyword "null", and create a JSON_NULL for it.
	@param parser Pointer to a Parser.
	@return Pointer to a newly created jsonObject of type JSON_NULL, or NULL upon error.

	We already saw an 'n', or we wouldn't be here.  Make sure that the next three characters
	are 'u', 'l', and 'l', and that the character after that is not a letter or a digit.

	If all goes well, create a jsonObject of type JSON_NULL, and return a pointer to it.
	Otherwise log an error message and return NULL.
*/
static jsonObject* get_null( Parser* parser ) {

	if( parser_nextc( parser ) != 'u' ||
		parser_nextc( parser ) != 'l' ||
		parser_nextc( parser ) != 'l' ) {
		report_error( parser, parser->buff[ parser->index - 1 ],
				"Expected \"ull\" to follow \"n\"; didn't find it" );
		return NULL;
	}

	// Peek at the next character to make sure that it's kosher
	char c = parser_nextc( parser );
	if( ! isspace( (unsigned char) c ) )
		parser_ungetc( parser );

	if( isalnum( (unsigned char) c ) ) {
		report_error( parser, c, "Found letter or number after \"null\"" );
		return NULL;
	}

	// Everything's okay.  Return a JSON_NULL.
	return jsonNewObject( NULL );
}
Пример #19
0
static int oilsAuthInitBarcodeHandler(
    osrfMethodContext* ctx, const char* barcode, const char* nonce) {

    osrfLogInfo(OSRF_LOG_MARK, 
        "User logging in with barcode %s", barcode);

    int user_id = -1;
    jsonObject* resp = NULL; // free
    jsonObject* user_obj = oilsUtilsFetchUserByBarcode(ctx, barcode); // free

    if (user_obj && user_obj->type != JSON_NULL) 
        user_id = oilsFMGetObjectId(user_obj);

    jsonObjectFree(user_obj); // NULL OK

    char* seed = oilsAuthBuildInitCache(user_id, barcode, "barcode", nonce);
    resp = jsonNewObject(seed);
    free(seed);

    osrfAppRespondComplete(ctx, resp);
    jsonObjectFree(resp);
    return 0;
}
Пример #20
0
/**
	@brief Return a list of previously generated error messages for a specified query.
	@param ctx Pointer to the current method context.
	@return Zero if successful, or -1 if not.

	Method parameters:
	- query token, as previously returned by the .prepare method.

	Returns: A (possibly empty) array of strings, each one an error message generated during
	previous operations in connection with the specified query.
*/
int doMessages( osrfMethodContext* ctx ) {
	if(osrfMethodVerifyContext( ctx )) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	// Get the query token from a method parameter
	const jsonObject* token_obj = jsonObjectGetIndex( ctx->params, 0 );
	if( token_obj->type != JSON_STRING ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid parameter; query token must be a string" );
		return -1;
	}
	const char* token = jsonObjectGetString( token_obj );

	// Look up the query token in the session-level userData
	CachedQuery* query = search_token( ctx, token );
	if( !query ) {
		osrfAppSessionStatus( ctx->session, OSRF_STATUS_BADREQUEST, "osrfMethodException",
			ctx->request, "Invalid query token" );
		return -1;
	}

	osrfLogInfo( OSRF_LOG_MARK, "Returning messages for token %s", token );

	jsonObject* msgs = jsonNewObjectType( JSON_ARRAY );
	const osrfStringArray* error_msgs = query->state->error_msgs;
	int i;
	for( i = 0; i < error_msgs->size; ++i ) {
		jsonObject* msg = jsonNewObject( osrfStringArrayGetString( error_msgs, i ));
		jsonObjectPush( msgs, msg );
	}

	osrfAppRespondComplete( ctx, msgs );
	return 0;
}
Пример #21
0
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;
}
Пример #22
0
int oilsAuthLogin(osrfMethodContext* ctx) {
    OSRF_METHOD_VERIFY_CONTEXT(ctx);

    const jsonObject* args  = jsonObjectGetIndex(ctx->params, 0);

    const char* username    = jsonObjectGetString(jsonObjectGetKeyConst(args, "username"));
    const char* identifier  = jsonObjectGetString(jsonObjectGetKeyConst(args, "identifier"));
    const char* password    = jsonObjectGetString(jsonObjectGetKeyConst(args, "password"));
    const char* type        = jsonObjectGetString(jsonObjectGetKeyConst(args, "type"));
    int orgloc        = (int) jsonObjectGetNumber(jsonObjectGetKeyConst(args, "org"));
    const char* workstation = jsonObjectGetString(jsonObjectGetKeyConst(args, "workstation"));
    const char* barcode     = jsonObjectGetString(jsonObjectGetKeyConst(args, "barcode"));
    const char* ewho        = jsonObjectGetString(jsonObjectGetKeyConst(args, "agent"));

    const char* ws = (workstation) ? workstation : "";
    if (!type) type = OILS_AUTH_STAFF;

    jsonObject* userObj = NULL; // free me
    oilsEvent* response = NULL; // free me

    /* Use __FILE__, harmless_line_number for creating
     * OILS_EVENT_AUTH_FAILED events (instead of OSRF_LOG_MARK) to avoid
     * giving away information about why an authentication attempt failed.
     */
    int harmless_line_number = __LINE__;

    // translate a generic identifier into a username or barcode if necessary.
    if (identifier && !username && !barcode) {
        if (oilsAuthIdentIsBarcode(identifier, orgloc)) {
            barcode = identifier;
        } else {
            username = identifier;
        }
    }

    if (username) {
        barcode = NULL; // avoid superfluous identifiers
        userObj = oilsUtilsFetchUserByUsername(ctx, username);

    } else if (barcode) {
        userObj = oilsUtilsFetchUserByBarcode(ctx, barcode);

    } else {
        // not enough params
        return osrfAppRequestRespondException(ctx->session, ctx->request,
            "username/barcode and password required for method: %s", 
            ctx->method->name);
    }

    if (!userObj) { // user not found.  
        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
        osrfAppRespondComplete(ctx, oilsEventToJSON(response));
        oilsEventFree(response); // frees event JSON
        return 0;
    }

    long user_id = oilsFMGetObjectId(userObj);

    // username is freed when userObj is freed.
    // From here we can use the username as the generic identifier
    // since it's guaranteed to have a value.
    if (!username) username = oilsFMGetStringConst(userObj, "usrname");

    // See if the user is allowed to login.
    jsonObject* params = jsonNewObject(NULL);
    jsonObjectSetKey(params, "user_id", jsonNewNumberObject(user_id));
    jsonObjectSetKey(params,"org_unit", jsonNewNumberObject(orgloc));
    jsonObjectSetKey(params, "login_type", jsonNewObject(type));
    if (barcode) jsonObjectSetKey(params, "barcode", jsonNewObject(barcode));

    jsonObject* authEvt = oilsUtilsQuickReqCtx( // freed after password test
        ctx,
        "open-ils.auth_internal",
        "open-ils.auth_internal.user.validate", params);
    jsonObjectFree(params);

    if (!authEvt) { // unknown error
        jsonObjectFree(userObj);
        return -1;
    }

    const char* authEvtCode = 
        jsonObjectGetString(jsonObjectGetKey(authEvt, "textcode"));

    if (!strcmp(authEvtCode, OILS_EVENT_AUTH_FAILED)) {
        // Received the generic login failure event.

        osrfLogInfo(OSRF_LOG_MARK,  
            "failed login: username=%s, barcode=%s, workstation=%s",
            username, (barcode ? barcode : "(none)"), ws);

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
    }

    if (!response && // user exists and is not barred, etc.
        !oilsAuthLoginVerifyPassword(ctx, user_id, username, password)) {
        // User provided the wrong password or is blocked from too 
        // many previous login failures.

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);

        osrfLogInfo(OSRF_LOG_MARK,  
            "failed login: username=%s, barcode=%s, workstation=%s",
                username, (barcode ? barcode : "(none)"), ws );
    }

    // Below here, we know the password check succeeded if no response
    // object is present.

    if (!response && (
        !strcmp(authEvtCode, "PATRON_INACTIVE") ||
        !strcmp(authEvtCode, "PATRON_CARD_INACTIVE"))) {
        // Patron and/or card is inactive but the correct password 
        // was provided.  Alert the caller to the inactive-ness.
        response = oilsNewEvent2(
            OSRF_LOG_MARK, authEvtCode,
            jsonObjectGetKey(authEvt, "payload")   // cloned within Event
        );
    }

    if (!response && strcmp(authEvtCode, OILS_EVENT_SUCCESS)) {
        // Validate API returned an unexpected non-success event.
        // To be safe, treat this as a generic login failure.

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
    }

    if (!response) {
        // password OK and no other events have prevented login completion.

        char* ewhat = "login";

        if (0 == strcmp(ctx->method->name, "open-ils.auth.authenticate.verify")) {
            response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_SUCCESS );
            ewhat = "verify";

        } else {
            response = oilsAuthHandleLoginOK(
                ctx, userObj, username, type, orgloc, workstation);
        }

        oilsUtilsTrackUserActivity(
            ctx,
            oilsFMGetObjectId(userObj), 
            ewho, ewhat, 
            osrfAppSessionGetIngress()
        );
    }

    // reply
    osrfAppRespondComplete(ctx, oilsEventToJSON(response));

    // clean up
    oilsEventFree(response);
    jsonObjectFree(userObj);
    jsonObjectFree(authEvt);

	return 0;
}
Пример #23
0
/**
	@brief Implement the "complete" method.
	@param ctx The method context.
	@return -1 upon error; zero if successful, and if a STATUS message has been sent to the
	client to indicate completion; a positive integer if successful but no such STATUS
	message has been sent.

	Method parameters:
	- a hash with some combination of the following elements:
		- "username"
		- "barcode"
		- "password" (hashed with the cached seed; not plaintext)
		- "type"
		- "org"
		- "workstation"
		- "agent" (what software/interface/3rd-party is making the request)
		- "nonce" optional login seed to differentiate logins using the same username.

	The password is required.  Either a username or a barcode must also be present.

	Return to client: Intermediate authentication seed.

	Validate the password, using the username if available, or the barcode if not.  The
	user must be active, and not barred from logging on.  The barcode, if used for
	authentication, must be active as well.  The workstation, if specified, must be valid.

	Upon deciding whether to allow the logon, return a corresponding event to the client.
*/
int oilsAuthComplete( osrfMethodContext* ctx ) {
    OSRF_METHOD_VERIFY_CONTEXT(ctx);

    const jsonObject* args  = jsonObjectGetIndex(ctx->params, 0);

    const char* uname       = jsonObjectGetString(jsonObjectGetKeyConst(args, "username"));
    const char* identifier  = jsonObjectGetString(jsonObjectGetKeyConst(args, "identifier"));
    const char* password    = jsonObjectGetString(jsonObjectGetKeyConst(args, "password"));
    const char* type        = jsonObjectGetString(jsonObjectGetKeyConst(args, "type"));
    int orgloc        = (int) jsonObjectGetNumber(jsonObjectGetKeyConst(args, "org"));
    const char* workstation = jsonObjectGetString(jsonObjectGetKeyConst(args, "workstation"));
    const char* barcode     = jsonObjectGetString(jsonObjectGetKeyConst(args, "barcode"));
    const char* ewho        = jsonObjectGetString(jsonObjectGetKeyConst(args, "agent"));
    const char* nonce       = jsonObjectGetString(jsonObjectGetKeyConst(args, "nonce"));

    const char* ws = (workstation) ? workstation : "";
    if (!nonce) nonce = "";

    // we no longer care how the identifier reaches us, 
    // as long as we have one.
    if (!identifier) {
        if (uname) {
            identifier = uname;
        } else if (barcode) {
            identifier = barcode;
        }
    }

    if (!identifier) {
        return osrfAppRequestRespondException(ctx->session, ctx->request,
            "username/barcode and password required for method: %s", 
            ctx->method->name);
    }

    osrfLogInfo(OSRF_LOG_MARK, 
        "Patron completing authentication with identifer %s", identifier);

    /* Use __FILE__, harmless_line_number for creating
     * OILS_EVENT_AUTH_FAILED events (instead of OSRF_LOG_MARK) to avoid
     * giving away information about why an authentication attempt failed.
     */
    int harmless_line_number = __LINE__;

    if( !type )
         type = OILS_AUTH_STAFF;

    oilsEvent* response = NULL; // free
    jsonObject* userObj = NULL; // free

    char* cache_key = va_list_to_string(
        "%s%s%s", OILS_AUTH_CACHE_PRFX, identifier, nonce);
    jsonObject* cacheObj = osrfCacheGetObject(cache_key); // free

    if (!cacheObj) {
        return osrfAppRequestRespondException(ctx->session,
            ctx->request, "No authentication seed found. "
            "open-ils.auth.authenticate.init must be called first "
            " (check that memcached is running and can be connected to) "
        );
    }

    int user_id = jsonObjectGetNumber(
        jsonObjectGetKeyConst(cacheObj, "user_id"));

    if (user_id == -1) {
        // User was not found during init.  Clean up and exit early.
        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
        osrfAppRespondComplete(ctx, oilsEventToJSON(response));
        oilsEventFree(response); // frees event JSON
        osrfCacheRemove(cache_key);
        jsonObjectFree(cacheObj);
        return 0;
    }

    jsonObject* param = jsonNewNumberObject(user_id); // free
    userObj = oilsUtilsCStoreReqCtx(
        ctx, "open-ils.cstore.direct.actor.user.retrieve", param);
    jsonObjectFree(param);

    char* freeable_uname = NULL;
    if (!uname) {
        uname = freeable_uname = oilsFMGetString(userObj, "usrname");
    }

    // See if the user is allowed to login.

    jsonObject* params = jsonNewObject(NULL);
    jsonObjectSetKey(params, "user_id", 
        jsonNewNumberObject(oilsFMGetObjectId(userObj)));
    jsonObjectSetKey(params,"org_unit", jsonNewNumberObject(orgloc));
    jsonObjectSetKey(params, "login_type", jsonNewObject(type));
    if (barcode) jsonObjectSetKey(params, "barcode", jsonNewObject(barcode));

    jsonObject* authEvt = oilsUtilsQuickReqCtx( // freed after password test
        ctx,
        "open-ils.auth_internal",
        "open-ils.auth_internal.user.validate", params);
    jsonObjectFree(params);

    if (!authEvt) {
        // Something went seriously wrong.  Get outta here before 
        // we start segfaulting.
        jsonObjectFree(userObj);
        if(freeable_uname) free(freeable_uname);
        return -1;
    }

    const char* authEvtCode = 
        jsonObjectGetString(jsonObjectGetKey(authEvt, "textcode"));

    if (!strcmp(authEvtCode, OILS_EVENT_AUTH_FAILED)) {
        // Received the generic login failure event.

        osrfLogInfo(OSRF_LOG_MARK,  
            "failed login: username=%s, barcode=%s, workstation=%s",
            uname, (barcode ? barcode : "(none)"), ws);

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
    }

    int passOK = 0;
    
    if (!response) {
        // User exists and is not barred, etc.  Test the password.

        passOK = oilsAuthVerifyPassword(
            ctx, user_id, identifier, password, nonce);

        if (!passOK) {
            // Password check failed. Return generic login failure.

            response = oilsNewEvent(
                __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);

            osrfLogInfo(OSRF_LOG_MARK,  
                "failed login: username=%s, barcode=%s, workstation=%s",
                    uname, (barcode ? barcode : "(none)"), ws );
        }
    }


    // Below here, we know the password check succeeded if no response
    // object is present.

    if (!response && (
        !strcmp(authEvtCode, "PATRON_INACTIVE") ||
        !strcmp(authEvtCode, "PATRON_CARD_INACTIVE"))) {
        // Patron and/or card is inactive but the correct password 
        // was provided.  Alert the caller to the inactive-ness.
        response = oilsNewEvent2(
            OSRF_LOG_MARK, authEvtCode,
            jsonObjectGetKey(authEvt, "payload")   // cloned within Event
        );
    }

    if (!response && strcmp(authEvtCode, OILS_EVENT_SUCCESS)) {
        // Validate API returned an unexpected non-success event.
        // To be safe, treat this as a generic login failure.

        response = oilsNewEvent(
            __FILE__, harmless_line_number, OILS_EVENT_AUTH_FAILED);
    }

    if (!response) {
        // password OK and no other events have prevented login completion.

        char* ewhat = "login";

        if (0 == strcmp(ctx->method->name, "open-ils.auth.authenticate.verify")) {
            response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_SUCCESS );
            ewhat = "verify";

        } else {
            response = oilsAuthHandleLoginOK(
                ctx, userObj, uname, type, orgloc, workstation);
        }

        oilsUtilsTrackUserActivity(
            ctx,
            oilsFMGetObjectId(userObj), 
            ewho, ewhat, 
            osrfAppSessionGetIngress()
        );
    }

    // reply
    osrfAppRespondComplete(ctx, oilsEventToJSON(response));

    // clean up
    oilsEventFree(response);
    jsonObjectFree(userObj);
    jsonObjectFree(authEvt);
    jsonObjectFree(cacheObj);
    if(freeable_uname)
        free(freeable_uname);

    return 0;
}
Пример #24
0
/**
	@brief Translate an osrfMessage into a jsonObject.
	@param msg Pointer to the osrfMessage to be translated.
	@return Pointer to a newly created jsonObject.

	The resulting jsonObject is a JSON_HASH with a classname of "osrfMessage", and the following keys:
	- "threadTrace"
	- "locale"
	- "type"
	- "payload" (only for STATUS, REQUEST, and RESULT messages)

	The data for "payload" is also a JSON_HASH, whose structure depends on the message type:

	For a STATUS message, the payload's classname is msg->status_name.  The keys are "status"
	(carrying msg->status_text) and "statusCode" (carrying the status code as a string).

	For a REQUEST message, the payload's classname is "osrfMethod".  The keys are "method"
	(carrying msg->method_name) and "params" (carrying a jsonObject to pass any parameters
	to the method call).

	For a RESULT message, the payload's classname is "osrfResult".  The keys are "status"
	(carrying msg->status_text), "statusCode" (carrying the status code as a string), and
	"content" (carrying a jsonObject to return any results from the method call).

	The calling code is responsible for freeing the returned jsonObject.
*/
jsonObject* osrfMessageToJSON( const osrfMessage* msg ) {

	jsonObject* json = jsonNewObjectType(JSON_HASH);
	jsonObjectSetClass(json, "osrfMessage");
	jsonObject* payload;
	char sc[64];
	osrf_clearbuf(sc, sizeof(sc));

	INT_TO_STRING(msg->thread_trace);
	jsonObjectSetKey(json, "threadTrace", jsonNewObject(INTSTR));

	if (msg->sender_locale != NULL) {
		jsonObjectSetKey(json, "locale", jsonNewObject(msg->sender_locale));
	} else if (current_locale != NULL) {
		jsonObjectSetKey(json, "locale", jsonNewObject(current_locale));
	} else {
		jsonObjectSetKey(json, "locale", jsonNewObject(default_locale));
	}

	switch(msg->m_type) {

		case CONNECT:
			jsonObjectSetKey(json, "type", jsonNewObject("CONNECT"));
			break;

		case DISCONNECT:
			jsonObjectSetKey(json, "type", jsonNewObject("DISCONNECT"));
			break;

		case STATUS:
			jsonObjectSetKey(json, "type", jsonNewObject("STATUS"));
			payload = jsonNewObject(NULL);
			jsonObjectSetClass(payload, msg->status_name);
			jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
			snprintf(sc, sizeof(sc), "%d", msg->status_code);
			jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
			jsonObjectSetKey(json, "payload", payload);
			break;

		case REQUEST:
			jsonObjectSetKey(json, "type", jsonNewObject("REQUEST"));
			payload = jsonNewObject(NULL);
			jsonObjectSetClass(payload, "osrfMethod");
			jsonObjectSetKey(payload, "method", jsonNewObject(msg->method_name));
			jsonObjectSetKey( payload, "params", jsonObjectDecodeClass( msg->_params ) );
			jsonObjectSetKey(json, "payload", payload);

			break;

		case RESULT:
			jsonObjectSetKey(json, "type", jsonNewObject("RESULT"));
			payload = jsonNewObject(NULL);
			jsonObjectSetClass(payload,"osrfResult");
			jsonObjectSetKey(payload, "status", jsonNewObject(msg->status_text));
			snprintf(sc, sizeof(sc), "%d", msg->status_code);
			jsonObjectSetKey(payload, "statusCode", jsonNewObject(sc));
			jsonObjectSetKey(payload, "content", jsonObjectDecodeClass( msg->_result_content ));
			jsonObjectSetKey(json, "payload", payload);
			break;
	}

	return json;
}
Пример #25
0
int osrfVersion( osrfMethodContext* ctx ) {
	if( osrfMethodVerifyContext( ctx ) ) {
		osrfLogError( OSRF_LOG_MARK,  "Invalid method context" );
		return -1;
	}

	/* First, see if the data is in the cache */
	char* json = jsonObjectToJSON(ctx->params);
	char* paramsmd5 = md5sum(json);
	char* cachedmd5 = osrfCacheGetString(paramsmd5);
	free(json);

	if( cachedmd5 ) {
		osrfLogDebug(OSRF_LOG_MARK,  "Found %s object in cache, returning....", cachedmd5 );
		jsonObject* resp = jsonNewObject(cachedmd5);
		osrfAppRespondComplete( ctx, resp  );
		jsonObjectFree(resp);
		free(paramsmd5);
		free(cachedmd5);
		return 0;
	}

	const jsonObject* serv = jsonObjectGetIndex(ctx->params, 0);
	const jsonObject* meth = jsonObjectGetIndex(ctx->params, 1);
	const char* service = jsonObjectGetString(serv);
	const char* methd = jsonObjectGetString(meth);

	if( service && methd ) {
		/* shove the additional params into an array */
		jsonObject* tmpArray = jsonNewObject(NULL);
		int i;
		for( i = 2; i != ctx->params->size; i++ )
			jsonObjectPush( tmpArray, jsonObjectClone(jsonObjectGetIndex(ctx->params, i)));

		osrfAppSession* ses = osrfAppSessionClientInit(service);
		int reqid = osrfAppSessionSendRequest( ses, tmpArray, methd, 1 );
		osrfMessage* omsg = osrfAppSessionRequestRecv( ses, reqid, 60 );
		jsonObjectFree(tmpArray);

		if( omsg ) {

			const jsonObject* result = osrfMessageGetResult( omsg );
			char* resultjson = jsonObjectToJSON(result);
			char* resultmd5 = md5sum(resultjson);
			free(resultjson);
			osrfMessageFree(omsg);

			if( resultmd5 ) {
				jsonObject* resp = jsonNewObject(resultmd5);
				osrfAppRespondComplete( ctx, resp );
				jsonObjectFree(resp);
				osrfAppSessionFree(ses);
				osrfLogDebug(OSRF_LOG_MARK, 
					"Found version string %s, caching and returning...", resultmd5 );
				osrfCachePutString( paramsmd5, resultmd5, OSRF_VERSION_CACHE_TIME );
				free(resultmd5);
				free(paramsmd5);
				return 0;
			}
		}
		osrfAppSessionFree(ses);
	}

	free(paramsmd5);

	return -1;
}
Пример #26
0
START_TEST(test_osrf_message_set_method)
  osrf_message_set_method(o, NULL);
  fail_unless(o->method_name == NULL,
      "osrf_message_set_method should return NULL if given a NULL method_name arg");
  osrf_message_set_method(o, "add");
  fail_unless(strcmp(o->method_name, "add") == 0,
      "osrf_message_set_method should set osrfMessage->method_name if successful");
END_TEST

START_TEST(test_osrf_message_set_params)
  osrf_message_set_params(o, NULL);
  fail_unless(o->_params == NULL,
      "osrf_message_set_params should set msg->_params to NULL when passed a NULL o arg");
  jsonObject *testJSONObject;
  testJSONObject = jsonNewObject("test");
  osrf_message_set_params(o, testJSONObject);
  fail_unless(strcmp(jsonObjectGetIndex(o->_params, 0)->value.s, "test") == 0,
      "osrf_message_set_params should set msg->_params to an array containing the\
      jsonObject passed");
  jsonObjectFree(testJSONObject);
END_TEST

//END Tests

Suite *osrf_message_suite(void) {
  //Create test suite, test case, initialize fixture
  Suite *s = suite_create("osrf_message");
  TCase *tc_core = tcase_create("Core");
  tcase_add_checked_fixture (tc_core, setup, teardown);
void* osrf_responder_thread_main_body(transport_message *tmsg) {

    osrfList *msg_list = NULL;
    osrfMessage *one_msg = NULL;
    int i;

    osrfLogDebug(OSRF_LOG_MARK, 
        "WS received opensrf response for thread=%s", tmsg->thread);

    // first we need to perform some maintenance
    msg_list = osrfMessageDeserialize(tmsg->body, NULL);

    for (i = 0; i < msg_list->size; i++) {
        one_msg = OSRF_LIST_GET_INDEX(msg_list, i);

        osrfLogDebug(OSRF_LOG_MARK, 
            "WS returned response of type %d", one_msg->m_type);

        /*  if our client just successfully connected to an opensrf service,
            cache the sender so that future calls on this thread will use
            the correct recipient. */
        if (one_msg && one_msg->m_type == STATUS) {

            if (one_msg->status_code == OSRF_STATUS_OK) {

                if (!apr_hash_get(trans->stateful_session_cache, 
                        tmsg->thread, APR_HASH_KEY_STRING)) {

                    apr_size_t ses_size = 
                        apr_hash_count(trans->stateful_session_cache);

                    if (ses_size < MAX_ACTIVE_STATEFUL_SESSIONS) {

                        osrfLogDebug(OSRF_LOG_MARK, "WS caching sender "
                            "thread=%s, sender=%s; concurrent=%d", 
                            tmsg->thread, tmsg->sender, ses_size);

                        apr_hash_set(trans->stateful_session_cache, 
                            apr_pstrdup(trans->stateful_session_pool, tmsg->thread),
                            APR_HASH_KEY_STRING, 
                            apr_pstrdup(trans->stateful_session_pool, tmsg->sender));

                    } else {
                        osrfLogWarning(OSRF_LOG_MARK, 
                            "WS max concurrent sessions (%d) reached.  "
                            "Current session will not be tracked",
                            MAX_ACTIVE_STATEFUL_SESSIONS
                        );
                    }
                }

            } else {

                // connection timed out; clear the cached recipient
                if (one_msg->status_code == OSRF_STATUS_TIMEOUT) {
                    clear_cached_recipient(tmsg->thread);

                } else {
                    if (one_msg->status_code == OSRF_STATUS_COMPLETE)
                        requests_in_flight--;
                }
            }
        }
    }

    // osrfMessageDeserialize applies the freeItem handler to the 
    // newly created osrfList.  We only need to free the list and 
    // the individual osrfMessage's will be freed along with it
    osrfListFree(msg_list);
    
    // relay the response messages to the client
    jsonObject *msg_wrapper = NULL;
    char *msg_string = NULL;

    // build the wrapper object
    msg_wrapper = jsonNewObject(NULL);
    jsonObjectSetKey(msg_wrapper, "thread", jsonNewObject(tmsg->thread));
    jsonObjectSetKey(msg_wrapper, "log_xid", jsonNewObject(tmsg->osrf_xid));
    jsonObjectSetKey(msg_wrapper, "osrf_msg", jsonParseRaw(tmsg->body));

    if (tmsg->is_error) {
        osrfLogError(OSRF_LOG_MARK, 
            "WS received jabber error message in response to thread=%s", 
            tmsg->thread);
        jsonObjectSetKey(msg_wrapper, "transport_error", jsonNewBoolObject(1));
    }

    msg_string = jsonObjectToJSONRaw(msg_wrapper);

    // drop the JSON on the outbound wire
    trans->server->send(trans->server, MESSAGE_TYPE_TEXT, 
        (unsigned char*) msg_string, strlen(msg_string));

    free(msg_string);
    jsonObjectFree(msg_wrapper);
}