File: | libsofia-sip-ua/nua/nua_session.c |
Warning: | line 2668, column 25 Although the value stored to 'uas' is used in the enclosing expression, the value is never actually read from 'uas' |
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 (status < 200 && cr->cr_answer_recv >= 200) { |
952 | LOG3("call already answered, ignoring provisional 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__, 952, "nua(%p): %s: %s %s in %u %s (%u)\n" , (void *)nh, cr->cr_method_name, ("call already answered, ignoring provisional response" ), 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); |
Although the value stored to 'uas' is used in the enclosing expression, the value is never actually read from 'uas' | |
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 | } |