]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_auth.c
In oils_auth.c: Fixed a bug.
[Evergreen.git] / Open-ILS / src / c-apps / oils_auth.c
1 #include "opensrf/osrf_app_session.h"
2 #include "opensrf/osrf_application.h"
3 #include "opensrf/osrf_settings.h"
4 #include "opensrf/osrf_json.h"
5 #include "opensrf/log.h"
6 #include "openils/oils_utils.h"
7 #include "openils/oils_constants.h"
8 #include "openils/oils_event.h"
9
10 #define OILS_AUTH_CACHE_PRFX "oils_auth_"
11
12 #define MODULENAME "open-ils.auth"
13
14 #define OILS_AUTH_OPAC "opac"
15 #define OILS_AUTH_STAFF "staff"
16 #define OILS_AUTH_TEMP "temp"
17
18 int osrfAppInitialize();
19 int osrfAppChildInit();
20
21 static int _oilsAuthOPACTimeout = 0;
22 static int _oilsAuthStaffTimeout = 0;
23 static int _oilsAuthOverrideTimeout = 0;
24
25
26 int osrfAppInitialize() {
27
28         osrfLogInfo(OSRF_LOG_MARK, "Initializing Auth Server...");
29
30     /* load and parse the IDL */
31         if (!oilsInitIDL(NULL)) return 1; /* return non-zero to indicate error */
32
33         osrfAppRegisterMethod( 
34                 MODULENAME, 
35                 "open-ils.auth.authenticate.init", 
36                 "oilsAuthInit", 
37                 "Start the authentication process and returns the intermediate authentication seed"
38                 " PARAMS( username )", 1, 0 );
39
40         osrfAppRegisterMethod( 
41                 MODULENAME, 
42                 "open-ils.auth.authenticate.complete", 
43                 "oilsAuthComplete", 
44                 "Completes the authentication process.  Returns an object like so: "
45                 "{authtoken : <token>, authtime:<time>}, where authtoken is the login "
46                 "token and authtime is the number of seconds the session will be active"
47                 "PARAMS(username, md5sum( seed + md5sum( password ) ), type, org_id ) "
48                 "type can be one of 'opac','staff', or 'temp' and it defaults to 'staff' "
49                 "org_id is the location at which the login should be considered "
50                 "active for login timeout purposes"     , 1, 0 );
51
52         osrfAppRegisterMethod( 
53                 MODULENAME, 
54                 "open-ils.auth.session.retrieve", 
55                 "oilsAuthSessionRetrieve", 
56                 "Pass in the auth token and this retrieves the user object.  The auth "
57                 "timeout is reset when this call is made "
58                 "Returns the user object (password blanked) for the given login session "
59                 "PARAMS( authToken )", 1, 0 );
60
61         osrfAppRegisterMethod( 
62                 MODULENAME, 
63                 "open-ils.auth.session.delete", 
64                 "oilsAuthSessionDelete", 
65                 "Destroys the given login session "
66                 "PARAMS( authToken )",  1, 0 );
67
68         osrfAppRegisterMethod(
69                 MODULENAME,
70                 "open-ils.auth.session.reset_timeout",
71                 "oilsAuthResetTimeout",
72                 "Resets the login timeout for the given session "
73                 "Returns an ILS Event with payload = session_timeout of session "
74                 "if found, otherwise returns the NO_SESSION event"
75                 "PARAMS( authToken )", 1, 0 );
76
77         return 0;
78 }
79
80 int osrfAppChildInit() {
81         return 0;
82 }
83
84 int oilsAuthInit( osrfMethodContext* ctx ) {
85         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
86
87         jsonObject* resp;
88
89         char* username = NULL;
90         char* seed              = NULL;
91         char* md5seed   = NULL;
92         char* key               = NULL;
93
94         if( (username = jsonObjectToSimpleString(jsonObjectGetIndex(ctx->params, 0))) ) {
95
96                 if( strchr( username, ' ' ) ) {
97
98                         /* spaces are not allowed */
99                         resp = jsonNewObject("x");       /* 'x' will never be a valid seed */
100                         osrfAppRespondComplete( ctx, resp );
101
102                 } else {
103
104                         seed = va_list_to_string( "%d.%ld.%s", time(NULL), (long) getpid(), username );
105                         key = va_list_to_string( "%s%s", OILS_AUTH_CACHE_PRFX, username );
106         
107                         md5seed = md5sum(seed);
108                         osrfCachePutString( key, md5seed, 30 );
109         
110                         osrfLogDebug( OSRF_LOG_MARK, "oilsAuthInit(): has seed %s and key %s", md5seed, key );
111         
112                         resp = jsonNewObject(md5seed);  
113                         osrfAppRespondComplete( ctx, resp );
114         
115                         free(seed);
116                         free(md5seed);
117                         free(key);
118                 }
119
120                 jsonObjectFree(resp);
121                 free(username);
122                 return 0;
123         }
124
125         return -1;
126 }
127
128 /** Verifies that the user has permission to login with the 
129  * given type.  If the permission fails, an oilsEvent is returned
130  * to the caller.
131  * @return -1 if the permission check failed, 0 if the permission
132  * is granted
133  */
134 static int oilsAuthCheckLoginPerm( 
135                 osrfMethodContext* ctx, const jsonObject* userObj, const char* type ) {
136
137         if(!(userObj && type)) return -1;
138         oilsEvent* perm = NULL;
139
140         if(!strcasecmp(type, OILS_AUTH_OPAC)) {
141                 char* permissions[] = { "OPAC_LOGIN" };
142                 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
143
144         } else if(!strcasecmp(type, OILS_AUTH_STAFF)) {
145                 char* permissions[] = { "STAFF_LOGIN" };
146                 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
147
148         } else if(!strcasecmp(type, OILS_AUTH_TEMP)) {
149                 char* permissions[] = { "STAFF_LOGIN" };
150                 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
151         }
152
153         if(perm) {
154                 osrfAppRespondComplete( ctx, oilsEventToJSON(perm) ); 
155                 oilsEventFree(perm);
156                 return -1;
157         }
158
159         return 0;
160 }
161
162 /**
163  * Returns 1 if the password provided matches the user's real password
164  * Returns 0 otherwise
165  * Returns -1 on error
166  */
167 static int oilsAuthVerifyPassword( const osrfMethodContext* ctx,
168                 const jsonObject* userObj, const char* uname, const char* password ) {
169
170         int ret = 0;
171         char* realPassword = oilsFMGetString( userObj, "passwd" ); /**/
172         char* seed = osrfCacheGetString( "%s%s", OILS_AUTH_CACHE_PRFX, uname ); /**/
173
174         if(!seed) {
175                 free(realPassword);
176                 return osrfAppRequestRespondException( ctx->session,
177                         ctx->request, "No authentication seed found. "
178                         "open-ils.auth.authenticate.init must be called first");
179         }
180
181         osrfLogInternal(OSRF_LOG_MARK, "oilsAuth retrieved real password: [%s]", realPassword);
182         osrfLogDebug(OSRF_LOG_MARK,  "oilsAuth retrieved seed from cache: %s", seed );
183         char* maskedPw = md5sum( "%s%s", seed, realPassword );
184         free(realPassword);
185         free(seed);
186
187         if(!maskedPw)
188                 return -1;
189
190         osrfLogDebug(OSRF_LOG_MARK,  "oilsAuth generated masked password %s. "
191                         "Testing against provided password %s", maskedPw, password );
192
193         if( !strcmp( maskedPw, password ) ) ret = 1;
194
195         free(maskedPw);
196
197         return ret;
198 }
199
200 /**
201  * Calculates the login timeout
202  * 1. If orgloc is 1 or greater and has a timeout specified as an 
203  * org unit setting, it is used
204  * 2. If orgloc is not valid, we check the org unit auth timeout 
205  * setting for the home org unit of the user logging in
206  * 3. If that setting is not defined, we use the configured defaults
207  */
208 static double oilsAuthGetTimeout( const jsonObject* userObj, const char* type, double orgloc ) {
209
210         if(!_oilsAuthOPACTimeout) { /* Load the default timeouts */
211
212                 jsonObject* value_obj;
213
214                 value_obj = osrf_settings_host_value_object(
215                         "/apps/open-ils.auth/app_settings/default_timeout/opac" );
216                 _oilsAuthOPACTimeout = jsonObjectGetNumber(value_obj);
217                 jsonObjectFree(value_obj);
218
219                 value_obj = osrf_settings_host_value_object(
220                         "/apps/open-ils.auth/app_settings/default_timeout/staff" );
221                 _oilsAuthStaffTimeout = jsonObjectGetNumber(value_obj);
222                 jsonObjectFree(value_obj);
223
224                 value_obj = osrf_settings_host_value_object(
225                                 "/apps/open-ils.auth/app_settings/default_timeout/temp" );
226                 _oilsAuthOverrideTimeout = jsonObjectGetNumber(value_obj);
227                 jsonObjectFree(value_obj);
228
229
230                 osrfLogInfo(OSRF_LOG_MARK, "Set default auth timeouts: opac => %d : staff => %d : temp => %d",
231                                 _oilsAuthOPACTimeout, _oilsAuthStaffTimeout, _oilsAuthOverrideTimeout );
232         }
233
234         char* setting = NULL;
235
236         double home_ou = jsonObjectGetNumber( oilsFMGetObject( userObj, "home_ou" ) );
237         if(orgloc < 1) orgloc = (int) home_ou;
238
239         if(!strcmp(type, OILS_AUTH_OPAC)) 
240                 setting = OILS_ORG_SETTING_OPAC_TIMEOUT;
241         else if(!strcmp(type, OILS_AUTH_STAFF)) 
242                 setting = OILS_ORG_SETTING_STAFF_TIMEOUT;
243         else if(!strcmp(type, OILS_AUTH_TEMP)) 
244                 setting = OILS_ORG_SETTING_TEMP_TIMEOUT;
245
246         char* timeout = oilsUtilsFetchOrgSetting( orgloc, setting );
247
248         if(!timeout) {
249                 if( orgloc != home_ou ) {
250                         osrfLogDebug(OSRF_LOG_MARK, "Auth timeout not defined for org %d, "
251                                                                 "trying home_ou %d", orgloc, home_ou );
252                         timeout = oilsUtilsFetchOrgSetting( (int) home_ou, setting );
253                 }
254                 if(!timeout) {
255                         if(!strcmp(type, OILS_AUTH_STAFF)) return _oilsAuthStaffTimeout;
256                         if(!strcmp(type, OILS_AUTH_TEMP)) return _oilsAuthOverrideTimeout;
257                         return _oilsAuthOPACTimeout;
258                 }
259         }
260
261         double t = atof(timeout);
262         free(timeout);
263         return t ;
264 }
265
266 /* Adds the authentication token to the user cache.  The timeout for the 
267  * auth token is based on the type of login as well as (if type=='opac') 
268  * the org location id.
269  * Returns the event that should be returned to the user.  
270  * Event must be freed
271  */
272 static oilsEvent* oilsAuthHandleLoginOK( jsonObject* userObj, const char* uname,
273                 const char* type, double orgloc, const char* workstation ) {
274                 
275         oilsEvent* response;
276
277         double timeout;
278         char* wsorg = jsonObjectToSimpleString(oilsFMGetObject(userObj, "ws_ou"));
279         if(wsorg) { /* if there is a workstation, use it for the timeout */
280                 osrfLogDebug( OSRF_LOG_MARK, 
281                                 "Auth session trying workstation id %d for auth timeout", atoi(wsorg));
282                 timeout = oilsAuthGetTimeout( userObj, type, atoi(wsorg) );
283                 free(wsorg);
284         } else {
285                 osrfLogDebug( OSRF_LOG_MARK, 
286                                 "Auth session trying org from param [%d] for auth timeout", orgloc );
287                 timeout = oilsAuthGetTimeout( userObj, type, orgloc );
288         }
289         osrfLogDebug(OSRF_LOG_MARK, "Auth session timeout for %s: %f", uname, timeout );
290
291         char* string = va_list_to_string( 
292                         "%d.%ld.%s", (long) getpid(), time(NULL), uname ); 
293         char* authToken = md5sum(string); 
294         char* authKey = va_list_to_string( 
295                         "%s%s", OILS_AUTH_CACHE_PRFX, authToken ); 
296
297         const char* ws = (workstation) ? workstation : "";
298         osrfLogActivity(OSRF_LOG_MARK,  
299                 "successful login: username=%s, authtoken=%s, workstation=%s", uname, authToken, ws );
300
301         oilsFMSetString( userObj, "passwd", "" );
302         jsonObject* cacheObj = jsonParseStringFmt("{\"authtime\": %f}", timeout);
303         jsonObjectSetKey( cacheObj, "userobj", jsonObjectClone(userObj));
304
305         osrfCachePutObject( authKey, cacheObj, timeout ); 
306         jsonObjectFree(cacheObj);
307         osrfLogInternal(OSRF_LOG_MARK, "oilsAuthHandleLoginOK(): Placed user object into cache");
308         jsonObject* payload = jsonParseStringFmt(
309                 "{ \"authtoken\": \"%s\", \"authtime\": %f }", authToken, timeout );
310
311         response = oilsNewEvent2( OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload );
312         free(string); free(authToken); free(authKey);
313         jsonObjectFree(payload);
314
315         return response;
316 }
317
318 static oilsEvent* oilsAuthVerifyWorkstation( 
319                 const osrfMethodContext* ctx, jsonObject* userObj, const char* ws ) {
320         osrfLogInfo(OSRF_LOG_MARK, "Attaching workstation to user at login: %s", ws);
321         jsonObject* workstation = oilsUtilsFetchWorkstationByName(ws);
322         if(!workstation || workstation->type == JSON_NULL) {
323         jsonObjectFree(workstation);
324         return oilsNewEvent(OSRF_LOG_MARK, "WORKSTATION_NOT_FOUND");
325     }
326         long wsid = oilsFMGetObjectId(workstation);
327         LONG_TO_STRING(wsid);
328         char* orgid = oilsFMGetString(workstation, "owning_lib");
329         oilsFMSetString(userObj, "wsid", LONGSTR);
330         oilsFMSetString(userObj, "ws_ou", orgid);
331         free(orgid);
332         jsonObjectFree(workstation);
333         return NULL;
334 }
335
336
337
338 /* see if the card used to login is marked as barred */
339 static oilsEvent* oilsAuthCheckCard( const char* barcode ) {
340         if(!barcode) return NULL;
341         osrfLogDebug(OSRF_LOG_MARK, "Checking to see if barcode %s is active", barcode);
342
343         jsonObject* params = jsonParseStringFmt("{\"barcode\":\"%s\"}", barcode);
344         jsonObject* card = oilsUtilsQuickReq(
345                 "open-ils.cstore", "open-ils.cstore.direct.actor.card.search", params );
346         jsonObjectFree(params);
347
348         char* active = oilsFMGetString(card, "active");
349         jsonObjectFree(card);
350
351         oilsEvent* return_event = NULL;
352         if( ! oilsUtilsIsDBTrue(active) ) {
353                 osrfLogInfo(OSRF_LOG_MARK, "barcode %s is not active, returning event", barcode);
354                 return_event = oilsNewEvent(OSRF_LOG_MARK, "PATRON_CARD_INACTIVE");
355         }
356
357         free(active);
358         return return_event;
359 }
360
361
362
363 int oilsAuthComplete( osrfMethodContext* ctx ) {
364         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
365
366         const jsonObject* args  = jsonObjectGetIndex(ctx->params, 0);
367
368         const char* uname               = jsonObjectGetString(jsonObjectGetKeyConst(args, "username"));
369         const char* password    = jsonObjectGetString(jsonObjectGetKeyConst(args, "password"));
370         const char* type                = jsonObjectGetString(jsonObjectGetKeyConst(args, "type"));
371         double orgloc                   = jsonObjectGetNumber(jsonObjectGetKeyConst(args, "org"));
372         const char* workstation = jsonObjectGetString(jsonObjectGetKeyConst(args, "workstation"));
373         char* barcode                   = jsonObjectToSimpleString(jsonObjectGetKeyConst(args, "barcode"));
374
375         const char* ws = (workstation) ? workstation : "";
376
377
378         if(!type) type = OILS_AUTH_STAFF;
379
380         if( !( (uname || barcode) && password) ) {
381                 free(barcode);
382                 return osrfAppRequestRespondException( ctx->session, ctx->request, 
383                         "username/barcode and password required for method: %s", ctx->method->name );
384         }
385
386         oilsEvent* response = NULL;
387         jsonObject* userObj = NULL;
388
389         if(uname) {
390                 userObj = oilsUtilsFetchUserByUsername( uname );
391                 if( userObj && JSON_NULL == userObj->type ) {
392                         jsonObjectFree( userObj );
393                         userObj = NULL;         // username not found
394                 }
395         }
396         else if(barcode)
397                  userObj = oilsUtilsFetchUserByBarcode( barcode );
398         
399         if(!userObj) { 
400                 response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_AUTH_FAILED );
401                 osrfLogInfo(OSRF_LOG_MARK,  "failed login: username=%s, barcode=%s, workstation=%s", uname, barcode, ws );
402                 osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); 
403                 oilsEventFree(response);
404                 free(barcode);
405                 return 0;
406         }
407
408         /* first let's see if they have the right credentials */
409         int passOK = -1;
410         if(uname) passOK = oilsAuthVerifyPassword( ctx, userObj, uname, password );
411         else if (barcode) 
412                 passOK = oilsAuthVerifyPassword( ctx, userObj, barcode, password );
413
414         if( passOK < 0 ) {
415                 jsonObjectFree(userObj);
416                 free(barcode);
417                 return passOK;
418         }
419
420         /* first see if their account is inactive */
421         char* active = oilsFMGetString(userObj, "active");
422         if( !oilsUtilsIsDBTrue(active) ) {
423                 response = oilsNewEvent(OSRF_LOG_MARK, "PATRON_INACTIVE");
424                 osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); 
425                 oilsEventFree(response);
426                 jsonObjectFree(userObj);
427                 free(barcode);
428                 free(active);
429                 return 0;
430         }
431         free(active);
432
433         /* then see if the barcode they used is active */
434         if( barcode && ctx && userObj && (response = oilsAuthCheckCard( barcode )) ) {
435                 osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); 
436                 oilsEventFree(response);
437                 jsonObjectFree(userObj);
438                 free(barcode);
439                 return 0;
440         }
441
442
443         /* check to see if the user is even allowed to login */
444         if( oilsAuthCheckLoginPerm( ctx, userObj, type ) == -1 ) {
445                 jsonObjectFree(userObj);
446                 free(barcode);
447                 return 0;
448         }
449         
450
451         /* if a workstation is defined, flesh the user with the workstation info */
452         if( workstation != NULL ) {
453                 osrfLogDebug(OSRF_LOG_MARK, "Workstation is %s", workstation);
454                 response = oilsAuthVerifyWorkstation( ctx, userObj, workstation );
455                 if(response) {
456                         jsonObjectFree(userObj);
457                         osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); 
458                         oilsEventFree(response);
459                         free(barcode);
460                         return 0;
461                 }
462
463         } else {
464                 /* otherwise, use the home org as the workstation org on the user */
465                 char* orgid = oilsFMGetString(userObj, "home_ou");
466                 oilsFMSetString(userObj, "ws_ou", orgid);
467                 free(orgid);
468         }
469
470         char* freeable_uname = NULL;
471         if(!uname) {
472                 uname = freeable_uname = oilsFMGetString( userObj, "usrname" );
473         }
474
475         if( passOK ) {
476                 response = oilsAuthHandleLoginOK( userObj, uname, type, orgloc, workstation );
477
478         } else {
479                 response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_AUTH_FAILED );
480                 osrfLogInfo(OSRF_LOG_MARK,  "failed login: username=%s, barcode=%s, workstation=%s", uname, barcode, ws );
481         }
482
483         jsonObjectFree(userObj);
484         osrfAppRespondComplete( ctx, oilsEventToJSON(response) ); 
485         oilsEventFree(response);
486         free(barcode);
487
488         if(freeable_uname) free(freeable_uname);
489
490         return 0;
491 }
492
493
494
495 int oilsAuthSessionDelete( osrfMethodContext* ctx ) {
496         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
497
498         const char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
499         jsonObject* resp = NULL;
500
501         if( authToken ) {
502                 osrfLogDebug(OSRF_LOG_MARK, "Removing auth session: %s", authToken );
503                 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); /**/
504                 osrfCacheRemove(key);
505                 resp = jsonNewObject(authToken); /**/
506                 free(key);
507         }
508
509         osrfAppRespondComplete( ctx, resp );
510         jsonObjectFree(resp);
511         return 0;
512 }
513
514 /** Resets the auth login timeout
515  * @return The event object, OILS_EVENT_SUCCESS, or OILS_EVENT_NO_SESSION
516  */
517 static oilsEvent*  _oilsAuthResetTimeout( const char* authToken ) {
518         if(!authToken) return NULL;
519
520         oilsEvent* evt = NULL;
521         double timeout;
522
523         osrfLogDebug(OSRF_LOG_MARK, "Resetting auth timeout for session %s", authToken);
524         char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); 
525         jsonObject* cacheObj = osrfCacheGetObject( key ); 
526
527         if(!cacheObj) {
528                 osrfLogInfo(OSRF_LOG_MARK, "No user in the cache exists with key %s", key);
529                 evt = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_NO_SESSION);
530
531         } else {
532
533                 timeout = jsonObjectGetNumber( jsonObjectGetKeyConst( cacheObj, "authtime"));
534                 osrfCacheSetExpire( timeout, key );
535                 jsonObject* payload = jsonNewNumberObject(timeout);
536                 evt = oilsNewEvent2(OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload);
537                 jsonObjectFree(payload);
538                 jsonObjectFree(cacheObj);
539         }
540
541         free(key);
542         return evt;
543 }
544
545 int oilsAuthResetTimeout( osrfMethodContext* ctx ) {
546         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
547         const char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
548         oilsEvent* evt = _oilsAuthResetTimeout(authToken);
549         osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
550         oilsEventFree(evt);
551         return 0;
552 }
553
554
555 int oilsAuthSessionRetrieve( osrfMethodContext* ctx ) {
556         OSRF_METHOD_VERIFY_CONTEXT(ctx); 
557
558         const char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
559         jsonObject* cacheObj = NULL;
560         oilsEvent* evt = NULL;
561
562         if( authToken ){
563
564                 evt = _oilsAuthResetTimeout(authToken);
565
566                 if( evt && strcmp(evt->event, OILS_EVENT_SUCCESS) ) {
567                         osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
568
569                 } else {
570
571                         osrfLogDebug(OSRF_LOG_MARK, "Retrieving auth session: %s", authToken);
572                         char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); 
573                         cacheObj = osrfCacheGetObject( key ); 
574                         if(cacheObj) {
575                                 osrfAppRespondComplete( ctx, jsonObjectGetKeyConst( cacheObj, "userobj"));
576                                 jsonObjectFree(cacheObj);
577                         } else {
578                                 oilsEvent* evt2 = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_NO_SESSION);
579                                 osrfAppRespondComplete( ctx, oilsEventToJSON(evt2) ); /* should be event.. */
580                                 oilsEventFree(evt2);
581                         }
582                         free(key);
583                 }
584
585         } else {
586
587                 evt = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_NO_SESSION);
588                 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
589         }
590
591         if(evt)
592                 oilsEventFree(evt);
593
594         return 0;
595 }
596
597
598