blob: 6a5c3c2ff6236f88fd6c635ea1ffdef6ecfcdeb4 [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;
68 char *contact_buf;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000069 pjsip_generic_string_hdr *contact_hdr;
Benny Prijono84126ab2006-02-09 09:30:09 +000070 pjsip_expires_hdr *expires_hdr;
71 pjsip_contact_hdr *unreg_contact_hdr;
72 pjsip_expires_hdr *unreg_expires_hdr;
73 pj_uint32_t expires;
74 pjsip_route_hdr route_set;
Benny Prijono8fc6de02006-11-11 21:25:55 +000075 pjsip_hdr hdr_list;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000076
Benny Prijono5dcb38d2005-11-21 01:55:47 +000077 /* Authorization sessions. */
Benny Prijono84126ab2006-02-09 09:30:09 +000078 pjsip_auth_clt_sess auth_sess;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000079
80 /* Auto refresh registration. */
Benny Prijono84126ab2006-02-09 09:30:09 +000081 pj_bool_t auto_reg;
Benny Prijonobcaed6c2006-02-19 15:37:19 +000082 pj_time_val last_reg;
83 pj_time_val next_reg;
Benny Prijono84126ab2006-02-09 09:30:09 +000084 pj_timer_entry timer;
Benny Prijono720d0a82007-01-12 06:37:35 +000085
86 /* Transport selector */
87 pjsip_tpselector tp_sel;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000088};
89
90
91
Benny Prijonoccf95622006-02-07 18:48:01 +000092PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
93 pjsip_regc_cb *cb,
94 pjsip_regc **p_regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +000095{
96 pj_pool_t *pool;
97 pjsip_regc *regc;
Benny Prijonoccf95622006-02-07 18:48:01 +000098 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +000099
Benny Prijonoccf95622006-02-07 18:48:01 +0000100 /* Verify arguments. */
101 PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000102
103 pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
Benny Prijonoccf95622006-02-07 18:48:01 +0000104 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
105
Benny Prijonoa1e69682007-05-11 15:14:34 +0000106 regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000107
108 regc->pool = pool;
109 regc->endpt = endpt;
110 regc->token = token;
111 regc->cb = cb;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000112 regc->contact_buf = (char*)pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000113 regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
114
Benny Prijonoccf95622006-02-07 18:48:01 +0000115 status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
116 if (status != PJ_SUCCESS)
117 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000118
Benny Prijono84126ab2006-02-09 09:30:09 +0000119 pj_list_init(&regc->route_set);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000120 pj_list_init(&regc->hdr_list);
Benny Prijono84126ab2006-02-09 09:30:09 +0000121
Benny Prijonoccf95622006-02-07 18:48:01 +0000122 /* Done */
123 *p_regc = regc;
124 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000125}
126
127
Benny Prijonoccf95622006-02-07 18:48:01 +0000128PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000129{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000130 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
131
Benny Prijono25a86c72006-12-01 20:50:01 +0000132 if (regc->has_tsx || regc->busy) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000133 regc->_delete_flag = 1;
134 regc->cb = NULL;
135 } else {
Benny Prijono720d0a82007-01-12 06:37:35 +0000136 pjsip_tpselector_dec_ref(&regc->tp_sel);
Benny Prijonoccf95622006-02-07 18:48:01 +0000137 pjsip_endpt_release_pool(regc->endpt, regc->pool);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000138 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000139
140 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000141}
142
143
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000144PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
145 pjsip_regc_info *info )
146{
147 PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
148
149 info->server_uri = regc->str_srv_url;
150 info->client_uri = regc->from_uri;
Benny Prijono25a86c72006-12-01 20:50:01 +0000151 info->is_busy = (regc->busy || regc->has_tsx);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000152 info->auto_reg = regc->auto_reg;
153 info->interval = regc->expires;
154
Benny Prijono25a86c72006-12-01 20:50:01 +0000155 if (regc->has_tsx)
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000156 info->next_reg = 0;
157 else if (regc->auto_reg == 0)
158 info->next_reg = 0;
159 else if (regc->expires < 0)
160 info->next_reg = regc->expires;
161 else {
162 pj_time_val now, next_reg;
163
164 next_reg = regc->next_reg;
165 pj_gettimeofday(&now);
166 PJ_TIME_VAL_SUB(next_reg, now);
167 info->next_reg = next_reg.sec;
168 }
169
170 return PJ_SUCCESS;
171}
172
173
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000174PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
175{
176 return regc->pool;
177}
178
179static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
180{
181 if (expires != regc->expires) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000182 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000183 } else {
184 regc->expires_hdr = NULL;
185 }
186}
187
188
189static pj_status_t set_contact( pjsip_regc *regc,
190 int contact_cnt,
191 const pj_str_t contact[] )
192{
193 int i;
194 char *s;
195 const pj_str_t contact_STR = { "Contact", 7};
196
197 /* Concatenate contacts. */
198 for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
199 if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000200 return PJSIP_EURITOOLONG;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000201 }
202 pj_memcpy(s, contact[i].ptr, contact[i].slen);
203 s += contact[i].slen;
204
205 if (i != contact_cnt - 1) {
206 *s++ = ',';
207 *s++ = ' ';
208 }
209 }
210
211 /* Set "Contact" header. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000212 regc->contact_hdr = pjsip_generic_string_hdr_create(regc->pool,
213 &contact_STR,
214 NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000215 regc->contact_hdr->hvalue.ptr = regc->contact_buf;
216 regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
217
Benny Prijonoccf95622006-02-07 18:48:01 +0000218 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000219}
220
221
222PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
223 const pj_str_t *srv_url,
224 const pj_str_t *from_url,
225 const pj_str_t *to_url,
226 int contact_cnt,
227 const pj_str_t contact[],
228 pj_uint32_t expires)
229{
230 pj_str_t tmp;
Benny Prijonoccf95622006-02-07 18:48:01 +0000231 pj_status_t status;
232
233 PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
234 contact_cnt && contact && expires, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000235
236 /* Copy server URL. */
237 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
238
239 /* Set server URL. */
240 tmp = regc->str_srv_url;
241 regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
242 if (regc->srv_url == NULL) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000243 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000244 }
245
246 /* Set "From" header. */
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000247 pj_strdup_with_null(regc->pool, &regc->from_uri, from_url);
248 tmp = regc->from_uri;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000249 regc->from_hdr = pjsip_from_hdr_create(regc->pool);
250 regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
251 PJSIP_PARSE_URI_AS_NAMEADDR);
252 if (!regc->from_hdr->uri) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000253 PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
254 from_url->slen, from_url->ptr));
255 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000256 }
257
258 /* Set "To" header. */
259 pj_strdup_with_null(regc->pool, &tmp, to_url);
260 regc->to_hdr = pjsip_to_hdr_create(regc->pool);
261 regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
262 PJSIP_PARSE_URI_AS_NAMEADDR);
263 if (!regc->to_hdr->uri) {
264 PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
Benny Prijonoccf95622006-02-07 18:48:01 +0000265 return PJSIP_EINVALIDURI;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000266 }
267
268
269 /* Set "Contact" header. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000270 status = set_contact( regc, contact_cnt, contact);
271 if (status != PJ_SUCCESS)
272 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000273
274 /* Set "Expires" header, if required. */
275 set_expires( regc, expires);
276
277 /* Set "Call-ID" header. */
278 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
279 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
280
281 /* Set "CSeq" header. */
282 regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
Benny Prijono59ca70f2006-02-22 22:18:58 +0000283 regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000284 pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
285
286 /* Create "Contact" header used in unregistration. */
287 regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
288 regc->unreg_contact_hdr->star = 1;
289
290 /* Create "Expires" header used in unregistration. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000291 regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool, 0);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000292
293 /* Done. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000294 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000295}
296
297PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
298 int count,
299 const pjsip_cred_info cred[] )
300{
Benny Prijonoccf95622006-02-07 18:48:01 +0000301 PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
302 return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000303}
304
Benny Prijono84126ab2006-02-09 09:30:09 +0000305PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
306 const pjsip_route_hdr *route_set)
307{
308 const pjsip_route_hdr *chdr;
309
310 PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
311
312 pj_list_init(&regc->route_set);
313
314 chdr = route_set->next;
315 while (chdr != route_set) {
316 pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
317 chdr = chdr->next;
318 }
319
320 return PJ_SUCCESS;
321}
322
Benny Prijono720d0a82007-01-12 06:37:35 +0000323
324/*
325 * Bind client registration to a specific transport/listener.
326 */
327PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
328 const pjsip_tpselector *sel)
329{
330 PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
331
332 pjsip_tpselector_dec_ref(&regc->tp_sel);
333 pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
334 pjsip_tpselector_add_ref(&regc->tp_sel);
335
336 return PJ_SUCCESS;
337}
338
339
Benny Prijono8fc6de02006-11-11 21:25:55 +0000340PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
341 const pjsip_hdr *hdr_list)
342{
343 const pjsip_hdr *hdr;
344
345 PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
346
347 //This is "add" operation, so don't remove headers.
348 //pj_list_init(&regc->hdr_list);
349
350 hdr = hdr_list->next;
351 while (hdr != hdr_list) {
352 pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
353 hdr = hdr->next;
354 }
355
356 return PJ_SUCCESS;
357}
358
Benny Prijonoccf95622006-02-07 18:48:01 +0000359static pj_status_t create_request(pjsip_regc *regc,
360 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000361{
Benny Prijonoccf95622006-02-07 18:48:01 +0000362 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000363 pjsip_tx_data *tdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000364
Benny Prijonoccf95622006-02-07 18:48:01 +0000365 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000366
Benny Prijonoccf95622006-02-07 18:48:01 +0000367 /* Create the request. */
368 status = pjsip_endpt_create_request_from_hdr( regc->endpt,
369 &pjsip_register_method,
370 regc->srv_url,
371 regc->from_hdr,
372 regc->to_hdr,
373 NULL,
374 regc->cid_hdr,
375 regc->cseq_hdr->cseq,
376 NULL,
377 &tdata);
378 if (status != PJ_SUCCESS)
379 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000380
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000381 /* Add cached authorization headers. */
Benny Prijonoccf95622006-02-07 18:48:01 +0000382 pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000383
Benny Prijono84126ab2006-02-09 09:30:09 +0000384 /* Add Route headers from route set, ideally after Via header */
385 if (!pj_list_empty(&regc->route_set)) {
386 pjsip_hdr *route_pos;
387 const pjsip_route_hdr *route;
388
Benny Prijonoa1e69682007-05-11 15:14:34 +0000389 route_pos = (pjsip_hdr*)
390 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000391 if (!route_pos)
392 route_pos = &tdata->msg->hdr;
393
394 route = regc->route_set.next;
395 while (route != &regc->route_set) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000396 pjsip_hdr *new_hdr = (pjsip_hdr*)
397 pjsip_hdr_shallow_clone(tdata->pool, route);
Benny Prijono84126ab2006-02-09 09:30:09 +0000398 pj_list_insert_after(route_pos, new_hdr);
399 route_pos = new_hdr;
400 route = route->next;
401 }
402 }
403
Benny Prijono8fc6de02006-11-11 21:25:55 +0000404 /* Add additional request headers */
405 if (!pj_list_empty(&regc->hdr_list)) {
406 const pjsip_hdr *hdr;
407
408 hdr = regc->hdr_list.next;
409 while (hdr != &regc->hdr_list) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000410 pjsip_hdr *new_hdr = (pjsip_hdr*)
411 pjsip_hdr_shallow_clone(tdata->pool, hdr);
Benny Prijono8fc6de02006-11-11 21:25:55 +0000412 pjsip_msg_add_hdr(tdata->msg, new_hdr);
413 hdr = hdr->next;
414 }
415 }
416
Benny Prijonoccf95622006-02-07 18:48:01 +0000417 /* Done. */
418 *p_tdata = tdata;
419 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000420}
421
422
Benny Prijonoccf95622006-02-07 18:48:01 +0000423PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
424 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000425{
426 pjsip_msg *msg;
Benny Prijonoccf95622006-02-07 18:48:01 +0000427 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000428 pjsip_tx_data *tdata;
429
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000430 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
431
Benny Prijonoccf95622006-02-07 18:48:01 +0000432 status = create_request(regc, &tdata);
433 if (status != PJ_SUCCESS)
434 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000435
Benny Prijonoccf95622006-02-07 18:48:01 +0000436 /* Add Contact header. */
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000437 msg = tdata->msg;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000438 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
439 pjsip_hdr_shallow_clone(tdata->pool,
Benny Prijono4093f7c2006-09-13 23:48:45 +0000440 regc->contact_hdr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000441 if (regc->expires_hdr)
Benny Prijonoa1e69682007-05-11 15:14:34 +0000442 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
443 pjsip_hdr_shallow_clone(tdata->pool,
Benny Prijono4093f7c2006-09-13 23:48:45 +0000444 regc->expires_hdr));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000445
446 if (regc->timer.id != 0) {
447 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
448 regc->timer.id = 0;
449 }
450
451 regc->auto_reg = autoreg;
452
Benny Prijonoccf95622006-02-07 18:48:01 +0000453 /* Done */
454 *p_tdata = tdata;
455 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000456}
457
458
Benny Prijonoccf95622006-02-07 18:48:01 +0000459PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
460 pjsip_tx_data **p_tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000461{
462 pjsip_tx_data *tdata;
463 pjsip_msg *msg;
Benny Prijonoccf95622006-02-07 18:48:01 +0000464 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000465
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000466 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
467
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000468 if (regc->timer.id != 0) {
469 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
470 regc->timer.id = 0;
471 }
472
Benny Prijonoccf95622006-02-07 18:48:01 +0000473 status = create_request(regc, &tdata);
474 if (status != PJ_SUCCESS)
475 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000476
477 msg = tdata->msg;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000478 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
479 pjsip_hdr_shallow_clone(tdata->pool,
Benny Prijonodfc4c482006-12-02 07:25:29 +0000480 regc->contact_hdr));
481 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
482
483 *p_tdata = tdata;
484 return PJ_SUCCESS;
485}
486
487PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
488 pjsip_tx_data **p_tdata)
489{
490 pjsip_tx_data *tdata;
491 pjsip_msg *msg;
492 pj_status_t status;
493
494 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
495
496 if (regc->timer.id != 0) {
497 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
498 regc->timer.id = 0;
499 }
500
501 status = create_request(regc, &tdata);
502 if (status != PJ_SUCCESS)
503 return status;
504
505 msg = tdata->msg;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000506 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
507 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
508
Benny Prijonoccf95622006-02-07 18:48:01 +0000509 *p_tdata = tdata;
510 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000511}
512
513
514PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
515 int contact_cnt,
516 const pj_str_t contact[] )
517{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000518 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000519 return set_contact( regc, contact_cnt, contact );
520}
521
522
523PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
524 pj_uint32_t expires )
525{
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000526 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000527 set_expires( regc, expires );
Benny Prijonoccf95622006-02-07 18:48:01 +0000528 return PJ_SUCCESS;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000529}
530
531
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000532static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
533 const pj_str_t *reason,
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000534 pjsip_rx_data *rdata, pj_int32_t expiration,
535 int contact_cnt, pjsip_contact_hdr *contact[])
536{
537 struct pjsip_regc_cbparam cbparam;
538
539
Benny Prijonof2651802007-01-26 17:13:56 +0000540 if (!regc->cb)
541 return;
542
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000543 cbparam.regc = regc;
544 cbparam.token = regc->token;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000545 cbparam.status = status;
546 cbparam.code = st_code;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000547 cbparam.reason = *reason;
548 cbparam.rdata = rdata;
549 cbparam.contact_cnt = contact_cnt;
550 cbparam.expiration = expiration;
551 if (contact_cnt) {
552 pj_memcpy( cbparam.contact, contact,
553 contact_cnt*sizeof(pjsip_contact_hdr*));
554 }
555
556 (*regc->cb)(&cbparam);
557}
558
559static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
560 struct pj_timer_entry *entry)
561{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000562 pjsip_regc *regc = (pjsip_regc*) entry->user_data;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000563 pjsip_tx_data *tdata;
Benny Prijonoccf95622006-02-07 18:48:01 +0000564 pj_status_t status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000565
Benny Prijonoccf95622006-02-07 18:48:01 +0000566 PJ_UNUSED_ARG(timer_heap);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000567
Benny Prijonof2651802007-01-26 17:13:56 +0000568 /* Temporarily increase busy flag to prevent regc from being deleted
569 * in pjsip_regc_send()
570 */
571 regc->busy++;
572
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000573 entry->id = 0;
Benny Prijonoccf95622006-02-07 18:48:01 +0000574 status = pjsip_regc_register(regc, 1, &tdata);
575 if (status == PJ_SUCCESS) {
Benny Prijono27042582006-08-08 14:04:21 +0000576 status = pjsip_regc_send(regc, tdata);
577 }
578
Benny Prijonof2651802007-01-26 17:13:56 +0000579 if (status != PJ_SUCCESS && regc->cb) {
Benny Prijonoccf95622006-02-07 18:48:01 +0000580 char errmsg[PJ_ERR_MSG_SIZE];
581 pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000582 call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000583 }
Benny Prijonof2651802007-01-26 17:13:56 +0000584
585 regc->busy--;
586
587 /* Delete the record if user destroy regc during the callback. */
588 if (regc->_delete_flag && regc->busy==0) {
589 pjsip_regc_destroy(regc);
590 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000591}
592
593static void tsx_callback(void *token, pjsip_event *event)
594{
Benny Prijonoccf95622006-02-07 18:48:01 +0000595 pj_status_t status;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000596 pjsip_regc *regc = (pjsip_regc*) token;
Benny Prijonoccf95622006-02-07 18:48:01 +0000597 pjsip_transaction *tsx = event->body.tsx_state.tsx;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000598
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000599 /* Decrement pending transaction counter. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000600 pj_assert(regc->has_tsx);
601 regc->has_tsx = PJ_FALSE;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000602
Benny Prijono58990232007-01-21 16:11:18 +0000603 /* Handle 401/407 challenge (even when _delete_flag is set) */
604 if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
605 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000606 {
Benny Prijonoccf95622006-02-07 18:48:01 +0000607 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000608 pjsip_tx_data *tdata;
609
Benny Prijonoccf95622006-02-07 18:48:01 +0000610 status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
611 rdata,
612 tsx->last_tx,
613 &tdata);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000614
Benny Prijonoccf95622006-02-07 18:48:01 +0000615 if (status == PJ_SUCCESS) {
Benny Prijono3fe034a2007-06-01 12:51:07 +0000616 ++regc->busy;
Benny Prijono27042582006-08-08 14:04:21 +0000617 status = pjsip_regc_send(regc, tdata);
Benny Prijono3fe034a2007-06-01 12:51:07 +0000618 --regc->busy;
619 }
Benny Prijono27042582006-08-08 14:04:21 +0000620
621 if (status != PJ_SUCCESS) {
Benny Prijono58990232007-01-21 16:11:18 +0000622
623 /* Only call callback if application is still interested
624 * in it.
625 */
626 if (regc->_delete_flag == 0) {
627 /* Increment busy flag temporarily to prevent regc from
628 * being destroyed.
629 */
630 ++regc->busy;
631
632 call_callback(regc, status, tsx->status_code,
633 &rdata->msg_info.msg->line.status.reason,
634 rdata, -1, 0, NULL);
635
636 /* Decrement busy flag */
637 --regc->busy;
638 }
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000639 }
Benny Prijono27042582006-08-08 14:04:21 +0000640
Benny Prijono58990232007-01-21 16:11:18 +0000641 } else if (regc->_delete_flag) {
642
643 /* User has called pjsip_regc_destroy(), so don't call callback.
644 * This regc will be destroyed later in this function.
645 */
646
647 /* Nothing to do */
648 ;
Benny Prijono27042582006-08-08 14:04:21 +0000649
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000650 } else {
651 int contact_cnt = 0;
652 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
653 pjsip_rx_data *rdata;
654 pj_int32_t expiration = 0xFFFF;
655
656 if (tsx->status_code/100 == 2) {
657 int i;
658 pjsip_contact_hdr *hdr;
659 pjsip_msg *msg;
660 pjsip_expires_hdr *expires;
661
Benny Prijonoccf95622006-02-07 18:48:01 +0000662 rdata = event->body.tsx_state.src.rdata;
663 msg = rdata->msg_info.msg;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000664 hdr = (pjsip_contact_hdr*)
665 pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000666 while (hdr) {
667 contact[contact_cnt++] = hdr;
668 hdr = hdr->next;
669 if (hdr == (void*)&msg->hdr)
670 break;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000671 hdr = (pjsip_contact_hdr*)
672 pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000673 }
674
Benny Prijonoa1e69682007-05-11 15:14:34 +0000675 expires = (pjsip_expires_hdr*)
676 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000677
678 if (expires)
679 expiration = expires->ivalue;
680
681 for (i=0; i<contact_cnt; ++i) {
682 hdr = contact[i];
683 if (hdr->expires >= 0 && hdr->expires < expiration)
684 expiration = contact[i]->expires;
685 }
686
687 if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
688 pj_time_val delay = { 0, 0};
689
690 delay.sec = expiration - DELAY_BEFORE_REFRESH;
691 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
692 delay.sec > (pj_int32_t)regc->expires)
693 {
694 delay.sec = regc->expires;
695 }
696 if (delay.sec < DELAY_BEFORE_REFRESH)
697 delay.sec = DELAY_BEFORE_REFRESH;
698 regc->timer.cb = &regc_refresh_timer_cb;
699 regc->timer.id = REFRESH_TIMER;
700 regc->timer.user_data = regc;
701 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000702 pj_gettimeofday(&regc->last_reg);
703 regc->next_reg = regc->last_reg;
704 regc->next_reg.sec += delay.sec;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000705 }
706
707 } else {
Benny Prijonoccf95622006-02-07 18:48:01 +0000708 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
709 event->body.tsx_state.src.rdata : NULL;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000710 }
711
Benny Prijono25a86c72006-12-01 20:50:01 +0000712 /* Increment busy flag temporarily to prevent regc from
Benny Prijono197cabf2006-10-16 20:05:27 +0000713 * being destroyed.
714 */
Benny Prijono25a86c72006-12-01 20:50:01 +0000715 ++regc->busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000716
717 /* Call callback. */
718 if (expiration == 0xFFFF) expiration = -1;
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000719 call_callback(regc, PJ_SUCCESS, tsx->status_code,
Benny Prijonoccf95622006-02-07 18:48:01 +0000720 (rdata ? &rdata->msg_info.msg->line.status.reason
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000721 : pjsip_get_status_text(tsx->status_code)),
722 rdata, expiration,
723 contact_cnt, contact);
724
Benny Prijono25a86c72006-12-01 20:50:01 +0000725 /* Decrement busy flag */
726 --regc->busy;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000727 }
728
729 /* Delete the record if user destroy regc during the callback. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000730 if (regc->_delete_flag && regc->busy==0) {
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000731 pjsip_regc_destroy(regc);
732 }
733}
734
Benny Prijonoccf95622006-02-07 18:48:01 +0000735PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000736{
Benny Prijonoccf95622006-02-07 18:48:01 +0000737 pj_status_t status;
Benny Prijono59ca70f2006-02-22 22:18:58 +0000738 pjsip_cseq_hdr *cseq_hdr;
739 pj_uint32_t cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000740
741 /* Make sure we don't have pending transaction. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000742 if (regc->has_tsx) {
Benny Prijono5b656872006-08-08 00:41:00 +0000743 PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
744 "transaction pending"));
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000745 pjsip_tx_data_dec_ref( tdata );
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000746 return PJSIP_EBUSY;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000747 }
748
749 /* Invalidate message buffer. */
750 pjsip_tx_data_invalidate_msg(tdata);
751
752 /* Increment CSeq */
Benny Prijono59ca70f2006-02-22 22:18:58 +0000753 cseq = ++regc->cseq_hdr->cseq;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000754 cseq_hdr = (pjsip_cseq_hdr*)
755 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
Benny Prijono59ca70f2006-02-22 22:18:58 +0000756 cseq_hdr->cseq = cseq;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000757
Benny Prijonodc39fe82006-05-26 12:17:46 +0000758 /* Increment pending transaction first, since transaction callback
759 * may be called even before send_request() returns!
760 */
Benny Prijono25a86c72006-12-01 20:50:01 +0000761 regc->has_tsx = PJ_TRUE;
762 ++regc->busy;
Benny Prijono0f35f912007-02-05 18:59:31 +0000763 status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
764 regc, &tsx_callback);
Benny Prijono5b656872006-08-08 00:41:00 +0000765 if (status!=PJ_SUCCESS) {
Benny Prijono5b656872006-08-08 00:41:00 +0000766 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
767 }
Benny Prijono25a86c72006-12-01 20:50:01 +0000768 --regc->busy;
Benny Prijono197cabf2006-10-16 20:05:27 +0000769
770 /* Delete the record if user destroy regc during the callback. */
Benny Prijono25a86c72006-12-01 20:50:01 +0000771 if (regc->_delete_flag && regc->busy==0) {
Benny Prijono197cabf2006-10-16 20:05:27 +0000772 pjsip_regc_destroy(regc);
773 }
Benny Prijonoccf95622006-02-07 18:48:01 +0000774
775 return status;
Benny Prijono5dcb38d2005-11-21 01:55:47 +0000776}
777
778