Ignore:
Timestamp:
Feb 7, 2006 6:48:01 PM (18 years ago)
Author:
bennylp
Message:

Tested initial implementation: basic UAC, client registration, authentication, etc

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/src/pjsua/main.c

    r139 r141  
    1818 */ 
    1919#include "pjsua.h" 
     20#include "getopt.h" 
     21 
    2022 
    2123/* For debugging, disable threading. */ 
     
    3335 * Notify UI when invite state has changed. 
    3436 */ 
    35 void ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) 
     37void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) 
    3638{ 
    3739    const char *state_names[] = 
     
    5153    PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s", state_names[inv->state])); 
    5254 
    53     if (inv->state == PJSIP_INV_STATE_DISCONNECTED || 
    54         inv->state == PJSIP_INV_STATE_TERMINATED) 
    55     { 
     55    if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { 
    5656        if (inv == inv_session) 
    5757            inv_session = NULL; 
     
    104104            } 
    105105 
    106 #if 0 
     106#if 1 
    107107            printf("Enter URL to call: "); 
    108108            fgets(buf, sizeof(buf), stdin); 
     
    122122 
    123123            pjsua_invite(buf, &inv); 
     124 
     125#else 
     126 
     127            pjsua_invite("sip:localhost:5061", &inv); 
    124128#endif 
    125  
    126             pjsua_invite("sip:localhost:5061", &inv); 
    127129            break; 
    128130 
     
    163165} 
    164166 
     167 
     168/***************************************************************************** 
     169 * This is a very simple PJSIP module, whose sole purpose is to display 
     170 * incoming and outgoing messages to log. This module will have priority 
     171 * higher than transport layer, which means: 
     172 * 
     173 *  - incoming messages will come to this module first before reaching 
     174 *    transaction layer. 
     175 * 
     176 *  - outgoing messages will come to this module last, after the message 
     177 *    has been 'printed' to contiguous buffer by transport layer and 
     178 *    appropriate transport instance has been decided for this message. 
     179 * 
     180 */ 
     181 
     182/* Notification on incoming messages */ 
    165183static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata) 
    166184{ 
     
    174192                         rdata->msg_info.msg_buf)); 
    175193     
    176     /* Must return false for logger! */ 
     194    /* Always return false, otherwise messages will not get processed! */ 
    177195    return PJ_FALSE; 
    178196} 
    179197 
     198/* Notification on outgoing messages */ 
    180199static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata) 
    181200{ 
     201     
     202    /* Important note: 
     203     *  tp_info field is only valid after outgoing messages has passed 
     204     *  transport layer. So don't try to access tp_info when the module 
     205     *  has lower priority than transport layer. 
     206     */ 
     207 
    182208    PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n" 
    183209                         "%s\n" 
     
    189215                         tdata->buf.start)); 
    190216 
     217    /* Always return success, otherwise message will not get sent! */ 
    191218    return PJ_SUCCESS; 
    192219} 
    193220 
     221/* The module instance. */ 
    194222static pjsip_module console_msg_logger =  
    195223{ 
     
    212240 
    213241 
    214 int main() 
     242 
     243/***************************************************************************** 
     244 * Console application custom logging: 
     245 */ 
     246 
     247 
     248static FILE *log_file; 
     249 
     250 
     251static void app_log_writer(int level, const char *buffer, int len) 
     252{ 
     253    /* Write to both stdout and file. */ 
     254 
     255    if (level <= pjsua.app_log_level) 
     256        pj_log_write(level, buffer, len); 
     257 
     258    if (log_file) { 
     259        fwrite(buffer, len, 1, log_file); 
     260        fflush(log_file); 
     261    } 
     262} 
     263 
     264 
     265void app_logging_init(void) 
     266{ 
     267    /* Redirect log function to ours */ 
     268 
     269    pj_log_set_log_func( &app_log_writer ); 
     270 
     271    /* If output log file is desired, create the file: */ 
     272 
     273    if (pjsua.log_filename) 
     274        log_file = fopen(pjsua.log_filename, "wt"); 
     275} 
     276 
     277 
     278void app_logging_shutdown(void) 
     279{ 
     280    /* Close logging file, if any: */ 
     281 
     282    if (log_file) { 
     283        fclose(log_file); 
     284        log_file = NULL; 
     285    } 
     286} 
     287 
     288/***************************************************************************** 
     289 * Command line argument processing: 
     290 */ 
     291 
     292 
     293/* Show usage */ 
     294static void usage(void) 
     295{ 
     296    puts("Usage:"); 
     297    puts("  pjsua [options] [sip-url]"); 
     298    puts(""); 
     299    puts("  [sip-url]   Default URL to invite."); 
     300    puts(""); 
     301    puts("General options:"); 
     302    puts("  --config-file=file  Read the config/arguments from file."); 
     303    puts("  --log-file=fname    Log to filename (default stderr)"); 
     304    puts("  --log-level=N       Set log max level to N (0(none) to 6(trace))"); 
     305    puts("  --app-log-level=N   Set log max level for stdout display to N"); 
     306    puts("  --help              Display this help screen"); 
     307    puts("  --version           Display version info"); 
     308    puts(""); 
     309    puts("Media options:"); 
     310    puts("  --null-audio        Use NULL audio device"); 
     311    puts(""); 
     312    puts("User Agent options:"); 
     313    puts("  --auto-answer=sec   Auto-answer all incoming calls after sec seconds."); 
     314    puts("  --auto-hangup=sec   Auto-hangup all calls after sec seconds."); 
     315    puts(""); 
     316    puts("SIP options:"); 
     317    puts("  --local-port=port   Set TCP/UDP port"); 
     318    puts("  --id=url            Set the URL of local ID (used in From header)"); 
     319    puts("  --contact=url       Override the Contact information"); 
     320    puts("  --proxy=url         Set the URL of proxy server"); 
     321    puts("  --outbound=url      Set the URL of outbound proxy server"); 
     322    puts("  --registrar=url     Set the URL of registrar server"); 
     323    puts("  --reg-timeout=secs  Set registration interval to secs (default 3600)"); 
     324    puts(""); 
     325    puts("Authentication options:"); 
     326    puts("  --realm=string      Set realm"); 
     327    puts("  --username=string   Set authentication username"); 
     328    puts("  --password=string   Set authentication password"); 
     329    puts(""); 
     330    puts("STUN options (all must be specified):"); 
     331    puts("  --use-stun1=host[:port]"); 
     332    puts("  --use-stun2=host[:port]  Use STUN and set host name and port of STUN servers"); 
     333    puts(""); 
     334    puts("SIMPLE options (may be specified more than once):"); 
     335    puts("  --add-buddy url     Add the specified URL to the buddy list."); 
     336    puts("  --offer-x-ms-msg    Offer \"x-ms-message\" in outgoing INVITE"); 
     337    puts("  --no-presence       Do not subscribe presence of buddies"); 
     338    puts(""); 
     339    fflush(stdout); 
     340} 
     341 
     342 
     343/* 
     344 * Verify that valid SIP url is given. 
     345 */ 
     346static pj_status_t verify_sip_url(char *url) 
     347{ 
     348    pjsip_uri *p; 
     349    pj_pool_t *pool; 
     350    int len = (url ? strlen(url) : 0); 
     351 
     352    if (!len) return -1; 
     353 
     354    pool = pj_pool_create(&pjsua.cp.factory, "check%p", 1024, 0, NULL); 
     355    if (!pool) return -1; 
     356 
     357    p = pjsip_parse_uri(pool, url, len, 0); 
     358    if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0) 
     359        p = NULL; 
     360 
     361    pj_pool_release(pool); 
     362    return p ? 0 : -1; 
     363} 
     364 
     365 
     366/* 
     367 * Read command arguments from config file. 
     368 */ 
     369static int read_config_file(pj_pool_t *pool, const char *filename,  
     370                            int *app_argc, char ***app_argv) 
     371{ 
     372    int i; 
     373    FILE *fhnd; 
     374    char line[200]; 
     375    int argc = 0; 
     376    char **argv; 
     377    enum { MAX_ARGS = 64 }; 
     378 
     379    /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */ 
     380    argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*)); 
     381    argv[argc++] = *app_argv[0]; 
     382 
     383    /* Open config file. */ 
     384    fhnd = fopen(filename, "rt"); 
     385    if (!fhnd) { 
     386        printf("Unable to open config file %s\n", filename); 
     387        return -1; 
     388    } 
     389 
     390    /* Scan tokens in the file. */ 
     391    while (argc < MAX_ARGS && !feof(fhnd)) { 
     392        char *token, *p = line; 
     393 
     394        if (fgets(line, sizeof(line), fhnd) == NULL) break; 
     395 
     396        for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS;  
     397             token = strtok(NULL, " \t\r\n")) 
     398        { 
     399            int token_len; 
     400             
     401            if (!token) break; 
     402            if (*token == '#') break; 
     403 
     404            token_len = strlen(token); 
     405            if (!token_len) 
     406                continue; 
     407            argv[argc] = pj_pool_alloc(pool, token_len+1); 
     408            pj_memcpy(argv[argc], token, token_len+1); 
     409            ++argc; 
     410        } 
     411    } 
     412 
     413    /* Copy arguments from command line */ 
     414    for (i=1; i<*app_argc && argc < MAX_ARGS; ++i) 
     415        argv[argc++] = (*app_argv)[i]; 
     416 
     417    if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) { 
     418        printf("Too many arguments specified in cmd line/config file\n"); 
     419        fclose(fhnd); 
     420        return -1; 
     421    } 
     422 
     423    fclose(fhnd); 
     424 
     425    /* Assign the new command line back to the original command line. */ 
     426    *app_argc = argc; 
     427    *app_argv = argv; 
     428    return 0; 
     429 
     430} 
     431 
     432 
     433/* Parse arguments. */ 
     434static pj_status_t parse_args(int argc, char *argv[]) 
     435{ 
     436    int c; 
     437    int option_index; 
     438    enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,  
     439           OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, 
     440           OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, 
     441           OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT,  
     442           OPT_REALM, OPT_USERNAME, OPT_PASSWORD, 
     443           OPT_USE_STUN1, OPT_USE_STUN2,  
     444           OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, 
     445           OPT_AUTO_ANSWER, OPT_AUTO_HANGUP}; 
     446    struct option long_options[] = { 
     447        { "config-file",1, 0, OPT_CONFIG_FILE}, 
     448        { "log-file",   1, 0, OPT_LOG_FILE}, 
     449        { "log-level",  1, 0, OPT_LOG_LEVEL}, 
     450        { "app-log-level",1,0,OPT_APP_LOG_LEVEL}, 
     451        { "help",       0, 0, OPT_HELP}, 
     452        { "version",    0, 0, OPT_VERSION}, 
     453        { "null-audio", 0, 0, OPT_NULL_AUDIO}, 
     454        { "local-port", 1, 0, OPT_LOCAL_PORT}, 
     455        { "proxy",      1, 0, OPT_PROXY}, 
     456        { "outbound",   1, 0, OPT_OUTBOUND_PROXY}, 
     457        { "registrar",  1, 0, OPT_REGISTRAR}, 
     458        { "reg-timeout",1, 0, OPT_REG_TIMEOUT}, 
     459        { "id",         1, 0, OPT_ID}, 
     460        { "contact",    1, 0, OPT_CONTACT}, 
     461        { "realm",      1, 0, OPT_REALM}, 
     462        { "username",   1, 0, OPT_USERNAME}, 
     463        { "password",   1, 0, OPT_PASSWORD}, 
     464        { "use-stun1",  1, 0, OPT_USE_STUN1}, 
     465        { "use-stun2",  1, 0, OPT_USE_STUN2}, 
     466        { "add-buddy",  1, 0, OPT_ADD_BUDDY}, 
     467        { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG}, 
     468        { "no-presence", 0, 0, OPT_NO_PRESENCE}, 
     469        { "auto-answer",1, 0, OPT_AUTO_ANSWER}, 
     470        { "auto-hangup",1, 0, OPT_AUTO_HANGUP}, 
     471        { NULL, 0, 0, 0} 
     472    }; 
     473    pj_status_t status; 
     474    char *config_file = NULL; 
     475 
     476    /* Run getopt once to see if user specifies config file to read. */ 
     477    while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { 
     478        switch (c) { 
     479        case OPT_CONFIG_FILE: 
     480            config_file = optarg; 
     481            break; 
     482        } 
     483        if (config_file) 
     484            break; 
     485    } 
     486 
     487    if (config_file) { 
     488        status = read_config_file(pjsua.pool, config_file, &argc, &argv); 
     489        if (status != 0) 
     490            return status; 
     491    } 
     492 
     493 
     494    /* Reinitialize and re-run getopt again, possibly with new arguments 
     495     * read from config file. 
     496     */ 
     497    optind = 0; 
     498    while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { 
     499        char *p; 
     500        pj_str_t tmp; 
     501        long lval; 
     502 
     503        switch (c) { 
     504 
     505        case OPT_LOG_FILE: 
     506            pjsua.log_filename = optarg; 
     507            break; 
     508 
     509        case OPT_LOG_LEVEL: 
     510            c = pj_strtoul(pj_cstr(&tmp, optarg)); 
     511            if (c < 0 || c > 6) { 
     512                printf("Error: expecting integer value 0-6 for --log-level\n"); 
     513                return PJ_EINVAL; 
     514            } 
     515            pj_log_set_level( c ); 
     516            break; 
     517 
     518        case OPT_APP_LOG_LEVEL: 
     519            pjsua.app_log_level = pj_strtoul(pj_cstr(&tmp, optarg)); 
     520            if (pjsua.app_log_level < 0 || pjsua.app_log_level > 6) { 
     521                printf("Error: expecting integer value 0-6 for --app-log-level\n"); 
     522                return PJ_EINVAL; 
     523            } 
     524            break; 
     525 
     526        case OPT_HELP: 
     527            usage(); 
     528            return PJ_EINVAL; 
     529 
     530        case OPT_VERSION:   /* version */ 
     531            pj_dump_config(); 
     532            return PJ_EINVAL; 
     533 
     534        case OPT_NULL_AUDIO: 
     535            pjsua.null_audio = 1; 
     536            break; 
     537 
     538        case OPT_LOCAL_PORT:   /* local-port */ 
     539            lval = pj_strtoul(pj_cstr(&tmp, optarg)); 
     540            if (lval < 1 || lval > 65535) { 
     541                printf("Error: expecting integer value for --local-port\n"); 
     542                return PJ_EINVAL; 
     543            } 
     544            pjsua.sip_port = (pj_uint16_t)lval; 
     545            break; 
     546 
     547        case OPT_PROXY:   /* proxy */ 
     548            if (verify_sip_url(optarg) != 0) { 
     549                printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg); 
     550                return PJ_EINVAL; 
     551            } 
     552            pjsua.proxy = pj_str(optarg); 
     553            break; 
     554 
     555        case OPT_OUTBOUND_PROXY:   /* outbound proxy */ 
     556            if (verify_sip_url(optarg) != 0) { 
     557                printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg); 
     558                return PJ_EINVAL; 
     559            } 
     560            pjsua.outbound_proxy = pj_str(optarg); 
     561            break; 
     562 
     563        case OPT_REGISTRAR:   /* registrar */ 
     564            if (verify_sip_url(optarg) != 0) { 
     565                printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg); 
     566                return PJ_EINVAL; 
     567            } 
     568            pjsua.registrar_uri = pj_str(optarg); 
     569            break; 
     570 
     571        case OPT_REG_TIMEOUT:   /* reg-timeout */ 
     572            pjsua.reg_timeout = pj_strtoul(pj_cstr(&tmp,optarg)); 
     573            if (pjsua.reg_timeout < 1 || pjsua.reg_timeout > 3600) { 
     574                printf("Error: invalid value for --reg-timeout (expecting 1-3600)\n"); 
     575                return PJ_EINVAL; 
     576            } 
     577            break; 
     578 
     579        case OPT_ID:   /* id */ 
     580            if (verify_sip_url(optarg) != 0) { 
     581                printf("Error: invalid SIP URL '%s' in local id argument\n", optarg); 
     582                return PJ_EINVAL; 
     583            } 
     584            pjsua.local_uri = pj_str(optarg); 
     585            break; 
     586 
     587        case OPT_CONTACT:   /* contact */ 
     588            if (verify_sip_url(optarg) != 0) { 
     589                printf("Error: invalid SIP URL '%s' in contact argument\n", optarg); 
     590                return PJ_EINVAL; 
     591            } 
     592            pjsua.contact_uri = pj_str(optarg); 
     593            break; 
     594 
     595        case OPT_USERNAME:   /* Default authentication user */ 
     596            if (!pjsua.cred_count) pjsua.cred_count = 1; 
     597            pjsua.cred_info[0].username = pj_str(optarg); 
     598            break; 
     599 
     600        case OPT_REALM:     /* Default authentication realm. */ 
     601            if (!pjsua.cred_count) pjsua.cred_count = 1; 
     602            pjsua.cred_info[0].realm = pj_str(optarg); 
     603            break; 
     604 
     605        case OPT_PASSWORD:   /* authentication password */ 
     606            if (!pjsua.cred_count) pjsua.cred_count = 1; 
     607            pjsua.cred_info[0].data_type = 0; 
     608            pjsua.cred_info[0].data = pj_str(optarg); 
     609            break; 
     610 
     611        case OPT_USE_STUN1:   /* STUN server 1 */ 
     612            p = pj_native_strchr(optarg, ':'); 
     613            if (p) { 
     614                *p = '\0'; 
     615                pjsua.stun_srv1 = pj_str(optarg); 
     616                pjsua.stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1)); 
     617                if (pjsua.stun_port1 < 1 || pjsua.stun_port1 > 65535) { 
     618                    printf("Error: expecting port number with option --use-stun1\n"); 
     619                    return PJ_EINVAL; 
     620                } 
     621            } else { 
     622                pjsua.stun_port1 = 3478; 
     623                pjsua.stun_srv1 = pj_str(optarg); 
     624            } 
     625            break; 
     626 
     627        case OPT_USE_STUN2:   /* STUN server 2 */ 
     628            p = pj_native_strchr(optarg, ':'); 
     629            if (p) { 
     630                *p = '\0'; 
     631                pjsua.stun_srv2 = pj_str(optarg); 
     632                pjsua.stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1)); 
     633                if (pjsua.stun_port2 < 1 || pjsua.stun_port2 > 65535) { 
     634                    printf("Error: expecting port number with option --use-stun2\n"); 
     635                    return PJ_EINVAL; 
     636                } 
     637            } else { 
     638                pjsua.stun_port2 = 3478; 
     639                pjsua.stun_srv2 = pj_str(optarg); 
     640            } 
     641            break; 
     642        } 
     643    } 
     644 
     645    if (optind != argc) { 
     646        printf("Error: unknown options %s\n", argv[optind]); 
     647        return PJ_EINVAL; 
     648    } 
     649 
     650    if (pjsua.reg_timeout == 0) 
     651        pjsua.reg_timeout = 3600; 
     652 
     653 
     654    return PJ_SUCCESS; 
     655} 
     656 
     657 
     658 
     659/***************************************************************************** 
     660 * main(): 
     661 */ 
     662int main(int argc, char *argv[]) 
    215663{ 
    216664    /* Init default settings. */ 
     
    224672 
    225673 
    226     /* Initialize pjsua. 
    227      * This will start worker thread, client registration, etc. 
     674    /* Initialize pjsua (to create pool etc). 
    228675     */ 
    229676 
    230677    if (pjsua_init() != PJ_SUCCESS) 
    231678        return 1; 
     679 
     680 
     681    /* Parse command line arguments: */ 
     682 
     683    if (parse_args(argc, argv) != PJ_SUCCESS) 
     684        return 1; 
     685 
     686 
     687    /* Init logging: */ 
     688 
     689    app_logging_init(); 
     690 
    232691 
    233692    /* Register message logger to print incoming and outgoing 
     
    238697 
    239698 
     699    /* Start pjsua! */ 
     700 
     701    if (pjsua_start() != PJ_SUCCESS) { 
     702 
     703        pjsua_destroy(); 
     704        return 1; 
     705    } 
     706 
     707 
    240708    /* Sleep for a while, let any messages get printed to console: */ 
    241709 
     
    252720    pjsua_destroy(); 
    253721 
     722 
     723    /* Close logging: */ 
     724 
     725    app_logging_shutdown(); 
     726 
     727 
    254728    /* Exit... */ 
    255729 
Note: See TracChangeset for help on using the changeset viewer.