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>
33 #define NOW (time(NULL))
38 void (*free_value)(void*);
41 struct afb_event_listener_list
43 struct afb_event_listener_list *next;
44 struct afb_event_listener listener;
51 time_t expiration; // expiration time of the token
53 char uuid[37]; // long term authentication of remote client
54 char token[37]; // short term authentication of remote client
55 struct client_value *values;
56 struct afb_event_listener_list *listeners;
59 // Session UUID are store in a simple array [for 10 sessions this should be enough]
61 pthread_mutex_t mutex; // declare a mutex to protect hash table
62 struct AFB_clientCtx **store; // sessions store
63 int count; // current number of sessions
70 // Free context [XXXX Should be protected again memory abort XXXX]
71 static void ctxUuidFreeCB (struct AFB_clientCtx *client)
75 // If application add a handle let's free it now
76 assert (client->values != NULL);
78 // Free client handle with a standard Free function, with app callback or ignore it
79 for (idx=0; idx < sessions.apicount; idx ++)
80 ctxClientValueSet(client, idx, NULL, NULL);
83 // Create a new store in RAM, not that is too small it will be automatically extended
84 void ctxStoreInit (int max_session_count, int timeout, const char *initok, int context_count)
86 // let's create as store as hashtable does not have any
87 sessions.store = calloc (1 + (unsigned)max_session_count, sizeof(struct AFB_clientCtx));
88 sessions.max = max_session_count;
89 sessions.timeout = timeout;
90 sessions.apicount = context_count;
91 if (strlen(initok) >= sizeof(sessions.store[0]->token)) {
92 ERROR("initial token '%s' too long (max length 36)", initok);
95 sessions.initok = initok;
98 static struct AFB_clientCtx *ctxStoreSearch (const char* uuid)
101 struct AFB_clientCtx *client;
103 assert (uuid != NULL);
105 pthread_mutex_lock(&sessions.mutex);
107 for (idx=0; idx < sessions.max; idx++) {
108 client = sessions.store[idx];
109 if (client && (0 == strcmp (uuid, client->uuid)))
115 pthread_mutex_unlock(&sessions.mutex);
119 static int ctxStoreDel (struct AFB_clientCtx *client)
124 assert (client != NULL);
126 pthread_mutex_lock(&sessions.mutex);
128 for (idx=0; idx < sessions.max; idx++) {
129 if (sessions.store[idx] == client) {
130 sessions.store[idx] = NULL;
138 pthread_mutex_unlock(&sessions.mutex);
142 static int ctxStoreAdd (struct AFB_clientCtx *client)
147 assert (client != NULL);
149 pthread_mutex_lock(&sessions.mutex);
151 for (idx=0; idx < sessions.max; idx++) {
152 if (NULL == sessions.store[idx]) {
153 sessions.store[idx] = client;
161 pthread_mutex_unlock(&sessions.mutex);
165 // Check if context timeout or not
166 static int ctxStoreTooOld (struct AFB_clientCtx *ctx, time_t now)
168 return ctx->expiration <= now;
171 // Loop on every entry and remove old context sessions.hash
172 static void ctxStoreCleanUp (time_t now)
174 struct AFB_clientCtx *ctx;
177 // Loop on Sessions Table and remove anything that is older than timeout
178 for (idx=0; idx < sessions.max; idx++) {
179 ctx = sessions.store[idx];
180 if (ctx != NULL && ctxStoreTooOld(ctx, now)) {
181 ctxClientClose (ctx);
186 // This function will return exiting client context or newly created client context
187 struct AFB_clientCtx *ctxClientGetSession (const char *uuid, int *created)
190 struct AFB_clientCtx *clientCtx;
195 ctxStoreCleanUp (now);
197 /* search for an existing one not too old */
199 if (strlen(uuid) >= sizeof clientCtx->uuid) {
203 clientCtx = ctxStoreSearch(uuid);
204 if (clientCtx != NULL) {
210 /* returns a new one */
211 clientCtx = calloc(1, sizeof(struct AFB_clientCtx) + ((unsigned)sessions.apicount * sizeof(*clientCtx->values)));
212 if (clientCtx == NULL) {
216 clientCtx->values = (void*)(clientCtx + 1);
218 /* generate the uuid */
220 uuid_generate(newuuid);
221 uuid_unparse_lower(newuuid, clientCtx->uuid);
223 strcpy(clientCtx->uuid, uuid);
227 strcpy(clientCtx->token, sessions.initok);
228 clientCtx->expiration = now + sessions.timeout;
229 if (!ctxStoreAdd (clientCtx)) {
236 clientCtx->access = now;
237 clientCtx->refcount++;
246 struct AFB_clientCtx *ctxClientAddRef(struct AFB_clientCtx *clientCtx)
248 if (clientCtx != NULL)
249 clientCtx->refcount++;
253 void ctxClientUnref(struct AFB_clientCtx *clientCtx)
255 if (clientCtx != NULL) {
256 assert(clientCtx->refcount != 0);
257 --clientCtx->refcount;
258 if (clientCtx->refcount == 0 && clientCtx->uuid[0] == 0) {
259 ctxStoreDel (clientCtx);
264 // Free Client Session Context
265 void ctxClientClose (struct AFB_clientCtx *clientCtx)
267 assert(clientCtx != NULL);
268 ctxUuidFreeCB (clientCtx);
269 clientCtx->uuid[0] = 0;
272 // Sample Generic Ping Debug API
273 int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token)
275 assert(clientCtx != NULL);
276 assert(token != NULL);
278 // compare current token with previous one
279 if (ctxStoreTooOld (clientCtx, NOW))
282 if (clientCtx->token[0] && strcmp (token, clientCtx->token) != 0)
288 // generate a new token and update client context
289 void ctxTokenNew (struct AFB_clientCtx *clientCtx)
293 assert(clientCtx != NULL);
295 // Old token was valid let's regenerate a new one
296 uuid_generate(newuuid); // create a new UUID
297 uuid_unparse_lower(newuuid, clientCtx->token);
299 // keep track of time for session timeout and further clean up
300 clientCtx->expiration = NOW + sessions.timeout;
303 int ctxClientEventListenerAdd(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
305 struct afb_event_listener_list *iter, **prv;
307 prv = &clientCtx->listeners;
311 iter = calloc(1, sizeof *iter);
316 iter->listener = listener;
321 if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
329 void ctxClientEventListenerRemove(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
331 struct afb_event_listener_list *iter, **prv;
333 prv = &clientCtx->listeners;
338 if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
339 if (!--iter->refcount) {
349 static int send(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
351 struct afb_event_listener_list *iter;
355 iter = clientCtx->listeners;
356 while (iter != NULL) {
357 iter->listener.itf->send(iter->listener.closure, event, json_object_get(object));
365 int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
371 if (clientCtx != NULL)
372 result = send(clientCtx, event, object);
376 for (idx=0; idx < sessions.max; idx++) {
377 clientCtx = sessions.store[idx];
378 if (clientCtx != NULL && !ctxStoreTooOld(clientCtx, now)) {
379 clientCtx = ctxClientAddRef(clientCtx);
380 result += send(clientCtx, event, object);
381 ctxClientUnref(clientCtx);
388 const char *ctxClientGetUuid (struct AFB_clientCtx *clientCtx)
390 assert(clientCtx != NULL);
391 return clientCtx->uuid;
394 const char *ctxClientGetToken (struct AFB_clientCtx *clientCtx)
396 assert(clientCtx != NULL);
397 return clientCtx->token;
400 void *ctxClientValueGet(struct AFB_clientCtx *clientCtx, int index)
402 assert(clientCtx != NULL);
404 assert(index < sessions.apicount);
405 return clientCtx->values[index].value;
408 void ctxClientValueSet(struct AFB_clientCtx *clientCtx, int index, void *value, void (*free_value)(void*))
410 struct client_value prev;
411 assert(clientCtx != NULL);
413 assert(index < sessions.apicount);
414 prev = clientCtx->values[index];
415 clientCtx->values[index] = (struct client_value){.value = value, .free_value = free_value};
416 if (prev.value != NULL && prev.free_value != NULL)
417 prev.free_value(prev.value);