Index: /pjproject/trunk/pjsip/build/pjsua.dsp
===================================================================
--- /pjproject/trunk/pjsip/build/pjsua.dsp	(revision 235)
+++ /pjproject/trunk/pjsip/build/pjsua.dsp	(revision 236)
@@ -99,9 +99,9 @@
 # Begin Source File
 
-SOURCE=..\src\pjsua\pjsua_core.c
+SOURCE=..\src\pjsua\pjsua_call.c
 # End Source File
 # Begin Source File
 
-SOURCE=..\src\pjsua\pjsua_inv.c
+SOURCE=..\src\pjsua\pjsua_core.c
 # End Source File
 # Begin Source File
Index: /pjproject/trunk/pjsip/src/pjsua/main.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/main.c	(revision 235)
+++ /pjproject/trunk/pjsip/src/pjsua/main.c	(revision 236)
@@ -24,27 +24,84 @@
 
 /* Current dialog */
-static struct pjsua_inv_data *inv_session;
+static int current_acc;
+static int current_call = -1;
+
+
+/*
+ * Find next call.
+ */
+static pj_bool_t find_next_call(void)
+{
+    int i;
+
+    for (i=current_call+1; i<(int)pjsua.max_calls; ++i) {
+	if (pjsua.calls[i].inv != NULL) {
+	    current_call = i;
+	    return PJ_TRUE;
+	}
+    }
+
+    for (i=0; i<current_call; ++i) {
+	if (pjsua.calls[i].inv != NULL) {
+	    current_call = i;
+	    return PJ_TRUE;
+	}
+    }
+
+    current_call = -1;
+    return PJ_FALSE;
+}
+
+
+/*
+ * Find previous call.
+ */
+static pj_bool_t find_prev_call(void)
+{
+    int i;
+
+    for (i=current_call-1; i>=0; --i) {
+	if (pjsua.calls[i].inv != NULL) {
+	    current_call = i;
+	    return PJ_TRUE;
+	}
+    }
+
+    for (i=pjsua.max_calls-1; i>current_call; --i) {
+	if (pjsua.calls[i].inv != NULL) {
+	    current_call = i;
+	    return PJ_TRUE;
+	}
+    }
+
+    current_call = -1;
+    return PJ_FALSE;
+}
+
+
 
 /*
  * Notify UI when invite state has changed.
  */
-void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
-{
+void pjsua_ui_inv_on_state_changed(int call_index, pjsip_event *e)
+{
+    pjsua_call *call = &pjsua.calls[call_index];
+
     PJ_UNUSED_ARG(e);
 
-    PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s", 
-	      pjsua_inv_state_names[inv->state]));
-
-    if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
-	if (inv == inv_session->inv) {
-	    inv_session = inv_session->next;
-	    if (inv_session == &pjsua.inv_list)
-		inv_session = pjsua.inv_list.next;
+    PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s", 
+	      call_index,
+	      pjsua_inv_state_names[call->inv->state]));
+
+    if (call->inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+	call->inv = NULL;
+	if ((int)call->index == current_call) {
+	    find_next_call();
 	}
 
     } else {
 
-	if (inv_session == &pjsua.inv_list || inv_session == NULL)
-	    inv_session = inv->mod_data[pjsua.mod.id];
+	if (call && current_call==-1)
+	    current_call = call->index;
 
     }
@@ -67,8 +124,8 @@
 static void print_buddy_list(void)
 {
-    unsigned i;
+    int i;
 
     puts("Buddy list:");
-    //puts("-------------------------------------------------------------------------------");
+
     if (pjsua.buddy_cnt == 0)
 	puts(" -none-");
@@ -94,24 +151,28 @@
 }
 
+
 /*
- * Show a bit of help.
- */
-static void keystroke_help(void)
+ * Print account status.
+ */
+static void print_acc_status(int acc_index)
 {
     char reg_status[128];
 
-    if (pjsua.regc == NULL) {
+    if (pjsua.acc[acc_index].regc == NULL) {
 	pj_ansi_strcpy(reg_status, " -not registered to server-");
-    } else if (pjsua.regc_last_err != PJ_SUCCESS) {
-	pj_strerror(pjsua.regc_last_err, reg_status, sizeof(reg_status));
-    } else if (pjsua.regc_last_code>=200 && pjsua.regc_last_code<=699) {
+
+    } 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;
 
-	pjsip_regc_get_info(pjsua.regc, &info);
+	pjsip_regc_get_info(pjsua.acc[acc_index].regc, &info);
 
 	pj_snprintf(reg_status, sizeof(reg_status),
 		    "%s (%.*s;expires=%d)",
-		    pjsip_get_status_text(pjsua.regc_last_code)->ptr,
+		    pjsip_get_status_text(pjsua.acc[acc_index].reg_last_code)->ptr,
 		    (int)info.client_uri.slen,
 		    info.client_uri.ptr,
@@ -119,10 +180,25 @@
 
     } else {
-	pj_sprintf(reg_status, "in progress (%d)", pjsua.regc_last_code);
-    }
-
-    printf(">>>>\nRegistration status: %s\n", reg_status);
-    printf("Online status: %s\n", 
-	   (pjsua.online_status ? "Online" : "Invisible"));
+	pj_sprintf(reg_status, "in progress (%d)", 
+		   pjsua.acc[acc_index].reg_last_code);
+    }
+
+    printf("[%2d] Registration status: %s\n", acc_index, reg_status);
+    printf("     Online status: %s\n", 
+	   (pjsua.acc[acc_index].online_status ? "Online" : "Invisible"));
+}
+
+/*
+ * Show a bit of help.
+ */
+static void keystroke_help(void)
+{
+    int i;
+
+    printf(">>>>\n");
+
+    for (i=0; i<pjsua.acc_cnt; ++i)
+	print_acc_status(i);
+
     print_buddy_list();
     
@@ -135,5 +211,5 @@
     puts("|  h  Hangup call              |  u  Unsubscribe presence | ru  Unregister    |");
     puts("|  ]  Select next dialog       |  t  ToGgle Online status |  d  Dump status   |");
-    puts("|  [  Select previous dialog   |                          |                   |");
+    puts("|  [  Select previous dialog   |                          | dc  Dump config   |");
     puts("|                              +--------------------------+-------------------+");
     puts("|  H  Hold call                |     Conference Command   |                   |");
@@ -263,5 +339,5 @@
 {
     unsigned i, count;
-    pjmedia_conf_port_info info[16];
+    pjmedia_conf_port_info info[PJSUA_MAX_CALLS];
 
     printf("Conference ports:\n");
@@ -270,10 +346,10 @@
     pjmedia_conf_get_ports_info(pjsua.mconf, &count, info);
     for (i=0; i<count; ++i) {
-	char txlist[80];
-	unsigned j;
+	char txlist[PJSUA_MAX_CALLS*4+10];
+	int j;
 	pjmedia_conf_port_info *port_info = &info[i];	
 	
 	txlist[0] = '\0';
-	for (j=0; j<pjsua.max_ports; ++j) {
+	for (j=0; j<pjsua.max_calls+PJSUA_CONF_MORE_PORTS; ++j) {
 	    char s[10];
 	    if (port_info->listener[j]) {
@@ -310,6 +386,5 @@
 	case 'm':
 	    /* Make call! : */
-	    if (pj_list_size(&pjsua.inv_list))
-		printf("(You have %d calls)\n", pj_list_size(&pjsua.inv_list));
+	    printf("(You currently have %d calls)\n", pjsua.call_cnt);
 	    
 	    ui_input_url("Make call", buf, sizeof(buf), &result);
@@ -318,7 +393,9 @@
 		    puts("You can't do that with make call!");
 		else
-		    pjsua_invite(pjsua.buddies[result.nb_result].uri.ptr, NULL);
+		    pjsua_make_call( current_acc, 
+				     pjsua.buddies[result.nb_result].uri.ptr, 
+				     NULL);
 	    } else if (result.uri_result)
-		pjsua_invite(result.uri_result, NULL);
+		pjsua_make_call( current_acc, result.uri_result, NULL);
 	    
 	    break;
@@ -327,7 +404,7 @@
 	case 'a':
 
-	    if (inv_session == &pjsua.inv_list || 
-		inv_session->inv->role != PJSIP_ROLE_UAS ||
-		inv_session->inv->state >= PJSIP_INV_STATE_CONNECTING) 
+	    if (current_call == -1 || 
+		pjsua.calls[current_call].inv->role != PJSIP_ROLE_UAS ||
+		pjsua.calls[current_call].inv->state >= PJSIP_INV_STATE_CONNECTING)
 	    {
 		puts("No pending incoming call");
@@ -350,5 +427,5 @@
 		 * keyboard input.
 		 */
-		if (inv_session == &pjsua.inv_list) {
+		if (current_call == -1) {
 		    puts("Call has been disconnected");
 		    fflush(stdout);
@@ -356,8 +433,10 @@
 		}
 
-		status = pjsip_inv_answer(inv_session->inv, atoi(buf), 
+		status = pjsip_inv_answer(pjsua.calls[current_call].inv, 
+					  atoi(buf), 
 					  NULL, NULL, &tdata);
 		if (status == PJ_SUCCESS)
-		    status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL);
+		    status = pjsip_inv_send_msg(pjsua.calls[current_call].inv,
+						tdata, NULL);
 
 		if (status != PJ_SUCCESS)
@@ -371,5 +450,5 @@
 	case 'h':
 
-	    if (inv_session == &pjsua.inv_list) {
+	    if (current_call == -1) {
 		puts("No current call");
 		fflush(stdout);
@@ -377,5 +456,5 @@
 
 	    } else {
-		pjsua_inv_hangup(inv_session, PJSIP_SC_DECLINE);
+		pjsua_call_hangup(current_call, PJSIP_SC_DECLINE);
 	    }
 	    break;
@@ -387,20 +466,17 @@
 	     */
 	    if (menuin[0] == ']') {
-		inv_session = inv_session->next;
-		if (inv_session == &pjsua.inv_list)
-		    inv_session = pjsua.inv_list.next;
+		find_next_call();
 
 	    } else {
-		inv_session = inv_session->prev;
-		if (inv_session == &pjsua.inv_list)
-		    inv_session = pjsua.inv_list.prev;
-	    }
-
-	    if (inv_session != &pjsua.inv_list) {
+		find_prev_call();
+	    }
+
+	    if (current_call != -1) {
 		char url[PJSIP_MAX_URL_SIZE];
 		int len;
-
-		len = pjsip_uri_print(0, inv_session->inv->dlg->remote.info->uri,
-				      url, sizeof(url)-1);
+		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>");
@@ -420,7 +496,7 @@
 	     * Hold call.
 	     */
-	    if (inv_session != &pjsua.inv_list) {
+	    if (current_call != -1) {
 		
-		pjsua_inv_set_hold(inv_session);
+		pjsua_call_set_hold(current_call);
 
 	    } else {
@@ -433,7 +509,7 @@
 	     * Send re-INVITE (to release hold, etc).
 	     */
-	    if (inv_session != &pjsua.inv_list) {
+	    if (current_call != -1) {
 		
-		pjsua_inv_reinvite(inv_session);
+		pjsua_call_reinvite(current_call);
 
 	    } else {
@@ -446,10 +522,10 @@
 	     * Transfer call.
 	     */
-	    if (inv_session == &pjsua.inv_list) {
+	    if (current_call == -1) {
 		
 		PJ_LOG(3,(THIS_FILE, "No current call"));
 
 	    } else {
-		struct pjsua_inv_data *cur = inv_session;
+		int call = current_call;
 
 		ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
@@ -457,5 +533,5 @@
 		/* Check if call is still there. */
 
-		if (cur != inv_session) {
+		if (call != current_call) {
 		    puts("Call has been disconnected");
 		    continue;
@@ -466,9 +542,9 @@
 			puts("You can't do that with transfer call!");
 		    else
-			pjsua_inv_xfer_call( inv_session,
-					     pjsua.buddies[result.nb_result].uri.ptr);
+			pjsua_call_xfer( current_call,
+					 pjsua.buddies[result.nb_result].uri.ptr);
 
 		} else if (result.uri_result) {
-		    pjsua_inv_xfer_call( inv_session, result.uri_result);
+		    pjsua_call_xfer( current_call, result.uri_result);
 		}
 	    }
@@ -479,9 +555,9 @@
 	     * Send DTMF strings.
 	     */
-	    if (inv_session == &pjsua.inv_list) {
+	    if (current_call == -1) {
 		
 		PJ_LOG(3,(THIS_FILE, "No current call"));
 
-	    } else if (inv_session->session == NULL) {
+	    } else if (pjsua.calls[current_call].session == NULL) {
 
 		PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
@@ -489,5 +565,5 @@
 	    } else {
 		pj_str_t digits;
-		struct pjsua_inv_data *cur = inv_session;
+		int call = current_call;
 		pj_status_t status;
 
@@ -498,5 +574,5 @@
 		}
 
-		if (cur != inv_session) {
+		if (call != current_call) {
 		    puts("Call has been disconnected");
 		    continue;
@@ -504,5 +580,5 @@
 
 		digits = pj_str(buf);
-		status = pjmedia_session_dial_dtmf(inv_session->session, 0, 
+		status = pjmedia_session_dial_dtmf(pjsua.calls[current_call].session, 0, 
 						   &digits);
 		if (status != PJ_SUCCESS) {
@@ -522,5 +598,5 @@
 	    if (result.nb_result != NO_NB) {
 		if (result.nb_result == -1) {
-		    unsigned i;
+		    int i;
 		    for (i=0; i<pjsua.buddy_cnt; ++i)
 			pjsua.buddies[i].monitor = (menuin[0]=='s');
@@ -529,5 +605,5 @@
 		}
 
-		pjsua_pres_refresh();
+		pjsua_pres_refresh(current_acc);
 
 	    } else if (result.uri_result) {
@@ -544,5 +620,5 @@
 		 * Re-Register.
 		 */
-		pjsua_regc_update(PJ_TRUE);
+		pjsua_regc_update(current_acc, PJ_TRUE);
 		break;
 	    case 'u':
@@ -550,5 +626,5 @@
 		 * Unregister
 		 */
-		pjsua_regc_update(PJ_FALSE);
+		pjsua_regc_update(current_acc, PJ_FALSE);
 		break;
 	    }
@@ -556,6 +632,7 @@
 	    
 	case 't':
-	    pjsua.online_status = !pjsua.online_status;
-	    pjsua_pres_refresh();
+	    pjsua.acc[current_acc].online_status = 
+		!pjsua.acc[current_acc].online_status;
+	    pjsua_pres_refresh(current_acc);
 	    break;
 
@@ -607,5 +684,18 @@
 
 	case 'd':
-	    pjsua_dump();
+	    if (menuin[1] == 'c') {
+		char settings[2000];
+		int len;
+
+		len = pjsua_dump_settings(settings, sizeof(settings));
+		if (len < 1)
+		    PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
+		else
+		    PJ_LOG(3,(THIS_FILE, 
+			      "Dumping configuration (%d bytes):\n%s\n",
+			      len, settings));
+	    } else {
+		pjsua_dump();
+	    }
 	    break;
 
@@ -766,5 +856,4 @@
 
     /* Init default settings. */
-
     pjsua_default();
 
@@ -772,5 +861,4 @@
     /* Initialize pjsua (to create pool etc).
      */
-
     if (pjsua_init() != PJ_SUCCESS)
 	return 1;
@@ -778,5 +866,4 @@
 
     /* Parse command line arguments: */
-
     if (pjsua_parse_args(argc, argv) != PJ_SUCCESS)
 	return 1;
@@ -784,5 +871,4 @@
 
     /* Init logging: */
-
     app_logging_init();
 
@@ -791,10 +877,8 @@
      * messages.
      */
-
     pjsip_endpt_register_module(pjsua.endpt, &console_msg_logger);
 
 
     /* Start pjsua! */
-
     if (pjsua_start() != PJ_SUCCESS) {
 
@@ -805,25 +889,16 @@
 
     /* Sleep for a while, let any messages get printed to console: */
-
     pj_thread_sleep(500);
 
 
-    /* No current call initially: */
-
-    inv_session = &pjsua.inv_list;
-
-
     /* Start UI console main loop: */
-
     ui_console_main();
 
 
     /* Destroy pjsua: */
-
     pjsua_destroy();
 
 
     /* Close logging: */
-
     app_logging_shutdown();
 
Index: /pjproject/trunk/pjsip/src/pjsua/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua.h	(revision 235)
+++ /pjproject/trunk/pjsip/src/pjsua/pjsua.h	(revision 236)
@@ -48,28 +48,60 @@
  * Max buddies in buddy list.
  */
-#define PJSUA_MAX_BUDDIES   32
+#ifndef PJSUA_MAX_BUDDIES
+#   define PJSUA_MAX_BUDDIES	    32
+#endif
+
 
 /**
  * Max simultaneous calls.
  */
-#define PJSUA_MAX_CALLS	    8
+#ifndef PJSUA_MAX_CALLS
+#   define PJSUA_MAX_CALLS	    256
+#endif
+
+
+/**
+ * Aditional ports to be allocated in the conference ports for non-call
+ * streams.
+ */
+#define PJSUA_CONF_MORE_PORTS	    2
+
+
+/**
+ * Maximum accounts.
+ */
+#ifndef PJSUA_MAX_ACC
+#   define PJSUA_MAX_ACC	    8
+#endif
+
+
+/**
+ * Maximum credentials.
+ */
+#ifndef PJSUA_MAX_CRED
+#   define PJSUA_MAX_CRED	    PJSUA_MAX_ACC
+#endif
 
 
 /** 
- * Structure to be attached to all dialog. 
+ * 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_inv_data
+struct pjsua_call
 {
-    PJ_DECL_LIST_MEMBER(struct pjsua_inv_data);
-
+    unsigned		 index;	    /**< Index in pjsua array.		    */
     pjsip_inv_session	*inv;	    /**< The invite session.		    */
+    int			 acc_index; /**< Account index being used.	    */
     pjmedia_session	*session;   /**< The media session.		    */
     unsigned		 conf_slot; /**< Slot # in conference bridge.	    */
-    unsigned		 call_slot; /**< RTP media index in med_sock_use[]  */
     pjsip_evsub		*xfer_sub;  /**< Xfer server subscription, if this
 					 call was triggered by xfer.	    */
+    pjmedia_sock_info	 skinfo;    /**< Preallocated media sockets.	    */
+
+    void		*app_data;  /**< Application data.		    */
 };
+
+typedef struct pjsua_call pjsua_call;
 
 
@@ -80,4 +112,5 @@
 {
     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	*/
@@ -101,4 +134,34 @@
 
 
+/**
+ * Account
+ */
+struct pjsua_acc
+{
+    int		     index;	    /**< Index in accounts array.	*/
+    pj_str_t	     local_uri;	    /**< Uri in From: header.		*/
+    pj_str_t	     user_part;	    /**< User part of local URI.	*/
+    pj_str_t	     host_part;	    /**< Host part of local URI.	*/
+    pj_str_t	     contact_uri;   /**< Uri in Contact: header.	*/
+
+    pj_str_t	     reg_uri;	    /**< Registrar URI.			*/
+    pjsip_regc	    *regc;	    /**< Client registration session.   */
+    pj_int32_t	     reg_timeout;   /**< Default timeout.		*/
+    pj_timer_entry   reg_timer;	    /**< Registration timer.		*/
+    pj_status_t	     reg_last_err;  /**< Last registration error.	*/
+    int		     reg_last_code; /**< Last status last register.	*/
+
+    pj_str_t	     proxy;	    /**< Proxy URL.			*/
+    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.		*/
+};
+
+
+typedef struct pjsua_acc pjsua_acc;
+
 
 /* PJSUA application variables. */
@@ -106,5 +169,4 @@
 {
     /* Control: */
-
     pj_caching_pool  cp;	    /**< Global pool factory.		*/
     pjsip_endpoint  *endpt;	    /**< Global endpoint.		*/
@@ -114,53 +176,29 @@
 
     /* Media:  */
-
     pjmedia_endpt   *med_endpt;	    /**< Media endpoint.		*/
-    unsigned	     max_ports;	    /**< Max ports in conf.		*/
     pjmedia_conf    *mconf;	    /**< Media conference.		*/
     pj_bool_t	     null_audio;    /**< Null audio flag.		*/
     char	    *wav_file;	    /**< WAV file name to play.		*/
     unsigned	     wav_slot;	    /**< WAV player slot in bridge	*/
+    pj_bool_t	     auto_play;	    /**< Auto play file for calls?	*/
+    pj_bool_t	     auto_loop;	    /**< Auto loop RTP stream?		*/
+    pj_bool_t	     auto_conf;	    /**< Auto put to conference?	*/
+
 
     /* User Agent behaviour: */
-
     int		     auto_answer;   /**< Automatically answer in calls.	*/
 
-
-    /* Since we support simultaneous calls, we need to have multiple
-     * RTP sockets.
-     */
-    pjmedia_sock_info med_sock_info[PJSUA_MAX_CALLS];
-    pj_bool_t	      med_sock_use[PJSUA_MAX_CALLS];
-
-    /* User info: */
-
-    pj_str_t	     local_uri;	    /**< Uri in From: header.		*/
-    pj_str_t	     contact_uri;   /**< Uri in Contact: header.	*/
-
-    /* Proxy URLs: */
-
-    pj_str_t	     proxy;
-    pj_str_t	     outbound_proxy;
-    pjsip_route_hdr  route_set;
-
-
-    /* Registration: */
-
-    pj_str_t	     registrar_uri;
-    pjsip_regc	    *regc;
-    pj_int32_t	     reg_timeout;
-    pj_timer_entry   regc_timer;
-    pj_status_t	     regc_last_err; /**< Last registration error.	*/
-    int		     regc_last_code;/**< Last status last register.	*/
+    /* Account: */
+    int		     acc_cnt;	    /**< Number of client registrations	*/
+    pjsua_acc	     acc[PJSUA_MAX_ACC];    /** Client regs array.	*/
 
 
     /* Authentication credentials: */
 
-    unsigned	     cred_count;
-    pjsip_cred_info  cred_info[4];
+    int		     cred_count;    /**< Number of credentials.		*/
+    pjsip_cred_info  cred_info[10]; /**< Array of credentials.		*/
 
 
     /* Threading (optional): */
-
     int		     thread_cnt;    /**< Thread count.			*/
     pj_thread_t	    *threads[8];    /**< Thread instances.		*/
@@ -168,13 +206,12 @@
 
     /* Transport (UDP): */
-
     pj_uint16_t	     sip_port;	    /**< SIP signaling port.		*/
     pj_sock_t	     sip_sock;	    /**< SIP UDP socket.		*/
     pj_sockaddr_in   sip_sock_name; /**< Public/STUN UDP socket addr.	*/
 
+    pj_str_t	     outbound_proxy;/**< Outbound proxy.		*/
 
 
     /* STUN: */
-
     pj_str_t	     stun_srv1;
     int		     stun_port1;
@@ -183,6 +220,5 @@
 
 
-    /* Logging: */
-    
+    /* Logging: */    
     int		     log_level;	    /**< Logging verbosity.		*/
     int		     app_log_level; /**< stdout log verbosity.		*/
@@ -191,16 +227,13 @@
 
 
-    /* List of invite sessions: */
-
-    struct pjsua_inv_data inv_list;
+    /* PJSUA Calls: */
+    int		     max_calls;	    /**< Max nb of calls.		*/
+    int		     call_cnt;	    /**< Number of calls.		*/
+    pjsua_call	     calls[PJSUA_MAX_CALLS];	/** Calls array.	*/
 
 
     /* SIMPLE and buddy status: */
-
-    pj_bool_t	    online_status;  /**< Out online status.		*/
-    pjsua_srv_pres  pres_srv_list;  /**< Server subscription list.	*/
-
-    unsigned	    buddy_cnt;
-    pjsua_buddy	    buddies[PJSUA_MAX_BUDDIES];
+    int		     buddy_cnt;
+    pjsua_buddy	     buddies[PJSUA_MAX_BUDDIES];
 };
 
@@ -256,13 +289,31 @@
 
 
-/*****************************************************************************
- * PJSUA Invite session API (defined in pjsua_inv.c).
- */
+/**
+ * Find account for incoming request.
+ */
+int pjsua_find_account_for_incoming(pjsip_rx_data *rdata);
+
+
+/**
+ * Find account for outgoing request.
+ */
+int pjsua_find_account_for_outgoing(const pj_str_t *url);
+
+
+/*****************************************************************************
+ * PJSUA Call API (defined in pjsua_call.c).
+ */
+
+/**
+ * Init pjsua call module.
+ */
+pj_status_t pjsua_call_init(void);
 
 /**
  * Make outgoing call.
  */
-pj_status_t pjsua_invite(const char *cstr_dest_uri,
-			 struct pjsua_inv_data **p_inv_data);
+pj_status_t pjsua_make_call(int acc_index,
+			    const char *cstr_dest_uri,
+			    int *p_call_index);
 
 
@@ -270,11 +321,16 @@
  * Handle incoming invite request.
  */
-pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata);
-
+pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata);
+
+
+/**
+ * Answer call.
+ */
+void pjsua_call_answer(int call_index, int code);
 
 /**
  * Hangup call.
  */
-void pjsua_inv_hangup(struct pjsua_inv_data *inv_session, int code);
+void pjsua_call_hangup(int call_index, int code);
 
 
@@ -282,5 +338,5 @@
  * Put call on-hold.
  */
-void pjsua_inv_set_hold(struct pjsua_inv_data *inv_session);
+void pjsua_call_set_hold(int call_index);
 
 
@@ -288,5 +344,5 @@
  * Send re-INVITE (to release hold).
  */
-void pjsua_inv_reinvite(struct pjsua_inv_data *inv_session);
+void pjsua_call_reinvite(int call_index);
 
 
@@ -294,41 +350,6 @@
  * Transfer call.
  */
-void pjsua_inv_xfer_call(struct pjsua_inv_data *inv_session,
-			 const char *dest);
-
-
-/**
- * Callback to be called by session when invite session's state has changed.
- */
-void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e);
-
-
-/**
- * Callback to be called by session when outgoing dialog has forked.
- * This function will create a forked dialog.
- */
-void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e);
-
-
-/**
- * Callback to be called when SDP offer/answer negotiation has just completed
- * in the session. This function will start/update media if negotiation
- * has succeeded.
- */
-void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status);
-
-/**
- * Callback called when invite session received new offer.
- */
-void pjsua_inv_on_rx_offer( pjsip_inv_session *inv,
-			    const pjmedia_sdp_session *offer);
-
-/**
- * Callback to receive transaction state inside invite session or dialog
- * (e.g. REFER, MESSAGE).
- */
-void pjsua_inv_on_tsx_state_changed(pjsip_inv_session *inv,
-				    pjsip_transaction *tsx,
-				    pjsip_event *e);
+void pjsua_call_xfer(int call_index, const char *dest);
+
 
 /**
@@ -347,5 +368,5 @@
  * @param app_callback	Optional callback
  */
-pj_status_t pjsua_regc_init(void);
+pj_status_t pjsua_regc_init(int acc_index);
 
 /**
@@ -353,5 +374,5 @@
  * this will start unregistration process.
  */
-void pjsua_regc_update(pj_bool_t renew);
+void pjsua_regc_update(int acc_index, pj_bool_t renew);
 
 
@@ -370,5 +391,5 @@
  * Refresh both presence client and server subscriptions.
  */
-void pjsua_pres_refresh(void);
+void pjsua_pres_refresh(int acc_index);
 
 /**
@@ -393,10 +414,10 @@
  * Notify UI when invite state has changed.
  */
-void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e);
+void pjsua_ui_inv_on_state_changed(int call_index, pjsip_event *e);
 
 /**
  * Notify UI when registration status has changed.
  */
-void pjsua_ui_regc_on_state_changed(int code);
+void pjsua_ui_regc_on_state_changed(int acc_index);
 
 
@@ -418,4 +439,9 @@
  */
 pj_status_t pjsua_load_settings(const char *filename);
+
+/**
+ * Dump settings.
+ */
+int pjsua_dump_settings(char *buf, pj_size_t max);
 
 /**
Index: /pjproject/trunk/pjsip/src/pjsua/pjsua_call.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua_call.c	(revision 236)
+++ /pjproject/trunk/pjsip/src/pjsua/pjsua_call.c	(revision 236)
@@ -0,0 +1,1090 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * 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 "pjsua.h"
+#include <pj/log.h>
+
+
+/*
+ * pjsua_inv.c
+ *
+ * Invite session specific functionalities.
+ */
+
+#define THIS_FILE   "pjsua_inv.c"
+
+
+/**
+ * Make outgoing call.
+ */
+pj_status_t pjsua_make_call(int acc_index,
+			    const char *cstr_dest_uri,
+			    int *p_call_index)
+{
+    pj_str_t dest_uri;
+    pjsip_dialog *dlg;
+    pjmedia_sdp_session *offer;
+    pjsip_inv_session *inv;
+    int call_index = -1;
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    /* Convert cstr_dest_uri to dest_uri */
+    
+    dest_uri = pj_str((char*)cstr_dest_uri);
+
+    /* Find free call slot. */
+    for (call_index=0; call_index<pjsua.max_calls; ++call_index) {
+	if (pjsua.calls[call_index].inv == NULL)
+	    break;
+    }
+
+    if (call_index == pjsua.max_calls) {
+	PJ_LOG(3,(THIS_FILE, "Error: too many calls!"));
+	return PJ_ETOOMANY;
+    }
+
+    /* Create outgoing dialog: */
+
+    status = pjsip_dlg_create_uac( pjsip_ua_instance(), 
+				   &pjsua.acc[acc_index].local_uri,
+				   &pjsua.acc[acc_index].contact_uri, 
+				   &dest_uri, &dest_uri,
+				   &dlg);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Dialog creation failed", status);
+	return status;
+    }
+
+    /* Get media capability from media endpoint: */
+
+    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool, 1, 
+				       &pjsua.calls[call_index].skinfo, 
+				       &offer);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
+	goto on_error;
+    }
+
+    /* Create the INVITE session: */
+
+    status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Invite session creation failed", status);
+	goto on_error;
+    }
+
+
+    /* Create and associate our data in the session. */
+
+    pjsua.calls[call_index].inv = inv;
+
+    dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
+    inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
+
+
+    /* Set dialog Route-Set: */
+
+    if (!pj_list_empty(&pjsua.acc[acc_index].route_set))
+	pjsip_dlg_set_route_set(dlg, &pjsua.acc[acc_index].route_set);
+
+
+    /* Set credentials: */
+
+    pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count, 
+				    pjsua.cred_info);
+
+
+    /* Create initial INVITE: */
+
+    status = pjsip_inv_invite(inv, &tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create initial INVITE request", 
+		     status);
+	goto on_error;
+    }
+
+
+    /* Send initial INVITE: */
+
+    status = pjsip_inv_send_msg(inv, tdata, NULL);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to send initial INVITE request", 
+		     status);
+	goto on_error;
+    }
+
+
+    /* Done. */
+
+    ++pjsua.call_cnt;
+
+    if (p_call_index)
+	*p_call_index = call_index;
+
+    return PJ_SUCCESS;
+
+
+on_error:
+    PJ_TODO(DESTROY_DIALOG_ON_FAIL);
+    if (call_index != -1) {
+	pjsua.calls[call_index].inv = NULL;
+    }
+    return status;
+}
+
+
+/**
+ * Handle incoming INVITE request.
+ */
+pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
+{
+    pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
+    pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
+    pjsip_msg *msg = rdata->msg_info.msg;
+    pjsip_tx_data *response = NULL;
+    unsigned options = 0;
+    pjsip_inv_session *inv;
+    int acc_index;
+    int call_index = -1;
+    pjmedia_sdp_session *answer;
+    pj_status_t status;
+
+    /* Don't want to handle anything but INVITE */
+    if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
+	return PJ_FALSE;
+
+    /* Don't want to handle anything that's already associated with
+     * existing dialog or transaction.
+     */
+    if (dlg || tsx)
+	return PJ_FALSE;
+
+
+    /* Verify that we can handle the request. */
+    status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
+				      pjsua.endpt, &response);
+    if (status != PJ_SUCCESS) {
+
+	/*
+	 * No we can't handle the incoming INVITE request.
+	 */
+
+	if (response) {
+	    pjsip_response_addr res_addr;
+
+	    pjsip_get_response_addr(response->pool, rdata, &res_addr);
+	    pjsip_endpt_send_response(pjsua.endpt, &res_addr, response, 
+				      NULL, NULL);
+
+	} else {
+
+	    /* Respond with 500 (Internal Server Error) */
+	    pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
+					  NULL, NULL);
+	}
+
+	return PJ_TRUE;
+    } 
+
+
+    /*
+     * Yes we can handle the incoming INVITE request.
+     */
+
+    /* Find free call slot. */
+    for (call_index=0; call_index < pjsua.max_calls; ++call_index) {
+	if (pjsua.calls[call_index].inv == NULL)
+	    break;
+    }
+
+    if (call_index == PJSUA_MAX_CALLS) {
+	pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 
+				      PJSIP_SC_BUSY_HERE, NULL,
+				      NULL, NULL);
+	return PJ_TRUE;
+    }
+
+
+    /* Get media capability from media endpoint: */
+
+    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool, 1,
+				       &pjsua.calls[call_index].skinfo, 
+				       &answer );
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
+				      NULL, NULL);
+
+	return PJ_TRUE;
+    }
+
+    /* TODO: 
+     *
+     * Get which account is most likely to be associated with this incoming
+     * call. We need the account to find which contact URI to put for
+     * the call.
+     */
+    acc_index = 0;
+
+    /* Create dialog: */
+
+    status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
+				   &pjsua.acc[acc_index].contact_uri, 
+				   &dlg);
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
+				      NULL, NULL);
+
+	return PJ_TRUE;
+    }
+
+
+    /* Create invite session: */
+
+    status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
+    if (status != PJ_SUCCESS) {
+
+	pjsip_dlg_respond(dlg, rdata, 500, NULL);
+
+	// TODO: Need to delete dialog
+	return PJ_TRUE;
+    }
+
+
+    /* Create and attach pjsua data to the dialog: */
+
+    pjsua.calls[call_index].inv = inv;
+
+    dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
+    inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
+
+
+    /* Must answer with some response to initial INVITE.
+     * If auto-answer flag is set, send 200 straight away, otherwise send 100.
+     */
+    
+    status = pjsip_inv_initial_answer(inv, rdata, 
+				      (pjsua.auto_answer ? 200 : 100), 
+				      NULL, NULL, &response);
+    if (status != PJ_SUCCESS) {
+	
+	pjsua_perror(THIS_FILE, "Unable to create 100 response", status);
+
+	pjsip_dlg_respond(dlg, rdata, 500, NULL);
+
+	// TODO: Need to delete dialog
+
+    } else {
+	status = pjsip_inv_send_msg(inv, response, NULL);
+	if (status != PJ_SUCCESS)
+	    pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
+    }
+
+    if (pjsua.auto_answer < 200) {
+	PJ_LOG(3,(THIS_FILE,
+		  "\nIncoming call!!\n"
+		  "From: %.*s\n"
+		  "To:   %.*s\n"
+		  "(press 'a' to answer, 'h' to decline)",
+		  (int)dlg->remote.info_str.slen,
+		  dlg->remote.info_str.ptr,
+		  (int)dlg->local.info_str.slen,
+		  dlg->local.info_str.ptr));
+    } else {
+	PJ_LOG(3,(THIS_FILE,
+		  "Call From:%.*s To:%.*s was answered with %d (%s)",
+		  (int)dlg->remote.info_str.slen,
+		  dlg->remote.info_str.ptr,
+		  (int)dlg->local.info_str.slen,
+		  dlg->local.info_str.ptr,
+		  pjsua.auto_answer,
+		  pjsip_get_status_text(pjsua.auto_answer)->ptr ));
+    }
+
+    ++pjsua.call_cnt;
+
+    /* This INVITE request has been handled. */
+    return PJ_TRUE;
+}
+
+
+/*
+ * This callback receives notification from invite session when the
+ * session state has changed.
+ */
+static void pjsua_call_on_state_changed(pjsip_inv_session *inv, 
+					pjsip_event *e)
+{
+    pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
+
+    /* If this is an outgoing INVITE that was created because of
+     * REFER/transfer, send NOTIFY to transferer.
+     */
+    if (call && call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE)  {
+	int st_code = -1;
+	pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
+	
+
+	switch (call->inv->state) {
+	case PJSIP_INV_STATE_NULL:
+	case PJSIP_INV_STATE_CALLING:
+	    /* Do nothing */
+	    break;
+
+	case PJSIP_INV_STATE_EARLY:
+	case PJSIP_INV_STATE_CONNECTING:
+	    st_code = e->body.tsx_state.tsx->status_code;
+	    ev_state = PJSIP_EVSUB_STATE_ACTIVE;
+	    break;
+
+	case PJSIP_INV_STATE_CONFIRMED:
+	    /* When state is confirmed, send the final 200/OK and terminate
+	     * subscription.
+	     */
+	    st_code = e->body.tsx_state.tsx->status_code;
+	    ev_state = PJSIP_EVSUB_STATE_TERMINATED;
+	    break;
+
+	case PJSIP_INV_STATE_DISCONNECTED:
+	    st_code = e->body.tsx_state.tsx->status_code;
+	    ev_state = PJSIP_EVSUB_STATE_TERMINATED;
+	    break;
+	}
+
+	if (st_code != -1) {
+	    pjsip_tx_data *tdata;
+	    pj_status_t status;
+
+	    status = pjsip_xfer_notify( call->xfer_sub,
+					ev_state, st_code,
+					NULL, &tdata);
+	    if (status != PJ_SUCCESS) {
+		pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
+	    } else {
+		status = pjsip_xfer_send_request(call->xfer_sub, tdata);
+		if (status != PJ_SUCCESS) {
+		    pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
+		}
+	    }
+	}
+    }
+
+
+    pjsua_ui_inv_on_state_changed(call->index, e);
+
+    /* call->inv may be NULL now */
+
+    /* Destroy media session when invite session is disconnected. */
+    if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+
+	pj_assert(call != NULL);
+
+	if (call && call->session) {
+	    pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot);
+	    pjmedia_session_destroy(call->session);
+	    call->session = NULL;
+
+	    PJ_LOG(3,(THIS_FILE,"Media session is destroyed"));
+	}
+
+	call->inv = NULL;
+	--pjsua.call_cnt;
+    }
+}
+
+
+/*
+ * Callback called by event framework when the xfer subscription state
+ * has changed.
+ */
+static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
+{
+    
+    PJ_UNUSED_ARG(event);
+
+    /*
+     * We're only interested when subscription is terminated, to 
+     * clear the xfer_sub member of the inv_data.
+     */
+    if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+	pjsua_call *call;
+
+	call = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
+	if (!call)
+	    return;
+
+	pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
+	call->xfer_sub = NULL;
+
+	PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
+    }
+}
+
+
+/*
+ * Follow transfer (REFER) request.
+ */
+static void on_call_transfered( pjsip_inv_session *inv,
+			        pjsip_rx_data *rdata )
+{
+    pj_status_t status;
+    pjsip_tx_data *tdata;
+    pjsua_call *existing_call;
+    int new_call;
+    const pj_str_t str_refer_to = { "Refer-To", 8};
+    pjsip_generic_string_hdr *refer_to;
+    char *uri;
+    struct pjsip_evsub_user xfer_cb;
+    pjsip_evsub *sub;
+
+    existing_call = inv->dlg->mod_data[pjsua.mod.id];
+
+    /* Find the Refer-To header */
+    refer_to = (pjsip_generic_string_hdr*)
+	pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
+
+    if (refer_to == NULL) {
+	/* Invalid Request.
+	 * No Refer-To header!
+	 */
+	PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
+	pjsip_dlg_respond( inv->dlg, rdata, 400, NULL);
+	return;
+    }
+
+    PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
+	      (int)inv->dlg->remote.info_str.slen,
+	      inv->dlg->remote.info_str.ptr,
+	      (int)refer_to->hvalue.slen, 
+	      refer_to->hvalue.ptr));
+
+    /* Init callback */
+    pj_memset(&xfer_cb, 0, sizeof(xfer_cb));
+    xfer_cb.on_evsub_state = &xfer_on_evsub_state;
+
+    /* Create transferee event subscription */
+    status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
+	pjsip_dlg_respond( inv->dlg, rdata, 500, NULL);
+	return;
+    }
+
+    /* Accept the REFER request, send 200 (OK). */
+    pjsip_xfer_accept(sub, rdata, 200, NULL);
+
+    /* Create initial NOTIFY request */
+    status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
+				100, NULL, &tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
+	return;
+    }
+
+    /* Send initial NOTIFY request */
+    status = pjsip_xfer_send_request( sub, tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
+	return;
+    }
+
+    /* We're cheating here.
+     * We need to get a null terminated string from a pj_str_t.
+     * So grab the pointer from the hvalue and NULL terminate it, knowing
+     * that the NULL position will be occupied by a newline. 
+     */
+    uri = refer_to->hvalue.ptr;
+    uri[refer_to->hvalue.slen] = '\0';
+
+    /* Now make the outgoing call. */
+    status = pjsua_make_call(existing_call->acc_index, uri, &new_call);
+    if (status != PJ_SUCCESS) {
+
+	/* Notify xferer about the error */
+	status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
+				   500, NULL, &tdata);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", 
+			  status);
+	    return;
+	}
+	status = pjsip_xfer_send_request(sub, tdata);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", 
+			  status);
+	    return;
+	}
+	return;
+    }
+
+    /* Put the server subscription in inv_data.
+     * Subsequent state changed in pjsua_inv_on_state_changed() will be
+     * reported back to the server subscription.
+     */
+    pjsua.calls[new_call].xfer_sub = sub;
+
+    /* Put the invite_data in the subscription. */
+    pjsip_evsub_set_mod_data(sub, pjsua.mod.id, &pjsua.calls[new_call]);
+}
+
+
+/*
+ * This callback is called when transaction state has changed in INVITE
+ * session. We use this to trap incoming REFER request.
+ */
+static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
+					    pjsip_transaction *tsx,
+					    pjsip_event *e)
+{
+    pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
+
+    if (tsx->role==PJSIP_ROLE_UAS &&
+	tsx->state==PJSIP_TSX_STATE_TRYING &&
+	pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
+    {
+	/*
+	 * Incoming REFER request.
+	 */
+	on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
+    }
+}
+
+
+/*
+ * This callback is called by invite session framework when UAC session
+ * has forked.
+ */
+static void pjsua_call_on_forked( pjsip_inv_session *inv, 
+				  pjsip_event *e)
+{
+    PJ_UNUSED_ARG(inv);
+    PJ_UNUSED_ARG(e);
+
+    PJ_TODO(HANDLE_FORKED_DIALOG);
+}
+
+
+/*
+ * Create inactive SDP for call hold.
+ */
+static pj_status_t create_inactive_sdp(pjsua_call *call,
+				       pjmedia_sdp_session **p_answer)
+{
+    pj_status_t status;
+    pjmedia_sdp_conn *conn;
+    pjmedia_sdp_attr *attr;
+    pjmedia_sdp_session *sdp;
+
+    /* Create new offer */
+    status = pjmedia_endpt_create_sdp(pjsua.med_endpt, pjsua.pool, 1,
+				      &call->skinfo, &sdp);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
+	return status;
+    }
+
+    /* Get SDP media connection line */
+    conn = sdp->media[0]->conn;
+    if (!conn)
+	conn = sdp->conn;
+
+    /* Modify address */
+    conn->addr = pj_str("0.0.0.0");
+
+    /* Remove existing directions attributes */
+    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
+    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
+    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
+    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
+
+    /* Add inactive attribute */
+    attr = pjmedia_sdp_attr_create(pjsua.pool, "inactive", NULL);
+    pjmedia_sdp_media_add_attr(sdp->media[0], attr);
+
+    *p_answer = sdp;
+
+    return status;
+}
+
+/*
+ * Called when session received new offer.
+ */
+static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
+				   const pjmedia_sdp_session *offer)
+{
+    pjsua_call *call;
+    pjmedia_sdp_conn *conn;
+    pjmedia_sdp_session *answer;
+    pj_bool_t is_remote_active;
+    pj_status_t status;
+
+    call = inv->dlg->mod_data[pjsua.mod.id];
+
+    /*
+     * See if remote is offering active media (i.e. not on-hold)
+     */
+    is_remote_active = PJ_TRUE;
+
+    conn = offer->media[0]->conn;
+    if (!conn)
+	conn = offer->conn;
+
+    if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
+	pj_strcmp2(&conn->addr, "0")==0)
+    {
+	is_remote_active = PJ_FALSE;
+
+    } 
+    else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
+    {
+	is_remote_active = PJ_FALSE;
+    }
+
+    PJ_LOG(4,(THIS_FILE, "Received SDP offer, remote media is %s",
+	      (is_remote_active ? "active" : "inactive")));
+
+    /* Supply candidate answer */
+    if (is_remote_active) {
+	status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
+					   &call->skinfo, &answer);
+    } else {
+	status = create_inactive_sdp( call, &answer );
+    }
+
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
+	return;
+    }
+
+    status = pjsip_inv_set_sdp_answer(call->inv, answer);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to set answer", status);
+	return;
+    }
+
+}
+
+
+/*
+ * Callback to be called when SDP offer/answer negotiation has just completed
+ * in the session. This function will start/update media if negotiation
+ * has succeeded.
+ */
+static void pjsua_call_on_media_update(pjsip_inv_session *inv,
+				       pj_status_t status)
+{
+    pjsua_call *call;
+    const pjmedia_sdp_session *local_sdp;
+    const pjmedia_sdp_session *remote_sdp;
+    pjmedia_port *media_port;
+    pj_str_t port_name;
+    char tmp[PJSIP_MAX_URL_SIZE];
+
+    call = inv->dlg->mod_data[pjsua.mod.id];
+
+    if (status != PJ_SUCCESS) {
+
+	pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
+	return;
+
+    }
+
+    /* Destroy existing media session, if any. */
+
+    if (call && call->session) {
+	pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot);
+	pjmedia_session_destroy(call->session);
+	call->session = NULL;
+    }
+
+    /* Get local and remote SDP */
+
+    status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, 
+		     "Unable to retrieve currently active local SDP", 
+		     status);
+	return;
+    }
+
+
+    status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, 
+		     "Unable to retrieve currently active remote SDP", 
+		     status);
+	return;
+    }
+
+    /* Create new media session. 
+     * The media session is active immediately.
+     */
+    if (pjsua.null_audio)
+	return;
+    
+    status = pjmedia_session_create( pjsua.med_endpt, 1, 
+				     &call->skinfo,
+				     local_sdp, remote_sdp, 
+				     call,
+				     &call->session );
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create media session", 
+		     status);
+	return;
+    }
+
+
+    /* Get the port interface of the first stream in the session.
+     * We need the port interface to add to the conference bridge.
+     */
+    pjmedia_session_get_port(call->session, 0, &media_port);
+
+
+    /*
+     * Add the call to conference bridge.
+     */
+    port_name.ptr = tmp;
+    port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
+				     call->inv->dlg->remote.info->uri,
+				     tmp, sizeof(tmp));
+    if (port_name.slen < 1) {
+	port_name = pj_str("call");
+    }
+    status = pjmedia_conf_add_port( pjsua.mconf, call->inv->pool,
+				    media_port, 
+				    &port_name,
+				    &call->conf_slot);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create conference slot", 
+		     status);
+	pjmedia_session_destroy(call->session);
+	call->session = NULL;
+	return;
+    }
+
+    /* If auto-play is configured, connect the call to the file player 
+     * port 
+     */
+    if (pjsua.auto_play && pjsua.wav_file && 
+	call->inv->role == PJSIP_ROLE_UAS) 
+    {
+
+	pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot, 
+				   call->conf_slot);
+
+    } else if (pjsua.auto_loop && call->inv->role == PJSIP_ROLE_UAS) {
+
+	pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 
+				   call->conf_slot);
+
+    } else if (pjsua.auto_conf) {
+
+	int i;
+
+	pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot);
+	pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0);
+
+	for (i=0; i < pjsua.max_calls; ++i) {
+
+	    if (!pjsua.calls[i].session)
+		continue;
+
+	    pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 
+				       pjsua.calls[i].conf_slot);
+	    pjmedia_conf_connect_port( pjsua.mconf, pjsua.calls[i].conf_slot,
+				       call->conf_slot);
+	}
+
+    } else {
+
+	/* Connect new call to the sound device port (port zero) in the
+	 * main conference bridge.
+	 */
+	pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot);
+	pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0);
+    }
+
+
+    /* Done. */
+    {
+	struct pjmedia_session_info sess_info;
+	char info[80];
+	int info_len = 0;
+	unsigned i;
+
+	pjmedia_session_get_info(call->session, &sess_info);
+	for (i=0; i<sess_info.stream_cnt; ++i) {
+	    int len;
+	    const char *dir;
+	    pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
+
+	    switch (strm_info->dir) {
+	    case PJMEDIA_DIR_NONE:
+		dir = "inactive";
+		break;
+	    case PJMEDIA_DIR_ENCODING:
+		dir = "sendonly";
+		break;
+	    case PJMEDIA_DIR_DECODING:
+		dir = "recvonly";
+		break;
+	    case PJMEDIA_DIR_ENCODING_DECODING:
+		dir = "sendrecv";
+		break;
+	    default:
+		dir = "unknown";
+		break;
+	    }
+	    len = pj_ansi_sprintf( info+info_len,
+				   ", stream #%d: %.*s (%s)", i,
+				   (int)strm_info->fmt.encoding_name.slen,
+				   (int)strm_info->fmt.encoding_name.ptr,
+				   dir);
+	    if (len > 0)
+		info_len += len;
+	}
+	PJ_LOG(3,(THIS_FILE,"Media started%s", info));
+    }
+}
+
+
+/*
+ * Hangup call.
+ */
+void pjsua_call_hangup(int call_index, int code)
+{
+    pjsua_call *call;
+    pj_status_t status;
+    pjsip_tx_data *tdata;
+
+
+    call = &pjsua.calls[call_index];
+
+    if (!call->inv) {
+	PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+	return;
+    }
+
+    status = pjsip_inv_end_session(call->inv, code, NULL, &tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, 
+		     "Failed to create end session message", 
+		     status);
+	return;
+    }
+
+    /* pjsip_inv_end_session may return PJ_SUCCESS with NULL 
+     * as p_tdata when INVITE transaction has not been answered
+     * with any provisional responses.
+     */
+    if (tdata == NULL)
+	return;
+
+    status = pjsip_inv_send_msg(call->inv, tdata, NULL);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, 
+		     "Failed to send end session message", 
+		     status);
+	return;
+    }
+}
+
+
+/*
+ * Put call on-Hold.
+ */
+void pjsua_call_set_hold(int call_index)
+{
+    pjmedia_sdp_session *sdp;
+    pjsua_call *call;
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    call = &pjsua.calls[call_index];
+    
+    if (!call->inv) {
+	PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+	return;
+    }
+
+    if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
+	PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
+	return;
+    }
+
+    status = create_inactive_sdp(call, &sdp);
+    if (status != PJ_SUCCESS)
+	return;
+
+    /* Send re-INVITE with new offer */
+    status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
+	return;
+    }
+
+    status = pjsip_inv_send_msg( call->inv, tdata, NULL);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
+	return;
+    }
+}
+
+
+/*
+ * re-INVITE.
+ */
+void pjsua_call_reinvite(int call_index)
+{
+    pjmedia_sdp_session *sdp;
+    pjsip_tx_data *tdata;
+    pjsua_call *call;
+    pj_status_t status;
+
+    call = &pjsua.calls[call_index];
+
+    if (!call->inv) {
+	PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+	return;
+    }
+
+
+    if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
+	PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
+	return;
+    }
+
+    /* Create SDP */
+    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
+				       &call->skinfo, &sdp);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", 
+		     status);
+	return;
+    }
+
+    /* Send re-INVITE with new offer */
+    status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
+	return;
+    }
+
+    status = pjsip_inv_send_msg( call->inv, tdata, NULL);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
+	return;
+    }
+}
+
+
+/*
+ * Transfer call.
+ */
+void pjsua_call_xfer(int call_index, const char *dest)
+{
+    pjsip_evsub *sub;
+    pjsip_tx_data *tdata;
+    pjsua_call *call;
+    pj_str_t tmp;
+    pj_status_t status;
+
+    
+    call = &pjsua.calls[call_index];
+
+    if (!call->inv) {
+	PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+	return;
+    }
+   
+    /* Create xfer client subscription.
+     * We're not interested in knowing the transfer result, so we
+     * put NULL as the callback.
+     */
+    status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create xfer", status);
+	return;
+    }
+
+    /*
+     * Create REFER request.
+     */
+    status = pjsip_xfer_initiate(sub, pj_cstr(&tmp, dest), &tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
+	return;
+    }
+
+    /* Send. */
+    status = pjsip_xfer_send_request(sub, tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
+	return;
+    }
+
+    /* For simplicity (that's what this program is intended to be!), 
+     * leave the original invite session as it is. More advanced application
+     * may want to hold the INVITE, or terminate the invite, or whatever.
+     */
+}
+
+
+/*
+ * Terminate all calls.
+ */
+void pjsua_inv_shutdown()
+{
+    int i;
+
+    for (i=0; i<pjsua.max_calls; ++i) {
+	pjsip_tx_data *tdata;
+	pjsua_call *call;
+
+	if (pjsua.calls[i].inv == NULL)
+	    continue;
+
+	call = &pjsua.calls[i];
+
+	if (pjsip_inv_end_session(call->inv, 410, NULL, &tdata)==0) {
+	    if (tdata)
+		pjsip_inv_send_msg(call->inv, tdata, NULL);
+	}
+    }
+}
+
+
+pj_status_t pjsua_call_init(void)
+{
+    /* Initialize invite session callback. */
+    pjsip_inv_callback inv_cb;
+    pj_status_t status;
+
+    pj_memset(&inv_cb, 0, sizeof(inv_cb));
+    inv_cb.on_state_changed = &pjsua_call_on_state_changed;
+    inv_cb.on_new_session = &pjsua_call_on_forked;
+    inv_cb.on_media_update = &pjsua_call_on_media_update;
+    inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
+    inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
+
+
+    /* Initialize invite session module: */
+    status = pjsip_inv_usage_init(pjsua.endpt, &pjsua.mod, &inv_cb);
+    
+    return status;
+}
Index: /pjproject/trunk/pjsip/src/pjsua/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua_core.c	(revision 235)
+++ /pjproject/trunk/pjsip/src/pjsua/pjsua_core.c	(revision 236)
@@ -46,4 +46,6 @@
 void pjsua_default(void)
 {
+    unsigned i;
+
 
     /* Normally need another thread for console application, because main 
@@ -54,10 +56,8 @@
 
     /* Default transport settings: */
-
     pjsua.sip_port = 5060;
 
 
     /* Default logging settings: */
-
     pjsua.log_level = 5;
     pjsua.app_log_level = 4;
@@ -65,31 +65,27 @@
 		      PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
 
+
     /* Default: do not use STUN: */
-
     pjsua.stun_port1 = pjsua.stun_port2 = 0;
 
-    /* Default URIs: */
-
-    pjsua.local_uri = pj_str(PJSUA_LOCAL_URI);
-
-    /* Default registration timeout: */
-
-    pjsua.reg_timeout = 55;
-
-    /* Default maximum conference ports: */
-
-    pjsua.max_ports = 8;
-
-    /* Init route set list: */
-
-    pj_list_init(&pjsua.route_set);
-
-    /* Init invite session list: */
-
-    pj_list_init(&pjsua.inv_list);
+    /* Init accounts: */
+    pjsua.acc_cnt = 1;
+    for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
+	pjsua.acc[i].index = i;
+	pjsua.acc[i].local_uri = pj_str(PJSUA_LOCAL_URI);
+	pjsua.acc[i].reg_timeout = 55;
+	pj_list_init(&pjsua.acc[i].route_set);
+	pj_list_init(&pjsua.acc[i].pres_srv_list);
+    }
+
+    /* Init call array: */
+    for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i)
+	pjsua.calls[i].index = i;
+
+    /* Default max nb of calls. */
+    pjsua.max_calls = 4;
 
     /* Init server presence subscription list: */
     
-    pj_list_init(&pjsua.pres_srv_list);
 
 }
@@ -111,6 +107,5 @@
     if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
 
-	return pjsua_inv_on_incoming(rdata);
-
+	return pjsua_call_on_incoming(rdata);
     }
 
@@ -146,5 +141,5 @@
 	RTP_START_PORT = 4000,
 	RTP_RANDOM_START = 2,
-	RTP_RETRY = 20 
+	RTP_RETRY = 100
     };
     enum {
@@ -241,14 +236,17 @@
 		pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
 
-	    if (sip)
-		mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)pjsua.sip_port);
-	    mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port);
-	    mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
+	    if (sip) {
+		mapped_addr[SIP_SOCK].sin_port = 
+		    pj_htons((pj_uint16_t)pjsua.sip_port);
+	    }
+	    mapped_addr[RTP_SOCK].sin_port=pj_htons((pj_uint16_t)rtp_port);
+	    mapped_addr[RTCP_SOCK].sin_port=pj_htons((pj_uint16_t)(rtp_port+1));
 	    break;
+
 	} else {
-	    status = pj_stun_get_mapped_addr( &pjsua.cp.factory, 3, sock,
-					      &pjsua.stun_srv1, pjsua.stun_port1,
-					      &pjsua.stun_srv2, pjsua.stun_port2,
-					      mapped_addr);
+	    status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 3, sock,
+					   &pjsua.stun_srv1, pjsua.stun_port1,
+					   &pjsua.stun_srv2, pjsua.stun_port2,
+					   mapped_addr);
 	    if (status != PJ_SUCCESS) {
 		pjsua_perror(THIS_FILE, "STUN error", status);
@@ -256,14 +254,21 @@
 	    }
 
-	    if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1)
+	    if (pj_ntohs(mapped_addr[2].sin_port) == 
+		pj_ntohs(mapped_addr[1].sin_port)+1)
+	    {
 		break;
-
-	    pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
-	    pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
+	    }
+
+	    pj_sock_close(sock[RTP_SOCK]); 
+	    sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+
+	    pj_sock_close(sock[RTCP_SOCK]); 
+	    sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
 	}
     }
 
     if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) {
-	PJ_LOG(1,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination"));
+	PJ_LOG(1,(THIS_FILE, 
+		  "Unable to find appropriate RTP/RTCP ports combination"));
 	goto on_error;
     }
@@ -271,5 +276,7 @@
     if (sip) {
 	pjsua.sip_sock = sock[SIP_SOCK];
-	pj_memcpy(&pjsua.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in));
+	pj_memcpy(&pjsua.sip_sock_name, 
+		  &mapped_addr[SIP_SOCK], 
+		  sizeof(pj_sockaddr_in));
     } else {
 	pj_sock_close(sock[0]);
@@ -392,27 +399,10 @@
     /* Initialize invite session module: */
 
-    {
-	
-	/* Initialize invite session callback. */
-	pjsip_inv_callback inv_cb;
-
-	pj_memset(&inv_cb, 0, sizeof(inv_cb));
-	inv_cb.on_state_changed = &pjsua_inv_on_state_changed;
-	inv_cb.on_new_session = &pjsua_inv_on_new_session;
-	inv_cb.on_media_update = &pjsua_inv_on_media_update;
-	inv_cb.on_rx_offer = &pjsua_inv_on_rx_offer;
-	inv_cb.on_tsx_state_changed = &pjsua_inv_on_tsx_state_changed;
-
-
-	/* Initialize invite session module: */
-	status = pjsip_inv_usage_init(pjsua.endpt, &pjsua.mod, &inv_cb);
-	if (status != PJ_SUCCESS) {
-	    pjsua_perror(THIS_FILE, "Invite usage initialization error", 
-			 status);
-	    goto on_error;
-	}
-
-    }
-
+    status = pjsua_call_init();
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Invite usage initialization error", 
+		     status);
+	goto on_error;
+    }
 
     /* Done */
@@ -521,5 +511,6 @@
     /* Init conference bridge. */
 
-    status = pjmedia_conf_create(pjsua.pool, pjsua.max_ports, 
+    status = pjmedia_conf_create(pjsua.pool, 
+				 pjsua.max_calls+PJSUA_CONF_MORE_PORTS, 
 				 8000, 160, 16, &pjsua.mconf);
     if (status != PJ_SUCCESS) {
@@ -548,4 +539,65 @@
 
 
+/*
+ * Find account for incoming request.
+ */
+int pjsua_find_account_for_incoming(pjsip_rx_data *rdata)
+{
+    pjsip_uri *uri;
+    pjsip_sip_uri *sip_uri;
+    int acc_index;
+
+    uri = rdata->msg_info.to->uri;
+
+    /* Just return account #0 if To URI is not SIP: */
+    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && 
+	!PJSIP_URI_SCHEME_IS_SIPS(uri)) 
+    {
+	return 0;
+    }
+
+
+    sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
+
+    /* Find account which has matching username and domain. */
+    for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
+
+	pjsua_acc *acc = &pjsua.acc[acc_index];
+
+	if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
+	    pj_stricmp(&acc->host_part, &sip_uri->host)==0) 
+	{
+	    /* Match ! */
+	    return acc_index;
+	}
+    }
+
+    /* No matching, try match domain part only. */
+    for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
+
+	pjsua_acc *acc = &pjsua.acc[acc_index];
+
+	if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) {
+	    /* Match ! */
+	    return acc_index;
+	}
+    }
+
+    /* Still no match, just return account #0 */
+    return 0;
+}
+
+
+/*
+ * Find account for outgoing request.
+ */
+int pjsua_find_account_for_outgoing(const pj_str_t *url)
+{
+    PJ_UNUSED_ARG(url);
+
+    /* Just use account #0 */
+    return 0;
+}
+
 
 /*
@@ -589,9 +641,16 @@
 
     /* Init sockets (STUN etc): */
-    for (i=0; i<PJ_ARRAY_SIZE(pjsua.med_sock_info); ++i) {
-	status = init_sockets(i==0, &pjsua.med_sock_info[i]);
+    for (i=0; i<(int)pjsua.max_calls; ++i) {
+	status = init_sockets(i==0, &pjsua.calls[i].skinfo);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "init_sockets() has returned error", 
 			 status);
+	    --i;
+	    if (i >= 0)
+		pj_sock_close(pjsua.sip_sock);
+	    while (i >= 0) {
+		pj_sock_close(pjsua.calls[i].skinfo.rtp_sock);
+		pj_sock_close(pjsua.calls[i].skinfo.rtcp_sock);
+	    }
 	    return status;
 	}
@@ -624,22 +683,13 @@
 
     /* Initialize Contact URI, if one is not specified: */
-
-    if (pjsua.contact_uri.slen == 0 && pjsua.local_uri.slen) {
+    for (i=0; i<pjsua.acc_cnt; ++i) {
 
 	pjsip_uri *uri;
 	pjsip_sip_uri *sip_uri;
-	char contact[128];
-	int len;
-
-	/* 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.
-	 */
-	
+
 	/* Need to parse local_uri to get the elements: */
 
-	uri = pjsip_parse_uri(pjsua.pool, pjsua.local_uri.ptr, 
-			      pjsua.local_uri.slen, 0);
+	uri = pjsip_parse_uri(pjsua.pool, pjsua.acc[i].local_uri.ptr,
+			      pjsua.acc[i].local_uri.slen, 0);
 	if (uri == NULL) {
 	    pjsua_perror(THIS_FILE, "Invalid local URI", 
@@ -648,8 +698,9 @@
 	}
 
-
 	/* Local URI MUST be a SIP or SIPS: */
 
-	if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
+	if (!PJSIP_URI_SCHEME_IS_SIP(uri) && 
+	    !PJSIP_URI_SCHEME_IS_SIPS(uri)) 
+	{
 	    pjsua_perror(THIS_FILE, "Invalid local URI", 
 			 PJSIP_EINVALIDSCHEME);
@@ -662,37 +713,52 @@
 	sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
 
-	
-	/* Build temporary contact string. */
-
-	if (sip_uri->user.slen) {
-
-	    /* With the user part. */
-	    len = pj_snprintf(contact, sizeof(contact),
-			      "<sip:%.*s@%.*s:%d>",
-			      (int)sip_uri->user.slen,
-			      sip_uri->user.ptr,
-			      (int)udp_transport->local_name.host.slen,
-			      udp_transport->local_name.host.ptr,
-			      udp_transport->local_name.port);
-	} else {
-
-	    /* Without user part */
-
-	    len = pj_snprintf(contact, sizeof(contact),
-			      "<sip:%.*s:%d>",
-			      (int)udp_transport->local_name.host.slen,
-			      udp_transport->local_name.host.ptr,
-			      udp_transport->local_name.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, &pjsua.contact_uri, contact);
-
+	pjsua.acc[i].user_part = sip_uri->user;
+	pjsua.acc[i].host_part = sip_uri->host;
+
+	if (pjsua.acc[i].contact_uri.slen == 0 && 
+	    pjsua.acc[i].local_uri.slen) 
+	{
+	    char contact[128];
+	    int len;
+
+	    /* 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_snprintf(contact, sizeof(contact),
+				  "<sip:%.*s@%.*s:%d>",
+				  (int)sip_uri->user.slen,
+				  sip_uri->user.ptr,
+				  (int)udp_transport->local_name.host.slen,
+				  udp_transport->local_name.host.ptr,
+				  udp_transport->local_name.port);
+	    } else {
+
+		/* Without user part */
+
+		len = pj_snprintf(contact, sizeof(contact),
+				  "<sip:%.*s:%d>",
+				  (int)udp_transport->local_name.host.slen,
+				  udp_transport->local_name.host.ptr,
+				  udp_transport->local_name.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, &pjsua.acc[i].contact_uri, contact);
+
+	}
     }
 
@@ -715,5 +781,7 @@
 	}
 
-	pj_list_push_back(&pjsua.route_set, route);
+	for (i=0; i<pjsua.acc_cnt; ++i) {
+	    pj_list_push_front(&pjsua.acc[i].route_set, route);
+	}
     }
 
@@ -737,12 +805,20 @@
 
     /* Create client registration session: */
-
-    status = pjsua_regc_init();
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Perform registration, if required. */
-    if (pjsua.regc) {
-	pjsua_regc_update(1);
+    for (i=0; i<pjsua.acc_cnt; ++i) {
+	status = pjsua_regc_init(i);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Perform registration, if required. */
+	if (pjsua.acc[i].regc) {
+	    pjsua_regc_update(i, 1);
+	}
+    }
+
+
+    /* 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);
     }
 
@@ -777,4 +853,17 @@
     /* Signal threads to quit: */
     pjsua.quit_flag = 1;
+
+    /* Terminate all calls. */
+    pjsua_inv_shutdown();
+
+    /* Terminate all presence subscriptions. */
+    pjsua_pres_shutdown();
+
+    /* Unregister, if required: */
+    for (i=0; i<pjsua.acc_cnt; ++i) {
+	if (pjsua.acc[i].regc) {
+	    pjsua_regc_update(i, 0);
+	}
+    }
 
     /* Wait worker threads to quit: */
@@ -789,15 +878,4 @@
 
 
-    /* Terminate all calls. */
-    pjsua_inv_shutdown();
-
-    /* Terminate all presence subscriptions. */
-    pjsua_pres_shutdown();
-
-    /* Unregister, if required: */
-    if (pjsua.regc) {
-	pjsua_regc_update(0);
-    }
-
     /* Wait for some time to allow unregistration to complete: */
     PJ_LOG(4,(THIS_FILE, "Shutting down..."));
Index: project/trunk/pjsip/src/pjsua/pjsua_inv.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua_inv.c	(revision 235)
+++ 	(revision )
@@ -1,1025 +1,0 @@
-/* $Id$ */
-/* 
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * 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 "pjsua.h"
-#include <pj/log.h>
-
-
-/*
- * pjsua_inv.c
- *
- * Invite session specific functionalities.
- */
-
-#define THIS_FILE   "pjsua_inv.c"
-
-
-/**
- * Make outgoing call.
- */
-pj_status_t pjsua_invite(const char *cstr_dest_uri,
-			 struct pjsua_inv_data **p_inv_data)
-{
-    pj_str_t dest_uri;
-    pjsip_dialog *dlg;
-    pjmedia_sdp_session *offer;
-    pjsip_inv_session *inv;
-    struct pjsua_inv_data *inv_data;
-    pjsip_tx_data *tdata;
-    int med_sk_index = 0;
-    pj_status_t status;
-
-    /* Convert cstr_dest_uri to dest_uri */
-    
-    dest_uri = pj_str((char*)cstr_dest_uri);
-
-    /* Find free socket. */
-    for (med_sk_index=0; med_sk_index<PJSUA_MAX_CALLS; ++med_sk_index) {
-	if (!pjsua.med_sock_use[med_sk_index])
-	    break;
-    }
-
-    if (med_sk_index == PJSUA_MAX_CALLS) {
-	PJ_LOG(3,(THIS_FILE, "Error: too many calls!"));
-	return PJ_ETOOMANY;
-    }
-
-    pjsua.med_sock_use[med_sk_index] = 1;
-
-    /* Create outgoing dialog: */
-
-    status = pjsip_dlg_create_uac( pjsip_ua_instance(), &pjsua.local_uri,
-				   &pjsua.contact_uri, &dest_uri, &dest_uri,
-				   &dlg);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Dialog creation failed", status);
-	return status;
-    }
-
-    /* Get media capability from media endpoint: */
-
-    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool,
-				       1, &pjsua.med_sock_info[med_sk_index], 
-				       &offer);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
-	goto on_error;
-    }
-
-    /* Create the INVITE session: */
-
-    status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Invite session creation failed", status);
-	goto on_error;
-    }
-
-
-    /* Create and associate our data in the session. */
-
-    inv_data = pj_pool_zalloc( dlg->pool, sizeof(struct pjsua_inv_data));
-    inv_data->inv = inv;
-    inv_data->call_slot = med_sk_index;
-    dlg->mod_data[pjsua.mod.id] = inv_data;
-    inv->mod_data[pjsua.mod.id] = inv_data;
-
-
-    /* Set dialog Route-Set: */
-
-    if (!pj_list_empty(&pjsua.route_set))
-	pjsip_dlg_set_route_set(dlg, &pjsua.route_set);
-
-
-    /* Set credentials: */
-
-    pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count, 
-				    pjsua.cred_info);
-
-
-    /* Create initial INVITE: */
-
-    status = pjsip_inv_invite(inv, &tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create initial INVITE request", 
-		     status);
-	goto on_error;
-    }
-
-
-    /* Add invite session to the list. */
-    
-    pj_list_push_back(&pjsua.inv_list, inv_data);
-
-
-    /* Send initial INVITE: */
-
-    status = pjsip_inv_send_msg(inv, tdata, NULL);
-    if (status != PJ_SUCCESS) {
-	/*
-	 * Note:
-	 *  inv_data will be removed from the list in the callback
-	 */
-	pjsua_perror(THIS_FILE, "Unable to send initial INVITE request", 
-		     status);
-	goto on_error;
-    }
-
-
-    /* Done. */
-    if (p_inv_data)
-	*p_inv_data = inv_data;
-
-    return PJ_SUCCESS;
-
-
-on_error:
-
-    PJ_TODO(DESTROY_DIALOG_ON_FAIL);
-    pjsua.med_sock_use[med_sk_index] = 0;
-    return status;
-}
-
-
-/**
- * Handle incoming INVITE request.
- */
-pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
-{
-    pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
-    pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
-    pjsip_msg *msg = rdata->msg_info.msg;
-    pjsip_tx_data *response = NULL;
-    unsigned options = 0;
-    pjsip_inv_session *inv;
-    struct pjsua_inv_data *inv_data;
-    pjmedia_sdp_session *answer;
-    int med_sk_index;
-    pj_status_t status;
-
-    /* Don't want to handle anything but INVITE */
-    if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
-	return PJ_FALSE;
-
-    /* Don't want to handle anything that's already associated with
-     * existing dialog or transaction.
-     */
-    if (dlg || tsx)
-	return PJ_FALSE;
-
-
-    /* Verify that we can handle the request. */
-    status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
-				      pjsua.endpt, &response);
-    if (status != PJ_SUCCESS) {
-
-	/*
-	 * No we can't handle the incoming INVITE request.
-	 */
-
-	if (response) {
-	    pjsip_response_addr res_addr;
-
-	    pjsip_get_response_addr(response->pool, rdata, &res_addr);
-	    pjsip_endpt_send_response(pjsua.endpt, &res_addr, response, 
-				      NULL, NULL);
-
-	} else {
-
-	    /* Respond with 500 (Internal Server Error) */
-	    pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
-					  NULL, NULL);
-	}
-
-	return PJ_TRUE;
-    } 
-
-
-    /*
-     * Yes we can handle the incoming INVITE request.
-     */
-
-    /* Find free call slot. */
-    for (med_sk_index=0; med_sk_index<PJSUA_MAX_CALLS; ++med_sk_index) {
-	if (!pjsua.med_sock_use[med_sk_index])
-	    break;
-    }
-
-    if (med_sk_index == PJSUA_MAX_CALLS) {
-	pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 
-				      PJSIP_SC_BUSY_HERE, NULL,
-				      NULL, NULL);
-	return PJ_TRUE;
-    }
-
-
-    pjsua.med_sock_use[med_sk_index] = 1;
-
-    /* Get media capability from media endpoint: */
-
-    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool,
-				       1, &pjsua.med_sock_info[med_sk_index], 
-				       &answer );
-    if (status != PJ_SUCCESS) {
-	pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
-				      NULL, NULL);
-
-	/* Free call socket. */
-	pjsua.med_sock_use[med_sk_index] = 0;
-	return PJ_TRUE;
-    }
-
-    /* Create dialog: */
-
-    status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
-				   &pjsua.contact_uri, &dlg);
-    if (status != PJ_SUCCESS) {
-	pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
-				      NULL, NULL);
-
-	/* Free call socket. */
-	pjsua.med_sock_use[med_sk_index] = 0;
-	return PJ_TRUE;
-    }
-
-
-    /* Create invite session: */
-
-    status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
-    if (status != PJ_SUCCESS) {
-
-	pjsip_dlg_respond(dlg, rdata, 500, NULL);
-
-	/* Free call socket. */
-	pjsua.med_sock_use[med_sk_index] = 0;
-
-	// TODO: Need to delete dialog
-	return PJ_TRUE;
-    }
-
-
-    /* Create and attach pjsua data to the dialog: */
-
-    inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data));
-    inv_data->inv = inv;
-    inv_data->call_slot = inv_data->call_slot = med_sk_index;
-    dlg->mod_data[pjsua.mod.id] = inv_data;
-    inv->mod_data[pjsua.mod.id] = inv_data;
-
-    pj_list_push_back(&pjsua.inv_list, inv_data);
-
-
-    /* Must answer with some response to initial INVITE.
-     * If auto-answer flag is set, send 200 straight away, otherwise send 100.
-     */
-    
-    status = pjsip_inv_initial_answer(inv, rdata, 
-				      (pjsua.auto_answer ? 200 : 100), 
-				      NULL, NULL, &response);
-    if (status != PJ_SUCCESS) {
-	
-	pjsua_perror(THIS_FILE, "Unable to create 100 response", status);
-
-	pjsip_dlg_respond(dlg, rdata, 500, NULL);
-
-	/* Free call socket. */
-	pjsua.med_sock_use[med_sk_index] = 0;
-
-	// TODO: Need to delete dialog
-
-    } else {
-	status = pjsip_inv_send_msg(inv, response, NULL);
-	if (status != PJ_SUCCESS)
-	    pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
-    }
-
-    if (pjsua.auto_answer < 200) {
-	PJ_LOG(3,(THIS_FILE,
-		  "\nIncoming call!!\n"
-		  "From: %.*s\n"
-		  "To:   %.*s\n"
-		  "(press 'a' to answer, 'h' to decline)",
-		  (int)dlg->remote.info_str.slen,
-		  dlg->remote.info_str.ptr,
-		  (int)dlg->local.info_str.slen,
-		  dlg->local.info_str.ptr));
-    } else {
-	PJ_LOG(3,(THIS_FILE,
-		  "Call From:%.*s To:%.*s was answered with %d (%s)",
-		  (int)dlg->remote.info_str.slen,
-		  dlg->remote.info_str.ptr,
-		  (int)dlg->local.info_str.slen,
-		  dlg->local.info_str.ptr,
-		  pjsua.auto_answer,
-		  pjsip_get_status_text(pjsua.auto_answer)->ptr ));
-    }
-
-    /* This INVITE request has been handled. */
-    return PJ_TRUE;
-}
-
-
-/*
- * This callback receives notification from invite session when the
- * session state has changed.
- */
-void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
-{
-    struct pjsua_inv_data *inv_data;
-
-    inv_data = inv->dlg->mod_data[pjsua.mod.id];
-
-    /* If this is an outgoing INVITE that was created because of
-     * REFER/transfer, send NOTIFY to transferer.
-     */
-    if (inv_data && inv_data->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) 
-    {
-	int st_code = -1;
-	pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
-	
-
-	switch (inv->state) {
-	case PJSIP_INV_STATE_NULL:
-	case PJSIP_INV_STATE_CALLING:
-	    /* Do nothing */
-	    break;
-
-	case PJSIP_INV_STATE_EARLY:
-	case PJSIP_INV_STATE_CONNECTING:
-	    st_code = e->body.tsx_state.tsx->status_code;
-	    ev_state = PJSIP_EVSUB_STATE_ACTIVE;
-	    break;
-
-	case PJSIP_INV_STATE_CONFIRMED:
-	    /* When state is confirmed, send the final 200/OK and terminate
-	     * subscription.
-	     */
-	    st_code = e->body.tsx_state.tsx->status_code;
-	    ev_state = PJSIP_EVSUB_STATE_TERMINATED;
-	    break;
-
-	case PJSIP_INV_STATE_DISCONNECTED:
-	    st_code = e->body.tsx_state.tsx->status_code;
-	    ev_state = PJSIP_EVSUB_STATE_TERMINATED;
-	    break;
-	}
-
-	if (st_code != -1) {
-	    pjsip_tx_data *tdata;
-	    pj_status_t status;
-
-	    status = pjsip_xfer_notify( inv_data->xfer_sub,
-					ev_state, st_code,
-					NULL, &tdata);
-	    if (status != PJ_SUCCESS) {
-		pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
-	    } else {
-		status = pjsip_xfer_send_request(inv_data->xfer_sub, tdata);
-		if (status != PJ_SUCCESS) {
-		    pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
-		}
-	    }
-	}
-    }
-
-
-    /* Destroy media session when invite session is disconnected. */
-    if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
-
-	pj_assert(inv_data != NULL);
-
-	if (inv_data && inv_data->session) {
-	    pjmedia_conf_remove_port(pjsua.mconf, inv_data->conf_slot);
-	    pjmedia_session_destroy(inv_data->session);
-	    pjsua.med_sock_use[inv_data->call_slot] = 0;
-	    inv_data->session = NULL;
-
-	    PJ_LOG(3,(THIS_FILE,"Media session is destroyed"));
-	}
-
-	if (inv_data) {
-
-	    pj_list_erase(inv_data);
-
-	}
-    }
-
-    pjsua_ui_inv_on_state_changed(inv, e);
-}
-
-
-/*
- * Callback called by event framework when the xfer subscription state
- * has changed.
- */
-static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
-{
-    
-    PJ_UNUSED_ARG(event);
-
-    /*
-     * We're only interested when subscription is terminated, to 
-     * clear the xfer_sub member of the inv_data.
-     */
-    if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
-	struct pjsua_inv_data *inv_data;
-
-	inv_data = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
-	if (!inv_data)
-	    return;
-
-	pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
-	inv_data->xfer_sub = NULL;
-
-	PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
-    }
-}
-
-
-/*
- * Follow transfer (REFER) request.
- */
-static void on_call_transfered( pjsip_inv_session *inv,
-			        pjsip_rx_data *rdata )
-{
-    pj_status_t status;
-    pjsip_tx_data *tdata;
-    struct pjsua_inv_data *inv_data;
-    const pj_str_t str_refer_to = { "Refer-To", 8};
-    pjsip_generic_string_hdr *refer_to;
-    char *uri;
-    struct pjsip_evsub_user xfer_cb;
-    pjsip_evsub *sub;
-
-    /* Find the Refer-To header */
-    refer_to = (pjsip_generic_string_hdr*)
-	pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
-
-    if (refer_to == NULL) {
-	/* Invalid Request.
-	 * No Refer-To header!
-	 */
-	PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
-	pjsip_dlg_respond( inv->dlg, rdata, 400, NULL);
-	return;
-    }
-
-    PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
-	      (int)inv->dlg->remote.info_str.slen,
-	      inv->dlg->remote.info_str.ptr,
-	      (int)refer_to->hvalue.slen, 
-	      refer_to->hvalue.ptr));
-
-    /* Init callback */
-    pj_memset(&xfer_cb, 0, sizeof(xfer_cb));
-    xfer_cb.on_evsub_state = &xfer_on_evsub_state;
-
-    /* Create transferee event subscription */
-    status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
-	pjsip_dlg_respond( inv->dlg, rdata, 500, NULL);
-	return;
-    }
-
-    /* Accept the REFER request, send 200 (OK). */
-    pjsip_xfer_accept(sub, rdata, 200, NULL);
-
-    /* Create initial NOTIFY request */
-    status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
-				100, NULL, &tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
-	return;
-    }
-
-    /* Send initial NOTIFY request */
-    status = pjsip_xfer_send_request( sub, tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
-	return;
-    }
-
-    /* We're cheating here.
-     * We need to get a null terminated string from a pj_str_t.
-     * So grab the pointer from the hvalue and NULL terminate it, knowing
-     * that the NULL position will be occupied by a newline. 
-     */
-    uri = refer_to->hvalue.ptr;
-    uri[refer_to->hvalue.slen] = '\0';
-
-    /* Now make the outgoing call. */
-    status = pjsua_invite(uri, &inv_data);
-    if (status != PJ_SUCCESS) {
-
-	/* Notify xferer about the error */
-	status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
-				   500, NULL, &tdata);
-	if (status != PJ_SUCCESS) {
-	    pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", 
-			  status);
-	    return;
-	}
-	status = pjsip_xfer_send_request(sub, tdata);
-	if (status != PJ_SUCCESS) {
-	    pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", 
-			  status);
-	    return;
-	}
-	return;
-    }
-
-    /* Put the server subscription in inv_data.
-     * Subsequent state changed in pjsua_inv_on_state_changed() will be
-     * reported back to the server subscription.
-     */
-    inv_data->xfer_sub = sub;
-
-    /* Put the invite_data in the subscription. */
-    pjsip_evsub_set_mod_data(sub, pjsua.mod.id, inv_data);
-}
-
-
-/*
- * This callback is called when transaction state has changed in INVITE
- * session. We use this to trap incoming REFER request.
- */
-void pjsua_inv_on_tsx_state_changed(pjsip_inv_session *inv,
-				    pjsip_transaction *tsx,
-				    pjsip_event *e)
-{
-    if (tsx->role==PJSIP_ROLE_UAS &&
-	tsx->state==PJSIP_TSX_STATE_TRYING &&
-	pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
-    {
-	/*
-	 * Incoming REFER request.
-	 */
-	on_call_transfered(inv, e->body.tsx_state.src.rdata);
-    }
-}
-
-
-/*
- * This callback is called by invite session framework when UAC session
- * has forked.
- */
-void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
-{
-    PJ_UNUSED_ARG(inv);
-    PJ_UNUSED_ARG(e);
-
-    PJ_TODO(HANDLE_FORKED_DIALOG);
-}
-
-
-/*
- * Create inactive SDP for call hold.
- */
-static pj_status_t create_inactive_sdp(struct pjsua_inv_data *inv_session,
-				       pjmedia_sdp_session **p_answer)
-{
-    pj_status_t status;
-    pjmedia_sdp_conn *conn;
-    pjmedia_sdp_attr *attr;
-    pjmedia_sdp_session *sdp;
-
-    /* Create new offer */
-    status = pjmedia_endpt_create_sdp(pjsua.med_endpt, pjsua.pool, 1,
-				      &pjsua.med_sock_info[inv_session->call_slot],
-				      &sdp);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
-	return status;
-    }
-
-    /* Get SDP media connection line */
-    conn = sdp->media[0]->conn;
-    if (!conn)
-	conn = sdp->conn;
-
-    /* Modify address */
-    conn->addr = pj_str("0.0.0.0");
-
-    /* Remove existing directions attributes */
-    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
-    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
-    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
-    pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
-
-    /* Add inactive attribute */
-    attr = pjmedia_sdp_attr_create(pjsua.pool, "inactive", NULL);
-    pjmedia_sdp_media_add_attr(sdp->media[0], attr);
-
-    *p_answer = sdp;
-
-    return status;
-}
-
-/*
- * Called when session received new offer.
- */
-void pjsua_inv_on_rx_offer( pjsip_inv_session *inv,
-			    const pjmedia_sdp_session *offer)
-{
-    struct pjsua_inv_data *inv_data;
-    pjmedia_sdp_conn *conn;
-    pjmedia_sdp_session *answer;
-    pj_bool_t is_remote_active;
-    pj_status_t status;
-
-    inv_data = inv->dlg->mod_data[pjsua.mod.id];
-
-    /*
-     * See if remote is offering active media (i.e. not on-hold)
-     */
-    is_remote_active = PJ_TRUE;
-
-    conn = offer->media[0]->conn;
-    if (!conn)
-	conn = offer->conn;
-
-    if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
-	pj_strcmp2(&conn->addr, "0")==0)
-    {
-	is_remote_active = PJ_FALSE;
-
-    } 
-    else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
-    {
-	is_remote_active = PJ_FALSE;
-    }
-
-    PJ_LOG(4,(THIS_FILE, "Received SDP offer, remote media is %s",
-	      (is_remote_active ? "active" : "inactive")));
-
-    /* Supply candidate answer */
-    if (is_remote_active) {
-	status = pjmedia_endpt_create_sdp( pjsua.med_endpt, inv->pool, 1,
-					   &pjsua.med_sock_info[inv_data->call_slot],
-					   &answer);
-    } else {
-	status = create_inactive_sdp( inv_data, &answer );
-    }
-
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
-	return;
-    }
-
-    status = pjsip_inv_set_sdp_answer(inv, answer);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to set answer", status);
-	return;
-    }
-
-}
-
-
-/*
- * Callback to be called when SDP offer/answer negotiation has just completed
- * in the session. This function will start/update media if negotiation
- * has succeeded.
- */
-void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
-{
-    struct pjsua_inv_data *inv_data;
-    const pjmedia_sdp_session *local_sdp;
-    const pjmedia_sdp_session *remote_sdp;
-    pjmedia_port *media_port;
-    pj_str_t port_name;
-    char tmp[PJSIP_MAX_URL_SIZE];
-
-    if (status != PJ_SUCCESS) {
-
-	pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
-	return;
-
-    }
-
-    /* Destroy existing media session, if any. */
-
-    inv_data = inv->dlg->mod_data[pjsua.mod.id];
-    if (inv_data && inv_data->session) {
-	pjmedia_conf_remove_port(pjsua.mconf, inv_data->conf_slot);
-	pjmedia_session_destroy(inv_data->session);
-	pjsua.med_sock_use[inv_data->call_slot] = 0;
-	inv_data->session = NULL;
-    }
-
-    /* Get local and remote SDP */
-
-    status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, 
-		     "Unable to retrieve currently active local SDP", 
-		     status);
-	return;
-    }
-
-
-    status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, 
-		     "Unable to retrieve currently active remote SDP", 
-		     status);
-	return;
-    }
-
-    /* Create new media session. 
-     * The media session is active immediately.
-     */
-    if (pjsua.null_audio)
-	return;
-    
-    status = pjmedia_session_create( pjsua.med_endpt, 1, 
-				     &pjsua.med_sock_info[inv_data->call_slot],
-				     local_sdp, remote_sdp, 
-				     inv_data,
-				     &inv_data->session );
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create media session", 
-		     status);
-	return;
-    }
-
-
-    /* Get the port interface of the first stream in the session.
-     * We need the port interface to add to the conference bridge.
-     */
-    pjmedia_session_get_port(inv_data->session, 0, &media_port);
-
-
-    /*
-     * Add the call to conference bridge.
-     */
-    port_name.ptr = tmp;
-    port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
-				     inv_data->inv->dlg->remote.info->uri,
-				     tmp, sizeof(tmp));
-    if (port_name.slen < 1) {
-	port_name = pj_str("call");
-    }
-    status = pjmedia_conf_add_port( pjsua.mconf, inv->pool,
-				    media_port, 
-				    &port_name,
-				    &inv_data->conf_slot);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create conference slot", 
-		     status);
-	pjmedia_session_destroy(inv_data->session);
-	inv_data->session = NULL;
-	return;
-    }
-
-    /* If auto-play is configured, connect the call to the file player 
-     * port 
-     */
-    if (pjsua.wav_file && inv->role == PJSIP_ROLE_UAS) {
-
-	pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot, 
-				   inv_data->conf_slot);
-
-    } else {
-
-	/* Connect new call to the sound device port (port zero) in the
-	 * main conference bridge.
-	 */
-	pjmedia_conf_connect_port( pjsua.mconf, 0, inv_data->conf_slot);
-	pjmedia_conf_connect_port( pjsua.mconf, inv_data->conf_slot, 0);
-    }
-
-
-    /* Done. */
-    {
-	struct pjmedia_session_info sess_info;
-	char info[80];
-	int info_len = 0;
-	unsigned i;
-
-	pjmedia_session_get_info(inv_data->session, &sess_info);
-	for (i=0; i<sess_info.stream_cnt; ++i) {
-	    int len;
-	    const char *dir;
-	    pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
-
-	    switch (strm_info->dir) {
-	    case PJMEDIA_DIR_NONE:
-		dir = "inactive";
-		break;
-	    case PJMEDIA_DIR_ENCODING:
-		dir = "sendonly";
-		break;
-	    case PJMEDIA_DIR_DECODING:
-		dir = "recvonly";
-		break;
-	    case PJMEDIA_DIR_ENCODING_DECODING:
-		dir = "sendrecv";
-		break;
-	    default:
-		dir = "unknown";
-		break;
-	    }
-	    len = pj_ansi_sprintf( info+info_len,
-				   ", stream #%d: %.*s (%s)", i,
-				   (int)strm_info->fmt.encoding_name.slen,
-				   (int)strm_info->fmt.encoding_name.ptr,
-				   dir);
-	    if (len > 0)
-		info_len += len;
-	}
-	PJ_LOG(3,(THIS_FILE,"Media started%s", info));
-    }
-}
-
-
-/*
- * Hangup call.
- */
-void pjsua_inv_hangup(struct pjsua_inv_data *inv_session, int code)
-{
-    pj_status_t status;
-    pjsip_tx_data *tdata;
-
-    status = pjsip_inv_end_session(inv_session->inv, 
-				   code, NULL, &tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, 
-		     "Failed to create end session message", 
-		     status);
-	return;
-    }
-
-    /* pjsip_inv_end_session may return PJ_SUCCESS with NULL 
-     * as p_tdata when INVITE transaction has not been answered
-     * with any provisional responses.
-     */
-    if (tdata == NULL)
-	return;
-
-    status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, 
-		     "Failed to send end session message", 
-		     status);
-	return;
-    }
-}
-
-
-/*
- * Put call on-Hold.
- */
-void pjsua_inv_set_hold(struct pjsua_inv_data *inv_session)
-{
-    pjmedia_sdp_session *sdp;
-    pjsip_inv_session *inv = inv_session->inv;
-    pjsip_tx_data *tdata;
-    pj_status_t status;
-
-    if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
-	PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
-	return;
-    }
-
-    status = create_inactive_sdp(inv_session, &sdp);
-    if (status != PJ_SUCCESS)
-	return;
-
-    /* Send re-INVITE with new offer */
-    status = pjsip_inv_reinvite( inv_session->inv, NULL, sdp, &tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
-	return;
-    }
-
-    status = pjsip_inv_send_msg( inv_session->inv, tdata, NULL);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
-	return;
-    }
-}
-
-
-/*
- * re-INVITE.
- */
-void pjsua_inv_reinvite(struct pjsua_inv_data *inv_session)
-{
-    pjmedia_sdp_session *sdp;
-    pjsip_tx_data *tdata;
-    pjsip_inv_session *inv = inv_session->inv;
-    pj_status_t status;
-
-
-    if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
-	PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
-	return;
-    }
-
-    /* Create SDP */
-    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, inv->pool, 1,
-				       &pjsua.med_sock_info[inv_session->call_slot],
-				       &sdp);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", status);
-	return;
-    }
-
-    /* Send re-INVITE with new offer */
-    status = pjsip_inv_reinvite( inv_session->inv, NULL, sdp, &tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
-	return;
-    }
-
-    status = pjsip_inv_send_msg( inv_session->inv, tdata, NULL);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
-	return;
-    }
-}
-
-
-/*
- * Transfer call.
- */
-void pjsua_inv_xfer_call(struct pjsua_inv_data *inv_session,
-			 const char *dest)
-{
-    pjsip_evsub *sub;
-    pjsip_tx_data *tdata;
-    pj_str_t tmp;
-    pj_status_t status;
- 
-    
-    /* Create xfer client subscription.
-     * We're not interested in knowing the transfer result, so we
-     * put NULL as the callback.
-     */
-    status = pjsip_xfer_create_uac(inv_session->inv->dlg, NULL, &sub);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create xfer", status);
-	return;
-    }
-
-    /*
-     * Create REFER request.
-     */
-    status = pjsip_xfer_initiate(sub, pj_cstr(&tmp, dest), &tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
-	return;
-    }
-
-    /* Send. */
-    status = pjsip_xfer_send_request(sub, tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
-	return;
-    }
-
-    /* For simplicity (that's what this program is intended to be!), 
-     * leave the original invite session as it is. More advanced application
-     * may want to hold the INVITE, or terminate the invite, or whatever.
-     */
-}
-
-
-/*
- * Terminate all calls.
- */
-void pjsua_inv_shutdown()
-{
-    struct pjsua_inv_data *inv_data, *next;
-
-    inv_data = pjsua.inv_list.next;
-    while (inv_data != &pjsua.inv_list) {
-	pjsip_tx_data *tdata;
-
-	next = inv_data->next;
-
-	if (pjsip_inv_end_session(inv_data->inv, 410, NULL, &tdata)==0) {
-	    if (tdata)
-		pjsip_inv_send_msg(inv_data->inv, tdata, NULL);
-	}
-
-	inv_data = next;
-    }
-}
-
-
Index: /pjproject/trunk/pjsip/src/pjsua/pjsua_opt.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua_opt.c	(revision 235)
+++ /pjproject/trunk/pjsip/src/pjsua/pjsua_opt.c	(revision 236)
@@ -44,6 +44,4 @@
     puts("  pjsua [options]");
     puts("");
-    puts("  [sip-url]   Default URL to invite.");
-    puts("");
     puts("General options:");
     puts("  --help              Display this help screen");
@@ -56,27 +54,34 @@
     puts("  --app-log-level=N   Set log max level for stdout display to N");
     puts("");
+    puts("SIP Account options:");
+    puts("  --id=url            Set the URL of local ID (used in From header)");
+    puts("  --contact=url       Override the Contact information");
+    puts("  --proxy=url         Set the URL of proxy server");
+    puts("");
+    puts("SIP Account Registration Options:");
+    puts("  --registrar=url     Set the URL of registrar server");
+    puts("  --reg-timeout=secs  Set registration interval to secs (default 3600)");
+    puts("");
+    puts("SIP Account Control:");
+    puts("  --next-account      Add more account");
+    puts("");
     puts("Authentication options:");
     puts("  --realm=string      Set realm");
     puts("  --username=string   Set authentication username");
     puts("  --password=string   Set authentication password");
-    puts("");
-    puts("SIP options:");
-    puts("  --id=url            Set the URL of local ID (used in From header)");
-    puts("  --contact=url       Override the Contact information");
-    puts("  --proxy=url         Set the URL of proxy server");
-    //puts("  --outbound=url      Set the URL of outbound proxy server");
-    puts("");
-    puts("Registration Options:");
-    puts("  --registrar=url     Set the URL of registrar server");
-    puts("  --reg-timeout=secs  Set registration interval to secs (default 3600)");
+    puts("  --next-cred         Add more credential");
     puts("");
     puts("Transport Options:");
-    puts("  --local-port=port   Set TCP/UDP port");
+    puts("  --local-port=port        Set TCP/UDP port");
+    puts("  --outbound=url           Set the URL of outbound proxy server");
     puts("  --use-stun1=host[:port]");
-    puts("  --use-stun2=host[:port]  Use STUN and set host name and port of STUN servers");
+    puts("  --use-stun2=host[:port]  Resolve local IP with the specified STUN servers");
     puts("");
     puts("Media Options:");
     puts("  --null-audio        Use NULL audio device");
-    //puts("  --wav-file=file     Play WAV file in conference bridge");
+    puts("  --play-file=file    Play WAV file in conference bridge");
+    puts("  --auto-play         Automatically play the file (to incoming calls only)");
+    puts("  --auto-loop         Automatically loop incoming RTP to outgoing RTP");
+    puts("  --auto-conf         Automatically put incoming calls to conference");
     puts("");
     puts("Buddy List (can be more than one):");
@@ -85,5 +90,5 @@
     puts("User Agent options:");
     puts("  --auto-answer=code  Automatically answer incoming calls with code (e.g. 200)");
-    puts("  --auto-play=file    Automatically play WAVE file to incoming calls");
+    puts("  --max-calls=N       Maximum number of concurrent calls (default:4, max:255)");
     puts("");
     fflush(stdout);
@@ -200,5 +205,9 @@
 	   OPT_USE_STUN1, OPT_USE_STUN2, 
 	   OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
-	   OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY};
+	   OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
+	   OPT_AUTO_CONF,
+	   OPT_PLAY_FILE,
+	   OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
+    };
     struct option long_options[] = {
 	{ "config-file",1, 0, OPT_CONFIG_FILE},
@@ -226,8 +235,16 @@
 	{ "auto-answer",1, 0, OPT_AUTO_ANSWER},
 	{ "auto-hangup",1, 0, OPT_AUTO_HANGUP},
-	{ "auto-play",  1, 0, OPT_AUTO_PLAY},
+	{ "auto-play",  0, 0, OPT_AUTO_PLAY},
+	{ "auto-loop",  0, 0, OPT_AUTO_LOOP},
+	{ "auto-conf",  0, 0, OPT_AUTO_CONF},
+	{ "play-file",  1, 0, OPT_PLAY_FILE},
+	{ "next-account",0,0, OPT_NEXT_ACCOUNT},
+	{ "next-cred",	0, 0, OPT_NEXT_CRED},
+	{ "max-calls",	1, 0, OPT_MAX_CALLS},
 	{ NULL, 0, 0, 0}
     };
     pj_status_t status;
+    pjsua_acc *cur_acc;
+    pjsip_cred_info *cur_cred;
     char *config_file = NULL;
 
@@ -248,4 +265,8 @@
 	    return status;
     }
+
+
+    cur_acc = &pjsua.acc[0];
+    cur_cred = &pjsua.cred_info[0];
 
 
@@ -308,5 +329,5 @@
 		return PJ_EINVAL;
 	    }
-	    pjsua.proxy = pj_str(optarg);
+	    cur_acc->proxy = pj_str(optarg);
 	    break;
 
@@ -324,10 +345,10 @@
 		return PJ_EINVAL;
 	    }
-	    pjsua.registrar_uri = pj_str(optarg);
+	    cur_acc->reg_uri = pj_str(optarg);
 	    break;
 
 	case OPT_REG_TIMEOUT:   /* reg-timeout */
-	    pjsua.reg_timeout = pj_strtoul(pj_cstr(&tmp,optarg));
-	    if (pjsua.reg_timeout < 1 || pjsua.reg_timeout > 3600) {
+	    cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,optarg));
+	    if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
 		printf("Error: invalid value for --reg-timeout (expecting 1-3600)\n");
 		return PJ_EINVAL;
@@ -340,5 +361,5 @@
 		return PJ_EINVAL;
 	    }
-	    pjsua.local_uri = pj_str(optarg);
+	    cur_acc->local_uri = pj_str(optarg);
 	    break;
 
@@ -348,21 +369,31 @@
 		return PJ_EINVAL;
 	    }
-	    pjsua.contact_uri = pj_str(optarg);
+	    cur_acc->contact_uri = pj_str(optarg);
+	    break;
+
+	case OPT_NEXT_ACCOUNT: /* Add more account. */
+	    pjsua.acc_cnt++;
+	    cur_acc = &pjsua.acc[pjsua.acc_cnt - 1];
 	    break;
 
 	case OPT_USERNAME:   /* Default authentication user */
-	    if (!pjsua.cred_count) pjsua.cred_count = 1;
-	    pjsua.cred_info[0].username = pj_str(optarg);
+	    if (pjsua.cred_count==0) pjsua.cred_count=1;
+	    cur_cred->username = pj_str(optarg);
 	    break;
 
 	case OPT_REALM:	    /* Default authentication realm. */
-	    if (!pjsua.cred_count) pjsua.cred_count = 1;
-	    pjsua.cred_info[0].realm = pj_str(optarg);
+	    if (pjsua.cred_count==0) pjsua.cred_count=1;
+	    cur_cred->realm = pj_str(optarg);
 	    break;
 
 	case OPT_PASSWORD:   /* authentication password */
-	    if (!pjsua.cred_count) pjsua.cred_count = 1;
-	    pjsua.cred_info[0].data_type = 0;
-	    pjsua.cred_info[0].data = pj_str(optarg);
+	    if (pjsua.cred_count==0) pjsua.cred_count=1;
+	    cur_cred->data_type = 0;
+	    cur_cred->data = pj_str(optarg);
+	    break;
+
+	case OPT_NEXT_CRED: /* Next credential */
+	    pjsua.cred_count++;
+	    cur_cred = &pjsua.cred_info[pjsua.cred_count - 1];
 	    break;
 
@@ -412,4 +443,16 @@
 
 	case OPT_AUTO_PLAY:
+	    pjsua.auto_play = 1;
+	    break;
+
+	case OPT_AUTO_LOOP:
+	    pjsua.auto_loop = 1;
+	    break;
+
+	case OPT_AUTO_CONF:
+	    pjsua.auto_conf = 1;
+	    break;
+
+	case OPT_PLAY_FILE:
 	    pjsua.wav_file = optarg;
 	    break;
@@ -422,4 +465,12 @@
 	    }
 	    break;
+
+	case OPT_MAX_CALLS:
+	    pjsua.max_calls = atoi(optarg);
+	    if (pjsua.max_calls < 1 || pjsua.max_calls > 255) {
+		puts("Too many calls for max-calls (1-255)");
+		return -1;
+	    }
+	    break;
 	}
     }
@@ -430,8 +481,4 @@
     }
 
-    if (pjsua.reg_timeout == 0)
-	pjsua.reg_timeout = 3600;
-
-
     return PJ_SUCCESS;
 }
@@ -439,10 +486,10 @@
 
 
-static void print_invite_session(const char *title,
-				 struct pjsua_inv_data *inv_data, 
-				 char *buf, pj_size_t size)
+static void print_call(const char *title,
+		       int call_index, 
+		       char *buf, pj_size_t size)
 {
     int len;
-    pjsip_inv_session *inv = inv_data->inv;
+    pjsip_inv_session *inv = pjsua.calls[call_index].inv;
     pjsip_dialog *dlg = inv->dlg;
     char userinfo[128];
@@ -516,5 +563,4 @@
 void pjsua_dump(void)
 {
-    struct pjsua_inv_data *inv_data;
     char buf[128];
     unsigned old_decor;
@@ -534,21 +580,21 @@
     PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
 
-    if (pj_list_empty(&pjsua.inv_list)) {
+    if (pjsua.call_cnt == 0) {
 
 	PJ_LOG(3,(THIS_FILE, "  - no sessions -"));
 
     } else {
-
-	inv_data = pjsua.inv_list.next;
-
-	while (inv_data != &pjsua.inv_list) {
-
-	    print_invite_session("  ", inv_data, buf, sizeof(buf));
+	int i;
+
+	for (i=0; i<pjsua.max_calls; ++i) {
+
+	    if (pjsua.calls[i].inv == NULL)
+		continue;
+
+	    print_call("  ", i, buf, sizeof(buf));
 	    PJ_LOG(3,(THIS_FILE, "%s", buf));
 
-	    if (inv_data->session)
-		dump_media_session(inv_data->session);
-
-	    inv_data = inv_data->next;
+	    if (pjsua.calls[i].session)
+		dump_media_session(pjsua.calls[i].session);
 	}
     }
@@ -576,38 +622,94 @@
 
 /*
- * Save settings.
+ * Save account settings
  */
-pj_status_t pjsua_save_settings(const char *filename)
-{
-    unsigned i;
+static void save_account_settings(int acc_index, pj_str_t *result)
+{
+    char line[128];
+    pjsua_acc *acc = &pjsua.acc[acc_index];
+
+    
+    pj_ansi_sprintf(line, "#\n# Account %d:\n#\n", acc_index);
+    pj_strcat2(result, line);
+
+
+    /* Identity */
+    if (acc->local_uri.slen) {
+	pj_ansi_sprintf(line, "--id %.*s\n", 
+			(int)acc->local_uri.slen, 
+			acc->local_uri.ptr);
+	pj_strcat2(result, line);
+    }
+
+    /* Registrar server */
+    if (acc->reg_uri.slen) {
+	pj_ansi_sprintf(line, "--registrar %.*s\n",
+			      (int)acc->reg_uri.slen,
+			      acc->reg_uri.ptr);
+	pj_strcat2(result, line);
+
+	pj_ansi_sprintf(line, "--reg-timeout %u\n",
+			      acc->reg_timeout);
+	pj_strcat2(result, line);
+    }
+
+
+    /* Proxy */
+    if (acc->proxy.slen) {
+	pj_ansi_sprintf(line, "--proxy %.*s\n",
+			      (int)acc->proxy.slen,
+			      acc->proxy.ptr);
+	pj_strcat2(result, line);
+    }
+}
+
+
+
+/*
+ * Dump settings.
+ */
+int pjsua_dump_settings(char *buf, pj_size_t max)
+{
+    int acc_index;
+    int i;
     pj_str_t cfg;
     char line[128];
-    pj_pool_t *pool;
-    FILE *fhnd;
-
-    /* Create pool for temporary buffer. */
-    pool = pj_pool_create(&pjsua.cp.factory, "settings", 4000, 0, NULL);
-    if (!pool)
-	return PJ_ENOMEM;
-
-
-    cfg.ptr = pj_pool_alloc(pool, 3800);
-    if (!cfg.ptr) {
-	pj_pool_release(pool);
-	return PJ_EBUG;
-    }
+
+    cfg.ptr = buf;
     cfg.slen = 0;
 
 
-    /* Identity */
-    if (pjsua.local_uri.slen) {
-	pj_ansi_sprintf(line, "--id %.*s\n", 
-			(int)pjsua.local_uri.slen, 
-			pjsua.local_uri.ptr);
-	pj_strcat2(&cfg, line);
+    /* Logging. */
+    pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
+    pj_ansi_sprintf(line, "--log-level %d\n",
+		    pjsua.log_level);
+    pj_strcat2(&cfg, line);
+
+    pj_ansi_sprintf(line, "--app-log-level %d\n",
+		    pjsua.app_log_level);
+    pj_strcat2(&cfg, line);
+
+    if (pjsua.log_filename) {
+	pj_ansi_sprintf(line, "--log-file %s\n",
+			pjsua.log_filename);
+	pj_strcat2(&cfg, line);
+    }
+
+
+    /* Save account settings. */
+    for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
+	
+	save_account_settings(acc_index, &cfg);
+
+	if (acc_index < pjsua.acc_cnt-1)
+	    pj_strcat2(&cfg, "--next-account\n");
     }
 
     /* Credentials. */
     for (i=0; i<pjsua.cred_count; ++i) {
+
+	pj_ansi_sprintf(line, "#\n# Credential %d:\n#\n", i);
+	pj_strcat2(&cfg, line);
+
 	if (pjsua.cred_info[i].realm.slen) {
 	    pj_ansi_sprintf(line, "--realm %.*s\n",
@@ -626,14 +728,11 @@
 			      pjsua.cred_info[i].data.ptr);
 	pj_strcat2(&cfg, line);
-    }
-
-    /* Registrar server */
-    if (pjsua.registrar_uri.slen) {
-	pj_ansi_sprintf(line, "--registrar %.*s\n",
-			      (int)pjsua.registrar_uri.slen,
-			      pjsua.registrar_uri.ptr);
-	pj_strcat2(&cfg, line);
-    }
-
+
+	if (i < pjsua.cred_count-1)
+	    pj_strcat2(&cfg, "--next-cred\n");
+    }
+
+
+    pj_strcat2(&cfg, "#\n# Network settings:\n#\n");
 
     /* Outbound proxy */
@@ -644,8 +743,4 @@
 	pj_strcat2(&cfg, line);
     }
-
-    /* Media */
-    if (pjsua.null_audio)
-	pj_strcat2(&cfg, "--null-audio\n");
 
 
@@ -673,4 +768,40 @@
 
 
+    pj_strcat2(&cfg, "#\n# Media settings:\n#\n");
+
+
+    /* Media */
+    if (pjsua.null_audio)
+	pj_strcat2(&cfg, "--null-audio\n");
+    if (pjsua.auto_play)
+	pj_strcat2(&cfg, "--auto-play\n");
+    if (pjsua.auto_loop)
+	pj_strcat2(&cfg, "--auto-loop\n");
+    if (pjsua.auto_conf)
+	pj_strcat2(&cfg, "--auto-conf\n");
+    if (pjsua.wav_file) {
+	pj_ansi_sprintf(line, "--play-file %s\n",
+			pjsua.wav_file);
+	pj_strcat2(&cfg, line);
+    }
+
+
+    pj_strcat2(&cfg, "#\n# User agent:\n#\n");
+
+    /* Auto-answer. */
+    if (pjsua.auto_answer != 0) {
+	pj_ansi_sprintf(line, "--auto-answer %d\n",
+			pjsua.auto_answer);
+	pj_strcat2(&cfg, line);
+    }
+
+    /* Max calls. */
+    pj_ansi_sprintf(line, "--max-calls %d\n",
+		    pjsua.max_calls);
+    pj_strcat2(&cfg, line);
+
+
+    pj_strcat2(&cfg, "#\n# Buddies:\n#\n");
+
     /* Add buddies. */
     for (i=0; i<pjsua.buddy_cnt; ++i) {
@@ -682,4 +813,37 @@
 
 
+    *(cfg.ptr + cfg.slen) = '\0';
+    return cfg.slen;
+}
+
+/*
+ * Save settings.
+ */
+pj_status_t pjsua_save_settings(const char *filename)
+{
+    pj_str_t cfg;
+    pj_pool_t *pool;
+    FILE *fhnd;
+
+    /* Create pool for temporary buffer. */
+    pool = pj_pool_create(&pjsua.cp.factory, "settings", 4000, 0, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+
+    cfg.ptr = pj_pool_alloc(pool, 3800);
+    if (!cfg.ptr) {
+	pj_pool_release(pool);
+	return PJ_EBUG;
+    }
+
+
+    cfg.slen = pjsua_dump_settings(cfg.ptr, 3800);
+    if (cfg.slen < 1) {
+	pj_pool_release(pool);
+	return PJ_ENOMEM;
+    }
+
+
     /* Write to file. */
     fhnd = fopen(filename, "wt");
Index: /pjproject/trunk/pjsip/src/pjsua/pjsua_pres.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua_pres.c	(revision 235)
+++ /pjproject/trunk/pjsip/src/pjsua/pjsua_pres.c	(revision 236)
@@ -80,4 +80,5 @@
 static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
 {
+    int acc_index;
     pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
     pjsua_srv_pres *uapres;
@@ -94,7 +95,11 @@
     /* Incoming SUBSCRIBE: */
 
+    /* Find which account for the incoming request. */
+    acc_index = pjsua_find_account_for_incoming(rdata);
+
     /* Create UAS dialog: */
     status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, 
-				   &pjsua.contact_uri, &dlg);
+				   &pjsua.acc[acc_index].contact_uri, 
+				   &dlg);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, 
@@ -131,5 +136,5 @@
 
     /* Add server subscription to the list: */
-    pj_list_push_back(&pjsua.pres_srv_list, uapres);
+    pj_list_push_back(&pjsua.acc[acc_index].pres_srv_list, uapres);
 
 
@@ -147,5 +152,5 @@
     pj_memset(&pres_status, 0, sizeof(pres_status));
     pres_status.info_cnt = 1;
-    pres_status.info[0].basic_open = pjsua.online_status;
+    pres_status.info[0].basic_open = pjsua.acc[acc_index].online_status;
     //Both pjsua.local_uri and pjsua.contact_uri are enclosed in "<" and ">"
     //causing XML parsing to fail.
@@ -175,11 +180,11 @@
 
 /* Refresh subscription (e.g. when our online status has changed) */
-static void refresh_server_subscription()
+static void refresh_server_subscription(int acc_index)
 {
     pjsua_srv_pres *uapres;
 
-    uapres = pjsua.pres_srv_list.next;
-
-    while (uapres != &pjsua.pres_srv_list) {
+    uapres = pjsua.acc[acc_index].pres_srv_list.next;
+
+    while (uapres != &pjsua.acc[acc_index].pres_srv_list) {
 	
 	pjsip_pres_status pres_status;
@@ -187,6 +192,6 @@
 
 	pjsip_pres_get_status(uapres->sub, &pres_status);
-	if (pres_status.info[0].basic_open != pjsua.online_status) {
-	    pres_status.info[0].basic_open = pjsua.online_status;
+	if (pres_status.info[0].basic_open != pjsua.acc[acc_index].online_status) {
+	    pres_status.info[0].basic_open = pjsua.acc[acc_index].online_status;
 	    pjsip_pres_set_status(uapres->sub, &pres_status);
 
@@ -299,11 +304,14 @@
 static void subscribe_buddy_presence(unsigned index)
 {
+    int acc_index;
     pjsip_dialog *dlg;
     pjsip_tx_data *tdata;
     pj_status_t status;
 
+    acc_index = pjsua.buddies[index].acc_index;
+
     status = pjsip_dlg_create_uac( pjsip_ua_instance(), 
-				   &pjsua.local_uri,
-				   &pjsua.contact_uri,
+				   &pjsua.acc[acc_index].local_uri,
+				   &pjsua.acc[acc_index].contact_uri,
 				   &pjsua.buddies[index].uri,
 				   NULL, &dlg);
@@ -326,5 +334,5 @@
 			     &pjsua.buddies[index]);
 
-    status = pjsip_pres_initiate(pjsua.buddies[index].sub, 60, &tdata);
+    status = pjsip_pres_initiate(pjsua.buddies[index].sub, -1, &tdata);
     if (status != PJ_SUCCESS) {
 	pjsua.buddies[index].sub = NULL;
@@ -382,5 +390,5 @@
 static void refresh_client_subscription(void)
 {
-    unsigned i;
+    int i;
 
     for (i=0; i<pjsua.buddy_cnt; ++i) {
@@ -416,8 +424,8 @@
  * Refresh presence
  */
-void pjsua_pres_refresh(void)
+void pjsua_pres_refresh(int acc_index)
 {
     refresh_client_subscription();
-    refresh_server_subscription();
+    refresh_server_subscription(acc_index);
 }
 
@@ -428,11 +436,18 @@
 void pjsua_pres_shutdown(void)
 {
-    unsigned i;
-
-    pjsua.online_status = 0;
+    int acc_index;
+    int i;
+
+    for (acc_index=0; acc_index<pjsua.acc_cnt; ++acc_index) {
+	pjsua.acc[acc_index].online_status = 0;
+    }
+
     for (i=0; i<pjsua.buddy_cnt; ++i) {
 	pjsua.buddies[i].monitor = 0;
     }
-    pjsua_pres_refresh();
+
+    for (acc_index=0; acc_index<pjsua.acc_cnt; ++acc_index) {
+	pjsua_pres_refresh(acc_index);
+    }
 }
 
@@ -442,20 +457,28 @@
 void pjsua_pres_dump(void)
 {
-    unsigned i;
+    int acc_index;
+    int i;
 
     PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
-    if (pj_list_empty(&pjsua.pres_srv_list)) {
-	PJ_LOG(3,(THIS_FILE, "  - none - "));
-    } else {
-	struct pjsua_srv_pres *uapres;
-
-	uapres = pjsua.pres_srv_list.next;
-	while (uapres != &pjsua.pres_srv_list) {
-	
-	    PJ_LOG(3,(THIS_FILE, "  %10s %s",
-		      pjsip_evsub_get_state_name(uapres->sub),
-		      uapres->remote));
-
-	    uapres = uapres->next;
+    for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
+
+	PJ_LOG(3,(THIS_FILE, "  %.*s",
+		  (int)pjsua.acc[acc_index].local_uri.slen,
+		  pjsua.acc[acc_index].local_uri.ptr));
+
+	if (pj_list_empty(&pjsua.acc[acc_index].pres_srv_list)) {
+	    PJ_LOG(3,(THIS_FILE, "  - none - "));
+	} else {
+	    struct pjsua_srv_pres *uapres;
+
+	    uapres = pjsua.acc[acc_index].pres_srv_list.next;
+	    while (uapres != &pjsua.acc[acc_index].pres_srv_list) {
+	    
+		PJ_LOG(3,(THIS_FILE, "    %10s %s",
+			  pjsip_evsub_get_state_name(uapres->sub),
+			  uapres->remote));
+
+		uapres = uapres->next;
+	    }
 	}
     }
Index: /pjproject/trunk/pjsip/src/pjsua/pjsua_reg.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua/pjsua_reg.c	(revision 235)
+++ /pjproject/trunk/pjsip/src/pjsua/pjsua_reg.c	(revision 236)
@@ -35,4 +35,7 @@
 static void regc_cb(struct pjsip_regc_cbparam *param)
 {
+
+    pjsua_acc *acc = param->token;
+
     /*
      * Print registration status.
@@ -41,5 +44,6 @@
 	pjsua_perror(THIS_FILE, "SIP registration error", 
 		     param->status);
-	pjsua.regc = NULL;
+	pjsip_regc_destroy(acc->regc);
+	acc->regc = NULL;
 	
     } else if (param->code < 0 || param->code >= 300) {
@@ -47,12 +51,23 @@
 		   param->code, 
 		   pjsip_get_status_text(param->code)->ptr));
-	pjsua.regc = NULL;
+	pjsip_regc_destroy(acc->regc);
+	acc->regc = NULL;
 
     } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
-	PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), "
-			      "will re-register in %d seconds", 
-			      param->code,
-			      pjsip_get_status_text(param->code)->ptr,
-			      param->expiration));
+
+	if (param->expiration < 1) {
+	    pjsip_regc_destroy(acc->regc);
+	    acc->regc = NULL;
+	    PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
+		      acc->local_uri.ptr));
+	} else {
+	    PJ_LOG(3, (THIS_FILE, 
+		       "%s: registration success, status=%d (%s), "
+		       "will re-register in %d seconds", 
+		       acc->local_uri.ptr,
+		       param->code,
+		       pjsip_get_status_text(param->code)->ptr,
+		       param->expiration));
+	}
 
     } else {
@@ -60,8 +75,8 @@
     }
 
-    pjsua.regc_last_err = param->status;
-    pjsua.regc_last_code = param->code;
+    acc->reg_last_err = param->status;
+    acc->reg_last_code = param->code;
 
-    pjsua_ui_regc_on_state_changed(pjsua.regc_last_code);
+    pjsua_ui_regc_on_state_changed(acc->index);
 }
 
@@ -70,5 +85,5 @@
  * Update registration. If renew is false, then unregistration will be performed.
  */
-void pjsua_regc_update(pj_bool_t renew)
+void pjsua_regc_update(int acc_index, pj_bool_t renew)
 {
     pj_status_t status;
@@ -76,6 +91,6 @@
 
     if (renew) {
-	if (pjsua.regc == NULL) {
-	    status = pjsua_regc_init();
+	if (pjsua.acc[acc_index].regc == NULL) {
+	    status = pjsua_regc_init(acc_index);
 	    if (status != PJ_SUCCESS) {
 		pjsua_perror(THIS_FILE, "Unable to create registration", 
@@ -84,15 +99,15 @@
 	    }
 	}
-	status = pjsip_regc_register(pjsua.regc, 1, &tdata);
+	status = pjsip_regc_register(pjsua.acc[acc_index].regc, 1, &tdata);
     } else {
-	if (pjsua.regc == NULL) {
+	if (pjsua.acc[acc_index].regc == NULL) {
 	    PJ_LOG(3,(THIS_FILE, "Currently not registered"));
 	    return;
 	}
-	status = pjsip_regc_unregister(pjsua.regc, &tdata);
+	status = pjsip_regc_unregister(pjsua.acc[acc_index].regc, &tdata);
     }
 
     if (status == PJ_SUCCESS)
-	status = pjsip_regc_send( pjsua.regc, tdata );
+	status = pjsip_regc_send( pjsua.acc[acc_index].regc, tdata );
 
     if (status != PJ_SUCCESS) {
@@ -108,12 +123,15 @@
  * Initialize client registration.
  */
-pj_status_t pjsua_regc_init(void)
+pj_status_t pjsua_regc_init(int acc_index)
 {
     pj_status_t status;
 
     /* initialize SIP registration if registrar is configured */
-    if (pjsua.registrar_uri.slen) {
+    if (pjsua.acc[acc_index].reg_uri.slen) {
 
-	status = pjsip_regc_create( pjsua.endpt, NULL, &regc_cb, &pjsua.regc);
+	status = pjsip_regc_create( pjsua.endpt, 
+				    &pjsua.acc[acc_index], 
+				    &regc_cb, 
+				    &pjsua.acc[acc_index].regc);
 
 	if (status != PJ_SUCCESS) {
@@ -124,9 +142,10 @@
 
 
-	status = pjsip_regc_init( pjsua.regc, &pjsua.registrar_uri, 
-				  &pjsua.local_uri, 
-				  &pjsua.local_uri,
-				  1, &pjsua.contact_uri, 
-				  pjsua.reg_timeout);
+	status = pjsip_regc_init( pjsua.acc[acc_index].regc, 
+				  &pjsua.acc[acc_index].reg_uri, 
+				  &pjsua.acc[acc_index].local_uri, 
+				  &pjsua.acc[acc_index].local_uri,
+				  1, &pjsua.acc[acc_index].contact_uri, 
+				  pjsua.acc[acc_index].reg_timeout);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, 
@@ -136,8 +155,10 @@
 	}
 
-	pjsip_regc_set_credentials( pjsua.regc, pjsua.cred_count, 
+	pjsip_regc_set_credentials( pjsua.acc[acc_index].regc, 
+				    pjsua.cred_count, 
 				    pjsua.cred_info );
 
-	pjsip_regc_set_route_set( pjsua.regc, &pjsua.route_set );
+	pjsip_regc_set_route_set( pjsua.acc[acc_index].regc, 
+				  &pjsua.acc[acc_index].route_set );
     }
 
