Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/account.py
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/account.py	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/account.py	(revision 4657)
@@ -46,4 +46,5 @@
 		self.cfg =  pj.AccountConfig()
 		self.cfgChanged = False
+		self.buddyList = []
 
 	def statusText(self):
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/accountsetting.py
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/accountsetting.py	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/accountsetting.py	(revision 4657)
@@ -41,4 +41,5 @@
 		self.transient(parent)
 		self.parent = parent
+		self.geometry("+100+100")
 		self.title('Account settings')
 		
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/application.py
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/application.py	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/application.py	(revision 4657)
@@ -33,4 +33,5 @@
 import accountsetting
 import account
+import buddy
 import endpoint
 
@@ -63,4 +64,5 @@
 		self.pack(expand='yes', fill='both')
 		self.master.title('pjsua2 Demo')
+		self.master.geometry('500x500+100+100')
 		
 		# Logger
@@ -72,5 +74,5 @@
 		# GUI variables
 		self.showLogWindow = tk.IntVar()
-		self.showLogWindow.set(1)
+		self.showLogWindow.set(0)
 		self.quitting = False 
 		
@@ -100,4 +102,8 @@
 		t.cfg.port = 0
 		self.transportCfgs.append(t)
+		t = SipTransportConfig()
+		t.type = pj.PJSIP_TRANSPORT_TCP
+		t.cfg.port = 0
+		self.transportCfgs.append(t)
 		
 	
@@ -116,5 +122,12 @@
 		node = json.writeNewArray("accounts")
 		for acc in self.accList:
-			node.writeObject(acc.cfg);
+			acc_node = node.writeNewContainer("Account")
+			acc_node.writeObject(acc.cfg);
+
+			# Write buddy configs
+			buddy_node = acc_node.writeNewArray("buddies")
+			for bud in acc.buddyList:
+				buddy_node.writeObject(bud.cfg)
+				
 		json.saveFile(filename)
 	
@@ -141,8 +154,19 @@
 			node = json.readArray("accounts")
 			while node.hasUnread():
-				cfg = pj.AccountConfig()
-				cfg.readObject(node)
-				acc_cfgs.append(cfg)
-		
+				acc_node = node.readContainer("Account")
+				acc_cfg = pj.AccountConfig()
+				acc_cfg.readObject(acc_node)
+		
+				# Load buddy configs
+				buddy_cfgs = []
+				buddy_node = acc_node.readArray("buddies")
+				while buddy_node.hasUnread():
+					buddy_cfg = pj.BuddyConfig()
+					buddy_cfg.readObject(buddy_node)
+					buddy_cfgs.append(buddy_cfg)
+
+				acc_cfgs.append((acc_cfg, buddy_cfgs))
+				
+				
 		# Initialize library
 		self.epCfg.uaConfig.userAgent = "pygui-" + self.ep.libVersion().full;
@@ -155,7 +179,10 @@
 			
 		# Add accounts
-		for cfg in acc_cfgs:
+		for cfg, buddy_cfgs in acc_cfgs:
 			self._createAcc(cfg)
-		
+			acc = self.accList[-1]
+			for buddy_cfg in buddy_cfgs:
+				self._createBuddy(acc, buddy_cfg)
+				
 		# Start library
 		self.ep.libStart()
@@ -173,7 +200,16 @@
 			self.tv.item(iid, text=text, values=values)
 		else:
-			self.tv.insert('', 0,  iid, open=True, text=text, values=values)
-			self.tv.insert(iid, 0, '', open=True, text='Buddy 1', values=('Online',))
-			self.tv.insert(iid, 1, '', open=True, text='Buddy 2', values=('Online',))
+			self.tv.insert('', 'end',  iid, open=True, text=text, values=values)
+		
+	def updateBuddy(self, bud):
+		iid = 'buddy' + str(bud.randId)
+		text = bud.cfg.uri
+		status = bud.statusText()
+		
+		values = (status,)
+		if self.tv.exists(iid):
+			self.tv.item(iid, text=text, values=values)
+		else:
+			self.tv.insert(str(bud.account.randId), 'end',  iid, open=True, text=text, values=values)
 		
 	def _createAcc(self, acc_cfg):
@@ -186,4 +222,12 @@
 		self.updateAccount(acc)
 				
+	def _createBuddy(self, acc, buddy_cfg):
+		bud = buddy.Buddy(self)
+		bud.cfg = buddy_cfg
+		bud.account = acc
+		bud.create(acc, bud.cfg)
+		self.updateBuddy(bud)
+		acc.buddyList.append(bud)
+
 	def _createWidgets(self):
 		self._createAppMenu()
@@ -222,7 +266,12 @@
 	def _createContextMenu(self):
 		top = self.winfo_toplevel()
+
+		# Create Account context menu
 		self.accMenu = tk.Menu(top, tearoff=False)
 		# Labels, must match with _onAccContextMenu()
-		labels = ['Unregister', 'Reregister', '-', 'Online', 'Invisible', 'Away', 'Busy', '-', 'Settings...', '-', 'Delete...']
+		labels = ['Unregister', 'Reregister', 'Add buddy...', '-',
+			  'Online', 'Invisible', 'Away', 'Busy', '-',
+			  'Settings...', '-',
+			  'Delete...']
 		for label in labels:
 			if label=='-':
@@ -232,4 +281,18 @@
 				self.accMenu.add_command(label=label, command=cmd)
 		
+		# Create Buddy context menu
+		# Labels, must match with _onAccContextMenu()
+		self.buddyMenu = tk.Menu(top, tearoff=False)
+		labels = ['Video call', 'Audio call', 'Send instant message', '-',
+			  'Settings...', '-',
+			  'Delete...']
+		
+		for label in labels:
+			if label=='-':
+				self.buddyMenu.add_separator()
+			else:
+				cmd = lambda arg=label: self._onBuddyContextMenu(arg)
+				self.buddyMenu.add_command(label=label, command=cmd)
+		
 		if (top.tk.call('tk', 'windowingsystem')=='aqua'):
 			self.tv.bind('<2>', self._onTvRightClick)
@@ -251,8 +314,29 @@
 		return accs[0]
 	
+	def _getSelectedBuddy(self):
+		items = self.tv.selection()
+		if not items:
+			return None
+		try:
+			iid = int(items[0][5:])
+			iid_parent = int(self.tv.parent(items[0]))
+		except:
+			return None
+			
+		accs = [acc for acc in self.accList if acc.randId==iid_parent]
+		if not accs:
+			return None
+			
+		buds = [b for b in accs[0].buddyList if b.randId==iid]
+		if not buds:
+			return None
+			
+		return buds[0]
+	
 	def _onTvRightClick(self, event):
-		iid = self.tv.identify('item', event.x, event.y)
+		iid = self.tv.identify_row(event.y)
+		#iid = self.tv.identify('item', event.x, event.y)
 		if iid:
-			self.tv.selection_add( (iid,) )
+			self.tv.selection_set( (iid,) )
 			acc = self._getSelectedAccount()
 			if acc:
@@ -260,5 +344,5 @@
 			else:
 				# A buddy is selected
-				pass
+				self.buddyMenu.post(event.x_root, event.y_root)
 	
 	def _onAccContextMenu(self, label):
@@ -272,13 +356,13 @@
 			acc.setRegistration(True)
 		elif label=='Online':
-			ps = pj.AccountPresenceStatus()
+			ps = pj.PresenceStatus()
 			ps.isOnline = True
 			acc.setOnlineStatus(ps)
 		elif label=='Invisible':
-			ps = pj.AccountPresenceStatus()
+			ps = pj.PresenceStatus()
 			ps.isOnline = False
 			acc.setOnlineStatus(ps)
 		elif label=='Away':
-			ps = pj.AccountPresenceStatus()
+			ps = pj.PresenceStatus()
 			ps.isOnline = True
 			ps.activity = pj.PJRPID_ACTIVITY_AWAY
@@ -286,5 +370,5 @@
 			acc.setOnlineStatus(ps)
 		elif label=='Busy':
-			ps = pj.AccountPresenceStatus()
+			ps = pj.PresenceStatus()
 			ps.isOnline = True
 			ps.activity = pj.PJRPID_ACTIVITY_BUSY
@@ -305,7 +389,54 @@
 			del acc
 			self.tv.delete( (iid,) )
+		elif label=='Add buddy...':
+			cfg = pj.BuddyConfig()
+			dlg = buddy.SettingDialog(self.master, cfg)
+			if dlg.doModal():
+				self._createBuddy(acc, cfg)
 		else:
 			assert not ("Unknown menu " + label)
 	
+	def _onBuddyContextMenu(self, label):
+		bud = self._getSelectedBuddy()
+		if not bud:
+			return
+		acc = bud.account
+			
+		if label=='Video call':
+			pass
+		elif label=='Audio call':
+			pass
+		elif label=='Send instant message':
+			pass
+		elif label=='Settings...':
+			subs = bud.cfg.subscribe
+			uri  = bud.cfg.uri
+			dlg = buddy.SettingDialog(self.master, bud.cfg)
+			if dlg.doModal():
+				self.updateBuddy(bud)
+				# URI updated?
+				if uri != bud.cfg.uri:
+					cfg = bud.cfg
+					# del old
+					iid = 'buddy' + str(bud.randId)
+					acc.buddyList.remove(bud)
+					del bud
+					self.tv.delete( (iid,) )
+					# add new
+					self._createBuddy(acc, cfg)
+				# presence subscribe setting updated
+				elif subs != bud.cfg.subscribe:
+					bud.subscribePresence(bud.cfg.subscribe)
+		elif label=='Delete...':
+			msg = "Do you really want to delete buddy '%s'?" % bud.cfg.uri
+			if msgbox.askquestion('Delete buddy?', msg, default=msgbox.NO) != u'yes':
+				return
+			iid = 'buddy' + str(bud.randId)
+			acc.buddyList.remove(bud)
+			del bud
+			self.tv.delete( (iid,) )
+		else:
+			assert not ("Unknown menu " + label)
+			
 	def _onTimer(self):
 		if not self.quitting:
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/buddy.py
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/buddy.py	(revision 4657)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/pygui/buddy.py	(revision 4657)
@@ -0,0 +1,152 @@
+# $Id$
+#
+# pjsua Python GUI Demo
+#
+# Copyright (C)2013 Teluu Inc. (http://www.teluu.com)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+#
+import sys
+if sys.version_info[0] >= 3: # Python 3
+	import tkinter as tk
+	from tkinter import ttk
+	from tkinter import messagebox as msgbox
+else:
+	import Tkinter as tk
+	import tkMessageBox as msgbox
+	import ttk
+
+import random
+import pjsua2 as pj
+import endpoint
+import application
+
+# Buddy class
+class Buddy(pj.Buddy):
+	"""
+	High level Python Buddy object, derived from pjsua2's Buddy object.
+	"""
+	def __init__(self, app):
+		pj.Buddy.__init__(self)
+		self.app = app
+		self.randId = random.randint(1, 9999)
+		self.cfg = None
+		self.account = None
+
+	def statusText(self):
+		bi = self.getInfo()
+		status = ''
+		if bi.subState == pj.PJSIP_EVSUB_STATE_ACTIVE:
+			if bi.presStatus.status == pj.PJSUA_BUDDY_STATUS_ONLINE:
+				status = bi.presStatus.statusText
+				if not status:
+					status = 'Online'
+			elif bi.presStatus.status == pj.PJSUA_BUDDY_STATUS_OFFLINE:
+				status = 'Offline'
+			else:
+				status = 'Unknown'
+		return status
+	
+	def onBuddyState(self):
+		self.app.updateBuddy(self)
+
+class SettingDialog(tk.Toplevel):
+	"""
+	This implements buddy settings dialog to manipulate buddy settings.
+	"""
+	def __init__(self, parent, cfg):
+		tk.Toplevel.__init__(self, parent)
+		self.transient(parent)
+		self.parent = parent
+		self.geometry("+100+100")
+		self.title('Buddy settings')
+		
+		self.frm = ttk.Frame(self)
+		self.frm.pack(expand='yes', fill='both')
+		
+		self.isOk = False
+		self.cfg = cfg
+		
+		self.createWidgets()
+	
+	def doModal(self):
+		if self.parent:
+			self.parent.wait_window(self)
+		else:
+			self.wait_window(self)
+		return self.isOk
+		
+	def createWidgets(self):
+		# The notebook
+		self.frm.rowconfigure(0, weight=1)
+		self.frm.rowconfigure(1, weight=0)
+		self.frm.columnconfigure(0, weight=1)
+		self.frm.columnconfigure(1, weight=1)
+		self.wTab = ttk.Notebook(self.frm)
+		self.wTab.grid(column=0, row=0, columnspan=2, padx=5, pady=5, sticky=tk.N+tk.S+tk.W+tk.E)
+		
+		# Main buttons
+		btnOk = ttk.Button(self.frm, text='Ok', command=self.onOk)
+		btnOk.grid(column=0, row=1, sticky=tk.E, padx=20, pady=10)
+		btnCancel = ttk.Button(self.frm, text='Cancel', command=self.onCancel)
+		btnCancel.grid(column=1, row=1, sticky=tk.W, padx=20, pady=10)
+		
+		# Tabs
+		self.createBasicTab()
+		
+	def createBasicTab(self):
+		# Prepare the variables to set/receive values from GUI
+		self.cfgUri = tk.StringVar()
+		self.cfgUri.set( self.cfg.uri )
+		self.cfgSubscribe = tk.IntVar()
+		self.cfgSubscribe.set(self.cfg.subscribe)
+		
+		# Build the tab page
+		frm = ttk.Frame(self.frm)
+		frm.columnconfigure(0, weight=1)
+		frm.columnconfigure(1, weight=2)
+		row = 0
+		ttk.Label(frm, text='URI:').grid(row=row, column=0, sticky=tk.E, pady=2)
+		ttk.Entry(frm, textvariable=self.cfgUri, width=40).grid(row=row, column=1, sticky=tk.W+tk.E, padx=6)
+		row += 1
+		ttk.Checkbutton(frm, text='Subscribe presence', variable=self.cfgSubscribe).grid(row=row, column=1, sticky=tk.W, padx=6, pady=2)
+
+		self.wTab.add(frm, text='Basic Settings')
+		
+	
+	def onOk(self):
+		# Check basic settings
+		errors = "";
+		if self.cfgUri.get():
+			if not endpoint.validateSipUri(self.cfgUri.get()):
+				errors += "Invalid Buddy URI: '%s'\n" % (self.cfgUri.get())
+				
+		if errors:
+			msgbox.showerror("Error detected:", errors)
+			return
+		
+		# Basic settings
+		self.cfg.uri = self.cfgUri.get()
+		self.cfg.subscribe = self.cfgSubscribe.get()
+		
+		self.isOk = True
+		self.destroy()
+		
+	def onCancel(self):
+		self.destroy()
+
+
+if __name__ == '__main__':
+	application.main()
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/importsym.py
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/importsym.py	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/importsym.py	(revision 4657)
@@ -30,4 +30,7 @@
 		return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
 	
+	if sys.platform == 'win32' and not program.endswith(".exe"):
+		program += ".exe"
+
 	fpath, fname = os.path.split(program)
 	if fpath:
@@ -66,5 +69,4 @@
 C_HEADING_SECTION = """
 #define PJ_AUTOCONF		1
-#define CC_DUMMY
 #define jmp_buf			int
 #define __attribute__(x)
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/pjsua2.i
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/pjsua2.i	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/pjsua2.i	(revision 4657)
@@ -37,4 +37,6 @@
 %feature("director") Endpoint; 
 %feature("director") Account;
+%feature("director") Buddy;
+%feature("director") FindBuddyMatch;
 
 
@@ -60,7 +62,11 @@
 %include "pjsua2/types.hpp"
 
+%ignore pj::ContainerNode::op;
+%ignore pj::ContainerNode::data;
 %ignore container_node_op;
+%ignore container_node_internal_data;
 %include "pjsua2/persistent.hpp"
 
+%ignore pj::TransportInfo::TransportInfo(const pjsua_transport_info &info);
 %include "pjsua2/siptypes.hpp"
 
@@ -68,9 +74,11 @@
 %template(AuthCredInfoVector)		std::vector<pj::AuthCredInfo>;
 %template(SipMultipartPartVector)	std::vector<pj::SipMultipartPart>;
+%template(BuddyVector)			std::vector<pj::Buddy*>;
 
 %include "pjsua2/endpoint.hpp"
+%include "pjsua2/presence.hpp"
 %include "pjsua2/account.hpp"
 
-%ignore JsonDocument::allocElement();
-%ignore JsonDocument::getPool();
+%ignore pj::JsonDocument::allocElement;
+%ignore pj::JsonDocument::getPool;
 %include "pjsua2/json.hpp"
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.i
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.i	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.i	(revision 4657)
@@ -55,4 +55,6 @@
 typedef enum pjrpid_activity {PJRPID_ACTIVITY_UNKNOWN, PJRPID_ACTIVITY_AWAY, PJRPID_ACTIVITY_BUSY} pjrpid_activity;
 
+typedef enum pjsip_evsub_state {PJSIP_EVSUB_STATE_NULL, PJSIP_EVSUB_STATE_SENT, PJSIP_EVSUB_STATE_ACCEPTED, PJSIP_EVSUB_STATE_PENDING, PJSIP_EVSUB_STATE_ACTIVE, PJSIP_EVSUB_STATE_TERMINATED, PJSIP_EVSUB_STATE_UNKNOWN} pjsip_evsub_state;
+
 enum pjsua_invalid_id_const_ {PJSUA_INVALID_ID = -1};
 
@@ -73,2 +75,4 @@
 typedef enum pjsua_ipv6_use {PJSUA_IPV6_DISABLED, PJSUA_IPV6_ENABLED} pjsua_ipv6_use;
 
+typedef enum pjsua_buddy_status {PJSUA_BUDDY_STATUS_UNKNOWN, PJSUA_BUDDY_STATUS_ONLINE, PJSUA_BUDDY_STATUS_OFFLINE} pjsua_buddy_status;
+
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.lst
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.lst	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.lst	(revision 4657)
@@ -18,4 +18,5 @@
 
 pjsip-simple/rpid.h		pjrpid_activity
+pjsip-simple/evsub.h		pjsip_evsub_state
 
-pjsua-lib/pjsua.h		pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use
+pjsua-lib/pjsua.h		pjsua_invalid_id_const_ pjsua_state pjsua_stun_use pjsua_call_hold_type pjsua_acc_id pjsua_destroy_flag pjsua_100rel_use pjsua_sip_timer_use pjsua_ipv6_use pjsua_buddy_status
Index: /pjproject/branches/projects/pjsua2/pjsip/build/Makefile
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/build/Makefile	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/build/Makefile	(revision 4657)
@@ -91,5 +91,5 @@
 export PJSUA2_LIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
 			   account.o endpoint.o json.o persistent.o types.o \
-			   siptypes.o
+			   siptypes.o presence.o
 export PJSUA2_LIB_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS)
 export PJSUA2_LIB_CXXFLAGS = $(PJSUA2_LIB_CFLAGS) 
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsip-simple/evsub.h
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsip-simple/evsub.h	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsip-simple/evsub.h	(revision 4657)
@@ -59,5 +59,5 @@
  * in state_str member of the susbcription structure.
  */
-enum pjsip_evsub_state
+typedef enum pjsip_evsub_state
 {
     PJSIP_EVSUB_STATE_NULL,	 /**< State is NULL.			    */
@@ -71,10 +71,5 @@
 				      Application can query the state by 
 				      calling #pjsip_evsub_get_state_name().*/
-};
-
-/**
- * @see pjsip_evsub_state
- */
-typedef enum pjsip_evsub_state pjsip_evsub_state;
+} pjsip_evsub_state;
 
 
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2.hpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2.hpp	(revision 4657)
@@ -22,4 +22,5 @@
 #include <pjsua2/endpoint.hpp>
 #include <pjsua2/account.hpp>
+#include <pjsua2/presence.hpp>
 #include <pjsua2/json.hpp>
 
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/account.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/account.hpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/account.hpp	(revision 4657)
@@ -26,4 +26,5 @@
 #include <pjsua-lib/pjsua.h>
 #include <pjsua2/persistent.hpp>
+#include <pjsua2/presence.hpp>
 #include <pjsua2/siptypes.hpp>
 
@@ -913,25 +914,4 @@
 };
 
-/**
- * This describes account presence status.
- */
-struct AccountPresenceStatus
-{
-    /** Basic status. */
-    bool		isOnline;
-
-    /** Activity type. */
-    pjrpid_activity	activity;
-
-    /** Optional text describing the person/element. */
-    string		note;
-
-    /** Optional RPID ID string. */
-    string		rpidId;
-
-public:
-    AccountPresenceStatus();
-};
-
 
 /**
@@ -1071,4 +1051,11 @@
 {
     /**
+     * Server presence subscription instance. If application delays
+     * the acceptance of the request, it will need to specify this object
+     * when calling Account::presNotify().
+     */
+    void	       *srvPres;
+
+    /**
      *  Sender URI.
      */
@@ -1220,4 +1207,70 @@
 };
 
+/**
+ * Parameters for presNotify() account method.
+ */
+struct PresNotifyParam
+{
+    /**
+     * Server presence subscription instance.
+     */
+    void	       *srvPres;
+
+    /**
+     * Server presence subscription state to set.
+     */
+    pjsip_evsub_state	state;
+    
+    /**
+     * Optionally specify the state string name, if state is not "active",
+     * "pending", or "terminated".
+     */
+    string		stateStr;
+
+    /**
+     * If the new state is PJSIP_EVSUB_STATE_TERMINATED, optionally specify
+     * the termination reason.
+     */
+    string		reason;
+
+    /**
+     * If the new state is PJSIP_EVSUB_STATE_TERMINATED, this specifies
+     * whether the NOTIFY request should contain message body containing
+     * account's presence information.
+     */
+    bool		withBody;
+
+    /**
+     * Optional list of headers to be sent with the NOTIFY request.
+     */
+    SipTxOption		txOption;
+};
+
+
+/**
+ * Wrapper class for Buddy matching algo.
+ *
+ * Default algo is a simple substring lookup of search-token in the
+ * Buddy URIs, with case sensitive. Application can implement its own
+ * matching algo by overriding this class and specifying its instance
+ * in Account::findBuddy().
+ */
+class FindBuddyMatch
+{
+public:
+    /**
+     * Default algo implementation.
+     */
+    virtual bool match(const string &token, const Buddy &buddy)
+    {
+	BuddyInfo bi = buddy.getInfo();
+	return bi.uri.find(token) != string::npos;
+    }
+
+    /**
+     * Destructor.
+     */
+    virtual ~FindBuddyMatch() {}
+};
 
 /**
@@ -1332,5 +1385,5 @@
      * @param pres_st		Presence online status.
      */
-    void setOnlineStatus(const AccountPresenceStatus &pres_st) throw(Error);
+    void setOnlineStatus(const PresenceStatus &pres_st) throw(Error);
 
     /**
@@ -1350,4 +1403,46 @@
     void setTransport(TransportId tp_id) throw(Error);
 
+    /**
+     * Send NOTIFY to inform account presence status or to terminate server
+     * side presence subscription. If application wants to reject the incoming
+     * request, it should set the param \a PresNotifyParam.state to
+     * PJSIP_EVSUB_STATE_TERMINATED.
+     *
+     * @param prm		The sending NOTIFY parameter.
+     */
+    void presNotify(const PresNotifyParam &prm) throw(Error);
+    
+    /**
+     * Enumerate all buddies of the account.
+     *
+     * @return			The buddy list.
+     */
+    const BuddyVector& enumBuddies() const throw(Error);
+
+    /**
+     * Find a buddy in the buddy list with the specified URI. 
+     *
+     * Exception: if buddy is not found, PJ_ENOTFOUND will be thrown.
+     *
+     * @param uri		The buddy URI.
+     * @param buddy_match	The buddy match algo.
+     *
+     * @return			The pointer to buddy.
+     */
+    Buddy* findBuddy(string uri, FindBuddyMatch *buddy_match = NULL) const
+		    throw(Error);
+
+    /**
+     * An internal function to add a Buddy to Account buddy list.
+     * This function must never be used by application.
+     */
+    void addBuddy(Buddy *buddy);
+
+    /**
+     * An internal function to remove a Buddy from Account buddy list.
+     * This function must never be used by application.
+     */
+    void removeBuddy(Buddy *buddy);
+
 public:
     /*
@@ -1360,5 +1455,5 @@
      */
     virtual void onIncomingCall(OnIncomingCallParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -1371,5 +1466,5 @@
      */
     virtual void onRegStarted(OnRegStartedParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -1381,5 +1476,5 @@
      */
     virtual void onRegState(OnRegStateParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -1403,6 +1498,6 @@
      *    user permission whether to accept or reject the request. In this
      *	  case, the application MUST set the IncomingSubscribeParam.code
-     *	  argument to 202, then IMMEDIATELY calls #pjsua_pres_notify() with
-     *	  state PJSIP_EVSUB_STATE_PENDING and later calls #pjsua_pres_notify()
+     *	  argument to 202, then IMMEDIATELY calls presNotify() with
+     *	  state PJSIP_EVSUB_STATE_PENDING and later calls presNotify()
      *    again to accept or reject the subscription request.
      *
@@ -1416,5 +1511,5 @@
      */
     virtual void onIncomingSubscribe(OnIncomingSubscribeParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -1425,5 +1520,5 @@
      */
     virtual void onInstantMessage(OnInstantMessageParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -1434,5 +1529,5 @@
      */
     virtual void onInstantMessageStatus(OnInstantMessageStatusParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -1442,5 +1537,5 @@
      */
     virtual void onTypingIndication(OnTypingIndicationParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -1453,5 +1548,5 @@
      */
     virtual void onMwiInfo(OnMwiInfoParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
 protected:
@@ -1461,4 +1556,5 @@
     pjsua_acc_id 	 id;
     string		 tmpReason;	// for saving response's reason
+    BuddyVector		 buddyList;
 };
 
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/endpoint.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/endpoint.hpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/endpoint.hpp	(revision 4657)
@@ -960,5 +960,5 @@
     virtual void onNatDetectionComplete(
 			const OnNatDetectionCompleteParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -970,5 +970,5 @@
     virtual void onNatCheckStunServersComplete(
 			const OnNatCheckStunServersCompleteParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -979,5 +979,5 @@
     virtual void onTransportState(
 			const OnTransportStateParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -988,5 +988,5 @@
      */
     virtual void onTimer(const OnTimerParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
     /**
@@ -1006,5 +1006,5 @@
      */
     virtual void onSelectAccount(OnSelectAccountParam &prm)
-    {}
+    { PJ_UNUSED_ARG(prm); }
 
 
@@ -1071,4 +1071,6 @@
     static void on_mwi_info(pjsua_acc_id acc_id,
                             pjsua_mwi_info *mwi_info);
+    static void on_buddy_state(pjsua_buddy_id buddy_id);
+
 };
 
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/persistent.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/persistent.hpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/persistent.hpp	(revision 4657)
@@ -334,4 +334,272 @@
 				      throw(Error);
 };
+
+
+/**
+ * Forward declaration of container_node_op.
+ */
+struct container_node_op;
+
+
+/**
+ * Internal data for ContainerNode. See ContainerNode implementation notes
+ * for more info.
+ */
+struct container_node_internal_data
+{
+    void	*doc;
+    void	*data1;
+    void	*data2;
+};
+
+/**
+ * A container node is a placeholder for storing other data elements, which
+ * could be boolean, number, string, array of strings, or another container.
+ * Each data in the container is basically a name/value pair, with a type
+ * internally associated with it so that written data can be read in the
+ * correct type. Data is read and written serially, hence the order of
+ * reading must be the same as the order of writing.
+ *
+ * Application can read data from it by using the various read methods, and
+ * write data to it using the various write methods. Alternatively, it
+ * may be more convenient to use the provided macros below to read and write
+ * the data, because these macros set the name automatically:
+ *	- NODE_READ_BOOL(node,item)
+ *	- NODE_READ_UNSIGNED(node,item)
+ *	- NODE_READ_INT(node,item)
+ *	- NODE_READ_FLOAT(node,item)
+ *	- NODE_READ_NUM_T(node,type,item)
+ *	- NODE_READ_STRING(node,item)
+ *	- NODE_READ_STRINGV(node,item)
+ *	- NODE_READ_OBJ(node,item)
+ *	- NODE_WRITE_BOOL(node,item)
+ *	- NODE_WRITE_UNSIGNED(node,item)
+ *	- NODE_WRITE_INT(node,item)
+ *	- NODE_WRITE_FLOAT(node,item)
+ *	- NODE_WRITE_NUM_T(node,type,item)
+ *	- NODE_WRITE_STRING(node,item)
+ *	- NODE_WRITE_STRINGV(node,item)
+ *	- NODE_WRITE_OBJ(node,item)
+ *
+ * Implementation notes:
+ *
+ * The ContainerNode class is subclass-able, but not in the usual C++ way.
+ * With the usual C++ inheritance, some methods will be made pure virtual
+ * and must be implemented by the actual class. However, doing so will
+ * require dynamic instantiation of the ContainerNode class, which means
+ * we will need to pass around the class as pointer, for example as the
+ * return value of readContainer() and writeNewContainer() methods. Then
+ * we will need to establish who needs or how to delete these objects, or
+ * use shared pointer mechanism, each of which is considered too inconvenient
+ * or complicated for the purpose.
+ *
+ * So hence we use C style "inheritance", where the methods are declared in
+ * container_node_op and the data in container_node_internal_data structures.
+ * An implementation of ContainerNode class will need to set up these members
+ * with values that makes sense to itself. The methods in container_node_op
+ * contains the pointer to the actual implementation of the operation, which
+ * would be specific according to the format of the document. The methods in
+ * this ContainerNode class are just thin wrappers which call the
+ * implementation in the container_node_op structure.
+ *
+ */
+class ContainerNode
+{
+public:
+    /**
+     * Determine if there is unread element. If yes, then app can use one of
+     * the readXxx() functions to read it.
+     */
+    bool		hasUnread() const;
+
+    /**
+     * Get the name of the next unread element.
+     */
+    string		unreadName() const throw(Error);
+
+    /**
+     * Read an integer value from the document and return the value.
+     * This will throw Error if the current element is not a number.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    int			readInt(const string &name="") const throw(Error);
+
+    /**
+     * Read a number value from the document and return the value.
+     * This will throw Error if the current element is not a number.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    float		readNumber(const string &name="") const throw(Error);
+
+    /**
+     * Read a boolean value from the container and return the value.
+     * This will throw Error if the current element is not a boolean.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    bool		readBool(const string &name="") const throw(Error);
+
+    /**
+     * Read a string value from the container and return the value.
+     * This will throw Error if the current element is not a string.
+     * The read position will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    string		readString(const string &name="") const throw(Error);
+
+    /**
+     * Read a string array from the container. This will throw Error
+     * if the current element is not a string array. The read position
+     * will be advanced to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		The value.
+     */
+    StringVector	readStringVector(const string &name="") const
+					 throw(Error);
+
+    /**
+     * Read the specified object from the container. This is equal to
+     * calling PersistentObject.readObject(ContainerNode);
+     *
+     * @param obj	The object to read.
+     */
+    void		readObject(PersistentObject &obj) const throw(Error);
+
+    /**
+     * Read a container from the container. This will throw Error if the
+     * current element is not a container. The read position will be advanced
+     * to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		Container object.
+     */
+    ContainerNode	readContainer(const string &name="") const
+				      throw(Error);
+
+    /**
+     * Read array container from the container. This will throw Error if the
+     * current element is not an array. The read position will be advanced
+     * to the next element.
+     *
+     * @param name	If specified, then the function will check if the
+     * 			name of the next element matches the specified
+     * 			name and throw Error if it doesn't match.
+     *
+     * @return		Container object.
+     */
+    ContainerNode	readArray(const string &name="") const
+				  throw(Error);
+
+    /**
+     * Write a number value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param value	The value to be written.
+     */
+    void		writeNumber(const string &name,
+        		            float num) throw(Error);
+
+    /**
+     * Write a number value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param value	The value to be written.
+     */
+    void		writeInt(const string &name,
+        		         int num) throw(Error);
+
+    /**
+     * Write a boolean value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param value	The value to be written.
+     */
+    void		writeBool(const string &name,
+        		          bool value) throw(Error);
+
+    /**
+     * Write a string value to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param value	The value to be written.
+     */
+    void		writeString(const string &name,
+        		            const string &value) throw(Error);
+
+    /**
+     * Write string vector to the container.
+     *
+     * @param name	The name for the value in the container.
+     * @param array	The vector to be written.
+     */
+    void		writeStringVector(const string &name,
+        		                  const StringVector &value)
+					  throw(Error);
+
+    /**
+     * Write an object to the container. This is equal to calling
+     * PersistentObject.writeObject(ContainerNode);
+     *
+     * @param obj	The object to be written
+     */
+    void		writeObject(const PersistentObject &obj) throw(Error);
+
+    /**
+     * Create and write an empty Object node that can be used as parent
+     * for subsequent write operations.
+     *
+     * @param name	The name for the new container in the container.
+     *
+     * @return		A sub-container.
+     */
+    ContainerNode 	writeNewContainer(const string &name)
+					  throw(Error);
+
+    /**
+     * Create and write an empty array node that can be used as parent
+     * for subsequent write operations.
+     *
+     * @param name	The name for the array.
+     *
+     * @return		A sub-container.
+     */
+    ContainerNode 	writeNewArray(const string &name)
+				      throw(Error);
+
+public:
+    /* internal data */
+    container_node_op *op;
+    container_node_internal_data data;
+};
+
 
 /**
@@ -387,263 +655,4 @@
 };
 
-/**
- * Internal data for ContainerNode. See ContainerNode implementation notes
- * for more info.
- */
-struct container_node_internal_data
-{
-    void	*doc;
-    void	*data1;
-    void	*data2;
-};
-
-/**
- * A container node is a placeholder for storing other data elements, which
- * could be boolean, number, string, array of strings, or another container.
- * Each data in the container is basically a name/value pair, with a type
- * internally associated with it so that written data can be read in the
- * correct type. Data is read and written serially, hence the order of
- * reading must be the same as the order of writing.
- *
- * Application can read data from it by using the various read methods, and
- * write data to it using the various write methods. Alternatively, it
- * may be more convenient to use the provided macros below to read and write
- * the data, because these macros set the name automatically:
- *	- NODE_READ_BOOL(node,item)
- *	- NODE_READ_UNSIGNED(node,item)
- *	- NODE_READ_INT(node,item)
- *	- NODE_READ_FLOAT(node,item)
- *	- NODE_READ_NUM_T(node,type,item)
- *	- NODE_READ_STRING(node,item)
- *	- NODE_READ_STRINGV(node,item)
- *	- NODE_READ_OBJ(node,item)
- *	- NODE_WRITE_BOOL(node,item)
- *	- NODE_WRITE_UNSIGNED(node,item)
- *	- NODE_WRITE_INT(node,item)
- *	- NODE_WRITE_FLOAT(node,item)
- *	- NODE_WRITE_NUM_T(node,type,item)
- *	- NODE_WRITE_STRING(node,item)
- *	- NODE_WRITE_STRINGV(node,item)
- *	- NODE_WRITE_OBJ(node,item)
- *
- * Implementation notes:
- *
- * The ContainerNode class is subclass-able, but not in the usual C++ way.
- * With the usual C++ inheritance, some methods will be made pure virtual
- * and must be implemented by the actual class. However, doing so will
- * require dynamic instantiation of the ContainerNode class, which means
- * we will need to pass around the class as pointer, for example as the
- * return value of readContainer() and writeNewContainer() methods. Then
- * we will need to establish who needs or how to delete these objects, or
- * use shared pointer mechanism, each of which is considered too inconvenient
- * or complicated for the purpose.
- *
- * So hence we use C style "inheritance", where the methods are declared in
- * container_node_op and the data in container_node_internal_data structures.
- * An implementation of ContainerNode class will need to set up these members
- * with values that makes sense to itself. The methods in container_node_op
- * contains the pointer to the actual implementation of the operation, which
- * would be specific according to the format of the document. The methods in
- * this ContainerNode class are just thin wrappers which call the
- * implementation in the container_node_op structure.
- *
- */
-class ContainerNode
-{
-public:
-    /**
-     * Determine if there is unread element. If yes, then app can use one of
-     * the readXxx() functions to read it.
-     */
-    bool		hasUnread() const;
-
-    /**
-     * Get the name of the next unread element.
-     */
-    string		unreadName() const throw(Error);
-
-    /**
-     * Read an integer value from the document and return the value.
-     * This will throw Error if the current element is not a number.
-     * The read position will be advanced to the next element.
-     *
-     * @param name	If specified, then the function will check if the
-     * 			name of the next element matches the specified
-     * 			name and throw Error if it doesn't match.
-     *
-     * @return		The value.
-     */
-    int			readInt(const string &name="") const throw(Error);
-
-    /**
-     * Read a number value from the document and return the value.
-     * This will throw Error if the current element is not a number.
-     * The read position will be advanced to the next element.
-     *
-     * @param name	If specified, then the function will check if the
-     * 			name of the next element matches the specified
-     * 			name and throw Error if it doesn't match.
-     *
-     * @return		The value.
-     */
-    float		readNumber(const string &name="") const throw(Error);
-
-    /**
-     * Read a boolean value from the container and return the value.
-     * This will throw Error if the current element is not a boolean.
-     * The read position will be advanced to the next element.
-     *
-     * @param name	If specified, then the function will check if the
-     * 			name of the next element matches the specified
-     * 			name and throw Error if it doesn't match.
-     *
-     * @return		The value.
-     */
-    bool		readBool(const string &name="") const throw(Error);
-
-    /**
-     * Read a string value from the container and return the value.
-     * This will throw Error if the current element is not a string.
-     * The read position will be advanced to the next element.
-     *
-     * @param name	If specified, then the function will check if the
-     * 			name of the next element matches the specified
-     * 			name and throw Error if it doesn't match.
-     *
-     * @return		The value.
-     */
-    string		readString(const string &name="") const throw(Error);
-
-    /**
-     * Read a string array from the container. This will throw Error
-     * if the current element is not a string array. The read position
-     * will be advanced to the next element.
-     *
-     * @param name	If specified, then the function will check if the
-     * 			name of the next element matches the specified
-     * 			name and throw Error if it doesn't match.
-     *
-     * @return		The value.
-     */
-    StringVector	readStringVector(const string &name="") const
-					 throw(Error);
-
-    /**
-     * Read the specified object from the container. This is equal to
-     * calling PersistentObject.readObject(ContainerNode);
-     *
-     * @param obj	The object to read.
-     */
-    void		readObject(PersistentObject &obj) const throw(Error);
-
-    /**
-     * Read a container from the container. This will throw Error if the
-     * current element is not a container. The read position will be advanced
-     * to the next element.
-     *
-     * @param name	If specified, then the function will check if the
-     * 			name of the next element matches the specified
-     * 			name and throw Error if it doesn't match.
-     *
-     * @return		Container object.
-     */
-    ContainerNode	readContainer(const string &name="") const
-				      throw(Error);
-
-    /**
-     * Read array container from the container. This will throw Error if the
-     * current element is not an array. The read position will be advanced
-     * to the next element.
-     *
-     * @param name	If specified, then the function will check if the
-     * 			name of the next element matches the specified
-     * 			name and throw Error if it doesn't match.
-     *
-     * @return		Container object.
-     */
-    ContainerNode	readArray(const string &name="") const
-				  throw(Error);
-
-    /**
-     * Write a number value to the container.
-     *
-     * @param name	The name for the value in the container.
-     * @param value	The value to be written.
-     */
-    void		writeNumber(const string &name,
-        		            float num) throw(Error);
-
-    /**
-     * Write a number value to the container.
-     *
-     * @param name	The name for the value in the container.
-     * @param value	The value to be written.
-     */
-    void		writeInt(const string &name,
-        		         int num) throw(Error);
-
-    /**
-     * Write a boolean value to the container.
-     *
-     * @param name	The name for the value in the container.
-     * @param value	The value to be written.
-     */
-    void		writeBool(const string &name,
-        		          bool value) throw(Error);
-
-    /**
-     * Write a string value to the container.
-     *
-     * @param name	The name for the value in the container.
-     * @param value	The value to be written.
-     */
-    void		writeString(const string &name,
-        		            const string &value) throw(Error);
-
-    /**
-     * Write string vector to the container.
-     *
-     * @param name	The name for the value in the container.
-     * @param array	The vector to be written.
-     */
-    void		writeStringVector(const string &name,
-        		                  const StringVector &value)
-					  throw(Error);
-
-    /**
-     * Write an object to the container. This is equal to calling
-     * PersistentObject.writeObject(ContainerNode);
-     *
-     * @param obj	The object to be written
-     */
-    void		writeObject(const PersistentObject &obj) throw(Error);
-
-    /**
-     * Create and write an empty Object node that can be used as parent
-     * for subsequent write operations.
-     *
-     * @param name	The name for the new container in the container.
-     *
-     * @return		A sub-container.
-     */
-    ContainerNode 	writeNewContainer(const string &name)
-					  throw(Error);
-
-    /**
-     * Create and write an empty array node that can be used as parent
-     * for subsequent write operations.
-     *
-     * @param name	The name for the array.
-     *
-     * @return		A sub-container.
-     */
-    ContainerNode 	writeNewArray(const string &name)
-				      throw(Error);
-
-public:
-    /* internal data */
-    container_node_op *op;
-    container_node_internal_data data;
-};
 
 /*
@@ -660,7 +669,7 @@
 
 #define NODE_WRITE_BOOL(node,item)	node.writeBool(#item, item)
-#define NODE_WRITE_UNSIGNED(node,item)	node.writeNumber(#item, item)
-#define NODE_WRITE_INT(node,item)	node.writeNumber(#item, item)
-#define NODE_WRITE_NUM_T(node,T,item)	node.writeNumber(#item, (int)item)
+#define NODE_WRITE_UNSIGNED(node,item)	node.writeNumber(#item, (float)item)
+#define NODE_WRITE_INT(node,item)	node.writeNumber(#item, (float)item)
+#define NODE_WRITE_NUM_T(node,T,item)	node.writeNumber(#item, (float)item)
 #define NODE_WRITE_FLOAT(node,item)	node.writeNumber(#item, item)
 #define NODE_WRITE_STRING(node,item)	node.writeString(#item, item)
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/presence.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/presence.hpp	(revision 4657)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/presence.hpp	(revision 4657)
@@ -0,0 +1,294 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __PJSUA2_PRESENCE_HPP__
+#define __PJSUA2_PRESENCE_HPP__
+
+/**
+ * @file pjsua2/presence.hpp
+ * @brief PJSUA2 Presence Operations
+ */
+#include <pjsua2/persistent.hpp>
+#include <pjsua2/siptypes.hpp>
+
+/**
+ * @defgroup PJSUA2_PRES Presence
+ * @ingroup PJSUA2_Ref
+ * @{
+ */
+
+/** PJSUA2 API is inside pj namespace */
+namespace pj
+{
+using std::string;
+using std::vector;
+
+
+/**
+ * This describes presence status.
+ */
+struct PresenceStatus
+{
+    /**
+     * Buddy's online status.
+     */
+    pjsua_buddy_status	 status;
+
+    /**
+     * Text to describe buddy's online status.
+     */
+    string		 statusText;
+    
+    /**
+     * Activity type.
+     */
+    pjrpid_activity	 activity;
+
+    /**
+     * Optional text describing the person/element.
+     */
+    string		 note;
+
+    /**
+     * Optional RPID ID string.
+     */
+    string		 rpidId;
+
+public:
+    /**
+     * Constructor.
+     */
+    PresenceStatus();
+};
+
+
+/**
+ * This structure describes buddy configuration when adding a buddy to
+ * the buddy list with Buddy::create().
+ */
+struct BuddyConfig : public PersistentObject
+{
+    /**
+     * Buddy URL or name address.
+     */
+    string	 	 uri;
+
+    /**
+     * Specify whether presence subscription should start immediately.
+     */
+    bool	 	 subscribe;
+
+public:
+    /**
+     * Read this object from a container node.
+     *
+     * @param node		Container to read values from.
+     */
+    virtual void readObject(const ContainerNode &node) throw(Error);
+
+    /**
+     * Write this object to a container node.
+     *
+     * @param node		Container to write values to.
+     */
+    virtual void writeObject(ContainerNode &node) const throw(Error);
+};
+
+
+/**
+ * This structure describes buddy info, which can be retrieved by via
+ * Buddy::getInfo().
+ */
+struct BuddyInfo
+{
+    /**
+     * The full URI of the buddy, as specified in the configuration.
+     */
+    string		 uri;
+
+    /**
+     * Buddy's Contact, only available when presence subscription has
+     * been established to the buddy.
+     */
+    string		 contact;
+
+    /**
+     * Flag to indicate that we should monitor the presence information for
+     * this buddy (normally yes, unless explicitly disabled).
+     */
+    bool		 presMonitorEnabled;
+
+    /**
+     * If \a presMonitorEnabled is true, this specifies the last state of
+     * the presence subscription. If presence subscription session is currently
+     * active, the value will be PJSIP_EVSUB_STATE_ACTIVE. If presence
+     * subscription request has been rejected, the value will be
+     * PJSIP_EVSUB_STATE_TERMINATED, and the termination reason will be
+     * specified in \a subTermReason.
+     */
+    pjsip_evsub_state	 subState;
+
+    /**
+     * String representation of subscription state.
+     */
+    string	         subStateName;
+
+    /**
+     * 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 subTermReason field.
+     */
+    pjsip_status_code	 subTermCode;
+
+    /**
+     * Specifies the last presence subscription termination reason. If 
+     * presence subscription is currently active, the value will be empty.
+     */
+    string		 subTermReason;
+
+    /**
+     * Presence status.
+     */
+    PresenceStatus	 presStatus;
+    
+public:
+    void fromPj(const pjsua_buddy_info &pbi);
+};
+
+
+/**
+ * Buddy.
+ */
+class Buddy
+{
+public:
+    /**
+     * Constructor.
+     */
+    Buddy();
+    
+    /**
+     * Destructor. Note that if the Buddy instance is deleted, it will also
+     * delete the corresponding buddy in the PJSUA-LIB.
+     */
+    virtual ~Buddy();
+    
+    /**
+     * Create buddy and register the buddy to PJSUA-LIB.
+     *
+     * @param acc		The account for this buddy.
+     * @param cfg		The buddy config.
+     */
+    void create(Account &acc, const BuddyConfig &cfg) throw(Error);
+    
+    /**
+     * Check if this buddy is valid.
+     *
+     * @return			True if it is.
+     */
+    bool isValid() const;
+
+    /**
+     * Get detailed buddy info.
+     *
+     * @return			Buddy info.
+     */
+    BuddyInfo getInfo() const throw(Error);
+
+    /**
+     * Enable/disable buddy's presence monitoring. Once buddy's presence is
+     * subscribed, application will be informed about buddy's presence status
+     * changed via \a onBuddyState() callback.
+     *
+     * @param subscribe		Specify true to activate presence
+     *				subscription.
+     */
+    void subscribePresence(bool subscribe) throw(Error);
+    
+    /**
+     * Update the presence information for the buddy. Although the library
+     * periodically refreshes the presence subscription for all buddies,
+     * some application may want to refresh the buddy's presence subscription
+     * immediately, and in this case it can use this function to accomplish
+     * this.
+     *
+     * Note that the buddy's presence subscription will only be initiated
+     * if presence monitoring is enabled for the buddy. See
+     * subscribePresence() for more info. Also if presence subscription for
+     * the buddy is already active, this function will not do anything.
+     *
+     * Once the presence subscription is activated successfully for the buddy,
+     * application will be notified about the buddy's presence status in the
+     * \a onBuddyState() callback.
+     */
+     void updatePresence(void) throw(Error);
+     
+    /**
+     * Send instant messaging outside dialog, using this buddy's specified
+     * account for route set and authentication.
+     *
+     * @param prm	Sending instant message parameter.
+     */
+    void sendInstantMessage(const SendInstantMessageParam &prm) throw(Error);
+
+    /**
+     * Send typing indication outside dialog.
+     *
+     * @param prm	Sending instant message parameter.
+     */
+    void sendTypingIndication(const SendTypingIndicationParam &prm)
+	 throw(Error);
+
+public:
+    /*
+     * Callbacks
+     */
+     
+    /**
+     * Notify application when the buddy state has changed.
+     * Application may then query the buddy info to get the details.
+     */
+    virtual void onBuddyState()
+    {}
+     
+private:
+     /**
+      * Buddy ID.
+      */
+     pjsua_buddy_id	 id;
+     
+     /**
+      * Account.
+      */
+     Account		*acc;
+};
+
+
+/** Array of buddies */
+typedef std::vector<Buddy*> BuddyVector;
+
+
+} // namespace pj
+
+/**
+ * @}  // PJSUA2_PRES
+ */
+
+#endif	/* __PJSUA2_PRESENCE_HPP__ */
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/siptypes.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/siptypes.hpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/siptypes.hpp	(revision 4657)
@@ -589,4 +589,50 @@
      */
     void toPj(pjsua_msg_data &msg_data) const;
+};
+
+
+/**
+ * This structure contains parameters for sending instance message methods,
+ * e.g: Buddy::sendInstantMessage(), Call:sendInstantMessage().
+ */
+struct SendInstantMessageParam
+{
+    /**
+     * MIME type. Default is "text/plain".
+     */
+    string contentType;
+    
+    /**
+     * The message content.
+     */
+    string content;
+    
+    /**
+     * List of headers etc to be included in outgoing request.
+     */
+    SipTxOption txOption;
+
+    /**
+     * User data, which will be given back when the IM callback is called.
+     */
+    Token userData;
+};
+
+
+/**
+ * This structure contains parameters for sending typing indication methods,
+ * e.g: Buddy::sendTypingIndication(), Call:sendTypingIndication().
+ */
+struct SendTypingIndicationParam
+{
+    /**
+     * True to indicate to remote that local person is currently typing an IM.
+     */
+    bool isTyping;
+    
+    /**
+     * List of headers etc to be included in outgoing request.
+     */
+    SipTxOption txOption;
 };
 
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/types.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/types.hpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/types.hpp	(revision 4657)
@@ -20,4 +20,8 @@
 #define __PJSUA2_TYPES_HPP__
 
+#ifdef _MSC_VER
+#   pragma warning( disable : 4290 ) // exception spec ignored
+#endif
+
 /**
  * @file pjsua2/types.hpp
Index: /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/account.cpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/account.cpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/account.cpp	(revision 4657)
@@ -19,4 +19,5 @@
 #include <pjsua2/account.hpp>
 #include <pjsua2/endpoint.hpp>
+#include <pjsua2/presence.hpp>
 #include <pj/ctype.h>
 #include "util.hpp"
@@ -446,5 +447,5 @@
     regConfig.firstRetryIntervalSec = prm.reg_first_retry_interval;
     regConfig.delayBeforeRefreshSec = prm.reg_delay_before_refresh;
-    regConfig.dropCallsOnFail	= prm.drop_calls_on_reg_fail;
+    regConfig.dropCallsOnFail	= PJ2BOOL(prm.drop_calls_on_reg_fail);
     regConfig.unregWaitSec	= prm.unreg_timeout;
     regConfig.proxyUse		= prm.reg_use_proxy;
@@ -484,5 +485,5 @@
     sipConfig.contactParams	= pj2Str(prm.contact_params);
     sipConfig.contactUriParams	= pj2Str(prm.contact_uri_params);
-    sipConfig.authInitialEmpty	= prm.auth_pref.initial_auth;
+    sipConfig.authInitialEmpty	= PJ2BOOL(prm.auth_pref.initial_auth);
     sipConfig.authInitialAlgorithm = pj2Str(prm.auth_pref.algorithm);
     sipConfig.transportId	= prm.transport_id;
@@ -504,11 +505,11 @@
 	hdr = hdr->next;
     }
-    presConfig.publishEnabled	= prm.publish_enabled;
-    presConfig.publishQueue	= prm.publish_opt.queue_request;
+    presConfig.publishEnabled	= PJ2BOOL(prm.publish_enabled);
+    presConfig.publishQueue	= PJ2BOOL(prm.publish_opt.queue_request);
     presConfig.publishShutdownWaitMsec = prm.unpublish_max_wait_time_msec;
     presConfig.pidfTupleId	= pj2Str(prm.pidf_tuple_id);
 
     // AccountMwiConfig
-    mwiConfig.enabled		= prm.mwi_enabled;
+    mwiConfig.enabled		= PJ2BOOL(prm.mwi_enabled);
     mwiConfig.expirationSec	= prm.mwi_expires;
 
@@ -517,11 +518,11 @@
     natConfig.mediaStunUse	= prm.media_stun_use;
     if (prm.ice_cfg_use == PJSUA_ICE_CONFIG_USE_CUSTOM) {
-	natConfig.iceEnabled = prm.ice_cfg.enable_ice;
+	natConfig.iceEnabled = PJ2BOOL(prm.ice_cfg.enable_ice);
 	natConfig.iceMaxHostCands = prm.ice_cfg.ice_max_host_cands;
-	natConfig.iceAggressiveNomination = prm.ice_cfg.ice_opt.aggressive;
+	natConfig.iceAggressiveNomination = PJ2BOOL(prm.ice_cfg.ice_opt.aggressive);
 	natConfig.iceNominatedCheckDelayMsec = prm.ice_cfg.ice_opt.nominated_check_delay;
 	natConfig.iceWaitNominationTimeoutMsec = prm.ice_cfg.ice_opt.controlled_agent_want_nom_timeout;
-	natConfig.iceNoRtcp	= prm.ice_cfg.ice_no_rtcp;
-	natConfig.iceAlwaysUpdate = prm.ice_cfg.ice_always_update;
+	natConfig.iceNoRtcp	= PJ2BOOL(prm.ice_cfg.ice_no_rtcp);
+	natConfig.iceAlwaysUpdate = PJ2BOOL(prm.ice_cfg.ice_always_update);
     } else {
 	pjsua_media_config default_mcfg;
@@ -530,15 +531,15 @@
 	    mcfg = &default_mcfg;
 	}
-	natConfig.iceEnabled	= mcfg->enable_ice;
+	natConfig.iceEnabled	= PJ2BOOL(mcfg->enable_ice);
 	natConfig.iceMaxHostCands= mcfg->ice_max_host_cands;
-	natConfig.iceAggressiveNomination = mcfg->ice_opt.aggressive;
+	natConfig.iceAggressiveNomination = PJ2BOOL(mcfg->ice_opt.aggressive);
 	natConfig.iceNominatedCheckDelayMsec = mcfg->ice_opt.nominated_check_delay;
 	natConfig.iceWaitNominationTimeoutMsec = mcfg->ice_opt.controlled_agent_want_nom_timeout;
-	natConfig.iceNoRtcp	= mcfg->ice_no_rtcp;
-	natConfig.iceAlwaysUpdate = mcfg->ice_always_update;
+	natConfig.iceNoRtcp	= PJ2BOOL(mcfg->ice_no_rtcp);
+	natConfig.iceAlwaysUpdate = PJ2BOOL(mcfg->ice_always_update);
     }
 
     if (prm.turn_cfg_use == PJSUA_TURN_CONFIG_USE_CUSTOM) {
-	natConfig.turnEnabled	= prm.turn_cfg.enable_turn;
+	natConfig.turnEnabled	= PJ2BOOL(prm.turn_cfg.enable_turn);
 	natConfig.turnServer	= pj2Str(prm.turn_cfg.turn_server);
 	natConfig.turnConnType	= prm.turn_cfg.turn_conn_type;
@@ -552,5 +553,5 @@
 	    mcfg = &default_mcfg;
 	}
-	natConfig.turnEnabled	= mcfg->enable_turn;
+	natConfig.turnEnabled	= PJ2BOOL(mcfg->enable_turn);
 	natConfig.turnServer	= pj2Str(mcfg->turn_server);
 	natConfig.turnConnType	= mcfg->turn_conn_type;
@@ -571,7 +572,7 @@
     // AccountMediaConfig
     mediaConfig.transportConfig.fromPj(prm.rtp_cfg);
-    mediaConfig.lockCodecEnabled= prm.lock_codec;
+    mediaConfig.lockCodecEnabled= PJ2BOOL(prm.lock_codec);
 #if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0)
-    mediaConfig.streamKaEnabled	= prm.use_stream_ka;
+    mediaConfig.streamKaEnabled	= PJ2BOOL(prm.use_stream_ka);
 #else
     mediaConfig.streamKaEnabled	= false;
@@ -582,6 +583,6 @@
 
     // AccountVideoConfig
-    videoConfig.autoShowIncoming 	= prm.vid_in_auto_show;
-    videoConfig.autoTransmitOutgoing	= prm.vid_out_auto_transmit;
+    videoConfig.autoShowIncoming 	= PJ2BOOL(prm.vid_in_auto_show);
+    videoConfig.autoTransmitOutgoing	= PJ2BOOL(prm.vid_out_auto_transmit);
     videoConfig.windowFlags		= prm.vid_wnd_flags;
     videoConfig.defaultCaptureDevice	= prm.vid_cap_dev;
@@ -623,11 +624,4 @@
 }
 
-
-///////////////////////////////////////////////////////////////////////////////
-
-AccountPresenceStatus::AccountPresenceStatus()
-: isOnline(false), activity(PJRPID_ACTIVITY_UNKNOWN)
-{
-}
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -662,4 +656,10 @@
      */
     if (isValid() && pjsua_get_state() < PJSUA_STATE_CLOSING) {
+        // Cleanup buddies in the buddy list
+	while(buddyList.size() > 0) {
+	    Buddy *b = buddyList[0];
+	    delete b;
+	}
+
 	PJSUA2_CHECK_EXPR( pjsua_acc_set_user_data(id, NULL) );
 	PJSUA2_CHECK_EXPR( pjsua_acc_del(id) );
@@ -724,5 +724,5 @@
 
 void
-Account::setOnlineStatus(const AccountPresenceStatus &pres_st) throw(Error)
+Account::setOnlineStatus(const PresenceStatus &pres_st) throw(Error)
 {
     pjrpid_element pj_rpid;
@@ -734,6 +734,7 @@
     pj_rpid.note	= str2Pj(pres_st.note);
 
-    PJSUA2_CHECK_EXPR( pjsua_acc_set_online_status2(id, pres_st.isOnline,
-                                                    &pj_rpid) );
+    PJSUA2_CHECK_EXPR( pjsua_acc_set_online_status2(
+			    id, pres_st.status == PJSUA_BUDDY_STATUS_ONLINE,
+			    &pj_rpid) );
 }
 
@@ -743,2 +744,56 @@
 }
 
+void Account::presNotify(const PresNotifyParam &prm) throw(Error)
+{
+    pj_str_t pj_state_str   = str2Pj(prm.stateStr);
+    pj_str_t pj_reason	    = str2Pj(prm.reason);
+    pjsua_msg_data msg_data;
+    prm.txOption.toPj(msg_data);
+
+    PJSUA2_CHECK_EXPR( pjsua_pres_notify(id, (pjsua_srv_pres*)prm.srvPres,
+					 prm.state, &pj_state_str,
+					 &pj_reason, prm.withBody,
+					 &msg_data) );
+}
+
+const BuddyVector& Account::enumBuddies() const throw(Error)
+{
+    return buddyList;
+}
+
+Buddy* Account::findBuddy(string uri, FindBuddyMatch *buddy_match) const
+		throw(Error)
+{
+    if (!buddy_match) {
+	static FindBuddyMatch def_bm;
+	buddy_match = &def_bm;
+    }
+
+    for (unsigned i = 0; i < buddyList.size(); i++) {
+	if (buddy_match->match(uri, *buddyList[i]))
+	    return buddyList[i];
+    }
+    PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
+}
+
+void Account::addBuddy(Buddy *buddy)
+{
+    pj_assert(buddy);
+
+    buddyList.push_back(buddy);
+}
+
+void Account::removeBuddy(Buddy *buddy)
+{
+    pj_assert(buddy);
+
+    BuddyVector::iterator it;
+    for (it = buddyList.begin(); it != buddyList.end(); it++) {
+	if (*it == buddy) {
+	    buddyList.erase(it);
+	    return;
+	}
+    }
+
+    pj_assert(!"Bug! Buddy to be removed is not in the buddy list!");
+}
Index: /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/endpoint.cpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/endpoint.cpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/endpoint.cpp	(revision 4657)
@@ -19,4 +19,5 @@
 #include <pjsua2/endpoint.hpp>
 #include <pjsua2/account.hpp>
+#include <pjsua2/presence.hpp>
 #include "util.hpp"
 
@@ -63,7 +64,7 @@
     }
 
-    this->stunIgnoreFailure = ua_cfg.stun_ignore_failure;
+    this->stunIgnoreFailure = PJ2BOOL(ua_cfg.stun_ignore_failure);
     this->natTypeInSdp = ua_cfg.nat_type_in_sdp;
-    this->mwiUnsolicitedEnabled = ua_cfg.enable_unsolicited_mwi;
+    this->mwiUnsolicitedEnabled = PJ2BOOL(ua_cfg.enable_unsolicited_mwi);
 }
 
@@ -205,9 +206,9 @@
     this->audioFramePtime = mc.audio_frame_ptime;
     this->maxMediaPorts = mc.max_media_ports;
-    this->hasIoqueue = mc.has_ioqueue;
+    this->hasIoqueue = PJ2BOOL(mc.has_ioqueue);
     this->threadCnt = mc.thread_cnt;
     this->quality = mc.quality;
     this->ptime = mc.ptime;
-    this->noVad = mc.no_vad;
+    this->noVad = PJ2BOOL(mc.no_vad);
     this->ilbcMode = mc.ilbc_mode;
     this->txDropPct = mc.tx_drop_pct;
@@ -222,5 +223,5 @@
     this->jbMax = mc.jb_max;
     this->sndAutoCloseTime = mc.snd_auto_close_time;
-    this->vidPreviewEnableNative = mc.vid_preview_enable_native;
+    this->vidPreviewEnableNative = PJ2BOOL(mc.vid_preview_enable_native);
 }
 
@@ -362,4 +363,5 @@
     } catch (Error &err) {
 	// Ignore
+	PJ_UNUSED_ARG(err);
     }
     delete writer;
@@ -412,4 +414,6 @@
                         pj_timer_entry *entry)
 {
+    PJ_UNUSED_ARG(timer_heap);
+
     Endpoint &ep = Endpoint::instance();
     UserTimer *ut = (UserTimer*) entry->user_data;
@@ -505,5 +509,5 @@
 
     OnRegStartedParam prm;
-    prm.renew = renew;
+    prm.renew = PJ2BOOL(renew);
     acc->onRegStarted(prm);
 }
@@ -536,4 +540,7 @@
                                      pjsua_msg_data *msg_data)
 {
+    PJ_UNUSED_ARG(buddy_id);
+    PJ_UNUSED_ARG(srv_pres);
+
     Account *acc = lookupAcc(acc_id, "on_incoming_subscribe()");
     if (!acc) {
@@ -543,8 +550,10 @@
 
     OnIncomingSubscribeParam prm;
+    prm.srvPres		= srv_pres;
     prm.fromUri 	= pj2Str(*from);
     prm.rdata.fromPj(*rdata);
     prm.code		= *code;
     prm.reason		= pj2Str(*reason);
+    prm.txOption.fromPj(*msg_data);
 
     acc->onIncomingSubscribe(prm);
@@ -553,6 +562,5 @@
     acc->tmpReason = prm.reason;
     *reason = str2Pj(acc->tmpReason);
-    // TODO:
-    //	apply msg_data
+    prm.txOption.toPj(*msg_data);
 }
 
@@ -599,4 +607,6 @@
 				 pjsua_acc_id acc_id)
 {
+    PJ_UNUSED_ARG(tdata);
+
     OnInstantMessageStatusParam prm;
     prm.userData	= user_data;
@@ -667,4 +677,15 @@
 }
 
+void Endpoint::on_buddy_state(pjsua_buddy_id buddy_id)
+{
+    Buddy *buddy = (Buddy*)pjsua_buddy_get_user_data(buddy_id);
+    if (!buddy || !buddy->isValid()) {
+	/* Ignored */
+	return;
+    }
+
+    buddy->onBuddyState();
+}
+
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -723,4 +744,5 @@
     ua_cfg.cb.on_typing2	= &Endpoint::on_typing2;
     ua_cfg.cb.on_mwi_info	= &Endpoint::on_mwi_info;
+    ua_cfg.cb.on_buddy_state	= &Endpoint::on_buddy_state;
 
     /* Init! */
Index: /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/json.cpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/json.cpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/json.cpp	(revision 4657)
@@ -124,5 +124,5 @@
 	PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
 
-    pj_ssize_t size = pj_file_size(filename.c_str());
+    pj_ssize_t size = (pj_ssize_t)pj_file_size(filename.c_str());
     pj_status_t status;
 
@@ -359,5 +359,5 @@
     json_verify(jdat, "readBool()", name, PJ_JSON_VAL_BOOL);
     jdat->childPtr = jdat->childPtr->next;
-    return jdat->childPtr->prev->value.is_true;
+    return PJ2BOOL(jdat->childPtr->prev->value.is_true);
 }
 
Index: /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/persistent.cpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/persistent.cpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/persistent.cpp	(revision 4657)
@@ -85,5 +85,5 @@
 				  int num) throw(Error)
 {
-    getRootContainer().writeNumber(name, num);
+    getRootContainer().writeNumber(name, (float)num);
 }
 
@@ -188,5 +188,5 @@
 			     int num) throw(Error)
 {
-    return op->writeNumber(this, name, num);
+    return op->writeNumber(this, name, (float)num);
 }
 
Index: /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/presence.cpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/presence.cpp	(revision 4657)
+++ /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/presence.cpp	(revision 4657)
@@ -0,0 +1,190 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <pjsua2/presence.hpp>
+#include <pjsua2/account.hpp>
+#include "util.hpp"
+
+using namespace pj;
+using namespace std;
+
+#define THIS_FILE		"presence.cpp"
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+PresenceStatus::PresenceStatus()
+: status(PJSUA_BUDDY_STATUS_UNKNOWN), activity(PJRPID_ACTIVITY_UNKNOWN)
+{
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+void BuddyConfig::readObject(const ContainerNode &node) throw(Error)
+{
+    ContainerNode this_node = node.readContainer("BuddyConfig");
+
+    NODE_READ_STRING   ( this_node, uri);
+    NODE_READ_BOOL     ( this_node, subscribe);
+}
+
+void BuddyConfig::writeObject(ContainerNode &node) const throw(Error)
+{
+    ContainerNode this_node = node.writeNewContainer("BuddyConfig");
+
+    NODE_WRITE_STRING  ( this_node, uri);
+    NODE_WRITE_BOOL    ( this_node, subscribe);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void BuddyInfo::fromPj(const pjsua_buddy_info &pbi)
+{
+    uri 		= pj2Str(pbi.uri);
+    contact 		= pj2Str(pbi.contact);
+    presMonitorEnabled 	= PJ2BOOL(pbi.monitor_pres);
+    subState 		= pbi.sub_state;
+    subStateName 	= string(pbi.sub_state_name);
+    subTermCode 	= (pjsip_status_code)pbi.sub_term_code;
+    subTermReason 	= pj2Str(pbi.sub_term_reason);
+    
+    /* Presence status */
+    presStatus.status	= pbi.status;
+    presStatus.statusText = pj2Str(pbi.status_text);
+    presStatus.activity = pbi.rpid.activity;
+    presStatus.note	= pj2Str(pbi.rpid.note);
+    presStatus.rpidId	= pj2Str(pbi.rpid.id);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Constructor.
+ */
+Buddy::Buddy()
+: id(PJSUA_INVALID_ID)
+{
+}
+ 
+/*
+ * Destructor.
+ */
+Buddy::~Buddy()
+{
+    if (isValid()) {
+	pjsua_buddy_set_user_data(id, NULL);
+	pjsua_buddy_del(id);
+
+	/* Remove from account buddy list */
+	acc->removeBuddy(this);
+    }
+}
+    
+/*
+ * Create buddy and register the buddy to PJSUA-LIB.
+ */
+void Buddy::create(Account &account, const BuddyConfig &cfg) throw(Error)
+{
+    pjsua_buddy_config pj_cfg;
+    pjsua_buddy_config_default(&pj_cfg);
+    
+    if (!account.isValid())
+	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "Buddy::create()", "Invalid account");
+    
+    pj_cfg.uri = str2Pj(cfg.uri);
+    pj_cfg.subscribe = cfg.subscribe;
+    pj_cfg.user_data = (void*)this;
+    PJSUA2_CHECK_EXPR( pjsua_buddy_add(&pj_cfg, &id) );
+    
+    acc = &account;
+    acc->addBuddy(this);
+}
+    
+/*
+ * Check if this buddy is valid.
+ */
+bool Buddy::isValid() const
+{
+    return PJ2BOOL( pjsua_buddy_is_valid(id) );
+}
+
+/*
+ * Get detailed buddy info.
+ */
+BuddyInfo Buddy::getInfo() const throw(Error)
+{
+    pjsua_buddy_info pj_bi;
+    BuddyInfo bi;
+
+    PJSUA2_CHECK_EXPR( pjsua_buddy_get_info(id, &pj_bi) );
+    bi.fromPj(pj_bi);
+    return bi;
+}
+
+/*
+ * Enable/disable buddy's presence monitoring.
+ */
+void Buddy::subscribePresence(bool subscribe) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_buddy_subscribe_pres(id, subscribe) );
+}
+
+    
+/*
+ * Update the presence information for the buddy.
+ */
+void Buddy::updatePresence(void) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_buddy_update_pres(id) );
+}
+     
+/*
+ * Send instant messaging outside dialog.
+ */
+void Buddy::sendInstantMessage(const SendInstantMessageParam &prm) throw(Error)
+{
+    BuddyInfo bi = getInfo();
+
+    pj_str_t to = str2Pj(bi.contact.empty()? bi.uri : bi.contact);
+    pj_str_t mime_type = str2Pj(prm.contentType);
+    pj_str_t content = str2Pj(prm.content);
+    void *user_data = (void*)prm.userData;
+    pjsua_msg_data msg_data;
+    prm.txOption.toPj(msg_data);
+    
+    PJSUA2_CHECK_EXPR( pjsua_im_send(acc->getId(), &to, &mime_type, &content,
+				     &msg_data, user_data) );
+}
+
+/*
+ * Send typing indication outside dialog.
+ */
+void Buddy::sendTypingIndication(const SendTypingIndicationParam &prm)
+     throw(Error)
+{
+    BuddyInfo bi = getInfo();
+
+    pj_str_t to = str2Pj(bi.contact.empty()? bi.uri : bi.contact);
+    pjsua_msg_data msg_data;
+    prm.txOption.toPj(msg_data);
+    
+    PJSUA2_CHECK_EXPR( pjsua_im_typing(acc->getId(), &to, prm.isTyping,
+				       &msg_data) );
+}
+
Index: /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/siptypes.cpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/siptypes.cpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/siptypes.cpp	(revision 4657)
@@ -46,5 +46,5 @@
     ContainerNode array_node = node.writeNewArray(array_name);
     for (unsigned i=0; i<v.size(); ++i) {
-	array_node.writeNumber("", v[i]);
+	array_node.writeNumber("", (float)v[i]);
     }
 }
@@ -190,11 +190,11 @@
     pj_assert(sizeof(prm.ciphers[0]) == sizeof(int));
     this->ciphers 	= IntVector(prm.ciphers, prm.ciphers+prm.ciphers_num);
-    this->verifyServer 	= prm.verify_server;
-    this->verifyClient 	= prm.verify_client;
-    this->requireClientCert = prm.require_client_cert;
+    this->verifyServer 	= PJ2BOOL(prm.verify_server);
+    this->verifyClient 	= PJ2BOOL(prm.verify_client);
+    this->requireClientCert = PJ2BOOL(prm.require_client_cert);
     this->msecTimeout 	= PJ_TIME_VAL_MSEC(prm.timeout);
     this->qosType 	= prm.qos_type;
     this->qosParams 	= prm.qos_params;
-    this->qosIgnoreError = prm.qos_ignore_error;
+    this->qosIgnoreError = PJ2BOOL(prm.qos_ignore_error);
 }
 
Index: /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/util.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/util.hpp	(revision 4656)
+++ /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/util.hpp	(revision 4657)
@@ -21,4 +21,6 @@
 #include <string>
 
+#define PJ2BOOL(var) ((var) != PJ_FALSE)
+
 namespace pj
 {
