Index: pjproject/trunk/pjsip-apps/src/pjsua/main.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/main.c	(revision 481)
+++ pjproject/trunk/pjsip-apps/src/pjsua/main.c	(revision 482)
@@ -28,5 +28,4 @@
 int main(int argc, char *argv[])
 {
-
     pjsua_config cfg;
 
@@ -56,5 +55,5 @@
      * messages.
      */
-    pjsip_endpt_register_module(pjsua.endpt, 
+    pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
 				&pjsua_console_app_msg_logger);
 
@@ -62,5 +61,4 @@
     /* Start pjsua! */
     if (pjsua_start() != PJ_SUCCESS) {
-
 	pjsua_destroy();
 	return 1;
@@ -79,4 +77,9 @@
     pjsua_destroy();
 
+    /* This is for internal testing, to make sure that pjsua_destroy()
+     * can be called multiple times. 
+     */
+    pjsua_destroy();
+
 
     /* Close logging: */
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 481)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 482)
@@ -69,61 +69,4 @@
 
 
-
-/** 
- * Structure to be attached to invite dialog. 
- * Given a dialog "dlg", application can retrieve this structure
- * by accessing dlg->mod_data[pjsua.mod.id].
- */
-struct pjsua_call
-{
-    unsigned		 index;	    /**< Index in pjsua array.		    */
-    pjsip_inv_session	*inv;	    /**< The invite session.		    */
-    pj_time_val		 start_time;/**< First INVITE sent/received.	    */
-    pj_time_val		 res_time;  /**< First response sent/received.	    */
-    pj_time_val		 conn_time; /**< Connected/confirmed time.	    */
-    pj_time_val		 dis_time;  /**< Disconnect time.		    */
-    int			 acc_index; /**< Account index being used.	    */
-    pjmedia_session	*session;   /**< The media session.		    */
-    unsigned		 conf_slot; /**< Slot # in conference bridge.	    */
-    pjsip_evsub		*xfer_sub;  /**< Xfer server subscription, if this
-					 call was triggered by xfer.	    */
-    pjmedia_sock_info	 skinfo;    /**< Preallocated media sockets.	    */
-    pjmedia_transport	*med_tp;    /**< Media transport.		    */
-    void		*app_data;  /**< Application data.		    */
-    pj_timer_entry	 refresh_tm;/**< Timer to send re-INVITE.	    */
-    pj_timer_entry	 hangup_tm; /**< Timer to hangup call.		    */
-};
-
-typedef struct pjsua_call pjsua_call;
-
-
-/**
- * Buddy data.
- */
-struct pjsua_buddy
-{
-    pj_str_t		 uri;	    /**< Buddy URI		        */
-    int			 acc_index; /**< Which account to use.		*/
-    pj_bool_t		 monitor;   /**< Should we monitor?		*/
-    pjsip_evsub		*sub;	    /**< Buddy presence subscription	*/
-    pjsip_pres_status	 status;    /**< Buddy presence status.		*/
-};
-
-typedef struct pjsua_buddy pjsua_buddy;
-
-
-/**
- * Server presence subscription list head.
- */
-struct pjsua_srv_pres
-{
-    PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres);
-    pjsip_evsub	    *sub;
-    char	    *remote;
-};
-
-typedef struct pjsua_srv_pres pjsua_srv_pres;
-
-
 /**
  * Account configuration.
@@ -159,33 +102,4 @@
  */
 typedef struct pjsua_acc_config pjsua_acc_config;
-
-
-/**
- * Account
- */
-struct pjsua_acc
-{
-    int		     index;	    /**< Index in accounts array.	*/
-    pj_str_t	     user_part;	    /**< User part of local URI.	*/
-    pj_str_t	     host_part;	    /**< Host part of local URI.	*/
-
-    pjsip_regc	    *regc;	    /**< Client registration session.   */
-    pj_timer_entry   reg_timer;	    /**< Registration timer.		*/
-    pj_status_t	     reg_last_err;  /**< Last registration error.	*/
-    int		     reg_last_code; /**< Last status last register.	*/
-
-    pjsip_route_hdr  route_set;	    /**< Route set.			*/
-
-    pj_bool_t	     online_status; /**< Our online status.		*/
-    pjsua_srv_pres   pres_srv_list; /**< Server subscription list.	*/
-
-    void	    *app_data;	    /**< Application data.		*/
-};
-
-
-/**
- * @see pjsua_acc
- */
-typedef struct pjsua_acc pjsua_acc;
 
 
@@ -248,4 +162,10 @@
     unsigned	stun_port2;
 
+    /** Sound player device ID (default: 0) */
+    unsigned	snd_player_id;
+
+    /** Sound capture device ID (default: 0) */
+    unsigned	snd_capture_id;
+
     /** Internal clock rate (to be applied to sound devices and conference
      *  bridge, default is 0/follows the codec, or 44100 for MacOS).
@@ -340,15 +260,36 @@
 {
     /**
-     * Notify UI when invite state has changed.
+     * Notify application when invite state has changed.
+     * Application may then query the call info to get the
+     * detail call states.
      */
     void (*on_call_state)(int call_index, pjsip_event *e);
 
     /**
-     * Notify UI when registration status has changed.
+     * Notify application on call being transfered.
+     * Application can decide to accept/reject transfer request
+     * by setting the code (default is 200). When this callback
+     * is not defined, the default behavior is to accept the
+     * transfer.
+     */
+    void (*on_call_transfered)(int call_index,
+			       const pj_str_t *dst,
+			       pjsip_status_code *code);
+
+    /**
+     * Notify application when registration status has changed.
+     * Application may then query the account info to get the
+     * registration details.
      */
     void (*on_reg_state)(int acc_index);
 
     /**
-     * Notify UI on incoming pager (i.e. MESSAGE request).
+     * Notify application when the buddy state has changed.
+     * Application may then query the buddy into to get the details.
+     */
+    void (*on_buddy_state)(int buddy_index);
+
+    /**
+     * Notify application on incoming pager (i.e. MESSAGE request).
      * Argument call_index will be -1 if MESSAGE request is not related to an 
      * existing call.
@@ -358,5 +299,5 @@
 
     /**
-     * Notify UI about typing indication.
+     * Notify application about typing indication.
      */
     void (*on_typing)(int call_index, const pj_str_t *from,
@@ -371,53 +312,77 @@
 
 
-/* PJSUA application variables. */
-struct pjsua
+/**
+ * Call info.
+ */
+struct pjsua_call_info
 {
-    /* Control: */
-    pj_caching_pool  cp;	    /**< Global pool factory.		*/
-    pjsip_endpoint  *endpt;	    /**< Global endpoint.		*/
-    pj_pool_t	    *pool;	    /**< pjsua's private pool.		*/
-    pjsip_module     mod;	    /**< pjsua's PJSIP module.		*/
-
-    
-    /* Config: */
-    pjsua_config    config;	    /**< PJSUA configs			*/
-
-    /* Application callback: */
-    pjsua_callback  cb;		    /**< Application callback.		*/
-
-    /* Media:  */
-    pjmedia_endpt   *med_endpt;	    /**< Media endpoint.		*/
-    pjmedia_conf    *mconf;	    /**< Media conference.		*/
-    unsigned	     wav_slot;	    /**< WAV player slot in bridge	*/
-    pjmedia_port    *file_port;	    /**< WAV player port.		*/
-
-
-    /* Account: */
-    pjsua_acc	     acc[PJSUA_MAX_ACC];    /** Client regs array.	*/
-
-
-    /* Threading (optional): */
-    pj_thread_t	    *threads[8];    /**< Thread instances.		*/
-    pj_bool_t	     quit_flag;	    /**< To signal thread to quit.	*/
-
-    /* Transport (UDP): */
-    pj_sock_t	     sip_sock;	    /**< SIP UDP socket.		*/
-    pj_sockaddr_in   sip_sock_name; /**< Public/STUN UDP socket addr.	*/
-
-
-    /* PJSUA Calls: */
-    int		     call_cnt;	    /**< Number of calls.		*/
-    pjsua_call	     calls[PJSUA_MAX_CALLS];	/** Calls array.	*/
-
-
-    /* SIMPLE and buddy status: */
-    int		     buddy_cnt;
-    pjsua_buddy	     buddies[PJSUA_MAX_BUDDIES];
+    unsigned		index;
+    pj_bool_t		active;
+    pjsip_role_e	role;
+    pj_str_t		local_info;
+    pj_str_t		remote_info;
+    pjsip_inv_state	state;
+    pj_str_t		state_text;
+    pj_time_val		connect_duration;
+    pj_time_val		total_duration;
+    pjsip_status_code	cause;
+    pj_str_t		cause_text;
+    pj_bool_t		has_media;
+    unsigned		conf_slot;
 };
 
-
-/** PJSUA instance. */
-extern struct pjsua pjsua;
+typedef struct pjsua_call_info pjsua_call_info;
+
+
+enum pjsua_buddy_status
+{
+    PJSUA_BUDDY_STATUS_UNKNOWN,
+    PJSUA_BUDDY_STATUS_ONLINE,
+    PJSUA_BUDDY_STATUS_OFFLINE,
+};
+
+typedef enum pjsua_buddy_status pjsua_buddy_status;
+
+
+/**
+ * Buddy info.
+ */
+struct pjsua_buddy_info
+{
+    unsigned		index;
+    pj_bool_t		is_valid;
+    pj_str_t		name;
+    pj_str_t		display_name;
+    pj_str_t		host;
+    unsigned		port;
+    pj_str_t		uri;
+    pjsua_buddy_status	status;
+    pj_str_t		status_text;
+    pj_bool_t		monitor;
+};
+
+typedef struct pjsua_buddy_info pjsua_buddy_info;
+
+
+/**
+ * Account info.
+ */
+struct pjsua_acc_info
+{
+    unsigned		index;
+    pj_str_t		acc_id;
+    pj_bool_t		has_registration;
+    int			expires;
+    pjsip_status_code	status;
+    pj_str_t		status_text;
+    pj_bool_t		online_status;
+    char		buf[PJ_ERR_MSG_SIZE];
+};
+
+typedef struct pjsua_acc_info pjsua_acc_info;
+
+
+typedef int pjsua_player_id;
+typedef int pjsua_recorder_id;
 
 
@@ -434,5 +399,5 @@
 
 /**
- * Test configuration.
+ * Validate configuration.
  */
 PJ_DECL(pj_status_t) pjsua_test_config(const pjsua_config *cfg,
@@ -442,7 +407,6 @@
 
 /**
- * Create pjsua application.
- * This initializes pjlib/pjlib-util, and creates memory pool factory to
- * be used by application.
+ * Instantiate pjsua application. This initializes pjlib/pjlib-util, and 
+ * creates memory pool factory to be used by application.
  */
 PJ_DECL(pj_status_t) pjsua_create(void);
@@ -470,9 +434,27 @@
 PJ_DECL(pj_status_t) pjsua_start(void);
 
-
 /**
  * Destroy pjsua.
  */
 PJ_DECL(pj_status_t) pjsua_destroy(void);
+
+/**
+ * Get SIP endpoint instance.
+ * Only valid after pjsua_init().
+ */
+PJ_DECL(pjsip_endpoint*) pjsua_get_pjsip_endpt(void);
+
+/**
+ * Get media endpoint instance.
+ * Only valid after pjsua_init().
+ */
+PJ_DECL(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void);
+
+/**
+ * Replace media transport.
+ */
+PJ_DECL(pj_status_t) pjsua_set_call_media_transport(unsigned call_index,
+						    const pjmedia_sock_info *i,
+						    pjmedia_transport *tp);
 
 
@@ -482,8 +464,46 @@
 
 /**
+ * Get maximum number of calls configured in pjsua.
+ */
+PJ_DECL(unsigned) pjsua_get_max_calls(void);
+
+/**
+ * Get current number of active calls.
+ */
+PJ_DECL(unsigned) pjsua_get_call_count(void);
+
+/**
+ * Check if the specified call has active INVITE session and the INVITE
+ * session has not been disconnected.
+ */
+PJ_DECL(pj_bool_t) pjsua_call_is_active(unsigned call_index);
+
+
+/**
+ * Check if call has a media session.
+ */
+PJ_DECL(pj_bool_t) pjsua_call_has_media(unsigned call_index);
+
+
+/**
+ * Get call info.
+ */
+PJ_DECL(pj_status_t) pjsua_get_call_info(unsigned call_index,
+					 pjsua_call_info *info);
+
+
+/**
+ * Duplicate call info.
+ */
+PJ_DECL(void) pjsua_dup_call_info(pj_pool_t *pool,
+				  pjsua_call_info *dst_info,
+				  const pjsua_call_info *src_info);
+
+
+/**
  * Make outgoing call.
  */
-PJ_DECL(pj_status_t) pjsua_make_call(int acc_index,
-				     const char *cstr_dest_uri,
+PJ_DECL(pj_status_t) pjsua_make_call(unsigned acc_index,
+				     const pj_str_t *dst_uri,
 				     int *p_call_index);
 
@@ -515,5 +535,11 @@
  * Transfer call.
  */
-PJ_DECL(void) pjsua_call_xfer(int call_index, const char *dest);
+PJ_DECL(void) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest);
+
+/**
+ * Dial DTMF.
+ */
+PJ_DECL(pj_status_t) pjsua_call_dial_dtmf(unsigned call_index, 
+					  const pj_str_t *digits);
 
 
@@ -521,5 +547,5 @@
  * Send instant messaging inside INVITE session.
  */
-PJ_DECL(void) pjsua_call_send_im(int call_index, const char *text);
+PJ_DECL(void) pjsua_call_send_im(int call_index, const pj_str_t *text);
 
 
@@ -536,6 +562,36 @@
 
 /*****************************************************************************
- * PJSUA Client Registration API (defined in pjsua_reg.c).
- */
+ * PJSUA Account and Client Registration API (defined in pjsua_reg.c).
+ */
+
+
+/**
+ * Get number of accounts.
+ */
+PJ_DECL(unsigned) pjsua_get_acc_count(void);
+
+/**
+ * Get account info.
+ */
+PJ_DECL(pj_status_t) pjsua_acc_get_info(unsigned acc_index,
+					pjsua_acc_info *info);
+
+/**
+ * Add a new account.
+ * This function should be called after pjsua_init().
+ * Application should call pjsua_acc_set_registration() to start 
+ * registration for this account.
+ */
+PJ_DECL(pj_status_t) pjsua_acc_add(const pjsua_acc_config *cfg,
+				   int *acc_index);
+
+
+/**
+ * Set account's presence status.
+ * Must call pjsua_pres_refresh() after this.
+ */
+PJ_DECL(pj_status_t) pjsua_acc_set_online_status(unsigned acc_index,
+						 pj_bool_t is_online);
+
 
 /**
@@ -543,5 +599,5 @@
  * this will start unregistration process.
  */
-PJ_DECL(void) pjsua_regc_update(int acc_index, pj_bool_t renew);
+PJ_DECL(void) pjsua_acc_set_registration(unsigned acc_index, pj_bool_t renew);
 
 
@@ -553,4 +609,31 @@
 
 /**
+ * Get buddy count.
+ */
+PJ_DECL(unsigned) pjsua_get_buddy_count(void);
+
+
+/**
+ * Get buddy info.
+ */
+PJ_DECL(pj_status_t) pjsua_buddy_get_info(unsigned buddy_index,
+					  pjsua_buddy_info *info);
+
+/**
+ * Add new buddy.
+ */
+PJ_DECL(pj_status_t) pjsua_buddy_add(const pj_str_t *uri,
+				     int *buddy_index);
+
+
+/**
+ * Enable/disable buddy's presence monitoring.
+ * Must call pjsua_pres_refresh() after this.
+ */
+PJ_DECL(pj_status_t) pjsua_buddy_subscribe_pres(unsigned buddy_index,
+						pj_bool_t monitor);
+
+
+/**
  * Refresh both presence client and server subscriptions.
  */
@@ -577,6 +660,6 @@
  * Send IM outside dialog.
  */
-PJ_DECL(pj_status_t) pjsua_im_send(int acc_index, const char *dst_uri, 
-				   const char *text);
+PJ_DECL(pj_status_t) pjsua_im_send(int acc_index, const pj_str_t *dst_uri, 
+				   const pj_str_t *text);
 
 
@@ -584,7 +667,100 @@
  * Send typing indication outside dialog.
  */
-PJ_DECL(pj_status_t) pjsua_im_typing(int acc_index, const char *dst_uri, 
+PJ_DECL(pj_status_t) pjsua_im_typing(int acc_index, const pj_str_t *dst_uri, 
 				     pj_bool_t is_typing);
 
+
+
+/*****************************************************************************
+ * Media.
+ */
+
+/**
+ * Get maxinum number of conference ports.
+ */
+PJ_DECL(unsigned) pjsua_conf_max_ports(void);
+
+
+/**
+ * Enum all conference ports.
+ */
+PJ_DECL(pj_status_t) pjsua_conf_enum_ports(unsigned *count,
+					   pjmedia_conf_port_info info[]);
+
+
+/**
+ * Connect conference port.
+ */
+PJ_DECL(pj_status_t) pjsua_conf_connect(unsigned src_port,
+					unsigned dst_port);
+
+
+/**
+ * Connect conference port connection.
+ */
+PJ_DECL(pj_status_t) pjsua_conf_disconnect(unsigned src_port,
+					   unsigned dst_port);
+
+
+/**
+ * Create a file player.
+ */
+PJ_DECL(pj_status_t) pjsua_player_create(const pj_str_t *filename,
+					 pjsua_player_id *id);
+
+
+/**
+ * Get conference port associated with player.
+ */
+PJ_DECL(unsigned) pjsua_player_get_conf_port(pjsua_player_id id);
+
+
+/**
+ * Set playback position.
+ */
+PJ_DECL(pj_status_t) pjsua_player_set_pos(pjsua_player_id id,
+					  pj_uint32_t samples);
+
+
+/**
+ * Destroy player.
+ */
+PJ_DECL(pj_status_t) pjsua_player_destroy(pjsua_player_id id);
+
+
+
+/**
+ * Create a file recorder.
+ */
+PJ_DECL(pj_status_t) pjsua_recorder_create(const pj_str_t *filename,
+					   pjsua_recorder_id *id);
+
+
+/**
+ * Get conference port associated with recorder.
+ */
+PJ_DECL(unsigned) pjsua_recorder_get_conf_port(pjsua_recorder_id id);
+
+
+/**
+ * Destroy recorder (will complete recording).
+ */
+PJ_DECL(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id);
+
+
+/**
+ * Enum sound devices.
+ */
+PJ_DECL(pj_status_t) pjsua_enum_snd_devices(unsigned *count,
+					    pjmedia_snd_dev_info info[]);
+
+
+/**
+ * Select or change sound device.
+ * This will only change the device ID in configuration (not changing
+ * the current device).
+ */
+PJ_DECL(pj_status_t) pjsua_set_snd_dev(int snd_capture_id,
+				       int snd_player_id);
 
 
@@ -610,5 +786,12 @@
 
 /**
+ * Get pjsua running config.
+ */
+PJ_DECL(const pjsua_config*) pjsua_get_config(void);
+
+
+/**
  * Dump settings.
+ * If cfg is NULL, it will dump current settings.
  */
 PJ_DECL(int) pjsua_dump_settings(const pjsua_config *cfg,
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 481)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 482)
@@ -126,11 +126,128 @@
 
 /**
+ * Get maximum number of calls configured in pjsua.
+ */
+PJ_DEF(unsigned) pjsua_get_max_calls(void)
+{
+    return pjsua.config.max_calls;
+}
+
+
+/**
+ * Get current number of active calls.
+ */
+PJ_DEF(unsigned) pjsua_get_call_count(void)
+{
+    return pjsua.call_cnt;
+}
+
+
+/**
+ * Check if the specified call is active.
+ */
+PJ_DEF(pj_bool_t) pjsua_call_is_active(unsigned call_index)
+{
+    PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls,
+		     PJ_EINVAL);
+    return pjsua.calls[call_index].inv != NULL &&
+	   pjsua.calls[call_index].inv->state != PJSIP_INV_STATE_DISCONNECTED;
+}
+
+/**
+ * Check if call has a media session.
+ */
+PJ_DEF(pj_bool_t) pjsua_call_has_media(unsigned call_index)
+{
+    PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, PJ_EINVAL);
+    return pjsua.calls[call_index].session != NULL;
+}
+
+
+/**
+ * Get call info.
+ */
+PJ_DEF(pj_status_t) pjsua_get_call_info( unsigned call_index,
+					 pjsua_call_info *info)
+{
+    pjsua_call *call;
+
+    PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls,
+		     PJ_EINVAL);
+
+    pj_memset(info, 0, sizeof(pjsua_call_info));
+
+    call = &pjsua.calls[call_index];
+    info->active = pjsua_call_is_active(call_index);
+
+    if (call->inv == NULL)
+	return PJ_SUCCESS;
+
+    info->index = call_index;
+    info->role = call->inv->role;
+    info->local_info = call->inv->dlg->local.info_str;
+    info->remote_info = call->inv->dlg->remote.info_str;
+    info->state = call->inv->state;
+    info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
+    
+    if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
+
+	info->total_duration = call->dis_time;
+	PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
+
+	if (call->conn_time.sec) {
+	    info->connect_duration = call->dis_time;
+	    PJ_TIME_VAL_SUB(info->total_duration, call->conn_time);
+	}
+
+    } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
+
+	pj_gettimeofday(&info->total_duration);
+	PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
+
+	pj_gettimeofday(&info->connect_duration);
+	PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
+
+    } else {
+	pj_gettimeofday(&info->total_duration);
+	PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
+    }
+
+    info->cause = call->inv->cause;
+    info->cause_text = *pjsip_get_status_text(info->cause);
+
+    info->has_media = (call->session != NULL);
+    info->conf_slot = call->conf_slot;
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Duplicate call info.
+ */
+PJ_DEF(void) pjsua_dup_call_info( pj_pool_t *pool,
+				  pjsua_call_info *dst_info,
+				  const pjsua_call_info *src_info)
+{
+    PJ_ASSERT_ON_FAIL(pool && dst_info && src_info, return);
+
+    pj_memcpy(dst_info, src_info, sizeof(pjsua_call_info));
+
+    pj_strdup(pool, &dst_info->local_info, &src_info->local_info);
+    pj_strdup(pool, &dst_info->remote_info, &src_info->remote_info);
+    
+    /* state_text and cause_text belong to pjsip, so don't need to be
+     * duplicated because they'll always be available.
+     */
+}
+
+
+/**
  * Make outgoing call.
  */
-PJ_DEF(pj_status_t) pjsua_make_call(int acc_index,
-				    const char *cstr_dest_uri,
+PJ_DEF(pj_status_t) pjsua_make_call(unsigned acc_index,
+				    const pj_str_t *dest_uri,
 				    int *p_call_index)
 {
-    pj_str_t dest_uri;
     pjsip_dialog *dlg = NULL;
     pjmedia_sdp_session *offer;
@@ -140,7 +257,8 @@
     pj_status_t status;
 
-    /* Convert cstr_dest_uri to dest_uri */
-    
-    dest_uri = pj_str((char*)cstr_dest_uri);
+
+    PJ_ASSERT_RETURN(acc_index==0 || acc_index < pjsua.config.acc_cnt, 
+		     PJ_EINVAL);
+
 
     /* Find free call slot. */
@@ -165,5 +283,5 @@
 				   &pjsua.config.acc_config[acc_index].id,
 				   &pjsua.config.acc_config[acc_index].contact,
-				   &dest_uri, &dest_uri,
+				   dest_uri, dest_uri,
 				   &dlg);
     if (status != PJ_SUCCESS) {
@@ -656,5 +774,7 @@
     pjsip_generic_string_hdr *refer_to;
     char *uri;
+    pj_str_t tmp;
     struct pjsip_evsub_user xfer_cb;
+    pjsip_status_code code;
     pjsip_evsub *sub;
 
@@ -674,4 +794,18 @@
     }
 
+    /* Notify callback */
+    code = PJSIP_SC_OK;
+    if (pjsua.cb.on_call_transfered)
+	(*pjsua.cb.on_call_transfered)(existing_call->index,
+				       &refer_to->hvalue, &code);
+
+    if (code < 200)
+	code = 200;
+    if (code >= 300) {
+	/* Application rejects call transfer request */
+	pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
+	return;
+    }
+
     PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
 	      (int)inv->dlg->remote.info_str.slen,
@@ -693,5 +827,5 @@
 
     /* Accept the REFER request, send 200 (OK). */
-    pjsip_xfer_accept(sub, rdata, 200, NULL);
+    pjsip_xfer_accept(sub, rdata, code, NULL);
 
     /* Create initial NOTIFY request */
@@ -719,5 +853,6 @@
 
     /* Now make the outgoing call. */
-    status = pjsua_make_call(existing_call->acc_index, uri, &new_call);
+    tmp = pj_str(uri);
+    status = pjsua_make_call(existing_call->acc_index, &tmp, &new_call);
     if (status != PJ_SUCCESS) {
 
@@ -1084,5 +1219,5 @@
     {
 
-	pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot, 
+	pjmedia_conf_connect_port( pjsua.mconf, pjsua.player[0].slot, 
 				   call->conf_slot, 0);
 
@@ -1308,10 +1443,9 @@
  * Transfer call.
  */
-PJ_DEF(void) pjsua_call_xfer(int call_index, const char *dest)
+PJ_DEF(void) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest)
 {
     pjsip_evsub *sub;
     pjsip_tx_data *tdata;
     pjsua_call *call;
-    pj_str_t tmp;
     pj_status_t status;
 
@@ -1337,5 +1471,5 @@
      * Create REFER request.
      */
-    status = pjsip_xfer_initiate(sub, pj_cstr(&tmp, dest), &tdata);
+    status = pjsip_xfer_initiate(sub, dest, &tdata);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
@@ -1358,12 +1492,30 @@
 
 /**
+ * Dial DTMF.
+ */
+PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( unsigned call_index, 
+					  const pj_str_t *digits)
+{
+    pjsua_call *call = &pjsua.calls[call_index];
+
+    PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, PJ_EINVAL);
+
+    if (!call->session) {
+	PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
+	return -1;
+    }
+
+    return pjmedia_session_dial_dtmf( call->session, 0, digits);
+}
+
+
+/**
  * Send instant messaging inside INVITE session.
  */
-PJ_DECL(void) pjsua_call_send_im(int call_index, const char *str)
+PJ_DECL(void) pjsua_call_send_im(int call_index, const pj_str_t *str)
 {
     pjsua_call *call;
     const pj_str_t mime_text = pj_str("text");
     const pj_str_t mime_plain = pj_str("plain");
-    pj_str_t text;
     pjsip_tx_data *tdata;
     pj_status_t status;
@@ -1393,6 +1545,5 @@
     /* Create "text/plain" message body. */
     tdata->msg->body = pjsip_msg_body_create( tdata->pool, &mime_text,
-					      &mime_plain, 
-					      pj_cstr(&text, str));
+					      &mime_plain, str);
     if (tdata->msg->body == NULL) {
 	pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
@@ -1505,2 +1656,21 @@
     return status;
 }
+
+/**
+ * Replace media transport.
+ */
+PJ_DEF(pj_status_t) pjsua_set_call_media_transport( unsigned call_index,
+						    const pjmedia_sock_info *i,
+						    pjmedia_transport *tp)
+{
+    pjsua_call *call = &pjsua.calls[call_index];
+
+    if (i)
+	pj_memcpy(&call->skinfo, i, sizeof(pjmedia_sock_info));
+    
+    if (call->med_tp)
+	(*call->med_tp->op->destroy)(call->med_tp);
+
+    call->med_tp = tp;
+    return PJ_SUCCESS;
+}
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_console_app.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_console_app.c	(revision 481)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_console_app.c	(revision 482)
@@ -35,8 +35,9 @@
 static pj_bool_t find_next_call(void)
 {
-    int i;
-
-    for (i=current_call+1; i<(int)pjsua.config.max_calls; ++i) {
-	if (pjsua.calls[i].inv != NULL) {
+    int i, max;
+
+    max = pjsua_get_max_calls();
+    for (i=current_call+1; i<max; ++i) {
+	if (pjsua_call_is_active(i)) {
 	    current_call = i;
 	    return PJ_TRUE;
@@ -45,5 +46,5 @@
 
     for (i=0; i<current_call; ++i) {
-	if (pjsua.calls[i].inv != NULL) {
+	if (pjsua_call_is_active(i)) {
 	    current_call = i;
 	    return PJ_TRUE;
@@ -61,8 +62,9 @@
 static pj_bool_t find_prev_call(void)
 {
-    int i;
-
+    int i, max;
+
+    max = pjsua_get_max_calls();
     for (i=current_call-1; i>=0; --i) {
-	if (pjsua.calls[i].inv != NULL) {
+	if (pjsua_call_is_active(i)) {
 	    current_call = i;
 	    return PJ_TRUE;
@@ -70,6 +72,6 @@
     }
 
-    for (i=pjsua.config.max_calls-1; i>current_call; --i) {
-	if (pjsua.calls[i].inv != NULL) {
+    for (i=max-1; i>current_call; --i) {
+	if (pjsua_call_is_active(i)) {
 	    current_call = i;
 	    return PJ_TRUE;
@@ -88,17 +90,18 @@
 static void console_on_call_state(int call_index, pjsip_event *e)
 {
-    pjsua_call *call = &pjsua.calls[call_index];
+    pjsua_call_info call_info;
 
     PJ_UNUSED_ARG(e);
 
-    if (call->inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+    pjsua_get_call_info(call_index, &call_info);
+
+    if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
 
 	PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]", 
 		  call_index,
-		  call->inv->cause,
-		  pjsip_get_status_text(call->inv->cause)->ptr));
-
-	call->inv = NULL;
-	if ((int)call->index == current_call) {
+		  call_info.cause,
+		  call_info.cause_text.ptr));
+
+	if ((int)call_index == current_call) {
 	    find_next_call();
 	}
@@ -108,8 +111,8 @@
 	PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s", 
 		  call_index,
-		  pjsua_inv_state_names[call->inv->state]));
-
-	if (call && current_call==-1)
-	    current_call = call->index;
+		  call_info.state_text.ptr));
+
+	if (current_call==-1)
+	    current_call = call_index;
 
     }
@@ -124,4 +127,20 @@
 
     // Log already written.
+}
+
+
+/**
+ * Notify UI on buddy state changed.
+ */
+static void console_on_buddy_state(int buddy_index)
+{
+    pjsua_buddy_info info;
+    pjsua_buddy_get_info(buddy_index, &info);
+
+    PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s",
+	      (int)info.uri.slen,
+	      info.uri.ptr,
+	      (int)info.status_text.slen,
+	      info.status_text.ptr));
 }
 
@@ -163,26 +182,26 @@
 static void print_buddy_list(void)
 {
-    int i;
+    int i, count;
 
     puts("Buddy list:");
 
-    if (pjsua.buddy_cnt == 0)
+    count = pjsua_get_buddy_count();
+
+    if (count == 0)
 	puts(" -none-");
     else {
-	for (i=0; i<pjsua.buddy_cnt; ++i) {
-	    const char *status;
-
-	    if (pjsua.buddies[i].sub == NULL || 
-		pjsua.buddies[i].status.info_cnt==0)
-	    {
-		status = "   ?   ";
-	    } 
-	    else if (pjsua.buddies[i].status.info[0].basic_open)
-		status = " Online";
-	    else
-		status = "Offline";
-
-	    printf(" [%2d] <%s>  %s\n", 
-		    i+1, status, pjsua.buddies[i].uri.ptr);
+	for (i=0; i<count; ++i) {
+	    pjsua_buddy_info info;
+
+	    if (pjsua_buddy_get_info(i, &info) != PJ_SUCCESS)
+		continue;
+
+	    if (!info.is_valid)
+		continue;
+
+	    printf(" [%2d] <%7s>  %.*s\n", 
+		    i+1, info.status_text.ptr, 
+		    (int)info.uri.slen,
+		    info.uri.ptr);
 	}
     }
@@ -196,36 +215,26 @@
 static void print_acc_status(int acc_index)
 {
-    char reg_status[128];
-
-    if (pjsua.acc[acc_index].regc == NULL) {
-	pj_ansi_strcpy(reg_status, " -not registered to server-");
-
-    } else if (pjsua.acc[acc_index].reg_last_err != PJ_SUCCESS) {
-	pj_strerror(pjsua.acc[acc_index].reg_last_err, reg_status, sizeof(reg_status));
-
-    } else if (pjsua.acc[acc_index].reg_last_code>=200 && 
-	       pjsua.acc[acc_index].reg_last_code<=699) {
-
-	pjsip_regc_info info;
-	const pj_str_t *status_str;
-
-	pjsip_regc_get_info(pjsua.acc[acc_index].regc, &info);
-
-	status_str = pjsip_get_status_text(pjsua.acc[acc_index].reg_last_code);
-	pj_ansi_snprintf(reg_status, sizeof(reg_status),
-			 "%s (%.*s;expires=%d)",
-			 status_str->ptr,
-			 (int)info.client_uri.slen,
-			 info.client_uri.ptr,
-			 info.next_reg);
+    char buf[80];
+    pjsua_acc_info info;
+
+    pjsua_acc_get_info(acc_index, &info);
+
+    if (!info.has_registration) {
+	pj_ansi_strcpy(buf, " -not registered to server-");
 
     } else {
-	pj_ansi_sprintf(reg_status, "in progress (%d)", 
-		        pjsua.acc[acc_index].reg_last_code);
-    }
-
-    printf("[%2d] Registration status: %s\n", acc_index, reg_status);
+	pj_ansi_snprintf(buf, sizeof(buf),
+			 "%.*s (%.*s;expires=%d)",
+			 (int)info.status_text.slen,
+			 info.status_text.ptr,
+			 (int)info.acc_id.slen,
+			 info.acc_id.ptr,
+			 info.expires);
+
+    }
+
+    printf("[%2d] Registration status: %s\n", acc_index, buf);
     printf("     Online status: %s\n", 
-	   (pjsua.acc[acc_index].online_status ? "Online" : "Invisible"));
+	   (info.online_status ? "Online" : "Invisible"));
 }
 
@@ -239,5 +248,5 @@
     printf(">>>>\n");
 
-    for (i=0; i<(int)pjsua.config.acc_cnt; ++i)
+    for (i=0; i<(int)pjsua_get_acc_count(); ++i)
 	print_acc_status(i);
 
@@ -312,5 +321,5 @@
 	   "  URL        An URL\n"
 	   "  <Enter>    Empty input (or 'q') to cancel\n"
-	   , pjsua.buddy_cnt, pjsua.buddy_cnt);
+	   , pjsua_get_buddy_count(), pjsua_get_buddy_count());
     printf("%s: ", title);
 
@@ -350,5 +359,7 @@
 	result->nb_result = atoi(buf);
 
-	if (result->nb_result >= 0 && result->nb_result <= (int)pjsua.buddy_cnt) {
+	if (result->nb_result >= 0 && 
+	    result->nb_result <= (int)pjsua_get_buddy_count()) 
+	{
 	    return;
 	}
@@ -380,5 +391,5 @@
 
     count = PJ_ARRAY_SIZE(info);
-    pjmedia_conf_get_ports_info(pjsua.mconf, &count, info);
+    pjsua_conf_enum_ports(&count, info);
     for (i=0; i<count; ++i) {
 	char txlist[PJSUA_MAX_CALLS*4+10];
@@ -414,10 +425,14 @@
     int i, count;
     char *uri;
+    pj_str_t tmp;
     struct input_result result;
+    pjsua_call_info call_info;
+    pjsua_acc_info acc_info;
 
 
     /* If user specifies URI to call, then call the URI */
-    if (pjsua.config.uri_to_call.slen) {
-	pjsua_make_call( current_acc, pjsua.config.uri_to_call.ptr, NULL);
+    if (pjsua_get_config()->uri_to_call.slen) {
+	pjsua_make_call( current_acc, &pjsua_get_config()->uri_to_call, 
+			 NULL);
     }
 
@@ -435,5 +450,5 @@
 	case 'm':
 	    /* Make call! : */
-	    printf("(You currently have %d calls)\n", pjsua.call_cnt);
+	    printf("(You currently have %d calls)\n", pjsua_get_call_count());
 	    
 	    uri = NULL;
@@ -445,5 +460,7 @@
 		    continue;
 		} else {
-		    uri = pjsua.buddies[result.nb_result-1].uri.ptr;
+		    pjsua_buddy_info binfo;
+		    pjsua_buddy_get_info(result.nb_result-1, &binfo);
+		    uri = binfo.uri.ptr;
 		}
 
@@ -452,10 +469,11 @@
 	    }
 	    
-	    pjsua_make_call( current_acc, uri, NULL);
+	    tmp = pj_str(uri);
+	    pjsua_make_call( current_acc, &tmp, NULL);
 	    break;
 
 	case 'M':
 	    /* Make multiple calls! : */
-	    printf("(You currently have %d calls)\n", pjsua.call_cnt);
+	    printf("(You currently have %d calls)\n", pjsua_get_call_count());
 	    
 	    if (!simple_input("Number of calls", menuin, sizeof(menuin)))
@@ -468,9 +486,11 @@
 	    ui_input_url("Make call", buf, sizeof(buf), &result);
 	    if (result.nb_result != NO_NB) {
+		pjsua_buddy_info binfo;
 		if (result.nb_result == -1 || result.nb_result == 0) {
 		    puts("You can't do that with make call!");
 		    continue;
 		}
-		uri = pjsua.buddies[result.nb_result-1].uri.ptr;
+		pjsua_buddy_get_info(result.nb_result-1, &binfo);
+		uri = binfo.uri.ptr;
 	    } else {
 		uri =  result.uri_result;
@@ -479,6 +499,7 @@
 	    for (i=0; i<atoi(menuin); ++i) {
 		pj_status_t status;
-
-		status = pjsua_make_call(current_acc, uri, NULL);
+	    
+		tmp = pj_str(uri);
+		status = pjsua_make_call(current_acc, &tmp, NULL);
 		if (status != PJ_SUCCESS)
 		    break;
@@ -508,5 +529,7 @@
 
 		} else {
-		    uri = pjsua.buddies[result.nb_result-1].uri.ptr;
+		    pjsua_buddy_info binfo;
+		    pjsua_buddy_get_info(result.nb_result-1, &binfo);
+		    uri = binfo.uri.ptr;
 		}
 
@@ -519,6 +542,8 @@
 	    if (i != -1)
 		pjsua_call_typing(i, PJ_TRUE);
-	    else
-		pjsua_im_typing(current_acc, uri, PJ_TRUE);
+	    else {
+		pj_str_t tmp_uri = pj_str(uri);
+		pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE);
+	    }
 
 	    /* Input the IM . */
@@ -530,14 +555,20 @@
 		if (i != -1)
 		    pjsua_call_typing(i, PJ_FALSE);
-		else
-		    pjsua_im_typing(current_acc, uri, PJ_FALSE);
+		else {
+		    pj_str_t tmp_uri = pj_str(uri);
+		    pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE);
+		}
 		continue;
 	    }
+
+	    tmp = pj_str(text);
 
 	    /* Send the IM */
 	    if (i != -1)
-		pjsua_call_send_im(i, text);
-	    else
-		pjsua_im_send(current_acc, uri, text);
+		pjsua_call_send_im(i, &tmp);
+	    else {
+		pj_str_t tmp_uri = pj_str(uri);
+		pjsua_im_send(current_acc, &tmp_uri, &tmp);
+	    }
 
 	    break;
@@ -545,7 +576,17 @@
 	case 'a':
 
+	    if (current_call != -1) {
+		pjsua_get_call_info(current_call, &call_info);
+	    } else {
+		/* Make compiler happy */
+		call_info.active = 0;
+		call_info.role = PJSIP_ROLE_UAC;
+		call_info.state = PJSIP_INV_STATE_DISCONNECTED;
+	    }
+
 	    if (current_call == -1 || 
-		pjsua.calls[current_call].inv->role != PJSIP_ROLE_UAS ||
-		pjsua.calls[current_call].inv->state >= PJSIP_INV_STATE_CONNECTING)
+		call_info.active==0 ||
+		call_info.role != PJSIP_ROLE_UAS ||
+		call_info.state >= PJSIP_INV_STATE_CONNECTING)
 	    {
 		puts("No pending incoming call");
@@ -609,17 +650,9 @@
 
 	    if (current_call != -1) {
-		char url[PJSIP_MAX_URL_SIZE];
-		int len;
-		const pjsip_uri *u;
-
-		u = pjsua.calls[current_call].inv->dlg->remote.info->uri;
-		len = pjsip_uri_print(0, u, url, sizeof(url)-1);
-		if (len < 1) {
-		    pj_ansi_strcpy(url, "<uri is too long>");
-		} else {
-		    url[len] = '\0';
-		}
-
-		PJ_LOG(3,(THIS_FILE,"Current dialog: %s", url));
+		
+		pjsua_get_call_info(current_call, &call_info);
+		PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s", 
+			  (int)call_info.remote_info.slen, 
+			  call_info.remote_info.ptr));
 
 	    } else {
@@ -677,10 +710,15 @@
 		    if (result.nb_result == -1 || result.nb_result == 0)
 			puts("You can't do that with transfer call!");
-		    else
+		    else {
+			pjsua_buddy_info binfo;
+			pjsua_buddy_get_info(result.nb_result-1, &binfo);
 			pjsua_call_xfer( current_call,
-					 pjsua.buddies[result.nb_result-1].uri.ptr);
+					 &binfo.uri);
+		    }
 
 		} else if (result.uri_result) {
-		    pjsua_call_xfer( current_call, result.uri_result);
+		    pj_str_t tmp;
+		    tmp = pj_str(result.uri_result);
+		    pjsua_call_xfer( current_call, &tmp);
 		}
 	    }
@@ -695,5 +733,5 @@
 		PJ_LOG(3,(THIS_FILE, "No current call"));
 
-	    } else if (pjsua.calls[current_call].session == NULL) {
+	    } else if (!pjsua_call_has_media(current_call)) {
 
 		PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
@@ -716,6 +754,5 @@
 
 		digits = pj_str(buf);
-		status = pjmedia_session_dial_dtmf(pjsua.calls[current_call].session, 0, 
-						   &digits);
+		status = pjsua_call_dial_dtmf(current_call, &digits);
 		if (status != PJ_SUCCESS) {
 		    pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
@@ -734,12 +771,13 @@
 	    if (result.nb_result != NO_NB) {
 		if (result.nb_result == -1) {
-		    int i;
-		    for (i=0; i<pjsua.buddy_cnt; ++i)
-			pjsua.buddies[i].monitor = (menuin[0]=='s');
+		    int i, count;
+		    count = pjsua_get_buddy_count();
+		    for (i=0; i<count; ++i)
+			pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
 		} else if (result.nb_result == 0) {
 		    puts("Sorry, can only subscribe to buddy's presence, "
 			 "not from existing call");
 		} else {
-		    pjsua.buddies[result.nb_result-1].monitor = (menuin[0]=='s');
+		    pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
 		}
 
@@ -759,5 +797,5 @@
 		 * Re-Register.
 		 */
-		pjsua_regc_update(current_acc, PJ_TRUE);
+		pjsua_acc_set_registration(current_acc, PJ_TRUE);
 		break;
 	    case 'u':
@@ -765,5 +803,5 @@
 		 * Unregister
 		 */
-		pjsua_regc_update(current_acc, PJ_FALSE);
+		pjsua_acc_set_registration(current_acc, PJ_FALSE);
 		break;
 	    }
@@ -771,9 +809,10 @@
 	    
 	case 't':
-	    pjsua.acc[current_acc].online_status = 
-		!pjsua.acc[current_acc].online_status;
+	    pjsua_acc_get_info(current_acc, &acc_info);
+	    acc_info.online_status = !acc_info.online_status;
+	    pjsua_acc_set_online_status(current_acc, acc_info.online_status);
 	    printf("Setting %s online status to %s\n",
-		   pjsua.config.acc_config[current_acc].id.ptr,
-		   (pjsua.acc[current_acc].online_status?"online":"offline"));
+		   acc_info.acc_id.ptr,
+		   (acc_info.online_status?"online":"offline"));
 	    pjsua_pres_refresh(current_acc);
 	    break;
@@ -807,12 +846,9 @@
 
 		    if (menuin[1]=='c') {
-			status = pjmedia_conf_connect_port(pjsua.mconf, 
-							   atoi(src_port), 
-							   atoi(dst_port),
-							   0);
+			status = pjsua_conf_connect(atoi(src_port), 
+						    atoi(dst_port));
 		    } else {
-			status = pjmedia_conf_disconnect_port(pjsua.mconf, 
-							      atoi(src_port), 
-							      atoi(dst_port));
+			status = pjsua_conf_disconnect(atoi(src_port), 
+						       atoi(dst_port));
 		    }
 		    if (status == PJ_SUCCESS) {
@@ -831,5 +867,5 @@
 		int len;
 
-		len = pjsua_dump_settings(&pjsua.config, settings, 
+		len = pjsua_dump_settings(NULL, settings, 
 					  sizeof(settings));
 		if (len < 1)
@@ -947,5 +983,5 @@
     /* Write to both stdout and file. */
 
-    if (level <= (int)pjsua.config.app_log_level)
+    if (level <= (int)pjsua_get_config()->app_log_level)
 	pj_log_write(level, buffer, len);
 
@@ -1010,5 +1046,7 @@
 {
     &console_on_call_state,
+    NULL,   /* default accept transfer */
     &console_on_reg_state,
+    &console_on_buddy_state,
     &console_on_pager,
     &console_on_typing,
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 481)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 482)
@@ -85,4 +85,5 @@
 	cfg->acc_config[i].reg_timeout = 55;
     }
+
 }
 
@@ -526,6 +527,4 @@
     int i;
     unsigned options;
-    unsigned clock_rate;
-    unsigned samples_per_frame;
     pj_str_t codec_id;
     pj_status_t status;
@@ -604,5 +603,5 @@
 
     /* Init options for conference bridge. */
-    options = 0;
+    options = PJMEDIA_CONF_NO_DEVICE;
 
     /* Calculate maximum number of ports, if it's not specified */
@@ -612,11 +611,11 @@
 
     /* Init conference bridge. */
-    clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000;
-    samples_per_frame = clock_rate * 10 / 1000;
+    pjsua.clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000;
+    pjsua.samples_per_frame = pjsua.clock_rate * 10 / 1000;
     status = pjmedia_conf_create(pjsua.pool, 
 				 pjsua.config.conf_ports, 
-				 clock_rate, 
+				 pjsua.clock_rate, 
 				 1, /* mono */
-				 samples_per_frame, 
+				 pjsua.samples_per_frame, 
 				 16, 
 				 options,
@@ -629,37 +628,90 @@
     }
 
+    if (pjsua.config.null_audio == PJ_FALSE) {
+	pjmedia_port *conf_port;
+
+	/* Create sound device port */
+	status = pjmedia_snd_port_create(pjsua.pool, 
+					 pjsua.config.snd_capture_id,
+					 pjsua.config.snd_player_id, 
+					 pjsua.clock_rate, 1 /* mono */,
+					 pjsua.samples_per_frame, 16,
+					 0, &pjsua.snd_port);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to create sound device", status);
+	    return status;
+	}
+
+	/* Get the port interface of the conference bridge */
+	conf_port = pjmedia_conf_get_master_port(pjsua.mconf);
+
+	/* Connect conference port interface to sound port */
+	pjmedia_snd_port_connect( pjsua.snd_port, conf_port);
+
+    } else {
+	pjmedia_port *null_port, *conf_port;
+
+	/* Create NULL port */
+	status = pjmedia_null_port_create(pjsua.pool, pjsua.clock_rate,
+					  1, pjsua.samples_per_frame, 16,
+					  &null_port);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to create NULL port", status);
+	    return status;
+	}
+
+	/* Get the port interface of the conference bridge */
+	conf_port = pjmedia_conf_get_master_port(pjsua.mconf);
+
+	/* Create master port to control conference bridge's clock */
+	status = pjmedia_master_port_create(pjsua.pool, null_port, conf_port,
+					    0, &pjsua.master_port);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to create master port", status);
+	    return status;
+	}
+    }
+
     /* Create WAV file player if required: */
 
     if (pjsua.config.wav_file.slen) {
-	pj_str_t port_name;
-
-	/* Create the file player port. */
-	status = pjmedia_wav_player_port_create(  pjsua.pool, 
-						  pjsua.config.wav_file.ptr,
-						  0, 0, -1, NULL, 
-						  &pjsua.file_port);
+
+	status = pjsua_player_create(&pjsua.config.wav_file, NULL);
 	if (status != PJ_SUCCESS) {
-	    pjsua_perror(THIS_FILE, 
-			 "Error playing media file", 
+	    pjsua_perror(THIS_FILE, "Unable to create file player", 
 			 status);
 	    return status;
 	}
-
-	/* Add port to conference bridge: */
-	port_name = pjsua.config.wav_file;
-	status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool, 
-				       pjsua.file_port, 
-				       &port_name,
-				       &pjsua.wav_slot);
-	if (status != PJ_SUCCESS) {
-	    pjsua_perror(THIS_FILE, 
-			 "Unable to add file player to conference bridge", 
-			 status);
-	    return status;
-	}
     }
 
 
     return PJ_SUCCESS;
+}
+
+
+/*
+ * Copy account configuration.
+ */
+static void copy_acc_config(pj_pool_t *pool,
+			    pjsua_acc_config *dst_acc,
+			    const pjsua_acc_config *src_acc)
+{
+    unsigned j;
+
+    pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id);
+    pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri);
+    pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact);
+    pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy);
+
+    for (j=0; j<src_acc->cred_count; ++j) {
+	pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm, 
+			    &src_acc->cred_info[j].realm);
+	pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme, 
+			    &src_acc->cred_info[j].scheme);
+	pj_strdup_with_null(pool, &dst_acc->cred_info[j].username, 
+			    &src_acc->cred_info[j].username);
+	pj_strdup_with_null(pool, &dst_acc->cred_info[j].data, 
+			    &src_acc->cred_info[j].data);
+    }
 }
 
@@ -692,21 +744,5 @@
 	pjsua_acc_config *dst_acc = &dst->acc_config[i];
 	const pjsua_acc_config *src_acc = &src->acc_config[i];
-	unsigned j;
-
-	pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id);
-	pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri);
-	pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact);
-	pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy);
-
-	for (j=0; j<src_acc->cred_count; ++j) {
-	    pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm, 
-				&src_acc->cred_info[j].realm);
-	    pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme, 
-				&src_acc->cred_info[j].scheme);
-	    pj_strdup_with_null(pool, &dst_acc->cred_info[j].username, 
-				&src_acc->cred_info[j].username);
-	    pj_strdup_with_null(pool, &dst_acc->cred_info[j].data, 
-				&src_acc->cred_info[j].data);
-	}
+	copy_acc_config(pool, dst_acc, src_acc);
     }
 
@@ -748,4 +784,9 @@
     }
 
+    /* Init buddies array */
+    for (i=0; i<PJ_ARRAY_SIZE(pjsua.buddies); ++i) {
+	pjsua.buddies[i].index = i;
+    }
+
     /* Copy configuration */
     copy_config(pjsua.pool, &pjsua.config, cfg);
@@ -757,5 +798,6 @@
     if (pjsua_test_config(&pjsua.config, errmsg, sizeof(errmsg))) {
 	PJ_LOG(1,(THIS_FILE, "Error in configuration: %s", errmsg));
-	return -1;
+	status = -1;
+	goto on_error;
     }
 
@@ -774,5 +816,5 @@
 				      &pjsua.sip_sock_name);
 	if (status != PJ_SUCCESS)
-	    return status;
+	    goto on_error;
     
 	pj_strdup2_with_null(pjsua.pool, &pjsua.config.sip_host,
@@ -786,5 +828,6 @@
 	    PJ_LOG(1,(THIS_FILE, 
 		      "Error: sip_host and sip_port must be specified"));
-	    return PJ_EINVAL;
+	    status = PJ_EINVAL;
+	    goto on_error;
 	}
 
@@ -796,5 +839,5 @@
     status = init_media();
     if (status != PJ_SUCCESS)
-	return status;
+	goto on_error;
 
 
@@ -805,7 +848,8 @@
 	    unsigned j;
 	    for (j=0; j<i; ++j) {
-		pjmedia_transport_udp_close(pjsua.calls[j].med_tp);
+		if (pjsua.calls[i].med_tp)
+		    pjsua.calls[i].med_tp->op->destroy(pjsua.calls[i].med_tp);
 	    }
-	    return status;
+	    goto on_error;
 	}
 	status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL,
@@ -897,5 +941,5 @@
 
 on_error:
-    pj_caching_pool_destroy(&pjsua.cp);
+    pjsua_destroy();
     return status;
 }
@@ -964,4 +1008,142 @@
 
 
+/*
+ * Init account
+ */
+static pj_status_t init_acc(unsigned acc_index)
+{
+    pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index];
+    pjsua_acc *acc = &pjsua.acc[acc_index];
+    pjsip_uri *uri;
+    pjsip_sip_uri *sip_uri;
+
+    /* Need to parse local_uri to get the elements: */
+
+    uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr,
+			  acc_cfg->id.slen, 0);
+    if (uri == NULL) {
+	pjsua_perror(THIS_FILE, "Invalid local URI", 
+		     PJSIP_EINVALIDURI);
+	return PJSIP_EINVALIDURI;
+    }
+
+    /* Local URI MUST be a SIP or SIPS: */
+
+    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && 
+	!PJSIP_URI_SCHEME_IS_SIPS(uri)) 
+    {
+	pjsua_perror(THIS_FILE, "Invalid local URI", 
+		     PJSIP_EINVALIDSCHEME);
+	return PJSIP_EINVALIDSCHEME;
+    }
+
+
+    /* Get the SIP URI object: */
+
+    sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
+
+    acc->user_part = sip_uri->user;
+    acc->host_part = sip_uri->host;
+
+    /* Build Contact header */
+
+    if (acc_cfg->contact.slen == 0)  {
+	char contact[128];
+	const char *addr;
+	int port;
+	int len;
+
+	addr = pjsua.config.sip_host.ptr;
+	port = pjsua.config.sip_port;
+
+	/* The local Contact is the username@ip-addr, where
+	 *  - username is taken from the local URI,
+	 *  - ip-addr in UDP transport's address name (which may have been
+	 *    resolved from STUN.
+	 */
+	
+	/* Build temporary contact string. */
+
+	if (sip_uri->user.slen) {
+
+	    /* With the user part. */
+	    len = pj_ansi_snprintf(contact, sizeof(contact),
+			      "<sip:%.*s@%s:%d>",
+			      (int)sip_uri->user.slen,
+			      sip_uri->user.ptr,
+			      addr, port);
+	} else {
+
+	    /* Without user part */
+
+	    len = pj_ansi_snprintf(contact, sizeof(contact),
+			      "<sip:%s:%d>",
+			      addr, port);
+	}
+
+	if (len < 1 || len >= sizeof(contact)) {
+	    pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG);
+	    return PJSIP_EURITOOLONG;
+	}
+
+	/* Duplicate Contact uri. */
+
+	pj_strdup2(pjsua.pool, &acc_cfg->contact, contact);
+
+    }
+
+
+    /* Build route-set for this account */
+    if (pjsua.config.outbound_proxy.slen) {
+	pj_str_t hname = { "Route", 5};
+	pjsip_route_hdr *r;
+	pj_str_t tmp;
+
+	pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy);
+	r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
+	pj_list_push_back(&acc->route_set, r);
+    }
+
+    if (acc_cfg->proxy.slen) {
+	pj_str_t hname = { "Route", 5};
+	pjsip_route_hdr *r;
+	pj_str_t tmp;
+
+	pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy);
+	r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
+	pj_list_push_back(&acc->route_set, r);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Add a new account.
+ */
+PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
+				   int *acc_index)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pjsua.config.acc_cnt<PJ_ARRAY_SIZE(pjsua.config.acc_config),
+		     PJ_ETOOMANY);
+
+    copy_acc_config(pjsua.pool, &pjsua.config.acc_config[pjsua.config.acc_cnt], cfg);
+    
+    status = init_acc(pjsua.config.acc_cnt);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error adding account", status);
+	return status;
+    }
+
+    if (acc_index)
+	*acc_index = pjsua.config.acc_cnt;
+
+    pjsua.config.acc_cnt++;
+
+    return PJ_SUCCESS;
+}
+
+
 
 /*
@@ -972,4 +1154,5 @@
 {
     int i;  /* Must be signed */
+    unsigned count;
     pj_status_t status = PJ_SUCCESS;
 
@@ -995,16 +1178,13 @@
 	    pjsua_perror(THIS_FILE, "Unable to start UDP transport", 
 			 status);
-	    return status;
-	}
-    }
-
-    /* The last account is default account to be used when nothing match 
-     * any configured accounts.
+	    goto on_error;
+	}
+    }
+
+    /* Initialize all unused accounts with default id and contact.
      */
     {
 	char buf[80];
-	pj_str_t tmp;
-	pjsua_acc_config *acc_cfg = 
-	    &pjsua.config.acc_config[pjsua.config.acc_cnt];
+	pj_str_t tmp, id;
 
 	tmp.ptr = buf;
@@ -1012,7 +1192,15 @@
 				   pjsua.config.sip_host.ptr,
 				   pjsua.config.sip_port);
-
-	pj_strdup_with_null( pjsua.pool, &acc_cfg->id, &tmp);
-	acc_cfg->contact = acc_cfg->id;
+	pj_strdup_with_null( pjsua.pool, &id, &tmp);
+
+	for (i=pjsua.config.acc_cnt; i<PJ_ARRAY_SIZE(pjsua.config.acc_config);
+	     ++i)
+	{
+	    pjsua_acc_config *acc_cfg = 
+		&pjsua.config.acc_config[pjsua.config.acc_cnt];
+
+	    acc_cfg->id = id;
+	    acc_cfg->contact = id;
+	}
     }
     
@@ -1020,108 +1208,10 @@
     /* Initialize accounts: */
     for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
-
-	pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[i];
-	pjsua_acc *acc = &pjsua.acc[i];
-	pjsip_uri *uri;
-	pjsip_sip_uri *sip_uri;
-
-	/* Need to parse local_uri to get the elements: */
-
-	uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr,
-			      acc_cfg->id.slen, 0);
-	if (uri == NULL) {
-	    pjsua_perror(THIS_FILE, "Invalid local URI", 
-			 PJSIP_EINVALIDURI);
-	    return PJSIP_EINVALIDURI;
-	}
-
-	/* Local URI MUST be a SIP or SIPS: */
-
-	if (!PJSIP_URI_SCHEME_IS_SIP(uri) && 
-	    !PJSIP_URI_SCHEME_IS_SIPS(uri)) 
-	{
-	    pjsua_perror(THIS_FILE, "Invalid local URI", 
-			 PJSIP_EINVALIDSCHEME);
-	    return PJSIP_EINVALIDSCHEME;
-	}
-
-
-	/* Get the SIP URI object: */
-
-	sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
-
-	acc->user_part = sip_uri->user;
-	acc->host_part = sip_uri->host;
-
-	/* Build Contact header */
-
-	if (acc_cfg->contact.slen == 0)  {
-	    char contact[128];
-	    const char *addr;
-	    int port;
-	    int len;
-
-	    addr = pjsua.config.sip_host.ptr;
-	    port = pjsua.config.sip_port;
-
-	    /* The local Contact is the username@ip-addr, where
-	     *  - username is taken from the local URI,
-	     *  - ip-addr in UDP transport's address name (which may have been
-	     *    resolved from STUN.
-	     */
-	    
-	    /* Build temporary contact string. */
-
-	    if (sip_uri->user.slen) {
-
-		/* With the user part. */
-		len = pj_ansi_snprintf(contact, sizeof(contact),
-				  "<sip:%.*s@%s:%d>",
-				  (int)sip_uri->user.slen,
-				  sip_uri->user.ptr,
-				  addr, port);
-	    } else {
-
-		/* Without user part */
-
-		len = pj_ansi_snprintf(contact, sizeof(contact),
-				  "<sip:%s:%d>",
-				  addr, port);
-	    }
-
-	    if (len < 1 || len >= sizeof(contact)) {
-		pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG);
-		return PJSIP_EURITOOLONG;
-	    }
-
-	    /* Duplicate Contact uri. */
-
-	    pj_strdup2(pjsua.pool, &acc_cfg->contact, contact);
-
-	}
-
-
-	/* Build route-set for this account */
-	if (pjsua.config.outbound_proxy.slen) {
-	    pj_str_t hname = { "Route", 5};
-	    pjsip_route_hdr *r;
-	    pj_str_t tmp;
-
-	    pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy);
-	    r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
-	    pj_list_push_back(&acc->route_set, r);
-	}
-
-	if (acc_cfg->proxy.slen) {
-	    pj_str_t hname = { "Route", 5};
-	    pjsip_route_hdr *r;
-	    pj_str_t tmp;
-
-	    pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy);
-	    r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
-	    pj_list_push_back(&acc->route_set, r);
-	}
-    }
-
+	status = init_acc(i);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Error initializing account", status);
+	    goto on_error;
+	}
+    }
 
 
@@ -1138,5 +1228,5 @@
 		pj_thread_destroy(pjsua.threads[i]);
 	    }
-	    return status;
+	    goto on_error;
 	}
     }
@@ -1148,28 +1238,29 @@
 	status = pjsua_regc_init(i);
 	if (status != PJ_SUCCESS)
-	    return status;
+	    goto on_error;
 
 	/* Perform registration, if required. */
 	if (pjsua.acc[i].regc) {
-	    pjsua_regc_update(i, 1);
-	}
-    }
-
-
-    /* Init buddies */
-    for (i=0; i<(int)pjsua.config.buddy_cnt; ++i) {
-	pjsua.buddies[i].uri = pjsua.config.buddy_uri[i];
-    }
-    pjsua.buddy_cnt = pjsua.config.buddy_cnt;
-
-    /* Find account for outgoing preence subscription */
-    for (i=0; i<pjsua.buddy_cnt; ++i) {
-	pjsua.buddies[i].acc_index = 
-	    pjsua_find_account_for_outgoing(&pjsua.buddies[i].uri);
-    }
+	    pjsua_acc_set_registration(i, PJ_TRUE);
+	}
+    }
+
+
+    /* Re-init buddies */
+    count = pjsua.config.buddy_cnt;
+    pjsua.config.buddy_cnt = 0;
+    for (i=0; i<(int)count; ++i) {
+	pj_str_t uri = pjsua.config.buddy_uri[i];
+	pjsua_buddy_add(&uri, NULL);
+    }
+
 
 
     PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION));
     return PJ_SUCCESS;
+
+on_error:
+    pjsua_destroy();
+    return status;
 }
 
@@ -1190,4 +1281,235 @@
 }
 
+/**
+ * Get maxinum number of conference ports.
+ */
+PJ_DEF(unsigned) pjsua_conf_max_ports(void)
+{
+    return pjsua.config.conf_ports;
+}
+
+
+/**
+ * Enum all conference ports.
+ */
+PJ_DEF(pj_status_t) pjsua_conf_enum_ports( unsigned *count,
+					   pjmedia_conf_port_info info[])
+{
+    return pjmedia_conf_get_ports_info(pjsua.mconf, count, info);
+}
+
+
+
+
+/**
+ * Connect conference port.
+ */
+PJ_DEF(pj_status_t) pjsua_conf_connect( unsigned src_port,
+					unsigned dst_port)
+{
+    return pjmedia_conf_connect_port(pjsua.mconf, src_port, dst_port, 0);
+}
+
+
+/**
+ * Connect conference port connection.
+ */
+PJ_DEF(pj_status_t) pjsua_conf_disconnect( unsigned src_port,
+					   unsigned dst_port)
+{
+    return pjmedia_conf_disconnect_port(pjsua.mconf, src_port, dst_port);
+}
+
+
+
+/**
+ * Create a file player.
+ */
+PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename,
+					 pjsua_player_id *id)
+{
+    unsigned slot;
+    char path[128];
+    pjmedia_port *port;
+    pj_status_t status;
+
+    if (pjsua.player_cnt >= PJ_ARRAY_SIZE(pjsua.player))
+	return PJ_ETOOMANY;
+
+    pj_memcpy(path, filename->ptr, filename->slen);
+    path[filename->slen] = '\0';
+    status = pjmedia_wav_player_port_create(pjsua.pool, path,
+					    pjsua.samples_per_frame *
+					       1000 / pjsua.clock_rate,
+					    0, 0, NULL,
+					    &port);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool, 
+				   port, filename, &slot);
+    if (status != PJ_SUCCESS) {
+	pjmedia_port_destroy(port);
+	return status;
+    }
+
+    pjsua.player[pjsua.player_cnt].port = port;
+    pjsua.player[pjsua.player_cnt].slot = slot;
+
+    if (*id)
+	*id = pjsua.player_cnt;
+
+    ++pjsua.player_cnt;
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Get conference port associated with player.
+ */
+PJ_DEF(unsigned) pjsua_player_get_conf_port(pjsua_player_id id)
+{
+    PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);
+    return pjsua.player[id].slot;
+}
+
+
+/**
+ * Re-wind playback.
+ */
+PJ_DEF(pj_status_t) pjsua_player_set_pos(pjsua_player_id id,
+					 pj_uint32_t samples)
+{
+    PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua.player[id].port != NULL, PJ_EINVALIDOP);
+
+    return pjmedia_wav_player_port_set_pos(pjsua.player[id].port, samples);
+}
+
+
+/**
+ * Get conference port associated with player.
+ */
+PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id)
+{
+    PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);
+
+    if (pjsua.player[id].port) {
+	pjmedia_port_destroy(pjsua.player[id].port);
+	pjsua.player[id].port = NULL;
+	pjsua.player[id].slot = 0xFFFF;
+	pjsua.player_cnt--;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Create a file recorder.
+ */
+PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,
+					   pjsua_recorder_id *id)
+{
+    unsigned slot;
+    char path[128];
+    pjmedia_port *port;
+    pj_status_t status;
+
+    if (pjsua.recorder_cnt >= PJ_ARRAY_SIZE(pjsua.recorder))
+	return PJ_ETOOMANY;
+
+    pj_memcpy(path, filename->ptr, filename->slen);
+    path[filename->slen] = '\0';
+    status = pjmedia_wav_writer_port_create(pjsua.pool, path,
+					    pjsua.clock_rate, 1,
+					    pjsua.samples_per_frame,
+					    16, 0, 0, NULL,
+					    &port);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool, 
+				   port, filename, &slot);
+    if (status != PJ_SUCCESS) {
+	pjmedia_port_destroy(port);
+	return status;
+    }
+
+    pjsua.recorder[pjsua.recorder_cnt].port = port;
+    pjsua.recorder[pjsua.recorder_cnt].slot = slot;
+
+    if (*id)
+	*id = pjsua.recorder_cnt;
+
+    ++pjsua.recorder_cnt;
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Get conference port associated with recorder.
+ */
+PJ_DEF(unsigned) pjsua_recorder_get_conf_port(pjsua_recorder_id id)
+{
+    PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL);
+    return pjsua.recorder[id].slot;
+}
+
+
+/**
+ * Destroy recorder (will complete recording).
+ */
+PJ_DEF(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id)
+{
+    PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL);
+
+    if (pjsua.recorder[id].port) {
+	pjmedia_port_destroy(pjsua.recorder[id].port);
+	pjsua.recorder[id].port = NULL;
+	pjsua.recorder[id].slot = 0xFFFF;
+	pjsua.recorder_cnt--;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/**
+ * Enum sound devices.
+ */
+PJ_DEF(pj_status_t) pjsua_enum_snd_devices( unsigned *count,
+					    pjmedia_snd_dev_info info[])
+{
+    int i, dev_count;
+
+    dev_count = pjmedia_snd_get_dev_count();
+    if (dev_count > (int)*count)
+	dev_count = *count;
+
+    for (i=0; i<dev_count; ++i) {
+	const pjmedia_snd_dev_info *dev_info;
+	dev_info = pjmedia_snd_get_dev_info(i);
+	pj_memcpy(&info[i], dev_info, sizeof(pjmedia_snd_dev_info));
+    }
+
+    *count = dev_count;
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Select or change sound device.
+ */
+PJ_DEF(pj_status_t) pjsua_set_snd_dev( int snd_capture_id,
+				       int snd_player_id)
+{
+    pjsua.config.snd_capture_id = snd_capture_id;
+    pjsua.config.snd_player_id = snd_player_id;
+    return PJ_SUCCESS;
+}
+
+
 /*
  * Destroy pjsua.
@@ -1209,5 +1531,5 @@
     for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
 	if (pjsua.acc[i].regc) {
-	    pjsua_regc_update(i, 0);
+	    pjsua_acc_set_registration(i, PJ_FALSE);
 	}
     }
@@ -1225,50 +1547,84 @@
 
     /* Wait for some time to allow unregistration to complete: */
-    PJ_LOG(4,(THIS_FILE, "Shutting down..."));
-    busy_sleep(1000);
-
-    /* Destroy conference bridge. */
-    if (pjsua.mconf)
-	pjmedia_conf_destroy(pjsua.mconf);
-
-    /* Destroy file port */
-    if (pjsua.file_port)
-	pjmedia_port_destroy(pjsua.file_port);
-
-
-    /* Shutdown all codecs: */
-#if PJMEDIA_HAS_SPEEX_CODEC
-    pjmedia_codec_speex_deinit();
-#endif /* PJMEDIA_HAS_SPEEX_CODEC */
-
-#if PJMEDIA_HAS_GSM_CODEC
-    pjmedia_codec_gsm_deinit();
-#endif /* PJMEDIA_HAS_GSM_CODEC */
-
-#if PJMEDIA_HAS_G711_CODEC
-    pjmedia_codec_g711_deinit();
-#endif	/* PJMEDIA_HAS_G711_CODEC */
-
-#if PJMEDIA_HAS_L16_CODEC
-    pjmedia_codec_l16_deinit();
-#endif	/* PJMEDIA_HAS_L16_CODEC */
+    if (pjsua.endpt) {
+	PJ_LOG(4,(THIS_FILE, "Shutting down..."));
+	busy_sleep(1000);
+    }
+
+    /* If we have master port, destroying master port will recursively
+     * destroy conference bridge, otherwise must destroy it manually. 
+     */
+    if (pjsua.master_port) {
+	pjmedia_master_port_destroy(pjsua.master_port);
+	pjsua.master_port = NULL;
+    } else {
+	if (pjsua.snd_port) {
+	    pjmedia_snd_port_destroy(pjsua.snd_port);
+	    pjsua.snd_port = NULL;
+	}
+	if (pjsua.mconf) {
+	    pjmedia_conf_destroy(pjsua.mconf);
+	    pjsua.mconf = NULL;
+	}
+    }
+
+    /* Destroy file players */
+    for (i=0; i<PJ_ARRAY_SIZE(pjsua.player); ++i) {
+	if (pjsua.player[i].port) {
+	    pjmedia_port_destroy(pjsua.player[i].port);
+	    pjsua.player[i].port = NULL;
+	}
+    }
+
+
+    /* Destroy file recorders */
+    for (i=0; i<PJ_ARRAY_SIZE(pjsua.recorder); ++i) {
+	if (pjsua.recorder[i].port) {
+	    pjmedia_port_destroy(pjsua.recorder[i].port);
+	    pjsua.recorder[i].port = NULL;
+	}
+    }
 
 
     /* Close transports */
-    for (i=0; pjsua.config.start_rtp_port && i<(int)pjsua.config.max_calls; ++i) {
-	pjmedia_transport_udp_close(pjsua.calls[i].med_tp);
+    for (i=0; i<(int)pjsua.config.max_calls; ++i) {
+	if (pjsua.calls[i].med_tp) {
+	    (*pjsua.calls[i].med_tp->op->destroy)(pjsua.calls[i].med_tp);
+	    pjsua.calls[i].med_tp = NULL;
+	}
     }
 
     /* Destroy media endpoint. */
-
-    pjmedia_endpt_destroy(pjsua.med_endpt);
+    if (pjsua.med_endpt) {
+
+	/* Shutdown all codecs: */
+#	if PJMEDIA_HAS_SPEEX_CODEC
+	    pjmedia_codec_speex_deinit();
+#	endif /* PJMEDIA_HAS_SPEEX_CODEC */
+
+#	if PJMEDIA_HAS_GSM_CODEC
+	    pjmedia_codec_gsm_deinit();
+#	endif /* PJMEDIA_HAS_GSM_CODEC */
+
+#	if PJMEDIA_HAS_G711_CODEC
+	    pjmedia_codec_g711_deinit();
+#	endif	/* PJMEDIA_HAS_G711_CODEC */
+
+#	if PJMEDIA_HAS_L16_CODEC
+	    pjmedia_codec_l16_deinit();
+#	endif	/* PJMEDIA_HAS_L16_CODEC */
+
+
+	pjmedia_endpt_destroy(pjsua.med_endpt);
+	pjsua.med_endpt = NULL;
+    }
 
     /* Destroy endpoint. */
-
-    pjsip_endpt_destroy(pjsua.endpt);
-    pjsua.endpt = NULL;
+    if (pjsua.endpt) {
+	pjsip_endpt_destroy(pjsua.endpt);
+	pjsua.endpt = NULL;
+    }
 
     /* Destroy caching pool. */
-
     pj_caching_pool_destroy(&pjsua.cp);
 
@@ -1279,2 +1635,21 @@
 }
 
+
+/**
+ * Get SIP endpoint instance.
+ * Only valid after pjsua_init().
+ */
+PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
+{
+    return pjsua.endpt;
+}
+
+/**
+ * Get media endpoint instance.
+ * Only valid after pjsua_init().
+ */
+PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
+{
+    return pjsua.med_endpt;
+}
+
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_im.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_im.c	(revision 481)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_im.c	(revision 482)
@@ -19,4 +19,5 @@
 #include <pjsua-lib/pjsua.h>
 #include <pj/log.h>
+#include "pjsua_imp.h"
 
 /*
@@ -272,6 +273,6 @@
  * Send IM outside dialog.
  */
-PJ_DEF(pj_status_t) pjsua_im_send(int acc_index, const char *dst_uri, 
-				  const char *str)
+PJ_DEF(pj_status_t) pjsua_im_send(int acc_index, const pj_str_t *dst_uri, 
+				  const pj_str_t *str)
 {
     pjsip_tx_data *tdata;
@@ -280,12 +281,11 @@
     const pj_str_t mime_plain = pj_str("plain");
     pj_str_t *text;
-    const pj_str_t dst = pj_str((char*)dst_uri);
     pj_status_t status;
 
     /* Create request. */
     status = pjsip_endpt_create_request(pjsua.endpt, &pjsip_message_method,
-					&dst, 
+					dst_uri, 
 					&pjsua.config.acc_config[acc_index].id,
-					&dst, NULL, NULL, -1, NULL, &tdata);
+					dst_uri, NULL, NULL, -1, NULL, &tdata);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create request", status);
@@ -308,5 +308,5 @@
      */
     text = pj_pool_alloc(tdata->pool, sizeof(pj_str_t));
-    pj_strdup2_with_null(tdata->pool, text, str);
+    pj_strdup_with_null(tdata->pool, text, str);
 
     /* Add message body */
@@ -334,8 +334,7 @@
  * Send typing indication outside dialog.
  */
-PJ_DEF(pj_status_t) pjsua_im_typing(int acc_index, const char *dst_uri, 
+PJ_DEF(pj_status_t) pjsua_im_typing(int acc_index, const pj_str_t *dst_uri, 
 				    pj_bool_t is_typing)
 {
-    const pj_str_t dst = pj_str((char*)dst_uri);
     pjsip_tx_data *tdata;
     pj_status_t status;
@@ -343,7 +342,7 @@
     /* Create request. */
     status = pjsip_endpt_create_request( pjsua.endpt, &pjsip_message_method,
-					 &dst, 
+					 dst_uri, 
 					 &pjsua.config.acc_config[acc_index].id,
-					 &dst, NULL, NULL, -1, NULL, &tdata);
+					 dst_uri, NULL, NULL, -1, NULL, &tdata);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create request", status);
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_imp.h
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_imp.h	(revision 481)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_imp.h	(revision 482)
@@ -22,4 +22,166 @@
 
 
+
+
+/** 
+ * Structure to be attached to invite dialog. 
+ * Given a dialog "dlg", application can retrieve this structure
+ * by accessing dlg->mod_data[pjsua.mod.id].
+ */
+struct pjsua_call
+{
+    unsigned		 index;	    /**< Index in pjsua array.		    */
+    pjsip_inv_session	*inv;	    /**< The invite session.		    */
+    pj_time_val		 start_time;/**< First INVITE sent/received.	    */
+    pj_time_val		 res_time;  /**< First response sent/received.	    */
+    pj_time_val		 conn_time; /**< Connected/confirmed time.	    */
+    pj_time_val		 dis_time;  /**< Disconnect time.		    */
+    int			 acc_index; /**< Account index being used.	    */
+    pjmedia_session	*session;   /**< The media session.		    */
+    unsigned		 conf_slot; /**< Slot # in conference bridge.	    */
+    pjsip_evsub		*xfer_sub;  /**< Xfer server subscription, if this
+					 call was triggered by xfer.	    */
+    pjmedia_sock_info	 skinfo;    /**< Preallocated media sockets.	    */
+    pjmedia_transport	*med_tp;    /**< Media transport.		    */
+    void		*app_data;  /**< Application data.		    */
+    pj_timer_entry	 refresh_tm;/**< Timer to send re-INVITE.	    */
+    pj_timer_entry	 hangup_tm; /**< Timer to hangup call.		    */
+};
+
+typedef struct pjsua_call pjsua_call;
+
+
+/**
+ * Buddy data.
+ */
+struct pjsua_buddy
+{
+    unsigned		 index;	    /**< Buddy index.			*/
+    pj_str_t		 name;	    /**< Buddy name.			*/
+    pj_str_t		 display;   /**< Buddy display name.		*/
+    pj_str_t		 host;	    /**< Buddy host.			*/
+    unsigned		 port;	    /**< Buddy port.			*/
+    int			 acc_index; /**< Which account to use.		*/
+    pj_bool_t		 monitor;   /**< Should we monitor?		*/
+    pjsip_evsub		*sub;	    /**< Buddy presence subscription	*/
+    pjsip_pres_status	 status;    /**< Buddy presence status.		*/
+};
+
+typedef struct pjsua_buddy pjsua_buddy;
+
+
+/**
+ * Server presence subscription list head.
+ */
+struct pjsua_srv_pres
+{
+    PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres);
+    pjsip_evsub	    *sub;
+    char	    *remote;
+};
+
+typedef struct pjsua_srv_pres pjsua_srv_pres;
+
+
+
+/**
+ * Account
+ */
+struct pjsua_acc
+{
+    int		     index;	    /**< Index in accounts array.	*/
+    pj_str_t	     user_part;	    /**< User part of local URI.	*/
+    pj_str_t	     host_part;	    /**< Host part of local URI.	*/
+
+    pjsip_regc	    *regc;	    /**< Client registration session.   */
+    pj_timer_entry   reg_timer;	    /**< Registration timer.		*/
+    pj_status_t	     reg_last_err;  /**< Last registration error.	*/
+    int		     reg_last_code; /**< Last status last register.	*/
+
+    pjsip_route_hdr  route_set;	    /**< Route set.			*/
+
+    pj_bool_t	     online_status; /**< Our online status.		*/
+    pjsua_srv_pres   pres_srv_list; /**< Server subscription list.	*/
+
+    void	    *app_data;	    /**< Application data.		*/
+};
+
+
+/**
+ * @see pjsua_acc
+ */
+typedef struct pjsua_acc pjsua_acc;
+
+
+/* PJSUA application variables. */
+struct pjsua
+{
+    /* Control: */
+    pj_caching_pool  cp;	    /**< Global pool factory.		*/
+    pjsip_endpoint  *endpt;	    /**< Global endpoint.		*/
+    pj_pool_t	    *pool;	    /**< pjsua's private pool.		*/
+    pjsip_module     mod;	    /**< pjsua's PJSIP module.		*/
+
+    
+    /* Config: */
+    pjsua_config    config;	    /**< PJSUA configs			*/
+
+    /* Application callback
+    : */
+    pjsua_callback  cb;		    /**< Application callback.		*/
+
+    /* Media:  */
+    pjmedia_endpt   *med_endpt;	    /**< Media endpoint.		*/
+    unsigned	     clock_rate;    /**< Conference bridge's clock rate.*/
+    unsigned	     samples_per_frame; /**< Bridge's frame size.	*/
+    pjmedia_conf    *mconf;	    /**< Media conference.		*/
+    
+    pjmedia_snd_port *snd_port;	    /**< Sound device port.		*/
+    pjmedia_master_port *master_port; /**< Master port, when no snd dev	*/
+    
+    unsigned	     player_cnt;    /**< Number of file player.		*/
+
+    /** Array of file players */
+    struct {
+	unsigned     slot;	    /**< WAV player slot in bridge	*/
+	pjmedia_port *port;	    /**< WAV player port.		*/
+    } player[32];
+
+    unsigned	     recorder_cnt;  /**< Number of file recorders.	*/
+
+    /** Array of file recorders */
+    struct {
+	unsigned     slot;	    /**< Slot # in conf bridge.		*/
+	pjmedia_port *port;	    /**< The recorder media port.	*/
+    } recorder[32];
+
+    /* Account: */
+    pjsua_acc	     acc[PJSUA_MAX_ACC];    /** Client regs array.	*/
+
+
+    /* Threading (optional): */
+    pj_thread_t	    *threads[8];    /**< Thread instances.		*/
+    pj_bool_t	     quit_flag;	    /**< To signal thread to quit.	*/
+
+    /* Transport (UDP): */
+    pj_sock_t	     sip_sock;	    /**< SIP UDP socket.		*/
+    pj_sockaddr_in   sip_sock_name; /**< Public/STUN UDP socket addr.	*/
+
+
+    /* PJSUA Calls: */
+    unsigned	     call_cnt;	    /**< Number of calls.		*/
+    pjsua_call	     calls[PJSUA_MAX_CALLS];	/** Calls array.	*/
+
+
+    /* SIMPLE and buddy status: */
+    pjsua_buddy	     buddies[PJSUA_MAX_BUDDIES];
+};
+
+
+/** PJSUA instance. */
+extern struct pjsua pjsua;
+
+
+
 /**
  * Find account for incoming request.
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c	(revision 481)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_pres.c	(revision 482)
@@ -236,6 +236,7 @@
     if (buddy) {
 	PJ_LOG(3,(THIS_FILE, 
-		  "Presence subscription to %s is %s",
-		  buddy->uri.ptr, 
+		  "Presence subscription to %.*s is %s",
+		  (int)pjsua.config.buddy_uri[buddy->index].slen,
+		  pjsua.config.buddy_uri[buddy->index].ptr, 
 		  pjsip_evsub_get_state_name(sub)));
 
@@ -245,4 +246,8 @@
 	    pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
 	}
+
+	/* Call callback */
+	if (pjsua.cb.on_buddy_state)
+	    (*pjsua.cb.on_buddy_state)(buddy->index);
     }
 }
@@ -262,13 +267,4 @@
 	/* Update our info. */
 	pjsip_pres_get_status(sub, &buddy->status);
-
-	if (buddy->status.info_cnt) {
-	    PJ_LOG(3,(THIS_FILE, "%s is %s",
-		      buddy->uri.ptr,
-		      (buddy->status.info[0].basic_open?"online":"offline")));
-	} else {
-	    PJ_LOG(3,(THIS_FILE, "No presence info for %s",
-		      buddy->uri.ptr));
-	}
     }
 
@@ -321,5 +317,5 @@
 				   &acc_config->id,
 				   &acc_config->contact,
-				   &pjsua.buddies[index].uri,
+				   &pjsua.config.buddy_uri[index],
 				   NULL, &dlg);
     if (status != PJ_SUCCESS) {
@@ -401,7 +397,7 @@
 static void refresh_client_subscription(void)
 {
-    int i;
-
-    for (i=0; i<pjsua.buddy_cnt; ++i) {
+    unsigned i;
+
+    for (i=0; i<pjsua.config.buddy_cnt; ++i) {
 
 	if (pjsua.buddies[i].monitor && !pjsua.buddies[i].sub) {
@@ -433,4 +429,123 @@
 
 /*
+ * Get buddy count.
+ */
+PJ_DEF(unsigned) pjsua_get_buddy_count(void)
+{
+    return pjsua.config.buddy_cnt;
+}
+
+
+/**
+ * Get buddy info.
+ */
+PJ_DEF(pj_status_t) pjsua_buddy_get_info(unsigned index,
+					 pjsua_buddy_info *info)
+{
+    pjsua_buddy *buddy;
+
+    PJ_ASSERT_RETURN(index < pjsua.config.buddy_cnt, PJ_EINVAL);
+
+    pj_memset(info, 0, sizeof(pjsua_buddy_info));
+
+    buddy = &pjsua.buddies[index];
+    info->index = buddy->index;
+    info->is_valid = pjsua.config.buddy_uri[index].slen;
+    if (!info->is_valid)
+	return PJ_SUCCESS;
+
+    info->name = buddy->name;
+    info->display_name = buddy->display;
+    info->host = buddy->host;
+    info->port = buddy->port;
+    info->uri = pjsua.config.buddy_uri[index];
+    
+    if (buddy->sub == NULL || buddy->status.info_cnt==0) {
+	info->status = PJSUA_BUDDY_STATUS_UNKNOWN;
+	info->status_text = pj_str("?");
+    } else if (pjsua.buddies[index].status.info[0].basic_open) {
+	info->status = PJSUA_BUDDY_STATUS_ONLINE;
+	info->status_text = pj_str("Online");
+    } else {
+	info->status = PJSUA_BUDDY_STATUS_OFFLINE;
+	info->status_text = pj_str("Offline");
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * Add new buddy.
+ */
+PJ_DEF(pj_status_t) pjsua_buddy_add( const pj_str_t *uri,
+				     int *buddy_index)
+{
+    pjsip_name_addr *url;
+    pjsip_sip_uri *sip_uri;
+    int index;
+    pj_str_t tmp;
+
+    PJ_ASSERT_RETURN(pjsua.config.buddy_cnt <= PJ_ARRAY_SIZE(pjsua.config.buddy_uri),
+		     PJ_ETOOMANY);
+
+    index = pjsua.config.buddy_cnt;
+
+    /* Get name and display name for buddy */
+    pj_strdup_with_null(pjsua.pool, &tmp, uri);
+    url = (pjsip_name_addr*)pjsip_parse_uri(pjsua.pool, tmp.ptr, tmp.slen,
+					    PJSIP_PARSE_URI_AS_NAMEADDR);
+
+    if (url == NULL)
+	return PJSIP_EINVALIDURI;
+
+    /* Save URI */
+    pjsua.config.buddy_uri[index] = tmp;
+
+    sip_uri = (pjsip_sip_uri*) url->uri;
+    pjsua.buddies[index].name = sip_uri->user;
+    pjsua.buddies[index].display = url->display;
+    pjsua.buddies[index].host = sip_uri->host;
+    pjsua.buddies[index].port = sip_uri->port;
+    if (pjsua.buddies[index].port == 0)
+	pjsua.buddies[index].port = 5060;
+
+    /* Find account for outgoing preence subscription */
+    pjsua.buddies[index].acc_index = 
+	pjsua_find_account_for_outgoing(&pjsua.config.buddy_uri[index]);
+
+    if (buddy_index)
+	*buddy_index = index;
+
+    pjsua.config.buddy_cnt++;
+
+    return PJ_SUCCESS;
+}
+
+
+
+PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( unsigned index,
+						pj_bool_t monitor)
+{
+    pjsua_buddy *buddy;
+
+    PJ_ASSERT_RETURN(index < pjsua.config.buddy_cnt, PJ_EINVAL);
+
+    buddy = &pjsua.buddies[index];
+    buddy->monitor = monitor;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsua_acc_set_online_status( unsigned acc_index,
+						 pj_bool_t is_online)
+{
+    PJ_ASSERT_RETURN(acc_index < pjsua.config.acc_cnt, PJ_EINVAL);
+    pjsua.acc[acc_index].online_status = is_online;
+    return PJ_SUCCESS;
+}
+
+
+/*
  * Refresh presence
  */
@@ -447,6 +562,6 @@
 void pjsua_pres_shutdown(void)
 {
-    int acc_index;
-    int i;
+    unsigned acc_index;
+    unsigned i;
 
     for (acc_index=0; acc_index<(int)pjsua.config.acc_cnt; ++acc_index) {
@@ -454,5 +569,5 @@
     }
 
-    for (i=0; i<pjsua.buddy_cnt; ++i) {
+    for (i=0; i<pjsua.config.buddy_cnt; ++i) {
 	pjsua.buddies[i].monitor = 0;
     }
@@ -468,6 +583,6 @@
 void pjsua_pres_dump(pj_bool_t detail)
 {
-    int acc_index;
-    int i;
+    unsigned acc_index;
+    unsigned i;
 
 
@@ -498,5 +613,5 @@
 	count = 0;
 
-	for (i=0; i<pjsua.buddy_cnt; ++i) {
+	for (i=0; i<pjsua.config.buddy_cnt; ++i) {
 	    if (pjsua.buddies[i].sub) {
 		++count;
@@ -545,19 +660,21 @@
     PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
 
-    if (pjsua.buddy_cnt == 0) {
+    if (pjsua.config.buddy_cnt == 0) {
 
 	PJ_LOG(3,(THIS_FILE, "  - no buddy list - "));
 
     } else {
-	for (i=0; i<pjsua.buddy_cnt; ++i) {
+	for (i=0; i<pjsua.config.buddy_cnt; ++i) {
 
 	    if (pjsua.buddies[i].sub) {
-		PJ_LOG(3,(THIS_FILE, "  %10s %s",
+		PJ_LOG(3,(THIS_FILE, "  %10s %.*s",
 			  pjsip_evsub_get_state_name(pjsua.buddies[i].sub),
-			  pjsua.buddies[i].uri.ptr));
+			  (int)pjsua.config.buddy_uri[i].slen,
+			  pjsua.config.buddy_uri[i].ptr));
 	    } else {
-		PJ_LOG(3,(THIS_FILE, "  %10s %s",
+		PJ_LOG(3,(THIS_FILE, "  %10s %.*s",
 			  "(null)",
-			  pjsua.buddies[i].uri.ptr));
+			  (int)pjsua.config.buddy_uri[i].slen,
+			  pjsua.config.buddy_uri[i].ptr));
 	    }
 	}
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_reg.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_reg.c	(revision 481)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_reg.c	(revision 482)
@@ -18,4 +18,5 @@
  */
 #include <pjsua-lib/pjsua.h>
+#include "pjsua_imp.h"
 
 
@@ -83,8 +84,54 @@
 
 
+/**
+ * Get number of accounts.
+ */
+PJ_DEF(unsigned) pjsua_get_acc_count(void)
+{
+    return pjsua.config.acc_cnt;
+}
+
+
+/**
+ * Get account info.
+ */
+PJ_DEF(pj_status_t) pjsua_acc_get_info( unsigned acc_index,
+					pjsua_acc_info *info)
+{
+    pjsua_acc *acc = &pjsua.acc[acc_index];
+    pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index];
+
+    PJ_ASSERT_RETURN(acc_index < pjsua.config.acc_cnt, PJ_EINVAL);
+
+    pj_memset(info, 0, sizeof(pjsua_acc_info));
+
+    info->index = acc_index;
+    info->acc_id = acc_cfg->id;
+    info->has_registration = (acc->regc != NULL);
+    info->online_status = acc->online_status;
+    
+    if (acc->reg_last_err) {
+	info->status = acc->reg_last_err;
+	pj_strerror(acc->reg_last_err, info->buf, sizeof(info->buf));
+	info->status_text = pj_str(info->buf);
+    } else {
+	info->status = acc->reg_last_code;
+	info->status_text = *pjsip_get_status_text(acc->reg_last_code);
+    }
+    
+    if (acc->regc) {
+	pjsip_regc_info regc_info;
+	pjsip_regc_get_info(acc->regc, &regc_info);
+	info->expires = regc_info.next_reg;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
 /*
  * Update registration. If renew is false, then unregistration will be performed.
  */
-PJ_DECL(void) pjsua_regc_update(int acc_index, pj_bool_t renew)
+PJ_DECL(void) pjsua_acc_set_registration(unsigned acc_index, pj_bool_t renew)
 {
     pj_status_t status = 0;
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_settings.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_settings.c	(revision 481)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_settings.c	(revision 482)
@@ -19,4 +19,6 @@
 #include <pjsua-lib/pjsua.h>
 #include <stdio.h>
+#include "pjsua_imp.h"
+
 
 /*
@@ -1008,4 +1010,7 @@
     PJ_UNUSED_ARG(max);
 
+    if (config == NULL)
+	config = &pjsua.config;
+
     cfg.ptr = buf;
     cfg.slen = 0;
@@ -1211,2 +1216,11 @@
     return PJ_SUCCESS;
 }
+
+/**
+ * Get pjsua running config.
+ */
+PJ_DEF(const pjsua_config*) pjsua_get_config(void)
+{
+    return &pjsua.config;
+}
+
