Bug Summary

File:libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c
Location:line 1007, column 26
Description:Access to field 'du_event' results in a dereference of a null pointer (loaded from field 'sr_usage')

Annotated Source Code

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_notifier.c
26 * @brief SUBSCRIBE server, NOTIFY client and REFER server
27 *
28 * Simpler event server. See nua_event_server.c for more complex event
29 * server.
30 *
31 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
32 *
33 * @date Created: Wed Mar 8 15:10:08 EET 2006 ppessi
34 */
35
36#include "config.h"
37
38#include <stddef.h>
39#include <stdlib.h>
40#include <string.h>
41#include <limits.h>
42
43#include <assert.h>
44
45#include <sofia-sip/su_string.h>
46#include <sofia-sip/sip_protos.h>
47#include <sofia-sip/sip_extra.h>
48#include <sofia-sip/sip_status.h>
49#include <sofia-sip/sip_util.h>
50#include <sofia-sip/su_uniqueid.h>
51#include <sofia-sip/su_md5.h>
52#include <sofia-sip/token64.h>
53
54#include "nua_stack.h"
55
56/* ---------------------------------------------------------------------- */
57/* Notifier event usage */
58
59struct notifier_usage
60{
61 enum nua_substate nu_substate; /**< Subscription state */
62 sip_time_t nu_expires; /**< Expiration time */
63 sip_time_t nu_requested; /**< Requested expiration time */
64#if SU_HAVE_EXPERIMENTAL
65 char *nu_tag; /**< @ETag in last NOTIFY */
66 unsigned nu_etags:1; /**< Subscriber supports etags */
67 unsigned nu_appl_etags:1; /**< Application generates etags */
68 unsigned nu_no_body:1; /**< Suppress body */
69#endif
70};
71
72static char const *nua_notify_usage_name(nua_dialog_usage_t const *du);
73static int nua_notify_usage_add(nua_handle_t *nh,
74 nua_dialog_state_t *ds,
75 nua_dialog_usage_t *du);
76static void nua_notify_usage_remove(nua_handle_t *nh,
77 nua_dialog_state_t *ds,
78 nua_dialog_usage_t *du,
79 nua_client_request_t *cr,
80 nua_server_request_t *sr);
81static void nua_notify_usage_refresh(nua_handle_t *nh,
82 nua_dialog_state_t *ds,
83 nua_dialog_usage_t *du,
84 sip_time_t now);
85static int nua_notify_usage_shutdown(nua_handle_t *nh,
86 nua_dialog_state_t *ds,
87 nua_dialog_usage_t *du);
88
89static nua_usage_class const nua_notify_usage[1] = {
90 {
91 sizeof (struct notifier_usage), (sizeof nua_notify_usage),
92 nua_notify_usage_add,
93 nua_notify_usage_remove,
94 nua_notify_usage_name,
95 nua_base_usage_update_params,
96 NULL((void*)0),
97 nua_notify_usage_refresh,
98 nua_notify_usage_shutdown,
99 }};
100
101static char const *nua_notify_usage_name(nua_dialog_usage_t const *du)
102{
103 return "notify";
104}
105
106static
107int nua_notify_usage_add(nua_handle_t *nh,
108 nua_dialog_state_t *ds,
109 nua_dialog_usage_t *du)
110{
111 ds->ds_has_events++;
112 ds->ds_has_notifys++;
113 return 0;
114}
115
116static
117void nua_notify_usage_remove(nua_handle_t *nh,
118 nua_dialog_state_t *ds,
119 nua_dialog_usage_t *du,
120 nua_client_request_t *cr,
121 nua_server_request_t *sr)
122{
123 ds->ds_has_events--;
124 ds->ds_has_notifys--;
125}
126
127/* ====================================================================== */
128/* SUBSCRIBE server */
129
130/** @NUA_EVENT nua_i_subscribe
131 *
132 * Incoming @b SUBSCRIBE request.
133 *
134 * @b SUBSCRIBE request is used to query SIP event state or establish a SIP
135 * event subscription.
136 *
137 * @param status status code of response sent automatically by stack
138 * @param phrase response phrase sent automatically by stack
139 * @param nh operation handle associated with the incoming request
140 * @param hmagic application context associated with the handle
141 * (NULL when handle is created by the stack)
142 * @param sip SUBSCRIBE request headers
143 * @param tags NUTAG_SUBSTATE()
144 *
145 * Initial SUBSCRIBE requests are dropped with <i>489 Bad Event</i>
146 * response, unless the application has explicitly included the @Event in
147 * the list of allowed events with nua_set_params() tag NUTAG_ALLOW_EVENTS()
148 * (or SIPTAG_ALLOW_EVENTS() or SIPTAG_ALLOW_EVENTS_STR()).
149 *
150 * If the event has been allowed the application
151 * can decide whether to accept the SUBSCRIBE request or reject it. The
152 * nua_response() call responding to a SUBSCRIBE request must have
153 * NUTAG_WITH() (or NUTAG_WITH_THIS()/NUTAG_WITH_SAVED()) tag.
154 *
155 * If the application accepts the SUBSCRIBE request, it must immediately
156 * send an initial NOTIFY establishing the dialog. This is because the
157 * response to the SUBSCRIBE request may be lost by an intermediate proxy
158 * because it had forked the SUBSCRIBE request.
159 *
160 * SUBSCRIBE requests modifying (usually refreshing or terminating) an
161 * existing event subscription are accepted by default and a <i>200 OK</i>
162 * response along with a copy of previously sent NOTIFY is sent
163 * automatically to the subscriber.
164 *
165 * By default, only event subscriptions accepted are those created
166 * implicitly by REFER request. See #nua_i_refer how the application must
167 * handle the REFER requests.
168 *
169 * @par Subscription Lifetime and Terminating Subscriptions
170 *
171 * Accepting the SUBSCRIBE request creates a dialog with a <i>notifier
172 * dialog usage</i> on the handle. The dialog usage is active, until the
173 * subscriber terminates the subscription, it times out or the application
174 * terminates the usage with nua_notify() call containing the tag
175 * NUTAG_SUBSTATE(nua_substate_terminated) or @SubscriptionState header with
176 * state "terminated" and/or expiration time 0.
177 *
178 * When the subscriber terminates the subscription, the application is
179 * notified of an termination by a #nua_i_subscribe event with
180 * NUTAG_SUBSTATE(nua_substate_terminated) tag. When the subscription times
181 * out, nua automatically initiates a NOTIFY transaction. When it is
182 * terminated, the application is sent a #nua_r_notify event with
183 * NUTAG_SUBSTATE(nua_substate_terminated) tag.
184 *
185 * @sa @RFC3265, nua_notify(), NUTAG_SUBSTATE(), @SubscriptionState,
186 * @Event, nua_subscribe(), #nua_r_subscribe, #nua_i_refer, nua_refer()
187 *
188 * @END_NUA_EVENT
189 */
190
191static int nua_subscribe_server_init(nua_server_request_t *sr);
192static int nua_subscribe_server_preprocess(nua_server_request_t *sr);
193static int nua_subscribe_server_respond(nua_server_request_t*, tagi_t const *);
194static int nua_subscribe_server_report(nua_server_request_t*, tagi_t const *);
195
196nua_server_methods_t const nua_subscribe_server_methods =
197 {
198 SIP_METHOD_SUBSCRIBEsip_method_subscribe, "SUBSCRIBE",
199 nua_i_subscribe, /* Event */
200 {
201 1, /* Create dialog */
202 0, /* Initial request */
203 1, /* Target refresh request */
204 1, /* Add Contact */
205 },
206 nua_subscribe_server_init,
207 nua_subscribe_server_preprocess,
208 nua_base_server_params((void*)0),
209 nua_subscribe_server_respond,
210 nua_subscribe_server_report,
211 };
212
213int nua_subscribe_server_init(nua_server_request_t *sr)
214{
215 nua_handle_t *nh = sr->sr_owner;
216 nua_dialog_state_t *ds = nh->nh_ds;
217 sip_allow_events_t const *allow_events = NH_PGET(nh, allow_events)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_allow_events ?
((nh)->nh_prefs)->nhp_allow_events : ((nh)->nh_nua->
nua_handles->nh_prefs)->nhp_allow_events)
;
218 sip_t const *sip = sr->sr_request.sip;
219 sip_event_t *o = sip->sip_event;
220 char const *event = o ? o->o_type : NULL((void*)0);
221
222 if (sr->sr_initial || !nua_dialog_usage_get(ds, nua_notify_usage, o)) {
223 if (su_strmatch(event, "refer"))
224 /* refer event subscription should be initiated with REFER */
225 return SR_STATUS1(sr, SIP_403_FORBIDDEN)sr_status(sr, 403, sip_403_Forbidden);
226
227 /* XXX - event is case-sensitive, should use msg_header_find_item() */
228 if (!event || !msg_header_find_param(allow_events->k_common, event))
229 return SR_STATUS1(sr, SIP_489_BAD_EVENT)sr_status(sr, 489, sip_489_Bad_event);
230 }
231
232 return 0;
233}
234
235int nua_subscribe_server_preprocess(nua_server_request_t *sr)
236{
237 nua_handle_t *nh = sr->sr_owner;
238 nua_dialog_state_t *ds = nh->nh_ds;
239 nua_dialog_usage_t *du;
240 struct notifier_usage *nu;
241 sip_t const *sip = sr->sr_request.sip;
242 sip_event_t *o = sip->sip_event;
243 char const *event = o ? o->o_type : NULL((void*)0);
244 /* Maximum expiration time */
245 unsigned long expires = sip->sip_expires ? sip->sip_expires->ex_delta : 3600;
246 sip_time_t now = sip_now();
247
248 assert(nh && nh->nh_nua->nua_dhandle != nh)((nh && nh->nh_nua->nua_handles != nh) ? (void)
(0) : __assert_fail ("nh && nh->nh_nua->nua_handles != nh"
, "nua_notifier.c", 248, __PRETTY_FUNCTION__))
;
249
250 du = nua_dialog_usage_get(ds, nua_notify_usage, o);
251
252 if (du == NULL((void*)0)) {
253 /* Create a new subscription */
254 du = nua_dialog_usage_add(nh, ds, nua_notify_usage, o);
255 if (du == NULL((void*)0))
256 return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR)sr_status(sr, 500, sip_500_Internal_server_error);
257 }
258 else {
259 /* Refresh existing subscription */
260 if (su_strmatch(event, "refer")){
261 expires = NH_PGET(nh, refer_expires)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refer_expires ?
((nh)->nh_prefs)->nhp_refer_expires : ((nh)->nh_nua
->nua_handles->nh_prefs)->nhp_refer_expires)
;
262
263 SR_STATUS1(sr, SIP_200_OK)sr_status(sr, 200, sip_200_OK);}
264 }
265
266 nu = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0));
267
268 if (now + expires >= now)
269 nu->nu_requested = now + expires;
270 else
271 nu->nu_requested = SIP_TIME_MAX((sip_time_t)((msg_time_t)(9223372036854775807L *2UL+1UL))) - 1;
272
273#if SU_HAVE_EXPERIMENTAL
274 nu->nu_etags =
275 sip_suppress_body_if_match(sip) ||
276 sip_suppress_notify_if_match(sip) ||
277 sip_has_feature(sr->sr_request.sip->sip_supported, "etags");
278#endif
279
280 sr->sr_usage = du;
281
282 return sr->sr_status <= 100 ? 0 : sr->sr_status;
283}
284
285/** @internal Respond to a SUBSCRIBE request.
286 *
287 */
288static
289int nua_subscribe_server_respond(nua_server_request_t *sr, tagi_t const *tags)
290{
291 struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void*
)0))
;
292
293 msg_t *msg = sr->sr_response.msg;
294 sip_t *sip = sr->sr_response.sip;
295
296 if (200 <= sr->sr_status && sr->sr_status < 300) {
297 sip_expires_t ex[1];
298
299 sip_expires_init(ex);
300
301 if (nu) {
302 sip_time_t now = sip_now();
303
304 if (nu->nu_requested) {
305 if (sip->sip_expires) {
306 /* Expires in response can only shorten the expiration time */
307 if (nu->nu_requested > now + sip->sip_expires->ex_delta)
308 nu->nu_requested = now + sip->sip_expires->ex_delta;
309 }
310 else {
311 unsigned sub_expires = NH_PGET(sr->sr_owner, sub_expires)(((sr->sr_owner)->nh_prefs)->nhp_set_.set_bits.nhb_sub_expires
? ((sr->sr_owner)->nh_prefs)->nhp_sub_expires : ((sr
->sr_owner)->nh_nua->nua_handles->nh_prefs)->nhp_sub_expires
)
;
312 if (nu->nu_requested > now + sub_expires)
313 nu->nu_requested = now + sub_expires;
314 }
315
316 if (nu->nu_requested >= now)
317 nu->nu_expires = nu->nu_requested;
318 else
319 nu->nu_expires = now;
320
321 if (nu->nu_expires <= now)
322 nu->nu_substate = nua_substate_terminated;
323 }
324
325 if (nu->nu_expires > now)
326 ex->ex_delta = nu->nu_expires - now;
327 }
328 else {
329 /* Always add header Expires: 0 */
330 }
331
332 if (!sip->sip_expires || sip->sip_expires->ex_delta > ex->ex_delta)
333 sip_add_dup(msg, sip, (sip_header_t *)ex);
334 }
335
336 return nua_base_server_respond(sr, tags);
337}
338
339static
340int nua_subscribe_server_report(nua_server_request_t *sr, tagi_t const *tags)
341{
342 nua_handle_t *nh = sr->sr_owner;
343 nua_dialog_state_t *ds = nh->nh_ds;
344 nua_dialog_usage_t *du = sr->sr_usage;
345 struct notifier_usage *nu = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0));
346 enum nua_substate substate = nua_substate_terminated;
347 int notify = 0;
348 int retval;
349
350 if (nu && !sr->sr_terminating) {
351 substate = nu->nu_substate;
352 }
353
354 /* nu_requested is set by SUBSCRIBE and cleared when NOTIFY is sent */
355 if (nu && nu->nu_requested && substate != nua_substate_embryonic) {
356#if SU_HAVE_EXPERIMENTAL
357 sip_t const *sip = sr->sr_request.sip;
358 sip_suppress_notify_if_match_t *snim = sip_suppress_notify_if_match(sip);
359 sip_suppress_body_if_match_t *sbim = sip_suppress_body_if_match(sip);
360
361 if (!nu->nu_tag)
362 notify = 1;
363 else if (snim && su_casematch(snim->snim_tag, nu->nu_tag))
364 notify = 0;
365 else if (sbim && su_casematch(snim->snim_tag, nu->nu_tag))
366 notify = 1, nu->nu_no_body = 1;
367 else
368#endif
369 notify = 1;
370
371 notify = notify && du->du_cr != NULL((void*)0);
372 }
373
374 retval = nua_base_server_treport(sr, NUTAG_SUBSTATE(substate)nutag_substate, tag_int_v(substate), TAG_END()(tag_type_t)0, (tag_value_t)0);
375
376 if (retval >= 2 || du == NULL((void*)0))
377 return retval;
378
379 if (notify) {
380 /* Send NOTIFY (and terminate subscription, when needed) */
381 nua_dialog_usage_refresh(nh, ds, du, sip_now());
382 }
383
384 return retval;
385}
386
387/* ======================================================================== */
388/* NOTIFY client */
389
390/**@fn void nua_notify(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
391 *
392 * Send a SIP NOTIFY request message.
393 *
394 * This function is used when the application implements itself the
395 * notifier. The application must provide valid @SubscriptionState and
396 * @Event headers using SIP tags. The subscription state can be modified
397 * with NUTAG_SUBSTATE(), however, its effect is overriden by
398 * @SubscriptionState header included in the nua_notify() tags.
399 *
400 * @bug If the @Event is not given by application, stack uses the @Event
401 * header from the first subscription usage on handle.
402 *
403 * If there is no active <i>notifier dialog usage</i> or no notifier dialog
404 * usage matches the @Event header given by the application the nua_notify()
405 * request is rejected locally by the stack with status code 481. The local
406 * rejection can be bypassed if NUTAG_NEWSUB(1) is included in tags.
407 *
408 * Please note that including NUTAG_NEWSUB(1) in nua_notify() tags if there
409 * is a valid subscription may lead to an extra NOTIFY sent to subscriber if
410 * the subscription had been terminated by the subscriber or by a timeout
411 * before the nua_notify() is processed.
412 *
413 * @param nh Pointer to operation handle
414 * @param tag, value, ... List of tagged parameters
415 *
416 * @return
417 * nothing
418 *
419 * @par Related Tags:
420 * NUTAG_SUBSTATE() \n
421 * NUTAG_NEWSUB() \n
422 * Tags of nua_set_hparams() \n
423 * Header tags defined in <sofia-sip/sip_tag.h>
424 *
425 * @par Events:
426 * #nua_r_notify
427 *
428 * @sa @RFC3265, #nua_i_subscribe, #nua_i_refer, NUTAG_ALLOW_EVENTS()
429 */
430
431#if 0
432static int nua_notify_client_init(nua_client_request_t *cr,
433 msg_t *, sip_t *,
434 tagi_t const *tags);
435
436
437static int nua_notify_client_init_etag(nua_client_request_t *cr,
438 msg_t *msg, sip_t *sip,
439 tagi_t const *tags);
440
441static int nua_notify_client_request(nua_client_request_t *cr,
442 msg_t *, sip_t *,
443 tagi_t const *tags);
444static int nua_notify_client_report(nua_client_request_t *cr,
445 int status, char const *phrase,
446 sip_t const *sip,
447 nta_outgoing_t *orq,
448 tagi_t const *tags);
449#endif
450#if 0
451static nua_client_methods_t const nua_notify_client_methods = {
452 SIP_METHOD_NOTIFYsip_method_notify, "NOTIFY", /* crm_method, crm_method_name */
453 0, /* crm_extra */
454 { /* crm_flags */
455 /* create_dialog */ 1,
456 /* in_dialog */ 1,
457 /* target refresh */ 1
458 },
459 NULL((void*)0), /* crm_template */
460 nua_notify_client_init, /* crm_init */
461 nua_notify_client_request, /* crm_send */
462 NULL((void*)0), /* crm_check_restart */
463 NULL((void*)0), /* crm_recv */
464 NULL((void*)0), /* crm_preliminary */
465 nua_notify_client_report, /* crm_report */
466 NULL((void*)0), /* crm_complete */
467};
468#endif
469
470nua_client_methods_t const nua_notify_client_methods = {
471 SIP_METHOD_NOTIFYsip_method_notify, "NOTIFY", /* crm_method, crm_method_name */
472 0, /* crm_extra */
473 { /* crm_flags */
474 /* create_dialog */ 1,
475 /* in_dialog */ 1,
476 /* target refresh */ 1
477 },
478 NULL((void*)0), /* crm_template */
479 NULL((void*)0), /* crm_init */
480 NULL((void*)0), /* crm_send */
481 NULL((void*)0), /* crm_check_restart */
482 NULL((void*)0), /* crm_recv */
483 NULL((void*)0), /* crm_preliminary */
484 NULL((void*)0), /* crm_report */
485 NULL((void*)0), /* crm_complete */
486};
487
488/**@internal Send NOTIFY. */
489int nua_stack_notify(nua_t *nua,
490 nua_handle_t *nh,
491 nua_event_t e,
492 tagi_t const *tags)
493{
494 return nua_client_create(nh, e, &nua_notify_client_methods, tags);
495}
496#if 0
497static int nua_notify_client_init(nua_client_request_t *cr,
498 msg_t *msg, sip_t *sip,
499 tagi_t const *tags)
500{
501 nua_handle_t *nh = cr->cr_owner;
502 nua_dialog_usage_t *du;
503 struct notifier_usage *nu;
504 sip_event_t const *o = sip->sip_event;
505 sip_subscription_state_t *ss = sip->sip_subscription_state;
506 sip_time_t now = sip_now();
507
508 if (o == NULL((void*)0) && nh->nh_ds->ds_has_notifys == 1)
509 o = NONE((void *)-1);
510
511 du = nua_dialog_usage_get(nh->nh_ds, nua_notify_usage, o);
512
513 if (!du) {
514 tagi_t const *newsub = tl_find_last(tags, nutag_newsub);
515
516 if (!newsub || !newsub->t_value)
517 return 0; /* Rejected eventually by nua_notify_client_request() */
518
519 /* Create new notifier */
520 du = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o);
521 if (du == NULL((void*)0))
522 return -1;
523
524 nu = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0));
525 nu->nu_expires = now;
526 }
527 else
528 nu = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0));
529
530
531 if (nu->nu_substate == nua_substate_terminated) {
532 /*Xyzzy*/;
533 }
534 else if (ss != NULL((void*)0)) {
535 /* SIPTAG_SUBSCRIPTION_STATE() overrides NUTAG_SUBSTATE() */
536 nu->nu_substate = nua_substate_make(ss->ss_substate);
537
538 if (ss->ss_expires) {
539 unsigned long expires = strtoul(ss->ss_expires, NULL((void*)0), 10);
540 if (now + expires < now)
541 expires = SIP_TIME_MAX((sip_time_t)((msg_time_t)(9223372036854775807L *2UL+1UL))) - now - 1;
542
543 /* We can change the lifetime of unsolicited subscription at will */
544 if (nu->nu_requested == 0)
545 nu->nu_expires = nu->nu_requested = now + expires;
546 /* Notifier can only shorten the subscribed time */
547 else if (nu->nu_requested >= now + expires)
548 nu->nu_expires = nu->nu_requested = now + expires;
549 }
550 else {
551 if (nu->nu_requested >= nu->nu_expires)
552 nu->nu_expires = nu->nu_requested;
553 }
554
555 }
556 else {
557 enum nua_substate substate = nu->nu_substate;
558
559 if (nu->nu_requested >= nu->nu_expires)
560 nu->nu_expires = nu->nu_requested;
561
562 if (nu->nu_expires > now) {
563 tagi_t const *t = tl_find_last(tags, nutag_substate);
564 if (t)
565 substate = (enum nua_substate)t->t_value;
566 }
567 else
568 substate = nua_substate_terminated;
569
570 switch (substate) {
571 case nua_substate_embryonic:
572 /*FALLTHROUGH*/
573 case nua_substate_pending:
574 nu->nu_substate = nua_substate_pending;
575 break;
576 case nua_substate_active:
577 default:
578 nu->nu_substate = nua_substate_active;
579 break;
580 case nua_substate_terminated:
581 nu->nu_substate = nua_substate_terminated;
582 break;
583 }
584 }
585
586 cr->cr_usage = du;
587
588 return nua_notify_client_init_etag(cr, msg, sip, tags);
589}
590
591static int nua_notify_client_init_etag(nua_client_request_t *cr,
592 msg_t *msg, sip_t *sip,
593 tagi_t const *tags)
594{
595#if SU_HAVE_EXPERIMENTAL
596 nua_handle_t *nh = cr->cr_owner;
597 struct notifier_usage *nu = nua_dialog_usage_private(cr->cr_usage)((cr->cr_usage) ? (void*)((cr->cr_usage) + 1) : ((void*
)0))
;
598 nua_server_request_t *sr;
599
600 if (nu->nu_tag)
601 su_free(nh->nh_home, nu->nu_tag), nu->nu_tag = NULL((void*)0);
602 nu->nu_no_body = 0;
603
604 if (sip->sip_etag) {
605 nu->nu_appl_etags = 1;
606 nu->nu_tag = su_strdup(nh->nh_home, sip->sip_etag->g_string);
607 }
608 else if (!nu->nu_appl_etags && nu->nu_etags) {
609 su_md5_t md5[1];
610 unsigned char digest[SU_MD5_DIGEST_SIZE16];
611 sip_payload_t pl[1] = { SIP_PAYLOAD_INIT(){{{ 0, 0, sip_payload_class }}} };
612 char token[2 * 16];
613
614 su_md5_init(md5);
615
616 if (sip->sip_payload) *pl = *sip->sip_payload;
617
618 if (pl->pl_len)
619 su_md5_update(md5, pl->pl_data, pl->pl_len);
620 su_md5_update(md5, &pl->pl_len, sizeof(pl->pl_len));
621
622 if (sip->sip_content_type)
623 su_md5_striupdate(md5, sip->sip_content_type->c_type);
624
625 su_md5_digest(md5, digest);
626 token64_e(token, sizeof token, digest, sizeof digest);
627 token[(sizeof token) - 1] = '\0';
628 nu->nu_tag = su_strdup(nh->nh_home, token);
629 }
630
631 if (!nu->nu_requested || !nu->nu_tag)
632 return 0;
633
634 /* Check if SUBSCRIBE had matching suppression */
635 for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next)
636 if (sr->sr_usage == cr->cr_usage && sr->sr_method == sip_method_subscribe)
637 break;
638
639 if (sr) {
640 sip_t const *sip = sr->sr_request.sip;
641
642 sip_suppress_body_if_match_t *sbim;
643 sip_suppress_notify_if_match_t *snim;
644
645 if (cr->cr_usage->du_ready) {
646 snim = sip_suppress_notify_if_match(sip);
647
648 if (snim && su_casematch(snim->snim_tag, nu->nu_tag)) {
649 if (nu->nu_requested > nu->nu_expires)
650 nu->nu_expires = nu->nu_requested;
651 nu->nu_requested = 0;
652 return nua_client_return(cr, 202, "NOTIFY Suppressed", msg);
653 }
654 }
655
656 sbim = sip_suppress_body_if_match(sip);
657 if (sbim && su_casematch(sbim->sbim_tag, nu->nu_tag))
658 nu->nu_no_body = 1;
659 }
660#endif
661
662 return 0;
663}
664
665static
666int nua_notify_client_request(nua_client_request_t *cr,
667 msg_t *msg, sip_t *sip,
668 tagi_t const *tags)
669{
670 nua_dialog_usage_t *du = cr->cr_usage;
671 struct notifier_usage *nu = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0));
672 su_home_t *home = msg_home(msg)((su_home_t*)(msg));
673 sip_time_t now = sip_now();
674 sip_subscription_state_t *ss = sip->sip_subscription_state;
675 char const *expires;
676
677 if (du == NULL((void*)0)) /* Subscription has been terminated */
678 return nua_client_return(cr, SIP_481_NO_TRANSACTION481, sip_481_No_transaction, msg);
679
680 assert(du && nu)((du && nu) ? (void) (0) : __assert_fail ("du && nu"
, "nua_notifier.c", 680, __PRETTY_FUNCTION__))
;
681
682 if (du && nua_client_bind(cr, du) < 0)
683 return -1;
684
685 if (nu->nu_requested)
686 nu->nu_expires = nu->nu_requested;
687 nu->nu_requested = 0;
688
689 if (nu->nu_expires <= now || du->du_shutdown) {
690 nu->nu_substate = nua_substate_terminated;
691 expires = "expires=0";
692 }
693 else {
694 expires = su_sprintf(home, "expires=%lu", nu->nu_expires - now);
695 }
696
697 if (ss == NULL((void*)0) || nua_substate_make(ss->ss_substate) != nu->nu_substate) {
698 if (nu->nu_substate == nua_substate_terminated)
699 expires = nu->nu_expires > now ? "reason=noresource" : "reason=timeout";
700
701 ss = sip_subscription_state_format(home, "%s;%s",
702 nua_substate_name(nu->nu_substate),
703 expires);
704
705 msg_header_insert(msg, (void *)sip, (void *)ss);
706 }
707 else if (nu->nu_substate != nua_substate_terminated) {
708 msg_header_replace_param(home, ss->ss_common, expires);
709 }
710
711#if SU_HAVE_EXPERIMENTAL
712 if (nu->nu_tag && !sip->sip_etag)
713 msg_header_add_make(msg, (void *)sip, sip_etag_class, nu->nu_tag);
714
715 if (nu->nu_no_body) {
716 nu->nu_no_body = 0;
717 msg_header_remove(msg, (void *)sip, (void *)sip->sip_payload);
718 msg_header_remove(msg, (void *)sip, (void *)sip->sip_content_length);
719 }
720#endif
721
722 if (nu->nu_substate == nua_substate_terminated)
723 nua_client_set_terminating(cr, 1);
724
725 if (cr->cr_terminating) {
726 nua_server_request_t *sr;
727 for (sr = du->du_dialog->ds_sr; sr; sr = sr->sr_next) {
728 if (sr->sr_usage == du) {
729 /* If subscribe has not been responded, don't terminate usage by NOTIFY */
730 sr->sr_terminating = 1;
731 // nua_client_set_terminating(cr, 0);
732 break;
733 }
734 }
735 }
736
737 if (du->du_event && !sip->sip_event)
738 sip_add_dup(cr->cr_msg, sip, (sip_header_t *)du->du_event);
739
740 return nua_base_client_request(cr, msg, sip, tags);
741}
742#endif
743/** @NUA_EVENT nua_r_notify
744 *
745 * Response to an outgoing @b NOTIFY request.
746 *
747 * The @b NOTIFY may be sent explicitly by nua_notify() or implicitly by NUA
748 * state machine. Implicit @b NOTIFY is sent when an established dialog is
749 * refreshed by client or it is terminated (either by client or because of a
750 * timeout).
751 *
752 * The current subscription state is included in NUTAG_SUBSTATE() tag. The
753 * nua_substate_terminated indicates that the subscription is terminated,
754 * the notifier usage has been removed and when there was no other usages of
755 * the dialog the dialog state is also removed.
756 *
757 * @param status response status code
758 * (if the request is retried, @a status is 100, the @a
759 * sip->sip_status->st_status contain the real status code
760 * from the response message, e.g., 302, 401, or 407)
761 * @param phrase a short textual description of @a status code
762 * @param nh operation handle associated with the subscription
763 * @param hmagic application context associated with the handle
764 * @param sip response to @b NOTIFY request or NULL upon an error
765 * (status code is in @a status and
766 * descriptive message in @a phrase parameters)
767 * @param tags NUTAG_SUBSTATE() indicating subscription state
768 * SIPTAG_EVENT() indicating subscription event
769 *
770 * @sa nua_notify(), @RFC3265, #nua_i_subscribe, #nua_i_refer, NUTAG_SUBSTATE()
771 *
772 * @END_NUA_EVENT
773 */
774#if 0
775static int nua_notify_client_report(nua_client_request_t *cr,
776 int status, char const *phrase,
777 sip_t const *sip,
778 nta_outgoing_t *orq,
779 tagi_t const *tags)
780{
781 nua_handle_t *nh = cr->cr_owner;
782 nua_dialog_usage_t *du = cr->cr_usage;
783 struct notifier_usage *nu = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0));
784 enum nua_substate substate = nua_substate_terminated;
785
786 if (nu && !cr->cr_terminated)
787 substate = nu->nu_substate;
788
789 nua_stack_tevent(nh->nh_nua, nh,
790 nta_outgoing_getresponse(orq),
791 (enum nua_event_e)cr->cr_event,
792 status, phrase,
793 NUTAG_SUBSTATE(substate)nutag_substate, tag_int_v(substate),
794 SIPTAG_EVENT(du ? du->du_event : NULL)siptag_event, siptag_event_v(du ? du->du_event : ((void*)0
))
,
795 TAG_NEXT(tags)tag_next, (tag_value_t)(tags));
796
797 if (du && du->du_cr == cr && !cr->cr_terminated) {
798 if (nu->nu_requested) {
799 /* Re-SUBSCRIBEd while NOTIFY was in progress, resend NOTIFY */
800 nua_client_resend_request(cr, 0);
801 }
802 else if (nu->nu_expires) {
803 nua_dialog_usage_set_refresh_at(du, nu->nu_expires);
804 }
805 }
806
807 return 0;
808}
809#endif
810
811static void nua_notify_usage_refresh(nua_handle_t *nh,
812 nua_dialog_state_t *ds,
813 nua_dialog_usage_t *du,
814 sip_time_t now)
815{
816 struct notifier_usage *nu = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0));
817 nua_client_request_t *cr = du->du_cr;
818 nua_event_t e = nua_r_notify;
819
820 if (cr) {
821 int terminating = 0;
822
823 if (nu->nu_expires && nu->nu_expires <= now)
824 terminating = 1;
825 else if (nu->nu_requested && nu->nu_requested <= now)
826 terminating = 1;
827
828 if (nua_client_resend_request(cr, terminating) >= 0)
829 return;
830 }
831 else {
832 if (nua_client_create(nh, e, &nua_notify_client_methods, NULL((void*)0)) >= 0)
833 return;
834 }
835
836 nua_stack_tevent(nh->nh_nua, nh, NULL((void*)0), e, NUA_ERROR_AT(__FILE__, __LINE__)900, "Internal error at " "nua_notifier.c" ":" "836",
837 NUTAG_SUBSTATE(nua_substate_terminated)nutag_substate, tag_int_v(nua_substate_terminated),
838 TAG_END()(tag_type_t)0, (tag_value_t)0);
839
840 nua_dialog_usage_remove(nh, ds, du, NULL((void*)0), NULL((void*)0));
841}
842
843/** @interal Shut down NOTIFY usage.
844 *
845 * @retval >0 shutdown done
846 * @retval 0 shutdown in progress
847 * @retval <0 try again later
848 */
849static int nua_notify_usage_shutdown(nua_handle_t *nh,
850 nua_dialog_state_t *ds,
851 nua_dialog_usage_t *du)
852{
853 struct notifier_usage *nu = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0));
854 //nua_client_request_t *cr = du->du_cr;
855
856 nu->nu_substate = nua_substate_terminated;
857#if 0
858 if (cr) {
859 SU_DEBUG_5(("%s(%p, %p, %p): using existing cr=%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)) >= 5 ? (_su_llog((nua_log), 5, "nua_notifier.c"
, (const char *)__func__, 861, "%s(%p, %p, %p): using existing cr=%p\n"
, "nua_notify_usage_shutdown", (void *)nh, (void *)ds, (void *
)du, (void *)cr)) : (void)0)
860 "nua_notify_usage_shutdown",((((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_notifier.c"
, (const char *)__func__, 861, "%s(%p, %p, %p): using existing cr=%p\n"
, "nua_notify_usage_shutdown", (void *)nh, (void *)ds, (void *
)du, (void *)cr)) : (void)0)
861 (void *)nh, (void *)ds, (void *)du, (void *)cr))((((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_notifier.c"
, (const char *)__func__, 861, "%s(%p, %p, %p): using existing cr=%p\n"
, "nua_notify_usage_shutdown", (void *)nh, (void *)ds, (void *
)du, (void *)cr)) : (void)0)
;
862
863 if (nua_client_resend_request(cr, 1) >= 0)
864 return 0;
865 }
866 else {
867 SU_DEBUG_5(("%s(%p, %p, %p): new NOTIFY cr for %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_notifier.c"
, (const char *)__func__, 870, "%s(%p, %p, %p): new NOTIFY cr for %s\n"
, "nua_notify_usage_shutdown", (void *)nh, (void *)ds, (void *
)du, du->du_event ? du->du_event->o_type : "<implicit>"
)) : (void)0)
868 "nua_notify_usage_shutdown",((((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_notifier.c"
, (const char *)__func__, 870, "%s(%p, %p, %p): new NOTIFY cr for %s\n"
, "nua_notify_usage_shutdown", (void *)nh, (void *)ds, (void *
)du, du->du_event ? du->du_event->o_type : "<implicit>"
)) : (void)0)
869 (void *)nh, (void *)ds, (void *)du,((((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_notifier.c"
, (const char *)__func__, 870, "%s(%p, %p, %p): new NOTIFY cr for %s\n"
, "nua_notify_usage_shutdown", (void *)nh, (void *)ds, (void *
)du, du->du_event ? du->du_event->o_type : "<implicit>"
)) : (void)0)
870 du->du_event ? du->du_event->o_type : "<implicit>"))((((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_notifier.c"
, (const char *)__func__, 870, "%s(%p, %p, %p): new NOTIFY cr for %s\n"
, "nua_notify_usage_shutdown", (void *)nh, (void *)ds, (void *
)du, du->du_event ? du->du_event->o_type : "<implicit>"
)) : (void)0)
;
871
872 if (nua_client_tcreate(nh, nua_r_notify,
873 &nua_notify_client_methods,
874 SIPTAG_EVENT(du->du_event)siptag_event, siptag_event_v(du->du_event),
875 NUTAG_SUBSTATE(nua_substate_terminated)nutag_substate, tag_int_v(nua_substate_terminated),
876 TAG_END()(tag_type_t)0, (tag_value_t)0) >= 0)
877 return 0;
878 }
879#endif
880 nua_dialog_usage_remove(nh, ds, du, NULL((void*)0), NULL((void*)0));
881 return 200;
882}
883
884/* ======================================================================== */
885/* REFER */
886/* RFC 3515 */
887
888static int nua_refer_server_init(nua_server_request_t *sr);
889static int nua_refer_server_preprocess(nua_server_request_t *sr);
890static int nua_refer_server_respond(nua_server_request_t*, tagi_t const *);
891static int nua_refer_server_report(nua_server_request_t*, tagi_t const *);
892
893nua_server_methods_t const nua_refer_server_methods =
894 {
895 SIP_METHOD_REFERsip_method_refer, "REFER",
896 nua_i_refer, /* Event */
897 {
898 1, /* Create dialog */
899 0, /* Initial request */
900 1, /* Target refresh request */
901 1, /* Add Contact */
902 },
903 nua_refer_server_init,
904 nua_refer_server_preprocess,
905 nua_base_server_params((void*)0),
906 nua_refer_server_respond,
907 nua_refer_server_report,
908 };
909
910static int nua_refer_server_init(nua_server_request_t *sr)
911{
912 return 0;
913}
914
915static int nua_refer_server_preprocess(nua_server_request_t *sr)
916{
917 nua_handle_t *nh = sr->sr_owner;
918 sip_t const *sip = sr->sr_request.sip;
919 struct notifier_usage *nu;
920 sip_event_t *o;
921
922 if (nh->nh_ds->ds_got_referrals || NH_PGET(nh, refer_with_id)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refer_with_id ?
((nh)->nh_prefs)->nhp_refer_with_id : ((nh)->nh_nua
->nua_handles->nh_prefs)->nhp_refer_with_id)
)
923 o = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq);
924 else
925 o = sip_event_make(nh->nh_home, "refer");
926
927 if (o) {
928 sr->sr_usage = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o);
929 msg_header_free(nh->nh_home, (msg_header_t *)o);
930 }
931
932 if (!sr->sr_usage)
933 return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR)sr_status(sr, 500, sip_500_Internal_server_error);
934
935 nu = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void*
)0))
;
936 nu->nu_requested = sip_now() + NH_PGET(nh, refer_expires)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refer_expires ?
((nh)->nh_prefs)->nhp_refer_expires : ((nh)->nh_nua
->nua_handles->nh_prefs)->nhp_refer_expires)
;
937
938 return 0;
939}
940
941static
942int nua_refer_server_respond(nua_server_request_t *sr, tagi_t const *tags)
943{
944 nua_handle_t *nh = sr->sr_owner;
945 struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void*
)0))
;
946 sip_refer_sub_t const *rs = sip_refer_sub(sr->sr_response.sip)((sip_refer_sub_t *)msg_header_access((msg_pub_t*)(sr->sr_response
.sip), sip_refer_sub_class))
;
947
948 if (sr->sr_status < 200 || nu == NULL((void*)0)) {
949 }
950 else if (sr->sr_status < 300 &&
951 /* No subscription if Refer-Sub: false in response */
952 (rs == NULL((void*)0) || !su_casematch(rs->rs_value, "false"))) {
953 sr->sr_usage->du_ready = 1;
954
955 nu->nu_expires = sip_now() + NH_PGET(nh, refer_expires)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refer_expires ?
((nh)->nh_prefs)->nhp_refer_expires : ((nh)->nh_nua
->nua_handles->nh_prefs)->nhp_refer_expires)
;
956
957 if (sr->sr_application) /* Application responded to REFER */
958 nu->nu_substate = nua_substate_active;
959 }
960 else {
961 /* Destroy the implicit subscription usage */
962 sr->sr_terminating = 1;
963 }
964
965 return nua_base_server_respond(sr, tags);
966}
967
968
969/** @NUA_EVENT nua_i_refer
970 *
971 * Incoming @b REFER request used to transfer calls. The tag list will
972 * contain tag NUTAG_REFER_EVENT() with the @Event header constructed from
973 * the REFER request. It will also contain the SIPTAG_REFERRED_BY() tag with
974 * the @ReferredBy header containing the identity of the party sending the
975 * REFER. The @ReferredBy structure contained in the tag is constructed from
976 * the @From header if the @ReferredBy header was not present in the REFER
977 * request.
978 *
979 * The application can let the nua to send NOTIFYs from the call it
980 * initiates with nua_invite() if it includes in the nua_invite() arguments
981 * both the NUTAG_NOTIFY_REFER() with the handle with which nua_i_refer was
982 * received and the NUTAG_REFER_EVENT() from #nua_i_refer event tags.
983 *
984 * @param status status code of response sent automatically by stack
985 * @param phrase a short textual description of @a status code
986 * @param nh operation handle associated with the incoming request
987 * @param hmagic application context associated with the handle
988 * (NULL if outside of an already established session)
989 * @param sip incoming REFER request
990 * @param tags NUTAG_REFER_EVENT() \n
991 * SIPTAG_REFERRED_BY()
992 *
993 * @sa nua_refer(), #nua_r_refer, @ReferTo, NUTAG_REFER_EVENT(),
994 * SIPTAG_REFERRED_BY(), @ReferredBy, NUTAG_NOTIFY_REFER(),
995 * NUTAG_REFER_WITH_ID(), @RFC3515.
996 *
997 * @END_NUA_EVENT
998 */
999
1000static
1001int nua_refer_server_report(nua_server_request_t *sr, tagi_t const *tags)
1002{
1003 //nua_handle_t *nh = sr->sr_owner;
1004 struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void*
)0))
;
1
Within the expansion of the macro 'nua_dialog_usage_private':
a
Assuming pointer value is null
1005 sip_t const *sip = sr->sr_request.sip;
1006 sip_referred_by_t *by = sip->sip_referred_by, default_by[1];
1007 sip_event_t const *o = sr->sr_usage->du_event;
2
Access to field 'du_event' results in a dereference of a null pointer (loaded from field 'sr_usage')
1008 enum nua_substate substate = nua_substate_terminated;
1009 //int initial = sr->sr_initial, retval;
1010 int retval;
1011
1012 if (nu) {
1013 if (!sr->sr_terminating)
1014 substate = nu->nu_substate;
1015 }
1016
1017 if (by == NULL((void*)0)) {
1018 by = sip_referred_by_init(default_by);
1019
1020 by->b_display = sip->sip_from->a_display;
1021 *by->b_url = *sip->sip_from->a_url;
1022 }
1023
1024 retval = nua_base_server_treport(sr,
1025 NUTAG_SUBSTATE(substate)nutag_substate, tag_int_v(substate),
1026 NUTAG_REFER_EVENT(o)nutag_refer_event, siptag_event_v(o),
1027 TAG_IF(by, SIPTAG_REFERRED_BY(by))!(by) ? tag_skip : siptag_referred_by, siptag_referred_by_v(by
)
,
1028 TAG_END()(tag_type_t)0, (tag_value_t)0);
1029
1030 if (retval >= 2 || nu == NULL((void*)0))
1031 return retval;
1032
1033#if 0
1034 if (initial)
1035 nua_stack_post_signal(nh,
1036 nua_r_notify,
1037 SIPTAG_EVENT(o)siptag_event, siptag_event_v(o),
1038 SIPTAG_CONTENT_TYPE_STR("message/sipfrag")siptag_content_type_str, tag_str_v("message/sipfrag"),
1039 SIPTAG_PAYLOAD_STR("SIP/2.0 100 Trying\r\n")siptag_payload_str, tag_str_v("SIP/2.0 100 Trying\r\n"),
1040 TAG_END()(tag_type_t)0, (tag_value_t)0);
1041#endif
1042 return retval;
1043}