blob: d6078c7314da00ac6b21d8d3bcef4ac9e8fe6f3c [file] [log] [blame]
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
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
41/**
42 * SIP client registration structure.
43 */
44struct pjsip_regc
45{
Benny Prijono84126ab2006-02-09 09:30:09 +000046 pj_pool_t *pool;
47 pjsip_endpoint *endpt;
48 pj_bool_t _delete_flag;
Benny Prijono25a86c72006-12-01 20:50:01 +000049 pj_bool_t has_tsx;
50 pj_int32_t busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000051
Benny Prijono84126ab2006-02-09 09:30:09 +000052 void *token;
53 pjsip_regc_cb *cb;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000054
Benny Prijono84126ab2006-02-09 09:30:09 +000055 pj_str_t str_srv_url;
56 pjsip_uri *srv_url;
57 pjsip_cid_hdr *cid_hdr;
58 pjsip_cseq_hdr *cseq_hdr;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000059 pj_str_t from_uri;
Benny Prijono84126ab2006-02-09 09:30:09 +000060 pjsip_from_hdr *from_hdr;
61 pjsip_to_hdr *to_hdr;
62 char *contact_buf;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000063 pjsip_generic_string_hdr *contact_hdr;
Benny Prijono84126ab2006-02-09 09:30:09 +000064 pjsip_expires_hdr *expires_hdr;
65 pjsip_contact_hdr *unreg_contact_hdr;
66 pjsip_expires_hdr *unreg_expires_hdr;
67 pj_uint32_t expires;
68 pjsip_route_hdr route_set;
Benny Prijono8fc6de02006-11-11 21:25:55 +000069 pjsip_hdr hdr_list;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000070
Benny Prijono5dcb38d2005-11-21 01:55:47 +000071 /* Authorization sessions. */
Benny Prijono84126ab2006-02-09 09:30:09 +000072 pjsip_auth_clt_sess auth_sess;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000073
74 /* Auto refresh registration. */
Benny Prijono84126ab2006-02-09 09:30:09 +000075 pj_bool_t auto_reg;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000076 pj_time_val last_reg;
77 pj_time_val next_reg;
Benny Prijono84126ab2006-02-09 09:30:09 +000078 pj_timer_entry timer;
Benny Prijono720d0a82007-01-12 06:37:35 +000079
80 /* Transport selector */
81 pjsip_tpselector tp_sel;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000082};
83
84
85
Benny Prijonoccf95622006-02-07 18:48:01 +000086PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
87 pjsip_regc_cb *cb,
88 pjsip_regc **p_regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +000089{
90 pj_pool_t *pool;
91 pjsip_regc *regc;
Benny Prijonoccf95622006-02-07 18:48:01 +000092 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000093
Benny Prijonoccf95622006-02-07 18:48:01 +000094 /* Verify arguments. */
95 PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +000096
97 pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
Benny Prijonoccf95622006-02-07 18:48:01 +000098 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
99
100 regc = pj_pool_zalloc(pool, sizeof(struct pjsip_regc));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000101
102 regc->pool = pool;
103 regc->endpt = endpt;
104 regc->token = token;
105 regc->cb = cb;
106 regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
107 regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
108
Benny Prijonoccf95622006-02-07 18:48:01 +0000109 status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
110 if (status != PJ_SUCCESS)
111 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000112
Benny Prijono84126ab2006-02-09 09:30:09 +0000113 pj_list_init(&regc->route_set);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000114 pj_list_init(&regc->hdr_list);
Benny Prijono84126ab2006-02-09 09:30:09 +0000115
Benny Prijonoccf95622006-02-07 18:48:01 +0000116 /* Done */
117 *p_regc = regc;
118 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000119}
120
121
Benny Prijonoccf95622006-02-07 18:48:01 +0000122PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000123{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000124 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
125
Benny Prijono25a86c72006-12-01 20:50:01 +0000126 if (regc->has_tsx || regc->busy) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000127 regc->_delete_flag = 1;
128 regc->cb = NULL;
129 } else {
Benny Prijono720d0a82007-01-12 06:37:35 +0000130 pjsip_tpselector_dec_ref(&regc->tp_sel);
Benny Prijonoccf95622006-02-07 18:48:01 +0000131 pjsip_endpt_release_pool(regc->endpt, regc->pool);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000132 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000133
134 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000135}
136
137
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000138PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
139 pjsip_regc_info *info )
140{
141 PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
142
143 info->server_uri = regc->str_srv_url;
144 info->client_uri = regc->from_uri;
Benny Prijono25a86c72006-12-01 20:50:01 +0000145 info->is_busy = (regc->busy || regc->has_tsx);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000146 info->auto_reg = regc->auto_reg;
147 info->interval = regc->expires;
148
Benny Prijono25a86c72006-12-01 20:50:01 +0000149 if (regc->has_tsx)
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000150 info->next_reg = 0;
151 else if (regc->auto_reg == 0)
152 info->next_reg = 0;
153 else if (regc->expires < 0)
154 info->next_reg = regc->expires;
155 else {
156 pj_time_val now, next_reg;
157
158 next_reg = regc->next_reg;
159 pj_gettimeofday(&now);
160 PJ_TIME_VAL_SUB(next_reg, now);
161 info->next_reg = next_reg.sec;
162 }
163
164 return PJ_SUCCESS;
165}
166
167
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000168PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
169{
170 return regc->pool;
171}
172
173static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
174{
175 if (expires != regc->expires) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000176 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000177 } else {
178 regc->expires_hdr = NULL;
179 }
180}
181
182
183static pj_status_t set_contact( pjsip_regc *regc,
184 int contact_cnt,
185 const pj_str_t contact[] )
186{
187 int i;
188 char *s;
189 const pj_str_t contact_STR = { "Contact", 7};
190
191 /* Concatenate contacts. */
192 for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
193 if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000194 return PJSIP_EURITOOLONG;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000195 }
196 pj_memcpy(s, contact[i].ptr, contact[i].slen);
197 s += contact[i].slen;
198
199 if (i != contact_cnt - 1) {
200 *s++ = ',';
201 *s++ = ' ';
202 }
203 }
204
205 /* Set "Contact" header. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000206 regc->contact_hdr = pjsip_generic_string_hdr_create(regc->pool,
207 &contact_STR,
208 NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000209 regc->contact_hdr->hvalue.ptr = regc->contact_buf;
210 regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
211
Benny Prijonoccf95622006-02-07 18:48:01 +0000212 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000213}
214
215
216PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
217 const pj_str_t *srv_url,
218 const pj_str_t *from_url,
219 const pj_str_t *to_url,
220 int contact_cnt,
221 const pj_str_t contact[],
222 pj_uint32_t expires)
223{
224 pj_str_t tmp;
Benny Prijonoccf95622006-02-07 18:48:01 +0000225 pj_status_t status;
226
227 PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
228 contact_cnt && contact && expires, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000229
230 /* Copy server URL. */
231 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
232
233 /* Set server URL. */
234 tmp = regc->str_srv_url;
235 regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
236 if (regc->srv_url == NULL) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000237 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000238 }
239
240 /* Set "From" header. */
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000241 pj_strdup_with_null(regc->pool, &regc->from_uri, from_url);
242 tmp = regc->from_uri;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000243 regc->from_hdr = pjsip_from_hdr_create(regc->pool);
244 regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
245 PJSIP_PARSE_URI_AS_NAMEADDR);
246 if (!regc->from_hdr->uri) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000247 PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
248 from_url->slen, from_url->ptr));
249 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000250 }
251
252 /* Set "To" header. */
253 pj_strdup_with_null(regc->pool, &tmp, to_url);
254 regc->to_hdr = pjsip_to_hdr_create(regc->pool);
255 regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
256 PJSIP_PARSE_URI_AS_NAMEADDR);
257 if (!regc->to_hdr->uri) {
258 PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
Benny Prijonoccf95622006-02-07 18:48:01 +0000259 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000260 }
261
262
263 /* Set "Contact" header. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000264 status = set_contact( regc, contact_cnt, contact);
265 if (status != PJ_SUCCESS)
266 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000267
268 /* Set "Expires" header, if required. */
269 set_expires( regc, expires);
270
271 /* Set "Call-ID" header. */
272 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
273 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
274
275 /* Set "CSeq" header. */
276 regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
Benny Prijono59ca70f2006-02-22 22:18:58 +0000277 regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000278 pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
279
280 /* Create "Contact" header used in unregistration. */
281 regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
282 regc->unreg_contact_hdr->star = 1;
283
284 /* Create "Expires" header used in unregistration. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000285 regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool, 0);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000286
287 /* Done. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000288 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000289}
290
291PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
292 int count,
293 const pjsip_cred_info cred[] )
294{
Benny Prijonoccf95622006-02-07 18:48:01 +0000295 PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
296 return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000297}
298
Benny Prijono84126ab2006-02-09 09:30:09 +0000299PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
300 const pjsip_route_hdr *route_set)
301{
302 const pjsip_route_hdr *chdr;
303
304 PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
305
306 pj_list_init(&regc->route_set);
307
308 chdr = route_set->next;
309 while (chdr != route_set) {
310 pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
311 chdr = chdr->next;
312 }
313
314 return PJ_SUCCESS;
315}
316
Benny Prijono720d0a82007-01-12 06:37:35 +0000317
318/*
319 * Bind client registration to a specific transport/listener.
320 */
321PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
322 const pjsip_tpselector *sel)
323{
324 PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
325
326 pjsip_tpselector_dec_ref(&regc->tp_sel);
327 pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
328 pjsip_tpselector_add_ref(&regc->tp_sel);
329
330 return PJ_SUCCESS;
331}
332
333
Benny Prijono8fc6de02006-11-11 21:25:55 +0000334PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
335 const pjsip_hdr *hdr_list)
336{
337 const pjsip_hdr *hdr;
338
339 PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
340
341 //This is "add" operation, so don't remove headers.
342 //pj_list_init(&regc->hdr_list);
343
344 hdr = hdr_list->next;
345 while (hdr != hdr_list) {
346 pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
347 hdr = hdr->next;
348 }
349
350 return PJ_SUCCESS;
351}
352
Benny Prijonoccf95622006-02-07 18:48:01 +0000353static pj_status_t create_request(pjsip_regc *regc,
354 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000355{
Benny Prijonoccf95622006-02-07 18:48:01 +0000356 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000357 pjsip_tx_data *tdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000358
Benny Prijonoccf95622006-02-07 18:48:01 +0000359 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000360
Benny Prijonoccf95622006-02-07 18:48:01 +0000361 /* Create the request. */
362 status = pjsip_endpt_create_request_from_hdr( regc->endpt,
363 &pjsip_register_method,
364 regc->srv_url,
365 regc->from_hdr,
366 regc->to_hdr,
367 NULL,
368 regc->cid_hdr,
369 regc->cseq_hdr->cseq,
370 NULL,
371 &tdata);
372 if (status != PJ_SUCCESS)
373 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000374
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000375 /* Add cached authorization headers. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000376 pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000377
Benny Prijono84126ab2006-02-09 09:30:09 +0000378 /* Add Route headers from route set, ideally after Via header */
379 if (!pj_list_empty(&regc->route_set)) {
380 pjsip_hdr *route_pos;
381 const pjsip_route_hdr *route;
382
383 route_pos = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
384 if (!route_pos)
385 route_pos = &tdata->msg->hdr;
386
387 route = regc->route_set.next;
388 while (route != &regc->route_set) {
389 pjsip_hdr *new_hdr = pjsip_hdr_shallow_clone(tdata->pool, route);
390 pj_list_insert_after(route_pos, new_hdr);
391 route_pos = new_hdr;
392 route = route->next;
393 }
394 }
395
Benny Prijono8fc6de02006-11-11 21:25:55 +0000396 /* Add additional request headers */
397 if (!pj_list_empty(&regc->hdr_list)) {
398 const pjsip_hdr *hdr;
399
400 hdr = regc->hdr_list.next;
401 while (hdr != &regc->hdr_list) {
402 pjsip_hdr *new_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
403 pjsip_msg_add_hdr(tdata->msg, new_hdr);
404 hdr = hdr->next;
405 }
406 }
407
Benny Prijonoccf95622006-02-07 18:48:01 +0000408 /* Done. */
409 *p_tdata = tdata;
410 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000411}
412
413
Benny Prijonoccf95622006-02-07 18:48:01 +0000414PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
415 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000416{
417 pjsip_msg *msg;
Benny Prijonoccf95622006-02-07 18:48:01 +0000418 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000419 pjsip_tx_data *tdata;
420
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000421 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
422
Benny Prijonoccf95622006-02-07 18:48:01 +0000423 status = create_request(regc, &tdata);
424 if (status != PJ_SUCCESS)
425 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000426
Benny Prijonoccf95622006-02-07 18:48:01 +0000427 /* Add Contact header. */
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000428 msg = tdata->msg;
Benny Prijono4093f7c2006-09-13 23:48:45 +0000429 pjsip_msg_add_hdr(msg, pjsip_hdr_shallow_clone(tdata->pool,
430 regc->contact_hdr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000431 if (regc->expires_hdr)
Benny Prijono4093f7c2006-09-13 23:48:45 +0000432 pjsip_msg_add_hdr(msg, pjsip_hdr_shallow_clone(tdata->pool,
433 regc->expires_hdr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000434
435 if (regc->timer.id != 0) {
436 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
437 regc->timer.id = 0;
438 }
439
440 regc->auto_reg = autoreg;
441
Benny Prijonoccf95622006-02-07 18:48:01 +0000442 /* Done */
443 *p_tdata = tdata;
444 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000445}
446
447
Benny Prijonoccf95622006-02-07 18:48:01 +0000448PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
449 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000450{
451 pjsip_tx_data *tdata;
452 pjsip_msg *msg;
Benny Prijonoccf95622006-02-07 18:48:01 +0000453 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000454
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000455 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
456
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000457 if (regc->timer.id != 0) {
458 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
459 regc->timer.id = 0;
460 }
461
Benny Prijonoccf95622006-02-07 18:48:01 +0000462 status = create_request(regc, &tdata);
463 if (status != PJ_SUCCESS)
464 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000465
466 msg = tdata->msg;
Benny Prijonodfc4c482006-12-02 07:25:29 +0000467 pjsip_msg_add_hdr(msg, pjsip_hdr_shallow_clone(tdata->pool,
468 regc->contact_hdr));
469 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
470
471 *p_tdata = tdata;
472 return PJ_SUCCESS;
473}
474
475PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
476 pjsip_tx_data **p_tdata)
477{
478 pjsip_tx_data *tdata;
479 pjsip_msg *msg;
480 pj_status_t status;
481
482 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
483
484 if (regc->timer.id != 0) {
485 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
486 regc->timer.id = 0;
487 }
488
489 status = create_request(regc, &tdata);
490 if (status != PJ_SUCCESS)
491 return status;
492
493 msg = tdata->msg;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000494 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
495 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
496
Benny Prijonoccf95622006-02-07 18:48:01 +0000497 *p_tdata = tdata;
498 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000499}
500
501
502PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
503 int contact_cnt,
504 const pj_str_t contact[] )
505{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000506 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000507 return set_contact( regc, contact_cnt, contact );
508}
509
510
511PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
512 pj_uint32_t expires )
513{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000514 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000515 set_expires( regc, expires );
Benny Prijonoccf95622006-02-07 18:48:01 +0000516 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000517}
518
519
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000520static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
521 const pj_str_t *reason,
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000522 pjsip_rx_data *rdata, pj_int32_t expiration,
523 int contact_cnt, pjsip_contact_hdr *contact[])
524{
525 struct pjsip_regc_cbparam cbparam;
526
527
528 cbparam.regc = regc;
529 cbparam.token = regc->token;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000530 cbparam.status = status;
531 cbparam.code = st_code;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000532 cbparam.reason = *reason;
533 cbparam.rdata = rdata;
534 cbparam.contact_cnt = contact_cnt;
535 cbparam.expiration = expiration;
536 if (contact_cnt) {
537 pj_memcpy( cbparam.contact, contact,
538 contact_cnt*sizeof(pjsip_contact_hdr*));
539 }
540
541 (*regc->cb)(&cbparam);
542}
543
544static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
545 struct pj_timer_entry *entry)
546{
547 pjsip_regc *regc = entry->user_data;
548 pjsip_tx_data *tdata;
Benny Prijonoccf95622006-02-07 18:48:01 +0000549 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000550
Benny Prijonoccf95622006-02-07 18:48:01 +0000551 PJ_UNUSED_ARG(timer_heap);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000552
553 entry->id = 0;
Benny Prijonoccf95622006-02-07 18:48:01 +0000554 status = pjsip_regc_register(regc, 1, &tdata);
555 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000556 status = pjsip_regc_send(regc, tdata);
557 }
558
559 if (status != PJ_SUCCESS) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000560 char errmsg[PJ_ERR_MSG_SIZE];
561 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000562 call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000563 }
564}
565
566static void tsx_callback(void *token, pjsip_event *event)
567{
Benny Prijonoccf95622006-02-07 18:48:01 +0000568 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000569 pjsip_regc *regc = token;
Benny Prijonoccf95622006-02-07 18:48:01 +0000570 pjsip_transaction *tsx = event->body.tsx_state.tsx;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000571
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000572 /* Decrement pending transaction counter. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000573 pj_assert(regc->has_tsx);
574 regc->has_tsx = PJ_FALSE;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000575
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000576 /* If registration data has been deleted by user then remove registration
577 * data from transaction's callback, and don't call callback.
578 */
579 if (regc->_delete_flag) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000580
581 /* Nothing to do */
582 ;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000583
584 } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
585 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
586 {
Benny Prijonoccf95622006-02-07 18:48:01 +0000587 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000588 pjsip_tx_data *tdata;
589
Benny Prijonoccf95622006-02-07 18:48:01 +0000590 status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
591 rdata,
592 tsx->last_tx,
593 &tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000594
Benny Prijonoccf95622006-02-07 18:48:01 +0000595 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000596 status = pjsip_regc_send(regc, tdata);
597 }
598
599 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000600 call_callback(regc, status, tsx->status_code,
601 &rdata->msg_info.msg->line.status.reason,
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000602 rdata, -1, 0, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000603 }
Benny Prijono27042582006-08-08 14:04:21 +0000604
605 return;
606
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000607 } else {
608 int contact_cnt = 0;
609 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
610 pjsip_rx_data *rdata;
611 pj_int32_t expiration = 0xFFFF;
612
613 if (tsx->status_code/100 == 2) {
614 int i;
615 pjsip_contact_hdr *hdr;
616 pjsip_msg *msg;
617 pjsip_expires_hdr *expires;
618
Benny Prijonoccf95622006-02-07 18:48:01 +0000619 rdata = event->body.tsx_state.src.rdata;
620 msg = rdata->msg_info.msg;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000621 hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
622 while (hdr) {
623 contact[contact_cnt++] = hdr;
624 hdr = hdr->next;
625 if (hdr == (void*)&msg->hdr)
626 break;
627 hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
628 }
629
630 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
631
632 if (expires)
633 expiration = expires->ivalue;
634
635 for (i=0; i<contact_cnt; ++i) {
636 hdr = contact[i];
637 if (hdr->expires >= 0 && hdr->expires < expiration)
638 expiration = contact[i]->expires;
639 }
640
641 if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
642 pj_time_val delay = { 0, 0};
643
644 delay.sec = expiration - DELAY_BEFORE_REFRESH;
645 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
646 delay.sec > (pj_int32_t)regc->expires)
647 {
648 delay.sec = regc->expires;
649 }
650 if (delay.sec < DELAY_BEFORE_REFRESH)
651 delay.sec = DELAY_BEFORE_REFRESH;
652 regc->timer.cb = &regc_refresh_timer_cb;
653 regc->timer.id = REFRESH_TIMER;
654 regc->timer.user_data = regc;
655 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000656 pj_gettimeofday(&regc->last_reg);
657 regc->next_reg = regc->last_reg;
658 regc->next_reg.sec += delay.sec;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000659 }
660
661 } else {
Benny Prijonoccf95622006-02-07 18:48:01 +0000662 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
663 event->body.tsx_state.src.rdata : NULL;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000664 }
665
Benny Prijono25a86c72006-12-01 20:50:01 +0000666 /* Increment busy flag temporarily to prevent regc from
Benny Prijono197cabf2006-10-16 20:05:27 +0000667 * being destroyed.
668 */
Benny Prijono25a86c72006-12-01 20:50:01 +0000669 ++regc->busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000670
671 /* Call callback. */
672 if (expiration == 0xFFFF) expiration = -1;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000673 call_callback(regc, PJ_SUCCESS, tsx->status_code,
Benny Prijonoccf95622006-02-07 18:48:01 +0000674 (rdata ? &rdata->msg_info.msg->line.status.reason
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000675 : pjsip_get_status_text(tsx->status_code)),
676 rdata, expiration,
677 contact_cnt, contact);
678
Benny Prijono25a86c72006-12-01 20:50:01 +0000679 /* Decrement busy flag */
680 --regc->busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000681 }
682
683 /* Delete the record if user destroy regc during the callback. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000684 if (regc->_delete_flag && regc->busy==0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000685 pjsip_regc_destroy(regc);
686 }
687}
688
Benny Prijonoccf95622006-02-07 18:48:01 +0000689PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000690{
Benny Prijonoccf95622006-02-07 18:48:01 +0000691 pj_status_t status;
Benny Prijono59ca70f2006-02-22 22:18:58 +0000692 pjsip_cseq_hdr *cseq_hdr;
693 pj_uint32_t cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000694
695 /* Make sure we don't have pending transaction. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000696 if (regc->has_tsx) {
Benny Prijono5b656872006-08-08 00:41:00 +0000697 PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
698 "transaction pending"));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000699 pjsip_tx_data_dec_ref( tdata );
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000700 return PJSIP_EBUSY;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000701 }
702
703 /* Invalidate message buffer. */
704 pjsip_tx_data_invalidate_msg(tdata);
705
706 /* Increment CSeq */
Benny Prijono59ca70f2006-02-22 22:18:58 +0000707 cseq = ++regc->cseq_hdr->cseq;
708 cseq_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
709 cseq_hdr->cseq = cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000710
Benny Prijonodc39fe82006-05-26 12:17:46 +0000711 /* Increment pending transaction first, since transaction callback
712 * may be called even before send_request() returns!
713 */
Benny Prijono25a86c72006-12-01 20:50:01 +0000714 regc->has_tsx = PJ_TRUE;
715 ++regc->busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000716 status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
Benny Prijono5b656872006-08-08 00:41:00 +0000717 if (status!=PJ_SUCCESS) {
Benny Prijono5b656872006-08-08 00:41:00 +0000718 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
719 }
Benny Prijono25a86c72006-12-01 20:50:01 +0000720 --regc->busy;
Benny Prijono197cabf2006-10-16 20:05:27 +0000721
722 /* Delete the record if user destroy regc during the callback. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000723 if (regc->_delete_flag && regc->busy==0) {
Benny Prijono197cabf2006-10-16 20:05:27 +0000724 pjsip_regc_destroy(regc);
725 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000726
727 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000728}
729
730