* #27232: jni: added pjproject checkout as regular git content

We will remove it once the next release of pjsip (with Android support)
comes out and is merged into SFLphone.
diff --git a/jni/pjproject-android/.svn/pristine/8f/8f32797e879e5e1231acf9832102ee93c4985ffe.svn-base b/jni/pjproject-android/.svn/pristine/8f/8f32797e879e5e1231acf9832102ee93c4985ffe.svn-base
new file mode 100644
index 0000000..06bfaf0
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/8f/8f32797e879e5e1231acf9832102ee93c4985ffe.svn-base
@@ -0,0 +1,1089 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjsip-ua/sip_timer.h>
+#include <pjsip/print_util.h>
+#include <pjsip/sip_endpoint.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+
+#define THIS_FILE		"sip_timer.c"
+
+
+/* Constant of Session Timers */
+#define ABS_MIN_SE		90	/* Absolute Min-SE, in seconds	    */
+
+
+/* String definitions */
+static const pj_str_t STR_SE		= {"Session-Expires", 15};
+static const pj_str_t STR_SHORT_SE	= {"x", 1};
+static const pj_str_t STR_MIN_SE	= {"Min-SE", 6};
+static const pj_str_t STR_REFRESHER	= {"refresher", 9};
+static const pj_str_t STR_UAC		= {"uac", 3};
+static const pj_str_t STR_UAS		= {"uas", 3};
+static const pj_str_t STR_TIMER		= {"timer", 5};
+
+
+/* Enumeration of refresher */
+enum timer_refresher {
+    TR_UNKNOWN,
+    TR_UAC,
+    TR_UAS
+};
+
+/* Structure definition of Session Timers */
+struct pjsip_timer 
+{
+    pj_bool_t			 active;	/**< Active/inactive flag   */
+    pjsip_timer_setting		 setting;	/**< Session Timers setting */
+    enum timer_refresher	 refresher;	/**< Session refresher	    */
+    pj_time_val			 last_refresh;	/**< Timestamp of last
+						     refresh		    */
+    pj_timer_entry		 timer;		/**< Timer entry	    */
+    pj_bool_t			 use_update;	/**< Use UPDATE method to
+						     refresh the session    */
+    pj_bool_t		  	 with_sdp;	/**< SDP in UPDATE?	    */
+    pjsip_role_e		 role;		/**< Role in last INVITE/
+						     UPDATE transaction.    */
+
+};
+
+/* External global vars */
+extern pj_bool_t pjsip_use_compact_form;
+
+/* Local functions & vars */
+static void stop_timer(pjsip_inv_session *inv);
+static void start_timer(pjsip_inv_session *inv);
+static pj_bool_t is_initialized;
+const pjsip_method pjsip_update_method = { PJSIP_OTHER_METHOD, {"UPDATE", 6}};
+/*
+ * Session-Expires header vptr.
+ */
+static int se_hdr_print(pjsip_sess_expires_hdr *hdr, 
+			char *buf, pj_size_t size);
+static pjsip_sess_expires_hdr* se_hdr_clone(pj_pool_t *pool, 
+					    const pjsip_sess_expires_hdr *hdr);
+static pjsip_sess_expires_hdr* se_hdr_shallow_clone( 
+					    pj_pool_t *pool,
+					    const pjsip_sess_expires_hdr* hdr);
+
+static pjsip_hdr_vptr se_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &se_hdr_clone,
+    (pjsip_hdr_clone_fptr) &se_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &se_hdr_print,
+};
+
+/*
+ * Min-SE header vptr.
+ */
+static int min_se_hdr_print(pjsip_min_se_hdr *hdr, 
+			    char *buf, pj_size_t size);
+static pjsip_min_se_hdr* min_se_hdr_clone(pj_pool_t *pool, 
+					  const pjsip_min_se_hdr *hdr);
+static pjsip_min_se_hdr* min_se_hdr_shallow_clone( 
+					  pj_pool_t *pool,
+					  const pjsip_min_se_hdr* hdr);
+
+static pjsip_hdr_vptr min_se_hdr_vptr = 
+{
+    (pjsip_hdr_clone_fptr) &min_se_hdr_clone,
+    (pjsip_hdr_clone_fptr) &min_se_hdr_shallow_clone,
+    (pjsip_hdr_print_fptr) &min_se_hdr_print,
+};
+
+/*
+ * Session-Expires header vptr.
+ */
+static int se_hdr_print(pjsip_sess_expires_hdr *hdr, 
+			char *buf, pj_size_t size)
+{
+    char *p = buf;
+    char *endbuf = buf+size;
+    pj_ssize_t printed;
+    const pjsip_parser_const_t *pc = pjsip_parser_const();
+    const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name;
+
+    /* Print header name and value */
+    if ((endbuf - p) < (hname->slen + 16))
+	return -1;
+
+    copy_advance(p, (*hname));
+    *p++ = ':';
+    *p++ = ' ';
+
+    printed = pj_utoa(hdr->sess_expires, p);
+    p += printed;
+
+    /* Print 'refresher' param */
+    if (hdr->refresher.slen)
+    {
+	if  ((endbuf - p) < (STR_REFRESHER.slen + 2 + hdr->refresher.slen))
+	    return -1;
+
+	*p++ = ';';
+	copy_advance(p, STR_REFRESHER);
+	*p++ = '=';
+	copy_advance(p, hdr->refresher);
+    }
+
+    /* Print generic params */
+    printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p,
+				   &pc->pjsip_TOKEN_SPEC, 
+				   &pc->pjsip_TOKEN_SPEC, ';');
+    if (printed < 0)
+	return (int)printed;
+
+    p += printed;
+    return (int)(p - buf);
+}
+
+static pjsip_sess_expires_hdr* se_hdr_clone(pj_pool_t *pool, 
+					    const pjsip_sess_expires_hdr *hsrc)
+{
+    pjsip_sess_expires_hdr *hdr = pjsip_sess_expires_hdr_create(pool);
+    hdr->sess_expires = hsrc->sess_expires;
+    pj_strdup(pool, &hdr->refresher, &hsrc->refresher);
+    pjsip_param_clone(pool, &hdr->other_param, &hsrc->other_param);
+    return hdr;
+}
+
+static pjsip_sess_expires_hdr* se_hdr_shallow_clone( 
+					    pj_pool_t *pool,
+					    const pjsip_sess_expires_hdr* hsrc)
+{
+    pjsip_sess_expires_hdr *hdr = PJ_POOL_ALLOC_T(pool,pjsip_sess_expires_hdr);
+    pj_memcpy(hdr, hsrc, sizeof(*hdr));
+    pjsip_param_shallow_clone(pool, &hdr->other_param, &hsrc->other_param);
+    return hdr;
+}
+
+/*
+ * Min-SE header vptr.
+ */
+static int min_se_hdr_print(pjsip_min_se_hdr *hdr, 
+			    char *buf, pj_size_t size)
+{
+    char *p = buf;
+    char *endbuf = buf+size;
+    pj_ssize_t printed;
+    const pjsip_parser_const_t *pc = pjsip_parser_const();
+
+    /* Print header name and value */
+    if ((endbuf - p) < (hdr->name.slen + 16))
+	return -1;
+
+    copy_advance(p, hdr->name);
+    *p++ = ':';
+    *p++ = ' ';
+
+    printed = pj_utoa(hdr->min_se, p);
+    p += printed;
+
+    /* Print generic params */
+    printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p,
+				   &pc->pjsip_TOKEN_SPEC, 
+				   &pc->pjsip_TOKEN_SPEC, ';');
+    if (printed < 0)
+	return (int)printed;
+
+    p += printed;
+    return (int)(p - buf);
+}
+
+static pjsip_min_se_hdr* min_se_hdr_clone(pj_pool_t *pool, 
+					  const pjsip_min_se_hdr *hsrc)
+{
+    pjsip_min_se_hdr *hdr = pjsip_min_se_hdr_create(pool);
+    hdr->min_se = hsrc->min_se;
+    pjsip_param_clone(pool, &hdr->other_param, &hsrc->other_param);
+    return hdr;
+}
+
+static pjsip_min_se_hdr* min_se_hdr_shallow_clone( 
+					  pj_pool_t *pool,
+					  const pjsip_min_se_hdr* hsrc)
+{
+    pjsip_min_se_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_min_se_hdr);
+    pj_memcpy(hdr, hsrc, sizeof(*hdr));
+    pjsip_param_shallow_clone(pool, &hdr->other_param, &hsrc->other_param);
+    return hdr;
+}
+
+
+/*
+ * Parse Session-Expires header.
+ */
+static pjsip_hdr *parse_hdr_se(pjsip_parse_ctx *ctx)
+{
+    pjsip_sess_expires_hdr *hdr = pjsip_sess_expires_hdr_create(ctx->pool);
+    const pjsip_parser_const_t *pc = pjsip_parser_const();
+    pj_str_t token;
+
+    pj_scan_get(ctx->scanner, &pc->pjsip_DIGIT_SPEC, &token);
+    hdr->sess_expires = pj_strtoul(&token);
+
+    while (*ctx->scanner->curptr == ';') {
+	pj_str_t pname, pvalue;
+
+	pj_scan_get_char(ctx->scanner);
+	pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
+
+	if (pj_stricmp(&pname, &STR_REFRESHER)==0) {
+	    hdr->refresher = pvalue;
+	} else {
+	    pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param);
+	    param->name = pname;
+	    param->value = pvalue;
+	    pj_list_push_back(&hdr->other_param, param);
+	}
+    }
+    pjsip_parse_end_hdr_imp( ctx->scanner );
+    return (pjsip_hdr*)hdr;
+}
+
+/*
+ * Parse Min-SE header.
+ */
+static pjsip_hdr *parse_hdr_min_se(pjsip_parse_ctx *ctx)
+{
+    pjsip_min_se_hdr *hdr = pjsip_min_se_hdr_create(ctx->pool);
+    const pjsip_parser_const_t *pc = pjsip_parser_const();
+    pj_str_t token;
+
+    pj_scan_get(ctx->scanner, &pc->pjsip_DIGIT_SPEC, &token);
+    hdr->min_se = pj_strtoul(&token);
+
+    while (*ctx->scanner->curptr == ';') {
+	pj_str_t pname, pvalue;
+	pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param);
+
+	pj_scan_get_char(ctx->scanner);
+	pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
+
+	param->name = pname;
+	param->value = pvalue;
+	pj_list_push_back(&hdr->other_param, param);
+    }
+    pjsip_parse_end_hdr_imp( ctx->scanner );
+    return (pjsip_hdr*)hdr;
+}
+
+
+/* Add "Session-Expires" and "Min-SE" headers. Note that "Min-SE" header
+ * can only be added to INVITE/UPDATE request and 422 response.
+ */
+static void add_timer_headers(pjsip_inv_session *inv, pjsip_tx_data *tdata,
+			      pj_bool_t add_se, pj_bool_t add_min_se)
+{
+    pjsip_timer *timer = inv->timer;
+
+    /* Add Session-Expires header */
+    if (add_se) {
+	pjsip_sess_expires_hdr *hdr;
+
+	hdr = pjsip_sess_expires_hdr_create(tdata->pool);
+	hdr->sess_expires = timer->setting.sess_expires;
+	if (timer->refresher != TR_UNKNOWN)
+	    hdr->refresher = (timer->refresher == TR_UAC? STR_UAC : STR_UAS);
+
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) hdr);
+    }
+
+    /* Add Min-SE header */
+    if (add_min_se) {
+	pjsip_min_se_hdr *hdr;
+
+	hdr = pjsip_min_se_hdr_create(tdata->pool);
+	hdr->min_se = timer->setting.min_se;
+
+	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) hdr);
+    }
+}
+
+/* Timer callback. When the timer is fired, it can be time to refresh
+ * the session if UA is the refresher, otherwise it is time to end 
+ * the session.
+ */
+static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
+{
+    pjsip_inv_session *inv = (pjsip_inv_session*) entry->user_data;
+    pjsip_tx_data *tdata = NULL;
+    pj_status_t status;
+    pj_bool_t as_refresher;
+
+    pj_assert(inv);
+
+    inv->timer->timer.id = 0;
+
+    PJ_UNUSED_ARG(timer_heap);
+
+    /* Lock dialog. */
+    pjsip_dlg_inc_lock(inv->dlg);
+
+    /* Check our role */
+    as_refresher =
+	(inv->timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) ||
+	(inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS);
+
+    /* Do action based on role, refresher or refreshee */
+    if (as_refresher) {
+	pj_time_val now;
+
+	/* As refresher, reshedule the refresh request on the following:
+	 *  - msut not send re-INVITE if another INVITE or SDP negotiation
+	 *    is in progress.
+	 *  - must not send UPDATE with SDP if SDP negotiation is in progress
+	 */
+	pjmedia_sdp_neg_state neg_state = pjmedia_sdp_neg_get_state(inv->neg);
+	if ( (!inv->timer->use_update && (
+			inv->invite_tsx != NULL ||
+			neg_state != PJMEDIA_SDP_NEG_STATE_DONE)
+             )
+	     ||
+	     (inv->timer->use_update && inv->timer->with_sdp &&
+		     neg_state != PJMEDIA_SDP_NEG_STATE_DONE
+	     )
+	   )
+	{
+	    pj_time_val delay = {1, 0};
+
+	    inv->timer->timer.id = 1;
+	    pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer,
+				       &delay);
+	    pjsip_dlg_dec_lock(inv->dlg);
+	    return;
+	}
+
+	/* Refresher, refresh the session */
+	if (inv->timer->use_update) {
+	    const pjmedia_sdp_session *offer = NULL;
+
+	    if (inv->timer->with_sdp) {
+		pjmedia_sdp_neg_get_active_local(inv->neg, &offer);
+	    }
+	    status = pjsip_inv_update(inv, NULL, offer, &tdata);
+	} else {
+	    /* Create re-INVITE without modifying session */
+	    pjsip_msg_body *body;
+	    const pjmedia_sdp_session *offer = NULL;
+
+	    pj_assert(pjmedia_sdp_neg_get_state(inv->neg) == 
+		      PJMEDIA_SDP_NEG_STATE_DONE);
+
+	    status = pjsip_inv_invite(inv, &tdata);
+	    if (status == PJ_SUCCESS)
+		status = pjmedia_sdp_neg_send_local_offer(inv->pool_prov, 
+							  inv->neg, &offer);
+	    if (status == PJ_SUCCESS)
+		status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
+	    if (status == PJ_SUCCESS) {
+		status = pjsip_create_sdp_body(tdata->pool, 
+					(pjmedia_sdp_session*)offer, &body);
+		tdata->msg->body = body;
+	    }
+	}
+
+	pj_gettimeofday(&now);
+	PJ_LOG(4, (inv->pool->obj_name,
+		   "Refreshing session after %ds (expiration period=%ds)",
+		   (now.sec-inv->timer->last_refresh.sec),
+		   inv->timer->setting.sess_expires));
+    } else {
+	
+	pj_time_val now;
+
+	/* Refreshee, terminate the session */
+	status = pjsip_inv_end_session(inv, PJSIP_SC_REQUEST_TIMEOUT, 
+				       NULL, &tdata);
+
+	pj_gettimeofday(&now);
+	PJ_LOG(3, (inv->pool->obj_name, 
+		   "No session refresh received after %ds "
+		   "(expiration period=%ds), stopping session now!",
+		   (now.sec-inv->timer->last_refresh.sec),
+		   inv->timer->setting.sess_expires));
+    }
+
+    /* Unlock dialog. */
+    pjsip_dlg_dec_lock(inv->dlg);
+
+    /* Send message, if any */
+    if (tdata && status == PJ_SUCCESS) {
+	status = pjsip_inv_send_msg(inv, tdata);
+    }
+
+    /* Print error message, if any */
+    if (status != PJ_SUCCESS) {
+	PJ_PERROR(2, (inv->pool->obj_name, status,
+		      "Error in %s session timer",
+		      (as_refresher? "refreshing" : "terminating")));
+    }
+}
+
+/* Start Session Timers */
+static void start_timer(pjsip_inv_session *inv)
+{
+    const pj_str_t UPDATE = { "UPDATE", 6 };
+    pjsip_timer *timer = inv->timer;
+    pj_time_val delay = {0};
+
+    pj_assert(inv->timer->active == PJ_TRUE);
+
+    stop_timer(inv);
+
+    inv->timer->use_update =
+	    (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL,
+				      &UPDATE) == PJSIP_DIALOG_CAP_SUPPORTED);
+    if (!inv->timer->use_update) {
+	/* INVITE always needs SDP */
+	inv->timer->with_sdp = PJ_TRUE;
+    }
+
+    pj_timer_entry_init(&timer->timer,
+			1,		    /* id */
+			inv,		    /* user data */
+			timer_cb);	    /* callback */
+    
+    /* Set delay based on role, refresher or refreshee */
+    if ((timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) ||
+	(timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS))
+    {
+	/* Next refresh, the delay is half of session expire */
+	delay.sec = timer->setting.sess_expires / 2;
+    } else {
+	/* Send BYE if no refresh received until this timer fired, delay
+	 * is the minimum of 32 seconds and one third of the session interval
+	 * before session expiration.
+	 */
+	delay.sec = timer->setting.sess_expires - 
+		    timer->setting.sess_expires/3;
+	delay.sec = PJ_MAX((long)timer->setting.sess_expires-32, delay.sec);
+    }
+
+    /* Schedule the timer */
+    pjsip_endpt_schedule_timer(inv->dlg->endpt, &timer->timer, &delay);
+
+    /* Update last refresh time */
+    pj_gettimeofday(&timer->last_refresh);
+}
+
+/* Stop Session Timers */
+static void stop_timer(pjsip_inv_session *inv)
+{
+    if (inv->timer->timer.id != 0) {
+	pjsip_endpt_cancel_timer(inv->dlg->endpt, &inv->timer->timer);
+	inv->timer->timer.id = 0;
+    }
+}
+
+/* Deinitialize Session Timers */
+static void pjsip_timer_deinit_module(pjsip_endpoint *endpt)
+{
+    PJ_TODO(provide_initialized_flag_for_each_endpoint);
+    PJ_UNUSED_ARG(endpt);
+    is_initialized = PJ_FALSE;
+}
+
+/*
+ * Initialize Session Timers support in PJSIP. 
+ */
+PJ_DEF(pj_status_t) pjsip_timer_init_module(pjsip_endpoint *endpt)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
+
+    if (is_initialized)
+	return PJ_SUCCESS;
+
+    /* Register Session-Expires header parser */
+    status = pjsip_register_hdr_parser( STR_SE.ptr, STR_SHORT_SE.ptr, 
+				        &parse_hdr_se);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Register Min-SE header parser */
+    status = pjsip_register_hdr_parser( STR_MIN_SE.ptr, NULL, 
+				        &parse_hdr_min_se);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Register 'timer' capability to endpoint */
+    status = pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED,
+					NULL, 1, &STR_TIMER);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Register deinit module to be executed when PJLIB shutdown */
+    if (pjsip_endpt_atexit(endpt, &pjsip_timer_deinit_module) != PJ_SUCCESS) {
+	/* Failure to register this function may cause this module won't 
+	 * work properly when the stack is restarted (without quitting 
+	 * application).
+	 */
+	pj_assert(!"Failed to register Session Timer deinit.");
+	PJ_LOG(1, (THIS_FILE, "Failed to register Session Timer deinit."));
+    }
+
+    is_initialized = PJ_TRUE;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Initialize Session Timers setting with default values.
+ */
+PJ_DEF(pj_status_t) pjsip_timer_setting_default(pjsip_timer_setting *setting)
+{
+    pj_bzero(setting, sizeof(pjsip_timer_setting));
+
+    setting->sess_expires = PJSIP_SESS_TIMER_DEF_SE;
+    setting->min_se = ABS_MIN_SE;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Initialize Session Timers in an INVITE session. 
+ */
+PJ_DEF(pj_status_t) pjsip_timer_init_session(
+					pjsip_inv_session *inv,
+					const pjsip_timer_setting *setting)
+{
+    pjsip_timer_setting *s;
+
+    pj_assert(is_initialized);
+    PJ_ASSERT_RETURN(inv, PJ_EINVAL);
+
+    /* Allocate and/or reset Session Timers structure */
+    if (!inv->timer)
+	inv->timer = PJ_POOL_ZALLOC_T(inv->pool, pjsip_timer);
+    else
+	pj_bzero(inv->timer, sizeof(pjsip_timer));
+
+    s = &inv->timer->setting;
+
+    /* Init Session Timers setting */
+    if (setting) {
+	PJ_ASSERT_RETURN(setting->min_se >= ABS_MIN_SE,
+			 PJ_ETOOSMALL);
+	PJ_ASSERT_RETURN(setting->sess_expires >= setting->min_se,
+			 PJ_EINVAL);
+
+	pj_memcpy(s, setting, sizeof(*s));
+    } else {
+	pjsip_timer_setting_default(s);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Create Session-Expires header.
+ */
+PJ_DEF(pjsip_sess_expires_hdr*) pjsip_sess_expires_hdr_create(
+							pj_pool_t *pool)
+{
+    pjsip_sess_expires_hdr *hdr = PJ_POOL_ZALLOC_T(pool,
+						   pjsip_sess_expires_hdr);
+
+    pj_assert(is_initialized);
+
+    hdr->type = PJSIP_H_OTHER;
+    hdr->name = STR_SE;
+    hdr->sname = STR_SHORT_SE;
+    hdr->vptr = &se_hdr_vptr;
+    pj_list_init(hdr);
+    pj_list_init(&hdr->other_param);
+    return hdr;
+}
+
+
+/*
+ * Create Min-SE header.
+ */
+PJ_DEF(pjsip_min_se_hdr*) pjsip_min_se_hdr_create(pj_pool_t *pool)
+{
+    pjsip_min_se_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_min_se_hdr);
+
+    pj_assert(is_initialized);
+
+    hdr->type = PJSIP_H_OTHER;
+    hdr->name = STR_MIN_SE;
+    hdr->vptr = &min_se_hdr_vptr;
+    pj_list_init(hdr);
+    pj_list_init(&hdr->other_param);
+    return hdr;
+}
+
+
+/* 
+ * This function generates headers for Session Timers for intial and
+ * refresh INVITE or UPDATE.
+ */
+PJ_DEF(pj_status_t) pjsip_timer_update_req(pjsip_inv_session *inv,
+					   pjsip_tx_data *tdata)
+{
+    PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL);
+
+    /* Check if Session Timers is supported */
+    if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0)
+	return PJ_SUCCESS;
+
+    pj_assert(is_initialized);
+
+    /* Make sure Session Timers is initialized */
+    if (inv->timer == NULL)
+	pjsip_timer_init_session(inv, NULL);
+
+    /* If refresher role (i.e: ours or peer) has been set/negotiated, 
+     * better to keep it.
+     */
+    if (inv->timer->refresher != TR_UNKNOWN) {
+	pj_bool_t as_refresher;
+
+	/* Check our refresher role */
+	as_refresher = 
+	    (inv->timer->refresher==TR_UAC && inv->timer->role==PJSIP_ROLE_UAC) ||
+	    (inv->timer->refresher==TR_UAS && inv->timer->role==PJSIP_ROLE_UAS);
+
+	/* Update transaction role */
+	inv->timer->role = PJSIP_ROLE_UAC;
+
+	/* Update refresher role */
+	inv->timer->refresher = as_refresher? TR_UAC : TR_UAS;
+    }
+
+    /* Add Session Timers headers */
+    add_timer_headers(inv, tdata, PJ_TRUE, PJ_TRUE);
+
+    return PJ_SUCCESS;
+}
+
+/* 
+ * This function will handle Session Timers part of INVITE/UPDATE 
+ * responses with code:
+ * - 422 (Session Interval Too Small)
+ * - 2xx final response
+ */
+PJ_DEF(pj_status_t) pjsip_timer_process_resp(pjsip_inv_session *inv,
+					     const pjsip_rx_data *rdata,
+					     pjsip_status_code *st_code)
+{
+    const pjsip_msg *msg;
+
+    PJ_ASSERT_ON_FAIL(inv && rdata,
+	{if(st_code)*st_code=PJSIP_SC_INTERNAL_SERVER_ERROR;return PJ_EINVAL;});
+
+    /* Check if Session Timers is supported */
+    if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0)
+	return PJ_SUCCESS;
+
+    pj_assert(is_initialized);
+
+    msg = rdata->msg_info.msg;
+    pj_assert(msg->type == PJSIP_RESPONSE_MSG);
+
+    /* Only process response of INVITE or UPDATE */
+    if (rdata->msg_info.cseq->method.id != PJSIP_INVITE_METHOD &&
+	pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_update_method))
+    {
+	return PJ_SUCCESS;
+    }
+
+    if (msg->line.status.code == PJSIP_SC_SESSION_TIMER_TOO_SMALL) {
+	/* Our Session-Expires is too small, let's update it based on
+	 * Min-SE header in the response.
+	 */
+	pjsip_tx_data *tdata;
+	pjsip_min_se_hdr *min_se_hdr;
+	pjsip_hdr *hdr;
+	pjsip_via_hdr *via;
+
+	/* Get Min-SE value from response */
+	min_se_hdr = (pjsip_min_se_hdr*) 
+		     pjsip_msg_find_hdr_by_name(msg, &STR_MIN_SE, NULL);
+	if (min_se_hdr == NULL) {
+	    /* Response 422 should contain Min-SE header */
+	    return PJ_SUCCESS;
+	}
+
+	/* Session Timers should have been initialized here */
+	pj_assert(inv->timer);
+
+	/* Update Min-SE */
+	inv->timer->setting.min_se = PJ_MAX(min_se_hdr->min_se, 
+					    inv->timer->setting.min_se);
+
+	/* Update Session Timers setting */
+	if (inv->timer->setting.sess_expires < inv->timer->setting.min_se)
+	    inv->timer->setting.sess_expires = inv->timer->setting.min_se;
+
+	/* Prepare to restart the request */
+
+	/* Get the original INVITE request. */
+	tdata = inv->invite_req;
+
+	/* Remove branch param in Via header. */
+	via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+	pj_assert(via);
+	via->branch_param.slen = 0;
+
+	/* Restore strict route set.
+	 * See http://trac.pjsip.org/repos/ticket/492
+	 */
+	pjsip_restore_strict_route_set(tdata);
+
+	/* Must invalidate the message! */
+	pjsip_tx_data_invalidate_msg(tdata);
+
+	pjsip_tx_data_add_ref(tdata);
+
+	/* Update Session Timers headers */
+	hdr = (pjsip_hdr*) pjsip_msg_find_hdr_by_name(tdata->msg, 
+						      &STR_MIN_SE, NULL);
+	if (hdr != NULL) pj_list_erase(hdr);
+
+	hdr = (pjsip_hdr*) pjsip_msg_find_hdr_by_names(tdata->msg, &STR_SE,
+						       &STR_SHORT_SE, NULL);
+	if (hdr != NULL) pj_list_erase(hdr);
+
+	add_timer_headers(inv, tdata, PJ_TRUE, PJ_TRUE);
+
+	/* Restart UAC */
+	pjsip_inv_uac_restart(inv, PJ_FALSE);
+	pjsip_inv_send_msg(inv, tdata);
+
+	return PJ_SUCCESS;
+
+    } else if (msg->line.status.code/100 == 2) {
+
+	pjsip_sess_expires_hdr *se_hdr;
+
+	/* Find Session-Expires header */
+	se_hdr = (pjsip_sess_expires_hdr*) pjsip_msg_find_hdr_by_names(
+						msg, &STR_SE, 
+						&STR_SHORT_SE, NULL);
+	if (se_hdr == NULL) {
+	    /* Remote doesn't support/want Session Timers, check if local 
+	     * require or force to use Session Timers.
+	     */
+	    if (inv->options & PJSIP_INV_REQUIRE_TIMER) {
+		if (st_code)
+		    *st_code = PJSIP_SC_EXTENSION_REQUIRED;
+		pjsip_timer_end_session(inv);
+		return PJSIP_ERRNO_FROM_SIP_STATUS(
+					    PJSIP_SC_EXTENSION_REQUIRED);
+	    }
+
+	    if ((inv->options & PJSIP_INV_ALWAYS_USE_TIMER) == 0) {
+		/* Session Timers not forced */
+		pjsip_timer_end_session(inv);
+		return PJ_SUCCESS;
+	    }
+	}
+	    
+	/* Make sure Session Timers is initialized */
+	if (inv->timer == NULL)
+	    pjsip_timer_init_session(inv, NULL);
+
+	/* Session expiration period specified by remote is lower than our
+	 * Min-SE.
+	 */
+	if (se_hdr && 
+	    se_hdr->sess_expires < inv->timer->setting.min_se)
+	{
+	    /* See ticket #954, instead of returning non-PJ_SUCCESS (which
+	     * may cause disconnecting call/dialog), let's just accept the
+	     * SE and update our local SE, as long as it isn't less than 90s.
+	     */
+	    if (se_hdr->sess_expires >= ABS_MIN_SE) {
+		PJ_LOG(3, (inv->pool->obj_name, 
+			   "Peer responds with bad Session-Expires, %ds, "
+			   "which is less than Min-SE specified in request, "
+			   "%ds. Well, let's just accept and use it.",
+			   se_hdr->sess_expires, inv->timer->setting.min_se));
+
+		inv->timer->setting.sess_expires = se_hdr->sess_expires;
+		inv->timer->setting.min_se = se_hdr->sess_expires;
+	    }
+
+	    //if (st_code)
+	    //	*st_code = PJSIP_SC_SESSION_TIMER_TOO_SMALL;
+	    //pjsip_timer_end_session(inv);
+	    //return PJSIP_ERRNO_FROM_SIP_STATUS(
+	    //				    PJSIP_SC_SESSION_TIMER_TOO_SMALL);
+	}
+
+	/* Update SE. Session-Expires in response cannot be lower than Min-SE.
+	 * Session-Expires in response can only be equal or lower than in 
+	 * request.
+	 */
+	if (se_hdr && 
+	    se_hdr->sess_expires <= inv->timer->setting.sess_expires &&
+	    se_hdr->sess_expires >= inv->timer->setting.min_se)
+	{
+	    /* Good SE from remote, update local SE */
+	    inv->timer->setting.sess_expires = se_hdr->sess_expires;
+	}
+
+	/* Set the refresher */
+	if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAC) == 0)
+	    inv->timer->refresher = TR_UAC;
+	else if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAS) == 0)
+	    inv->timer->refresher = TR_UAS;
+	else
+	    /* UAS should set the refresher, however, there is a case that
+	     * UAS doesn't support/want Session Timers but the UAC insists
+	     * to use Session Timers.
+	     */
+	    inv->timer->refresher = TR_UAC;
+
+	/* Remember our role in this transaction */
+	inv->timer->role = PJSIP_ROLE_UAC;
+
+	/* Finally, set active flag and start the Session Timers */
+	inv->timer->active = PJ_TRUE;
+	start_timer(inv);
+
+    } else if (pjsip_method_cmp(&rdata->msg_info.cseq->method,
+				&pjsip_update_method) == 0 &&
+	       msg->line.status.code >= 400 && msg->line.status.code < 600)
+    {
+	/* This is to handle error response to previous UPDATE that was
+	 * sent without SDP. In this case, retry sending UPDATE but
+	 * with SDP this time.
+	 * Note: the additional expressions are to check that the
+	 *       UPDATE was really the one sent by us, not by other
+	 *       call components (e.g. to change codec)
+	 */
+	if (inv->timer->timer.id == 0 && inv->timer->use_update &&
+	    inv->timer->with_sdp == PJ_FALSE)
+	{
+	    inv->timer->with_sdp = PJ_TRUE;
+	    timer_cb(NULL, &inv->timer->timer);
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Handle incoming INVITE or UPDATE request.
+ */
+PJ_DEF(pj_status_t) pjsip_timer_process_req(pjsip_inv_session *inv,
+					    const pjsip_rx_data *rdata,
+					    pjsip_status_code *st_code)
+{
+    pjsip_min_se_hdr *min_se_hdr;
+    pjsip_sess_expires_hdr *se_hdr;
+    const pjsip_msg *msg;
+    unsigned min_se;
+
+    PJ_ASSERT_ON_FAIL(inv && rdata,
+	{if(st_code)*st_code=PJSIP_SC_INTERNAL_SERVER_ERROR;return PJ_EINVAL;});
+
+    /* Check if Session Timers is supported */
+    if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0)
+	return PJ_SUCCESS;
+
+    pj_assert(is_initialized);
+
+    msg = rdata->msg_info.msg;
+    pj_assert(msg->type == PJSIP_REQUEST_MSG);
+
+    /* Only process INVITE or UPDATE request */
+    if (msg->line.req.method.id != PJSIP_INVITE_METHOD &&
+	pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_update_method))
+    {
+	return PJ_SUCCESS;
+    }
+
+    /* Find Session-Expires header */
+    se_hdr = (pjsip_sess_expires_hdr*) pjsip_msg_find_hdr_by_names(
+					    msg, &STR_SE, &STR_SHORT_SE, NULL);
+    if (se_hdr == NULL) {
+	/* Remote doesn't support/want Session Timers, check if local 
+	 * require or force to use Session Timers. Note that Supported and 
+	 * Require headers negotiation should have been verified by invite 
+	 * session.
+	 */
+	if ((inv->options & 
+	    (PJSIP_INV_REQUIRE_TIMER | PJSIP_INV_ALWAYS_USE_TIMER)) == 0)
+	{
+	    /* Session Timers not forced/required */
+	    pjsip_timer_end_session(inv);
+	    return PJ_SUCCESS;
+	}
+    }
+
+    /* Make sure Session Timers is initialized */
+    if (inv->timer == NULL)
+	pjsip_timer_init_session(inv, NULL);
+
+    /* Find Min-SE header */
+    min_se_hdr = (pjsip_min_se_hdr*) pjsip_msg_find_hdr_by_name(msg, 
+							    &STR_MIN_SE, NULL);
+    /* Update Min-SE */
+    min_se = inv->timer->setting.min_se;
+    if (min_se_hdr)
+	min_se = PJ_MAX(min_se_hdr->min_se, min_se);
+
+    /* Validate SE. Session-Expires cannot be lower than Min-SE 
+     * (or 90 seconds if Min-SE is not set).
+     */
+    if (se_hdr && se_hdr->sess_expires < min_se) {
+	if (st_code)
+	    *st_code = PJSIP_SC_SESSION_TIMER_TOO_SMALL;
+	return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_SESSION_TIMER_TOO_SMALL);
+    }
+
+    /* Update SE. Note that there is a case that SE is not available in the
+     * request (which means remote doesn't want/support it), but local insists
+     * to use Session Timers.
+     */
+    if (se_hdr) {
+	/* Update SE as specified by peer. */
+	inv->timer->setting.sess_expires = se_hdr->sess_expires;
+    } else if (inv->timer->setting.sess_expires < min_se) {
+	/* There is no SE in the request (remote support Session Timers but
+	 * doesn't want to use it, it just specify Min-SE) and local SE is 
+	 * lower than Min-SE specified by remote.
+	 */
+	inv->timer->setting.sess_expires = min_se;
+    }
+
+    /* Set the refresher */
+    if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAC) == 0)
+	inv->timer->refresher = TR_UAC;
+    else if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAS) == 0)
+	inv->timer->refresher = TR_UAS;
+    else {
+	/* If refresher role (i.e: ours or peer) has been set/negotiated, 
+	 * better to keep it.
+	 */
+	if (inv->timer->refresher != TR_UNKNOWN) {
+	    pj_bool_t as_refresher;
+
+	    /* Check our refresher role */
+	    as_refresher = 
+		(inv->timer->refresher==TR_UAC && inv->timer->role==PJSIP_ROLE_UAC) ||
+		(inv->timer->refresher==TR_UAS && inv->timer->role==PJSIP_ROLE_UAS);
+
+	    /* Update refresher role */
+	    inv->timer->refresher = as_refresher? TR_UAS : TR_UAC;
+	} else {
+	    /* If UAC support timer (currently check the existance of 
+	     * Session-Expires header in the request), set UAC as refresher.
+	     */
+	    inv->timer->refresher = se_hdr? TR_UAC : TR_UAS;
+	}
+    }
+
+    /* Remember our role in this transaction */
+    inv->timer->role = PJSIP_ROLE_UAS;
+
+    /* Set active flag */
+    inv->timer->active = PJ_TRUE;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Handle outgoing response with status code 2xx & 422.
+ */
+PJ_DEF(pj_status_t) pjsip_timer_update_resp(pjsip_inv_session *inv,
+					    pjsip_tx_data *tdata)
+{
+    pjsip_msg *msg;
+
+    /* Check if Session Timers is supported */
+    if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0)
+	return PJ_SUCCESS;
+
+    pj_assert(is_initialized);
+    PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL);
+
+    msg = tdata->msg;
+
+    if (msg->line.status.code/100 == 2)
+    {
+	if (inv->timer && inv->timer->active) {
+	    /* Add Session-Expires header and start the timer */
+	    add_timer_headers(inv, tdata, PJ_TRUE, PJ_FALSE);
+
+	    /* Add 'timer' to Require header (see ticket #1560). */
+	    if (inv->timer->refresher == TR_UAC) {
+		pjsip_require_hdr *req_hdr;
+		pj_bool_t req_hdr_has_timer = PJ_FALSE;
+
+		req_hdr = (pjsip_require_hdr*)
+			   pjsip_msg_find_hdr(tdata->msg, PJSIP_H_REQUIRE,
+					      NULL);
+		if (req_hdr == NULL) {
+		    req_hdr = pjsip_require_hdr_create(tdata->pool);
+		    PJ_ASSERT_RETURN(req_hdr, PJ_ENOMEM);
+		    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr);
+		} else {
+		    unsigned i;
+		    for (i = 0; i < req_hdr->count; ++i) {
+			if (pj_stricmp(&req_hdr->values[i], &STR_TIMER)) {
+			    req_hdr_has_timer = PJ_TRUE;
+			    break;
+			}
+		    }
+		}
+		if (!req_hdr_has_timer)
+		    req_hdr->values[req_hdr->count++] = STR_TIMER;
+	    }
+	    
+	    /* Finally, start timer. */
+	    start_timer(inv);
+	}
+    } 
+    else if (msg->line.status.code == PJSIP_SC_SESSION_TIMER_TOO_SMALL)
+    {
+	/* Add Min-SE header */
+	add_timer_headers(inv, tdata, PJ_FALSE, PJ_TRUE);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * End the Session Timers.
+ */
+PJ_DEF(pj_status_t) pjsip_timer_end_session(pjsip_inv_session *inv)
+{
+    PJ_ASSERT_RETURN(inv, PJ_EINVAL);
+
+    if (inv->timer) {
+	/* Reset active flag */
+	inv->timer->active = PJ_FALSE;
+
+	/* Stop Session Timers */
+	stop_timer(inv);
+    }
+
+    return PJ_SUCCESS;
+}
diff --git a/jni/pjproject-android/.svn/pristine/8f/8fa90b8ed8cb151229289b8af523ea7286c02d29.svn-base b/jni/pjproject-android/.svn/pristine/8f/8fa90b8ed8cb151229289b8af523ea7286c02d29.svn-base
new file mode 100644
index 0000000..cab4e53
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/8f/8fa90b8ed8cb151229289b8af523ea7286c02d29.svn-base
@@ -0,0 +1,118 @@
+<?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             -->
+<!--                                                                    -->
+<!--                 Sipp default 'uas' scenario.                       -->
+<!--                                                                    -->
+
+<scenario name="Sending re-INVITE and ACK (with same branch) without SDP (#1045)">
+  <!-- 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 request="INVITE" crlf="true">
+    <action>
+	<ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>
+	<ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>
+        <assign assign_to="4" variable="5" />
+    </action>
+  </recv>
+
+  <send retrans="500">
+    <![CDATA[
+
+      SIP/2.0 200 OK
+      [last_Via:]
+      [last_From:]
+      [last_To:]
+      [last_Call-ID:]
+      [last_CSeq:]
+      Contact: sip:sipp@[local_ip]:[local_port]
+      Content-Type: application/sdp
+      Content-Length: [len]
+
+      v=0
+      o=- 3441953879 3441953879 IN IP4 192.168.0.15
+      s=pjmedia
+      c=IN IP4 192.168.0.15
+      t=0 0
+      m=audio 4004 RTP/AVP 0
+
+    ]]>
+  </send>
+
+  <recv request="ACK" crlf="true">
+  </recv>
+
+  <pause milliseconds="2000"/>
+
+  <send retrans="500">
+    <![CDATA[
+
+      INVITE sip:[$5] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=z9hG4bK-same-branch
+      From: sipp  <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+      To[$3]
+      Call-ID: [call_id]
+      Cseq: 1 INVITE
+      Contact: sip:sipp@[local_ip]:[local_port]
+      Max-Forwards: 70
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+  <recv response="100"
+        optional="true">
+  </recv>
+
+  <recv response="180" optional="true">
+  </recv>
+
+  <recv response="200" rtd="true">
+  </recv>
+
+  <send>
+    <![CDATA[
+
+      ACK sip:[$5] SIP/2.0
+      Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=z9hG4bK-same-branch
+      From: sipp  <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+      To[$3]
+      Call-ID: [call_id]
+      Cseq: 1 ACK
+      Contact: sip:sipp@[local_ip]:[local_port]
+      Max-Forwards: 70
+      Content-Length: 0
+
+    ]]>
+  </send>
+
+
+  <!-- Keep the call open for a while in case the 200 is lost to be     -->
+  <!-- able to retransmit it if we receive the BYE again.               -->
+  <pause milliseconds="4000"/>
+
+
+  <!-- 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/8f/8faaa0faa6cd692b602eb74340c754c739d5eaa3.svn-base b/jni/pjproject-android/.svn/pristine/8f/8faaa0faa6cd692b602eb74340c754c739d5eaa3.svn-base
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/8f/8faaa0faa6cd692b602eb74340c754c739d5eaa3.svn-base
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/jni/pjproject-android/.svn/pristine/8f/8fc576e59813377f43f2c57ff0ef905faf8dcdb9.svn-base b/jni/pjproject-android/.svn/pristine/8f/8fc576e59813377f43f2c57ff0ef905faf8dcdb9.svn-base
new file mode 100644
index 0000000..9b03599
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/8f/8fc576e59813377f43f2c57ff0ef905faf8dcdb9.svn-base
@@ -0,0 +1,366 @@
+/* $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 <pjlib-util/stun_simple.h>
+#include <pjlib-util/errno.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/sock_select.h>
+#include <pj/string.h>
+
+
+enum { MAX_REQUEST = 4 };
+static int stun_timer[] = {500, 500, 500, 500 };
+#define STUN_MAGIC 0x2112A442
+
+#define THIS_FILE	"stun_client.c"
+#define LOG_ADDR(addr)	pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port)
+
+#define TRACE_(x)	PJ_LOG(6,x)
+
+PJ_DEF(pj_status_t) pjstun_get_mapped_addr( pj_pool_factory *pf,
+					    int sock_cnt, pj_sock_t sock[],
+					    const pj_str_t *srv1, int port1,
+					    const pj_str_t *srv2, int port2,
+					    pj_sockaddr_in mapped_addr[])
+{
+    pjstun_setting opt;
+
+    pj_bzero(&opt, sizeof(opt));
+    opt.use_stun2 = PJ_FALSE;
+    opt.srv1 = *srv1;
+    opt.port1 = port1;
+    opt.srv2 = *srv2;
+    opt.port2 = port2;
+
+    return pjstun_get_mapped_addr2(pf, &opt, sock_cnt, sock, mapped_addr);
+}
+
+PJ_DEF(pj_status_t) pjstun_get_mapped_addr2(pj_pool_factory *pf,
+					    const pjstun_setting *opt,
+					    int sock_cnt,
+					    pj_sock_t sock[],
+					    pj_sockaddr_in mapped_addr[])
+{
+    unsigned srv_cnt;
+    const pj_str_t *srv1, *srv2;
+    int port1, port2;
+    pj_sockaddr_in srv_addr[2];
+    int i, send_cnt = 0, nfds;
+    pj_pool_t *pool;
+    struct query_rec {
+	struct {
+	    pj_uint32_t	mapped_addr;
+	    pj_uint32_t	mapped_port;
+	} srv[2];
+    } *rec;
+    void       *out_msg;
+    pj_size_t	out_msg_len;
+    int wait_resp = 0;
+    pj_status_t status;
+
+    PJ_CHECK_STACK();
+
+    srv1 = &opt->srv1;
+    port1 = opt->port1;
+    srv2 = &opt->srv1;
+    port2 = opt->port2;
+
+    TRACE_((THIS_FILE, "Entering pjstun_get_mapped_addr()"));
+
+    /* Create pool. */
+    pool = pj_pool_create(pf, "stun%p", 400, 400, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+
+    /* Allocate client records */
+    rec = (struct query_rec*) pj_pool_calloc(pool, sock_cnt, sizeof(*rec));
+    if (!rec) {
+	status = PJ_ENOMEM;
+	goto on_error;
+    }
+
+    TRACE_((THIS_FILE, "  Memory allocated."));
+
+    /* Create the outgoing BIND REQUEST message template */
+    status = pjstun_create_bind_req( pool, &out_msg, &out_msg_len, 
+				      pj_rand(), pj_rand());
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Insert magic cookie (specified in RFC 5389) when requested to. */
+    if (opt->use_stun2) {
+	pjstun_msg_hdr *hdr = (pjstun_msg_hdr*)out_msg;
+	hdr->tsx[0] = pj_htonl(STUN_MAGIC);
+    }
+
+    TRACE_((THIS_FILE, "  Binding request created."));
+
+    /* Resolve servers. */
+    status = pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    srv_cnt = 1;
+
+    if (srv2 && port2) {
+	status = pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+
+	if (srv_addr[1].sin_addr.s_addr != srv_addr[0].sin_addr.s_addr &&
+	    srv_addr[1].sin_port != srv_addr[0].sin_port)
+	{
+	    srv_cnt++;
+	}
+    }
+
+    TRACE_((THIS_FILE, "  Server initialized, using %d server(s)", srv_cnt));
+
+    /* Init mapped addresses to zero */
+    pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in));
+
+    /* We need these many responses */
+    wait_resp = sock_cnt * srv_cnt;
+
+    TRACE_((THIS_FILE, "  Done initialization."));
+
+#if defined(PJ_SELECT_NEEDS_NFDS) && PJ_SELECT_NEEDS_NFDS!=0
+    nfds = -1;
+    for (i=0; i<sock_cnt; ++i) {
+	if (sock[i] > nfds) {
+	    nfds = sock[i];
+	}
+    }
+#else
+    nfds = PJ_IOQUEUE_MAX_HANDLES-1;
+#endif
+
+    /* Main retransmission loop. */
+    for (send_cnt=0; send_cnt<MAX_REQUEST; ++send_cnt) {
+	pj_time_val next_tx, now;
+	pj_fd_set_t r;
+	int select_rc;
+
+	PJ_FD_ZERO(&r);
+
+	/* Send messages to servers that has not given us response. */
+	for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
+	    unsigned j;
+	    for (j=0; j<srv_cnt && status==PJ_SUCCESS; ++j) {
+		pjstun_msg_hdr *msg_hdr = (pjstun_msg_hdr*) out_msg;
+                pj_ssize_t sent_len;
+
+		if (rec[i].srv[j].mapped_port != 0)
+		    continue;
+
+		/* Modify message so that we can distinguish response. */
+		msg_hdr->tsx[2] = pj_htonl(i);
+		msg_hdr->tsx[3] = pj_htonl(j);
+
+		/* Send! */
+                sent_len = out_msg_len;
+		status = pj_sock_sendto(sock[i], out_msg, &sent_len, 0,
+					(pj_sockaddr_t*)&srv_addr[j],
+					sizeof(pj_sockaddr_in));
+	    }
+	}
+
+	/* All requests sent.
+	 * The loop below will wait for responses until all responses have
+	 * been received (i.e. wait_resp==0) or timeout occurs, which then
+	 * we'll go to the next retransmission iteration.
+	 */
+	TRACE_((THIS_FILE, "  Request(s) sent, counter=%d", send_cnt));
+
+	/* Calculate time of next retransmission. */
+	pj_gettickcount(&next_tx);
+	next_tx.sec += (stun_timer[send_cnt]/1000);
+	next_tx.msec += (stun_timer[send_cnt]%1000);
+	pj_time_val_normalize(&next_tx);
+
+	for (pj_gettickcount(&now), select_rc=1;
+	     status==PJ_SUCCESS && select_rc>=1 && wait_resp>0 
+	       && PJ_TIME_VAL_LT(now, next_tx); 
+	     pj_gettickcount(&now))
+	{
+	    pj_time_val timeout;
+
+	    timeout = next_tx;
+	    PJ_TIME_VAL_SUB(timeout, now);
+
+	    for (i=0; i<sock_cnt; ++i) {
+		PJ_FD_SET(sock[i], &r);
+	    }
+
+	    select_rc = pj_sock_select(nfds+1, &r, NULL, NULL, &timeout);
+	    TRACE_((THIS_FILE, "  select() rc=%d", select_rc));
+	    if (select_rc < 1)
+		continue;
+
+	    for (i=0; i<sock_cnt; ++i) {
+		int sock_idx, srv_idx;
+                pj_ssize_t len;
+		pjstun_msg msg;
+		pj_sockaddr_in addr;
+		int addrlen = sizeof(addr);
+		pjstun_mapped_addr_attr *attr;
+		char recv_buf[128];
+
+		if (!PJ_FD_ISSET(sock[i], &r))
+		    continue;
+
+                len = sizeof(recv_buf);
+		status = pj_sock_recvfrom( sock[i], recv_buf, 
+				           &len, 0,
+				           (pj_sockaddr_t*)&addr,
+					   &addrlen);
+
+		if (status != PJ_SUCCESS) {
+		    char errmsg[PJ_ERR_MSG_SIZE];
+
+		    PJ_LOG(4,(THIS_FILE, "recvfrom() error ignored: %s",
+			      pj_strerror(status, errmsg,sizeof(errmsg)).ptr));
+
+		    /* Ignore non-PJ_SUCCESS status.
+		     * It possible that other SIP entity is currently 
+		     * sending SIP request to us, and because SIP message
+		     * is larger than STUN, we could get EMSGSIZE when
+		     * we call recvfrom().
+		     */
+		    status = PJ_SUCCESS;
+		    continue;
+		}
+
+		status = pjstun_parse_msg(recv_buf, len, &msg);
+		if (status != PJ_SUCCESS) {
+		    char errmsg[PJ_ERR_MSG_SIZE];
+
+		    PJ_LOG(4,(THIS_FILE, "STUN parsing error ignored: %s",
+			      pj_strerror(status, errmsg,sizeof(errmsg)).ptr));
+
+		    /* Also ignore non-successful parsing. This may not
+		     * be STUN response at all. See the comment above.
+		     */
+		    status = PJ_SUCCESS;
+		    continue;
+		}
+
+		sock_idx = pj_ntohl(msg.hdr->tsx[2]);
+		srv_idx = pj_ntohl(msg.hdr->tsx[3]);
+
+		if (sock_idx<0 || sock_idx>=sock_cnt || sock_idx!=i ||
+			srv_idx<0 || srv_idx>=2)
+		{
+		    status = PJLIB_UTIL_ESTUNININDEX;
+		    continue;
+		}
+
+		if (pj_ntohs(msg.hdr->type) != PJSTUN_BINDING_RESPONSE) {
+		    status = PJLIB_UTIL_ESTUNNOBINDRES;
+		    continue;
+		}
+
+		if (rec[sock_idx].srv[srv_idx].mapped_port != 0) {
+		    /* Already got response */
+		    continue;
+		}
+
+		/* From this part, we consider the packet as a valid STUN
+		 * response for our request.
+		 */
+		--wait_resp;
+
+		if (pjstun_msg_find_attr(&msg, PJSTUN_ATTR_ERROR_CODE) != NULL) {
+		    status = PJLIB_UTIL_ESTUNRECVERRATTR;
+		    continue;
+		}
+
+		attr = (pjstun_mapped_addr_attr*) 
+		       pjstun_msg_find_attr(&msg, PJSTUN_ATTR_MAPPED_ADDR);
+		if (!attr) {
+		    attr = (pjstun_mapped_addr_attr*) 
+			   pjstun_msg_find_attr(&msg, PJSTUN_ATTR_XOR_MAPPED_ADDR);
+		    if (!attr || attr->family != 1) {
+			status = PJLIB_UTIL_ESTUNNOMAP;
+			continue;
+		    }
+		}
+
+		rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr;
+		rec[sock_idx].srv[srv_idx].mapped_port = attr->port;
+		if (pj_ntohs(attr->hdr.type) == PJSTUN_ATTR_XOR_MAPPED_ADDR) {
+		    rec[sock_idx].srv[srv_idx].mapped_addr ^= pj_htonl(STUN_MAGIC);
+		    rec[sock_idx].srv[srv_idx].mapped_port ^= pj_htons(STUN_MAGIC >> 16);
+		}
+	    }
+	}
+
+	/* The best scenario is if all requests have been replied.
+	 * Then we don't need to go to the next retransmission iteration.
+	 */
+	if (wait_resp <= 0)
+	    break;
+    }
+
+    TRACE_((THIS_FILE, "  All responses received, calculating result.."));
+
+    for (i=0; i<sock_cnt && status==PJ_SUCCESS; ++i) {
+	if (srv_cnt == 1) {
+	    mapped_addr[i].sin_family = pj_AF_INET();
+	    mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
+	    mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
+
+	    if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
+		status = PJLIB_UTIL_ESTUNNOTRESPOND;
+		break;
+	    }
+	} else if (rec[i].srv[0].mapped_addr == rec[i].srv[1].mapped_addr &&
+	           rec[i].srv[0].mapped_port == rec[i].srv[1].mapped_port)
+	{
+	    mapped_addr[i].sin_family = pj_AF_INET();
+	    mapped_addr[i].sin_addr.s_addr = rec[i].srv[0].mapped_addr;
+	    mapped_addr[i].sin_port = (pj_uint16_t)rec[i].srv[0].mapped_port;
+
+	    if (rec[i].srv[0].mapped_addr == 0 || rec[i].srv[0].mapped_port == 0) {
+		status = PJLIB_UTIL_ESTUNNOTRESPOND;
+		break;
+	    }
+	} else {
+	    status = PJLIB_UTIL_ESTUNSYMMETRIC;
+	    break;
+	}
+    }
+
+    TRACE_((THIS_FILE, "  Pool usage=%d of %d", pj_pool_get_used_size(pool),
+	    pj_pool_get_capacity(pool)));
+
+    pj_pool_release(pool);
+
+    TRACE_((THIS_FILE, "  Done."));
+    return status;
+
+on_error:
+    if (pool) pj_pool_release(pool);
+    return status;
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/8f/8fee29ba3b1cacf8335d0972794c9b847f052ff6.svn-base b/jni/pjproject-android/.svn/pristine/8f/8fee29ba3b1cacf8335d0972794c9b847f052ff6.svn-base
new file mode 100644
index 0000000..674298d
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/8f/8fee29ba3b1cacf8335d0972794c9b847f052ff6.svn-base
@@ -0,0 +1,174 @@
+/* $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 "auth.h"
+
+#define REALM		"pjsip.org"
+//#define TURN_PORT	PJ_STUN_TURN_PORT
+#define TURN_PORT	34780
+#define LOG_LEVEL	4
+
+
+static pj_caching_pool g_cp;
+
+int err(const char *title, pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+    pj_strerror(status, errmsg, sizeof(errmsg));
+
+    printf("%s: %s\n", title, errmsg);
+    return 1;
+}
+
+static void dump_status(pj_turn_srv *srv)
+{
+    char addr[80];
+    pj_hash_iterator_t itbuf, *it;
+    pj_time_val now;
+    unsigned i;
+
+    for (i=0; i<srv->core.lis_cnt; ++i) {
+	pj_turn_listener *lis = srv->core.listener[i];
+	printf("Server address : %s\n", lis->info);
+    }
+
+    printf("Worker threads : %d\n", srv->core.thread_cnt);
+    printf("Total mem usage: %u.%03uMB\n", (unsigned)(g_cp.used_size / 1000000), 
+	   (unsigned)((g_cp.used_size % 1000000)/1000));
+    printf("UDP port range : %u %u %u (next/min/max)\n", srv->ports.next_udp,
+	   srv->ports.min_udp, srv->ports.max_udp);
+    printf("TCP port range : %u %u %u (next/min/max)\n", srv->ports.next_tcp,
+	   srv->ports.min_tcp, srv->ports.max_tcp);
+    printf("Clients #      : %u\n", pj_hash_count(srv->tables.alloc));
+
+    puts("");
+
+    if (pj_hash_count(srv->tables.alloc)==0) {
+	return;
+    }
+
+    puts("#    Client addr.          Alloc addr.            Username Lftm Expy #prm #chl");
+    puts("------------------------------------------------------------------------------");
+
+    pj_gettimeofday(&now);
+
+    it = pj_hash_first(srv->tables.alloc, &itbuf);
+    i=1;
+    while (it) {
+	pj_turn_allocation *alloc = (pj_turn_allocation*) 
+				    pj_hash_this(srv->tables.alloc, it);
+	printf("%-3d %-22s %-22s %-8.*s %-4d %-4ld %-4d %-4d\n",
+	       i,
+	       alloc->info,
+	       pj_sockaddr_print(&alloc->relay.hkey.addr, addr, sizeof(addr), 3),
+	       (int)alloc->cred.data.static_cred.username.slen,
+	       alloc->cred.data.static_cred.username.ptr,
+	       alloc->relay.lifetime,
+	       alloc->relay.expiry.sec - now.sec,
+	       pj_hash_count(alloc->peer_table), 
+	       pj_hash_count(alloc->ch_table));
+
+	it = pj_hash_next(srv->tables.alloc, it);
+	++i;
+    }
+}
+
+static void menu(void)
+{
+    puts("");
+    puts("Menu:");
+    puts(" d   Dump status");
+    puts(" q   Quit");
+    printf(">> ");
+}
+
+static void console_main(pj_turn_srv *srv)
+{
+    pj_bool_t quit = PJ_FALSE;
+
+    while (!quit) {
+	char line[10];
+	
+	menu();
+	    
+	if (fgets(line, sizeof(line), stdin) == NULL)
+	    break;
+
+	switch (line[0]) {
+	case 'd':
+	    dump_status(srv);
+	    break;
+	case 'q':
+	    quit = PJ_TRUE;
+	    break;
+	}
+    }
+}
+
+int main()
+{
+    pj_turn_srv *srv;
+    pj_turn_listener *listener;
+    pj_status_t status;
+
+    status = pj_init();
+    if (status != PJ_SUCCESS)
+	return err("pj_init() error", status);
+
+    pjlib_util_init();
+    pjnath_init();
+
+    pj_caching_pool_init(&g_cp, NULL, 0);
+
+    pj_turn_auth_init(REALM);
+
+    status = pj_turn_srv_create(&g_cp.factory, &srv);
+    if (status != PJ_SUCCESS)
+	return err("Error creating server", status);
+
+    status = pj_turn_listener_create_udp(srv, pj_AF_INET(), NULL, 
+					 TURN_PORT, 1, 0, &listener);
+    if (status != PJ_SUCCESS)
+	return err("Error creating UDP listener", status);
+
+#if PJ_HAS_TCP
+    status = pj_turn_listener_create_tcp(srv, pj_AF_INET(), NULL, 
+					 TURN_PORT, 1, 0, &listener);
+    if (status != PJ_SUCCESS)
+	return err("Error creating listener", status);
+#endif
+
+    status = pj_turn_srv_add_listener(srv, listener);
+    if (status != PJ_SUCCESS)
+	return err("Error adding listener", status);
+
+    puts("Server is running");
+
+    pj_log_set_level(LOG_LEVEL);
+
+    console_main(srv);
+
+    pj_turn_srv_destroy(srv);
+    pj_caching_pool_destroy(&g_cp);
+    pj_shutdown();
+
+    return 0;
+}
+