blob: ed15dfbe8a5dfc8deac93ade0727ca65803e9940 [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>
30#include <pj/os.h>
31#include <pj/pool.h>
32#include <pj/log.h>
Benny Prijono59ca70f2006-02-22 22:18:58 +000033#include <pj/rand.h>
Benny Prijonobcaed6c2006-02-19 15:37:19 +000034#include <pj/string.h>
Benny Prijonoccf95622006-02-07 18:48:01 +000035
Benny Prijono5dcb38d2005-11-21 01:55:47 +000036
37#define REFRESH_TIMER 1
Benny Prijonodd742da2008-05-17 12:45:00 +000038#define DELAY_BEFORE_REFRESH PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH
Benny Prijono315999b2007-09-30 10:58:36 +000039#define THIS_FILE "sip_reg.c"
Benny Prijono5dcb38d2005-11-21 01:55:47 +000040
Benny Prijono0f35f912007-02-05 18:59:31 +000041/* Outgoing transaction timeout when server sends 100 but never replies
42 * with final response. Value is in MILISECONDS!
43 */
44#define REGC_TSX_TIMEOUT 33000
45
Benny Prijonodd742da2008-05-17 12:45:00 +000046enum { NOEXP = 0x1FFFFFFF };
47
48static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 };
49
50
51/* Current/pending operation */
52enum regc_op
53{
54 REGC_IDLE,
55 REGC_REGISTERING,
56 REGC_UNREGISTERING
57};
Benny Prijono0f35f912007-02-05 18:59:31 +000058
Benny Prijono5dcb38d2005-11-21 01:55:47 +000059/**
60 * SIP client registration structure.
61 */
62struct pjsip_regc
63{
Benny Prijono84126ab2006-02-09 09:30:09 +000064 pj_pool_t *pool;
65 pjsip_endpoint *endpt;
66 pj_bool_t _delete_flag;
Benny Prijono25a86c72006-12-01 20:50:01 +000067 pj_bool_t has_tsx;
68 pj_int32_t busy;
Benny Prijonodd742da2008-05-17 12:45:00 +000069 enum regc_op current_op;
70
71 pj_bool_t add_xuid_param;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000072
Benny Prijono84126ab2006-02-09 09:30:09 +000073 void *token;
74 pjsip_regc_cb *cb;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000075
Benny Prijono84126ab2006-02-09 09:30:09 +000076 pj_str_t str_srv_url;
77 pjsip_uri *srv_url;
78 pjsip_cid_hdr *cid_hdr;
79 pjsip_cseq_hdr *cseq_hdr;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000080 pj_str_t from_uri;
Benny Prijono84126ab2006-02-09 09:30:09 +000081 pjsip_from_hdr *from_hdr;
82 pjsip_to_hdr *to_hdr;
Benny Prijonodd742da2008-05-17 12:45:00 +000083 pjsip_contact_hdr contact_hdr_list;
84 pjsip_contact_hdr removed_contact_hdr_list;
Benny Prijono84126ab2006-02-09 09:30:09 +000085 pjsip_expires_hdr *expires_hdr;
Benny Prijono84126ab2006-02-09 09:30:09 +000086 pj_uint32_t expires;
87 pjsip_route_hdr route_set;
Benny Prijono8fc6de02006-11-11 21:25:55 +000088 pjsip_hdr hdr_list;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000089
Benny Prijono5dcb38d2005-11-21 01:55:47 +000090 /* Authorization sessions. */
Benny Prijono84126ab2006-02-09 09:30:09 +000091 pjsip_auth_clt_sess auth_sess;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000092
93 /* Auto refresh registration. */
Benny Prijono84126ab2006-02-09 09:30:09 +000094 pj_bool_t auto_reg;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000095 pj_time_val last_reg;
96 pj_time_val next_reg;
Benny Prijono84126ab2006-02-09 09:30:09 +000097 pj_timer_entry timer;
Benny Prijono720d0a82007-01-12 06:37:35 +000098
99 /* Transport selector */
100 pjsip_tpselector tp_sel;
Benny Prijonobdc093f2007-10-04 09:48:25 +0000101
102 /* Last transport used. We acquire the transport to keep
103 * it open.
104 */
105 pjsip_transport *last_transport;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000106};
107
108
Benny Prijonoccf95622006-02-07 18:48:01 +0000109PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
110 pjsip_regc_cb *cb,
111 pjsip_regc **p_regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000112{
113 pj_pool_t *pool;
114 pjsip_regc *regc;
Benny Prijonoccf95622006-02-07 18:48:01 +0000115 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000116
Benny Prijonoccf95622006-02-07 18:48:01 +0000117 /* Verify arguments. */
118 PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000119
120 pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
Benny Prijonoccf95622006-02-07 18:48:01 +0000121 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
122
Benny Prijonoa1e69682007-05-11 15:14:34 +0000123 regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000124
125 regc->pool = pool;
126 regc->endpt = endpt;
127 regc->token = token;
128 regc->cb = cb;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000129 regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
Benny Prijonodd742da2008-05-17 12:45:00 +0000130 regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000131
Benny Prijonoccf95622006-02-07 18:48:01 +0000132 status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
133 if (status != PJ_SUCCESS)
134 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000135
Benny Prijono84126ab2006-02-09 09:30:09 +0000136 pj_list_init(&regc->route_set);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000137 pj_list_init(&regc->hdr_list);
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000138 pj_list_init(&regc->contact_hdr_list);
Benny Prijonodd742da2008-05-17 12:45:00 +0000139 pj_list_init(&regc->removed_contact_hdr_list);
Benny Prijono84126ab2006-02-09 09:30:09 +0000140
Benny Prijonoccf95622006-02-07 18:48:01 +0000141 /* Done */
142 *p_regc = regc;
143 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000144}
145
146
Benny Prijonoccf95622006-02-07 18:48:01 +0000147PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000148{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000149 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
150
Benny Prijono25a86c72006-12-01 20:50:01 +0000151 if (regc->has_tsx || regc->busy) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000152 regc->_delete_flag = 1;
153 regc->cb = NULL;
154 } else {
Benny Prijono720d0a82007-01-12 06:37:35 +0000155 pjsip_tpselector_dec_ref(&regc->tp_sel);
Benny Prijonobdc093f2007-10-04 09:48:25 +0000156 if (regc->last_transport) {
157 pjsip_transport_dec_ref(regc->last_transport);
158 regc->last_transport = NULL;
159 }
Benny Prijonodd742da2008-05-17 12:45:00 +0000160 if (regc->timer.id != 0) {
161 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
162 regc->timer.id = 0;
163 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000164 pjsip_endpt_release_pool(regc->endpt, regc->pool);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000165 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000166
167 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000168}
169
170
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000171PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
172 pjsip_regc_info *info )
173{
174 PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
175
176 info->server_uri = regc->str_srv_url;
177 info->client_uri = regc->from_uri;
Benny Prijono25a86c72006-12-01 20:50:01 +0000178 info->is_busy = (regc->busy || regc->has_tsx);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000179 info->auto_reg = regc->auto_reg;
180 info->interval = regc->expires;
181
Benny Prijono25a86c72006-12-01 20:50:01 +0000182 if (regc->has_tsx)
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000183 info->next_reg = 0;
184 else if (regc->auto_reg == 0)
185 info->next_reg = 0;
186 else if (regc->expires < 0)
187 info->next_reg = regc->expires;
188 else {
189 pj_time_val now, next_reg;
190
191 next_reg = regc->next_reg;
192 pj_gettimeofday(&now);
193 PJ_TIME_VAL_SUB(next_reg, now);
194 info->next_reg = next_reg.sec;
195 }
196
197 return PJ_SUCCESS;
198}
199
200
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000201PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
202{
203 return regc->pool;
204}
205
206static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
207{
208 if (expires != regc->expires) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000209 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000210 } else {
211 regc->expires_hdr = NULL;
212 }
213}
214
215
216static pj_status_t set_contact( pjsip_regc *regc,
217 int contact_cnt,
218 const pj_str_t contact[] )
219{
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000220 const pj_str_t CONTACT = { "Contact", 7 };
Benny Prijonodd742da2008-05-17 12:45:00 +0000221 pjsip_contact_hdr *h;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000222 int i;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000223
Benny Prijonodd742da2008-05-17 12:45:00 +0000224 /* Save existing contact list to removed_contact_hdr_list and
225 * clear contact_hdr_list.
226 */
227 pj_list_merge_last(&regc->removed_contact_hdr_list,
228 &regc->contact_hdr_list);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000229
Benny Prijonodd742da2008-05-17 12:45:00 +0000230 /* Set the expiration of Contacts in to removed_contact_hdr_list
231 * zero.
232 */
233 h = regc->removed_contact_hdr_list.next;
234 while (h != &regc->removed_contact_hdr_list) {
235 h->expires = 0;
236 h = h->next;
237 }
238
239 /* Process new contacts */
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000240 for (i=0; i<contact_cnt; ++i) {
Benny Prijonodd742da2008-05-17 12:45:00 +0000241 pjsip_contact_hdr *hdr;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000242 pj_str_t tmp;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000243
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000244 pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
Benny Prijonodd742da2008-05-17 12:45:00 +0000245 hdr = (pjsip_contact_hdr*)
Benny Prijono91a5a3a2007-09-24 21:16:48 +0000246 pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000247 if (hdr == NULL) {
248 PJ_LOG(4,(THIS_FILE, "Invalid Contact URI: \"%.*s\"",
249 (int)tmp.slen, tmp.ptr));
250 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000251 }
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000252
Benny Prijonodd742da2008-05-17 12:45:00 +0000253 /* Find the new contact in old contact list. If found, remove
254 * the old header from the old header list.
255 */
256 h = regc->removed_contact_hdr_list.next;
257 while (h != &regc->removed_contact_hdr_list) {
258 int rc;
259
260 rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
261 h->uri, hdr->uri);
262 if (rc == 0) {
263 /* Match */
264 pj_list_erase(h);
265 break;
266 }
267
268 h = h->next;
269 }
270
271 /* If add_xuid_param option is enabled and Contact URI is sip/sips,
272 * add xuid parameter to assist matching the Contact URI in the
273 * REGISTER response later.
274 */
275 if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
276 PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))
277 {
278 pjsip_param *xuid_param;
279 pjsip_sip_uri *sip_uri;
280
281 xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
282 xuid_param->name = XUID_PARAM_NAME;
283 pj_create_unique_string(regc->pool, &xuid_param->value);
284
285 sip_uri = pjsip_uri_get_uri(hdr->uri);
286 pj_list_push_back(&sip_uri->other_param, xuid_param);
287 }
288
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000289 pj_list_push_back(&regc->contact_hdr_list, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000290 }
291
Benny Prijonoccf95622006-02-07 18:48:01 +0000292 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000293}
294
295
296PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
297 const pj_str_t *srv_url,
298 const pj_str_t *from_url,
299 const pj_str_t *to_url,
300 int contact_cnt,
301 const pj_str_t contact[],
302 pj_uint32_t expires)
303{
304 pj_str_t tmp;
Benny Prijonoccf95622006-02-07 18:48:01 +0000305 pj_status_t status;
306
307 PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
308 contact_cnt && contact && expires, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000309
310 /* Copy server URL. */
311 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
312
313 /* Set server URL. */
314 tmp = regc->str_srv_url;
315 regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
316 if (regc->srv_url == NULL) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000317 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000318 }
319
320 /* Set "From" header. */
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000321 pj_strdup_with_null(regc->pool, &regc->from_uri, from_url);
322 tmp = regc->from_uri;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000323 regc->from_hdr = pjsip_from_hdr_create(regc->pool);
324 regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
325 PJSIP_PARSE_URI_AS_NAMEADDR);
326 if (!regc->from_hdr->uri) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000327 PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
328 from_url->slen, from_url->ptr));
329 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000330 }
331
332 /* Set "To" header. */
333 pj_strdup_with_null(regc->pool, &tmp, to_url);
334 regc->to_hdr = pjsip_to_hdr_create(regc->pool);
335 regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
336 PJSIP_PARSE_URI_AS_NAMEADDR);
337 if (!regc->to_hdr->uri) {
338 PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
Benny Prijonoccf95622006-02-07 18:48:01 +0000339 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000340 }
341
342
343 /* Set "Contact" header. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000344 status = set_contact( regc, contact_cnt, contact);
345 if (status != PJ_SUCCESS)
346 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000347
348 /* Set "Expires" header, if required. */
349 set_expires( regc, expires);
350
351 /* Set "Call-ID" header. */
352 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
353 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
354
355 /* Set "CSeq" header. */
356 regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
Benny Prijono59ca70f2006-02-22 22:18:58 +0000357 regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000358 pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
359
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000360 /* Done. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000361 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000362}
363
364PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
365 int count,
366 const pjsip_cred_info cred[] )
367{
Benny Prijonoccf95622006-02-07 18:48:01 +0000368 PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
369 return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000370}
371
Benny Prijono48ab2b72007-11-08 09:24:30 +0000372PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
373 const pjsip_auth_clt_pref *pref)
374{
375 PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
376 return pjsip_auth_clt_set_prefs(&regc->auth_sess, pref);
377}
378
Benny Prijono84126ab2006-02-09 09:30:09 +0000379PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
380 const pjsip_route_hdr *route_set)
381{
382 const pjsip_route_hdr *chdr;
383
384 PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
385
386 pj_list_init(&regc->route_set);
387
388 chdr = route_set->next;
389 while (chdr != route_set) {
390 pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
391 chdr = chdr->next;
392 }
393
394 return PJ_SUCCESS;
395}
396
Benny Prijono720d0a82007-01-12 06:37:35 +0000397
398/*
399 * Bind client registration to a specific transport/listener.
400 */
401PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
402 const pjsip_tpselector *sel)
403{
404 PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
405
406 pjsip_tpselector_dec_ref(&regc->tp_sel);
407 pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
408 pjsip_tpselector_add_ref(&regc->tp_sel);
409
410 return PJ_SUCCESS;
411}
412
413
Benny Prijono8fc6de02006-11-11 21:25:55 +0000414PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
415 const pjsip_hdr *hdr_list)
416{
417 const pjsip_hdr *hdr;
418
419 PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
420
421 //This is "add" operation, so don't remove headers.
422 //pj_list_init(&regc->hdr_list);
423
424 hdr = hdr_list->next;
425 while (hdr != hdr_list) {
426 pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
427 hdr = hdr->next;
428 }
429
430 return PJ_SUCCESS;
431}
432
Benny Prijonoccf95622006-02-07 18:48:01 +0000433static pj_status_t create_request(pjsip_regc *regc,
434 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000435{
Benny Prijonoccf95622006-02-07 18:48:01 +0000436 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000437 pjsip_tx_data *tdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000438
Benny Prijonoccf95622006-02-07 18:48:01 +0000439 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000440
Benny Prijonoccf95622006-02-07 18:48:01 +0000441 /* Create the request. */
442 status = pjsip_endpt_create_request_from_hdr( regc->endpt,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000443 pjsip_get_register_method(),
Benny Prijonoccf95622006-02-07 18:48:01 +0000444 regc->srv_url,
445 regc->from_hdr,
446 regc->to_hdr,
447 NULL,
448 regc->cid_hdr,
449 regc->cseq_hdr->cseq,
450 NULL,
451 &tdata);
452 if (status != PJ_SUCCESS)
453 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000454
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000455 /* Add cached authorization headers. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000456 pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000457
Benny Prijono84126ab2006-02-09 09:30:09 +0000458 /* Add Route headers from route set, ideally after Via header */
459 if (!pj_list_empty(&regc->route_set)) {
460 pjsip_hdr *route_pos;
461 const pjsip_route_hdr *route;
462
Benny Prijonoa1e69682007-05-11 15:14:34 +0000463 route_pos = (pjsip_hdr*)
464 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000465 if (!route_pos)
466 route_pos = &tdata->msg->hdr;
467
468 route = regc->route_set.next;
469 while (route != &regc->route_set) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000470 pjsip_hdr *new_hdr = (pjsip_hdr*)
471 pjsip_hdr_shallow_clone(tdata->pool, route);
Benny Prijono84126ab2006-02-09 09:30:09 +0000472 pj_list_insert_after(route_pos, new_hdr);
473 route_pos = new_hdr;
474 route = route->next;
475 }
476 }
477
Benny Prijono8fc6de02006-11-11 21:25:55 +0000478 /* Add additional request headers */
479 if (!pj_list_empty(&regc->hdr_list)) {
480 const pjsip_hdr *hdr;
481
482 hdr = regc->hdr_list.next;
483 while (hdr != &regc->hdr_list) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000484 pjsip_hdr *new_hdr = (pjsip_hdr*)
485 pjsip_hdr_shallow_clone(tdata->pool, hdr);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000486 pjsip_msg_add_hdr(tdata->msg, new_hdr);
487 hdr = hdr->next;
488 }
489 }
490
Benny Prijonoccf95622006-02-07 18:48:01 +0000491 /* Done. */
492 *p_tdata = tdata;
493 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000494}
495
496
Benny Prijonoccf95622006-02-07 18:48:01 +0000497PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
498 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000499{
500 pjsip_msg *msg;
Benny Prijonodd742da2008-05-17 12:45:00 +0000501 pjsip_contact_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000502 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000503 pjsip_tx_data *tdata;
504
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000505 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
506
Benny Prijonoccf95622006-02-07 18:48:01 +0000507 status = create_request(regc, &tdata);
508 if (status != PJ_SUCCESS)
509 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000510
511 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000512
513 /* Add Contact headers. */
514 hdr = regc->contact_hdr_list.next;
515 while (hdr != &regc->contact_hdr_list) {
516 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
517 pjsip_hdr_shallow_clone(tdata->pool, hdr));
518 hdr = hdr->next;
519 }
520
Benny Prijonodd742da2008-05-17 12:45:00 +0000521 /* Also add bindings which are to be removed */
522 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
523 hdr = regc->removed_contact_hdr_list.next;
524 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
525 pjsip_hdr_clone(tdata->pool, hdr));
526 pj_list_erase(hdr);
527 }
528
529
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000530 if (regc->expires_hdr)
Benny Prijonoa1e69682007-05-11 15:14:34 +0000531 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
532 pjsip_hdr_shallow_clone(tdata->pool,
Benny Prijono4093f7c2006-09-13 23:48:45 +0000533 regc->expires_hdr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000534
535 if (regc->timer.id != 0) {
536 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
537 regc->timer.id = 0;
538 }
539
540 regc->auto_reg = autoreg;
541
Benny Prijonoccf95622006-02-07 18:48:01 +0000542 /* Done */
543 *p_tdata = tdata;
544 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000545}
546
547
Benny Prijonoccf95622006-02-07 18:48:01 +0000548PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
549 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000550{
551 pjsip_tx_data *tdata;
552 pjsip_msg *msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000553 pjsip_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000554 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000555
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000556 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
557
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000558 if (regc->timer.id != 0) {
559 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
560 regc->timer.id = 0;
561 }
562
Benny Prijonoccf95622006-02-07 18:48:01 +0000563 status = create_request(regc, &tdata);
564 if (status != PJ_SUCCESS)
565 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000566
567 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000568
569 /* Add Contact headers. */
Benny Prijonodd742da2008-05-17 12:45:00 +0000570 hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
Benny Prijono43047252008-06-04 14:44:29 +0000571 while ((void*)hdr != (void*)&regc->contact_hdr_list) {
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000572 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
573 pjsip_hdr_shallow_clone(tdata->pool, hdr));
574 hdr = hdr->next;
575 }
576
Benny Prijonodd742da2008-05-17 12:45:00 +0000577 /* Also add bindings which are to be removed */
578 while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
579 hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next;
580 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
581 pjsip_hdr_clone(tdata->pool, hdr));
582 pj_list_erase(hdr);
583 }
584
585 /* Add Expires:0 header */
586 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
587 pjsip_msg_add_hdr(msg, hdr);
Benny Prijonodfc4c482006-12-02 07:25:29 +0000588
589 *p_tdata = tdata;
590 return PJ_SUCCESS;
591}
592
593PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
594 pjsip_tx_data **p_tdata)
595{
596 pjsip_tx_data *tdata;
Benny Prijonodd742da2008-05-17 12:45:00 +0000597 pjsip_contact_hdr *hcontact;
598 pjsip_hdr *hdr;
Benny Prijonodfc4c482006-12-02 07:25:29 +0000599 pjsip_msg *msg;
600 pj_status_t status;
601
602 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
603
604 if (regc->timer.id != 0) {
605 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
606 regc->timer.id = 0;
607 }
608
609 status = create_request(regc, &tdata);
610 if (status != PJ_SUCCESS)
611 return status;
612
613 msg = tdata->msg;
Benny Prijonodd742da2008-05-17 12:45:00 +0000614
615 /* Clear removed_contact_hdr_list */
616 pj_list_init(&regc->removed_contact_hdr_list);
617
618 /* Add Contact:* header */
619 hcontact = pjsip_contact_hdr_create(tdata->pool);
620 hcontact->star = 1;
621 pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
622
623 /* Add Expires:0 header */
624 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
625 pjsip_msg_add_hdr(msg, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000626
Benny Prijonoccf95622006-02-07 18:48:01 +0000627 *p_tdata = tdata;
628 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000629}
630
631
632PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
633 int contact_cnt,
634 const pj_str_t contact[] )
635{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000636 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000637 return set_contact( regc, contact_cnt, contact );
638}
639
640
641PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
642 pj_uint32_t expires )
643{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000644 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000645 set_expires( regc, expires );
Benny Prijonoccf95622006-02-07 18:48:01 +0000646 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000647}
648
649
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000650static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
651 const pj_str_t *reason,
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000652 pjsip_rx_data *rdata, pj_int32_t expiration,
653 int contact_cnt, pjsip_contact_hdr *contact[])
654{
655 struct pjsip_regc_cbparam cbparam;
656
657
Benny Prijonof2651802007-01-26 17:13:56 +0000658 if (!regc->cb)
659 return;
660
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000661 cbparam.regc = regc;
662 cbparam.token = regc->token;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000663 cbparam.status = status;
664 cbparam.code = st_code;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000665 cbparam.reason = *reason;
666 cbparam.rdata = rdata;
667 cbparam.contact_cnt = contact_cnt;
668 cbparam.expiration = expiration;
669 if (contact_cnt) {
670 pj_memcpy( cbparam.contact, contact,
671 contact_cnt*sizeof(pjsip_contact_hdr*));
672 }
673
674 (*regc->cb)(&cbparam);
675}
676
677static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
678 struct pj_timer_entry *entry)
679{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000680 pjsip_regc *regc = (pjsip_regc*) entry->user_data;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000681 pjsip_tx_data *tdata;
Benny Prijonoccf95622006-02-07 18:48:01 +0000682 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000683
Benny Prijonoccf95622006-02-07 18:48:01 +0000684 PJ_UNUSED_ARG(timer_heap);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000685
Benny Prijonof2651802007-01-26 17:13:56 +0000686 /* Temporarily increase busy flag to prevent regc from being deleted
687 * in pjsip_regc_send()
688 */
689 regc->busy++;
690
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000691 entry->id = 0;
Benny Prijonoccf95622006-02-07 18:48:01 +0000692 status = pjsip_regc_register(regc, 1, &tdata);
693 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000694 status = pjsip_regc_send(regc, tdata);
695 }
696
Benny Prijonof2651802007-01-26 17:13:56 +0000697 if (status != PJ_SUCCESS && regc->cb) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000698 char errmsg[PJ_ERR_MSG_SIZE];
699 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000700 call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000701 }
Benny Prijonof2651802007-01-26 17:13:56 +0000702
703 regc->busy--;
704
705 /* Delete the record if user destroy regc during the callback. */
706 if (regc->_delete_flag && regc->busy==0) {
707 pjsip_regc_destroy(regc);
708 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000709}
710
Benny Prijonodd742da2008-05-17 12:45:00 +0000711static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
712 const pjsip_rx_data *rdata,
713 unsigned *contact_cnt,
714 unsigned max_contact,
715 pjsip_contact_hdr *contacts[])
716{
717 pj_int32_t expiration = NOEXP;
718 const pjsip_msg *msg = rdata->msg_info.msg;
719 const pjsip_hdr *hdr;
720
721 /* Enumerate all Contact headers in the response */
722 *contact_cnt = 0;
723 for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
724 if (hdr->type == PJSIP_H_CONTACT &&
725 *contact_cnt < max_contact)
726 {
727 contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
728 ++(*contact_cnt);
729 }
730 }
731
732 if (regc->current_op == REGC_REGISTERING) {
733 pj_bool_t has_our_contact = PJ_FALSE;
734 const pjsip_expires_hdr *expires;
735
736 /* Get Expires header */
737 expires = (const pjsip_expires_hdr*)
738 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
739
740 /* Try to find the Contact URIs that we register, in the response
741 * to get the expires value. We'll try both with comparing the URI
742 * and comparing the extension param only.
743 */
744 if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
745 unsigned i;
746 for (i=0; i<*contact_cnt; ++i) {
747 const pjsip_contact_hdr *our_hdr;
748
749 our_hdr = (const pjsip_contact_hdr*)
750 regc->contact_hdr_list.next;
751
752 /* Match with our Contact header(s) */
753 while ((void*)our_hdr != (void*)&regc->contact_hdr_list) {
754
755 const pjsip_uri *uri1, *uri2;
756 pj_bool_t matched = PJ_FALSE;
757
758 /* Exclude the display name when comparing the URI
759 * since server may not return it.
760 */
761 uri1 = (const pjsip_uri*)
762 pjsip_uri_get_uri(contacts[i]->uri);
763 uri2 = (const pjsip_uri*)
764 pjsip_uri_get_uri(our_hdr->uri);
765
766 /* First try with exact matching, according to RFC 3261
767 * Section 19.1.4 URI Comparison
768 */
769 if (pjsip_cfg()->regc.check_contact) {
770 matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
771 uri1, uri2)==0;
772 }
773
774 /* If no match is found, try with matching the extension
775 * parameter only if extension parameter was added.
776 */
777 if (!matched && regc->add_xuid_param &&
778 (PJSIP_URI_SCHEME_IS_SIP(uri1) ||
779 PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&
780 (PJSIP_URI_SCHEME_IS_SIP(uri2) ||
781 PJSIP_URI_SCHEME_IS_SIPS(uri2)))
782 {
783 const pjsip_sip_uri *sip_uri1, *sip_uri2;
784 const pjsip_param *p1, *p2;
785
786 sip_uri1 = (const pjsip_sip_uri*)uri1;
787 sip_uri2 = (const pjsip_sip_uri*)uri2;
788
789 p1 = pjsip_param_cfind(&sip_uri1->other_param,
790 &XUID_PARAM_NAME);
791 p2 = pjsip_param_cfind(&sip_uri2->other_param,
792 &XUID_PARAM_NAME);
793 matched = p1 && p2 &&
794 pj_strcmp(&p1->value, &p2->value)==0;
795
796 }
797
798 if (matched) {
799 has_our_contact = PJ_TRUE;
800
801 if (contacts[i]->expires >= 0 &&
802 contacts[i]->expires < expiration)
803 {
804 /* Get the lowest expiration time. */
805 expiration = contacts[i]->expires;
806 }
807
808 break;
809 }
810
811 our_hdr = our_hdr->next;
812
813 } /* while ((void.. */
814
815 } /* for (i=.. */
816
817 /* If matching Contact header(s) are found but the
818 * header doesn't contain expires parameter, get the
819 * expiration value from the Expires header. And
820 * if Expires header is not present, get the expiration
821 * value from the request.
822 */
823 if (has_our_contact && expiration == NOEXP) {
824 if (expires) {
825 expiration = expires->ivalue;
826 } else if (regc->expires_hdr) {
827 expiration = regc->expires_hdr->ivalue;
828 } else {
829 /* We didn't request explicit expiration value,
830 * and server doesn't specify it either. This
831 * shouldn't happen unless we have a broken
832 * registrar.
833 */
834 expiration = 3600;
835 }
836 }
837
838 }
839
840 /* If we still couldn't get matching Contact header(s), it means
841 * there must be something wrong with the registrar (e.g. it may
842 * have modified the URI's in the response, which is prohibited).
843 */
844 if (expiration==NOEXP) {
845 /* If the number of Contact headers in the response matches
846 * ours, they're all probably ours. Get the expiration
847 * from there if this is the case, or from Expires header
848 * if we don't have exact Contact header count, or
849 * from the request as the last resort.
850 */
851 unsigned our_contact_cnt;
852
853 our_contact_cnt = pj_list_size(&regc->contact_hdr_list);
854
855 if (*contact_cnt == our_contact_cnt && *contact_cnt &&
856 contacts[0]->expires >= 0)
857 {
858 expiration = contacts[0]->expires;
859 } else if (expires)
860 expiration = expires->ivalue;
861 else if (regc->expires_hdr)
862 expiration = regc->expires_hdr->ivalue;
863 else
864 expiration = 3600;
865 }
866
867 } else {
868 /* Just assume that the unregistration has been successful. */
869 expiration = 0;
870 }
871
872 /* Must have expiration value by now */
873 pj_assert(expiration != NOEXP);
874
875 return expiration;
876}
877
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000878static void tsx_callback(void *token, pjsip_event *event)
879{
Benny Prijonoccf95622006-02-07 18:48:01 +0000880 pj_status_t status;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000881 pjsip_regc *regc = (pjsip_regc*) token;
Benny Prijonoccf95622006-02-07 18:48:01 +0000882 pjsip_transaction *tsx = event->body.tsx_state.tsx;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000883
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000884 /* Decrement pending transaction counter. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000885 pj_assert(regc->has_tsx);
886 regc->has_tsx = PJ_FALSE;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000887
Benny Prijonobdc093f2007-10-04 09:48:25 +0000888 /* Add reference to the transport */
889 if (tsx->transport != regc->last_transport) {
890 if (regc->last_transport) {
891 pjsip_transport_dec_ref(regc->last_transport);
892 regc->last_transport = NULL;
893 }
894
895 if (tsx->transport) {
896 regc->last_transport = tsx->transport;
897 pjsip_transport_add_ref(regc->last_transport);
898 }
899 }
900
Benny Prijono58990232007-01-21 16:11:18 +0000901 /* Handle 401/407 challenge (even when _delete_flag is set) */
902 if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
903 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000904 {
Benny Prijonoccf95622006-02-07 18:48:01 +0000905 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000906 pjsip_tx_data *tdata;
907
Benny Prijonodd742da2008-05-17 12:45:00 +0000908 /* reset current op */
909 regc->current_op = REGC_IDLE;
910
Benny Prijonoccf95622006-02-07 18:48:01 +0000911 status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
912 rdata,
913 tsx->last_tx,
914 &tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000915
Benny Prijonoccf95622006-02-07 18:48:01 +0000916 if (status == PJ_SUCCESS) {
Benny Prijono3fe034a2007-06-01 12:51:07 +0000917 ++regc->busy;
Benny Prijono27042582006-08-08 14:04:21 +0000918 status = pjsip_regc_send(regc, tdata);
Benny Prijono3fe034a2007-06-01 12:51:07 +0000919 --regc->busy;
920 }
Benny Prijono27042582006-08-08 14:04:21 +0000921
922 if (status != PJ_SUCCESS) {
Benny Prijono58990232007-01-21 16:11:18 +0000923
924 /* Only call callback if application is still interested
925 * in it.
926 */
927 if (regc->_delete_flag == 0) {
928 /* Increment busy flag temporarily to prevent regc from
929 * being destroyed.
930 */
931 ++regc->busy;
932
933 call_callback(regc, status, tsx->status_code,
934 &rdata->msg_info.msg->line.status.reason,
935 rdata, -1, 0, NULL);
936
937 /* Decrement busy flag */
938 --regc->busy;
939 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000940 }
Benny Prijono27042582006-08-08 14:04:21 +0000941
Benny Prijono58990232007-01-21 16:11:18 +0000942 } else if (regc->_delete_flag) {
943
944 /* User has called pjsip_regc_destroy(), so don't call callback.
945 * This regc will be destroyed later in this function.
946 */
947
Benny Prijonodd742da2008-05-17 12:45:00 +0000948 /* Just reset current op */
949 regc->current_op = REGC_IDLE;
Benny Prijono27042582006-08-08 14:04:21 +0000950
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000951 } else {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000952 pjsip_rx_data *rdata;
Benny Prijonoe1c984f2007-11-04 02:05:13 +0000953 pj_int32_t expiration = NOEXP;
Benny Prijonodd742da2008-05-17 12:45:00 +0000954 unsigned contact_cnt = 0;
955 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000956
957 if (tsx->status_code/100 == 2) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000958
Benny Prijonoccf95622006-02-07 18:48:01 +0000959 rdata = event->body.tsx_state.src.rdata;
Benny Prijonoe1c984f2007-11-04 02:05:13 +0000960
Benny Prijonodd742da2008-05-17 12:45:00 +0000961 /* Calculate expiration */
962 expiration = calculate_response_expiration(regc, rdata,
963 &contact_cnt,
964 PJSIP_REGC_MAX_CONTACT,
965 contact);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000966
Benny Prijonodd742da2008-05-17 12:45:00 +0000967 /* Mark operation as complete */
968 regc->current_op = REGC_IDLE;
Benny Prijonoe1c984f2007-11-04 02:05:13 +0000969
970 /* Schedule next registration */
Benny Prijonodd742da2008-05-17 12:45:00 +0000971 if (regc->auto_reg && expiration > 0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000972 pj_time_val delay = { 0, 0};
973
974 delay.sec = expiration - DELAY_BEFORE_REFRESH;
975 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
976 delay.sec > (pj_int32_t)regc->expires)
977 {
978 delay.sec = regc->expires;
979 }
980 if (delay.sec < DELAY_BEFORE_REFRESH)
981 delay.sec = DELAY_BEFORE_REFRESH;
982 regc->timer.cb = &regc_refresh_timer_cb;
983 regc->timer.id = REFRESH_TIMER;
984 regc->timer.user_data = regc;
985 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000986 pj_gettimeofday(&regc->last_reg);
987 regc->next_reg = regc->last_reg;
988 regc->next_reg.sec += delay.sec;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000989 }
990
991 } else {
Benny Prijonoccf95622006-02-07 18:48:01 +0000992 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
993 event->body.tsx_state.src.rdata : NULL;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000994 }
995
Benny Prijono25a86c72006-12-01 20:50:01 +0000996 /* Increment busy flag temporarily to prevent regc from
Benny Prijono197cabf2006-10-16 20:05:27 +0000997 * being destroyed.
998 */
Benny Prijono25a86c72006-12-01 20:50:01 +0000999 ++regc->busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001000
Benny Prijonodd742da2008-05-17 12:45:00 +00001001 /* Update registration */
1002 if (expiration==NOEXP) expiration=-1;
1003 regc->expires = expiration;
1004
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001005 /* Call callback. */
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001006 call_callback(regc, PJ_SUCCESS, tsx->status_code,
Benny Prijonoccf95622006-02-07 18:48:01 +00001007 (rdata ? &rdata->msg_info.msg->line.status.reason
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001008 : pjsip_get_status_text(tsx->status_code)),
1009 rdata, expiration,
1010 contact_cnt, contact);
1011
Benny Prijono25a86c72006-12-01 20:50:01 +00001012 /* Decrement busy flag */
1013 --regc->busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001014 }
1015
1016 /* Delete the record if user destroy regc during the callback. */
Benny Prijono25a86c72006-12-01 20:50:01 +00001017 if (regc->_delete_flag && regc->busy==0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001018 pjsip_regc_destroy(regc);
1019 }
1020}
1021
Benny Prijonoccf95622006-02-07 18:48:01 +00001022PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001023{
Benny Prijonoccf95622006-02-07 18:48:01 +00001024 pj_status_t status;
Benny Prijono59ca70f2006-02-22 22:18:58 +00001025 pjsip_cseq_hdr *cseq_hdr;
Benny Prijonodd742da2008-05-17 12:45:00 +00001026 pjsip_expires_hdr *expires_hdr;
Benny Prijono59ca70f2006-02-22 22:18:58 +00001027 pj_uint32_t cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001028
1029 /* Make sure we don't have pending transaction. */
Benny Prijono25a86c72006-12-01 20:50:01 +00001030 if (regc->has_tsx) {
Benny Prijono5b656872006-08-08 00:41:00 +00001031 PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
1032 "transaction pending"));
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001033 pjsip_tx_data_dec_ref( tdata );
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001034 return PJSIP_EBUSY;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001035 }
1036
Benny Prijonodd742da2008-05-17 12:45:00 +00001037 pj_assert(regc->current_op == REGC_IDLE);
1038
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001039 /* Invalidate message buffer. */
1040 pjsip_tx_data_invalidate_msg(tdata);
1041
1042 /* Increment CSeq */
Benny Prijono59ca70f2006-02-22 22:18:58 +00001043 cseq = ++regc->cseq_hdr->cseq;
Benny Prijonoa1e69682007-05-11 15:14:34 +00001044 cseq_hdr = (pjsip_cseq_hdr*)
1045 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
Benny Prijono59ca70f2006-02-22 22:18:58 +00001046 cseq_hdr->cseq = cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001047
Benny Prijonodd742da2008-05-17 12:45:00 +00001048 /* Find Expires header */
1049 expires_hdr = (pjsip_expires_hdr*)
1050 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
1051
Benny Prijono37db51c2007-11-23 03:41:54 +00001052 /* Bind to transport selector */
1053 pjsip_tx_data_set_transport(tdata, &regc->tp_sel);
1054
Benny Prijonodc39fe82006-05-26 12:17:46 +00001055 /* Increment pending transaction first, since transaction callback
1056 * may be called even before send_request() returns!
1057 */
Benny Prijono25a86c72006-12-01 20:50:01 +00001058 regc->has_tsx = PJ_TRUE;
1059 ++regc->busy;
Benny Prijonodd742da2008-05-17 12:45:00 +00001060
1061 /* Set current operation based on the value of Expires header */
1062 if (expires_hdr && expires_hdr->ivalue==0)
1063 regc->current_op = REGC_UNREGISTERING;
1064 else
1065 regc->current_op = REGC_REGISTERING;
1066
Benny Prijono0f35f912007-02-05 18:59:31 +00001067 status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
1068 regc, &tsx_callback);
Benny Prijono5b656872006-08-08 00:41:00 +00001069 if (status!=PJ_SUCCESS) {
Benny Prijono5b656872006-08-08 00:41:00 +00001070 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
1071 }
Benny Prijono25a86c72006-12-01 20:50:01 +00001072 --regc->busy;
Benny Prijono197cabf2006-10-16 20:05:27 +00001073
1074 /* Delete the record if user destroy regc during the callback. */
Benny Prijono25a86c72006-12-01 20:50:01 +00001075 if (regc->_delete_flag && regc->busy==0) {
Benny Prijono197cabf2006-10-16 20:05:27 +00001076 pjsip_regc_destroy(regc);
1077 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001078
1079 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001080}
1081
1082