1 /*
2  * This file is part of the Sofia-SIP package
3  *
4  * Copyright (C) 2005 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 nea.c  Nokia Event Client API agent implementation.
26  *
27  * @author Pekka Pessi <Pekka.Pessi@nokia.com>
28  *
29  * @date Created: Wed Feb 14 18:32:58 2001 ppessi
30  */
31 
32 #include "config.h"
33 
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <assert.h>
40 
41 #include <sofia-sip/su_tagarg.h>
42 #include <sofia-sip/su_string.h>
43 
44 #include <sofia-sip/sip.h>
45 #include <sofia-sip/sip_header.h>
46 #include <sofia-sip/sip_util.h>
47 #include <sofia-sip/sip_status.h>
48 
49 #define SU_TIMER_ARG_T       struct nea_s
50 #define NTA_LEG_MAGIC_T      struct nea_s
51 #define NTA_OUTGOING_MAGIC_T struct nea_s
52 
53 #define NEA_TIMER_DELTA 2 /* time to resubscribe without expiration */
54 #define EXPIRES_DEFAULT       3600
55 
56 #include <sofia-sip/su_wait.h>
57 
58 #include "sofia-sip/nea.h"
59 
60 struct nea_s {
61   su_home_t         nea_home[1];
62   su_timer_t       *nea_timer;
63 
64   nta_agent_t      *nea_agent;
65   nta_leg_t        *nea_leg;
66   nta_outgoing_t   *nea_oreq;		/**< Outstanding request */
67   sip_to_t         *nea_to;		/**< The other end of subscription :) */
68   nea_notify_f      nea_callback;	/**< Notify callback  */
69   nea_magic_t      *nea_context;	/**< Application context */
70 
71   sip_contact_t    *nea_contact;	/**< */
72   sip_expires_t    *nea_expires;	/**< Proposed expiration time */
73 
74   nea_state_t       nea_state;	        /**< State of our subscription */
75   sip_time_t        nea_deadline;	/**< When our subscription expires */
76   tagi_t           *nea_args;
77 
78   unsigned          nea_dialog : 1;     /**< Dialog has been established */
79   unsigned          nea_notify_received : 1;
80   unsigned          nea_terminating : 1;
81   unsigned          nea_strict_3265 : 1;       /**< Strict mode */
82 };
83 
84 int details = 0;
85 
86 static int process_nea_request(nea_t *nea,
87 			       nta_leg_t *leg,
88 			       nta_incoming_t *ireq,
89 			       sip_t const *sip);
90 
91 static int handle_notify(nta_leg_magic_t *lmagic,
92 			 nta_leg_t *leg,
93 			 nta_incoming_t *ireq,
94 			 sip_t const *sip);
95 
96 static int response_to_subscribe(nea_t *nea,
97 				 nta_outgoing_t *req,
98 				 sip_t const *sip);
99 
100 static int response_to_unsubscribe(nea_t *nea,
101 				   nta_outgoing_t *req,
102 				   sip_t const *sip);
103 
104 static void nea_expires_renew(su_root_magic_t *magic,
105 		       su_timer_t *timer,
106 		       nea_t *nea);
107 
108 /* ---------------------------------------------------------- */
109 
110 /** Create a event watcher object.
111  *
112  */
nea_create(nta_agent_t * agent,su_root_t * root,nea_notify_f no_callback,nea_magic_t * context,tag_type_t tag,tag_value_t value,...)113 nea_t *nea_create(nta_agent_t *agent,
114 		  su_root_t *root,
115 		  nea_notify_f no_callback,
116 		  nea_magic_t *context,
117 		  tag_type_t tag, tag_value_t value, ...)
118 {
119   nea_t *nea = NULL;
120   ta_list ta;
121   int have_from, have_to, have_contact;
122   sip_expires_t const *expires = NULL;
123   char const *expires_str = NULL;
124   sip_method_t method = sip_method_subscribe;
125   char const *SUBSCRIBE = "SUBSCRIBE";
126   char const *method_name = SUBSCRIBE;
127 
128   ta_start(ta, tag, value);
129 
130   have_to =
131     tl_find(ta_args(ta), siptag_to) || tl_find(ta_args(ta), siptag_to_str);
132   have_from =
133     tl_find(ta_args(ta), siptag_from) || tl_find(ta_args(ta), siptag_from_str);
134   have_contact =
135     tl_find(ta_args(ta), siptag_contact) ||
136     tl_find(ta_args(ta), siptag_contact_str);
137 
138   if (have_to && (nea = su_home_new(sizeof(nea_t)))) {
139     su_home_t      *home = nea->nea_home;
140     sip_contact_t  *m = nta_agent_contact(agent);
141     sip_from_t     *from;
142     sip_to_t const *to;
143     int strict = 0;
144 
145     nea->nea_agent = agent;
146     nea->nea_callback = no_callback;
147     nea->nea_context = context;
148 
149     if (!have_from)
150       from = sip_from_create(home, (url_string_t*)m->m_url);
151     else
152       from = NULL;
153 
154     nea->nea_args = tl_tlist(home,
155 			     TAG_IF(!have_contact, SIPTAG_CONTACT(m)),
156 			     ta_tags(ta));
157 
158     /* Get and remove Expires header from tag list */
159     tl_gets(nea->nea_args,
160 	    SIPTAG_EXPIRES_REF(expires),
161 	    SIPTAG_EXPIRES_STR_REF(expires_str),
162 	    SIPTAG_TO_REF(to),
163 	    NEATAG_STRICT_3265_REF(strict),
164 	    NTATAG_METHOD_REF(method_name),
165 	    TAG_END());
166 
167     nea->nea_strict_3265 = strict;
168 
169     if (to)
170       nea->nea_to = sip_to_dup(home, to);
171 
172     if (expires)
173       nea->nea_expires = sip_expires_dup(home, expires);
174     else if (expires_str)
175       nea->nea_expires = sip_expires_make(home, expires_str);
176     else
177       nea->nea_expires = sip_expires_create(home, EXPIRES_DEFAULT);
178 
179     tl_tremove(nea->nea_args,
180 	       SIPTAG_EXPIRES(0),
181 	       SIPTAG_EXPIRES_STR(0),
182 	       TAG_END());
183 
184     if (method_name != SUBSCRIBE)
185       method = sip_method_code(method_name);
186 
187     if (method != sip_method_invalid)
188       /* Create the timer object */
189       nea->nea_timer = su_timer_create(su_root_task(root), 0L);
190 
191     if (nea->nea_timer) {
192       /* Create leg for NOTIFY requests */
193       nea->nea_leg = nta_leg_tcreate(nea->nea_agent,
194 				     process_nea_request, nea,
195 				     TAG_IF(!have_from, SIPTAG_FROM(from)),
196 				     TAG_NEXT(nea->nea_args));
197 
198       if (nea->nea_leg) {
199 	nta_leg_tag(nea->nea_leg, NULL);
200 	nea->nea_oreq = nta_outgoing_tcreate(nea->nea_leg,
201 					     response_to_subscribe, nea,
202 					     NULL,
203 					     method, method_name,
204 					     NULL,
205 					     SIPTAG_EXPIRES(nea->nea_expires),
206 					     TAG_NEXT(nea->nea_args));
207       }
208     }
209 
210     if (!nea->nea_leg ||
211 	!nea->nea_oreq ||
212 	!nea->nea_timer)
213       nea_destroy(nea), nea = NULL;
214   }
215 
216   ta_end(ta);
217   return nea;
218 }
219 
220 
nea_update(nea_t * nea,tag_type_t tag,tag_value_t value,...)221 int nea_update(nea_t *nea,
222 	       tag_type_t tag,
223 	       tag_value_t value,
224 	       ...)
225 {
226   ta_list ta;
227   sip_expires_t const *expires = NULL;
228   sip_payload_t const *pl = NULL;
229   sip_content_type_t const *ct = NULL;
230   char const *cts = NULL;
231 
232   /* char const *expires_str = NULL; */
233   su_home_t *home = nea->nea_home;
234 
235   /* XXX - hack, previous request still waiting for response */
236   if (!nea->nea_leg || nea->nea_oreq)
237     return -1;
238 
239   ta_start(ta, tag, value);
240 
241   tl_gets(ta_args(ta),
242 	  SIPTAG_CONTENT_TYPE_REF(ct),
243 	  SIPTAG_CONTENT_TYPE_STR_REF(cts),
244 	  SIPTAG_PAYLOAD_REF(pl),
245 	  SIPTAG_EXPIRES_REF(expires),
246 	  TAG_NULL());
247 
248   if (!pl || (!ct && !cts)) {
249     ta_end(ta);
250     return -1;
251   }
252 
253   tl_tremove(nea->nea_args,
254 	     SIPTAG_CONTENT_TYPE(0),
255 	     SIPTAG_CONTENT_TYPE_STR(0),
256 	     SIPTAG_PAYLOAD(0),
257 	     SIPTAG_PAYLOAD_STR(0),
258 	     TAG_END());
259 
260   su_free(home, nea->nea_expires);
261 
262   if (expires)
263     nea->nea_expires = sip_expires_dup(home, expires);
264   else
265     nea->nea_expires = sip_expires_create(home, EXPIRES_DEFAULT);
266 
267   /* nta_leg_tag(nea->nea_leg, NULL); */
268   nea->nea_oreq = nta_outgoing_tcreate(nea->nea_leg,
269 				       response_to_subscribe, nea,
270 				       NULL,
271 				       SIP_METHOD_SUBSCRIBE,
272 				       NULL,
273 				       SIPTAG_TO(nea->nea_to),
274 				       SIPTAG_PAYLOAD(pl),
275 				       TAG_IF(ct, SIPTAG_CONTENT_TYPE(ct)),
276 				       TAG_IF(cts, SIPTAG_CONTENT_TYPE_STR(cts)),
277 				       SIPTAG_EXPIRES(nea->nea_expires),
278 				       TAG_NEXT(nea->nea_args));
279 
280   ta_end(ta);
281 
282   if (!nea->nea_oreq)
283     return -1;
284 
285   return 0;
286 }
287 
288 
289 /** Unsubscribe the agent. */
nea_end(nea_t * nea)290 void nea_end(nea_t *nea)
291 {
292   if (nea == NULL)
293     return;
294 
295   nea->nea_terminating = 1;
296 
297   su_timer_destroy(nea->nea_timer), nea->nea_timer = NULL;
298 
299   if (nea->nea_leg && nea->nea_deadline) {
300     nea->nea_oreq =
301       nta_outgoing_tcreate(nea->nea_leg,
302 			   response_to_unsubscribe,
303 			   nea,
304 			   NULL,
305 			   SIP_METHOD_SUBSCRIBE,
306 			   NULL,
307 			   SIPTAG_EXPIRES_STR("0"),
308 			   TAG_NEXT(nea->nea_args));
309   }
310 }
311 
nea_destroy(nea_t * nea)312 void nea_destroy(nea_t *nea)
313 {
314   if (nea == NULL)
315     return;
316 
317   if (nea->nea_oreq)
318     nta_outgoing_destroy(nea->nea_oreq), nea->nea_oreq = NULL;
319 
320   if (nea->nea_leg)
321     nta_leg_destroy(nea->nea_leg), nea->nea_leg = NULL;
322 
323   if (nea->nea_timer) {
324     su_timer_reset(nea->nea_timer);
325     su_timer_destroy(nea->nea_timer), nea->nea_timer = NULL;
326   }
327 
328   su_free(NULL, nea);
329 }
330 
331 
332 /* Function called by NTA to handle incoming requests belonging to the leg */
process_nea_request(nea_t * nea,nta_leg_t * leg,nta_incoming_t * ireq,sip_t const * sip)333 int process_nea_request(nea_t *nea,
334 			nta_leg_t *leg,
335 			nta_incoming_t *ireq,
336 			sip_t const *sip)
337 {
338 
339   switch (sip->sip_request->rq_method) {
340   case sip_method_notify:
341     return handle_notify(nea, leg, ireq, sip);
342   case sip_method_ack:
343     return 400;
344   default:
345     nta_incoming_treply(ireq, SIP_405_METHOD_NOT_ALLOWED,
346 			SIPTAG_ALLOW_STR("NOTIFY"), TAG_END());
347     return 405;
348   }
349 }
350 
351 
352 /* Callback function to handle subscription requests */
response_to_subscribe(nea_t * nea,nta_outgoing_t * oreq,sip_t const * sip)353 int response_to_subscribe(nea_t *nea,
354 			  nta_outgoing_t *oreq,
355 			  sip_t const *sip)
356 {
357   int status = sip->sip_status->st_status;
358   int error = status >= 300;
359 
360   if (status >= 200 && oreq == nea->nea_oreq)
361     nea->nea_oreq = NULL;
362 
363   nea->nea_callback(nea, nea->nea_context, sip);
364 
365   if (status < 200)
366     return 0;
367 
368   nea->nea_oreq = NULL;
369 
370   if (status < 300) {
371     sip_time_t now = sip_now();
372     if (!nea->nea_notify_received) {
373       nea->nea_deadline = now +
374 	sip_contact_expires(NULL, sip->sip_expires, sip->sip_date,
375 			    EXPIRES_DEFAULT, now);
376       if (sip->sip_to->a_tag && !nea->nea_dialog) {
377 	nea->nea_dialog = 1;
378 	nta_leg_rtag(nea->nea_leg, sip->sip_to->a_tag);
379 	nta_leg_client_route(nea->nea_leg,
380 			     sip->sip_record_route, sip->sip_contact);
381       }
382     }
383   }
384   else {
385     nea->nea_deadline = 0;
386     nea->nea_state = nea_terminated;
387     if (status == 301 || status == 302 || status == 305) {
388       sip_contact_t *m;
389 
390       for (m = sip->sip_contact; m; m = m->m_next) {
391 	if (m->m_url->url_type == url_sip ||
392 	    m->m_url->url_type == url_sips)
393 	  break;
394       }
395 
396       if (m) {
397 	url_string_t const *proxy, *url;
398 	if (status == 305)
399 	  url = NULL, proxy = (url_string_t *)m->m_url;
400 	else
401 	  url = (url_string_t *)m->m_url, proxy = NULL;
402 
403 	nea->nea_oreq =
404 	  nta_outgoing_tcreate(nea->nea_leg,
405 			       response_to_subscribe,
406 			       nea,
407 			       proxy,
408 			       SIP_METHOD_SUBSCRIBE,
409 			       url,
410 			       SIPTAG_EXPIRES(nea->nea_expires),
411 			       TAG_NEXT(nea->nea_args));
412       }
413     } else if (status == 423 && sip->sip_min_expires) {
414       unsigned value = sip->sip_min_expires->me_delta;
415       su_free(nea->nea_home, nea->nea_expires);
416       nea->nea_expires = sip_expires_format(nea->nea_home, "%u", value);
417 
418       nea->nea_oreq =
419 	nta_outgoing_tcreate(nea->nea_leg,
420 			     response_to_subscribe,
421 			     nea,
422 			     NULL,
423 			     SIP_METHOD_SUBSCRIBE,
424 			     NULL,
425 			     SIPTAG_EXPIRES(nea->nea_expires),
426 			     TAG_NEXT(nea->nea_args));
427     }
428   }
429 
430   if (status >= 200)
431     nta_outgoing_destroy(oreq);
432 
433   if (nea->nea_oreq || !error) {
434     su_time_t now = su_now();
435     now.tv_sec = nea->nea_deadline;
436     su_timer_set_at(nea->nea_timer,
437 		    nea_expires_renew,
438 		    nea,
439 		    now);
440   }
441   else
442     nea->nea_callback(nea, nea->nea_context, NULL);
443 
444   return 0;
445 }
446 
447 
response_to_unsubscribe(nea_t * nea,nta_outgoing_t * orq,sip_t const * sip)448 int response_to_unsubscribe(nea_t *nea,
449 			    nta_outgoing_t *orq,
450 			    sip_t const *sip)
451 {
452   int status = sip->sip_status->st_status;
453 
454   nea->nea_callback(nea, nea->nea_context, sip);
455 
456   if (status >= 200)
457     nta_outgoing_destroy(orq), nea->nea_oreq = NULL;
458   if (status >= 300)
459     nea->nea_callback(nea, nea->nea_context, NULL);
460 
461   return 0;
462 }
463 
464 /** handle notifications */
handle_notify(nea_t * nea,nta_leg_t * leg,nta_incoming_t * irq,sip_t const * sip)465 int handle_notify(nea_t *nea,
466 		  nta_leg_t *leg,
467 		  nta_incoming_t *irq,
468 		  sip_t const *sip)
469 {
470   sip_subscription_state_t *ss = sip->sip_subscription_state;
471   sip_subscription_state_t ss0[1];
472   char expires[32];
473 
474   if (nea->nea_strict_3265) {
475     char const *phrase = NULL;
476 
477     if (ss == NULL)
478       phrase = "NOTIFY Has No Subscription-State Header";
479     else if (sip->sip_event == NULL)
480       phrase = "Event Header Missing";
481 
482     if (phrase) {
483       nta_incoming_treply(irq, 400, phrase, TAG_END());
484       nta_incoming_destroy(irq);
485       nta_leg_destroy(nea->nea_leg), nea->nea_leg = NULL;
486       nea->nea_state = nea_terminated;
487       nea->nea_callback(nea, nea->nea_context, NULL);
488       return 0;
489     }
490   }
491 
492   if (ss == NULL) {
493     /* Do some compatibility stuff here */
494     unsigned long delta = 3600;
495 
496     sip_subscription_state_init(ss = ss0);
497 
498     if (sip->sip_expires)
499       delta = sip->sip_expires->ex_delta;
500 
501     if (delta == 0)
502       ss->ss_substate = "terminated";
503     else
504       ss->ss_substate = "active";
505 
506     if (delta > 0) {
507       snprintf(expires, sizeof expires, "%lu", delta);
508       ss->ss_expires = expires;
509     }
510   }
511 
512   if (!nea->nea_dialog) {
513     nea->nea_dialog = 1;
514     nta_leg_rtag(nea->nea_leg, sip->sip_from->a_tag);
515     nta_leg_server_route(nea->nea_leg,
516 			 sip->sip_record_route, sip->sip_contact);
517   }
518 
519   nea->nea_notify_received = 1;
520   nea->nea_callback(nea, nea->nea_context, sip);
521 
522   if (su_casematch(ss->ss_substate, "terminated")) {
523     nta_leg_destroy(nea->nea_leg), nea->nea_leg = NULL;
524     nea->nea_state = nea_terminated;
525 
526     if (su_casematch(ss->ss_reason, "deactivated")) {
527       nea->nea_state = nea_embryonic;
528       nea->nea_deadline = sip_now();
529     } else if (su_casematch(ss->ss_reason, "probation")) {
530       sip_time_t retry = sip_now() + NEA_TIMER_DELTA;
531 
532       if (ss->ss_retry_after)
533 	retry += strtoul(ss->ss_retry_after, NULL, 10);
534       else
535 	retry += NEA_TIMER_DELTA;
536 
537       nea->nea_state = nea_embryonic;
538       nea->nea_deadline = retry;
539     } else {
540       nea->nea_deadline = 0;
541       nea->nea_callback(nea, nea->nea_context, NULL);
542       return 200;
543     }
544   }
545   else if (su_casematch(ss->ss_substate, "pending"))
546     nea->nea_state = nea_pending;
547   else if (su_casematch(ss->ss_substate, "active"))
548     nea->nea_state = nea_active;
549   else
550     nea->nea_state = nea_extended;
551 
552   if (nea->nea_state != nea_embryonic && ss->ss_expires) {
553     unsigned retry = strtoul(ss->ss_expires, NULL, 10);
554     if (retry > 60) retry -= 30; else retry /= 2;
555     nea->nea_deadline = sip_now() + retry;
556   }
557 
558   {
559     su_time_t now = su_now();
560     now.tv_sec = nea->nea_deadline;
561     su_timer_set_at(nea->nea_timer,
562 		    nea_expires_renew,
563 		    nea,
564 		    now);
565   }
566 
567   return 200;
568 }
569 
nea_expires_renew(su_root_magic_t * magic,su_timer_t * timer,nea_t * nea)570 void nea_expires_renew(su_root_magic_t *magic,
571 		       su_timer_t *timer,
572 		       nea_t *nea)
573 {
574   sip_time_t now = sip_now();
575 
576   /* re-subscribe if expires soon */
577   if (nea->nea_state == nea_terminated ||
578       nea->nea_deadline == 0 ||
579       nea->nea_deadline > now + NEA_TIMER_DELTA)
580     return;
581 
582   if (!nea->nea_notify_received)	/* Hmph. */
583     return;
584 
585   nea->nea_notify_received = 0;
586 
587   nea->nea_oreq =
588     nta_outgoing_tcreate(nea->nea_leg,
589 			 response_to_subscribe,
590 			 nea,
591 			 NULL,
592 			 SIP_METHOD_SUBSCRIBE,
593 			 NULL,
594 			 SIPTAG_EXPIRES(nea->nea_expires),
595 			 TAG_NEXT(nea->nea_args));
596 
597   return;
598 }
599