blob: 1b22170ecf9eb01e03e535c3f01bcc00e3f4dec8 [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 */
19#include <pjsip_mod_ua/sip_reg.h>
20#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>
27#include <pj/pool.h>
28#include <pj/string.h>
29#include <pj/guid.h>
30#include <pj/log.h>
31
32#define REFRESH_TIMER 1
33#define DELAY_BEFORE_REFRESH 5
34#define THIS_FILE "sip_regc.c"
35
36/**
37 * SIP client registration structure.
38 */
39struct pjsip_regc
40{
41 pj_pool_t *pool;
42 pjsip_endpoint *endpt;
43 pj_bool_t _delete_flag;
44 int pending_tsx;
45
46 void *token;
47 pjsip_regc_cb *cb;
48
49 pj_str_t str_srv_url;
50 pjsip_uri *srv_url;
51 pjsip_cid_hdr *cid_hdr;
52 pjsip_cseq_hdr *cseq_hdr;
53 pjsip_from_hdr *from_hdr;
54 pjsip_to_hdr *to_hdr;
55 char *contact_buf;
56 pjsip_generic_string_hdr *contact_hdr;
57 pjsip_expires_hdr *expires_hdr;
58 pjsip_contact_hdr *unreg_contact_hdr;
59 pjsip_expires_hdr *unreg_expires_hdr;
60 pj_uint32_t expires;
61
62 /* Credentials. */
63 int cred_count;
64 pjsip_cred_info *cred_info;
65
66 /* Authorization sessions. */
67 pjsip_auth_session auth_sess_list;
68
69 /* Auto refresh registration. */
70 pj_bool_t auto_reg;
71 pj_timer_entry timer;
72};
73
74
75
76PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
77 pjsip_regc_cb *cb)
78{
79 pj_pool_t *pool;
80 pjsip_regc *regc;
81
82 if (cb == NULL)
83 return NULL;
84
85 pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
86 regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc));
87
88 regc->pool = pool;
89 regc->endpt = endpt;
90 regc->token = token;
91 regc->cb = cb;
92 regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
93 regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
94
95 pj_list_init(&regc->auth_sess_list);
96
97 return regc;
98}
99
100
101PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc)
102{
103 if (regc->pending_tsx) {
104 regc->_delete_flag = 1;
105 regc->cb = NULL;
106 } else {
107 pjsip_endpt_destroy_pool(regc->endpt, regc->pool);
108 }
109}
110
111
112PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
113{
114 return regc->pool;
115}
116
117static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
118{
119 if (expires != regc->expires) {
120 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool);
121 regc->expires_hdr->ivalue = expires;
122 } else {
123 regc->expires_hdr = NULL;
124 }
125}
126
127
128static pj_status_t set_contact( pjsip_regc *regc,
129 int contact_cnt,
130 const pj_str_t contact[] )
131{
132 int i;
133 char *s;
134 const pj_str_t contact_STR = { "Contact", 7};
135
136 /* Concatenate contacts. */
137 for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
138 if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
139 return -1;
140 }
141 pj_memcpy(s, contact[i].ptr, contact[i].slen);
142 s += contact[i].slen;
143
144 if (i != contact_cnt - 1) {
145 *s++ = ',';
146 *s++ = ' ';
147 }
148 }
149
150 /* Set "Contact" header. */
151 regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR);
152 regc->contact_hdr->hvalue.ptr = regc->contact_buf;
153 regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
154
155 return 0;
156}
157
158
159PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
160 const pj_str_t *srv_url,
161 const pj_str_t *from_url,
162 const pj_str_t *to_url,
163 int contact_cnt,
164 const pj_str_t contact[],
165 pj_uint32_t expires)
166{
167 pj_str_t tmp;
168
169 /* Copy server URL. */
170 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
171
172 /* Set server URL. */
173 tmp = regc->str_srv_url;
174 regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
175 if (regc->srv_url == NULL) {
176 return -1;
177 }
178
179 /* Set "From" header. */
180 pj_strdup_with_null(regc->pool, &tmp, from_url);
181 regc->from_hdr = pjsip_from_hdr_create(regc->pool);
182 regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
183 PJSIP_PARSE_URI_AS_NAMEADDR);
184 if (!regc->from_hdr->uri) {
185 PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr));
186 return -1;
187 }
188
189 /* Set "To" header. */
190 pj_strdup_with_null(regc->pool, &tmp, to_url);
191 regc->to_hdr = pjsip_to_hdr_create(regc->pool);
192 regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
193 PJSIP_PARSE_URI_AS_NAMEADDR);
194 if (!regc->to_hdr->uri) {
195 PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
196 return -1;
197 }
198
199
200 /* Set "Contact" header. */
201 if (set_contact( regc, contact_cnt, contact) != 0)
202 return -1;
203
204 /* Set "Expires" header, if required. */
205 set_expires( regc, expires);
206
207 /* Set "Call-ID" header. */
208 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
209 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
210
211 /* Set "CSeq" header. */
212 regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
213 regc->cseq_hdr->cseq = 0;
214 pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
215
216 /* Create "Contact" header used in unregistration. */
217 regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
218 regc->unreg_contact_hdr->star = 1;
219
220 /* Create "Expires" header used in unregistration. */
221 regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool);
222 regc->unreg_expires_hdr->ivalue = 0;
223
224 /* Done. */
225 return 0;
226}
227
228PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
229 int count,
230 const pjsip_cred_info cred[] )
231{
232 if (count > 0) {
233 regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info));
234 pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info));
235 }
236 regc->cred_count = count;
237 return 0;
238}
239
240static pjsip_tx_data *create_request(pjsip_regc *regc)
241{
242 pjsip_tx_data *tdata;
243 pjsip_msg *msg;
244
245 /* Create transmit data. */
246 tdata = pjsip_endpt_create_tdata(regc->endpt);
247 if (!tdata) {
248 return NULL;
249 }
250
251 /* Create request message. */
252 msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
253 tdata->msg = msg;
254
255 /* Initialize request line. */
256 pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD);
257 msg->line.req.uri = regc->srv_url;
258
259 /* Add headers. */
260 pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr);
261 pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr);
262 pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr);
263 pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr);
264
265 /* Add cached authorization headers. */
266 pjsip_auth_init_req( regc->pool, tdata, &regc->auth_sess_list,
267 regc->cred_count, regc->cred_info );
268
269 /* Add reference counter to transmit data. */
270 pjsip_tx_data_add_ref(tdata);
271
272 return tdata;
273}
274
275
276PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg)
277{
278 pjsip_msg *msg;
279 pjsip_tx_data *tdata;
280
281 tdata = create_request(regc);
282 if (!tdata)
283 return NULL;
284
285 msg = tdata->msg;
286 pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr);
287 if (regc->expires_hdr)
288 pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr);
289
290 if (regc->timer.id != 0) {
291 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
292 regc->timer.id = 0;
293 }
294
295 regc->auto_reg = autoreg;
296
297 return tdata;
298}
299
300
301PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc)
302{
303 pjsip_tx_data *tdata;
304 pjsip_msg *msg;
305
306 if (regc->timer.id != 0) {
307 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
308 regc->timer.id = 0;
309 }
310
311 tdata = create_request(regc);
312 if (!tdata)
313 return NULL;
314
315 msg = tdata->msg;
316 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
317 pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
318
319 return tdata;
320}
321
322
323PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
324 int contact_cnt,
325 const pj_str_t contact[] )
326{
327 return set_contact( regc, contact_cnt, contact );
328}
329
330
331PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
332 pj_uint32_t expires )
333{
334 set_expires( regc, expires );
335 return 0;
336}
337
338
339static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason,
340 pjsip_rx_data *rdata, pj_int32_t expiration,
341 int contact_cnt, pjsip_contact_hdr *contact[])
342{
343 struct pjsip_regc_cbparam cbparam;
344
345
346 cbparam.regc = regc;
347 cbparam.token = regc->token;
348 cbparam.code = status;
349 cbparam.reason = *reason;
350 cbparam.rdata = rdata;
351 cbparam.contact_cnt = contact_cnt;
352 cbparam.expiration = expiration;
353 if (contact_cnt) {
354 pj_memcpy( cbparam.contact, contact,
355 contact_cnt*sizeof(pjsip_contact_hdr*));
356 }
357
358 (*regc->cb)(&cbparam);
359}
360
361static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
362 struct pj_timer_entry *entry)
363{
364 pjsip_regc *regc = entry->user_data;
365 pjsip_tx_data *tdata;
366
367 PJ_UNUSED_ARG(timer_heap)
368
369 entry->id = 0;
370 tdata = pjsip_regc_register(regc, 1);
371 if (tdata) {
372 pjsip_regc_send(regc, tdata);
373 } else {
374 pj_str_t reason = pj_str("Unable to create txdata");
375 call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
376 }
377}
378
379static void tsx_callback(void *token, pjsip_event *event)
380{
381 pjsip_regc *regc = token;
382 pjsip_transaction *tsx = event->obj.tsx;
383
384 /* If registration data has been deleted by user then remove registration
385 * data from transaction's callback, and don't call callback.
386 */
387 if (regc->_delete_flag) {
388 --regc->pending_tsx;
389
390 } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
391 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
392 {
393 pjsip_rx_data *rdata = event->src.rdata;
394 pjsip_tx_data *tdata;
395
396 tdata = pjsip_auth_reinit_req( regc->endpt,
397 regc->pool, &regc->auth_sess_list,
398 regc->cred_count, regc->cred_info,
399 tsx->last_tx, event->src.rdata );
400
401 if (tdata) {
402 --regc->pending_tsx;
403 pjsip_regc_send(regc, tdata);
404 return;
405 } else {
406 call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason,
407 rdata, -1, 0, NULL);
408 --regc->pending_tsx;
409 }
410 } else {
411 int contact_cnt = 0;
412 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
413 pjsip_rx_data *rdata;
414 pj_int32_t expiration = 0xFFFF;
415
416 if (tsx->status_code/100 == 2) {
417 int i;
418 pjsip_contact_hdr *hdr;
419 pjsip_msg *msg;
420 pjsip_expires_hdr *expires;
421
422 rdata = event->src.rdata;
423 msg = rdata->msg;
424 hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
425 while (hdr) {
426 contact[contact_cnt++] = hdr;
427 hdr = hdr->next;
428 if (hdr == (void*)&msg->hdr)
429 break;
430 hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
431 }
432
433 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
434
435 if (expires)
436 expiration = expires->ivalue;
437
438 for (i=0; i<contact_cnt; ++i) {
439 hdr = contact[i];
440 if (hdr->expires >= 0 && hdr->expires < expiration)
441 expiration = contact[i]->expires;
442 }
443
444 if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
445 pj_time_val delay = { 0, 0};
446
447 delay.sec = expiration - DELAY_BEFORE_REFRESH;
448 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
449 delay.sec > (pj_int32_t)regc->expires)
450 {
451 delay.sec = regc->expires;
452 }
453 if (delay.sec < DELAY_BEFORE_REFRESH)
454 delay.sec = DELAY_BEFORE_REFRESH;
455 regc->timer.cb = &regc_refresh_timer_cb;
456 regc->timer.id = REFRESH_TIMER;
457 regc->timer.user_data = regc;
458 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
459 }
460
461 } else {
462 rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL;
463 }
464
465
466 /* Call callback. */
467 if (expiration == 0xFFFF) expiration = -1;
468 call_callback(regc, tsx->status_code,
469 (rdata ? &rdata->msg->line.status.reason
470 : pjsip_get_status_text(tsx->status_code)),
471 rdata, expiration,
472 contact_cnt, contact);
473
474 --regc->pending_tsx;
475 }
476
477 /* Delete the record if user destroy regc during the callback. */
478 if (regc->_delete_flag && regc->pending_tsx==0) {
479 pjsip_regc_destroy(regc);
480 }
481}
482
483PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
484{
485 int status;
486
487 /* Make sure we don't have pending transaction. */
488 if (regc->pending_tsx) {
489 pj_str_t reason = pj_str("Transaction in progress");
490 call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
491 pjsip_tx_data_dec_ref( tdata );
492 return;
493 }
494
495 /* Invalidate message buffer. */
496 pjsip_tx_data_invalidate_msg(tdata);
497
498 /* Increment CSeq */
499 regc->cseq_hdr->cseq++;
500
501 /* Send. */
502 status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
503 if (status==0)
504 ++regc->pending_tsx;
505 else {
506 pj_str_t reason = pj_str("Unable to send request.");
507 call_callback(regc, status, &reason, NULL, -1, 0, NULL);
508 }
509}
510
511