Asterisk - The Open Source Telephony Project  GIT-master-93d0901
res_pjsip_pubsub.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 /*!
19  * \brief Opaque structure representing an RFC 3265 SIP subscription
20  */
21 
22 /*** MODULEINFO
23  <depend>pjproject</depend>
24  <depend>res_pjsip</depend>
25  <support_level>core</support_level>
26  ***/
27 
28 #include "asterisk.h"
29 
30 #include <pjsip.h>
31 #include <pjsip_simple.h>
32 #include <pjlib.h>
33 
34 #include "asterisk/mwi.h"
36 #include "asterisk/module.h"
37 #include "asterisk/linkedlists.h"
38 #include "asterisk/astobj2.h"
39 #include "asterisk/datastore.h"
40 #include "asterisk/uuid.h"
41 #include "asterisk/taskprocessor.h"
42 #include "asterisk/sched.h"
43 #include "asterisk/res_pjsip.h"
44 #include "asterisk/callerid.h"
45 #include "asterisk/manager.h"
46 #include "asterisk/cli.h"
47 #include "asterisk/test.h"
50 
51 /*** DOCUMENTATION
52  <manager name="PJSIPShowSubscriptionsInbound" language="en_US">
53  <synopsis>
54  Lists subscriptions.
55  </synopsis>
56  <syntax />
57  <description>
58  <para>
59  Provides a listing of all inbound subscriptions. An event <literal>InboundSubscriptionDetail</literal>
60  is issued for each subscription object. Once all detail events are completed an
61  <literal>InboundSubscriptionDetailComplete</literal> event is issued.
62  </para>
63  </description>
64  </manager>
65  <manager name="PJSIPShowSubscriptionsOutbound" language="en_US">
66  <synopsis>
67  Lists subscriptions.
68  </synopsis>
69  <syntax />
70  <description>
71  <para>
72  Provides a listing of all outbound subscriptions. An event <literal>OutboundSubscriptionDetail</literal>
73  is issued for each subscription object. Once all detail events are completed an
74  <literal>OutboundSubscriptionDetailComplete</literal> event is issued.
75  </para>
76  </description>
77  </manager>
78  <manager name="PJSIPShowResourceLists" language="en_US">
79  <synopsis>
80  Displays settings for configured resource lists.
81  </synopsis>
82  <syntax />
83  <description>
84  <para>
85  Provides a listing of all resource lists. An event <literal>ResourceListDetail</literal>
86  is issued for each resource list object. Once all detail events are completed a
87  <literal>ResourceListDetailComplete</literal> event is issued.
88  </para>
89  </description>
90  </manager>
91 
92  <configInfo name="res_pjsip_pubsub" language="en_US">
93  <synopsis>Module that implements publish and subscribe support.</synopsis>
94  <configFile name="pjsip.conf">
95  <configObject name="subscription_persistence">
96  <synopsis>Persists SIP subscriptions so they survive restarts.</synopsis>
97  <configOption name="packet">
98  <synopsis>Entire SIP SUBSCRIBE packet that created the subscription</synopsis>
99  </configOption>
100  <configOption name="src_name">
101  <synopsis>The source address of the subscription</synopsis>
102  </configOption>
103  <configOption name="src_port">
104  <synopsis>The source port of the subscription</synopsis>
105  </configOption>
106  <configOption name="transport_key">
107  <synopsis>The type of transport the subscription was received on</synopsis>
108  </configOption>
109  <configOption name="local_name">
110  <synopsis>The local address the subscription was received on</synopsis>
111  </configOption>
112  <configOption name="local_port">
113  <synopsis>The local port the subscription was received on</synopsis>
114  </configOption>
115  <configOption name="cseq">
116  <synopsis>The sequence number of the next NOTIFY to be sent</synopsis>
117  </configOption>
118  <configOption name="tag">
119  <synopsis>The local tag of the dialog for the subscription</synopsis>
120  </configOption>
121  <configOption name="endpoint">
122  <synopsis>The name of the endpoint that subscribed</synopsis>
123  </configOption>
124  <configOption name="expires">
125  <synopsis>The time at which the subscription expires</synopsis>
126  </configOption>
127  <configOption name="contact_uri">
128  <synopsis>The Contact URI of the dialog for the subscription</synopsis>
129  </configOption>
130  <configOption name="prune_on_boot">
131  <synopsis>If set, indicates that the contact used a reliable transport
132  and therefore the subscription must be deleted after an asterisk restart.
133  </synopsis>
134  </configOption>
135  <configOption name="generator_data">
136  <synopsis>If set, contains persistence data for all generators of content
137  for the subscription.
138  </synopsis>
139  </configOption>
140  </configObject>
141  <configObject name="resource_list">
142  <synopsis>Resource list configuration parameters.</synopsis>
143  <description>
144  <para>This configuration object allows for RFC 4662 resource list subscriptions
145  to be specified. This can be useful to decrease the amount of subscription traffic
146  that a server has to process.</para>
147  <note>
148  <para>Current limitations limit the size of SIP NOTIFY requests that Asterisk sends
149  to double that of the PJSIP maximum packet length. If your resource list notifications
150  are larger than this maximum, you will need to make adjustments.</para>
151  </note>
152  </description>
153  <configOption name="type">
154  <synopsis>Must be of type 'resource_list'</synopsis>
155  </configOption>
156  <configOption name="event">
157  <synopsis>The SIP event package that the list resource belong to.</synopsis>
158  <description><para>
159  The SIP event package describes the types of resources that Asterisk reports
160  the state of.
161  </para>
162  <enumlist>
163  <enum name="presence"><para>
164  Device state and presence reporting.
165  </para></enum>
166  <enum name="dialog"><para>
167  This is identical to <replaceable>presence</replaceable>.
168  </para></enum>
169  <enum name="message-summary"><para>
170  Message-waiting indication (MWI) reporting.
171  </para></enum>
172  </enumlist>
173  </description>
174  </configOption>
175  <configOption name="list_item">
176  <synopsis>The name of a resource to report state on</synopsis>
177  <description>
178  <para>In general Asterisk looks up list items in the following way:</para>
179  <para>1. Check if the list item refers to another configured resource list.</para>
180  <para>2. Pass the name of the resource off to event-package-specific handlers
181  to find the specified resource.</para>
182  <para>The second part means that the way the list item is specified depends
183  on what type of list this is. For instance, if you have the <replaceable>event</replaceable>
184  set to <literal>presence</literal>, then list items should be in the form of
185  dialplan_extension@dialplan_context. For <literal>message-summary</literal> mailbox
186  names should be listed.</para>
187  </description>
188  </configOption>
189  <configOption name="full_state" default="no">
190  <synopsis>Indicates if the entire list's state should be sent out.</synopsis>
191  <description>
192  <para>If this option is enabled, and a resource changes state, then Asterisk will construct
193  a notification that contains the state of all resources in the list. If the option is
194  disabled, Asterisk will construct a notification that only contains the states of
195  resources that have changed.</para>
196  <note>
197  <para>Even with this option disabled, there are certain situations where Asterisk is forced
198  to send a notification with the states of all resources in the list. When a subscriber
199  renews or terminates its subscription to the list, Asterisk MUST send a full state
200  notification.</para>
201  </note>
202  </description>
203  </configOption>
204  <configOption name="notification_batch_interval" default="0">
205  <synopsis>Time Asterisk should wait, in milliseconds, before sending notifications.</synopsis>
206  <description>
207  <para>When a resource's state changes, it may be desired to wait a certain amount before Asterisk
208  sends a notification to subscribers. This allows for other state changes to accumulate, so that
209  Asterisk can communicate multiple state changes in a single notification instead of rapidly sending
210  many notifications.</para>
211  </description>
212  </configOption>
213  </configObject>
214  <configObject name="inbound-publication">
215  <synopsis>The configuration for inbound publications</synopsis>
216  <configOption name="endpoint" default="">
217  <synopsis>Optional name of an endpoint that is only allowed to publish to this resource</synopsis>
218  </configOption>
219  <configOption name="type">
220  <synopsis>Must be of type 'inbound-publication'.</synopsis>
221  </configOption>
222  </configObject>
223  </configFile>
224  </configInfo>
225  ***/
226 
227 static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata);
228 
229 static struct pjsip_module pubsub_module = {
230  .name = { "PubSub Module", 13 },
231  .priority = PJSIP_MOD_PRIORITY_APPLICATION,
232  .on_rx_request = pubsub_on_rx_request,
233 };
234 
235 #define MOD_DATA_PERSISTENCE "sub_persistence"
236 #define MOD_DATA_MSG "sub_msg"
237 
238 static const pj_str_t str_event_name = { "Event", 5 };
239 
240 /*! \brief Scheduler used for automatically expiring publications */
241 static struct ast_sched_context *sched;
242 
243 /*! \brief Number of buckets for publications (on a per handler) */
244 #define PUBLICATIONS_BUCKETS 37
245 
246 /*! \brief Default expiration time for PUBLISH if one is not specified */
247 #define DEFAULT_PUBLISH_EXPIRES 3600
248 
249 /*! \brief Number of buckets for subscription datastore */
250 #define DATASTORE_BUCKETS 53
251 
252 /*! \brief Default expiration for subscriptions */
253 #define DEFAULT_EXPIRES 3600
254 
255 /*! \brief Defined method for PUBLISH */
256 const pjsip_method pjsip_publish_method =
257 {
258  PJSIP_OTHER_METHOD,
259  { "PUBLISH", 7 }
260 };
261 
262 /*!
263  * \brief The types of PUBLISH messages defined in RFC 3903
264  */
266  /*!
267  * \brief Unknown
268  *
269  * \details
270  * This actually is not defined in RFC 3903. We use this as a constant
271  * to indicate that an incoming PUBLISH does not fit into any of the
272  * other categories and is thus invalid.
273  */
275 
276  /*!
277  * \brief Initial
278  *
279  * \details
280  * The first PUBLISH sent. This will contain a non-zero Expires header
281  * as well as a body that indicates the current state of the endpoint
282  * that has sent the message. The initial PUBLISH is the only type
283  * of PUBLISH to not contain a Sip-If-Match header in it.
284  */
286 
287  /*!
288  * \brief Refresh
289  *
290  * \details
291  * Used to keep a published state from expiring. This will contain a
292  * non-zero Expires header but no body since its purpose is not to
293  * update state.
294  */
296 
297  /*!
298  * \brief Modify
299  *
300  * \details
301  * Used to change state from its previous value. This will contain
302  * a body updating the published state. May or may not contain an
303  * Expires header.
304  */
306 
307  /*!
308  * \brief Remove
309  *
310  * \details
311  * Used to remove published state from an ESC. This will contain
312  * an Expires header set to 0 and likely no body.
313  */
315 };
316 
317 /*!
318  * \brief A vector of strings commonly used throughout this module
319  */
320 AST_VECTOR(resources, const char *);
321 
322 /*!
323  * \brief Resource list configuration item
324  */
326  SORCERY_OBJECT(details);
327  /*! SIP event package the list uses. */
328  char event[32];
329  /*! Strings representing resources in the list. */
330  struct resources items;
331  /*! Indicates if Asterisk sends full or partial state on notifications. */
332  unsigned int full_state;
333  /*! Time, in milliseconds Asterisk waits before sending a batched notification.*/
335 };
336 
337 /*!
338  * Used to create new entity IDs by ESCs.
339  */
340 static int esc_etag_counter;
341 
342 /*!
343  * \brief Structure representing a SIP publication
344  */
346  /*! Publication datastores set up by handlers */
348  /*! \brief Entity tag for the publication */
350  /*! \brief Handler for this publication */
352  /*! \brief The endpoint with which the subscription is communicating */
354  /*! \brief Expiration time of the publication */
355  unsigned int expires;
356  /*! \brief Scheduled item for expiration of publication */
357  int sched_id;
358  /*! \brief The resource the publication is to */
359  char *resource;
360  /*! \brief The name of the event type configuration */
362  /*! \brief Data containing the above */
363  char data[0];
364 };
365 
366 
367 /*!
368  * \brief Structure used for persisting an inbound subscription
369  */
371  /*! Sorcery object details */
372  SORCERY_OBJECT(details);
373  /*! The name of the endpoint involved in the subscription */
374  char *endpoint;
375  /*! SIP message that creates the subscription */
376  char packet[PJSIP_MAX_PKT_LEN];
377  /*! Source address of the message */
378  char src_name[PJ_INET6_ADDRSTRLEN];
379  /*! Source port of the message */
380  int src_port;
381  /*! Local transport key type */
382  char transport_key[32];
383  /*! Local transport address */
384  char local_name[PJ_INET6_ADDRSTRLEN];
385  /*! Local transport port */
387  /*! Next CSeq to use for message */
388  unsigned int cseq;
389  /*! Local tag of the dialog */
390  char *tag;
391  /*! When this subscription expires */
392  struct timeval expires;
393  /*! Contact URI */
394  char contact_uri[PJSIP_MAX_URL_SIZE];
395  /*! Prune subscription on restart */
397  /*! Body generator specific persistence data */
399 };
400 
401 /*!
402  * \brief The state of the subscription tree
403  */
405  /*! Normal operation */
407  /*! A terminate has been requested by Asterisk, the client, or pjproject */
409  /*! The terminate is in progress */
411  /*! The terminate process has finished and the subscription tree is no longer valid */
413 };
414 
415 static char *sub_tree_state_description[] = {
416  "Normal",
417  "TerminatePending",
418  "TerminateInProgress",
419  "Terminated"
420 };
421 
422 /*!
423  * \brief A tree of SIP subscriptions
424  *
425  * Because of the ability to subscribe to resource lists, a SIP
426  * subscription can result in a tree of subscriptions being created.
427  * This structure represents the information relevant to the subscription
428  * as a whole, to include the underlying PJSIP structure for the
429  * subscription.
430  */
432  /*! The endpoint with which the subscription is communicating */
434  /*! Serializer on which to place operations for this subscription */
436  /*! The role for this subscription */
438  /*! Persistence information */
440  /*! The underlying PJSIP event subscription structure */
441  pjsip_evsub *evsub;
442  /*! The underlying PJSIP dialog */
443  pjsip_dialog *dlg;
444  /*! Interval to use for batching notifications */
446  /*! Scheduler ID for batched notification */
448  /*! Indicator if scheduled batched notification should be sent */
449  unsigned int send_scheduled_notify;
450  /*! The root of the subscription tree */
452  /*! Is this subscription to a list? */
453  int is_list;
454  /*! Next item in the list */
456  /*! Subscription tree state */
458  /*! On asterisk restart, this is the task data used
459  * to restart the expiration timer if pjproject isn't
460  * capable of restarting the timer.
461  */
463  /*! The transport the subscription was received on.
464  * Only used for reliable transports.
465  */
466  pjsip_transport *transport;
467 };
468 
469 /*!
470  * \brief Structure representing a "virtual" SIP subscription.
471  *
472  * This structure serves a dual purpose. Structurally, it is
473  * the constructed tree of subscriptions based on the resources
474  * being subscribed to. API-wise, this serves as the handle that
475  * subscription handlers use in order to interact with the pubsub API.
476  */
478  /*! Subscription datastores set up by handlers */
480  /*! The handler for this subscription */
482  /*! Pointer to the base of the tree */
484  /*! Body generator for NOTIFYs */
486  /*! Vector of child subscriptions */
488  /*! Saved NOTIFY body text for this subscription */
490  /*! Indicator that the body text has changed since the last notification */
492  /*! The current state of the subscription */
493  pjsip_evsub_state subscription_state;
494  /*! For lists, the current version to place in the RLMI body */
495  unsigned int version;
496  /*! For lists, indicates if full state should always be communicated. */
497  unsigned int full_state;
498  /*! URI associated with the subscription */
499  pjsip_sip_uri *uri;
500  /*! Data to be persisted with the subscription */
502  /*! Name of resource being subscribed to */
503  char resource[0];
504 };
505 
506 /*!
507  * \brief Structure representing a publication resource
508  */
510  /*! \brief Sorcery object details */
511  SORCERY_OBJECT(details);
512  /*! \brief Optional name of an endpoint that is only allowed to publish to this resource */
513  char *endpoint;
514  /*! \brief Mapping for event types to configuration */
516 };
517 
518 static const char *sip_subscription_roles_map[] = {
519  [AST_SIP_SUBSCRIBER] = "Subscriber",
520  [AST_SIP_NOTIFIER] = "Notifier"
521 };
522 
524  /*! Called from send request */
526  /*! Subscription created from initial client request */
528  /*! Subscription recreated by asterisk on startup */
530  /*! Subscription created from client refresh */
532 };
533 
535 
538 
539 static pjsip_media_type rlmi_media_type;
540 
541 static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *event);
542 static void pubsub_on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata,
543  int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
544 static void pubsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code,
545  pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
546 static void pubsub_on_client_refresh(pjsip_evsub *sub);
547 static void pubsub_on_server_timeout(pjsip_evsub *sub);
548 
549 static pjsip_evsub_user pubsub_cb = {
550  .on_evsub_state = pubsub_on_evsub_state,
551  .on_rx_refresh = pubsub_on_rx_refresh,
552  .on_rx_notify = pubsub_on_rx_notify,
553  .on_client_refresh = pubsub_on_client_refresh,
554  .on_server_timeout = pubsub_on_server_timeout,
555 };
556 
557 /*! \brief Destructor for publication resource */
558 static void publication_resource_destroy(void *obj)
559 {
560  struct ast_sip_publication_resource *resource = obj;
561 
562  ast_free(resource->endpoint);
563  ast_variables_destroy(resource->events);
564 }
565 
566 /*! \brief Allocator for publication resource */
567 static void *publication_resource_alloc(const char *name)
568 {
570 }
571 
573 {
574  struct sip_subscription_tree *sub_tree = data;
575 
576  if (!sub_tree->evsub) {
577  /* Something else already terminated the subscription. */
578  ao2_ref(sub_tree, -1);
579  return 0;
580  }
581 
582  ast_debug(3, "Transport destroyed. Removing subscription '%s->%s' prune on boot: %d\n",
583  sub_tree->persistence->endpoint, sub_tree->root->resource,
584  sub_tree->persistence->prune_on_boot);
585 
587  pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
588 
589  ao2_ref(sub_tree, -1);
590  return 0;
591 }
592 
593 /*!
594  * \internal
595  * \brief The reliable transport we used as a subscription contact has shutdown.
596  *
597  * \param data What subscription needs to be terminated.
598  *
599  * \note Normally executed by the pjsip monitor thread.
600  */
601 static void sub_tree_transport_cb(void *data)
602 {
603  struct sip_subscription_tree *sub_tree = data;
604 
605  /*
606  * Push off the subscription termination to the serializer to
607  * avoid deadlock. Another thread could be trying to send a
608  * message on the subscription that can deadlock with this
609  * thread.
610  */
611  ao2_ref(sub_tree, +1);
613  sub_tree)) {
614  ao2_ref(sub_tree, -1);
615  }
616 }
617 
618 /*! \brief Destructor for subscription persistence */
619 static void subscription_persistence_destroy(void *obj)
620 {
621  struct subscription_persistence *persistence = obj;
622 
623  ast_free(persistence->endpoint);
624  ast_free(persistence->tag);
625  ast_json_unref(persistence->generator_data);
626 }
627 
628 /*! \brief Allocator for subscription persistence */
629 static void *subscription_persistence_alloc(const char *name)
630 {
632 }
633 
634 /*! \brief Function which creates initial persistence information of a subscription in sorcery */
636 {
637  char tag[PJ_GUID_STRING_LENGTH + 1];
638 
639  /* The id of this persistence object doesn't matter as we keep it on the subscription and don't need to
640  * look it up by id at all.
641  */
643  "subscription_persistence", NULL);
644 
645  pjsip_dialog *dlg = sub_tree->dlg;
646 
647  if (!persistence) {
648  return NULL;
649  }
650 
651  persistence->endpoint = ast_strdup(ast_sorcery_object_get_id(sub_tree->endpoint));
652  ast_copy_pj_str(tag, &dlg->local.info->tag, sizeof(tag));
653  persistence->tag = ast_strdup(tag);
654 
655  ast_sorcery_create(ast_sip_get_sorcery(), persistence);
656  return persistence;
657 }
658 
659 /*! \brief Function which updates persistence information of a subscription in sorcery */
661  pjsip_rx_data *rdata, enum sip_persistence_update_type type)
662 {
663  pjsip_dialog *dlg;
664 
665  if (!sub_tree->persistence) {
666  return;
667  }
668 
669  ast_debug(3, "Updating persistence for '%s->%s' prune on boot: %s\n",
670  sub_tree->persistence->endpoint, sub_tree->root->resource,
671  sub_tree->persistence->prune_on_boot ? "yes" : "no");
672 
673  dlg = sub_tree->dlg;
674  sub_tree->persistence->cseq = dlg->local.cseq;
675 
676  if (rdata) {
677  unsigned int expires;
678  pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
679  pjsip_contact_hdr *contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
680 
681  expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
683 
684  if (contact_hdr) {
685  if (contact_hdr) {
687  sub_tree->persistence->prune_on_boot =
689  (pjsip_sip_uri *)pjsip_uri_get_uri(contact_hdr->uri),
690  sub_tree->endpoint, rdata);
691 
692  if (sub_tree->persistence->prune_on_boot) {
693  ast_debug(3, "adding transport monitor on %s for '%s->%s' prune on boot: %d\n",
694  rdata->tp_info.transport->obj_name,
695  sub_tree->persistence->endpoint, sub_tree->root->resource,
696  sub_tree->persistence->prune_on_boot);
697  sub_tree->transport = rdata->tp_info.transport;
698  ast_sip_transport_monitor_register(rdata->tp_info.transport,
699  sub_tree_transport_cb, sub_tree);
700  /*
701  * FYI: ast_sip_transport_monitor_register holds a reference to the sub_tree
702  */
703  }
704  }
705  }
706 
707  pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri,
708  sub_tree->persistence->contact_uri, sizeof(sub_tree->persistence->contact_uri));
709  } else {
710  ast_log(LOG_WARNING, "Contact not updated due to missing contact header\n");
711  }
712 
713  /* When receiving a packet on an streaming transport, it's possible to receive more than one SIP
714  * message at a time into the rdata->pkt_info.packet buffer. However, the rdata->msg_info.msg_buf
715  * will always point to the proper SIP message that is to be processed. When updating subscription
716  * persistence that is pulled from persistent storage, though, the rdata->pkt_info.packet will
717  * only ever have a single SIP message on it, and so we base persistence on that.
718  */
721  if (rdata->msg_info.msg_buf) {
722  ast_copy_string(sub_tree->persistence->packet, rdata->msg_info.msg_buf,
723  MIN(sizeof(sub_tree->persistence->packet), rdata->msg_info.len + 1));
724  } else {
725  ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet,
726  sizeof(sub_tree->persistence->packet));
727  }
728  }
729  ast_copy_string(sub_tree->persistence->src_name, rdata->pkt_info.src_name,
730  sizeof(sub_tree->persistence->src_name));
731  sub_tree->persistence->src_port = rdata->pkt_info.src_port;
732  ast_copy_string(sub_tree->persistence->transport_key, rdata->tp_info.transport->type_name,
733  sizeof(sub_tree->persistence->transport_key));
734  ast_copy_pj_str(sub_tree->persistence->local_name, &rdata->tp_info.transport->local_name.host,
735  sizeof(sub_tree->persistence->local_name));
736  sub_tree->persistence->local_port = rdata->tp_info.transport->local_name.port;
737  }
738 
740 }
741 
742 /*! \brief Function which removes persistence of a subscription from sorcery */
744 {
745  if (!sub_tree->persistence) {
746  return;
747  }
748 
749  if (sub_tree->persistence->prune_on_boot && sub_tree->transport) {
750  ast_debug(3, "Unregistering transport monitor on %s '%s->%s'\n",
751  sub_tree->transport->obj_name,
752  sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",
753  sub_tree->root ? sub_tree->root->resource : "Unknown");
755  sub_tree_transport_cb, sub_tree, NULL);
756  }
757 
759  ao2_ref(sub_tree->persistence, -1);
760  sub_tree->persistence = NULL;
761 }
762 
763 
766  size_t num_accept, const char *body_type);
767 
768 /*! \brief Retrieve a handler using the Event header of an rdata message */
769 static struct ast_sip_subscription_handler *subscription_get_handler_from_rdata(pjsip_rx_data *rdata, const char *endpoint)
770 {
771  pjsip_event_hdr *event_header;
772  char event[32];
774 
775  event_header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_event_name, rdata->msg_info.msg->hdr.next);
776  if (!event_header) {
777  ast_log(LOG_WARNING, "Incoming SUBSCRIBE request from %s with no Event header\n",
778  endpoint ? endpoint : "Unknown");
779  return NULL;
780  }
781  ast_copy_pj_str(event, &event_header->event_type, sizeof(event));
782 
784  if (!handler) {
785  ast_log(LOG_WARNING, "No registered subscribe handler for event %s from %s\n", event,
786  endpoint ? endpoint : "Unknown");
787  }
788 
789  return handler;
790 }
791 
792 /*!
793  * \brief Accept headers that are exceptions to the rule
794  *
795  * Typically, when a SUBSCRIBE arrives, we attempt to find a
796  * body generator that matches one of the Accept headers in
797  * the request. When subscribing to a single resource, this works
798  * great. However, when subscribing to a list, things work
799  * differently. Most Accept header values are fine, but there
800  * are a couple that are endemic to resource lists that need
801  * to be ignored when searching for a body generator to use
802  * for the individual resources of the subscription.
803  */
804 const char *accept_exceptions[] = {
805  "multipart/related",
806  "application/rlmi+xml",
807 };
808 
809 /*!
810  * \brief Is the Accept header from the SUBSCRIBE in the list of exceptions?
811  *
812  * \retval 1 This Accept header value is an exception to the rule.
813  * \retval 0 This Accept header is not an exception to the rule.
814  */
815 static int exceptional_accept(const pj_str_t *accept)
816 {
817  int i;
818 
819  for (i = 0; i < ARRAY_LEN(accept_exceptions); ++i) {
820  if (!pj_strcmp2(accept, accept_exceptions[i])) {
821  return 1;
822  }
823  }
824 
825  return 0;
826 }
827 
828 /*! \brief Retrieve a body generator using the Accept header of an rdata message */
831 {
832  pjsip_accept_hdr *accept_header = (pjsip_accept_hdr *) &rdata->msg_info.msg->hdr;
833  char accept[AST_SIP_MAX_ACCEPT][64];
834  size_t num_accept_headers = 0;
835 
836  while ((accept_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, accept_header->next)) &&
837  (num_accept_headers < AST_SIP_MAX_ACCEPT)) {
838  int i;
839 
840  for (i = 0; i < accept_header->count && num_accept_headers < AST_SIP_MAX_ACCEPT; ++i) {
841  if (!exceptional_accept(&accept_header->values[i])) {
842  ast_copy_pj_str(accept[num_accept_headers], &accept_header->values[i], sizeof(accept[num_accept_headers]));
843  ++num_accept_headers;
844  }
845  }
846  }
847 
848  if (num_accept_headers == 0) {
849  /* If a SUBSCRIBE contains no Accept headers, then we must assume that
850  * the default accept type for the event package is to be used.
851  */
852  ast_copy_string(accept[0], handler->notifier->default_accept, sizeof(accept[0]));
853  num_accept_headers = 1;
854  }
855 
856  return find_body_generator(accept, num_accept_headers, handler->body_type);
857 }
858 
859 /*! \brief Check if the rdata has a Supported header containing 'eventlist'
860  *
861  * \retval 1 rdata has an eventlist containing supported header
862  * \retval 0 rdata doesn't have an eventlist containing supported header
863  */
864 static int ast_sip_pubsub_has_eventlist_support(pjsip_rx_data *rdata)
865 {
866  pjsip_supported_hdr *supported_header = (pjsip_supported_hdr *) &rdata->msg_info.msg->hdr;
867 
868  while ((supported_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_SUPPORTED, supported_header->next))) {
869  int i;
870 
871  for (i = 0; i < supported_header->count; i++) {
872  if (!pj_stricmp2(&supported_header->values[i], "eventlist")) {
873  return 1;
874  }
875  }
876  }
877 
878  return 0;
879 }
880 
881 struct resource_tree;
882 
883 /*!
884  * \brief A node for a resource tree.
885  */
886 struct tree_node {
888  unsigned int full_state;
889  char resource[0];
890 };
891 
892 /*!
893  * \brief Helper function for retrieving a resource list for a given event.
894  *
895  * This will retrieve a resource list that corresponds to the resource and event provided.
896  *
897  * \param resource The name of the resource list to retrieve
898  * \param event The expected event name on the resource list
899  */
900 static struct resource_list *retrieve_resource_list(const char *resource, const char *event)
901 {
902  struct resource_list *list;
903 
904  list = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "resource_list", resource);
905  if (!list) {
906  return NULL;
907  }
908 
909  if (strcmp(list->event, event)) {
910  ast_log(LOG_WARNING, "Found resource list %s, but its event type (%s) does not match SUBSCRIBE's (%s)\n",
911  resource, list->event, event);
912  ao2_cleanup(list);
913  return NULL;
914  }
915 
916  return list;
917 }
918 
919 /*!
920  * \brief Allocate a tree node
921  *
922  * In addition to allocating and initializing the tree node, the node is also added
923  * to the vector of visited resources. See \ref build_resource_tree for more information
924  * on the visited resources.
925  *
926  * \param resource The name of the resource for this tree node.
927  * \param visited The vector of resources that have been visited.
928  * \param full_state if allocating a list, indicate whether full state is requested in notifications.
929  * \retval NULL Allocation failure.
930  * \retval non-NULL The newly-allocated tree_node
931  */
932 static struct tree_node *tree_node_alloc(const char *resource, struct resources *visited, unsigned int full_state)
933 {
934  struct tree_node *node;
935 
936  node = ast_calloc(1, sizeof(*node) + strlen(resource) + 1);
937  if (!node) {
938  return NULL;
939  }
940 
941  strcpy(node->resource, resource);
942  if (AST_VECTOR_INIT(&node->children, 4)) {
943  ast_free(node);
944  return NULL;
945  }
946  node->full_state = full_state;
947 
948  if (visited) {
949  AST_VECTOR_APPEND(visited, resource);
950  }
951  return node;
952 }
953 
954 /*!
955  * \brief Destructor for a tree node
956  *
957  * This function calls recursively in order to destroy
958  * all nodes lower in the tree from the given node in
959  * addition to the node itself.
960  *
961  * \param node The node to destroy.
962  */
963 static void tree_node_destroy(struct tree_node *node)
964 {
965  int i;
966  if (!node) {
967  return;
968  }
969 
970  for (i = 0; i < AST_VECTOR_SIZE(&node->children); ++i) {
971  tree_node_destroy(AST_VECTOR_GET(&node->children, i));
972  }
973  AST_VECTOR_FREE(&node->children);
974  ast_free(node);
975 }
976 
977 /*!
978  * \brief Determine if this resource has been visited already
979  *
980  * See \ref build_resource_tree for more information
981  *
982  * \param resource The resource currently being visited
983  * \param visited The resources that have previously been visited
984  */
985 static int have_visited(const char *resource, struct resources *visited)
986 {
987  int i;
988 
989  for (i = 0; i < AST_VECTOR_SIZE(visited); ++i) {
990  if (!strcmp(resource, AST_VECTOR_GET(visited, i))) {
991  return 1;
992  }
993  }
994 
995  return 0;
996 }
997 
998 /*!
999  * \brief Build child nodes for a given parent.
1000  *
1001  * This iterates through the items on a resource list and creates tree nodes for each one. The
1002  * tree nodes created are children of the supplied parent node. If an item in the resource
1003  * list is itself a list, then this function is called recursively to provide children for
1004  * the new node.
1005  *
1006  * If an item in a resource list is not a list, then the supplied subscription handler is
1007  * called into as if a new SUBSCRIBE for the list item were presented. The handler's response
1008  * is used to determine if the node can be added to the tree or not.
1009  *
1010  * If a parent node ends up having no child nodes added under it, then the parent node is
1011  * pruned from the tree.
1012  *
1013  * \param endpoint The endpoint that sent the inbound SUBSCRIBE.
1014  * \param handler The subscription handler for leaf nodes in the tree.
1015  * \param list The configured resource list from which the child node is being built.
1016  * \param parent The parent node for these children.
1017  * \param visited The resources that have already been visited.
1018  */
1019 static void build_node_children(struct ast_sip_endpoint *endpoint, const struct ast_sip_subscription_handler *handler,
1020  struct resource_list *list, struct tree_node *parent, struct resources *visited)
1021 {
1022  int i;
1023 
1024  for (i = 0; i < AST_VECTOR_SIZE(&list->items); ++i) {
1025  struct tree_node *current;
1026  struct resource_list *child_list;
1027  const char *resource = AST_VECTOR_GET(&list->items, i);
1028 
1029  if (have_visited(resource, visited)) {
1030  ast_debug(1, "Already visited resource %s. Avoiding duplicate resource or potential loop.\n", resource);
1031  continue;
1032  }
1033 
1034  child_list = retrieve_resource_list(resource, list->event);
1035  if (!child_list) {
1036  int resp = handler->notifier->new_subscribe(endpoint, resource);
1037  if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
1038  current = tree_node_alloc(resource, visited, 0);
1039  if (!current) {
1040  ast_debug(1,
1041  "Subscription to leaf resource %s was successful, but encountered allocation error afterwards\n",
1042  resource);
1043  continue;
1044  }
1045  ast_debug(2, "Subscription to leaf resource %s resulted in success. Adding to parent %s\n",
1046  resource, parent->resource);
1047  if (AST_VECTOR_APPEND(&parent->children, current)) {
1049  }
1050  } else {
1051  ast_debug(2, "Subscription to leaf resource %s resulted in error response %d\n",
1052  resource, resp);
1053  }
1054  } else {
1055  ast_debug(2, "Resource %s (child of %s) is a list\n", resource, parent->resource);
1056  current = tree_node_alloc(resource, visited, child_list->full_state);
1057  if (!current) {
1058  ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
1059  continue;
1060  }
1061  build_node_children(endpoint, handler, child_list, current, visited);
1062  if (AST_VECTOR_SIZE(&current->children) > 0) {
1063  ast_debug(1, "List %s had no successful children.\n", resource);
1064  if (AST_VECTOR_APPEND(&parent->children, current)) {
1066  }
1067  } else {
1068  ast_debug(2, "List %s had successful children. Adding to parent %s\n",
1069  resource, parent->resource);
1071  }
1072  ao2_cleanup(child_list);
1073  }
1074  }
1075 }
1076 
1077 /*!
1078  * \brief A resource tree
1079  *
1080  * When an inbound SUBSCRIBE arrives, the resource being subscribed to may
1081  * be a resource list. If this is the case, the resource list may contain resources
1082  * that are themselves lists. The structure needed to hold the resources is
1083  * a tree.
1084  *
1085  * Upon receipt of the SUBSCRIBE, the tree is built by determining if subscriptions
1086  * to the individual resources in the tree would be successful or not. Any successful
1087  * subscriptions result in a node in the tree being created. Any unsuccessful subscriptions
1088  * result in no node being created.
1089  *
1090  * This tree can be seen as a bare-bones analog of the tree of ast_sip_subscriptions that
1091  * will end up being created to actually carry out the duties of a SIP SUBSCRIBE dialog.
1092  */
1094  struct tree_node *root;
1096 };
1097 
1098 /*!
1099  * \brief Destroy a resource tree.
1100  *
1101  * This function makes no assumptions about how the tree itself was
1102  * allocated and does not attempt to free the tree itself. Callers
1103  * of this function are responsible for freeing the tree.
1104  *
1105  * \param tree The tree to destroy.
1106  */
1107 static void resource_tree_destroy(struct resource_tree *tree)
1108 {
1109  if (tree) {
1110  tree_node_destroy(tree->root);
1111  }
1112 }
1113 
1114 /*!
1115  * \brief Build a resource tree
1116  *
1117  * This function builds a resource tree based on the requested resource in a SUBSCRIBE request.
1118  *
1119  * This function also creates a container that has all resources that have been visited during
1120  * creation of the tree, whether those resources resulted in a tree node being created or not.
1121  * Keeping this container of visited resources allows for misconfigurations such as loops in
1122  * the tree or duplicated resources to be detected.
1123  *
1124  * \param endpoint The endpoint that sent the SUBSCRIBE request.
1125  * \param handler The subscription handler for leaf nodes in the tree.
1126  * \param resource The resource requested in the SUBSCRIBE request.
1127  * \param tree The tree that is to be built.
1128  * \param has_eventlist_support
1129  *
1130  * \retval 200-299 Successfully subscribed to at least one resource.
1131  * \retval 300-699 Failure to subscribe to requested resource.
1132  */
1134  const char *resource, struct resource_tree *tree, int has_eventlist_support)
1135 {
1136  RAII_VAR(struct resource_list *, list, NULL, ao2_cleanup);
1137  struct resources visited;
1138 
1139  if (!has_eventlist_support || !(list = retrieve_resource_list(resource, handler->event_name))) {
1140  ast_debug(2, "Subscription '%s->%s' is not to a list\n",
1141  ast_sorcery_object_get_id(endpoint), resource);
1142  tree->root = tree_node_alloc(resource, NULL, 0);
1143  if (!tree->root) {
1144  return 500;
1145  }
1146  return handler->notifier->new_subscribe(endpoint, resource);
1147  }
1148 
1149  ast_debug(2, "Subscription '%s->%s' is a list\n",
1150  ast_sorcery_object_get_id(endpoint), resource);
1151  if (AST_VECTOR_INIT(&visited, AST_VECTOR_SIZE(&list->items))) {
1152  return 500;
1153  }
1154 
1155  tree->root = tree_node_alloc(resource, &visited, list->full_state);
1156  if (!tree->root) {
1157  AST_VECTOR_FREE(&visited);
1158  return 500;
1159  }
1160 
1161  tree->notification_batch_interval = list->notification_batch_interval;
1162 
1163  build_node_children(endpoint, handler, list, tree->root, &visited);
1164  AST_VECTOR_FREE(&visited);
1165 
1166  if (AST_VECTOR_SIZE(&tree->root->children) > 0) {
1167  return 200;
1168  } else {
1169  return 500;
1170  }
1171 }
1172 
1173 static void add_subscription(struct sip_subscription_tree *obj)
1174 {
1178 }
1179 
1181 {
1182  struct sip_subscription_tree *i;
1183 
1186  if (i == obj) {
1188  if (i->root) {
1189  ast_debug(2, "Removing subscription '%s->%s' from list of subscriptions\n",
1191  }
1192  break;
1193  }
1194  }
1197 }
1198 
1200 {
1201  ast_debug(3, "Destroying SIP subscription from '%s->%s'\n",
1202  sub->tree && sub->tree->endpoint ? ast_sorcery_object_get_id(sub->tree->endpoint) : "Unknown",
1203  sub->resource);
1204 
1205  ast_free(sub->body_text);
1206 
1207  AST_VECTOR_FREE(&sub->children);
1208  ao2_cleanup(sub->datastores);
1209  ast_json_unref(sub->persistence_data);
1210  ast_free(sub);
1211 }
1212 
1214 {
1215  int i;
1216 
1217  if (!root) {
1218  return;
1219  }
1220 
1221  for (i = 0; i < AST_VECTOR_SIZE(&root->children); ++i) {
1222  struct ast_sip_subscription *child;
1223 
1224  child = AST_VECTOR_GET(&root->children, i);
1225  destroy_subscriptions(child);
1226  }
1227 
1228  destroy_subscription(root);
1229 }
1230 
1232  const char *resource, struct sip_subscription_tree *tree)
1233 {
1234  struct ast_sip_subscription *sub;
1235  pjsip_sip_uri *contact_uri;
1236 
1237  sub = ast_calloc(1, sizeof(*sub) + strlen(resource) + 1);
1238  if (!sub) {
1239  return NULL;
1240  }
1241  strcpy(sub->resource, resource); /* Safe */
1242 
1243  sub->datastores = ast_datastores_alloc();
1244  if (!sub->datastores) {
1246  return NULL;
1247  }
1248 
1249  sub->body_text = ast_str_create(128);
1250  if (!sub->body_text) {
1252  return NULL;
1253  }
1254 
1255  sub->uri = pjsip_sip_uri_create(tree->dlg->pool, PJ_FALSE);
1256  contact_uri = pjsip_uri_get_uri(tree->dlg->local.contact->uri);
1257  pjsip_sip_uri_assign(tree->dlg->pool, sub->uri, contact_uri);
1258  pj_strdup2(tree->dlg->pool, &sub->uri->user, resource);
1259 
1260  /* If there is any persistence information available for this subscription that was persisted
1261  * then make it available so that the NOTIFY has the correct state.
1262  */
1263 
1266  }
1267 
1268  sub->handler = handler;
1269  sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
1270  sub->tree = ao2_bump(tree);
1271 
1272  return sub;
1273 }
1274 
1275 /*!
1276  * \brief Create a tree of virtual subscriptions based on a resource tree node.
1277  *
1278  * \param handler The handler to supply to leaf subscriptions.
1279  * \param resource The requested resource for this subscription.
1280  * \param generator Body generator to use for leaf subscriptions.
1281  * \param tree The root of the subscription tree.
1282  * \param current The tree node that corresponds to the subscription being created.
1283  */
1285  const char *resource, struct ast_sip_pubsub_body_generator *generator,
1286  struct sip_subscription_tree *tree, struct tree_node *current)
1287 {
1288  int i;
1289  struct ast_sip_subscription *sub;
1290 
1292  if (!sub) {
1293  return NULL;
1294  }
1295 
1296  sub->full_state = current->full_state;
1297  sub->body_generator = generator;
1298  AST_VECTOR_INIT(&sub->children, AST_VECTOR_SIZE(&current->children));
1299 
1300  for (i = 0; i < AST_VECTOR_SIZE(&current->children); ++i) {
1301  struct ast_sip_subscription *child;
1302  struct tree_node *child_node = AST_VECTOR_GET(&current->children, i);
1303 
1304  child = create_virtual_subscriptions(handler, child_node->resource, generator,
1305  tree, child_node);
1306 
1307  if (!child) {
1308  ast_debug(1, "Child subscription to resource %s could not be created\n",
1309  child_node->resource);
1310  continue;
1311  }
1312 
1313  if (AST_VECTOR_APPEND(&sub->children, child)) {
1314  ast_debug(1, "Child subscription to resource %s could not be appended\n",
1315  child_node->resource);
1316  destroy_subscription(child);
1317  /* Have to release tree here too because a ref was added
1318  * to child that destroy_subscription() doesn't release. */
1319  ao2_cleanup(tree);
1320  }
1321  }
1322 
1323  return sub;
1324 }
1325 
1327 {
1328  int i;
1329 
1330  if (!sub) {
1331  return;
1332  }
1333 
1334  if (AST_VECTOR_SIZE(&sub->children) > 0) {
1335  for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
1336  shutdown_subscriptions(AST_VECTOR_GET(&sub->children, i));
1337  }
1338  return;
1339  }
1340 
1341  /* We notify subscription shutdown only on the tree leaves. */
1342  if (sub->handler->subscription_shutdown) {
1343  sub->handler->subscription_shutdown(sub);
1344  }
1345 }
1346 static int subscription_unreference_dialog(void *obj)
1347 {
1348  struct sip_subscription_tree *sub_tree = obj;
1349 
1350  /* This is why we keep the dialog on the subscription. When the subscription
1351  * is destroyed, there is no guarantee that the underlying dialog is ready
1352  * to be destroyed. Furthermore, there's no guarantee in the opposite direction
1353  * either. The dialog could be destroyed before our subscription is. We fix
1354  * this problem by keeping a reference to the dialog until it is time to
1355  * destroy the subscription. We need to have the dialog available when the
1356  * subscription is destroyed so that we can guarantee that our attempt to
1357  * remove the serializer will be successful.
1358  */
1359  pjsip_dlg_dec_session(sub_tree->dlg, &pubsub_module);
1360  sub_tree->dlg = NULL;
1361 
1362  return 0;
1363 }
1364 
1365 static void subscription_tree_destructor(void *obj)
1366 {
1367  struct sip_subscription_tree *sub_tree = obj;
1368 
1369  ast_debug(3, "Destroying subscription tree %p '%s->%s'\n",
1370  sub_tree,
1371  sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",
1372  sub_tree->root ? sub_tree->root->resource : "Unknown");
1373 
1374  destroy_subscriptions(sub_tree->root);
1375 
1376  if (sub_tree->dlg) {
1379  }
1380 
1381  ao2_cleanup(sub_tree->endpoint);
1382 
1385 }
1386 
1388 {
1389  ast_debug(3, "Removing subscription %p '%s->%s' reference to subscription tree %p\n",
1390  sub, ast_sorcery_object_get_id(sub->tree->endpoint), sub->resource, sub->tree);
1391  ao2_cleanup(sub->tree);
1392 }
1393 
1394 static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg)
1395 {
1396  sub_tree->dlg = dlg;
1399  pjsip_evsub_set_mod_data(sub_tree->evsub, pubsub_module.id, sub_tree);
1400  pjsip_dlg_inc_session(dlg, &pubsub_module);
1401 }
1402 
1403 static struct sip_subscription_tree *allocate_subscription_tree(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
1404 {
1405  struct sip_subscription_tree *sub_tree;
1406 
1407  sub_tree = ao2_alloc(sizeof *sub_tree, subscription_tree_destructor);
1408  if (!sub_tree) {
1409  return NULL;
1410  }
1411 
1413 
1414  if (rdata) {
1415  /*
1416  * We must continue using the serializer that the original
1417  * SUBSCRIBE came in on for the dialog. There may be
1418  * retransmissions already enqueued in the original
1419  * serializer that can result in reentrancy and message
1420  * sequencing problems.
1421  */
1423  } else {
1424  char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
1425 
1426  /* Create name with seq number appended. */
1427  ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/pubsub/%s",
1429 
1430  sub_tree->serializer = ast_sip_create_serializer(tps_name);
1431  }
1432  if (!sub_tree->serializer) {
1433  ao2_ref(sub_tree, -1);
1434  return NULL;
1435  }
1436 
1437  sub_tree->endpoint = ao2_bump(endpoint);
1438  sub_tree->notify_sched_id = -1;
1439 
1440  return sub_tree;
1441 }
1442 
1443 /*!
1444  * \brief Create a subscription tree based on a resource tree.
1445  *
1446  * Using the previously-determined valid resources in the provided resource tree,
1447  * a corresponding tree of ast_sip_subscriptions are created. The root of the
1448  * subscription tree is a real subscription, and the rest in the tree are
1449  * virtual subscriptions.
1450  *
1451  * \param handler The handler to use for leaf subscriptions
1452  * \param endpoint The endpoint that sent the SUBSCRIBE request
1453  * \param rdata The SUBSCRIBE content
1454  * \param resource The requested resource in the SUBSCRIBE request
1455  * \param generator The body generator to use in leaf subscriptions
1456  * \param tree The resource tree on which the subscription tree is based
1457  * \param[out] dlg_status The result of attempting to create a dialog
1458  * \param persistence
1459  *
1460  * \retval NULL Could not create the subscription tree
1461  * \retval non-NULL The root of the created subscription tree
1462  */
1463 
1465  struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource,
1466  struct ast_sip_pubsub_body_generator *generator, struct resource_tree *tree,
1467  pj_status_t *dlg_status, struct subscription_persistence *persistence)
1468 {
1469  struct sip_subscription_tree *sub_tree;
1470  pjsip_dialog *dlg;
1471 
1472  sub_tree = allocate_subscription_tree(endpoint, rdata);
1473  if (!sub_tree) {
1474  *dlg_status = PJ_ENOMEM;
1475  return NULL;
1476  }
1477  sub_tree->role = AST_SIP_NOTIFIER;
1478 
1479  dlg = ast_sip_create_dialog_uas_locked(endpoint, rdata, dlg_status);
1480  if (!dlg) {
1481  if (*dlg_status != PJ_EEXISTS) {
1482  ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n");
1483  }
1484  ao2_ref(sub_tree, -1);
1485  return NULL;
1486  }
1487 
1488  persistence = ast_sip_mod_data_get(rdata->endpt_info.mod_data,
1490  if (persistence) {
1491  /* Update the created dialog with the persisted information */
1492  pjsip_ua_unregister_dlg(pjsip_ua_instance(), dlg);
1493  pj_strdup2(dlg->pool, &dlg->local.info->tag, persistence->tag);
1494  dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->local.info->tag);
1495  pjsip_ua_register_dlg(pjsip_ua_instance(), dlg);
1496  dlg->local.cseq = persistence->cseq;
1497  }
1498 
1499  pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub_tree->evsub);
1500 
1501  subscription_setup_dialog(sub_tree, dlg);
1502 
1503  /*
1504  * The evsub and subscription setup both add dialog refs, so the dialog ref that
1505  * was added when the dialog was created (see ast_sip_create_dialog_uas_lock) can
1506  * now be removed. The lock should no longer be needed so can be removed too.
1507  */
1508  pjsip_dlg_dec_lock(dlg);
1509 
1510 #ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
1511  pjsip_evsub_add_ref(sub_tree->evsub);
1512 #endif
1513 
1514  ast_sip_mod_data_set(dlg->pool, dlg->mod_data, pubsub_module.id, MOD_DATA_MSG,
1515  pjsip_msg_clone(dlg->pool, rdata->msg_info.msg));
1516 
1518 
1519  /* Persistence information needs to be available for all the subscriptions */
1520  sub_tree->persistence = ao2_bump(persistence);
1521 
1522  sub_tree->root = create_virtual_subscriptions(handler, resource, generator, sub_tree, tree->root);
1523  if (AST_VECTOR_SIZE(&sub_tree->root->children) > 0) {
1524  sub_tree->is_list = 1;
1525  }
1526 
1527  add_subscription(sub_tree);
1528 
1529  return sub_tree;
1530 }
1531 
1532 /*! Wrapper structure for initial_notify_task */
1535  unsigned int expires;
1536 };
1537 
1538 static int initial_notify_task(void *obj);
1539 static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state);
1540 
1541 /*! Persistent subscription recreation continuation under distributor serializer data */
1544  pjsip_rx_data *rdata;
1545 };
1546 
1547 /*!
1548  * \internal
1549  * \brief subscription_persistence_recreate continuation under distributor serializer.
1550  * \since 13.10.0
1551  *
1552  * \retval 0 on success.
1553  * \retval -1 on error.
1554  */
1555 static int sub_persistence_recreate(void *obj)
1556 {
1557  struct persistence_recreate_data *recreate_data = obj;
1558  struct subscription_persistence *persistence = recreate_data->persistence;
1559  pjsip_rx_data *rdata = recreate_data->rdata;
1560  struct ast_sip_endpoint *endpoint;
1561  struct sip_subscription_tree *sub_tree;
1562  struct ast_sip_pubsub_body_generator *generator;
1564  char *resource;
1565  pjsip_sip_uri *request_uri;
1566  size_t resource_size;
1567  int resp;
1568  struct resource_tree tree;
1569  pjsip_expires_hdr *expires_header;
1570  int64_t expires;
1571 
1572  request_uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
1573  resource_size = pj_strlen(&request_uri->user) + 1;
1574  resource = ast_alloca(resource_size);
1575  ast_copy_pj_str(resource, &request_uri->user, resource_size);
1576 
1577  /*
1578  * We may want to match without any user options getting
1579  * in the way.
1580  */
1582 
1583  handler = subscription_get_handler_from_rdata(rdata, persistence->endpoint);
1584  if (!handler || !handler->notifier) {
1585  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not get subscription handler.\n",
1586  persistence->endpoint);
1587  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1588  return 0;
1589  }
1590 
1591  generator = subscription_get_generator_from_rdata(rdata, handler);
1592  if (!generator) {
1593  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Body generator not available.\n",
1594  persistence->endpoint);
1595  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1596  return 0;
1597  }
1598 
1599  ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data,
1600  pubsub_module.id, MOD_DATA_PERSISTENCE, persistence);
1601 
1602  /* Getting the endpoint may take some time that can affect the expiration. */
1603  endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
1604  persistence->endpoint);
1605  if (!endpoint) {
1606  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The endpoint was not found\n",
1607  persistence->endpoint);
1608  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1609  return 0;
1610  }
1611 
1612  /* Update the expiration header with the new expiration */
1613  expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
1614  rdata->msg_info.msg->hdr.next);
1615  if (!expires_header) {
1616  expires_header = pjsip_expires_hdr_create(rdata->tp_info.pool, 0);
1617  if (!expires_header) {
1618  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not update expires header.\n",
1619  persistence->endpoint);
1620  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1621  ao2_ref(endpoint, -1);
1622  return 0;
1623  }
1624  pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr *) expires_header);
1625  }
1626 
1627  expires = (ast_tvdiff_ms(persistence->expires, ast_tvnow()) / 1000);
1628  if (expires <= 0) {
1629  /* The subscription expired since we started recreating the subscription. */
1630  ast_debug(3, "Expired subscription retrived from persistent store '%s' %s\n",
1631  persistence->endpoint, persistence->tag);
1632  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1633  ao2_ref(endpoint, -1);
1634  return 0;
1635  }
1636  expires_header->ivalue = expires;
1637 
1638  memset(&tree, 0, sizeof(tree));
1639  resp = build_resource_tree(endpoint, handler, resource, &tree,
1641  if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
1642  pj_status_t dlg_status;
1643 
1644  sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator,
1645  &tree, &dlg_status, persistence);
1646  if (!sub_tree) {
1647  if (dlg_status != PJ_EEXISTS) {
1648  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not create subscription tree.\n",
1649  persistence->endpoint);
1650  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1651  }
1652  } else {
1653  struct initial_notify_data *ind = ast_malloc(sizeof(*ind));
1654 
1655  if (!ind) {
1656  pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
1657  goto error;
1658  }
1659 
1660  ind->sub_tree = ao2_bump(sub_tree);
1661  ind->expires = expires_header->ivalue;
1662 
1665  /* Could not send initial subscribe NOTIFY */
1666  pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
1667  ao2_ref(sub_tree, -1);
1668  ast_free(ind);
1669  }
1670  }
1671  } else {
1672  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1673  }
1674 
1675 error:
1676  resource_tree_destroy(&tree);
1677  ao2_ref(endpoint, -1);
1678 
1679  return 0;
1680 }
1681 
1682 /*! \brief Callback function to perform the actual recreation of a subscription */
1683 static int subscription_persistence_recreate(void *obj, void *arg, int flags)
1684 {
1685  struct subscription_persistence *persistence = obj;
1686  pj_pool_t *pool = arg;
1687  struct ast_taskprocessor *serializer;
1688  pjsip_rx_data rdata;
1689  struct persistence_recreate_data recreate_data;
1690 
1691  /* If this subscription used a reliable transport it can't be reestablished so remove it */
1692  if (persistence->prune_on_boot) {
1693  ast_debug(3, "Deleting subscription marked as 'prune' from persistent store '%s' %s\n",
1696  return 0;
1697  }
1698 
1699  /* If this subscription has already expired remove it */
1700  if (ast_tvdiff_ms(persistence->expires, ast_tvnow()) <= 0) {
1701  ast_debug(3, "Expired subscription retrived from persistent store '%s' %s\n",
1704  return 0;
1705  }
1706 
1707  memset(&rdata, 0, sizeof(rdata));
1708  pj_pool_reset(pool);
1709  rdata.tp_info.pool = pool;
1710 
1714  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The message could not be parsed\n",
1717  return 0;
1718  }
1719 
1720  if (rdata.msg_info.msg->type != PJSIP_REQUEST_MSG) {
1721  ast_log(LOG_NOTICE, "Failed recreating '%s' subscription: Stored a SIP response instead of a request.\n",
1724  return 0;
1725  }
1726 
1727  /* Continue the remainder in the distributor serializer */
1729  if (!serializer) {
1730  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not get distributor serializer.\n",
1733  return 0;
1734  }
1735  recreate_data.persistence = persistence;
1736  recreate_data.rdata = &rdata;
1738  &recreate_data)) {
1739  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not continue under distributor serializer.\n",
1742  }
1744 
1745  return 0;
1746 }
1747 
1748 /*! \brief Function which loads and recreates persisted subscriptions upon startup when the system is fully booted */
1749 static int subscription_persistence_load(void *data)
1750 {
1751  struct ao2_container *persisted_subscriptions = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
1752  "subscription_persistence", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1753  pj_pool_t *pool;
1754 
1755  pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "rtd%p", PJSIP_POOL_RDATA_LEN,
1756  PJSIP_POOL_RDATA_INC);
1757  if (!pool) {
1758  ast_log(LOG_WARNING, "Could not create a memory pool for recreating SIP subscriptions\n");
1759  return 0;
1760  }
1761 
1762  ao2_callback(persisted_subscriptions, OBJ_NODATA, subscription_persistence_recreate, pool);
1763 
1764  pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1765 
1766  ao2_ref(persisted_subscriptions, -1);
1767  return 0;
1768 }
1769 
1770 /*! \brief Event callback which fires subscription persistence recreation when the system is fully booted */
1772 {
1773  struct ast_json_payload *payload;
1774  const char *type;
1775 
1777  return;
1778  }
1779 
1780  payload = stasis_message_data(message);
1781  type = ast_json_string_get(ast_json_object_get(payload->json, "type"));
1782 
1783  /* This subscription only responds to the FullyBooted event so that all modules have been loaded when we
1784  * recreate SIP subscriptions.
1785  */
1786  if (strcmp(type, "FullyBooted")) {
1787  return;
1788  }
1789 
1790  /* This has to be here so the subscription is recreated when the body generator is available */
1792 
1793  /* Once the system is fully booted we don't care anymore */
1795 }
1796 
1797 typedef int (*on_subscription_t)(struct sip_subscription_tree *sub, void *arg);
1798 
1799 static int for_each_subscription(on_subscription_t on_subscription, void *arg)
1800 {
1801  int num = 0;
1802  struct sip_subscription_tree *i;
1803 
1804  if (!on_subscription) {
1805  return num;
1806  }
1807 
1810  if (on_subscription(i, arg)) {
1811  break;
1812  }
1813  ++num;
1814  }
1816  return num;
1817 }
1818 
1819 static void sip_subscription_to_ami(struct sip_subscription_tree *sub_tree,
1820  struct ast_str **buf)
1821 {
1822  char str[256];
1823  struct ast_sip_endpoint_id_configuration *id = &sub_tree->endpoint->id;
1824 
1825  ast_str_append(buf, 0, "Role: %s\r\n",
1826  sip_subscription_roles_map[sub_tree->role]);
1827  ast_str_append(buf, 0, "Endpoint: %s\r\n",
1828  ast_sorcery_object_get_id(sub_tree->endpoint));
1829 
1830  if (sub_tree->dlg) {
1831  ast_copy_pj_str(str, &sub_tree->dlg->call_id->id, sizeof(str));
1832  } else {
1833  ast_copy_string(str, "<unknown>", sizeof(str));
1834  }
1835  ast_str_append(buf, 0, "Callid: %s\r\n", str);
1836 
1837  ast_str_append(buf, 0, "State: %s\r\n", pjsip_evsub_get_state_name(sub_tree->evsub));
1838 
1839  ast_callerid_merge(str, sizeof(str),
1840  S_COR(id->self.name.valid, id->self.name.str, NULL),
1841  S_COR(id->self.number.valid, id->self.number.str, NULL),
1842  "Unknown");
1843 
1844  ast_str_append(buf, 0, "Callerid: %s\r\n", str);
1845 
1846  /* XXX This needs to be done recursively for lists */
1847  if (sub_tree->root->handler->to_ami) {
1848  sub_tree->root->handler->to_ami(sub_tree->root, buf);
1849  }
1850 }
1851 
1852 
1854 {
1855  pjsip_dialog *dlg;
1856  pjsip_msg *msg;
1857  pj_str_t name;
1858 
1859  dlg = sub->tree->dlg;
1860  msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
1861  pj_cstr(&name, header);
1862 
1863  return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
1864 }
1865 
1866 /* XXX This function is not used. */
1868  struct ast_sip_endpoint *endpoint, const char *resource)
1869 {
1870  struct ast_sip_subscription *sub;
1871  pjsip_dialog *dlg;
1872  struct ast_sip_contact *contact;
1873  pj_str_t event;
1874  pjsip_tx_data *tdata;
1875  pjsip_evsub *evsub;
1876  struct sip_subscription_tree *sub_tree = NULL;
1877 
1879  if (!sub_tree) {
1880  return NULL;
1881  }
1882 
1883  sub = allocate_subscription(handler, resource, sub_tree);
1884  if (!sub) {
1885  ao2_cleanup(sub_tree);
1886  return NULL;
1887  }
1888 
1890  if (!contact || ast_strlen_zero(contact->uri)) {
1891  ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create SIP subsription\n",
1893  ao2_ref(sub_tree, -1);
1895  return NULL;
1896  }
1897 
1900  if (!dlg) {
1901  ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n");
1902  ao2_ref(sub_tree, -1);
1903  return NULL;
1904  }
1905 
1906  pj_cstr(&event, handler->event_name);
1907  pjsip_evsub_create_uac(dlg, &pubsub_cb, &event, 0, &sub_tree->evsub);
1908  subscription_setup_dialog(sub_tree, dlg);
1909 
1910  evsub = sub_tree->evsub;
1911 
1912  if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
1913  pjsip_evsub_send_request(sub_tree->evsub, tdata);
1914  } else {
1915  /* pjsip_evsub_terminate will result in pubsub_on_evsub_state,
1916  * being called and terminating the subscription. Therefore, we don't
1917  * need to decrease the reference count of sub here.
1918  */
1919  pjsip_evsub_terminate(evsub, PJ_TRUE);
1920  ao2_ref(sub_tree, -1);
1921  return NULL;
1922  }
1923 
1924  add_subscription(sub_tree);
1925 
1926  return sub;
1927 }
1928 
1930 {
1931  ast_assert(sub->tree->dlg != NULL);
1932  return sub->tree->dlg;
1933 }
1934 
1936 {
1937  ast_assert(sub->tree->endpoint != NULL);
1938  return ao2_bump(sub->tree->endpoint);
1939 }
1940 
1942 {
1943  ast_assert(sub->tree->serializer != NULL);
1944  return sub->tree->serializer;
1945 }
1946 
1947 /*!
1948  * \brief Pre-allocate a buffer for the transmission
1949  *
1950  * Typically, we let PJSIP do this step for us when we send a request. PJSIP's buffer
1951  * allocation algorithm is to allocate a buffer of PJSIP_MAX_PKT_LEN bytes and attempt
1952  * to write the packet to the allocated buffer. If the buffer is too small to hold the
1953  * packet, then we get told the message is too long to be sent.
1954  *
1955  * When dealing with SIP NOTIFY, especially with RLS, it is possible to exceed
1956  * PJSIP_MAX_PKT_LEN. Rather than accepting the limitation imposed on us by default,
1957  * we instead take the strategy of pre-allocating the buffer, testing for ourselves
1958  * if the message will fit, and resizing the buffer as required.
1959  *
1960  * The limit we impose is double that of the maximum packet length.
1961  *
1962  * \param tdata The tdata onto which to allocate a buffer
1963  * \retval 0 Success
1964  * \retval -1 The message is too large
1965  */
1966 static int allocate_tdata_buffer(pjsip_tx_data *tdata)
1967 {
1968  int buf_size;
1969  int size = -1;
1970  char *buf;
1971 
1972  for (buf_size = PJSIP_MAX_PKT_LEN; size == -1 && buf_size < (PJSIP_MAX_PKT_LEN * 2); buf_size *= 2) {
1973  buf = pj_pool_alloc(tdata->pool, buf_size);
1974  size = pjsip_msg_print(tdata->msg, buf, buf_size);
1975  }
1976 
1977  if (size == -1) {
1978  return -1;
1979  }
1980 
1981  tdata->buf.start = buf;
1982  tdata->buf.cur = tdata->buf.start;
1983  tdata->buf.end = tdata->buf.start + buf_size;
1984 
1985  return 0;
1986 }
1987 
1988 static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata)
1989 {
1990 #ifdef TEST_FRAMEWORK
1991  struct ast_sip_endpoint *endpoint = sub_tree->endpoint;
1992  pjsip_evsub *evsub = sub_tree->evsub;
1993 #endif
1994  int res;
1995 
1996  if (allocate_tdata_buffer(tdata)) {
1997  ast_log(LOG_ERROR, "SIP request %s is too large to send.\n", tdata->info);
1998  pjsip_tx_data_dec_ref(tdata);
1999  return -1;
2000  }
2001 
2002  res = pjsip_evsub_send_request(sub_tree->evsub, tdata);
2003 
2005 
2006  ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
2007  "StateText: %s\r\n"
2008  "Endpoint: %s\r\n",
2009  pjsip_evsub_get_state_name(evsub),
2010  ast_sorcery_object_get_id(endpoint));
2011 
2012  return (res == PJ_SUCCESS ? 0 : -1);
2013 }
2014 
2015 /*!
2016  * \brief Add a resource XML element to an RLMI body
2017  *
2018  * Each resource element represents a subscribed resource in the list. This function currently
2019  * will unconditionally add an instance element to each created resource element. Instance
2020  * elements refer to later parts in the multipart body.
2021  *
2022  * \param pool PJLIB allocation pool
2023  * \param rlmi
2024  * \param cid Content-ID header of the resource
2025  * \param resource_name Name of the resource
2026  * \param resource_uri URI of the resource
2027  * \param state State of the subscribed resource
2028  */
2029 static void add_rlmi_resource(pj_pool_t *pool, pj_xml_node *rlmi, const pjsip_generic_string_hdr *cid,
2030  const char *resource_name, const pjsip_sip_uri *resource_uri, pjsip_evsub_state state)
2031 {
2032  static pj_str_t cid_name = { "cid", 3 };
2033  pj_xml_node *resource;
2034  pj_xml_node *name;
2035  pj_xml_node *instance;
2036  pj_xml_attr *cid_attr;
2037  char id[6];
2038  char uri[PJSIP_MAX_URL_SIZE];
2039 
2040  /* This creates a string representing the Content-ID without the enclosing < > */
2041  const pj_str_t cid_stripped = {
2042  .ptr = cid->hvalue.ptr + 1,
2043  .slen = cid->hvalue.slen - 2,
2044  };
2045 
2046  resource = ast_sip_presence_xml_create_node(pool, rlmi, "resource");
2047  name = ast_sip_presence_xml_create_node(pool, resource, "name");
2048  instance = ast_sip_presence_xml_create_node(pool, resource, "instance");
2049 
2050  pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, resource_uri, uri, sizeof(uri));
2051  ast_sip_presence_xml_create_attr(pool, resource, "uri", uri);
2052 
2053  pj_strdup2(pool, &name->content, resource_name);
2054 
2055  ast_generate_random_string(id, sizeof(id));
2056 
2057  ast_sip_presence_xml_create_attr(pool, instance, "id", id);
2058  ast_sip_presence_xml_create_attr(pool, instance, "state",
2059  state == PJSIP_EVSUB_STATE_TERMINATED ? "terminated" : "active");
2060 
2061  /* Use the PJLIB-util XML library directly here since we are using a
2062  * pj_str_t
2063  */
2064 
2065  cid_attr = pj_xml_attr_new(pool, &cid_name, &cid_stripped);
2066  pj_xml_add_attr(instance, cid_attr);
2067 }
2068 
2069 /*!
2070  * \brief A multipart body part and meta-information
2071  *
2072  * When creating a multipart body part, the end result (the
2073  * pjsip_multipart_part) is hard to inspect without undoing
2074  * a lot of what was done to create it. Therefore, we use this
2075  * structure to store meta-information about the body part.
2076  *
2077  * The main consumer of this is the creator of the RLMI body
2078  * part of a multipart resource list body.
2079  */
2080 struct body_part {
2081  /*! Content-ID header for the body part */
2082  pjsip_generic_string_hdr *cid;
2083  /*! Subscribed resource represented in the body part */
2084  const char *resource;
2085  /*! URI for the subscribed body part */
2086  pjsip_sip_uri *uri;
2087  /*! Subscription state of the resource represented in the body part */
2088  pjsip_evsub_state state;
2089  /*! The actual body part that will be present in the multipart body */
2090  pjsip_multipart_part *part;
2091 };
2092 
2093 /*!
2094  * \brief Type declaration for container of body part structures
2095  */
2097 
2098 /*!
2099  * \brief Create a Content-ID header
2100  *
2101  * Content-ID headers are required by RFC2387 for multipart/related
2102  * bodies. They serve as identifiers for each part of the multipart body.
2103  *
2104  * \param pool PJLIB allocation pool
2105  * \param sub Subscription to a resource
2106  */
2107 static pjsip_generic_string_hdr *generate_content_id_hdr(pj_pool_t *pool,
2108  const struct ast_sip_subscription *sub)
2109 {
2110  static const pj_str_t cid_name = { "Content-ID", 10 };
2111  pjsip_generic_string_hdr *cid;
2112  char id[6];
2113  size_t alloc_size;
2114  pj_str_t cid_value;
2115 
2116  /* '<' + '@' + '>' = 3. pj_str_t does not require a null-terminator */
2117  alloc_size = sizeof(id) + pj_strlen(&sub->uri->host) + 3;
2118  cid_value.ptr = pj_pool_alloc(pool, alloc_size);
2119  cid_value.slen = sprintf(cid_value.ptr, "<%s@%.*s>",
2120  ast_generate_random_string(id, sizeof(id)),
2121  (int) pj_strlen(&sub->uri->host), pj_strbuf(&sub->uri->host));
2122  cid = pjsip_generic_string_hdr_create(pool, &cid_name, &cid_value);
2123 
2124  return cid;
2125 }
2126 
2127 static int rlmi_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size)
2128 {
2129  int num_printed;
2130  pj_xml_node *rlmi = msg_body->data;
2131 
2132  num_printed = pj_xml_print(rlmi, buf, size, PJ_TRUE);
2133  if (num_printed <= AST_PJSIP_XML_PROLOG_LEN) {
2134  return -1;
2135  }
2136 
2137  return num_printed;
2138 }
2139 
2140 static void *rlmi_clone_data(pj_pool_t *pool, const void *data, unsigned len)
2141 {
2142  const pj_xml_node *rlmi = data;
2143 
2144  return pj_xml_clone(pool, rlmi);
2145 }
2146 
2147 /*!
2148  * \brief Create an RLMI body part for a multipart resource list body
2149  *
2150  * RLMI (Resource list meta information) is a special body type that lists
2151  * the subscribed resources and tells subscribers the number of subscribed
2152  * resources and what other body parts are in the multipart body. The
2153  * RLMI body also has a version number that a subscriber can use to ensure
2154  * that the locally-stored state corresponds to server state.
2155  *
2156  * \param pool The allocation pool
2157  * \param sub The subscription representing the subscribed resource list
2158  * \param body_parts A container of body parts that RLMI will refer to
2159  * \param full_state Indicates whether this is a full or partial state notification
2160  * \return The multipart part representing the RLMI body
2161  */
2162 static pjsip_multipart_part *build_rlmi_body(pj_pool_t *pool, struct ast_sip_subscription *sub,
2163  struct body_part_list *body_parts, unsigned int full_state)
2164 {
2165  pj_xml_node *rlmi;
2166  pj_xml_node *name;
2167  pjsip_multipart_part *rlmi_part;
2168  char version_str[32];
2169  char uri[PJSIP_MAX_URL_SIZE];
2170  pjsip_generic_string_hdr *cid;
2171  int i;
2172 
2173  rlmi = ast_sip_presence_xml_create_node(pool, NULL, "list");
2174  ast_sip_presence_xml_create_attr(pool, rlmi, "xmlns", "urn:ietf:params:xml:ns:rlmi");
2175 
2176  ast_sip_subscription_get_local_uri(sub, uri, sizeof(uri));
2177  ast_sip_presence_xml_create_attr(pool, rlmi, "uri", uri);
2178 
2179  snprintf(version_str, sizeof(version_str), "%u", sub->version++);
2180  ast_sip_presence_xml_create_attr(pool, rlmi, "version", version_str);
2181  ast_sip_presence_xml_create_attr(pool, rlmi, "fullState", full_state ? "true" : "false");
2182 
2183  name = ast_sip_presence_xml_create_node(pool, rlmi, "name");
2184  pj_strdup2(pool, &name->content, ast_sip_subscription_get_resource_name(sub));
2185 
2186  for (i = 0; i < AST_VECTOR_SIZE(body_parts); ++i) {
2187  const struct body_part *part = AST_VECTOR_GET(body_parts, i);
2188 
2189  add_rlmi_resource(pool, rlmi, part->cid, part->resource, part->uri, part->state);
2190  }
2191 
2192  rlmi_part = pjsip_multipart_create_part(pool);
2193 
2194  rlmi_part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
2195  pjsip_media_type_cp(pool, &rlmi_part->body->content_type, &rlmi_media_type);
2196 
2197  rlmi_part->body->data = pj_xml_clone(pool, rlmi);
2198  rlmi_part->body->clone_data = rlmi_clone_data;
2199  rlmi_part->body->print_body = rlmi_print_body;
2200 
2201  cid = generate_content_id_hdr(pool, sub);
2202  pj_list_insert_before(&rlmi_part->hdr, cid);
2203 
2204  return rlmi_part;
2205 }
2206 
2207 static pjsip_msg_body *generate_notify_body(pj_pool_t *pool, struct ast_sip_subscription *root,
2208  unsigned int force_full_state);
2209 
2210 /*!
2211  * \brief Destroy a list of body parts
2212  *
2213  * \param parts The container of parts to destroy
2214  */
2215 static void free_body_parts(struct body_part_list *parts)
2216 {
2217  int i;
2218 
2219  for (i = 0; i < AST_VECTOR_SIZE(parts); ++i) {
2220  struct body_part *part = AST_VECTOR_GET(parts, i);
2221  ast_free(part);
2222  }
2223 
2224  AST_VECTOR_FREE(parts);
2225 }
2226 
2227 /*!
2228  * \brief Allocate and initialize a body part structure
2229  *
2230  * \param pool PJLIB allocation pool
2231  * \param sub Subscription representing a subscribed resource
2232  */
2233 static struct body_part *allocate_body_part(pj_pool_t *pool, const struct ast_sip_subscription *sub)
2234 {
2235  struct body_part *bp;
2236 
2237  bp = ast_calloc(1, sizeof(*bp));
2238  if (!bp) {
2239  return NULL;
2240  }
2241 
2242  bp->cid = generate_content_id_hdr(pool, sub);
2243  bp->resource = sub->resource;
2244  bp->state = sub->subscription_state;
2245  bp->uri = sub->uri;
2246 
2247  return bp;
2248 }
2249 
2250 /*!
2251  * \brief Create a multipart body part for a subscribed resource
2252  *
2253  * \param pool PJLIB allocation pool
2254  * \param sub The subscription representing a subscribed resource
2255  * \param parts A vector of parts to append the created part to.
2256  * \param use_full_state Unused locally, but may be passed to other functions
2257  */
2258 static void build_body_part(pj_pool_t *pool, struct ast_sip_subscription *sub,
2259  struct body_part_list *parts, unsigned int use_full_state)
2260 {
2261  struct body_part *bp;
2262  pjsip_msg_body *body;
2263 
2264  bp = allocate_body_part(pool, sub);
2265  if (!bp) {
2266  return;
2267  }
2268 
2269  body = generate_notify_body(pool, sub, use_full_state);
2270  if (!body) {
2271  /* Partial state was requested and the resource has not changed state */
2272  ast_free(bp);
2273  return;
2274  }
2275 
2276  bp->part = pjsip_multipart_create_part(pool);
2277  bp->part->body = body;
2278  pj_list_insert_before(&bp->part->hdr, bp->cid);
2279 
2280  if (AST_VECTOR_APPEND(parts, bp)) {
2281  ast_free(bp);
2282  }
2283 }
2284 
2285 /*!
2286  * \brief Create and initialize the PJSIP multipart body structure for a resource list subscription
2287  *
2288  * \param pool
2289  * \return The multipart message body
2290  */
2291 static pjsip_msg_body *create_multipart_body(pj_pool_t *pool)
2292 {
2293  pjsip_media_type media_type;
2294  pjsip_param *media_type_param;
2295  char boundary[6];
2296  pj_str_t pj_boundary;
2297 
2298  pjsip_media_type_init2(&media_type, "multipart", "related");
2299 
2300  media_type_param = pj_pool_alloc(pool, sizeof(*media_type_param));
2301  pj_list_init(media_type_param);
2302 
2303  pj_strdup2(pool, &media_type_param->name, "type");
2304  pj_strdup2(pool, &media_type_param->value, "\"application/rlmi+xml\"");
2305 
2306  pj_list_insert_before(&media_type.param, media_type_param);
2307 
2308  pj_cstr(&pj_boundary, ast_generate_random_string(boundary, sizeof(boundary)));
2309  return pjsip_multipart_create(pool, &media_type, &pj_boundary);
2310 }
2311 
2312 /*!
2313  * \brief Create a resource list body for NOTIFY requests
2314  *
2315  * Resource list bodies are multipart/related bodies. The first part of the multipart body
2316  * is an RLMI body that describes the rest of the parts to come. The other parts of the body
2317  * convey state of individual subscribed resources.
2318  *
2319  * \param pool PJLIB allocation pool
2320  * \param sub Subscription details from which to generate body
2321  * \param force_full_state If true, ignore resource list settings and send a full state notification
2322  * \return The generated multipart/related body
2323  */
2324 static pjsip_msg_body *generate_list_body(pj_pool_t *pool, struct ast_sip_subscription *sub,
2325  unsigned int force_full_state)
2326 {
2327  int i;
2328  pjsip_multipart_part *rlmi_part;
2329  pjsip_msg_body *multipart;
2330  struct body_part_list body_parts;
2331  unsigned int use_full_state = force_full_state ? 1 : sub->full_state;
2332 
2333  if (AST_VECTOR_INIT(&body_parts, AST_VECTOR_SIZE(&sub->children))) {
2334  return NULL;
2335  }
2336 
2337  for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
2338  build_body_part(pool, AST_VECTOR_GET(&sub->children, i), &body_parts, use_full_state);
2339  }
2340 
2341  /* This can happen if issuing partial state and no children of the list have changed state */
2342  if (AST_VECTOR_SIZE(&body_parts) == 0) {
2343  free_body_parts(&body_parts);
2344  return NULL;
2345  }
2346 
2347  multipart = create_multipart_body(pool);
2348 
2349  rlmi_part = build_rlmi_body(pool, sub, &body_parts, use_full_state);
2350  if (!rlmi_part) {
2351  free_body_parts(&body_parts);
2352  return NULL;
2353  }
2354  pjsip_multipart_add_part(pool, multipart, rlmi_part);
2355 
2356  for (i = 0; i < AST_VECTOR_SIZE(&body_parts); ++i) {
2357  pjsip_multipart_add_part(pool, multipart, AST_VECTOR_GET(&body_parts, i)->part);
2358  }
2359 
2360  free_body_parts(&body_parts);
2361  return multipart;
2362 }
2363 
2364 /*!
2365  * \brief Create the body for a NOTIFY request.
2366  *
2367  * \param pool The pool used for allocations
2368  * \param root The root of the subscription tree
2369  * \param force_full_state If true, ignore resource list settings and send a full state notification
2370  */
2371 static pjsip_msg_body *generate_notify_body(pj_pool_t *pool, struct ast_sip_subscription *root,
2372  unsigned int force_full_state)
2373 {
2374  pjsip_msg_body *body;
2375 
2376  if (AST_VECTOR_SIZE(&root->children) == 0) {
2377  if (force_full_state || root->body_changed) {
2378  /* Not a list. We've already generated the body and saved it on the subscription.
2379  * Use that directly.
2380  */
2381  pj_str_t type;
2382  pj_str_t subtype;
2383  pj_str_t text;
2384 
2385  pj_cstr(&type, ast_sip_subscription_get_body_type(root));
2386  pj_cstr(&subtype, ast_sip_subscription_get_body_subtype(root));
2387  pj_cstr(&text, ast_str_buffer(root->body_text));
2388 
2389  body = pjsip_msg_body_create(pool, &type, &subtype, &text);
2390  root->body_changed = 0;
2391  } else {
2392  body = NULL;
2393  }
2394  } else {
2395  body = generate_list_body(pool, root, force_full_state);
2396  }
2397 
2398  return body;
2399 }
2400 
2401 /*!
2402  * \brief Shortcut method to create a Require: eventlist header
2403  */
2404 static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
2405 {
2406  pjsip_require_hdr *require;
2407 
2408  require = pjsip_require_hdr_create(pool);
2409  pj_strdup2(pool, &require->values[0], "eventlist");
2410  require->count = 1;
2411 
2412  return require;
2413 }
2414 
2415 /*!
2416  * \brief Send a NOTIFY request to a subscriber
2417  *
2418  * \pre sub_tree->dlg is locked
2419  *
2420  * \param sub_tree The subscription tree representing the subscription
2421  * \param force_full_state If true, ignore resource list settings and send full resource list state.
2422  * \retval 0 Success
2423  * \retval non-zero Failure
2424  */
2425 static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state)
2426 {
2427  pjsip_evsub *evsub = sub_tree->evsub;
2428  pjsip_tx_data *tdata;
2429 
2430  if (ast_shutdown_final()
2431  && sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED
2432  && sub_tree->persistence) {
2433  return 0;
2434  }
2435 
2436  if (pjsip_evsub_notify(evsub, sub_tree->root->subscription_state,
2437  NULL, NULL, &tdata) != PJ_SUCCESS) {
2438  return -1;
2439  }
2440 
2441  tdata->msg->body = generate_notify_body(tdata->pool, sub_tree->root, force_full_state);
2442  if (!tdata->msg->body) {
2443  pjsip_tx_data_dec_ref(tdata);
2444  return -1;
2445  }
2446 
2447  if (sub_tree->is_list) {
2448  pjsip_require_hdr *require = create_require_eventlist(tdata->pool);
2449  pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
2450  }
2451 
2452  if (sip_subscription_send_request(sub_tree, tdata)) {
2453  /* do not call pjsip_tx_data_dec_ref(tdata). The pjsip_dlg_send_request deletes the message on error */
2454  return -1;
2455  }
2456 
2457  sub_tree->send_scheduled_notify = 0;
2458 
2459  return 0;
2460 }
2461 
2462 static int serialized_send_notify(void *userdata)
2463 {
2464  struct sip_subscription_tree *sub_tree = userdata;
2465  pjsip_dialog *dlg = sub_tree->dlg;
2466 
2467  pjsip_dlg_inc_lock(dlg);
2468 
2469  /* It's possible that between when the notification was scheduled
2470  * and now a new SUBSCRIBE arrived requiring full state to be
2471  * sent out in an immediate NOTIFY. It's also possible that we're
2472  * already processing a terminate. If that has happened, we need to
2473  * bail out here instead of sending the batched NOTIFY.
2474  */
2475 
2476  if (sub_tree->state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS
2477  || !sub_tree->send_scheduled_notify) {
2478  pjsip_dlg_dec_lock(dlg);
2479  ao2_cleanup(sub_tree);
2480  return 0;
2481  }
2482 
2483  if (sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED) {
2485  }
2486 
2487  send_notify(sub_tree, 0);
2488 
2490  sub_tree->state == SIP_SUB_TREE_TERMINATED
2491  ? "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_STATE_CHANGED",
2492  "Resource: %s", sub_tree->root->resource);
2493 
2494  sub_tree->notify_sched_id = -1;
2495  pjsip_dlg_dec_lock(dlg);
2496  ao2_cleanup(sub_tree);
2497  return 0;
2498 }
2499 
2500 static int sched_cb(const void *data)
2501 {
2502  struct sip_subscription_tree *sub_tree = (struct sip_subscription_tree *) data;
2503 
2504  /* We don't need to bump the refcount of sub_tree since we bumped it when scheduling this task */
2505  if (ast_sip_push_task(sub_tree->serializer, serialized_send_notify, sub_tree)) {
2506  ao2_cleanup(sub_tree);
2507  }
2508 
2509  return 0;
2510 }
2511 
2512 static int schedule_notification(struct sip_subscription_tree *sub_tree)
2513 {
2514  /* There's already a notification scheduled */
2515  if (sub_tree->notify_sched_id > -1) {
2516  return 0;
2517  }
2518 
2519  sub_tree->send_scheduled_notify = 1;
2521  if (sub_tree->notify_sched_id < 0) {
2522  ao2_cleanup(sub_tree);
2523  return -1;
2524  }
2525 
2526  return 0;
2527 }
2528 
2530  int terminate)
2531 {
2532  int res;
2533  pjsip_dialog *dlg = sub->tree->dlg;
2534 
2535  pjsip_dlg_inc_lock(dlg);
2536 
2537  if (sub->tree->state != SIP_SUB_TREE_NORMAL) {
2538  pjsip_dlg_dec_lock(dlg);
2539  return 0;
2540  }
2541 
2544  pjsip_dlg_dec_lock(dlg);
2545  return -1;
2546  }
2547 
2548  sub->body_changed = 1;
2549  if (terminate) {
2550  sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
2551  sub->tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
2552  }
2553 
2554  if (sub->tree->notification_batch_interval) {
2555  res = schedule_notification(sub->tree);
2556  } else {
2557  /* See the note in pubsub_on_rx_refresh() for why sub->tree is refbumped here */
2558  ao2_ref(sub->tree, +1);
2559  if (terminate) {
2560  sub->tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
2561  }
2562  res = send_notify(sub->tree, 0);
2563  ast_test_suite_event_notify(terminate ? "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_STATE_CHANGED",
2564  "Resource: %s",
2565  sub->tree->root->resource);
2566  ao2_ref(sub->tree, -1);
2567  }
2568 
2569  pjsip_dlg_dec_lock(dlg);
2570  return res;
2571 }
2572 
2574 {
2575  return sub->uri;
2576 }
2577 
2579 {
2580  pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, sub->uri, buf, size);
2581 }
2582 
2584 {
2585  pjsip_dialog *dlg;
2586  pjsip_sip_uri *uri;
2587 
2588  dlg = sub->tree->dlg;
2589  uri = pjsip_uri_get_uri(dlg->remote.info->uri);
2590 
2591  if (pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, uri, buf, size) < 0) {
2592  *buf = '\0';
2593  }
2594 }
2595 
2597 {
2598  return sub->resource;
2599 }
2600 
2602 {
2603  return sub->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ? 1 : 0;
2604 }
2605 
2606 static int sip_subscription_accept(struct sip_subscription_tree *sub_tree, pjsip_rx_data *rdata, int response)
2607 {
2608  pjsip_hdr res_hdr;
2609 
2610  /* If this is a persistence recreation the subscription has already been accepted */
2611  if (ast_sip_mod_data_get(rdata->endpt_info.mod_data, pubsub_module.id, MOD_DATA_PERSISTENCE)) {
2612  return 0;
2613  }
2614 
2615  pj_list_init(&res_hdr);
2616  if (sub_tree->is_list) {
2617  /* If subscribing to a list, our response has to have a Require: eventlist header in it */
2618  pj_list_insert_before(&res_hdr, create_require_eventlist(rdata->tp_info.pool));
2619  }
2620 
2621  return pjsip_evsub_accept(sub_tree->evsub, rdata, response, &res_hdr) == PJ_SUCCESS ? 0 : -1;
2622 }
2623 
2625 {
2627 }
2628 
2629 int ast_sip_subscription_add_datastore(struct ast_sip_subscription *subscription, struct ast_datastore *datastore)
2630 {
2631  return ast_datastores_add(subscription->datastores, datastore);
2632 }
2633 
2635 {
2636  return ast_datastores_find(subscription->datastores, name);
2637 }
2638 
2639 void ast_sip_subscription_remove_datastore(struct ast_sip_subscription *subscription, const char *name)
2640 {
2641  ast_datastores_remove(subscription->datastores, name);
2642 }
2643 
2645 {
2646  return subscription->datastores;
2647 }
2648 
2649 int ast_sip_publication_add_datastore(struct ast_sip_publication *publication, struct ast_datastore *datastore)
2650 {
2651  return ast_datastores_add(publication->datastores, datastore);
2652 }
2653 
2655 {
2656  return ast_datastores_find(publication->datastores, name);
2657 }
2658 
2659 void ast_sip_publication_remove_datastore(struct ast_sip_publication *publication, const char *name)
2660 {
2661  ast_datastores_remove(publication->datastores, name);
2662 }
2663 
2665 {
2666  return publication->datastores;
2667 }
2668 
2669 void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data)
2670 {
2671  ast_json_unref(subscription->persistence_data);
2672  subscription->persistence_data = persistence_data;
2673 
2674  if (subscription->tree->persistence) {
2675  if (!subscription->tree->persistence->generator_data) {
2677  if (!subscription->tree->persistence->generator_data) {
2678  return;
2679  }
2680  }
2681  ast_json_object_set(subscription->tree->persistence->generator_data, subscription->resource,
2682  ast_json_ref(persistence_data));
2683  }
2684 }
2685 
2687 {
2688  return subscription->persistence_data;
2689 }
2690 
2692 
2693 static int publication_hash_fn(const void *obj, const int flags)
2694 {
2695  const struct ast_sip_publication *publication = obj;
2696  const int *entity_tag = obj;
2697 
2698  return flags & OBJ_KEY ? *entity_tag : publication->entity_tag;
2699 }
2700 
2701 static int publication_cmp_fn(void *obj, void *arg, int flags)
2702 {
2703  const struct ast_sip_publication *publication1 = obj;
2704  const struct ast_sip_publication *publication2 = arg;
2705  const int *entity_tag = arg;
2706 
2707  return (publication1->entity_tag == (flags & OBJ_KEY ? *entity_tag : publication2->entity_tag) ?
2708  CMP_MATCH | CMP_STOP : 0);
2709 }
2710 
2712 {
2716 }
2717 
2719 {
2720  if (ast_strlen_zero(handler->event_name)) {
2721  ast_log(LOG_ERROR, "No event package specified for publish handler. Cannot register\n");
2722  return -1;
2723  }
2724 
2727  if (!handler->publications) {
2728  ast_log(LOG_ERROR, "Could not allocate publications container for event '%s'\n",
2729  handler->event_name);
2730  return -1;
2731  }
2732 
2734 
2735  return 0;
2736 }
2737 
2739 {
2740  struct ast_sip_publish_handler *iter;
2741 
2744  if (handler == iter) {
2746  ao2_cleanup(handler->publications);
2747  break;
2748  }
2749  }
2752 }
2753 
2755 
2757 {
2761 }
2762 
2764 {
2765  struct ast_sip_subscription_handler *iter;
2766 
2769  if (!strcmp(iter->event_name, event_name)) {
2770  break;
2771  }
2772  }
2774  return iter;
2775 }
2776 
2778 {
2779  pj_str_t event;
2780  pj_str_t accept[AST_SIP_MAX_ACCEPT] = { {0, }, };
2781  struct ast_sip_subscription_handler *existing;
2782  int i = 0;
2783 
2784  if (ast_strlen_zero(handler->event_name)) {
2785  ast_log(LOG_ERROR, "No event package specified for subscription handler. Cannot register\n");
2786  return -1;
2787  }
2788 
2789  existing = find_sub_handler_for_event_name(handler->event_name);
2790  if (existing) {
2792  "Unable to register subscription handler for event %s. A handler is already registered\n",
2793  handler->event_name);
2794  return -1;
2795  }
2796 
2797  for (i = 0; i < AST_SIP_MAX_ACCEPT && !ast_strlen_zero(handler->accept[i]); ++i) {
2798  pj_cstr(&accept[i], handler->accept[i]);
2799  }
2800 
2801  pj_cstr(&event, handler->event_name);
2802 
2803  pjsip_evsub_register_pkg(&pubsub_module, &event, DEFAULT_EXPIRES, i, accept);
2804 
2806 
2807  return 0;
2808 }
2809 
2811 {
2812  struct ast_sip_subscription_handler *iter;
2813 
2816  if (handler == iter) {
2818  break;
2819  }
2820  }
2823 }
2824 
2826 {
2828 
2830  if (!strcmp(gen->type, type)
2831  && !strcmp(gen->subtype, subtype)) {
2832  break;
2833  }
2834  }
2835 
2836  return gen;
2837 }
2838 
2840 {
2842 
2846  return gen;
2847 }
2848 
2850 {
2851  char *accept_copy = ast_strdupa(accept);
2852  char *subtype = accept_copy;
2853  char *type = strsep(&subtype, "/");
2854 
2856  return NULL;
2857  }
2858 
2860 }
2861 
2863  size_t num_accept, const char *body_type)
2864 {
2865  int i;
2866  struct ast_sip_pubsub_body_generator *generator = NULL;
2867 
2868  for (i = 0; i < num_accept; ++i) {
2869  generator = find_body_generator_accept(accept[i]);
2870  if (generator) {
2871  ast_debug(3, "Body generator %p found for accept type %s\n", generator, accept[i]);
2872  if (strcmp(generator->body_type, body_type)) {
2873  ast_log(LOG_WARNING, "Body generator '%s/%s'(%p) does not accept the type of data this event generates\n",
2874  generator->type, generator->subtype, generator);
2875  generator = NULL;
2876  continue;
2877  }
2878  break;
2879  } else {
2880  ast_debug(3, "No body generator found for accept type %s\n", accept[i]);
2881  }
2882  }
2883 
2884  return generator;
2885 }
2886 
2888 {
2889  void *notify_data;
2890  int res;
2891  struct ast_sip_body_data data = {
2892  .body_type = sub->handler->body_type,
2893  };
2894 
2895  if (AST_VECTOR_SIZE(&sub->children) > 0) {
2896  int i;
2897 
2898  for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
2899  if (generate_initial_notify(AST_VECTOR_GET(&sub->children, i))) {
2900  return -1;
2901  }
2902  }
2903 
2904  return 0;
2905  }
2906 
2907  /* We notify subscription establishment only on the tree leaves. */
2908  if (sub->handler->notifier->subscription_established(sub)) {
2909  return -1;
2910  }
2911 
2912  notify_data = sub->handler->notifier->get_notify_data(sub);
2913  if (!notify_data) {
2914  return -1;
2915  }
2916 
2917  data.body_data = notify_data;
2918 
2920  ast_sip_subscription_get_body_subtype(sub), &data, &sub->body_text);
2921 
2923 
2924  return res;
2925 }
2926 
2927 static int pubsub_on_refresh_timeout(void *userdata);
2928 
2929 static int initial_notify_task(void * obj)
2930 {
2931  struct initial_notify_data *ind = obj;
2932 
2933  if (generate_initial_notify(ind->sub_tree->root)) {
2934  pjsip_evsub_terminate(ind->sub_tree->evsub, PJ_TRUE);
2935  } else {
2936  send_notify(ind->sub_tree, 1);
2937  ast_test_suite_event_notify("SUBSCRIPTION_ESTABLISHED",
2938  "Resource: %s",
2939  ind->sub_tree->root->resource);
2940  }
2941 
2942  if (ind->expires != PJSIP_EXPIRES_NOT_SPECIFIED) {
2943  char *name = ast_alloca(strlen("->/ ") +
2944  strlen(ind->sub_tree->persistence->endpoint) +
2945  strlen(ind->sub_tree->root->resource) +
2946  strlen(ind->sub_tree->root->handler->event_name) +
2947  ind->sub_tree->dlg->call_id->id.slen + 1);
2948 
2949  sprintf(name, "%s->%s/%s %.*s", ind->sub_tree->persistence->endpoint,
2951  (int)ind->sub_tree->dlg->call_id->id.slen, ind->sub_tree->dlg->call_id->id.ptr);
2952 
2953  ast_debug(3, "Scheduling timer: %s\n", name);
2955  ind->expires * 1000, pubsub_on_refresh_timeout, name,
2957  if (!ind->sub_tree->expiration_task) {
2958  ast_log(LOG_ERROR, "Unable to create expiration timer of %d seconds for %s\n",
2959  ind->expires, name);
2960  }
2961  }
2962 
2963  ao2_ref(ind->sub_tree, -1);
2964  ast_free(ind);
2965 
2966  return 0;
2967 }
2968 
2969 static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
2970 {
2971  pjsip_expires_hdr *expires_header;
2973  RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
2974  struct sip_subscription_tree *sub_tree;
2975  struct ast_sip_pubsub_body_generator *generator;
2976  char *resource;
2977  pjsip_uri *request_uri;
2978  pjsip_sip_uri *request_uri_sip;
2979  size_t resource_size;
2980  int resp;
2981  struct resource_tree tree;
2982  pj_status_t dlg_status;
2983 
2984  endpoint = ast_pjsip_rdata_get_endpoint(rdata);
2985  ast_assert(endpoint != NULL);
2986 
2987  if (!endpoint->subscription.allow) {
2988  ast_log(LOG_WARNING, "Subscriptions not permitted for endpoint %s.\n", ast_sorcery_object_get_id(endpoint));
2989  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 603, NULL, NULL, NULL);
2990  return PJ_TRUE;
2991  }
2992 
2993  request_uri = rdata->msg_info.msg->line.req.uri;
2994 
2995  if (!PJSIP_URI_SCHEME_IS_SIP(request_uri) && !PJSIP_URI_SCHEME_IS_SIPS(request_uri)) {
2996  char uri_str[PJSIP_MAX_URL_SIZE];
2997 
2998  pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str, sizeof(uri_str));
2999  ast_log(LOG_WARNING, "Request URI '%s' is not a sip: or sips: URI.\n", uri_str);
3000  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
3001  return PJ_TRUE;
3002  }
3003 
3004  request_uri_sip = pjsip_uri_get_uri(request_uri);
3005  resource_size = pj_strlen(&request_uri_sip->user) + 1;
3006  resource = ast_alloca(resource_size);
3007  ast_copy_pj_str(resource, &request_uri_sip->user, resource_size);
3008 
3009  /*
3010  * We may want to match without any user options getting
3011  * in the way.
3012  */
3014 
3015  expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, rdata->msg_info.msg->hdr.next);
3016  if (expires_header) {
3017  if (expires_header->ivalue == 0) {
3018  ast_debug(1, "Subscription request from endpoint %s rejected. Expiration of 0 is invalid\n",
3019  ast_sorcery_object_get_id(endpoint));
3020  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
3021  return PJ_TRUE;
3022  }
3023  if (expires_header->ivalue < endpoint->subscription.minexpiry) {
3024  ast_log(LOG_WARNING, "Subscription expiration %d is too brief for endpoint %s. Minimum is %u\n",
3025  expires_header->ivalue, ast_sorcery_object_get_id(endpoint), endpoint->subscription.minexpiry);
3026  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 423, NULL, NULL, NULL);
3027  return PJ_TRUE;
3028  }
3029  }
3030 
3032  if (!handler) {
3033  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3034  return PJ_TRUE;
3035  }
3036 
3037  generator = subscription_get_generator_from_rdata(rdata, handler);
3038  if (!generator) {
3039  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3040  return PJ_TRUE;
3041  }
3042 
3043  memset(&tree, 0, sizeof(tree));
3044  resp = build_resource_tree(endpoint, handler, resource, &tree,
3046  if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
3047  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
3048  resource_tree_destroy(&tree);
3049  return PJ_TRUE;
3050  }
3051 
3052  sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status, NULL);
3053  if (!sub_tree) {
3054  if (dlg_status != PJ_EEXISTS) {
3055  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
3056  }
3057  } else {
3058  struct initial_notify_data *ind = ast_malloc(sizeof(*ind));
3059 
3060  if (!ind) {
3061  pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
3062  resource_tree_destroy(&tree);
3063  return PJ_TRUE;
3064  }
3065 
3066  ind->sub_tree = ao2_bump(sub_tree);
3067  /* Since this is a normal subscribe, pjproject takes care of the timer */
3069 
3072  sip_subscription_accept(sub_tree, rdata, resp);
3074  pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
3075  ao2_ref(sub_tree, -1);
3076  ast_free(ind);
3077  }
3078  }
3079 
3080  resource_tree_destroy(&tree);
3081  return PJ_TRUE;
3082 }
3083 
3085 {
3086  struct ast_sip_publish_handler *iter = NULL;
3087 
3090  if (strcmp(event, iter->event_name)) {
3091  ast_debug(3, "Event %s does not match %s\n", event, iter->event_name);
3092  continue;
3093  }
3094  ast_debug(3, "Event name match: %s = %s\n", event, iter->event_name);
3095  break;
3096  }
3098 
3099  return iter;
3100 }
3101 
3102 static enum sip_publish_type determine_sip_publish_type(pjsip_rx_data *rdata,
3103  pjsip_generic_string_hdr *etag_hdr, unsigned int *expires, int *entity_id)
3104 {
3105  pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
3106 
3107  if (etag_hdr) {
3108  char etag[pj_strlen(&etag_hdr->hvalue) + 1];
3109 
3110  ast_copy_pj_str(etag, &etag_hdr->hvalue, sizeof(etag));
3111 
3112  if (sscanf(etag, "%30d", entity_id) != 1) {
3113  return SIP_PUBLISH_UNKNOWN;
3114  }
3115  }
3116 
3117  *expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
3118 
3119  if (!(*expires)) {
3120  return SIP_PUBLISH_REMOVE;
3121  } else if (!etag_hdr && rdata->msg_info.msg->body) {
3122  return SIP_PUBLISH_INITIAL;
3123  } else if (etag_hdr && !rdata->msg_info.msg->body) {
3124  return SIP_PUBLISH_REFRESH;
3125  } else if (etag_hdr && rdata->msg_info.msg->body) {
3126  return SIP_PUBLISH_MODIFY;
3127  }
3128 
3129  return SIP_PUBLISH_UNKNOWN;
3130 }
3131 
3132 /*! \brief Internal destructor for publications */
3133 static void publication_destroy_fn(void *obj)
3134 {
3135  struct ast_sip_publication *publication = obj;
3136 
3137  ast_debug(3, "Destroying SIP publication\n");
3138 
3139  ao2_cleanup(publication->datastores);
3140  ao2_cleanup(publication->endpoint);
3141 
3143 }
3144 
3145 static struct ast_sip_publication *sip_create_publication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata,
3146  const char *resource, const char *event_configuration_name)
3147 {
3148  struct ast_sip_publication *publication;
3149  pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
3150  size_t resource_len = strlen(resource) + 1, event_configuration_name_len = strlen(event_configuration_name) + 1;
3151  char *dst;
3152 
3153  ast_assert(endpoint != NULL);
3154 
3155  if (!(publication = ao2_alloc(sizeof(*publication) + resource_len + event_configuration_name_len, publication_destroy_fn))) {
3156  return NULL;
3157  }
3158 
3160 
3161  if (!(publication->datastores = ast_datastores_alloc())) {
3162  ao2_ref(publication, -1);
3163  return NULL;
3164  }
3165 
3167  ao2_ref(endpoint, +1);
3168  publication->endpoint = endpoint;
3169  publication->expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
3170  publication->sched_id = -1;
3171  dst = publication->data;
3172  publication->resource = strcpy(dst, resource);
3173  dst += resource_len;
3174  publication->event_configuration_name = strcpy(dst, event_configuration_name);
3175 
3176  return publication;
3177 }
3178 
3179 static int sip_publication_respond(struct ast_sip_publication *pub, int status_code,
3180  pjsip_rx_data *rdata)
3181 {
3182  pjsip_tx_data *tdata;
3183  pjsip_transaction *tsx;
3184 
3185  if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, status_code, NULL, &tdata) != PJ_SUCCESS) {
3186  return -1;
3187  }
3188 
3189  if (PJSIP_IS_STATUS_IN_CLASS(status_code, 200)) {
3190  char buf[30];
3191 
3192  snprintf(buf, sizeof(buf), "%d", pub->entity_tag);
3193  ast_sip_add_header(tdata, "SIP-ETag", buf);
3194 
3195  snprintf(buf, sizeof(buf), "%d", pub->expires);
3196  ast_sip_add_header(tdata, "Expires", buf);
3197  }
3198 
3199  if (pjsip_tsx_create_uas(&pubsub_module, rdata, &tsx) != PJ_SUCCESS) {
3200  pjsip_tx_data_dec_ref(tdata);
3201  return -1;
3202  }
3203 
3204  pjsip_tsx_recv_msg(tsx, rdata);
3205 
3206  if (pjsip_tsx_send_msg(tsx, tdata) != PJ_SUCCESS) {
3207  pjsip_tx_data_dec_ref(tdata);
3208  return -1;
3209  }
3210 
3211  return 0;
3212 }
3213 
3214 static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata,
3216 {
3217  struct ast_sip_publication *publication;
3218  char *resource_name;
3219  size_t resource_size;
3221  struct ast_variable *event_configuration_name = NULL;
3222  pjsip_uri *request_uri;
3223  pjsip_sip_uri *request_uri_sip;
3224  int resp;
3225 
3226  request_uri = rdata->msg_info.msg->line.req.uri;
3227 
3228  if (!PJSIP_URI_SCHEME_IS_SIP(request_uri) && !PJSIP_URI_SCHEME_IS_SIPS(request_uri)) {
3229  char uri_str[PJSIP_MAX_URL_SIZE];
3230 
3231  pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str, sizeof(uri_str));
3232  ast_log(LOG_WARNING, "Request URI '%s' is not a sip: or sips: URI.\n", uri_str);
3233  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
3234  return NULL;
3235  }
3236 
3237  request_uri_sip = pjsip_uri_get_uri(request_uri);
3238  resource_size = pj_strlen(&request_uri_sip->user) + 1;
3239  resource_name = ast_alloca(resource_size);
3240  ast_copy_pj_str(resource_name, &request_uri_sip->user, resource_size);
3241 
3242  /*
3243  * We may want to match without any user options getting
3244  * in the way.
3245  */
3246  AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(resource_name);
3247 
3248  resource = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "inbound-publication", resource_name);
3249  if (!resource) {
3250  ast_debug(1, "No 'inbound-publication' defined for resource '%s'\n", resource_name);
3251  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
3252  return NULL;
3253  }
3254 
3255  if (!ast_strlen_zero(resource->endpoint) && strcmp(resource->endpoint, ast_sorcery_object_get_id(endpoint))) {
3256  ast_debug(1, "Resource %s has a defined endpoint '%s', but does not match endpoint '%s' that received the request\n",
3257  resource_name, resource->endpoint, ast_sorcery_object_get_id(endpoint));
3258  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
3259  return NULL;
3260  }
3261 
3262  for (event_configuration_name = resource->events; event_configuration_name; event_configuration_name = event_configuration_name->next) {
3263  if (!strcmp(event_configuration_name->name, handler->event_name)) {
3264  break;
3265  }
3266  }
3267 
3268  if (!event_configuration_name) {
3269  ast_debug(1, "Event '%s' is not configured for '%s'\n", handler->event_name, resource_name);
3270  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
3271  return NULL;
3272  }
3273 
3274  resp = handler->new_publication(endpoint, resource_name, event_configuration_name->value);
3275 
3276  if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
3277  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
3278  return NULL;
3279  }
3280 
3281  publication = sip_create_publication(endpoint, rdata, S_OR(resource_name, ""), event_configuration_name->value);
3282 
3283  if (!publication) {
3284  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 503, NULL, NULL, NULL);
3285  return NULL;
3286  }
3287 
3288  publication->handler = handler;
3289  if (publication->handler->publication_state_change(publication, rdata->msg_info.msg->body,
3291  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
3292  ao2_cleanup(publication);
3293  return NULL;
3294  }
3295 
3296  sip_publication_respond(publication, resp, rdata);
3297 
3298  return publication;
3299 }
3300 
3301 static int publish_expire_callback(void *data)
3302 {
3303  RAII_VAR(struct ast_sip_publication *, publication, data, ao2_cleanup);
3304 
3305  if (publication->handler->publish_expire) {
3306  publication->handler->publish_expire(publication);
3307  }
3308 
3309  return 0;
3310 }
3311 
3312 static int publish_expire(const void *data)
3313 {
3314  struct ast_sip_publication *publication = (struct ast_sip_publication*)data;
3315 
3316  ao2_unlink(publication->handler->publications, publication);
3317  publication->sched_id = -1;
3318 
3319  if (ast_sip_push_task(NULL, publish_expire_callback, publication)) {
3320  ao2_cleanup(publication);
3321  }
3322 
3323  return 0;
3324 }
3325 
3326 static pj_bool_t pubsub_on_rx_publish_request(pjsip_rx_data *rdata)
3327 {
3328  pjsip_event_hdr *event_header;
3330  RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
3331  char event[32];
3332  static const pj_str_t str_sip_if_match = { "SIP-If-Match", 12 };
3333  pjsip_generic_string_hdr *etag_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_sip_if_match, NULL);
3334  enum sip_publish_type publish_type;
3335  RAII_VAR(struct ast_sip_publication *, publication, NULL, ao2_cleanup);
3336  unsigned int expires = 0;
3337  int entity_id, response = 0;
3338 
3339  endpoint = ast_pjsip_rdata_get_endpoint(rdata);
3340  ast_assert(endpoint != NULL);
3341 
3342  event_header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_event_name, rdata->msg_info.msg->hdr.next);
3343  if (!event_header) {
3344  ast_log(LOG_WARNING, "Incoming PUBLISH request from %s with no Event header\n",
3345  ast_sorcery_object_get_id(endpoint));
3346  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3347  return PJ_TRUE;
3348  }
3349  ast_copy_pj_str(event, &event_header->event_type, sizeof(event));
3350 
3352  if (!handler) {
3353  ast_log(LOG_WARNING, "No registered publish handler for event %s from %s\n", event,
3354  ast_sorcery_object_get_id(endpoint));
3355  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3356  return PJ_TRUE;
3357  }
3358 
3359  publish_type = determine_sip_publish_type(rdata, etag_hdr, &expires, &entity_id);
3360 
3361  /* If this is not an initial publish ensure that a publication is present */
3362  if ((publish_type != SIP_PUBLISH_INITIAL) && (publish_type != SIP_PUBLISH_UNKNOWN)) {
3363  if (!(publication = ao2_find(handler->publications, &entity_id, OBJ_KEY | OBJ_UNLINK))) {
3364  static const pj_str_t str_conditional_request_failed = { "Conditional Request Failed", 26 };
3365 
3366  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 412, &str_conditional_request_failed,
3367  NULL, NULL);
3368  return PJ_TRUE;
3369  }
3370 
3371  /* Per the RFC every response has to have a new entity tag */
3372  publication->entity_tag = ast_atomic_fetchadd_int(&esc_etag_counter, +1);
3373 
3374  /* Update the expires here so that the created responses will contain the correct value */
3375  publication->expires = expires;
3376  }
3377 
3378  switch (publish_type) {
3379  case SIP_PUBLISH_INITIAL:
3380  publication = publish_request_initial(endpoint, rdata, handler);
3381  break;
3382  case SIP_PUBLISH_REFRESH:
3383  case SIP_PUBLISH_MODIFY:
3384  if (handler->publication_state_change(publication, rdata->msg_info.msg->body,
3386  /* If an error occurs we want to terminate the publication */
3387  expires = 0;
3388  }
3389  response = 200;
3390  break;
3391  case SIP_PUBLISH_REMOVE:
3392  handler->publication_state_change(publication, rdata->msg_info.msg->body,
3394  response = 200;
3395  break;
3396  case SIP_PUBLISH_UNKNOWN:
3397  default:
3398  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
3399  break;
3400  }
3401 
3402  if (publication) {
3403  if (expires) {
3404  ao2_link(handler->publications, publication);
3405 
3406  AST_SCHED_REPLACE_UNREF(publication->sched_id, sched, expires * 1000, publish_expire, publication,
3407  ao2_ref(_data, -1), ao2_ref(publication, -1), ao2_ref(publication, +1));
3408  } else {
3409  AST_SCHED_DEL_UNREF(sched, publication->sched_id, ao2_ref(publication, -1));
3410  }
3411  }
3412 
3413  if (response) {
3414  sip_publication_respond(publication, response, rdata);
3415  }
3416 
3417  return PJ_TRUE;
3418 }
3419 
3421 {
3422  return pub->endpoint;
3423 }
3424 
3426 {
3427  return pub->resource;
3428 }
3429 
3431 {
3432  return pub->event_configuration_name;
3433 }
3434 
3435 int ast_sip_pubsub_is_body_generator_registered(const char *type, const char *subtype)
3436 {
3437  return !!find_body_generator_type_subtype(type, subtype);
3438 }
3439 
3441 {
3442  struct ast_sip_pubsub_body_generator *existing;
3443  pj_str_t accept;
3444  pj_size_t accept_len;
3445 
3447  existing = find_body_generator_type_subtype_nolock(generator->type, generator->subtype);
3448  if (existing) {
3450  ast_log(LOG_WARNING, "A body generator for %s/%s is already registered.\n",
3451  generator->type, generator->subtype);
3452  return -1;
3453  }
3456 
3457  /* Lengths of type and subtype plus a slash. */
3458  accept_len = strlen(generator->type) + strlen(generator->subtype) + 1;
3459 
3460  /* Add room for null terminator that sprintf() will set. */
3461  pj_strset(&accept, ast_alloca(accept_len + 1), accept_len);
3462  sprintf((char *) pj_strbuf(&accept), "%s/%s", generator->type, generator->subtype);/* Safe */
3463 
3464  pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), &pubsub_module,
3465  PJSIP_H_ACCEPT, NULL, 1, &accept);
3466 
3467  return 0;
3468 }
3469 
3471 {
3472  struct ast_sip_pubsub_body_generator *iter;
3473 
3476  if (iter == generator) {
3478  break;
3479  }
3480  }
3483 }
3484 
3486 {
3490 
3491  return 0;
3492 }
3493 
3495 {
3496  struct ast_sip_pubsub_body_supplement *iter;
3497 
3500  if (iter == supplement) {
3502  break;
3503  }
3504  }
3507 }
3508 
3510 {
3511  return sub->body_generator->type;
3512 }
3513 
3515 {
3516  return sub->body_generator->subtype;
3517 }
3518 
3520  struct ast_sip_body_data *data, struct ast_str **str)
3521 {
3522  struct ast_sip_pubsub_body_supplement *supplement;
3523  struct ast_sip_pubsub_body_generator *generator;
3524  int res = 0;
3525  void *body;
3526 
3528  if (!generator) {
3529  ast_log(LOG_WARNING, "Unable to find a body generator for %s/%s\n",
3530  type, subtype);
3531  return -1;
3532  }
3533 
3534  if (strcmp(data->body_type, generator->body_type)) {
3535  ast_log(LOG_WARNING, "%s/%s body generator does not accept the type of data provided\n",
3536  type, subtype);
3537  return -1;
3538  }
3539 
3540  body = generator->allocate_body(data->body_data);
3541  if (!body) {
3542  ast_log(LOG_WARNING, "%s/%s body generator could not to allocate a body\n",
3543  type, subtype);
3544  return -1;
3545  }
3546 
3547  if (generator->generate_body_content(body, data->body_data)) {
3548  res = -1;
3549  goto end;
3550  }
3551 
3553  AST_RWLIST_TRAVERSE(&body_supplements, supplement, list) {
3554  if (!strcmp(generator->type, supplement->type) &&
3555  !strcmp(generator->subtype, supplement->subtype)) {
3556  res = supplement->supplement_body(body, data->body_data);
3557  if (res) {
3558  break;
3559  }
3560  }
3561  }
3563 
3564  if (!res) {
3565  generator->to_string(body, str);
3566  }
3567 
3568 end:
3569  if (generator->destroy_body) {
3570  generator->destroy_body(body);
3571  }
3572 
3573  return res;
3574 }
3575 
3582  char message_account[PJSIP_MAX_URL_SIZE];
3583 };
3584 
3585 static int parse_simple_message_summary(char *body,
3586  struct simple_message_summary *summary)
3587 {
3588  char *line;
3589  char *buffer;
3590  int found_counts = 0;
3591 
3592  if (ast_strlen_zero(body) || !summary) {
3593  return -1;
3594  }
3595 
3596  buffer = ast_strdupa(body);
3597  memset(summary, 0, sizeof(*summary));
3598 
3599  while ((line = ast_read_line_from_buffer(&buffer))) {
3600  line = ast_str_to_lower(line);
3601 
3602  if (sscanf(line, "voice-message: %d/%d (%d/%d)",
3603  &summary->voice_messages_new, &summary->voice_messages_old,
3604  &summary->voice_messages_urgent_new, &summary->voice_messages_urgent_old)) {
3605  found_counts = 1;
3606  } else {
3607  sscanf(line, "message-account: %s", summary->message_account);
3608  }
3609  }
3610 
3611  return !found_counts;
3612 }
3613 
3614 static pj_bool_t pubsub_on_rx_mwi_notify_request(pjsip_rx_data *rdata)
3615 {
3616  RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
3617  struct simple_message_summary summary;
3618  const char *endpoint_name;
3619  char *atsign;
3620  char *context;
3621  char *body;
3622  char *mailbox;
3623  int rc;
3624 
3625  endpoint = ast_pjsip_rdata_get_endpoint(rdata);
3626  if (!endpoint) {
3627  ast_debug(1, "Incoming MWI: Endpoint not found in rdata (%p)\n", rdata);
3628  rc = 404;
3629  goto error;
3630  }
3631 
3632  endpoint_name = ast_sorcery_object_get_id(endpoint);
3633  ast_debug(1, "Incoming MWI: Found endpoint: %s\n", endpoint_name);
3634  if (ast_strlen_zero(endpoint->incoming_mwi_mailbox)) {
3635  ast_debug(1, "Incoming MWI: No incoming mailbox specified for endpoint '%s'\n", endpoint_name);
3636  ast_test_suite_event_notify("PUBSUB_NO_INCOMING_MWI_MAILBOX",
3637  "Endpoint: %s", endpoint_name);
3638  rc = 404;
3639  goto error;
3640  }
3641 
3642  mailbox = ast_strdupa(endpoint->incoming_mwi_mailbox);
3643  atsign = strchr(mailbox, '@');
3644  if (!atsign) {
3645  ast_debug(1, "Incoming MWI: No '@' found in endpoint %s's incoming mailbox '%s'. Can't parse context\n",
3646  endpoint_name, endpoint->incoming_mwi_mailbox);
3647  rc = 404;
3648  goto error;
3649  }
3650 
3651  *atsign = '\0';
3652  context = atsign + 1;
3653 
3654  body = ast_alloca(rdata->msg_info.msg->body->len + 1);
3655  rdata->msg_info.msg->body->print_body(rdata->msg_info.msg->body, body,
3656  rdata->msg_info.msg->body->len + 1);
3657 
3658  if (parse_simple_message_summary(body, &summary) != 0) {
3659  ast_debug(1, "Incoming MWI: Endpoint: '%s' There was an issue getting message info from body '%s'\n",
3660  ast_sorcery_object_get_id(endpoint), body);
3661  rc = 404;
3662  goto error;
3663  }
3664 
3666  summary.voice_messages_new, summary.voice_messages_old)) {
3667  ast_log(LOG_ERROR, "Incoming MWI: Endpoint: '%s' Could not publish MWI to stasis. "
3668  "Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
3669  endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
3670  summary.voice_messages_new, summary.voice_messages_old,
3672  rc = 404;
3673  } else {
3674  ast_debug(1, "Incoming MWI: Endpoint: '%s' Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
3675  endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
3676  summary.voice_messages_new, summary.voice_messages_old,
3678  ast_test_suite_event_notify("PUBSUB_INCOMING_MWI_PUBLISH",
3679  "Endpoint: %s\r\n"
3680  "Mailbox: %s\r\n"
3681  "MessageAccount: %s\r\n"
3682  "VoiceMessagesNew: %d\r\n"
3683  "VoiceMessagesOld: %d\r\n"
3684  "VoiceMessagesUrgentNew: %d\r\n"
3685  "VoiceMessagesUrgentOld: %d",
3686  endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
3687  summary.voice_messages_new, summary.voice_messages_old,
3689  rc = 200;
3690  }
3691 
3692 error:
3693  pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, rc, NULL, NULL, NULL);
3694  return PJ_TRUE;
3695 }
3696 
3697 static pj_bool_t pubsub_on_rx_notify_request(pjsip_rx_data *rdata)
3698 {
3699  if (rdata->msg_info.msg->body &&
3700  ast_sip_is_content_type(&rdata->msg_info.msg->body->content_type,
3701  "application", "simple-message-summary")) {
3702  return pubsub_on_rx_mwi_notify_request(rdata);
3703  }
3704  return PJ_FALSE;
3705 }
3706 
3707 static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
3708 {
3709  if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) {
3710  return pubsub_on_rx_subscribe_request(rdata);
3711  } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_publish_method)) {
3712  return pubsub_on_rx_publish_request(rdata);
3713  } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_notify_method)) {
3714  return pubsub_on_rx_notify_request(rdata);
3715  }
3716 
3717  return PJ_FALSE;
3718 }
3719 
3721 {
3722  int i;
3723 
3724  sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
3725  for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
3726  set_state_terminated(AST_VECTOR_GET(&sub->children, i));
3727  }
3728 }
3729 
3730 /*!
3731  * \brief Callback sequence for subscription terminate:
3732  *
3733  * * Client initiated:
3734  * pjproject receives SUBSCRIBE on the subscription's serializer thread
3735  * calls pubsub_on_rx_refresh with dialog locked
3736  * pubsub_on_rx_refresh sets TERMINATE_PENDING
3737  * pushes serialized_pubsub_on_refresh_timeout
3738  * returns to pjproject
3739  * pjproject calls pubsub_on_evsub_state
3740  * pubsub_evsub_set_state checks state == TERMINATE_IN_PROGRESS (no)
3741  * ignore and return
3742  * pjproject unlocks dialog
3743  * serialized_pubsub_on_refresh_timeout starts (1)
3744  * locks dialog
3745  * checks state == TERMINATE_PENDING
3746  * sets TERMINATE_IN_PROGRESS
3747  * calls send_notify (2)
3748  * send_notify ultimately calls pjsip_evsub_send_request
3749  * pjsip_evsub_send_request calls evsub's set_state
3750  * set_state calls pubsub_evsub_set_state
3751  * pubsub_on_evsub_state checks state == TERMINATE_IN_PROGRESS
3752  * removes the subscriptions
3753  * cleans up references to evsub
3754  * sets state = TERMINATED
3755  * serialized_pubsub_on_refresh_timeout unlocks dialog
3756  *
3757  * * Subscription timer expires:
3758  * pjproject timer expires
3759  * locks dialog
3760  * calls pubsub_on_server_timeout
3761  * pubsub_on_server_timeout checks state == NORMAL
3762  * sets TERMINATE_PENDING
3763  * pushes serialized_pubsub_on_refresh_timeout
3764  * returns to pjproject
3765  * pjproject unlocks dialog
3766  * serialized_pubsub_on_refresh_timeout starts
3767  * See (1) Above
3768  *
3769  * * Transmission failure sending NOTIFY or error response from client
3770  * pjproject transaction timer expires or non OK response
3771  * pjproject locks dialog
3772  * calls pubsub_on_evsub_state with event TSX_STATE
3773  * pubsub_on_evsub_state checks event == TSX_STATE
3774  * removes the subscriptions
3775  * cleans up references to evsub
3776  * sets state = TERMINATED
3777  * pjproject unlocks dialog
3778  *
3779  * * ast_sip_subscription_notify is called
3780  * checks state == NORMAL
3781  * if not batched...
3782  * sets TERMINATE_IN_PROGRESS (if terminate is requested)
3783  * calls send_notify
3784  * See (2) Above
3785  * if batched...
3786  * sets TERMINATE_PENDING
3787  * schedules task
3788  * scheduler runs sched_task
3789  * sched_task pushes serialized_send_notify
3790  * serialized_send_notify starts
3791  * checks state <= TERMINATE_PENDING
3792  * if state == TERMINATE_PENDING set state = TERMINATE_IN_PROGRESS
3793  * call send_notify
3794  * See (2) Above
3795  *
3796  */
3797 
3798 /*!
3799  * \brief PJSIP callback when underlying SIP subscription changes state
3800  *
3801  * Although this function is called for every state change, we only care
3802  * about the TERMINATED state, and only when we're actually processing the final
3803  * notify (SIP_SUB_TREE_TERMINATE_IN_PROGRESS) OR when a transmission failure
3804  * occurs (PJSIP_EVENT_TSX_STATE). In this case, we do all the subscription tree
3805  * cleanup tasks and decrement the evsub reference.
3806  */
3807 static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
3808 {
3809  struct sip_subscription_tree *sub_tree =
3810  pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
3811 
3812  ast_debug(3, "evsub %p state %s event %s sub_tree %p sub_tree state %s\n", evsub,
3813  pjsip_evsub_get_state_name(evsub), pjsip_event_str(event->type), sub_tree,
3814  (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN"));
3815 
3816  if (!sub_tree || pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
3817  return;
3818  }
3819 
3820  /* It's easier to write this as what we WANT to process, then negate it. */
3821  if (!(sub_tree->state == SIP_SUB_TREE_TERMINATE_IN_PROGRESS
3822  || (event->type == PJSIP_EVENT_TSX_STATE && sub_tree->state == SIP_SUB_TREE_NORMAL)
3823  )) {
3824  ast_debug(3, "Do nothing.\n");
3825  return;
3826  }
3827 
3828  if (sub_tree->expiration_task) {
3829  char task_name[256];
3830 
3831  ast_sip_sched_task_get_name(sub_tree->expiration_task, task_name, sizeof(task_name));
3832  ast_debug(3, "Cancelling timer: %s\n", task_name);
3834  ao2_cleanup(sub_tree->expiration_task);
3835  sub_tree->expiration_task = NULL;
3836  }
3837 
3838  remove_subscription(sub_tree);
3839 
3840  pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
3841 
3842 #ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
3843  pjsip_evsub_dec_ref(sub_tree->evsub);
3844 #endif
3845 
3846  sub_tree->evsub = NULL;
3847 
3849  ast_sip_dialog_set_endpoint(sub_tree->dlg, NULL);
3850 
3852  shutdown_subscriptions(sub_tree->root);
3853 
3854  sub_tree->state = SIP_SUB_TREE_TERMINATED;
3855  /* Remove evsub's reference to the sub_tree */
3856  ao2_ref(sub_tree, -1);
3857 }
3858 
3859 static int pubsub_on_refresh_timeout(void *userdata)
3860 {
3861  struct sip_subscription_tree *sub_tree = userdata;
3862  pjsip_dialog *dlg = sub_tree->dlg;
3863 
3864  ast_debug(3, "sub_tree %p sub_tree state %s\n", sub_tree,
3865  (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN"));
3866 
3867  pjsip_dlg_inc_lock(dlg);
3868  if (sub_tree->state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS) {
3869  pjsip_dlg_dec_lock(dlg);
3870  return 0;
3871  }
3872 
3873  if (sub_tree->state == SIP_SUB_TREE_TERMINATE_PENDING) {
3875  set_state_terminated(sub_tree->root);
3876  }
3877 
3878  send_notify(sub_tree, 1);
3879 
3880  ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
3881  "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
3882  "Resource: %s", sub_tree->root->resource);
3883 
3884  pjsip_dlg_dec_lock(dlg);
3885 
3886  return 0;
3887 }
3888 
3889 static int serialized_pubsub_on_refresh_timeout(void *userdata)
3890 {
3891  struct sip_subscription_tree *sub_tree = userdata;
3892 
3893  ast_debug(3, "sub_tree %p sub_tree state %s\n", sub_tree,
3894  (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN"));
3895 
3896  pubsub_on_refresh_timeout(userdata);
3897  ao2_cleanup(sub_tree);
3898 
3899  return 0;
3900 }
3901 
3902 /*!
3903  * \brief Called whenever an in-dialog SUBSCRIBE is received
3904  *
3905  * This includes both SUBSCRIBE requests that actually refresh the subscription
3906  * as well as SUBSCRIBE requests that end the subscription.
3907  *
3908  * In either case we push serialized_pubsub_on_refresh_timeout to send an
3909  * appropriate NOTIFY request.
3910  */
3911 static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
3912  int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
3913 {
3914  struct sip_subscription_tree *sub_tree;
3915 
3916  sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
3917  ast_debug(3, "evsub %p sub_tree %p sub_tree state %s\n", evsub, sub_tree,
3918  (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN"));
3919 
3920  if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) {
3921  return;
3922  }
3923 
3924  if (sub_tree->expiration_task) {
3925  char task_name[256];
3926 
3927  ast_sip_sched_task_get_name(sub_tree->expiration_task, task_name, sizeof(task_name));
3928  ast_debug(3, "Cancelling timer: %s\n", task_name);
3930  ao2_cleanup(sub_tree->expiration_task);
3931  sub_tree->expiration_task = NULL;
3932  }
3933 
3934  /* PJSIP will set the evsub's state to terminated before calling into this function
3935  * if the Expires value of the incoming SUBSCRIBE is 0.
3936  */
3937 
3938  if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
3940  }
3941 
3943 
3945  /* If we can't push the NOTIFY refreshing task...we'll just go with it. */
3946  ast_log(LOG_ERROR, "Failed to push task to send NOTIFY.\n");
3947  sub_tree->state = SIP_SUB_TREE_NORMAL;
3948  ao2_ref(sub_tree, -1);
3949  }
3950 
3951  if (sub_tree->is_list) {
3952  pj_list_insert_before(res_hdr, create_require_eventlist(rdata->tp_info.pool));
3953  }
3954 }
3955 
3956 static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code,
3957  pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
3958 {
3959  struct ast_sip_subscription *sub;
3960 
3961  if (!(sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
3962  return;
3963  }
3964 
3965  sub->handler->subscriber->state_change(sub, rdata->msg_info.msg->body,
3966  pjsip_evsub_get_state(evsub));
3967 }
3968 
3969 static int serialized_pubsub_on_client_refresh(void *userdata)
3970 {
3971  struct sip_subscription_tree *sub_tree = userdata;
3972  pjsip_tx_data *tdata;
3973 
3974  if (!sub_tree->evsub) {
3975  ao2_cleanup(sub_tree);
3976  return 0;
3977  }
3978 
3979  if (pjsip_evsub_initiate(sub_tree->evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
3980  pjsip_evsub_send_request(sub_tree->evsub, tdata);
3981  } else {
3982  pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
3983  }
3984 
3985  ao2_cleanup(sub_tree);
3986  return 0;
3987 }
3988 
3989 static void pubsub_on_client_refresh(pjsip_evsub *evsub)
3990 {
3991  struct sip_subscription_tree *sub_tree;
3992 
3993  if (!(sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
3994  return;
3995  }
3996 
3998  ao2_cleanup(sub_tree);
3999  }
4000 }
4001 
4002 static void pubsub_on_server_timeout(pjsip_evsub *evsub)
4003 {
4004  struct sip_subscription_tree *sub_tree;
4005 
4006  /* PJSIP does not terminate the server timeout timer when a SUBSCRIBE
4007  * with Expires: 0 arrives to end a subscription, nor does it terminate
4008  * this timer when we send a NOTIFY request in response to receiving such
4009  * a SUBSCRIBE. PJSIP does not stop the server timeout timer until the
4010  * NOTIFY transaction has finished (either through receiving a response
4011  * or through a transaction timeout).
4012  *
4013  * Therefore, it is possible that we can be told that a server timeout
4014  * occurred after we already thought that the subscription had been
4015  * terminated. In such a case, we will have already removed the sub_tree
4016  * from the evsub's mod_data array.
4017  */
4018 
4019  sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
4020  if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) {
4021  return;
4022  }
4023 
4026  sub_tree->state = SIP_SUB_TREE_NORMAL;
4027  ao2_cleanup(sub_tree);
4028  }
4029 }
4030 
4032  struct ast_sip_ami *ami,
4033  const char *event)
4034 {
4035  struct ast_str *buf;
4036 
4038  if (!buf) {
4039  return -1;
4040  }
4041 
4042  sip_subscription_to_ami(sub_tree, &buf);
4043  astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
4044  ast_free(buf);
4045 
4046  ++ami->count;
4047  return 0;
4048 }
4049 
4050 static int ami_subscription_detail_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4051 {
4052  return sub_tree->role == AST_SIP_NOTIFIER ? ami_subscription_detail(
4053  sub_tree, arg, "InboundSubscriptionDetail") : 0;
4054 }
4055 
4056 static int ami_subscription_detail_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4057 {
4058  return sub_tree->role == AST_SIP_SUBSCRIBER ? ami_subscription_detail(
4059  sub_tree, arg, "OutboundSubscriptionDetail") : 0;
4060 }
4061 
4062 static int ami_show_subscriptions_inbound(struct mansession *s, const struct message *m)
4063 {
4064  struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
4065 
4066  astman_send_listack(s, m, "Following are Events for each inbound Subscription",
4067  "start");
4068 
4070 
4071  astman_send_list_complete_start(s, m, "InboundSubscriptionDetailComplete", ami.count);
4073  return 0;
4074 }
4075 
4076 static int ami_show_subscriptions_outbound(struct mansession *s, const struct message *m)
4077 {
4078  struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
4079 
4080  astman_send_listack(s, m, "Following are Events for each outbound Subscription",
4081  "start");
4082 
4084 
4085  astman_send_list_complete_start(s, m, "OutboundSubscriptionDetailComplete", ami.count);
4087  return 0;
4088 }
4089 
4090 static int format_ami_resource_lists(void *obj, void *arg, int flags)
4091 {
4092  struct resource_list *list = obj;
4093  struct ast_sip_ami *ami = arg;
4094  struct ast_str *buf;
4095 
4096  buf = ast_sip_create_ami_event("ResourceListDetail", ami);
4097  if (!buf) {
4098  return CMP_STOP;
4099  }
4100 
4101  if (ast_sip_sorcery_object_to_ami(list, &buf)) {
4102  ast_free(buf);
4103  return CMP_STOP;
4104  }
4105  astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
4106  ast_free(buf);
4107 
4108  ++ami->count;
4109  return 0;
4110 }
4111 
4112 static int ami_show_resource_lists(struct mansession *s, const struct message *m)
4113 {
4114  struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
4115  struct ao2_container *lists;
4116 
4117  lists = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "resource_list",
4119 
4120  if (!lists || !ao2_container_count(lists)) {
4121  astman_send_error(s, m, "No resource lists found\n");
4122  return 0;
4123  }
4124 
4125  astman_send_listack(s, m, "A listing of resource lists follows, presented as ResourceListDetail events",
4126  "start");
4127 
4129 
4130  astman_send_list_complete_start(s, m, "ResourceListDetailComplete", ami.count);
4132  return 0;
4133 }
4134 
4135 #define AMI_SHOW_SUBSCRIPTIONS_INBOUND "PJSIPShowSubscriptionsInbound"
4136 #define AMI_SHOW_SUBSCRIPTIONS_OUTBOUND "PJSIPShowSubscriptionsOutbound"
4137 
4138 #define MAX_REGEX_ERROR_LEN 128
4139 
4141  /*! CLI handler entry e parameter */
4142  struct ast_cli_entry *e;
4143  /*! CLI handler entry a parameter */
4144  struct ast_cli_args *a;
4145  /*! CLI subscription entry output line(s) */
4146  struct ast_str *buf;
4147  /*! Compiled regular expression to select if buf is written to CLI when not NULL. */
4148  regex_t *like;
4149  int count;
4150 };
4151 
4153  struct ast_cli_args *a;
4154  /*! Found callid for search position */
4155  char *callid;
4156  int wordlen;
4157  int which;
4158 };
4159 
4161 {
4162  pj_str_t *callid;
4163 
4164  if (!sub_tree->dlg) {
4165  return 0;
4166  }
4167 
4168  callid = &sub_tree->dlg->call_id->id;
4169  if (cli->wordlen <= pj_strlen(callid)
4170  && !strncasecmp(cli->a->word, pj_strbuf(callid), cli->wordlen)
4171  && (++cli->which > cli->a->n)) {
4172  cli->callid = ast_malloc(pj_strlen(callid) + 1);
4173  if (cli->callid) {
4174  ast_copy_pj_str(cli->callid, callid, pj_strlen(callid) + 1);
4175  }
4176  return -1;
4177  }
4178  return 0;
4179 }
4180 
4181 static int cli_complete_subscription_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4182 {
4183  return sub_tree->role == AST_SIP_NOTIFIER
4184  ? cli_complete_subscription_common(sub_tree, arg) : 0;
4185 }
4186 
4187 static int cli_complete_subscription_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4188 {
4189  return sub_tree->role == AST_SIP_SUBSCRIBER
4190  ? cli_complete_subscription_common(sub_tree, arg) : 0;
4191 }
4192 
4194 {
4195  struct cli_sub_complete_parms cli;
4196  on_subscription_t on_subscription;
4197 
4198  if (a->pos != 4) {
4199  return NULL;
4200  }
4201 
4202  if (!strcasecmp(a->argv[3], "inbound")) {
4203  on_subscription = cli_complete_subscription_inbound;
4204  } else if (!strcasecmp(a->argv[3], "outbound")) {
4205  on_subscription = cli_complete_subscription_outbound;
4206  } else {
4207  /* Should never get here */
4208  ast_assert(0);
4209  return NULL;
4210  }
4211 
4212  cli.a = a;
4213  cli.callid = NULL;
4214  cli.wordlen = strlen(a->word);
4215  cli.which = 0;
4216  for_each_subscription(on_subscription, &cli);
4217 
4218  return cli.callid;
4219 }
4220 
4221 static unsigned int cli_subscription_expiry(struct sip_subscription_tree *sub_tree)
4222 {
4223  int expiry;
4224 
4225  expiry = sub_tree->persistence
4226  ? ast_tvdiff_ms(sub_tree->persistence->expires, ast_tvnow()) / 1000
4227  : 0;
4228  if (expiry < 0) {
4229  /* Subscription expired */
4230  expiry = 0;
4231  }
4232  return expiry;
4233 }
4234 
4236 {
4237  const char *callid = (const char *) cli->buf;/* Member repurposed to pass in callid */
4238  pj_str_t *sub_callid;
4239  struct ast_str *buf;
4240  char *src;
4241  char *dest;
4242  char *key;
4243  char *value;
4244  char *value_end;
4245  int key_len;
4246  int key_filler_width;
4247  int value_len;
4248 
4249  if (!sub_tree->dlg) {
4250  return 0;
4251  }
4252  sub_callid = &sub_tree->dlg->call_id->id;
4253  if (pj_strcmp2(sub_callid, callid)) {
4254  return 0;
4255  }
4256 
4257  buf = ast_str_create(512);
4258  if (!buf) {
4259  return -1;
4260  }
4261 
4262  ast_cli(cli->a->fd,
4263  "%-20s: %s\n"
4264  "===========================================================================\n",
4265  "ParameterName", "ParameterValue");
4266 
4267  ast_str_append(&buf, 0, "Resource: %s\n", sub_tree->root->resource);
4268  ast_str_append(&buf, 0, "Event: %s\n", sub_tree->root->handler->event_name);
4269  ast_str_append(&buf, 0, "Expiry: %u\n", cli_subscription_expiry(sub_tree));
4270 
4271  sip_subscription_to_ami(sub_tree, &buf);
4272 
4273  /* Convert AMI \r\n to \n line terminators. */
4274  src = strchr(ast_str_buffer(buf), '\r');
4275  if (src) {
4276  dest = src;
4277  ++src;
4278  while (*src) {
4279  if (*src == '\r') {
4280  ++src;
4281  continue;
4282  }
4283  *dest++ = *src++;
4284  }
4285  *dest = '\0';
4287  }
4288 
4289  /* Reformat AMI key value pairs to pretty columns */
4290  key = ast_str_buffer(buf);
4291  do {
4292  value = strchr(key, ':');
4293  if (!value) {
4294  break;
4295  }
4296  value_end = strchr(value, '\n');
4297  if (!value_end) {
4298  break;
4299  }
4300 
4301  /* Calculate field lengths */
4302  key_len = value - key;
4303  key_filler_width = 20 - key_len;
4304  if (key_filler_width < 0) {
4305  key_filler_width = 0;
4306  }
4307  value_len = value_end - value;
4308 
4309  ast_cli(cli->a->fd, "%.*s%*s%.*s\n",
4310  key_len, key, key_filler_width, "",
4311  value_len, value);
4312 
4313  key = value_end + 1;
4314  } while (*key);
4315  ast_cli(cli->a->fd, "\n");
4316 
4317  ast_free(buf);
4318 
4319  return -1;
4320 }
4321 
4322 static int cli_show_subscription_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4323 {
4324  return sub_tree->role == AST_SIP_NOTIFIER
4325  ? cli_show_subscription_common(sub_tree, arg) : 0;
4326 }
4327 
4328 static int cli_show_subscription_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4329 {
4330  return sub_tree->role == AST_SIP_SUBSCRIBER
4331  ? cli_show_subscription_common(sub_tree, arg) : 0;
4332 }
4333 
4334 static char *cli_show_subscription_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4335 {
4336  on_subscription_t on_subscription;
4337  struct cli_sub_parms cli;
4338 
4339  switch (cmd) {
4340  case CLI_INIT:
4341  e->command = "pjsip show subscription {inbound|outbound}";
4342  e->usage = "Usage:\n"
4343  " pjsip show subscription inbound <call-id>\n"
4344  " pjsip show subscription outbound <call-id>\n"
4345  " Show active subscription with the dialog call-id\n";
4346  return NULL;
4347  case CLI_GENERATE:
4349  }
4350 
4351  if (a->argc != 5) {
4352  return CLI_SHOWUSAGE;
4353  }
4354 
4355  if (!strcasecmp(a->argv[3], "inbound")) {
4356  on_subscription = cli_show_subscription_inbound;
4357  } else if (!strcasecmp(a->argv[3], "outbound")) {
4358  on_subscription = cli_show_subscription_outbound;
4359  } else {
4360  /* Should never get here */
4361  ast_assert(0);
4362  return NULL;
4363  }
4364 
4365  /* Find the subscription with the specified call-id */
4366  cli.a = a;
4367  cli.e = e;
4368  cli.buf = (void *) a->argv[4];/* Repurpose the buf member to pass in callid */
4369  for_each_subscription(on_subscription, &cli);
4370 
4371  return CLI_SUCCESS;
4372 }
4373 
4374 #define CLI_SHOW_SUB_FORMAT_HEADER \
4375  "Endpoint: <Endpoint/Caller-ID.............................................>\n" \
4376  "Resource: <Resource/Event.................................................>\n" \
4377  " Expiry: <Expiry> <Call-id..............................................>\n" \
4378  "===========================================================================\n\n"
4379 #define CLI_SHOW_SUB_FORMAT_ENTRY \
4380  "Endpoint: %s/%s\n" \
4381  "Resource: %s/%s\n" \
4382  " Expiry: %8d %s\n\n"
4383 
4385 {
4386  char caller_id[256];
4387  char callid[256];
4388 
4389  ast_callerid_merge(caller_id, sizeof(caller_id),
4390  S_COR(sub_tree->endpoint->id.self.name.valid,
4391  sub_tree->endpoint->id.self.name.str, NULL),
4392  S_COR(sub_tree->endpoint->id.self.number.valid,
4393  sub_tree->endpoint->id.self.number.str, NULL),
4394  "<none>");
4395 
4396  /* Call-id */
4397  if (sub_tree->dlg) {
4398  ast_copy_pj_str(callid, &sub_tree->dlg->call_id->id, sizeof(callid));
4399  } else {
4400  ast_copy_string(callid, "<unknown>", sizeof(callid));
4401  }
4402 
4404  ast_sorcery_object_get_id(sub_tree->endpoint), caller_id,
4405  sub_tree->root->resource, sub_tree->root->handler->event_name,
4406  cli_subscription_expiry(sub_tree), callid);
4407 
4408  if (cli->like) {
4409  if (regexec(cli->like, ast_str_buffer(cli->buf), 0, NULL, 0)) {
4410  /* Output line did not match the regex */
4411  return 0;
4412  }
4413  }
4414 
4415  ast_cli(cli->a->fd, "%s", ast_str_buffer(cli->buf));
4416  ++cli->count;
4417 
4418  return 0;
4419 }
4420 
4421 static int cli_show_subscriptions_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4422 {
4423  return sub_tree->role == AST_SIP_NOTIFIER
4424  ? cli_show_subscriptions_detail(sub_tree, arg) : 0;
4425 }
4426 
4427 static int cli_show_subscriptions_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4428 {
4429  return sub_tree->role == AST_SIP_SUBSCRIBER
4430  ? cli_show_subscriptions_detail(sub_tree, arg) : 0;
4431 }
4432 
4433 static char *cli_show_subscriptions_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4434 {
4435  on_subscription_t on_subscription;
4436  struct cli_sub_parms cli;
4437  regex_t like;
4438  const char *regex;
4439 
4440  switch (cmd) {
4441  case CLI_INIT:
4442  e->command = "pjsip show subscriptions {inbound|outbound} [like]";
4443  e->usage = "Usage:\n"
4444  " pjsip show subscriptions inbound [like <regex>]\n"
4445  " Show active inbound subscriptions\n"
4446  " pjsip show subscriptions outbound [like <regex>]\n"
4447  " Show active outbound subscriptions\n"
4448  "\n"
4449  " The regex selects a subscriptions output that matches.\n"
4450  " i.e., All output lines for a subscription are checked\n"
4451  " as a block by the regex.\n";
4452  return NULL;
4453  case CLI_GENERATE:
4454  return NULL;
4455  }
4456 
4457  if (a->argc != 4 && a->argc != 6) {
4458  return CLI_SHOWUSAGE;
4459  }
4460  if (!strcasecmp(a->argv[3], "inbound")) {
4461  on_subscription = cli_show_subscriptions_inbound;
4462  } else if (!strcasecmp(a->argv[3], "outbound")) {
4463  on_subscription = cli_show_subscriptions_outbound;
4464  } else {
4465  /* Should never get here */
4466  ast_assert(0);
4467  return CLI_SHOWUSAGE;
4468  }
4469  if (a->argc == 6) {
4470  int rc;
4471 
4472  if (strcasecmp(a->argv[4], "like")) {
4473  return CLI_SHOWUSAGE;
4474  }
4475 
4476  /* Setup regular expression */
4477  memset(&like, 0, sizeof(like));
4478  cli.like = &like;
4479  regex = a->argv[5];
4480  rc = regcomp(cli.like, regex, REG_EXTENDED | REG_NOSUB);
4481  if (rc) {
4482  char *regerr = ast_alloca(MAX_REGEX_ERROR_LEN);
4483 
4484  regerror(rc, cli.like, regerr, MAX_REGEX_ERROR_LEN);
4485  ast_cli(a->fd, "Regular expression '%s' failed to compile: %s\n",
4486  regex, regerr);
4487  return CLI_FAILURE;
4488  }
4489  } else {
4490  cli.like = NULL;
4491  regex = NULL;
4492  }
4493 
4494  cli.a = a;
4495  cli.e = e;
4496  cli.count = 0;
4497  cli.buf = ast_str_create(256);
4498  if (!cli.buf) {
4499  if (cli.like) {
4500  regfree(cli.like);
4501  }
4502  return CLI_FAILURE;
4503  }
4504 
4506  for_each_subscription(on_subscription, &cli);
4507  ast_cli(a->fd, "%d active subscriptions%s%s%s\n",
4508  cli.count,
4509  regex ? " matched \"" : "",
4510  regex ?: "",
4511  regex ? "\"" : "");
4512 
4513  ast_free(cli.buf);
4514  if (cli.like) {
4515  regfree(cli.like);
4516  }
4517 
4518  return CLI_SUCCESS;
4519 }
4520 
4521 #define CLI_LIST_SUB_FORMAT_HEADER "%-30.30s %-30.30s %6.6s %s\n"
4522 #define CLI_LIST_SUB_FORMAT_ENTRY "%-30.30s %-30.30s %6d %s\n"
4523 
4525 {
4526  char ep_cid_buf[50];
4527  char res_evt_buf[50];
4528  char callid[256];
4529 
4530  /* Endpoint/CID column */
4531  snprintf(ep_cid_buf, sizeof(ep_cid_buf), "%s/%s",
4533  S_COR(sub_tree->endpoint->id.self.name.valid, sub_tree->endpoint->id.self.name.str,
4534  S_COR(sub_tree->endpoint->id.self.number.valid,
4535  sub_tree->endpoint->id.self.number.str, "<none>")));
4536 
4537  /* Resource/Event column */
4538  snprintf(res_evt_buf, sizeof(res_evt_buf), "%s/%s",
4539  sub_tree->root->resource,
4540  sub_tree->root->handler->event_name);
4541 
4542  /* Call-id column */
4543  if (sub_tree->dlg) {
4544  ast_copy_pj_str(callid, &sub_tree->dlg->call_id->id, sizeof(callid));
4545  } else {
4546  ast_copy_string(callid, "<unknown>", sizeof(callid));
4547  }
4548 
4550  ep_cid_buf,
4551  res_evt_buf,
4552  cli_subscription_expiry(sub_tree),
4553  callid);
4554 
4555  if (cli->like) {
4556  if (regexec(cli->like, ast_str_buffer(cli->buf), 0, NULL, 0)) {
4557  /* Output line did not match the regex */
4558  return 0;
4559  }
4560  }
4561 
4562  ast_cli(cli->a->fd, "%s", ast_str_buffer(cli->buf));
4563  ++cli->count;
4564 
4565  return 0;
4566 }
4567 
4568 static int cli_list_subscriptions_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4569 {
4570  return sub_tree->role == AST_SIP_NOTIFIER
4571  ? cli_list_subscriptions_detail(sub_tree, arg) : 0;
4572 }
4573 
4574 static int cli_list_subscriptions_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4575 {
4576  return sub_tree->role == AST_SIP_SUBSCRIBER
4577  ? cli_list_subscriptions_detail(sub_tree, arg) : 0;
4578 }
4579 
4580 static char *cli_list_subscriptions_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4581 {
4582  on_subscription_t on_subscription;
4583  struct cli_sub_parms cli;
4584  regex_t like;
4585  const char *regex;
4586 
4587  switch (cmd) {
4588  case CLI_INIT:
4589  e->command = "pjsip list subscriptions {inbound|outbound} [like]";
4590  e->usage = "Usage:\n"
4591  " pjsip list subscriptions inbound [like <regex>]\n"
4592  " List active inbound subscriptions\n"
4593  " pjsip list subscriptions outbound [like <regex>]\n"
4594  " List active outbound subscriptions\n"
4595  "\n"
4596  " The regex selects output lines that match.\n";
4597  return NULL;
4598  case CLI_GENERATE:
4599  return NULL;
4600  }
4601 
4602  if (a->argc != 4 && a->argc != 6) {
4603  return CLI_SHOWUSAGE;
4604  }
4605  if (!strcasecmp(a->argv[3], "inbound")) {
4606  on_subscription = cli_list_subscriptions_inbound;
4607  } else if (!strcasecmp(a->argv[3], "outbound")) {
4608  on_subscription = cli_list_subscriptions_outbound;
4609  } else {
4610  /* Should never get here */
4611  ast_assert(0);
4612  return CLI_SHOWUSAGE;
4613  }
4614  if (a->argc == 6) {
4615  int rc;
4616 
4617  if (strcasecmp(a->argv[4], "like")) {
4618  return CLI_SHOWUSAGE;
4619  }
4620 
4621  /* Setup regular expression */
4622  memset(&like, 0, sizeof(like));
4623  cli.like = &like;
4624  regex = a->argv[5];
4625  rc = regcomp(cli.like, regex, REG_EXTENDED | REG_NOSUB);
4626  if (rc) {
4627  char *regerr = ast_alloca(MAX_REGEX_ERROR_LEN);
4628 
4629  regerror(rc, cli.like, regerr, MAX_REGEX_ERROR_LEN);
4630  ast_cli(a->fd, "Regular expression '%s' failed to compile: %s\n",
4631  regex, regerr);
4632  return CLI_FAILURE;
4633  }
4634  } else {
4635  cli.like = NULL;
4636  regex = NULL;
4637  }
4638 
4639  cli.a = a;
4640  cli.e = e;
4641  cli.count = 0;
4642  cli.buf = ast_str_create(256);
4643  if (!cli.buf) {
4644  if (cli.like) {
4645  regfree(cli.like);
4646  }
4647  return CLI_FAILURE;
4648  }
4649 
4651  "Endpoint/CLI", "Resource/Event", "Expiry", "Call-id");
4652  for_each_subscription(on_subscription, &cli);
4653  ast_cli(a->fd, "\n%d active subscriptions%s%s%s\n",
4654  cli.count,
4655  regex ? " matched \"" : "",
4656  regex ?: "",
4657  regex ? "\"" : "");
4658 
4659  ast_free(cli.buf);
4660  if (cli.like) {
4661  regfree(cli.like);
4662  }
4663 
4664  return CLI_SUCCESS;
4665 }
4666 
4667 static struct ast_cli_entry cli_commands[] = {
4668  AST_CLI_DEFINE(cli_list_subscriptions_inout, "List active inbound/outbound subscriptions"),
4669  AST_CLI_DEFINE(cli_show_subscription_inout, "Show active subscription details"),
4670  AST_CLI_DEFINE(cli_show_subscriptions_inout, "Show active inbound/outbound subscriptions"),
4671 };
4672 
4673 static int persistence_endpoint_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
4674 {
4675  struct subscription_persistence *persistence = obj;
4676 
4677  persistence->endpoint = ast_strdup(var->value);
4678  return 0;
4679 }
4680 
4681 static int persistence_endpoint_struct2str(const void *obj, const intptr_t *args, char **buf)
4682 {
4683  const struct subscription_persistence *persistence = obj;
4684 
4685  *buf = ast_strdup(persistence->endpoint);
4686  return 0;
4687 }
4688 
4689 static int persistence_tag_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
4690 {
4691  struct subscription_persistence *persistence = obj;
4692 
4693  persistence->tag = ast_strdup(var->value);
4694  return 0;
4695 }
4696 
4697 static int persistence_tag_struct2str(const void *obj, const intptr_t *args, char **buf)
4698 {
4699  const struct subscription_persistence *persistence = obj;
4700 
4701  *buf = ast_strdup(persistence->tag);
4702  return 0;
4703 }
4704 
4705 static int persistence_generator_data_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
4706 {
4707  struct subscription_persistence *persistence = obj;
4708  struct ast_json_error error;
4709 
4710  /* We tolerate a failure of the JSON to load and instead start fresh, since this field
4711  * originates from the persistence code and not a user.
4712  */
4713  persistence->generator_data = ast_json_load_string(var->value, &error);
4714 
4715  return 0;
4716 }
4717 
4718 static int persistence_generator_data_struct2str(const void *obj, const intptr_t *args, char **buf)
4719 {
4720  const struct subscription_persistence *persistence = obj;
4721  char *value;
4722 
4723  if (!persistence->generator_data) {
4724  return 0;
4725  }
4726 
4727  value = ast_json_dump_string(persistence->generator_data);
4728  if (!value) {
4729  return -1;
4730  }
4731 
4732  *buf = ast_strdup(value);
4734 
4735  return 0;
4736 }
4737 
4738 static int persistence_expires_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
4739 {
4740  struct subscription_persistence *persistence = obj;
4741  return ast_get_timeval(var->value, &persistence->expires, ast_tv(0, 0), NULL);
4742 }
4743 
4744 static int persistence_expires_struct2str(const void *obj, const intptr_t *args, char **buf)
4745 {
4746  const struct subscription_persistence *persistence = obj;
4747  return (ast_asprintf(buf, "%ld", persistence->expires.tv_sec) < 0) ? -1 : 0;
4748 }
4749 
4750 #define RESOURCE_LIST_INIT_SIZE 4
4751 
4752 static void resource_list_destructor(void *obj)
4753 {
4754  struct resource_list *list = obj;
4755  int i;
4756 
4757  for (i = 0; i < AST_VECTOR_SIZE(&list->items); ++i) {
4758  ast_free((char *) AST_VECTOR_GET(&list->items, i));
4759  }
4760 
4761  AST_VECTOR_FREE(&list->items);
4762 }
4763 
4764 static void *resource_list_alloc(const char *name)
4765 {
4766  struct resource_list *list;
4767 
4768  list = ast_sorcery_generic_alloc(sizeof(*list), resource_list_destructor);
4769  if (!list) {
4770  return NULL;
4771  }
4772 
4774  ao2_cleanup(list);
4775  return NULL;
4776  }
4777 
4778  return list;
4779 }
4780 
4781 static int item_in_vector(const struct resource_list *list, const char *item)
4782 {
4783  int i;
4784 
4785  for (i = 0; i < AST_VECTOR_SIZE(&list->items); ++i) {
4786  if (!strcmp(item, AST_VECTOR_GET(&list->items, i))) {
4787  return 1;
4788  }
4789  }
4790 
4791  return 0;
4792 }
4793 
4794 static int list_item_handler(const struct aco_option *opt,
4795  struct ast_variable *var, void *obj)
4796 {
4797  struct resource_list *list = obj;
4798  char *items = ast_strdupa(var->value);
4799  char *item;
4800 
4801  while ((item = ast_strip(strsep(&items, ",")))) {
4802  if (ast_strlen_zero(item)) {
4803  continue;
4804  }
4805 
4806  if (item_in_vector(list, item)) {
4807  ast_log(LOG_WARNING, "Ignoring duplicated list item '%s'\n", item);
4808  continue;
4809  }
4810 
4811  item = ast_strdup(item);
4812  if (!item || AST_VECTOR_APPEND(&list->items, item)) {
4813  ast_free(item);
4814  return -1;
4815  }
4816  }
4817 
4818  return 0;
4819 }
4820 
4821 static int list_item_to_str(const void *obj, const intptr_t *args, char **buf)
4822 {
4823  const struct resource_list *list = obj;
4824  int i;
4825  struct ast_str *str = ast_str_create(32);
4826 
4827  for (i = 0; i < AST_VECTOR_SIZE(&list->items); ++i) {
4828  ast_str_append(&str, 0, "%s,", AST_VECTOR_GET(&list->items, i));
4829  }
4830 
4831  /* Chop off trailing comma */
4832  ast_str_truncate(str, -1);
4834  ast_free(str);
4835  return 0;
4836 }