2 * Copyright (C) 2015 "IoT.bzh"
3 * Author "Fulup Ar Foll"
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
24 #include <uuid/uuid.h>
30 #define NOW (time(NULL))
32 // Session UUID are store in a simple array [for 10 sessions this should be enough]
34 pthread_mutex_t mutex; // declare a mutex to protect hash table
35 struct AFB_clientCtx **store; // sessions store
36 int count; // current number of sessions
43 // Free context [XXXX Should be protected again memory abort XXXX]
44 static void ctxUuidFreeCB (struct AFB_clientCtx *client)
48 // If application add a handle let's free it now
49 assert (client->contexts != NULL);
51 // Free client handle with a standard Free function, with app callback or ignore it
52 for (idx=0; idx < sessions.apicount; idx ++) {
53 if (client->contexts[idx] != NULL) {
54 afb_apis_free_context(idx, client->contexts[idx]);
55 client->contexts[idx] = NULL;
60 // Create a new store in RAM, not that is too small it will be automatically extended
61 void ctxStoreInit (int nbSession, int timeout, const char *initok)
63 // let's create as store as hashtable does not have any
64 sessions.store = calloc (1 + (unsigned)nbSession, sizeof(struct AFB_clientCtx));
65 sessions.max = nbSession;
66 sessions.timeout = timeout;
67 sessions.apicount = afb_apis_count();
68 if (strlen(initok) >= 37) {
69 fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok);
72 sessions.initok = initok;
75 static struct AFB_clientCtx *ctxStoreSearch (const char* uuid)
78 struct AFB_clientCtx *client;
80 assert (uuid != NULL);
82 pthread_mutex_lock(&sessions.mutex);
84 for (idx=0; idx < sessions.max; idx++) {
85 client = sessions.store[idx];
86 if (client && (0 == strcmp (uuid, client->uuid)))
92 pthread_mutex_unlock(&sessions.mutex);
96 static int ctxStoreDel (struct AFB_clientCtx *client)
101 assert (client != NULL);
103 pthread_mutex_lock(&sessions.mutex);
105 for (idx=0; idx < sessions.max; idx++) {
106 if (sessions.store[idx] == client) {
107 sessions.store[idx]=NULL;
115 pthread_mutex_unlock(&sessions.mutex);
119 static int ctxStoreAdd (struct AFB_clientCtx *client)
124 assert (client != NULL);
126 //fprintf (stderr, "ctxStoreAdd request uuid=%s count=%d\n", client->uuid, sessions.count);
128 pthread_mutex_lock(&sessions.mutex);
130 for (idx=0; idx < sessions.max; idx++) {
131 if (NULL == sessions.store[idx]) {
132 sessions.store[idx]= client;
140 pthread_mutex_unlock(&sessions.mutex);
144 // Check if context timeout or not
145 static int ctxStoreTooOld (struct AFB_clientCtx *ctx, time_t now)
147 return ctx->expiration <= now;
150 // Loop on every entry and remove old context sessions.hash
151 static void ctxStoreCleanUp (time_t now)
153 struct AFB_clientCtx *ctx;
156 // Loop on Sessions Table and remove anything that is older than timeout
157 for (idx=0; idx < sessions.max; idx++) {
158 ctx = sessions.store[idx];
159 if (ctx != NULL && ctxStoreTooOld(ctx, now)) {
160 ctxClientClose (ctx);
165 // This function will return exiting client context or newly created client context
166 struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid)
169 struct AFB_clientCtx *clientCtx;
172 /* search for an existing one not too old */
174 ctxStoreCleanUp (now);
175 clientCtx = uuid != NULL ? ctxStoreSearch (uuid) : NULL;
177 clientCtx->refcount++;
181 /* mimic old behaviour */
183 TODO remove? not remove?
184 if (sessions.initok == NULL)
187 /* check the uuid if given */
188 if (uuid != NULL && strlen(uuid) >= sizeof clientCtx->uuid)
191 /* returns a new one */
192 clientCtx = calloc(1, sizeof(struct AFB_clientCtx)); // init NULL clientContext
193 if (clientCtx != NULL) {
194 clientCtx->contexts = calloc ((unsigned)sessions.apicount, sizeof (void*));
195 if (clientCtx->contexts != NULL) {
196 /* generate the uuid */
198 uuid_generate(newuuid);
199 uuid_unparse_lower(newuuid, clientCtx->uuid);
201 strcpy(clientCtx->uuid, uuid);
203 strcpy(clientCtx->token, sessions.initok);
204 clientCtx->expiration = now + sessions.timeout;
205 clientCtx->refcount = 1;
206 if (ctxStoreAdd (clientCtx))
208 free(clientCtx->contexts);
215 struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx)
217 if (clientCtx != NULL)
218 clientCtx->refcount++;
222 void ctxClientPut(struct AFB_clientCtx *clientCtx)
224 if (clientCtx != NULL) {
225 assert(clientCtx->refcount != 0);
226 --clientCtx->refcount;
230 // Free Client Session Context
231 void ctxClientClose (struct AFB_clientCtx *clientCtx)
233 assert(clientCtx != NULL);
234 if (clientCtx->created) {
235 clientCtx->created = 0;
236 ctxUuidFreeCB (clientCtx);
238 if (clientCtx->refcount == 0)
239 ctxStoreDel (clientCtx);
242 // Sample Generic Ping Debug API
243 int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t length)
245 assert(clientCtx != NULL);
246 assert(token != NULL);
248 // compare current token with previous one
249 if (ctxStoreTooOld (clientCtx, NOW))
252 if (clientCtx->token[0] && (length >= sizeof(clientCtx->token) || strncmp (token, clientCtx->token, length) || clientCtx->token[length]))
255 clientCtx->created = 1; /* creates by default */
259 // Sample Generic Ping Debug API
260 int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token)
262 assert(clientCtx != NULL);
263 assert(token != NULL);
265 return ctxTokenCheckLen(clientCtx, token, strlen(token));
268 // generate a new token and update client context
269 void ctxTokenNew (struct AFB_clientCtx *clientCtx)
273 assert(clientCtx != NULL);
275 // Old token was valid let's regenerate a new one
276 uuid_generate(newuuid); // create a new UUID
277 uuid_unparse_lower(newuuid, clientCtx->token);
279 // keep track of time for session timeout and further clean up
280 clientCtx->expiration = NOW + sessions.timeout;