2 * Copyright (C) 2015 "IoT.bzh"
3 * Author "Fulup Ar Foll"
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * http://stackoverflow.com/questions/25971505/how-to-delete-element-from-hsearch
29 #include <uuid/uuid.h>
35 #define NOW (time(NULL))
37 // Session UUID are store in a simple array [for 10 sessions this should be enough]
39 pthread_mutex_t mutex; // declare a mutex to protect hash table
40 struct AFB_clientCtx **store; // sessions store
41 int count; // current number of sessions
48 // Free context [XXXX Should be protected again memory abort XXXX]
49 static void ctxUuidFreeCB (struct AFB_clientCtx *client)
53 // If application add a handle let's free it now
54 assert (client->contexts != NULL);
56 // Free client handle with a standard Free function, with app callback or ignore it
57 for (idx=0; idx < sessions.apicount; idx ++) {
58 if (client->contexts[idx] != NULL) {
59 afb_apis_free_context(idx, client->contexts[idx]);
60 client->contexts[idx] = NULL;
65 // Create a new store in RAM, not that is too small it will be automatically extended
66 void ctxStoreInit (int nbSession, int timeout, const char *initok)
68 // let's create as store as hashtable does not have any
69 sessions.store = calloc (1 + (unsigned)nbSession, sizeof(struct AFB_clientCtx));
70 sessions.max = nbSession;
71 sessions.timeout = timeout;
72 sessions.apicount = afb_apis_count();
73 if (strlen(initok) >= 37) {
74 fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok);
77 sessions.initok = initok;
80 static struct AFB_clientCtx *ctxStoreSearch (const char* uuid)
83 struct AFB_clientCtx *client;
85 assert (uuid != NULL);
87 pthread_mutex_lock(&sessions.mutex);
89 for (idx=0; idx < sessions.max; idx++) {
90 client = sessions.store[idx];
91 if (client && (0 == strcmp (uuid, client->uuid)))
97 pthread_mutex_unlock(&sessions.mutex);
101 static int ctxStoreDel (struct AFB_clientCtx *client)
106 assert (client != NULL);
108 pthread_mutex_lock(&sessions.mutex);
110 for (idx=0; idx < sessions.max; idx++) {
111 if (sessions.store[idx] == client) {
112 sessions.store[idx]=NULL;
120 pthread_mutex_unlock(&sessions.mutex);
124 static int ctxStoreAdd (struct AFB_clientCtx *client)
129 assert (client != NULL);
131 //fprintf (stderr, "ctxStoreAdd request uuid=%s count=%d\n", client->uuid, sessions.count);
133 pthread_mutex_lock(&sessions.mutex);
135 for (idx=0; idx < sessions.max; idx++) {
136 if (NULL == sessions.store[idx]) {
137 sessions.store[idx]= client;
145 pthread_mutex_unlock(&sessions.mutex);
149 // Check if context timeout or not
150 static int ctxStoreTooOld (struct AFB_clientCtx *ctx, time_t now)
152 return ctx->expiration <= now;
155 // Loop on every entry and remove old context sessions.hash
156 static void ctxStoreCleanUp (time_t now)
158 struct AFB_clientCtx *ctx;
161 // Loop on Sessions Table and remove anything that is older than timeout
162 for (idx=0; idx < sessions.max; idx++) {
163 ctx = sessions.store[idx];
164 if (ctx != NULL && ctxStoreTooOld(ctx, now)) {
165 ctxClientClose (ctx);
170 // This function will return exiting client context or newly created client context
171 struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid)
174 struct AFB_clientCtx *clientCtx;
177 /* search for an existing one not too old */
179 ctxStoreCleanUp (now);
180 clientCtx = uuid != NULL ? ctxStoreSearch (uuid) : NULL;
182 clientCtx->refcount++;
186 /* mimic old behaviour */
188 TODO remove? not remove?
189 if (sessions.initok == NULL)
192 /* check the uuid if given */
193 if (uuid != NULL && strlen(uuid) >= sizeof clientCtx->uuid)
196 /* returns a new one */
197 clientCtx = calloc(1, sizeof(struct AFB_clientCtx)); // init NULL clientContext
198 if (clientCtx != NULL) {
199 clientCtx->contexts = calloc ((unsigned)sessions.apicount, sizeof (void*));
200 if (clientCtx->contexts != NULL) {
201 /* generate the uuid */
203 uuid_generate(newuuid);
204 uuid_unparse_lower(newuuid, clientCtx->uuid);
206 strcpy(clientCtx->uuid, uuid);
208 strcpy(clientCtx->token, sessions.initok);
209 clientCtx->expiration = now + sessions.timeout;
210 clientCtx->refcount = 1;
211 if (ctxStoreAdd (clientCtx))
213 free(clientCtx->contexts);
220 struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx)
222 if (clientCtx != NULL)
223 clientCtx->refcount++;
227 void ctxClientPut(struct AFB_clientCtx *clientCtx)
229 if (clientCtx != NULL) {
230 assert(clientCtx->refcount != 0);
231 --clientCtx->refcount;
235 // Free Client Session Context
236 void ctxClientClose (struct AFB_clientCtx *clientCtx)
238 assert(clientCtx != NULL);
239 if (clientCtx->created) {
240 clientCtx->created = 0;
241 ctxUuidFreeCB (clientCtx);
243 if (clientCtx->refcount == 0)
244 ctxStoreDel (clientCtx);
247 // Sample Generic Ping Debug API
248 int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t length)
250 assert(clientCtx != NULL);
251 assert(token != NULL);
253 // compare current token with previous one
254 if (ctxStoreTooOld (clientCtx, NOW))
257 if (clientCtx->token[0] && (length >= sizeof(clientCtx->token) || strncmp (token, clientCtx->token, length) || clientCtx->token[length]))
260 clientCtx->created = 1; /* creates by default */
264 // Sample Generic Ping Debug API
265 int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token)
267 assert(clientCtx != NULL);
268 assert(token != NULL);
270 return ctxTokenCheckLen(clientCtx, token, strlen(token));
273 // generate a new token and update client context
274 void ctxTokenNew (struct AFB_clientCtx *clientCtx)
278 assert(clientCtx != NULL);
280 // Old token was valid let's regenerate a new one
281 uuid_generate(newuuid); // create a new UUID
282 uuid_unparse_lower(newuuid, clientCtx->token);
284 // keep track of time for session timeout and further clean up
285 clientCtx->expiration = NOW + sessions.timeout;