Ver código fonte

modules/ims_registrar_scscf: New features to fully support subscription to reg_event
Main change to support PUBLISH of reg event information
And to use new usrloc API storage of callid/totag/fromtag presentity combination to correctly process usbsequent SUBSCRIBE

Richard Good 11 anos atrás
pai
commit
784280cfac

+ 2 - 0
modules/ims_registrar_scscf/reg_mod.c

@@ -195,6 +195,8 @@ static cmd_export_t cmds[] = {
     {"reg_free_contacts", (cmd_function) pv_free_contacts, 1, fixup_str_null, 0, REQUEST_ROUTE | FAILURE_ROUTE},
     {"reg_free_contacts", (cmd_function) pv_free_contacts, 1, fixup_str_null, 0, REQUEST_ROUTE | FAILURE_ROUTE},
     {"can_subscribe_to_reg", (cmd_function) can_subscribe_to_reg, 1, domain_fixup, 0, REQUEST_ROUTE},
     {"can_subscribe_to_reg", (cmd_function) can_subscribe_to_reg, 1, domain_fixup, 0, REQUEST_ROUTE},
     {"subscribe_to_reg", (cmd_function) subscribe_to_reg, 1, domain_fixup, 0, REQUEST_ROUTE},
     {"subscribe_to_reg", (cmd_function) subscribe_to_reg, 1, domain_fixup, 0, REQUEST_ROUTE},
+    {"can_publish_reg", (cmd_function) can_publish_reg, 1, domain_fixup, 0, REQUEST_ROUTE},
+    {"publish_reg", (cmd_function) publish_reg, 1, domain_fixup, 0, REQUEST_ROUTE},
     //{"bind_registrar", (cmd_function) bind_registrar, 0, 0, 0, 0},  TODO put this back in !
     //{"bind_registrar", (cmd_function) bind_registrar, 0, 0, 0, 0},  TODO put this back in !
     {0, 0, 0, 0, 0, 0}
     {0, 0, 0, 0, 0, 0}
 };
 };

+ 636 - 22
modules/ims_registrar_scscf/registrar_notify.c

@@ -50,8 +50,26 @@
 #include "regtime.h"
 #include "regtime.h"
 #include "usrloc_cb.h"
 #include "usrloc_cb.h"
 
 
+
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_content.h"
+#include "../../parser/parse_uri.h"
+#include <libxml/parser.h>
+
 #include "../../lib/ims/useful_defs.h"
 #include "../../lib/ims/useful_defs.h"
 
 
+#define STATE_ACTIVE 1
+#define STATE_TERMINATED 0
+#define STATE_UNKNOWN -1
+
+#define EVENT_UNKNOWN -1
+#define EVENT_REGISTERED 0
+#define EVENT_UNREGISTERED 1
+#define EVENT_TERMINATED 2
+#define EVENT_CREATED 3
+#define EVENT_REFRESHED 4
+#define EVENT_EXPIRED 5
+
 /**
 /**
  * Initializes the reg notifications list.
  * Initializes the reg notifications list.
  */
  */
@@ -99,13 +117,138 @@ void notify_destroy() {
     shm_free(notification_list);
     shm_free(notification_list);
 }
 }
 
 
+
+int can_publish_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 publish reg 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 != 7 ||
+		memcmp(msg->first_line.u.request.method.s, "PUBLISH", 7) != 0) {
+	    LM_ERR("This message is not a PUBLISH\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);
+	    goto done;
+	}
+
+	asserted_id = cscf_get_asserted_identity(msg);
+	if (!asserted_id.len) {
+	    LM_ERR("P-Asserted-Identity empty.\n");
+	    goto error;
+	}
+	LM_DBG("P-Asserted-Identity <%.*s>.\n", asserted_id.len, asserted_id.s);
+
+	//get presentity URI
+	presentity_uri = cscf_get_public_identity_from_requri(msg);
+	
+	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);
+	    goto done;
+	}
+
+	LM_DBG("<%.*s> found in usrloc\n", presentity_uri.len, presentity_uri.s);
+
+	//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);
+	LM_DBG("Did not find p-asserted-identity <%.*s> in SP\n", asserted_id.len, asserted_id.s);
+
+	//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++)
+		    LM_DBG("Path: <%.*s>.\n",
+		    c->path.len, c->path.s);
+		    //we compare the asserted_id without "sip:" to the path 
+		    if (strncasecmp(c->path.s + i, asserted_id.s+4, asserted_id.len-4) == 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;
+	}
+	LM_DBG("Did not find p-asserted-identity <%.*s> on Path\n", asserted_id.len, asserted_id.s);
+
+	ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
+	LM_DBG("Publish forbidden\n");
+    
+done:
+	if (presentity_uri.s) shm_free(presentity_uri.s); // shm_malloc in cscf_get_public_identity_from_requri
+	return ret;
+error:
+	if (presentity_uri.s) shm_free(presentity_uri.s); // shm_malloc in cscf_get_public_identity_from_requri
+	ret = CSCF_RETURN_ERROR;
+	return ret;
+}
+
 int can_subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
 int can_subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
 
 
     int ret = CSCF_RETURN_FALSE;
     int ret = CSCF_RETURN_FALSE;
     str presentity_uri = {0, 0};
     str presentity_uri = {0, 0};
+    str callid = {0, 0};
+    str ftag = {0, 0};
+    str ttag = {0, 0};
     str event;
     str event;
     str asserted_id;
     str asserted_id;
-
     ucontact_t* c = 0;
     ucontact_t* c = 0;
     impurecord_t* r;
     impurecord_t* r;
     int res;
     int res;
@@ -133,19 +276,46 @@ int can_subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
     if (event.len != 3 || strncasecmp(event.s, "reg", 3) != 0) {
     if (event.len != 3 || strncasecmp(event.s, "reg", 3) != 0) {
         LM_ERR("Accepting only <Event: reg>. Found: <%.*s>\n",
         LM_ERR("Accepting only <Event: reg>. Found: <%.*s>\n",
                 event.len, event.s);
                 event.len, event.s);
-        ret = CSCF_RETURN_FALSE;
         goto done;
         goto done;
     }
     }
-
-    //get the target/presentity URI from To header
-    cscf_get_to_uri(msg, &presentity_uri);
+    
+    //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");
+        goto error;
+    }
+    //ftag
+    if (!cscf_get_from_tag(msg, &ftag)) {
+        LM_ERR("Unable to get ftag\n");
+        goto error;
+    }
+    
+    //ttag
+     if (!cscf_get_to_tag(msg, &ttag)) {
+        LM_ERR("Unable to get ttag\n");
+        goto error;
+    }
+    
+    //get presentity URI
+    //check if SUBSCRIBE is initial or SUBSEQUENT
+    if (ttag.len == 0) {
+        LM_DBG("Msg has no ttag - this is initial subscribe - get presentity URI from req URI\n");
+	presentity_uri = cscf_get_public_identity_from_requri(msg);
+    } else {
+	presentity_uri = ul.get_presentity_from_subscriber_dialog(&callid, &ttag, &ftag);
+	if (presentity_uri.len == 0) {
+	    LM_ERR("Unable to get pres uri from subscriber dialog with callid <%.*s>, ttag <%.*s> and ftag <%.*s>\n", callid.len, callid.s, ttag.len, ttag.s, ftag.len, ftag.s);
+	    goto done;
+	}
+    }
 
 
 
 
     asserted_id = cscf_get_asserted_identity(msg);
     asserted_id = cscf_get_asserted_identity(msg);
     if (!asserted_id.len) {
     if (!asserted_id.len) {
         LM_ERR("P-Asserted-Identity empty.\n");
         LM_ERR("P-Asserted-Identity empty.\n");
-        ret = CSCF_RETURN_FALSE;
-        goto done;
+        goto error;
     }
     }
     LM_DBG("P-Asserted-Identity <%.*s>.\n",
     LM_DBG("P-Asserted-Identity <%.*s>.\n",
             asserted_id.len, asserted_id.s);
             asserted_id.len, asserted_id.s);
@@ -158,9 +328,10 @@ int can_subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
     if (res > 0) {
     if (res > 0) {
         LM_DBG("'%.*s' Not found in usrloc\n", presentity_uri.len, presentity_uri.s);
         LM_DBG("'%.*s' Not found in usrloc\n", presentity_uri.len, presentity_uri.s);
         ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
         ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
-        ret = CSCF_RETURN_FALSE;
         goto done;
         goto done;
     }
     }
+    
+    LM_DBG("<%.*s> found in usrloc\n", presentity_uri.len, presentity_uri.s);
 
 
     //check if the asserted identity is in the same group as that presentity uri
     //check if the asserted identity is in the same group as that presentity uri
     if (r->public_identity.len == asserted_id.len &&
     if (r->public_identity.len == asserted_id.len &&
@@ -191,6 +362,7 @@ int can_subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
             }
             }
     }
     }
     lock_release(r->s->lock);
     lock_release(r->s->lock);
+    LM_DBG("Did not find p-asserted-identity <%.*s> in SP\n", asserted_id.len, asserted_id.s);
 
 
     //check if asserted is present in any of the path headers
     //check if asserted is present in any of the path headers
     c = r->contacts;
     c = r->contacts;
@@ -211,13 +383,17 @@ int can_subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
         }
         }
         c = c->next;
         c = c->next;
     }
     }
+    LM_DBG("Did not find p-asserted-identity <%.*s> on Path\n", asserted_id.len, asserted_id.s);
     
     
     ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
     ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
+    LM_DBG("Subscribe forbidden\n");
 
 
 done:
 done:
-    return ret;
+	if (presentity_uri.s) shm_free(presentity_uri.s); // shm_malloc in cscf_get_public_identity_from_requri or get_presentity_from_subscriber_dialog
+	return ret;
 error:
 error:
     ret = CSCF_RETURN_ERROR;
     ret = CSCF_RETURN_ERROR;
+	if (presentity_uri.s) shm_free(presentity_uri.s); // shm_malloc in cscf_get_public_identity_from_requri or get_presentity_from_subscriber_dialog
     return ret;
     return ret;
 }
 }
 
 
@@ -305,6 +481,388 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int e
     }
     }
 }
 }
 
 
+int process_contact(impurecord_t* presentity_impurecord, udomain_t * _d, int expires, str contact_uri, int contact_state) {
+	
+	int ret = CSCF_RETURN_TRUE;
+	int i, j;
+	ims_public_identity* pi = 0;
+	struct ucontact* ucontact;
+	str callid = {0, 0};
+	str path = {0, 0};
+	ims_subscription* subscription = 0;
+	impurecord_t* implicit_impurecord = 0;
+
+	//first get the subscription
+	//then go through each implicit public identity (exclude the explicit identity)
+	//get the IMPU rec for each implicit public identity
+	//then get the contact for each implicit IMPU and delete if contact_state == STATE_TERMINATED
+	//then get the contact for each explicit IMPU and delete if contact_state == STATE_TERMINATED
+	
+	subscription = presentity_impurecord->s;
+	if (!subscription) {
+	    LM_DBG("No subscriber info associated with <%.*s>, so no implicit IMPUs to process\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s);
+	    goto done;
+	} 
+
+	lock_get(subscription->lock);
+	subscription->ref_count++;
+	LM_DBG("subscription ref count after add is now %d\n", subscription->ref_count);
+	lock_release(subscription->lock);
+
+	//now update the implicit set
+	for (i = 0; i < subscription->service_profiles_cnt; i++) {
+	    for (j = 0; j < subscription->service_profiles[i].public_identities_cnt; j++) {
+		pi = &(subscription->service_profiles[i].public_identities[j]);
+
+		if (memcmp(presentity_impurecord->public_identity.s, pi->public_identity.s, presentity_impurecord->public_identity.len) == 0) { //we don't need to update the explicit IMPU
+		    LM_DBG("Ignoring explicit identity <%.*s>, updating later.....\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s);
+		    goto next_implicit_impu;
+		}
+		ul.lock_udomain(_d, &pi->public_identity);
+		if (ul.get_impurecord(_d, &pi->public_identity, &implicit_impurecord) != 0) {
+		    LM_DBG("usrloc does not have imprecord for implicity IMPU, ignore\n");
+		    goto next_implicit_impu;
+		}
+		if (ul.get_ucontact(implicit_impurecord, &contact_uri, &callid, &path, 0/*cseq*/,  &ucontact) != 0) { //contact does not exist
+		    LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_ONLY\n", contact_uri.len, contact_uri.s);
+		    goto next_implicit_impu;
+		} else {//contact exists
+			if (contact_state == STATE_TERMINATED) {
+				//delete contact
+				LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s);
+				if (ul.delete_ucontact(implicit_impurecord, ucontact) != 0) {
+				    LM_ERR("Failed to delete ucontact <%.*s> from implicit IMPU\n", contact_uri.len, contact_uri.s);
+				    goto next_implicit_impu;
+				}
+			}else {//state is active
+				LM_DBG("This contact: <%.*s> is not in state terminated and is in usrloc, ignore\n", contact_uri.len, contact_uri.s);
+				goto next_implicit_impu;
+			}
+		}
+next_implicit_impu:
+		ul.unlock_udomain(_d, &pi->public_identity);
+	    }
+	}
+
+	lock_get(subscription->lock);
+	subscription->ref_count--;
+	LM_DBG("subscription ref count after sub is now %d\n", subscription->ref_count);
+	lock_release(subscription->lock);
+
+	
+	ul.lock_udomain(_d, &presentity_impurecord->public_identity);
+	
+	if (ul.get_ucontact(presentity_impurecord, &contact_uri, &callid, &path, 0/*cseq*/,  &ucontact) != 0) { //contact does not exist
+	    LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_ONLY\n", contact_uri.len, contact_uri.s);
+	    goto done;
+	} else {//contact exists
+		if (contact_state == STATE_TERMINATED) {
+			//delete contact
+			LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s);
+			if (ul.delete_ucontact(presentity_impurecord, ucontact) != 0) {
+			    LM_ERR("Failed to delete ucontact <%.*s>\n", contact_uri.len, contact_uri.s);
+			    ret = CSCF_RETURN_FALSE;
+			    goto done;
+			}
+		}else {//state is active
+			LM_DBG("This contact: <%.*s> is not in state terminated and is in usrloc, ignore\n", contact_uri.len, contact_uri.s);
+			goto done;
+		}
+	}
+	
+done:
+	    ul.unlock_udomain(_d, &presentity_impurecord->public_identity);
+	    return ret;
+}
+
+
+int reginfo_parse_state(char * s) {
+	if (s == NULL) {
+		return STATE_UNKNOWN;
+	}
+	switch (strlen(s)) {
+		case 6:
+			if (strncmp(s, "active", 6) ==  0) return STATE_ACTIVE;
+			break;
+		case 10:
+			if (strncmp(s, "terminated", 10) ==  0) return STATE_TERMINATED;
+			break;
+		default:
+			LM_ERR("Unknown State %s\n", s);
+			return STATE_UNKNOWN;
+	}
+	LM_ERR("Unknown State %s\n", s);
+	return STATE_UNKNOWN;
+}
+
+int reginfo_parse_event(char * s) {
+	if (s == NULL) {
+		return EVENT_UNKNOWN;
+	}
+	switch (strlen(s)) {
+		case 7:
+			if (strncmp(s, "created", 7) ==  0) return EVENT_CREATED;
+			if (strncmp(s, "expired", 7) ==  0) return EVENT_EXPIRED;
+			break;
+		case 9:
+			if (strncmp(s, "refreshed", 9) ==  0) return EVENT_CREATED;
+			break;
+		case 10:
+			if (strncmp(s, "registered", 10) ==  0) return EVENT_REGISTERED;
+			if (strncmp(s, "terminated", 10) ==  0) return EVENT_TERMINATED;
+			break;
+		case 12:
+			if (strncmp(s, "unregistered", 12) ==  0) return EVENT_UNREGISTERED;
+			break;
+		default:
+			LM_ERR("Unknown Event %s\n", s);
+			return EVENT_UNKNOWN;
+	}
+	LM_ERR("Unknown Event %s\n", s);
+	return EVENT_UNKNOWN;
+}
+
+xmlNodePtr xmlGetNodeByName(xmlNodePtr parent, const char *name) {
+	xmlNodePtr cur = parent;
+	xmlNodePtr match = NULL;
+	while (cur) {
+		if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0)
+			return cur;
+		match = xmlGetNodeByName(cur->children, name);
+		if (match)
+			return match;
+		cur = cur->next;
+	}
+	return NULL;
+}
+
+char * xmlGetAttrContentByName(xmlNodePtr node, const char *name) {
+	xmlAttrPtr attr = node->properties;
+	while (attr) {
+		if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0)
+			return (char*)xmlNodeGetContent(attr->children);
+		attr = attr->next;
+	}
+	return NULL;
+}
+
+
+int process_publish_body(struct sip_msg* msg, str publish_body, udomain_t * domain) {
+	xmlDocPtr doc= NULL;
+	xmlNodePtr doc_root = NULL, registrations = NULL, contacts = NULL, uris = NULL;
+	str aor = {0, 0};
+	str callid = {0, 0};
+	str contact_uri = {0, 0};
+	str received = {0,0};
+	str path = {0,0};
+	str user_agent = {0, 0};
+	int reg_state, contact_state, event, expires, result, final_result = CSCF_RETURN_FALSE;
+	char * expires_char,  * cseq_char;
+	int cseq = 0;
+	impurecord_t* presentity_impurecord;
+	
+	doc = xmlParseMemory(publish_body.s, publish_body.len);
+	if(doc== NULL)  {
+		LM_ERR("Error while parsing the xml body message, Body is:\n%.*s\n",
+			publish_body.len, publish_body.s);
+		return -1;
+	}
+	doc_root = xmlGetNodeByName(doc->children, "reginfo");
+	if(doc_root == NULL) {
+		LM_ERR("while extracting the reginfo node\n");
+		goto error;
+	}
+	registrations = doc_root->children;
+	while (registrations) {
+		/* Only process registration sub-items */
+		if (xmlStrcasecmp(registrations->name, BAD_CAST "registration") != 0)
+			goto next_registration;
+		reg_state = reginfo_parse_state(xmlGetAttrContentByName(registrations, "state"));
+		aor.s = xmlGetAttrContentByName(registrations, "aor");
+		if (aor.s == NULL) {
+			LM_ERR("No AOR for this registration!\n");		
+			goto next_registration;
+		}
+		aor.len = strlen(aor.s);
+		LM_DBG("AOR %.*s has reg_state \"%d\"\n", aor.len, aor.s, reg_state);
+		
+		//TOD get IMPU record here
+		ul.lock_udomain(domain, &aor);
+		if (ul.get_impurecord(domain, &aor, &presentity_impurecord) != 0) {
+		    LM_DBG("usrloc does not have imprecord for presentity being published too, ignore\n");
+		    ul.unlock_udomain(domain, &aor);
+		    goto next_registration;
+		}
+		ul.unlock_udomain(domain, &aor);
+
+		LM_DBG("Received impurecord for presentity being published on [%.*s]\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s);
+		
+		if (reg_state == STATE_TERMINATED) {
+		    LM_DBG("This impurecord is in STATE_TERMINATED - TODO we should should delete all contacts");
+		}
+		else {
+		    /* Now lets process the Contact's from this Registration: */
+		    contacts = registrations->children;
+		    while (contacts) {
+			    if (xmlStrcasecmp(contacts->name, BAD_CAST "contact") != 0)
+				    goto next_contact;
+			    callid.s = xmlGetAttrContentByName(contacts, "callid");
+			    if (callid.s == NULL) {
+				    LM_DBG("No Call-ID for this contact!\n");		
+				    callid.len = 0;
+			    } else {
+				    callid.len = strlen(callid.s);
+				    LM_DBG("contact has callid <%.*s>\n", callid.len, callid.s);		
+			    }	
+
+			    received.s = xmlGetAttrContentByName(contacts, "received");
+			    if (received.s == NULL) {
+				    LM_DBG("No received for this contact!\n");
+				    received.len = 0;
+			    } else {
+				    received.len = strlen(received.s);
+				    LM_DBG("contact has received <%.*s>\n", received.len, received.s);
+			    }
+
+			    path.s = xmlGetAttrContentByName(contacts, "path");	
+			    if (path.s == NULL) {
+				    LM_DBG("No path for this contact!\n");
+				    path.len = 0;
+			    } else {
+				    path.len = strlen(path.s);
+				    LM_DBG("contact has path <%.*s>\n", path.len, path.s);
+			    }
+
+			    user_agent.s = xmlGetAttrContentByName(contacts, "user_agent");
+			    if (user_agent.s == NULL) {
+				    LM_DBG("No user_agent for this contact!\n");
+				    user_agent.len = 0;
+			    } else {
+				    user_agent.len = strlen(user_agent.s);
+				    LM_DBG("contact has user_agent <%.*s>\n", user_agent.len, user_agent.s);
+			    }
+			    event = reginfo_parse_event(xmlGetAttrContentByName(contacts, "event"));
+			    if (event == EVENT_UNKNOWN) {
+				    LM_ERR("No event for this contact - going to next contact!\n");		
+				    goto next_contact;
+			    }
+			    expires_char = xmlGetAttrContentByName(contacts, "expires");
+			    if (expires_char == NULL) {
+				    LM_ERR("No expires for this contact - going to next contact!\n");		
+				    goto next_contact;
+			    }
+			    expires = atoi(expires_char);
+			    if (expires < 0) {
+				    LM_ERR("No valid expires for this contact - going to next contact!\n");		
+				    goto next_contact;
+			    }
+
+			    contact_state = reginfo_parse_state(xmlGetAttrContentByName(contacts, "state"));
+			    if (contact_state == STATE_UNKNOWN) {
+				LM_ERR("No state for this contact - going to next contact!\n");		
+				goto next_contact;
+			    } 
+
+			    LM_DBG("Contact state %d: Event \"%d\", expires %d\n", contact_state, event, expires);
+
+			    cseq_char = xmlGetAttrContentByName(contacts, "cseq");
+			    if (cseq_char == NULL) {
+				    LM_DBG("No cseq for this contact!\n");		
+			    } else {
+				    cseq = atoi(cseq_char);
+				    if (cseq < 0) {
+					    LM_DBG("No valid cseq for this contact!\n");		
+				    }
+			    }
+
+			    /* Now lets process the URI's from this Contact: */
+			    uris = contacts->children;
+			    while (uris) {
+				    if (xmlStrcasecmp(uris->name, BAD_CAST "uri") != 0)
+					    goto next_uri;
+				    contact_uri.s = (char*)xmlNodeGetContent(uris);	
+				    if (contact_uri.s == NULL) {
+					    LM_ERR("No URI for this contact - going to next registration!\n");		
+					    goto next_registration;
+				    }
+				    contact_uri.len = strlen(contact_uri.s);
+				    LM_DBG("Contact: %.*s\n",
+					    contact_uri.len, contact_uri.s);
+
+				    /* Add to Usrloc: */
+				    result = process_contact(presentity_impurecord, domain, expires, contact_uri, contact_state);
+
+				    /* Process the result */
+				    if (final_result != CSCF_RETURN_TRUE) final_result = result;
+    next_uri:
+				    uris = uris->next;
+			    }
+    next_contact:
+			    contacts = contacts->next;
+		    }
+		}
+next_registration:
+		registrations = registrations->next;
+	}
+error:
+	/* Free the XML-Document */
+    	if(doc) xmlFreeDoc(doc);
+	return final_result;
+}
+
+
+/**
+ * Modify the subscription based on publish
+ * @param msg - the SIP PUBLISH 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 publish_reg(struct sip_msg *msg, char *_t, char *str2) {
+    
+    udomain_t* domain = (udomain_t*) _t;
+    int expires = 0;
+    int ret = CSCF_RETURN_FALSE;
+    str body;
+    
+    LM_DBG("Publishing reg info\n");
+    
+    
+    /* If not done yet, parse the whole message now: */
+    if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
+	    LM_ERR("Error parsing headers\n");
+	    return -1;
+    }
+    if (get_content_length(msg) == 0) {
+	    LM_DBG("Content length = 0\n");
+	    /* No Body? Then there is no published information available, which is ok. */
+	    goto done;
+    } else {
+	    body.s=get_body(msg);
+	    if (body.s== NULL) {
+		    LM_ERR("cannot extract body from msg\n");
+		    goto done;
+	    }
+	    body.len = get_content_length(msg);
+    }
+
+    LM_DBG("Body is %.*s\n", body.len, body.s);
+
+    ret = process_publish_body(msg, body, (udomain_t*)domain);
+    
+done:
+    //get expires
+    expires = cscf_get_expires_hdr(msg, 0);
+    if (expires == -1) expires = subscription_default_expires;
+    
+    if(ret==CSCF_RETURN_TRUE){
+	LM_DBG("Sending 200 OK to publishing user");
+	subscribe_reply(msg, 200, MSG_REG_PUBLISH_OK, &expires, &scscf_name_str);
+    }
+       
+    return ret;
+}
+
 /**
 /**
  * Save this subscription.
  * Save this subscription.
  * @param msg - the SIP SUBSCRIBE message
  * @param msg - the SIP SUBSCRIBE message
@@ -384,9 +942,36 @@ int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
         ret = CSCF_RETURN_FALSE;
         ret = CSCF_RETURN_FALSE;
         goto error;
         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);
+    
+    //ttag
+     if (!cscf_get_to_tag(msg, &ttag)) {
+        LM_ERR("Unable to get ttag\n");
+        ret = CSCF_RETURN_FALSE;
+        goto error;
+    }
+    
+    //check if SUBSCRIBE is initial or SUBSEQUENT
+    if (ttag.len == 0) {
+        LM_DBG("Msg has no ttag - this is initial subscribe\n");
+	//to tag - doesn't exist in initial 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);
+	LM_DBG("This is initial subscribe - get presentity URI from req URI\n");
+	presentity_uri = cscf_get_public_identity_from_requri(msg);
+	
+    } else {
+	LM_DBG("Msg has ttag: [%.*s] - this is subsequent subscribe\n", ttag.len, ttag.s);
+	//cscf_get_to_uri(msg, &presentity_uri);
+	LM_DBG("This is subsequent subscribe - get presentity URI from stored subscriber dialog\n");
+	//get the presentity uri from To Header
+	//cscf_get_to_uri(msg, &presentity_uri);
+	presentity_uri = ul.get_presentity_from_subscriber_dialog(&callid, &ttag, &ftag);
+	if (presentity_uri.len == 0) {
+	    LM_ERR("Unable to get pres uri from subscriber dialog with callid <%.*s>, ttag <%.*s> and ftag <%.*s>\n", callid.len, callid.s, ttag.len, ttag.s, ftag.len, ftag.s);
+	    ret = CSCF_RETURN_FALSE;
+	    goto error;
+	}
+    }
 
 
     //get cseq
     //get cseq
     remote_cseq = cscf_get_cseq(msg, 0);
     remote_cseq = cscf_get_cseq(msg, 0);
@@ -409,8 +994,8 @@ int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
     }
     }
 
 
     //get the presentity uri from To Header
     //get the presentity uri from To Header
-    cscf_get_to_uri(msg, &presentity_uri);
-
+    //cscf_get_to_uri(msg, &presentity_uri);
+	
     //get the watcher uri from the to header
     //get the watcher uri from the to header
     cscf_get_from_uri(msg, &watcher_impu);
     cscf_get_from_uri(msg, &watcher_impu);
 
 
@@ -465,7 +1050,7 @@ int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
         ul.lock_udomain(domain, &presentity_uri);
         ul.lock_udomain(domain, &presentity_uri);
         res = ul.get_impurecord(domain, &presentity_uri, &presentity_impurecord);
         res = ul.get_impurecord(domain, &presentity_uri, &presentity_impurecord);
         if (res != 0) {
         if (res != 0) {
-            LM_DBG("usrloc does not have imprecord for presnetity being subscribed too, we should create one.... TODO\n");
+            LM_DBG("usrloc does not have imprecord for presentity being subscribed too, This a problem we shouldn't get here as offline users should have been assigned in config file\n");
             ul.unlock_udomain(domain, &presentity_uri);
             ul.unlock_udomain(domain, &presentity_uri);
             ret = CSCF_RETURN_FALSE;
             ret = CSCF_RETURN_FALSE;
             goto error;
             goto error;
@@ -474,7 +1059,7 @@ int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
         LM_DBG("Received impurecord for presentity being subscribed to [%.*s]\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s);
         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, &reg_subscriber);
         res = ul.get_subscriber(presentity_impurecord, &presentity_uri, &watcher_contact, event_i, &reg_subscriber);
-        if (res != 0) {
+	if (res != 0) {
             LM_DBG("this must be a new subscriber, lets add it\n");
             LM_DBG("this must be a new subscriber, lets add it\n");
             res = ul.add_subscriber(presentity_impurecord, &watcher_impu, &watcher_contact, &subscriber_data, &reg_subscriber);
             res = ul.add_subscriber(presentity_impurecord, &watcher_impu, &watcher_contact, &subscriber_data, &reg_subscriber);
             if (res != 0) {
             if (res != 0) {
@@ -510,7 +1095,7 @@ int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
         if (new_subscription) {
         if (new_subscription) {
             if (event_reg(domain, 0, 0, event_type, &presentity_uri, &watcher_contact) != 0) {
             if (event_reg(domain, 0, 0, event_type, &presentity_uri, &watcher_contact) != 0) {
                 LM_ERR("failed to send NOTIFYs for reg events\n");
                 LM_ERR("failed to send NOTIFYs for reg events\n");
-                ret = CSCF_RETURN_BREAK;
+                ret = CSCF_RETURN_ERROR;
                 goto error;
                 goto error;
             } else {
             } else {
                 LM_DBG("success sending NOTIFY\n");
                 LM_DBG("success sending NOTIFY\n");
@@ -540,7 +1125,8 @@ int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
         } else {
         } else {
             LM_DBG("subscription s exists");
             LM_DBG("subscription s exists");
             LM_DBG("deleting subscriber from usrloc");
             LM_DBG("deleting subscriber from usrloc");
-            ul.external_delete_subscriber(reg_subscriber, (udomain_t*) _t);
+            ul.external_delete_subscriber(reg_subscriber, (udomain_t*) _t, 0 /*domain is already locked*/);
+	    ul.unlock_udomain(domain, &presentity_uri);
         }
         }
         ret = CSCF_RETURN_TRUE;
         ret = CSCF_RETURN_TRUE;
         LM_DBG("Sending 200 OK to subscribing user");
         LM_DBG("Sending 200 OK to subscribing user");
@@ -548,10 +1134,12 @@ int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
     }
     }
 
 
     //free memory
     //free memory
+    if (presentity_uri.s) shm_free(presentity_uri.s); // shm_malloc in cscf_get_public_identity_from_requri or get_presentity_from_subscriber_dialog
     if (record_route.s) pkg_free(record_route.s);
     if (record_route.s) pkg_free(record_route.s);
     return ret;
     return ret;
 error:
 error:
     //free memory
     //free memory
+    if (presentity_uri.s) shm_free(presentity_uri.s); // shm_malloc in cscf_get_public_identity_from_requri or get_presentity_from_subscriber_dialog
     if (record_route.s) pkg_free(record_route.s);
     if (record_route.s) pkg_free(record_route.s);
 
 
     return ret;
     return ret;
@@ -874,6 +1462,10 @@ str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type) {
     str buf, pad;
     str buf, pad;
     char bufc[MAX_REGINFO_SIZE], padc[MAX_REGINFO_SIZE];
     char bufc[MAX_REGINFO_SIZE], padc[MAX_REGINFO_SIZE];
     int expires = -1;
     int expires = -1;
+    
+    int terminate_impu = 1;
+    
+    ucontact_t *c_tmp;
 
 
     str state, event;
     str state, event;
 
 
@@ -894,10 +1486,32 @@ str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type) {
                 //richard we only use expired and unregistered
                 //richard we only use expired and unregistered
                 (event_type == IMS_REGISTRAR_CONTACT_EXPIRED ||
                 (event_type == IMS_REGISTRAR_CONTACT_EXPIRED ||
                 event_type == IMS_REGISTRAR_CONTACT_UNREGISTERED)
                 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);
+                ){
+	    //check if impu record has any other active contacts - if not then set this to terminated - if so then keep this active
+	    //check if asserted is present in any of the path headers
+	    c_tmp = r->contacts;
+
+	    while (c_tmp) {
+		if ((strncasecmp(c_tmp->c.s, c->c.s, c_tmp->c.len) != 0)  && ((c_tmp->expires - act_time) > 0)) {
+		    LM_DBG("IMPU <%.*s> has another active contact <%.*s> so will set its state to active\n",
+			r->public_identity.len, r->public_identity.s, c_tmp->c.len, c_tmp->c.s);
+			terminate_impu = 0;
+			break;
+		}
+		c_tmp = c_tmp->next;
+	    }
+	    
+	    if(terminate_impu){
+		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);
+	    }
+	}
+        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);
         pad.len = strlen(pad.s);
         STR_APPEND(buf, pad);
         STR_APPEND(buf, pad);
         if (c) {
         if (c) {

+ 5 - 0
modules/ims_registrar_scscf/registrar_notify.h

@@ -53,6 +53,7 @@
 
 
 #define MSG_REG_SUBSCRIBE_OK "Subscription to REG saved"
 #define MSG_REG_SUBSCRIBE_OK "Subscription to REG saved"
 #define MSG_REG_UNSUBSCRIBE_OK "Subscription to REG dropped"
 #define MSG_REG_UNSUBSCRIBE_OK "Subscription to REG dropped"
+#define MSG_REG_PUBLISH_OK "Publish to REG saved"
 
 
 
 
 
 
@@ -112,6 +113,10 @@ int can_subscribe_to_reg(struct sip_msg *msg, char *str1, char *str2);
 
 
 int subscribe_to_reg(struct sip_msg *msg, char *str1, char *str2);
 int subscribe_to_reg(struct sip_msg *msg, char *str1, char *str2);
 
 
+int can_publish_reg(struct sip_msg *msg, char *str1, char *str2);
+
+int publish_reg(struct sip_msg *msg, char *str1, char *str2);
+
 int subscribe_reply(struct sip_msg *msg, int code, char *text, int *expires, str *contact);
 int subscribe_reply(struct sip_msg *msg, int code, char *text, int *expires, str *contact);
 
 
 int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int event_type, str *presentity_uri, str *watcher_contact);
 int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int event_type, str *presentity_uri, str *watcher_contact);

+ 45 - 20
modules/ims_registrar_scscf/save.c

@@ -480,13 +480,14 @@ static inline int is_impu_registered(udomain_t* _d, str* public_identity) {
             LM_DBG("IMPU <%.*s> is not currently registered\n", public_identity->len, public_identity->s);
             LM_DBG("IMPU <%.*s> is not currently registered\n", public_identity->len, public_identity->s);
             ret = 0;
             ret = 0;
         }
         }
-        //		//check valid contacts
-        //		if (res != 0) {
-        //			//valid contacts were found
-        //			ret = 1;
-        //		}
+	
+	//check valid contacts
+        if(impu->contacts == 0)
+	{
+	    LM_DBG("IMPU <%.*s> has no valid contacts\n", public_identity->len, public_identity->s);
+            ret = 0;
+	}
     }
     }
-    //ul.release_impurecord(impu);
     ul.unlock_udomain(_d, public_identity);
     ul.unlock_udomain(_d, public_identity);
     return ret;
     return ret;
 
 
@@ -591,6 +592,8 @@ static inline int unregister_contact(udomain_t* _d, str* public_identity, contac
     struct ucontact* ucontact;
     struct ucontact* ucontact;
     str callid = {0, 0};
     str callid = {0, 0};
     str path = {0, 0};
     str path = {0, 0};
+    
+    reg_subscriber *s;
 
 
     if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) {
     if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) {
         LM_ERR("Error, no public identity exists for <%.*s>\n", public_identity->len, public_identity->s);
         LM_ERR("Error, no public identity exists for <%.*s>\n", public_identity->len, public_identity->s);
@@ -601,6 +604,23 @@ static inline int unregister_contact(udomain_t* _d, str* public_identity, contac
         LM_ERR("Can't unregister contact that does not exist <%.*s>\n", chi->uri.len, chi->uri.s);
         LM_ERR("Can't unregister contact that does not exist <%.*s>\n", chi->uri.len, chi->uri.s);
         goto error;
         goto error;
     }
     }
+    
+    //Richard added this  - fix to remove subscribes that have presentity and watcher uri same as a contact aor that is being removed
+    //When UEs explicitly dereg - they don't unsubscribe, so we remove subscriptions for them
+    s = impu_rec->shead;
+    LM_DBG("Checking if there is a subscription to this IMPU that has same watcher contact as this contact");
+    while (s) {
+        
+        LM_DBG("Subscription for this impurecord: watcher uri [%.*s] presentity uri [%.*s] watcher contact [%.*s] ", s->watcher_uri.len, s->watcher_uri.s, 
+                s->presentity_uri.len, s->presentity_uri.s, s->watcher_contact.len, s->watcher_contact.s);
+        LM_DBG("Contact to be removed [%.*s] ", ucontact->c.len, ucontact->c.s);
+        if ((s->watcher_contact.len == ucontact->c.len) && (strncasecmp(s->watcher_contact.s, ucontact->c.s, ucontact->c.len) == 0)) {
+            LM_DBG("This contact has a subscription to its own status - so going to delete the subscription");
+            ul.external_delete_subscriber(s, _d, 0 /*domain is locked*/);
+        }
+        s = s->next;
+    }
+    
     if (ul.delete_ucontact(impu_rec, ucontact) != 0) {
     if (ul.delete_ucontact(impu_rec, ucontact) != 0) {
         LM_ERR("Failed to delete ucontact <%.*s>\n", chi->uri.len, chi->uri.s);
         LM_ERR("Failed to delete ucontact <%.*s>\n", chi->uri.len, chi->uri.s);
     }
     }
@@ -655,7 +675,7 @@ int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
                     } else {
                     } else {
                     	is_primary_impu = 0;
                     	is_primary_impu = 0;
                     }
                     }
-                    if (ul.update_impurecord(_d, &pi->public_identity, reg_state,
+                    if (ul.update_impurecord(_d, &pi->public_identity, reg_state, -1 /*do not change send sar on delete */,
                             pi->barring, is_primary_impu, s, ccf1, ccf2, ecf1, ecf2, &impu_rec) != 0) {
                             pi->barring, is_primary_impu, s, ccf1, ccf2, ecf1, ecf2, &impu_rec) != 0) {
                         LM_ERR("Unable to update impurecord for <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
                         LM_ERR("Unable to update impurecord for <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
                         ul.unlock_udomain(_d, &pi->public_identity);
                         ul.unlock_udomain(_d, &pi->public_identity);
@@ -706,7 +726,7 @@ int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
             if (!subscription) {
             if (!subscription) {
                 LM_ERR("No subscriber info associated with <%.*s>, not doing any implicit re-registrations\n", impu_rec->public_identity.len, impu_rec->public_identity.s);
                 LM_ERR("No subscriber info associated with <%.*s>, not doing any implicit re-registrations\n", impu_rec->public_identity.len, impu_rec->public_identity.s);
                 //update the new subscription infor for the explicit IMPU
                 //update the new subscription infor for the explicit IMPU
-                if (ul.update_impurecord(_d, public_identity, reg_state, 0 /*this is explicit so barring must be 0*/, 0, s, ccf1, ccf2,
+                if (ul.update_impurecord(_d, public_identity, reg_state, -1 /*do not change send sar on delete */, 0 /*this is explicit so barring must be 0*/, 0, s, ccf1, ccf2,
                         ecf1, ecf2, &impu_rec) != 0) {
                         ecf1, ecf2, &impu_rec) != 0) {
                     LM_ERR("Unable to update explicit impurecord for <%.*s>\n", public_identity->len, public_identity->s);
                     LM_ERR("Unable to update explicit impurecord for <%.*s>\n", public_identity->len, public_identity->s);
                 }
                 }
@@ -741,7 +761,7 @@ int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
 
 
                     //update the implicit IMPU with the new data
                     //update the implicit IMPU with the new data
                     if (ul.update_impurecord(_d, &pi->public_identity,
                     if (ul.update_impurecord(_d, &pi->public_identity,
-                            reg_state, pi->barring, 0, s, ccf1, ccf2, ecf1, ecf2,
+                            reg_state, -1 /*do not change send sar on delete */, pi->barring, 0, s, ccf1, ccf2, ecf1, ecf2,
                             &impu_rec) != 0) {
                             &impu_rec) != 0) {
                         LM_ERR("Unable to update implicit impurecord for <%.*s>.... continuing\n", pi->public_identity.len, pi->public_identity.s);
                         LM_ERR("Unable to update implicit impurecord for <%.*s>.... continuing\n", pi->public_identity.len, pi->public_identity.s);
                         ul.unlock_udomain(_d, &pi->public_identity);
                         ul.unlock_udomain(_d, &pi->public_identity);
@@ -764,7 +784,7 @@ int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
 
 
             //finally we update the explicit IMPU record with the new data
             //finally we update the explicit IMPU record with the new data
             ul.lock_udomain(_d, public_identity);
             ul.lock_udomain(_d, public_identity);
-            if (ul.update_impurecord(_d, public_identity, reg_state, 0 /*this is explicit so barring must be 0*/, 0, s, ccf1, ccf2, ecf1, ecf2, &impu_rec) != 0) {
+            if (ul.update_impurecord(_d, public_identity, reg_state, -1 /*do not change send sar on delete */, 0 /*this is explicit so barring must be 0*/, 0, s, ccf1, ccf2, ecf1, ecf2, &impu_rec) != 0) {
                 LM_ERR("Unable to update explicit impurecord for <%.*s>\n", public_identity->len, public_identity->s);
                 LM_ERR("Unable to update explicit impurecord for <%.*s>\n", public_identity->len, public_identity->s);
             }
             }
             ul.unlock_udomain(_d, public_identity);
             ul.unlock_udomain(_d, public_identity);
@@ -875,9 +895,11 @@ int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
                             LM_DBG("contacts still available after implicit dereg for IMPU: <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
                             LM_DBG("contacts still available after implicit dereg for IMPU: <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
                         else {
                         else {
                             LM_DBG("no contacts left after implicit dereg for IMPU: <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
                             LM_DBG("no contacts left after implicit dereg for IMPU: <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
-                            //no contacts left for implicit IMPU so we can delete it
-                            LM_DBG("TODO: We should check if there are any subscribers to this reg left  - if so DO NOT DELETE");
-                            ul.delete_impurecord(_d, &pi->public_identity, tmp_impu_rec);
+                            LM_DBG("Updating impu record to not send SAR on delete as this is explicit dereg");
+			    reg_state = IMS_USER_REGISTERED;//keep reg_state as it is
+			    if (ul.update_impurecord(_d, &pi->public_identity, reg_state, 0 /*do not send sar on delete */, -1 /*do not change barring*/, 0, 0, 0, 0, 0, 0, &tmp_impu_rec) != 0) {
+				    LM_ERR("Unable to update explicit impurecord for <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
+			    }
                         }
                         }
 
 
                         ul.unlock_udomain(_d, &pi->public_identity);
                         ul.unlock_udomain(_d, &pi->public_identity);
@@ -888,12 +910,15 @@ int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
                 unlock(subscription->lock);
                 unlock(subscription->lock);
             }
             }
 
 
-            if (ret == 2) {
-                //we need to remove the IMPU as there are no more contacts left
-                ul.lock_udomain(_d, public_identity);
-                LM_DBG("TODO: We should check if there are any subscribers to this reg left  - if so DO NOT DELETE");
-                ul.delete_impurecord(_d, public_identity, NULL);
-                ul.unlock_udomain(_d, public_identity);
+	    if (ret == 2) {
+                LM_DBG("no contacts left after explicit dereg for IMPU: <%.*s>\n", public_identity->len, public_identity->s);
+                LM_DBG("Updating impu record to not send SAR on delete as this is explicit dereg");
+		reg_state = IMS_USER_REGISTERED;//keep reg_state as it is
+		ul.lock_udomain(_d, public_identity);
+		if (ul.update_impurecord(_d, public_identity, reg_state, 0 /*do not send sar on delete */, -1 /*do not change barring*/, 0, 0, 0, 0, 0, 0, &tmp_impu_rec) != 0) {
+			LM_ERR("Unable to update explicit impurecord for <%.*s>\n", public_identity->len, public_identity->s);
+		}
+		ul.unlock_udomain(_d, public_identity);
             }
             }
             break;
             break;
 
 
@@ -905,7 +930,7 @@ int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
                         j++) {
                         j++) {
                     pi = &((*s)->service_profiles[i].public_identities[j]);
                     pi = &((*s)->service_profiles[i].public_identities[j]);
                     ul.lock_udomain(_d, &pi->public_identity);
                     ul.lock_udomain(_d, &pi->public_identity);
-                    if (ul.update_impurecord(_d, &pi->public_identity, reg_state,
+                    if (ul.update_impurecord(_d, &pi->public_identity, reg_state, -1 /*do not change send sar on delete */,
                             pi->barring, 0, s, ccf1, ccf2, ecf1, ecf2, &impu_rec)
                             pi->barring, 0, s, ccf1, ccf2, ecf1, ecf2, &impu_rec)
                             != 0) {
                             != 0) {
                         LM_ERR("Unable to update impurecord for <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
                         LM_ERR("Unable to update impurecord for <%.*s>\n", pi->public_identity.len, pi->public_identity.s);

+ 1 - 0
modules/ims_registrar_scscf/save.h

@@ -66,5 +66,6 @@ int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
         str* public_identity, int assignment_type, ims_subscription** s,
         str* public_identity, int assignment_type, ims_subscription** s,
         str* ccf1, str* ccf2, str* ecf1, str* ecf2, contact_for_header_t** contact_header);
         str* ccf1, str* ccf2, str* ecf1, str* ecf2, contact_for_header_t** contact_header);
 
 
+struct sip_msg* get_request_from_reply(struct sip_msg *reply);
 
 
 #endif /* SAVE_H */
 #endif /* SAVE_H */

+ 3 - 2
modules/ims_registrar_scscf/usrloc_cb.c

@@ -70,10 +70,11 @@ void ul_impu_removed(impurecord_t* r, ucontact_t* c, int type, void* param) {
     int assignment_type = AVP_IMS_SAR_USER_DEREGISTRATION;
     int assignment_type = AVP_IMS_SAR_USER_DEREGISTRATION;
     int data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE;
     int data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE;
 
 
-    //we only send SAR if the REGISTRATION state is (NOT) IMPU_NOT_REGISTERED
+    //we only send SAR if the REGISTRATION state is (NOT) IMPU_NOT_REGISTERED and if send_sar_on_delete is set
+    //send_sar_on_delete is set by default - only unset if impu is deleted due to explicit dereg
     LM_DBG("Received notification of UL IMPU removed for IMPU <%.*s>", r->public_identity.len, r->public_identity.s);
     LM_DBG("Received notification of UL IMPU removed for IMPU <%.*s>", r->public_identity.len, r->public_identity.s);
 
 
-    if (r->reg_state != IMPU_NOT_REGISTERED) {
+    if (r->reg_state != IMPU_NOT_REGISTERED && r->send_sar_on_delete) {
         LM_DBG("Sending SAR to DeRegister [%.*s] (pvt: <%.*s>)\n",
         LM_DBG("Sending SAR to DeRegister [%.*s] (pvt: <%.*s>)\n",
                 r->public_identity.len, r->public_identity.s,
                 r->public_identity.len, r->public_identity.s,
                 r->s->private_identity.len, r->s->private_identity.s);
                 r->s->private_identity.len, r->s->private_identity.s);