+static int add_listener(struct afb_event_listener_list **head, struct afb_event_listener listener)
+{
+ struct afb_event_listener_list *iter, **prv;
+
+ prv = head;
+ for (;;) {
+ iter = *prv;
+ if (iter == NULL) {
+ iter = calloc(1, sizeof *iter);
+ if (iter == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ iter->listener = listener;
+ iter->refcount = 1;
+ *prv = iter;
+ return 0;
+ }
+ if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
+ iter->refcount++;
+ return 0;
+ }
+ prv = &iter->next;
+ }
+}
+
+int ctxClientEventListenerAdd(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
+{
+ return add_listener(clientCtx != NULL ? &clientCtx->listeners : &sessions.listeners, listener);
+}
+
+static void remove_listener(struct afb_event_listener_list **head, struct afb_event_listener listener)
+{
+ struct afb_event_listener_list *iter, **prv;
+
+ prv = head;
+ for (;;) {
+ iter = *prv;
+ if (iter == NULL)
+ return;
+ if (iter->listener.itf == listener.itf && iter->listener.closure == listener.closure) {
+ if (!--iter->refcount) {
+ *prv = iter->next;
+ free(iter);
+ }
+ return;
+ }
+ prv = &iter->next;
+ }
+}
+
+void ctxClientEventListenerRemove(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
+{
+ remove_listener(clientCtx != NULL ? &clientCtx->listeners : &sessions.listeners, listener);
+}
+
+static int send(struct afb_event_listener_list *head, const char *event, struct json_object *object)
+{
+ struct afb_event_listener_list *iter;
+ int result;
+
+ result = 0;
+ iter = head;
+ while (iter != NULL) {
+ iter->listener.itf->send(iter->listener.closure, event, json_object_get(object));
+ result++;
+ iter = iter->next;
+ }
+
+ return result;
+}
+
+int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object)
+{
+ long idx;
+ time_t now;
+ int result;
+
+ now = NOW;
+ if (clientCtx != NULL) {
+ result = ctxIsActive(clientCtx, now) ? send(clientCtx->listeners, event, object) : 0;
+ } else {
+ result = send(sessions.listeners, event, object);
+ for (idx=0; idx < sessions.max; idx++) {
+ clientCtx = ctxClientAddRef(sessions.store[idx]);
+ if (clientCtx != NULL && ctxIsActive(clientCtx, now)) {
+ clientCtx = ctxClientAddRef(clientCtx);
+ result += send(clientCtx->listeners, event, object);
+ }
+ ctxClientUnref(clientCtx);
+ }
+ }
+ return result;
+}
+
+const char *ctxClientGetUuid (struct AFB_clientCtx *clientCtx)
+{
+ assert(clientCtx != NULL);
+ return clientCtx->uuid;
+}
+
+const char *ctxClientGetToken (struct AFB_clientCtx *clientCtx)
+{
+ assert(clientCtx != NULL);
+ return clientCtx->token;
+}
+
+void *ctxClientValueGet(struct AFB_clientCtx *clientCtx, int index)
+{
+ assert(clientCtx != NULL);
+ assert(index >= 0);
+ assert(index < sessions.apicount);
+ return clientCtx->values[index].value;
+}
+
+void ctxClientValueSet(struct AFB_clientCtx *clientCtx, int index, void *value, void (*free_value)(void*))
+{
+ struct client_value prev;
+ assert(clientCtx != NULL);
+ assert(index >= 0);
+ assert(index < sessions.apicount);
+ prev = clientCtx->values[index];
+ clientCtx->values[index] = (struct client_value){.value = value, .free_value = free_value};
+ if (prev.value != NULL && prev.free_value != NULL)
+ prev.free_value(prev.value);
+}