blob: 9f63845a74dd0b717daf3d7b2945170fcdee53cb [file] [log] [blame]
Benny Prijono268ca612006-02-07 12:34:11 +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-ua/sip_inv.h>
20#include <pjsip/sip_module.h>
21#include <pjsip/sip_endpoint.h>
22#include <pjsip/sip_event.h>
23#include <pjsip/sip_transaction.h>
24#include <pjmedia/sdp.h>
25#include <pjmedia/sdp_neg.h>
26#include <pj/string.h>
27#include <pj/pool.h>
28#include <pj/assert.h>
29
30#define THIS_FILE "sip_invite_session.c"
31
32/*
33 * Static prototypes.
34 */
35static pj_status_t mod_inv_load(pjsip_endpoint *endpt);
36static pj_status_t mod_inv_unload(void);
37static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata);
38static pj_bool_t mod_inv_on_rx_response(pjsip_rx_data *rdata);
39static void mod_inv_on_tsx_state(pjsip_transaction*, pjsip_event*);
40
41static void inv_on_state_null( pjsip_inv_session *s, pjsip_event *e);
42static void inv_on_state_calling( pjsip_inv_session *s, pjsip_event *e);
43static void inv_on_state_incoming( pjsip_inv_session *s, pjsip_event *e);
44static void inv_on_state_early( pjsip_inv_session *s, pjsip_event *e);
45static void inv_on_state_connecting( pjsip_inv_session *s, pjsip_event *e);
46static void inv_on_state_confirmed( pjsip_inv_session *s, pjsip_event *e);
47static void inv_on_state_disconnected( pjsip_inv_session *s, pjsip_event *e);
48static void inv_on_state_terminated( pjsip_inv_session *s, pjsip_event *e);
49
50static void (*inv_state_handler[])( pjsip_inv_session *s, pjsip_event *e) =
51{
52 &inv_on_state_null,
53 &inv_on_state_calling,
54 &inv_on_state_incoming,
55 &inv_on_state_early,
56 &inv_on_state_connecting,
57 &inv_on_state_confirmed,
58 &inv_on_state_disconnected,
59 &inv_on_state_terminated,
60};
61
62static struct mod_inv
63{
64 pjsip_module mod;
65 pjsip_endpoint *endpt;
66 pjsip_inv_callback cb;
67 pjsip_module *app_user;
68} mod_inv =
69{
70 {
71 NULL, NULL, /* prev, next. */
72 { "mod-invite", 10 }, /* Name. */
73 -1, /* Id */
74 PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */
75 NULL, /* User data. */
76 &mod_inv_load, /* load() */
77 NULL, /* start() */
78 NULL, /* stop() */
79 &mod_inv_unload, /* unload() */
80 &mod_inv_on_rx_request, /* on_rx_request() */
81 &mod_inv_on_rx_response, /* on_rx_response() */
82 NULL, /* on_tx_request. */
83 NULL, /* on_tx_response() */
84 &mod_inv_on_tsx_state, /* on_tsx_state() */
85 }
86};
87
88
89
90static pj_status_t mod_inv_load(pjsip_endpoint *endpt)
91{
92 pj_str_t allowed[] = {{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}};
93
94 /* Register supported methods: INVITE, ACK, BYE, CANCEL */
95 pjsip_endpt_add_capability(endpt, &mod_inv.mod, PJSIP_H_ALLOW, NULL,
96 PJ_ARRAY_SIZE(allowed), allowed);
97
98 return PJ_SUCCESS;
99}
100
101static pj_status_t mod_inv_unload(void)
102{
103 /* Should remove capability here */
104 return PJ_SUCCESS;
105}
106
107static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata)
108{
Benny Prijonoccf95622006-02-07 18:48:01 +0000109 pjsip_dialog *dlg;
110
Benny Prijono268ca612006-02-07 12:34:11 +0000111 /* Ignore requests outside dialog */
Benny Prijonoccf95622006-02-07 18:48:01 +0000112 dlg = pjsip_rdata_get_dlg(rdata);
113 if (dlg == NULL)
Benny Prijono268ca612006-02-07 12:34:11 +0000114 return PJ_FALSE;
115
Benny Prijonoccf95622006-02-07 18:48:01 +0000116 /* Answer BYE with 200/OK. */
117 if (rdata->msg_info.msg->line.req.method.id == PJSIP_BYE_METHOD) {
118 pj_status_t status;
119 pjsip_tx_data *tdata;
120
121 status = pjsip_dlg_create_response(dlg, rdata, 200, NULL, &tdata);
122 if (status == PJ_SUCCESS)
123 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
124 tdata);
125
126 return status==PJ_SUCCESS ? PJ_TRUE : PJ_FALSE;
127 }
128
Benny Prijono268ca612006-02-07 12:34:11 +0000129 return PJ_FALSE;
130}
131
132static pj_status_t send_ack(pjsip_inv_session *inv, pjsip_rx_data *rdata)
133{
134 pjsip_tx_data *tdata;
135 pj_status_t status;
136
137 status = pjsip_dlg_create_request(inv->dlg, &pjsip_ack_method,
138 rdata->msg_info.cseq->cseq, &tdata);
139 if (status != PJ_SUCCESS) {
140 /* Better luck next time */
141 pj_assert(!"Unable to create ACK!");
142 return status;
143 }
144
145 status = pjsip_dlg_send_request(inv->dlg, tdata, NULL);
146 if (status != PJ_SUCCESS) {
147 /* Better luck next time */
148 pj_assert(!"Unable to send ACK!");
149 return status;
150 }
151
152 return PJ_SUCCESS;
153}
154
155static pj_bool_t mod_inv_on_rx_response(pjsip_rx_data *rdata)
156{
157 pjsip_dialog *dlg;
158 pjsip_inv_session *inv;
159 pjsip_msg *msg = rdata->msg_info.msg;
160
161 dlg = pjsip_rdata_get_dlg(rdata);
162
163 /* Ignore responses outside dialog */
164 if (dlg == NULL)
165 return PJ_FALSE;
166
167 /* Ignore responses not belonging to invite session */
168 inv = pjsip_dlg_get_inv_session(dlg);
169 if (inv == NULL)
170 return PJ_FALSE;
171
172 /* This MAY be retransmission of 2xx response to INVITE.
173 * If it is, we need to send ACK.
174 */
175 if (msg->type == PJSIP_RESPONSE_MSG && msg->line.status.code/100==2 &&
176 rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) {
177
178 send_ack(inv, rdata);
179 return PJ_TRUE;
180
181 }
182
183 /* No other processing needs to be done here. */
184 return PJ_FALSE;
185}
186
187static void mod_inv_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
188{
189 pjsip_dialog *dlg;
190 pjsip_inv_session *inv;
191
192 dlg = pjsip_tsx_get_dlg(tsx);
193 if (dlg == NULL)
194 return;
195
196 inv = pjsip_dlg_get_inv_session(dlg);
197 if (inv == NULL)
198 return;
199
200 /* Call state handler for the invite session. */
201 (*inv_state_handler[inv->state])(inv, e);
202
203 /* Call on_tsx_state */
204 if (mod_inv.cb.on_tsx_state_changed)
205 (*mod_inv.cb.on_tsx_state_changed)(inv, tsx, e);
206
207 /* Clear invite transaction when tsx is terminated. */
208 if (tsx->state==PJSIP_TSX_STATE_TERMINATED && tsx == inv->invite_tsx)
209 inv->invite_tsx = NULL;
210}
211
212PJ_DEF(pj_status_t) pjsip_inv_usage_init( pjsip_endpoint *endpt,
213 pjsip_module *app_module,
214 const pjsip_inv_callback *cb)
215{
216 pj_status_t status;
217
218 /* Check arguments. */
219 PJ_ASSERT_RETURN(endpt && app_module && cb, PJ_EINVAL);
220
221 /* Some callbacks are mandatory */
222 PJ_ASSERT_RETURN(cb->on_state_changed && cb->on_new_session, PJ_EINVAL);
223
224 /* Check if module already registered. */
225 PJ_ASSERT_RETURN(mod_inv.mod.id == -1, PJ_EINVALIDOP);
226
227 /* Copy param. */
228 pj_memcpy(&mod_inv.cb, cb, sizeof(pjsip_inv_callback));
229
230 mod_inv.endpt = endpt;
231 mod_inv.app_user = app_module;
232
233 /* Register the module. */
234 status = pjsip_endpt_register_module(endpt, &mod_inv.mod);
235
236 return status;
237}
238
239PJ_DEF(pjsip_module*) pjsip_inv_usage_instance(void)
240{
241 return &mod_inv.mod;
242}
243
244
245PJ_DEF(pjsip_inv_session*) pjsip_dlg_get_inv_session(pjsip_dialog *dlg)
246{
247 return dlg->mod_data[mod_inv.mod.id];
248}
249
250/*
251 * Create UAC session.
252 */
253PJ_DEF(pj_status_t) pjsip_inv_create_uac( pjsip_dialog *dlg,
254 const pjmedia_sdp_session *local_sdp,
255 unsigned options,
256 pjsip_inv_session **p_inv)
257{
258 pjsip_inv_session *inv;
259 pj_status_t status;
260
261 /* Verify arguments. */
262 PJ_ASSERT_RETURN(dlg && p_inv, PJ_EINVAL);
263
264 /* Normalize options */
265 if (options & PJSIP_INV_REQUIRE_100REL)
266 options |= PJSIP_INV_SUPPORT_100REL;
267
268 if (options & PJSIP_INV_REQUIRE_TIMER)
269 options |= PJSIP_INV_SUPPORT_TIMER;
270
271 /* Create the session */
272 inv = pj_pool_zalloc(dlg->pool, sizeof(pjsip_inv_session));
273 PJ_ASSERT_RETURN(inv != NULL, PJ_ENOMEM);
274
275 inv->pool = dlg->pool;
276 inv->role = PJSIP_ROLE_UAC;
277 inv->state = PJSIP_INV_STATE_NULL;
278 inv->dlg = dlg;
279 inv->options = options;
280
281 /* Create negotiator if local_sdp is specified. */
282 if (local_sdp) {
283 status = pjmedia_sdp_neg_create_w_local_offer(dlg->pool, local_sdp,
284 &inv->neg);
285 if (status != PJ_SUCCESS)
286 return status;
287 }
288
289 /* Register invite as dialog usage. */
290 status = pjsip_dlg_add_usage(dlg, &mod_inv.mod, inv);
291 if (status != PJ_SUCCESS)
292 return status;
293
294 /* Increment dialog session */
295 pjsip_dlg_inc_session(dlg);
296
297 /* Done */
298 *p_inv = inv;
299 return PJ_SUCCESS;
300}
301
302/*
303 * Verify incoming INVITE request.
304 */
305PJ_DEF(pj_status_t) pjsip_inv_verify_request(pjsip_rx_data *rdata,
306 unsigned *options,
307 const pjmedia_sdp_session *l_sdp,
308 pjsip_dialog *dlg,
309 pjsip_endpoint *endpt,
310 pjsip_tx_data **p_tdata)
311{
312 pjsip_msg *msg;
313 pjsip_allow_hdr *allow;
314 pjsip_supported_hdr *sup_hdr;
315 pjsip_require_hdr *req_hdr;
316 int code = 200;
317 unsigned rem_option = 0;
318 pj_status_t status = PJ_SUCCESS;
319 pjsip_hdr res_hdr_list;
320
321 /* Init return arguments. */
322 if (p_tdata) *p_tdata = NULL;
323
324 /* Verify arguments. */
325 PJ_ASSERT_RETURN(rdata != NULL && options != NULL, PJ_EINVAL);
326
327 /* Normalize options */
328 if (*options & PJSIP_INV_REQUIRE_100REL)
329 *options |= PJSIP_INV_SUPPORT_100REL;
330
331 if (*options & PJSIP_INV_REQUIRE_TIMER)
332 *options |= PJSIP_INV_SUPPORT_TIMER;
333
334 /* Get the message in rdata */
335 msg = rdata->msg_info.msg;
336
337 /* Must be INVITE request. */
338 PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG &&
339 msg->line.req.method.id == PJSIP_INVITE_METHOD,
340 PJ_EINVAL);
341
342 /* If tdata is specified, then either dlg or endpt must be specified */
343 PJ_ASSERT_RETURN((!p_tdata) || (endpt || dlg), PJ_EINVAL);
344
345 /* Get the endpoint */
346 endpt = endpt ? endpt : dlg->endpt;
347
348 /* Init response header list */
349 pj_list_init(&res_hdr_list);
350
351 /* Check the request body, see if it's something that we support
352 * (i.e. SDP).
353 */
354 if (msg->body) {
355 pjsip_msg_body *body = msg->body;
356 pj_str_t str_application = {"application", 11};
357 pj_str_t str_sdp = { "sdp", 3 };
358 pjmedia_sdp_session *sdp;
359
360 /* Check content type. */
361 if (pj_stricmp(&body->content_type.type, &str_application) != 0 ||
362 pj_stricmp(&body->content_type.subtype, &str_sdp) != 0)
363 {
364 /* Not "application/sdp" */
365 code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
366 status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
367
368 if (p_tdata) {
369 /* Add Accept header to response */
370 pjsip_accept_hdr *acc;
371
372 acc = pjsip_accept_hdr_create(rdata->tp_info.pool);
373 PJ_ASSERT_RETURN(acc, PJ_ENOMEM);
374 acc->values[acc->count++] = pj_str("application/sdp");
375 pj_list_push_back(&res_hdr_list, acc);
376 }
377
378 goto on_return;
379 }
380
381 /* Parse and validate SDP */
382 status = pjmedia_sdp_parse(rdata->tp_info.pool, body->data, body->len,
383 &sdp);
384 if (status == PJ_SUCCESS)
385 status = pjmedia_sdp_validate(sdp);
386
387 if (status != PJ_SUCCESS) {
388 /* Unparseable or invalid SDP */
389 code = PJSIP_SC_BAD_REQUEST;
390
391 if (p_tdata) {
392 /* Add Warning header. */
393 pjsip_warning_hdr *w;
394
395 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
396 pjsip_endpt_name(endpt),
397 status);
398 PJ_ASSERT_RETURN(w, PJ_ENOMEM);
399
400 pj_list_push_back(&res_hdr_list, w);
401 }
402
403 goto on_return;
404 }
405
406 /* Negotiate with local SDP */
407 if (l_sdp) {
408 pjmedia_sdp_neg *neg;
409
410 /* Local SDP must be valid! */
411 PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(l_sdp))==PJ_SUCCESS,
412 status);
413
414 /* Create SDP negotiator */
415 status = pjmedia_sdp_neg_create_w_remote_offer(
416 rdata->tp_info.pool, l_sdp, sdp, &neg);
417 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
418
419 /* Negotiate SDP */
420 status = pjmedia_sdp_neg_negotiate(rdata->tp_info.pool, neg, 0);
421 if (status != PJ_SUCCESS) {
422
423 /* Incompatible media */
424 code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
425 status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
426
427 if (p_tdata) {
428 pjsip_accept_hdr *acc;
429 pjsip_warning_hdr *w;
430
431 /* Add Warning header. */
432 w = pjsip_warning_hdr_create_from_status(
433 rdata->tp_info.pool,
434 pjsip_endpt_name(endpt), status);
435 PJ_ASSERT_RETURN(w, PJ_ENOMEM);
436
437 pj_list_push_back(&res_hdr_list, w);
438
439 /* Add Accept header to response */
440 acc = pjsip_accept_hdr_create(rdata->tp_info.pool);
441 PJ_ASSERT_RETURN(acc, PJ_ENOMEM);
442 acc->values[acc->count++] = pj_str("application/sdp");
443 pj_list_push_back(&res_hdr_list, acc);
444
445 }
446
447 goto on_return;
448 }
449 }
450 }
451
452 /* Check supported methods, see if peer supports UPDATE.
453 * We just assume that peer supports standard INVITE, ACK, CANCEL, and BYE
454 * implicitly by sending this INVITE.
455 */
456 allow = pjsip_msg_find_hdr(msg, PJSIP_H_ALLOW, NULL);
457 if (allow) {
458 unsigned i;
459 const pj_str_t STR_UPDATE = { "UPDATE", 6 };
460
461 for (i=0; i<allow->count; ++i) {
462 if (pj_stricmp(&allow->values[i], &STR_UPDATE)==0)
463 break;
464 }
465
466 if (i != allow->count) {
467 /* UPDATE is present in Allow */
468 rem_option |= PJSIP_INV_SUPPORT_UPDATE;
469 }
470
471 }
472
473 /* Check Supported header */
474 sup_hdr = pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL);
475 if (sup_hdr) {
476 unsigned i;
477 pj_str_t STR_100REL = { "100rel", 6};
478 pj_str_t STR_TIMER = { "timer", 5 };
479
480 for (i=0; i<sup_hdr->count; ++i) {
481 if (pj_stricmp(&sup_hdr->values[i], &STR_100REL)==0)
482 rem_option |= PJSIP_INV_SUPPORT_100REL;
483 else if (pj_stricmp(&sup_hdr->values[i], &STR_TIMER)==0)
484 rem_option |= PJSIP_INV_SUPPORT_TIMER;
485 }
486 }
487
488 /* Check Require header */
489 req_hdr = pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL);
490 if (req_hdr) {
491 unsigned i;
492 pj_str_t STR_100REL = { "100rel", 6};
493 pj_str_t STR_TIMER = { "timer", 5 };
494 unsigned unsupp_cnt = 0;
495 pj_str_t unsupp_tags[PJSIP_GENERIC_ARRAY_MAX_COUNT];
496
497 for (i=0; i<req_hdr->count; ++i) {
498 if ((*options & PJSIP_INV_SUPPORT_100REL) &&
499 pj_stricmp(&req_hdr->values[i], &STR_100REL)==0)
500 {
501 rem_option |= PJSIP_INV_REQUIRE_100REL;
502
503 } else if ((*options && PJSIP_INV_SUPPORT_TIMER) &&
504 pj_stricmp(&req_hdr->values[i], &STR_TIMER)==0)
505 {
506 rem_option |= PJSIP_INV_REQUIRE_TIMER;
507
508 } else {
509 /* Unknown/unsupported extension tag! */
510 unsupp_tags[unsupp_cnt++] = req_hdr->values[i];
511 }
512 }
513
514 /* Check if there are required tags that we don't support */
515 if (unsupp_cnt) {
516
517 code = PJSIP_SC_BAD_EXTENSION;
518 status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
519
520 if (p_tdata) {
521 pjsip_unsupported_hdr *unsupp_hdr;
522 const pjsip_hdr *h;
523
524 /* Add Unsupported header. */
525 unsupp_hdr = pjsip_unsupported_hdr_create(rdata->tp_info.pool);
526 PJ_ASSERT_RETURN(unsupp_hdr != NULL, PJ_ENOMEM);
527
528 unsupp_hdr->count = unsupp_cnt;
529 for (i=0; i<unsupp_cnt; ++i)
530 unsupp_hdr->values[i] = unsupp_tags[i];
531
532 pj_list_push_back(&res_hdr_list, unsupp_hdr);
533
534 /* Add Supported header. */
535 h = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED,
536 NULL);
537 pj_assert(h);
538 if (h) {
539 sup_hdr = pjsip_hdr_clone(rdata->tp_info.pool, h);
540 pj_list_push_back(&res_hdr_list, sup_hdr);
541 }
542 }
543
544 goto on_return;
545 }
546 }
547
548 /* Check if there are local requirements that are not supported
549 * by peer.
550 */
551 if ( ((*options & PJSIP_INV_REQUIRE_100REL)!=0 &&
552 (rem_option & PJSIP_INV_SUPPORT_100REL)==0) ||
553 ((*options & PJSIP_INV_REQUIRE_TIMER)!=0 &&
554 (rem_option & PJSIP_INV_SUPPORT_TIMER)==0))
555 {
556 code = PJSIP_SC_EXTENSION_REQUIRED;
557 status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
558
559 if (p_tdata) {
560 const pjsip_hdr *h;
561
562 /* Add Require header. */
563 req_hdr = pjsip_require_hdr_create(rdata->tp_info.pool);
564 PJ_ASSERT_RETURN(req_hdr != NULL, PJ_ENOMEM);
565
566 if (*options & PJSIP_INV_REQUIRE_100REL)
567 req_hdr->values[req_hdr->count++] = pj_str("100rel");
568
569 if (*options & PJSIP_INV_REQUIRE_TIMER)
570 req_hdr->values[req_hdr->count++] = pj_str("timer");
571
572 pj_list_push_back(&res_hdr_list, req_hdr);
573
574 /* Add Supported header. */
575 h = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED,
576 NULL);
577 pj_assert(h);
578 if (h) {
579 sup_hdr = pjsip_hdr_clone(rdata->tp_info.pool, h);
580 pj_list_push_back(&res_hdr_list, sup_hdr);
581 }
582
583 }
584
585 goto on_return;
586 }
587
588on_return:
589
590 /* Create response if necessary */
591 if (code != 200 && p_tdata) {
592 pjsip_tx_data *tdata;
593 const pjsip_hdr *h;
594
595 if (dlg) {
596 status = pjsip_dlg_create_response(dlg, rdata, code, NULL,
597 &tdata);
598 } else {
599 status = pjsip_endpt_create_response(endpt, rdata, code, NULL,
600 &tdata);
601 }
602
603 if (status != PJ_SUCCESS)
604 return status;
605
606 /* Add response headers. */
607 h = res_hdr_list.next;
608 while (h != &res_hdr_list) {
609 pjsip_hdr *cloned;
610
611 cloned = pjsip_hdr_clone(tdata->pool, h);
612 PJ_ASSERT_RETURN(cloned, PJ_ENOMEM);
613
614 pjsip_msg_add_hdr(tdata->msg, cloned);
615
616 h = h->next;
617 }
618
619 *p_tdata = tdata;
620 }
621
622 return status;
623}
624
625/*
626 * Create UAS session.
627 */
628PJ_DEF(pj_status_t) pjsip_inv_create_uas( pjsip_dialog *dlg,
629 pjsip_rx_data *rdata,
630 const pjmedia_sdp_session *local_sdp,
631 unsigned options,
632 pjsip_inv_session **p_inv)
633{
634 pjsip_inv_session *inv;
635 pjsip_msg *msg;
636 pjmedia_sdp_session *rem_sdp = NULL;
637 pj_status_t status;
638
639 /* Verify arguments. */
640 PJ_ASSERT_RETURN(dlg && rdata && p_inv, PJ_EINVAL);
641
642 /* Dialog MUST have been initialised. */
643 PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata) != NULL, PJ_EINVALIDOP);
644
645 msg = rdata->msg_info.msg;
646
647 /* rdata MUST contain INVITE request */
648 PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG &&
649 msg->line.req.method.id == PJSIP_INVITE_METHOD,
650 PJ_EINVALIDOP);
651
652 /* Normalize options */
653 if (options & PJSIP_INV_REQUIRE_100REL)
654 options |= PJSIP_INV_SUPPORT_100REL;
655
656 if (options & PJSIP_INV_REQUIRE_TIMER)
657 options |= PJSIP_INV_SUPPORT_TIMER;
658
659 /* Create the session */
660 inv = pj_pool_zalloc(dlg->pool, sizeof(pjsip_inv_session));
661 PJ_ASSERT_RETURN(inv != NULL, PJ_ENOMEM);
662
663 inv->pool = dlg->pool;
664 inv->role = PJSIP_ROLE_UAS;
665 inv->state = PJSIP_INV_STATE_NULL;
666 inv->dlg = dlg;
667 inv->options = options;
668
669 /* Parse SDP in message body, if present. */
670 if (msg->body) {
671 pjsip_msg_body *body = msg->body;
672
673 /* Parse and validate SDP */
674 status = pjmedia_sdp_parse(inv->pool, body->data, body->len,
675 &rem_sdp);
676 if (status == PJ_SUCCESS)
677 status = pjmedia_sdp_validate(rem_sdp);
678
679 if (status != PJ_SUCCESS)
680 return status;
681 }
682
683 /* Create negotiator. */
684 if (rem_sdp) {
685 status = pjmedia_sdp_neg_create_w_remote_offer(inv->pool, local_sdp,
686 rem_sdp, &inv->neg);
687
688 } else if (local_sdp) {
689 status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, local_sdp,
690 &inv->neg);
691 } else {
692 pj_assert(!"UAS dialog without remote and local offer is not supported!");
693 PJ_TODO(IMPLEMENT_DELAYED_UAS_OFFER);
694 status = PJ_ENOTSUP;
695 }
696
697 if (status != PJ_SUCCESS)
698 return status;
699
700 /* Register invite as dialog usage. */
701 status = pjsip_dlg_add_usage(dlg, &mod_inv.mod, inv);
702 if (status != PJ_SUCCESS)
703 return status;
704
705 /* Increment session in the dialog. */
706 pjsip_dlg_inc_session(dlg);
707
708 /* Save the invite transaction. */
709 inv->invite_tsx = pjsip_rdata_get_tsx(rdata);
710 inv->invite_tsx->mod_data[mod_inv.mod.id] = inv;
711
712 /* Done */
713 *p_inv = inv;
714 return PJ_SUCCESS;
715}
716
717static void *clone_sdp(pj_pool_t *pool, const void *data, unsigned len)
718{
719 PJ_UNUSED_ARG(len);
720 return pjmedia_sdp_session_clone(pool, data);
721}
722
723static int print_sdp(pjsip_msg_body *body, char *buf, pj_size_t len)
724{
725 return pjmedia_sdp_print(body->data, buf, len);
726}
727
728static pjsip_msg_body *create_sdp_body(pj_pool_t *pool,
729 const pjmedia_sdp_session *c_sdp)
730{
731 pjsip_msg_body *body;
732
733
734 body = pj_pool_zalloc(pool, sizeof(pjsip_msg_body));
735 PJ_ASSERT_RETURN(body != NULL, NULL);
736
737 body->content_type.type = pj_str("application");
738 body->content_type.subtype = pj_str("sdp");
739 body->data = pjmedia_sdp_session_clone(pool, c_sdp);
740 body->len = 0;
741 body->clone_data = &clone_sdp;
742 body->print_body = &print_sdp;
743
744 return body;
745}
746
747/*
748 * Create initial INVITE request.
749 */
750PJ_DEF(pj_status_t) pjsip_inv_invite( pjsip_inv_session *inv,
751 pjsip_tx_data **p_tdata )
752{
753 pjsip_tx_data *tdata;
754 const pjsip_hdr *hdr;
755 pj_status_t status;
756
757 /* Verify arguments. */
758 PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
759
760 /* State MUST be NULL. */
761 PJ_ASSERT_RETURN(inv->state == PJSIP_INV_STATE_NULL, PJ_EINVAL);
762
763 /* Create the INVITE request. */
764 status = pjsip_dlg_create_request(inv->dlg, &pjsip_invite_method, -1,
765 &tdata);
766 if (status != PJ_SUCCESS)
767 return status;
768
769 /* Add SDP, if any. */
770 if (inv->neg &&
771 pjmedia_sdp_neg_get_state(inv->neg)==PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
772 {
773 const pjmedia_sdp_session *offer;
774
775 status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
776 if (status != PJ_SUCCESS)
777 return status;
778
779 tdata->msg->body = create_sdp_body(tdata->pool, offer);
780 }
781
782 /* Add Allow header. */
783 hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_ALLOW, NULL);
784 if (hdr) {
785 pjsip_msg_add_hdr(tdata->msg,
786 pjsip_hdr_shallow_clone(tdata->pool, hdr));
787 }
788
789 /* Add Supported header */
790 hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL);
791 if (hdr) {
792 pjsip_msg_add_hdr(tdata->msg,
793 pjsip_hdr_shallow_clone(tdata->pool, hdr));
794 }
795
796 /* Add Require header. */
797 PJ_TODO(INVITE_ADD_REQUIRE_HEADER);
798
799 /* Done. */
800 *p_tdata = tdata;
801
802 return PJ_SUCCESS;
803}
804
805
806/*
807 * Answer initial INVITE.
808 */
809PJ_DEF(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv,
810 int st_code,
811 const pj_str_t *st_text,
812 const pjmedia_sdp_session *local_sdp,
813 pjsip_tx_data **p_tdata )
814{
815 pjsip_tx_data *last_res;
816 pj_status_t status;
817
818 /* Verify arguments. */
819 PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
820
821 /* Must have INVITE transaction. */
822 PJ_ASSERT_RETURN(inv->invite_tsx, PJ_EBUG);
823
824 /* INVITE transaction MUST have transmitted a response (e.g. 100) */
825 PJ_ASSERT_RETURN(inv->invite_tsx->last_tx, PJ_EINVALIDOP);
826
827 /* If local_sdp is specified, then we MUST NOT have answered the
828 * offer before.
829 */
830 PJ_ASSERT_RETURN(!local_sdp ||
831 (pjmedia_sdp_neg_get_state(inv->neg)==PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER),
832 PJ_EINVALIDOP);
833 if (local_sdp) {
834 status = pjmedia_sdp_neg_set_local_answer(inv->pool, inv->neg,
835 local_sdp);
836 if (status != PJ_SUCCESS)
837 return status;
838 }
839
840 last_res = inv->invite_tsx->last_tx;
841
842 /* Modify last response. */
843 status = pjsip_dlg_modify_response(inv->dlg, last_res, st_code, st_text);
844 if (status != PJ_SUCCESS)
845 return status;
846
847 /* Include SDP for 18x and 2xx response. */
848 if (st_code/10 == 18 || st_code/10 == 20) {
849 pjmedia_sdp_session *local;
850
851 status = pjmedia_sdp_neg_get_neg_local(inv->neg, &local);
852 if (status == PJ_SUCCESS)
853 last_res->msg->body = create_sdp_body(last_res->pool, local);
854 ;
855 }
856
857 /* Do we need to increment tdata's reference counter? */
858 PJ_TODO(INV_ANSWER_MAY_HAVE_TO_INCREMENT_REF_COUNTER);
859
860 *p_tdata = last_res;
861
862 return PJ_SUCCESS;
863}
864
865
866/*
867 * End session.
868 */
869PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv,
870 int st_code,
871 const pj_str_t *st_text,
872 pjsip_tx_data **p_tdata )
873{
874 pjsip_tx_data *tdata;
875 pj_status_t status;
876
877 /* Verify arguments. */
878 PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
879
880 /* Create appropriate message. */
881 switch (inv->state) {
882 case PJSIP_INV_STATE_CALLING:
883 case PJSIP_INV_STATE_EARLY:
884 case PJSIP_INV_STATE_INCOMING:
885
886 if (inv->role == PJSIP_ROLE_UAC) {
887
888 /* For UAC when session has not been confirmed, create CANCEL. */
889
890 /* MUST have the original UAC INVITE transaction. */
891 PJ_ASSERT_RETURN(inv->invite_tsx != NULL, PJ_EBUG);
892
893 /* But CANCEL should only be called when we have received a
894 * provisional response. If we haven't received any responses,
895 * just destroy the transaction.
896 */
897 if (inv->invite_tsx->status_code < 100) {
898
899 pjsip_tsx_terminate(inv->invite_tsx, 487);
900
901 return PJSIP_ETSXDESTROYED;
902 }
903
904 /* The CSeq here assumes that the dialog is started with an
905 * INVITE session. This may not be correct; dialog can be
906 * started as SUBSCRIBE session.
907 * So fix this!
908 */
909 status = pjsip_endpt_create_cancel(inv->dlg->endpt,
910 inv->invite_tsx->last_tx,
911 &tdata);
912
913 } else {
914
915 /* For UAS, send a final response. */
916 tdata = inv->invite_tsx->last_tx;
917 PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP);
918
919 status = pjsip_dlg_modify_response(inv->dlg, tdata, st_code,
920 st_text);
921 }
922 break;
923
924 case PJSIP_INV_STATE_CONNECTING:
925 case PJSIP_INV_STATE_CONFIRMED:
926 /* For established dialog, send BYE */
927 status = pjsip_dlg_create_request(inv->dlg, &pjsip_bye_method, -1,
928 &tdata);
929 break;
930
931 case PJSIP_INV_STATE_DISCONNECTED:
Benny Prijono268ca612006-02-07 12:34:11 +0000932 /* No need to do anything. */
933 PJ_TODO(RETURN_A_PROPER_STATUS_CODE_HERE);
934 return PJ_EINVALIDOP;
935
936 default:
937 pj_assert("!Invalid operation!");
938 return PJ_EINVALIDOP;
939 }
940
941 if (status != PJ_SUCCESS)
942 return status;
943
944
945 /* Done */
946
947 *p_tdata = tdata;
948
949 return PJ_SUCCESS;
950}
951
952
953/*
954 * Create re-INVITE.
955 */
956PJ_DEF(pj_status_t) pjsip_inv_reinvite( pjsip_inv_session *inv,
957 const pj_str_t *new_contact,
958 const pjmedia_sdp_session *new_offer,
959 pjsip_tx_data **p_tdata )
960{
961 PJ_UNUSED_ARG(inv);
962 PJ_UNUSED_ARG(new_contact);
963 PJ_UNUSED_ARG(new_offer);
964 PJ_UNUSED_ARG(p_tdata);
965
966 PJ_TODO(CREATE_REINVITE_REQUEST);
967 return PJ_ENOTSUP;
968}
969
970/*
971 * Create UPDATE.
972 */
973PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv,
974 const pj_str_t *new_contact,
975 const pjmedia_sdp_session *new_offer,
976 pjsip_tx_data **p_tdata )
977{
978 PJ_UNUSED_ARG(inv);
979 PJ_UNUSED_ARG(new_contact);
980 PJ_UNUSED_ARG(new_offer);
981 PJ_UNUSED_ARG(p_tdata);
982
983 PJ_TODO(CREATE_UPDATE_REQUEST);
984 return PJ_ENOTSUP;
985}
986
987/*
988 * Send a request or response message.
989 */
990PJ_DEF(pj_status_t) pjsip_inv_send_msg( pjsip_inv_session *inv,
991 pjsip_tx_data *tdata,
992 void *token )
993{
994 pj_status_t status;
995
996 /* Verify arguments. */
997 PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL);
998
999 if (tdata->msg->type == PJSIP_REQUEST_MSG) {
1000 pjsip_transaction *tsx;
1001
1002 status = pjsip_dlg_send_request(inv->dlg, tdata, &tsx);
1003 if (status != PJ_SUCCESS)
1004 return status;
1005
1006 tsx->mod_data[mod_inv.mod.id] = inv;
1007 tsx->mod_data[mod_inv.app_user->id] = token;
1008
1009 } else {
1010 pjsip_cseq_hdr *cseq;
1011
1012 /* Can only do this to send response to original INVITE
1013 * request.
1014 */
1015 PJ_ASSERT_RETURN((cseq=pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL)) != NULL &&
1016 (cseq->cseq == inv->invite_tsx->cseq),
1017 PJ_EINVALIDOP);
1018
1019 status = pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata);
1020 if (status != PJ_SUCCESS)
1021 return status;
1022 }
1023
1024 /* Done (?) */
1025 return PJ_SUCCESS;
1026}
1027
1028
1029void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state,
1030 pjsip_event *e)
1031{
1032 inv->state = state;
1033 if (mod_inv.cb.on_state_changed)
1034 (*mod_inv.cb.on_state_changed)(inv, e);
1035
1036 if (inv->state == PJSIP_INV_STATE_DISCONNECTED)
1037 pjsip_dlg_dec_session(inv->dlg);
1038}
1039
1040static void inv_on_state_null( pjsip_inv_session *s, pjsip_event *e)
1041{
1042 pjsip_transaction *tsx = e->body.tsx_state.tsx;
1043 pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx);
1044
1045 PJ_ASSERT_ON_FAIL(tsx && dlg, return);
1046
1047 if (tsx->method.id == PJSIP_INVITE_METHOD) {
1048
1049 if (dlg->role == PJSIP_ROLE_UAC) {
1050
1051 /* Keep the initial INVITE transaction. */
1052 if (s->invite_tsx == NULL)
1053 s->invite_tsx = tsx;
1054
1055 switch (tsx->state) {
1056 case PJSIP_TSX_STATE_CALLING:
1057 inv_set_state(s, PJSIP_INV_STATE_CALLING, e);
1058 break;
1059 default:
1060 pj_assert(!"Unexpected state");
1061 break;
1062 }
1063
1064 } else {
1065 switch (tsx->state) {
1066 case PJSIP_TSX_STATE_TRYING:
1067 inv_set_state(s, PJSIP_INV_STATE_INCOMING, e);
1068 break;
1069 default:
1070 pj_assert(!"Unexpected state");
1071 }
1072 }
1073
1074 } else {
1075 pj_assert(!"Unexpected transaction type");
1076 }
1077}
1078
1079static void inv_on_state_calling( pjsip_inv_session *s, pjsip_event *e)
1080{
1081 pjsip_transaction *tsx = e->body.tsx_state.tsx;
1082 pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx);
Benny Prijonoccf95622006-02-07 18:48:01 +00001083 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +00001084
1085 PJ_ASSERT_ON_FAIL(tsx && dlg, return);
1086
Benny Prijonoccf95622006-02-07 18:48:01 +00001087 if (tsx == s->invite_tsx) {
Benny Prijono268ca612006-02-07 12:34:11 +00001088
1089 switch (tsx->state) {
1090
1091 case PJSIP_TSX_STATE_PROCEEDING:
1092 if (dlg->remote.info->tag.slen) {
1093 inv_set_state(s, PJSIP_INV_STATE_EARLY, e);
1094 } else {
1095 /* Ignore 100 (Trying) response, as it doesn't change
1096 * session state. It only ceases retransmissions.
1097 */
1098 }
1099 break;
1100
1101 case PJSIP_TSX_STATE_COMPLETED:
1102 if (tsx->status_code/100 == 2) {
1103
1104 /* This should not happen.
1105 * When transaction receives 2xx, it should be terminated
1106 */
1107 pj_assert(0);
1108 inv_set_state(s, PJSIP_INV_STATE_CONNECTING, e);
1109
Benny Prijonoccf95622006-02-07 18:48:01 +00001110 } else if (tsx->status_code==401 || tsx->status_code==407) {
1111
1112 /* Handle authentication failure:
1113 * Resend the request with Authorization header.
1114 */
1115 pjsip_tx_data *tdata;
1116
1117 status = pjsip_auth_clt_reinit_req(&s->dlg->auth_sess,
1118 e->body.tsx_state.src.rdata,
1119 tsx->last_tx,
1120 &tdata);
1121
1122 if (status != PJ_SUCCESS) {
1123
1124 /* Does not have proper credentials.
1125 * End the session.
1126 */
1127 inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
1128
1129 } else {
1130
1131 /* Restart session. */
1132 s->state = PJSIP_INV_STATE_NULL;
1133 s->invite_tsx = NULL;
1134
1135 /* Send the request. */
1136 status = pjsip_inv_send_msg(s, tdata, NULL );
1137 }
1138
Benny Prijono268ca612006-02-07 12:34:11 +00001139 } else {
Benny Prijonoccf95622006-02-07 18:48:01 +00001140
Benny Prijono268ca612006-02-07 12:34:11 +00001141 inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
Benny Prijonoccf95622006-02-07 18:48:01 +00001142
Benny Prijono268ca612006-02-07 12:34:11 +00001143 }
1144 break;
1145
1146 case PJSIP_TSX_STATE_TERMINATED:
1147 /* INVITE transaction can be terminated either because UAC
1148 * transaction received 2xx response or because of transport
1149 * error.
1150 */
1151 if (tsx->status_code/100 == 2) {
1152 /* This must be receipt of 2xx response */
1153
1154 /* Set state to CONNECTING */
1155 inv_set_state(s, PJSIP_INV_STATE_CONNECTING, e);
1156
1157 /* Send ACK */
1158 pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
1159
1160 send_ack(s, e->body.tsx_state.src.rdata);
1161
1162 } else {
1163 inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
Benny Prijono268ca612006-02-07 12:34:11 +00001164 }
1165 break;
1166
1167 default:
1168 pj_assert(!"Unexpected state");
1169 }
1170
1171 } else {
1172 pj_assert(!"Unexpected transaction type");
1173 }
1174}
1175
1176static void inv_on_state_incoming( pjsip_inv_session *s, pjsip_event *e)
1177{
1178 pjsip_transaction *tsx = e->body.tsx_state.tsx;
1179 pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx);
1180
1181 PJ_ASSERT_ON_FAIL(tsx && dlg, return);
1182
Benny Prijonoccf95622006-02-07 18:48:01 +00001183 if (tsx == s->invite_tsx) {
Benny Prijono268ca612006-02-07 12:34:11 +00001184 switch (tsx->state) {
1185 case PJSIP_TSX_STATE_PROCEEDING:
1186 if (tsx->status_code > 100)
1187 inv_set_state(s, PJSIP_INV_STATE_EARLY, e);
1188 break;
1189 case PJSIP_TSX_STATE_COMPLETED:
1190 if (tsx->status_code/100 == 2)
1191 inv_set_state(s, PJSIP_INV_STATE_CONNECTING, e);
1192 else
1193 inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
1194 break;
1195 case PJSIP_TSX_STATE_TERMINATED:
1196 /* This happens on transport error */
1197 inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
Benny Prijono268ca612006-02-07 12:34:11 +00001198 break;
1199 default:
1200 pj_assert(!"Unexpected state");
1201 }
1202 } else {
1203 pj_assert(!"Unexpected transaction type");
1204 }
1205}
1206
1207static void inv_on_state_early( pjsip_inv_session *s, pjsip_event *e)
1208{
1209 pjsip_transaction *tsx = e->body.tsx_state.tsx;
1210 pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx);
1211
1212 PJ_ASSERT_ON_FAIL(tsx && dlg, return);
1213
Benny Prijonoccf95622006-02-07 18:48:01 +00001214 if (tsx == s->invite_tsx) {
Benny Prijono268ca612006-02-07 12:34:11 +00001215
1216 switch (tsx->state) {
1217
1218 case PJSIP_TSX_STATE_PROCEEDING:
1219 /* Send/received another provisional response. */
1220 inv_set_state(s, PJSIP_INV_STATE_EARLY, e);
1221 break;
1222
1223 case PJSIP_TSX_STATE_COMPLETED:
1224 if (tsx->status_code/100 == 2)
1225 inv_set_state(s, PJSIP_INV_STATE_CONNECTING, e);
1226 else
1227 inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
1228 break;
1229
1230 case PJSIP_TSX_STATE_TERMINATED:
1231 /* INVITE transaction can be terminated either because UAC
1232 * transaction received 2xx response or because of transport
1233 * error.
1234 */
1235 if (tsx->status_code/100 == 2) {
1236
1237 /* This must be receipt of 2xx response */
1238
1239 /* Set state to CONNECTING */
1240 inv_set_state(s, PJSIP_INV_STATE_CONNECTING, e);
1241
1242 /* if UAC, send ACK and move state to confirmed. */
1243 if (tsx->role == PJSIP_ROLE_UAC) {
1244 pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
1245
1246 send_ack(s, e->body.tsx_state.src.rdata);
1247
1248 inv_set_state(s, PJSIP_INV_STATE_CONFIRMED, e);
1249 }
1250
1251 } else {
1252 inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
Benny Prijono268ca612006-02-07 12:34:11 +00001253 }
1254 break;
1255
1256 default:
1257 pj_assert(!"Unexpected state");
1258 }
1259
1260 } else if (tsx->method.id == PJSIP_CANCEL_METHOD) {
1261
1262 /* Handle incoming CANCEL request. */
1263 if (tsx->role == PJSIP_ROLE_UAS && s->role == PJSIP_ROLE_UAS &&
1264 e->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
1265 {
1266
1267 pj_status_t status;
1268 pjsip_tx_data *tdata;
1269
1270 /* Respond CANCEL with 200 (OK) */
1271 status = pjsip_dlg_create_response(dlg, e->body.tsx_state.src.rdata,
1272 200, NULL, &tdata);
1273 if (status == PJ_SUCCESS) {
1274 pjsip_dlg_send_response(dlg, tsx, tdata);
1275 }
1276
1277 /* Respond the original UAS transaction with 487 (Request
1278 * Terminated) response.
1279 */
1280 pj_assert(s->invite_tsx && s->invite_tsx->last_tx);
1281 tdata = s->invite_tsx->last_tx;
1282
1283 status = pjsip_dlg_modify_response(dlg, tdata, 487, NULL);
1284 if (status == PJ_SUCCESS) {
1285 pjsip_dlg_send_response(dlg, s->invite_tsx, tdata);
1286 }
1287 }
1288
Benny Prijonoccf95622006-02-07 18:48:01 +00001289 } else if (tsx->method.id == PJSIP_INVITE_METHOD) {
1290
1291 /* Ignore previously failed INVITE transaction event
1292 * (e.g. when rejected with 401/407)
1293 */
1294
Benny Prijono268ca612006-02-07 12:34:11 +00001295 } else {
1296 pj_assert(!"Unexpected transaction type");
1297 }
1298}
1299
1300static void inv_on_state_connecting( pjsip_inv_session *s, pjsip_event *e)
1301{
1302 pjsip_transaction *tsx = e->body.tsx_state.tsx;
1303 pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx);
1304
1305 PJ_ASSERT_ON_FAIL(tsx && dlg, return);
1306
Benny Prijonoccf95622006-02-07 18:48:01 +00001307 if (tsx == s->invite_tsx) {
Benny Prijono268ca612006-02-07 12:34:11 +00001308
1309 switch (tsx->state) {
1310
1311 case PJSIP_TSX_STATE_CONFIRMED:
1312 break;
1313
1314 case PJSIP_TSX_STATE_TERMINATED:
1315 /* INVITE transaction can be terminated either because UAC
1316 * transaction received 2xx response or because of transport
1317 * error.
1318 */
1319 if (tsx->status_code/100 != 2) {
1320 inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
Benny Prijono268ca612006-02-07 12:34:11 +00001321 }
1322 break;
1323
1324 case PJSIP_TSX_STATE_DESTROYED:
1325 /* Do nothing. */
1326 break;
1327
1328 default:
1329 pj_assert(!"Unexpected state");
1330 }
1331
1332 } else {
1333 pj_assert(!"Unexpected transaction type");
1334 }
1335}
1336
1337static void inv_on_state_confirmed( pjsip_inv_session *s, pjsip_event *e)
1338{
1339 pjsip_transaction *tsx = e->body.tsx_state.tsx;
1340 pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx);
1341
1342 PJ_ASSERT_ON_FAIL(tsx && dlg, return);
1343
1344 if (tsx->method.id == PJSIP_BYE_METHOD) {
1345 inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e);
1346
Benny Prijonoccf95622006-02-07 18:48:01 +00001347 } else if (tsx == s->invite_tsx) {
Benny Prijono268ca612006-02-07 12:34:11 +00001348
1349 switch (tsx->state) {
1350 case PJSIP_TSX_STATE_TERMINATED:
1351 case PJSIP_TSX_STATE_DESTROYED:
1352 break;
1353 default:
1354 pj_assert(!"Unexpected state");
1355 break;
1356 }
1357
Benny Prijonoccf95622006-02-07 18:48:01 +00001358 } else if (tsx->method.id == PJSIP_INVITE_METHOD) {
1359
1360 /* Re-INVITE */
Benny Prijono268ca612006-02-07 12:34:11 +00001361
1362 } else {
1363 pj_assert(!"Unexpected transaction type");
1364 }
1365}
1366
1367static void inv_on_state_disconnected( pjsip_inv_session *s, pjsip_event *e)
1368{
1369 PJ_UNUSED_ARG(s);
1370 PJ_UNUSED_ARG(e);
1371}
1372
1373static void inv_on_state_terminated( pjsip_inv_session *s, pjsip_event *e)
1374{
1375 PJ_UNUSED_ARG(s);
1376 PJ_UNUSED_ARG(e);
1377}
1378