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

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjsip/src/pjsip-ua/sip_reg.c

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: sip_reg.c 4319 2013-01-16 10:20:55Z 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
 
#include <pjsip-ua/sip_regc.h>
21
 
#include <pjsip/sip_endpoint.h>
22
 
#include <pjsip/sip_parser.h>
23
 
#include <pjsip/sip_module.h>
24
 
#include <pjsip/sip_transaction.h>
25
 
#include <pjsip/sip_event.h>
26
 
#include <pjsip/sip_util.h>
27
 
#include <pjsip/sip_auth_msg.h>
28
 
#include <pjsip/sip_errno.h>
29
 
#include <pj/assert.h>
30
 
#include <pj/guid.h>
31
 
#include <pj/lock.h>
32
 
#include <pj/os.h>
33
 
#include <pj/pool.h>
34
 
#include <pj/log.h>
35
 
#include <pj/rand.h>
36
 
#include <pj/string.h>
37
 
 
38
 
 
39
 
#define REFRESH_TIMER           1
40
 
#define DELAY_BEFORE_REFRESH    PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH
41
 
#define THIS_FILE               "sip_reg.c"
42
 
 
43
 
/* Outgoing transaction timeout when server sends 100 but never replies
44
 
 * with final response. Value is in MILISECONDS!
45
 
 */
46
 
#define REGC_TSX_TIMEOUT        33000
47
 
 
48
 
enum { NOEXP = 0x1FFFFFFF };
49
 
 
50
 
static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 };
51
 
 
52
 
 
53
 
/* Current/pending operation */
54
 
enum regc_op
55
 
{
56
 
    REGC_IDLE,
57
 
    REGC_REGISTERING,
58
 
    REGC_UNREGISTERING
59
 
};
60
 
 
61
 
/**
62
 
 * SIP client registration structure.
63
 
 */
64
 
struct pjsip_regc
65
 
{
66
 
    pj_pool_t                   *pool;
67
 
    pjsip_endpoint              *endpt;
68
 
    pj_lock_t                   *lock;
69
 
    pj_bool_t                    _delete_flag;
70
 
    pj_bool_t                    has_tsx;
71
 
    pj_atomic_t                 *busy_ctr;
72
 
    enum regc_op                 current_op;
73
 
 
74
 
    pj_bool_t                    add_xuid_param;
75
 
 
76
 
    void                        *token;
77
 
    pjsip_regc_cb               *cb;
78
 
 
79
 
    pj_str_t                     str_srv_url;
80
 
    pjsip_uri                   *srv_url;
81
 
    pjsip_cid_hdr               *cid_hdr;
82
 
    pjsip_cseq_hdr              *cseq_hdr;
83
 
    pj_str_t                     from_uri;
84
 
    pjsip_from_hdr              *from_hdr;
85
 
    pjsip_to_hdr                *to_hdr;
86
 
    pjsip_contact_hdr            contact_hdr_list;
87
 
    pjsip_contact_hdr            removed_contact_hdr_list;
88
 
    pjsip_expires_hdr           *expires_hdr;
89
 
    pj_uint32_t                  expires;
90
 
    pj_uint32_t                  delay_before_refresh;
91
 
    pjsip_route_hdr              route_set;
92
 
    pjsip_hdr                    hdr_list;
93
 
    pjsip_host_port              via_addr;
94
 
    const void                  *via_tp;
95
 
 
96
 
    /* Authorization sessions. */
97
 
    pjsip_auth_clt_sess          auth_sess;
98
 
 
99
 
    /* Auto refresh registration. */
100
 
    pj_bool_t                    auto_reg;
101
 
    pj_time_val                  last_reg;
102
 
    pj_time_val                  next_reg;
103
 
    pj_timer_entry               timer;
104
 
 
105
 
    /* Transport selector */
106
 
    pjsip_tpselector             tp_sel;
107
 
 
108
 
    /* Last transport used. We acquire the transport to keep
109
 
     * it open.
110
 
     */
111
 
    pjsip_transport             *last_transport;
112
 
};
113
 
 
114
 
 
115
 
PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
116
 
                                       pjsip_regc_cb *cb,
117
 
                                       pjsip_regc **p_regc)
118
 
{
119
 
    pj_pool_t *pool;
120
 
    pjsip_regc *regc;
121
 
    pj_status_t status;
122
 
 
123
 
    /* Verify arguments. */
124
 
    PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
125
 
 
126
 
    pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
127
 
    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
128
 
 
129
 
    regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
130
 
 
131
 
    regc->pool = pool;
132
 
    regc->endpt = endpt;
133
 
    regc->token = token;
134
 
    regc->cb = cb;
135
 
    regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
136
 
    regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param;
137
 
 
138
 
    status = pj_lock_create_recursive_mutex(pool, pool->obj_name, 
139
 
                                            &regc->lock);
140
 
    if (status != PJ_SUCCESS) {
141
 
        pj_pool_release(pool);
142
 
        return status;
143
 
    }
144
 
 
145
 
    status = pj_atomic_create(pool, 0, &regc->busy_ctr);
146
 
    if (status != PJ_SUCCESS) {
147
 
        pj_lock_destroy(regc->lock);
148
 
        pj_pool_release(pool);
149
 
        return status;
150
 
    }
151
 
 
152
 
    status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
153
 
    if (status != PJ_SUCCESS)
154
 
        return status;
155
 
 
156
 
    pj_list_init(&regc->route_set);
157
 
    pj_list_init(&regc->hdr_list);
158
 
    pj_list_init(&regc->contact_hdr_list);
159
 
    pj_list_init(&regc->removed_contact_hdr_list);
160
 
 
161
 
    /* Done */
162
 
    *p_regc = regc;
163
 
    return PJ_SUCCESS;
164
 
}
165
 
 
166
 
 
167
 
PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
168
 
{
169
 
    PJ_ASSERT_RETURN(regc, PJ_EINVAL);
170
 
 
171
 
    pj_lock_acquire(regc->lock);
172
 
    if (regc->has_tsx || pj_atomic_get(regc->busy_ctr) != 0) {
173
 
        regc->_delete_flag = 1;
174
 
        regc->cb = NULL;
175
 
        pj_lock_release(regc->lock);
176
 
    } else {
177
 
        pjsip_tpselector_dec_ref(&regc->tp_sel);
178
 
        if (regc->last_transport) {
179
 
            pjsip_transport_dec_ref(regc->last_transport);
180
 
            regc->last_transport = NULL;
181
 
        }
182
 
        if (regc->timer.id != 0) {
183
 
            pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
184
 
            regc->timer.id = 0;
185
 
        }
186
 
        pj_atomic_destroy(regc->busy_ctr);
187
 
        pj_lock_release(regc->lock);
188
 
        pj_lock_destroy(regc->lock);
189
 
        regc->lock = NULL;
190
 
        pjsip_endpt_release_pool(regc->endpt, regc->pool);
191
 
    }
192
 
 
193
 
    return PJ_SUCCESS;
194
 
}
195
 
 
196
 
 
197
 
PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
198
 
                                         pjsip_regc_info *info )
199
 
{
200
 
    PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
201
 
 
202
 
    pj_lock_acquire(regc->lock);
203
 
 
204
 
    info->server_uri = regc->str_srv_url;
205
 
    info->client_uri = regc->from_uri;
206
 
    info->is_busy = (pj_atomic_get(regc->busy_ctr) || regc->has_tsx);
207
 
    info->auto_reg = regc->auto_reg;
208
 
    info->interval = regc->expires;
209
 
    info->transport = regc->last_transport;
210
 
    
211
 
    if (regc->has_tsx)
212
 
        info->next_reg = 0;
213
 
    else if (regc->auto_reg == 0)
214
 
        info->next_reg = 0;
215
 
    else if (regc->expires < 0)
216
 
        info->next_reg = regc->expires;
217
 
    else {
218
 
        pj_time_val now, next_reg;
219
 
 
220
 
        next_reg = regc->next_reg;
221
 
        pj_gettimeofday(&now);
222
 
        PJ_TIME_VAL_SUB(next_reg, now);
223
 
        info->next_reg = next_reg.sec;
224
 
    }
225
 
 
226
 
    pj_lock_release(regc->lock);
227
 
 
228
 
    return PJ_SUCCESS;
229
 
}
230
 
 
231
 
 
232
 
PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
233
 
{
234
 
    return regc->pool;
235
 
}
236
 
 
237
 
static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
238
 
{
239
 
    if (expires != regc->expires) {
240
 
        regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
241
 
    } else {
242
 
        regc->expires_hdr = NULL;
243
 
    }
244
 
}
245
 
 
246
 
 
247
 
static pj_status_t set_contact( pjsip_regc *regc,
248
 
                                int contact_cnt,
249
 
                                const pj_str_t contact[] )
250
 
{
251
 
    const pj_str_t CONTACT = { "Contact", 7 };
252
 
    pjsip_contact_hdr *h;
253
 
    int i;
254
 
    
255
 
    /* Save existing contact list to removed_contact_hdr_list and
256
 
     * clear contact_hdr_list.
257
 
     */
258
 
    pj_list_merge_last(&regc->removed_contact_hdr_list, 
259
 
                       &regc->contact_hdr_list);
260
 
 
261
 
    /* Set the expiration of Contacts in to removed_contact_hdr_list 
262
 
     * zero.
263
 
     */
264
 
    h = regc->removed_contact_hdr_list.next;
265
 
    while (h != &regc->removed_contact_hdr_list) {
266
 
        h->expires = 0;
267
 
        h = h->next;
268
 
    }
269
 
 
270
 
    /* Process new contacts */
271
 
    for (i=0; i<contact_cnt; ++i) {
272
 
        pjsip_contact_hdr *hdr;
273
 
        pj_str_t tmp;
274
 
 
275
 
        pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
276
 
        hdr = (pjsip_contact_hdr*)
277
 
              pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
278
 
        if (hdr == NULL) {
279
 
            PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"", 
280
 
                     (int)tmp.slen, tmp.ptr));
281
 
            return PJSIP_EINVALIDURI;
282
 
        }
283
 
 
284
 
        /* Find the new contact in old contact list. If found, remove
285
 
         * the old header from the old header list.
286
 
         */
287
 
        h = regc->removed_contact_hdr_list.next;
288
 
        while (h != &regc->removed_contact_hdr_list) {
289
 
            int rc;
290
 
 
291
 
            rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, 
292
 
                               h->uri, hdr->uri);
293
 
            if (rc == 0) {
294
 
                /* Match */
295
 
                pj_list_erase(h);
296
 
                break;
297
 
            }
298
 
 
299
 
            h = h->next;
300
 
        }
301
 
 
302
 
        /* If add_xuid_param option is enabled and Contact URI is sip/sips,
303
 
         * add xuid parameter to assist matching the Contact URI in the 
304
 
         * REGISTER response later.
305
 
         */
306
 
        if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
307
 
                                     PJSIP_URI_SCHEME_IS_SIPS(hdr->uri))) 
308
 
        {
309
 
            pjsip_param *xuid_param;
310
 
            pjsip_sip_uri *sip_uri;
311
 
 
312
 
            xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
313
 
            xuid_param->name = XUID_PARAM_NAME;
314
 
            pj_create_unique_string(regc->pool, &xuid_param->value);
315
 
 
316
 
            sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
317
 
            pj_list_push_back(&sip_uri->other_param, xuid_param);
318
 
        }
319
 
 
320
 
        pj_list_push_back(&regc->contact_hdr_list, hdr);
321
 
    }
322
 
 
323
 
    return PJ_SUCCESS;
324
 
}
325
 
 
326
 
 
327
 
PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
328
 
                                     const pj_str_t *srv_url,
329
 
                                     const pj_str_t *from_url,
330
 
                                     const pj_str_t *to_url,
331
 
                                     int contact_cnt,
332
 
                                     const pj_str_t contact[],
333
 
                                     pj_uint32_t expires)
334
 
{
335
 
    pj_str_t tmp;
336
 
    pj_status_t status;
337
 
 
338
 
    PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url && 
339
 
                     expires, PJ_EINVAL);
340
 
 
341
 
    /* Copy server URL. */
342
 
    pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
343
 
 
344
 
    /* Set server URL. */
345
 
    tmp = regc->str_srv_url;
346
 
    regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
347
 
    if (regc->srv_url == NULL) {
348
 
        return PJSIP_EINVALIDURI;
349
 
    }
350
 
 
351
 
    /* Set "From" header. */
352
 
    pj_strdup_with_null(regc->pool, &regc->from_uri, from_url);
353
 
    tmp = regc->from_uri;
354
 
    regc->from_hdr = pjsip_from_hdr_create(regc->pool);
355
 
    regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, 
356
 
                                          PJSIP_PARSE_URI_AS_NAMEADDR);
357
 
    if (!regc->from_hdr->uri) {
358
 
        PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", 
359
 
                  from_url->slen, from_url->ptr));
360
 
        return PJSIP_EINVALIDURI;
361
 
    }
362
 
 
363
 
    /* Set "To" header. */
364
 
    pj_strdup_with_null(regc->pool, &tmp, to_url);
365
 
    regc->to_hdr = pjsip_to_hdr_create(regc->pool);
366
 
    regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, 
367
 
                                        PJSIP_PARSE_URI_AS_NAMEADDR);
368
 
    if (!regc->to_hdr->uri) {
369
 
        PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
370
 
        return PJSIP_EINVALIDURI;
371
 
    }
372
 
 
373
 
 
374
 
    /* Set "Contact" header. */
375
 
    status = set_contact( regc, contact_cnt, contact);
376
 
    if (status != PJ_SUCCESS)
377
 
        return status;
378
 
 
379
 
    /* Set "Expires" header, if required. */
380
 
    set_expires( regc, expires);
381
 
    regc->delay_before_refresh = DELAY_BEFORE_REFRESH;
382
 
 
383
 
    /* Set "Call-ID" header. */
384
 
    regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
385
 
    pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
386
 
 
387
 
    /* Set "CSeq" header. */
388
 
    regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
389
 
    regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
390
 
    pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
391
 
 
392
 
    /* Done. */
393
 
    return PJ_SUCCESS;
394
 
}
395
 
 
396
 
PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
397
 
                                                int count,
398
 
                                                const pjsip_cred_info cred[] )
399
 
{
400
 
    PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
401
 
    return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
402
 
}
403
 
 
404
 
PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
405
 
                                          const pjsip_auth_clt_pref *pref)
406
 
{
407
 
    PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
408
 
    return pjsip_auth_clt_set_prefs(&regc->auth_sess, pref);
409
 
}
410
 
 
411
 
PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
412
 
                                              const pjsip_route_hdr *route_set)
413
 
{
414
 
    const pjsip_route_hdr *chdr;
415
 
 
416
 
    PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
417
 
 
418
 
    pj_list_init(&regc->route_set);
419
 
 
420
 
    chdr = route_set->next;
421
 
    while (chdr != route_set) {
422
 
        pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
423
 
        chdr = chdr->next;
424
 
    }
425
 
 
426
 
    return PJ_SUCCESS;
427
 
}
428
 
 
429
 
 
430
 
/*
431
 
 * Bind client registration to a specific transport/listener. 
432
 
 */
433
 
PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
434
 
                                              const pjsip_tpselector *sel)
435
 
{
436
 
    PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
437
 
 
438
 
    pjsip_tpselector_dec_ref(&regc->tp_sel);
439
 
    pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
440
 
    pjsip_tpselector_add_ref(&regc->tp_sel);
441
 
 
442
 
    return PJ_SUCCESS;
443
 
}
444
 
 
445
 
/* Release transport */
446
 
PJ_DEF(pj_status_t) pjsip_regc_release_transport(pjsip_regc *regc)
447
 
{
448
 
    PJ_ASSERT_RETURN(regc, PJ_EINVAL);
449
 
    if (regc->last_transport) {
450
 
        pjsip_transport_dec_ref(regc->last_transport);
451
 
        regc->last_transport = NULL;
452
 
    }
453
 
    return PJ_SUCCESS;
454
 
}
455
 
 
456
 
 
457
 
PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
458
 
                                            const pjsip_hdr *hdr_list)
459
 
{
460
 
    const pjsip_hdr *hdr;
461
 
 
462
 
    PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
463
 
 
464
 
    //This is "add" operation, so don't remove headers.
465
 
    //pj_list_init(&regc->hdr_list);
466
 
 
467
 
    hdr = hdr_list->next;
468
 
    while (hdr != hdr_list) {
469
 
        pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
470
 
        hdr = hdr->next;
471
 
    }
472
 
 
473
 
    return PJ_SUCCESS;
474
 
}
475
 
 
476
 
static pj_status_t create_request(pjsip_regc *regc, 
477
 
                                  pjsip_tx_data **p_tdata)
478
 
{
479
 
    pj_status_t status;
480
 
    pjsip_tx_data *tdata;
481
 
 
482
 
    PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
483
 
 
484
 
    /* Create the request. */
485
 
    status = pjsip_endpt_create_request_from_hdr( regc->endpt, 
486
 
                                                  pjsip_get_register_method(),
487
 
                                                  regc->srv_url,
488
 
                                                  regc->from_hdr,
489
 
                                                  regc->to_hdr,
490
 
                                                  NULL,
491
 
                                                  regc->cid_hdr,
492
 
                                                  regc->cseq_hdr->cseq,
493
 
                                                  NULL,
494
 
                                                  &tdata);
495
 
    if (status != PJ_SUCCESS)
496
 
        return status;
497
 
 
498
 
    /* Add cached authorization headers. */
499
 
    pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
500
 
 
501
 
    /* Add Route headers from route set, ideally after Via header */
502
 
    if (!pj_list_empty(&regc->route_set)) {
503
 
        pjsip_hdr *route_pos;
504
 
        const pjsip_route_hdr *route;
505
 
 
506
 
        route_pos = (pjsip_hdr*)
507
 
                    pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
508
 
        if (!route_pos)
509
 
            route_pos = &tdata->msg->hdr;
510
 
 
511
 
        route = regc->route_set.next;
512
 
        while (route != &regc->route_set) {
513
 
            pjsip_hdr *new_hdr = (pjsip_hdr*)
514
 
                                 pjsip_hdr_shallow_clone(tdata->pool, route);
515
 
            pj_list_insert_after(route_pos, new_hdr);
516
 
            route_pos = new_hdr;
517
 
            route = route->next;
518
 
        }
519
 
    }
520
 
 
521
 
    /* Add additional request headers */
522
 
    if (!pj_list_empty(&regc->hdr_list)) {
523
 
        const pjsip_hdr *hdr;
524
 
 
525
 
        hdr = regc->hdr_list.next;
526
 
        while (hdr != &regc->hdr_list) {
527
 
            pjsip_hdr *new_hdr = (pjsip_hdr*)
528
 
                                 pjsip_hdr_shallow_clone(tdata->pool, hdr);
529
 
            pjsip_msg_add_hdr(tdata->msg, new_hdr);
530
 
            hdr = hdr->next;
531
 
        }
532
 
    }
533
 
 
534
 
    /* Done. */
535
 
    *p_tdata = tdata;
536
 
    return PJ_SUCCESS;
537
 
}
538
 
 
539
 
 
540
 
PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
541
 
                                        pjsip_tx_data **p_tdata)
542
 
{
543
 
    pjsip_msg *msg;
544
 
    pjsip_contact_hdr *hdr;
545
 
    const pjsip_hdr *h_allow;
546
 
    pj_status_t status;
547
 
    pjsip_tx_data *tdata;
548
 
 
549
 
    PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
550
 
 
551
 
    pj_lock_acquire(regc->lock);
552
 
 
553
 
    status = create_request(regc, &tdata);
554
 
    if (status != PJ_SUCCESS) {
555
 
        pj_lock_release(regc->lock);
556
 
        return status;
557
 
    }
558
 
 
559
 
    msg = tdata->msg;
560
 
 
561
 
    /* Add Contact headers. */
562
 
    hdr = regc->contact_hdr_list.next;
563
 
    while (hdr != &regc->contact_hdr_list) {
564
 
        pjsip_msg_add_hdr(msg, (pjsip_hdr*)
565
 
                               pjsip_hdr_shallow_clone(tdata->pool, hdr));
566
 
        hdr = hdr->next;
567
 
    }
568
 
 
569
 
    /* Also add bindings which are to be removed */
570
 
    while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
571
 
        hdr = regc->removed_contact_hdr_list.next;
572
 
        pjsip_msg_add_hdr(msg, (pjsip_hdr*)
573
 
                               pjsip_hdr_clone(tdata->pool, hdr));
574
 
        pj_list_erase(hdr);
575
 
    }
576
 
    
577
 
 
578
 
    if (regc->expires_hdr)
579
 
        pjsip_msg_add_hdr(msg, (pjsip_hdr*)
580
 
                               pjsip_hdr_shallow_clone(tdata->pool,
581
 
                                                       regc->expires_hdr));
582
 
 
583
 
    if (regc->timer.id != 0) {
584
 
        pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
585
 
        regc->timer.id = 0;
586
 
    }
587
 
 
588
 
    /* Add Allow header (http://trac.pjsip.org/repos/ticket/1039) */
589
 
    h_allow = pjsip_endpt_get_capability(regc->endpt, PJSIP_H_ALLOW, NULL);
590
 
    if (h_allow) {
591
 
        pjsip_msg_add_hdr(msg, (pjsip_hdr*)
592
 
                               pjsip_hdr_shallow_clone(tdata->pool, h_allow));
593
 
 
594
 
    }
595
 
 
596
 
    regc->auto_reg = autoreg;
597
 
 
598
 
    pj_lock_release(regc->lock);
599
 
 
600
 
    /* Done */
601
 
    *p_tdata = tdata;
602
 
    return PJ_SUCCESS;
603
 
}
604
 
 
605
 
 
606
 
PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
607
 
                                          pjsip_tx_data **p_tdata)
608
 
{
609
 
    pjsip_tx_data *tdata;
610
 
    pjsip_msg *msg;
611
 
    pjsip_hdr *hdr;
612
 
    pj_status_t status;
613
 
 
614
 
    PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
615
 
 
616
 
    pj_lock_acquire(regc->lock);
617
 
 
618
 
    if (regc->timer.id != 0) {
619
 
        pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
620
 
        regc->timer.id = 0;
621
 
    }
622
 
 
623
 
    status = create_request(regc, &tdata);
624
 
    if (status != PJ_SUCCESS) {
625
 
        pj_lock_release(regc->lock);
626
 
        return status;
627
 
    }
628
 
 
629
 
    msg = tdata->msg;
630
 
 
631
 
    /* Add Contact headers. */
632
 
    hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
633
 
    while ((void*)hdr != (void*)&regc->contact_hdr_list) {
634
 
        pjsip_msg_add_hdr(msg, (pjsip_hdr*)
635
 
                               pjsip_hdr_shallow_clone(tdata->pool, hdr));
636
 
        hdr = hdr->next;
637
 
    }
638
 
 
639
 
    /* Also add bindings which are to be removed */
640
 
    while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
641
 
        hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next;
642
 
        pjsip_msg_add_hdr(msg, (pjsip_hdr*)
643
 
                               pjsip_hdr_clone(tdata->pool, hdr));
644
 
        pj_list_erase(hdr);
645
 
    }
646
 
 
647
 
    /* Add Expires:0 header */
648
 
    hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
649
 
    pjsip_msg_add_hdr(msg, hdr);
650
 
 
651
 
    pj_lock_release(regc->lock);
652
 
 
653
 
    *p_tdata = tdata;
654
 
    return PJ_SUCCESS;
655
 
}
656
 
 
657
 
PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
658
 
                                              pjsip_tx_data **p_tdata)
659
 
{
660
 
    pjsip_tx_data *tdata;
661
 
    pjsip_contact_hdr *hcontact;
662
 
    pjsip_hdr *hdr;
663
 
    pjsip_msg *msg;
664
 
    pj_status_t status;
665
 
 
666
 
    PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
667
 
 
668
 
    pj_lock_acquire(regc->lock);
669
 
 
670
 
    if (regc->timer.id != 0) {
671
 
        pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
672
 
        regc->timer.id = 0;
673
 
    }
674
 
 
675
 
    status = create_request(regc, &tdata);
676
 
    if (status != PJ_SUCCESS) {
677
 
        pj_lock_release(regc->lock);
678
 
        return status;
679
 
    }
680
 
 
681
 
    msg = tdata->msg;
682
 
 
683
 
    /* Clear removed_contact_hdr_list */
684
 
    pj_list_init(&regc->removed_contact_hdr_list);
685
 
 
686
 
    /* Add Contact:* header */
687
 
    hcontact = pjsip_contact_hdr_create(tdata->pool);
688
 
    hcontact->star = 1;
689
 
    pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
690
 
    
691
 
    /* Add Expires:0 header */
692
 
    hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
693
 
    pjsip_msg_add_hdr(msg, hdr);
694
 
 
695
 
    pj_lock_release(regc->lock);
696
 
 
697
 
    *p_tdata = tdata;
698
 
    return PJ_SUCCESS;
699
 
}
700
 
 
701
 
 
702
 
PJ_DEF(pj_status_t) pjsip_regc_update_contact(  pjsip_regc *regc,
703
 
                                                int contact_cnt,
704
 
                                                const pj_str_t contact[] )
705
 
{
706
 
    pj_status_t status;
707
 
 
708
 
    PJ_ASSERT_RETURN(regc, PJ_EINVAL);
709
 
 
710
 
    pj_lock_acquire(regc->lock);
711
 
    status = set_contact( regc, contact_cnt, contact );
712
 
    pj_lock_release(regc->lock);
713
 
 
714
 
    return status;
715
 
}
716
 
 
717
 
 
718
 
PJ_DEF(pj_status_t) pjsip_regc_update_expires(  pjsip_regc *regc,
719
 
                                                pj_uint32_t expires )
720
 
{
721
 
    PJ_ASSERT_RETURN(regc, PJ_EINVAL);
722
 
 
723
 
    pj_lock_acquire(regc->lock);
724
 
    set_expires( regc, expires );
725
 
    pj_lock_release(regc->lock);
726
 
 
727
 
    return PJ_SUCCESS;
728
 
}
729
 
 
730
 
 
731
 
static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code, 
732
 
                          const pj_str_t *reason,
733
 
                          pjsip_rx_data *rdata, pj_int32_t expiration,
734
 
                          int contact_cnt, pjsip_contact_hdr *contact[])
735
 
{
736
 
    struct pjsip_regc_cbparam cbparam;
737
 
 
738
 
 
739
 
    if (!regc->cb)
740
 
        return;
741
 
 
742
 
    cbparam.regc = regc;
743
 
    cbparam.token = regc->token;
744
 
    cbparam.status = status;
745
 
    cbparam.code = st_code;
746
 
    cbparam.reason = *reason;
747
 
    cbparam.rdata = rdata;
748
 
    cbparam.contact_cnt = contact_cnt;
749
 
    cbparam.expiration = expiration;
750
 
    if (contact_cnt) {
751
 
        pj_memcpy( cbparam.contact, contact, 
752
 
                   contact_cnt*sizeof(pjsip_contact_hdr*));
753
 
    }
754
 
 
755
 
    (*regc->cb)(&cbparam);
756
 
}
757
 
 
758
 
static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
759
 
                                   struct pj_timer_entry *entry)
760
 
{
761
 
    pjsip_regc *regc = (pjsip_regc*) entry->user_data;
762
 
    pjsip_tx_data *tdata;
763
 
    pj_status_t status;
764
 
    
765
 
    PJ_UNUSED_ARG(timer_heap);
766
 
 
767
 
    /* Temporarily increase busy flag to prevent regc from being deleted
768
 
     * in pjsip_regc_send() or in the callback
769
 
     */
770
 
    pj_atomic_inc(regc->busy_ctr);
771
 
 
772
 
    entry->id = 0;
773
 
    status = pjsip_regc_register(regc, 1, &tdata);
774
 
    if (status == PJ_SUCCESS) {
775
 
        status = pjsip_regc_send(regc, tdata);
776
 
    } 
777
 
    
778
 
    if (status != PJ_SUCCESS && regc->cb) {
779
 
        char errmsg[PJ_ERR_MSG_SIZE];
780
 
        pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
781
 
        call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
782
 
    }
783
 
 
784
 
    /* Delete the record if user destroy regc during the callback. */
785
 
    if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
786
 
        pjsip_regc_destroy(regc);
787
 
    }
788
 
}
789
 
 
790
 
static void schedule_registration ( pjsip_regc *regc, pj_int32_t expiration )
791
 
{
792
 
    if (regc->auto_reg && expiration > 0) {
793
 
        pj_time_val delay = { 0, 0};
794
 
 
795
 
        delay.sec = expiration - regc->delay_before_refresh;
796
 
        if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && 
797
 
            delay.sec > (pj_int32_t)regc->expires) 
798
 
        {
799
 
            delay.sec = regc->expires;
800
 
        }
801
 
        if (delay.sec < DELAY_BEFORE_REFRESH) 
802
 
            delay.sec = DELAY_BEFORE_REFRESH;
803
 
        regc->timer.cb = &regc_refresh_timer_cb;
804
 
        regc->timer.id = REFRESH_TIMER;
805
 
        regc->timer.user_data = regc;
806
 
        pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
807
 
        pj_gettimeofday(&regc->last_reg);
808
 
        regc->next_reg = regc->last_reg;
809
 
        regc->next_reg.sec += delay.sec;
810
 
    }
811
 
}
812
 
 
813
 
PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc,
814
 
                                                pjsip_host_port *via_addr,
815
 
                                                pjsip_transport *via_tp)
816
 
{
817
 
    PJ_ASSERT_RETURN(regc, PJ_EINVAL);
818
 
 
819
 
    if (!via_addr)
820
 
        pj_bzero(&regc->via_addr, sizeof(regc->via_addr));
821
 
    else {
822
 
        if (pj_strcmp(&regc->via_addr.host, &via_addr->host))
823
 
            pj_strdup(regc->pool, &regc->via_addr.host, &via_addr->host);
824
 
        regc->via_addr.port = via_addr->port;
825
 
    }
826
 
    regc->via_tp = via_tp;
827
 
 
828
 
    return PJ_SUCCESS;
829
 
}
830
 
 
831
 
PJ_DEF(pj_status_t)
832
 
pjsip_regc_set_delay_before_refresh( pjsip_regc *regc,
833
 
                                     pj_uint32_t delay )
834
 
{
835
 
    PJ_ASSERT_RETURN(regc, PJ_EINVAL);
836
 
 
837
 
    if (delay > regc->expires)
838
 
        return PJ_ETOOBIG;
839
 
 
840
 
    if (regc->delay_before_refresh != delay)
841
 
    {
842
 
        regc->delay_before_refresh = delay;
843
 
 
844
 
        if (regc->timer.id != 0) {
845
 
            /* Cancel registration timer */
846
 
            pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
847
 
            regc->timer.id = 0;
848
 
 
849
 
            /* Schedule next registration */
850
 
            schedule_registration(regc, regc->expires);
851
 
        }
852
 
    }
853
 
 
854
 
    return PJ_SUCCESS;
855
 
}
856
 
 
857
 
 
858
 
static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
859
 
                                                const pjsip_rx_data *rdata,
860
 
                                                unsigned *contact_cnt,
861
 
                                                unsigned max_contact,
862
 
                                                pjsip_contact_hdr *contacts[])
863
 
{
864
 
    pj_int32_t expiration = NOEXP;
865
 
    const pjsip_msg *msg = rdata->msg_info.msg;
866
 
    const pjsip_hdr *hdr;
867
 
 
868
 
    /* Enumerate all Contact headers in the response */
869
 
    *contact_cnt = 0;
870
 
    for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
871
 
        if (hdr->type == PJSIP_H_CONTACT && 
872
 
            *contact_cnt < max_contact) 
873
 
        {
874
 
            contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
875
 
            ++(*contact_cnt);
876
 
        }
877
 
    }
878
 
 
879
 
    if (regc->current_op == REGC_REGISTERING) {
880
 
        pj_bool_t has_our_contact = PJ_FALSE;
881
 
        const pjsip_expires_hdr *expires;
882
 
 
883
 
        /* Get Expires header */
884
 
        expires = (const pjsip_expires_hdr*)
885
 
                  pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
886
 
 
887
 
        /* Try to find the Contact URIs that we register, in the response
888
 
         * to get the expires value. We'll try both with comparing the URI
889
 
         * and comparing the extension param only.
890
 
         */
891
 
        if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
892
 
            unsigned i;
893
 
            for (i=0; i<*contact_cnt; ++i) {
894
 
                const pjsip_contact_hdr *our_hdr;
895
 
 
896
 
                our_hdr = (const pjsip_contact_hdr*) 
897
 
                          regc->contact_hdr_list.next;
898
 
 
899
 
                /* Match with our Contact header(s) */
900
 
                while ((void*)our_hdr != (void*)&regc->contact_hdr_list) {
901
 
 
902
 
                    const pjsip_uri *uri1, *uri2;
903
 
                    pj_bool_t matched = PJ_FALSE;
904
 
 
905
 
                    /* Exclude the display name when comparing the URI 
906
 
                     * since server may not return it.
907
 
                     */
908
 
                    uri1 = (const pjsip_uri*)
909
 
                           pjsip_uri_get_uri(contacts[i]->uri);
910
 
                    uri2 = (const pjsip_uri*)
911
 
                           pjsip_uri_get_uri(our_hdr->uri);
912
 
 
913
 
                    /* First try with exact matching, according to RFC 3261
914
 
                     * Section 19.1.4 URI Comparison
915
 
                     */
916
 
                    if (pjsip_cfg()->regc.check_contact) {
917
 
                        matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
918
 
                                                uri1, uri2)==0;
919
 
                    }
920
 
 
921
 
                    /* If no match is found, try with matching the extension
922
 
                     * parameter only if extension parameter was added.
923
 
                     */
924
 
                    if (!matched && regc->add_xuid_param &&
925
 
                        (PJSIP_URI_SCHEME_IS_SIP(uri1) ||
926
 
                         PJSIP_URI_SCHEME_IS_SIPS(uri1)) && 
927
 
                        (PJSIP_URI_SCHEME_IS_SIP(uri2) ||
928
 
                         PJSIP_URI_SCHEME_IS_SIPS(uri2))) 
929
 
                    {
930
 
                        const pjsip_sip_uri *sip_uri1, *sip_uri2;
931
 
                        const pjsip_param *p1, *p2;
932
 
                
933
 
                        sip_uri1 = (const pjsip_sip_uri*)uri1;
934
 
                        sip_uri2 = (const pjsip_sip_uri*)uri2;
935
 
 
936
 
                        p1 = pjsip_param_cfind(&sip_uri1->other_param,
937
 
                                               &XUID_PARAM_NAME);
938
 
                        p2 = pjsip_param_cfind(&sip_uri2->other_param,
939
 
                                               &XUID_PARAM_NAME);
940
 
                        matched = p1 && p2 &&
941
 
                                  pj_strcmp(&p1->value, &p2->value)==0;
942
 
 
943
 
                    }
944
 
 
945
 
                    if (matched) {
946
 
                        has_our_contact = PJ_TRUE;
947
 
 
948
 
                        if (contacts[i]->expires >= 0 && 
949
 
                            contacts[i]->expires < expiration) 
950
 
                        {
951
 
                            /* Get the lowest expiration time. */
952
 
                            expiration = contacts[i]->expires;
953
 
                        }
954
 
 
955
 
                        break;
956
 
                    }
957
 
 
958
 
                    our_hdr = our_hdr->next;
959
 
 
960
 
                } /* while ((void.. */
961
 
 
962
 
            }  /* for (i=.. */
963
 
 
964
 
            /* If matching Contact header(s) are found but the
965
 
             * header doesn't contain expires parameter, get the
966
 
             * expiration value from the Expires header. And
967
 
             * if Expires header is not present, get the expiration
968
 
             * value from the request.
969
 
             */
970
 
            if (has_our_contact && expiration == NOEXP) {
971
 
                if (expires) {
972
 
                    expiration = expires->ivalue;
973
 
                } else if (regc->expires_hdr) {
974
 
                    expiration = regc->expires_hdr->ivalue;
975
 
                } else {
976
 
                    /* We didn't request explicit expiration value,
977
 
                     * and server doesn't specify it either. This 
978
 
                     * shouldn't happen unless we have a broken
979
 
                     * registrar.
980
 
                     */
981
 
                    expiration = 3600;
982
 
                }
983
 
            }
984
 
 
985
 
        }
986
 
 
987
 
        /* If we still couldn't get matching Contact header(s), it means
988
 
         * there must be something wrong with the  registrar (e.g. it may
989
 
         * have modified the URI's in the response, which is prohibited).
990
 
         */
991
 
        if (expiration==NOEXP) {
992
 
            /* If the number of Contact headers in the response matches 
993
 
             * ours, they're all probably ours. Get the expiration
994
 
             * from there if this is the case, or from Expires header
995
 
             * if we don't have exact Contact header count, or
996
 
             * from the request as the last resort.
997
 
             */
998
 
            unsigned our_contact_cnt;
999
 
 
1000
 
            our_contact_cnt = pj_list_size(&regc->contact_hdr_list);
1001
 
 
1002
 
            if (*contact_cnt == our_contact_cnt && *contact_cnt &&
1003
 
                contacts[0]->expires >= 0) 
1004
 
            {
1005
 
                expiration = contacts[0]->expires;
1006
 
            } else if (expires)
1007
 
                expiration = expires->ivalue;
1008
 
            else if (regc->expires_hdr)
1009
 
                expiration = regc->expires_hdr->ivalue;
1010
 
            else
1011
 
                expiration = 3600;
1012
 
        }
1013
 
 
1014
 
    } else {
1015
 
        /* Just assume that the unregistration has been successful. */
1016
 
        expiration = 0;
1017
 
    }
1018
 
 
1019
 
    /* Must have expiration value by now */
1020
 
    pj_assert(expiration != NOEXP);
1021
 
 
1022
 
    return expiration;
1023
 
}
1024
 
 
1025
 
static void regc_tsx_callback(void *token, pjsip_event *event)
1026
 
{
1027
 
    pj_status_t status;
1028
 
    pjsip_regc *regc = (pjsip_regc*) token;
1029
 
    pjsip_transaction *tsx = event->body.tsx_state.tsx;
1030
 
    pj_bool_t handled = PJ_TRUE;
1031
 
 
1032
 
    pj_atomic_inc(regc->busy_ctr);
1033
 
    pj_lock_acquire(regc->lock);
1034
 
 
1035
 
    /* Decrement pending transaction counter. */
1036
 
    pj_assert(regc->has_tsx);
1037
 
    regc->has_tsx = PJ_FALSE;
1038
 
 
1039
 
    /* Add reference to the transport */
1040
 
    if (tsx->transport != regc->last_transport) {
1041
 
        if (regc->last_transport) {
1042
 
            pjsip_transport_dec_ref(regc->last_transport);
1043
 
            regc->last_transport = NULL;
1044
 
        }
1045
 
 
1046
 
        if (tsx->transport) {
1047
 
            regc->last_transport = tsx->transport;
1048
 
            pjsip_transport_add_ref(regc->last_transport);
1049
 
        }
1050
 
    }
1051
 
 
1052
 
    /* Handle 401/407 challenge (even when _delete_flag is set) */
1053
 
    if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
1054
 
        tsx->status_code == PJSIP_SC_UNAUTHORIZED)
1055
 
    {
1056
 
        pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1057
 
        pjsip_tx_data *tdata;
1058
 
 
1059
 
        /* reset current op */
1060
 
        regc->current_op = REGC_IDLE;
1061
 
 
1062
 
        status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
1063
 
                                            rdata, 
1064
 
                                            tsx->last_tx,  
1065
 
                                            &tdata);
1066
 
 
1067
 
        if (status == PJ_SUCCESS) {
1068
 
            status = pjsip_regc_send(regc, tdata);
1069
 
        }
1070
 
        
1071
 
        if (status != PJ_SUCCESS) {
1072
 
 
1073
 
            /* Only call callback if application is still interested
1074
 
             * in it.
1075
 
             */
1076
 
            if (regc->_delete_flag == 0) {
1077
 
                /* Should be safe to release the lock temporarily.
1078
 
                 * We do this to avoid deadlock. 
1079
 
                 */
1080
 
                pj_lock_release(regc->lock);
1081
 
                call_callback(regc, status, tsx->status_code, 
1082
 
                              &rdata->msg_info.msg->line.status.reason,
1083
 
                              rdata, -1, 0, NULL);
1084
 
                pj_lock_acquire(regc->lock);
1085
 
            }
1086
 
        }
1087
 
 
1088
 
    } else if (regc->_delete_flag) {
1089
 
 
1090
 
        /* User has called pjsip_regc_destroy(), so don't call callback. 
1091
 
         * This regc will be destroyed later in this function.
1092
 
         */
1093
 
 
1094
 
        /* Just reset current op */
1095
 
        regc->current_op = REGC_IDLE;
1096
 
 
1097
 
    } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
1098
 
               regc->current_op == REGC_REGISTERING)
1099
 
    {
1100
 
        /* Handle 423 response automatically:
1101
 
         *  - set requested expiration to Min-Expires header, ONLY IF
1102
 
         *    the original request is a registration (as opposed to
1103
 
         *    unregistration) and the requested expiration was indeed
1104
 
         *    lower than Min-Expires)
1105
 
         *  - resend the request
1106
 
         */
1107
 
        pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1108
 
        pjsip_min_expires_hdr *me_hdr;
1109
 
        pjsip_tx_data *tdata;
1110
 
        pj_int32_t min_exp;
1111
 
 
1112
 
        /* reset current op */
1113
 
        regc->current_op = REGC_IDLE;
1114
 
 
1115
 
        /* Update requested expiration */
1116
 
        me_hdr = (pjsip_min_expires_hdr*)
1117
 
                 pjsip_msg_find_hdr(rdata->msg_info.msg,
1118
 
                                    PJSIP_H_MIN_EXPIRES, NULL);
1119
 
        if (me_hdr) {
1120
 
            min_exp = me_hdr->ivalue;
1121
 
        } else {
1122
 
            /* Broken server, Min-Expires doesn't exist.
1123
 
             * Just guestimate then, BUT ONLY if if this is the
1124
 
             * first time we received such response.
1125
 
             */
1126
 
            enum {
1127
 
                /* Note: changing this value would require changing couple of
1128
 
                 *       Python test scripts.
1129
 
                 */
1130
 
                UNSPECIFIED_MIN_EXPIRES = 3601
1131
 
            };
1132
 
            if (!regc->expires_hdr ||
1133
 
                 regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
1134
 
            {
1135
 
                min_exp = UNSPECIFIED_MIN_EXPIRES;
1136
 
            } else {
1137
 
                handled = PJ_FALSE;
1138
 
                PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
1139
 
                                     "without Min-Expires header is invalid"));
1140
 
                goto handle_err;
1141
 
            }
1142
 
        }
1143
 
 
1144
 
        if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
1145
 
            /* But we already send with greater expiration time, why does
1146
 
             * the server send us with 423? Oh well, just fail the request.
1147
 
             */
1148
 
            handled = PJ_FALSE;
1149
 
            PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
1150
 
                                 "Min-Expires header value in response"));
1151
 
            goto handle_err;
1152
 
        }
1153
 
 
1154
 
        set_expires(regc, min_exp);
1155
 
 
1156
 
        status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
1157
 
        if (status == PJ_SUCCESS) {
1158
 
            status = pjsip_regc_send(regc, tdata);
1159
 
        }
1160
 
 
1161
 
        if (status != PJ_SUCCESS) {
1162
 
            /* Only call callback if application is still interested
1163
 
             * in it.
1164
 
             */
1165
 
            if (!regc->_delete_flag) {
1166
 
                /* Should be safe to release the lock temporarily.
1167
 
                 * We do this to avoid deadlock.
1168
 
                 */
1169
 
                pj_lock_release(regc->lock);
1170
 
                call_callback(regc, status, tsx->status_code,
1171
 
                              &rdata->msg_info.msg->line.status.reason,
1172
 
                              rdata, -1, 0, NULL);
1173
 
                pj_lock_acquire(regc->lock);
1174
 
            }
1175
 
        }
1176
 
 
1177
 
    } else {
1178
 
        handled = PJ_FALSE;
1179
 
    }
1180
 
 
1181
 
handle_err:
1182
 
    if (!handled) {
1183
 
        pjsip_rx_data *rdata;
1184
 
        pj_int32_t expiration = NOEXP;
1185
 
        unsigned contact_cnt = 0;
1186
 
        pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
1187
 
 
1188
 
        if (tsx->status_code/100 == 2) {
1189
 
 
1190
 
            rdata = event->body.tsx_state.src.rdata;
1191
 
 
1192
 
            /* Calculate expiration */
1193
 
            expiration = calculate_response_expiration(regc, rdata, 
1194
 
                                                       &contact_cnt,
1195
 
                                                       PJSIP_REGC_MAX_CONTACT,
1196
 
                                                       contact);
1197
 
 
1198
 
            /* Schedule next registration */
1199
 
            schedule_registration(regc, expiration);
1200
 
 
1201
 
        } else {
1202
 
            rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? 
1203
 
                        event->body.tsx_state.src.rdata : NULL;
1204
 
        }
1205
 
 
1206
 
        /* Update registration */
1207
 
        if (expiration==NOEXP) expiration=-1;
1208
 
        regc->expires = expiration;
1209
 
 
1210
 
        /* Mark operation as complete */
1211
 
        regc->current_op = REGC_IDLE;
1212
 
 
1213
 
        /* Call callback. */
1214
 
        /* Should be safe to release the lock temporarily.
1215
 
         * We do this to avoid deadlock. 
1216
 
         */
1217
 
        pj_lock_release(regc->lock);
1218
 
        call_callback(regc, PJ_SUCCESS, tsx->status_code, 
1219
 
                      (rdata ? &rdata->msg_info.msg->line.status.reason 
1220
 
                        : &tsx->status_text),
1221
 
                      rdata, expiration, 
1222
 
                      contact_cnt, contact);
1223
 
        pj_lock_acquire(regc->lock);
1224
 
    }
1225
 
 
1226
 
    pj_lock_release(regc->lock);
1227
 
 
1228
 
    /* Delete the record if user destroy regc during the callback. */
1229
 
    if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
1230
 
        pjsip_regc_destroy(regc);
1231
 
    }
1232
 
}
1233
 
 
1234
 
PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
1235
 
{
1236
 
    pj_status_t status;
1237
 
    pjsip_cseq_hdr *cseq_hdr;
1238
 
    pjsip_expires_hdr *expires_hdr;
1239
 
    pj_uint32_t cseq;
1240
 
 
1241
 
    pj_atomic_inc(regc->busy_ctr);
1242
 
    pj_lock_acquire(regc->lock);
1243
 
 
1244
 
    /* Make sure we don't have pending transaction. */
1245
 
    if (regc->has_tsx) {
1246
 
        PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
1247
 
                             "transaction pending"));
1248
 
        pjsip_tx_data_dec_ref( tdata );
1249
 
        pj_lock_release(regc->lock);
1250
 
        pj_atomic_dec(regc->busy_ctr);
1251
 
        return PJSIP_EBUSY;
1252
 
    }
1253
 
 
1254
 
    pj_assert(regc->current_op == REGC_IDLE);
1255
 
 
1256
 
    /* Invalidate message buffer. */
1257
 
    pjsip_tx_data_invalidate_msg(tdata);
1258
 
 
1259
 
    /* Increment CSeq */
1260
 
    cseq = ++regc->cseq_hdr->cseq;
1261
 
    cseq_hdr = (pjsip_cseq_hdr*)
1262
 
               pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
1263
 
    cseq_hdr->cseq = cseq;
1264
 
 
1265
 
    /* Find Expires header */
1266
 
    expires_hdr = (pjsip_expires_hdr*)
1267
 
                  pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
1268
 
 
1269
 
    /* Bind to transport selector */
1270
 
    pjsip_tx_data_set_transport(tdata, &regc->tp_sel);
1271
 
 
1272
 
    regc->has_tsx = PJ_TRUE;
1273
 
 
1274
 
    /* Set current operation based on the value of Expires header */
1275
 
    if (expires_hdr && expires_hdr->ivalue==0)
1276
 
        regc->current_op = REGC_UNREGISTERING;
1277
 
    else
1278
 
        regc->current_op = REGC_REGISTERING;
1279
 
 
1280
 
    /* Prevent deletion of tdata, e.g: when something wrong in sending,
1281
 
     * we need tdata to retrieve the transport.
1282
 
     */
1283
 
    pjsip_tx_data_add_ref(tdata);
1284
 
 
1285
 
    /* If via_addr is set, use this address for the Via header. */
1286
 
    if (regc->via_addr.host.slen > 0) {
1287
 
        tdata->via_addr = regc->via_addr;
1288
 
        tdata->via_tp = regc->via_tp;
1289
 
    }
1290
 
 
1291
 
    /* Need to unlock the regc temporarily while sending the message to
1292
 
     * prevent deadlock (https://trac.pjsip.org/repos/ticket/1247).
1293
 
     * It should be safe to do this since the regc's refcount has been
1294
 
     * incremented.
1295
 
     */
1296
 
    pj_lock_release(regc->lock);
1297
 
 
1298
 
    /* Now send the message */
1299
 
    status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
1300
 
                                      regc, &regc_tsx_callback);
1301
 
    if (status!=PJ_SUCCESS) {
1302
 
        PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
1303
 
    }
1304
 
 
1305
 
    /* Reacquire the lock */
1306
 
    pj_lock_acquire(regc->lock);
1307
 
 
1308
 
    /* Get last transport used and add reference to it */
1309
 
    if (tdata->tp_info.transport != regc->last_transport &&
1310
 
        status==PJ_SUCCESS)
1311
 
    {
1312
 
        if (regc->last_transport) {
1313
 
            pjsip_transport_dec_ref(regc->last_transport);
1314
 
            regc->last_transport = NULL;
1315
 
        }
1316
 
 
1317
 
        if (tdata->tp_info.transport) {
1318
 
            regc->last_transport = tdata->tp_info.transport;
1319
 
            pjsip_transport_add_ref(regc->last_transport);
1320
 
        }
1321
 
    }
1322
 
 
1323
 
    /* Release tdata */
1324
 
    pjsip_tx_data_dec_ref(tdata);
1325
 
 
1326
 
    pj_lock_release(regc->lock);
1327
 
 
1328
 
    /* Delete the record if user destroy regc during the callback. */
1329
 
    if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
1330
 
        pjsip_regc_destroy(regc);
1331
 
    }
1332
 
 
1333
 
    return status;
1334
 
}
1335
 
 
1336