blob: bfd714b1f702b8117e2e3dce9c34db53caa11aea [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +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 "pjsua.h"
20#include <pj/log.h>
21
22
23/*
24 * pjsua_inv.c
25 *
26 * Invite session specific functionalities.
27 */
28
29#define THIS_FILE "pjsua_inv.c"
30
31
32/**
33 * Make outgoing call.
34 */
35pj_status_t pjsua_invite(const char *cstr_dest_uri,
36 pjsip_inv_session **p_inv)
37{
38 pj_str_t dest_uri;
39 pjsip_dialog *dlg;
40 pjmedia_sdp_session *offer;
41 pjsip_inv_session *inv;
42 struct pjsua_inv_data *inv_data;
43 pjsip_tx_data *tdata;
44 pj_status_t status;
45
46 /* Convert cstr_dest_uri to dest_uri */
47
48 dest_uri = pj_str((char*)cstr_dest_uri);
49
50 /* Create outgoing dialog: */
51
52 status = pjsip_dlg_create_uac( pjsip_ua_instance(), &pjsua.local_uri,
53 &pjsua.contact_uri, &dest_uri, &dest_uri,
54 &dlg);
55 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +000056 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +000057 return status;
58 }
59
60 /* Get media capability from media endpoint: */
61
62 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool,
63 1, &pjsua.med_skinfo, &offer);
64 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +000065 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +000066 goto on_error;
67 }
68
69 /* Create the INVITE session: */
70
71 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
72 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +000073 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +000074 goto on_error;
75 }
76
77
78 /* Create and associate our data in the session. */
79
80 inv_data = pj_pool_zalloc( dlg->pool, sizeof(struct pjsua_inv_data));
Benny Prijono632ce712006-02-09 14:01:40 +000081 inv_data->inv = inv;
Benny Prijono84126ab2006-02-09 09:30:09 +000082 dlg->mod_data[pjsua.mod.id] = inv_data;
Benny Prijono834aee32006-02-19 01:38:06 +000083 inv->mod_data[pjsua.mod.id] = inv_data;
Benny Prijono84126ab2006-02-09 09:30:09 +000084
85
86 /* Set dialog Route-Set: */
87
88 if (!pj_list_empty(&pjsua.route_set))
89 pjsip_dlg_set_route_set(dlg, &pjsua.route_set);
90
91
92 /* Set credentials: */
93
94 pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count,
95 pjsua.cred_info);
96
97
98 /* Create initial INVITE: */
99
100 status = pjsip_inv_invite(inv, &tdata);
101 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000102 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
103 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000104 goto on_error;
105 }
106
107
108 /* Send initial INVITE: */
109
110 status = pjsip_inv_send_msg(inv, tdata, NULL);
111 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000112 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
113 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000114 goto on_error;
115 }
116
Benny Prijono632ce712006-02-09 14:01:40 +0000117 /* Add invite session to the list. */
118
119 pj_list_push_back(&pjsua.inv_list, inv_data);
120
Benny Prijono84126ab2006-02-09 09:30:09 +0000121
122 /* Done. */
123
124 *p_inv = inv;
125
126 return PJ_SUCCESS;
127
128
129on_error:
130
131 PJ_TODO(DESTROY_DIALOG_ON_FAIL);
132 return status;
133}
134
135
136/**
137 * Handle incoming INVITE request.
138 */
139pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
140{
141 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
142 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
143 pjsip_msg *msg = rdata->msg_info.msg;
144
145 /*
146 * Handle incoming INVITE outside dialog.
147 */
148 if (dlg == NULL && tsx == NULL &&
149 msg->line.req.method.id == PJSIP_INVITE_METHOD)
150 {
151 pj_status_t status;
152 pjsip_tx_data *response = NULL;
153 unsigned options = 0;
154
155 /* Verify that we can handle the request. */
156 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
157 pjsua.endpt, &response);
158 if (status != PJ_SUCCESS) {
159
160 /*
161 * No we can't handle the incoming INVITE request.
162 */
163
164 if (response) {
165 pjsip_response_addr res_addr;
166
167 pjsip_get_response_addr(response->pool, rdata, &res_addr);
168 pjsip_endpt_send_response(pjsua.endpt, &res_addr, response,
169 NULL, NULL);
170
171 } else {
172
173 /* Respond with 500 (Internal Server Error) */
174 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
175 NULL, NULL);
176 }
177
178 } else {
179 /*
180 * Yes we can handle the incoming INVITE request.
181 */
182 pjsip_inv_session *inv;
183 struct pjsua_inv_data *inv_data;
184 pjmedia_sdp_session *answer;
185
186
187 /* Get media capability from media endpoint: */
188
189 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool,
190 1, &pjsua.med_skinfo, &answer );
191 if (status != PJ_SUCCESS) {
192
193 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
194 NULL, NULL);
195 return PJ_TRUE;
196 }
197
198 /* Create dialog: */
199
200 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
201 &pjsua.contact_uri, &dlg);
202 if (status != PJ_SUCCESS)
203 return PJ_TRUE;
204
205
206 /* Create invite session: */
207
208 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
209 if (status != PJ_SUCCESS) {
210
211 status = pjsip_dlg_create_response( dlg, rdata, 500, NULL,
212 &response);
213 if (status == PJ_SUCCESS)
214 status = pjsip_dlg_send_response(dlg,
215 pjsip_rdata_get_tsx(rdata),
216 response);
217 return PJ_TRUE;
218
219 }
220
221
222 /* Create and attach pjsua data to the dialog: */
223
224 inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data));
Benny Prijono632ce712006-02-09 14:01:40 +0000225 inv_data->inv = inv;
Benny Prijono84126ab2006-02-09 09:30:09 +0000226 dlg->mod_data[pjsua.mod.id] = inv_data;
Benny Prijono834aee32006-02-19 01:38:06 +0000227 inv->mod_data[pjsua.mod.id] = inv_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000228
Benny Prijono632ce712006-02-09 14:01:40 +0000229 pj_list_push_back(&pjsua.inv_list, inv_data);
230
Benny Prijono84126ab2006-02-09 09:30:09 +0000231
232 /* Answer with 100 (using the dialog, not invite): */
233
234 status = pjsip_dlg_create_response(dlg, rdata, 100, NULL, &response);
235 if (status == PJ_SUCCESS)
236 status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), response);
237 }
238
239 /* This INVITE request has been handled. */
240 return PJ_TRUE;
241 }
242
243 return PJ_FALSE;
244}
245
246
247/*
248 * This callback receives notification from invite session when the
249 * session state has changed.
250 */
251void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
252{
253
254 /* Destroy media session when invite session is disconnected. */
255 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
256 struct pjsua_inv_data *inv_data;
257
258 inv_data = inv->dlg->mod_data[pjsua.mod.id];
Benny Prijono632ce712006-02-09 14:01:40 +0000259
260 pj_assert(inv_data != NULL);
261
Benny Prijono84126ab2006-02-09 09:30:09 +0000262 if (inv_data && inv_data->session) {
263 pjmedia_session_destroy(inv_data->session);
264 inv_data->session = NULL;
265
266 PJ_LOG(3,(THIS_FILE,"Media session is destroyed"));
267 }
268
Benny Prijono632ce712006-02-09 14:01:40 +0000269 if (inv_data) {
270
271 pj_list_erase(inv_data);
272
273 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000274 }
275
276 pjsua_ui_inv_on_state_changed(inv, e);
277}
278
279
280/*
281 * This callback is called by invite session framework when UAC session
282 * has forked.
283 */
284void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
285{
286 PJ_UNUSED_ARG(inv);
287 PJ_UNUSED_ARG(e);
288
289 PJ_TODO(HANDLE_FORKED_DIALOG);
290}
291
292
293/*
294 * Callback to be called when SDP offer/answer negotiation has just completed
295 * in the session. This function will start/update media if negotiation
296 * has succeeded.
297 */
298void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
299{
300 struct pjsua_inv_data *inv_data;
301 const pjmedia_sdp_session *local_sdp;
302 const pjmedia_sdp_session *remote_sdp;
303
304 if (status != PJ_SUCCESS) {
305
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000306 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000307 return;
308
309 }
310
311 /* Destroy existing media session, if any. */
312
313 inv_data = inv->dlg->mod_data[pjsua.mod.id];
314 if (inv_data && inv_data->session) {
315 pjmedia_session_destroy(inv_data->session);
316 inv_data->session = NULL;
317 }
318
319 /* Get local and remote SDP */
320
321 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
322 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000323 pjsua_perror(THIS_FILE,
324 "Unable to retrieve currently active local SDP",
325 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000326 return;
327 }
328
329
330 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
331 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000332 pjsua_perror(THIS_FILE,
333 "Unable to retrieve currently active remote SDP",
334 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000335 return;
336 }
337
338
339 /* Create new media session.
340 * The media session is active immediately.
341 */
342
343 if (!pjsua.null_audio) {
344
345 status = pjmedia_session_create( pjsua.med_endpt, 1, &pjsua.med_skinfo,
346 local_sdp, remote_sdp,
347 &inv_data->session );
348 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000349 pjsua_perror(THIS_FILE, "Unable to create media session",
350 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000351 return;
352 }
353
354 PJ_LOG(3,(THIS_FILE,"Media has been started successfully"));
355 }
356}
Benny Prijono834aee32006-02-19 01:38:06 +0000357
358
359/*
360 * Terminate all calls.
361 */
362void pjsua_inv_shutdown()
363{
364 struct pjsua_inv_data *inv_data, *next;
365
366 inv_data = pjsua.inv_list.next;
367 while (inv_data != &pjsua.inv_list) {
368 pjsip_tx_data *tdata;
369
370 next = inv_data->next;
371
372 if (pjsip_inv_end_session(inv_data->inv, 410, NULL, &tdata)==0)
373 pjsip_inv_send_msg(inv_data->inv, tdata, NULL);
374
375 inv_data = next;
376 }
377}
378