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_dialog.c
26  * @brief Dialog and dialog usage handling
27  *
28  * @author Pekka Pessi <Pekka.Pessi@nokia.com>
29  *
30  * @date Created: Wed Mar  8 11:48:49 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/su_uniqueid.h>
44 
45 #include <sofia-sip/sip_protos.h>
46 #include <sofia-sip/sip_status.h>
47 
48 #define NUA_OWNER_T su_home_t
49 
50 #include "nua_dialog.h"
51 
52 #define SU_LOG (nua_log)
53 #include <sofia-sip/su_debug.h>
54 
55 #ifndef NONE
56 
57 #ifndef _MSC_VER
58 #define NONE ((void *)-1)
59 #else
60 #define NONE ((void *)(INT_PTR)-1)
61 #endif
62 #endif
63 
64 /* ======================================================================== */
65 /* Dialog handling */
66 
67 static void nua_dialog_usage_remove_at(nua_owner_t*, nua_dialog_state_t*,
68 				       nua_dialog_usage_t**,
69 				       nua_client_request_t *cr,
70 				       nua_server_request_t *sr);
71 static void nua_dialog_log_usage(nua_owner_t *, nua_dialog_state_t *);
72 
73 /**@internal
74  * UAS tag and route.
75  *
76  * Update dialog tags and route on the UAS side.
77  *
78  * @param own  dialog owner
79  * @param ds   dialog state
80  * @param sip  SIP message containing response used to update dialog
81  * @param rtag if true, set remote tag within the leg
82  */
nua_dialog_uas_route(nua_owner_t * own,nua_dialog_state_t * ds,sip_t const * sip,int rtag)83 void nua_dialog_uas_route(nua_owner_t *own,
84 			  nua_dialog_state_t *ds,
85 			  sip_t const *sip,
86 			  int rtag)
87 {
88   int established = nua_dialog_is_established(ds);
89 
90   if (!established && sip->sip_from->a_tag)
91     ds->ds_remote_tag = su_strdup(own, sip->sip_from->a_tag);
92 
93   if (ds->ds_leg == NULL)
94     return;
95 
96   nta_leg_server_route(ds->ds_leg, sip->sip_record_route, sip->sip_contact);
97   ds->ds_route = ds->ds_route || sip->sip_record_route || sip->sip_contact;
98 
99   if (rtag && !established && sip->sip_from->a_tag)
100     nta_leg_rtag(ds->ds_leg, sip->sip_from->a_tag);
101 }
102 
103 /**@internal
104  * UAC tag and route.
105  *
106  * Update dialog tags and route on the UAC side.
107  *
108  * @param own  dialog owner
109  * @param ds   dialog state
110  * @param sip  SIP message containing response used to update dialog
111  * @param rtag if true, set remote tag within the leg
112  * @param initial if true, @a sip is response to initial transaction
113  */
nua_dialog_uac_route(nua_owner_t * own,nua_dialog_state_t * ds,sip_t const * sip,int rtag,int initial)114 void nua_dialog_uac_route(nua_owner_t *own,
115 			  nua_dialog_state_t *ds,
116 			  sip_t const *sip,
117 			  int rtag,
118 			  int initial)
119 {
120   int established = nua_dialog_is_established(ds);
121   int status = sip->sip_status->st_status;
122 
123   if (!established && sip->sip_to->a_tag)
124     ds->ds_remote_tag = su_strdup(own, sip->sip_to->a_tag);
125 
126   if (ds->ds_leg == NULL)
127     return;
128 
129   if (initial && status >= 200)
130     nta_leg_client_reroute(ds->ds_leg, sip->sip_record_route, sip->sip_contact, 1);
131   else
132     nta_leg_client_reroute(ds->ds_leg, sip->sip_record_route, sip->sip_contact, 0);
133 
134   ds->ds_route = ds->ds_route || sip->sip_record_route || sip->sip_contact;
135 
136   if (rtag && !established && sip->sip_to->a_tag)
137     nta_leg_rtag(ds->ds_leg, sip->sip_to->a_tag);
138 }
139 
140 /**@internal Store information from remote endpoint. */
nua_dialog_store_peer_info(nua_owner_t * own,nua_dialog_state_t * ds,sip_t const * sip)141 void nua_dialog_store_peer_info(nua_owner_t *own,
142 				nua_dialog_state_t *ds,
143 				sip_t const *sip)
144 {
145   nua_dialog_peer_info_t *nr = ds->ds_remote_ua;
146   nua_dialog_usage_t *du;
147   nua_dialog_peer_info_t old[1];
148 
149   *old = *nr;
150 
151   if (sip && sip->sip_status &&
152       sip->sip_status->st_status >= 300 &&
153       sip->sip_status->st_status <= 399)
154     sip = NULL;			/* Redirected */
155 
156   if (sip == NULL) {
157     nr->nr_via = NULL, su_free(own, old->nr_via);
158     nr->nr_allow = NULL, su_free(own, old->nr_allow);
159     nr->nr_accept = NULL, su_free(own, old->nr_accept);
160     nr->nr_require = NULL, su_free(own, old->nr_require);
161     nr->nr_supported = NULL, su_free(own, old->nr_supported);
162     nr->nr_user_agent = NULL, su_free(own, old->nr_user_agent);
163     return;
164   }
165 
166   if (sip->sip_allow) {
167     nr->nr_allow = sip_allow_dup(own, sip->sip_allow);
168     su_free(own, old->nr_allow);
169   }
170 
171   if (sip->sip_accept) {
172     nr->nr_accept = sip_accept_dup(own, sip->sip_accept);
173     su_free(own, old->nr_accept);
174   }
175 
176   if (sip->sip_require) {
177     nr->nr_require = sip_require_dup(own, sip->sip_require);
178     su_free(own, old->nr_require);
179   }
180 
181   if (sip->sip_supported) {
182     nr->nr_supported = sip_supported_dup(own, sip->sip_supported);
183     su_free(own, old->nr_supported);
184   }
185 
186   if (sip->sip_via) {
187     nr->nr_via = sip_via_dup(own, sip->sip_via);
188     su_free(own, old->nr_via);
189   }
190 
191   if (sip->sip_user_agent) {
192     nr->nr_user_agent = sip_user_agent_dup(own, sip->sip_user_agent);
193     su_free(own, old->nr_user_agent);
194   }
195   else if (sip->sip_server) {
196     nr->nr_user_agent = sip_user_agent_dup(own, sip->sip_server);
197     su_free(own, old->nr_user_agent);
198   }
199 
200   for (du = ds->ds_usage; du; du = du->du_next) {
201     if (du->du_class->usage_peer_info)
202       du->du_class->usage_peer_info(du, ds, sip);
203   }
204 }
205 
206 /** Remove dialog information. */
nua_dialog_zap(nua_owner_t * own,nua_dialog_state_t * ds)207 int nua_dialog_zap(nua_owner_t *own,
208 		   nua_dialog_state_t *ds)
209 {
210   /* zap peer info */
211   nua_dialog_store_peer_info(own, ds, NULL);
212   /* Local Contact */
213   msg_header_free(own, (msg_header_t *)ds->ds_ltarget), ds->ds_ltarget = NULL;
214   /* Leg */
215   nta_leg_destroy(ds->ds_leg), ds->ds_leg = NULL;
216   /* Remote tag */
217   su_free(own, (void *)ds->ds_remote_tag), ds->ds_remote_tag = NULL;
218   /* Ready to set route/remote target */
219   ds->ds_route = 0;
220 
221   return 0;
222 }
223 
224 /** Remove dialog (if there is no other usages). */
nua_dialog_remove(nua_owner_t * own,nua_dialog_state_t * ds,nua_dialog_usage_t * usage)225 int nua_dialog_remove(nua_owner_t *own,
226 		      nua_dialog_state_t *ds,
227 		      nua_dialog_usage_t *usage)
228 {
229   if (ds->ds_usage == usage && (usage == NULL || usage->du_next == NULL)) {
230     return nua_dialog_zap(own, ds);
231   }
232   return 0;
233 }
234 
235 /** @internal Get dialog usage slot. */
236 nua_dialog_usage_t **
nua_dialog_usage_at(nua_dialog_state_t const * ds,nua_usage_class const * kind,sip_event_t const * event)237 nua_dialog_usage_at(nua_dialog_state_t const *ds,
238 		    nua_usage_class const *kind,
239 		    sip_event_t const *event)
240 {
241   static nua_dialog_usage_t *none = NULL;
242 
243   if (ds) {
244     nua_dialog_usage_t *du, * const * prev;
245     sip_event_t const *o;
246 
247     for (prev = &ds->ds_usage; (du = *prev); prev = &du->du_next) {
248       if (du->du_class != kind)
249 	continue;
250 
251       if (event == NONE)
252 	return (nua_dialog_usage_t **)prev;
253 
254       o = du->du_event;
255 
256       if (!event && !o)
257 	return (nua_dialog_usage_t **)prev;
258 
259       if (event != o) {
260 	if (event == NULL || o == NULL)
261 	  continue;
262 	if (!su_strmatch(event->o_type, o->o_type))
263 	  continue;
264 	if (!su_casematch(event->o_id, o->o_id)) {
265 	  if (event->o_id || !su_strmatch(event->o_type, "refer"))
266 	    continue;
267 	}
268       }
269 
270       return (nua_dialog_usage_t **)prev;
271     }
272   }
273 
274   return &none;
275 }
276 
277 /** @internal Get a dialog usage */
nua_dialog_usage_get(nua_dialog_state_t const * ds,nua_usage_class const * kind,sip_event_t const * event)278 nua_dialog_usage_t *nua_dialog_usage_get(nua_dialog_state_t const *ds,
279 					 nua_usage_class const *kind,
280 					 sip_event_t const *event)
281 {
282   return *nua_dialog_usage_at(ds, kind, event);
283 }
284 
285 /** @internal Get dialog usage name */
nua_dialog_usage_name(nua_dialog_usage_t const * du)286 char const *nua_dialog_usage_name(nua_dialog_usage_t const *du)
287 {
288   if (du == NULL)
289     return "<NULL>";
290   return du->du_class->usage_name(du);
291 }
292 
293 /** @internal Add dialog usage */
nua_dialog_usage_add(nua_owner_t * own,struct nua_dialog_state * ds,nua_usage_class const * uclass,sip_event_t const * event)294 nua_dialog_usage_t *nua_dialog_usage_add(nua_owner_t *own,
295 					 struct nua_dialog_state *ds,
296 					 nua_usage_class const *uclass,
297 					 sip_event_t const *event)
298 {
299   if (ds) {
300     sip_event_t *o;
301     nua_dialog_usage_t *du, **prev_du;
302 
303     prev_du = nua_dialog_usage_at(ds, uclass, event);
304     du = *prev_du;
305     if (du) {		/* Already exists */
306       SU_DEBUG_5(("nua(%p): adding already existing %s usage%s%s\n",
307 		  (void *)own, nua_dialog_usage_name(du),
308 		  event ? "  with event " : "", event ? event->o_type : ""));
309 
310       if (prev_du != &ds->ds_usage) {
311 	/* Move as a first usage in the list */
312 	*prev_du = du->du_next;
313 	du->du_next = ds->ds_usage;
314 	ds->ds_usage = du;
315       }
316       return du;
317     }
318 
319     o = event ? sip_event_dup(own, event) : NULL;
320 
321     if (o != NULL || event == NULL)
322       du = su_zalloc(own, sizeof *du + uclass->usage_size);
323 
324     if (du) {
325       su_home_ref(own);
326       du->du_dialog = ds;
327       du->du_class = uclass;
328       du->du_event = o;
329 
330       if (uclass->usage_add(own, ds, du) < 0) {
331 	su_free(own, o);
332 	su_free(own, du);
333 	return NULL;
334       }
335 
336       SU_DEBUG_5(("nua(%p): adding %s usage%s%s\n",
337 		  (void *)own, nua_dialog_usage_name(du),
338 		  o ? " with event " : "", o ? o->o_type :""));
339 
340       du->du_next = ds->ds_usage, ds->ds_usage = du;
341 
342       return du;
343     }
344 
345     su_free(own, o);
346   }
347 
348   return NULL;
349 }
350 
351 /** @internal Remove dialog usage. */
nua_dialog_usage_remove(nua_owner_t * own,nua_dialog_state_t * ds,nua_dialog_usage_t * du,nua_client_request_t * cr,nua_server_request_t * sr)352 void nua_dialog_usage_remove(nua_owner_t *own,
353 			     nua_dialog_state_t *ds,
354 			     nua_dialog_usage_t *du,
355 			     nua_client_request_t *cr,
356 			     nua_server_request_t *sr)
357 {
358   nua_dialog_usage_t **at;
359 
360   assert(own); assert(ds); assert(du);
361 
362   for (at = &ds->ds_usage; *at; at = &(*at)->du_next)
363     if (du == *at)
364       break;
365 
366   assert(*at);
367 
368   nua_dialog_usage_remove_at(own, ds, at, cr, sr);
369 }
370 
371 /** @internal Remove dialog usage.
372  *
373  * Zap dialog state (leg, tag and route) if no usages remain.
374 */
375 static void
nua_dialog_usage_remove_at(nua_owner_t * own,nua_dialog_state_t * ds,nua_dialog_usage_t ** at,nua_client_request_t * cr0,nua_server_request_t * sr0)376 nua_dialog_usage_remove_at(nua_owner_t *own,
377 			   nua_dialog_state_t *ds,
378 			   nua_dialog_usage_t **at,
379 			   nua_client_request_t *cr0,
380 			   nua_server_request_t *sr0)
381 {
382 	int unref = 0;
383 	nua_dialog_usage_t *du = NULL;
384 
385   if (*at) {
386     sip_event_t const *o = NULL;
387     nua_client_request_t *cr, *cr_next;
388     nua_server_request_t *sr, *sr_next;
389     du = *at;
390 
391     *at = du->du_next;
392 
393     o = du->du_event;
394 
395     SU_DEBUG_5(("nua(%p): removing %s usage%s%s\n",
396 		(void *)own, nua_dialog_usage_name(du),
397 		o ? " with event " : "", o ? o->o_type :""));
398     du->du_class->usage_remove(own, ds, du, cr0, sr0);
399 
400     /* Clean reference to saved client request */
401     if (du->du_cr)
402       nua_client_bind(du->du_cr, NULL);
403 
404     /* Clean references from queued client requests */
405     for (cr = ds->ds_cr; cr; cr = cr_next) {
406       cr_next = cr->cr_next;
407       if (cr->cr_usage == du)
408 	cr->cr_usage = NULL;
409     }
410 
411     /* Clean references from queued server requests */
412     for (sr = ds->ds_sr; sr; sr = sr_next) {
413       sr_next = sr->sr_next;
414       if (sr->sr_usage == du) {
415 	sr->sr_usage = NULL;
416 	if (sr != sr0)
417 	  nua_server_request_destroy(sr);
418       }
419     }
420 
421 	unref = 1;
422   }
423 
424   /* Zap dialog if there are no more usages */
425   if (ds->ds_terminating)
426     ;
427   else if (ds->ds_usage == NULL) {
428     nua_dialog_remove(own, ds, NULL);
429     ds->ds_has_events = 0;
430 	if (unref) {
431 		su_home_unref(own);
432 		su_free(own, du);
433 	}
434     return;
435   }
436   else {
437     nua_dialog_log_usage(own, ds);
438   }
439 
440   if (unref) {
441     su_home_unref(own);
442     su_free(own, du);
443   }
444 }
445 
446 static
nua_dialog_log_usage(nua_owner_t * own,nua_dialog_state_t * ds)447 void nua_dialog_log_usage(nua_owner_t *own, nua_dialog_state_t *ds)
448 {
449   nua_dialog_usage_t *du;
450 
451   if (SU_LOG->log_level >= 3) {
452     char buffer[160];
453     size_t l = 0, N = sizeof buffer;
454     ssize_t n;
455 
456     buffer[0] = '\0';
457 
458     for (du = ds->ds_usage; du; du = du->du_next) {
459       msg_header_t const *h = (void *)du->du_event;
460 
461       if (!h)
462 	continue;
463 
464       n = sip_event_e(buffer + l, N - l, h, 0);
465       if (n == -1)
466 	break;
467       l += (size_t)n;
468       if (du->du_next && l + 2 < sizeof(buffer)) {
469 	strcpy(buffer + l, ", ");
470 	l += 2;
471       }
472     }
473 
474     SU_DEBUG_3(("nua(%p): handle with %s%s%s\n", (void *)own,
475 		ds->ds_has_session ? "session and " : "",
476 		ds->ds_has_events ? "events " : "",
477 		buffer));
478   }
479 }
480 
481 /** Deinitialize dialog and its usage. @internal */
nua_dialog_deinit(nua_owner_t * own,nua_dialog_state_t * ds)482 void nua_dialog_deinit(nua_owner_t *own,
483 		       nua_dialog_state_t *ds)
484 {
485   ds->ds_terminating = 1;
486 
487   while (ds->ds_usage) {
488     nua_dialog_usage_remove_at(own, ds, &ds->ds_usage, NULL, NULL);
489   }
490 
491   nua_dialog_remove(own, ds, NULL);
492 
493   ds->ds_has_events = 0;
494   ds->ds_terminating = 0;
495 }
496 
nua_dialog_update_params(nua_dialog_state_t * ds,nua_handle_preferences_t const * changed,nua_handle_preferences_t const * params,nua_handle_preferences_t const * defaults)497 void nua_dialog_update_params(nua_dialog_state_t *ds,
498 			      nua_handle_preferences_t const *changed,
499 			      nua_handle_preferences_t const *params,
500 			      nua_handle_preferences_t const *defaults)
501 {
502   nua_dialog_usage_t *usage;
503 
504   for (usage = ds->ds_usage; usage; usage = usage->du_next) {
505     usage->du_class->usage_update_params(usage, changed, params, defaults);
506   }
507 }
508 
nua_base_usage_update_params(nua_dialog_usage_t const * du,nua_handle_preferences_t const * changed,nua_handle_preferences_t const * params,nua_handle_preferences_t const * defaults)509 void nua_base_usage_update_params(nua_dialog_usage_t const *du,
510 				  nua_handle_preferences_t const *changed,
511 				  nua_handle_preferences_t const *params,
512 				  nua_handle_preferences_t const *defaults)
513 {
514   (void)du, (void)changed, (void)params, (void)defaults;
515 }
516 
517 /**@internal
518  * Set refresh value suitably.
519  *
520  * The refresh time is set either around half of the @a delta interval or,
521  * if @a delta is less than 5 minutes but longer than 90 seconds, 30..60
522  * seconds before end of interval.
523  *
524  * If @a delta is 0, the dialog usage is never refreshed.
525  */
nua_dialog_usage_set_refresh(nua_dialog_usage_t * du,unsigned delta)526 void nua_dialog_usage_set_refresh(nua_dialog_usage_t *du, unsigned delta)
527 {
528   if (delta == 0)
529     nua_dialog_usage_reset_refresh(du);
530   else if (delta > 90 && delta < 5 * 60)
531     /* refresh 30..60 seconds before deadline */
532     nua_dialog_usage_set_refresh_range(du, delta - 60, delta - 30);
533   else {
534     /* By default, refresh around half time before deadline */
535     unsigned min = (delta + 2) / 4;
536     unsigned max = (delta + 2) / 4 + (delta + 1) / 2;
537     if (min == 0)
538       min = 1;
539     nua_dialog_usage_set_refresh_range(du, min, max);
540   }
541 }
542 
543 /**@internal Set refresh in range min..max seconds in the future. */
nua_dialog_usage_set_refresh_range(nua_dialog_usage_t * du,unsigned min,unsigned max)544 void nua_dialog_usage_set_refresh_range(nua_dialog_usage_t *du,
545 					unsigned min, unsigned max)
546 {
547   sip_time_t now = sip_now(), target;
548   unsigned delta;
549 
550   if (max < min)
551     max = min;
552 
553   if (min != max)
554     delta = su_randint(min, max);
555   else
556     delta = min;
557 
558   if (now + delta >= now)
559     target = now + delta;
560   else
561     target = SIP_TIME_MAX;
562 
563   SU_DEBUG_7(("nua(): refresh %s after %lu seconds (in [%u..%u])\n",
564 	      nua_dialog_usage_name(du), target - now, min, max));
565 
566   du->du_refquested = now;
567 
568   du->du_refresh = target;
569 }
570 
571 /** Set absolute refresh time */
nua_dialog_usage_set_refresh_at(nua_dialog_usage_t * du,sip_time_t target)572 void nua_dialog_usage_set_refresh_at(nua_dialog_usage_t *du,
573 				     sip_time_t target)
574 {
575   SU_DEBUG_7(("nua(): refresh %s after %lu seconds\n",
576 	      nua_dialog_usage_name(du), target - sip_now()));
577   du->du_refresh = target;
578 }
579 
580 /**@internal Do not refresh. */
nua_dialog_usage_reset_refresh(nua_dialog_usage_t * du)581 void nua_dialog_usage_reset_refresh(nua_dialog_usage_t *du)
582 {
583   if (du) {
584     du->du_refquested = sip_now();
585     du->du_refresh = 0;
586   }
587 }
588 
589 /** @internal Refresh usage. */
nua_dialog_usage_refresh(nua_owner_t * owner,nua_dialog_state_t * ds,nua_dialog_usage_t * du,sip_time_t now)590 void nua_dialog_usage_refresh(nua_owner_t *owner,
591 			      nua_dialog_state_t *ds,
592 			      nua_dialog_usage_t *du,
593 			      sip_time_t now)
594 {
595   assert(du && du->du_class->usage_refresh);
596   du->du_class->usage_refresh(owner, ds, du, now);
597 }
598 
599 /** Terminate all dialog usages gracefully. */
nua_dialog_shutdown(nua_owner_t * owner,nua_dialog_state_t * ds)600 int nua_dialog_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
601 {
602   nua_dialog_usage_t *du;
603 
604   ds->ds_terminating = 1;
605 
606   do {
607     for (du = ds->ds_usage; du; du = du->du_next) {
608       if (!du->du_shutdown) {
609 	nua_dialog_usage_shutdown(owner, ds, du);
610 	break;
611       }
612     }
613   } while (du);
614 
615   return 1;
616 }
617 
618 /** Shutdown (gracefully terminate) usage.
619  *
620  * @retval >0  shutdown done
621  * @retval 0   shutdown in progress
622  * @retval <0  try again later
623  */
nua_dialog_usage_shutdown(nua_owner_t * owner,nua_dialog_state_t * ds,nua_dialog_usage_t * du)624 int nua_dialog_usage_shutdown(nua_owner_t *owner,
625 			      nua_dialog_state_t *ds,
626 			      nua_dialog_usage_t *du)
627 {
628   if (du) {
629     nua_dialog_usage_reset_refresh(du);
630     du->du_shutdown = 1;
631     assert(du->du_class->usage_shutdown);
632     return du->du_class->usage_shutdown(owner, ds, du);
633   }
634   else
635     return 200;
636 }
637 
638 /** Repeat shutdown of all usages.
639  *
640  * @note Caller must have a reference to nh
641  */
nua_dialog_repeat_shutdown(nua_owner_t * owner,nua_dialog_state_t * ds)642 int nua_dialog_repeat_shutdown(nua_owner_t *owner, nua_dialog_state_t *ds)
643 {
644   nua_dialog_usage_t *du;
645   nua_server_request_t *sr, *sr_next;
646 
647   for (sr = ds->ds_sr; sr; sr = sr_next) {
648     sr_next = sr->sr_next;
649 
650     if (nua_server_request_is_pending(sr)) {
651       SR_STATUS1(sr, SIP_410_GONE); /* 410 terminates dialog */
652       nua_server_respond(sr, NULL);
653       nua_server_report(sr);
654     }
655   }
656 
657   for (du = ds->ds_usage; du ;) {
658     nua_dialog_usage_t *du_next = du->du_next;
659 
660     nua_dialog_usage_shutdown(owner, ds, du);
661 
662     if (du_next == NULL)
663       break;
664 
665     for (du = ds->ds_usage; du; du = du->du_next) {
666       if (du == du_next)
667 	break;
668       else if (!du->du_shutdown)
669 	break;
670     }
671   }
672 
673   return ds->ds_usage != NULL;
674 }
675