~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjsip-apps/src/samples/sipecho.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: sipecho.c 4148 2012-05-31 12:21:59Z bennylp $ */
 
2
/* 
 
3
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 
4
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
19
 */
 
20
 
 
21
 
 
22
/**
 
23
 * sipecho.c
 
24
 *
 
25
 * - Accepts incoming calls and echoes back SDP and any media.
 
26
 * - Specify URI in cmdline argument to make call
 
27
 * - Accepts registration too!
 
28
 */
 
29
 
 
30
/* Include all headers. */
 
31
#include <pjsip.h>
 
32
#include <pjmedia/sdp.h>
 
33
#include <pjsip_ua.h>
 
34
#include <pjlib-util.h>
 
35
#include <pjlib.h>
 
36
 
 
37
/* For logging purpose. */
 
38
#define THIS_FILE   "sipecho.c"
 
39
 
 
40
#include "util.h"
 
41
 
 
42
 
 
43
/* Settings */
 
44
#define MAX_CALLS       8
 
45
 
 
46
typedef struct call_t
 
47
{
 
48
    pjsip_inv_session   *inv;
 
49
} call_t;
 
50
 
 
51
static struct app_t
 
52
{
 
53
    pj_caching_pool      cp;
 
54
    pj_pool_t           *pool;
 
55
 
 
56
    pjsip_endpoint      *sip_endpt;
 
57
    //pjmedia_endpt     *med_endpt;
 
58
 
 
59
    call_t               call[MAX_CALLS];
 
60
 
 
61
    pj_bool_t            quit;
 
62
    pj_thread_t         *worker_thread;
 
63
 
 
64
    pj_bool_t            enable_msg_logging;
 
65
} app;
 
66
 
 
67
/*
 
68
 * Prototypes:
 
69
 */
 
70
 
 
71
static void call_on_media_update(pjsip_inv_session *inv, pj_status_t status);
 
72
static void call_on_state_changed(pjsip_inv_session *inv, pjsip_event *e);
 
73
static void call_on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_session *offer);
 
74
static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
 
75
static pj_bool_t on_rx_request( pjsip_rx_data *rdata );
 
76
 
 
77
/* Globals */
 
78
static int sip_af;
 
79
static int sip_port = 5060;
 
80
static pj_bool_t sip_tcp;
 
81
 
 
82
/* This is a PJSIP module to be registered by application to handle
 
83
 * incoming requests outside any dialogs/transactions. The main purpose
 
84
 * here is to handle incoming INVITE request message, where we will
 
85
 * create a dialog and INVITE session for it.
 
86
 */
 
87
static pjsip_module mod_sipecho =
 
88
{
 
89
    NULL, NULL,                     /* prev, next.              */
 
90
    { "mod-sipecho", 11 },          /* Name.                    */
 
91
    -1,                             /* Id                       */
 
92
    PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
 
93
    NULL,                           /* load()                   */
 
94
    NULL,                           /* start()                  */
 
95
    NULL,                           /* stop()                   */
 
96
    NULL,                           /* unload()                 */
 
97
    &on_rx_request,                 /* on_rx_request()          */
 
98
    NULL,                           /* on_rx_response()         */
 
99
    NULL,                           /* on_tx_request.           */
 
100
    NULL,                           /* on_tx_response()         */
 
101
    NULL,                           /* on_tsx_state()           */
 
102
};
 
103
 
 
104
/* Notification on incoming messages */
 
105
static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
 
106
{
 
107
    if (!app.enable_msg_logging)
 
108
        return PJ_FALSE;
 
109
 
 
110
    PJ_LOG(3,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
 
111
                         "%.*s\n"
 
112
                         "--end msg--",
 
113
                         rdata->msg_info.len,
 
114
                         pjsip_rx_data_get_info(rdata),
 
115
                         rdata->tp_info.transport->type_name,
 
116
                         rdata->pkt_info.src_name,
 
117
                         rdata->pkt_info.src_port,
 
118
                         (int)rdata->msg_info.len,
 
119
                         rdata->msg_info.msg_buf));
 
120
    return PJ_FALSE;
 
121
}
 
122
 
 
123
/* Notification on outgoing messages */
 
124
static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
 
125
{
 
126
    if (!app.enable_msg_logging)
 
127
        return PJ_SUCCESS;
 
128
 
 
129
    PJ_LOG(3,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
 
130
                         "%.*s\n"
 
131
                         "--end msg--",
 
132
                         (tdata->buf.cur - tdata->buf.start),
 
133
                         pjsip_tx_data_get_info(tdata),
 
134
                         tdata->tp_info.transport->type_name,
 
135
                         tdata->tp_info.dst_name,
 
136
                         tdata->tp_info.dst_port,
 
137
                         (int)(tdata->buf.cur - tdata->buf.start),
 
138
                         tdata->buf.start));
 
139
    return PJ_SUCCESS;
 
140
}
 
141
 
 
142
/* The module instance. */
 
143
static pjsip_module msg_logger =
 
144
{
 
145
    NULL, NULL,                         /* prev, next.          */
 
146
    { "mod-msg-log", 13 },              /* Name.                */
 
147
    -1,                                 /* Id                   */
 
148
    PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority            */
 
149
    NULL,                               /* load()               */
 
150
    NULL,                               /* start()              */
 
151
    NULL,                               /* stop()               */
 
152
    NULL,                               /* unload()             */
 
153
    &logging_on_rx_msg,                 /* on_rx_request()      */
 
154
    &logging_on_rx_msg,                 /* on_rx_response()     */
 
155
    &logging_on_tx_msg,                 /* on_tx_request.       */
 
156
    &logging_on_tx_msg,                 /* on_tx_response()     */
 
157
    NULL,                               /* on_tsx_state()       */
 
158
 
 
159
};
 
160
 
 
161
static int worker_proc(void *arg)
 
162
{
 
163
    PJ_UNUSED_ARG(arg);
 
164
 
 
165
    while (!app.quit) {
 
166
        pj_time_val interval = { 0, 20 };
 
167
        pjsip_endpt_handle_events(app.sip_endpt, &interval);
 
168
    }
 
169
 
 
170
    return 0;
 
171
}
 
172
 
 
173
static void hangup_all(void)
 
174
{
 
175
    unsigned i;
 
176
    for (i=0; i<MAX_CALLS; ++i) {
 
177
        call_t *call = &app.call[i];
 
178
 
 
179
        if (call->inv && call->inv->state <= PJSIP_INV_STATE_CONFIRMED) {
 
180
            pj_status_t status;
 
181
            pjsip_tx_data *tdata;
 
182
 
 
183
            status = pjsip_inv_end_session(call->inv, PJSIP_SC_BUSY_HERE, NULL, &tdata);
 
184
            if (status==PJ_SUCCESS && tdata)
 
185
                pjsip_inv_send_msg(call->inv, tdata);
 
186
        }
 
187
    }
 
188
}
 
189
 
 
190
static void destroy_stack(void)
 
191
{
 
192
    enum { WAIT_CLEAR = 5000, WAIT_INTERVAL = 500 };
 
193
    unsigned i;
 
194
 
 
195
    PJ_LOG(3,(THIS_FILE, "Shutting down.."));
 
196
 
 
197
    /* Wait until all clear */
 
198
    hangup_all();
 
199
    for (i=0; i<WAIT_CLEAR/WAIT_INTERVAL; ++i) {
 
200
        unsigned j;
 
201
 
 
202
        for (j=0; j<MAX_CALLS; ++j) {
 
203
            call_t *call = &app.call[j];
 
204
            if (call->inv && call->inv->state <= PJSIP_INV_STATE_CONFIRMED)
 
205
                break;
 
206
        }
 
207
 
 
208
        if (j==MAX_CALLS)
 
209
            return;
 
210
 
 
211
        pj_thread_sleep(WAIT_INTERVAL);
 
212
    }
 
213
 
 
214
    app.quit = PJ_TRUE;
 
215
    if (app.worker_thread) {
 
216
        pj_thread_join(app.worker_thread);
 
217
        app.worker_thread = NULL;
 
218
    }
 
219
 
 
220
    //if (app.med_endpt)
 
221
        //pjmedia_endpt_destroy(app.med_endpt);
 
222
 
 
223
    if (app.sip_endpt)
 
224
        pjsip_endpt_destroy(app.sip_endpt);
 
225
 
 
226
    if (app.pool)
 
227
        pj_pool_release(app.pool);
 
228
 
 
229
    dump_pool_usage(THIS_FILE, &app.cp);
 
230
    pj_caching_pool_destroy(&app.cp);
 
231
}
 
232
 
 
233
#define CHECK_STATUS()  do { if (status != PJ_SUCCESS) return status; } while (0)
 
234
 
 
235
static pj_status_t init_stack()
 
236
{
 
237
    pj_sockaddr addr;
 
238
    pjsip_inv_callback inv_cb;
 
239
    pj_status_t status;
 
240
 
 
241
    pj_log_set_level(3);
 
242
 
 
243
    status = pjlib_util_init();
 
244
    CHECK_STATUS();
 
245
 
 
246
    pj_caching_pool_init(&app.cp, NULL, 0);
 
247
    app.pool = pj_pool_create( &app.cp.factory, "sipecho", 512, 512, 0);
 
248
 
 
249
    status = pjsip_endpt_create(&app.cp.factory, NULL, &app.sip_endpt);
 
250
    CHECK_STATUS();
 
251
 
 
252
    pj_log_set_level(4);
 
253
    pj_sockaddr_init((pj_uint16_t)sip_af, &addr, NULL, (pj_uint16_t)sip_port);
 
254
    if (sip_af == pj_AF_INET()) {
 
255
        if (sip_tcp) {
 
256
            status = pjsip_tcp_transport_start( app.sip_endpt, &addr.ipv4, 1,
 
257
                                                NULL);
 
258
        } else {
 
259
            status = pjsip_udp_transport_start( app.sip_endpt, &addr.ipv4,
 
260
                                                NULL, 1, NULL);
 
261
        }
 
262
    } else if (sip_af == pj_AF_INET6()) {
 
263
            status = pjsip_udp_transport_start6(app.sip_endpt, &addr.ipv6,
 
264
                                                NULL, 1, NULL);
 
265
    } else {
 
266
        status = PJ_EAFNOTSUP;
 
267
    }
 
268
 
 
269
    pj_log_set_level(3);
 
270
    CHECK_STATUS();
 
271
 
 
272
    status = pjsip_tsx_layer_init_module(app.sip_endpt) ||
 
273
             pjsip_ua_init_module( app.sip_endpt, NULL );
 
274
    CHECK_STATUS();
 
275
 
 
276
    pj_bzero(&inv_cb, sizeof(inv_cb));
 
277
    inv_cb.on_state_changed = &call_on_state_changed;
 
278
    inv_cb.on_new_session = &call_on_forked;
 
279
    inv_cb.on_media_update = &call_on_media_update;
 
280
    inv_cb.on_rx_offer = &call_on_rx_offer;
 
281
 
 
282
    status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb) ||
 
283
             pjsip_100rel_init_module(app.sip_endpt) ||
 
284
             pjsip_endpt_register_module( app.sip_endpt, &mod_sipecho) ||
 
285
             pjsip_endpt_register_module( app.sip_endpt, &msg_logger) ||
 
286
             //pjmedia_endpt_create(&app.cp.factory,
 
287
                //                pjsip_endpt_get_ioqueue(app.sip_endpt),
 
288
                //                0, &app.med_endpt) ||
 
289
             pj_thread_create(app.pool, "sipecho", &worker_proc, NULL, 0, 0,
 
290
                              &app.worker_thread);
 
291
    CHECK_STATUS();
 
292
 
 
293
    return PJ_SUCCESS;
 
294
}
 
295
 
 
296
static void destroy_call(call_t *call)
 
297
{
 
298
    call->inv = NULL;
 
299
}
 
300
 
 
301
static pjmedia_sdp_attr * find_remove_sdp_attrs(unsigned *cnt,
 
302
                                                pjmedia_sdp_attr *attr[],
 
303
                                                unsigned cnt_attr_to_remove,
 
304
                                                const char* attr_to_remove[])
 
305
{
 
306
    pjmedia_sdp_attr *found_attr = NULL;
 
307
    int i;
 
308
 
 
309
    for (i=0; i<(int)*cnt; ++i) {
 
310
        unsigned j;
 
311
        for (j=0; j<cnt_attr_to_remove; ++j) {
 
312
            if (pj_strcmp2(&attr[i]->name, attr_to_remove[j])==0) {
 
313
                if (!found_attr) found_attr = attr[i];
 
314
                pj_array_erase(attr, sizeof(attr[0]), *cnt, i);
 
315
                --(*cnt);
 
316
                --i;
 
317
                break;
 
318
            }
 
319
        }
 
320
    }
 
321
 
 
322
    return found_attr;
 
323
}
 
324
 
 
325
static pjmedia_sdp_session *create_answer(int call_num, pj_pool_t *pool,
 
326
                                          const pjmedia_sdp_session *offer)
 
327
{
 
328
    const char* dir_attrs[] = { "sendrecv", "sendonly", "recvonly", "inactive" };
 
329
    const char *ice_attrs[] = {"ice-pwd", "ice-ufrag", "candidate"};
 
330
    pjmedia_sdp_session *answer = pjmedia_sdp_session_clone(pool, offer);
 
331
    pjmedia_sdp_attr *sess_dir_attr = NULL;
 
332
    unsigned mi;
 
333
 
 
334
    PJ_LOG(3,(THIS_FILE, "Call %d: creating answer:", call_num));
 
335
 
 
336
    answer->name = pj_str("sipecho");
 
337
    sess_dir_attr = find_remove_sdp_attrs(&answer->attr_count, answer->attr,
 
338
                                          PJ_ARRAY_SIZE(dir_attrs),
 
339
                                          dir_attrs);
 
340
 
 
341
    for (mi=0; mi<answer->media_count; ++mi) {
 
342
        pjmedia_sdp_media *m = answer->media[mi];
 
343
        pjmedia_sdp_attr *m_dir_attr;
 
344
        pjmedia_sdp_attr *dir_attr;
 
345
        const char *our_dir = NULL;
 
346
        pjmedia_sdp_conn *c;
 
347
 
 
348
        /* Match direction */
 
349
        m_dir_attr = find_remove_sdp_attrs(&m->attr_count, m->attr,
 
350
                                           PJ_ARRAY_SIZE(dir_attrs),
 
351
                                           dir_attrs);
 
352
        dir_attr = m_dir_attr ? m_dir_attr : sess_dir_attr;
 
353
 
 
354
        if (dir_attr) {
 
355
            if (pj_strcmp2(&dir_attr->name, "sendonly")==0)
 
356
                our_dir = "recvonly";
 
357
            else if (pj_strcmp2(&dir_attr->name, "inactive")==0)
 
358
                our_dir = "inactive";
 
359
            else if (pj_strcmp2(&dir_attr->name, "recvonly")==0)
 
360
                our_dir = "inactive";
 
361
 
 
362
            if (our_dir) {
 
363
                dir_attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
 
364
                dir_attr->name = pj_str((char*)our_dir);
 
365
                m->attr[m->attr_count++] = dir_attr;
 
366
            }
 
367
        }
 
368
 
 
369
        /* Remove ICE attributes */
 
370
        find_remove_sdp_attrs(&m->attr_count, m->attr, PJ_ARRAY_SIZE(ice_attrs), ice_attrs);
 
371
 
 
372
        /* Done */
 
373
        c = m->conn ? m->conn : answer->conn;
 
374
        PJ_LOG(3,(THIS_FILE, "  Media %d, %.*s: %s <--> %.*s:%d",
 
375
                  mi, (int)m->desc.media.slen, m->desc.media.ptr,
 
376
                  (our_dir ? our_dir : "sendrecv"),
 
377
                  (int)c->addr.slen, c->addr.ptr, m->desc.port));
 
378
    }
 
379
 
 
380
    return answer;
 
381
}
 
382
 
 
383
static void call_on_state_changed( pjsip_inv_session *inv, 
 
384
                                   pjsip_event *e)
 
385
{
 
386
    call_t *call = (call_t*)inv->mod_data[mod_sipecho.id];
 
387
    if (!call)
 
388
        return;
 
389
 
 
390
    PJ_UNUSED_ARG(e);
 
391
    if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
 
392
        PJ_LOG(3,(THIS_FILE, "Call %d: DISCONNECTED [reason=%d (%s)]",
 
393
                  call - app.call, inv->cause,
 
394
                  pjsip_get_status_text(inv->cause)->ptr));
 
395
        destroy_call(call);
 
396
    } else {
 
397
        PJ_LOG(3,(THIS_FILE, "Call %d: state changed to %s",
 
398
                  call - app.call, pjsip_inv_state_name(inv->state)));
 
399
    }
 
400
}
 
401
 
 
402
static void call_on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_session *offer)
 
403
{
 
404
    call_t *call = (call_t*) inv->mod_data[mod_sipecho.id];
 
405
    pjsip_inv_set_sdp_answer(inv, create_answer(call - app.call, inv->pool_prov, offer));
 
406
}
 
407
 
 
408
static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
 
409
{
 
410
    PJ_UNUSED_ARG(inv);
 
411
    PJ_UNUSED_ARG(e);
 
412
}
 
413
 
 
414
static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
 
415
{
 
416
    pj_sockaddr hostaddr;
 
417
    char temp[80], hostip[PJ_INET6_ADDRSTRLEN];
 
418
    pj_str_t local_uri;
 
419
    pjsip_dialog *dlg;
 
420
    pjsip_rdata_sdp_info *sdp_info;
 
421
    pjmedia_sdp_session *answer = NULL;
 
422
    pjsip_tx_data *tdata = NULL;
 
423
    call_t *call = NULL;
 
424
    unsigned i;
 
425
    pj_status_t status;
 
426
 
 
427
    PJ_LOG(3,(THIS_FILE, "RX %.*s from %s",
 
428
              (int)rdata->msg_info.msg->line.req.method.name.slen,
 
429
              rdata->msg_info.msg->line.req.method.name.ptr,
 
430
              rdata->pkt_info.src_name));
 
431
 
 
432
    if (rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD) {
 
433
        /* Let me be a registrar! */
 
434
        pjsip_hdr hdr_list, *h;
 
435
        pjsip_msg *msg;
 
436
        int expires = -1;
 
437
 
 
438
        pj_list_init(&hdr_list);
 
439
        msg = rdata->msg_info.msg;
 
440
        h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
 
441
        if (h) {
 
442
            expires = ((pjsip_expires_hdr*)h)->ivalue;
 
443
            pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h));
 
444
            PJ_LOG(3,(THIS_FILE, " Expires=%d", expires));
 
445
        }
 
446
        if (expires != 0) {
 
447
            h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL);
 
448
            if (h)
 
449
                pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h));
 
450
        }
 
451
 
 
452
        pjsip_endpt_respond(app.sip_endpt, &mod_sipecho, rdata, 200, NULL,
 
453
                            &hdr_list, NULL, NULL);
 
454
        return PJ_TRUE;
 
455
    }
 
456
 
 
457
    if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
 
458
        if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
 
459
            pj_str_t reason = pj_str("Go away");
 
460
            pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
 
461
                                           400, &reason,
 
462
                                           NULL, NULL);
 
463
        }
 
464
        return PJ_TRUE;
 
465
    }
 
466
 
 
467
    sdp_info = pjsip_rdata_get_sdp_info(rdata);
 
468
    if (!sdp_info || !sdp_info->sdp) {
 
469
        pj_str_t reason = pj_str("Require valid offer");
 
470
        pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
 
471
                                       400, &reason,
 
472
                                       NULL, NULL);
 
473
    }
 
474
 
 
475
    for (i=0; i<MAX_CALLS; ++i) {
 
476
        if (app.call[i].inv == NULL) {
 
477
            call = &app.call[i];
 
478
            break;
 
479
        }
 
480
    }
 
481
 
 
482
    if (i==MAX_CALLS) {
 
483
        pj_str_t reason = pj_str("We're full");
 
484
        pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
 
485
                                       PJSIP_SC_BUSY_HERE, &reason,
 
486
                                       NULL, NULL);
 
487
        return PJ_TRUE;
 
488
    }
 
489
 
 
490
    /* Generate Contact URI */
 
491
    status = pj_gethostip(sip_af, &hostaddr);
 
492
    if (status != PJ_SUCCESS) {
 
493
        app_perror(THIS_FILE, "Unable to retrieve local host IP", status);
 
494
        return PJ_TRUE;
 
495
    }
 
496
    pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);
 
497
    pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>", hostip, sip_port);
 
498
    local_uri = pj_str(temp);
 
499
 
 
500
    status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
 
501
                                   &local_uri, &dlg);
 
502
 
 
503
    if (status == PJ_SUCCESS)
 
504
        answer = create_answer(call-app.call, dlg->pool, sdp_info->sdp);
 
505
    if (status == PJ_SUCCESS)
 
506
        status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &call->inv);
 
507
    if (status == PJ_SUCCESS)
 
508
        status = pjsip_inv_initial_answer(call->inv, rdata, 100,
 
509
                                          NULL, NULL, &tdata);
 
510
    if (status == PJ_SUCCESS)
 
511
        status = pjsip_inv_send_msg(call->inv, tdata);
 
512
 
 
513
    if (status == PJ_SUCCESS)
 
514
        status = pjsip_inv_answer(call->inv, 180, NULL,
 
515
                                  NULL, &tdata);
 
516
    if (status == PJ_SUCCESS)
 
517
        status = pjsip_inv_send_msg(call->inv, tdata);
 
518
 
 
519
    if (status == PJ_SUCCESS)
 
520
        status = pjsip_inv_answer(call->inv, 200, NULL,
 
521
                                  NULL, &tdata);
 
522
    if (status == PJ_SUCCESS)
 
523
        status = pjsip_inv_send_msg(call->inv, tdata);
 
524
 
 
525
    if (status != PJ_SUCCESS) {
 
526
        pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
 
527
                                       500, NULL, NULL, NULL);
 
528
        destroy_call(call);
 
529
    } else {
 
530
        call->inv->mod_data[mod_sipecho.id] = call;
 
531
    }
 
532
 
 
533
    return PJ_TRUE;
 
534
}
 
535
 
 
536
static void call_on_media_update( pjsip_inv_session *inv,
 
537
                                  pj_status_t status)
 
538
{
 
539
    PJ_UNUSED_ARG(inv);
 
540
    PJ_UNUSED_ARG(status);
 
541
}
 
542
 
 
543
 
 
544
static void usage()
 
545
{
 
546
    printf("\nUsage: sipecho OPTIONS\n");
 
547
    printf("\n");
 
548
    printf("where OPTIONS:\n");
 
549
    printf("  --local-port, -p PORT        Bind to port PORT.\n");
 
550
    printf("  --tcp, -t                    Listen to TCP instead.\n");
 
551
    printf("  --ipv6, -6                   Use IPv6 instead.\n");
 
552
    printf("  --help, -h                   Show this help page.\n");
 
553
}
 
554
 
 
555
/* main()
 
556
 *
 
557
 * If called with argument, treat argument as SIP URL to be called.
 
558
 * Otherwise wait for incoming calls.
 
559
 */
 
560
int main(int argc, char *argv[])
 
561
{
 
562
    struct pj_getopt_option long_options[] = {
 
563
        { "local-port", 1, 0, 'p' },
 
564
        { "tcp",        0, 0, 't' },
 
565
        { "ipv6",       0, 0, '6' },
 
566
        { "help",       0, 0, 'h' }
 
567
    };
 
568
    int c, option_index;
 
569
 
 
570
    pj_log_set_level(5);
 
571
 
 
572
    pj_init();
 
573
 
 
574
    sip_af = pj_AF_INET();
 
575
 
 
576
    pj_optind = 0;
 
577
    while ((c = pj_getopt_long(argc, argv, "p:t6h", long_options,
 
578
                               &option_index)) != -1)
 
579
    {
 
580
        switch (c) {
 
581
        case 'p':
 
582
            sip_port = atoi(pj_optarg);
 
583
            break;
 
584
        case 't':
 
585
            sip_tcp = PJ_TRUE;
 
586
            break;
 
587
        case 'h':
 
588
            usage();
 
589
            return 0;
 
590
        case '6':
 
591
            sip_af = pj_AF_INET6();
 
592
            break;
 
593
        default:
 
594
            PJ_LOG(1,(THIS_FILE,
 
595
                      "Argument \"%s\" is not valid. Use --help to see help",
 
596
                      argv[pj_optind-1]));
 
597
            return -1;
 
598
        }
 
599
    }
 
600
 
 
601
    if (init_stack())
 
602
        goto on_error;
 
603
 
 
604
    /* If URL is specified, then make call immediately. */
 
605
    if (pj_optind != argc) {
 
606
        pj_sockaddr hostaddr;
 
607
        char hostip[PJ_INET6_ADDRSTRLEN+2];
 
608
        char temp[80];
 
609
        call_t *call;
 
610
        pj_str_t dst_uri = pj_str(argv[pj_optind]);
 
611
        pj_str_t local_uri;
 
612
        pjsip_dialog *dlg;
 
613
        pj_status_t status;
 
614
        pjsip_tx_data *tdata;
 
615
 
 
616
        if (pj_gethostip(sip_af, &hostaddr) != PJ_SUCCESS) {
 
617
            PJ_LOG(1,(THIS_FILE, "Unable to retrieve local host IP"));
 
618
            goto on_error;
 
619
        }
 
620
        pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);
 
621
 
 
622
        pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>",
 
623
                        hostip, sip_port);
 
624
        local_uri = pj_str(temp);
 
625
 
 
626
        call = &app.call[0];
 
627
 
 
628
        status = pjsip_dlg_create_uac( pjsip_ua_instance(),
 
629
                                       &local_uri,  /* local URI */
 
630
                                       &local_uri,  /* local Contact */
 
631
                                       &dst_uri,    /* remote URI */
 
632
                                       &dst_uri,    /* remote target */
 
633
                                       &dlg);       /* dialog */
 
634
        if (status != PJ_SUCCESS) {
 
635
            app_perror(THIS_FILE, "Unable to create UAC dialog", status);
 
636
            return 1;
 
637
        }
 
638
 
 
639
        status = pjsip_inv_create_uac( dlg, NULL, 0, &call->inv);
 
640
        if (status != PJ_SUCCESS) goto on_error;
 
641
 
 
642
        call->inv->mod_data[mod_sipecho.id] = call;
 
643
 
 
644
        status = pjsip_inv_invite(call->inv, &tdata);
 
645
        if (status != PJ_SUCCESS) goto on_error;
 
646
 
 
647
        status = pjsip_inv_send_msg(call->inv, tdata);
 
648
        if (status != PJ_SUCCESS) goto on_error;
 
649
 
 
650
        puts("Press ENTER to quit...");
 
651
    } else {
 
652
        puts("Ready for incoming calls. Press ENTER to quit...");
 
653
    }
 
654
 
 
655
    for (;;) {
 
656
        char s[10];
 
657
 
 
658
        printf("\nMenu:\n"
 
659
               "  h    Hangup all calls\n"
 
660
               "  l    %s message logging\n"
 
661
               "  q    Quit\n",
 
662
               (app.enable_msg_logging? "Disable" : "Enable"));
 
663
 
 
664
        if (fgets(s, sizeof(s), stdin) == NULL)
 
665
            continue;
 
666
 
 
667
        if (s[0]=='q')
 
668
            break;
 
669
        switch (s[0]) {
 
670
        case 'l':
 
671
            app.enable_msg_logging = !app.enable_msg_logging;
 
672
            break;
 
673
        case 'h':
 
674
            hangup_all();
 
675
            break;
 
676
        }
 
677
    }
 
678
 
 
679
    destroy_stack();
 
680
 
 
681
    puts("Bye bye..");
 
682
    return 0;
 
683
 
 
684
on_error:
 
685
    puts("An error has occurred. run a debugger..");
 
686
    return 1;
 
687
}
 
688