* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/f8/f81e20ded557e10a2f81596028cae770b3da65f1.svn-base b/jni/pjproject-android/.svn/pristine/f8/f81e20ded557e10a2f81596028cae770b3da65f1.svn-base
new file mode 100644
index 0000000..9695973
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f81e20ded557e10a2f81596028cae770b3da65f1.svn-base
@@ -0,0 +1,1497 @@
+/* $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 <pjnath/stun_session.h>
+#include <pjnath/errno.h>
+#include <pjlib.h>
+
+struct pj_stun_session
+{
+ pj_stun_config *cfg;
+ pj_pool_t *pool;
+ pj_grp_lock_t *grp_lock;
+ pj_stun_session_cb cb;
+ void *user_data;
+ pj_bool_t is_destroying;
+
+ pj_bool_t use_fingerprint;
+
+ pj_pool_t *rx_pool;
+
+#if PJ_LOG_MAX_LEVEL >= 5
+ char dump_buf[1000];
+#endif
+ unsigned log_flag;
+
+ pj_stun_auth_type auth_type;
+ pj_stun_auth_cred cred;
+ int auth_retry;
+ pj_str_t next_nonce;
+ pj_str_t server_realm;
+
+ pj_str_t srv_name;
+
+ pj_stun_tx_data pending_request_list;
+ pj_stun_tx_data cached_response_list;
+};
+
+#define SNAME(s_) ((s_)->pool->obj_name)
+#define THIS_FILE "stun_session.c"
+
+#if 1
+# define TRACE_(expr) PJ_LOG(5,expr)
+#else
+# define TRACE_(expr)
+#endif
+
+#define LOG_ERR_(sess,title,rc) PJ_PERROR(3,(sess->pool->obj_name,rc,title))
+
+#define TDATA_POOL_SIZE PJNATH_POOL_LEN_STUN_TDATA
+#define TDATA_POOL_INC PJNATH_POOL_INC_STUN_TDATA
+
+
+static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
+ pj_status_t status,
+ const pj_stun_msg *response,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len);
+static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx,
+ const void *stun_pkt,
+ pj_size_t pkt_size);
+static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx);
+static void stun_sess_on_destroy(void *comp);
+
+static pj_stun_tsx_cb tsx_cb =
+{
+ &stun_tsx_on_complete,
+ &stun_tsx_on_send_msg,
+ &stun_tsx_on_destroy
+};
+
+
+static pj_status_t tsx_add(pj_stun_session *sess,
+ pj_stun_tx_data *tdata)
+{
+ pj_list_push_front(&sess->pending_request_list, tdata);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t tsx_erase(pj_stun_session *sess,
+ pj_stun_tx_data *tdata)
+{
+ PJ_UNUSED_ARG(sess);
+ pj_list_erase(tdata);
+ return PJ_SUCCESS;
+}
+
+static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess,
+ const pj_stun_msg *msg)
+{
+ pj_stun_tx_data *tdata;
+
+ tdata = sess->pending_request_list.next;
+ while (tdata != &sess->pending_request_list) {
+ pj_assert(sizeof(tdata->msg_key)==sizeof(msg->hdr.tsx_id));
+ if (tdata->msg_magic == msg->hdr.magic &&
+ pj_memcmp(tdata->msg_key, msg->hdr.tsx_id,
+ sizeof(msg->hdr.tsx_id))==0)
+ {
+ return tdata;
+ }
+ tdata = tdata->next;
+ }
+
+ return NULL;
+}
+
+static pj_status_t create_tdata(pj_stun_session *sess,
+ pj_stun_tx_data **p_tdata)
+{
+ pj_pool_t *pool;
+ pj_stun_tx_data *tdata;
+
+ /* Create pool and initialize basic tdata attributes */
+ pool = pj_pool_create(sess->cfg->pf, "tdata%p",
+ TDATA_POOL_SIZE, TDATA_POOL_INC, NULL);
+ PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
+
+ tdata = PJ_POOL_ZALLOC_T(pool, pj_stun_tx_data);
+ tdata->pool = pool;
+ tdata->sess = sess;
+
+ pj_list_init(tdata);
+
+ *p_tdata = tdata;
+
+ return PJ_SUCCESS;
+}
+
+static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx)
+{
+ pj_stun_tx_data *tdata;
+
+ tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
+ pj_stun_client_tsx_stop(tsx);
+ if (tdata) {
+ tsx_erase(tdata->sess, tdata);
+ pj_pool_release(tdata->pool);
+ }
+
+ TRACE_((THIS_FILE, "STUN transaction %p destroyed", tsx));
+}
+
+static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force)
+{
+ TRACE_((THIS_FILE, "tdata %p destroy request, force=%d, tsx=%p", tdata,
+ force, tdata->client_tsx));
+
+ if (tdata->res_timer.id != PJ_FALSE) {
+ pj_timer_heap_cancel_if_active(tdata->sess->cfg->timer_heap,
+ &tdata->res_timer, PJ_FALSE);
+ pj_list_erase(tdata);
+ }
+
+ if (force) {
+ pj_list_erase(tdata);
+ if (tdata->client_tsx) {
+ pj_stun_client_tsx_stop(tdata->client_tsx);
+ pj_stun_client_tsx_set_data(tdata->client_tsx, NULL);
+ }
+ pj_pool_release(tdata->pool);
+
+ } else {
+ if (tdata->client_tsx) {
+ /* "Probably" this is to absorb retransmission */
+ pj_time_val delay = {0, 300};
+ pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay);
+
+ } else {
+ pj_pool_release(tdata->pool);
+ }
+ }
+}
+
+/*
+ * Destroy the transmit data.
+ */
+PJ_DEF(void) pj_stun_msg_destroy_tdata( pj_stun_session *sess,
+ pj_stun_tx_data *tdata)
+{
+ PJ_UNUSED_ARG(sess);
+ destroy_tdata(tdata, PJ_FALSE);
+}
+
+
+/* Timer callback to be called when it's time to destroy response cache */
+static void on_cache_timeout(pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pj_stun_tx_data *tdata;
+
+ PJ_UNUSED_ARG(timer_heap);
+
+ entry->id = PJ_FALSE;
+ tdata = (pj_stun_tx_data*) entry->user_data;
+
+ PJ_LOG(5,(SNAME(tdata->sess), "Response cache deleted"));
+
+ pj_list_erase(tdata);
+ destroy_tdata(tdata, PJ_FALSE);
+}
+
+static pj_status_t apply_msg_options(pj_stun_session *sess,
+ pj_pool_t *pool,
+ const pj_stun_req_cred_info *auth_info,
+ pj_stun_msg *msg)
+{
+ pj_status_t status = 0;
+ pj_str_t realm, username, nonce, auth_key;
+
+ /* If the agent is sending a request, it SHOULD add a SOFTWARE attribute
+ * to the request. The server SHOULD include a SOFTWARE attribute in all
+ * responses.
+ *
+ * If magic value is not PJ_STUN_MAGIC, only apply the attribute for
+ * responses.
+ */
+ if (sess->srv_name.slen &&
+ pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL &&
+ (PJ_STUN_IS_RESPONSE(msg->hdr.type) ||
+ (PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC)))
+ {
+ pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE,
+ &sess->srv_name);
+ }
+
+ if (pj_stun_auth_valid_for_msg(msg) && auth_info) {
+ realm = auth_info->realm;
+ username = auth_info->username;
+ nonce = auth_info->nonce;
+ auth_key = auth_info->auth_key;
+ } else {
+ realm.slen = username.slen = nonce.slen = auth_key.slen = 0;
+ }
+
+ /* Create and add USERNAME attribute if needed */
+ if (username.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) {
+ status = pj_stun_msg_add_string_attr(pool, msg,
+ PJ_STUN_ATTR_USERNAME,
+ &username);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+ }
+
+ /* Add REALM only when long term credential is used */
+ if (realm.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) {
+ status = pj_stun_msg_add_string_attr(pool, msg,
+ PJ_STUN_ATTR_REALM,
+ &realm);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+ }
+
+ /* Add NONCE when desired */
+ if (nonce.slen &&
+ (PJ_STUN_IS_REQUEST(msg->hdr.type) ||
+ PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)))
+ {
+ status = pj_stun_msg_add_string_attr(pool, msg,
+ PJ_STUN_ATTR_NONCE,
+ &nonce);
+ }
+
+ /* Add MESSAGE-INTEGRITY attribute */
+ if (username.slen && auth_key.slen) {
+ status = pj_stun_msg_add_msgint_attr(pool, msg);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+ }
+
+
+ /* Add FINGERPRINT attribute if necessary */
+ if (sess->use_fingerprint) {
+ status = pj_stun_msg_add_uint_attr(pool, msg,
+ PJ_STUN_ATTR_FINGERPRINT, 0);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t handle_auth_challenge(pj_stun_session *sess,
+ const pj_stun_tx_data *request,
+ const pj_stun_msg *response,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len,
+ pj_bool_t *notify_user)
+{
+ const pj_stun_errcode_attr *ea;
+
+ *notify_user = PJ_TRUE;
+
+ if (response==NULL)
+ return PJ_SUCCESS;
+
+ if (sess->auth_type != PJ_STUN_AUTH_LONG_TERM)
+ return PJ_SUCCESS;
+
+ if (!PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) {
+ sess->auth_retry = 0;
+ return PJ_SUCCESS;
+ }
+
+ ea = (const pj_stun_errcode_attr*)
+ pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0);
+ if (!ea) {
+ PJ_LOG(4,(SNAME(sess), "Invalid error response: no ERROR-CODE"
+ " attribute"));
+ *notify_user = PJ_FALSE;
+ return PJNATH_EINSTUNMSG;
+ }
+
+ if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED ||
+ ea->err_code == PJ_STUN_SC_STALE_NONCE)
+ {
+ const pj_stun_nonce_attr *anonce;
+ const pj_stun_realm_attr *arealm;
+ pj_stun_tx_data *tdata;
+ unsigned i;
+ pj_status_t status;
+
+ anonce = (const pj_stun_nonce_attr*)
+ pj_stun_msg_find_attr(response, PJ_STUN_ATTR_NONCE, 0);
+ if (!anonce) {
+ PJ_LOG(4,(SNAME(sess), "Invalid response: missing NONCE"));
+ *notify_user = PJ_FALSE;
+ return PJNATH_EINSTUNMSG;
+ }
+
+ /* Bail out if we've supplied the correct nonce */
+ if (pj_strcmp(&anonce->value, &sess->next_nonce)==0) {
+ return PJ_SUCCESS;
+ }
+
+ /* Bail out if we've tried too many */
+ if (++sess->auth_retry > 3) {
+ PJ_LOG(4,(SNAME(sess), "Error: authentication failed (too "
+ "many retries)"));
+ return PJ_STATUS_FROM_STUN_CODE(401);
+ }
+
+ /* Save next_nonce */
+ pj_strdup(sess->pool, &sess->next_nonce, &anonce->value);
+
+ /* Copy the realm from the response */
+ arealm = (pj_stun_realm_attr*)
+ pj_stun_msg_find_attr(response, PJ_STUN_ATTR_REALM, 0);
+ if (arealm) {
+ pj_strdup(sess->pool, &sess->server_realm, &arealm->value);
+ while (sess->server_realm.slen &&
+ !sess->server_realm.ptr[sess->server_realm.slen-1])
+ {
+ --sess->server_realm.slen;
+ }
+ }
+
+ /* Create new request */
+ status = pj_stun_session_create_req(sess, request->msg->hdr.type,
+ request->msg->hdr.magic,
+ NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Duplicate all the attributes in the old request, except
+ * USERNAME, REALM, M-I, and NONCE, which will be filled in
+ * later.
+ */
+ for (i=0; i<request->msg->attr_count; ++i) {
+ const pj_stun_attr_hdr *asrc = request->msg->attr[i];
+
+ if (asrc->type == PJ_STUN_ATTR_USERNAME ||
+ asrc->type == PJ_STUN_ATTR_REALM ||
+ asrc->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY ||
+ asrc->type == PJ_STUN_ATTR_NONCE)
+ {
+ continue;
+ }
+
+ tdata->msg->attr[tdata->msg->attr_count++] =
+ pj_stun_attr_clone(tdata->pool, asrc);
+ }
+
+ /* Will retry the request with authentication, no need to
+ * notify user.
+ */
+ *notify_user = PJ_FALSE;
+
+ PJ_LOG(4,(SNAME(sess), "Retrying request with new authentication"));
+
+ /* Retry the request */
+ status = pj_stun_session_send_msg(sess, request->token, PJ_TRUE,
+ request->retransmit, src_addr,
+ src_addr_len, tdata);
+
+ } else {
+ sess->auth_retry = 0;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
+ pj_status_t status,
+ const pj_stun_msg *response,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ pj_stun_session *sess;
+ pj_bool_t notify_user = PJ_TRUE;
+ pj_stun_tx_data *tdata;
+
+ tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
+ sess = tdata->sess;
+
+ /* Lock the session and prevent user from destroying us in the callback */
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ pj_grp_lock_release(sess->grp_lock);
+ return;
+ }
+
+ /* Handle authentication challenge */
+ handle_auth_challenge(sess, tdata, response, src_addr,
+ src_addr_len, ¬ify_user);
+
+ if (notify_user && sess->cb.on_request_complete) {
+ (*sess->cb.on_request_complete)(sess, status, tdata->token, tdata,
+ response, src_addr, src_addr_len);
+ }
+
+ /* Destroy the transmit data. This will remove the transaction
+ * from the pending list too.
+ */
+ if (status == PJNATH_ESTUNTIMEDOUT)
+ destroy_tdata(tdata, PJ_TRUE);
+ else
+ destroy_tdata(tdata, PJ_FALSE);
+ tdata = NULL;
+
+ pj_grp_lock_release(sess->grp_lock);
+}
+
+static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx,
+ const void *stun_pkt,
+ pj_size_t pkt_size)
+{
+ pj_stun_tx_data *tdata;
+ pj_stun_session *sess;
+ pj_status_t status;
+
+ tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
+ sess = tdata->sess;
+
+ /* Lock the session and prevent user from destroying us in the callback */
+ pj_grp_lock_acquire(sess->grp_lock);
+
+ if (sess->is_destroying) {
+ /* Stray timer */
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt,
+ pkt_size, tdata->dst_addr,
+ tdata->addr_len);
+ if (pj_grp_lock_release(sess->grp_lock))
+ return PJ_EGONE;
+
+ return status;
+}
+
+/* **************************************************************************/
+
+PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg,
+ const char *name,
+ const pj_stun_session_cb *cb,
+ pj_bool_t fingerprint,
+ pj_grp_lock_t *grp_lock,
+ pj_stun_session **p_sess)
+{
+ pj_pool_t *pool;
+ pj_stun_session *sess;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(cfg && cb && p_sess, PJ_EINVAL);
+
+ if (name==NULL)
+ name = "stuse%p";
+
+ pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_STUN_SESS,
+ PJNATH_POOL_INC_STUN_SESS, NULL);
+ PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
+
+ sess = PJ_POOL_ZALLOC_T(pool, pj_stun_session);
+ sess->cfg = cfg;
+ sess->pool = pool;
+ pj_memcpy(&sess->cb, cb, sizeof(*cb));
+ sess->use_fingerprint = fingerprint;
+ sess->log_flag = 0xFFFF;
+
+ if (grp_lock) {
+ sess->grp_lock = grp_lock;
+ } else {
+ status = pj_grp_lock_create(pool, NULL, &sess->grp_lock);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+ }
+
+ pj_grp_lock_add_ref(sess->grp_lock);
+ pj_grp_lock_add_handler(sess->grp_lock, pool, sess,
+ &stun_sess_on_destroy);
+
+ pj_stun_session_set_software_name(sess, &cfg->software_name);
+
+ sess->rx_pool = pj_pool_create(sess->cfg->pf, name,
+ PJNATH_POOL_LEN_STUN_TDATA,
+ PJNATH_POOL_INC_STUN_TDATA, NULL);
+
+ pj_list_init(&sess->pending_request_list);
+ pj_list_init(&sess->cached_response_list);
+
+ *p_sess = sess;
+
+ return PJ_SUCCESS;
+}
+
+static void stun_sess_on_destroy(void *comp)
+{
+ pj_stun_session *sess = (pj_stun_session*)comp;
+
+ while (!pj_list_empty(&sess->pending_request_list)) {
+ pj_stun_tx_data *tdata = sess->pending_request_list.next;
+ destroy_tdata(tdata, PJ_TRUE);
+ }
+
+ while (!pj_list_empty(&sess->cached_response_list)) {
+ pj_stun_tx_data *tdata = sess->cached_response_list.next;
+ destroy_tdata(tdata, PJ_TRUE);
+ }
+
+ if (sess->rx_pool) {
+ pj_pool_release(sess->rx_pool);
+ sess->rx_pool = NULL;
+ }
+
+ pj_pool_release(sess->pool);
+
+ TRACE_((THIS_FILE, "STUN session %p destroyed", sess));
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess)
+{
+ pj_stun_tx_data *tdata;
+
+ PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+ TRACE_((SNAME(sess), "STUN session %p destroy request, ref_cnt=%d",
+ sess, pj_grp_lock_get_ref(sess->grp_lock)));
+
+ pj_grp_lock_acquire(sess->grp_lock);
+
+ if (sess->is_destroying) {
+ /* Prevent from decrementing the ref counter more than once */
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ sess->is_destroying = PJ_TRUE;
+
+ /* We need to stop transactions and cached response because they are
+ * holding the group lock's reference counter while retransmitting.
+ */
+ tdata = sess->pending_request_list.next;
+ while (tdata != &sess->pending_request_list) {
+ if (tdata->client_tsx)
+ pj_stun_client_tsx_stop(tdata->client_tsx);
+ tdata = tdata->next;
+ }
+
+ tdata = sess->cached_response_list.next;
+ while (tdata != &sess->cached_response_list) {
+ pj_timer_heap_cancel_if_active(tdata->sess->cfg->timer_heap,
+ &tdata->res_timer, PJ_FALSE);
+ tdata = tdata->next;
+ }
+
+ pj_grp_lock_dec_ref(sess->grp_lock);
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_stun_session_set_user_data( pj_stun_session *sess,
+ void *user_data)
+{
+ PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+ pj_grp_lock_acquire(sess->grp_lock);
+ sess->user_data = user_data;
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess)
+{
+ PJ_ASSERT_RETURN(sess, NULL);
+ return sess->user_data;
+}
+
+PJ_DEF(pj_grp_lock_t *) pj_stun_session_get_grp_lock(pj_stun_session *sess)
+{
+ PJ_ASSERT_RETURN(sess, NULL);
+ return sess->grp_lock;
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess,
+ const pj_str_t *sw)
+{
+ PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sw && sw->slen)
+ pj_strdup(sess->pool, &sess->srv_name, sw);
+ else
+ sess->srv_name.slen = 0;
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
+ pj_stun_auth_type auth_type,
+ const pj_stun_auth_cred *cred)
+{
+ PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+ pj_grp_lock_acquire(sess->grp_lock);
+ sess->auth_type = auth_type;
+ if (cred) {
+ pj_stun_auth_cred_dup(sess->pool, &sess->cred, cred);
+ } else {
+ sess->auth_type = PJ_STUN_AUTH_NONE;
+ pj_bzero(&sess->cred, sizeof(sess->cred));
+ }
+ pj_grp_lock_release(sess->grp_lock);
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_stun_session_set_log( pj_stun_session *sess,
+ unsigned flags)
+{
+ PJ_ASSERT_ON_FAIL(sess, return);
+ sess->log_flag = flags;
+}
+
+PJ_DEF(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess,
+ pj_bool_t use)
+{
+ pj_bool_t old_use;
+
+ PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+ old_use = sess->use_fingerprint;
+ sess->use_fingerprint = use;
+ return old_use;
+}
+
+static pj_status_t get_auth(pj_stun_session *sess,
+ pj_stun_tx_data *tdata)
+{
+ if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) {
+ //tdata->auth_info.realm = sess->cred.data.static_cred.realm;
+ tdata->auth_info.realm = sess->server_realm;
+ tdata->auth_info.username = sess->cred.data.static_cred.username;
+ tdata->auth_info.nonce = sess->cred.data.static_cred.nonce;
+
+ pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key,
+ &tdata->auth_info.realm,
+ &tdata->auth_info.username,
+ sess->cred.data.static_cred.data_type,
+ &sess->cred.data.static_cred.data);
+
+ } else if (sess->cred.type == PJ_STUN_AUTH_CRED_DYNAMIC) {
+ pj_str_t password;
+ void *user_data = sess->cred.data.dyn_cred.user_data;
+ pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN;
+ pj_status_t rc;
+
+ rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data,
+ tdata->pool,
+ &tdata->auth_info.realm,
+ &tdata->auth_info.username,
+ &tdata->auth_info.nonce,
+ &data_type, &password);
+ if (rc != PJ_SUCCESS)
+ return rc;
+
+ pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key,
+ &tdata->auth_info.realm, &tdata->auth_info.username,
+ data_type, &password);
+
+ } else {
+ pj_assert(!"Unknown credential type");
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess,
+ int method,
+ pj_uint32_t magic,
+ const pj_uint8_t tsx_id[12],
+ pj_stun_tx_data **p_tdata)
+{
+ pj_stun_tx_data *tdata = NULL;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
+
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ status = create_tdata(sess, &tdata);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Create STUN message */
+ status = pj_stun_msg_create(tdata->pool, method, magic,
+ tsx_id, &tdata->msg);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* copy the request's transaction ID as the transaction key. */
+ pj_assert(sizeof(tdata->msg_key)==sizeof(tdata->msg->hdr.tsx_id));
+ tdata->msg_magic = tdata->msg->hdr.magic;
+ pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id,
+ sizeof(tdata->msg->hdr.tsx_id));
+
+
+ /* Get authentication information for the request */
+ if (sess->auth_type == PJ_STUN_AUTH_NONE) {
+ /* No authentication */
+
+ } else if (sess->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
+ /* MUST put authentication in request */
+ status = get_auth(sess, tdata);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ } else if (sess->auth_type == PJ_STUN_AUTH_LONG_TERM) {
+ /* Only put authentication information if we've received
+ * response from server.
+ */
+ if (sess->next_nonce.slen != 0) {
+ status = get_auth(sess, tdata);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ tdata->auth_info.nonce = sess->next_nonce;
+ tdata->auth_info.realm = sess->server_realm;
+ }
+
+ } else {
+ pj_assert(!"Invalid authentication type");
+ status = PJ_EBUG;
+ goto on_error;
+ }
+
+ *p_tdata = tdata;
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_SUCCESS;
+
+on_error:
+ if (tdata)
+ pj_pool_release(tdata->pool);
+ pj_grp_lock_release(sess->grp_lock);
+ return status;
+}
+
+PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
+ int msg_type,
+ pj_stun_tx_data **p_tdata)
+{
+ pj_stun_tx_data *tdata = NULL;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
+
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ status = create_tdata(sess, &tdata);
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(sess->grp_lock);
+ return status;
+ }
+
+ /* Create STUN message */
+ msg_type |= PJ_STUN_INDICATION_BIT;
+ status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC,
+ NULL, &tdata->msg);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(tdata->pool);
+ pj_grp_lock_release(sess->grp_lock);
+ return status;
+ }
+
+ *p_tdata = tdata;
+
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create a STUN response message.
+ */
+PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
+ const pj_stun_rx_data *rdata,
+ unsigned err_code,
+ const pj_str_t *err_msg,
+ pj_stun_tx_data **p_tdata)
+{
+ pj_status_t status;
+ pj_stun_tx_data *tdata = NULL;
+
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ status = create_tdata(sess, &tdata);
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(sess->grp_lock);
+ return status;
+ }
+
+ /* Create STUN response message */
+ status = pj_stun_msg_create_response(tdata->pool, rdata->msg,
+ err_code, err_msg, &tdata->msg);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(tdata->pool);
+ pj_grp_lock_release(sess->grp_lock);
+ return status;
+ }
+
+ /* copy the request's transaction ID as the transaction key. */
+ pj_assert(sizeof(tdata->msg_key)==sizeof(rdata->msg->hdr.tsx_id));
+ tdata->msg_magic = rdata->msg->hdr.magic;
+ pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id,
+ sizeof(rdata->msg->hdr.tsx_id));
+
+ /* copy the credential found in the request */
+ pj_stun_req_cred_info_dup(tdata->pool, &tdata->auth_info, &rdata->info);
+
+ *p_tdata = tdata;
+
+ pj_grp_lock_release(sess->grp_lock);
+
+ return PJ_SUCCESS;
+}
+
+
+/* Print outgoing message to log */
+static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
+ unsigned pkt_size, const pj_sockaddr_t *addr)
+{
+ char dst_name[PJ_INET6_ADDRSTRLEN+10];
+
+ if ((PJ_STUN_IS_REQUEST(msg->hdr.type) &&
+ (sess->log_flag & PJ_STUN_SESS_LOG_TX_REQ)==0) ||
+ (PJ_STUN_IS_RESPONSE(msg->hdr.type) &&
+ (sess->log_flag & PJ_STUN_SESS_LOG_TX_RES)==0) ||
+ (PJ_STUN_IS_INDICATION(msg->hdr.type) &&
+ (sess->log_flag & PJ_STUN_SESS_LOG_TX_IND)==0))
+ {
+ return;
+ }
+
+ pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3);
+
+ PJ_LOG(5,(SNAME(sess),
+ "TX %d bytes STUN message to %s:\n"
+ "--- begin STUN message ---\n"
+ "%s"
+ "--- end of STUN message ---\n",
+ pkt_size, dst_name,
+ pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf),
+ NULL)));
+
+}
+
+
+PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
+ void *token,
+ pj_bool_t cache_res,
+ pj_bool_t retransmit,
+ const pj_sockaddr_t *server,
+ unsigned addr_len,
+ pj_stun_tx_data *tdata)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL);
+
+ /* Lock the session and prevent user from destroying us in the callback */
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ pj_log_push_indent();
+
+ /* Allocate packet */
+ tdata->max_len = PJ_STUN_MAX_PKT_LEN;
+ tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len);
+
+ tdata->token = token;
+ tdata->retransmit = retransmit;
+
+ /* Apply options */
+ status = apply_msg_options(sess, tdata->pool, &tdata->auth_info,
+ tdata->msg);
+ if (status != PJ_SUCCESS) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ LOG_ERR_(sess, "Error applying options", status);
+ goto on_return;
+ }
+
+ /* Encode message */
+ status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt,
+ tdata->max_len, 0,
+ &tdata->auth_info.auth_key,
+ &tdata->pkt_size);
+ if (status != PJ_SUCCESS) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ LOG_ERR_(sess, "STUN encode() error", status);
+ goto on_return;
+ }
+
+ /* Dump packet */
+ dump_tx_msg(sess, tdata->msg, (unsigned)tdata->pkt_size, server);
+
+ /* If this is a STUN request message, then send the request with
+ * a new STUN client transaction.
+ */
+ if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) {
+
+ /* Create STUN client transaction */
+ status = pj_stun_client_tsx_create(sess->cfg, tdata->pool,
+ sess->grp_lock,
+ &tsx_cb, &tdata->client_tsx);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
+ pj_stun_client_tsx_set_data(tdata->client_tsx, (void*)tdata);
+
+ /* Save the remote address */
+ tdata->addr_len = addr_len;
+ tdata->dst_addr = server;
+
+ /* Send the request! */
+ status = pj_stun_client_tsx_send_msg(tdata->client_tsx, retransmit,
+ tdata->pkt,
+ (unsigned)tdata->pkt_size);
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ LOG_ERR_(sess, "Error sending STUN request", status);
+ goto on_return;
+ }
+
+ /* Add to pending request list */
+ tsx_add(sess, tdata);
+
+ } else {
+ if (cache_res &&
+ (PJ_STUN_IS_SUCCESS_RESPONSE(tdata->msg->hdr.type) ||
+ PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type)))
+ {
+ /* Requested to keep the response in the cache */
+ pj_time_val timeout;
+
+ pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer));
+ pj_timer_entry_init(&tdata->res_timer, PJ_FALSE, tdata,
+ &on_cache_timeout);
+
+ timeout.sec = sess->cfg->res_cache_msec / 1000;
+ timeout.msec = sess->cfg->res_cache_msec % 1000;
+
+ status = pj_timer_heap_schedule_w_grp_lock(sess->cfg->timer_heap,
+ &tdata->res_timer,
+ &timeout, PJ_TRUE,
+ sess->grp_lock);
+ if (status != PJ_SUCCESS) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ LOG_ERR_(sess, "Error scheduling response timer", status);
+ goto on_return;
+ }
+
+ pj_list_push_back(&sess->cached_response_list, tdata);
+ }
+
+ /* Otherwise for non-request message, send directly to transport. */
+ status = sess->cb.on_send_msg(sess, token, tdata->pkt,
+ tdata->pkt_size, server, addr_len);
+
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ LOG_ERR_(sess, "Error sending STUN request", status);
+ goto on_return;
+ }
+
+ /* Destroy only when response is not cached*/
+ if (tdata->res_timer.id == 0) {
+ pj_stun_msg_destroy_tdata(sess, tdata);
+ }
+ }
+
+on_return:
+ pj_log_pop_indent();
+
+ if (pj_grp_lock_release(sess->grp_lock))
+ return PJ_EGONE;
+
+ return status;
+}
+
+
+/*
+ * Create and send STUN response message.
+ */
+PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess,
+ const pj_stun_rx_data *rdata,
+ unsigned code,
+ const char *errmsg,
+ void *token,
+ pj_bool_t cache,
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len)
+{
+ pj_status_t status;
+ pj_str_t reason;
+ pj_stun_tx_data *tdata;
+
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ status = pj_stun_session_create_res(sess, rdata, code,
+ (errmsg?pj_cstr(&reason,errmsg):NULL),
+ &tdata);
+ if (status != PJ_SUCCESS) {
+ pj_grp_lock_release(sess->grp_lock);
+ return status;
+ }
+
+ status = pj_stun_session_send_msg(sess, token, cache, PJ_FALSE,
+ dst_addr, addr_len, tdata);
+
+ pj_grp_lock_release(sess->grp_lock);
+ return status;
+}
+
+
+/*
+ * Cancel outgoing STUN transaction.
+ */
+PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess,
+ pj_stun_tx_data *tdata,
+ pj_bool_t notify,
+ pj_status_t notify_status)
+{
+ PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
+ PJ_ASSERT_RETURN(!notify || notify_status!=PJ_SUCCESS, PJ_EINVAL);
+ PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
+
+ /* Lock the session and prevent user from destroying us in the callback */
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ if (notify) {
+ (sess->cb.on_request_complete)(sess, notify_status, tdata->token,
+ tdata, NULL, NULL, 0);
+ }
+
+ /* Just destroy tdata. This will destroy the transaction as well */
+ pj_stun_msg_destroy_tdata(sess, tdata);
+
+ pj_grp_lock_release(sess->grp_lock);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Explicitly request retransmission of the request.
+ */
+PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
+ pj_stun_tx_data *tdata,
+ pj_bool_t mod_count)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
+ PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
+
+ /* Lock the session and prevent user from destroying us in the callback */
+ pj_grp_lock_acquire(sess->grp_lock);
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ status = pj_stun_client_tsx_retransmit(tdata->client_tsx, mod_count);
+
+ pj_grp_lock_release(sess->grp_lock);
+
+ return status;
+}
+
+
+/* Send response */
+static pj_status_t send_response(pj_stun_session *sess, void *token,
+ pj_pool_t *pool, pj_stun_msg *response,
+ const pj_stun_req_cred_info *auth_info,
+ pj_bool_t retransmission,
+ const pj_sockaddr_t *addr, unsigned addr_len)
+{
+ pj_uint8_t *out_pkt;
+ pj_size_t out_max_len, out_len;
+ pj_status_t status;
+
+ /* Apply options */
+ if (!retransmission) {
+ status = apply_msg_options(sess, pool, auth_info, response);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ /* Alloc packet buffer */
+ out_max_len = PJ_STUN_MAX_PKT_LEN;
+ out_pkt = (pj_uint8_t*) pj_pool_alloc(pool, out_max_len);
+
+ /* Encode */
+ status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0,
+ &auth_info->auth_key, &out_len);
+ if (status != PJ_SUCCESS) {
+ LOG_ERR_(sess, "Error encoding message", status);
+ return status;
+ }
+
+ /* Print log */
+ dump_tx_msg(sess, response, (unsigned)out_len, addr);
+
+ /* Send packet */
+ status = sess->cb.on_send_msg(sess, token, out_pkt, (unsigned)out_len,
+ addr, addr_len);
+
+ return status;
+}
+
+/* Authenticate incoming message */
+static pj_status_t authenticate_req(pj_stun_session *sess,
+ void *token,
+ const pj_uint8_t *pkt,
+ unsigned pkt_len,
+ pj_stun_rx_data *rdata,
+ pj_pool_t *tmp_pool,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ pj_stun_msg *response;
+ pj_status_t status;
+
+ if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) ||
+ sess->auth_type == PJ_STUN_AUTH_NONE)
+ {
+ return PJ_SUCCESS;
+ }
+
+ status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg,
+ &sess->cred, tmp_pool, &rdata->info,
+ &response);
+ if (status != PJ_SUCCESS && response != NULL) {
+ PJ_LOG(5,(SNAME(sess), "Message authentication failed"));
+ send_response(sess, token, tmp_pool, response, &rdata->info,
+ PJ_FALSE, src_addr, src_addr_len);
+ }
+
+ return status;
+}
+
+
+/* Handle incoming response */
+static pj_status_t on_incoming_response(pj_stun_session *sess,
+ unsigned options,
+ const pj_uint8_t *pkt,
+ unsigned pkt_len,
+ pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ pj_stun_tx_data *tdata;
+ pj_status_t status;
+
+ /* Lookup pending client transaction */
+ tdata = tsx_lookup(sess, msg);
+ if (tdata == NULL) {
+ PJ_LOG(5,(SNAME(sess),
+ "Transaction not found, response silently discarded"));
+ return PJ_SUCCESS;
+ }
+
+ if (sess->auth_type == PJ_STUN_AUTH_NONE)
+ options |= PJ_STUN_NO_AUTHENTICATE;
+
+ /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
+ * is specified in the option.
+ */
+ if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 &&
+ tdata->auth_info.auth_key.slen != 0 &&
+ pj_stun_auth_valid_for_msg(msg))
+ {
+ status = pj_stun_authenticate_response(pkt, pkt_len, msg,
+ &tdata->auth_info.auth_key);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(5,(SNAME(sess),
+ "Response authentication failed"));
+ return status;
+ }
+ }
+
+ /* Pass the response to the transaction.
+ * If the message is accepted, transaction callback will be called,
+ * and this will call the session callback too.
+ */
+ status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg,
+ src_addr, src_addr_len);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/* For requests, check if we cache the response */
+static pj_status_t check_cached_response(pj_stun_session *sess,
+ pj_pool_t *tmp_pool,
+ const pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ pj_stun_tx_data *t;
+
+ /* First lookup response in response cache */
+ t = sess->cached_response_list.next;
+ while (t != &sess->cached_response_list) {
+ if (t->msg_magic == msg->hdr.magic &&
+ t->msg->hdr.type == msg->hdr.type &&
+ pj_memcmp(t->msg_key, msg->hdr.tsx_id,
+ sizeof(msg->hdr.tsx_id))==0)
+ {
+ break;
+ }
+ t = t->next;
+ }
+
+ if (t != &sess->cached_response_list) {
+ /* Found response in the cache */
+
+ PJ_LOG(5,(SNAME(sess),
+ "Request retransmission, sending cached response"));
+
+ send_response(sess, t->token, tmp_pool, t->msg, &t->auth_info,
+ PJ_TRUE, src_addr, src_addr_len);
+ return PJ_SUCCESS;
+ }
+
+ return PJ_ENOTFOUND;
+}
+
+/* Handle incoming request */
+static pj_status_t on_incoming_request(pj_stun_session *sess,
+ unsigned options,
+ void *token,
+ pj_pool_t *tmp_pool,
+ const pj_uint8_t *in_pkt,
+ unsigned in_pkt_len,
+ pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ pj_stun_rx_data rdata;
+ pj_status_t status;
+
+ /* Init rdata */
+ rdata.msg = msg;
+ pj_bzero(&rdata.info, sizeof(rdata.info));
+
+ if (sess->auth_type == PJ_STUN_AUTH_NONE)
+ options |= PJ_STUN_NO_AUTHENTICATE;
+
+ /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
+ * is specified in the option.
+ */
+ if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) {
+ status = authenticate_req(sess, token, (const pj_uint8_t*) in_pkt,
+ in_pkt_len,&rdata, tmp_pool, src_addr,
+ src_addr_len);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+ }
+
+ /* Distribute to handler, or respond with Bad Request */
+ if (sess->cb.on_rx_request) {
+ status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, &rdata,
+ token, src_addr, src_addr_len);
+ } else {
+ pj_str_t err_text;
+ pj_stun_msg *response;
+
+ err_text = pj_str("Callback is not set to handle request");
+ status = pj_stun_msg_create_response(tmp_pool, msg,
+ PJ_STUN_SC_BAD_REQUEST,
+ &err_text, &response);
+ if (status == PJ_SUCCESS && response) {
+ status = send_response(sess, token, tmp_pool, response,
+ NULL, PJ_FALSE, src_addr, src_addr_len);
+ }
+ }
+
+ return status;
+}
+
+
+/* Handle incoming indication */
+static pj_status_t on_incoming_indication(pj_stun_session *sess,
+ void *token,
+ pj_pool_t *tmp_pool,
+ const pj_uint8_t *in_pkt,
+ unsigned in_pkt_len,
+ const pj_stun_msg *msg,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ PJ_UNUSED_ARG(tmp_pool);
+
+ /* Distribute to handler */
+ if (sess->cb.on_rx_indication) {
+ return (*sess->cb.on_rx_indication)(sess, in_pkt, in_pkt_len, msg,
+ token, src_addr, src_addr_len);
+ } else {
+ return PJ_SUCCESS;
+ }
+}
+
+
+/* Print outgoing message to log */
+static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
+ unsigned pkt_size, const pj_sockaddr_t *addr)
+{
+ char src_info[PJ_INET6_ADDRSTRLEN+10];
+
+ if ((PJ_STUN_IS_REQUEST(msg->hdr.type) &&
+ (sess->log_flag & PJ_STUN_SESS_LOG_RX_REQ)==0) ||
+ (PJ_STUN_IS_RESPONSE(msg->hdr.type) &&
+ (sess->log_flag & PJ_STUN_SESS_LOG_RX_RES)==0) ||
+ (PJ_STUN_IS_INDICATION(msg->hdr.type) &&
+ (sess->log_flag & PJ_STUN_SESS_LOG_RX_IND)==0))
+ {
+ return;
+ }
+
+ pj_sockaddr_print(addr, src_info, sizeof(src_info), 3);
+
+ PJ_LOG(5,(SNAME(sess),
+ "RX %d bytes STUN message from %s:\n"
+ "--- begin STUN message ---\n"
+ "%s"
+ "--- end of STUN message ---\n",
+ pkt_size, src_info,
+ pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf),
+ NULL)));
+
+}
+
+/* Incoming packet */
+PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
+ const void *packet,
+ pj_size_t pkt_size,
+ unsigned options,
+ void *token,
+ pj_size_t *parsed_len,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len)
+{
+ pj_stun_msg *msg, *response;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL);
+
+ /* Lock the session and prevent user from destroying us in the callback */
+ pj_grp_lock_acquire(sess->grp_lock);
+
+ if (sess->is_destroying) {
+ pj_grp_lock_release(sess->grp_lock);
+ return PJ_EINVALIDOP;
+ }
+
+ pj_log_push_indent();
+
+ /* Reset pool */
+ pj_pool_reset(sess->rx_pool);
+
+ /* Try to parse the message */
+ status = pj_stun_msg_decode(sess->rx_pool, (const pj_uint8_t*)packet,
+ pkt_size, options,
+ &msg, parsed_len, &response);
+ if (status != PJ_SUCCESS) {
+ LOG_ERR_(sess, "STUN msg_decode() error", status);
+ if (response) {
+ send_response(sess, token, sess->rx_pool, response, NULL,
+ PJ_FALSE, src_addr, src_addr_len);
+ }
+ goto on_return;
+ }
+
+ dump_rx_msg(sess, msg, (unsigned)pkt_size, src_addr);
+
+ /* For requests, check if we have cached response */
+ status = check_cached_response(sess, sess->rx_pool, msg,
+ src_addr, src_addr_len);
+ if (status == PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ /* Handle message */
+ if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) ||
+ PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
+ {
+ status = on_incoming_response(sess, options,
+ (const pj_uint8_t*) packet,
+ (unsigned)pkt_size, msg,
+ src_addr, src_addr_len);
+
+ } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
+
+ status = on_incoming_request(sess, options, token, sess->rx_pool,
+ (const pj_uint8_t*) packet,
+ (unsigned)pkt_size,
+ msg, src_addr, src_addr_len);
+
+ } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) {
+
+ status = on_incoming_indication(sess, token, sess->rx_pool,
+ (const pj_uint8_t*) packet,
+ (unsigned)pkt_size, msg, src_addr,
+ src_addr_len);
+
+ } else {
+ pj_assert(!"Unexpected!");
+ status = PJ_EBUG;
+ }
+
+on_return:
+ pj_log_pop_indent();
+
+ if (pj_grp_lock_release(sess->grp_lock))
+ return PJ_EGONE;
+
+ return status;
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/f8/f838216083b8163c5b79a4cfb9408b62b593c24f.svn-base b/jni/pjproject-android/.svn/pristine/f8/f838216083b8163c5b79a4cfb9408b62b593c24f.svn-base
new file mode 100644
index 0000000..ac38841
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f838216083b8163c5b79a4cfb9408b62b593c24f.svn-base
@@ -0,0 +1,27 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-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
+ */
+#import <UIKit/UIKit.h>
+
+int main(int argc, char *argv[]) {
+
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ int retVal = UIApplicationMain(argc, argv, nil, nil);
+ [pool release];
+ return retVal;
+}
diff --git a/jni/pjproject-android/.svn/pristine/f8/f84040453d29d4d765e99e0f053d83b6727ba71f.svn-base b/jni/pjproject-android/.svn/pristine/f8/f84040453d29d4d765e99e0f053d83b6727ba71f.svn-base
new file mode 100644
index 0000000..ed18206
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f84040453d29d4d765e99e0f053d83b6727ba71f.svn-base
@@ -0,0 +1,956 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-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 "test.h"
+
+#if INCLUDE_HTTP_CLIENT_TEST
+
+#define THIS_FILE "test_http"
+//#define VERBOSE
+#define STR_PREC(s) (int)s.slen, s.ptr
+#define USE_LOCAL_SERVER
+
+#include <pjlib.h>
+#include <pjlib-util.h>
+
+#define ACTION_REPLY 0
+#define ACTION_IGNORE -1
+
+static struct server_t
+{
+ pj_sock_t sock;
+ pj_uint16_t port;
+ pj_thread_t *thread;
+
+ /* Action:
+ * 0: reply with the response in resp.
+ * -1: ignore query (to simulate timeout).
+ * other: reply with that error
+ */
+ int action;
+ pj_bool_t send_content_length;
+ unsigned data_size;
+ unsigned buf_size;
+} g_server;
+
+static pj_bool_t thread_quit;
+static pj_timer_heap_t *timer_heap;
+static pj_ioqueue_t *ioqueue;
+static pj_pool_t *pool;
+static pj_http_req *http_req;
+static pj_bool_t test_cancel = PJ_FALSE;
+static pj_size_t total_size;
+static pj_size_t send_size = 0;
+static pj_status_t sstatus;
+static pj_sockaddr_in addr;
+static int counter = 0;
+
+static int server_thread(void *p)
+{
+ struct server_t *srv = (struct server_t*)p;
+ char *pkt = (char*)pj_pool_alloc(pool, srv->buf_size);
+ pj_sock_t newsock = PJ_INVALID_SOCKET;
+
+ while (!thread_quit) {
+ pj_ssize_t pkt_len;
+ int rc;
+ pj_fd_set_t rset;
+ pj_time_val timeout = {0, 500};
+
+ while (!thread_quit) {
+ PJ_FD_ZERO(&rset);
+ PJ_FD_SET(srv->sock, &rset);
+ rc = pj_sock_select((int)srv->sock+1, &rset, NULL, NULL, &timeout);
+ if (rc != 1) {
+ continue;
+ }
+
+ rc = pj_sock_accept(srv->sock, &newsock, NULL, NULL);
+ if (rc == PJ_SUCCESS) {
+ break;
+ }
+ }
+
+ if (thread_quit)
+ break;
+
+ while (!thread_quit) {
+ PJ_FD_ZERO(&rset);
+ PJ_FD_SET(newsock, &rset);
+ rc = pj_sock_select((int)newsock+1, &rset, NULL, NULL, &timeout);
+ if (rc != 1) {
+ PJ_LOG(3,("http test", "client timeout"));
+ continue;
+ }
+
+ pkt_len = srv->buf_size;
+ rc = pj_sock_recv(newsock, pkt, &pkt_len, 0);
+ if (rc == PJ_SUCCESS) {
+ break;
+ }
+ }
+
+ if (thread_quit)
+ break;
+
+ /* Simulate network RTT */
+ pj_thread_sleep(50);
+
+ if (srv->action == ACTION_IGNORE) {
+ continue;
+ } else if (srv->action == ACTION_REPLY) {
+ pj_size_t send_size = 0;
+ unsigned ctr = 0;
+ pj_ansi_sprintf(pkt, "HTTP/1.0 200 OK\r\n");
+ if (srv->send_content_length) {
+ pj_ansi_sprintf(pkt + pj_ansi_strlen(pkt),
+ "Content-Length: %d\r\n",
+ srv->data_size);
+ }
+ pj_ansi_sprintf(pkt + pj_ansi_strlen(pkt), "\r\n");
+ pkt_len = pj_ansi_strlen(pkt);
+ rc = pj_sock_send(newsock, pkt, &pkt_len, 0);
+ if (rc != PJ_SUCCESS) {
+ pj_sock_close(newsock);
+ continue;
+ }
+ while (send_size < srv->data_size) {
+ pkt_len = srv->data_size - send_size;
+ if (pkt_len > (signed)srv->buf_size)
+ pkt_len = srv->buf_size;
+ send_size += pkt_len;
+ pj_create_random_string(pkt, pkt_len);
+ pj_ansi_sprintf(pkt, "\nPacket: %d", ++ctr);
+ pkt[pj_ansi_strlen(pkt)] = '\n';
+ rc = pj_sock_send(newsock, pkt, &pkt_len, 0);
+ if (rc != PJ_SUCCESS)
+ break;
+ }
+ pj_sock_close(newsock);
+ }
+ }
+
+ return 0;
+}
+
+static void on_data_read(pj_http_req *hreq, void *data, pj_size_t size)
+{
+ PJ_UNUSED_ARG(hreq);
+ PJ_UNUSED_ARG(data);
+
+ PJ_LOG(5, (THIS_FILE, "\nData received: %d bytes", size));
+ if (size > 0) {
+#ifdef VERBOSE
+ printf("%.*s\n", (int)size, (char *)data);
+#endif
+ }
+}
+
+static void on_send_data(pj_http_req *hreq,
+ void **data, pj_size_t *size)
+{
+ char *sdata;
+ pj_size_t sendsz = 8397;
+
+ PJ_UNUSED_ARG(hreq);
+
+ if (send_size + sendsz > total_size) {
+ sendsz = total_size - send_size;
+ }
+ send_size += sendsz;
+
+ sdata = (char*)pj_pool_alloc(pool, sendsz);
+ pj_create_random_string(sdata, sendsz);
+ pj_ansi_sprintf(sdata, "\nSegment #%d\n", ++counter);
+ *data = sdata;
+ *size = sendsz;
+
+ PJ_LOG(5, (THIS_FILE, "\nSending data progress: %d out of %d bytes",
+ send_size, total_size));
+}
+
+
+static void on_complete(pj_http_req *hreq, pj_status_t status,
+ const pj_http_resp *resp)
+{
+ PJ_UNUSED_ARG(hreq);
+
+ if (status == PJ_ECANCELLED) {
+ PJ_LOG(5, (THIS_FILE, "Request cancelled"));
+ return;
+ } else if (status == PJ_ETIMEDOUT) {
+ PJ_LOG(5, (THIS_FILE, "Request timed out!"));
+ return;
+ } else if (status != PJ_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Error %d", status));
+ return;
+ }
+ PJ_LOG(5, (THIS_FILE, "\nData completed: %d bytes", resp->size));
+ if (resp->size > 0 && resp->data) {
+#ifdef VERBOSE
+ printf("%.*s\n", (int)resp->size, (char *)resp->data);
+#endif
+ }
+}
+
+static void on_response(pj_http_req *hreq, const pj_http_resp *resp)
+{
+ pj_size_t i;
+
+ PJ_UNUSED_ARG(hreq);
+ PJ_UNUSED_ARG(resp);
+ PJ_UNUSED_ARG(i);
+
+#ifdef VERBOSE
+ printf("%.*s, %d, %.*s\n", STR_PREC(resp->version),
+ resp->status_code, STR_PREC(resp->reason));
+ for (i = 0; i < resp->headers.count; i++) {
+ printf("%.*s : %.*s\n",
+ STR_PREC(resp->headers.header[i].name),
+ STR_PREC(resp->headers.header[i].value));
+ }
+#endif
+
+ if (test_cancel) {
+ /* Need to delay closing the client socket here, otherwise the
+ * server will get SIGPIPE when sending response.
+ */
+ pj_thread_sleep(100);
+ pj_http_req_cancel(hreq, PJ_TRUE);
+ test_cancel = PJ_FALSE;
+ }
+}
+
+
+pj_status_t parse_url(const char *url, pj_http_url *hurl)
+{
+ pj_str_t surl;
+ pj_status_t status;
+
+ pj_cstr(&surl, url);
+ status = pj_http_req_parse_url(&surl, hurl);
+#ifdef VERBOSE
+ if (!status) {
+ printf("URL: %s\nProtocol: %.*s\nHost: %.*s\nPort: %d\nPath: %.*s\n\n",
+ url, STR_PREC(hurl->protocol), STR_PREC(hurl->host),
+ hurl->port, STR_PREC(hurl->path));
+ } else {
+ }
+#endif
+ return status;
+}
+
+static int parse_url_test()
+{
+ struct test_data
+ {
+ char *url;
+ pj_status_t result;
+ const char *username;
+ const char *passwd;
+ const char *host;
+ int port;
+ const char *path;
+ } test_data[] =
+ {
+ /* Simple URL without '/' in the end */
+ {"http://www.pjsip.org", PJ_SUCCESS, "", "", "www.pjsip.org", 80, "/"},
+
+ /* Simple URL with port number but without '/' in the end */
+ {"http://pjsip.org:8080", PJ_SUCCESS, "", "", "pjsip.org", 8080, "/"},
+
+ /* URL with path */
+ {"http://127.0.0.1:280/Joomla/index.php?option=com_content&task=view&id=5&Itemid=6",
+ PJ_SUCCESS, "", "", "127.0.0.1", 280,
+ "/Joomla/index.php?option=com_content&task=view&id=5&Itemid=6"},
+
+ /* URL with port and path */
+ {"http://pjsip.org:81/about-us/", PJ_SUCCESS, "", "", "pjsip.org", 81, "/about-us/"},
+
+ /* unsupported protocol */
+ {"ftp://www.pjsip.org", PJ_ENOTSUP, "", "", "", 80, ""},
+
+ /* invalid format */
+ {"http:/pjsip.org/about-us/", PJLIB_UTIL_EHTTPINURL, "", "", "", 80, ""},
+
+ /* invalid port number */
+ {"http://pjsip.org:xyz/", PJLIB_UTIL_EHTTPINPORT, "", "", "", 80, ""},
+
+ /* with username and password */
+ {"http://user:pass@pjsip.org", PJ_SUCCESS, "user", "pass", "pjsip.org", 80, "/"},
+
+ /* password only*/
+ {"http://:pass@pjsip.org", PJ_SUCCESS, "", "pass", "pjsip.org", 80, "/"},
+
+ /* user only*/
+ {"http://user:@pjsip.org", PJ_SUCCESS, "user", "", "pjsip.org", 80, "/"},
+
+ /* empty username and passwd*/
+ {"http://:@pjsip.org", PJ_SUCCESS, "", "", "pjsip.org", 80, "/"},
+
+ /* '@' character in username and path */
+ {"http://user@pjsip.org/@", PJ_SUCCESS, "user", "", "pjsip.org", 80, "/@"},
+
+ /* '@' character in path */
+ {"http://pjsip.org/@", PJ_SUCCESS, "", "", "pjsip.org", 80, "/@"},
+
+ /* '@' character in path */
+ {"http://pjsip.org/one@", PJ_SUCCESS, "", "", "pjsip.org", 80, "/one@"},
+
+ /* Invalid URL */
+ {"http://:", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http://@", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http:/", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http://", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http:///", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http://@/", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http:///@", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http://:::", PJ_EINVAL, "", "", "", 0, ""},
+ };
+ unsigned i;
+
+ for (i=0; i<PJ_ARRAY_SIZE(test_data); ++i) {
+ struct test_data *ptd;
+ pj_http_url hurl;
+ pj_status_t status;
+
+ ptd = &test_data[i];
+
+ PJ_LOG(3, (THIS_FILE, ".. %s", ptd->url));
+ status = parse_url(ptd->url, &hurl);
+
+ if (status != ptd->result) {
+ PJ_LOG(3,(THIS_FILE, "%d", status));
+ return -11;
+ }
+ if (status != PJ_SUCCESS)
+ continue;
+ if (pj_strcmp2(&hurl.username, ptd->username))
+ return -12;
+ if (pj_strcmp2(&hurl.passwd, ptd->passwd))
+ return -13;
+ if (pj_strcmp2(&hurl.host, ptd->host))
+ return -14;
+ if (hurl.port != ptd->port)
+ return -15;
+ if (pj_strcmp2(&hurl.path, ptd->path))
+ return -16;
+ }
+
+ return 0;
+}
+
+/*
+ * GET request scenario 1: using on_response() and on_data_read()
+ * Server replies with content-length. Application cancels the
+ * request upon receiving the response, then start it again.
+ */
+int http_client_test1()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_data_read = &on_data_read;
+ hcb.on_response = &on_response;
+ pj_http_req_param_default(¶m);
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -31;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -32;
+
+#ifdef USE_LOCAL_SERVER
+
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_REPLY;
+ g_server.send_content_length = PJ_TRUE;
+ g_server.data_size = 2970;
+ g_server.buf_size = 1024;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d/about-us/",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://www.teluu.com/about-us/");
+#endif
+
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ ¶m, &hcb, &http_req))
+ return -33;
+
+ test_cancel = PJ_TRUE;
+ if (pj_http_req_start(http_req))
+ return -35;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+ if (pj_http_req_start(http_req))
+ return -37;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * GET request scenario 2: using on_complete() to get the
+ * complete data. Server does not reply with content-length.
+ * Request timed out, application sets a longer timeout, then
+ * then restart the request.
+ */
+int http_client_test2()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ pj_time_val timeout;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_response = &on_response;
+ pj_http_req_param_default(¶m);
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -41;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -42;
+
+#ifdef USE_LOCAL_SERVER
+
+ pj_cstr(&url, "http://127.0.0.1:380");
+ param.timeout.sec = 0;
+ param.timeout.msec = 2000;
+
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_IGNORE;
+ g_server.send_content_length = PJ_FALSE;
+ g_server.data_size = 4173;
+ g_server.buf_size = 1024;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://www.google.com.sg");
+ param.timeout.sec = 0;
+ param.timeout.msec = 50;
+#endif
+
+ pj_http_headers_add_elmt2(¶m.headers, (char*)"Accept",
+ (char*)"image/gif, image/x-xbitmap, image/jpeg, "
+ "image/pjpeg, application/x-ms-application,"
+ " application/vnd.ms-xpsdocument, "
+ "application/xaml+xml, "
+ "application/x-ms-xbap, "
+ "application/x-shockwave-flash, "
+ "application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, "
+ "application/msword, */*");
+ pj_http_headers_add_elmt2(¶m.headers, (char*)"Accept-Language",
+ (char*)"en-sg");
+ pj_http_headers_add_elmt2(¶m.headers, (char*)"User-Agent",
+ (char*)"Mozilla/4.0 (compatible; MSIE 7.0; "
+ "Windows NT 6.0; SLCC1; "
+ ".NET CLR 2.0.50727; "
+ ".NET CLR 3.0.04506)");
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ ¶m, &hcb, &http_req))
+ return -43;
+
+ if (pj_http_req_start(http_req))
+ return -45;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ g_server.action = ACTION_REPLY;
+#endif
+
+ timeout.sec = 0; timeout.msec = 10000;
+ pj_http_req_set_timeout(http_req, &timeout);
+ if (pj_http_req_start(http_req))
+ return -47;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * PUT request scenario 1: sending the whole data at once
+ */
+int http_client_test_put1()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ char *data;
+ int length = 3875;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_data_read = &on_data_read;
+ hcb.on_response = &on_response;
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -51;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -52;
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_REPLY;
+ g_server.send_content_length = PJ_TRUE;
+ g_server.data_size = 0;
+ g_server.buf_size = 4096;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d/test/test.txt",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://127.0.0.1:280/test/test.txt");
+
+#endif
+
+ pj_http_req_param_default(¶m);
+ pj_strset2(¶m.method, (char*)"PUT");
+ data = (char*)pj_pool_alloc(pool, length);
+ pj_create_random_string(data, length);
+ pj_ansi_sprintf(data, "PUT test\n");
+ param.reqdata.data = data;
+ param.reqdata.size = length;
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ ¶m, &hcb, &http_req))
+ return -53;
+
+ if (pj_http_req_start(http_req))
+ return -55;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * PUT request scenario 2: using on_send_data() callback to
+ * sending the data in chunks
+ */
+int http_client_test_put2()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_send_data = &on_send_data;
+ hcb.on_data_read = &on_data_read;
+ hcb.on_response = &on_response;
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -51;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -52;
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_REPLY;
+ g_server.send_content_length = PJ_TRUE;
+ g_server.data_size = 0;
+ g_server.buf_size = 16384;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d/test/test2.txt",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://127.0.0.1:280/test/test2.txt");
+
+#endif
+
+ pj_http_req_param_default(¶m);
+ pj_strset2(¶m.method, (char*)"PUT");
+ total_size = 15383;
+ send_size = 0;
+ param.reqdata.total_size = total_size;
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ ¶m, &hcb, &http_req))
+ return -53;
+
+ if (pj_http_req_start(http_req))
+ return -55;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+int http_client_test_delete()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_response = &on_response;
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -61;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -62;
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_REPLY;
+ g_server.send_content_length = PJ_TRUE;
+ g_server.data_size = 0;
+ g_server.buf_size = 1024;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d/test/test2.txt",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://127.0.0.1:280/test/test2.txt");
+#endif
+
+ pj_http_req_param_default(¶m);
+ pj_strset2(¶m.method, (char*)"DELETE");
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ ¶m, &hcb, &http_req))
+ return -63;
+
+ if (pj_http_req_start(http_req))
+ return -65;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+int http_client_test()
+{
+ int rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing URL parsing"));
+ rc = parse_url_test();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing GET request scenario 1"));
+ rc = http_client_test1();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing GET request scenario 2"));
+ rc = http_client_test2();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing PUT request scenario 1"));
+ rc = http_client_test_put1();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing PUT request scenario 2"));
+ rc = http_client_test_put2();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing DELETE request"));
+ rc = http_client_test_delete();
+ if (rc)
+ return rc;
+
+ return PJ_SUCCESS;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_http_client_test;
+#endif /* INCLUDE_HTTP_CLIENT_TEST */
diff --git a/jni/pjproject-android/.svn/pristine/f8/f85942d3536375cb79ebeb7f2cd722d19db9a282.svn-base b/jni/pjproject-android/.svn/pristine/f8/f85942d3536375cb79ebeb7f2cd722d19db9a282.svn-base
new file mode 100644
index 0000000..8cccd42
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f85942d3536375cb79ebeb7f2cd722d19db9a282.svn-base
@@ -0,0 +1,140 @@
+/* $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
+ */
+#ifndef __PJ_COMPAT_OS_DARWINOS_H__
+#define __PJ_COMPAT_OS_DARWINOS_H__
+
+/**
+ * @file os_darwinos.h
+ * @brief Describes Darwin/MacOSX operating system specifics.
+ */
+
+#define PJ_OS_NAME "darwin"
+
+#define PJ_HAS_ARPA_INET_H 1
+#define PJ_HAS_ASSERT_H 1
+#define PJ_HAS_CTYPE_H 1
+#define PJ_HAS_ERRNO_H 1
+#define PJ_HAS_LINUX_SOCKET_H 0
+#define PJ_HAS_MALLOC_H 0
+#define PJ_HAS_NETDB_H 1
+#define PJ_HAS_NETINET_IN_H 1
+#define PJ_HAS_NETINET_TCP_H 1
+#define PJ_HAS_SETJMP_H 1
+#define PJ_HAS_STDARG_H 1
+#define PJ_HAS_STDDEF_H 1
+#define PJ_HAS_STDIO_H 1
+#define PJ_HAS_STDLIB_H 1
+#define PJ_HAS_STRING_H 1
+#define PJ_HAS_SYS_IOCTL_H 1
+#define PJ_HAS_SYS_SELECT_H 1
+#define PJ_HAS_SYS_SOCKET_H 1
+#define PJ_HAS_SYS_TIME_H 1
+#define PJ_HAS_SYS_TIMEB_H 1
+#define PJ_HAS_SYS_TYPES_H 1
+#define PJ_HAS_TIME_H 1
+#define PJ_HAS_UNISTD_H 1
+
+#define PJ_HAS_MSWSOCK_H 0
+#define PJ_HAS_WINSOCK_H 0
+#define PJ_HAS_WINSOCK2_H 0
+
+/* Is errno a good way to retrieve OS errors?
+ */
+#define PJ_HAS_ERRNO_VAR 1
+
+/* Has inet_aton() ?
+ */
+#define PJ_SOCK_HAS_INET_ATON 1
+
+/* When this macro is set, getsockopt(SOL_SOCKET, SO_ERROR) will return
+ * the status of non-blocking connect() operation.
+ */
+#define PJ_HAS_SO_ERROR 1
+
+/* This value specifies the value set in errno by the OS when a non-blocking
+ * socket recv() can not return immediate daata.
+ */
+#define PJ_BLOCKING_ERROR_VAL EWOULDBLOCK
+
+/* This value specifies the value set in errno by the OS when a non-blocking
+ * socket connect() can not get connected immediately.
+ */
+#define PJ_BLOCKING_CONNECT_ERROR_VAL EINPROGRESS
+
+/* Default threading is enabled, unless it's overridden. */
+#ifndef PJ_HAS_THREADS
+# define PJ_HAS_THREADS (1)
+#endif
+
+#define PJ_HAS_HIGH_RES_TIMER 1
+#define PJ_HAS_MALLOC 1
+#ifndef PJ_OS_HAS_CHECK_STACK
+# define PJ_OS_HAS_CHECK_STACK 0
+#endif
+#define PJ_NATIVE_STRING_IS_UNICODE 0
+
+#define PJ_ATOMIC_VALUE_TYPE long
+
+/* Set 1 if native sockaddr_in has sin_len member.
+ * Default: 0
+ */
+#define PJ_SOCKADDR_HAS_LEN 1
+
+/*
+ * gcc complains that it can not use precompiled header because
+ * the value of FD_SETSIZE that we declare in pj/config.h is
+ * different than the value in /usr/include/sys/types.h.
+ *
+ * This changes the default value for Darwin.
+ */
+#define PJ_IOQUEUE_MAX_HANDLES 1024
+
+/**
+ * If this macro is set, it tells select I/O Queue that select() needs to
+ * be given correct value of nfds (i.e. largest fd + 1). This requires
+ * select ioqueue to re-scan the descriptors on each registration and
+ * unregistration.
+ * If this macro is not set, then ioqueue will always give FD_SETSIZE for
+ * nfds argument when calling select().
+ *
+ * Default: 0
+ */
+#define PJ_SELECT_NEEDS_NFDS 0
+
+/* If 1, use Read/Write mutex emulation for platforms that don't support it */
+#define PJ_EMULATE_RWMUTEX 0
+
+/* If 1, pj_thread_create() should enforce the stack size when creating
+ * threads.
+ * Default: 0 (let OS decide the thread's stack size).
+ */
+#define PJ_THREAD_SET_STACK_SIZE 0
+
+/* If 1, pj_thread_create() should allocate stack from the pool supplied.
+ * Default: 0 (let OS allocate memory for thread's stack).
+ */
+#define PJ_THREAD_ALLOCATE_STACK 0
+
+/* MacOS has had socklen since 10.4 */
+#define PJ_HAS_SOCKLEN_T 1
+
+
+#endif /* __PJ_COMPAT_OS_DARWINOS_H__ */
+
diff --git a/jni/pjproject-android/.svn/pristine/f8/f8818dc1d63605151861852d88f831d488ae3322.svn-base b/jni/pjproject-android/.svn/pristine/f8/f8818dc1d63605151861852d88f831d488ae3322.svn-base
new file mode 100644
index 0000000..d00ccb6
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f8818dc1d63605151861852d88f831d488ae3322.svn-base
@@ -0,0 +1,163 @@
+# By default, the test application includes main.o.
+# OS make file may override this with os-specific files
+export PJNATH_TEST_OBJS = main.o
+
+include ../../build.mak
+include ../../version.mak
+include $(PJDIR)/build/common.mak
+
+export LIBDIR := ../lib
+export BINDIR := ../bin
+
+RULES_MAK := $(PJDIR)/build/rules.mak
+
+PJLIB_LIB:=../../pjlib/lib/libpj-$(TARGET_NAME)$(LIBEXT)
+PJLIB_UTIL_LIB:=../../pjlib-util/lib/libpjlib-util-$(TARGET_NAME)$(LIBEXT)
+
+export PJNATH_LIB:=libpjnath-$(TARGET_NAME)$(LIBEXT)
+
+ifeq ($(PJ_SHARED_LIBRARIES),)
+else
+export PJNATH_SONAME := libpjnath.$(SHLIB_SUFFIX)
+export PJNATH_SHLIB := $(PJNATH_SONAME).$(PJ_VERSION_MAJOR)
+endif
+
+###############################################################################
+# Gather all flags.
+#
+export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \
+ $(CFLAGS) $(CC_INC)../include $(CC_INC)../../pjlib/include \
+ $(CC_INC)../../pjlib-util/include
+export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \
+ $(HOST_CXXFLAGS) $(CXXFLAGS)
+export _LDFLAGS := $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \
+ $(APP_LDFLAGS) $(LDFLAGS)
+
+###############################################################################
+# Defines for building PJNATH library
+#
+export PJNATH_SRCDIR = ../src/pjnath
+export PJNATH_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
+ errno.o ice_session.o ice_strans.o nat_detect.o stun_auth.o \
+ stun_msg.o stun_msg_dump.o stun_session.o stun_sock.o \
+ stun_transaction.o turn_session.o turn_sock.o
+export PJNATH_CFLAGS += $(_CFLAGS)
+export PJNATH_CXXFLAGS += $(_CXXFLAGS)
+export PJNATH_LDFLAGS += $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
+
+###############################################################################
+# Defines for building test application
+#
+export PJNATH_TEST_SRCDIR = ../src/pjnath-test
+export PJNATH_TEST_OBJS += ice_test.o stun.o sess_auth.o server.o concur_test.o \
+ stun_sock_test.o turn_sock_test.o test.o
+export PJNATH_TEST_CFLAGS += $(_CFLAGS)
+export PJNATH_TEST_CXXFLAGS += $(_CXXFLAGS)
+export PJNATH_TEST_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
+export PJNATH_TEST_EXE:=pjnath-test-$(TARGET_NAME)$(HOST_EXE)
+
+
+###############################################################################
+# Defines for building TURN client application
+#
+export PJTURN_CLIENT_SRCDIR = ../src/pjturn-client
+export PJTURN_CLIENT_OBJS += client_main.o
+export PJTURN_CLIENT_CFLAGS += $(_CFLAGS)
+export PJTURN_CLIENT_CXXFLAGS += $(_CXXFLAGS)
+export PJTURN_CLIENT_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
+export PJTURN_CLIENT_EXE:=pjturn-client-$(TARGET_NAME)$(HOST_EXE)
+
+###############################################################################
+# Defines for building TURN server application
+#
+export PJTURN_SRV_SRCDIR = ../src/pjturn-srv
+export PJTURN_SRV_OBJS += allocation.o auth.o listener_udp.o \
+ listener_tcp.o server.o main.o
+export PJTURN_SRV_CFLAGS += $(_CFLAGS)
+export PJTURN_SRV_CXXFLAGS += $(_CXXFLAGS)
+export PJTURN_SRV_LDFLAGS += $(PJNATH_LDLIB) $(PJLIB_UTIL_LDLIB) $(PJLIB_LDLIB) $(_LDFLAGS)
+export PJTURN_SRV_EXE:=pjturn-srv-$(TARGET_NAME)$(HOST_EXE)
+
+
+
+export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
+###############################################################################
+# Main entry
+#
+# $(TARGET) is defined in os-$(OS_NAME).mak file in current directory.
+#
+TARGETS := $(PJNATH_LIB) $(PJNATH_SONAME) $(PJNATH_TEST_EXE) $(PJTURN_CLIENT_EXE) $(PJTURN_SRV_EXE)
+
+all: $(TARGETS)
+
+doc:
+ cd .. && rm -rf docs/$(PJ_VERSION) && doxygen docs/doxygen.cfg
+ @if [ -n "$(WWWDIR)" ] && ! [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html" ] ; then \
+ echo "Creating docs/$(PJ_VERSION)/pjnath/docs/html" ; \
+ mkdir -p $(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html ; \
+ fi
+ @if [ -n "$(WWWDIR)" ] && [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html" ] ; then \
+ echo "Copying docs/$(PJ_VERSION) to $(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html.." ; \
+ cp -v -a ../docs/$(PJ_VERSION)/html/* $(WWWDIR)/docs/$(PJ_VERSION)/pjnath/docs/html/ ; \
+ fi
+
+dep: depend
+distclean: realclean
+
+.PHONY: all dep depend clean realclean distclean
+.PHONY: $(TARGETS)
+.PHONY: $(PJNATH_LIB) $(PJNATH_SONAME)
+.PHONY: $(PJNATH_TEST_EXE) $(PJTURN_CLIENT_EXE) $(PJTURN_SRV_EXE)
+
+pjnath: $(PJNATH_LIB)
+$(PJNATH_SONAME): $(PJNATH_LIB)
+$(PJNATH_LIB) $(PJNATH_SONAME): $(PJLIB_LIB) $(PJLIB_SONAME) $(PJLIB_UTIL_LIB) $(PJLIB_UTIL_SONAME)
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $(subst /,$(HOST_PSEP),$(LIBDIR)/$@)
+
+pjnath-test: $(PJNATH_TEST_EXE)
+$(PJNATH_TEST_EXE): $(PJNATH_LIB) $(PJNATH_SONAME)
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $(subst /,$(HOST_PSEP),$(BINDIR)/$@)
+
+pjturn-client: $(PJTURN_CLIENT_EXE)
+$(PJTURN_CLIENT_EXE): $(PJNATH_LIB) $(PJNATH_SONAME)
+ $(MAKE) -f $(RULES_MAK) APP=PJTURN_CLIENT app=pjturn-client $(subst /,$(HOST_PSEP),$(BINDIR)/$@)
+
+pjturn-srv: $(PJTURN_SRV_EXE)
+$(PJTURN_SRV_EXE): $(PJNATH_LIB) $(PJNATH_SONAME)
+ $(MAKE) -f $(RULES_MAK) APP=PJTURN_SRV app=pjturn-srv $(subst /,$(HOST_PSEP),$(BINDIR)/$@)
+
+.PHONY: pjnath.ko
+pjnath.ko:
+ echo Making $@
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $(subst /,$(HOST_PSEP),$(LIBDIR)/$@)
+
+.PHONY: pjnath-test.ko
+pjnath-test.ko:
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $(subst /,$(HOST_PSEP),$(LIBDIR)/$@)
+
+clean:
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $@
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $@
+ $(MAKE) -f $(RULES_MAK) APP=PJTURN_CLIENT app=pjturn-client $@
+ $(MAKE) -f $(RULES_MAK) APP=PJTURN_SRV app=pjturn-srv $@
+
+realclean:
+ $(subst @@,$(subst /,$(HOST_PSEP),.pjnath-$(TARGET_NAME).depend),$(HOST_RMR))
+ $(subst @@,$(subst /,$(HOST_PSEP),.pjnath-test-$(TARGET_NAME).depend),$(HOST_RMR))
+ $(subst @@,$(subst /,$(HOST_PSEP),.pjturn-client-$(TARGET_NAME).depend),$(HOST_RMR))
+ $(subst @@,$(subst /,$(HOST_PSEP),.pjturn-srv-$(TARGET_NAME).depend),$(HOST_RMR))
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $@
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $@
+ $(MAKE) -f $(RULES_MAK) APP=PJTURN_CLIENT app=pjturn-client $@
+ $(MAKE) -f $(RULES_MAK) APP=PJTURN_SRV app=pjturn-srv $@
+
+depend:
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH app=pjnath $@
+ $(MAKE) -f $(RULES_MAK) APP=PJNATH_TEST app=pjnath-test $@
+ $(MAKE) -f $(RULES_MAK) APP=PJTURN_CLIENT app=pjturn-client $@
+ $(MAKE) -f $(RULES_MAK) APP=PJTURN_SRV app=pjturn-srv $@
+ echo '$(BINDIR)/$(PJNATH_TEST_EXE): $(LIBDIR)/$(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjnath-test-$(TARGET_NAME).depend
+ echo '$(BINDIR)/$(PJTURN_CLIENT_EXE): $(LIBDIR)/$(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjturn-client-$(TARGET_NAME).depend
+ echo '$(BINDIR)/$(PJTURN_SRV_EXE): $(LIBDIR)/$(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjturn-srv-$(TARGET_NAME).depend
+
+
diff --git a/jni/pjproject-android/.svn/pristine/f8/f888237bd89b873ec98caf6d482bf57507817fff.svn-base b/jni/pjproject-android/.svn/pristine/f8/f888237bd89b873ec98caf6d482bf57507817fff.svn-base
new file mode 100644
index 0000000..d5eed81
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f888237bd89b873ec98caf6d482bf57507817fff.svn-base
@@ -0,0 +1,461 @@
+/****************************************************************************************
+**
+** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C
+** > Software Release 2.1 (2008-06)
+** (Simple repackaging; no change from 2005-05 Release 2.0 code)
+**
+** © 2004 Polycom, Inc.
+**
+** All rights reserved.
+**
+****************************************************************************************/
+
+/****************************************************************************************
+ Filename: common.c
+
+ Purpose: Contains the functions used for both G.722.1 Annex C encoder and decoder
+
+ Design Notes:
+
+****************************************************************************************/
+/****************************************************************************************
+ Include files
+****************************************************************************************/
+#include "defs.h"
+#include "huff_def.h"
+#include "huff_tab.h"
+#include "tables.h"
+#include "count.h"
+
+/****************************************************************************************
+ Function: categorize
+
+ Syntax: void categorize(Word16 number_of_available_bits,
+ Word16 number_of_regions,
+ Word16 num_categorization_control_possibilities,
+ Word16 rms_index,
+ Word16 power_categories,
+ Word16 category_balances)
+
+ inputs: number_of_regions
+ num_categorization_control_possibilities
+ number_of_available_bits
+ rms_index[MAX_NUMBER_OF_REGIONS]
+
+ outputs: power_categories[MAX_NUMBER_OF_REGIONS]
+ category_balances[MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES-1]
+
+ Description: Computes a series of categorizations
+
+ WMOPS: 7kHz | 24kbit | 32kbit
+ -------|--------------|----------------
+ AVG | 0.14 | 0.14
+ -------|--------------|----------------
+ MAX | 0.15 | 0.15
+ -------|--------------|----------------
+
+ 14kHz | 24kbit | 32kbit | 48kbit
+ -------|--------------|----------------|----------------
+ AVG | 0.42 | 0.45 | 0.48
+ -------|--------------|----------------|----------------
+ MAX | 0.47 | 0.52 | 0.52
+ -------|--------------|----------------|----------------
+
+****************************************************************************************/
+void categorize(Word16 number_of_available_bits,
+ Word16 number_of_regions,
+ Word16 num_categorization_control_possibilities,
+ Word16 *rms_index,
+ Word16 *power_categories,
+ Word16 *category_balances)
+{
+
+ Word16 offset;
+ Word16 temp;
+ Word16 frame_size;
+
+ /* At higher bit rates, there is an increase for most categories in average bit
+ consumption per region. We compensate for this by pretending we have fewer
+ available bits. */
+ test();
+ if (number_of_regions == NUMBER_OF_REGIONS)
+ {
+ frame_size = DCT_LENGTH;
+ }
+ else
+ {
+ frame_size = MAX_DCT_LENGTH;
+ }
+
+ temp = sub(number_of_available_bits,frame_size);
+
+ test();
+ if (temp > 0)
+ {
+ number_of_available_bits = sub(number_of_available_bits,frame_size);
+ number_of_available_bits = extract_l(L_mult0(number_of_available_bits,5));
+ number_of_available_bits = shr_nocheck(number_of_available_bits,3);
+ number_of_available_bits = add(number_of_available_bits,frame_size);
+ }
+
+ /* calculate the offset using the original category assignments */
+ offset = calc_offset(rms_index,number_of_regions,number_of_available_bits);
+
+
+
+ /* compute the power categories based on the uniform offset */
+ compute_raw_pow_categories(power_categories,rms_index,number_of_regions,offset);
+
+
+ /* adjust the category assignments */
+ /* compute the new power categories and category balances */
+ comp_powercat_and_catbalance(power_categories,category_balances,rms_index,number_of_available_bits,number_of_regions,num_categorization_control_possibilities,offset);
+
+}
+
+/***************************************************************************
+ Function: comp_powercat_and_catbalance
+
+ Syntax: void comp_powercat_and_catbalance(Word16 *power_categories,
+ Word16 *category_balances,
+ Word16 *rms_index,
+ Word16 number_of_available_bits,
+ Word16 number_of_regions,
+ Word16 num_categorization_control_possibilities,
+ Word16 offset)
+
+
+ inputs: *rms_index
+ number_of_available_bits
+ number_of_regions
+ num_categorization_control_possibilities
+ offset
+
+ outputs: *power_categories
+ *category_balances
+
+
+ Description: Computes the power_categories and the category balances
+
+ WMOPS: 7kHz | 24kbit | 32kbit
+ -------|--------------|----------------
+ AVG | 0.10 | 0.10
+ -------|--------------|----------------
+ MAX | 0.11 | 0.11
+ -------|--------------|----------------
+
+ 14kHz | 24kbit | 32kbit | 48kbit
+ -------|--------------|----------------|----------------
+ AVG | 0.32 | 0.35 | 0.38
+ -------|--------------|----------------|----------------
+ MAX | 0.38 | 0.42 | 0.43
+ -------|--------------|----------------|----------------
+
+***************************************************************************/
+void comp_powercat_and_catbalance(Word16 *power_categories,
+ Word16 *category_balances,
+ Word16 *rms_index,
+ Word16 number_of_available_bits,
+ Word16 number_of_regions,
+ Word16 num_categorization_control_possibilities,
+ Word16 offset)
+{
+
+ Word16 expected_number_of_code_bits;
+ Word16 region;
+ Word16 max_region;
+ Word16 j;
+ Word16 max_rate_categories[MAX_NUMBER_OF_REGIONS];
+ Word16 min_rate_categories[MAX_NUMBER_OF_REGIONS];
+ Word16 temp_category_balances[2*MAX_NUM_CATEGORIZATION_CONTROL_POSSIBILITIES];
+ Word16 raw_max, raw_min;
+ Word16 raw_max_index=0, raw_min_index=0;
+ Word16 max_rate_pointer, min_rate_pointer;
+ Word16 max, min;
+ Word16 itemp0;
+ Word16 itemp1;
+ Word16 min_plus_max;
+ Word16 two_x_number_of_available_bits;
+
+ Word16 temp;
+
+ expected_number_of_code_bits = 0;
+ move16();
+
+ for (region=0; region<number_of_regions; region++)
+ expected_number_of_code_bits = add(expected_number_of_code_bits,expected_bits_table[power_categories[region]]);
+
+
+ for (region=0; region<number_of_regions; region++)
+ {
+ max_rate_categories[region] = power_categories[region];
+ move16();
+
+ min_rate_categories[region] = power_categories[region];
+ move16();
+ }
+
+ max = expected_number_of_code_bits;
+ move16();
+ min = expected_number_of_code_bits;
+ move16();
+ max_rate_pointer = num_categorization_control_possibilities;
+ move16();
+ min_rate_pointer = num_categorization_control_possibilities;
+ move16();
+
+ for (j=0; j<num_categorization_control_possibilities-1; j++)
+ {
+ min_plus_max = add(max,min);
+ two_x_number_of_available_bits = shl_nocheck(number_of_available_bits,1);
+
+ temp = sub(min_plus_max,two_x_number_of_available_bits);
+ test();
+ if (temp <= 0)
+ {
+ raw_min = 99;
+ move16();
+ /* Search from lowest freq regions to highest for best */
+ /* region to reassign to a higher bit rate category. */
+ for (region=0; region<number_of_regions; region++)
+ {
+ test();
+ if (max_rate_categories[region] > 0)
+ {
+ itemp0 = shl_nocheck(max_rate_categories[region],1);
+ itemp1 = sub(offset,rms_index[region]);
+ itemp0 = sub(itemp1,itemp0);
+
+ temp = sub(itemp0,raw_min);
+ test();
+ if (temp < 0)
+ {
+ raw_min = itemp0;
+ raw_min_index = region;
+ }
+ }
+ }
+ max_rate_pointer = sub(max_rate_pointer,1);
+ temp_category_balances[max_rate_pointer] = raw_min_index;
+ move16();
+
+ max = sub(max,expected_bits_table[max_rate_categories[raw_min_index]]);
+ max_rate_categories[raw_min_index] = sub(max_rate_categories[raw_min_index],1);
+ move16();
+
+ max = add(max,expected_bits_table[max_rate_categories[raw_min_index]]);
+ }
+ else
+ {
+ raw_max = -99;
+ move16();
+ /* Search from highest freq regions to lowest for best region to reassign to
+ a lower bit rate category. */
+ max_region = sub(number_of_regions,1);
+ for (region= max_region; region >= 0; region--)
+ {
+ temp = sub(min_rate_categories[region],(NUM_CATEGORIES-1));
+ test();
+ if (temp < 0)
+ {
+ itemp0 = shl_nocheck(min_rate_categories[region],1);
+ itemp1 = sub(offset,rms_index[region]);
+ itemp0 = sub(itemp1,itemp0);
+
+ temp = sub(itemp0,raw_max);
+ test();
+ if (temp > 0)
+ {
+ raw_max = itemp0;
+ move16();
+ raw_max_index = region;
+ move16();
+ }
+ }
+ }
+ temp_category_balances[min_rate_pointer] = raw_max_index;
+ move16();
+
+ min_rate_pointer = add(min_rate_pointer,1);
+ min = sub(min,expected_bits_table[min_rate_categories[raw_max_index]]);
+
+ min_rate_categories[raw_max_index] = add(min_rate_categories[raw_max_index],1);
+ move16();
+
+ min = add(min,expected_bits_table[min_rate_categories[raw_max_index]]);
+ }
+ }
+
+ for (region=0; region<number_of_regions; region++)
+ {
+ power_categories[region] = max_rate_categories[region];
+ move16();
+ }
+
+ for (j=0; j<num_categorization_control_possibilities-1; j++)
+ {
+ category_balances[j] = temp_category_balances[max_rate_pointer++];
+ move16();
+ }
+
+}
+/***************************************************************************
+ Function: calc_offset
+
+ Syntax: offset=calc_offset(Word16 *rms_index,Word16 number_of_regions,Word16 available_bits)
+
+ input: Word16 *rms_index
+ Word16 number_of_regions
+ Word16 available_bits
+
+ output: Word16 offset
+
+ Description: Calculates the the category offset. This is the shift required
+ To get the most out of the number of available bits. A binary
+ type search is used to find the offset.
+
+ WMOPS: 7kHz | 24kbit | 32kbit
+ -------|--------------|----------------
+ AVG | 0.04 | 0.04
+ -------|--------------|----------------
+ MAX | 0.04 | 0.04
+ -------|--------------|----------------
+
+ 14kHz | 24kbit | 32kbit | 48kbit
+ -------|--------------|----------------|----------------
+ AVG | 0.08 | 0.08 | 0.08
+ -------|--------------|----------------|----------------
+ MAX | 0.09 | 0.09 | 0.09
+ -------|--------------|----------------|----------------
+
+***************************************************************************/
+Word16 calc_offset(Word16 *rms_index,Word16 number_of_regions,Word16 available_bits)
+{
+
+ Word16 answer;
+ Word16 delta;
+ Word16 test_offset;
+ Word16 region,j;
+ Word16 power_cats[MAX_NUMBER_OF_REGIONS];
+ Word16 bits;
+ Word16 offset;
+ Word16 temp;
+
+ /* initialize vars */
+ answer = -32;
+ move16();
+ delta = 32;
+ move16();
+
+ do
+ {
+ test_offset = add(answer,delta);
+
+ /* obtain a category for each region */
+ /* using the test offset */
+ for (region=0; region<number_of_regions; region++)
+ {
+ j = sub(test_offset,rms_index[region]);
+ j = shr_nocheck(j,1);
+
+ /* Ensure j is between 0 and NUM_CAT-1 */
+ test();
+ if (j < 0)
+ {
+ j = 0;
+ move16();
+ }
+ temp = sub(j,NUM_CATEGORIES-1);
+ test();
+ if (temp > 0)
+ {
+ j = sub(NUM_CATEGORIES,1);
+ move16();
+ }
+ power_cats[region] = j;
+ move16();
+ }
+ bits = 0;
+ move16();
+
+ /* compute the number of bits that will be used given the cat assignments */
+ for (region=0; region<number_of_regions; region++)
+ bits = add(bits,expected_bits_table[power_cats[region]]);
+
+ /* if (bits > available_bits - 32) then divide the offset region for the bin search */
+ offset = sub(available_bits,32);
+ temp = sub(bits,offset);
+ test();
+ if (temp >= 0)
+ {
+ answer = test_offset;
+ move16();
+ }
+ delta = shr_nocheck(delta,1);
+ test(); /* for the while loop */
+ } while (delta > 0);
+
+ return(answer);
+}
+/***************************************************************************
+ Function: compute_raw_pow_categories
+
+ Syntax: void compute_raw_pow_categories(Word16 *power_categories,
+ Word16 *rms_index,
+ Word16 number_of_regions,
+ Word16 offset)
+ inputs: *rms_index
+ number_of_regions
+ offset
+
+ outputs: *power_categories
+
+
+
+ Description: This function computes the power categories given the offset
+ This is kind of redundant since they were already computed
+ in calc_offset to determine the offset.
+
+ WMOPS: | 24kbit | 32kbit
+ -------|--------------|----------------
+ AVG | 0.01 | 0.01
+ -------|--------------|----------------
+ MAX | 0.01 | 0.01
+ -------|--------------|----------------
+
+ 14kHz | 24kbit | 32kbit | 48kbit
+ -------|--------------|----------------|----------------
+ AVG | 0.01 | 0.01 | 0.01
+ -------|--------------|----------------|----------------
+ MAX | 0.01 | 0.01 | 0.01
+ -------|--------------|----------------|----------------
+
+***************************************************************************/
+void compute_raw_pow_categories(Word16 *power_categories,Word16 *rms_index,Word16 number_of_regions,Word16 offset)
+{
+ Word16 region;
+ Word16 j;
+ Word16 temp;
+
+ for (region=0; region<number_of_regions; region++)
+ {
+ j = sub(offset,rms_index[region]);
+ j = shr_nocheck(j,1);
+
+ /* make sure j is between 0 and NUM_CAT-1 */
+ test();
+ if (j < 0)
+ {
+ j = 0;
+ move16();
+ }
+ temp = sub(j,(NUM_CATEGORIES-1));
+ test();
+ if (temp > 0)
+ j = sub(NUM_CATEGORIES,1);
+
+ power_categories[region] = j;
+ move16();
+ }
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/f8/f8b5e580472624433ad17ea70b2c68f4fe2d2de9.svn-base b/jni/pjproject-android/.svn/pristine/f8/f8b5e580472624433ad17ea70b2c68f4fe2d2de9.svn-base
new file mode 100644
index 0000000..7081aed
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f8b5e580472624433ad17ea70b2c68f4fe2d2de9.svn-base
@@ -0,0 +1,226 @@
+# $Id$
+import time
+import imp
+import sys
+import inc_const as const
+from inc_cfg import *
+
+# Load configuration
+cfg_file = imp.load_source("cfg_file", ARGS[1])
+
+# Check media flow between ua1 and ua2
+def check_media(ua1, ua2):
+ ua1.send("#")
+ ua1.expect("#")
+ ua1.send("1122")
+ ua2.expect(const.RX_DTMF + "1")
+ ua2.expect(const.RX_DTMF + "1")
+ ua2.expect(const.RX_DTMF + "2")
+ ua2.expect(const.RX_DTMF + "2")
+
+
+# Test body function
+def test_func(t):
+ callee = t.process[0]
+ caller = t.process[1]
+
+ # if have_reg then wait for couple of seconds for PUBLISH
+ # to complete (just in case pUBLISH is used)
+ if callee.inst_param.have_reg:
+ time.sleep(1)
+ if caller.inst_param.have_reg:
+ time.sleep(1)
+
+ # Caller making call
+ caller.send("m")
+ caller.send(t.inst_params[0].uri)
+ caller.expect(const.STATE_CALLING)
+
+ # Callee waits for call and answers with 180/Ringing
+ time.sleep(0.2)
+ callee.expect(const.EVENT_INCOMING_CALL)
+ callee.send("a")
+ callee.send("180")
+ callee.expect("SIP/2.0 180")
+ caller.expect("SIP/2.0 180")
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Callee answers with 200/OK
+ callee.send("a")
+ callee.send("200")
+
+ # Wait until call is connected in both endpoints
+ time.sleep(0.2)
+ caller.expect(const.STATE_CONFIRMED)
+ callee.expect(const.STATE_CONFIRMED)
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+ time.sleep(0.1)
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Test that media is okay
+ time.sleep(0.3)
+ check_media(caller, callee)
+ check_media(callee, caller)
+
+ # Hold call by caller
+ caller.send("H")
+ caller.expect("INVITE sip:")
+ callee.expect("INVITE sip:")
+ caller.expect(const.MEDIA_HOLD)
+ callee.expect(const.MEDIA_HOLD)
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Release hold
+ time.sleep(0.5)
+ caller.send("v")
+ caller.expect("INVITE sip:")
+ callee.expect("INVITE sip:")
+ caller.expect(const.MEDIA_ACTIVE, title="waiting for media active after call hold")
+ callee.expect(const.MEDIA_ACTIVE, title="waiting for media active after call hold")
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Test that media is okay
+ check_media(caller, callee)
+ check_media(callee, caller)
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Hold call by callee
+ callee.send("H")
+ callee.expect("INVITE sip:")
+ caller.expect("INVITE sip:")
+ caller.expect(const.MEDIA_HOLD)
+ callee.expect(const.MEDIA_HOLD)
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Release hold
+ time.sleep(0.1)
+ callee.send("v")
+ callee.expect("INVITE sip:")
+ caller.expect("INVITE sip:")
+ callee.expect(const.MEDIA_ACTIVE, title="waiting for media active after call hold")
+ caller.expect(const.MEDIA_ACTIVE, title="waiting for media active after call hold")
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Test that media is okay
+ # Wait for some time for ICE negotiation
+ time.sleep(0.6)
+ check_media(caller, callee)
+ check_media(callee, caller)
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # UPDATE (by caller)
+ caller.send("U")
+ #caller.sync_stdout()
+ callee.expect(const.MEDIA_ACTIVE, title="waiting for media active with UPDATE")
+ caller.expect(const.MEDIA_ACTIVE, title="waiting for media active with UPDATE")
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Test that media is okay
+ time.sleep(0.1)
+ check_media(caller, callee)
+ check_media(callee, caller)
+
+ # UPDATE (by callee)
+ callee.send("U")
+ callee.expect("UPDATE sip:")
+ caller.expect("UPDATE sip:")
+ caller.expect(const.MEDIA_ACTIVE, title="waiting for media active with UPDATE")
+ callee.expect(const.MEDIA_ACTIVE, title="waiting for media active with UPDATE")
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Test that media is okay
+ time.sleep(0.1)
+ check_media(caller, callee)
+ check_media(callee, caller)
+
+ # Synchronize stdout
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Set codecs in both caller and callee so that there is
+ # no common codec between them.
+ # In caller we only enable PCMU, in callee we only enable PCMA
+ caller.send("Cp")
+ caller.expect("Enter codec")
+ caller.send("* 0")
+ caller.send("Cp")
+ caller.expect("Enter codec")
+ caller.send("pcmu 120")
+
+ callee.send("Cp")
+ callee.expect("Enter codec")
+ callee.send("* 0")
+ callee.send("Cp")
+ callee.expect("Enter codec")
+ callee.send("pcma 120")
+
+ # Test when UPDATE fails (by callee)
+ callee.send("U")
+ caller.expect("SIP/2.0 488")
+ callee.expect("SIP/2.0 488")
+ callee.sync_stdout()
+ caller.sync_stdout()
+
+ # Test that media is still okay
+ time.sleep(0.1)
+ check_media(caller, callee)
+ check_media(callee, caller)
+
+ # Test when UPDATE fails (by caller)
+ caller.send("U")
+ caller.expect("UPDATE sip:")
+ callee.expect("UPDATE sip:")
+ callee.expect("SIP/2.0 488")
+ caller.expect("SIP/2.0 488")
+ caller.sync_stdout()
+ callee.sync_stdout()
+
+ # Test that media is still okay
+ time.sleep(0.1)
+ check_media(callee, caller)
+ check_media(caller, callee)
+
+ # Hangup call
+ time.sleep(0.1)
+ caller.send("h")
+
+ # Wait until calls are cleared in both endpoints
+ caller.expect(const.STATE_DISCONNECTED)
+ callee.expect(const.STATE_DISCONNECTED)
+
+
+# Here where it all comes together
+test = cfg_file.test_param
+test.test_func = test_func
+
diff --git a/jni/pjproject-android/.svn/pristine/f8/f8c0405be84da517713c762ea3f4fb62ad6065d7.svn-base b/jni/pjproject-android/.svn/pristine/f8/f8c0405be84da517713c762ea3f4fb62ad6065d7.svn-base
new file mode 100644
index 0000000..a77340b
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f8c0405be84da517713c762ea3f4fb62ad6065d7.svn-base
@@ -0,0 +1,949 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2012-2012 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
+ *
+ * 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
+ */
+/* This file is the implementation of Android OpenSL ES audio device.
+ * The original code was originally part of CSipSimple
+ * (http://code.google.com/p/csipsimple/) and was kindly donated
+ * by Regis Montoya.
+ */
+
+#include <pjmedia-audiodev/audiodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pjmedia/errno.h>
+
+#if defined(PJMEDIA_AUDIO_DEV_HAS_OPENSL) && PJMEDIA_AUDIO_DEV_HAS_OPENSL != 0
+
+#include <SLES/OpenSLES.h>
+
+#ifdef __ANDROID__
+ #include <SLES/OpenSLES_Android.h>
+ #include <SLES/OpenSLES_AndroidConfiguration.h>
+ #include <sys/system_properties.h>
+ #include <android/api-level.h>
+
+ #define W_SLBufferQueueItf SLAndroidSimpleBufferQueueItf
+ #define W_SLBufferQueueState SLAndroidSimpleBufferQueueState
+ #define W_SL_IID_BUFFERQUEUE SL_IID_ANDROIDSIMPLEBUFFERQUEUE
+#else
+ #define W_SLBufferQueueItf SLBufferQueueItf
+ #define W_SLBufferQueueState SLBufferQueueState
+ #define W_SL_IID_BUFFERQUEUE SL_IID_BUFFERQUEUE
+#endif
+
+#define THIS_FILE "opensl_dev.c"
+#define DRIVER_NAME "OpenSL"
+
+#define NUM_BUFFERS 2
+
+struct opensl_aud_factory
+{
+ pjmedia_aud_dev_factory base;
+ pj_pool_factory *pf;
+ pj_pool_t *pool;
+
+ SLObjectItf engineObject;
+ SLEngineItf engineEngine;
+ SLObjectItf outputMixObject;
+};
+
+/*
+ * Sound stream descriptor.
+ * This struct may be used for both unidirectional or bidirectional sound
+ * streams.
+ */
+struct opensl_aud_stream
+{
+ pjmedia_aud_stream base;
+ pj_pool_t *pool;
+ pj_str_t name;
+ pjmedia_dir dir;
+ pjmedia_aud_param param;
+
+ void *user_data;
+ pj_bool_t quit_flag;
+ pjmedia_aud_rec_cb rec_cb;
+ pjmedia_aud_play_cb play_cb;
+
+ pj_timestamp play_timestamp;
+ pj_timestamp rec_timestamp;
+
+ pj_bool_t rec_thread_initialized;
+ pj_thread_desc rec_thread_desc;
+ pj_thread_t *rec_thread;
+
+ pj_bool_t play_thread_initialized;
+ pj_thread_desc play_thread_desc;
+ pj_thread_t *play_thread;
+
+ /* Player */
+ SLObjectItf playerObj;
+ SLPlayItf playerPlay;
+ SLVolumeItf playerVol;
+ unsigned playerBufferSize;
+ char *playerBuffer[NUM_BUFFERS];
+ int playerBufIdx;
+
+ /* Recorder */
+ SLObjectItf recordObj;
+ SLRecordItf recordRecord;
+ unsigned recordBufferSize;
+ char *recordBuffer[NUM_BUFFERS];
+ int recordBufIdx;
+
+ W_SLBufferQueueItf playerBufQ;
+ W_SLBufferQueueItf recordBufQ;
+};
+
+/* Factory prototypes */
+static pj_status_t opensl_init(pjmedia_aud_dev_factory *f);
+static pj_status_t opensl_destroy(pjmedia_aud_dev_factory *f);
+static pj_status_t opensl_refresh(pjmedia_aud_dev_factory *f);
+static unsigned opensl_get_dev_count(pjmedia_aud_dev_factory *f);
+static pj_status_t opensl_get_dev_info(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_dev_info *info);
+static pj_status_t opensl_default_param(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_param *param);
+static pj_status_t opensl_create_stream(pjmedia_aud_dev_factory *f,
+ const pjmedia_aud_param *param,
+ pjmedia_aud_rec_cb rec_cb,
+ pjmedia_aud_play_cb play_cb,
+ void *user_data,
+ pjmedia_aud_stream **p_aud_strm);
+
+/* Stream prototypes */
+static pj_status_t strm_get_param(pjmedia_aud_stream *strm,
+ pjmedia_aud_param *param);
+static pj_status_t strm_get_cap(pjmedia_aud_stream *strm,
+ pjmedia_aud_dev_cap cap,
+ void *value);
+static pj_status_t strm_set_cap(pjmedia_aud_stream *strm,
+ pjmedia_aud_dev_cap cap,
+ const void *value);
+static pj_status_t strm_start(pjmedia_aud_stream *strm);
+static pj_status_t strm_stop(pjmedia_aud_stream *strm);
+static pj_status_t strm_destroy(pjmedia_aud_stream *strm);
+
+static pjmedia_aud_dev_factory_op opensl_op =
+{
+ &opensl_init,
+ &opensl_destroy,
+ &opensl_get_dev_count,
+ &opensl_get_dev_info,
+ &opensl_default_param,
+ &opensl_create_stream,
+ &opensl_refresh
+};
+
+static pjmedia_aud_stream_op opensl_strm_op =
+{
+ &strm_get_param,
+ &strm_get_cap,
+ &strm_set_cap,
+ &strm_start,
+ &strm_stop,
+ &strm_destroy
+};
+
+/* This callback is called every time a buffer finishes playing. */
+void bqPlayerCallback(W_SLBufferQueueItf bq, void *context)
+{
+ struct opensl_aud_stream *stream = (struct opensl_aud_stream*) context;
+ SLresult result;
+ int status;
+
+ pj_assert(context != NULL);
+ pj_assert(bq == stream->playerBufQ);
+
+ if (stream->play_thread_initialized == 0 || !pj_thread_is_registered())
+ {
+ pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
+ status = pj_thread_register("opensl_play", stream->play_thread_desc,
+ &stream->play_thread);
+ stream->play_thread_initialized = 1;
+ PJ_LOG(5, (THIS_FILE, "Player thread started"));
+ }
+
+ if (!stream->quit_flag) {
+ pjmedia_frame frame;
+ char * buf;
+
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.buf = buf = stream->playerBuffer[stream->playerBufIdx++];
+ frame.size = stream->playerBufferSize;
+ frame.timestamp.u64 = stream->play_timestamp.u64;
+ frame.bit_info = 0;
+
+ status = (*stream->play_cb)(stream->user_data, &frame);
+ if (status != PJ_SUCCESS || frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
+ pj_bzero(buf, stream->playerBufferSize);
+
+ stream->play_timestamp.u64 += stream->param.samples_per_frame /
+ stream->param.channel_count;
+
+ result = (*bq)->Enqueue(bq, buf, stream->playerBufferSize);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Unable to enqueue next player buffer !!! %d",
+ result));
+ }
+
+ stream->playerBufIdx %= NUM_BUFFERS;
+ }
+}
+
+/* This callback handler is called every time a buffer finishes recording */
+void bqRecorderCallback(W_SLBufferQueueItf bq, void *context)
+{
+ struct opensl_aud_stream *stream = (struct opensl_aud_stream*) context;
+ SLresult result;
+ int status;
+
+ pj_assert(context != NULL);
+ pj_assert(bq == stream->recordBufQ);
+
+ if (stream->rec_thread_initialized == 0 || !pj_thread_is_registered())
+ {
+ pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc));
+ status = pj_thread_register("opensl_rec", stream->rec_thread_desc,
+ &stream->rec_thread);
+ stream->rec_thread_initialized = 1;
+ PJ_LOG(5, (THIS_FILE, "Recorder thread started"));
+ }
+
+ if (!stream->quit_flag) {
+ pjmedia_frame frame;
+ char *buf;
+
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.buf = buf = stream->recordBuffer[stream->recordBufIdx++];
+ frame.size = stream->recordBufferSize;
+ frame.timestamp.u64 = stream->rec_timestamp.u64;
+ frame.bit_info = 0;
+
+ status = (*stream->rec_cb)(stream->user_data, &frame);
+
+ stream->rec_timestamp.u64 += stream->param.samples_per_frame /
+ stream->param.channel_count;
+
+ /* And now enqueue next buffer */
+ result = (*bq)->Enqueue(bq, buf, stream->recordBufferSize);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Unable to enqueue next record buffer !!! %d",
+ result));
+ }
+
+ stream->recordBufIdx %= NUM_BUFFERS;
+ }
+}
+
+pj_status_t opensl_to_pj_error(SLresult code)
+{
+ switch(code) {
+ case SL_RESULT_SUCCESS:
+ return PJ_SUCCESS;
+ case SL_RESULT_PRECONDITIONS_VIOLATED:
+ case SL_RESULT_PARAMETER_INVALID:
+ case SL_RESULT_CONTENT_CORRUPTED:
+ case SL_RESULT_FEATURE_UNSUPPORTED:
+ return PJMEDIA_EAUD_INVOP;
+ case SL_RESULT_MEMORY_FAILURE:
+ case SL_RESULT_BUFFER_INSUFFICIENT:
+ return PJ_ENOMEM;
+ case SL_RESULT_RESOURCE_ERROR:
+ case SL_RESULT_RESOURCE_LOST:
+ case SL_RESULT_CONTROL_LOST:
+ return PJMEDIA_EAUD_NOTREADY;
+ case SL_RESULT_CONTENT_UNSUPPORTED:
+ return PJ_ENOTSUP;
+ default:
+ return PJMEDIA_EAUD_ERR;
+ }
+}
+
+/* Init Android audio driver. */
+pjmedia_aud_dev_factory* pjmedia_opensl_factory(pj_pool_factory *pf)
+{
+ struct opensl_aud_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "opensles", 256, 256, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct opensl_aud_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &opensl_op;
+
+ return &f->base;
+}
+
+/* API: Init factory */
+static pj_status_t opensl_init(pjmedia_aud_dev_factory *f)
+{
+ struct opensl_aud_factory *pa = (struct opensl_aud_factory*)f;
+ SLresult result;
+
+ /* Create engine */
+ result = slCreateEngine(&pa->engineObject, 0, NULL, 0, NULL, NULL);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot create engine %d ", result));
+ return opensl_to_pj_error(result);
+ }
+
+ /* Realize the engine */
+ result = (*pa->engineObject)->Realize(pa->engineObject, SL_BOOLEAN_FALSE);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot realize engine"));
+ opensl_destroy(f);
+ return opensl_to_pj_error(result);
+ }
+
+ /* Get the engine interface, which is needed in order to create
+ * other objects.
+ */
+ result = (*pa->engineObject)->GetInterface(pa->engineObject,
+ SL_IID_ENGINE,
+ &pa->engineEngine);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot get engine interface"));
+ opensl_destroy(f);
+ return opensl_to_pj_error(result);
+ }
+
+ /* Create output mix */
+ result = (*pa->engineEngine)->CreateOutputMix(pa->engineEngine,
+ &pa->outputMixObject,
+ 0, NULL, NULL);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot create output mix"));
+ opensl_destroy(f);
+ return opensl_to_pj_error(result);
+ }
+
+ /* Realize the output mix */
+ result = (*pa->outputMixObject)->Realize(pa->outputMixObject,
+ SL_BOOLEAN_FALSE);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot realize output mix"));
+ opensl_destroy(f);
+ return opensl_to_pj_error(result);
+ }
+
+ PJ_LOG(4,(THIS_FILE, "OpenSL sound library initialized"));
+ return PJ_SUCCESS;
+}
+
+/* API: Destroy factory */
+static pj_status_t opensl_destroy(pjmedia_aud_dev_factory *f)
+{
+ struct opensl_aud_factory *pa = (struct opensl_aud_factory*)f;
+ pj_pool_t *pool;
+
+ PJ_LOG(4,(THIS_FILE, "OpenSL sound library shutting down.."));
+
+ /* Destroy Output Mix object */
+ if (pa->outputMixObject) {
+ (*pa->outputMixObject)->Destroy(pa->outputMixObject);
+ pa->outputMixObject = NULL;
+ }
+
+ /* Destroy engine object, and invalidate all associated interfaces */
+ if (pa->engineObject) {
+ (*pa->engineObject)->Destroy(pa->engineObject);
+ pa->engineObject = NULL;
+ pa->engineEngine = NULL;
+ }
+
+ pool = pa->pool;
+ pa->pool = NULL;
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t opensl_refresh(pjmedia_aud_dev_factory *f)
+{
+ PJ_UNUSED_ARG(f);
+ return PJ_SUCCESS;
+}
+
+/* API: Get device count. */
+static unsigned opensl_get_dev_count(pjmedia_aud_dev_factory *f)
+{
+ PJ_UNUSED_ARG(f);
+ return 1;
+}
+
+/* API: Get device info. */
+static pj_status_t opensl_get_dev_info(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_dev_info *info)
+{
+ PJ_UNUSED_ARG(f);
+
+ pj_bzero(info, sizeof(*info));
+
+ pj_ansi_strcpy(info->name, "OpenSL ES Audio");
+ info->default_samples_per_sec = 8000;
+ info->caps = PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
+ info->input_count = 1;
+ info->output_count = 1;
+
+ return PJ_SUCCESS;
+}
+
+/* API: fill in with default parameter. */
+static pj_status_t opensl_default_param(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_param *param)
+{
+
+ pjmedia_aud_dev_info adi;
+ pj_status_t status;
+
+ status = opensl_get_dev_info(f, index, &adi);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_bzero(param, sizeof(*param));
+ if (adi.input_count && adi.output_count) {
+ param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+ param->rec_id = index;
+ param->play_id = index;
+ } else if (adi.input_count) {
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->rec_id = index;
+ param->play_id = PJMEDIA_AUD_INVALID_DEV;
+ } else if (adi.output_count) {
+ param->dir = PJMEDIA_DIR_PLAYBACK;
+ param->play_id = index;
+ param->rec_id = PJMEDIA_AUD_INVALID_DEV;
+ } else {
+ return PJMEDIA_EAUD_INVDEV;
+ }
+
+ param->clock_rate = adi.default_samples_per_sec;
+ param->channel_count = 1;
+ param->samples_per_frame = adi.default_samples_per_sec * 20 / 1000;
+ param->bits_per_sample = 16;
+ param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
+ param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
+
+ return PJ_SUCCESS;
+}
+
+/* API: create stream */
+static pj_status_t opensl_create_stream(pjmedia_aud_dev_factory *f,
+ const pjmedia_aud_param *param,
+ pjmedia_aud_rec_cb rec_cb,
+ pjmedia_aud_play_cb play_cb,
+ void *user_data,
+ pjmedia_aud_stream **p_aud_strm)
+{
+ /* Audio sink for recorder and audio source for player */
+#ifdef __ANDROID__
+ SLDataLocator_AndroidSimpleBufferQueue loc_bq =
+ { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS };
+#else
+ SLDataLocator_BufferQueue loc_bq =
+ { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS };
+#endif
+ struct opensl_aud_factory *pa = (struct opensl_aud_factory*)f;
+ pj_pool_t *pool;
+ struct opensl_aud_stream *stream;
+ pj_status_t status = PJ_SUCCESS;
+ int i, bufferSize;
+ SLresult result;
+ SLDataFormat_PCM format_pcm;
+
+ /* Only supports for mono channel for now */
+ PJ_ASSERT_RETURN(param->channel_count == 1, PJ_EINVAL);
+ PJ_ASSERT_RETURN(play_cb && rec_cb && p_aud_strm, PJ_EINVAL);
+
+ PJ_LOG(4,(THIS_FILE, "Creating OpenSL stream"));
+
+ pool = pj_pool_create(pa->pf, "openslstrm", 1024, 1024, NULL);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ stream = PJ_POOL_ZALLOC_T(pool, struct opensl_aud_stream);
+ stream->pool = pool;
+ pj_strdup2_with_null(pool, &stream->name, "OpenSL");
+ stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+ pj_memcpy(&stream->param, param, sizeof(*param));
+ stream->user_data = user_data;
+ stream->rec_cb = rec_cb;
+ stream->play_cb = play_cb;
+ bufferSize = param->samples_per_frame * param->bits_per_sample / 8;
+
+ /* Configure audio PCM format */
+ format_pcm.formatType = SL_DATAFORMAT_PCM;
+ format_pcm.numChannels = param->channel_count;
+ /* Here samples per sec should be supported else we will get an error */
+ format_pcm.samplesPerSec = (SLuint32) param->clock_rate * 1000;
+ format_pcm.bitsPerSample = (SLuint16) param->bits_per_sample;
+ format_pcm.containerSize = (SLuint16) param->bits_per_sample;
+ format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
+ format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
+
+ if (stream->dir & PJMEDIA_DIR_PLAYBACK) {
+ /* Audio source */
+ SLDataSource audioSrc = {&loc_bq, &format_pcm};
+ /* Audio sink */
+ SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX,
+ pa->outputMixObject};
+ SLDataSink audioSnk = {&loc_outmix, NULL};
+ /* Audio interface */
+#ifdef __ANDROID__
+ int numIface = 3;
+ const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE,
+ SL_IID_VOLUME,
+ SL_IID_ANDROIDCONFIGURATION};
+ const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
+ SL_BOOLEAN_TRUE};
+ SLAndroidConfigurationItf playerConfig;
+ SLint32 streamType = SL_ANDROID_STREAM_VOICE;
+#else
+ int numIface = 2;
+ const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE,
+ SL_IID_VOLUME};
+ const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+#endif
+
+ /* Create audio player */
+ result = (*pa->engineEngine)->CreateAudioPlayer(pa->engineEngine,
+ &stream->playerObj,
+ &audioSrc, &audioSnk,
+ numIface, ids, req);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot create audio player: %d", result));
+ goto on_error;
+ }
+
+#ifdef __ANDROID__
+ /* Set Android configuration */
+ result = (*stream->playerObj)->GetInterface(stream->playerObj,
+ SL_IID_ANDROIDCONFIGURATION,
+ &playerConfig);
+ if (result == SL_RESULT_SUCCESS && playerConfig) {
+ result = (*playerConfig)->SetConfiguration(
+ playerConfig, SL_ANDROID_KEY_STREAM_TYPE,
+ &streamType, sizeof(SLint32));
+ }
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(4, (THIS_FILE, "Warning: Unable to set android "
+ "player configuration"));
+ }
+#endif
+
+ /* Realize the player */
+ result = (*stream->playerObj)->Realize(stream->playerObj,
+ SL_BOOLEAN_FALSE);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot realize player : %d", result));
+ goto on_error;
+ }
+
+ /* Get the play interface */
+ result = (*stream->playerObj)->GetInterface(stream->playerObj,
+ SL_IID_PLAY,
+ &stream->playerPlay);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot get play interface"));
+ goto on_error;
+ }
+
+ /* Get the buffer queue interface */
+ result = (*stream->playerObj)->GetInterface(stream->playerObj,
+ SL_IID_BUFFERQUEUE,
+ &stream->playerBufQ);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot get buffer queue interface"));
+ goto on_error;
+ }
+
+ /* Get the volume interface */
+ result = (*stream->playerObj)->GetInterface(stream->playerObj,
+ SL_IID_VOLUME,
+ &stream->playerVol);
+
+ /* Register callback on the buffer queue */
+ result = (*stream->playerBufQ)->RegisterCallback(stream->playerBufQ,
+ bqPlayerCallback,
+ (void *)stream);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot register player callback"));
+ goto on_error;
+ }
+
+ stream->playerBufferSize = bufferSize;
+ for (i = 0; i < NUM_BUFFERS; i++) {
+ stream->playerBuffer[i] = (char *)
+ pj_pool_alloc(stream->pool,
+ stream->playerBufferSize);
+ }
+ }
+
+ if (stream->dir & PJMEDIA_DIR_CAPTURE) {
+ /* Audio source */
+ SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE,
+ SL_IODEVICE_AUDIOINPUT,
+ SL_DEFAULTDEVICEID_AUDIOINPUT,
+ NULL};
+ SLDataSource audioSrc = {&loc_dev, NULL};
+ /* Audio sink */
+ SLDataSink audioSnk = {&loc_bq, &format_pcm};
+ /* Audio interface */
+#ifdef __ANDROID__
+ int numIface = 2;
+ const SLInterfaceID ids[2] = {W_SL_IID_BUFFERQUEUE,
+ SL_IID_ANDROIDCONFIGURATION};
+ const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+ SLAndroidConfigurationItf recorderConfig;
+#else
+ int numIface = 1;
+ const SLInterfaceID ids[1] = {W_SL_IID_BUFFERQUEUE};
+ const SLboolean req[1] = {SL_BOOLEAN_TRUE};
+#endif
+
+ /* Create audio recorder
+ * (requires the RECORD_AUDIO permission)
+ */
+ result = (*pa->engineEngine)->CreateAudioRecorder(pa->engineEngine,
+ &stream->recordObj,
+ &audioSrc, &audioSnk,
+ numIface, ids, req);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot create recorder: %d", result));
+ goto on_error;
+ }
+
+#ifdef __ANDROID__
+ /* Set Android configuration */
+ result = (*stream->recordObj)->GetInterface(stream->recordObj,
+ SL_IID_ANDROIDCONFIGURATION,
+ &recorderConfig);
+ if (result == SL_RESULT_SUCCESS) {
+ SLint32 streamType = SL_ANDROID_RECORDING_PRESET_GENERIC;
+#if __ANDROID_API__ >= 14
+ char sdk_version[PROP_VALUE_MAX];
+ pj_str_t pj_sdk_version;
+ int sdk_v;
+
+ __system_property_get("ro.build.version.sdk", sdk_version);
+ pj_sdk_version = pj_str(sdk_version);
+ sdk_v = pj_strtoul(&pj_sdk_version);
+ if (sdk_v >= 14)
+ streamType = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
+ PJ_LOG(4, (THIS_FILE, "Recording stream type %d, SDK : %d",
+ streamType, sdk_v));
+#endif
+ result = (*recorderConfig)->SetConfiguration(
+ recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET,
+ &streamType, sizeof(SLint32));
+ }
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(4, (THIS_FILE, "Warning: Unable to set android "
+ "recorder configuration"));
+ }
+#endif
+
+ /* Realize the recorder */
+ result = (*stream->recordObj)->Realize(stream->recordObj,
+ SL_BOOLEAN_FALSE);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot realize recorder : %d", result));
+ goto on_error;
+ }
+
+ /* Get the record interface */
+ result = (*stream->recordObj)->GetInterface(stream->recordObj,
+ SL_IID_RECORD,
+ &stream->recordRecord);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot get record interface"));
+ goto on_error;
+ }
+
+ /* Get the buffer queue interface */
+ result = (*stream->recordObj)->GetInterface(
+ stream->recordObj, W_SL_IID_BUFFERQUEUE,
+ &stream->recordBufQ);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot get recorder buffer queue iface"));
+ goto on_error;
+ }
+
+ /* Register callback on the buffer queue */
+ result = (*stream->recordBufQ)->RegisterCallback(stream->recordBufQ,
+ bqRecorderCallback,
+ (void *) stream);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot register recorder callback"));
+ goto on_error;
+ }
+
+ stream->recordBufferSize = bufferSize;
+ for (i = 0; i < NUM_BUFFERS; i++) {
+ stream->recordBuffer[i] = (char *)
+ pj_pool_alloc(stream->pool,
+ stream->recordBufferSize);
+ }
+
+ }
+
+ if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
+ strm_set_cap(&stream->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ ¶m->output_vol);
+ }
+
+ /* Done */
+ stream->base.op = &opensl_strm_op;
+ *p_aud_strm = &stream->base;
+ return PJ_SUCCESS;
+
+on_error:
+ strm_destroy(&stream->base);
+ return status;
+}
+
+/* API: Get stream parameters */
+static pj_status_t strm_get_param(pjmedia_aud_stream *s,
+ pjmedia_aud_param *pi)
+{
+ struct opensl_aud_stream *strm = (struct opensl_aud_stream*)s;
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ if (strm_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ &pi->output_vol) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t strm_get_cap(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ void *pval)
+{
+ struct opensl_aud_stream *strm = (struct opensl_aud_stream*)s;
+ pj_status_t status = PJMEDIA_EAUD_INVCAP;
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
+ (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
+ {
+ if (strm->playerVol) {
+ SLresult res;
+ SLmillibel vol, mvol;
+
+ res = (*strm->playerVol)->GetMaxVolumeLevel(strm->playerVol,
+ &mvol);
+ if (res == SL_RESULT_SUCCESS) {
+ res = (*strm->playerVol)->GetVolumeLevel(strm->playerVol,
+ &vol);
+ if (res == SL_RESULT_SUCCESS) {
+ *(int *)pval = ((int)vol - SL_MILLIBEL_MIN) * 100 /
+ ((int)mvol - SL_MILLIBEL_MIN);
+ return PJ_SUCCESS;
+ }
+ }
+ }
+ }
+
+ return status;
+}
+
+/* API: set capability */
+static pj_status_t strm_set_cap(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ const void *value)
+{
+ struct opensl_aud_stream *strm = (struct opensl_aud_stream*)s;
+
+ PJ_ASSERT_RETURN(s && value, PJ_EINVAL);
+
+ if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
+ (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
+ {
+ if (strm->playerVol) {
+ SLresult res;
+ SLmillibel vol, mvol;
+
+ res = (*strm->playerVol)->GetMaxVolumeLevel(strm->playerVol,
+ &mvol);
+ if (res == SL_RESULT_SUCCESS) {
+ vol = (SLmillibel)(*(int *)value *
+ ((int)mvol - SL_MILLIBEL_MIN) / 100 + SL_MILLIBEL_MIN);
+ res = (*strm->playerVol)->SetVolumeLevel(strm->playerVol,
+ vol);
+ if (res == SL_RESULT_SUCCESS)
+ return PJ_SUCCESS;
+ }
+ }
+ }
+
+ return PJMEDIA_EAUD_INVCAP;
+}
+
+/* API: start stream. */
+static pj_status_t strm_start(pjmedia_aud_stream *s)
+{
+ struct opensl_aud_stream *stream = (struct opensl_aud_stream*)s;
+ int i;
+ SLresult result = SL_RESULT_SUCCESS;
+
+ PJ_LOG(4, (THIS_FILE, "Starting %s stream..", stream->name.ptr));
+ stream->quit_flag = 0;
+
+ if (stream->recordBufQ && stream->recordRecord) {
+ /* Enqueue an empty buffer to be filled by the recorder
+ * (for streaming recording, we need to enqueue at least 2 empty
+ * buffers to start things off)
+ */
+ for (i = 0; i < NUM_BUFFERS; i++) {
+ result = (*stream->recordBufQ)->Enqueue(stream->recordBufQ,
+ stream->recordBuffer[i],
+ stream->recordBufferSize);
+ /* The most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
+ * which for this code would indicate a programming error
+ */
+ pj_assert(result == SL_RESULT_SUCCESS);
+ }
+
+ result = (*stream->recordRecord)->SetRecordState(
+ stream->recordRecord, SL_RECORDSTATE_RECORDING);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot start recorder"));
+ goto on_error;
+ }
+ }
+
+ if (stream->playerPlay && stream->playerBufQ) {
+ /* Set the player's state to playing */
+ result = (*stream->playerPlay)->SetPlayState(stream->playerPlay,
+ SL_PLAYSTATE_PLAYING);
+ if (result != SL_RESULT_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Cannot start player"));
+ goto on_error;
+ }
+
+ for (i = 0; i < NUM_BUFFERS; i++) {
+ pj_bzero(stream->playerBuffer[i], stream->playerBufferSize/100);
+ result = (*stream->playerBufQ)->Enqueue(stream->playerBufQ,
+ stream->playerBuffer[i],
+ stream->playerBufferSize/100);
+ pj_assert(result == SL_RESULT_SUCCESS);
+ }
+ }
+
+ PJ_LOG(4, (THIS_FILE, "%s stream started", stream->name.ptr));
+ return PJ_SUCCESS;
+
+on_error:
+ if (result != SL_RESULT_SUCCESS)
+ strm_stop(&stream->base);
+ return opensl_to_pj_error(result);
+}
+
+/* API: stop stream. */
+static pj_status_t strm_stop(pjmedia_aud_stream *s)
+{
+ struct opensl_aud_stream *stream = (struct opensl_aud_stream*)s;
+
+ if (stream->quit_flag)
+ return PJ_SUCCESS;
+
+ PJ_LOG(4, (THIS_FILE, "Stopping stream"));
+
+ stream->quit_flag = 1;
+
+ if (stream->recordBufQ && stream->recordRecord) {
+ /* Stop recording and clear buffer queue */
+ (*stream->recordRecord)->SetRecordState(stream->recordRecord,
+ SL_RECORDSTATE_STOPPED);
+ (*stream->recordBufQ)->Clear(stream->recordBufQ);
+ }
+
+ if (stream->playerBufQ && stream->playerPlay) {
+ /* Wait until the PCM data is done playing, the buffer queue callback
+ * will continue to queue buffers until the entire PCM data has been
+ * played. This is indicated by waiting for the count member of the
+ * SLBufferQueueState to go to zero.
+ */
+/*
+ SLresult result;
+ W_SLBufferQueueState state;
+
+ result = (*stream->playerBufQ)->GetState(stream->playerBufQ, &state);
+ while (state.count) {
+ (*stream->playerBufQ)->GetState(stream->playerBufQ, &state);
+ } */
+ /* Stop player */
+ (*stream->playerPlay)->SetPlayState(stream->playerPlay,
+ SL_PLAYSTATE_STOPPED);
+ }
+
+ PJ_LOG(4,(THIS_FILE, "OpenSL stream stopped"));
+
+ return PJ_SUCCESS;
+
+}
+
+/* API: destroy stream. */
+static pj_status_t strm_destroy(pjmedia_aud_stream *s)
+{
+ struct opensl_aud_stream *stream = (struct opensl_aud_stream*)s;
+
+ /* Stop the stream */
+ strm_stop(s);
+
+ if (stream->playerObj) {
+ /* Destroy the player */
+ (*stream->playerObj)->Destroy(stream->playerObj);
+ /* Invalidate all associated interfaces */
+ stream->playerObj = NULL;
+ stream->playerPlay = NULL;
+ stream->playerBufQ = NULL;
+ stream->playerVol = NULL;
+ }
+
+ if (stream->recordObj) {
+ /* Destroy the recorder */
+ (*stream->recordObj)->Destroy(stream->recordObj);
+ /* Invalidate all associated interfaces */
+ stream->recordObj = NULL;
+ stream->recordRecord = NULL;
+ stream->recordBufQ = NULL;
+ }
+
+ pj_pool_release(stream->pool);
+ PJ_LOG(4, (THIS_FILE, "OpenSL stream destroyed"));
+
+ return PJ_SUCCESS;
+}
+
+#endif /* PJMEDIA_AUDIO_DEV_HAS_OPENSL */
diff --git a/jni/pjproject-android/.svn/pristine/f8/f8d0402ec5efa72af43f4300ac3f14c2d39293aa.svn-base b/jni/pjproject-android/.svn/pristine/f8/f8d0402ec5efa72af43f4300ac3f14c2d39293aa.svn-base
new file mode 100644
index 0000000..1e5f317
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f8d0402ec5efa72af43f4300ac3f14c2d39293aa.svn-base
@@ -0,0 +1 @@
+#include "../../../portaudio/src/common/pa_debugprint.h"
diff --git a/jni/pjproject-android/.svn/pristine/f8/f8d5c8965caf5988a19007da07c0ee5f6b2aef82.svn-base b/jni/pjproject-android/.svn/pristine/f8/f8d5c8965caf5988a19007da07c0ee5f6b2aef82.svn-base
new file mode 100644
index 0000000..3e482af
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/f8/f8d5c8965caf5988a19007da07c0ee5f6b2aef82.svn-base
@@ -0,0 +1,92 @@
+EXPORTS
+ pj_cis_add_alpha @ 1 NONAME
+ pj_cis_add_cis @ 2 NONAME
+ pj_cis_add_num @ 3 NONAME
+ pj_cis_add_range @ 4 NONAME
+ pj_cis_add_str @ 5 NONAME
+ pj_cis_buf_init @ 6 NONAME
+ pj_cis_del_range @ 7 NONAME
+ pj_cis_del_str @ 8 NONAME
+ pj_cis_dup @ 9 NONAME
+ pj_cis_init @ 10 NONAME
+ pj_cis_invert @ 11 NONAME
+ pj_crc32_calc @ 12 NONAME
+ pj_crc32_final @ 13 NONAME
+ pj_crc32_init @ 14 NONAME
+ pj_crc32_update @ 15 NONAME
+ pj_dns_dump_packet @ 16 NONAME
+ pj_dns_get_type_name @ 17 NONAME
+ pj_dns_make_query @ 18 NONAME
+ pj_dns_packet_dup @ 19 NONAME
+ pj_dns_parse_a_response @ 20 NONAME
+ pj_dns_parse_packet @ 21 NONAME
+ pj_dns_resolver_add_entry @ 22 NONAME
+ pj_dns_resolver_cancel_query @ 23 NONAME
+ pj_dns_resolver_create @ 24 NONAME
+ pj_dns_resolver_destroy @ 25 NONAME
+ pj_dns_resolver_dump @ 26 NONAME
+ pj_dns_resolver_get_cached_count @ 27 NONAME
+ pj_dns_resolver_get_settings @ 28 NONAME
+ pj_dns_resolver_handle_events @ 29 NONAME
+ pj_dns_resolver_set_ns @ 30 NONAME
+ pj_dns_resolver_set_settings @ 31 NONAME
+ pj_dns_resolver_start_query @ 32 NONAME
+ pj_dns_settings_default @ 33 NONAME
+ pj_dns_srv_resolve @ 34 NONAME
+ pj_hmac_md5 @ 35 NONAME
+ pj_hmac_md5_final @ 36 NONAME
+ pj_hmac_md5_init @ 37 NONAME
+ pj_hmac_md5_update @ 38 NONAME
+ pj_hmac_sha1 @ 39 NONAME
+ pj_hmac_sha1_final @ 40 NONAME
+ pj_hmac_sha1_init @ 41 NONAME
+ pj_hmac_sha1_update @ 42 NONAME
+ pj_md5_final @ 43 NONAME
+ pj_md5_init @ 44 NONAME
+ pj_md5_update @ 45 NONAME
+ pj_scan_advance_n @ 46 NONAME
+ pj_scan_fini @ 47 NONAME
+ pj_scan_get @ 48 NONAME
+ pj_scan_get_char @ 49 NONAME
+ pj_scan_get_n @ 50 NONAME
+ pj_scan_get_newline @ 51 NONAME
+ pj_scan_get_quote @ 52 NONAME
+ pj_scan_get_quotes @ 53 NONAME
+ pj_scan_get_unescape @ 54 NONAME
+ pj_scan_get_until @ 55 NONAME
+ pj_scan_get_until_ch @ 56 NONAME
+ pj_scan_get_until_chr @ 57 NONAME
+ pj_scan_init @ 58 NONAME
+ pj_scan_peek @ 59 NONAME
+ pj_scan_peek_n @ 60 NONAME
+ pj_scan_peek_until @ 61 NONAME
+ pj_scan_restore_state @ 62 NONAME
+ pj_scan_save_state @ 63 NONAME
+ pj_scan_skip_line @ 64 NONAME
+ pj_scan_skip_whitespace @ 65 NONAME
+ pj_scan_strcmp @ 66 NONAME
+ pj_scan_stricmp @ 67 NONAME
+ pj_scan_stricmp_alnum @ 68 NONAME
+ pj_sha1_final @ 69 NONAME
+ pj_sha1_init @ 70 NONAME
+ pj_sha1_update @ 71 NONAME
+ pj_str_unescape @ 72 NONAME
+ pj_strcpy_unescape @ 73 NONAME
+ pj_strncpy2_escape @ 74 NONAME
+ pj_strncpy_escape @ 75 NONAME
+ pj_xml_add_attr @ 76 NONAME
+ pj_xml_add_node @ 77 NONAME
+ pj_xml_attr_new @ 78 NONAME
+ pj_xml_clone @ 79 NONAME
+ pj_xml_find @ 80 NONAME
+ pj_xml_find_attr @ 81 NONAME
+ pj_xml_find_next_node @ 82 NONAME
+ pj_xml_find_node @ 83 NONAME
+ pj_xml_node_new @ 84 NONAME
+ pj_xml_parse @ 85 NONAME
+ pj_xml_print @ 86 NONAME
+ pjlib_util_init @ 87 NONAME
+ pjstun_create_bind_req @ 88 NONAME
+ pjstun_get_mapped_addr @ 89 NONAME
+ pjstun_msg_find_attr @ 90 NONAME
+ pjstun_parse_msg @ 91 NONAME