blob: df42c53965bf0595d6f27270a641a4f4f80d8bd0 [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
38#define DELAY_BEFORE_REFRESH 5
39#define THIS_FILE "sip_regc.c"
40
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
46
Benny Prijono5dcb38d2005-11-21 01:55:47 +000047/**
48 * SIP client registration structure.
49 */
50struct pjsip_regc
51{
Benny Prijono84126ab2006-02-09 09:30:09 +000052 pj_pool_t *pool;
53 pjsip_endpoint *endpt;
54 pj_bool_t _delete_flag;
Benny Prijono25a86c72006-12-01 20:50:01 +000055 pj_bool_t has_tsx;
56 pj_int32_t busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000057
Benny Prijono84126ab2006-02-09 09:30:09 +000058 void *token;
59 pjsip_regc_cb *cb;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000060
Benny Prijono84126ab2006-02-09 09:30:09 +000061 pj_str_t str_srv_url;
62 pjsip_uri *srv_url;
63 pjsip_cid_hdr *cid_hdr;
64 pjsip_cseq_hdr *cseq_hdr;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000065 pj_str_t from_uri;
Benny Prijono84126ab2006-02-09 09:30:09 +000066 pjsip_from_hdr *from_hdr;
67 pjsip_to_hdr *to_hdr;
Benny Prijonoe56b5b12007-09-11 09:04:51 +000068 pjsip_hdr contact_hdr_list;
Benny Prijono84126ab2006-02-09 09:30:09 +000069 pjsip_expires_hdr *expires_hdr;
70 pjsip_contact_hdr *unreg_contact_hdr;
71 pjsip_expires_hdr *unreg_expires_hdr;
72 pj_uint32_t expires;
73 pjsip_route_hdr route_set;
Benny Prijono8fc6de02006-11-11 21:25:55 +000074 pjsip_hdr hdr_list;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000075
Benny Prijono5dcb38d2005-11-21 01:55:47 +000076 /* Authorization sessions. */
Benny Prijono84126ab2006-02-09 09:30:09 +000077 pjsip_auth_clt_sess auth_sess;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000078
79 /* Auto refresh registration. */
Benny Prijono84126ab2006-02-09 09:30:09 +000080 pj_bool_t auto_reg;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000081 pj_time_val last_reg;
82 pj_time_val next_reg;
Benny Prijono84126ab2006-02-09 09:30:09 +000083 pj_timer_entry timer;
Benny Prijono720d0a82007-01-12 06:37:35 +000084
85 /* Transport selector */
86 pjsip_tpselector tp_sel;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000087};
88
89
90
Benny Prijonoccf95622006-02-07 18:48:01 +000091PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
92 pjsip_regc_cb *cb,
93 pjsip_regc **p_regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +000094{
95 pj_pool_t *pool;
96 pjsip_regc *regc;
Benny Prijonoccf95622006-02-07 18:48:01 +000097 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000098
Benny Prijonoccf95622006-02-07 18:48:01 +000099 /* Verify arguments. */
100 PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000101
102 pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
Benny Prijonoccf95622006-02-07 18:48:01 +0000103 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
104
Benny Prijonoa1e69682007-05-11 15:14:34 +0000105 regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000106
107 regc->pool = pool;
108 regc->endpt = endpt;
109 regc->token = token;
110 regc->cb = cb;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000111 regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
112
Benny Prijonoccf95622006-02-07 18:48:01 +0000113 status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
114 if (status != PJ_SUCCESS)
115 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000116
Benny Prijono84126ab2006-02-09 09:30:09 +0000117 pj_list_init(&regc->route_set);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000118 pj_list_init(&regc->hdr_list);
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000119 pj_list_init(&regc->contact_hdr_list);
Benny Prijono84126ab2006-02-09 09:30:09 +0000120
Benny Prijonoccf95622006-02-07 18:48:01 +0000121 /* Done */
122 *p_regc = regc;
123 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000124}
125
126
Benny Prijonoccf95622006-02-07 18:48:01 +0000127PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000128{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000129 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
130
Benny Prijono25a86c72006-12-01 20:50:01 +0000131 if (regc->has_tsx || regc->busy) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000132 regc->_delete_flag = 1;
133 regc->cb = NULL;
134 } else {
Benny Prijono720d0a82007-01-12 06:37:35 +0000135 pjsip_tpselector_dec_ref(&regc->tp_sel);
Benny Prijonoccf95622006-02-07 18:48:01 +0000136 pjsip_endpt_release_pool(regc->endpt, regc->pool);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000137 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000138
139 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000140}
141
142
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000143PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
144 pjsip_regc_info *info )
145{
146 PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
147
148 info->server_uri = regc->str_srv_url;
149 info->client_uri = regc->from_uri;
Benny Prijono25a86c72006-12-01 20:50:01 +0000150 info->is_busy = (regc->busy || regc->has_tsx);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000151 info->auto_reg = regc->auto_reg;
152 info->interval = regc->expires;
153
Benny Prijono25a86c72006-12-01 20:50:01 +0000154 if (regc->has_tsx)
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000155 info->next_reg = 0;
156 else if (regc->auto_reg == 0)
157 info->next_reg = 0;
158 else if (regc->expires < 0)
159 info->next_reg = regc->expires;
160 else {
161 pj_time_val now, next_reg;
162
163 next_reg = regc->next_reg;
164 pj_gettimeofday(&now);
165 PJ_TIME_VAL_SUB(next_reg, now);
166 info->next_reg = next_reg.sec;
167 }
168
169 return PJ_SUCCESS;
170}
171
172
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000173PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
174{
175 return regc->pool;
176}
177
178static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
179{
180 if (expires != regc->expires) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000181 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000182 } else {
183 regc->expires_hdr = NULL;
184 }
185}
186
187
188static pj_status_t set_contact( pjsip_regc *regc,
189 int contact_cnt,
190 const pj_str_t contact[] )
191{
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000192 const pj_str_t CONTACT = { "Contact", 7 };
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000193 int i;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000194
195 /* Clear existing contacts */
196 pj_list_init(&regc->contact_hdr_list);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000197
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000198 for (i=0; i<contact_cnt; ++i) {
199 pjsip_hdr *hdr;
200 pj_str_t tmp;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000201
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000202 pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
203 hdr = pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
204 if (hdr == NULL) {
205 PJ_LOG(4,(THIS_FILE, "Invalid Contact URI: \"%.*s\"",
206 (int)tmp.slen, tmp.ptr));
207 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000208 }
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000209
210 pj_list_push_back(&regc->contact_hdr_list, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000211 }
212
Benny Prijonoccf95622006-02-07 18:48:01 +0000213 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000214}
215
216
217PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
218 const pj_str_t *srv_url,
219 const pj_str_t *from_url,
220 const pj_str_t *to_url,
221 int contact_cnt,
222 const pj_str_t contact[],
223 pj_uint32_t expires)
224{
225 pj_str_t tmp;
Benny Prijonoccf95622006-02-07 18:48:01 +0000226 pj_status_t status;
227
228 PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
229 contact_cnt && contact && expires, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000230
231 /* Copy server URL. */
232 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
233
234 /* Set server URL. */
235 tmp = regc->str_srv_url;
236 regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
237 if (regc->srv_url == NULL) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000238 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000239 }
240
241 /* Set "From" header. */
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000242 pj_strdup_with_null(regc->pool, &regc->from_uri, from_url);
243 tmp = regc->from_uri;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000244 regc->from_hdr = pjsip_from_hdr_create(regc->pool);
245 regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
246 PJSIP_PARSE_URI_AS_NAMEADDR);
247 if (!regc->from_hdr->uri) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000248 PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
249 from_url->slen, from_url->ptr));
250 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000251 }
252
253 /* Set "To" header. */
254 pj_strdup_with_null(regc->pool, &tmp, to_url);
255 regc->to_hdr = pjsip_to_hdr_create(regc->pool);
256 regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
257 PJSIP_PARSE_URI_AS_NAMEADDR);
258 if (!regc->to_hdr->uri) {
259 PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
Benny Prijonoccf95622006-02-07 18:48:01 +0000260 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000261 }
262
263
264 /* Set "Contact" header. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000265 status = set_contact( regc, contact_cnt, contact);
266 if (status != PJ_SUCCESS)
267 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000268
269 /* Set "Expires" header, if required. */
270 set_expires( regc, expires);
271
272 /* Set "Call-ID" header. */
273 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
274 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
275
276 /* Set "CSeq" header. */
277 regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
Benny Prijono59ca70f2006-02-22 22:18:58 +0000278 regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000279 pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
280
281 /* Create "Contact" header used in unregistration. */
282 regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
283 regc->unreg_contact_hdr->star = 1;
284
285 /* Create "Expires" header used in unregistration. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000286 regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool, 0);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000287
288 /* Done. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000289 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000290}
291
292PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
293 int count,
294 const pjsip_cred_info cred[] )
295{
Benny Prijonoccf95622006-02-07 18:48:01 +0000296 PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
297 return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000298}
299
Benny Prijono84126ab2006-02-09 09:30:09 +0000300PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
301 const pjsip_route_hdr *route_set)
302{
303 const pjsip_route_hdr *chdr;
304
305 PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
306
307 pj_list_init(&regc->route_set);
308
309 chdr = route_set->next;
310 while (chdr != route_set) {
311 pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
312 chdr = chdr->next;
313 }
314
315 return PJ_SUCCESS;
316}
317
Benny Prijono720d0a82007-01-12 06:37:35 +0000318
319/*
320 * Bind client registration to a specific transport/listener.
321 */
322PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
323 const pjsip_tpselector *sel)
324{
325 PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
326
327 pjsip_tpselector_dec_ref(&regc->tp_sel);
328 pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
329 pjsip_tpselector_add_ref(&regc->tp_sel);
330
331 return PJ_SUCCESS;
332}
333
334
Benny Prijono8fc6de02006-11-11 21:25:55 +0000335PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
336 const pjsip_hdr *hdr_list)
337{
338 const pjsip_hdr *hdr;
339
340 PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
341
342 //This is "add" operation, so don't remove headers.
343 //pj_list_init(&regc->hdr_list);
344
345 hdr = hdr_list->next;
346 while (hdr != hdr_list) {
347 pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
348 hdr = hdr->next;
349 }
350
351 return PJ_SUCCESS;
352}
353
Benny Prijonoccf95622006-02-07 18:48:01 +0000354static pj_status_t create_request(pjsip_regc *regc,
355 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000356{
Benny Prijonoccf95622006-02-07 18:48:01 +0000357 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000358 pjsip_tx_data *tdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000359
Benny Prijonoccf95622006-02-07 18:48:01 +0000360 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000361
Benny Prijonoccf95622006-02-07 18:48:01 +0000362 /* Create the request. */
363 status = pjsip_endpt_create_request_from_hdr( regc->endpt,
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000364 pjsip_get_register_method(),
Benny Prijonoccf95622006-02-07 18:48:01 +0000365 regc->srv_url,
366 regc->from_hdr,
367 regc->to_hdr,
368 NULL,
369 regc->cid_hdr,
370 regc->cseq_hdr->cseq,
371 NULL,
372 &tdata);
373 if (status != PJ_SUCCESS)
374 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000375
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000376 /* Add cached authorization headers. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000377 pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000378
Benny Prijono84126ab2006-02-09 09:30:09 +0000379 /* Add Route headers from route set, ideally after Via header */
380 if (!pj_list_empty(&regc->route_set)) {
381 pjsip_hdr *route_pos;
382 const pjsip_route_hdr *route;
383
Benny Prijonoa1e69682007-05-11 15:14:34 +0000384 route_pos = (pjsip_hdr*)
385 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000386 if (!route_pos)
387 route_pos = &tdata->msg->hdr;
388
389 route = regc->route_set.next;
390 while (route != &regc->route_set) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000391 pjsip_hdr *new_hdr = (pjsip_hdr*)
392 pjsip_hdr_shallow_clone(tdata->pool, route);
Benny Prijono84126ab2006-02-09 09:30:09 +0000393 pj_list_insert_after(route_pos, new_hdr);
394 route_pos = new_hdr;
395 route = route->next;
396 }
397 }
398
Benny Prijono8fc6de02006-11-11 21:25:55 +0000399 /* Add additional request headers */
400 if (!pj_list_empty(&regc->hdr_list)) {
401 const pjsip_hdr *hdr;
402
403 hdr = regc->hdr_list.next;
404 while (hdr != &regc->hdr_list) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000405 pjsip_hdr *new_hdr = (pjsip_hdr*)
406 pjsip_hdr_shallow_clone(tdata->pool, hdr);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000407 pjsip_msg_add_hdr(tdata->msg, new_hdr);
408 hdr = hdr->next;
409 }
410 }
411
Benny Prijonoccf95622006-02-07 18:48:01 +0000412 /* Done. */
413 *p_tdata = tdata;
414 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000415}
416
417
Benny Prijonoccf95622006-02-07 18:48:01 +0000418PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
419 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000420{
421 pjsip_msg *msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000422 pjsip_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000423 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000424 pjsip_tx_data *tdata;
425
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000426 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
427
Benny Prijonoccf95622006-02-07 18:48:01 +0000428 status = create_request(regc, &tdata);
429 if (status != PJ_SUCCESS)
430 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000431
432 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000433
434 /* Add Contact headers. */
435 hdr = regc->contact_hdr_list.next;
436 while (hdr != &regc->contact_hdr_list) {
437 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
438 pjsip_hdr_shallow_clone(tdata->pool, hdr));
439 hdr = hdr->next;
440 }
441
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000442 if (regc->expires_hdr)
Benny Prijonoa1e69682007-05-11 15:14:34 +0000443 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
444 pjsip_hdr_shallow_clone(tdata->pool,
Benny Prijono4093f7c2006-09-13 23:48:45 +0000445 regc->expires_hdr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000446
447 if (regc->timer.id != 0) {
448 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
449 regc->timer.id = 0;
450 }
451
452 regc->auto_reg = autoreg;
453
Benny Prijonoccf95622006-02-07 18:48:01 +0000454 /* Done */
455 *p_tdata = tdata;
456 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000457}
458
459
Benny Prijonoccf95622006-02-07 18:48:01 +0000460PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
461 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000462{
463 pjsip_tx_data *tdata;
464 pjsip_msg *msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000465 pjsip_hdr *hdr;
Benny Prijonoccf95622006-02-07 18:48:01 +0000466 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000467
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000468 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
469
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000470 if (regc->timer.id != 0) {
471 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
472 regc->timer.id = 0;
473 }
474
Benny Prijonoccf95622006-02-07 18:48:01 +0000475 status = create_request(regc, &tdata);
476 if (status != PJ_SUCCESS)
477 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000478
479 msg = tdata->msg;
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000480
481 /* Add Contact headers. */
482 hdr = regc->contact_hdr_list.next;
483 while (hdr != &regc->contact_hdr_list) {
484 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
485 pjsip_hdr_shallow_clone(tdata->pool, hdr));
486 hdr = hdr->next;
487 }
488
Benny Prijonodfc4c482006-12-02 07:25:29 +0000489 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
490
491 *p_tdata = tdata;
492 return PJ_SUCCESS;
493}
494
495PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
496 pjsip_tx_data **p_tdata)
497{
498 pjsip_tx_data *tdata;
499 pjsip_msg *msg;
500 pj_status_t status;
501
502 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
503
504 if (regc->timer.id != 0) {
505 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
506 regc->timer.id = 0;
507 }
508
509 status = create_request(regc, &tdata);
510 if (status != PJ_SUCCESS)
511 return status;
512
513 msg = tdata->msg;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000514 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
515 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
516
Benny Prijonoccf95622006-02-07 18:48:01 +0000517 *p_tdata = tdata;
518 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000519}
520
521
522PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
523 int contact_cnt,
524 const pj_str_t contact[] )
525{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000526 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000527 return set_contact( regc, contact_cnt, contact );
528}
529
530
531PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
532 pj_uint32_t expires )
533{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000534 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000535 set_expires( regc, expires );
Benny Prijonoccf95622006-02-07 18:48:01 +0000536 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000537}
538
539
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000540static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
541 const pj_str_t *reason,
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000542 pjsip_rx_data *rdata, pj_int32_t expiration,
543 int contact_cnt, pjsip_contact_hdr *contact[])
544{
545 struct pjsip_regc_cbparam cbparam;
546
547
Benny Prijonof2651802007-01-26 17:13:56 +0000548 if (!regc->cb)
549 return;
550
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000551 cbparam.regc = regc;
552 cbparam.token = regc->token;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000553 cbparam.status = status;
554 cbparam.code = st_code;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000555 cbparam.reason = *reason;
556 cbparam.rdata = rdata;
557 cbparam.contact_cnt = contact_cnt;
558 cbparam.expiration = expiration;
559 if (contact_cnt) {
560 pj_memcpy( cbparam.contact, contact,
561 contact_cnt*sizeof(pjsip_contact_hdr*));
562 }
563
564 (*regc->cb)(&cbparam);
565}
566
567static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
568 struct pj_timer_entry *entry)
569{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000570 pjsip_regc *regc = (pjsip_regc*) entry->user_data;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000571 pjsip_tx_data *tdata;
Benny Prijonoccf95622006-02-07 18:48:01 +0000572 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000573
Benny Prijonoccf95622006-02-07 18:48:01 +0000574 PJ_UNUSED_ARG(timer_heap);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000575
Benny Prijonof2651802007-01-26 17:13:56 +0000576 /* Temporarily increase busy flag to prevent regc from being deleted
577 * in pjsip_regc_send()
578 */
579 regc->busy++;
580
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000581 entry->id = 0;
Benny Prijonoccf95622006-02-07 18:48:01 +0000582 status = pjsip_regc_register(regc, 1, &tdata);
583 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000584 status = pjsip_regc_send(regc, tdata);
585 }
586
Benny Prijonof2651802007-01-26 17:13:56 +0000587 if (status != PJ_SUCCESS && regc->cb) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000588 char errmsg[PJ_ERR_MSG_SIZE];
589 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000590 call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000591 }
Benny Prijonof2651802007-01-26 17:13:56 +0000592
593 regc->busy--;
594
595 /* Delete the record if user destroy regc during the callback. */
596 if (regc->_delete_flag && regc->busy==0) {
597 pjsip_regc_destroy(regc);
598 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000599}
600
601static void tsx_callback(void *token, pjsip_event *event)
602{
Benny Prijonoccf95622006-02-07 18:48:01 +0000603 pj_status_t status;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000604 pjsip_regc *regc = (pjsip_regc*) token;
Benny Prijonoccf95622006-02-07 18:48:01 +0000605 pjsip_transaction *tsx = event->body.tsx_state.tsx;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000606
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000607 /* Decrement pending transaction counter. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000608 pj_assert(regc->has_tsx);
609 regc->has_tsx = PJ_FALSE;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000610
Benny Prijono58990232007-01-21 16:11:18 +0000611 /* Handle 401/407 challenge (even when _delete_flag is set) */
612 if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
613 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000614 {
Benny Prijonoccf95622006-02-07 18:48:01 +0000615 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000616 pjsip_tx_data *tdata;
617
Benny Prijonoccf95622006-02-07 18:48:01 +0000618 status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
619 rdata,
620 tsx->last_tx,
621 &tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000622
Benny Prijonoccf95622006-02-07 18:48:01 +0000623 if (status == PJ_SUCCESS) {
Benny Prijono3fe034a2007-06-01 12:51:07 +0000624 ++regc->busy;
Benny Prijono27042582006-08-08 14:04:21 +0000625 status = pjsip_regc_send(regc, tdata);
Benny Prijono3fe034a2007-06-01 12:51:07 +0000626 --regc->busy;
627 }
Benny Prijono27042582006-08-08 14:04:21 +0000628
629 if (status != PJ_SUCCESS) {
Benny Prijono58990232007-01-21 16:11:18 +0000630
631 /* Only call callback if application is still interested
632 * in it.
633 */
634 if (regc->_delete_flag == 0) {
635 /* Increment busy flag temporarily to prevent regc from
636 * being destroyed.
637 */
638 ++regc->busy;
639
640 call_callback(regc, status, tsx->status_code,
641 &rdata->msg_info.msg->line.status.reason,
642 rdata, -1, 0, NULL);
643
644 /* Decrement busy flag */
645 --regc->busy;
646 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000647 }
Benny Prijono27042582006-08-08 14:04:21 +0000648
Benny Prijono58990232007-01-21 16:11:18 +0000649 } else if (regc->_delete_flag) {
650
651 /* User has called pjsip_regc_destroy(), so don't call callback.
652 * This regc will be destroyed later in this function.
653 */
654
655 /* Nothing to do */
656 ;
Benny Prijono27042582006-08-08 14:04:21 +0000657
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000658 } else {
659 int contact_cnt = 0;
660 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
661 pjsip_rx_data *rdata;
662 pj_int32_t expiration = 0xFFFF;
663
664 if (tsx->status_code/100 == 2) {
665 int i;
666 pjsip_contact_hdr *hdr;
667 pjsip_msg *msg;
668 pjsip_expires_hdr *expires;
669
Benny Prijonoccf95622006-02-07 18:48:01 +0000670 rdata = event->body.tsx_state.src.rdata;
671 msg = rdata->msg_info.msg;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000672 hdr = (pjsip_contact_hdr*)
673 pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000674 while (hdr) {
675 contact[contact_cnt++] = hdr;
676 hdr = hdr->next;
677 if (hdr == (void*)&msg->hdr)
678 break;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000679 hdr = (pjsip_contact_hdr*)
680 pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000681 }
682
Benny Prijonoa1e69682007-05-11 15:14:34 +0000683 expires = (pjsip_expires_hdr*)
684 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000685
686 if (expires)
687 expiration = expires->ivalue;
688
689 for (i=0; i<contact_cnt; ++i) {
690 hdr = contact[i];
Benny Prijonoe56b5b12007-09-11 09:04:51 +0000691 if (hdr->expires >= 0 && hdr->expires < expiration) {
692 pjsip_contact_hdr *our_contact;
693
694 our_contact = (pjsip_contact_hdr*)
695 regc->contact_hdr_list.next;
696 if ((void*)our_contact==(void*)&regc->contact_hdr_list.next)
697 continue;
698
699 /* Only set expiration time if this is the same Contact
700 * that we register.
701 */
702 if (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
703 hdr->uri,
704 our_contact->uri)==0)
705 {
706 expiration = contact[i]->expires;
707 }
708 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000709 }
710
711 if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
712 pj_time_val delay = { 0, 0};
713
714 delay.sec = expiration - DELAY_BEFORE_REFRESH;
715 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
716 delay.sec > (pj_int32_t)regc->expires)
717 {
718 delay.sec = regc->expires;
719 }
720 if (delay.sec < DELAY_BEFORE_REFRESH)
721 delay.sec = DELAY_BEFORE_REFRESH;
722 regc->timer.cb = &regc_refresh_timer_cb;
723 regc->timer.id = REFRESH_TIMER;
724 regc->timer.user_data = regc;
725 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000726 pj_gettimeofday(&regc->last_reg);
727 regc->next_reg = regc->last_reg;
728 regc->next_reg.sec += delay.sec;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000729 }
730
731 } else {
Benny Prijonoccf95622006-02-07 18:48:01 +0000732 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
733 event->body.tsx_state.src.rdata : NULL;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000734 }
735
Benny Prijono25a86c72006-12-01 20:50:01 +0000736 /* Increment busy flag temporarily to prevent regc from
Benny Prijono197cabf2006-10-16 20:05:27 +0000737 * being destroyed.
738 */
Benny Prijono25a86c72006-12-01 20:50:01 +0000739 ++regc->busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000740
741 /* Call callback. */
742 if (expiration == 0xFFFF) expiration = -1;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000743 call_callback(regc, PJ_SUCCESS, tsx->status_code,
Benny Prijonoccf95622006-02-07 18:48:01 +0000744 (rdata ? &rdata->msg_info.msg->line.status.reason
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000745 : pjsip_get_status_text(tsx->status_code)),
746 rdata, expiration,
747 contact_cnt, contact);
748
Benny Prijono25a86c72006-12-01 20:50:01 +0000749 /* Decrement busy flag */
750 --regc->busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000751 }
752
753 /* Delete the record if user destroy regc during the callback. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000754 if (regc->_delete_flag && regc->busy==0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000755 pjsip_regc_destroy(regc);
756 }
757}
758
Benny Prijonoccf95622006-02-07 18:48:01 +0000759PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000760{
Benny Prijonoccf95622006-02-07 18:48:01 +0000761 pj_status_t status;
Benny Prijono59ca70f2006-02-22 22:18:58 +0000762 pjsip_cseq_hdr *cseq_hdr;
763 pj_uint32_t cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000764
765 /* Make sure we don't have pending transaction. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000766 if (regc->has_tsx) {
Benny Prijono5b656872006-08-08 00:41:00 +0000767 PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
768 "transaction pending"));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000769 pjsip_tx_data_dec_ref( tdata );
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000770 return PJSIP_EBUSY;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000771 }
772
773 /* Invalidate message buffer. */
774 pjsip_tx_data_invalidate_msg(tdata);
775
776 /* Increment CSeq */
Benny Prijono59ca70f2006-02-22 22:18:58 +0000777 cseq = ++regc->cseq_hdr->cseq;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000778 cseq_hdr = (pjsip_cseq_hdr*)
779 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
Benny Prijono59ca70f2006-02-22 22:18:58 +0000780 cseq_hdr->cseq = cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000781
Benny Prijonodc39fe82006-05-26 12:17:46 +0000782 /* Increment pending transaction first, since transaction callback
783 * may be called even before send_request() returns!
784 */
Benny Prijono25a86c72006-12-01 20:50:01 +0000785 regc->has_tsx = PJ_TRUE;
786 ++regc->busy;
Benny Prijono0f35f912007-02-05 18:59:31 +0000787 status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
788 regc, &tsx_callback);
Benny Prijono5b656872006-08-08 00:41:00 +0000789 if (status!=PJ_SUCCESS) {
Benny Prijono5b656872006-08-08 00:41:00 +0000790 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
791 }
Benny Prijono25a86c72006-12-01 20:50:01 +0000792 --regc->busy;
Benny Prijono197cabf2006-10-16 20:05:27 +0000793
794 /* Delete the record if user destroy regc during the callback. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000795 if (regc->_delete_flag && regc->busy==0) {
Benny Prijono197cabf2006-10-16 20:05:27 +0000796 pjsip_regc_destroy(regc);
797 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000798
799 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000800}
801
802