Quellcode durchsuchen

modules_k/rls, modules_k/presence: Support for splitting large RLS NOTIFY requests into multiple NOTIFY requests

- Many SIP devices (including proxies and SBCs) are not able to handle
  vary large SIP requests.  Requests over 64k in size when using TCP
  are a common problem.

  It is easily possible with large (and even not so large) resource
  lists for RLS to generate NOTIFY requests that are too big for some
  SIP devices to handle.

  This enhancement gives RLS the ability to split these large NOTIFY
  requests into multiple smaller requests (that still comply with the)
  RFC.

  The default behaviour is the old (send a single big NOTIFY request)
  behaviour.

- Feature added by Andrew Miller at Crocodile RCS
pd vor 14 Jahren
Ursprung
Commit
04d7cafbdd

+ 2 - 3
modules_k/presence/hash.c

@@ -360,9 +360,8 @@ int update_shtable(shtable_t htable,unsigned int hash_code,
 	}
 	else
 	{
-		subs->local_cseq= s->local_cseq;
-		s->local_cseq++;	
-		s->version= subs->version+ 1;
+		subs->local_cseq = ++s->local_cseq;
+		s->version = ++subs->version;
 	}
 	
 	if(strncmp(s->contact.s, subs->contact.s, subs->contact.len))

+ 21 - 6
modules_k/rls/README

@@ -37,6 +37,7 @@ Anca-Maria Vamanu
               3.12. rls_event (str)
               3.13. outbound_proxy (str)
               3.14. server_address (str)
+              3.15. max_notify_body_length (int)
 
         4. Exported Functions
 
@@ -64,9 +65,10 @@ Anca-Maria Vamanu
    1.12. Set rls_event parameter
    1.13. Set outbound_proxy parameter
    1.14. Set server_address parameter
-   1.15. rls_handle_subscribe usage
-   1.16. rls_handle_notify usage
-   1.17. rls_update_subs usage
+   1.15. Set max_notify_body_length parameter
+   1.16. rls_handle_subscribe usage
+   1.17. rls_handle_notify usage
+   1.18. rls_update_subs usage
 
 Chapter 1. Admin Guide
 
@@ -94,6 +96,7 @@ Chapter 1. Admin Guide
         3.12. rls_event (str)
         3.13. outbound_proxy (str)
         3.14. server_address (str)
+        3.15. max_notify_body_length (int)
 
    4. Exported Functions
 
@@ -166,6 +169,7 @@ Chapter 1. Admin Guide
    3.12. rls_event (str)
    3.13. outbound_proxy (str)
    3.14. server_address (str)
+   3.15. max_notify_body_length (int)
 
 3.1. db_url(str)
 
@@ -338,6 +342,17 @@ modparam("rls", "outbound_proxy", "sip:presence.kamailio.org")
 modparam("rls", "server_address", "sip:[email protected]:5060")
 ...
 
+3.15. max_notify_body_length (int)
+
+   The maximum size that the body of a NOTIFY message may be. If set to 0
+   (the default), no size limit is applied. Note that this refers only to
+   the body, not the complete NOTIFY message.
+
+   Example 1.15. Set max_notify_body_length parameter
+...
+modparam("rls", "max_notify_body_length", 32000)
+...
+
 4. Exported Functions
 
    4.1. rls_handle_subscribe()
@@ -353,7 +368,7 @@ modparam("rls", "server_address", "sip:[email protected]:5060")
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 1.15. rls_handle_subscribe usage
+   Example 1.16. rls_handle_subscribe usage
 ...
 For presence and rls on the same machine:
         modparam("rls", "to_presence_code", 10)
@@ -381,7 +396,7 @@ For rls only:
 
    This function can be used from REQUEST_ROUTE.
 
-   Example 1.16. rls_handle_notify usage
+   Example 1.17. rls_handle_notify usage
 ...
 if(method=="NOTIFY")
     rls_handle_notify();
@@ -400,7 +415,7 @@ if(method=="NOTIFY")
 
    This function can be used from ANY_ROUTE.
 
-   Example 1.17. rls_update_subs usage
+   Example 1.18. rls_update_subs usage
 ...
 Within event_route[xhttp:request]:
         case "PUT":

+ 17 - 0
modules_k/rls/doc/rls_admin.xml

@@ -373,6 +373,23 @@ modparam("rls", "outbound_proxy", "sip:presence.kamailio.org")
 		<programlisting format="linespecific">
 ...
 modparam("rls", "server_address", "sip:[email protected]:5060")
+...
+		</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>max_notify_body_length</varname> (int)</title>
+		<para>
+			The maximum size that the body of a NOTIFY message may be. 
+            If set to 0 (the default), no size limit is applied.
+            Note that this refers only to the body, not the complete NOTIFY message.
+		</para>
+		<example>
+		<title>Set <varname>max_notify_body_length</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rls", "max_notify_body_length", 32000)
 ...
 		</programlisting>
 		</example>

+ 248 - 204
modules_k/rls/notify.c

@@ -51,19 +51,22 @@
 
 typedef struct res_param
 {
-	xmlNodePtr list_node;
-	db1_res_t* db_result;
-	char** cid_array;
+    struct uri_link **next;
 }res_param_t;
 
+typedef struct uri_link
+{
+    char *uri;
+    struct uri_link *next;
+} uri_link_t;
+
 int resource_uri_col=0, content_type_col, pres_state_col= 0,
 	auth_state_col= 0, reason_col= 0;
 
-str* constr_rlmi_doc(db1_res_t* result, str* rl_uri, int version,
+xmlDocPtr constr_rlmi_doc(db1_res_t* result, str* rl_uri, int version,
 		xmlNodePtr rl_node, char*** cid_array,
 		str username, str domain);
-str* constr_multipart_body(db1_res_t* result,char** cid_array,
-		char* boundary_string);
+void constr_multipart_body(str *multipart_body, const str *const content_type, const str *const body, str *cid, int boundary_len, char *boundary_string);
 
 dlg_t* rls_notify_dlg(subs_t* subs);
 
@@ -72,19 +75,28 @@ void rls_notify_callback( struct cell *t, int type, struct tmcb_params *ps);
 int parse_xcap_uri(char *uri, str *host, unsigned short *port, str *path);
 int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 		xmlNodePtr *rl_node, xmlDocPtr *xmldoc);
+int add_resource_to_list(char* uri, void* param);
+int add_resource(char* uri, xmlNodePtr list_node, str *multipart_body, char * boundary_string, db1_res_t *result, int *len_est);
 
-int send_full_notify(subs_t* subs, xmlNodePtr rl_node, int version, str* rl_uri,
+int send_full_notify(subs_t* subs, xmlNodePtr rl_node, str* rl_uri,
 		unsigned int hash_code)
 {
-	str* rlmi_body= NULL;
+	xmlDocPtr rlmi_body= NULL;
+    xmlNodePtr list_node= NULL;
 	str* multipart_body= NULL;
 	db_key_t query_cols[2], update_cols[2], result_cols[7];
 	db_val_t query_vals[2], update_vals[2];
 	db1_res_t *result= NULL;
-	int n_result_cols= 0, i;
+	int n_result_cols= 0;
 	char* boundary_string;
-	char** cid_array= NULL;
 	str rlsubs_did= {0, 0};
+    str* rlmi_cont= NULL;
+    uri_link_t *uri_list_head = NULL;
+    int len_est;
+    res_param_t param;
+    char* body_buffer;
+    int size= BUF_REALLOC_SIZE;
+    int resource_added = 0; /* Flag to indicate that we have added at least one resource */
 
 	LM_DBG("start\n");
 	/* query in alfabetical order */
@@ -121,36 +133,99 @@ int send_full_notify(subs_t* subs, xmlNodePtr rl_node, int version, str* rl_uri,
 	if(result== NULL)
 		goto error;
 
-	rlmi_body= constr_rlmi_doc(result, rl_uri, version, rl_node, &cid_array, subs->from_user, subs->from_domain);
-	if(rlmi_body== NULL)
+    /* Allocate an initial buffer for the multipart body.
+	 * This buffer will be reallocated if neccessary */
+    body_buffer= (char *)pkg_malloc(size);
+	if(body_buffer== NULL)
+	{
+		ERR_MEM(PKG_MEM_STR);
+	}
+    
+    multipart_body= (str*)pkg_malloc(sizeof(str));
+	if(multipart_body== NULL)
 	{
-		LM_ERR("while constructing rlmi doc\n");
+		ERR_MEM(PKG_MEM_STR);
+	}
+
+	multipart_body->s= body_buffer;
+	multipart_body->len= 0;
+
+    /* Create an empty rlmi document */
+	len_est = create_empty_rlmi_doc(&rlmi_body, &list_node, rl_uri, subs->version, 1);
+	xmlDocSetRootElement(rlmi_body, list_node);
+
+    /* Find all the uri's to which we are subscribed */
+	param.next = &uri_list_head;
+	if(	process_list_and_exec(rl_node, subs->from_user, subs->from_domain, add_resource_to_list,(void*)(&param))< 0)
+	{
+		LM_ERR("in process_list_and_exec function\n");
 		goto error;
 	}
 
 	boundary_string= generate_string((int)time(NULL), BOUNDARY_STRING_LEN);
 	
-	if(result->n> 0)
-	{
-		multipart_body= constr_multipart_body(result, cid_array, 
-				boundary_string);
-		if(multipart_body== NULL)
+    while (uri_list_head)
+	{
+        uri_link_t *last = uri_list_head;
+        if (add_resource(uri_list_head->uri, list_node, multipart_body, boundary_string, result, &len_est) >0)
+        {
+            if (resource_added == 0)
+            {
+                /* We have exceeded our length estimate without adding any resource.
+                   We cannot send this resource, move on. */
+                LM_ERR("Failed to add a single resource %d vs %d\n", len_est, rls_max_notify_body_len);
+                uri_list_head = uri_list_head->next;
+                pkg_free(last);
+            }
+            else
+            {
+                LM_DBG("send_full_notify estimate exceeded %d vs %d\n", len_est, rls_max_notify_body_len);
+                /* If add_resource returns > 0 the resource did not fit in our size limit */
+                rlmi_cont= (str*)pkg_malloc(sizeof(str));
+                if(rlmi_cont== NULL)
+                {
+                    ERR_MEM(PKG_MEM_STR);
+                }
+                /* Where we are worried about length we won't use padding */
+                xmlDocDumpFormatMemory(rlmi_body,(xmlChar**)(void*)&rlmi_cont->s,
+                        &rlmi_cont->len, 0);
+                xmlFreeDoc(rlmi_body);
+
+                if(agg_body_sendn_update(rl_uri, boundary_string, rlmi_cont,
+                    multipart_body, subs, hash_code)< 0)
+                {
+                    LM_ERR("in function agg_body_sendn_update\n");
+                    goto error;
+                }
+                
+                /* Create a new rlmi body, but not a full_state one this time */
+                len_est = create_empty_rlmi_doc(&rlmi_body, &list_node, rl_uri, subs->version, 0);
+                xmlDocSetRootElement(rlmi_body, list_node);
+                multipart_body->len = 0;
+                resource_added = 0;
+            }
+        }
+        else
 		{
-			LM_ERR("while constructing multipart body\n");
-			goto error;
-		}
-		for(i = 0; i<result->n; i++)
-		{
-			if(cid_array[i])
-				pkg_free(cid_array[i]);
+            resource_added = 1;
+            uri_list_head = uri_list_head->next;
+            pkg_free(last);
 		}
 	}
-	pkg_free(cid_array);
-	cid_array= NULL;
+    
+	rlmi_cont= (str*)pkg_malloc(sizeof(str));
+	if(rlmi_cont== NULL)
+	{
+		ERR_MEM(PKG_MEM_STR);
+	}
+	xmlDocDumpFormatMemory(rlmi_body,(xmlChar**)(void*)&rlmi_cont->s,
+			&rlmi_cont->len, (rls_max_notify_body_len == 0));
+	xmlFreeDoc(rlmi_body);
+
 	rls_dbf.free_result(rls_db, result);
 	result= NULL;
 
-	if(agg_body_sendn_update(rl_uri, boundary_string, rlmi_body,
+	if(agg_body_sendn_update(rl_uri, boundary_string, rlmi_cont,
 		multipart_body, subs, hash_code)< 0)
 	{
 		LM_ERR("in function agg_body_sendn_update\n");
@@ -175,8 +250,8 @@ int send_full_notify(subs_t* subs, xmlNodePtr rl_node, int version, str* rl_uri,
 		goto error;
 	}
 
-	xmlFree(rlmi_body->s);
-	pkg_free(rlmi_body);
+	xmlFree(rlmi_cont->s);
+	pkg_free(rlmi_cont);
 
 	if(multipart_body)			
 	{
@@ -188,11 +263,11 @@ int send_full_notify(subs_t* subs, xmlNodePtr rl_node, int version, str* rl_uri,
 	return 0;
 error:
 
-	if(rlmi_body)
+	if(rlmi_cont)
 	{
-		if(rlmi_body->s)
-			xmlFree(rlmi_body->s);
-		pkg_free(rlmi_body);
+		if(rlmi_cont->s)
+			xmlFree(rlmi_cont->s);
+		pkg_free(rlmi_cont);
 	}
 	if(multipart_body)
 	{
@@ -201,13 +276,6 @@ error:
 		pkg_free(multipart_body);
 	}
 	
-	if(cid_array)
-	{
-		for(i= 0; i< result->n ; i++)
-			if(cid_array[i])
-				pkg_free(cid_array[i]);
-		pkg_free(cid_array);
-	}
 	if(result)
 		rls_dbf.free_result(rls_db, result);
 	if(rlsubs_did.s)
@@ -287,7 +355,8 @@ error:
 
 
 int add_resource_instance(char* uri, xmlNodePtr resource_node,
-		db1_res_t* result, char** cid_array)
+		db1_res_t* result, str *multipart_body, char * boundary_string,
+        int *len_est)
 {
 	xmlNodePtr instance_node= NULL;
 	db_row_t *row;	
@@ -295,8 +364,11 @@ int add_resource_instance(char* uri, xmlNodePtr resource_node,
 	int i, cmp_code;
 	char* auth_state= NULL;
 	int contor= 0;
-	str cid;
 	int auth_state_flag;
+    int boundary_len = strlen(boundary_string);
+    str cid;
+  	str content_type= {0, 0};
+    str body= {0, 0};
 
 	for(i= 0; i< result->n; i++)
 	{
@@ -311,16 +383,7 @@ int add_resource_instance(char* uri, xmlNodePtr resource_node,
 		if(cmp_code== 0)
 		{
 			contor++;
-			instance_node= xmlNewChild(resource_node, NULL, 
-					BAD_CAST "instance", NULL);
-			if(instance_node== NULL)
-			{
-				LM_ERR("while adding instance child\n");
-				goto error;
-			}
 		
-			xmlNewProp(instance_node, BAD_CAST "id",
-					BAD_CAST generate_string(contor, 8));
 			auth_state_flag= row_vals[auth_state_col].val.int_val;
 			auth_state= get_auth_string(auth_state_flag );
 			if(auth_state== NULL)
@@ -328,20 +391,52 @@ int add_resource_instance(char* uri, xmlNodePtr resource_node,
 				LM_ERR("bad authorization status flag\n");
 				goto error;
 			}
-			xmlNewProp(instance_node, BAD_CAST "state", BAD_CAST auth_state);
+            *len_est += strlen(auth_state) + 38; /* <instance id="12345678" state="[auth_state]" />r/n */
 
 			if(auth_state_flag & ACTIVE_STATE)
 			{
 				cid.s= generate_cid(uri, strlen(uri));
 				cid.len= strlen(cid.s);
-
-				cid_array[i]= (char*)pkg_malloc((1+ cid.len)*sizeof(char));
-				if(cid_array[i]== NULL)
+                body.s= (char*)row_vals[pres_state_col].val.string_val;
+                body.len= strlen(body.s);
+                trim(&body);
+
+                *len_est += cid.len + 8; /* cid="[cid]" */
+				content_type.s = (char*)row_vals[content_type_col].val.string_val;
+				content_type.len = strlen(content_type.s);
+				*len_est += 4 + boundary_len
+ 						 + 35
+						 + 16 + cid.len
+						 + 18 + content_type.len
+						 + 4 + body.len + 8;
+			}
+			else
+			if(auth_state_flag & TERMINATED_STATE)
 				{
-					ERR_MEM(PKG_MEM_STR);
-				}	
-				memcpy(cid_array[i], cid.s, cid.len);
-				cid_array[i][cid.len]= '\0';
+			    *len_est += strlen(row_vals[resource_uri_col].val.string_val) + 10; /* reason="[resaon]" */
+			}
+            if (rls_max_notify_body_len > 0 && *len_est > rls_max_notify_body_len)
+            {
+                /* We have a limit on body length set, and we were about to exceed it */
+                return *len_est;
+			}
+            
+            instance_node= xmlNewChild(resource_node, NULL, 
+					BAD_CAST "instance", NULL);
+			if(instance_node== NULL)
+			{
+				LM_ERR("while adding instance child\n");
+				goto error;
+			}
+		
+            /* OK, we are happy this will fit */
+			xmlNewProp(instance_node, BAD_CAST "id",
+					BAD_CAST generate_string(contor, 8));
+			xmlNewProp(instance_node, BAD_CAST "state", BAD_CAST auth_state);
+
+			if(auth_state_flag & ACTIVE_STATE)
+			{
+                constr_multipart_body (multipart_body, &content_type, &body, &cid, boundary_len, boundary_string);
 
 				xmlNewProp(instance_node, BAD_CAST "cid", BAD_CAST cid.s);
 			}
@@ -360,206 +455,156 @@ error:
 	return -1;
 }
 
-int add_resource(char* uri, void* param)
+int add_resource(char* uri, xmlNodePtr list_node, str *multipart_body, char * boundary_string, db1_res_t *result, int *len_est)
 {
-	char** cid_array= ((res_param_t*)param)->cid_array;
-	xmlNodePtr list_node= ((res_param_t*)param)->list_node;
 	xmlNodePtr resource_node= NULL;
-	db1_res_t *result= ((res_param_t*)param)->db_result;
-
-	LM_DBG("uri= %s\n", uri);
+    int res;
+
+    if (rls_max_notify_body_len > 0)
+    {
+        *len_est += strlen (uri) + 35; /* <resource uri="[uri]"></resource>/r/n */
+        if (*len_est > rls_max_notify_body_len)
+        {
+            return *len_est;
+        }
+    }
 	resource_node= xmlNewChild(list_node, NULL, BAD_CAST "resource", NULL);
 	if(resource_node== NULL)
 	{
-		LM_ERR("while adding new rsource_node\n");
 		goto error;
 	}
 	xmlNewProp(resource_node, BAD_CAST "uri", BAD_CAST uri);
 
-	if(add_resource_instance(uri, resource_node, result, cid_array)< 0)
+    res = add_resource_instance(uri, resource_node, result, multipart_body, boundary_string, len_est);
+	if(res < 0)
 	{
 		LM_ERR("while adding resource instance node\n");
 		goto error;
 	}
 
-	return 0;
+	return res;
 error:
 	return -1;
 }
 
-str* constr_rlmi_doc(db1_res_t *result, str* rl_uri, int version,
-		xmlNodePtr rl_node, char*** rlmi_cid_array,
-		str username, str domain)
+int add_resource_to_list(char* uri, void* param)
 {
-	xmlDocPtr doc= NULL;
-	xmlNodePtr list_node= NULL;
-	str* rlmi_cont= NULL;
-	int len; 
-	char* uri;
-	res_param_t param;
-	char** cid_array= NULL;
-	int n= result->n;
-
-	LM_DBG("start\n");
-	cid_array= (char**)pkg_malloc(n* sizeof(char*));
-	if(cid_array== NULL)
+    struct uri_link **next = ((res_param_t*)param)->next;
+    *next = pkg_malloc(sizeof(uri_link_t));
+    if (*next == NULL)
 	{
-		ERR_MEM(PKG_MEM_STR);
+    	LM_ERR("while creating linked list element\n");
+		goto error;
 	}
-	memset(cid_array, 0, n* sizeof(char*));
 
-	doc= xmlNewDoc(BAD_CAST "1.0");
-	if(doc== NULL)
+    (*next)->next = NULL;
+    (*next)->uri = pkg_malloc(strlen(uri) + 1);
+    if ((*next)->uri == NULL)
 	{
-		LM_ERR("while constructing new xml doc\n");
+    	LM_ERR("while creating uri store\n");
+        pkg_free(*next);
+        *next = NULL;
 		goto error;
 	}
-	list_node= xmlNewNode(NULL, BAD_CAST "list");
-	if(list_node== NULL)
+    strcpy((*next)->uri, uri);
+    ((res_param_t*)param)->next = &(*next)->next;
+
+	return 0;
+error:
+	return -1;
+}
+
+int create_empty_rlmi_doc(xmlDocPtr *rlmi_doc, xmlNodePtr *list_node, str *uri, int version, int full_state)
+{
+    /* length is an pessimitic estimate of the size of an empty document
+       We calculate it once for performance reasons.
+       We add in the uri length each time as this varies, and it is cheap to add */
+    static int length = 0;
+    char* rl_uri= NULL;
+    int len;
+    
+    /* make new rlmi and multipart documents */
+    *rlmi_doc= xmlNewDoc(BAD_CAST "1.0");
+    if(*rlmi_doc== NULL)
+    {
+        LM_ERR("when creating new xml doc\n");
+        return 0;
+    }
+    *list_node= xmlNewNode(NULL, BAD_CAST "list");
+    if(*list_node== NULL)
 	{
 		LM_ERR("while creating new xml node\n");
-		goto error;
+        return 0;
 	}
-	uri= (char*)pkg_malloc(rl_uri->len+ 1);
-	if(uri== NULL)
+    rl_uri= (char*)pkg_malloc((uri->len+ 1)* sizeof(char));
+    if(rl_uri==  NULL)
 	{
 		ERR_MEM(PKG_MEM_STR);
 	}
-	memcpy(uri, rl_uri->s, rl_uri->len);
-	uri[rl_uri->len]= '\0';
-	xmlNewProp(list_node, BAD_CAST "uri", BAD_CAST uri);
-	pkg_free(uri);
+    memcpy(rl_uri, uri->s, uri->len);
+    rl_uri[uri->len]= '\0';
 
-	xmlNewProp(list_node, BAD_CAST "xmlns",
+    xmlNewProp(*list_node, BAD_CAST "uri", BAD_CAST rl_uri);
+    xmlNewProp(*list_node, BAD_CAST "xmlns",
 			BAD_CAST "urn:ietf:params:xml:ns:rlmi");
-	xmlNewProp(list_node, BAD_CAST "version", BAD_CAST int2str(version, &len));
-	xmlNewProp(list_node, BAD_CAST "fullState", BAD_CAST "true");
-
-	xmlDocSetRootElement(doc, list_node);
+    xmlNewProp(*list_node, BAD_CAST "version",
+            BAD_CAST int2str(version, &len));
+    if (full_state)               
+        xmlNewProp(*list_node, BAD_CAST "fullState", BAD_CAST "true");
+    else
+        xmlNewProp(*list_node, BAD_CAST "fullState", BAD_CAST "false");
 	
-	/* go through the list -- and add the appropriate 'resource' nodes*/
-	
-	param.list_node= list_node;
-	param.db_result= result;
-	param.cid_array= cid_array;
-
-	if(process_list_and_exec(rl_node, username, domain, add_resource,(void*)(&param))< 0)
-	{
-		LM_ERR("in process_list_and_exec function\n");
-		goto error;
-	}
-	rlmi_cont= (str*)pkg_malloc(sizeof(str));
-	if(rlmi_cont== NULL)
-	{
-		ERR_MEM(PKG_MEM_STR);
+    xmlDocSetRootElement(*rlmi_doc, *list_node);
+    pkg_free(rl_uri);  /* xmlNewProp takes a copy, so we can free this now */
+
+    if (length == 0)
+	{
+        /* We haven't found out how big an empty doc is
+           Let's find out now ! */
+        xmlChar* dumped_document;
+        /* Where we are worried about length we won't use padding */
+        xmlDocDumpFormatMemory( *rlmi_doc,&dumped_document, &length, 0);
+        xmlFree(dumped_document);
+        length -= uri->len; /* The uri varies, so we will add it each time */
 	}
-	xmlDocDumpFormatMemory(doc,(xmlChar**)(void*)&rlmi_cont->s,
-			&rlmi_cont->len, 1);
-
-	*rlmi_cid_array= cid_array;
-	
-	xmlFreeDoc(doc);
-
-	return rlmi_cont;
-
+    return length + uri->len;
 error:
-	if(doc)
-		xmlFreeDoc(doc);
-	return NULL;	
+    return 0;
 }
 
 
-str* constr_multipart_body(db1_res_t* result, char** cid_array, 
-		char* boundary_string)
+void constr_multipart_body(str *multipart_body, const str *const content_type, const str *const body, str *cid, int boundary_len, char *boundary_string)
 {
-	char* buf= NULL;
+	char* buf= multipart_body->s;
 	int size= BUF_REALLOC_SIZE;
-	int i, length= 0;
-	db_row_t *row;	
-	db_val_t *row_vals;
-	str content_id = {0, 0};
-	str body= {0, 0};
-	str content_type= {0, 0};
+	int length= multipart_body->len;
 	int chunk_len;
-	str* multi_body= NULL;
-	str bstr = {0, 0};
 	
 	LM_DBG("start\n");
-	buf= pkg_malloc(size* sizeof(char));
-	if(buf== NULL)
-	{
-		ERR_MEM(PKG_MEM_STR);
-	}
-
-	bstr.s = boundary_string;
-	bstr.len = strlen(bstr.s);
-
-	for(i= 0; i< result->n; i++)
-	{
-		row = &result->rows[i];
-		row_vals = ROW_VALUES(row);
-	
-		if(row_vals[auth_state_col].val.int_val!= ACTIVE_STATE)
-			continue;
-	
-		body.s= (char*)row_vals[pres_state_col].val.string_val;
-		body.len= strlen(body.s);
-		trim(&body);
-		content_type.s = (char*)row_vals[content_type_col].val.string_val;
-		content_type.len = strlen(content_type.s);
-		content_id.s= cid_array[i];
-		if(content_id.s== NULL)
-		{
-			LM_ERR("No cid found in array for uri= %s\n",
-					row_vals[resource_uri_col].val.string_val);
-			goto error;
-		}
-		content_id.len = strlen(content_id.s);
 
-
-		chunk_len = 4 + bstr.len
-					+ 35
-					+ 16 + content_id.len
-					+ 18 + content_type.len
-					+ 4 + body.len + 8;
+    chunk_len = 4 + boundary_len
+                + 35
+                + 16 + cid->len
+                + 18 + content_type->len
+                + 4 + body->len + 8;
 		if(length + chunk_len >= size)
 		{
 			REALLOC_BUF
 		}
 
 		length+= sprintf(buf+ length, "--%.*s\r\n",
-				bstr.len, bstr.s);
+            boundary_len, boundary_string);
 		length+= sprintf(buf+ length, "Content-Transfer-Encoding: binary\r\n");
 		length+= sprintf(buf+ length, "Content-ID: <%.*s>\r\n",
-				content_id.len, content_id.s);
+            cid->len, cid->s);
 		length+= sprintf(buf+ length, "Content-Type: %.*s\r\n\r\n",
-				content_type.len, content_type.s);
+            content_type->len, content_type->s);
 		length+= sprintf(buf+length,"%.*s\r\n\r\n",
-				body.len, body.s);
-	}
-
-	if(length+ strlen( boundary_string)+ 7> size )
-	{
-		REALLOC_BUF
-	}
-	buf[length]= '\0';
-	
-	multi_body= (str*)pkg_malloc(sizeof(str));
-	if(multi_body== NULL)
-	{
-		ERR_MEM(PKG_MEM_STR);
-	}
-
-	multi_body->s= buf;
-	multi_body->len= length;
-
-	return multi_body;
+            body->len, body->s);
 
 error:
 
-	if(buf)
-		pkg_free(buf);
-	return NULL;
+	return;
 }
 
 str* rls_notify_extra_hdr(subs_t* subs, char* start_cid, char* boundary_string)
@@ -892,7 +937,6 @@ int process_list_and_exec(xmlNodePtr list_node, str username, str domain,
 	char* uri = NULL;
 	int res = 0;
 
-	LM_DBG("start\n");
 	for(node= list_node->children; node; node= node->next)
 	{
 		if(xmlStrcasecmp(node->name,(unsigned char*)"resource-list")==0)
@@ -1177,7 +1221,7 @@ int rls_get_resource_list(str *rl_uri, str *username, str *domain,
 	*xmldoc = xmlParseMemory(body.s, body.len);
 	if(*xmldoc==NULL)
 	{
-		LM_ERR("while parsing XML memory\n");
+		LM_ERR("while parsing XML memory. len = %d\n", body.len);
 		goto error;
 	}
 

+ 2 - 2
modules_k/rls/notify.h

@@ -47,7 +47,7 @@
 		{	ERR_MEM("constr_multipart_body");}
 
 int send_full_notify(subs_t* subs, xmlNodePtr rl_node, 
-		int version, str* rl_uri, unsigned int hash_code);
+                     str* rl_uri, unsigned int hash_code);
 
 typedef int (*list_func_t)(char* uri, void* param); 
 
@@ -59,5 +59,5 @@ char* get_auth_string(int flag);
 int agg_body_sendn_update(str* rl_uri, char* boundary_string, str* rlmi_body,
 		str* multipart_body, subs_t* subs, unsigned int hash_code);
 int rls_send_notify(subs_t* subs,str* body,char* start_cid,char* boundary_string);
-
+int create_empty_rlmi_doc(xmlDocPtr *rlmi_doc, xmlNodePtr *list_node, str *uri, int version, int full_state);
 #endif

+ 423 - 364
modules_k/rls/resource_notify.c

@@ -61,6 +61,385 @@ static str su_200_rpl     = str_init("OK");
 	dest_len= source_len;\
 	size+= source_len;
 
+int parse_rlsubs_did(char* str_did, str* callid, str* from_tag, str* to_tag)
+{
+	char* smc= NULL;
+
+	smc= strstr(str_did, RLS_DID_SEP);
+    if(smc== NULL)
+    {
+        LM_ERR("bad format for resource list Subscribe dialog"
+            " indentifier[rlsubs did]= %s\n", str_did);
+        return -1;
+    }
+	callid->s= str_did;
+	callid->len= smc- str_did;
+			
+	from_tag->s= smc+ RLS_DID_SEP_LEN;
+	smc= strstr(from_tag->s, RLS_DID_SEP);
+	if(smc== NULL)
+    {
+        LM_ERR("bad format for resource list Subscribe dialog"
+            " indentifier(rlsubs did)= %s\n", str_did);
+        return -1;
+    }
+	from_tag->len= smc- from_tag->s;
+		
+	to_tag->s= smc+ RLS_DID_SEP_LEN;
+	to_tag->len= strlen(str_did)- 2* RLS_DID_SEP_LEN- callid->len- from_tag->len;
+
+	return 0;
+}
+
+
+void get_dialog_from_did(char* did, subs_t **dialog, unsigned int *hash_code)
+{
+    str callid, to_tag, from_tag;
+    subs_t* s;
+    
+    *dialog= NULL;
+
+    /* search the subscription in rlsubs_table*/		
+    if( parse_rlsubs_did(did, &callid, &from_tag, &to_tag)< 0)
+	{
+        LM_ERR("bad format for "
+            "resource list Subscribe dialog indentifier(rlsubs did)\n");
+        return;
+	}
+    *hash_code= core_hash(&callid, &to_tag, hash_size);
+    
+    lock_get(&rls_table[*hash_code].lock);
+    s= pres_search_shtable(rls_table,callid,to_tag,from_tag,*hash_code);
+    if(s== NULL)
+	{
+        LM_ERR("record not found in hash_table [rlsubs_did]= %s\n",
+                did);
+        lock_release(&rls_table[*hash_code].lock);
+        return;
+	}
+
+    /* save dialog info */
+    *dialog= pres_copy_subs(s, PKG_MEM_TYPE);
+    if(*dialog== NULL)
+	{
+        LM_ERR("while copying subs_t structure\n");
+	}
+    lock_release(&rls_table[*hash_code].lock);
+	
+}
+
+int send_notify(xmlDocPtr * rlmi_doc, char * buf, int buf_len, 
+                 const str bstr, subs_t * dialog, unsigned int hash_code)
+{
+    int result = 0;
+    str rlmi_cont= {0, 0}, multi_cont;
+
+    xmlDocDumpFormatMemory(*rlmi_doc,(xmlChar**)(void*)&rlmi_cont.s,
+				&rlmi_cont.len, 0);
+		
+    multi_cont.s= buf;
+    multi_cont.len= buf_len;
+
+    result =agg_body_sendn_update(&dialog->pres_uri, bstr.s, &rlmi_cont, 
+                 (buf_len==0)?NULL:&multi_cont, dialog, hash_code);
+    xmlFree(rlmi_cont.s);
+    xmlFreeDoc(*rlmi_doc);
+    *rlmi_doc= NULL;
+    return result;
+}
+
+
+void send_notifies(db1_res_t *result, int did_col, int resource_uri_col, int auth_state_col, int reason_col,
+                   int pres_state_col, int content_type_col, subs_t* subscription, int external_hash)
+{
+    int i;
+	char* prev_did= NULL, * curr_did= NULL;
+	db_row_t *row;	
+	db_val_t *row_vals;
+	char* resource_uri;
+	str pres_state = {0, 0};
+	xmlDocPtr rlmi_doc= NULL;
+	xmlNodePtr list_node= NULL, instance_node= NULL, resource_node;
+	unsigned int hash_code= 0;
+	int size= BUF_REALLOC_SIZE, buf_len= 0;	
+	char* buf= NULL, *auth_state= NULL, *boundary_string= NULL;
+	str cid = {0,0};
+	str content_type= {0, 0};
+	int contor= 0, auth_state_flag;
+	int chunk_len=0;
+	str bstr= {0, 0};
+	subs_t* dialog= NULL;
+    int len_est = 0;
+    int resource_added = 0; /* Flag to indicate that we have added at least one resource */
+
+	/* generate the boundary string */
+    boundary_string= generate_string((int)time(NULL), BOUNDARY_STRING_LEN);
+	bstr.len= strlen(boundary_string);
+	bstr.s= (char*)pkg_malloc((bstr.len+ 1)* sizeof(char));
+	if(bstr.s== NULL)
+	{
+		ERR_MEM(PKG_MEM_STR);
+	}
+	memcpy(bstr.s, boundary_string, bstr.len);
+	bstr.s[bstr.len]= '\0';
+
+	/* Allocate an initial buffer for the multipart body.
+	 * This buffer will be reallocated if neccessary */
+	buf= pkg_malloc(size* sizeof(char));
+	if(buf== NULL)
+	{
+		ERR_MEM(PKG_MEM_STR);
+	}
+
+    if (subscription == NULL)
+	{
+        dialog = subscription;
+        /* If we have been given a subscription to use
+           We should also have been given a hash value. */
+        hash_code = external_hash;
+	}
+
+	LM_DBG("found %d records with updated state\n", result->n);
+	for(i= 0; i< result->n; i++)
+	{
+		row = &result->rows[i];
+		row_vals = ROW_VALUES(row);
+		
+		curr_did=     (char*)row_vals[did_col].val.string_val;
+        resource_uri= (char*)row_vals[resource_uri_col].val.string_val;
+		auth_state_flag=     row_vals[auth_state_col].val.int_val;
+		pres_state.s=   (char*)row_vals[pres_state_col].val.string_val;
+		pres_state.len = strlen(pres_state.s);
+		trim(&pres_state);
+		
+        /* If we have moved onto a new resource list Subscribe dialog indentifier, 
+           Send a NOTIFY for the previous ID and then drop the existing documents. */
+		if(prev_did!= NULL && strcmp(prev_did, curr_did)) 
+		{
+            if (send_notify(&rlmi_doc, buf, buf_len, bstr, dialog, hash_code))
+            {  
+                LM_ERR("in send_notify\n");
+                goto error;
+            }
+            len_est = 0;
+            
+            if (subscription == NULL)
+            {
+                pkg_free(dialog);
+                dialog= NULL;
+            }
+        }
+
+		/*if first or different*/
+		if(prev_did==NULL || strcmp(prev_did, curr_did)!=0)
+        {
+            if (subscription == NULL)
+            {
+                /* We have not been given a subscription
+                   Work it out from the did. */
+                get_dialog_from_did(curr_did, &dialog, &hash_code);
+                if(dialog== NULL)
+                {
+                    prev_did = NULL;
+                    LM_ERR("Dialog is NULL\n");
+                    continue;
+                }
+            }
+		
+            len_est = create_empty_rlmi_doc(&rlmi_doc, &list_node, &dialog->pres_uri, dialog->version, 0);
+            len_est += 2*strlen(boundary_string)+4+102+2+50+strlen(resource_uri)+20;
+			buf_len= 0;
+
+			/* !!!! for now I will include the auth state without checking if 
+			 * it has changed - > in future chech if it works */		
+        }
+
+		/* add a node in rlmi_doc and if any presence state registered add 
+		 * it in the buffer */
+		
+		resource_node= xmlNewChild(list_node,NULL,BAD_CAST "resource", NULL);
+		if(resource_node== NULL)
+        {
+			LM_ERR("when adding resource child\n");
+			goto done;
+        }
+		xmlNewProp(resource_node, BAD_CAST "uri", BAD_CAST resource_uri);
+        len_est += strlen (resource_uri) + 35; /* <resource uri="[uri]"></resource>/r/n */
+
+		/* there might be more records with the same uri- more instances-
+		 * search and add them all */
+		
+		contor= 0;
+		while(1)
+		{
+			contor++;
+			cid.s= NULL;
+			cid.len= 0;
+			
+			auth_state= get_auth_string(auth_state_flag);
+			if(auth_state== NULL)
+            {
+				LM_ERR("bad authorization status flag\n");
+                goto error;
+            }	
+			len_est += strlen(auth_state) + 38; /* <instance id="12345678" state="[auth_state]" />r/n */
+
+			if(auth_state_flag & ACTIVE_STATE)
+            {
+				cid.s= generate_cid(resource_uri, strlen(resource_uri));
+                cid.len = strlen(cid.s);
+                len_est += cid.len + 8; /* cid="[cid]" */
+				content_type.s = (char*)row_vals[content_type_col].val.string_val;
+				content_type.len = strlen(content_type.s);
+				chunk_len = 4 + bstr.len
+							+ 35
+							+ 16 + cid.len
+							+ 18 + content_type.len
+							+ 4 + pres_state.len + 8;
+                len_est += chunk_len;
+            }
+			else
+			if(auth_state_flag & TERMINATED_STATE)
+            {
+			    len_est += strlen(row_vals[resource_uri_col].val.string_val) + 10; /* reason="[resaon]" */
+            }
+            
+            if (rls_max_notify_body_len > 0 && len_est > rls_max_notify_body_len)
+            {
+                /* We have a limit on body length set, and we were about to exceed it */
+                if (resource_added == 1)
+                {
+                    /* We added at least one resource. */
+                    LM_ERR("timer_send_notify hit the size limit. len_est = %d\n", len_est);
+                    if (send_notify(&rlmi_doc, buf, buf_len, bstr, dialog, hash_code))
+                    {
+                        LM_ERR("in send_notify\n");
+                        goto error;
+                    }
+                    i --;
+                }
+                else
+                {
+                    LM_ERR("timer_send_notify hit the size limit. NO RESOURCE ADDED len_est = %d\n", len_est);
+                }
+                len_est = 0;
+
+                if (subscription == NULL)
+                {
+                    pkg_free(dialog);
+                    dialog= NULL;
+                }
+                
+                curr_did=NULL;
+                break;
+            }
+
+            /* OK, we are happy this will fit */
+            instance_node= xmlNewChild(resource_node, NULL, BAD_CAST "instance", NULL);
+            if(instance_node== NULL)
+            {
+				LM_ERR("while adding instance child\n");
+				goto error;
+            }	
+
+            xmlNewProp(instance_node, BAD_CAST "id", 
+					BAD_CAST generate_string(contor, 8));
+            if(auth_state_flag & ACTIVE_STATE)
+            {
+                xmlNewProp(instance_node, BAD_CAST "state", BAD_CAST auth_state);
+            }
+			else
+			if(auth_state_flag & TERMINATED_STATE)
+            {
+            	xmlNewProp(instance_node, BAD_CAST "reason",
+					BAD_CAST row_vals[resource_uri_col].val.string_val);
+            }
+            xmlNewProp(instance_node, BAD_CAST "cid", BAD_CAST cid.s);
+
+			/* add in the multipart buffer */
+			if(cid.s)
+			{
+	
+				if(buf_len + chunk_len >= size)
+				{
+					REALLOC_BUF
+				}
+				buf_len+= sprintf(buf+ buf_len, "--%.*s\r\n", bstr.len,
+						bstr.s);
+				buf_len+= sprintf(buf+ buf_len,
+						"Content-Transfer-Encoding: binary\r\n");
+				buf_len+= sprintf(buf+ buf_len, "Content-ID: <%.*s>\r\n",
+						cid.len, cid.s);
+				buf_len+= sprintf(buf+ buf_len, "Content-Type: %.*s\r\n\r\n",
+						content_type.len, content_type.s);
+				buf_len+= sprintf(buf+buf_len,"%.*s\r\n\r\n", pres_state.len,
+						pres_state.s);
+			}
+
+			i++;
+			if(i== result->n)
+			{
+				i--;
+				break;
+			}
+	
+			row = &result->rows[i];
+			row_vals = ROW_VALUES(row);
+
+			if(strncmp(resource_uri, row_vals[resource_uri_col].val.string_val,
+					strlen(resource_uri))
+				|| strncmp(curr_did, row_vals[did_col].val.string_val,
+					strlen(curr_did)))
+			{
+				i--;
+				break;
+			}
+			resource_uri= (char*)row_vals[resource_uri_col].val.string_val;
+			auth_state_flag=     row_vals[auth_state_col].val.int_val;
+			pres_state.s=   (char*)row_vals[pres_state_col].val.string_val;
+			pres_state.len= strlen(pres_state.s);
+			trim(&pres_state);
+		}
+
+		prev_did= curr_did;
+	}
+
+	if(rlmi_doc)
+	{
+        LM_ERR("timer_send_notify at end len_est = %d resource_added = %d\n", len_est, resource_added);
+        if (resource_added == 1)
+        {
+            send_notify(&rlmi_doc, buf, buf_len, bstr, dialog, hash_code);
+        }
+        if (subscription == NULL)
+        {
+            /* We are using a derived subscription */
+            if(dialog)
+            {
+                pkg_free(dialog);
+            }
+            dialog= NULL;
+        }
+	}
+
+	
+error:
+done:
+	if(bstr.s)
+		pkg_free(bstr.s);
+
+	if(buf)
+		pkg_free(buf);
+    if (subscription == NULL)
+	{
+        /* We are using a derived subscription */
+        if(dialog)
+            pkg_free(dialog);
+    }
+	return;
+}
+
+
 int parse_subs_state(str auth_state, str** reason, int* expires)
 {
 	str str_exp;
@@ -87,10 +466,10 @@ int parse_subs_state(str auth_state, str** reason, int* expires)
 		{
 			LM_ERR("terminated state and no reason found");
 			return -1;
-		}
+        }
 		res= (str*)pkg_malloc(sizeof(str));
 		if(res== NULL)
-		{
+        {
 			ERR_MEM(PKG_MEM_STR);
 		}
 		len=  auth_state.len- 10- 1- 7;
@@ -103,7 +482,7 @@ int parse_subs_state(str auth_state, str** reason, int* expires)
 		res->len= len;
 		return TERMINATED_STATE;
 	}
-
+	
 	if(flag> 0)
 	{
 		smc= strchr(auth_state.s, ';');
@@ -111,7 +490,7 @@ int parse_subs_state(str auth_state, str** reason, int* expires)
 		{
 			LM_ERR("active or pending state and no expires parameter found");
 			return -1;
-		}
+        }	
 		if(strncmp(smc+1, "expires=", 8))
 		{
 			LM_ERR("active or pending state and no expires parameter found");
@@ -121,10 +500,10 @@ int parse_subs_state(str auth_state, str** reason, int* expires)
 		str_exp.len= auth_state.s+ auth_state.len- smc- 9;
 
 		if( str2int(&str_exp, (unsigned int*)expires)< 0)
-		{
+        {
 			LM_ERR("while getting int from str\n");
 			return -1;
-		}
+        }
 		return flag;
 	
 	}
@@ -175,9 +554,9 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 	{
 		LM_ERR("cannot parse TO header\n");
 		return -1;
-	}
+    }
 	if(msg->to->parsed != NULL)
-	{
+    {
 		pto = (struct to_body*)msg->to->parsed;
 		LM_DBG("'To' header ALREADY PARSED: <%.*s>\n",
 				pto->uri.len, pto->uri.s );	
@@ -190,13 +569,13 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 		{
 			LM_ERR(" 'To' header NOT parsed\n");
 			return -1;
-		}
+	}
 		pto = &TO;
 	}
 	memset(&dialog, 0, sizeof(ua_pres_t));
 	dialog.watcher_uri= &pto->uri;
     if (pto->tag_value.s==NULL || pto->tag_value.len==0 )
-	{  
+	{
 		LM_ERR("to tag value not parsed\n");
 		goto error;
 	}
@@ -220,12 +599,12 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 		if ( parse_from_header( msg )<0 ) 
 		{
 			LM_ERR("cannot parse From header\n");
-			goto error;
-		}
+            goto error;
+        }
 	}
 	pfrom = (struct to_body*)msg->from->parsed;
 	dialog.pres_uri= &pfrom->uri;
-		
+
 	if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0)
 	{
 		LM_ERR("no from tag value present\n");
@@ -256,7 +635,7 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 	{
 		LM_ERR("'Subscription-State' header not found\n");
 		goto error;
-	}	
+	}
 	auth_state = hdr->body;
 
 	/* extract state and reason */
@@ -266,7 +645,7 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 		LM_ERR("while parsing 'Subscription-State' header\n");
 		goto error;
 	}
-	if(pua_get_record_id(&dialog, &res_id)< 0) // verify if within a stored dialog
+	if(pua_get_record_id(&dialog, &res_id)< 0) /* verify if within a stored dialog */
 	{
 		LM_ERR("occured when trying to get record id\n");
 		goto error;
@@ -280,22 +659,23 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 		 */
 		if(auth_flag==TERMINATED_STATE)
 			goto done;
-		LM_ERR("no presence dialog record for non-TERMINATED state\n");
+        LM_ERR("no presence dialog record for non-TERMINATED state uri pres_uri = %.*s watcher_uri = %.*s\n",
+                dialog.pres_uri->len, dialog.pres_uri->s, dialog.watcher_uri->len, dialog.watcher_uri->s);
 		goto error;
 	}
-
+		
 	if(msg->content_type== NULL || msg->content_type->body.s== NULL)
 	{
 		LM_DBG("cannot find content type header header\n");
 	}
 	else
 		content_type= msg->content_type->body;
-
+					
 	/*constructing the xml body*/
 	if(get_content_length(msg) == 0 )
-	{
-		goto done;
-	}	
+    {	
+        goto done;
+    }	
 	else
 	{
 		if(content_type.s== 0)
@@ -313,7 +693,7 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 
 	}
 	/* update in rlpres_table where rlsusb_did= res_id and resource_uri= from_uri*/
-	
+
 	LM_DBG("body= %.*s\n", body.len, body.s);
 
 	query_cols[n_query_cols]= &str_rlsubs_did_col;
@@ -321,7 +701,7 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 	query_vals[n_query_cols].nul = 0;
 	query_vals[n_query_cols].val.str_val= *res_id; 
 	n_query_cols++;
-	
+
 	query_cols[n_query_cols]= &str_resource_uri_col;
 	query_vals[n_query_cols].type = DB1_STR;
 	query_vals[n_query_cols].nul = 0;
@@ -333,7 +713,7 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 	query_vals[n_query_cols].nul = 0;
 	query_vals[n_query_cols].val.int_val= UPDATED_TYPE; 
 	n_query_cols++;
-
+		
 	query_cols[n_query_cols]= &str_auth_state_col;
 	query_vals[n_query_cols].type = DB1_INT;
 	query_vals[n_query_cols].nul = 0;
@@ -353,13 +733,13 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 	query_vals[n_query_cols].nul = 0;
 	query_vals[n_query_cols].val.str_val= content_type;
 	n_query_cols++;
-
+			
 	query_cols[n_query_cols]= &str_presence_state_col;
 	query_vals[n_query_cols].type = DB1_STR;
 	query_vals[n_query_cols].nul = 0;
 	query_vals[n_query_cols].val.str_val= body;
 	n_query_cols++;
-	
+		
 	query_cols[n_query_cols]= &str_expires_col;
 	query_vals[n_query_cols].type = DB1_INT;
 	query_vals[n_query_cols].nul = 0;
@@ -373,7 +753,7 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 	}
 	/* query-> if not present insert // else update */
 	result_cols[0]= &str_updated_col;
-
+			
 	if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols,
 					2, 1, 0, &result)< 0)
 	{
@@ -386,7 +766,7 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 		goto error;
 	n= result->n;
 	rls_dbf.free_result(rls_db, result);
-
+		
 	if(n<= 0)
 	{
 		if(rls_dbf.insert(rls_db, query_cols, query_vals, n_query_cols)< 0)
@@ -406,7 +786,7 @@ int rls_handle_notify(struct sip_msg* msg, char* c1, char* c2)
 			goto error;
 		}
 	}
-	
+
 	LM_DBG("Updated rlpres_table\n");	
 	/* reply 200OK */
 done:
@@ -414,7 +794,7 @@ done:
 	{
 		LM_ERR("while sending reply\n");
 		goto error;
-	}	
+	}
 
 	if(res_id!=NULL)
 	{
@@ -430,38 +810,8 @@ error:
 		pkg_free(res_id);
 	}
 	return -1;
-}
-/* callid, from_tag, to_tag parameters must be allocated */
-
-int parse_rlsubs_did(char* str_did, str* callid, str* from_tag, str* to_tag)
-{
-	char* smc= NULL;
-
-	smc= strstr(str_did, RLS_DID_SEP);
-	if(smc== NULL)
-	{
-		LM_ERR("bad format for resource list Subscribe dialog"
-				" indentifier[rlsubs did]= %s\n", str_did);
-		return -1;
 	}
-	callid->s= str_did;
-	callid->len= smc- str_did;
-			
-	from_tag->s= smc+ RLS_DID_SEP_LEN;
-	smc= strstr(from_tag->s, RLS_DID_SEP);
-	if(smc== NULL)
-	{
-		LM_ERR("bad format for resource list Subscribe dialog"
-				" indentifier(rlsubs did)= %s\n", str_did);
-		return -1;
-	}
-	from_tag->len= smc- from_tag->s;
-		
-	to_tag->s= smc+ RLS_DID_SEP_LEN;
-	to_tag->len= strlen(str_did)- 2* RLS_DID_SEP_LEN- callid->len- from_tag->len;
-
-	return 0;
-}
+/* callid, from_tag, to_tag parameters must be allocated */
 
 void timer_send_notify(unsigned int ticks,void *param)
 {
@@ -469,29 +819,9 @@ void timer_send_notify(unsigned int ticks,void *param)
 	db_val_t query_vals[2], update_vals[1];
 	int did_col, resource_uri_col, auth_state_col, reason_col,
 		pres_state_col, content_type_col;
-	int n_result_cols= 0, i;
+	int n_result_cols= 0;
 	db1_res_t *result= NULL;
-	char* prev_did= NULL, * curr_did= NULL;
-	db_row_t *row;	
-	db_val_t *row_vals;
-	char* resource_uri;
-	str pres_state = {0, 0};
-	str callid, to_tag, from_tag;
-	xmlDocPtr rlmi_doc= NULL;
-	xmlNodePtr list_node= NULL, instance_node= NULL, resource_node;
-	unsigned int hash_code= 0;
-	int len;
-	int size= BUF_REALLOC_SIZE, buf_len= 0;	
-	char* buf= NULL, *auth_state= NULL, *boundary_string= NULL;
-	str cid = {0,0};
-	str content_type = {0,0};
-	int contor= 0, auth_state_flag;
-	int chunk_len;
-	str bstr= {0, 0};
-	str rlmi_cont= {0, 0}, multi_cont;
-	subs_t* s, *dialog= NULL;
-	char* rl_uri= NULL;
-
+		
 	query_cols[0]= &str_updated_col;
 	query_vals[0].type = DB1_INT;
 	query_vals[0].nul = 0;
@@ -504,9 +834,14 @@ void timer_send_notify(unsigned int ticks,void *param)
 	result_cols[reason_col= n_result_cols++]= &str_reason_col;
 	result_cols[pres_state_col= n_result_cols++]= &str_presence_state_col;
 
-	/* query in alfabetical order after rlsusbs_did 
-	 * (resource list Subscribe dialog indentifier)*/
+	update_cols[0]= &str_updated_col;
+	update_vals[0].type = DB1_INT;
+	update_vals[0].nul = 0;
+	update_vals[0].val.int_val= NO_UPDATE_TYPE; 
 
+	/* query in alphabetical order after rlsusbs_did 
+	 * (resource list Subscribe dialog indentifier)*/
+	
 	if (rls_dbf.use_table(rls_db, &rlpres_table) < 0) 
 	{
 		LM_ERR("in use_table\n");
@@ -523,16 +858,6 @@ void timer_send_notify(unsigned int ticks,void *param)
 		goto done;
 
 	/* update the rlpres table */
-	update_cols[0]= &str_updated_col;
-	update_vals[0].type = DB1_INT;
-	update_vals[0].nul = 0;
-	update_vals[0].val.int_val= NO_UPDATE_TYPE; 
-
-	if (rls_dbf.use_table(rls_db, &rlpres_table) < 0) 
-	{
-		LM_ERR("in use_table\n");
-		goto error;
-	}
 	if(rls_dbf.update(rls_db, query_cols, 0, query_vals, update_cols,
 					update_vals, 1, 1)< 0)
 	{
@@ -540,279 +865,13 @@ void timer_send_notify(unsigned int ticks,void *param)
 		goto error;
 	}
 
-	/* generate the boundary string */
-
-	boundary_string= generate_string((int)time(NULL), BOUNDARY_STRING_LEN);
-	bstr.len= strlen(boundary_string);
-	bstr.s= (char*)pkg_malloc((bstr.len+ 1)* sizeof(char));
-	if(bstr.s== NULL)
-	{
-		ERR_MEM(PKG_MEM_STR);
-	}
-	memcpy(bstr.s, boundary_string, bstr.len);
-	bstr.s[bstr.len]= '\0';
-
-	/* for the multipart body , use here also an initial allocated
-	 * and reallocated on need buffer */
-	buf= pkg_malloc(size* sizeof(char));
-	if(buf== NULL)
-	{
-		ERR_MEM(PKG_MEM_STR);
-	}
-
-	LM_DBG("found %d records with updated state\n", result->n);
-	for(i= 0; i< result->n; i++)
-	{
-		row = &result->rows[i];
-		row_vals = ROW_VALUES(row);
-		
-		curr_did=     (char*)row_vals[did_col].val.string_val;
-		resource_uri= (char*)row_vals[resource_uri_col].val.string_val;
-		auth_state_flag=     row_vals[auth_state_col].val.int_val;
-		pres_state.s=   (char*)row_vals[pres_state_col].val.string_val;
-		pres_state.len = strlen(pres_state.s);
-		trim(&pres_state);
-		
-		if(prev_did!= NULL && strcmp(prev_did, curr_did)) 
-		{
-			xmlDocDumpFormatMemory(rlmi_doc,(xmlChar**)(void*)&rlmi_cont.s,
-				&rlmi_cont.len, 1);
-		
-			multi_cont.s= buf;
-			multi_cont.len= buf_len;
-
-			 if(agg_body_sendn_update(&dialog->pres_uri, bstr.s, &rlmi_cont, 
-						 (buf_len==0)?NULL:&multi_cont, dialog, hash_code)<0)
-			 {
-				 LM_ERR("in function agg_body_sendn_update\n");
-				 goto error;
-			 }
-			xmlFree(rlmi_cont.s);
-			xmlFreeDoc(rlmi_doc);
-			rlmi_doc= NULL;
-			pkg_free(rl_uri);
-			rl_uri= NULL;
-			pkg_free(dialog);
-			dialog= NULL;
-		}
-
-		/*if first or different*/
-		if(prev_did==NULL || strcmp(prev_did, curr_did)!=0)
-		{
-			/* search the subscription in rlsubs_table*/		
-			if( parse_rlsubs_did(curr_did, &callid, &from_tag, &to_tag)< 0)
-			{
-				LM_ERR("bad format for "
-					"resource list Subscribe dialog indentifier(rlsubs did)\n");
-				prev_did = NULL;
-				continue;
-
-			}
-			hash_code= core_hash(&callid, &to_tag, hash_size);
-		
-			lock_get(&rls_table[hash_code].lock);
-			s= pres_search_shtable(rls_table,callid,to_tag,from_tag,hash_code);
-			if(s== NULL)
-			{
-				LM_DBG("record not found in hash_table [rlsubs_did]= %s\n",
-						curr_did);
-				LM_DBG("callid= %.*s\tfrom_tag= %.*s\tto_tag= %.*s\n",
-						callid.len, callid.s,from_tag.len,from_tag.s,
-						to_tag.len,to_tag.s);
-				lock_release(&rls_table[hash_code].lock);
-				prev_did = NULL;
-				continue;
-			}
-			LM_DBG("Found rl-subs record in hash table\n");
-					
-			/* save dialog info and rl_uri*/
-			dialog= pres_copy_subs(s, PKG_MEM_TYPE);
-			if(dialog== NULL)
-			{	
-				LM_ERR("while copying subs_t structure\n");
-				lock_release(&rls_table[hash_code].lock);
-				goto done;
-			}	
-			lock_release(&rls_table[hash_code].lock);
-
-			/* make new rlmi and multipart documents */
-			rlmi_doc= xmlNewDoc(BAD_CAST "1.0");
-			if(rlmi_doc== NULL)
-			{
-				LM_ERR("when creating new xml doc\n");
-				goto done;
-			}
-			list_node= xmlNewNode(NULL, BAD_CAST "list");
-			if(list_node== NULL)
-			{
-				LM_ERR("while creating new xml node\n");
-				goto done;
-			}
-			rl_uri= (char*)pkg_malloc((dialog->pres_uri.len+ 1)* sizeof(char));
-			if(rl_uri==  NULL)
-			{
-				ERR_MEM(PKG_MEM_STR);
-			}
-			memcpy(rl_uri, dialog->pres_uri.s, dialog->pres_uri.len);
-			rl_uri[dialog->pres_uri.len]= '\0';
-
-			xmlNewProp(list_node, BAD_CAST "uri", BAD_CAST rl_uri);
-			xmlNewProp(list_node, BAD_CAST "xmlns",
-					BAD_CAST "urn:ietf:params:xml:ns:rlmi");
-			xmlNewProp(list_node, BAD_CAST "version",
-					BAD_CAST int2str(dialog->version, &len));
-			xmlNewProp(list_node, BAD_CAST "fullState", BAD_CAST "false");
-
-			xmlDocSetRootElement(rlmi_doc, list_node);
-			buf_len= 0;
-
-			/* !!!! for now I will include the auth state without checking if 
-			 * it has changed - > in future chech if it works */		
-		}		
-
-		/* add a node in rlmi_doc and if any presence state registered add 
-		 * it in the buffer */
-		
-		resource_node= xmlNewChild(list_node,NULL,BAD_CAST "resource", NULL);
-		if(resource_node== NULL)
-		{
-			LM_ERR("when adding resource child\n");
-			goto done;
-		}
-		xmlNewProp(resource_node, BAD_CAST "uri", BAD_CAST resource_uri);
-			
-		/* there might be more records with the same uri- more instances-
-		 * search and add them all */
-		
-		contor= 0;
-		while(1)
-		{
-			contor++;
-			cid.s= NULL;
-			cid.len= 0;
-			instance_node= xmlNewChild(resource_node, NULL, 
-					BAD_CAST "instance", NULL);
-			if(instance_node== NULL)
-			{
-				LM_ERR("while adding instance child\n");
-				goto error;
-			}
-			xmlNewProp(instance_node, BAD_CAST "id", 
-					BAD_CAST generate_string(contor, 8));
-			
-			auth_state= get_auth_string(auth_state_flag);
-			if(auth_state== NULL)
-			{
-				LM_ERR("bad authorization status flag\n");
-				goto error;
-			}
-			xmlNewProp(instance_node, BAD_CAST "state", BAD_CAST auth_state);
-		
-			if(auth_state_flag & ACTIVE_STATE)
-			{
-				cid.s= generate_cid(resource_uri, strlen(resource_uri));
-				xmlNewProp(instance_node, BAD_CAST "cid", BAD_CAST cid.s);
-			}
-			else
-			if(auth_state_flag & TERMINATED_STATE)
-			{
-				xmlNewProp(instance_node, BAD_CAST "reason",
-					BAD_CAST row_vals[resource_uri_col].val.string_val);
-			}
-		
-			/* add in the multipart buffer */
-			if(cid.s)
-			{
-				cid.len = strlen(cid.s);
-				content_type.s = (char*)row_vals[content_type_col].val.string_val;
-				content_type.len = strlen(content_type.s);
-				chunk_len = 4 + bstr.len
-							+ 35
-							+ 16 + cid.len
-							+ 18 + content_type.len
-							+ 4 + pres_state.len + 8;
-				if(buf_len + chunk_len >= size)
-				{
-					REALLOC_BUF
-				}
-				buf_len+= sprintf(buf+ buf_len, "--%.*s\r\n", bstr.len,
-						bstr.s);
-				buf_len+= sprintf(buf+ buf_len,
-						"Content-Transfer-Encoding: binary\r\n");
-				buf_len+= sprintf(buf+ buf_len, "Content-ID: <%.*s>\r\n",
-						cid.len, cid.s);
-				buf_len+= sprintf(buf+ buf_len, "Content-Type: %.*s\r\n\r\n",
-						content_type.len, content_type.s);
-				buf_len+= sprintf(buf+buf_len,"%.*s\r\n\r\n", pres_state.len,
-						pres_state.s);
-			}
-
-			i++;
-			if(i== result->n)
-			{
-				i--;
-				break;
-			}
-
-			row = &result->rows[i];
-			row_vals = ROW_VALUES(row);
-		
-			if(strncmp(resource_uri, row_vals[resource_uri_col].val.string_val,
-					strlen(resource_uri))
-				|| strncmp(curr_did, row_vals[did_col].val.string_val,
-					strlen(curr_did)))
-			{
-				i--;
-				break;
-			}
-			resource_uri= (char*)row_vals[resource_uri_col].val.string_val;
-			auth_state_flag=     row_vals[auth_state_col].val.int_val;
-			pres_state.s=   (char*)row_vals[pres_state_col].val.string_val;
-			pres_state.len= strlen(pres_state.s);
-			trim(&pres_state);
-		}
-
-		prev_did= curr_did;
-	}
-
-	if(rlmi_doc)
-	{
-		xmlDocDumpFormatMemory( rlmi_doc,(xmlChar**)(void*)&rlmi_cont.s,
-		&rlmi_cont.len, 1);
-		
-		multi_cont.s= buf;
-		multi_cont.len= buf_len;
-	
-		 if(agg_body_sendn_update(&dialog->pres_uri, bstr.s, &rlmi_cont, 
-			 (buf_len==0)?NULL:&multi_cont, dialog, hash_code)<0)
-		 {
-			 LM_ERR("in function agg_body_sendn_update\n");
-			 goto error;
-		}
-		xmlFree(rlmi_cont.s);
-		pkg_free(rl_uri);
-		rl_uri= NULL;
-		pkg_free(dialog);
-		dialog= NULL;
-	}
-
-
+    send_notifies(result, did_col, resource_uri_col, auth_state_col, reason_col,
+                  pres_state_col, content_type_col,
+                  NULL, 0);  /* Work out the subscription */
 error:
 done:
 	if(result)
 		rls_dbf.free_result(rls_db, result);
-	if(rlmi_doc)
-		xmlFreeDoc(rlmi_doc);
-	if(rl_uri)
-		pkg_free(rl_uri);
-	if(bstr.s)
-		pkg_free(bstr.s);
-		
-	if(buf)
-		pkg_free(buf);
-	if(dialog)
-		pkg_free(dialog);
-	return;
 }
 
 

+ 2 - 0
modules_k/rls/rls.c

@@ -106,6 +106,7 @@ int rls_events= EVENT_PRESENCE;
 int to_presence_code = 1;
 int rls_max_expires = 7200;
 int rls_reload_db_subs = 0;
+int rls_max_notify_body_len = 0;
 
 /* functions imported from xcap_client module */
 xcapGetNewDoc_t xcap_GetNewDoc = 0;
@@ -194,6 +195,7 @@ static param_export_t params[]={
 	{ "rls_event",              STR_PARAM|USE_FUNC_PARAM,(void*)add_rls_event},
 	{ "outbound_proxy",         STR_PARAM,   &rls_outbound_proxy.s           },
 	{ "reload_db_subs",         INT_PARAM,   &rls_reload_db_subs             },
+	{ "max_notify_body_length", INT_PARAM,	 &rls_max_notify_body_len	     },
 	{0,                         0,           0                               }
 };
 

+ 1 - 0
modules_k/rls/rls.h

@@ -96,6 +96,7 @@ extern int rls_integrated_xcap_server;
 extern int rls_events;
 extern int to_presence_code;
 extern str rls_outbound_proxy;
+extern int rls_max_notify_body_len;
 
 /* database connection */
 extern db1_con_t *rls_db;

+ 1 - 2
modules_k/rls/subscribe.c

@@ -623,8 +623,7 @@ int rls_handle_subscribe(struct sip_msg* msg, char* s1, char* s2)
 	}
 
 	/* sending notify with full state */
-	if(send_full_notify(&subs, service_node, subs.version, &subs.pres_uri,
-				hash_code)<0)
+	if(send_full_notify(&subs, service_node, &subs.pres_uri, hash_code)<0)
 	{
 		LM_ERR("failed sending full state notify\n");
 		goto error;