Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 generaator for NOTIFYs */
486  /*! Vector of child subscriptions */
487  AST_VECTOR(, struct ast_sip_subscription *) children;
488  /*! Saved NOTIFY body text for this subscription */
489  struct ast_str *body_text;
490  /*! Indicator that the body text has changed since the last notification */
491  int body_changed;
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 */
501  struct ast_json *persistence_data;
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  * \return Nothing
602  */
603 static void sub_tree_transport_cb(void *data)
604 {
605  struct sip_subscription_tree *sub_tree = data;
606 
607  /*
608  * Push off the subscription termination to the serializer to
609  * avoid deadlock. Another thread could be trying to send a
610  * message on the subscription that can deadlock with this
611  * thread.
612  */
613  ao2_ref(sub_tree, +1);
615  sub_tree)) {
616  ao2_ref(sub_tree, -1);
617  }
618 }
619 
620 /*! \brief Destructor for subscription persistence */
621 static void subscription_persistence_destroy(void *obj)
622 {
623  struct subscription_persistence *persistence = obj;
624 
625  ast_free(persistence->endpoint);
626  ast_free(persistence->tag);
627  ast_json_unref(persistence->generator_data);
628 }
629 
630 /*! \brief Allocator for subscription persistence */
631 static void *subscription_persistence_alloc(const char *name)
632 {
634 }
635 
636 /*! \brief Function which creates initial persistence information of a subscription in sorcery */
638 {
639  char tag[PJ_GUID_STRING_LENGTH + 1];
640 
641  /* The id of this persistence object doesn't matter as we keep it on the subscription and don't need to
642  * look it up by id at all.
643  */
645  "subscription_persistence", NULL);
646 
647  pjsip_dialog *dlg = sub_tree->dlg;
648 
649  if (!persistence) {
650  return NULL;
651  }
652 
653  persistence->endpoint = ast_strdup(ast_sorcery_object_get_id(sub_tree->endpoint));
654  ast_copy_pj_str(tag, &dlg->local.info->tag, sizeof(tag));
655  persistence->tag = ast_strdup(tag);
656 
657  ast_sorcery_create(ast_sip_get_sorcery(), persistence);
658  return persistence;
659 }
660 
661 /*! \brief Function which updates persistence information of a subscription in sorcery */
663  pjsip_rx_data *rdata, enum sip_persistence_update_type type)
664 {
665  pjsip_dialog *dlg;
666 
667  if (!sub_tree->persistence) {
668  return;
669  }
670 
671  ast_debug(3, "Updating persistence for '%s->%s' prune on boot: %s\n",
672  sub_tree->persistence->endpoint, sub_tree->root->resource,
673  sub_tree->persistence->prune_on_boot ? "yes" : "no");
674 
675  dlg = sub_tree->dlg;
676  sub_tree->persistence->cseq = dlg->local.cseq;
677 
678  if (rdata) {
679  unsigned int expires;
680  pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
681  pjsip_contact_hdr *contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
682 
683  expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
684  sub_tree->persistence->expires = ast_tvadd(ast_tvnow(), ast_samp2tv(expires, 1));
685 
686  if (contact_hdr) {
687  if (contact_hdr) {
688  if (type == SUBSCRIPTION_PERSISTENCE_CREATED) {
689  sub_tree->persistence->prune_on_boot =
691  (pjsip_sip_uri *)pjsip_uri_get_uri(contact_hdr->uri),
692  sub_tree->endpoint, rdata);
693 
694  if (sub_tree->persistence->prune_on_boot) {
695  ast_debug(3, "adding transport monitor on %s for '%s->%s' prune on boot: %d\n",
696  rdata->tp_info.transport->obj_name,
697  sub_tree->persistence->endpoint, sub_tree->root->resource,
698  sub_tree->persistence->prune_on_boot);
699  sub_tree->transport = rdata->tp_info.transport;
700  ast_sip_transport_monitor_register(rdata->tp_info.transport,
701  sub_tree_transport_cb, sub_tree);
702  /*
703  * FYI: ast_sip_transport_monitor_register holds a reference to the sub_tree
704  */
705  }
706  }
707  }
708 
709  pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri,
710  sub_tree->persistence->contact_uri, sizeof(sub_tree->persistence->contact_uri));
711  } else {
712  ast_log(LOG_WARNING, "Contact not updated due to missing contact header\n");
713  }
714 
715  /* When receiving a packet on an streaming transport, it's possible to receive more than one SIP
716  * message at a time into the rdata->pkt_info.packet buffer. However, the rdata->msg_info.msg_buf
717  * will always point to the proper SIP message that is to be processed. When updating subscription
718  * persistence that is pulled from persistent storage, though, the rdata->pkt_info.packet will
719  * only ever have a single SIP message on it, and so we base persistence on that.
720  */
723  if (rdata->msg_info.msg_buf) {
724  ast_copy_string(sub_tree->persistence->packet, rdata->msg_info.msg_buf,
725  MIN(sizeof(sub_tree->persistence->packet), rdata->msg_info.len + 1));
726  } else {
727  ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet,
728  sizeof(sub_tree->persistence->packet));
729  }
730  }
731  ast_copy_string(sub_tree->persistence->src_name, rdata->pkt_info.src_name,
732  sizeof(sub_tree->persistence->src_name));
733  sub_tree->persistence->src_port = rdata->pkt_info.src_port;
734  ast_copy_string(sub_tree->persistence->transport_key, rdata->tp_info.transport->type_name,
735  sizeof(sub_tree->persistence->transport_key));
736  ast_copy_pj_str(sub_tree->persistence->local_name, &rdata->tp_info.transport->local_name.host,
737  sizeof(sub_tree->persistence->local_name));
738  sub_tree->persistence->local_port = rdata->tp_info.transport->local_name.port;
739  }
740 
742 }
743 
744 /*! \brief Function which removes persistence of a subscription from sorcery */
746 {
747  if (!sub_tree->persistence) {
748  return;
749  }
750 
751  if (sub_tree->persistence->prune_on_boot && sub_tree->transport) {
752  ast_debug(3, "Unregistering transport monitor on %s '%s->%s'\n",
753  sub_tree->transport->obj_name,
754  sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",
755  sub_tree->root ? sub_tree->root->resource : "Unknown");
757  sub_tree_transport_cb, sub_tree, NULL);
758  }
759 
761  ao2_ref(sub_tree->persistence, -1);
762  sub_tree->persistence = NULL;
763 }
764 
765 
768  size_t num_accept, const char *body_type);
769 
770 /*! \brief Retrieve a handler using the Event header of an rdata message */
771 static struct ast_sip_subscription_handler *subscription_get_handler_from_rdata(pjsip_rx_data *rdata, const char *endpoint)
772 {
773  pjsip_event_hdr *event_header;
774  char event[32];
776 
777  event_header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_event_name, rdata->msg_info.msg->hdr.next);
778  if (!event_header) {
779  ast_log(LOG_WARNING, "Incoming SUBSCRIBE request from %s with no Event header\n",
780  endpoint ? endpoint : "Unknown");
781  return NULL;
782  }
783  ast_copy_pj_str(event, &event_header->event_type, sizeof(event));
784 
786  if (!handler) {
787  ast_log(LOG_WARNING, "No registered subscribe handler for event %s from %s\n", event,
788  endpoint ? endpoint : "Unknown");
789  }
790 
791  return handler;
792 }
793 
794 /*!
795  * \brief Accept headers that are exceptions to the rule
796  *
797  * Typically, when a SUBSCRIBE arrives, we attempt to find a
798  * body generator that matches one of the Accept headers in
799  * the request. When subscribing to a single resource, this works
800  * great. However, when subscribing to a list, things work
801  * differently. Most Accept header values are fine, but there
802  * are a couple that are endemic to resource lists that need
803  * to be ignored when searching for a body generator to use
804  * for the individual resources of the subscription.
805  */
806 const char *accept_exceptions[] = {
807  "multipart/related",
808  "application/rlmi+xml",
809 };
810 
811 /*!
812  * \brief Is the Accept header from the SUBSCRIBE in the list of exceptions?
813  *
814  * \retval 1 This Accept header value is an exception to the rule.
815  * \retval 0 This Accept header is not an exception to the rule.
816  */
817 static int exceptional_accept(const pj_str_t *accept)
818 {
819  int i;
820 
821  for (i = 0; i < ARRAY_LEN(accept_exceptions); ++i) {
822  if (!pj_strcmp2(accept, accept_exceptions[i])) {
823  return 1;
824  }
825  }
826 
827  return 0;
828 }
829 
830 /*! \brief Retrieve a body generator using the Accept header of an rdata message */
833 {
834  pjsip_accept_hdr *accept_header = (pjsip_accept_hdr *) &rdata->msg_info.msg->hdr;
835  char accept[AST_SIP_MAX_ACCEPT][64];
836  size_t num_accept_headers = 0;
837 
838  while ((accept_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, accept_header->next)) &&
839  (num_accept_headers < AST_SIP_MAX_ACCEPT)) {
840  int i;
841 
842  for (i = 0; i < accept_header->count && num_accept_headers < AST_SIP_MAX_ACCEPT; ++i) {
843  if (!exceptional_accept(&accept_header->values[i])) {
844  ast_copy_pj_str(accept[num_accept_headers], &accept_header->values[i], sizeof(accept[num_accept_headers]));
845  ++num_accept_headers;
846  }
847  }
848  }
849 
850  if (num_accept_headers == 0) {
851  /* If a SUBSCRIBE contains no Accept headers, then we must assume that
852  * the default accept type for the event package is to be used.
853  */
854  ast_copy_string(accept[0], handler->notifier->default_accept, sizeof(accept[0]));
855  num_accept_headers = 1;
856  }
857 
858  return find_body_generator(accept, num_accept_headers, handler->body_type);
859 }
860 
861 /*! \brief Check if the rdata has a Supported header containing 'eventlist'
862  *
863  * \retval 1 rdata has an eventlist containing supported header
864  * \retval 0 rdata doesn't have an eventlist containing supported header
865  */
866 static int ast_sip_pubsub_has_eventlist_support(pjsip_rx_data *rdata)
867 {
868  pjsip_supported_hdr *supported_header = (pjsip_supported_hdr *) &rdata->msg_info.msg->hdr;
869 
870  while ((supported_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_SUPPORTED, supported_header->next))) {
871  int i;
872 
873  for (i = 0; i < supported_header->count; i++) {
874  if (!pj_stricmp2(&supported_header->values[i], "eventlist")) {
875  return 1;
876  }
877  }
878  }
879 
880  return 0;
881 }
882 
883 struct resource_tree;
884 
885 /*!
886  * \brief A node for a resource tree.
887  */
888 struct tree_node {
889  AST_VECTOR(, struct tree_node *) children;
890  unsigned int full_state;
891  char resource[0];
892 };
893 
894 /*!
895  * \brief Helper function for retrieving a resource list for a given event.
896  *
897  * This will retrieve a resource list that corresponds to the resource and event provided.
898  *
899  * \param resource The name of the resource list to retrieve
900  * \param event The expected event name on the resource list
901  */
902 static struct resource_list *retrieve_resource_list(const char *resource, const char *event)
903 {
904  struct resource_list *list;
905 
906  list = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "resource_list", resource);
907  if (!list) {
908  return NULL;
909  }
910 
911  if (strcmp(list->event, event)) {
912  ast_log(LOG_WARNING, "Found resource list %s, but its event type (%s) does not match SUBSCRIBE's (%s)\n",
913  resource, list->event, event);
914  ao2_cleanup(list);
915  return NULL;
916  }
917 
918  return list;
919 }
920 
921 /*!
922  * \brief Allocate a tree node
923  *
924  * In addition to allocating and initializing the tree node, the node is also added
925  * to the vector of visited resources. See \ref build_resource_tree for more information
926  * on the visited resources.
927  *
928  * \param resource The name of the resource for this tree node.
929  * \param visited The vector of resources that have been visited.
930  * \param if allocating a list, indicate whether full state is requested in notifications.
931  * \retval NULL Allocation failure.
932  * \retval non-NULL The newly-allocated tree_node
933  */
934 static struct tree_node *tree_node_alloc(const char *resource, struct resources *visited, unsigned int full_state)
935 {
936  struct tree_node *node;
937 
938  node = ast_calloc(1, sizeof(*node) + strlen(resource) + 1);
939  if (!node) {
940  return NULL;
941  }
942 
943  strcpy(node->resource, resource);
944  if (AST_VECTOR_INIT(&node->children, 4)) {
945  ast_free(node);
946  return NULL;
947  }
948  node->full_state = full_state;
949 
950  if (visited) {
951  AST_VECTOR_APPEND(visited, resource);
952  }
953  return node;
954 }
955 
956 /*!
957  * \brief Destructor for a tree node
958  *
959  * This function calls recursively in order to destroy
960  * all nodes lower in the tree from the given node in
961  * addition to the node itself.
962  *
963  * \param node The node to destroy.
964  */
965 static void tree_node_destroy(struct tree_node *node)
966 {
967  int i;
968  if (!node) {
969  return;
970  }
971 
972  for (i = 0; i < AST_VECTOR_SIZE(&node->children); ++i) {
973  tree_node_destroy(AST_VECTOR_GET(&node->children, i));
974  }
975  AST_VECTOR_FREE(&node->children);
976  ast_free(node);
977 }
978 
979 /*!
980  * \brief Determine if this resource has been visited already
981  *
982  * See \ref build_resource_tree for more information
983  *
984  * \param resource The resource currently being visited
985  * \param visited The resources that have previously been visited
986  */
987 static int have_visited(const char *resource, struct resources *visited)
988 {
989  int i;
990 
991  for (i = 0; i < AST_VECTOR_SIZE(visited); ++i) {
992  if (!strcmp(resource, AST_VECTOR_GET(visited, i))) {
993  return 1;
994  }
995  }
996 
997  return 0;
998 }
999 
1000 /*!
1001  * \brief Build child nodes for a given parent.
1002  *
1003  * This iterates through the items on a resource list and creates tree nodes for each one. The
1004  * tree nodes created are children of the supplied parent node. If an item in the resource
1005  * list is itself a list, then this function is called recursively to provide children for
1006  * the new node.
1007  *
1008  * If an item in a resource list is not a list, then the supplied subscription handler is
1009  * called into as if a new SUBSCRIBE for the list item were presented. The handler's response
1010  * is used to determine if the node can be added to the tree or not.
1011  *
1012  * If a parent node ends up having no child nodes added under it, then the parent node is
1013  * pruned from the tree.
1014  *
1015  * \param endpoint The endpoint that sent the inbound SUBSCRIBE.
1016  * \param handler The subscription handler for leaf nodes in the tree.
1017  * \param list The configured resource list from which the child node is being built.
1018  * \param parent The parent node for these children.
1019  * \param visited The resources that have already been visited.
1020  */
1021 static void build_node_children(struct ast_sip_endpoint *endpoint, const struct ast_sip_subscription_handler *handler,
1022  struct resource_list *list, struct tree_node *parent, struct resources *visited)
1023 {
1024  int i;
1025 
1026  for (i = 0; i < AST_VECTOR_SIZE(&list->items); ++i) {
1027  struct tree_node *current;
1028  struct resource_list *child_list;
1029  const char *resource = AST_VECTOR_GET(&list->items, i);
1030 
1031  if (have_visited(resource, visited)) {
1032  ast_debug(1, "Already visited resource %s. Avoiding duplicate resource or potential loop.\n", resource);
1033  continue;
1034  }
1035 
1036  child_list = retrieve_resource_list(resource, list->event);
1037  if (!child_list) {
1038  int resp = handler->notifier->new_subscribe(endpoint, resource);
1039  if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
1040  current = tree_node_alloc(resource, visited, 0);
1041  if (!current) {
1042  ast_debug(1,
1043  "Subscription to leaf resource %s was successful, but encountered allocation error afterwards\n",
1044  resource);
1045  continue;
1046  }
1047  ast_debug(2, "Subscription to leaf resource %s resulted in success. Adding to parent %s\n",
1048  resource, parent->resource);
1049  if (AST_VECTOR_APPEND(&parent->children, current)) {
1050  tree_node_destroy(current);
1051  }
1052  } else {
1053  ast_debug(2, "Subscription to leaf resource %s resulted in error response %d\n",
1054  resource, resp);
1055  }
1056  } else {
1057  ast_debug(2, "Resource %s (child of %s) is a list\n", resource, parent->resource);
1058  current = tree_node_alloc(resource, visited, child_list->full_state);
1059  if (!current) {
1060  ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
1061  continue;
1062  }
1063  build_node_children(endpoint, handler, child_list, current, visited);
1064  if (AST_VECTOR_SIZE(&current->children) > 0) {
1065  ast_debug(1, "List %s had no successful children.\n", resource);
1066  if (AST_VECTOR_APPEND(&parent->children, current)) {
1067  tree_node_destroy(current);
1068  }
1069  } else {
1070  ast_debug(2, "List %s had successful children. Adding to parent %s\n",
1071  resource, parent->resource);
1072  tree_node_destroy(current);
1073  }
1074  ao2_cleanup(child_list);
1075  }
1076  }
1077 }
1078 
1079 /*!
1080  * \brief A resource tree
1081  *
1082  * When an inbound SUBSCRIBE arrives, the resource being subscribed to may
1083  * be a resource list. If this is the case, the resource list may contain resources
1084  * that are themselves lists. The structure needed to hold the resources is
1085  * a tree.
1086  *
1087  * Upon receipt of the SUBSCRIBE, the tree is built by determining if subscriptions
1088  * to the individual resources in the tree would be successful or not. Any successful
1089  * subscriptions result in a node in the tree being created. Any unsuccessful subscriptions
1090  * result in no node being created.
1091  *
1092  * This tree can be seen as a bare-bones analog of the tree of ast_sip_subscriptions that
1093  * will end up being created to actually carry out the duties of a SIP SUBSCRIBE dialog.
1094  */
1096  struct tree_node *root;
1098 };
1099 
1100 /*!
1101  * \brief Destroy a resource tree.
1102  *
1103  * This function makes no assumptions about how the tree itself was
1104  * allocated and does not attempt to free the tree itself. Callers
1105  * of this function are responsible for freeing the tree.
1106  *
1107  * \param tree The tree to destroy.
1108  */
1109 static void resource_tree_destroy(struct resource_tree *tree)
1110 {
1111  if (tree) {
1112  tree_node_destroy(tree->root);
1113  }
1114 }
1115 
1116 /*!
1117  * \brief Build a resource tree
1118  *
1119  * This function builds a resource tree based on the requested resource in a SUBSCRIBE request.
1120  *
1121  * This function also creates a container that has all resources that have been visited during
1122  * creation of the tree, whether those resources resulted in a tree node being created or not.
1123  * Keeping this container of visited resources allows for misconfigurations such as loops in
1124  * the tree or duplicated resources to be detected.
1125  *
1126  * \param endpoint The endpoint that sent the SUBSCRIBE request.
1127  * \param handler The subscription handler for leaf nodes in the tree.
1128  * \param resource The resource requested in the SUBSCRIBE request.
1129  * \param tree The tree that is to be built.
1130  * \param has_eventlist_support
1131  *
1132  * \retval 200-299 Successfully subscribed to at least one resource.
1133  * \retval 300-699 Failure to subscribe to requested resource.
1134  */
1136  const char *resource, struct resource_tree *tree, int has_eventlist_support)
1137 {
1138  RAII_VAR(struct resource_list *, list, NULL, ao2_cleanup);
1139  struct resources visited;
1140 
1141  if (!has_eventlist_support || !(list = retrieve_resource_list(resource, handler->event_name))) {
1142  ast_debug(2, "Subscription '%s->%s' is not to a list\n",
1143  ast_sorcery_object_get_id(endpoint), resource);
1144  tree->root = tree_node_alloc(resource, NULL, 0);
1145  if (!tree->root) {
1146  return 500;
1147  }
1148  return handler->notifier->new_subscribe(endpoint, resource);
1149  }
1150 
1151  ast_debug(2, "Subscription '%s->%s' is a list\n",
1152  ast_sorcery_object_get_id(endpoint), resource);
1153  if (AST_VECTOR_INIT(&visited, AST_VECTOR_SIZE(&list->items))) {
1154  return 500;
1155  }
1156 
1157  tree->root = tree_node_alloc(resource, &visited, list->full_state);
1158  if (!tree->root) {
1159  AST_VECTOR_FREE(&visited);
1160  return 500;
1161  }
1162 
1163  tree->notification_batch_interval = list->notification_batch_interval;
1164 
1165  build_node_children(endpoint, handler, list, tree->root, &visited);
1166  AST_VECTOR_FREE(&visited);
1167 
1168  if (AST_VECTOR_SIZE(&tree->root->children) > 0) {
1169  return 200;
1170  } else {
1171  return 500;
1172  }
1173 }
1174 
1175 static void add_subscription(struct sip_subscription_tree *obj)
1176 {
1180 }
1181 
1183 {
1184  struct sip_subscription_tree *i;
1185 
1188  if (i == obj) {
1190  if (i->root) {
1191  ast_debug(2, "Removing subscription '%s->%s' from list of subscriptions\n",
1193  }
1194  break;
1195  }
1196  }
1199 }
1200 
1202 {
1203  ast_debug(3, "Destroying SIP subscription from '%s->%s'\n",
1204  sub->tree && sub->tree->endpoint ? ast_sorcery_object_get_id(sub->tree->endpoint) : "Unknown",
1205  sub->resource);
1206 
1207  ast_free(sub->body_text);
1208 
1209  AST_VECTOR_FREE(&sub->children);
1210  ao2_cleanup(sub->datastores);
1211  ast_json_unref(sub->persistence_data);
1212  ast_free(sub);
1213 }
1214 
1216 {
1217  int i;
1218 
1219  if (!root) {
1220  return;
1221  }
1222 
1223  for (i = 0; i < AST_VECTOR_SIZE(&root->children); ++i) {
1224  struct ast_sip_subscription *child;
1225 
1226  child = AST_VECTOR_GET(&root->children, i);
1227  destroy_subscriptions(child);
1228  }
1229 
1230  destroy_subscription(root);
1231 }
1232 
1234  const char *resource, struct sip_subscription_tree *tree)
1235 {
1236  struct ast_sip_subscription *sub;
1237  pjsip_sip_uri *contact_uri;
1238 
1239  sub = ast_calloc(1, sizeof(*sub) + strlen(resource) + 1);
1240  if (!sub) {
1241  return NULL;
1242  }
1243  strcpy(sub->resource, resource); /* Safe */
1244 
1246  if (!sub->datastores) {
1247  destroy_subscription(sub);
1248  return NULL;
1249  }
1250 
1251  sub->body_text = ast_str_create(128);
1252  if (!sub->body_text) {
1253  destroy_subscription(sub);
1254  return NULL;
1255  }
1256 
1257  sub->uri = pjsip_sip_uri_create(tree->dlg->pool, PJ_FALSE);
1258  contact_uri = pjsip_uri_get_uri(tree->dlg->local.contact->uri);
1259  pjsip_sip_uri_assign(tree->dlg->pool, sub->uri, contact_uri);
1260  pj_strdup2(tree->dlg->pool, &sub->uri->user, resource);
1261 
1262  /* If there is any persistence information available for this subscription that was persisted
1263  * then make it available so that the NOTIFY has the correct state.
1264  */
1265 
1266  if (tree->persistence && tree->persistence->generator_data) {
1267  sub->persistence_data = ast_json_ref(ast_json_object_get(tree->persistence->generator_data, resource));
1268  }
1269 
1270  sub->handler = handler;
1271  sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
1272  sub->tree = ao2_bump(tree);
1273 
1274  return sub;
1275 }
1276 
1277 /*!
1278  * \brief Create a tree of virtual subscriptions based on a resource tree node.
1279  *
1280  * \param handler The handler to supply to leaf subscriptions.
1281  * \param resource The requested resource for this subscription.
1282  * \param generator Body generator to use for leaf subscriptions.
1283  * \param tree The root of the subscription tree.
1284  * \param current The tree node that corresponds to the subscription being created.
1285  */
1287  const char *resource, struct ast_sip_pubsub_body_generator *generator,
1288  struct sip_subscription_tree *tree, struct tree_node *current)
1289 {
1290  int i;
1291  struct ast_sip_subscription *sub;
1292 
1293  sub = allocate_subscription(handler, resource, tree);
1294  if (!sub) {
1295  return NULL;
1296  }
1297 
1298  sub->full_state = current->full_state;
1299  sub->body_generator = generator;
1300  AST_VECTOR_INIT(&sub->children, AST_VECTOR_SIZE(&current->children));
1301 
1302  for (i = 0; i < AST_VECTOR_SIZE(&current->children); ++i) {
1303  struct ast_sip_subscription *child;
1304  struct tree_node *child_node = AST_VECTOR_GET(&current->children, i);
1305 
1306  child = create_virtual_subscriptions(handler, child_node->resource, generator,
1307  tree, child_node);
1308 
1309  if (!child) {
1310  ast_debug(1, "Child subscription to resource %s could not be created\n",
1311  child_node->resource);
1312  continue;
1313  }
1314 
1315  if (AST_VECTOR_APPEND(&sub->children, child)) {
1316  ast_debug(1, "Child subscription to resource %s could not be appended\n",
1317  child_node->resource);
1318  destroy_subscription(child);
1319  /* Have to release tree here too because a ref was added
1320  * to child that destroy_subscription() doesn't release. */
1321  ao2_cleanup(tree);
1322  }
1323  }
1324 
1325  return sub;
1326 }
1327 
1329 {
1330  int i;
1331 
1332  if (!sub) {
1333  return;
1334  }
1335 
1336  if (AST_VECTOR_SIZE(&sub->children) > 0) {
1337  for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
1338  shutdown_subscriptions(AST_VECTOR_GET(&sub->children, i));
1339  }
1340  return;
1341  }
1342 
1343  /* We notify subscription shutdown only on the tree leaves. */
1344  if (sub->handler->subscription_shutdown) {
1345  sub->handler->subscription_shutdown(sub);
1346  }
1347 }
1348 static int subscription_unreference_dialog(void *obj)
1349 {
1350  struct sip_subscription_tree *sub_tree = obj;
1351 
1352  /* This is why we keep the dialog on the subscription. When the subscription
1353  * is destroyed, there is no guarantee that the underlying dialog is ready
1354  * to be destroyed. Furthermore, there's no guarantee in the opposite direction
1355  * either. The dialog could be destroyed before our subscription is. We fix
1356  * this problem by keeping a reference to the dialog until it is time to
1357  * destroy the subscription. We need to have the dialog available when the
1358  * subscription is destroyed so that we can guarantee that our attempt to
1359  * remove the serializer will be successful.
1360  */
1361  pjsip_dlg_dec_session(sub_tree->dlg, &pubsub_module);
1362  sub_tree->dlg = NULL;
1363 
1364  return 0;
1365 }
1366 
1367 static void subscription_tree_destructor(void *obj)
1368 {
1369  struct sip_subscription_tree *sub_tree = obj;
1370 
1371  ast_debug(3, "Destroying subscription tree %p '%s->%s'\n",
1372  sub_tree,
1373  sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",
1374  sub_tree->root ? sub_tree->root->resource : "Unknown");
1375 
1376  destroy_subscriptions(sub_tree->root);
1377 
1378  if (sub_tree->dlg) {
1381  }
1382 
1383  ao2_cleanup(sub_tree->endpoint);
1384 
1387 }
1388 
1390 {
1391  ast_debug(3, "Removing subscription %p '%s->%s' reference to subscription tree %p\n",
1392  sub, ast_sorcery_object_get_id(sub->tree->endpoint), sub->resource, sub->tree);
1393  ao2_cleanup(sub->tree);
1394 }
1395 
1396 static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg)
1397 {
1398  sub_tree->dlg = dlg;
1399  ast_sip_dialog_set_serializer(dlg, sub_tree->serializer);
1400  ast_sip_dialog_set_endpoint(dlg, sub_tree->endpoint);
1401  pjsip_evsub_set_mod_data(sub_tree->evsub, pubsub_module.id, sub_tree);
1402  pjsip_dlg_inc_session(dlg, &pubsub_module);
1403 }
1404 
1405 static struct sip_subscription_tree *allocate_subscription_tree(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
1406 {
1407  struct sip_subscription_tree *sub_tree;
1408 
1409  sub_tree = ao2_alloc(sizeof *sub_tree, subscription_tree_destructor);
1410  if (!sub_tree) {
1411  return NULL;
1412  }
1413 
1415 
1416  if (rdata) {
1417  /*
1418  * We must continue using the serializer that the original
1419  * SUBSCRIBE came in on for the dialog. There may be
1420  * retransmissions already enqueued in the original
1421  * serializer that can result in reentrancy and message
1422  * sequencing problems.
1423  */
1425  } else {
1426  char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
1427 
1428  /* Create name with seq number appended. */
1429  ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/pubsub/%s",
1430  ast_sorcery_object_get_id(endpoint));
1431 
1432  sub_tree->serializer = ast_sip_create_serializer(tps_name);
1433  }
1434  if (!sub_tree->serializer) {
1435  ao2_ref(sub_tree, -1);
1436  return NULL;
1437  }
1438 
1439  sub_tree->endpoint = ao2_bump(endpoint);
1440  sub_tree->notify_sched_id = -1;
1441 
1442  return sub_tree;
1443 }
1444 
1445 /*!
1446  * \brief Create a subscription tree based on a resource tree.
1447  *
1448  * Using the previously-determined valid resources in the provided resource tree,
1449  * a corresponding tree of ast_sip_subscriptions are created. The root of the
1450  * subscription tree is a real subscription, and the rest in the tree are
1451  * virtual subscriptions.
1452  *
1453  * \param handler The handler to use for leaf subscriptions
1454  * \param endpoint The endpoint that sent the SUBSCRIBE request
1455  * \param rdata The SUBSCRIBE content
1456  * \param resource The requested resource in the SUBSCRIBE request
1457  * \param generator The body generator to use in leaf subscriptions
1458  * \param tree The resource tree on which the subscription tree is based
1459  * \param dlg_status[out] The result of attempting to create a dialog.
1460  *
1461  * \retval NULL Could not create the subscription tree
1462  * \retval non-NULL The root of the created subscription tree
1463  */
1464 
1466  struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource,
1468  pj_status_t *dlg_status, struct subscription_persistence *persistence)
1469 {
1470  struct sip_subscription_tree *sub_tree;
1471  pjsip_dialog *dlg;
1472 
1473  sub_tree = allocate_subscription_tree(endpoint, rdata);
1474  if (!sub_tree) {
1475  *dlg_status = PJ_ENOMEM;
1476  return NULL;
1477  }
1478  sub_tree->role = AST_SIP_NOTIFIER;
1479 
1480  dlg = ast_sip_create_dialog_uas_locked(endpoint, rdata, dlg_status);
1481  if (!dlg) {
1482  if (*dlg_status != PJ_EEXISTS) {
1483  ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n");
1484  }
1485  ao2_ref(sub_tree, -1);
1486  return NULL;
1487  }
1488 
1489  persistence = ast_sip_mod_data_get(rdata->endpt_info.mod_data,
1491  if (persistence) {
1492  /* Update the created dialog with the persisted information */
1493  pjsip_ua_unregister_dlg(pjsip_ua_instance(), dlg);
1494  pj_strdup2(dlg->pool, &dlg->local.info->tag, persistence->tag);
1495  dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->local.info->tag);
1496  pjsip_ua_register_dlg(pjsip_ua_instance(), dlg);
1497  dlg->local.cseq = persistence->cseq;
1498  }
1499 
1500  pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub_tree->evsub);
1501 
1502  subscription_setup_dialog(sub_tree, dlg);
1503 
1504  /*
1505  * The evsub and subscription setup both add dialog refs, so the dialog ref that
1506  * was added when the dialog was created (see ast_sip_create_dialog_uas_lock) can
1507  * now be removed. The lock should no longer be needed so can be removed too.
1508  */
1509  pjsip_dlg_dec_lock(dlg);
1510 
1511 #ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
1512  pjsip_evsub_add_ref(sub_tree->evsub);
1513 #endif
1514 
1515  ast_sip_mod_data_set(dlg->pool, dlg->mod_data, pubsub_module.id, MOD_DATA_MSG,
1516  pjsip_msg_clone(dlg->pool, rdata->msg_info.msg));
1517 
1519 
1520  /* Persistence information needs to be available for all the subscriptions */
1521  sub_tree->persistence = ao2_bump(persistence);
1522 
1523  sub_tree->root = create_virtual_subscriptions(handler, resource, generator, sub_tree, tree->root);
1524  if (AST_VECTOR_SIZE(&sub_tree->root->children) > 0) {
1525  sub_tree->is_list = 1;
1526  }
1527 
1528  add_subscription(sub_tree);
1529 
1530  return sub_tree;
1531 }
1532 
1533 /*! Wrapper structure for initial_notify_task */
1536  unsigned int expires;
1537 };
1538 
1539 static int initial_notify_task(void *obj);
1540 static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state);
1541 
1542 /*! Persistent subscription recreation continuation under distributor serializer data */
1545  pjsip_rx_data *rdata;
1546 };
1547 
1548 /*!
1549  * \internal
1550  * \brief subscription_persistence_recreate continuation under distributor serializer.
1551  * \since 13.10.0
1552  *
1553  * \retval 0 on success.
1554  * \retval -1 on error.
1555  */
1556 static int sub_persistence_recreate(void *obj)
1557 {
1558  struct persistence_recreate_data *recreate_data = obj;
1559  struct subscription_persistence *persistence = recreate_data->persistence;
1560  pjsip_rx_data *rdata = recreate_data->rdata;
1561  struct ast_sip_endpoint *endpoint;
1562  struct sip_subscription_tree *sub_tree;
1565  char *resource;
1566  pjsip_sip_uri *request_uri;
1567  size_t resource_size;
1568  int resp;
1569  struct resource_tree tree;
1570  pjsip_expires_hdr *expires_header;
1571  int64_t expires;
1572 
1573  request_uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
1574  resource_size = pj_strlen(&request_uri->user) + 1;
1575  resource = ast_alloca(resource_size);
1576  ast_copy_pj_str(resource, &request_uri->user, resource_size);
1577 
1578  /*
1579  * We may want to match without any user options getting
1580  * in the way.
1581  */
1583 
1584  handler = subscription_get_handler_from_rdata(rdata, persistence->endpoint);
1585  if (!handler || !handler->notifier) {
1586  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not get subscription handler.\n",
1587  persistence->endpoint);
1588  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1589  return 0;
1590  }
1591 
1592  generator = subscription_get_generator_from_rdata(rdata, handler);
1593  if (!generator) {
1594  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Body generator not available.\n",
1595  persistence->endpoint);
1596  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1597  return 0;
1598  }
1599 
1600  ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data,
1601  pubsub_module.id, MOD_DATA_PERSISTENCE, persistence);
1602 
1603  /* Getting the endpoint may take some time that can affect the expiration. */
1604  endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
1605  persistence->endpoint);
1606  if (!endpoint) {
1607  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The endpoint was not found\n",
1608  persistence->endpoint);
1609  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1610  return 0;
1611  }
1612 
1613  /* Update the expiration header with the new expiration */
1614  expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
1615  rdata->msg_info.msg->hdr.next);
1616  if (!expires_header) {
1617  expires_header = pjsip_expires_hdr_create(rdata->tp_info.pool, 0);
1618  if (!expires_header) {
1619  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not update expires header.\n",
1620  persistence->endpoint);
1621  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1622  ao2_ref(endpoint, -1);
1623  return 0;
1624  }
1625  pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr *) expires_header);
1626  }
1627 
1628  expires = (ast_tvdiff_ms(persistence->expires, ast_tvnow()) / 1000);
1629  if (expires <= 0) {
1630  /* The subscription expired since we started recreating the subscription. */
1631  ast_debug(3, "Expired subscription retrived from persistent store '%s' %s\n",
1632  persistence->endpoint, persistence->tag);
1633  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1634  ao2_ref(endpoint, -1);
1635  return 0;
1636  }
1637  expires_header->ivalue = expires;
1638 
1639  memset(&tree, 0, sizeof(tree));
1640  resp = build_resource_tree(endpoint, handler, resource, &tree,
1642  if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
1643  pj_status_t dlg_status;
1644 
1645  sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator,
1646  &tree, &dlg_status, persistence);
1647  if (!sub_tree) {
1648  if (dlg_status != PJ_EEXISTS) {
1649  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not create subscription tree.\n",
1650  persistence->endpoint);
1651  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1652  }
1653  } else {
1654  struct initial_notify_data *ind = ast_malloc(sizeof(*ind));
1655 
1656  if (!ind) {
1657  pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
1658  goto error;
1659  }
1660 
1661  ind->sub_tree = ao2_bump(sub_tree);
1662  ind->expires = expires_header->ivalue;
1663 
1665  if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ind)) {
1666  /* Could not send initial subscribe NOTIFY */
1667  pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
1668  ao2_ref(sub_tree, -1);
1669  ast_free(ind);
1670  }
1671  }
1672  } else {
1673  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1674  }
1675 
1676 error:
1677  resource_tree_destroy(&tree);
1678  ao2_ref(endpoint, -1);
1679 
1680  return 0;
1681 }
1682 
1683 /*! \brief Callback function to perform the actual recreation of a subscription */
1684 static int subscription_persistence_recreate(void *obj, void *arg, int flags)
1685 {
1686  struct subscription_persistence *persistence = obj;
1687  pj_pool_t *pool = arg;
1688  struct ast_taskprocessor *serializer;
1689  pjsip_rx_data rdata;
1690  struct persistence_recreate_data recreate_data;
1691 
1692  /* If this subscription used a reliable transport it can't be reestablished so remove it */
1693  if (persistence->prune_on_boot) {
1694  ast_debug(3, "Deleting subscription marked as 'prune' from persistent store '%s' %s\n",
1695  persistence->endpoint, persistence->tag);
1696  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1697  return 0;
1698  }
1699 
1700  /* If this subscription has already expired remove it */
1701  if (ast_tvdiff_ms(persistence->expires, ast_tvnow()) <= 0) {
1702  ast_debug(3, "Expired subscription retrived from persistent store '%s' %s\n",
1703  persistence->endpoint, persistence->tag);
1704  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1705  return 0;
1706  }
1707 
1708  memset(&rdata, 0, sizeof(rdata));
1709  pj_pool_reset(pool);
1710  rdata.tp_info.pool = pool;
1711 
1712  if (ast_sip_create_rdata_with_contact(&rdata, persistence->packet, persistence->src_name,
1713  persistence->src_port, persistence->transport_key, persistence->local_name,
1714  persistence->local_port, persistence->contact_uri)) {
1715  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The message could not be parsed\n",
1716  persistence->endpoint);
1717  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1718  return 0;
1719  }
1720 
1721  if (rdata.msg_info.msg->type != PJSIP_REQUEST_MSG) {
1722  ast_log(LOG_NOTICE, "Failed recreating '%s' subscription: Stored a SIP response instead of a request.\n",
1723  persistence->endpoint);
1724  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1725  return 0;
1726  }
1727 
1728  /* Continue the remainder in the distributor serializer */
1729  serializer = ast_sip_get_distributor_serializer(&rdata);
1730  if (!serializer) {
1731  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not get distributor serializer.\n",
1732  persistence->endpoint);
1733  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1734  return 0;
1735  }
1736  recreate_data.persistence = persistence;
1737  recreate_data.rdata = &rdata;
1739  &recreate_data)) {
1740  ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not continue under distributor serializer.\n",
1741  persistence->endpoint);
1742  ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
1743  }
1744  ast_taskprocessor_unreference(serializer);
1745 
1746  return 0;
1747 }
1748 
1749 /*! \brief Function which loads and recreates persisted subscriptions upon startup when the system is fully booted */
1750 static int subscription_persistence_load(void *data)
1751 {
1752  struct ao2_container *persisted_subscriptions = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
1753  "subscription_persistence", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1754  pj_pool_t *pool;
1755 
1756  pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "rtd%p", PJSIP_POOL_RDATA_LEN,
1757  PJSIP_POOL_RDATA_INC);
1758  if (!pool) {
1759  ast_log(LOG_WARNING, "Could not create a memory pool for recreating SIP subscriptions\n");
1760  return 0;
1761  }
1762 
1763  ao2_callback(persisted_subscriptions, OBJ_NODATA, subscription_persistence_recreate, pool);
1764 
1765  pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1766 
1767  ao2_ref(persisted_subscriptions, -1);
1768  return 0;
1769 }
1770 
1771 /*! \brief Event callback which fires subscription persistence recreation when the system is fully booted */
1773 {
1774  struct ast_json_payload *payload;
1775  const char *type;
1776 
1778  return;
1779  }
1780 
1781  payload = stasis_message_data(message);
1782  type = ast_json_string_get(ast_json_object_get(payload->json, "type"));
1783 
1784  /* This subscription only responds to the FullyBooted event so that all modules have been loaded when we
1785  * recreate SIP subscriptions.
1786  */
1787  if (strcmp(type, "FullyBooted")) {
1788  return;
1789  }
1790 
1791  /* This has to be here so the subscription is recreated when the body generator is available */
1793 
1794  /* Once the system is fully booted we don't care anymore */
1795  stasis_unsubscribe(sub);
1796 }
1797 
1798 typedef int (*on_subscription_t)(struct sip_subscription_tree *sub, void *arg);
1799 
1800 static int for_each_subscription(on_subscription_t on_subscription, void *arg)
1801 {
1802  int num = 0;
1803  struct sip_subscription_tree *i;
1804 
1805  if (!on_subscription) {
1806  return num;
1807  }
1808 
1811  if (on_subscription(i, arg)) {
1812  break;
1813  }
1814  ++num;
1815  }
1817  return num;
1818 }
1819 
1820 static void sip_subscription_to_ami(struct sip_subscription_tree *sub_tree,
1821  struct ast_str **buf)
1822 {
1823  char str[256];
1824  struct ast_sip_endpoint_id_configuration *id = &sub_tree->endpoint->id;
1825 
1826  ast_str_append(buf, 0, "Role: %s\r\n",
1827  sip_subscription_roles_map[sub_tree->role]);
1828  ast_str_append(buf, 0, "Endpoint: %s\r\n",
1829  ast_sorcery_object_get_id(sub_tree->endpoint));
1830 
1831  if (sub_tree->dlg) {
1832  ast_copy_pj_str(str, &sub_tree->dlg->call_id->id, sizeof(str));
1833  } else {
1834  ast_copy_string(str, "<unknown>", sizeof(str));
1835  }
1836  ast_str_append(buf, 0, "Callid: %s\r\n", str);
1837 
1838  ast_str_append(buf, 0, "State: %s\r\n", pjsip_evsub_get_state_name(sub_tree->evsub));
1839 
1840  ast_callerid_merge(str, sizeof(str),
1841  S_COR(id->self.name.valid, id->self.name.str, NULL),
1842  S_COR(id->self.number.valid, id->self.number.str, NULL),
1843  "Unknown");
1844 
1845  ast_str_append(buf, 0, "Callerid: %s\r\n", str);
1846 
1847  /* XXX This needs to be done recursively for lists */
1848  if (sub_tree->root->handler->to_ami) {
1849  sub_tree->root->handler->to_ami(sub_tree->root, buf);
1850  }
1851 }
1852 
1853 
1854 void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, const char *header)
1855 {
1856  pjsip_dialog *dlg;
1857  pjsip_msg *msg;
1858  pj_str_t name;
1859 
1860  dlg = sub->tree->dlg;
1861  msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
1862  pj_cstr(&name, header);
1863 
1864  return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
1865 }
1866 
1867 /* XXX This function is not used. */
1869  struct ast_sip_endpoint *endpoint, const char *resource)
1870 {
1871  struct ast_sip_subscription *sub;
1872  pjsip_dialog *dlg;
1873  struct ast_sip_contact *contact;
1874  pj_str_t event;
1875  pjsip_tx_data *tdata;
1876  pjsip_evsub *evsub;
1877  struct sip_subscription_tree *sub_tree = NULL;
1878 
1879  sub_tree = allocate_subscription_tree(endpoint, NULL);
1880  if (!sub_tree) {
1881  return NULL;
1882  }
1883 
1884  sub = allocate_subscription(handler, resource, sub_tree);
1885  if (!sub) {
1886  ao2_cleanup(sub_tree);
1887  return NULL;
1888  }
1889 
1891  if (!contact || ast_strlen_zero(contact->uri)) {
1892  ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create SIP subsription\n",
1893  ast_sorcery_object_get_id(endpoint));
1894  ao2_ref(sub_tree, -1);
1895  ao2_cleanup(contact);
1896  return NULL;
1897  }
1898 
1899  dlg = ast_sip_create_dialog_uac(endpoint, contact->uri, NULL);
1900  ao2_cleanup(contact);
1901  if (!dlg) {
1902  ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n");
1903  ao2_ref(sub_tree, -1);
1904  return NULL;
1905  }
1906 
1907  pj_cstr(&event, handler->event_name);
1908  pjsip_evsub_create_uac(dlg, &pubsub_cb, &event, 0, &sub_tree->evsub);
1909  subscription_setup_dialog(sub_tree, dlg);
1910 
1911  evsub = sub_tree->evsub;
1912 
1913  if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
1914  pjsip_evsub_send_request(sub_tree->evsub, tdata);
1915  } else {
1916  /* pjsip_evsub_terminate will result in pubsub_on_evsub_state,
1917  * being called and terminating the subscription. Therefore, we don't
1918  * need to decrease the reference count of sub here.
1919  */
1920  pjsip_evsub_terminate(evsub, PJ_TRUE);
1921  ao2_ref(sub_tree, -1);
1922  return NULL;
1923  }
1924 
1925  add_subscription(sub_tree);
1926 
1927  return sub;
1928 }
1929 
1931 {
1932  ast_assert(sub->tree->dlg != NULL);
1933  return sub->tree->dlg;
1934 }
1935 
1937 {
1938  ast_assert(sub->tree->endpoint != NULL);
1939  return ao2_bump(sub->tree->endpoint);
1940 }
1941 
1943 {
1944  ast_assert(sub->tree->serializer != NULL);
1945  return sub->tree->serializer;
1946 }
1947 
1948 /*!
1949  * \brief Pre-allocate a buffer for the transmission
1950  *
1951  * Typically, we let PJSIP do this step for us when we send a request. PJSIP's buffer
1952  * allocation algorithm is to allocate a buffer of PJSIP_MAX_PKT_LEN bytes and attempt
1953  * to write the packet to the allocated buffer. If the buffer is too small to hold the
1954  * packet, then we get told the message is too long to be sent.
1955  *
1956  * When dealing with SIP NOTIFY, especially with RLS, it is possible to exceed
1957  * PJSIP_MAX_PKT_LEN. Rather than accepting the limitation imposed on us by default,
1958  * we instead take the strategy of pre-allocating the buffer, testing for ourselves
1959  * if the message will fit, and resizing the buffer as required.
1960  *
1961  * The limit we impose is double that of the maximum packet length.
1962  *
1963  * \param tdata The tdata onto which to allocate a buffer
1964  * \retval 0 Success
1965  * \retval -1 The message is too large
1966  */
1967 static int allocate_tdata_buffer(pjsip_tx_data *tdata)
1968 {
1969  int buf_size;
1970  int size = -1;
1971  char *buf;
1972 
1973  for (buf_size = PJSIP_MAX_PKT_LEN; size == -1 && buf_size < (PJSIP_MAX_PKT_LEN * 2); buf_size *= 2) {
1974  buf = pj_pool_alloc(tdata->pool, buf_size);
1975  size = pjsip_msg_print(tdata->msg, buf, buf_size);
1976  }
1977 
1978  if (size == -1) {
1979  return -1;
1980  }
1981 
1982  tdata->buf.start = buf;
1983  tdata->buf.cur = tdata->buf.start;
1984  tdata->buf.end = tdata->buf.start + buf_size;
1985 
1986  return 0;
1987 }
1988 
1989 static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata)
1990 {
1991 #ifdef TEST_FRAMEWORK
1992  struct ast_sip_endpoint *endpoint = sub_tree->endpoint;
1993  pjsip_evsub *evsub = sub_tree->evsub;
1994 #endif
1995  int res;
1996 
1997  if (allocate_tdata_buffer(tdata)) {
1998  ast_log(LOG_ERROR, "SIP request %s is too large to send.\n", tdata->info);
1999  pjsip_tx_data_dec_ref(tdata);
2000  return -1;
2001  }
2002 
2003  res = pjsip_evsub_send_request(sub_tree->evsub, tdata);
2004 
2006 
2007  ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
2008  "StateText: %s\r\n"
2009  "Endpoint: %s\r\n",
2010  pjsip_evsub_get_state_name(evsub),
2011  ast_sorcery_object_get_id(endpoint));
2012 
2013  return (res == PJ_SUCCESS ? 0 : -1);
2014 }
2015 
2016 /*!
2017  * \brief Add a resource XML element to an RLMI body
2018  *
2019  * Each resource element represents a subscribed resource in the list. This function currently
2020  * will unconditionally add an instance element to each created resource element. Instance
2021  * elements refer to later parts in the multipart body.
2022  *
2023  * \param pool PJLIB allocation pool
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  */
2096 AST_VECTOR(body_part_list, struct body_part *);
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;
2520  sub_tree->notify_sched_id = ast_sched_add(sched, sub_tree->notification_batch_interval, sched_cb, ao2_bump(sub_tree));
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 
2543  ast_sip_subscription_get_body_subtype(sub), notify_data, &sub->body_text)) {
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;
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) {
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 
2578 void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
2579 {
2580  pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, sub->uri, buf, size);
2581 }
2582 
2583 void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
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 {
2626  return ast_datastores_alloc_datastore(info, uid);
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 {
2714  AST_RWLIST_INSERT_TAIL(&publish_handlers, handler, next);
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 
2733  publish_add_handler(handler);
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 
2805  sub_add_handler(handler);
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 
2844  gen = find_body_generator_type_subtype_nolock(type, subtype);
2846  return gen;
2847 }
2848 
2850 {
2851  char *accept_copy = ast_strdupa(accept);
2852  char *subtype = accept_copy;
2853  char *type = strsep(&subtype, "/");
2854 
2855  if (ast_strlen_zero(type) || ast_strlen_zero(subtype)) {
2856  return NULL;
2857  }
2858 
2859  return find_body_generator_type_subtype(type, subtype);
2860 }
2861 
2863  size_t num_accept, const char *body_type)
2864 {
2865  int i;
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 
2922  ao2_cleanup(notify_data);
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,
2950  ind->sub_tree->root->resource, ind->sub_tree->root->handler->event_name,
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;
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 
3070  sub_tree->persistence = subscription_persistence_create(sub_tree);
3072  sip_subscription_accept(sub_tree, rdata, resp);
3073  if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ind)) {
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 
3351  handler = find_pub_handler(event);
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;
3524  int res = 0;
3525  void *body;
3526 
3527  generator = find_body_generator_type_subtype(type, subtype);
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 
3665  if (ast_publish_mwi_state(mailbox, context,
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 
4037  buf = ast_sip_create_ami_event(event, ami);
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);
4733  ast_json_free(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);
4833  *buf = ast_strdup(ast_str_buffer(str));
4834  ast_free(str);
4835  return 0;
4836 }
4837 
4838 static int resource_list_apply_handler(const struct ast_sorcery *sorcery, void *obj)
4839 {
4840  struct resource_list *list = obj;
4841 
4842  if (ast_strlen_zero(list->event)) {
4843  ast_log(LOG_WARNING, "Resource list '%s' has no event set\n",
4845  return -1;
4846  }
4847 
4848  if (AST_VECTOR_SIZE(&list->items) == 0) {
4849  ast_log(LOG_WARNING, "Resource list '%s' has no list items\n",
4851  return -1;
4852  }
4853 
4854  return 0;
4855 }
4856 
4858 {
4859  ast_sorcery_apply_default(sorcery, "resource_list", "config",
4860  "pjsip.conf,criteria=type=resource_list");
4861  if (ast_sorcery_object_register(sorcery, "resource_list", resource_list_alloc,
4863  return -1;
4864  }
4865 
4866  ast_sorcery_object_field_register(sorcery, "resource_list", "type", "",
4867  OPT_NOOP_T, 0, 0);
4868  ast_sorcery_object_field_register(sorcery, "resource_list", "event", "",
4870  ast_sorcery_object_field_register(sorcery, "resource_list", "full_state", "no",
4871  OPT_BOOL_T, 1, FLDSET(struct resource_list, full_state));
4872  ast_sorcery_object_field_register(sorcery, "resource_list", "notification_batch_interval",
4874  ast_sorcery_object_field_register_custom(sorcery, "resource_list", "list_item",
4876 
4877  ast_sorcery_reload_object(sorcery, "resource_list");
4878 
4879  return 0;
4880 }
4881 
4882 #ifdef TEST_FRAMEWORK
4883 
4884 /*!
4885  * \brief "bad" resources
4886  *
4887  * These are resources that the test handler will reject subscriptions to.
4888  */
4889 const char *bad_resources[] = {
4890  "coconut",
4891  "cilantro",
4892  "olive",
4893  "cheese",
4894 };
4895 
4896 /*!
4897  * \brief new_subscribe callback for unit tests
4898  *
4899  * Will give a 200 OK response to any resource except the "bad" ones.
4900  */
4901 static int test_new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource)
4902 {
4903  int i;
4904 
4905  for (i = 0; i < ARRAY_LEN(bad_resources); ++i) {
4906  if (!strcmp(resource, bad_resources[i])) {
4907  return 400;
4908  }
4909  }
4910 
4911  return 200;
4912 }
4913 
4914 /*!
4915  * \brief Subscription notifier for unit tests.
4916  *
4917  * Since unit tests are only concerned with building a resource tree,
4918  * only the new_subscribe callback needs to be defined.
4919  */
4922 };
4923 
4924 /*!
4925  * \brief Subscription handler for unit tests.
4926  */
4928  .event_name = "test",
4929  .notifier = &test_notifier,
4930 };
4931 
4932 /*!
4933  * \brief Set properties on an allocated resource list
4934  *
4935  * \param list The list to set details on.
4936  * \param event The list's event.
4937  * \param resources Array of resources to add to the list.
4938  * \param num_resources Number of resources in the array.
4939  * \retval 0 Success
4940  * \retval non-zero Failure
4941  */
4942 static int populate_list(struct resource_list *list, const char *event, const char **resources, size_t num_resources)
4943 {
4944  int i;
4945 
4946  ast_copy_string(list->event, event, sizeof(list->event));
4947 
4948  for (i = 0; i < num_resources; ++i) {
4949  char *resource = ast_strdup(resources[i]);
4950 
4951  if (!resource || AST_VECTOR_APPEND(&list->items, resource)) {
4952  ast_free(resource);
4953  return -1;
4954  }
4955  }
4956  return 0;
4957 }
4958 
4959 /*!
4960  * \brief RAII callback to destroy a resource list
4961  */
4962 static void cleanup_resource_list(struct resource_list *list)
4963 {
4964  if (!list) {
4965  return;
4966  }
4967 
4969  ao2_cleanup(list);
4970 }
4971 
4972 /*!
4973  * \brief allocate a resource list, store it in sorcery, and set its details
4974  *
4975  * \param test The unit test. Used for logging status messages.
4976  * \param list_name The name of the list to create.
4977  * \param event The event the list services
4978  * \param resources Array of resources to apply to the list
4979  * \param num_resources The number of resources in the array
4980  * \retval NULL Failed to allocate or populate list
4981  * \retval non-NULL The created list
4982  */
4984  const char *list_name, const char *event, const char **resources, size_t num_resources)
4985 {
4986  struct resource_list *list;
4987 
4988  list = ast_sorcery_alloc(ast_sip_get_sorcery(), "resource_list", list_name);
4989  if (!list) {
4990  ast_test_status_update(test, "Could not allocate resource list in sorcery\n");
4991  return NULL;
4992  }
4993 
4994  if (ast_sorcery_create(ast_sip_get_sorcery(), list)) {
4995  ast_test_status_update(test, "Could not store the resource list in sorcery\n");
4996  ao2_cleanup(list);
4997  return NULL;
4998  }
4999 
5000  if (populate_list(list, event, resources, num_resources)) {
5001  ast_test_status_update(test, "Could not add resources to the resource list\n");
5002  cleanup_resource_list(list);
5003  return NULL;
5004  }
5005 
5006  return list;
5007 }
5008 
5009 /*!
5010  * \brief Check the integrity of a tree node against a set of resources.
5011  *
5012  * The tree node's resources must be in the same order as the resources in
5013  * the supplied resources array. Because of this constraint, tests can misrepresent
5014  * the size of the resources array as being smaller than it really is if resources
5015  * at the end of the array should not be present in the tree node.
5016  *
5017  * \param test The unit test. Used for printing status messages.
5018  * \param node The constructed tree node whose integrity is under question.
5019  * \param resources Array of expected resource values
5020  * \param num_resources The number of resources to check in the array.
5021  */
5022 static int check_node(struct ast_test *test, struct tree_node *node,
5023  const char **resources, size_t num_resources)
5024 {
5025  int i;
5026 
5027  if (AST_VECTOR_SIZE(&node->children) != num_resources) {
5028  ast_test_status_update(test, "Unexpected number of resources in tree. Expected %zu, got %zu\n",
5029  num_resources, AST_VECTOR_SIZE(&node->children));
5030  return -1;
5031  }
5032 
5033  for (i = 0; i < num_resources; ++i) {
5034  if (strcmp(resources[i], AST_VECTOR_GET(&node->children, i)->resource)) {
5035  ast_test_status_update(test, "Mismatched resources. Expected '%s' but got '%s'\n",
5036  resources[i], AST_VECTOR_GET(&node->children, i)->resource);
5037  return -1;
5038  }
5039  }
5040 
5041  return 0;
5042 }
5043 
5044 /*!
5045  * \brief RAII_VAR callback to destroy an allocated resource tree
5046  */
5048 {
5049  resource_tree_destroy(tree);
5050  ast_free(tree);
5051 }
5052 
5054 {
5055  struct ast_config *config;
5056  struct ast_flags flags = {0,};
5057  const char *value;
5058 
5059  config = ast_config_load("sorcery.conf", flags);
5060  if (!config) {
5061  return 1;
5062  }
5063 
5064  value = ast_variable_retrieve(config, "res_pjsip_pubsub", "resource_list");
5065  if (ast_strlen_zero(value)) {
5066  ast_config_destroy(config);
5067  return 1;
5068  }
5069 
5070  if (strcasecmp(value, "memory") && strcasecmp(value, "astdb")) {
5071  ast_config_destroy(config);
5072  return 1;
5073  }
5074 
5075  return 0;
5076 }
5077 
5079 {
5082  const char *resources[] = {
5083  "huey",
5084  "dewey",
5085  "louie",
5086  };
5087  int resp;
5088 
5089  switch (cmd) {
5090  case TEST_INIT:
5091  info->name = "resource_tree";
5092  info->category = "/res/res_pjsip_pubsub/";
5093  info->summary = "Basic resource tree integrity check";
5094  info->description =
5095  "Create a resource list and ensure that our attempt to build a tree works as expected.";
5096  return AST_TEST_NOT_RUN;
5097  case TEST_EXECUTE:
5098  break;
5099  }
5100 
5101  if (ineligible_configuration()) {
5102  ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5103  "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5104  return AST_TEST_NOT_RUN;
5105  }
5106 
5107  list = create_resource_list(test, "foo", "test", resources, ARRAY_LEN(resources));
5108  if (!list) {
5109  return AST_TEST_FAIL;
5110  }
5111 
5112  tree = ast_calloc(1, sizeof(*tree));
5113  resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5114  if (resp != 200) {
5115  ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5116  return AST_TEST_FAIL;
5117  }
5118 
5119  if (!tree->root) {
5120  ast_test_status_update(test, "Resource tree has no root\n");
5121  return AST_TEST_FAIL;
5122  }
5123 
5124  if (check_node(test, tree->root, resources, ARRAY_LEN(resources))) {
5125  return AST_TEST_FAIL;
5126  }
5127 
5128  return AST_TEST_PASS;
5129 }
5130 
5131 AST_TEST_DEFINE(complex_resource_tree)
5132 {
5133  RAII_VAR(struct resource_list *, list_1, NULL, cleanup_resource_list);
5134  RAII_VAR(struct resource_list *, list_2, NULL, cleanup_resource_list);
5136  const char *resources_1[] = {
5137  "huey",
5138  "dewey",
5139  "louie",
5140  "dwarves",
5141  };
5142  const char *resources_2[] = {
5143  "happy",
5144  "grumpy",
5145  "doc",
5146  "bashful",
5147  "dopey",
5148  "sneezy",
5149  "sleepy",
5150  };
5151  int resp;
5152  struct tree_node *node;
5153 
5154  switch (cmd) {
5155  case TEST_INIT:
5156  info->name = "complex_resource_tree";
5157  info->category = "/res/res_pjsip_pubsub/";
5158  info->summary = "Complex resource tree integrity check";
5159  info->description =
5160  "Create a complex resource list and ensure that our attempt to build a tree works as expected.";
5161  return AST_TEST_NOT_RUN;
5162  case TEST_EXECUTE:
5163  break;
5164  }
5165 
5166  if (ineligible_configuration()) {
5167  ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5168  "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5169  return AST_TEST_NOT_RUN;
5170  }
5171 
5172  list_1 = create_resource_list(test, "foo", "test", resources_1, ARRAY_LEN(resources_1));
5173  if (!list_1) {
5174  return AST_TEST_FAIL;
5175  }
5176 
5177  list_2 = create_resource_list(test, "dwarves", "test", resources_2, ARRAY_LEN(resources_2));
5178  if (!list_2) {
5179  return AST_TEST_FAIL;
5180  }
5181 
5182  tree = ast_calloc(1, sizeof(*tree));
5183  resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5184  if (resp != 200) {
5185  ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5186  return AST_TEST_FAIL;
5187  }
5188 
5189  if (!tree->root) {
5190  ast_test_status_update(test, "Resource tree has no root\n");
5191  return AST_TEST_FAIL;
5192  }
5193 
5194  node = tree->root;
5195  if (check_node(test, node, resources_1, ARRAY_LEN(resources_1))) {
5196  return AST_TEST_FAIL;
5197  }
5198 
5199  /* The embedded list is at index 3 in the root node's children */
5200  node = AST_VECTOR_GET(&node->children, 3);
5201  if (check_node(test, node, resources_2, ARRAY_LEN(resources_2))) {
5202  return AST_TEST_FAIL;
5203  }
5204 
5205  return AST_TEST_PASS;
5206 }
5207 
5208 AST_TEST_DEFINE(bad_resource)
5209 {
5212  const char *resources[] = {
5213  "huey",
5214  "dewey",
5215  "louie",
5216  "coconut", /* A "bad" resource */
5217  };
5218  int resp;
5219 
5220  switch (cmd) {
5221  case TEST_INIT:
5222  info->name = "bad_resource";
5223  info->category = "/res/res_pjsip_pubsub/";
5224  info->summary = "Ensure bad resources do not end up in the tree";
5225  info->description =
5226  "Create a resource list with a single bad resource. Ensure the bad resource does not end up in the tree.";
5227  return AST_TEST_NOT_RUN;
5228  case TEST_EXECUTE:
5229  break;
5230  }
5231 
5232  if (ineligible_configuration()) {
5233  ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5234  "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5235  return AST_TEST_NOT_RUN;
5236  }
5237 
5238  list = create_resource_list(test, "foo", "test", resources, ARRAY_LEN(resources));
5239  if (!list) {
5240  return AST_TEST_FAIL;
5241  }
5242 
5243  tree = ast_calloc(1, sizeof(*tree));
5244  resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5245  if (resp != 200) {
5246  ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5247  return AST_TEST_FAIL;
5248  }
5249 
5250  if (!tree->root) {
5251  ast_test_status_update(test, "Resource tree has no root\n");
5252  return AST_TEST_FAIL;
5253  }
5254 
5255  /* We check against all but the final resource since we expect it not to be in the tree */
5256  if (check_node(test, tree->root, resources, ARRAY_LEN(resources) - 1)) {
5257  return AST_TEST_FAIL;
5258  }
5259 
5260  return AST_TEST_PASS;
5261 
5262 }
5263 
5264 AST_TEST_DEFINE(bad_branch)
5265 {
5266  RAII_VAR(struct resource_list *, list_1, NULL, cleanup_resource_list);
5267  RAII_VAR(struct resource_list *, list_2, NULL, cleanup_resource_list);
5269  const char *resources_1[] = {
5270  "huey",
5271  "dewey",
5272  "louie",
5273  "gross",
5274  };
5275  /* This list has nothing but bad resources */
5276  const char *resources_2[] = {
5277  "coconut",
5278  "cilantro",
5279  "olive",
5280  "cheese",
5281  };
5282  int resp;
5283 
5284  switch (cmd) {
5285  case TEST_INIT:
5286  info->name = "bad_branch";
5287  info->category = "/res/res_pjsip_pubsub/";
5288  info->summary = "Ensure bad branches are pruned from the tree";
5289  info->description =
5290  "Create a resource list that makes a tree with an entire branch of bad resources.\n"
5291  "Ensure the bad branch is pruned from the tree.";
5292  return AST_TEST_NOT_RUN;
5293  case TEST_EXECUTE:
5294  break;
5295  }
5296 
5297  if (ineligible_configuration()) {
5298  ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5299  "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5300  return AST_TEST_NOT_RUN;
5301  }
5302 
5303  list_1 = create_resource_list(test, "foo", "test", resources_1, ARRAY_LEN(resources_1));
5304  if (!list_1) {
5305  return AST_TEST_FAIL;
5306  }
5307  list_2 = create_resource_list(test, "gross", "test", resources_2, ARRAY_LEN(resources_2));
5308  if (!list_2) {
5309  return AST_TEST_FAIL;
5310  }
5311 
5312  tree = ast_calloc(1, sizeof(*tree));
5313  resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5314  if (resp != 200) {
5315  ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5316  return AST_TEST_FAIL;
5317  }
5318 
5319  if (!tree->root) {
5320  ast_test_status_update(test, "Resource tree has no root\n");
5321  return AST_TEST_FAIL;
5322  }
5323 
5324  /* We check against all but the final resource of the list since the entire branch should
5325  * be pruned from the tree
5326  */
5327  if (check_node(test, tree->root, resources_1, ARRAY_LEN(resources_1) - 1)) {
5328  return AST_TEST_FAIL;
5329  }
5330 
5331  return AST_TEST_PASS;
5332 
5333 }
5334 
5335 AST_TEST_DEFINE(duplicate_resource)
5336 {
5337  RAII_VAR(struct resource_list *, list_1, NULL, cleanup_resource_list);
5338  RAII_VAR(struct resource_list *, list_2, NULL, cleanup_resource_list);
5340  const char *resources_1[] = {
5341  "huey",
5342  "ducks",
5343  "dewey",
5344  "louie",
5345  };
5346  const char *resources_2[] = {
5347  "donald",
5348  "daisy",
5349  "scrooge",
5350  "dewey",
5351  "louie",
5352  "huey",
5353  };
5354  int resp;
5355  struct tree_node *node;
5356 
5357  switch (cmd) {
5358  case TEST_INIT:
5359  info->name = "duplicate_resource";
5360  info->category = "/res/res_pjsip_pubsub/";
5361  info->summary = "Ensure duplicated resources do not end up in the tree";
5362  info->description =
5363  "Create a resource list with a single duplicated resource. Ensure the duplicated resource does not end up in the tree.";
5364  return AST_TEST_NOT_RUN;
5365  case TEST_EXECUTE:
5366  break;
5367  }
5368 
5369  if (ineligible_configuration()) {
5370  ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5371  "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5372  return AST_TEST_NOT_RUN;
5373  }
5374 
5375  list_1 = create_resource_list(test, "foo", "test", resources_1, ARRAY_LEN(resources_1));
5376  if (!list_1) {
5377  return AST_TEST_FAIL;
5378  }
5379 
5380  list_2 = create_resource_list(test, "ducks", "test", resources_2, ARRAY_LEN(resources_2));
5381  if (!list_2) {
5382  return AST_TEST_FAIL;
5383  }
5384 
5385  tree = ast_calloc(1, sizeof(*tree));
5386  resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5387  if (resp != 200) {
5388  ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5389  return AST_TEST_FAIL;
5390  }
5391 
5392  if (!tree->root) {
5393  ast_test_status_update(test, "Resource tree has no root\n");
5394  return AST_TEST_FAIL;
5395  }
5396 
5397  node = tree->root;
5398  /* This node should have "huey" and "ducks". "dewey" and "louie" should not
5399  * be present since they were found in the "ducks" list.
5400  */
5401  if (check_node(test, node, resources_1, ARRAY_LEN(resources_1) - 2)) {
5402  return AST_TEST_FAIL;
5403  }
5404 
5405  /* This node should have "donald", "daisy", "scrooge", "dewey", and "louie".
5406  * "huey" is not here since that was already encountered in the parent list
5407  */
5408  node = AST_VECTOR_GET(&node->children, 1);
5409  if (check_node(test, node, resources_2, ARRAY_LEN(resources_2) - 1)) {
5410  return AST_TEST_FAIL;
5411  }
5412 
5413  return AST_TEST_PASS;
5414 }
5415 
5417 {
5418  RAII_VAR(struct resource_list *, list_1, NULL, cleanup_resource_list);
5419  RAII_VAR(struct resource_list *, list_2, NULL, cleanup_resource_list);
5421  const char *resources_1[] = {
5422  "derp",
5423  };
5424  const char *resources_2[] = {
5425  "herp",
5426  };
5427  int resp;
5428 
5429  switch (cmd) {
5430  case TEST_INIT:
5431  info->name = "loop";
5432  info->category = "/res/res_pjsip_pubsub/";
5433  info->summary = "Test that loops are properly detected.";
5434  info->description =
5435  "Create two resource lists that refer to each other. Ensure that attempting to build a tree\n"
5436  "results in an empty tree.";
5437  return AST_TEST_NOT_RUN;
5438  case TEST_EXECUTE:
5439  break;
5440  }
5441 
5442  if (ineligible_configuration()) {
5443  ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5444  "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5445  return AST_TEST_NOT_RUN;
5446  }
5447 
5448  list_1 = create_resource_list(test, "herp", "test", resources_1, ARRAY_LEN(resources_1));
5449  if (!list_1) {
5450  return AST_TEST_FAIL;
5451  }
5452  list_2 = create_resource_list(test, "derp", "test", resources_2, ARRAY_LEN(resources_2));
5453  if (!list_2) {
5454  return AST_TEST_FAIL;
5455  }
5456 
5457  tree = ast_calloc(1, sizeof(*tree));
5458  resp = build_resource_tree(NULL, &test_handler, "herp", tree, 1);
5459  if (resp == 200) {
5460  ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5461  return AST_TEST_FAIL;
5462  }
5463 
5464  return AST_TEST_PASS;
5465 }
5466 
5468 {
5471  const char *resources[] = {
5472  "huey",
5473  "dewey",
5474  "louie",
5475  };
5476  int resp;
5477 
5478  switch (cmd) {
5479  case TEST_INIT:
5480  info->name = "bad_event";
5481  info->category = "/res/res_pjsip_pubsub/";
5482  info->summary = "Ensure that list with wrong event specified is not retrieved";
5483  info->description =
5484  "Create a simple resource list for event 'tsetse'. Ensure that trying to retrieve the list for event 'test' fails.";
5485  return AST_TEST_NOT_RUN;
5486  case TEST_EXECUTE:
5487  break;
5488  }
5489 
5490  if (ineligible_configuration()) {
5491  ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5492  "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5493  return AST_TEST_NOT_RUN;
5494  }
5495 
5496  list = create_resource_list(test, "foo", "tsetse", resources, ARRAY_LEN(resources));
5497  if (!list) {
5498  return AST_TEST_FAIL;
5499  }
5500 
5501  tree = ast_calloc(1, sizeof(*tree));
5502  /* Since the test_handler is for event "test", this should not build a list, but
5503  * instead result in a single resource being created, called "foo"
5504  */
5505  resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1);
5506  if (resp != 200) {
5507  ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5508  return AST_TEST_FAIL;
5509  }
5510 
5511  if (!tree->root) {
5512  ast_test_status_update(test, "Resource tree has no root\n");
5513  return AST_TEST_FAIL;
5514  }
5515 
5516  if (strcmp(tree->root->resource, "foo")) {
5517  ast_test_status_update(test, "Unexpected resource %s found in tree\n", tree->root->resource);
5518  return AST_TEST_FAIL;
5519  }
5520 
5521  return AST_TEST_PASS;
5522 }
5523 
5524 #endif
5525 
5526 static int resource_endpoint_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
5527 {
5528  struct ast_sip_publication_resource *resource = obj;
5529 
5530  ast_free(resource->endpoint);
5531  resource->endpoint = ast_strdup(var->value);
5532 
5533  return 0;
5534 }
5535 
5536 static int resource_event_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
5537 {
5538  struct ast_sip_publication_resource *resource = obj;
5539  /* The event configuration name starts with 'event_' so skip past it to get the real name */
5540  const char *event = var->name + 6;
5541  struct ast_variable *item;
5542 
5543  if (ast_strlen_zero(event) || ast_strlen_zero(var->value)) {
5544  return -1;
5545  }
5546 
5547  item = ast_variable_new(event, var->value, "");
5548  if (!item) {
5549  return -1;
5550  }
5551 
5552  if (resource->events) {
5553  item->next = resource->events;
5554  }
5555  resource->events = item;
5556 
5557  return 0;
5558 }
5559 
5560 static int load_module(void)
5561 {
5562  static const pj_str_t str_PUBLISH = { "PUBLISH", 7 };
5563  struct ast_sorcery *sorcery;
5564 
5565  sorcery = ast_sip_get_sorcery();
5566 
5567  if (!(sched = ast_sched_context_create())) {
5568  ast_log(LOG_ERROR, "Could not create scheduler for publication expiration\n");
5569  return AST_MODULE_LOAD_DECLINE;
5570  }
5571 
5572  if (ast_sched_start_thread(sched)) {
5573  ast_log(LOG_ERROR, "Could not start scheduler thread for publication expiration\n");
5575  return AST_MODULE_LOAD_DECLINE;
5576  }
5577 
5578  ast_sorcery_apply_config(sorcery, "res_pjsip_pubsub");
5579  ast_sorcery_apply_default(sorcery, "subscription_persistence", "astdb", "subscription_persistence");
5580  if (ast_sorcery_object_register(sorcery, "subscription_persistence", subscription_persistence_alloc,
5581  NULL, NULL)) {
5582  ast_log(LOG_ERROR, "Could not register subscription persistence object support\n");
5584  return AST_MODULE_LOAD_DECLINE;
5585  }
5586  ast_sorcery_object_field_register(sorcery, "subscription_persistence", "packet", "", OPT_CHAR_ARRAY_T, 0,
5587  CHARFLDSET(struct subscription_persistence, packet));
5588  ast_sorcery_object_field_register(sorcery, "subscription_persistence", "src_name", "", OPT_CHAR_ARRAY_T, 0,
5589  CHARFLDSET(struct subscription_persistence, src_name));
5590  ast_sorcery_object_field_register(sorcery, "subscription_persistence", "src_port", "0", OPT_UINT_T, 0,
5591  FLDSET(struct subscription_persistence, src_port));
5592  ast_sorcery_object_field_register(sorcery, "subscription_persistence", "transport_key", "0", OPT_CHAR_ARRAY_T, 0,
5593  CHARFLDSET(struct subscription_persistence, transport_key));
5594  ast_sorcery_object_field_register(sorcery, "subscription_persistence", "local_name", "", OPT_CHAR_ARRAY_T, 0,
5595  CHARFLDSET(struct subscription_persistence, local_name));
5596  ast_sorcery_object_field_register(sorcery, "subscription_persistence", "local_port", "0", OPT_UINT_T, 0,
5597  FLDSET(struct subscription_persistence, local_port));
5598  ast_sorcery_object_field_register(sorcery, "subscription_persistence", "cseq", "0", OPT_UINT_T, 0,
5599  FLDSET(struct subscription_persistence, cseq));
5600  ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "endpoint", "",
5602  ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "tag", "",
5604  ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "expires", "",
5606  ast_sorcery_object_field_register(sorcery, "subscription_persistence", "contact_uri", "", OPT_CHAR_ARRAY_T, 0,
5607  CHARFLDSET(struct subscription_persistence, contact_uri));
5608  ast_sorcery_object_field_register(sorcery, "subscription_persistence", "prune_on_boot", "no", OPT_YESNO_T, 1,
5609  FLDSET(struct subscription_persistence, prune_on_boot));
5610  ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "generator_data", "",
5612 
5613  if (apply_list_configuration(sorcery)) {
5615  return AST_MODULE_LOAD_DECLINE;
5616  }
5617 
5618  ast_sorcery_apply_default(sorcery, "inbound-publication", "config", "pjsip.conf,criteria=type=inbound-publication");
5619  if (ast_sorcery_object_register(sorcery, "inbound-publication", publication_resource_alloc,
5620  NULL, NULL)) {
5621  ast_log(LOG_ERROR, "Could not register subscription persistence object support\n");
5623  return AST_MODULE_LOAD_DECLINE;
5624  }
5625  ast_sorcery_object_field_register(sorcery, "inbound-publication", "type", "", OPT_NOOP_T, 0, 0);
5626  ast_sorcery_object_field_register_custom(sorcery, "inbound-publication", "endpoint", "",
5628  ast_sorcery_object_fields_register(sorcery, "inbound-publication", "^event_", resource_event_handler, NULL);
5629  ast_sorcery_reload_object(sorcery, "inbound-publication");
5630 
5632  ast_log(LOG_ERROR, "Could not register pubsub service\n");
5634  return AST_MODULE_LOAD_DECLINE;
5635  }
5636 
5637  if (pjsip_evsub_init_module(ast_sip_get_pjsip_endpoint()) != PJ_SUCCESS) {
5638  ast_log(LOG_ERROR, "Could not initialize pjsip evsub module.\n");
5641  return AST_MODULE_LOAD_DECLINE;
5642  }
5643 
5644  /* Once pjsip_evsub_init_module succeeds we cannot unload.
5645  * Keep all module_load errors above this point. */
5647 
5648  pjsip_media_type_init2(&rlmi_media_type, "application", "rlmi+xml");
5649 
5650  pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &str_PUBLISH);
5651 
5654  } else {
5655  struct stasis_subscription *sub;
5656 
5660  }
5661 
5666  ast_manager_register_xml("PJSIPShowResourceLists", EVENT_FLAG_SYSTEM,
5668 
5669  ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
5670 
5672  AST_TEST_REGISTER(complex_resource_tree);
5673  AST_TEST_REGISTER(bad_resource);
5674  AST_TEST_REGISTER(bad_branch);
5675  AST_TEST_REGISTER(duplicate_resource);
5676  AST_TEST_REGISTER(loop);
5677  AST_TEST_REGISTER(bad_event);
5678 
5679  return AST_MODULE_LOAD_SUCCESS;
5680 }
5681 
5682 static int unload_module(void)
5683 {
5685  AST_TEST_UNREGISTER(complex_resource_tree);
5686  AST_TEST_UNREGISTER(bad_resource);
5687  AST_TEST_UNREGISTER(bad_branch);
5688  AST_TEST_UNREGISTER(duplicate_resource);
5689  AST_TEST_UNREGISTER(loop);
5690  AST_TEST_UNREGISTER(bad_event);
5691 
5693 
5694  ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
5695 
5698  ast_manager_unregister("PJSIPShowResourceLists");
5699 
5701  if (sched) {
5703  }
5704 
5705  return 0;
5706 }
5707 
5709  .support_level = AST_MODULE_SUPPORT_CORE,
5710  .load = load_module,
5711  .unload = unload_module,
5712  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
5713  .requires = "res_pjsip",
5714 );
void(* to_string)(void *body, struct ast_str **str)
Convert the body to a string.
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
Definition: sched.c:195
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
struct ast_variable * next
struct ast_sip_subscription * ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler, struct ast_sip_endpoint *endpoint, const char *resource)
Create a new ast_sip_subscription structure.
const char * event_name
The name of the event this handler deals with.
static const char type[]
Definition: chan_ooh323.c:109
enum sip_cc_notify_state state
Definition: chan_sip.c:959
static struct ast_sip_publish_handler * find_pub_handler(const char *event)
void *(* get_notify_data)(struct ast_sip_subscription *sub)
Supply data needed to create a NOTIFY body.
static struct tree_node * tree_node_alloc(const char *resource, struct resources *visited, unsigned int full_state)
Allocate a tree node.
static void subscription_tree_destructor(void *obj)
static int generate_initial_notify(struct ast_sip_subscription *sub)
Definition: test_heap.c:38
#define AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(str)
Truncate the URI user field options string if enabled.
Definition: res_pjsip.h:3036
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
char data[0]
Data containing the above.
char * str
Subscriber phone number (Malloced)
Definition: channel.h:292
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
struct ast_sip_subscription * root
SORCERY_OBJECT(details)
void ast_taskprocessor_build_name(char *buf, unsigned int size, const char *format,...)
Build a taskprocessor name with a sequence number on the end.
static void pubsub_on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
Called whenever an in-dialog SUBSCRIBE is received.
static struct resource_list * create_resource_list(struct ast_test *test, const char *list_name, const char *event, const char **resources, size_t num_resources)
allocate a resource list, store it in sorcery, and set its details
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3080
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static pj_bool_t pubsub_on_rx_publish_request(pjsip_rx_data *rdata)
sip_subscription_tree_state
The state of the subscription tree.
static struct ast_sip_pubsub_body_generator * find_body_generator_type_subtype_nolock(const char *type, const char *subtype)
static int ami_subscription_detail_inbound(struct sip_subscription_tree *sub_tree, void *arg)
int ast_sip_publication_add_datastore(struct ast_sip_publication *publication, struct ast_datastore *datastore)
Add a datastore to a SIP publication.
static int serialized_send_notify(void *userdata)
#define CLI_SHOW_SUB_FORMAT_ENTRY
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
Definition: linkedlists.h:332
static int sched_cb(const void *data)
#define AMI_SHOW_SUBSCRIPTIONS_OUTBOUND
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
static pjsip_media_type rlmi_media_type
static void remove_subscription(struct sip_subscription_tree *obj)
static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
Opaque structure representing an RFC 3265 SIP subscription.
char * config
Definition: conf2ael.c:66
struct ast_sip_subscriber * subscriber
static void publication_destroy_fn(void *obj)
Internal destructor for publications.
media_type
Media types generate different "dummy answers" for not accepting the offer of a media stream...
Definition: sip.h:489
struct ast_cli_entry * e
static int rlmi_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size)
void ast_sip_unregister_subscription_handler(struct ast_sip_subscription_handler *handler)
Unregister a subscription handler.
struct sip_subscription_tree * tree
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
static struct pjsip_module pubsub_module
struct ast_taskprocessor * ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub)
Get the serializer for the subscription.
Pubsub body generator.
A multipart body part and meta-information.
#define ast_test_flag(p, flag)
Definition: utils.h:63
static void subscription_persistence_destroy(void *obj)
Destructor for subscription persistence.
const char * type
Content type In "plain/text", "plain" is the type.
struct ast_sip_endpoint * endpoint
struct ao2_container * ast_sip_publication_get_datastores(const struct ast_sip_publication *publication)
Get the datastores container for a publication.
static struct subscription_persistence * subscription_persistence_create(struct sip_subscription_tree *sub_tree)
Function which creates initial persistence information of a subscription in sorcery.
const char * accept_exceptions[]
Accept headers that are exceptions to the rule.
const char * body_type
static int cli_show_subscriptions_outbound(struct sip_subscription_tree *sub_tree, void *arg)
Data used to create bodies for NOTIFY/PUBLISH requests.
const pjsip_method pjsip_publish_method
Defined method for PUBLISH.
static void add_subscription(struct sip_subscription_tree *obj)
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_party_name name
Subscriber name.
Definition: channel.h:341
#define OBJ_KEY
Definition: astobj2.h:1155
const struct message * m
Definition: res_pjsip.h:2741
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
int ast_sip_sched_task_cancel(struct ast_sip_sched_task *schtd)
Cancels the next invocation of a task.
void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor *serializer)
Set a serializer on a SIP dialog so requests and responses are automatically serialized.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
static int persistence_generator_data_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition: manager.c:3237
static int parse_simple_message_summary(char *body, struct simple_message_summary *summary)
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
static void build_node_children(struct ast_sip_endpoint *endpoint, const struct ast_sip_subscription_handler *handler, struct resource_list *list, struct tree_node *parent, struct resources *visited)
Build child nodes for a given parent.
enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb, void *ao2_data)
Register a reliable transport shutdown monitor callback.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
static int initial_notify_task(void *obj)
static void build_body_part(pj_pool_t *pool, struct ast_sip_subscription *sub, struct body_part_list *parts, unsigned int use_full_state)
Create a multipart body part for a subscribed resource.
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
struct ast_sip_notifier * notifier
void ast_json_free(void *p)
Asterisk&#39;s custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
void * arg
Definition: res_pjsip.h:2745
AMI variable container.
Definition: res_pjsip.h:2737
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
char local_name[PJ_INET6_ADDRSTRLEN]
const char * accept[AST_SIP_MAX_ACCEPT]
#define DEFAULT_EXPIRES
Default expiration for subscriptions.
static int publish_expire(const void *data)
Party identification options for endpoints.
Definition: res_pjsip.h:628
Structure for variables, used for configurations and for channel variables.
static pj_pool_t * pool
Global memory pool for configuration and timers.
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
Set an endpoint on a SIP dialog so in-dialog requests do not undergo endpoint lookup.
static int sub_tree_subscription_terminate_cb(void *data)
static int ami_show_resource_lists(struct mansession *s, const struct message *m)
#define RESOURCE_LIST_INIT_SIZE
static int persistence_endpoint_struct2str(const void *obj, const intptr_t *args, char **buf)
#define var
Definition: ast_expr2f.c:614
struct ast_json * json
Definition: json.h:1025
pj_xml_attr * ast_sip_presence_xml_create_attr(pj_pool_t *pool, pj_xml_node *node, const char *name, const char *value)
Create XML attribute.
Definition: presence_xml.c:140
static const char * sip_subscription_roles_map[]
static int format_ami_resource_lists(void *obj, void *arg, int flags)
static struct ast_sip_pubsub_body_generator * find_body_generator_accept(const char *accept)
void ast_sip_pubsub_unregister_body_generator(struct ast_sip_pubsub_body_generator *generator)
Unregister a body generator with the pubsub core.
Universally unique identifier support.
int ast_sip_register_publish_handler(struct ast_sip_publish_handler *handler)
Register a publish handler.
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:763
struct ast_cli_args * a
static void * publication_resource_alloc(const char *name)
Allocator for publication resource.
Test Framework API.
Perform no matching, return all objects.
Definition: sorcery.h:123
int ast_datastores_add(struct ao2_container *datastores, struct ast_datastore *datastore)
Add a data store to a container.
Definition: datastore.c:101
static void subscription_persistence_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Event callback which fires subscription persistence recreation when the system is fully booted...
int(* new_publication)(struct ast_sip_endpoint *endpoint, const char *resource, const char *event_configuration)
Called when a PUBLISH to establish a new publication arrives.
static int publish_expire_callback(void *data)
#define CLI_LIST_SUB_FORMAT_ENTRY
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
Definition: cli.h:152
int ast_sorcery_object_fields_register(struct ast_sorcery *sorcery, const char *type, const char *regex, aco_option_handler config_handler, sorcery_fields_handler sorcery_handler)
Register a regex for multiple fields within an object.
Definition: sorcery.c:1160
if(!yyg->yy_init)
Definition: ast_expr2f.c:868
#define CHARFLDSET(type, field)
A helper macro to pass the appropriate arguments to aco_option_register for OPT_CHAR_ARRAY_T.
Full structure for sorcery.
Definition: sorcery.c:230
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
Type for a default handler that should do nothing.
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition: stasis.c:1079
static void * subscription_persistence_alloc(const char *name)
Allocator for subscription persistence.
Structure for a data store type.
Definition: datastore.h:31
char * str
Subscriber name (Malloced)
Definition: channel.h:265
int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator *generator)
Register a body generator with the pubsub core.
#define AST_SCHED_DEL_UNREF(sched, id, refcall)
schedule task to get deleted and call unref function
Definition: sched.h:80
const char * resource
Definition: astman.c:222
struct tree_node * root
Structure used for persisting an inbound subscription.
#define CLI_LIST_SUB_FORMAT_HEADER
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
const char * ast_sip_publication_get_event_configuration(const struct ast_sip_publication *pub)
Given a publication, get the configuration name for the event type in use.
static struct resource_list * retrieve_resource_list(const char *resource, const char *event)
Helper function for retrieving a resource list for a given event.
pjsip_transport * transport
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
static pjsip_evsub_user pubsub_cb
static struct aco_type item
Definition: test_config.c:1463
const char * type
Content type In "plain/text", "plain" is the type.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
static int persistence_tag_struct2str(const void *obj, const intptr_t *args, char **buf)
#define ast_sorcery_apply_config(sorcery, name)
Definition: sorcery.h:456
Return all matching objects.
Definition: sorcery.h:120
static int persistence_endpoint_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
struct ast_json * ast_json_load_string(const char *input, struct ast_json_error *error)
Parse null terminated string into a JSON object or array.
Definition: json.c:546
#define ast_assert(a)
Definition: utils.h:695
#define DEFAULT_PUBLISH_EXPIRES
Default expiration time for PUBLISH if one is not specified.
char * text
Definition: app_queue.c:1508
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:98
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Structure for a data store object.
Definition: datastore.h:68
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
Copy a pj_str_t into a standard character buffer.
Definition: res_pjsip.c:5240
const char * str
Definition: app_jack.c:147
const char * args
static int cli_list_subscriptions_outbound(struct sip_subscription_tree *sub_tree, void *arg)
#define NULL
Definition: resample.c:96
void(* to_ami)(struct ast_sip_subscription *sub, struct ast_str **buf)
Converts the subscriber to AMI.
static int apply_list_configuration(struct ast_sorcery *sorcery)
AST_VECTOR(resources, const char *)
A vector of strings commonly used throughout this module.
char * end
Definition: eagi_proxy.c:73
char packet[PJSIP_MAX_PKT_LEN]
int value
Definition: syslog.c:37
static void sub_add_handler(struct ast_sip_subscription_handler *handler)
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
struct subscription_persistence * persistence
int ast_sip_subscription_is_terminated(const struct ast_sip_subscription *sub)
Get whether the subscription has been terminated or not.
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
Definition: taskprocessor.h:60
int ast_sip_push_task_wait_servant(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Push a task to SIP servants and wait for it to complete.
Definition: res_pjsip.c:5204
int ast_get_timeval(const char *src, struct timeval *tv, struct timeval _default, int *consumed)
get values from config variables.
Definition: main/utils.c:2171
A node for a resource tree.
static int persistence_expires_struct2str(const void *obj, const intptr_t *args, char **buf)
JSON parsing error information.
Definition: json.h:829
static pjsip_require_hdr * create_require_eventlist(pj_pool_t *pool)
Shortcut method to create a Require: eventlist header.
static int ami_show_subscriptions_inbound(struct mansession *s, const struct message *m)
#define MAX_REGEX_ERROR_LEN
static struct ast_cli_entry cli_commands[]
struct ast_sip_subscription_handler test_handler
Subscription handler for unit tests.
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:755
enum ast_sip_subscription_role role
char message_account[PJSIP_MAX_URL_SIZE]
#define ast_publish_mwi_state(mailbox, context, new_msgs, old_msgs)
Publish a MWI state update via stasis.
Definition: mwi.h:380
int ast_sip_add_header(pjsip_tx_data *tdata, const char *name, const char *value)
Add a header to an outbound SIP message.
Definition: res_pjsip.c:5063
Resource list configuration item.
struct ast_datastore * ast_sip_publication_get_datastore(struct ast_sip_publication *publication, const char *name)
Retrieve a publication datastore.
static int subscription_persistence_recreate(void *obj, void *arg, int flags)
Callback function to perform the actual recreation of a subscription.
const char * uid
Definition: datastore.h:69
pjsip_dialog * ast_sip_subscription_get_dialog(struct ast_sip_subscription *sub)
Get the pjsip dialog that is associated with this subscription.
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Definition: json.c:404
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:269
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:738
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:2820
struct ast_sip_pubsub_body_generator * body_generator
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:469
struct ast_datastore * ast_datastores_find(struct ao2_container *datastores, const char *name)
Find a data store in a container.
Definition: datastore.c:119
#define ast_strlen_zero(foo)
Definition: strings.h:52
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
static char * sub_tree_state_description[]
static void * resource_list_alloc(const char *name)
static int ami_subscription_detail_outbound(struct sip_subscription_tree *sub_tree, void *arg)
struct ast_sip_endpoint * ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata)
Get the looked-up endpoint on an out-of dialog request or response.
struct ao2_container * ast_datastores_alloc(void)
Allocate a specialized data stores container.
Definition: datastore.c:95
int ast_sip_subscription_add_datastore(struct ast_sip_subscription *subscription, struct ast_datastore *datastore)
Add a datastore to a SIP subscription.
struct ao2_container * datastores
unsigned int notification_batch_interval
static void add_rlmi_resource(pj_pool_t *pool, pj_xml_node *rlmi, const pjsip_generic_string_hdr *cid, const char *resource_name, const pjsip_sip_uri *resource_uri, pjsip_evsub_state state)
Add a resource XML element to an RLMI body.
static int sip_subscription_accept(struct sip_subscription_tree *sub_tree, pjsip_rx_data *rdata, int response)
Asterisk datastore objects.
#define ao2_bump(obj)
Definition: astobj2.h:491
static pjsip_multipart_part * build_rlmi_body(pj_pool_t *pool, struct ast_sip_subscription *sub, struct body_part_list *body_parts, unsigned int full_state)
Create an RLMI body part for a multipart resource list body.
static int serialized_pubsub_on_refresh_timeout(void *userdata)
Type for default option handler for character array strings.
static void subscription_persistence_update(struct sip_subscription_tree *sub_tree, pjsip_rx_data *rdata, enum sip_persistence_update_type type)
Function which updates persistence information of a subscription in sorcery.
static void sip_subscription_to_ami(struct sip_subscription_tree *sub_tree, struct ast_str **buf)
static void resource_list_destructor(void *obj)
static struct body_part * allocate_body_part(pj_pool_t *pool, const struct ast_sip_subscription *sub)
Allocate and initialize a body part structure.
#define MIN(a, b)
Definition: utils.h:226
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
Retrive the remote URI for this subscription.
Type for default option handler for bools (ast_true/ast_false)
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
struct ast_sip_sched_task * ast_sip_schedule_task(struct ast_taskprocessor *serializer, int interval, ast_sip_task sip_task, const char *name, void *task_data, enum ast_sip_scheduler_task_flags flags)
Schedule a task to run in the res_pjsip thread pool.
const char * subtype
Content subtype In "plain/text", "text" is the subtype.
static void pubsub_on_server_timeout(pjsip_evsub *sub)
static char mailbox[AST_MAX_MAILBOX_UNIQUEID]
Definition: chan_mgcp.c:204
#define EVENT_FLAG_SYSTEM
Definition: manager.h:71
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
struct stasis_message_type * ast_manager_get_generic_type(void)
Get the stasis_message_type for generic messages.
#define ast_log
Definition: astobj2.c:42
int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
Create and potentially persist an object using an available wizard.
Definition: sorcery.c:2057
void ast_sip_pubsub_unregister_body_supplement(struct ast_sip_pubsub_body_supplement *supplement)
Unregister a body generator with the pubsub core.
#define ast_sorcery_object_field_register_custom(sorcery, type, name, default_val, config_handler, sorcery_handler, multiple_handler, flags,...)
Register a field within an object with custom handlers.
Definition: sorcery.h:1005
static int exceptional_accept(const pj_str_t *accept)
Is the Accept header from the SUBSCRIBE in the list of exceptions?
#define ast_config_load(filename, flags)
Load a config file.
static int for_each_subscription(on_subscription_t on_subscription, void *arg)
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
static pjsip_msg_body * create_multipart_body(pj_pool_t *pool)
Create and initialize the PJSIP multipart body structure for a resource list subscription.
#define CLI_SHOW_SUB_FORMAT_HEADER
static int pubsub_on_refresh_timeout(void *userdata)
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
struct ast_module * self
Definition: module.h:342
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1073
struct ao2_container * datastores
#define PUBLICATIONS_BUCKETS
Number of buckets for publications (on a per handler)
A resource tree.
#define ast_sip_mod_data_set(pool, mod_data, id, key, val)
Utilizing a mod_data array for a given id, set the value associated with the given key...
Definition: res_pjsip.h:2670
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:3245
pjsip_sip_uri * ast_sip_subscription_get_sip_uri(struct ast_sip_subscription *sub)
Retrieve the local sip uri for this subscription.
pjsip_sip_uri * uri
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
Type for default option handler for unsigned integers.
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
static void cleanup_resource_list(struct resource_list *list)
RAII callback to destroy a resource list.
int(* on_subscription_t)(struct sip_subscription_tree *sub, void *arg)
static void publication_resource_destroy(void *obj)
Destructor for publication resource.
void *(* allocate_body)(void *data)
allocate body structure.
static struct ast_generator gen
const int fd
Definition: cli.h:159
void ast_sip_subscription_get_local_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
Retrieve the local URI for this subscription.
static int list_item_to_str(const void *obj, const intptr_t *args, char **buf)
static int schedule_notification(struct sip_subscription_tree *sub_tree)
static void sub_tree_transport_cb(void *data)
static int cli_list_subscriptions_detail(struct sip_subscription_tree *sub_tree, struct cli_sub_parms *cli)
const char * ast_sip_subscription_get_body_subtype(struct ast_sip_subscription *sub)
Get the body subtype used for this subscription.
void ast_sip_publication_remove_datastore(struct ast_sip_publication *publication, const char *name)
Remove a publication datastore from the publication.
const int n
Definition: cli.h:165
int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Push a task to the serializer and wait for it to complete.
Definition: res_pjsip.c:5218
const struct ast_sip_subscription_handler * handler
static int subscription_unreference_dialog(void *obj)
while(1)
Definition: ast_expr2f.c:894
static struct ast_sip_pubsub_body_generator * subscription_get_generator_from_rdata(pjsip_rx_data *rdata, const struct ast_sip_subscription_handler *handler)
Retrieve a body generator using the Accept header of an rdata message.
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
Scheduler Routines (derived from cheops)
#define AST_SIP_MAX_ACCEPT
struct ast_cli_args * a
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:85
struct ast_sip_publish_handler * next
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
In case you didn&#39;t read that giant block of text above the mansession_session struct, the struct mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1625
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:238
#define AST_RWLIST_REMOVE_CURRENT
Definition: linkedlists.h:569
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
static pjsip_msg_body * generate_notify_body(pj_pool_t *pool, struct ast_sip_subscription *root, unsigned int force_full_state)
Create the body for a NOTIFY request.
const char * ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub)
Get the name of the subscribed resource.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
struct ast_datastore * ast_datastores_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Allocate a datastore for use with the datastores container.
Definition: datastore.c:138
int(* subscription_established)(struct ast_sip_subscription *sub)
Called when an inbound subscription has been accepted.
struct mansession * s
Definition: res_pjsip.h:2739
A set of macros to manage forward-linked lists.
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
#define ast_variable_new(name, value, filename)
static void resource_tree_destroy(struct resource_tree *tree)
Destroy a resource tree.
static void publish_add_handler(struct ast_sip_publish_handler *handler)
static struct ast_sip_pubsub_body_generator * find_body_generator_type_subtype(const char *type, const char *subtype)
An entity with which Asterisk communicates.
Definition: res_pjsip.h:812
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:838
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition: res_pjsip.c:3315
const char * subtype
Content subtype In "plain/text", "text" is the subtype.
static void destroy_subscriptions(struct ast_sip_subscription *root)
struct ast_sip_pubsub_body_generator::@306 list
static force_inline char * ast_str_to_lower(char *str)
Convert a string to all lower-case.
Definition: strings.h:1268
enum sip_subscription_tree_state state
struct ast_sip_contact * ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list)
Retrieve the first bound contact from a list of AORs.
Definition: location.c:304
static char * cli_list_subscriptions_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:544
const char * bad_resources[]
"bad" resources
struct ast_sip_endpoint * ast_sip_publication_get_endpoint(struct ast_sip_publication *pub)
Given a publication, get the associated endpoint.
#define ast_sip_mod_data_get(mod_data, id, key)
Using the dictionary stored in mod_data array at a given id, retrieve the value associated with the g...
Definition: res_pjsip.h:2638
#define AST_PJSIP_XML_PROLOG_LEN
Length of the XML prolog when printing presence or other XML in PJSIP.
static pj_bool_t pubsub_on_rx_mwi_notify_request(pjsip_rx_data *rdata)
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:236
static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
Callback sequence for subscription terminate:
void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
Unregister a reliable transport shutdown monitor.
Structure representing a "virtual" SIP subscription.
int ast_sip_is_content_type(pjsip_media_type *content_type, char *type, char *subtype)
Checks if the given content type matches type/subtype.
Definition: res_pjsip.c:5259
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:196
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
const char *const * argv
Definition: cli.h:161
static struct ast_cli_entry cli[]
Definition: codec_dahdi.c:265
struct ast_sip_endpoint_id_configuration id
Definition: res_pjsip.h:847
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7258
static char * cli_complete_subscription_callid(struct ast_cli_args *a)
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
int(* publication_state_change)(struct ast_sip_publication *pub, pjsip_msg_body *body, enum ast_sip_publish_state state)
Published resource has changed states.
static int persistence_tag_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
int ast_sip_pubsub_is_body_generator_registered(const char *type, const char *subtype)
Is a body generator registered for the given type/subtype.
int(* new_subscribe)(struct ast_sip_endpoint *endpoint, const char *resource)
Called when a SUBSCRIBE arrives attempting to establish a new subscription.
static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata)
int ast_shutdown_final(void)
Definition: asterisk.c:1829
int ast_sip_pubsub_generate_body_content(const char *type, const char *subtype, struct ast_sip_body_data *data, struct ast_str **str)
Generate body content for a PUBLISH or NOTIFY.
static int publication_hash_fn(const void *obj, const int flags)
int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
Delete an object.
Definition: sorcery.c:2233
static pj_bool_t pubsub_on_rx_notify_request(pjsip_rx_data *rdata)
unsigned int expires
Expiration time of the publication.
static int resource_event_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
#define LOG_ERROR
Definition: logger.h:285
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
static int cli_show_subscription_outbound(struct sip_subscription_tree *sub_tree, void *arg)
struct ao2_container * ast_sip_subscription_get_datastores(const struct ast_sip_subscription *subscription)
Get the datastores container for a subscription.
struct ast_sip_publish_handler * handler
Handler for this publication.
struct ast_sip_pubsub_body_supplement::@307 list
static struct ast_sip_publication * publish_request_initial(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, struct ast_sip_publish_handler *handler)
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
static struct ast_generator generator
Definition: app_fax.c:359
static enum sip_publish_type determine_sip_publish_type(pjsip_rx_data *rdata, pjsip_generic_string_hdr *etag_hdr, unsigned int *expires, int *entity_id)
static struct ast_sip_subscription_handler * subscription_get_handler_from_rdata(pjsip_rx_data *rdata, const char *endpoint)
Retrieve a handler using the Event header of an rdata message.
const char * ast_sip_subscription_get_body_type(struct ast_sip_subscription *sub)
Get the body type used for this subscription.
static int allocate_tdata_buffer(pjsip_tx_data *tdata)
Pre-allocate a buffer for the transmission.
static int cli_complete_subscription_outbound(struct sip_subscription_tree *sub_tree, void *arg)
#define ao2_unlink(container, obj)
Definition: astobj2.h:1598
#define CLI_SHOWUSAGE
Definition: cli.h:45
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5138
static int test_new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource)
new_subscribe callback for unit tests
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct ast_sip_subscription_handler *handler, const char *resource, struct resource_tree *tree, int has_eventlist_support)
Build a resource tree.
static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state)
Send a NOTIFY request to a subscriber.
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg)
struct subscription_persistence * persistence
static int check_node(struct ast_test *test, struct tree_node *node, const char **resources, size_t num_resources)
Check the integrity of a tree node against a set of resources.
char * event_configuration_name
The name of the event type configuration.
Type for default option handler for bools (ast_true/ast_false)
char * ast_generate_random_string(char *buf, size_t size)
Create a pseudo-random string of a fixed length.
Definition: strings.c:227
static int cli_complete_subscription_inbound(struct sip_subscription_tree *sub_tree, void *arg)
def info(msg)
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:464
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
const struct ast_json * ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription)
Retrieve persistence data for a subscription.
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition: sorcery.h:477
static int sub_persistence_recreate(void *obj)
Contact associated with an address of record.
Definition: res_pjsip.h:281
#define stasis_subscribe_pool(topic, callback, data)
Definition: stasis.h:682
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2283
static int subscription_persistence_load(void *data)
Function which loads and recreates persisted subscriptions upon startup when the system is fully boot...
int ast_sip_register_subscription_handler(struct ast_sip_subscription_handler *handler)
Register a subscription handler.
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
Definition: res_pjsip.c:5133
void(* destroy_body)(void *body)
Deallocate resources created for the body.
static int ami_subscription_detail(struct sip_subscription_tree *sub_tree, struct ast_sip_ami *ami, const char *event)
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:973
Callbacks that publication handlers will define.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
char * resource
The resource the publication is to.
struct ast_taskprocessor * ast_sip_get_distributor_serializer(pjsip_rx_data *rdata)
Determine the distributor serializer for the SIP message.
#define LOG_NOTICE
Definition: logger.h:263
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition: sorcery.c:1744
static int sip_publication_respond(struct ast_sip_publication *pub, int status_code, pjsip_rx_data *rdata)
void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data)
Set persistence data for a subscription.
struct sip_subscription_tree * next
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static void subscription_persistence_remove(struct sip_subscription_tree *sub_tree)
Function which removes persistence of a subscription from sorcery.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:710
const char * ast_sip_publication_get_resource(const struct ast_sip_publication *pub)
Given a publication, get the resource the publication is to.
static int cli_show_subscription_inbound(struct sip_subscription_tree *sub_tree, void *arg)
struct ast_datastore * ast_sip_subscription_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
static char version[AST_MAX_EXTENSION]
Definition: chan_ooh323.c:391
#define CLI_FAILURE
Definition: cli.h:46
static int ineligible_configuration(void)
struct ast_sip_endpoint * endpoint
The endpoint with which the subscription is communicating.
int ast_sip_create_rdata_with_contact(pjsip_rx_data *rdata, char *packet, const char *src_name, int src_port, char *transport_type, const char *local_name, int local_port, const char *contact_uri)
General purpose method for creating an rdata structure using specific information.
Definition: res_pjsip.c:4271
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static int cli_show_subscriptions_detail(struct sip_subscription_tree *sub_tree, struct cli_sub_parms *cli)
pjsip_dialog * ast_sip_create_dialog_uas_locked(const struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pj_status_t *status)
General purpose method for creating a UAS dialog with an endpoint.
Definition: res_pjsip.c:4249
struct ast_sip_sched_task * expiration_task
struct resources items
static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
Definition: func_strings.c:948
void(* state_change)(struct ast_sip_subscription *sub, pjsip_msg_body *body, enum pjsip_evsub_state state)
A NOTIFY has been received.
static void shutdown_subscriptions(struct ast_sip_subscription *sub)
int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
Definition: res_pjsip.c:3723
void ast_datastores_remove(struct ao2_container *datastores, const char *name)
Remove a data store from a container.
Definition: datastore.c:114
static struct ast_sip_subscription * create_virtual_subscriptions(const struct ast_sip_subscription_handler *handler, const char *resource, struct ast_sip_pubsub_body_generator *generator, struct sip_subscription_tree *tree, struct tree_node *current)
Create a tree of virtual subscriptions based on a resource tree node.
pjsip_generic_string_hdr * cid
static void * rlmi_clone_data(pj_pool_t *pool, const void *data, unsigned len)
pj_xml_node * ast_sip_presence_xml_create_node(pj_pool_t *pool, pj_xml_node *parent, const char *name)
Create XML node.
Definition: presence_xml.c:152
const char * word
Definition: cli.h:163
char * endpoint
Optional name of an endpoint that is only allowed to publish to this resource.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
AST_TEST_DEFINE(resource_tree)
static void test_resource_tree_destroy(struct resource_tree *tree)
RAII_VAR callback to destroy an allocated resource tree.
static int list_item_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
An API for managing task processing threads that can be shared across modules.
int ast_sip_pubsub_register_body_supplement(struct ast_sip_pubsub_body_supplement *supplement)
Register a body generator with the pubsub core.
int ast_sched_add(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data) attribute_warn_unused_result
Adds a scheduled event.
Definition: sched.c:565
struct ast_taskprocessor * serializer
Structure used to handle boolean flags.
Definition: utils.h:199
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition: manager.c:1720
struct ast_str * ast_sip_create_ami_event(const char *event, struct ast_sip_ami *ami)
Creates a string to store AMI event data in.
static char cid_name[AST_MAX_EXTENSION]
Definition: chan_mgcp.c:165
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip_body_data *notify_data, int terminate)
Notify a SIP subscription of a state change.
const char * usage
Definition: cli.h:177
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition: sorcery.c:1897
static int item_in_vector(const struct resource_list *list, const char *item)
void ast_sip_subscription_remove_datastore(struct ast_sip_subscription *subscription, const char *name)
Remove a subscription datastore from the subscription.
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:3718
#define CLI_SUCCESS
Definition: cli.h:44
struct ast_json * ast_json_object_create(void)
Create a new JSON object.
Definition: json.c:389
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
#define AST_SCHED_REPLACE_UNREF(id, sched, when, callback, data, unrefcall, addfailcall, refcall)
Definition: sched.h:150
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:740
static char * cli_show_subscriptions_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
struct ast_flags ast_options
Definition: options.c:61
char * strsep(char **str, const char *delims)
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
static struct sip_subscription_tree * create_subscription_tree(const struct ast_sip_subscription_handler *handler, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource, struct ast_sip_pubsub_body_generator *generator, struct resource_tree *tree, pj_status_t *dlg_status, struct subscription_persistence *persistence)
Create a subscription tree based on a resource tree.
static struct ast_sip_publication * sip_create_publication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource, const char *event_configuration_name)
static struct ast_sorcery * sorcery
Definition: test.c:65
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
struct ast_party_id self
Definition: res_pjsip.h:629
char contact_uri[PJSIP_MAX_URL_SIZE]
ast_sip_subscription_role
Role for the subscription that is being created.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
void ast_str_update(struct ast_str *buf)
Update the length of the buffer, after using ast_str merely as a buffer.
Definition: strings.h:663
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
Definition: time.h:226
struct sip_subscription_tree * sub_tree
#define AMI_SHOW_SUBSCRIPTIONS_INBOUND
const int pos
Definition: cli.h:164
static int publication_cmp_fn(void *obj, void *arg, int flags)
void * ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, const char *header)
Get a header value for a subscription.
void ast_sip_unregister_service(pjsip_module *module)
Definition: res_pjsip.c:3331
static void handler(const char *name, int response_code, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Definition: test_ari.c:59
Asterisk MWI API.
static int persistence_expires_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
static struct ast_sip_subscription_handler * find_sub_handler_for_event_name(const char *event_name)
int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf)
Converts a sorcery object to a string of object properties.
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition: stasis.c:1025
static struct ast_sip_subscription * allocate_subscription(const struct ast_sip_subscription_handler *handler, const char *resource, struct sip_subscription_tree *tree)
struct stasis_forward * sub
Definition: res_corosync.c:240
sip_publish_type
The types of PUBLISH messages defined in RFC 3903.
Definition: sip.h:1562
static int populate_list(struct resource_list *list, const char *event, const char **resources, size_t num_resources)
Set properties on an allocated resource list.
static int load_module(void)
Abstract JSON element (object, array, string, int, ...).
static void pubsub_on_client_refresh(pjsip_evsub *sub)
static void pubsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
static char * cli_show_subscription_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Structure representing a SIP publication.
static struct ast_sip_pubsub_body_generator * find_body_generator(char accept[AST_SIP_MAX_ACCEPT][64], size_t num_accept, const char *body_type)
int entity_tag
Entity tag for the publication.
pjsip_multipart_part * part
int error(const char *format,...)
Definition: utils/frame.c:999
pjsip_dialog * ast_sip_create_dialog_uac(const struct ast_sip_endpoint *endpoint, const char *aor_name, const char *request_user)
General purpose method for creating a UAC dialog with an endpoint.
Definition: res_pjsip.c:4028
static int resource_list_apply_handler(const struct ast_sorcery *sorcery, void *obj)
enum queue_result id
Definition: app_queue.c:1507
static void set_state_terminated(struct ast_sip_subscription *sub)
struct ast_sip_subscription_handler * next
static int ami_show_subscriptions_outbound(struct mansession *s, const struct message *m)
static pjsip_generic_string_hdr * generate_content_id_hdr(pj_pool_t *pool, const struct ast_sip_subscription *sub)
Create a Content-ID header.
Generic container type.
void ast_sip_unregister_publish_handler(struct ast_sip_publish_handler *handler)
Unregister a publish handler.
static int have_visited(const char *resource, struct resources *visited)
Determine if this resource has been visited already.
struct ast_str * buf
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:280
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
int(* supplement_body)(void *body, void *data)
Add additional content to a SIP request body.
struct ast_sip_endpoint * ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
Get the endpoint that is associated with this subscription.
int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, size_t maxlen)
Gets the task name.
const ast_string_field aors
Definition: res_pjsip.h:821
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:186
static pjsip_msg_body * generate_list_body(pj_pool_t *pool, struct ast_sip_subscription *sub, unsigned int force_full_state)
Create a resource list body for NOTIFY requests.
static const pj_str_t str_event_name
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
const char * default_accept
Default body type defined for the event package this notifier handles.
const ast_string_field uri
Definition: res_pjsip.h:303
sip_persistence_update_type
Structure representing a publication resource.
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3159
Asterisk module definitions.
static int persistence_generator_data_struct2str(const void *obj, const intptr_t *args, char **buf)
static int cli_show_subscriptions_inbound(struct sip_subscription_tree *sub_tree, void *arg)
struct ast_variable * events
Mapping for event types to configuration.
static unsigned int cli_subscription_expiry(struct sip_subscription_tree *sub_tree)
unsigned int send_scheduled_notify
struct ao2_container * publications
Publications.
static void destroy_subscription(struct ast_sip_subscription *sub)
char src_name[PJ_INET6_ADDRSTRLEN]
void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
Unregister a transport shutdown monitor from all reliable transports.
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:298
#define PJSIP_EXPIRES_NOT_SPECIFIED
Definition: res_pjsip.h:63
int(* generate_body_content)(void *body, void *data)
Add content to the body of a SIP request.
void(* subscription_shutdown)(struct ast_sip_subscription *subscription)
Called when a subscription is to be destroyed.
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:269
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:616
struct ast_datastore * ast_sip_subscription_get_datastore(struct ast_sip_subscription *subscription, const char *name)
Retrieve a subscription datastore.
unsigned int notification_batch_interval
void ast_sip_subscription_destroy(struct ast_sip_subscription *sub)
Alert the pubsub core that the subscription is ready for destruction.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
static struct ast_sched_context * sched
Scheduler used for automatically expiring publications.
#define MOD_DATA_PERSISTENCE
void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects.
Definition: sorcery.c:1442
struct ast_sip_notifier test_notifier
Subscription notifier for unit tests.
unsigned int full_state
A tree of SIP subscriptions.
static struct sip_subscription_tree * allocate_subscription_tree(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
static int cli_list_subscriptions_inbound(struct sip_subscription_tree *sub_tree, void *arg)
#define MOD_DATA_MSG
struct ast_json * generator_data
pjsip_evsub_state state
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
static int serialized_pubsub_on_client_refresh(void *userdata)
static int ast_sip_pubsub_has_eventlist_support(pjsip_rx_data *rdata)
Check if the rdata has a Supported header containing &#39;eventlist&#39;.
int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
Update an object.
Definition: sorcery.c:2145
static int cli_complete_subscription_common(struct sip_subscription_tree *sub_tree, struct cli_sub_complete_parms *cli)
int sched_id
Scheduled item for expiration of publication.
static int cli_show_subscription_common(struct sip_subscription_tree *sub_tree, struct cli_sub_parms *cli)
static void tree_node_destroy(struct tree_node *node)
Destructor for a tree node.
char * ast_read_line_from_buffer(char **buffer)
Read lines from a string buffer.
Definition: strings.c:372
static int unload_module(void)
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:3201
unsigned int notification_batch_interval
static struct test_val a
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:343
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443
static int esc_etag_counter
#define ao2_link(container, obj)
Definition: astobj2.h:1549
static int resource_endpoint_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static void free_body_parts(struct body_part_list *parts)
Destroy a list of body parts.