Tidying up white space. No substantive changes.
[working/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 /**
129         Verifies that the user has permission to login with the
130         given type.  If the permission fails, an oilsEvent is returned
131         to the caller.
132         @return -1 if the permission check failed, 0 if the permission
133         is granted
134 */
135 static int oilsAuthCheckLoginPerm(
136                 osrfMethodContext* ctx, const jsonObject* userObj, const char* type ) {
137
138         if(!(userObj && type)) return -1;
139         oilsEvent* perm = NULL;
140
141         if(!strcasecmp(type, OILS_AUTH_OPAC)) {
142                 char* permissions[] = { "OPAC_LOGIN" };
143                 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
144
145         } else if(!strcasecmp(type, OILS_AUTH_STAFF)) {
146                 char* permissions[] = { "STAFF_LOGIN" };
147                 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
148
149         } else if(!strcasecmp(type, OILS_AUTH_TEMP)) {
150                 char* permissions[] = { "STAFF_LOGIN" };
151                 perm = oilsUtilsCheckPerms( oilsFMGetObjectId( userObj ), -1, permissions, 1 );
152         }
153
154         if(perm) {
155                 osrfAppRespondComplete( ctx, oilsEventToJSON(perm) );
156                 oilsEventFree(perm);
157                 return -1;
158         }
159
160         return 0;
161 }
162
163 /**
164         Returns 1 if the password provided matches the user's real password
165         Returns 0 otherwise
166         Returns -1 on error
167 */
168 static int oilsAuthVerifyPassword( const osrfMethodContext* ctx,
169                 const jsonObject* userObj, const char* uname, const char* password ) {
170
171         int ret = 0;
172         char* realPassword = oilsFMGetString( userObj, "passwd" ); /**/
173         char* seed = osrfCacheGetString( "%s%s", OILS_AUTH_CACHE_PRFX, uname ); /**/
174
175         if(!seed) {
176                 free(realPassword);
177                 return osrfAppRequestRespondException( ctx->session,
178                         ctx->request, "No authentication seed found. "
179                         "open-ils.auth.authenticate.init must be called first");
180         }
181
182         osrfLogInternal(OSRF_LOG_MARK, "oilsAuth retrieved real password: [%s]", realPassword);
183         osrfLogDebug(OSRF_LOG_MARK,  "oilsAuth retrieved seed from cache: %s", seed );
184         char* maskedPw = md5sum( "%s%s", seed, realPassword );
185         free(realPassword);
186         free(seed);
187
188         if(!maskedPw)
189                 return -1;
190
191         osrfLogDebug(OSRF_LOG_MARK,  "oilsAuth generated masked password %s. "
192                         "Testing against provided password %s", maskedPw, password );
193
194         if( !strcmp( maskedPw, password ) ) ret = 1;
195
196         free(maskedPw);
197
198         return ret;
199 }
200
201 /**
202         Calculates the login timeout
203         1. If orgloc is 1 or greater and has a timeout specified as an
204         org unit setting, it is used
205         2. If orgloc is not valid, we check the org unit auth timeout
206         setting for the home org unit of the user logging in
207         3. If that setting is not defined, we use the configured defaults
208 */
209 static double oilsAuthGetTimeout( const jsonObject* userObj, const char* type, double orgloc ) {
210
211         if(!_oilsAuthOPACTimeout) { /* Load the default timeouts */
212
213                 jsonObject* value_obj;
214
215                 value_obj = osrf_settings_host_value_object(
216                         "/apps/open-ils.auth/app_settings/default_timeout/opac" );
217                 _oilsAuthOPACTimeout = jsonObjectGetNumber(value_obj);
218                 jsonObjectFree(value_obj);
219
220                 value_obj = osrf_settings_host_value_object(
221                         "/apps/open-ils.auth/app_settings/default_timeout/staff" );
222                 _oilsAuthStaffTimeout = jsonObjectGetNumber(value_obj);
223                 jsonObjectFree(value_obj);
224
225                 value_obj = osrf_settings_host_value_object(
226                                 "/apps/open-ils.auth/app_settings/default_timeout/temp" );
227                 _oilsAuthOverrideTimeout = jsonObjectGetNumber(value_obj);
228                 jsonObjectFree(value_obj);
229
230
231                 osrfLogInfo(OSRF_LOG_MARK,
232                                 "Set default auth timeouts: opac => %d : staff => %d : temp => %d",
233                                 _oilsAuthOPACTimeout, _oilsAuthStaffTimeout, _oilsAuthOverrideTimeout );
234         }
235
236         char* setting = NULL;
237
238         double home_ou = jsonObjectGetNumber( oilsFMGetObject( userObj, "home_ou" ) );
239         if(orgloc < 1) orgloc = (int) home_ou;
240
241         if(!strcmp(type, OILS_AUTH_OPAC))
242                 setting = OILS_ORG_SETTING_OPAC_TIMEOUT;
243         else if(!strcmp(type, OILS_AUTH_STAFF))
244                 setting = OILS_ORG_SETTING_STAFF_TIMEOUT;
245         else if(!strcmp(type, OILS_AUTH_TEMP))
246                 setting = OILS_ORG_SETTING_TEMP_TIMEOUT;
247
248         char* timeout = oilsUtilsFetchOrgSetting( orgloc, setting );
249
250         if(!timeout) {
251                 if( orgloc != home_ou ) {
252                         osrfLogDebug(OSRF_LOG_MARK, "Auth timeout not defined for org %d, "
253                                                                 "trying home_ou %d", orgloc, home_ou );
254                         timeout = oilsUtilsFetchOrgSetting( (int) home_ou, setting );
255                 }
256                 if(!timeout) {
257                         if(!strcmp(type, OILS_AUTH_STAFF)) return _oilsAuthStaffTimeout;
258                         if(!strcmp(type, OILS_AUTH_TEMP)) return _oilsAuthOverrideTimeout;
259                         return _oilsAuthOPACTimeout;
260                 }
261         }
262
263         double t = atof(timeout);
264         free(timeout);
265         return t ;
266 }
267
268 /*
269         Adds the authentication token to the user cache.  The timeout for the
270         auth token is based on the type of login as well as (if type=='opac')
271         the org location id.
272         Returns the event that should be returned to the user.
273         Event must be freed
274 */
275 static oilsEvent* oilsAuthHandleLoginOK( jsonObject* userObj, const char* uname,
276                 const char* type, double orgloc, const char* workstation ) {
277
278         oilsEvent* response;
279
280         double timeout;
281         char* wsorg = jsonObjectToSimpleString(oilsFMGetObject(userObj, "ws_ou"));
282         if(wsorg) { /* if there is a workstation, use it for the timeout */
283                 osrfLogDebug( OSRF_LOG_MARK,
284                                 "Auth session trying workstation id %d for auth timeout", atoi(wsorg));
285                 timeout = oilsAuthGetTimeout( userObj, type, atoi(wsorg) );
286                 free(wsorg);
287         } else {
288                 osrfLogDebug( OSRF_LOG_MARK,
289                                 "Auth session trying org from param [%d] for auth timeout", orgloc );
290                 timeout = oilsAuthGetTimeout( userObj, type, orgloc );
291         }
292         osrfLogDebug(OSRF_LOG_MARK, "Auth session timeout for %s: %f", uname, timeout );
293
294         char* string = va_list_to_string(
295                         "%d.%ld.%s", (long) getpid(), time(NULL), uname );
296         char* authToken = md5sum(string);
297         char* authKey = va_list_to_string(
298                         "%s%s", OILS_AUTH_CACHE_PRFX, authToken );
299
300         const char* ws = (workstation) ? workstation : "";
301         osrfLogActivity(OSRF_LOG_MARK,
302                 "successful login: username=%s, authtoken=%s, workstation=%s", uname, authToken, ws );
303
304         oilsFMSetString( userObj, "passwd", "" );
305         jsonObject* cacheObj = jsonParseFmt("{\"authtime\": %f}", timeout);
306         jsonObjectSetKey( cacheObj, "userobj", jsonObjectClone(userObj));
307
308         osrfCachePutObject( authKey, cacheObj, timeout );
309         jsonObjectFree(cacheObj);
310         osrfLogInternal(OSRF_LOG_MARK, "oilsAuthHandleLoginOK(): Placed user object into cache");
311         jsonObject* payload = jsonParseFmt(
312                 "{ \"authtoken\": \"%s\", \"authtime\": %f }", authToken, timeout );
313
314         response = oilsNewEvent2( OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload );
315         free(string); free(authToken); free(authKey);
316         jsonObjectFree(payload);
317
318         return response;
319 }
320
321 static oilsEvent* oilsAuthVerifyWorkstation(
322                 const osrfMethodContext* ctx, jsonObject* userObj, const char* ws ) {
323         osrfLogInfo(OSRF_LOG_MARK, "Attaching workstation to user at login: %s", ws);
324         jsonObject* workstation = oilsUtilsFetchWorkstationByName(ws);
325         if(!workstation || workstation->type == JSON_NULL) {
326                 jsonObjectFree(workstation);
327                 return oilsNewEvent(OSRF_LOG_MARK, "WORKSTATION_NOT_FOUND");
328         }
329         long wsid = oilsFMGetObjectId(workstation);
330         LONG_TO_STRING(wsid);
331         char* orgid = oilsFMGetString(workstation, "owning_lib");
332         oilsFMSetString(userObj, "wsid", LONGSTR);
333         oilsFMSetString(userObj, "ws_ou", orgid);
334         free(orgid);
335         jsonObjectFree(workstation);
336         return NULL;
337 }
338
339
340
341 /* see if the card used to login is marked as barred */
342 static oilsEvent* oilsAuthCheckCard( const char* barcode ) {
343         if(!barcode) return NULL;
344         osrfLogDebug(OSRF_LOG_MARK, "Checking to see if barcode %s is active", barcode);
345
346         jsonObject* params = jsonParseFmt("{\"barcode\":\"%s\"}", barcode);
347         jsonObject* card = oilsUtilsQuickReq(
348                 "open-ils.cstore", "open-ils.cstore.direct.actor.card.search", params );
349         jsonObjectFree(params);
350
351         char* active = oilsFMGetString(card, "active");
352         jsonObjectFree(card);
353
354         oilsEvent* return_event = NULL;
355         if( ! oilsUtilsIsDBTrue(active) ) {
356                 osrfLogInfo(OSRF_LOG_MARK, "barcode %s is not active, returning event", barcode);
357                 return_event = oilsNewEvent(OSRF_LOG_MARK, "PATRON_CARD_INACTIVE");
358         }
359
360         free(active);
361         return return_event;
362 }
363
364
365
366 int oilsAuthComplete( osrfMethodContext* ctx ) {
367         OSRF_METHOD_VERIFY_CONTEXT(ctx);
368
369         const jsonObject* args  = jsonObjectGetIndex(ctx->params, 0);
370
371         const char* uname       = jsonObjectGetString(jsonObjectGetKeyConst(args, "username"));
372         const char* password    = jsonObjectGetString(jsonObjectGetKeyConst(args, "password"));
373         const char* type        = jsonObjectGetString(jsonObjectGetKeyConst(args, "type"));
374         double orgloc           = jsonObjectGetNumber(jsonObjectGetKeyConst(args, "org"));
375         const char* workstation = jsonObjectGetString(jsonObjectGetKeyConst(args, "workstation"));
376         const char* barcode     = jsonObjectGetString(jsonObjectGetKeyConst(args, "barcode"));
377
378         const char* ws = (workstation) ? workstation : "";
379
380
381         if(!type) type = OILS_AUTH_STAFF;
382
383         if( !( (uname || barcode) && password) ) {
384                 return osrfAppRequestRespondException( ctx->session, ctx->request,
385                         "username/barcode and password required for method: %s", ctx->method->name );
386         }
387
388         oilsEvent* response = NULL;
389         jsonObject* userObj = NULL;
390
391         if(uname) {
392                 userObj = oilsUtilsFetchUserByUsername( uname );
393                 if( userObj && JSON_NULL == userObj->type ) {
394                         jsonObjectFree( userObj );
395                         userObj = NULL;         // username not found
396                 }
397         }
398         else if(barcode)
399                  userObj = oilsUtilsFetchUserByBarcode( barcode );
400
401         if(!userObj) {
402                 response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_AUTH_FAILED );
403                 osrfLogInfo(OSRF_LOG_MARK,  "failed login: username=%s, barcode=%s, workstation=%s",
404                                 uname, (barcode ? barcode : "(none)"), ws );
405                 osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
406                 oilsEventFree(response);
407                 return 0;
408         }
409
410         /* first let's see if they have the right credentials */
411         int passOK = -1;
412         if(uname)
413                 passOK = oilsAuthVerifyPassword( ctx, userObj, uname, password );
414         else if (barcode)
415                 passOK = oilsAuthVerifyPassword( ctx, userObj, barcode, password );
416
417         if( passOK < 0 ) {
418                 jsonObjectFree(userObj);
419                 return passOK;
420         }
421
422         /* first see if their account is inactive */
423         char* active = oilsFMGetString(userObj, "active");
424         if( !oilsUtilsIsDBTrue(active) ) {
425                 if( passOK )
426                         response = oilsNewEvent( OSRF_LOG_MARK, "PATRON_INACTIVE" );
427                 else
428                         response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_AUTH_FAILED );
429
430                 osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
431                 oilsEventFree(response);
432                 jsonObjectFree(userObj);
433                 free(active);
434                 return 0;
435         }
436         free(active);
437
438         /* then see if the barcode they used is active */
439         if( barcode && ctx && userObj && (response = oilsAuthCheckCard( barcode )) ) {
440                 osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
441                 oilsEventFree(response);
442                 jsonObjectFree(userObj);
443                 return 0;
444         }
445
446
447         /* check to see if the user is even allowed to login */
448         if( oilsAuthCheckLoginPerm( ctx, userObj, type ) == -1 ) {
449                 jsonObjectFree(userObj);
450                 return 0;
451         }
452
453
454         /* if a workstation is defined, flesh the user with the workstation info */
455         if( workstation != NULL ) {
456                 osrfLogDebug(OSRF_LOG_MARK, "Workstation is %s", workstation);
457                 response = oilsAuthVerifyWorkstation( ctx, userObj, workstation );
458                 if(response) {
459                         jsonObjectFree(userObj);
460                         osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
461                         oilsEventFree(response);
462                         return 0;
463                 }
464
465         } else {
466                 /* otherwise, use the home org as the workstation org on the user */
467                 char* orgid = oilsFMGetString(userObj, "home_ou");
468                 oilsFMSetString(userObj, "ws_ou", orgid);
469                 free(orgid);
470         }
471
472         char* freeable_uname = NULL;
473         if(!uname) {
474                 uname = freeable_uname = oilsFMGetString( userObj, "usrname" );
475         }
476
477         if( passOK ) {
478                 response = oilsAuthHandleLoginOK( userObj, uname, type, orgloc, workstation );
479
480         } else {
481                 response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_AUTH_FAILED );
482                 osrfLogInfo(OSRF_LOG_MARK,  "failed login: username=%s, barcode=%s, workstation=%s",
483                                 uname, (barcode ? barcode : "(none)"), ws );
484         }
485
486         jsonObjectFree(userObj);
487         osrfAppRespondComplete( ctx, oilsEventToJSON(response) );
488         oilsEventFree(response);
489
490         if(freeable_uname)
491                 free(freeable_uname);
492
493         return 0;
494 }
495
496
497
498 int oilsAuthSessionDelete( osrfMethodContext* ctx ) {
499         OSRF_METHOD_VERIFY_CONTEXT(ctx);
500
501         const char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
502         jsonObject* resp = NULL;
503
504         if( authToken ) {
505                 osrfLogDebug(OSRF_LOG_MARK, "Removing auth session: %s", authToken );
506                 char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken ); /**/
507                 osrfCacheRemove(key);
508                 resp = jsonNewObject(authToken); /**/
509                 free(key);
510         }
511
512         osrfAppRespondComplete( ctx, resp );
513         jsonObjectFree(resp);
514         return 0;
515 }
516
517 /**
518         Resets the auth login timeout
519         @return The event object, OILS_EVENT_SUCCESS, or OILS_EVENT_NO_SESSION
520 */
521 static oilsEvent*  _oilsAuthResetTimeout( const char* authToken ) {
522         if(!authToken) return NULL;
523
524         oilsEvent* evt = NULL;
525         double timeout;
526
527         osrfLogDebug(OSRF_LOG_MARK, "Resetting auth timeout for session %s", authToken);
528         char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken );
529         jsonObject* cacheObj = osrfCacheGetObject( key );
530
531         if(!cacheObj) {
532                 osrfLogInfo(OSRF_LOG_MARK, "No user in the cache exists with key %s", key);
533                 evt = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_NO_SESSION);
534
535         } else {
536
537                 timeout = jsonObjectGetNumber( jsonObjectGetKeyConst( cacheObj, "authtime"));
538                 osrfCacheSetExpire( timeout, key );
539                 jsonObject* payload = jsonNewNumberObject(timeout);
540                 evt = oilsNewEvent2(OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload);
541                 jsonObjectFree(payload);
542                 jsonObjectFree(cacheObj);
543         }
544
545         free(key);
546         return evt;
547 }
548
549 int oilsAuthResetTimeout( osrfMethodContext* ctx ) {
550         OSRF_METHOD_VERIFY_CONTEXT(ctx);
551         const char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
552         oilsEvent* evt = _oilsAuthResetTimeout(authToken);
553         osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
554         oilsEventFree(evt);
555         return 0;
556 }
557
558
559 int oilsAuthSessionRetrieve( osrfMethodContext* ctx ) {
560         OSRF_METHOD_VERIFY_CONTEXT(ctx);
561
562         const char* authToken = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0));
563         jsonObject* cacheObj = NULL;
564         oilsEvent* evt = NULL;
565
566         if( authToken ){
567
568                 evt = _oilsAuthResetTimeout(authToken);
569
570                 if( evt && strcmp(evt->event, OILS_EVENT_SUCCESS) ) {
571                         osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
572
573                 } else {
574
575                         osrfLogDebug(OSRF_LOG_MARK, "Retrieving auth session: %s", authToken);
576                         char* key = va_list_to_string("%s%s", OILS_AUTH_CACHE_PRFX, authToken );
577                         cacheObj = osrfCacheGetObject( key );
578                         if(cacheObj) {
579                                 osrfAppRespondComplete( ctx, jsonObjectGetKeyConst( cacheObj, "userobj"));
580                                 jsonObjectFree(cacheObj);
581                         } else {
582                                 oilsEvent* evt2 = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_NO_SESSION);
583                                 osrfAppRespondComplete( ctx, oilsEventToJSON(evt2) ); /* should be event.. */
584                                 oilsEventFree(evt2);
585                         }
586                         free(key);
587                 }
588
589         } else {
590
591                 evt = oilsNewEvent(OSRF_LOG_MARK, OILS_EVENT_NO_SESSION);
592                 osrfAppRespondComplete( ctx, oilsEventToJSON(evt) );
593         }
594
595         if(evt)
596                 oilsEventFree(evt);
597
598         return 0;
599 }