blob: d08427dbef1a93adc0e96658649516024431696c [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;
206
Benny Prijono25a86c72006-12-01 20:50:01 +0000207 if (regc->has_tsx)
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000208 info->next_reg = 0;
209 else if (regc->auto_reg == 0)
210 info->next_reg = 0;
211 else if (regc->expires < 0)
212 info->next_reg = regc->expires;
213 else {
214 pj_time_val now, next_reg;
215
216 next_reg = regc->next_reg;
217 pj_gettimeofday(&now);
218 PJ_TIME_VAL_SUB(next_reg, now);
219 info->next_reg = next_reg.sec;
220 }
221
Benny Prijono11893652008-06-06 22:52:48 +0000222 pj_lock_release(regc->lock);
223
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000224 return PJ_SUCCESS;
225}
226
227
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000228PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
229{
230 return regc->pool;
231}
232
233static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
234{
235 if (expires != regc->expires) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000236 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000237 } else {
238 regc->expires_hdr = NULL;
239 }
240}
241
242
243static pj_status_t set_contact( pjsip_regc *regc,
244 int contact_cnt,
245 const pj_str_t contact[] )
246{
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000247 const pj_str_t CONTACT = { "Contact", 7 };
Benny Prijonodd742da2008-05-17 12:45:00 +0000248 pjsip_contact_hdr *h;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000249 int i;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000250
Benny Prijonodd742da2008-05-17 12:45:00 +0000251 /* Save existing contact list to removed_contact_hdr_list and
252 * clear contact_hdr_list.
253 */
254 pj_list_merge_last(&regc->removed_contact_hdr_list,
255 &regc->contact_hdr_list);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000256
Benny Prijonodd742da2008-05-17 12:45:00 +0000257 /* Set the expiration of Contacts in to removed_contact_hdr_list
258 * zero.
259 */
260 h = regc->removed_contact_hdr_list.next;
261 while (h != &regc->removed_contact_hdr_list) {
262 h->expires = 0;
263 h = h->next;
264 }
265
266 /* Process new contacts */
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000267 for (i=0; i<contact_cnt; ++i) {
Benny Prijonodd742da2008-05-17 12:45:00 +0000268 pjsip_contact_hdr *hdr;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000269 pj_str_t tmp;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000270
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000271 pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
Benny Prijonodd742da2008-05-17 12:45:00 +0000272 hdr = (pjsip_contact_hdr*)
Benny Prijono91a5a3a2007-09-24 21:16:48 +0000273 pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000274 if (hdr == NULL) {
275 PJ_LOG(4,(THIS_FILE, "Invalid Contact URI: \"%.*s\"",
276 (int)tmp.slen, tmp.ptr));
277 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000278 }
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000279
Benny Prijonodd742da2008-05-17 12:45:00 +0000280 /* Find the new contact in old contact list. If found, remove
281 * the old header from the old header list.
282 */
283 h = regc->removed_contact_hdr_list.next;
284 while (h != &regc->removed_contact_hdr_list) {
285 int rc;
286
287 rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
288 h->uri, hdr->uri);
289 if (rc == 0) {
290 /* Match */
291 pj_list_erase(h);
292 break;
293 }
294
295 h = h->next;
296 }
297
298 /* If add_xuid_param option is enabled and Contact URI is sip/sips,
299 * add xuid parameter to assist matching the Contact URI in the
300 * REGISTER response later.
301 */
302 if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
303 PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))
304 {
305 pjsip_param *xuid_param;
306 pjsip_sip_uri *sip_uri;
307
308 xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
309 xuid_param->name = XUID_PARAM_NAME;
310 pj_create_unique_string(regc->pool, &xuid_param->value);
311
Benny Prijono89ac2b42008-06-13 12:52:56 +0000312 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
Benny Prijonodd742da2008-05-17 12:45:00 +0000313 pj_list_push_back(&sip_uri->other_param, xuid_param);
314 }
315
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000316 pj_list_push_back(&regc->contact_hdr_list, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000317 }
318
Benny Prijonoccf95622006-02-07 18:48:01 +0000319 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000320}
321
322
323PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
324 const pj_str_t *srv_url,
325 const pj_str_t *from_url,
326 const pj_str_t *to_url,
327 int contact_cnt,
328 const pj_str_t contact[],
329 pj_uint32_t expires)
330{
331 pj_str_t tmp;
Benny Prijonoccf95622006-02-07 18:48:01 +0000332 pj_status_t status;
333
334 PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
335 contact_cnt && contact && expires, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000336
337 /* Copy server URL. */
338 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
339
340 /* Set server URL. */
341 tmp = regc->str_srv_url;
342 regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
343 if (regc->srv_url == NULL) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000344 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000345 }
346
347 /* Set "From" header. */
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000348 pj_strdup_with_null(regc->pool, &regc->from_uri, from_url);
349 tmp = regc->from_uri;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000350 regc->from_hdr = pjsip_from_hdr_create(regc->pool);
351 regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
352 PJSIP_PARSE_URI_AS_NAMEADDR);
353 if (!regc->from_hdr->uri) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000354 PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
355 from_url->slen, from_url->ptr));
356 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000357 }
358
359 /* Set "To" header. */
360 pj_strdup_with_null(regc->pool, &tmp, to_url);
361 regc->to_hdr = pjsip_to_hdr_create(regc->pool);
362 regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
363 PJSIP_PARSE_URI_AS_NAMEADDR);
364 if (!regc->to_hdr->uri) {
365 PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
Benny Prijonoccf95622006-02-07 18:48:01 +0000366 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000367 }
368
369
370 /* Set "Contact" header. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000371 status = set_contact( regc, contact_cnt, contact);
372 if (status != PJ_SUCCESS)
373 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000374
375 /* Set "Expires" header, if required. */
376 set_expires( regc, expires);
377
378 /* Set "Call-ID" header. */
379 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
380 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
381
382 /* Set "CSeq" header. */
383 regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
Benny Prijono59ca70f2006-02-22 22:18:58 +0000384 regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000385 pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
386
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000387 /* Done. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000388 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000389}
390
391PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
392 int count,
393 const pjsip_cred_info cred[] )
394{
Benny Prijonoccf95622006-02-07 18:48:01 +0000395 PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
396 return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000397}
398
Benny Prijono48ab2b72007-11-08 09:24:30 +0000399PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
400 const pjsip_auth_clt_pref *pref)
401{
402 PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
403 return pjsip_auth_clt_set_prefs(&regc->auth_sess, pref);
404}
405
Benny Prijono84126ab2006-02-09 09:30:09 +0000406PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
407 const pjsip_route_hdr *route_set)
408{
409 const pjsip_route_hdr *chdr;
410
411 PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
412
413 pj_list_init(&regc->route_set);
414
415 chdr = route_set->next;
416 while (chdr != route_set) {
417 pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
418 chdr = chdr->next;
419 }
420
421 return PJ_SUCCESS;
422}
423
Benny Prijono720d0a82007-01-12 06:37:35 +0000424
425/*
426 * Bind client registration to a specific transport/listener.
427 */
428PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
429 const pjsip_tpselector *sel)
430{
431 PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
432
433 pjsip_tpselector_dec_ref(&regc->tp_sel);
434 pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
435 pjsip_tpselector_add_ref(&regc->tp_sel);
436
437 return PJ_SUCCESS;
438}
439
440
Benny Prijono8fc6de02006-11-11 21:25:55 +0000441PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
442 const pjsip_hdr *hdr_list)
443{
444 const pjsip_hdr *hdr;
445
446 PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
447
448 //This is "add" operation, so don't remove headers.
449 //pj_list_init(&regc->hdr_list);
450
451 hdr = hdr_list->next;
452 while (hdr != hdr_list) {
453 pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
454 hdr = hdr->next;
455 }
456
457 return PJ_SUCCESS;
458}
459
Benny Prijonoccf95622006-02-07 18:48:01 +0000460static pj_status_t create_request(pjsip_regc *regc,
461 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000462{
Benny Prijonoccf95622006-02-07 18:48:01 +0000463 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000464 pjsip_tx_data *tdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000465
Benny Prijonoccf95622006-02-07 18:48:01 +0000466 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000467
Benny Prijonoccf95622006-02-07 18:48:01 +0000468 /* Create the request. */
469 status = pjsip_endpt_create_request_from_hdr( regc->endpt,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000470 pjsip_get_register_method(),
Benny Prijonoccf95622006-02-07 18:48:01 +0000471 regc->srv_url,
472 regc->from_hdr,
473 regc->to_hdr,
474 NULL,
475 regc->cid_hdr,
476 regc->cseq_hdr->cseq,
477 NULL,
478 &tdata);
479 if (status != PJ_SUCCESS)
480 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000481
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000482 /* Add cached authorization headers. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000483 pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000484
Benny Prijono84126ab2006-02-09 09:30:09 +0000485 /* Add Route headers from route set, ideally after Via header */
486 if (!pj_list_empty(&regc->route_set)) {
487 pjsip_hdr *route_pos;
488 const pjsip_route_hdr *route;
489
Benny Prijonoa1e69682007-05-11 15:14:34 +0000490 route_pos = (pjsip_hdr*)
491 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000492 if (!route_pos)
493 route_pos = &tdata->msg->hdr;
494
495 route = regc->route_set.next;
496 while (route != &regc->route_set) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000497 pjsip_hdr *new_hdr = (pjsip_hdr*)
498 pjsip_hdr_shallow_clone(tdata->pool, route);
Benny Prijono84126ab2006-02-09 09:30:09 +0000499 pj_list_insert_after(route_pos, new_hdr);
500 route_pos = new_hdr;
501 route = route->next;
502 }
503 }
504
Benny Prijono8fc6de02006-11-11 21:25:55 +0000505 /* Add additional request headers */
506 if (!pj_list_empty(&regc->hdr_list)) {
507 const pjsip_hdr *hdr;
508
509 hdr = regc->hdr_list.next;
510 while (hdr != &regc->hdr_list) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000511 pjsip_hdr *new_hdr = (pjsip_hdr*)
512 pjsip_hdr_shallow_clone(tdata->pool, hdr);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000513 pjsip_msg_add_hdr(tdata->msg, new_hdr);
514 hdr = hdr->next;
515 }
516 }
517
Benny Prijonoccf95622006-02-07 18:48:01 +0000518 /* Done. */
519 *p_tdata = tdata;
520 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000521}
522
523
Benny Prijonoccf95622006-02-07 18:48:01 +0000524PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
525 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000526{
527 pjsip_msg *msg;
Benny Prijonodd742da2008-05-17 12:45:00 +0000528 pjsip_contact_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000529 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000530 pjsip_tx_data *tdata;
531
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000532 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
533
Benny Prijono11893652008-06-06 22:52:48 +0000534 pj_lock_acquire(regc->lock);
535
Benny Prijonoccf95622006-02-07 18:48:01 +0000536 status = create_request(regc, &tdata);
Benny Prijono11893652008-06-06 22:52:48 +0000537 if (status != PJ_SUCCESS) {
538 pj_lock_release(regc->lock);
Benny Prijonoccf95622006-02-07 18:48:01 +0000539 return status;
Benny Prijono11893652008-06-06 22:52:48 +0000540 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000541
542 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000543
544 /* Add Contact headers. */
545 hdr = regc->contact_hdr_list.next;
546 while (hdr != &regc->contact_hdr_list) {
547 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
548 pjsip_hdr_shallow_clone(tdata->pool, hdr));
549 hdr = hdr->next;
550 }
551
Benny Prijonodd742da2008-05-17 12:45:00 +0000552 /* Also add bindings which are to be removed */
553 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
554 hdr = regc->removed_contact_hdr_list.next;
555 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
556 pjsip_hdr_clone(tdata->pool, hdr));
557 pj_list_erase(hdr);
558 }
559
560
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000561 if (regc->expires_hdr)
Benny Prijonoa1e69682007-05-11 15:14:34 +0000562 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
563 pjsip_hdr_shallow_clone(tdata->pool,
Benny Prijono4093f7c2006-09-13 23:48:45 +0000564 regc->expires_hdr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000565
566 if (regc->timer.id != 0) {
567 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
568 regc->timer.id = 0;
569 }
570
571 regc->auto_reg = autoreg;
572
Benny Prijono11893652008-06-06 22:52:48 +0000573 pj_lock_release(regc->lock);
574
Benny Prijonoccf95622006-02-07 18:48:01 +0000575 /* Done */
576 *p_tdata = tdata;
577 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000578}
579
580
Benny Prijonoccf95622006-02-07 18:48:01 +0000581PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
582 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000583{
584 pjsip_tx_data *tdata;
585 pjsip_msg *msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000586 pjsip_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000587 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000588
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000589 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
590
Benny Prijono11893652008-06-06 22:52:48 +0000591 pj_lock_acquire(regc->lock);
592
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000593 if (regc->timer.id != 0) {
594 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
595 regc->timer.id = 0;
596 }
597
Benny Prijonoccf95622006-02-07 18:48:01 +0000598 status = create_request(regc, &tdata);
Benny Prijono11893652008-06-06 22:52:48 +0000599 if (status != PJ_SUCCESS) {
600 pj_lock_release(regc->lock);
Benny Prijonoccf95622006-02-07 18:48:01 +0000601 return status;
Benny Prijono11893652008-06-06 22:52:48 +0000602 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000603
604 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000605
606 /* Add Contact headers. */
Benny Prijonodd742da2008-05-17 12:45:00 +0000607 hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
Benny Prijono43047252008-06-04 14:44:29 +0000608 while ((void*)hdr != (void*)&regc->contact_hdr_list) {
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000609 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
610 pjsip_hdr_shallow_clone(tdata->pool, hdr));
611 hdr = hdr->next;
612 }
613
Benny Prijonodd742da2008-05-17 12:45:00 +0000614 /* Also add bindings which are to be removed */
615 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
616 hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next;
617 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
618 pjsip_hdr_clone(tdata->pool, hdr));
619 pj_list_erase(hdr);
620 }
621
622 /* Add Expires:0 header */
623 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
624 pjsip_msg_add_hdr(msg, hdr);
Benny Prijonodfc4c482006-12-02 07:25:29 +0000625
Benny Prijono11893652008-06-06 22:52:48 +0000626 pj_lock_release(regc->lock);
627
Benny Prijonodfc4c482006-12-02 07:25:29 +0000628 *p_tdata = tdata;
629 return PJ_SUCCESS;
630}
631
632PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
633 pjsip_tx_data **p_tdata)
634{
635 pjsip_tx_data *tdata;
Benny Prijonodd742da2008-05-17 12:45:00 +0000636 pjsip_contact_hdr *hcontact;
637 pjsip_hdr *hdr;
Benny Prijonodfc4c482006-12-02 07:25:29 +0000638 pjsip_msg *msg;
639 pj_status_t status;
640
641 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
642
Benny Prijono11893652008-06-06 22:52:48 +0000643 pj_lock_acquire(regc->lock);
644
Benny Prijonodfc4c482006-12-02 07:25:29 +0000645 if (regc->timer.id != 0) {
646 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
647 regc->timer.id = 0;
648 }
649
650 status = create_request(regc, &tdata);
Benny Prijono11893652008-06-06 22:52:48 +0000651 if (status != PJ_SUCCESS) {
652 pj_lock_release(regc->lock);
Benny Prijonodfc4c482006-12-02 07:25:29 +0000653 return status;
Benny Prijono11893652008-06-06 22:52:48 +0000654 }
Benny Prijonodfc4c482006-12-02 07:25:29 +0000655
656 msg = tdata->msg;
Benny Prijonodd742da2008-05-17 12:45:00 +0000657
658 /* Clear removed_contact_hdr_list */
659 pj_list_init(&regc->removed_contact_hdr_list);
660
661 /* Add Contact:* header */
662 hcontact = pjsip_contact_hdr_create(tdata->pool);
663 hcontact->star = 1;
664 pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
665
666 /* Add Expires:0 header */
667 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
668 pjsip_msg_add_hdr(msg, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000669
Benny Prijono11893652008-06-06 22:52:48 +0000670 pj_lock_release(regc->lock);
671
Benny Prijonoccf95622006-02-07 18:48:01 +0000672 *p_tdata = tdata;
673 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000674}
675
676
677PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
678 int contact_cnt,
679 const pj_str_t contact[] )
680{
Benny Prijono11893652008-06-06 22:52:48 +0000681 pj_status_t status;
682
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000683 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono11893652008-06-06 22:52:48 +0000684
685 pj_lock_acquire(regc->lock);
686 status = set_contact( regc, contact_cnt, contact );
687 pj_lock_release(regc->lock);
688
689 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000690}
691
692
693PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
694 pj_uint32_t expires )
695{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000696 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono11893652008-06-06 22:52:48 +0000697
698 pj_lock_acquire(regc->lock);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000699 set_expires( regc, expires );
Benny Prijono11893652008-06-06 22:52:48 +0000700 pj_lock_release(regc->lock);
701
Benny Prijonoccf95622006-02-07 18:48:01 +0000702 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000703}
704
705
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000706static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
707 const pj_str_t *reason,
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000708 pjsip_rx_data *rdata, pj_int32_t expiration,
709 int contact_cnt, pjsip_contact_hdr *contact[])
710{
711 struct pjsip_regc_cbparam cbparam;
712
713
Benny Prijonof2651802007-01-26 17:13:56 +0000714 if (!regc->cb)
715 return;
716
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000717 cbparam.regc = regc;
718 cbparam.token = regc->token;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000719 cbparam.status = status;
720 cbparam.code = st_code;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000721 cbparam.reason = *reason;
722 cbparam.rdata = rdata;
723 cbparam.contact_cnt = contact_cnt;
724 cbparam.expiration = expiration;
725 if (contact_cnt) {
726 pj_memcpy( cbparam.contact, contact,
727 contact_cnt*sizeof(pjsip_contact_hdr*));
728 }
729
730 (*regc->cb)(&cbparam);
731}
732
733static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
734 struct pj_timer_entry *entry)
735{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000736 pjsip_regc *regc = (pjsip_regc*) entry->user_data;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000737 pjsip_tx_data *tdata;
Benny Prijonoccf95622006-02-07 18:48:01 +0000738 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000739
Benny Prijonoccf95622006-02-07 18:48:01 +0000740 PJ_UNUSED_ARG(timer_heap);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000741
Benny Prijonof2651802007-01-26 17:13:56 +0000742 /* Temporarily increase busy flag to prevent regc from being deleted
Benny Prijono11893652008-06-06 22:52:48 +0000743 * in pjsip_regc_send() or in the callback
Benny Prijonof2651802007-01-26 17:13:56 +0000744 */
Benny Prijono11893652008-06-06 22:52:48 +0000745 pj_atomic_inc(regc->busy_ctr);
Benny Prijonof2651802007-01-26 17:13:56 +0000746
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000747 entry->id = 0;
Benny Prijonoccf95622006-02-07 18:48:01 +0000748 status = pjsip_regc_register(regc, 1, &tdata);
749 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000750 status = pjsip_regc_send(regc, tdata);
751 }
752
Benny Prijonof2651802007-01-26 17:13:56 +0000753 if (status != PJ_SUCCESS && regc->cb) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000754 char errmsg[PJ_ERR_MSG_SIZE];
755 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000756 call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000757 }
Benny Prijonof2651802007-01-26 17:13:56 +0000758
Benny Prijonof2651802007-01-26 17:13:56 +0000759 /* Delete the record if user destroy regc during the callback. */
Benny Prijono11893652008-06-06 22:52:48 +0000760 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
Benny Prijonof2651802007-01-26 17:13:56 +0000761 pjsip_regc_destroy(regc);
762 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000763}
764
Benny Prijonodd742da2008-05-17 12:45:00 +0000765static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
766 const pjsip_rx_data *rdata,
767 unsigned *contact_cnt,
768 unsigned max_contact,
769 pjsip_contact_hdr *contacts[])
770{
771 pj_int32_t expiration = NOEXP;
772 const pjsip_msg *msg = rdata->msg_info.msg;
773 const pjsip_hdr *hdr;
774
775 /* Enumerate all Contact headers in the response */
776 *contact_cnt = 0;
777 for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
778 if (hdr->type == PJSIP_H_CONTACT &&
779 *contact_cnt < max_contact)
780 {
781 contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
782 ++(*contact_cnt);
783 }
784 }
785
786 if (regc->current_op == REGC_REGISTERING) {
787 pj_bool_t has_our_contact = PJ_FALSE;
788 const pjsip_expires_hdr *expires;
789
790 /* Get Expires header */
791 expires = (const pjsip_expires_hdr*)
792 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
793
794 /* Try to find the Contact URIs that we register, in the response
795 * to get the expires value. We'll try both with comparing the URI
796 * and comparing the extension param only.
797 */
798 if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
799 unsigned i;
800 for (i=0; i<*contact_cnt; ++i) {
801 const pjsip_contact_hdr *our_hdr;
802
803 our_hdr = (const pjsip_contact_hdr*)
804 regc->contact_hdr_list.next;
805
806 /* Match with our Contact header(s) */
807 while ((void*)our_hdr != (void*)&regc->contact_hdr_list) {
808
809 const pjsip_uri *uri1, *uri2;
810 pj_bool_t matched = PJ_FALSE;
811
812 /* Exclude the display name when comparing the URI
813 * since server may not return it.
814 */
815 uri1 = (const pjsip_uri*)
816 pjsip_uri_get_uri(contacts[i]->uri);
817 uri2 = (const pjsip_uri*)
818 pjsip_uri_get_uri(our_hdr->uri);
819
820 /* First try with exact matching, according to RFC 3261
821 * Section 19.1.4 URI Comparison
822 */
823 if (pjsip_cfg()->regc.check_contact) {
824 matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
825 uri1, uri2)==0;
826 }
827
828 /* If no match is found, try with matching the extension
829 * parameter only if extension parameter was added.
830 */
831 if (!matched && regc->add_xuid_param &&
832 (PJSIP_URI_SCHEME_IS_SIP(uri1) ||
833 PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&
834 (PJSIP_URI_SCHEME_IS_SIP(uri2) ||
835 PJSIP_URI_SCHEME_IS_SIPS(uri2)))
836 {
837 const pjsip_sip_uri *sip_uri1, *sip_uri2;
838 const pjsip_param *p1, *p2;
839
840 sip_uri1 = (const pjsip_sip_uri*)uri1;
841 sip_uri2 = (const pjsip_sip_uri*)uri2;
842
843 p1 = pjsip_param_cfind(&sip_uri1->other_param,
844 &XUID_PARAM_NAME);
845 p2 = pjsip_param_cfind(&sip_uri2->other_param,
846 &XUID_PARAM_NAME);
847 matched = p1 && p2 &&
848 pj_strcmp(&p1->value, &p2->value)==0;
849
850 }
851
852 if (matched) {
853 has_our_contact = PJ_TRUE;
854
855 if (contacts[i]->expires >= 0 &&
856 contacts[i]->expires < expiration)
857 {
858 /* Get the lowest expiration time. */
859 expiration = contacts[i]->expires;
860 }
861
862 break;
863 }
864
865 our_hdr = our_hdr->next;
866
867 } /* while ((void.. */
868
869 } /* for (i=.. */
870
871 /* If matching Contact header(s) are found but the
872 * header doesn't contain expires parameter, get the
873 * expiration value from the Expires header. And
874 * if Expires header is not present, get the expiration
875 * value from the request.
876 */
877 if (has_our_contact && expiration == NOEXP) {
878 if (expires) {
879 expiration = expires->ivalue;
880 } else if (regc->expires_hdr) {
881 expiration = regc->expires_hdr->ivalue;
882 } else {
883 /* We didn't request explicit expiration value,
884 * and server doesn't specify it either. This
885 * shouldn't happen unless we have a broken
886 * registrar.
887 */
888 expiration = 3600;
889 }
890 }
891
892 }
893
894 /* If we still couldn't get matching Contact header(s), it means
895 * there must be something wrong with the registrar (e.g. it may
896 * have modified the URI's in the response, which is prohibited).
897 */
898 if (expiration==NOEXP) {
899 /* If the number of Contact headers in the response matches
900 * ours, they're all probably ours. Get the expiration
901 * from there if this is the case, or from Expires header
902 * if we don't have exact Contact header count, or
903 * from the request as the last resort.
904 */
905 unsigned our_contact_cnt;
906
907 our_contact_cnt = pj_list_size(&regc->contact_hdr_list);
908
909 if (*contact_cnt == our_contact_cnt && *contact_cnt &&
910 contacts[0]->expires >= 0)
911 {
912 expiration = contacts[0]->expires;
913 } else if (expires)
914 expiration = expires->ivalue;
915 else if (regc->expires_hdr)
916 expiration = regc->expires_hdr->ivalue;
917 else
918 expiration = 3600;
919 }
920
921 } else {
922 /* Just assume that the unregistration has been successful. */
923 expiration = 0;
924 }
925
926 /* Must have expiration value by now */
927 pj_assert(expiration != NOEXP);
928
929 return expiration;
930}
931
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000932static void tsx_callback(void *token, pjsip_event *event)
933{
Benny Prijonoccf95622006-02-07 18:48:01 +0000934 pj_status_t status;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000935 pjsip_regc *regc = (pjsip_regc*) token;
Benny Prijonoccf95622006-02-07 18:48:01 +0000936 pjsip_transaction *tsx = event->body.tsx_state.tsx;
Benny Prijono11893652008-06-06 22:52:48 +0000937
938 pj_atomic_inc(regc->busy_ctr);
939 pj_lock_acquire(regc->lock);
940
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000941 /* Decrement pending transaction counter. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000942 pj_assert(regc->has_tsx);
943 regc->has_tsx = PJ_FALSE;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000944
Benny Prijonobdc093f2007-10-04 09:48:25 +0000945 /* Add reference to the transport */
946 if (tsx->transport != regc->last_transport) {
947 if (regc->last_transport) {
948 pjsip_transport_dec_ref(regc->last_transport);
949 regc->last_transport = NULL;
950 }
951
952 if (tsx->transport) {
953 regc->last_transport = tsx->transport;
954 pjsip_transport_add_ref(regc->last_transport);
955 }
956 }
957
Benny Prijono58990232007-01-21 16:11:18 +0000958 /* Handle 401/407 challenge (even when _delete_flag is set) */
959 if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
960 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000961 {
Benny Prijonoccf95622006-02-07 18:48:01 +0000962 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000963 pjsip_tx_data *tdata;
964
Benny Prijonodd742da2008-05-17 12:45:00 +0000965 /* reset current op */
966 regc->current_op = REGC_IDLE;
967
Benny Prijonoccf95622006-02-07 18:48:01 +0000968 status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
969 rdata,
970 tsx->last_tx,
971 &tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000972
Benny Prijonoccf95622006-02-07 18:48:01 +0000973 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000974 status = pjsip_regc_send(regc, tdata);
Benny Prijono3fe034a2007-06-01 12:51:07 +0000975 }
Benny Prijono27042582006-08-08 14:04:21 +0000976
977 if (status != PJ_SUCCESS) {
Benny Prijono58990232007-01-21 16:11:18 +0000978
979 /* Only call callback if application is still interested
980 * in it.
981 */
982 if (regc->_delete_flag == 0) {
Benny Prijono11893652008-06-06 22:52:48 +0000983 /* Should be safe to release the lock temporarily.
984 * We do this to avoid deadlock.
Benny Prijono58990232007-01-21 16:11:18 +0000985 */
Benny Prijono11893652008-06-06 22:52:48 +0000986 pj_lock_release(regc->lock);
Benny Prijono58990232007-01-21 16:11:18 +0000987 call_callback(regc, status, tsx->status_code,
988 &rdata->msg_info.msg->line.status.reason,
989 rdata, -1, 0, NULL);
Benny Prijono11893652008-06-06 22:52:48 +0000990 pj_lock_acquire(regc->lock);
Benny Prijono58990232007-01-21 16:11:18 +0000991 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000992 }
Benny Prijono27042582006-08-08 14:04:21 +0000993
Benny Prijono58990232007-01-21 16:11:18 +0000994 } else if (regc->_delete_flag) {
995
996 /* User has called pjsip_regc_destroy(), so don't call callback.
997 * This regc will be destroyed later in this function.
998 */
999
Benny Prijonodd742da2008-05-17 12:45:00 +00001000 /* Just reset current op */
1001 regc->current_op = REGC_IDLE;
Benny Prijono27042582006-08-08 14:04:21 +00001002
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001003 } else {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001004 pjsip_rx_data *rdata;
Benny Prijonoe1c984f2007-11-04 02:05:13 +00001005 pj_int32_t expiration = NOEXP;
Benny Prijonodd742da2008-05-17 12:45:00 +00001006 unsigned contact_cnt = 0;
1007 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001008
1009 if (tsx->status_code/100 == 2) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001010
Benny Prijonoccf95622006-02-07 18:48:01 +00001011 rdata = event->body.tsx_state.src.rdata;
Benny Prijonoe1c984f2007-11-04 02:05:13 +00001012
Benny Prijonodd742da2008-05-17 12:45:00 +00001013 /* Calculate expiration */
1014 expiration = calculate_response_expiration(regc, rdata,
1015 &contact_cnt,
1016 PJSIP_REGC_MAX_CONTACT,
1017 contact);
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001018
Benny Prijonodd742da2008-05-17 12:45:00 +00001019 /* Mark operation as complete */
1020 regc->current_op = REGC_IDLE;
Benny Prijonoe1c984f2007-11-04 02:05:13 +00001021
1022 /* Schedule next registration */
Benny Prijonodd742da2008-05-17 12:45:00 +00001023 if (regc->auto_reg && expiration > 0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001024 pj_time_val delay = { 0, 0};
1025
1026 delay.sec = expiration - DELAY_BEFORE_REFRESH;
1027 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
1028 delay.sec > (pj_int32_t)regc->expires)
1029 {
1030 delay.sec = regc->expires;
1031 }
1032 if (delay.sec < DELAY_BEFORE_REFRESH)
1033 delay.sec = DELAY_BEFORE_REFRESH;
1034 regc->timer.cb = &regc_refresh_timer_cb;
1035 regc->timer.id = REFRESH_TIMER;
1036 regc->timer.user_data = regc;
1037 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001038 pj_gettimeofday(&regc->last_reg);
1039 regc->next_reg = regc->last_reg;
1040 regc->next_reg.sec += delay.sec;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001041 }
1042
1043 } else {
Benny Prijonoccf95622006-02-07 18:48:01 +00001044 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
1045 event->body.tsx_state.src.rdata : NULL;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001046 }
1047
Benny Prijonodd742da2008-05-17 12:45:00 +00001048 /* Update registration */
1049 if (expiration==NOEXP) expiration=-1;
1050 regc->expires = expiration;
1051
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001052 /* Call callback. */
Benny Prijono11893652008-06-06 22:52:48 +00001053 /* Should be safe to release the lock temporarily.
1054 * We do this to avoid deadlock.
1055 */
1056 pj_lock_release(regc->lock);
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001057 call_callback(regc, PJ_SUCCESS, tsx->status_code,
Benny Prijonoccf95622006-02-07 18:48:01 +00001058 (rdata ? &rdata->msg_info.msg->line.status.reason
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001059 : pjsip_get_status_text(tsx->status_code)),
1060 rdata, expiration,
1061 contact_cnt, contact);
Benny Prijono11893652008-06-06 22:52:48 +00001062 pj_lock_acquire(regc->lock);
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001063 }
1064
Benny Prijono11893652008-06-06 22:52:48 +00001065 pj_lock_release(regc->lock);
1066
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001067 /* Delete the record if user destroy regc during the callback. */
Benny Prijono11893652008-06-06 22:52:48 +00001068 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001069 pjsip_regc_destroy(regc);
1070 }
1071}
1072
Benny Prijonoccf95622006-02-07 18:48:01 +00001073PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001074{
Benny Prijonoccf95622006-02-07 18:48:01 +00001075 pj_status_t status;
Benny Prijono59ca70f2006-02-22 22:18:58 +00001076 pjsip_cseq_hdr *cseq_hdr;
Benny Prijonodd742da2008-05-17 12:45:00 +00001077 pjsip_expires_hdr *expires_hdr;
Benny Prijono59ca70f2006-02-22 22:18:58 +00001078 pj_uint32_t cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001079
Benny Prijono11893652008-06-06 22:52:48 +00001080 pj_atomic_inc(regc->busy_ctr);
1081 pj_lock_acquire(regc->lock);
1082
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001083 /* Make sure we don't have pending transaction. */
Benny Prijono25a86c72006-12-01 20:50:01 +00001084 if (regc->has_tsx) {
Benny Prijono5b656872006-08-08 00:41:00 +00001085 PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
1086 "transaction pending"));
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001087 pjsip_tx_data_dec_ref( tdata );
Benny Prijono11893652008-06-06 22:52:48 +00001088 pj_lock_release(regc->lock);
1089 pj_atomic_dec(regc->busy_ctr);
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001090 return PJSIP_EBUSY;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001091 }
1092
Benny Prijonodd742da2008-05-17 12:45:00 +00001093 pj_assert(regc->current_op == REGC_IDLE);
1094
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001095 /* Invalidate message buffer. */
1096 pjsip_tx_data_invalidate_msg(tdata);
1097
1098 /* Increment CSeq */
Benny Prijono59ca70f2006-02-22 22:18:58 +00001099 cseq = ++regc->cseq_hdr->cseq;
Benny Prijonoa1e69682007-05-11 15:14:34 +00001100 cseq_hdr = (pjsip_cseq_hdr*)
1101 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
Benny Prijono59ca70f2006-02-22 22:18:58 +00001102 cseq_hdr->cseq = cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001103
Benny Prijonodd742da2008-05-17 12:45:00 +00001104 /* Find Expires header */
1105 expires_hdr = (pjsip_expires_hdr*)
1106 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
1107
Benny Prijono37db51c2007-11-23 03:41:54 +00001108 /* Bind to transport selector */
1109 pjsip_tx_data_set_transport(tdata, &regc->tp_sel);
1110
Benny Prijono25a86c72006-12-01 20:50:01 +00001111 regc->has_tsx = PJ_TRUE;
Benny Prijonodd742da2008-05-17 12:45:00 +00001112
1113 /* Set current operation based on the value of Expires header */
1114 if (expires_hdr && expires_hdr->ivalue==0)
1115 regc->current_op = REGC_UNREGISTERING;
1116 else
1117 regc->current_op = REGC_REGISTERING;
1118
Benny Prijono0f35f912007-02-05 18:59:31 +00001119 status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
1120 regc, &tsx_callback);
Benny Prijono5b656872006-08-08 00:41:00 +00001121 if (status!=PJ_SUCCESS) {
Benny Prijono5b656872006-08-08 00:41:00 +00001122 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
1123 }
Benny Prijono11893652008-06-06 22:52:48 +00001124
1125 pj_lock_release(regc->lock);
Benny Prijono197cabf2006-10-16 20:05:27 +00001126
1127 /* Delete the record if user destroy regc during the callback. */
Benny Prijono11893652008-06-06 22:52:48 +00001128 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
Benny Prijono197cabf2006-10-16 20:05:27 +00001129 pjsip_regc_destroy(regc);
1130 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001131
1132 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001133}
1134
1135