* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/18/181f4cf69afd8581a6d53ed10fe36f4833263e3f.svn-base b/jni/pjproject-android/.svn/pristine/18/181f4cf69afd8581a6d53ed10fe36f4833263e3f.svn-base
new file mode 100644
index 0000000..d1cc04f
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/18/181f4cf69afd8581a6d53ed10fe36f4833263e3f.svn-base
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- 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             -->
+<!--                                                                    -->
+<!--                                                                    -->
+
+<!-- Note:
+     For this test to work, PJSUA-LIB needs to add video line, with
+     this patch:
+
+     pjsua_media.c:1253, after call to pjmedia_endpt_create_sdp():
+
+    if (1) {
+	pjmedia_sdp_media *m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
+	m->desc.media = pj_str("video");
+	m->desc.port = 3000;
+	m->desc.transport = pj_str("RTP/AVP");
+	m->desc.fmt_count = 1;
+	m->desc.fmt[0] = pj_str("0");
+	sdp->media[sdp->media_count++] = m;
+    }
+
+-->
+     
+
+<scenario name="UAC with bad ACK">
+  <!-- UAC with bad ACK causes assertion with pjsip 1.4			-->
+  <send retrans="500">
+    <![CDATA[
+
+      INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+      To: sut <sip:[service]@[remote_ip]:[remote_port]>
+      Call-ID: [call_id]
+      CSeq: 1 INVITE
+      Contact: sip:sipp@[local_ip]:[local_port]
+      Max-Forwards: 70
+      Subject: Performance Test
+      Content-Type: application/sdp
+      Content-Length: [len]
+
+      v=0
+      o=Tester 234 123 IN IP4 89.208.145.194
+      s=Tester
+      c=IN IP4 89.208.145.194
+      t=0 0
+      m=audio 17424 RTP/AVP 111 0 18 101
+      a=rtpmap:111 SPEEX/16000
+      a=rtpmap:0 PCMU/8000
+      a=rtpmap:18 G729/8000
+      a=rtpmap:101 telephone-event/8000
+      a=sendrecv
+      a=rtcp:17425
+      m=video 11128 RTP/AVP 34 103 104
+      a=rtpmap:34 H263/90000
+      a=rtpmap:103 H263-1998/90000
+      a=rtpmap:104 H264/90000
+      a=sendrecv
+      a=rtcp:11129
+
+    ]]>
+  </send>
+
+  <recv response="100"
+        optional="true">
+  </recv>
+
+  <recv response="180" optional="true">
+  </recv>
+
+  <!-- By adding rrs="true" (Record Route Sets), the route sets         -->
+  <!-- are saved and used for following messages sent. Useful to test   -->
+  <!-- against stateful SIP proxies/B2BUAs.                             -->
+  <recv response="200" rtd="true">
+  </recv>
+
+  <!-- Packet lost can be simulated in any send/recv message by         -->
+  <!-- by adding the 'lost = "10"'. Value can be [1-100] percent.       -->
+  <send>
+    <![CDATA[
+
+      ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+      To: sut <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
+      Call-ID: [call_id]
+      CSeq: 1 ACK
+      Contact: sip:sipp@[local_ip]:[local_port]
+      Max-Forwards: 70
+      Subject: Performance Test
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <!-- This delay can be customized by the -d command-line option       -->
+  <!-- or by adding a 'milliseconds = "value"' option here.             -->
+  <pause milliseconds="2000"/>
+
+  <send retrans="500">
+    <![CDATA[
+
+      INVITE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+      To: sut <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
+      Call-ID: [call_id]
+      CSeq: 2 INVITE
+      Contact: sip:sipp@[local_ip]:[local_port]
+      Max-Forwards: 70
+      Subject: Performance Test
+      Content-Type: application/sdp
+      Content-Length: [len]
+
+      v=0
+      o=Tester 234 124 IN IP4 89.208.145.194
+      s=Tester
+      c=IN IP4 89.208.145.194
+      t=0 0
+      m=audio 17424 RTP/AVP 111 0 18 101
+      a=rtpmap:111 SPEEX/16000
+      a=rtpmap:0 PCMU/8000
+      a=rtpmap:18 G729/8000
+      a=rtpmap:101 telephone-event/8000
+      a=sendrecv
+      a=rtcp:17425
+      m=video 0 RTP/AVP 34 103 104
+      a=sendrecv
+
+
+    ]]>
+  </send>
+
+  <!-- By adding rrs="true" (Record Route Sets), the route sets         -->
+  <!-- are saved and used for following messages sent. Useful to test   -->
+  <!-- against stateful SIP proxies/B2BUAs.                             -->
+  <recv response="200" rtd="true">
+  </recv>
+
+  <!-- Packet lost can be simulated in any send/recv message by         -->
+  <!-- by adding the 'lost = "10"'. Value can be [1-100] percent.       -->
+  <send>
+    <![CDATA[
+
+      ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+      To: sut <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
+      Call-ID: [call_id]
+      CSeq: 2 ACK
+      Contact: sip:sipp@[local_ip]:[local_port]
+      Max-Forwards: 70
+      Subject: Performance Test
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+
+  <pause milliseconds="2000"/>
+
+
+  <!-- The 'crlf' option inserts a blank line in the statistics report. -->
+  <send retrans="500">
+    <![CDATA[
+
+      BYE sip:[service]@[remote_ip]:[remote_port] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
+      From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+      To: sut <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
+      Call-ID: [call_id]
+      CSeq: 3 BYE
+      Contact: sip:sipp@[local_ip]:[local_port]
+      Max-Forwards: 70
+      Subject: Performance Test
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="200" crlf="true">
+  </recv>
+
+
+  <!-- definition of the response time repartition table (unit is ms)   -->
+  <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+  <!-- definition of the call length repartition table (unit is ms)     -->
+  <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+
diff --git a/jni/pjproject-android/.svn/pristine/18/183baa97f45396f175325debff11484910f79dc6.svn-base b/jni/pjproject-android/.svn/pristine/18/183baa97f45396f175325debff11484910f79dc6.svn-base
new file mode 100644
index 0000000..18ffd3c
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/18/183baa97f45396f175325debff11484910f79dc6.svn-base
@@ -0,0 +1,164 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 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 <pjmedia/plc.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/wsola.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+static void* plc_wsola_create(pj_pool_t*, unsigned c, unsigned f);
+static void  plc_wsola_save(void*, pj_int16_t*);
+static void  plc_wsola_generate(void*, pj_int16_t*);
+
+/**
+ * This struct is used internally to represent a PLC backend.
+ */
+struct plc_alg
+{
+    void* (*plc_create)(pj_pool_t*, unsigned c, unsigned f);
+    void  (*plc_save)(void*, pj_int16_t*);
+    void  (*plc_generate)(void*, pj_int16_t*);
+};
+
+
+static struct plc_alg plc_wsola =
+{
+    &plc_wsola_create,
+    &plc_wsola_save,
+    &plc_wsola_generate
+};
+
+
+struct pjmedia_plc
+{
+    void	    *obj;
+    struct plc_alg  *op;
+};
+
+
+/*
+ * Create PLC session. This function will select the PLC algorithm to
+ * use based on the arguments.
+ */
+PJ_DEF(pj_status_t) pjmedia_plc_create( pj_pool_t *pool,
+					unsigned clock_rate,
+					unsigned samples_per_frame,
+					unsigned options,
+					pjmedia_plc **p_plc)
+{
+    pjmedia_plc *plc;
+
+    PJ_ASSERT_RETURN(pool && clock_rate && samples_per_frame && p_plc,
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(options);
+
+    plc = PJ_POOL_ZALLOC_T(pool, pjmedia_plc);
+
+    plc->op = &plc_wsola;
+    plc->obj = plc->op->plc_create(pool, clock_rate, samples_per_frame);
+
+    *p_plc = plc;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Save a good frame to PLC.
+ */
+PJ_DEF(pj_status_t) pjmedia_plc_save( pjmedia_plc *plc,
+				      pj_int16_t *frame )
+{
+    PJ_ASSERT_RETURN(plc && frame, PJ_EINVAL);
+ 
+    plc->op->plc_save(plc->obj, frame);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Generate a replacement for lost frame.
+ */
+PJ_DEF(pj_status_t) pjmedia_plc_generate( pjmedia_plc *plc,
+					  pj_int16_t *frame )
+{
+    PJ_ASSERT_RETURN(plc && frame, PJ_EINVAL);
+    
+    plc->op->plc_generate(plc->obj, frame);
+    return PJ_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * Packet loss concealment based on WSOLA
+ */
+struct wsola_plc
+{
+    pjmedia_wsola   *wsola;
+    pj_bool_t	     prev_lost;
+};
+
+
+static void* plc_wsola_create(pj_pool_t *pool, unsigned clock_rate, 
+			      unsigned samples_per_frame)
+{
+    struct wsola_plc *o;
+    unsigned flag;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(clock_rate);
+
+    o = PJ_POOL_ZALLOC_T(pool, struct wsola_plc);
+    o->prev_lost = PJ_FALSE;
+
+    flag = PJMEDIA_WSOLA_NO_DISCARD;
+    if (PJMEDIA_WSOLA_PLC_NO_FADING)
+	flag |= PJMEDIA_WSOLA_NO_FADING;
+
+    status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1,
+				  flag, &o->wsola);
+    if (status != PJ_SUCCESS)
+	return NULL;
+
+    return o;
+}
+
+static void plc_wsola_save(void *plc, pj_int16_t *frame)
+{
+    struct wsola_plc *o = (struct wsola_plc*) plc;
+
+    pjmedia_wsola_save(o->wsola, frame, o->prev_lost);
+    o->prev_lost = PJ_FALSE;
+}
+
+static void plc_wsola_generate(void *plc, pj_int16_t *frame)
+{
+    struct wsola_plc *o = (struct wsola_plc*) plc;
+    
+    pjmedia_wsola_generate(o->wsola, frame);
+    o->prev_lost = PJ_TRUE;
+}
+
+
diff --git a/jni/pjproject-android/.svn/pristine/18/186d2ad7e40db8371edf39be7534bcc8e1b63ea8.svn-base b/jni/pjproject-android/.svn/pristine/18/186d2ad7e40db8371edf39be7534bcc8e1b63ea8.svn-base
new file mode 100644
index 0000000..38eda08
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/18/186d2ad7e40db8371edf39be7534bcc8e1b63ea8.svn-base
@@ -0,0 +1,39 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 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 
+ */
+#ifndef __PJSIP_SIP_AUTOCONF_H__
+#define __PJSIP_SIP_AUTOCONF_H__
+
+/**
+ * @file sip_autoconf.h
+ * @brief Describes operating system specifics (automatically detected by
+ *        autoconf)
+ */
+
+/*
+ * Enable/disable TLS transport, as configured by autoconf.
+ * But only do this if user doesn't explicitly configure in pj/config_site.h.
+ */
+/* Since 1.5, the default setting will follow PJ_HAS_SSL_SOCK setting. */
+//#ifndef PJSIP_HAS_TLS_TRANSPORT
+//#undef PJSIP_HAS_TLS_TRANSPORT
+//#endif
+
+#endif	/* __PJSIP_SIP_AUTOCONF_H__ */
+
diff --git a/jni/pjproject-android/.svn/pristine/18/187a5c4b3ca776174671fe9b97e6fa20900b1603.svn-base b/jni/pjproject-android/.svn/pristine/18/187a5c4b3ca776174671fe9b97e6fa20900b1603.svn-base
new file mode 100644
index 0000000..8ef8a5f
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/18/187a5c4b3ca776174671fe9b97e6fa20900b1603.svn-base
@@ -0,0 +1,126 @@
+/*
+ * kernel_driver.c
+ *
+ * a test driver for the crypto_kernel
+ *
+ * David A. McGrew
+ * Cisco Systems, Inc.
+ */
+/*
+ *	
+ * Copyright(c) 2001-2006 Cisco Systems, Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ *   Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ * 
+ *   Neither the name of the Cisco Systems, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <stdio.h>           /* for printf() */
+#include <unistd.h>          /* for getopt() */
+#include "crypto_kernel.h"
+
+void
+usage(char *prog_name) {
+  printf("usage: %s [ -v ][ -d debug_module ]*\n", prog_name);
+  exit(255);
+}
+
+int
+main (int argc, char *argv[]) {
+  extern char *optarg;
+  int q;
+  int do_validation      = 0;
+  err_status_t status;
+
+  if (argc == 1)
+    usage(argv[0]);
+
+  /* initialize kernel - we need to do this before anything else */ 
+  status = crypto_kernel_init();
+  if (status) {
+    printf("error: crypto_kernel init failed\n");
+    exit(1);
+  }
+  printf("crypto_kernel successfully initalized\n");
+
+  /* process input arguments */
+  while (1) {
+    q = getopt(argc, argv, "vd:");
+    if (q == -1) 
+      break;
+    switch (q) {
+    case 'v':
+      do_validation = 1;
+      break;
+    case 'd':
+      status = crypto_kernel_set_debug_module(optarg, 1);
+      if (status) {
+	printf("error: set debug module (%s) failed\n", optarg);
+	exit(1);
+      }
+      break;
+    default:
+      usage(argv[0]);
+    }    
+  }
+
+  if (do_validation) {
+    printf("checking crypto_kernel status...\n");
+    status = crypto_kernel_status();
+    if (status) {
+      printf("failed\n");
+      exit(1);
+    }
+    printf("crypto_kernel passed self-tests\n");
+  }
+
+  status = crypto_kernel_shutdown();
+  if (status) {
+    printf("error: crypto_kernel shutdown failed\n");
+    exit(1);
+  }
+  printf("crypto_kernel successfully shut down\n");
+  
+  return 0;
+}
+
+/*
+ * crypto_kernel_cipher_test() is a test of the cipher interface
+ * of the crypto_kernel
+ */
+
+err_status_t
+crypto_kernel_cipher_test(void) {
+
+  /* not implemented yet! */
+
+  return err_status_ok;
+}
diff --git a/jni/pjproject-android/.svn/pristine/18/188383f865f24de0596b6db8be8e3ad47a9303a3.svn-base b/jni/pjproject-android/.svn/pristine/18/188383f865f24de0596b6db8be8e3ad47a9303a3.svn-base
new file mode 100644
index 0000000..d5b1879
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/18/188383f865f24de0596b6db8be8e3ad47a9303a3.svn-base
@@ -0,0 +1,116 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 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 "test.h"
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if INCLUDE_OS_TEST
+static int endianness_test32(void)
+{
+    union t
+    {
+	pj_uint32_t u32;
+	pj_uint16_t u16[2];
+	pj_uint8_t u8[4];
+    } t;
+
+    PJ_LOG(3,("", " Testing endianness.."));
+
+    t.u32 = 0x11223344;
+
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN
+    PJ_LOG(3,("", "   Library is set to little endian"));
+
+#  if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN
+#    error Error: Both PJ_IS_LITTLE_ENDIAN and PJ_IS_BIG_ENDIAN are set!
+#  endif
+
+    if ((t.u16[0] & 0xFFFF) != 0x3344 ||
+	(t.u16[1] & 0xFFFF) != 0x1122)
+    {
+	PJ_LOG(3,("", "   Error: wrong 16bit values 0x%x and 0x%x",
+		      (t.u16[0] & 0xFFFF), (t.u16[1] & 0xFFFF)));
+	return 10;
+    }
+
+    if ((t.u8[0] & 0xFF) != 0x44 ||
+	(t.u8[1] & 0xFF) != 0x33 ||
+	(t.u8[2] & 0xFF) != 0x22 ||
+	(t.u8[3] & 0xFF) != 0x11)
+    {
+	PJ_LOG(3,("", "   Error: wrong 8bit values"));
+	return 12;
+    }
+
+#elif defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN
+    PJ_LOG(3,("", "   Library is set to big endian"));
+
+    if ((t.u16[0] & 0xFFFF) != 0x1122 ||
+	(t.u16[1] & 0xFFFF) != 0x3344)
+    {
+	PJ_LOG(3,("", "   Error: wrong 16bit values 0x%x and 0x%x",
+		      (t.u16[0] & 0xFFFF), (t.u16[1] & 0xFFFF)));
+	return 20;
+    }
+
+    if ((t.u8[0] & 0xFF) != 0x11 ||
+	(t.u8[1] & 0xFF) != 0x22 ||
+	(t.u8[2] & 0xFF) != 0x33 ||
+	(t.u8[3] & 0xFF) != 0x44)
+    {
+	PJ_LOG(3,("", "   Error: wrong 8bit values"));
+	return 22;
+    }
+
+#  if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN
+#    error Error: Both PJ_IS_LITTLE_ENDIAN and PJ_IS_BIG_ENDIAN are set!
+#  endif
+
+
+#else
+#    error Error: Endianness is not set properly!
+#endif
+
+    return 0;
+}
+
+int os_test(void)
+{
+    const pj_sys_info *si;
+    int rc = 0;
+
+    PJ_LOG(3,("", " Sys info:"));
+    si = pj_get_sys_info();
+    PJ_LOG(3,("", "   machine:  %s", si->machine.ptr));
+    PJ_LOG(3,("", "   os_name:  %s", si->os_name.ptr));
+    PJ_LOG(3,("", "   os_ver:   0x%x", si->os_ver));
+    PJ_LOG(3,("", "   sdk_name: %s", si->sdk_name.ptr));
+    PJ_LOG(3,("", "   sdk_ver:  0x%x", si->sdk_ver));
+    PJ_LOG(3,("", "   info:     %s", si->info.ptr));
+
+    rc = endianness_test32();
+
+    return rc;
+}
+
+#else
+int dummy_os_var;
+#endif
+
diff --git a/jni/pjproject-android/.svn/pristine/18/18b80243e0b616b3acd84c47a8f7dcb99fb5b24b.svn-base b/jni/pjproject-android/.svn/pristine/18/18b80243e0b616b3acd84c47a8f7dcb99fb5b24b.svn-base
new file mode 100644
index 0000000..796ed47
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/18/18b80243e0b616b3acd84c47a8f7dcb99fb5b24b.svn-base
@@ -0,0 +1,490 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 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 "turn.h"
+#include <pj/compat/socket.h>
+
+#if PJ_HAS_TCP
+
+struct accept_op
+{
+    pj_ioqueue_op_key_t	op_key;
+    pj_sock_t		sock;
+    pj_sockaddr		src_addr;
+    int			src_addr_len;
+};
+
+struct tcp_listener
+{
+    pj_turn_listener	     base;
+    pj_ioqueue_key_t	    *key;
+    unsigned		     accept_cnt;
+    struct accept_op	    *accept_op;	/* Array of accept_op's	*/
+};
+
+
+static void lis_on_accept_complete(pj_ioqueue_key_t *key, 
+				   pj_ioqueue_op_key_t *op_key, 
+				   pj_sock_t sock, 
+				   pj_status_t status);
+static pj_status_t lis_destroy(pj_turn_listener *listener);
+static void transport_create(pj_sock_t sock, pj_turn_listener *lis,
+			     pj_sockaddr_t *src_addr, int src_addr_len);
+
+static void show_err(const char *sender, const char *title, 
+		     pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+    PJ_LOG(4,(sender, "%s: %s", title, errmsg));
+}
+
+
+/*
+ * Create a new listener on the specified port.
+ */
+PJ_DEF(pj_status_t) pj_turn_listener_create_tcp(pj_turn_srv *srv,
+					        int af,
+					        const pj_str_t *bound_addr,
+					        unsigned port,
+						unsigned concurrency_cnt,
+						unsigned flags,
+						pj_turn_listener **p_listener)
+{
+    pj_pool_t *pool;
+    struct tcp_listener *tcp_lis;
+    pj_ioqueue_callback ioqueue_cb;
+    unsigned i;
+    pj_status_t status;
+
+    /* Create structure */
+    pool = pj_pool_create(srv->core.pf, "tcpl%p", 1000, 1000, NULL);
+    tcp_lis = PJ_POOL_ZALLOC_T(pool, struct tcp_listener);
+    tcp_lis->base.pool = pool;
+    tcp_lis->base.obj_name = pool->obj_name;
+    tcp_lis->base.server = srv;
+    tcp_lis->base.tp_type = PJ_TURN_TP_TCP;
+    tcp_lis->base.sock = PJ_INVALID_SOCKET;
+    //tcp_lis->base.sendto = &tcp_sendto;
+    tcp_lis->base.destroy = &lis_destroy;
+    tcp_lis->accept_cnt = concurrency_cnt;
+    tcp_lis->base.flags = flags;
+
+    /* Create socket */
+    status = pj_sock_socket(af, pj_SOCK_STREAM(), 0, &tcp_lis->base.sock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Init bind address */
+    status = pj_sockaddr_init(af, &tcp_lis->base.addr, bound_addr, 
+			      (pj_uint16_t)port);
+    if (status != PJ_SUCCESS) 
+	goto on_error;
+    
+    /* Create info */
+    pj_ansi_strcpy(tcp_lis->base.info, "TCP:");
+    pj_sockaddr_print(&tcp_lis->base.addr, tcp_lis->base.info+4, 
+		      sizeof(tcp_lis->base.info)-4, 3);
+
+    /* Bind socket */
+    status = pj_sock_bind(tcp_lis->base.sock, &tcp_lis->base.addr, 
+			  pj_sockaddr_get_len(&tcp_lis->base.addr));
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Listen() */
+    status = pj_sock_listen(tcp_lis->base.sock, 5);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Register to ioqueue */
+    pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
+    ioqueue_cb.on_accept_complete = &lis_on_accept_complete;
+    status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, tcp_lis->base.sock,
+				      tcp_lis, &ioqueue_cb, &tcp_lis->key);
+
+    /* Create op keys */
+    tcp_lis->accept_op = (struct accept_op*)pj_pool_calloc(pool, concurrency_cnt,
+						    sizeof(struct accept_op));
+
+    /* Create each accept_op and kick off read operation */
+    for (i=0; i<concurrency_cnt; ++i) {
+	lis_on_accept_complete(tcp_lis->key, &tcp_lis->accept_op[i].op_key, 
+			       PJ_INVALID_SOCKET, PJ_EPENDING);
+    }
+
+    /* Done */
+    PJ_LOG(4,(tcp_lis->base.obj_name, "Listener %s created", 
+	   tcp_lis->base.info));
+
+    *p_listener = &tcp_lis->base;
+    return PJ_SUCCESS;
+
+
+on_error:
+    lis_destroy(&tcp_lis->base);
+    return status;
+}
+
+
+/*
+ * Destroy listener.
+ */
+static pj_status_t lis_destroy(pj_turn_listener *listener)
+{
+    struct tcp_listener *tcp_lis = (struct tcp_listener *)listener;
+    unsigned i;
+
+    if (tcp_lis->key) {
+	pj_ioqueue_unregister(tcp_lis->key);
+	tcp_lis->key = NULL;
+	tcp_lis->base.sock = PJ_INVALID_SOCKET;
+    } else if (tcp_lis->base.sock != PJ_INVALID_SOCKET) {
+	pj_sock_close(tcp_lis->base.sock);
+	tcp_lis->base.sock = PJ_INVALID_SOCKET;
+    }
+
+    for (i=0; i<tcp_lis->accept_cnt; ++i) {
+	/* Nothing to do */
+    }
+
+    if (tcp_lis->base.pool) {
+	pj_pool_t *pool = tcp_lis->base.pool;
+
+	PJ_LOG(4,(tcp_lis->base.obj_name, "Listener %s destroyed", 
+		  tcp_lis->base.info));
+
+	tcp_lis->base.pool = NULL;
+	pj_pool_release(pool);
+    }
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Callback on new TCP connection.
+ */
+static void lis_on_accept_complete(pj_ioqueue_key_t *key, 
+				   pj_ioqueue_op_key_t *op_key, 
+				   pj_sock_t sock, 
+				   pj_status_t status)
+{
+    struct tcp_listener *tcp_lis;
+    struct accept_op *accept_op = (struct accept_op*) op_key;
+
+    tcp_lis = (struct tcp_listener*) pj_ioqueue_get_user_data(key);
+
+    PJ_UNUSED_ARG(sock);
+
+    do {
+	/* Report new connection. */
+	if (status == PJ_SUCCESS) {
+	    char addr[PJ_INET6_ADDRSTRLEN+8];
+	    PJ_LOG(5,(tcp_lis->base.obj_name, "Incoming TCP from %s",
+		      pj_sockaddr_print(&accept_op->src_addr, addr,
+					sizeof(addr), 3)));
+	    transport_create(accept_op->sock, &tcp_lis->base,
+			     &accept_op->src_addr, accept_op->src_addr_len);
+	} else if (status != PJ_EPENDING) {
+	    show_err(tcp_lis->base.obj_name, "accept()", status);
+	}
+
+	/* Prepare next accept() */
+	accept_op->src_addr_len = sizeof(accept_op->src_addr);
+	status = pj_ioqueue_accept(key, op_key, &accept_op->sock,
+				   NULL,
+				   &accept_op->src_addr,
+				   &accept_op->src_addr_len);
+
+    } while (status != PJ_EPENDING && status != PJ_ECANCELLED &&
+	     status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL));
+}
+
+
+/****************************************************************************/
+/*
+ * Transport
+ */
+enum
+{
+    TIMER_NONE,
+    TIMER_DESTROY
+};
+
+/* The delay in seconds to be applied before TCP transport is destroyed when 
+ * no allocation is referencing it. This also means the initial time to wait
+ * after the initial TCP connection establishment to receive a valid STUN
+ * message in the transport.
+ */
+#define SHUTDOWN_DELAY  10
+
+struct recv_op
+{
+    pj_ioqueue_op_key_t	op_key;
+    pj_turn_pkt		pkt;
+};
+
+struct tcp_transport
+{
+    pj_turn_transport	 base;
+    pj_pool_t		*pool;
+    pj_timer_entry	 timer;
+
+    pj_turn_allocation	*alloc;
+    int			 ref_cnt;
+
+    pj_sock_t		 sock;
+    pj_ioqueue_key_t	*key;
+    struct recv_op	 recv_op;
+    pj_ioqueue_op_key_t	 send_op;
+};
+
+
+static void tcp_on_read_complete(pj_ioqueue_key_t *key, 
+				 pj_ioqueue_op_key_t *op_key, 
+				 pj_ssize_t bytes_read);
+
+static pj_status_t tcp_sendto(pj_turn_transport *tp,
+			      const void *packet,
+			      pj_size_t size,
+			      unsigned flag,
+			      const pj_sockaddr_t *addr,
+			      int addr_len);
+static void tcp_destroy(struct tcp_transport *tcp);
+static void tcp_add_ref(pj_turn_transport *tp,
+			pj_turn_allocation *alloc);
+static void tcp_dec_ref(pj_turn_transport *tp,
+			pj_turn_allocation *alloc);
+static void timer_callback(pj_timer_heap_t *timer_heap,
+			   pj_timer_entry *entry);
+
+static void transport_create(pj_sock_t sock, pj_turn_listener *lis,
+			     pj_sockaddr_t *src_addr, int src_addr_len)
+{
+    pj_pool_t *pool;
+    struct tcp_transport *tcp;
+    pj_ioqueue_callback cb;
+    pj_status_t status;
+
+    pool = pj_pool_create(lis->server->core.pf, "tcp%p", 1000, 1000, NULL);
+
+    tcp = PJ_POOL_ZALLOC_T(pool, struct tcp_transport);
+    tcp->base.obj_name = pool->obj_name;
+    tcp->base.listener = lis;
+    tcp->base.info = lis->info;
+    tcp->base.sendto = &tcp_sendto;
+    tcp->base.add_ref = &tcp_add_ref;
+    tcp->base.dec_ref = &tcp_dec_ref;
+    tcp->pool = pool;
+    tcp->sock = sock;
+
+    pj_timer_entry_init(&tcp->timer, TIMER_NONE, tcp, &timer_callback);
+
+    /* Register to ioqueue */
+    pj_bzero(&cb, sizeof(cb));
+    cb.on_read_complete = &tcp_on_read_complete;
+    status = pj_ioqueue_register_sock(pool, lis->server->core.ioqueue, sock,
+				      tcp, &cb, &tcp->key);
+    if (status != PJ_SUCCESS) {
+	tcp_destroy(tcp);
+	return;
+    }
+
+    /* Init pkt */
+    tcp->recv_op.pkt.pool = pj_pool_create(lis->server->core.pf, "tcpkt%p", 
+					   1000, 1000, NULL);
+    tcp->recv_op.pkt.transport = &tcp->base;
+    tcp->recv_op.pkt.src.tp_type = PJ_TURN_TP_TCP;
+    tcp->recv_op.pkt.src_addr_len = src_addr_len;
+    pj_memcpy(&tcp->recv_op.pkt.src.clt_addr, src_addr, src_addr_len);
+
+    tcp_on_read_complete(tcp->key, &tcp->recv_op.op_key, -PJ_EPENDING);
+    /* Should not access transport from now, it may have been destroyed */
+}
+
+
+static void tcp_destroy(struct tcp_transport *tcp)
+{
+    if (tcp->key) {
+	pj_ioqueue_unregister(tcp->key);
+	tcp->key = NULL;
+	tcp->sock = 0;
+    } else if (tcp->sock) {
+	pj_sock_close(tcp->sock);
+	tcp->sock = 0;
+    }
+
+    if (tcp->pool) {
+	pj_pool_release(tcp->pool);
+    }
+}
+
+
+static void timer_callback(pj_timer_heap_t *timer_heap,
+			   pj_timer_entry *entry)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*) entry->user_data;
+
+    PJ_UNUSED_ARG(timer_heap);
+
+    tcp_destroy(tcp);
+}
+
+
+static void tcp_on_read_complete(pj_ioqueue_key_t *key, 
+				 pj_ioqueue_op_key_t *op_key, 
+				 pj_ssize_t bytes_read)
+{
+    struct tcp_transport *tcp;
+    struct recv_op *recv_op = (struct recv_op*) op_key;
+    pj_status_t status;
+
+    tcp = (struct tcp_transport*) pj_ioqueue_get_user_data(key);
+
+    do {
+	/* Report to server or allocation, if we have allocation */
+	if (bytes_read > 0) {
+
+	    recv_op->pkt.len = bytes_read;
+	    pj_gettimeofday(&recv_op->pkt.rx_time);
+
+	    tcp_add_ref(&tcp->base, NULL);
+
+	    if (tcp->alloc) {
+		pj_turn_allocation_on_rx_client_pkt(tcp->alloc, &recv_op->pkt);
+	    } else {
+		pj_turn_srv_on_rx_pkt(tcp->base.listener->server, &recv_op->pkt);
+	    }
+
+	    pj_assert(tcp->ref_cnt > 0);
+	    tcp_dec_ref(&tcp->base, NULL);
+
+	} else if (bytes_read != -PJ_EPENDING) {
+	    /* TCP connection closed/error. Notify client and then destroy 
+	     * ourselves.
+	     * Note: the -PJ_EPENDING is the value passed during init.
+	     */
+	    ++tcp->ref_cnt;
+
+	    if (tcp->alloc) {
+		if (bytes_read != 0) {
+		    show_err(tcp->base.obj_name, "TCP socket error", 
+			     -bytes_read);
+		} else {
+		    PJ_LOG(5,(tcp->base.obj_name, "TCP socket closed"));
+		}
+		pj_turn_allocation_on_transport_closed(tcp->alloc, &tcp->base);
+		tcp->alloc = NULL;
+	    }
+
+	    pj_assert(tcp->ref_cnt > 0);
+	    if (--tcp->ref_cnt == 0) {
+		tcp_destroy(tcp);
+		return;
+	    }
+	}
+
+	/* Reset pool */
+	pj_pool_reset(recv_op->pkt.pool);
+
+	/* If packet is full discard it */
+	if (recv_op->pkt.len == sizeof(recv_op->pkt.pkt)) {
+	    PJ_LOG(4,(tcp->base.obj_name, "Buffer discarded"));
+	    recv_op->pkt.len = 0;
+	}
+
+	/* Read next packet */
+	bytes_read = sizeof(recv_op->pkt.pkt) - recv_op->pkt.len;
+	status = pj_ioqueue_recv(tcp->key, op_key,
+				 recv_op->pkt.pkt + recv_op->pkt.len, 
+				 &bytes_read, 0);
+
+	if (status != PJ_EPENDING && status != PJ_SUCCESS)
+	    bytes_read = -status;
+
+    } while (status != PJ_EPENDING && status != PJ_ECANCELLED &&
+	     status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL));
+
+}
+
+
+static pj_status_t tcp_sendto(pj_turn_transport *tp,
+			      const void *packet,
+			      pj_size_t size,
+			      unsigned flag,
+			      const pj_sockaddr_t *addr,
+			      int addr_len)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*) tp;
+    pj_ssize_t length = size;
+
+    PJ_UNUSED_ARG(addr);
+    PJ_UNUSED_ARG(addr_len);
+
+    return pj_ioqueue_send(tcp->key, &tcp->send_op, packet, &length, flag);
+}
+
+
+static void tcp_add_ref(pj_turn_transport *tp,
+			pj_turn_allocation *alloc)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*) tp;
+
+    ++tcp->ref_cnt;
+
+    if (tcp->alloc == NULL && alloc) {
+	tcp->alloc = alloc;
+    }
+
+    /* Cancel shutdown timer if it's running */
+    if (tcp->timer.id != TIMER_NONE) {
+	pj_timer_heap_cancel(tcp->base.listener->server->core.timer_heap,
+			     &tcp->timer);
+	tcp->timer.id = TIMER_NONE;
+    }
+}
+
+
+static void tcp_dec_ref(pj_turn_transport *tp,
+			pj_turn_allocation *alloc)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*) tp;
+
+    --tcp->ref_cnt;
+
+    if (alloc && alloc == tcp->alloc) {
+	tcp->alloc = NULL;
+    }
+
+    if (tcp->ref_cnt == 0 && tcp->timer.id == TIMER_NONE) {
+	pj_time_val delay = { SHUTDOWN_DELAY, 0 };
+	tcp->timer.id = TIMER_DESTROY;
+	pj_timer_heap_schedule(tcp->base.listener->server->core.timer_heap,
+			       &tcp->timer, &delay);
+    }
+}
+
+#else	/* PJ_HAS_TCP */
+
+/* To avoid empty translation unit warning */
+int listener_tcp_dummy = 0;
+
+#endif	/* PJ_HAS_TCP */
+
diff --git a/jni/pjproject-android/.svn/pristine/18/18f585f5c05dcd1e2d4e91a2f735a6d971f43b2c.svn-base b/jni/pjproject-android/.svn/pristine/18/18f585f5c05dcd1e2d4e91a2f735a6d971f43b2c.svn-base
new file mode 100644
index 0000000..8533c79
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/18/18f585f5c05dcd1e2d4e91a2f735a6d971f43b2c.svn-base
@@ -0,0 +1,3459 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 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-lib/pjsua.h>
+#include <pjsua-lib/pjsua_internal.h>
+
+
+#define THIS_FILE		"pjsua_acc.c"
+
+enum
+{
+    OUTBOUND_UNKNOWN,	// status unknown
+    OUTBOUND_WANTED,	// initiated in registration
+    OUTBOUND_ACTIVE,	// got positive response from server
+    OUTBOUND_NA		// not wanted or got negative response from server
+};
+
+
+static void schedule_reregistration(pjsua_acc *acc);
+static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te);
+
+/*
+ * Get number of current accounts.
+ */
+PJ_DEF(unsigned) pjsua_acc_get_count(void)
+{
+    return pjsua_var.acc_cnt;
+}
+
+
+/*
+ * Check if the specified account ID is valid.
+ */
+PJ_DEF(pj_bool_t) pjsua_acc_is_valid(pjsua_acc_id acc_id)
+{
+    return acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc) &&
+	   pjsua_var.acc[acc_id].valid;
+}
+
+
+/*
+ * Set default account
+ */
+PJ_DEF(pj_status_t) pjsua_acc_set_default(pjsua_acc_id acc_id)
+{
+    pjsua_var.default_acc = acc_id;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get default account.
+ */
+PJ_DEF(pjsua_acc_id) pjsua_acc_get_default(void)
+{
+    return pjsua_var.default_acc;
+}
+
+
+/*
+ * Copy account configuration.
+ */
+PJ_DEF(void) pjsua_acc_config_dup( pj_pool_t *pool,
+				   pjsua_acc_config *dst,
+				   const pjsua_acc_config *src)
+{
+    unsigned i;
+
+    pj_memcpy(dst, src, sizeof(pjsua_acc_config));
+
+    pj_strdup_with_null(pool, &dst->id, &src->id);
+    pj_strdup_with_null(pool, &dst->reg_uri, &src->reg_uri);
+    pj_strdup_with_null(pool, &dst->force_contact, &src->force_contact);
+    pj_strdup_with_null(pool, &dst->contact_params, &src->contact_params);
+    pj_strdup_with_null(pool, &dst->contact_uri_params,
+                        &src->contact_uri_params);
+    pj_strdup_with_null(pool, &dst->pidf_tuple_id, &src->pidf_tuple_id);
+    pj_strdup_with_null(pool, &dst->rfc5626_instance_id,
+                        &src->rfc5626_instance_id);
+    pj_strdup_with_null(pool, &dst->rfc5626_reg_id, &src->rfc5626_reg_id);
+
+    dst->proxy_cnt = src->proxy_cnt;
+    for (i=0; i<src->proxy_cnt; ++i)
+	pj_strdup_with_null(pool, &dst->proxy[i], &src->proxy[i]);
+
+    dst->reg_timeout = src->reg_timeout;
+    dst->reg_delay_before_refresh = src->reg_delay_before_refresh;
+    dst->cred_count = src->cred_count;
+
+    for (i=0; i<src->cred_count; ++i) {
+	pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]);
+    }
+
+    pj_list_init(&dst->reg_hdr_list);
+    if (!pj_list_empty(&src->reg_hdr_list)) {
+	const pjsip_hdr *hdr;
+
+	hdr = src->reg_hdr_list.next;
+	while (hdr != &src->reg_hdr_list) {
+	    pj_list_push_back(&dst->reg_hdr_list, pjsip_hdr_clone(pool, hdr));
+	    hdr = hdr->next;
+	}
+    }
+
+    pj_list_init(&dst->sub_hdr_list);
+    if (!pj_list_empty(&src->sub_hdr_list)) {
+	const pjsip_hdr *hdr;
+
+	hdr = src->sub_hdr_list.next;
+	while (hdr != &src->sub_hdr_list) {
+	    pj_list_push_back(&dst->sub_hdr_list, pjsip_hdr_clone(pool, hdr));
+	    hdr = hdr->next;
+	}
+    }
+
+    pjsip_auth_clt_pref_dup(pool, &dst->auth_pref, &src->auth_pref);
+
+    pjsua_transport_config_dup(pool, &dst->rtp_cfg, &src->rtp_cfg);
+
+    pjsua_ice_config_dup(pool, &dst->ice_cfg, &src->ice_cfg);
+    pjsua_turn_config_dup(pool, &dst->turn_cfg, &src->turn_cfg);
+
+    pj_strdup(pool, &dst->ka_data, &src->ka_data);
+}
+
+/*
+ * Calculate CRC of proxy list.
+ */
+static pj_uint32_t calc_proxy_crc(const pj_str_t proxy[], pj_size_t cnt)
+{
+    pj_crc32_context ctx;
+    unsigned i;
+    
+    pj_crc32_init(&ctx);
+    for (i=0; i<cnt; ++i) {
+	pj_crc32_update(&ctx, (pj_uint8_t*)proxy[i].ptr, proxy[i].slen);
+    }
+
+    return pj_crc32_final(&ctx);
+}
+
+/*
+ * Initialize a new account (after configuration is set).
+ */
+static pj_status_t initialize_acc(unsigned acc_id)
+{
+    pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
+    pjsua_acc *acc = &pjsua_var.acc[acc_id];
+    pjsip_name_addr *name_addr;
+    pjsip_sip_uri *sip_reg_uri;
+    pj_status_t status;
+    unsigned i;
+
+    /* Need to parse local_uri to get the elements: */
+
+    name_addr = (pjsip_name_addr*)
+		    pjsip_parse_uri(acc->pool, acc_cfg->id.ptr,
+				    acc_cfg->id.slen, 
+				    PJSIP_PARSE_URI_AS_NAMEADDR);
+    if (name_addr == NULL) {
+	pjsua_perror(THIS_FILE, "Invalid local URI", 
+		     PJSIP_EINVALIDURI);
+	return PJSIP_EINVALIDURI;
+    }
+
+    /* Local URI MUST be a SIP or SIPS: */
+    if (!PJSIP_URI_SCHEME_IS_SIP(name_addr) && 
+	!PJSIP_URI_SCHEME_IS_SIPS(name_addr)) 
+    {
+	acc->display = name_addr->display;
+	acc->user_part = name_addr->display;
+	acc->srv_domain = pj_str("");
+	acc->srv_port = 0;
+    } else {
+	pjsip_sip_uri *sip_uri;
+
+	/* Get the SIP URI object: */
+	sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(name_addr);
+
+	/* Save the user and domain part. These will be used when finding an
+	 * account for incoming requests.
+	 */
+	acc->display = name_addr->display;
+	acc->user_part = sip_uri->user;
+	acc->srv_domain = sip_uri->host;
+	acc->srv_port = 0;
+    }
+    acc->is_sips = PJSIP_URI_SCHEME_IS_SIPS(name_addr);
+
+
+    /* Parse registrar URI, if any */
+    if (acc_cfg->reg_uri.slen) {
+	pjsip_uri *reg_uri;
+
+	reg_uri = pjsip_parse_uri(acc->pool, acc_cfg->reg_uri.ptr,
+				  acc_cfg->reg_uri.slen, 0);
+	if (reg_uri == NULL) {
+	    pjsua_perror(THIS_FILE, "Invalid registrar URI", 
+			 PJSIP_EINVALIDURI);
+	    return PJSIP_EINVALIDURI;
+	}
+
+	/* Registrar URI MUST be a SIP or SIPS: */
+	if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) && 
+	    !PJSIP_URI_SCHEME_IS_SIPS(reg_uri)) 
+	{
+	    pjsua_perror(THIS_FILE, "Invalid registar URI", 
+			 PJSIP_EINVALIDSCHEME);
+	    return PJSIP_EINVALIDSCHEME;
+	}
+
+	sip_reg_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri);
+
+    } else {
+	sip_reg_uri = NULL;
+    }
+
+    if (sip_reg_uri) {
+	acc->srv_port = sip_reg_uri->port;
+    }
+
+    /* Create Contact header if not present. */
+    //if (acc_cfg->contact.slen == 0) {
+    //	acc_cfg->contact = acc_cfg->id;
+    //}
+
+    /* Build account route-set from outbound proxies and route set from 
+     * account configuration.
+     */
+    pj_list_init(&acc->route_set);
+
+    if (!pj_list_empty(&pjsua_var.outbound_proxy)) {
+	pjsip_route_hdr *r;
+
+	r = pjsua_var.outbound_proxy.next;
+	while (r != &pjsua_var.outbound_proxy) {
+	    pj_list_push_back(&acc->route_set,
+			      pjsip_hdr_shallow_clone(acc->pool, r));
+	    r = r->next;
+	}
+    }
+
+    for (i=0; i<acc_cfg->proxy_cnt; ++i) {
+    	pj_str_t hname = { "Route", 5};
+	pjsip_route_hdr *r;
+	pj_str_t tmp;
+
+	pj_strdup_with_null(acc->pool, &tmp, &acc_cfg->proxy[i]);
+	r = (pjsip_route_hdr*)
+	    pjsip_parse_hdr(acc->pool, &hname, tmp.ptr, tmp.slen, NULL);
+	if (r == NULL) {
+	    pjsua_perror(THIS_FILE, "Invalid URI in account route set",
+			 PJ_EINVAL);
+	    return PJ_EINVAL;
+	}
+	pj_list_push_back(&acc->route_set, r);
+    }
+
+    /* Concatenate credentials from account config and global config */
+    acc->cred_cnt = 0;
+    for (i=0; i<acc_cfg->cred_count; ++i) {
+	acc->cred[acc->cred_cnt++] = acc_cfg->cred_info[i];
+    }
+    for (i=0; i<pjsua_var.ua_cfg.cred_count && 
+	      acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i)
+    {
+	acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i];
+    }
+
+    /* If account's ICE and TURN customization is not set, then
+     * initialize it with the settings from the global media config.
+     */
+    if (acc->cfg.ice_cfg_use == PJSUA_ICE_CONFIG_USE_DEFAULT) {
+	pjsua_ice_config_from_media_config(NULL, &acc->cfg.ice_cfg,
+	                                &pjsua_var.media_cfg);
+    }
+    if (acc->cfg.turn_cfg_use == PJSUA_TURN_CONFIG_USE_DEFAULT) {
+	pjsua_turn_config_from_media_config(NULL, &acc->cfg.turn_cfg,
+	                                    &pjsua_var.media_cfg);
+    }
+
+    /* If ICE is enabled, add "+sip.ice" media feature tag in account's
+     * contact params.
+     */
+#if PJSUA_ADD_ICE_TAGS
+    if (acc_cfg->ice_cfg.enable_ice) {
+	pj_ssize_t new_len;
+	pj_str_t new_prm;
+
+	new_len = acc_cfg->contact_params.slen + 10;
+	new_prm.ptr = (char*)pj_pool_alloc(acc->pool, new_len);
+	pj_strcpy(&new_prm, &acc_cfg->contact_params);
+	pj_strcat2(&new_prm, ";+sip.ice");
+	acc_cfg->contact_params = new_prm;
+    }
+#endif
+
+    status = pjsua_pres_init_acc(acc_id);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* If SIP outbound is enabled, generate instance and reg ID if they are
+     * not specified
+     */
+    if (acc_cfg->use_rfc5626) {
+	if (acc_cfg->rfc5626_instance_id.slen==0) {
+	    const pj_str_t *hostname;
+	    pj_uint32_t hval;
+	    pj_size_t pos;
+	    char instprm[] = ";+sip.instance=\"<urn:uuid:00000000-0000-0000-0000-0000CCDDEEFF>\"";
+
+	    hostname = pj_gethostname();
+	    pos = pj_ansi_strlen(instprm) - 10;
+	    hval = pj_hash_calc(0, hostname->ptr, (unsigned)hostname->slen);
+	    pj_val_to_hex_digit( ((char*)&hval)[0], instprm+pos+0);
+	    pj_val_to_hex_digit( ((char*)&hval)[1], instprm+pos+2);
+	    pj_val_to_hex_digit( ((char*)&hval)[2], instprm+pos+4);
+	    pj_val_to_hex_digit( ((char*)&hval)[3], instprm+pos+6);
+
+	    pj_strdup2(acc->pool, &acc->rfc5626_instprm, instprm);
+	} else {
+	    const char *prmname = ";+sip.instance=\"";
+	    pj_size_t len;
+
+	    len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_instance_id.slen + 1;
+	    acc->rfc5626_instprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
+	    pj_ansi_snprintf(acc->rfc5626_instprm.ptr, len+1,
+		             "%s%.*s\"",
+		             prmname,
+		             (int)acc_cfg->rfc5626_instance_id.slen,
+		             acc_cfg->rfc5626_instance_id.ptr);
+	    acc->rfc5626_instprm.slen = len;
+	}
+
+	if (acc_cfg->rfc5626_reg_id.slen==0) {
+	    acc->rfc5626_regprm = pj_str(";reg-id=1");
+	} else {
+	    const char *prmname = ";reg-id=";
+	    pj_size_t len;
+
+	    len = pj_ansi_strlen(prmname) + acc_cfg->rfc5626_reg_id.slen;
+	    acc->rfc5626_regprm.ptr = (char*)pj_pool_alloc(acc->pool, len+1);
+	    pj_ansi_snprintf(acc->rfc5626_regprm.ptr, len+1,
+		             "%s%.*s\"",
+		             prmname,
+		             (int)acc_cfg->rfc5626_reg_id.slen,
+		             acc_cfg->rfc5626_reg_id.ptr);
+	    acc->rfc5626_regprm.slen = len;
+	}
+    }
+
+    /* Mark account as valid */
+    pjsua_var.acc[acc_id].valid = PJ_TRUE;
+
+    /* Insert account ID into account ID array, sorted by priority */
+    for (i=0; i<pjsua_var.acc_cnt; ++i) {
+	if ( pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority <
+	     pjsua_var.acc[acc_id].cfg.priority)
+	{
+	    break;
+	}
+    }
+    pj_array_insert(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]),
+		    pjsua_var.acc_cnt, i, &acc_id);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Add a new account to pjsua.
+ */
+PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
+				   pj_bool_t is_default,
+				   pjsua_acc_id *p_acc_id)
+{
+    pjsua_acc *acc;
+    unsigned i, id;
+    pj_status_t status = PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(cfg, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_var.acc_cnt < PJ_ARRAY_SIZE(pjsua_var.acc),
+		     PJ_ETOOMANY);
+
+    /* Must have a transport */
+    PJ_ASSERT_RETURN(pjsua_var.tpdata[0].data.ptr != NULL, PJ_EINVALIDOP);
+
+    PJ_LOG(4,(THIS_FILE, "Adding account: id=%.*s",
+	      (int)cfg->id.slen, cfg->id.ptr));
+    pj_log_push_indent();
+
+    PJSUA_LOCK();
+
+    /* Find empty account id. */
+    for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.acc); ++id) {
+	if (pjsua_var.acc[id].valid == PJ_FALSE)
+	    break;
+    }
+
+    /* Expect to find a slot */
+    PJ_ASSERT_ON_FAIL(	id < PJ_ARRAY_SIZE(pjsua_var.acc), 
+			{PJSUA_UNLOCK(); return PJ_EBUG;});
+
+    acc = &pjsua_var.acc[id];
+
+    /* Create pool for this account. */
+    if (acc->pool)
+	pj_pool_reset(acc->pool);
+    else
+	acc->pool = pjsua_pool_create("acc%p", 512, 256);
+
+    /* Copy config */
+    pjsua_acc_config_dup(acc->pool, &pjsua_var.acc[id].cfg, cfg);
+    
+    /* Normalize registration timeout and refresh delay */
+    if (pjsua_var.acc[id].cfg.reg_uri.slen) {
+        if (pjsua_var.acc[id].cfg.reg_timeout == 0) {
+            pjsua_var.acc[id].cfg.reg_timeout = PJSUA_REG_INTERVAL;
+        }
+        if (pjsua_var.acc[id].cfg.reg_delay_before_refresh == 0) {
+            pjsua_var.acc[id].cfg.reg_delay_before_refresh =
+                PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH;
+        }
+    }
+
+    /* Check the route URI's and force loose route if required */
+    for (i=0; i<acc->cfg.proxy_cnt; ++i) {
+	status = normalize_route_uri(acc->pool, &acc->cfg.proxy[i]);
+	if (status != PJ_SUCCESS) {
+	    PJSUA_UNLOCK();
+	    pj_log_pop_indent();
+	    return status;
+	}
+    }
+
+    /* Get CRC of account proxy setting */
+    acc->local_route_crc = calc_proxy_crc(acc->cfg.proxy, acc->cfg.proxy_cnt);
+
+    /* Get CRC of global outbound proxy setting */
+    acc->global_route_crc=calc_proxy_crc(pjsua_var.ua_cfg.outbound_proxy,
+					 pjsua_var.ua_cfg.outbound_proxy_cnt);
+
+    status = initialize_acc(id);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error adding account", status);
+	pj_pool_release(acc->pool);
+	acc->pool = NULL;
+	PJSUA_UNLOCK();
+	pj_log_pop_indent();
+	return status;
+    }
+
+    if (is_default)
+	pjsua_var.default_acc = id;
+
+    if (p_acc_id)
+	*p_acc_id = id;
+
+    pjsua_var.acc_cnt++;
+
+    PJSUA_UNLOCK();
+
+    PJ_LOG(4,(THIS_FILE, "Account %.*s added with id %d",
+	      (int)cfg->id.slen, cfg->id.ptr, id));
+
+    /* If accounts has registration enabled, start registration */
+    if (pjsua_var.acc[id].cfg.reg_uri.slen) {
+	if (pjsua_var.acc[id].cfg.register_on_acc_add)
+            pjsua_acc_set_registration(id, PJ_TRUE);
+    } else {
+	/* Otherwise subscribe to MWI, if it's enabled */
+	if (pjsua_var.acc[id].cfg.mwi_enabled)
+	    pjsua_start_mwi(id, PJ_TRUE);
+
+	/* Start publish too */
+	if (acc->cfg.publish_enabled)
+	    pjsua_pres_init_publish_acc(id);
+    }
+
+    pj_log_pop_indent();
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Add local account
+ */
+PJ_DEF(pj_status_t) pjsua_acc_add_local( pjsua_transport_id tid,
+					 pj_bool_t is_default,
+					 pjsua_acc_id *p_acc_id)
+{
+    pjsua_acc_config cfg;
+    pjsua_transport_data *t = &pjsua_var.tpdata[tid];
+    const char *beginquote, *endquote;
+    char transport_param[32];
+    char uri[PJSIP_MAX_URL_SIZE];
+
+    /* ID must be valid */
+    PJ_ASSERT_RETURN(tid>=0 && tid<(int)PJ_ARRAY_SIZE(pjsua_var.tpdata), 
+		     PJ_EINVAL);
+
+    /* Transport must be valid */
+    PJ_ASSERT_RETURN(t->data.ptr != NULL, PJ_EINVAL);
+    
+    pjsua_acc_config_default(&cfg);
+
+    /* Lower the priority of local account */
+    --cfg.priority;
+
+    /* Enclose IPv6 address in square brackets */
+    if (t->type & PJSIP_TRANSPORT_IPV6) {
+	beginquote = "[";
+	endquote = "]";
+    } else {
+	beginquote = endquote = "";
+    }
+
+    /* Don't add transport parameter if it's UDP */
+    if (t->type!=PJSIP_TRANSPORT_UDP && t->type!=PJSIP_TRANSPORT_UDP6) {
+	pj_ansi_snprintf(transport_param, sizeof(transport_param),
+		         ";transport=%s",
+			 pjsip_transport_get_type_name(t->type));
+    } else {
+	transport_param[0] = '\0';
+    }
+
+    /* Build URI for the account */
+    pj_ansi_snprintf(uri, PJSIP_MAX_URL_SIZE,
+		     "<sip:%s%.*s%s:%d%s>", 
+		     beginquote,
+		     (int)t->local_name.host.slen,
+		     t->local_name.host.ptr,
+		     endquote,
+		     t->local_name.port,
+		     transport_param);
+
+    cfg.id = pj_str(uri);
+    
+    return pjsua_acc_add(&cfg, is_default, p_acc_id);
+}
+
+
+/*
+ * Set arbitrary data to be associated with the account.
+ */
+PJ_DEF(pj_status_t) pjsua_acc_set_user_data(pjsua_acc_id acc_id,
+					    void *user_data)
+{
+    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
+
+    PJSUA_LOCK();
+
+    pjsua_var.acc[acc_id].cfg.user_data = user_data;
+
+    PJSUA_UNLOCK();
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Retrieve arbitrary data associated with the account.
+ */
+PJ_DEF(void*) pjsua_acc_get_user_data(pjsua_acc_id acc_id)
+{
+    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
+		     NULL);
+    PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, NULL);
+
+    return pjsua_var.acc[acc_id].cfg.user_data;
+}
+
+
+/*
+ * Delete account.
+ */
+PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id)
+{
+    pjsua_acc *acc;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
+
+    PJ_LOG(4,(THIS_FILE, "Deleting account %d..", acc_id));
+    pj_log_push_indent();
+
+    PJSUA_LOCK();
+
+    acc = &pjsua_var.acc[acc_id];
+
+    /* Cancel keep-alive timer, if any */
+    if (acc->ka_timer.id) {
+	pjsip_endpt_cancel_timer(pjsua_var.endpt, &acc->ka_timer);
+	acc->ka_timer.id = PJ_FALSE;
+    }
+    if (acc->ka_transport) {
+	pjsip_transport_dec_ref(acc->ka_transport);
+	acc->ka_transport = NULL;
+    }
+
+    /* Cancel any re-registration timer */
+    if (acc->auto_rereg.timer.id) {
+	acc->auto_rereg.timer.id = PJ_FALSE;
+	pjsua_cancel_timer(&acc->auto_rereg.timer);
+    }
+
+    /* Delete registration */
+    if (acc->regc != NULL) {
+	pjsua_acc_set_registration(acc_id, PJ_FALSE);
+	if (acc->regc) {
+	    pjsip_regc_destroy(acc->regc);
+	}
+	acc->regc = NULL;
+    }
+
+    /* Terminate mwi subscription */
+    if (acc->cfg.mwi_enabled) {
+        acc->cfg.mwi_enabled = PJ_FALSE;
+        pjsua_start_mwi(acc_id, PJ_FALSE);
+    }
+
+    /* Delete server presence subscription */
+    pjsua_pres_delete_acc(acc_id, 0);
+
+    /* Release account pool */
+    if (acc->pool) {
+	pj_pool_release(acc->pool);
+	acc->pool = NULL;
+    }
+
+    /* Invalidate */
+    acc->valid = PJ_FALSE;
+    acc->contact.slen = 0;
+    acc->reg_mapped_addr.slen = 0;
+    pj_bzero(&acc->via_addr, sizeof(acc->via_addr));
+    acc->via_tp = NULL;
+    acc->next_rtp_port = 0;
+
+    /* Remove from array */
+    for (i=0; i<pjsua_var.acc_cnt; ++i) {
+	if (pjsua_var.acc_ids[i] == acc_id)
+	    break;
+    }
+    if (i != pjsua_var.acc_cnt) {
+	pj_array_erase(pjsua_var.acc_ids, sizeof(pjsua_var.acc_ids[0]),
+		       pjsua_var.acc_cnt, i);
+	--pjsua_var.acc_cnt;
+    }
+
+    /* Leave the calls intact, as I don't think calls need to
+     * access account once it's created
+     */
+
+    /* Update default account */
+    if (pjsua_var.default_acc == acc_id)
+	pjsua_var.default_acc = 0;
+
+    PJSUA_UNLOCK();
+
+    PJ_LOG(4,(THIS_FILE, "Account id %d deleted", acc_id));
+
+    pj_log_pop_indent();
+    return PJ_SUCCESS;
+}
+
+
+/* Get config */
+PJ_DEF(pj_status_t) pjsua_acc_get_config(pjsua_acc_id acc_id,
+                                         pj_pool_t *pool,
+                                         pjsua_acc_config *acc_cfg)
+{
+    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc)
+                     && pjsua_var.acc[acc_id].valid, PJ_EINVAL);
+    //this now would not work due to corrupt header list
+    //pj_memcpy(acc_cfg, &pjsua_var.acc[acc_id].cfg, sizeof(*acc_cfg));
+    pjsua_acc_config_dup(pool, acc_cfg, &pjsua_var.acc[acc_id].cfg);
+    return PJ_SUCCESS;
+}
+
+/* Compare two SIP headers. Return zero if equal */
+static int pjsip_hdr_cmp(const pjsip_hdr *h1, const pjsip_hdr *h2)
+{
+    char buf1[PJSIP_MAX_URL_SIZE];
+    char buf2[PJSIP_MAX_URL_SIZE];
+    pj_str_t p1, p2;
+
+    p1.ptr = buf1;
+    p1.slen = 0;
+    p2.ptr = buf2;
+    p2.slen = 0;
+
+    p1.slen = pjsip_hdr_print_on((void*)h1, buf1, sizeof(buf1));
+    if (p1.slen < 0)
+	p1.slen = 0;
+    p2.slen = pjsip_hdr_print_on((void*)h2, buf2, sizeof(buf2));
+    if (p2.slen < 0)
+	p2.slen = 0;
+
+    return pj_strcmp(&p1, &p2);
+}
+
+/* Update SIP header list from another list. Return PJ_TRUE if
+ * the list has been updated */
+static pj_bool_t update_hdr_list(pj_pool_t *pool, pjsip_hdr *dst,
+                                 const pjsip_hdr *src)
+{
+    pjsip_hdr *dst_i;
+    const pjsip_hdr *src_i;
+    pj_bool_t changed = PJ_FALSE;
+
+    /* Remove header that's no longer needed */
+    for (dst_i = dst->next; dst_i != dst; ) {
+	for (src_i = src->next; src_i != src; src_i = src_i->next) {
+	    if (pjsip_hdr_cmp(dst_i, src_i) == 0)
+		break;
+	}
+	if (src_i == src) {
+	    pjsip_hdr *next = dst_i->next;
+	    pj_list_erase(dst_i);
+	    changed = PJ_TRUE;
+	    dst_i = next;
+	} else {
+	    dst_i = dst_i->next;
+	}
+    }
+
+    /* Add new header */
+    for (src_i = src->next; src_i != src; src_i = src_i->next) {
+	for (dst_i = dst->next; dst_i != dst; dst_i = dst_i->next) {
+	    if (pjsip_hdr_cmp(dst_i, src_i) == 0)
+		break;
+	}
+	if (dst_i == dst) {
+	    dst_i = pjsip_hdr_clone(pool, src_i);
+	    pj_list_push_back(dst, dst_i);
+	    changed = PJ_TRUE;
+	}
+    }
+
+    return changed;
+}
+
+/*
+ * Modify account information.
+ */
+PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id,
+				      const pjsua_acc_config *cfg)
+{
+    pjsua_acc *acc;
+    pjsip_name_addr *id_name_addr = NULL;
+    pjsip_sip_uri *id_sip_uri = NULL;
+    pjsip_sip_uri *reg_sip_uri = NULL;
+    pj_uint32_t local_route_crc, global_route_crc;
+    pjsip_route_hdr global_route;
+    pjsip_route_hdr local_route;
+    pj_str_t acc_proxy[PJSUA_ACC_MAX_PROXIES];
+    pj_bool_t update_reg = PJ_FALSE;
+    pj_bool_t unreg_first = PJ_FALSE;
+    pj_bool_t update_mwi = PJ_FALSE;
+    pj_status_t status = PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
+		     PJ_EINVAL);
+
+    PJ_LOG(4,(THIS_FILE, "Modifying accunt %d", acc_id));
+    pj_log_push_indent();
+
+    PJSUA_LOCK();
+
+    acc = &pjsua_var.acc[acc_id];
+    if (!acc->valid) {
+	status = PJ_EINVAL;
+	goto on_return;
+    }
+
+    /* == Validate first == */
+
+    /* Account id */
+    if (pj_strcmp(&acc->cfg.id, &cfg->id)) {
+	/* Need to parse id to get the elements: */
+	id_name_addr = (pjsip_name_addr*)
+			pjsip_parse_uri(acc->pool, cfg->id.ptr, cfg->id.slen,
+					PJSIP_PARSE_URI_AS_NAMEADDR);
+	if (id_name_addr == NULL) {
+	    status = PJSIP_EINVALIDURI;
+	    pjsua_perror(THIS_FILE, "Invalid local URI", status);
+	    goto on_return;
+	}
+
+	/* URI MUST be a SIP or SIPS: */
+	if (!PJSIP_URI_SCHEME_IS_SIP(id_name_addr) && 
+	    !PJSIP_URI_SCHEME_IS_SIPS(id_name_addr)) 
+	{
+	    status = PJSIP_EINVALIDSCHEME;
+	    pjsua_perror(THIS_FILE, "Invalid local URI", status);
+	    goto on_return;
+	}
+
+	/* Get the SIP URI object: */
+	id_sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(id_name_addr);
+    }
+
+    /* Registrar URI */
+    if (pj_strcmp(&acc->cfg.reg_uri, &cfg->reg_uri) && cfg->reg_uri.slen) {
+	pjsip_uri *reg_uri;
+
+	/* Need to parse reg_uri to get the elements: */
+	reg_uri = pjsip_parse_uri(acc->pool, cfg->reg_uri.ptr, 
+				  cfg->reg_uri.slen, 0);
+	if (reg_uri == NULL) {
+	    status = PJSIP_EINVALIDURI;
+	    pjsua_perror(THIS_FILE, "Invalid registrar URI", status);
+	    goto on_return;
+	}
+
+	/* Registrar URI MUST be a SIP or SIPS: */
+	if (!PJSIP_URI_SCHEME_IS_SIP(reg_uri) && 
+	    !PJSIP_URI_SCHEME_IS_SIPS(reg_uri)) 
+	{
+	    status = PJSIP_EINVALIDSCHEME;
+	    pjsua_perror(THIS_FILE, "Invalid registar URI", status);
+	    goto on_return;
+	}
+
+	reg_sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(reg_uri);
+    }
+
+    /* REGISTER header list */
+    if (update_hdr_list(acc->pool, &acc->cfg.reg_hdr_list, &cfg->reg_hdr_list)) {
+	update_reg = PJ_TRUE;
+	unreg_first = PJ_TRUE;
+    }
+
+    /* SUBSCRIBE header list */
+    update_hdr_list(acc->pool, &acc->cfg.sub_hdr_list, &cfg->sub_hdr_list);
+
+    /* Global outbound proxy */
+    global_route_crc = calc_proxy_crc(pjsua_var.ua_cfg.outbound_proxy, 
+				      pjsua_var.ua_cfg.outbound_proxy_cnt);
+    if (global_route_crc != acc->global_route_crc) {
+	pjsip_route_hdr *r;
+
+	/* Copy from global outbound proxies */
+	pj_list_init(&global_route);
+	r = pjsua_var.outbound_proxy.next;
+	while (r != &pjsua_var.outbound_proxy) {
+	    pj_list_push_back(&global_route,
+		              pjsip_hdr_shallow_clone(acc->pool, r));
+	    r = r->next;
+	}
+    }
+
+    /* Account proxy */
+    local_route_crc = calc_proxy_crc(cfg->proxy, cfg->proxy_cnt);
+    if (local_route_crc != acc->local_route_crc) {
+	pjsip_route_hdr *r;
+	unsigned i;
+
+	/* Validate the local route and save it to temporary var */
+	pj_list_init(&local_route);
+	for (i=0; i<cfg->proxy_cnt; ++i) {
+	    pj_str_t hname = { "Route", 5};
+
+	    pj_strdup_with_null(acc->pool, &acc_proxy[i], &cfg->proxy[i]);
+	    status = normalize_route_uri(acc->pool, &acc_proxy[i]);
+	    if (status != PJ_SUCCESS)
+		goto on_return;
+	    r = (pjsip_route_hdr*)
+		pjsip_parse_hdr(acc->pool, &hname, acc_proxy[i].ptr, 
+				acc_proxy[i].slen, NULL);
+	    if (r == NULL) {
+		status = PJSIP_EINVALIDURI;
+		pjsua_perror(THIS_FILE, "Invalid URI in account route set",
+			     status);
+		goto on_return;
+	    }
+
+	    pj_list_push_back(&local_route, r);
+	}
+
+	/* Recalculate the CRC again after route URI normalization */
+	local_route_crc = calc_proxy_crc(acc_proxy, cfg->proxy_cnt);
+    }
+
+
+    /* == Apply the new config == */
+
+    /* Account ID. */
+    if (id_name_addr && id_sip_uri) {
+	pj_strdup_with_null(acc->pool, &acc->cfg.id, &cfg->id);
+	pj_strdup_with_null(acc->pool, &acc->display, &id_name_addr->display);
+	pj_strdup_with_null(acc->pool, &acc->user_part, &id_sip_uri->user);
+	pj_strdup_with_null(acc->pool, &acc->srv_domain, &id_sip_uri->host);
+	acc->srv_port = 0;
+	acc->is_sips = PJSIP_URI_SCHEME_IS_SIPS(id_name_addr);
+	update_reg = PJ_TRUE;
+	unreg_first = PJ_TRUE;
+    }
+
+    /* User data */
+    acc->cfg.user_data = cfg->user_data;
+
+    /* Priority */
+    if (acc->cfg.priority != cfg->priority) {
+	unsigned i;
+
+	acc->cfg.priority = cfg->priority;
+	
+	/* Resort accounts priority */
+	for (i=0; i<pjsua_var.acc_cnt; ++i) {
+	    if (pjsua_var.acc_ids[i] == acc_id)
+		break;
+	}
+	pj_assert(i < pjsua_var.acc_cnt);
+	pj_array_erase(pjsua_var.acc_ids, sizeof(acc_id),
+		       pjsua_var.acc_cnt, i);
+	for (i=0; i<pjsua_var.acc_cnt; ++i) {
+	    if (pjsua_var.acc[pjsua_var.acc_ids[i]].cfg.priority <
+		acc->cfg.priority)
+	    {
+		break;
+	    }
+	}
+	pj_array_insert(pjsua_var.acc_ids, sizeof(acc_id),
+			pjsua_var.acc_cnt, i, &acc_id);
+    }
+
+    /* MWI */
+    if (acc->cfg.mwi_enabled != cfg->mwi_enabled) {
+	acc->cfg.mwi_enabled = cfg->mwi_enabled;
+	update_mwi = PJ_TRUE;
+    }
+    if (acc->cfg.mwi_expires != cfg->mwi_expires && cfg->mwi_expires > 0) {
+	acc->cfg.mwi_expires = cfg->mwi_expires;
+	update_mwi = PJ_TRUE;
+    }
+
+    /* PIDF tuple ID */
+    if (pj_strcmp(&acc->cfg.pidf_tuple_id, &cfg->pidf_tuple_id))
+	pj_strdup_with_null(acc->pool, &acc->cfg.pidf_tuple_id,
+			    &cfg->pidf_tuple_id);
+
+    /* Publish */
+    acc->cfg.publish_opt = cfg->publish_opt;
+    acc->cfg.unpublish_max_wait_time_msec = cfg->unpublish_max_wait_time_msec;
+    if (acc->cfg.publish_enabled != cfg->publish_enabled) {
+	acc->cfg.publish_enabled = cfg->publish_enabled;
+	if (!acc->cfg.publish_enabled)
+	    pjsua_pres_unpublish(acc, 0);
+	else
+	    update_reg = PJ_TRUE;
+    }
+
+    /* Force contact URI */
+    if (pj_strcmp(&acc->cfg.force_contact, &cfg->force_contact)) {
+	pj_strdup_with_null(acc->pool, &acc->cfg.force_contact,
+			    &cfg->force_contact);
+	update_reg = PJ_TRUE;
+	unreg_first = PJ_TRUE;
+    }
+
+    /* Contact param */
+    if (pj_strcmp(&acc->cfg.contact_params, &cfg->contact_params)) {
+	pj_strdup_with_null(acc->pool, &acc->cfg.contact_params,
+			    &cfg->contact_params);
+	update_reg = PJ_TRUE;
+    }
+
+    /* Contact URI params */
+    if (pj_strcmp(&acc->cfg.contact_uri_params, &cfg->contact_uri_params)) {
+	pj_strdup_with_null(acc->pool, &acc->cfg.contact_uri_params,
+			    &cfg->contact_uri_params);
+	update_reg = PJ_TRUE;
+    }
+
+    /* Reliable provisional response */
+    acc->cfg.require_100rel = cfg->require_100rel;
+
+    /* Session timer */
+    acc->cfg.use_timer = cfg->use_timer;
+    acc->cfg.timer_setting = cfg->timer_setting;
+
+    /* Transport */
+    if (acc->cfg.transport_id != cfg->transport_id) {
+	acc->cfg.transport_id = cfg->transport_id;
+	update_reg = PJ_TRUE;
+	unreg_first = PJ_TRUE;
+    }
+
+    /* Update keep-alive */
+    if (acc->cfg.ka_interval != cfg->ka_interval ||
+	pj_strcmp(&acc->cfg.ka_data, &cfg->ka_data))
+    {
+	pjsip_transport *ka_transport = acc->ka_transport;
+
+	if (acc->ka_timer.id) {
+	    pjsip_endpt_cancel_timer(pjsua_var.endpt, &acc->ka_timer);
+	    acc->ka_timer.id = PJ_FALSE;
+	}
+	if (acc->ka_transport) {
+	    pjsip_transport_dec_ref(acc->ka_transport);
+	    acc->ka_transport = NULL;
+	}
+
+	acc->cfg.ka_interval = cfg->ka_interval;
+
+	if (cfg->ka_interval) {
+	    if (ka_transport) {
+		/* Keep-alive has been running so we can just restart it */
+		pj_time_val delay;
+
+		pjsip_transport_add_ref(ka_transport);
+		acc->ka_transport = ka_transport;
+
+		acc->ka_timer.cb = &keep_alive_timer_cb;
+		acc->ka_timer.user_data = (void*)acc;
+
+		delay.sec = acc->cfg.ka_interval;
+		delay.msec = 0;
+		status = pjsua_schedule_timer(&acc->ka_timer, &delay);
+		if (status == PJ_SUCCESS) {
+		    acc->ka_timer.id = PJ_TRUE;
+		} else {
+		    pjsip_transport_dec_ref(ka_transport);
+		    acc->ka_transport = NULL;
+		    pjsua_perror(THIS_FILE, "Error starting keep-alive timer",
+		                 status);
+		}
+
+	    } else {
+		/* Keep-alive has not been running, we need to (re)register
+		 * first.
+		 */
+		update_reg = PJ_TRUE;
+	    }
+	}
+    }
+
+    if (pj_strcmp(&acc->cfg.ka_data, &cfg->ka_data))
+	pj_strdup(acc->pool, &acc->cfg.ka_data, &cfg->ka_data);
+#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
+    acc->cfg.use_srtp = cfg->use_srtp;
+    acc->cfg.srtp_secure_signaling = cfg->srtp_secure_signaling;
+    acc->cfg.srtp_optional_dup_offer = cfg->srtp_optional_dup_offer;    
+#endif
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0)
+    acc->cfg.use_stream_ka = cfg->use_stream_ka;
+#endif
+
+    /* Use of proxy */
+    if (acc->cfg.reg_use_proxy != cfg->reg_use_proxy) {
+        acc->cfg.reg_use_proxy = cfg->reg_use_proxy;
+        update_reg = PJ_TRUE;
+	unreg_first = PJ_TRUE;
+    }
+
+    /* Global outbound proxy */
+    if (global_route_crc != acc->global_route_crc) {
+	unsigned i;
+	pj_size_t rcnt;
+
+	/* Remove the outbound proxies from the route set */
+	rcnt = pj_list_size(&acc->route_set);
+	for (i=0; i < rcnt - acc->cfg.proxy_cnt; ++i) {
+	    pjsip_route_hdr *r = acc->route_set.next;
+	    pj_list_erase(r);
+	}
+
+	/* Insert the outbound proxies to the beginning of route set */
+	pj_list_merge_first(&acc->route_set, &global_route);
+
+	/* Update global route CRC */
+	acc->global_route_crc = global_route_crc;
+
+	update_reg = PJ_TRUE;
+	unreg_first = PJ_TRUE;
+    }
+
+    /* Account proxy */
+    if (local_route_crc != acc->local_route_crc) {
+	unsigned i;
+
+	/* Remove the current account proxies from the route set */
+	for (i=0; i < acc->cfg.proxy_cnt; ++i) {
+	    pjsip_route_hdr *r = acc->route_set.prev;
+	    pj_list_erase(r);
+	}
+
+	/* Insert new proxy setting to the route set */
+	pj_list_merge_last(&acc->route_set, &local_route);
+
+	/* Update the proxy setting */
+	acc->cfg.proxy_cnt = cfg->proxy_cnt;
+	for (i = 0; i < cfg->proxy_cnt; ++i)
+	    acc->cfg.proxy[i] = acc_proxy[i];
+
+	/* Update local route CRC */
+	acc->local_route_crc = local_route_crc;
+
+	update_reg = PJ_TRUE;
+	unreg_first = PJ_TRUE;
+    }
+
+    /* Credential info */
+    {
+	unsigned i;
+	pj_bool_t cred_changed = PJ_FALSE;
+
+	/* Selective update credential info. */
+	for (i = 0; i < cfg->cred_count; ++i) {
+	    unsigned j;
+	    pjsip_cred_info ci;
+
+	    /* Find if this credential is already listed */
+	    for (j = i; j < acc->cfg.cred_count; ++j) {
+		if (pjsip_cred_info_cmp(&acc->cfg.cred_info[j], 
+					&cfg->cred_info[i]) == 0)
+		{
+		    /* Found, but different index/position, swap */
+		    if (j != i) {
+			ci = acc->cfg.cred_info[i];
+			acc->cfg.cred_info[i] = acc->cfg.cred_info[j];
+			acc->cfg.cred_info[j] = ci;
+		    }
+		    break;
+		}
+	    }
+
+	    /* Not found, insert this */
+	    if (j == acc->cfg.cred_count) {
+		cred_changed = PJ_TRUE;
+
+		/* If account credential is full, discard the last one. */
+		if (acc->cfg.cred_count == PJ_ARRAY_SIZE(acc->cfg.cred_info)) {
+    		    pj_array_erase(acc->cfg.cred_info, sizeof(pjsip_cred_info),
+				   acc->cfg.cred_count, acc->cfg.cred_count-1);
+		    acc->cfg.cred_count--;
+		}
+
+		/* Insert this */
+		pjsip_cred_info_dup(acc->pool, &ci, &cfg->cred_info[i]);
+		pj_array_insert(acc->cfg.cred_info, sizeof(pjsip_cred_info),
+				acc->cfg.cred_count, i, &ci);
+	    }
+	}
+	acc->cfg.cred_count = cfg->cred_count;
+
+	/* Concatenate credentials from account config and global config */
+	acc->cred_cnt = 0;
+	for (i=0; i<acc->cfg.cred_count; ++i) {
+	    acc->cred[acc->cred_cnt++] = acc->cfg.cred_info[i];
+	}
+	for (i=0; i<pjsua_var.ua_cfg.cred_count && 
+		  acc->cred_cnt < PJ_ARRAY_SIZE(acc->cred); ++i)
+	{
+	    acc->cred[acc->cred_cnt++] = pjsua_var.ua_cfg.cred_info[i];
+	}
+
+	if (cred_changed) {
+	    update_reg = PJ_TRUE;
+	    unreg_first = PJ_TRUE;
+	}
+    }
+
+    /* Authentication preference */
+    acc->cfg.auth_pref.initial_auth = cfg->auth_pref.initial_auth;
+    if (pj_strcmp(&acc->cfg.auth_pref.algorithm, &cfg->auth_pref.algorithm)) {
+	pj_strdup_with_null(acc->pool, &acc->cfg.auth_pref.algorithm, 
+			    &cfg->auth_pref.algorithm);
+	update_reg = PJ_TRUE;
+	unreg_first = PJ_TRUE;
+    }
+
+    /* Registration */
+    if (acc->cfg.reg_timeout != cfg->reg_timeout) {
+	acc->cfg.reg_timeout = cfg->reg_timeout;
+	if (acc->regc != NULL)
+	    pjsip_regc_update_expires(acc->regc, acc->cfg.reg_timeout);
+
+	update_reg = PJ_TRUE;
+    }
+    acc->cfg.unreg_timeout = cfg->unreg_timeout;
+    acc->cfg.allow_contact_rewrite = cfg->allow_contact_rewrite;
+    acc->cfg.reg_retry_interval = cfg->reg_retry_interval;
+    acc->cfg.reg_first_retry_interval = cfg->reg_first_retry_interval;
+    acc->cfg.drop_calls_on_reg_fail = cfg->drop_calls_on_reg_fail;
+    acc->cfg.register_on_acc_add = cfg->register_on_acc_add;
+    if (acc->cfg.reg_delay_before_refresh != cfg->reg_delay_before_refresh) {
+        acc->cfg.reg_delay_before_refresh = cfg->reg_delay_before_refresh;
+	if (acc->regc != NULL)
+	    pjsip_regc_set_delay_before_refresh(acc->regc,
+						cfg->reg_delay_before_refresh);
+    }
+
+    /* Allow via rewrite */
+    if (acc->cfg.allow_via_rewrite != cfg->allow_via_rewrite) {
+        if (acc->regc != NULL) {
+            if (cfg->allow_via_rewrite) {
+                pjsip_regc_set_via_sent_by(acc->regc, &acc->via_addr,
+                                           acc->via_tp);
+            } else
+                pjsip_regc_set_via_sent_by(acc->regc, NULL, NULL);
+        }
+        if (acc->publish_sess != NULL) {
+            if (cfg->allow_via_rewrite) {
+                pjsip_publishc_set_via_sent_by(acc->publish_sess,
+                                               &acc->via_addr, acc->via_tp);
+            } else
+                pjsip_publishc_set_via_sent_by(acc->publish_sess, NULL, NULL);
+        }
+        acc->cfg.allow_via_rewrite = cfg->allow_via_rewrite;
+    }
+
+    /* Normalize registration timeout and refresh delay */
+    if (acc->cfg.reg_uri.slen ) {
+        if (acc->cfg.reg_timeout == 0) {
+            acc->cfg.reg_timeout = PJSUA_REG_INTERVAL;
+        }
+        if (acc->cfg.reg_delay_before_refresh == 0) {
+	    acc->cfg.reg_delay_before_refresh =
+                PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH;
+        }
+    }
+
+    /* Registrar URI */
+    if (pj_strcmp(&acc->cfg.reg_uri, &cfg->reg_uri)) {
+	if (cfg->reg_uri.slen) {
+	    pj_strdup_with_null(acc->pool, &acc->cfg.reg_uri, &cfg->reg_uri);
+	    if (reg_sip_uri)
+		acc->srv_port = reg_sip_uri->port;
+	} else {
+	    /* Unregister if registration was set */
+	    if (acc->cfg.reg_uri.slen)
+		pjsua_acc_set_registration(acc->index, PJ_FALSE);
+	    pj_bzero(&acc->cfg.reg_uri, sizeof(acc->cfg.reg_uri));
+	}
+	update_reg = PJ_TRUE;
+	unreg_first = PJ_TRUE;
+    }
+
+    /* SIP outbound setting */
+    if (acc->cfg.use_rfc5626 != cfg->use_rfc5626 ||
+	pj_strcmp(&acc->cfg.rfc5626_instance_id, &cfg->rfc5626_instance_id) ||
+	pj_strcmp(&acc->cfg.rfc5626_reg_id, &cfg->rfc5626_reg_id))
+    {
+	update_reg = PJ_TRUE;
+    }
+
+    /* Video settings */
+    acc->cfg.vid_in_auto_show = cfg->vid_in_auto_show;
+    acc->cfg.vid_out_auto_transmit = cfg->vid_out_auto_transmit;
+    acc->cfg.vid_wnd_flags = cfg->vid_wnd_flags;
+    acc->cfg.vid_cap_dev = cfg->vid_cap_dev;
+    acc->cfg.vid_rend_dev = cfg->vid_rend_dev;
+    acc->cfg.vid_stream_rc_cfg = cfg->vid_stream_rc_cfg;
+
+    /* Media settings */
+    if (pj_stricmp(&acc->cfg.rtp_cfg.public_addr, &cfg->rtp_cfg.public_addr) ||
+	pj_stricmp(&acc->cfg.rtp_cfg.bound_addr, &cfg->rtp_cfg.bound_addr))
+    {
+	pjsua_transport_config_dup(acc->pool, &acc->cfg.rtp_cfg,
+				   &cfg->rtp_cfg);
+    } else {
+	/* ..to save memory by not using the pool */
+	acc->cfg.rtp_cfg =  cfg->rtp_cfg;
+    }
+
+    acc->cfg.ipv6_media_use = cfg->ipv6_media_use;
+
+    /* STUN and Media customization */
+    if (acc->cfg.sip_stun_use != cfg->sip_stun_use) {
+	acc->cfg.sip_stun_use = cfg->sip_stun_use;
+	update_reg = PJ_TRUE;
+    }
+    acc->cfg.media_stun_use = cfg->media_stun_use;
+
+    /* ICE settings */
+    acc->cfg.ice_cfg_use = cfg->ice_cfg_use;
+    switch (acc->cfg.ice_cfg_use) {
+    case PJSUA_ICE_CONFIG_USE_DEFAULT:
+	/* Copy ICE settings from media settings so that we don't need to
+	 * check the media config if we look for ICE config.
+	 */
+	pjsua_ice_config_from_media_config(NULL, &acc->cfg.ice_cfg,
+	                                &pjsua_var.media_cfg);
+	break;
+    case PJSUA_ICE_CONFIG_USE_CUSTOM:
+	pjsua_ice_config_dup(acc->pool, &acc->cfg.ice_cfg, &cfg->ice_cfg);
+	break;
+    }
+
+    /* TURN settings */
+    acc->cfg.turn_cfg_use = cfg->turn_cfg_use;
+    switch (acc->cfg.turn_cfg_use) {
+    case PJSUA_TURN_CONFIG_USE_DEFAULT:
+	/* Copy TURN settings from media settings so that we don't need to
+	 * check the media config if we look for TURN config.
+	 */
+	pjsua_turn_config_from_media_config(NULL, &acc->cfg.turn_cfg,
+	                                    &pjsua_var.media_cfg);
+	break;
+    case PJSUA_TURN_CONFIG_USE_CUSTOM:
+	pjsua_turn_config_dup(acc->pool, &acc->cfg.turn_cfg,
+	                      &cfg->turn_cfg);
+	break;
+    }
+
+    acc->cfg.use_srtp = cfg->use_srtp;
+
+    /* Call hold type */
+    acc->cfg.call_hold_type = cfg->call_hold_type;
+
+    /* Unregister first */
+    if (unreg_first) {
+	pjsua_acc_set_registration(acc->index, PJ_FALSE);
+	if (acc->regc != NULL) {
+	    pjsip_regc_destroy(acc->regc);
+	    acc->regc = NULL;
+	    acc->contact.slen = 0;
+	    acc->reg_mapped_addr.slen = 0;
+	}
+    }
+
+    /* Update registration */
+    if (update_reg) {
+	/* If accounts has registration enabled, start registration */
+	if (acc->cfg.reg_uri.slen)
+	    pjsua_acc_set_registration(acc->index, PJ_TRUE);
+    }
+
+    /* Update MWI subscription */
+    if (update_mwi) {
+	pjsua_start_mwi(acc_id, PJ_TRUE);
+    }
+
+on_return:
+    PJSUA_UNLOCK();
+    pj_log_pop_indent();
+    return status;
+}
+
+
+/*
+ * Modify account's presence status to be advertised to remote/presence
+ * subscribers.
+ */
+PJ_DEF(pj_status_t) pjsua_acc_set_online_status( pjsua_acc_id acc_id,
+						 pj_bool_t is_online)
+{
+    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
+
+    PJ_LOG(4,(THIS_FILE, "Acc %d: setting online status to %d..",
+	      acc_id, is_online));
+    pj_log_push_indent();
+
+    pjsua_var.acc[acc_id].online_status = is_online;
+    pj_bzero(&pjsua_var.acc[acc_id].rpid, sizeof(pjrpid_element));
+    pjsua_pres_update_acc(acc_id, PJ_FALSE);
+
+    pj_log_pop_indent();
+    return PJ_SUCCESS;
+}
+
+
+/* 
+ * Set online status with extended information 
+ */
+PJ_DEF(pj_status_t) pjsua_acc_set_online_status2( pjsua_acc_id acc_id,
+						  pj_bool_t is_online,
+						  const pjrpid_element *pr)
+{
+    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
+
+    PJ_LOG(4,(THIS_FILE, "Acc %d: setting online status to %d..",
+    	      acc_id, is_online));
+    pj_log_push_indent();
+
+    PJSUA_LOCK();
+    pjsua_var.acc[acc_id].online_status = is_online;
+    pjrpid_element_dup(pjsua_var.acc[acc_id].pool, &pjsua_var.acc[acc_id].rpid, pr);
+    PJSUA_UNLOCK();
+
+    pjsua_pres_update_acc(acc_id, PJ_TRUE);
+    pj_log_pop_indent();
+
+    return PJ_SUCCESS;
+}
+
+/* Create reg_contact, mainly for SIP outbound */
+static void update_regc_contact(pjsua_acc *acc)
+{
+    pjsua_acc_config *acc_cfg = &acc->cfg;
+    pj_bool_t need_outbound = PJ_FALSE;
+    const pj_str_t tcp_param = pj_str(";transport=tcp");
+    const pj_str_t tls_param = pj_str(";transport=tls");
+
+    if (!acc_cfg->use_rfc5626)
+	goto done;
+
+    /* Check if outbound has been requested and rejected */
+    if (acc->rfc5626_status == OUTBOUND_NA)
+	goto done;
+
+    if (pj_stristr(&acc->contact, &tcp_param)==NULL &&
+	pj_stristr(&acc->contact, &tls_param)==NULL)
+    {
+	/* Currently we can only do SIP outbound for TCP
+	 * and TLS.
+	 */
+	goto done;
+    }
+
+    /* looks like we can use outbound */
+    need_outbound = PJ_TRUE;
+
+done:
+    if (!need_outbound) {
+	/* Outbound is not needed/wanted for the account. acc->reg_contact
+	 * is set to the same as acc->contact.
+	 */
+	acc->reg_contact = acc->contact;
+	acc->rfc5626_status = OUTBOUND_NA;
+    } else {
+	/* Need to use outbound, append the contact with +sip.instance and
+	 * reg-id parameters.
+	 */
+	pj_ssize_t len;
+	pj_str_t reg_contact;
+
+	acc->rfc5626_status = OUTBOUND_WANTED;
+	len = acc->contact.slen + acc->rfc5626_instprm.slen +
+	      acc->rfc5626_regprm.slen;
+	reg_contact.ptr = (char*) pj_pool_alloc(acc->pool, len);
+
+	pj_strcpy(&reg_contact, &acc->contact);
+	pj_strcat(&reg_contact, &acc->rfc5626_regprm);
+	pj_strcat(&reg_contact, &acc->rfc5626_instprm);
+
+	acc->reg_contact = reg_contact;
+
+	PJ_LOG(4,(THIS_FILE,
+		  "Contact for acc %d updated for SIP outbound: %.*s",
+		  acc->index,
+		  (int)acc->reg_contact.slen,
+		  acc->reg_contact.ptr));
+    }
+}
+
+/* Check if IP is private IP address */
+static pj_bool_t is_private_ip(const pj_str_t *addr)
+{
+    const pj_str_t private_net[] = 
+    {
+	{ "10.", 3 },
+	{ "127.", 4 },
+	{ "172.16.", 7 }, { "172.17.", 7 }, { "172.18.", 7 }, { "172.19.", 7 },
+        { "172.20.", 7 }, { "172.21.", 7 }, { "172.22.", 7 }, { "172.23.", 7 },
+        { "172.24.", 7 }, { "172.25.", 7 }, { "172.26.", 7 }, { "172.27.", 7 },
+        { "172.28.", 7 }, { "172.29.", 7 }, { "172.30.", 7 }, { "172.31.", 7 },
+	{ "192.168.", 8 }
+    };
+    unsigned i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(private_net); ++i) {
+	if (pj_strncmp(addr, &private_net[i], private_net[i].slen)==0)
+	    return PJ_TRUE;
+    }
+
+    return PJ_FALSE;
+}
+
+/* Update NAT address from the REGISTER response */
+static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
+                                    int contact_rewrite_method,
+				    struct pjsip_regc_cbparam *param)
+{
+    pjsip_transport *tp;
+    const pj_str_t *via_addr;
+    pj_pool_t *pool;
+    int rport;
+    pjsip_sip_uri *uri;
+    pjsip_via_hdr *via;
+    pj_sockaddr contact_addr;
+    pj_sockaddr recv_addr;
+    pj_status_t status;
+    pj_bool_t matched;
+    pj_str_t srv_ip;
+    pjsip_contact_hdr *contact_hdr;
+    const pj_str_t STR_CONTACT = { "Contact", 7 };
+
+    tp = param->rdata->tp_info.transport;
+
+    /* Get the received and rport info */
+    via = param->rdata->msg_info.via;
+    if (via->rport_param < 1) {
+	/* Remote doesn't support rport */
+	rport = via->sent_by.port;
+	if (rport==0) {
+	    pjsip_transport_type_e tp_type;
+	    tp_type = (pjsip_transport_type_e) tp->key.type;
+	    rport = pjsip_transport_get_default_port_for_type(tp_type);
+	}
+    } else
+	rport = via->rport_param;
+
+    if (via->recvd_param.slen != 0)
+        via_addr = &via->recvd_param;
+    else
+        via_addr = &via->sent_by.host;
+
+    /* If allow_via_rewrite is enabled, we save the Via "received" address
+     * from the response, if either of the following condition is met:
+     *  - the Via "received" address differs from saved one (or we haven't
+     *    saved any yet)
+     *  - transport is different
+     *  - only the port has changed, AND either the received address is
+     *    public IP or allow_contact_rewrite is 2
+     */
+    if (acc->cfg.allow_via_rewrite &&
+        (pj_strcmp(&acc->via_addr.host, via_addr) || acc->via_tp != tp ||
+         (acc->via_addr.port != rport &&
+           (!is_private_ip(via_addr) || acc->cfg.allow_contact_rewrite == 2))))
+    {
+        if (pj_strcmp(&acc->via_addr.host, via_addr))
+            pj_strdup(acc->pool, &acc->via_addr.host, via_addr);
+        acc->via_addr.port = rport;
+        acc->via_tp = tp;
+        pjsip_regc_set_via_sent_by(acc->regc, &acc->via_addr, acc->via_tp);
+        if (acc->publish_sess != NULL) {
+                pjsip_publishc_set_via_sent_by(acc->publish_sess,
+                                               &acc->via_addr, acc->via_tp);
+        }
+    }
+
+    /* Save mapped address if needed */
+    if (acc->cfg.allow_sdp_nat_rewrite &&
+	pj_strcmp(&acc->reg_mapped_addr, via_addr))
+    {
+	pj_strdup(acc->pool, &acc->reg_mapped_addr, via_addr);
+    }
+
+    /* Only update if account is configured to auto-update */
+    if (acc->cfg.allow_contact_rewrite == PJ_FALSE)
+	return PJ_FALSE;
+
+    /* If SIP outbound is active, no need to update */
+    if (acc->rfc5626_status == OUTBOUND_ACTIVE) {
+	PJ_LOG(4,(THIS_FILE, "Acc %d has SIP outbound active, no need to "
+			     "update registration Contact", acc->index));
+	return PJ_FALSE;
+    }
+
+#if 0
+    // Always update
+    // See http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/2008-March/002178.html
+
+    /* For UDP, only update if STUN is enabled (for now).
+     * For TCP/TLS, always check.
+     */
+    if ((tp->key.type == PJSIP_TRANSPORT_UDP &&
+	 (pjsua_var.ua_cfg.stun_domain.slen != 0 ||
+	 (pjsua_var.ua_cfg.stun_host.slen != 0))  ||
+	(tp->key.type == PJSIP_TRANSPORT_TCP) ||
+	(tp->key.type == PJSIP_TRANSPORT_TLS))
+    {
+	/* Yes we will check */
+    } else {
+	return PJ_FALSE;
+    }
+#endif
+
+    /* Compare received and rport with the URI in our registration */
+    pool = pjsua_pool_create("tmp", 512, 512);
+    contact_hdr = (pjsip_contact_hdr*)
+		  pjsip_parse_hdr(pool, &STR_CONTACT, acc->contact.ptr, 
+				  acc->contact.slen, NULL);
+    pj_assert(contact_hdr != NULL);
+    uri = (pjsip_sip_uri*) contact_hdr->uri;
+    pj_assert(uri != NULL);
+    uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
+
+    if (uri->port == 0) {
+	pjsip_transport_type_e tp_type;
+	tp_type = (pjsip_transport_type_e) tp->key.type;
+	uri->port = pjsip_transport_get_default_port_for_type(tp_type);
+    }
+
+    /* Convert IP address strings into sockaddr for comparison.
+     * (http://trac.pjsip.org/repos/ticket/863)
+     */
+    status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &uri->host, 
+			       &contact_addr);
+    if (status == PJ_SUCCESS)
+	status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, via_addr, 
+				   &recv_addr);
+    if (status == PJ_SUCCESS) {
+	/* Compare the addresses as sockaddr according to the ticket above */
+	matched = (uri->port == rport &&
+		   pj_sockaddr_cmp(&contact_addr, &recv_addr)==0);
+    } else {
+	/* Compare the addresses as string, as before */
+	matched = (uri->port == rport &&
+		   pj_stricmp(&uri->host, via_addr)==0);
+    }
+
+    if (matched) {
+	/* Address doesn't change */
+	pj_pool_release(pool);
+	return PJ_FALSE;
+    }
+
+    /* Get server IP */
+    srv_ip = pj_str(param->rdata->pkt_info.src_name);
+
+    /* At this point we've detected that the address as seen by registrar.
+     * has changed.
+     */
+
+    /* Do not switch if both Contact and server's IP address are
+     * public but response contains private IP. A NAT in the middle
+     * might have messed up with the SIP packets. See:
+     * http://trac.pjsip.org/repos/ticket/643
+     *
+     * This exception can be disabled by setting allow_contact_rewrite
+     * to 2. In this case, the switch will always be done whenever there
+     * is difference in the IP address in the response.
+     */
+    if (acc->cfg.allow_contact_rewrite != 2 && !is_private_ip(&uri->host) &&
+	!is_private_ip(&srv_ip) && is_private_ip(via_addr))
+    {
+	/* Don't switch */
+	pj_pool_release(pool);
+	return PJ_FALSE;
+    }
+
+    /* Also don't switch if only the port number part is different, and
+     * the Via received address is private.
+     * See http://trac.pjsip.org/repos/ticket/864
+     */
+    if (acc->cfg.allow_contact_rewrite != 2 &&
+	pj_sockaddr_cmp(&contact_addr, &recv_addr)==0 &&
+	is_private_ip(via_addr))
+    {
+	/* Don't switch */
+	pj_pool_release(pool);
+	return PJ_FALSE;
+    }
+
+    PJ_LOG(3,(THIS_FILE, "IP address change detected for account %d "
+			 "(%.*s:%d --> %.*s:%d). Updating registration "
+			 "(using method %d)",
+			 acc->index,
+			 (int)uri->host.slen,
+			 uri->host.ptr,
+			 uri->port,
+			 (int)via_addr->slen,
+			 via_addr->ptr,
+			 rport,
+			 contact_rewrite_method));
+
+    pj_assert(contact_rewrite_method == PJSUA_CONTACT_REWRITE_UNREGISTER ||
+	      contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG ||
+              contact_rewrite_method == PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE);
+
+    if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_UNREGISTER) {
+	/* Unregister current contact */
+	pjsua_acc_set_registration(acc->index, PJ_FALSE);
+	if (acc->regc != NULL) {
+	    pjsip_regc_destroy(acc->regc);
+	    acc->regc = NULL;
+	    acc->contact.slen = 0;
+	}
+    }
+
+    /*
+     * Build new Contact header
+     */
+    {
+	const char *ob = ";ob";
+	char *tmp;
+	const char *beginquote, *endquote;
+	char transport_param[32];
+	int len;
+	pj_bool_t secure;
+
+	secure = pjsip_transport_get_flag_from_type(tp->key.type) &
+		 PJSIP_TRANSPORT_SECURE;
+
+	/* Enclose IPv6 address in square brackets */
+	if (tp->key.type & PJSIP_TRANSPORT_IPV6) {
+	    beginquote = "[";
+	    endquote = "]";
+	} else {
+	    beginquote = endquote = "";
+	}
+
+	/* Don't add transport parameter if it's UDP */
+	if (tp->key.type != PJSIP_TRANSPORT_UDP &&
+		tp->key.type != PJSIP_TRANSPORT_UDP6)
+	{
+	    pj_ansi_snprintf(transport_param, sizeof(transport_param),
+			     ";transport=%s",
+			     pjsip_transport_get_type_name(
+				     (pjsip_transport_type_e)tp->key.type));
+	} else {
+	    transport_param[0] = '\0';
+	}
+
+	tmp = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+	len = pj_ansi_snprintf(tmp, PJSIP_MAX_URL_SIZE,
+			       "<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s",
+			       ((secure && acc->is_sips)? "sips" : "sip"),
+			       (int)acc->user_part.slen,
+			       acc->user_part.ptr,
+			       (acc->user_part.slen? "@" : ""),
+			       beginquote,
+			       (int)via_addr->slen,
+			       via_addr->ptr,
+			       endquote,
+			       rport,
+			       transport_param,
+			       (int)acc->cfg.contact_uri_params.slen,
+			       acc->cfg.contact_uri_params.ptr,
+			       (acc->cfg.use_rfc5626? ob: ""),
+			       (int)acc->cfg.contact_params.slen,
+			       acc->cfg.contact_params.ptr);
+	if (len < 1 || len >= PJSIP_MAX_URL_SIZE) {
+	    PJ_LOG(1,(THIS_FILE, "URI too long"));
+	    pj_pool_release(pool);
+	    return PJ_FALSE;
+	}
+	pj_strdup2_with_null(acc->pool, &acc->contact, tmp);
+
+	update_regc_contact(acc);
+
+	/* Always update, by http://trac.pjsip.org/repos/ticket/864. */
+        /* Since the Via address will now be overwritten to the correct
+         * address by https://trac.pjsip.org/repos/ticket/1537, we do
+         * not need to update the transport address.
+         */
+        /*
+	pj_strdup_with_null(tp->pool, &tp->local_name.host, via_addr);
+	tp->local_name.port = rport;
+         */
+
+    }
+
+    if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG &&
+        acc->regc != NULL)
+    {
+	pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact);
+    }
+
+    /* Perform new registration */
+    if (contact_rewrite_method < PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE) {
+        pjsua_acc_set_registration(acc->index, PJ_TRUE);
+    }
+
+    pj_pool_release(pool);
+
+    return PJ_TRUE;
+}
+
+/* Check and update Service-Route header */
+void update_service_route(pjsua_acc *acc, pjsip_rx_data *rdata)
+{
+    pjsip_generic_string_hdr *hsr = NULL;
+    pjsip_route_hdr *hr, *h;
+    const pj_str_t HNAME = { "Service-Route", 13 };
+    const pj_str_t HROUTE = { "Route", 5 };
+    pjsip_uri *uri[PJSUA_ACC_MAX_PROXIES];
+    unsigned i, uri_cnt = 0;
+    pj_size_t rcnt;
+
+    /* Find and parse Service-Route headers */
+    for (;;) {
+	char saved;
+	int parsed_len;
+
+	/* Find Service-Route header */
+	hsr = (pjsip_generic_string_hdr*)
+	      pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &HNAME, hsr);
+	if (!hsr)
+	    break;
+
+	/* Parse as Route header since the syntax is similar. This may
+	 * return more than one headers.
+	 */
+	saved = hsr->hvalue.ptr[hsr->hvalue.slen];
+	hsr->hvalue.ptr[hsr->hvalue.slen] = '\0';
+	hr = (pjsip_route_hdr*)
+	     pjsip_parse_hdr(rdata->tp_info.pool, &HROUTE, hsr->hvalue.ptr,
+			     hsr->hvalue.slen, &parsed_len);
+	hsr->hvalue.ptr[hsr->hvalue.slen] = saved;
+
+	if (hr == NULL) {
+	    /* Error */
+	    PJ_LOG(1,(THIS_FILE, "Error parsing Service-Route header"));
+	    return;
+	}
+
+	/* Save each URI in the result */
+	h = hr;
+	do {
+	    if (!PJSIP_URI_SCHEME_IS_SIP(h->name_addr.uri) &&
+		!PJSIP_URI_SCHEME_IS_SIPS(h->name_addr.uri))
+	    {
+		PJ_LOG(1,(THIS_FILE,"Error: non SIP URI in Service-Route: %.*s",
+			  (int)hsr->hvalue.slen, hsr->hvalue.ptr));
+		return;
+	    }
+
+	    uri[uri_cnt++] = h->name_addr.uri;
+	    h = h->next;
+	} while (h != hr && uri_cnt != PJ_ARRAY_SIZE(uri));
+
+	if (h != hr) {
+	    PJ_LOG(1,(THIS_FILE, "Error: too many Service-Route headers"));
+	    return;
+	}
+
+	/* Prepare to find next Service-Route header */
+	hsr = hsr->next;
+	if ((void*)hsr == (void*)&rdata->msg_info.msg->hdr)
+	    break;
+    }
+
+    if (uri_cnt == 0)
+	return;
+
+    /* 
+     * Update account's route set 
+     */
+    
+    /* First remove all routes which are not the outbound proxies */
+    rcnt = pj_list_size(&acc->route_set);
+    if (rcnt != pjsua_var.ua_cfg.outbound_proxy_cnt + acc->cfg.proxy_cnt) {
+	for (i=pjsua_var.ua_cfg.outbound_proxy_cnt + acc->cfg.proxy_cnt, 
+		hr=acc->route_set.prev; 
+	     i<rcnt; 
+	     ++i)
+	 {
+	    pjsip_route_hdr *prev = hr->prev;
+	    pj_list_erase(hr);
+	    hr = prev;
+	 }
+    }
+
+    /* Then append the Service-Route URIs */
+    for (i=0; i<uri_cnt; ++i) {
+	hr = pjsip_route_hdr_create(acc->pool);
+	hr->name_addr.uri = (pjsip_uri*)pjsip_uri_clone(acc->pool, uri[i]);
+	pj_list_push_back(&acc->route_set, hr);
+    }
+
+    /* Done */
+
+    PJ_LOG(4,(THIS_FILE, "Service-Route updated for acc %d with %d URI(s)",
+	      acc->index, uri_cnt));
+}
+
+
+/* Keep alive timer callback */
+static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
+{
+    pjsua_acc *acc;
+    pjsip_tpselector tp_sel;
+    pj_time_val delay;
+    char addrtxt[PJ_INET6_ADDRSTRLEN];
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(th);
+
+    PJSUA_LOCK();
+
+    te->id = PJ_FALSE;
+
+    acc = (pjsua_acc*) te->user_data;
+
+    /* Select the transport to send the packet */
+    pj_bzero(&tp_sel, sizeof(tp_sel));
+    tp_sel.type = PJSIP_TPSELECTOR_TRANSPORT;
+    tp_sel.u.transport = acc->ka_transport;
+
+    PJ_LOG(5,(THIS_FILE, 
+	      "Sending %d bytes keep-alive packet for acc %d to %s",
+	      acc->cfg.ka_data.slen, acc->index,
+	      pj_sockaddr_print(&acc->ka_target, addrtxt, sizeof(addrtxt),3)));
+
+    /* Send raw packet */
+    status = pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(pjsua_var.endpt),
+				  PJSIP_TRANSPORT_UDP, &tp_sel,
+				  NULL, acc->cfg.ka_data.ptr, 
+				  acc->cfg.ka_data.slen, 
+				  &acc->ka_target, acc->ka_target_len,
+				  NULL, NULL);
+
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	pjsua_perror(THIS_FILE, "Error sending keep-alive packet", status);
+    }
+
+    /* Check just in case keep-alive has been disabled. This shouldn't happen
+     * though as when ka_interval is changed this timer should have been
+     * cancelled.
+     */
+    if (acc->cfg.ka_interval == 0)
+	goto on_return;
+
+    /* Reschedule next timer */
+    delay.sec = acc->cfg.ka_interval;
+    delay.msec = 0;
+    status = pjsip_endpt_schedule_timer(pjsua_var.endpt, te, &delay);
+    if (status == PJ_SUCCESS) {
+	te->id = PJ_TRUE;
+    } else {
+	pjsua_perror(THIS_FILE, "Error starting keep-alive timer", status);
+    }
+
+on_return:
+    PJSUA_UNLOCK();
+}
+
+
+/* Update keep-alive for the account */
+static void update_keep_alive(pjsua_acc *acc, pj_bool_t start,
+			      struct pjsip_regc_cbparam *param)
+{
+    /* In all cases, stop keep-alive timer if it's running. */
+    if (acc->ka_timer.id) {
+	pjsip_endpt_cancel_timer(pjsua_var.endpt, &acc->ka_timer);
+	acc->ka_timer.id = PJ_FALSE;
+
+	pjsip_transport_dec_ref(acc->ka_transport);
+	acc->ka_transport = NULL;
+    }
+
+    if (start) {
+	pj_time_val delay;
+	pj_status_t status;
+
+	/* Only do keep-alive if:
+	 *  - ka_interval is not zero in the account, and
+	 *  - transport is UDP.
+	 *
+	 * Previously we only enabled keep-alive when STUN is enabled, since
+	 * we thought that keep-alive is only needed in Internet situation.
+	 * But it has been discovered that Windows Firewall on WinXP also
+	 * needs to be kept-alive, otherwise incoming packets will be dropped.
+	 * So because of this, now keep-alive is always enabled for UDP,
+	 * regardless of whether STUN is enabled or not.
+	 *
+	 * Note that this applies only for UDP. For TCP/TLS, the keep-alive
+	 * is done by the transport layer.
+	 */
+	if (/*pjsua_var.stun_srv.ipv4.sin_family == 0 ||*/
+	    acc->cfg.ka_interval == 0 ||
+	    param->rdata->tp_info.transport->key.type != PJSIP_TRANSPORT_UDP)
+	{
+	    /* Keep alive is not necessary */
+	    return;
+	}
+
+	/* Save transport and destination address. */
+	acc->ka_transport = param->rdata->tp_info.transport;
+	pjsip_transport_add_ref(acc->ka_transport);
+
+	/* https://trac.pjsip.org/repos/ticket/1607:
+	 * Calculate the destination address from the original request. Some
+	 * (broken) servers send the response using different source address
+	 * than the one that receives the request, which is forbidden by RFC
+	 * 3581.
+	 */
+	{
+	    pjsip_transaction *tsx;
+	    pjsip_tx_data *req;
+
+	    tsx = pjsip_rdata_get_tsx(param->rdata);
+	    PJ_ASSERT_ON_FAIL(tsx, return);
+
+	    req = tsx->last_tx;
+
+	    pj_memcpy(&acc->ka_target, &req->tp_info.dst_addr,
+	              req->tp_info.dst_addr_len);
+	    acc->ka_target_len = req->tp_info.dst_addr_len;
+	}
+
+	/* Setup and start the timer */
+	acc->ka_timer.cb = &keep_alive_timer_cb;
+	acc->ka_timer.user_data = (void*)acc;
+
+	delay.sec = acc->cfg.ka_interval;
+	delay.msec = 0;
+	status = pjsip_endpt_schedule_timer(pjsua_var.endpt, &acc->ka_timer, 
+					    &delay);
+	if (status == PJ_SUCCESS) {
+	    acc->ka_timer.id = PJ_TRUE;
+	    PJ_LOG(4,(THIS_FILE, "Keep-alive timer started for acc %d, "
+				 "destination:%s:%d, interval:%ds",
+				 acc->index,
+				 param->rdata->pkt_info.src_name,
+				 param->rdata->pkt_info.src_port,
+				 acc->cfg.ka_interval));
+	} else {
+	    acc->ka_timer.id = PJ_FALSE;
+	    pjsip_transport_dec_ref(acc->ka_transport);
+	    acc->ka_transport = NULL;
+	    pjsua_perror(THIS_FILE, "Error starting keep-alive timer", status);
+	}
+    }
+}
+
+
+/* Update the status of SIP outbound registration request */
+static void update_rfc5626_status(pjsua_acc *acc, pjsip_rx_data *rdata)
+{
+    pjsip_require_hdr *hreq;
+    const pj_str_t STR_OUTBOUND = {"outbound", 8};
+    unsigned i;
+
+    if (acc->rfc5626_status == OUTBOUND_UNKNOWN) {
+	goto on_return;
+    }
+
+    hreq = rdata->msg_info.require;
+    if (!hreq) {
+	acc->rfc5626_status = OUTBOUND_NA;
+	goto on_return;
+    }
+
+    for (i=0; i<hreq->count; ++i) {
+	if (pj_stricmp(&hreq->values[i], &STR_OUTBOUND)==0) {
+	    acc->rfc5626_status = OUTBOUND_ACTIVE;
+	    goto on_return;
+	}
+    }
+
+    /* Server does not support outbound */
+    acc->rfc5626_status = OUTBOUND_NA;
+
+on_return:
+    if (acc->rfc5626_status != OUTBOUND_ACTIVE) {
+	acc->reg_contact = acc->contact;
+    }
+    PJ_LOG(4,(THIS_FILE, "SIP outbound status for acc %d is %s",
+			 acc->index, (acc->rfc5626_status==OUTBOUND_ACTIVE?
+					 "active": "not active")));
+}
+
+static void regc_tsx_cb(struct pjsip_regc_tsx_cb_param *param)
+{
+    pjsua_acc *acc = (pjsua_acc*) param->cbparam.token;
+
+    PJSUA_LOCK();
+
+    if (param->cbparam.regc != acc->regc) {
+        PJSUA_UNLOCK();
+	return;
+    }
+
+    pj_log_push_indent();
+
+    if ((acc->cfg.contact_rewrite_method &
+         PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE) ==
+        PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE &&
+        param->cbparam.code >= 400 &&
+        param->cbparam.rdata)
+    {
+        if (acc_check_nat_addr(acc, PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE,
+                               &param->cbparam))
+        {
+            param->contact_cnt = 1;
+            param->contact[0] = acc->reg_contact;
+        }
+    }
+
+    PJSUA_UNLOCK();
+    pj_log_pop_indent();
+}
+
+/*
+ * This callback is called by pjsip_regc when outgoing register
+ * request has completed.
+ */
+static void regc_cb(struct pjsip_regc_cbparam *param)
+{
+
+    pjsua_acc *acc = (pjsua_acc*) param->token;
+
+    PJSUA_LOCK();
+
+    if (param->regc != acc->regc) {
+        PJSUA_UNLOCK();
+	return;
+    }
+
+    pj_log_push_indent();
+
+    /*
+     * Print registration status.
+     */
+    if (param->status!=PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "SIP registration error", 
+		     param->status);
+	pjsip_regc_destroy(acc->regc);
+	acc->regc = NULL;
+	acc->contact.slen = 0;
+	acc->reg_mapped_addr.slen = 0;
+	
+	/* Stop keep-alive timer if any. */
+	update_keep_alive(acc, PJ_FALSE, NULL);
+
+    } else if (param->code < 0 || param->code >= 300) {
+	PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%.*s)", 
+		   param->code, 
+		   (int)param->reason.slen, param->reason.ptr));
+	pjsip_regc_destroy(acc->regc);
+	acc->regc = NULL;
+	acc->contact.slen = 0;
+	acc->reg_mapped_addr.slen = 0;
+
+	/* Stop keep-alive timer if any. */
+	update_keep_alive(acc, PJ_FALSE, NULL);
+
+    } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
+
+	/* Update auto registration flag */
+	acc->auto_rereg.active = PJ_FALSE;
+	acc->auto_rereg.attempt_cnt = 0;
+
+	if (param->expiration < 1) {
+	    pjsip_regc_destroy(acc->regc);
+	    acc->regc = NULL;
+	    acc->contact.slen = 0;
+	    acc->reg_mapped_addr.slen = 0;
+
+	    /* Stop keep-alive timer if any. */
+	    update_keep_alive(acc, PJ_FALSE, NULL);
+
+	    PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
+		      pjsua_var.acc[acc->index].cfg.id.ptr));
+	} else {
+	    /* Check and update SIP outbound status first, since the result
+	     * will determine if we should update re-registration
+	     */
+	    update_rfc5626_status(acc, param->rdata);
+
+	    /* Check NAT bound address */
+            if (acc_check_nat_addr(acc, (acc->cfg.contact_rewrite_method & 3),
+                                   param))
+            {
+		PJSUA_UNLOCK();
+		pj_log_pop_indent();
+		return;
+	    }
+
+	    /* Check and update Service-Route header */
+	    update_service_route(acc, param->rdata);
+
+	    PJ_LOG(3, (THIS_FILE, 
+		       "%s: registration success, status=%d (%.*s), "
+		       "will re-register in %d seconds", 
+		       pjsua_var.acc[acc->index].cfg.id.ptr,
+		       param->code,
+		       (int)param->reason.slen, param->reason.ptr,
+		       param->expiration));
+
+	    /* Start keep-alive timer if necessary. */
+	    update_keep_alive(acc, PJ_TRUE, param);
+
+	    /* Send initial PUBLISH if it is enabled */
+	    if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
+		pjsua_pres_init_publish_acc(acc->index);
+
+	    /* Subscribe to MWI, if it's enabled */
+	    if (acc->cfg.mwi_enabled)
+		pjsua_start_mwi(acc->index, PJ_FALSE);
+	}
+
+    } else {
+	PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
+    }
+
+    acc->reg_last_err = param->status;
+    acc->reg_last_code = param->code;
+
+    /* Check if we need to auto retry registration. Basically, registration
+     * failure codes triggering auto-retry are those of temporal failures
+     * considered to be recoverable in relatively short term.
+     */
+    if (acc->cfg.reg_retry_interval && 
+	(param->code == PJSIP_SC_REQUEST_TIMEOUT ||
+	 param->code == PJSIP_SC_INTERNAL_SERVER_ERROR ||
+	 param->code == PJSIP_SC_BAD_GATEWAY ||
+	 param->code == PJSIP_SC_SERVICE_UNAVAILABLE ||
+	 param->code == PJSIP_SC_SERVER_TIMEOUT ||
+	 param->code == PJSIP_SC_TEMPORARILY_UNAVAILABLE ||
+	 PJSIP_IS_STATUS_IN_CLASS(param->code, 600))) /* Global failure */
+    {
+	schedule_reregistration(acc);
+    }
+
+    /* Call the registration status callback */
+
+    if (pjsua_var.ua_cfg.cb.on_reg_state) {
+	(*pjsua_var.ua_cfg.cb.on_reg_state)(acc->index);
+    }
+
+    if (pjsua_var.ua_cfg.cb.on_reg_state2) {
+	pjsua_reg_info reg_info;
+
+	reg_info.cbparam = param;
+	(*pjsua_var.ua_cfg.cb.on_reg_state2)(acc->index, &reg_info);
+    }
+    
+    PJSUA_UNLOCK();
+    pj_log_pop_indent();
+}
+
+
+/*
+ * Initialize client registration.
+ */
+static pj_status_t pjsua_regc_init(int acc_id)
+{
+    pjsua_acc *acc;
+    pj_pool_t *pool;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
+    acc = &pjsua_var.acc[acc_id];
+
+    if (acc->cfg.reg_uri.slen == 0) {
+	PJ_LOG(3,(THIS_FILE, "Registrar URI is not specified"));
+	return PJ_SUCCESS;
+    }
+
+    /* Destroy existing session, if any */
+    if (acc->regc) {
+	pjsip_regc_destroy(acc->regc);
+	acc->regc = NULL;
+	acc->contact.slen = 0;
+	acc->reg_mapped_addr.slen = 0;
+    }
+
+    /* initialize SIP registration if registrar is configured */
+
+    status = pjsip_regc_create( pjsua_var.endpt, 
+				acc, &regc_cb, &acc->regc);
+
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create client registration", 
+		     status);
+	return status;
+    }
+
+    pool = pjsua_pool_create("tmpregc", 512, 512);
+
+    if (acc->contact.slen == 0) {
+	pj_str_t tmp_contact;
+
+	status = pjsua_acc_create_uac_contact( pool, &tmp_contact,
+					       acc_id, &acc->cfg.reg_uri);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to generate suitable Contact header"
+				    " for registration", 
+			 status);
+	    pjsip_regc_destroy(acc->regc);
+	    pj_pool_release(pool);
+	    acc->regc = NULL;
+	    return status;
+	}
+
+	pj_strdup_with_null(acc->pool, &acc->contact, &tmp_contact);
+	update_regc_contact(acc);
+    }
+
+    status = pjsip_regc_init( acc->regc,
+			      &acc->cfg.reg_uri, 
+			      &acc->cfg.id, 
+			      &acc->cfg.id,
+			      1, &acc->reg_contact,
+			      acc->cfg.reg_timeout);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, 
+		     "Client registration initialization error", 
+		     status);
+	pjsip_regc_destroy(acc->regc);
+	pj_pool_release(pool);
+	acc->regc = NULL;
+	acc->contact.slen = 0;
+	acc->reg_mapped_addr.slen = 0;
+	return status;
+    }
+
+    pjsip_regc_set_reg_tsx_cb(acc->regc, regc_tsx_cb);
+
+    /* If account is locked to specific transport, then set transport to
+     * the client registration.
+     */
+    if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
+	pjsip_tpselector tp_sel;
+
+	pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
+	pjsip_regc_set_transport(acc->regc, &tp_sel);
+    }
+
+
+    /* Set credentials
+     */
+    if (acc->cred_cnt) {
+	pjsip_regc_set_credentials( acc->regc, acc->cred_cnt, acc->cred);
+    }
+
+    /* Set delay before registration refresh */
+    pjsip_regc_set_delay_before_refresh(acc->regc,
+                                        acc->cfg.reg_delay_before_refresh);
+
+    /* Set authentication preference */
+    pjsip_regc_set_prefs(acc->regc, &acc->cfg.auth_pref);
+
+    /* Set route-set
+     */
+    if (acc->cfg.reg_use_proxy) {
+	pjsip_route_hdr route_set;
+	const pjsip_route_hdr *r;
+
+	pj_list_init(&route_set);
+
+	if (acc->cfg.reg_use_proxy & PJSUA_REG_USE_OUTBOUND_PROXY) {
+	    r = pjsua_var.outbound_proxy.next;
+	    while (r != &pjsua_var.outbound_proxy) {
+		pj_list_push_back(&route_set, pjsip_hdr_shallow_clone(pool, r));
+		r = r->next;
+	    }
+	}
+
+	if (acc->cfg.reg_use_proxy & PJSUA_REG_USE_ACC_PROXY &&
+	    acc->cfg.proxy_cnt)
+	{
+	    int cnt = acc->cfg.proxy_cnt;
+	    pjsip_route_hdr *pos = route_set.prev;
+	    int i;
+
+	    r = acc->route_set.prev;
+	    for (i=0; i<cnt; ++i) {
+		pj_list_push_front(pos, pjsip_hdr_shallow_clone(pool, r));
+		r = r->prev;
+	    }
+	}
+
+	if (!pj_list_empty(&route_set))
+	    pjsip_regc_set_route_set( acc->regc, &route_set );
+    }
+
+    /* Add custom request headers specified in the account config */
+    pjsip_regc_add_headers(acc->regc, &acc->cfg.reg_hdr_list);
+
+    /* Add other request headers. */
+    if (pjsua_var.ua_cfg.user_agent.slen) {
+	pjsip_hdr hdr_list;
+	const pj_str_t STR_USER_AGENT = { "User-Agent", 10 };
+	pjsip_generic_string_hdr *h;
+
+	pj_list_init(&hdr_list);
+
+	h = pjsip_generic_string_hdr_create(pool, &STR_USER_AGENT, 
+					    &pjsua_var.ua_cfg.user_agent);
+	pj_list_push_back(&hdr_list, (pjsip_hdr*)h);
+
+	pjsip_regc_add_headers(acc->regc, &hdr_list);
+    }
+
+    /* If SIP outbound is used, add "Supported: outbound, path header" */
+    if (acc->rfc5626_status == OUTBOUND_WANTED) {
+	pjsip_hdr hdr_list;
+	pjsip_supported_hdr *hsup;
+
+	pj_list_init(&hdr_list);
+	hsup = pjsip_supported_hdr_create(pool);
+	pj_list_push_back(&hdr_list, hsup);
+
+	hsup->count = 2;
+	hsup->values[0] = pj_str("outbound");
+	hsup->values[1] = pj_str("path");
+
+	pjsip_regc_add_headers(acc->regc, &hdr_list);
+    }
+
+    pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+pj_bool_t pjsua_sip_acc_is_using_stun(pjsua_acc_id acc_id)
+{
+    pjsua_acc *acc = &pjsua_var.acc[acc_id];
+
+    return acc->cfg.sip_stun_use != PJSUA_STUN_USE_DISABLED &&
+	    pjsua_var.ua_cfg.stun_srv_cnt != 0;
+}
+
+/*
+ * Update registration or perform unregistration. 
+ */
+PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, 
+						pj_bool_t renew)
+{
+    pjsua_acc *acc;
+    pj_status_t status = 0;
+    pjsip_tx_data *tdata = 0;
+
+    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
+
+    PJ_LOG(4,(THIS_FILE, "Acc %d: setting %sregistration..",
+	      acc_id, (renew? "" : "un")));
+    pj_log_push_indent();
+
+    PJSUA_LOCK();
+
+    acc = &pjsua_var.acc[acc_id];
+
+    /* Cancel any re-registration timer */
+    if (pjsua_var.acc[acc_id].auto_rereg.timer.id) {
+	pjsua_var.acc[acc_id].auto_rereg.timer.id = PJ_FALSE;
+	pjsua_cancel_timer(&pjsua_var.acc[acc_id].auto_rereg.timer);
+    }
+
+    /* Reset pointer to registration transport */
+    pjsua_var.acc[acc_id].auto_rereg.reg_tp = NULL;
+
+    if (renew) {
+	if (pjsua_var.acc[acc_id].regc == NULL) {
+	    status = pjsua_regc_init(acc_id);
+	    if (status != PJ_SUCCESS) {
+		pjsua_perror(THIS_FILE, "Unable to create registration", 
+			     status);
+		goto on_return;
+	    }
+	}
+	if (!pjsua_var.acc[acc_id].regc) {
+	    status = PJ_EINVALIDOP;
+	    goto on_return;
+	}
+
+	status = pjsip_regc_register(pjsua_var.acc[acc_id].regc, 1, 
+				     &tdata);
+
+	if (0 && status == PJ_SUCCESS && pjsua_var.acc[acc_id].cred_cnt) {
+	    pjsua_acc *acc = &pjsua_var.acc[acc_id];
+	    pjsip_authorization_hdr *h;
+	    char *uri;
+	    int d;
+
+	    uri = (char*) pj_pool_alloc(tdata->pool, acc->cfg.reg_uri.slen+10);
+	    d = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri,
+				uri, acc->cfg.reg_uri.slen+10);
+	    pj_assert(d > 0);
+
+	    h = pjsip_authorization_hdr_create(tdata->pool);
+	    h->scheme = pj_str("Digest");
+	    h->credential.digest.username = acc->cred[0].username;
+	    h->credential.digest.realm = acc->srv_domain;
+	    h->credential.digest.uri = pj_str(uri);
+	    h->credential.digest.algorithm = pj_str("md5");
+
+	    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h);
+	}
+
+    } else {
+	if (pjsua_var.acc[acc_id].regc == NULL) {
+	    PJ_LOG(3,(THIS_FILE, "Currently not registered"));
+	    status = PJ_EINVALIDOP;
+	    goto on_return;
+	}
+
+	pjsua_pres_unpublish(&pjsua_var.acc[acc_id], 0);
+
+	status = pjsip_regc_unregister(pjsua_var.acc[acc_id].regc, &tdata);
+    }
+
+    if (status == PJ_SUCCESS) {
+        if (pjsua_var.acc[acc_id].cfg.allow_via_rewrite &&
+            pjsua_var.acc[acc_id].via_addr.host.slen > 0)
+        {
+            pjsip_regc_set_via_sent_by(pjsua_var.acc[acc_id].regc,
+                                       &pjsua_var.acc[acc_id].via_addr,
+                                       pjsua_var.acc[acc_id].via_tp);
+        } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
+            /* Choose local interface to use in Via if acc is not using
+             * STUN
+             */
+            pjsua_acc_get_uac_addr(acc_id, tdata->pool,
+	                           &acc->cfg.reg_uri,
+	                           &tdata->via_addr,
+	                           NULL, NULL,
+	                           &tdata->via_tp);
+        }
+
+	//pjsua_process_msg_data(tdata, NULL);
+	status = pjsip_regc_send( pjsua_var.acc[acc_id].regc, tdata );
+    }
+
+    /* Update pointer to registration transport */
+    if (status == PJ_SUCCESS) {
+	pjsip_regc_info reg_info;
+
+	pjsip_regc_get_info(pjsua_var.acc[acc_id].regc, &reg_info);
+	pjsua_var.acc[acc_id].auto_rereg.reg_tp = reg_info.transport;
+        
+        if (pjsua_var.ua_cfg.cb.on_reg_started) {
+            (*pjsua_var.ua_cfg.cb.on_reg_started)(acc_id, renew);
+        }
+    }
+
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create/send REGISTER", 
+		     status);
+    } else {
+	PJ_LOG(4,(THIS_FILE, "Acc %d: %s sent", acc_id,
+	         (renew? "Registration" : "Unregistration")));
+    }
+
+on_return:
+    PJSUA_UNLOCK();
+    pj_log_pop_indent();
+    return status;
+}
+
+
+/*
+ * Get account information.
+ */
+PJ_DEF(pj_status_t) pjsua_acc_get_info( pjsua_acc_id acc_id,
+					pjsua_acc_info *info)
+{
+    pjsua_acc *acc = &pjsua_var.acc[acc_id];
+    pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg;
+
+    PJ_ASSERT_RETURN(info != NULL, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
+    
+    pj_bzero(info, sizeof(pjsua_acc_info));
+
+    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc), 
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);
+
+    PJSUA_LOCK();
+    
+    if (pjsua_var.acc[acc_id].valid == PJ_FALSE) {
+	PJSUA_UNLOCK();
+	return PJ_EINVALIDOP;
+    }
+
+    info->id = acc_id;
+    info->is_default = (pjsua_var.default_acc == acc_id);
+    info->acc_uri = acc_cfg->id;
+    info->has_registration = (acc->cfg.reg_uri.slen > 0);
+    info->online_status = acc->online_status;
+    pj_memcpy(&info->rpid, &acc->rpid, sizeof(pjrpid_element));
+    if (info->rpid.note.slen)
+	info->online_status_text = info->rpid.note;
+    else if (info->online_status)
+	info->online_status_text = pj_str("Online");
+    else
+	info->online_status_text = pj_str("Offline");
+
+    if (acc->reg_last_code) {
+	if (info->has_registration) {
+	    info->status = (pjsip_status_code) acc->reg_last_code;
+	    info->status_text = *pjsip_get_status_text(acc->reg_last_code);
+            if (acc->reg_last_err)
+	        info->reg_last_err = acc->reg_last_err;
+	} else {
+	    info->status = (pjsip_status_code) 0;
+	    info->status_text = pj_str("not registered");
+	}
+    } else if (acc->cfg.reg_uri.slen) {
+	info->status = PJSIP_SC_TRYING;
+	info->status_text = pj_str("In Progress");
+    } else {
+	info->status = (pjsip_status_code) 0;
+	info->status_text = pj_str("does not register");
+    }
+    
+    if (acc->regc) {
+	pjsip_regc_info regc_info;
+	pjsip_regc_get_info(acc->regc, &regc_info);
+	info->expires = regc_info.next_reg;
+    } else {
+	info->expires = -1;
+    }
+
+    PJSUA_UNLOCK();
+
+    return PJ_SUCCESS;
+
+}
+
+
+/*
+ * Enum accounts all account ids.
+ */
+PJ_DEF(pj_status_t) pjsua_enum_accs(pjsua_acc_id ids[],
+				    unsigned *count )
+{
+    unsigned i, c;
+
+    PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
+
+    PJSUA_LOCK();
+
+    for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+	if (!pjsua_var.acc[i].valid)
+	    continue;
+	ids[c] = i;
+	++c;
+    }
+
+    *count = c;
+
+    PJSUA_UNLOCK();
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum accounts info.
+ */
+PJ_DEF(pj_status_t) pjsua_acc_enum_info( pjsua_acc_info info[],
+					 unsigned *count )
+{
+    unsigned i, c;
+
+    PJ_ASSERT_RETURN(info && *count, PJ_EINVAL);
+
+    PJSUA_LOCK();
+
+    for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+	if (!pjsua_var.acc[i].valid)
+	    continue;
+
+	pjsua_acc_get_info(i, &info[c]);
+	++c;
+    }
+
+    *count = c;
+
+    PJSUA_UNLOCK();
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * This is an internal function to find the most appropriate account to
+ * used to reach to the specified URL.
+ */
+PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *url)
+{
+    pj_str_t tmp;
+    pjsip_uri *uri;
+    pjsip_sip_uri *sip_uri;
+    pj_pool_t *tmp_pool;
+    unsigned i;
+
+    PJSUA_LOCK();
+
+    tmp_pool = pjsua_pool_create("tmpacc10", 256, 256);
+
+    pj_strdup_with_null(tmp_pool, &tmp, url);
+
+    uri = pjsip_parse_uri(tmp_pool, tmp.ptr, tmp.slen, 0);
+    if (!uri) {
+	pj_pool_release(tmp_pool);
+	PJSUA_UNLOCK();
+	return pjsua_var.default_acc;
+    }
+
+    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && 
+	!PJSIP_URI_SCHEME_IS_SIPS(uri)) 
+    {
+	/* Return the first account with proxy */
+	for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+	    if (!pjsua_var.acc[i].valid)
+		continue;
+	    if (!pj_list_empty(&pjsua_var.acc[i].route_set))
+		break;
+	}
+
+	if (i != PJ_ARRAY_SIZE(pjsua_var.acc)) {
+	    /* Found rather matching account */
+	    pj_pool_release(tmp_pool);
+	    PJSUA_UNLOCK();
+	    return i;
+	}
+
+	/* Not found, use default account */
+	pj_pool_release(tmp_pool);
+	PJSUA_UNLOCK();
+	return pjsua_var.default_acc;
+    }
+
+    sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
+
+    /* Find matching domain AND port */
+    for (i=0; i<pjsua_var.acc_cnt; ++i) {
+	unsigned acc_id = pjsua_var.acc_ids[i];
+	if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0 &&
+	    pjsua_var.acc[acc_id].srv_port == sip_uri->port)
+	{
+	    pj_pool_release(tmp_pool);
+	    PJSUA_UNLOCK();
+	    return acc_id;
+	}
+    }
+
+    /* If no match, try to match the domain part only */
+    for (i=0; i<pjsua_var.acc_cnt; ++i) {
+	unsigned acc_id = pjsua_var.acc_ids[i];
+	if (pj_stricmp(&pjsua_var.acc[acc_id].srv_domain, &sip_uri->host)==0)
+	{
+	    pj_pool_release(tmp_pool);
+	    PJSUA_UNLOCK();
+	    return acc_id;
+	}
+    }
+
+
+    /* Still no match, just use default account */
+    pj_pool_release(tmp_pool);
+    PJSUA_UNLOCK();
+    return pjsua_var.default_acc;
+}
+
+
+/*
+ * This is an internal function to find the most appropriate account to be
+ * used to handle incoming calls.
+ */
+PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata)
+{
+    pjsip_uri *uri;
+    pjsip_sip_uri *sip_uri;
+    pjsua_acc_id id = PJSUA_INVALID_ID;
+    unsigned i;
+
+    /* Check that there's at least one account configured */
+    PJ_ASSERT_RETURN(pjsua_var.acc_cnt!=0, pjsua_var.default_acc);
+
+    uri = rdata->msg_info.to->uri;
+
+    PJSUA_LOCK();
+
+    /* Use Req URI if To URI is not SIP */
+    if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
+	!PJSIP_URI_SCHEME_IS_SIPS(uri))
+    {
+	if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG)
+	    uri = rdata->msg_info.msg->line.req.uri;
+	else
+	    goto on_return;
+    }
+
+    /* Just return default account if both To and Req URI are not SIP: */
+    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && 
+	!PJSIP_URI_SCHEME_IS_SIPS(uri)) 
+    {
+	goto on_return;
+    }
+
+    sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
+
+    /* Find account which has matching username and domain. */
+    for (i=0; i < pjsua_var.acc_cnt; ++i) {
+	unsigned acc_id = pjsua_var.acc_ids[i];
+	pjsua_acc *acc = &pjsua_var.acc[acc_id];
+
+	if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
+	    pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) 
+	{
+	    /* Match ! */
+	    id = acc_id;
+	    goto on_return;
+	}
+    }
+
+    /* No matching account, try match domain part only. */
+    for (i=0; i < pjsua_var.acc_cnt; ++i) {
+	unsigned acc_id = pjsua_var.acc_ids[i];
+	pjsua_acc *acc = &pjsua_var.acc[acc_id];
+
+	if (acc->valid && pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) {
+	    /* Match ! */
+	    id = acc_id;
+	    goto on_return;
+	}
+    }
+
+    /* No matching account, try match user part (and transport type) only. */
+    for (i=0; i < pjsua_var.acc_cnt; ++i) {
+	unsigned acc_id = pjsua_var.acc_ids[i];
+	pjsua_acc *acc = &pjsua_var.acc[acc_id];
+
+	if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0) {
+
+	    if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
+		pjsip_transport_type_e type;
+		type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
+		if (type == PJSIP_TRANSPORT_UNSPECIFIED)
+		    type = PJSIP_TRANSPORT_UDP;
+
+		if (pjsua_var.tpdata[acc->cfg.transport_id].type != type)
+		    continue;
+	    }
+
+	    /* Match ! */
+	    id = acc_id;
+	    goto on_return;
+	}
+    }
+
+on_return:
+    PJSUA_UNLOCK();
+
+    /* Still no match, use default account */
+    if (id == PJSUA_INVALID_ID)
+	id = pjsua_var.default_acc;
+
+    /* Invoke account find callback */
+    if (pjsua_var.ua_cfg.cb.on_acc_find_for_incoming)
+	(*pjsua_var.ua_cfg.cb.on_acc_find_for_incoming)(rdata, &id);
+
+    /* Verify if the specified account id is valid */
+    if (!pjsua_acc_is_valid(id))
+	id = pjsua_var.default_acc;
+
+    return id;
+}
+
+
+/*
+ * Create arbitrary requests for this account. 
+ */
+PJ_DEF(pj_status_t) pjsua_acc_create_request(pjsua_acc_id acc_id,
+					     const pjsip_method *method,
+					     const pj_str_t *target,
+					     pjsip_tx_data **p_tdata)
+{
+    pjsip_tx_data *tdata;
+    pjsua_acc *acc;
+    pjsip_route_hdr *r;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(method && target && p_tdata, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
+
+    acc = &pjsua_var.acc[acc_id];
+
+    status = pjsip_endpt_create_request(pjsua_var.endpt, method, target, 
+					&acc->cfg.id, target,
+					NULL, NULL, -1, NULL, &tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create request", status);
+	return status;
+    }
+
+    /* Copy routeset */
+    r = acc->route_set.next;
+    while (r != &acc->route_set) {
+	pjsip_msg_add_hdr(tdata->msg, 
+			  (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, r));
+	r = r->next;
+    }
+
+    /* If account is locked to specific transport, then set that transport to
+     * the transmit data.
+     */
+    if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
+	pjsip_tpselector tp_sel;
+
+	pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
+	pjsip_tx_data_set_transport(tdata, &tp_sel);
+    }
+
+    /* If via_addr is set, use this address for the Via header. */
+    if (pjsua_var.acc[acc_id].cfg.allow_via_rewrite &&
+        pjsua_var.acc[acc_id].via_addr.host.slen > 0)
+    {
+        tdata->via_addr = pjsua_var.acc[acc_id].via_addr;
+        tdata->via_tp = pjsua_var.acc[acc_id].via_tp;
+    } else if (!pjsua_sip_acc_is_using_stun(acc_id)) {
+        /* Choose local interface to use in Via if acc is not using
+         * STUN
+         */
+        pjsua_acc_get_uac_addr(acc_id, tdata->pool,
+	                       target,
+	                       &tdata->via_addr,
+	                       NULL, NULL,
+	                       &tdata->via_tp);
+    }
+
+    /* Done */
+    *p_tdata = tdata;
+    return PJ_SUCCESS;
+}
+
+/* Get local transport address suitable to be used for Via or Contact address
+ * to send request to the specified destination URI.
+ */
+pj_status_t pjsua_acc_get_uac_addr(pjsua_acc_id acc_id,
+				   pj_pool_t *pool,
+				   const pj_str_t *dst_uri,
+				   pjsip_host_port *addr,
+				   pjsip_transport_type_e *p_tp_type,
+				   int *secure,
+				   const void **p_tp)
+{
+    pjsua_acc *acc;
+    pjsip_sip_uri *sip_uri;
+    pj_status_t status;
+    pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
+    unsigned flag;
+    pjsip_tpselector tp_sel;
+    pjsip_tpmgr *tpmgr;
+    pjsip_tpmgr_fla2_param tfla2_prm;
+
+    PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
+    acc = &pjsua_var.acc[acc_id];
+
+    /* If route-set is configured for the account, then URI is the
+     * first entry of the route-set.
+     */
+    if (!pj_list_empty(&acc->route_set)) {
+	sip_uri = (pjsip_sip_uri*)
+		  pjsip_uri_get_uri(acc->route_set.next->name_addr.uri);
+    } else {
+	pj_str_t tmp;
+	pjsip_uri *uri;
+
+	pj_strdup_with_null(pool, &tmp, dst_uri);
+
+	uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
+	if (uri == NULL)
+	    return PJSIP_EINVALIDURI;
+
+	/* For non-SIP scheme, route set should be configured */
+	if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
+	    return PJSIP_ENOROUTESET;
+
+	sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
+    }
+
+    /* Get transport type of the URI */
+    if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
+	tp_type = PJSIP_TRANSPORT_TLS;
+    else if (sip_uri->transport_param.slen == 0) {
+	tp_type = PJSIP_TRANSPORT_UDP;
+    } else
+	tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
+
+    if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
+	return PJSIP_EUNSUPTRANSPORT;
+
+    /* If destination URI specifies IPv6, then set transport type
+     * to use IPv6 as well.
+     */
+    if (pj_strchr(&sip_uri->host, ':'))
+	tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6);
+
+    flag = pjsip_transport_get_flag_from_type(tp_type);
+
+    /* Init transport selector. */
+    pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
+
+    /* Get local address suitable to send request from */
+    pjsip_tpmgr_fla2_param_default(&tfla2_prm);
+    tfla2_prm.tp_type = tp_type;
+    tfla2_prm.tp_sel = &tp_sel;
+    tfla2_prm.dst_host = sip_uri->host;
+    tfla2_prm.local_if = (!pjsua_sip_acc_is_using_stun(acc_id) ||
+	                  (flag & PJSIP_TRANSPORT_RELIABLE));
+
+    tpmgr = pjsip_endpt_get_tpmgr(pjsua_var.endpt);
+    status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    addr->host = tfla2_prm.ret_addr;
+    addr->port = tfla2_prm.ret_port;
+
+    if (p_tp_type)
+	*p_tp_type = tp_type;
+
+    if (secure) {
+	*secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
+    }
+
+    if (p_tp)
+	*p_tp = tfla2_prm.ret_tp;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool,
+						  pj_str_t *contact,
+						  pjsua_acc_id acc_id,
+						  const pj_str_t *suri)
+{
+    pjsua_acc *acc;
+    pj_status_t status;
+    pjsip_transport_type_e tp_type;
+    pjsip_host_port addr;
+    int secure;
+    const char *beginquote, *endquote;
+    char transport_param[32];
+    const char *ob = ";ob";
+
+    
+    PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
+    acc = &pjsua_var.acc[acc_id];
+
+    /* If force_contact is configured, then use use it */
+    if (acc->cfg.force_contact.slen) {
+	*contact = acc->cfg.force_contact;
+	return PJ_SUCCESS;
+    }
+
+    status = pjsua_acc_get_uac_addr(acc_id, pool, suri, &addr,
+                                    &tp_type, &secure, NULL);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Enclose IPv6 address in square brackets */
+    if (tp_type & PJSIP_TRANSPORT_IPV6) {
+	beginquote = "[";
+	endquote = "]";
+    } else {
+	beginquote = endquote = "";
+    }
+
+    /* Don't add transport parameter if it's UDP */
+    if (tp_type!=PJSIP_TRANSPORT_UDP && tp_type!=PJSIP_TRANSPORT_UDP6) {
+	pj_ansi_snprintf(transport_param, sizeof(transport_param),
+		         ";transport=%s",
+			 pjsip_transport_get_type_name(tp_type));
+    } else {
+	transport_param[0] = '\0';
+    }
+
+
+    /* Create the contact header */
+    contact->ptr = (char*)pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+    contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
+				     "%s%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s",
+				     (acc->display.slen?"\"" : ""),
+				     (int)acc->display.slen,
+				     acc->display.ptr,
+				     (acc->display.slen?"\" " : ""),
+				     ((secure && acc->is_sips)? "sips" : "sip"),
+				     (int)acc->user_part.slen,
+				     acc->user_part.ptr,
+				     (acc->user_part.slen?"@":""),
+				     beginquote,
+				     (int)addr.host.slen,
+				     addr.host.ptr,
+				     endquote,
+				     addr.port,
+				     transport_param,
+				     (int)acc->cfg.contact_uri_params.slen,
+				     acc->cfg.contact_uri_params.ptr,
+				     (acc->cfg.use_rfc5626? ob: ""),
+				     (int)acc->cfg.contact_params.slen,
+				     acc->cfg.contact_params.ptr);
+    if (contact->slen < 1 || contact->slen >= (int)PJSIP_MAX_URL_SIZE)
+	return PJ_ETOOSMALL;
+    return PJ_SUCCESS;
+}
+
+
+
+PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool,
+						  pj_str_t *contact,
+						  pjsua_acc_id acc_id,
+						  pjsip_rx_data *rdata )
+{
+    /* 
+     *  Section 12.1.1, paragraph about using SIPS URI in Contact.
+     *  If the request that initiated the dialog contained a SIPS URI 
+     *  in the Request-URI or in the top Record-Route header field value, 
+     *  if there was any, or the Contact header field if there was no 
+     *  Record-Route header field, the Contact header field in the response
+     *  MUST be a SIPS URI.
+     */
+    pjsua_acc *acc;
+    pjsip_sip_uri *sip_uri;
+    pj_status_t status;
+    pjsip_transport_type_e tp_type = PJSIP_TRANSPORT_UNSPECIFIED;
+    pj_str_t local_addr;
+    pjsip_tpselector tp_sel;
+    pjsip_tpmgr *tpmgr;
+    pjsip_tpmgr_fla2_param tfla2_prm;
+    unsigned flag;
+    int secure;
+    int local_port;
+    const char *beginquote, *endquote;
+    char transport_param[32];
+    
+    PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
+    acc = &pjsua_var.acc[acc_id];
+
+    /* If force_contact is configured, then use use it */
+    if (acc->cfg.force_contact.slen) {
+	*contact = acc->cfg.force_contact;
+	return PJ_SUCCESS;
+    }
+
+    /* If Record-Route is present, then URI is the top Record-Route. */
+    if (rdata->msg_info.record_route) {
+	sip_uri = (pjsip_sip_uri*) 
+		pjsip_uri_get_uri(rdata->msg_info.record_route->name_addr.uri);
+    } else {
+	pjsip_hdr *pos = NULL;
+	pjsip_contact_hdr *h_contact;
+	pjsip_uri *uri = NULL;
+
+	/* Otherwise URI is Contact URI.
+	 * Iterate the Contact URI until we find sip: or sips: scheme.
+	 */
+	do {
+	    h_contact = (pjsip_contact_hdr*)
+			pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,
+					   pos);
+	    if (h_contact) {
+		if (h_contact->uri)
+		    uri = (pjsip_uri*) pjsip_uri_get_uri(h_contact->uri);
+		else
+		    uri = NULL;
+		if (!uri || (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
+		             !PJSIP_URI_SCHEME_IS_SIPS(uri)))
+		{
+		    pos = (pjsip_hdr*)h_contact->next;
+		    if (pos == &rdata->msg_info.msg->hdr)
+			h_contact = NULL;
+		} else {
+		    break;
+		}
+	    }
+	} while (h_contact);
+	
+
+	/* Or if Contact URI is not present, take the remote URI from
+	 * the From URI.
+	 */
+	if (uri == NULL)
+	    uri = (pjsip_uri*) pjsip_uri_get_uri(rdata->msg_info.from->uri);
+
+
+	/* Can only do sip/sips scheme at present. */
+	if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
+	    return PJSIP_EINVALIDREQURI;
+
+	sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
+    }
+
+    /* Get transport type of the URI */
+    if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri))
+	tp_type = PJSIP_TRANSPORT_TLS;
+    else if (sip_uri->transport_param.slen == 0) {
+	tp_type = PJSIP_TRANSPORT_UDP;
+    } else
+	tp_type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
+
+    if (tp_type == PJSIP_TRANSPORT_UNSPECIFIED)
+	return PJSIP_EUNSUPTRANSPORT;
+
+    /* If destination URI specifies IPv6, then set transport type
+     * to use IPv6 as well.
+     */
+    if (pj_strchr(&sip_uri->host, ':'))
+	tp_type = (pjsip_transport_type_e)(((int)tp_type) + PJSIP_TRANSPORT_IPV6);
+
+    flag = pjsip_transport_get_flag_from_type(tp_type);
+    secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
+
+    /* Init transport selector. */
+    pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
+
+    /* Get local address suitable to send request from */
+    pjsip_tpmgr_fla2_param_default(&tfla2_prm);
+    tfla2_prm.tp_type = tp_type;
+    tfla2_prm.tp_sel = &tp_sel;
+    tfla2_prm.dst_host = sip_uri->host;
+    tfla2_prm.local_if = (!pjsua_sip_acc_is_using_stun(acc_id) ||
+	                  (flag & PJSIP_TRANSPORT_RELIABLE));
+
+    tpmgr = pjsip_endpt_get_tpmgr(pjsua_var.endpt);
+    status = pjsip_tpmgr_find_local_addr2(tpmgr, pool, &tfla2_prm);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    local_addr = tfla2_prm.ret_addr;
+    local_port = tfla2_prm.ret_port;
+
+
+    /* Enclose IPv6 address in square brackets */
+    if (tp_type & PJSIP_TRANSPORT_IPV6) {
+	beginquote = "[";
+	endquote = "]";
+    } else {
+	beginquote = endquote = "";
+    }
+
+    /* Don't add transport parameter if it's UDP */
+    if (tp_type!=PJSIP_TRANSPORT_UDP && tp_type!=PJSIP_TRANSPORT_UDP6) {
+	pj_ansi_snprintf(transport_param, sizeof(transport_param),
+		         ";transport=%s",
+			 pjsip_transport_get_type_name(tp_type));
+    } else {
+	transport_param[0] = '\0';
+    }
+
+
+    /* Create the contact header */
+    contact->ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+    contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE,
+				     "%s%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s",
+				     (acc->display.slen?"\"" : ""),
+				     (int)acc->display.slen,
+				     acc->display.ptr,
+				     (acc->display.slen?"\" " : ""),
+				     ((secure && acc->is_sips)? "sips" : "sip"),
+				     (int)acc->user_part.slen,
+				     acc->user_part.ptr,
+				     (acc->user_part.slen?"@":""),
+				     beginquote,
+				     (int)local_addr.slen,
+				     local_addr.ptr,
+				     endquote,
+				     local_port,
+				     transport_param,
+				     (int)acc->cfg.contact_uri_params.slen,
+				     acc->cfg.contact_uri_params.ptr,
+				     (int)acc->cfg.contact_params.slen,
+				     acc->cfg.contact_params.ptr);
+    if (contact->slen < 1 || contact->slen >= (int)PJSIP_MAX_URL_SIZE)
+	return PJ_ETOOSMALL;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjsua_acc_set_transport( pjsua_acc_id acc_id,
+					     pjsua_transport_id tp_id)
+{
+    pjsua_acc *acc;
+
+    PJ_ASSERT_RETURN(pjsua_acc_is_valid(acc_id), PJ_EINVAL);
+    acc = &pjsua_var.acc[acc_id];
+
+    PJ_ASSERT_RETURN(tp_id >= 0 && tp_id < (int)PJ_ARRAY_SIZE(pjsua_var.tpdata),
+		     PJ_EINVAL);
+    
+    acc->cfg.transport_id = tp_id;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Auto re-registration timeout callback */
+static void auto_rereg_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te)
+{
+    pjsua_acc *acc;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(th);
+    acc = (pjsua_acc*) te->user_data;
+    pj_assert(acc);
+
+    PJSUA_LOCK();
+
+    /* Check if the reregistration timer is still valid, e.g: while waiting
+     * timeout timer application might have deleted the account or disabled
+     * the auto-reregistration.
+     */
+    if (!acc->valid || !acc->auto_rereg.active || 
+	acc->cfg.reg_retry_interval == 0)
+    {
+	goto on_return;
+    }
+
+    /* Start re-registration */
+    acc->auto_rereg.attempt_cnt++;
+    status = pjsua_acc_set_registration(acc->index, PJ_TRUE);
+    if (status != PJ_SUCCESS)
+	schedule_reregistration(acc);
+
+on_return:
+    PJSUA_UNLOCK();
+}
+
+
+/* Schedule reregistration for specified account. Note that the first 
+ * re-registration after a registration failure will be done immediately.
+ * Also note that this function should be called within PJSUA mutex.
+ */
+static void schedule_reregistration(pjsua_acc *acc)
+{
+    pj_time_val delay;
+
+    pj_assert(acc);
+
+    /* Validate the account and re-registration feature status */
+    if (!acc->valid || acc->cfg.reg_retry_interval == 0) {
+	return;
+    }
+
+    /* If configured, disconnect calls of this account after the first
+     * reregistration attempt failed.
+     */
+    if (acc->cfg.drop_calls_on_reg_fail && acc->auto_rereg.attempt_cnt >= 1)
+    {
+	unsigned i, cnt;
+
+	for (i = 0, cnt = 0; i < pjsua_var.ua_cfg.max_calls; ++i) {
+	    if (pjsua_var.calls[i].acc_id == acc->index) {
+		pjsua_call_hangup(i, 0, NULL, NULL);
+		++cnt;
+	    }
+	}
+
+	if (cnt) {
+	    PJ_LOG(3, (THIS_FILE, "Disconnecting %d call(s) of account #%d "
+				  "after reregistration attempt failed",
+				  cnt, acc->index));
+	}
+    }
+
+    /* Cancel any re-registration timer */
+    if (acc->auto_rereg.timer.id) {
+	acc->auto_rereg.timer.id = PJ_FALSE;
+	pjsua_cancel_timer(&acc->auto_rereg.timer);
+    }
+
+    /* Update re-registration flag */
+    acc->auto_rereg.active = PJ_TRUE;
+
+    /* Set up timer for reregistration */
+    acc->auto_rereg.timer.cb = &auto_rereg_timer_cb;
+    acc->auto_rereg.timer.user_data = acc;
+
+    /* Reregistration attempt. The first attempt will be done immediately. */
+    delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval :
+					     acc->cfg.reg_first_retry_interval;
+    delay.msec = 0;
+
+    /* Randomize interval by +/- 10 secs */
+    if (delay.sec >= 10) {
+	delay.msec = -10000 + (pj_rand() % 20000);
+    } else {
+	delay.sec = 0;
+	delay.msec = (pj_rand() % 10000);
+    }
+    pj_time_val_normalize(&delay);
+
+    PJ_LOG(4,(THIS_FILE,
+	      "Scheduling re-registration retry for acc %d in %u seconds..",
+	      acc->index, delay.sec));
+
+    acc->auto_rereg.timer.id = PJ_TRUE;
+    if (pjsua_schedule_timer(&acc->auto_rereg.timer, &delay) != PJ_SUCCESS)
+	acc->auto_rereg.timer.id = PJ_FALSE;
+}
+
+
+/* Internal function to perform auto-reregistration on transport 
+ * connection/disconnection events.
+ */
+void pjsua_acc_on_tp_state_changed(pjsip_transport *tp,
+				   pjsip_transport_state state,
+				   const pjsip_transport_state_info *info)
+{
+    unsigned i;
+
+    PJ_UNUSED_ARG(info);
+
+    /* Only care for transport disconnection events */
+    if (state != PJSIP_TP_STATE_DISCONNECTED)
+	return;
+
+    PJ_LOG(4,(THIS_FILE, "Disconnected notification for transport %s",
+	      tp->obj_name));
+    pj_log_push_indent();
+
+    /* Shutdown this transport, to make sure that the transport manager 
+     * will create a new transport for reconnection.
+     */
+    pjsip_transport_shutdown(tp);
+
+    PJSUA_LOCK();
+
+    /* Enumerate accounts using this transport and perform actions
+     * based on the transport state.
+     */
+    for (i = 0; i < PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+	pjsua_acc *acc = &pjsua_var.acc[i];
+
+	/* Skip if this account is not valid OR auto re-registration
+	 * feature is disabled OR this transport is not used by this account.
+	 */
+	if (!acc->valid || !acc->cfg.reg_retry_interval || 
+	    tp != acc->auto_rereg.reg_tp)
+	{
+	    continue;
+	}
+
+	/* Release regc transport immediately
+	 * See https://trac.pjsip.org/repos/ticket/1481
+	 */
+	if (pjsua_var.acc[i].regc) {
+	    pjsip_regc_release_transport(pjsua_var.acc[i].regc);
+	}
+
+	/* Schedule reregistration for this account */
+	schedule_reregistration(acc);
+    }
+
+    PJSUA_UNLOCK();
+    pj_log_pop_indent();
+}