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