2 * Copyright (C) 2015, 2016, 2017 "IoT.bzh"
3 * Author "Fulup Ar Foll"
4 * Author: José Bollo <jose.bollo@iot.bzh>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
25 #include <uuid/uuid.h>
29 #include <json-c/json.h>
31 #include "afb-session.h"
34 #define NOW (time(NULL))
39 void (*freecb)(void*);
47 void (*freecb)(void*);
55 time_t expiration; // expiration time of the token
57 char uuid[37]; // long term authentication of remote client
58 char token[37]; // short term authentication of remote client
60 struct cookie *cookies;
63 // Session UUID are store in a simple array [for 10 sessions this should be enough]
65 pthread_mutex_t mutex; // declare a mutex to protect hash table
66 struct afb_session **store; // sessions store
67 int count; // current number of sessions
75 static void new_uuid(char uuid[37])
78 uuid_generate(newuuid);
79 uuid_unparse_lower(newuuid, uuid);
82 // Free context [XXXX Should be protected again memory abort XXXX]
83 static void free_data (struct afb_session *session)
86 struct cookie *cookie;
88 // If application add a handle let's free it now
89 assert (session->values != NULL);
91 // Free session handle with a standard Free function, with app callback or ignore it
92 for (idx=0; idx < sessions.apicount; idx ++)
93 afb_session_set_value(session, idx, NULL, NULL);
96 cookie = session->cookies;
97 while (cookie != NULL) {
98 session->cookies = cookie->next;
99 if (cookie->value != NULL && cookie->freecb != NULL)
100 cookie->freecb(cookie->value);
102 cookie = session->cookies;
106 // Create a new store in RAM, not that is too small it will be automatically extended
107 void afb_session_init (int max_session_count, int timeout, const char *initok, int context_count)
109 // let's create as store as hashtable does not have any
110 sessions.store = calloc (1 + (unsigned)max_session_count, sizeof(struct afb_session));
111 sessions.max = max_session_count;
112 sessions.timeout = timeout;
113 sessions.apicount = context_count;
115 /* without token, a secret is made to forbid creation of sessions */
116 new_uuid(sessions.initok);
117 else if (strlen(initok) < sizeof(sessions.store[0]->token))
118 strcpy(sessions.initok, initok);
120 ERROR("initial token '%s' too long (max length 36)", initok);
125 static struct afb_session *search (const char* uuid)
128 struct afb_session *session;
130 assert (uuid != NULL);
132 pthread_mutex_lock(&sessions.mutex);
134 for (idx=0; idx < sessions.max; idx++) {
135 session = sessions.store[idx];
136 if (session && (0 == strcmp (uuid, session->uuid)))
142 pthread_mutex_unlock(&sessions.mutex);
146 static int destroy (struct afb_session *session)
151 assert (session != NULL);
153 pthread_mutex_lock(&sessions.mutex);
155 for (idx=0; idx < sessions.max; idx++) {
156 if (sessions.store[idx] == session) {
157 sessions.store[idx] = NULL;
165 pthread_mutex_unlock(&sessions.mutex);
169 static int add (struct afb_session *session)
174 assert (session != NULL);
176 pthread_mutex_lock(&sessions.mutex);
178 for (idx=0; idx < sessions.max; idx++) {
179 if (NULL == sessions.store[idx]) {
180 sessions.store[idx] = session;
188 pthread_mutex_unlock(&sessions.mutex);
192 // Check if context timeout or not
193 static int is_expired (struct afb_session *ctx, time_t now)
195 assert (ctx != NULL);
196 return ctx->expiration < now;
199 // Check if context is active or not
200 static int is_active (struct afb_session *ctx, time_t now)
202 assert (ctx != NULL);
203 return ctx->uuid[0] != 0 && ctx->expiration >= now;
206 // Loop on every entry and remove old context sessions.hash
207 static void cleanup (time_t now)
209 struct afb_session *ctx;
212 // Loop on Sessions Table and remove anything that is older than timeout
213 for (idx=0; idx < sessions.max; idx++) {
214 ctx = sessions.store[idx];
215 if (ctx != NULL && is_expired(ctx, now)) {
216 afb_session_close (ctx);
221 static struct afb_session *make_session (const char *uuid, int timeout, time_t now)
223 struct afb_session *session;
225 /* allocates a new one */
226 session = calloc(1, sizeof(struct afb_session) + ((unsigned)sessions.apicount * sizeof(*session->values)));
227 if (session == NULL) {
231 session->values = (void*)(session + 1);
233 /* generate the uuid */
235 new_uuid(session->uuid);
237 if (strlen(uuid) >= sizeof session->uuid) {
241 strcpy(session->uuid, uuid);
245 strcpy(session->token, sessions.initok);
246 session->timeout = timeout;
248 session->expiration = now + timeout;
250 session->expiration = (time_t)(~(time_t)0);
251 if (session->expiration < 0)
252 session->expiration = (time_t)(((unsigned long long)session->expiration) >> 1);
254 if (!add (session)) {
259 session->access = now;
260 session->refcount = 1;
269 struct afb_session *afb_session_create (const char *uuid, int timeout)
277 /* search for an existing one not too old */
278 if (uuid != NULL && search(uuid) != NULL) {
283 return make_session(uuid, timeout, now);
286 // This function will return exiting session or newly created session
287 struct afb_session *afb_session_get (const char *uuid, int *created)
289 struct afb_session *session;
296 /* search for an existing one not too old */
298 session = search(uuid);
299 if (session != NULL) {
301 session->access = now;
308 return make_session(uuid, sessions.timeout, now);
311 struct afb_session *afb_session_addref(struct afb_session *session)
318 void afb_session_unref(struct afb_session *session)
320 if (session != NULL) {
321 assert(session->refcount != 0);
323 if (session->refcount == 0 && session->uuid[0] == 0) {
330 // Free Client Session Context
331 void afb_session_close (struct afb_session *session)
333 assert(session != NULL);
334 if (session->uuid[0] != 0) {
335 session->uuid[0] = 0;
337 if (session->refcount == 0) {
344 // Sample Generic Ping Debug API
345 int afb_session_check_token (struct afb_session *session, const char *token)
347 assert(session != NULL);
348 assert(token != NULL);
350 // compare current token with previous one
351 if (!is_active (session, NOW))
354 if (session->token[0] && strcmp (token, session->token) != 0)
360 // generate a new token and update client context
361 void afb_session_new_token (struct afb_session *session)
363 assert(session != NULL);
365 // Old token was valid let's regenerate a new one
366 new_uuid(session->token);
368 // keep track of time for session timeout and further clean up
369 if (session->timeout != 0)
370 session->expiration = NOW + session->timeout;
373 const char *afb_session_uuid (struct afb_session *session)
375 assert(session != NULL);
376 return session->uuid;
379 const char *afb_session_token (struct afb_session *session)
381 assert(session != NULL);
382 return session->token;
385 unsigned afb_session_get_LOA (struct afb_session *session)
387 assert(session != NULL);
391 void afb_session_set_LOA (struct afb_session *session, unsigned loa)
393 assert(session != NULL);
397 void *afb_session_get_value(struct afb_session *session, int index)
399 assert(session != NULL);
401 assert(index < sessions.apicount);
402 return session->values[index].value;
405 void afb_session_set_value(struct afb_session *session, int index, void *value, void (*freecb)(void*))
408 assert(session != NULL);
410 assert(index < sessions.apicount);
411 prev = session->values[index];
412 session->values[index] = (struct value){.value = value, .freecb = freecb};
413 if (prev.value != NULL && prev.value != value && prev.freecb != NULL)
414 prev.freecb(prev.value);
417 void *afb_session_get_cookie(struct afb_session *session, const void *key)
419 struct cookie *cookie;
421 cookie = session->cookies;
422 while(cookie != NULL) {
423 if (cookie->key == key)
424 return cookie->value;
425 cookie = cookie->next;
430 int afb_session_set_cookie(struct afb_session *session, const void *key, void *value, void (*freecb)(void*))
432 struct cookie *cookie;
434 /* search for a replacement */
435 cookie = session->cookies;
436 while(cookie != NULL) {
437 if (cookie->key == key) {
438 if (cookie->value != NULL && cookie->value != value && cookie->freecb != NULL)
439 cookie->freecb(cookie->value);
440 cookie->value = value;
441 cookie->freecb = freecb;
444 cookie = cookie->next;
448 cookie = malloc(sizeof *cookie);
449 if (cookie == NULL) {
455 cookie->value = value;
456 cookie->freecb = freecb;
457 cookie->next = session->cookies;
458 session->cookies = cookie;