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>
32 #define NOW (time(NULL))
37 void (*free_value)(void*);
40 struct afb_event_listener_list
42 struct afb_event_listener_list *next;
43 struct afb_event_listener listener;
50 time_t expiration; // expiration time of the token
52 char uuid[37]; // long term authentication of remote client
53 char token[37]; // short term authentication of remote client
54 struct client_value *values;
55 struct afb_event_listener_list *listeners;
58 // Session UUID are store in a simple array [for 10 sessions this should be enough]
60 pthread_mutex_t mutex; // declare a mutex to protect hash table
61 struct AFB_clientCtx **store; // sessions store
62 int count; // current number of sessions
69 // Free context [XXXX Should be protected again memory abort XXXX]
70 static void ctxUuidFreeCB (struct AFB_clientCtx *client)
74 // If application add a handle let's free it now
75 assert (client->values != NULL);
77 // Free client handle with a standard Free function, with app callback or ignore it
78 for (idx=0; idx < sessions.apicount; idx ++)
79 ctxClientValueSet(client, idx, NULL, NULL);
82 // Create a new store in RAM, not that is too small it will be automatically extended
83 void ctxStoreInit (int max_session_count, int timeout, const char *initok, int context_count)
85 // let's create as store as hashtable does not have any
86 sessions.store = calloc (1 + (unsigned)max_session_count, sizeof(struct AFB_clientCtx));
87 sessions.max = max_session_count;
88 sessions.timeout = timeout;
89 sessions.apicount = context_count;
90 if (strlen(initok) >= sizeof(sessions.store[0]->token)) {
91 fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok);
94 sessions.initok = initok;
97 static struct AFB_clientCtx *ctxStoreSearch (const char* uuid)
100 struct AFB_clientCtx *client;
102 assert (uuid != NULL);
104 pthread_mutex_lock(&sessions.mutex);
106 for (idx=0; idx < sessions.max; idx++) {
107 client = sessions.store[idx];
108 if (client && (0 == strcmp (uuid, client->uuid)))
114 pthread_mutex_unlock(&sessions.mutex);
118 static int ctxStoreDel (struct AFB_clientCtx *client)
123 assert (client != NULL);
125 pthread_mutex_lock(&sessions.mutex);
127 for (idx=0; idx < sessions.max; idx++) {
128 if (sessions.store[idx] == client) {
129 sessions.store[idx] = NULL;
137 pthread_mutex_unlock(&sessions.mutex);
141 static int ctxStoreAdd (struct AFB_clientCtx *client)
146 assert (client != NULL);
148 //fprintf (stderr, "ctxStoreAdd request uuid=%s count=%d\n", client->uuid, sessions.count);
150 pthread_mutex_lock(&sessions.mutex);
152 for (idx=0; idx < sessions.max; idx++) {
153 if (NULL == sessions.store[idx]) {
154 sessions.store[idx] = client;
162 pthread_mutex_unlock(&sessions.mutex);
166 // Check if context timeout or not
167 static int ctxStoreTooOld (struct AFB_clientCtx *ctx, time_t now)
169 return ctx->expiration <= now;
172 // Loop on every entry and remove old context sessions.hash
173 static void ctxStoreCleanUp (time_t now)
175 struct AFB_clientCtx *ctx;
178 // Loop on Sessions Table and remove anything that is older than timeout
179 for (idx=0; idx < sessions.max; idx++) {
180 ctx = sessions.store[idx];
181 if (ctx != NULL && ctxStoreTooOld(ctx, now)) {
182 ctxClientClose (ctx);
187 // This function will return exiting client context or newly created client context
188 struct AFB_clientCtx *ctxClientGetSession (const char *uuid, int *created)
191 struct AFB_clientCtx *clientCtx;
196 ctxStoreCleanUp (now);
198 /* search for an existing one not too old */
200 if (strlen(uuid) >= sizeof clientCtx->uuid) {
204 clientCtx = ctxStoreSearch(uuid);
205 if (clientCtx != NULL) {
211 /* returns a new one */
212 clientCtx = calloc(1, sizeof(struct AFB_clientCtx) + ((unsigned)sessions.apicount * sizeof(*clientCtx->values)));
213 if (clientCtx == NULL) {
217 clientCtx->values = (void*)(clientCtx + 1);
219 /* generate the uuid */
221 uuid_generate(newuuid);
222 uuid_unparse_lower(newuuid, clientCtx->uuid);
224 strcpy(clientCtx->uuid, uuid);
228 strcpy(clientCtx->token, sessions.initok);
229 clientCtx->expiration = now + sessions.timeout;
230 if (!ctxStoreAdd (clientCtx)) {
237 clientCtx->access = now;
238 clientCtx->refcount++;
247 struct AFB_clientCtx *ctxClientAddRef(struct AFB_clientCtx *clientCtx)
249 if (clientCtx != NULL)
250 clientCtx->refcount++;
254 void ctxClientUnref(struct AFB_clientCtx *clientCtx)
256 if (clientCtx != NULL) {
257 assert(clientCtx->refcount != 0);
258 --clientCtx->refcount;
259 if (clientCtx->refcount == 0 && clientCtx->uuid[0] == 0) {
260 ctxStoreDel (clientCtx);
265 // Free Client Session Context
266 void ctxClientClose (struct AFB_clientCtx *clientCtx)
268 assert(clientCtx != NULL);
269 ctxUuidFreeCB (clientCtx);
270 clientCtx->uuid[0] = 0;
273 // Sample Generic Ping Debug API
274 int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token)
276 assert(clientCtx != NULL);
277 assert(token != NULL);
279 // compare current token with previous one
280 if (ctxStoreTooOld (clientCtx, NOW))
283 if (clientCtx->token[0] && strcmp (token, clientCtx->token) != 0)
289 // generate a new token and update client context
290 void ctxTokenNew (struct AFB_clientCtx *clientCtx)
294 assert(clientCtx != NULL);
296 // Old token was valid let's regenerate a new one
297 uuid_generate(newuuid); // create a new UUID
298 uuid_unparse_lower(newuuid, clientCtx->token);
300 // keep track of time for session timeout and further clean up
301 clientCtx->expiration = NOW + sessions.timeout;
304 int ctxClientEventListenerAdd(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
306 struct afb_event_listener_list *iter, **prv;
308 prv = &clientCtx->listeners;
312 iter = calloc(1, sizeof *iter);
317 iter->listener = listener;
322 if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
330 void ctxClientEventListenerRemove(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
332 struct afb_event_listener_list *iter, **prv;
334 prv = &clientCtx->listeners;
339 if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
340 if (!--iter->refcount) {
350 static int send(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
352 struct afb_event_listener_list *iter;
356 iter = clientCtx->listeners;
357 while (iter != NULL) {
358 iter->listener.itf->send(iter->listener.closure, event, json_object_get(object));
366 int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
372 if (clientCtx != NULL)
373 result = send(clientCtx, event, object);
377 for (idx=0; idx < sessions.max; idx++) {
378 clientCtx = sessions.store[idx];
379 if (clientCtx != NULL && !ctxStoreTooOld(clientCtx, now)) {
380 clientCtx = ctxClientAddRef(clientCtx);
381 result += send(clientCtx, event, object);
382 ctxClientUnref(clientCtx);
389 const char *ctxClientGetUuid (struct AFB_clientCtx *clientCtx)
391 assert(clientCtx != NULL);
392 return clientCtx->uuid;
395 const char *ctxClientGetToken (struct AFB_clientCtx *clientCtx)
397 assert(clientCtx != NULL);
398 return clientCtx->token;
401 void *ctxClientValueGet(struct AFB_clientCtx *clientCtx, int index)
403 assert(clientCtx != NULL);
405 assert(index < sessions.apicount);
406 return clientCtx->values[index].value;
409 void ctxClientValueSet(struct AFB_clientCtx *clientCtx, int index, void *value, void (*free_value)(void*))
411 struct client_value prev;
412 assert(clientCtx != NULL);
414 assert(index < sessions.apicount);
415 prev = clientCtx->values[index];
416 clientCtx->values[index] = (struct client_value){.value = value, .free_value = free_value};
417 if (prev.value != NULL && prev.free_value != NULL)
418 prev.free_value(prev.value);