pjsip logo pjsip.org
Open source SIP stack and media stack for presence, im/instant messaging, and multimedia communication

HOME

SIP/media Features
High Performance SIP
Small Footprint SIP
Symbian Port

FAQ

Documentation

Licensing

Download

Development (Trac)

Projects using pjsip

Mailing List

Open Source Links


About: PJLIB, PJLIB-UTIL, PJSIP, and PJMEDIA are created by: Benny Prijono
<bennylp@pjsip.org>


 

Home --> Documentations --> PJSIP Reference

PJSUA

This is the reference implementation for PJSIP and PJMEDIA. PJSUA is a console based application, designed to be simple enough to be readble, but powerful enough to demonstrate all features available in PJSIP and PJMEDIA.

This file is pjsip-apps/src/pjsua/pjsua_app.c

Screenshot on WinXP:

pjsua.jpg

pjsua on WinXP

00001 /* $Id: pjsua_app.c 2968 2009-10-26 11:21:37Z bennylp $ */
00002 /* 
00003  * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
00004  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
00019  */
00020 #include <pjsua-lib/pjsua.h>
00021 
00022 
00023 #define THIS_FILE       "pjsua_app.c"
00024 #define NO_LIMIT        (int)0x7FFFFFFF
00025 
00026 //#define STEREO_DEMO
00027 //#define TRANSPORT_ADAPTER_SAMPLE
00028 
00029 /* Ringtones                US         UK  */
00030 #define RINGBACK_FREQ1      440     /* 400 */
00031 #define RINGBACK_FREQ2      480     /* 450 */
00032 #define RINGBACK_ON         2000    /* 400 */
00033 #define RINGBACK_OFF        4000    /* 200 */
00034 #define RINGBACK_CNT        1       /* 2   */
00035 #define RINGBACK_INTERVAL   4000    /* 2000 */
00036 
00037 #define RING_FREQ1          800
00038 #define RING_FREQ2          640
00039 #define RING_ON             200
00040 #define RING_OFF            100
00041 #define RING_CNT            3
00042 #define RING_INTERVAL       3000
00043 
00044 
00045 /* Call specific data */
00046 struct call_data
00047 {
00048     pj_timer_entry          timer;
00049     pj_bool_t               ringback_on;
00050     pj_bool_t               ring_on;
00051 };
00052 
00053 
00054 /* Pjsua application data */
00055 static struct app_config
00056 {
00057     pjsua_config            cfg;
00058     pjsua_logging_config    log_cfg;
00059     pjsua_media_config      media_cfg;
00060     pj_bool_t               no_refersub;
00061     pj_bool_t               ipv6;
00062     pj_bool_t               enable_qos;
00063     pj_bool_t               no_tcp;
00064     pj_bool_t               no_udp;
00065     pj_bool_t               use_tls;
00066     pjsua_transport_config  udp_cfg;
00067     pjsua_transport_config  rtp_cfg;
00068     pjsip_redirect_op       redir_op;
00069 
00070     unsigned                acc_cnt;
00071     pjsua_acc_config        acc_cfg[PJSUA_MAX_ACC];
00072 
00073     unsigned                buddy_cnt;
00074     pjsua_buddy_config      buddy_cfg[PJSUA_MAX_BUDDIES];
00075 
00076     struct call_data        call_data[PJSUA_MAX_CALLS];
00077 
00078     pj_pool_t              *pool;
00079     /* Compatibility with older pjsua */
00080 
00081     unsigned                codec_cnt;
00082     pj_str_t                codec_arg[32];
00083     unsigned                codec_dis_cnt;
00084     pj_str_t                codec_dis[32];
00085     pj_bool_t               null_audio;
00086     unsigned                wav_count;
00087     pj_str_t                wav_files[32];
00088     unsigned                tone_count;
00089     pjmedia_tone_desc       tones[32];
00090     pjsua_conf_port_id      tone_slots[32];
00091     pjsua_player_id         wav_id;
00092     pjsua_conf_port_id      wav_port;
00093     pj_bool_t               auto_play;
00094     pj_bool_t               auto_play_hangup;
00095     pj_timer_entry          auto_hangup_timer;
00096     pj_bool_t               auto_loop;
00097     pj_bool_t               auto_conf;
00098     pj_str_t                rec_file;
00099     pj_bool_t               auto_rec;
00100     pjsua_recorder_id       rec_id;
00101     pjsua_conf_port_id      rec_port;
00102     unsigned                auto_answer;
00103     unsigned                duration;
00104 
00105 #ifdef STEREO_DEMO
00106     pjmedia_snd_port       *snd;
00107     pjmedia_port           *sc, *sc_ch1;
00108     pjsua_conf_port_id      sc_ch1_slot;
00109 #endif
00110 
00111     float                   mic_level,
00112                             speaker_level;
00113 
00114     int                     capture_dev, playback_dev;
00115     unsigned                capture_lat, playback_lat;
00116 
00117     pj_bool_t               no_tones;
00118     int                     ringback_slot;
00119     int                     ringback_cnt;
00120     pjmedia_port           *ringback_port;
00121     int                     ring_slot;
00122     int                     ring_cnt;
00123     pjmedia_port           *ring_port;
00124 
00125 } app_config;
00126 
00127 
00128 //static pjsua_acc_id   current_acc;
00129 #define current_acc     pjsua_acc_get_default()
00130 static pjsua_call_id    current_call = PJSUA_INVALID_ID;
00131 static pj_bool_t        cmd_echo;
00132 static int              stdout_refresh = -1;
00133 static const char      *stdout_refresh_text = "STDOUT_REFRESH";
00134 static pj_bool_t        stdout_refresh_quit = PJ_FALSE;
00135 static pj_str_t         uri_arg;
00136 
00137 static char some_buf[1024 * 3];
00138 
00139 #ifdef STEREO_DEMO
00140 static void stereo_demo();
00141 #endif
00142 #ifdef TRANSPORT_ADAPTER_SAMPLE
00143 static pj_status_t transport_adapter_sample(void);
00144 #endif
00145 static pj_status_t create_ipv6_media_transports(void);
00146 pj_status_t app_destroy(void);
00147 
00148 static void ringback_start(pjsua_call_id call_id);
00149 static void ring_start(pjsua_call_id call_id);
00150 static void ring_stop(pjsua_call_id call_id);
00151 
00152 pj_bool_t app_restart;
00153 
00154 /*****************************************************************************
00155  * Configuration manipulation
00156  */
00157 
00158 /* Show usage */
00159 static void usage(void)
00160 {
00161     puts  ("Usage:");
00162     puts  ("  pjsua [options] [SIP URL to call]");
00163     puts  ("");
00164     puts  ("General options:");
00165     puts  ("  --config-file=file  Read the config/arguments from file.");
00166     puts  ("  --help              Display this help screen");
00167     puts  ("  --version           Display version info");
00168     puts  ("");
00169     puts  ("Logging options:");
00170     puts  ("  --log-file=fname    Log to filename (default stderr)");
00171     puts  ("  --log-level=N       Set log max level to N (0(none) to 6(trace)) (default=5)");
00172     puts  ("  --app-log-level=N   Set log max level for stdout display (default=4)");
00173     puts  ("  --color             Use colorful logging (default yes on Win32)");
00174     puts  ("  --no-color          Disable colorful logging");
00175     puts  ("  --light-bg          Use dark colors for light background (default is dark bg)");
00176 
00177     puts  ("");
00178     puts  ("SIP Account options:");
00179     puts  ("  --use-ims           Enable 3GPP/IMS related settings on this account");
00180 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00181     puts  ("  --use-srtp=N        Use SRTP?  0:disabled, 1:optional, 2:mandatory (def:0)");
00182     puts  ("  --srtp-secure=N     SRTP require secure SIP? 0:no, 1:tls, 1:sips (def:1)");
00183 #endif
00184     puts  ("  --registrar=url     Set the URL of registrar server");
00185     puts  ("  --id=url            Set the URL of local ID (used in From header)");
00186     puts  ("  --contact=url       Optionally override the Contact information");
00187     puts  ("  --contact-params=S  Append the specified parameters S in Contact header");
00188     puts  ("  --contact-uri-params=S  Append the specified parameters S in Contact URI");
00189     puts  ("  --proxy=url         Optional URL of proxy server to visit");
00190     puts  ("                      May be specified multiple times");
00191     puts  ("  --reg-timeout=SEC   Optional registration interval (default 55)");
00192     puts  ("  --realm=string      Set realm");
00193     puts  ("  --username=string   Set authentication username");
00194     puts  ("  --password=string   Set authentication password");
00195     puts  ("  --publish           Send presence PUBLISH for this account");
00196     puts  ("  --mwi               Subscribe to message summary/waiting indication");
00197     puts  ("  --use-100rel        Require reliable provisional response (100rel)");
00198     puts  ("  --use-timer         Require SIP session timers");
00199     puts  ("  --timer-se=N        Session timers expiration period, in secs (def:1800)");
00200     puts  ("  --timer-min-se=N    Session timers minimum expiration period, in secs (def:90)");
00201     puts  ("  --auto-update-nat=N Where N is 0 or 1 to enable/disable SIP traversal behind");
00202     puts  ("                      symmetric NAT (default 1)");
00203     puts  ("  --next-cred         Add another credentials");
00204     puts  ("");
00205     puts  ("SIP Account Control:");
00206     puts  ("  --next-account      Add more account");
00207     puts  ("");
00208     puts  ("Transport Options:");
00209 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
00210     puts  ("  --ipv6              Use IPv6 instead for SIP and media.");
00211 #endif
00212     puts  ("  --set-qos           Enable QoS tagging for SIP and media.");
00213     puts  ("  --local-port=port   Set TCP/UDP port. This implicitly enables both ");
00214     puts  ("                      TCP and UDP transports on the specified port, unless");
00215     puts  ("                      if TCP or UDP is disabled.");
00216     puts  ("  --ip-addr=IP        Use the specifed address as SIP and RTP addresses.");
00217     puts  ("                      (Hint: the IP may be the public IP of the NAT/router)");
00218     puts  ("  --bound-addr=IP     Bind transports to this IP interface");
00219     puts  ("  --no-tcp            Disable TCP transport.");
00220     puts  ("  --no-udp            Disable UDP transport.");
00221     puts  ("  --nameserver=NS     Add the specified nameserver to enable SRV resolution");
00222     puts  ("                      This option can be specified multiple times.");
00223     puts  ("  --outbound=url      Set the URL of global outbound proxy server");
00224     puts  ("                      May be specified multiple times");
00225     puts  ("  --stun-srv=FORMAT   Set STUN server host or domain. This option may be");
00226     puts  ("                      specified more than once. FORMAT is hostdom[:PORT]");
00227     puts  ("");
00228     puts  ("TLS Options:");
00229     puts  ("  --use-tls           Enable TLS transport (default=no)");
00230     puts  ("  --tls-ca-file       Specify TLS CA file (default=none)");
00231     puts  ("  --tls-cert-file     Specify TLS certificate file (default=none)");
00232     puts  ("  --tls-privkey-file  Specify TLS private key file (default=none)");
00233     puts  ("  --tls-password      Specify TLS password to private key file (default=none)");
00234     puts  ("  --tls-verify-server Verify server's certificate (default=no)");
00235     puts  ("  --tls-verify-client Verify client's certificate (default=no)");
00236     puts  ("  --tls-neg-timeout   Specify TLS negotiation timeout (default=no)");
00237     puts  ("  --tls-srv-name      Specify TLS server name for multi-hosting server (optional)");
00238 
00239     puts  ("");
00240     puts  ("Media Options:");
00241     puts  ("  --add-codec=name    Manually add codec (default is to enable all)");
00242     puts  ("  --dis-codec=name    Disable codec (can be specified multiple times)");
00243     puts  ("  --clock-rate=N      Override conference bridge clock rate");
00244     puts  ("  --snd-clock-rate=N  Override sound device clock rate");
00245     puts  ("  --stereo            Audio device and conference bridge opened in stereo mode");
00246     puts  ("  --null-audio        Use NULL audio device");
00247     puts  ("  --play-file=file    Register WAV file in conference bridge.");
00248     puts  ("                      This can be specified multiple times.");
00249     puts  ("  --play-tone=FORMAT  Register tone to the conference bridge.");
00250     puts  ("                      FORMAT is 'F1,F2,ON,OFF', where F1,F2 are");
00251     puts  ("                      frequencies, and ON,OFF=on/off duration in msec.");
00252     puts  ("                      This can be specified multiple times.");
00253     puts  ("  --auto-play         Automatically play the file (to incoming calls only)");
00254     puts  ("  --auto-loop         Automatically loop incoming RTP to outgoing RTP");
00255     puts  ("  --auto-conf         Automatically put calls in conference with others");
00256     puts  ("  --rec-file=file     Open file recorder (extension can be .wav or .mp3");
00257     puts  ("  --auto-rec          Automatically record conversation");
00258     puts  ("  --quality=N         Specify media quality (0-10, default=6)");
00259     puts  ("  --ptime=MSEC        Override codec ptime to MSEC (default=specific)");
00260     puts  ("  --no-vad            Disable VAD/silence detector (default=vad enabled)");
00261     puts  ("  --ec-tail=MSEC      Set echo canceller tail length (default=256)");
00262     puts  ("  --ec-opt=OPT        Select echo canceller algorithm (0=default, ");
00263     puts  ("                        1=speex, 2=suppressor)");
00264     puts  ("  --ilbc-mode=MODE    Set iLBC codec mode (20 or 30, default is 30)");
00265     puts  ("  --capture-dev=id    Audio capture device ID (default=-1)");
00266     puts  ("  --playback-dev=id   Audio playback device ID (default=-1)");
00267     puts  ("  --capture-lat=N     Audio capture latency, in ms (default=100)");
00268     puts  ("  --playback-lat=N    Audio playback latency, in ms (default=100)");
00269     puts  ("  --snd-auto-close=N  Auto close audio device when it is idle for N seconds.");
00270     puts  ("                      Specify N=-1 (default) to disable this feature.");
00271     puts  ("                      Specify N=0 for instant close when unused.");
00272     puts  ("  --no-tones          Disable audible tones");
00273     puts  ("  --jb-max-size       Specify jitter buffer maximum size, in frames (default=-1)");
00274 
00275     puts  ("");
00276     puts  ("Media Transport Options:");
00277     puts  ("  --use-ice           Enable ICE (default:no)");
00278     puts  ("  --ice-regular       Use ICE regular nomination (default: aggressive)");
00279     puts  ("  --ice-max-hosts=N   Set maximum number of ICE host candidates");
00280     puts  ("  --ice-no-rtcp       Disable RTCP component in ICE (default: no)");
00281     puts  ("  --rtp-port=N        Base port to try for RTP (default=4000)");
00282     puts  ("  --rx-drop-pct=PCT   Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
00283     puts  ("  --tx-drop-pct=PCT   Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
00284     puts  ("  --use-turn          Enable TURN relay with ICE (default:no)");
00285     puts  ("  --turn-srv          Domain or host name of TURN server (\"NAME:PORT\" format)");
00286     puts  ("  --turn-tcp          Use TCP connection to TURN server (default no)");
00287     puts  ("  --turn-user         TURN username");
00288     puts  ("  --turn-passwd       TURN password");
00289 
00290     puts  ("");
00291     puts  ("Buddy List (can be more than one):");
00292     puts  ("  --add-buddy url     Add the specified URL to the buddy list.");
00293     puts  ("");
00294     puts  ("User Agent options:");
00295     puts  ("  --auto-answer=code  Automatically answer incoming calls with code (e.g. 200)");
00296     puts  ("  --max-calls=N       Maximum number of concurrent calls (default:4, max:255)");
00297     puts  ("  --thread-cnt=N      Number of worker threads (default:1)");
00298     puts  ("  --duration=SEC      Set maximum call duration (default:no limit)");
00299     puts  ("  --norefersub        Suppress event subscription when transfering calls");
00300     puts  ("  --use-compact-form  Minimize SIP message size");
00301     puts  ("  --no-force-lr       Allow strict-route to be used (i.e. do not force lr)");
00302     puts  ("  --accept-redirect=N Specify how to handle call redirect (3xx) response.");
00303     puts  ("                      0: reject, 1: follow automatically (default), 2: ask");
00304 
00305     puts  ("");
00306     puts  ("When URL is specified, pjsua will immediately initiate call to that URL");
00307     puts  ("");
00308 
00309     fflush(stdout);
00310 }
00311 
00312 
00313 /* Set default config. */
00314 static void default_config(struct app_config *cfg)
00315 {
00316     char tmp[80];
00317     unsigned i;
00318 
00319     pjsua_config_default(&cfg->cfg);
00320     pj_ansi_sprintf(tmp, "PJSUA v%s/%s", pj_get_version(), PJ_OS_NAME);
00321     pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
00322 
00323     pjsua_logging_config_default(&cfg->log_cfg);
00324     pjsua_media_config_default(&cfg->media_cfg);
00325     pjsua_transport_config_default(&cfg->udp_cfg);
00326     cfg->udp_cfg.port = 5060;
00327     pjsua_transport_config_default(&cfg->rtp_cfg);
00328     cfg->rtp_cfg.port = 4000;
00329     cfg->redir_op = PJSIP_REDIRECT_ACCEPT;
00330     cfg->duration = NO_LIMIT;
00331     cfg->wav_id = PJSUA_INVALID_ID;
00332     cfg->rec_id = PJSUA_INVALID_ID;
00333     cfg->wav_port = PJSUA_INVALID_ID;
00334     cfg->rec_port = PJSUA_INVALID_ID;
00335     cfg->mic_level = cfg->speaker_level = 1.0;
00336     cfg->capture_dev = PJSUA_INVALID_ID;
00337     cfg->playback_dev = PJSUA_INVALID_ID;
00338     cfg->capture_lat = PJMEDIA_SND_DEFAULT_REC_LATENCY;
00339     cfg->playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
00340     cfg->ringback_slot = PJSUA_INVALID_ID;
00341     cfg->ring_slot = PJSUA_INVALID_ID;
00342 
00343     for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i)
00344         pjsua_acc_config_default(&cfg->acc_cfg[i]);
00345 
00346     for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
00347         pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
00348 }
00349 
00350 
00351 /*
00352  * Read command arguments from config file.
00353  */
00354 static int read_config_file(pj_pool_t *pool, const char *filename, 
00355                             int *app_argc, char ***app_argv)
00356 {
00357     int i;
00358     FILE *fhnd;
00359     char line[200];
00360     int argc = 0;
00361     char **argv;
00362     enum { MAX_ARGS = 64 };
00363 
00364     /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
00365     argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
00366     argv[argc++] = *app_argv[0];
00367 
00368     /* Open config file. */
00369     fhnd = fopen(filename, "rt");
00370     if (!fhnd) {
00371         PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
00372         fflush(stdout);
00373         return -1;
00374     }
00375 
00376     /* Scan tokens in the file. */
00377     while (argc < MAX_ARGS && !feof(fhnd)) {
00378         char  *token;
00379         char  *p;
00380         const char *whitespace = " \t\r\n";
00381         char  cDelimiter;
00382         int   len, token_len;
00383         
00384         if (fgets(line, sizeof(line), fhnd) == NULL) break;
00385         
00386         // Trim ending newlines
00387         len = strlen(line);
00388         if (line[len-1]=='\n')
00389             line[--len] = '\0';
00390         if (line[len-1]=='\r')
00391             line[--len] = '\0';
00392 
00393         if (len==0) continue;
00394 
00395         for (p = line; *p != '\0' && argc < MAX_ARGS; p++) {
00396             // first, scan whitespaces
00397             while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
00398 
00399             if (*p == '\0')                 // are we done yet?
00400                 break;
00401             
00402             if (*p == '"' || *p == '\'') {    // is token a quoted string
00403                 cDelimiter = *p++;          // save quote delimiter
00404                 token = p;
00405                 
00406                 while (*p != '\0' && *p != cDelimiter) p++;
00407                 
00408                 if (*p == '\0')         // found end of the line, but,
00409                     cDelimiter = '\0';  // didn't find a matching quote
00410 
00411             } else {                    // token's not a quoted string
00412                 token = p;
00413                 
00414                 while (*p != '\0' && strchr(whitespace, *p) == NULL) p++;
00415                 
00416                 cDelimiter = *p;
00417             }
00418             
00419             *p = '\0';
00420             token_len = p-token;
00421             
00422             if (token_len > 0) {
00423                 if (*token == '#')
00424                     break;  // ignore remainder of line
00425                 
00426                 argv[argc] = pj_pool_alloc(pool, token_len + 1);
00427                 pj_memcpy(argv[argc], token, token_len + 1);
00428                 ++argc;
00429             }
00430             
00431             *p = cDelimiter;
00432         }
00433     }
00434 
00435     /* Copy arguments from command line */
00436     for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
00437         argv[argc++] = (*app_argv)[i];
00438 
00439     if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
00440         PJ_LOG(1,(THIS_FILE, 
00441                   "Too many arguments specified in cmd line/config file"));
00442         fflush(stdout);
00443         fclose(fhnd);
00444         return -1;
00445     }
00446 
00447     fclose(fhnd);
00448 
00449     /* Assign the new command line back to the original command line. */
00450     *app_argc = argc;
00451     *app_argv = argv;
00452     return 0;
00453 
00454 }
00455 
00456 static int my_atoi(const char *cs)
00457 {
00458     pj_str_t s;
00459 
00460     pj_cstr(&s, cs);
00461     if (cs[0] == '-') {
00462         s.ptr++, s.slen--;
00463         return 0 - (int)pj_strtoul(&s);
00464     } else if (cs[0] == '+') {
00465         s.ptr++, s.slen--;
00466         return pj_strtoul(&s);
00467     } else {
00468         return pj_strtoul(&s);
00469     }
00470 }
00471 
00472 
00473 /* Parse arguments. */
00474 static pj_status_t parse_args(int argc, char *argv[],
00475                               struct app_config *cfg,
00476                               pj_str_t *uri_to_call)
00477 {
00478     int c;
00479     int option_index;
00480     enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, 
00481            OPT_COLOR, OPT_NO_COLOR, OPT_LIGHT_BG,
00482            OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, OPT_SND_AUTO_CLOSE,
00483            OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY, 
00484            OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
00485            OPT_BOUND_ADDR, OPT_CONTACT_PARAMS, OPT_CONTACT_URI_PARAMS,
00486            OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
00487            OPT_MWI, OPT_NAMESERVER, OPT_STUN_SRV,
00488            OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
00489            OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
00490            OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
00491            OPT_USE_ICE, OPT_ICE_REGULAR, OPT_USE_SRTP, OPT_SRTP_SECURE,
00492            OPT_USE_TURN, OPT_ICE_MAX_HOSTS, OPT_ICE_NO_RTCP, OPT_TURN_SRV, 
00493            OPT_TURN_TCP, OPT_TURN_USER, OPT_TURN_PASSWD,
00494            OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, 
00495            OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
00496            OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
00497            OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL, OPT_EC_OPT,
00498            OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS, 
00499            OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
00500            OPT_NOREFERSUB, OPT_ACCEPT_REDIRECT,
00501            OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE,
00502            OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT,
00503            OPT_TLS_NEG_TIMEOUT, OPT_TLS_SRV_NAME,
00504            OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV,
00505            OPT_CAPTURE_LAT, OPT_PLAYBACK_LAT, OPT_NO_TONES, OPT_JB_MAX_SIZE,
00506            OPT_STDOUT_REFRESH, OPT_STDOUT_REFRESH_TEXT, OPT_IPV6, OPT_QOS,
00507 #ifdef _IONBF
00508            OPT_STDOUT_NO_BUF,
00509 #endif
00510            OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC,
00511            OPT_NO_FORCE_LR,
00512            OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE
00513     };
00514     struct pj_getopt_option long_options[] = {
00515         { "config-file",1, 0, OPT_CONFIG_FILE},
00516         { "log-file",   1, 0, OPT_LOG_FILE},
00517         { "log-level",  1, 0, OPT_LOG_LEVEL},
00518         { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
00519         { "color",      0, 0, OPT_COLOR},
00520         { "no-color",   0, 0, OPT_NO_COLOR},
00521         { "light-bg",           0, 0, OPT_LIGHT_BG},
00522         { "help",       0, 0, OPT_HELP},
00523         { "version",    0, 0, OPT_VERSION},
00524         { "clock-rate", 1, 0, OPT_CLOCK_RATE},
00525         { "snd-clock-rate",     1, 0, OPT_SND_CLOCK_RATE},
00526         { "stereo",     0, 0, OPT_STEREO},
00527         { "null-audio", 0, 0, OPT_NULL_AUDIO},
00528         { "local-port", 1, 0, OPT_LOCAL_PORT},
00529         { "ip-addr",    1, 0, OPT_IP_ADDR},
00530         { "bound-addr", 1, 0, OPT_BOUND_ADDR},
00531         { "no-tcp",     0, 0, OPT_NO_TCP},
00532         { "no-udp",     0, 0, OPT_NO_UDP},
00533         { "norefersub", 0, 0, OPT_NOREFERSUB},
00534         { "proxy",      1, 0, OPT_PROXY},
00535         { "outbound",   1, 0, OPT_OUTBOUND_PROXY},
00536         { "registrar",  1, 0, OPT_REGISTRAR},
00537         { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
00538         { "publish",    0, 0, OPT_PUBLISH},
00539         { "mwi",        0, 0, OPT_MWI},
00540         { "use-100rel", 0, 0, OPT_100REL},
00541         { "use-ims",    0, 0, OPT_USE_IMS},
00542         { "id",         1, 0, OPT_ID},
00543         { "contact",    1, 0, OPT_CONTACT},
00544         { "contact-params",1,0, OPT_CONTACT_PARAMS},
00545         { "contact-uri-params",1,0, OPT_CONTACT_URI_PARAMS},
00546         { "auto-update-nat",    1, 0, OPT_AUTO_UPDATE_NAT},
00547         { "use-compact-form",   0, 0, OPT_USE_COMPACT_FORM},
00548         { "accept-redirect", 1, 0, OPT_ACCEPT_REDIRECT},
00549         { "no-force-lr",0, 0, OPT_NO_FORCE_LR},
00550         { "realm",      1, 0, OPT_REALM},
00551         { "username",   1, 0, OPT_USERNAME},
00552         { "password",   1, 0, OPT_PASSWORD},
00553         { "nameserver", 1, 0, OPT_NAMESERVER},
00554         { "stun-srv",   1, 0, OPT_STUN_SRV},
00555         { "add-buddy",  1, 0, OPT_ADD_BUDDY},
00556         { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
00557         { "no-presence", 0, 0, OPT_NO_PRESENCE},
00558         { "auto-answer",1, 0, OPT_AUTO_ANSWER},
00559         { "auto-play",  0, 0, OPT_AUTO_PLAY},
00560         { "auto-play-hangup",0, 0, OPT_AUTO_PLAY_HANGUP},
00561         { "auto-rec",   0, 0, OPT_AUTO_REC},
00562         { "auto-loop",  0, 0, OPT_AUTO_LOOP},
00563         { "auto-conf",  0, 0, OPT_AUTO_CONF},
00564         { "play-file",  1, 0, OPT_PLAY_FILE},
00565         { "play-tone",  1, 0, OPT_PLAY_TONE},
00566         { "rec-file",   1, 0, OPT_REC_FILE},
00567         { "rtp-port",   1, 0, OPT_RTP_PORT},
00568 
00569         { "use-ice",    0, 0, OPT_USE_ICE},
00570         { "ice-regular",0, 0, OPT_ICE_REGULAR},
00571         { "use-turn",   0, 0, OPT_USE_TURN},
00572         { "ice-max-hosts",1, 0, OPT_ICE_MAX_HOSTS},
00573         { "ice-no-rtcp",0, 0, OPT_ICE_NO_RTCP},
00574         { "turn-srv",   1, 0, OPT_TURN_SRV},
00575         { "turn-tcp",   0, 0, OPT_TURN_TCP},
00576         { "turn-user",  1, 0, OPT_TURN_USER},
00577         { "turn-passwd",1, 0, OPT_TURN_PASSWD},
00578 
00579 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00580         { "use-srtp",   1, 0, OPT_USE_SRTP},
00581         { "srtp-secure",1, 0, OPT_SRTP_SECURE},
00582 #endif
00583         { "add-codec",  1, 0, OPT_ADD_CODEC},
00584         { "dis-codec",  1, 0, OPT_DIS_CODEC},
00585         { "complexity", 1, 0, OPT_COMPLEXITY},
00586         { "quality",    1, 0, OPT_QUALITY},
00587         { "ptime",      1, 0, OPT_PTIME},
00588         { "no-vad",     0, 0, OPT_NO_VAD},
00589         { "ec-tail",    1, 0, OPT_EC_TAIL},
00590         { "ec-opt",     1, 0, OPT_EC_OPT},
00591         { "ilbc-mode",  1, 0, OPT_ILBC_MODE},
00592         { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
00593         { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
00594         { "next-account",0,0, OPT_NEXT_ACCOUNT},
00595         { "next-cred",  0, 0, OPT_NEXT_CRED},
00596         { "max-calls",  1, 0, OPT_MAX_CALLS},
00597         { "duration",   1, 0, OPT_DURATION},
00598         { "thread-cnt", 1, 0, OPT_THREAD_CNT},
00599         { "use-tls",    0, 0, OPT_USE_TLS}, 
00600         { "tls-ca-file",1, 0, OPT_TLS_CA_FILE},
00601         { "tls-cert-file",1,0, OPT_TLS_CERT_FILE}, 
00602         { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE},
00603         { "tls-password",1,0, OPT_TLS_PASSWORD},
00604         { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER},
00605         { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT},
00606         { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT},
00607         { "tls-srv-name", 1, 0, OPT_TLS_SRV_NAME},
00608         { "capture-dev",    1, 0, OPT_CAPTURE_DEV},
00609         { "playback-dev",   1, 0, OPT_PLAYBACK_DEV},
00610         { "capture-lat",    1, 0, OPT_CAPTURE_LAT},
00611         { "playback-lat",   1, 0, OPT_PLAYBACK_LAT},
00612         { "stdout-refresh", 1, 0, OPT_STDOUT_REFRESH},
00613         { "stdout-refresh-text", 1, 0, OPT_STDOUT_REFRESH_TEXT},
00614 #ifdef _IONBF
00615         { "stdout-no-buf",  0, 0, OPT_STDOUT_NO_BUF },
00616 #endif
00617         { "snd-auto-close", 1, 0, OPT_SND_AUTO_CLOSE},
00618         { "no-tones",    0, 0, OPT_NO_TONES},
00619         { "jb-max-size", 1, 0, OPT_JB_MAX_SIZE},
00620 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
00621         { "ipv6",        0, 0, OPT_IPV6},
00622 #endif
00623         { "set-qos",     0, 0, OPT_QOS},
00624         { "use-timer",  0, 0, OPT_TIMER},
00625         { "timer-se",   1, 0, OPT_TIMER_SE},
00626         { "timer-min-se", 1, 0, OPT_TIMER_MIN_SE},
00627         { NULL, 0, 0, 0}
00628     };
00629     pj_status_t status;
00630     pjsua_acc_config *cur_acc;
00631     char *config_file = NULL;
00632     unsigned i;
00633 
00634     /* Run pj_getopt once to see if user specifies config file to read. */ 
00635     pj_optind = 0;
00636     while ((c=pj_getopt_long(argc, argv, "", long_options, 
00637                              &option_index)) != -1) 
00638     {
00639         switch (c) {
00640         case OPT_CONFIG_FILE:
00641             config_file = pj_optarg;
00642             break;
00643         }
00644         if (config_file)
00645             break;
00646     }
00647 
00648     if (config_file) {
00649         status = read_config_file(app_config.pool, config_file, &argc, &argv);
00650         if (status != 0)
00651             return status;
00652     }
00653 
00654     cfg->acc_cnt = 0;
00655     cur_acc = &cfg->acc_cfg[0];
00656 
00657 
00658     /* Reinitialize and re-run pj_getopt again, possibly with new arguments
00659      * read from config file.
00660      */
00661     pj_optind = 0;
00662     while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
00663         pj_str_t tmp;
00664         long lval;
00665 
00666         switch (c) {
00667 
00668         case OPT_CONFIG_FILE:
00669             /* Ignore as this has been processed before */
00670             break;
00671         
00672         case OPT_LOG_FILE:
00673             cfg->log_cfg.log_filename = pj_str(pj_optarg);
00674             break;
00675 
00676         case OPT_LOG_LEVEL:
00677             c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00678             if (c < 0 || c > 6) {
00679                 PJ_LOG(1,(THIS_FILE, 
00680                           "Error: expecting integer value 0-6 "
00681                           "for --log-level"));
00682                 return PJ_EINVAL;
00683             }
00684             cfg->log_cfg.level = c;
00685             pj_log_set_level( c );
00686             break;
00687 
00688         case OPT_APP_LOG_LEVEL:
00689             cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00690             if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) {
00691                 PJ_LOG(1,(THIS_FILE, 
00692                           "Error: expecting integer value 0-6 "
00693                           "for --app-log-level"));
00694                 return PJ_EINVAL;
00695             }
00696             break;
00697 
00698         case OPT_COLOR:
00699             cfg->log_cfg.decor |= PJ_LOG_HAS_COLOR;
00700             break;
00701 
00702         case OPT_NO_COLOR:
00703             cfg->log_cfg.decor &= ~PJ_LOG_HAS_COLOR;
00704             break;
00705 
00706         case OPT_LIGHT_BG:
00707             pj_log_set_color(1, PJ_TERM_COLOR_R);
00708             pj_log_set_color(2, PJ_TERM_COLOR_R | PJ_TERM_COLOR_G);
00709             pj_log_set_color(3, PJ_TERM_COLOR_B | PJ_TERM_COLOR_G);
00710             pj_log_set_color(4, 0);
00711             pj_log_set_color(5, 0);
00712             pj_log_set_color(77, 0);
00713             break;
00714 
00715         case OPT_HELP:
00716             usage();
00717             return PJ_EINVAL;
00718 
00719         case OPT_VERSION:   /* version */
00720             pj_dump_config();
00721             return PJ_EINVAL;
00722 
00723         case OPT_NULL_AUDIO:
00724             cfg->null_audio = PJ_TRUE;
00725             break;
00726 
00727         case OPT_CLOCK_RATE:
00728             lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00729             if (lval < 8000 || lval > 192000) {
00730                 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
00731                                      "8000-192000 for conference clock rate"));
00732                 return PJ_EINVAL;
00733             }
00734             cfg->media_cfg.clock_rate = lval; 
00735             break;
00736 
00737         case OPT_SND_CLOCK_RATE:
00738             lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00739             if (lval < 8000 || lval > 192000) {
00740                 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
00741                                      "8000-192000 for sound device clock rate"));
00742                 return PJ_EINVAL;
00743             }
00744             cfg->media_cfg.snd_clock_rate = lval; 
00745             break;
00746 
00747         case OPT_STEREO:
00748             cfg->media_cfg.channel_count = 2;
00749             break;
00750 
00751         case OPT_LOCAL_PORT:   /* local-port */
00752             lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00753             if (lval < 0 || lval > 65535) {
00754                 PJ_LOG(1,(THIS_FILE, 
00755                           "Error: expecting integer value for "
00756                           "--local-port"));
00757                 return PJ_EINVAL;
00758             }
00759             cfg->udp_cfg.port = (pj_uint16_t)lval;
00760             break;
00761 
00762         case OPT_IP_ADDR: /* ip-addr */
00763             cfg->udp_cfg.public_addr = pj_str(pj_optarg);
00764             cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
00765             break;
00766 
00767         case OPT_BOUND_ADDR: /* bound-addr */
00768             cfg->udp_cfg.bound_addr = pj_str(pj_optarg);
00769             cfg->rtp_cfg.bound_addr = pj_str(pj_optarg);
00770             break;
00771 
00772         case OPT_NO_UDP: /* no-udp */
00773             if (cfg->no_tcp) {
00774               PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
00775               return PJ_EINVAL;
00776             }
00777 
00778             cfg->no_udp = PJ_TRUE;
00779             break;
00780 
00781         case OPT_NOREFERSUB: /* norefersub */
00782             cfg->no_refersub = PJ_TRUE;
00783             break;
00784 
00785         case OPT_NO_TCP: /* no-tcp */
00786             if (cfg->no_udp) {
00787               PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
00788               return PJ_EINVAL;
00789             }
00790 
00791             cfg->no_tcp = PJ_TRUE;
00792             break;
00793 
00794         case OPT_PROXY:   /* proxy */
00795             if (pjsua_verify_sip_url(pj_optarg) != 0) {
00796                 PJ_LOG(1,(THIS_FILE, 
00797                           "Error: invalid SIP URL '%s' "
00798                           "in proxy argument", pj_optarg));
00799                 return PJ_EINVAL;
00800             }
00801             cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
00802             break;
00803 
00804         case OPT_OUTBOUND_PROXY:   /* outbound proxy */
00805             if (pjsua_verify_sip_url(pj_optarg) != 0) {
00806                 PJ_LOG(1,(THIS_FILE, 
00807                           "Error: invalid SIP URL '%s' "
00808                           "in outbound proxy argument", pj_optarg));
00809                 return PJ_EINVAL;
00810             }
00811             cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
00812             break;
00813 
00814         case OPT_REGISTRAR:   /* registrar */
00815             if (pjsua_verify_sip_url(pj_optarg) != 0) {
00816                 PJ_LOG(1,(THIS_FILE, 
00817                           "Error: invalid SIP URL '%s' in "
00818                           "registrar argument", pj_optarg));
00819                 return PJ_EINVAL;
00820             }
00821             cur_acc->reg_uri = pj_str(pj_optarg);
00822             break;
00823 
00824         case OPT_REG_TIMEOUT:   /* reg-timeout */
00825             cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
00826             if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
00827                 PJ_LOG(1,(THIS_FILE, 
00828                           "Error: invalid value for --reg-timeout "
00829                           "(expecting 1-3600)"));
00830                 return PJ_EINVAL;
00831             }
00832             break;
00833 
00834         case OPT_PUBLISH:   /* publish */
00835             cur_acc->publish_enabled = PJ_TRUE;
00836             break;
00837 
00838         case OPT_MWI:   /* mwi */
00839             cur_acc->mwi_enabled = PJ_TRUE;
00840             break;
00841 
00842         case OPT_100REL: 
00843             cur_acc->require_100rel = PJ_TRUE;
00844             cfg->cfg.require_100rel = PJ_TRUE;
00845             break;
00846 
00847         case OPT_TIMER: 
00848             cur_acc->require_timer = PJ_TRUE;
00849             cfg->cfg.require_timer = PJ_TRUE;
00850             break;
00851 
00852         case OPT_TIMER_SE: 
00853             cur_acc->timer_setting.sess_expires = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00854             if (cur_acc->timer_setting.sess_expires < 90) {
00855                 PJ_LOG(1,(THIS_FILE, 
00856                           "Error: invalid value for --timer-se "
00857                           "(expecting higher than 90)"));
00858                 return PJ_EINVAL;
00859             }
00860             cfg->cfg.timer_setting.sess_expires = cur_acc->timer_setting.sess_expires;
00861             break;
00862 
00863         case OPT_TIMER_MIN_SE: 
00864             cur_acc->timer_setting.min_se = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00865             if (cur_acc->timer_setting.min_se < 90) {
00866                 PJ_LOG(1,(THIS_FILE, 
00867                           "Error: invalid value for --timer-min-se "
00868                           "(expecting higher than 90)"));
00869                 return PJ_EINVAL;
00870             }
00871             cfg->cfg.timer_setting.min_se = cur_acc->timer_setting.min_se;
00872             break;
00873 
00874         case OPT_USE_IMS: /* Activate IMS settings */
00875             cur_acc->auth_pref.initial_auth = PJ_TRUE;
00876             break;
00877 
00878         case OPT_ID:   /* id */
00879             if (pjsua_verify_sip_url(pj_optarg) != 0) {
00880                 PJ_LOG(1,(THIS_FILE, 
00881                           "Error: invalid SIP URL '%s' "
00882                           "in local id argument", pj_optarg));
00883                 return PJ_EINVAL;
00884             }
00885             cur_acc->id = pj_str(pj_optarg);
00886             break;
00887 
00888         case OPT_CONTACT:   /* contact */
00889             if (pjsua_verify_sip_url(pj_optarg) != 0) {
00890                 PJ_LOG(1,(THIS_FILE, 
00891                           "Error: invalid SIP URL '%s' "
00892                           "in contact argument", pj_optarg));
00893                 return PJ_EINVAL;
00894             }
00895             cur_acc->force_contact = pj_str(pj_optarg);
00896             break;
00897 
00898         case OPT_CONTACT_PARAMS:
00899             cur_acc->contact_params = pj_str(pj_optarg);
00900             break;
00901 
00902         case OPT_CONTACT_URI_PARAMS:
00903             cur_acc->contact_uri_params = pj_str(pj_optarg);
00904             break;
00905 
00906         case OPT_AUTO_UPDATE_NAT:   /* OPT_AUTO_UPDATE_NAT */
00907             cur_acc->allow_contact_rewrite  = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00908             break;
00909 
00910         case OPT_USE_COMPACT_FORM:
00911             /* enable compact form - from Ticket #342 */
00912             {
00913                 extern pj_bool_t pjsip_use_compact_form;
00914                 extern pj_bool_t pjsip_include_allow_hdr_in_dlg;
00915                 extern pj_bool_t pjmedia_add_rtpmap_for_static_pt;
00916 
00917                 pjsip_use_compact_form = PJ_TRUE;
00918                 /* do not transmit Allow header */
00919                 pjsip_include_allow_hdr_in_dlg = PJ_FALSE;
00920                 /* Do not include rtpmap for static payload types (<96) */
00921                 pjmedia_add_rtpmap_for_static_pt = PJ_FALSE;
00922             }
00923             break;
00924 
00925         case OPT_ACCEPT_REDIRECT:
00926             cfg->redir_op = my_atoi(pj_optarg);
00927             if (cfg->redir_op<0 || cfg->redir_op>PJSIP_REDIRECT_STOP) {
00928                 PJ_LOG(1,(THIS_FILE, 
00929                           "Error: accept-redirect value '%s' ", pj_optarg));
00930                 return PJ_EINVAL;
00931             }
00932             break;
00933 
00934         case OPT_NO_FORCE_LR:
00935             cfg->cfg.force_lr = PJ_FALSE;
00936             break;
00937 
00938         case OPT_NEXT_ACCOUNT: /* Add more account. */
00939             cfg->acc_cnt++;
00940             cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
00941             break;
00942 
00943         case OPT_USERNAME:   /* Default authentication user */
00944             cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
00945             cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("Digest");
00946             break;
00947 
00948         case OPT_REALM:     /* Default authentication realm. */
00949             cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
00950             break;
00951 
00952         case OPT_PASSWORD:   /* authentication password */
00953             cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
00954             cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
00955 #if PJSIP_HAS_DIGEST_AKA_AUTH
00956             cur_acc->cred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA;
00957             cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg);
00958             cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response;
00959 #endif
00960             break;
00961 
00962         case OPT_NEXT_CRED: /* next credential */
00963             cur_acc->cred_count++;
00964             break;
00965 
00966         case OPT_NAMESERVER: /* nameserver */
00967             cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
00968             if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
00969                 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
00970                 return PJ_ETOOMANY;
00971             }
00972             break;
00973 
00974         case OPT_STUN_SRV:   /* STUN server */
00975             cfg->cfg.stun_host = pj_str(pj_optarg);
00976             if (cfg->cfg.stun_srv_cnt==PJ_ARRAY_SIZE(cfg->cfg.stun_srv)) {
00977                 PJ_LOG(1,(THIS_FILE, "Error: too many STUN servers"));
00978                 return PJ_ETOOMANY;
00979             }
00980             cfg->cfg.stun_srv[cfg->cfg.stun_srv_cnt++] = pj_str(pj_optarg);
00981             break;
00982 
00983         case OPT_ADD_BUDDY: /* Add to buddy list. */
00984             if (pjsua_verify_sip_url(pj_optarg) != 0) {
00985                 PJ_LOG(1,(THIS_FILE, 
00986                           "Error: invalid URL '%s' in "
00987                           "--add-buddy option", pj_optarg));
00988                 return -1;
00989             }
00990             if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
00991                 PJ_LOG(1,(THIS_FILE, 
00992                           "Error: too many buddies in buddy list."));
00993                 return -1;
00994             }
00995             cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
00996             cfg->buddy_cnt++;
00997             break;
00998 
00999         case OPT_AUTO_PLAY:
01000             cfg->auto_play = 1;
01001             break;
01002 
01003         case OPT_AUTO_PLAY_HANGUP:
01004             cfg->auto_play_hangup = 1;
01005             break;
01006 
01007         case OPT_AUTO_REC:
01008             cfg->auto_rec = 1;
01009             break;
01010 
01011         case OPT_AUTO_LOOP:
01012             cfg->auto_loop = 1;
01013             break;
01014 
01015         case OPT_AUTO_CONF:
01016             cfg->auto_conf = 1;
01017             break;
01018 
01019         case OPT_PLAY_FILE:
01020             cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
01021             break;
01022 
01023         case OPT_PLAY_TONE:
01024             {
01025                 int f1, f2, on, off;
01026                 int n;
01027 
01028                 n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
01029                 if (n != 4) {
01030                     puts("Expecting f1,f2,on,off in --play-tone");
01031                     return -1;
01032                 }
01033 
01034                 cfg->tones[cfg->tone_count].freq1 = (short)f1;
01035                 cfg->tones[cfg->tone_count].freq2 = (short)f2;
01036                 cfg->tones[cfg->tone_count].on_msec = (short)on;
01037                 cfg->tones[cfg->tone_count].off_msec = (short)off;
01038                 ++cfg->tone_count;
01039             }
01040             break;
01041 
01042         case OPT_REC_FILE:
01043             cfg->rec_file = pj_str(pj_optarg);
01044             break;
01045 
01046         case OPT_USE_ICE:
01047             cfg->media_cfg.enable_ice = PJ_TRUE;
01048             break;
01049 
01050         case OPT_ICE_REGULAR:
01051             cfg->media_cfg.ice_opt.aggressive = PJ_FALSE;
01052             break;
01053 
01054         case OPT_USE_TURN:
01055             cfg->media_cfg.enable_turn = PJ_TRUE;
01056             break;
01057 
01058         case OPT_ICE_MAX_HOSTS:
01059             cfg->media_cfg.ice_max_host_cands = my_atoi(pj_optarg);
01060             break;
01061 
01062         case OPT_ICE_NO_RTCP:
01063             cfg->media_cfg.ice_no_rtcp = PJ_TRUE;
01064             break;
01065 
01066         case OPT_TURN_SRV:
01067             cfg->media_cfg.turn_server = pj_str(pj_optarg);
01068             break;
01069 
01070         case OPT_TURN_TCP:
01071             cfg->media_cfg.turn_conn_type = PJ_TURN_TP_TCP;
01072             break;
01073 
01074         case OPT_TURN_USER:
01075             cfg->media_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
01076             cfg->media_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
01077             cfg->media_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
01078             break;
01079 
01080         case OPT_TURN_PASSWD:
01081             cfg->media_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
01082             cfg->media_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
01083             break;
01084 
01085 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
01086         case OPT_USE_SRTP:
01087             app_config.cfg.use_srtp = my_atoi(pj_optarg);
01088             if (!pj_isdigit(*pj_optarg) || app_config.cfg.use_srtp > 2) {
01089                 PJ_LOG(1,(THIS_FILE, "Invalid value for --use-srtp option"));
01090                 return -1;
01091             }
01092             cur_acc->use_srtp = app_config.cfg.use_srtp;
01093             break;
01094         case OPT_SRTP_SECURE:
01095             app_config.cfg.srtp_secure_signaling = my_atoi(pj_optarg);
01096             if (!pj_isdigit(*pj_optarg) || 
01097                 app_config.cfg.srtp_secure_signaling > 2) 
01098             {
01099                 PJ_LOG(1,(THIS_FILE, "Invalid value for --srtp-secure option"));
01100                 return -1;
01101             }
01102             cur_acc->srtp_secure_signaling = app_config.cfg.srtp_secure_signaling;
01103             break;
01104 #endif
01105 
01106         case OPT_RTP_PORT:
01107             cfg->rtp_cfg.port = my_atoi(pj_optarg);
01108             if (cfg->rtp_cfg.port == 0) {
01109                 enum { START_PORT=4000 };
01110                 unsigned range;
01111 
01112                 range = (65535-START_PORT-PJSUA_MAX_CALLS*2);
01113                 cfg->rtp_cfg.port = START_PORT + 
01114                                     ((pj_rand() % range) & 0xFFFE);
01115             }
01116 
01117             if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
01118                 PJ_LOG(1,(THIS_FILE,
01119                           "Error: rtp-port argument value "
01120                           "(expecting 1-65535"));
01121                 return -1;
01122             }
01123             break;
01124 
01125         case OPT_DIS_CODEC:
01126             cfg->codec_dis[cfg->codec_dis_cnt++] = pj_str(pj_optarg);
01127             break;
01128 
01129         case OPT_ADD_CODEC:
01130             cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
01131             break;
01132 
01133         /* These options were no longer valid after new pjsua */
01134         /*
01135         case OPT_COMPLEXITY:
01136             cfg->complexity = my_atoi(pj_optarg);
01137             if (cfg->complexity < 0 || cfg->complexity > 10) {
01138                 PJ_LOG(1,(THIS_FILE,
01139                           "Error: invalid --complexity (expecting 0-10"));
01140                 return -1;
01141             }
01142             break;
01143         */
01144 
01145         case OPT_DURATION:
01146             cfg->duration = my_atoi(pj_optarg);
01147             break;
01148 
01149         case OPT_THREAD_CNT:
01150             cfg->cfg.thread_cnt = my_atoi(pj_optarg);
01151             if (cfg->cfg.thread_cnt > 128) {
01152                 PJ_LOG(1,(THIS_FILE,
01153                           "Error: invalid --thread-cnt option"));
01154                 return -1;
01155             }
01156             break;
01157 
01158         case OPT_PTIME:
01159             cfg->media_cfg.ptime = my_atoi(pj_optarg);
01160             if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
01161                 PJ_LOG(1,(THIS_FILE,
01162                           "Error: invalid --ptime option"));
01163                 return -1;
01164             }
01165             break;
01166 
01167         case OPT_NO_VAD:
01168             cfg->media_cfg.no_vad = PJ_TRUE;
01169             break;
01170 
01171         case OPT_EC_TAIL:
01172             cfg->media_cfg.ec_tail_len = my_atoi(pj_optarg);
01173             if (cfg->media_cfg.ec_tail_len > 1000) {
01174                 PJ_LOG(1,(THIS_FILE, "I think the ec-tail length setting "
01175                           "is too big"));
01176                 return -1;
01177             }
01178             break;
01179 
01180         case OPT_EC_OPT:
01181             cfg->media_cfg.ec_options = my_atoi(pj_optarg);
01182             break;
01183 
01184         case OPT_QUALITY:
01185             cfg->media_cfg.quality = my_atoi(pj_optarg);
01186             if (cfg->media_cfg.quality < 0 || cfg->media_cfg.quality > 10) {
01187                 PJ_LOG(1,(THIS_FILE,
01188                           "Error: invalid --quality (expecting 0-10"));
01189                 return -1;
01190             }
01191             break;
01192 
01193         case OPT_ILBC_MODE:
01194             cfg->media_cfg.ilbc_mode = my_atoi(pj_optarg);
01195             if (cfg->media_cfg.ilbc_mode!=20 && cfg->media_cfg.ilbc_mode!=30) {
01196                 PJ_LOG(1,(THIS_FILE,
01197                           "Error: invalid --ilbc-mode (expecting 20 or 30"));
01198                 return -1;
01199             }
01200             break;
01201 
01202         case OPT_RX_DROP_PCT:
01203             cfg->media_cfg.rx_drop_pct = my_atoi(pj_optarg);
01204             if (cfg->media_cfg.rx_drop_pct > 100) {
01205                 PJ_LOG(1,(THIS_FILE,
01206                           "Error: invalid --rx-drop-pct (expecting <= 100"));
01207                 return -1;
01208             }
01209             break;
01210             
01211         case OPT_TX_DROP_PCT:
01212             cfg->media_cfg.tx_drop_pct = my_atoi(pj_optarg);
01213             if (cfg->media_cfg.tx_drop_pct > 100) {
01214                 PJ_LOG(1,(THIS_FILE,
01215                           "Error: invalid --tx-drop-pct (expecting <= 100"));
01216                 return -1;
01217             }
01218             break;
01219 
01220         case OPT_AUTO_ANSWER:
01221             cfg->auto_answer = my_atoi(pj_optarg);
01222             if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
01223                 PJ_LOG(1,(THIS_FILE,
01224                           "Error: invalid code in --auto-answer "
01225                           "(expecting 100-699"));
01226                 return -1;
01227             }
01228             break;
01229 
01230         case OPT_MAX_CALLS:
01231             cfg->cfg.max_calls = my_atoi(pj_optarg);
01232             if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
01233                 PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
01234                                     "compile time limit (PJSUA_MAX_CALLS=%d)",
01235                           PJSUA_MAX_CALLS));
01236                 return -1;
01237             }
01238             break;
01239 
01240         case OPT_USE_TLS:
01241             cfg->use_tls = PJ_TRUE;
01242 #if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
01243             PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
01244             return -1;
01245 #endif
01246             break;
01247             
01248         case OPT_TLS_CA_FILE:
01249             cfg->udp_cfg.tls_setting.ca_list_file = pj_str(pj_optarg);
01250 #if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
01251             PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
01252             return -1;
01253 #endif
01254             break;
01255             
01256         case OPT_TLS_CERT_FILE:
01257             cfg->udp_cfg.tls_setting.cert_file = pj_str(pj_optarg);
01258 #if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
01259             PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
01260             return -1;
01261 #endif
01262             break;
01263             
01264         case OPT_TLS_PRIV_FILE:
01265             cfg->udp_cfg.tls_setting.privkey_file = pj_str(pj_optarg);
01266             break;
01267 
01268         case OPT_TLS_PASSWORD:
01269             cfg->udp_cfg.tls_setting.password = pj_str(pj_optarg);
01270 #if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
01271             PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
01272             return -1;
01273 #endif
01274             break;
01275 
01276         case OPT_TLS_VERIFY_SERVER:
01277             cfg->udp_cfg.tls_setting.verify_server = PJ_TRUE;
01278             break;
01279 
01280         case OPT_TLS_VERIFY_CLIENT:
01281             cfg->udp_cfg.tls_setting.verify_client = PJ_TRUE;
01282             break;
01283 
01284         case OPT_TLS_NEG_TIMEOUT:
01285             cfg->udp_cfg.tls_setting.timeout.sec = atoi(pj_optarg);
01286             break;
01287 
01288         case OPT_TLS_SRV_NAME:
01289             cfg->udp_cfg.tls_setting.server_name = pj_str(pj_optarg);
01290             break;
01291 
01292         case OPT_CAPTURE_DEV:
01293             cfg->capture_dev = atoi(pj_optarg);
01294             break;
01295 
01296         case OPT_PLAYBACK_DEV:
01297             cfg->playback_dev = atoi(pj_optarg);
01298             break;
01299 
01300         case OPT_STDOUT_REFRESH:
01301             stdout_refresh = atoi(pj_optarg);
01302             break;
01303 
01304         case OPT_STDOUT_REFRESH_TEXT:
01305             stdout_refresh_text = pj_optarg;
01306             break;
01307 
01308 #ifdef _IONBF
01309         case OPT_STDOUT_NO_BUF:
01310             setvbuf(stdout, NULL, _IONBF, 0);
01311             break;
01312 #endif
01313 
01314         case OPT_CAPTURE_LAT:
01315             cfg->capture_lat = atoi(pj_optarg);
01316             break;
01317 
01318         case OPT_PLAYBACK_LAT:
01319             cfg->playback_lat = atoi(pj_optarg);
01320             break;
01321 
01322         case OPT_SND_AUTO_CLOSE:
01323             cfg->media_cfg.snd_auto_close_time = atoi(pj_optarg);
01324             break;
01325 
01326         case OPT_NO_TONES:
01327             cfg->no_tones = PJ_TRUE;
01328             break;
01329 
01330         case OPT_JB_MAX_SIZE:
01331             cfg->media_cfg.jb_max = atoi(pj_optarg);
01332             break;
01333 
01334 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
01335         case OPT_IPV6:
01336             cfg->ipv6 = PJ_TRUE;
01337             break;
01338 #endif
01339         case OPT_QOS:
01340             cfg->enable_qos = PJ_TRUE;
01341             /* Set RTP traffic type to Voice */
01342             cfg->rtp_cfg.qos_type = PJ_QOS_TYPE_VOICE;
01343             /* Directly apply DSCP value to SIP traffic. Say lets
01344              * set it to CS3 (DSCP 011000). Note that this will not 
01345              * work on all platforms.
01346              */
01347             cfg->udp_cfg.qos_params.flags = PJ_QOS_PARAM_HAS_DSCP;
01348             cfg->udp_cfg.qos_params.dscp_val = 0x18;
01349             break;
01350         default:
01351             PJ_LOG(1,(THIS_FILE, 
01352                       "Argument \"%s\" is not valid. Use --help to see help",
01353                       argv[pj_optind-1]));
01354             return -1;
01355         }
01356     }
01357 
01358     if (pj_optind != argc) {
01359         pj_str_t uri_arg;
01360 
01361         if (pjsua_verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
01362             PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
01363             return -1;
01364         }
01365         uri_arg = pj_str(argv[pj_optind]);
01366         if (uri_to_call)
01367             *uri_to_call = uri_arg;
01368         pj_optind++;
01369 
01370         /* Add URI to call to buddy list if it's not already there */
01371         for (i=0; i<cfg->buddy_cnt; ++i) {
01372             if (pj_stricmp(&cfg->buddy_cfg[i].uri, &uri_arg)==0)
01373                 break;
01374         }
01375         if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
01376             cfg->buddy_cfg[cfg->buddy_cnt++].uri = uri_arg;
01377         }
01378 
01379     } else {
01380         if (uri_to_call)
01381             uri_to_call->slen = 0;
01382     }
01383 
01384     if (pj_optind != argc) {
01385         PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
01386         return PJ_EINVAL;
01387     }
01388 
01389     if (cfg->acc_cfg[cfg->acc_cnt].id.slen)
01390         cfg->acc_cnt++;
01391 
01392     for (i=0; i<cfg->acc_cnt; ++i) {
01393         pjsua_acc_config *acfg = &cfg->acc_cfg[i];
01394 
01395         if (acfg->cred_info[acfg->cred_count].username.slen)
01396         {
01397             acfg->cred_count++;
01398         }
01399 
01400         /* When IMS mode is enabled for the account, verify that settings
01401          * are okay.
01402          */
01403         /* For now we check if IMS mode is activated by looking if
01404          * initial_auth is set.
01405          */
01406         if (acfg->auth_pref.initial_auth && acfg->cred_count) {
01407             /* Realm must point to the real domain */
01408             if (*acfg->cred_info[0].realm.ptr=='*') {
01409                 PJ_LOG(1,(THIS_FILE, 
01410                           "Error: cannot use '*' as realm with IMS"));
01411                 return PJ_EINVAL;
01412             }
01413 
01414             /* Username for authentication must be in a@b format */
01415             if (strchr(acfg->cred_info[0].username.ptr, '@')==0) {
01416                 PJ_LOG(1,(THIS_FILE, 
01417                           "Error: Username for authentication must "
01418                           "be in user@domain format with IMS"));
01419                 return PJ_EINVAL;
01420             }
01421         }
01422     }
01423 
01424 
01425     return PJ_SUCCESS;
01426 }
01427 
01428 
01429 /*
01430  * Save account settings
01431  */
01432 static void write_account_settings(int acc_index, pj_str_t *result)
01433 {
01434     unsigned i;
01435     char line[128];
01436     pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index];
01437 
01438     
01439     pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index);
01440     pj_strcat2(result, line);
01441 
01442 
01443     /* Identity */
01444     if (acc_cfg->id.slen) {
01445         pj_ansi_sprintf(line, "--id %.*s\n", 
01446                         (int)acc_cfg->id.slen, 
01447                         acc_cfg->id.ptr);
01448         pj_strcat2(result, line);
01449     }
01450 
01451     /* Registrar server */
01452     if (acc_cfg->reg_uri.slen) {
01453         pj_ansi_sprintf(line, "--registrar %.*s\n",
01454                               (int)acc_cfg->reg_uri.slen,
01455                               acc_cfg->reg_uri.ptr);
01456         pj_strcat2(result, line);
01457 
01458         pj_ansi_sprintf(line, "--reg-timeout %u\n",
01459                               acc_cfg->reg_timeout);
01460         pj_strcat2(result, line);
01461     }
01462 
01463     /* Contact */
01464     if (acc_cfg->force_contact.slen) {
01465         pj_ansi_sprintf(line, "--contact %.*s\n", 
01466                         (int)acc_cfg->force_contact.slen, 
01467                         acc_cfg->force_contact.ptr);
01468         pj_strcat2(result, line);
01469     }
01470 
01471     /* Contact header parameters */
01472     if (acc_cfg->contact_params.slen) {
01473         pj_ansi_sprintf(line, "--contact-params %.*s\n", 
01474                         (int)acc_cfg->contact_params.slen, 
01475                         acc_cfg->contact_params.ptr);
01476         pj_strcat2(result, line);
01477     }
01478 
01479     /* Contact URI parameters */
01480     if (acc_cfg->contact_uri_params.slen) {
01481         pj_ansi_sprintf(line, "--contact-uri-params %.*s\n", 
01482                         (int)acc_cfg->contact_uri_params.slen, 
01483                         acc_cfg->contact_uri_params.ptr);
01484         pj_strcat2(result, line);
01485     }
01486 
01487     /*  */
01488     if (acc_cfg->allow_contact_rewrite!=1)
01489     {
01490         pj_ansi_sprintf(line, "--auto-update-nat %i\n",
01491                         (int)acc_cfg->allow_contact_rewrite);
01492         pj_strcat2(result, line);
01493     }
01494 
01495 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
01496     /* SRTP */
01497     if (acc_cfg->use_srtp) {
01498         pj_ansi_sprintf(line, "--use-srtp %i\n",
01499                         (int)acc_cfg->use_srtp);
01500         pj_strcat2(result, line);
01501     }
01502 #endif
01503 
01504     /* Proxy */
01505     for (i=0; i<acc_cfg->proxy_cnt; ++i) {
01506         pj_ansi_sprintf(line, "--proxy %.*s\n",
01507                               (int)acc_cfg->proxy[i].slen,
01508                               acc_cfg->proxy[i].ptr);
01509         pj_strcat2(result, line);
01510     }
01511 
01512     /* Credentials */
01513     for (i=0; i<acc_cfg->cred_count; ++i) {
01514         if (acc_cfg->cred_info[i].realm.slen) {
01515             pj_ansi_sprintf(line, "--realm %.*s\n",
01516                                   (int)acc_cfg->cred_info[i].realm.slen,
01517                                   acc_cfg->cred_info[i].realm.ptr);
01518             pj_strcat2(result, line);
01519         }
01520 
01521         if (acc_cfg->cred_info[i].username.slen) {
01522             pj_ansi_sprintf(line, "--username %.*s\n",
01523                                   (int)acc_cfg->cred_info[i].username.slen,
01524                                   acc_cfg->cred_info[i].username.ptr);
01525             pj_strcat2(result, line);
01526         }
01527 
01528         if (acc_cfg->cred_info[i].data.slen) {
01529             pj_ansi_sprintf(line, "--password %.*s\n",
01530                                   (int)acc_cfg->cred_info[i].data.slen,
01531                                   acc_cfg->cred_info[i].data.ptr);
01532             pj_strcat2(result, line);
01533         }
01534 
01535         if (i != acc_cfg->cred_count - 1)
01536             pj_strcat2(result, "--next-cred\n");
01537     }
01538 
01539     /* 100rel extension */
01540     if (acc_cfg->require_100rel) {
01541         pj_strcat2(result, "--use-100rel\n");
01542     }
01543 
01544     /* Session Timer extension */
01545     if (acc_cfg->require_timer) {
01546         pj_strcat2(result, "--use-timer\n");
01547     }
01548     if (acc_cfg->timer_setting.min_se != 90) {
01549         pj_ansi_sprintf(line, "--timer-min-se %d\n",
01550                               acc_cfg->timer_setting.min_se);
01551         pj_strcat2(result, line);
01552     }
01553     if (acc_cfg->timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) {
01554         pj_ansi_sprintf(line, "--timer-se %d\n",
01555                               acc_cfg->timer_setting.sess_expires);
01556         pj_strcat2(result, line);
01557     }
01558 
01559     /* Publish */
01560     if (acc_cfg->publish_enabled)
01561         pj_strcat2(result, "--publish\n");
01562 
01563     /* MWI */
01564     if (acc_cfg->mwi_enabled)
01565         pj_strcat2(result, "--mwi\n");
01566 }
01567 
01568 
01569 /*
01570  * Write settings.
01571  */
01572 static int write_settings(const struct app_config *config,
01573                           char *buf, pj_size_t max)
01574 {
01575     unsigned acc_index;
01576     unsigned i;
01577     pj_str_t cfg;
01578     char line[128];
01579     extern pj_bool_t pjsip_use_compact_form;
01580 
01581     PJ_UNUSED_ARG(max);
01582 
01583     cfg.ptr = buf;
01584     cfg.slen = 0;
01585 
01586     /* Logging. */
01587     pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
01588     pj_ansi_sprintf(line, "--log-level %d\n",
01589                     config->log_cfg.level);
01590     pj_strcat2(&cfg, line);
01591 
01592     pj_ansi_sprintf(line, "--app-log-level %d\n",
01593                     config->log_cfg.console_level);
01594     pj_strcat2(&cfg, line);
01595 
01596     if (config->log_cfg.log_filename.slen) {
01597         pj_ansi_sprintf(line, "--log-file %.*s\n",
01598                         (int)config->log_cfg.log_filename.slen,
01599                         config->log_cfg.log_filename.ptr);
01600         pj_strcat2(&cfg, line);
01601     }
01602 
01603 
01604     /* Save account settings. */
01605     for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
01606         
01607         write_account_settings(acc_index, &cfg);
01608 
01609         if (acc_index < config->acc_cnt-1)
01610             pj_strcat2(&cfg, "--next-account\n");
01611     }
01612 
01613 
01614     pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
01615 
01616     /* Nameservers */
01617     for (i=0; i<config->cfg.nameserver_count; ++i) {
01618         pj_ansi_sprintf(line, "--nameserver %.*s\n",
01619                               (int)config->cfg.nameserver[i].slen,
01620                               config->cfg.nameserver[i].ptr);
01621         pj_strcat2(&cfg, line);
01622     }
01623 
01624     /* Outbound proxy */
01625     for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
01626         pj_ansi_sprintf(line, "--outbound %.*s\n",
01627                               (int)config->cfg.outbound_proxy[i].slen,
01628                               config->cfg.outbound_proxy[i].ptr);
01629         pj_strcat2(&cfg, line);
01630     }
01631 
01632     /* Transport options */
01633     if (config->ipv6) {
01634         pj_strcat2(&cfg, "--ipv6\n");
01635     }
01636     if (config->enable_qos) {
01637         pj_strcat2(&cfg, "--set-qos\n");
01638     }
01639 
01640     /* UDP Transport. */
01641     pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
01642     pj_strcat2(&cfg, line);
01643 
01644     /* IP address, if any. */
01645     if (config->udp_cfg.public_addr.slen) {
01646         pj_ansi_sprintf(line, "--ip-addr %.*s\n", 
01647                         (int)config->udp_cfg.public_addr.slen,
01648                         config->udp_cfg.public_addr.ptr);
01649         pj_strcat2(&cfg, line);
01650     }
01651 
01652     /* Bound IP address, if any. */
01653     if (config->udp_cfg.bound_addr.slen) {
01654         pj_ansi_sprintf(line, "--bound-addr %.*s\n", 
01655                         (int)config->udp_cfg.bound_addr.slen,
01656                         config->udp_cfg.bound_addr.ptr);
01657         pj_strcat2(&cfg, line);
01658     }
01659 
01660     /* No TCP ? */
01661     if (config->no_tcp) {
01662         pj_strcat2(&cfg, "--no-tcp\n");
01663     }
01664 
01665     /* No UDP ? */
01666     if (config->no_udp) {
01667         pj_strcat2(&cfg, "--no-udp\n");
01668     }
01669 
01670     /* STUN */
01671     for (i=0; i<config->cfg.stun_srv_cnt; ++i) {
01672         pj_ansi_sprintf(line, "--stun-srv %.*s\n",
01673                         (int)config->cfg.stun_srv[i].slen, 
01674                         config->cfg.stun_srv[i].ptr);
01675         pj_strcat2(&cfg, line);
01676     }
01677 
01678     /* TLS */
01679     if (config->use_tls)
01680         pj_strcat2(&cfg, "--use-tls\n");
01681     if (config->udp_cfg.tls_setting.ca_list_file.slen) {
01682         pj_ansi_sprintf(line, "--tls-ca-file %.*s\n",
01683                         (int)config->udp_cfg.tls_setting.ca_list_file.slen, 
01684                         config->udp_cfg.tls_setting.ca_list_file.ptr);
01685         pj_strcat2(&cfg, line);
01686     }
01687     if (config->udp_cfg.tls_setting.cert_file.slen) {
01688         pj_ansi_sprintf(line, "--tls-cert-file %.*s\n",
01689                         (int)config->udp_cfg.tls_setting.cert_file.slen, 
01690                         config->udp_cfg.tls_setting.cert_file.ptr);
01691         pj_strcat2(&cfg, line);
01692     }
01693     if (config->udp_cfg.tls_setting.privkey_file.slen) {
01694         pj_ansi_sprintf(line, "--tls-privkey-file %.*s\n",
01695                         (int)config->udp_cfg.tls_setting.privkey_file.slen, 
01696                         config->udp_cfg.tls_setting.privkey_file.ptr);
01697         pj_strcat2(&cfg, line);
01698     }
01699 
01700     if (config->udp_cfg.tls_setting.password.slen) {
01701         pj_ansi_sprintf(line, "--tls-password %.*s\n",
01702                         (int)config->udp_cfg.tls_setting.password.slen, 
01703                         config->udp_cfg.tls_setting.password.ptr);
01704         pj_strcat2(&cfg, line);
01705     }
01706 
01707     if (config->udp_cfg.tls_setting.server_name.slen) {
01708         pj_ansi_sprintf(line, "--tls-srv-name %.*s\n",
01709                         (int)config->udp_cfg.tls_setting.server_name.slen, 
01710                         config->udp_cfg.tls_setting.server_name.ptr);
01711         pj_strcat2(&cfg, line);
01712     }
01713 
01714     if (config->udp_cfg.tls_setting.verify_server)
01715         pj_strcat2(&cfg, "--tls-verify-server\n");
01716 
01717     if (config->udp_cfg.tls_setting.verify_client)
01718         pj_strcat2(&cfg, "--tls-verify-client\n");
01719 
01720     if (config->udp_cfg.tls_setting.timeout.sec) {
01721         pj_ansi_sprintf(line, "--tls-neg-timeout %d\n",
01722                         (int)config->udp_cfg.tls_setting.timeout.sec);
01723         pj_strcat2(&cfg, line);
01724     }
01725 
01726     pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n");
01727 
01728     /* SRTP */
01729 #if PJMEDIA_HAS_SRTP
01730     if (app_config.cfg.use_srtp != PJSUA_DEFAULT_USE_SRTP) {
01731         pj_ansi_sprintf(line, "--use-srtp %d\n",
01732                         app_config.cfg.use_srtp);
01733         pj_strcat2(&cfg, line);
01734     }
01735     if (app_config.cfg.srtp_secure_signaling != 
01736         PJSUA_DEFAULT_SRTP_SECURE_SIGNALING) 
01737     {
01738         pj_ansi_sprintf(line, "--srtp-secure %d\n",
01739                         app_config.cfg.srtp_secure_signaling);
01740         pj_strcat2(&cfg, line);
01741     }
01742 #endif
01743 
01744     /* Media Transport*/
01745     if (config->media_cfg.enable_ice)
01746         pj_strcat2(&cfg, "--use-ice\n");
01747 
01748     if (config->media_cfg.ice_opt.aggressive == PJ_FALSE)
01749         pj_strcat2(&cfg, "--ice-regular\n");
01750 
01751     if (config->media_cfg.enable_turn)
01752         pj_strcat2(&cfg, "--use-turn\n");
01753 
01754     if (config->media_cfg.ice_max_host_cands >= 0) {
01755         pj_ansi_sprintf(line, "--ice_max_host_cands %d\n",
01756                         config->media_cfg.ice_max_host_cands);
01757         pj_strcat2(&cfg, line);
01758     }
01759 
01760     if (config->media_cfg.ice_no_rtcp)
01761         pj_strcat2(&cfg, "--ice-no-rtcp\n");
01762 
01763     if (config->media_cfg.turn_server.slen) {
01764         pj_ansi_sprintf(line, "--turn-srv %.*s\n",
01765                         (int)config->media_cfg.turn_server.slen,
01766                         config->media_cfg.turn_server.ptr);
01767         pj_strcat2(&cfg, line);
01768     }
01769 
01770     if (config->media_cfg.turn_conn_type == PJ_TURN_TP_TCP)
01771         pj_strcat2(&cfg, "--turn-tcp\n");
01772 
01773     if (config->media_cfg.turn_auth_cred.data.static_cred.username.slen) {
01774         pj_ansi_sprintf(line, "--turn-user %.*s\n",
01775                         (int)config->media_cfg.turn_auth_cred.data.static_cred.username.slen,
01776                         config->media_cfg.turn_auth_cred.data.static_cred.username.ptr);
01777         pj_strcat2(&cfg, line);
01778     }
01779 
01780     if (config->media_cfg.turn_auth_cred.data.static_cred.data.slen) {
01781         pj_ansi_sprintf(line, "--turn-passwd %.*s\n",
01782                         (int)config->media_cfg.turn_auth_cred.data.static_cred.data.slen,
01783                         config->media_cfg.turn_auth_cred.data.static_cred.data.ptr);
01784         pj_strcat2(&cfg, line);
01785     }
01786 
01787     /* Media */
01788     if (config->null_audio)
01789         pj_strcat2(&cfg, "--null-audio\n");
01790     if (config->auto_play)
01791         pj_strcat2(&cfg, "--auto-play\n");
01792     if (config->auto_loop)
01793         pj_strcat2(&cfg, "--auto-loop\n");
01794     if (config->auto_conf)
01795         pj_strcat2(&cfg, "--auto-conf\n");
01796     for (i=0; i<config->wav_count; ++i) {
01797         pj_ansi_sprintf(line, "--play-file %s\n",
01798                         config->wav_files[i].ptr);
01799         pj_strcat2(&cfg, line);
01800     }
01801     for (i=0; i<config->tone_count; ++i) {
01802         pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n",
01803                         config->tones[i].freq1, config->tones[i].freq2, 
01804                         config->tones[i].on_msec, config->tones[i].off_msec);
01805         pj_strcat2(&cfg, line);
01806     }
01807     if (config->rec_file.slen) {
01808         pj_ansi_sprintf(line, "--rec-file %s\n",
01809                         config->rec_file.ptr);
01810         pj_strcat2(&cfg, line);
01811     }
01812     if (config->auto_rec)
01813         pj_strcat2(&cfg, "--auto-rec\n");
01814     if (config->capture_dev != PJSUA_INVALID_ID) {
01815         pj_ansi_sprintf(line, "--capture-dev %d\n", config->capture_dev);
01816         pj_strcat2(&cfg, line);
01817     }
01818     if (config->playback_dev != PJSUA_INVALID_ID) {
01819         pj_ansi_sprintf(line, "--playback-dev %d\n", config->playback_dev);
01820         pj_strcat2(&cfg, line);
01821     }
01822     if (config->media_cfg.snd_auto_close_time != -1) {
01823         pj_ansi_sprintf(line, "--snd-auto-close %d\n", 
01824                         config->media_cfg.snd_auto_close_time);
01825         pj_strcat2(&cfg, line);
01826     }
01827     if (config->no_tones) {
01828         pj_strcat2(&cfg, "--no-tones\n");
01829     }
01830     if (config->media_cfg.jb_max != -1) {
01831         pj_ansi_sprintf(line, "--jb-max-size %d\n", 
01832                         config->media_cfg.jb_max);
01833         pj_strcat2(&cfg, line);
01834     }
01835 
01836     /* Sound device latency */
01837     if (config->capture_lat != PJMEDIA_SND_DEFAULT_REC_LATENCY) {
01838         pj_ansi_sprintf(line, "--capture-lat %d\n", config->capture_lat);
01839         pj_strcat2(&cfg, line);
01840     }
01841     if (config->playback_lat != PJMEDIA_SND_DEFAULT_PLAY_LATENCY) {
01842         pj_ansi_sprintf(line, "--playback-lat %d\n", config->playback_lat);
01843         pj_strcat2(&cfg, line);
01844     }
01845 
01846     /* Media clock rate. */
01847     if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) {
01848         pj_ansi_sprintf(line, "--clock-rate %d\n",
01849                         config->media_cfg.clock_rate);
01850         pj_strcat2(&cfg, line);
01851     } else {
01852         pj_ansi_sprintf(line, "#using default --clock-rate %d\n",
01853                         config->media_cfg.clock_rate);
01854         pj_strcat2(&cfg, line);
01855     }
01856 
01857     if (config->media_cfg.snd_clock_rate && 
01858         config->media_cfg.snd_clock_rate != config->media_cfg.clock_rate) 
01859     {
01860         pj_ansi_sprintf(line, "--snd-clock-rate %d\n",
01861                         config->media_cfg.snd_clock_rate);
01862         pj_strcat2(&cfg, line);
01863     }
01864 
01865     /* Stereo mode. */
01866     if (config->media_cfg.channel_count == 2) {
01867         pj_ansi_sprintf(line, "--stereo\n");
01868         pj_strcat2(&cfg, line);
01869     }
01870 
01871     /* quality */
01872     if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) {
01873         pj_ansi_sprintf(line, "--quality %d\n",
01874                         config->media_cfg.quality);
01875         pj_strcat2(&cfg, line);
01876     } else {
01877         pj_ansi_sprintf(line, "#using default --quality %d\n",
01878                         config->media_cfg.quality);
01879         pj_strcat2(&cfg, line);
01880     }
01881 
01882 
01883     /* ptime */
01884     if (config->media_cfg.ptime) {
01885         pj_ansi_sprintf(line, "--ptime %d\n",
01886                         config->media_cfg.ptime);
01887         pj_strcat2(&cfg, line);
01888     }
01889 
01890     /* no-vad */
01891     if (config->media_cfg.no_vad) {
01892         pj_strcat2(&cfg, "--no-vad\n");
01893     }
01894 
01895     /* ec-tail */
01896     if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) {
01897         pj_ansi_sprintf(line, "--ec-tail %d\n",
01898                         config->media_cfg.ec_tail_len);
01899         pj_strcat2(&cfg, line);
01900     } else {
01901         pj_ansi_sprintf(line, "#using default --ec-tail %d\n",
01902                         config->media_cfg.ec_tail_len);
01903         pj_strcat2(&cfg, line);
01904     }
01905 
01906     /* ec-opt */
01907     if (config->media_cfg.ec_options != 0) {
01908         pj_ansi_sprintf(line, "--ec-opt %d\n",
01909                         config->media_cfg.ec_options);
01910         pj_strcat2(&cfg, line);
01911     } 
01912 
01913     /* ilbc-mode */
01914     if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) {
01915         pj_ansi_sprintf(line, "--ilbc-mode %d\n",
01916                         config->media_cfg.ilbc_mode);
01917         pj_strcat2(&cfg, line);
01918     } else {
01919         pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n",
01920                         config->media_cfg.ilbc_mode);
01921         pj_strcat2(&cfg, line);
01922     }
01923 
01924     /* RTP drop */
01925     if (config->media_cfg.tx_drop_pct) {
01926         pj_ansi_sprintf(line, "--tx-drop-pct %d\n",
01927                         config->media_cfg.tx_drop_pct);
01928         pj_strcat2(&cfg, line);
01929 
01930     }
01931     if (config->media_cfg.rx_drop_pct) {
01932         pj_ansi_sprintf(line, "--rx-drop-pct %d\n",
01933                         config->media_cfg.rx_drop_pct);
01934         pj_strcat2(&cfg, line);
01935 
01936     }
01937 
01938 
01939     /* Start RTP port. */
01940     pj_ansi_sprintf(line, "--rtp-port %d\n",
01941                     config->rtp_cfg.port);
01942     pj_strcat2(&cfg, line);
01943 
01944     /* Add codec. */
01945     for (i=0; i<config->codec_cnt; ++i) {
01946         pj_ansi_sprintf(line, "--add-codec %s\n",
01947                     config->codec_arg[i].ptr);
01948         pj_strcat2(&cfg, line);
01949     }
01950     /* Disable codec */
01951     for (i=0; i<config->codec_dis_cnt; ++i) {
01952         pj_ansi_sprintf(line, "--dis-codec %s\n",
01953                     config->codec_dis[i].ptr);
01954         pj_strcat2(&cfg, line);
01955     }
01956 
01957     pj_strcat2(&cfg, "\n#\n# User agent:\n#\n");
01958 
01959     /* Auto-answer. */
01960     if (config->auto_answer != 0) {
01961         pj_ansi_sprintf(line, "--auto-answer %d\n",
01962                         config->auto_answer);
01963         pj_strcat2(&cfg, line);
01964     }
01965 
01966     /* accept-redirect */
01967     if (config->redir_op != PJSIP_REDIRECT_ACCEPT) {
01968         pj_ansi_sprintf(line, "--accept-redirect %d\n",
01969                         config->redir_op);
01970         pj_strcat2(&cfg, line);
01971     }
01972 
01973     /* Max calls. */
01974     pj_ansi_sprintf(line, "--max-calls %d\n",
01975                     config->cfg.max_calls);
01976     pj_strcat2(&cfg, line);
01977 
01978     /* Uas-duration. */
01979     if (config->duration != NO_LIMIT) {
01980         pj_ansi_sprintf(line, "--duration %d\n",
01981                         config->duration);
01982         pj_strcat2(&cfg, line);
01983     }
01984 
01985     /* norefersub ? */
01986     if (config->no_refersub) {
01987         pj_strcat2(&cfg, "--norefersub\n");
01988     }
01989 
01990     if (pjsip_use_compact_form)
01991     {
01992         pj_strcat2(&cfg, "--use-compact-form\n");
01993     }
01994 
01995     if (!config->cfg.force_lr) {
01996         pj_strcat2(&cfg, "--no-force-lr\n");
01997     }
01998 
01999     pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
02000 
02001     /* Add buddies. */
02002     for (i=0; i<config->buddy_cnt; ++i) {
02003         pj_ansi_sprintf(line, "--add-buddy %.*s\n",
02004                               (int)config->buddy_cfg[i].uri.slen,
02005                               config->buddy_cfg[i].uri.ptr);
02006         pj_strcat2(&cfg, line);
02007     }
02008 
02009     /* SIP extensions. */
02010     pj_strcat2(&cfg, "\n#\n# SIP extensions:\n#\n");
02011     /* 100rel extension */
02012     if (config->cfg.require_100rel) {
02013         pj_strcat2(&cfg, "--use-100rel\n");
02014     }
02015     /* Session Timer extension */
02016     if (config->cfg.require_timer) {
02017         pj_strcat2(&cfg, "--use-timer\n");
02018     }
02019     if (config->cfg.timer_setting.min_se != 90) {
02020         pj_ansi_sprintf(line, "--timer-min-se %d\n",
02021                               config->cfg.timer_setting.min_se);
02022         pj_strcat2(&cfg, line);
02023     }
02024     if (config->cfg.timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) {
02025         pj_ansi_sprintf(line, "--timer-se %d\n",
02026                               config->cfg.timer_setting.sess_expires);
02027         pj_strcat2(&cfg, line);
02028     }
02029 
02030     *(cfg.ptr + cfg.slen) = '\0';
02031     return cfg.slen;
02032 }
02033 
02034 
02035 /*
02036  * Dump application states.
02037  */
02038 static void app_dump(pj_bool_t detail)
02039 {
02040     pjsua_dump(detail);
02041 }
02042 
02043 /*
02044  * Print log of call states. Since call states may be too long for logger,
02045  * printing it is a bit tricky, it should be printed part by part as long 
02046  * as the logger can accept.
02047  */
02048 static void log_call_dump(int call_id) 
02049 {
02050     unsigned call_dump_len;
02051     unsigned part_len;
02052     unsigned part_idx;
02053     unsigned log_decor;
02054 
02055     pjsua_call_dump(call_id, PJ_TRUE, some_buf, 
02056                     sizeof(some_buf), "  ");
02057     call_dump_len = strlen(some_buf);
02058 
02059     log_decor = pj_log_get_decor();
02060     pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
02061     PJ_LOG(3,(THIS_FILE, "\n"));
02062     pj_log_set_decor(0);
02063 
02064     part_idx = 0;
02065     part_len = PJ_LOG_MAX_SIZE-80;
02066     while (part_idx < call_dump_len) {
02067         char p_orig, *p;
02068 
02069         p = &some_buf[part_idx];
02070         if (part_idx + part_len > call_dump_len)
02071             part_len = call_dump_len - part_idx;
02072         p_orig = p[part_len];
02073         p[part_len] = '\0';
02074         PJ_LOG(3,(THIS_FILE, "%s", p));
02075         p[part_len] = p_orig;
02076         part_idx += part_len;
02077     }
02078     pj_log_set_decor(log_decor);
02079 }
02080 
02081 /*****************************************************************************
02082  * Console application
02083  */
02084 
02085 static void ringback_start(pjsua_call_id call_id)
02086 {
02087     if (app_config.no_tones)
02088         return;
02089 
02090     if (app_config.call_data[call_id].ringback_on)
02091         return;
02092 
02093     app_config.call_data[call_id].ringback_on = PJ_TRUE;
02094 
02095     if (++app_config.ringback_cnt==1 && 
02096         app_config.ringback_slot!=PJSUA_INVALID_ID) 
02097     {
02098         pjsua_conf_connect(app_config.ringback_slot, 0);
02099     }
02100 }
02101 
02102 static void ring_stop(pjsua_call_id call_id)
02103 {
02104     if (app_config.no_tones)
02105         return;
02106 
02107     if (app_config.call_data[call_id].ringback_on) {
02108         app_config.call_data[call_id].ringback_on = PJ_FALSE;
02109 
02110         pj_assert(app_config.ringback_cnt>0);
02111         if (--app_config.ringback_cnt == 0 && 
02112             app_config.ringback_slot!=PJSUA_INVALID_ID) 
02113         {
02114             pjsua_conf_disconnect(app_config.ringback_slot, 0);
02115             pjmedia_tonegen_rewind(app_config.ringback_port);
02116         }
02117     }
02118 
02119     if (app_config.call_data[call_id].ring_on) {
02120         app_config.call_data[call_id].ring_on = PJ_FALSE;
02121 
02122         pj_assert(app_config.ring_cnt>0);
02123         if (--app_config.ring_cnt == 0 && 
02124             app_config.ring_slot!=PJSUA_INVALID_ID) 
02125         {
02126             pjsua_conf_disconnect(app_config.ring_slot, 0);
02127             pjmedia_tonegen_rewind(app_config.ring_port);
02128         }
02129     }
02130 }
02131 
02132 static void ring_start(pjsua_call_id call_id)
02133 {
02134     if (app_config.no_tones)
02135         return;
02136 
02137     if (app_config.call_data[call_id].ring_on)
02138         return;
02139 
02140     app_config.call_data[call_id].ring_on = PJ_TRUE;
02141 
02142     if (++app_config.ring_cnt==1 && 
02143         app_config.ring_slot!=PJSUA_INVALID_ID) 
02144     {
02145         pjsua_conf_connect(app_config.ring_slot, 0);
02146     }
02147 }
02148 
02149 /*
02150  * Find next call when current call is disconnected or when user
02151  * press ']'
02152  */
02153 static pj_bool_t find_next_call(void)
02154 {
02155     int i, max;
02156 
02157     max = pjsua_call_get_max_count();
02158     for (i=current_call+1; i<max; ++i) {
02159         if (pjsua_call_is_active(i)) {
02160             current_call = i;
02161             return PJ_TRUE;
02162         }
02163     }
02164 
02165     for (i=0; i<current_call; ++i) {
02166         if (pjsua_call_is_active(i)) {
02167             current_call = i;
02168             return PJ_TRUE;
02169         }
02170     }
02171 
02172     current_call = PJSUA_INVALID_ID;
02173     return PJ_FALSE;
02174 }
02175 
02176 
02177 /*
02178  * Find previous call when user press '['
02179  */
02180 static pj_bool_t find_prev_call(void)
02181 {
02182     int i, max;
02183 
02184     max = pjsua_call_get_max_count();
02185     for (i=current_call-1; i>=0; --i) {
02186         if (pjsua_call_is_active(i)) {
02187             current_call = i;
02188             return PJ_TRUE;
02189         }
02190     }
02191 
02192     for (i=max-1; i>current_call; --i) {
02193         if (pjsua_call_is_active(i)) {
02194             current_call = i;
02195             return PJ_TRUE;
02196         }
02197     }
02198 
02199     current_call = PJSUA_INVALID_ID;
02200     return PJ_FALSE;
02201 }
02202 
02203 
02204 /* Callback from timer when the maximum call duration has been
02205  * exceeded.
02206  */
02207 static void call_timeout_callback(pj_timer_heap_t *timer_heap,
02208                                   struct pj_timer_entry *entry)
02209 {
02210     pjsua_call_id call_id = entry->id;
02211     pjsua_msg_data msg_data;
02212     pjsip_generic_string_hdr warn;
02213     pj_str_t hname = pj_str("Warning");
02214     pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
02215 
02216     PJ_UNUSED_ARG(timer_heap);
02217 
02218     if (call_id == PJSUA_INVALID_ID) {
02219         PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
02220         return;
02221     }
02222     
02223     /* Add warning header */
02224     pjsua_msg_data_init(&msg_data);
02225     pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
02226     pj_list_push_back(&msg_data.hdr_list, &warn);
02227 
02228     /* Call duration has been exceeded; disconnect the call */
02229     PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
02230                          "for call %d, disconnecting the call",
02231                          app_config.duration, call_id));
02232     entry->id = PJSUA_INVALID_ID;
02233     pjsua_call_hangup(call_id, 200, NULL, &msg_data);
02234 }
02235 
02236 
02237 /*
02238  * Handler when invite state has changed.
02239  */
02240 static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
02241 {
02242     pjsua_call_info call_info;
02243 
02244     PJ_UNUSED_ARG(e);
02245 
02246     pjsua_call_get_info(call_id, &call_info);
02247 
02248     if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
02249 
02250         /* Stop all ringback for this call */
02251         ring_stop(call_id);
02252 
02253         /* Cancel duration timer, if any */
02254         if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
02255             struct call_data *cd = &app_config.call_data[call_id];
02256             pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
02257 
02258             cd->timer.id = PJSUA_INVALID_ID;
02259             pjsip_endpt_cancel_timer(endpt, &cd->timer);
02260         }
02261 
02262         /* Rewind play file when hangup automatically, 
02263          * since file is not looped
02264          */
02265         if (app_config.auto_play_hangup)
02266             pjsua_player_set_pos(app_config.wav_id, 0);
02267 
02268 
02269         PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]", 
02270                   call_id,
02271                   call_info.last_status,
02272                   call_info.last_status_text.ptr));
02273 
02274         if (call_id == current_call) {
02275             find_next_call();
02276         }
02277 
02278         /* Dump media state upon disconnected */
02279         if (1) {
02280             PJ_LOG(5,(THIS_FILE, 
02281                       "Call %d disconnected, dumping media stats..", 
02282                       call_id));
02283             log_call_dump(call_id);
02284         }
02285 
02286     } else {
02287 
02288         if (app_config.duration!=NO_LIMIT && 
02289             call_info.state == PJSIP_INV_STATE_CONFIRMED) 
02290         {
02291             /* Schedule timer to hangup call after the specified duration */
02292             struct call_data *cd = &app_config.call_data[call_id];
02293             pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
02294             pj_time_val delay;
02295 
02296             cd->timer.id = call_id;
02297             delay.sec = app_config.duration;
02298             delay.msec = 0;
02299             pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
02300         }
02301 
02302         if (call_info.state == PJSIP_INV_STATE_EARLY) {
02303             int code;
02304             pj_str_t reason;
02305             pjsip_msg *msg;
02306 
02307             /* This can only occur because of TX or RX message */
02308             pj_assert(e->type == PJSIP_EVENT_TSX_STATE);
02309 
02310             if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
02311                 msg = e->body.tsx_state.src.rdata->msg_info.msg;
02312             } else {
02313                 msg = e->body.tsx_state.src.tdata->msg;
02314             }
02315 
02316             code = msg->line.status.code;
02317             reason = msg->line.status.reason;
02318 
02319             /* Start ringback for 180 for UAC unless there's SDP in 180 */
02320             if (call_info.role==PJSIP_ROLE_UAC && code==180 && 
02321                 msg->body == NULL && 
02322                 call_info.media_status==PJSUA_CALL_MEDIA_NONE) 
02323             {
02324                 ringback_start(call_id);
02325             }
02326 
02327             PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s (%d %.*s)", 
02328                       call_id, call_info.state_text.ptr,
02329                       code, (int)reason.slen, reason.ptr));
02330         } else {
02331             PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s", 
02332                       call_id,
02333                       call_info.state_text.ptr));
02334         }
02335 
02336         if (current_call==PJSUA_INVALID_ID)
02337             current_call = call_id;
02338 
02339     }
02340 }
02341 
02342 
02346 static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
02347                              pjsip_rx_data *rdata)
02348 {
02349     pjsua_call_info call_info;
02350 
02351     PJ_UNUSED_ARG(acc_id);
02352     PJ_UNUSED_ARG(rdata);
02353 
02354     pjsua_call_get_info(call_id, &call_info);
02355 
02356     /* Start ringback */
02357     ring_start(call_id);
02358 
02359     if (current_call==PJSUA_INVALID_ID)
02360         current_call = call_id;
02361 
02362     if (app_config.auto_answer > 0) {
02363         pjsua_call_answer(call_id, app_config.auto_answer, NULL, NULL);
02364     }
02365 
02366     if (app_config.auto_answer < 200) {
02367         PJ_LOG(3,(THIS_FILE,
02368                   "Incoming call for account %d!\n"
02369                   "From: %s\n"
02370                   "To: %s\n"
02371                   "Press a to answer or h to reject call",
02372                   acc_id,
02373                   call_info.remote_info.ptr,
02374                   call_info.local_info.ptr));
02375     }
02376 }
02377 
02378 
02379 /*
02380  * Handler when a transaction within a call has changed state.
02381  */
02382 static void on_call_tsx_state(pjsua_call_id call_id,
02383                               pjsip_transaction *tsx,
02384                               pjsip_event *e)
02385 {
02386     const pjsip_method info_method = 
02387     {
02388         PJSIP_OTHER_METHOD,
02389         { "INFO", 4 }
02390     };
02391 
02392     if (pjsip_method_cmp(&tsx->method, &info_method)==0) {
02393         /*
02394          * Handle INFO method.
02395          */
02396         if (tsx->role == PJSIP_ROLE_UAC && 
02397             (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
02398                (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
02399                 e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED))) 
02400         {
02401             /* Status of outgoing INFO request */
02402             if (tsx->status_code >= 200 && tsx->status_code < 300) {
02403                 PJ_LOG(4,(THIS_FILE, 
02404                           "Call %d: DTMF sent successfully with INFO",
02405                           call_id));
02406             } else if (tsx->status_code >= 300) {
02407                 PJ_LOG(4,(THIS_FILE, 
02408                           "Call %d: Failed to send DTMF with INFO: %d/%.*s",
02409                           call_id,
02410                           tsx->status_code,
02411                           (int)tsx->status_text.slen,
02412                           tsx->status_text.ptr));
02413             }
02414         } else if (tsx->role == PJSIP_ROLE_UAS &&
02415                    tsx->state == PJSIP_TSX_STATE_TRYING)
02416         {
02417             /* Answer incoming INFO with 200/OK */
02418             pjsip_rx_data *rdata;
02419             pjsip_tx_data *tdata;
02420             pj_status_t status;
02421 
02422             rdata = e->body.tsx_state.src.rdata;
02423 
02424             if (rdata->msg_info.msg->body) {
02425                 status = pjsip_endpt_create_response(tsx->endpt, rdata,
02426                                                      200, NULL, &tdata);
02427                 if (status == PJ_SUCCESS)
02428                     status = pjsip_tsx_send_msg(tsx, tdata);
02429 
02430                 PJ_LOG(3,(THIS_FILE, "Call %d: incoming INFO:\n%.*s", 
02431                           call_id,
02432                           (int)rdata->msg_info.msg->body->len,
02433                           rdata->msg_info.msg->body->data));
02434             } else {
02435                 status = pjsip_endpt_create_response(tsx->endpt, rdata,
02436                                                      400, NULL, &tdata);
02437                 if (status == PJ_SUCCESS)
02438                     status = pjsip_tsx_send_msg(tsx, tdata);
02439             }
02440         }
02441     }
02442 }
02443 
02444 
02445 /*
02446  * Callback on media state changed event.
02447  * The action may connect the call to sound device, to file, or
02448  * to loop the call.
02449  */
02450 static void on_call_media_state(pjsua_call_id call_id)
02451 {
02452     pjsua_call_info call_info;
02453 
02454     pjsua_call_get_info(call_id, &call_info);
02455 
02456     /* Stop ringback */
02457     ring_stop(call_id);
02458 
02459     /* Connect ports appropriately when media status is ACTIVE or REMOTE HOLD,
02460      * otherwise we should NOT connect the ports.
02461      */
02462     if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE ||
02463         call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD)
02464     {
02465         pj_bool_t connect_sound = PJ_TRUE;
02466 
02467         /* Loopback sound, if desired */
02468         if (app_config.auto_loop) {
02469             pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot);
02470             connect_sound = PJ_FALSE;
02471         }
02472 
02473         /* Automatically record conversation, if desired */
02474         if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
02475             pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
02476         }
02477 
02478         /* Stream a file, if desired */
02479         if ((app_config.auto_play || app_config.auto_play_hangup) && 
02480             app_config.wav_port != PJSUA_INVALID_ID)
02481         {
02482             pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
02483             connect_sound = PJ_FALSE;
02484         }
02485 
02486         /* Put call in conference with other calls, if desired */
02487         if (app_config.auto_conf) {
02488             pjsua_call_id call_ids[PJSUA_MAX_CALLS];
02489             unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
02490             unsigned i;
02491 
02492             /* Get all calls, and establish media connection between
02493              * this call and other calls.
02494              */
02495             pjsua_enum_calls(call_ids, &call_cnt);
02496 
02497             for (i=0; i<call_cnt; ++i) {
02498                 if (call_ids[i] == call_id)
02499                     continue;
02500                 
02501                 if (!pjsua_call_has_media(call_ids[i]))
02502                     continue;
02503 
02504                 pjsua_conf_connect(call_info.conf_slot,
02505                                    pjsua_call_get_conf_port(call_ids[i]));
02506                 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
02507                                    call_info.conf_slot);
02508 
02509                 /* Automatically record conversation, if desired */
02510                 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
02511                     pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]), 
02512                                        app_config.rec_port);
02513                 }
02514 
02515             }
02516 
02517             /* Also connect call to local sound device */
02518             connect_sound = PJ_TRUE;
02519         }
02520 
02521         /* Otherwise connect to sound device */
02522         if (connect_sound) {
02523             pjsua_conf_connect(call_info.conf_slot, 0);
02524             pjsua_conf_connect(0, call_info.conf_slot);
02525 
02526             /* Automatically record conversation, if desired */
02527             if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
02528                 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
02529                 pjsua_conf_connect(0, app_config.rec_port);
02530             }
02531         }
02532     }
02533 
02534     /* Handle media status */
02535     switch (call_info.media_status) {
02536     case PJSUA_CALL_MEDIA_ACTIVE:
02537         PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
02538         break;
02539 
02540     case PJSUA_CALL_MEDIA_LOCAL_HOLD:
02541         PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
02542                   call_id));
02543         break;
02544 
02545     case PJSUA_CALL_MEDIA_REMOTE_HOLD:
02546         PJ_LOG(3,(THIS_FILE, 
02547                   "Media for call %d is suspended (hold) by remote",
02548                   call_id));
02549         break;
02550 
02551     case PJSUA_CALL_MEDIA_ERROR:
02552         PJ_LOG(3,(THIS_FILE,
02553                   "Media has reported error, disconnecting call"));
02554         {
02555             pj_str_t reason = pj_str("ICE negotiation failed");
02556             pjsua_call_hangup(call_id, 500, &reason, NULL);
02557         }
02558         break;
02559 
02560     case PJSUA_CALL_MEDIA_NONE:
02561         PJ_LOG(3,(THIS_FILE, 
02562                   "Media for call %d is inactive",
02563                   call_id));
02564         break;
02565 
02566     default:
02567         pj_assert(!"Unhandled media status");
02568         break;
02569     }
02570 }
02571 
02572 /*
02573  * DTMF callback.
02574  */
02575 static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
02576 {
02577     PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
02578 }
02579 
02580 /*
02581  * Redirection handler.
02582  */
02583 static pjsip_redirect_op call_on_redirected(pjsua_call_id call_id, 
02584                                             const pjsip_uri *target,
02585                                             const pjsip_event *e)
02586 {
02587     PJ_UNUSED_ARG(e);
02588 
02589     if (app_config.redir_op == PJSIP_REDIRECT_PENDING) {
02590         char uristr[PJSIP_MAX_URL_SIZE];
02591         int len;
02592 
02593         len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr, 
02594                               sizeof(uristr));
02595         if (len < 1) {
02596             pj_ansi_strcpy(uristr, "--URI too long--");
02597         }
02598 
02599         PJ_LOG(3,(THIS_FILE, "Call %d is being redirected to %.*s. "
02600                   "Press 'Ra' to accept, 'Rr' to reject, or 'Rd' to "
02601                   "disconnect.",
02602                   call_id, len, uristr));
02603     }
02604 
02605     return app_config.redir_op;
02606 }
02607 
02608 /*
02609  * Handler registration status has changed.
02610  */
02611 static void on_reg_state(pjsua_acc_id acc_id)
02612 {
02613     PJ_UNUSED_ARG(acc_id);
02614 
02615     // Log already written.
02616 }
02617 
02618 
02619 /*
02620  * Handler for incoming presence subscription request
02621  */
02622 static void on_incoming_subscribe(pjsua_acc_id acc_id,
02623                                   pjsua_srv_pres *srv_pres,
02624                                   pjsua_buddy_id buddy_id,
02625                                   const pj_str_t *from,
02626                                   pjsip_rx_data *rdata,
02627                                   pjsip_status_code *code,
02628                                   pj_str_t *reason,
02629                                   pjsua_msg_data *msg_data)
02630 {
02631     /* Just accept the request (the default behavior) */
02632     PJ_UNUSED_ARG(acc_id);
02633     PJ_UNUSED_ARG(srv_pres);
02634     PJ_UNUSED_ARG(buddy_id);
02635     PJ_UNUSED_ARG(from);
02636     PJ_UNUSED_ARG(rdata);
02637     PJ_UNUSED_ARG(code);
02638     PJ_UNUSED_ARG(reason);
02639     PJ_UNUSED_ARG(msg_data);
02640 }
02641 
02642 
02643 /*
02644  * Handler on buddy state changed.
02645  */
02646 static void on_buddy_state(pjsua_buddy_id buddy_id)
02647 {
02648     pjsua_buddy_info info;
02649     pjsua_buddy_get_info(buddy_id, &info);
02650 
02651     PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s, subscription state is %s "
02652                          "(last termination reason code=%d %.*s)",
02653               (int)info.uri.slen,
02654               info.uri.ptr,
02655               (int)info.status_text.slen,
02656               info.status_text.ptr,
02657               info.sub_state_name,
02658               info.sub_term_code,
02659               (int)info.sub_term_reason.slen,
02660               info.sub_term_reason.ptr));
02661 }
02662 
02663 
02667 static void on_pager(pjsua_call_id call_id, const pj_str_t *from, 
02668                      const pj_str_t *to, const pj_str_t *contact,
02669                      const pj_str_t *mime_type, const pj_str_t *text)
02670 {
02671     /* Note: call index may be -1 */
02672     PJ_UNUSED_ARG(call_id);
02673     PJ_UNUSED_ARG(to);
02674     PJ_UNUSED_ARG(contact);
02675     PJ_UNUSED_ARG(mime_type);
02676 
02677     PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s (%.*s)",
02678               (int)from->slen, from->ptr,
02679               (int)text->slen, text->ptr,
02680               (int)mime_type->slen, mime_type->ptr));
02681 }
02682 
02683 
02687 static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
02688                       const pj_str_t *to, const pj_str_t *contact,
02689                       pj_bool_t is_typing)
02690 {
02691     PJ_UNUSED_ARG(call_id);
02692     PJ_UNUSED_ARG(to);
02693     PJ_UNUSED_ARG(contact);
02694 
02695     PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
02696               (int)from->slen, from->ptr,
02697               (is_typing?"is typing..":"has stopped typing")));
02698 }
02699 
02700 
02704 static void on_call_transfer_status(pjsua_call_id call_id,
02705                                     int status_code,
02706                                     const pj_str_t *status_text,
02707                                     pj_bool_t final,
02708                                     pj_bool_t *p_cont)
02709 {
02710     PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
02711               call_id, status_code,
02712               (int)status_text->slen, status_text->ptr,
02713               (final ? "[final]" : "")));
02714 
02715     if (status_code/100 == 2) {
02716         PJ_LOG(3,(THIS_FILE, 
02717                   "Call %d: call transfered successfully, disconnecting call",
02718                   call_id));
02719         pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
02720         *p_cont = PJ_FALSE;
02721     }
02722 }
02723 
02724 
02725 /*
02726  * Notification that call is being replaced.
02727  */
02728 static void on_call_replaced(pjsua_call_id old_call_id,
02729                              pjsua_call_id new_call_id)
02730 {
02731     pjsua_call_info old_ci, new_ci;
02732 
02733     pjsua_call_get_info(old_call_id, &old_ci);
02734     pjsua_call_get_info(new_call_id, &new_ci);
02735 
02736     PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
02737                          "call %d with %.*s",
02738                          old_call_id, 
02739                          (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
02740                          new_call_id,
02741                          (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
02742 }
02743 
02744 
02745 /*
02746  * NAT type detection callback.
02747  */
02748 static void on_nat_detect(const pj_stun_nat_detect_result *res)
02749 {
02750     if (res->status != PJ_SUCCESS) {
02751         pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
02752     } else {
02753         PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
02754     }
02755 }
02756 
02757 
02758 /*
02759  * MWI indication
02760  */
02761 static void on_mwi_info(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info)
02762 {
02763     pj_str_t body;
02764     
02765     PJ_LOG(3,(THIS_FILE, "Received MWI for acc %d:", acc_id));
02766 
02767     if (mwi_info->rdata->msg_info.ctype) {
02768         const pjsip_ctype_hdr *ctype = mwi_info->rdata->msg_info.ctype;
02769 
02770         PJ_LOG(3,(THIS_FILE, " Content-Type: %.*s/%.*s",
02771                   (int)ctype->media.type.slen,
02772                   ctype->media.type.ptr,
02773                   (int)ctype->media.subtype.slen,
02774                   ctype->media.subtype.ptr));
02775     }
02776 
02777     if (!mwi_info->rdata->msg_info.msg->body) {
02778         PJ_LOG(3,(THIS_FILE, "  no message body"));
02779         return;
02780     }
02781 
02782     body.ptr = mwi_info->rdata->msg_info.msg->body->data;
02783     body.slen = mwi_info->rdata->msg_info.msg->body->len;
02784 
02785     PJ_LOG(3,(THIS_FILE, " Body:\n%.*s", (int)body.slen, body.ptr));
02786 }
02787 
02788 
02789 /*
02790  * Print buddy list.
02791  */
02792 static void print_buddy_list(void)
02793 {
02794     pjsua_buddy_id ids[64];
02795     int i;
02796     unsigned count = PJ_ARRAY_SIZE(ids);
02797 
02798     puts("Buddy list:");
02799 
02800     pjsua_enum_buddies(ids, &count);
02801 
02802     if (count == 0)
02803         puts(" -none-");
02804     else {
02805         for (i=0; i<(int)count; ++i) {
02806             pjsua_buddy_info info;
02807 
02808             if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
02809                 continue;
02810 
02811             printf(" [%2d] <%.*s>  %.*s\n", 
02812                     ids[i]+1, 
02813                     (int)info.status_text.slen,
02814                     info.status_text.ptr, 
02815                     (int)info.uri.slen,
02816                     info.uri.ptr);
02817         }
02818     }
02819     puts("");
02820 }
02821 
02822 
02823 /*
02824  * Print account status.
02825  */
02826 static void print_acc_status(int acc_id)
02827 {
02828     char buf[80];
02829     pjsua_acc_info info;
02830 
02831     pjsua_acc_get_info(acc_id, &info);
02832 
02833     if (!info.has_registration) {
02834         pj_ansi_snprintf(buf, sizeof(buf), "%.*s", 
02835                          (int)info.status_text.slen,
02836                          info.status_text.ptr);
02837 
02838     } else {
02839         pj_ansi_snprintf(buf, sizeof(buf),
02840                          "%d/%.*s (expires=%d)",
02841                          info.status,
02842                          (int)info.status_text.slen,
02843                          info.status_text.ptr,
02844                          info.expires);
02845 
02846     }
02847 
02848     printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
02849            acc_id,  (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
02850     printf("       Online status: %.*s\n", 
02851         (int)info.online_status_text.slen,
02852         info.online_status_text.ptr);
02853 }
02854 
02855 /* Playfile done notification, set timer to hangup calls */
02856 pj_status_t on_playfile_done(pjmedia_port *port, void *usr_data)
02857 {
02858     pj_time_val delay;
02859 
02860     PJ_UNUSED_ARG(port);
02861     PJ_UNUSED_ARG(usr_data);
02862 
02863     /* Just rewind WAV when it is played outside of call */
02864     if (pjsua_call_get_count() == 0) {
02865         pjsua_player_set_pos(app_config.wav_id, 0);
02866         return PJ_SUCCESS;
02867     }
02868 
02869     /* Timer is already active */
02870     if (app_config.auto_hangup_timer.id == 1)
02871         return PJ_SUCCESS;
02872 
02873     app_config.auto_hangup_timer.id = 1;
02874     delay.sec = 0;
02875     delay.msec = 200; /* Give 200 ms before hangup */
02876     pjsip_endpt_schedule_timer(pjsua_get_pjsip_endpt(), 
02877                                &app_config.auto_hangup_timer, 
02878                                &delay);
02879 
02880     return PJ_SUCCESS;
02881 }
02882 
02883 /* Auto hangup timer callback */
02884 static void hangup_timeout_callback(pj_timer_heap_t *timer_heap,
02885                                     struct pj_timer_entry *entry)
02886 {
02887     PJ_UNUSED_ARG(timer_heap);
02888     PJ_UNUSED_ARG(entry);
02889 
02890     app_config.auto_hangup_timer.id = 0;
02891     pjsua_call_hangup_all();
02892 }
02893 
02894 /*
02895  * Show a bit of help.
02896  */
02897 static void keystroke_help(void)
02898 {
02899     pjsua_acc_id acc_ids[16];
02900     unsigned count = PJ_ARRAY_SIZE(acc_ids);
02901     int i;
02902 
02903     printf(">>>>\n");
02904 
02905     pjsua_enum_accs(acc_ids, &count);
02906 
02907     printf("Account list:\n");
02908     for (i=0; i<(int)count; ++i)
02909         print_acc_status(acc_ids[i]);
02910 
02911     print_buddy_list();
02912     
02913     //puts("Commands:");
02914     puts("+=============================================================================+");
02915     puts("|       Call Commands:         |   Buddy, IM & Presence:  |     Account:      |");
02916     puts("|                              |                          |                   |");
02917     puts("|  m  Make new call            | +b  Add new buddy       .| +a  Add new accnt |");
02918     puts("|  M  Make multiple calls      | -b  Delete buddy         | -a  Delete accnt. |");
02919     puts("|  a  Answer call              |  i  Send IM              | !a  Modify accnt. |");
02920     puts("|  h  Hangup call  (ha=all)    |  s  Subscribe presence   | rr  (Re-)register |");
02921     puts("|  H  Hold call                |  u  Unsubscribe presence | ru  Unregister    |");
02922     puts("|  v  re-inVite (release hold) |  t  ToGgle Online status |  >  Cycle next ac.|");
02923     puts("|  U  send UPDATE              |  T  Set online status    |  <  Cycle prev ac.|");
02924     puts("| ],[ Select next/prev call    +--------------------------+-------------------+");
02925     puts("|  x  Xfer call                |      Media Commands:     |  Status & Config: |");
02926     puts("|  X  Xfer with Replaces       |                          |                   |");
02927     puts("|  #  Send RFC 2833 DTMF       | cl  List ports           |  d  Dump status   |");
02928     puts("|  *  Send DTMF with INFO      | cc  Connect port         | dd  Dump detailed |");
02929     puts("| dq  Dump curr. call quality  | cd  Disconnect port      | dc  Dump config   |");
02930     puts("|                              |  V  Adjust audio Volume  |  f  Save config   |");
02931     puts("|  S  Send arbitrary REQUEST   | Cp  Codec priorities     |  f  Save config   |");
02932     puts("+------------------------------+--------------------------+-------------------+");
02933     puts("|  q  QUIT   L  ReLoad   sleep MS   echo [0|1|txt]     n: detect NAT type     |");
02934     puts("+=============================================================================+");
02935 
02936     i = pjsua_call_get_count();
02937     printf("You have %d active call%s\n", i, (i>1?"s":""));
02938 
02939     if (current_call != PJSUA_INVALID_ID) {
02940         pjsua_call_info ci;
02941         if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
02942             printf("Current call id=%d to %.*s [%.*s]\n", current_call,
02943                    (int)ci.remote_info.slen, ci.remote_info.ptr,
02944                    (int)ci.state_text.slen, ci.state_text.ptr);
02945     }
02946 }
02947 
02948 
02949 /*
02950  * Input simple string
02951  */
02952 static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
02953 {
02954     char *p;
02955 
02956     printf("%s (empty to cancel): ", title); fflush(stdout);
02957     if (fgets(buf, len, stdin) == NULL)
02958         return PJ_FALSE;
02959 
02960     /* Remove trailing newlines. */
02961     for (p=buf; ; ++p) {
02962         if (*p=='\r' || *p=='\n') *p='\0';
02963         else if (!*p) break;
02964     }
02965 
02966     if (!*buf)
02967         return PJ_FALSE;
02968     
02969     return PJ_TRUE;
02970 }
02971 
02972 
02973 #define NO_NB   -2
02974 struct input_result
02975 {
02976     int   nb_result;
02977     char *uri_result;
02978 };
02979 
02980 
02981 /*
02982  * Input URL.
02983  */
02984 static void ui_input_url(const char *title, char *buf, int len, 
02985                          struct input_result *result)
02986 {
02987     result->nb_result = NO_NB;
02988     result->uri_result = NULL;
02989 
02990     print_buddy_list();
02991 
02992     printf("Choices:\n"
02993            "   0         For current dialog.\n"
02994            "  -1         All %d buddies in buddy list\n"
02995            "  [1 -%2d]    Select from buddy list\n"
02996            "  URL        An URL\n"
02997            "  <Enter>    Empty input (or 'q') to cancel\n"
02998            , pjsua_get_buddy_count(), pjsua_get_buddy_count());
02999     printf("%s: ", title);
03000 
03001     fflush(stdout);
03002     if (fgets(buf, len, stdin) == NULL)
03003         return;
03004     len = strlen(buf);
03005 
03006     /* Left trim */
03007     while (pj_isspace(*buf)) {
03008         ++buf;
03009         --len;
03010     }
03011 
03012     /* Remove trailing newlines */
03013     while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
03014         buf[--len] = '\0';
03015 
03016     if (len == 0 || buf[0]=='q')
03017         return;
03018 
03019     if (pj_isdigit(*buf) || *buf=='-') {
03020         
03021         int i;
03022         
03023         if (*buf=='-')
03024             i = 1;
03025         else
03026             i = 0;
03027 
03028         for (; i<len; ++i) {
03029             if (!pj_isdigit(buf[i])) {
03030                 puts("Invalid input");
03031                 return;
03032             }
03033         }
03034 
03035         result->nb_result = my_atoi(buf);
03036 
03037         if (result->nb_result >= 0 && 
03038             result->nb_result <= (int)pjsua_get_buddy_count()) 
03039         {
03040             return;
03041         }
03042         if (result->nb_result == -1)
03043             return;
03044 
03045         puts("Invalid input");
03046         result->nb_result = NO_NB;
03047         return;
03048 
03049     } else {
03050         pj_status_t status;
03051 
03052         if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
03053             pjsua_perror(THIS_FILE, "Invalid URL", status);
03054             return;
03055         }
03056 
03057         result->uri_result = buf;
03058     }
03059 }
03060 
03061 /*
03062  * List the ports in conference bridge
03063  */
03064 static void conf_list(void)
03065 {
03066     unsigned i, count;
03067     pjsua_conf_port_id id[PJSUA_MAX_CALLS];
03068 
03069     printf("Conference ports:\n");
03070 
03071     count = PJ_ARRAY_SIZE(id);
03072     pjsua_enum_conf_ports(id, &count);
03073 
03074     for (i=0; i<count; ++i) {
03075         char txlist[PJSUA_MAX_CALLS*4+10];
03076         unsigned j;
03077         pjsua_conf_port_info info;
03078 
03079         pjsua_conf_get_port_info(id[i], &info);
03080 
03081         txlist[0] = '\0';
03082         for (j=0; j<info.listener_cnt; ++j) {
03083             char s[10];
03084             pj_ansi_sprintf(s, "#%d ", info.listeners[j]);
03085             pj_ansi_strcat(txlist, s);
03086         }
03087         printf("Port #%02d[%2dKHz/%dms/%d] %20.*s  transmitting to: %s\n", 
03088                info.slot_id, 
03089                info.clock_rate/1000,
03090                info.samples_per_frame*1000/info.channel_count/info.clock_rate,
03091                info.channel_count,
03092                (int)info.name.slen, 
03093                info.name.ptr,
03094                txlist);
03095 
03096     }
03097     puts("");
03098 }
03099 
03100 
03101 /*
03102  * Send arbitrary request to remote host
03103  */
03104 static void send_request(char *cstr_method, const pj_str_t *dst_uri)
03105 {
03106     pj_str_t str_method;
03107     pjsip_method method;
03108     pjsip_tx_data *tdata;
03109     pjsip_endpoint *endpt;
03110     pj_status_t status;
03111 
03112     endpt = pjsua_get_pjsip_endpt();
03113 
03114     str_method = pj_str(cstr_method);
03115     pjsip_method_init_np(&method, &str_method);
03116 
03117     status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata);
03118 
03119     status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
03120     if (status != PJ_SUCCESS) {
03121         pjsua_perror(THIS_FILE, "Unable to send request", status);
03122         return;
03123     }
03124 }
03125 
03126 
03127 /*
03128  * Change extended online status.
03129  */
03130 static void change_online_status(void)
03131 {
03132     char menuin[32];
03133     pj_bool_t online_status;
03134     pjrpid_element elem;
03135     int i, choice;
03136 
03137     enum {
03138         AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
03139     };
03140 
03141     struct opt {
03142         int id;
03143         char *name;
03144     } opts[] = {
03145         { AVAILABLE, "Available" },
03146         { BUSY, "Busy"},
03147         { OTP, "On the phone"},
03148         { IDLE, "Idle"},
03149         { AWAY, "Away"},
03150         { BRB, "Be right back"},
03151         { OFFLINE, "Offline"}
03152     };
03153 
03154     printf("\n"
03155            "Choices:\n");
03156     for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
03157         printf("  %d  %s\n", opts[i].id+1, opts[i].name);
03158     }
03159 
03160     if (!simple_input("Select status", menuin, sizeof(menuin)))
03161         return;
03162 
03163     choice = atoi(menuin) - 1;
03164     if (choice < 0 || choice >= OPT_MAX) {
03165         puts("Invalid selection");
03166         return;
03167     }
03168 
03169     pj_bzero(&elem, sizeof(elem));
03170     elem.type = PJRPID_ELEMENT_TYPE_PERSON;
03171 
03172     online_status = PJ_TRUE;
03173 
03174     switch (choice) {
03175     case AVAILABLE:
03176         break;
03177     case BUSY:
03178         elem.activity = PJRPID_ACTIVITY_BUSY;
03179         elem.note = pj_str("Busy");
03180         break;
03181     case OTP:
03182         elem.activity = PJRPID_ACTIVITY_BUSY;
03183         elem.note = pj_str("On the phone");
03184         break;
03185     case IDLE:
03186         elem.activity = PJRPID_ACTIVITY_UNKNOWN;
03187         elem.note = pj_str("Idle");
03188         break;
03189     case AWAY:
03190         elem.activity = PJRPID_ACTIVITY_AWAY;
03191         elem.note = pj_str("Away");
03192         break;
03193     case BRB:
03194         elem.activity = PJRPID_ACTIVITY_UNKNOWN;
03195         elem.note = pj_str("Be right back");
03196         break;
03197     case OFFLINE:
03198         online_status = PJ_FALSE;
03199         break;
03200     }
03201 
03202     pjsua_acc_set_online_status2(current_acc, online_status, &elem);
03203 }
03204 
03205 
03206 /*
03207  * Change codec priorities.
03208  */
03209 static void manage_codec_prio(void)
03210 {
03211     pjsua_codec_info c[32];
03212     unsigned i, count = PJ_ARRAY_SIZE(c);
03213     char input[32];
03214     char *codec, *prio;
03215     pj_str_t id;
03216     int new_prio;
03217     pj_status_t status;
03218 
03219     printf("List of codecs:\n");
03220 
03221     pjsua_enum_codecs(c, &count);
03222     for (i=0; i<count; ++i) {
03223         printf("  %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
03224                                c[i].codec_id.ptr);
03225     }
03226 
03227     puts("");
03228     puts("Enter codec id and its new priority "
03229          "(e.g. \"speex/16000 200\"), empty to cancel:");
03230 
03231     printf("Codec name (\"*\" for all) and priority: ");
03232     if (fgets(input, sizeof(input), stdin) == NULL)
03233         return;
03234     if (input[0]=='\r' || input[0]=='\n') {
03235         puts("Done");
03236         return;
03237     }
03238 
03239     codec = strtok(input, " \t\r\n");
03240     prio = strtok(NULL, " \r\n");
03241 
03242     if (!codec || !prio) {
03243         puts("Invalid input");
03244         return;
03245     }
03246 
03247     new_prio = atoi(prio);
03248     if (new_prio < 0) 
03249         new_prio = 0;
03250     else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST) 
03251         new_prio = PJMEDIA_CODEC_PRIO_HIGHEST;
03252 
03253     status = pjsua_codec_set_priority(pj_cstr(&id, codec), 
03254                                       (pj_uint8_t)new_prio);
03255     if (status != PJ_SUCCESS)
03256         pjsua_perror(THIS_FILE, "Error setting codec priority", status);
03257 }
03258 
03259 
03260 /*
03261  * Main "user interface" loop.
03262  */
03263 void console_app_main(const pj_str_t *uri_to_call)
03264 {
03265     char menuin[32];
03266     char buf[128];
03267     char text[128];
03268     int i, count;
03269     char *uri;
03270     pj_str_t tmp;
03271     struct input_result result;
03272     pjsua_call_info call_info;
03273     pjsua_acc_info acc_info;
03274 
03275 
03276     /* If user specifies URI to call, then call the URI */
03277     if (uri_to_call->slen) {
03278         pjsua_call_make_call( current_acc, uri_to_call, 0, NULL, NULL, NULL);
03279     }
03280 
03281     keystroke_help();
03282 
03283     for (;;) {
03284 
03285         printf(">>> ");
03286         fflush(stdout);
03287 
03288         if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
03289             /* 
03290              * Be friendly to users who redirect commands into
03291              * program, when file ends, resume with kbd.
03292              * If exit is desired end script with q for quit
03293              */
03294             /* Reopen stdin/stdout/stderr to /dev/console */
03295 #if defined(PJ_WIN32) && PJ_WIN32!=0
03296             if (freopen ("CONIN$", "r", stdin) == NULL) {
03297 #else
03298             if (1) {
03299 #endif
03300                 puts("Cannot switch back to console from file redirection");
03301                 menuin[0] = 'q';
03302                 menuin[1] = '\0';
03303             } else {
03304                 puts("Switched back to console from file redirection");
03305                 continue;
03306             }
03307         }
03308 
03309         if (cmd_echo) {
03310             printf("%s", menuin);
03311         }
03312 
03313         switch (menuin[0]) {
03314 
03315         case 'm':
03316             /* Make call! : */
03317             printf("(You currently have %d calls)\n", 
03318                      pjsua_call_get_count());
03319             
03320             uri = NULL;
03321             ui_input_url("Make call", buf, sizeof(buf), &result);
03322             if (result.nb_result != NO_NB) {
03323 
03324                 if (result.nb_result == -1 || result.nb_result == 0) {
03325                     puts("You can't do that with make call!");
03326                     continue;
03327                 } else {
03328                     pjsua_buddy_info binfo;
03329                     pjsua_buddy_get_info(result.nb_result-1, &binfo);
03330                     tmp.ptr = buf;
03331                     pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
03332                 }
03333 
03334             } else if (result.uri_result) {
03335                 tmp = pj_str(result.uri_result);
03336             } else {
03337                 tmp.slen = 0;
03338             }
03339             
03340             pjsua_call_make_call( current_acc, &tmp, 0, NULL, NULL, NULL);
03341             break;
03342 
03343         case 'M':
03344             /* Make multiple calls! : */
03345             printf("(You currently have %d calls)\n", 
03346                    pjsua_call_get_count());
03347             
03348             if (!simple_input("Number of calls", menuin, sizeof(menuin)))
03349                 continue;
03350 
03351             count = my_atoi(menuin);
03352             if (count < 1)
03353                 continue;
03354 
03355             ui_input_url("Make call", buf, sizeof(buf), &result);
03356             if (result.nb_result != NO_NB) {
03357                 pjsua_buddy_info binfo;
03358                 if (result.nb_result == -1 || result.nb_result == 0) {
03359                     puts("You can't do that with make call!");
03360                     continue;
03361                 }
03362                 pjsua_buddy_get_info(result.nb_result-1, &binfo);
03363                 tmp.ptr = buf;
03364                 pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
03365             } else {
03366                 tmp = pj_str(result.uri_result);
03367             }
03368 
03369             for (i=0; i<my_atoi(menuin); ++i) {
03370                 pj_status_t status;
03371             
03372                 status = pjsua_call_make_call(current_acc, &tmp, 0, NULL,
03373                                               NULL, NULL);
03374                 if (status != PJ_SUCCESS)
03375                     break;
03376             }
03377             break;
03378 
03379         case 'n':
03380             i = pjsua_detect_nat_type();
03381             if (i != PJ_SUCCESS)
03382                 pjsua_perror(THIS_FILE, "Error", i);
03383             break;
03384 
03385         case 'i':
03386             /* Send instant messaeg */
03387 
03388             /* i is for call index to send message, if any */
03389             i = -1;
03390     
03391             /* Make compiler happy. */
03392             uri = NULL;
03393 
03394             /* Input destination. */
03395             ui_input_url("Send IM to", buf, sizeof(buf), &result);
03396             if (result.nb_result != NO_NB) {
03397 
03398                 if (result.nb_result == -1) {
03399                     puts("You can't send broadcast IM like that!");
03400                     continue;
03401 
03402                 } else if (result.nb_result == 0) {
03403     
03404                     i = current_call;
03405 
03406                 } else {
03407                     pjsua_buddy_info binfo;
03408                     pjsua_buddy_get_info(result.nb_result-1, &binfo);
03409                     tmp.ptr = buf;
03410                     pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
03411                     uri = buf;
03412                 }
03413 
03414             } else if (result.uri_result) {
03415                 uri = result.uri_result;
03416             }
03417             
03418 
03419             /* Send typing indication. */
03420             if (i != -1)
03421                 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
03422             else {
03423                 pj_str_t tmp_uri = pj_str(uri);
03424                 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
03425             }
03426 
03427             /* Input the IM . */
03428             if (!simple_input("Message", text, sizeof(text))) {
03429                 /*
03430                  * Cancelled.
03431                  * Send typing notification too, saying we're not typing.
03432                  */
03433                 if (i != -1)
03434                     pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
03435                 else {
03436                     pj_str_t tmp_uri = pj_str(uri);
03437                     pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
03438                 }
03439                 continue;
03440             }
03441 
03442             tmp = pj_str(text);
03443 
03444             /* Send the IM */
03445             if (i != -1)
03446                 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
03447             else {
03448                 pj_str_t tmp_uri = pj_str(uri);
03449                 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
03450             }
03451 
03452             break;
03453 
03454         case 'a':
03455 
03456             if (current_call != -1) {
03457                 pjsua_call_get_info(current_call, &call_info);
03458             } else {
03459                 /* Make compiler happy */
03460                 call_info.role = PJSIP_ROLE_UAC;
03461                 call_info.state = PJSIP_INV_STATE_DISCONNECTED;
03462             }
03463 
03464             if (current_call == -1 || 
03465                 call_info.role != PJSIP_ROLE_UAS ||
03466                 call_info.state >= PJSIP_INV_STATE_CONNECTING)
03467             {
03468                 puts("No pending incoming call");
03469                 fflush(stdout);
03470                 continue;
03471 
03472             } else {
03473                 int st_code;
03474                 char contact[120];
03475                 pj_str_t hname = { "Contact", 7 };
03476                 pj_str_t hvalue;
03477                 pjsip_generic_string_hdr hcontact;
03478                 pjsua_msg_data msg_data;
03479 
03480                 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
03481                     continue;
03482                 
03483                 st_code = my_atoi(buf);
03484                 if (st_code < 100)
03485                     continue;
03486 
03487                 pjsua_msg_data_init(&msg_data);
03488 
03489                 if (st_code/100 == 3) {
03490                     if (!simple_input("Enter URL to be put in Contact", 
03491                                       contact, sizeof(contact)))
03492                         continue;
03493                     hvalue = pj_str(contact);
03494                     pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
03495 
03496                     pj_list_push_back(&msg_data.hdr_list, &hcontact);
03497                 }
03498 
03499                 /*
03500                  * Must check again!
03501                  * Call may have been disconnected while we're waiting for 
03502                  * keyboard input.
03503                  */
03504                 if (current_call == -1) {
03505                     puts("Call has been disconnected");
03506                     fflush(stdout);
03507                     continue;
03508                 }
03509 
03510                 pjsua_call_answer(current_call, st_code, NULL, &msg_data);
03511             }
03512 
03513             break;
03514 
03515 
03516         case 'h':
03517 
03518             if (current_call == -1) {
03519                 puts("No current call");
03520                 fflush(stdout);
03521                 continue;
03522 
03523             } else if (menuin[1] == 'a') {
03524                 
03525                 /* Hangup all calls */
03526                 pjsua_call_hangup_all();
03527 
03528             } else {
03529 
03530                 /* Hangup current calls */
03531                 pjsua_call_hangup(current_call, 0, NULL, NULL);
03532             }
03533             break;
03534 
03535         case ']':
03536         case '[':
03537             /*
03538              * Cycle next/prev dialog.
03539              */
03540             if (menuin[0] == ']') {
03541                 find_next_call();
03542 
03543             } else {
03544                 find_prev_call();
03545             }
03546 
03547             if (current_call != -1) {
03548                 
03549                 pjsua_call_get_info(current_call, &call_info);
03550                 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s", 
03551                           (int)call_info.remote_info.slen, 
03552                           call_info.remote_info.ptr));
03553 
03554             } else {
03555                 PJ_LOG(3,(THIS_FILE,"No current dialog"));
03556             }
03557             break;
03558 
03559 
03560         case '>':
03561         case '<':
03562             if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
03563                 break;
03564 
03565             i = my_atoi(buf);
03566             if (pjsua_acc_is_valid(i)) {
03567                 pjsua_acc_set_default(i);
03568                 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
03569             } else {
03570                 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
03571             }
03572             break;
03573 
03574 
03575         case '+':
03576             if (menuin[1] == 'b') {
03577             
03578                 pjsua_buddy_config buddy_cfg;
03579                 pjsua_buddy_id buddy_id;
03580                 pj_status_t status;
03581 
03582                 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
03583                     break;
03584 
03585                 if (pjsua_verify_sip_url(buf) != PJ_SUCCESS) {
03586                     printf("Invalid SIP URI '%s'\n", buf);
03587                     break;
03588                 }
03589 
03590                 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
03591 
03592                 buddy_cfg.uri = pj_str(buf);
03593                 buddy_cfg.subscribe = PJ_TRUE;
03594 
03595                 status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
03596                 if (status == PJ_SUCCESS) {
03597                     printf("New buddy '%s' added at index %d\n",
03598                            buf, buddy_id+1);
03599                 }
03600 
03601             } else if (menuin[1] == 'a') {
03602 
03603                 char id[80], registrar[80], realm[80], uname[80], passwd[30];
03604                 pjsua_acc_config acc_cfg;
03605                 pj_status_t status;
03606 
03607                 if (!simple_input("Your SIP URL:", id, sizeof(id)))
03608                     break;
03609                 if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
03610                     break;
03611                 if (!simple_input("Auth Realm:", realm, sizeof(realm)))
03612                     break;
03613                 if (!simple_input("Auth Username:", uname, sizeof(uname)))
03614                     break;
03615                 if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
03616                     break;
03617 
03618                 pjsua_acc_config_default(&acc_cfg);
03619                 acc_cfg.id = pj_str(id);
03620                 acc_cfg.reg_uri = pj_str(registrar);
03621                 acc_cfg.cred_count = 1;
03622                 acc_cfg.cred_info[0].scheme = pj_str("Digest");
03623                 acc_cfg.cred_info[0].realm = pj_str(realm);
03624                 acc_cfg.cred_info[0].username = pj_str(uname);
03625                 acc_cfg.cred_info[0].data_type = 0;
03626                 acc_cfg.cred_info[0].data = pj_str(passwd);
03627 
03628                 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
03629                 if (status != PJ_SUCCESS) {
03630                     pjsua_perror(THIS_FILE, "Error adding new account", status);
03631                 }
03632 
03633             } else {
03634                 printf("Invalid input %s\n", menuin);
03635             }
03636             break;
03637 
03638         case '-':
03639             if (menuin[1] == 'b') {
03640                 if (!simple_input("Enter buddy ID to delete",buf,sizeof(buf)))
03641                     break;
03642 
03643                 i = my_atoi(buf) - 1;
03644 
03645                 if (!pjsua_buddy_is_valid(i)) {
03646                     printf("Invalid buddy id %d\n", i);
03647                 } else {
03648                     pjsua_buddy_del(i);
03649                     printf("Buddy %d deleted\n", i);
03650                 }
03651 
03652             } else if (menuin[1] == 'a') {
03653 
03654                 if (!simple_input("Enter account ID to delete",buf,sizeof(buf)))
03655                     break;
03656 
03657                 i = my_atoi(buf);
03658 
03659                 if (!pjsua_acc_is_valid(i)) {
03660                     printf("Invalid account id %d\n", i);
03661                 } else {
03662                     pjsua_acc_del(i);
03663                     printf("Account %d deleted\n", i);
03664                 }
03665 
03666             } else {
03667                 printf("Invalid input %s\n", menuin);
03668             }
03669             break;
03670 
03671         case 'H':
03672             /*
03673              * Hold call.
03674              */
03675             if (current_call != -1) {
03676                 
03677                 pjsua_call_set_hold(current_call, NULL);
03678 
03679             } else {
03680                 PJ_LOG(3,(THIS_FILE, "No current call"));
03681             }
03682             break;
03683 
03684         case 'v':
03685             /*
03686              * Send re-INVITE (to release hold, etc).
03687              */
03688             if (current_call != -1) {
03689                 
03690                 pjsua_call_reinvite(current_call, PJ_TRUE, NULL);
03691 
03692             } else {
03693                 PJ_LOG(3,(THIS_FILE, "No current call"));
03694             }
03695             break;
03696 
03697         case 'U':
03698             /*
03699              * Send UPDATE
03700              */
03701             if (current_call != -1) {
03702                 
03703                 pjsua_call_update(current_call, 0, NULL);
03704 
03705             } else {
03706                 PJ_LOG(3,(THIS_FILE, "No current call"));
03707             }
03708             break;
03709 
03710         case 'C':
03711             if (menuin[1] == 'p') {
03712                 manage_codec_prio();
03713             }
03714             break;
03715 
03716         case 'x':
03717             /*
03718              * Transfer call.
03719              */
03720             if (current_call == -1) {
03721                 
03722                 PJ_LOG(3,(THIS_FILE, "No current call"));
03723 
03724             } else {
03725                 int call = current_call;
03726                 pjsua_msg_data msg_data;
03727                 pjsip_generic_string_hdr refer_sub;
03728                 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
03729                 pj_str_t STR_FALSE = { "false", 5 };
03730                 pjsua_call_info ci;
03731 
03732                 pjsua_call_get_info(current_call, &ci);
03733                 printf("Transfering current call [%d] %.*s\n",
03734                        current_call,
03735                        (int)ci.remote_info.slen, ci.remote_info.ptr);
03736 
03737                 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
03738 
03739                 /* Check if call is still there. */
03740 
03741                 if (call != current_call) {
03742                     puts("Call has been disconnected");
03743                     continue;
03744                 }
03745 
03746                 pjsua_msg_data_init(&msg_data);
03747                 if (app_config.no_refersub) {
03748                     /* Add Refer-Sub: false in outgoing REFER request */
03749                     pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
03750                                                    &STR_FALSE);
03751                     pj_list_push_back(&msg_data.hdr_list, &refer_sub);
03752                 }
03753                 if (result.nb_result != NO_NB) {
03754                     if (result.nb_result == -1 || result.nb_result == 0)
03755                         puts("You can't do that with transfer call!");
03756                     else {
03757                         pjsua_buddy_info binfo;
03758                         pjsua_buddy_get_info(result.nb_result-1, &binfo);
03759                         pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
03760                     }
03761 
03762                 } else if (result.uri_result) {
03763                     pj_str_t tmp;
03764                     tmp = pj_str(result.uri_result);
03765                     pjsua_call_xfer( current_call, &tmp, &msg_data);
03766                 }
03767             }
03768             break;
03769 
03770         case 'X':
03771             /*
03772              * Transfer call with replaces.
03773              */
03774             if (current_call == -1) {
03775                 
03776                 PJ_LOG(3,(THIS_FILE, "No current call"));
03777 
03778             } else {
03779                 int call = current_call;
03780                 int dst_call;
03781                 pjsua_msg_data msg_data;
03782                 pjsip_generic_string_hdr refer_sub;
03783                 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
03784                 pj_str_t STR_FALSE = { "false", 5 };
03785                 pjsua_call_id ids[PJSUA_MAX_CALLS];
03786                 pjsua_call_info ci;
03787                 unsigned i, count;
03788 
03789                 count = PJ_ARRAY_SIZE(ids);
03790                 pjsua_enum_calls(ids, &count);
03791 
03792                 if (count <= 1) {
03793                     puts("There are no other calls");
03794                     continue;
03795                 }
03796 
03797                 pjsua_call_get_info(current_call, &ci);
03798                 printf("Transfer call [%d] %.*s to one of the following:\n",
03799                        current_call,
03800                        (int)ci.remote_info.slen, ci.remote_info.ptr);
03801 
03802                 for (i=0; i<count; ++i) {
03803                     pjsua_call_info call_info;
03804 
03805                     if (ids[i] == call)
03806                         continue;
03807 
03808                     pjsua_call_get_info(ids[i], &call_info);
03809                     printf("%d  %.*s [%.*s]\n",
03810                            ids[i],
03811                            (int)call_info.remote_info.slen,
03812                            call_info.remote_info.ptr,
03813                            (int)call_info.state_text.slen,
03814                            call_info.state_text.ptr);
03815                 }
03816 
03817                 if (!simple_input("Enter call number to be replaced", 
03818                                   buf, sizeof(buf)))
03819                     continue;
03820 
03821                 dst_call = my_atoi(buf);
03822 
03823                 /* Check if call is still there. */
03824 
03825                 if (call != current_call) {
03826                     puts("Call has been disconnected");
03827                     continue;
03828                 }
03829 
03830                 /* Check that destination call is valid. */
03831                 if (dst_call == call) {
03832                     puts("Destination call number must not be the same "
03833                          "as the call being transfered");
03834                     continue;
03835                 }
03836                 if (dst_call >= PJSUA_MAX_CALLS) {
03837                     puts("Invalid destination call number");
03838                     continue;
03839                 }
03840                 if (!pjsua_call_is_active(dst_call)) {
03841                     puts("Invalid destination call number");
03842                     continue;
03843                 }
03844 
03845                 pjsua_msg_data_init(&msg_data);
03846                 if (app_config.no_refersub) {
03847                     /* Add Refer-Sub: false in outgoing REFER request */
03848                     pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
03849                                                    &STR_FALSE);
03850                     pj_list_push_back(&msg_data.hdr_list, &refer_sub);
03851                 }
03852 
03853                 pjsua_call_xfer_replaces(call, dst_call, 
03854                                          PJSUA_XFER_NO_REQUIRE_REPLACES, 
03855                                          &msg_data);
03856             }
03857             break;
03858 
03859         case '#':
03860             /*
03861              * Send DTMF strings.
03862              */
03863             if (current_call == -1) {
03864                 
03865                 PJ_LOG(3,(THIS_FILE, "No current call"));
03866 
03867             } else if (!pjsua_call_has_media(current_call)) {
03868 
03869                 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
03870 
03871             } else {
03872                 pj_str_t digits;
03873                 int call = current_call;
03874                 pj_status_t status;
03875 
03876                 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf, 
03877                                   sizeof(buf)))
03878                 {
03879                         break;
03880                 }
03881 
03882                 if (call != current_call) {
03883                     puts("Call has been disconnected");
03884                     continue;
03885                 }
03886 
03887                 digits = pj_str(buf);
03888                 status = pjsua_call_dial_dtmf(current_call, &digits);
03889                 if (status != PJ_SUCCESS) {
03890                     pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
03891                 } else {
03892                     puts("DTMF digits enqueued for transmission");
03893                 }
03894             }
03895             break;
03896 
03897         case '*':
03898             /* Send DTMF with INFO */
03899             if (current_call == -1) {
03900                 
03901                 PJ_LOG(3,(THIS_FILE, "No current call"));
03902 
03903             } else {
03904                 const pj_str_t SIP_INFO = pj_str("INFO");
03905                 pj_str_t digits;
03906                 int call = current_call;
03907                 int i;
03908                 pj_status_t status;
03909 
03910                 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf, 
03911                                   sizeof(buf)))
03912                 {
03913                         break;
03914                 }
03915 
03916                 if (call != current_call) {
03917                     puts("Call has been disconnected");
03918                     continue;
03919                 }
03920 
03921                 digits = pj_str(buf);
03922                 for (i=0; i<digits.slen; ++i) {
03923                     pjsua_msg_data msg_data;
03924                     char body[80];
03925 
03926                     pjsua_msg_data_init(&msg_data);
03927                     msg_data.content_type = pj_str("application/dtmf-relay");
03928                     
03929                     pj_ansi_snprintf(body, sizeof(body),
03930                                      "Signal=%c\r\n"
03931                                      "Duration=160",
03932                                      buf[i]);
03933                     msg_data.msg_body = pj_str(body);
03934 
03935                     status = pjsua_call_send_request(current_call, &SIP_INFO, 
03936                                                      &msg_data);
03937                     if (status != PJ_SUCCESS) {
03938                         break;
03939                     }
03940                 }
03941             }
03942             break;
03943 
03944         case 'S':
03945             /*
03946              * Send arbitrary request
03947              */
03948             if (pjsua_acc_get_count() == 0) {
03949                 puts("Sorry, need at least one account configured");
03950                 break;
03951             }
03952 
03953             puts("Send arbitrary request to remote host");
03954 
03955             /* Input METHOD */
03956             if (!simple_input("Request method:",text,sizeof(text)))
03957                 break;
03958 
03959             /* Input destination URI */
03960             uri = NULL;
03961             ui_input_url("Destination URI", buf, sizeof(buf), &result);
03962             if (result.nb_result != NO_NB) {
03963 
03964                 if (result.nb_result == -1) {
03965                     puts("Sorry you can't do that!");
03966                     continue;
03967                 } else if (result.nb_result == 0) {
03968                     uri = NULL;
03969                     if (current_call == PJSUA_INVALID_ID) {
03970                         puts("No current call");
03971                         continue;
03972                     }
03973                 } else {
03974                     pjsua_buddy_info binfo;
03975                     pjsua_buddy_get_info(result.nb_result-1, &binfo);
03976                     tmp.ptr = buf;
03977                     pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
03978                     uri = buf;
03979                 }
03980 
03981             } else if (result.uri_result) {
03982                 uri = result.uri_result;
03983             }
03984             
03985             if (uri) {
03986                 tmp = pj_str(uri);
03987                 send_request(text, &tmp);
03988             } else {
03989                 /* If you send call control request using this method
03990                  * (such requests includes BYE, CANCEL, etc.), it will
03991                  * not go well with the call state, so don't do it
03992                  * unless it's for testing.
03993                  */
03994                 pj_str_t method = pj_str(text);
03995                 pjsua_call_send_request(current_call, &method, NULL);
03996             }
03997             break;
03998 
03999         case 'e':
04000             if (pj_ansi_strnicmp(menuin, "echo", 4)==0) {
04001                 pj_str_t tmp;
04002 
04003                 tmp.ptr = menuin+5;
04004                 tmp.slen = pj_ansi_strlen(menuin)-6;
04005 
04006                 if (tmp.slen < 1) {
04007                     puts("Usage: echo [0|1]");
04008                     break;
04009                 }
04010 
04011                 cmd_echo = *tmp.ptr != '0' || tmp.slen!=1;
04012             }
04013             break;
04014 
04015         case 's':
04016             if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
04017                 pj_str_t tmp;
04018                 int delay;
04019 
04020                 tmp.ptr = menuin+6;
04021                 tmp.slen = pj_ansi_strlen(menuin)-7;
04022 
04023                 if (tmp.slen < 1) {
04024                     puts("Usage: sleep MSEC");
04025                     break;
04026                 }
04027 
04028                 delay = pj_strtoul(&tmp);
04029                 if (delay < 0) delay = 0;
04030                 pj_thread_sleep(delay);
04031                 break;
04032             }
04033             /* Continue below */
04034 
04035         case 'u':
04036             /*
04037              * Subscribe/unsubscribe presence.
04038              */
04039             ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
04040             if (result.nb_result != NO_NB) {
04041                 if (result.nb_result == -1) {
04042                     int i, count;
04043                     count = pjsua_get_buddy_count();
04044                     for (i=0; i<count; ++i)
04045                         pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
04046                 } else if (result.nb_result == 0) {
04047                     puts("Sorry, can only subscribe to buddy's presence, "
04048                          "not from existing call");
04049                 } else {
04050                     pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
04051                 }
04052 
04053             } else if (result.uri_result) {
04054                 puts("Sorry, can only subscribe to buddy's presence, "
04055                      "not arbitrary URL (for now)");
04056             }
04057 
04058             break;
04059 
04060         case 'r':
04061             switch (menuin[1]) {
04062             case 'r':
04063                 /*
04064                  * Re-Register.
04065                  */
04066                 pjsua_acc_set_registration(current_acc, PJ_TRUE);
04067                 break;
04068             case 'u':
04069                 /*
04070                  * Unregister
04071                  */
04072                 pjsua_acc_set_registration(current_acc, PJ_FALSE);
04073                 break;
04074             }
04075             break;
04076             
04077         case 't':
04078             pjsua_acc_get_info(current_acc, &acc_info);
04079             acc_info.online_status = !acc_info.online_status;
04080             pjsua_acc_set_online_status(current_acc, acc_info.online_status);
04081             printf("Setting %s online status to %s\n",
04082                    acc_info.acc_uri.ptr,
04083                    (acc_info.online_status?"online":"offline"));
04084             break;
04085 
04086         case 'T':
04087             change_online_status();
04088             break;
04089 
04090         case 'c':
04091             switch (menuin[1]) {
04092             case 'l':
04093                 conf_list();
04094                 break;
04095             case 'c':
04096             case 'd':
04097                 {
04098                     char tmp[10], src_port[10], dst_port[10];
04099                     pj_status_t status;
04100                     int cnt;
04101                     const char *src_title, *dst_title;
04102 
04103                     cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
04104 
04105                     if (cnt != 3) {
04106                         conf_list();
04107 
04108                         src_title = (menuin[1]=='c'?
04109                                      "Connect src port #":
04110                                      "Disconnect src port #");
04111                         dst_title = (menuin[1]=='c'?
04112                                      "To dst port #":
04113                                      "From dst port #");
04114 
04115                         if (!simple_input(src_title, src_port, sizeof(src_port)))
04116                             break;
04117 
04118                         if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
04119                             break;
04120                     }
04121 
04122                     if (menuin[1]=='c') {
04123                         status = pjsua_conf_connect(my_atoi(src_port), 
04124                                                     my_atoi(dst_port));
04125                     } else {
04126                         status = pjsua_conf_disconnect(my_atoi(src_port), 
04127                                                        my_atoi(dst_port));
04128                     }
04129                     if (status == PJ_SUCCESS) {
04130                         puts("Success");
04131                     } else {
04132                         puts("ERROR!!");
04133                     }
04134                 }
04135                 break;
04136             }
04137             break;
04138 
04139         case 'V':
04140             /* Adjust audio volume */
04141             sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
04142             if (simple_input(buf,text,sizeof(text))) {
04143                 char *err;
04144                 app_config.mic_level = (float)strtod(text, &err);
04145                 pjsua_conf_adjust_rx_level(0, app_config.mic_level);
04146             }
04147             sprintf(buf, "Adjust speaker level: [%4.1fx] ", 
04148                     app_config.speaker_level);
04149             if (simple_input(buf,text,sizeof(text))) {
04150                 char *err;
04151                 app_config.speaker_level = (float)strtod(text, &err);
04152                 pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
04153             }
04154 
04155             break;
04156 
04157         case 'd':
04158             if (menuin[1] == 'c') {
04159                 char settings[2000];
04160                 int len;
04161 
04162                 len = write_settings(&app_config, settings, sizeof(settings));
04163                 if (len < 1)
04164                     PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
04165                 else
04166                     PJ_LOG(3,(THIS_FILE, 
04167                               "Dumping configuration (%d bytes):\n%s\n",
04168                               len, settings));
04169 
04170             } else if (menuin[1] == 'q') {
04171 
04172                 if (current_call != PJSUA_INVALID_ID) {
04173                     log_call_dump(current_call);
04174                 } else {
04175                     PJ_LOG(3,(THIS_FILE, "No current call"));
04176                 }
04177 
04178             } else {
04179                 app_dump(menuin[1]=='d');
04180             }
04181             break;
04182 
04183 
04184         case 'f':
04185             if (simple_input("Enter output filename", buf, sizeof(buf))) {
04186                 char settings[2000];
04187                 int len;
04188 
04189                 len = write_settings(&app_config, settings, sizeof(settings));
04190                 if (len < 1)
04191                     PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
04192                 else {
04193                     pj_oshandle_t fd;
04194                     pj_status_t status;
04195 
04196                     status = pj_file_open(app_config.pool, buf, 
04197                                           PJ_O_WRONLY, &fd);
04198                     if (status != PJ_SUCCESS) {
04199                         pjsua_perror(THIS_FILE, "Unable to open file", status);
04200                     } else {
04201                         pj_ssize_t size = len;
04202                         pj_file_write(fd, settings, &size);
04203                         pj_file_close(fd);
04204 
04205                         printf("Settings successfully written to '%s'\n", buf);
04206                     }
04207                 }
04208                 
04209             }
04210             break;
04211 
04212 
04213         case 'L':   /* Restart */
04214             app_restart = PJ_TRUE;
04215             /* Continues below */
04216 
04217         case 'q':
04218             goto on_exit;
04219 
04220         case 'R':
04221             if (!pjsua_call_is_active(current_call)) {
04222                 PJ_LOG(1,(THIS_FILE, "Call %d has gone", current_call));
04223             } else if (menuin[1] == 'a') {
04224                 pjsua_call_process_redirect(current_call, 
04225                                             PJSIP_REDIRECT_ACCEPT);
04226             } else if (menuin[1] == 'r') {
04227                 pjsua_call_process_redirect(current_call,
04228                                             PJSIP_REDIRECT_REJECT);
04229             } else {
04230                 pjsua_call_process_redirect(current_call,
04231                                             PJSIP_REDIRECT_STOP);
04232             }
04233             break;
04234 
04235         default:
04236             if (menuin[0] != '\n' && menuin[0] != '\r') {
04237                 printf("Invalid input %s", menuin);
04238             }
04239             keystroke_help();
04240             break;
04241         }
04242     }
04243 
04244 on_exit:
04245     ;
04246 }
04247 
04248 /*****************************************************************************
04249  * A simple module to handle otherwise unhandled request. We will register
04250  * this with the lowest priority.
04251  */
04252 
04253 /* Notification on incoming request */
04254 static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata)
04255 {
04256     pjsip_tx_data *tdata;
04257     pjsip_status_code status_code;
04258     pj_status_t status;
04259 
04260     /* Don't respond to ACK! */
04261     if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, 
04262                          &pjsip_ack_method) == 0)
04263         return PJ_TRUE;
04264 
04265     /* Create basic response. */
04266     if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, 
04267                          &pjsip_notify_method) == 0)
04268     {
04269         /* Unsolicited NOTIFY's, send with Bad Request */
04270         status_code = PJSIP_SC_BAD_REQUEST;
04271     } else {
04272         /* Probably unknown method */
04273         status_code = PJSIP_SC_METHOD_NOT_ALLOWED;
04274     }
04275     status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(), 
04276                                          rdata, status_code, 
04277                                          NULL, &tdata);
04278     if (status != PJ_SUCCESS) {
04279         pjsua_perror(THIS_FILE, "Unable to create response", status);
04280         return PJ_TRUE;
04281     }
04282 
04283     /* Add Allow if we're responding with 405 */
04284     if (status_code == PJSIP_SC_METHOD_NOT_ALLOWED) {
04285         const pjsip_hdr *cap_hdr;
04286         cap_hdr = pjsip_endpt_get_capability(pjsua_get_pjsip_endpt(), 
04287                                              PJSIP_H_ALLOW, NULL);
04288         if (cap_hdr) {
04289             pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, 
04290                                                            cap_hdr));
04291         }
04292     }
04293 
04294     /* Add User-Agent header */
04295     {
04296         pj_str_t user_agent;
04297         char tmp[80];
04298         const pj_str_t USER_AGENT = { "User-Agent", 10};
04299         pjsip_hdr *h;
04300 
04301         pj_ansi_snprintf(tmp, sizeof(tmp), "PJSUA v%s/%s", 
04302                          pj_get_version(), PJ_OS_NAME);
04303         pj_strdup2_with_null(tdata->pool, &user_agent, tmp);
04304 
04305         h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
04306                                                          &USER_AGENT,
04307                                                          &user_agent);
04308         pjsip_msg_add_hdr(tdata->msg, h);
04309     }
04310 
04311     pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(), rdata, tdata, 
04312                                NULL, NULL);
04313 
04314     return PJ_TRUE;
04315 }
04316 
04317 
04318 /* The module instance. */
04319 static pjsip_module mod_default_handler = 
04320 {
04321     NULL, NULL,                         /* prev, next.          */
04322     { "mod-default-handler", 19 },      /* Name.                */
04323     -1,                                 /* Id                   */
04324     PJSIP_MOD_PRIORITY_APPLICATION+99,  /* Priority             */
04325     NULL,                               /* load()               */
04326     NULL,                               /* start()              */
04327     NULL,                               /* stop()               */
04328     NULL,                               /* unload()             */
04329     &default_mod_on_rx_request,         /* on_rx_request()      */
04330     NULL,                               /* on_rx_response()     */
04331     NULL,                               /* on_tx_request.       */
04332     NULL,                               /* on_tx_response()     */
04333     NULL,                               /* on_tsx_state()       */
04334 
04335 };
04336 
04337 
04338 
04339 
04340 /*****************************************************************************
04341  * Public API
04342  */
04343 
04344 pj_status_t app_init(int argc, char *argv[])
04345 {
04346     pjsua_transport_id transport_id = -1;
04347     pjsua_transport_config tcp_cfg;
04348     unsigned i;
04349     pj_status_t status;
04350 
04351     app_restart = PJ_FALSE;
04352 
04353     /* Create pjsua */
04354     status = pjsua_create();
04355     if (status != PJ_SUCCESS)
04356         return status;
04357 
04358     /* Create pool for application */
04359     app_config.pool = pjsua_pool_create("pjsua-app", 1000, 1000);
04360 
04361     /* Initialize default config */
04362     default_config(&app_config);
04363     
04364     /* Parse the arguments */
04365     status = parse_args(argc, argv, &app_config, &uri_arg);
04366     if (status != PJ_SUCCESS)
04367         return status;
04368 
04369     /* Initialize application callbacks */
04370     app_config.cfg.cb.on_call_state = &on_call_state;
04371     app_config.cfg.cb.on_call_media_state = &on_call_media_state;
04372     app_config.cfg.cb.on_incoming_call = &on_incoming_call;
04373     app_config.cfg.cb.on_call_tsx_state = &on_call_tsx_state;
04374     app_config.cfg.cb.on_dtmf_digit = &call_on_dtmf_callback;
04375     app_config.cfg.cb.on_call_redirected = &call_on_redirected;
04376     app_config.cfg.cb.on_reg_state = &on_reg_state;
04377     app_config.cfg.cb.on_incoming_subscribe = &on_incoming_subscribe;
04378     app_config.cfg.cb.on_buddy_state = &on_buddy_state;
04379     app_config.cfg.cb.on_pager = &on_pager;
04380     app_config.cfg.cb.on_typing = &on_typing;
04381     app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
04382     app_config.cfg.cb.on_call_replaced = &on_call_replaced;
04383     app_config.cfg.cb.on_nat_detect = &on_nat_detect;
04384     app_config.cfg.cb.on_mwi_info = &on_mwi_info;
04385 
04386     /* Set sound device latency */
04387     if (app_config.capture_lat > 0)
04388         app_config.media_cfg.snd_rec_latency = app_config.capture_lat;
04389     if (app_config.playback_lat)
04390         app_config.media_cfg.snd_play_latency = app_config.playback_lat;
04391 
04392     /* Initialize pjsua */
04393     status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
04394                         &app_config.media_cfg);
04395     if (status != PJ_SUCCESS)
04396         return status;
04397 
04398     /* Initialize our module to handle otherwise unhandled request */
04399     status = pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
04400                                          &mod_default_handler);
04401     if (status != PJ_SUCCESS)
04402         return status;
04403 
04404 #ifdef STEREO_DEMO
04405     stereo_demo();
04406 #endif
04407 
04408     /* Initialize calls data */
04409     for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
04410         app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
04411         app_config.call_data[i].timer.cb = &call_timeout_callback;
04412     }
04413 
04414     /* Optionally registers WAV file */
04415     for (i=0; i<app_config.wav_count; ++i) {
04416         pjsua_player_id wav_id;
04417         unsigned play_options = 0;
04418 
04419         if (app_config.auto_play_hangup)
04420             play_options |= PJMEDIA_FILE_NO_LOOP;
04421 
04422         status = pjsua_player_create(&app_config.wav_files[i], play_options, 
04423                                      &wav_id);
04424         if (status != PJ_SUCCESS)
04425             goto on_error;
04426 
04427         if (app_config.wav_id == PJSUA_INVALID_ID) {
04428             app_config.wav_id = wav_id;
04429             app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
04430             if (app_config.auto_play_hangup) {
04431                 pjmedia_port *port;
04432 
04433                 pjsua_player_get_port(app_config.wav_id, &port);
04434                 status = pjmedia_wav_player_set_eof_cb(port, NULL, 
04435                                                        &on_playfile_done);
04436                 if (status != PJ_SUCCESS)
04437                     goto on_error;
04438 
04439                 pj_timer_entry_init(&app_config.auto_hangup_timer, 0, NULL, 
04440                                     &hangup_timeout_callback);
04441             }
04442         }
04443     }
04444 
04445     /* Optionally registers tone players */
04446     for (i=0; i<app_config.tone_count; ++i) {
04447         pjmedia_port *tport;
04448         char name[80];
04449         pj_str_t label;
04450         pj_status_t status;
04451 
04452         pj_ansi_snprintf(name, sizeof(name), "tone-%d,%d",
04453                          app_config.tones[i].freq1, 
04454                          app_config.tones[i].freq2);
04455         label = pj_str(name);
04456         status = pjmedia_tonegen_create2(app_config.pool, &label,
04457                                          8000, 1, 160, 16, 
04458                                          PJMEDIA_TONEGEN_LOOP,  &tport);
04459         if (status != PJ_SUCCESS) {
04460             pjsua_perror(THIS_FILE, "Unable to create tone generator", status);
04461             goto on_error;
04462         }
04463 
04464         status = pjsua_conf_add_port(app_config.pool, tport, 
04465                                      &app_config.tone_slots[i]);
04466         pj_assert(status == PJ_SUCCESS);
04467 
04468         status = pjmedia_tonegen_play(tport, 1, &app_config.tones[i], 0);
04469         pj_assert(status == PJ_SUCCESS);
04470     }
04471 
04472     /* Optionally create recorder file, if any. */
04473     if (app_config.rec_file.slen) {
04474         status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
04475                                        &app_config.rec_id);
04476         if (status != PJ_SUCCESS)
04477             goto on_error;
04478 
04479         app_config.rec_port = pjsua_recorder_get_conf_port(app_config.rec_id);
04480     }
04481 
04482     pj_memcpy(&tcp_cfg, &app_config.udp_cfg, sizeof(tcp_cfg));
04483 
04484     /* Create ringback tones */
04485     if (app_config.no_tones == PJ_FALSE) {
04486         unsigned i, samples_per_frame;
04487         pjmedia_tone_desc tone[RING_CNT+RINGBACK_CNT];
04488         pj_str_t name;
04489 
04490         samples_per_frame = app_config.media_cfg.audio_frame_ptime * 
04491                             app_config.media_cfg.clock_rate *
04492                             app_config.media_cfg.channel_count / 1000;
04493 
04494         /* Ringback tone (call is ringing) */
04495         name = pj_str("ringback");
04496         status = pjmedia_tonegen_create2(app_config.pool, &name, 
04497                                          app_config.media_cfg.clock_rate,
04498                                          app_config.media_cfg.channel_count, 
04499                                          samples_per_frame,
04500                                          16, PJMEDIA_TONEGEN_LOOP, 
04501                                          &app_config.ringback_port);
04502         if (status != PJ_SUCCESS)
04503             goto on_error;
04504 
04505         pj_bzero(&tone, sizeof(tone));
04506         for (i=0; i<RINGBACK_CNT; ++i) {
04507             tone[i].freq1 = RINGBACK_FREQ1;
04508             tone[i].freq2 = RINGBACK_FREQ2;
04509             tone[i].on_msec = RINGBACK_ON;
04510             tone[i].off_msec = RINGBACK_OFF;
04511         }
04512         tone[RINGBACK_CNT-1].off_msec = RINGBACK_INTERVAL;
04513 
04514         pjmedia_tonegen_play(app_config.ringback_port, RINGBACK_CNT, tone,
04515                              PJMEDIA_TONEGEN_LOOP);
04516 
04517 
04518         status = pjsua_conf_add_port(app_config.pool, app_config.ringback_port,
04519                                      &app_config.ringback_slot);
04520         if (status != PJ_SUCCESS)
04521             goto on_error;
04522 
04523         /* Ring (to alert incoming call) */
04524         name = pj_str("ring");
04525         status = pjmedia_tonegen_create2(app_config.pool, &name, 
04526                                          app_config.media_cfg.clock_rate,
04527                                          app_config.media_cfg.channel_count, 
04528                                          samples_per_frame,
04529                                          16, PJMEDIA_TONEGEN_LOOP, 
04530                                          &app_config.ring_port);
04531         if (status != PJ_SUCCESS)
04532             goto on_error;
04533 
04534         for (i=0; i<RING_CNT; ++i) {
04535             tone[i].freq1 = RING_FREQ1;
04536             tone[i].freq2 = RING_FREQ2;
04537             tone[i].on_msec = RING_ON;
04538             tone[i].off_msec = RING_OFF;
04539         }
04540         tone[RING_CNT-1].off_msec = RING_INTERVAL;
04541 
04542         pjmedia_tonegen_play(app_config.ring_port, RING_CNT, 
04543                              tone, PJMEDIA_TONEGEN_LOOP);
04544 
04545         status = pjsua_conf_add_port(app_config.pool, app_config.ring_port,
04546                                      &app_config.ring_slot);
04547         if (status != PJ_SUCCESS)
04548             goto on_error;
04549 
04550     }
04551 
04552     /* Add UDP transport unless it's disabled. */
04553     if (!app_config.no_udp) {
04554         pjsua_acc_id aid;
04555         pjsip_transport_type_e type = PJSIP_TRANSPORT_UDP;
04556 
04557         if (app_config.ipv6)
04558             type = PJSIP_TRANSPORT_UDP6;
04559 
04560         status = pjsua_transport_create(type,
04561                                         &app_config.udp_cfg, 
04562                                         &transport_id);
04563         if (status != PJ_SUCCESS)
04564             goto on_error;
04565 
04566         /* Add local account */
04567         pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
04568         //pjsua_acc_set_transport(aid, transport_id);
04569         pjsua_acc_set_online_status(current_acc, PJ_TRUE);
04570 
04571         if (app_config.udp_cfg.port == 0) {
04572             pjsua_transport_info ti;
04573             pj_sockaddr_in *a;
04574 
04575             pjsua_transport_get_info(transport_id, &ti);
04576             a = (pj_sockaddr_in*)&ti.local_addr;
04577 
04578             tcp_cfg.port = pj_ntohs(a->sin_port);
04579         }
04580     }
04581 
04582     /* Add TCP transport unless it's disabled */
04583     if (!app_config.no_tcp) {
04584         status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
04585                                         &tcp_cfg, 
04586                                         &transport_id);
04587         if (status != PJ_SUCCESS)
04588             goto on_error;
04589 
04590         /* Add local account */
04591         pjsua_acc_add_local(transport_id, PJ_TRUE, NULL);
04592         pjsua_acc_set_online_status(current_acc, PJ_TRUE);
04593 
04594     }
04595 
04596 
04597 #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
04598     /* Add TLS transport when application wants one */
04599     if (app_config.use_tls) {
04600 
04601         pjsua_acc_id acc_id;
04602 
04603         /* Set TLS port as TCP port+1 */
04604         tcp_cfg.port++;
04605         status = pjsua_transport_create(PJSIP_TRANSPORT_TLS,
04606                                         &tcp_cfg, 
04607                                         &transport_id);
04608         tcp_cfg.port--;
04609         if (status != PJ_SUCCESS)
04610             goto on_error;
04611         
04612         /* Add local account */
04613         pjsua_acc_add_local(transport_id, PJ_FALSE, &acc_id);
04614         pjsua_acc_set_online_status(acc_id, PJ_TRUE);
04615     }
04616 #endif
04617 
04618     if (transport_id == -1) {
04619         PJ_LOG(1,(THIS_FILE, "Error: no transport is configured"));
04620         status = -1;
04621         goto on_error;
04622     }
04623 
04624 
04625     /* Add accounts */
04626     for (i=0; i<app_config.acc_cnt; ++i) {
04627         status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
04628         if (status != PJ_SUCCESS)
04629             goto on_error;
04630         pjsua_acc_set_online_status(current_acc, PJ_TRUE);
04631     }
04632 
04633     /* Add buddies */
04634     for (i=0; i<app_config.buddy_cnt; ++i) {
04635         status = pjsua_buddy_add(&app_config.buddy_cfg[i], NULL);
04636         if (status != PJ_SUCCESS)
04637             goto on_error;
04638     }
04639 
04640     /* Optionally set codec orders */
04641     for (i=0; i<app_config.codec_cnt; ++i) {
04642         pjsua_codec_set_priority(&app_config.codec_arg[i],
04643                                  (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
04644     }
04645 
04646     /* Optionally disable some codec */
04647     for (i=0; i<app_config.codec_dis_cnt; ++i) {
04648         pjsua_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED);
04649     }
04650 
04651     /* Add RTP transports */
04652 #ifdef TRANSPORT_ADAPTER_SAMPLE
04653     status = transport_adapter_sample();
04654 
04655 #else
04656     if (app_config.ipv6)
04657         status = create_ipv6_media_transports();
04658     else
04659         status = pjsua_media_transports_create(&app_config.rtp_cfg);
04660 #endif
04661     if (status != PJ_SUCCESS)
04662         goto on_error;
04663 
04664     /* Use null sound device? */
04665 #ifndef STEREO_DEMO
04666     if (app_config.null_audio) {
04667         status = pjsua_set_null_snd_dev();
04668         if (status != PJ_SUCCESS)
04669             return status;
04670     }
04671 #endif
04672 
04673     if (app_config.capture_dev  != PJSUA_INVALID_ID ||
04674         app_config.playback_dev != PJSUA_INVALID_ID) 
04675     {
04676         status = pjsua_set_snd_dev(app_config.capture_dev, 
04677                                    app_config.playback_dev);
04678         if (status != PJ_SUCCESS)
04679             goto on_error;
04680     }
04681 
04682     return PJ_SUCCESS;
04683 
04684 on_error:
04685     app_destroy();
04686     return status;
04687 }
04688 
04689 
04690 static int stdout_refresh_proc(void *arg)
04691 {
04692     PJ_UNUSED_ARG(arg);
04693 
04694     /* Set thread to lowest priority so that it doesn't clobber
04695      * stdout output
04696      */
04697     pj_thread_set_prio(pj_thread_this(), 
04698                        pj_thread_get_prio_min(pj_thread_this()));
04699 
04700     while (!stdout_refresh_quit) {
04701         pj_thread_sleep(stdout_refresh * 1000);
04702         puts(stdout_refresh_text);
04703         fflush(stdout);
04704     }
04705 
04706     return 0;
04707 }
04708 
04709 pj_status_t app_main(void)
04710 {
04711     pj_thread_t *stdout_refresh_thread = NULL;
04712     pj_status_t status;
04713 
04714     /* Start pjsua */
04715     status = pjsua_start();
04716     if (status != PJ_SUCCESS) {
04717         app_destroy();
04718         return status;
04719     }
04720 
04721     /* Start console refresh thread */
04722     if (stdout_refresh > 0) {
04723         pj_thread_create(app_config.pool, "stdout", &stdout_refresh_proc,
04724                          NULL, 0, 0, &stdout_refresh_thread);
04725     }
04726 
04727     console_app_main(&uri_arg);
04728 
04729     if (stdout_refresh_thread) {
04730         stdout_refresh_quit = PJ_TRUE;
04731         pj_thread_join(stdout_refresh_thread);
04732         pj_thread_destroy(stdout_refresh_thread);
04733     }
04734 
04735     return PJ_SUCCESS;
04736 }
04737 
04738 pj_status_t app_destroy(void)
04739 {
04740     pj_status_t status;
04741     unsigned i;
04742 
04743 #ifdef STEREO_DEMO
04744     if (app_config.snd) {
04745         pjmedia_snd_port_destroy(app_config.snd);
04746         app_config.snd = NULL;
04747     }
04748     if (app_config.sc_ch1) {
04749         pjsua_conf_remove_port(app_config.sc_ch1_slot);
04750         app_config.sc_ch1_slot = PJSUA_INVALID_ID;
04751         pjmedia_port_destroy(app_config.sc_ch1);
04752         app_config.sc_ch1 = NULL;
04753     }
04754     if (app_config.sc) {
04755         pjmedia_port_destroy(app_config.sc);
04756         app_config.sc = NULL;
04757     }
04758 #endif
04759 
04760     /* Close ringback port */
04761     if (app_config.ringback_port && 
04762         app_config.ringback_slot != PJSUA_INVALID_ID) 
04763     {
04764         pjsua_conf_remove_port(app_config.ringback_slot);
04765         app_config.ringback_slot = PJSUA_INVALID_ID;
04766         pjmedia_port_destroy(app_config.ringback_port);
04767         app_config.ringback_port = NULL;
04768     }
04769 
04770     /* Close ring port */
04771     if (app_config.ring_port && app_config.ring_slot != PJSUA_INVALID_ID) {
04772         pjsua_conf_remove_port(app_config.ring_slot);
04773         app_config.ring_slot = PJSUA_INVALID_ID;
04774         pjmedia_port_destroy(app_config.ring_port);
04775         app_config.ring_port = NULL;
04776     }
04777 
04778     /* Close tone generators */
04779     for (i=0; i<app_config.tone_count; ++i) {
04780         pjsua_conf_remove_port(app_config.tone_slots[i]);
04781     }
04782 
04783     if (app_config.pool) {
04784         pj_pool_release(app_config.pool);
04785         app_config.pool = NULL;
04786     }
04787 
04788     status = pjsua_destroy();
04789 
04790     pj_bzero(&app_config, sizeof(app_config));
04791 
04792     return status;
04793 }
04794 
04795 
04796 #ifdef STEREO_DEMO
04797 /*
04798  * In this stereo demo, we open the sound device in stereo mode and
04799  * arrange the attachment to the PJSUA-LIB conference bridge as such
04800  * so that channel0/left channel of the sound device corresponds to
04801  * slot 0 in the bridge, and channel1/right channel of the sound
04802  * device corresponds to slot 1 in the bridge. Then user can independently
04803  * feed different media to/from the speakers/microphones channels, by
04804  * connecting them to slot 0 or 1 respectively.
04805  *
04806  * Here's how the connection looks like:
04807  *
04808    +-----------+ stereo +-----------------+ 2x mono +-----------+
04809    | AUDIO DEV |<------>| SPLITCOMB   left|<------->|#0  BRIDGE |
04810    +-----------+        |            right|<------->|#1         |
04811                         +-----------------+         +-----------+
04812  */
04813 static void stereo_demo()
04814 {
04815     pjmedia_port *conf;
04816     pj_status_t status;
04817 
04818     /* Disable existing sound device */
04819     conf = pjsua_set_no_snd_dev();
04820 
04821     /* Create stereo-mono splitter/combiner */
04822     status = pjmedia_splitcomb_create(app_config.pool, 
04823                                       conf->info.clock_rate /* clock rate */,
04824                                       2     /* stereo */,
04825                                       2 * conf->info.samples_per_frame,
04826                                       conf->info.bits_per_sample,
04827                                       0     /* options */,
04828                                       &app_config.sc);
04829     pj_assert(status == PJ_SUCCESS);
04830 
04831     /* Connect channel0 (left channel?) to conference port slot0 */
04832     status = pjmedia_splitcomb_set_channel(app_config.sc, 0 /* ch0 */, 
04833                                            0 /*options*/,
04834                                            conf);
04835     pj_assert(status == PJ_SUCCESS);
04836 
04837     /* Create reverse channel for channel1 (right channel?)... */
04838     status = pjmedia_splitcomb_create_rev_channel(app_config.pool,
04839                                                   app_config.sc,
04840                                                   1  /* ch1 */,
04841                                                   0  /* options */,
04842                                                   &app_config.sc_ch1);
04843     pj_assert(status == PJ_SUCCESS);
04844 
04845     /* .. and register it to conference bridge (it would be slot1
04846      * if there's no other devices connected to the bridge)
04847      */
04848     status = pjsua_conf_add_port(app_config.pool, app_config.sc_ch1, 
04849                                  &app_config.sc_ch1_slot);
04850     pj_assert(status == PJ_SUCCESS);
04851     
04852     /* Create sound device */
04853     status = pjmedia_snd_port_create(app_config.pool, -1, -1, 
04854                                      conf->info.clock_rate,
04855                                      2      /* stereo */,
04856                                      2 * conf->info.samples_per_frame,
04857                                      conf->info.bits_per_sample,
04858                                      0, &app_config.snd);
04859     pj_assert(status == PJ_SUCCESS);
04860 
04861 
04862     /* Connect the splitter to the sound device */
04863     status = pjmedia_snd_port_connect(app_config.snd, app_config.sc);
04864     pj_assert(status == PJ_SUCCESS);
04865 
04866 }
04867 #endif
04868 
04869 #ifdef TRANSPORT_ADAPTER_SAMPLE
04870 static pj_status_t create_transport_adapter(pjmedia_endpt *med_endpt, int port,
04871                                             pjmedia_transport **p_tp)
04872 {
04873     pjmedia_transport *udp;
04874     pj_status_t status;
04875 
04876     /* Create the UDP media transport */
04877     status = pjmedia_transport_udp_create(med_endpt, NULL, port, 0, &udp);
04878     if (status != PJ_SUCCESS)
04879         return status;
04880 
04881     /* Create the adapter */
04882     status = pjmedia_tp_adapter_create(med_endpt, NULL, udp, p_tp);
04883     if (status != PJ_SUCCESS) {
04884         pjmedia_transport_close(udp);
04885         return status;
04886     }
04887 
04888     return PJ_SUCCESS;
04889 }
04890 
04891 static pj_status_t transport_adapter_sample(void)
04892 {
04893     pjsua_media_transport tp[PJSUA_MAX_CALLS];
04894     pj_status_t status;
04895     int port = 7000;
04896     unsigned i;
04897 
04898     for (i=0; i<app_config.cfg.max_calls; ++i) {
04899         status = create_transport_adapter(pjsua_get_pjmedia_endpt(), 
04900                                           port + i*10,
04901                                           &tp[i].transport);
04902         if (status != PJ_SUCCESS)
04903             return status;
04904     }
04905 
04906     return pjsua_media_transports_attach(tp, i, PJ_TRUE);
04907 }
04908 #endif
04909 
04910 static pj_status_t create_ipv6_media_transports(void)
04911 {
04912     pjsua_media_transport tp[PJSUA_MAX_CALLS];
04913     pj_status_t status;
04914     int port = app_config.rtp_cfg.port;
04915     unsigned i;
04916 
04917     for (i=0; i<app_config.cfg.max_calls; ++i) {
04918         enum { MAX_RETRY = 10 };
04919         pj_sock_t sock[2];
04920         pjmedia_sock_info si;
04921         unsigned j;
04922 
04923         /* Get rid of uninitialized var compiler warning with MSVC */
04924         status = PJ_SUCCESS;
04925 
04926         for (j=0; j<MAX_RETRY; ++j) {
04927             unsigned k;
04928 
04929             for (k=0; k<2; ++k) {
04930                 pj_sockaddr bound_addr;
04931 
04932                 status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0, &sock[k]);
04933                 if (status != PJ_SUCCESS)
04934                     break;
04935 
04936                 status = pj_sockaddr_init(pj_AF_INET6(), &bound_addr,
04937                                           &app_config.rtp_cfg.bound_addr, 
04938                                           (unsigned short)(port+k));
04939                 if (status != PJ_SUCCESS)
04940                     break;
04941 
04942                 status = pj_sock_bind(sock[k], &bound_addr, 
04943                                       pj_sockaddr_get_len(&bound_addr));
04944                 if (status != PJ_SUCCESS)
04945                     break;
04946             }
04947             if (status != PJ_SUCCESS) {
04948                 if (k==1)
04949                     pj_sock_close(sock[0]);
04950 
04951                 if (port != 0)
04952                     port += 10;
04953                 else
04954                     break;
04955 
04956                 continue;
04957             }
04958 
04959             pj_bzero(&si, sizeof(si));
04960             si.rtp_sock = sock[0];
04961             si.rtcp_sock = sock[1];
04962         
04963             pj_sockaddr_init(pj_AF_INET6(), &si.rtp_addr_name, 
04964                              &app_config.rtp_cfg.public_addr, 
04965                              (unsigned short)(port));
04966             pj_sockaddr_init(pj_AF_INET6(), &si.rtcp_addr_name, 
04967                              &app_config.rtp_cfg.public_addr, 
04968                              (unsigned short)(port+1));
04969 
04970             status = pjmedia_transport_udp_attach(pjsua_get_pjmedia_endpt(),
04971                                                   NULL,
04972                                                   &si,
04973                                                   0,
04974                                                   &tp[i].transport);
04975             if (port != 0)
04976                 port += 10;
04977             else
04978                 break;
04979 
04980             if (status == PJ_SUCCESS)
04981                 break;
04982         }
04983 
04984         if (status != PJ_SUCCESS) {
04985             pjsua_perror(THIS_FILE, "Error creating IPv6 UDP media transport", 
04986                          status);
04987             for (j=0; j<i; ++j) {
04988                 pjmedia_transport_close(tp[j].transport);
04989             }
04990             return status;
04991         }
04992     }
04993 
04994     return pjsua_media_transports_attach(tp, i, PJ_TRUE);
04995 }

 


PJSIP Open Source, high performance, small footprint, and very very portable SIP stack
Copyright (C) 2006-2008 Teluu Inc.