summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-05-26 12:17:46 +0000
committerBenny Prijono <bennylp@teluu.com>2006-05-26 12:17:46 +0000
commitc01fdece34cb0eac0c1fdbafb5c1cc242ec01933 (patch)
treee48d300bf63489fd01dd66db8b75b8b49681be6d /pjsip
parentb2627727779145512c9c414cb532cb7e18133ddd (diff)
First stage in pjsua library re-arrangements towards creating an easy to use high level API
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@476 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/build/pjsua_lib.dsp12
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h485
-rw-r--r--pjsip/include/pjsua-lib/pjsua_console_app.h31
-rw-r--r--pjsip/src/pjsip-ua/sip_reg.c10
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c136
-rw-r--r--pjsip/src/pjsua-lib/pjsua_console_app.c1015
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c1112
-rw-r--r--pjsip/src/pjsua-lib/pjsua_im.c30
-rw-r--r--pjsip/src/pjsua-lib/pjsua_imp.h95
-rw-r--r--pjsip/src/pjsua-lib/pjsua_pres.c36
-rw-r--r--pjsip/src/pjsua-lib/pjsua_reg.c32
-rw-r--r--pjsip/src/pjsua-lib/pjsua_settings.c373
12 files changed, 2430 insertions, 937 deletions
diff --git a/pjsip/build/pjsua_lib.dsp b/pjsip/build/pjsua_lib.dsp
index a49b3606..d3f42ba7 100644
--- a/pjsip/build/pjsua_lib.dsp
+++ b/pjsip/build/pjsua_lib.dsp
@@ -91,6 +91,10 @@ SOURCE="..\src\pjsua-lib\pjsua_call.c"
# End Source File
# Begin Source File
+SOURCE="..\src\pjsua-lib\pjsua_console_app.c"
+# End Source File
+# Begin Source File
+
SOURCE="..\src\pjsua-lib\pjsua_core.c"
# End Source File
# Begin Source File
@@ -99,6 +103,10 @@ SOURCE="..\src\pjsua-lib\pjsua_im.c"
# End Source File
# Begin Source File
+SOURCE="..\src\pjsua-lib\pjsua_imp.h"
+# End Source File
+# Begin Source File
+
SOURCE="..\src\pjsua-lib\pjsua_pres.c"
# End Source File
# Begin Source File
@@ -117,6 +125,10 @@ SOURCE="..\src\pjsua-lib\pjsua_settings.c"
SOURCE="..\include\pjsua-lib\pjsua.h"
# End Source File
+# Begin Source File
+
+SOURCE="..\include\pjsua-lib\pjsua_console_app.h"
+# End Source File
# End Group
# End Target
# End Project
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index a3c0c3a4..6484d729 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -48,7 +48,7 @@ PJ_BEGIN_DECL
* Max buddies in buddy list.
*/
#ifndef PJSUA_MAX_BUDDIES
-# define PJSUA_MAX_BUDDIES 32
+# define PJSUA_MAX_BUDDIES 256
#endif
@@ -61,27 +61,13 @@ PJ_BEGIN_DECL
/**
- * Aditional ports to be allocated in the conference ports for non-call
- * streams.
- */
-#define PJSUA_CONF_MORE_PORTS 3
-
-
-/**
* Maximum accounts.
*/
#ifndef PJSUA_MAX_ACC
-# define PJSUA_MAX_ACC 8
+# define PJSUA_MAX_ACC 32
#endif
-/**
- * Maximum credentials.
- */
-#ifndef PJSUA_MAX_CRED
-# define PJSUA_MAX_CRED PJSUA_MAX_ACC
-#endif
-
/**
* Structure to be attached to invite dialog.
@@ -140,24 +126,54 @@ typedef struct pjsua_srv_pres pjsua_srv_pres;
/**
+ * Account configuration.
+ */
+struct pjsua_acc_config
+{
+ /** SIP URL for account ID (mandatory) */
+ pj_str_t id;
+
+ /** Registrar URI (mandatory) */
+ pj_str_t reg_uri;
+
+ /** Optional contact URI */
+ pj_str_t contact;
+
+ /** Service proxy (default: none) */
+ pj_str_t proxy;
+
+ /** Default timeout (mandatory) */
+ pj_int32_t reg_timeout;
+
+ /** Number of credentials. */
+ unsigned cred_count;
+
+ /** Array of credentials. */
+ pjsip_cred_info cred_info[4];
+
+};
+
+
+/**
+ * @see pjsua_acc_config
+ */
+typedef struct pjsua_acc_config pjsua_acc_config;
+
+
+/**
* Account
*/
struct pjsua_acc
{
int index; /**< Index in accounts array. */
- pj_str_t local_uri; /**< Uri in From: header. */
pj_str_t user_part; /**< User part of local URI. */
pj_str_t host_part; /**< Host part of local URI. */
- pj_str_t contact_uri; /**< Uri in Contact: header. */
- pj_str_t reg_uri; /**< Registrar URI. */
pjsip_regc *regc; /**< Client registration session. */
- pj_int32_t reg_timeout; /**< Default timeout. */
pj_timer_entry reg_timer; /**< Registration timer. */
pj_status_t reg_last_err; /**< Last registration error. */
int reg_last_code; /**< Last status last register. */
- pj_str_t proxy; /**< Proxy URL. */
pjsip_route_hdr route_set; /**< Route set. */
pj_bool_t online_status; /**< Our online status. */
@@ -167,9 +183,193 @@ struct pjsua_acc
};
+/**
+ * @see pjsua_acc
+ */
typedef struct pjsua_acc pjsua_acc;
+/**
+ * PJSUA settings.
+ */
+struct pjsua_config
+{
+ /** SIP UDP signaling port. Set to zero to disable UDP signaling,
+ * which in this case application must manually add a transport
+ * to SIP endpoint.
+ * (default: 5060)
+ */
+ unsigned udp_port;
+
+ /** Optional hostname or IP address to publish as the host part of
+ * Contact header. This must be specified if UDP transport is
+ * disabled.
+ * (default: NULL)
+ */
+ pj_str_t sip_host;
+
+ /** Optional port number to publish in the port part of Contact header.
+ * This must be specified if UDP transport is disabled.
+ * (default: 0)
+ */
+ unsigned sip_port;
+
+ /** Start of RTP port. Set to zero to prevent pjsua from creating
+ * media transports, which in this case application must manually
+ * create media transport for each calls.
+ * (default: 4000)
+ */
+ unsigned start_rtp_port;
+
+ /** Maximum calls to support (default: 4) */
+ unsigned max_calls;
+
+ /** Maximum slots in the conference bridge (default: 0/calculated
+ * as max_calls*2
+ */
+ unsigned conf_ports;
+
+ /** Number of worker threads (default: 1) */
+ unsigned thread_cnt;
+
+ /** First STUN server IP address. When STUN is configured, then the
+ * two STUN server settings must be fully set.
+ * (default: none)
+ */
+ pj_str_t stun_srv1;
+
+ /** First STUN port number */
+ unsigned stun_port1;
+
+ /** Second STUN server IP address */
+ pj_str_t stun_srv2;
+
+ /** Second STUN server port number */
+ unsigned stun_port2;
+
+ /** Internal clock rate (to be applied to sound devices and conference
+ * bridge, default is 0/follows the codec, or 44100 for MacOS).
+ */
+ unsigned clock_rate;
+
+ /** Do not use sound device (default: 0). */
+ pj_bool_t null_audio;
+
+ /** WAV file to load for auto_play (default: NULL) */
+ pj_str_t wav_file;
+
+ /** Auto play WAV file for calls? (default: no) */
+ pj_bool_t auto_play;
+
+ /** Auto loopback calls? (default: no) */
+ pj_bool_t auto_loop;
+
+ /** Automatically put calls to conference? (default: no) */
+ pj_bool_t auto_conf;
+
+ /** Speex codec complexity? (default: 10) */
+ unsigned complexity;
+
+ /** Speex codec quality? (default: 10) */
+ unsigned quality;
+
+ /** Codec ptime? (default: 0 (follows the codec)) */
+ unsigned ptime;
+
+ /** Number of additional codecs/"--add-codec" with pjsua (default: 0) */
+ unsigned codec_cnt;
+
+ /** Additional codecs/"--add-codec" options */
+ pj_str_t codec_arg[32];
+
+ /** SIP status code to be automatically sent to incoming calls
+ * (default: 100).
+ */
+ unsigned auto_answer;
+
+ /** Periodic time to refresh call with re-INVITE (default: 0)
+ */
+ unsigned uas_refresh;
+
+ /** Maximum incoming call duration (default: 3600) */
+ unsigned uas_duration;
+
+ /** Outbound proxy (default: none) */
+ pj_str_t outbound_proxy;
+
+ /** URI to call. */
+ pj_str_t uri_to_call;
+
+ /** Number of SIP accounts */
+ unsigned acc_cnt;
+
+ /** SIP accounts configuration */
+ pjsua_acc_config acc_config[32];
+
+ /** Logging verbosity (default: 5). */
+ unsigned log_level;
+
+ /** Logging to be displayed to stdout (default: 4) */
+ unsigned app_log_level;
+
+ /** Log decoration */
+ unsigned log_decor;
+
+ /** Optional log filename (default: NULL) */
+ pj_str_t log_filename;
+
+ /** Number of buddies in address book (default: 0) */
+ unsigned buddy_cnt;
+
+ /** Buddies URI */
+ pj_str_t buddy_uri[256];
+};
+
+
+/**
+ * @see pjsua_config
+ */
+typedef struct pjsua_config pjsua_config;
+
+
+
+/**
+ * Application callbacks.
+ */
+struct pjsua_callback
+{
+ /**
+ * Notify UI when invite state has changed.
+ */
+ void (*on_call_state)(int call_index, pjsip_event *e);
+
+ /**
+ * Notify UI when registration status has changed.
+ */
+ void (*on_reg_state)(int acc_index);
+
+ /**
+ * Notify UI on incoming pager (i.e. MESSAGE request).
+ * Argument call_index will be -1 if MESSAGE request is not related to an
+ * existing call.
+ */
+ void (*on_pager)(int call_index, const pj_str_t *from,
+ const pj_str_t *to, const pj_str_t *txt);
+
+ /**
+ * Notify UI about typing indication.
+ */
+ void (*on_typing)(int call_index, const pj_str_t *from,
+ const pj_str_t *to, pj_bool_t is_typing);
+
+};
+
+/**
+ * @see pjsua_callback
+ */
+typedef struct pjsua_callback pjsua_callback;
+
+
/* PJSUA application variables. */
struct pjsua
{
@@ -178,79 +378,35 @@ struct pjsua
pjsip_endpoint *endpt; /**< Global endpoint. */
pj_pool_t *pool; /**< pjsua's private pool. */
pjsip_module mod; /**< pjsua's PJSIP module. */
+
+ /* Config: */
+ pjsua_config config; /**< PJSUA configs */
+
+ /* Application callback: */
+ pjsua_callback cb; /**< Application callback. */
/* Media: */
- int start_rtp_port;/**< Start of RTP port to try. */
pjmedia_endpt *med_endpt; /**< Media endpoint. */
- unsigned clock_rate; /**< Internal clock rate. */
pjmedia_conf *mconf; /**< Media conference. */
- pj_bool_t null_audio; /**< Null audio flag. */
- pj_bool_t no_mic; /**< Disable microphone. */
- char *wav_file; /**< WAV file name to play. */
unsigned wav_slot; /**< WAV player slot in bridge */
pjmedia_port *file_port; /**< WAV player port. */
- pjmedia_port *null_port; /**< NULL port. */
- pj_bool_t auto_play; /**< Auto play file for calls? */
- pj_bool_t auto_loop; /**< Auto loop RTP stream? */
- pj_bool_t auto_conf; /**< Auto put to conference? */
- int complexity; /**< Codec complexity. */
- int quality; /**< Codec quality. */
- int ptime; /**< Codec ptime in msec. */
-
- /* Codec arguments: */
- int codec_cnt; /**< Number of --add-codec args. */
- pj_str_t codec_arg[32]; /**< Array of --add-codec args. */
- pj_status_t (*codec_deinit[32])(void); /**< Array of funcs. */
-
- /* User Agent behaviour: */
- int auto_answer; /**< Automatically answer in calls. */
- int uas_refresh; /**< Time to re-INVITE. */
- int uas_duration; /**< Max call duration. */
/* Account: */
- pj_bool_t has_acc; /**< Any --id cmdline? */
- int acc_cnt; /**< Number of client registrations */
pjsua_acc acc[PJSUA_MAX_ACC]; /** Client regs array. */
- /* Authentication credentials: */
-
- int cred_count; /**< Number of credentials. */
- pjsip_cred_info cred_info[10]; /**< Array of credentials. */
-
-
/* Threading (optional): */
- int thread_cnt; /**< Thread count. */
pj_thread_t *threads[8]; /**< Thread instances. */
pj_bool_t quit_flag; /**< To signal thread to quit. */
/* Transport (UDP): */
- pj_uint16_t sip_port; /**< SIP signaling port. */
pj_sock_t sip_sock; /**< SIP UDP socket. */
pj_sockaddr_in sip_sock_name; /**< Public/STUN UDP socket addr. */
- pj_str_t outbound_proxy;/**< Outbound proxy. */
-
-
- /* STUN: */
- pj_str_t stun_srv1;
- int stun_port1;
- pj_str_t stun_srv2;
- int stun_port2;
-
-
- /* Logging: */
- int log_level; /**< Logging verbosity. */
- int app_log_level; /**< stdout log verbosity. */
- unsigned log_decor; /**< Log decoration. */
- char *log_filename; /**< Log filename. */
-
/* PJSUA Calls: */
- pj_str_t uri_to_call; /**< URI to call. */
- int max_calls; /**< Max nb of calls. */
int call_cnt; /**< Number of calls. */
pjsua_call calls[PJSUA_MAX_CALLS]; /** Calls array. */
@@ -273,26 +429,35 @@ extern struct pjsua pjsua;
/**
* Initialize pjsua settings with default parameters.
*/
-void pjsua_default(void);
+PJ_DECL(void) pjsua_default_config(pjsua_config *cfg);
/**
- * Display error message for the specified error code.
+ * Test configuration.
*/
-void pjsua_perror(const char *sender, const char *title,
- pj_status_t status);
+PJ_DECL(pj_status_t) pjsua_test_config(const pjsua_config *cfg,
+ char *errmsg,
+ int len);
/**
- * Initialize pjsua application. Application can call this before parsing
- * application settings.
+ * Create pjsua application.
+ * This initializes pjlib/pjlib-util, and creates memory pool factory to
+ * be used by application.
+ */
+PJ_DECL(pj_status_t) pjsua_create(void);
+
+
+/**
+ * Initialize pjsua application with the specified settings.
*
* This will initialize all libraries, create endpoint instance, and register
- * pjsip modules. Transport will NOT be created however.
+ * pjsip modules.
*
* Application may register module after calling this function.
*/
-pj_status_t pjsua_init(void);
+PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg,
+ const pjsua_callback *cb);
/**
@@ -302,25 +467,13 @@ pj_status_t pjsua_init(void);
* This will start the transport, worker threads (if any), and registration
* process, if registration is configured.
*/
-pj_status_t pjsua_start(void);
+PJ_DECL(pj_status_t) pjsua_start(void);
/**
* Destroy pjsua.
*/
-pj_status_t pjsua_destroy(void);
-
-
-/**
- * Find account for incoming request.
- */
-int pjsua_find_account_for_incoming(pjsip_rx_data *rdata);
-
-
-/**
- * Find account for outgoing request.
- */
-int pjsua_find_account_for_outgoing(const pj_str_t *url);
+PJ_DECL(pj_status_t) pjsua_destroy(void);
/*****************************************************************************
@@ -328,68 +481,57 @@ int pjsua_find_account_for_outgoing(const pj_str_t *url);
*/
/**
- * Init pjsua call module.
- */
-pj_status_t pjsua_call_init(void);
-
-/**
* Make outgoing call.
*/
-pj_status_t pjsua_make_call(int acc_index,
- const char *cstr_dest_uri,
- int *p_call_index);
-
-
-/**
- * Handle incoming invite request.
- */
-pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata);
+PJ_DECL(pj_status_t) pjsua_make_call(int acc_index,
+ const char *cstr_dest_uri,
+ int *p_call_index);
/**
* Answer call.
*/
-void pjsua_call_answer(int call_index, int code);
+PJ_DECL(void) pjsua_call_answer(int call_index, int code);
/**
* Hangup call.
*/
-void pjsua_call_hangup(int call_index);
+PJ_DECL(void) pjsua_call_hangup(int call_index);
/**
* Put call on-hold.
*/
-void pjsua_call_set_hold(int call_index);
+PJ_DECL(void) pjsua_call_set_hold(int call_index);
/**
* Send re-INVITE (to release hold).
*/
-void pjsua_call_reinvite(int call_index);
+PJ_DECL(void) pjsua_call_reinvite(int call_index);
/**
* Transfer call.
*/
-void pjsua_call_xfer(int call_index, const char *dest);
+PJ_DECL(void) pjsua_call_xfer(int call_index, const char *dest);
/**
* Send instant messaging inside INVITE session.
*/
-void pjsua_call_send_im(int call_index, const char *text);
+PJ_DECL(void) pjsua_call_send_im(int call_index, const char *text);
/**
* Send IM typing indication inside INVITE session.
*/
-void pjsua_call_typing(int call_index, pj_bool_t is_typing);
+PJ_DECL(void) pjsua_call_typing(int call_index, pj_bool_t is_typing);
/**
* Terminate all calls.
*/
-void pjsua_call_hangup_all(void);
+PJ_DECL(void) pjsua_call_hangup_all(void);
/*****************************************************************************
@@ -397,17 +539,10 @@ void pjsua_call_hangup_all(void);
*/
/**
- * Initialize client registration session.
- *
- * @param app_callback Optional callback
- */
-pj_status_t pjsua_regc_init(int acc_index);
-
-/**
* Update registration or perform unregistration. If renew argument is zero,
* this will start unregistration process.
*/
-void pjsua_regc_update(int acc_index, pj_bool_t renew);
+PJ_DECL(void) pjsua_regc_update(int acc_index, pj_bool_t renew);
@@ -417,24 +552,14 @@ void pjsua_regc_update(int acc_index, pj_bool_t renew);
*/
/**
- * Init presence.
- */
-pj_status_t pjsua_pres_init();
-
-/**
* Refresh both presence client and server subscriptions.
*/
-void pjsua_pres_refresh(int acc_index);
-
-/**
- * Terminate all subscriptions
- */
-void pjsua_pres_shutdown(void);
+PJ_DECL(void) pjsua_pres_refresh(int acc_index);
/**
* Dump presence subscriptions.
*/
-void pjsua_pres_dump(pj_bool_t detail);
+PJ_DECL(void) pjsua_pres_dump(pj_bool_t detail);
/*****************************************************************************
@@ -447,79 +572,21 @@ void pjsua_pres_dump(pj_bool_t detail);
extern const pjsip_method pjsip_message_method;
-/**
- * Init IM module handler to handle incoming MESSAGE outside dialog.
- */
-pj_status_t pjsua_im_init();
-
-
-/**
- * Create Accept header for MESSAGE.
- */
-pjsip_accept_hdr* pjsua_im_create_accept(pj_pool_t *pool);
/**
* Send IM outside dialog.
*/
-pj_status_t pjsua_im_send(int acc_index, const char *dst_uri,
- const char *text);
+PJ_DECL(pj_status_t) pjsua_im_send(int acc_index, const char *dst_uri,
+ const char *text);
/**
* Send typing indication outside dialog.
*/
-pj_status_t pjsua_im_typing(int acc_index, const char *dst_uri,
- pj_bool_t is_typing);
-
-
-/**
- * Private: check if we can accept the message.
- * If not, then p_accept header will be filled with a valid
- * Accept header.
- */
-pj_bool_t pjsua_im_accept_pager(pjsip_rx_data *rdata,
- pjsip_accept_hdr **p_accept_hdr);
-
-/**
- * Private: process pager message.
- * This may trigger pjsua_ui_on_pager() or pjsua_ui_on_typing().
- */
-void pjsua_im_process_pager(int call_id, const pj_str_t *from,
- const pj_str_t *to, pjsip_rx_data *rdata);
-
-
-/*****************************************************************************
- * User Interface API.
- *
- * The UI API specifies functions that will be called by pjsua upon
- * occurence of various events.
- */
-
-/**
- * Notify UI when invite state has changed.
- */
-void pjsua_ui_on_call_state(int call_index, pjsip_event *e);
-
-/**
- * Notify UI when registration status has changed.
- */
-void pjsua_ui_on_reg_state(int acc_index);
-
-/**
- * Notify UI on incoming pager (i.e. MESSAGE request).
- * Argument call_index will be -1 if MESSAGE request is not related to an
- * existing call.
- */
-void pjsua_ui_on_pager(int call_index, const pj_str_t *from,
- const pj_str_t *to, const pj_str_t *txt);
+PJ_DECL(pj_status_t) pjsua_im_typing(int acc_index, const char *dst_uri,
+ pj_bool_t is_typing);
-/**
- * Notify UI about typing indication.
- */
-void pjsua_ui_on_typing(int call_index, const pj_str_t *from,
- const pj_str_t *to, pj_bool_t is_typing);
-
/*****************************************************************************
* Utilities.
@@ -532,34 +599,46 @@ extern const char *pjsua_inv_state_names[];
/**
* Parse arguments (pjsua_opt.c).
*/
-pj_status_t pjsua_parse_args(int argc, char *argv[]);
+PJ_DECL(pj_status_t) pjsua_parse_args(int argc, char *argv[],
+ pjsua_config *cfg);
/**
* Load settings from a file.
*/
-pj_status_t pjsua_load_settings(const char *filename);
+PJ_DECL(pj_status_t) pjsua_load_settings(const char *filename,
+ pjsua_config *cfg);
/**
* Dump settings.
*/
-int pjsua_dump_settings(char *buf, pj_size_t max);
+PJ_DECL(int) pjsua_dump_settings(const pjsua_config *cfg,
+ char *buf, pj_size_t max);
/**
* Save settings to a file.
*/
-pj_status_t pjsua_save_settings(const char *filename);
+PJ_DECL(pj_status_t) pjsua_save_settings(const char *filename,
+ const pjsua_config *cfg);
/*
* Verify that valid SIP url is given.
* @return PJ_SUCCESS if valid.
*/
-pj_status_t pjsua_verify_sip_url(const char *c_url);
+PJ_DECL(pj_status_t) pjsua_verify_sip_url(const char *c_url);
/*
* Dump application states.
*/
-void pjsua_dump(pj_bool_t detail);
+PJ_DECL(void) pjsua_dump(pj_bool_t detail);
+
+/**
+ * Display error message for the specified error code.
+ */
+PJ_DECL(void) pjsua_perror(const char *sender, const char *title,
+ pj_status_t status);
+
+
PJ_END_DECL
diff --git a/pjsip/include/pjsua-lib/pjsua_console_app.h b/pjsip/include/pjsua-lib/pjsua_console_app.h
new file mode 100644
index 00000000..058d0637
--- /dev/null
+++ b/pjsip/include/pjsua-lib/pjsua_console_app.h
@@ -0,0 +1,31 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSUA_CONSOLE_APP_H__
+#define __PJSUA_CONSOLE_APP_H__
+
+
+pj_status_t pjsua_console_app_logging_init(const pjsua_config *cfg);
+void pjsua_console_app_logging_shutdown(void);
+
+void pjsua_console_app_main(void);
+
+extern pjsip_module pjsua_console_app_msg_logger;
+extern pjsua_callback console_callback;
+
+#endif /* __PJSUA_CONSOLE_APP_H__ */
diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
index 84fb57dd..6272e34c 100644
--- a/pjsip/src/pjsip-ua/sip_reg.c
+++ b/pjsip/src/pjsip-ua/sip_reg.c
@@ -484,6 +484,7 @@ static void tsx_callback(void *token, pjsip_event *event)
pjsip_transaction *tsx = event->body.tsx_state.tsx;
/* Decrement pending transaction counter. */
+ pj_assert(regc->pending_tsx > 0);
--regc->pending_tsx;
/* If registration data has been deleted by user then remove registration
@@ -609,10 +610,13 @@ PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
cseq_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
cseq_hdr->cseq = cseq;
- /* Send. */
+ /* Increment pending transaction first, since transaction callback
+ * may be called even before send_request() returns!
+ */
+ ++regc->pending_tsx;
status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
- if (status==PJ_SUCCESS)
- ++regc->pending_tsx;
+ if (status!=PJ_SUCCESS)
+ --regc->pending_tsx;
return status;
}
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index 4dbfb596..d6013a13 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -18,7 +18,7 @@
*/
#include <pjsua-lib/pjsua.h>
#include <pj/log.h>
-
+#include "pjsua_imp.h"
/*
* pjsua_call.c
@@ -56,7 +56,8 @@ static void call_on_timer(pj_timer_heap_t *ht, pj_timer_entry *e)
pjsua_call_hangup(call->index);
} else {
PJ_LOG(3,(THIS_FILE, "Refreshing call %d", call->index));
- schedule_call_timer(call,e,REFRESH_CALL_TIMER,pjsua.uas_refresh);
+ schedule_call_timer(call,e,REFRESH_CALL_TIMER,
+ pjsua.config.uas_refresh);
pjsua_call_reinvite(call->index);
}
@@ -126,15 +127,15 @@ static pj_status_t call_destroy_media(int call_index)
/**
* Make outgoing call.
*/
-pj_status_t pjsua_make_call(int acc_index,
- const char *cstr_dest_uri,
- int *p_call_index)
+PJ_DEF(pj_status_t) pjsua_make_call(int acc_index,
+ const char *cstr_dest_uri,
+ int *p_call_index)
{
pj_str_t dest_uri;
pjsip_dialog *dlg = NULL;
pjmedia_sdp_session *offer;
pjsip_inv_session *inv = NULL;
- int call_index = -1;
+ unsigned call_index;
pjsip_tx_data *tdata;
pj_status_t status;
@@ -143,12 +144,12 @@ pj_status_t pjsua_make_call(int acc_index,
dest_uri = pj_str((char*)cstr_dest_uri);
/* Find free call slot. */
- for (call_index=0; call_index<pjsua.max_calls; ++call_index) {
+ for (call_index=0; call_index<pjsua.config.max_calls; ++call_index) {
if (pjsua.calls[call_index].inv == NULL)
break;
}
- if (call_index == pjsua.max_calls) {
+ if (call_index == pjsua.config.max_calls) {
PJ_LOG(3,(THIS_FILE, "Error: too many calls!"));
return PJ_ETOOMANY;
}
@@ -161,8 +162,8 @@ pj_status_t pjsua_make_call(int acc_index,
/* Create outgoing dialog: */
status = pjsip_dlg_create_uac( pjsip_ua_instance(),
- &pjsua.acc[acc_index].local_uri,
- &pjsua.acc[acc_index].contact_uri,
+ &pjsua.config.acc_config[acc_index].id,
+ &pjsua.config.acc_config[acc_index].contact,
&dest_uri, &dest_uri,
&dlg);
if (status != PJ_SUCCESS) {
@@ -204,9 +205,12 @@ pj_status_t pjsua_make_call(int acc_index,
/* Set credentials: */
-
- pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count,
- pjsua.cred_info);
+ if (pjsua.config.acc_config[acc_index].cred_count) {
+ pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index];
+ pjsip_auth_clt_set_credentials( &dlg->auth_sess,
+ acc_cfg->cred_count,
+ acc_cfg->cred_info);
+ }
/* Create initial INVITE: */
@@ -225,6 +229,12 @@ pj_status_t pjsua_make_call(int acc_index,
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
status);
+
+ /* Upon failure to send first request, both dialog and invite
+ * session would have been cleared.
+ */
+ inv = NULL;
+ dlg = NULL;
goto on_error;
}
@@ -242,7 +252,7 @@ pj_status_t pjsua_make_call(int acc_index,
on_error:
if (inv != NULL) {
pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
- } else {
+ } else if (dlg) {
pjsip_dlg_terminate(dlg);
}
@@ -254,6 +264,36 @@ on_error:
/**
+ * Answer call.
+ */
+PJ_DEF(void) pjsua_call_answer(int call_index, int code)
+{
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_ASSERT_ON_FAIL(call_index >= 0 &&
+ call_index < (int)pjsua.config.max_calls,
+ return);
+
+ if (pjsua.calls[call_index].inv == NULL) {
+ PJ_LOG(3,(THIS_FILE, "Call %d already disconnected"));
+ return;
+ }
+
+ status = pjsip_inv_answer(pjsua.calls[call_index].inv,
+ code, NULL, NULL, &tdata);
+ if (status == PJ_SUCCESS)
+ status = pjsip_inv_send_msg(pjsua.calls[call_index].inv,
+ tdata);
+
+ if (status != PJ_SUCCESS)
+ pjsua_perror(THIS_FILE, "Unable to create/send response",
+ status);
+
+}
+
+
+/**
* Handle incoming INVITE request.
*/
pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
@@ -265,7 +305,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
unsigned options = 0;
pjsip_inv_session *inv = NULL;
int acc_index;
- int call_index = -1;
+ unsigned call_index;
pjmedia_sdp_session *answer;
pj_status_t status;
@@ -312,7 +352,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
*/
/* Find free call slot. */
- for (call_index=0; call_index < pjsua.max_calls; ++call_index) {
+ for (call_index=0; call_index < pjsua.config.max_calls; ++call_index) {
if (pjsua.calls[call_index].inv == NULL)
break;
}
@@ -353,7 +393,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
/* Create dialog: */
status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
- &pjsua.acc[acc_index].contact_uri,
+ &pjsua.config.acc_config[acc_index].contact,
&dlg);
if (status != PJ_SUCCESS) {
pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
@@ -387,8 +427,8 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
*/
status = pjsip_inv_initial_answer(inv, rdata,
- (pjsua.auto_answer ? pjsua.auto_answer
- : 100),
+ (pjsua.config.auto_answer ?
+ pjsua.config.auto_answer : 100),
NULL, NULL, &response);
if (status != PJ_SUCCESS) {
@@ -400,7 +440,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
/* If failed to send 2xx response, there's a good chance that it is
* because SDP negotiation has failed.
*/
- if (pjsua.auto_answer/100 == 2)
+ if (pjsua.config.auto_answer/100 == 2)
st_code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
else
st_code = 500;
@@ -415,7 +455,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
}
- if (pjsua.auto_answer < 200) {
+ if (pjsua.config.auto_answer < 200) {
PJ_LOG(3,(THIS_FILE,
"\nIncoming call!!\n"
"From: %.*s\n"
@@ -432,26 +472,26 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
dlg->remote.info_str.ptr,
(int)dlg->local.info_str.slen,
dlg->local.info_str.ptr,
- pjsua.auto_answer,
- pjsip_get_status_text(pjsua.auto_answer)->ptr ));
+ pjsua.config.auto_answer,
+ pjsip_get_status_text(pjsua.config.auto_answer)->ptr ));
}
++pjsua.call_cnt;
/* Schedule timer to refresh. */
- if (pjsua.uas_refresh > 0) {
+ if (pjsua.config.uas_refresh > 0) {
schedule_call_timer( &pjsua.calls[call_index],
&pjsua.calls[call_index].refresh_tm,
REFRESH_CALL_TIMER,
- pjsua.uas_refresh);
+ pjsua.config.uas_refresh);
}
/* Schedule timer to hangup call. */
- if (pjsua.uas_duration > 0) {
+ if (pjsua.config.uas_duration > 0) {
schedule_call_timer( &pjsua.calls[call_index],
&pjsua.calls[call_index].hangup_tm,
HANGUP_CALL_TIMER,
- pjsua.uas_duration);
+ pjsua.config.uas_duration);
}
/* This INVITE request has been handled. */
@@ -550,7 +590,8 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
}
- pjsua_ui_on_call_state(call->index, e);
+ if (pjsua.cb.on_call_state)
+ (*pjsua.cb.on_call_state)(call->index, e);
/* call->inv may be NULL now */
@@ -965,7 +1006,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
return;
}
- if (pjsua.null_audio)
+ if (pjsua.config.null_audio)
return;
/* Create media session info based on SDP parameters.
@@ -983,9 +1024,10 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
}
/* Override ptime, if this option is specified. */
- if (pjsua.ptime) {
+ if (pjsua.config.ptime) {
sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
- (pjsua.ptime / sess_info.stream_info[0].param->info.frm_ptime);
+ (pjsua.config.ptime /
+ sess_info.stream_info[0].param->info.frm_ptime);
if (sess_info.stream_info[0].param->setting.frm_per_pkt==0)
sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
}
@@ -1037,7 +1079,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
/* If auto-play is configured, connect the call to the file player
* port
*/
- if (pjsua.auto_play && pjsua.wav_file &&
+ if (pjsua.config.auto_play && pjsua.config.wav_file.slen &&
call->inv->role == PJSIP_ROLE_UAS)
{
@@ -1045,19 +1087,19 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
call->conf_slot, 0);
}
- if (pjsua.auto_loop && call->inv->role == PJSIP_ROLE_UAS) {
+ if (pjsua.config.auto_loop && call->inv->role == PJSIP_ROLE_UAS) {
pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
call->conf_slot, 0);
}
- if (pjsua.auto_conf) {
- int i;
+ if (pjsua.config.auto_conf) {
+ unsigned i;
pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
- for (i=0; i < pjsua.max_calls; ++i) {
+ for (i=0; i < pjsua.config.max_calls; ++i) {
if (!pjsua.calls[i].session)
continue;
@@ -1073,8 +1115,8 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
/* Normal operation: if no auto_xx is given, connect new call to
* the sound device port (port zero) in the main conference bridge.
*/
- if (pjsua.auto_play == 0 && pjsua.auto_loop == 0 &&
- pjsua.auto_conf == 0)
+ if (pjsua.config.auto_play == 0 && pjsua.config.auto_loop == 0 &&
+ pjsua.config.auto_conf == 0)
{
pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
@@ -1127,7 +1169,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
/*
* Hangup call.
*/
-void pjsua_call_hangup(int call_index)
+PJ_DEF(void) pjsua_call_hangup(int call_index)
{
pjsua_call *call;
int code;
@@ -1177,7 +1219,7 @@ void pjsua_call_hangup(int call_index)
/*
* Put call on-Hold.
*/
-void pjsua_call_set_hold(int call_index)
+PJ_DEF(void) pjsua_call_set_hold(int call_index)
{
pjmedia_sdp_session *sdp;
pjsua_call *call;
@@ -1218,7 +1260,7 @@ void pjsua_call_set_hold(int call_index)
/*
* re-INVITE.
*/
-void pjsua_call_reinvite(int call_index)
+PJ_DEF(void) pjsua_call_reinvite(int call_index)
{
pjmedia_sdp_session *sdp;
pjsip_tx_data *tdata;
@@ -1265,7 +1307,7 @@ void pjsua_call_reinvite(int call_index)
/*
* Transfer call.
*/
-void pjsua_call_xfer(int call_index, const char *dest)
+PJ_DEF(void) pjsua_call_xfer(int call_index, const char *dest)
{
pjsip_evsub *sub;
pjsip_tx_data *tdata;
@@ -1317,7 +1359,7 @@ void pjsua_call_xfer(int call_index, const char *dest)
/**
* Send instant messaging inside INVITE session.
*/
-void pjsua_call_send_im(int call_index, const char *str)
+PJ_DECL(void) pjsua_call_send_im(int call_index, const char *str)
{
pjsua_call *call;
const pj_str_t mime_text = pj_str("text");
@@ -1373,7 +1415,7 @@ on_return:
/**
* Send IM typing indication inside INVITE session.
*/
-void pjsua_call_typing(int call_index, pj_bool_t is_typing)
+PJ_DECL(void) pjsua_call_typing(int call_index, pj_bool_t is_typing)
{
pjsua_call *call;
pjsip_tx_data *tdata;
@@ -1415,11 +1457,11 @@ on_return:
/*
* Terminate all calls.
*/
-void pjsua_call_hangup_all(void)
+PJ_DEF(void) pjsua_call_hangup_all(void)
{
- int i;
+ unsigned i;
- for (i=0; i<pjsua.max_calls; ++i) {
+ for (i=0; i<pjsua.config.max_calls; ++i) {
pjsip_tx_data *tdata;
int st_code;
pjsua_call *call;
diff --git a/pjsip/src/pjsua-lib/pjsua_console_app.c b/pjsip/src/pjsua-lib/pjsua_console_app.c
new file mode 100644
index 00000000..c2f94e62
--- /dev/null
+++ b/pjsip/src/pjsua-lib/pjsua_console_app.c
@@ -0,0 +1,1015 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjsua-lib/pjsua.h>
+#include <pjsua-lib/pjsua_console_app.h>
+#include <stdlib.h> /* atoi */
+#include <stdio.h>
+
+#define THIS_FILE "main.c"
+
+/* Current dialog */
+static int current_acc;
+static int current_call = -1;
+
+
+/*
+ * Find next call.
+ */
+static pj_bool_t find_next_call(void)
+{
+ int i;
+
+ for (i=current_call+1; i<(int)pjsua.config.max_calls; ++i) {
+ if (pjsua.calls[i].inv != NULL) {
+ current_call = i;
+ return PJ_TRUE;
+ }
+ }
+
+ for (i=0; i<current_call; ++i) {
+ if (pjsua.calls[i].inv != NULL) {
+ current_call = i;
+ return PJ_TRUE;
+ }
+ }
+
+ current_call = -1;
+ return PJ_FALSE;
+}
+
+
+/*
+ * Find previous call.
+ */
+static pj_bool_t find_prev_call(void)
+{
+ int i;
+
+ for (i=current_call-1; i>=0; --i) {
+ if (pjsua.calls[i].inv != NULL) {
+ current_call = i;
+ return PJ_TRUE;
+ }
+ }
+
+ for (i=pjsua.config.max_calls-1; i>current_call; --i) {
+ if (pjsua.calls[i].inv != NULL) {
+ current_call = i;
+ return PJ_TRUE;
+ }
+ }
+
+ current_call = -1;
+ return PJ_FALSE;
+}
+
+
+
+/*
+ * Notify UI when invite state has changed.
+ */
+static void console_on_call_state(int call_index, pjsip_event *e)
+{
+ pjsua_call *call = &pjsua.calls[call_index];
+
+ PJ_UNUSED_ARG(e);
+
+ if (call->inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+
+ PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
+ call_index,
+ call->inv->cause,
+ pjsip_get_status_text(call->inv->cause)->ptr));
+
+ call->inv = NULL;
+ if ((int)call->index == current_call) {
+ find_next_call();
+ }
+
+ } else {
+
+ PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
+ call_index,
+ pjsua_inv_state_names[call->inv->state]));
+
+ if (call && current_call==-1)
+ current_call = call->index;
+
+ }
+}
+
+/**
+ * Notify UI when registration status has changed.
+ */
+static void console_on_reg_state(int acc_index)
+{
+ PJ_UNUSED_ARG(acc_index);
+
+ // Log already written.
+}
+
+
+/**
+ * Incoming IM message (i.e. MESSAGE request)!
+ */
+static void console_on_pager(int call_index, const pj_str_t *from,
+ const pj_str_t *to, const pj_str_t *text)
+{
+ /* Note: call index may be -1 */
+ PJ_UNUSED_ARG(call_index);
+ PJ_UNUSED_ARG(to);
+
+ PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s",
+ (int)from->slen, from->ptr,
+ (int)text->slen, text->ptr));
+}
+
+
+/**
+ * Typing indication
+ */
+static void console_on_typing(int call_index, const pj_str_t *from,
+ const pj_str_t *to, pj_bool_t is_typing)
+{
+ PJ_UNUSED_ARG(call_index);
+ PJ_UNUSED_ARG(to);
+
+ PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
+ (int)from->slen, from->ptr,
+ (is_typing?"is typing..":"has stopped typing")));
+}
+
+
+/*
+ * Print buddy list.
+ */
+static void print_buddy_list(void)
+{
+ int i;
+
+ puts("Buddy list:");
+
+ if (pjsua.buddy_cnt == 0)
+ puts(" -none-");
+ else {
+ for (i=0; i<pjsua.buddy_cnt; ++i) {
+ const char *status;
+
+ if (pjsua.buddies[i].sub == NULL ||
+ pjsua.buddies[i].status.info_cnt==0)
+ {
+ status = " ? ";
+ }
+ else if (pjsua.buddies[i].status.info[0].basic_open)
+ status = " Online";
+ else
+ status = "Offline";
+
+ printf(" [%2d] <%s> %s\n",
+ i+1, status, pjsua.buddies[i].uri.ptr);
+ }
+ }
+ puts("");
+}
+
+
+/*
+ * Print account status.
+ */
+static void print_acc_status(int acc_index)
+{
+ char reg_status[128];
+
+ if (pjsua.acc[acc_index].regc == NULL) {
+ pj_ansi_strcpy(reg_status, " -not registered to server-");
+
+ } else if (pjsua.acc[acc_index].reg_last_err != PJ_SUCCESS) {
+ pj_strerror(pjsua.acc[acc_index].reg_last_err, reg_status, sizeof(reg_status));
+
+ } else if (pjsua.acc[acc_index].reg_last_code>=200 &&
+ pjsua.acc[acc_index].reg_last_code<=699) {
+
+ pjsip_regc_info info;
+ const pj_str_t *status_str;
+
+ pjsip_regc_get_info(pjsua.acc[acc_index].regc, &info);
+
+ status_str = pjsip_get_status_text(pjsua.acc[acc_index].reg_last_code);
+ pj_ansi_snprintf(reg_status, sizeof(reg_status),
+ "%s (%.*s;expires=%d)",
+ status_str->ptr,
+ (int)info.client_uri.slen,
+ info.client_uri.ptr,
+ info.next_reg);
+
+ } else {
+ pj_ansi_sprintf(reg_status, "in progress (%d)",
+ pjsua.acc[acc_index].reg_last_code);
+ }
+
+ printf("[%2d] Registration status: %s\n", acc_index, reg_status);
+ printf(" Online status: %s\n",
+ (pjsua.acc[acc_index].online_status ? "Online" : "Invisible"));
+}
+
+/*
+ * Show a bit of help.
+ */
+static void keystroke_help(void)
+{
+ int i;
+
+ printf(">>>>\n");
+
+ for (i=0; i<(int)pjsua.config.acc_cnt; ++i)
+ print_acc_status(i);
+
+ print_buddy_list();
+
+ //puts("Commands:");
+ puts("+=============================================================================+");
+ puts("| Call Commands: | IM & Presence: | Misc: |");
+ puts("| | | |");
+ puts("| m Make new call | i Send IM | o Send OPTIONS |");
+ puts("| M Make multiple calls | s Subscribe presence | rr (Re-)register |");
+ puts("| a Answer call | u Unsubscribe presence | ru Unregister |");
+ puts("| h Hangup call (ha=all) | t ToGgle Online status | |");
+ puts("| H Hold call | | |");
+ puts("| v re-inVite (release hold) +--------------------------+-------------------+");
+ puts("| ] Select next dialog | Conference Command | |");
+ puts("| [ Select previous dialog | cl List ports | d Dump status |");
+ puts("| x Xfer call | cc Connect port | dd Dump detailed |");
+ puts("| # Send DTMF string | cd Disconnect port | dc Dump config |");
+ puts("+------------------------------+--------------------------+-------------------+");
+ puts("| q QUIT |");
+ puts("+=============================================================================+");
+}
+
+
+/*
+ * Input simple string
+ */
+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);
+ fgets(buf, len, stdin);
+
+ /* 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;
+}
+
+
+#define NO_NB -2
+struct input_result
+{
+ int nb_result;
+ char *uri_result;
+};
+
+
+/*
+ * Input URL.
+ */
+static void ui_input_url(const char *title, char *buf, int len,
+ struct input_result *result)
+{
+ result->nb_result = 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.buddy_cnt, pjsua.buddy_cnt);
+ printf("%s: ", title);
+
+ fflush(stdout);
+ fgets(buf, len, stdin);
+ 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=='-') {
+
+ int i;
+
+ if (*buf=='-')
+ i = 1;
+ else
+ i = 0;
+
+ for (; i<len; ++i) {
+ if (!pj_isdigit(buf[i])) {
+ puts("Invalid input");
+ return;
+ }
+ }
+
+ result->nb_result = atoi(buf);
+
+ if (result->nb_result >= 0 && result->nb_result <= (int)pjsua.buddy_cnt) {
+ return;
+ }
+ if (result->nb_result == -1)
+ return;
+
+ puts("Invalid input");
+ result->nb_result = NO_NB;
+ return;
+
+ } else {
+ pj_status_t status;
+
+ if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Invalid URL", status);
+ return;
+ }
+
+ result->uri_result = buf;
+ }
+}
+
+static void conf_list(void)
+{
+ unsigned i, count;
+ pjmedia_conf_port_info info[PJSUA_MAX_CALLS];
+
+ printf("Conference ports:\n");
+
+ count = PJ_ARRAY_SIZE(info);
+ pjmedia_conf_get_ports_info(pjsua.mconf, &count, info);
+ for (i=0; i<count; ++i) {
+ char txlist[PJSUA_MAX_CALLS*4+10];
+ int j;
+ pjmedia_conf_port_info *port_info = &info[i];
+
+ txlist[0] = '\0';
+ for (j=0; j<(int)count; ++j) {
+ char s[10];
+ if (port_info->listener[j]) {
+ pj_ansi_sprintf(s, "#%d ", j);
+ pj_ansi_strcat(txlist, s);
+ }
+ }
+ printf("Port #%02d[%2dKHz/%dms] %20.*s transmitting to: %s\n",
+ port_info->slot,
+ port_info->clock_rate/1000,
+ port_info->samples_per_frame * 1000 / port_info->clock_rate,
+ (int)port_info->name.slen,
+ port_info->name.ptr,
+ txlist);
+
+ }
+ puts("");
+}
+
+
+void pjsua_console_app_main(void)
+{
+ char menuin[10];
+ char buf[128];
+ char text[128];
+ int i, count;
+ char *uri;
+ struct input_result result;
+
+
+ /* If user specifies URI to call, then call the URI */
+ if (pjsua.config.uri_to_call.slen) {
+ pjsua_make_call( current_acc, pjsua.config.uri_to_call.ptr, NULL);
+ }
+
+ keystroke_help();
+
+ for (;;) {
+
+ printf(">>> ");
+ fflush(stdout);
+
+ fgets(menuin, sizeof(menuin), stdin);
+
+ switch (menuin[0]) {
+
+ case 'm':
+ /* Make call! : */
+ printf("(You currently have %d calls)\n", pjsua.call_cnt);
+
+ uri = NULL;
+ ui_input_url("Make call", buf, sizeof(buf), &result);
+ if (result.nb_result != NO_NB) {
+
+ if (result.nb_result == -1 || result.nb_result == 0) {
+ puts("You can't do that with make call!");
+ continue;
+ } else {
+ uri = pjsua.buddies[result.nb_result-1].uri.ptr;
+ }
+
+ } else if (result.uri_result) {
+ uri = result.uri_result;
+ }
+
+ pjsua_make_call( current_acc, uri, NULL);
+ break;
+
+ case 'M':
+ /* Make multiple calls! : */
+ printf("(You currently have %d calls)\n", pjsua.call_cnt);
+
+ if (!simple_input("Number of calls", menuin, sizeof(menuin)))
+ continue;
+
+ count = atoi(menuin);
+ if (count < 1)
+ continue;
+
+ ui_input_url("Make call", buf, sizeof(buf), &result);
+ if (result.nb_result != NO_NB) {
+ if (result.nb_result == -1 || result.nb_result == 0) {
+ puts("You can't do that with make call!");
+ continue;
+ }
+ uri = pjsua.buddies[result.nb_result-1].uri.ptr;
+ } else {
+ uri = result.uri_result;
+ }
+
+ for (i=0; i<atoi(menuin); ++i) {
+ pj_status_t status;
+
+ status = pjsua_make_call(current_acc, uri, NULL);
+ if (status != PJ_SUCCESS)
+ break;
+ }
+ break;
+
+ case 'i':
+ /* Send instant messaeg */
+
+ /* i is for call index to send message, if any */
+ i = -1;
+
+ /* Make compiler happy. */
+ uri = NULL;
+
+ /* Input destination. */
+ ui_input_url("Send IM to", buf, sizeof(buf), &result);
+ if (result.nb_result != NO_NB) {
+
+ if (result.nb_result == -1) {
+ puts("You can't send broadcast IM like that!");
+ continue;
+
+ } else if (result.nb_result == 0) {
+
+ i = current_call;
+
+ } else {
+ uri = pjsua.buddies[result.nb_result-1].uri.ptr;
+ }
+
+ } else if (result.uri_result) {
+ uri = result.uri_result;
+ }
+
+
+ /* Send typing indication. */
+ if (i != -1)
+ pjsua_call_typing(i, PJ_TRUE);
+ else
+ pjsua_im_typing(current_acc, uri, PJ_TRUE);
+
+ /* 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_typing(i, PJ_FALSE);
+ else
+ pjsua_im_typing(current_acc, uri, PJ_FALSE);
+ continue;
+ }
+
+ /* Send the IM */
+ if (i != -1)
+ pjsua_call_send_im(i, text);
+ else
+ pjsua_im_send(current_acc, uri, text);
+
+ break;
+
+ case 'a':
+
+ if (current_call == -1 ||
+ pjsua.calls[current_call].inv->role != PJSIP_ROLE_UAS ||
+ pjsua.calls[current_call].inv->state >= PJSIP_INV_STATE_CONNECTING)
+ {
+ puts("No pending incoming call");
+ fflush(stdout);
+ continue;
+
+ } else {
+ if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
+ continue;
+
+ if (atoi(buf) < 100)
+ continue;
+
+ /*
+ * 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);
+ continue;
+ }
+
+ pjsua_call_answer(current_call, atoi(buf));
+ }
+
+ break;
+
+
+ case 'h':
+
+ if (current_call == -1) {
+ puts("No current call");
+ fflush(stdout);
+ continue;
+
+ } else if (menuin[1] == 'a') {
+
+ /* Hangup all calls */
+ pjsua_call_hangup_all();
+
+ } else {
+
+ /* Hangup current calls */
+ pjsua_call_hangup(current_call);
+ }
+ break;
+
+ case ']':
+ case '[':
+ /*
+ * Cycle next/prev dialog.
+ */
+ if (menuin[0] == ']') {
+ find_next_call();
+
+ } else {
+ find_prev_call();
+ }
+
+ if (current_call != -1) {
+ char url[PJSIP_MAX_URL_SIZE];
+ int len;
+ const pjsip_uri *u;
+
+ u = pjsua.calls[current_call].inv->dlg->remote.info->uri;
+ len = pjsip_uri_print(0, u, url, sizeof(url)-1);
+ if (len < 1) {
+ pj_ansi_strcpy(url, "<uri is too long>");
+ } else {
+ url[len] = '\0';
+ }
+
+ PJ_LOG(3,(THIS_FILE,"Current dialog: %s", url));
+
+ } else {
+ PJ_LOG(3,(THIS_FILE,"No current dialog"));
+ }
+ break;
+
+ case 'H':
+ /*
+ * Hold call.
+ */
+ if (current_call != -1) {
+
+ pjsua_call_set_hold(current_call);
+
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ }
+ break;
+
+ case 'v':
+ /*
+ * Send re-INVITE (to release hold, etc).
+ */
+ if (current_call != -1) {
+
+ pjsua_call_reinvite(current_call);
+
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+ }
+ break;
+
+ case 'x':
+ /*
+ * Transfer call.
+ */
+ if (current_call == -1) {
+
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+
+ } else {
+ int call = current_call;
+
+ 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");
+ continue;
+ }
+
+ if (result.nb_result != NO_NB) {
+ if (result.nb_result == -1 || result.nb_result == 0)
+ puts("You can't do that with transfer call!");
+ else
+ pjsua_call_xfer( current_call,
+ pjsua.buddies[result.nb_result-1].uri.ptr);
+
+ } else if (result.uri_result) {
+ pjsua_call_xfer( current_call, result.uri_result);
+ }
+ }
+ break;
+
+ case '#':
+ /*
+ * Send DTMF strings.
+ */
+ if (current_call == -1) {
+
+ PJ_LOG(3,(THIS_FILE, "No current call"));
+
+ } else if (pjsua.calls[current_call].session == NULL) {
+
+ PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
+
+ } else {
+ pj_str_t digits;
+ int call = current_call;
+ pj_status_t status;
+
+ if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
+ sizeof(buf)))
+ {
+ break;
+ }
+
+ if (call != current_call) {
+ puts("Call has been disconnected");
+ continue;
+ }
+
+ digits = pj_str(buf);
+ status = pjmedia_session_dial_dtmf(pjsua.calls[current_call].session, 0,
+ &digits);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
+ } else {
+ puts("DTMF digits enqueued for transmission");
+ }
+ }
+ break;
+
+ case 's':
+ case 'u':
+ /*
+ * Subscribe/unsubscribe presence.
+ */
+ ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
+ if (result.nb_result != NO_NB) {
+ if (result.nb_result == -1) {
+ int i;
+ for (i=0; i<pjsua.buddy_cnt; ++i)
+ pjsua.buddies[i].monitor = (menuin[0]=='s');
+ } else if (result.nb_result == 0) {
+ puts("Sorry, can only subscribe to buddy's presence, "
+ "not from existing call");
+ } else {
+ pjsua.buddies[result.nb_result-1].monitor = (menuin[0]=='s');
+ }
+
+ pjsua_pres_refresh(current_acc);
+
+ } else if (result.uri_result) {
+ puts("Sorry, can only subscribe to buddy's presence, "
+ "not arbitrary URL (for now)");
+ }
+
+ break;
+
+ case 'r':
+ switch (menuin[1]) {
+ case 'r':
+ /*
+ * Re-Register.
+ */
+ pjsua_regc_update(current_acc, PJ_TRUE);
+ break;
+ case 'u':
+ /*
+ * Unregister
+ */
+ pjsua_regc_update(current_acc, PJ_FALSE);
+ break;
+ }
+ break;
+
+ case 't':
+ pjsua.acc[current_acc].online_status =
+ !pjsua.acc[current_acc].online_status;
+ printf("Setting %s online status to %s\n",
+ pjsua.config.acc_config[current_acc].id.ptr,
+ (pjsua.acc[current_acc].online_status?"online":"offline"));
+ pjsua_pres_refresh(current_acc);
+ break;
+
+ case 'c':
+ switch (menuin[1]) {
+ case 'l':
+ conf_list();
+ break;
+ case 'c':
+ case 'd':
+ {
+ char src_port[10], dst_port[10];
+ pj_status_t status;
+ const char *src_title, *dst_title;
+
+ 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)))
+ break;
+
+ if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
+ break;
+
+ if (menuin[1]=='c') {
+ status = pjmedia_conf_connect_port(pjsua.mconf,
+ atoi(src_port),
+ atoi(dst_port),
+ 0);
+ } else {
+ status = pjmedia_conf_disconnect_port(pjsua.mconf,
+ atoi(src_port),
+ atoi(dst_port));
+ }
+ if (status == PJ_SUCCESS) {
+ puts("Success");
+ } else {
+ puts("ERROR!!");
+ }
+ }
+ break;
+ }
+ break;
+
+ case 'd':
+ if (menuin[1] == 'c') {
+ char settings[2000];
+ int len;
+
+ len = pjsua_dump_settings(&pjsua.config, settings,
+ sizeof(settings));
+ if (len < 1)
+ PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
+ else
+ PJ_LOG(3,(THIS_FILE,
+ "Dumping configuration (%d bytes):\n%s\n",
+ len, settings));
+ } else {
+ pjsua_dump(menuin[1]=='d');
+ }
+ break;
+
+ case 'q':
+ goto on_exit;
+
+ default:
+ if (menuin[0] != '\n' && menuin[0] != '\r') {
+ printf("Invalid input %s", menuin);
+ }
+ keystroke_help();
+ break;
+ }
+ }
+
+on_exit:
+ ;
+}
+
+
+/*****************************************************************************
+ * This is a very simple PJSIP module, whose sole purpose is to display
+ * incoming and outgoing messages to log. This module will have priority
+ * higher than transport layer, which means:
+ *
+ * - incoming messages will come to this module first before reaching
+ * transaction layer.
+ *
+ * - outgoing messages will come to this module last, after the message
+ * has been 'printed' to contiguous buffer by transport layer and
+ * appropriate transport instance has been decided for this message.
+ *
+ */
+
+/* Notification on incoming messages */
+static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata)
+{
+ PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
+ "%s\n"
+ "--end msg--",
+ rdata->msg_info.len,
+ pjsip_rx_data_get_info(rdata),
+ rdata->pkt_info.src_name,
+ rdata->pkt_info.src_port,
+ rdata->msg_info.msg_buf));
+
+ /* Always return false, otherwise messages will not get processed! */
+ return PJ_FALSE;
+}
+
+/* Notification on outgoing messages */
+static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata)
+{
+
+ /* Important note:
+ * tp_info field is only valid after outgoing messages has passed
+ * transport layer. So don't try to access tp_info when the module
+ * has lower priority than transport layer.
+ */
+
+ PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
+ "%s\n"
+ "--end msg--",
+ (tdata->buf.cur - tdata->buf.start),
+ pjsip_tx_data_get_info(tdata),
+ tdata->tp_info.dst_name,
+ tdata->tp_info.dst_port,
+ tdata->buf.start));
+
+ /* Always return success, otherwise message will not get sent! */
+ return PJ_SUCCESS;
+}
+
+/* The module instance. */
+pjsip_module pjsua_console_app_msg_logger =
+{
+ NULL, NULL, /* prev, next. */
+ { "mod-pjsua-log", 13 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &console_on_rx_msg, /* on_rx_request() */
+ &console_on_rx_msg, /* on_rx_response() */
+ &console_on_tx_msg, /* on_tx_request. */
+ &console_on_tx_msg, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+
+};
+
+
+
+/*****************************************************************************
+ * Console application custom logging:
+ */
+
+
+static FILE *log_file;
+
+
+static void app_log_writer(int level, const char *buffer, int len)
+{
+ /* Write to both stdout and file. */
+
+ if (level <= (int)pjsua.config.app_log_level)
+ pj_log_write(level, buffer, len);
+
+ if (log_file) {
+ fwrite(buffer, len, 1, log_file);
+ fflush(log_file);
+ }
+}
+
+
+pj_status_t pjsua_console_app_logging_init(const pjsua_config *cfg)
+{
+ /* Redirect log function to ours */
+
+ pj_log_set_log_func( &app_log_writer );
+
+ /* If output log file is desired, create the file: */
+
+ if (cfg->log_filename.slen) {
+ log_file = fopen(cfg->log_filename.ptr, "wt");
+ if (log_file == NULL) {
+ PJ_LOG(1,(THIS_FILE, "Unable to open log file %s",
+ cfg->log_filename.ptr));
+ return -1;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+void pjsua_console_app_logging_shutdown(void)
+{
+ /* Close logging file, if any: */
+
+ if (log_file) {
+ fclose(log_file);
+ log_file = NULL;
+ }
+}
+
+/*****************************************************************************
+ * Error display:
+ */
+
+/*
+ * Display error message for the specified error code.
+ */
+void pjsua_perror(const char *sender, const char *title,
+ pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
+
+ pj_strerror(status, errmsg, sizeof(errmsg));
+
+ PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
+}
+
+
+
+pjsua_callback console_callback =
+{
+ &console_on_call_state,
+ &console_on_reg_state,
+ &console_on_pager,
+ &console_on_typing,
+};
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 5959d065..dba563ce 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjsua-lib/pjsua.h>
+#include "pjsua_imp.h"
/*
* pjsua_core.c
@@ -43,75 +44,147 @@ struct pjsua pjsua;
/*
* Init default application parameters.
*/
-void pjsua_default(void)
+PJ_DEF(void) pjsua_default_config(pjsua_config *cfg)
{
unsigned i;
+ pj_memset(cfg, 0, sizeof(pjsua_config));
- /* Normally need another thread for console application, because main
- * thread will be blocked in fgets().
- */
- pjsua.thread_cnt = 1;
+ cfg->thread_cnt = 1;
+ cfg->udp_port = 5060;
+ cfg->start_rtp_port = 4000;
+ cfg->max_calls = 4;
+ cfg->conf_ports = 0;
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ pjsua.clock_rate = 44100;
+#endif
- /* Default transport settings: */
- pjsua.sip_port = 5060;
+ cfg->complexity = 10;
+ cfg->quality = 10;
+
+ cfg->auto_answer = 100;
+ cfg->uas_duration = 3600;
+ /* Default logging settings: */
+ cfg->log_level = 5;
+ cfg->app_log_level = 4;
+ cfg->log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
+ PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
- /* Default we start RTP at port 4000 */
- pjsua.start_rtp_port = 4000;
+ /* Also init logging settings in pjsua.config, because log
+ * may be written before pjsua_init() is called.
+ */
+ pjsua.config.log_level = 5;
+ pjsua.config.app_log_level = 4;
- /* Default logging settings: */
- pjsua.log_level = 5;
- pjsua.app_log_level = 4;
- pjsua.log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
- PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
+ /* Init accounts: */
+ for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
+ cfg->acc_config[i].reg_timeout = 55;
+ }
+}
- /* Default call settings. */
- pjsua.uas_refresh = -1;
- pjsua.uas_duration = -1;
- /* Default: do not use STUN: */
- pjsua.stun_port1 = pjsua.stun_port2 = 0;
+#define strncpy_with_null(dst,src,len) \
+do { \
+ strncpy(dst, src, len); \
+ dst[len-1] = '\0'; \
+} while (0)
- /* Default for media: */
-#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
- pjsua.clock_rate = 44100;
-#endif
- pjsua.complexity = -1;
- pjsua.quality = 4;
- /* Init accounts: */
- pjsua.acc_cnt = 1;
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
- pjsua.acc[i].index = i;
- pjsua.acc[i].local_uri = pj_str(PJSUA_LOCAL_URI);
- pjsua.acc[i].reg_timeout = 55;
- pjsua.acc[i].online_status = PJ_TRUE;
- pj_list_init(&pjsua.acc[i].route_set);
- pj_list_init(&pjsua.acc[i].pres_srv_list);
+PJ_DEF(pj_status_t) pjsua_test_config( const pjsua_config *cfg,
+ char *errmsg,
+ int len)
+{
+ unsigned i;
+
+ /* If UDP port is zero, then sip_host and sip_port must be specified */
+ if (cfg->udp_port == 0) {
+ if (cfg->sip_host.slen==0 || cfg->sip_port==0) {
+ strncpy_with_null(errmsg,
+ "sip_host and sip_port must be specified",
+ len);
+ return -1;
+ }
}
- /* Init call array: */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i) {
- pjsua.calls[i].index = i;
- pjsua.calls[i].refresh_tm._timer_id = -1;
- pjsua.calls[i].hangup_tm._timer_id = -1;
- pjsua.calls[i].conf_slot = 0;
+ if (cfg->max_calls < 1) {
+ strncpy_with_null(errmsg,
+ "max_calls needs to be at least 1",
+ len);
+ return -1;
}
- /* Default max nb of calls. */
- pjsua.max_calls = 4;
+ /* STUN */
+ if (cfg->stun_srv1.slen || cfg->stun_port1 || cfg->stun_port2 ||
+ cfg->stun_srv2.slen)
+ {
+ if (cfg->stun_port1 == 0) {
+ strncpy_with_null(errmsg, "stun_port1 required", len);
+ return -1;
+ }
+ if (cfg->stun_srv1.slen == 0) {
+ strncpy_with_null(errmsg, "stun_srv1 required", len);
+ return -1;
+ }
+ if (cfg->stun_port2 == 0) {
+ strncpy_with_null(errmsg, "stun_port2 required", len);
+ return -1;
+ }
+ if (cfg->stun_srv2.slen == 0) {
+ strncpy_with_null(errmsg, "stun_srv2 required", len);
+ return -1;
+ }
+ }
- /* Init server presence subscription list: */
-
+ /* Verify accounts */
+ for (i=0; i<cfg->acc_cnt; ++i) {
+ const pjsua_acc_config *acc_cfg = &cfg->acc_config[i];
+ unsigned j;
-}
+ if (acc_cfg->id.slen == 0) {
+ strncpy_with_null(errmsg, "missing account ID", len);
+ return -1;
+ }
+
+ if (acc_cfg->id.slen == 0) {
+ strncpy_with_null(errmsg, "missing registrar URI", len);
+ return -1;
+ }
+
+ if (acc_cfg->reg_timeout == 0) {
+ strncpy_with_null(errmsg, "missing registration timeout", len);
+ return -1;
+ }
+ for (j=0; j<acc_cfg->cred_count; ++j) {
+
+ if (acc_cfg->cred_info[j].scheme.slen == 0) {
+ strncpy_with_null(errmsg, "missing auth scheme in account",
+ len);
+ return -1;
+ }
+
+ if (acc_cfg->cred_info[j].realm.slen == 0) {
+ strncpy_with_null(errmsg, "missing realm in account", len);
+ return -1;
+ }
+
+ if (acc_cfg->cred_info[j].username.slen == 0) {
+ strncpy_with_null(errmsg, "missing username in account", len);
+ return -1;
+ }
+
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
/*
* Handler for receiving incoming requests.
@@ -152,85 +225,146 @@ static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
}
-/*
- * Initialize sockets and optionally get the public address via STUN.
- */
-static pj_status_t init_sockets(pj_bool_t sip,
- pjmedia_sock_info *skinfo)
+static int PJ_THREAD_FUNC pjsua_poll(void *arg)
{
- enum {
- RTP_RETRY = 100
- };
- enum {
- SIP_SOCK,
- RTP_SOCK,
- RTCP_SOCK,
- };
- int i;
- static pj_uint16_t rtp_port;
- pj_sock_t sock[3];
- pj_sockaddr_in mapped_addr[3];
- pj_status_t status = PJ_SUCCESS;
+ pj_status_t last_err = 0;
- if (rtp_port == 0)
- rtp_port = (pj_uint16_t)pjsua.start_rtp_port;
+ PJ_UNUSED_ARG(arg);
- for (i=0; i<3; ++i)
- sock[i] = PJ_INVALID_SOCKET;
+ do {
+ pj_time_val timeout = { 0, 10 };
+ pj_status_t status;
+
+ status = pjsip_endpt_handle_events (pjsua.endpt, &timeout);
+ if (status != PJ_SUCCESS && status != last_err) {
+ last_err = status;
+ pjsua_perror(THIS_FILE, "handle_events() returned error", status);
+ }
+ } while (!pjsua.quit_flag);
+
+ return 0;
+}
+
+
+
+#define pjsua_has_stun() (pjsua.config.stun_port1 && \
+ pjsua.config.stun_port2)
+
+
+/*
+ * Create and initialize SIP socket (and possibly resolve public
+ * address via STUN, depending on config).
+ */
+static pj_status_t create_sip_udp_sock(int port,
+ pj_sock_t *p_sock,
+ pj_sockaddr_in *p_pub_addr)
+{
+ pj_sock_t sock;
+ pj_status_t status;
- /* Create and bind SIP UDP socket. */
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[SIP_SOCK]);
+ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "socket() error", status);
- goto on_error;
+ return status;
+ }
+
+ status = pj_sock_bind_in(sock, 0, (pj_uint16_t)port);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "bind() error", status);
+ pj_sock_close(sock);
+ return status;
}
- if (sip) {
- status = pj_sock_bind_in(sock[SIP_SOCK], 0, pjsua.sip_port);
+ if (pjsua_has_stun()) {
+ status = pj_stun_get_mapped_addr(&pjsua.cp.factory, 1, &sock,
+ &pjsua.config.stun_srv1,
+ pjsua.config.stun_port1,
+ &pjsua.config.stun_srv2,
+ pjsua.config.stun_port2,
+ p_pub_addr);
if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "bind() error", status);
- goto on_error;
+ pjsua_perror(THIS_FILE, "STUN resolve error", status);
+ pj_sock_close(sock);
+ return status;
}
+
} else {
- status = pj_sock_bind_in(sock[SIP_SOCK], 0, 0);
+
+ const pj_str_t *hostname = pj_gethostname();
+ struct pj_hostent he;
+
+ status = pj_gethostbyname(hostname, &he);
if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "bind() error", status);
- goto on_error;
+ pjsua_perror(THIS_FILE, "Unable to resolve local host", status);
+ pj_sock_close(sock);
+ return status;
}
+
+ pj_memset(p_pub_addr, 0, sizeof(pj_sockaddr_in));
+ p_pub_addr->sin_family = PJ_AF_INET;
+ p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
+ p_pub_addr->sin_addr = *(pj_in_addr*)he.h_addr;
}
+ *p_sock = sock;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Create RTP and RTCP socket pair, and possibly resolve their public
+ * address via STUN.
+ */
+static pj_status_t create_rtp_rtcp_sock(pjmedia_sock_info *skinfo)
+{
+ enum {
+ RTP_RETRY = 100
+ };
+ int i;
+ static pj_uint16_t rtp_port;
+ pj_sockaddr_in mapped_addr[2];
+ pj_status_t status = PJ_SUCCESS;
+ pj_sock_t sock[2];
+
+ if (rtp_port == 0)
+ rtp_port = (pj_uint16_t)pjsua.config.start_rtp_port;
+
+ for (i=0; i<2; ++i)
+ sock[i] = PJ_INVALID_SOCKET;
+
/* Loop retry to bind RTP and RTCP sockets. */
for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
/* Create and bind RTP socket. */
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[RTP_SOCK]);
+ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[0]);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "socket() error", status);
- goto on_error;
+ return status;
}
- status = pj_sock_bind_in(sock[RTP_SOCK], 0, rtp_port);
+ status = pj_sock_bind_in(sock[0], 0, rtp_port);
if (status != PJ_SUCCESS) {
- pj_sock_close(sock[RTP_SOCK]);
- sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+ pj_sock_close(sock[0]);
+ sock[0] = PJ_INVALID_SOCKET;
continue;
}
/* Create and bind RTCP socket. */
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[RTCP_SOCK]);
+ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[1]);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "socket() error", status);
- goto on_error;
+ pj_sock_close(sock[0]);
+ return status;
}
- status = pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1));
+ status = pj_sock_bind_in(sock[1], 0, (pj_uint16_t)(rtp_port+1));
if (status != PJ_SUCCESS) {
- pj_sock_close(sock[RTP_SOCK]);
- sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+ pj_sock_close(sock[0]);
+ sock[0] = PJ_INVALID_SOCKET;
- pj_sock_close(sock[RTCP_SOCK]);
- sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
+ pj_sock_close(sock[1]);
+ sock[1] = PJ_INVALID_SOCKET;
continue;
}
@@ -238,7 +372,32 @@ static pj_status_t init_sockets(pj_bool_t sip,
* If we're configured to use STUN, then find out the mapped address,
* and make sure that the mapped RTCP port is adjacent with the RTP.
*/
- if (pjsua.stun_port1 == 0) {
+ if (pjsua_has_stun()) {
+ status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 2, sock,
+ &pjsua.config.stun_srv1,
+ pjsua.config.stun_port1,
+ &pjsua.config.stun_srv2,
+ pjsua.config.stun_port2,
+ mapped_addr);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "STUN resolve error", status);
+ goto on_error;
+ }
+
+ if (pj_ntohs(mapped_addr[1].sin_port) ==
+ pj_ntohs(mapped_addr[0].sin_port)+1)
+ {
+ /* Success! */
+ break;
+ }
+
+ pj_sock_close(sock[0]);
+ sock[0] = PJ_INVALID_SOCKET;
+
+ pj_sock_close(sock[1]);
+ sock[1] = PJ_INVALID_SOCKET;
+
+ } else {
const pj_str_t *hostname;
pj_sockaddr_in addr;
@@ -254,69 +413,30 @@ static pj_status_t init_sockets(pj_bool_t sip,
goto on_error;
}
- for (i=0; i<3; ++i)
+ for (i=0; i<2; ++i)
pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
- if (sip) {
- mapped_addr[SIP_SOCK].sin_port =
- pj_htons((pj_uint16_t)pjsua.sip_port);
- }
- mapped_addr[RTP_SOCK].sin_port=pj_htons((pj_uint16_t)rtp_port);
- mapped_addr[RTCP_SOCK].sin_port=pj_htons((pj_uint16_t)(rtp_port+1));
+ mapped_addr[0].sin_port=pj_htons((pj_uint16_t)rtp_port);
+ mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(rtp_port+1));
break;
-
- } else {
- status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 3, sock,
- &pjsua.stun_srv1, pjsua.stun_port1,
- &pjsua.stun_srv2, pjsua.stun_port2,
- mapped_addr);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "STUN error", status);
- goto on_error;
- }
-
- if (pj_ntohs(mapped_addr[2].sin_port) ==
- pj_ntohs(mapped_addr[1].sin_port)+1)
- {
- break;
- }
-
- pj_sock_close(sock[RTP_SOCK]);
- sock[RTP_SOCK] = PJ_INVALID_SOCKET;
-
- pj_sock_close(sock[RTCP_SOCK]);
- sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
}
}
- if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) {
+ if (sock[0] == PJ_INVALID_SOCKET) {
PJ_LOG(1,(THIS_FILE,
"Unable to find appropriate RTP/RTCP ports combination"));
goto on_error;
}
- if (sip) {
- pjsua.sip_sock = sock[SIP_SOCK];
- pj_memcpy(&pjsua.sip_sock_name,
- &mapped_addr[SIP_SOCK],
- sizeof(pj_sockaddr_in));
- } else {
- pj_sock_close(sock[0]);
- }
- skinfo->rtp_sock = sock[RTP_SOCK];
+ skinfo->rtp_sock = sock[0];
pj_memcpy(&skinfo->rtp_addr_name,
- &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in));
+ &mapped_addr[0], sizeof(pj_sockaddr_in));
- skinfo->rtcp_sock = sock[RTCP_SOCK];
+ skinfo->rtcp_sock = sock[1];
pj_memcpy(&skinfo->rtcp_addr_name,
- &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in));
+ &mapped_addr[1], sizeof(pj_sockaddr_in));
- if (sip) {
- PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
- pj_inet_ntoa(pjsua.sip_sock_name.sin_addr),
- pj_ntohs(pjsua.sip_sock_name.sin_port)));
- }
PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
pj_inet_ntoa(skinfo->rtp_addr_name.sin_addr),
pj_ntohs(skinfo->rtp_addr_name.sin_port)));
@@ -328,9 +448,7 @@ static pj_status_t init_sockets(pj_bool_t sip,
return PJ_SUCCESS;
on_error:
- for (i=0; i<3; ++i) {
- if (sip && i==0)
- continue;
+ for (i=0; i<2; ++i) {
if (sock[i] != PJ_INVALID_SOCKET)
pj_sock_close(sock[i]);
}
@@ -339,141 +457,15 @@ on_error:
-/*
- * Initialize stack.
- */
-static pj_status_t init_stack(void)
-{
- pj_status_t status;
-
- /* Create global endpoint: */
-
- {
- const pj_str_t *hostname;
- const char *endpt_name;
-
- /* Endpoint MUST be assigned a globally unique name.
- * The name will be used as the hostname in Warning header.
- */
-
- /* For this implementation, we'll use hostname for simplicity */
- hostname = pj_gethostname();
- endpt_name = hostname->ptr;
-
- /* Create the endpoint: */
-
- status = pjsip_endpt_create(&pjsua.cp.factory, endpt_name,
- &pjsua.endpt);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status);
- return status;
- }
- }
-
-
- /* Initialize transaction layer: */
-
- status = pjsip_tsx_layer_init_module(pjsua.endpt);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Transaction layer initialization error",
- status);
- goto on_error;
- }
-
- /* Initialize UA layer module: */
-
- status = pjsip_ua_init_module( pjsua.endpt, NULL );
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "UA layer initialization error", status);
- goto on_error;
- }
-
- /* Initialize and register pjsua's application module: */
-
- {
- pjsip_module my_mod =
- {
- NULL, NULL, /* prev, next. */
- { "mod-pjsua", 9 }, /* Name. */
- -1, /* Id */
- PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
- NULL, /* load() */
- NULL, /* start() */
- NULL, /* stop() */
- NULL, /* unload() */
- &mod_pjsua_on_rx_request, /* on_rx_request() */
- &mod_pjsua_on_rx_response, /* on_rx_response() */
- NULL, /* on_tx_request. */
- NULL, /* on_tx_response() */
- NULL, /* on_tsx_state() */
- };
-
- pjsua.mod = my_mod;
-
- status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to register pjsua module",
- status);
- goto on_error;
- }
- }
-
- /* Initialize invite session module: */
-
- status = pjsua_call_init();
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Invite usage initialization error",
- status);
- goto on_error;
- }
-
- /* Done */
-
- return PJ_SUCCESS;
-
-
-on_error:
- pjsip_endpt_destroy(pjsua.endpt);
- pjsua.endpt = NULL;
- return status;
-}
-
-
-static int PJ_THREAD_FUNC pjsua_poll(void *arg)
-{
- pj_status_t last_err = 0;
-
- PJ_UNUSED_ARG(arg);
-
- do {
- pj_time_val timeout = { 0, 10 };
- pj_status_t status;
-
- status = pjsip_endpt_handle_events (pjsua.endpt, &timeout);
- if (status != PJ_SUCCESS && status != last_err) {
- last_err = status;
- pjsua_perror(THIS_FILE, "handle_events() returned error", status);
- }
- } while (!pjsua.quit_flag);
-
- return 0;
-}
-
-/*
- * Initialize pjsua application.
- * This will initialize all libraries, create endpoint instance, and register
- * pjsip modules.
+/**
+ * Create pjsua application.
+ * This initializes pjlib/pjlib-util, and creates memory pool factory to
+ * be used by application.
*/
-pj_status_t pjsua_init(void)
+PJ_DEF(pj_status_t) pjsua_create(void)
{
pj_status_t status;
- /* Init PJLIB logging: */
-
- pj_log_set_level(pjsua.log_level);
- pj_log_set_decor(pjsua.log_decor);
-
-
/* Init PJLIB: */
status = pj_init();
@@ -498,118 +490,33 @@ pj_status_t pjsua_init(void)
/* Create memory pool for application. */
pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL);
+ /* Must create endpoint to initialize SIP parser. */
+ /* Create global endpoint: */
- /* Init PJSIP : */
-
- status = init_stack();
+ status = pjsip_endpt_create(&pjsua.cp.factory,
+ pj_gethostname()->ptr,
+ &pjsua.endpt);
if (status != PJ_SUCCESS) {
- pj_caching_pool_destroy(&pjsua.cp);
- pjsua_perror(THIS_FILE, "Stack initialization has returned error",
- status);
+ pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status);
return status;
}
-
- /* Init core SIMPLE module : */
-
- pjsip_evsub_init_module(pjsua.endpt);
-
- /* Init presence module: */
-
- pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance());
-
- /* Init xfer/REFER module */
-
- pjsip_xfer_init_module( pjsua.endpt );
-
- /* Init pjsua presence handler: */
-
- pjsua_pres_init();
-
- /* Init out-of-dialog MESSAGE request handler. */
-
- pjsua_im_init();
-
-
- /* Init media endpoint: */
-
+ /* Must create media endpoint too */
status = pjmedia_endpt_create(&pjsua.cp.factory,
pjsip_endpt_get_ioqueue(pjsua.endpt), 0,
&pjsua.med_endpt);
if (status != PJ_SUCCESS) {
- pj_caching_pool_destroy(&pjsua.cp);
pjsua_perror(THIS_FILE,
"Media stack initialization has returned error",
status);
return status;
}
- /* Done. */
- return PJ_SUCCESS;
-}
-
-
-/*
- * Find account for incoming request.
- */
-int pjsua_find_account_for_incoming(pjsip_rx_data *rdata)
-{
- pjsip_uri *uri;
- pjsip_sip_uri *sip_uri;
- int acc_index;
-
- uri = rdata->msg_info.to->uri;
-
- /* Just return account #0 if To URI is not SIP: */
- if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
- !PJSIP_URI_SCHEME_IS_SIPS(uri))
- {
- return 0;
- }
-
-
- sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
-
- /* Find account which has matching username and domain. */
- for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
-
- pjsua_acc *acc = &pjsua.acc[acc_index];
- if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
- pj_stricmp(&acc->host_part, &sip_uri->host)==0)
- {
- /* Match ! */
- return acc_index;
- }
- }
-
- /* No matching, try match domain part only. */
- for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
-
- pjsua_acc *acc = &pjsua.acc[acc_index];
-
- if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) {
- /* Match ! */
- return acc_index;
- }
- }
-
- /* Still no match, just return account #0 */
- return 0;
+ return PJ_SUCCESS;
}
-/*
- * Find account for outgoing request.
- */
-int pjsua_find_account_for_outgoing(const pj_str_t *url)
-{
- PJ_UNUSED_ARG(url);
-
- /* Just use account #0 */
- return 0;
-}
-
/*
* Init media.
@@ -628,7 +535,8 @@ static pj_status_t init_media(void)
/* Register speex. */
status = pjmedia_codec_speex_init(pjsua.med_endpt,
PJMEDIA_SPEEX_NO_UWB,
- pjsua.quality, pjsua.complexity );
+ pjsua.config.quality,
+ pjsua.config.complexity );
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error initializing Speex codec",
status);
@@ -686,24 +594,27 @@ static pj_status_t init_media(void)
/* Enable those codecs that user put with "--add-codec", and move
* the priority to top
*/
- for (i=0; i<pjsua.codec_cnt; ++i) {
+ for (i=0; i<(int)pjsua.config.codec_cnt; ++i) {
pjmedia_codec_mgr_set_codec_priority(
pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
- &pjsua.codec_arg[i],
+ &pjsua.config.codec_arg[i],
PJMEDIA_CODEC_PRIO_HIGHEST);
}
/* Init options for conference bridge. */
options = 0;
- if (pjsua.no_mic)
- options |= PJMEDIA_CONF_NO_MIC;
+
+ /* Calculate maximum number of ports, if it's not specified */
+ if (pjsua.config.conf_ports == 0) {
+ pjsua.config.conf_ports = 3 * pjsua.config.max_calls;
+ }
/* Init conference bridge. */
- clock_rate = pjsua.clock_rate ? pjsua.clock_rate : 16000;
+ clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000;
samples_per_frame = clock_rate * 10 / 1000;
status = pjmedia_conf_create(pjsua.pool,
- pjsua.max_calls+PJSUA_CONF_MORE_PORTS,
+ pjsua.config.conf_ports,
clock_rate,
1, /* mono */
samples_per_frame,
@@ -717,21 +628,14 @@ static pj_status_t init_media(void)
return status;
}
- /* Add NULL port to the bridge. */
- status = pjmedia_null_port_create( pjsua.pool, clock_rate,
- 1, /* mono */
- samples_per_frame, 16,
- &pjsua.null_port);
- pjmedia_conf_add_port( pjsua.mconf, pjsua.pool, pjsua.null_port,
- &pjsua.null_port->info.name, NULL );
-
/* Create WAV file player if required: */
- if (pjsua.wav_file) {
+ if (pjsua.config.wav_file.slen) {
pj_str_t port_name;
/* Create the file player port. */
- status = pjmedia_wav_player_port_create( pjsua.pool, pjsua.wav_file,
+ status = pjmedia_wav_player_port_create( pjsua.pool,
+ pjsua.config.wav_file.ptr,
0, 0, -1, NULL,
&pjsua.file_port);
if (status != PJ_SUCCESS) {
@@ -742,9 +646,10 @@ static pj_status_t init_media(void)
}
/* Add port to conference bridge: */
+ port_name = pjsua.config.wav_file;
status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool,
pjsua.file_port,
- pj_cstr(&port_name, pjsua.wav_file),
+ &port_name,
&pjsua.wav_slot);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
@@ -760,60 +665,332 @@ static pj_status_t init_media(void)
/*
- * Start pjsua stack.
- * This will start the registration process, if registration is configured.
+ * Copy configuration.
*/
-pj_status_t pjsua_start(void)
+static void copy_config(pj_pool_t *pool, pjsua_config *dst,
+ const pjsua_config *src)
{
- int i; /* Must be signed */
- pjsip_transport *udp_transport;
- pj_status_t status = PJ_SUCCESS;
+ unsigned i;
- /*
- * Init media subsystem (codecs, conference bridge, et all).
- */
+ /* Plain memcpy */
+ pj_memcpy(dst, src, sizeof(pjsua_config));
+
+ /* Duplicate strings */
+ pj_strdup_with_null(pool, &dst->sip_host, &src->sip_host);
+ pj_strdup_with_null(pool, &dst->stun_srv1, &src->stun_srv1);
+ pj_strdup_with_null(pool, &dst->stun_srv2, &src->stun_srv2);
+ pj_strdup_with_null(pool, &dst->wav_file, &src->wav_file);
+
+ for (i=0; i<src->codec_cnt; ++i) {
+ pj_strdup_with_null(pool, &dst->codec_arg[i], &src->codec_arg[i]);
+ }
+
+ pj_strdup_with_null(pool, &dst->outbound_proxy, &src->outbound_proxy);
+ pj_strdup_with_null(pool, &dst->uri_to_call, &src->uri_to_call);
+
+ for (i=0; i<src->acc_cnt; ++i) {
+ pjsua_acc_config *dst_acc = &dst->acc_config[i];
+ const pjsua_acc_config *src_acc = &src->acc_config[i];
+ unsigned j;
+
+ pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id);
+ pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri);
+ pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact);
+ pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy);
+
+ for (j=0; j<src_acc->cred_count; ++j) {
+ pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm,
+ &src_acc->cred_info[j].realm);
+ pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme,
+ &src_acc->cred_info[j].scheme);
+ pj_strdup_with_null(pool, &dst_acc->cred_info[j].username,
+ &src_acc->cred_info[j].username);
+ pj_strdup_with_null(pool, &dst_acc->cred_info[j].data,
+ &src_acc->cred_info[j].data);
+ }
+ }
+
+ pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
+
+ for (i=0; i<src->buddy_cnt; ++i) {
+ pj_strdup_with_null(pool, &dst->buddy_uri[i], &src->buddy_uri[i]);
+ }
+}
+
+
+/*
+ * Initialize pjsua application.
+ * This will initialize all libraries, create endpoint instance, and register
+ * pjsip modules.
+ */
+PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg,
+ const pjsua_callback *cb)
+{
+ char errmsg[80];
+ unsigned i;
+ pj_status_t status;
+
+
+ /* Init accounts: */
+ for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
+ pjsua.acc[i].index = i;
+ pjsua.acc[i].online_status = PJ_TRUE;
+ pj_list_init(&pjsua.acc[i].route_set);
+ pj_list_init(&pjsua.acc[i].pres_srv_list);
+ }
+
+ /* Init call array: */
+ for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i) {
+ pjsua.calls[i].index = i;
+ pjsua.calls[i].refresh_tm._timer_id = -1;
+ pjsua.calls[i].hangup_tm._timer_id = -1;
+ pjsua.calls[i].conf_slot = 0;
+ }
+
+ /* Copy configuration */
+ copy_config(pjsua.pool, &pjsua.config, cfg);
+
+ /* Copy callback */
+ pj_memcpy(&pjsua.cb, cb, sizeof(pjsua_callback));
+
+ /* Test configuration */
+ if (pjsua_test_config(&pjsua.config, errmsg, sizeof(errmsg))) {
+ PJ_LOG(1,(THIS_FILE, "Error in configuration: %s", errmsg));
+ return -1;
+ }
+
+
+ /* Init PJLIB logging: */
+
+ pj_log_set_level(pjsua.config.log_level);
+ pj_log_set_decor(pjsua.config.log_decor);
+
+
+ /* Create SIP UDP socket */
+ if (pjsua.config.udp_port) {
+
+ status = create_sip_udp_sock( pjsua.config.udp_port,
+ &pjsua.sip_sock,
+ &pjsua.sip_sock_name);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_strdup2_with_null(pjsua.pool, &pjsua.config.sip_host,
+ pj_inet_ntoa(pjsua.sip_sock_name.sin_addr));
+ pjsua.config.sip_port = pj_ntohs(pjsua.sip_sock_name.sin_port);
+
+ } else {
+
+ /* Check that SIP host and port is configured */
+ if (cfg->sip_host.slen == 0 || cfg->sip_port == 0) {
+ PJ_LOG(1,(THIS_FILE,
+ "Error: sip_host and sip_port must be specified"));
+ return PJ_EINVAL;
+ }
+
+ pjsua.sip_sock = PJ_INVALID_SOCKET;
+ }
+
+
+ /* Init media endpoint */
status = init_media();
if (status != PJ_SUCCESS)
return status;
- /* Init sockets (STUN etc): */
- for (i=0; i<(int)pjsua.max_calls; ++i) {
- status = init_sockets(i==0, &pjsua.calls[i].skinfo);
- if (status == PJ_SUCCESS)
- status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL,
- &pjsua.calls[i].skinfo,
- &pjsua.calls[i].med_tp);
+
+ /* Init RTP sockets, only when UDP transport is enabled */
+ for (i=0; pjsua.config.start_rtp_port && i<pjsua.config.max_calls; ++i) {
+ status = create_rtp_rtcp_sock(&pjsua.calls[i].skinfo);
if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "init_sockets() has returned error",
- status);
- --i;
- if (i >= 0)
- pj_sock_close(pjsua.sip_sock);
- while (i >= 0) {
- pjmedia_transport_udp_close(pjsua.calls[i].med_tp);
+ unsigned j;
+ for (j=0; j<i; ++j) {
+ pjmedia_transport_udp_close(pjsua.calls[j].med_tp);
}
return status;
}
+ status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL,
+ &pjsua.calls[i].skinfo,
+ &pjsua.calls[i].med_tp);
}
- /* Add UDP transport: */
+ /* Init PJSIP : */
+
+ /* Initialize transaction layer: */
+
+ status = pjsip_tsx_layer_init_module(pjsua.endpt);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Transaction layer initialization error",
+ status);
+ goto on_error;
+ }
+
+ /* Initialize UA layer module: */
+
+ status = pjsip_ua_init_module( pjsua.endpt, NULL );
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "UA layer initialization error", status);
+ goto on_error;
+ }
+
+ /* Initialize and register pjsua's application module: */
+
+ {
+ pjsip_module my_mod =
+ {
+ NULL, NULL, /* prev, next. */
+ { "mod-pjsua", 9 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &mod_pjsua_on_rx_request, /* on_rx_request() */
+ &mod_pjsua_on_rx_response, /* on_rx_response() */
+ NULL, /* on_tx_request. */
+ NULL, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+ };
+
+ pjsua.mod = my_mod;
+
+ status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to register pjsua module",
+ status);
+ goto on_error;
+ }
+ }
+
+ /* Initialize invite session module: */
+
+ status = pjsua_call_init();
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Invite usage initialization error",
+ status);
+ goto on_error;
+ }
+
+ /* Init core SIMPLE module : */
+
+ pjsip_evsub_init_module(pjsua.endpt);
+
+ /* Init presence module: */
+
+ pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance());
+
+ /* Init xfer/REFER module */
+
+ pjsip_xfer_init_module( pjsua.endpt );
+
+ /* Init pjsua presence handler: */
+
+ pjsua_pres_init();
+
+ /* Init out-of-dialog MESSAGE request handler. */
+
+ pjsua_im_init();
+
+
+ /* Done. */
+ return PJ_SUCCESS;
+
+on_error:
+ pj_caching_pool_destroy(&pjsua.cp);
+ return status;
+}
+
+
+/*
+ * Find account for incoming request.
+ */
+int pjsua_find_account_for_incoming(pjsip_rx_data *rdata)
+{
+ pjsip_uri *uri;
+ pjsip_sip_uri *sip_uri;
+ unsigned acc_index;
+
+ uri = rdata->msg_info.to->uri;
+ /* Just return last account if To URI is not SIP: */
+ if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
+ !PJSIP_URI_SCHEME_IS_SIPS(uri))
{
+ return pjsua.config.acc_cnt;
+ }
+
+
+ sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
+
+ /* Find account which has matching username and domain. */
+ for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {
+
+ pjsua_acc *acc = &pjsua.acc[acc_index];
+
+ if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
+ pj_stricmp(&acc->host_part, &sip_uri->host)==0)
+ {
+ /* Match ! */
+ return acc_index;
+ }
+ }
+
+ /* No matching, try match domain part only. */
+ for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {
+
+ pjsua_acc *acc = &pjsua.acc[acc_index];
+
+ if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) {
+ /* Match ! */
+ return acc_index;
+ }
+ }
+
+ /* Still no match, just return last account */
+ return pjsua.config.acc_cnt;
+}
+
+
+/*
+ * Find account for outgoing request.
+ */
+int pjsua_find_account_for_outgoing(const pj_str_t *url)
+{
+ PJ_UNUSED_ARG(url);
+
+ /* Just use account #0 */
+ return 0;
+}
+
+
+
+/*
+ * Start pjsua stack.
+ * This will start the registration process, if registration is configured.
+ */
+PJ_DEF(pj_status_t) pjsua_start(void)
+{
+ int i; /* Must be signed */
+ pj_status_t status = PJ_SUCCESS;
+
+
+ /* Add UDP transport: */
+ if (pjsua.sip_sock > 0) {
+
/* Init the published name for the transport.
* Depending whether STUN is used, this may be the STUN mapped
* address, or socket's bound address.
*/
pjsip_host_port addr_name;
- addr_name.host.ptr = pj_inet_ntoa(pjsua.sip_sock_name.sin_addr);
- addr_name.host.slen = pj_ansi_strlen(addr_name.host.ptr);
- addr_name.port = pj_ntohs(pjsua.sip_sock_name.sin_port);
+ addr_name.host = pjsua.config.sip_host;
+ addr_name.port = pjsua.config.sip_port;
/* Create UDP transport from previously created UDP socket: */
status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock,
&addr_name, 1,
- &udp_transport);
+ NULL);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to start UDP transport",
status);
@@ -821,16 +998,37 @@ pj_status_t pjsua_start(void)
}
}
- /* Initialize Contact URI, if one is not specified: */
- for (i=0; i<pjsua.acc_cnt; ++i) {
+ /* The last account is default account to be used when nothing match
+ * any configured accounts.
+ */
+ {
+ char buf[80];
+ pj_str_t tmp;
+ pjsua_acc_config *acc_cfg =
+ &pjsua.config.acc_config[pjsua.config.acc_cnt];
+
+ tmp.ptr = buf;
+ tmp.slen = pj_ansi_sprintf(tmp.ptr, "<sip:%s:%d>",
+ pjsua.config.sip_host.ptr,
+ pjsua.config.sip_port);
+
+ pj_strdup_with_null( pjsua.pool, &acc_cfg->id, &tmp);
+ acc_cfg->contact = acc_cfg->id;
+ }
+
+
+ /* Initialize accounts: */
+ for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
+ pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[i];
+ pjsua_acc *acc = &pjsua.acc[i];
pjsip_uri *uri;
pjsip_sip_uri *sip_uri;
/* Need to parse local_uri to get the elements: */
- uri = pjsip_parse_uri(pjsua.pool, pjsua.acc[i].local_uri.ptr,
- pjsua.acc[i].local_uri.slen, 0);
+ uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr,
+ acc_cfg->id.slen, 0);
if (uri == NULL) {
pjsua_perror(THIS_FILE, "Invalid local URI",
PJSIP_EINVALIDURI);
@@ -852,15 +1050,20 @@ pj_status_t pjsua_start(void)
sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
- pjsua.acc[i].user_part = sip_uri->user;
- pjsua.acc[i].host_part = sip_uri->host;
+ acc->user_part = sip_uri->user;
+ acc->host_part = sip_uri->host;
- if (pjsua.acc[i].contact_uri.slen == 0 &&
- pjsua.acc[i].local_uri.slen)
- {
+ /* Build Contact header */
+
+ if (acc_cfg->contact.slen == 0) {
char contact[128];
+ const char *addr;
+ int port;
int len;
+ addr = pjsua.config.sip_host.ptr;
+ port = pjsua.config.sip_port;
+
/* The local Contact is the username@ip-addr, where
* - username is taken from the local URI,
* - ip-addr in UDP transport's address name (which may have been
@@ -873,21 +1076,17 @@ pj_status_t pjsua_start(void)
/* With the user part. */
len = pj_ansi_snprintf(contact, sizeof(contact),
- "<sip:%.*s@%.*s:%d>",
+ "<sip:%.*s@%s:%d>",
(int)sip_uri->user.slen,
sip_uri->user.ptr,
- (int)udp_transport->local_name.host.slen,
- udp_transport->local_name.host.ptr,
- udp_transport->local_name.port);
+ addr, port);
} else {
/* Without user part */
len = pj_ansi_snprintf(contact, sizeof(contact),
- "<sip:%.*s:%d>",
- (int)udp_transport->local_name.host.slen,
- udp_transport->local_name.host.ptr,
- udp_transport->local_name.port);
+ "<sip:%s:%d>",
+ addr, port);
}
if (len < 1 || len >= sizeof(contact)) {
@@ -897,38 +1096,39 @@ pj_status_t pjsua_start(void)
/* Duplicate Contact uri. */
- pj_strdup2(pjsua.pool, &pjsua.acc[i].contact_uri, contact);
+ pj_strdup2(pjsua.pool, &acc_cfg->contact, contact);
}
- }
- /* If outbound_proxy is specified, put it in the route_set: */
- if (pjsua.outbound_proxy.slen) {
+ /* Build route-set for this account */
+ if (pjsua.config.outbound_proxy.slen) {
+ pj_str_t hname = { "Route", 5};
+ pjsip_route_hdr *r;
+ pj_str_t tmp;
- pjsip_route_hdr *route;
- const pj_str_t hname = { "Route", 5 };
- int parsed_len;
-
- route = pjsip_parse_hdr( pjsua.pool, &hname,
- pjsua.outbound_proxy.ptr,
- pjsua.outbound_proxy.slen,
- &parsed_len);
- if (route == NULL) {
- pjsua_perror(THIS_FILE, "Invalid outbound proxy URL",
- PJSIP_EINVALIDURI);
- return PJSIP_EINVALIDURI;
+ pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy);
+ r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
+ pj_list_push_back(&acc->route_set, r);
}
- for (i=0; i<pjsua.acc_cnt; ++i) {
- pj_list_push_front(&pjsua.acc[i].route_set, route);
+ if (acc_cfg->proxy.slen) {
+ pj_str_t hname = { "Route", 5};
+ pjsip_route_hdr *r;
+ pj_str_t tmp;
+
+ pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy);
+ r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
+ pj_list_push_back(&acc->route_set, r);
}
}
+
+
/* Create worker thread(s), if required: */
- for (i=0; i<pjsua.thread_cnt; ++i) {
+ for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {
status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll,
NULL, 0, 0, &pjsua.threads[i]);
if (status != PJ_SUCCESS) {
@@ -944,7 +1144,7 @@ pj_status_t pjsua_start(void)
/* Start registration: */
/* Create client registration session: */
- for (i=0; i<pjsua.acc_cnt; ++i) {
+ for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
status = pjsua_regc_init(i);
if (status != PJ_SUCCESS)
return status;
@@ -956,6 +1156,12 @@ pj_status_t pjsua_start(void)
}
+ /* Init buddies */
+ for (i=0; i<(int)pjsua.config.buddy_cnt; ++i) {
+ pjsua.buddies[i].uri = pjsua.config.buddy_uri[i];
+ }
+ pjsua.buddy_cnt = pjsua.config.buddy_cnt;
+
/* Find account for outgoing preence subscription */
for (i=0; i<pjsua.buddy_cnt; ++i) {
pjsua.buddies[i].acc_index =
@@ -986,7 +1192,7 @@ static void busy_sleep(unsigned msec)
/*
* Destroy pjsua.
*/
-pj_status_t pjsua_destroy(void)
+PJ_DEF(pj_status_t) pjsua_destroy(void)
{
int i; /* Must be signed */
@@ -1000,14 +1206,14 @@ pj_status_t pjsua_destroy(void)
pjsua_pres_shutdown();
/* Unregister, if required: */
- for (i=0; i<pjsua.acc_cnt; ++i) {
+ for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
if (pjsua.acc[i].regc) {
pjsua_regc_update(i, 0);
}
}
/* Wait worker threads to quit: */
- for (i=0; i<pjsua.thread_cnt; ++i) {
+ for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {
if (pjsua.threads[i]) {
pj_thread_join(pjsua.threads[i]);
@@ -1029,10 +1235,6 @@ pj_status_t pjsua_destroy(void)
if (pjsua.file_port)
pjmedia_port_destroy(pjsua.file_port);
- /* Destroy null port. */
- if (pjsua.null_port)
- pjmedia_port_destroy(pjsua.null_port);
-
/* Shutdown all codecs: */
#if PJMEDIA_HAS_SPEEX_CODEC
@@ -1053,7 +1255,7 @@ pj_status_t pjsua_destroy(void)
/* Close transports */
- for (i=0; i<pjsua.call_cnt; ++i) {
+ for (i=0; pjsua.config.start_rtp_port && i<(int)pjsua.config.max_calls; ++i) {
pjmedia_transport_udp_close(pjsua.calls[i].med_tp);
}
diff --git a/pjsip/src/pjsua-lib/pjsua_im.c b/pjsip/src/pjsua-lib/pjsua_im.c
index cd0bd898..a5d96df4 100644
--- a/pjsip/src/pjsua-lib/pjsua_im.c
+++ b/pjsip/src/pjsua-lib/pjsua_im.c
@@ -152,7 +152,8 @@ void pjsua_im_process_pager(int call_index, const pj_str_t *from,
text.ptr = rdata->msg_info.msg->body->data;
text.slen = rdata->msg_info.msg->body->len;
- pjsua_ui_on_pager(call_index, from, to, &text);
+ if (pjsua.cb.on_pager)
+ (*pjsua.cb.on_pager)(call_index, from, to, &text);
} else {
@@ -169,7 +170,8 @@ void pjsua_im_process_pager(int call_index, const pj_str_t *from,
return;
}
- pjsua_ui_on_typing(call_index, from, to, is_typing);
+ if (pjsua.cb.on_typing)
+ (*pjsua.cb.on_typing)(call_index, from, to, is_typing);
}
}
@@ -269,8 +271,8 @@ static void im_callback(void *token, pjsip_event *e)
/**
* Send IM outside dialog.
*/
-pj_status_t pjsua_im_send(int acc_index, const char *dst_uri,
- const char *str)
+PJ_DEF(pj_status_t) pjsua_im_send(int acc_index, const char *dst_uri,
+ const char *str)
{
pjsip_tx_data *tdata;
const pj_str_t STR_CONTACT = { "Contact", 7 };
@@ -281,9 +283,10 @@ pj_status_t pjsua_im_send(int acc_index, const char *dst_uri,
pj_status_t status;
/* Create request. */
- status = pjsip_endpt_create_request( pjsua.endpt, &pjsip_message_method,
- &dst, &pjsua.acc[acc_index].local_uri,
- &dst, NULL, NULL, -1, NULL, &tdata);
+ status = pjsip_endpt_create_request(pjsua.endpt, &pjsip_message_method,
+ &dst,
+ &pjsua.config.acc_config[acc_index].id,
+ &dst, NULL, NULL, -1, NULL, &tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create request", status);
return status;
@@ -295,9 +298,9 @@ pj_status_t pjsua_im_send(int acc_index, const char *dst_uri,
/* Add contact. */
pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
- pjsip_generic_string_hdr_create(tdata->pool,
- &STR_CONTACT,
- &pjsua.acc[acc_index].contact_uri));
+ pjsip_generic_string_hdr_create(tdata->pool,
+ &STR_CONTACT,
+ &pjsua.config.acc_config[acc_index].contact));
/* Duplicate text.
* We need to keep the text because we will display it when we fail to
@@ -330,8 +333,8 @@ pj_status_t pjsua_im_send(int acc_index, const char *dst_uri,
/**
* Send typing indication outside dialog.
*/
-pj_status_t pjsua_im_typing(int acc_index, const char *dst_uri,
- pj_bool_t is_typing)
+PJ_DEF(pj_status_t) pjsua_im_typing(int acc_index, const char *dst_uri,
+ pj_bool_t is_typing)
{
const pj_str_t dst = pj_str((char*)dst_uri);
pjsip_tx_data *tdata;
@@ -339,7 +342,8 @@ pj_status_t pjsua_im_typing(int acc_index, const char *dst_uri,
/* Create request. */
status = pjsip_endpt_create_request( pjsua.endpt, &pjsip_message_method,
- &dst, &pjsua.acc[acc_index].local_uri,
+ &dst,
+ &pjsua.config.acc_config[acc_index].id,
&dst, NULL, NULL, -1, NULL, &tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create request", status);
diff --git a/pjsip/src/pjsua-lib/pjsua_imp.h b/pjsip/src/pjsua-lib/pjsua_imp.h
new file mode 100644
index 00000000..b406415f
--- /dev/null
+++ b/pjsip/src/pjsua-lib/pjsua_imp.h
@@ -0,0 +1,95 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSUA_IMP_H__
+#define __PJSUA_IMP_H__
+
+
+
+/**
+ * Find account for incoming request.
+ */
+int pjsua_find_account_for_incoming(pjsip_rx_data *rdata);
+
+
+/**
+ * Find account for outgoing request.
+ */
+int pjsua_find_account_for_outgoing(const pj_str_t *url);
+
+
+/**
+ * Init pjsua call module.
+ */
+pj_status_t pjsua_call_init(void);
+
+
+/**
+ * Handle incoming invite request.
+ */
+pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata);
+
+
+/**
+ * Initialize client registration session.
+ *
+ * @param app_callback Optional callback
+ */
+pj_status_t pjsua_regc_init(int acc_index);
+
+
+/**
+ * Init presence.
+ */
+pj_status_t pjsua_pres_init();
+
+
+/**
+ * Terminate all subscriptions
+ */
+void pjsua_pres_shutdown(void);
+
+/**
+ * Init IM module handler to handle incoming MESSAGE outside dialog.
+ */
+pj_status_t pjsua_im_init();
+
+/**
+ * Create Accept header for MESSAGE.
+ */
+pjsip_accept_hdr* pjsua_im_create_accept(pj_pool_t *pool);
+
+/**
+ * Private: check if we can accept the message.
+ * If not, then p_accept header will be filled with a valid
+ * Accept header.
+ */
+pj_bool_t pjsua_im_accept_pager(pjsip_rx_data *rdata,
+ pjsip_accept_hdr **p_accept_hdr);
+
+/**
+ * Private: process pager message.
+ * This may trigger pjsua_ui_on_pager() or pjsua_ui_on_typing().
+ */
+void pjsua_im_process_pager(int call_id, const pj_str_t *from,
+ const pj_str_t *to, pjsip_rx_data *rdata);
+
+
+
+#endif /* __PJSUA_IMP_H__ */
+
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index b77871f9..be31e2e1 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjsua-lib/pjsua.h>
+#include "pjsua_imp.h"
/*
* pjsua_pres.c
@@ -80,6 +81,7 @@ static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
{
int acc_index;
+ pjsua_acc_config *acc_config;
pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
pjsua_srv_pres *uapres;
pjsip_evsub *sub;
@@ -96,11 +98,12 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
/* Find which account for the incoming request. */
acc_index = pjsua_find_account_for_incoming(rdata);
+ acc_config = &pjsua.config.acc_config[acc_index];
/* Create UAS dialog: */
- status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
- &pjsua.acc[acc_index].contact_uri,
- &dlg);
+ status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
+ &acc_config->contact,
+ &dlg);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Unable to create UAS dialog for subscription",
@@ -306,15 +309,17 @@ static pjsip_evsub_user pres_callback =
static void subscribe_buddy_presence(unsigned index)
{
int acc_index;
+ pjsua_acc_config *acc_config;
pjsip_dialog *dlg;
pjsip_tx_data *tdata;
pj_status_t status;
acc_index = pjsua.buddies[index].acc_index;
+ acc_config = &pjsua.config.acc_config[acc_index];
status = pjsip_dlg_create_uac( pjsip_ua_instance(),
- &pjsua.acc[acc_index].local_uri,
- &pjsua.acc[acc_index].contact_uri,
+ &acc_config->id,
+ &acc_config->contact,
&pjsua.buddies[index].uri,
NULL, &dlg);
if (status != PJ_SUCCESS) {
@@ -323,8 +328,11 @@ static void subscribe_buddy_presence(unsigned index)
return;
}
- pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count,
- pjsua.cred_info);
+ if (acc_config->cred_count) {
+ pjsip_auth_clt_set_credentials( &dlg->auth_sess,
+ acc_config->cred_count,
+ acc_config->cred_info);
+ }
status = pjsip_pres_create_uac( dlg, &pres_callback,
&pjsua.buddies[index].sub);
@@ -426,7 +434,7 @@ pj_status_t pjsua_pres_init()
/*
* Refresh presence
*/
-void pjsua_pres_refresh(int acc_index)
+PJ_DEF(void) pjsua_pres_refresh(int acc_index)
{
refresh_client_subscription();
refresh_server_subscription(acc_index);
@@ -441,7 +449,7 @@ void pjsua_pres_shutdown(void)
int acc_index;
int i;
- for (acc_index=0; acc_index<pjsua.acc_cnt; ++acc_index) {
+ for (acc_index=0; acc_index<(int)pjsua.config.acc_cnt; ++acc_index) {
pjsua.acc[acc_index].online_status = 0;
}
@@ -449,7 +457,7 @@ void pjsua_pres_shutdown(void)
pjsua.buddies[i].monitor = 0;
}
- for (acc_index=0; acc_index<pjsua.acc_cnt; ++acc_index) {
+ for (acc_index=0; acc_index<(int)pjsua.config.acc_cnt; ++acc_index) {
pjsua_pres_refresh(acc_index);
}
}
@@ -471,7 +479,7 @@ void pjsua_pres_dump(pj_bool_t detail)
int count = 0;
- for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
+ for (acc_index=0; acc_index < (int)pjsua.config.acc_cnt; ++acc_index) {
if (!pj_list_empty(&pjsua.acc[acc_index].pres_srv_list)) {
struct pjsua_srv_pres *uapres;
@@ -506,11 +514,11 @@ void pjsua_pres_dump(pj_bool_t detail)
*/
PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
- for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
+ for (acc_index=0; acc_index < (int)pjsua.config.acc_cnt; ++acc_index) {
PJ_LOG(3,(THIS_FILE, " %.*s",
- (int)pjsua.acc[acc_index].local_uri.slen,
- pjsua.acc[acc_index].local_uri.ptr));
+ (int)pjsua.config.acc_config[acc_index].id.slen,
+ pjsua.config.acc_config[acc_index].id.ptr));
if (pj_list_empty(&pjsua.acc[acc_index].pres_srv_list)) {
diff --git a/pjsip/src/pjsua-lib/pjsua_reg.c b/pjsip/src/pjsua-lib/pjsua_reg.c
index 83485f12..64ebe97e 100644
--- a/pjsip/src/pjsua-lib/pjsua_reg.c
+++ b/pjsip/src/pjsua-lib/pjsua_reg.c
@@ -59,12 +59,12 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
pjsip_regc_destroy(acc->regc);
acc->regc = NULL;
PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
- acc->local_uri.ptr));
+ pjsua.config.acc_config[acc->index].id.ptr));
} else {
PJ_LOG(3, (THIS_FILE,
"%s: registration success, status=%d (%s), "
"will re-register in %d seconds",
- acc->local_uri.ptr,
+ pjsua.config.acc_config[acc->index].id.ptr,
param->code,
pjsip_get_status_text(param->code)->ptr,
param->expiration));
@@ -77,14 +77,15 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
acc->reg_last_err = param->status;
acc->reg_last_code = param->code;
- pjsua_ui_on_reg_state(acc->index);
+ if (pjsua.cb.on_reg_state)
+ (*pjsua.cb.on_reg_state)(acc->index);
}
/*
* Update registration. If renew is false, then unregistration will be performed.
*/
-void pjsua_regc_update(int acc_index, pj_bool_t renew)
+PJ_DECL(void) pjsua_regc_update(int acc_index, pj_bool_t renew)
{
pj_status_t status = 0;
pjsip_tx_data *tdata = 0;
@@ -129,9 +130,12 @@ void pjsua_regc_update(int acc_index, pj_bool_t renew)
*/
pj_status_t pjsua_regc_init(int acc_index)
{
+ pjsua_acc_config *acc_config;
pj_status_t status;
- if (pjsua.acc[acc_index].reg_uri.slen == 0) {
+ acc_config = &pjsua.config.acc_config[acc_index];
+
+ if (acc_config->reg_uri.slen == 0) {
PJ_LOG(3,(THIS_FILE, "Registrar URI is not specified"));
return PJ_SUCCESS;
}
@@ -151,11 +155,11 @@ pj_status_t pjsua_regc_init(int acc_index)
status = pjsip_regc_init( pjsua.acc[acc_index].regc,
- &pjsua.acc[acc_index].reg_uri,
- &pjsua.acc[acc_index].local_uri,
- &pjsua.acc[acc_index].local_uri,
- 1, &pjsua.acc[acc_index].contact_uri,
- pjsua.acc[acc_index].reg_timeout);
+ &acc_config->reg_uri,
+ &acc_config->id,
+ &acc_config->id,
+ 1, &acc_config->contact,
+ acc_config->reg_timeout);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Client registration initialization error",
@@ -163,9 +167,11 @@ pj_status_t pjsua_regc_init(int acc_index)
return status;
}
- pjsip_regc_set_credentials( pjsua.acc[acc_index].regc,
- pjsua.cred_count,
- pjsua.cred_info );
+ if (acc_config->cred_count) {
+ pjsip_regc_set_credentials( pjsua.acc[acc_index].regc,
+ acc_config->cred_count,
+ acc_config->cred_info );
+ }
pjsip_regc_set_route_set( pjsua.acc[acc_index].regc,
&pjsua.acc[acc_index].route_set );
diff --git a/pjsip/src/pjsua-lib/pjsua_settings.c b/pjsip/src/pjsua-lib/pjsua_settings.c
index cb98effe..e1bdd34f 100644
--- a/pjsip/src/pjsua-lib/pjsua_settings.c
+++ b/pjsip/src/pjsua-lib/pjsua_settings.c
@@ -59,22 +59,17 @@ static void usage(void)
puts (" --app-log-level=N Set log max level for stdout display (default=4)");
puts ("");
puts ("SIP Account options:");
- puts (" --id=url Set the URL of local ID (used in From header)");
- puts (" --contact=url Override the Contact information");
- puts (" --proxy=url Set the URL of proxy server");
- puts ("");
- puts ("SIP Account Registration Options:");
puts (" --registrar=url Set the URL of registrar server");
- puts (" --reg-timeout=secs Set registration interval to secs (default 3600)");
- puts ("");
- puts ("SIP Account Control:");
- puts (" --next-account Add more account");
- puts ("");
- puts ("Authentication options:");
+ puts (" --id=url Set the URL of local ID (used in From header)");
+ puts (" --contact=url Optionally override the Contact information");
+ puts (" --proxy=url Optional URL of proxy server to visit");
puts (" --realm=string Set realm");
puts (" --username=string Set authentication username");
puts (" --password=string Set authentication password");
- puts (" --next-cred Add more credential");
+ puts (" --reg-timeout=SEC Optional registration interval (default 55)");
+ puts ("");
+ puts ("SIP Account Control:");
+ puts (" --next-account Add more account");
puts ("");
puts ("Transport Options:");
puts (" --local-port=port Set TCP/UDP port");
@@ -86,7 +81,6 @@ static void usage(void)
puts (" --add-codec=name Manually add codec (default is to enable all)");
puts (" --clock-rate=N Override sound device clock rate");
puts (" --null-audio Use NULL audio device");
- puts (" --no-mic Disable microphone device");
puts (" --play-file=file Play WAV file in conference bridge");
puts (" --auto-play Automatically play the file (to incoming calls only)");
puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
@@ -113,7 +107,7 @@ static void usage(void)
/*
* Verify that valid SIP url is given.
*/
-pj_status_t pjsua_verify_sip_url(const char *c_url)
+PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
{
pjsip_uri *p;
pj_pool_t *pool;
@@ -214,12 +208,13 @@ static int my_atoi(const char *cs)
/* Parse arguments. */
-pj_status_t pjsua_parse_args(int argc, char *argv[])
+PJ_DEF(pj_status_t) pjsua_parse_args(int argc, char *argv[],
+ pjsua_config *cfg)
{
int c;
int option_index;
enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
- OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, OPT_NO_MIC,
+ OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT,
OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
@@ -229,7 +224,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
OPT_AUTO_CONF, OPT_CLOCK_RATE,
OPT_PLAY_FILE, OPT_RTP_PORT, OPT_ADD_CODEC,
OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME,
- OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS, OPT_UAS_REFRESH,
+ OPT_NEXT_ACCOUNT, OPT_MAX_CALLS, OPT_UAS_REFRESH,
OPT_UAS_DURATION,
};
struct pj_getopt_option long_options[] = {
@@ -241,7 +236,6 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
{ "version", 0, 0, OPT_VERSION},
{ "clock-rate", 1, 0, OPT_CLOCK_RATE},
{ "null-audio", 0, 0, OPT_NULL_AUDIO},
- { "no-mic", 0, 0, OPT_NO_MIC},
{ "local-port", 1, 0, OPT_LOCAL_PORT},
{ "proxy", 1, 0, OPT_PROXY},
{ "outbound", 1, 0, OPT_OUTBOUND_PROXY},
@@ -269,19 +263,21 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
{ "quality", 1, 0, OPT_QUALITY},
{ "ptime", 1, 0, OPT_PTIME},
{ "next-account",0,0, OPT_NEXT_ACCOUNT},
- { "next-cred", 0, 0, OPT_NEXT_CRED},
{ "max-calls", 1, 0, OPT_MAX_CALLS},
{ "uas-refresh",1, 0, OPT_UAS_REFRESH},
{ "uas-duration",1,0, OPT_UAS_DURATION},
{ NULL, 0, 0, 0}
};
pj_status_t status;
- pjsua_acc *cur_acc;
- pjsip_cred_info *cur_cred;
+ pjsua_acc_config *cur_acc;
+ char errmsg[80];
char *config_file = NULL;
+ unsigned i;
/* Run pj_getopt once to see if user specifies config file to read. */
- while ((c=pj_getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+ while ((c=pj_getopt_long(argc, argv, "", long_options,
+ &option_index)) != -1)
+ {
switch (c) {
case OPT_CONFIG_FILE:
config_file = pj_optarg;
@@ -297,16 +293,15 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
return status;
}
-
- cur_acc = &pjsua.acc[0];
- cur_cred = &pjsua.cred_info[0];
+ cfg->acc_cnt = 0;
+ cur_acc = &cfg->acc_config[0];
/* Reinitialize and re-run pj_getopt again, possibly with new arguments
* read from config file.
*/
pj_optind = 0;
- while((c=pj_getopt_long(argc, argv, "", long_options, &option_index))!=-1) {
+ while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
char *p;
pj_str_t tmp;
long lval;
@@ -314,7 +309,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
switch (c) {
case OPT_LOG_FILE:
- pjsua.log_filename = pj_optarg;
+ cfg->log_filename = pj_str(pj_optarg);
break;
case OPT_LOG_LEVEL:
@@ -325,12 +320,13 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
"for --log-level"));
return PJ_EINVAL;
}
+ cfg->log_level = c;
pj_log_set_level( c );
break;
case OPT_APP_LOG_LEVEL:
- pjsua.app_log_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
- if (pjsua.app_log_level < 0 || pjsua.app_log_level > 6) {
+ cfg->app_log_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
+ if (cfg->app_log_level < 0 || cfg->app_log_level > 6) {
PJ_LOG(1,(THIS_FILE,
"Error: expecting integer value 0-6 "
"for --app-log-level"));
@@ -347,11 +343,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
return PJ_EINVAL;
case OPT_NULL_AUDIO:
- pjsua.null_audio = 1;
- break;
-
- case OPT_NO_MIC:
- pjsua.no_mic = 1;
+ cfg->null_audio = 1;
break;
case OPT_CLOCK_RATE:
@@ -361,7 +353,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
"8000-48000 for clock rate"));
return PJ_EINVAL;
}
- pjsua.clock_rate = (int)lval;
+ cfg->clock_rate = lval;
break;
case OPT_LOCAL_PORT: /* local-port */
@@ -372,7 +364,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
"--local-port"));
return PJ_EINVAL;
}
- pjsua.sip_port = (pj_uint16_t)lval;
+ cfg->udp_port = (pj_uint16_t)lval;
break;
case OPT_PROXY: /* proxy */
@@ -392,7 +384,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
"in outbound proxy argument", pj_optarg));
return PJ_EINVAL;
}
- pjsua.outbound_proxy = pj_str(pj_optarg);
+ cfg->outbound_proxy = pj_str(pj_optarg);
break;
case OPT_REGISTRAR: /* registrar */
@@ -422,8 +414,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
"in local id argument", pj_optarg));
return PJ_EINVAL;
}
- cur_acc->local_uri = pj_str(pj_optarg);
- pjsua.has_acc = 1;
+ cur_acc->id = pj_str(pj_optarg);
break;
case OPT_CONTACT: /* contact */
@@ -433,50 +424,42 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
"in contact argument", pj_optarg));
return PJ_EINVAL;
}
- cur_acc->contact_uri = pj_str(pj_optarg);
+ cur_acc->contact = pj_str(pj_optarg);
break;
case OPT_NEXT_ACCOUNT: /* Add more account. */
- pjsua.acc_cnt++;
- cur_acc = &pjsua.acc[pjsua.acc_cnt - 1];
+ cfg->acc_cnt++;
+ cur_acc = &cfg->acc_config[cfg->acc_cnt - 1];
break;
case OPT_USERNAME: /* Default authentication user */
- if (pjsua.cred_count==0) pjsua.cred_count=1;
- cur_cred->username = pj_str(pj_optarg);
+ cur_acc->cred_info[0].username = pj_str(pj_optarg);
break;
case OPT_REALM: /* Default authentication realm. */
- if (pjsua.cred_count==0) pjsua.cred_count=1;
- cur_cred->realm = pj_str(pj_optarg);
+ cur_acc->cred_info[0].realm = pj_str(pj_optarg);
break;
case OPT_PASSWORD: /* authentication password */
- if (pjsua.cred_count==0) pjsua.cred_count=1;
- cur_cred->data_type = 0;
- cur_cred->data = pj_str(pj_optarg);
- break;
-
- case OPT_NEXT_CRED: /* Next credential */
- pjsua.cred_count++;
- cur_cred = &pjsua.cred_info[pjsua.cred_count - 1];
+ cur_acc->cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
+ cur_acc->cred_info[0].data = pj_str(pj_optarg);
break;
case OPT_USE_STUN1: /* STUN server 1 */
p = pj_ansi_strchr(pj_optarg, ':');
if (p) {
*p = '\0';
- pjsua.stun_srv1 = pj_str(pj_optarg);
- pjsua.stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1));
- if (pjsua.stun_port1 < 1 || pjsua.stun_port1 > 65535) {
+ cfg->stun_srv1 = pj_str(pj_optarg);
+ cfg->stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1));
+ if (cfg->stun_port1 < 1 || cfg->stun_port1 > 65535) {
PJ_LOG(1,(THIS_FILE,
"Error: expecting port number with "
"option --use-stun1"));
return PJ_EINVAL;
}
} else {
- pjsua.stun_port1 = 3478;
- pjsua.stun_srv1 = pj_str(pj_optarg);
+ cfg->stun_port1 = 3478;
+ cfg->stun_srv1 = pj_str(pj_optarg);
}
break;
@@ -484,17 +467,17 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
p = pj_ansi_strchr(pj_optarg, ':');
if (p) {
*p = '\0';
- pjsua.stun_srv2 = pj_str(pj_optarg);
- pjsua.stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1));
- if (pjsua.stun_port2 < 1 || pjsua.stun_port2 > 65535) {
+ cfg->stun_srv2 = pj_str(pj_optarg);
+ cfg->stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1));
+ if (cfg->stun_port2 < 1 || cfg->stun_port2 > 65535) {
PJ_LOG(1,(THIS_FILE,
"Error: expecting port number with "
"option --use-stun2"));
return PJ_EINVAL;
}
} else {
- pjsua.stun_port2 = 3478;
- pjsua.stun_srv2 = pj_str(pj_optarg);
+ cfg->stun_port2 = 3478;
+ cfg->stun_srv2 = pj_str(pj_optarg);
}
break;
@@ -505,33 +488,33 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
"--add-buddy option", pj_optarg));
return -1;
}
- if (pjsua.buddy_cnt == PJSUA_MAX_BUDDIES) {
+ if (cfg->buddy_cnt == PJSUA_MAX_BUDDIES) {
PJ_LOG(1,(THIS_FILE,
"Error: too many buddies in buddy list."));
return -1;
}
- pjsua.buddies[pjsua.buddy_cnt++].uri = pj_str(pj_optarg);
+ cfg->buddy_uri[cfg->buddy_cnt++] = pj_str(pj_optarg);
break;
case OPT_AUTO_PLAY:
- pjsua.auto_play = 1;
+ cfg->auto_play = 1;
break;
case OPT_AUTO_LOOP:
- pjsua.auto_loop = 1;
+ cfg->auto_loop = 1;
break;
case OPT_AUTO_CONF:
- pjsua.auto_conf = 1;
+ cfg->auto_conf = 1;
break;
case OPT_PLAY_FILE:
- pjsua.wav_file = pj_optarg;
+ cfg->wav_file = pj_str(pj_optarg);
break;
case OPT_RTP_PORT:
- pjsua.start_rtp_port = my_atoi(pj_optarg);
- if (pjsua.start_rtp_port < 1 || pjsua.start_rtp_port > 65535) {
+ cfg->start_rtp_port = my_atoi(pj_optarg);
+ if (cfg->start_rtp_port < 1 || cfg->start_rtp_port > 65535) {
PJ_LOG(1,(THIS_FILE,
"Error: rtp-port argument value "
"(expecting 1-65535"));
@@ -540,12 +523,12 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
break;
case OPT_ADD_CODEC:
- pjsua.codec_arg[pjsua.codec_cnt++] = pj_str(pj_optarg);
+ cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
break;
case OPT_COMPLEXITY:
- pjsua.complexity = my_atoi(pj_optarg);
- if (pjsua.complexity < 0 || pjsua.complexity > 10) {
+ cfg->complexity = my_atoi(pj_optarg);
+ if (cfg->complexity < 0 || cfg->complexity > 10) {
PJ_LOG(1,(THIS_FILE,
"Error: invalid --complexity (expecting 0-10"));
return -1;
@@ -553,8 +536,8 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
break;
case OPT_QUALITY:
- pjsua.quality = my_atoi(pj_optarg);
- if (pjsua.quality < 0 || pjsua.quality > 10) {
+ cfg->quality = my_atoi(pj_optarg);
+ if (cfg->quality < 0 || cfg->quality > 10) {
PJ_LOG(1,(THIS_FILE,
"Error: invalid --quality (expecting 0-10"));
return -1;
@@ -562,8 +545,8 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
break;
case OPT_PTIME:
- pjsua.ptime = my_atoi(pj_optarg);
- if (pjsua.ptime < 10 || pjsua.ptime > 1000) {
+ cfg->ptime = my_atoi(pj_optarg);
+ if (cfg->ptime < 10 || cfg->ptime > 1000) {
PJ_LOG(1,(THIS_FILE,
"Error: invalid --ptime option"));
return -1;
@@ -571,8 +554,8 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
break;
case OPT_AUTO_ANSWER:
- pjsua.auto_answer = my_atoi(pj_optarg);
- if (pjsua.auto_answer < 100 || pjsua.auto_answer > 699) {
+ cfg->auto_answer = my_atoi(pj_optarg);
+ if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
PJ_LOG(1,(THIS_FILE,
"Error: invalid code in --auto-answer "
"(expecting 100-699"));
@@ -581,16 +564,16 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
break;
case OPT_MAX_CALLS:
- pjsua.max_calls = my_atoi(pj_optarg);
- if (pjsua.max_calls < 1 || pjsua.max_calls > 255) {
+ cfg->max_calls = my_atoi(pj_optarg);
+ if (cfg->max_calls < 1 || cfg->max_calls > 255) {
PJ_LOG(1,(THIS_FILE,"Too many calls for max-calls (1-255)"));
return -1;
}
break;
case OPT_UAS_REFRESH:
- pjsua.uas_refresh = my_atoi(pj_optarg);
- if (pjsua.uas_refresh < 1) {
+ cfg->uas_refresh = my_atoi(pj_optarg);
+ if (cfg->uas_refresh < 1) {
PJ_LOG(1,(THIS_FILE,
"Invalid value for --uas-refresh (must be >0)"));
return -1;
@@ -598,8 +581,8 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
break;
case OPT_UAS_DURATION:
- pjsua.uas_duration = my_atoi(pj_optarg);
- if (pjsua.uas_duration < 1) {
+ cfg->uas_duration = my_atoi(pj_optarg);
+ if (cfg->uas_duration < 1) {
PJ_LOG(1,(THIS_FILE,
"Invalid value for --uas-duration "
"(must be >0)"));
@@ -610,22 +593,21 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
}
if (pj_optind != argc) {
- int i;
if (pjsua_verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
return -1;
}
- pjsua.uri_to_call = pj_str(argv[pj_optind]);
+ cfg->uri_to_call = pj_str(argv[pj_optind]);
pj_optind++;
/* Add URI to call to buddy list if it's not already there */
- for (i=0; i<pjsua.buddy_cnt; ++i) {
- if (pj_stricmp(&pjsua.buddies[i].uri, &pjsua.uri_to_call)==0)
+ for (i=0; i<cfg->buddy_cnt; ++i) {
+ if (pj_stricmp(&cfg->buddy_uri[i], &cfg->uri_to_call)==0)
break;
}
- if (i == pjsua.buddy_cnt && pjsua.buddy_cnt < PJSUA_MAX_BUDDIES) {
- pjsua.buddies[pjsua.buddy_cnt++].uri = pjsua.uri_to_call;
+ if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
+ cfg->buddy_uri[cfg->buddy_cnt++] = cfg->uri_to_call;
}
}
@@ -634,6 +616,23 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
return PJ_EINVAL;
}
+ if (cfg->acc_config[0].id.slen && cfg->acc_cnt==0)
+ cfg->acc_cnt = 1;
+
+ for (i=0; i<cfg->acc_cnt; ++i) {
+ if (cfg->acc_config[i].cred_info[0].username.slen ||
+ cfg->acc_config[i].cred_info[0].realm.slen)
+ {
+ cfg->acc_config[i].cred_count = 1;
+ cfg->acc_config[i].cred_info[0].scheme = pj_str("digest");
+ }
+ }
+
+ if (pjsua_test_config(cfg, errmsg, sizeof(errmsg)) != PJ_SUCCESS) {
+ PJ_LOG(1,(THIS_FILE, "Error: %s", errmsg));
+ return -1;
+ }
+
return PJ_SUCCESS;
}
@@ -831,7 +830,7 @@ static void dump_media_session(pjmedia_session *session)
/*
* Dump application states.
*/
-void pjsua_dump(pj_bool_t detail)
+PJ_DEF(void) pjsua_dump(pj_bool_t detail)
{
char buf[128];
unsigned old_decor;
@@ -858,9 +857,9 @@ void pjsua_dump(pj_bool_t detail)
PJ_LOG(3,(THIS_FILE, " - no sessions -"));
} else {
- int i;
+ unsigned i;
- for (i=0; i<pjsua.max_calls; ++i) {
+ for (i=0; i<pjsua.config.max_calls; ++i) {
pjsua_call *call = &pjsua.calls[i];
pj_time_val duration, res_delay, con_delay;
@@ -917,13 +916,14 @@ void pjsua_dump(pj_bool_t detail)
/*
* Load settings.
*/
-pj_status_t pjsua_load_settings(const char *filename)
+PJ_DECL(pj_status_t) pjsua_load_settings(const char *filename,
+ pjsua_config *cfg)
{
int argc = 3;
char *argv[4] = { "pjsua", "--config-file", NULL, NULL};
argv[3] = (char*)filename;
- return pjsua_parse_args(argc, argv);
+ return pjsua_parse_args(argc, argv, cfg);
}
@@ -933,7 +933,7 @@ pj_status_t pjsua_load_settings(const char *filename)
static void save_account_settings(int acc_index, pj_str_t *result)
{
char line[128];
- pjsua_acc *acc = &pjsua.acc[acc_index];
+ pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index];
pj_ansi_sprintf(line, "#\n# Account %d:\n#\n", acc_index);
@@ -941,33 +941,55 @@ static void save_account_settings(int acc_index, pj_str_t *result)
/* Identity */
- if (acc->local_uri.slen) {
+ if (acc_cfg->id.slen) {
pj_ansi_sprintf(line, "--id %.*s\n",
- (int)acc->local_uri.slen,
- acc->local_uri.ptr);
+ (int)acc_cfg->id.slen,
+ acc_cfg->id.ptr);
pj_strcat2(result, line);
}
/* Registrar server */
- if (acc->reg_uri.slen) {
+ if (acc_cfg->reg_uri.slen) {
pj_ansi_sprintf(line, "--registrar %.*s\n",
- (int)acc->reg_uri.slen,
- acc->reg_uri.ptr);
+ (int)acc_cfg->reg_uri.slen,
+ acc_cfg->reg_uri.ptr);
pj_strcat2(result, line);
pj_ansi_sprintf(line, "--reg-timeout %u\n",
- acc->reg_timeout);
+ acc_cfg->reg_timeout);
pj_strcat2(result, line);
}
/* Proxy */
- if (acc->proxy.slen) {
+ if (acc_cfg->proxy.slen) {
pj_ansi_sprintf(line, "--proxy %.*s\n",
- (int)acc->proxy.slen,
- acc->proxy.ptr);
+ (int)acc_cfg->proxy.slen,
+ acc_cfg->proxy.ptr);
+ pj_strcat2(result, line);
+ }
+
+ if (acc_cfg->cred_info[0].realm.slen) {
+ pj_ansi_sprintf(line, "--realm %.*s\n",
+ (int)acc_cfg->cred_info[0].realm.slen,
+ acc_cfg->cred_info[0].realm.ptr);
pj_strcat2(result, line);
}
+
+ if (acc_cfg->cred_info[0].username.slen) {
+ pj_ansi_sprintf(line, "--username %.*s\n",
+ (int)acc_cfg->cred_info[0].username.slen,
+ acc_cfg->cred_info[0].username.ptr);
+ pj_strcat2(result, line);
+ }
+
+ if (acc_cfg->cred_info[0].data.slen) {
+ pj_ansi_sprintf(line, "--password %.*s\n",
+ (int)acc_cfg->cred_info[0].data.slen,
+ acc_cfg->cred_info[0].data.ptr);
+ pj_strcat2(result, line);
+ }
+
}
@@ -975,10 +997,11 @@ static void save_account_settings(int acc_index, pj_str_t *result)
/*
* Dump settings.
*/
-int pjsua_dump_settings(char *buf, pj_size_t max)
+PJ_DEF(int) pjsua_dump_settings(const pjsua_config *config,
+ char *buf, pj_size_t max)
{
- int acc_index;
- int i;
+ unsigned acc_index;
+ unsigned i;
pj_str_t cfg;
char line[128];
@@ -991,89 +1014,60 @@ int pjsua_dump_settings(char *buf, pj_size_t max)
/* Logging. */
pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
pj_ansi_sprintf(line, "--log-level %d\n",
- pjsua.log_level);
+ config->log_level);
pj_strcat2(&cfg, line);
pj_ansi_sprintf(line, "--app-log-level %d\n",
- pjsua.app_log_level);
+ config->app_log_level);
pj_strcat2(&cfg, line);
- if (pjsua.log_filename) {
+ if (config->log_filename.slen) {
pj_ansi_sprintf(line, "--log-file %s\n",
- pjsua.log_filename);
+ config->log_filename.ptr);
pj_strcat2(&cfg, line);
}
/* Save account settings. */
- if (pjsua.has_acc) {
- for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
-
- save_account_settings(acc_index, &cfg);
-
- if (acc_index < pjsua.acc_cnt-1)
- pj_strcat2(&cfg, "--next-account\n");
- }
- }
-
- /* Credentials. */
- for (i=0; i<pjsua.cred_count; ++i) {
-
- pj_ansi_sprintf(line, "#\n# Credential %d:\n#\n", i);
- pj_strcat2(&cfg, line);
-
- if (pjsua.cred_info[i].realm.slen) {
- pj_ansi_sprintf(line, "--realm %.*s\n",
- (int)pjsua.cred_info[i].realm.slen,
- pjsua.cred_info[i].realm.ptr);
- pj_strcat2(&cfg, line);
- }
-
- pj_ansi_sprintf(line, "--username %.*s\n",
- (int)pjsua.cred_info[i].username.slen,
- pjsua.cred_info[i].username.ptr);
- pj_strcat2(&cfg, line);
-
- pj_ansi_sprintf(line, "--password %.*s\n",
- (int)pjsua.cred_info[i].data.slen,
- pjsua.cred_info[i].data.ptr);
- pj_strcat2(&cfg, line);
+ for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
+
+ save_account_settings(acc_index, &cfg);
- if (i < pjsua.cred_count-1)
- pj_strcat2(&cfg, "--next-cred\n");
+ if (acc_index < config->acc_cnt-1)
+ pj_strcat2(&cfg, "--next-account\n");
}
pj_strcat2(&cfg, "#\n# Network settings:\n#\n");
/* Outbound proxy */
- if (pjsua.outbound_proxy.slen) {
+ if (config->outbound_proxy.slen) {
pj_ansi_sprintf(line, "--outbound %.*s\n",
- (int)pjsua.outbound_proxy.slen,
- pjsua.outbound_proxy.ptr);
+ (int)config->outbound_proxy.slen,
+ config->outbound_proxy.ptr);
pj_strcat2(&cfg, line);
}
/* Transport. */
- pj_ansi_sprintf(line, "--local-port %d\n", pjsua.sip_port);
+ pj_ansi_sprintf(line, "--local-port %d\n", config->udp_port);
pj_strcat2(&cfg, line);
/* STUN */
- if (pjsua.stun_port1) {
+ if (config->stun_port1) {
pj_ansi_sprintf(line, "--use-stun1 %.*s:%d\n",
- (int)pjsua.stun_srv1.slen,
- pjsua.stun_srv1.ptr,
- pjsua.stun_port1);
+ (int)config->stun_srv1.slen,
+ config->stun_srv1.ptr,
+ config->stun_port1);
pj_strcat2(&cfg, line);
}
- if (pjsua.stun_port2) {
+ if (config->stun_port2) {
pj_ansi_sprintf(line, "--use-stun2 %.*s:%d\n",
- (int)pjsua.stun_srv2.slen,
- pjsua.stun_srv2.ptr,
- pjsua.stun_port2);
+ (int)config->stun_srv2.slen,
+ config->stun_srv2.ptr,
+ config->stun_port2);
pj_strcat2(&cfg, line);
}
@@ -1082,89 +1076,89 @@ int pjsua_dump_settings(char *buf, pj_size_t max)
/* Media */
- if (pjsua.null_audio)
+ if (config->null_audio)
pj_strcat2(&cfg, "--null-audio\n");
- if (pjsua.auto_play)
+ if (config->auto_play)
pj_strcat2(&cfg, "--auto-play\n");
- if (pjsua.auto_loop)
+ if (config->auto_loop)
pj_strcat2(&cfg, "--auto-loop\n");
- if (pjsua.auto_conf)
+ if (config->auto_conf)
pj_strcat2(&cfg, "--auto-conf\n");
- if (pjsua.wav_file) {
+ if (config->wav_file.slen) {
pj_ansi_sprintf(line, "--play-file %s\n",
- pjsua.wav_file);
+ config->wav_file.ptr);
pj_strcat2(&cfg, line);
}
/* Media clock rate. */
- if (pjsua.clock_rate) {
+ if (config->clock_rate) {
pj_ansi_sprintf(line, "--clock-rate %d\n",
- pjsua.clock_rate);
+ config->clock_rate);
pj_strcat2(&cfg, line);
}
/* Encoding quality and complexity */
pj_ansi_sprintf(line, "--quality %d\n",
- pjsua.quality);
+ config->quality);
pj_strcat2(&cfg, line);
pj_ansi_sprintf(line, "--complexity %d\n",
- pjsua.complexity);
+ config->complexity);
pj_strcat2(&cfg, line);
/* ptime */
- if (pjsua.ptime) {
+ if (config->ptime) {
pj_ansi_sprintf(line, "--ptime %d\n",
- pjsua.ptime);
+ config->ptime);
pj_strcat2(&cfg, line);
}
/* Start RTP port. */
pj_ansi_sprintf(line, "--rtp-port %d\n",
- pjsua.start_rtp_port);
+ config->start_rtp_port);
pj_strcat2(&cfg, line);
/* Add codec. */
- for (i=0; i<pjsua.codec_cnt; ++i) {
+ for (i=0; i<config->codec_cnt; ++i) {
pj_ansi_sprintf(line, "--add-codec %s\n",
- pjsua.codec_arg[i].ptr);
+ config->codec_arg[i].ptr);
pj_strcat2(&cfg, line);
}
pj_strcat2(&cfg, "#\n# User agent:\n#\n");
/* Auto-answer. */
- if (pjsua.auto_answer != 0) {
+ if (config->auto_answer != 0) {
pj_ansi_sprintf(line, "--auto-answer %d\n",
- pjsua.auto_answer);
+ config->auto_answer);
pj_strcat2(&cfg, line);
}
/* Max calls. */
pj_ansi_sprintf(line, "--max-calls %d\n",
- pjsua.max_calls);
+ config->max_calls);
pj_strcat2(&cfg, line);
/* Uas-refresh. */
- if (pjsua.uas_refresh > 0) {
+ if (config->uas_refresh > 0) {
pj_ansi_sprintf(line, "--uas-refresh %d\n",
- pjsua.uas_refresh);
+ config->uas_refresh);
pj_strcat2(&cfg, line);
}
/* Uas-duration. */
- if (pjsua.uas_duration > 0) {
+ if (config->uas_duration > 0) {
pj_ansi_sprintf(line, "--uas-duration %d\n",
- pjsua.uas_duration);
+ config->uas_duration);
pj_strcat2(&cfg, line);
}
pj_strcat2(&cfg, "#\n# Buddies:\n#\n");
/* Add buddies. */
- for (i=0; i<pjsua.buddy_cnt; ++i) {
+ for (i=0; i<config->buddy_cnt; ++i) {
pj_ansi_sprintf(line, "--add-buddy %.*s\n",
- (int)pjsua.buddies[i].uri.slen,
- pjsua.buddies[i].uri.ptr);
+ (int)config->buddy_uri[i].slen,
+ config->buddy_uri[i].ptr);
pj_strcat2(&cfg, line);
}
@@ -1176,7 +1170,8 @@ int pjsua_dump_settings(char *buf, pj_size_t max)
/*
* Save settings.
*/
-pj_status_t pjsua_save_settings(const char *filename)
+PJ_DEF(pj_status_t) pjsua_save_settings(const char *filename,
+ const pjsua_config *config)
{
pj_str_t cfg;
pj_pool_t *pool;
@@ -1195,7 +1190,7 @@ pj_status_t pjsua_save_settings(const char *filename)
}
- cfg.slen = pjsua_dump_settings(cfg.ptr, 3800);
+ cfg.slen = pjsua_dump_settings(config, cfg.ptr, 3800);
if (cfg.slen < 1) {
pj_pool_release(pool);
return PJ_ENOMEM;