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_subnotref.c
26  * @brief Subscriber (event watcher)
27  *
28  * This file contains implementation SUBSCRIBE UAC, NOTIFY UAS, REFER UAC.
29  * The implementation of SUBSCRIBE UAS, NOTIFY UAC and REFER UAS is in
30  * nua_notifier.c.
31  * Alternative implementation using nea is in nua_event_server.c.
32  *
33  * @author Pekka Pessi <Pekka.Pessi@nokia.com>
34  *
35  * @date Created: Wed Mar  8 15:10:08 EET 2006 ppessi
36  */
37 
38 #include "config.h"
39 
40 #include <stddef.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <limits.h>
44 
45 #include <assert.h>
46 
47 #include <sofia-sip/su_string.h>
48 #include <sofia-sip/sip_protos.h>
49 #include <sofia-sip/sip_status.h>
50 #include <sofia-sip/sip_extra.h>
51 #include <sofia-sip/sip_util.h>
52 #include <sofia-sip/su_uniqueid.h>
53 
54 #include "nua_stack.h"
55 
56 /* ---------------------------------------------------------------------- */
57 /* Subcriber event usage */
58 
59 struct event_usage
60 {
61   enum nua_substate eu_substate;	/**< Subscription state */
62   unsigned eu_delta;	                /**< Proposed expiration */
63   sip_time_t eu_expires;	        /**< Absolute expiration time */
64   unsigned eu_notified;		        /**< Number of NOTIFYs received */
65   unsigned eu_unsolicited:1;	        /**< Not SUBSCRIBEd or REFERed */
66   unsigned eu_refer:1;		        /**< Implied subscription by refer */
67   unsigned eu_final_wait:1;	        /**< Waiting for final NOTIFY */
68   unsigned eu_no_id:1;		        /**< Do not use "id" (even if we have one) */
69 };
70 
71 static char const *nua_subscribe_usage_name(nua_dialog_usage_t const *du);
72 static int nua_subscribe_usage_add(nua_handle_t *nh,
73 				   nua_dialog_state_t *ds,
74 				   nua_dialog_usage_t *du);
75 static void nua_subscribe_usage_remove(nua_handle_t *nh,
76 				       nua_dialog_state_t *ds,
77 				       nua_dialog_usage_t *du,
78 				       nua_client_request_t *cr,
79 				       nua_server_request_t *sr);
80 static void nua_subscribe_usage_refresh(nua_handle_t *,
81 					nua_dialog_state_t *,
82 					nua_dialog_usage_t *,
83 					sip_time_t);
84 static int nua_subscribe_usage_shutdown(nua_handle_t *,
85 					nua_dialog_state_t *,
86 					nua_dialog_usage_t *);
87 
88 static nua_usage_class const nua_subscribe_usage[1] = {
89   {
90     sizeof (struct event_usage), (sizeof nua_subscribe_usage),
91     nua_subscribe_usage_add,
92     nua_subscribe_usage_remove,
93     nua_subscribe_usage_name,
94     nua_base_usage_update_params,
95     NULL,
96     nua_subscribe_usage_refresh,
97     nua_subscribe_usage_shutdown
98   }};
99 
nua_subscribe_usage_name(nua_dialog_usage_t const * du)100 static char const *nua_subscribe_usage_name(nua_dialog_usage_t const *du)
101 {
102   return "subscribe";
103 }
104 
105 static
nua_subscribe_usage_add(nua_handle_t * nh,nua_dialog_state_t * ds,nua_dialog_usage_t * du)106 int nua_subscribe_usage_add(nua_handle_t *nh,
107 			   nua_dialog_state_t *ds,
108 			   nua_dialog_usage_t *du)
109 {
110   ds->ds_has_events++;
111   ds->ds_has_subscribes++;
112 
113   return 0;
114 }
115 
116 static
nua_subscribe_usage_remove(nua_handle_t * nh,nua_dialog_state_t * ds,nua_dialog_usage_t * du,nua_client_request_t * cr,nua_server_request_t * sr)117 void nua_subscribe_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_subscribes--;
125 }
126 
127 /* ====================================================================== */
128 /* SUBSCRIBE */
129 
130 /**@fn void nua_subscribe(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
131  *
132  *  Subscribe to a SIP event.
133  *
134  * Subscribe a SIP event using the SIP SUBSCRIBE request. If the
135  * SUBSCRBE is successful a subscription state is established and
136  * the subscription is refreshed regularly. The refresh requests will
137  * generate #nua_r_subscribe events.
138  *
139  * @param nh              Pointer to operation handle
140  * @param tag, value, ... List of tagged parameters
141  *
142  * @return
143  *    nothing
144  *
145  * @par Related Tags:
146  *    NUTAG_URL()
147  *    Header tags defined in <sofia-sip/sip_tag.h>
148  *
149  * @par Events:
150  *    #nua_r_subscribe \n
151  *    #nua_i_notify
152  *
153  * @sa NUTAG_SUBSTATE(), @RFC3265
154  */
155 
156 /**@fn void nua_unsubscribe(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
157  *
158  * Unsubscribe an event.
159  *
160  * Unsubscribe an active or pending subscription with SUBSCRIBE request
161  * containing Expires: header with value 0. The dialog associated with
162  * subscription will be destroyed if there is no other subscriptions or
163  * call using this dialog.
164  *
165  * @param nh              Pointer to operation handle
166  * @param tag, value, ... List of tagged parameters
167  *
168  * @return
169  *    nothing
170  *
171  * @par Related Tags:
172  *    SIPTAG_EVENT() or SIPTAG_EVENT_STR() \n
173  *    Header tags defined in <sofia-sip/sip_tag.h> except SIPTAG_EXPIRES() or SIPTAG_EXPIRES_STR()
174  *
175  * @par Events:
176  *    #nua_r_unsubscribe
177  *
178  * @sa NUTAG_SUBSTATE(), @RFC3265
179  */
180 
181 static int nua_subscribe_client_init(nua_client_request_t *cr,
182 				     msg_t *, sip_t *,
183 				     tagi_t const *tags);
184 static int nua_subscribe_client_request(nua_client_request_t *cr,
185 					msg_t *, sip_t *,
186 					tagi_t const *tags);
187 static int nua_subscribe_client_response(nua_client_request_t *cr,
188 					 int status, char const *phrase,
189 					 sip_t const *sip);
190 
191 static nua_client_methods_t const nua_subscribe_client_methods = {
192   SIP_METHOD_SUBSCRIBE,		/* crm_method, crm_method_name */
193   0,				/* crm_extra */
194   {				/* crm_flags */
195     /* create_dialog */ 1,
196     /* in_dialog */ 1,
197     /* target refresh */ 1
198   },
199   NULL,				/* crm_template */
200   nua_subscribe_client_init,	/* crm_init */
201   nua_subscribe_client_request,	/* crm_send */
202   NULL,				/* crm_check_restart */
203   nua_subscribe_client_response, /* crm_recv */
204   NULL,				/* crm_preliminary */
205   NULL,				/* crm_report */
206   NULL,				/* crm_complete */
207 };
208 
209 int
nua_stack_subscribe(nua_t * nua,nua_handle_t * nh,nua_event_t e,tagi_t const * tags)210 nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
211 		    tagi_t const *tags)
212 {
213   return nua_client_create(nh, e, &nua_subscribe_client_methods, tags);
214 }
215 
nua_subscribe_client_init(nua_client_request_t * cr,msg_t * msg,sip_t * sip,tagi_t const * tags)216 static int nua_subscribe_client_init(nua_client_request_t *cr,
217 				     msg_t *msg, sip_t *sip,
218 				     tagi_t const *tags)
219 {
220   nua_handle_t *nh = cr->cr_owner;
221   nua_dialog_usage_t *du;
222   sip_event_t *o = sip->sip_event;
223 
224   du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, o);
225 
226   if (du == NULL && o == NULL)
227     du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, NONE);
228 
229   if (du) {
230     if (du->du_event && o == NULL)
231       /* Add Event header */
232       sip_add_dup(msg, sip, (sip_header_t *)du->du_event);
233   }
234   else if (cr->cr_event == nua_r_subscribe) {
235     /* Create dialog usage */
236     du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, o);
237     /* Note that we allow SUBSCRIBE without event */
238   }
239 
240   cr->cr_usage = du;
241 
242   return 0;
243 }
244 
nua_subscribe_client_request(nua_client_request_t * cr,msg_t * msg,sip_t * sip,tagi_t const * tags)245 static int nua_subscribe_client_request(nua_client_request_t *cr,
246 					msg_t *msg, sip_t *sip,
247 					tagi_t const *tags)
248 {
249   nua_dialog_usage_t *du = cr->cr_usage;
250   sip_time_t expires = 0;
251 
252   if (cr->cr_event == nua_r_destroy || !du || du->du_shutdown)
253     nua_client_set_terminating(cr, 1);
254 
255   if (du) {
256     struct event_usage *eu = nua_dialog_usage_private(du);
257     sip_event_t *o = sip->sip_event;
258 
259     if (nua_client_bind(cr, du) < 0)
260       return -1;
261 
262     if (eu->eu_no_id && o && o->o_id) {
263       /* Notifier does not handle id properly, remove it */
264       msg_header_remove_param(o->o_common, "id");
265     }
266 
267 #if 0
268     if (cr->cr_terminating) {
269       /* Already terminated subscription? */
270       if (eu->eu_substate == nua_substate_terminated ||
271 	  eu->eu_substate == nua_substate_embryonic) {
272 	return nua_client_return(cr, SIP_200_OK, msg);
273       }
274     }
275 #endif
276 
277     nua_dialog_usage_reset_refresh(du); /* during SUBSCRIBE transaction */
278 
279     if (cr->cr_terminating || cr->cr_event != nua_r_subscribe)
280       expires = eu->eu_delta = 0;
281     else if (sip->sip_expires)
282       /* Use value specified by application or negotiated with Min-Expires */
283       expires = eu->eu_delta = sip->sip_expires->ex_delta;
284     else
285     /* We just use common default value, but the default is actually
286        package-specific according to the RFC 3265 section 4.4.4:
287        [Event] packages MUST also define a
288        default "Expires" value to be used if none is specified. */
289       expires = eu->eu_delta = 3600;
290 
291     eu->eu_final_wait = 0;
292 
293     if (eu->eu_substate == nua_substate_terminated)
294       eu->eu_substate = nua_substate_embryonic;
295   }
296 
297   if (!sip->sip_expires || sip->sip_expires->ex_delta != expires) {
298     sip_expires_t ex[1];
299     sip_expires_init(ex)->ex_delta = expires;
300     sip_add_dup(msg, sip, (sip_header_t *)ex);
301   }
302 
303   return nua_base_client_request(cr, msg, sip, tags);
304 }
305 
306 /** @NUA_EVENT nua_r_subscribe
307  *
308  * Response to an outgoing SUBSCRIBE request.
309  *
310  * The SUBSCRIBE request may have been sent explicitly by nua_subscribe() or
311  * implicitly by NUA state machine.
312  *
313  * @param status response status code
314  *               (if the request is retried, @a status is 100, the @a
315  *               sip->sip_status->st_status contain the real status code
316  *               from the response message, e.g., 302, 401, or 407)
317  * @param phrase a short textual description of @a status code
318  * @param nh     operation handle associated with the subscription
319  * @param hmagic application context associated with the handle
320  * @param sip    response to SUBSCRIBE request or NULL upon an error
321  *               (status code is in @a status and
322  *                descriptive message in @a phrase parameters)
323  * @param tags   NUTAG_SUBSTATE()
324  *
325  * @sa nua_subscribe(), @RFC3265
326  *
327  * @END_NUA_EVENT
328  */
329 
330 /** @NUA_EVENT nua_r_unsubscribe
331  *
332  * Response to an outgoing un-SUBSCRIBE.
333  *
334  * @param status response status code
335  *               (if the request is retried, @a status is 100, the @a
336  *               sip->sip_status->st_status contain the real status code
337  *               from the response message, e.g., 302, 401, or 407)
338  * @param phrase a short textual description of @a status code
339  * @param nh     operation handle associated with the subscription
340  * @param hmagic application context associated with the handle
341  * @param sip    response to SUBSCRIBE request or NULL upon an error
342  *               (status code is in @a status and
343  *                descriptive message in @a phrase parameters)
344  * @param tags   NUTAG_SUBSTATE()
345  *
346  * @sa nua_unsubscribe(), @RFC3265
347  *
348  * @END_NUA_EVENT
349  */
350 
nua_subscribe_client_response(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip)351 static int nua_subscribe_client_response(nua_client_request_t *cr,
352 					 int status, char const *phrase,
353 					 sip_t const *sip)
354 {
355   nua_handle_t *nh = cr->cr_owner;
356   nua_dialog_usage_t *du = cr->cr_usage;
357   struct event_usage *eu = nua_dialog_usage_private(du);
358   enum nua_substate substate;
359 
360   if (eu == NULL || cr->cr_terminated)
361     substate = nua_substate_terminated;
362   else if (status >= 300)
363     substate = eu->eu_substate;
364   else {
365     int win_messenger_enable = NH_PGET(nh, win_messenger_enable);
366     sip_time_t delta, now = sip_now();
367 
368     du->du_ready = 1;
369 
370     if (eu->eu_substate != nua_substate_terminated)
371       /* If there is no @Expires header,
372 	 use default value stored in eu_delta */
373       delta = sip_contact_expires(NULL, sip->sip_expires, sip->sip_date,
374 				  eu->eu_delta, now);
375     else
376       delta = 0;
377 
378     if (delta > eu->eu_delta)
379       delta = eu->eu_delta;
380 
381     if (win_messenger_enable && !nua_dialog_is_established(nh->nh_ds)) {
382       /* Notify from messanger does not match with dialog tag */
383       nh->nh_ds->ds_remote_tag = su_strdup(nh->nh_home, "");
384     }
385 
386     if (delta > 0) {
387       nua_dialog_usage_set_refresh(du, delta);
388       eu->eu_expires = du->du_refquested + delta;
389     }
390     else {
391       if (eu->eu_substate == nua_substate_terminated) {
392 	if (!eu->eu_notified)
393 	  eu->eu_substate = nua_substate_embryonic;
394       }
395 
396       if (eu->eu_substate != nua_substate_terminated) {
397 	/* Wait 32 seconds for NOTIFY. */
398 	delta = 64 * NTA_SIP_T1 / 1000;
399 
400 	eu->eu_final_wait = 1;
401 
402 	if (!eu->eu_notified && win_messenger_enable)
403 	  delta = 4 * 60; 	/* Wait 4 minutes for NOTIFY from Messenger */
404 
405 	nua_dialog_usage_set_refresh_range(du, delta, delta);
406       }
407       else {
408 	nua_dialog_usage_reset_refresh(du);
409       }
410 
411       eu->eu_expires = du->du_refquested;
412     }
413 
414     substate = eu->eu_substate;
415 
416     if (substate == nua_substate_terminated)
417       /* let nua_base_client_tresponse to remove usage */
418       cr->cr_terminated = 1;
419   }
420 
421   return nua_base_client_tresponse(cr, status, phrase, sip,
422 				   NUTAG_SUBSTATE(substate),
423 				   SIPTAG_EVENT(du ? du->du_event : NULL),
424 				   TAG_END());
425 }
426 
427 /** Refresh subscription */
nua_subscribe_usage_refresh(nua_handle_t * nh,nua_dialog_state_t * ds,nua_dialog_usage_t * du,sip_time_t now)428 static void nua_subscribe_usage_refresh(nua_handle_t *nh,
429 					nua_dialog_state_t *ds,
430 					nua_dialog_usage_t *du,
431 					sip_time_t now)
432 {
433   nua_client_request_t *cr = du->du_cr;
434   struct event_usage *eu = nua_dialog_usage_private(du);
435 
436   assert(eu);
437 
438   if (eu->eu_final_wait) {
439     /* Did not receive NOTIFY for fetch */
440     sip_event_t const *o = du->du_event;
441     char const *id = o ? o->o_id : NULL;
442 
443     SU_DEBUG_3(("nua(%p): event %s%s%s fetch timeouts\n",
444 		(void *)nh, o ? o->o_type : "(empty)",
445 		id ? "; id=" : "", id ? id : ""));
446 
447     nua_stack_tevent(nh->nh_nua, nh,  NULL,
448 		     nua_i_notify, 408, "Fetch Timeouts without NOTIFY",
449 		     NUTAG_SUBSTATE(nua_substate_terminated),
450 		     SIPTAG_EVENT(du->du_event),
451 		     TAG_END());
452     nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
453 
454     return;
455   }
456 
457   if (cr) {
458     if (nua_client_resend_request(cr, 0) >= 0)
459       return;
460   }
461   else if (eu->eu_refer) {
462     /*
463      * XXX - If we have received a NOTIFY, we should try to terminate
464      * subscription
465      */
466   }
467 
468   if (!eu->eu_unsolicited)
469     nua_stack_tevent(nh->nh_nua, nh, NULL,
470 		     nua_i_notify, NUA_ERROR_AT(__FILE__, __LINE__),
471 		     NUTAG_SUBSTATE(nua_substate_terminated),
472 		     SIPTAG_EVENT(du->du_event),
473 		     TAG_END());
474 
475   nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
476 }
477 
478 /** Terminate subscription.
479  *
480  * @retval >0  shutdown done
481  * @retval 0   shutdown in progress
482  * @retval <0  try again later
483  */
nua_subscribe_usage_shutdown(nua_handle_t * nh,nua_dialog_state_t * ds,nua_dialog_usage_t * du)484 static int nua_subscribe_usage_shutdown(nua_handle_t *nh,
485 					nua_dialog_state_t *ds,
486 					nua_dialog_usage_t *du)
487 {
488   struct event_usage *eu = nua_dialog_usage_private(du);
489   nua_client_request_t *cr = du->du_cr;
490 
491   assert(eu); (void)eu;
492 
493   if (cr) {
494     if (nua_client_resend_request(cr, 1) >= 0)
495       return 0;
496   }
497 
498   nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
499   return 200;
500 }
501 
502 /* ======================================================================== */
503 /* NOTIFY server */
504 
505 /** @NUA_EVENT nua_i_notify
506  *
507  * Event for incoming NOTIFY request.
508  *
509  * @param status statuscode of response sent automatically by stack
510  * @param phrase a short textual description of @a status code
511  * @param nh     operation handle associated with the subscription
512  * @param hmagic application context associated with the handle
513  * @param sip    incoming NOTIFY request
514  * @param tags   NUTAG_SUBSTATE() indicating the subscription state
515  *
516  * @sa nua_subscribe(), nua_unsubscribe(), @RFC3265, #nua_i_subscribe
517  *
518  * @END_NUA_EVENT
519  */
520 
521 int nua_notify_server_init(nua_server_request_t *sr);
522 int nua_notify_server_preprocess(nua_server_request_t *sr);
523 int nua_notify_server_report(nua_server_request_t *, tagi_t const *);
524 
525 nua_server_methods_t const nua_notify_server_methods =
526   {
527     SIP_METHOD_NOTIFY,
528     nua_i_notify,		/* Event */
529     {
530       /* create_dialog: */ 1,	/* Do create dialog */
531       /* in_dialog: */ 0,	/* Not always in-dialog request */
532       /* target_refresh: */ 1,	/* Target refresh request  */
533       /* add_contact: */ 1,	/* Add Contact to response */
534     },
535     nua_notify_server_init,
536     nua_notify_server_preprocess,
537     nua_base_server_params,
538     nua_base_server_respond,
539     nua_notify_server_report,
540   };
541 
542 
nua_notify_server_init(nua_server_request_t * sr)543 int nua_notify_server_init(nua_server_request_t *sr)
544 {
545   if (!sr->sr_initial) {
546     nua_dialog_state_t *ds = sr->sr_owner->nh_ds;
547 
548     /* Check for forked subscription. */
549     if (ds->ds_remote_tag && ds->ds_remote_tag[0] &&
550 	su_strcasecmp(ds->ds_remote_tag,
551 		      sr->sr_request.sip->sip_from->a_tag)) {
552       sip_contact_t const *m = NULL;
553 
554       m = nua_stack_get_contact(sr->sr_owner->nh_nua->nua_registrations);
555 
556       if (m) {
557 	sip_warning_t w[1];
558 
559 	sip_warning_init(w)->w_code = 399;
560 	w->w_host = m->m_url->url_host;
561 	w->w_port = m->m_url->url_port;
562 	w->w_text = "Forking SUBSCRIBEs are not supported";
563 
564 	sip_add_dup(sr->sr_response.msg, NULL, (sip_header_t*)w);
565       }
566 
567       return SR_STATUS(sr, 481, "Subscription Does Not Exist");
568     }
569   }
570 
571   return 0;
572 }
573 
nua_notify_server_preprocess(nua_server_request_t * sr)574 int nua_notify_server_preprocess(nua_server_request_t *sr)
575 {
576   nua_dialog_state_t *ds = sr->sr_owner->nh_ds;
577   nua_dialog_usage_t *du;
578   struct event_usage *eu;
579   sip_t const *sip = sr->sr_request.sip;
580   sip_event_t *o = sip->sip_event;
581   enum nua_substate substate = nua_substate_terminated;
582   sip_subscription_state_t *subs = sip->sip_subscription_state;
583   char const *what = "", *reason = NULL;
584   int solicited = 1;
585 
586   du = nua_dialog_usage_get(ds, nua_subscribe_usage, o);
587 
588   if (du == NULL) {
589     if (!sip_is_allowed(NH_PGET(sr->sr_owner, appl_method), SIP_METHOD_NOTIFY))
590       return SR_STATUS(sr, 481, "Subscription Does Not Exist");
591 
592     solicited = 0;    /* Let application to handle unsolicited NOTIFY */
593     du = nua_dialog_usage_add(sr->sr_owner, ds, nua_subscribe_usage, o);
594     if (!du)
595       return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR);
596   }
597 
598   sr->sr_usage = du;
599   eu = nua_dialog_usage_private(du); assert(eu);
600   eu->eu_notified++;
601   if (!o || !o->o_id)
602     eu->eu_no_id = 1;
603 
604   if (subs == NULL) {
605     /* Compatibility */
606     unsigned long delta = eu->eu_delta;
607     if (sip->sip_expires)
608       delta = sip->sip_expires->ex_delta;
609 
610     if (delta == 0)
611       substate = nua_substate_terminated, what = "terminated";
612     else
613       substate = nua_substate_active, what = "active";
614   }
615   else if (su_casematch(subs->ss_substate, what = "terminated")) {
616     substate = nua_substate_terminated;
617     reason = subs->ss_reason;
618 
619     if (su_casematch(reason, "deactivated") ||
620 	su_casematch(reason, "probation"))
621       substate = nua_substate_embryonic;
622   }
623   else if (su_casematch(subs->ss_substate, what = "pending")) {
624     substate = nua_substate_pending;
625   }
626   else /* if (su_casematch(subs->ss_substate, what = "active")) */ {
627     /* Any extended state is considered as active */
628     what = subs->ss_substate;
629     substate = nua_substate_active;
630   }
631 
632   eu->eu_substate = substate;
633   if (!solicited)
634     eu->eu_unsolicited = 1;
635 
636   SU_DEBUG_5(("nua(%p): %s: %s (%s)\n",
637 	      (void *)sr->sr_owner, "nua_notify_server_preprocess",
638 	      what, reason ? reason : ""));
639 
640   if (solicited)
641     return SR_STATUS1(sr, SIP_200_OK);
642 
643   return 0;
644 }
645 
646 
nua_notify_server_report(nua_server_request_t * sr,tagi_t const * tags)647 int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags)
648 {
649   nua_handle_t *nh = sr->sr_owner;
650   nua_dialog_usage_t *du = sr->sr_usage;
651   struct event_usage *eu = nua_dialog_usage_private(du);
652   sip_t const *sip = sr->sr_request.sip;
653   enum nua_substate substate = nua_substate_terminated;
654   sip_time_t delta = SIP_TIME_MAX;
655   sip_event_t const *o = sip->sip_event;
656   int retry = -1;
657   int retval;
658 
659   if (eu) {
660     sip_subscription_state_t *subs = sip->sip_subscription_state;
661 
662     substate = eu->eu_substate;
663 
664     if (substate == nua_substate_active || substate == nua_substate_pending) {
665       if (subs && subs->ss_expires) {
666 	sip_time_t now = sip_now();
667 	sip_time_t delta0 = strtoul(subs->ss_expires, NULL, 10);
668 	if (now + delta0 <= eu->eu_expires)
669 	  delta = delta0;
670       }
671     }
672     else if (substate == nua_substate_embryonic) {
673       if (subs && subs->ss_reason) {
674 	if (su_casematch(subs->ss_reason, "deactivated")) {
675 	  retry = 0;		/* retry immediately */
676 	}
677 	else if (su_casematch(subs->ss_reason, "probation")) {
678 	  retry = 30;
679 	  if (subs->ss_retry_after)
680 	    retry = strtoul(subs->ss_retry_after, NULL, 10);
681 	  if (retry > 3600)
682 	    retry = 3600;
683 	}
684       }
685     }
686     else if (substate == nua_substate_terminated) {
687       sr->sr_terminating = 1;
688     }
689   }
690 
691   retval = nua_base_server_treport(sr, /* can destroy sr */
692 				   NUTAG_SUBSTATE(substate),
693 				   SIPTAG_EVENT(o),
694 				   TAG_NEXT(tags));
695 
696   if (retval != 1 || du == NULL)
697     return retval;
698 
699   if (eu->eu_unsolicited) {
700     /* Xyzzy */;
701   }
702   else if (retry >= 0) {		/* Try to subscribe again */
703     /* XXX - this needs through testing */
704     nua_dialog_remove(nh, nh->nh_ds, du); /* tear down */
705     nua_dialog_usage_set_refresh_range(du, retry, retry + 5);
706   }
707   else {
708     if (delta < SIP_TIME_MAX) {
709       nua_dialog_usage_set_refresh(du, delta);
710       eu->eu_expires = du->du_refquested + delta;
711     }
712   }
713 
714   return retval;
715 }
716 
717 
718 /* ======================================================================== */
719 /* REFER */
720 
721 /**@fn void nua_refer(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
722  *
723  * Transfer a call.
724  *
725  * Send a REFER request asking the recipient to transfer the call.
726  *
727  * The REFER request also establishes an implied subscription to the "refer"
728  * event. The "refer" event can have an "id" parameter, which has the value
729  * of CSeq number in the REFER request. After initiating the REFER request,
730  * the nua engine sends application a #nua_r_refer event with status 100 and
731  * tag NUTAG_REFER_EVENT() containing a matching event header with id
732  * parameter.
733  *
734  * Note that the @Event header in the locally generated #nua_r_refer event
735  * contains the @a id parameter. The @a id parameter contains the @CSeq
736  * number of the REFER request, and it may get incremented if the request is
737  * retried because it got challenged or redirected. In that case, the
738  * application gets a new #nua_r_refer event with status 100 and tag
739  * NUTAG_REFER_EVENT(). Also the recipient of the REFER request may or may
740  * not include the @a id parameter with the @Event header in the NOTIFY
741  * requests messages which it sends to the sender of the REFER request.
742  *
743  * Therefore the application is not able to modify the state of the implied
744  * subscription before receiving the first NOTIFY request.
745  *
746  * @param nh              Pointer to operation handle
747  * @param tag, value, ... List of tagged parameters
748  *
749  * @return
750  *    nothing
751  *
752  * @par Related Tags:
753  *    NUTAG_URL() \n
754  *    Tags of nua_set_hparams() \n
755  *    Header tags defined in <sofia-sip/sip_tag.h>
756  *
757  * @par Events:
758  *    #nua_r_refer \n
759  *    #nua_i_notify
760  *
761  * @sa #nua_r_refer, NUTAG_SUBSTATE(), NUTAG_REFER_EVENT(),#nua_i_refer,
762  * @RFC3515, @ReferTo, SIPTAG_REFER_TO(), SIPTAG_REFER_TO_STR(),
763  * @RFC3892, @ReferredBy, SIPTAG_REFERRED_BY(), SIPTAG_REFERRED_BY_STR(),
764  * @RFC3891, @Replaces, SIPTAG_REPLACES(), SIPTAG_REPLACES_STR(),
765  * @RFC4488, @ReferSub, SIPTAG_REFER_SUB(), SIPTAG_REFER_SUB_STR()
766  */
767 
768 /**@NUA_EVENT nua_r_refer
769  *
770  * @brief Response to outgoing REFER.
771  *
772  * @param status response status code
773  *               (if the request is retried, @a status is 100, the @a
774  *               sip->sip_status->st_status contain the real status code
775  *               from the response message, e.g., 302, 401, or 407)
776  * @param phrase a short textual description of @a status code
777  * @param nh     operation handle associated with the REFER request
778  * @param hmagic application context associated with the handle
779  * @param sip    response to REFER request or NULL upon an error
780  *               (status code is in @a status and
781  *                descriptive message in @a phrase parameters)
782  * @param tags    NUTAG_REFER_EVENT() \n
783  *                NUTAG_SUBSTATE()
784  *
785  * @sa nua_refer(), NUTAG_SUBSTATE(), #nua_i_refer,
786  * @RFC3515, @RFC4488, @ReferSub
787  *
788  * @END_NUA_EVENT
789  */
790 
791 static int nua_refer_client_init(nua_client_request_t *cr,
792 				 msg_t *, sip_t *,
793 				 tagi_t const *tags);
794 static int nua_refer_client_request(nua_client_request_t *cr,
795 				    msg_t *, sip_t *,
796 				    tagi_t const *tags);
797 static int nua_refer_client_response(nua_client_request_t *cr,
798 				     int status, char const *phrase,
799 				     sip_t const *sip);
800 
801 static nua_client_methods_t const nua_refer_client_methods = {
802   SIP_METHOD_REFER,		/* crm_method, crm_method_name */
803   0,				/* crm_extra */
804   {				/* crm_flags */
805     /* create_dialog */ 1,
806     /* in_dialog */ 1,
807     /* target refresh */ 1
808   },
809   NULL,				/* crm_template */
810   nua_refer_client_init,	/* crm_init */
811   nua_refer_client_request,	/* crm_send */
812   NULL,				/* crm_check_restart */
813   nua_refer_client_response,	/* crm_recv */
814   nua_refer_client_response,	/* crm_preliminary */
815   NULL,				/* crm_report */
816   NULL,				/* crm_complete */
817 };
818 
819 int
nua_stack_refer(nua_t * nua,nua_handle_t * nh,nua_event_t e,tagi_t const * tags)820 nua_stack_refer(nua_t *nua, nua_handle_t *nh, nua_event_t e,
821 		    tagi_t const *tags)
822 {
823   return nua_client_create(nh, e, &nua_refer_client_methods, tags);
824 }
825 
nua_refer_client_init(nua_client_request_t * cr,msg_t * msg,sip_t * sip,tagi_t const * tags)826 static int nua_refer_client_init(nua_client_request_t *cr,
827 				 msg_t *msg, sip_t *sip,
828 				 tagi_t const *tags)
829 {
830   nua_handle_t *nh = cr->cr_owner;
831 
832   if (sip->sip_referred_by == NULL) {
833     sip_from_t *a = sip->sip_from;
834     sip_referred_by_t by[1];
835 
836     sip_referred_by_init(by);
837 
838     if (a == NULL)
839       a = nh->nh_nua->nua_from;
840     by->b_display = a->a_display;
841     *by->b_url = *a->a_url;
842 
843     sip_add_dup(msg, sip, (sip_header_t *)by);
844   }
845 
846   if (sip->sip_event)
847     sip_header_remove(msg, sip, (sip_header_t *)sip->sip_event);
848 
849   return 0;
850 }
851 
nua_refer_client_request(nua_client_request_t * cr,msg_t * msg,sip_t * sip,tagi_t const * tags)852 static int nua_refer_client_request(nua_client_request_t *cr,
853 				    msg_t *msg, sip_t *sip,
854 				    tagi_t const *tags)
855 {
856   nua_handle_t *nh = cr->cr_owner;
857   nua_dialog_usage_t *du, *du0 = cr->cr_usage;
858   struct event_usage *eu;
859   sip_event_t *event;
860   int error;
861 
862   cr->cr_usage = NULL;
863 
864   event = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq);
865   if (!event)
866     return -1;
867 
868   if (du0 == NULL ||
869       du0->du_event == NULL ||
870       du0->du_event->o_id == NULL ||
871       strcmp(du0->du_event->o_id, event->o_id)) {
872     du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, event);
873     if (!du)
874       return -1;
875   }
876   else {
877     du = du0, du0 = NULL;
878   }
879 
880   if (du0)
881     nua_dialog_usage_remove(nh, nh->nh_ds, du0, NULL, NULL);
882 
883   eu = nua_dialog_usage_private(cr->cr_usage = du);
884   eu ->eu_refer = 1;
885 
886   error = nua_base_client_request(cr, msg, sip, tags);
887 
888   if (!error) {
889     /* Give application an Event header for matching NOTIFYs with REFER */
890     nua_stack_tevent(nh->nh_nua, nh, NULL,
891 		     (enum nua_event_e)cr->cr_event, SIP_100_TRYING,
892 		     NUTAG_REFER_EVENT(event),
893 		     SIPTAG_EVENT(event),
894 		     TAG_END());
895     su_free(nh->nh_home, event);
896   }
897 
898   return error;
899 }
900 
nua_refer_client_response(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip)901 static int nua_refer_client_response(nua_client_request_t *cr,
902 				     int status, char const *phrase,
903 				     sip_t const *sip)
904 {
905   nua_dialog_usage_t *du = cr->cr_usage;
906   enum nua_substate substate = nua_substate_terminated;
907 
908   if (du) {
909     struct event_usage *eu = nua_dialog_usage_private(du);
910 
911     if (status < 200) {
912       substate = eu->eu_substate;
913     }
914     else if (status < 300) {
915       sip_refer_sub_t const *rs = sip_refer_sub(sip);
916 
917       if (rs && su_casematch("false", rs->rs_value))
918 	cr->cr_terminated = 1;
919 
920       if (!cr->cr_terminated)
921 	substate = eu->eu_substate;
922     }
923   }
924 
925   return nua_base_client_tresponse(cr, status, phrase, sip,
926 				   NUTAG_SUBSTATE(substate),
927 				   SIPTAG_EVENT(du ? du->du_event : NULL),
928 				   TAG_END());
929 }
930