File: | nua/nua_session.c |
Warning: | line 4405, column 21 Dereference of null pointer |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * This file is part of the Sofia-SIP package | |||
3 | * | |||
4 | * Copyright (C) 2006 Nokia Corporation. | |||
5 | * | |||
6 | * Contact: Pekka Pessi <pekka.pessi@nokia.com> | |||
7 | * | |||
8 | * This library is free software; you can redistribute it and/or | |||
9 | * modify it under the terms of the GNU Lesser General Public License | |||
10 | * as published by the Free Software Foundation; either version 2.1 of | |||
11 | * the License, or (at your option) any later version. | |||
12 | * | |||
13 | * This library is distributed in the hope that it will be useful, but | |||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
16 | * Lesser General Public License for more details. | |||
17 | * | |||
18 | * You should have received a copy of the GNU Lesser General Public | |||
19 | * License along with this library; if not, write to the Free Software | |||
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||
21 | * 02110-1301 USA | |||
22 | * | |||
23 | */ | |||
24 | ||||
25 | /**@CFILE nua_session.c | |||
26 | * @brief SIP session handling | |||
27 | * | |||
28 | * @author Pekka Pessi <Pekka.Pessi@nokia.com> | |||
29 | * | |||
30 | * @date Created: Wed Mar 8 16:17:27 EET 2006 ppessi | |||
31 | */ | |||
32 | ||||
33 | #include "config.h" | |||
34 | ||||
35 | #include <stddef.h> | |||
36 | #include <stdlib.h> | |||
37 | #include <string.h> | |||
38 | #include <limits.h> | |||
39 | ||||
40 | #include <assert.h> | |||
41 | ||||
42 | #include <sofia-sip/su_string.h> | |||
43 | #include <sofia-sip/sip_protos.h> | |||
44 | #include <sofia-sip/sip_status.h> | |||
45 | #include <sofia-sip/sip_util.h> | |||
46 | #include <sofia-sip/su_uniqueid.h> | |||
47 | #include <sofia-sip/msg_mime_protos.h> | |||
48 | ||||
49 | #define NTA_INCOMING_MAGIC_Tstruct nua_server_request struct nua_server_request | |||
50 | #define NTA_OUTGOING_MAGIC_Tstruct nua_client_request struct nua_client_request | |||
51 | #define NTA_RELIABLE_MAGIC_Tstruct nua_server_request struct nua_server_request | |||
52 | ||||
53 | #include "nua_stack.h" | |||
54 | #include <sofia-sip/soa.h> | |||
55 | ||||
56 | #ifndef SDP_H | |||
57 | typedef struct sdp_session_s sdp_session_t; | |||
58 | #endif | |||
59 | ||||
60 | /* ---------------------------------------------------------------------- */ | |||
61 | ||||
62 | /** @enum nua_callstate | |||
63 | ||||
64 | The states for SIP session established with INVITE. | |||
65 | ||||
66 | Initially the call states follow the state of the INVITE transaction. If the | |||
67 | initial INVITE transaction fails, the call is terminated. The status codes | |||
68 | 401 and 407 are an exception: if the client (on the left side in the diagram | |||
69 | below) receives them, it enters in #nua_callstate_authenticating state. | |||
70 | ||||
71 | If a re-INVITE transaction fails, the result depends on the status code in | |||
72 | failure. The call can return to the ready state, be terminated immediately, | |||
73 | or be terminated gracefully. The proper action to take is determined with | |||
74 | sip_response_terminates_dialog(). | |||
75 | ||||
76 | @sa @ref nua_call_model, #nua_i_state, nua_invite(), #nua_i_invite | |||
77 | ||||
78 | @par Session State Diagram | |||
79 | ||||
80 | @code | |||
81 | +----------+ | |||
82 | | |---------------------+ | |||
83 | | Init | | | |||
84 | | |----------+ | | |||
85 | +----------+ | | | |||
86 | | | | | | |||
87 | --/INVITE| |INVITE/100 | | | |||
88 | V V | | | |||
89 | +----------+ +----------+ | | | |||
90 | +--------| | | | | | | |||
91 | | 18X +-| Calling | | Received | |INVITE/ | | |||
92 | | /- | | | | | | /18X | | |||
93 | | V +----------+ +----------+ V | | |||
94 | | +----------+ | | | +----------+ | | |||
95 | |---| | |2XX -/ | -/ | | | | | |||
96 | | | Proceed- | | /- 2XX| 18X| | Early | |INVITE/ | |||
97 | | | ing | | | +->| | | /200 | |||
98 | | +----------+ V V +----------+ | | |||
99 | | | +----------+ +----------+ | -/ | | |||
100 | | 2XX| | | | |<--+ 2XX | | |||
101 | | /-| | Complet- | | Complete |<-----------+ | |||
102 | | +->| ing | | |------+ | |||
103 | | +----------+ +----------+ | | |||
104 | | | | | | | |||
105 | |401,407/ -/ACK| |ACK/- |timeout/ | | |||
106 | | /ACK V V | /BYE | | |||
107 | | +----------+ | | | |||
108 | | | | | | | |||
109 | | +--| Ready | | | | |||
110 | | | | | | | | |||
111 | | | +----------+ | | | |||
112 | | | | | | | |||
113 | | BYE/ | |-/BYE | |BYE/ | |||
114 | V /200 | V | |/200 | |||
115 | +----------+ | +----------+ | | | |||
116 | | | | | | | | | |||
117 | |Authentic-| | | Terminat-|<----+ | | |||
118 | | ating | | | ing | | | |||
119 | +----------+ | +----------+ | | |||
120 | | | | | |||
121 | | |[23456]XX/- | | |||
122 | | V | | |||
123 | | +----------+ | | |||
124 | | | | | | |||
125 | +->|Terminated|<--------------+ | |||
126 | | | | |||
127 | +----------+ | |||
128 | | | |||
129 | V | |||
130 | +----------+ | |||
131 | | | | |||
132 | | Init | | |||
133 | | | | |||
134 | +----------+ | |||
135 | @endcode | |||
136 | */ | |||
137 | ||||
138 | /* ---------------------------------------------------------------------- */ | |||
139 | /* Session event usage */ | |||
140 | ||||
141 | /** @internal @brief Session-related state. */ | |||
142 | typedef struct nua_session_usage | |||
143 | { | |||
144 | enum nua_callstate ss_state; /**< Session status (enum nua_callstate) */ | |||
145 | ||||
146 | unsigned ss_100rel:1; /**< Use 100rel, send 183 */ | |||
147 | unsigned ss_alerting:1; /**< 180 is sent/received */ | |||
148 | ||||
149 | unsigned ss_update_needed:2; /**< Send an UPDATE (do O/A if > 1) */ | |||
150 | ||||
151 | unsigned ss_precondition:1; /**< Precondition required */ | |||
152 | ||||
153 | unsigned ss_reporting:1; /**< True if reporting state */ | |||
154 | unsigned : 0; | |||
155 | ||||
156 | struct session_timer { | |||
157 | unsigned interval; /**< Negotiated expiration time */ | |||
158 | enum nua_session_refresher refresher; /**< Our Negotiated role */ | |||
159 | ||||
160 | struct { | |||
161 | unsigned expires, defaults; /**< Value of Session-Expires (delta) */ | |||
162 | unsigned min_se; /**< Minimum session expires */ | |||
163 | /** none, local or remote */ | |||
164 | enum nua_session_refresher refresher; | |||
165 | unsigned supported:1, require:1, :0; | |||
166 | } local, remote; | |||
167 | ||||
168 | unsigned timer_set:1; /**< We have active session timer. */ | |||
169 | } ss_timer[1]; | |||
170 | ||||
171 | char const *ss_reason; /**< Reason for termination. */ | |||
172 | ||||
173 | /* Offer-Answer status */ | |||
174 | char const *ss_oa_recv, *ss_oa_sent; | |||
175 | ||||
176 | /**< Version of user SDP from latest successful O/A */ | |||
177 | int ss_sdp_version; | |||
178 | } nua_session_usage_t; | |||
179 | ||||
180 | static char const Offer[] = "offer", Answer[] = "answer"; | |||
181 | ||||
182 | static char const *nua_session_usage_name(nua_dialog_usage_t const *du); | |||
183 | static int nua_session_usage_add(nua_handle_t *nh, | |||
184 | nua_dialog_state_t *ds, | |||
185 | nua_dialog_usage_t *du); | |||
186 | static void nua_session_usage_remove(nua_handle_t *nh, | |||
187 | nua_dialog_state_t *ds, | |||
188 | nua_dialog_usage_t *du, | |||
189 | nua_client_request_t *cr, | |||
190 | nua_server_request_t *sr); | |||
191 | static void nua_session_usage_refresh(nua_owner_t *, | |||
192 | nua_dialog_state_t *, | |||
193 | nua_dialog_usage_t *, | |||
194 | sip_time_t now); | |||
195 | static int nua_session_usage_shutdown(nua_owner_t *, | |||
196 | nua_dialog_state_t *, | |||
197 | nua_dialog_usage_t *); | |||
198 | ||||
199 | static void signal_call_state_change(nua_handle_t *nh, | |||
200 | nua_session_usage_t *ss, | |||
201 | int status, char const *phrase, | |||
202 | enum nua_callstate next_state); | |||
203 | ||||
204 | static int nua_invite_client_should_ack(nua_client_request_t const *cr); | |||
205 | static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags); | |||
206 | static int nua_invite_client_complete(nua_client_request_t *cr); | |||
207 | ||||
208 | static nua_usage_class const nua_session_usage[1] = { | |||
209 | { | |||
210 | sizeof (nua_session_usage_t), | |||
211 | sizeof nua_session_usage, | |||
212 | nua_session_usage_add, | |||
213 | nua_session_usage_remove, | |||
214 | nua_session_usage_name, | |||
215 | nua_base_usage_update_params, | |||
216 | NULL((void*)0), | |||
217 | nua_session_usage_refresh, | |||
218 | nua_session_usage_shutdown | |||
219 | }}; | |||
220 | ||||
221 | static char const *nua_session_usage_name(nua_dialog_usage_t const *du) | |||
222 | { | |||
223 | return "session"; | |||
224 | } | |||
225 | ||||
226 | static | |||
227 | int nua_session_usage_add(nua_handle_t *nh, | |||
228 | nua_dialog_state_t *ds, | |||
229 | nua_dialog_usage_t *du) | |||
230 | { | |||
231 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||
232 | ||||
233 | if (ds->ds_has_session) | |||
234 | return -1; | |||
235 | ds->ds_has_session = 1; | |||
236 | ds->ds_got_session = 1; | |||
237 | ||||
238 | ss->ss_timer->local.refresher = nua_any_refresher; | |||
239 | ss->ss_timer->remote.refresher = nua_any_refresher; | |||
240 | ||||
241 | return 0; | |||
242 | } | |||
243 | ||||
244 | static | |||
245 | void nua_session_usage_remove(nua_handle_t *nh, | |||
246 | nua_dialog_state_t *ds, | |||
247 | nua_dialog_usage_t *du, | |||
248 | nua_client_request_t *cr0, | |||
249 | nua_server_request_t *sr0) | |||
250 | { | |||
251 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||
252 | nua_client_request_t *cr, *cr_next; | |||
253 | nua_server_request_t *sr; | |||
254 | ||||
255 | /* Destroy queued INVITE transactions */ | |||
256 | for (cr = ds->ds_cr; cr; cr = cr_next) { | |||
257 | cr_next = cr->cr_next; | |||
258 | ||||
259 | if (cr->cr_method != sip_method_invite) | |||
260 | continue; | |||
261 | ||||
262 | if (cr == cr0) | |||
263 | continue; | |||
264 | ||||
265 | nua_client_request_ref(cr); | |||
266 | ||||
267 | if (nua_invite_client_should_ack(cr)) { | |||
268 | ss->ss_reporting = 1; | |||
269 | nua_invite_client_ack(cr, NULL((void*)0)); | |||
270 | ss->ss_reporting = 0; | |||
271 | } | |||
272 | ||||
273 | if (cr == du->du_cr && cr->cr_orq) { | |||
274 | nua_client_request_unref(cr); | |||
275 | continue; | |||
276 | } | |||
277 | ||||
278 | if (cr->cr_status < 200) { | |||
279 | nua_stack_event(nh->nh_nua, nh, | |||
280 | NULL((void*)0), | |||
281 | (enum nua_event_e)cr->cr_event, | |||
282 | SIP_481_NO_TRANSACTION481, sip_481_No_transaction, | |||
283 | NULL((void*)0)); | |||
284 | } | |||
285 | ||||
286 | nua_client_request_remove(cr); | |||
287 | ||||
288 | nua_client_request_unref(cr); | |||
289 | ||||
290 | cr_next = ds->ds_cr; | |||
291 | } | |||
292 | ||||
293 | if (ss->ss_state != nua_callstate_terminated && | |||
294 | ss->ss_state != nua_callstate_init && | |||
295 | !ss->ss_reporting) { | |||
296 | int status = 0; char const *phrase = "Terminated"; | |||
297 | ||||
298 | if (cr0) | |||
299 | status = cr0->cr_status, phrase = cr0->cr_phrase ? cr0->cr_phrase : phrase; | |||
300 | else if (sr0) | |||
301 | status = sr0->sr_status, phrase = sr0->sr_phrase; | |||
302 | ||||
303 | signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminated); | |||
304 | } | |||
305 | ||||
306 | /* Application can respond to BYE after the session usage has terminated */ | |||
307 | for (sr = ds->ds_sr; sr; sr = sr->sr_next) { | |||
308 | if (sr->sr_usage == du && sr->sr_method == sip_method_bye) | |||
309 | sr->sr_usage = NULL((void*)0); | |||
310 | } | |||
311 | ||||
312 | ds->ds_has_session = 0; | |||
313 | nh->nh_has_invite = 0; | |||
314 | nh->nh_active_call = 0; | |||
315 | nh->nh_hold_remote = 0; | |||
316 | ||||
317 | if (nh->nh_soa) | |||
318 | soa_destroy(nh->nh_soa), nh->nh_soa = NULL((void*)0); | |||
319 | } | |||
320 | ||||
321 | static | |||
322 | nua_dialog_usage_t *nua_dialog_usage_for_session(nua_dialog_state_t const *ds) | |||
323 | { | |||
324 | if (ds == ((nua_handle_t *)NULL((void*)0))->nh_ds) | |||
325 | return NULL((void*)0); | |||
326 | ||||
327 | return nua_dialog_usage_get(ds, nua_session_usage, NULL((void*)0)); | |||
328 | } | |||
329 | ||||
330 | static | |||
331 | nua_session_usage_t *nua_session_usage_for_dialog(nua_dialog_state_t const *ds) | |||
332 | { | |||
333 | nua_dialog_usage_t *du; | |||
334 | ||||
335 | if (ds == ((nua_handle_t *)NULL((void*)0))->nh_ds) | |||
336 | return NULL((void*)0); | |||
337 | ||||
338 | du = nua_dialog_usage_get(ds, nua_session_usage, NULL((void*)0)); | |||
339 | ||||
340 | return (nua_session_usage_t *)nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
341 | } | |||
342 | ||||
343 | /** Zap the session associated with the handle */ | |||
344 | static | |||
345 | void nua_session_usage_destroy(nua_handle_t *nh, | |||
346 | nua_session_usage_t *ss) | |||
347 | { | |||
348 | /* Remove usage */ | |||
349 | nua_dialog_usage_remove(nh, nh->nh_ds, nua_dialog_usage_public(ss)((ss) ? (nua_dialog_usage_t*)(ss) - 1 : ((void*)0)), NULL((void*)0), NULL((void*)0)); | |||
350 | ||||
351 | SU_DEBUG_5(("nua: terminated session %p\n", (void *)nh))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 351, "nua: terminated session %p\n" , (void *)nh)) : (void)0); | |||
352 | } | |||
353 | ||||
354 | /* ======================================================================== */ | |||
355 | /* INVITE and call (session) processing */ | |||
356 | ||||
357 | static int session_timer_is_supported(struct session_timer const *t); | |||
358 | ||||
359 | static void session_timer_preferences(struct session_timer *t, | |||
360 | sip_t const *sip, | |||
361 | sip_supported_t const *supported, | |||
362 | unsigned expires, int isset, | |||
363 | enum nua_session_refresher refresher, | |||
364 | unsigned min_se); | |||
365 | ||||
366 | static void session_timer_store(struct session_timer *t, | |||
367 | sip_t const *sip); | |||
368 | ||||
369 | static int session_timer_check_min_se(msg_t *msg, sip_t *sip, | |||
370 | sip_t const *request, | |||
371 | unsigned long min_se); | |||
372 | ||||
373 | static int session_timer_add_headers(struct session_timer *t, | |||
374 | int initial, | |||
375 | msg_t *msg, sip_t *sip, | |||
376 | nua_handle_t *nh); | |||
377 | ||||
378 | static void session_timer_negotiate(struct session_timer *t, int uas); | |||
379 | ||||
380 | static void session_timer_set(nua_session_usage_t *ss, int uas); | |||
381 | ||||
382 | static int session_timer_check_restart(nua_client_request_t *cr, | |||
383 | int status, char const *phrase, | |||
384 | sip_t const *sip); | |||
385 | ||||
386 | static int nh_referral_check(nua_handle_t *nh, tagi_t const *tags); | |||
387 | static void nh_referral_respond(nua_handle_t *, | |||
388 | int status, char const *phrase); | |||
389 | ||||
390 | static | |||
391 | int session_get_description(sip_t const *sip, | |||
392 | char const **return_sdp, | |||
393 | size_t *return_len); | |||
394 | ||||
395 | static | |||
396 | int session_include_description(soa_session_t *soa, | |||
397 | int session, | |||
398 | msg_t *msg, | |||
399 | sip_t *sip); | |||
400 | ||||
401 | static | |||
402 | int session_make_description(su_home_t *home, | |||
403 | soa_session_t *soa, | |||
404 | int session, | |||
405 | sip_content_disposition_t **return_cd, | |||
406 | sip_content_type_t **return_ct, | |||
407 | sip_payload_t **return_pl); | |||
408 | ||||
409 | static | |||
410 | int nua_server_retry_after(nua_server_request_t *sr, | |||
411 | int status, char const *phrase, | |||
412 | int min, int max); | |||
413 | ||||
414 | /**@fn void nua_invite(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||
415 | * | |||
416 | * Place a call using SIP @b INVITE method. | |||
417 | * | |||
418 | * The INVITE method is used to initiate a call between two parties. The | |||
419 | * call is also known as <i>SIP session</i>. | |||
420 | * | |||
421 | * At SIP level the session is represented as @e Dialog, which is a | |||
422 | * peer-to-peer association between two SIP User-Agents. The dialog is | |||
423 | * established by a successful 2XX response to the INVITE. The dialog is | |||
424 | * terminated by BYE transaction, which application can initiate with | |||
425 | * nua_bye() call. | |||
426 | * | |||
427 | * An @e early @e dialog is established by an preliminary response | |||
428 | * (101..199), such as <i>180 Ringing</i>. An early dialog is terminated | |||
429 | * with an error response with response code in range 300...699. | |||
430 | * | |||
431 | * The media session belonging to the SIP session is usually represented by | |||
432 | * SDP, Session Description Protocol. The media session it is usually | |||
433 | * established during the call set-up with procedure known as SDP | |||
434 | * Offer/Answer exchange, defined by @RFC3264. See <b>Media Session | |||
435 | * Handling</b> below for details. | |||
436 | * | |||
437 | * @param nh Pointer to operation handle | |||
438 | * @param tag, value, ... List of tagged parameters | |||
439 | * | |||
440 | * @return | |||
441 | * nothing | |||
442 | * | |||
443 | * @par Events: | |||
444 | * #nua_r_invite \n | |||
445 | * #nua_i_state (#nua_i_active, #nua_i_terminated) \n | |||
446 | * #nua_i_media_error \n | |||
447 | * #nua_i_fork \n | |||
448 | * | |||
449 | * @par Tags: | |||
450 | * NUTAG_AUTH_CACHE() \n | |||
451 | * NUTAG_AUTOACK() \n | |||
452 | * NUTAG_AUTOANSWER() \n | |||
453 | * NUTAG_EARLY_MEDIA() \n | |||
454 | * NUTAG_ENABLEINVITE() \n | |||
455 | * NUTAG_INITIAL_ROUTE(), NUTAG_INITIAL_ROUTE_STR() \n | |||
456 | * NUTAG_INVITE_TIMER() \n | |||
457 | * NUTAG_MEDIA_ENABLE() \n | |||
458 | * NUTAG_MEDIA_FEATURES() \n | |||
459 | * NUTAG_MIN_SE() \n | |||
460 | * NUTAG_RETRY_COUNT() \n | |||
461 | * NUTAG_SERVICE_ROUTE_ENABLE() \n | |||
462 | * NUTAG_SESSION_REFRESHER() \n | |||
463 | * NUTAG_SESSION_TIMER() \n | |||
464 | * NUTAG_SOA_NAME() \n | |||
465 | * NUTAG_UPDATE_REFRESH() \n | |||
466 | * | |||
467 | * @par Populating SIP Request Message with Tagged Arguments | |||
468 | * The tagged arguments can be used to pass values for any SIP headers to | |||
469 | * the stack. When the INVITE message (or any other SIP message) is created, | |||
470 | * the tagged values saved with nua_handle() are used first, next the tagged | |||
471 | * values given with the operation (nua_invite()) are added. | |||
472 | * | |||
473 | * @par | |||
474 | * When multiple tags for the same header are specified, the behaviour | |||
475 | * depends on the header type. If only a single header field can be included | |||
476 | * in a SIP message, the latest non-NULL value is used, e.g., @Subject. | |||
477 | * However, if the SIP header can consist of multiple lines or header fields | |||
478 | * separated by comma, e.g., @Accept, all the tagged | |||
479 | * values are concatenated. | |||
480 | * | |||
481 | * @par | |||
482 | * However, if a tag value is #SIP_NONE (-1 casted as a void pointer), the | |||
483 | * values from previous tags are ignored. | |||
484 | * | |||
485 | * @par | |||
486 | * Next, values previously set with nua_set_params() or nua_set_hparams() | |||
487 | * are used: @Allow, @Supported, @Organization, and @UserAgent headers are | |||
488 | * added to the request if they are not already set. | |||
489 | * | |||
490 | * @par | |||
491 | * Now, the target URI for the request needs to be determined. | |||
492 | * | |||
493 | * @par | |||
494 | * For initial INVITE requests, values from tags are used. If NUTAG_URL() is | |||
495 | * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given, it | |||
496 | * is used as target URI. If neither is given, the complete request line | |||
497 | * already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR() is used. | |||
498 | * If none of the tags above are given, an internal error is returned to the | |||
499 | * application. At this point, the target URI is stored in the request line, | |||
500 | * together with method name ("INVITE") and protocol version ("SIP/2.0"). | |||
501 | * The initial dialog information is also created: @CallID, @CSeq headers | |||
502 | * are generated, if they do not exist, and an unique tag is added to @From | |||
503 | * header. | |||
504 | * | |||
505 | * @par | |||
506 | * For the initial INVITE requests, the @Route headers specified by | |||
507 | * SIPTAG_ROUTE()/SIPTAG_ROUTER_STR() tags in nua_handle() and nua_invite() | |||
508 | * calls are inserted to the request. Next the initial route set specified | |||
509 | * by NUTAG_INITIAL_ROUTE()/NUTAG_INITIAL_ROUTE_STR() tags is prepended to | |||
510 | * the route. Finally (unless NUTAG_SERVICE_ROUTE_ENABLE(0) is used) the | |||
511 | * @ServiceRoute set received from the registrar is also appended to the | |||
512 | * route set of the initial request message. | |||
513 | * | |||
514 | * @par | |||
515 | * Next, the stack generates a @Contact header for the request (Unless the | |||
516 | * application already gave a @Contact header or it does not want to use | |||
517 | * @Contact and indicates that by including SIPTAG_CONTACT(NULL) or | |||
518 | * SIPTAG_CONTACT(SIP_NONE) in the tagged parameters.) If the application | |||
519 | * has a registration active, the @Contact header used with registration is | |||
520 | * used. Otherwise, the @Contact header is generated from the local IP | |||
521 | * address and port number, taking also the values from NUTAG_M_DISPLAY(), | |||
522 | * NUTAG_M_FEATURES(), NUTAG_M_PARAMS(), and NUTAG_M_USERNAME(). | |||
523 | * | |||
524 | * @par | |||
525 | * For in-dialog INVITE (re-INVITE), the request URI is taken from the | |||
526 | * @Contact header received from the remote party during the dialog | |||
527 | * establishment. Also, the @CallID and @CSeq headers and @From and @To tags | |||
528 | * are generated based on the dialog information and added to the request. | |||
529 | * If the dialog has a route (set by @RecordRoute headers), it is added to | |||
530 | * the request, too. | |||
531 | * | |||
532 | * @par | |||
533 | * @MaxForwards header (with default value set by NTATAG_MAX_FORWARDS()) is | |||
534 | * also added now, if it does not exist. | |||
535 | * | |||
536 | * @par | |||
537 | * The INVITE request message created by nua_invite() operation is saved as | |||
538 | * a template for automatic re-INVITE requests sent by the session timer | |||
539 | * ("timer") feature (see NUTAG_SESSION_TIMER() for more details). Please | |||
540 | * note that the template message is not used when ACK, PRACK, UPDATE or | |||
541 | * INFO requests are created (however, these requests will include | |||
542 | * dialog-specific headers like @To, @From, and @CallID as well as | |||
543 | * preference headers @Allow, @Supported, @UserAgent, @Organization). | |||
544 | * | |||
545 | * @par Tags Related to SIP Headers and Request-URI | |||
546 | * NUTAG_URL(), SIPTAG_REQUEST(), SIPTAG_REQUEST_STR() \n | |||
547 | * NUTAG_INITIAL_ROUTE(), NUTAG_INITIAL_ROUTE_STR(), | |||
548 | * SIPTAG_ROUTE(), SIPTAG_ROUTE_STR(), | |||
549 | * NUTAG_SERVICE_ROUTE_ENABLE() \n | |||
550 | * SIPTAG_MAX_FORWARDS(), SIPTAG_MAX_FORWARDS_STR() \n | |||
551 | * SIPTAG_PROXY_REQUIRE(), SIPTAG_PROXY_REQUIRE_STR() \n | |||
552 | * SIPTAG_FROM(), SIPTAG_FROM_STR() \n | |||
553 | * SIPTAG_TO(), SIPTAG_TO_STR() \n | |||
554 | * SIPTAG_CALL_ID(), SIPTAG_CALL_ID_STR() \n | |||
555 | * SIPTAG_CSEQ(), SIPTAG_CSEQ_STR() | |||
556 | * (note that @CSeq value is incremented if request gets retried)\n | |||
557 | * SIPTAG_CONTACT(), SIPTAG_CONTACT_STR() \n | |||
558 | * SIPTAG_REQUEST_DISPOSITION(), SIPTAG_REQUEST_DISPOSITION_STR() \n | |||
559 | * SIPTAG_ACCEPT_CONTACT(), SIPTAG_ACCEPT_CONTACT_STR() \n | |||
560 | * SIPTAG_REJECT_CONTACT(), SIPTAG_REJECT_CONTACT_STR() \n | |||
561 | * SIPTAG_EXPIRES(), SIPTAG_EXPIRES_STR() \n | |||
562 | * SIPTAG_DATE(), SIPTAG_DATE_STR() \n | |||
563 | * SIPTAG_TIMESTAMP(), SIPTAG_TIMESTAMP_STR() \n | |||
564 | * SIPTAG_SUBJECT(), SIPTAG_SUBJECT_STR() \n | |||
565 | * SIPTAG_PRIORITY(), SIPTAG_PRIORITY_STR() \n | |||
566 | * SIPTAG_CALL_INFO(), SIPTAG_CALL_INFO_STR() \n | |||
567 | * SIPTAG_ORGANIZATION(), SIPTAG_ORGANIZATION_STR() \n | |||
568 | * NUTAG_USER_AGENT(), SIPTAG_USER_AGENT() and SIPTAG_USER_AGENT_STR() \n | |||
569 | * SIPTAG_IN_REPLY_TO(), SIPTAG_IN_REPLY_TO_STR() \n | |||
570 | * SIPTAG_ACCEPT(), SIPTAG_ACCEPT_STR() \n | |||
571 | * SIPTAG_ACCEPT_ENCODING(), SIPTAG_ACCEPT_ENCODING_STR() \n | |||
572 | * SIPTAG_ACCEPT_LANGUAGE(), SIPTAG_ACCEPT_LANGUAGE_STR() \n | |||
573 | * NUTAG_ALLOW(), SIPTAG_ALLOW(), and SIPTAG_ALLOW_STR() \n | |||
574 | * NUTAG_EARLY_MEDIA(), SIPTAG_REQUIRE(), and SIPTAG_REQUIRE_STR() \n | |||
575 | * NUTAG_SUPPORTED(), SIPTAG_SUPPORTED(), and SIPTAG_SUPPORTED_STR() \n | |||
576 | * SIPTAG_ALLOW_EVENTS(), SIPTAG_ALLOW_EVENTS_STR() \n | |||
577 | * SIPTAG_PROXY_AUTHORIZATION(), SIPTAG_PROXY_AUTHORIZATION_STR() \n | |||
578 | * SIPTAG_AUTHORIZATION(), SIPTAG_AUTHORIZATION_STR() \n | |||
579 | * SIPTAG_REFERRED_BY(), SIPTAG_REFERRED_BY_STR() \n | |||
580 | * SIPTAG_REPLACES(), SIPTAG_REPLACES_STR() \n | |||
581 | * NUTAG_SESSION_TIMER(), NUTAG_SESSION_REFRESHER(), | |||
582 | * SIPTAG_SESSION_EXPIRES(), SIPTAG_SESSION_EXPIRES_STR() \n | |||
583 | * NUTAG_MIN_SE(), SIPTAG_MIN_SE(), SIPTAG_MIN_SE_STR() \n | |||
584 | * SIPTAG_SECURITY_CLIENT(), SIPTAG_SECURITY_CLIENT_STR() \n | |||
585 | * SIPTAG_SECURITY_VERIFY(), SIPTAG_SECURITY_VERIFY_STR() \n | |||
586 | * SIPTAG_PRIVACY(), SIPTAG_PRIVACY_STR() \n | |||
587 | * SIPTAG_MIME_VERSION(), SIPTAG_MIME_VERSION_STR() \n | |||
588 | * SIPTAG_CONTENT_TYPE(), SIPTAG_CONTENT_TYPE_STR() \n | |||
589 | * SIPTAG_CONTENT_ENCODING(), SIPTAG_CONTENT_ENCODING_STR() \n | |||
590 | * SIPTAG_CONTENT_LANGUAGE(), SIPTAG_CONTENT_LANGUAGE_STR() \n | |||
591 | * SIPTAG_CONTENT_DISPOSITION(), SIPTAG_CONTENT_DISPOSITION_STR() \n | |||
592 | * SIPTAG_HEADER(), SIPTAG_HEADER_STR() \n | |||
593 | * SIPTAG_PAYLOAD(), SIPTAG_PAYLOAD_STR() \n | |||
594 | * | |||
595 | * @par SDP Handling | |||
596 | * By default the nua_invite() uses an @ref soa_session_t "SOA media | |||
597 | * session" object to take care of the Offer/Answer exchange. The SOA can | |||
598 | * be disabled with tag NUTAG_MEDIA_ENABLE(0). | |||
599 | * | |||
600 | * @par | |||
601 | * The SDP description of the | |||
602 | * @ref soa_session_t "soa media session" is included in the INVITE request | |||
603 | * as a message body. | |||
604 | * The SDP in the message body of the 1XX or 2XX response message is | |||
605 | * interpreted as an answer, given to the @ref soa_session_t "soa media | |||
606 | * session" object for processing. | |||
607 | * | |||
608 | * @bug If the INVITE request already contains a message body, SDP is not | |||
609 | * added. Also, if the response contains a multipart body, it is not parsed. | |||
610 | * | |||
611 | * @par Tags Related to SDP Management and Offer/Answer Model: | |||
612 | * NUTAG_MEDIA_ENABLE(), \n | |||
613 | * NUTAG_INCLUDE_EXTRA_SDP(), \n | |||
614 | * SOATAG_HOLD(), SOATAG_AF(), SOATAG_ADDRESS(), | |||
615 | * SOATAG_ORDERED_USER(), SOATAG_REUSE_REJECTED(), | |||
616 | * SOATAG_RTP_SELECT(), SOATAG_RTP_SORT(), SOATAG_RTP_MISMATCH(), | |||
617 | * SOATAG_AUDIO_AUX(), \n | |||
618 | * SOATAG_USER_SDP() or SOATAG_USER_SDP_STR() \n | |||
619 | * | |||
620 | * @par Alternative Call Models | |||
621 | * In addition to the basic SIP call model described in @RFC3261 and | |||
622 | * @RFC3264, the early media model described in @RFC3262 is available. The | |||
623 | * use of 100rel and early media can be use can be forced with | |||
624 | * NUTAG_EARLY_MEDIA(1). | |||
625 | * | |||
626 | * Also, the "precondition" call model described in @RFC3312 is supported at | |||
627 | * SIP level, that is, the SIP PRACK and UPDATE requests are sent if | |||
628 | * "precondition" is added to the @Require header in the INVITE request. | |||
629 | * | |||
630 | * Optionally | |||
631 | * - uses early media if NUTAG_EARLY_MEDIA() tag is used with non zero-value | |||
632 | * - media parameters can be set by SOA tags | |||
633 | * - nua_invite() can be used to change status of an existing call: | |||
634 | * - #SOATAG_HOLD tag can be used to list the media that will be put on hold, | |||
635 | * the value "*" sets all the media beloginging to the session on hold | |||
636 | * | |||
637 | * @par Authentication | |||
638 | * The INVITE request may need authentication. Each proxy or server | |||
639 | * requiring authentication can respond with 401 or 407 response. The | |||
640 | * nua_authenticate() operation stores authentication information (username | |||
641 | * and password) to the handle, and stack tries to authenticate all the rest | |||
642 | * of the requests (e.g., PRACK, ACK, UPDATE, re-INVITE, BYE) using the | |||
643 | * stored username and password. | |||
644 | * | |||
645 | * @sa @ref nua_call_model, #nua_r_invite, #nua_i_state, \n | |||
646 | * nua_handle_has_active_call() \n | |||
647 | * nua_handle_has_call_on_hold()\n | |||
648 | * nua_handle_has_invite() \n | |||
649 | * nua_authenticate() \n | |||
650 | * nua_prack() \n | |||
651 | * nua_update() \n | |||
652 | * nua_info() \n | |||
653 | * nua_cancel() \n | |||
654 | * nua_bye() \n | |||
655 | * #nua_i_invite, nua_respond() | |||
656 | */ | |||
657 | ||||
658 | /* Tags not implemented | |||
659 | * NUTAG_REFER_PAUSE() \n | |||
660 | */ | |||
661 | ||||
662 | static int nua_invite_client_init(nua_client_request_t *cr, | |||
663 | msg_t *msg, sip_t *sip, | |||
664 | tagi_t const *tags); | |||
665 | static int nua_invite_client_request(nua_client_request_t *cr, | |||
666 | msg_t *msg, sip_t *sip, | |||
667 | tagi_t const *tags); | |||
668 | static int nua_invite_client_preliminary(nua_client_request_t *cr, | |||
669 | int status, char const *phrase, | |||
670 | sip_t const *sip); | |||
671 | static int nua_invite_client_response(nua_client_request_t *cr, | |||
672 | int status, char const *phrase, | |||
673 | sip_t const *sip); | |||
674 | static int nua_session_client_response(nua_client_request_t *cr, | |||
675 | int status, char const *phrase, | |||
676 | sip_t const *sip); | |||
677 | static int nua_invite_client_report(nua_client_request_t *cr, | |||
678 | int status, char const *phrase, | |||
679 | sip_t const *sip, | |||
680 | nta_outgoing_t *orq, | |||
681 | tagi_t const *tags); | |||
682 | ||||
683 | nua_client_methods_t const nua_invite_client_methods = { | |||
684 | SIP_METHOD_INVITEsip_method_invite, "INVITE", /* crm_method, crm_method_name */ | |||
685 | 0, /* crm_extra */ | |||
686 | { /* crm_flags */ | |||
687 | /* create_dialog */ 1, | |||
688 | /* in_dialog */ 1, | |||
689 | /* target refresh */ 1 | |||
690 | }, | |||
691 | NULL((void*)0), /* crm_template */ | |||
692 | nua_invite_client_init, /* crm_init */ | |||
693 | nua_invite_client_request, /* crm_send */ | |||
694 | session_timer_check_restart, /* crm_check_restart */ | |||
695 | nua_invite_client_response, /* crm_recv */ | |||
696 | nua_invite_client_preliminary, /* crm_preliminary */ | |||
697 | nua_invite_client_report, /* crm_report */ | |||
698 | nua_invite_client_complete, /* crm_complete */ | |||
699 | }; | |||
700 | ||||
701 | extern nua_client_methods_t const nua_bye_client_methods; | |||
702 | extern nua_client_methods_t const nua_cancel_client_methods; | |||
703 | extern nua_client_methods_t const nua_update_client_methods; | |||
704 | extern nua_client_methods_t const nua_prack_client_methods; | |||
705 | ||||
706 | int nua_stack_invite(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||
707 | tagi_t const *tags) | |||
708 | { | |||
709 | return nua_client_create(nh, e, &nua_invite_client_methods, tags); | |||
710 | } | |||
711 | ||||
712 | static int nua_invite_client_init(nua_client_request_t *cr, | |||
713 | msg_t *msg, sip_t *sip, | |||
714 | tagi_t const *tags) | |||
715 | { | |||
716 | nua_handle_t *nh = cr->cr_owner; | |||
717 | nua_dialog_usage_t *du; | |||
718 | nua_session_usage_t *ss; | |||
719 | ||||
720 | cr->cr_usage = du = nua_dialog_usage_for_session(nh->nh_ds); | |||
721 | /* Errors returned by nua_invite_client_init() | |||
722 | do not change the session state */ | |||
723 | cr->cr_neutral = 1; | |||
724 | ||||
725 | if (nh_is_special(nh) || | |||
726 | nua_stack_set_handle_special(nh, nh_has_invite, nua_i_error)) | |||
727 | return nua_client_return(cr, 900, "Invalid handle for INVITE", msg); | |||
728 | else if (nh_referral_check(nh, tags) < 0) | |||
729 | return nua_client_return(cr, 900, "Invalid referral", msg); | |||
730 | ||||
731 | if (du) { | |||
732 | nua_server_request_t *sr; | |||
733 | for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next) | |||
734 | /* INVITE in progress? */ | |||
735 | if (sr->sr_usage == du && sr->sr_method == sip_method_invite && | |||
736 | nua_server_request_is_pending(sr)) | |||
737 | return nua_client_return(cr, SIP_491_REQUEST_PENDING491, sip_491_Request_pending, msg); | |||
738 | cr->cr_initial = 0; | |||
739 | } | |||
740 | else { | |||
741 | du = nua_dialog_usage_add(nh, nh->nh_ds, nua_session_usage, NULL((void*)0)); | |||
742 | cr->cr_initial = 1; | |||
743 | } | |||
744 | ||||
745 | if (!du) | |||
746 | return -1; | |||
747 | ||||
748 | ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
749 | ||||
750 | if (ss->ss_state >= nua_callstate_terminating) | |||
751 | return nua_client_return(cr, 900, "Session is terminating", msg); | |||
752 | ||||
753 | if (nua_client_bind(cr, du) < 0) | |||
754 | return nua_client_return(cr, 900, "INVITE already in progress", msg); | |||
755 | ||||
756 | cr->cr_neutral = 0; | |||
757 | ||||
758 | session_timer_preferences(ss->ss_timer, | |||
759 | sip, | |||
760 | NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), | |||
761 | NH_PGET(nh, session_timer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_session_timer ? ((nh)->nh_prefs)->nhp_session_timer : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_session_timer), | |||
762 | NUA_PISSET(nh->nh_nua, nh, session_timer)((((nh->nh_nua)->nua_handles->nh_prefs)->nhp_set_ .set_bits.nhb_session_timer) || ((nh) && (((nh)->nh_prefs )->nhp_set_.set_bits.nhb_session_timer))), | |||
763 | NH_PGET(nh, refresher)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresher ? (( nh)->nh_prefs)->nhp_refresher : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_refresher), | |||
764 | NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se)); | |||
765 | ||||
766 | return 0; | |||
767 | } | |||
768 | ||||
769 | static int nua_invite_client_request(nua_client_request_t *cr, | |||
770 | msg_t *msg, sip_t *sip, | |||
771 | tagi_t const *tags) | |||
772 | { | |||
773 | nua_handle_t *nh = cr->cr_owner; | |||
774 | nua_dialog_usage_t *du = cr->cr_usage; | |||
775 | nua_session_usage_t *ss; | |||
776 | int offer_sent = 0, retval; | |||
777 | ||||
778 | if (du == NULL((void*)0)) /* Call terminated */ | |||
779 | return nua_client_return(cr, SIP_481_NO_TRANSACTION481, sip_481_No_transaction, msg); | |||
780 | ||||
781 | ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||
782 | ||||
783 | if (ss->ss_state >= nua_callstate_terminating) | |||
784 | return nua_client_return(cr, 900, "Session is terminating", msg); | |||
785 | ||||
786 | nua_dialog_usage_reset_refresh(du); | |||
787 | ||||
788 | /* Add session timer headers */ | |||
789 | if (session_timer_is_supported(ss->ss_timer)) | |||
790 | session_timer_add_headers(ss->ss_timer, ss->ss_state == nua_callstate_init, | |||
791 | msg, sip, nh); | |||
792 | ||||
793 | ss->ss_100rel = NH_PGET(nh, early_media)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_early_media ? ( (nh)->nh_prefs)->nhp_early_media : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_early_media); | |||
794 | ss->ss_precondition = sip_has_feature(sip->sip_require, "precondition"); | |||
795 | if (ss->ss_precondition) | |||
796 | ss->ss_update_needed = ss->ss_100rel = 1; | |||
797 | ||||
798 | if (nh->nh_soa) { | |||
799 | soa_init_offer_answer(nh->nh_soa); | |||
800 | ||||
801 | if (sip->sip_payload) | |||
802 | offer_sent = 0; /* XXX - kludge */ | |||
803 | else if (soa_generate_offer(nh->nh_soa, 0, NULL((void*)0)) < 0) | |||
804 | return -1; | |||
805 | else | |||
806 | offer_sent = 1; | |||
807 | ||||
808 | if (offer_sent > 0 && | |||
809 | session_include_description(nh->nh_soa, 1, msg, sip) < 0) | |||
810 | return nua_client_return(cr, 900, "Internal media error", msg); | |||
811 | ||||
812 | if (NH_PGET(nh, media_features)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_media_features ? ((nh)->nh_prefs)->nhp_media_features : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_media_features) && | |||
813 | !nua_dialog_is_established(nh->nh_ds) && | |||
814 | !sip->sip_accept_contact && !sip->sip_reject_contact) { | |||
815 | sip_accept_contact_t ac[1]; | |||
816 | sip_accept_contact_init(ac); | |||
817 | ||||
818 | ac->cp_params = (msg_param_t *) | |||
819 | soa_media_features(nh->nh_soa, 1, msg_home(msg)((su_home_t*)(msg))); | |||
820 | ||||
821 | if (ac->cp_params) { | |||
822 | msg_header_replace_param(msg_home(msg)((su_home_t*)(msg)), ac->cp_common, "explicit"); | |||
823 | sip_add_dup(msg, sip, (sip_header_t *)ac); | |||
824 | } | |||
825 | } | |||
826 | } | |||
827 | else { | |||
828 | offer_sent = session_get_description(sip, NULL((void*)0), NULL((void*)0)); | |||
829 | } | |||
830 | ||||
831 | retval = nua_base_client_trequest(cr, msg, sip, | |||
832 | NTATAG_REL100(ss->ss_100rel)ntatag_rel100, tag_bool_v((ss->ss_100rel)), | |||
833 | TAG_NEXT(tags)tag_next, (tag_value_t)(tags)); | |||
834 | if (retval == 0) { | |||
835 | if ((cr->cr_offer_sent = offer_sent)) | |||
836 | ss->ss_oa_sent = Offer; | |||
837 | ||||
838 | if (!cr->cr_restarting) /* Restart logic calls nua_invite_client_report */ | |||
839 | signal_call_state_change(nh, ss, 0, "INVITE sent", | |||
840 | nua_callstate_calling); | |||
841 | } | |||
842 | ||||
843 | return retval; | |||
844 | } | |||
845 | ||||
846 | static int nua_invite_client_response(nua_client_request_t *cr, | |||
847 | int status, char const *phrase, | |||
848 | sip_t const *sip) | |||
849 | { | |||
850 | nua_dialog_usage_t *du = cr->cr_usage; | |||
851 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
852 | int uas; | |||
853 | ||||
854 | if (ss == NULL((void*)0) || sip == NULL((void*)0)) { | |||
855 | /* Xyzzy */ | |||
856 | } | |||
857 | else if (status < 300) { | |||
858 | du->du_ready = 1; | |||
859 | ||||
860 | if (session_timer_is_supported(ss->ss_timer)) | |||
861 | session_timer_store(ss->ss_timer, sip); | |||
862 | ||||
863 | session_timer_set(ss, uas = 0); | |||
864 | } | |||
865 | ||||
866 | return nua_session_client_response(cr, status, phrase, sip); | |||
867 | } | |||
868 | ||||
869 | static int nua_invite_client_preliminary(nua_client_request_t *cr, | |||
870 | int status, char const *phrase, | |||
871 | sip_t const *sip) | |||
872 | { | |||
873 | nua_handle_t *nh = cr->cr_owner; | |||
874 | nua_dialog_usage_t *du = cr->cr_usage; | |||
875 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
876 | ||||
877 | assert(sip)((void) sizeof ((sip) ? 1 : 0), __extension__ ({ if (sip) ; else __assert_fail ("sip", "nua_session.c", 877, __extension__ __PRETTY_FUNCTION__ ); })); | |||
878 | ||||
879 | if (ss && sip && sip->sip_rseq) { | |||
880 | /* Handle 100rel responses */ | |||
881 | sip_rseq_t *rseq = sip->sip_rseq; | |||
882 | ||||
883 | /* Establish early dialog - we should fork here */ | |||
884 | if (!nua_dialog_is_established(nh->nh_ds)) { | |||
885 | nta_outgoing_t *tagged; | |||
886 | ||||
887 | nua_dialog_uac_route(nh, nh->nh_ds, sip, 1, 1); | |||
888 | nua_dialog_store_peer_info(nh, nh->nh_ds, sip); | |||
889 | ||||
890 | /* Tag the INVITE request */ | |||
891 | tagged = nta_outgoing_tagged(cr->cr_orq, | |||
892 | nua_client_orq_response, cr, | |||
893 | sip->sip_to->a_tag, sip->sip_rseq); | |||
894 | if (tagged) { | |||
895 | nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = tagged; | |||
896 | } | |||
897 | else { | |||
898 | cr->cr_graceful = 1; | |||
899 | ss->ss_reason = "SIP;cause=500;text=\"Cannot Create Early Dialog\""; | |||
900 | } | |||
901 | } | |||
902 | ||||
903 | if (!rseq) { | |||
904 | SU_DEBUG_5(("nua(%p): 100rel missing RSeq\n", (void *)nh))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 904, "nua(%p): 100rel missing RSeq\n" , (void *)nh)) : (void)0); | |||
905 | } | |||
906 | else if (nta_outgoing_rseq(cr->cr_orq) > rseq->rs_response) { | |||
907 | SU_DEBUG_5(("nua(%p): 100rel bad RSeq %u (got %u)\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 909, "nua(%p): 100rel bad RSeq %u (got %u)\n" , (void *)nh, (unsigned)rseq->rs_response, nta_outgoing_rseq (cr->cr_orq))) : (void)0) | |||
908 | (unsigned)rseq->rs_response,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 909, "nua(%p): 100rel bad RSeq %u (got %u)\n" , (void *)nh, (unsigned)rseq->rs_response, nta_outgoing_rseq (cr->cr_orq))) : (void)0) | |||
909 | nta_outgoing_rseq(cr->cr_orq)))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 909, "nua(%p): 100rel bad RSeq %u (got %u)\n" , (void *)nh, (unsigned)rseq->rs_response, nta_outgoing_rseq (cr->cr_orq))) : (void)0); | |||
910 | return 1; /* Do not send event */ | |||
911 | } | |||
912 | else if (nta_outgoing_setrseq(cr->cr_orq, rseq->rs_response) < 0) { | |||
913 | SU_DEBUG_1(("nua(%p): cannot set RSeq %u\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 1 ? (_su_llog((nua_log), 1, "nua_session.c" , (const char *)__func__, 914, "nua(%p): cannot set RSeq %u\n" , (void *)nh, (unsigned)rseq->rs_response)) : (void)0) | |||
914 | (unsigned)rseq->rs_response))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 1 ? (_su_llog((nua_log), 1, "nua_session.c" , (const char *)__func__, 914, "nua(%p): cannot set RSeq %u\n" , (void *)nh, (unsigned)rseq->rs_response)) : (void)0); | |||
915 | cr->cr_graceful = 1; | |||
916 | ss->ss_reason = "SIP;cause=400;text=\"Bad RSeq\""; | |||
917 | } | |||
918 | } | |||
919 | ||||
920 | return nua_session_client_response(cr, status, phrase, sip); | |||
921 | } | |||
922 | ||||
923 | /** Process response to a session request (INVITE, PRACK, UPDATE) */ | |||
924 | static int nua_session_client_response(nua_client_request_t *cr, | |||
925 | int status, char const *phrase, | |||
926 | sip_t const *sip) | |||
927 | { | |||
928 | nua_handle_t *nh = cr->cr_owner; | |||
929 | nua_dialog_usage_t *du = cr->cr_usage; | |||
930 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
931 | ||||
932 | char const *sdp = NULL((void*)0); | |||
933 | size_t len; | |||
934 | char const *received = NULL((void*)0); | |||
935 | ||||
936 | #define LOG3(m)((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 936, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, (m), received ? received : "SDP", status, phrase, cr->cr_answer_recv)) : (void)0) \ | |||
937 | SU_DEBUG_3(("nua(%p): %s: %s %s in %u %s (%u)\n", \((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 939, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, (m), received ? received : "SDP", status, phrase, cr->cr_answer_recv)) : (void)0) | |||
938 | (void *)nh, cr->cr_method_name, (m), \((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 939, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, (m), received ? received : "SDP", status, phrase, cr->cr_answer_recv)) : (void)0) | |||
939 | received ? received : "SDP", status, phrase, cr->cr_answer_recv))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 939, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, (m), received ? received : "SDP", status, phrase, cr->cr_answer_recv)) : (void)0) | |||
940 | #define LOG5(m)((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 940, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, (m), received, status, phrase , cr->cr_answer_recv)) : (void)0) \ | |||
941 | SU_DEBUG_5(("nua(%p): %s: %s %s in %u %s (%u)\n", \((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 942, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, (m), received, status, phrase , cr->cr_answer_recv)) : (void)0) | |||
942 | (void *)nh, cr->cr_method_name, (m), received, status, phrase, cr->cr_answer_recv))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 942, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, (m), received, status, phrase , cr->cr_answer_recv)) : (void)0) | |||
943 | ||||
944 | retry: | |||
945 | ||||
946 | if (!ss || !sip || 300 <= status) | |||
947 | /* Xyzzy */; | |||
948 | else if (!session_get_description(sip, &sdp, &len)) | |||
949 | /* No SDP */; | |||
950 | else if (cr->cr_answer_recv) { | |||
951 | if (cr->cr_answer_recv > status) { | |||
952 | LOG3("status is older than previous answer, ignoring")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 952, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("status is older than previous answer, ignoring" ), received ? received : "SDP", status, phrase, cr->cr_answer_recv )) : (void)0); | |||
953 | sdp = NULL((void*)0); | |||
954 | return 0; | |||
955 | } else { | |||
956 | // we need to make sure its *actually* a dup, so we can't assume for now. | |||
957 | LOG3("multiple answers received, processing")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 957, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("multiple answers received, processing" ), received ? received : "SDP", status, phrase, cr->cr_answer_recv )) : (void)0); | |||
958 | cr->cr_answer_recv = 0; | |||
959 | goto retry; | |||
960 | } | |||
961 | } | |||
962 | else if (cr->cr_offer_sent) { | |||
963 | /* case 1: answer to our offer */ | |||
964 | cr->cr_answer_recv = status; | |||
965 | received = Answer; | |||
966 | ||||
967 | if (nh->nh_soa == NULL((void*)0)) | |||
968 | LOG5("got SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 968, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("got SDP"), received, status , phrase, cr->cr_answer_recv)) : (void)0); | |||
969 | else if (soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sdp, len) < 0) { | |||
970 | LOG3("error parsing SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 970, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("error parsing SDP"), received ? received : "SDP", status, phrase, cr->cr_answer_recv)) : (void)0); | |||
971 | sdp = NULL((void*)0); | |||
972 | cr->cr_graceful = 1; | |||
973 | ss->ss_reason = "SIP;cause=400;text=\"Malformed Session Description\""; | |||
974 | } | |||
975 | else if (soa_process_answer(nh->nh_soa, NULL((void*)0)) < 0) { | |||
976 | LOG5("error processing SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 976, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("error processing SDP") , received, status, phrase, cr->cr_answer_recv)) : (void)0 ); | |||
977 | /* XXX */ | |||
978 | sdp = NULL((void*)0); | |||
979 | } | |||
980 | else if (soa_activate(nh->nh_soa, NULL((void*)0)) < 0) { | |||
981 | /* XXX - what about errors? */ | |||
982 | LOG3("error activating media after")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 982, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("error activating media after" ), received ? received : "SDP", status, phrase, cr->cr_answer_recv )) : (void)0); | |||
983 | } | |||
984 | else { | |||
985 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||
986 | LOG5("processed SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 986, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("processed SDP"), received , status, phrase, cr->cr_answer_recv)) : (void)0); | |||
987 | } | |||
988 | } | |||
989 | else if (cr->cr_method != sip_method_invite) { | |||
990 | /* If non-invite request did not have offer, ignore SDP in response */ | |||
991 | LOG3("ignoring extra")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 991, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("ignoring extra"), received ? received : "SDP", status, phrase, cr->cr_answer_recv)) : (void)0); | |||
992 | sdp = NULL((void*)0); | |||
993 | } | |||
994 | else { | |||
995 | /* case 2: new offer */ | |||
996 | cr->cr_offer_recv = 1, cr->cr_answer_sent = 0; | |||
997 | received = Offer; | |||
998 | ||||
999 | if (nh->nh_soa && soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sdp, len) < 0) { | |||
1000 | LOG3("error parsing SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1000, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("error parsing SDP"), received ? received : "SDP", status, phrase, cr->cr_answer_recv)) : (void)0); | |||
1001 | sdp = NULL((void*)0); | |||
1002 | cr->cr_graceful = 1; | |||
1003 | ss->ss_reason = "SIP;cause=400;text=\"Malformed Session Description\""; | |||
1004 | } | |||
1005 | else | |||
1006 | LOG5("got SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 1006, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("got SDP"), received, status , phrase, cr->cr_answer_recv)) : (void)0); | |||
1007 | } | |||
1008 | ||||
1009 | if (ss && received) | |||
1010 | ss->ss_oa_recv = received; | |||
1011 | ||||
1012 | if (sdp && nh->nh_soa) | |||
1013 | return nua_base_client_tresponse(cr, status, phrase, sip, | |||
1014 | NH_REMOTE_MEDIA_TAGS(1, nh->nh_soa)!((1) && (nh->nh_soa) && soa_is_remote_audio_active (nh->nh_soa) >= 0) ? tag_skip : soatag_active_audio, tag_int_v (soa_is_remote_audio_active(nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_remote_video_active(nh-> nh_soa) >= 0) ? tag_skip : soatag_active_video, tag_int_v( soa_is_remote_video_active(nh->nh_soa)), !((1) && ( nh->nh_soa) && soa_is_remote_image_active(nh->nh_soa ) >= 0) ? tag_skip : soatag_active_image, tag_int_v(soa_is_remote_image_active (nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_remote_chat_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_chat, tag_int_v(soa_is_remote_chat_active(nh ->nh_soa)), | |||
1015 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
1016 | else | |||
1017 | return nua_base_client_response(cr, status, phrase, sip, NULL((void*)0)); | |||
1018 | } | |||
1019 | ||||
1020 | static int nua_invite_client_report(nua_client_request_t *cr, | |||
1021 | int status, char const *phrase, | |||
1022 | sip_t const *sip, | |||
1023 | nta_outgoing_t *orq, | |||
1024 | tagi_t const *tags) | |||
1025 | { | |||
1026 | nua_handle_t *nh = cr->cr_owner; | |||
1027 | nua_dialog_state_t *ds = nh->nh_ds; | |||
1028 | nua_dialog_usage_t *du = cr->cr_usage; | |||
1029 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
1030 | msg_t *response = nta_outgoing_getresponse(orq); | |||
1031 | unsigned next_state; | |||
1032 | int error; | |||
1033 | ||||
1034 | nh_referral_respond(nh, status, phrase); /* XXX - restarting after 401/407 */ | |||
1035 | ||||
1036 | nua_stack_event(nh->nh_nua, nh, | |||
1037 | response, | |||
1038 | (enum nua_event_e)cr->cr_event, | |||
1039 | status, phrase, | |||
1040 | tags); | |||
1041 | ||||
1042 | if (cr->cr_waiting) | |||
1043 | /* Do not report call state change if waiting for restart */ | |||
1044 | return 1; | |||
1045 | ||||
1046 | if (ss == NULL((void*)0)) { | |||
1047 | signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminated); | |||
1048 | return 1; | |||
1049 | } | |||
1050 | ||||
1051 | ss->ss_reporting = 1; | |||
1052 | ||||
1053 | if (cr->cr_neutral) { | |||
1054 | signal_call_state_change(nh, ss, status, phrase, ss->ss_state); | |||
1055 | ss->ss_reporting = 0; | |||
1056 | return 1; | |||
1057 | } | |||
1058 | ||||
1059 | response = msg_ref_create(response); /* Keep reference to contents of sip */ | |||
1060 | ||||
1061 | if (orq != cr->cr_orq && cr->cr_orq) { /* Being restarted */ | |||
1062 | next_state = nua_callstate_calling; | |||
1063 | } | |||
1064 | else if (status == 100) { | |||
1065 | next_state = nua_callstate_calling; | |||
1066 | } | |||
1067 | else if (status < 300 && cr->cr_graceful) { | |||
1068 | next_state = nua_callstate_terminating; | |||
1069 | if (200 <= status) { | |||
1070 | nua_invite_client_ack(cr, NULL((void*)0)); | |||
1071 | } | |||
1072 | } | |||
1073 | else if (status < 200) { | |||
1074 | next_state = nua_callstate_proceeding; | |||
1075 | ||||
1076 | if (sip && sip->sip_rseq && | |||
1077 | !SIP_IS_ALLOWED(NH_PGET(nh, appl_method), sip_method_prack)(sip_method_unknown < (sip_method_prack) && (sip_method_prack ) < 32 && ((((nh)->nh_prefs)->nhp_set_.set_bits .nhb_appl_method ? ((nh)->nh_prefs)->nhp_appl_method : ( (nh)->nh_nua->nua_handles->nh_prefs)->nhp_appl_method )) && (((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_appl_method ? ((nh)->nh_prefs)->nhp_appl_method : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_appl_method))->k_bitmap & (1 << (sip_method_prack))) != 0)) { | |||
1078 | sip_rack_t rack[1]; | |||
1079 | ||||
1080 | sip_rack_init(rack); | |||
1081 | rack->ra_response = sip->sip_rseq->rs_response; | |||
1082 | rack->ra_cseq = sip->sip_cseq->cs_seq; | |||
1083 | rack->ra_method = sip->sip_cseq->cs_method; | |||
1084 | rack->ra_method_name = sip->sip_cseq->cs_method_name; | |||
1085 | ||||
1086 | error = nua_client_tcreate(nh, nua_r_prack, &nua_prack_client_methods, | |||
1087 | SIPTAG_RACK(rack)siptag_rack, siptag_rack_v(rack), | |||
1088 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
1089 | if (error < 0) { | |||
1090 | cr->cr_graceful = 1; | |||
1091 | next_state = nua_callstate_terminating; | |||
1092 | } | |||
1093 | } | |||
1094 | } | |||
1095 | else if (status < 300) { | |||
1096 | next_state = nua_callstate_completing; | |||
1097 | } | |||
1098 | else if (cr->cr_terminated) { | |||
1099 | next_state = nua_callstate_terminated; | |||
1100 | } | |||
1101 | else if (cr->cr_graceful && ss->ss_state >= nua_callstate_completing) { | |||
1102 | next_state = nua_callstate_terminating; | |||
1103 | } | |||
1104 | else { | |||
1105 | next_state = nua_callstate_init; | |||
1106 | } | |||
1107 | ||||
1108 | if (next_state == nua_callstate_calling) { | |||
1109 | if (sip && sip->sip_status && sip->sip_status->st_status == 100) { | |||
1110 | ss->ss_reporting = 0; | |||
1111 | return 1; | |||
1112 | } | |||
1113 | } | |||
1114 | ||||
1115 | if (next_state == nua_callstate_completing) { | |||
1116 | if (NH_PGET(nh, auto_ack)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack ? ((nh )->nh_prefs)->nhp_auto_ack : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_auto_ack) || | |||
1117 | /* Auto-ACK response to re-INVITE when media is enabled | |||
1118 | and auto_ack is not set to 0 on handle */ | |||
1119 | (ss->ss_state == nua_callstate_ready && nh->nh_soa && | |||
1120 | !NH_PISSET(nh, auto_ack)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ))) { | |||
1121 | nua_client_request_t *cru; | |||
1122 | ||||
1123 | for (cru = ds->ds_cr; cru; cru = cru->cr_next) { | |||
1124 | if (cr != cru && cru->cr_offer_sent && !cru->cr_answer_recv) | |||
1125 | break; | |||
1126 | } | |||
1127 | ||||
1128 | if (cru) | |||
1129 | /* A final response to UPDATE or PRACK with answer on its way? */; | |||
1130 | else if (nua_invite_client_ack(cr, NULL((void*)0)) > 0) | |||
1131 | next_state = nua_callstate_ready; | |||
1132 | else | |||
1133 | next_state = nua_callstate_terminating; | |||
1134 | } | |||
1135 | } | |||
1136 | ||||
1137 | if (next_state == nua_callstate_terminating) { | |||
1138 | /* Send BYE or CANCEL */ | |||
1139 | /* XXX - Forking - send BYE to early dialog?? */ | |||
1140 | if (ss->ss_state > nua_callstate_proceeding || status >= 200) | |||
1141 | error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)); | |||
1142 | else | |||
1143 | error = nua_client_create(nh, nua_r_cancel, | |||
1144 | &nua_cancel_client_methods, tags); | |||
1145 | ||||
1146 | if (error) { | |||
1147 | next_state = nua_callstate_terminated; | |||
1148 | cr->cr_terminated = 1; | |||
1149 | } | |||
1150 | cr->cr_graceful = 0; | |||
1151 | } | |||
1152 | ||||
1153 | ss->ss_reporting = 0; | |||
1154 | ||||
1155 | signal_call_state_change(nh, ss, status, phrase, (enum nua_callstate)next_state); | |||
1156 | ||||
1157 | msg_destroy(response); | |||
1158 | ||||
1159 | return 1; | |||
1160 | } | |||
1161 | ||||
1162 | /**@fn void nua_ack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||
1163 | * | |||
1164 | * Acknowledge a succesful response to INVITE request. | |||
1165 | * | |||
1166 | * Acknowledge a successful response (200..299) to INVITE request with the | |||
1167 | * SIP ACK request message. This function is needed only if NUTAG_AUTOACK() | |||
1168 | * parameter has been cleared. | |||
1169 | * | |||
1170 | * @param nh Pointer to operation handle | |||
1171 | * @param tag, value, ... List of tagged parameters | |||
1172 | * | |||
1173 | * @return | |||
1174 | * nothing | |||
1175 | * | |||
1176 | * @par Related Tags: | |||
1177 | * Header tags defined in <sofia-sip/sip_tag.h> | |||
1178 | * | |||
1179 | * @par Events: | |||
1180 | * #nua_i_media_error \n | |||
1181 | * #nua_i_state (#nua_i_active, #nua_i_terminated) | |||
1182 | * | |||
1183 | * @sa NUTAG_AUTOACK(), @ref nua_call_model, #nua_i_state | |||
1184 | */ | |||
1185 | ||||
1186 | int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||
1187 | tagi_t const *tags) | |||
1188 | { | |||
1189 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||
1190 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
1191 | nua_client_request_t *cr = du ? du->du_cr : NULL((void*)0); | |||
1192 | int error; | |||
1193 | ||||
1194 | if (!cr || cr->cr_orq == NULL((void*)0) || cr->cr_status < 200) { | |||
1195 | UA_EVENT2(nua_i_error, 900, "No response to ACK")nua_stack_event(nua, nh, ((void*)0), nua_i_error, 900, "No response to ACK" , ((void*)0)); | |||
1196 | return 1; | |||
1197 | } | |||
1198 | ||||
1199 | if (tags) | |||
1200 | nua_stack_set_params(nua, nh, nua_i_error, tags); | |||
1201 | ||||
1202 | nua_client_request_ref(cr); | |||
1203 | error = nua_invite_client_ack(cr, tags); | |||
1204 | ||||
1205 | if (error < 0) { | |||
1206 | if (ss->ss_reason == NULL((void*)0)) | |||
1207 | ss->ss_reason = "SIP;cause=500;text=\"Internal Error\""; | |||
1208 | ss->ss_reporting = 1; /* We report terminated state here if BYE fails */ | |||
1209 | error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)); | |||
1210 | ss->ss_reporting = 0; | |||
1211 | signal_call_state_change(nh, ss, 500, "Internal Error", | |||
1212 | error | |||
1213 | ? nua_callstate_terminated | |||
1214 | : nua_callstate_terminating); | |||
1215 | } | |||
1216 | else if (ss) | |||
1217 | signal_call_state_change(nh, ss, 200, "ACK sent", nua_callstate_ready); | |||
1218 | ||||
1219 | nua_client_request_unref(cr); | |||
1220 | ||||
1221 | return 0; | |||
1222 | } | |||
1223 | ||||
1224 | /** Send ACK, destroy INVITE transaction. | |||
1225 | * | |||
1226 | * @retval 1 if successful | |||
1227 | * @retval < 0 if an error occurred | |||
1228 | */ | |||
1229 | static | |||
1230 | int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags) | |||
1231 | { | |||
1232 | nua_handle_t *nh = cr->cr_owner; | |||
1233 | nua_dialog_state_t *ds = nh->nh_ds; | |||
1234 | nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage)((cr->cr_usage) ? (void*)((cr->cr_usage) + 1) : ((void* )0)); | |||
1235 | ||||
1236 | msg_t *msg; | |||
1237 | sip_t *sip; | |||
1238 | int error = -1; | |||
1239 | sip_authorization_t *wa; | |||
1240 | sip_proxy_authorization_t *pa; | |||
1241 | sip_cseq_t *cseq; | |||
1242 | int proxy_is_set; | |||
1243 | url_string_t *proxy; | |||
1244 | nta_outgoing_t *ack; | |||
1245 | int status = 200; | |||
1246 | char const *phrase = "OK", *reason = NULL((void*)0); | |||
1247 | char const *invite_branch; | |||
1248 | char const *pl_s = NULL((void*)0); | |||
1249 | ||||
1250 | assert(cr->cr_orq)((void) sizeof ((cr->cr_orq) ? 1 : 0), __extension__ ({ if (cr->cr_orq) ; else __assert_fail ("cr->cr_orq", "nua_session.c" , 1250, __extension__ __PRETTY_FUNCTION__); })); | |||
1251 | assert(cr->cr_method == sip_method_invite)((void) sizeof ((cr->cr_method == sip_method_invite) ? 1 : 0), __extension__ ({ if (cr->cr_method == sip_method_invite ) ; else __assert_fail ("cr->cr_method == sip_method_invite" , "nua_session.c", 1251, __extension__ __PRETTY_FUNCTION__); } )); | |||
1252 | ||||
1253 | cr->cr_initial = 0; | |||
1254 | ||||
1255 | if (!ds->ds_leg) { | |||
1256 | /* XXX - fix nua_dialog_usage_remove_at() instead! */ | |||
1257 | goto error; | |||
1258 | } | |||
1259 | ||||
1260 | tl_gets(tags, | |||
1261 | SIPTAG_PAYLOAD_STR_REF(pl_s)siptag_payload_str_ref, tag_str_vr(&(pl_s)), | |||
1262 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
1263 | ||||
1264 | ||||
1265 | assert(ds->ds_leg)((void) sizeof ((ds->ds_leg) ? 1 : 0), __extension__ ({ if (ds->ds_leg) ; else __assert_fail ("ds->ds_leg", "nua_session.c" , 1265, __extension__ __PRETTY_FUNCTION__); })); | |||
1266 | ||||
1267 | msg = nta_outgoing_getrequest(cr->cr_orq); | |||
1268 | sip = sip_object(msg); | |||
1269 | if (!msg) | |||
1270 | goto error; | |||
1271 | invite_branch = nta_outgoing_branch(cr->cr_orq); | |||
1272 | ||||
1273 | wa = sip_authorization(sip)((sip_authorization_t *)msg_header_access((msg_pub_t*)(sip), sip_authorization_class )); | |||
1274 | pa = sip_proxy_authorization(sip)((sip_proxy_authorization_t *)msg_header_access((msg_pub_t*)( sip), sip_proxy_authorization_class)); | |||
1275 | ||||
1276 | msg_destroy(msg); | |||
1277 | ||||
1278 | msg = nta_msg_create(nh->nh_nua->nua_nta, 0); | |||
1279 | sip = sip_object(msg); | |||
1280 | if (!msg) | |||
1281 | goto error; | |||
1282 | ||||
1283 | cseq = sip_cseq_create(msg_home(msg)((su_home_t*)(msg)), cr->cr_seq, SIP_METHOD_ACKsip_method_ack, "ACK"); | |||
1284 | ||||
1285 | if (!cseq) | |||
1286 | ; | |||
1287 | else if (nh->nh_tags && sip_add_tl(msg, sip, TAG_NEXT(nh->nh_tags)tag_next, (tag_value_t)(nh->nh_tags)) < 0) | |||
1288 | ; | |||
1289 | else if (tags && sip_add_tl(msg, sip, TAG_NEXT(tags)tag_next, (tag_value_t)(tags)) < 0) | |||
1290 | ; | |||
1291 | else if (wa && sip_add_dup(msg, sip, (sip_header_t *)wa) < 0) | |||
1292 | ; | |||
1293 | else if (pa && sip_add_dup(msg, sip, (sip_header_t *)pa) < 0) | |||
1294 | ; | |||
1295 | else if (sip_header_insert(msg, sip, (sip_header_t *)cseq) < 0) | |||
1296 | ; | |||
1297 | else if (nta_msg_request_complete(msg, ds->ds_leg, SIP_METHOD_ACKsip_method_ack, "ACK", NULL((void*)0)) < 0) | |||
1298 | ; | |||
1299 | else { | |||
1300 | /* Remove extra headers */ | |||
1301 | while (sip->sip_allow) | |||
1302 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_allow); | |||
1303 | while (sip->sip_priority) | |||
1304 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_priority); | |||
1305 | while (sip->sip_proxy_require) | |||
1306 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_proxy_require); | |||
1307 | while (sip->sip_require) | |||
1308 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_require); | |||
1309 | while (sip->sip_subject) | |||
1310 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_subject); | |||
1311 | while (sip->sip_supported) | |||
1312 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_supported); | |||
1313 | ||||
1314 | if (ss == NULL((void*)0) || ss->ss_state > nua_callstate_ready || pl_s) | |||
1315 | ; | |||
1316 | else if (cr->cr_offer_recv && !cr->cr_answer_sent) { | |||
1317 | if (nh->nh_soa == NULL((void*)0)) { | |||
1318 | if (session_get_description(sip, NULL((void*)0), NULL((void*)0))) | |||
1319 | cr->cr_answer_sent = 1, ss->ss_oa_sent = Answer; | |||
1320 | } | |||
1321 | else if (soa_generate_answer(nh->nh_soa, NULL((void*)0)) < 0 || | |||
1322 | session_include_description(nh->nh_soa, 1, msg, sip) < 0) { | |||
1323 | status = 900, phrase = "Internal media error"; | |||
1324 | reason = "SIP;cause=500;text=\"Internal media error\""; | |||
1325 | /* reason = soa_error_as_sip_reason(nh->nh_soa); */ | |||
1326 | } | |||
1327 | else { | |||
1328 | cr->cr_answer_sent = 1, ss->ss_oa_sent = Answer; | |||
1329 | } | |||
1330 | } | |||
1331 | ||||
1332 | if (ss == NULL((void*)0) || ss->ss_state > nua_callstate_ready || reason) | |||
1333 | ; | |||
1334 | else if (nh->nh_soa && soa_is_complete(nh->nh_soa)) { | |||
1335 | /* signal SOA that O/A round(s) is (are) complete */ | |||
1336 | if (soa_activate(nh->nh_soa, NULL((void*)0)) >= 0) { | |||
1337 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||
1338 | } | |||
1339 | } | |||
1340 | else if (nh->nh_soa == NULL((void*)0) | |||
1341 | /* NUA does not necessarily know dirty details */ | |||
1342 | /* && !(cr->cr_offer_sent && !cr->cr_answer_recv) */) { | |||
1343 | ; | |||
1344 | } | |||
1345 | else { | |||
1346 | nua_client_request_t *cru; | |||
1347 | ||||
1348 | /* Final response to UPDATE or PRACK may be on its way ... */ | |||
1349 | for (cru = ds->ds_cr; cru; cru = cru->cr_next) { | |||
1350 | if (cr != cru && cru->cr_offer_sent && !cru->cr_answer_recv) | |||
1351 | break; | |||
1352 | } | |||
1353 | ||||
1354 | if (cru == NULL((void*)0)) { | |||
1355 | /* No SDP answer -> terminate call */ | |||
1356 | status = 988, phrase = "Incomplete offer/answer"; | |||
1357 | reason = "SIP;cause=488;text=\"Incomplete offer/answer\""; | |||
1358 | } | |||
1359 | } | |||
1360 | ||||
1361 | proxy_is_set = NH_PISSET(nh, proxy)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_proxy) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ); | |||
1362 | proxy = NH_PGET(nh, proxy)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_proxy ? ((nh)-> nh_prefs)->nhp_proxy : ((nh)->nh_nua->nua_handles-> nh_prefs)->nhp_proxy); | |||
1363 | ||||
1364 | if ((ack = nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL((void*)0), NULL((void*)0), NULL((void*)0), | |||
1365 | msg, | |||
1366 | NTATAG_ACK_BRANCH(invite_branch)ntatag_ack_branch, tag_str_v((invite_branch)), | |||
1367 | TAG_IF(proxy_is_set,!(proxy_is_set) ? tag_skip : ntatag_default_proxy, urltag_url_v ((proxy)) | |||
1368 | NTATAG_DEFAULT_PROXY(proxy))!(proxy_is_set) ? tag_skip : ntatag_default_proxy, urltag_url_v ((proxy)), | |||
1369 | SIPTAG_END()siptag_end, (tag_value_t)0, | |||
1370 | TAG_NEXT(tags)tag_next, (tag_value_t)(tags)))) { | |||
1371 | /* TR engine keeps this around for T2 so it catches all 2XX retransmissions */ | |||
1372 | nta_outgoing_destroy(ack); | |||
1373 | ||||
1374 | if (nh->nh_soa && reason && ss && ss->ss_state <= nua_callstate_ready) | |||
1375 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||
1376 | nua_i_media_error, status, phrase, | |||
1377 | NULL((void*)0)); | |||
1378 | } | |||
1379 | else if (!reason) { | |||
1380 | status = 900, phrase = "Cannot send ACK"; | |||
1381 | reason = "SIP;cause=500;text=\"Internal Error\""; | |||
1382 | } | |||
1383 | ||||
1384 | if (ss && reason) | |||
1385 | ss->ss_reason = reason; | |||
1386 | ||||
1387 | if (status < 300) | |||
1388 | error = 1; | |||
1389 | else | |||
1390 | error = -2; | |||
1391 | } | |||
1392 | ||||
1393 | if (error == -1) | |||
1394 | msg_destroy(msg); | |||
1395 | ||||
1396 | error: | |||
1397 | cr->cr_acked = 1; /* ... or we have at least tried */ | |||
1398 | ||||
1399 | nua_client_request_remove(cr); | |||
1400 | nua_client_request_clean(cr); | |||
1401 | ||||
1402 | return error; | |||
1403 | } | |||
1404 | ||||
1405 | static int | |||
1406 | nua_invite_client_should_ack(nua_client_request_t const *cr) | |||
1407 | { | |||
1408 | return | |||
1409 | cr && cr->cr_orq && !cr->cr_acked && | |||
1410 | 200 <= cr->cr_status && cr->cr_status < 300; | |||
1411 | } | |||
1412 | ||||
1413 | /** Complete client request */ | |||
1414 | static int nua_invite_client_complete(nua_client_request_t *cr) | |||
1415 | { | |||
1416 | if (cr->cr_orq == NULL((void*)0)) | |||
1417 | /* Xyzzy */; | |||
1418 | else if (cr->cr_status < 200) | |||
1419 | nta_outgoing_cancel(cr->cr_orq); | |||
1420 | else if (cr->cr_status < 300 && !cr->cr_acked) | |||
1421 | nua_invite_client_ack(cr, NULL((void*)0)); | |||
1422 | ||||
1423 | return 0; | |||
1424 | } | |||
1425 | ||||
1426 | /**@fn void nua_cancel(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||
1427 | * | |||
1428 | * Cancel an INVITE operation | |||
1429 | * | |||
1430 | * @param nh Pointer to operation handle | |||
1431 | * @param tag, value, ... List of tagged parameters | |||
1432 | * | |||
1433 | * @return | |||
1434 | * nothing | |||
1435 | * | |||
1436 | * @par Related Tags: | |||
1437 | * Header tags defined in <sofia-sip/sip_tag.h> | |||
1438 | * | |||
1439 | * @par Events: | |||
1440 | * #nua_r_cancel, #nua_i_state (#nua_i_active, #nua_i_terminated) | |||
1441 | * | |||
1442 | * @sa @ref nua_call_model, nua_invite(), #nua_i_cancel | |||
1443 | */ | |||
1444 | ||||
1445 | static int nua_cancel_client_request(nua_client_request_t *cr, | |||
1446 | msg_t *msg, sip_t *sip, | |||
1447 | tagi_t const *tags); | |||
1448 | static int nua_cancel_client_check_restart(nua_client_request_t *cr, | |||
1449 | int status, | |||
1450 | char const *phrase, | |||
1451 | sip_t const *sip); | |||
1452 | ||||
1453 | nua_client_methods_t const nua_cancel_client_methods = { | |||
1454 | SIP_METHOD_CANCELsip_method_cancel, "CANCEL", /* crm_method, crm_method_name */ | |||
1455 | 0, /* crm_extra */ | |||
1456 | { /* crm_flags */ | |||
1457 | /* create_dialog */ 0, | |||
1458 | /* in_dialog */ 1, | |||
1459 | /* target refresh */ 0 | |||
1460 | }, | |||
1461 | NULL((void*)0), /* crm_template */ | |||
1462 | NULL((void*)0), /* crm_init */ | |||
1463 | nua_cancel_client_request, /* .. not really crm_send */ | |||
1464 | nua_cancel_client_check_restart, /* crm_check_restart */ | |||
1465 | NULL((void*)0), /* crm_recv */ | |||
1466 | NULL((void*)0), /* crm_preliminary */ | |||
1467 | NULL((void*)0), /* crm_report */ | |||
1468 | NULL((void*)0), /* crm_complete */ | |||
1469 | }; | |||
1470 | ||||
1471 | int nua_stack_cancel(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||
1472 | tagi_t const *tags) | |||
1473 | { | |||
1474 | return nua_client_create(nh, e, &nua_cancel_client_methods, tags); | |||
1475 | } | |||
1476 | ||||
1477 | static int nua_cancel_client_request(nua_client_request_t *cr, | |||
1478 | msg_t *msg, sip_t *sip, | |||
1479 | tagi_t const *tags) | |||
1480 | { | |||
1481 | nua_handle_t *nh = cr->cr_owner; | |||
1482 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||
1483 | ||||
1484 | if (!du || !du->du_cr || !du->du_cr->cr_orq || | |||
1485 | nta_outgoing_status(du->du_cr->cr_orq) >= 200) { | |||
1486 | return nua_client_return(cr, 481, "No transaction to CANCEL", msg); | |||
1487 | } | |||
1488 | ||||
1489 | assert(cr->cr_orq == NULL)((void) sizeof ((cr->cr_orq == ((void*)0)) ? 1 : 0), __extension__ ({ if (cr->cr_orq == ((void*)0)) ; else __assert_fail ("cr->cr_orq == NULL" , "nua_session.c", 1489, __extension__ __PRETTY_FUNCTION__); } )); | |||
1490 | ||||
1491 | cr->cr_orq = nta_outgoing_tcancel(du->du_cr->cr_orq, | |||
1492 | nua_client_orq_response, | |||
1493 | nua_client_request_ref(cr), | |||
1494 | TAG_NEXT(tags)tag_next, (tag_value_t)(tags)); | |||
1495 | ||||
1496 | if (cr->cr_orq == NULL((void*)0)) { | |||
1497 | nua_client_request_unref(cr); | |||
1498 | return -1; | |||
1499 | } | |||
1500 | ||||
1501 | return 0; | |||
1502 | } | |||
1503 | ||||
1504 | static int | |||
1505 | nua_cancel_client_check_restart(nua_client_request_t *cr, | |||
1506 | int status, | |||
1507 | char const *phrase, | |||
1508 | sip_t const *sip) | |||
1509 | { | |||
1510 | /* We cannot really restart CANCEL */ | |||
1511 | return 0; | |||
1512 | } | |||
1513 | ||||
1514 | /** @NUA_EVENT nua_r_cancel | |||
1515 | * | |||
1516 | * Answer to outgoing CANCEL. | |||
1517 | * | |||
1518 | * The CANCEL may be sent explicitly by nua_cancel() or implicitly by NUA | |||
1519 | * state machine. | |||
1520 | * | |||
1521 | * @param status response status code | |||
1522 | * @param phrase a short textual description of @a status code | |||
1523 | * @param nh operation handle associated with the call | |||
1524 | * @param hmagic application context associated with the call | |||
1525 | * @param sip response to CANCEL request or NULL upon an error | |||
1526 | * (status code is in @a status and | |||
1527 | * descriptive message in @a phrase parameters) | |||
1528 | * @param tags empty | |||
1529 | * | |||
1530 | * @sa nua_cancel(), @ref nua_uac_call_model, #nua_r_invite, nua_invite(), | |||
1531 | * #nua_i_state | |||
1532 | * | |||
1533 | * @END_NUA_EVENT | |||
1534 | */ | |||
1535 | ||||
1536 | static void nua_session_usage_refresh(nua_handle_t *nh, | |||
1537 | nua_dialog_state_t *ds, | |||
1538 | nua_dialog_usage_t *du, | |||
1539 | sip_time_t now) | |||
1540 | { | |||
1541 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||
1542 | nua_client_request_t const *cr = du->du_cr; | |||
1543 | nua_server_request_t const *sr; | |||
1544 | ||||
1545 | if (ss->ss_state >= nua_callstate_terminating || | |||
1546 | /* INVITE is in progress or being authenticated */ | |||
1547 | nua_client_request_in_progress(cr)) | |||
1548 | return; | |||
1549 | ||||
1550 | if (ds->ds_cr) return; /* request queued */ | |||
1551 | ||||
1552 | /* UPDATE has been queued */ | |||
1553 | //for (cr = ds->ds_cr; cr; cr = cr->cr_next) | |||
1554 | //if (cr->cr_method == sip_method_update) | |||
1555 | // return; | |||
1556 | ||||
1557 | /* INVITE or UPDATE in progress on server side */ | |||
1558 | for (sr = ds->ds_sr; sr; sr = sr->sr_next) | |||
1559 | if (sr->sr_usage == du && | |||
1560 | (sr->sr_method == sip_method_invite || | |||
1561 | sr->sr_method == sip_method_update)) | |||
1562 | return; | |||
1563 | ||||
1564 | if (ss->ss_timer->refresher == nua_remote_refresher) { | |||
1565 | SU_DEBUG_3(("nua(%p): session almost expired, sending BYE before timeout.\n", (void *)nh))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1565, "nua(%p): session almost expired, sending BYE before timeout.\n" , (void *)nh)) : (void)0); | |||
1566 | ss->ss_reason = "SIP;cause=408;text=\"Session timeout\""; | |||
1567 | nua_stack_bye(nh->nh_nua, nh, nua_r_bye, NULL((void*)0)); | |||
1568 | return; | |||
1569 | } | |||
1570 | else if (NH_PGET(nh, update_refresh)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_update_refresh ? ((nh)->nh_prefs)->nhp_update_refresh : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_update_refresh)) { | |||
1571 | nua_stack_update(nh->nh_nua, nh, nua_r_update, NULL((void*)0)); | |||
1572 | } | |||
1573 | else if (du->du_cr) { | |||
1574 | nua_client_resend_request(du->du_cr, 0); | |||
1575 | } | |||
1576 | else { | |||
1577 | nua_stack_invite(nh->nh_nua, nh, nua_r_invite, NULL((void*)0)); | |||
1578 | } | |||
1579 | } | |||
1580 | ||||
1581 | /** @interal Shut down session usage. | |||
1582 | * | |||
1583 | * @retval >0 shutdown done | |||
1584 | * @retval 0 shutdown in progress | |||
1585 | * @retval <0 try again later | |||
1586 | */ | |||
1587 | static int nua_session_usage_shutdown(nua_handle_t *nh, | |||
1588 | nua_dialog_state_t *ds, | |||
1589 | nua_dialog_usage_t *du) | |||
1590 | { | |||
1591 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||
1592 | nua_server_request_t *sr, *sr_next; | |||
1593 | nua_client_request_t *cri; | |||
1594 | ||||
1595 | assert(ss == nua_session_usage_for_dialog(nh->nh_ds))((void) sizeof ((ss == nua_session_usage_for_dialog(nh->nh_ds )) ? 1 : 0), __extension__ ({ if (ss == nua_session_usage_for_dialog (nh->nh_ds)) ; else __assert_fail ("ss == nua_session_usage_for_dialog(nh->nh_ds)" , "nua_session.c", 1595, __extension__ __PRETTY_FUNCTION__); } )); | |||
1596 | ||||
1597 | /* Zap server-side transactions */ | |||
1598 | for (sr = ds->ds_sr; sr; sr = sr_next) { | |||
1599 | sr_next = sr->sr_next; | |||
1600 | if (sr->sr_usage == du) { | |||
1601 | assert(sr->sr_usage == du)((void) sizeof ((sr->sr_usage == du) ? 1 : 0), __extension__ ({ if (sr->sr_usage == du) ; else __assert_fail ("sr->sr_usage == du" , "nua_session.c", 1601, __extension__ __PRETTY_FUNCTION__); } )); | |||
1602 | sr->sr_usage = NULL((void*)0); | |||
1603 | ||||
1604 | if (nua_server_request_is_pending(sr)) { | |||
1605 | SR_STATUS1(sr, SIP_480_TEMPORARILY_UNAVAILABLE)sr_status(sr, 480, sip_480_Temporarily_unavailable); | |||
1606 | nua_server_respond(sr, NULL((void*)0)); | |||
1607 | if (nua_server_report(sr) >= 2) | |||
1608 | return 480; | |||
1609 | } | |||
1610 | else | |||
1611 | nua_server_request_destroy(sr); | |||
1612 | } | |||
1613 | } | |||
1614 | ||||
1615 | cri = du->du_cr; | |||
1616 | ||||
1617 | switch (ss->ss_state) { | |||
1618 | case nua_callstate_calling: | |||
1619 | case nua_callstate_proceeding: | |||
1620 | return nua_client_create(nh, nua_r_cancel, &nua_cancel_client_methods, NULL((void*)0)); | |||
1621 | ||||
1622 | case nua_callstate_completing: | |||
1623 | case nua_callstate_completed: | |||
1624 | case nua_callstate_ready: | |||
1625 | if (cri && cri->cr_orq) { | |||
1626 | if (cri->cr_status < 200) { | |||
1627 | nua_client_create(nh, nua_r_cancel, &nua_cancel_client_methods, NULL((void*)0)); | |||
1628 | } | |||
1629 | else if (cri->cr_status < 300 && !cri->cr_acked) { | |||
1630 | nua_invite_client_ack(cri, NULL((void*)0)); | |||
1631 | } | |||
1632 | } | |||
1633 | if (nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)) != 0) | |||
1634 | break; | |||
1635 | ||||
1636 | signal_call_state_change(nh, ss, 487, "BYE sent", | |||
1637 | nua_callstate_terminating); | |||
1638 | return 0; | |||
1639 | ||||
1640 | case nua_callstate_terminating: | |||
1641 | case nua_callstate_terminated: /* XXX */ | |||
1642 | return 0; | |||
1643 | ||||
1644 | default: | |||
1645 | break; | |||
1646 | } | |||
1647 | ||||
1648 | nua_dialog_usage_remove(nh, ds, du, NULL((void*)0), NULL((void*)0)); | |||
1649 | ||||
1650 | return 200; | |||
1651 | } | |||
1652 | ||||
1653 | /**@fn void nua_prack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||
1654 | * Send a PRACK request. | |||
1655 | * | |||
1656 | * PRACK is used to acknowledge receipt of 100rel responses. See @RFC3262. | |||
1657 | * | |||
1658 | * @param nh Pointer to operation handle | |||
1659 | * @param tag, value, ... List of tagged parameters | |||
1660 | * | |||
1661 | * @return | |||
1662 | * nothing | |||
1663 | * | |||
1664 | * @par Related Tags: | |||
1665 | * Tags in <sofia-sip/soa_tag.h>, <sofia-sip/sip_tag.h>. | |||
1666 | * | |||
1667 | * @par Events: | |||
1668 | * #nua_r_prack | |||
1669 | */ | |||
1670 | ||||
1671 | /** @NUA_EVENT nua_r_prack | |||
1672 | * | |||
1673 | * Response to an outgoing @b PRACK request. PRACK request is used to | |||
1674 | * acknowledge reliable preliminary responses and it is usually sent | |||
1675 | * automatically by the nua stack. | |||
1676 | * | |||
1677 | * @param status response status code | |||
1678 | * (if the request is retried, @a status is 100, the @a | |||
1679 | * sip->sip_status->st_status contain the real status code | |||
1680 | * from the response message, e.g., 302, 401, or 407) | |||
1681 | * @param phrase a short textual description of @a status code | |||
1682 | * @param nh operation handle associated with the call | |||
1683 | * @param hmagic application context associated with the call | |||
1684 | * @param sip response to @b PRACK or NULL upon an error | |||
1685 | * (status code is in @a status and | |||
1686 | * descriptive message in @a phrase parameters) | |||
1687 | * @param tags empty | |||
1688 | * | |||
1689 | * @sa nua_prack(), #nua_i_prack, @RFC3262 | |||
1690 | * | |||
1691 | * @END_NUA_EVENT | |||
1692 | */ | |||
1693 | ||||
1694 | static int nua_prack_client_init(nua_client_request_t *cr, | |||
1695 | msg_t *msg, sip_t *sip, | |||
1696 | tagi_t const *tags); | |||
1697 | static int nua_prack_client_request(nua_client_request_t *cr, | |||
1698 | msg_t *msg, sip_t *sip, | |||
1699 | tagi_t const *tags); | |||
1700 | static int nua_prack_client_response(nua_client_request_t *cr, | |||
1701 | int status, char const *phrase, | |||
1702 | sip_t const *sip); | |||
1703 | static int nua_prack_client_report(nua_client_request_t *cr, | |||
1704 | int status, char const *phrase, | |||
1705 | sip_t const *sip, | |||
1706 | nta_outgoing_t *orq, | |||
1707 | tagi_t const *tags); | |||
1708 | ||||
1709 | nua_client_methods_t const nua_prack_client_methods = { | |||
1710 | SIP_METHOD_PRACKsip_method_prack, "PRACK", /* crm_method, crm_method_name */ | |||
1711 | 0, /* crm_extra */ | |||
1712 | { /* crm_flags */ | |||
1713 | /* create_dialog */ 0, | |||
1714 | /* in_dialog */ 1, | |||
1715 | /* target refresh */ 0 | |||
1716 | }, | |||
1717 | NULL((void*)0), /* crm_template */ | |||
1718 | nua_prack_client_init, /* crm_init */ | |||
1719 | nua_prack_client_request, /* crm_send */ | |||
1720 | NULL((void*)0), /* crm_check_restart */ | |||
1721 | nua_prack_client_response, /* crm_recv */ | |||
1722 | NULL((void*)0), /* crm_preliminary */ | |||
1723 | nua_prack_client_report, /* crm_report */ | |||
1724 | NULL((void*)0), /* crm_complete */ | |||
1725 | }; | |||
1726 | ||||
1727 | int nua_stack_prack(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||
1728 | tagi_t const *tags) | |||
1729 | { | |||
1730 | return nua_client_create(nh, e, &nua_prack_client_methods, tags); | |||
1731 | } | |||
1732 | ||||
1733 | static int nua_prack_client_init(nua_client_request_t *cr, | |||
1734 | msg_t *msg, sip_t *sip, | |||
1735 | tagi_t const *tags) | |||
1736 | { | |||
1737 | nua_handle_t *nh = cr->cr_owner; | |||
1738 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||
1739 | ||||
1740 | cr->cr_usage = du; | |||
1741 | ||||
1742 | return 0; | |||
1743 | } | |||
1744 | ||||
1745 | static int nua_prack_client_request(nua_client_request_t *cr, | |||
1746 | msg_t *msg, sip_t *sip, | |||
1747 | tagi_t const *tags) | |||
1748 | { | |||
1749 | nua_handle_t *nh = cr->cr_owner; | |||
1750 | nua_dialog_usage_t *du = cr->cr_usage; | |||
1751 | nua_session_usage_t *ss; | |||
1752 | nua_client_request_t *cri; | |||
1753 | int offer_sent = 0, answer_sent = 0, retval; | |||
1754 | int status = 0; char const *phrase = "PRACK Sent"; | |||
1755 | //uint32_t rseq = 0; | |||
1756 | ||||
1757 | if (du == NULL((void*)0)) /* Call terminated */ | |||
1758 | return nua_client_return(cr, SIP_481_NO_TRANSACTION481, sip_481_No_transaction, msg); | |||
1759 | ||||
1760 | ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||
1761 | if (ss->ss_state >= nua_callstate_terminating) | |||
1762 | return nua_client_return(cr, 900, "Session is terminating", msg); | |||
1763 | ||||
1764 | cri = du->du_cr; | |||
1765 | ||||
1766 | // if (sip->sip_rack) | |||
1767 | // rseq = sip->sip_rack->ra_response; | |||
1768 | ||||
1769 | if (cri->cr_offer_recv && !cri->cr_answer_sent) { | |||
1770 | if (nh->nh_soa == NULL((void*)0)) | |||
1771 | /* It is up to application to handle SDP */ | |||
1772 | answer_sent = session_get_description(sip, NULL((void*)0), NULL((void*)0)); | |||
1773 | else if (sip->sip_payload) | |||
1774 | /* XXX - we should just do MIME in session_include_description() */; | |||
1775 | else if (soa_generate_answer(nh->nh_soa, NULL((void*)0)) < 0 || | |||
1776 | session_include_description(nh->nh_soa, 1, msg, sip) < 0) { | |||
1777 | status = soa_error_as_sip_response(nh->nh_soa, &phrase); | |||
1778 | SU_DEBUG_3(("nua(%p): local response to PRACK: %d %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1779, "nua(%p): local response to PRACK: %d %s\n" , (void *)nh, status, phrase)) : (void)0) | |||
1779 | (void *)nh, status, phrase))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1779, "nua(%p): local response to PRACK: %d %s\n" , (void *)nh, status, phrase)) : (void)0); | |||
1780 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||
1781 | nua_i_media_error, status, phrase, | |||
1782 | NULL((void*)0)); | |||
1783 | return nua_client_return(cr, status, phrase, msg); | |||
1784 | } | |||
1785 | else { | |||
1786 | answer_sent = 1; | |||
1787 | if (soa_activate(nh->nh_soa, NULL((void*)0)) >= 0) { | |||
1788 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||
1789 | } | |||
1790 | } | |||
1791 | } | |||
1792 | else if (nh->nh_soa == NULL((void*)0)) { | |||
1793 | offer_sent = session_get_description(sip, NULL((void*)0), NULL((void*)0)); | |||
1794 | } | |||
1795 | else { | |||
1796 | /* When 100rel response status was 183 do support for preconditions */ | |||
1797 | int send_offer = ss->ss_precondition && | |||
1798 | cri->cr_status == 183 && cri->cr_offer_sent && cri->cr_answer_recv; | |||
1799 | ||||
1800 | if (!send_offer) { | |||
1801 | tagi_t const *t = tl_find_last(tags, nutag_include_extra_sdp); | |||
1802 | send_offer = t && t->t_value; | |||
1803 | } | |||
1804 | ||||
1805 | if (!send_offer) { | |||
1806 | } | |||
1807 | else if (soa_generate_offer(nh->nh_soa, 0, NULL((void*)0)) >= 0 && | |||
1808 | session_include_description(nh->nh_soa, 1, msg, sip) >= 0) { | |||
1809 | offer_sent = 1; | |||
1810 | } | |||
1811 | else { | |||
1812 | status = soa_error_as_sip_response(nh->nh_soa, &phrase); | |||
1813 | SU_DEBUG_3(("nua(%p): PRACK offer: %d %s\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1814, "nua(%p): PRACK offer: %d %s\n" , (void *)nh, status, phrase)) : (void)0) | |||
1814 | status, phrase))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1814, "nua(%p): PRACK offer: %d %s\n" , (void *)nh, status, phrase)) : (void)0); | |||
1815 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||
1816 | nua_i_media_error, status, phrase, NULL((void*)0)); | |||
1817 | return nua_client_return(cr, status, phrase, msg); | |||
1818 | } | |||
1819 | } | |||
1820 | ||||
1821 | retval = nua_base_client_request(cr, msg, sip, NULL((void*)0)); | |||
1822 | ||||
1823 | if (retval == 0) { | |||
1824 | cr->cr_offer_sent = offer_sent; | |||
1825 | cr->cr_answer_sent = answer_sent; | |||
1826 | ||||
1827 | if (offer_sent) | |||
1828 | ss->ss_oa_sent = Offer; | |||
1829 | else if (answer_sent) | |||
1830 | ss->ss_oa_sent = Answer; | |||
1831 | ||||
1832 | if (cr->cr_restarting) | |||
1833 | /* Restart logic calls nua_prack_client_report */; | |||
1834 | else if (!cr->cr_auto && (!offer_sent || !answer_sent)) | |||
1835 | /* Suppose application know it called nua_prack() */; | |||
1836 | else | |||
1837 | signal_call_state_change(nh, ss, status, phrase, ss->ss_state); | |||
1838 | } | |||
1839 | ||||
1840 | return retval; | |||
1841 | } | |||
1842 | ||||
1843 | static int nua_prack_client_response(nua_client_request_t *cr, | |||
1844 | int status, char const *phrase, | |||
1845 | sip_t const *sip) | |||
1846 | { | |||
1847 | /* XXX - fatal error cases? */ | |||
1848 | ||||
1849 | return nua_session_client_response(cr, status, phrase, sip); | |||
1850 | } | |||
1851 | ||||
1852 | static int nua_prack_client_report(nua_client_request_t *cr, | |||
1853 | int status, char const *phrase, | |||
1854 | sip_t const *sip, | |||
1855 | nta_outgoing_t *orq, | |||
1856 | tagi_t const *tags) | |||
1857 | { | |||
1858 | nua_handle_t *nh = cr->cr_owner; | |||
1859 | nua_dialog_usage_t *du = cr->cr_usage; | |||
1860 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
1861 | int acked = 0; | |||
1862 | ||||
1863 | nua_stack_event(nh->nh_nua, nh, | |||
1864 | nta_outgoing_getresponse(orq), | |||
1865 | (enum nua_event_e)cr->cr_event, | |||
1866 | status, phrase, | |||
1867 | tags); | |||
1868 | ||||
1869 | if (!ss || cr->cr_terminated || cr->cr_graceful || cr->cr_waiting) | |||
1870 | return 1; | |||
1871 | ||||
1872 | if (cr->cr_offer_sent || cr->cr_answer_sent) { | |||
1873 | unsigned next_state = ss->ss_state; | |||
1874 | ||||
1875 | if (status < 200) | |||
1876 | ; | |||
1877 | else if (nua_invite_client_should_ack(du->du_cr)) { | |||
1878 | /* There is an un-ACK-ed INVITE there */ | |||
1879 | assert(du->du_cr->cr_method == sip_method_invite)((void) sizeof ((du->du_cr->cr_method == sip_method_invite ) ? 1 : 0), __extension__ ({ if (du->du_cr->cr_method == sip_method_invite) ; else __assert_fail ("du->du_cr->cr_method == sip_method_invite" , "nua_session.c", 1879, __extension__ __PRETTY_FUNCTION__); } )); | |||
1880 | if (NH_PGET(nh, auto_ack)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack ? ((nh )->nh_prefs)->nhp_auto_ack : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_auto_ack) || | |||
1881 | /* Auto-ACK response to re-INVITE when media is enabled | |||
1882 | and auto_ack is not set to 0 on handle */ | |||
1883 | (ss->ss_state == nua_callstate_ready && nh->nh_soa && | |||
1884 | !NH_PISSET(nh, auto_ack)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ))) { | |||
1885 | /* There should be no UPDATE with offer/answer | |||
1886 | if PRACK with offer/answer was ongoing! */ | |||
1887 | if (nua_invite_client_ack(du->du_cr, NULL((void*)0)) > 0) | |||
1888 | next_state = nua_callstate_ready; | |||
1889 | else | |||
1890 | next_state = nua_callstate_terminating; | |||
1891 | ||||
1892 | acked = 1; | |||
1893 | } | |||
1894 | } | |||
1895 | ||||
1896 | signal_call_state_change(nh, ss, status, phrase, (enum nua_callstate)next_state); | |||
1897 | } | |||
1898 | ||||
1899 | if (acked && | |||
1900 | nua_client_is_queued(du->du_cr) && | |||
1901 | du->du_cr->cr_method == sip_method_invite) { | |||
1902 | /* New INVITE was queued - do not send UPDATE */ | |||
1903 | } | |||
1904 | else if (ss->ss_update_needed && 200 <= status && status < 300 && | |||
1905 | !SIP_IS_ALLOWED(NH_PGET(nh, appl_method), sip_method_update)(sip_method_unknown < (sip_method_update) && (sip_method_update ) < 32 && ((((nh)->nh_prefs)->nhp_set_.set_bits .nhb_appl_method ? ((nh)->nh_prefs)->nhp_appl_method : ( (nh)->nh_nua->nua_handles->nh_prefs)->nhp_appl_method )) && (((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_appl_method ? ((nh)->nh_prefs)->nhp_appl_method : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_appl_method))->k_bitmap & (1 << (sip_method_update))) != 0)) | |||
1906 | nua_client_create(nh, nua_r_update, &nua_update_client_methods, NULL((void*)0)); | |||
1907 | ||||
1908 | return 1; | |||
1909 | } | |||
1910 | ||||
1911 | /* ---------------------------------------------------------------------- */ | |||
1912 | /* UAS side of INVITE */ | |||
1913 | ||||
1914 | /** @NUA_EVENT nua_i_invite | |||
1915 | * | |||
1916 | * Indication of incoming call or re-INVITE request. | |||
1917 | * | |||
1918 | * @param status statuscode of response sent automatically by stack | |||
1919 | * @param phrase a short textual description of @a status code | |||
1920 | * @param nh operation handle associated with this call | |||
1921 | * (maybe created for this call) | |||
1922 | * @param hmagic application context associated with this call | |||
1923 | * (maybe NULL if call handle was created for this call) | |||
1924 | * @param sip incoming INVITE request | |||
1925 | * @param tags SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO() | |||
1926 | * | |||
1927 | * @par | |||
1928 | * @par Responding to INVITE with nua_respond() | |||
1929 | * | |||
1930 | * If @a status in #nua_i_invite event is below 200, the application should | |||
1931 | * accept or reject the call with nua_respond(). See the @ref nua_call_model | |||
1932 | * for the detailed explanation of various options in call processing at | |||
1933 | * server end. | |||
1934 | * | |||
1935 | * The @b INVITE request takes care of session setup using SDP Offer-Answer | |||
1936 | * negotiation as specified in @RFC3264 (updated in @RFC3262 section 5, | |||
1937 | * @RFC3311, and @RFC3312). The Offer-Answer can be taken care by | |||
1938 | * application (if NUTAG_MEDIA_ENABLE(0) parameter has been set) or by the | |||
1939 | * built-in SDP Offer/Answer engine @soa (by default and when | |||
1940 | * NUTAG_MEDIA_ENABLE(1) parameter has been set). When @soa is enabled, it | |||
1941 | * will take care of parsing the SDP, negotiating the media and codecs, and | |||
1942 | * including the SDP in the SIP message bodies as required by the | |||
1943 | * Offer-Answer model. | |||
1944 | * | |||
1945 | * When @soa is enabled, the SDP in the incoming INVITE is parsed and feed | |||
1946 | * to a #soa_session_t object. The #nua_i_state event sent to the | |||
1947 | * application immediately after #nua_i_invite will contain the parsing | |||
1948 | * results in SOATAG_REMOTE_SDP() and SOATAG_REMOTE_SDP_STR() tags. | |||
1949 | * | |||
1950 | * Note that currently the parser within @nua does not handle MIME | |||
1951 | * multipart. The SDP Offer/Answer engine can get confused if the SDP offer | |||
1952 | * is included in a MIME multipart, therefore such an @b INVITE is rejected | |||
1953 | * with <i>415 Unsupported Media Type</i> error response: the client is | |||
1954 | * expected to retry the INVITE without MIME multipart content. | |||
1955 | * | |||
1956 | * If the call is to be accepted, the application should include the SDP in | |||
1957 | * the 2XX response. If @soa is not disabled with NUTAG_MEDIA_ENABLE(0), the | |||
1958 | * SDP should be included in the SOATAG_USER_SDP() or SOATAG_USER_SDP_STR() | |||
1959 | * parameter given to nua_respond(). If it is disabled, the SDP should be | |||
1960 | * included in the response message using SIPTAG_PAYLOAD() or | |||
1961 | * SIPTAG_PAYLOAD_STR(). Also, the @ContentType should be set using | |||
1962 | * SIPTAG_CONTENT_TYPE() or SIPTAG_CONTENT_TYPE_STR(). | |||
1963 | * | |||
1964 | * @par Preliminary Responses and 100rel | |||
1965 | * | |||
1966 | * Call progress can be signaled with preliminary responses (with status | |||
1967 | * code in the range 101..199). It is possible to conclude the SDP | |||
1968 | * Offer-Answer negotiation using preliminary responses, too. If | |||
1969 | * NUTAG_EARLY_ANSWER(1), SOATAG_USER_SDP() or SOATAG_USER_SDP_STR() | |||
1970 | * parameter is included with in a preliminary nua_response(), the SDP | |||
1971 | * answer is generated and sent with the preliminary responses, too. | |||
1972 | * | |||
1973 | * The preliminary responses are sent reliably if feature tag "100rel" is | |||
1974 | * included in the @Require header of the response or if | |||
1975 | * NUTAG_EARLY_MEDIA(1) parameter has been given. The reliably delivery of | |||
1976 | * preliminary responses mean that a sequence number is included in the | |||
1977 | * @RSeq header in the response message and the response message is resent | |||
1978 | * until the client responds with a @b PRACK request with matching sequence | |||
1979 | * number in @RAck header. | |||
1980 | * | |||
1981 | * Note that only the "183" response is sent reliably if the | |||
1982 | * NUTAG_ONLY183_100REL(1) parameter has been given. The reliable | |||
1983 | * preliminary responses are acknowledged with @b PRACK request sent by the | |||
1984 | * client. | |||
1985 | * | |||
1986 | * Note if the SDP offer-answer is completed with the reliable preliminary | |||
1987 | * responses, the is no need to include SDP in 200 OK response (or other 2XX | |||
1988 | * response). However, it the tag NUTAG_INCLUDE_EXTRA_SDP(1) is included | |||
1989 | * with nua_respond(), a copy of the SDP answer generated earlier by @soa is | |||
1990 | * included as the message body. | |||
1991 | * | |||
1992 | * @sa nua_respond(), @ref nua_uas_call_model, #nua_i_state, | |||
1993 | * NUTAG_MEDIA_ENABLE(), SOATAG_USER_SDP(), SOATAG_USER_SDP_STR(), | |||
1994 | * @RFC3262, NUTAG_EARLY_ANSWER(), NUTAG_EARLY_MEDIA(), | |||
1995 | * NUTAG_ONLY183_100REL(), | |||
1996 | * NUTAG_INCLUDE_EXTRA_SDP(), | |||
1997 | * #nua_i_prack, #nua_i_update, nua_update(), | |||
1998 | * nua_invite(), #nua_r_invite | |||
1999 | * | |||
2000 | * @par | |||
2001 | * @par Third Party Call Control | |||
2002 | * | |||
2003 | * When so called 2rd party call control is used, the initial @b INVITE may | |||
2004 | * not contain SDP offer. In that case, the offer is sent by the recipient | |||
2005 | * of the @b INVITE request (User-Agent Server, UAS). The SDP sent in 2XX | |||
2006 | * response (or in a preliminary reliable response) is considered as an | |||
2007 | * offer, and the answer will be included in the @b ACK request sent by the | |||
2008 | * UAC (or @b PRACK in case of preliminary reliable response). | |||
2009 | * | |||
2010 | * @sa @ref nua_3pcc_call_model | |||
2011 | * | |||
2012 | * @END_NUA_EVENT | |||
2013 | */ | |||
2014 | ||||
2015 | static int nua_invite_server_init(nua_server_request_t *sr); | |||
2016 | static int nua_session_server_init(nua_server_request_t *sr); | |||
2017 | static int nua_invite_server_preprocess(nua_server_request_t *sr); | |||
2018 | static int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *); | |||
2019 | static int nua_invite_server_is_100rel(nua_server_request_t *, tagi_t const *); | |||
2020 | static int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *); | |||
2021 | ||||
2022 | static int | |||
2023 | process_ack_or_cancel(nua_server_request_t *, nta_incoming_t *, | |||
2024 | sip_t const *), | |||
2025 | process_ack(nua_server_request_t *, nta_incoming_t *, sip_t const *), | |||
2026 | process_ack_error(nua_server_request_t *sr, msg_t *ackmsg, | |||
2027 | int status, char const *phrase, char const *reason), | |||
2028 | process_cancel(nua_server_request_t *, nta_incoming_t *, sip_t const *), | |||
2029 | process_timeout(nua_server_request_t *, nta_incoming_t *), | |||
2030 | process_prack(nua_server_request_t *, | |||
2031 | nta_reliable_t *rel, | |||
2032 | nta_incoming_t *irq, | |||
2033 | sip_t const *sip); | |||
2034 | ||||
2035 | nua_server_methods_t const nua_invite_server_methods = | |||
2036 | { | |||
2037 | SIP_METHOD_INVITEsip_method_invite, "INVITE", | |||
2038 | nua_i_invite, /* Event */ | |||
2039 | { | |||
2040 | 1, /* Create dialog */ | |||
2041 | 0, /* Initial request */ | |||
2042 | 1, /* Target refresh request */ | |||
2043 | 1, /* Add Contact */ | |||
2044 | }, | |||
2045 | nua_invite_server_init, | |||
2046 | nua_invite_server_preprocess, | |||
2047 | nua_base_server_params((void*)0), | |||
2048 | nua_invite_server_respond, | |||
2049 | nua_invite_server_report, | |||
2050 | }; | |||
2051 | ||||
2052 | ||||
2053 | /** @internal Preprocess incoming invite - sure we have a valid request. | |||
2054 | * | |||
2055 | * @return 0 if request is valid, or error statuscode otherwise | |||
2056 | */ | |||
2057 | static int | |||
2058 | nua_invite_server_init(nua_server_request_t *sr) | |||
2059 | { | |||
2060 | nua_handle_t *nh = sr->sr_owner; | |||
2061 | nua_t *nua = nh->nh_nua; | |||
2062 | nua_session_usage_t *ss; | |||
2063 | ||||
2064 | sr->sr_neutral = 1; | |||
2065 | ||||
2066 | if (!NUA_PGET(nua, nh, invite_enable)(((nh) ? (nh)->nh_prefs : (nua)->nua_handles->nh_prefs )->nhp_set_.set_bits.nhb_invite_enable ? ((nh) ? (nh)-> nh_prefs : (nua)->nua_handles->nh_prefs)->nhp_invite_enable : ((nua)->nua_handles->nh_prefs)->nhp_invite_enable )) | |||
2067 | return SR_STATUS1(sr, SIP_403_FORBIDDEN)sr_status(sr, 403, sip_403_Forbidden); | |||
2068 | ||||
2069 | if (nua_session_server_init(sr)) | |||
2070 | return sr->sr_status; | |||
2071 | ||||
2072 | if (sr->sr_usage) { | |||
2073 | /* Existing session - check for overlap and glare */ | |||
2074 | ||||
2075 | nua_server_request_t const *sr0; | |||
2076 | nua_client_request_t const *cr; | |||
2077 | ||||
2078 | for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr0->sr_next) { | |||
2079 | /* Previous INVITE has not been ACKed */ | |||
2080 | if (sr0->sr_method == sip_method_invite) | |||
2081 | break; | |||
2082 | /* Or we have sent offer but have not received an answer */ | |||
2083 | if (sr->sr_sdp && sr0->sr_offer_sent && !sr0->sr_answer_recv) | |||
2084 | break; | |||
2085 | /* Or we have received request with offer but not sent an answer */ | |||
2086 | if (sr->sr_sdp && sr0->sr_offer_recv && !sr0->sr_answer_sent) | |||
2087 | break; | |||
2088 | } | |||
2089 | ||||
2090 | if (sr0) { | |||
2091 | /* Overlapping invites - RFC 3261 14.2 */ | |||
2092 | return nua_server_retry_after(sr, 500, "Overlapping Requests", 0, 10); | |||
2093 | } | |||
2094 | ||||
2095 | for (cr = nh->nh_ds->ds_cr; cr; cr = cr->cr_next) { | |||
2096 | if (cr->cr_usage == sr->sr_usage && cr->cr_orq && cr->cr_offer_sent) | |||
2097 | /* Glare - RFC 3261 14.2 and RFC 3311 section 5.2 */ | |||
2098 | return SR_STATUS1(sr, SIP_491_REQUEST_PENDING)sr_status(sr, 491, sip_491_Request_pending); | |||
2099 | } | |||
2100 | ||||
2101 | ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2102 | ||||
2103 | if (ss->ss_state < nua_callstate_ready && | |||
2104 | ss->ss_state != nua_callstate_init) { | |||
2105 | return nua_server_retry_after(sr, 500, "Overlapping Requests 2", 0, 10); | |||
2106 | } | |||
2107 | } | |||
2108 | ||||
2109 | sr->sr_neutral = 0; | |||
2110 | ||||
2111 | return 0; | |||
2112 | } | |||
2113 | ||||
2114 | /** Initialize session server request. | |||
2115 | * | |||
2116 | * Ensure that the request is valid. | |||
2117 | */ | |||
2118 | static int | |||
2119 | nua_session_server_init(nua_server_request_t *sr) | |||
2120 | { | |||
2121 | nua_handle_t *nh = sr->sr_owner; | |||
2122 | nua_t *nua = nh->nh_nua; | |||
2123 | ||||
2124 | msg_t *msg = sr->sr_response.msg; | |||
2125 | sip_t *sip = sr->sr_response.sip; | |||
2126 | ||||
2127 | sip_t *request = (sip_t *) sr->sr_request.sip; | |||
2128 | ||||
2129 | if (!sr->sr_initial) | |||
2130 | sr->sr_usage = nua_dialog_usage_get(nh->nh_ds, nua_session_usage, NULL((void*)0)); | |||
2131 | ||||
2132 | if (sr->sr_method != sip_method_invite && sr->sr_usage == NULL((void*)0)) { | |||
2133 | /* UPDATE/PRACK sent within an existing dialog? */ | |||
2134 | return SR_STATUS(sr, 481, "Call Does Not Exist")((sr)->sr_phrase = ("Call Does Not Exist"), (sr)->sr_status = (481)); | |||
2135 | } | |||
2136 | else if (sr->sr_usage) { | |||
2137 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2138 | if (ss->ss_state >= nua_callstate_terminating) | |||
2139 | return SR_STATUS(sr, 481, "Call is being terminated")((sr)->sr_phrase = ("Call is being terminated"), (sr)-> sr_status = (481)); | |||
2140 | } | |||
2141 | ||||
2142 | if (nh->nh_soa) { | |||
2143 | sip_accept_t *a = nua->nua_invite_accept; | |||
2144 | ||||
2145 | /* XXX - soa should know what it supports */ | |||
2146 | sip_add_dup(msg, sip, (sip_header_t *)a); | |||
2147 | ||||
2148 | /* if we see there is a multipart content-type, | |||
2149 | parse it into the sip structre and find the SDP and replace it | |||
2150 | into the request as the requested content */ | |||
2151 | if (request->sip_content_type && | |||
2152 | su_casenmatch(request->sip_content_type->c_type, "multipart/", 10)) { | |||
2153 | msg_multipart_t *mp, *mpp; | |||
2154 | ||||
2155 | if (request->sip_multipart) { | |||
2156 | mp = request->sip_multipart; | |||
2157 | } else { | |||
2158 | mp = msg_multipart_parse(nua_handle_home(nh)((su_home_t *)(nh)), | |||
2159 | request->sip_content_type, | |||
2160 | (sip_payload_t *)request->sip_payload); | |||
2161 | request->sip_multipart = mp; | |||
2162 | } | |||
2163 | ||||
2164 | if (mp) { | |||
2165 | int sdp = 0; | |||
2166 | ||||
2167 | /* extract the SDP and set the primary content-type and payload to that SDP as if it was the only content so SOA will work */ | |||
2168 | for(mpp = mp; mpp; mpp = mpp->mp_next) { | |||
2169 | if (mpp->mp_content_type && mpp->mp_content_type->c_type && | |||
2170 | mpp->mp_payload && mpp->mp_payload->pl_data && | |||
2171 | su_casenmatch(mpp->mp_content_type->c_type, "application/sdp", 15)) { | |||
2172 | ||||
2173 | request->sip_content_type = msg_content_type_dup(nua_handle_home(nh)((su_home_t *)(nh)), mpp->mp_content_type); | |||
2174 | ||||
2175 | if (request->sip_content_length) { | |||
2176 | request->sip_content_length->l_length = mpp->mp_payload->pl_len; | |||
2177 | } | |||
2178 | ||||
2179 | request->sip_payload->pl_data = su_strdup(nua_handle_home(nh)((su_home_t *)(nh)), mpp->mp_payload->pl_data); | |||
2180 | request->sip_payload->pl_len = mpp->mp_payload->pl_len; | |||
2181 | ||||
2182 | sdp++; | |||
2183 | ||||
2184 | break; | |||
2185 | } | |||
2186 | } | |||
2187 | ||||
2188 | /* insist on the existance of a SDP in the content or refuse the request */ | |||
2189 | if (!sdp) { | |||
2190 | return SR_STATUS1(sr, SIP_406_NOT_ACCEPTABLE)sr_status(sr, 406, sip_406_Not_acceptable); | |||
2191 | } | |||
2192 | } | |||
2193 | } | |||
2194 | ||||
2195 | ||||
2196 | /* Make sure caller uses application/sdp without compression */ | |||
2197 | if (nta_check_session_content(NULL((void*)0), request, a, TAG_END()(tag_type_t)0, (tag_value_t)0)) { | |||
2198 | sip_add_make(msg, sip, sip_accept_encoding_class, ""); | |||
2199 | return SR_STATUS1(sr, SIP_415_UNSUPPORTED_MEDIA)sr_status(sr, 415, sip_415_Unsupported_media); | |||
2200 | } | |||
2201 | ||||
2202 | /* Make sure caller accepts application/sdp */ | |||
2203 | if (nta_check_accept(NULL((void*)0), request, a, NULL((void*)0), TAG_END()(tag_type_t)0, (tag_value_t)0)) { | |||
2204 | sip_add_make(msg, sip, sip_accept_encoding_class, ""); | |||
2205 | return SR_STATUS1(sr, SIP_406_NOT_ACCEPTABLE)sr_status(sr, 406, sip_406_Not_acceptable); | |||
2206 | } | |||
2207 | } | |||
2208 | ||||
2209 | if (request->sip_session_expires && | |||
2210 | sip_has_feature(NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), "timer") && | |||
2211 | session_timer_check_min_se(msg, sip, request, NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se))) { | |||
2212 | if (sip->sip_min_se) | |||
2213 | return SR_STATUS1(sr, SIP_422_SESSION_TIMER_TOO_SMALL)sr_status(sr, 422, sip_422_Session_timer); | |||
2214 | else | |||
2215 | return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR)sr_status(sr, 500, sip_500_Internal_server_error); | |||
2216 | } | |||
2217 | ||||
2218 | session_get_description(request, &sr->sr_sdp, &sr->sr_sdp_len); | |||
2219 | ||||
2220 | return 0; | |||
2221 | } | |||
2222 | ||||
2223 | /** Preprocess INVITE. | |||
2224 | * | |||
2225 | * This is called after a handle has been created for an incoming INVITE. | |||
2226 | */ | |||
2227 | int nua_invite_server_preprocess(nua_server_request_t *sr) | |||
2228 | { | |||
2229 | nua_handle_t *nh = sr->sr_owner; | |||
2230 | nua_dialog_state_t *ds = nh->nh_ds; | |||
2231 | nua_session_usage_t *ss; | |||
2232 | ||||
2233 | sip_t const *request = sr->sr_request.sip; | |||
2234 | ||||
2235 | assert(sr->sr_status == 100)((void) sizeof ((sr->sr_status == 100) ? 1 : 0), __extension__ ({ if (sr->sr_status == 100) ; else __assert_fail ("sr->sr_status == 100" , "nua_session.c", 2235, __extension__ __PRETTY_FUNCTION__); } )); | |||
2236 | assert(nh != nh->nh_nua->nua_dhandle)((void) sizeof ((nh != nh->nh_nua->nua_handles) ? 1 : 0 ), __extension__ ({ if (nh != nh->nh_nua->nua_handles) ; else __assert_fail ("nh != nh->nh_nua->nua_dhandle", "nua_session.c" , 2236, __extension__ __PRETTY_FUNCTION__); })); | |||
2237 | ||||
2238 | if (sr->sr_status > 100) | |||
2239 | return sr->sr_status; | |||
2240 | ||||
2241 | if (nh->nh_soa) | |||
2242 | soa_init_offer_answer(nh->nh_soa); | |||
2243 | ||||
2244 | if (sr->sr_sdp) { | |||
2245 | if (nh->nh_soa && | |||
2246 | soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sr->sr_sdp, sr->sr_sdp_len) < 0) { | |||
2247 | SU_DEBUG_5(("nua(%p): %s server: error parsing SDP\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2248, "nua(%p): %s server: error parsing SDP\n" , (void *)nh, "INVITE")) : (void)0) | |||
2248 | "INVITE"))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2248, "nua(%p): %s server: error parsing SDP\n" , (void *)nh, "INVITE")) : (void)0); | |||
2249 | return SR_STATUS(sr, 400, "Bad Session Description")((sr)->sr_phrase = ("Bad Session Description"), (sr)->sr_status = (400)); | |||
2250 | } | |||
2251 | else | |||
2252 | sr->sr_offer_recv = 1; | |||
2253 | } | |||
2254 | ||||
2255 | /* Add the session usage */ | |||
2256 | if (sr->sr_usage == NULL((void*)0)) { | |||
2257 | sr->sr_usage = nua_dialog_usage_add(nh, ds, nua_session_usage, NULL((void*)0)); | |||
2258 | if (sr->sr_usage == NULL((void*)0)) | |||
2259 | return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR)sr_status(sr, 500, sip_500_Internal_server_error); | |||
2260 | } | |||
2261 | ||||
2262 | ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2263 | ||||
2264 | if (sr->sr_offer_recv) | |||
2265 | ss->ss_oa_recv = Offer; | |||
2266 | ||||
2267 | ss->ss_100rel = NH_PGET(nh, early_media)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_early_media ? ( (nh)->nh_prefs)->nhp_early_media : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_early_media); | |||
2268 | ss->ss_precondition = sip_has_feature(request->sip_require, "precondition"); | |||
2269 | if (ss->ss_precondition) | |||
2270 | ss->ss_100rel = 1; | |||
2271 | ||||
2272 | session_timer_store(ss->ss_timer, request); | |||
2273 | ||||
2274 | #if 0 /* The glare and overlap tests should take care of this. */ | |||
2275 | assert(ss->ss_state >= nua_callstate_completed ||((void) sizeof ((ss->ss_state >= nua_callstate_completed || ss->ss_state == nua_callstate_init) ? 1 : 0), __extension__ ({ if (ss->ss_state >= nua_callstate_completed || ss-> ss_state == nua_callstate_init) ; else __assert_fail ("ss->ss_state >= nua_callstate_completed || ss->ss_state == nua_callstate_init" , "nua_session.c", 2276, __extension__ __PRETTY_FUNCTION__); } )) | |||
2276 | ss->ss_state == nua_callstate_init)((void) sizeof ((ss->ss_state >= nua_callstate_completed || ss->ss_state == nua_callstate_init) ? 1 : 0), __extension__ ({ if (ss->ss_state >= nua_callstate_completed || ss-> ss_state == nua_callstate_init) ; else __assert_fail ("ss->ss_state >= nua_callstate_completed || ss->ss_state == nua_callstate_init" , "nua_session.c", 2276, __extension__ __PRETTY_FUNCTION__); } )); | |||
2277 | #endif | |||
2278 | ||||
2279 | if (NH_PGET(nh, auto_answer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_answer ? ( (nh)->nh_prefs)->nhp_auto_answer : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_auto_answer) || | |||
2280 | /* Auto-answer to re-INVITE unless auto_answer is set to 0 on handle */ | |||
2281 | (ss->ss_state == nua_callstate_ready && | |||
2282 | /* Auto-answer requires enabled media (soa). | |||
2283 | * XXX - if the re-INVITE modifies the media we should not auto-answer. | |||
2284 | */ | |||
2285 | nh->nh_soa && | |||
2286 | !NH_PISSET(nh, auto_answer)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_answer) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ))) { | |||
2287 | SR_STATUS1(sr, SIP_200_OK)sr_status(sr, 200, sip_200_OK); | |||
2288 | } | |||
2289 | else if (NH_PGET(nh, auto_alert)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_alert ? ( (nh)->nh_prefs)->nhp_auto_alert : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_auto_alert)) { | |||
2290 | if (ss->ss_100rel && | |||
2291 | (sip_has_feature(request->sip_supported, "100rel") || | |||
2292 | sip_has_feature(request->sip_require, "100rel"))) { | |||
2293 | SR_STATUS1(sr, SIP_183_SESSION_PROGRESS)sr_status(sr, 183, sip_183_Session_progress); | |||
2294 | } | |||
2295 | else { | |||
2296 | SR_STATUS1(sr, SIP_180_RINGING)sr_status(sr, 180, sip_180_Ringing); | |||
2297 | } | |||
2298 | } | |||
2299 | ||||
2300 | return 0; | |||
2301 | } | |||
2302 | ||||
2303 | ||||
2304 | /** @internal Respond to an INVITE request. | |||
2305 | * | |||
2306 | */ | |||
2307 | static | |||
2308 | int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags) | |||
2309 | { | |||
2310 | nua_handle_t *nh = sr->sr_owner; | |||
2311 | nua_dialog_usage_t *du = sr->sr_usage; | |||
2312 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
2313 | msg_t *msg = sr->sr_response.msg; | |||
2314 | sip_t *sip = sr->sr_response.sip; | |||
2315 | ||||
2316 | int reliable = 0, maybe_answer = 0, offer = 0, answer = 0, extra = 0; | |||
2317 | ||||
2318 | enter(void)((((nua_log) != ((void*)0) && (nua_log)->log_init ) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 9 ? (_su_llog((nua_log), 9, "nua_session.c" , (const char *)__func__, 2318, "nua: %s: entering\n", __func__ )) : (void)0); | |||
2319 | ||||
2320 | if (du == NULL((void*)0)) { | |||
2321 | if (sr->sr_status < 300) | |||
2322 | sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR500, sip_500_Internal_server_error); | |||
2323 | return nua_base_server_respond(sr, tags); | |||
2324 | } | |||
2325 | ||||
2326 | if (200 <= sr->sr_status && sr->sr_status < 300) { | |||
2327 | reliable = 1, maybe_answer = 1; | |||
2328 | } | |||
2329 | else if (nua_invite_server_is_100rel(sr, tags)) { | |||
2330 | reliable = 1, maybe_answer = 1; | |||
2331 | } | |||
2332 | else if (!nh->nh_soa || sr->sr_status >= 300) { | |||
2333 | if (sr->sr_neutral) | |||
2334 | return nua_base_server_respond(sr, tags); | |||
2335 | } | |||
2336 | else if (tags && 100 < sr->sr_status && sr->sr_status < 200 && | |||
2337 | !NHP_ISSET(nh->nh_prefs, early_answer)((nh->nh_prefs)->nhp_set_.set_bits.nhb_early_answer)) { | |||
2338 | sdp_session_t const *user_sdp = NULL((void*)0); | |||
2339 | char const *user_sdp_str = NULL((void*)0); | |||
2340 | ||||
2341 | tl_gets(tags, | |||
2342 | SOATAG_USER_SDP_REF(user_sdp)soatag_user_sdp_ref, sdptag_session_vr(&(user_sdp)), | |||
2343 | SOATAG_USER_SDP_STR_REF(user_sdp_str)soatag_user_sdp_str_ref, tag_str_vr(&(user_sdp_str)), | |||
2344 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
2345 | ||||
2346 | maybe_answer = user_sdp || user_sdp_str; | |||
2347 | } | |||
2348 | else { | |||
2349 | maybe_answer = NH_PGET(nh, early_answer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_early_answer ? ((nh)->nh_prefs)->nhp_early_answer : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_early_answer); | |||
2350 | } | |||
2351 | ||||
2352 | if (!nh->nh_soa) { | |||
2353 | if (session_get_description(sip, NULL((void*)0), NULL((void*)0))) { | |||
2354 | if (sr->sr_offer_recv) | |||
2355 | answer = 1; | |||
2356 | else if (sr->sr_offer_sent < 2) | |||
2357 | offer = 1; | |||
2358 | } | |||
2359 | } | |||
2360 | else if (sr->sr_status >= 300) { | |||
2361 | soa_clear_remote_sdp(nh->nh_soa); | |||
2362 | } | |||
2363 | else if (sr->sr_offer_sent && !sr->sr_answer_recv) | |||
2364 | /* Wait for answer */; | |||
2365 | else if (sr->sr_offer_recv && sr->sr_answer_sent > 1) { | |||
2366 | /* We have sent answer */ | |||
2367 | /* ... but we may want to send it again */ | |||
2368 | tagi_t const *t = tl_find_last(tags, nutag_include_extra_sdp); | |||
2369 | extra = t && t->t_value; | |||
2370 | } | |||
2371 | else if (sr->sr_offer_recv && !sr->sr_answer_sent && maybe_answer) { | |||
2372 | /* Generate answer */ | |||
2373 | if (soa_generate_answer(nh->nh_soa, NULL((void*)0)) >= 0 && | |||
2374 | soa_activate(nh->nh_soa, NULL((void*)0)) >= 0) { | |||
2375 | answer = 1; /* signal that O/A answer sent (answer to invite) */ | |||
2376 | /* ss_sdp_version is updated only after answer is sent reliably */ | |||
2377 | } | |||
2378 | /* We have an error! */ | |||
2379 | else if (sr->sr_status >= 200) { | |||
2380 | sip_warning_t *warning = NULL((void*)0); | |||
2381 | int wcode; | |||
2382 | char const *text; | |||
2383 | char const *host = "invalid."; | |||
2384 | ||||
2385 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||
2386 | ||||
2387 | wcode = soa_get_warning(nh->nh_soa, &text); | |||
2388 | ||||
2389 | if (wcode) { | |||
2390 | if (sip->sip_contact) | |||
2391 | host = sip->sip_contact->m_url->url_host; | |||
2392 | warning = sip_warning_format(msg_home(msg)((su_home_t*)(msg)), "%u %s \"%s\"", | |||
2393 | wcode, host, text); | |||
2394 | sip_header_insert(msg, sip, (sip_header_t *)warning); | |||
2395 | } | |||
2396 | } | |||
2397 | else { | |||
2398 | /* 1xx - we don't have to send answer */ | |||
2399 | } | |||
2400 | } | |||
2401 | else if (sr->sr_offer_recv && sr->sr_answer_sent == 1 && maybe_answer) { | |||
2402 | /* The answer was sent unreliably, keep sending it */ | |||
2403 | answer = 1; | |||
2404 | } | |||
2405 | else if (!sr->sr_offer_recv && !sr->sr_offer_sent && reliable) { | |||
2406 | if (200 <= sr->sr_status && nua_callstate_ready <= ss->ss_state && | |||
2407 | NH_PGET(nh, refresh_without_sdp)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresh_without_sdp ? ((nh)->nh_prefs)->nhp_refresh_without_sdp : ((nh)-> nh_nua->nua_handles->nh_prefs)->nhp_refresh_without_sdp )) | |||
2408 | /* This is a re-INVITE without SDP - do not try to send offer in 200 */; | |||
2409 | else | |||
2410 | /* Generate offer */ | |||
2411 | if (soa_generate_offer(nh->nh_soa, 0, NULL((void*)0)) < 0) | |||
2412 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||
2413 | else | |||
2414 | offer = 1; | |||
2415 | } | |||
2416 | ||||
2417 | if (sr->sr_status < 300 && (offer || answer || extra)) { | |||
2418 | if (nh->nh_soa && session_include_description(nh->nh_soa, 1, msg, sip) < 0) | |||
2419 | SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR)sr_status(sr, 500, sip_500_Internal_server_error); | |||
2420 | else if (offer) | |||
2421 | sr->sr_offer_sent = 1 + reliable, ss->ss_oa_sent = Offer; | |||
2422 | else if (answer) | |||
2423 | sr->sr_answer_sent = 1 + reliable, ss->ss_oa_sent = Answer; | |||
2424 | ||||
2425 | if (answer && reliable && nh->nh_soa) { | |||
2426 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||
2427 | } | |||
2428 | } | |||
2429 | ||||
2430 | if (reliable && sr->sr_status < 200) { | |||
2431 | sr->sr_response.msg = NULL((void*)0), sr->sr_response.sip = NULL((void*)0); | |||
2432 | if (nta_reliable_mreply(sr->sr_irq, process_prack, sr, msg) == NULL((void*)0)) | |||
2433 | return -1; | |||
2434 | sr->sr_100rel = 1; | |||
2435 | return 0; | |||
2436 | } | |||
2437 | ||||
2438 | if (200 <= sr->sr_status && sr->sr_status < 300) { | |||
2439 | session_timer_preferences(ss->ss_timer, | |||
2440 | sip, | |||
2441 | NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), | |||
2442 | NH_PGET(nh, session_timer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_session_timer ? ((nh)->nh_prefs)->nhp_session_timer : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_session_timer), | |||
2443 | NUA_PISSET(nh->nh_nua, nh, session_timer)((((nh->nh_nua)->nua_handles->nh_prefs)->nhp_set_ .set_bits.nhb_session_timer) || ((nh) && (((nh)->nh_prefs )->nhp_set_.set_bits.nhb_session_timer))), | |||
2444 | NH_PGET(nh, refresher)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresher ? (( nh)->nh_prefs)->nhp_refresher : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_refresher), | |||
2445 | NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se)); | |||
2446 | ||||
2447 | if (session_timer_is_supported(ss->ss_timer)) | |||
2448 | session_timer_add_headers(ss->ss_timer, 0, msg, sip, nh); | |||
2449 | } | |||
2450 | ||||
2451 | return nua_base_server_respond(sr, tags); | |||
2452 | } | |||
2453 | ||||
2454 | /** Check if the response should be sent reliably. | |||
2455 | * XXX - use tags to indicate when to use reliable responses ??? | |||
2456 | */ | |||
2457 | static | |||
2458 | int nua_invite_server_is_100rel(nua_server_request_t *sr, tagi_t const *tags) | |||
2459 | { | |||
2460 | nua_handle_t *nh = sr->sr_owner; | |||
2461 | sip_require_t *require = sr->sr_request.sip->sip_require; | |||
2462 | sip_supported_t *supported = sr->sr_request.sip->sip_supported; | |||
2463 | ||||
2464 | if (sr->sr_status >= 200) | |||
2465 | return 0; | |||
2466 | else if (sr->sr_status == 100) | |||
2467 | return 0; | |||
2468 | ||||
2469 | if (sip_has_feature(sr->sr_response.sip->sip_require, "100rel")) | |||
2470 | return 1; | |||
2471 | ||||
2472 | if (require == NULL((void*)0) && supported == NULL((void*)0)) | |||
2473 | return 0; | |||
2474 | ||||
2475 | if (!sip_has_feature(NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), "100rel")) | |||
2476 | return 0; | |||
2477 | if (sip_has_feature(require, "100rel")) | |||
2478 | return 1; | |||
2479 | if (!sip_has_feature(supported, "100rel")) | |||
2480 | return 0; | |||
2481 | if (sr->sr_status == 183) | |||
2482 | return 1; | |||
2483 | ||||
2484 | if (NH_PGET(nh, early_media)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_early_media ? ( (nh)->nh_prefs)->nhp_early_media : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_early_media) && !NH_PGET(nh, only183_100rel)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_only183_100rel ? ((nh)->nh_prefs)->nhp_only183_100rel : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_only183_100rel)) | |||
2485 | return 1; | |||
2486 | ||||
2487 | if (sip_has_feature(require, "precondition")) { | |||
2488 | if (!NH_PGET(nh, only183_100rel)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_only183_100rel ? ((nh)->nh_prefs)->nhp_only183_100rel : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_only183_100rel)) | |||
2489 | return 1; | |||
2490 | if (sr->sr_offer_recv && !sr->sr_answer_sent) | |||
2491 | return 1; | |||
2492 | } | |||
2493 | ||||
2494 | return 0; | |||
2495 | } | |||
2496 | ||||
2497 | ||||
2498 | int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags) | |||
2499 | { | |||
2500 | nua_handle_t *nh = sr->sr_owner; | |||
2501 | nua_dialog_usage_t *du = sr->sr_usage; | |||
2502 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2503 | int initial = sr->sr_initial && !sr->sr_event; | |||
2504 | int neutral = sr->sr_neutral; | |||
2505 | int application = sr->sr_application; | |||
2506 | int status = sr->sr_status; char const *phrase = sr->sr_phrase; | |||
2507 | int retval; | |||
2508 | ||||
2509 | if (!sr->sr_event && status < 300) { /* Not reported yet */ | |||
2510 | nta_incoming_bind(sr->sr_irq, process_ack_or_cancel, sr); | |||
2511 | } | |||
2512 | ||||
2513 | retval = nua_base_server_report(sr, tags), sr = NULL((void*)0); /* destroys sr */ | |||
2514 | ||||
2515 | if (retval >= 2 || ss == NULL((void*)0)) { | |||
2516 | /* Session has been terminated. */ | |||
2517 | if (!initial && !neutral) { | |||
2518 | #if 0 | |||
2519 | signal_call_state_change(nh, NULL((void*)0), status, phrase, | |||
2520 | nua_callstate_terminated); | |||
2521 | #endif | |||
2522 | } | |||
2523 | return retval; | |||
2524 | } | |||
2525 | ||||
2526 | /* Update session state */ | |||
2527 | if (status < 300 || application != 0) { | |||
2528 | assert(ss->ss_state != nua_callstate_calling)((void) sizeof ((ss->ss_state != nua_callstate_calling) ? 1 : 0), __extension__ ({ if (ss->ss_state != nua_callstate_calling ) ; else __assert_fail ("ss->ss_state != nua_callstate_calling" , "nua_session.c", 2528, __extension__ __PRETTY_FUNCTION__); } )); | |||
2529 | assert(ss->ss_state != nua_callstate_proceeding)((void) sizeof ((ss->ss_state != nua_callstate_proceeding) ? 1 : 0), __extension__ ({ if (ss->ss_state != nua_callstate_proceeding ) ; else __assert_fail ("ss->ss_state != nua_callstate_proceeding" , "nua_session.c", 2529, __extension__ __PRETTY_FUNCTION__); } )); | |||
2530 | signal_call_state_change(nh, ss, status, phrase, | |||
2531 | status >= 300 | |||
2532 | ? nua_callstate_init | |||
2533 | : status >= 200 | |||
2534 | ? nua_callstate_completed | |||
2535 | : status > 100 | |||
2536 | ? nua_callstate_early | |||
2537 | : nua_callstate_received); | |||
2538 | } | |||
2539 | ||||
2540 | if (status == 180) | |||
2541 | ss->ss_alerting = 1; | |||
2542 | else if (status >= 200) | |||
2543 | ss->ss_alerting = 0; | |||
2544 | ||||
2545 | if (200 <= status && status < 300) { | |||
2546 | du->du_ready = 1; | |||
2547 | } | |||
2548 | else if (300 <= status && !neutral) { | |||
2549 | if (nh->nh_soa) | |||
2550 | soa_init_offer_answer(nh->nh_soa); | |||
2551 | } | |||
2552 | ||||
2553 | if (ss->ss_state == nua_callstate_init) { | |||
2554 | assert(status >= 300)((void) sizeof ((status >= 300) ? 1 : 0), __extension__ ({ if (status >= 300) ; else __assert_fail ("status >= 300" , "nua_session.c", 2554, __extension__ __PRETTY_FUNCTION__); } )); | |||
2555 | nua_session_usage_destroy(nh, ss); | |||
2556 | } | |||
2557 | ||||
2558 | return retval; | |||
2559 | } | |||
2560 | ||||
2561 | /** @internal Process ACK or CANCEL or timeout (no ACK) for incoming INVITE */ | |||
2562 | static | |||
2563 | int process_ack_or_cancel(nua_server_request_t *sr, | |||
2564 | nta_incoming_t *irq, | |||
2565 | sip_t const *sip) | |||
2566 | { | |||
2567 | enter(void)((((nua_log) != ((void*)0) && (nua_log)->log_init ) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 9 ? (_su_llog((nua_log), 9, "nua_session.c" , (const char *)__func__, 2567, "nua: %s: entering\n", __func__ )) : (void)0); | |||
2568 | ||||
2569 | assert(sr->sr_usage)((void) sizeof ((sr->sr_usage) ? 1 : 0), __extension__ ({ if (sr->sr_usage) ; else __assert_fail ("sr->sr_usage", "nua_session.c" , 2569, __extension__ __PRETTY_FUNCTION__); })); | |||
2570 | assert(sr->sr_usage->du_class == nua_session_usage)((void) sizeof ((sr->sr_usage->du_class == nua_session_usage ) ? 1 : 0), __extension__ ({ if (sr->sr_usage->du_class == nua_session_usage) ; else __assert_fail ("sr->sr_usage->du_class == nua_session_usage" , "nua_session.c", 2570, __extension__ __PRETTY_FUNCTION__); } )); | |||
2571 | ||||
2572 | if (sip && sip->sip_request->rq_method == sip_method_ack) | |||
2573 | return process_ack(sr, irq, sip); | |||
2574 | else if (sip && sip->sip_request->rq_method == sip_method_cancel) | |||
2575 | return process_cancel(sr, irq, sip); | |||
2576 | else | |||
2577 | return process_timeout(sr, irq); | |||
2578 | } | |||
2579 | ||||
2580 | /** @NUA_EVENT nua_i_ack | |||
2581 | * | |||
2582 | * Final response to INVITE has been acknowledged by UAC with ACK. | |||
2583 | * | |||
2584 | * @note This event is only sent after 2XX response. | |||
2585 | * | |||
2586 | * @param nh operation handle associated with the call | |||
2587 | * @param hmagic application context associated with the call | |||
2588 | * @param sip incoming ACK request | |||
2589 | * @param tags empty | |||
2590 | * | |||
2591 | * @sa #nua_i_invite, #nua_i_state, @ref nua_uas_call_model, nua_ack() | |||
2592 | * | |||
2593 | * @END_NUA_EVENT | |||
2594 | */ | |||
2595 | static | |||
2596 | int process_ack(nua_server_request_t *sr, | |||
2597 | nta_incoming_t *irq, | |||
2598 | sip_t const *sip) | |||
2599 | { | |||
2600 | nua_handle_t *nh = sr->sr_owner; | |||
2601 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2602 | msg_t *msg = nta_incoming_getrequest_ackcancel(irq); | |||
2603 | char const *recv = NULL((void*)0); | |||
2604 | int uas; | |||
2605 | ||||
2606 | if (ss == NULL((void*)0)) | |||
2607 | return 0; | |||
2608 | ||||
2609 | if (sr->sr_offer_sent && !sr->sr_answer_recv) { | |||
2610 | char const *sdp; | |||
2611 | size_t len; | |||
2612 | ||||
2613 | if (session_get_description(sip, &sdp, &len)) | |||
2614 | recv = Answer; | |||
2615 | ||||
2616 | if (recv) { | |||
2617 | assert(ss->ss_oa_recv == NULL)((void) sizeof ((ss->ss_oa_recv == ((void*)0)) ? 1 : 0), __extension__ ({ if (ss->ss_oa_recv == ((void*)0)) ; else __assert_fail ("ss->ss_oa_recv == NULL", "nua_session.c", 2617, __extension__ __PRETTY_FUNCTION__); })); | |||
2618 | ss->ss_oa_recv = recv; | |||
2619 | } | |||
2620 | ||||
2621 | if (nh->nh_soa == NULL((void*)0)) | |||
2622 | ; | |||
2623 | else if (recv == NULL((void*)0) ) { | |||
2624 | if (ss->ss_state >= nua_callstate_ready && | |||
2625 | soa_get_user_version(nh->nh_soa) == ss->ss_sdp_version && | |||
2626 | soa_process_reject(nh->nh_soa, NULL((void*)0)) >= 0) { | |||
2627 | url_t const *m; | |||
2628 | ||||
2629 | /* The re-INVITE was a refresh and re-INVITEr ignored our offer */ | |||
2630 | ss->ss_oa_sent = NULL((void*)0); | |||
2631 | ||||
2632 | if (sr->sr_request.sip->sip_contact) | |||
2633 | m = sr->sr_request.sip->sip_contact->m_url; | |||
2634 | else | |||
2635 | m = sr->sr_request.sip->sip_from->a_url; | |||
2636 | ||||
2637 | SU_DEBUG_3(("nua(%p): re-INVITEr ignored offer in our %u response "((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2639, "nua(%p): re-INVITEr ignored offer in our %u response " "(Contact: <" "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" ">)\n" , (void *)nh, sr->sr_status, (m)->url_scheme ? (m)-> url_scheme : "", (m)->url_type != url_any && (m)-> url_scheme && (m)->url_scheme[0] ? ":" : "", (m)-> url_root && ((m)->url_host || (m)->url_user) ? "//" : "", (m)->url_user ? (m)->url_user : "", (m)->url_user && (m)->url_password ? ":" : "", (m)->url_user && (m)->url_password ? (m)->url_password : "", (m)->url_user && (m)->url_host ? "@" : "", (m) ->url_host ? (m)->url_host : "", (m)->url_host && (m)->url_port ? ":" : "", (m)->url_host && (m) ->url_port ? (m)->url_port : "", (m)->url_root && (m)->url_path ? "/" : "", (m)->url_path ? (m)->url_path : "", (m)->url_params ? ";" : "", (m)->url_params ? (m )->url_params : "", (m)->url_headers ? "?" : "", (m)-> url_headers ? (m)->url_headers : "", (m)->url_fragment ? "#" : "", (m)->url_fragment ? (m)->url_fragment : "")) : (void)0) | |||
2638 | "(Contact: <" URL_PRINT_FORMAT ">)\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2639, "nua(%p): re-INVITEr ignored offer in our %u response " "(Contact: <" "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" ">)\n" , (void *)nh, sr->sr_status, (m)->url_scheme ? (m)-> url_scheme : "", (m)->url_type != url_any && (m)-> url_scheme && (m)->url_scheme[0] ? ":" : "", (m)-> url_root && ((m)->url_host || (m)->url_user) ? "//" : "", (m)->url_user ? (m)->url_user : "", (m)->url_user && (m)->url_password ? ":" : "", (m)->url_user && (m)->url_password ? (m)->url_password : "", (m)->url_user && (m)->url_host ? "@" : "", (m) ->url_host ? (m)->url_host : "", (m)->url_host && (m)->url_port ? ":" : "", (m)->url_host && (m) ->url_port ? (m)->url_port : "", (m)->url_root && (m)->url_path ? "/" : "", (m)->url_path ? (m)->url_path : "", (m)->url_params ? ";" : "", (m)->url_params ? (m )->url_params : "", (m)->url_headers ? "?" : "", (m)-> url_headers ? (m)->url_headers : "", (m)->url_fragment ? "#" : "", (m)->url_fragment ? (m)->url_fragment : "")) : (void)0) | |||
2639 | (void *)nh, sr->sr_status, URL_PRINT_ARGS(m)))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2639, "nua(%p): re-INVITEr ignored offer in our %u response " "(Contact: <" "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" ">)\n" , (void *)nh, sr->sr_status, (m)->url_scheme ? (m)-> url_scheme : "", (m)->url_type != url_any && (m)-> url_scheme && (m)->url_scheme[0] ? ":" : "", (m)-> url_root && ((m)->url_host || (m)->url_user) ? "//" : "", (m)->url_user ? (m)->url_user : "", (m)->url_user && (m)->url_password ? ":" : "", (m)->url_user && (m)->url_password ? (m)->url_password : "", (m)->url_user && (m)->url_host ? "@" : "", (m) ->url_host ? (m)->url_host : "", (m)->url_host && (m)->url_port ? ":" : "", (m)->url_host && (m) ->url_port ? (m)->url_port : "", (m)->url_root && (m)->url_path ? "/" : "", (m)->url_path ? (m)->url_path : "", (m)->url_params ? ";" : "", (m)->url_params ? (m )->url_params : "", (m)->url_headers ? "?" : "", (m)-> url_headers ? (m)->url_headers : "", (m)->url_fragment ? "#" : "", (m)->url_fragment ? (m)->url_fragment : "")) : (void)0); | |||
2640 | if (sr->sr_request.sip->sip_user_agent) | |||
2641 | SU_DEBUG_3(("nua(%p): re-INVITE: \"User-Agent: %s\"\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2642, "nua(%p): re-INVITE: \"User-Agent: %s\"\n" , (void *)nh, sr->sr_request.sip->sip_user_agent->g_string )) : (void)0) | |||
2642 | sr->sr_request.sip->sip_user_agent->g_string))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2642, "nua(%p): re-INVITE: \"User-Agent: %s\"\n" , (void *)nh, sr->sr_request.sip->sip_user_agent->g_string )) : (void)0); | |||
2643 | } | |||
2644 | else | |||
2645 | return process_ack_error(sr, msg, 488, "Offer-Answer error", | |||
2646 | "SIP;cause=488;text=\"No answer to offer\""); | |||
2647 | } | |||
2648 | else if (soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sdp, len) >= 0 && | |||
2649 | soa_process_answer(nh->nh_soa, NULL((void*)0)) >= 0 && | |||
2650 | soa_activate(nh->nh_soa, NULL((void*)0)) >= 0) { | |||
2651 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||
2652 | } | |||
2653 | else { | |||
2654 | int status; char const *phrase, *reason; | |||
2655 | ||||
2656 | status = soa_error_as_sip_response(nh->nh_soa, &phrase); | |||
2657 | reason = soa_error_as_sip_reason(nh->nh_soa); | |||
2658 | ||||
2659 | return process_ack_error(sr, msg, status, phrase, reason); | |||
2660 | } | |||
2661 | } | |||
2662 | ||||
2663 | if (nh->nh_soa) | |||
2664 | soa_clear_remote_sdp(nh->nh_soa); | |||
2665 | ||||
2666 | nua_stack_event(nh->nh_nua, nh, msg, nua_i_ack, SIP_200_OK200, sip_200_OK, NULL((void*)0)); | |||
2667 | signal_call_state_change(nh, ss, 200, "OK", nua_callstate_ready); | |||
2668 | session_timer_set(ss, uas = 1); | |||
2669 | ||||
2670 | nua_server_request_destroy(sr); | |||
2671 | ||||
2672 | return 0; | |||
2673 | } | |||
2674 | ||||
2675 | static int | |||
2676 | process_ack_error(nua_server_request_t *sr, | |||
2677 | msg_t *ackmsg, | |||
2678 | int status, | |||
2679 | char const *phrase, | |||
2680 | char const *reason) | |||
2681 | { | |||
2682 | nua_handle_t *nh = sr->sr_owner; | |||
2683 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2684 | int error; | |||
2685 | ||||
2686 | nua_stack_event(nh->nh_nua, nh, ackmsg, | |||
2687 | nua_i_ack, status, phrase, NULL((void*)0)); | |||
2688 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||
2689 | nua_i_media_error, status, phrase, NULL((void*)0)); | |||
2690 | ||||
2691 | if (reason) ss->ss_reason = reason; | |||
2692 | ss->ss_reporting = 1; | |||
2693 | error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)); | |||
2694 | ss->ss_reporting = 0; | |||
2695 | ||||
2696 | signal_call_state_change(nh, ss, | |||
2697 | 488, "Offer-Answer Error", | |||
2698 | /* We report terminated state if BYE failed */ | |||
2699 | error | |||
2700 | ? nua_callstate_terminated | |||
2701 | : nua_callstate_terminating); | |||
2702 | ||||
2703 | return 0; | |||
2704 | } | |||
2705 | ||||
2706 | ||||
2707 | /** @NUA_EVENT nua_i_cancel | |||
2708 | * | |||
2709 | * Incoming INVITE has been cancelled by the client. | |||
2710 | * | |||
2711 | * @param status status code of response to CANCEL sent automatically by stack | |||
2712 | * @param phrase a short textual description of @a status code | |||
2713 | * @param nh operation handle associated with the call | |||
2714 | * @param hmagic application context associated with the call | |||
2715 | * @param sip incoming CANCEL request | |||
2716 | * @param tags empty | |||
2717 | * | |||
2718 | * @sa @ref nua_uas_call_model, nua_cancel(), #nua_i_invite, #nua_i_state | |||
2719 | * | |||
2720 | * @END_NUA_EVENT | |||
2721 | */ | |||
2722 | ||||
2723 | /* CANCEL */ | |||
2724 | static | |||
2725 | int process_cancel(nua_server_request_t *sr, | |||
2726 | nta_incoming_t *irq, | |||
2727 | sip_t const *sip) | |||
2728 | { | |||
2729 | nua_handle_t *nh = sr->sr_owner; | |||
2730 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2731 | msg_t *cancel = nta_incoming_getrequest_ackcancel(irq); | |||
2732 | ||||
2733 | assert(ss)((void) sizeof ((ss) ? 1 : 0), __extension__ ({ if (ss) ; else __assert_fail ("ss", "nua_session.c", 2733, __extension__ __PRETTY_FUNCTION__ ); })); assert(ss == nua_session_usage_for_dialog(nh->nh_ds))((void) sizeof ((ss == nua_session_usage_for_dialog(nh->nh_ds )) ? 1 : 0), __extension__ ({ if (ss == nua_session_usage_for_dialog (nh->nh_ds)) ; else __assert_fail ("ss == nua_session_usage_for_dialog(nh->nh_ds)" , "nua_session.c", 2733, __extension__ __PRETTY_FUNCTION__); } )); (void)ss; | |||
2734 | ||||
2735 | assert(nta_incoming_status(irq) < 200)((void) sizeof ((nta_incoming_status(irq) < 200) ? 1 : 0), __extension__ ({ if (nta_incoming_status(irq) < 200) ; else __assert_fail ("nta_incoming_status(irq) < 200", "nua_session.c" , 2735, __extension__ __PRETTY_FUNCTION__); })); | |||
2736 | ||||
2737 | nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK200, sip_200_OK, NULL((void*)0)); | |||
2738 | sr->sr_application = SR_STATUS1(sr, SIP_487_REQUEST_TERMINATED)sr_status(sr, 487, sip_487_Request_terminated); | |||
2739 | nua_server_respond(sr, NULL((void*)0)); | |||
2740 | nua_server_report(sr); | |||
2741 | ||||
2742 | return 0; | |||
2743 | } | |||
2744 | ||||
2745 | /* Timeout (no ACK or PRACK received) */ | |||
2746 | static | |||
2747 | int process_timeout(nua_server_request_t *sr, | |||
2748 | nta_incoming_t *irq) | |||
2749 | { | |||
2750 | nua_handle_t *nh = sr->sr_owner; | |||
2751 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2752 | char const *phrase = "ACK Timeout"; | |||
2753 | char const *reason = "SIP;cause=408;text=\"ACK Timeout\""; | |||
2754 | int error; | |||
2755 | ||||
2756 | assert(ss)((void) sizeof ((ss) ? 1 : 0), __extension__ ({ if (ss) ; else __assert_fail ("ss", "nua_session.c", 2756, __extension__ __PRETTY_FUNCTION__ ); })); assert(ss == nua_session_usage_for_dialog(nh->nh_ds))((void) sizeof ((ss == nua_session_usage_for_dialog(nh->nh_ds )) ? 1 : 0), __extension__ ({ if (ss == nua_session_usage_for_dialog (nh->nh_ds)) ; else __assert_fail ("ss == nua_session_usage_for_dialog(nh->nh_ds)" , "nua_session.c", 2756, __extension__ __PRETTY_FUNCTION__); } )); | |||
2757 | ||||
2758 | if (nua_server_request_is_pending(sr)) { | |||
2759 | phrase = "PRACK Timeout"; | |||
2760 | reason = "SIP;cause=504;text=\"PRACK Timeout\""; | |||
2761 | } | |||
2762 | ||||
2763 | nua_stack_event(nh->nh_nua, nh, 0, nua_i_error, 408, phrase, NULL((void*)0)); | |||
2764 | ||||
2765 | if (nua_server_request_is_pending(sr)) { | |||
2766 | /* PRACK timeout */ | |||
2767 | SR_STATUS1(sr, SIP_504_GATEWAY_TIME_OUT)sr_status(sr, 504, sip_504_Gateway_time_out); | |||
2768 | nua_server_trespond(sr, | |||
2769 | SIPTAG_REASON_STR(reason)siptag_reason_str, tag_str_v(reason), | |||
2770 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
2771 | if (nua_server_report(sr) >= 2) | |||
2772 | return 0; /* Done */ | |||
2773 | sr = NULL((void*)0); | |||
2774 | } | |||
2775 | ||||
2776 | /* send BYE, too, if 200 OK (or 183 to re-INVITE) timeouts */ | |||
2777 | ss->ss_reason = reason; | |||
2778 | ||||
2779 | ss->ss_reporting = 1; /* We report terminated state here if BYE fails */ | |||
2780 | error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)); | |||
2781 | ss->ss_reporting = 0; | |||
2782 | ||||
2783 | signal_call_state_change(nh, ss, 0, phrase, | |||
2784 | error | |||
2785 | ? nua_callstate_terminated | |||
2786 | : nua_callstate_terminating); | |||
2787 | ||||
2788 | if (sr) | |||
2789 | nua_server_request_destroy(sr); | |||
2790 | ||||
2791 | return 0; | |||
2792 | } | |||
2793 | ||||
2794 | ||||
2795 | /** @NUA_EVENT nua_i_prack | |||
2796 | * | |||
2797 | * Incoming PRACK request. PRACK request is used to acknowledge reliable | |||
2798 | * preliminary responses and it is usually sent automatically by the nua | |||
2799 | * stack. | |||
2800 | * | |||
2801 | * @param status status code of response sent automatically by stack | |||
2802 | * @param phrase a short textual description of @a status code | |||
2803 | * @param nh operation handle associated with the call | |||
2804 | * @param hmagic application context associated with the call | |||
2805 | * @param sip incoming PRACK request | |||
2806 | * @param tags empty | |||
2807 | * | |||
2808 | * @sa nua_prack(), #nua_r_prack, @RFC3262, NUTAG_EARLY_MEDIA() | |||
2809 | * | |||
2810 | * @END_NUA_EVENT | |||
2811 | */ | |||
2812 | ||||
2813 | int nua_prack_server_init(nua_server_request_t *sr); | |||
2814 | int nua_prack_server_respond(nua_server_request_t *sr, tagi_t const *tags); | |||
2815 | int nua_prack_server_report(nua_server_request_t *sr, tagi_t const *tags); | |||
2816 | ||||
2817 | nua_server_methods_t const nua_prack_server_methods = | |||
2818 | { | |||
2819 | SIP_METHOD_PRACKsip_method_prack, "PRACK", | |||
2820 | nua_i_prack, /* Event */ | |||
2821 | { | |||
2822 | 0, /* Do not create dialog */ | |||
2823 | 1, /* In-dialog request */ | |||
2824 | 1, /* Target refresh request */ | |||
2825 | 1, /* Add Contact */ | |||
2826 | }, | |||
2827 | nua_prack_server_init, | |||
2828 | nua_base_server_preprocess((void*)0), | |||
2829 | nua_base_server_params((void*)0), | |||
2830 | nua_prack_server_respond, | |||
2831 | nua_prack_server_report, | |||
2832 | }; | |||
2833 | ||||
2834 | /** @internal Process reliable response PRACK or (timeout from 100rel) */ | |||
2835 | static int process_prack(nua_server_request_t *sr, | |||
2836 | nta_reliable_t *rel, | |||
2837 | nta_incoming_t *irq, | |||
2838 | sip_t const *sip) | |||
2839 | { | |||
2840 | nua_handle_t *nh; | |||
2841 | ||||
2842 | nta_reliable_destroy(rel); | |||
2843 | ||||
2844 | if (irq == NULL((void*)0)) | |||
2845 | /* Final response interrupted 100rel, we did not actually receive PRACK */ | |||
2846 | return 200; | |||
2847 | ||||
2848 | sr->sr_pracked = 1; | |||
2849 | ||||
2850 | if (!nua_server_request_is_pending(sr)) /* There is no INVITE anymore */ | |||
2851 | return 481; | |||
2852 | ||||
2853 | nh = sr->sr_owner; | |||
2854 | ||||
2855 | if (nh->nh_ds->ds_leg == NULL((void*)0)) | |||
2856 | return 500; | |||
2857 | ||||
2858 | if (sip == NULL((void*)0)) { | |||
2859 | /* 100rel timeout */ | |||
2860 | SR_STATUS(sr, 504, "Reliable Response Timeout")((sr)->sr_phrase = ("Reliable Response Timeout"), (sr)-> sr_status = (504)); | |||
2861 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), nua_i_error, | |||
2862 | sr->sr_status, sr->sr_phrase, | |||
2863 | NULL((void*)0)); | |||
2864 | nua_server_trespond(sr, | |||
2865 | SIPTAG_REASON_STR("SIP;cause=504;"siptag_reason_str, tag_str_v("SIP;cause=504;" "text=\"PRACK Timeout\"" ) | |||
2866 | "text=\"PRACK Timeout\"")siptag_reason_str, tag_str_v("SIP;cause=504;" "text=\"PRACK Timeout\"" ), | |||
2867 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
2868 | nua_server_report(sr); | |||
2869 | return 504; | |||
2870 | } | |||
2871 | ||||
2872 | nta_incoming_bind(irq, NULL((void*)0), (void *)sr); | |||
2873 | ||||
2874 | return nua_stack_process_request(nh, nh->nh_ds->ds_leg, irq, sip); | |||
2875 | } | |||
2876 | ||||
2877 | ||||
2878 | int nua_prack_server_init(nua_server_request_t *sr) | |||
2879 | { | |||
2880 | nua_handle_t *nh = sr->sr_owner; | |||
2881 | nua_server_request_t *sri = nta_incoming_magic(sr->sr_irq, NULL((void*)0)); | |||
2882 | ||||
2883 | if (sri == NULL((void*)0)) | |||
2884 | return SR_STATUS(sr, 481, "No Such Preliminary Response")((sr)->sr_phrase = ("No Such Preliminary Response"), (sr)-> sr_status = (481)); | |||
2885 | ||||
2886 | if (nua_session_server_init(sr)) | |||
2887 | return sr->sr_status; | |||
2888 | ||||
2889 | if (sr->sr_sdp) { | |||
2890 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(sr->sr_usage)((void *)((sr->sr_usage) + 1)); | |||
2891 | char const *offeranswer; | |||
2892 | ||||
2893 | /* XXX - check for overlap? */ | |||
2894 | ||||
2895 | if (sri->sr_offer_sent && !sri->sr_answer_recv) | |||
2896 | sr->sr_answer_recv = 1, sri->sr_answer_recv = 1, offeranswer = Answer; | |||
2897 | else | |||
2898 | sr->sr_offer_recv = 1, offeranswer = Offer; | |||
2899 | ||||
2900 | ss->ss_oa_recv = offeranswer; | |||
2901 | ||||
2902 | if (nh->nh_soa && | |||
2903 | soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sr->sr_sdp, sr->sr_sdp_len) < 0) { | |||
2904 | SU_DEBUG_5(("nua(%p): %s server: error parsing %s\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2905, "nua(%p): %s server: error parsing %s\n" , (void *)nh, "PRACK", offeranswer)) : (void)0) | |||
2905 | "PRACK", offeranswer))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2905, "nua(%p): %s server: error parsing %s\n" , (void *)nh, "PRACK", offeranswer)) : (void)0); | |||
2906 | return | |||
2907 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||
2908 | } | |||
2909 | } | |||
2910 | ||||
2911 | return 0; | |||
2912 | } | |||
2913 | ||||
2914 | int nua_prack_server_respond(nua_server_request_t *sr, tagi_t const *tags) | |||
2915 | { | |||
2916 | nua_handle_t *nh = sr->sr_owner; | |||
2917 | ||||
2918 | if (sr->sr_status < 200 || 300 <= sr->sr_status) | |||
2919 | return nua_base_server_respond(sr, tags); | |||
2920 | ||||
2921 | if (sr->sr_sdp) { | |||
2922 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2923 | msg_t *msg = sr->sr_response.msg; | |||
2924 | sip_t *sip = sr->sr_response.sip; | |||
2925 | ||||
2926 | if (nh->nh_soa == NULL((void*)0)) { | |||
2927 | if (sr->sr_offer_recv && session_get_description(sip, NULL((void*)0), NULL((void*)0))) | |||
2928 | sr->sr_answer_sent = 1, ss ? ss->ss_oa_sent = Answer : Answer; | |||
2929 | } | |||
2930 | else if ((sr->sr_offer_recv && soa_generate_answer(nh->nh_soa, NULL((void*)0)) < 0) || | |||
2931 | (sr->sr_answer_recv && soa_process_answer(nh->nh_soa, NULL((void*)0)) < 0)) { | |||
2932 | SU_DEBUG_5(("nua(%p): %s server: %s %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2935, "nua(%p): %s server: %s %s\n" , (void *)nh, "PRACK", "error processing", sr->sr_offer_recv ? Offer : Answer)) : (void)0) | |||
2933 | (void *)nh, "PRACK",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2935, "nua(%p): %s server: %s %s\n" , (void *)nh, "PRACK", "error processing", sr->sr_offer_recv ? Offer : Answer)) : (void)0) | |||
2934 | "error processing",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2935, "nua(%p): %s server: %s %s\n" , (void *)nh, "PRACK", "error processing", sr->sr_offer_recv ? Offer : Answer)) : (void)0) | |||
2935 | sr->sr_offer_recv ? Offer : Answer))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2935, "nua(%p): %s server: %s %s\n" , (void *)nh, "PRACK", "error processing", sr->sr_offer_recv ? Offer : Answer)) : (void)0); | |||
2936 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||
2937 | } | |||
2938 | else if (sr->sr_offer_recv) { | |||
2939 | if (session_include_description(nh->nh_soa, 1, msg, sip) < 0) | |||
2940 | sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR500, sip_500_Internal_server_error); | |||
2941 | else | |||
2942 | sr->sr_answer_sent = 1, ss ? ss->ss_oa_sent = Answer : Answer; | |||
2943 | } | |||
2944 | } | |||
2945 | ||||
2946 | return nua_base_server_respond(sr, tags); | |||
2947 | } | |||
2948 | ||||
2949 | int nua_prack_server_report(nua_server_request_t *sr, tagi_t const *tags) | |||
2950 | { | |||
2951 | nua_handle_t *nh = sr->sr_owner; | |||
2952 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
2953 | nua_server_request_t *sri = nta_incoming_magic(sr->sr_irq, NULL((void*)0)); | |||
2954 | int status = sr->sr_status; char const *phrase = sr->sr_phrase; | |||
2955 | int offer_recv_or_answer_sent = sr->sr_offer_recv || sr->sr_answer_sent || sr->sr_offer_sent || sr->sr_answer_recv; | |||
2956 | int retval; | |||
2957 | ||||
2958 | retval = nua_base_server_report(sr, tags), sr = NULL((void*)0); /* destroys sr */ | |||
2959 | ||||
2960 | if (retval >= 2 || ss == NULL((void*)0)) { | |||
2961 | #if 0 | |||
2962 | signal_call_state_change(nh, NULL((void*)0), | |||
2963 | status, phrase, | |||
2964 | nua_callstate_terminated); | |||
2965 | #endif | |||
2966 | return retval; | |||
2967 | } | |||
2968 | ||||
2969 | if (offer_recv_or_answer_sent) { | |||
2970 | /* signal offer received, answer sent */ | |||
2971 | signal_call_state_change(nh, ss, | |||
2972 | status, phrase, | |||
2973 | ss->ss_state); | |||
2974 | if (nh->nh_soa) { | |||
2975 | soa_activate(nh->nh_soa, NULL((void*)0)); | |||
2976 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||
2977 | } | |||
2978 | } | |||
2979 | ||||
2980 | if (status < 200 || 300 <= status) | |||
2981 | return retval; | |||
2982 | ||||
2983 | assert(sri)((void) sizeof ((sri) ? 1 : 0), __extension__ ({ if (sri) ; else __assert_fail ("sri", "nua_session.c", 2983, __extension__ __PRETTY_FUNCTION__ ); })); | |||
2984 | ||||
2985 | if (sri == NULL((void*)0)) { | |||
2986 | ||||
2987 | } | |||
2988 | else if (SR_HAS_SAVED_SIGNAL(sri)((sri)->sr_signal[0] != ((void*)0))) { | |||
2989 | nua_signal_data_t const *e; | |||
2990 | ||||
2991 | e = nua_signal_data(sri->sr_signal); | |||
2992 | ||||
2993 | sri->sr_application = SR_STATUS(sri, e->e_status, e->e_phrase)((sri)->sr_phrase = (e->e_phrase), (sri)->sr_status = (e->e_status)); | |||
2994 | ||||
2995 | nua_server_params(sri, e->e_tags); | |||
2996 | nua_server_respond(sri, e->e_tags); | |||
2997 | nua_server_report(sri); | |||
2998 | } | |||
2999 | else if (ss->ss_state < nua_callstate_ready | |||
3000 | && !ss->ss_alerting | |||
3001 | && !ss->ss_precondition | |||
3002 | && NH_PGET(nh, auto_alert)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_alert ? ( (nh)->nh_prefs)->nhp_auto_alert : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_auto_alert)) { | |||
3003 | SR_STATUS1(sri, SIP_180_RINGING)sr_status(sri, 180, sip_180_Ringing); | |||
3004 | nua_server_respond(sri, NULL((void*)0)); | |||
3005 | nua_server_report(sri); | |||
3006 | } | |||
3007 | ||||
3008 | return retval; | |||
3009 | } | |||
3010 | ||||
3011 | /* ---------------------------------------------------------------------- */ | |||
3012 | /* Automatic notifications from a referral */ | |||
3013 | ||||
3014 | static int | |||
3015 | nh_referral_check(nua_handle_t *nh, tagi_t const *tags) | |||
3016 | { | |||
3017 | sip_event_t const *event = NULL((void*)0); | |||
3018 | int pause = 1; | |||
3019 | struct nua_referral *ref = nh->nh_referral; | |||
3020 | nua_handle_t *ref_handle = ref->ref_handle; | |||
3021 | ||||
3022 | if (!ref_handle | |||
3023 | && | |||
3024 | tl_gets(tags, | |||
3025 | NUTAG_NOTIFY_REFER_REF(ref_handle)nutag_notify_refer_ref, nutag_handle_vr(&(ref_handle)), | |||
3026 | NUTAG_REFER_EVENT_REF(event)nutag_refer_event_ref, siptag_event_vr(&(event)), | |||
3027 | NUTAG_REFER_PAUSE_REF(pause)nutag_refer_pause_ref, tag_bool_vr(&(pause)), | |||
3028 | TAG_END()(tag_type_t)0, (tag_value_t)0) == 0 | |||
3029 | && | |||
3030 | tl_gets(nh->nh_tags, | |||
3031 | NUTAG_NOTIFY_REFER_REF(ref_handle)nutag_notify_refer_ref, nutag_handle_vr(&(ref_handle)), | |||
3032 | NUTAG_REFER_EVENT_REF(event)nutag_refer_event_ref, siptag_event_vr(&(event)), | |||
3033 | NUTAG_REFER_PAUSE_REF(pause)nutag_refer_pause_ref, tag_bool_vr(&(pause)), | |||
3034 | TAG_END()(tag_type_t)0, (tag_value_t)0) == 0) | |||
3035 | return 0; | |||
3036 | ||||
3037 | if (!ref_handle) | |||
3038 | return 0; | |||
3039 | ||||
3040 | /* Remove nh_referral and nh_notevent */ | |||
3041 | tl_tremove(nh->nh_tags, | |||
3042 | NUTAG_NOTIFY_REFER(ref_handle)nutag_notify_refer, nutag_handle_v(ref_handle), | |||
3043 | TAG_IF(event, NUTAG_REFER_EVENT(event))!(event) ? tag_skip : nutag_refer_event, siptag_event_v(event ), | |||
3044 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
3045 | ||||
3046 | if (event) | |||
3047 | ref->ref_event = sip_event_dup(nh->nh_home, event); | |||
3048 | ||||
3049 | if (!nh_validate(nh->nh_nua, ref_handle)) { | |||
3050 | SU_DEBUG_3(("nua: invalid NOTIFY_REFER handle\n" VA_NONE))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 3050, "nua: invalid NOTIFY_REFER handle\n" "%s", "")) : (void)0); | |||
3051 | return -1; | |||
3052 | } | |||
3053 | else if (!ref->ref_event) { | |||
3054 | SU_DEBUG_3(("nua: NOTIFY event missing\n" VA_NONE))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 3054, "nua: NOTIFY event missing\n" "%s", "")) : (void)0); | |||
3055 | return -1; | |||
3056 | } | |||
3057 | ||||
3058 | if (ref_handle != ref->ref_handle) { | |||
3059 | if (ref->ref_handle) | |||
3060 | nua_handle_unref(ref->ref_handle); | |||
3061 | ref->ref_handle = nua_handle_ref(ref_handle); | |||
3062 | } | |||
3063 | ||||
3064 | #if 0 | |||
3065 | if (pause) { | |||
3066 | /* Pause media on REFER handle */ | |||
3067 | nmedia_pause(nua, ref_handle->nh_nm, NULL((void*)0)); | |||
3068 | } | |||
3069 | #endif | |||
3070 | ||||
3071 | return 0; | |||
3072 | } | |||
3073 | ||||
3074 | static void | |||
3075 | nh_referral_respond(nua_handle_t *nh, int status, char const *phrase) | |||
3076 | { | |||
3077 | char payload[128]; | |||
3078 | char const *substate; | |||
3079 | struct nua_referral *ref = nh->nh_referral; | |||
3080 | ||||
3081 | if (!nh_validate(nh->nh_nua, ref->ref_handle)) { | |||
3082 | if (ref) { | |||
3083 | if (ref->ref_handle) | |||
3084 | SU_DEBUG_1(("nh_handle_referral: stale referral handle %p\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 1 ? (_su_llog((nua_log), 1, "nua_session.c" , (const char *)__func__, 3085, "nh_handle_referral: stale referral handle %p\n" , (void *)ref->ref_handle)) : (void)0) | |||
3085 | (void *)ref->ref_handle))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 1 ? (_su_llog((nua_log), 1, "nua_session.c" , (const char *)__func__, 3085, "nh_handle_referral: stale referral handle %p\n" , (void *)ref->ref_handle)) : (void)0); | |||
3086 | ref->ref_handle = NULL((void*)0); | |||
3087 | } | |||
3088 | return; | |||
3089 | } | |||
3090 | ||||
3091 | /* XXX - we should have a policy here whether to send 101..199 */ | |||
3092 | ||||
3093 | assert(ref->ref_event)((void) sizeof ((ref->ref_event) ? 1 : 0), __extension__ ( { if (ref->ref_event) ; else __assert_fail ("ref->ref_event" , "nua_session.c", 3093, __extension__ __PRETTY_FUNCTION__); } )); | |||
3094 | ||||
3095 | if (status >= 300) | |||
3096 | status = 503, phrase = sip_503_Service_unavailable; | |||
3097 | ||||
3098 | snprintf(payload, sizeof(payload), "SIP/2.0 %03u %s\r\n", status, phrase); | |||
3099 | ||||
3100 | if (status < 200) | |||
3101 | substate = "active"; | |||
3102 | else | |||
3103 | substate = "terminated ;reason=noresource"; | |||
3104 | ||||
3105 | nua_stack_post_signal(ref->ref_handle, | |||
3106 | nua_r_notify, | |||
3107 | SIPTAG_EVENT(ref->ref_event)siptag_event, siptag_event_v(ref->ref_event), | |||
3108 | SIPTAG_SUBSCRIPTION_STATE_STR(substate)siptag_subscription_state_str, tag_str_v(substate), | |||
3109 | SIPTAG_CONTENT_TYPE_STR("message/sipfrag")siptag_content_type_str, tag_str_v("message/sipfrag"), | |||
3110 | SIPTAG_PAYLOAD_STR(payload)siptag_payload_str, tag_str_v(payload), | |||
3111 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
3112 | ||||
3113 | if (status < 200) | |||
3114 | return; | |||
3115 | ||||
3116 | su_free(nh->nh_home, ref->ref_event), ref->ref_event = NULL((void*)0); | |||
3117 | ||||
3118 | nua_handle_unref(ref->ref_handle), ref->ref_handle = NULL((void*)0); | |||
3119 | } | |||
3120 | ||||
3121 | /* ======================================================================== */ | |||
3122 | /* INFO */ | |||
3123 | ||||
3124 | /**@fn void nua_info(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||
3125 | * | |||
3126 | * Send an INFO request. | |||
3127 | * | |||
3128 | * INFO is used to send call related information like DTMF | |||
3129 | * digit input events. See @RFC2976. | |||
3130 | * | |||
3131 | * @param nh Pointer to operation handle | |||
3132 | * @param tag, value, ... List of tagged parameters | |||
3133 | * | |||
3134 | * @return | |||
3135 | * nothing | |||
3136 | * | |||
3137 | * @par Related Tags: | |||
3138 | * Header tags defined in <sofia-sip/sip_tag.h>. | |||
3139 | * | |||
3140 | * @par Events: | |||
3141 | * #nua_r_info | |||
3142 | * | |||
3143 | * @sa #nua_i_info | |||
3144 | */ | |||
3145 | ||||
3146 | nua_client_methods_t const nua_info_client_methods = { | |||
3147 | SIP_METHOD_INFOsip_method_info, "INFO", /* crm_method, crm_method_name */ | |||
3148 | 0, /* crm_extra */ | |||
3149 | { /* crm_flags */ | |||
3150 | /* create_dialog */ 0, | |||
3151 | /* in_dialog */ 1, | |||
3152 | /* target refresh */ 0 | |||
3153 | }, | |||
3154 | NULL((void*)0), /* crm_template */ | |||
3155 | NULL((void*)0), /* crm_init */ | |||
3156 | NULL((void*)0), /* crm_send */ | |||
3157 | NULL((void*)0), /* crm_check_restart */ | |||
3158 | NULL((void*)0), /* crm_recv */ | |||
3159 | NULL((void*)0), /* crm_preliminary */ | |||
3160 | NULL((void*)0), /* crm_report */ | |||
3161 | NULL((void*)0), /* crm_complete */ | |||
3162 | }; | |||
3163 | ||||
3164 | int | |||
3165 | nua_stack_info(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags) | |||
3166 | { | |||
3167 | return nua_client_create(nh, e, &nua_info_client_methods, tags); | |||
3168 | } | |||
3169 | ||||
3170 | /** @NUA_EVENT nua_r_info | |||
3171 | * | |||
3172 | * Response to an outgoing @b INFO request. | |||
3173 | * | |||
3174 | * @param status response status code | |||
3175 | * (if the request is retried, @a status is 100, the @a | |||
3176 | * sip->sip_status->st_status contain the real status code | |||
3177 | * from the response message, e.g., 302, 401, or 407) | |||
3178 | * @param phrase a short textual description of @a status code | |||
3179 | * @param nh operation handle associated with the call | |||
3180 | * @param hmagic application context associated with the call | |||
3181 | * @param sip response to @b INFO or NULL upon an error | |||
3182 | * (status code is in @a status and | |||
3183 | * descriptive message in @a phrase parameters) | |||
3184 | * @param tags empty | |||
3185 | * | |||
3186 | * @sa nua_info(), #nua_i_info, @RFC2976 | |||
3187 | * | |||
3188 | * @END_NUA_EVENT | |||
3189 | */ | |||
3190 | ||||
3191 | /** @NUA_EVENT nua_i_info | |||
3192 | * | |||
3193 | * Incoming session INFO request. | |||
3194 | * | |||
3195 | * @param status statuscode of response sent automatically by stack | |||
3196 | * @param phrase a short textual description of @a status code | |||
3197 | * @param nh operation handle associated with the call | |||
3198 | * @param hmagic application context associated with the call | |||
3199 | * @param sip incoming INFO request | |||
3200 | * @param tags empty | |||
3201 | * | |||
3202 | * @sa nua_info(), #nua_r_info, @RFC2976 | |||
3203 | * | |||
3204 | * @END_NUA_EVENT | |||
3205 | */ | |||
3206 | ||||
3207 | nua_server_methods_t const nua_info_server_methods = | |||
3208 | { | |||
3209 | SIP_METHOD_INFOsip_method_info, "INFO", | |||
3210 | nua_i_info, /* Event */ | |||
3211 | { | |||
3212 | 0, /* Do not create dialog */ | |||
3213 | 0, /* Allow outside dialog, too */ | |||
3214 | 0, /* Not a target refresh request */ | |||
3215 | 0, /* Do not add Contact */ | |||
3216 | }, | |||
3217 | nua_base_server_init((void*)0), | |||
3218 | nua_base_server_preprocess((void*)0), | |||
3219 | nua_base_server_params((void*)0), | |||
3220 | nua_base_server_respond, | |||
3221 | nua_base_server_report, | |||
3222 | }; | |||
3223 | ||||
3224 | /* ======================================================================== */ | |||
3225 | /* UPDATE */ | |||
3226 | ||||
3227 | /**@fn void nua_update(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||
3228 | * | |||
3229 | * Update a session. | |||
3230 | * | |||
3231 | * Update a session using SIP UPDATE method. See @RFC3311. | |||
3232 | * | |||
3233 | * Update method can be used when the session has been established with | |||
3234 | * INVITE. It's mainly used during the session establishment when | |||
3235 | * preconditions are used (@RFC3312). It can be also used during the call if | |||
3236 | * no user input is needed for offer/answer negotiation. | |||
3237 | * | |||
3238 | * @param nh Pointer to operation handle | |||
3239 | * @param tag, value, ... List of tagged parameters | |||
3240 | * | |||
3241 | * @return | |||
3242 | * nothing | |||
3243 | * | |||
3244 | * @par Related Tags: | |||
3245 | * same as nua_invite() | |||
3246 | * | |||
3247 | * @par Events: | |||
3248 | * #nua_r_update \n | |||
3249 | * #nua_i_state (#nua_i_active, #nua_i_terminated)\n | |||
3250 | * #nua_i_media_error \n | |||
3251 | * | |||
3252 | * @sa @ref nua_call_model, @RFC3311, nua_update(), #nua_i_update | |||
3253 | */ | |||
3254 | ||||
3255 | static int nua_update_client_init(nua_client_request_t *cr, | |||
3256 | msg_t *msg, sip_t *sip, | |||
3257 | tagi_t const *tags); | |||
3258 | static int nua_update_client_request(nua_client_request_t *cr, | |||
3259 | msg_t *msg, sip_t *sip, | |||
3260 | tagi_t const *tags); | |||
3261 | static int nua_update_client_response(nua_client_request_t *cr, | |||
3262 | int status, char const *phrase, | |||
3263 | sip_t const *sip); | |||
3264 | static int nua_update_client_report(nua_client_request_t *cr, | |||
3265 | int status, char const *phrase, | |||
3266 | sip_t const *sip, | |||
3267 | nta_outgoing_t *orq, | |||
3268 | tagi_t const *tags); | |||
3269 | ||||
3270 | nua_client_methods_t const nua_update_client_methods = { | |||
3271 | SIP_METHOD_UPDATEsip_method_update, "UPDATE", /* crm_method, crm_method_name */ | |||
3272 | 0, /* crm_extrasize of private data */ | |||
3273 | { /* crm_flags */ | |||
3274 | /* create_dialog */ 0, | |||
3275 | /* in_dialog */ 1, | |||
3276 | /* target refresh */ 1 | |||
3277 | }, | |||
3278 | NULL((void*)0), /* crm_template */ | |||
3279 | nua_update_client_init, /* crm_init */ | |||
3280 | nua_update_client_request, /* crm_send */ | |||
3281 | session_timer_check_restart, /* crm_check_restart */ | |||
3282 | nua_update_client_response, /* crm_recv */ | |||
3283 | NULL((void*)0), /* crm_preliminary */ | |||
3284 | nua_update_client_report, /* crm_report */ | |||
3285 | NULL((void*)0), /* crm_complete */ | |||
3286 | }; | |||
3287 | ||||
3288 | int nua_stack_update(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||
3289 | tagi_t const *tags) | |||
3290 | { | |||
3291 | return nua_client_create(nh, e, &nua_update_client_methods, tags); | |||
3292 | } | |||
3293 | ||||
3294 | static int nua_update_client_init(nua_client_request_t *cr, | |||
3295 | msg_t *msg, sip_t *sip, | |||
3296 | tagi_t const *tags) | |||
3297 | { | |||
3298 | nua_handle_t *nh = cr->cr_owner; | |||
3299 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||
3300 | ||||
3301 | cr->cr_usage = du; | |||
3302 | ||||
3303 | return 0; | |||
3304 | } | |||
3305 | ||||
3306 | static int nua_update_client_request(nua_client_request_t *cr, | |||
3307 | msg_t *msg, sip_t *sip, | |||
3308 | tagi_t const *tags) | |||
3309 | { | |||
3310 | nua_handle_t *nh = cr->cr_owner; | |||
3311 | nua_dialog_usage_t *du = cr->cr_usage; | |||
3312 | nua_session_usage_t *ss; | |||
3313 | nua_server_request_t *sr; | |||
3314 | nua_client_request_t *cri; | |||
3315 | int offer_sent = 0, retval; | |||
3316 | ||||
3317 | if (du == NULL((void*)0)) /* Call terminated */ | |||
3318 | return nua_client_return(cr, SIP_481_NO_TRANSACTION481, sip_481_No_transaction, msg); | |||
3319 | ||||
3320 | ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||
3321 | if (ss->ss_state >= nua_callstate_terminating) | |||
3322 | return nua_client_return(cr, 900, "Session is terminating", msg); | |||
3323 | ||||
3324 | cri = du->du_cr; | |||
3325 | ||||
3326 | for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next) | |||
3327 | if ((sr->sr_offer_sent && !sr->sr_answer_recv) || | |||
3328 | (sr->sr_offer_recv && !sr->sr_answer_sent)) | |||
3329 | break; | |||
3330 | ||||
3331 | if (nh->nh_soa == NULL((void*)0)) { | |||
3332 | offer_sent = session_get_description(sip, NULL((void*)0), NULL((void*)0)); | |||
3333 | } | |||
3334 | else if (sr || | |||
3335 | (cri && cri->cr_offer_sent && !cri->cr_answer_recv) || | |||
3336 | (cri && cri->cr_offer_recv && !cri->cr_answer_sent)) { | |||
3337 | if (session_get_description(sip, NULL((void*)0), NULL((void*)0))) | |||
3338 | return nua_client_return(cr, 500, "Overlapping Offer/Answer", msg); | |||
3339 | } | |||
3340 | else if (!sip->sip_payload) { | |||
3341 | soa_init_offer_answer(nh->nh_soa); | |||
3342 | ||||
3343 | if (soa_generate_offer(nh->nh_soa, 0, NULL((void*)0)) < 0 || | |||
3344 | session_include_description(nh->nh_soa, 1, msg, sip) < 0) { | |||
3345 | if (ss->ss_state < nua_callstate_ready) { | |||
3346 | /* XXX - use soa_error_as_sip_reason(nh->nh_soa) */ | |||
3347 | cr->cr_graceful = 1; | |||
3348 | ss->ss_reason = "SIP;cause=400;text=\"Local media failure\""; | |||
3349 | } | |||
3350 | return nua_client_return(cr, 900, "Local media failed", msg); | |||
3351 | } | |||
3352 | offer_sent = 1; | |||
3353 | } | |||
3354 | ||||
3355 | /* Add session timer headers */ | |||
3356 | session_timer_preferences(ss->ss_timer, | |||
3357 | sip, | |||
3358 | NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), | |||
3359 | NH_PGET(nh, session_timer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_session_timer ? ((nh)->nh_prefs)->nhp_session_timer : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_session_timer), | |||
3360 | NUA_PISSET(nh->nh_nua, nh, session_timer)((((nh->nh_nua)->nua_handles->nh_prefs)->nhp_set_ .set_bits.nhb_session_timer) || ((nh) && (((nh)->nh_prefs )->nhp_set_.set_bits.nhb_session_timer))), | |||
3361 | NH_PGET(nh, refresher)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresher ? (( nh)->nh_prefs)->nhp_refresher : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_refresher), | |||
3362 | NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se)); | |||
3363 | ||||
3364 | if (session_timer_is_supported(ss->ss_timer)) | |||
3365 | session_timer_add_headers(ss->ss_timer, ss->ss_state < nua_callstate_ready, | |||
3366 | msg, sip, nh); | |||
3367 | ||||
3368 | retval = nua_base_client_request(cr, msg, sip, NULL((void*)0)); | |||
3369 | ||||
3370 | if (retval == 0) { | |||
3371 | enum nua_callstate state = ss->ss_state; | |||
3372 | cr->cr_offer_sent = offer_sent; | |||
3373 | ss->ss_update_needed = 0; | |||
3374 | ||||
3375 | if (state == nua_callstate_ready) | |||
3376 | state = nua_callstate_calling; /* XXX */ | |||
3377 | ||||
3378 | if (offer_sent) | |||
3379 | ss->ss_oa_sent = Offer; | |||
3380 | ||||
3381 | if (!cr->cr_restarting) /* Restart logic calls nua_update_client_report */ | |||
3382 | signal_call_state_change(nh, ss, 0, "UPDATE sent", state); | |||
3383 | } | |||
3384 | ||||
3385 | return retval; | |||
3386 | } | |||
3387 | ||||
3388 | static int nua_update_client_response(nua_client_request_t *cr, | |||
3389 | int status, char const *phrase, | |||
3390 | sip_t const *sip) | |||
3391 | { | |||
3392 | nua_handle_t *nh = cr->cr_owner; | |||
3393 | nua_dialog_usage_t *du = cr->cr_usage; | |||
3394 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
3395 | int uas; | |||
3396 | ||||
3397 | assert(200 <= status)((void) sizeof ((200 <= status) ? 1 : 0), __extension__ ({ if (200 <= status) ; else __assert_fail ("200 <= status" , "nua_session.c", 3397, __extension__ __PRETTY_FUNCTION__); } )); | |||
3398 | ||||
3399 | if (ss && sip && status < 300) { | |||
3400 | if (session_timer_is_supported(ss->ss_timer)) { | |||
3401 | nua_server_request_t *sr; | |||
3402 | ||||
3403 | for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next) | |||
3404 | if (sr->sr_method == sip_method_invite || | |||
3405 | sr->sr_method == sip_method_update) | |||
3406 | break; | |||
3407 | ||||
3408 | if (!sr && (!du->du_cr || !du->du_cr->cr_orq)) { | |||
3409 | session_timer_store(ss->ss_timer, sip); | |||
3410 | session_timer_set(ss, uas = 0); | |||
3411 | } | |||
3412 | } | |||
3413 | } | |||
3414 | ||||
3415 | return nua_session_client_response(cr, status, phrase, sip); | |||
3416 | } | |||
3417 | ||||
3418 | /** @NUA_EVENT nua_r_update | |||
3419 | * | |||
3420 | * Answer to outgoing UPDATE. | |||
3421 | * | |||
3422 | * The UPDATE may be sent explicitly by nua_update() or | |||
3423 | * implicitly by NUA state machine. | |||
3424 | * | |||
3425 | * @param status response status code | |||
3426 | * (if the request is retried, @a status is 100, the @a | |||
3427 | * sip->sip_status->st_status contain the real status code | |||
3428 | * from the response message, e.g., 302, 401, or 407) | |||
3429 | * @param phrase a short textual description of @a status code | |||
3430 | * @param nh operation handle associated with the call | |||
3431 | * @param hmagic application context associated with the call | |||
3432 | * @param sip response to UPDATE request or NULL upon an error | |||
3433 | * (status code is in @a status and | |||
3434 | * descriptive message in @a phrase parameters) | |||
3435 | * @param tags empty | |||
3436 | * | |||
3437 | * @sa @ref nua_call_model, @RFC3311, nua_update(), #nua_i_update | |||
3438 | * | |||
3439 | * @END_NUA_EVENT | |||
3440 | */ | |||
3441 | ||||
3442 | static int nua_update_client_report(nua_client_request_t *cr, | |||
3443 | int status, char const *phrase, | |||
3444 | sip_t const *sip, | |||
3445 | nta_outgoing_t *orq, | |||
3446 | tagi_t const *tags) | |||
3447 | { | |||
3448 | nua_handle_t *nh = cr->cr_owner; | |||
3449 | nua_dialog_usage_t *du = cr->cr_usage; | |||
3450 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
3451 | ||||
3452 | nua_stack_event(nh->nh_nua, nh, | |||
3453 | nta_outgoing_getresponse(orq), | |||
3454 | (enum nua_event_e)cr->cr_event, | |||
3455 | status, phrase, | |||
3456 | tags); | |||
3457 | ||||
3458 | if (!ss || cr->cr_terminated || cr->cr_graceful || cr->cr_waiting) | |||
3459 | return 1; | |||
3460 | ||||
3461 | if (cr->cr_offer_sent) { | |||
3462 | unsigned next_state = ss->ss_state; | |||
3463 | ||||
3464 | if (status < 200) | |||
3465 | ; | |||
3466 | else if (nua_invite_client_should_ack(du->du_cr)) { | |||
3467 | /* There is an un-ACK-ed INVITE there */ | |||
3468 | assert(du->du_cr->cr_method == sip_method_invite)((void) sizeof ((du->du_cr->cr_method == sip_method_invite ) ? 1 : 0), __extension__ ({ if (du->du_cr->cr_method == sip_method_invite) ; else __assert_fail ("du->du_cr->cr_method == sip_method_invite" , "nua_session.c", 3468, __extension__ __PRETTY_FUNCTION__); } )); | |||
3469 | ||||
3470 | if (NH_PGET(nh, auto_ack)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack ? ((nh )->nh_prefs)->nhp_auto_ack : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_auto_ack) || | |||
3471 | /* Auto-ACK response to re-INVITE when media is enabled | |||
3472 | and auto_ack is not set to 0 on handle */ | |||
3473 | (ss->ss_state == nua_callstate_ready && nh->nh_soa && | |||
3474 | !NH_PISSET(nh, auto_ack)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ))) { | |||
3475 | if (nua_invite_client_ack(du->du_cr, NULL((void*)0)) > 0) | |||
3476 | next_state = nua_callstate_ready; | |||
3477 | else | |||
3478 | next_state = nua_callstate_terminating; | |||
3479 | } | |||
3480 | } | |||
3481 | ||||
3482 | signal_call_state_change(nh, ss, status, phrase, (enum nua_callstate)next_state); | |||
3483 | } | |||
3484 | ||||
3485 | return 1; | |||
3486 | } | |||
3487 | ||||
3488 | /* ---------------------------------------------------------------------- */ | |||
3489 | /* UPDATE server */ | |||
3490 | ||||
3491 | int nua_update_server_init(nua_server_request_t *sr); | |||
3492 | int nua_update_server_respond(nua_server_request_t *sr, tagi_t const *tags); | |||
3493 | int nua_update_server_report(nua_server_request_t *, tagi_t const *); | |||
3494 | ||||
3495 | nua_server_methods_t const nua_update_server_methods = | |||
3496 | { | |||
3497 | SIP_METHOD_UPDATEsip_method_update, "UPDATE", | |||
3498 | nua_i_update, /* Event */ | |||
3499 | { | |||
3500 | 0, /* Do not create dialog */ | |||
3501 | 1, /* In-dialog request */ | |||
3502 | 1, /* Target refresh request */ | |||
3503 | 1, /* Add Contact */ | |||
3504 | }, | |||
3505 | nua_update_server_init, | |||
3506 | nua_base_server_preprocess((void*)0), | |||
3507 | nua_base_server_params((void*)0), | |||
3508 | nua_update_server_respond, | |||
3509 | nua_update_server_report, | |||
3510 | }; | |||
3511 | ||||
3512 | int nua_update_server_init(nua_server_request_t *sr) | |||
3513 | { | |||
3514 | nua_handle_t *nh = sr->sr_owner; | |||
3515 | nua_session_usage_t *ss; | |||
3516 | ||||
3517 | sip_t const *request = sr->sr_request.sip; | |||
3518 | ||||
3519 | if (nua_session_server_init(sr)) | |||
| ||||
3520 | return sr->sr_status; | |||
3521 | ||||
3522 | ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
3523 | ||||
3524 | /* Do session timer negotiation */ | |||
3525 | if (request->sip_session_expires) | |||
3526 | session_timer_store(ss->ss_timer, request); | |||
3527 | ||||
3528 | if (sr->sr_sdp) { /* Check for overlap */ | |||
3529 | nua_client_request_t *cr; | |||
3530 | nua_server_request_t *sr0; | |||
3531 | int overlap = 0; | |||
3532 | ||||
3533 | /* | |||
3534 | A UAS that receives an UPDATE before it has generated a final | |||
3535 | response to a previous UPDATE on the same dialog MUST return a 500 | |||
3536 | response to the new UPDATE, and MUST include a Retry-After header | |||
3537 | field with a randomly chosen value between 0 and 10 seconds. | |||
3538 | ||||
3539 | If an UPDATE is received that contains an offer, and the UAS has | |||
3540 | generated an offer (in an UPDATE, PRACK or INVITE) to which it has | |||
3541 | not yet received an answer, the UAS MUST reject the UPDATE with a 491 | |||
3542 | response. Similarly, if an UPDATE is received that contains an | |||
3543 | offer, and the UAS has received an offer (in an UPDATE, PRACK, or | |||
3544 | INVITE) to which it has not yet generated an answer, the UAS MUST | |||
3545 | reject the UPDATE with a 500 response, and MUST include a Retry-After | |||
3546 | header field with a randomly chosen value between 0 and 10 seconds. | |||
3547 | */ | |||
3548 | for (cr = nh->nh_ds->ds_cr; cr; cr = cr->cr_next) | |||
3549 | if ((overlap = cr->cr_offer_sent && !cr->cr_answer_recv)) | |||
3550 | break; | |||
3551 | ||||
3552 | if (!overlap) | |||
3553 | for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr0->sr_next) | |||
3554 | if ((overlap = sr0->sr_offer_recv && !sr0->sr_answer_sent)) | |||
3555 | break; | |||
3556 | ||||
3557 | if (nh->nh_soa && overlap) { | |||
3558 | return nua_server_retry_after(sr, 500, "Overlapping Offer/Answer", 1, 9); | |||
3559 | } | |||
3560 | ||||
3561 | if (nh->nh_soa && | |||
3562 | soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sr->sr_sdp, sr->sr_sdp_len) < 0) { | |||
3563 | SU_DEBUG_5(("nua(%p): %s server: error parsing %s\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3564, "nua(%p): %s server: error parsing %s\n" , (void *)nh, "UPDATE", Offer)) : (void)0) | |||
3564 | "UPDATE", Offer))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3564, "nua(%p): %s server: error parsing %s\n" , (void *)nh, "UPDATE", Offer)) : (void)0); | |||
3565 | return | |||
3566 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||
3567 | } | |||
3568 | ||||
3569 | sr->sr_offer_recv = 1; | |||
3570 | ss ? ss->ss_oa_recv = Offer : Offer; | |||
3571 | } | |||
3572 | ||||
3573 | return 0; | |||
3574 | } | |||
3575 | ||||
3576 | /** @internal Respond to an UPDATE request. | |||
3577 | * | |||
3578 | */ | |||
3579 | int nua_update_server_respond(nua_server_request_t *sr, tagi_t const *tags) | |||
3580 | { | |||
3581 | nua_handle_t *nh = sr->sr_owner; | |||
3582 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||
3583 | msg_t *msg = sr->sr_response.msg; | |||
3584 | sip_t *sip = sr->sr_response.sip; | |||
3585 | ||||
3586 | if (200 <= sr->sr_status && sr->sr_status < 300 && sr->sr_sdp) { | |||
3587 | if (nh->nh_soa == NULL((void*)0)) { | |||
3588 | sr->sr_answer_sent = 1, ss ? ss->ss_oa_sent = Answer : Answer; | |||
3589 | } | |||
3590 | else if (soa_generate_answer(nh->nh_soa, NULL((void*)0)) < 0) { | |||
3591 | SU_DEBUG_5(("nua(%p): %s server: %s %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3592, "nua(%p): %s server: %s %s\n" , (void *)nh, "UPDATE", "error processing", Offer)) : (void)0 ) | |||
3592 | (void *)nh, "UPDATE", "error processing", Offer))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3592, "nua(%p): %s server: %s %s\n" , (void *)nh, "UPDATE", "error processing", Offer)) : (void)0 ); | |||
3593 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||
3594 | } | |||
3595 | else if (soa_activate(nh->nh_soa, NULL((void*)0)) < 0) { | |||
3596 | SU_DEBUG_5(("nua(%p): %s server: error activating media\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3597, "nua(%p): %s server: error activating media\n" , (void *)nh, "UPDATE")) : (void)0) | |||
3597 | (void *)nh, "UPDATE"))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3597, "nua(%p): %s server: error activating media\n" , (void *)nh, "UPDATE")) : (void)0); | |||
3598 | /* XXX */ | |||
3599 | } | |||
3600 | else if (session_include_description(nh->nh_soa, 1, msg, sip) < 0) { | |||
3601 | sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR500, sip_500_Internal_server_error); | |||
3602 | } | |||
3603 | else { | |||
3604 | sr->sr_answer_sent = 1; | |||
3605 | if (ss) { | |||
3606 | ss->ss_oa_sent = Answer; | |||
3607 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||
3608 | } | |||
3609 | } | |||
3610 | } | |||
3611 | ||||
3612 | if (ss && 200 <= sr->sr_status && sr->sr_status < 300) { | |||
3613 | session_timer_preferences(ss->ss_timer, | |||
3614 | sip, | |||
3615 | NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), | |||
3616 | NH_PGET(nh, session_timer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_session_timer ? ((nh)->nh_prefs)->nhp_session_timer : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_session_timer), | |||
3617 | NUA_PISSET(nh->nh_nua, nh, session_timer)((((nh->nh_nua)->nua_handles->nh_prefs)->nhp_set_ .set_bits.nhb_session_timer) || ((nh) && (((nh)->nh_prefs )->nhp_set_.set_bits.nhb_session_timer))), | |||
3618 | NH_PGET(nh, refresher)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresher ? (( nh)->nh_prefs)->nhp_refresher : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_refresher), | |||
3619 | NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se)); | |||
3620 | ||||
3621 | if (session_timer_is_supported(ss->ss_timer)) { | |||
3622 | nua_server_request_t *sr0; | |||
3623 | int uas; | |||
3624 | ||||
3625 | session_timer_add_headers(ss->ss_timer, 0, msg, sip, nh); | |||
3626 | ||||
3627 | for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr0->sr_next) | |||
3628 | if (sr0->sr_method == sip_method_invite) | |||
3629 | break; | |||
3630 | ||||
3631 | if (!sr0 && (!sr->sr_usage->du_cr || !sr->sr_usage->du_cr->cr_orq)) | |||
3632 | session_timer_set(ss, uas = 1); | |||
3633 | } | |||
3634 | } | |||
3635 | ||||
3636 | return nua_base_server_respond(sr, tags); | |||
3637 | } | |||
3638 | ||||
3639 | /** @NUA_EVENT nua_i_update | |||
3640 | * | |||
3641 | * @brief Incoming session UPDATE request. | |||
3642 | * | |||
3643 | * @param status statuscode of response sent automatically by stack | |||
3644 | * @param phrase a short textual description of @a status code | |||
3645 | * @param nh operation handle associated with the call | |||
3646 | * @param hmagic application context associated with the call | |||
3647 | * @param sip incoming UPDATE request | |||
3648 | * @param tags empty | |||
3649 | * | |||
3650 | * @sa nua_update(), #nua_r_update, #nua_i_state | |||
3651 | * | |||
3652 | * @END_NUA_EVENT | |||
3653 | */ | |||
3654 | ||||
3655 | int nua_update_server_report(nua_server_request_t *sr, tagi_t const *tags) | |||
3656 | { | |||
3657 | nua_handle_t *nh = sr->sr_owner; | |||
3658 | nua_dialog_usage_t *du = sr->sr_usage; | |||
3659 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
3660 | int status = sr->sr_status; char const *phrase = sr->sr_phrase; | |||
3661 | int offer_recv_or_answer_sent = sr->sr_offer_recv || sr->sr_answer_sent; | |||
3662 | int retval; | |||
3663 | ||||
3664 | retval = nua_base_server_report(sr, tags), sr = NULL((void*)0); /* destroys sr */ | |||
3665 | ||||
3666 | if (retval >= 2 || ss == NULL((void*)0)) { | |||
3667 | #if 0 | |||
3668 | signal_call_state_change(nh, NULL((void*)0), status, phrase, | |||
3669 | nua_callstate_terminated); | |||
3670 | #endif | |||
3671 | return retval; | |||
3672 | } | |||
3673 | ||||
3674 | if (offer_recv_or_answer_sent) { | |||
3675 | /* signal offer received, answer sent */ | |||
3676 | enum nua_callstate state = ss->ss_state; | |||
3677 | ||||
3678 | if (state == nua_callstate_ready && status < 200) | |||
3679 | state = nua_callstate_received; | |||
3680 | ||||
3681 | signal_call_state_change(nh, ss, status, phrase, state); | |||
3682 | } | |||
3683 | ||||
3684 | if (200 <= status && status < 300 | |||
3685 | && ss->ss_state < nua_callstate_ready | |||
3686 | && ss->ss_precondition | |||
3687 | && !ss->ss_alerting | |||
3688 | && NH_PGET(nh, auto_alert)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_alert ? ( (nh)->nh_prefs)->nhp_auto_alert : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_auto_alert)) { | |||
3689 | nua_server_request_t *sri; | |||
3690 | ||||
3691 | for (sri = nh->nh_ds->ds_sr; sri; sri = sri->sr_next) | |||
3692 | if (sri->sr_method == sip_method_invite && | |||
3693 | nua_server_request_is_pending(sri)) | |||
3694 | break; | |||
3695 | ||||
3696 | if (sri) { | |||
3697 | SR_STATUS1(sri, SIP_180_RINGING)sr_status(sri, 180, sip_180_Ringing); | |||
3698 | nua_server_respond(sri, NULL((void*)0)); | |||
3699 | nua_server_report(sri); | |||
3700 | } | |||
3701 | } | |||
3702 | ||||
3703 | return retval; | |||
3704 | } | |||
3705 | ||||
3706 | /* ======================================================================== */ | |||
3707 | /* BYE */ | |||
3708 | ||||
3709 | /**@fn void nua_bye(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||
3710 | * | |||
3711 | * Hangdown a call. | |||
3712 | * | |||
3713 | * Hangdown a call using SIP BYE method. Also the media session | |||
3714 | * associated with the call is terminated. | |||
3715 | * | |||
3716 | * @param nh Pointer to operation handle | |||
3717 | * @param tag, value, ... List of tagged parameters | |||
3718 | * | |||
3719 | * @return | |||
3720 | * nothing | |||
3721 | * | |||
3722 | * @par Related Tags: | |||
3723 | * none | |||
3724 | * | |||
3725 | * @par Events: | |||
3726 | * #nua_r_bye \n | |||
3727 | * #nua_i_media_error | |||
3728 | */ | |||
3729 | ||||
3730 | static int nua_bye_client_init(nua_client_request_t *cr, | |||
3731 | msg_t *msg, sip_t *sip, | |||
3732 | tagi_t const *tags); | |||
3733 | static int nua_bye_client_request(nua_client_request_t *cr, | |||
3734 | msg_t *msg, sip_t *sip, | |||
3735 | tagi_t const *tags); | |||
3736 | static int nua_bye_client_response(nua_client_request_t *cr, | |||
3737 | int status, char const *phrase, | |||
3738 | sip_t const *sip); | |||
3739 | static int nua_bye_client_report(nua_client_request_t *cr, | |||
3740 | int status, char const *phrase, | |||
3741 | sip_t const *sip, | |||
3742 | nta_outgoing_t *orq, | |||
3743 | tagi_t const *tags); | |||
3744 | ||||
3745 | nua_client_methods_t const nua_bye_client_methods = { | |||
3746 | SIP_METHOD_BYEsip_method_bye, "BYE", /* crm_method, crm_method_name */ | |||
3747 | 0, /* crm_extrasize */ | |||
3748 | { /* crm_flags */ | |||
3749 | /* create_dialog */ 0, | |||
3750 | /* in_dialog */ 1, | |||
3751 | /* target refresh */ 0 | |||
3752 | }, | |||
3753 | NULL((void*)0), /* crm_template */ | |||
3754 | nua_bye_client_init, /* crm_init */ | |||
3755 | nua_bye_client_request, /* crm_send */ | |||
3756 | NULL((void*)0), /* crm_check_restart */ | |||
3757 | nua_bye_client_response, /* crm_recv */ | |||
3758 | NULL((void*)0), /* crm_preliminary */ | |||
3759 | nua_bye_client_report, /* crm_report */ | |||
3760 | NULL((void*)0), /* crm_complete */ | |||
3761 | }; | |||
3762 | ||||
3763 | int | |||
3764 | nua_stack_bye(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags) | |||
3765 | { | |||
3766 | nua_session_usage_t *ss = nua_session_usage_for_dialog(nh->nh_ds); | |||
3767 | ||||
3768 | if (ss && | |||
3769 | nua_callstate_calling <= ss->ss_state && | |||
3770 | ss->ss_state <= nua_callstate_proceeding) | |||
3771 | return nua_client_create(nh, e, &nua_cancel_client_methods, tags); | |||
3772 | else | |||
3773 | return nua_client_create(nh, e, &nua_bye_client_methods, tags); | |||
3774 | } | |||
3775 | ||||
3776 | static int nua_bye_client_init(nua_client_request_t *cr, | |||
3777 | msg_t *msg, sip_t *sip, | |||
3778 | tagi_t const *tags) | |||
3779 | { | |||
3780 | nua_handle_t *nh = cr->cr_owner; | |||
3781 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||
3782 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
3783 | ||||
3784 | if (!ss || (ss->ss_state >= nua_callstate_terminating && !cr->cr_auto)) | |||
3785 | return nua_client_return(cr, 900, "Invalid handle for BYE", msg); | |||
3786 | ||||
3787 | if (!cr->cr_auto) | |||
3788 | /* Implicit state transition by nua_bye() */ | |||
3789 | ss->ss_state = nua_callstate_terminating; | |||
3790 | ||||
3791 | if (nh->nh_soa) | |||
3792 | soa_terminate(nh->nh_soa, 0); | |||
3793 | ||||
3794 | nua_client_bind(cr, du); | |||
3795 | ||||
3796 | return 0; | |||
3797 | } | |||
3798 | ||||
3799 | static int nua_bye_client_request(nua_client_request_t *cr, | |||
3800 | msg_t *msg, sip_t *sip, | |||
3801 | tagi_t const *tags) | |||
3802 | { | |||
3803 | nua_dialog_usage_t *du = cr->cr_usage; | |||
3804 | nua_session_usage_t *ss; | |||
3805 | char const *reason = NULL((void*)0); | |||
3806 | ||||
3807 | int error; | |||
3808 | nua_server_request_t *sr; | |||
3809 | ||||
3810 | if (du == NULL((void*)0)) | |||
3811 | return nua_client_return(cr, SIP_481_NO_TRANSACTION481, sip_481_No_transaction, msg); | |||
3812 | ||||
3813 | ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
3814 | reason = ss->ss_reason; | |||
3815 | ||||
3816 | error = nua_base_client_trequest(cr, msg, sip, | |||
3817 | SIPTAG_REASON_STR(reason)siptag_reason_str, tag_str_v(reason), | |||
3818 | TAG_NEXT(tags)tag_next, (tag_value_t)(tags)); | |||
3819 | ||||
3820 | if (error == 0) { | |||
3821 | nua_dialog_usage_reset_refresh(du); | |||
3822 | ss->ss_timer->timer_set = 0; | |||
3823 | ||||
3824 | /* Terminate server transactions associated with session, too. */ | |||
3825 | for (sr = du->du_dialog->ds_sr; sr; sr = sr->sr_next) { | |||
3826 | if (sr->sr_usage == du && nua_server_request_is_pending(sr) && | |||
3827 | sr->sr_method != sip_method_bye) { | |||
3828 | sr_status(sr, SIP_486_BUSY_HERE486, sip_486_Busy_here); | |||
3829 | nua_server_respond(sr, 0); | |||
3830 | } | |||
3831 | } | |||
3832 | } | |||
3833 | ||||
3834 | return error; | |||
3835 | } | |||
3836 | static int nua_bye_client_response(nua_client_request_t *cr, | |||
3837 | int status, char const *phrase, | |||
3838 | sip_t const *sip) { | |||
3839 | ||||
3840 | nua_dialog_usage_t *du = cr->cr_usage; | |||
3841 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
3842 | ||||
3843 | if (ss && ss->ss_reporting && status >= 900) | |||
3844 | return 1; | |||
3845 | ||||
3846 | return nua_base_client_response(cr, status, phrase, sip, NULL((void*)0)); | |||
3847 | } | |||
3848 | ||||
3849 | /** @NUA_EVENT nua_r_bye | |||
3850 | * | |||
3851 | * Answer to outgoing BYE. | |||
3852 | * | |||
3853 | * The BYE may be sent explicitly by nua_bye() or implicitly by NUA state | |||
3854 | * machine. | |||
3855 | * | |||
3856 | * @param status response status code | |||
3857 | * (if the request is retried, @a status is 100, the @a | |||
3858 | * sip->sip_status->st_status contain the real status code | |||
3859 | * from the response message, e.g., 302, 401, or 407) | |||
3860 | * @param phrase a short textual description of @a status code | |||
3861 | * @param nh operation handle associated with the call | |||
3862 | * @param hmagic application context associated with the call | |||
3863 | * @param sip response to BYE request or NULL upon an error | |||
3864 | * (status code is in @a status and | |||
3865 | * descriptive message in @a phrase parameters) | |||
3866 | * @param tags empty | |||
3867 | * | |||
3868 | * @sa nua_bye(), @ref nua_call_model, #nua_i_state, #nua_r_invite() | |||
3869 | * | |||
3870 | * @END_NUA_EVENT | |||
3871 | */ | |||
3872 | ||||
3873 | static int nua_bye_client_report(nua_client_request_t *cr, | |||
3874 | int status, char const *phrase, | |||
3875 | sip_t const *sip, | |||
3876 | nta_outgoing_t *orq, | |||
3877 | tagi_t const *tags) | |||
3878 | { | |||
3879 | nua_handle_t *nh = cr->cr_owner; | |||
3880 | nua_dialog_usage_t *du = cr->cr_usage; | |||
3881 | ||||
3882 | nua_stack_event(nh->nh_nua, nh, | |||
3883 | nta_outgoing_getresponse(orq), | |||
3884 | (enum nua_event_e)cr->cr_event, | |||
3885 | status, phrase, | |||
3886 | tags); | |||
3887 | ||||
3888 | if (du == NULL((void*)0)) { | |||
3889 | /* No more session */ | |||
3890 | } | |||
3891 | else if (status < 200) { | |||
3892 | /* Preliminary */ | |||
3893 | } | |||
3894 | else { | |||
3895 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
3896 | nua_client_request_t *cri; | |||
3897 | ||||
3898 | if (ss->ss_reporting) { | |||
3899 | return 1; /* Somebody else's problem */ | |||
3900 | } | |||
3901 | else if (cr->cr_waiting) { | |||
3902 | return 1; /* Application problem */ | |||
3903 | } | |||
3904 | ||||
3905 | nua_client_bind(cr, NULL((void*)0)); | |||
3906 | ||||
3907 | signal_call_state_change(nh, ss, status, "to BYE", | |||
3908 | nua_callstate_terminated); | |||
3909 | ||||
3910 | for (cri = du->du_dialog->ds_cr; cri; cri = cri->cr_next) { | |||
3911 | if (cri->cr_method == sip_method_invite) | |||
3912 | break; | |||
3913 | } | |||
3914 | ||||
3915 | if (!cri || cri->cr_status >= 200) { | |||
3916 | /* INVITE is completed, we can zap the session... */; | |||
3917 | nua_session_usage_destroy(nh, ss); | |||
3918 | } | |||
3919 | } | |||
3920 | ||||
3921 | return 1; | |||
3922 | } | |||
3923 | ||||
3924 | /** @NUA_EVENT nua_i_bye | |||
3925 | * | |||
3926 | * Incoming BYE request, call hangup. | |||
3927 | * | |||
3928 | * @param status statuscode of response sent automatically by stack | |||
3929 | * @param phrase a short textual description of @a status code | |||
3930 | * @param nh operation handle associated with the call | |||
3931 | * @param hmagic application context associated with the call | |||
3932 | * @param sip pointer to BYE request | |||
3933 | * @param tags empty | |||
3934 | * | |||
3935 | * @sa @ref nua_call_model, #nua_i_state, nua_bye(), nua_bye(), #nua_r_cancel | |||
3936 | * | |||
3937 | * @END_NUA_EVENT | |||
3938 | */ | |||
3939 | ||||
3940 | int nua_bye_server_init(nua_server_request_t *sr); | |||
3941 | int nua_bye_server_report(nua_server_request_t *sr, tagi_t const *tags); | |||
3942 | ||||
3943 | nua_server_methods_t const nua_bye_server_methods = | |||
3944 | { | |||
3945 | SIP_METHOD_BYEsip_method_bye, "BYE", | |||
3946 | nua_i_bye, /* Event */ | |||
3947 | { | |||
3948 | 0, /* Do not create dialog */ | |||
3949 | 1, /* In-dialog request */ | |||
3950 | 0, /* Not a target refresh request */ | |||
3951 | 0, /* Do not add Contact */ | |||
3952 | }, | |||
3953 | nua_bye_server_init, | |||
3954 | nua_base_server_preprocess((void*)0), | |||
3955 | nua_base_server_params((void*)0), | |||
3956 | nua_base_server_respond, | |||
3957 | nua_bye_server_report, | |||
3958 | }; | |||
3959 | ||||
3960 | ||||
3961 | int nua_bye_server_init(nua_server_request_t *sr) | |||
3962 | { | |||
3963 | nua_handle_t *nh = sr->sr_owner; | |||
3964 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||
3965 | ||||
3966 | sr->sr_terminating = 1; | |||
3967 | ||||
3968 | if (du) | |||
3969 | sr->sr_usage = du; | |||
3970 | else | |||
3971 | return SR_STATUS(sr, 481, "No Such Call")((sr)->sr_phrase = ("No Such Call"), (sr)->sr_status = ( 481)); | |||
3972 | ||||
3973 | return 0; | |||
3974 | } | |||
3975 | ||||
3976 | int nua_bye_server_report(nua_server_request_t *sr, tagi_t const *tags) | |||
3977 | { | |||
3978 | nua_handle_t *nh = sr->sr_owner; | |||
3979 | nua_dialog_usage_t *du = sr->sr_usage; | |||
3980 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||
3981 | int early = 0, retval; | |||
3982 | ||||
3983 | if (sr->sr_status < 200) | |||
3984 | return nua_base_server_report(sr, tags); | |||
3985 | ||||
3986 | if (ss) { | |||
3987 | nua_server_request_t *sr0 = NULL((void*)0), *sr_next; | |||
3988 | char const *phrase; | |||
3989 | ||||
3990 | early = ss->ss_state < nua_callstate_ready; | |||
3991 | phrase = early ? "Early Session Terminated" : "Session Terminated"; | |||
3992 | ||||
3993 | #if 0 | |||
3994 | sr->sr_usage = NULL((void*)0); | |||
3995 | #endif | |||
3996 | ||||
3997 | for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr_next) { | |||
3998 | sr_next = sr0->sr_next; | |||
3999 | ||||
4000 | if (sr == sr0 || sr0->sr_usage != sr->sr_usage) | |||
4001 | continue; | |||
4002 | ||||
4003 | if (nua_server_request_is_pending(sr0)) { | |||
4004 | SR_STATUS(sr0, 487, phrase)((sr0)->sr_phrase = (phrase), (sr0)->sr_status = (487)); | |||
4005 | nua_server_respond(sr0, NULL((void*)0)); | |||
4006 | } | |||
4007 | nua_server_request_destroy(sr0); | |||
4008 | } | |||
4009 | ||||
4010 | sr->sr_phrase = phrase; | |||
4011 | } | |||
4012 | ||||
4013 | retval = nua_base_server_report(sr, tags); | |||
4014 | ||||
4015 | //assert(2 <= retval && retval < 4); | |||
4016 | ||||
4017 | #if 0 | |||
4018 | if (ss) { | |||
4019 | signal_call_state_change(nh, ss, 200, | |||
4020 | early ? "Received early BYE" : "Received BYE", | |||
4021 | nua_callstate_terminated); | |||
4022 | nua_dialog_usage_remove(nh, nh->nh_ds, du); | |||
4023 | } | |||
4024 | #endif | |||
4025 | ||||
4026 | return retval; | |||
4027 | } | |||
4028 | ||||
4029 | /* ---------------------------------------------------------------------- */ | |||
4030 | ||||
4031 | /** @NUA_EVENT nua_i_state | |||
4032 | * | |||
4033 | * @brief Call state has changed. | |||
4034 | * | |||
4035 | * This event will be sent whenever the call state changes. | |||
4036 | * | |||
4037 | * In addition to basic changes of session status indicated with enum | |||
4038 | * ::nua_callstate, the @RFC3264 SDP Offer/Answer negotiation status is also | |||
4039 | * included. The tags NUTAG_OFFER_RECV() or NUTAG_ANSWER_RECV() indicate | |||
4040 | * whether the remote SDP that was received was considered as an offer or an | |||
4041 | * answer. Tags NUTAG_OFFER_SENT() or NUTAG_ANSWER_SENT() indicate whether | |||
4042 | * the local SDP which was sent was considered as an offer or answer. | |||
4043 | * | |||
4044 | * If the @b soa SDP negotiation is enabled (by default or with | |||
4045 | * NUTAG_MEDIA_ENABLE(1)), the received remote SDP is included in tags | |||
4046 | * SOATAG_REMOTE_SDP() and SOATAG_REMOTE_SDP_STR(). The SDP negotiation | |||
4047 | * result from @b soa is included in the tags SOATAG_LOCAL_SDP() and | |||
4048 | * SOATAG_LOCAL_SDP_STR(). | |||
4049 | * | |||
4050 | * SOATAG_ACTIVE_AUDIO() and SOATAG_ACTIVE_VIDEO() are informational tags | |||
4051 | * used to indicate what is the status of audio or video. | |||
4052 | * | |||
4053 | * Note that #nua_i_state also covers the information relayed in call | |||
4054 | * establisment (#nua_i_active) and termination (#nua_i_terminated) events. | |||
4055 | * | |||
4056 | * @param status protocol status code \n | |||
4057 | * (always present) | |||
4058 | * @param phrase short description of status code \n | |||
4059 | * (always present) | |||
4060 | * @param nh operation handle associated with the call | |||
4061 | * @param hmagic application context associated with the call | |||
4062 | * @param sip NULL | |||
4063 | * @param tags NUTAG_CALLSTATE(), | |||
4064 | * SOATAG_LOCAL_SDP(), SOATAG_LOCAL_SDP_STR(), | |||
4065 | * NUTAG_OFFER_SENT(), NUTAG_ANSWER_SENT(), | |||
4066 | * SOATAG_REMOTE_SDP(), SOATAG_REMOTE_SDP_STR(), | |||
4067 | * NUTAG_OFFER_RECV(), NUTAG_ANSWER_RECV(), | |||
4068 | * SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO(), | |||
4069 | * SOATAG_ACTIVE_IMAGE(), SOATAG_ACTIVE_CHAT(). | |||
4070 | * | |||
4071 | * @sa @ref nua_call_model, #nua_i_active, #nua_i_terminated, | |||
4072 | * nua_invite(), #nua_r_invite, #nua_i_invite, nua_respond(), | |||
4073 | * NUTAG_MEDIA_ENABLE(), | |||
4074 | * NUTAG_AUTOALERT(), NUTAG_AUTOANSWER(), NUTAG_EARLY_MEDIA(), | |||
4075 | * NUTAG_EARLY_ANSWER(), NUTAG_INCLUDE_EXTRA_SDP(), | |||
4076 | * nua_ack(), NUTAG_AUTOACK(), nua_bye(), #nua_r_bye, #nua_i_bye, | |||
4077 | * nua_cancel(), #nua_r_cancel, #nua_i_cancel, | |||
4078 | * nua_prack(), #nua_r_prack, #nua_i_prack, | |||
4079 | * nua_update(), #nua_r_update, #nua_i_update | |||
4080 | * | |||
4081 | * @par History | |||
4082 | * Prior @VERSION_1_12_6 the tags NUTAG_OFFER_RECV(), NUTAG_ANSWER_RECV(), | |||
4083 | * NUTAG_ANSWER_SENT(), NUTAG_OFFER_SENT() were not included with | |||
4084 | * nua_i_state eventif media was disabled. | |||
4085 | * | |||
4086 | * @END_NUA_EVENT | |||
4087 | */ | |||
4088 | ||||
4089 | /** | |||
4090 | * Delivers call state changed event to the nua client. @internal | |||
4091 | * | |||
4092 | * @param nh call handle | |||
4093 | * @param status status code | |||
4094 | * @param tr_event SIP transaction event triggering this change | |||
4095 | * @param oa_recv Received SDP | |||
4096 | * @param oa_sent Sent SDP | |||
4097 | */ | |||
4098 | ||||
4099 | static void signal_call_state_change(nua_handle_t *nh, | |||
4100 | nua_session_usage_t *ss, | |||
4101 | int status, char const *phrase, | |||
4102 | enum nua_callstate next_state) | |||
4103 | { | |||
4104 | enum nua_callstate ss_state = nua_callstate_init; | |||
4105 | enum nua_callstate invite_state = next_state; | |||
4106 | ||||
4107 | char const *oa_recv = NULL((void*)0); | |||
4108 | char const *oa_sent = NULL((void*)0); | |||
4109 | ||||
4110 | int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0; | |||
4111 | ||||
4112 | if (ss) { | |||
4113 | if (ss->ss_reporting) | |||
4114 | return; | |||
4115 | ||||
4116 | ss_state = ss->ss_state; | |||
4117 | oa_recv = ss->ss_oa_recv, ss->ss_oa_recv = NULL((void*)0); | |||
4118 | oa_sent = ss->ss_oa_sent, ss->ss_oa_sent = NULL((void*)0); | |||
4119 | ||||
4120 | assert(oa_sent == Offer || oa_sent == Answer || oa_sent == NULL)((void) sizeof ((oa_sent == Offer || oa_sent == Answer || oa_sent == ((void*)0)) ? 1 : 0), __extension__ ({ if (oa_sent == Offer || oa_sent == Answer || oa_sent == ((void*)0)) ; else __assert_fail ("oa_sent == Offer || oa_sent == Answer || oa_sent == NULL", "nua_session.c", 4120, __extension__ __PRETTY_FUNCTION__); } )); | |||
4121 | assert(oa_recv == Offer || oa_recv == Answer || oa_recv == NULL)((void) sizeof ((oa_recv == Offer || oa_recv == Answer || oa_recv == ((void*)0)) ? 1 : 0), __extension__ ({ if (oa_recv == Offer || oa_recv == Answer || oa_recv == ((void*)0)) ; else __assert_fail ("oa_recv == Offer || oa_recv == Answer || oa_recv == NULL", "nua_session.c", 4121, __extension__ __PRETTY_FUNCTION__); } )); | |||
4122 | ||||
4123 | if (oa_recv) { | |||
4124 | offer_recv = oa_recv == Offer; | |||
4125 | answer_recv = oa_recv == Answer; | |||
4126 | } | |||
4127 | ||||
4128 | if (oa_sent) { | |||
4129 | offer_sent = oa_sent == Offer; | |||
4130 | answer_sent = oa_sent == Answer; | |||
4131 | } | |||
4132 | } | |||
4133 | ||||
4134 | if (ss_state < nua_callstate_ready || next_state > nua_callstate_ready) | |||
4135 | SU_DEBUG_5(("nua(%p): call state changed: %s -> %s%s%s%s%s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4140, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||
4136 | (void *)nh, nua_callstate_name(ss_state),((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4140, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||
4137 | nua_callstate_name(next_state),((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4140, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||
4138 | oa_recv ? ", received " : "", oa_recv ? oa_recv : "",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4140, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||
4139 | oa_sent && oa_recv ? ", and sent " :((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4140, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||
4140 | oa_sent ? ", sent " : "", oa_sent ? oa_sent : ""))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4140, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0); | |||
4141 | else | |||
4142 | SU_DEBUG_5(("nua(%p): ready call updated: %s%s%s%s%s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4146, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0) | |||
4143 | (void *)nh, nua_callstate_name(next_state),((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4146, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0) | |||
4144 | oa_recv ? " received " : "", oa_recv ? oa_recv : "",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4146, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0) | |||
4145 | oa_sent && oa_recv ? ", sent " :((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4146, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0) | |||
4146 | oa_sent ? " sent " : "", oa_sent ? oa_sent : ""))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4146, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0); | |||
4147 | ||||
4148 | if (next_state == nua_callstate_terminating && | |||
4149 | ss_state >= nua_callstate_terminating) | |||
4150 | return; | |||
4151 | ||||
4152 | if (ss) { | |||
4153 | /* Update state variables */ | |||
4154 | if (next_state == nua_callstate_init) { | |||
4155 | if (ss_state < nua_callstate_ready) | |||
4156 | ss->ss_state = next_state; | |||
4157 | else if (ss->ss_state == nua_callstate_ready) | |||
4158 | next_state = ss->ss_state; | |||
4159 | else if (ss->ss_state == nua_callstate_terminating) | |||
4160 | return; | |||
4161 | else | |||
4162 | ss->ss_state = next_state = nua_callstate_terminated; | |||
4163 | } | |||
4164 | else if (next_state > ss_state) | |||
4165 | ss->ss_state = next_state; | |||
4166 | } | |||
4167 | ||||
4168 | if (next_state == nua_callstate_init) | |||
4169 | next_state = nua_callstate_terminated; | |||
4170 | ||||
4171 | if (ss && ss->ss_state == nua_callstate_ready) | |||
4172 | nh->nh_active_call = 1; | |||
4173 | else if (next_state == nua_callstate_terminated) | |||
4174 | nh->nh_active_call = 0; | |||
4175 | ||||
4176 | /* Send events */ | |||
4177 | if (phrase == NULL((void*)0)) | |||
4178 | phrase = "Call state"; | |||
4179 | ||||
4180 | { | |||
4181 | sdp_session_t const *remote_sdp = NULL((void*)0); | |||
4182 | char const *remote_sdp_str = NULL((void*)0); | |||
4183 | sdp_session_t const *local_sdp = NULL((void*)0); | |||
4184 | char const *local_sdp_str = NULL((void*)0); | |||
4185 | ||||
4186 | if (nh->nh_soa) { | |||
4187 | if (oa_recv) | |||
4188 | soa_get_remote_sdp(nh->nh_soa, &remote_sdp, &remote_sdp_str, 0); | |||
4189 | if (oa_sent) | |||
4190 | soa_get_local_sdp(nh->nh_soa, &local_sdp, &local_sdp_str, 0); | |||
4191 | ||||
4192 | if (answer_recv || answer_sent) { /* Update nh_hold_remote */ | |||
4193 | char const *held = NULL((void*)0); | |||
4194 | soa_get_params(nh->nh_soa, SOATAG_HOLD_REF(held)soatag_hold_ref, tag_str_vr(&(held)), TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
4195 | nh->nh_hold_remote = held && strlen(held) > 0; | |||
4196 | } | |||
4197 | } | |||
4198 | else | |||
4199 | oa_recv = NULL((void*)0), oa_sent = NULL((void*)0); | |||
4200 | ||||
4201 | nua_stack_tevent(nh->nh_nua, nh, NULL((void*)0), nua_i_state, | |||
4202 | status, phrase, | |||
4203 | NUTAG_CALLSTATE(next_state)nutag_callstate, tag_int_v(next_state), | |||
4204 | NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa)!((1) && (nh->nh_soa) && soa_is_audio_active (nh->nh_soa) >= 0) ? tag_skip : soatag_active_audio, tag_int_v (soa_is_audio_active(nh->nh_soa)), !((1) && (nh-> nh_soa) && soa_is_video_active(nh->nh_soa) >= 0 ) ? tag_skip : soatag_active_video, tag_int_v(soa_is_video_active (nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_image_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_image , tag_int_v(soa_is_image_active(nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_chat_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_chat, tag_int_v(soa_is_chat_active (nh->nh_soa)), | |||
4205 | /* NUTAG_SOA_SESSION(nh->nh_soa), */ | |||
4206 | TAG_IF(offer_recv, NUTAG_OFFER_RECV(offer_recv))!(offer_recv) ? tag_skip : nutag_offer_recv, tag_bool_v(offer_recv ), | |||
4207 | TAG_IF(answer_recv, NUTAG_ANSWER_RECV(answer_recv))!(answer_recv) ? tag_skip : nutag_answer_recv, tag_bool_v(answer_recv ), | |||
4208 | TAG_IF(offer_sent, NUTAG_OFFER_SENT(offer_sent))!(offer_sent) ? tag_skip : nutag_offer_sent, tag_bool_v(offer_sent ), | |||
4209 | TAG_IF(answer_sent, NUTAG_ANSWER_SENT(answer_sent))!(answer_sent) ? tag_skip : nutag_answer_sent, tag_bool_v(answer_sent ), | |||
4210 | TAG_IF(oa_recv, SOATAG_REMOTE_SDP(remote_sdp))!(oa_recv) ? tag_skip : soatag_remote_sdp, sdptag_session_v(remote_sdp ), | |||
4211 | TAG_IF(oa_recv, SOATAG_REMOTE_SDP_STR(remote_sdp_str))!(oa_recv) ? tag_skip : soatag_remote_sdp_str, tag_str_v(remote_sdp_str ), | |||
4212 | TAG_IF(oa_sent, SOATAG_LOCAL_SDP(local_sdp))!(oa_sent) ? tag_skip : soatag_local_sdp, sdptag_session_v(local_sdp ), | |||
4213 | TAG_IF(oa_sent, SOATAG_LOCAL_SDP_STR(local_sdp_str))!(oa_sent) ? tag_skip : soatag_local_sdp_str, tag_str_v(local_sdp_str ), | |||
4214 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
4215 | } | |||
4216 | ||||
4217 | if (next_state == nua_callstate_ready && ss_state <= nua_callstate_ready) { | |||
4218 | nua_stack_tevent(nh->nh_nua, nh, NULL((void*)0), nua_i_active, status, "Call active", | |||
4219 | NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa)!((1) && (nh->nh_soa) && soa_is_audio_active (nh->nh_soa) >= 0) ? tag_skip : soatag_active_audio, tag_int_v (soa_is_audio_active(nh->nh_soa)), !((1) && (nh-> nh_soa) && soa_is_video_active(nh->nh_soa) >= 0 ) ? tag_skip : soatag_active_video, tag_int_v(soa_is_video_active (nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_image_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_image , tag_int_v(soa_is_image_active(nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_chat_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_chat, tag_int_v(soa_is_chat_active (nh->nh_soa)), | |||
4220 | /* NUTAG_SOA_SESSION(nh->nh_soa), */ | |||
4221 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
4222 | } | |||
4223 | ||||
4224 | else if (next_state == nua_callstate_terminated) { | |||
4225 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||
4226 | nua_i_terminated, status, phrase, | |||
4227 | NULL((void*)0)); | |||
4228 | } | |||
4229 | ||||
4230 | if (invite_state == nua_callstate_ready) { | |||
4231 | /* Start next INVITE request, if queued */ | |||
4232 | nua_client_next_request(nh->nh_ds->ds_cr, 1); | |||
4233 | } | |||
4234 | } | |||
4235 | ||||
4236 | /** @NUA_EVENT nua_i_active | |||
4237 | * | |||
4238 | * A call has been activated. | |||
4239 | * | |||
4240 | * This event will be sent after a succesful response to the initial | |||
4241 | * INVITE has been received and the media has been activated. | |||
4242 | * | |||
4243 | * @param nh operation handle associated with the call | |||
4244 | * @param hmagic application context associated with the call | |||
4245 | * @param sip NULL | |||
4246 | * @param tags SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO(), | |||
4247 | * SOATAG_ACTIVE_IMAGE(), SOATAG_ACTIVE_CHAT(). | |||
4248 | * | |||
4249 | * @deprecated Use #nua_i_state instead. | |||
4250 | * | |||
4251 | * @sa @ref nua_call_model, #nua_i_state, #nua_i_terminated, | |||
4252 | * #nua_i_invite | |||
4253 | * | |||
4254 | * @END_NUA_EVENT | |||
4255 | */ | |||
4256 | ||||
4257 | /** @NUA_EVENT nua_i_terminated | |||
4258 | * | |||
4259 | * A call has been terminated. | |||
4260 | * | |||
4261 | * This event will be sent after a call has been terminated. A call is | |||
4262 | * terminated, when | |||
4263 | * 1) an error response (300..599) is sent to an incoming initial INVITE | |||
4264 | * 2) a reliable response (200..299 or reliable preliminary response) to | |||
4265 | * an incoming initial INVITE is not acknowledged with ACK or PRACK | |||
4266 | * 3) BYE is received or sent | |||
4267 | * | |||
4268 | * @param nh operation handle associated with the call | |||
4269 | * @param hmagic application context associated with the call | |||
4270 | * @param sip NULL | |||
4271 | * @param tags empty | |||
4272 | * | |||
4273 | * @deprecated Use #nua_i_state instead. | |||
4274 | * | |||
4275 | * @sa @ref nua_call_model, #nua_i_state, #nua_i_active, #nua_i_bye, | |||
4276 | * #nua_i_invite | |||
4277 | * | |||
4278 | * @END_NUA_EVENT | |||
4279 | */ | |||
4280 | ||||
4281 | ||||
4282 | /* ======================================================================== */ | |||
4283 | ||||
4284 | static | |||
4285 | int nua_server_retry_after(nua_server_request_t *sr, | |||
4286 | int status, char const *phrase, | |||
4287 | int min, int max) | |||
4288 | { | |||
4289 | sip_retry_after_t af[1]; | |||
4290 | ||||
4291 | sip_retry_after_init(af); | |||
4292 | af->af_delta = (unsigned)su_randint(min, max); | |||
4293 | af->af_comment = phrase; | |||
4294 | ||||
4295 | sip_add_dup(sr->sr_response.msg, sr->sr_response.sip, (sip_header_t *)af); | |||
4296 | ||||
4297 | return sr_status(sr, status, phrase); | |||
4298 | } | |||
4299 | ||||
4300 | /* ======================================================================== */ | |||
4301 | /* Session timer - RFC 4028 */ | |||
4302 | ||||
4303 | static int session_timer_is_supported(struct session_timer const *t) | |||
4304 | { | |||
4305 | return t->local.supported; | |||
4306 | } | |||
4307 | ||||
4308 | /** Set session timer preferences */ | |||
4309 | static | |||
4310 | void session_timer_preferences(struct session_timer *t, | |||
4311 | sip_t const *sip, | |||
4312 | sip_supported_t const *supported, | |||
4313 | unsigned expires, | |||
4314 | int isset, | |||
4315 | enum nua_session_refresher refresher, | |||
4316 | unsigned min_se) | |||
4317 | { | |||
4318 | memset(&t->local, 0, sizeof t->local); | |||
4319 | ||||
4320 | t->local.require = sip_has_feature(sip->sip_require, "timer"); | |||
4321 | t->local.supported = | |||
4322 | sip_has_feature(supported, "timer") || | |||
4323 | sip_has_feature(sip->sip_supported, "timer"); | |||
4324 | if (isset || refresher != nua_no_refresher) | |||
4325 | t->local.expires = expires; | |||
4326 | else | |||
4327 | t->local.defaults = expires; | |||
4328 | t->local.min_se = min_se; | |||
4329 | t->local.refresher = refresher; | |||
4330 | } | |||
4331 | ||||
4332 | static int session_timer_check_restart(nua_client_request_t *cr, | |||
4333 | int status, char const *phrase, | |||
4334 | sip_t const *sip) | |||
4335 | { | |||
4336 | if (status == 422) { | |||
4337 | nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage)((cr->cr_usage) ? (void*)((cr->cr_usage) + 1) : ((void* )0)); | |||
4338 | ||||
4339 | if (ss && session_timer_is_supported(ss->ss_timer)) { | |||
4340 | struct session_timer *t = ss->ss_timer; | |||
4341 | ||||
4342 | if (sip->sip_min_se && t->local.min_se < sip->sip_min_se->min_delta) | |||
4343 | t->local.min_se = sip->sip_min_se->min_delta; | |||
4344 | if (t->local.expires != 0 && t->local.min_se > t->local.expires) | |||
4345 | t->local.expires = t->local.min_se; | |||
4346 | ||||
4347 | return nua_client_restart(cr, 100, "Re-Negotiating Session Timer"); | |||
4348 | } | |||
4349 | } | |||
4350 | ||||
4351 | return nua_base_client_check_restart(cr, status, phrase, sip); | |||
4352 | } | |||
4353 | ||||
4354 | /** Check that received Session-Expires is longer than Min-SE */ | |||
4355 | static | |||
4356 | int session_timer_check_min_se(msg_t *msg, | |||
4357 | sip_t *sip, | |||
4358 | sip_t const *request, | |||
4359 | unsigned long min) | |||
4360 | { | |||
4361 | if (min == 0) | |||
4362 | min = 1; | |||
4363 | ||||
4364 | /* | |||
4365 | If an incoming request contains a Supported header field with a value | |||
4366 | 'timer' and a Session Expires header field, the UAS MAY reject the | |||
4367 | INVITE request with a 422 (Session Interval Too Small) response if | |||
4368 | the session interval in the Session-Expires header field is smaller | |||
4369 | than the minimum interval defined by the UAS' local policy. When | |||
4370 | sending the 422 response, the UAS MUST include a Min-SE header field | |||
4371 | with the value of its minimum interval. This minimum interval MUST | |||
4372 | NOT be lower than 90 seconds. | |||
4373 | */ | |||
4374 | if (request->sip_session_expires && | |||
4375 | sip_has_feature(request->sip_supported, "timer") && | |||
4376 | request->sip_session_expires->x_delta < min) { | |||
4377 | sip_min_se_t min_se[1]; | |||
4378 | ||||
4379 | if (min < 90) | |||
4380 | min = 90; | |||
4381 | ||||
4382 | sip_min_se_init(min_se)->min_delta = min; | |||
4383 | ||||
4384 | /* Include extension parameters, if any */ | |||
4385 | if (request->sip_min_se) | |||
4386 | min_se->min_params = request->sip_min_se->min_params; | |||
4387 | ||||
4388 | sip_add_dup(msg, sip, (sip_header_t *)min_se); | |||
4389 | ||||
4390 | return 422; | |||
4391 | } | |||
4392 | ||||
4393 | return 0; | |||
4394 | } | |||
4395 | ||||
4396 | /** Store session timer parameters in request from uac / response from uas */ | |||
4397 | static | |||
4398 | void session_timer_store(struct session_timer *t, | |||
4399 | sip_t const *sip) | |||
4400 | { | |||
4401 | sip_require_t const *require = sip->sip_require; | |||
4402 | sip_supported_t const *supported = sip->sip_supported; | |||
4403 | sip_session_expires_t const *x = sip->sip_session_expires; | |||
4404 | ||||
4405 | t->remote.require = (require && sip_has_feature(require, "timer")); | |||
| ||||
4406 | t->remote.supported = | |||
4407 | t->remote.supported || (supported && sip_has_feature(supported, "timer")); | |||
4408 | ||||
4409 | t->remote.expires = 0; | |||
4410 | t->remote.refresher = nua_any_refresher; | |||
4411 | t->remote.min_se = 0; | |||
4412 | ||||
4413 | if (x) { | |||
4414 | t->remote.expires = x->x_delta; | |||
4415 | ||||
4416 | if (x->x_refresher) { | |||
4417 | int uas = sip->sip_request != NULL((void*)0); | |||
4418 | ||||
4419 | if (su_casenmatch(x->x_refresher, "uac", (sizeof "uac"))) | |||
4420 | t->remote.refresher = uas ? nua_remote_refresher : nua_local_refresher; | |||
4421 | else if (su_casenmatch(x->x_refresher, "uas", (sizeof "uas"))) | |||
4422 | t->remote.refresher = uas ? nua_local_refresher : nua_remote_refresher; | |||
4423 | } | |||
4424 | else if (t->remote.require) { | |||
4425 | /* Require: timer but no refresher parameter in Session-Expires header */ | |||
4426 | t->remote.refresher = nua_local_refresher; | |||
4427 | } | |||
4428 | } | |||
4429 | ||||
4430 | if (sip->sip_min_se) | |||
4431 | t->remote.min_se = sip->sip_min_se->min_delta; | |||
4432 | } | |||
4433 | ||||
4434 | /** Add timer feature and Session-Expires/Min-SE headers to request/response | |||
4435 | * | |||
4436 | */ | |||
4437 | static int | |||
4438 | session_timer_add_headers(struct session_timer *t, | |||
4439 | int initial, | |||
4440 | msg_t *msg, | |||
4441 | sip_t *sip, | |||
4442 | nua_handle_t *nh) | |||
4443 | { | |||
4444 | unsigned long expires, min; | |||
4445 | sip_min_se_t min_se[1]; | |||
4446 | sip_session_expires_t x[1]; | |||
4447 | int uas; | |||
4448 | int autorequire = 1; | |||
4449 | ||||
4450 | enum nua_session_refresher refresher = nua_any_refresher; | |||
4451 | ||||
4452 | static sip_param_t const x_params_uac[] = {"refresher=uac", NULL((void*)0)}; | |||
4453 | static sip_param_t const x_params_uas[] = {"refresher=uas", NULL((void*)0)}; | |||
4454 | ||||
4455 | if ( !NH_PGET(nh, timer_autorequire)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_timer_autorequire ? ((nh)->nh_prefs)->nhp_timer_autorequire : ((nh)-> nh_nua->nua_handles->nh_prefs)->nhp_timer_autorequire ) && NH_PISSET(nh, timer_autorequire)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_timer_autorequire ) && (nh)->nh_nua->nua_handles->nh_prefs != ( nh)->nh_prefs)) { | |||
4456 | autorequire = 0; | |||
4457 | } | |||
4458 | ||||
4459 | if (!t->local.supported) | |||
4460 | return 0; | |||
4461 | ||||
4462 | uas = sip->sip_status != NULL((void*)0); | |||
4463 | ||||
4464 | min = t->local.min_se; | |||
4465 | if (min < t->remote.min_se) | |||
4466 | min = t->remote.min_se; | |||
4467 | ||||
4468 | if (uas) { | |||
4469 | session_timer_negotiate(t, uas = 1); | |||
4470 | ||||
4471 | refresher = t->refresher; | |||
4472 | expires = t->interval; | |||
4473 | } | |||
4474 | else { | |||
4475 | /* RFC 4028: | |||
4476 | * The UAC MAY include the refresher parameter with value 'uac' if it | |||
4477 | * wants to perform the refreshes. However, it is RECOMMENDED that the | |||
4478 | * parameter be omitted so that it can be selected by the negotiation | |||
4479 | * mechanisms described below. | |||
4480 | */ | |||
4481 | if (t->local.refresher == nua_local_refresher) | |||
4482 | refresher = nua_local_refresher; | |||
4483 | else if (!initial) | |||
4484 | refresher = t->refresher; | |||
4485 | ||||
4486 | expires = t->local.expires; | |||
4487 | if (expires != 0 && expires < min) | |||
4488 | expires = min; | |||
4489 | ||||
4490 | if (expires == 0 && !initial && t->interval) | |||
4491 | expires = t->interval; | |||
4492 | } | |||
4493 | ||||
4494 | sip_min_se_init(min_se)->min_delta = min; | |||
4495 | ||||
4496 | sip_session_expires_init(x)->x_delta = expires; | |||
4497 | if (refresher == nua_remote_refresher) | |||
4498 | x->x_params = uas ? x_params_uac : x_params_uas; | |||
4499 | else if (refresher == nua_local_refresher) | |||
4500 | x->x_params = uas ? x_params_uas : x_params_uac; | |||
4501 | ||||
4502 | if (expires == 0 && t->remote.min_se == 0) | |||
4503 | /* Session timer is not used, do not add headers */ | |||
4504 | return 1; | |||
4505 | ||||
4506 | sip_add_tl(msg, sip, | |||
4507 | TAG_IF(expires != 0, SIPTAG_SESSION_EXPIRES(x))!(expires != 0) ? tag_skip : siptag_session_expires, siptag_session_expires_v (x), | |||
4508 | TAG_IF((!uas || sip->sip_status->st_status == 422) && (min != 0!((!uas || sip->sip_status->st_status == 422) && (min != 0 || !initial)) ? tag_skip : siptag_min_se, siptag_min_se_v (min_se) | |||
4509 | /* Min-SE: 0 is optional with initial INVITE */!((!uas || sip->sip_status->st_status == 422) && (min != 0 || !initial)) ? tag_skip : siptag_min_se, siptag_min_se_v (min_se) | |||
4510 | || !initial),!((!uas || sip->sip_status->st_status == 422) && (min != 0 || !initial)) ? tag_skip : siptag_min_se, siptag_min_se_v (min_se) | |||
4511 | SIPTAG_MIN_SE(min_se))!((!uas || sip->sip_status->st_status == 422) && (min != 0 || !initial)) ? tag_skip : siptag_min_se, siptag_min_se_v (min_se), | |||
4512 | TAG_IF(autorequire && refresher == nua_remote_refresher && expires != 0, SIPTAG_REQUIRE_STR("timer"))!(autorequire && refresher == nua_remote_refresher && expires != 0) ? tag_skip : siptag_require_str, tag_str_v("timer" ), | |||
4513 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
4514 | ||||
4515 | return 1; | |||
4516 | } | |||
4517 | ||||
4518 | static void | |||
4519 | session_timer_negotiate(struct session_timer *t, int uas) | |||
4520 | { | |||
4521 | if (!t->local.supported) | |||
4522 | t->refresher = nua_no_refresher; | |||
4523 | else if (!t->remote.supported) | |||
4524 | t->refresher = nua_local_refresher; | |||
4525 | else if (t->remote.refresher == nua_local_refresher) | |||
4526 | t->refresher = nua_local_refresher; | |||
4527 | else if (t->remote.refresher == nua_remote_refresher) | |||
4528 | t->refresher = nua_remote_refresher; | |||
4529 | else if (uas) | |||
4530 | /* UAS defaults UAC to refreshing */ | |||
4531 | t->refresher = nua_remote_refresher; | |||
4532 | else | |||
4533 | /* UAC refreshes by itself */ | |||
4534 | t->refresher = nua_local_refresher; | |||
4535 | ||||
4536 | t->interval = t->remote.expires; | |||
4537 | if (t->interval == 0) | |||
4538 | t->interval = t->local.expires; | |||
4539 | if (t->local.expires != 0 && t->interval > t->local.expires) | |||
4540 | t->interval = t->local.expires; | |||
4541 | if (t->local.defaults != 0 && t->interval > t->local.defaults) | |||
4542 | t->interval = t->local.defaults; | |||
4543 | ||||
4544 | if (t->interval != 0) { | |||
4545 | if (t->interval < t->local.min_se) | |||
4546 | t->interval = t->local.min_se; | |||
4547 | if (t->interval < t->remote.min_se) | |||
4548 | t->interval = t->remote.min_se; | |||
4549 | } | |||
4550 | ||||
4551 | if (t->interval == 0) | |||
4552 | t->refresher = nua_no_refresher; | |||
4553 | } | |||
4554 | ||||
4555 | static void | |||
4556 | session_timer_set(nua_session_usage_t *ss, int uas) | |||
4557 | { | |||
4558 | nua_dialog_usage_t *du = nua_dialog_usage_public(ss)((ss) ? (nua_dialog_usage_t*)(ss) - 1 : ((void*)0)); | |||
4559 | struct session_timer *t; | |||
4560 | ||||
4561 | if (ss == NULL((void*)0)) | |||
4562 | return; | |||
4563 | ||||
4564 | t = ss->ss_timer; | |||
4565 | ||||
4566 | session_timer_negotiate(t, uas); | |||
4567 | ||||
4568 | if (t->refresher == nua_local_refresher) { | |||
4569 | unsigned low = t->interval / 2, high = t->interval / 2; | |||
4570 | ||||
4571 | if (t->interval >= 90) | |||
4572 | low -=5, high += 5; | |||
4573 | ||||
4574 | nua_dialog_usage_set_refresh_range(du, low, high); | |||
4575 | t->timer_set = 1; | |||
4576 | } | |||
4577 | else if (t->refresher == nua_remote_refresher) { | |||
4578 | /* RFC 4028 10.3 and 10.4: Send BYE before the session expires. | |||
4579 | Increased interval from 2/3 to 9/10 of session expiration delay | |||
4580 | because some endpoints won't UPDATE early enough with very short | |||
4581 | sessions (e.g. 120). */ | |||
4582 | ||||
4583 | unsigned interval = t->interval; | |||
4584 | ||||
4585 | interval -= 32 > interval / 10 ? interval / 10 : 32; | |||
4586 | ||||
4587 | nua_dialog_usage_set_refresh_range(du, interval, interval); | |||
4588 | t->timer_set = 1; | |||
4589 | } | |||
4590 | else { | |||
4591 | nua_dialog_usage_reset_refresh(du); | |||
4592 | t->timer_set = 0; | |||
4593 | } | |||
4594 | } | |||
4595 | ||||
4596 | /* ======================================================================== */ | |||
4597 | ||||
4598 | /** Get SDP from a SIP message. | |||
4599 | * | |||
4600 | * @retval 1 if message contains SDP | |||
4601 | * @retval 0 if message does not contain valid SDP | |||
4602 | */ | |||
4603 | static | |||
4604 | int session_get_description(sip_t const *sip, | |||
4605 | char const **return_sdp, | |||
4606 | size_t *return_len) | |||
4607 | { | |||
4608 | sip_payload_t const *pl = sip->sip_payload; | |||
4609 | sip_content_type_t const *ct = sip->sip_content_type; | |||
4610 | int matching_content_type = 0; | |||
4611 | ||||
4612 | if (pl == NULL((void*)0)) | |||
4613 | return 0; | |||
4614 | else if (pl->pl_len == 0 || pl->pl_data == NULL((void*)0)) | |||
4615 | return 0; | |||
4616 | else if (ct == NULL((void*)0)) | |||
4617 | /* Be bug-compatible with our old gateways */ | |||
4618 | SU_DEBUG_3(("nua: no %s, assuming %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 4619, "nua: no %s, assuming %s\n", "Content-Type" , nua_application_sdp)) : (void)0) | |||
4619 | "Content-Type", SDP_MIME_TYPE))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 4619, "nua: no %s, assuming %s\n", "Content-Type" , nua_application_sdp)) : (void)0); | |||
4620 | else if (ct->c_type == NULL((void*)0)) | |||
4621 | SU_DEBUG_3(("nua: empty %s, assuming %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 4622, "nua: empty %s, assuming %s\n" , "Content-Type", nua_application_sdp)) : (void)0) | |||
4622 | "Content-Type", SDP_MIME_TYPE))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 4622, "nua: empty %s, assuming %s\n" , "Content-Type", nua_application_sdp)) : (void)0); | |||
4623 | else if (!su_casematch(ct->c_type, SDP_MIME_TYPEnua_application_sdp)) { | |||
4624 | SU_DEBUG_5(("nua: unknown %s: %s\n", "Content-Type", ct->c_type))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4624, "nua: unknown %s: %s\n", "Content-Type" , ct->c_type)) : (void)0); | |||
4625 | return 0; | |||
4626 | } | |||
4627 | else | |||
4628 | matching_content_type = 1; | |||
4629 | ||||
4630 | if (pl == NULL((void*)0)) | |||
4631 | return 0; | |||
4632 | ||||
4633 | if (!matching_content_type) { | |||
4634 | /* Make sure we got SDP */ | |||
4635 | if (pl->pl_len < 3 || !su_casenmatch(pl->pl_data, "v=0", 3)) | |||
4636 | return 0; | |||
4637 | } | |||
4638 | ||||
4639 | if (return_sdp && return_len) { | |||
4640 | *return_sdp = pl->pl_data; | |||
4641 | *return_len = pl->pl_len; | |||
4642 | } | |||
4643 | ||||
4644 | return 1; | |||
4645 | } | |||
4646 | ||||
4647 | /** Insert SDP into SIP message */ | |||
4648 | static | |||
4649 | int session_include_description(soa_session_t *soa, | |||
4650 | int session, | |||
4651 | msg_t *msg, | |||
4652 | sip_t *sip) | |||
4653 | { | |||
4654 | su_home_t *home = msg_home(msg)((su_home_t*)(msg)); | |||
4655 | ||||
4656 | sip_content_disposition_t *cd = NULL((void*)0); | |||
4657 | sip_content_type_t *ct = NULL((void*)0); | |||
4658 | sip_payload_t *pl = NULL((void*)0); | |||
4659 | ||||
4660 | int retval; | |||
4661 | ||||
4662 | if (!soa) | |||
4663 | return 0; | |||
4664 | ||||
4665 | retval = session_make_description(home, soa, session, &cd, &ct, &pl); | |||
4666 | ||||
4667 | if (retval <= 0) | |||
4668 | return retval; | |||
4669 | ||||
4670 | if ((cd && sip_header_insert(msg, sip, (sip_header_t *)cd) < 0) || | |||
4671 | sip_header_insert(msg, sip, (sip_header_t *)ct) < 0 || | |||
4672 | sip_header_insert(msg, sip, (sip_header_t *)pl) < 0) | |||
4673 | return -1; | |||
4674 | ||||
4675 | return retval; | |||
4676 | } | |||
4677 | ||||
4678 | /** Generate SDP headers */ | |||
4679 | static | |||
4680 | int session_make_description(su_home_t *home, | |||
4681 | soa_session_t *soa, | |||
4682 | int session, | |||
4683 | sip_content_disposition_t **return_cd, | |||
4684 | sip_content_type_t **return_ct, | |||
4685 | sip_payload_t **return_pl) | |||
4686 | { | |||
4687 | char const *sdp; | |||
4688 | isize_t len; | |||
4689 | int retval; | |||
4690 | ||||
4691 | if (!soa) | |||
4692 | return 0; | |||
4693 | ||||
4694 | if (session) | |||
4695 | retval = soa_get_local_sdp(soa, 0, &sdp, &len); | |||
4696 | else | |||
4697 | retval = soa_get_capability_sdp(soa, 0, &sdp, &len); | |||
4698 | ||||
4699 | if (retval > 0) { | |||
4700 | *return_pl = sip_payload_create(home, sdp, len); | |||
4701 | *return_ct = sip_content_type_make(home, SDP_MIME_TYPEnua_application_sdp); | |||
4702 | if (session) | |||
4703 | *return_cd = sip_content_disposition_make(home, "session"); | |||
4704 | else | |||
4705 | *return_cd = NULL((void*)0); | |||
4706 | ||||
4707 | if (!*return_pl || !*return_ct) | |||
4708 | return -1; | |||
4709 | ||||
4710 | if (session && !*return_cd) | |||
4711 | return -1; | |||
4712 | } | |||
4713 | ||||
4714 | return retval; | |||
4715 | } | |||
4716 | ||||
4717 | /* ====================================================================== */ | |||
4718 | ||||
4719 | /** @NUA_EVENT nua_i_options | |||
4720 | * | |||
4721 | * Incoming OPTIONS request. The user-agent should respond to an OPTIONS | |||
4722 | * request with the same statuscode as it would respond to an INVITE | |||
4723 | * request. | |||
4724 | * | |||
4725 | * Stack responds automatically to OPTIONS request unless OPTIONS is | |||
4726 | * included in the set of application methods, set by NUTAG_APPL_METHOD(). | |||
4727 | * | |||
4728 | * The OPTIONS request does not create a dialog. Currently the processing | |||
4729 | * of incoming OPTIONS creates a new handle for each incoming request which | |||
4730 | * is not assiciated with an existing dialog. If the handle @a nh is not | |||
4731 | * bound, you should probably destroy it after responding to the OPTIONS | |||
4732 | * request. | |||
4733 | * | |||
4734 | * @param status status code of response sent automatically by stack | |||
4735 | * @param phrase a short textual description of @a status code | |||
4736 | * @param nh operation handle associated with the OPTIONS request | |||
4737 | * @param hmagic application context associated with the call | |||
4738 | * (NULL if outside session) | |||
4739 | * @param sip incoming OPTIONS request | |||
4740 | * @param tags empty | |||
4741 | * | |||
4742 | * @sa nua_respond(), nua_options(), #nua_r_options, @RFC3261 section 11.2 | |||
4743 | * | |||
4744 | * @END_NUA_EVENT | |||
4745 | */ | |||
4746 | ||||
4747 | int nua_options_server_respond(nua_server_request_t *sr, tagi_t const *tags); | |||
4748 | ||||
4749 | nua_server_methods_t const nua_options_server_methods = | |||
4750 | { | |||
4751 | SIP_METHOD_OPTIONSsip_method_options, "OPTIONS", | |||
4752 | nua_i_options, /* Event */ | |||
4753 | { | |||
4754 | 0, /* Do not create dialog */ | |||
4755 | 0, /* Initial request */ | |||
4756 | 0, /* Not a target refresh request */ | |||
4757 | 1, /* Add Contact */ | |||
4758 | }, | |||
4759 | nua_base_server_init((void*)0), | |||
4760 | nua_base_server_preprocess((void*)0), | |||
4761 | nua_base_server_params((void*)0), | |||
4762 | nua_options_server_respond, | |||
4763 | nua_base_server_report, | |||
4764 | }; | |||
4765 | ||||
4766 | /** @internal Respond to an OPTIONS request. | |||
4767 | * | |||
4768 | */ | |||
4769 | int nua_options_server_respond(nua_server_request_t *sr, tagi_t const *tags) | |||
4770 | { | |||
4771 | nua_handle_t *nh = sr->sr_owner; | |||
4772 | nua_t *nua = nh->nh_nua; | |||
4773 | ||||
4774 | if (200 <= sr->sr_status && sr->sr_status < 300) { | |||
4775 | msg_t *msg = sr->sr_response.msg; | |||
4776 | sip_t *sip = sr->sr_response.sip; | |||
4777 | ||||
4778 | sip_add_tl(msg, sip, SIPTAG_ACCEPT(nua->nua_invite_accept)siptag_accept, siptag_accept_v(nua->nua_invite_accept), TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
4779 | ||||
4780 | if (!sip->sip_payload) { /* XXX - do MIME multipart? */ | |||
4781 | soa_session_t *soa = nh->nh_soa; | |||
4782 | ||||
4783 | if (soa == NULL((void*)0)) | |||
4784 | soa = nua->nua_dhandlenua_handles->nh_soa; | |||
4785 | ||||
4786 | session_include_description(soa, 0, msg, sip); | |||
4787 | } | |||
4788 | } | |||
4789 | ||||
4790 | return nua_base_server_respond(sr, tags); | |||
4791 | } |