Browse Source

presence: check if all related dialog states are terminated

- fix for incorrect dialog state for multiple dialogs xml body
- reported by GH #1427
Daniel-Constantin Mierla 7 years ago
parent
commit
76b40abd65
1 changed files with 43 additions and 35 deletions
  1. 43 35
      src/modules/presence/presentity.c

+ 43 - 35
src/modules/presence/presentity.c

@@ -304,27 +304,31 @@ int check_if_dialog(str body, int *is_dialog, char **dialog_id)
 	return 0;
 	return 0;
 }
 }
 
 
-int parse_dialog_state_from_body(str body, int *is_dialog, char **state)
+/**
+ * return 1 if all dialog nodes have the vstate
+ *   * 0 if at least one state is different
+ *   * -1 in case of error
+ */
+int ps_match_dialog_state_from_body(str body, int *is_dialog, char *vstate)
 {
 {
 	xmlDocPtr doc;
 	xmlDocPtr doc;
 	xmlNodePtr node;
 	xmlNodePtr node;
 	xmlNodePtr childNode;
 	xmlNodePtr childNode;
 	char *tmp_state;
 	char *tmp_state;
+	int rmatch = 0;
 
 
-	*state = NULL;
 	*is_dialog = 0;
 	*is_dialog = 0;
 
 
 	doc = xmlParseMemory(body.s, body.len);
 	doc = xmlParseMemory(body.s, body.len);
-	if(doc== NULL)
+	if(doc == NULL || doc->children == NULL)
 	{
 	{
 		LM_ERR("failed to parse xml document\n");
 		LM_ERR("failed to parse xml document\n");
 		return -1;
 		return -1;
 	}
 	}
 
 
-	node = doc->children;
-	node = xmlNodeGetChildByName(node, "dialog");
+	node = xmlNodeGetChildByName(doc->children, "dialog");
 
 
-	if(node != NULL)
+	while(node != NULL)
 	{
 	{
 		*is_dialog = 1;
 		*is_dialog = 1;
 
 
@@ -333,16 +337,33 @@ int parse_dialog_state_from_body(str body, int *is_dialog, char **state)
 
 
 		if (tmp_state != NULL)
 		if (tmp_state != NULL)
 		{
 		{
-			*state = strdup(tmp_state);
+			if(strcmp(tmp_state, vstate)!=0) {
+				xmlFree(tmp_state);
+				rmatch = 0;
+				goto done;
+			}
+			rmatch = 1;
 			xmlFree(tmp_state);
 			xmlFree(tmp_state);
 		}
 		}
+		/* search for next dialog node */
+		do {
+			if(node->next != NULL && node->next->name != NULL
+					&& xmlStrcmp(node->name, node->next->name) == 0) {
+				node = node->next;
+				break;
+			}
+			node = node->next;
+		} while(node != NULL);
 	}
 	}
 
 
+done:
 	xmlFreeDoc(doc);
 	xmlFreeDoc(doc);
-	return 0;
+	return rmatch;
 }
 }
 
 
-int delete_presentity_if_dialog_id_exists(presentity_t* presentity, char* dialog_id) {
+int delete_presentity_if_dialog_id_exists(presentity_t* presentity,
+		char* dialog_id)
+{
 	db_key_t query_cols[13], result_cols[6];
 	db_key_t query_cols[13], result_cols[6];
 	db_op_t  query_ops[13];
 	db_op_t  query_ops[13];
 	db_val_t query_vals[13];
 	db_val_t query_vals[13];
@@ -448,7 +469,10 @@ int delete_presentity_if_dialog_id_exists(presentity_t* presentity, char* dialog
 	return 0;
 	return 0;
 }
 }
 
 
-int get_dialog_state(presentity_t* presentity, char** state)
+/**
+ * check if the states of all related dialogs match vstate
+ */
+int ps_match_dialog_state(presentity_t* presentity, char* vstate)
 {
 {
 	db_key_t query_cols[13], result_cols[6];
 	db_key_t query_cols[13], result_cols[6];
 	db_op_t  query_ops[13];
 	db_op_t  query_ops[13];
@@ -460,9 +484,7 @@ int get_dialog_state(presentity_t* presentity, char** state)
 	db_val_t *row_vals = NULL;
 	db_val_t *row_vals = NULL;
 	int db_is_dialog = 0;
 	int db_is_dialog = 0;
 	str tmp_db_body;
 	str tmp_db_body;
-	int i = 0, parse_state_result = 0;
-
-	*state = NULL;
+	int i = 0, rmatch = 0;
 
 
 	query_cols[n_query_cols] = &str_domain_col;
 	query_cols[n_query_cols] = &str_domain_col;
 	query_ops[n_query_cols] = OP_EQ;
 	query_ops[n_query_cols] = OP_EQ;
@@ -516,7 +538,7 @@ int get_dialog_state(presentity_t* presentity, char** state)
 		return 0;
 		return 0;
 	}
 	}
 
 
-	parse_state_result = 0;
+	rmatch = 0;
 	// Loop the rows returned from the DB
 	// Loop the rows returned from the DB
 	for (i=0; i < result->n; i++)
 	for (i=0; i < result->n; i++)
 	{
 	{
@@ -525,34 +547,20 @@ int get_dialog_state(presentity_t* presentity, char** state)
 		tmp_db_body.s = (char*)row_vals[rez_body_col].val.string_val;
 		tmp_db_body.s = (char*)row_vals[rez_body_col].val.string_val;
 		tmp_db_body.len = strlen(tmp_db_body.s);
 		tmp_db_body.len = strlen(tmp_db_body.s);
 
 
-		parse_state_result = parse_dialog_state_from_body(tmp_db_body,
-				&db_is_dialog, state);
+		rmatch = ps_match_dialog_state_from_body(tmp_db_body,
+				&db_is_dialog, vstate);
 
 
-		if(parse_state_result==0) {
-			/* successful parsing */
+		if(rmatch<=0) {
+			/* failure or not a match */
 			pa_dbf.free_result(pa_db, result);
 			pa_dbf.free_result(pa_db, result);
 			result = NULL;
 			result = NULL;
-			return parse_state_result;
+			return rmatch;
 		}
 		}
 	}
 	}
 
 
 	pa_dbf.free_result(pa_db, result);
 	pa_dbf.free_result(pa_db, result);
 	result = NULL;
 	result = NULL;
-	return parse_state_result;
-}
-
-int is_dialog_terminated(presentity_t* presentity)
-{
-	char *state = NULL;
-	int rtn;
-
-	get_dialog_state(presentity, &state);
-
-	rtn = state && !strcasecmp(state, "terminated");
-
-	free(state);
-
-	return rtn;
+	return rmatch;
 }
 }
 
 
 int update_presentity(struct sip_msg* msg, presentity_t* presentity, str* body,
 int update_presentity(struct sip_msg* msg, presentity_t* presentity, str* body,
@@ -1088,7 +1096,7 @@ after_etag_generation:
 			cur_etag= presentity->etag;
 			cur_etag= presentity->etag;
 
 
 		if (presentity->event->evp->type==EVENT_DIALOG) {
 		if (presentity->event->evp->type==EVENT_DIALOG) {
-			if(is_dialog_terminated(presentity))
+			if(ps_match_dialog_state(presentity, "terminated")==1)
 			{
 			{
 				LM_WARN("Trying to update an already terminated state."
 				LM_WARN("Trying to update an already terminated state."
 						" Skipping update.\n");
 						" Skipping update.\n");