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))
41 void (*freecb)(void*);
49 time_t expiration; // expiration time of the token
51 char uuid[37]; // long term authentication of remote client
52 char token[37]; // short term authentication of remote client
53 struct cookie *cookies;
56 // Session UUID are store in a simple array [for 10 sessions this should be enough]
58 pthread_mutex_t mutex; // declare a mutex to protect hash table
59 struct afb_session **store; // sessions store
60 int count; // current number of sessions
67 static void new_uuid(char uuid[37])
70 uuid_generate(newuuid);
71 uuid_unparse_lower(newuuid, uuid);
74 // Free context [XXXX Should be protected again memory abort XXXX]
75 static void free_data (struct afb_session *session)
77 struct cookie *cookie;
80 cookie = session->cookies;
81 while (cookie != NULL) {
82 session->cookies = cookie->next;
83 if (cookie->value != NULL && cookie->freecb != NULL)
84 cookie->freecb(cookie->value);
86 cookie = session->cookies;
90 // Create a new store in RAM, not that is too small it will be automatically extended
91 void afb_session_init (int max_session_count, int timeout, const char *initok)
93 // let's create as store as hashtable does not have any
94 sessions.store = calloc (1 + (unsigned)max_session_count, sizeof(struct afb_session));
95 sessions.max = max_session_count;
96 sessions.timeout = timeout;
98 /* without token, a secret is made to forbid creation of sessions */
99 new_uuid(sessions.initok);
100 else if (strlen(initok) < sizeof(sessions.store[0]->token))
101 strcpy(sessions.initok, initok);
103 ERROR("initial token '%s' too long (max length 36)", initok);
108 static struct afb_session *search (const char* uuid)
111 struct afb_session *session;
113 assert (uuid != NULL);
115 pthread_mutex_lock(&sessions.mutex);
117 for (idx=0; idx < sessions.max; idx++) {
118 session = sessions.store[idx];
119 if (session && (0 == strcmp (uuid, session->uuid)))
125 pthread_mutex_unlock(&sessions.mutex);
129 static int destroy (struct afb_session *session)
134 assert (session != NULL);
136 pthread_mutex_lock(&sessions.mutex);
138 for (idx=0; idx < sessions.max; idx++) {
139 if (sessions.store[idx] == session) {
140 sessions.store[idx] = NULL;
148 pthread_mutex_unlock(&sessions.mutex);
152 static int add (struct afb_session *session)
157 assert (session != NULL);
159 pthread_mutex_lock(&sessions.mutex);
161 for (idx=0; idx < sessions.max; idx++) {
162 if (NULL == sessions.store[idx]) {
163 sessions.store[idx] = session;
171 pthread_mutex_unlock(&sessions.mutex);
175 // Check if context timeout or not
176 static int is_expired (struct afb_session *ctx, time_t now)
178 assert (ctx != NULL);
179 return ctx->expiration < now;
182 // Check if context is active or not
183 static int is_active (struct afb_session *ctx, time_t now)
185 assert (ctx != NULL);
186 return ctx->uuid[0] != 0 && ctx->expiration >= now;
189 // Loop on every entry and remove old context sessions.hash
190 static void cleanup (time_t now)
192 struct afb_session *ctx;
195 // Loop on Sessions Table and remove anything that is older than timeout
196 for (idx=0; idx < sessions.max; idx++) {
197 ctx = sessions.store[idx];
198 if (ctx != NULL && is_expired(ctx, now)) {
199 afb_session_close (ctx);
204 static struct afb_session *make_session (const char *uuid, int timeout, time_t now)
206 struct afb_session *session;
208 /* allocates a new one */
209 session = calloc(1, sizeof(struct afb_session));
210 if (session == NULL) {
215 /* generate the uuid */
217 new_uuid(session->uuid);
219 if (strlen(uuid) >= sizeof session->uuid) {
223 strcpy(session->uuid, uuid);
227 strcpy(session->token, sessions.initok);
228 session->timeout = timeout;
230 session->expiration = now + timeout;
232 session->expiration = (time_t)(~(time_t)0);
233 if (session->expiration < 0)
234 session->expiration = (time_t)(((unsigned long long)session->expiration) >> 1);
236 if (!add (session)) {
241 session->access = now;
242 session->refcount = 1;
251 struct afb_session *afb_session_create (const char *uuid, int timeout)
259 /* search for an existing one not too old */
260 if (uuid != NULL && search(uuid) != NULL) {
265 return make_session(uuid, timeout, now);
268 // This function will return exiting session or newly created session
269 struct afb_session *afb_session_get (const char *uuid, int *created)
271 struct afb_session *session;
278 /* search for an existing one not too old */
280 session = search(uuid);
281 if (session != NULL) {
283 session->access = now;
290 return make_session(uuid, sessions.timeout, now);
293 struct afb_session *afb_session_addref(struct afb_session *session)
300 void afb_session_unref(struct afb_session *session)
302 if (session != NULL) {
303 assert(session->refcount != 0);
305 if (session->refcount == 0 && session->uuid[0] == 0) {
312 // Free Client Session Context
313 void afb_session_close (struct afb_session *session)
315 assert(session != NULL);
316 if (session->uuid[0] != 0) {
317 session->uuid[0] = 0;
319 if (session->refcount == 0) {
326 // Sample Generic Ping Debug API
327 int afb_session_check_token (struct afb_session *session, const char *token)
329 assert(session != NULL);
330 assert(token != NULL);
332 // compare current token with previous one
333 if (!is_active (session, NOW))
336 if (session->token[0] && strcmp (token, session->token) != 0)
342 // generate a new token and update client context
343 void afb_session_new_token (struct afb_session *session)
345 assert(session != NULL);
347 // Old token was valid let's regenerate a new one
348 new_uuid(session->token);
350 // keep track of time for session timeout and further clean up
351 if (session->timeout != 0)
352 session->expiration = NOW + session->timeout;
355 const char *afb_session_uuid (struct afb_session *session)
357 assert(session != NULL);
358 return session->uuid;
361 const char *afb_session_token (struct afb_session *session)
363 assert(session != NULL);
364 return session->token;
367 unsigned afb_session_get_LOA (struct afb_session *session)
369 assert(session != NULL);
373 void afb_session_set_LOA (struct afb_session *session, unsigned loa)
375 assert(session != NULL);
379 void *afb_session_get_cookie(struct afb_session *session, const void *key)
381 struct cookie *cookie;
383 cookie = session->cookies;
384 while(cookie != NULL) {
385 if (cookie->key == key)
386 return cookie->value;
387 cookie = cookie->next;
392 int afb_session_set_cookie(struct afb_session *session, const void *key, void *value, void (*freecb)(void*))
394 struct cookie *cookie;
396 /* search for a replacement */
397 cookie = session->cookies;
398 while(cookie != NULL) {
399 if (cookie->key == key) {
400 if (cookie->value != NULL && cookie->value != value && cookie->freecb != NULL)
401 cookie->freecb(cookie->value);
402 cookie->value = value;
403 cookie->freecb = freecb;
406 cookie = cookie->next;
410 cookie = malloc(sizeof *cookie);
411 if (cookie == NULL) {
417 cookie->value = value;
418 cookie->freecb = freecb;
419 cookie->next = session->cookies;
420 session->cookies = cookie;