Index: pjproject/trunk/pjsip/include/pjsip-simple/presence.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip-simple/presence.h	(revision 2761)
+++ pjproject/trunk/pjsip/include/pjsip-simple/presence.h	(revision 2762)
@@ -90,4 +90,12 @@
 	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.		    */
Index: pjproject/trunk/pjsip/include/pjsip/sip_config.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip/sip_config.h	(revision 2761)
+++ pjproject/trunk/pjsip/include/pjsip/sip_config.h	(revision 2762)
@@ -873,4 +873,15 @@
 
 
+/**
+ * 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
 
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 2761)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 2762)
@@ -2908,4 +2908,9 @@
 
     /**
+     * Extended presence info.
+     */
+    pjsip_pres_status	pres_status;
+
+    /**
      * Internal buffer.
      */
Index: pjproject/trunk/pjsip/src/pjsip-simple/presence.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip-simple/presence.c	(revision 2761)
+++ pjproject/trunk/pjsip/src/pjsip-simple/presence.c	(revision 2762)
@@ -74,5 +74,7 @@
     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.		    */
@@ -186,4 +188,5 @@
     pj_status_t status;
     pjsip_pres *pres;
+    char obj_name[PJ_MAX_OBJ_NAME];
     pjsip_evsub *sub;
 
@@ -205,4 +208,11 @@
 	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);
@@ -229,4 +239,5 @@
     pjsip_evsub *sub;
     pjsip_pres *pres;
+    char obj_name[PJ_MAX_OBJ_NAME];
     pj_status_t status;
 
@@ -298,4 +309,11 @@
 	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);
@@ -356,8 +374,11 @@
     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;
@@ -372,4 +393,5 @@
 {
     unsigned i;
+    pj_pool_t *tmp;
     pjsip_pres *pres;
 
@@ -381,5 +403,7 @@
     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);
@@ -389,5 +413,5 @@
 		      &status->info[i].id);
 	}
-	pj_strdup(pres->dlg->pool, 
+	pj_strdup(pres->tmp_pool, 
 		  &pres->status.info[i].contact,
 		  &status->info[i].contact);
@@ -396,8 +420,8 @@
 	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,4 +430,10 @@
 
     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;
@@ -573,4 +603,15 @@
     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;
+	}
+    }
 }
 
@@ -671,5 +712,5 @@
 	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);
     }
@@ -678,5 +719,5 @@
 	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);
     }
@@ -741,9 +782,21 @@
 
     } 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;
@@ -768,8 +821,16 @@
      */
     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 */
Index: pjproject/trunk/pjsip/src/pjsip-simple/presence_body.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip-simple/presence_body.c	(revision 2761)
+++ pjproject/trunk/pjsip/src/pjsip-simple/presence_body.c	(revision 2762)
@@ -101,4 +101,27 @@
 	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
     }
 
@@ -180,6 +203,9 @@
 
     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, 
@@ -232,4 +258,5 @@
     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;
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c	(revision 2761)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c	(revision 2762)
@@ -167,4 +167,7 @@
     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) {
