unsigned refcount;
int timeout;
time_t expiration; // expiration time of the token
- time_t access;
pthread_mutex_t mutex;
char uuid[37]; // long term authentication of remote client
char token[37]; // short term authentication of remote client
// Session UUID are store in a simple array [for 10 sessions this should be enough]
static struct {
pthread_mutex_t mutex; // declare a mutex to protect hash table
- struct afb_session **store; // sessions store
+ struct afb_session **store; // sessions store
int count; // current number of sessions
int max;
int timeout;
char initok[37];
} sessions;
-/**
- * Get the index of the 'key' in the cookies array.
- * @param key the key to scan
- * @return the index of the list for key within cookies
- */
-static int cookeyidx(const void *key)
-{
- intptr_t x = (intptr_t)key;
- unsigned r = (unsigned)((x >> 5) ^ (x >> 15));
- return r & COOKEYMASK;
-}
-
/* generate a uuid */
static void new_uuid(char uuid[37])
{
}
// Free context [XXXX Should be protected again memory abort XXXX]
-static void free_data (struct afb_session *session)
+static void remove_all_cookies(struct afb_session *session)
{
int idx;
struct cookie *cookie, *next;
}
}
+const char *afb_session_initial_token()
+{
+ return sessions.initok;
+}
+
static struct afb_session *search (const char* uuid)
{
int idx;
{
struct afb_session *session;
+ if (!AFB_SESSION_TIMEOUT_IS_VALID(timeout)
+ || (uuid && strlen(uuid) >= sizeof session->uuid)) {
+ errno = EINVAL;
+ goto error;
+ }
+
/* allocates a new one */
session = calloc(1, sizeof *session);
if (session == NULL) {
/* generate the uuid */
if (uuid == NULL) {
- new_uuid(session->uuid);
+ do { new_uuid(session->uuid); } while(search(session->uuid));
} else {
- if (strlen(uuid) >= sizeof session->uuid) {
- errno = EINVAL;
- goto error2;
- }
strcpy(session->uuid, uuid);
}
/* init the token */
strcpy(session->token, sessions.initok);
+
+ /* init timeout */
+ if (timeout == AFB_SESSION_TIMEOUT_DEFAULT)
+ timeout = sessions.timeout;
session->timeout = timeout;
- if (timeout != 0)
- session->expiration = now + timeout;
- else {
+ session->expiration = now + timeout;
+ if (timeout == AFB_SESSION_TIMEOUT_INFINITE || session->expiration < 0) {
session->expiration = (time_t)(~(time_t)0);
if (session->expiration < 0)
session->expiration = (time_t)(((unsigned long long)session->expiration) >> 1);
goto error2;
}
- session->access = now;
session->refcount = 1;
return session;
return NULL;
}
-struct afb_session *afb_session_create (const char *uuid, int timeout)
+/* Creates a new session with 'timeout' */
+struct afb_session *afb_session_create (int timeout)
{
time_t now;
now = NOW;
cleanup (now);
- /* search for an existing one not too old */
- if (uuid != NULL && search(uuid) != NULL) {
- errno = EEXIST;
- return NULL;
- }
+ return make_session(NULL, timeout, now);
+}
+
+/* Searchs the session of 'uuid' */
+struct afb_session *afb_session_search (const char *uuid)
+{
+ time_t now;
+ struct afb_session *session;
+
+ /* cleaning */
+ now = NOW;
+ cleanup (now);
+ session = search(uuid);
+ return session;
- return make_session(uuid, timeout, now);
}
-// This function will return exiting session or newly created session
-struct afb_session *afb_session_get (const char *uuid, int *created)
+/* This function will return exiting session or newly created session */
+struct afb_session *afb_session_get (const char *uuid, int timeout, int *created)
{
struct afb_session *session;
time_t now;
/* search for an existing one not too old */
if (uuid != NULL) {
session = search(uuid);
- if (!created)
- return session;
if (session != NULL) {
- *created = 0;
- session->access = now;
+ if (created)
+ *created = 0;
session->refcount++;
return session;
}
}
+ /* no existing session found, create it */
+ session = make_session(uuid, timeout, now);
if (created)
- *created = 1;
+ *created = !!session;
- return make_session(uuid, sessions.timeout, now);
+ return session;
}
struct afb_session *afb_session_addref(struct afb_session *session)
assert(session != NULL);
if (session->uuid[0] != 0) {
session->uuid[0] = 0;
- free_data (session);
+ remove_all_cookies(session);
if (session->refcount == 0) {
destroy (session);
free(session);
session->expiration = NOW + session->timeout;
}
+/* Returns the uuid of 'session' */
const char *afb_session_uuid (struct afb_session *session)
{
assert(session != NULL);
return session->uuid;
}
+/* Returns the token of 'session' */
const char *afb_session_token (struct afb_session *session)
{
assert(session != NULL);
return session->token;
}
-static struct cookie *cookie_search(struct afb_session *session, const void *key, int *idx)
-{
- struct cookie *cookie;
-
- cookie = session->cookies[*idx = cookeyidx(key)];
- while(cookie != NULL && cookie->key != key)
- cookie = cookie->next;
- return cookie;
-}
-
-static struct cookie *cookie_add(struct afb_session *session, int idx, const void *key, void *value, void (*freecb)(void*))
+/**
+ * Get the index of the 'key' in the cookies array.
+ * @param key the key to scan
+ * @return the index of the list for key within cookies
+ */
+static int cookeyidx(const void *key)
{
- struct cookie *cookie;
-
- cookie = malloc(sizeof *cookie);
- if (!cookie)
- errno = ENOMEM;
- else {
- cookie->key = key;
- cookie->value = value;
- cookie->freecb = freecb;
- cookie->next = session->cookies[idx];
- session->cookies[idx] = cookie;
- }
- return cookie;
+ intptr_t x = (intptr_t)key;
+ unsigned r = (unsigned)((x >> 5) ^ (x >> 15));
+ return r & COOKEYMASK;
}
-void *afb_session_cookie(struct afb_session *session, const void *key, void *(*makecb)(void), void (*freecb)(void*))
+/**
+ * Set, get, replace, remove a cookie of 'key' for the 'session'
+ *
+ * The behaviour of this function depends on its parameters:
+ *
+ * @param session the session
+ * @param key the key of the cookie
+ * @param makecb the creation function
+ * @param freecb the release function
+ * @param closure an argument
+ * @param replace a boolean enforcing replecement of the previous value
+ *
+ * @return the value of the cookie
+ *
+ * The 'key' is a pointer and compared as pointers.
+ *
+ * For getting the current value of the cookie:
+ *
+ * afb_session_cookie(session, key, NULL, NULL, NULL, 0)
+ *
+ * For storing the value of the cookie
+ *
+ * afb_session_cookie(session, key, NULL, NULL, value, 1)
+ */
+void *afb_session_cookie(struct afb_session *session, const void *key, void *(*makecb)(void *closure), void (*freecb)(void *item), void *closure, int replace)
{
int idx;
void *value;
- struct cookie *cookie;
+ struct cookie *cookie, **prv;
+ /* get key hashed index */
+ idx = cookeyidx(key);
+
+ /* lock session and search for the cookie of 'key' */
lock(session);
- cookie = cookie_search(session, key, &idx);
- if (cookie)
- value = cookie->value;
- else {
- value = makecb ? makecb() : NULL;
- if (makecb || freecb) {
- cookie = cookie_add(session, idx, key, value, freecb);
- if (!cookie) {
- if (makecb && freecb)
- free(value);
- value = NULL;
+ prv = &session->cookies[idx];
+ for (;;) {
+ cookie = *prv;
+ if (!cookie) {
+ /* 'key' not found, create value using 'closure' and 'makecb' */
+ value = makecb ? makecb(closure) : closure;
+ /* store the the only if it has some meaning */
+ if (replace || makecb || freecb) {
+ cookie = malloc(sizeof *cookie);
+ if (!cookie) {
+ errno = ENOMEM;
+ /* calling freecb if there is no makecb may have issue */
+ if (makecb && freecb)
+ freecb(value);
+ value = NULL;
+ } else {
+ cookie->key = key;
+ cookie->value = value;
+ cookie->freecb = freecb;
+ cookie->next = NULL;
+ *prv = cookie;
+ }
}
+ break;
+ } else if (cookie->key == key) {
+ /* cookie of key found */
+ if (!replace)
+ /* not replacing, get the value */
+ value = cookie->value;
+ else {
+ /* create value using 'closure' and 'makecb' */
+ value = makecb ? makecb(closure) : closure;
+
+ /* free previous value is needed */
+ if (cookie->value != value && cookie->freecb)
+ cookie->freecb(cookie->value);
+
+ /* store the value and its releaser */
+ cookie->value = value;
+ cookie->freecb = freecb;
+
+ /* but if both are NULL drop the cookie */
+ if (!value && !freecb) {
+ *prv = cookie->next;
+ free(cookie);
+ }
+ }
+ break;
+ } else {
+ prv = &(cookie->next);
}
}
+
+ /* unlock the session and return the value */
unlock(session);
return value;
}
void *afb_session_get_cookie(struct afb_session *session, const void *key)
{
- int idx;
- void *value;
- struct cookie *cookie;
-
- lock(session);
- cookie = cookie_search(session, key, &idx);
- value = cookie ? cookie->value : NULL;
- unlock(session);
- return value;
+ return afb_session_cookie(session, key, NULL, NULL, NULL, 0);
}
int afb_session_set_cookie(struct afb_session *session, const void *key, void *value, void (*freecb)(void*))
{
- int idx;
- struct cookie *cookie;
-
- lock(session);
- cookie = cookie_search(session, key, &idx);
- if (!cookie)
- cookie = cookie_add(session, idx, key, value, freecb);
- else {
- if (cookie->value != value && cookie->freecb)
- cookie->freecb(cookie->value);
- cookie->value = value;
- cookie->freecb = freecb;
- }
- unlock(session);
- return -!cookie;
+ return -(value != afb_session_cookie(session, key, NULL, freecb, value, 1));
}