Removes uses of readdir_r
[src/app-framework-binder.git] / src / session.c
index 22e50cf..7e1e4c6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 "IoT.bzh"
+ * Copyright (C) 2015, 2016, 2017 "IoT.bzh"
  * Author "Fulup Ar Foll"
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -38,22 +38,25 @@ struct client_value
        void (*free_value)(void*);
 };
 
-struct afb_event_listener_list
+struct cookie
 {
-       struct afb_event_listener_list *next;
-       struct afb_event_listener listener;
-       int refcount;
+       struct cookie *next;
+       const void *key;
+       void *value;
+       void (*free_value)(void*);
 };
 
 struct AFB_clientCtx
 {
        unsigned refcount;
+       unsigned loa;
+       int timeout;
        time_t expiration;    // expiration time of the token
        time_t access;
        char uuid[37];        // long term authentication of remote client
        char token[37];       // short term authentication of remote client
        struct client_value *values;
-       struct afb_event_listener_list *listeners;
+       struct cookie *cookies;
 };
 
 // Session UUID are store in a simple array [for 10 sessions this should be enough]
@@ -65,7 +68,6 @@ static struct {
   int timeout;
   int apicount;
   char initok[37];
-  struct afb_event_listener_list *listeners;
 } sessions;
 
 /* generate a uuid */
@@ -80,6 +82,7 @@ static void new_uuid(char uuid[37])
 static void ctxUuidFreeCB (struct AFB_clientCtx *client)
 {
        int idx;
+       struct cookie *cookie;
 
        // If application add a handle let's free it now
        assert (client->values != NULL);
@@ -87,6 +90,16 @@ static void ctxUuidFreeCB (struct AFB_clientCtx *client)
        // Free client handle with a standard Free function, with app callback or ignore it
        for (idx=0; idx < sessions.apicount; idx ++)
                ctxClientValueSet(client, idx, NULL, NULL);
+
+       // free cookies
+       cookie = client->cookies;
+       while (cookie != NULL) {
+               client->cookies = cookie->next;
+               if (cookie->value != NULL && cookie->free_value != NULL)
+                       cookie->free_value(cookie->value);
+               free(cookie);
+               cookie = client->cookies;
+       }
 }
 
 // Create a new store in RAM, not that is too small it will be automatically extended
@@ -197,38 +210,18 @@ static void ctxStoreCleanUp (time_t now)
 
        // Loop on Sessions Table and remove anything that is older than timeout
        for (idx=0; idx < sessions.max; idx++) {
-               ctx = ctxClientAddRef(sessions.store[idx]);
+               ctx = sessions.store[idx];
                if (ctx != NULL && ctxStoreTooOld(ctx, now)) {
                        ctxClientClose (ctx);
                }
-               ctxClientUnref(ctx);
        }
 }
 
-// This function will return exiting client context or newly created client context
-struct AFB_clientCtx *ctxClientGetSession (const char *uuid, int *created)
+static struct AFB_clientCtx *new_context (const char *uuid, int timeout, time_t now)
 {
        struct AFB_clientCtx *clientCtx;
-       time_t now;
-
-       /* cleaning */
-       now = NOW;
-       ctxStoreCleanUp (now);
 
-       /* search for an existing one not too old */
-       if (uuid != NULL) {
-               if (strlen(uuid) >= sizeof clientCtx->uuid) {
-                       errno = EINVAL;
-                       goto error;
-               }
-               clientCtx = ctxStoreSearch(uuid);
-               if (clientCtx != NULL) {
-                       *created = 0;
-                       goto found;
-               }
-       }
-
-       /* returns a new one */
+       /* allocates a new one */
         clientCtx = calloc(1, sizeof(struct AFB_clientCtx) + ((unsigned)sessions.apicount * sizeof(*clientCtx->values)));
        if (clientCtx == NULL) {
                errno = ENOMEM;
@@ -240,21 +233,30 @@ struct AFB_clientCtx *ctxClientGetSession (const char *uuid, int *created)
        if (uuid == NULL) {
                new_uuid(clientCtx->uuid);
        } else {
+               if (strlen(uuid) >= sizeof clientCtx->uuid) {
+                       errno = EINVAL;
+                       goto error2;
+               }
                strcpy(clientCtx->uuid, uuid);
        }
 
        /* init the token */
        strcpy(clientCtx->token, sessions.initok);
-       clientCtx->expiration = now + sessions.timeout;
+       clientCtx->timeout = timeout;
+       if (timeout != 0)
+               clientCtx->expiration = now + timeout;
+       else {
+               clientCtx->expiration = (time_t)(~(time_t)0);
+               if (clientCtx->expiration < 0)
+                       clientCtx->expiration = (time_t)(((unsigned long long)clientCtx->expiration) >> 1);
+       }
        if (!ctxStoreAdd (clientCtx)) {
                errno = ENOMEM;
                goto error2;
        }
-       *created = 1;
 
-found:
        clientCtx->access = now;
-       clientCtx->refcount++;
+       clientCtx->refcount = 1;
        return clientCtx;
 
 error2:
@@ -263,6 +265,48 @@ error:
        return NULL;
 }
 
+struct AFB_clientCtx *ctxClientCreate (const char *uuid, int timeout)
+{
+       time_t now;
+
+       /* cleaning */
+       now = NOW;
+       ctxStoreCleanUp (now);
+
+       /* search for an existing one not too old */
+       if (uuid != NULL && ctxStoreSearch(uuid) != NULL) {
+               errno = EEXIST;
+               return NULL;
+       }
+
+       return new_context(uuid, timeout, now);
+}
+
+// This function will return exiting client context or newly created client context
+struct AFB_clientCtx *ctxClientGetSession (const char *uuid, int *created)
+{
+       struct AFB_clientCtx *clientCtx;
+       time_t now;
+
+       /* cleaning */
+       now = NOW;
+       ctxStoreCleanUp (now);
+
+       /* search for an existing one not too old */
+       if (uuid != NULL) {
+               clientCtx = ctxStoreSearch(uuid);
+               if (clientCtx != NULL) {
+                       *created = 0;
+                       clientCtx->access = now;
+                       clientCtx->refcount++;
+                       return clientCtx;
+               }
+       }
+
+       *created = 1;
+       return new_context(uuid, sessions.timeout, now);
+}
+
 struct AFB_clientCtx *ctxClientAddRef(struct AFB_clientCtx *clientCtx)
 {
        if (clientCtx != NULL)
@@ -289,8 +333,10 @@ void ctxClientClose (struct AFB_clientCtx *clientCtx)
        if (clientCtx->uuid[0] != 0) {
                clientCtx->uuid[0] = 0;
                ctxUuidFreeCB (clientCtx);
-               while(clientCtx->listeners != NULL)
-                       ctxClientEventListenerRemove(clientCtx, clientCtx->listeners->listener);
+                       if (clientCtx->refcount == 0) {
+                       ctxStoreDel (clientCtx);
+                       free(clientCtx);
+               }
        }
 }
 
@@ -319,116 +365,32 @@ void ctxTokenNew (struct AFB_clientCtx *clientCtx)
        new_uuid(clientCtx->token);
 
        // keep track of time for session timeout and further clean up
-       clientCtx->expiration = NOW + sessions.timeout;
-}
-
-static int add_listener(struct afb_event_listener_list **head, struct afb_event_listener listener)
-{
-       struct afb_event_listener_list *iter, **prv;
-
-       prv = head;
-       for (;;) {
-               iter = *prv;
-               if (iter == NULL) {
-                       iter = calloc(1, sizeof *iter);
-                       if (iter == NULL) {
-                               errno = ENOMEM;
-                               return -1;
-                       }
-                       iter->listener = listener;
-                       iter->refcount = 1;
-                       *prv = iter;
-                       return 0;
-               }
-               if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
-                       iter->refcount++;
-                       return 0;
-               }
-               prv = &iter->next;
-       }
+       if (clientCtx->timeout != 0)
+               clientCtx->expiration = NOW + clientCtx->timeout;
 }
 
-int ctxClientEventListenerAdd(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
-{
-       return add_listener(clientCtx != NULL ? &clientCtx->listeners : &sessions.listeners, listener);
-}
-
-static void remove_listener(struct afb_event_listener_list **head, struct afb_event_listener listener)
-{
-       struct afb_event_listener_list *iter, **prv;
-
-       prv = head;
-       for (;;) {
-               iter = *prv;
-               if (iter == NULL)
-                       return;
-               if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
-                       if (!--iter->refcount) {
-                               *prv = iter->next;
-                               free(iter);
-                       }
-                       return;
-               }
-               prv = &iter->next;
-       }
-}
-
-void ctxClientEventListenerRemove(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
-{
-       remove_listener(clientCtx != NULL ? &clientCtx->listeners : &sessions.listeners, listener);
-}
-
-static int send(struct afb_event_listener_list *head, const char *event, struct json_object *object)
+const char *ctxClientGetUuid (struct AFB_clientCtx *clientCtx)
 {
-       struct afb_event_listener_list *iter;
-       int result;
-
-       result = 0;
-       iter = head;
-       while (iter != NULL) {
-               if (iter->listener.itf->expects == NULL || iter->listener.itf->expects(iter->listener.closure, event)) {
-                       iter->listener.itf->send(iter->listener.closure, event, json_object_get(object));
-                       result++;
-               }
-               iter = iter->next;
-       }
-
-       return result;
+       assert(clientCtx != NULL);
+       return clientCtx->uuid;
 }
 
-int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
+const char *ctxClientGetToken (struct AFB_clientCtx *clientCtx)
 {
-       long idx;
-       time_t now;
-       int result;
-
-       now = NOW;
-       if (clientCtx != NULL) {
-               result = ctxIsActive(clientCtx, now) ? send(clientCtx->listeners, event, object) : 0;
-       } else {
-               result = send(sessions.listeners, event, object);
-               for (idx=0; idx < sessions.max; idx++) {
-                       clientCtx = ctxClientAddRef(sessions.store[idx]);
-                       if (clientCtx != NULL && ctxIsActive(clientCtx, now)) {
-                               clientCtx = ctxClientAddRef(clientCtx);
-                               result += send(clientCtx->listeners, event, object);
-                       }
-                       ctxClientUnref(clientCtx);
-               }
-       }
-       return result;
+       assert(clientCtx != NULL);
+       return clientCtx->token;
 }
 
-const char *ctxClientGetUuid (struct AFB_clientCtx *clientCtx)
+unsigned ctxClientGetLOA (struct AFB_clientCtx *clientCtx)
 {
        assert(clientCtx != NULL);
-       return clientCtx->uuid;
+       return clientCtx->loa;
 }
 
-const char *ctxClientGetToken (struct AFB_clientCtx *clientCtx)
+void ctxClientSetLOA (struct AFB_clientCtx *clientCtx, unsigned loa)
 {
        assert(clientCtx != NULL);
-       return clientCtx->token;
+       clientCtx->loa = loa;
 }
 
 void *ctxClientValueGet(struct AFB_clientCtx *clientCtx, int index)
@@ -447,6 +409,52 @@ void ctxClientValueSet(struct AFB_clientCtx *clientCtx, int index, void *value,
        assert(index < sessions.apicount);
        prev = clientCtx->values[index];
        clientCtx->values[index] = (struct client_value){.value = value, .free_value = free_value};
-       if (prev.value !=  NULL && prev.free_value != NULL)
+       if (prev.value != NULL && prev.value != value && prev.free_value != NULL)
                prev.free_value(prev.value);
 }
+
+void *ctxClientCookieGet(struct AFB_clientCtx *clientCtx, const void *key)
+{
+       struct cookie *cookie;
+
+       cookie = clientCtx->cookies;
+       while(cookie != NULL) {
+               if (cookie->key == key)
+                       return cookie->value;
+               cookie = cookie->next;
+       }
+       return NULL;
+}
+
+int ctxClientCookieSet(struct AFB_clientCtx *clientCtx, const void *key, void *value, void (*free_value)(void*))
+{
+       struct cookie *cookie;
+
+       /* search for a replacement */
+       cookie = clientCtx->cookies;
+       while(cookie != NULL) {
+               if (cookie->key == key) {
+                       if (cookie->value != NULL && cookie->value != value && cookie->free_value != NULL)
+                               cookie->free_value(cookie->value);
+                       cookie->value = value;
+                       cookie->free_value = free_value;
+                       return 0;
+               }
+               cookie = cookie->next;
+       }
+
+       /* allocates */
+       cookie = malloc(sizeof *cookie);
+       if (cookie == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       cookie->key = key;
+       cookie->value = value;
+       cookie->free_value = free_value;
+       cookie->next = clientCtx->cookies;
+       clientCtx->cookies = cookie;
+       return 0;
+}
+