Ticket #873: Include the parsed XML tuple in the pjsip_pres_status, and include it in the pjsua_buddy_info in PJSUA-LIB, in case the PIDF document contains other info that is needed by application (thanks Johan Lantz for the suggestion)

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@2762 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/include/pjsip-simple/presence.h b/pjsip/include/pjsip-simple/presence.h
index efebeb2..7c99612 100644
--- a/pjsip/include/pjsip-simple/presence.h
+++ b/pjsip/include/pjsip-simple/presence.h
@@ -90,6 +90,14 @@
 	pj_str_t	id;		/**< Tuple id.			    */
 	pj_str_t	contact;	/**< Optional contact address.	    */
 
+	pj_xml_node    *tuple_node;	/**< Pointer to tuple XML node of
+					     parsed PIDF body received from
+					     remote agent. Only valid for
+					     client subscription. If the
+					     last received NOTIFY request
+					     does not contain any PIDF body,
+					     this valud will be set to NULL */
+
     } info[PJSIP_PRES_STATUS_MAX_INFO];	/**< Array of info.		    */
 
     pj_bool_t		_is_valid;	/**< Internal flag.		    */
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index fe4fbe2..293c3c1 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -872,6 +872,17 @@
 #endif
 
 
+/**
+ * Add "timestamp" information in generated PIDF document for both server
+ * subscription and presence publication.
+ *
+ * Default: 1 (yes)
+ */
+#ifndef PJSIP_PRES_PIDF_ADD_TIMESTAMP
+#   define PJSIP_PRES_PIDF_ADD_TIMESTAMP	1
+#endif
+
+
 PJ_END_DECL
 
 /**
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index 44980ca..8744e9a 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -2907,6 +2907,11 @@
     pjrpid_element	rpid;
 
     /**
+     * Extended presence info.
+     */
+    pjsip_pres_status	pres_status;
+
+    /**
      * Internal buffer.
      */
     char		buf_[512];
diff --git a/pjsip/src/pjsip-simple/presence.c b/pjsip/src/pjsip-simple/presence.c
index 31c83fc..2404935 100644
--- a/pjsip/src/pjsip-simple/presence.c
+++ b/pjsip/src/pjsip-simple/presence.c
@@ -73,7 +73,9 @@
     pjsip_evsub		*sub;		/**< Event subscribtion record.	    */
     pjsip_dialog	*dlg;		/**< The dialog.		    */
     content_type_e	 content_type;	/**< Content-Type.		    */
+    pj_pool_t		*status_pool;	/**< Pool for pres_status	    */
     pjsip_pres_status	 status;	/**< Presence status.		    */
+    pj_pool_t		*tmp_pool;	/**< Pool for tmp_status	    */
     pjsip_pres_status	 tmp_status;	/**< Temp, before NOTIFY is answred.*/
     pjsip_evsub_user	 user_cb;	/**< The user callback.		    */
 };
@@ -185,6 +187,7 @@
 {
     pj_status_t status;
     pjsip_pres *pres;
+    char obj_name[PJ_MAX_OBJ_NAME];
     pjsip_evsub *sub;
 
     PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
@@ -204,6 +207,13 @@
     if (user_cb)
 	pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
 
+    pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
+    pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name, 
+				       512, 512, NULL);
+    pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
+    pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name, 
+				    512, 512, NULL);
+
     /* Attach to evsub */
     pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
 
@@ -228,6 +238,7 @@
     content_type_e content_type = CONTENT_TYPE_NONE;
     pjsip_evsub *sub;
     pjsip_pres *pres;
+    char obj_name[PJ_MAX_OBJ_NAME];
     pj_status_t status;
 
     /* Check arguments */
@@ -297,6 +308,13 @@
     if (user_cb)
 	pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
 
+    pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool);
+    pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name, 
+				       512, 512, NULL);
+    pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool);
+    pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name, 
+				    512, 512, NULL);
+
     /* Attach to evsub */
     pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
 
@@ -355,10 +373,13 @@
     pres = (pjsip_pres*) pjsip_evsub_get_mod_data(sub, mod_presence.id);
     PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
 
-    if (pres->tmp_status._is_valid)
+    if (pres->tmp_status._is_valid) {
+	PJ_ASSERT_RETURN(pres->tmp_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
 	pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
-    else
+    } else {
+	PJ_ASSERT_RETURN(pres->status_pool!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
 	pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
+    }
 
     return PJ_SUCCESS;
 }
@@ -371,6 +392,7 @@
 					   const pjsip_pres_status *status )
 {
     unsigned i;
+    pj_pool_t *tmp;
     pjsip_pres *pres;
 
     PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
@@ -380,7 +402,9 @@
 
     for (i=0; i<status->info_cnt; ++i) {
 	pres->status.info[i].basic_open = status->info[i].basic_open;
-	if (status->info[i].id.slen == 0) {
+	if (pres->status.info[i].id.slen) {
+	    /* Id already set */
+	} else if (status->info[i].id.slen == 0) {
 	    pj_create_unique_string(pres->dlg->pool, 
 	    			    &pres->status.info[i].id);
 	} else {
@@ -388,17 +412,17 @@
 		      &pres->status.info[i].id,
 		      &status->info[i].id);
 	}
-	pj_strdup(pres->dlg->pool, 
+	pj_strdup(pres->tmp_pool, 
 		  &pres->status.info[i].contact,
 		  &status->info[i].contact);
 
 	/* Duplicate <person> */
 	pres->status.info[i].rpid.activity = 
 	    status->info[i].rpid.activity;
-	pj_strdup(pres->dlg->pool, 
+	pj_strdup(pres->tmp_pool, 
 		  &pres->status.info[i].rpid.id,
 		  &status->info[i].rpid.id);
-	pj_strdup(pres->dlg->pool,
+	pj_strdup(pres->tmp_pool,
 		  &pres->status.info[i].rpid.note,
 		  &status->info[i].rpid.note);
 
@@ -406,6 +430,12 @@
 
     pres->status.info_cnt = status->info_cnt;
 
+    /* Swap pools */
+    tmp = pres->tmp_pool;
+    pres->tmp_pool = pres->status_pool;
+    pres->status_pool = tmp;
+    pj_pool_reset(pres->tmp_pool);
+
     return PJ_SUCCESS;
 }
 
@@ -572,6 +602,17 @@
 
     if (pres->user_cb.on_evsub_state)
 	(*pres->user_cb.on_evsub_state)(sub, event);
+
+    if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+	if (pres->status_pool) {
+	    pj_pool_release(pres->status_pool);
+	    pres->status_pool = NULL;
+	}
+	if (pres->tmp_pool) {
+	    pj_pool_release(pres->tmp_pool);
+	    pres->tmp_pool = NULL;
+	}
+    }
 }
 
 /*
@@ -670,14 +711,14 @@
     if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
 	pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
     {
-	status = pjsip_pres_parse_pidf( rdata, pres->dlg->pool,
+	status = pjsip_pres_parse_pidf( rdata, pres->tmp_pool,
 					&pres->tmp_status);
     }
     else 
     if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
 	pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
     {
-	status = pjsip_pres_parse_xpidf( rdata, pres->dlg->pool,
+	status = pjsip_pres_parse_xpidf( rdata, pres->tmp_pool,
 					 &pres->tmp_status);
     }
     else
@@ -740,11 +781,23 @@
 	    return;
 
     } else {
+#if 1
+	/* This is the newest change, http://trac.pjsip.org/repos/ticket/873
+	 * Some app want to be notified about the empty NOTIFY, e.g. to 
+	 * decide whether it should consider the buddy as offline.
+	 * In this case, leave the buddy state unchanged, but set the
+	 * "tuple_node" in pjsip_pres_status to NULL.
+	 */
+	unsigned i;
+	for (i=0; i<pres->status.info_cnt; ++i) {
+	    pres->status.info[i].tuple_node = NULL;
+	}
+
+#elif 0
 	/* This has just been changed. Previously, we treat incoming NOTIFY
 	 * with no message body as having the presence subscription closed.
 	 * Now we treat it as no change in presence status (ref: EyeBeam).
 	 */
-#if 1
 	*p_st_code = 200;
 	return;
 #else
@@ -767,10 +820,18 @@
      * to main status, and mark the temporary status as invalid.
      */
     if ((*p_st_code)/100 == 2) {
+	pj_pool_t *tmp;
+
 	pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
+
+	/* Swap the pool */
+	tmp = pres->tmp_pool;
+	pres->tmp_pool = pres->status_pool;
+	pres->status_pool = tmp;
     }
 
     pres->tmp_status._is_valid = PJ_FALSE;
+    pj_pool_reset(pres->tmp_pool);
 
     /* Done */
 }
diff --git a/pjsip/src/pjsip-simple/presence_body.c b/pjsip/src/pjsip-simple/presence_body.c
index 6b215b4..30b6318 100644
--- a/pjsip/src/pjsip-simple/presence_body.c
+++ b/pjsip/src/pjsip-simple/presence_body.c
@@ -100,6 +100,29 @@
 	pidf_status = pjpidf_tuple_get_status(pidf_tuple);
 	pjpidf_status_set_basic_open(pidf_status, 
 				     status->info[i].basic_open);
+
+	/* Add <timestamp> if configured */
+#if defined(PJSIP_PRES_PIDF_ADD_TIMESTAMP) && PJSIP_PRES_PIDF_ADD_TIMESTAMP
+	if (PJSIP_PRES_PIDF_ADD_TIMESTAMP) {
+	  char buf[50];
+	  int tslen = 0;
+	  pj_time_val tv;
+	  pj_parsed_time pt;
+
+	  pj_gettimeofday(&tv);
+	  /* TODO: convert time to GMT! (unsupported by pjlib) */
+	  pj_time_decode( &tv, &pt);
+
+	  tslen = pj_ansi_snprintf(buf, sizeof(buf),
+				   "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
+				   pt.year, pt.mon, pt.day, 
+				   pt.hour, pt.min, pt.sec, pt.msec);
+	  if (tslen > 0 && tslen < sizeof(buf)) {
+	      pj_str_t time = pj_str(buf);
+	      pjpidf_tuple_set_timestamp(pool, pidf_tuple, &time);
+	  }
+	}
+#endif
     }
 
     /* Create <person> (RPID) */
@@ -179,9 +202,12 @@
     pres_status->info_cnt = 0;
 
     pidf_tuple = pjpidf_pres_get_first_tuple(pidf);
-    while (pidf_tuple) {
+    while (pidf_tuple && pres_status->info_cnt < PJSIP_PRES_STATUS_MAX_INFO) {
 	pjpidf_status *pidf_status;
 
+	pres_status->info[pres_status->info_cnt].tuple_node = 
+	    pj_xml_clone(pool, pidf_tuple);
+
 	pj_strdup(pool, 
 		  &pres_status->info[pres_status->info_cnt].id,
 		  pjpidf_tuple_get_id(pidf_tuple));
@@ -231,6 +257,7 @@
 	      pjxpidf_get_uri(xpidf));
     pres_status->info[0].basic_open = pjxpidf_get_status(xpidf);
     pres_status->info[0].id.slen = 0;
+    pres_status->info[0].tuple_node = NULL;
 
     return PJ_SUCCESS;
 }
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index d980e61..797fb84 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -166,6 +166,9 @@
     pj_strncpy(&info->contact, &buddy->contact, sizeof(info->buf_)-total);
     total += info->contact.slen;
 
+    /* Presence status */
+    pj_memcpy(&info->pres_status, &buddy->status, sizeof(pjsip_pres_status));
+
     /* status and status text */    
     if (buddy->sub == NULL || buddy->status.info_cnt==0) {
 	info->status = PJSUA_BUDDY_STATUS_UNKNOWN;