123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- #include "rl_subscription.h"
- #include "../../mem/mem.h"
- #include "../../mem/shm_mem.h"
- #include <xcap/resource_list.h>
- #include "rls_mod.h"
- #include "result_codes.h"
- #include <cds/dstring.h>
- #include <cds/logger.h>
- #include <presence/qsa.h>
- #include <presence/pres_doc.h>
- #include <presence/pidf.h>
- #include <cds/list.h>
- #include "rls_data.h"
- #include "rls_auth.h"
- /* shared structure holding the data */
- typedef struct {
- virtual_subscription_t *first;
- virtual_subscription_t *last;
- /* hash, ... */
- notifier_domain_t *domain;
- qsa_content_type_t *ct_presence_info;
- qsa_content_type_t *ct_raw;
- } vs_data_t;
- static vs_data_t *vsd = NULL;
- /******** global functions (initialization) ********/
- int vs_init()
- {
- static str presence_info = STR_STATIC_INIT(CT_PRESENCE_INFO);
- static str raw = STR_STATIC_INIT(CT_RAW);
-
- vsd = (vs_data_t*)mem_alloc(sizeof(vs_data_t));
- if (!vsd) {
- LOG(L_ERR, "vs_init(): memory allocation error\n");
- return -1;
- }
- vsd->first = NULL;
- vsd->last = NULL;
- vsd->domain = qsa_get_default_domain();
- if (!vsd->domain) {
- LOG(L_ERR, "vs_init(): can't register notifier domain\n");
- return -1;
- }
- DEBUG_LOG("QSA (vs) domain: %p\n", vsd->domain);
-
- vsd->ct_presence_info = register_content_type(vsd->domain,
- &presence_info, (destroy_function_f)free_presentity_info);
- if (!vsd->ct_presence_info) {
- ERR("can't register QSA content type\n");
- return -1;
- }
- else TRACE("RLS_PRESENCE_INFO: %p\n", vsd->ct_presence_info);
-
- vsd->ct_raw = register_content_type(vsd->domain,
- &raw, (destroy_function_f)free_raw_presence_info);
- if (!vsd->ct_raw) {
- ERR("can't register QSA content type\n");
- return -1;
- }
- else TRACE("RLS_RAW: %p\n", vsd->ct_raw);
-
- return 0;
- }
- int vs_destroy()
- {
- /* virtual subscriptions are freed in rls_free */
- if (vsd) {
- qsa_release_domain(vsd->domain);
- vsd->domain = NULL;
- mem_free(vsd);
- vsd = NULL;
- }
- return 0;
- }
- /******** Helper functions ********/
- /* sets new documents (frees them if equal) */
- static void set_vs_document(virtual_subscription_t *vs,
- str_t *new_doc,
- str_t *new_content_type)
- {
- if (str_case_equals(&vs->state_document, new_doc) == 0) {
- /* DEBUG("new document is equal to the older one\n"); */
- str_free_content(new_doc);
- }
- else {
- str_free_content(&vs->state_document);
- if (new_doc) vs->state_document = *new_doc;
- else str_clear(&vs->state_document);
- vs->changed = 1;
- }
-
- if (str_case_equals(&vs->content_type, new_content_type) == 0) {
- /* DEBUG("new content-type is equal to the older one\n"); */
- str_free_content(new_content_type);
- }
- else {
- str_free_content(&vs->content_type);
- if (new_content_type) vs->content_type = *new_content_type;
- else str_clear(&vs->content_type);
- vs->changed = 1;
- }
- }
- /* duplicates documents if changed */
- static int set_vs_document_dup(virtual_subscription_t *vs,
- str_t *new_doc,
- str_t *new_content_type)
- {
- if (str_case_equals(&vs->state_document, new_doc) == 0) {
- /* DEBUG("new document is equal to the older one\n"); */
- }
- else {
- str_free_content(&vs->state_document);
- str_dup(&vs->state_document, new_doc);
- vs->changed = 1;
- }
-
- if (str_case_equals(&vs->content_type, new_content_type) == 0) {
- /* DEBUG("new content-type is equal to the older one\n"); */
- }
- else {
- str_free_content(&vs->content_type);
- str_dup(&vs->content_type, new_content_type);
- vs->changed = 1;
- }
- return 0;
- }
- static void propagate_change(virtual_subscription_t *vs)
- {
- if (vs->subscription->type == rls_internal_subscription) {
- /* propagate change to higher level */
- rls_generate_notify(vs->subscription, 1);
- }
- else {
- /* external subscriptions will send NOTIFY only sometimes
- * => we mark it as changed now */
- vs->subscription->changed++;
- /* FIXME: put this subscription in some queue? (needs remove from it
- * when freeing!) */
- if (rls) rls->changed_subscriptions++; /* change indicator */
- }
- }
- void process_rls_notification(virtual_subscription_t *vs, client_notify_info_t *info)
- {
- presentity_info_t *pinfo;
- raw_presence_info_t *raw;
- str_t new_doc = STR_NULL;
- str_t new_type = STR_NULL;
- subscription_status_t old_status;
-
- if ((!vs) || (!info)) return;
-
- DBG("Processing notification for VS %p\n", vs);
- /* FIXME: put information from more sources together ? */
- old_status = vs->status;
- switch (info->status) {
- case qsa_subscription_active:
- vs->status = subscription_active;
- break;
- case qsa_subscription_pending:
- vs->status = subscription_pending;
- break;
- case qsa_subscription_rejected:
- vs->status = subscription_terminated;
- break;
- case qsa_subscription_terminated:
- vs->status = subscription_terminated;
- break;
- }
- if (old_status != vs->status) vs->changed = 1;
- if (info->content_type == vsd->ct_raw) {
- DEBUG("Processing raw notification\n");
-
- raw = (raw_presence_info_t*)info->data;
- if (!raw) return;
-
- /* document MUST be duplicated !!! */
- if (set_vs_document_dup(vs, &raw->pres_doc, &raw->content_type) < 0) {
- ERR("can't set new status document for VS %p\n", vs);
- return;
- }
- }
- else {
- if (info->content_type == vsd->ct_presence_info) {
- DEBUG("Processing structured notification\n");
-
- pinfo = (presentity_info_t*)info->data;
- if (!pinfo) {
- str_clear(&new_doc);
- str_clear(&new_type);
- }
- else {
- if (create_pidf_document(pinfo, &new_doc, &new_type) < 0) {
- ERR("can't create PIDF document\n");
- str_free_content(&vs->state_document);
- str_free_content(&vs->content_type);
- return;
- }
- set_vs_document(vs, &new_doc, &new_type);
- }
- }
- else {
- if (info->content_type)
- ERR("received unacceptable notification (%.*s)\n",
- FMT_STR(info->content_type->name));
- else ERR("received unacceptable notification without content type\n");
- str_free_content(&vs->state_document);
- str_free_content(&vs->content_type);
- return;
- }
- }
-
- if (vs->changed) propagate_change(vs);
- }
- void process_internal_notify(virtual_subscription_t *vs,
- str_t *new_state_document,
- str_t *new_content_type)
- {
- if (!vs) return;
-
- DBG("Processing internal notification for VS %p\n", vs);
-
- /* don't copy document - use it directly */
- set_vs_document(vs, new_state_document, new_content_type);
- if (vs->changed) propagate_change(vs);
- }
- #if 0
- static void mark_as_modified(virtual_subscription_t *vs)
- {
- rl_subscription_t *rls = vs->subscription;
- switch (rls->type) {
- case rls_external_subscription:
- if (sm_subscription_pending(&rls->u.external) == 0) {
- /* pending subscription will not be notified */
- return;
- }
- break;
- case rls_internal_subscription:
- /* FIXME: something like above? */
- break;
- }
-
- /* NOTIFY should be send only for nonpending subscriptions (or active?)*/
- vs->subscription->changed++;
- DEBUG_LOG("RL subscription status changed (%p, %d)\n",
- rls, rls->changed);
- }
- static void vs_timer_cb(unsigned int ticks, void *param)
- {
- virtual_subscription_t *vs;
- int changed = 0;
- int cntr = 0;
- time_t start, stop;
- start = time(NULL);
- rls_lock();
- /* process all messages for virtual subscriptions */
- vs = vsd->first;
- while (vs) {
- if (process_vs_messages(vs) > 0) {
- DEBUG_LOG("VS status changed\n");
- mark_as_modified(vs);
- changed = 1;
- }
- vs = vs->next;
- cntr++; /* debugging purposes */
- }
- /* TRACE_LOG("processed messages for %d virtual subscription(s)\n", cntr); */
-
- if (changed) {
- /* this could be called from some rli_timer ? */
- notify_all_modified();
- }
-
- rls_unlock();
- stop = time(NULL);
- if (stop - start > 1) WARN("vs_timer_cb took %d secs\n", (int) (stop - start));
- }
- #endif
- static int add_to_vs_list(virtual_subscription_t *vs)
- {
- if (!vs) return RES_INTERNAL_ERR;
- if (!vsd) {
- LOG(L_ERR, "vs_add(): vsd not set!\n");
- return RES_INTERNAL_ERR;
- }
- DOUBLE_LINKED_LIST_ADD(vsd->first, vsd->last, vs);
- return RES_OK;
- }
- static int remove_from_vs_list(virtual_subscription_t *vs)
- {
- if (!vs) return RES_INTERNAL_ERR;
- if (!vsd) {
- LOG(L_ERR, "vs_remove(): vsd not set!\n");
- return RES_INTERNAL_ERR;
- }
- DOUBLE_LINKED_LIST_REMOVE(vsd->first, vsd->last, vs);
-
- return RES_OK;
- }
- int xcap_query_rls_services(xcap_query_params_t *xcap_params,
- const str *uri, const str *package,
- flat_list_t **dst)
- {
- if (dst) *dst = NULL;
-
- if (reduce_xcap_needs)
- return get_rls_from_full_doc(uri, xcap_params, package, dst);
- else
- return get_rls(uri, xcap_params, package, dst);
- }
- static int create_subscriptions(virtual_subscription_t *vs, int nesting_level)
- {
- /* create concrete local subscription */
- str *package = NULL;
- str *subscriber = NULL;
- flat_list_t *flat = NULL;
- package = rls_get_package(vs->subscription);
- DEBUG_LOG("creating local subscription to %.*s\n", FMT_STR(vs->uri));
- if ((nesting_level != 0) &&
- (xcap_query_rls_services(&vs->subscription->xcap_params,
- &vs->uri, package, &flat) == 0)) {
- if (nesting_level > 0) nesting_level--;
- /* it is resource list -> do internal subscription to RLS */
- if (rls_create_internal_subscription(vs,
- &vs->local_subscription_list, flat,
- nesting_level) != 0) {
- ERR("can't create internal subscription\n");
- free_flat_list(flat);
- return -1;
- }
- free_flat_list(flat);
-
- vs->status = subscription_active;
- /* FIXME: rls_authorize_subscription(vs->local_subscription_list); */
- }
- else {
- /* fill QSA subscription data */
- clear_subscription_data(&vs->local_subscription_pres_data);
- vs->local_subscription_pres_data.dst = &rls->notify_mq;
- vs->local_subscription_pres_data.record_id = vs->uri;
- subscriber = rls_get_subscriber(vs->subscription);
- vs->local_subscription_pres_data.subscriber_data = vs;
- if (subscriber)
- vs->local_subscription_pres_data.subscriber_id = *subscriber;
- /* not RLS record -> do QSA subscription to given package */
- vs->local_subscription_pres = subscribe(vsd->domain,
- package, &vs->local_subscription_pres_data);
- if (!vs->local_subscription_pres) {
- LOG(L_ERR, "can't create local subscription (pres)!\n");
- return -1;
- }
- }
-
- return 0;
- }
- /******** VS manipulation ********/
- int vs_create(str *uri,
- virtual_subscription_t **dst,
- display_name_t *dnames,
- rl_subscription_t *subscription,
- int nesting_level)
- {
- int res;
- display_name_t *d;
- if (!dst) return RES_INTERNAL_ERR;
- *dst = NULL;
- if (!uri) {
- LOG(L_ERR, "vs_create(): no uri given\n");
- return RES_INTERNAL_ERR;
- }
- if ((!uri->s) || (uri->len < 1)) {
- LOG(L_ERR, "vs_create(): no uri given\n");
- return RES_INTERNAL_ERR;
- }
-
- *dst = (virtual_subscription_t*)mem_alloc(sizeof(virtual_subscription_t) + uri->len + 1);
- if (!(*dst)) {
- LOG(L_ERR, "vs_create(): can't allocate memory\n");
- return RES_MEMORY_ERR;
- }
- (*dst)->next = NULL;
- (*dst)->prev = NULL;
- vector_init(&(*dst)->display_names, sizeof(vs_display_name_t), 4);
- memcpy((*dst)->uri_str, uri->s, uri->len);
- (*dst)->uri.s = (*dst)->uri_str;
- (*dst)->uri.len = uri->len;
- (*dst)->state_document.len = 0;
- (*dst)->state_document.s = NULL;
- (*dst)->content_type.len = 0;
- (*dst)->content_type.s = NULL;
- (*dst)->status = subscription_pending;
- (*dst)->local_subscription_pres = NULL;
- (*dst)->local_subscription_list = NULL;
- (*dst)->subscription = subscription;
- (*dst)->changed = 0;
- generate_db_id(&(*dst)->dbid, *dst);
- add_to_vs_list(*dst);
- DBG("created VS %p to %.*s\n", *dst, uri->len, uri->s);
-
- res = create_subscriptions(*dst, nesting_level);
- if (res != 0) {
- vs_free(*dst);
- return res;
- }
-
- /* TODO: remember the list of Accept headers from client subscribe
- * it will be used for Back-End subscriptions */
-
- /* add names */
- if (dnames) {
- d = SEQUENCE_FIRST(dnames);
- while (d) {
- vs_add_display_name((*dst), d->name, d->lang);
- d = SEQUENCE_NEXT(d);
- }
- }
- return RES_OK;
- }
- int vs_add_display_name(virtual_subscription_t *vs, const char *name, const char *lang)
- {
- vs_display_name_t dn;
- if (name) {
- dn.name.len = strlen(name);
- if (dn.name.len > 0) {
- dn.name.s = (char *)mem_alloc(dn.name.len);
- if (!dn.name.s) dn.name.len = 0;
- else memcpy(dn.name.s, name, dn.name.len);
- }
- }
- else {
- dn.name.len = 0;
- dn.name.s = NULL;
- }
-
- if (lang) {
- dn.lang.len = strlen(lang);
- if (dn.lang.len > 0) {
- dn.lang.s = (char *)mem_alloc(dn.lang.len);
- if (!dn.lang.s) dn.lang.len = 0;
- else memcpy(dn.lang.s, lang, dn.lang.len);
- }
- }
- else {
- dn.lang.len = 0;
- dn.lang.s = NULL;
- }
- /* TRACE_LOG("adding display name: %s\n", name);*/
- return vector_add(&vs->display_names, &dn);
- }
- void vs_free(virtual_subscription_t *vs)
- {
- int i, cnt;
- vs_display_name_t dn;
-
- if (vs) {
- if (vs->local_subscription_pres)
- unsubscribe(vsd->domain, vs->local_subscription_pres);
- if (vs->local_subscription_list)
- rls_remove(vs->local_subscription_list);
-
- /* remove notification messages for given subscription */
- destroy_notifications(vs->local_subscription_pres);
-
- remove_from_vs_list(vs);
-
- str_free_content(&vs->state_document);
- str_free_content(&vs->content_type);
- /* if ( (vs->package.len > 0) && (vs->package.s) )
- mem_free(vs->package.s); */
-
- cnt = vector_size(&vs->display_names);
- for (i = 0; i < cnt; i++) {
- if (vector_get(&vs->display_names, i, &dn) != 0) continue;
- if (dn.name.s && (dn.name.len > 0)) mem_free(dn.name.s);
- if (dn.lang.s && (dn.lang.len > 0)) mem_free(dn.lang.s);
- }
- vector_destroy(&vs->display_names);
- mem_free(vs);
- /* LOG(L_TRACE, "Virtual Subscription freed\n");*/
- }
- }
|