* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/c8/c848a87a144cbcfa440fbb078b9cf1daef599bca.svn-base b/jni/pjproject-android/.svn/pristine/c8/c848a87a144cbcfa440fbb078b9cf1daef599bca.svn-base
new file mode 100644
index 0000000..7cc9920
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c848a87a144cbcfa440fbb078b9cf1daef599bca.svn-base
@@ -0,0 +1,63 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+#include <pj/string.h>
+
+#if defined(PJ_SUNOS) && PJ_SUNOS!=0
+#include <signal.h>
+static void init_signals()
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_IGN;
+
+ sigaction(SIGALRM, &act, NULL);
+}
+
+#else
+#define init_signals()
+#endif
+
+#define boost()
+
+int main(int argc, char *argv[])
+{
+ int rc;
+
+ PJ_UNUSED_ARG(argc);
+ PJ_UNUSED_ARG(argv);
+
+ boost();
+ init_signals();
+
+ rc = test_main();
+
+ if (argc==2 && pj_ansi_strcmp(argv[1], "-i")==0) {
+ char s[10];
+
+ puts("Press ENTER to quit");
+ if (fgets(s, sizeof(s), stdin) == NULL)
+ return rc;
+ }
+
+ return rc;
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8500ba78b4fc9de1d5e3abc6f3ab02a63676a9e.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8500ba78b4fc9de1d5e3abc6f3ab02a63676a9e.svn-base
new file mode 100644
index 0000000..cefec10
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8500ba78b4fc9de1d5e3abc6f3ab02a63676a9e.svn-base
@@ -0,0 +1,43 @@
+/* $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 <pj/log.h>
+
+/**
+ * \page page_pjlib_samples_log_c Example: Log, Hello World
+ *
+ * Very simple program to write log.
+ *
+ * \includelineno pjlib-samples/log.c
+ */
+
+int main()
+{
+ pj_status_t rc;
+
+ // Error handling omited for clarity
+
+ // Must initialize PJLIB first!
+ rc = pj_init();
+
+ PJ_LOG(3, ("main.c", "Hello world!"));
+
+ return 0;
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c86c1c59d11fb99c125c5d36b8e477fe51a05ae2.svn-base b/jni/pjproject-android/.svn/pristine/c8/c86c1c59d11fb99c125c5d36b8e477fe51a05ae2.svn-base
new file mode 100644
index 0000000..5b68ead
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c86c1c59d11fb99c125c5d36b8e477fe51a05ae2.svn-base
@@ -0,0 +1,117 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010 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
+ */
+#ifndef __PJLIB_UTIL_CLI_CONSOLE_H__
+#define __PJLIB_UTIL_CLI_CONSOLE_H__
+
+/**
+ * @file cli_console.h
+ * @brief Command Line Interface Console Front End API
+ */
+
+#include <pjlib-util/cli_imp.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @ingroup PJLIB_UTIL_CLI_IMP
+ * @{
+ *
+ */
+
+
+/**
+ * This structure contains various options for CLI console front-end.
+ * Application must call pj_cli_console_cfg_default() to initialize
+ * this structure with its default values.
+ */
+typedef struct pj_cli_console_cfg
+{
+ /**
+ * Default log verbosity level for the session.
+ *
+ * Default value: PJ_CLI_CONSOLE_LOG_LEVEL
+ */
+ int log_level;
+
+ /**
+ * Specify text message as a prompt string to user.
+ *
+ * Default: empty
+ */
+ pj_str_t prompt_str;
+
+ /**
+ * Specify the command to quit the application.
+ *
+ * Default: empty
+ */
+ pj_str_t quit_command;
+
+} pj_cli_console_cfg;
+
+
+/**
+ * Initialize pj_cli_console_cfg with its default values.
+ *
+ * @param param The structure to be initialized.
+ */
+PJ_DECL(void) pj_cli_console_cfg_default(pj_cli_console_cfg *param);
+
+
+/**
+ * Create a console front-end for the specified CLI application, and return
+ * the only session instance for the console front end. On Windows operating
+ * system, this may open a new console window.
+ *
+ * @param cli The CLI application instance.
+ * @param param Optional console CLI parameters. If this value is
+ * NULL, default parameters will be used.
+ * @param p_sess Pointer to receive the session instance for the
+ * console front end.
+ * @param p_fe Optional pointer to receive the front-end instance
+ * of the console front-end just created.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_cli_console_create(pj_cli_t *cli,
+ const pj_cli_console_cfg *param,
+ pj_cli_sess **p_sess,
+ pj_cli_front_end **p_fe);
+
+/**
+ * Retrieve a cmdline from console stdin and process the input accordingly.
+ *
+ * @param sess The CLI session.
+ * @param buf Pointer to receive the buffer.
+ * @param maxlen Maximum length to read.
+ *
+ * @return PJ_SUCCESS if an input was read
+ */
+PJ_DECL(pj_status_t) pj_cli_console_process(pj_cli_sess *sess,
+ char *buf,
+ unsigned maxlen);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJLIB_UTIL_CLI_CONSOLE_H__ */
diff --git a/jni/pjproject-android/.svn/pristine/c8/c87a184e8b8dfa874bd63777fe622b76e18d31b2.svn-base b/jni/pjproject-android/.svn/pristine/c8/c87a184e8b8dfa874bd63777fe622b76e18d31b2.svn-base
new file mode 100644
index 0000000..4dd73e0
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c87a184e8b8dfa874bd63777fe622b76e18d31b2.svn-base
@@ -0,0 +1,36 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+"""
+
+# RFC 4028 Section 9:
+# If the incoming request contains a Supported header field with a
+# value 'timer' but does not contain a Session-Expires header, it means
+# that the UAS is indicating support for timers but is not requesting
+# one. The UAS may request a session timer in the 2XX response by
+# including a Session-Expires header field. The value MUST NOT be set
+# to a duration lower than the value in the Min-SE header field in the
+# request, if it is present.
+
+pjsua_args = "--null-audio --auto-answer 200 --use-timer 2 --timer-min-se 90 --timer-se 1800"
+extra_headers = "Supported: timer\n"
+include = ["Session-Expires: .*;refresher=.*"]
+exclude = []
+sendto_cfg = sip.SendtoCfg("Session Timer initiated by UAS", pjsua_args, sdp, 200,
+ extra_headers=extra_headers,
+ resp_inc=include, resp_exc=exclude)
+
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8b3d783969c6a925a1d8af5b2844c354e6f046c.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8b3d783969c6a925a1d8af5b2844c354e6f046c.svn-base
new file mode 100644
index 0000000..70c8b68
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8b3d783969c6a925a1d8af5b2844c354e6f046c.svn-base
@@ -0,0 +1,15 @@
+@ECHO OFF
+
+REM Bldmake-generated batch file - ABLD.BAT
+REM ** DO NOT EDIT **
+
+perl -S ABLD.PL "\Users\Teluu\pjsip\trunk\pjsip-apps\src\pjsua\symbian\group\\" %1 %2 %3 %4 %5 %6 %7 %8 %9
+if errorlevel==1 goto CheckPerl
+goto End
+
+:CheckPerl
+perl -v >NUL
+if errorlevel==1 echo Is Perl, version 5.003_07 or later, installed?
+goto End
+
+:End
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8ba373748c0a69fd1014b41ac12639cbcde1c13.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8ba373748c0a69fd1014b41ac12639cbcde1c13.svn-base
new file mode 100644
index 0000000..df6155a
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8ba373748c0a69fd1014b41ac12639cbcde1c13.svn-base
@@ -0,0 +1,370 @@
+/* $Id$ */
+/*
+ * 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_TIMER_H__
+#define __PJ_TIMER_H__
+
+/**
+ * @file timer.h
+ * @brief Timer Heap
+ */
+
+#include <pj/types.h>
+#include <pj/lock.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_TIMER Timer Heap Management.
+ * @ingroup PJ_MISC
+ * @brief
+ * The timer scheduling implementation here is based on ACE library's
+ * ACE_Timer_Heap, with only little modification to suit our library's style
+ * (I even left most of the comments in the original source).
+ *
+ * To quote the original quote in ACE_Timer_Heap_T class:
+ *
+ * This implementation uses a heap-based callout queue of
+ * absolute times. Therefore, in the average and worst case,
+ * scheduling, canceling, and expiring timers is O(log N) (where
+ * N is the total number of timers). In addition, we can also
+ * preallocate as many \a ACE_Timer_Nodes as there are slots in
+ * the heap. This allows us to completely remove the need for
+ * dynamic memory allocation, which is important for real-time
+ * systems.
+ *
+ * You can find the fine ACE library at:
+ * http://www.cs.wustl.edu/~schmidt/ACE.html
+ *
+ * ACE is Copyright (C)1993-2006 Douglas C. Schmidt <d.schmidt@vanderbilt.edu>
+ *
+ * @{
+ *
+ * \section pj_timer_examples_sec Examples
+ *
+ * For some examples on how to use the timer heap, please see the link below.
+ *
+ * - \ref page_pjlib_timer_test
+ */
+
+
+/**
+ * The type for internal timer ID.
+ */
+typedef int pj_timer_id_t;
+
+/**
+ * Forward declaration for pj_timer_entry.
+ */
+struct pj_timer_entry;
+
+/**
+ * The type of callback function to be called by timer scheduler when a timer
+ * has expired.
+ *
+ * @param timer_heap The timer heap.
+ * @param entry Timer entry which timer's has expired.
+ */
+typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry);
+
+
+/**
+ * This structure represents an entry to the timer.
+ */
+typedef struct pj_timer_entry
+{
+ /**
+ * User data to be associated with this entry.
+ * Applications normally will put the instance of object that
+ * owns the timer entry in this field.
+ */
+ void *user_data;
+
+ /**
+ * Arbitrary ID assigned by the user/owner of this entry.
+ * Applications can use this ID to distinguish multiple
+ * timer entries that share the same callback and user_data.
+ */
+ int id;
+
+ /**
+ * Callback to be called when the timer expires.
+ */
+ pj_timer_heap_callback *cb;
+
+ /**
+ * Internal unique timer ID, which is assigned by the timer heap.
+ * Application should not touch this ID.
+ */
+ pj_timer_id_t _timer_id;
+
+ /**
+ * The future time when the timer expires, which the value is updated
+ * by timer heap when the timer is scheduled.
+ */
+ pj_time_val _timer_value;
+
+ /**
+ * Internal: the group lock used by this entry, set when
+ * pj_timer_heap_schedule_w_lock() is used.
+ */
+ pj_grp_lock_t *_grp_lock;
+
+#if PJ_TIMER_DEBUG
+ const char *src_file;
+ int src_line;
+#endif
+} pj_timer_entry;
+
+
+/**
+ * Calculate memory size required to create a timer heap.
+ *
+ * @param count Number of timer entries to be supported.
+ * @return Memory size requirement in bytes.
+ */
+PJ_DECL(pj_size_t) pj_timer_heap_mem_size(pj_size_t count);
+
+/**
+ * Create a timer heap.
+ *
+ * @param pool The pool where allocations in the timer heap will be
+ * allocated. The timer heap will dynamicly allocate
+ * more storate from the pool if the number of timer
+ * entries registered is more than the size originally
+ * requested when calling this function.
+ * @param count The maximum number of timer entries to be supported
+ * initially. If the application registers more entries
+ * during runtime, then the timer heap will resize.
+ * @param ht Pointer to receive the created timer heap.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
+ pj_size_t count,
+ pj_timer_heap_t **ht);
+
+/**
+ * Destroy the timer heap.
+ *
+ * @param ht The timer heap.
+ */
+PJ_DECL(void) pj_timer_heap_destroy( pj_timer_heap_t *ht );
+
+
+/**
+ * Set lock object to be used by the timer heap. By default, the timer heap
+ * uses dummy synchronization.
+ *
+ * @param ht The timer heap.
+ * @param lock The lock object to be used for synchronization.
+ * @param auto_del If nonzero, the lock object will be destroyed when
+ * the timer heap is destroyed.
+ */
+PJ_DECL(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht,
+ pj_lock_t *lock,
+ pj_bool_t auto_del );
+
+/**
+ * Set maximum number of timed out entries to process in a single poll.
+ *
+ * @param ht The timer heap.
+ * @param count Number of entries.
+ *
+ * @return The old number.
+ */
+PJ_DECL(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
+ unsigned count );
+
+/**
+ * Initialize a timer entry. Application should call this function at least
+ * once before scheduling the entry to the timer heap, to properly initialize
+ * the timer entry.
+ *
+ * @param entry The timer entry to be initialized.
+ * @param id Arbitrary ID assigned by the user/owner of this entry.
+ * Applications can use this ID to distinguish multiple
+ * timer entries that share the same callback and user_data.
+ * @param user_data User data to be associated with this entry.
+ * Applications normally will put the instance of object that
+ * owns the timer entry in this field.
+ * @param cb Callback function to be called when the timer elapses.
+ *
+ * @return The timer entry itself.
+ */
+PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+ int id,
+ void *user_data,
+ pj_timer_heap_callback *cb );
+
+/**
+ * Queries whether a timer entry is currently running.
+ *
+ * @param entry The timer entry to query.
+ *
+ * @return PJ_TRUE if the timer is running. PJ_FALSE if not.
+ */
+PJ_DECL(pj_bool_t) pj_timer_entry_running( pj_timer_entry *entry );
+
+/**
+ * Schedule a timer entry which will expire AFTER the specified delay.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be registered.
+ * @param delay The interval to expire.
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+#if PJ_TIMER_DEBUG
+# define pj_timer_heap_schedule(ht,e,d) \
+ pj_timer_heap_schedule_dbg(ht,e,d,__FILE__,__LINE__)
+
+ PJ_DECL(pj_status_t) pj_timer_heap_schedule_dbg( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ const char *src_file,
+ int src_line);
+#else
+PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay);
+#endif /* PJ_TIMER_DEBUG */
+
+/**
+ * Schedule a timer entry which will expire AFTER the specified delay, and
+ * increment the reference counter of the group lock while the timer entry
+ * is active. The group lock reference counter will automatically be released
+ * after the timer callback is called or when the timer is cancelled.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be registered.
+ * @param id_val The value to be set to the "id" field of the timer entry
+ * once the timer is scheduled.
+ * @param delay The interval to expire.
+ * @param grp_lock The group lock.
+ *
+ * @return PJ_SUCCESS, or the appropriate error code.
+ */
+#if PJ_TIMER_DEBUG
+# define pj_timer_heap_schedule_w_grp_lock(ht,e,d,id,g) \
+ pj_timer_heap_schedule_w_grp_lock_dbg(ht,e,d,id,g,__FILE__,__LINE__)
+
+ PJ_DECL(pj_status_t) pj_timer_heap_schedule_w_grp_lock_dbg(
+ pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ int id_val,
+ pj_grp_lock_t *grp_lock,
+ const char *src_file,
+ int src_line);
+#else
+PJ_DECL(pj_status_t) pj_timer_heap_schedule_w_grp_lock(
+ pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ const pj_time_val *delay,
+ int id_val,
+ pj_grp_lock_t *grp_lock);
+#endif /* PJ_TIMER_DEBUG */
+
+
+/**
+ * Cancel a previously registered timer. This will also decrement the
+ * reference counter of the group lock associated with the timer entry,
+ * if the entry was scheduled with one.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be cancelled.
+ * @return The number of timer cancelled, which should be one if the
+ * entry has really been registered, or zero if no timer was
+ * cancelled.
+ */
+PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+ pj_timer_entry *entry);
+
+/**
+ * Cancel only if the previously registered timer is active. This will
+ * also decrement the reference counter of the group lock associated
+ * with the timer entry, if the entry was scheduled with one. In any
+ * case, set the "id" to the specified value.
+ *
+ * @param ht The timer heap.
+ * @param entry The entry to be cancelled.
+ * @param id_val Value to be set to "id"
+ *
+ * @return The number of timer cancelled, which should be one if the
+ * entry has really been registered, or zero if no timer was
+ * cancelled.
+ */
+PJ_DECL(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
+ pj_timer_entry *entry,
+ int id_val);
+
+/**
+ * Get the number of timer entries.
+ *
+ * @param ht The timer heap.
+ * @return The number of timer entries.
+ */
+PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht );
+
+/**
+ * Get the earliest time registered in the timer heap. The timer heap
+ * MUST have at least one timer being scheduled (application should use
+ * #pj_timer_heap_count() before calling this function).
+ *
+ * @param ht The timer heap.
+ * @param timeval The time deadline of the earliest timer entry.
+ *
+ * @return PJ_SUCCESS, or PJ_ENOTFOUND if no entry is scheduled.
+ */
+PJ_DECL(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t *ht,
+ pj_time_val *timeval);
+
+/**
+ * Poll the timer heap, check for expired timers and call the callback for
+ * each of the expired timers.
+ *
+ * Note: polling the timer heap is not necessary in Symbian. Please see
+ * @ref PJ_SYMBIAN_OS for more info.
+ *
+ * @param ht The timer heap.
+ * @param next_delay If this parameter is not NULL, it will be filled up with
+ * the time delay until the next timer elapsed, or
+ * PJ_MAXINT32 in the sec part if no entry exist.
+ *
+ * @return The number of timers expired.
+ */
+PJ_DECL(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
+ pj_time_val *next_delay);
+
+#if PJ_TIMER_DEBUG
+/**
+ * Dump timer heap entries.
+ *
+ * @param ht The timer heap.
+ */
+PJ_DECL(void) pj_timer_heap_dump(pj_timer_heap_t *ht);
+#endif
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJ_TIMER_H__ */
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8bb8cc339f400fd82fc0d272632dd1fdebe81f9.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8bb8cc339f400fd82fc0d272632dd1fdebe81f9.svn-base
new file mode 100644
index 0000000..c54c0c4
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8bb8cc339f400fd82fc0d272632dd1fdebe81f9.svn-base
@@ -0,0 +1,64 @@
+# $Id$
+# Useful constants
+
+
+##########################
+# MENU OUTPUT
+#
+
+
+##########################
+# EVENTS
+#
+
+# Text to expect when there is incoming call
+EVENT_INCOMING_CALL = "Press .* answer"
+
+
+##########################
+# CALL STATES
+#
+
+# Call state is CALLING
+STATE_CALLING = "state.*CALLING"
+# Call state is EARLY
+STATE_EARLY = "state.*EARLY"
+# Call state is CONFIRMED
+STATE_CONFIRMED = "state.*CONFIRMED"
+# Call state is DISCONNECTED
+STATE_DISCONNECTED = "Call .* DISCONNECTED"
+
+# Media call is put on-hold
+MEDIA_HOLD = "Call [0-9]+ media [0-9]+ .*, status is .* hold"
+# Media call is active
+MEDIA_ACTIVE = "Call [0-9]+ media [0-9]+ .*, status is Active"
+#MEDIA_ACTIVE = "Media for call [0-9]+ is active"
+# RX_DTMF
+RX_DTMF = "Incoming DTMF on call [0-9]+: "
+
+##########################
+# MEDIA
+#
+
+# Connecting/disconnecting ports
+MEDIA_CONN_PORT_SUCCESS = "Port \d+ \(.+\) transmitting to port"
+MEDIA_DISCONN_PORT_SUCCESS = "Port \d+ \(.+\) stop transmitting to port"
+
+# Filename to play / record
+MEDIA_PLAY_FILE = "--play-file\s+(\S+)"
+MEDIA_REC_FILE = "--rec-file\s+(\S+)"
+
+##########################
+# MISC
+#
+
+# The command prompt
+PROMPT = ">>>"
+# When pjsua has been destroyed
+DESTROYED = "PJSUA destroyed"
+# Assertion failure
+ASSERT = "Assertion failed"
+# Stdout refresh text
+STDOUT_REFRESH = "XXSTDOUT_REFRESHXX"
+
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8d9de641a1648d486c7069e43d61bc35f39c572.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8d9de641a1648d486c7069e43d61bc35f39c572.svn-base
new file mode 100644
index 0000000..659d3a4
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8d9de641a1648d486c7069e43d61bc35f39c572.svn-base
@@ -0,0 +1,88 @@
+/* $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_CC_MSVC_H__
+#define __PJ_COMPAT_CC_MSVC_H__
+
+/**
+ * @file cc_msvc.h
+ * @brief Describes Microsoft Visual C compiler specifics.
+ */
+
+#ifndef _MSC_VER
+# error "This header file is only for Visual C compiler!"
+#endif
+
+#define PJ_CC_NAME "msvc"
+#define PJ_CC_VER_1 (_MSC_VER/100)
+#define PJ_CC_VER_2 (_MSC_VER%100)
+#define PJ_CC_VER_3 0
+
+/* Disable CRT deprecation warnings. */
+#if PJ_CC_VER_1 >= 8 && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+#if PJ_CC_VER_1 >= 8 && !defined(_CRT_SECURE_NO_WARNINGS)
+# define _CRT_SECURE_NO_WARNINGS
+ /* The above doesn't seem to work, at least on VS2005, so lets use
+ * this construct as well.
+ */
+# pragma warning(disable: 4996)
+#endif
+
+#pragma warning(disable: 4127) // conditional expression is constant
+#pragma warning(disable: 4611) // not wise to mix setjmp with C++
+#pragma warning(disable: 4514) // unref. inline function has been removed
+#ifdef NDEBUG
+# pragma warning(disable: 4702) // unreachable code
+# pragma warning(disable: 4710) // function is not inlined.
+# pragma warning(disable: 4711) // function selected for auto inline expansion
+#endif
+
+#ifdef __cplusplus
+# define PJ_INLINE_SPECIFIER inline
+#else
+# define PJ_INLINE_SPECIFIER static __inline
+#endif
+
+#define PJ_EXPORT_DECL_SPECIFIER __declspec(dllexport)
+#define PJ_EXPORT_DEF_SPECIFIER __declspec(dllexport)
+#define PJ_IMPORT_DECL_SPECIFIER __declspec(dllimport)
+
+#define PJ_THREAD_FUNC
+#define PJ_NORETURN __declspec(noreturn)
+#define PJ_ATTR_NORETURN
+#define PJ_ATTR_MAY_ALIAS
+
+#define PJ_HAS_INT64 1
+
+typedef __int64 pj_int64_t;
+typedef unsigned __int64 pj_uint64_t;
+
+#define PJ_INT64(val) val##i64
+#define PJ_UINT64(val) val##ui64
+#define PJ_INT64_FMT "I64"
+
+#define PJ_UNREACHED(x)
+
+#define PJ_ALIGN_DATA(declaration, alignment) __declspec(align(alignment)) declaration
+
+
+#endif /* __PJ_COMPAT_CC_MSVC_H__ */
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8e190c7112b7d38394ee4eb6e6bba1d51390c76.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8e190c7112b7d38394ee4eb6e6bba1d51390c76.svn-base
new file mode 100644
index 0000000..30725b5
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8e190c7112b7d38394ee4eb6e6bba1d51390c76.svn-base
@@ -0,0 +1,1933 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjsua-lib/pjsua.h>
+#include "pjsua_app_common.h"
+
+#define THIS_FILE "pjsua_app_legacy.c"
+
+static pj_bool_t cmd_echo;
+
+/*
+ * Print buddy list.
+ */
+static void print_buddy_list()
+{
+ pjsua_buddy_id ids[64];
+ int i;
+ unsigned count = PJ_ARRAY_SIZE(ids);
+
+ puts("Buddy list:");
+
+ pjsua_enum_buddies(ids, &count);
+
+ if (count == 0)
+ puts(" -none-");
+ else {
+ for (i=0; i<(int)count; ++i) {
+ pjsua_buddy_info info;
+
+ if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
+ continue;
+
+ printf(" [%2d] <%.*s> %.*s\n",
+ ids[i]+1,
+ (int)info.status_text.slen,
+ info.status_text.ptr,
+ (int)info.uri.slen,
+ info.uri.ptr);
+ }
+ }
+ puts("");
+}
+
+/*
+ * Input URL.
+ */
+static void ui_input_url(const char *title, char *buf, pj_size_t len,
+ input_result *result)
+{
+ result->nb_result = PJSUA_APP_NO_NB;
+ result->uri_result = NULL;
+
+ print_buddy_list();
+
+ printf("Choices:\n"
+ " 0 For current dialog.\n"
+ " -1 All %d buddies in buddy list\n"
+ " [1 -%2d] Select from buddy list\n"
+ " URL An URL\n"
+ " <Enter> Empty input (or 'q') to cancel\n"
+ , pjsua_get_buddy_count(), pjsua_get_buddy_count());
+ printf("%s: ", title);
+
+ fflush(stdout);
+ if (fgets(buf, (int)len, stdin) == NULL)
+ return;
+ len = strlen(buf);
+
+ /* Left trim */
+ while (pj_isspace(*buf)) {
+ ++buf;
+ --len;
+ }
+
+ /* Remove trailing newlines */
+ while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
+ buf[--len] = '\0';
+
+ if (len == 0 || buf[0]=='q')
+ return;
+
+ if (pj_isdigit(*buf) || *buf=='-') {
+
+ unsigned i;
+
+ if (*buf=='-')
+ i = 1;
+ else
+ i = 0;
+
+ for (; i<len; ++i) {
+ if (!pj_isdigit(buf[i])) {
+ puts("Invalid input");
+ return;
+ }
+ }
+
+ result->nb_result = my_atoi(buf);
+
+ if (result->nb_result >= 0 &&
+ result->nb_result <= (int)pjsua_get_buddy_count())
+ {
+ return;
+ }
+ if (result->nb_result == -1)
+ return;
+
+ puts("Invalid input");
+ result->nb_result = PJSUA_APP_NO_NB;
+ return;
+
+ } else {
+ pj_status_t status;
+
+ if ((status=pjsua_verify_url(buf)) != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Invalid URL", status);
+ return;
+ }
+
+ result->uri_result = buf;
+ }
+}
+
+static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
+{
+ char *p;
+
+ printf("%s (empty to cancel): ", title); fflush(stdout);
+ if (fgets(buf, (int)len, stdin) == NULL)
+ return PJ_FALSE;
+
+ /* Remove trailing newlines. */
+ for (p=buf; ; ++p) {
+ if (*p=='\r' || *p=='\n') *p='\0';
+ else if (!*p) break;
+ }
+
+ if (!*buf)
+ return PJ_FALSE;
+
+ return PJ_TRUE;
+}
+
+/*
+ * Print account status.
+ */
+static void print_acc_status(int acc_id)
+{
+ char buf[80];
+ pjsua_acc_info info;
+
+ pjsua_acc_get_info(acc_id, &info);
+
+ if (!info.has_registration) {
+ pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
+ (int)info.status_text.slen,
+ info.status_text.ptr);
+
+ } else {
+ pj_ansi_snprintf(buf, sizeof(buf),
+ "%d/%.*s (expires=%d)",
+ info.status,
+ (int)info.status_text.slen,
+ info.status_text.ptr,
+ info.expires);
+
+ }
+
+ printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
+ acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
+ printf(" Online status: %.*s\n",
+ (int)info.online_status_text.slen,
+ info.online_status_text.ptr);
+}
+
+/*
+ * Show a bit of help.
+ */
+static void keystroke_help()
+{
+ pjsua_acc_id acc_ids[16];
+ unsigned count = PJ_ARRAY_SIZE(acc_ids);
+ int i;
+
+ printf(">>>>\n");
+
+ pjsua_enum_accs(acc_ids, &count);
+
+ printf("Account list:\n");
+ for (i=0; i<(int)count; ++i)
+ print_acc_status(acc_ids[i]);
+
+ print_buddy_list();
+
+ //puts("Commands:");
+ puts("+=============================================================================+");
+ puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
+ puts("| | | |");
+ puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
+ puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
+ puts("| a Answer call | i Send IM | !a Modify accnt. |");
+ puts("| h Hangup call (ha=all) | s Subscribe presence | rr (Re-)register |");
+ puts("| H Hold call | u Unsubscribe presence | ru Unregister |");
+ puts("| v re-inVite (release hold) | t ToGgle Online status | > Cycle next ac.|");
+ puts("| U send UPDATE | T Set online status | < Cycle prev ac.|");
+ puts("| ],[ Select next/prev call +--------------------------+-------------------+");
+ puts("| x Xfer call | Media Commands: | Status & Config: |");
+ puts("| X Xfer with Replaces | | |");
+ puts("| # Send RFC 2833 DTMF | cl List ports | d Dump status |");
+ puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |");
+ puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |");
+ puts("| | V Adjust audio Volume | f Save config |");
+ puts("| S Send arbitrary REQUEST | Cp Codec priorities | |");
+ puts("+-----------------------------------------------------------------------------+");
+#if PJSUA_HAS_VIDEO
+ puts("| Video: \"vid help\" for more info |");
+ puts("+-----------------------------------------------------------------------------+");
+#endif
+ puts("| q QUIT L ReLoad sleep MS echo [0|1|txt] n: detect NAT type |");
+ puts("+=============================================================================+");
+
+ i = pjsua_call_get_count();
+ printf("You have %d active call%s\n", i, (i>1?"s":""));
+
+ if (current_call != PJSUA_INVALID_ID) {
+ pjsua_call_info ci;
+ if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
+ printf("Current call id=%d to %.*s [%.*s]\n", current_call,
+ (int)ci.remote_info.slen, ci.remote_info.ptr,
+ (int)ci.state_text.slen, ci.state_text.ptr);
+ }
+}
+
+/* Help screen for video */
+#if PJSUA_HAS_VIDEO
+static void vid_show_help()
+{
+ pj_bool_t vid_enabled = (app_config.vid.vid_cnt > 0);
+
+ puts("+=============================================================================+");
+ puts("| Video commands: |");
+ puts("| |");
+ puts("| vid help Show this help screen |");
+ puts("| vid enable|disable Enable or disable video in next offer/answer |");
+ puts("| vid acc show Show current account video settings |");
+ puts("| vid acc autorx on|off Automatically show incoming video on/off |");
+ puts("| vid acc autotx on|off Automatically offer video on/off |");
+ puts("| vid acc cap ID Set default capture device for current acc |");
+ puts("| vid acc rend ID Set default renderer device for current acc |");
+ puts("| vid call rx on|off N Enable/disable video RX for stream N in curr call |");
+ puts("| vid call tx on|off N Enable/disable video TX for stream N in curr call |");
+ puts("| vid call add Add video stream for current call |");
+ puts("| vid call enable|disable N Enable/disable stream #N in current call |");
+ puts("| vid call cap N ID Set capture dev ID for stream #N in current call |");
+ puts("| vid dev list List all video devices |");
+ puts("| vid dev refresh Refresh video device list |");
+ puts("| vid dev prev on|off ID Enable/disable preview for specified device ID |");
+ puts("| vid codec list List video codecs |");
+ puts("| vid codec prio ID PRIO Set codec ID priority to PRIO |");
+ puts("| vid codec fps ID NUM DEN Set codec ID framerate to (NUM/DEN) fps |");
+ puts("| vid codec bw ID AVG MAX Set codec ID bitrate to AVG & MAX kbps |");
+ puts("| vid codec size ID W H Set codec ID size/resolution to W x H |");
+ puts("| vid win list List all active video windows |");
+ puts("| vid win arrange Auto arrange windows |");
+ puts("| vid win show|hide ID Show/hide the specified video window ID |");
+ puts("| vid win move ID X Y Move window ID to position X,Y |");
+ puts("| vid win resize ID w h Resize window ID to the specified width, height |");
+ puts("+=============================================================================+");
+ printf("| Video will be %s in the next offer/answer %s |\n",
+ (vid_enabled? "enabled" : "disabled"), (vid_enabled? " " : ""));
+ puts("+=============================================================================+");
+}
+
+static void vid_handle_menu(char *menuin)
+{
+ char *argv[8];
+ int argc = 0;
+
+ /* Tokenize */
+ argv[argc] = strtok(menuin, " \t\r\n");
+ while (argv[argc] && *argv[argc]) {
+ argc++;
+ argv[argc] = strtok(NULL, " \t\r\n");
+ }
+
+ if (argc == 1 || strcmp(argv[1], "help")==0) {
+ vid_show_help();
+ } else if (argc == 2 && (strcmp(argv[1], "enable")==0 ||
+ strcmp(argv[1], "disable")==0))
+ {
+ pj_bool_t enabled = (strcmp(argv[1], "enable")==0);
+ app_config.vid.vid_cnt = (enabled ? 1 : 0);
+ PJ_LOG(3,(THIS_FILE, "Video will be %s in next offer/answer",
+ (enabled?"enabled":"disabled")));
+ } else if (strcmp(argv[1], "acc")==0) {
+ pjsua_acc_config acc_cfg;
+ pj_bool_t changed = PJ_FALSE;
+ pj_pool_t *tmp_pool = pjsua_pool_create("tmp-pjsua", 1000, 1000);
+
+ pjsua_acc_get_config(current_acc, tmp_pool, &acc_cfg);
+
+ if (argc == 3 && strcmp(argv[2], "show")==0) {
+ app_config_show_video(current_acc, &acc_cfg);
+ } else if (argc == 4 && strcmp(argv[2], "autorx")==0) {
+ int on = (strcmp(argv[3], "on")==0);
+ acc_cfg.vid_in_auto_show = on;
+ changed = PJ_TRUE;
+ } else if (argc == 4 && strcmp(argv[2], "autotx")==0) {
+ int on = (strcmp(argv[3], "on")==0);
+ acc_cfg.vid_out_auto_transmit = on;
+ changed = PJ_TRUE;
+ } else if (argc == 4 && strcmp(argv[2], "cap")==0) {
+ int dev = atoi(argv[3]);
+ acc_cfg.vid_cap_dev = dev;
+ changed = PJ_TRUE;
+ } else if (argc == 4 && strcmp(argv[2], "rend")==0) {
+ int dev = atoi(argv[3]);
+ acc_cfg.vid_rend_dev = dev;
+ changed = PJ_TRUE;
+ } else {
+ pj_pool_release(tmp_pool);
+ goto on_error;
+ }
+
+ if (changed) {
+ pj_status_t status = pjsua_acc_modify(current_acc, &acc_cfg);
+ if (status != PJ_SUCCESS)
+ PJ_PERROR(1,(THIS_FILE, status, "Error modifying account %d",
+ current_acc));
+ }
+ pj_pool_release(tmp_pool);
+
+ } else if (strcmp(argv[1], "call")==0) {
+ pjsua_call_vid_strm_op_param param;
+ pj_status_t status = PJ_SUCCESS;
+
+ pjsua_call_vid_strm_op_param_default(¶m);
+
+ if (argc == 5 && strcmp(argv[2], "rx")==0) {
+ pjsua_stream_info si;
+ pj_bool_t on = (strcmp(argv[3], "on") == 0);
+
+ param.med_idx = atoi(argv[4]);
+ if (pjsua_call_get_stream_info(current_call, param.med_idx, &si) ||
+ si.type != PJMEDIA_TYPE_VIDEO)
+ {
+ PJ_PERROR(1,(THIS_FILE, PJ_EINVAL, "Invalid stream"));
+ return;
+ }
+
+ if (on) param.dir = (si.info.vid.dir | PJMEDIA_DIR_DECODING);
+ else param.dir = (si.info.vid.dir & PJMEDIA_DIR_ENCODING);
+
+ status = pjsua_call_set_vid_strm(current_call,
+ PJSUA_CALL_VID_STRM_CHANGE_DIR,
+ ¶m);
+ }
+ else if (argc == 5 && strcmp(argv[2], "tx")==0) {
+ pj_bool_t on = (strcmp(argv[3], "on") == 0);
+ pjsua_call_vid_strm_op op = on? PJSUA_CALL_VID_STRM_START_TRANSMIT :
+ PJSUA_CALL_VID_STRM_STOP_TRANSMIT;
+
+ param.med_idx = atoi(argv[4]);
+
+ status = pjsua_call_set_vid_strm(current_call, op, ¶m);
+ }
+ else if (argc == 3 && strcmp(argv[2], "add")==0) {
+ status = pjsua_call_set_vid_strm(current_call,
+ PJSUA_CALL_VID_STRM_ADD, NULL);
+ }
+ else if (argc >= 3 &&
+ (strcmp(argv[2], "disable")==0 || strcmp(argv[2], "enable")==0))
+ {
+ pj_bool_t enable = (strcmp(argv[2], "enable") == 0);
+ pjsua_call_vid_strm_op op = enable? PJSUA_CALL_VID_STRM_CHANGE_DIR :
+ PJSUA_CALL_VID_STRM_REMOVE;
+
+ param.med_idx = argc >= 4? atoi(argv[3]) : -1;
+ param.dir = PJMEDIA_DIR_ENCODING_DECODING;
+ status = pjsua_call_set_vid_strm(current_call, op, ¶m);
+ }
+ else if (argc >= 3 && strcmp(argv[2], "cap")==0) {
+ param.med_idx = argc >= 4? atoi(argv[3]) : -1;
+ param.cap_dev = argc >= 5? atoi(argv[4]) : PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
+ status = pjsua_call_set_vid_strm(current_call,
+ PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV,
+ ¶m);
+ } else
+ goto on_error;
+
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "Error modifying video stream"));
+ }
+
+ } else if (argc >= 3 && strcmp(argv[1], "dev")==0) {
+ if (strcmp(argv[2], "list")==0) {
+ vid_list_devs();
+ } else if (strcmp(argv[2], "refresh")==0) {
+ pjmedia_vid_dev_refresh();
+ } else if (strcmp(argv[2], "prev")==0) {
+ if (argc != 5) {
+ goto on_error;
+ } else {
+ pj_bool_t on = (strcmp(argv[3], "on") == 0);
+ int dev_id = atoi(argv[4]);
+ if (on) {
+ pjsua_vid_preview_param param;
+
+ pjsua_vid_preview_param_default(¶m);
+ param.wnd_flags = PJMEDIA_VID_DEV_WND_BORDER |
+ PJMEDIA_VID_DEV_WND_RESIZABLE;
+ pjsua_vid_preview_start(dev_id, ¶m);
+ arrange_window(pjsua_vid_preview_get_win(dev_id));
+ } else {
+ pjsua_vid_win_id wid;
+ wid = pjsua_vid_preview_get_win(dev_id);
+ if (wid != PJSUA_INVALID_ID) {
+ /* Preview window hiding once it is stopped is
+ * responsibility of app */
+ pjsua_vid_win_set_show(wid, PJ_FALSE);
+ pjsua_vid_preview_stop(dev_id);
+ }
+ }
+ }
+ } else
+ goto on_error;
+ } else if (strcmp(argv[1], "win")==0) {
+ pj_status_t status = PJ_SUCCESS;
+
+ if (argc==3 && strcmp(argv[2], "list")==0) {
+ pjsua_vid_win_id wids[PJSUA_MAX_VID_WINS];
+ unsigned i, cnt = PJ_ARRAY_SIZE(wids);
+
+ pjsua_vid_enum_wins(wids, &cnt);
+
+ PJ_LOG(3,(THIS_FILE, "Found %d video windows:", cnt));
+ PJ_LOG(3,(THIS_FILE, "WID show pos size"));
+ PJ_LOG(3,(THIS_FILE, "------------------------------"));
+ for (i = 0; i < cnt; ++i) {
+ pjsua_vid_win_info wi;
+ pjsua_vid_win_get_info(wids[i], &wi);
+ PJ_LOG(3,(THIS_FILE, "%3d %c (%d,%d) %dx%d",
+ wids[i], (wi.show?'Y':'N'), wi.pos.x, wi.pos.y,
+ wi.size.w, wi.size.h));
+ }
+ } else if (argc==4 && (strcmp(argv[2], "show")==0 ||
+ strcmp(argv[2], "hide")==0))
+ {
+ pj_bool_t show = (strcmp(argv[2], "show")==0);
+ pjsua_vid_win_id wid = atoi(argv[3]);
+ status = pjsua_vid_win_set_show(wid, show);
+ } else if (argc==6 && strcmp(argv[2], "move")==0) {
+ pjsua_vid_win_id wid = atoi(argv[3]);
+ pjmedia_coord pos;
+
+ pos.x = atoi(argv[4]);
+ pos.y = atoi(argv[5]);
+ status = pjsua_vid_win_set_pos(wid, &pos);
+ } else if (argc==6 && strcmp(argv[2], "resize")==0) {
+ pjsua_vid_win_id wid = atoi(argv[3]);
+ pjmedia_rect_size size;
+
+ size.w = atoi(argv[4]);
+ size.h = atoi(argv[5]);
+ status = pjsua_vid_win_set_size(wid, &size);
+ } else if (argc==3 && strcmp(argv[2], "arrange")==0) {
+ arrange_window(PJSUA_INVALID_ID);
+ } else
+ goto on_error;
+
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "Window operation error"));
+ }
+
+ } else if (strcmp(argv[1], "codec")==0) {
+ pjsua_codec_info ci[PJMEDIA_CODEC_MGR_MAX_CODECS];
+ unsigned count = PJ_ARRAY_SIZE(ci);
+ pj_status_t status;
+
+ if (argc==3 && strcmp(argv[2], "list")==0) {
+ status = pjsua_vid_enum_codecs(ci, &count);
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(1,(THIS_FILE, status, "Error enumerating codecs"));
+ } else {
+ unsigned i;
+ PJ_LOG(3,(THIS_FILE, "Found %d video codecs:", count));
+ PJ_LOG(3,(THIS_FILE, "codec id prio fps bw(kbps) size"));
+ PJ_LOG(3,(THIS_FILE, "------------------------------------------"));
+ for (i=0; i<count; ++i) {
+ pjmedia_vid_codec_param cp;
+ pjmedia_video_format_detail *vfd;
+
+ status = pjsua_vid_codec_get_param(&ci[i].codec_id, &cp);
+ if (status != PJ_SUCCESS)
+ continue;
+
+ vfd = pjmedia_format_get_video_format_detail(&cp.enc_fmt,
+ PJ_TRUE);
+ PJ_LOG(3,(THIS_FILE, "%.*s%.*s %3d %7.2f %4d/%4d %dx%d",
+ (int)ci[i].codec_id.slen, ci[i].codec_id.ptr,
+ 13-(int)ci[i].codec_id.slen, " ",
+ ci[i].priority,
+ (vfd->fps.num*1.0/vfd->fps.denum),
+ vfd->avg_bps/1000, vfd->max_bps/1000,
+ vfd->size.w, vfd->size.h));
+ }
+ }
+ } else if (argc==5 && strcmp(argv[2], "prio")==0) {
+ pj_str_t cid;
+ int prio;
+ cid = pj_str(argv[3]);
+ prio = atoi(argv[4]);
+ status = pjsua_vid_codec_set_priority(&cid, (pj_uint8_t)prio);
+ if (status != PJ_SUCCESS)
+ PJ_PERROR(1,(THIS_FILE, status, "Set codec priority error"));
+ } else if (argc==6 && strcmp(argv[2], "fps")==0) {
+ pjmedia_vid_codec_param cp;
+ pj_str_t cid;
+ int M, N;
+ cid = pj_str(argv[3]);
+ M = atoi(argv[4]);
+ N = atoi(argv[5]);
+ status = pjsua_vid_codec_get_param(&cid, &cp);
+ if (status == PJ_SUCCESS) {
+ cp.enc_fmt.det.vid.fps.num = M;
+ cp.enc_fmt.det.vid.fps.denum = N;
+ status = pjsua_vid_codec_set_param(&cid, &cp);
+ }
+ if (status != PJ_SUCCESS)
+ PJ_PERROR(1,(THIS_FILE, status, "Set codec framerate error"));
+ } else if (argc==6 && strcmp(argv[2], "bw")==0) {
+ pjmedia_vid_codec_param cp;
+ pj_str_t cid;
+ int M, N;
+ cid = pj_str(argv[3]);
+ M = atoi(argv[4]);
+ N = atoi(argv[5]);
+ status = pjsua_vid_codec_get_param(&cid, &cp);
+ if (status == PJ_SUCCESS) {
+ cp.enc_fmt.det.vid.avg_bps = M * 1000;
+ cp.enc_fmt.det.vid.max_bps = N * 1000;
+ status = pjsua_vid_codec_set_param(&cid, &cp);
+ }
+ if (status != PJ_SUCCESS)
+ PJ_PERROR(1,(THIS_FILE, status, "Set codec bitrate error"));
+ } else if (argc==6 && strcmp(argv[2], "size")==0) {
+ pjmedia_vid_codec_param cp;
+ pj_str_t cid;
+ int M, N;
+ cid = pj_str(argv[3]);
+ M = atoi(argv[4]);
+ N = atoi(argv[5]);
+ status = pjsua_vid_codec_get_param(&cid, &cp);
+ if (status == PJ_SUCCESS) {
+ cp.enc_fmt.det.vid.size.w = M;
+ cp.enc_fmt.det.vid.size.h = N;
+ status = pjsua_vid_codec_set_param(&cid, &cp);
+ }
+ if (status != PJ_SUCCESS)
+ PJ_PERROR(1,(THIS_FILE, status, "Set codec size error"));
+ } else
+ goto on_error;
+ } else
+ goto on_error;
+
+ return;
+
+on_error:
+ PJ_LOG(1,(THIS_FILE, "Invalid command, use 'vid help'"));
+}
+
+#endif /* PJSUA_HAS_VIDEO */
+
+/** UI Command **/
+static void ui_make_new_call()
+{
+ char buf[128];
+ pjsua_msg_data msg_data;
+ input_result result;
+ pj_str_t tmp;
+
+ printf("(You currently have %d calls)\n", pjsua_call_get_count());
+
+ ui_input_url("Make call", buf, sizeof(buf), &result);
+ if (result.nb_result != PJSUA_APP_NO_NB) {
+
+ if (result.nb_result == -1 || result.nb_result == 0) {
+ puts("You can't do that with make call!");
+ return;
+ } else {
+ pjsua_buddy_info binfo;
+ pjsua_buddy_get_info(result.nb_result-1, &binfo);
+ tmp.ptr = buf;
+ pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
+ }
+
+ } else if (result.uri_result) {
+ tmp = pj_str(result.uri_result);
+ } else {
+ tmp.slen = 0;
+ }
+
+ pjsua_msg_data_init(&msg_data);
+ TEST_MULTIPART(&msg_data);
+ pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL,
+ &msg_data, ¤t_call);
+}
+
+static void ui_make_multi_call()
+{
+ char menuin[32];
+ int count;
+ char buf[128];
+ input_result result;
+ pj_str_t tmp;
+ int i;
+
+ printf("(You currently have %d calls)\n", pjsua_call_get_count());
+
+ if (!simple_input("Number of calls", menuin, sizeof(menuin)))
+ return;
+
+ count = my_atoi(menuin);
+ if (count < 1)
+ return;
+
+ ui_input_url("Make call", buf, sizeof(buf), &result);
+ if (result.nb_result != PJSUA_APP_NO_NB) {
+ pjsua_buddy_info binfo;
+ if (result.nb_result == -1 || result.nb_result == 0) {
+ puts("You can't do that with make call!");
+ return;
+ }
+ pjsua_buddy_get_info(result.nb_result-1, &binfo);
+ tmp.ptr = buf;
+ pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
+ } else {
+ tmp = pj_str(result.uri_result);
+ }
+
+ for (i=0; i<my_atoi(menuin); ++i) {
+ pj_status_t status;
+
+ status = pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL,
+ NULL, NULL);
+ if (status != PJ_SUCCESS)
+ break;
+ }
+}
+
+static void ui_detect_nat_type()
+{
+ int i = pjsua_detect_nat_type();
+ if (i != PJ_SUCCESS)
+ pjsua_perror(THIS_FILE, "Error", i);
+}
+
+static void ui_send_instant_message()
+{
+ char *uri = NULL;
+ /* i is for call index to send message, if any */
+ int i = -1;
+ input_result result;
+ char buf[128];
+ char text[128];
+ pj_str_t tmp;
+
+ /* Input destination. */
+ ui_input_url("Send IM to", buf, sizeof(buf), &result);
+ if (result.nb_result != PJSUA_APP_NO_NB) {
+
+ if (result.nb_result == -1) {
+ puts("You can't send broadcast IM like that!");
+ return;
+
+ } else if (result.nb_result == 0) {
+ i = current_call;
+ } else {
+ pjsua_buddy_info binfo;
+ pjsua_buddy_get_info(result.nb_result-1, &binfo);
+ tmp.ptr = buf;
+ pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
+ uri = buf;
+ }
+
+ } else if (result.uri_result) {
+ uri = result.uri_result;
+ }
+
+
+ /* Send typing indication. */
+ if (i != -1)
+ pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
+ else {
+ pj_str_t tmp_uri = pj_str(uri);
+ pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
+ }
+
+ /* Input the IM . */
+ if (!simple_input("Message", text, sizeof(text))) {
+ /*
+ * Cancelled.
+ * Send typing notification too, saying we're not typing.
+ */
+ if (i != -1)
+ pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
+ else {
+ pj_str_t tmp_uri = pj_str(uri);
+ pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
+ }
+ return;
+ }
+
+ tmp = pj_str(text);
+
+ /* Send the IM */
+ if (i != -1)
+ pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
+ else {
+ pj_str_t tmp_uri = pj_str(uri);
+ pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
+ }
+}
+
+static void ui_answer_call()
+{
+ pjsua_call_info call_info;
+ char buf[128];
+ pjsua_msg_data msg_data;
+
+ if (current_call != -1) {
+ pjsua_call_get_info(current_call, &call_info);
+ } else {
+ /* Make compiler happy */
+ call_info.role = PJSIP_ROLE_UAC;
+ call_info.state = PJSIP_INV_STATE_DISCONNECTED;
+ }
+
+ if (current_call == -1 ||
+ call_info.role != PJSIP_ROLE_UAS ||
+ call_info.state >= PJSIP_INV_STATE_CONNECTING)
+ {
+ puts("No pending incoming call");
+ fflush(stdout);
+ return;
+
+ } else {
+ int st_code;
+ char contact[120];
+ pj_str_t hname = { "Contact", 7 };
+ pj_str_t hvalue;
+ pjsip_generic_string_hdr hcontact;
+
+ if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
+ return;
+
+ st_code = my_atoi(buf);
+ if (st_code < 100)
+ return;
+
+ pjsua_msg_data_init(&msg_data);
+
+ if (st_code/100 == 3) {
+ if (!simple_input("Enter URL to be put in Contact",
+ contact, sizeof(contact)))
+ return;
+ hvalue = pj_str(contact);
+ pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
+
+ pj_list_push_back(&msg_data.hdr_list, &hcontact);
+ }
+
+ /*
+ * Must check again!
+ * Call may have been disconnected while we're waiting for
+ * keyboard input.
+ */
+ if (current_call == -1) {
+ puts("Call has been disconnected");
+ fflush(stdout);
+ return;
+ }
+
+ pjsua_call_answer2(current_call, &call_opt, st_code, NULL, &msg_data);
+ }
+}
+
+static void ui_hangup_call(char menuin[])
+{
+ if (current_call == -1) {
+ puts("No current call");
+ fflush(stdout);
+ return;
+
+ } else if (menuin[1] == 'a') {
+ /* Hangup all calls */
+ pjsua_call_hangup_all();
+ } else {
+ /* Hangup current calls */
+ pjsua_call_hangup(current_call, 0, NULL, NULL);
+ }
+}
+
+static void ui_cycle_dialog(char menuin[])
+{
+ if (menuin[0] == ']') {
+ find_next_call();
+
+ } else {
+ find_prev_call();
+ }
+
+ if (current_call != -1) {
+ pjsua_call_info call_info;
+
+ pjsua_call_get_info(current_call, &call_info);
+ PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",
+ (int)call_info.remote_info.slen,
+ call_info.remote_info.ptr));
+
+ } else {
+ PJ_LOG(3,(THIS_FILE,"No current dialog"));
+ }
+}
+
+static void ui_cycle_account()
+{
+ int i;
+ char buf[128];
+
+ if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
+ return;
+
+ i = my_atoi(buf);
+ if (pjsua_acc_is_valid(i)) {
+ pjsua_acc_set_default(i);
+ PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
+ } else {
+ PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
+ }
+}
+
+static void ui_add_buddy()
+{
+ char buf[128];
+ pjsua_buddy_config buddy_cfg;
+ pjsua_buddy_id buddy_id;
+ pj_status_t status;
+
+ if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
+ return;
+
+ if (pjsua_verify_url(buf) != PJ_SUCCESS) {
+ printf("Invalid URI '%s'\n", buf);
+ return;
+ }
+
+ pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
+
+ buddy_cfg.uri = pj_str(buf);
+ buddy_cfg.subscribe = PJ_TRUE;
+
+ status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
+ if (status == PJ_SUCCESS) {
+ printf("New buddy '%s' added at index %d\n",
+ buf, buddy_id+1);
+ }
+}
+
+static void ui_add_account(pjsua_transport_config *rtp_cfg)
+{
+ char id[80], registrar[80], realm[80], uname[80], passwd[30];
+ pjsua_acc_config acc_cfg;
+ pj_status_t status;
+
+ if (!simple_input("Your SIP URL:", id, sizeof(id)))
+ return;
+ if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
+ return;
+ if (!simple_input("Auth Realm:", realm, sizeof(realm)))
+ return;
+ if (!simple_input("Auth Username:", uname, sizeof(uname)))
+ return;
+ if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
+ return;
+
+ pjsua_acc_config_default(&acc_cfg);
+ acc_cfg.id = pj_str(id);
+ acc_cfg.reg_uri = pj_str(registrar);
+ acc_cfg.cred_count = 1;
+ acc_cfg.cred_info[0].scheme = pj_str("Digest");
+ acc_cfg.cred_info[0].realm = pj_str(realm);
+ acc_cfg.cred_info[0].username = pj_str(uname);
+ acc_cfg.cred_info[0].data_type = 0;
+ acc_cfg.cred_info[0].data = pj_str(passwd);
+
+ acc_cfg.rtp_cfg = *rtp_cfg;
+ app_config_init_video(&acc_cfg);
+
+ status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error adding new account", status);
+ }
+}
+
+static void ui_delete_buddy()
+{
+ char buf[128];
+ int i;
+
+ if (!simple_input("Enter buddy ID to delete", buf, sizeof(buf)))
+ return;
+
+ i = my_atoi(buf) - 1;
+
+ if (!pjsua_buddy_is_valid(i)) {
+ printf("Invalid buddy id %d\n", i);
+ } else {
+ pjsua_buddy_del(i);
+ printf("Buddy %d deleted\n", i);
+ }
+}
+
+static void ui_delete_account()
+{
+ char buf[128];
+ int i;
+
+ if (!simple_input("Enter account ID to delete", buf, sizeof(buf)))
+ return;
+
+ i = my_atoi(buf);
+
+ if (!pjsua_acc_is_valid(i)) {
+ printf("Invalid account id %d\n", i);
+ } else {
+ pjsua_acc_del(i);
+ printf("Account %d deleted\n", i);
+ }
+}
+
+static void ui_call_hold()
+{
+ if (current_call != -1) {
+ pjsua_call_set_hold(current_call, NULL);
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ }
+}
+
+static void ui_call_reinvite()
+{
+ call_opt.flag |= PJSUA_CALL_UNHOLD;
+ pjsua_call_reinvite2(current_call, &call_opt, NULL);
+}
+
+static void ui_send_update()
+{
+ if (current_call != -1) {
+ pjsua_call_update2(current_call, &call_opt, NULL);
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ }
+}
+
+/*
+ * Change codec priorities.
+ */
+static void ui_manage_codec_prio()
+{
+ pjsua_codec_info c[32];
+ unsigned i, count = PJ_ARRAY_SIZE(c);
+ char input[32];
+ char *codec, *prio;
+ pj_str_t id;
+ int new_prio;
+ pj_status_t status;
+
+ printf("List of audio codecs:\n");
+ pjsua_enum_codecs(c, &count);
+ for (i=0; i<count; ++i) {
+ printf(" %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
+ c[i].codec_id.ptr);
+ }
+
+#if PJSUA_HAS_VIDEO
+ puts("");
+ printf("List of video codecs:\n");
+ pjsua_vid_enum_codecs(c, &count);
+ for (i=0; i<count; ++i) {
+ printf(" %d\t%.*s%s%.*s\n", c[i].priority,
+ (int)c[i].codec_id.slen,
+ c[i].codec_id.ptr,
+ c[i].desc.slen? " - ":"",
+ (int)c[i].desc.slen,
+ c[i].desc.ptr);
+ }
+#endif
+
+ puts("");
+ puts("Enter codec id and its new priority (e.g. \"speex/16000 200\", "
+ """\"H263 200\"),");
+ puts("or empty to cancel.");
+
+ printf("Codec name (\"*\" for all) and priority: ");
+ if (fgets(input, sizeof(input), stdin) == NULL)
+ return;
+ if (input[0]=='\r' || input[0]=='\n') {
+ puts("Done");
+ return;
+ }
+
+ codec = strtok(input, " \t\r\n");
+ prio = strtok(NULL, " \r\n");
+
+ if (!codec || !prio) {
+ puts("Invalid input");
+ return;
+ }
+
+ new_prio = atoi(prio);
+ if (new_prio < 0)
+ new_prio = 0;
+ else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST)
+ new_prio = PJMEDIA_CODEC_PRIO_HIGHEST;
+
+ status = pjsua_codec_set_priority(pj_cstr(&id, codec),
+ (pj_uint8_t)new_prio);
+#if PJSUA_HAS_VIDEO
+ if (status != PJ_SUCCESS) {
+ status = pjsua_vid_codec_set_priority(pj_cstr(&id, codec),
+ (pj_uint8_t)new_prio);
+ }
+#endif
+ if (status != PJ_SUCCESS)
+ pjsua_perror(THIS_FILE, "Error setting codec priority", status);
+}
+
+static void ui_call_transfer(pj_bool_t no_refersub)
+{
+ if (current_call == -1) {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ } else {
+ int call = current_call;
+ char buf[128];
+ pjsip_generic_string_hdr refer_sub;
+ pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
+ pj_str_t STR_FALSE = { "false", 5 };
+ pjsua_call_info ci;
+ input_result result;
+ pjsua_msg_data msg_data;
+
+ pjsua_call_get_info(current_call, &ci);
+ printf("Transfering current call [%d] %.*s\n", current_call,
+ (int)ci.remote_info.slen, ci.remote_info.ptr);
+
+ ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
+
+ /* Check if call is still there. */
+
+ if (call != current_call) {
+ puts("Call has been disconnected");
+ return;
+ }
+
+ pjsua_msg_data_init(&msg_data);
+ if (no_refersub) {
+ /* Add Refer-Sub: false in outgoing REFER request */
+ pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
+ &STR_FALSE);
+ pj_list_push_back(&msg_data.hdr_list, &refer_sub);
+ }
+ if (result.nb_result != PJSUA_APP_NO_NB) {
+ if (result.nb_result == -1 || result.nb_result == 0)
+ puts("You can't do that with transfer call!");
+ else {
+ pjsua_buddy_info binfo;
+ pjsua_buddy_get_info(result.nb_result-1, &binfo);
+ pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
+ }
+
+ } else if (result.uri_result) {
+ pj_str_t tmp;
+ tmp = pj_str(result.uri_result);
+ pjsua_call_xfer( current_call, &tmp, &msg_data);
+ }
+ }
+}
+
+static void ui_call_transfer_replaces(pj_bool_t no_refersub)
+{
+ if (current_call == -1) {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ } else {
+ int call = current_call;
+ int dst_call;
+ pjsip_generic_string_hdr refer_sub;
+ pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
+ pj_str_t STR_FALSE = { "false", 5 };
+ pjsua_call_id ids[PJSUA_MAX_CALLS];
+ pjsua_call_info ci;
+ pjsua_msg_data msg_data;
+ char buf[128];
+ unsigned i, count;
+
+ count = PJ_ARRAY_SIZE(ids);
+ pjsua_enum_calls(ids, &count);
+
+ if (count <= 1) {
+ puts("There are no other calls");
+ return;
+ }
+
+ pjsua_call_get_info(current_call, &ci);
+ printf("Transfer call [%d] %.*s to one of the following:\n",
+ current_call,
+ (int)ci.remote_info.slen, ci.remote_info.ptr);
+
+ for (i=0; i<count; ++i) {
+ pjsua_call_info call_info;
+
+ if (ids[i] == call)
+ return;
+
+ pjsua_call_get_info(ids[i], &call_info);
+ printf("%d %.*s [%.*s]\n",
+ ids[i],
+ (int)call_info.remote_info.slen,
+ call_info.remote_info.ptr,
+ (int)call_info.state_text.slen,
+ call_info.state_text.ptr);
+ }
+
+ if (!simple_input("Enter call number to be replaced", buf, sizeof(buf)))
+ return;
+
+ dst_call = my_atoi(buf);
+
+ /* Check if call is still there. */
+
+ if (call != current_call) {
+ puts("Call has been disconnected");
+ return;
+ }
+
+ /* Check that destination call is valid. */
+ if (dst_call == call) {
+ puts("Destination call number must not be the same "
+ "as the call being transfered");
+ return;
+ }
+ if (dst_call >= PJSUA_MAX_CALLS) {
+ puts("Invalid destination call number");
+ return;
+ }
+ if (!pjsua_call_is_active(dst_call)) {
+ puts("Invalid destination call number");
+ return;
+ }
+
+ pjsua_msg_data_init(&msg_data);
+ if (no_refersub) {
+ /* Add Refer-Sub: false in outgoing REFER request */
+ pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
+ &STR_FALSE);
+ pj_list_push_back(&msg_data.hdr_list, &refer_sub);
+ }
+
+ pjsua_call_xfer_replaces(call, dst_call,
+ PJSUA_XFER_NO_REQUIRE_REPLACES,
+ &msg_data);
+ }
+}
+
+static void ui_send_dtmf_2833()
+{
+ if (current_call == -1) {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ } else if (!pjsua_call_has_media(current_call)) {
+ PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
+ } else {
+ pj_str_t digits;
+ int call = current_call;
+ pj_status_t status;
+ char buf[128];
+
+ if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
+ sizeof(buf)))
+ {
+ return;
+ }
+
+ if (call != current_call) {
+ puts("Call has been disconnected");
+ return;
+ }
+
+ digits = pj_str(buf);
+ status = pjsua_call_dial_dtmf(current_call, &digits);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
+ } else {
+ puts("DTMF digits enqueued for transmission");
+ }
+ }
+}
+
+static void ui_send_dtmf_info()
+{
+ if (current_call == -1) {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ } else {
+ const pj_str_t SIP_INFO = pj_str("INFO");
+ pj_str_t digits;
+ int call = current_call;
+ int i;
+ pj_status_t status;
+ char buf[128];
+
+ if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
+ sizeof(buf)))
+ {
+ return;
+ }
+
+ if (call != current_call) {
+ puts("Call has been disconnected");
+ return;
+ }
+
+ digits = pj_str(buf);
+ for (i=0; i<digits.slen; ++i) {
+ char body[80];
+ pjsua_msg_data msg_data;
+
+ pjsua_msg_data_init(&msg_data);
+ msg_data.content_type = pj_str("application/dtmf-relay");
+
+ pj_ansi_snprintf(body, sizeof(body),
+ "Signal=%c\r\n"
+ "Duration=160",
+ buf[i]);
+ msg_data.msg_body = pj_str(body);
+
+ status = pjsua_call_send_request(current_call, &SIP_INFO,
+ &msg_data);
+ if (status != PJ_SUCCESS) {
+ return;
+ }
+ }
+ }
+}
+
+static void ui_send_arbitrary_request()
+{
+ char text[128];
+ char buf[128];
+ char *uri;
+ input_result result;
+ pj_str_t tmp;
+
+ if (pjsua_acc_get_count() == 0) {
+ puts("Sorry, need at least one account configured");
+ return;
+ }
+
+ puts("Send arbitrary request to remote host");
+
+ /* Input METHOD */
+ if (!simple_input("Request method:",text,sizeof(text)))
+ return;
+
+ /* Input destination URI */
+ uri = NULL;
+ ui_input_url("Destination URI", buf, sizeof(buf), &result);
+ if (result.nb_result != PJSUA_APP_NO_NB) {
+
+ if (result.nb_result == -1) {
+ puts("Sorry you can't do that!");
+ return;
+ } else if (result.nb_result == 0) {
+ uri = NULL;
+ if (current_call == PJSUA_INVALID_ID) {
+ puts("No current call");
+ return;
+ }
+ } else {
+ pjsua_buddy_info binfo;
+ pjsua_buddy_get_info(result.nb_result-1, &binfo);
+ tmp.ptr = buf;
+ pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
+ uri = buf;
+ }
+
+ } else if (result.uri_result) {
+ uri = result.uri_result;
+ } else {
+ return;
+ }
+
+ if (uri) {
+ tmp = pj_str(uri);
+ send_request(text, &tmp);
+ } else {
+ /* If you send call control request using this method
+ * (such requests includes BYE, CANCEL, etc.), it will
+ * not go well with the call state, so don't do it
+ * unless it's for testing.
+ */
+ pj_str_t method = pj_str(text);
+ pjsua_call_send_request(current_call, &method, NULL);
+ }
+}
+
+static void ui_echo(char menuin[])
+{
+ if (pj_ansi_strnicmp(menuin, "echo", 4)==0) {
+ pj_str_t tmp;
+
+ tmp.ptr = menuin+5;
+ tmp.slen = pj_ansi_strlen(menuin)-6;
+
+ if (tmp.slen < 1) {
+ puts("Usage: echo [0|1]");
+ return;
+ }
+ cmd_echo = *tmp.ptr != '0' || tmp.slen!=1;
+ }
+}
+
+static void ui_sleep(char menuin[])
+{
+ if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
+ pj_str_t tmp;
+ int delay;
+
+ tmp.ptr = menuin+6;
+ tmp.slen = pj_ansi_strlen(menuin)-7;
+
+ if (tmp.slen < 1) {
+ puts("Usage: sleep MSEC");
+ return;
+ }
+
+ delay = pj_strtoul(&tmp);
+ if (delay < 0) delay = 0;
+ pj_thread_sleep(delay);
+ }
+}
+
+static void ui_subscribe(char menuin[])
+{
+ char buf[128];
+ input_result result;
+
+ ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
+ if (result.nb_result != PJSUA_APP_NO_NB) {
+ if (result.nb_result == -1) {
+ int i, count;
+ count = pjsua_get_buddy_count();
+ for (i=0; i<count; ++i)
+ pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
+ } else if (result.nb_result == 0) {
+ puts("Sorry, can only subscribe to buddy's presence, "
+ "not from existing call");
+ } else {
+ pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
+ }
+
+ } else if (result.uri_result) {
+ puts("Sorry, can only subscribe to buddy's presence, "
+ "not arbitrary URL (for now)");
+ }
+}
+
+static void ui_register(char menuin[])
+{
+ switch (menuin[1]) {
+ case 'r':
+ /*
+ * Re-Register.
+ */
+ pjsua_acc_set_registration(current_acc, PJ_TRUE);
+ break;
+ case 'u':
+ /*
+ * Unregister
+ */
+ pjsua_acc_set_registration(current_acc, PJ_FALSE);
+ break;
+ }
+}
+
+static void ui_toggle_state()
+{
+ pjsua_acc_info acc_info;
+
+ pjsua_acc_get_info(current_acc, &acc_info);
+ acc_info.online_status = !acc_info.online_status;
+ pjsua_acc_set_online_status(current_acc, acc_info.online_status);
+ printf("Setting %s online status to %s\n",
+ acc_info.acc_uri.ptr,
+ (acc_info.online_status?"online":"offline"));
+}
+
+/*
+ * Change extended online status.
+ */
+static void ui_change_online_status()
+{
+ char menuin[32];
+ pj_bool_t online_status;
+ pjrpid_element elem;
+ int i, choice;
+
+ enum {
+ AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
+ };
+
+ struct opt {
+ int id;
+ char *name;
+ } opts[] = {
+ { AVAILABLE, "Available" },
+ { BUSY, "Busy"},
+ { OTP, "On the phone"},
+ { IDLE, "Idle"},
+ { AWAY, "Away"},
+ { BRB, "Be right back"},
+ { OFFLINE, "Offline"}
+ };
+
+ printf("\n"
+ "Choices:\n");
+ for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
+ printf(" %d %s\n", opts[i].id+1, opts[i].name);
+ }
+
+ if (!simple_input("Select status", menuin, sizeof(menuin)))
+ return;
+
+ choice = atoi(menuin) - 1;
+ if (choice < 0 || choice >= OPT_MAX) {
+ puts("Invalid selection");
+ return;
+ }
+
+ pj_bzero(&elem, sizeof(elem));
+ elem.type = PJRPID_ELEMENT_TYPE_PERSON;
+
+ online_status = PJ_TRUE;
+
+ switch (choice) {
+ case AVAILABLE:
+ break;
+ case BUSY:
+ elem.activity = PJRPID_ACTIVITY_BUSY;
+ elem.note = pj_str("Busy");
+ break;
+ case OTP:
+ elem.activity = PJRPID_ACTIVITY_BUSY;
+ elem.note = pj_str("On the phone");
+ break;
+ case IDLE:
+ elem.activity = PJRPID_ACTIVITY_UNKNOWN;
+ elem.note = pj_str("Idle");
+ break;
+ case AWAY:
+ elem.activity = PJRPID_ACTIVITY_AWAY;
+ elem.note = pj_str("Away");
+ break;
+ case BRB:
+ elem.activity = PJRPID_ACTIVITY_UNKNOWN;
+ elem.note = pj_str("Be right back");
+ break;
+ case OFFLINE:
+ online_status = PJ_FALSE;
+ break;
+ }
+
+ pjsua_acc_set_online_status2(current_acc, online_status, &elem);
+}
+
+/*
+ * List the ports in conference bridge
+ */
+static void ui_conf_list()
+{
+ unsigned i, count;
+ pjsua_conf_port_id id[PJSUA_MAX_CALLS];
+
+ printf("Conference ports:\n");
+
+ count = PJ_ARRAY_SIZE(id);
+ pjsua_enum_conf_ports(id, &count);
+
+ for (i=0; i<count; ++i) {
+ char txlist[PJSUA_MAX_CALLS*4+10];
+ unsigned j;
+ pjsua_conf_port_info info;
+
+ pjsua_conf_get_port_info(id[i], &info);
+
+ txlist[0] = '\0';
+ for (j=0; j<info.listener_cnt; ++j) {
+ char s[10];
+ pj_ansi_snprintf(s, sizeof(s), "#%d ", info.listeners[j]);
+ pj_ansi_strcat(txlist, s);
+ }
+ printf("Port #%02d[%2dKHz/%dms/%d] %20.*s transmitting to: %s\n",
+ info.slot_id,
+ info.clock_rate/1000,
+ info.samples_per_frame*1000/info.channel_count/info.clock_rate,
+ info.channel_count,
+ (int)info.name.slen,
+ info.name.ptr,
+ txlist);
+
+ }
+ puts("");
+}
+
+static void ui_conf_connect(char menuin[])
+{
+ char tmp[10], src_port[10], dst_port[10];
+ pj_status_t status;
+ int cnt;
+ const char *src_title, *dst_title;
+
+ cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
+
+ if (cnt != 3) {
+ ui_conf_list();
+
+ src_title = (menuin[1]=='c'? "Connect src port #":
+ "Disconnect src port #");
+ dst_title = (menuin[1]=='c'? "To dst port #":"From dst port #");
+
+ if (!simple_input(src_title, src_port, sizeof(src_port)))
+ return;
+
+ if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
+ return;
+ }
+
+ if (menuin[1]=='c') {
+ status = pjsua_conf_connect(my_atoi(src_port), my_atoi(dst_port));
+ } else {
+ status = pjsua_conf_disconnect(my_atoi(src_port), my_atoi(dst_port));
+ }
+ if (status == PJ_SUCCESS) {
+ puts("Success");
+ } else {
+ puts("ERROR!!");
+ }
+}
+
+static void ui_adjust_volume()
+{
+ char buf[128];
+ char text[128];
+ sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
+ if (simple_input(buf,text,sizeof(text))) {
+ char *err;
+ app_config.mic_level = (float)strtod(text, &err);
+ pjsua_conf_adjust_rx_level(0, app_config.mic_level);
+ }
+ sprintf(buf, "Adjust speaker level: [%4.1fx] ", app_config.speaker_level);
+ if (simple_input(buf,text,sizeof(text))) {
+ char *err;
+ app_config.speaker_level = (float)strtod(text, &err);
+ pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
+ }
+}
+
+static void ui_dump_call_quality()
+{
+ if (current_call != PJSUA_INVALID_ID) {
+ log_call_dump(current_call);
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ }
+}
+
+static void ui_dump_configuration()
+{
+ char settings[2000];
+ int len;
+
+ len = write_settings(&app_config, settings, sizeof(settings));
+ if (len < 1)
+ PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
+ else
+ PJ_LOG(3,(THIS_FILE, "Dumping configuration (%d bytes):\n%s\n",
+ len, settings));
+}
+
+static void ui_write_settings()
+{
+ char settings[2000];
+ int len;
+ char buf[128];
+
+ len = write_settings(&app_config, settings, sizeof(settings));
+ if (len < 1)
+ PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
+ else {
+ pj_oshandle_t fd;
+ pj_status_t status;
+
+ status = pj_file_open(app_config.pool, buf, PJ_O_WRONLY, &fd);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to open file", status);
+ } else {
+ pj_ssize_t size = len;
+ pj_file_write(fd, settings, &size);
+ pj_file_close(fd);
+
+ printf("Settings successfully written to '%s'\n", buf);
+ }
+ }
+}
+
+/*
+ * Dump application states.
+ */
+static void ui_app_dump(pj_bool_t detail)
+{
+ pjsua_dump(detail);
+}
+
+static void ui_call_redirect(char menuin[])
+{
+ if (current_call == PJSUA_INVALID_ID) {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ } else {
+ if (!pjsua_call_is_active(current_call)) {
+ PJ_LOG(1,(THIS_FILE, "Call %d has gone", current_call));
+ } else if (menuin[1] == 'a') {
+ pjsua_call_process_redirect(current_call,
+ PJSIP_REDIRECT_ACCEPT_REPLACE);
+ } else if (menuin[1] == 'A') {
+ pjsua_call_process_redirect(current_call,
+ PJSIP_REDIRECT_ACCEPT);
+ } else if (menuin[1] == 'r') {
+ pjsua_call_process_redirect(current_call,
+ PJSIP_REDIRECT_REJECT);
+ } else {
+ pjsua_call_process_redirect(current_call,
+ PJSIP_REDIRECT_STOP);
+ }
+ }
+}
+
+/*
+ * Main "user interface" loop.
+ */
+void legacy_main()
+{
+ char menuin[80];
+ char buf[128];
+
+ keystroke_help();
+
+ for (;;) {
+
+ printf(">>> ");
+ fflush(stdout);
+
+ if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
+ /*
+ * Be friendly to users who redirect commands into
+ * program, when file ends, resume with kbd.
+ * If exit is desired end script with q for quit
+ */
+ /* Reopen stdin/stdout/stderr to /dev/console */
+#if ((defined(PJ_WIN32) && PJ_WIN32!=0) || \
+ (defined(PJ_WIN64) && PJ_WIN64!=0)) && \
+ (!defined(PJ_WIN32_WINCE) || PJ_WIN32_WINCE==0)
+ if (freopen ("CONIN$", "r", stdin) == NULL) {
+#else
+ if (1) {
+#endif
+ puts("Cannot switch back to console from file redirection");
+ menuin[0] = 'q';
+ menuin[1] = '\0';
+ } else {
+ puts("Switched back to console from file redirection");
+ continue;
+ }
+ }
+
+ if (cmd_echo) {
+ printf("%s", menuin);
+ }
+
+ /* Update call setting */
+ pjsua_call_setting_default(&call_opt);
+ call_opt.aud_cnt = app_config.aud_cnt;
+ call_opt.vid_cnt = app_config.vid.vid_cnt;
+
+ switch (menuin[0]) {
+
+ case 'm':
+ /* Make call! : */
+ ui_make_new_call();
+ break;
+
+ case 'M':
+ /* Make multiple calls! : */
+ ui_make_multi_call();
+ break;
+
+ case 'n':
+ ui_detect_nat_type();
+ break;
+
+ case 'i':
+ /* Send instant messaeg */
+ ui_send_instant_message();
+ break;
+
+ case 'a':
+ ui_answer_call();
+ break;
+
+ case 'h':
+ ui_hangup_call(menuin);
+ break;
+
+ case ']':
+ case '[':
+ /*
+ * Cycle next/prev dialog.
+ */
+ ui_cycle_dialog(menuin);
+ break;
+
+ case '>':
+ case '<':
+ ui_cycle_account();
+ break;
+
+ case '+':
+ if (menuin[1] == 'b') {
+ ui_add_buddy();
+ } else if (menuin[1] == 'a') {
+ ui_add_account(&app_config.rtp_cfg);
+ } else {
+ printf("Invalid input %s\n", menuin);
+ }
+ break;
+
+ case '-':
+ if (menuin[1] == 'b') {
+ ui_delete_buddy();
+ } else if (menuin[1] == 'a') {
+ ui_delete_account();
+ } else {
+ printf("Invalid input %s\n", menuin);
+ }
+ break;
+
+ case 'H':
+ /*
+ * Hold call.
+ */
+ ui_call_hold();
+ break;
+
+ case 'v':
+#if PJSUA_HAS_VIDEO
+ if (menuin[1]=='i' && menuin[2]=='d' && menuin[3]==' ') {
+ vid_handle_menu(menuin);
+ } else
+#endif
+ if (current_call != -1) {
+ /*
+ * re-INVITE
+ */
+ ui_call_reinvite();
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ }
+ break;
+
+ case 'U':
+ /*
+ * Send UPDATE
+ */
+ ui_send_update();
+ break;
+
+ case 'C':
+ if (menuin[1] == 'p') {
+ ui_manage_codec_prio();
+ }
+ break;
+
+ case 'x':
+ /*
+ * Transfer call.
+ */
+ ui_call_transfer(app_config.no_refersub);
+ break;
+
+ case 'X':
+ /*
+ * Transfer call with replaces.
+ */
+ ui_call_transfer_replaces(app_config.no_refersub);
+ break;
+
+ case '#':
+ /*
+ * Send DTMF strings.
+ */
+ ui_send_dtmf_2833();
+ break;
+
+ case '*':
+ /* Send DTMF with INFO */
+ ui_send_dtmf_info();
+ break;
+
+ case 'S':
+ /*
+ * Send arbitrary request
+ */
+ ui_send_arbitrary_request();
+ break;
+
+ case 'e':
+ ui_echo(menuin);
+ break;
+
+ case 's':
+ if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
+ ui_sleep(menuin);
+ break;
+ }
+ /* Continue below */
+
+ case 'u':
+ /*
+ * Subscribe/unsubscribe presence.
+ */
+ ui_subscribe(menuin);
+ break;
+
+ case 'r':
+ ui_register(menuin);
+ break;
+
+ case 't':
+ ui_toggle_state();
+ break;
+
+ case 'T':
+ ui_change_online_status();
+ break;
+
+ case 'c':
+ switch (menuin[1]) {
+ case 'l':
+ ui_conf_list();
+ break;
+ case 'c':
+ case 'd':
+ ui_conf_connect(menuin);
+ break;
+ }
+ break;
+
+ case 'V':
+ /* Adjust audio volume */
+ ui_adjust_volume();
+ break;
+
+ case 'd':
+ if (menuin[1] == 'c') {
+ ui_dump_configuration();
+ } else if (menuin[1] == 'q') {
+ ui_dump_call_quality();
+ } else {
+ ui_app_dump(menuin[1]=='d');
+ }
+ break;
+
+ case 'f':
+ if (simple_input("Enter output filename", buf, sizeof(buf))) {
+ ui_write_settings();
+ }
+ break;
+
+ case 'L': /* Restart */
+ case 'q':
+ legacy_on_stopped(menuin[0]=='L');
+ goto on_exit;
+
+ case 'R':
+ ui_call_redirect(menuin);
+ break;
+
+ default:
+ if (menuin[0] != '\n' && menuin[0] != '\r') {
+ printf("Invalid input %s", menuin);
+ }
+ keystroke_help();
+ break;
+ }
+ }
+
+on_exit:
+ ;
+}
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8e50eadcbf6176950210349e5a9338d3d66ee0d.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8e50eadcbf6176950210349e5a9338d3d66ee0d.svn-base
new file mode 100644
index 0000000..ab22c5e
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8e50eadcbf6176950210349e5a9338d3d66ee0d.svn-base
@@ -0,0 +1,772 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "test.h"
+#include <pjsip.h>
+#include <pjlib.h>
+
+#define THIS_FILE "transport_test.c"
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic testing for transport, to make sure that basic
+ * attributes have been initialized properly.
+ */
+int generic_transport_test(pjsip_transport *tp)
+{
+ PJ_LOG(3,(THIS_FILE, " structure test..."));
+
+ /* Check that local address name is valid. */
+ {
+ struct pj_in_addr addr;
+
+ /* Note: inet_aton() returns non-zero if addr is valid! */
+ if (pj_inet_aton(&tp->local_name.host, &addr) != 0) {
+ if (addr.s_addr==PJ_INADDR_ANY || addr.s_addr==PJ_INADDR_NONE) {
+ PJ_LOG(3,(THIS_FILE, " Error: invalid address name"));
+ return -420;
+ }
+ } else {
+ /* It's okay. local_name.host may be a hostname instead of
+ * IP address.
+ */
+ }
+ }
+
+ /* Check that port is valid. */
+ if (tp->local_name.port <= 0) {
+ return -430;
+ }
+
+ /* Check length of address (for now we only check against sockaddr_in). */
+ if (tp->addr_len != sizeof(pj_sockaddr_in))
+ return -440;
+
+ /* Check type. */
+ if (tp->key.type == PJSIP_TRANSPORT_UNSPECIFIED)
+ return -450;
+
+ /* That's it. */
+ return PJ_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Send/receive test.
+ *
+ * This test sends a request to loopback address; as soon as request is
+ * received, response will be sent, and time is recorded.
+ *
+ * The main purpose is to test that the basic transport functionalities works,
+ * before we continue with more complicated tests.
+ */
+#define FROM_HDR "Bob <sip:bob@example.com>"
+#define CONTACT_HDR "Bob <sip:bob@127.0.0.1>"
+#define CALL_ID_HDR "SendRecv-Test"
+#define CSEQ_VALUE 100
+#define BODY "Hello World!"
+
+static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata);
+
+/* Flag to indicate message has been received
+ * (or failed to send)
+ */
+#define NO_STATUS -2
+static int send_status = NO_STATUS;
+static int recv_status = NO_STATUS;
+static pj_timestamp my_send_time, my_recv_time;
+
+/* Module to receive messages for this test. */
+static pjsip_module my_module =
+{
+ NULL, NULL, /* prev and next */
+ { "Transport-Test", 14}, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &my_on_rx_request, /* on_rx_request() */
+ &my_on_rx_response, /* on_rx_response() */
+ NULL, /* on_tsx_state() */
+};
+
+
+static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata)
+{
+ /* Check that this is our request. */
+ if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
+ /* It is! */
+ /* Send response. */
+ pjsip_tx_data *tdata;
+ pjsip_response_addr res_addr;
+ pj_status_t status;
+
+ status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ recv_status = status;
+ return PJ_TRUE;
+ }
+ status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
+ if (status != PJ_SUCCESS) {
+ recv_status = status;
+ pjsip_tx_data_dec_ref(tdata);
+ return PJ_TRUE;
+ }
+ status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ recv_status = status;
+ pjsip_tx_data_dec_ref(tdata);
+ return PJ_TRUE;
+ }
+ return PJ_TRUE;
+ }
+
+ /* Not ours. */
+ return PJ_FALSE;
+}
+
+static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata)
+{
+ if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
+ pj_get_timestamp(&my_recv_time);
+ recv_status = PJ_SUCCESS;
+ return PJ_TRUE;
+ }
+ return PJ_FALSE;
+}
+
+/* Transport callback. */
+static void send_msg_callback(pjsip_send_state *stateless_data,
+ pj_ssize_t sent, pj_bool_t *cont)
+{
+ PJ_UNUSED_ARG(stateless_data);
+
+ if (sent < 1) {
+ /* Obtain the error code. */
+ send_status = (int)-sent;
+ } else {
+ send_status = PJ_SUCCESS;
+ }
+
+ /* Don't want to continue. */
+ *cont = PJ_FALSE;
+}
+
+
+/* Test that we receive loopback message. */
+int transport_send_recv_test( pjsip_transport_type_e tp_type,
+ pjsip_transport *ref_tp,
+ char *target_url,
+ int *p_usec_rtt)
+{
+ pj_bool_t msg_log_enabled;
+ pj_status_t status;
+ pj_str_t target, from, to, contact, call_id, body;
+ pjsip_method method;
+ pjsip_tx_data *tdata;
+ pj_time_val timeout;
+
+ PJ_UNUSED_ARG(tp_type);
+ PJ_UNUSED_ARG(ref_tp);
+
+ PJ_LOG(3,(THIS_FILE, " single message round-trip test..."));
+
+ /* Register out test module to receive the message (if necessary). */
+ if (my_module.id == -1) {
+ status = pjsip_endpt_register_module( endpt, &my_module );
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to register module", status);
+ return -500;
+ }
+ }
+
+ /* Disable message logging. */
+ msg_log_enabled = msg_logger_set_enabled(0);
+
+ /* Create a request message. */
+ target = pj_str(target_url);
+ from = pj_str(FROM_HDR);
+ to = pj_str(target_url);
+ contact = pj_str(CONTACT_HDR);
+ call_id = pj_str(CALL_ID_HDR);
+ body = pj_str(BODY);
+
+ pjsip_method_set(&method, PJSIP_OPTIONS_METHOD);
+ status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to,
+ &contact, &call_id, CSEQ_VALUE,
+ &body, &tdata );
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ return -510;
+ }
+
+ /* Reset statuses */
+ send_status = recv_status = NO_STATUS;
+
+ /* Start time. */
+ pj_get_timestamp(&my_send_time);
+
+ /* Send the message (statelessly). */
+ PJ_LOG(5,(THIS_FILE, "Sending request to %.*s",
+ (int)target.slen, target.ptr));
+ status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL,
+ &send_msg_callback);
+ if (status != PJ_SUCCESS) {
+ /* Immediate error! */
+ pjsip_tx_data_dec_ref(tdata);
+ send_status = status;
+ }
+
+ /* Set the timeout (2 seconds from now) */
+ pj_gettimeofday(&timeout);
+ timeout.sec += 2;
+
+ /* Loop handling events until we get status */
+ do {
+ pj_time_val now;
+ pj_time_val poll_interval = { 0, 10 };
+
+ pj_gettimeofday(&now);
+ if (PJ_TIME_VAL_GTE(now, timeout)) {
+ PJ_LOG(3,(THIS_FILE, " error: timeout in send/recv test"));
+ status = -540;
+ goto on_return;
+ }
+
+ if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) {
+ app_perror(" error sending message", send_status);
+ status = -550;
+ goto on_return;
+ }
+
+ if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) {
+ app_perror(" error receiving message", recv_status);
+ status = -560;
+ goto on_return;
+ }
+
+ if (send_status!=NO_STATUS && recv_status!=NO_STATUS) {
+ /* Success! */
+ break;
+ }
+
+ pjsip_endpt_handle_events(endpt, &poll_interval);
+
+ } while (1);
+
+ if (status == PJ_SUCCESS) {
+ unsigned usec_rt;
+ usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time);
+
+ PJ_LOG(3,(THIS_FILE, " round-trip = %d usec", usec_rt));
+
+ *p_usec_rtt = usec_rt;
+ }
+
+ /* Restore message logging. */
+ msg_logger_set_enabled(msg_log_enabled);
+
+ status = PJ_SUCCESS;
+
+on_return:
+ return status;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Multithreaded round-trip test
+ *
+ * This test will spawn multiple threads, each of them send a request. As soon
+ * as request is received, response will be sent, and time is recorded.
+ *
+ * The main purpose of this test is to ensure there's no crash when multiple
+ * threads are sending/receiving messages.
+ *
+ */
+static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata);
+
+static pjsip_module rt_module =
+{
+ NULL, NULL, /* prev and next */
+ { "Transport-RT-Test", 17}, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &rt_on_rx_request, /* on_rx_request() */
+ &rt_on_rx_response, /* on_rx_response() */
+ NULL, /* tsx_handler() */
+};
+
+static struct
+{
+ pj_thread_t *thread;
+ pj_timestamp send_time;
+ pj_timestamp total_rt_time;
+ int sent_request_count, recv_response_count;
+ pj_str_t call_id;
+ pj_timer_entry timeout_timer;
+ pj_timer_entry tx_timer;
+ pj_mutex_t *mutex;
+} rt_test_data[16];
+
+static char rt_target_uri[64];
+static pj_bool_t rt_stop;
+static pj_str_t rt_call_id;
+
+static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata)
+{
+ if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) {
+ pjsip_tx_data *tdata;
+ pjsip_response_addr res_addr;
+ pj_status_t status;
+
+ status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error creating response", status);
+ return PJ_TRUE;
+ }
+ status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error in get response address", status);
+ pjsip_tx_data_dec_ref(tdata);
+ return PJ_TRUE;
+ }
+ status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error sending response", status);
+ pjsip_tx_data_dec_ref(tdata);
+ return PJ_TRUE;
+ }
+ return PJ_TRUE;
+
+ }
+ return PJ_FALSE;
+}
+
+static pj_status_t rt_send_request(int thread_id)
+{
+ pj_status_t status;
+ pj_str_t target, from, to, contact, call_id;
+ pjsip_tx_data *tdata;
+ pj_time_val timeout_delay;
+
+ pj_mutex_lock(rt_test_data[thread_id].mutex);
+
+ /* Create a request message. */
+ target = pj_str(rt_target_uri);
+ from = pj_str(FROM_HDR);
+ to = pj_str(rt_target_uri);
+ contact = pj_str(CONTACT_HDR);
+ call_id = rt_test_data[thread_id].call_id;
+
+ status = pjsip_endpt_create_request( endpt, &pjsip_options_method,
+ &target, &from, &to,
+ &contact, &call_id, -1,
+ NULL, &tdata );
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create request", status);
+ pj_mutex_unlock(rt_test_data[thread_id].mutex);
+ return -610;
+ }
+
+ /* Start time. */
+ pj_get_timestamp(&rt_test_data[thread_id].send_time);
+
+ /* Send the message (statelessly). */
+ status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ /* Immediate error! */
+ app_perror(" error: send request", status);
+ pjsip_tx_data_dec_ref(tdata);
+ pj_mutex_unlock(rt_test_data[thread_id].mutex);
+ return -620;
+ }
+
+ /* Update counter. */
+ rt_test_data[thread_id].sent_request_count++;
+
+ /* Set timeout timer. */
+ if (rt_test_data[thread_id].timeout_timer.user_data != NULL) {
+ pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer);
+ }
+ timeout_delay.sec = 100; timeout_delay.msec = 0;
+ rt_test_data[thread_id].timeout_timer.user_data = (void*)(pj_ssize_t)1;
+ pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].timeout_timer,
+ &timeout_delay);
+
+ pj_mutex_unlock(rt_test_data[thread_id].mutex);
+ return PJ_SUCCESS;
+}
+
+static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata)
+{
+ if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) {
+ char *pos = pj_strchr(&rdata->msg_info.cid->id, '/')+1;
+ int thread_id = (*pos - '0');
+ pj_timestamp recv_time;
+
+ pj_mutex_lock(rt_test_data[thread_id].mutex);
+
+ /* Stop timer. */
+ pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer);
+
+ /* Update counter and end-time. */
+ rt_test_data[thread_id].recv_response_count++;
+ pj_get_timestamp(&recv_time);
+
+ pj_sub_timestamp(&recv_time, &rt_test_data[thread_id].send_time);
+ pj_add_timestamp(&rt_test_data[thread_id].total_rt_time, &recv_time);
+
+ if (!rt_stop) {
+ pj_time_val tx_delay = { 0, 0 };
+ pj_assert(rt_test_data[thread_id].tx_timer.user_data == NULL);
+ rt_test_data[thread_id].tx_timer.user_data = (void*)(pj_ssize_t)1;
+ pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].tx_timer,
+ &tx_delay);
+ }
+
+ pj_mutex_unlock(rt_test_data[thread_id].mutex);
+
+ return PJ_TRUE;
+ }
+ return PJ_FALSE;
+}
+
+static void rt_timeout_timer( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry )
+{
+ pj_mutex_lock(rt_test_data[entry->id].mutex);
+
+ PJ_UNUSED_ARG(timer_heap);
+ PJ_LOG(3,(THIS_FILE, " timeout waiting for response"));
+ rt_test_data[entry->id].timeout_timer.user_data = NULL;
+
+ if (rt_test_data[entry->id].tx_timer.user_data == NULL) {
+ pj_time_val delay = { 0, 0 };
+ rt_test_data[entry->id].tx_timer.user_data = (void*)(pj_ssize_t)1;
+ pjsip_endpt_schedule_timer(endpt, &rt_test_data[entry->id].tx_timer,
+ &delay);
+ }
+
+ pj_mutex_unlock(rt_test_data[entry->id].mutex);
+}
+
+static void rt_tx_timer( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry )
+{
+ pj_mutex_lock(rt_test_data[entry->id].mutex);
+
+ PJ_UNUSED_ARG(timer_heap);
+ pj_assert(rt_test_data[entry->id].tx_timer.user_data != NULL);
+ rt_test_data[entry->id].tx_timer.user_data = NULL;
+ rt_send_request(entry->id);
+
+ pj_mutex_unlock(rt_test_data[entry->id].mutex);
+}
+
+
+static int rt_worker_thread(void *arg)
+{
+ int i;
+ pj_time_val poll_delay = { 0, 10 };
+
+ PJ_UNUSED_ARG(arg);
+
+ /* Sleep to allow main threads to run. */
+ pj_thread_sleep(10);
+
+ while (!rt_stop) {
+ pjsip_endpt_handle_events(endpt, &poll_delay);
+ }
+
+ /* Exhaust responses. */
+ for (i=0; i<100; ++i)
+ pjsip_endpt_handle_events(endpt, &poll_delay);
+
+ return 0;
+}
+
+int transport_rt_test( pjsip_transport_type_e tp_type,
+ pjsip_transport *ref_tp,
+ char *target_url,
+ int *lost)
+{
+ enum { THREADS = 4, INTERVAL = 10 };
+ int i;
+ pj_status_t status;
+ pj_pool_t *pool;
+ pj_bool_t logger_enabled;
+
+ pj_timestamp zero_time, total_time;
+ unsigned usec_rt;
+ unsigned total_sent;
+ unsigned total_recv;
+
+ PJ_UNUSED_ARG(tp_type);
+ PJ_UNUSED_ARG(ref_tp);
+
+ PJ_LOG(3,(THIS_FILE, " multithreaded round-trip test (%d threads)...",
+ THREADS));
+ PJ_LOG(3,(THIS_FILE, " this will take approx %d seconds, please wait..",
+ INTERVAL));
+
+ /* Make sure msg logger is disabled. */
+ logger_enabled = msg_logger_set_enabled(0);
+
+ /* Register module (if not yet registered) */
+ if (rt_module.id == -1) {
+ status = pjsip_endpt_register_module( endpt, &rt_module );
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to register module", status);
+ return -600;
+ }
+ }
+
+ /* Create pool for this test. */
+ pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
+ if (!pool)
+ return -610;
+
+ /* Initialize static test data. */
+ pj_ansi_strcpy(rt_target_uri, target_url);
+ rt_call_id = pj_str("RT-Call-Id/");
+ rt_stop = PJ_FALSE;
+
+ /* Initialize thread data. */
+ for (i=0; i<THREADS; ++i) {
+ char buf[1];
+ pj_str_t str_id;
+
+ pj_strset(&str_id, buf, 1);
+ pj_bzero(&rt_test_data[i], sizeof(rt_test_data[i]));
+
+ /* Init timer entry */
+ rt_test_data[i].tx_timer.id = i;
+ rt_test_data[i].tx_timer.cb = &rt_tx_timer;
+ rt_test_data[i].timeout_timer.id = i;
+ rt_test_data[i].timeout_timer.cb = &rt_timeout_timer;
+
+ /* Generate Call-ID for each thread. */
+ rt_test_data[i].call_id.ptr = (char*) pj_pool_alloc(pool, rt_call_id.slen+1);
+ pj_strcpy(&rt_test_data[i].call_id, &rt_call_id);
+ buf[0] = '0' + (char)i;
+ pj_strcat(&rt_test_data[i].call_id, &str_id);
+
+ /* Init mutex. */
+ status = pj_mutex_create_recursive(pool, "rt", &rt_test_data[i].mutex);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create mutex", status);
+ return -615;
+ }
+
+ /* Create thread, suspended. */
+ status = pj_thread_create(pool, "rttest%p", &rt_worker_thread,
+ (void*)(pj_ssize_t)i, 0,
+ PJ_THREAD_SUSPENDED, &rt_test_data[i].thread);
+ if (status != PJ_SUCCESS) {
+ app_perror(" error: unable to create thread", status);
+ return -620;
+ }
+ }
+
+ /* Start threads! */
+ for (i=0; i<THREADS; ++i) {
+ pj_time_val delay = {0,0};
+ pj_thread_resume(rt_test_data[i].thread);
+
+ /* Schedule first message transmissions. */
+ rt_test_data[i].tx_timer.user_data = (void*)(pj_ssize_t)1;
+ pjsip_endpt_schedule_timer(endpt, &rt_test_data[i].tx_timer, &delay);
+ }
+
+ /* Sleep for some time. */
+ pj_thread_sleep(INTERVAL * 1000);
+
+ /* Signal thread to stop. */
+ rt_stop = PJ_TRUE;
+
+ /* Wait threads to complete. */
+ for (i=0; i<THREADS; ++i) {
+ pj_thread_join(rt_test_data[i].thread);
+ pj_thread_destroy(rt_test_data[i].thread);
+ }
+
+ /* Destroy rt_test_data */
+ for (i=0; i<THREADS; ++i) {
+ pj_mutex_destroy(rt_test_data[i].mutex);
+ pjsip_endpt_cancel_timer(endpt, &rt_test_data[i].timeout_timer);
+ }
+
+ /* Gather statistics. */
+ pj_bzero(&total_time, sizeof(total_time));
+ pj_bzero(&zero_time, sizeof(zero_time));
+ usec_rt = total_sent = total_recv = 0;
+ for (i=0; i<THREADS; ++i) {
+ total_sent += rt_test_data[i].sent_request_count;
+ total_recv += rt_test_data[i].recv_response_count;
+ pj_add_timestamp(&total_time, &rt_test_data[i].total_rt_time);
+ }
+
+ /* Display statistics. */
+ if (total_recv)
+ total_time.u64 = total_time.u64/total_recv;
+ else
+ total_time.u64 = 0;
+ usec_rt = pj_elapsed_usec(&zero_time, &total_time);
+ PJ_LOG(3,(THIS_FILE, " done."));
+ PJ_LOG(3,(THIS_FILE, " total %d messages sent", total_sent));
+ PJ_LOG(3,(THIS_FILE, " average round-trip=%d usec", usec_rt));
+
+ pjsip_endpt_release_pool(endpt, pool);
+
+ *lost = total_sent-total_recv;
+
+ /* Flush events. */
+ flush_events(500);
+
+ /* Restore msg logger. */
+ msg_logger_set_enabled(logger_enabled);
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Transport load testing
+ */
+static pj_bool_t load_on_rx_request(pjsip_rx_data *rdata);
+
+static struct mod_load_test
+{
+ pjsip_module mod;
+ pj_int32_t next_seq;
+ pj_bool_t err;
+} mod_load =
+{
+ {
+ NULL, NULL, /* prev and next */
+ { "mod-load-test", 13}, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &load_on_rx_request, /* on_rx_request() */
+ NULL, /* on_rx_response() */
+ NULL, /* tsx_handler() */
+ }
+};
+
+
+static pj_bool_t load_on_rx_request(pjsip_rx_data *rdata)
+{
+ if (rdata->msg_info.cseq->cseq != mod_load.next_seq) {
+ PJ_LOG(1,("THIS_FILE", " err: expecting cseq %u, got %u",
+ mod_load.next_seq, rdata->msg_info.cseq->cseq));
+ mod_load.err = PJ_TRUE;
+ mod_load.next_seq = rdata->msg_info.cseq->cseq + 1;
+ } else
+ mod_load.next_seq++;
+ return PJ_TRUE;
+}
+
+int transport_load_test(char *target_url)
+{
+ enum { COUNT = 2000 };
+ unsigned i;
+ pj_status_t status = PJ_SUCCESS;
+
+ /* exhaust packets */
+ do {
+ pj_time_val delay = {1, 0};
+ i = 0;
+ pjsip_endpt_handle_events2(endpt, &delay, &i);
+ } while (i != 0);
+
+ PJ_LOG(3,(THIS_FILE, " transport load test..."));
+
+ if (mod_load.mod.id == -1) {
+ status = pjsip_endpt_register_module( endpt, &mod_load.mod);
+ if (status != PJ_SUCCESS) {
+ app_perror("error registering module", status);
+ return -1;
+ }
+ }
+ mod_load.err = PJ_FALSE;
+ mod_load.next_seq = 0;
+
+ for (i=0; i<COUNT && !mod_load.err; ++i) {
+ pj_str_t target, from, call_id;
+ pjsip_tx_data *tdata;
+
+ target = pj_str(target_url);
+ from = pj_str("<sip:user@host>");
+ call_id = pj_str("thecallid");
+ status = pjsip_endpt_create_request(endpt, &pjsip_invite_method,
+ &target, &from,
+ &target, &from, &call_id,
+ i, NULL, &tdata );
+ if (status != PJ_SUCCESS) {
+ app_perror("error creating request", status);
+ goto on_return;
+ }
+
+ status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ app_perror("error sending request", status);
+ goto on_return;
+ }
+ }
+
+ do {
+ pj_time_val delay = {1, 0};
+ i = 0;
+ pjsip_endpt_handle_events2(endpt, &delay, &i);
+ } while (i != 0);
+
+ if (mod_load.next_seq != COUNT) {
+ PJ_LOG(1,("THIS_FILE", " err: expecting %u msg, got only %u",
+ COUNT, mod_load.next_seq));
+ status = -2;
+ goto on_return;
+ }
+
+on_return:
+ if (mod_load.mod.id != -1) {
+ pjsip_endpt_unregister_module( endpt, &mod_load.mod);
+ mod_load.mod.id = -1;
+ }
+ if (status != PJ_SUCCESS || mod_load.err) {
+ return -2;
+ }
+ PJ_LOG(3,(THIS_FILE, " success"));
+ return 0;
+}
+
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8eb610a93eaa21cc3e8cddc75cbec137221305e.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8eb610a93eaa21cc3e8cddc75cbec137221305e.svn-base
new file mode 100644
index 0000000..ebf5a2b
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8eb610a93eaa21cc3e8cddc75cbec137221305e.svn-base
@@ -0,0 +1,855 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "test.h"
+
+#define THIS_FILE "stun_sock_test.c"
+
+enum {
+ RESPOND_STUN = 1,
+ WITH_MAPPED = 2,
+ WITH_XOR_MAPPED = 4,
+
+ ECHO = 8
+};
+
+/*
+ * Simple STUN server
+ */
+struct stun_srv
+{
+ pj_activesock_t *asock;
+ unsigned flag;
+ pj_sockaddr addr;
+ unsigned rx_cnt;
+ pj_ioqueue_op_key_t send_key;
+ pj_str_t ip_to_send;
+ pj_uint16_t port_to_send;
+};
+
+static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock,
+ void *data,
+ pj_size_t size,
+ const pj_sockaddr_t *src_addr,
+ int addr_len,
+ pj_status_t status)
+{
+ struct stun_srv *srv;
+ pj_ssize_t sent;
+
+ srv = (struct stun_srv*) pj_activesock_get_user_data(asock);
+
+ /* Ignore error */
+ if (status != PJ_SUCCESS)
+ return PJ_TRUE;
+
+ ++srv->rx_cnt;
+
+ /* Ignore if we're not responding */
+ if (srv->flag & RESPOND_STUN) {
+ pj_pool_t *pool;
+ pj_stun_msg *req_msg, *res_msg;
+
+ pool = pj_pool_create(mem, "stunsrv", 512, 512, NULL);
+
+ /* Parse request */
+ status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size,
+ PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
+ &req_msg, NULL, NULL);
+ if (status != PJ_SUCCESS) {
+ app_perror(" pj_stun_msg_decode()", status);
+ pj_pool_release(pool);
+ return PJ_TRUE;
+ }
+
+ /* Create response */
+ status = pj_stun_msg_create(pool, PJ_STUN_BINDING_RESPONSE, PJ_STUN_MAGIC,
+ req_msg->hdr.tsx_id, &res_msg);
+ if (status != PJ_SUCCESS) {
+ app_perror(" pj_stun_msg_create()", status);
+ pj_pool_release(pool);
+ return PJ_TRUE;
+ }
+
+ /* Add MAPPED-ADDRESS or XOR-MAPPED-ADDRESS (or don't add) */
+ if (srv->flag & WITH_MAPPED) {
+ pj_sockaddr_in addr;
+
+ pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
+ pj_stun_msg_add_sockaddr_attr(pool, res_msg, PJ_STUN_ATTR_MAPPED_ADDR,
+ PJ_FALSE, &addr, sizeof(addr));
+ } else if (srv->flag & WITH_XOR_MAPPED) {
+ pj_sockaddr_in addr;
+
+ pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
+ pj_stun_msg_add_sockaddr_attr(pool, res_msg,
+ PJ_STUN_ATTR_XOR_MAPPED_ADDR,
+ PJ_TRUE, &addr, sizeof(addr));
+ }
+
+ /* Encode */
+ status = pj_stun_msg_encode(res_msg, (pj_uint8_t*)data, 100, 0,
+ NULL, &size);
+ if (status != PJ_SUCCESS) {
+ app_perror(" pj_stun_msg_encode()", status);
+ pj_pool_release(pool);
+ return PJ_TRUE;
+ }
+
+ /* Send back */
+ sent = size;
+ pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0,
+ src_addr, addr_len);
+
+ pj_pool_release(pool);
+
+ } else if (srv->flag & ECHO) {
+ /* Send back */
+ sent = size;
+ pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0,
+ src_addr, addr_len);
+
+ }
+
+ return PJ_TRUE;
+}
+
+static pj_status_t create_server(pj_pool_t *pool,
+ pj_ioqueue_t *ioqueue,
+ unsigned flag,
+ struct stun_srv **p_srv)
+{
+ struct stun_srv *srv;
+ pj_activesock_cb activesock_cb;
+ pj_status_t status;
+
+ srv = PJ_POOL_ZALLOC_T(pool, struct stun_srv);
+ srv->flag = flag;
+ srv->ip_to_send = pj_str("1.1.1.1");
+ srv->port_to_send = 1000;
+
+ status = pj_sockaddr_in_init(&srv->addr.ipv4, NULL, 0);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_bzero(&activesock_cb, sizeof(activesock_cb));
+ activesock_cb.on_data_recvfrom = &srv_on_data_recvfrom;
+ status = pj_activesock_create_udp(pool, &srv->addr, NULL, ioqueue,
+ &activesock_cb, srv, &srv->asock,
+ &srv->addr);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
+
+ status = pj_activesock_start_recvfrom(srv->asock, pool, 512, 0);
+ if (status != PJ_SUCCESS) {
+ pj_activesock_close(srv->asock);
+ return status;
+ }
+
+ *p_srv = srv;
+ return PJ_SUCCESS;
+}
+
+static void destroy_server(struct stun_srv *srv)
+{
+ pj_activesock_close(srv->asock);
+}
+
+
+struct stun_client
+{
+ pj_pool_t *pool;
+ pj_stun_sock *sock;
+
+ pj_ioqueue_op_key_t send_key;
+ pj_bool_t destroy_on_err;
+
+ unsigned on_status_cnt;
+ pj_stun_sock_op last_op;
+ pj_status_t last_status;
+
+ unsigned on_rx_data_cnt;
+};
+
+static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock,
+ pj_stun_sock_op op,
+ pj_status_t status)
+{
+ struct stun_client *client;
+
+ client = (struct stun_client*) pj_stun_sock_get_user_data(stun_sock);
+ client->on_status_cnt++;
+ client->last_op = op;
+ client->last_status = status;
+
+ if (status != PJ_SUCCESS && client->destroy_on_err) {
+ pj_stun_sock_destroy(client->sock);
+ client->sock = NULL;
+ return PJ_FALSE;
+ }
+
+ return PJ_TRUE;
+}
+
+static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock,
+ void *pkt,
+ unsigned pkt_len,
+ const pj_sockaddr_t *src_addr,
+ unsigned addr_len)
+{
+ struct stun_client *client;
+
+ PJ_UNUSED_ARG(pkt);
+ PJ_UNUSED_ARG(pkt_len);
+ PJ_UNUSED_ARG(src_addr);
+ PJ_UNUSED_ARG(addr_len);
+
+ client = (struct stun_client*) pj_stun_sock_get_user_data(stun_sock);
+ client->on_rx_data_cnt++;
+
+ return PJ_TRUE;
+}
+
+static pj_status_t create_client(pj_stun_config *cfg,
+ struct stun_client **p_client,
+ pj_bool_t destroy_on_err)
+{
+ pj_pool_t *pool;
+ struct stun_client *client;
+ pj_stun_sock_cfg sock_cfg;
+ pj_stun_sock_cb cb;
+ pj_status_t status;
+
+ pool = pj_pool_create(mem, "test", 512, 512, NULL);
+ client = PJ_POOL_ZALLOC_T(pool, struct stun_client);
+ client->pool = pool;
+
+ pj_stun_sock_cfg_default(&sock_cfg);
+
+ pj_bzero(&cb, sizeof(cb));
+ cb.on_status = &stun_sock_on_status;
+ cb.on_rx_data = &stun_sock_on_rx_data;
+ status = pj_stun_sock_create(cfg, NULL, pj_AF_INET(), &cb,
+ &sock_cfg, client, &client->sock);
+ if (status != PJ_SUCCESS) {
+ app_perror(" pj_stun_sock_create()", status);
+ pj_pool_release(pool);
+ return status;
+ }
+
+ pj_stun_sock_set_user_data(client->sock, client);
+
+ pj_ioqueue_op_key_init(&client->send_key, sizeof(client->send_key));
+
+ client->destroy_on_err = destroy_on_err;
+
+ *p_client = client;
+
+ return PJ_SUCCESS;
+}
+
+
+static void destroy_client(struct stun_client *client)
+{
+ if (client->sock) {
+ pj_stun_sock_destroy(client->sock);
+ client->sock = NULL;
+ }
+ pj_pool_release(client->pool);
+}
+
+static void handle_events(pj_stun_config *cfg, unsigned msec_delay)
+{
+ pj_time_val delay;
+
+ pj_timer_heap_poll(cfg->timer_heap, NULL);
+
+ delay.sec = 0;
+ delay.msec = msec_delay;
+ pj_time_val_normalize(&delay);
+
+ pj_ioqueue_poll(cfg->ioqueue, &delay);
+}
+
+/*
+ * Timeout test: scenario when no response is received from server
+ */
+static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
+{
+ struct stun_srv *srv;
+ struct stun_client *client;
+ pj_str_t srv_addr;
+ pj_time_val timeout, t;
+ int i, ret = 0;
+ pj_status_t status;
+
+ PJ_LOG(3,(THIS_FILE, " timeout test [%d]", destroy_on_err));
+
+ status = create_client(cfg, &client, destroy_on_err);
+ if (status != PJ_SUCCESS)
+ return -10;
+
+ status = create_server(client->pool, cfg->ioqueue, 0, &srv);
+ if (status != PJ_SUCCESS) {
+ destroy_client(client);
+ return -20;
+ }
+
+ srv_addr = pj_str("127.0.0.1");
+ status = pj_stun_sock_start(client->sock, &srv_addr,
+ pj_ntohs(srv->addr.ipv4.sin_port), NULL);
+ if (status != PJ_SUCCESS) {
+ destroy_server(srv);
+ destroy_client(client);
+ return -30;
+ }
+
+ /* Wait until on_status() callback is called with the failure */
+ pj_gettimeofday(&timeout);
+ timeout.sec += 60;
+ do {
+ handle_events(cfg, 100);
+ pj_gettimeofday(&t);
+ } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+ /* Check that callback with correct operation is called */
+ if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status"));
+ ret = -40;
+ goto on_return;
+ }
+ /* .. and with the correct status */
+ if (client->last_status != PJNATH_ESTUNTIMEDOUT) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting PJNATH_ESTUNTIMEDOUT"));
+ ret = -50;
+ goto on_return;
+ }
+ /* Check that server received correct retransmissions */
+ if (srv->rx_cnt != PJ_STUN_MAX_TRANSMIT_COUNT) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting %d retransmissions, got %d",
+ PJ_STUN_MAX_TRANSMIT_COUNT, srv->rx_cnt));
+ ret = -60;
+ goto on_return;
+ }
+ /* Check that client doesn't receive anything */
+ if (client->on_rx_data_cnt != 0) {
+ PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
+ ret = -70;
+ goto on_return;
+ }
+
+on_return:
+ destroy_server(srv);
+ destroy_client(client);
+ for (i=0; i<7; ++i)
+ handle_events(cfg, 50);
+ return ret;
+}
+
+
+/*
+ * Invalid response scenario: when server returns no MAPPED-ADDRESS or
+ * XOR-MAPPED-ADDRESS attribute.
+ */
+static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
+{
+ struct stun_srv *srv;
+ struct stun_client *client;
+ pj_str_t srv_addr;
+ pj_time_val timeout, t;
+ int i, ret = 0;
+ pj_status_t status;
+
+ PJ_LOG(3,(THIS_FILE, " missing attribute test [%d]", destroy_on_err));
+
+ status = create_client(cfg, &client, destroy_on_err);
+ if (status != PJ_SUCCESS)
+ return -110;
+
+ status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, &srv);
+ if (status != PJ_SUCCESS) {
+ destroy_client(client);
+ return -120;
+ }
+
+ srv_addr = pj_str("127.0.0.1");
+ status = pj_stun_sock_start(client->sock, &srv_addr,
+ pj_ntohs(srv->addr.ipv4.sin_port), NULL);
+ if (status != PJ_SUCCESS) {
+ destroy_server(srv);
+ destroy_client(client);
+ return -130;
+ }
+
+ /* Wait until on_status() callback is called with the failure */
+ pj_gettimeofday(&timeout);
+ timeout.sec += 60;
+ do {
+ handle_events(cfg, 100);
+ pj_gettimeofday(&t);
+ } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+ /* Check that callback with correct operation is called */
+ if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status"));
+ ret = -140;
+ goto on_return;
+ }
+ if (client->last_status != PJNATH_ESTUNNOMAPPEDADDR) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting PJNATH_ESTUNNOMAPPEDADDR"));
+ ret = -150;
+ goto on_return;
+ }
+ /* Check that client doesn't receive anything */
+ if (client->on_rx_data_cnt != 0) {
+ PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
+ ret = -170;
+ goto on_return;
+ }
+
+on_return:
+ destroy_server(srv);
+ destroy_client(client);
+ for (i=0; i<7; ++i)
+ handle_events(cfg, 50);
+ return ret;
+}
+
+/*
+ * Keep-alive test.
+ */
+static int keep_alive_test(pj_stun_config *cfg)
+{
+ struct stun_srv *srv;
+ struct stun_client *client;
+ pj_sockaddr_in mapped_addr;
+ pj_stun_sock_info info;
+ pj_str_t srv_addr;
+ pj_time_val timeout, t;
+ int i, ret = 0;
+ pj_status_t status;
+
+ PJ_LOG(3,(THIS_FILE, " normal operation"));
+
+ status = create_client(cfg, &client, PJ_TRUE);
+ if (status != PJ_SUCCESS)
+ return -310;
+
+ status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, &srv);
+ if (status != PJ_SUCCESS) {
+ destroy_client(client);
+ return -320;
+ }
+
+ /*
+ * Part 1: initial Binding resolution.
+ */
+ PJ_LOG(3,(THIS_FILE, " initial Binding request"));
+ srv_addr = pj_str("127.0.0.1");
+ status = pj_stun_sock_start(client->sock, &srv_addr,
+ pj_ntohs(srv->addr.ipv4.sin_port), NULL);
+ if (status != PJ_SUCCESS) {
+ destroy_server(srv);
+ destroy_client(client);
+ return -330;
+ }
+
+ /* Wait until on_status() callback is called with success status */
+ pj_gettimeofday(&timeout);
+ timeout.sec += 60;
+ do {
+ handle_events(cfg, 100);
+ pj_gettimeofday(&t);
+ } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+ /* Check that callback with correct operation is called */
+ if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status"));
+ ret = -340;
+ goto on_return;
+ }
+ if (client->last_status != PJ_SUCCESS) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting PJ_SUCCESS status"));
+ ret = -350;
+ goto on_return;
+ }
+ /* Check that client doesn't receive anything */
+ if (client->on_rx_data_cnt != 0) {
+ PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
+ ret = -370;
+ goto on_return;
+ }
+
+ /* Get info */
+ pj_bzero(&info, sizeof(info));
+ pj_stun_sock_get_info(client->sock, &info);
+
+ /* Check that we have server address */
+ if (!pj_sockaddr_has_addr(&info.srv_addr)) {
+ PJ_LOG(3,(THIS_FILE, " error: missing server address"));
+ ret = -380;
+ goto on_return;
+ }
+ /* .. and bound address port must not be zero */
+ if (pj_sockaddr_get_port(&info.bound_addr)==0) {
+ PJ_LOG(3,(THIS_FILE, " error: bound address is zero"));
+ ret = -381;
+ goto on_return;
+ }
+ /* .. and mapped address */
+ if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
+ PJ_LOG(3,(THIS_FILE, " error: missing mapped address"));
+ ret = -382;
+ goto on_return;
+ }
+ /* verify the mapped address */
+ pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
+ if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
+ PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched"));
+ ret = -383;
+ goto on_return;
+ }
+
+ /* .. and at least one alias */
+ if (info.alias_cnt == 0) {
+ PJ_LOG(3,(THIS_FILE, " error: must have at least one alias"));
+ ret = -384;
+ goto on_return;
+ }
+ if (!pj_sockaddr_has_addr(&info.aliases[0])) {
+ PJ_LOG(3,(THIS_FILE, " error: missing alias"));
+ ret = -386;
+ goto on_return;
+ }
+
+
+ /*
+ * Part 2: sending and receiving data
+ */
+ PJ_LOG(3,(THIS_FILE, " sending/receiving data"));
+
+ /* Change server operation mode to echo back data */
+ srv->flag = ECHO;
+
+ /* Reset server */
+ srv->rx_cnt = 0;
+
+ /* Client sending data to echo server */
+ {
+ char txt[100];
+ PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3)));
+ }
+ status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret),
+ 0, &info.srv_addr,
+ pj_sockaddr_get_len(&info.srv_addr));
+ if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+ app_perror(" error: server sending data", status);
+ ret = -390;
+ goto on_return;
+ }
+
+ /* Wait for a short period until client receives data. We can't wait for
+ * too long otherwise the keep-alive will kick in.
+ */
+ pj_gettimeofday(&timeout);
+ timeout.sec += 1;
+ do {
+ handle_events(cfg, 100);
+ pj_gettimeofday(&t);
+ } while (client->on_rx_data_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+ /* Check that data is received in server */
+ if (srv->rx_cnt == 0) {
+ PJ_LOG(3,(THIS_FILE, " error: server didn't receive data"));
+ ret = -395;
+ goto on_return;
+ }
+
+ /* Check that status is still OK */
+ if (client->last_status != PJ_SUCCESS) {
+ app_perror(" error: client has failed", client->last_status);
+ ret = -400;
+ goto on_return;
+ }
+ /* Check that data has been received */
+ if (client->on_rx_data_cnt == 0) {
+ PJ_LOG(3,(THIS_FILE, " error: client doesn't receive data"));
+ ret = -410;
+ goto on_return;
+ }
+
+ /*
+ * Part 3: Successful keep-alive,
+ */
+ PJ_LOG(3,(THIS_FILE, " successful keep-alive scenario"));
+
+ /* Change server operation mode to normal mode */
+ srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
+
+ /* Reset server */
+ srv->rx_cnt = 0;
+
+ /* Reset client */
+ client->on_status_cnt = 0;
+ client->last_status = PJ_SUCCESS;
+ client->on_rx_data_cnt = 0;
+
+ /* Wait for keep-alive duration to see if client actually sends the
+ * keep-alive.
+ */
+ pj_gettimeofday(&timeout);
+ timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
+ do {
+ handle_events(cfg, 100);
+ pj_gettimeofday(&t);
+ } while (PJ_TIME_VAL_LT(t, timeout));
+
+ /* Check that server receives some packets */
+ if (srv->rx_cnt == 0) {
+ PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received"));
+ ret = -420;
+ goto on_return;
+ }
+ /* Check that client status is still okay and on_status() callback is NOT
+ * called
+ */
+ /* No longer valid due to this ticket:
+ * http://trac.pjsip.org/repos/ticket/742
+
+ if (client->on_status_cnt != 0) {
+ PJ_LOG(3, (THIS_FILE, " error: on_status() must not be called on successful"
+ "keep-alive when mapped-address does not change"));
+ ret = -430;
+ goto on_return;
+ }
+ */
+ /* Check that client doesn't receive anything */
+ if (client->on_rx_data_cnt != 0) {
+ PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
+ ret = -440;
+ goto on_return;
+ }
+
+
+ /*
+ * Part 4: Successful keep-alive with IP address change
+ */
+ PJ_LOG(3,(THIS_FILE, " mapped IP address change"));
+
+ /* Change server operation mode to normal mode */
+ srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
+
+ /* Change mapped address in the response */
+ srv->ip_to_send = pj_str("2.2.2.2");
+ srv->port_to_send++;
+
+ /* Reset server */
+ srv->rx_cnt = 0;
+
+ /* Reset client */
+ client->on_status_cnt = 0;
+ client->last_status = PJ_SUCCESS;
+ client->on_rx_data_cnt = 0;
+
+ /* Wait for keep-alive duration to see if client actually sends the
+ * keep-alive.
+ */
+ pj_gettimeofday(&timeout);
+ timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
+ do {
+ handle_events(cfg, 100);
+ pj_gettimeofday(&t);
+ } while (PJ_TIME_VAL_LT(t, timeout));
+
+ /* Check that server receives some packets */
+ if (srv->rx_cnt == 0) {
+ PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received"));
+ ret = -450;
+ goto on_return;
+ }
+ /* Check that on_status() callback is called (because mapped address
+ * has changed)
+ */
+ if (client->on_status_cnt != 1) {
+ PJ_LOG(3, (THIS_FILE, " error: on_status() was not called"));
+ ret = -460;
+ goto on_return;
+ }
+ /* Check that callback was called with correct operation */
+ if (client->last_op != PJ_STUN_SOCK_MAPPED_ADDR_CHANGE) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status"));
+ ret = -470;
+ goto on_return;
+ }
+ /* Check that last status is still success */
+ if (client->last_status != PJ_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, " error: expecting successful status"));
+ ret = -480;
+ goto on_return;
+ }
+ /* Check that client doesn't receive anything */
+ if (client->on_rx_data_cnt != 0) {
+ PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
+ ret = -490;
+ goto on_return;
+ }
+
+ /* Get info */
+ pj_bzero(&info, sizeof(info));
+ pj_stun_sock_get_info(client->sock, &info);
+
+ /* Check that we have server address */
+ if (!pj_sockaddr_has_addr(&info.srv_addr)) {
+ PJ_LOG(3,(THIS_FILE, " error: missing server address"));
+ ret = -500;
+ goto on_return;
+ }
+ /* .. and mapped address */
+ if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
+ PJ_LOG(3,(THIS_FILE, " error: missing mapped address"));
+ ret = -510;
+ goto on_return;
+ }
+ /* verify the mapped address */
+ pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
+ if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
+ PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched"));
+ ret = -520;
+ goto on_return;
+ }
+
+ /* .. and at least one alias */
+ if (info.alias_cnt == 0) {
+ PJ_LOG(3,(THIS_FILE, " error: must have at least one alias"));
+ ret = -530;
+ goto on_return;
+ }
+ if (!pj_sockaddr_has_addr(&info.aliases[0])) {
+ PJ_LOG(3,(THIS_FILE, " error: missing alias"));
+ ret = -540;
+ goto on_return;
+ }
+
+
+ /*
+ * Part 5: Failed keep-alive
+ */
+ PJ_LOG(3,(THIS_FILE, " failed keep-alive scenario"));
+
+ /* Change server operation mode to respond without attribute */
+ srv->flag = RESPOND_STUN;
+
+ /* Reset server */
+ srv->rx_cnt = 0;
+
+ /* Reset client */
+ client->on_status_cnt = 0;
+ client->last_status = PJ_SUCCESS;
+ client->on_rx_data_cnt = 0;
+
+ /* Wait until on_status() is called with failure. */
+ pj_gettimeofday(&timeout);
+ timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + PJ_STUN_TIMEOUT_VALUE + 5);
+ do {
+ handle_events(cfg, 100);
+ pj_gettimeofday(&t);
+ } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+ /* Check that callback with correct operation is called */
+ if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status"));
+ ret = -600;
+ goto on_return;
+ }
+ if (client->last_status == PJ_SUCCESS) {
+ PJ_LOG(3,(THIS_FILE, " error: expecting failed keep-alive"));
+ ret = -610;
+ goto on_return;
+ }
+ /* Check that client doesn't receive anything */
+ if (client->on_rx_data_cnt != 0) {
+ PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
+ ret = -620;
+ goto on_return;
+ }
+
+
+on_return:
+ destroy_server(srv);
+ destroy_client(client);
+ for (i=0; i<7; ++i)
+ handle_events(cfg, 50);
+ return ret;
+}
+
+
+#define DO_TEST(expr) \
+ capture_pjlib_state(&stun_cfg, &pjlib_state); \
+ ret = expr; \
+ if (ret != 0) goto on_return; \
+ ret = check_pjlib_state(&stun_cfg, &pjlib_state); \
+ if (ret != 0) goto on_return;
+
+
+int stun_sock_test(void)
+{
+ struct pjlib_state pjlib_state;
+ pj_stun_config stun_cfg;
+ pj_ioqueue_t *ioqueue = NULL;
+ pj_timer_heap_t *timer_heap = NULL;
+ pj_pool_t *pool = NULL;
+ pj_status_t status;
+ int ret = 0;
+
+ pool = pj_pool_create(mem, NULL, 512, 512, NULL);
+
+ status = pj_ioqueue_create(pool, 12, &ioqueue);
+ if (status != PJ_SUCCESS) {
+ app_perror(" pj_ioqueue_create()", status);
+ ret = -4;
+ goto on_return;
+ }
+
+ status = pj_timer_heap_create(pool, 100, &timer_heap);
+ if (status != PJ_SUCCESS) {
+ app_perror(" pj_timer_heap_create()", status);
+ ret = -8;
+ goto on_return;
+ }
+
+ pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
+
+ DO_TEST(timeout_test(&stun_cfg, PJ_FALSE));
+ DO_TEST(timeout_test(&stun_cfg, PJ_TRUE));
+
+ DO_TEST(missing_attr_test(&stun_cfg, PJ_FALSE));
+ DO_TEST(missing_attr_test(&stun_cfg, PJ_TRUE));
+
+ DO_TEST(keep_alive_test(&stun_cfg));
+
+on_return:
+ if (timer_heap) pj_timer_heap_destroy(timer_heap);
+ if (ioqueue) pj_ioqueue_destroy(ioqueue);
+ if (pool) pj_pool_release(pool);
+ return ret;
+}
+
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8fbdd20b7b30e1d50fd7c24cc110bce0d4baaac.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8fbdd20b7b30e1d50fd7c24cc110bce0d4baaac.svn-base
new file mode 100644
index 0000000..4b5da40
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8fbdd20b7b30e1d50fd7c24cc110bce0d4baaac.svn-base
@@ -0,0 +1,116 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/except.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/errno.h>
+
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+ static const char *exception_id_names[PJ_MAX_EXCEPTION_ID];
+#else
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ static int last_exception_id = 1;
+#endif /* PJ_HAS_EXCEPTION_NAMES */
+
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+ pj_exception_id_t *id)
+{
+ unsigned i;
+
+ pj_enter_critical_section();
+
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ for (i=1; i<PJ_MAX_EXCEPTION_ID; ++i) {
+ if (exception_id_names[i] == NULL) {
+ exception_id_names[i] = name;
+ *id = i;
+ pj_leave_critical_section();
+ return PJ_SUCCESS;
+ }
+ }
+
+ pj_leave_critical_section();
+ return PJ_ETOOMANY;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, PJ_EINVAL);
+
+ pj_enter_critical_section();
+ exception_id_names[id] = NULL;
+ pj_leave_critical_section();
+
+ return PJ_SUCCESS;
+
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+ /*
+ * Start from 1 (not 0)!!!
+ * Exception 0 is reserved for normal path of setjmp()!!!
+ */
+ PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, "<Invalid ID>");
+
+ if (exception_id_names[id] == NULL)
+ return "<Unallocated ID>";
+
+ return exception_id_names[id];
+}
+
+#else /* PJ_HAS_EXCEPTION_NAMES */
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+ pj_exception_id_t *id)
+{
+ PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY);
+
+ *id = last_exception_id++;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+ return "";
+}
+
+#endif /* PJ_HAS_EXCEPTION_NAMES */
+
+
+