Example #1
0
static 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)
{
	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 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(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);
	}

	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;
			pkg_free(dialog);
			dialog= NULL;
		}

		/*if first or different*/
		if(prev_did==NULL || strcmp(prev_did, curr_did)!=0)
		{
			/* Work out a subscription from the did. */
			get_dialog_from_did(curr_did, &dialog, &hash_code);
			if(dialog== NULL)
			{
				prev_did = NULL;
				LM_INFO("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;
			resource_added = 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 */
		resource_added = 1;

		/* there might be more records with the same uri- more instances-
		 * search and add them all */
		
		while(1)
		{
			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_DBG("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_DBG("timer_send_notify hit the size limit. NO RESOURCE ADDED len_est = %d\n", len_est);
				}
				len_est = 0;

				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;
			}	

			/* Instance ID should be unique for each instance node
 			   within a resource node.  The same instance ID can be
			   used in different resource nodes.  Instance ID needs
			   to remain the same for each resource instance in
			   future updates.  We can just use a common string
			   here because you will only get multiple instances
			   for a resource when the back-end SUBSCRIBE is forked
			   and pua does not support this.  If/when pua supports
			   forking of the SUBSCRIBEs it sends this will need to
			   be fixed properly. */
			xmlNewProp(instance_node, BAD_CAST "id", 
					BAD_CAST instance_id);
			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)
			{
	
				while(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_DBG("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(dialog)
		{
			pkg_free(dialog);
		}
		dialog= NULL;
	}

	
error:
done:
	if(bstr.s)
		pkg_free(bstr.s);

	if(buf)
		pkg_free(buf);
	if(dialog)
		pkg_free(dialog);
	return;
}
void timer_send_notify(unsigned int ticks,void *param)
{
	db_key_t query_cols[2], update_cols[1], result_cols[7];
	db_val_t query_vals[2], update_vals[1];
	int did_col, resource_uri_col, auth_state_col, reason_col,
		body_col, ctype_col;
	int n_result_cols= 0, i;
	db_res_t *result= NULL;
	char* prev_did= NULL, * curr_did= NULL;
	db_row_t *row;	
	db_val_t *row_vals;
	char* resource_uri;
	str body;
	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;
	int contor= 0, auth_state_flag;
	str bstr= {0, 0};
	str rlmi_cont= {0, 0}, multi_cont;
	subs_t* s, *dialog= NULL;
	char* rl_uri= NULL;
	char* str_aux = NULL;
	str ctype, cid;
	int add_len;

	query_cols[0]= &str_updated_col;
	query_vals[0].type = DB_INT;
	query_vals[0].nul = 0;
	query_vals[0].val.int_val= UPDATED_TYPE; 

	result_cols[did_col= n_result_cols++]= &str_rlsubs_did_col;
	result_cols[resource_uri_col= n_result_cols++]= &str_resource_uri_col;
	result_cols[auth_state_col= n_result_cols++]= &str_auth_state_col;
	result_cols[ctype_col= n_result_cols++]= &str_content_type_col;
	result_cols[reason_col= n_result_cols++]= &str_reason_col;
	result_cols[body_col= n_result_cols++]= &str_presence_state_col;

	/* query in alfabetical 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");
		goto done;
	}

	if(rls_dbf.query(rls_db, query_cols, 0, query_vals, result_cols,
					1, n_result_cols, &str_rlsubs_did_col, &result)< 0)
	{
		LM_ERR("in sql query\n");
		goto done;
	}
	if(result== NULL || result->n<= 0)
		goto done;

	/* update the rlpres table */
	update_cols[0]= &str_updated_col;
	update_vals[0].type = DB_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)
	{
		LM_ERR("in sql update\n");
		goto error;
	}

	/* generate the boundary string */

	bstr.s= generate_string((int)time(NULL), BOUNDARY_STRING_LEN);
	if(bstr.s == NULL)
	{
		LM_ERR("failed to generate random string\n");
		goto error;
	}
	bstr.len= strlen(bstr.s);

	/* for the multipart body , use here also an initial allocated
	 * and reallocated on need buffer */
	buf= pkg_malloc(size);
	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;
		body.s=   (char*)row_vals[body_col].val.string_val;
		body.len= strlen(body.s);
		ctype.s = (char*)row_vals[ctype_col].val.string_val;
		ctype.len = strlen(ctype.s);

		/* if all the info for one dialog have been collected -> send notify */
		/* the 'dialog' variable must be filled with the dialog info */
		/* 'buf' must contain the body */
		if(prev_did!= NULL && strcmp(prev_did, curr_did)) 
		{
			xmlDocDumpMemory(rlmi_doc,(xmlChar**)(void*)&rlmi_cont.s,
				&rlmi_cont.len);
		
			multi_cont.s= buf;
			multi_cont.len= buf_len;
			 
			if(agg_body_sendn_update(&dialog->pres_uri, bstr, &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;
		}

		/* for the new dialog -> search the dialog info and 
		 * fill the dialog structure and start a new rlmi document */
		if(prev_did== NULL || strcmp(prev_did, curr_did)) 
		{
			/* 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;
			}
			dialog->expires-= (int)time(NULL);
			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;
			instance_node= xmlNewChild(resource_node, NULL, 
					BAD_CAST "instance", NULL);
			if(instance_node== NULL)
			{
				LM_ERR("while adding instance child\n");
				goto error;
			}
			str_aux = generate_string(contor, 8);
			if(str_aux == NULL)
			{
				LM_ERR("failed to create random string\n");
				goto error;
			}
			xmlNewProp(instance_node, BAD_CAST "id",
					BAD_CAST str_aux);
			pkg_free(str_aux);

			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));
				cid.len = strlen(cid.s);
				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)
			{
				APPEND_MULTIPART_BODY();
				pkg_free(cid.s);
				cid.s = NULL;
			}

			i++;
			if(i== result->n)
			{
				i--;
				break;
			}

			row = &result->rows[i];
			row_vals = ROW_VALUES(row);
	
			if(strncmp(row_vals[resource_uri_col].val.string_val,resource_uri,
					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;
			body.s=   (char*)row_vals[body_col].val.string_val;
			body.len = strlen(body.s);
		}

		prev_did= curr_did;
	}

	if(rlmi_doc)
	{
		xmlDocDumpMemory( rlmi_doc,(xmlChar**)(void*)&rlmi_cont.s,
		&rlmi_cont.len);
		
		multi_cont.s= buf;
		multi_cont.len= buf_len;
	
		 if(agg_body_sendn_update(&dialog->pres_uri, bstr, &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;
	}

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;
}