blob: 5190bb0f3beb06d92400d97dcc711309ece5fd28 [file] [log] [blame]
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono5dcb38d2005-11-21 01:55:47 +00005 *
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 */
Benny Prijonoccf95622006-02-07 18:48:01 +000020#include <pjsip-ua/sip_regc.h>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000021#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>
Benny Prijonoccf95622006-02-07 18:48:01 +000028#include <pjsip/sip_errno.h>
Benny Prijonoccf95622006-02-07 18:48:01 +000029#include <pj/assert.h>
Benny Prijonobcaed6c2006-02-19 15:37:19 +000030#include <pj/guid.h>
Benny Prijono11893652008-06-06 22:52:48 +000031#include <pj/lock.h>
Benny Prijonobcaed6c2006-02-19 15:37:19 +000032#include <pj/os.h>
33#include <pj/pool.h>
34#include <pj/log.h>
Benny Prijono59ca70f2006-02-22 22:18:58 +000035#include <pj/rand.h>
Benny Prijonobcaed6c2006-02-19 15:37:19 +000036#include <pj/string.h>
Benny Prijonoccf95622006-02-07 18:48:01 +000037
Benny Prijono5dcb38d2005-11-21 01:55:47 +000038
39#define REFRESH_TIMER 1
Benny Prijonodd742da2008-05-17 12:45:00 +000040#define DELAY_BEFORE_REFRESH PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH
Benny Prijono315999b2007-09-30 10:58:36 +000041#define THIS_FILE "sip_reg.c"
Benny Prijono5dcb38d2005-11-21 01:55:47 +000042
Benny Prijono0f35f912007-02-05 18:59:31 +000043/* 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
Benny Prijonodd742da2008-05-17 12:45:00 +000048enum { NOEXP = 0x1FFFFFFF };
49
50static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 };
51
52
53/* Current/pending operation */
54enum regc_op
55{
56 REGC_IDLE,
57 REGC_REGISTERING,
58 REGC_UNREGISTERING
59};
Benny Prijono0f35f912007-02-05 18:59:31 +000060
Benny Prijono5dcb38d2005-11-21 01:55:47 +000061/**
62 * SIP client registration structure.
63 */
64struct pjsip_regc
65{
Benny Prijono84126ab2006-02-09 09:30:09 +000066 pj_pool_t *pool;
67 pjsip_endpoint *endpt;
Benny Prijono11893652008-06-06 22:52:48 +000068 pj_lock_t *lock;
Benny Prijono84126ab2006-02-09 09:30:09 +000069 pj_bool_t _delete_flag;
Benny Prijono25a86c72006-12-01 20:50:01 +000070 pj_bool_t has_tsx;
Benny Prijono11893652008-06-06 22:52:48 +000071 pj_atomic_t *busy_ctr;
Benny Prijonodd742da2008-05-17 12:45:00 +000072 enum regc_op current_op;
73
74 pj_bool_t add_xuid_param;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000075
Benny Prijono84126ab2006-02-09 09:30:09 +000076 void *token;
77 pjsip_regc_cb *cb;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000078
Benny Prijono84126ab2006-02-09 09:30:09 +000079 pj_str_t str_srv_url;
80 pjsip_uri *srv_url;
81 pjsip_cid_hdr *cid_hdr;
82 pjsip_cseq_hdr *cseq_hdr;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000083 pj_str_t from_uri;
Benny Prijono84126ab2006-02-09 09:30:09 +000084 pjsip_from_hdr *from_hdr;
85 pjsip_to_hdr *to_hdr;
Benny Prijonodd742da2008-05-17 12:45:00 +000086 pjsip_contact_hdr contact_hdr_list;
87 pjsip_contact_hdr removed_contact_hdr_list;
Benny Prijono84126ab2006-02-09 09:30:09 +000088 pjsip_expires_hdr *expires_hdr;
Benny Prijono84126ab2006-02-09 09:30:09 +000089 pj_uint32_t expires;
90 pjsip_route_hdr route_set;
Benny Prijono8fc6de02006-11-11 21:25:55 +000091 pjsip_hdr hdr_list;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000092
Benny Prijono5dcb38d2005-11-21 01:55:47 +000093 /* Authorization sessions. */
Benny Prijono84126ab2006-02-09 09:30:09 +000094 pjsip_auth_clt_sess auth_sess;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000095
96 /* Auto refresh registration. */
Benny Prijono84126ab2006-02-09 09:30:09 +000097 pj_bool_t auto_reg;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000098 pj_time_val last_reg;
99 pj_time_val next_reg;
Benny Prijono84126ab2006-02-09 09:30:09 +0000100 pj_timer_entry timer;
Benny Prijono720d0a82007-01-12 06:37:35 +0000101
102 /* Transport selector */
103 pjsip_tpselector tp_sel;
Benny Prijonobdc093f2007-10-04 09:48:25 +0000104
105 /* Last transport used. We acquire the transport to keep
106 * it open.
107 */
108 pjsip_transport *last_transport;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000109};
110
111
Benny Prijonoccf95622006-02-07 18:48:01 +0000112PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
113 pjsip_regc_cb *cb,
114 pjsip_regc **p_regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000115{
116 pj_pool_t *pool;
117 pjsip_regc *regc;
Benny Prijonoccf95622006-02-07 18:48:01 +0000118 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000119
Benny Prijonoccf95622006-02-07 18:48:01 +0000120 /* Verify arguments. */
121 PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000122
123 pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
Benny Prijonoccf95622006-02-07 18:48:01 +0000124 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
125
Benny Prijonoa1e69682007-05-11 15:14:34 +0000126 regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000127
128 regc->pool = pool;
129 regc->endpt = endpt;
130 regc->token = token;
131 regc->cb = cb;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000132 regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
Benny Prijonodd742da2008-05-17 12:45:00 +0000133 regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000134
Benny Prijono11893652008-06-06 22:52:48 +0000135 status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
136 &regc->lock);
137 if (status != PJ_SUCCESS) {
138 pj_pool_release(pool);
139 return status;
140 }
141
142 status = pj_atomic_create(pool, 0, &regc->busy_ctr);
143 if (status != PJ_SUCCESS) {
144 pj_lock_destroy(regc->lock);
145 pj_pool_release(pool);
146 return status;
147 }
148
Benny Prijonoccf95622006-02-07 18:48:01 +0000149 status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
150 if (status != PJ_SUCCESS)
151 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000152
Benny Prijono84126ab2006-02-09 09:30:09 +0000153 pj_list_init(&regc->route_set);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000154 pj_list_init(&regc->hdr_list);
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000155 pj_list_init(&regc->contact_hdr_list);
Benny Prijonodd742da2008-05-17 12:45:00 +0000156 pj_list_init(&regc->removed_contact_hdr_list);
Benny Prijono84126ab2006-02-09 09:30:09 +0000157
Benny Prijonoccf95622006-02-07 18:48:01 +0000158 /* Done */
159 *p_regc = regc;
160 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000161}
162
163
Benny Prijonoccf95622006-02-07 18:48:01 +0000164PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000165{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000166 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
167
Benny Prijono11893652008-06-06 22:52:48 +0000168 pj_lock_acquire(regc->lock);
169 if (regc->has_tsx || pj_atomic_get(regc->busy_ctr) != 0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000170 regc->_delete_flag = 1;
171 regc->cb = NULL;
Benny Prijono11893652008-06-06 22:52:48 +0000172 pj_lock_release(regc->lock);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000173 } else {
Benny Prijono720d0a82007-01-12 06:37:35 +0000174 pjsip_tpselector_dec_ref(&regc->tp_sel);
Benny Prijonobdc093f2007-10-04 09:48:25 +0000175 if (regc->last_transport) {
176 pjsip_transport_dec_ref(regc->last_transport);
177 regc->last_transport = NULL;
178 }
Benny Prijonodd742da2008-05-17 12:45:00 +0000179 if (regc->timer.id != 0) {
180 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
181 regc->timer.id = 0;
182 }
Benny Prijono11893652008-06-06 22:52:48 +0000183 pj_atomic_destroy(regc->busy_ctr);
184 pj_lock_release(regc->lock);
185 pj_lock_destroy(regc->lock);
186 regc->lock = NULL;
Benny Prijonoccf95622006-02-07 18:48:01 +0000187 pjsip_endpt_release_pool(regc->endpt, regc->pool);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000188 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000189
190 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000191}
192
193
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000194PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
195 pjsip_regc_info *info )
196{
197 PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
198
Benny Prijono11893652008-06-06 22:52:48 +0000199 pj_lock_acquire(regc->lock);
200
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000201 info->server_uri = regc->str_srv_url;
202 info->client_uri = regc->from_uri;
Benny Prijono11893652008-06-06 22:52:48 +0000203 info->is_busy = (pj_atomic_get(regc->busy_ctr) || regc->has_tsx);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000204 info->auto_reg = regc->auto_reg;
205 info->interval = regc->expires;
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +0000206 info->transport = regc->last_transport;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000207
Benny Prijono25a86c72006-12-01 20:50:01 +0000208 if (regc->has_tsx)
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000209 info->next_reg = 0;
210 else if (regc->auto_reg == 0)
211 info->next_reg = 0;
212 else if (regc->expires < 0)
213 info->next_reg = regc->expires;
214 else {
215 pj_time_val now, next_reg;
216
217 next_reg = regc->next_reg;
218 pj_gettimeofday(&now);
219 PJ_TIME_VAL_SUB(next_reg, now);
220 info->next_reg = next_reg.sec;
221 }
222
Benny Prijono11893652008-06-06 22:52:48 +0000223 pj_lock_release(regc->lock);
224
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000225 return PJ_SUCCESS;
226}
227
228
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000229PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
230{
231 return regc->pool;
232}
233
234static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
235{
236 if (expires != regc->expires) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000237 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000238 } else {
239 regc->expires_hdr = NULL;
240 }
241}
242
243
244static pj_status_t set_contact( pjsip_regc *regc,
245 int contact_cnt,
246 const pj_str_t contact[] )
247{
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000248 const pj_str_t CONTACT = { "Contact", 7 };
Benny Prijonodd742da2008-05-17 12:45:00 +0000249 pjsip_contact_hdr *h;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000250 int i;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000251
Benny Prijonodd742da2008-05-17 12:45:00 +0000252 /* Save existing contact list to removed_contact_hdr_list and
253 * clear contact_hdr_list.
254 */
255 pj_list_merge_last(&regc->removed_contact_hdr_list,
256 &regc->contact_hdr_list);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000257
Benny Prijonodd742da2008-05-17 12:45:00 +0000258 /* Set the expiration of Contacts in to removed_contact_hdr_list
259 * zero.
260 */
261 h = regc->removed_contact_hdr_list.next;
262 while (h != &regc->removed_contact_hdr_list) {
263 h->expires = 0;
264 h = h->next;
265 }
266
267 /* Process new contacts */
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000268 for (i=0; i<contact_cnt; ++i) {
Benny Prijonodd742da2008-05-17 12:45:00 +0000269 pjsip_contact_hdr *hdr;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000270 pj_str_t tmp;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000271
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000272 pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
Benny Prijonodd742da2008-05-17 12:45:00 +0000273 hdr = (pjsip_contact_hdr*)
Benny Prijono91a5a3a2007-09-24 21:16:48 +0000274 pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000275 if (hdr == NULL) {
Nanang Izzuddin5af37ff2009-08-05 18:41:23 +0000276 PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"",
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000277 (int)tmp.slen, tmp.ptr));
278 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000279 }
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000280
Benny Prijonodd742da2008-05-17 12:45:00 +0000281 /* Find the new contact in old contact list. If found, remove
282 * the old header from the old header list.
283 */
284 h = regc->removed_contact_hdr_list.next;
285 while (h != &regc->removed_contact_hdr_list) {
286 int rc;
287
288 rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
289 h->uri, hdr->uri);
290 if (rc == 0) {
291 /* Match */
292 pj_list_erase(h);
293 break;
294 }
295
296 h = h->next;
297 }
298
299 /* If add_xuid_param option is enabled and Contact URI is sip/sips,
300 * add xuid parameter to assist matching the Contact URI in the
301 * REGISTER response later.
302 */
303 if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
304 PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))
305 {
306 pjsip_param *xuid_param;
307 pjsip_sip_uri *sip_uri;
308
309 xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
310 xuid_param->name = XUID_PARAM_NAME;
311 pj_create_unique_string(regc->pool, &xuid_param->value);
312
Benny Prijono89ac2b42008-06-13 12:52:56 +0000313 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
Benny Prijonodd742da2008-05-17 12:45:00 +0000314 pj_list_push_back(&sip_uri->other_param, xuid_param);
315 }
316
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000317 pj_list_push_back(&regc->contact_hdr_list, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000318 }
319
Benny Prijonoccf95622006-02-07 18:48:01 +0000320 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000321}
322
323
324PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
325 const pj_str_t *srv_url,
326 const pj_str_t *from_url,
327 const pj_str_t *to_url,
328 int contact_cnt,
329 const pj_str_t contact[],
330 pj_uint32_t expires)
331{
332 pj_str_t tmp;
Benny Prijonoccf95622006-02-07 18:48:01 +0000333 pj_status_t status;
334
335 PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
336 contact_cnt && contact && expires, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000337
338 /* Copy server URL. */
339 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
340
341 /* Set server URL. */
342 tmp = regc->str_srv_url;
343 regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
344 if (regc->srv_url == NULL) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000345 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000346 }
347
348 /* Set "From" header. */
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000349 pj_strdup_with_null(regc->pool, &regc->from_uri, from_url);
350 tmp = regc->from_uri;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000351 regc->from_hdr = pjsip_from_hdr_create(regc->pool);
352 regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
353 PJSIP_PARSE_URI_AS_NAMEADDR);
354 if (!regc->from_hdr->uri) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000355 PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
356 from_url->slen, from_url->ptr));
357 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000358 }
359
360 /* Set "To" header. */
361 pj_strdup_with_null(regc->pool, &tmp, to_url);
362 regc->to_hdr = pjsip_to_hdr_create(regc->pool);
363 regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
364 PJSIP_PARSE_URI_AS_NAMEADDR);
365 if (!regc->to_hdr->uri) {
366 PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
Benny Prijonoccf95622006-02-07 18:48:01 +0000367 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000368 }
369
370
371 /* Set "Contact" header. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000372 status = set_contact( regc, contact_cnt, contact);
373 if (status != PJ_SUCCESS)
374 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000375
376 /* Set "Expires" header, if required. */
377 set_expires( regc, expires);
378
379 /* Set "Call-ID" header. */
380 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
381 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
382
383 /* Set "CSeq" header. */
384 regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
Benny Prijono59ca70f2006-02-22 22:18:58 +0000385 regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000386 pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
387
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000388 /* Done. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000389 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000390}
391
392PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
393 int count,
394 const pjsip_cred_info cred[] )
395{
Benny Prijonoccf95622006-02-07 18:48:01 +0000396 PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
397 return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000398}
399
Benny Prijono48ab2b72007-11-08 09:24:30 +0000400PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
401 const pjsip_auth_clt_pref *pref)
402{
403 PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
404 return pjsip_auth_clt_set_prefs(&regc->auth_sess, pref);
405}
406
Benny Prijono84126ab2006-02-09 09:30:09 +0000407PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
408 const pjsip_route_hdr *route_set)
409{
410 const pjsip_route_hdr *chdr;
411
412 PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
413
414 pj_list_init(&regc->route_set);
415
416 chdr = route_set->next;
417 while (chdr != route_set) {
418 pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
419 chdr = chdr->next;
420 }
421
422 return PJ_SUCCESS;
423}
424
Benny Prijono720d0a82007-01-12 06:37:35 +0000425
426/*
427 * Bind client registration to a specific transport/listener.
428 */
429PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
430 const pjsip_tpselector *sel)
431{
432 PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
433
434 pjsip_tpselector_dec_ref(&regc->tp_sel);
435 pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
436 pjsip_tpselector_add_ref(&regc->tp_sel);
437
438 return PJ_SUCCESS;
439}
440
441
Benny Prijono8fc6de02006-11-11 21:25:55 +0000442PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
443 const pjsip_hdr *hdr_list)
444{
445 const pjsip_hdr *hdr;
446
447 PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
448
449 //This is "add" operation, so don't remove headers.
450 //pj_list_init(&regc->hdr_list);
451
452 hdr = hdr_list->next;
453 while (hdr != hdr_list) {
454 pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
455 hdr = hdr->next;
456 }
457
458 return PJ_SUCCESS;
459}
460
Benny Prijonoccf95622006-02-07 18:48:01 +0000461static pj_status_t create_request(pjsip_regc *regc,
462 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000463{
Benny Prijonoccf95622006-02-07 18:48:01 +0000464 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000465 pjsip_tx_data *tdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000466
Benny Prijonoccf95622006-02-07 18:48:01 +0000467 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000468
Benny Prijonoccf95622006-02-07 18:48:01 +0000469 /* Create the request. */
470 status = pjsip_endpt_create_request_from_hdr( regc->endpt,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000471 pjsip_get_register_method(),
Benny Prijonoccf95622006-02-07 18:48:01 +0000472 regc->srv_url,
473 regc->from_hdr,
474 regc->to_hdr,
475 NULL,
476 regc->cid_hdr,
477 regc->cseq_hdr->cseq,
478 NULL,
479 &tdata);
480 if (status != PJ_SUCCESS)
481 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000482
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000483 /* Add cached authorization headers. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000484 pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000485
Benny Prijono84126ab2006-02-09 09:30:09 +0000486 /* Add Route headers from route set, ideally after Via header */
487 if (!pj_list_empty(&regc->route_set)) {
488 pjsip_hdr *route_pos;
489 const pjsip_route_hdr *route;
490
Benny Prijonoa1e69682007-05-11 15:14:34 +0000491 route_pos = (pjsip_hdr*)
492 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000493 if (!route_pos)
494 route_pos = &tdata->msg->hdr;
495
496 route = regc->route_set.next;
497 while (route != &regc->route_set) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000498 pjsip_hdr *new_hdr = (pjsip_hdr*)
499 pjsip_hdr_shallow_clone(tdata->pool, route);
Benny Prijono84126ab2006-02-09 09:30:09 +0000500 pj_list_insert_after(route_pos, new_hdr);
501 route_pos = new_hdr;
502 route = route->next;
503 }
504 }
505
Benny Prijono8fc6de02006-11-11 21:25:55 +0000506 /* Add additional request headers */
507 if (!pj_list_empty(&regc->hdr_list)) {
508 const pjsip_hdr *hdr;
509
510 hdr = regc->hdr_list.next;
511 while (hdr != &regc->hdr_list) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000512 pjsip_hdr *new_hdr = (pjsip_hdr*)
513 pjsip_hdr_shallow_clone(tdata->pool, hdr);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000514 pjsip_msg_add_hdr(tdata->msg, new_hdr);
515 hdr = hdr->next;
516 }
517 }
518
Benny Prijonoccf95622006-02-07 18:48:01 +0000519 /* Done. */
520 *p_tdata = tdata;
521 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000522}
523
524
Benny Prijonoccf95622006-02-07 18:48:01 +0000525PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
526 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000527{
528 pjsip_msg *msg;
Benny Prijonodd742da2008-05-17 12:45:00 +0000529 pjsip_contact_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000530 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000531 pjsip_tx_data *tdata;
532
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000533 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
534
Benny Prijono11893652008-06-06 22:52:48 +0000535 pj_lock_acquire(regc->lock);
536
Benny Prijonoccf95622006-02-07 18:48:01 +0000537 status = create_request(regc, &tdata);
Benny Prijono11893652008-06-06 22:52:48 +0000538 if (status != PJ_SUCCESS) {
539 pj_lock_release(regc->lock);
Benny Prijonoccf95622006-02-07 18:48:01 +0000540 return status;
Benny Prijono11893652008-06-06 22:52:48 +0000541 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000542
543 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000544
545 /* Add Contact headers. */
546 hdr = regc->contact_hdr_list.next;
547 while (hdr != &regc->contact_hdr_list) {
548 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
549 pjsip_hdr_shallow_clone(tdata->pool, hdr));
550 hdr = hdr->next;
551 }
552
Benny Prijonodd742da2008-05-17 12:45:00 +0000553 /* Also add bindings which are to be removed */
554 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
555 hdr = regc->removed_contact_hdr_list.next;
556 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
557 pjsip_hdr_clone(tdata->pool, hdr));
558 pj_list_erase(hdr);
559 }
560
561
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000562 if (regc->expires_hdr)
Benny Prijonoa1e69682007-05-11 15:14:34 +0000563 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
564 pjsip_hdr_shallow_clone(tdata->pool,
Benny Prijono4093f7c2006-09-13 23:48:45 +0000565 regc->expires_hdr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000566
567 if (regc->timer.id != 0) {
568 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
569 regc->timer.id = 0;
570 }
571
572 regc->auto_reg = autoreg;
573
Benny Prijono11893652008-06-06 22:52:48 +0000574 pj_lock_release(regc->lock);
575
Benny Prijonoccf95622006-02-07 18:48:01 +0000576 /* Done */
577 *p_tdata = tdata;
578 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000579}
580
581
Benny Prijonoccf95622006-02-07 18:48:01 +0000582PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
583 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000584{
585 pjsip_tx_data *tdata;
586 pjsip_msg *msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000587 pjsip_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000588 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000589
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000590 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
591
Benny Prijono11893652008-06-06 22:52:48 +0000592 pj_lock_acquire(regc->lock);
593
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000594 if (regc->timer.id != 0) {
595 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
596 regc->timer.id = 0;
597 }
598
Benny Prijonoccf95622006-02-07 18:48:01 +0000599 status = create_request(regc, &tdata);
Benny Prijono11893652008-06-06 22:52:48 +0000600 if (status != PJ_SUCCESS) {
601 pj_lock_release(regc->lock);
Benny Prijonoccf95622006-02-07 18:48:01 +0000602 return status;
Benny Prijono11893652008-06-06 22:52:48 +0000603 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000604
605 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000606
607 /* Add Contact headers. */
Benny Prijonodd742da2008-05-17 12:45:00 +0000608 hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
Benny Prijono43047252008-06-04 14:44:29 +0000609 while ((void*)hdr != (void*)&regc->contact_hdr_list) {
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000610 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
611 pjsip_hdr_shallow_clone(tdata->pool, hdr));
612 hdr = hdr->next;
613 }
614
Benny Prijonodd742da2008-05-17 12:45:00 +0000615 /* Also add bindings which are to be removed */
616 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
617 hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next;
618 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
619 pjsip_hdr_clone(tdata->pool, hdr));
620 pj_list_erase(hdr);
621 }
622
623 /* Add Expires:0 header */
624 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
625 pjsip_msg_add_hdr(msg, hdr);
Benny Prijonodfc4c482006-12-02 07:25:29 +0000626
Benny Prijono11893652008-06-06 22:52:48 +0000627 pj_lock_release(regc->lock);
628
Benny Prijonodfc4c482006-12-02 07:25:29 +0000629 *p_tdata = tdata;
630 return PJ_SUCCESS;
631}
632
633PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
634 pjsip_tx_data **p_tdata)
635{
636 pjsip_tx_data *tdata;
Benny Prijonodd742da2008-05-17 12:45:00 +0000637 pjsip_contact_hdr *hcontact;
638 pjsip_hdr *hdr;
Benny Prijonodfc4c482006-12-02 07:25:29 +0000639 pjsip_msg *msg;
640 pj_status_t status;
641
642 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
643
Benny Prijono11893652008-06-06 22:52:48 +0000644 pj_lock_acquire(regc->lock);
645
Benny Prijonodfc4c482006-12-02 07:25:29 +0000646 if (regc->timer.id != 0) {
647 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
648 regc->timer.id = 0;
649 }
650
651 status = create_request(regc, &tdata);
Benny Prijono11893652008-06-06 22:52:48 +0000652 if (status != PJ_SUCCESS) {
653 pj_lock_release(regc->lock);
Benny Prijonodfc4c482006-12-02 07:25:29 +0000654 return status;
Benny Prijono11893652008-06-06 22:52:48 +0000655 }
Benny Prijonodfc4c482006-12-02 07:25:29 +0000656
657 msg = tdata->msg;
Benny Prijonodd742da2008-05-17 12:45:00 +0000658
659 /* Clear removed_contact_hdr_list */
660 pj_list_init(&regc->removed_contact_hdr_list);
661
662 /* Add Contact:* header */
663 hcontact = pjsip_contact_hdr_create(tdata->pool);
664 hcontact->star = 1;
665 pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
666
667 /* Add Expires:0 header */
668 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
669 pjsip_msg_add_hdr(msg, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000670
Benny Prijono11893652008-06-06 22:52:48 +0000671 pj_lock_release(regc->lock);
672
Benny Prijonoccf95622006-02-07 18:48:01 +0000673 *p_tdata = tdata;
674 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000675}
676
677
678PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
679 int contact_cnt,
680 const pj_str_t contact[] )
681{
Benny Prijono11893652008-06-06 22:52:48 +0000682 pj_status_t status;
683
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000684 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono11893652008-06-06 22:52:48 +0000685
686 pj_lock_acquire(regc->lock);
687 status = set_contact( regc, contact_cnt, contact );
688 pj_lock_release(regc->lock);
689
690 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000691}
692
693
694PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
695 pj_uint32_t expires )
696{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000697 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono11893652008-06-06 22:52:48 +0000698
699 pj_lock_acquire(regc->lock);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000700 set_expires( regc, expires );
Benny Prijono11893652008-06-06 22:52:48 +0000701 pj_lock_release(regc->lock);
702
Benny Prijonoccf95622006-02-07 18:48:01 +0000703 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000704}
705
706
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000707static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
708 const pj_str_t *reason,
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000709 pjsip_rx_data *rdata, pj_int32_t expiration,
710 int contact_cnt, pjsip_contact_hdr *contact[])
711{
712 struct pjsip_regc_cbparam cbparam;
713
714
Benny Prijonof2651802007-01-26 17:13:56 +0000715 if (!regc->cb)
716 return;
717
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000718 cbparam.regc = regc;
719 cbparam.token = regc->token;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000720 cbparam.status = status;
721 cbparam.code = st_code;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000722 cbparam.reason = *reason;
723 cbparam.rdata = rdata;
724 cbparam.contact_cnt = contact_cnt;
725 cbparam.expiration = expiration;
726 if (contact_cnt) {
727 pj_memcpy( cbparam.contact, contact,
728 contact_cnt*sizeof(pjsip_contact_hdr*));
729 }
730
731 (*regc->cb)(&cbparam);
732}
733
734static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
735 struct pj_timer_entry *entry)
736{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000737 pjsip_regc *regc = (pjsip_regc*) entry->user_data;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000738 pjsip_tx_data *tdata;
Benny Prijonoccf95622006-02-07 18:48:01 +0000739 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000740
Benny Prijonoccf95622006-02-07 18:48:01 +0000741 PJ_UNUSED_ARG(timer_heap);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000742
Benny Prijonof2651802007-01-26 17:13:56 +0000743 /* Temporarily increase busy flag to prevent regc from being deleted
Benny Prijono11893652008-06-06 22:52:48 +0000744 * in pjsip_regc_send() or in the callback
Benny Prijonof2651802007-01-26 17:13:56 +0000745 */
Benny Prijono11893652008-06-06 22:52:48 +0000746 pj_atomic_inc(regc->busy_ctr);
Benny Prijonof2651802007-01-26 17:13:56 +0000747
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000748 entry->id = 0;
Benny Prijonoccf95622006-02-07 18:48:01 +0000749 status = pjsip_regc_register(regc, 1, &tdata);
750 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000751 status = pjsip_regc_send(regc, tdata);
752 }
753
Benny Prijonof2651802007-01-26 17:13:56 +0000754 if (status != PJ_SUCCESS && regc->cb) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000755 char errmsg[PJ_ERR_MSG_SIZE];
756 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000757 call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000758 }
Benny Prijonof2651802007-01-26 17:13:56 +0000759
Benny Prijonof2651802007-01-26 17:13:56 +0000760 /* Delete the record if user destroy regc during the callback. */
Benny Prijono11893652008-06-06 22:52:48 +0000761 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
Benny Prijonof2651802007-01-26 17:13:56 +0000762 pjsip_regc_destroy(regc);
763 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000764}
765
Benny Prijonodd742da2008-05-17 12:45:00 +0000766static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
767 const pjsip_rx_data *rdata,
768 unsigned *contact_cnt,
769 unsigned max_contact,
770 pjsip_contact_hdr *contacts[])
771{
772 pj_int32_t expiration = NOEXP;
773 const pjsip_msg *msg = rdata->msg_info.msg;
774 const pjsip_hdr *hdr;
775
776 /* Enumerate all Contact headers in the response */
777 *contact_cnt = 0;
778 for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
779 if (hdr->type == PJSIP_H_CONTACT &&
780 *contact_cnt < max_contact)
781 {
782 contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
783 ++(*contact_cnt);
784 }
785 }
786
787 if (regc->current_op == REGC_REGISTERING) {
788 pj_bool_t has_our_contact = PJ_FALSE;
789 const pjsip_expires_hdr *expires;
790
791 /* Get Expires header */
792 expires = (const pjsip_expires_hdr*)
793 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
794
795 /* Try to find the Contact URIs that we register, in the response
796 * to get the expires value. We'll try both with comparing the URI
797 * and comparing the extension param only.
798 */
799 if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
800 unsigned i;
801 for (i=0; i<*contact_cnt; ++i) {
802 const pjsip_contact_hdr *our_hdr;
803
804 our_hdr = (const pjsip_contact_hdr*)
805 regc->contact_hdr_list.next;
806
807 /* Match with our Contact header(s) */
808 while ((void*)our_hdr != (void*)&regc->contact_hdr_list) {
809
810 const pjsip_uri *uri1, *uri2;
811 pj_bool_t matched = PJ_FALSE;
812
813 /* Exclude the display name when comparing the URI
814 * since server may not return it.
815 */
816 uri1 = (const pjsip_uri*)
817 pjsip_uri_get_uri(contacts[i]->uri);
818 uri2 = (const pjsip_uri*)
819 pjsip_uri_get_uri(our_hdr->uri);
820
821 /* First try with exact matching, according to RFC 3261
822 * Section 19.1.4 URI Comparison
823 */
824 if (pjsip_cfg()->regc.check_contact) {
825 matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
826 uri1, uri2)==0;
827 }
828
829 /* If no match is found, try with matching the extension
830 * parameter only if extension parameter was added.
831 */
832 if (!matched && regc->add_xuid_param &&
833 (PJSIP_URI_SCHEME_IS_SIP(uri1) ||
834 PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&
835 (PJSIP_URI_SCHEME_IS_SIP(uri2) ||
836 PJSIP_URI_SCHEME_IS_SIPS(uri2)))
837 {
838 const pjsip_sip_uri *sip_uri1, *sip_uri2;
839 const pjsip_param *p1, *p2;
840
841 sip_uri1 = (const pjsip_sip_uri*)uri1;
842 sip_uri2 = (const pjsip_sip_uri*)uri2;
843
844 p1 = pjsip_param_cfind(&sip_uri1->other_param,
845 &XUID_PARAM_NAME);
846 p2 = pjsip_param_cfind(&sip_uri2->other_param,
847 &XUID_PARAM_NAME);
848 matched = p1 && p2 &&
849 pj_strcmp(&p1->value, &p2->value)==0;
850
851 }
852
853 if (matched) {
854 has_our_contact = PJ_TRUE;
855
856 if (contacts[i]->expires >= 0 &&
857 contacts[i]->expires < expiration)
858 {
859 /* Get the lowest expiration time. */
860 expiration = contacts[i]->expires;
861 }
862
863 break;
864 }
865
866 our_hdr = our_hdr->next;
867
868 } /* while ((void.. */
869
870 } /* for (i=.. */
871
872 /* If matching Contact header(s) are found but the
873 * header doesn't contain expires parameter, get the
874 * expiration value from the Expires header. And
875 * if Expires header is not present, get the expiration
876 * value from the request.
877 */
878 if (has_our_contact && expiration == NOEXP) {
879 if (expires) {
880 expiration = expires->ivalue;
881 } else if (regc->expires_hdr) {
882 expiration = regc->expires_hdr->ivalue;
883 } else {
884 /* We didn't request explicit expiration value,
885 * and server doesn't specify it either. This
886 * shouldn't happen unless we have a broken
887 * registrar.
888 */
889 expiration = 3600;
890 }
891 }
892
893 }
894
895 /* If we still couldn't get matching Contact header(s), it means
896 * there must be something wrong with the registrar (e.g. it may
897 * have modified the URI's in the response, which is prohibited).
898 */
899 if (expiration==NOEXP) {
900 /* If the number of Contact headers in the response matches
901 * ours, they're all probably ours. Get the expiration
902 * from there if this is the case, or from Expires header
903 * if we don't have exact Contact header count, or
904 * from the request as the last resort.
905 */
906 unsigned our_contact_cnt;
907
908 our_contact_cnt = pj_list_size(&regc->contact_hdr_list);
909
910 if (*contact_cnt == our_contact_cnt && *contact_cnt &&
911 contacts[0]->expires >= 0)
912 {
913 expiration = contacts[0]->expires;
914 } else if (expires)
915 expiration = expires->ivalue;
916 else if (regc->expires_hdr)
917 expiration = regc->expires_hdr->ivalue;
918 else
919 expiration = 3600;
920 }
921
922 } else {
923 /* Just assume that the unregistration has been successful. */
924 expiration = 0;
925 }
926
927 /* Must have expiration value by now */
928 pj_assert(expiration != NOEXP);
929
930 return expiration;
931}
932
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000933static void tsx_callback(void *token, pjsip_event *event)
934{
Benny Prijonoccf95622006-02-07 18:48:01 +0000935 pj_status_t status;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000936 pjsip_regc *regc = (pjsip_regc*) token;
Benny Prijonoccf95622006-02-07 18:48:01 +0000937 pjsip_transaction *tsx = event->body.tsx_state.tsx;
Benny Prijono38c975d2010-02-23 11:03:07 +0000938 pj_bool_t handled = PJ_TRUE;
Benny Prijono11893652008-06-06 22:52:48 +0000939
940 pj_atomic_inc(regc->busy_ctr);
941 pj_lock_acquire(regc->lock);
942
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000943 /* Decrement pending transaction counter. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000944 pj_assert(regc->has_tsx);
945 regc->has_tsx = PJ_FALSE;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000946
Benny Prijonobdc093f2007-10-04 09:48:25 +0000947 /* Add reference to the transport */
948 if (tsx->transport != regc->last_transport) {
949 if (regc->last_transport) {
950 pjsip_transport_dec_ref(regc->last_transport);
951 regc->last_transport = NULL;
952 }
953
954 if (tsx->transport) {
955 regc->last_transport = tsx->transport;
956 pjsip_transport_add_ref(regc->last_transport);
957 }
958 }
959
Benny Prijono58990232007-01-21 16:11:18 +0000960 /* Handle 401/407 challenge (even when _delete_flag is set) */
961 if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
962 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000963 {
Benny Prijonoccf95622006-02-07 18:48:01 +0000964 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000965 pjsip_tx_data *tdata;
966
Benny Prijonodd742da2008-05-17 12:45:00 +0000967 /* reset current op */
968 regc->current_op = REGC_IDLE;
969
Benny Prijonoccf95622006-02-07 18:48:01 +0000970 status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
971 rdata,
972 tsx->last_tx,
973 &tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000974
Benny Prijonoccf95622006-02-07 18:48:01 +0000975 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000976 status = pjsip_regc_send(regc, tdata);
Benny Prijono3fe034a2007-06-01 12:51:07 +0000977 }
Benny Prijono27042582006-08-08 14:04:21 +0000978
979 if (status != PJ_SUCCESS) {
Benny Prijono58990232007-01-21 16:11:18 +0000980
981 /* Only call callback if application is still interested
982 * in it.
983 */
984 if (regc->_delete_flag == 0) {
Benny Prijono11893652008-06-06 22:52:48 +0000985 /* Should be safe to release the lock temporarily.
986 * We do this to avoid deadlock.
Benny Prijono58990232007-01-21 16:11:18 +0000987 */
Benny Prijono11893652008-06-06 22:52:48 +0000988 pj_lock_release(regc->lock);
Benny Prijono58990232007-01-21 16:11:18 +0000989 call_callback(regc, status, tsx->status_code,
990 &rdata->msg_info.msg->line.status.reason,
991 rdata, -1, 0, NULL);
Benny Prijono11893652008-06-06 22:52:48 +0000992 pj_lock_acquire(regc->lock);
Benny Prijono58990232007-01-21 16:11:18 +0000993 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000994 }
Benny Prijono27042582006-08-08 14:04:21 +0000995
Benny Prijono58990232007-01-21 16:11:18 +0000996 } else if (regc->_delete_flag) {
997
998 /* User has called pjsip_regc_destroy(), so don't call callback.
999 * This regc will be destroyed later in this function.
1000 */
1001
Benny Prijonodd742da2008-05-17 12:45:00 +00001002 /* Just reset current op */
1003 regc->current_op = REGC_IDLE;
Benny Prijono27042582006-08-08 14:04:21 +00001004
Benny Prijono38c975d2010-02-23 11:03:07 +00001005 } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
1006 regc->current_op == REGC_REGISTERING)
1007 {
1008 /* Handle 423 response automatically:
1009 * - set requested expiration to Min-Expires header, ONLY IF
1010 * the original request is a registration (as opposed to
1011 * unregistration) and the requested expiration was indeed
1012 * lower than Min-Expires)
1013 * - resend the request
1014 */
1015 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1016 pjsip_min_expires_hdr *me_hdr;
1017 pjsip_tx_data *tdata;
1018 pj_int32_t min_exp;
1019
1020 /* reset current op */
1021 regc->current_op = REGC_IDLE;
1022
1023 /* Update requested expiration */
1024 me_hdr = (pjsip_min_expires_hdr*)
1025 pjsip_msg_find_hdr(rdata->msg_info.msg,
1026 PJSIP_H_MIN_EXPIRES, NULL);
1027 if (me_hdr) {
1028 min_exp = me_hdr->ivalue;
1029 } else {
1030 /* Broken server, Min-Expires doesn't exist.
1031 * Just guestimate then, BUT ONLY if if this is the
1032 * first time we received such response.
1033 */
1034 enum {
1035 /* Note: changing this value would require changing couple of
1036 * Python test scripts.
1037 */
1038 UNSPECIFIED_MIN_EXPIRES = 3601
1039 };
1040 if (!regc->expires_hdr ||
1041 regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
1042 {
1043 min_exp = UNSPECIFIED_MIN_EXPIRES;
1044 } else {
1045 handled = PJ_FALSE;
1046 PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
1047 "without Min-Expires header is invalid"));
1048 goto handle_err;
1049 }
1050 }
1051
1052 if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
1053 /* But we already send with greater expiration time, why does
1054 * the server send us with 423? Oh well, just fail the request.
1055 */
1056 handled = PJ_FALSE;
1057 PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
1058 "Min-Expires header value in response"));
1059 goto handle_err;
1060 }
1061
1062 set_expires(regc, min_exp);
1063
1064 status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
1065 if (status == PJ_SUCCESS) {
1066 status = pjsip_regc_send(regc, tdata);
1067 }
1068
1069 if (status != PJ_SUCCESS) {
1070 /* Only call callback if application is still interested
1071 * in it.
1072 */
1073 if (!regc->_delete_flag) {
1074 /* Should be safe to release the lock temporarily.
1075 * We do this to avoid deadlock.
1076 */
1077 pj_lock_release(regc->lock);
1078 call_callback(regc, status, tsx->status_code,
1079 &rdata->msg_info.msg->line.status.reason,
1080 rdata, -1, 0, NULL);
1081 pj_lock_acquire(regc->lock);
1082 }
1083 }
1084
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001085 } else {
Benny Prijono38c975d2010-02-23 11:03:07 +00001086 handled = PJ_FALSE;
1087 }
1088
1089handle_err:
1090 if (!handled) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001091 pjsip_rx_data *rdata;
Benny Prijonoe1c984f2007-11-04 02:05:13 +00001092 pj_int32_t expiration = NOEXP;
Benny Prijonodd742da2008-05-17 12:45:00 +00001093 unsigned contact_cnt = 0;
1094 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001095
1096 if (tsx->status_code/100 == 2) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001097
Benny Prijonoccf95622006-02-07 18:48:01 +00001098 rdata = event->body.tsx_state.src.rdata;
Benny Prijonoe1c984f2007-11-04 02:05:13 +00001099
Benny Prijonodd742da2008-05-17 12:45:00 +00001100 /* Calculate expiration */
1101 expiration = calculate_response_expiration(regc, rdata,
1102 &contact_cnt,
1103 PJSIP_REGC_MAX_CONTACT,
1104 contact);
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001105
Benny Prijonodd742da2008-05-17 12:45:00 +00001106 /* Mark operation as complete */
1107 regc->current_op = REGC_IDLE;
Benny Prijonoe1c984f2007-11-04 02:05:13 +00001108
1109 /* Schedule next registration */
Benny Prijonodd742da2008-05-17 12:45:00 +00001110 if (regc->auto_reg && expiration > 0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001111 pj_time_val delay = { 0, 0};
1112
1113 delay.sec = expiration - DELAY_BEFORE_REFRESH;
1114 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
1115 delay.sec > (pj_int32_t)regc->expires)
1116 {
1117 delay.sec = regc->expires;
1118 }
1119 if (delay.sec < DELAY_BEFORE_REFRESH)
1120 delay.sec = DELAY_BEFORE_REFRESH;
1121 regc->timer.cb = &regc_refresh_timer_cb;
1122 regc->timer.id = REFRESH_TIMER;
1123 regc->timer.user_data = regc;
1124 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001125 pj_gettimeofday(&regc->last_reg);
1126 regc->next_reg = regc->last_reg;
1127 regc->next_reg.sec += delay.sec;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001128 }
1129
1130 } else {
Benny Prijonoccf95622006-02-07 18:48:01 +00001131 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
1132 event->body.tsx_state.src.rdata : NULL;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001133 }
1134
Benny Prijonodd742da2008-05-17 12:45:00 +00001135 /* Update registration */
1136 if (expiration==NOEXP) expiration=-1;
1137 regc->expires = expiration;
1138
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001139 /* Call callback. */
Benny Prijono11893652008-06-06 22:52:48 +00001140 /* Should be safe to release the lock temporarily.
1141 * We do this to avoid deadlock.
1142 */
1143 pj_lock_release(regc->lock);
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001144 call_callback(regc, PJ_SUCCESS, tsx->status_code,
Benny Prijonoccf95622006-02-07 18:48:01 +00001145 (rdata ? &rdata->msg_info.msg->line.status.reason
Benny Prijono25bc2052009-06-02 08:17:56 +00001146 : &tsx->status_text),
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001147 rdata, expiration,
1148 contact_cnt, contact);
Benny Prijono11893652008-06-06 22:52:48 +00001149 pj_lock_acquire(regc->lock);
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001150 }
1151
Benny Prijono11893652008-06-06 22:52:48 +00001152 pj_lock_release(regc->lock);
1153
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001154 /* Delete the record if user destroy regc during the callback. */
Benny Prijono11893652008-06-06 22:52:48 +00001155 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001156 pjsip_regc_destroy(regc);
1157 }
1158}
1159
Benny Prijonoccf95622006-02-07 18:48:01 +00001160PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001161{
Benny Prijonoccf95622006-02-07 18:48:01 +00001162 pj_status_t status;
Benny Prijono59ca70f2006-02-22 22:18:58 +00001163 pjsip_cseq_hdr *cseq_hdr;
Benny Prijonodd742da2008-05-17 12:45:00 +00001164 pjsip_expires_hdr *expires_hdr;
Benny Prijono59ca70f2006-02-22 22:18:58 +00001165 pj_uint32_t cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001166
Benny Prijono11893652008-06-06 22:52:48 +00001167 pj_atomic_inc(regc->busy_ctr);
1168 pj_lock_acquire(regc->lock);
1169
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001170 /* Make sure we don't have pending transaction. */
Benny Prijono25a86c72006-12-01 20:50:01 +00001171 if (regc->has_tsx) {
Benny Prijono5b656872006-08-08 00:41:00 +00001172 PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
1173 "transaction pending"));
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001174 pjsip_tx_data_dec_ref( tdata );
Benny Prijono11893652008-06-06 22:52:48 +00001175 pj_lock_release(regc->lock);
1176 pj_atomic_dec(regc->busy_ctr);
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001177 return PJSIP_EBUSY;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001178 }
1179
Benny Prijonodd742da2008-05-17 12:45:00 +00001180 pj_assert(regc->current_op == REGC_IDLE);
1181
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001182 /* Invalidate message buffer. */
1183 pjsip_tx_data_invalidate_msg(tdata);
1184
1185 /* Increment CSeq */
Benny Prijono59ca70f2006-02-22 22:18:58 +00001186 cseq = ++regc->cseq_hdr->cseq;
Benny Prijonoa1e69682007-05-11 15:14:34 +00001187 cseq_hdr = (pjsip_cseq_hdr*)
1188 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
Benny Prijono59ca70f2006-02-22 22:18:58 +00001189 cseq_hdr->cseq = cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001190
Benny Prijonodd742da2008-05-17 12:45:00 +00001191 /* Find Expires header */
1192 expires_hdr = (pjsip_expires_hdr*)
1193 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
1194
Benny Prijono37db51c2007-11-23 03:41:54 +00001195 /* Bind to transport selector */
1196 pjsip_tx_data_set_transport(tdata, &regc->tp_sel);
1197
Benny Prijono25a86c72006-12-01 20:50:01 +00001198 regc->has_tsx = PJ_TRUE;
Benny Prijonodd742da2008-05-17 12:45:00 +00001199
1200 /* Set current operation based on the value of Expires header */
1201 if (expires_hdr && expires_hdr->ivalue==0)
1202 regc->current_op = REGC_UNREGISTERING;
1203 else
1204 regc->current_op = REGC_REGISTERING;
1205
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001206 /* Prevent deletion of tdata, e.g: when something wrong in sending,
1207 * we need tdata to retrieve the transport.
1208 */
1209 pjsip_tx_data_add_ref(tdata);
1210
Benny Prijono0f35f912007-02-05 18:59:31 +00001211 status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
1212 regc, &tsx_callback);
Benny Prijono5b656872006-08-08 00:41:00 +00001213 if (status!=PJ_SUCCESS) {
Benny Prijono5b656872006-08-08 00:41:00 +00001214 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
1215 }
Benny Prijono11893652008-06-06 22:52:48 +00001216
Nanang Izzuddin36dd5b62010-03-30 11:13:59 +00001217 /* Get last transport used and add reference to it */
1218 if (tdata->tp_info.transport != regc->last_transport) {
1219 if (regc->last_transport) {
1220 pjsip_transport_dec_ref(regc->last_transport);
1221 regc->last_transport = NULL;
1222 }
1223
1224 if (tdata->tp_info.transport) {
1225 regc->last_transport = tdata->tp_info.transport;
1226 pjsip_transport_add_ref(regc->last_transport);
1227 }
1228 }
1229
1230 /* Release tdata */
1231 pjsip_tx_data_dec_ref(tdata);
1232
Benny Prijono11893652008-06-06 22:52:48 +00001233 pj_lock_release(regc->lock);
Benny Prijono197cabf2006-10-16 20:05:27 +00001234
1235 /* Delete the record if user destroy regc during the callback. */
Benny Prijono11893652008-06-06 22:52:48 +00001236 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
Benny Prijono197cabf2006-10-16 20:05:27 +00001237 pjsip_regc_destroy(regc);
1238 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001239
1240 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001241}
1242
1243