|
@@ -0,0 +1,1305 @@
|
|
|
+/*
|
|
|
+ * $Id$
|
|
|
+ *
|
|
|
+ * Copyright (C) 2012 Smile Communications, [email protected]
|
|
|
+ * Copyright (C) 2012 Smile Communications, [email protected]
|
|
|
+ *
|
|
|
+ * The initial version of this code was written by Dragos Vingarzan
|
|
|
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
|
|
|
+ * Fruanhofer Institute. It was and still is maintained in a separate
|
|
|
+ * branch of the original SER. We are therefore migrating it to
|
|
|
+ * Kamailio/SR and look forward to maintaining it from here on out.
|
|
|
+ * 2011/2012 Smile Communications, Pty. Ltd.
|
|
|
+ * ported/maintained/improved by
|
|
|
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
|
|
|
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
|
|
|
+ * effort to add full IMS support to Kamailio/SR using a new and
|
|
|
+ * improved architecture
|
|
|
+ *
|
|
|
+ * NB: Alot of this code was originally part of OpenIMSCore,
|
|
|
+ * FhG Fokus.
|
|
|
+ * Copyright (C) 2004-2006 FhG Fokus
|
|
|
+ * Thanks for great work! This is an effort to
|
|
|
+ * break apart the various CSCF functions into logically separate
|
|
|
+ * components. We hope this will drive wider use. We also feel
|
|
|
+ * that in this way the architecture is more complete and thereby easier
|
|
|
+ * to manage in the Kamailio/SR environment
|
|
|
+ *
|
|
|
+ * This file is part of Kamailio, a free SIP server.
|
|
|
+ *
|
|
|
+ * Kamailio is free software; you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
|
+ * (at your option) any later version
|
|
|
+ *
|
|
|
+ * Kamailio is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program; if not, write to the Free Software
|
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+#include "registrar_notify.h"
|
|
|
+
|
|
|
+#include "reg_mod.h"
|
|
|
+#include "../../lib/ims/ims_getters.h"
|
|
|
+#include "regtime.h"
|
|
|
+#include "usrloc_cb.h"
|
|
|
+
|
|
|
+#include "../../lib/ims/useful_defs.h"
|
|
|
+
|
|
|
+/**
|
|
|
+ * Initializes the reg notifications list.
|
|
|
+ */
|
|
|
+reg_notification_list *notification_list = 0; //< List of pending notifications
|
|
|
+
|
|
|
+extern struct tm_binds tmb;
|
|
|
+
|
|
|
+extern int subscription_default_expires;
|
|
|
+extern int subscription_min_expires;
|
|
|
+extern int subscription_max_expires;
|
|
|
+
|
|
|
+extern str scscf_name_str;
|
|
|
+
|
|
|
+static str event_hdr = {"Event: reg\r\n", 12};
|
|
|
+static str maxfwds_hdr = {"Max-Forwards: 70\r\n", 18};
|
|
|
+static str subss_hdr1 = {"Subscription-State: ", 20};
|
|
|
+static str subss_hdr2 = {"\r\n", 2};
|
|
|
+static str ctype_hdr1 = {"Content-Type: ", 14};
|
|
|
+static str ctype_hdr2 = {"\r\n", 2};
|
|
|
+
|
|
|
+int notify_init() {
|
|
|
+ notification_list = shm_malloc(sizeof (reg_notification_list));
|
|
|
+ if (!notification_list) return 0;
|
|
|
+ memset(notification_list, 0, sizeof (reg_notification_list));
|
|
|
+ notification_list->lock = lock_alloc();
|
|
|
+ if (!notification_list->lock) return 0;
|
|
|
+ notification_list->lock = lock_init(notification_list->lock);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Destroys the reg notifications list.
|
|
|
+ */
|
|
|
+void notify_destroy() {
|
|
|
+ reg_notification *n, *nn;
|
|
|
+ lock_get(notification_list->lock);
|
|
|
+ n = notification_list->head;
|
|
|
+ while (n) {
|
|
|
+ nn = n->next;
|
|
|
+ free_notification(n);
|
|
|
+ n = nn;
|
|
|
+ }
|
|
|
+ lock_destroy(notification_list->lock);
|
|
|
+ lock_dealloc(notification_list->lock);
|
|
|
+ shm_free(notification_list);
|
|
|
+}
|
|
|
+
|
|
|
+int can_subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
|
|
|
+
|
|
|
+ int ret = CSCF_RETURN_FALSE;
|
|
|
+ str presentity_uri = {0, 0};
|
|
|
+ str event;
|
|
|
+ str asserted_id;
|
|
|
+
|
|
|
+ ucontact_t* c = 0;
|
|
|
+ impurecord_t* r;
|
|
|
+ int res;
|
|
|
+
|
|
|
+ ims_public_identity *pi = 0;
|
|
|
+ int i, j;
|
|
|
+
|
|
|
+ LM_DBG("Checking if allowed to subscribe to event\n");
|
|
|
+
|
|
|
+ //check that this is a request
|
|
|
+ if (msg->first_line.type != SIP_REQUEST) {
|
|
|
+ LM_ERR("This message is not a request\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ //check that this is a subscribe request
|
|
|
+ if (msg->first_line.u.request.method.len != 9 ||
|
|
|
+ memcmp(msg->first_line.u.request.method.s, "SUBSCRIBE", 9) != 0) {
|
|
|
+ LM_ERR("This message is not a SUBSCRIBE\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ //check that this is a reg event - currently we only support reg event!
|
|
|
+ event = cscf_get_event(msg);
|
|
|
+ if (event.len != 3 || strncasecmp(event.s, "reg", 3) != 0) {
|
|
|
+ LM_ERR("Accepting only <Event: reg>. Found: <%.*s>\n",
|
|
|
+ event.len, event.s);
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ //get the target/presentity URI from the request uri
|
|
|
+ presentity_uri = cscf_get_public_identity_from_requri(msg);
|
|
|
+
|
|
|
+
|
|
|
+ asserted_id = cscf_get_asserted_identity(msg);
|
|
|
+ if (!asserted_id.len) {
|
|
|
+ LM_ERR("P-Asserted-Identity empty.\n");
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ LM_DBG("P-Asserted-Identity <%.*s>.\n",
|
|
|
+ asserted_id.len, asserted_id.s);
|
|
|
+
|
|
|
+ LM_DBG("Looking for IMPU in usrloc <%.*s>\n", presentity_uri.len, presentity_uri.s);
|
|
|
+
|
|
|
+ ul.lock_udomain((udomain_t*) _t, &presentity_uri);
|
|
|
+ res = ul.get_impurecord((udomain_t*) _t, &presentity_uri, &r);
|
|
|
+
|
|
|
+ if (res > 0) {
|
|
|
+ LM_DBG("'%.*s' Not found in usrloc\n", presentity_uri.len, presentity_uri.s);
|
|
|
+ ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ //check if the asserted identity is in the same group as that presentity uri
|
|
|
+ if (r->public_identity.len == asserted_id.len &&
|
|
|
+ strncasecmp(r->public_identity.s, asserted_id.s, asserted_id.len) == 0) {
|
|
|
+ LM_DBG("Identity found as AOR <%.*s>\n",
|
|
|
+ presentity_uri.len, presentity_uri.s);
|
|
|
+ ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
|
|
|
+ ret = CSCF_RETURN_TRUE;
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ //check if asserted identity is in service profile
|
|
|
+ lock_get(r->s->lock);
|
|
|
+ if (r->s) {
|
|
|
+ for (i = 0; i < r->s->service_profiles_cnt; i++)
|
|
|
+ for (j = 0; j < r->s->service_profiles[i].public_identities_cnt; j++) {
|
|
|
+ pi = &(r->s->service_profiles[i].public_identities[j]);
|
|
|
+ if (!pi->barring &&
|
|
|
+ pi->public_identity.len == asserted_id.len &&
|
|
|
+ strncasecmp(pi->public_identity.s, asserted_id.s, asserted_id.len) == 0) {
|
|
|
+ LM_DBG("Identity found in SP[%d][%d]\n",
|
|
|
+ i, j);
|
|
|
+ ret = CSCF_RETURN_TRUE;
|
|
|
+ ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
|
|
|
+ lock_release(r->s->lock);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lock_release(r->s->lock);
|
|
|
+
|
|
|
+ //check if asserted is present in any of the path headers
|
|
|
+ c = r->contacts;
|
|
|
+
|
|
|
+ while (c) {
|
|
|
+ if (c->path.len) {
|
|
|
+ for (i = 0; i < c->path.len - asserted_id.len; i++)
|
|
|
+ if (strncasecmp(c->path.s + i, asserted_id.s, asserted_id.len) == 0) {
|
|
|
+ LM_DBG("Identity found in Path <%.*s>\n",
|
|
|
+ c->path.len, c->path.s);
|
|
|
+ ret = CSCF_RETURN_TRUE;
|
|
|
+ ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ c = c->next;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+done:
|
|
|
+ if (presentity_uri.s) shm_free(presentity_uri.s);
|
|
|
+ return ret;
|
|
|
+error:
|
|
|
+ if (presentity_uri.s) shm_free(presentity_uri.s);
|
|
|
+ ret = CSCF_RETURN_ERROR;
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * called to deliver new event into notification process
|
|
|
+ * return 0 on success. anything else failure
|
|
|
+ */
|
|
|
+int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int event_type, str *presentity_uri, str *watcher_contact) {
|
|
|
+
|
|
|
+ str content = {0, 0};
|
|
|
+ impurecord_t* r;
|
|
|
+ int num_impus;
|
|
|
+ str* impu_list;
|
|
|
+
|
|
|
+ LM_DBG("Sending Reg event notifies\n");
|
|
|
+ LM_DBG("Switching on event type: %d", event_type);
|
|
|
+ switch (event_type) {
|
|
|
+ case IMS_REGISTRAR_NONE:
|
|
|
+ return 0;
|
|
|
+ case IMS_REGISTRAR_SUBSCRIBE:
|
|
|
+ if (r_passed || c_passed || !presentity_uri || !watcher_contact || !_d) {
|
|
|
+ LM_ERR("this is a subscribe called from cfg file: r_passed and c_passed should both be zero and presentity_uri, watcher_contact and _d should be valid for a subscribe");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ LM_DBG("Event type is IMS REGISTRAR SUBSCRIBE about to get reginfo_full");
|
|
|
+ //lets get IMPU list for presentity as well as register for callbacks (IFF its a new SUBSCRIBE)
|
|
|
+
|
|
|
+ ul.lock_udomain(_d, presentity_uri);
|
|
|
+ int res = ul.get_impurecord(_d, presentity_uri, &r);
|
|
|
+ if (res != 0) {
|
|
|
+ LM_WARN("Strange, '%.*s' Not found in usrloc\n", presentity_uri->len, presentity_uri->s);
|
|
|
+ ul.unlock_udomain(_d, presentity_uri);
|
|
|
+ //no point in continuing
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ //get IMPU set from the presentity's subscription
|
|
|
+ res = ul.get_impus_from_subscription_as_string(_d, r,
|
|
|
+ 0/*all unbarred impus*/, &impu_list, &num_impus);
|
|
|
+ if (res != 0) {
|
|
|
+ LM_WARN("failed to get IMPUs from subscription\n");
|
|
|
+ ul.unlock_udomain(_d, presentity_uri);
|
|
|
+ if (impu_list) {
|
|
|
+ pkg_free(impu_list);
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ ul.unlock_udomain((udomain_t*) _d, presentity_uri);
|
|
|
+
|
|
|
+ content = generate_reginfo_full(_d, impu_list,
|
|
|
+ num_impus);
|
|
|
+
|
|
|
+ if (impu_list) {
|
|
|
+ pkg_free(impu_list);
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("About to ceate notification");
|
|
|
+
|
|
|
+ create_notifications(_d, r_passed, c_passed, presentity_uri, watcher_contact, content, event_type);
|
|
|
+ if (content.s) pkg_free(content.s);
|
|
|
+ // if (send_now) notification_timer(0, 0);
|
|
|
+ return 0;
|
|
|
+ break;
|
|
|
+
|
|
|
+ //richard: we only use reg unreg expired and refresh
|
|
|
+ case IMS_REGISTRAR_CONTACT_UNREGISTERED:
|
|
|
+ case IMS_REGISTRAR_CONTACT_REGISTERED:
|
|
|
+ case IMS_REGISTRAR_CONTACT_REFRESHED:
|
|
|
+ case IMS_REGISTRAR_CONTACT_EXPIRED:
|
|
|
+ if (!r_passed || !c_passed || presentity_uri || watcher_contact || _d) {
|
|
|
+ LM_ERR("this is a contact change passed from ul callback: r_passed and c_passed should both be valid and presentity_uri, watcher_contact and _d should be 0 for ul callback");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ content = get_reginfo_partial(r_passed, c_passed, event_type);
|
|
|
+ create_notifications(_d, r_passed, c_passed, presentity_uri, watcher_contact, content, event_type);
|
|
|
+ if (content.s) pkg_free(content.s);
|
|
|
+ // if (send_now) notification_timer(0, 0);
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ default:
|
|
|
+ LM_ERR("ERR:event_reg: Unknown event %d\n", event_type);
|
|
|
+ // if (send_now) notification_timer(0, 0);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Save this subscription.
|
|
|
+ * @param msg - the SIP SUBSCRIBE message
|
|
|
+ * @param str1 - not used
|
|
|
+ * @param str2 - not used
|
|
|
+ * @returns #CSCF_RETURN_TRUE if allowed, #CSCF_RETURN_FALSE if not, #CSCF_RETURN_ERROR on error
|
|
|
+ */
|
|
|
+int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
|
|
|
+ int ret = CSCF_RETURN_FALSE;
|
|
|
+ int res;
|
|
|
+ str presentity_uri = {0, 0};
|
|
|
+ str event;
|
|
|
+ int event_i = IMS_EVENT_NONE;
|
|
|
+ int expires = 0, expires_time = 0;
|
|
|
+ str watcher_impu;
|
|
|
+ str watcher_contact;
|
|
|
+ impurecord_t* presentity_impurecord;
|
|
|
+ reg_subscriber *reg_subscriber;
|
|
|
+ subscriber_data_t subscriber_data;
|
|
|
+
|
|
|
+ int new_subscription = 1;
|
|
|
+ int event_type = IMS_REGISTRAR_NONE;
|
|
|
+
|
|
|
+ int rt = 0;
|
|
|
+
|
|
|
+ str callid = {0, 0};
|
|
|
+ str ftag = {0, 0};
|
|
|
+ str ttag = {0, 0};
|
|
|
+ str record_route = {0, 0};
|
|
|
+
|
|
|
+ int remote_cseq = 0;
|
|
|
+ int local_cseq = 0;
|
|
|
+
|
|
|
+ udomain_t* domain = (udomain_t*) _t;
|
|
|
+
|
|
|
+ LM_DBG("Saving SUBSCRIBE\n");
|
|
|
+
|
|
|
+ //check we have a valid transaction, if not, create one TODO
|
|
|
+
|
|
|
+ //check that this is a request
|
|
|
+ if (msg->first_line.type != SIP_REQUEST) {
|
|
|
+ LM_ERR("This message is not a request\n");
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ //check that this is a subscribe
|
|
|
+ if (msg->first_line.u.request.method.len != 9 ||
|
|
|
+ memcmp(msg->first_line.u.request.method.s, "SUBSCRIBE", 9) != 0) {
|
|
|
+ LM_ERR("This message is not a SUBSCRIBE\n");
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ //check that this is a reg event - we currently only support reg event
|
|
|
+ event = cscf_get_event(msg);
|
|
|
+ if (event.len != 3 || strncasecmp(event.s, "reg", 3) != 0) {
|
|
|
+ LM_WARN("Accepting only <Event: reg>. Found: <%.*s>\n",
|
|
|
+ event.len, event.s);
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ if (event.len == 0 && strncasecmp(event.s, "reg", 3) == 0)
|
|
|
+ event_i = IMS_EVENT_REG;
|
|
|
+
|
|
|
+ //get callid, from and to tags to be able to identify dialog
|
|
|
+ //callid
|
|
|
+ callid = cscf_get_call_id(msg, 0);
|
|
|
+ if (callid.len <= 0 || !callid.s) {
|
|
|
+ LM_ERR("unable to get callid\n");
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ //ftag
|
|
|
+ if (!cscf_get_from_tag(msg, &ftag)) {
|
|
|
+ LM_ERR("Unable to get ftag\n");
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ //to tag - doesn't exist in request, must use tm to get it
|
|
|
+ tmb.t_get_reply_totag(msg, &ttag);
|
|
|
+ LM_DBG("Got to tag from sent response: %.*s", ttag.len, ttag.s);
|
|
|
+
|
|
|
+ //get cseq
|
|
|
+ remote_cseq = cscf_get_cseq(msg, 0);
|
|
|
+ local_cseq = remote_cseq + 1;
|
|
|
+
|
|
|
+ //get sockinfo_str
|
|
|
+ str sockinfo_str = msg->rcv.bind_address->sock_str;
|
|
|
+
|
|
|
+ //get record route
|
|
|
+ /*process record route and add it to a string*/
|
|
|
+ if (msg->record_route != NULL) {
|
|
|
+ rt = print_rr_body(msg->record_route, &record_route, 0, 0);
|
|
|
+ if (rt != 0) {
|
|
|
+ LM_ERR("Failed processing the record route [%d]\n", rt);
|
|
|
+ record_route.s = NULL;
|
|
|
+ record_route.len = 0;
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //get the presentity uri from the request uri
|
|
|
+ presentity_uri = cscf_get_public_identity_from_requri(msg);
|
|
|
+
|
|
|
+ //get the watcher uri from the to header
|
|
|
+ cscf_get_from_uri(msg, &watcher_impu);
|
|
|
+
|
|
|
+ if (!watcher_impu.len) {
|
|
|
+ LM_ERR("Failed to get URI from To header.\n");
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ LM_DBG("To header URI (watcher URI) <%.*s>.\n",
|
|
|
+ watcher_impu.len, watcher_impu.s);
|
|
|
+
|
|
|
+ //get the watcher contact from contact header
|
|
|
+ watcher_contact = cscf_get_contact(msg);
|
|
|
+ if (!watcher_contact.len) {
|
|
|
+ LM_ERR("ERR: Contact empty.\n");
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ LM_DBG("watcher Contact <%.*s>.\n",
|
|
|
+ watcher_contact.len, watcher_contact.s);
|
|
|
+
|
|
|
+ //get expires
|
|
|
+ expires = cscf_get_expires_hdr(msg, 0);
|
|
|
+ if (expires == -1) expires = subscription_default_expires;
|
|
|
+
|
|
|
+ //build subscriber parcel for passing data around more easily
|
|
|
+ subscriber_data.callid = &callid;
|
|
|
+ subscriber_data.event = event_i;
|
|
|
+ subscriber_data.ftag = &ftag;
|
|
|
+ subscriber_data.ttag = &ttag;
|
|
|
+ subscriber_data.record_route = &record_route;
|
|
|
+ subscriber_data.sockinfo_str = &sockinfo_str;
|
|
|
+ subscriber_data.local_cseq = local_cseq;
|
|
|
+
|
|
|
+ if (expires > 0) {
|
|
|
+ LM_DBG("expires is more than zero - SUBSCRIBE");
|
|
|
+ event_type = IMS_REGISTRAR_SUBSCRIBE;
|
|
|
+
|
|
|
+ if (expires < subscription_min_expires) expires = subscription_min_expires;
|
|
|
+ if (expires > subscription_max_expires) expires = subscription_max_expires;
|
|
|
+
|
|
|
+ get_act_time();
|
|
|
+ expires_time = expires + act_time;
|
|
|
+
|
|
|
+ subscriber_data.expires = expires_time;
|
|
|
+
|
|
|
+
|
|
|
+ LM_DBG("Subscription expires time <%d> expiry length <%d>\n",
|
|
|
+ expires_time, expires);
|
|
|
+
|
|
|
+ LM_DBG("Received a new subscription (expires > 0), checking to see of impurecord for presentity exists\n");
|
|
|
+ ul.lock_udomain(domain, &presentity_uri);
|
|
|
+ res = ul.get_impurecord(domain, &presentity_uri, &presentity_impurecord);
|
|
|
+ if (res != 0) {
|
|
|
+ LM_DBG("usrloc does not have imprecord for presnetity being subscribed too, we should create one.... TODO\n");
|
|
|
+ ul.unlock_udomain(domain, &presentity_uri);
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("Received impurecord for presentity being subscribed to [%.*s]\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s);
|
|
|
+
|
|
|
+ res = ul.get_subscriber(presentity_impurecord, &presentity_uri, &watcher_contact, event_i, ®_subscriber);
|
|
|
+ if (res != 0) {
|
|
|
+ LM_DBG("this must be a new subscriber, lets add it\n");
|
|
|
+ res = ul.add_subscriber(presentity_impurecord, &watcher_impu, &watcher_contact, &subscriber_data, ®_subscriber);
|
|
|
+ if (res != 0) {
|
|
|
+ LM_ERR("Failed to add new subscription\n");
|
|
|
+ ul.unlock_udomain(domain, &presentity_uri);
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ //send full update on first registration
|
|
|
+
|
|
|
+ new_subscription = 1;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ LM_DBG("this must be a re subscribe, lets update it\n");
|
|
|
+ res = ul.update_subscriber(presentity_impurecord, &watcher_impu, &watcher_contact, &expires_time, ®_subscriber);
|
|
|
+ if (res != 1) {
|
|
|
+ LM_ERR("Failed to update subscription - expires is %d\n", expires_time);
|
|
|
+ ul.unlock_udomain(domain, &presentity_uri);
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ new_subscription = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ ul.unlock_udomain(domain, &presentity_uri);
|
|
|
+
|
|
|
+ ret = CSCF_RETURN_TRUE;
|
|
|
+ LM_DBG("Sending 200 OK to subscribing user");
|
|
|
+ subscribe_reply(msg, 200, MSG_REG_SUBSCRIBE_OK, &expires, &scscf_name_str);
|
|
|
+
|
|
|
+ //only do reg event on new subscriptions
|
|
|
+ if (new_subscription) {
|
|
|
+ if (event_reg(domain, 0, 0, event_type, &presentity_uri, &watcher_contact) != 0) {
|
|
|
+ LM_ERR("failed to send NOTIFYs for reg events\n");
|
|
|
+ ret = CSCF_RETURN_BREAK;
|
|
|
+ goto error;
|
|
|
+ } else {
|
|
|
+ LM_DBG("success sending NOTIFY\n");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ event_type = IMS_REGISTRAR_UNSUBSCRIBE;
|
|
|
+ LM_DBG("expires is zero or less - UNSUBSCRIBE");
|
|
|
+
|
|
|
+ ul.lock_udomain(domain, &presentity_uri);
|
|
|
+ res = ul.get_impurecord(domain, &presentity_uri, &presentity_impurecord);
|
|
|
+ if (res != 0) {
|
|
|
+ LM_DBG("usrloc does not have imprecord for presnetity being subscribed too, we should create one.... TODO\n");
|
|
|
+ ul.unlock_udomain(domain, &presentity_uri);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ LM_DBG("Received impurecord for presentity being unsubscribed to [%.*s]\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s);
|
|
|
+ // //get the subscription if it exists
|
|
|
+ LM_DBG("Getting subscription s from usrloc");
|
|
|
+
|
|
|
+ res = ul.get_subscriber(presentity_impurecord, &presentity_uri, &watcher_contact, event_i, ®_subscriber);
|
|
|
+ if (res != 0) {
|
|
|
+ LM_WARN("could not get subscriber\n");
|
|
|
+ ret = CSCF_RETURN_FALSE;
|
|
|
+ ul.unlock_udomain(domain, &presentity_uri);
|
|
|
+ goto error;
|
|
|
+ } else {
|
|
|
+ LM_DBG("subscription s exists");
|
|
|
+ LM_DBG("deleting subscriber from usrloc");
|
|
|
+ ul.external_delete_subscriber(reg_subscriber, (udomain_t*) _t);
|
|
|
+ }
|
|
|
+ ret = CSCF_RETURN_TRUE;
|
|
|
+ LM_DBG("Sending 200 OK to subscribing user");
|
|
|
+ subscribe_reply(msg, 200, MSG_REG_UNSUBSCRIBE_OK, &expires, &scscf_name_str);
|
|
|
+ }
|
|
|
+
|
|
|
+ //free memory
|
|
|
+ if (record_route.s) pkg_free(record_route.s);
|
|
|
+ if (presentity_uri.s) shm_free(presentity_uri.s);
|
|
|
+ return ret;
|
|
|
+error:
|
|
|
+ //free memory
|
|
|
+ if (record_route.s) pkg_free(record_route.s);
|
|
|
+ if (presentity_uri.s) shm_free(presentity_uri.s);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+str expires_hdr1 = {"Expires: ", 9};
|
|
|
+str expires_hdr2 = {"\r\n", 2};
|
|
|
+str contact_hdr1 = {"Contact: <", 10};
|
|
|
+str contact_hdr2 = {">\r\n", 3};
|
|
|
+
|
|
|
+/**
|
|
|
+ * Replies to a SUBSCRIBE and also adds the need headers.
|
|
|
+ * Path for example.
|
|
|
+ * @param msg - the SIP SUBSCRIBE message
|
|
|
+ * @param code - response code to send
|
|
|
+ * @param text - response phrase to send
|
|
|
+ * @param expires - expiration interval in seconds
|
|
|
+ * @param contact - contact to add to reply
|
|
|
+ * @returns the tmn.r_reply returned value value
|
|
|
+ */
|
|
|
+int subscribe_reply(struct sip_msg *msg, int code, char *text, int *expires, str * contact) {
|
|
|
+ str hdr = {0, 0};
|
|
|
+
|
|
|
+ if (expires) {
|
|
|
+ hdr.len = expires_hdr1.len + 12 + expires_hdr1.len;
|
|
|
+ hdr.s = pkg_malloc(hdr.len);
|
|
|
+ if (!hdr.s) {
|
|
|
+ LM_ERR("Error allocating %d bytes.\n",
|
|
|
+ hdr.len);
|
|
|
+ } else {
|
|
|
+ hdr.len = 0;
|
|
|
+ STR_APPEND(hdr, expires_hdr1);
|
|
|
+ sprintf(hdr.s + hdr.len, "%d", *expires);
|
|
|
+ hdr.len += strlen(hdr.s + hdr.len);
|
|
|
+ STR_APPEND(hdr, expires_hdr2);
|
|
|
+ cscf_add_header_rpl(msg, &hdr);
|
|
|
+ pkg_free(hdr.s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (contact) {
|
|
|
+ hdr.len = contact_hdr1.len + contact->len + contact_hdr2.len;
|
|
|
+ hdr.s = pkg_malloc(hdr.len);
|
|
|
+ if (!hdr.s) {
|
|
|
+ LM_ERR("Error allocating %d bytes.\n",
|
|
|
+ hdr.len);
|
|
|
+ } else {
|
|
|
+ hdr.len = 0;
|
|
|
+ STR_APPEND(hdr, contact_hdr1);
|
|
|
+ STR_APPEND(hdr, *contact);
|
|
|
+ STR_APPEND(hdr, contact_hdr2);
|
|
|
+ cscf_add_header_rpl(msg, &hdr);
|
|
|
+ pkg_free(hdr.s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return tmb.t_reply(msg, code, text);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+static str subs_terminated = {"terminated", 10};
|
|
|
+static str subs_active = {"active;expires=", 15};
|
|
|
+
|
|
|
+/**
|
|
|
+ * Creates notifications with the given content for all of the subscribers.
|
|
|
+ * @param r - r_public* to which it refers
|
|
|
+ * @param for_s - the r_subscriber* to which it refers or NULL if for all
|
|
|
+ * @param content - the body content
|
|
|
+ * @param expires - the remaining subcription expiration time in seconds
|
|
|
+ */
|
|
|
+void create_notifications(udomain_t* _t, impurecord_t* r_passed, ucontact_t* c_passed, str *presentity_uri, str *watcher_contact, str content, int event_type) {
|
|
|
+
|
|
|
+ reg_notification *n;
|
|
|
+ reg_subscriber *s;
|
|
|
+ impurecord_t* r;
|
|
|
+
|
|
|
+ str subscription_state = {"active;expires=10000000000", 26},
|
|
|
+ content_type = {"application/reginfo+xml", 23};
|
|
|
+
|
|
|
+ int domain_locked = -1;
|
|
|
+
|
|
|
+ get_act_time();
|
|
|
+
|
|
|
+ int res;
|
|
|
+
|
|
|
+ LM_DBG("Creating notification");
|
|
|
+
|
|
|
+ if (r_passed && c_passed && !presentity_uri && !watcher_contact) {
|
|
|
+ LM_DBG("r_passed and c_passed are valid and presentity uri and watcher_contact is 0 - this must be a ul callback no need to lock domain");
|
|
|
+ r = r_passed;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ LM_DBG("This must be a cfg file subscribe need to lock domain and get impurecord");
|
|
|
+ ul.lock_udomain(_t, presentity_uri);
|
|
|
+ res = ul.get_impurecord(_t, presentity_uri, &r);
|
|
|
+ if (res != 0) {
|
|
|
+ LM_WARN("No IMPU... ignoring\n");
|
|
|
+ ul.unlock_udomain(_t, presentity_uri);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ domain_locked = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ s = r->shead;
|
|
|
+ while (s) {
|
|
|
+ LM_DBG("Scrolling through reg subscribers for this IMPU");
|
|
|
+
|
|
|
+ if (s->expires > act_time) {
|
|
|
+ LM_DBG("Expires is greater than current time!");
|
|
|
+ subscription_state.s = pkg_malloc(32);
|
|
|
+ subscription_state.len = 0;
|
|
|
+ if (subscription_state.s) {
|
|
|
+
|
|
|
+ sprintf(subscription_state.s, "%.*s%ld", subs_active.len,
|
|
|
+ subs_active.s, s->expires - act_time);
|
|
|
+ subscription_state.len = strlen(subscription_state.s);
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("Subscription state: [%.*s]", subscription_state.len, subscription_state.s);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ STR_PKG_DUP(subscription_state, subs_terminated, "pkg subs state");
|
|
|
+ LM_DBG("Expires is past than current time!");
|
|
|
+ LM_DBG("Subscription state: [%.*s]", subscription_state.len, subscription_state.s);
|
|
|
+ }
|
|
|
+
|
|
|
+ //This is a fix to ensure that when a user subscribes a full reg info is only sent to that UE
|
|
|
+ if (event_type == IMS_REGISTRAR_SUBSCRIBE) {
|
|
|
+ if ((watcher_contact->len == s->watcher_contact.len) && (strncasecmp(s->watcher_contact.s, watcher_contact->s, watcher_contact->len) == 0) &&
|
|
|
+ (presentity_uri->len == s->presentity_uri.len) && (strncasecmp(s->presentity_uri.s, presentity_uri->s, presentity_uri->len) == 0)) {
|
|
|
+ LM_DBG("This is a fix to ensure that we only send full reg info XML to the UE that just subscribed");
|
|
|
+ LM_DBG("about to make new notification!");
|
|
|
+ n = new_notification(subscription_state, content_type, content,
|
|
|
+ s->version++, s);
|
|
|
+ if (n) {
|
|
|
+ //LM_DBG("Notification exists - about to add it");
|
|
|
+ //add_notification(n);
|
|
|
+
|
|
|
+ //Richard just gonna send it - not bother queueing etc.
|
|
|
+ //TODO look at impact of this - sending straight away vs queueing and getting another process to send
|
|
|
+ LM_DBG("About to send notification");
|
|
|
+ send_notification(n);
|
|
|
+ LM_DBG("About to free notification");
|
|
|
+ free_notification(n);
|
|
|
+ } else {
|
|
|
+ LM_DBG("Notification does not exist");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ LM_DBG("about to make new notification!");
|
|
|
+ n = new_notification(subscription_state, content_type, content,
|
|
|
+ s->version++, s);
|
|
|
+ if (n) {
|
|
|
+ //LM_DBG("Notification exists - about to add it");
|
|
|
+ //add_notification(n);
|
|
|
+
|
|
|
+ //Richard just gonna send it - not bother queueing etc.
|
|
|
+ //TODO look at impact of this - sending straight away vs queueing and getting another process to send
|
|
|
+ LM_DBG("About to send notification");
|
|
|
+ send_notification(n);
|
|
|
+ LM_DBG("About to free notification");
|
|
|
+ free_notification(n);
|
|
|
+ } else {
|
|
|
+ LM_DBG("Notification does not exist");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //}
|
|
|
+ s = s->next;
|
|
|
+
|
|
|
+ if (subscription_state.s) {
|
|
|
+ pkg_free(subscription_state.s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (domain_locked == 1) {
|
|
|
+ ul.unlock_udomain(_t, presentity_uri);
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+out_of_memory:
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/** Maximum reginfo XML size */
|
|
|
+#define MAX_REGINFO_SIZE 16384
|
|
|
+
|
|
|
+static str xml_start = {"<?xml version=\"1.0\"?>\n", 22};
|
|
|
+
|
|
|
+static str r_full = {"full", 4};
|
|
|
+static str r_partial = {"partial", 7};
|
|
|
+static str r_reginfo_s = {"<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" version=\"%s\" state=\"%.*s\">\n", 74};
|
|
|
+static str r_reginfo_e = {"</reginfo>\n", 11};
|
|
|
+
|
|
|
+static str r_active = {"active", 6};
|
|
|
+static str r_terminated = {"terminated", 10};
|
|
|
+static str registration_s = {"\t<registration aor=\"%.*s\" id=\"%p\" state=\"%.*s\">\n", 48};
|
|
|
+static str registration_e = {"\t</registration>\n", 17};
|
|
|
+
|
|
|
+//richard: we only use reg unreg refrsh and expire
|
|
|
+static str r_registered = {"registered", 10};
|
|
|
+static str r_refreshed = {"refreshed", 9};
|
|
|
+static str r_expired = {"expired", 7};
|
|
|
+static str r_unregistered = {"unregistered", 12};
|
|
|
+static str contact_s = {"\t\t<contact id=\"%p\" state=\"%.*s\" event=\"%.*s\" expires=\"%d\">\n", 59};
|
|
|
+static str contact_s_q = {"\t\t<contact id=\"%p\" state=\"%.*s\" event=\"%.*s\" expires=\"%d\" q=\"%.3f\">\n", 69};
|
|
|
+static str contact_e = {"\t\t</contact>\n", 13};
|
|
|
+
|
|
|
+static str uri_s = {"\t\t\t<uri>", 8};
|
|
|
+static str uri_e = {"</uri>\n", 7};
|
|
|
+
|
|
|
+/**
|
|
|
+ * Creates the full reginfo XML.
|
|
|
+ * @param pv - the r_public to create for
|
|
|
+ * @param event_type - event type
|
|
|
+ * @param subsExpires - subscription expiration
|
|
|
+ * @returns the str with the XML content
|
|
|
+ * if its a new subscription we do things like subscribe to updates on IMPU, etc
|
|
|
+ */
|
|
|
+str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus) {
|
|
|
+ str x = {0, 0};
|
|
|
+ str buf, pad;
|
|
|
+ char bufc[MAX_REGINFO_SIZE], padc[MAX_REGINFO_SIZE];
|
|
|
+ impurecord_t *r;
|
|
|
+ ucontact_t *c;
|
|
|
+ int i, res;
|
|
|
+
|
|
|
+ buf.s = bufc;
|
|
|
+ buf.len = 0;
|
|
|
+ pad.s = padc;
|
|
|
+ pad.len = 0;
|
|
|
+
|
|
|
+ LM_DBG("Getting reginfo_full");
|
|
|
+
|
|
|
+ STR_APPEND(buf, xml_start);
|
|
|
+ sprintf(pad.s, r_reginfo_s.s, "%d", r_full.len, r_full.s);
|
|
|
+ pad.len = strlen(pad.s);
|
|
|
+ STR_APPEND(buf, pad);
|
|
|
+
|
|
|
+ for (i = 0; i < num_impus; i++) {
|
|
|
+ ul.lock_udomain(_t, &impu_list[i]);
|
|
|
+ LM_DBG("Scrolling through public identities, current one <%.*s>", impu_list[i].len, impu_list[i].s);
|
|
|
+ res = ul.get_impurecord(_t, &(impu_list[i]), &r);
|
|
|
+ if (res != 0) {
|
|
|
+ LM_WARN("impu disappeared, ignoring it\n");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ LM_DBG("Retrieved IMPU record");
|
|
|
+
|
|
|
+ if (r->reg_state == IMPU_REGISTERED) {
|
|
|
+ LM_DBG("IMPU reg state is IMPU REGISTERED so putting in status active");
|
|
|
+ sprintf(pad.s, registration_s.s, r->public_identity.len,
|
|
|
+ r->public_identity.s, r, r_active.len, r_active.s);
|
|
|
+ } else {
|
|
|
+ LM_DBG("IMPU reg state is not IMPU REGISTERED so putting in status terminated");
|
|
|
+ sprintf(pad.s, registration_s.s, r->public_identity.len,
|
|
|
+ r->public_identity.s, r, r_terminated.len,
|
|
|
+ r_terminated.s);
|
|
|
+ }
|
|
|
+ pad.len = strlen(pad.s);
|
|
|
+ STR_APPEND(buf, pad);
|
|
|
+ c = r->contacts;
|
|
|
+ LM_DBG("Scrolling through contact for this IMPU");
|
|
|
+ while (c) {
|
|
|
+ if (c->q != -1) {
|
|
|
+ LM_DBG("q value not equal to -1");
|
|
|
+ float q = (float) c->q / 1000;
|
|
|
+ sprintf(pad.s, contact_s_q.s, c, r_active.len, r_active.s,
|
|
|
+ r_registered.len, r_registered.s, c->expires - act_time,
|
|
|
+ q);
|
|
|
+ } else {
|
|
|
+ LM_DBG("q value equal to -1");
|
|
|
+ sprintf(pad.s, contact_s.s, c, r_active.len, r_active.s,
|
|
|
+ r_registered.len, r_registered.s,
|
|
|
+ c->expires - act_time);
|
|
|
+ }
|
|
|
+ pad.len = strlen(pad.s);
|
|
|
+ STR_APPEND(buf, pad);
|
|
|
+ STR_APPEND(buf, uri_s);
|
|
|
+
|
|
|
+ LM_DBG("Appending contact address: <%.*s>", c->c.len, c->c.s);
|
|
|
+
|
|
|
+ STR_APPEND(buf, (c->c));
|
|
|
+ STR_APPEND(buf, uri_e);
|
|
|
+
|
|
|
+ STR_APPEND(buf, contact_e);
|
|
|
+ c = c->next;
|
|
|
+ }
|
|
|
+ STR_APPEND(buf, registration_e);
|
|
|
+
|
|
|
+ ul.unlock_udomain(_t, &impu_list[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ STR_APPEND(buf, r_reginfo_e);
|
|
|
+
|
|
|
+ x.s = pkg_malloc(buf.len + 1);
|
|
|
+ if (x.s) {
|
|
|
+ x.len = buf.len;
|
|
|
+ memcpy(x.s, buf.s, buf.len);
|
|
|
+ x.s[x.len] = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ LM_DBG("Returned full reg-info: [%.*s]", x.len, x.s);
|
|
|
+
|
|
|
+ return x;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Creates the partial reginfo XML.
|
|
|
+ * @param pv - the r_public to create for
|
|
|
+ * @param pc - the r_contatct to create for
|
|
|
+ * @param event_type - event type
|
|
|
+ * @param subsExpires - subscription expiration
|
|
|
+ * @returns the str with the XML content
|
|
|
+ */
|
|
|
+
|
|
|
+str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type) {
|
|
|
+ str x = {0, 0};
|
|
|
+ str buf, pad;
|
|
|
+ char bufc[MAX_REGINFO_SIZE], padc[MAX_REGINFO_SIZE];
|
|
|
+ int expires = -1;
|
|
|
+
|
|
|
+ str state, event;
|
|
|
+
|
|
|
+ buf.s = bufc;
|
|
|
+ buf.len = 0;
|
|
|
+ pad.s = padc;
|
|
|
+ pad.len = 0;
|
|
|
+
|
|
|
+ STR_APPEND(buf, xml_start);
|
|
|
+ sprintf(pad.s, r_reginfo_s.s, "%d", r_partial.len, r_partial.s);
|
|
|
+ pad.len = strlen(pad.s);
|
|
|
+ STR_APPEND(buf, pad);
|
|
|
+
|
|
|
+
|
|
|
+ if (r) {
|
|
|
+ expires = c->expires - act_time;
|
|
|
+ if (r->contacts == c &&
|
|
|
+ //richard we only use expired and unregistered
|
|
|
+ (event_type == IMS_REGISTRAR_CONTACT_EXPIRED ||
|
|
|
+ event_type == IMS_REGISTRAR_CONTACT_UNREGISTERED)
|
|
|
+ )
|
|
|
+ sprintf(pad.s, registration_s.s, r->public_identity.len, r->public_identity.s, r, r_terminated.len, r_terminated.s);
|
|
|
+ else
|
|
|
+ sprintf(pad.s, registration_s.s, r->public_identity.len, r->public_identity.s, r, r_active.len, r_active.s);
|
|
|
+ pad.len = strlen(pad.s);
|
|
|
+ STR_APPEND(buf, pad);
|
|
|
+ if (c) {
|
|
|
+ switch (event_type) {
|
|
|
+
|
|
|
+ //richard we only use registered and refreshed and expired and unregistered
|
|
|
+ case IMS_REGISTRAR_CONTACT_REGISTERED:
|
|
|
+ state = r_active;
|
|
|
+ event = r_registered;
|
|
|
+ break;
|
|
|
+ case IMS_REGISTRAR_CONTACT_REFRESHED:
|
|
|
+ state = r_active;
|
|
|
+ event = r_refreshed;
|
|
|
+ break;
|
|
|
+ case IMS_REGISTRAR_CONTACT_EXPIRED:
|
|
|
+ state = r_terminated;
|
|
|
+ event = r_expired;
|
|
|
+ expires = 0;
|
|
|
+ break;
|
|
|
+ case IMS_REGISTRAR_CONTACT_UNREGISTERED:
|
|
|
+ state = r_terminated;
|
|
|
+ event = r_unregistered;
|
|
|
+ expires = 0;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ state = r_active;
|
|
|
+ event = r_registered;
|
|
|
+ }
|
|
|
+ if (c->q != -1) {
|
|
|
+ float q = (float) c->q / 1000;
|
|
|
+ sprintf(pad.s, contact_s_q.s, c, r_active.len, r_active.s, r_registered.len, r_registered.s, c->expires - act_time, q);
|
|
|
+ } else
|
|
|
+ sprintf(pad.s, contact_s.s, c, state.len, state.s, event.len, event.s, expires);
|
|
|
+ pad.len = strlen(pad.s);
|
|
|
+ STR_APPEND(buf, pad);
|
|
|
+ STR_APPEND(buf, uri_s);
|
|
|
+ STR_APPEND(buf, (c->c));
|
|
|
+ STR_APPEND(buf, uri_e);
|
|
|
+
|
|
|
+ STR_APPEND(buf, contact_e);
|
|
|
+ STR_APPEND(buf, registration_e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ STR_APPEND(buf, r_reginfo_e);
|
|
|
+
|
|
|
+
|
|
|
+ x.s = pkg_malloc(buf.len + 1);
|
|
|
+ if (x.s) {
|
|
|
+ x.len = buf.len;
|
|
|
+ memcpy(x.s, buf.s, buf.len);
|
|
|
+ x.s[x.len] = 0;
|
|
|
+ }
|
|
|
+ return x;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Callback for the UAC response to NOTIFY
|
|
|
+ */
|
|
|
+void uac_request_cb(struct cell *t, int type, struct tmcb_params * ps) {
|
|
|
+ LM_DBG("DBG:uac_request_cb: Type %d\n", type);
|
|
|
+}
|
|
|
+
|
|
|
+static int free_tm_dlg(dlg_t * td) {
|
|
|
+ if (td) {
|
|
|
+ if (td->route_set)
|
|
|
+ free_rr(&td->route_set);
|
|
|
+ pkg_free(td);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Creates a NOTIFY message and sends it
|
|
|
+ * @param n - the r_notification to create the NOTIFY after
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+void send_notification(reg_notification * n) {
|
|
|
+ str h = {0, 0};
|
|
|
+
|
|
|
+ uac_req_t uac_r;
|
|
|
+ dlg_t* td = NULL;
|
|
|
+
|
|
|
+ str method = {"NOTIFY", 6};
|
|
|
+
|
|
|
+ LM_DBG("DBG:send_notification: NOTIFY about <%.*s>\n", n->watcher_uri.len, n->watcher_uri.s);
|
|
|
+
|
|
|
+ h.len = 0;
|
|
|
+ h.len += contact_hdr1.len + scscf_name_str.len + contact_hdr2.len;
|
|
|
+ if (n->subscription_state.len) h.len += subss_hdr1.len + subss_hdr2.len + n->subscription_state.len;
|
|
|
+ h.len += event_hdr.len;
|
|
|
+ h.len += maxfwds_hdr.len;
|
|
|
+ if (n->content_type.len) h.len += ctype_hdr1.len + ctype_hdr2.len + n->content_type.len;
|
|
|
+ h.s = pkg_malloc(h.len);
|
|
|
+ if (!h.s) {
|
|
|
+ LM_ERR("ERR:send_notification: Error allocating %d bytes\n", h.len);
|
|
|
+ h.len = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //Add SCSCF name as contact address
|
|
|
+ h.len = 0;
|
|
|
+ STR_APPEND(h, contact_hdr1);
|
|
|
+ STR_APPEND(h, scscf_name_str);
|
|
|
+ STR_APPEND(h, contact_hdr2);
|
|
|
+
|
|
|
+ STR_APPEND(h, event_hdr);
|
|
|
+ STR_APPEND(h, maxfwds_hdr);
|
|
|
+ if (n->subscription_state.len) {
|
|
|
+ STR_APPEND(h, subss_hdr1);
|
|
|
+ STR_APPEND(h, n->subscription_state);
|
|
|
+ STR_APPEND(h, subss_hdr2);
|
|
|
+ }
|
|
|
+ if (n->content_type.len) {
|
|
|
+ STR_APPEND(h, ctype_hdr1);
|
|
|
+ STR_APPEND(h, n->content_type);
|
|
|
+ STR_APPEND(h, ctype_hdr2);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* construct the dlg_t structure */
|
|
|
+ td = build_dlg_t_from_notification(n);
|
|
|
+ if (td == NULL) {
|
|
|
+ LM_ERR("while building dlg_t structure\n");
|
|
|
+ free_tm_dlg(td);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (n->content.len) {
|
|
|
+
|
|
|
+ LM_DBG("Notification content exists - about to send notification with subscription state: [%.*s] content_type: [%.*s] content: [%.*s] : presentity_uri: [%.*s] watcher_uri: [%.*s]",
|
|
|
+ n->subscription_state.len, n->subscription_state.s, n->content_type.len, n->content_type.s, n->content.len, n->content.s,
|
|
|
+ n->presentity_uri.len, n->presentity_uri.s, n->watcher_uri.len, n->watcher_uri.s);
|
|
|
+
|
|
|
+ set_uac_req(&uac_r, &method, &h, &n->content, td, TMCB_LOCAL_COMPLETED,
|
|
|
+ uac_request_cb, 0);
|
|
|
+ tmb.t_request_within(&uac_r);
|
|
|
+ } else {
|
|
|
+ LM_DBG("o notification content - about to send notification with subscription state: [%.*s] presentity_uri: [%.*s] watcher_uri: [%.*s]",
|
|
|
+ n->subscription_state.len, n->subscription_state.s, n->presentity_uri.len, n->presentity_uri.s,
|
|
|
+ n->watcher_uri.len, n->watcher_uri.s);
|
|
|
+
|
|
|
+
|
|
|
+ set_uac_req(&uac_r, &method, &h, 0, td, TMCB_LOCAL_COMPLETED,
|
|
|
+ uac_request_cb, 0);
|
|
|
+ tmb.t_request_within(&uac_r);
|
|
|
+ }
|
|
|
+ if (h.s) pkg_free(h.s);
|
|
|
+ free_tm_dlg(td);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * The Notification timer looks for unsent notifications and sends them.
|
|
|
+ * - because not all events should wait until the notifications for them are sent
|
|
|
+ * @param ticks - the current time
|
|
|
+ * @param param - pointer to the domain_list
|
|
|
+ */
|
|
|
+void notification_timer(unsigned int ticks, void* param) {
|
|
|
+
|
|
|
+ LM_DBG("Running notification timer");
|
|
|
+
|
|
|
+ reg_notification *n = 0;
|
|
|
+ LM_DBG("Getting lock of notification list");
|
|
|
+ lock_get(notification_list->lock);
|
|
|
+ LM_DBG("Scrolling through list");
|
|
|
+ while (notification_list->head) {
|
|
|
+ n = notification_list->head;
|
|
|
+ LM_DBG("Taking notification out of list with watcher uri <%.*s> and presentity uri <%.*s>", n->watcher_uri.len, n->watcher_uri.s, n->presentity_uri.len, n->presentity_uri.s);
|
|
|
+ notification_list->head = n->next;
|
|
|
+ if (n->next) n->next->prev = 0;
|
|
|
+ else notification_list->tail = n->next;
|
|
|
+
|
|
|
+ LM_DBG("Releasing lock");
|
|
|
+ lock_release(notification_list->lock);
|
|
|
+
|
|
|
+ LM_DBG("About to send notification");
|
|
|
+ send_notification(n);
|
|
|
+ LM_DBG("About to free notification");
|
|
|
+ free_notification(n);
|
|
|
+ LM_DBG("Getting lock of notification list again");
|
|
|
+ lock_get(notification_list->lock);
|
|
|
+ }
|
|
|
+ LM_DBG("Releasing lock again");
|
|
|
+ lock_release(notification_list->lock);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Creates a notification based on the given parameters
|
|
|
+ * @param req_uri - the Request-URI for the NOTIFY
|
|
|
+ * @param uri - uri to send to
|
|
|
+ * @param subscription_state - the Subscription-State header value
|
|
|
+ * @param event - the event
|
|
|
+ * @param content_type - content type
|
|
|
+ * @param content - content
|
|
|
+ * @param dialog - dialog to send on
|
|
|
+ * @returns the r_notification or NULL on error
|
|
|
+ */
|
|
|
+reg_notification * new_notification(str subscription_state,
|
|
|
+ str content_type, str content, int version, reg_subscriber * r) {
|
|
|
+
|
|
|
+ reg_notification *n = 0;
|
|
|
+
|
|
|
+ str buf;
|
|
|
+ char bufc[MAX_REGINFO_SIZE];
|
|
|
+
|
|
|
+ sprintf(bufc, content.s, version);
|
|
|
+ buf.s = bufc;
|
|
|
+ buf.len = strlen(bufc);
|
|
|
+
|
|
|
+
|
|
|
+ int len;
|
|
|
+ char *p;
|
|
|
+
|
|
|
+ len = sizeof (reg_notification) + r->call_id.len + r->from_tag.len + r->to_tag.len + r->watcher_uri.len + r->watcher_contact.len +
|
|
|
+ r->record_route.len + r->sockinfo_str.len + r->presentity_uri.len + subscription_state.len + content_type.len + buf.len;
|
|
|
+
|
|
|
+ LM_DBG("Creating new notification");
|
|
|
+
|
|
|
+ n = (reg_notification*) shm_malloc(len);
|
|
|
+ if (n == 0) {
|
|
|
+ LM_ERR("no more shm mem (%d)\n", len);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ memset(n, 0, len);
|
|
|
+
|
|
|
+ p = (char*) (n + 1);
|
|
|
+
|
|
|
+ n->call_id.s = p;
|
|
|
+ n->call_id.len = r->call_id.len;
|
|
|
+ memcpy(p, r->call_id.s, r->call_id.len);
|
|
|
+ p += r->call_id.len;
|
|
|
+ LM_DBG("call id: [%.*s]", n->call_id.len, n->call_id.s);
|
|
|
+
|
|
|
+ n->from_tag.s = p;
|
|
|
+ n->from_tag.len = r->from_tag.len;
|
|
|
+ memcpy(p, r->from_tag.s, r->from_tag.len);
|
|
|
+ p += r->from_tag.len;
|
|
|
+ LM_DBG("from tag: [%.*s]", n->from_tag.len, n->from_tag.s);
|
|
|
+
|
|
|
+ n->to_tag.s = p;
|
|
|
+ n->to_tag.len = r->to_tag.len;
|
|
|
+ memcpy(p, r->to_tag.s, r->to_tag.len);
|
|
|
+ p += r->to_tag.len;
|
|
|
+ LM_DBG("to tag: [%.*s]", n->to_tag.len, n->to_tag.s);
|
|
|
+
|
|
|
+ n->watcher_uri.s = p;
|
|
|
+ n->watcher_uri.len = r->watcher_uri.len;
|
|
|
+ memcpy(p, r->watcher_uri.s, r->watcher_uri.len);
|
|
|
+ p += r->watcher_uri.len;
|
|
|
+ LM_DBG("watcher_uri: [%.*s]", n->watcher_uri.len, n->watcher_uri.s);
|
|
|
+
|
|
|
+ n->watcher_contact.s = p;
|
|
|
+ n->watcher_contact.len = r->watcher_contact.len;
|
|
|
+ memcpy(p, r->watcher_contact.s, r->watcher_contact.len);
|
|
|
+ p += r->watcher_contact.len;
|
|
|
+ LM_DBG("watcher_contact: [%.*s]", n->watcher_contact.len, n->watcher_contact.s);
|
|
|
+
|
|
|
+ n->record_route.s = p;
|
|
|
+ n->record_route.len = r->record_route.len;
|
|
|
+ memcpy(p, r->record_route.s, r->record_route.len);
|
|
|
+ p += r->record_route.len;
|
|
|
+ LM_DBG("record_route: [%.*s]", n->record_route.len, n->record_route.s);
|
|
|
+
|
|
|
+ n->sockinfo_str.s = p;
|
|
|
+ n->sockinfo_str.len = r->sockinfo_str.len;
|
|
|
+ memcpy(p, r->sockinfo_str.s, r->sockinfo_str.len);
|
|
|
+ p += r->sockinfo_str.len;
|
|
|
+ LM_DBG("sockinfo_str: [%.*s]", n->sockinfo_str.len, n->sockinfo_str.s);
|
|
|
+
|
|
|
+ n->presentity_uri.s = p;
|
|
|
+ n->presentity_uri.len = r->presentity_uri.len;
|
|
|
+ memcpy(p, r->presentity_uri.s, r->presentity_uri.len);
|
|
|
+ p += r->presentity_uri.len;
|
|
|
+ LM_DBG("presentity_uri: [%.*s]", n->presentity_uri.len, n->presentity_uri.s);
|
|
|
+
|
|
|
+ n->subscription_state.s = p;
|
|
|
+ n->subscription_state.len = subscription_state.len;
|
|
|
+ memcpy(p, subscription_state.s, subscription_state.len);
|
|
|
+ p += subscription_state.len;
|
|
|
+ LM_DBG("Notification subscription state: [%.*s]", n->subscription_state.len, n->subscription_state.s);
|
|
|
+
|
|
|
+ n->content_type.s = p;
|
|
|
+ n->content_type.len = content_type.len;
|
|
|
+ memcpy(p, content_type.s, content_type.len);
|
|
|
+ p += content_type.len;
|
|
|
+ LM_DBG("Notification content type: [%.*s]", n->content_type.len, n->content_type.s);
|
|
|
+
|
|
|
+ n->content.s = p;
|
|
|
+ n->content.len = buf.len;
|
|
|
+ memcpy(p, buf.s, buf.len);
|
|
|
+ p += buf.len;
|
|
|
+ LM_DBG("Notification content: [%.*s]", n->content.len, n->content.s);
|
|
|
+
|
|
|
+ if (p != (((char*) n) + len)) {
|
|
|
+ LM_CRIT("buffer overflow\n");
|
|
|
+ free_notification(n);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Adds a notification to the list of notifications at the end (FIFO).
|
|
|
+ * @param n - the notification to be added
|
|
|
+ */
|
|
|
+void add_notification(reg_notification * n) {
|
|
|
+
|
|
|
+ LM_DBG("Adding notification");
|
|
|
+ if (!n) {
|
|
|
+ LM_DBG("Notification does not exist");
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ LM_DBG("Notification exists");
|
|
|
+ }
|
|
|
+ LM_DBG("Adding to notification list");
|
|
|
+ lock_get(notification_list->lock);
|
|
|
+ n->next = 0;
|
|
|
+ n->prev = notification_list->tail;
|
|
|
+ if (notification_list->tail) notification_list->tail->next = n;
|
|
|
+ notification_list->tail = n;
|
|
|
+ if (!notification_list->head) notification_list->head = n;
|
|
|
+ lock_release(notification_list->lock);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Frees up space taken by a notification
|
|
|
+ * @param n - the notification to be freed
|
|
|
+ */
|
|
|
+void free_notification(reg_notification * n) {
|
|
|
+ if (n) {
|
|
|
+ shm_free(n);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+dlg_t * build_dlg_t_from_notification(reg_notification * n) {
|
|
|
+ dlg_t* td = NULL;
|
|
|
+ td = (dlg_t*) pkg_malloc(sizeof (dlg_t));
|
|
|
+ if (td == NULL) {
|
|
|
+ LM_ERR("Error ran out of package memory");
|
|
|
+ }
|
|
|
+ memset(td, 0, sizeof (dlg_t));
|
|
|
+
|
|
|
+ LM_DBG("Building dlg_t structure");
|
|
|
+
|
|
|
+ td->loc_seq.value = n->local_cseq;
|
|
|
+ LM_DBG("local cseq %d", n->local_cseq);
|
|
|
+ td->loc_seq.is_set = 1;
|
|
|
+
|
|
|
+ td->id.call_id = n->call_id;
|
|
|
+ LM_DBG("call id %.*s", n->call_id.len, n->call_id.s);
|
|
|
+
|
|
|
+ td->id.rem_tag = n->from_tag;
|
|
|
+ LM_DBG("ftag %.*s", n->from_tag.len, n->from_tag.s);
|
|
|
+
|
|
|
+
|
|
|
+ td->id.loc_tag = n->to_tag;
|
|
|
+ LM_DBG("ttag %.*s", n->to_tag.len, n->to_tag.s);
|
|
|
+
|
|
|
+
|
|
|
+ td->loc_uri = n->presentity_uri;
|
|
|
+ LM_DBG("loc uri %.*s", n->presentity_uri.len, n->presentity_uri.s);
|
|
|
+
|
|
|
+ td->rem_target = n->watcher_contact;
|
|
|
+ LM_DBG("rem target %.*s", n->watcher_contact.len, n->watcher_contact.s);
|
|
|
+
|
|
|
+ td->rem_uri = n->watcher_uri;
|
|
|
+ LM_DBG("rem uri %.*s", n->watcher_uri.len, n->watcher_uri.s);
|
|
|
+
|
|
|
+ if (n->record_route.s && n->record_route.len) {
|
|
|
+ if (parse_rr_body(n->record_route.s, n->record_route.len,
|
|
|
+ &td->route_set) < 0) {
|
|
|
+ LM_ERR("in function parse_rr_body\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ td->state = DLG_CONFIRMED;
|
|
|
+
|
|
|
+ if (n->sockinfo_str.len) {
|
|
|
+ int port, proto;
|
|
|
+ str host;
|
|
|
+ char* tmp;
|
|
|
+ if ((tmp = as_asciiz(&n->sockinfo_str)) == NULL) {
|
|
|
+ LM_ERR("no pkg memory left\n");
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ if (parse_phostport(tmp, &host.s,
|
|
|
+ &host.len, &port, &proto)) {
|
|
|
+ LM_ERR("bad sockinfo string\n");
|
|
|
+ pkg_free(tmp);
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ pkg_free(tmp);
|
|
|
+ td->send_sock = grep_sock_info(
|
|
|
+ &host, (unsigned short) port, (unsigned short) proto);
|
|
|
+ }
|
|
|
+
|
|
|
+ return td;
|
|
|
+
|
|
|
+error:
|
|
|
+ free_tm_dlg(td);
|
|
|
+ return NULL;
|
|
|
+}
|