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))
34 // Session UUID are store in a simple array [for 10 sessions this should be enough]
36 pthread_mutex_t mutex; // declare a mutex to protect hash table
37 struct AFB_clientCtx **store; // sessions store
38 int count; // current number of sessions
45 void *afb_context_get(struct afb_context *actx)
50 void afb_context_set(struct afb_context *actx, void *context, void (*free_context)(void*))
52 fprintf(stderr, "afb_context_set(%p,%p) was (%p,%p)\n",context, free_context, actx->context, actx->free_context);
53 if (actx->context != NULL && actx->free_context != NULL)
54 actx->free_context(actx->context);
55 actx->context = context;
56 actx->free_context = free_context;
59 // Free context [XXXX Should be protected again memory abort XXXX]
60 static void ctxUuidFreeCB (struct AFB_clientCtx *client)
64 // If application add a handle let's free it now
65 assert (client->contexts != NULL);
67 // Free client handle with a standard Free function, with app callback or ignore it
68 for (idx=0; idx < sessions.apicount; idx ++)
69 afb_context_set(&client->contexts[idx], NULL, NULL);
72 // Create a new store in RAM, not that is too small it will be automatically extended
73 void ctxStoreInit (int max_session_count, int timeout, const char *initok, int context_count)
75 // let's create as store as hashtable does not have any
76 sessions.store = calloc (1 + (unsigned)max_session_count, sizeof(struct AFB_clientCtx));
77 sessions.max = max_session_count;
78 sessions.timeout = timeout;
79 sessions.apicount = context_count;
80 if (strlen(initok) >= 37) {
81 fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok);
84 sessions.initok = initok;
87 static struct AFB_clientCtx *ctxStoreSearch (const char* uuid)
90 struct AFB_clientCtx *client;
92 assert (uuid != NULL);
94 pthread_mutex_lock(&sessions.mutex);
96 for (idx=0; idx < sessions.max; idx++) {
97 client = sessions.store[idx];
98 if (client && (0 == strcmp (uuid, client->uuid)))
104 pthread_mutex_unlock(&sessions.mutex);
108 static int ctxStoreDel (struct AFB_clientCtx *client)
113 assert (client != NULL);
115 pthread_mutex_lock(&sessions.mutex);
117 for (idx=0; idx < sessions.max; idx++) {
118 if (sessions.store[idx] == client) {
119 sessions.store[idx] = NULL;
127 pthread_mutex_unlock(&sessions.mutex);
131 static int ctxStoreAdd (struct AFB_clientCtx *client)
136 assert (client != NULL);
138 //fprintf (stderr, "ctxStoreAdd request uuid=%s count=%d\n", client->uuid, sessions.count);
140 pthread_mutex_lock(&sessions.mutex);
142 for (idx=0; idx < sessions.max; idx++) {
143 if (NULL == sessions.store[idx]) {
144 sessions.store[idx] = client;
152 pthread_mutex_unlock(&sessions.mutex);
156 // Check if context timeout or not
157 static int ctxStoreTooOld (struct AFB_clientCtx *ctx, time_t now)
159 return ctx->expiration <= now;
162 // Loop on every entry and remove old context sessions.hash
163 static void ctxStoreCleanUp (time_t now)
165 struct AFB_clientCtx *ctx;
168 // Loop on Sessions Table and remove anything that is older than timeout
169 for (idx=0; idx < sessions.max; idx++) {
170 ctx = sessions.store[idx];
171 if (ctx != NULL && ctxStoreTooOld(ctx, now)) {
172 ctxClientClose (ctx);
177 // This function will return exiting client context or newly created client context
178 struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid)
181 struct AFB_clientCtx *clientCtx;
184 /* search for an existing one not too old */
186 ctxStoreCleanUp (now);
187 clientCtx = uuid != NULL ? ctxStoreSearch (uuid) : NULL;
189 clientCtx->refcount++;
193 /* mimic old behaviour */
195 TODO remove? not remove?
196 if (sessions.initok == NULL)
199 /* check the uuid if given */
200 if (uuid != NULL && strlen(uuid) >= sizeof clientCtx->uuid)
203 /* returns a new one */
204 clientCtx = calloc(1, sizeof(struct AFB_clientCtx)); // init NULL clientContext
205 if (clientCtx != NULL) {
206 clientCtx->contexts = calloc ((unsigned)sessions.apicount, sizeof(*clientCtx->contexts));
207 if (clientCtx->contexts != NULL) {
208 /* generate the uuid */
210 uuid_generate(newuuid);
211 uuid_unparse_lower(newuuid, clientCtx->uuid);
213 strcpy(clientCtx->uuid, uuid);
215 strcpy(clientCtx->token, sessions.initok);
216 clientCtx->expiration = now + sessions.timeout;
217 clientCtx->refcount = 1;
218 if (ctxStoreAdd (clientCtx))
220 free(clientCtx->contexts);
227 struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx)
229 if (clientCtx != NULL)
230 clientCtx->refcount++;
234 void ctxClientPut(struct AFB_clientCtx *clientCtx)
236 if (clientCtx != NULL) {
237 assert(clientCtx->refcount != 0);
238 --clientCtx->refcount;
242 // Free Client Session Context
243 void ctxClientClose (struct AFB_clientCtx *clientCtx)
245 assert(clientCtx != NULL);
246 if (clientCtx->created) {
247 clientCtx->created = 0;
248 ctxUuidFreeCB (clientCtx);
250 if (clientCtx->refcount == 0)
251 ctxStoreDel (clientCtx);
254 // Sample Generic Ping Debug API
255 int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t length)
257 assert(clientCtx != NULL);
258 assert(token != NULL);
260 // compare current token with previous one
261 if (ctxStoreTooOld (clientCtx, NOW))
264 if (clientCtx->token[0] && (length >= sizeof(clientCtx->token) || strncmp (token, clientCtx->token, length) || clientCtx->token[length]))
267 clientCtx->created = 1; /* creates by default */
271 // Sample Generic Ping Debug API
272 int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token)
274 assert(clientCtx != NULL);
275 assert(token != NULL);
277 return ctxTokenCheckLen(clientCtx, token, strlen(token));
280 // generate a new token and update client context
281 void ctxTokenNew (struct AFB_clientCtx *clientCtx)
285 assert(clientCtx != NULL);
287 // Old token was valid let's regenerate a new one
288 uuid_generate(newuuid); // create a new UUID
289 uuid_unparse_lower(newuuid, clientCtx->token);
291 // keep track of time for session timeout and further clean up
292 clientCtx->expiration = NOW + sessions.timeout;
295 struct afb_event_listener_list
297 struct afb_event_listener_list *next;
298 struct afb_event_listener listener;
302 int ctxClientEventListenerAdd(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
304 struct afb_event_listener_list *iter, **prv;
306 prv = &clientCtx->listeners;
310 iter = calloc(1, sizeof *iter);
315 iter->listener = listener;
320 if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
328 void ctxClientEventListenerRemove(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
330 struct afb_event_listener_list *iter, **prv;
332 prv = &clientCtx->listeners;
337 if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
338 if (!--iter->refcount) {
348 static int send(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
350 struct afb_event_listener_list *iter;
354 iter = clientCtx->listeners;
355 while (iter != NULL) {
356 iter->listener.itf->send(iter->listener.closure, event, json_object_get(object));
364 int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
370 if (clientCtx != NULL)
371 result = send(clientCtx, event, object);
375 for (idx=0; idx < sessions.max; idx++) {
376 clientCtx = sessions.store[idx];
377 if (clientCtx != NULL && !ctxStoreTooOld(clientCtx, now)) {
378 clientCtx = ctxClientGet(clientCtx);
379 result += send(clientCtx, event, object);
380 ctxClientPut(clientCtx);