Index: pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 2955)
+++ pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 2956)
@@ -2620,10 +2620,14 @@
     pjsua_buddy_get_info(buddy_id, &info);
 
-    PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s (subscription state is %s)",
+    PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s, subscription state is %s "
+			 "(last termination reason code=%d %.*s)",
 	      (int)info.uri.slen,
 	      info.uri.ptr,
 	      (int)info.status_text.slen,
 	      info.status_text.ptr,
-	      info.sub_state_name));
+	      info.sub_state_name,
+	      info.sub_term_code,
+	      (int)info.sub_term_reason.slen,
+	      info.sub_term_reason.ptr));
 }
 
Index: pjproject/trunk/pjsip/include/pjsip_simple.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip_simple.h	(revision 2955)
+++ pjproject/trunk/pjsip/include/pjsip_simple.h	(revision 2956)
@@ -36,4 +36,5 @@
 
 #include <pjsip-simple/evsub.h>
+#include <pjsip-simple/evsub_msg.h>
 #include <pjsip-simple/iscomposing.h>
 #include <pjsip-simple/presence.h>
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 2955)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 2956)
@@ -1398,4 +1398,30 @@
 
 /**
+ * Schedule a timer entry. Note that the timer callback may be executed
+ * by different thread, depending on whether worker thread is enabled or
+ * not.
+ *
+ * @param entry		Timer heap entry.
+ * @param delay     The interval to expire.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ *
+ * @see pjsip_endpt_schedule_timer()
+ */
+PJ_DECL(pj_status_t) pjsua_schedule_timer(pj_timer_entry *entry,
+					  const pj_time_val *delay);
+
+
+/**
+ * Cancel the previously scheduled timer.
+ *
+ * @param entry		Timer heap entry.
+ *
+ * @see pjsip_endpt_cancel_timer()
+ */
+PJ_DECL(void) pjsua_cancel_timer(pj_timer_entry *entry);
+
+
+/**
  * This is a utility function to display error message for the specified 
  * error code. The error message will be sent to the log.
@@ -3129,5 +3155,14 @@
 
     /**
-     * Specifies the last presence subscription terminatino reason. If 
+     * Specifies the last presence subscription termination code. This would
+     * return the last status of the SUBSCRIBE request. If the subscription
+     * is terminated with NOTIFY by the server, this value will be set to
+     * 200, and subscription termination reason will be given in the
+     * \a sub_term_reason field.
+     */
+    unsigned		sub_term_code;
+
+    /**
+     * Specifies the last presence subscription termination reason. If 
      * presence subscription is currently active, the value will be empty.
      */
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 2955)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 2956)
@@ -186,7 +186,8 @@
     pjsip_dialog	*dlg;	    /**< The underlying dialog.		*/
     pjsip_evsub		*sub;	    /**< Buddy presence subscription	*/
+    unsigned		 term_code; /**< Subscription termination code	*/
     pj_str_t		 term_reason;/**< Subscription termination reason */
     pjsip_pres_status	 status;    /**< Buddy presence status.		*/
-
+    pj_timer_entry	 timer;	    /**< Resubscription timer		*/
 } pjsua_buddy;
 
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 2955)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 2956)
@@ -2319,4 +2319,21 @@
 }
 
+/*
+ * Schedule a timer entry. 
+ */
+PJ_DEF(pj_status_t) pjsua_schedule_timer( pj_timer_entry *entry,
+					  const pj_time_val *delay)
+{
+    return pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, delay);
+}
+
+/*
+ * Cancel the previously scheduled timer.
+ *
+ */
+PJ_DEF(void) pjsua_cancel_timer(pj_timer_entry *entry)
+{
+    pjsip_endpt_cancel_timer(pjsua_var.endpt, entry);
+}
 
 /** 
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c	(revision 2955)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c	(revision 2956)
@@ -25,5 +25,6 @@
 
 
-static void subscribe_buddy_presence(unsigned index);
+static void subscribe_buddy_presence(pjsua_buddy_id buddy_id);
+static void unsubscribe_buddy_presence(pjsua_buddy_id buddy_id);
 
 
@@ -31,5 +32,5 @@
  * Find buddy.
  */
-static pjsua_buddy_id pjsua_find_buddy(const pjsip_uri *uri)
+static pjsua_buddy_id find_buddy(const pjsip_uri *uri)
 {
     const pjsip_sip_uri *sip_uri;
@@ -61,4 +62,85 @@
 }
 
+#define LOCK_DIALOG	1
+#define LOCK_PJSUA	2
+#define LOCK_ALL	(LOCK_DIALOG | LOCK_PJSUA)
+
+/* Buddy lock object */
+struct buddy_lock
+{
+    pjsua_buddy	    *buddy;
+    pjsip_dialog    *dlg;
+    pj_uint8_t	     flag;
+};
+
+/* Acquire lock to the specified buddy_id */
+pj_status_t lock_buddy(const char *title,
+		       pjsua_buddy_id buddy_id,
+		       struct buddy_lock *lck,
+		       unsigned _unused_)
+{
+    enum { MAX_RETRY=50 };
+    pj_bool_t has_pjsua_lock = PJ_FALSE;
+    unsigned retry;
+
+    PJ_UNUSED_ARG(_unused_);
+
+    pj_bzero(lck, sizeof(*lck));
+
+    for (retry=0; retry<MAX_RETRY; ++retry) {
+	
+	if (PJSUA_TRY_LOCK() != PJ_SUCCESS) {
+	    pj_thread_sleep(retry/10);
+	    continue;
+	}
+
+	has_pjsua_lock = PJ_TRUE;
+	lck->flag = LOCK_PJSUA;
+	lck->buddy = &pjsua_var.buddy[buddy_id];
+
+	if (lck->buddy->dlg == NULL)
+	    return PJ_SUCCESS;
+
+	if (pjsip_dlg_try_inc_lock(lck->buddy->dlg) != PJ_SUCCESS) {
+	    lck->flag = 0;
+	    lck->buddy = NULL;
+	    has_pjsua_lock = PJ_FALSE;
+	    PJSUA_UNLOCK();
+	    pj_thread_sleep(retry/10);
+	    continue;
+	}
+
+	lck->dlg = lck->buddy->dlg;
+	lck->flag = LOCK_DIALOG;
+	PJSUA_UNLOCK();
+
+	break;
+    }
+
+    if (lck->flag == 0) {
+	if (has_pjsua_lock == PJ_FALSE)
+	    PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
+				 "(possibly system has deadlocked) in %s",
+				 title));
+	else
+	    PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
+				 "(possibly system has deadlocked) in %s",
+				 title));
+	return PJ_ETIMEDOUT;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+/* Release buddy lock */
+static void unlock_buddy(struct buddy_lock *lck)
+{
+    if (lck->flag & LOCK_DIALOG)
+	pjsip_dlg_dec_lock(lck->dlg);
+
+    if (lck->flag & LOCK_PJSUA)
+	PJSUA_UNLOCK();
+}
+
 
 /*
@@ -87,6 +169,9 @@
     if (!uri)
 	buddy_id = PJSUA_INVALID_ID;
-    else
-	buddy_id = pjsua_find_buddy(uri);
+    else {
+	PJSUA_LOCK();
+	buddy_id = find_buddy(uri);
+	PJSUA_UNLOCK();
+    }
 
     pj_pool_release(pool);
@@ -140,18 +225,20 @@
 {
     unsigned total=0;
+    struct buddy_lock lck;
     pjsua_buddy *buddy;
-
-    PJ_ASSERT_RETURN(buddy_id>=0 && 
-		       buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy), 
-		     PJ_EINVAL);
-
-    PJSUA_LOCK();
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id),  PJ_EINVAL);
 
     pj_bzero(info, sizeof(pjsua_buddy_info));
 
-    buddy = &pjsua_var.buddy[buddy_id];
+    status = lock_buddy("pjsua_buddy_get_info()", buddy_id, &lck, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    buddy = lck.buddy;
     info->id = buddy->index;
     if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
-	PJSUA_UNLOCK();
+	unlock_buddy(&lck);
 	return PJ_SUCCESS;
     }
@@ -187,5 +274,10 @@
     } else {
 	info->status = PJSUA_BUDDY_STATUS_OFFLINE;
-	info->status_text = pj_str("Offline");
+	info->rpid = buddy->status.info[0].rpid;
+
+	if (info->rpid.note.slen)
+	    info->status_text = info->rpid.note;
+	else
+	    info->status_text = pj_str("Offline");
     }
 
@@ -194,4 +286,5 @@
 
     /* subscription state and termination reason */
+    info->sub_term_code = buddy->term_code;
     if (buddy->sub) {
 	info->sub_state = pjsip_evsub_get_state(buddy->sub);
@@ -219,5 +312,5 @@
     }
 
-    PJSUA_UNLOCK();
+    unlock_buddy(&lck);
     return PJ_SUCCESS;
 }
@@ -229,13 +322,16 @@
 					       void *user_data)
 {
-    PJ_ASSERT_RETURN(buddy_id>=0 && 
-		       buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
-		     PJ_EINVAL);
-
-    PJSUA_LOCK();
+    struct buddy_lock lck;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
+
+    status = lock_buddy("pjsua_buddy_set_user_data()", buddy_id, &lck, 0);
+    if (status != PJ_SUCCESS)
+	return status;
 
     pjsua_var.buddy[buddy_id].user_data = user_data;
 
-    PJSUA_UNLOCK();
+    unlock_buddy(&lck);
 
     return PJ_SUCCESS;
@@ -248,15 +344,17 @@
 PJ_DEF(void*) pjsua_buddy_get_user_data(pjsua_buddy_id buddy_id)
 {
+    struct buddy_lock lck;
+    pj_status_t status;
     void *user_data;
 
-    PJ_ASSERT_RETURN(buddy_id>=0 && 
-		       buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
-		     NULL);
-
-    PJSUA_LOCK();
+    PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), NULL);
+
+    status = lock_buddy("pjsua_buddy_get_user_data()", buddy_id, &lck, 0);
+    if (status != PJ_SUCCESS)
+	return NULL;
 
     user_data = pjsua_var.buddy[buddy_id].user_data;
 
-    PJSUA_UNLOCK();
+    unlock_buddy(&lck);
 
     return user_data;
@@ -383,4 +481,7 @@
 PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
 {
+    struct buddy_lock lck;
+    pj_status_t status;
+
     PJ_ASSERT_RETURN(buddy_id>=0 && 
 			buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
@@ -391,8 +492,10 @@
     }
 
+    status = lock_buddy("pjsua_buddy_del()", buddy_id, &lck, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+
     /* Unsubscribe presence */
     pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);
-
-    PJSUA_LOCK();
 
     /* Not interested with further events for this buddy */
@@ -409,5 +512,5 @@
     reset_buddy(buddy_id);
 
-    PJSUA_UNLOCK();
+    unlock_buddy(&lck);
     return PJ_SUCCESS;
 }
@@ -420,19 +523,18 @@
 						pj_bool_t subscribe)
 {
-    pjsua_buddy *buddy;
-
-    PJ_ASSERT_RETURN(buddy_id>=0 && 
-			buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
-		     PJ_EINVAL);
-
-    PJSUA_LOCK();
-
-    buddy = &pjsua_var.buddy[buddy_id];
-    buddy->monitor = subscribe;
-
-    PJSUA_UNLOCK();
-
-    pjsua_pres_refresh();
-
+    struct buddy_lock lck;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
+
+    status = lock_buddy("pjsua_buddy_subscribe_pres()", buddy_id, &lck, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    lck.buddy->monitor = subscribe;
+
+    pjsua_buddy_update_pres(buddy_id);
+
+    unlock_buddy(&lck);
     return PJ_SUCCESS;
 }
@@ -444,23 +546,23 @@
 PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
 {
-    pjsua_buddy *buddy;
-
-    PJ_ASSERT_RETURN(buddy_id>=0 && 
-			buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
-		     PJ_EINVAL);
-
-    PJSUA_LOCK();
-
-    buddy = &pjsua_var.buddy[buddy_id];
-
-    /* Return error if buddy's presence monitoring is not enabled */
-    if (!buddy->monitor) {
-	PJSUA_UNLOCK();
-	return PJ_EINVALIDOP;
+    struct buddy_lock lck;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pjsua_buddy_is_valid(buddy_id), PJ_EINVAL);
+
+    status = lock_buddy("pjsua_buddy_update_pres()", buddy_id, &lck, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Is this an unsubscribe request? */
+    if (!lck.buddy->monitor) {
+	unsubscribe_buddy_presence(buddy_id);
+	unlock_buddy(&lck);
+	return PJ_SUCCESS;
     }
 
     /* Ignore if presence is already active for the buddy */
-    if (buddy->sub) {
-	PJSUA_UNLOCK();
+    if (lck.buddy->sub) {
+	unlock_buddy(&lck);
 	return PJ_SUCCESS;
     }
@@ -469,5 +571,5 @@
     subscribe_buddy_presence(buddy_id);
 
-    PJSUA_UNLOCK();
+    unlock_buddy(&lck);
 
     return PJ_SUCCESS;
@@ -806,5 +908,5 @@
 	pjsua_buddy_id buddy_id;
 
-	buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);
+	buddy_id = find_buddy(rdata->msg_info.from->uri);
 
 	(*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
@@ -952,5 +1054,5 @@
 
     /* Subscribe to buddy's presence if we're not subscribed */
-    buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
+    buddy_id = find_buddy(srv_pres->dlg->remote.info->uri);
     if (buddy_id != PJSUA_INVALID_ID) {
 	pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
@@ -1281,4 +1383,38 @@
  */
 
+static void buddy_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry)
+{
+    pjsua_buddy *buddy = (pjsua_buddy*)entry->user_data;
+
+    PJ_UNUSED_ARG(th);
+
+    entry->id = PJ_FALSE;
+    pjsua_buddy_update_pres(buddy->index);
+}
+
+/* Reschedule subscription refresh timer or terminate the subscription
+ * refresh timer for the specified buddy.
+ */
+static void buddy_resubscribe(pjsua_buddy *buddy, pj_bool_t resched,
+			      unsigned msec_interval)
+{
+    if (buddy->timer.id) {
+	pjsua_cancel_timer(&buddy->timer);
+	buddy->timer.id = PJ_FALSE;
+    }
+
+    if (resched) {
+	pj_time_val delay;
+
+	pj_timer_entry_init(&buddy->timer, 0, buddy, &buddy_timer_cb);
+	delay.sec = 0;
+	delay.msec = msec_interval;
+	pj_time_val_normalize(&delay);
+
+	if (pjsua_schedule_timer(&buddy->timer, &delay)==PJ_SUCCESS)
+	    buddy->timer.id = PJ_TRUE;
+    }
+}
+
 /* Callback called when *client* subscription state has changed. */
 static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
@@ -1288,6 +1424,8 @@
     PJ_UNUSED_ARG(event);
 
-    PJSUA_LOCK();
-
+    /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
+     *   a dialog attached to it, lock_buddy() will use the dialog
+     *   lock, which we are currently holding!
+     */
     buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
     if (buddy) {
@@ -1299,4 +1437,6 @@
 
 	if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+	    int resub_delay = -1;
+
 	    if (buddy->term_reason.ptr == NULL) {
 		buddy->term_reason.ptr = (char*) 
@@ -1307,5 +1447,85 @@
 		       pjsip_evsub_get_termination_reason(sub), 
 		       PJSUA_BUDDY_SUB_TERM_REASON_LEN);
+
+	    buddy->term_code = 200;
+
+	    /* Determine whether to resubscribe automatically */
+	    if (event->type==PJSIP_EVENT_TSX_STATE) {
+		const pjsip_transaction *tsx = event->body.tsx_state.tsx;
+		if (pjsip_method_cmp(&tsx->method, 
+				     &pjsip_subscribe_method)==0)
+		{
+		    buddy->term_code = tsx->status_code;
+		    switch (tsx->status_code) {
+		    case PJSIP_SC_CALL_TSX_DOES_NOT_EXIST:
+			/* 481: we refreshed too late? resubscribe
+			 * immediately.
+			 */
+			resub_delay = 500;
+			break;
+		    }
+		} else if (pjsip_method_cmp(&tsx->method,
+					    &pjsip_notify_method)==0)
+		{
+		    if (pj_stricmp2(&buddy->term_reason, "deactivated")==0 ||
+			pj_stricmp2(&buddy->term_reason, "timeout")==0) {
+			/* deactivated: The subscription has been terminated, 
+			 * but the subscriber SHOULD retry immediately with 
+			 * a new subscription.
+			 */
+			/* timeout: The subscription has been terminated 
+			 * because it was not refreshed before it expired.
+			 * Clients MAY re-subscribe immediately. The 
+			 * "retry-after" parameter has no semantics for 
+			 * "timeout".
+			 */
+			resub_delay = 500;
+		    } 
+		    else if (pj_stricmp2(&buddy->term_reason, "probation")==0||
+			     pj_stricmp2(&buddy->term_reason, "giveup")==0) {
+			/* probation: The subscription has been terminated, 
+			 * but the client SHOULD retry at some later time.  
+			 * If a "retry-after" parameter is also present, the 
+			 * client SHOULD wait at least the number of seconds 
+			 * specified by that parameter before attempting to re-
+			 * subscribe.
+			 */
+			/* giveup: The subscription has been terminated because
+			 * the notifier could not obtain authorization in a 
+			 * timely fashion.  If a "retry-after" parameter is 
+			 * also present, the client SHOULD wait at least the
+			 * number of seconds specified by that parameter before
+			 * attempting to re-subscribe; otherwise, the client 
+			 * MAY retry immediately, but will likely get put back
+			 * into pending state.
+			 */
+			const pjsip_sub_state_hdr *sub_hdr;
+			pj_str_t sub_state = { "Subscription-State", 18 };
+			const pjsip_msg *msg;
+
+			msg = event->body.tsx_state.src.rdata->msg_info.msg;
+			sub_hdr = (const pjsip_sub_state_hdr*)
+				  pjsip_msg_find_hdr_by_name(msg, &sub_state,
+							     NULL);
+			if (sub_hdr && sub_hdr->retry_after > 0)
+			    resub_delay = sub_hdr->retry_after * 1000;
+		    }
+
+		}
+	    }
+
+	    /* For other cases of subscription termination, if resubscribe
+	     * timer is not set, schedule with default expiration (plus minus
+	     * some random value, to avoid sending SUBSCRIBEs all at once)
+	     */
+	    if (resub_delay == -1) {
+		pj_assert(PJSUA_PRES_TIMER >= 3);
+		resub_delay = PJSUA_PRES_TIMER*1000 - 2500 + (pj_rand()%5000);
+	    }
+
+	    buddy_resubscribe(buddy, PJ_TRUE, resub_delay);
+				  
 	} else {
+	    buddy->term_code = 0;
 	    buddy->term_reason.slen = 0;
 	}
@@ -1319,9 +1539,8 @@
 	    buddy->sub = NULL;
 	    buddy->status.info_cnt = 0;
+	    buddy->dlg = NULL;
 	    pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
 	}
     }
-
-    PJSUA_UNLOCK();
 }
 
@@ -1335,9 +1554,10 @@
     pjsip_contact_hdr *contact_hdr;
 
-    PJSUA_LOCK();
-
+    /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
+     *   a dialog attached to it, lock_buddy() will use the dialog
+     *   lock, which we are currently holding!
+     */
     buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
     if (!buddy) {
-	PJSUA_UNLOCK();
 	return;
     }
@@ -1348,5 +1568,4 @@
     if (buddy->contact.slen != 0) {
 	/* Contact already set */
-	PJSUA_UNLOCK();
 	return;
     }
@@ -1358,5 +1577,4 @@
 	pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
     {
-	PJSUA_UNLOCK();
 	return;
     }
@@ -1367,5 +1585,4 @@
 				     PJSIP_H_CONTACT, NULL);
     if (!contact_hdr) {
-	PJSUA_UNLOCK();
 	return;
     }
@@ -1379,6 +1596,4 @@
     if (buddy->contact.slen < 0)
 	buddy->contact.slen = 0;
-
-    PJSUA_UNLOCK();
 }
 
@@ -1394,6 +1609,8 @@
     pjsua_buddy *buddy;
 
-    PJSUA_LOCK();
-
+    /* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
+     *   a dialog attached to it, lock_buddy() will use the dialog
+     *   lock, which we are currently holding!
+     */
     buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
     if (buddy) {
@@ -1410,6 +1627,4 @@
     PJ_UNUSED_ARG(res_hdr);
     PJ_UNUSED_ARG(p_body);
-
-    PJSUA_UNLOCK();
 }
 
@@ -1437,5 +1652,5 @@
 
 /* It does what it says.. */
-static void subscribe_buddy_presence(unsigned index)
+static void subscribe_buddy_presence(pjsua_buddy_id buddy_id)
 {
     pj_pool_t *tmp_pool = NULL;
@@ -1447,5 +1662,5 @@
     pj_status_t status;
 
-    buddy = &pjsua_var.buddy[index];
+    buddy = &pjsua_var.buddy[buddy_id];
     acc_id = pjsua_acc_find_for_outgoing(&buddy->uri);
 
@@ -1453,5 +1668,5 @@
 
     PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription",
-			 acc_id, index));
+			 acc_id, buddy_id));
 
     /* Generate suitable Contact header unless one is already set in
@@ -1494,5 +1709,5 @@
 				    PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub);
     if (status != PJ_SUCCESS) {
-	pjsua_var.buddy[index].sub = NULL;
+	buddy->sub = NULL;
 	pjsua_perror(THIS_FILE, "Unable to create presence client", 
 		     status);
@@ -1565,5 +1780,5 @@
 
 /* It does what it says... */
-static void unsubscribe_buddy_presence(unsigned index)
+static void unsubscribe_buddy_presence(pjsua_buddy_id buddy_id)
 {
     pjsua_buddy *buddy;
@@ -1571,5 +1786,5 @@
     pj_status_t status;
 
-    buddy = &pjsua_var.buddy[index];
+    buddy = &pjsua_var.buddy[buddy_id];
 
     if (buddy->sub == NULL)
@@ -1577,5 +1792,5 @@
 
     if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) {
-	pjsua_var.buddy[index].sub = NULL;
+	buddy->sub = NULL;
 	return;
     }
@@ -1595,36 +1810,19 @@
 }
 
-
-/* Lock all buddies */
-#define LOCK_BUDDIES	unsigned cnt_ = 0; \
-			pjsip_dialog *dlg_list_[PJSUA_MAX_BUDDIES]; \
-			unsigned i_; \
-			for (i_=0; i_<PJ_ARRAY_SIZE(pjsua_var.buddy);++i_) { \
-			    if (pjsua_var.buddy[i_].sub) { \
-				dlg_list_[cnt_++] = pjsua_var.buddy[i_].dlg; \
-				pjsip_dlg_inc_lock(pjsua_var.buddy[i_].dlg); \
-			    } \
-			} \
-			PJSUA_LOCK();
-
-/* Unlock all buddies */
-#define UNLOCK_BUDDIES	PJSUA_UNLOCK(); \
-			for (i_=0; i_<cnt_; ++i_) { \
-			    pjsip_dlg_dec_lock(dlg_list_[i_]); \
-			}
-			
-
-
 /* It does what it says.. */
-static void refresh_client_subscriptions(void)
+static pj_status_t refresh_client_subscriptions(void)
 {
     unsigned i;
-
-    LOCK_BUDDIES;
+    pj_status_t status;
 
     for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
-
-	if (!pjsua_var.buddy[i].uri.slen)
+	struct buddy_lock lck;
+
+	if (!pjsua_buddy_is_valid(i))
 	    continue;
+
+	status = lock_buddy("refresh_client_subscriptions()", i, &lck, 0);
+	if (status != PJ_SUCCESS)
+	    return status;
 
 	if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) {
@@ -1635,7 +1833,9 @@
 
 	}
-    }
-
-    UNLOCK_BUDDIES;
+
+	unlock_buddy(&lck);
+    }
+
+    return PJ_SUCCESS;
 }
 
@@ -1655,5 +1855,9 @@
 
     entry->id = PJ_FALSE;
-    refresh_client_subscriptions();
+
+    /* #937: No need to do bulk client refresh, as buddies have their
+     *       own individual timer now.
+     */
+    //refresh_client_subscriptions();
 
     pjsip_endpt_schedule_timer(pjsua_var.endpt, entry, &delay);
@@ -1708,20 +1912,4 @@
 
 /*
- * Refresh presence subscriptions
- */
-void pjsua_pres_refresh()
-{
-    unsigned i;
-
-    refresh_client_subscriptions();
-
-    for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
-	if (pjsua_var.acc[i].valid)
-	    pjsua_pres_update_acc(i, PJ_FALSE);
-    }
-}
-
-
-/*
  * Shutdown presence.
  */
@@ -1747,4 +1935,9 @@
     }
 
-    pjsua_pres_refresh();
-}
+    refresh_client_subscriptions();
+
+    for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+	if (pjsua_var.acc[i].valid)
+	    pjsua_pres_update_acc(i, PJ_FALSE);
+    }
+}
