blob: 99e316dbfe39aba69266e3a45dd4ab541b22d31f [file] [log] [blame]
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijono5dcb38d2005-11-21 01:55:47 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijonoccf95622006-02-07 18:48:01 +000019#include <pjsip-ua/sip_regc.h>
Benny Prijono5dcb38d2005-11-21 01:55:47 +000020#include <pjsip/sip_endpoint.h>
21#include <pjsip/sip_parser.h>
22#include <pjsip/sip_module.h>
23#include <pjsip/sip_transaction.h>
24#include <pjsip/sip_event.h>
25#include <pjsip/sip_util.h>
26#include <pjsip/sip_auth_msg.h>
Benny Prijonoccf95622006-02-07 18:48:01 +000027#include <pjsip/sip_errno.h>
Benny Prijonoccf95622006-02-07 18:48:01 +000028#include <pj/assert.h>
Benny Prijonobcaed6c2006-02-19 15:37:19 +000029#include <pj/guid.h>
Benny Prijono11893652008-06-06 22:52:48 +000030#include <pj/lock.h>
Benny Prijonobcaed6c2006-02-19 15:37:19 +000031#include <pj/os.h>
32#include <pj/pool.h>
33#include <pj/log.h>
Benny Prijono59ca70f2006-02-22 22:18:58 +000034#include <pj/rand.h>
Benny Prijonobcaed6c2006-02-19 15:37:19 +000035#include <pj/string.h>
Benny Prijonoccf95622006-02-07 18:48:01 +000036
Benny Prijono5dcb38d2005-11-21 01:55:47 +000037
38#define REFRESH_TIMER 1
Benny Prijonodd742da2008-05-17 12:45:00 +000039#define DELAY_BEFORE_REFRESH PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH
Benny Prijono315999b2007-09-30 10:58:36 +000040#define THIS_FILE "sip_reg.c"
Benny Prijono5dcb38d2005-11-21 01:55:47 +000041
Benny Prijono0f35f912007-02-05 18:59:31 +000042/* Outgoing transaction timeout when server sends 100 but never replies
43 * with final response. Value is in MILISECONDS!
44 */
45#define REGC_TSX_TIMEOUT 33000
46
Benny Prijonodd742da2008-05-17 12:45:00 +000047enum { NOEXP = 0x1FFFFFFF };
48
49static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 };
50
51
52/* Current/pending operation */
53enum regc_op
54{
55 REGC_IDLE,
56 REGC_REGISTERING,
57 REGC_UNREGISTERING
58};
Benny Prijono0f35f912007-02-05 18:59:31 +000059
Benny Prijono5dcb38d2005-11-21 01:55:47 +000060/**
61 * SIP client registration structure.
62 */
63struct pjsip_regc
64{
Benny Prijono84126ab2006-02-09 09:30:09 +000065 pj_pool_t *pool;
66 pjsip_endpoint *endpt;
Benny Prijono11893652008-06-06 22:52:48 +000067 pj_lock_t *lock;
Benny Prijono84126ab2006-02-09 09:30:09 +000068 pj_bool_t _delete_flag;
Benny Prijono25a86c72006-12-01 20:50:01 +000069 pj_bool_t has_tsx;
Benny Prijono11893652008-06-06 22:52:48 +000070 pj_atomic_t *busy_ctr;
Benny Prijonodd742da2008-05-17 12:45:00 +000071 enum regc_op current_op;
72
73 pj_bool_t add_xuid_param;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000074
Benny Prijono84126ab2006-02-09 09:30:09 +000075 void *token;
76 pjsip_regc_cb *cb;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000077
Benny Prijono84126ab2006-02-09 09:30:09 +000078 pj_str_t str_srv_url;
79 pjsip_uri *srv_url;
80 pjsip_cid_hdr *cid_hdr;
81 pjsip_cseq_hdr *cseq_hdr;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000082 pj_str_t from_uri;
Benny Prijono84126ab2006-02-09 09:30:09 +000083 pjsip_from_hdr *from_hdr;
84 pjsip_to_hdr *to_hdr;
Benny Prijonodd742da2008-05-17 12:45:00 +000085 pjsip_contact_hdr contact_hdr_list;
86 pjsip_contact_hdr removed_contact_hdr_list;
Benny Prijono84126ab2006-02-09 09:30:09 +000087 pjsip_expires_hdr *expires_hdr;
Benny Prijono84126ab2006-02-09 09:30:09 +000088 pj_uint32_t expires;
89 pjsip_route_hdr route_set;
Benny Prijono8fc6de02006-11-11 21:25:55 +000090 pjsip_hdr hdr_list;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000091
Benny Prijono5dcb38d2005-11-21 01:55:47 +000092 /* Authorization sessions. */
Benny Prijono84126ab2006-02-09 09:30:09 +000093 pjsip_auth_clt_sess auth_sess;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000094
95 /* Auto refresh registration. */
Benny Prijono84126ab2006-02-09 09:30:09 +000096 pj_bool_t auto_reg;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000097 pj_time_val last_reg;
98 pj_time_val next_reg;
Benny Prijono84126ab2006-02-09 09:30:09 +000099 pj_timer_entry timer;
Benny Prijono720d0a82007-01-12 06:37:35 +0000100
101 /* Transport selector */
102 pjsip_tpselector tp_sel;
Benny Prijonobdc093f2007-10-04 09:48:25 +0000103
104 /* Last transport used. We acquire the transport to keep
105 * it open.
106 */
107 pjsip_transport *last_transport;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000108};
109
110
Benny Prijonoccf95622006-02-07 18:48:01 +0000111PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
112 pjsip_regc_cb *cb,
113 pjsip_regc **p_regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000114{
115 pj_pool_t *pool;
116 pjsip_regc *regc;
Benny Prijonoccf95622006-02-07 18:48:01 +0000117 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000118
Benny Prijonoccf95622006-02-07 18:48:01 +0000119 /* Verify arguments. */
120 PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000121
122 pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
Benny Prijonoccf95622006-02-07 18:48:01 +0000123 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
124
Benny Prijonoa1e69682007-05-11 15:14:34 +0000125 regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000126
127 regc->pool = pool;
128 regc->endpt = endpt;
129 regc->token = token;
130 regc->cb = cb;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000131 regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
Benny Prijonodd742da2008-05-17 12:45:00 +0000132 regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000133
Benny Prijono11893652008-06-06 22:52:48 +0000134 status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
135 &regc->lock);
136 if (status != PJ_SUCCESS) {
137 pj_pool_release(pool);
138 return status;
139 }
140
141 status = pj_atomic_create(pool, 0, &regc->busy_ctr);
142 if (status != PJ_SUCCESS) {
143 pj_lock_destroy(regc->lock);
144 pj_pool_release(pool);
145 return status;
146 }
147
Benny Prijonoccf95622006-02-07 18:48:01 +0000148 status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
149 if (status != PJ_SUCCESS)
150 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000151
Benny Prijono84126ab2006-02-09 09:30:09 +0000152 pj_list_init(&regc->route_set);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000153 pj_list_init(&regc->hdr_list);
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000154 pj_list_init(&regc->contact_hdr_list);
Benny Prijonodd742da2008-05-17 12:45:00 +0000155 pj_list_init(&regc->removed_contact_hdr_list);
Benny Prijono84126ab2006-02-09 09:30:09 +0000156
Benny Prijonoccf95622006-02-07 18:48:01 +0000157 /* Done */
158 *p_regc = regc;
159 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000160}
161
162
Benny Prijonoccf95622006-02-07 18:48:01 +0000163PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000164{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000165 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
166
Benny Prijono11893652008-06-06 22:52:48 +0000167 pj_lock_acquire(regc->lock);
168 if (regc->has_tsx || pj_atomic_get(regc->busy_ctr) != 0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000169 regc->_delete_flag = 1;
170 regc->cb = NULL;
Benny Prijono11893652008-06-06 22:52:48 +0000171 pj_lock_release(regc->lock);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000172 } else {
Benny Prijono720d0a82007-01-12 06:37:35 +0000173 pjsip_tpselector_dec_ref(&regc->tp_sel);
Benny Prijonobdc093f2007-10-04 09:48:25 +0000174 if (regc->last_transport) {
175 pjsip_transport_dec_ref(regc->last_transport);
176 regc->last_transport = NULL;
177 }
Benny Prijonodd742da2008-05-17 12:45:00 +0000178 if (regc->timer.id != 0) {
179 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
180 regc->timer.id = 0;
181 }
Benny Prijono11893652008-06-06 22:52:48 +0000182 pj_atomic_destroy(regc->busy_ctr);
183 pj_lock_release(regc->lock);
184 pj_lock_destroy(regc->lock);
185 regc->lock = NULL;
Benny Prijonoccf95622006-02-07 18:48:01 +0000186 pjsip_endpt_release_pool(regc->endpt, regc->pool);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000187 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000188
189 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000190}
191
192
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000193PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
194 pjsip_regc_info *info )
195{
196 PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
197
Benny Prijono11893652008-06-06 22:52:48 +0000198 pj_lock_acquire(regc->lock);
199
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000200 info->server_uri = regc->str_srv_url;
201 info->client_uri = regc->from_uri;
Benny Prijono11893652008-06-06 22:52:48 +0000202 info->is_busy = (pj_atomic_get(regc->busy_ctr) || regc->has_tsx);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000203 info->auto_reg = regc->auto_reg;
204 info->interval = regc->expires;
205
Benny Prijono25a86c72006-12-01 20:50:01 +0000206 if (regc->has_tsx)
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000207 info->next_reg = 0;
208 else if (regc->auto_reg == 0)
209 info->next_reg = 0;
210 else if (regc->expires < 0)
211 info->next_reg = regc->expires;
212 else {
213 pj_time_val now, next_reg;
214
215 next_reg = regc->next_reg;
216 pj_gettimeofday(&now);
217 PJ_TIME_VAL_SUB(next_reg, now);
218 info->next_reg = next_reg.sec;
219 }
220
Benny Prijono11893652008-06-06 22:52:48 +0000221 pj_lock_release(regc->lock);
222
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000223 return PJ_SUCCESS;
224}
225
226
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000227PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
228{
229 return regc->pool;
230}
231
232static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
233{
234 if (expires != regc->expires) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000235 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000236 } else {
237 regc->expires_hdr = NULL;
238 }
239}
240
241
242static pj_status_t set_contact( pjsip_regc *regc,
243 int contact_cnt,
244 const pj_str_t contact[] )
245{
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000246 const pj_str_t CONTACT = { "Contact", 7 };
Benny Prijonodd742da2008-05-17 12:45:00 +0000247 pjsip_contact_hdr *h;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000248 int i;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000249
Benny Prijonodd742da2008-05-17 12:45:00 +0000250 /* Save existing contact list to removed_contact_hdr_list and
251 * clear contact_hdr_list.
252 */
253 pj_list_merge_last(&regc->removed_contact_hdr_list,
254 &regc->contact_hdr_list);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000255
Benny Prijonodd742da2008-05-17 12:45:00 +0000256 /* Set the expiration of Contacts in to removed_contact_hdr_list
257 * zero.
258 */
259 h = regc->removed_contact_hdr_list.next;
260 while (h != &regc->removed_contact_hdr_list) {
261 h->expires = 0;
262 h = h->next;
263 }
264
265 /* Process new contacts */
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000266 for (i=0; i<contact_cnt; ++i) {
Benny Prijonodd742da2008-05-17 12:45:00 +0000267 pjsip_contact_hdr *hdr;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000268 pj_str_t tmp;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000269
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000270 pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
Benny Prijonodd742da2008-05-17 12:45:00 +0000271 hdr = (pjsip_contact_hdr*)
Benny Prijono91a5a3a2007-09-24 21:16:48 +0000272 pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000273 if (hdr == NULL) {
274 PJ_LOG(4,(THIS_FILE, "Invalid Contact URI: \"%.*s\"",
275 (int)tmp.slen, tmp.ptr));
276 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000277 }
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000278
Benny Prijonodd742da2008-05-17 12:45:00 +0000279 /* Find the new contact in old contact list. If found, remove
280 * the old header from the old header list.
281 */
282 h = regc->removed_contact_hdr_list.next;
283 while (h != &regc->removed_contact_hdr_list) {
284 int rc;
285
286 rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
287 h->uri, hdr->uri);
288 if (rc == 0) {
289 /* Match */
290 pj_list_erase(h);
291 break;
292 }
293
294 h = h->next;
295 }
296
297 /* If add_xuid_param option is enabled and Contact URI is sip/sips,
298 * add xuid parameter to assist matching the Contact URI in the
299 * REGISTER response later.
300 */
301 if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
302 PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))
303 {
304 pjsip_param *xuid_param;
305 pjsip_sip_uri *sip_uri;
306
307 xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
308 xuid_param->name = XUID_PARAM_NAME;
309 pj_create_unique_string(regc->pool, &xuid_param->value);
310
Benny Prijono89ac2b42008-06-13 12:52:56 +0000311 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
Benny Prijonodd742da2008-05-17 12:45:00 +0000312 pj_list_push_back(&sip_uri->other_param, xuid_param);
313 }
314
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000315 pj_list_push_back(&regc->contact_hdr_list, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000316 }
317
Benny Prijonoccf95622006-02-07 18:48:01 +0000318 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000319}
320
321
322PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
323 const pj_str_t *srv_url,
324 const pj_str_t *from_url,
325 const pj_str_t *to_url,
326 int contact_cnt,
327 const pj_str_t contact[],
328 pj_uint32_t expires)
329{
330 pj_str_t tmp;
Benny Prijonoccf95622006-02-07 18:48:01 +0000331 pj_status_t status;
332
333 PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
334 contact_cnt && contact && expires, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000335
336 /* Copy server URL. */
337 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
338
339 /* Set server URL. */
340 tmp = regc->str_srv_url;
341 regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
342 if (regc->srv_url == NULL) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000343 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000344 }
345
346 /* Set "From" header. */
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000347 pj_strdup_with_null(regc->pool, &regc->from_uri, from_url);
348 tmp = regc->from_uri;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000349 regc->from_hdr = pjsip_from_hdr_create(regc->pool);
350 regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
351 PJSIP_PARSE_URI_AS_NAMEADDR);
352 if (!regc->from_hdr->uri) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000353 PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
354 from_url->slen, from_url->ptr));
355 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000356 }
357
358 /* Set "To" header. */
359 pj_strdup_with_null(regc->pool, &tmp, to_url);
360 regc->to_hdr = pjsip_to_hdr_create(regc->pool);
361 regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
362 PJSIP_PARSE_URI_AS_NAMEADDR);
363 if (!regc->to_hdr->uri) {
364 PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
Benny Prijonoccf95622006-02-07 18:48:01 +0000365 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000366 }
367
368
369 /* Set "Contact" header. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000370 status = set_contact( regc, contact_cnt, contact);
371 if (status != PJ_SUCCESS)
372 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000373
374 /* Set "Expires" header, if required. */
375 set_expires( regc, expires);
376
377 /* Set "Call-ID" header. */
378 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
379 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
380
381 /* Set "CSeq" header. */
382 regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
Benny Prijono59ca70f2006-02-22 22:18:58 +0000383 regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000384 pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
385
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000386 /* Done. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000387 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000388}
389
390PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
391 int count,
392 const pjsip_cred_info cred[] )
393{
Benny Prijonoccf95622006-02-07 18:48:01 +0000394 PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
395 return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000396}
397
Benny Prijono48ab2b72007-11-08 09:24:30 +0000398PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
399 const pjsip_auth_clt_pref *pref)
400{
401 PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
402 return pjsip_auth_clt_set_prefs(&regc->auth_sess, pref);
403}
404
Benny Prijono84126ab2006-02-09 09:30:09 +0000405PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
406 const pjsip_route_hdr *route_set)
407{
408 const pjsip_route_hdr *chdr;
409
410 PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
411
412 pj_list_init(&regc->route_set);
413
414 chdr = route_set->next;
415 while (chdr != route_set) {
416 pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
417 chdr = chdr->next;
418 }
419
420 return PJ_SUCCESS;
421}
422
Benny Prijono720d0a82007-01-12 06:37:35 +0000423
424/*
425 * Bind client registration to a specific transport/listener.
426 */
427PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
428 const pjsip_tpselector *sel)
429{
430 PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
431
432 pjsip_tpselector_dec_ref(&regc->tp_sel);
433 pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
434 pjsip_tpselector_add_ref(&regc->tp_sel);
435
436 return PJ_SUCCESS;
437}
438
439
Benny Prijono8fc6de02006-11-11 21:25:55 +0000440PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
441 const pjsip_hdr *hdr_list)
442{
443 const pjsip_hdr *hdr;
444
445 PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
446
447 //This is "add" operation, so don't remove headers.
448 //pj_list_init(&regc->hdr_list);
449
450 hdr = hdr_list->next;
451 while (hdr != hdr_list) {
452 pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
453 hdr = hdr->next;
454 }
455
456 return PJ_SUCCESS;
457}
458
Benny Prijonoccf95622006-02-07 18:48:01 +0000459static pj_status_t create_request(pjsip_regc *regc,
460 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000461{
Benny Prijonoccf95622006-02-07 18:48:01 +0000462 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000463 pjsip_tx_data *tdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000464
Benny Prijonoccf95622006-02-07 18:48:01 +0000465 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000466
Benny Prijonoccf95622006-02-07 18:48:01 +0000467 /* Create the request. */
468 status = pjsip_endpt_create_request_from_hdr( regc->endpt,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000469 pjsip_get_register_method(),
Benny Prijonoccf95622006-02-07 18:48:01 +0000470 regc->srv_url,
471 regc->from_hdr,
472 regc->to_hdr,
473 NULL,
474 regc->cid_hdr,
475 regc->cseq_hdr->cseq,
476 NULL,
477 &tdata);
478 if (status != PJ_SUCCESS)
479 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000480
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000481 /* Add cached authorization headers. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000482 pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000483
Benny Prijono84126ab2006-02-09 09:30:09 +0000484 /* Add Route headers from route set, ideally after Via header */
485 if (!pj_list_empty(&regc->route_set)) {
486 pjsip_hdr *route_pos;
487 const pjsip_route_hdr *route;
488
Benny Prijonoa1e69682007-05-11 15:14:34 +0000489 route_pos = (pjsip_hdr*)
490 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000491 if (!route_pos)
492 route_pos = &tdata->msg->hdr;
493
494 route = regc->route_set.next;
495 while (route != &regc->route_set) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000496 pjsip_hdr *new_hdr = (pjsip_hdr*)
497 pjsip_hdr_shallow_clone(tdata->pool, route);
Benny Prijono84126ab2006-02-09 09:30:09 +0000498 pj_list_insert_after(route_pos, new_hdr);
499 route_pos = new_hdr;
500 route = route->next;
501 }
502 }
503
Benny Prijono8fc6de02006-11-11 21:25:55 +0000504 /* Add additional request headers */
505 if (!pj_list_empty(&regc->hdr_list)) {
506 const pjsip_hdr *hdr;
507
508 hdr = regc->hdr_list.next;
509 while (hdr != &regc->hdr_list) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000510 pjsip_hdr *new_hdr = (pjsip_hdr*)
511 pjsip_hdr_shallow_clone(tdata->pool, hdr);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000512 pjsip_msg_add_hdr(tdata->msg, new_hdr);
513 hdr = hdr->next;
514 }
515 }
516
Benny Prijonoccf95622006-02-07 18:48:01 +0000517 /* Done. */
518 *p_tdata = tdata;
519 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000520}
521
522
Benny Prijonoccf95622006-02-07 18:48:01 +0000523PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
524 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000525{
526 pjsip_msg *msg;
Benny Prijonodd742da2008-05-17 12:45:00 +0000527 pjsip_contact_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000528 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000529 pjsip_tx_data *tdata;
530
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000531 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
532
Benny Prijono11893652008-06-06 22:52:48 +0000533 pj_lock_acquire(regc->lock);
534
Benny Prijonoccf95622006-02-07 18:48:01 +0000535 status = create_request(regc, &tdata);
Benny Prijono11893652008-06-06 22:52:48 +0000536 if (status != PJ_SUCCESS) {
537 pj_lock_release(regc->lock);
Benny Prijonoccf95622006-02-07 18:48:01 +0000538 return status;
Benny Prijono11893652008-06-06 22:52:48 +0000539 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000540
541 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000542
543 /* Add Contact headers. */
544 hdr = regc->contact_hdr_list.next;
545 while (hdr != &regc->contact_hdr_list) {
546 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
547 pjsip_hdr_shallow_clone(tdata->pool, hdr));
548 hdr = hdr->next;
549 }
550
Benny Prijonodd742da2008-05-17 12:45:00 +0000551 /* Also add bindings which are to be removed */
552 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
553 hdr = regc->removed_contact_hdr_list.next;
554 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
555 pjsip_hdr_clone(tdata->pool, hdr));
556 pj_list_erase(hdr);
557 }
558
559
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000560 if (regc->expires_hdr)
Benny Prijonoa1e69682007-05-11 15:14:34 +0000561 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
562 pjsip_hdr_shallow_clone(tdata->pool,
Benny Prijono4093f7c2006-09-13 23:48:45 +0000563 regc->expires_hdr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000564
565 if (regc->timer.id != 0) {
566 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
567 regc->timer.id = 0;
568 }
569
570 regc->auto_reg = autoreg;
571
Benny Prijono11893652008-06-06 22:52:48 +0000572 pj_lock_release(regc->lock);
573
Benny Prijonoccf95622006-02-07 18:48:01 +0000574 /* Done */
575 *p_tdata = tdata;
576 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000577}
578
579
Benny Prijonoccf95622006-02-07 18:48:01 +0000580PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
581 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000582{
583 pjsip_tx_data *tdata;
584 pjsip_msg *msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000585 pjsip_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000586 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000587
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000588 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
589
Benny Prijono11893652008-06-06 22:52:48 +0000590 pj_lock_acquire(regc->lock);
591
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000592 if (regc->timer.id != 0) {
593 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
594 regc->timer.id = 0;
595 }
596
Benny Prijonoccf95622006-02-07 18:48:01 +0000597 status = create_request(regc, &tdata);
Benny Prijono11893652008-06-06 22:52:48 +0000598 if (status != PJ_SUCCESS) {
599 pj_lock_release(regc->lock);
Benny Prijonoccf95622006-02-07 18:48:01 +0000600 return status;
Benny Prijono11893652008-06-06 22:52:48 +0000601 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000602
603 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000604
605 /* Add Contact headers. */
Benny Prijonodd742da2008-05-17 12:45:00 +0000606 hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
Benny Prijono43047252008-06-04 14:44:29 +0000607 while ((void*)hdr != (void*)&regc->contact_hdr_list) {
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000608 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
609 pjsip_hdr_shallow_clone(tdata->pool, hdr));
610 hdr = hdr->next;
611 }
612
Benny Prijonodd742da2008-05-17 12:45:00 +0000613 /* Also add bindings which are to be removed */
614 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
615 hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next;
616 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
617 pjsip_hdr_clone(tdata->pool, hdr));
618 pj_list_erase(hdr);
619 }
620
621 /* Add Expires:0 header */
622 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
623 pjsip_msg_add_hdr(msg, hdr);
Benny Prijonodfc4c482006-12-02 07:25:29 +0000624
Benny Prijono11893652008-06-06 22:52:48 +0000625 pj_lock_release(regc->lock);
626
Benny Prijonodfc4c482006-12-02 07:25:29 +0000627 *p_tdata = tdata;
628 return PJ_SUCCESS;
629}
630
631PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
632 pjsip_tx_data **p_tdata)
633{
634 pjsip_tx_data *tdata;
Benny Prijonodd742da2008-05-17 12:45:00 +0000635 pjsip_contact_hdr *hcontact;
636 pjsip_hdr *hdr;
Benny Prijonodfc4c482006-12-02 07:25:29 +0000637 pjsip_msg *msg;
638 pj_status_t status;
639
640 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
641
Benny Prijono11893652008-06-06 22:52:48 +0000642 pj_lock_acquire(regc->lock);
643
Benny Prijonodfc4c482006-12-02 07:25:29 +0000644 if (regc->timer.id != 0) {
645 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
646 regc->timer.id = 0;
647 }
648
649 status = create_request(regc, &tdata);
Benny Prijono11893652008-06-06 22:52:48 +0000650 if (status != PJ_SUCCESS) {
651 pj_lock_release(regc->lock);
Benny Prijonodfc4c482006-12-02 07:25:29 +0000652 return status;
Benny Prijono11893652008-06-06 22:52:48 +0000653 }
Benny Prijonodfc4c482006-12-02 07:25:29 +0000654
655 msg = tdata->msg;
Benny Prijonodd742da2008-05-17 12:45:00 +0000656
657 /* Clear removed_contact_hdr_list */
658 pj_list_init(&regc->removed_contact_hdr_list);
659
660 /* Add Contact:* header */
661 hcontact = pjsip_contact_hdr_create(tdata->pool);
662 hcontact->star = 1;
663 pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
664
665 /* Add Expires:0 header */
666 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
667 pjsip_msg_add_hdr(msg, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000668
Benny Prijono11893652008-06-06 22:52:48 +0000669 pj_lock_release(regc->lock);
670
Benny Prijonoccf95622006-02-07 18:48:01 +0000671 *p_tdata = tdata;
672 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000673}
674
675
676PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
677 int contact_cnt,
678 const pj_str_t contact[] )
679{
Benny Prijono11893652008-06-06 22:52:48 +0000680 pj_status_t status;
681
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000682 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono11893652008-06-06 22:52:48 +0000683
684 pj_lock_acquire(regc->lock);
685 status = set_contact( regc, contact_cnt, contact );
686 pj_lock_release(regc->lock);
687
688 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000689}
690
691
692PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
693 pj_uint32_t expires )
694{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000695 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono11893652008-06-06 22:52:48 +0000696
697 pj_lock_acquire(regc->lock);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000698 set_expires( regc, expires );
Benny Prijono11893652008-06-06 22:52:48 +0000699 pj_lock_release(regc->lock);
700
Benny Prijonoccf95622006-02-07 18:48:01 +0000701 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000702}
703
704
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000705static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
706 const pj_str_t *reason,
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000707 pjsip_rx_data *rdata, pj_int32_t expiration,
708 int contact_cnt, pjsip_contact_hdr *contact[])
709{
710 struct pjsip_regc_cbparam cbparam;
711
712
Benny Prijonof2651802007-01-26 17:13:56 +0000713 if (!regc->cb)
714 return;
715
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000716 cbparam.regc = regc;
717 cbparam.token = regc->token;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000718 cbparam.status = status;
719 cbparam.code = st_code;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000720 cbparam.reason = *reason;
721 cbparam.rdata = rdata;
722 cbparam.contact_cnt = contact_cnt;
723 cbparam.expiration = expiration;
724 if (contact_cnt) {
725 pj_memcpy( cbparam.contact, contact,
726 contact_cnt*sizeof(pjsip_contact_hdr*));
727 }
728
729 (*regc->cb)(&cbparam);
730}
731
732static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
733 struct pj_timer_entry *entry)
734{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000735 pjsip_regc *regc = (pjsip_regc*) entry->user_data;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000736 pjsip_tx_data *tdata;
Benny Prijonoccf95622006-02-07 18:48:01 +0000737 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000738
Benny Prijonoccf95622006-02-07 18:48:01 +0000739 PJ_UNUSED_ARG(timer_heap);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000740
Benny Prijonof2651802007-01-26 17:13:56 +0000741 /* Temporarily increase busy flag to prevent regc from being deleted
Benny Prijono11893652008-06-06 22:52:48 +0000742 * in pjsip_regc_send() or in the callback
Benny Prijonof2651802007-01-26 17:13:56 +0000743 */
Benny Prijono11893652008-06-06 22:52:48 +0000744 pj_atomic_inc(regc->busy_ctr);
Benny Prijonof2651802007-01-26 17:13:56 +0000745
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000746 entry->id = 0;
Benny Prijonoccf95622006-02-07 18:48:01 +0000747 status = pjsip_regc_register(regc, 1, &tdata);
748 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000749 status = pjsip_regc_send(regc, tdata);
750 }
751
Benny Prijonof2651802007-01-26 17:13:56 +0000752 if (status != PJ_SUCCESS && regc->cb) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000753 char errmsg[PJ_ERR_MSG_SIZE];
754 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000755 call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000756 }
Benny Prijonof2651802007-01-26 17:13:56 +0000757
Benny Prijonof2651802007-01-26 17:13:56 +0000758 /* Delete the record if user destroy regc during the callback. */
Benny Prijono11893652008-06-06 22:52:48 +0000759 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
Benny Prijonof2651802007-01-26 17:13:56 +0000760 pjsip_regc_destroy(regc);
761 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000762}
763
Benny Prijonodd742da2008-05-17 12:45:00 +0000764static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
765 const pjsip_rx_data *rdata,
766 unsigned *contact_cnt,
767 unsigned max_contact,
768 pjsip_contact_hdr *contacts[])
769{
770 pj_int32_t expiration = NOEXP;
771 const pjsip_msg *msg = rdata->msg_info.msg;
772 const pjsip_hdr *hdr;
773
774 /* Enumerate all Contact headers in the response */
775 *contact_cnt = 0;
776 for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
777 if (hdr->type == PJSIP_H_CONTACT &&
778 *contact_cnt < max_contact)
779 {
780 contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
781 ++(*contact_cnt);
782 }
783 }
784
785 if (regc->current_op == REGC_REGISTERING) {
786 pj_bool_t has_our_contact = PJ_FALSE;
787 const pjsip_expires_hdr *expires;
788
789 /* Get Expires header */
790 expires = (const pjsip_expires_hdr*)
791 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
792
793 /* Try to find the Contact URIs that we register, in the response
794 * to get the expires value. We'll try both with comparing the URI
795 * and comparing the extension param only.
796 */
797 if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
798 unsigned i;
799 for (i=0; i<*contact_cnt; ++i) {
800 const pjsip_contact_hdr *our_hdr;
801
802 our_hdr = (const pjsip_contact_hdr*)
803 regc->contact_hdr_list.next;
804
805 /* Match with our Contact header(s) */
806 while ((void*)our_hdr != (void*)&regc->contact_hdr_list) {
807
808 const pjsip_uri *uri1, *uri2;
809 pj_bool_t matched = PJ_FALSE;
810
811 /* Exclude the display name when comparing the URI
812 * since server may not return it.
813 */
814 uri1 = (const pjsip_uri*)
815 pjsip_uri_get_uri(contacts[i]->uri);
816 uri2 = (const pjsip_uri*)
817 pjsip_uri_get_uri(our_hdr->uri);
818
819 /* First try with exact matching, according to RFC 3261
820 * Section 19.1.4 URI Comparison
821 */
822 if (pjsip_cfg()->regc.check_contact) {
823 matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
824 uri1, uri2)==0;
825 }
826
827 /* If no match is found, try with matching the extension
828 * parameter only if extension parameter was added.
829 */
830 if (!matched && regc->add_xuid_param &&
831 (PJSIP_URI_SCHEME_IS_SIP(uri1) ||
832 PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&
833 (PJSIP_URI_SCHEME_IS_SIP(uri2) ||
834 PJSIP_URI_SCHEME_IS_SIPS(uri2)))
835 {
836 const pjsip_sip_uri *sip_uri1, *sip_uri2;
837 const pjsip_param *p1, *p2;
838
839 sip_uri1 = (const pjsip_sip_uri*)uri1;
840 sip_uri2 = (const pjsip_sip_uri*)uri2;
841
842 p1 = pjsip_param_cfind(&sip_uri1->other_param,
843 &XUID_PARAM_NAME);
844 p2 = pjsip_param_cfind(&sip_uri2->other_param,
845 &XUID_PARAM_NAME);
846 matched = p1 && p2 &&
847 pj_strcmp(&p1->value, &p2->value)==0;
848
849 }
850
851 if (matched) {
852 has_our_contact = PJ_TRUE;
853
854 if (contacts[i]->expires >= 0 &&
855 contacts[i]->expires < expiration)
856 {
857 /* Get the lowest expiration time. */
858 expiration = contacts[i]->expires;
859 }
860
861 break;
862 }
863
864 our_hdr = our_hdr->next;
865
866 } /* while ((void.. */
867
868 } /* for (i=.. */
869
870 /* If matching Contact header(s) are found but the
871 * header doesn't contain expires parameter, get the
872 * expiration value from the Expires header. And
873 * if Expires header is not present, get the expiration
874 * value from the request.
875 */
876 if (has_our_contact && expiration == NOEXP) {
877 if (expires) {
878 expiration = expires->ivalue;
879 } else if (regc->expires_hdr) {
880 expiration = regc->expires_hdr->ivalue;
881 } else {
882 /* We didn't request explicit expiration value,
883 * and server doesn't specify it either. This
884 * shouldn't happen unless we have a broken
885 * registrar.
886 */
887 expiration = 3600;
888 }
889 }
890
891 }
892
893 /* If we still couldn't get matching Contact header(s), it means
894 * there must be something wrong with the registrar (e.g. it may
895 * have modified the URI's in the response, which is prohibited).
896 */
897 if (expiration==NOEXP) {
898 /* If the number of Contact headers in the response matches
899 * ours, they're all probably ours. Get the expiration
900 * from there if this is the case, or from Expires header
901 * if we don't have exact Contact header count, or
902 * from the request as the last resort.
903 */
904 unsigned our_contact_cnt;
905
906 our_contact_cnt = pj_list_size(&regc->contact_hdr_list);
907
908 if (*contact_cnt == our_contact_cnt && *contact_cnt &&
909 contacts[0]->expires >= 0)
910 {
911 expiration = contacts[0]->expires;
912 } else if (expires)
913 expiration = expires->ivalue;
914 else if (regc->expires_hdr)
915 expiration = regc->expires_hdr->ivalue;
916 else
917 expiration = 3600;
918 }
919
920 } else {
921 /* Just assume that the unregistration has been successful. */
922 expiration = 0;
923 }
924
925 /* Must have expiration value by now */
926 pj_assert(expiration != NOEXP);
927
928 return expiration;
929}
930
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000931static void tsx_callback(void *token, pjsip_event *event)
932{
Benny Prijonoccf95622006-02-07 18:48:01 +0000933 pj_status_t status;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000934 pjsip_regc *regc = (pjsip_regc*) token;
Benny Prijonoccf95622006-02-07 18:48:01 +0000935 pjsip_transaction *tsx = event->body.tsx_state.tsx;
Benny Prijono11893652008-06-06 22:52:48 +0000936
937 pj_atomic_inc(regc->busy_ctr);
938 pj_lock_acquire(regc->lock);
939
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000940 /* Decrement pending transaction counter. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000941 pj_assert(regc->has_tsx);
942 regc->has_tsx = PJ_FALSE;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000943
Benny Prijonobdc093f2007-10-04 09:48:25 +0000944 /* Add reference to the transport */
945 if (tsx->transport != regc->last_transport) {
946 if (regc->last_transport) {
947 pjsip_transport_dec_ref(regc->last_transport);
948 regc->last_transport = NULL;
949 }
950
951 if (tsx->transport) {
952 regc->last_transport = tsx->transport;
953 pjsip_transport_add_ref(regc->last_transport);
954 }
955 }
956
Benny Prijono58990232007-01-21 16:11:18 +0000957 /* Handle 401/407 challenge (even when _delete_flag is set) */
958 if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
959 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000960 {
Benny Prijonoccf95622006-02-07 18:48:01 +0000961 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000962 pjsip_tx_data *tdata;
963
Benny Prijonodd742da2008-05-17 12:45:00 +0000964 /* reset current op */
965 regc->current_op = REGC_IDLE;
966
Benny Prijonoccf95622006-02-07 18:48:01 +0000967 status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
968 rdata,
969 tsx->last_tx,
970 &tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000971
Benny Prijonoccf95622006-02-07 18:48:01 +0000972 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000973 status = pjsip_regc_send(regc, tdata);
Benny Prijono3fe034a2007-06-01 12:51:07 +0000974 }
Benny Prijono27042582006-08-08 14:04:21 +0000975
976 if (status != PJ_SUCCESS) {
Benny Prijono58990232007-01-21 16:11:18 +0000977
978 /* Only call callback if application is still interested
979 * in it.
980 */
981 if (regc->_delete_flag == 0) {
Benny Prijono11893652008-06-06 22:52:48 +0000982 /* Should be safe to release the lock temporarily.
983 * We do this to avoid deadlock.
Benny Prijono58990232007-01-21 16:11:18 +0000984 */
Benny Prijono11893652008-06-06 22:52:48 +0000985 pj_lock_release(regc->lock);
Benny Prijono58990232007-01-21 16:11:18 +0000986 call_callback(regc, status, tsx->status_code,
987 &rdata->msg_info.msg->line.status.reason,
988 rdata, -1, 0, NULL);
Benny Prijono11893652008-06-06 22:52:48 +0000989 pj_lock_acquire(regc->lock);
Benny Prijono58990232007-01-21 16:11:18 +0000990 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000991 }
Benny Prijono27042582006-08-08 14:04:21 +0000992
Benny Prijono58990232007-01-21 16:11:18 +0000993 } else if (regc->_delete_flag) {
994
995 /* User has called pjsip_regc_destroy(), so don't call callback.
996 * This regc will be destroyed later in this function.
997 */
998
Benny Prijonodd742da2008-05-17 12:45:00 +0000999 /* Just reset current op */
1000 regc->current_op = REGC_IDLE;
Benny Prijono27042582006-08-08 14:04:21 +00001001
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001002 } else {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001003 pjsip_rx_data *rdata;
Benny Prijonoe1c984f2007-11-04 02:05:13 +00001004 pj_int32_t expiration = NOEXP;
Benny Prijonodd742da2008-05-17 12:45:00 +00001005 unsigned contact_cnt = 0;
1006 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001007
1008 if (tsx->status_code/100 == 2) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001009
Benny Prijonoccf95622006-02-07 18:48:01 +00001010 rdata = event->body.tsx_state.src.rdata;
Benny Prijonoe1c984f2007-11-04 02:05:13 +00001011
Benny Prijonodd742da2008-05-17 12:45:00 +00001012 /* Calculate expiration */
1013 expiration = calculate_response_expiration(regc, rdata,
1014 &contact_cnt,
1015 PJSIP_REGC_MAX_CONTACT,
1016 contact);
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001017
Benny Prijonodd742da2008-05-17 12:45:00 +00001018 /* Mark operation as complete */
1019 regc->current_op = REGC_IDLE;
Benny Prijonoe1c984f2007-11-04 02:05:13 +00001020
1021 /* Schedule next registration */
Benny Prijonodd742da2008-05-17 12:45:00 +00001022 if (regc->auto_reg && expiration > 0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001023 pj_time_val delay = { 0, 0};
1024
1025 delay.sec = expiration - DELAY_BEFORE_REFRESH;
1026 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
1027 delay.sec > (pj_int32_t)regc->expires)
1028 {
1029 delay.sec = regc->expires;
1030 }
1031 if (delay.sec < DELAY_BEFORE_REFRESH)
1032 delay.sec = DELAY_BEFORE_REFRESH;
1033 regc->timer.cb = &regc_refresh_timer_cb;
1034 regc->timer.id = REFRESH_TIMER;
1035 regc->timer.user_data = regc;
1036 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001037 pj_gettimeofday(&regc->last_reg);
1038 regc->next_reg = regc->last_reg;
1039 regc->next_reg.sec += delay.sec;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001040 }
1041
1042 } else {
Benny Prijonoccf95622006-02-07 18:48:01 +00001043 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
1044 event->body.tsx_state.src.rdata : NULL;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001045 }
1046
Benny Prijonodd742da2008-05-17 12:45:00 +00001047 /* Update registration */
1048 if (expiration==NOEXP) expiration=-1;
1049 regc->expires = expiration;
1050
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001051 /* Call callback. */
Benny Prijono11893652008-06-06 22:52:48 +00001052 /* Should be safe to release the lock temporarily.
1053 * We do this to avoid deadlock.
1054 */
1055 pj_lock_release(regc->lock);
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001056 call_callback(regc, PJ_SUCCESS, tsx->status_code,
Benny Prijonoccf95622006-02-07 18:48:01 +00001057 (rdata ? &rdata->msg_info.msg->line.status.reason
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001058 : pjsip_get_status_text(tsx->status_code)),
1059 rdata, expiration,
1060 contact_cnt, contact);
Benny Prijono11893652008-06-06 22:52:48 +00001061 pj_lock_acquire(regc->lock);
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001062 }
1063
Benny Prijono11893652008-06-06 22:52:48 +00001064 pj_lock_release(regc->lock);
1065
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001066 /* Delete the record if user destroy regc during the callback. */
Benny Prijono11893652008-06-06 22:52:48 +00001067 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001068 pjsip_regc_destroy(regc);
1069 }
1070}
1071
Benny Prijonoccf95622006-02-07 18:48:01 +00001072PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001073{
Benny Prijonoccf95622006-02-07 18:48:01 +00001074 pj_status_t status;
Benny Prijono59ca70f2006-02-22 22:18:58 +00001075 pjsip_cseq_hdr *cseq_hdr;
Benny Prijonodd742da2008-05-17 12:45:00 +00001076 pjsip_expires_hdr *expires_hdr;
Benny Prijono59ca70f2006-02-22 22:18:58 +00001077 pj_uint32_t cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001078
Benny Prijono11893652008-06-06 22:52:48 +00001079 pj_atomic_inc(regc->busy_ctr);
1080 pj_lock_acquire(regc->lock);
1081
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001082 /* Make sure we don't have pending transaction. */
Benny Prijono25a86c72006-12-01 20:50:01 +00001083 if (regc->has_tsx) {
Benny Prijono5b656872006-08-08 00:41:00 +00001084 PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
1085 "transaction pending"));
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001086 pjsip_tx_data_dec_ref( tdata );
Benny Prijono11893652008-06-06 22:52:48 +00001087 pj_lock_release(regc->lock);
1088 pj_atomic_dec(regc->busy_ctr);
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001089 return PJSIP_EBUSY;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001090 }
1091
Benny Prijonodd742da2008-05-17 12:45:00 +00001092 pj_assert(regc->current_op == REGC_IDLE);
1093
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001094 /* Invalidate message buffer. */
1095 pjsip_tx_data_invalidate_msg(tdata);
1096
1097 /* Increment CSeq */
Benny Prijono59ca70f2006-02-22 22:18:58 +00001098 cseq = ++regc->cseq_hdr->cseq;
Benny Prijonoa1e69682007-05-11 15:14:34 +00001099 cseq_hdr = (pjsip_cseq_hdr*)
1100 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
Benny Prijono59ca70f2006-02-22 22:18:58 +00001101 cseq_hdr->cseq = cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001102
Benny Prijonodd742da2008-05-17 12:45:00 +00001103 /* Find Expires header */
1104 expires_hdr = (pjsip_expires_hdr*)
1105 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
1106
Benny Prijono37db51c2007-11-23 03:41:54 +00001107 /* Bind to transport selector */
1108 pjsip_tx_data_set_transport(tdata, &regc->tp_sel);
1109
Benny Prijono25a86c72006-12-01 20:50:01 +00001110 regc->has_tsx = PJ_TRUE;
Benny Prijonodd742da2008-05-17 12:45:00 +00001111
1112 /* Set current operation based on the value of Expires header */
1113 if (expires_hdr && expires_hdr->ivalue==0)
1114 regc->current_op = REGC_UNREGISTERING;
1115 else
1116 regc->current_op = REGC_REGISTERING;
1117
Benny Prijono0f35f912007-02-05 18:59:31 +00001118 status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
1119 regc, &tsx_callback);
Benny Prijono5b656872006-08-08 00:41:00 +00001120 if (status!=PJ_SUCCESS) {
Benny Prijono5b656872006-08-08 00:41:00 +00001121 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
1122 }
Benny Prijono11893652008-06-06 22:52:48 +00001123
1124 pj_lock_release(regc->lock);
Benny Prijono197cabf2006-10-16 20:05:27 +00001125
1126 /* Delete the record if user destroy regc during the callback. */
Benny Prijono11893652008-06-06 22:52:48 +00001127 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
Benny Prijono197cabf2006-10-16 20:05:27 +00001128 pjsip_regc_destroy(regc);
1129 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001130
1131 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001132}
1133
1134