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 void *afb_context_get(struct afb_context *actx)
48 void afb_context_set(struct afb_context *actx, void *context, void (*free_context)(void*))
50 fprintf(stderr, "afb_context_set(%p,%p) was (%p,%p)\n",context, free_context, actx->context, actx->free_context);
51 if (actx->context != NULL && actx->free_context != NULL)
52 actx->free_context(actx->context);
53 actx->context = context;
54 actx->free_context = free_context;
57 // Free context [XXXX Should be protected again memory abort XXXX]
58 static void ctxUuidFreeCB (struct AFB_clientCtx *client)
62 // If application add a handle let's free it now
63 assert (client->contexts != NULL);
65 // Free client handle with a standard Free function, with app callback or ignore it
66 for (idx=0; idx < sessions.apicount; idx ++)
67 afb_context_set(&client->contexts[idx], NULL, NULL);
70 // Create a new store in RAM, not that is too small it will be automatically extended
71 void ctxStoreInit (int max_session_count, int timeout, const char *initok, int context_count)
73 // let's create as store as hashtable does not have any
74 sessions.store = calloc (1 + (unsigned)max_session_count, sizeof(struct AFB_clientCtx));
75 sessions.max = max_session_count;
76 sessions.timeout = timeout;
77 sessions.apicount = context_count;
78 if (strlen(initok) >= 37) {
79 fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok);
82 sessions.initok = initok;
85 static struct AFB_clientCtx *ctxStoreSearch (const char* uuid)
88 struct AFB_clientCtx *client;
90 assert (uuid != NULL);
92 pthread_mutex_lock(&sessions.mutex);
94 for (idx=0; idx < sessions.max; idx++) {
95 client = sessions.store[idx];
96 if (client && (0 == strcmp (uuid, client->uuid)))
102 pthread_mutex_unlock(&sessions.mutex);
106 static int ctxStoreDel (struct AFB_clientCtx *client)
111 assert (client != NULL);
113 pthread_mutex_lock(&sessions.mutex);
115 for (idx=0; idx < sessions.max; idx++) {
116 if (sessions.store[idx] == client) {
117 sessions.store[idx] = NULL;
125 pthread_mutex_unlock(&sessions.mutex);
129 static int ctxStoreAdd (struct AFB_clientCtx *client)
134 assert (client != NULL);
136 //fprintf (stderr, "ctxStoreAdd request uuid=%s count=%d\n", client->uuid, sessions.count);
138 pthread_mutex_lock(&sessions.mutex);
140 for (idx=0; idx < sessions.max; idx++) {
141 if (NULL == sessions.store[idx]) {
142 sessions.store[idx] = client;
150 pthread_mutex_unlock(&sessions.mutex);
154 // Check if context timeout or not
155 static int ctxStoreTooOld (struct AFB_clientCtx *ctx, time_t now)
157 return ctx->expiration <= now;
160 // Loop on every entry and remove old context sessions.hash
161 static void ctxStoreCleanUp (time_t now)
163 struct AFB_clientCtx *ctx;
166 // Loop on Sessions Table and remove anything that is older than timeout
167 for (idx=0; idx < sessions.max; idx++) {
168 ctx = sessions.store[idx];
169 if (ctx != NULL && ctxStoreTooOld(ctx, now)) {
170 ctxClientClose (ctx);
175 // This function will return exiting client context or newly created client context
176 struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid)
179 struct AFB_clientCtx *clientCtx;
182 /* search for an existing one not too old */
184 ctxStoreCleanUp (now);
185 clientCtx = uuid != NULL ? ctxStoreSearch (uuid) : NULL;
187 clientCtx->refcount++;
191 /* mimic old behaviour */
193 TODO remove? not remove?
194 if (sessions.initok == NULL)
197 /* check the uuid if given */
198 if (uuid != NULL && strlen(uuid) >= sizeof clientCtx->uuid)
201 /* returns a new one */
202 clientCtx = calloc(1, sizeof(struct AFB_clientCtx)); // init NULL clientContext
203 if (clientCtx != NULL) {
204 clientCtx->contexts = calloc ((unsigned)sessions.apicount, sizeof(*clientCtx->contexts));
205 if (clientCtx->contexts != NULL) {
206 /* generate the uuid */
208 uuid_generate(newuuid);
209 uuid_unparse_lower(newuuid, clientCtx->uuid);
211 strcpy(clientCtx->uuid, uuid);
213 strcpy(clientCtx->token, sessions.initok);
214 clientCtx->expiration = now + sessions.timeout;
215 clientCtx->refcount = 1;
216 if (ctxStoreAdd (clientCtx))
218 free(clientCtx->contexts);
225 struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx)
227 if (clientCtx != NULL)
228 clientCtx->refcount++;
232 void ctxClientPut(struct AFB_clientCtx *clientCtx)
234 if (clientCtx != NULL) {
235 assert(clientCtx->refcount != 0);
236 --clientCtx->refcount;
240 // Free Client Session Context
241 void ctxClientClose (struct AFB_clientCtx *clientCtx)
243 assert(clientCtx != NULL);
244 if (clientCtx->created) {
245 clientCtx->created = 0;
246 ctxUuidFreeCB (clientCtx);
248 if (clientCtx->refcount == 0)
249 ctxStoreDel (clientCtx);
252 // Sample Generic Ping Debug API
253 int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t length)
255 assert(clientCtx != NULL);
256 assert(token != NULL);
258 // compare current token with previous one
259 if (ctxStoreTooOld (clientCtx, NOW))
262 if (clientCtx->token[0] && (length >= sizeof(clientCtx->token) || strncmp (token, clientCtx->token, length) || clientCtx->token[length]))
265 clientCtx->created = 1; /* creates by default */
269 // Sample Generic Ping Debug API
270 int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token)
272 assert(clientCtx != NULL);
273 assert(token != NULL);
275 return ctxTokenCheckLen(clientCtx, token, strlen(token));
278 // generate a new token and update client context
279 void ctxTokenNew (struct AFB_clientCtx *clientCtx)
283 assert(clientCtx != NULL);
285 // Old token was valid let's regenerate a new one
286 uuid_generate(newuuid); // create a new UUID
287 uuid_unparse_lower(newuuid, clientCtx->token);
289 // keep track of time for session timeout and further clean up
290 clientCtx->expiration = NOW + sessions.timeout;
293 struct afb_event_sender_list
295 struct afb_event_sender_list *next;
296 struct afb_event_sender sender;
300 int ctxClientEventSenderAdd(struct AFB_clientCtx *clientCtx, struct afb_event_sender sender)
302 struct afb_event_sender_list *iter, **prv;
304 prv = &clientCtx->senders;
308 iter = calloc(1, sizeof *iter);
313 iter->sender = sender;
318 if (iter->sender.itf == sender.itf && iter->sender.closure == sender.closure) {
326 void ctxClientEventSenderRemove(struct AFB_clientCtx *clientCtx, struct afb_event_sender sender)
328 struct afb_event_sender_list *iter, **prv;
330 prv = &clientCtx->senders;
335 if (iter->sender.itf == sender.itf && iter->sender.closure == sender.closure) {
336 if (!--iter->refcount) {
346 static int send(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
348 struct afb_event_sender_list *iter;
352 iter = clientCtx->senders;
353 while (iter != NULL) {
354 iter->sender.itf->send(iter->sender.closure, event, object);
362 int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
368 if (clientCtx != NULL)
369 result = send(clientCtx, event, object);
373 for (idx=0; idx < sessions.max; idx++) {
374 clientCtx = sessions.store[idx];
375 if (clientCtx != NULL && !ctxStoreTooOld(clientCtx, now)) {
376 clientCtx = ctxClientGet(clientCtx);
377 result += send(clientCtx, event, object);
378 ctxClientPut(clientCtx);