#include "qsa_interface.h" #include "pdomain.h" #include "pa_mod.h" #include "dlist.h" #include "auth.h" #include #include #include #include #include #include static notifier_domain_t *domain = NULL; static notifier_t *notifier = NULL; static qsa_content_type_t *ct_presence_info = NULL; /*static qsa_content_type_t *ct_pidf_xml = NULL;*/ /* static str_t notifier_name = { s: "pa", len: 2 }; */ static int pa_subscribe(notifier_t *n, qsa_subscription_t *subscription); static void pa_unsubscribe(notifier_t *n, qsa_subscription_t *subscription); extern dlist_t* root; /* ugly !!!!! */ int accept_internal_subscriptions = 0; /* QSA interface initialization */ int pa_qsa_interface_init() { static str presence_info = STR_STATIC_INIT(CT_PRESENCE_INFO); static str_t presence_package = { s: "presence", len: 8 }; domain = qsa_get_default_domain(); if (!domain) { ERR("can't register notifier domain\n"); return -1; } /* DBG("QSA (pa) domain: %p\n", domain); */ notifier = register_notifier(domain, &presence_package, pa_subscribe, pa_unsubscribe, NULL); if (!notifier) { ERR("can't register notifier\n"); return -1; } ct_presence_info = register_content_type(domain, &presence_info, (destroy_function_f)free_presentity_info); if (!ct_presence_info) { ERR("can't register QSA content type\n"); return -1; } else TRACE("PA_CONTENT_TYPE: %p\n", ct_presence_info); /* DBG("pa_qsa_interface_init(): created notifier %.*s\n", FMT_STR(notifier_name)); */ return 0; } void pa_qsa_interface_destroy() { if (domain && notifier) unregister_notifier(domain, notifier); notifier = NULL; /* no new qsa subscriptions will be created now - now can be * released all existing ones */ if (domain) qsa_release_domain(domain); /* no QSA operations should be done there (don't send * notification messages, ...) only subscriptions may * be released */ domain = NULL; } /* notifier functions */ static int add_internal_subscription(presentity_t *p, internal_pa_subscription_t *is) { if (is->status != WS_ACTIVE) is->status = authorize_internal_watcher(p, is); if (is->status == WS_REJECTED) return -1; DOUBLE_LINKED_LIST_ADD(p->first_qsa_subscription, p->last_qsa_subscription, is); return 0; } internal_pa_subscription_t *create_internal_subscription(qsa_subscription_t *s) { internal_pa_subscription_t *ss = cds_malloc(sizeof(internal_pa_subscription_t)); if (!ss) return ss; ss->subscription = s; ss->status = WS_PENDING; ss->prev = NULL; ss->next = NULL; return ss; } void free_internal_subscription(internal_pa_subscription_t *is) { if (is) cds_free(is); } static int pa_subscribe(notifier_t *n, qsa_subscription_t *subscription) { dlist_t *dl; presentity_t *p = NULL; internal_pa_subscription_t *ss; str uid = STR_NULL; str *record_id = NULL; xcap_query_params_t xcap_params; if (!accept_internal_subscriptions) return 0; /* do not accept subscriptions */ record_id = get_record_id(subscription); if (!record_id) { ERR("BUG: subscription to empty record\n"); return -1; } DBG("SUBSCRIBE to PA for %.*s [%.*s]\n", FMT_STR(*record_id), FMT_STR(subscription->package->name)); if (pres_uri2uid(&uid, record_id) != 0) { /* can't convert uri to uid */ INFO("can't convert URI to UID for internal PA subscription\n"); return -1; } /* DBG("SUBSCRIBE to uid: %.*s\n", FMT_STR(uid)); */ dl = root; /* FIXME: ugly and possibly unsafe (locking needed?) */ while (dl) { /* create new server subscription */ ss = create_internal_subscription(subscription); if (!ss) { ERROR_LOG("can't allocate memory for internal pa subscription\n"); break; } lock_pdomain(dl->d); if (find_presentity_uid(dl->d, &uid, &p) != 0) p = NULL; if (!p) { memset(&xcap_params, 0, sizeof(xcap_params)); if (fill_xcap_params) fill_xcap_params(NULL, &xcap_params); if (new_presentity(dl->d, record_id, &uid, &xcap_params, &p) < 0) { ERR("can't create presentity\n"); } } if (p) { /* add server subscription to p */ if (add_internal_subscription(p, ss) == 0) { p->flags |= PFLAG_WATCHERINFO_CHANGED; notify_internal_watcher(p, ss); } else { /* error adding subscription to p (auth failed, ...) */ free_internal_subscription(ss); } } unlock_pdomain(dl->d); dl = dl->next; } str_free_content(&uid); DBG("finished SUBSCRIBE to PA for %.*s [%.*s]\n", FMT_STR(*record_id), FMT_STR(subscription->package->name)); return 0; } static void remove_internal_subscription(presentity_t *p, qsa_subscription_t *s) { internal_pa_subscription_t *ss; ss = p->first_qsa_subscription; while (ss) { if (s == ss->subscription) { DOUBLE_LINKED_LIST_REMOVE(p->first_qsa_subscription, p->last_qsa_subscription, ss); free_internal_subscription(ss); break; /* may be only once */ } ss = ss->next; } } static void pa_unsubscribe(notifier_t *n, qsa_subscription_t *subscription) { dlist_t *dl; presentity_t *p = NULL; str uid = STR_NULL; str *record_id = NULL; if (!accept_internal_subscriptions) return; /* do not accept subscriptions */ record_id = get_record_id(subscription); if (!record_id) { ERR("BUG: unsubscription to empty record\n"); return; } if (pres_uri2uid(&uid, record_id) != 0) { /* can't convert uri to uid */ ERR("can't convert URI to UID for internal PA unsubscription\n"); return; } /* DBG("UNBSCRIBE from PA for %.*s [%.*s]\n", FMT_STR(subscription->record_id), FMT_STR(subscription->package->name)); */ dl = root; /* FIXME: ugly and possibly unsafe (locking needed?) */ while (dl) { lock_pdomain(dl->d); if (find_presentity_uid(dl->d, &uid, &p) != 0) p = NULL; if (!p) continue; remove_internal_subscription(p, subscription); p->flags |= PFLAG_WATCHERINFO_CHANGED; unlock_pdomain(dl->d); dl = dl->next; } str_free_content(&uid); } int notify_internal_watcher(presentity_t *p, internal_pa_subscription_t *ss) { presentity_info_t *pinfo; /* notify only accepted watchers */ switch (ss->status) { case WS_PENDING: return notify_subscriber(ss->subscription, notifier, ct_presence_info, NULL, qsa_subscription_pending); case WS_REJECTED: return notify_subscriber(ss->subscription, notifier, ct_presence_info, NULL, qsa_subscription_rejected); case WS_PENDING_TERMINATED: case WS_TERMINATED: return notify_subscriber(ss->subscription, notifier, ct_presence_info, NULL, qsa_subscription_terminated); case WS_ACTIVE: pinfo = dup_presentity_info(&p->data); if (!pinfo) { ERROR_LOG("can't create presentity info from presentity!\n"); return -1; } return notify_subscriber(ss->subscription, notifier, ct_presence_info, pinfo, qsa_subscription_active); } return 0; } int notify_qsa_watchers(presentity_t *p) { internal_pa_subscription_t *ss; int res = 0; /* DBG("notify_qsa_watchers for %.*s\n", FMT_STR(p->uri)); */ ss = p->first_qsa_subscription; while (ss) { if (notify_internal_watcher(p, ss) < 0) res = -1; ss = ss->next; } return res; } int subscribe_to_user(presentity_t *_p) { static str package = STR_STATIC_INIT("presence"); clear_subscription_data(&_p->presence_subscription_data); /* ??? FIXME msg queue */ _p->presence_subscription_data.dst = &_p->mq; _p->presence_subscription_data.record_id = _p->data.uri; _p->presence_subscription_data.subscriber_id = pa_subscription_uri; _p->presence_subscription_data.subscriber_data = _p; _p->presence_subscription = subscribe(domain, &package, &_p->presence_subscription_data); if (_p->presence_subscription) return 0; else return -1; } int unsubscribe_to_user(presentity_t *_p) { unsubscribe(domain, _p->presence_subscription); /* TODO ? clean messages ? they will be freed automaticaly ... */ return 0; }