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_publish.c
26  * @brief PUBLISH and publications
27  *
28  * @sa @RFC3903
29  *
30  * @author Pekka Pessi <Pekka.Pessi@nokia.com>
31  *
32  * @date Created: Wed Mar  8 17:01:32 EET 2006 ppessi
33  */
34 
35 #include "config.h"
36 
37 #include <stddef.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <limits.h>
41 
42 #include <assert.h>
43 
44 #include <sofia-sip/su_string.h>
45 #include <sofia-sip/sip_protos.h>
46 #include <sofia-sip/sip_status.h>
47 
48 #include "nua_stack.h"
49 
50 /* ====================================================================== */
51 /* Publish usage */
52 
53 struct publish_usage {
54   sip_etag_t *pu_etag;
55   int pu_published;
56 };
57 
58 static char const *nua_publish_usage_name(nua_dialog_usage_t const *du);
59 static int nua_publish_usage_add(nua_handle_t *nh,
60 				  nua_dialog_state_t *ds,
61 				  nua_dialog_usage_t *du);
62 static void nua_publish_usage_remove(nua_handle_t *nh,
63 				     nua_dialog_state_t *ds,
64 				     nua_dialog_usage_t *du,
65 				     nua_client_request_t *cr,
66 				     nua_server_request_t *sr);
67 static void nua_publish_usage_refresh(nua_handle_t *nh,
68 				      nua_dialog_state_t *ds,
69 				      nua_dialog_usage_t *du,
70 				      sip_time_t now);
71 static int nua_publish_usage_shutdown(nua_handle_t *nh,
72 				      nua_dialog_state_t *ds,
73 				      nua_dialog_usage_t *du);
74 
75 static nua_usage_class const nua_publish_usage[1] = {
76   {
77     sizeof (struct publish_usage),
78     sizeof nua_publish_usage,
79     nua_publish_usage_add,
80     nua_publish_usage_remove,
81     nua_publish_usage_name,
82     nua_base_usage_update_params,
83     NULL,
84     nua_publish_usage_refresh,
85     nua_publish_usage_shutdown,
86   }};
87 
88 static
nua_publish_usage_name(nua_dialog_usage_t const * du)89 char const *nua_publish_usage_name(nua_dialog_usage_t const *du)
90 {
91   return "publish";
92 }
93 
94 static
nua_publish_usage_add(nua_handle_t * nh,nua_dialog_state_t * ds,nua_dialog_usage_t * du)95 int nua_publish_usage_add(nua_handle_t *nh,
96 			   nua_dialog_state_t *ds,
97 			   nua_dialog_usage_t *du)
98 {
99   if (ds->ds_has_publish)
100     return -1;			/* There can be only one */
101   ds->ds_has_publish = 1;
102   return 0;
103 }
104 
105 static
nua_publish_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)106 void nua_publish_usage_remove(nua_handle_t *nh,
107 			      nua_dialog_state_t *ds,
108 			      nua_dialog_usage_t *du,
109 			      nua_client_request_t *cr,
110 			      nua_server_request_t *sr
111 )
112 {
113   struct publish_usage *pu = NUA_DIALOG_USAGE_PRIVATE(du);
114 
115   su_free(nh->nh_home, pu->pu_etag);
116 
117   ds->ds_has_publish = 0;	/* There can be only one */
118 }
119 
120 /* ======================================================================== */
121 /* PUBLISH */
122 
123 /**@fn \
124  * void nua_publish(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
125  *
126  * Send PUBLISH request to publication server.
127  *
128  * Request status will be delivered to the application using #nua_r_publish
129  * event. When successful the publication will be updated periodically until
130  * nua_unpublish() is called or handle is destroyed. Note that the periodic
131  * updates and unpublish do not include the original message body nor the @b
132  * Content-Type header. Instead, the periodic update will include the
133  * @SIPIfMatch header, which was generated from the latest @SIPETag
134  * header received in response to @b PUBLISH request.
135  *
136  * The handle used for publication cannot be used for any other purposes.
137  *
138  * @param nh              Pointer to operation handle
139  * @param tag, value, ... List of tagged parameters
140  *
141  * @return
142  *    nothing
143  *
144  * @par Related Tags:
145  *    NUTAG_URL() \n
146  *    Tags of nua_set_hparams() \n
147  *    Header tags defined in <sofia-sip/sip_tag.h>
148  *
149  * @par Events:
150  *    #nua_r_publish
151  *
152  * @sa #nua_r_publish, @RFC3903, @SIPIfMatch,
153  * nua_unpublish(), #nua_r_unpublish, #nua_i_publish
154  */
155 
156 /** @NUA_EVENT nua_r_publish
157  *
158  * Response to an outgoing PUBLISH.
159  *
160  * The PUBLISH request may be sent explicitly by nua_publish() or implicitly
161  * by NUA state machine.
162  *
163  * @param status status code of PUBLISH request
164  *               (if the request is retried, @a status is 100, the @a
165  *               sip->sip_status->st_status contain the real status code
166  *               from the response message, e.g., 302, 401, or 407)
167  * @param phrase a short textual description of @a status code
168  * @param nh     operation handle associated with the publication
169  * @param hmagic application context associated with the handle
170  * @param sip    response to PUBLISH request or NULL upon an error
171  *               (status code is in @a status and
172  *                descriptive message in @a phrase parameters)
173  * @param tags   empty
174  *
175  * @sa nua_publish(), @RFC3903, @SIPETag, @Expires,
176  * nua_unpublish(), #nua_r_unpublish, #nua_i_publish
177  *
178  * @END_NUA_EVENT
179  */
180 
181 /**@fn \
182 void nua_unpublish(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...);
183  *
184  * Send un-PUBLISH request to publication server. Un-PUBLISH request is just
185  * a PUBLISH request with @Expires set to 0. It is possible to un-publish a
186  * publication not associated with the handle by providing correct ETag in
187  * SIPTAG_IF_MATCH() or SIPTAG_IF_MATCH_STR() tags.
188  *
189  * Response to the un-PUBLISH request will be delivered to the application
190  * using #nua_r_unpublish event.
191  *
192  * The handle used for publication cannot be used for any other purposes.
193  *
194  * @param nh              Pointer to operation handle
195  * @param tag, value, ... List of tagged parameters
196  *
197  * @return
198  *    nothing
199  *
200  * @par Related Tags:
201  *    NUTAG_URL() \n
202  *    SIPTAG_IF_MATCH(), SIPTAG_IF_MATCH_STR() \n
203  *    SIPTAG_EVENT(), SIPTAG_EVENT_STR() \n
204  *    Tags of nua_set_hparams() \n
205  *    Other header tags defined in <sofia-sip/sip_tag.h> except SIPTAG_EXPIRES() or SIPTAG_EXPIRES_STR()
206  *
207  * @par Events:
208  *    #nua_r_unpublish
209  *
210  * @sa #nua_r_unpublish, @RFC3903, @SIPIfMatch,
211  * #nua_i_publish, nua_publish(), #nua_r_publish
212  */
213 
214 /** @NUA_EVENT nua_r_unpublish
215  *
216  * Response to an outgoing un-PUBLISH.
217  *
218  * @param status response status code
219  *               (if the request is retried, @a status is 100, the @a
220  *               sip->sip_status->st_status contain the real status code
221  *               from the response message, e.g., 302, 401, or 407)
222  * @param phrase a short textual description of @a status code
223  * @param nh     operation handle associated with the publication
224  * @param hmagic application context associated with the handle
225  * @param sip    response to PUBLISH request or NULL upon an error
226  *               (status code is in @a status and
227  *                descriptive message in @a phrase parameters)
228  * @param tags   empty
229  *
230  * @sa nua_unpublish(), @RFC3903, @SIPETag, @Expires,
231  * nua_publish(), #nua_r_publish, #nua_i_publish
232  *
233  * @END_NUA_EVENT
234  */
235 
236 static int nua_publish_client_template(nua_client_request_t *cr,
237 				       msg_t **return_msg,
238 				       tagi_t const *tags);
239 static int nua_publish_client_init(nua_client_request_t *cr,
240 				   msg_t *, sip_t *,
241 				   tagi_t const *tags);
242 static int nua_publish_client_request(nua_client_request_t *cr,
243 				      msg_t *, sip_t *,
244 				      tagi_t const *tags);
245 static int nua_publish_client_check_restart(nua_client_request_t *cr,
246 					    int status, char const *phrase,
247 					    sip_t const *sip);
248 static int nua_publish_client_response(nua_client_request_t *cr,
249 				       int status, char const *phrase,
250 				       sip_t const *sip);
251 
252 static nua_client_methods_t const nua_publish_client_methods = {
253   SIP_METHOD_PUBLISH,		/* crm_method, crm_method_name */
254   0,				/* crm_extra */
255   {				/* crm_flags */
256     /* create_dialog */ 0,
257     /* in_dialog */ 0,
258     /* target refresh */ 0
259   },
260   nua_publish_client_template,	/* crm_template */
261   nua_publish_client_init,	/* crm_init */
262   nua_publish_client_request,	/* crm_send */
263   nua_publish_client_check_restart, /* crm_check_restart */
264   nua_publish_client_response,	/* crm_recv */
265   NULL,				/* crm_preliminary */
266   NULL,				/* crm_report */
267   NULL,				/* crm_complete */
268 };
269 
270 /**@internal Send PUBLISH. */
nua_stack_publish(nua_t * nua,nua_handle_t * nh,nua_event_t e,tagi_t const * tags)271 int nua_stack_publish(nua_t *nua,
272 		     nua_handle_t *nh,
273 		     nua_event_t e,
274 		     tagi_t const *tags)
275 {
276   return nua_client_create(nh, e, &nua_publish_client_methods, tags);
277 }
278 
nua_publish_client_template(nua_client_request_t * cr,msg_t ** return_msg,tagi_t const * tags)279 static int nua_publish_client_template(nua_client_request_t *cr,
280 				       msg_t **return_msg,
281 				       tagi_t const *tags)
282 {
283   nua_dialog_usage_t *du;
284 
285   if (cr->cr_event == nua_r_publish)
286     return 0;
287 
288   du = nua_dialog_usage_get(cr->cr_owner->nh_ds, nua_publish_usage, NULL);
289   if (du && du->du_cr) {
290     if (nua_client_set_target(cr, du->du_cr->cr_target) < 0)
291       return -1;
292     *return_msg = msg_copy(du->du_cr->cr_msg);
293     return 1;
294   }
295 
296   return 0;
297 }
298 
nua_publish_client_init(nua_client_request_t * cr,msg_t * msg,sip_t * sip,tagi_t const * tags)299 static int nua_publish_client_init(nua_client_request_t *cr,
300 				   msg_t *msg, sip_t *sip,
301 				   tagi_t const *tags)
302 {
303   nua_handle_t *nh = cr->cr_owner;
304   nua_dialog_usage_t *du;
305   struct publish_usage *pu;
306 
307   if (cr->cr_event == nua_r_publish) {
308     du = nua_dialog_usage_add(nh, nh->nh_ds, nua_publish_usage, NULL);
309     if (!du)
310       return -1;
311     pu = nua_dialog_usage_private(du);
312     pu->pu_published = 0;
313     if (sip->sip_if_match) {
314       pu->pu_etag = sip_etag_dup(nh->nh_home, sip->sip_if_match);
315       if (!pu->pu_etag)
316 	return -1;
317       sip_header_remove(msg, sip, (sip_header_t *)sip->sip_if_match);
318     }
319   }
320   else
321     du = nua_dialog_usage_get(nh->nh_ds, nua_publish_usage, NULL);
322 
323   cr->cr_usage = du;
324 
325   return 0;
326 }
327 
328 static
nua_publish_client_request(nua_client_request_t * cr,msg_t * msg,sip_t * sip,tagi_t const * tags)329 int nua_publish_client_request(nua_client_request_t *cr,
330 			       msg_t *msg, sip_t *sip,
331 			       tagi_t const *tags)
332 {
333   nua_dialog_usage_t *du = cr->cr_usage;
334   int un, done;
335   sip_etag_t const *etag = NULL;
336 
337   un = cr->cr_terminating ||
338     cr->cr_event != nua_r_publish ||
339     (du && du->du_shutdown) ||
340     (sip->sip_expires && sip->sip_expires->ex_delta == 0);
341   nua_client_set_terminating(cr, un);
342   done = un;
343 
344   if (du) {
345     struct publish_usage *pu = nua_dialog_usage_private(du);
346 
347     if (nua_client_bind(cr, du) < 0)
348       return -1;
349     if (pu->pu_published)
350       done = 1;
351     etag = pu->pu_etag;
352   }
353 
354   return nua_base_client_trequest(cr, msg, sip,
355 				  SIPTAG_IF_MATCH(etag),
356 				  TAG_IF(done, SIPTAG_PAYLOAD(NONE)),
357 				  TAG_IF(done, SIPTAG_CONTENT_TYPE(NONE)),
358 				  TAG_IF(un, SIPTAG_EXPIRES_STR("0")),
359 				  TAG_NEXT(tags));
360 }
361 
nua_publish_client_check_restart(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip)362 static int nua_publish_client_check_restart(nua_client_request_t *cr,
363 					    int status, char const *phrase,
364 					    sip_t const *sip)
365 {
366   char const *restarting = NULL;
367 
368   if (cr->cr_terminating || !cr->cr_usage)
369     ;
370   else if (status == 412)
371     restarting = phrase;
372   else if (200 <= status && status < 300 &&
373 	   sip->sip_expires && sip->sip_expires->ex_delta == 0)
374     restarting = "Immediate re-PUBLISH";
375 
376   if (restarting) {
377     struct publish_usage *pu = nua_dialog_usage_private(cr->cr_usage);
378 
379     if (pu) {
380       pu->pu_published = 0;
381       su_free(cr->cr_owner->nh_home, pu->pu_etag), pu->pu_etag = NULL;
382       if (nua_client_restart(cr, 100, restarting))
383 	return 0;
384     }
385   }
386 
387   return nua_base_client_check_restart(cr, status, phrase, sip);
388 }
389 
nua_publish_client_response(nua_client_request_t * cr,int status,char const * phrase,sip_t const * sip)390 static int nua_publish_client_response(nua_client_request_t *cr,
391 				       int status, char const *phrase,
392 				       sip_t const *sip)
393 {
394   nua_handle_t *nh = cr->cr_owner;
395   nua_dialog_usage_t *du = cr->cr_usage;
396 
397   if (!cr->cr_terminated && du && sip) {
398     struct publish_usage *pu = nua_dialog_usage_private(du);
399     sip_expires_t const *ex = sip->sip_expires;
400 
401     /* Reset state */
402     pu->pu_published = 0;
403     if (pu->pu_etag)
404       su_free(nh->nh_home, pu->pu_etag), pu->pu_etag = NULL;
405 
406     if (status < 300) {
407       pu->pu_published = 1;
408       pu->pu_etag = sip_etag_dup(nh->nh_home, sip->sip_etag);
409 
410       if (!ex || ex->ex_delta == 0 || !pu->pu_etag) {
411 	cr->cr_terminated = 1;
412 
413 	if (!ex || ex->ex_delta == 0)
414 	  SET_STATUS(900, "Received Invalid Expiration Time");
415 	else
416 	  SET_STATUS(900, _NUA_INTERNAL_ERROR_AT(__FILE__, __LINE__));
417       }
418       else
419 	nua_dialog_usage_set_refresh(du, ex->ex_delta);
420     }
421   }
422 
423   return nua_base_client_response(cr, status, phrase, sip, NULL);
424 }
425 
nua_publish_usage_refresh(nua_handle_t * nh,nua_dialog_state_t * ds,nua_dialog_usage_t * du,sip_time_t now)426 static void nua_publish_usage_refresh(nua_handle_t *nh,
427 				     nua_dialog_state_t *ds,
428 				     nua_dialog_usage_t *du,
429 				     sip_time_t now)
430 {
431   nua_client_request_t *cr = du->du_cr;
432 
433   if (cr) {
434     if (nua_client_resend_request(cr, 0) >= 0)
435       return;
436   }
437 
438   nua_stack_event(nh->nh_nua, nh, NULL,
439 		  nua_r_publish, NUA_ERROR_AT(__FILE__, __LINE__),
440 		  NULL);
441 
442   nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
443 }
444 
445 /** @interal Shut down PUBLISH usage.
446  *
447  * @retval >0  shutdown done
448  * @retval 0   shutdown in progress
449  * @retval <0  try again later
450  */
nua_publish_usage_shutdown(nua_handle_t * nh,nua_dialog_state_t * ds,nua_dialog_usage_t * du)451 static int nua_publish_usage_shutdown(nua_handle_t *nh,
452 				     nua_dialog_state_t *ds,
453 				     nua_dialog_usage_t *du)
454 {
455   nua_client_request_t *cr = du->du_cr;
456 
457   if (cr) {
458     if (nua_client_resend_request(cr, 1) >= 0)
459       return 0;
460   }
461 
462   /* XXX - report to user */
463   nua_dialog_usage_remove(nh, ds, du, NULL, NULL);
464   return 200;
465 }
466 
467 /* ---------------------------------------------------------------------- */
468 /* Server side */
469 
470 /** @NUA_EVENT nua_i_publish
471  *
472  * Incoming PUBLISH request.
473  *
474  * In order to receive #nua_i_publish events, the application must enable
475  * both the PUBLISH method with NUTAG_ALLOW() tag and the acceptable SIP
476  * events with nua_set_params() tag NUTAG_ALLOW_EVENTS().
477  *
478  * The nua_response() call responding to a PUBLISH request must have
479  * NUTAG_WITH() (or NUTAG_WITH_THIS()/NUTAG_WITH_SAVED()) tag. Note that
480  * a successful response to PUBLISH @b MUST include @Expires and @SIPETag
481  * headers.
482  *
483  * The PUBLISH request does not create a dialog. Currently the processing
484  * of incoming PUBLISH creates a new handle for each incoming request which
485  * is not assiciated with an existing dialog. If the handle @a nh is not
486  * bound, you should probably destroy it after responding to the PUBLISH
487  * request.
488  *
489  * @param status status code of response sent automatically by stack
490  * @param phrase a short textual description of @a status code
491  * @param nh     operation handle associated with the incoming request
492  * @param hmagic application context associated with the call
493  *               (usually NULL)
494  * @param sip    incoming PUBLISH request
495  * @param tags   empty
496  *
497  * @sa @RFC3903, nua_respond(),
498  * @Expires, @SIPETag, @SIPIfMatch, @Event,
499  * nua_subscribe(), #nua_i_subscribe,
500  * nua_notifier(), #nua_i_subscription,
501  *
502  * @since First used in @VERSION_1_12_4
503  *
504  * @END_NUA_EVENT
505  */
506 
507 int nua_publish_server_init(nua_server_request_t *sr);
508 
509 nua_server_methods_t const nua_publish_server_methods =
510   {
511     SIP_METHOD_PUBLISH,
512     nua_i_publish,		/* Event */
513     {
514       0,			/* Do not create dialog */
515       0,			/* Initial request */
516       0,			/* Not a target refresh request  */
517       1,			/* Add Contact */
518     },
519     nua_publish_server_init,
520     nua_base_server_preprocess,
521     nua_base_server_params,
522     nua_base_server_respond,
523     nua_base_server_report,
524   };
525 
nua_publish_server_init(nua_server_request_t * sr)526 int nua_publish_server_init(nua_server_request_t *sr)
527 {
528   sip_allow_events_t *allow_events = NH_PGET(sr->sr_owner, allow_events);
529   sip_event_t *o = sr->sr_request.sip->sip_event;
530   char const *event = o ? o->o_type : NULL;
531 
532   if (!allow_events)
533     return SR_STATUS1(sr, SIP_501_NOT_IMPLEMENTED);
534   else if (!event || !msg_header_find_param(allow_events->k_common, event))
535     return SR_STATUS1(sr, SIP_489_BAD_EVENT);
536 
537   return 0;
538 }
539