Asterisk - The Open Source Telephony Project GIT-master-2de1a68
res_pjsip_pubsub.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * Mark Michelson <mmichelson@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18/*!
19 * \brief Opaque structure representing an RFC 3265 SIP subscription
20 */
21
22/*** MODULEINFO
23 <depend>pjproject</depend>
24 <depend>res_pjsip</depend>
25 <support_level>core</support_level>
26 ***/
27
28#include "asterisk.h"
29
30#include <pjsip.h>
31#include <pjsip_simple.h>
32#include <pjlib.h>
33
34#include "asterisk/mwi.h"
36#include "asterisk/module.h"
38#include "asterisk/astobj2.h"
39#include "asterisk/datastore.h"
40#include "asterisk/uuid.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 <configOption name="resource_display_name" default="no">
214 <synopsis>Indicates whether display name of resource or the resource name being reported.</synopsis>
215 <description>
216 <para>If this option is enabled, the Display Name will be reported as resource name.
217 If the <replaceable>event</replaceable> set to <literal>presence</literal> or <literal>dialog</literal>,
218 the non-empty HINT name will be set as the Display Name.
219 The <literal>message-summary</literal> is not supported yet.</para>
220 </description>
221 </configOption>
222 </configObject>
223 <configObject name="inbound-publication">
224 <synopsis>The configuration for inbound publications</synopsis>
225 <configOption name="endpoint" default="">
226 <synopsis>Optional name of an endpoint that is only allowed to publish to this resource</synopsis>
227 </configOption>
228 <configOption name="type">
229 <synopsis>Must be of type 'inbound-publication'.</synopsis>
230 </configOption>
231 </configObject>
232 </configFile>
233 </configInfo>
234 ***/
235
236static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata);
237
238static struct pjsip_module pubsub_module = {
239 .name = { "PubSub Module", 13 },
240 .priority = PJSIP_MOD_PRIORITY_APPLICATION,
241 .on_rx_request = pubsub_on_rx_request,
242};
243
244#define MOD_DATA_PERSISTENCE "sub_persistence"
245#define MOD_DATA_MSG "sub_msg"
246
247static const pj_str_t str_event_name = { "Event", 5 };
248
249/*! \brief Scheduler used for automatically expiring publications */
251
252/*! \brief Number of buckets for publications (on a per handler) */
253#define PUBLICATIONS_BUCKETS 37
254
255/*! \brief Default expiration time for PUBLISH if one is not specified */
256#define DEFAULT_PUBLISH_EXPIRES 3600
257
258/*! \brief Number of buckets for subscription datastore */
259#define DATASTORE_BUCKETS 53
260
261/*! \brief Default expiration for subscriptions */
262#define DEFAULT_EXPIRES 3600
263
264/*! \brief Defined method for PUBLISH */
265const pjsip_method pjsip_publish_method =
266{
267 PJSIP_OTHER_METHOD,
268 { "PUBLISH", 7 }
269};
270
271/*!
272 * \brief The types of PUBLISH messages defined in RFC 3903
273 */
275 /*!
276 * \brief Unknown
277 *
278 * \details
279 * This actually is not defined in RFC 3903. We use this as a constant
280 * to indicate that an incoming PUBLISH does not fit into any of the
281 * other categories and is thus invalid.
282 */
284
285 /*!
286 * \brief Initial
287 *
288 * \details
289 * The first PUBLISH sent. This will contain a non-zero Expires header
290 * as well as a body that indicates the current state of the endpoint
291 * that has sent the message. The initial PUBLISH is the only type
292 * of PUBLISH to not contain a Sip-If-Match header in it.
293 */
295
296 /*!
297 * \brief Refresh
298 *
299 * \details
300 * Used to keep a published state from expiring. This will contain a
301 * non-zero Expires header but no body since its purpose is not to
302 * update state.
303 */
305
306 /*!
307 * \brief Modify
308 *
309 * \details
310 * Used to change state from its previous value. This will contain
311 * a body updating the published state. May or may not contain an
312 * Expires header.
313 */
315
316 /*!
317 * \brief Remove
318 *
319 * \details
320 * Used to remove published state from an ESC. This will contain
321 * an Expires header set to 0 and likely no body.
322 */
324};
325
326/*!
327 * \brief A vector of strings commonly used throughout this module
328 */
329AST_VECTOR(resources, const char *);
330
331/*!
332 * \brief Resource list configuration item
333 */
336 /*! SIP event package the list uses. */
337 char event[32];
338 /*! Strings representing resources in the list. */
340 /*! Indicates if Asterisk sends full or partial state on notifications. */
341 unsigned int full_state;
342 /*! Time, in milliseconds Asterisk waits before sending a batched notification.*/
344 /*! Indicates whether display name of resource or the resource name being reported.*/
346};
347
348/*!
349 * Used to create new entity IDs by ESCs.
350 */
352
353/*!
354 * \brief Structure representing a SIP publication
355 */
357 /*! Publication datastores set up by handlers */
359 /*! \brief Entity tag for the publication */
361 /*! \brief Handler for this publication */
363 /*! \brief The endpoint with which the subscription is communicating */
365 /*! \brief Expiration time of the publication */
366 unsigned int expires;
367 /*! \brief Scheduled item for expiration of publication */
369 /*! \brief The resource the publication is to */
370 char *resource;
371 /*! \brief The name of the event type configuration */
373 /*! \brief Data containing the above */
374 char data[0];
375};
376
377
378/*!
379 * \brief Structure used for persisting an inbound subscription
380 */
382 /*! Sorcery object details */
384 /*! The name of the endpoint involved in the subscription */
385 char *endpoint;
386 /*! SIP message that creates the subscription */
387 char packet[PJSIP_MAX_PKT_LEN];
388 /*! Source address of the message */
389 char src_name[PJ_INET6_ADDRSTRLEN];
390 /*! Source port of the message */
392 /*! Local transport type (UDP,TCP,TLS)*/
394 /*! Local transport address */
395 char local_name[PJ_INET6_ADDRSTRLEN];
396 /*! Local transport port */
398 /*! Next CSeq to use for message */
399 unsigned int cseq;
400 /*! Local tag of the dialog */
401 char *tag;
402 /*! When this subscription expires */
403 struct timeval expires;
404 /*! Contact URI */
405 char contact_uri[PJSIP_MAX_URL_SIZE];
406 /*! Prune subscription on restart */
408 /*! Body generator specific persistence data */
410};
411
412/*!
413 * \brief The state of the subscription tree
414 */
416 /*! Normal operation */
418 /*! A terminate has been requested by Asterisk, the client, or pjproject */
420 /*! The terminate is in progress */
422 /*! The terminate process has finished and the subscription tree is no longer valid */
424};
425
427 "Normal",
428 "TerminatePending",
429 "TerminateInProgress",
430 "Terminated"
431};
432
433/*!
434 * \brief A tree of SIP subscriptions
435 *
436 * Because of the ability to subscribe to resource lists, a SIP
437 * subscription can result in a tree of subscriptions being created.
438 * This structure represents the information relevant to the subscription
439 * as a whole, to include the underlying PJSIP structure for the
440 * subscription.
441 */
443 /*! The endpoint with which the subscription is communicating */
445 /*! Serializer on which to place operations for this subscription */
447 /*! The role for this subscription */
449 /*! Persistence information */
451 /*! The underlying PJSIP event subscription structure */
452 pjsip_evsub *evsub;
453 /*! The underlying PJSIP dialog */
454 pjsip_dialog *dlg;
455 /*! Interval to use for batching notifications */
457 /*! Scheduler ID for batched notification */
459 /*! Indicator if scheduled batched notification should be sent */
461 /*! The root of the subscription tree */
463 /*! Is this subscription to a list? */
465 /*! Next item in the list */
467 /*! Subscription tree state */
469 /*! On asterisk restart, this is the task data used
470 * to restart the expiration timer if pjproject isn't
471 * capable of restarting the timer.
472 */
474 /*! The transport the subscription was received on.
475 * Only used for reliable transports.
476 */
478 /*! Indicator if initial notify should be generated.
479 * Used to refresh modified RLS.
480 */
482};
483
484/*!
485 * \brief Structure representing a "virtual" SIP subscription.
486 *
487 * This structure serves a dual purpose. Structurally, it is
488 * the constructed tree of subscriptions based on the resources
489 * being subscribed to. API-wise, this serves as the handle that
490 * subscription handlers use in order to interact with the pubsub API.
491 */
493 /*! Subscription datastores set up by handlers */
495 /*! The handler for this subscription */
497 /*! Pointer to the base of the tree */
499 /*! Body generator for NOTIFYs */
501 /*! Vector of child subscriptions */
503 /*! Saved NOTIFY body text for this subscription */
505 /*! Indicator that the body text has changed since the last notification */
507 /*! The current state of the subscription */
508 pjsip_evsub_state subscription_state;
509 /*! For lists, the current version to place in the RLMI body */
510 unsigned int version;
511 /*! For lists, indicates if full state should always be communicated. */
512 unsigned int full_state;
513 /*! URI associated with the subscription */
514 pjsip_sip_uri *uri;
515 /*! Data to be persisted with the subscription */
517 /*! Display Name of resource */
519 /*! Name of resource being subscribed to */
520 char resource[0];
521};
522
523/*!
524 * \brief Structure representing a publication resource
525 */
527 /*! \brief Sorcery object details */
529 /*! \brief Optional name of an endpoint that is only allowed to publish to this resource */
530 char *endpoint;
531 /*! \brief Mapping for event types to configuration */
533};
534
535static const char *sip_subscription_roles_map[] = {
536 [AST_SIP_SUBSCRIBER] = "Subscriber",
537 [AST_SIP_NOTIFIER] = "Notifier"
538};
539
541 /*! Called from send request */
543 /*! Subscription created from initial client request */
545 /*! Subscription recreated by asterisk on startup */
547 /*! Subscription created from client refresh */
549};
550
552
555
556static pjsip_media_type rlmi_media_type;
557
558static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *event);
559static void pubsub_on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata,
560 int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
561static void pubsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code,
562 pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body);
563static void pubsub_on_client_refresh(pjsip_evsub *sub);
564static void pubsub_on_server_timeout(pjsip_evsub *sub);
565
566static pjsip_evsub_user pubsub_cb = {
567 .on_evsub_state = pubsub_on_evsub_state,
568 .on_rx_refresh = pubsub_on_rx_refresh,
569 .on_rx_notify = pubsub_on_rx_notify,
570 .on_client_refresh = pubsub_on_client_refresh,
571 .on_server_timeout = pubsub_on_server_timeout,
572};
573
574/*! \brief Destructor for publication resource */
575static void publication_resource_destroy(void *obj)
576{
577 struct ast_sip_publication_resource *resource = obj;
578
579 ast_free(resource->endpoint);
580 ast_variables_destroy(resource->events);
581}
582
583/*! \brief Allocator for publication resource */
584static void *publication_resource_alloc(const char *name)
585{
587}
588
590{
591 struct sip_subscription_tree *sub_tree = data;
592
593 if (!sub_tree->evsub) {
594 /* Something else already terminated the subscription. */
595 ao2_ref(sub_tree, -1);
596 return 0;
597 }
598
599 ast_debug(3, "Transport destroyed. Removing subscription '%s->%s' prune on boot: %d\n",
600 sub_tree->persistence->endpoint, sub_tree->root->resource,
601 sub_tree->persistence->prune_on_boot);
602
604 pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
605
606 ao2_ref(sub_tree, -1);
607 return 0;
608}
609
610/*!
611 * \internal
612 * \brief The reliable transport we used as a subscription contact has shutdown.
613 *
614 * \param data What subscription needs to be terminated.
615 *
616 * \note Normally executed by the pjsip monitor thread.
617 */
618static void sub_tree_transport_cb(void *data)
619{
620 struct sip_subscription_tree *sub_tree = data;
621
622 /*
623 * Push off the subscription termination to the serializer to
624 * avoid deadlock. Another thread could be trying to send a
625 * message on the subscription that can deadlock with this
626 * thread.
627 */
628 ao2_ref(sub_tree, +1);
630 sub_tree)) {
631 ao2_ref(sub_tree, -1);
632 }
633}
634
635/*! \brief Destructor for subscription persistence */
637{
638 struct subscription_persistence *persistence = obj;
639
640 ast_free(persistence->endpoint);
641 ast_free(persistence->tag);
642 ast_json_unref(persistence->generator_data);
643}
644
645/*! \brief Allocator for subscription persistence */
646static void *subscription_persistence_alloc(const char *name)
647{
649}
650
651/*! \brief Function which creates initial persistence information of a subscription in sorcery */
653{
654 char tag[PJ_GUID_STRING_LENGTH + 1];
655
656 /* The id of this persistence object doesn't matter as we keep it on the subscription and don't need to
657 * look it up by id at all.
658 */
660 "subscription_persistence", NULL);
661
662 pjsip_dialog *dlg = sub_tree->dlg;
663
664 if (!persistence) {
665 return NULL;
666 }
667
668 persistence->endpoint = ast_strdup(ast_sorcery_object_get_id(sub_tree->endpoint));
669 ast_copy_pj_str(tag, &dlg->local.info->tag, sizeof(tag));
670 persistence->tag = ast_strdup(tag);
671
673 return persistence;
674}
675
676/*! \brief Function which updates persistence information of a subscription in sorcery */
678 pjsip_rx_data *rdata, enum sip_persistence_update_type type)
679{
680 pjsip_dialog *dlg;
681
682 if (!sub_tree->persistence) {
683 return;
684 }
685
686 ast_debug(3, "Updating persistence for '%s->%s' prune on boot: %s\n",
687 sub_tree->persistence->endpoint, sub_tree->root->resource,
688 sub_tree->persistence->prune_on_boot ? "yes" : "no");
689
690 dlg = sub_tree->dlg;
691 sub_tree->persistence->cseq = dlg->local.cseq;
692
693 if (rdata) {
694 unsigned int expires;
695 pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
696 pjsip_contact_hdr *contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);
697
698 expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
700
701 if (contact_hdr) {
702 if (contact_hdr) {
704 sub_tree->persistence->prune_on_boot =
706 (pjsip_sip_uri *)pjsip_uri_get_uri(contact_hdr->uri),
707 sub_tree->endpoint, rdata);
708
709 if (sub_tree->persistence->prune_on_boot) {
710 ast_debug(3, "adding transport monitor on %s for '%s->%s' prune on boot: %d\n",
711 rdata->tp_info.transport->obj_name,
712 sub_tree->persistence->endpoint, sub_tree->root->resource,
713 sub_tree->persistence->prune_on_boot);
714 AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(rdata->tp_info.transport,
715 sub_tree->transport_key);
717 sub_tree_transport_cb, sub_tree);
718 /*
719 * FYI: ast_sip_transport_monitor_register holds a reference to the sub_tree
720 */
721 }
722 }
723 }
724
725 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, contact_hdr->uri,
726 sub_tree->persistence->contact_uri, sizeof(sub_tree->persistence->contact_uri));
727 } else {
728 ast_log(LOG_WARNING, "Contact not updated due to missing contact header\n");
729 }
730
731 /* When receiving a packet on an streaming transport, it's possible to receive more than one SIP
732 * message at a time into the rdata->pkt_info.packet buffer. However, the rdata->msg_info.msg_buf
733 * will always point to the proper SIP message that is to be processed. When updating subscription
734 * persistence that is pulled from persistent storage, though, the rdata->pkt_info.packet will
735 * only ever have a single SIP message on it, and so we base persistence on that.
736 */
739 if (rdata->msg_info.msg_buf) {
740 ast_copy_string(sub_tree->persistence->packet, rdata->msg_info.msg_buf,
741 MIN(sizeof(sub_tree->persistence->packet), rdata->msg_info.len + 1));
742 } else {
743 ast_copy_string(sub_tree->persistence->packet, rdata->pkt_info.packet,
744 sizeof(sub_tree->persistence->packet));
745 }
746 }
747 ast_copy_string(sub_tree->persistence->src_name, rdata->pkt_info.src_name,
748 sizeof(sub_tree->persistence->src_name));
749 sub_tree->persistence->src_port = rdata->pkt_info.src_port;
750 ast_copy_string(sub_tree->persistence->transport_type, rdata->tp_info.transport->type_name,
751 sizeof(sub_tree->persistence->transport_type));
752 ast_copy_pj_str(sub_tree->persistence->local_name, &rdata->tp_info.transport->local_name.host,
753 sizeof(sub_tree->persistence->local_name));
754 sub_tree->persistence->local_port = rdata->tp_info.transport->local_name.port;
755 }
756
758}
759
760/*! \brief Function which removes persistence of a subscription from sorcery */
762{
763 if (!sub_tree->persistence) {
764 return;
765 }
766
767 if (sub_tree->persistence->prune_on_boot && !ast_strlen_zero(sub_tree->transport_key)) {
768 ast_debug(3, "Unregistering transport monitor on %s '%s->%s'\n",
769 sub_tree->transport_key,
770 sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",
771 sub_tree->root ? sub_tree->root->resource : "Unknown");
773 sub_tree_transport_cb, sub_tree, NULL);
774 }
775
777 ao2_ref(sub_tree->persistence, -1);
778 sub_tree->persistence = NULL;
779}
780
781
784 size_t num_accept, const char *body_type);
785
786/*! \brief Retrieve a handler using the Event header of an rdata message */
787static struct ast_sip_subscription_handler *subscription_get_handler_from_rdata(pjsip_rx_data *rdata, const char *endpoint)
788{
789 pjsip_event_hdr *event_header;
790 char event[32];
792
793 event_header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_event_name, rdata->msg_info.msg->hdr.next);
794 if (!event_header) {
795 ast_log(LOG_WARNING, "Incoming SUBSCRIBE request from %s with no Event header\n",
796 endpoint ? endpoint : "Unknown");
797 return NULL;
798 }
799 ast_copy_pj_str(event, &event_header->event_type, sizeof(event));
800
802 if (!handler) {
803 ast_log(LOG_WARNING, "No registered subscribe handler for event %s from %s\n", event,
804 endpoint ? endpoint : "Unknown");
805 }
806
807 return handler;
808}
809
810/*!
811 * \brief Accept headers that are exceptions to the rule
812 *
813 * Typically, when a SUBSCRIBE arrives, we attempt to find a
814 * body generator that matches one of the Accept headers in
815 * the request. When subscribing to a single resource, this works
816 * great. However, when subscribing to a list, things work
817 * differently. Most Accept header values are fine, but there
818 * are a couple that are endemic to resource lists that need
819 * to be ignored when searching for a body generator to use
820 * for the individual resources of the subscription.
821 */
822const char *accept_exceptions[] = {
823 "multipart/related",
824 "application/rlmi+xml",
825};
826
827/*!
828 * \brief Is the Accept header from the SUBSCRIBE in the list of exceptions?
829 *
830 * \retval 1 This Accept header value is an exception to the rule.
831 * \retval 0 This Accept header is not an exception to the rule.
832 */
833static int exceptional_accept(const pj_str_t *accept)
834{
835 int i;
836
837 for (i = 0; i < ARRAY_LEN(accept_exceptions); ++i) {
838 if (!pj_strcmp2(accept, accept_exceptions[i])) {
839 return 1;
840 }
841 }
842
843 return 0;
844}
845
846/*! \brief Retrieve a body generator using the Accept header of an rdata message */
849{
850 pjsip_accept_hdr *accept_header = (pjsip_accept_hdr *) &rdata->msg_info.msg->hdr;
851 char accept[AST_SIP_MAX_ACCEPT][64];
852 size_t num_accept_headers = 0;
853
854 while ((accept_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, accept_header->next)) &&
855 (num_accept_headers < AST_SIP_MAX_ACCEPT)) {
856 int i;
857
858 for (i = 0; i < accept_header->count && num_accept_headers < AST_SIP_MAX_ACCEPT; ++i) {
859 if (!exceptional_accept(&accept_header->values[i])) {
860 ast_copy_pj_str(accept[num_accept_headers], &accept_header->values[i], sizeof(accept[num_accept_headers]));
861 ++num_accept_headers;
862 }
863 }
864 }
865
866 if (num_accept_headers == 0) {
867 /* If a SUBSCRIBE contains no Accept headers, then we must assume that
868 * the default accept type for the event package is to be used.
869 */
870 ast_copy_string(accept[0], handler->notifier->default_accept, sizeof(accept[0]));
871 num_accept_headers = 1;
872 }
873
874 return find_body_generator(accept, num_accept_headers, handler->body_type);
875}
876
877/*! \brief Check if the rdata has a Supported header containing 'eventlist'
878 *
879 * \retval 1 rdata has an eventlist containing supported header
880 * \retval 0 rdata doesn't have an eventlist containing supported header
881 */
882static int ast_sip_pubsub_has_eventlist_support(pjsip_rx_data *rdata)
883{
884 pjsip_supported_hdr *supported_header = (pjsip_supported_hdr *) &rdata->msg_info.msg->hdr;
885
886 while ((supported_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_SUPPORTED, supported_header->next))) {
887 int i;
888
889 for (i = 0; i < supported_header->count; i++) {
890 if (!pj_stricmp2(&supported_header->values[i], "eventlist")) {
891 return 1;
892 }
893 }
894 }
895
896 return 0;
897}
898
899struct resource_tree;
900
901/*!
902 * \brief A node for a resource tree.
903 */
904struct tree_node {
906 unsigned int full_state;
908 char resource[0];
909};
910
911/*!
912 * \brief Helper function for retrieving a resource list for a given event.
913 *
914 * This will retrieve a resource list that corresponds to the resource and event provided.
915 *
916 * \param resource The name of the resource list to retrieve
917 * \param event The expected event name on the resource list
918 */
919static struct resource_list *retrieve_resource_list(const char *resource, const char *event)
920{
921 struct resource_list *list;
922
923 list = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "resource_list", resource);
924 if (!list) {
925 return NULL;
926 }
927
928 if (strcmp(list->event, event)) {
929 ast_log(LOG_WARNING, "Found resource list %s, but its event type (%s) does not match SUBSCRIBE's (%s)\n",
930 resource, list->event, event);
931 ao2_cleanup(list);
932 return NULL;
933 }
934
935 return list;
936}
937
938/*!
939 * \brief Allocate a tree node
940 *
941 * In addition to allocating and initializing the tree node, the node is also added
942 * to the vector of visited resources. See \ref build_resource_tree for more information
943 * on the visited resources.
944 *
945 * \param resource The name of the resource for this tree node.
946 * \param visited The vector of resources that have been visited.
947 * \param full_state if allocating a list, indicate whether full state is requested in notifications.
948 * \param display_name the display name to include with this tree node.
949 *
950 * \retval NULL Allocation failure.
951 * \retval non-NULL The newly-allocated tree_node
952 */
953static struct tree_node *tree_node_alloc(const char *resource, struct resources *visited, unsigned int full_state, const char *display_name)
954{
955 struct tree_node *node;
956
957 node = ast_calloc(1, sizeof(*node) + strlen(resource) + 1);
958 if (!node) {
959 return NULL;
960 }
961
962 strcpy(node->resource, resource);
963 if (AST_VECTOR_INIT(&node->children, 4)) {
964 ast_free(node);
965 return NULL;
966 }
967 node->full_state = full_state;
968 node->display_name = ast_strdup(display_name);
969
970 if (visited) {
971 AST_VECTOR_APPEND(visited, resource);
972 }
973 return node;
974}
975
976/*!
977 * \brief Destructor for a tree node
978 *
979 * This function calls recursively in order to destroy
980 * all nodes lower in the tree from the given node in
981 * addition to the node itself.
982 *
983 * \param node The node to destroy.
984 */
985static void tree_node_destroy(struct tree_node *node)
986{
987 int i;
988 if (!node) {
989 return;
990 }
991
992 for (i = 0; i < AST_VECTOR_SIZE(&node->children); ++i) {
993 tree_node_destroy(AST_VECTOR_GET(&node->children, i));
994 }
995 AST_VECTOR_FREE(&node->children);
996 ast_free(node->display_name);
997 ast_free(node);
998}
999
1000/*!
1001 * \brief Determine if this resource has been visited already
1002 *
1003 * See \ref build_resource_tree for more information
1004 *
1005 * \param resource The resource currently being visited
1006 * \param visited The resources that have previously been visited
1007 */
1008static int have_visited(const char *resource, struct resources *visited)
1009{
1010 int i;
1011
1012 for (i = 0; i < AST_VECTOR_SIZE(visited); ++i) {
1013 if (!strcmp(resource, AST_VECTOR_GET(visited, i))) {
1014 return 1;
1015 }
1016 }
1017
1018 return 0;
1019}
1020
1021#define NEW_SUBSCRIBE(notifier, endpoint, resource, rdata) notifier->new_subscribe_with_rdata ? notifier->new_subscribe_with_rdata(endpoint, resource, rdata) : notifier->new_subscribe(endpoint, resource)
1022
1023/*!
1024 * \brief Build child nodes for a given parent.
1025 *
1026 * This iterates through the items on a resource list and creates tree nodes for each one. The
1027 * tree nodes created are children of the supplied parent node. If an item in the resource
1028 * list is itself a list, then this function is called recursively to provide children for
1029 * the new node.
1030 *
1031 * If an item in a resource list is not a list, then the supplied subscription handler is
1032 * called into as if a new SUBSCRIBE for the list item were presented. The handler's response
1033 * is used to determine if the node can be added to the tree or not.
1034 *
1035 * If a parent node ends up having no child nodes added under it, then the parent node is
1036 * pruned from the tree.
1037 *
1038 * \param endpoint The endpoint that sent the inbound SUBSCRIBE.
1039 * \param handler The subscription handler for leaf nodes in the tree.
1040 * \param list The configured resource list from which the child node is being built.
1041 * \param parent The parent node for these children.
1042 * \param visited The resources that have already been visited.
1043 */
1045 struct resource_list *list, struct tree_node *parent, struct resources *visited, pjsip_rx_data *rdata)
1046{
1047 int i;
1048
1049 for (i = 0; i < AST_VECTOR_SIZE(&list->items); ++i) {
1050 struct tree_node *current;
1051 struct resource_list *child_list;
1052 const char *resource = AST_VECTOR_GET(&list->items, i);
1053
1054 if (have_visited(resource, visited)) {
1055 ast_debug(1, "Already visited resource %s. Avoiding duplicate resource or potential loop.\n", resource);
1056 continue;
1057 }
1058
1059 child_list = retrieve_resource_list(resource, list->event);
1060 if (!child_list) {
1061 int resp = NEW_SUBSCRIBE(handler->notifier, endpoint, resource, rdata);
1062 if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
1063 char display_name[AST_MAX_EXTENSION] = "";
1064 if (list->resource_display_name && handler->notifier->get_resource_display_name) {
1065 handler->notifier->get_resource_display_name(endpoint, resource, display_name, sizeof(display_name));
1066 }
1067 current = tree_node_alloc(resource, visited, 0, ast_strlen_zero(display_name) ? NULL : display_name);
1068 if (!current) {
1069 ast_debug(1,
1070 "Subscription to leaf resource %s was successful, but encountered allocation error afterwards\n",
1071 resource);
1072 continue;
1073 }
1074 ast_debug(2, "Subscription to leaf resource %s resulted in success. Adding to parent %s\n",
1075 resource, parent->resource);
1076 if (AST_VECTOR_APPEND(&parent->children, current)) {
1078 }
1079 } else {
1080 ast_debug(2, "Subscription to leaf resource %s resulted in error response %d\n",
1081 resource, resp);
1082 }
1083 } else {
1084 ast_debug(2, "Resource %s (child of %s) is a list\n", resource, parent->resource);
1085 current = tree_node_alloc(resource, visited, child_list->full_state, NULL);
1086 if (!current) {
1087 ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
1088 continue;
1089 }
1090 build_node_children(endpoint, handler, child_list, current, visited, rdata);
1091 if (AST_VECTOR_SIZE(&current->children) > 0) {
1092 ast_debug(1, "List %s had no successful children.\n", resource);
1093 if (AST_VECTOR_APPEND(&parent->children, current)) {
1095 }
1096 } else {
1097 ast_debug(2, "List %s had successful children. Adding to parent %s\n",
1098 resource, parent->resource);
1100 }
1101 ao2_cleanup(child_list);
1102 }
1103 }
1104}
1105
1106/*!
1107 * \brief A resource tree
1108 *
1109 * When an inbound SUBSCRIBE arrives, the resource being subscribed to may
1110 * be a resource list. If this is the case, the resource list may contain resources
1111 * that are themselves lists. The structure needed to hold the resources is
1112 * a tree.
1113 *
1114 * Upon receipt of the SUBSCRIBE, the tree is built by determining if subscriptions
1115 * to the individual resources in the tree would be successful or not. Any successful
1116 * subscriptions result in a node in the tree being created. Any unsuccessful subscriptions
1117 * result in no node being created.
1118 *
1119 * This tree can be seen as a bare-bones analog of the tree of ast_sip_subscriptions that
1120 * will end up being created to actually carry out the duties of a SIP SUBSCRIBE dialog.
1121 */
1125};
1126
1127/*!
1128 * \brief Destroy a resource tree.
1129 *
1130 * This function makes no assumptions about how the tree itself was
1131 * allocated and does not attempt to free the tree itself. Callers
1132 * of this function are responsible for freeing the tree.
1133 *
1134 * \param tree The tree to destroy.
1135 */
1136static void resource_tree_destroy(struct resource_tree *tree)
1137{
1138 if (tree) {
1139 tree_node_destroy(tree->root);
1140 }
1141}
1142
1143/*!
1144 * \brief Build a resource tree
1145 *
1146 * This function builds a resource tree based on the requested resource in a SUBSCRIBE request.
1147 *
1148 * This function also creates a container that has all resources that have been visited during
1149 * creation of the tree, whether those resources resulted in a tree node being created or not.
1150 * Keeping this container of visited resources allows for misconfigurations such as loops in
1151 * the tree or duplicated resources to be detected.
1152 *
1153 * \param endpoint The endpoint that sent the SUBSCRIBE request.
1154 * \param handler The subscription handler for leaf nodes in the tree.
1155 * \param resource The resource requested in the SUBSCRIBE request.
1156 * \param tree The tree that is to be built.
1157 * \param has_eventlist_support
1158 *
1159 * \retval 200-299 Successfully subscribed to at least one resource.
1160 * \retval 300-699 Failure to subscribe to requested resource.
1161 */
1163 const char *resource, struct resource_tree *tree, int has_eventlist_support, pjsip_rx_data *rdata)
1164{
1165 RAII_VAR(struct resource_list *, list, NULL, ao2_cleanup);
1166 struct resources visited;
1167
1168 int not_eventlist_but_needs_children = !strcmp(handler->body_type, AST_SIP_DEVICE_FEATURE_SYNC_DATA);
1169
1170 if ((!has_eventlist_support && !not_eventlist_but_needs_children) || !(list = retrieve_resource_list(resource, handler->event_name))) {
1171 ast_debug(2, "Subscription '%s->%s' is not to a list\n",
1172 ast_sorcery_object_get_id(endpoint), resource);
1173 tree->root = tree_node_alloc(resource, NULL, 0, NULL);
1174 if (!tree->root) {
1175 return 500;
1176 }
1177 return NEW_SUBSCRIBE(handler->notifier, endpoint, resource, rdata);
1178 }
1179
1180 ast_debug(2, "Subscription '%s->%s' is a list\n",
1181 ast_sorcery_object_get_id(endpoint), resource);
1182 if (AST_VECTOR_INIT(&visited, AST_VECTOR_SIZE(&list->items))) {
1183 return 500;
1184 }
1185
1186 tree->root = tree_node_alloc(resource, &visited, list->full_state, NULL);
1187 if (!tree->root) {
1188 AST_VECTOR_FREE(&visited);
1189 return 500;
1190 }
1191
1192 tree->notification_batch_interval = list->notification_batch_interval;
1193
1194 build_node_children(endpoint, handler, list, tree->root, &visited, rdata);
1195 AST_VECTOR_FREE(&visited);
1196
1197 if (AST_VECTOR_SIZE(&tree->root->children) > 0) {
1198 return 200;
1199 } else {
1200 return 500;
1201 }
1202}
1203
1205{
1209}
1210
1212{
1213 struct sip_subscription_tree *i;
1214
1217 if (i == obj) {
1219 if (i->root) {
1220 ast_debug(2, "Removing subscription '%s->%s' from list of subscriptions\n",
1222 }
1223 break;
1224 }
1225 }
1228}
1229
1231{
1232 ast_debug(3, "Destroying SIP subscription from '%s->%s'\n",
1233 sub->tree && sub->tree->endpoint ? ast_sorcery_object_get_id(sub->tree->endpoint) : "Unknown",
1234 sub->resource);
1235
1236 ast_free(sub->body_text);
1237
1238 AST_VECTOR_FREE(&sub->children);
1239 ao2_cleanup(sub->datastores);
1240 ast_json_unref(sub->persistence_data);
1241 ast_free(sub->display_name);
1242 ast_free(sub);
1243}
1244
1246{
1247 int i;
1248
1249 if (!root) {
1250 return;
1251 }
1252
1253 for (i = 0; i < AST_VECTOR_SIZE(&root->children); ++i) {
1254 struct ast_sip_subscription *child;
1255
1256 child = AST_VECTOR_GET(&root->children, i);
1257 destroy_subscriptions(child);
1258 }
1259
1261}
1262
1264 const char *resource, const char *display_name, struct sip_subscription_tree *tree)
1265{
1266 struct ast_sip_subscription *sub;
1267 pjsip_msg *msg;
1268 pjsip_sip_uri *request_uri;
1269
1271 if (!msg) {
1272 ast_log(LOG_ERROR, "No dialog message saved for SIP subscription. Cannot allocate subscription for resource %s\n", resource);
1273 return NULL;
1274 }
1275
1276 sub = ast_calloc(1, sizeof(*sub) + strlen(resource) + 1);
1277 if (!sub) {
1278 return NULL;
1279 }
1280 strcpy(sub->resource, resource); /* Safe */
1281
1282 sub->display_name = ast_strdup(display_name);
1283
1284 sub->datastores = ast_datastores_alloc();
1285 if (!sub->datastores) {
1287 return NULL;
1288 }
1289
1290 sub->body_text = ast_str_create(128);
1291 if (!sub->body_text) {
1293 return NULL;
1294 }
1295
1296 sub->uri = pjsip_sip_uri_create(tree->dlg->pool, PJ_FALSE);
1297 request_uri = pjsip_uri_get_uri(msg->line.req.uri);
1298 pjsip_sip_uri_assign(tree->dlg->pool, sub->uri, request_uri);
1299 pj_strdup2(tree->dlg->pool, &sub->uri->user, resource);
1300
1301 /* If there is any persistence information available for this subscription that was persisted
1302 * then make it available so that the NOTIFY has the correct state.
1303 */
1304
1307 }
1308
1309 sub->handler = handler;
1310 sub->subscription_state = PJSIP_EVSUB_STATE_ACTIVE;
1311 sub->tree = ao2_bump(tree);
1312
1313 return sub;
1314}
1315
1316/*!
1317 * \brief Create a tree of virtual subscriptions based on a resource tree node.
1318 *
1319 * \param handler The handler to supply to leaf subscriptions.
1320 * \param resource The requested resource for this subscription.
1321 * \param generator Body generator to use for leaf subscriptions.
1322 * \param tree The root of the subscription tree.
1323 * \param current The tree node that corresponds to the subscription being created.
1324 */
1326 const char *resource, struct ast_sip_pubsub_body_generator *generator,
1328{
1329 int i;
1330 struct ast_sip_subscription *sub;
1331
1333 if (!sub) {
1334 return NULL;
1335 }
1336
1337 sub->full_state = current->full_state;
1338 sub->body_generator = generator;
1339 AST_VECTOR_INIT(&sub->children, AST_VECTOR_SIZE(&current->children));
1340
1341 for (i = 0; i < AST_VECTOR_SIZE(&current->children); ++i) {
1342 struct ast_sip_subscription *child;
1343 struct tree_node *child_node = AST_VECTOR_GET(&current->children, i);
1344
1345 child = create_virtual_subscriptions(handler, child_node->resource, generator,
1346 tree, child_node);
1347
1348 if (!child) {
1349 ast_debug(1, "Child subscription to resource %s could not be created\n",
1350 child_node->resource);
1351 continue;
1352 }
1353
1354 if (AST_VECTOR_APPEND(&sub->children, child)) {
1355 ast_debug(1, "Child subscription to resource %s could not be appended\n",
1356 child_node->resource);
1357 destroy_subscription(child);
1358 /* Have to release tree here too because a ref was added
1359 * to child that destroy_subscription() doesn't release. */
1360 ao2_cleanup(tree);
1361 }
1362 }
1363
1364 return sub;
1365}
1366
1368{
1369 int i;
1370
1371 if (!sub) {
1372 return;
1373 }
1374
1375 if (AST_VECTOR_SIZE(&sub->children) > 0) {
1376 for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
1378 }
1379 return;
1380 }
1381
1382 /* We notify subscription shutdown only on the tree leaves. */
1383 if (sub->handler->subscription_shutdown) {
1384 sub->handler->subscription_shutdown(sub);
1385 }
1386}
1387
1389{
1390 struct sip_subscription_tree *sub_tree = obj;
1391
1392 /* This is why we keep the dialog on the subscription. When the subscription
1393 * is destroyed, there is no guarantee that the underlying dialog is ready
1394 * to be destroyed. Furthermore, there's no guarantee in the opposite direction
1395 * either. The dialog could be destroyed before our subscription is. We fix
1396 * this problem by keeping a reference to the dialog until it is time to
1397 * destroy the subscription. We need to have the dialog available when the
1398 * subscription is destroyed so that we can guarantee that our attempt to
1399 * remove the serializer will be successful.
1400 */
1401 pjsip_dlg_dec_session(sub_tree->dlg, &pubsub_module);
1402 sub_tree->dlg = NULL;
1403
1404 return 0;
1405}
1406
1407static void subscription_tree_destructor(void *obj)
1408{
1409 struct sip_subscription_tree *sub_tree = obj;
1410
1411 ast_debug(3, "Destroying subscription tree %p '%s->%s'\n",
1412 sub_tree,
1413 sub_tree->endpoint ? ast_sorcery_object_get_id(sub_tree->endpoint) : "Unknown",
1414 sub_tree->root ? sub_tree->root->resource : "Unknown");
1415
1416 destroy_subscriptions(sub_tree->root);
1417
1418 if (sub_tree->dlg) {
1421 }
1422
1423 ao2_cleanup(sub_tree->endpoint);
1424
1427}
1428
1430{
1431 ast_debug(3, "Removing subscription %p '%s->%s' reference to subscription tree %p\n",
1432 sub, ast_sorcery_object_get_id(sub->tree->endpoint), sub->resource, sub->tree);
1433 ao2_cleanup(sub->tree);
1434}
1435
1436static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg)
1437{
1438 sub_tree->dlg = dlg;
1441 pjsip_evsub_set_mod_data(sub_tree->evsub, pubsub_module.id, sub_tree);
1442 pjsip_dlg_inc_session(dlg, &pubsub_module);
1443}
1444
1446{
1447 struct sip_subscription_tree *sub_tree;
1448
1449 sub_tree = ao2_alloc(sizeof *sub_tree, subscription_tree_destructor);
1450 if (!sub_tree) {
1451 return NULL;
1452 }
1453
1455
1456 if (rdata) {
1457 /*
1458 * We must continue using the serializer that the original
1459 * SUBSCRIBE came in on for the dialog. There may be
1460 * retransmissions already enqueued in the original
1461 * serializer that can result in reentrancy and message
1462 * sequencing problems.
1463 */
1465 } else {
1466 char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
1467
1468 /* Create name with seq number appended. */
1469 ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/pubsub/%s",
1471
1472 sub_tree->serializer = ast_sip_create_serializer(tps_name);
1473 }
1474 if (!sub_tree->serializer) {
1475 ao2_ref(sub_tree, -1);
1476 return NULL;
1477 }
1478
1479 sub_tree->endpoint = ao2_bump(endpoint);
1480 sub_tree->notify_sched_id = -1;
1481
1482 return sub_tree;
1483}
1484
1485/*!
1486 * \brief Create a subscription tree based on a resource tree.
1487 *
1488 * Using the previously-determined valid resources in the provided resource tree,
1489 * a corresponding tree of ast_sip_subscriptions are created. The root of the
1490 * subscription tree is a real subscription, and the rest in the tree are
1491 * virtual subscriptions.
1492 *
1493 * \param handler The handler to use for leaf subscriptions
1494 * \param endpoint The endpoint that sent the SUBSCRIBE request
1495 * \param rdata The SUBSCRIBE content
1496 * \param resource The requested resource in the SUBSCRIBE request
1497 * \param generator The body generator to use in leaf subscriptions
1498 * \param tree The resource tree on which the subscription tree is based
1499 * \param[out] dlg_status The result of attempting to create a dialog
1500 * \param persistence
1501 *
1502 * \retval NULL Could not create the subscription tree
1503 * \retval non-NULL The root of the created subscription tree
1504 */
1505
1507 struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, const char *resource,
1508 struct ast_sip_pubsub_body_generator *generator, struct resource_tree *tree,
1509 pj_status_t *dlg_status, struct subscription_persistence *persistence)
1510{
1511 struct sip_subscription_tree *sub_tree;
1512 pjsip_dialog *dlg;
1513
1514 sub_tree = allocate_subscription_tree(endpoint, rdata);
1515 if (!sub_tree) {
1516 *dlg_status = PJ_ENOMEM;
1517 return NULL;
1518 }
1519 sub_tree->role = AST_SIP_NOTIFIER;
1520
1521 dlg = ast_sip_create_dialog_uas_locked(endpoint, rdata, dlg_status);
1522 if (!dlg) {
1523 if (*dlg_status != PJ_EEXISTS) {
1524 ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n");
1525 }
1526 ao2_ref(sub_tree, -1);
1527 return NULL;
1528 }
1529
1530 persistence = ast_sip_mod_data_get(rdata->endpt_info.mod_data,
1532 if (persistence) {
1533 /* Update the created dialog with the persisted information */
1534 pjsip_ua_unregister_dlg(pjsip_ua_instance(), dlg);
1535 pj_strdup2(dlg->pool, &dlg->local.info->tag, persistence->tag);
1536 dlg->local.tag_hval = pj_hash_calc_tolower(0, NULL, &dlg->local.info->tag);
1537 pjsip_ua_register_dlg(pjsip_ua_instance(), dlg);
1538 dlg->local.cseq = persistence->cseq;
1539 }
1540
1541 pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub_tree->evsub);
1542
1543 subscription_setup_dialog(sub_tree, dlg);
1544
1545 /*
1546 * The evsub and subscription setup both add dialog refs, so the dialog ref that
1547 * was added when the dialog was created (see ast_sip_create_dialog_uas_lock) can
1548 * now be removed. The lock should no longer be needed so can be removed too.
1549 */
1550 pjsip_dlg_dec_lock(dlg);
1551
1552#ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
1553 pjsip_evsub_add_ref(sub_tree->evsub);
1554#endif
1555
1557 pjsip_msg_clone(dlg->pool, rdata->msg_info.msg));
1558
1560
1561 /* Persistence information needs to be available for all the subscriptions */
1562 sub_tree->persistence = ao2_bump(persistence);
1563
1564 sub_tree->root = create_virtual_subscriptions(handler, resource, generator, sub_tree, tree->root);
1565 if (AST_VECTOR_SIZE(&sub_tree->root->children) > 0) {
1566 sub_tree->is_list = 1;
1567 }
1568
1569 add_subscription(sub_tree);
1570
1571 return sub_tree;
1572}
1573
1574/*! Wrapper structure for initial_notify_task */
1577 unsigned int expires;
1578};
1579
1580static int initial_notify_task(void *obj);
1581static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state);
1582
1583/*! Persistent subscription recreation continuation under distributor serializer data */
1586 pjsip_rx_data *rdata;
1587};
1588
1589/*!
1590 * \internal
1591 * \brief subscription_persistence_recreate continuation under distributor serializer.
1592 * \since 13.10.0
1593 *
1594 * \retval 0 on success.
1595 * \retval -1 on error.
1596 */
1597static int sub_persistence_recreate(void *obj)
1598{
1599 struct persistence_recreate_data *recreate_data = obj;
1600 struct subscription_persistence *persistence = recreate_data->persistence;
1601 pjsip_rx_data *rdata = recreate_data->rdata;
1602 struct ast_sip_endpoint *endpoint;
1603 struct sip_subscription_tree *sub_tree;
1604 struct ast_sip_pubsub_body_generator *generator;
1606 char *resource;
1607 size_t resource_size;
1608 int resp;
1609 struct resource_tree tree;
1610 pjsip_expires_hdr *expires_header;
1611 int64_t expires;
1612 const pj_str_t *user;
1613
1614 user = ast_sip_pjsip_uri_get_username(rdata->msg_info.msg->line.req.uri);
1615 resource_size = pj_strlen(user) + 1;
1616 resource = ast_alloca(resource_size);
1617 ast_copy_pj_str(resource, user, resource_size);
1618
1619 /*
1620 * We may want to match without any user options getting
1621 * in the way.
1622 */
1624
1626 if (!handler || !handler->notifier) {
1627 ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not get subscription handler.\n",
1628 persistence->endpoint);
1630 return 0;
1631 }
1632
1634 if (!generator) {
1635 ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Body generator not available.\n",
1636 persistence->endpoint);
1638 return 0;
1639 }
1640
1641 ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data,
1642 pubsub_module.id, MOD_DATA_PERSISTENCE, persistence);
1643
1644 /* Getting the endpoint may take some time that can affect the expiration. */
1645 endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
1646 persistence->endpoint);
1647 if (!endpoint) {
1648 ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The endpoint was not found\n",
1649 persistence->endpoint);
1651 return 0;
1652 }
1653
1654 /* Update the expiration header with the new expiration */
1655 expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
1656 rdata->msg_info.msg->hdr.next);
1657 if (!expires_header) {
1658 expires_header = pjsip_expires_hdr_create(rdata->tp_info.pool, 0);
1659 if (!expires_header) {
1660 ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not update expires header.\n",
1661 persistence->endpoint);
1663 ao2_ref(endpoint, -1);
1664 return 0;
1665 }
1666 pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr *) expires_header);
1667 }
1668
1669 expires = (ast_tvdiff_ms(persistence->expires, ast_tvnow()) / 1000);
1670 if (expires <= 0) {
1671 /* The subscription expired since we started recreating the subscription. */
1672 ast_debug(3, "Expired subscription retrived from persistent store '%s' %s\n",
1673 persistence->endpoint, persistence->tag);
1675 ao2_ref(endpoint, -1);
1676 return 0;
1677 }
1678 expires_header->ivalue = expires;
1679
1680 memset(&tree, 0, sizeof(tree));
1681 resp = build_resource_tree(endpoint, handler, resource, &tree,
1683 if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
1684 pj_status_t dlg_status;
1685
1686 sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator,
1687 &tree, &dlg_status, persistence);
1688 if (!sub_tree) {
1689 if (dlg_status != PJ_EEXISTS) {
1690 ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not create subscription tree.\n",
1691 persistence->endpoint);
1693 }
1694 } else {
1695 struct initial_notify_data *ind = ast_malloc(sizeof(*ind));
1696
1697 if (!ind) {
1698 pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
1699 goto error;
1700 }
1701
1702 ind->sub_tree = ao2_bump(sub_tree);
1703 ind->expires = expires_header->ivalue;
1704
1707 /* Could not send initial subscribe NOTIFY */
1708 pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
1709 ao2_ref(sub_tree, -1);
1710 ast_free(ind);
1711 }
1712 }
1713 } else {
1715 }
1716
1717error:
1718 resource_tree_destroy(&tree);
1719 ao2_ref(endpoint, -1);
1720
1721 return 0;
1722}
1723
1724/*! \brief Callback function to perform the actual recreation of a subscription */
1725static int subscription_persistence_recreate(void *obj, void *arg, int flags)
1726{
1727 struct subscription_persistence *persistence = obj;
1728 pj_pool_t *pool = arg;
1730 pjsip_rx_data rdata;
1731 struct persistence_recreate_data recreate_data;
1732
1733 /* If this subscription used a reliable transport it can't be reestablished so remove it */
1735 ast_debug(3, "Deleting subscription marked as 'prune' from persistent store '%s' %s\n",
1738 return 0;
1739 }
1740
1741 /* If this subscription has already expired remove it */
1743 ast_debug(3, "Expired subscription retrived from persistent store '%s' %s\n",
1746 return 0;
1747 }
1748
1749 memset(&rdata, 0, sizeof(rdata));
1750 pj_pool_reset(pool);
1751 rdata.tp_info.pool = pool;
1752
1756 ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The message could not be parsed\n",
1759 return 0;
1760 }
1761
1762 if (rdata.msg_info.msg->type != PJSIP_REQUEST_MSG) {
1763 ast_log(LOG_NOTICE, "Failed recreating '%s' subscription: Stored a SIP response instead of a request.\n",
1766 return 0;
1767 }
1768
1769 /* Continue the remainder in the distributor serializer */
1771 if (!serializer) {
1772 ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not get distributor serializer.\n",
1775 return 0;
1776 }
1777 recreate_data.persistence = persistence;
1778 recreate_data.rdata = &rdata;
1780 &recreate_data)) {
1781 ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not continue under distributor serializer.\n",
1784 }
1786
1787 return 0;
1788}
1789
1790/*! \brief Function which loads and recreates persisted subscriptions upon startup when the system is fully booted */
1791static int subscription_persistence_load(void *data)
1792{
1793 struct ao2_container *persisted_subscriptions = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(),
1794 "subscription_persistence", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1795 pj_pool_t *pool;
1796
1797 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "rtd%p", PJSIP_POOL_RDATA_LEN,
1798 PJSIP_POOL_RDATA_INC);
1799 if (!pool) {
1800 ast_log(LOG_WARNING, "Could not create a memory pool for recreating SIP subscriptions\n");
1801 return 0;
1802 }
1803
1804 ao2_callback(persisted_subscriptions, OBJ_NODATA, subscription_persistence_recreate, pool);
1805
1806 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1807
1808 ao2_ref(persisted_subscriptions, -1);
1809 return 0;
1810}
1811
1812/*! \brief Event callback which fires subscription persistence recreation when the system is fully booted */
1814{
1815 struct ast_json_payload *payload;
1816 const char *type;
1817
1819 return;
1820 }
1821
1822 payload = stasis_message_data(message);
1823 type = ast_json_string_get(ast_json_object_get(payload->json, "type"));
1824
1825 /* This subscription only responds to the FullyBooted event so that all modules have been loaded when we
1826 * recreate SIP subscriptions.
1827 */
1828 if (strcmp(type, "FullyBooted")) {
1829 return;
1830 }
1831
1832 /* This has to be here so the subscription is recreated when the body generator is available */
1834
1835 /* Once the system is fully booted we don't care anymore */
1837}
1838
1839typedef int (*on_subscription_t)(struct sip_subscription_tree *sub, void *arg);
1840
1841static int for_each_subscription(on_subscription_t on_subscription, void *arg)
1842{
1843 int num = 0;
1844 struct sip_subscription_tree *i;
1845
1846 if (!on_subscription) {
1847 return num;
1848 }
1849
1852 if (on_subscription(i, arg)) {
1853 break;
1854 }
1855 ++num;
1856 }
1858 return num;
1859}
1860
1862 struct ast_str **buf)
1863{
1864 char str[256];
1865 struct ast_sip_endpoint_id_configuration *id = &sub_tree->endpoint->id;
1866
1867 ast_str_append(buf, 0, "Role: %s\r\n",
1868 sip_subscription_roles_map[sub_tree->role]);
1869 ast_str_append(buf, 0, "Endpoint: %s\r\n",
1871
1872 if (sub_tree->dlg) {
1873 ast_copy_pj_str(str, &sub_tree->dlg->call_id->id, sizeof(str));
1874 } else {
1875 ast_copy_string(str, "<unknown>", sizeof(str));
1876 }
1877 ast_str_append(buf, 0, "Callid: %s\r\n", str);
1878
1879 ast_str_append(buf, 0, "State: %s\r\n", pjsip_evsub_get_state_name(sub_tree->evsub));
1880
1881 ast_callerid_merge(str, sizeof(str),
1882 S_COR(id->self.name.valid, id->self.name.str, NULL),
1883 S_COR(id->self.number.valid, id->self.number.str, NULL),
1884 "Unknown");
1885
1886 ast_str_append(buf, 0, "Callerid: %s\r\n", str);
1887
1888 /* XXX This needs to be done recursively for lists */
1889 if (sub_tree->root->handler->to_ami) {
1890 sub_tree->root->handler->to_ami(sub_tree->root, buf);
1891 }
1892}
1893
1894
1896{
1897 pjsip_dialog *dlg;
1898 pjsip_msg *msg;
1899 pj_str_t name;
1900
1901 dlg = sub->tree->dlg;
1902 msg = ast_sip_mod_data_get(dlg->mod_data, pubsub_module.id, MOD_DATA_MSG);
1903 pj_cstr(&name, header);
1904
1905 return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
1906}
1907
1908/* XXX This function is not used. */
1910 struct ast_sip_endpoint *endpoint, const char *resource)
1911{
1912 struct ast_sip_subscription *sub;
1913 pjsip_dialog *dlg;
1914 struct ast_sip_contact *contact;
1915 pj_str_t event;
1916 pjsip_tx_data *tdata;
1917 pjsip_evsub *evsub;
1918 struct sip_subscription_tree *sub_tree = NULL;
1919
1921 if (!sub_tree) {
1922 return NULL;
1923 }
1924
1925 sub = allocate_subscription(handler, resource, NULL, sub_tree);
1926 if (!sub) {
1927 ao2_cleanup(sub_tree);
1928 return NULL;
1929 }
1930
1932 if (!contact || ast_strlen_zero(contact->uri)) {
1933 ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create SIP subsription\n",
1935 ao2_ref(sub_tree, -1);
1936 ao2_cleanup(contact);
1937 return NULL;
1938 }
1939
1941 ao2_cleanup(contact);
1942 if (!dlg) {
1943 ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n");
1944 ao2_ref(sub_tree, -1);
1945 return NULL;
1946 }
1947
1948 pj_cstr(&event, handler->event_name);
1949 pjsip_evsub_create_uac(dlg, &pubsub_cb, &event, 0, &sub_tree->evsub);
1950 subscription_setup_dialog(sub_tree, dlg);
1951
1952 evsub = sub_tree->evsub;
1953
1954 if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
1955 pjsip_evsub_send_request(sub_tree->evsub, tdata);
1956 } else {
1957 /* pjsip_evsub_terminate will result in pubsub_on_evsub_state,
1958 * being called and terminating the subscription. Therefore, we don't
1959 * need to decrease the reference count of sub here.
1960 */
1961 pjsip_evsub_terminate(evsub, PJ_TRUE);
1962 ao2_ref(sub_tree, -1);
1963 return NULL;
1964 }
1965
1966 add_subscription(sub_tree);
1967
1968 return sub;
1969}
1970
1972{
1973 ast_assert(sub->tree->dlg != NULL);
1974 return sub->tree->dlg;
1975}
1976
1978{
1979 ast_assert(sub->tree->endpoint != NULL);
1980 return ao2_bump(sub->tree->endpoint);
1981}
1982
1984{
1985 ast_assert(sub->tree->serializer != NULL);
1986 return sub->tree->serializer;
1987}
1988
1989/*!
1990 * \brief Pre-allocate a buffer for the transmission
1991 *
1992 * Typically, we let PJSIP do this step for us when we send a request. PJSIP's buffer
1993 * allocation algorithm is to allocate a buffer of PJSIP_MAX_PKT_LEN bytes and attempt
1994 * to write the packet to the allocated buffer. If the buffer is too small to hold the
1995 * packet, then we get told the message is too long to be sent.
1996 *
1997 * When dealing with SIP NOTIFY, especially with RLS, it is possible to exceed
1998 * PJSIP_MAX_PKT_LEN. Rather than accepting the limitation imposed on us by default,
1999 * we instead take the strategy of pre-allocating the buffer, testing for ourselves
2000 * if the message will fit, and resizing the buffer as required.
2001 *
2002 * The limit we impose is double that of the maximum packet length.
2003 *
2004 * \param tdata The tdata onto which to allocate a buffer
2005 * \retval 0 Success
2006 * \retval -1 The message is too large
2007 */
2008static int allocate_tdata_buffer(pjsip_tx_data *tdata)
2009{
2010 int buf_size;
2011 int size = -1;
2012 char *buf;
2013
2014 for (buf_size = PJSIP_MAX_PKT_LEN; size == -1 && buf_size < (PJSIP_MAX_PKT_LEN * 2); buf_size *= 2) {
2015 buf = pj_pool_alloc(tdata->pool, buf_size);
2016 size = pjsip_msg_print(tdata->msg, buf, buf_size);
2017 }
2018
2019 if (size == -1) {
2020 return -1;
2021 }
2022
2023 tdata->buf.start = buf;
2024 tdata->buf.cur = tdata->buf.start;
2025 tdata->buf.end = tdata->buf.start + buf_size;
2026
2027 return 0;
2028}
2029
2030static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata)
2031{
2032#ifdef TEST_FRAMEWORK
2033 struct ast_sip_endpoint *endpoint = sub_tree->endpoint;
2034 pjsip_evsub *evsub = sub_tree->evsub;
2035#endif
2036 int res;
2037
2038 if (allocate_tdata_buffer(tdata)) {
2039 ast_log(LOG_ERROR, "SIP request %s is too large to send.\n", tdata->info);
2040 pjsip_tx_data_dec_ref(tdata);
2041 return -1;
2042 }
2043
2044 res = pjsip_evsub_send_request(sub_tree->evsub, tdata);
2045
2047
2048 ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
2049 "StateText: %s\r\n"
2050 "Endpoint: %s\r\n",
2051 pjsip_evsub_get_state_name(evsub),
2052 ast_sorcery_object_get_id(endpoint));
2053
2054 return (res == PJ_SUCCESS ? 0 : -1);
2055}
2056
2057/*!
2058 * \brief Add a resource XML element to an RLMI body
2059 *
2060 * Each resource element represents a subscribed resource in the list. This function currently
2061 * will unconditionally add an instance element to each created resource element. Instance
2062 * elements refer to later parts in the multipart body.
2063 *
2064 * \param pool PJLIB allocation pool
2065 * \param rlmi
2066 * \param cid Content-ID header of the resource
2067 * \param resource_name Name of the resource
2068 * \param resource_uri URI of the resource
2069 * \param state State of the subscribed resource
2070 */
2071static void add_rlmi_resource(pj_pool_t *pool, pj_xml_node *rlmi, const pjsip_generic_string_hdr *cid,
2072 const char *resource_name, const pjsip_sip_uri *resource_uri, pjsip_evsub_state state)
2073{
2074 static pj_str_t cid_name = { "cid", 3 };
2075 pj_xml_node *resource;
2076 pj_xml_node *name;
2077 pj_xml_node *instance;
2078 pj_xml_attr *cid_attr;
2079 char id[6];
2080 char uri[PJSIP_MAX_URL_SIZE];
2081 char name_sanitized[PJSIP_MAX_URL_SIZE];
2082
2083 /* This creates a string representing the Content-ID without the enclosing < > */
2084 const pj_str_t cid_stripped = {
2085 .ptr = cid->hvalue.ptr + 1,
2086 .slen = cid->hvalue.slen - 2,
2087 };
2088
2089 resource = ast_sip_presence_xml_create_node(pool, rlmi, "resource");
2090 name = ast_sip_presence_xml_create_node(pool, resource, "name");
2091 instance = ast_sip_presence_xml_create_node(pool, resource, "instance");
2092
2093 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, resource_uri, uri, sizeof(uri));
2094 ast_sip_presence_xml_create_attr(pool, resource, "uri", uri);
2095
2096 ast_sip_sanitize_xml(resource_name, name_sanitized, sizeof(name_sanitized));
2097 pj_strdup2(pool, &name->content, name_sanitized);
2098
2099 ast_generate_random_string(id, sizeof(id));
2100
2101 ast_sip_presence_xml_create_attr(pool, instance, "id", id);
2102 ast_sip_presence_xml_create_attr(pool, instance, "state",
2103 state == PJSIP_EVSUB_STATE_TERMINATED ? "terminated" : "active");
2104
2105 /* Use the PJLIB-util XML library directly here since we are using a
2106 * pj_str_t
2107 */
2108
2109 cid_attr = pj_xml_attr_new(pool, &cid_name, &cid_stripped);
2110 pj_xml_add_attr(instance, cid_attr);
2111}
2112
2113/*!
2114 * \brief A multipart body part and meta-information
2115 *
2116 * When creating a multipart body part, the end result (the
2117 * pjsip_multipart_part) is hard to inspect without undoing
2118 * a lot of what was done to create it. Therefore, we use this
2119 * structure to store meta-information about the body part.
2120 *
2121 * The main consumer of this is the creator of the RLMI body
2122 * part of a multipart resource list body.
2123 */
2125 /*! Content-ID header for the body part */
2126 pjsip_generic_string_hdr *cid;
2127 /*! Subscribed resource represented in the body part */
2128 const char *resource;
2129 /*! URI for the subscribed body part */
2130 pjsip_sip_uri *uri;
2131 /*! Subscription state of the resource represented in the body part */
2132 pjsip_evsub_state state;
2133 /*! The actual body part that will be present in the multipart body */
2134 pjsip_multipart_part *part;
2135 /*! Display name for the resource */
2136 const char *display_name;
2137};
2138
2139/*!
2140 * \brief Type declaration for container of body part structures
2141 */
2143
2144/*!
2145 * \brief Create a Content-ID header
2146 *
2147 * Content-ID headers are required by RFC2387 for multipart/related
2148 * bodies. They serve as identifiers for each part of the multipart body.
2149 *
2150 * \param pool PJLIB allocation pool
2151 * \param sub Subscription to a resource
2152 */
2153static pjsip_generic_string_hdr *generate_content_id_hdr(pj_pool_t *pool,
2154 const struct ast_sip_subscription *sub)
2155{
2156 static const pj_str_t cid_name = { "Content-ID", 10 };
2157 pjsip_generic_string_hdr *cid;
2158 char id[6];
2159 size_t alloc_size;
2160 pj_str_t cid_value;
2161
2162 /* '<' + '@' + '>' = 3. pj_str_t does not require a null-terminator */
2163 alloc_size = sizeof(id) + pj_strlen(&sub->uri->host) + 3;
2164 cid_value.ptr = pj_pool_alloc(pool, alloc_size);
2165 cid_value.slen = sprintf(cid_value.ptr, "<%s@%.*s>",
2166 ast_generate_random_string(id, sizeof(id)),
2167 (int) pj_strlen(&sub->uri->host), pj_strbuf(&sub->uri->host));
2168 cid = pjsip_generic_string_hdr_create(pool, &cid_name, &cid_value);
2169
2170 return cid;
2171}
2172
2173static int rlmi_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size)
2174{
2175 int num_printed;
2176 pj_xml_node *rlmi = msg_body->data;
2177
2178 num_printed = pj_xml_print(rlmi, buf, size, PJ_TRUE);
2179 if (num_printed <= AST_PJSIP_XML_PROLOG_LEN) {
2180 return -1;
2181 }
2182
2183 return num_printed;
2184}
2185
2186static void *rlmi_clone_data(pj_pool_t *pool, const void *data, unsigned len)
2187{
2188 const pj_xml_node *rlmi = data;
2189
2190 return pj_xml_clone(pool, rlmi);
2191}
2192
2193/*!
2194 * \brief Create an RLMI body part for a multipart resource list body
2195 *
2196 * RLMI (Resource list meta information) is a special body type that lists
2197 * the subscribed resources and tells subscribers the number of subscribed
2198 * resources and what other body parts are in the multipart body. The
2199 * RLMI body also has a version number that a subscriber can use to ensure
2200 * that the locally-stored state corresponds to server state.
2201 *
2202 * \param pool The allocation pool
2203 * \param sub The subscription representing the subscribed resource list
2204 * \param body_parts A container of body parts that RLMI will refer to
2205 * \param full_state Indicates whether this is a full or partial state notification
2206 * \return The multipart part representing the RLMI body
2207 */
2208static pjsip_multipart_part *build_rlmi_body(pj_pool_t *pool, struct ast_sip_subscription *sub,
2209 struct body_part_list *body_parts, unsigned int full_state)
2210{
2211 pj_xml_node *rlmi;
2212 pj_xml_node *name;
2213 pjsip_multipart_part *rlmi_part;
2214 char version_str[32];
2215 char uri[PJSIP_MAX_URL_SIZE];
2216 pjsip_generic_string_hdr *cid;
2217 int i;
2218
2219 rlmi = ast_sip_presence_xml_create_node(pool, NULL, "list");
2220 ast_sip_presence_xml_create_attr(pool, rlmi, "xmlns", "urn:ietf:params:xml:ns:rlmi");
2221
2222 ast_sip_subscription_get_local_uri(sub, uri, sizeof(uri));
2223 ast_sip_presence_xml_create_attr(pool, rlmi, "uri", uri);
2224
2225 snprintf(version_str, sizeof(version_str), "%u", sub->version++);
2226 ast_sip_presence_xml_create_attr(pool, rlmi, "version", version_str);
2227 ast_sip_presence_xml_create_attr(pool, rlmi, "fullState", full_state ? "true" : "false");
2228
2229 name = ast_sip_presence_xml_create_node(pool, rlmi, "name");
2230 pj_strdup2(pool, &name->content, ast_sip_subscription_get_resource_name(sub));
2231
2232 for (i = 0; i < AST_VECTOR_SIZE(body_parts); ++i) {
2233 const struct body_part *part = AST_VECTOR_GET(body_parts, i);
2234
2235 add_rlmi_resource(pool, rlmi, part->cid, S_OR(part->display_name, part->resource), part->uri, part->state);
2236 }
2237
2238 rlmi_part = pjsip_multipart_create_part(pool);
2239
2240 rlmi_part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
2241 pjsip_media_type_cp(pool, &rlmi_part->body->content_type, &rlmi_media_type);
2242
2243 rlmi_part->body->data = pj_xml_clone(pool, rlmi);
2244 rlmi_part->body->clone_data = rlmi_clone_data;
2245 rlmi_part->body->print_body = rlmi_print_body;
2246
2248 pj_list_insert_before(&rlmi_part->hdr, cid);
2249
2250 return rlmi_part;
2251}
2252
2253static pjsip_msg_body *generate_notify_body(pj_pool_t *pool, struct ast_sip_subscription *root,
2254 unsigned int force_full_state);
2255
2256/*!
2257 * \brief Destroy a list of body parts
2258 *
2259 * \param parts The container of parts to destroy
2260 */
2261static void free_body_parts(struct body_part_list *parts)
2262{
2263 int i;
2264
2265 for (i = 0; i < AST_VECTOR_SIZE(parts); ++i) {
2266 struct body_part *part = AST_VECTOR_GET(parts, i);
2267 ast_free(part);
2268 }
2269
2270 AST_VECTOR_FREE(parts);
2271}
2272
2273/*!
2274 * \brief Allocate and initialize a body part structure
2275 *
2276 * \param pool PJLIB allocation pool
2277 * \param sub Subscription representing a subscribed resource
2278 */
2279static struct body_part *allocate_body_part(pj_pool_t *pool, const struct ast_sip_subscription *sub)
2280{
2281 struct body_part *bp;
2282
2283 bp = ast_calloc(1, sizeof(*bp));
2284 if (!bp) {
2285 return NULL;
2286 }
2287
2288 bp->cid = generate_content_id_hdr(pool, sub);
2289 bp->resource = sub->resource;
2290 bp->state = sub->subscription_state;
2291 bp->uri = sub->uri;
2292 bp->display_name = sub->display_name;
2293
2294 return bp;
2295}
2296
2297/*!
2298 * \brief Create a multipart body part for a subscribed resource
2299 *
2300 * \param pool PJLIB allocation pool
2301 * \param sub The subscription representing a subscribed resource
2302 * \param parts A vector of parts to append the created part to.
2303 * \param use_full_state Unused locally, but may be passed to other functions
2304 */
2305static void build_body_part(pj_pool_t *pool, struct ast_sip_subscription *sub,
2306 struct body_part_list *parts, unsigned int use_full_state)
2307{
2308 struct body_part *bp;
2309 pjsip_msg_body *body;
2310
2311 bp = allocate_body_part(pool, sub);
2312 if (!bp) {
2313 return;
2314 }
2315
2316 body = generate_notify_body(pool, sub, use_full_state);
2317 if (!body) {
2318 /* Partial state was requested and the resource has not changed state */
2319 ast_free(bp);
2320 return;
2321 }
2322
2323 bp->part = pjsip_multipart_create_part(pool);
2324 bp->part->body = body;
2325 pj_list_insert_before(&bp->part->hdr, bp->cid);
2326
2327 if (AST_VECTOR_APPEND(parts, bp)) {
2328 ast_free(bp);
2329 }
2330}
2331
2332/*!
2333 * \brief Create and initialize the PJSIP multipart body structure for a resource list subscription
2334 *
2335 * \param pool
2336 * \return The multipart message body
2337 */
2338static pjsip_msg_body *create_multipart_body(pj_pool_t *pool)
2339{
2340 pjsip_media_type media_type;
2341 pjsip_param *media_type_param;
2342 char boundary[6];
2343 pj_str_t pj_boundary;
2344
2345 pjsip_media_type_init2(&media_type, "multipart", "related");
2346
2347 media_type_param = pj_pool_alloc(pool, sizeof(*media_type_param));
2348 pj_list_init(media_type_param);
2349
2350 pj_strdup2(pool, &media_type_param->name, "type");
2351 pj_strdup2(pool, &media_type_param->value, "\"application/rlmi+xml\"");
2352
2353 pj_list_insert_before(&media_type.param, media_type_param);
2354
2355 pj_cstr(&pj_boundary, ast_generate_random_string(boundary, sizeof(boundary)));
2356 return pjsip_multipart_create(pool, &media_type, &pj_boundary);
2357}
2358
2359/*!
2360 * \brief Create a resource list body for NOTIFY requests
2361 *
2362 * Resource list bodies are multipart/related bodies. The first part of the multipart body
2363 * is an RLMI body that describes the rest of the parts to come. The other parts of the body
2364 * convey state of individual subscribed resources.
2365 *
2366 * \param pool PJLIB allocation pool
2367 * \param sub Subscription details from which to generate body
2368 * \param force_full_state If true, ignore resource list settings and send a full state notification
2369 * \return The generated multipart/related body
2370 */
2371static pjsip_msg_body *generate_list_body(pj_pool_t *pool, struct ast_sip_subscription *sub,
2372 unsigned int force_full_state)
2373{
2374 int i;
2375 pjsip_multipart_part *rlmi_part;
2376 pjsip_msg_body *multipart;
2377 struct body_part_list body_parts;
2378 unsigned int use_full_state = force_full_state ? 1 : sub->full_state;
2379
2380 if (AST_VECTOR_INIT(&body_parts, AST_VECTOR_SIZE(&sub->children))) {
2381 return NULL;
2382 }
2383
2384 for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
2385 build_body_part(pool, AST_VECTOR_GET(&sub->children, i), &body_parts, use_full_state);
2386 }
2387
2388 /* This can happen if issuing partial state and no children of the list have changed state */
2389 if (AST_VECTOR_SIZE(&body_parts) == 0) {
2390 free_body_parts(&body_parts);
2391 return NULL;
2392 }
2393
2394 multipart = create_multipart_body(pool);
2395
2396 rlmi_part = build_rlmi_body(pool, sub, &body_parts, use_full_state);
2397 if (!rlmi_part) {
2398 free_body_parts(&body_parts);
2399 return NULL;
2400 }
2401 pjsip_multipart_add_part(pool, multipart, rlmi_part);
2402
2403 for (i = 0; i < AST_VECTOR_SIZE(&body_parts); ++i) {
2404 pjsip_multipart_add_part(pool, multipart, AST_VECTOR_GET(&body_parts, i)->part);
2405 }
2406
2407 free_body_parts(&body_parts);
2408 return multipart;
2409}
2410
2411/*!
2412 * \brief Create the body for a NOTIFY request.
2413 *
2414 * \param pool The pool used for allocations
2415 * \param root The root of the subscription tree
2416 * \param force_full_state If true, ignore resource list settings and send a full state notification
2417 */
2418static pjsip_msg_body *generate_notify_body(pj_pool_t *pool, struct ast_sip_subscription *root,
2419 unsigned int force_full_state)
2420{
2421 pjsip_msg_body *body;
2422
2423 if (AST_VECTOR_SIZE(&root->children) == 0) {
2424 if (force_full_state || root->body_changed) {
2425 /* Not a list. We've already generated the body and saved it on the subscription.
2426 * Use that directly.
2427 */
2428 pj_str_t type;
2429 pj_str_t subtype;
2430 pj_str_t text;
2431
2433 pj_cstr(&subtype, ast_sip_subscription_get_body_subtype(root));
2434 pj_cstr(&text, ast_str_buffer(root->body_text));
2435
2436 body = pjsip_msg_body_create(pool, &type, &subtype, &text);
2437 root->body_changed = 0;
2438 } else {
2439 body = NULL;
2440 }
2441 } else {
2442 body = generate_list_body(pool, root, force_full_state);
2443 }
2444
2445 return body;
2446}
2447
2448/*!
2449 * \brief Shortcut method to create a Require: eventlist header
2450 */
2451static pjsip_require_hdr *create_require_eventlist(pj_pool_t *pool)
2452{
2453 pjsip_require_hdr *require;
2454
2455 require = pjsip_require_hdr_create(pool);
2456 pj_strdup2(pool, &require->values[0], "eventlist");
2457 require->count = 1;
2458
2459 return require;
2460}
2461
2463{
2464 int i;
2465
2466 sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
2467 for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
2468 set_state_terminated(AST_VECTOR_GET(&sub->children, i));
2469 }
2470}
2471
2472/*!
2473 * \brief Send a NOTIFY request to a subscriber
2474 *
2475 * \pre sub_tree->dlg is locked
2476 *
2477 * \param sub_tree The subscription tree representing the subscription
2478 * \param force_full_state If true, ignore resource list settings and send full resource list state.
2479 * \retval 0 Success
2480 * \retval non-zero Failure
2481 */
2482static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state)
2483{
2484 pjsip_evsub *evsub = sub_tree->evsub;
2485 pjsip_tx_data *tdata;
2486
2487 if (ast_shutdown_final()
2488 && sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED
2489 && sub_tree->persistence) {
2490 return 0;
2491 }
2492
2493 if (pjsip_evsub_notify(evsub, sub_tree->root->subscription_state,
2494 NULL, NULL, &tdata) != PJ_SUCCESS) {
2495 return -1;
2496 }
2497
2498 tdata->msg->body = generate_notify_body(tdata->pool, sub_tree->root, force_full_state);
2499 if (!tdata->msg->body) {
2500 pjsip_tx_data_dec_ref(tdata);
2501 return -1;
2502 }
2503
2504 if (sub_tree->is_list) {
2505 pjsip_require_hdr *require = create_require_eventlist(tdata->pool);
2506 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
2507 }
2508
2509 if (sub_tree->root->handler->notifier->notify_created) {
2510 /* The module for this event wants a callback to the pjsip_tx_data,
2511 * e.g. so it can add custom headers or do something custom to the response. */
2512 sub_tree->root->handler->notifier->notify_created(sub_tree->root, tdata);
2513 }
2514
2515 if (sip_subscription_send_request(sub_tree, tdata)) {
2516 /* do not call pjsip_tx_data_dec_ref(tdata). The pjsip_dlg_send_request deletes the message on error */
2517 return -1;
2518 }
2519
2520 sub_tree->send_scheduled_notify = 0;
2521
2522 return 0;
2523}
2524
2525static int serialized_send_notify(void *userdata)
2526{
2527 struct sip_subscription_tree *sub_tree = userdata;
2528 pjsip_dialog *dlg = sub_tree->dlg;
2529
2530 pjsip_dlg_inc_lock(dlg);
2531
2532 sub_tree->notify_sched_id = -1;
2533
2534 /* It's possible that between when the notification was scheduled
2535 * and now a new SUBSCRIBE arrived requiring full state to be
2536 * sent out in an immediate NOTIFY. It's also possible that we're
2537 * already processing a terminate. If that has happened, we need to
2538 * bail out here instead of sending the batched NOTIFY.
2539 */
2540
2542 || !sub_tree->send_scheduled_notify) {
2543 pjsip_dlg_dec_lock(dlg);
2544 ao2_cleanup(sub_tree);
2545 return 0;
2546 }
2547
2548 if (sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED) {
2550 }
2551
2552 send_notify(sub_tree, 0);
2553
2555 sub_tree->state == SIP_SUB_TREE_TERMINATED
2556 ? "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_STATE_CHANGED",
2557 "Resource: %s", sub_tree->root->resource);
2558
2559 pjsip_dlg_dec_lock(dlg);
2560 ao2_cleanup(sub_tree);
2561 return 0;
2562}
2563
2564static int sched_cb(const void *data)
2565{
2566 struct sip_subscription_tree *sub_tree = (struct sip_subscription_tree *) data;
2567
2568 /* We don't need to bump the refcount of sub_tree since we bumped it when scheduling this task */
2569 if (ast_sip_push_task(sub_tree->serializer, serialized_send_notify, sub_tree)) {
2570 ao2_cleanup(sub_tree);
2571 }
2572
2573 return 0;
2574}
2575
2577{
2578 /* There's already a notification scheduled */
2579 if (sub_tree->notify_sched_id > -1) {
2580 return 0;
2581 }
2582
2583 sub_tree->send_scheduled_notify = 1;
2585 if (sub_tree->notify_sched_id < 0) {
2586 ao2_cleanup(sub_tree);
2587 return -1;
2588 }
2589
2590 return 0;
2591}
2592
2594 int terminate)
2595{
2596 int res;
2597 pjsip_dialog *dlg = sub->tree->dlg;
2598
2599 pjsip_dlg_inc_lock(dlg);
2600
2601 if (sub->tree->state != SIP_SUB_TREE_NORMAL) {
2602 pjsip_dlg_dec_lock(dlg);
2603 return 0;
2604 }
2605
2608 pjsip_dlg_dec_lock(dlg);
2609 return -1;
2610 }
2611
2612 sub->body_changed = 1;
2613 if (terminate) {
2614 sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
2615 sub->tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
2616 }
2617
2618 if (sub->tree->notification_batch_interval) {
2619 res = schedule_notification(sub->tree);
2620 } else {
2621 /* See the note in pubsub_on_rx_refresh() for why sub->tree is refbumped here */
2622 ao2_ref(sub->tree, +1);
2623 if (terminate) {
2625 }
2626 res = send_notify(sub->tree, 0);
2627 ast_test_suite_event_notify(terminate ? "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_STATE_CHANGED",
2628 "Resource: %s",
2629 sub->tree->root->resource);
2630 ao2_ref(sub->tree, -1);
2631 }
2632
2633 pjsip_dlg_dec_lock(dlg);
2634 return res;
2635}
2636
2638{
2639 return sub->uri;
2640}
2641
2643{
2644 pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, sub->uri, buf, size);
2645}
2646
2648{
2649 pjsip_dialog *dlg;
2650 pjsip_sip_uri *uri;
2651
2652 dlg = sub->tree->dlg;
2653 uri = pjsip_uri_get_uri(dlg->remote.info->uri);
2654
2655 if (pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, uri, buf, size) < 0) {
2656 *buf = '\0';
2657 }
2658}
2659
2661{
2662 return sub->resource;
2663}
2664
2666{
2667 return sub->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ? 1 : 0;
2668}
2669
2670static int sip_subscription_accept(struct sip_subscription_tree *sub_tree, pjsip_rx_data *rdata, int response)
2671{
2672 pjsip_hdr res_hdr;
2673
2674 /* If this is a persistence recreation the subscription has already been accepted */
2675 if (ast_sip_mod_data_get(rdata->endpt_info.mod_data, pubsub_module.id, MOD_DATA_PERSISTENCE)) {
2676 return 0;
2677 }
2678
2679 pj_list_init(&res_hdr);
2680 if (sub_tree->is_list) {
2681 /* If subscribing to a list, our response has to have a Require: eventlist header in it */
2682 pj_list_insert_before(&res_hdr, create_require_eventlist(rdata->tp_info.pool));
2683 }
2684
2685 return pjsip_evsub_accept(sub_tree->evsub, rdata, response, &res_hdr) == PJ_SUCCESS ? 0 : -1;
2686}
2687
2689{
2691}
2692
2694{
2695 return ast_datastores_add(subscription->datastores, datastore);
2696}
2697
2699{
2700 return ast_datastores_find(subscription->datastores, name);
2701}
2702
2704{
2705 ast_datastores_remove(subscription->datastores, name);
2706}
2707
2709{
2710 return subscription->datastores;
2711}
2712
2714{
2715 return ast_datastores_add(publication->datastores, datastore);
2716}
2717
2719{
2720 return ast_datastores_find(publication->datastores, name);
2721}
2722
2724{
2725 ast_datastores_remove(publication->datastores, name);
2726}
2727
2729{
2730 return publication->datastores;
2731}
2732
2733void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data)
2734{
2735 ast_json_unref(subscription->persistence_data);
2736 subscription->persistence_data = persistence_data;
2737
2738 if (subscription->tree->persistence) {
2739 if (!subscription->tree->persistence->generator_data) {
2741 if (!subscription->tree->persistence->generator_data) {
2742 return;
2743 }
2744 }
2745 ast_json_object_set(subscription->tree->persistence->generator_data, subscription->resource,
2746 ast_json_ref(persistence_data));
2747 }
2748}
2749
2751{
2752 return subscription->persistence_data;
2753}
2754
2756
2757static int publication_hash_fn(const void *obj, const int flags)
2758{
2759 const struct ast_sip_publication *publication = obj;
2760 const int *entity_tag = obj;
2761
2762 return flags & OBJ_KEY ? *entity_tag : publication->entity_tag;
2763}
2764
2765static int publication_cmp_fn(void *obj, void *arg, int flags)
2766{
2767 const struct ast_sip_publication *publication1 = obj;
2768 const struct ast_sip_publication *publication2 = arg;
2769 const int *entity_tag = arg;
2770
2771 return (publication1->entity_tag == (flags & OBJ_KEY ? *entity_tag : publication2->entity_tag) ?
2772 CMP_MATCH | CMP_STOP : 0);
2773}
2774
2776{
2780}
2781
2783{
2784 if (ast_strlen_zero(handler->event_name)) {
2785 ast_log(LOG_ERROR, "No event package specified for publish handler. Cannot register\n");
2786 return -1;
2787 }
2788
2791 if (!handler->publications) {
2792 ast_log(LOG_ERROR, "Could not allocate publications container for event '%s'\n",
2793 handler->event_name);
2794 return -1;
2795 }
2796
2798
2799 return 0;
2800}
2801
2803{
2804 struct ast_sip_publish_handler *iter;
2805
2808 if (handler == iter) {
2810 ao2_cleanup(handler->publications);
2811 break;
2812 }
2813 }
2816}
2817
2819
2821{
2825}
2826
2828{
2829 struct ast_sip_subscription_handler *iter;
2830
2833 if (!strcmp(iter->event_name, event_name)) {
2834 break;
2835 }
2836 }
2838 return iter;
2839}
2840
2842{
2843 pj_str_t event;
2844 pj_str_t accept[AST_SIP_MAX_ACCEPT] = { {0, }, };
2845 struct ast_sip_subscription_handler *existing;
2846 int i = 0;
2847
2848 if (ast_strlen_zero(handler->event_name)) {
2849 ast_log(LOG_ERROR, "No event package specified for subscription handler. Cannot register\n");
2850 return -1;
2851 }
2852
2853 existing = find_sub_handler_for_event_name(handler->event_name);
2854 if (existing) {
2856 "Unable to register subscription handler for event %s. A handler is already registered\n",
2857 handler->event_name);
2858 return -1;
2859 }
2860
2861 for (i = 0; i < AST_SIP_MAX_ACCEPT && !ast_strlen_zero(handler->accept[i]); ++i) {
2862 pj_cstr(&accept[i], handler->accept[i]);
2863 }
2864
2865 pj_cstr(&event, handler->event_name);
2866
2867 pjsip_evsub_register_pkg(&pubsub_module, &event, DEFAULT_EXPIRES, i, accept);
2868
2870
2871 return 0;
2872}
2873
2875{
2876 struct ast_sip_subscription_handler *iter;
2877
2880 if (handler == iter) {
2882 break;
2883 }
2884 }
2887}
2888
2890{
2892
2894 if (!strcmp(gen->type, type)
2895 && !strcmp(gen->subtype, subtype)) {
2896 break;
2897 }
2898 }
2899
2900 return gen;
2901}
2902
2904{
2906
2910 return gen;
2911}
2912
2914{
2915 char *accept_copy = ast_strdupa(accept);
2916 char *subtype = accept_copy;
2917 char *type = strsep(&subtype, "/");
2918
2920 return NULL;
2921 }
2922
2924}
2925
2927 size_t num_accept, const char *body_type)
2928{
2929 int i;
2930 struct ast_sip_pubsub_body_generator *generator = NULL;
2931
2932 for (i = 0; i < num_accept; ++i) {
2933 generator = find_body_generator_accept(accept[i]);
2934 if (generator) {
2935 ast_debug(3, "Body generator %p found for accept type %s\n", generator, accept[i]);
2936 if (strcmp(generator->body_type, body_type)) {
2937 ast_log(LOG_WARNING, "Body generator '%s/%s'(%p) does not accept the type of data this event generates\n",
2938 generator->type, generator->subtype, generator);
2939 generator = NULL;
2940 continue;
2941 }
2942 break;
2943 } else {
2944 ast_debug(3, "No body generator found for accept type %s\n", accept[i]);
2945 }
2946 }
2947
2948 return generator;
2949}
2950
2952{
2953 void *notify_data;
2954 int res;
2955 struct ast_sip_body_data data = {
2956 .body_type = sub->handler->body_type,
2957 };
2958
2959 if (AST_VECTOR_SIZE(&sub->children) > 0) {
2960 int i;
2961
2962 for (i = 0; i < AST_VECTOR_SIZE(&sub->children); ++i) {
2963 if (generate_initial_notify(AST_VECTOR_GET(&sub->children, i))) {
2964 return -1;
2965 }
2966 }
2967
2968 return 0;
2969 }
2970
2971 /* We notify subscription establishment only on the tree leaves. */
2972 if (sub->handler->notifier->subscription_established(sub)) {
2973 return -1;
2974 }
2975
2976 notify_data = sub->handler->notifier->get_notify_data(sub);
2977 if (!notify_data) {
2978 ast_debug(3, "No notify data, not generating any body content\n");
2979 return -1;
2980 }
2981
2982 data.body_data = notify_data;
2983
2985 ast_sip_subscription_get_body_subtype(sub), &data, &sub->body_text);
2986
2988
2989 return res;
2990}
2991
2992static int pubsub_on_refresh_timeout(void *userdata);
2993
2994static int initial_notify_task(void * obj)
2995{
2996 struct initial_notify_data *ind = obj;
2997
2999 pjsip_evsub_terminate(ind->sub_tree->evsub, PJ_TRUE);
3000 } else {
3001 send_notify(ind->sub_tree, 1);
3002 ast_test_suite_event_notify("SUBSCRIPTION_ESTABLISHED",
3003 "Resource: %s",
3004 ind->sub_tree->root->resource);
3005 }
3006
3008 char *name = ast_alloca(strlen("->/ ") +
3009 strlen(ind->sub_tree->persistence->endpoint) +
3010 strlen(ind->sub_tree->root->resource) +
3011 strlen(ind->sub_tree->root->handler->event_name) +
3012 ind->sub_tree->dlg->call_id->id.slen + 1);
3013
3014 sprintf(name, "%s->%s/%s %.*s", ind->sub_tree->persistence->endpoint,
3016 (int)ind->sub_tree->dlg->call_id->id.slen, ind->sub_tree->dlg->call_id->id.ptr);
3017
3018 ast_debug(3, "Scheduling timer: %s\n", name);
3022 if (!ind->sub_tree->expiration_task) {
3023 ast_log(LOG_ERROR, "Unable to create expiration timer of %d seconds for %s\n",
3024 ind->expires, name);
3025 }
3026 }
3027
3028 ao2_ref(ind->sub_tree, -1);
3029 ast_free(ind);
3030
3031 return 0;
3032}
3033
3034static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
3035{
3036 pjsip_expires_hdr *expires_header;
3038 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
3039 struct sip_subscription_tree *sub_tree;
3040 struct ast_sip_pubsub_body_generator *generator;
3041 char *resource;
3042 pjsip_uri *request_uri;
3043 size_t resource_size;
3044 int resp;
3045 struct resource_tree tree;
3046 pj_status_t dlg_status;
3047 const pj_str_t *user;
3048
3049 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
3050 ast_assert(endpoint != NULL);
3051
3052 if (!endpoint->subscription.allow) {
3053 ast_log(LOG_WARNING, "Subscriptions not permitted for endpoint %s.\n", ast_sorcery_object_get_id(endpoint));
3054 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 603, NULL, NULL, NULL);
3055 return PJ_TRUE;
3056 }
3057
3058 request_uri = rdata->msg_info.msg->line.req.uri;
3059
3060 if (!ast_sip_is_uri_sip_sips(request_uri)) {
3061 char uri_str[PJSIP_MAX_URL_SIZE];
3062
3063 pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str, sizeof(uri_str));
3064 ast_log(LOG_WARNING, "Request URI '%s' is not a sip: or sips: URI.\n", uri_str);
3065 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
3066 return PJ_TRUE;
3067 }
3068
3069 user = ast_sip_pjsip_uri_get_username(request_uri);
3070 resource_size = pj_strlen(user) + 1;
3071 resource = ast_alloca(resource_size);
3072 ast_copy_pj_str(resource, user, resource_size);
3073
3074 /*
3075 * We may want to match without any user options getting
3076 * in the way.
3077 */
3079
3080 expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, rdata->msg_info.msg->hdr.next);
3081 if (expires_header) {
3082 if (expires_header->ivalue == 0) {
3083 ast_debug(1, "Subscription request from endpoint %s rejected. Expiration of 0 is invalid\n",
3084 ast_sorcery_object_get_id(endpoint));
3085 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
3086 return PJ_TRUE;
3087 }
3088 if (expires_header->ivalue < endpoint->subscription.minexpiry) {
3089 ast_log(LOG_WARNING, "Subscription expiration %d is too brief for endpoint %s. Minimum is %u\n",
3090 expires_header->ivalue, ast_sorcery_object_get_id(endpoint), endpoint->subscription.minexpiry);
3091 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 423, NULL, NULL, NULL);
3092 return PJ_TRUE;
3093 }
3094 }
3095
3097 if (!handler) {
3098 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3099 return PJ_TRUE;
3100 }
3101
3103 if (!generator) {
3104 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3105 return PJ_TRUE;
3106 }
3107
3108 memset(&tree, 0, sizeof(tree));
3109 resp = build_resource_tree(endpoint, handler, resource, &tree,
3111 if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
3112 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
3113 resource_tree_destroy(&tree);
3114 return PJ_TRUE;
3115 }
3116
3117 sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator, &tree, &dlg_status, NULL);
3118 if (!sub_tree) {
3119 if (dlg_status != PJ_EEXISTS) {
3120 ast_debug(3, "No dialog exists, rejecting\n");
3121 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
3122 }
3123 } else {
3124 struct initial_notify_data *ind = ast_malloc(sizeof(*ind));
3125
3126 if (!ind) {
3127 pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
3128 resource_tree_destroy(&tree);
3129 return PJ_TRUE;
3130 }
3131
3132 ind->sub_tree = ao2_bump(sub_tree);
3133 /* Since this is a normal subscribe, pjproject takes care of the timer */
3135
3138 sip_subscription_accept(sub_tree, rdata, resp);
3140 pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
3141 ao2_ref(sub_tree, -1);
3142 ast_free(ind);
3143 }
3144 }
3145
3146 resource_tree_destroy(&tree);
3147 return PJ_TRUE;
3148}
3149
3151{
3152 struct ast_sip_publish_handler *iter = NULL;
3153
3156 if (strcmp(event, iter->event_name)) {
3157 ast_debug(3, "Event %s does not match %s\n", event, iter->event_name);
3158 continue;
3159 }
3160 ast_debug(3, "Event name match: %s = %s\n", event, iter->event_name);
3161 break;
3162 }
3164
3165 return iter;
3166}
3167
3168static enum sip_publish_type determine_sip_publish_type(pjsip_rx_data *rdata,
3169 pjsip_generic_string_hdr *etag_hdr, unsigned int *expires, int *entity_id)
3170{
3171 pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
3172
3173 if (etag_hdr) {
3174 char etag[pj_strlen(&etag_hdr->hvalue) + 1];
3175
3176 ast_copy_pj_str(etag, &etag_hdr->hvalue, sizeof(etag));
3177
3178 if (sscanf(etag, "%30d", entity_id) != 1) {
3179 return SIP_PUBLISH_UNKNOWN;
3180 }
3181 }
3182
3183 *expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
3184
3185 if (!(*expires)) {
3186 return SIP_PUBLISH_REMOVE;
3187 } else if (!etag_hdr && rdata->msg_info.msg->body) {
3188 return SIP_PUBLISH_INITIAL;
3189 } else if (etag_hdr && !rdata->msg_info.msg->body) {
3190 return SIP_PUBLISH_REFRESH;
3191 } else if (etag_hdr && rdata->msg_info.msg->body) {
3192 return SIP_PUBLISH_MODIFY;
3193 }
3194
3195 return SIP_PUBLISH_UNKNOWN;
3196}
3197
3198/*! \brief Internal destructor for publications */
3199static void publication_destroy_fn(void *obj)
3200{
3201 struct ast_sip_publication *publication = obj;
3202
3203 ast_debug(3, "Destroying SIP publication\n");
3204
3205 ao2_cleanup(publication->datastores);
3206 ao2_cleanup(publication->endpoint);
3207
3209}
3210
3211static struct ast_sip_publication *sip_create_publication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata,
3212 const char *resource, const char *event_configuration_name)
3213{
3214 struct ast_sip_publication *publication;
3215 pjsip_expires_hdr *expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
3216 size_t resource_len = strlen(resource) + 1, event_configuration_name_len = strlen(event_configuration_name) + 1;
3217 char *dst;
3218
3220
3221 if (!(publication = ao2_alloc(sizeof(*publication) + resource_len + event_configuration_name_len, publication_destroy_fn))) {
3222 return NULL;
3223 }
3224
3226
3227 if (!(publication->datastores = ast_datastores_alloc())) {
3228 ao2_ref(publication, -1);
3229 return NULL;
3230 }
3231
3233 ao2_ref(endpoint, +1);
3234 publication->endpoint = endpoint;
3235 publication->expires = expires_hdr ? expires_hdr->ivalue : DEFAULT_PUBLISH_EXPIRES;
3236 publication->sched_id = -1;
3237 dst = publication->data;
3238 publication->resource = strcpy(dst, resource);
3239 dst += resource_len;
3240 publication->event_configuration_name = strcpy(dst, event_configuration_name);
3241
3242 return publication;
3243}
3244
3245static int sip_publication_respond(struct ast_sip_publication *pub, int status_code,
3246 pjsip_rx_data *rdata)
3247{
3248 pjsip_tx_data *tdata;
3249 pjsip_transaction *tsx;
3250
3251 if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, status_code, NULL, &tdata) != PJ_SUCCESS) {
3252 return -1;
3253 }
3254
3255 if (PJSIP_IS_STATUS_IN_CLASS(status_code, 200)) {
3256 char buf[30];
3257
3258 snprintf(buf, sizeof(buf), "%d", pub->entity_tag);
3259 ast_sip_add_header(tdata, "SIP-ETag", buf);
3260
3261 snprintf(buf, sizeof(buf), "%d", pub->expires);
3262 ast_sip_add_header(tdata, "Expires", buf);
3263 }
3264
3265 if (pjsip_tsx_create_uas(&pubsub_module, rdata, &tsx) != PJ_SUCCESS) {
3266 pjsip_tx_data_dec_ref(tdata);
3267 return -1;
3268 }
3269
3270 pjsip_tsx_recv_msg(tsx, rdata);
3271
3272 if (pjsip_tsx_send_msg(tsx, tdata) != PJ_SUCCESS) {
3273 pjsip_tx_data_dec_ref(tdata);
3274 return -1;
3275 }
3276
3277 return 0;
3278}
3279
3280static struct ast_sip_publication *publish_request_initial(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata,
3282{
3283 struct ast_sip_publication *publication;
3284 char *resource_name;
3285 size_t resource_size;
3287 struct ast_variable *event_configuration_name = NULL;
3288 pjsip_uri *request_uri;
3289 int resp;
3290 const pj_str_t *user;
3291
3292 request_uri = rdata->msg_info.msg->line.req.uri;
3293
3294 if (!ast_sip_is_uri_sip_sips(request_uri)) {
3295 char uri_str[PJSIP_MAX_URL_SIZE];
3296
3297 pjsip_uri_print(PJSIP_URI_IN_REQ_URI, request_uri, uri_str, sizeof(uri_str));
3298 ast_log(LOG_WARNING, "Request URI '%s' is not a sip: or sips: URI.\n", uri_str);
3299 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
3300 return NULL;
3301 }
3302
3303 user = ast_sip_pjsip_uri_get_username(request_uri);
3304 resource_size = pj_strlen(user) + 1;
3305 resource_name = ast_alloca(resource_size);
3306 ast_copy_pj_str(resource_name, user, resource_size);
3307
3308 /*
3309 * We may want to match without any user options getting
3310 * in the way.
3311 */
3313
3314 resource = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "inbound-publication", resource_name);
3315 if (!resource) {
3316 ast_debug(1, "No 'inbound-publication' defined for resource '%s'\n", resource_name);
3317 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
3318 return NULL;
3319 }
3320
3321 if (!ast_strlen_zero(resource->endpoint) && strcmp(resource->endpoint, ast_sorcery_object_get_id(endpoint))) {
3322 ast_debug(1, "Resource %s has a defined endpoint '%s', but does not match endpoint '%s' that received the request\n",
3323 resource_name, resource->endpoint, ast_sorcery_object_get_id(endpoint));
3324 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
3325 return NULL;
3326 }
3327
3328 for (event_configuration_name = resource->events; event_configuration_name; event_configuration_name = event_configuration_name->next) {
3329 if (!strcmp(event_configuration_name->name, handler->event_name)) {
3330 break;
3331 }
3332 }
3333
3334 if (!event_configuration_name) {
3335 ast_debug(1, "Event '%s' is not configured for '%s'\n", handler->event_name, resource_name);
3336 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
3337 return NULL;
3338 }
3339
3340 resp = handler->new_publication(endpoint, resource_name, event_configuration_name->value);
3341
3342 if (!PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
3343 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, resp, NULL, NULL, NULL);
3344 return NULL;
3345 }
3346
3347 publication = sip_create_publication(endpoint, rdata, S_OR(resource_name, ""), event_configuration_name->value);
3348
3349 if (!publication) {
3350 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 503, NULL, NULL, NULL);
3351 return NULL;
3352 }
3353
3354 publication->handler = handler;
3355 if (publication->handler->publication_state_change(publication, rdata->msg_info.msg->body,
3357 ast_debug(3, "Publication state change failed\n");
3358 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
3359 ao2_cleanup(publication);
3360 return NULL;
3361 }
3362
3363 sip_publication_respond(publication, resp, rdata);
3364
3365 return publication;
3366}
3367
3368static int publish_expire_callback(void *data)
3369{
3370 RAII_VAR(struct ast_sip_publication *, publication, data, ao2_cleanup);
3371
3372 if (publication->handler->publish_expire) {
3373 publication->handler->publish_expire(publication);
3374 }
3375
3376 return 0;
3377}
3378
3379static int publish_expire(const void *data)
3380{
3381 struct ast_sip_publication *publication = (struct ast_sip_publication*)data;
3382
3383 ao2_unlink(publication->handler->publications, publication);
3384 publication->sched_id = -1;
3385
3386 if (ast_sip_push_task(NULL, publish_expire_callback, publication)) {
3387 ao2_cleanup(publication);
3388 }
3389
3390 return 0;
3391}
3392
3393static pj_bool_t pubsub_on_rx_publish_request(pjsip_rx_data *rdata)
3394{
3395 pjsip_event_hdr *event_header;
3397 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
3398 char event[32];
3399 static const pj_str_t str_sip_if_match = { "SIP-If-Match", 12 };
3400 pjsip_generic_string_hdr *etag_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_sip_if_match, NULL);
3401 enum sip_publish_type publish_type;
3402 RAII_VAR(struct ast_sip_publication *, publication, NULL, ao2_cleanup);
3403 unsigned int expires = 0;
3404 int entity_id, response = 0;
3405
3406 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
3407 ast_assert(endpoint != NULL);
3408
3409 event_header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_event_name, rdata->msg_info.msg->hdr.next);
3410 if (!event_header) {
3411 ast_log(LOG_WARNING, "Incoming PUBLISH request from %s with no Event header\n",
3412 ast_sorcery_object_get_id(endpoint));
3413 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3414 return PJ_TRUE;
3415 }
3416 ast_copy_pj_str(event, &event_header->event_type, sizeof(event));
3417
3419 if (!handler) {
3420 ast_log(LOG_WARNING, "No registered publish handler for event %s from %s\n", event,
3421 ast_sorcery_object_get_id(endpoint));
3422 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL);
3423 return PJ_TRUE;
3424 }
3425
3426 publish_type = determine_sip_publish_type(rdata, etag_hdr, &expires, &entity_id);
3427
3428 /* If this is not an initial publish ensure that a publication is present */
3429 if ((publish_type != SIP_PUBLISH_INITIAL) && (publish_type != SIP_PUBLISH_UNKNOWN)) {
3430 if (!(publication = ao2_find(handler->publications, &entity_id, OBJ_KEY | OBJ_UNLINK))) {
3431 static const pj_str_t str_conditional_request_failed = { "Conditional Request Failed", 26 };
3432
3433 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 412, &str_conditional_request_failed,
3434 NULL, NULL);
3435 return PJ_TRUE;
3436 }
3437
3438 /* Per the RFC every response has to have a new entity tag */
3439 publication->entity_tag = ast_atomic_fetchadd_int(&esc_etag_counter, +1);
3440
3441 /* Update the expires here so that the created responses will contain the correct value */
3442 publication->expires = expires;
3443 }
3444
3445 switch (publish_type) {
3447 publication = publish_request_initial(endpoint, rdata, handler);
3448 break;
3450 case SIP_PUBLISH_MODIFY:
3451 if (handler->publication_state_change(publication, rdata->msg_info.msg->body,
3453 /* If an error occurs we want to terminate the publication */
3454 expires = 0;
3455 }
3456 response = 200;
3457 break;
3458 case SIP_PUBLISH_REMOVE:
3459 handler->publication_state_change(publication, rdata->msg_info.msg->body,
3461 response = 200;
3462 break;
3464 default:
3465 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
3466 break;
3467 }
3468
3469 if (publication) {
3470 if (expires) {
3471 ao2_link(handler->publications, publication);
3472
3473 AST_SCHED_REPLACE_UNREF(publication->sched_id, sched, expires * 1000, publish_expire, publication,
3474 ao2_ref(_data, -1), ao2_ref(publication, -1), ao2_ref(publication, +1));
3475 } else {
3476 AST_SCHED_DEL_UNREF(sched, publication->sched_id, ao2_ref(publication, -1));
3477 }
3478 }
3479
3480 if (response) {
3481 sip_publication_respond(publication, response, rdata);
3482 }
3483
3484 return PJ_TRUE;
3485}
3486
3488{
3489 return pub->endpoint;
3490}
3491
3493{
3494 return pub->resource;
3495}
3496
3498{
3499 return pub->event_configuration_name;
3500}
3501
3502int ast_sip_pubsub_is_body_generator_registered(const char *type, const char *subtype)
3503{
3504 return !!find_body_generator_type_subtype(type, subtype);
3505}
3506
3508{
3509 struct ast_sip_pubsub_body_generator *existing;
3510 pj_str_t accept;
3511 pj_size_t accept_len;
3512
3514 existing = find_body_generator_type_subtype_nolock(generator->type, generator->subtype);
3515 if (existing) {
3517 ast_log(LOG_WARNING, "A body generator for %s/%s is already registered.\n",
3518 generator->type, generator->subtype);
3519 return -1;
3520 }
3523
3524 /* Lengths of type and subtype plus a slash. */
3525 accept_len = strlen(generator->type) + strlen(generator->subtype) + 1;
3526
3527 /* Add room for null terminator that sprintf() will set. */
3528 pj_strset(&accept, ast_alloca(accept_len + 1), accept_len);
3529 sprintf((char *) pj_strbuf(&accept), "%s/%s", generator->type, generator->subtype);/* Safe */
3530
3531 pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), &pubsub_module,
3532 PJSIP_H_ACCEPT, NULL, 1, &accept);
3533
3534 return 0;
3535}
3536
3538{
3539 struct ast_sip_pubsub_body_generator *iter;
3540
3543 if (iter == generator) {
3545 break;
3546 }
3547 }
3550}
3551
3553{
3557
3558 return 0;
3559}
3560
3562{
3563 struct ast_sip_pubsub_body_supplement *iter;
3564
3567 if (iter == supplement) {
3569 break;
3570 }
3571 }
3574}
3575
3577{
3578 return sub->body_generator->type;
3579}
3580
3582{
3583 return sub->body_generator->subtype;
3584}
3585
3587 struct ast_sip_body_data *data, struct ast_str **str)
3588{
3589 struct ast_sip_pubsub_body_supplement *supplement;
3590 struct ast_sip_pubsub_body_generator *generator;
3591 int res = 0;
3592 void *body;
3593
3595 if (!generator) {
3596 ast_log(LOG_WARNING, "Unable to find a body generator for %s/%s\n",
3597 type, subtype);
3598 return -1;
3599 }
3600
3601 if (strcmp(data->body_type, generator->body_type)) {
3602 ast_log(LOG_WARNING, "%s/%s body generator does not accept the type of data provided\n",
3603 type, subtype);
3604 return -1;
3605 }
3606
3607 body = generator->allocate_body(data->body_data);
3608 if (!body) {
3609 ast_log(LOG_WARNING, "%s/%s body generator could not to allocate a body\n",
3610 type, subtype);
3611 return -1;
3612 }
3613
3614 if (generator->generate_body_content(body, data->body_data)) {
3615 res = -1;
3616 goto end;
3617 }
3618
3621 if (!strcmp(generator->type, supplement->type) &&
3622 !strcmp(generator->subtype, supplement->subtype)) {
3623 res = supplement->supplement_body(body, data->body_data);
3624 if (res) {
3625 break;
3626 }
3627 }
3628 }
3630
3631 if (!res) {
3632 generator->to_string(body, str);
3633 }
3634
3635end:
3636 if (generator->destroy_body) {
3637 generator->destroy_body(body);
3638 }
3639
3640 return res;
3641}
3642
3649 char message_account[PJSIP_MAX_URL_SIZE];
3650};
3651
3652static int parse_simple_message_summary(char *body,
3653 struct simple_message_summary *summary)
3654{
3655 char *line;
3656 char *buffer;
3657 int found_counts = 0;
3658
3659 if (ast_strlen_zero(body) || !summary) {
3660 return -1;
3661 }
3662
3663 buffer = ast_strdupa(body);
3664 memset(summary, 0, sizeof(*summary));
3665
3666 while ((line = ast_read_line_from_buffer(&buffer))) {
3667 line = ast_str_to_lower(line);
3668
3669 if (sscanf(line, "voice-message: %d/%d (%d/%d)",
3670 &summary->voice_messages_new, &summary->voice_messages_old,
3672 found_counts = 1;
3673 } else {
3674 sscanf(line, "message-account: %s", summary->message_account);
3675 }
3676 }
3677
3678 return !found_counts;
3679}
3680
3681static pj_bool_t pubsub_on_rx_mwi_notify_request(pjsip_rx_data *rdata)
3682{
3683 RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
3684 struct simple_message_summary summary;
3685 const char *endpoint_name;
3686 char *atsign;
3687 char *context;
3688 char *body;
3689 char *mailbox;
3690 int rc;
3691
3692 endpoint = ast_pjsip_rdata_get_endpoint(rdata);
3693 if (!endpoint) {
3694 ast_debug(1, "Incoming MWI: Endpoint not found in rdata (%p)\n", rdata);
3695 rc = 404;
3696 goto error;
3697 }
3698
3699 endpoint_name = ast_sorcery_object_get_id(endpoint);
3700 ast_debug(1, "Incoming MWI: Found endpoint: %s\n", endpoint_name);
3701 if (ast_strlen_zero(endpoint->incoming_mwi_mailbox)) {
3702 ast_debug(1, "Incoming MWI: No incoming mailbox specified for endpoint '%s'\n", endpoint_name);
3703 ast_test_suite_event_notify("PUBSUB_NO_INCOMING_MWI_MAILBOX",
3704 "Endpoint: %s", endpoint_name);
3705 rc = 404;
3706 goto error;
3707 }
3708
3709 mailbox = ast_strdupa(endpoint->incoming_mwi_mailbox);
3710 atsign = strchr(mailbox, '@');
3711 if (!atsign) {
3712 ast_debug(1, "Incoming MWI: No '@' found in endpoint %s's incoming mailbox '%s'. Can't parse context\n",
3713 endpoint_name, endpoint->incoming_mwi_mailbox);
3714 rc = 404;
3715 goto error;
3716 }
3717
3718 *atsign = '\0';
3719 context = atsign + 1;
3720
3721 body = ast_alloca(rdata->msg_info.msg->body->len + 1);
3722 rdata->msg_info.msg->body->print_body(rdata->msg_info.msg->body, body,
3723 rdata->msg_info.msg->body->len + 1);
3724
3725 if (parse_simple_message_summary(body, &summary) != 0) {
3726 ast_debug(1, "Incoming MWI: Endpoint: '%s' There was an issue getting message info from body '%s'\n",
3727 ast_sorcery_object_get_id(endpoint), body);
3728 rc = 404;
3729 goto error;
3730 }
3731
3733 summary.voice_messages_new, summary.voice_messages_old)) {
3734 ast_log(LOG_ERROR, "Incoming MWI: Endpoint: '%s' Could not publish MWI to stasis. "
3735 "Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
3736 endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
3737 summary.voice_messages_new, summary.voice_messages_old,
3739 rc = 404;
3740 } else {
3741 ast_debug(1, "Incoming MWI: Endpoint: '%s' Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
3742 endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
3743 summary.voice_messages_new, summary.voice_messages_old,
3745 ast_test_suite_event_notify("PUBSUB_INCOMING_MWI_PUBLISH",
3746 "Endpoint: %s\r\n"
3747 "Mailbox: %s\r\n"
3748 "MessageAccount: %s\r\n"
3749 "VoiceMessagesNew: %d\r\n"
3750 "VoiceMessagesOld: %d\r\n"
3751 "VoiceMessagesUrgentNew: %d\r\n"
3752 "VoiceMessagesUrgentOld: %d",
3753 endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
3754 summary.voice_messages_new, summary.voice_messages_old,
3756 rc = 200;
3757 }
3758
3759error:
3760 pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, rc, NULL, NULL, NULL);
3761 return PJ_TRUE;
3762}
3763
3764static pj_bool_t pubsub_on_rx_notify_request(pjsip_rx_data *rdata)
3765{
3766 if (rdata->msg_info.msg->body &&
3767 ast_sip_is_content_type(&rdata->msg_info.msg->body->content_type,
3768 "application", "simple-message-summary")) {
3769 return pubsub_on_rx_mwi_notify_request(rdata);
3770 }
3771 return PJ_FALSE;
3772}
3773
3774static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
3775{
3776 if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) {
3777 return pubsub_on_rx_subscribe_request(rdata);
3778 } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_publish_method)) {
3779 return pubsub_on_rx_publish_request(rdata);
3780 } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_notify_method)) {
3781 return pubsub_on_rx_notify_request(rdata);
3782 }
3783
3784 return PJ_FALSE;
3785}
3786
3787/*!
3788 * \brief Callback sequence for subscription terminate:
3789 *
3790 * * Please note that the descriptions below represent pjproject behavior on versions
3791 * >= 2.13.
3792 * * Client initiated:
3793 * pjproject receives SUBSCRIBE on the subscription's serializer thread
3794 * calls pubsub_evsub_set_state with state = TERMINATED
3795 * pubsub_on_evsub_state checks the event and finds it is due to a received
3796 * SUBSCRIBE with an expires of 0 and so does nothing.
3797 * calls pubsub_on_rx_refresh with dialog locked
3798 * pubsub_on_rx_refresh sets TERMINATE_PENDING
3799 * calls pubsub_on_refresh_timeout to push final NOTIFY to pjproject
3800 * checks state == TERMINATE_PENDING
3801 * sets TERMINATE_IN_PROGRESS
3802 * calls send_notify (2)
3803 * send_notify ultimately calls pjsip_evsub_send_request
3804 * pjsip_evsub_send_request calls evsub's set_state
3805 * set_state calls pubsub_evsub_set_state
3806 * pubsub_on_evsub_state checks state == TERMINATE_IN_PROGRESS
3807 * removes the subscriptions
3808 * cleans up references to evsub
3809 * sets state = TERMINATED
3810 * pubsub_on_refresh_timeout unlocks dialog
3811 * returns to pjproject
3812 * pjproject unlocks dialog
3813 *
3814 * * Subscription timer expires:
3815 * pjproject timer expires
3816 * locks dialog
3817 * calls pubsub_on_server_timeout
3818 * pubsub_on_server_timeout checks state == NORMAL
3819 * sets TERMINATE_PENDING
3820 * pushes serialized_pubsub_on_refresh_timeout
3821 * returns to pjproject
3822 * pjproject unlocks dialog
3823 * serialized_pubsub_on_refresh_timeout starts (1)
3824 * locks dialog
3825 * checks state == TERMINATE_PENDING
3826 * sets TERMINATE_IN_PROGRESS
3827 * calls send_notify (2)
3828 * send_notify ultimately calls pjsip_evsub_send_request
3829 * pjsip_evsub_send_request calls evsub's set_state
3830 * set_state calls pubsub_evsub_set_state
3831 * pubsub_on_evsub_state checks state == TERMINATE_IN_PROGRESS
3832 * checks that the event is not due to un-SUBSCRIBE
3833 * removes the subscriptions
3834 * cleans up references to evsub
3835 * sets state = TERMINATED
3836 * serialized_pubsub_on_refresh_timeout unlocks dialog
3837 *
3838 * * Transmission failure sending NOTIFY or error response from client
3839 * pjproject transaction timer expires or non OK response
3840 * pjproject locks dialog
3841 * calls pubsub_on_evsub_state with event TSX_STATE
3842 * pubsub_on_evsub_state checks event == TSX_STATE
3843 * removes the subscriptions
3844 * cleans up references to evsub
3845 * sets state = TERMINATED
3846 * pjproject unlocks dialog
3847 *
3848 * * ast_sip_subscription_notify is called
3849 * checks state == NORMAL
3850 * if not batched...
3851 * sets TERMINATE_IN_PROGRESS (if terminate is requested)
3852 * calls send_notify
3853 * See (2) Above
3854 * if batched...
3855 * sets TERMINATE_PENDING
3856 * schedules task
3857 * scheduler runs sched_task
3858 * sched_task pushes serialized_send_notify
3859 * serialized_send_notify starts
3860 * checks state <= TERMINATE_PENDING
3861 * if state == TERMINATE_PENDING set state = TERMINATE_IN_PROGRESS
3862 * call send_notify
3863 * See (2) Above
3864 *
3865 */
3866
3867
3868/* The code in this function was previously in pubsub_on_evsub_state. */
3869static void clean_sub_tree(pjsip_evsub *evsub)
3870{
3871
3872 struct sip_subscription_tree *sub_tree;
3873 sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
3874
3875 ast_debug(3, "Cleaning subscription %p\n", evsub);
3876
3877 if (sub_tree->expiration_task) {
3878 char task_name[256];
3879
3880 ast_sip_sched_task_get_name(sub_tree->expiration_task, task_name, sizeof(task_name));
3881 ast_debug(3, "Cancelling timer: %s\n", task_name);
3883 ao2_cleanup(sub_tree->expiration_task);
3884 sub_tree->expiration_task = NULL;
3885 }
3886
3887 remove_subscription(sub_tree);
3888
3889 pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
3890
3891#ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
3892 pjsip_evsub_dec_ref(sub_tree->evsub);
3893#endif
3894
3895 sub_tree->evsub = NULL;
3896
3899
3901 shutdown_subscriptions(sub_tree->root);
3902
3903 sub_tree->state = SIP_SUB_TREE_TERMINATED;
3904 /* Remove evsub's reference to the sub_tree */
3905 ao2_ref(sub_tree, -1);
3906}
3907
3908/* This functionality appeared in pjsip 2.13 */
3909#if PJ_VERSION_NUM >= 0x020D0000
3910# define HAVE_PJSIP_EVSUB_PENDING_NOTIFY 1
3911#endif
3912
3913/*!
3914 * \brief PJSIP callback when underlying SIP subscription changes state
3915 *
3916 * Although this function is called for every state change, we only care
3917 * about the TERMINATED state, and only when we're actually processing the final
3918 * notify (SIP_SUB_TREE_TERMINATE_IN_PROGRESS) OR when a transmission failure
3919 * occurs (PJSIP_EVENT_TSX_STATE). In this case, we do all the subscription tree
3920 * cleanup tasks and decrement the evsub reference.
3921 */
3922static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
3923{
3924 struct sip_subscription_tree *sub_tree =
3925 pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
3926
3927 ast_debug(3, "evsub %p state %s event %s sub_tree %p sub_tree state %s\n", evsub,
3928 pjsip_evsub_get_state_name(evsub), pjsip_event_str(event->type), sub_tree,
3929 (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN"));
3930
3931 if (!sub_tree || pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
3932 return;
3933 }
3934
3935 /* It's easier to write this as what we WANT to process, then negate it. */
3936 if (!(sub_tree->state == SIP_SUB_TREE_TERMINATE_IN_PROGRESS
3937 || (event->type == PJSIP_EVENT_TSX_STATE && sub_tree->state == SIP_SUB_TREE_NORMAL)
3938 )) {
3939 ast_debug(3, "Do nothing.\n");
3940 return;
3941 }
3942
3943#ifdef HAVE_PJSIP_EVSUB_PENDING_NOTIFY
3944 /* This check looks for re-subscribes with an expires of 0. If we receive one of those,
3945 we don't want to clean the evsub because we still need it to send the final NOTIFY.
3946 This was previously handled by pubsub_on_rx_refresh setting:
3947 'sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING' */
3948 if (event->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
3949 !pjsip_method_cmp(&event->body.tsx_state.tsx->method, &pjsip_subscribe_method) &&
3950 pjsip_evsub_get_expires(evsub) == 0) {
3951 ast_debug(3, "Subscription ending, do nothing.\n");
3952 return;
3953 }
3954#endif
3955 /* If we made it this far, we want to clean the sub tree. For pjproject <2.13, the sub_tree
3956 state check makes sure the evsub is not cleaned at the wrong time */
3958}
3959
3960static int pubsub_on_refresh_timeout(void *userdata)
3961{
3962 struct sip_subscription_tree *sub_tree = userdata;
3963 pjsip_dialog *dlg = sub_tree->dlg;
3964
3965 ast_debug(3, "sub_tree %p sub_tree state %s\n", sub_tree,
3966 (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN"));
3967
3968 pjsip_dlg_inc_lock(dlg);
3969 if (sub_tree->state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS) {
3970 pjsip_dlg_dec_lock(dlg);
3971 return 0;
3972 }
3973
3974 if (sub_tree->state == SIP_SUB_TREE_TERMINATE_PENDING) {
3976 set_state_terminated(sub_tree->root);
3977 }
3978
3979 if (sub_tree->generate_initial_notify) {
3980 sub_tree->generate_initial_notify = 0;
3981 if (generate_initial_notify(sub_tree->root)) {
3982 pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
3983 pjsip_dlg_dec_lock(dlg);
3984 return 0;
3985 }
3986 }
3987
3988 send_notify(sub_tree, 1);
3989
3990 ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
3991 "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
3992 "Resource: %s", sub_tree->root->resource);
3993
3994 pjsip_dlg_dec_lock(dlg);
3995
3996 return 0;
3997}
3998
4000{
4001 struct sip_subscription_tree *sub_tree = userdata;
4002
4003 ast_debug(3, "sub_tree %p sub_tree state %s\n", sub_tree,
4004 (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN"));
4005
4006 pubsub_on_refresh_timeout(userdata);
4007 ao2_cleanup(sub_tree);
4008
4009 return 0;
4010}
4011
4012/*!
4013 * \brief Compare strings for equality checking for NULL.
4014 *
4015 * This function considers NULL values as empty strings.
4016 * This means NULL or empty strings are equal.
4017 *
4018 * \retval 0 The strings are equal
4019 * \retval 1 The strings are not equal
4020 */
4021static int cmp_strings(char *s1, char *s2)
4022{
4023 if (!ast_strlen_zero(s1) && !ast_strlen_zero(s2)) {
4024 return strcmp(s1, s2);
4025 }
4026
4027 return ast_strlen_zero(s1) == ast_strlen_zero(s2) ? 0 : 1;
4028}
4029
4030/*!
4031 * \brief compares the childrens of two ast_sip_subscription s1 and s2
4032 *
4033 * \retval 0 The s1 childrens match the s2 childrens
4034 * \retval 1 The s1 childrens do not match the s2 childrens
4035 */
4037{
4038 int i;
4039
4040 if (AST_VECTOR_SIZE(&s1->children) != AST_VECTOR_SIZE(&s2->children)) {
4041 return 1;
4042 }
4043
4044 for (i = 0; i < AST_VECTOR_SIZE(&s1->children); ++i) {
4045 struct ast_sip_subscription *c1 = AST_VECTOR_GET(&s1->children, i);
4046 struct ast_sip_subscription *c2 = AST_VECTOR_GET(&s2->children, i);
4047
4048 if (cmp_strings(c1->resource, c2->resource)
4049 || cmp_strings(c1->display_name, c2->display_name)) {
4050
4051 return 1;
4052 }
4053 }
4054
4055 return 0;
4056}
4057
4058static int destroy_subscriptions_task(void *obj)
4059{
4060 struct ast_sip_subscription *sub = (struct ast_sip_subscription *) obj;
4061
4063
4064 return 0;
4065}
4066
4067/*!
4068 * \brief Called whenever an in-dialog SUBSCRIBE is received
4069 *
4070 * This includes both SUBSCRIBE requests that actually refresh the subscription
4071 * as well as SUBSCRIBE requests that end the subscription.
4072 *
4073 * In either case we push an appropriate NOTIFY via pubsub_on_refresh_timeout.
4074 */
4075static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
4076 int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
4077{
4078 struct sip_subscription_tree *sub_tree;
4080
4081 sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
4082 ast_debug(3, "evsub %p sub_tree %p sub_tree state %s\n", evsub, sub_tree,
4083 (sub_tree ? sub_tree_state_description[sub_tree->state] : "UNKNOWN"));
4084
4085 if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) {
4086 return;
4087 }
4088
4089 if (sub_tree->expiration_task) {
4090 char task_name[256];
4091
4092 ast_sip_sched_task_get_name(sub_tree->expiration_task, task_name, sizeof(task_name));
4093 ast_debug(3, "Cancelling timer: %s\n", task_name);
4095 ao2_cleanup(sub_tree->expiration_task);
4096 sub_tree->expiration_task = NULL;
4097 }
4098
4099 /* PJSIP will set the evsub's state to terminated before calling into this function
4100 * if the Expires value of the incoming SUBSCRIBE is 0.
4101 */
4102
4103 if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
4105 }
4106
4108
4109 /* If the handler wants a callback on refresh, then do it (some protocols require this). */
4110 if (sub_tree->state == SIP_SUB_TREE_NORMAL && sub_tree->root->handler->notifier->refresh_subscribe) {
4111 if (!sub_tree->root->handler->notifier->refresh_subscribe(sub_tree->root, rdata)) {
4112 return; /* If the callback handled it, we're done. */
4113 }
4114 }
4115
4116 if (sub_tree->state == SIP_SUB_TREE_NORMAL && sub_tree->is_list) {
4117 /* update RLS */
4118 const char *resource = sub_tree->root->resource;
4119 struct ast_sip_subscription *old_root = sub_tree->root;
4120 struct ast_sip_subscription *new_root = NULL;
4121
4122 struct ast_sip_pubsub_body_generator *generator = NULL;
4123
4124 if (endpoint && (generator = subscription_get_generator_from_rdata(rdata, sub_tree->root->handler))) {
4125
4126 struct resource_tree tree;
4127 int resp;
4128
4129 memset(&tree, 0, sizeof(tree));
4130 resp = build_resource_tree(endpoint, sub_tree->root->handler, resource, &tree,
4132 if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
4133 new_root = create_virtual_subscriptions(sub_tree->root->handler, resource, generator, sub_tree, tree.root);
4134 if (new_root) {
4135 if (cmp_subscription_childrens(old_root, new_root)) {
4136 ast_debug(1, "RLS '%s->%s' was modified, regenerate it\n", ast_sorcery_object_get_id(endpoint), old_root->resource);
4137 new_root->version = old_root->version;
4138 sub_tree->root = new_root;
4139 sub_tree->generate_initial_notify = 1;
4140
4141 /* If there is scheduled notification need to delete it to avoid use old subscriptions */
4142 if (sub_tree->notify_sched_id > -1) {
4143 AST_SCHED_DEL_UNREF(sched, sub_tree->notify_sched_id, ao2_ref(sub_tree, -1));
4144 sub_tree->send_scheduled_notify = 0;
4145 }
4146
4147 /* Terminate old subscriptions to stop sending NOTIFY messages on exten/device state changes */
4148 set_state_terminated(old_root);
4149
4150 /* Shutdown old subscriptions to remove exten/device state change callbacks
4151 that can queue tasks for old subscriptions */
4152 shutdown_subscriptions(old_root);
4153
4154 /* Postpone destruction until all already queued tasks that may be using old subscriptions have completed */
4155 if (ast_sip_push_task(sub_tree->serializer, destroy_subscriptions_task, old_root)) {
4156 ast_log(LOG_ERROR, "Failed to push task to destroy old subscriptions for RLS '%s->%s'.\n",
4157 ast_sorcery_object_get_id(endpoint), old_root->resource);
4158 }
4159 } else {
4160 destroy_subscriptions(new_root);
4161 }
4162 }
4163 } else {
4165 pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
4166 }
4167
4168 resource_tree_destroy(&tree);
4169 }
4170 }
4171
4173
4174#ifdef HAVE_PJSIP_EVSUB_PENDING_NOTIFY
4175 /* As of pjsip 2.13, the NOTIFY has to be sent within this function as pjproject now
4176 requires it. Previously this would have caused an early NOTIFY to go out before the
4177 SUBSCRIBE's 200 OK. The previous solution was to push the NOTIFY, but now pjproject
4178 looks for the NOTIFY to be sent from this function and caches it to send after it
4179 auto-replies to the SUBSCRIBE. */
4180 pubsub_on_refresh_timeout(sub_tree);
4181#else
4183 /* If we can't push the NOTIFY refreshing task...we'll just go with it. */
4184 ast_log(LOG_ERROR, "Failed to push task to send NOTIFY.\n");
4185 sub_tree->state = SIP_SUB_TREE_NORMAL;
4186 ao2_ref(sub_tree, -1);
4187 }
4188#endif
4189
4190 if (sub_tree->is_list) {
4191 pj_list_insert_before(res_hdr, create_require_eventlist(rdata->tp_info.pool));
4192 }
4193}
4194
4195static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code,
4196 pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
4197{
4198 struct ast_sip_subscription *sub;
4199
4200 if (!(sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
4201 return;
4202 }
4203
4204 sub->handler->subscriber->state_change(sub, rdata->msg_info.msg->body,
4205 pjsip_evsub_get_state(evsub));
4206}
4207
4208static int serialized_pubsub_on_client_refresh(void *userdata)
4209{
4210 struct sip_subscription_tree *sub_tree = userdata;
4211 pjsip_tx_data *tdata;
4212
4213 if (!sub_tree->evsub) {
4214 ao2_cleanup(sub_tree);
4215 return 0;
4216 }
4217
4218 if (pjsip_evsub_initiate(sub_tree->evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
4219 pjsip_evsub_send_request(sub_tree->evsub, tdata);
4220 } else {
4221 pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
4222 }
4223
4224 ao2_cleanup(sub_tree);
4225 return 0;
4226}
4227
4228static void pubsub_on_client_refresh(pjsip_evsub *evsub)
4229{
4230 struct sip_subscription_tree *sub_tree;
4231
4232 if (!(sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
4233 return;
4234 }
4235
4237 ao2_cleanup(sub_tree);
4238 }
4239}
4240
4241static void pubsub_on_server_timeout(pjsip_evsub *evsub)
4242{
4243 struct sip_subscription_tree *sub_tree;
4244
4245 /* PJSIP does not terminate the server timeout timer when a SUBSCRIBE
4246 * with Expires: 0 arrives to end a subscription, nor does it terminate
4247 * this timer when we send a NOTIFY request in response to receiving such
4248 * a SUBSCRIBE. PJSIP does not stop the server timeout timer until the
4249 * NOTIFY transaction has finished (either through receiving a response
4250 * or through a transaction timeout).
4251 *
4252 * Therefore, it is possible that we can be told that a server timeout
4253 * occurred after we already thought that the subscription had been
4254 * terminated. In such a case, we will have already removed the sub_tree
4255 * from the evsub's mod_data array.
4256 */
4257
4258 sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
4259 if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) {
4260 return;
4261 }
4262
4265 sub_tree->state = SIP_SUB_TREE_NORMAL;
4266 ao2_cleanup(sub_tree);
4267 }
4268}
4269
4271 struct ast_sip_ami *ami,
4272 const char *event)
4273{
4274 struct ast_str *buf;
4275
4277 if (!buf) {
4278 return -1;
4279 }
4280
4281 sip_subscription_to_ami(sub_tree, &buf);
4282 astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
4283 ast_free(buf);
4284
4285 ++ami->count;
4286 return 0;
4287}
4288
4289static int ami_subscription_detail_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4290{
4291 return sub_tree->role == AST_SIP_NOTIFIER ? ami_subscription_detail(
4292 sub_tree, arg, "InboundSubscriptionDetail") : 0;
4293}
4294
4295static int ami_subscription_detail_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4296{
4297 return sub_tree->role == AST_SIP_SUBSCRIBER ? ami_subscription_detail(
4298 sub_tree, arg, "OutboundSubscriptionDetail") : 0;
4299}
4300
4301static int ami_show_subscriptions_inbound(struct mansession *s, const struct message *m)
4302{
4303 struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
4304
4305 astman_send_listack(s, m, "Following are Events for each inbound Subscription",
4306 "start");
4307
4309
4310 astman_send_list_complete_start(s, m, "InboundSubscriptionDetailComplete", ami.count);
4312 return 0;
4313}
4314
4315static int ami_show_subscriptions_outbound(struct mansession *s, const struct message *m)
4316{
4317 struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
4318
4319 astman_send_listack(s, m, "Following are Events for each outbound Subscription",
4320 "start");
4321
4323
4324 astman_send_list_complete_start(s, m, "OutboundSubscriptionDetailComplete", ami.count);
4326 return 0;
4327}
4328
4329static int format_ami_resource_lists(void *obj, void *arg, int flags)
4330{
4331 struct resource_list *list = obj;
4332 struct ast_sip_ami *ami = arg;
4333 struct ast_str *buf;
4334
4335 buf = ast_sip_create_ami_event("ResourceListDetail", ami);
4336 if (!buf) {
4337 return CMP_STOP;
4338 }
4339
4340 if (ast_sip_sorcery_object_to_ami(list, &buf)) {
4341 ast_free(buf);
4342 return CMP_STOP;
4343 }
4344 astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
4345 ast_free(buf);
4346
4347 ++ami->count;
4348 return 0;
4349}
4350
4351static int ami_show_resource_lists(struct mansession *s, const struct message *m)
4352{
4353 struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
4354 struct ao2_container *lists;
4355
4356 lists = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "resource_list",
4358
4359 if (!lists || !ao2_container_count(lists)) {
4360 astman_send_error(s, m, "No resource lists found\n");
4361 return 0;
4362 }
4363
4364 astman_send_listack(s, m, "A listing of resource lists follows, presented as ResourceListDetail events",
4365 "start");
4366
4368
4369 astman_send_list_complete_start(s, m, "ResourceListDetailComplete", ami.count);
4371 return 0;
4372}
4373
4374#define AMI_SHOW_SUBSCRIPTIONS_INBOUND "PJSIPShowSubscriptionsInbound"
4375#define AMI_SHOW_SUBSCRIPTIONS_OUTBOUND "PJSIPShowSubscriptionsOutbound"
4376
4377#define MAX_REGEX_ERROR_LEN 128
4378
4380 /*! CLI handler entry e parameter */
4382 /*! CLI handler entry a parameter */
4384 /*! CLI subscription entry output line(s) */
4385 struct ast_str *buf;
4386 /*! Compiled regular expression to select if buf is written to CLI when not NULL. */
4387 regex_t *like;
4389};
4390
4393 /*! Found callid for search position */
4394 char *callid;
4397};
4398
4400{
4401 pj_str_t *callid;
4402
4403 if (!sub_tree->dlg) {
4404 return 0;
4405 }
4406
4407 callid = &sub_tree->dlg->call_id->id;
4408 if (cli->wordlen <= pj_strlen(callid)
4409 && !strncasecmp(cli->a->word, pj_strbuf(callid), cli->wordlen)
4410 && (++cli->which > cli->a->n)) {
4411 cli->callid = ast_malloc(pj_strlen(callid) + 1);
4412 if (cli->callid) {
4413 ast_copy_pj_str(cli->callid, callid, pj_strlen(callid) + 1);
4414 }
4415 return -1;
4416 }
4417 return 0;
4418}
4419
4420static int cli_complete_subscription_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4421{
4422 return sub_tree->role == AST_SIP_NOTIFIER
4423 ? cli_complete_subscription_common(sub_tree, arg) : 0;
4424}
4425
4426static int cli_complete_subscription_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4427{
4428 return sub_tree->role == AST_SIP_SUBSCRIBER
4429 ? cli_complete_subscription_common(sub_tree, arg) : 0;
4430}
4431
4433{
4435 on_subscription_t on_subscription;
4436
4437 if (a->pos != 4) {
4438 return NULL;
4439 }
4440
4441 if (!strcasecmp(a->argv[3], "inbound")) {
4442 on_subscription = cli_complete_subscription_inbound;
4443 } else if (!strcasecmp(a->argv[3], "outbound")) {
4444 on_subscription = cli_complete_subscription_outbound;
4445 } else {
4446 /* Should never get here */
4447 ast_assert(0);
4448 return NULL;
4449 }
4450
4451 cli.a = a;
4452 cli.callid = NULL;
4453 cli.wordlen = strlen(a->word);
4454 cli.which = 0;
4455 for_each_subscription(on_subscription, &cli);
4456
4457 return cli.callid;
4458}
4459
4460static unsigned int cli_subscription_expiry(struct sip_subscription_tree *sub_tree)
4461{
4462 int expiry;
4463
4464 expiry = sub_tree->persistence
4465 ? ast_tvdiff_ms(sub_tree->persistence->expires, ast_tvnow()) / 1000
4466 : 0;
4467 if (expiry < 0) {
4468 /* Subscription expired */
4469 expiry = 0;
4470 }
4471 return expiry;
4472}
4473
4475{
4476 const char *callid = (const char *) cli->buf;/* Member repurposed to pass in callid */
4477 pj_str_t *sub_callid;
4478 struct ast_str *buf;
4479 char *src;
4480 char *dest;
4481 char *key;
4482 char *value;
4483 char *value_end;
4484 int key_len;
4485 int key_filler_width;
4486 int value_len;
4487
4488 if (!sub_tree->dlg) {
4489 return 0;
4490 }
4491 sub_callid = &sub_tree->dlg->call_id->id;
4492 if (pj_strcmp2(sub_callid, callid)) {
4493 return 0;
4494 }
4495
4496 buf = ast_str_create(512);
4497 if (!buf) {
4498 return -1;
4499 }
4500
4501 ast_cli(cli->a->fd,
4502 "%-20s: %s\n"
4503 "===========================================================================\n",
4504 "ParameterName", "ParameterValue");
4505
4506 ast_str_append(&buf, 0, "Resource: %s\n", sub_tree->root->resource);
4507 ast_str_append(&buf, 0, "Event: %s\n", sub_tree->root->handler->event_name);
4508 ast_str_append(&buf, 0, "Expiry: %u\n", cli_subscription_expiry(sub_tree));
4509
4510 sip_subscription_to_ami(sub_tree, &buf);
4511
4512 /* Convert AMI \r\n to \n line terminators. */
4513 src = strchr(ast_str_buffer(buf), '\r');
4514 if (src) {
4515 dest = src;
4516 ++src;
4517 while (*src) {
4518 if (*src == '\r') {
4519 ++src;
4520 continue;
4521 }
4522 *dest++ = *src++;
4523 }
4524 *dest = '\0';
4526 }
4527
4528 /* Reformat AMI key value pairs to pretty columns */
4529 key = ast_str_buffer(buf);
4530 do {
4531 value = strchr(key, ':');
4532 if (!value) {
4533 break;
4534 }
4535 value_end = strchr(value, '\n');
4536 if (!value_end) {
4537 break;
4538 }
4539
4540 /* Calculate field lengths */
4541 key_len = value - key;
4542 key_filler_width = 20 - key_len;
4543 if (key_filler_width < 0) {
4544 key_filler_width = 0;
4545 }
4546 value_len = value_end - value;
4547
4548 ast_cli(cli->a->fd, "%.*s%*s%.*s\n",
4549 key_len, key, key_filler_width, "",
4550 value_len, value);
4551
4552 key = value_end + 1;
4553 } while (*key);
4554 ast_cli(cli->a->fd, "\n");
4555
4556 ast_free(buf);
4557
4558 return -1;
4559}
4560
4561static int cli_show_subscription_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4562{
4563 return sub_tree->role == AST_SIP_NOTIFIER
4564 ? cli_show_subscription_common(sub_tree, arg) : 0;
4565}
4566
4567static int cli_show_subscription_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4568{
4569 return sub_tree->role == AST_SIP_SUBSCRIBER
4570 ? cli_show_subscription_common(sub_tree, arg) : 0;
4571}
4572
4573static char *cli_show_subscription_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4574{
4575 on_subscription_t on_subscription;
4576 struct cli_sub_parms cli;
4577
4578 switch (cmd) {
4579 case CLI_INIT:
4580 e->command = "pjsip show subscription {inbound|outbound}";
4581 e->usage = "Usage:\n"
4582 " pjsip show subscription inbound <call-id>\n"
4583 " pjsip show subscription outbound <call-id>\n"
4584 " Show active subscription with the dialog call-id\n";
4585 return NULL;
4586 case CLI_GENERATE:
4588 }
4589
4590 if (a->argc != 5) {
4591 return CLI_SHOWUSAGE;
4592 }
4593
4594 if (!strcasecmp(a->argv[3], "inbound")) {
4595 on_subscription = cli_show_subscription_inbound;
4596 } else if (!strcasecmp(a->argv[3], "outbound")) {
4597 on_subscription = cli_show_subscription_outbound;
4598 } else {
4599 /* Should never get here */
4600 ast_assert(0);
4601 return NULL;
4602 }
4603
4604 /* Find the subscription with the specified call-id */
4605 cli.a = a;
4606 cli.e = e;
4607 cli.buf = (void *) a->argv[4];/* Repurpose the buf member to pass in callid */
4608 for_each_subscription(on_subscription, &cli);
4609
4610 return CLI_SUCCESS;
4611}
4612
4613#define CLI_SHOW_SUB_FORMAT_HEADER \
4614 "Endpoint: <Endpoint/Caller-ID.............................................>\n" \
4615 "Resource: <Resource/Event.................................................>\n" \
4616 " Expiry: <Expiry> <Call-id..............................................>\n" \
4617 "===========================================================================\n\n"
4618#define CLI_SHOW_SUB_FORMAT_ENTRY \
4619 "Endpoint: %s/%s\n" \
4620 "Resource: %s/%s\n" \
4621 " Expiry: %8d %s\n\n"
4622
4624{
4625 char caller_id[256];
4626 char callid[256];
4627
4628 ast_callerid_merge(caller_id, sizeof(caller_id),
4629 S_COR(sub_tree->endpoint->id.self.name.valid,
4630 sub_tree->endpoint->id.self.name.str, NULL),
4631 S_COR(sub_tree->endpoint->id.self.number.valid,
4632 sub_tree->endpoint->id.self.number.str, NULL),
4633 "<none>");
4634
4635 /* Call-id */
4636 if (sub_tree->dlg) {
4637 ast_copy_pj_str(callid, &sub_tree->dlg->call_id->id, sizeof(callid));
4638 } else {
4639 ast_copy_string(callid, "<unknown>", sizeof(callid));
4640 }
4641
4643 ast_sorcery_object_get_id(sub_tree->endpoint), caller_id,
4644 sub_tree->root->resource, sub_tree->root->handler->event_name,
4645 cli_subscription_expiry(sub_tree), callid);
4646
4647 if (cli->like) {
4648 if (regexec(cli->like, ast_str_buffer(cli->buf), 0, NULL, 0)) {
4649 /* Output line did not match the regex */
4650 return 0;
4651 }
4652 }
4653
4654 ast_cli(cli->a->fd, "%s", ast_str_buffer(cli->buf));
4655 ++cli->count;
4656
4657 return 0;
4658}
4659
4660static int cli_show_subscriptions_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4661{
4662 return sub_tree->role == AST_SIP_NOTIFIER
4663 ? cli_show_subscriptions_detail(sub_tree, arg) : 0;
4664}
4665
4666static int cli_show_subscriptions_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4667{
4668 return sub_tree->role == AST_SIP_SUBSCRIBER
4669 ? cli_show_subscriptions_detail(sub_tree, arg) : 0;
4670}
4671
4672static char *cli_show_subscriptions_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4673{
4674 on_subscription_t on_subscription;
4675 struct cli_sub_parms cli;
4676 regex_t like;
4677 const char *regex;
4678
4679 switch (cmd) {
4680 case CLI_INIT:
4681 e->command = "pjsip show subscriptions {inbound|outbound} [like]";
4682 e->usage = "Usage:\n"
4683 " pjsip show subscriptions inbound [like <regex>]\n"
4684 " Show active inbound subscriptions\n"
4685 " pjsip show subscriptions outbound [like <regex>]\n"
4686 " Show active outbound subscriptions\n"
4687 "\n"
4688 " The regex selects a subscriptions output that matches.\n"
4689 " i.e., All output lines for a subscription are checked\n"
4690 " as a block by the regex.\n";
4691 return NULL;
4692 case CLI_GENERATE:
4693 return NULL;
4694 }
4695
4696 if (a->argc != 4 && a->argc != 6) {
4697 return CLI_SHOWUSAGE;
4698 }
4699 if (!strcasecmp(a->argv[3], "inbound")) {
4700 on_subscription = cli_show_subscriptions_inbound;
4701 } else if (!strcasecmp(a->argv[3], "outbound")) {
4702 on_subscription = cli_show_subscriptions_outbound;
4703 } else {
4704 /* Should never get here */
4705 ast_assert(0);
4706 return CLI_SHOWUSAGE;
4707 }
4708 if (a->argc == 6) {
4709 int rc;
4710
4711 if (strcasecmp(a->argv[4], "like")) {
4712 return CLI_SHOWUSAGE;
4713 }
4714
4715 /* Setup regular expression */
4716 memset(&like, 0, sizeof(like));
4717 cli.like = &like;
4718 regex = a->argv[5];
4719 rc = regcomp(cli.like, regex, REG_EXTENDED | REG_NOSUB);
4720 if (rc) {
4721 char *regerr = ast_alloca(MAX_REGEX_ERROR_LEN);
4722
4723 regerror(rc, cli.like, regerr, MAX_REGEX_ERROR_LEN);
4724 ast_cli(a->fd, "Regular expression '%s' failed to compile: %s\n",
4725 regex, regerr);
4726 return CLI_FAILURE;
4727 }
4728 } else {
4729 cli.like = NULL;
4730 regex = NULL;
4731 }
4732
4733 cli.a = a;
4734 cli.e = e;
4735 cli.count = 0;
4736 cli.buf = ast_str_create(256);
4737 if (!cli.buf) {
4738 if (cli.like) {
4739 regfree(cli.like);
4740 }
4741 return CLI_FAILURE;
4742 }
4743
4745 for_each_subscription(on_subscription, &cli);
4746 ast_cli(a->fd, "%d active subscriptions%s%s%s\n",
4747 cli.count,
4748 regex ? " matched \"" : "",
4749 regex ?: "",
4750 regex ? "\"" : "");
4751
4752 ast_free(cli.buf);
4753 if (cli.like) {
4754 regfree(cli.like);
4755 }
4756
4757 return CLI_SUCCESS;
4758}
4759
4760#define CLI_LIST_SUB_FORMAT_HEADER "%-30.30s %-30.30s %6.6s %s\n"
4761#define CLI_LIST_SUB_FORMAT_ENTRY "%-30.30s %-30.30s %6d %s\n"
4762
4764{
4765 char ep_cid_buf[50];
4766 char res_evt_buf[50];
4767 char callid[256];
4768
4769 /* Endpoint/CID column */
4770 snprintf(ep_cid_buf, sizeof(ep_cid_buf), "%s/%s",
4772 S_COR(sub_tree->endpoint->id.self.name.valid, sub_tree->endpoint->id.self.name.str,
4773 S_COR(sub_tree->endpoint->id.self.number.valid,
4774 sub_tree->endpoint->id.self.number.str, "<none>")));
4775
4776 /* Resource/Event column */
4777 snprintf(res_evt_buf, sizeof(res_evt_buf), "%s/%s",
4778 sub_tree->root->resource,
4779 sub_tree->root->handler->event_name);
4780
4781 /* Call-id column */
4782 if (sub_tree->dlg) {
4783 ast_copy_pj_str(callid, &sub_tree->dlg->call_id->id, sizeof(callid));
4784 } else {
4785 ast_copy_string(callid, "<unknown>", sizeof(callid));
4786 }
4787
4789 ep_cid_buf,
4790 res_evt_buf,
4791 cli_subscription_expiry(sub_tree),
4792 callid);
4793
4794 if (cli->like) {
4795 if (regexec(cli->like, ast_str_buffer(cli->buf), 0, NULL, 0)) {
4796 /* Output line did not match the regex */
4797 return 0;
4798 }
4799 }
4800
4801 ast_cli(cli->a->fd, "%s", ast_str_buffer(cli->buf));
4802 ++cli->count;
4803
4804 return 0;
4805}
4806
4807static int cli_list_subscriptions_inbound(struct sip_subscription_tree *sub_tree, void *arg)
4808{
4809 return sub_tree->role == AST_SIP_NOTIFIER
4810 ? cli_list_subscriptions_detail(sub_tree, arg) : 0;
4811}
4812
4813static int cli_list_subscriptions_outbound(struct sip_subscription_tree *sub_tree, void *arg)
4814{
4815 return sub_tree->role == AST_SIP_SUBSCRIBER
4816 ? cli_list_subscriptions_detail(sub_tree, arg) : 0;
4817}
4818
4819static char *cli_list_subscriptions_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4820{
4821 on_subscription_t on_subscription;
4822 struct cli_sub_parms cli;
4823 regex_t like;
4824 const char *regex;
4825
4826 switch (cmd) {
4827 case CLI_INIT:
4828 e->command = "pjsip list subscriptions {inbound|outbound} [like]";
4829 e->usage = "Usage:\n"
4830 " pjsip list subscriptions inbound [like <regex>]\n"
4831 " List active inbound subscriptions\n"
4832 " pjsip list subscriptions outbound [like <regex>]\n"
4833 " List active outbound subscriptions\n"
4834 "\n"
4835 " The regex selects output lines that match.\n";
4836 return NULL;
4837 case CLI_GENERATE:
4838 return NULL;
4839 }
4840
4841 if (a->argc != 4 && a->argc != 6) {
4842 return CLI_SHOWUSAGE;
4843 }
4844 if (!strcasecmp(a->argv[3], "inbound")) {
4845 on_subscription = cli_list_subscriptions_inbound;
4846 } else if (!strcasecmp(a->argv[3], "outbound")) {
4847 on_subscription = cli_list_subscriptions_outbound;
4848 } else {
4849 /* Should never get here */
4850 ast_assert(0);
4851 return CLI_SHOWUSAGE;
4852 }
4853 if (a->argc == 6) {
4854 int rc;
4855
4856 if (strcasecmp(a->argv[4], "like")) {
4857 return CLI_SHOWUSAGE;
4858 }
4859
4860 /* Setup regular expression */
4861 memset(&like, 0, sizeof(like));
4862 cli.like = &like;
4863 regex = a->argv[5];
4864 rc = regcomp(cli.like, regex, REG_EXTENDED | REG_NOSUB);
4865 if (rc) {
4866 char *regerr = ast_alloca(MAX_REGEX_ERROR_LEN);
4867
4868 regerror(rc, cli.like, regerr, MAX_REGEX_ERROR_LEN);
4869 ast_cli(a->fd, "Regular expression '%s' failed to compile: %s\n",
4870 regex, regerr);
4871 return CLI_FAILURE;
4872 }
4873 } else {
4874 cli.like = NULL;
4875 regex = NULL;
4876 }
4877
4878 cli.a = a;
4879 cli.e = e;
4880 cli.count = 0;
4881 cli.buf = ast_str_create(256);
4882 if (!cli.buf) {
4883 if (cli.like) {
4884 regfree(cli.like);
4885 }
4886 return CLI_FAILURE;
4887 }
4888
4890 "Endpoint/CLI", "Resource/Event", "Expiry", "Call-id");
4891 for_each_subscription(on_subscription, &cli);
4892 ast_cli(a->fd, "\n%d active subscriptions%s%s%s\n",
4893 cli.count,
4894 regex ? " matched \"" : "",
4895 regex ?: "",
4896 regex ? "\"" : "");
4897
4898 ast_free(cli.buf);
4899 if (cli.like) {
4900 regfree(cli.like);
4901 }
4902
4903 return CLI_SUCCESS;
4904}
4905
4906static struct ast_cli_entry cli_commands[] = {
4907 AST_CLI_DEFINE(cli_list_subscriptions_inout, "List active inbound/outbound subscriptions"),
4908 AST_CLI_DEFINE(cli_show_subscription_inout, "Show active subscription details"),
4909 AST_CLI_DEFINE(cli_show_subscriptions_inout, "Show active inbound/outbound subscriptions"),
4910};
4911
4912static int persistence_endpoint_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
4913{
4914 struct subscription_persistence *persistence = obj;
4915
4916 persistence->endpoint = ast_strdup(var->value);
4917 return 0;
4918}
4919
4920static int persistence_endpoint_struct2str(const void *obj, const intptr_t *args, char **buf)
4921{
4922 const struct subscription_persistence *persistence = obj;
4923
4924 *buf = ast_strdup(persistence->endpoint);
4925 return 0;
4926}
4927
4928static int persistence_tag_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
4929{
4930 struct subscription_persistence *persistence = obj;
4931
4932 persistence->tag = ast_strdup(var->value);
4933 return 0;
4934}
4935
4936static int persistence_tag_struct2str(const void *obj, const intptr_t *args, char **buf)
4937{
4938 const struct subscription_persistence *persistence = obj;
4939
4940 *buf = ast_strdup(persistence->tag);
4941 return 0;
4942}
4943
4944static int persistence_generator_data_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
4945{
4946 struct subscription_persistence *persistence = obj;
4947 struct ast_json_error error;
4948
4949 /* We tolerate a failure of the JSON to load and instead start fresh, since this field
4950 * originates from the persistence code and not a user.
4951 */
4952 persistence->generator_data = ast_json_load_string(var->value, &error);
4953
4954 return 0;
4955}
4956
4957static int persistence_generator_data_struct2str(const void *obj, const intptr_t *args, char **buf)
4958{
4959 const struct subscription_persistence *persistence = obj;
4960 char *value;
4961
4962 if (!persistence->generator_data) {
4963 return 0;
4964 }
4965
4967 if (!value) {
4968 return -1;
4969 }
4970
4971 *buf = ast_strdup(value);
4973
4974 return 0;
4975}
4976
4977static int persistence_expires_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
4978{
4979 struct subscription_persistence *persistence = obj;
4980 return ast_get_timeval(var->value, &persistence->expires, ast_tv(0, 0), NULL);
4981}
4982
4983static int persistence_expires_struct2str(const void *obj, const intptr_t *args, char **buf)
4984{
4985 const struct subscription_persistence *persistence = obj;
4986 char secs[AST_TIME_T_LEN];
4987
4988 ast_time_t_to_string(persistence->expires.tv_sec, secs, sizeof(secs));
4989
4990 return (ast_asprintf(buf, "%s", secs) < 0) ? -1 : 0;
4991}
4992
4993#define RESOURCE_LIST_INIT_SIZE 4
4994
4995static void resource_list_destructor(void *obj)
4996{
4997 struct resource_list *list = obj;
4998 int i;
4999
5000 for (i = 0; i < AST_VECTOR_SIZE(&list->items); ++i) {
5001 ast_free((char *) AST_VECTOR_GET(&list->items, i));
5002 }
5003
5004 AST_VECTOR_FREE(&list->items);
5005}
5006
5007static void *resource_list_alloc(const char *name)
5008{
5009 struct resource_list *list;
5010
5012 if (!list) {
5013 return NULL;
5014 }
5015
5017 ao2_cleanup(list);
5018 return NULL;
5019 }
5020
5021 return list;
5022}
5023
5024static int item_in_vector(const struct resource_list *list, const char *item)
5025{
5026 int i;
5027
5028 for (i = 0; i < AST_VECTOR_SIZE(&list->items); ++i) {
5029 if (!strcmp(item, AST_VECTOR_GET(&list->items, i))) {
5030 return 1;
5031 }
5032 }
5033
5034 return 0;
5035}
5036
5037static int list_item_handler(const struct aco_option *opt,
5038 struct ast_variable *var, void *obj)
5039{
5040 struct resource_list *list = obj;
5041 char *items = ast_strdupa(var->value);
5042 char *item;
5043
5044 while ((item = ast_strip(strsep(&items, ",")))) {
5045 if (ast_strlen_zero(item)) {
5046 continue;
5047 }
5048
5049 if (item_in_vector(list, item)) {
5050 ast_log(LOG_WARNING, "Ignoring duplicated list item '%s'\n", item);
5051 continue;
5052 }
5053
5054 item = ast_strdup(item);
5055 if (!item || AST_VECTOR_APPEND(&list->items, item)) {
5056 ast_free(item);
5057 return -1;
5058 }
5059 }
5060
5061 return 0;
5062}
5063
5064static int list_item_to_str(const void *obj, const intptr_t *args, char **buf)
5065{
5066 const struct resource_list *list = obj;
5067 int i;
5068 struct ast_str *str = ast_str_create(32);
5069
5070 for (i = 0; i < AST_VECTOR_SIZE(&list->items); ++i) {
5071 ast_str_append(&str, 0, "%s,", AST_VECTOR_GET(&list->items, i));
5072 }
5073
5074 /* Chop off trailing comma */
5075 ast_str_truncate(str, -1);
5077 ast_free(str);
5078 return 0;
5079}
5080
5081static int resource_list_apply_handler(const struct ast_sorcery *sorcery, void *obj)
5082{
5083 struct resource_list *list = obj;
5084
5085 if (ast_strlen_zero(list->event)) {
5086 ast_log(LOG_WARNING, "Resource list '%s' has no event set\n",
5088 return -1;
5089 }
5090
5091 if (AST_VECTOR_SIZE(&list->items) == 0) {
5092 ast_log(LOG_WARNING, "Resource list '%s' has no list items\n",
5094 return -1;
5095 }
5096
5097 return 0;
5098}
5099
5101{
5102 ast_sorcery_apply_default(sorcery, "resource_list", "config",
5103 "pjsip.conf,criteria=type=resource_list");
5106 return -1;
5107 }
5108
5109 ast_sorcery_object_field_register(sorcery, "resource_list", "type", "",
5110 OPT_NOOP_T, 0, 0);
5111 ast_sorcery_object_field_register(sorcery, "resource_list", "event", "",
5113 ast_sorcery_object_field_register(sorcery, "resource_list", "full_state", "no",
5115 ast_sorcery_object_field_register(sorcery, "resource_list", "notification_batch_interval",
5117 ast_sorcery_object_field_register_custom(sorcery, "resource_list", "list_item",
5119 ast_sorcery_object_field_register(sorcery, "resource_list", "resource_display_name", "no",
5121
5122 ast_sorcery_reload_object(sorcery, "resource_list");
5123
5124 return 0;
5125}
5126
5127#ifdef TEST_FRAMEWORK
5128
5129/*!
5130 * \brief "bad" resources
5131 *
5132 * These are resources that the test handler will reject subscriptions to.
5133 */
5134const char *bad_resources[] = {
5135 "coconut",
5136 "cilantro",
5137 "olive",
5138 "cheese",
5139};
5140
5141/*!
5142 * \brief new_subscribe callback for unit tests
5143 *
5144 * Will give a 200 OK response to any resource except the "bad" ones.
5145 */
5146static int test_new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource)
5147{
5148 int i;
5149
5150 for (i = 0; i < ARRAY_LEN(bad_resources); ++i) {
5151 if (!strcmp(resource, bad_resources[i])) {
5152 return 400;
5153 }
5154 }
5155
5156 return 200;
5157}
5158
5159/*!
5160 * \brief Subscription notifier for unit tests.
5161 *
5162 * Since unit tests are only concerned with building a resource tree,
5163 * only the new_subscribe callback needs to be defined.
5164 */
5165struct ast_sip_notifier test_notifier = {
5166 .new_subscribe = test_new_subscribe,
5167};
5168
5169/*!
5170 * \brief Subscription handler for unit tests.
5171 */
5173 .event_name = "test",
5174 .body_type = "",
5175 .notifier = &test_notifier,
5176};
5177
5178/*!
5179 * \brief Set properties on an allocated resource list
5180 *
5181 * \param list The list to set details on.
5182 * \param event The list's event.
5183 * \param resources Array of resources to add to the list.
5184 * \param num_resources Number of resources in the array.
5185 * \retval 0 Success
5186 * \retval non-zero Failure
5187 */
5188static int populate_list(struct resource_list *list, const char *event, const char **resources, size_t num_resources)
5189{
5190 int i;
5191
5192 ast_copy_string(list->event, event, sizeof(list->event));
5193
5194 for (i = 0; i < num_resources; ++i) {
5195 char *resource = ast_strdup(resources[i]);
5196
5197 if (!resource || AST_VECTOR_APPEND(&list->items, resource)) {
5198 ast_free(resource);
5199 return -1;
5200 }
5201 }
5202 return 0;
5203}
5204
5205/*!
5206 * \brief RAII callback to destroy a resource list
5207 */
5208static void cleanup_resource_list(struct resource_list *list)
5209{
5210 if (!list) {
5211 return;
5212 }
5213
5215 ao2_cleanup(list);
5216}
5217
5218/*!
5219 * \brief allocate a resource list, store it in sorcery, and set its details
5220 *
5221 * \param test The unit test. Used for logging status messages.
5222 * \param list_name The name of the list to create.
5223 * \param event The event the list services
5224 * \param resources Array of resources to apply to the list
5225 * \param num_resources The number of resources in the array
5226 * \retval NULL Failed to allocate or populate list
5227 * \retval non-NULL The created list
5228 */
5229static struct resource_list *create_resource_list(struct ast_test *test,
5230 const char *list_name, const char *event, const char **resources, size_t num_resources)
5231{
5232 struct resource_list *list;
5233
5234 list = ast_sorcery_alloc(ast_sip_get_sorcery(), "resource_list", list_name);
5235 if (!list) {
5236 ast_test_status_update(test, "Could not allocate resource list in sorcery\n");
5237 return NULL;
5238 }
5239
5241 ast_test_status_update(test, "Could not store the resource list in sorcery\n");
5242 ao2_cleanup(list);
5243 return NULL;
5244 }
5245
5246 if (populate_list(list, event, resources, num_resources)) {
5247 ast_test_status_update(test, "Could not add resources to the resource list\n");
5248 cleanup_resource_list(list);
5249 return NULL;
5250 }
5251
5252 return list;
5253}
5254
5255/*!
5256 * \brief Check the integrity of a tree node against a set of resources.
5257 *
5258 * The tree node's resources must be in the same order as the resources in
5259 * the supplied resources array. Because of this constraint, tests can misrepresent
5260 * the size of the resources array as being smaller than it really is if resources
5261 * at the end of the array should not be present in the tree node.
5262 *
5263 * \param test The unit test. Used for printing status messages.
5264 * \param node The constructed tree node whose integrity is under question.
5265 * \param resources Array of expected resource values
5266 * \param num_resources The number of resources to check in the array.
5267 */
5268static int check_node(struct ast_test *test, struct tree_node *node,
5269 const char **resources, size_t num_resources)
5270{
5271 int i;
5272
5273 if (AST_VECTOR_SIZE(&node->children) != num_resources) {
5274 ast_test_status_update(test, "Unexpected number of resources in tree. Expected %zu, got %zu\n",
5275 num_resources, AST_VECTOR_SIZE(&node->children));
5276 return -1;
5277 }
5278
5279 for (i = 0; i < num_resources; ++i) {
5280 if (strcmp(resources[i], AST_VECTOR_GET(&node->children, i)->resource)) {
5281 ast_test_status_update(test, "Mismatched resources. Expected '%s' but got '%s'\n",
5282 resources[i], AST_VECTOR_GET(&node->children, i)->resource);
5283 return -1;
5284 }
5285 }
5286
5287 return 0;
5288}
5289
5290/*!
5291 * \brief RAII_VAR callback to destroy an allocated resource tree
5292 */
5293static void test_resource_tree_destroy(struct resource_tree *tree)
5294{
5296 ast_free(tree);
5297}
5298
5299static int ineligible_configuration(void)
5300{
5301 struct ast_config *config;
5302 struct ast_flags flags = {0,};
5303 const char *value;
5304
5305 config = ast_config_load("sorcery.conf", flags);
5306 if (!config) {
5307 return 1;
5308 }
5309
5310 value = ast_variable_retrieve(config, "res_pjsip_pubsub", "resource_list");
5311 if (ast_strlen_zero(value)) {
5313 return 1;
5314 }
5315
5316 if (strcasecmp(value, "memory") && strcasecmp(value, "astdb")) {
5318 return 1;
5319 }
5320
5321 return 0;
5322}
5323
5325{
5326 RAII_VAR(struct resource_list *, list, NULL, cleanup_resource_list);
5327 RAII_VAR(struct resource_tree *, tree, NULL, test_resource_tree_destroy);
5328 const char *resources[] = {
5329 "huey",
5330 "dewey",
5331 "louie",
5332 };
5333 int resp;
5334
5335 switch (cmd) {
5336 case TEST_INIT:
5337 info->name = "resource_tree";
5338 info->category = "/res/res_pjsip_pubsub/";
5339 info->summary = "Basic resource tree integrity check";
5340 info->description =
5341 "Create a resource list and ensure that our attempt to build a tree works as expected.";
5342 return AST_TEST_NOT_RUN;
5343 case TEST_EXECUTE:
5344 break;
5345 }
5346
5347 if (ineligible_configuration()) {
5348 ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5349 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5350 return AST_TEST_NOT_RUN;
5351 }
5352
5353 list = create_resource_list(test, "foo", "test", resources, ARRAY_LEN(resources));
5354 if (!list) {
5355 return AST_TEST_FAIL;
5356 }
5357
5358 tree = ast_calloc(1, sizeof(*tree));
5359 resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
5360 if (resp != 200) {
5361 ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5362 return AST_TEST_FAIL;
5363 }
5364
5365 if (!tree->root) {
5366 ast_test_status_update(test, "Resource tree has no root\n");
5367 return AST_TEST_FAIL;
5368 }
5369
5370 if (check_node(test, tree->root, resources, ARRAY_LEN(resources))) {
5371 return AST_TEST_FAIL;
5372 }
5373
5374 return AST_TEST_PASS;
5375}
5376
5377AST_TEST_DEFINE(complex_resource_tree)
5378{
5379 RAII_VAR(struct resource_list *, list_1, NULL, cleanup_resource_list);
5380 RAII_VAR(struct resource_list *, list_2, NULL, cleanup_resource_list);
5381 RAII_VAR(struct resource_tree *, tree, NULL, test_resource_tree_destroy);
5382 const char *resources_1[] = {
5383 "huey",
5384 "dewey",
5385 "louie",
5386 "dwarves",
5387 };
5388 const char *resources_2[] = {
5389 "happy",
5390 "grumpy",
5391 "doc",
5392 "bashful",
5393 "dopey",
5394 "sneezy",
5395 "sleepy",
5396 };
5397 int resp;
5398 struct tree_node *node;
5399
5400 switch (cmd) {
5401 case TEST_INIT:
5402 info->name = "complex_resource_tree";
5403 info->category = "/res/res_pjsip_pubsub/";
5404 info->summary = "Complex resource tree integrity check";
5405 info->description =
5406 "Create a complex resource list and ensure that our attempt to build a tree works as expected.";
5407 return AST_TEST_NOT_RUN;
5408 case TEST_EXECUTE:
5409 break;
5410 }
5411
5412 if (ineligible_configuration()) {
5413 ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5414 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5415 return AST_TEST_NOT_RUN;
5416 }
5417
5418 list_1 = create_resource_list(test, "foo", "test", resources_1, ARRAY_LEN(resources_1));
5419 if (!list_1) {
5420 return AST_TEST_FAIL;
5421 }
5422
5423 list_2 = create_resource_list(test, "dwarves", "test", resources_2, ARRAY_LEN(resources_2));
5424 if (!list_2) {
5425 return AST_TEST_FAIL;
5426 }
5427
5428 tree = ast_calloc(1, sizeof(*tree));
5429 resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
5430 if (resp != 200) {
5431 ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5432 return AST_TEST_FAIL;
5433 }
5434
5435 if (!tree->root) {
5436 ast_test_status_update(test, "Resource tree has no root\n");
5437 return AST_TEST_FAIL;
5438 }
5439
5440 node = tree->root;
5441 if (check_node(test, node, resources_1, ARRAY_LEN(resources_1))) {
5442 return AST_TEST_FAIL;
5443 }
5444
5445 /* The embedded list is at index 3 in the root node's children */
5446 node = AST_VECTOR_GET(&node->children, 3);
5447 if (check_node(test, node, resources_2, ARRAY_LEN(resources_2))) {
5448 return AST_TEST_FAIL;
5449 }
5450
5451 return AST_TEST_PASS;
5452}
5453
5454AST_TEST_DEFINE(bad_resource)
5455{
5456 RAII_VAR(struct resource_list *, list, NULL, cleanup_resource_list);
5457 RAII_VAR(struct resource_tree *, tree, NULL, test_resource_tree_destroy);
5458 const char *resources[] = {
5459 "huey",
5460 "dewey",
5461 "louie",
5462 "coconut", /* A "bad" resource */
5463 };
5464 int resp;
5465
5466 switch (cmd) {
5467 case TEST_INIT:
5468 info->name = "bad_resource";
5469 info->category = "/res/res_pjsip_pubsub/";
5470 info->summary = "Ensure bad resources do not end up in the tree";
5471 info->description =
5472 "Create a resource list with a single bad resource. Ensure the bad resource does not end up in the tree.";
5473 return AST_TEST_NOT_RUN;
5474 case TEST_EXECUTE:
5475 break;
5476 }
5477
5478 if (ineligible_configuration()) {
5479 ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5480 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5481 return AST_TEST_NOT_RUN;
5482 }
5483
5484 list = create_resource_list(test, "foo", "test", resources, ARRAY_LEN(resources));
5485 if (!list) {
5486 return AST_TEST_FAIL;
5487 }
5488
5489 tree = ast_calloc(1, sizeof(*tree));
5490 resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
5491 if (resp != 200) {
5492 ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5493 return AST_TEST_FAIL;
5494 }
5495
5496 if (!tree->root) {
5497 ast_test_status_update(test, "Resource tree has no root\n");
5498 return AST_TEST_FAIL;
5499 }
5500
5501 /* We check against all but the final resource since we expect it not to be in the tree */
5502 if (check_node(test, tree->root, resources, ARRAY_LEN(resources) - 1)) {
5503 return AST_TEST_FAIL;
5504 }
5505
5506 return AST_TEST_PASS;
5507
5508}
5509
5510AST_TEST_DEFINE(bad_branch)
5511{
5512 RAII_VAR(struct resource_list *, list_1, NULL, cleanup_resource_list);
5513 RAII_VAR(struct resource_list *, list_2, NULL, cleanup_resource_list);
5514 RAII_VAR(struct resource_tree *, tree, NULL, test_resource_tree_destroy);
5515 const char *resources_1[] = {
5516 "huey",
5517 "dewey",
5518 "louie",
5519 "gross",
5520 };
5521 /* This list has nothing but bad resources */
5522 const char *resources_2[] = {
5523 "coconut",
5524 "cilantro",
5525 "olive",
5526 "cheese",
5527 };
5528 int resp;
5529
5530 switch (cmd) {
5531 case TEST_INIT:
5532 info->name = "bad_branch";
5533 info->category = "/res/res_pjsip_pubsub/";
5534 info->summary = "Ensure bad branches are pruned from the tree";
5535 info->description =
5536 "Create a resource list that makes a tree with an entire branch of bad resources.\n"
5537 "Ensure the bad branch is pruned from the tree.";
5538 return AST_TEST_NOT_RUN;
5539 case TEST_EXECUTE:
5540 break;
5541 }
5542
5543 if (ineligible_configuration()) {
5544 ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5545 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5546 return AST_TEST_NOT_RUN;
5547 }
5548
5549 list_1 = create_resource_list(test, "foo", "test", resources_1, ARRAY_LEN(resources_1));
5550 if (!list_1) {
5551 return AST_TEST_FAIL;
5552 }
5553 list_2 = create_resource_list(test, "gross", "test", resources_2, ARRAY_LEN(resources_2));
5554 if (!list_2) {
5555 return AST_TEST_FAIL;
5556 }
5557
5558 tree = ast_calloc(1, sizeof(*tree));
5559 resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
5560 if (resp != 200) {
5561 ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5562 return AST_TEST_FAIL;
5563 }
5564
5565 if (!tree->root) {
5566 ast_test_status_update(test, "Resource tree has no root\n");
5567 return AST_TEST_FAIL;
5568 }
5569
5570 /* We check against all but the final resource of the list since the entire branch should
5571 * be pruned from the tree
5572 */
5573 if (check_node(test, tree->root, resources_1, ARRAY_LEN(resources_1) - 1)) {
5574 return AST_TEST_FAIL;
5575 }
5576
5577 return AST_TEST_PASS;
5578
5579}
5580
5581AST_TEST_DEFINE(duplicate_resource)
5582{
5583 RAII_VAR(struct resource_list *, list_1, NULL, cleanup_resource_list);
5584 RAII_VAR(struct resource_list *, list_2, NULL, cleanup_resource_list);
5585 RAII_VAR(struct resource_tree *, tree, NULL, test_resource_tree_destroy);
5586 const char *resources_1[] = {
5587 "huey",
5588 "ducks",
5589 "dewey",
5590 "louie",
5591 };
5592 const char *resources_2[] = {
5593 "donald",
5594 "daisy",
5595 "scrooge",
5596 "dewey",
5597 "louie",
5598 "huey",
5599 };
5600 int resp;
5601 struct tree_node *node;
5602
5603 switch (cmd) {
5604 case TEST_INIT:
5605 info->name = "duplicate_resource";
5606 info->category = "/res/res_pjsip_pubsub/";
5607 info->summary = "Ensure duplicated resources do not end up in the tree";
5608 info->description =
5609 "Create a resource list with a single duplicated resource. Ensure the duplicated resource does not end up in the tree.";
5610 return AST_TEST_NOT_RUN;
5611 case TEST_EXECUTE:
5612 break;
5613 }
5614
5615 if (ineligible_configuration()) {
5616 ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5617 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5618 return AST_TEST_NOT_RUN;
5619 }
5620
5621 list_1 = create_resource_list(test, "foo", "test", resources_1, ARRAY_LEN(resources_1));
5622 if (!list_1) {
5623 return AST_TEST_FAIL;
5624 }
5625
5626 list_2 = create_resource_list(test, "ducks", "test", resources_2, ARRAY_LEN(resources_2));
5627 if (!list_2) {
5628 return AST_TEST_FAIL;
5629 }
5630
5631 tree = ast_calloc(1, sizeof(*tree));
5632 resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
5633 if (resp != 200) {
5634 ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5635 return AST_TEST_FAIL;
5636 }
5637
5638 if (!tree->root) {
5639 ast_test_status_update(test, "Resource tree has no root\n");
5640 return AST_TEST_FAIL;
5641 }
5642
5643 node = tree->root;
5644 /* This node should have "huey" and "ducks". "dewey" and "louie" should not
5645 * be present since they were found in the "ducks" list.
5646 */
5647 if (check_node(test, node, resources_1, ARRAY_LEN(resources_1) - 2)) {
5648 return AST_TEST_FAIL;
5649 }
5650
5651 /* This node should have "donald", "daisy", "scrooge", "dewey", and "louie".
5652 * "huey" is not here since that was already encountered in the parent list
5653 */
5654 node = AST_VECTOR_GET(&node->children, 1);
5655 if (check_node(test, node, resources_2, ARRAY_LEN(resources_2) - 1)) {
5656 return AST_TEST_FAIL;
5657 }
5658
5659 return AST_TEST_PASS;
5660}
5661
5662AST_TEST_DEFINE(loop)
5663{
5664 RAII_VAR(struct resource_list *, list_1, NULL, cleanup_resource_list);
5665 RAII_VAR(struct resource_list *, list_2, NULL, cleanup_resource_list);
5666 RAII_VAR(struct resource_tree *, tree, NULL, test_resource_tree_destroy);
5667 const char *resources_1[] = {
5668 "derp",
5669 };
5670 const char *resources_2[] = {
5671 "herp",
5672 };
5673 int resp;
5674
5675 switch (cmd) {
5676 case TEST_INIT:
5677 info->name = "loop";
5678 info->category = "/res/res_pjsip_pubsub/";
5679 info->summary = "Test that loops are properly detected.";
5680 info->description =
5681 "Create two resource lists that refer to each other. Ensure that attempting to build a tree\n"
5682 "results in an empty tree.";
5683 return AST_TEST_NOT_RUN;
5684 case TEST_EXECUTE:
5685 break;
5686 }
5687
5688 if (ineligible_configuration()) {
5689 ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5690 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5691 return AST_TEST_NOT_RUN;
5692 }
5693
5694 list_1 = create_resource_list(test, "herp", "test", resources_1, ARRAY_LEN(resources_1));
5695 if (!list_1) {
5696 return AST_TEST_FAIL;
5697 }
5698 list_2 = create_resource_list(test, "derp", "test", resources_2, ARRAY_LEN(resources_2));
5699 if (!list_2) {
5700 return AST_TEST_FAIL;
5701 }
5702
5703 tree = ast_calloc(1, sizeof(*tree));
5704 resp = build_resource_tree(NULL, &test_handler, "herp", tree, 1, NULL);
5705 if (resp == 200) {
5706 ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5707 return AST_TEST_FAIL;
5708 }
5709
5710 return AST_TEST_PASS;
5711}
5712
5713AST_TEST_DEFINE(bad_event)
5714{
5715 RAII_VAR(struct resource_list *, list, NULL, cleanup_resource_list);
5716 RAII_VAR(struct resource_tree *, tree, NULL, test_resource_tree_destroy);
5717 const char *resources[] = {
5718 "huey",
5719 "dewey",
5720 "louie",
5721 };
5722 int resp;
5723
5724 switch (cmd) {
5725 case TEST_INIT:
5726 info->name = "bad_event";
5727 info->category = "/res/res_pjsip_pubsub/";
5728 info->summary = "Ensure that list with wrong event specified is not retrieved";
5729 info->description =
5730 "Create a simple resource list for event 'tsetse'. Ensure that trying to retrieve the list for event 'test' fails.";
5731 return AST_TEST_NOT_RUN;
5732 case TEST_EXECUTE:
5733 break;
5734 }
5735
5736 if (ineligible_configuration()) {
5737 ast_test_status_update(test, "Ineligible configuration for this test. Please add a "
5738 "'res_pjsip_pubsub' section to sorcery.conf, and set 'resource_list=memory'\n");
5739 return AST_TEST_NOT_RUN;
5740 }
5741
5742 list = create_resource_list(test, "foo", "tsetse", resources, ARRAY_LEN(resources));
5743 if (!list) {
5744 return AST_TEST_FAIL;
5745 }
5746
5747 tree = ast_calloc(1, sizeof(*tree));
5748 /* Since the test_handler is for event "test", this should not build a list, but
5749 * instead result in a single resource being created, called "foo"
5750 */
5751 resp = build_resource_tree(NULL, &test_handler, "foo", tree, 1, NULL);
5752 if (resp != 200) {
5753 ast_test_status_update(test, "Unexpected response %d when building resource tree\n", resp);
5754 return AST_TEST_FAIL;
5755 }
5756
5757 if (!tree->root) {
5758 ast_test_status_update(test, "Resource tree has no root\n");
5759 return AST_TEST_FAIL;
5760 }
5761
5762 if (strcmp(tree->root->resource, "foo")) {
5763 ast_test_status_update(test, "Unexpected resource %s found in tree\n", tree->root->resource);
5764 return AST_TEST_FAIL;
5765 }
5766
5767 return AST_TEST_PASS;
5768}
5769
5770#endif
5771
5772static int resource_endpoint_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
5773{
5774 struct ast_sip_publication_resource *resource = obj;
5775
5776 ast_free(resource->endpoint);
5777 resource->endpoint = ast_strdup(var->value);
5778
5779 return 0;
5780}
5781
5782static int resource_event_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
5783{
5784 struct ast_sip_publication_resource *resource = obj;
5785 /* The event configuration name starts with 'event_' so skip past it to get the real name */
5786 const char *event = var->name + 6;
5787 struct ast_variable *item;
5788
5789 if (ast_strlen_zero(event) || ast_strlen_zero(var->value)) {
5790 return -1;
5791 }
5792
5793 item = ast_variable_new(event, var->value, "");
5794 if (!item) {
5795 return -1;
5796 }
5797
5798 if (resource->events) {
5799 item->next = resource->events;
5800 }
5801 resource->events = item;
5802
5803 return 0;
5804}
5805
5806static int load_module(void)
5807{
5808 static const pj_str_t str_PUBLISH = { "PUBLISH", 7 };
5809 struct ast_sorcery *sorcery;
5810
5812
5813 if (!(sched = ast_sched_context_create())) {
5814 ast_log(LOG_ERROR, "Could not create scheduler for publication expiration\n");
5816 }
5817
5819 ast_log(LOG_ERROR, "Could not start scheduler thread for publication expiration\n");
5822 }
5823
5824 ast_sorcery_apply_config(sorcery, "res_pjsip_pubsub");
5825 ast_sorcery_apply_default(sorcery, "subscription_persistence", "astdb", "subscription_persistence");
5827 NULL, NULL)) {
5828 ast_log(LOG_ERROR, "Could not register subscription persistence object support\n");
5831 }
5832 ast_sorcery_object_field_register(sorcery, "subscription_persistence", "packet", "", OPT_CHAR_ARRAY_T, 0,
5833 CHARFLDSET(struct subscription_persistence, packet));
5834 ast_sorcery_object_field_register(sorcery, "subscription_persistence", "src_name", "", OPT_CHAR_ARRAY_T, 0,
5835 CHARFLDSET(struct subscription_persistence, src_name));
5836 ast_sorcery_object_field_register(sorcery, "subscription_persistence", "src_port", "0", OPT_UINT_T, 0,
5837 FLDSET(struct subscription_persistence, src_port));
5838 ast_sorcery_object_field_register(sorcery, "subscription_persistence", "transport_key", "0", OPT_CHAR_ARRAY_T, 0,
5839 CHARFLDSET(struct subscription_persistence, transport_type));
5840 ast_sorcery_object_field_register(sorcery, "subscription_persistence", "local_name", "", OPT_CHAR_ARRAY_T, 0,
5841 CHARFLDSET(struct subscription_persistence, local_name));
5842 ast_sorcery_object_field_register(sorcery, "subscription_persistence", "local_port", "0", OPT_UINT_T, 0,
5843 FLDSET(struct subscription_persistence, local_port));
5844 ast_sorcery_object_field_register(sorcery, "subscription_persistence", "cseq", "0", OPT_UINT_T, 0,
5845 FLDSET(struct subscription_persistence, cseq));
5846 ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "endpoint", "",
5848 ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "tag", "",
5850 ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "expires", "",
5852 ast_sorcery_object_field_register(sorcery, "subscription_persistence", "contact_uri", "", OPT_CHAR_ARRAY_T, 0,
5853 CHARFLDSET(struct subscription_persistence, contact_uri));
5854 ast_sorcery_object_field_register(sorcery, "subscription_persistence", "prune_on_boot", "no", OPT_YESNO_T, 1,
5855 FLDSET(struct subscription_persistence, prune_on_boot));
5856 ast_sorcery_object_field_register_custom(sorcery, "subscription_persistence", "generator_data", "",
5858
5862 }
5863
5864 ast_sorcery_apply_default(sorcery, "inbound-publication", "config", "pjsip.conf,criteria=type=inbound-publication");
5866 NULL, NULL)) {
5867 ast_log(LOG_ERROR, "Could not register subscription persistence object support\n");
5870 }
5871 ast_sorcery_object_field_register(sorcery, "inbound-publication", "type", "", OPT_NOOP_T, 0, 0);
5872 ast_sorcery_object_field_register_custom(sorcery, "inbound-publication", "endpoint", "",
5874 ast_sorcery_object_fields_register(sorcery, "inbound-publication", "^event_", resource_event_handler, NULL);
5875 ast_sorcery_reload_object(sorcery, "inbound-publication");
5876
5878 ast_log(LOG_ERROR, "Could not register pubsub service\n");
5881 }
5882
5883 if (pjsip_evsub_init_module(ast_sip_get_pjsip_endpoint()) != PJ_SUCCESS) {
5884 ast_log(LOG_ERROR, "Could not initialize pjsip evsub module.\n");
5888 }
5889
5890 /* Once pjsip_evsub_init_module succeeds we cannot unload.
5891 * Keep all module_load errors above this point. */
5893
5894 pjsip_media_type_init2(&rlmi_media_type, "application", "rlmi+xml");
5895
5896 pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &str_PUBLISH);
5897
5900 } else {
5901 struct stasis_subscription *sub;
5902
5906 }
5907
5912 ast_manager_register_xml("PJSIPShowResourceLists", EVENT_FLAG_SYSTEM,
5914
5916
5918 AST_TEST_REGISTER(complex_resource_tree);
5919 AST_TEST_REGISTER(bad_resource);
5920 AST_TEST_REGISTER(bad_branch);
5921 AST_TEST_REGISTER(duplicate_resource);
5922 AST_TEST_REGISTER(loop);
5923 AST_TEST_REGISTER(bad_event);
5924
5926}
5927
5928static int unload_module(void)
5929{
5931 AST_TEST_UNREGISTER(complex_resource_tree);
5932 AST_TEST_UNREGISTER(bad_resource);
5933 AST_TEST_UNREGISTER(bad_branch);
5934 AST_TEST_UNREGISTER(duplicate_resource);
5935 AST_TEST_UNREGISTER(loop);
5936 AST_TEST_UNREGISTER(bad_event);
5937
5939
5941
5944 ast_manager_unregister("PJSIPShowResourceLists");
5945
5947 if (sched) {
5949 }
5950
5951 return 0;
5952}
5953
5955 .support_level = AST_MODULE_SUPPORT_CORE,
5956 .load = load_module,
5957 .unload = unload_module,
5958 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
5959 .requires = "res_pjsip",
static struct ast_generator gen
const char * str
Definition: app_jack.c:147
char * text
Definition: app_queue.c:1639
enum queue_result id
Definition: app_queue.c:1638
#define var
Definition: ast_expr2f.c:605
if(!yyg->yy_init)
Definition: ast_expr2f.c:854
while(1)
Definition: ast_expr2f.c:880
Asterisk main include file. File version handling, generic pbx functions.
int ast_shutdown_final(void)
Definition: asterisk.c:1867
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028
#define OBJ_KEY
Definition: astobj2.h:1151
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_UNLINK
Definition: astobj2.h:1039
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1174
static const char type[]
Definition: chan_ooh323.c:109
static const char config[]
Definition: chan_ooh323.c:111
#define AST_MAX_EXTENSION
Definition: channel.h:134
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
static struct ast_cli_entry cli[]
Definition: codec_dahdi.c:265
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
#define CHARFLDSET(type, field)
A helper macro to pass the appropriate arguments to aco_option_register for OPT_CHAR_ARRAY_T.
@ OPT_UINT_T
Type for default option handler for unsigned integers.
@ OPT_NOOP_T
Type for a default handler that should do nothing.
@ OPT_BOOL_T
Type for default option handler for bools (ast_true/ast_false)
@ OPT_CHAR_ARRAY_T
Type for default option handler for character array strings.
@ OPT_YESNO_T
Type for default option handler for bools (ast_true/ast_false)
Asterisk datastore objects.
void ast_datastores_remove(struct ao2_container *datastores, const char *name)
Remove a data store from a container.
Definition: datastore.c:118
struct ao2_container * ast_datastores_alloc(void)
Allocate a specialized data stores container.
Definition: datastore.c:99
int ast_datastores_add(struct ao2_container *datastores, struct ast_datastore *datastore)
Add a data store to a container.
Definition: datastore.c:105
struct ast_datastore * ast_datastores_find(struct ao2_container *datastores, const char *name)
Find a data store in a container.
Definition: datastore.c:123
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:142
char * end
Definition: eagi_proxy.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static const char name[]
Definition: format_mp3.c:68
static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
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:3396
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3354
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:3432
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition: manager.c:1845
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3015
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:3440
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3275
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8014
struct ast_flags ast_options
Definition: options.c:61
@ AST_OPT_FLAG_FULLY_BOOTED
Definition: options.h:58
struct ast_taskprocessor * ast_sip_get_distributor_serializer(pjsip_rx_data *rdata)
Determine the distributor serializer for the SIP message.
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:2099
struct ast_taskprocessor * ast_sip_create_serializer(const char *name)
Create a new serializer for SIP tasks.
Definition: res_pjsip.c:2094
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.
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.
int ast_sip_sched_task_cancel(struct ast_sip_sched_task *schtd)
Cancels the next invocation of a task.
int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, size_t maxlen)
Gets the task name.
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.
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:2165
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:2179
@ AST_SIP_SCHED_TASK_FIXED
Definition: res_pjsip.h:2063
@ AST_SIP_SCHED_TASK_DATA_AO2
Definition: res_pjsip.h:2086
char * strsep(char **str, const char *delims)
#define ast_config_load(filename, flags)
Load a config file.
#define ast_variable_new(name, value, filename)
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:783
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
struct ast_json * ast_json_object_create(void)
Create a new JSON object.
Definition: json.c:399
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:810
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:567
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
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:414
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
A set of macros to manage forward-linked lists.
#define AST_RWLIST_REMOVE_CURRENT
Definition: linkedlists.h:570
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:545
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#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:333
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:617
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:741
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:757
size_t current
Definition: main/cli.c:113
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define EVENT_FLAG_SYSTEM
Definition: manager.h:75
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
struct stasis_message_type * ast_manager_get_generic_type(void)
Get the stasis_message_type for generic messages.
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:316
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:469
#define ast_module_ref(mod)
Hold a reference to the module.
Definition: module.h:443
#define ast_module_shutdown_ref(mod)
Prevent unload of the module before shutdown.
Definition: module.h:464
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Asterisk MWI API.
#define ast_publish_mwi_state(mailbox, context, new_msgs, old_msgs)
Publish a MWI state update via stasis.
Definition: mwi.h:378
def info(msg)
static char user[512]
struct stasis_forward * sub
Definition: res_corosync.c:240
int ast_sip_will_uri_survive_restart(pjsip_sip_uri *uri, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
Definition: res_pjsip.c:525
const pj_str_t * ast_sip_pjsip_uri_get_username(pjsip_uri *uri)
Get the user portion of the pjsip_uri.
Definition: res_pjsip.c:3477
void ast_sip_unregister_service(pjsip_module *module)
Definition: res_pjsip.c:133
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
int ast_sip_register_service(pjsip_module *module)
Register a SIP service in Asterisk.
Definition: res_pjsip.c:117
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:964
enum ast_transport_monitor_reg ast_sip_transport_monitor_register_key(const char *transport_key, ast_transport_monitor_shutdown_cb cb, void *ao2_data)
Register a reliable transport shutdown monitor callback.
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:520
#define AST_SIP_MAKE_REMOTE_IPADDR_PORT_STR(_transport, _dest)
Fill a buffer with a pjsip transport's remote ip address and port.
Definition: res_pjsip.h:90
#define AST_SIP_USER_OPTIONS_TRUNCATE_CHECK(str)
Truncate the URI user field options string if enabled.
Definition: res_pjsip.h:3339
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.
int ast_sip_is_uri_sip_sips(pjsip_uri *uri)
Check whether a pjsip_uri is SIP/SIPS or not.
Definition: res_pjsip.c:3467
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:2201
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.
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:1192
#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:2968
#define IP6ADDR_COLON_PORT_BUFLEN
Definition: res_pjsip.h:82
#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:2936
int ast_sip_sorcery_object_to_ami(const void *obj, struct ast_str **buf)
Converts a sorcery object to a string of object properties.
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.
#define PJSIP_EXPIRES_NOT_SPECIFIED
Definition: res_pjsip.h:68
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:2008
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
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:2248
void ast_sip_transport_monitor_unregister_key(const char *transport_key, ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
Unregister a reliable transport shutdown monitor.
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:1214
static struct ast_sorcery * sorcery
void ast_sip_sanitize_xml(const char *input, char *output, size_t len)
Replace offensive XML characters with XML entities.
Definition: presence_xml.c:29
#define AST_PJSIP_XML_PROLOG_LEN
Length of the XML prolog when printing presence or other XML in PJSIP.
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
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
#define CLI_SHOW_SUB_FORMAT_ENTRY
static void subscription_tree_destructor(void *obj)
static int cli_show_subscriptions_detail(struct sip_subscription_tree *sub_tree, struct cli_sub_parms *cli)
struct ast_sip_endpoint * ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub)
Get the endpoint that is associated with this subscription.
int(* on_subscription_t)(struct sip_subscription_tree *sub, void *arg)
void ast_sip_subscription_get_remote_uri(struct ast_sip_subscription *sub, char *buf, size_t size)
Retrive the remote URI for this subscription.
static int subscription_persistence_recreate(void *obj, void *arg, int flags)
Callback function to perform the actual recreation of a subscription.
static int subscription_persistence_load(void *data)
Function which loads and recreates persisted subscriptions upon startup when the system is fully boot...
static struct subscription_persistence * subscription_persistence_create(struct sip_subscription_tree *sub_tree)
Function which creates initial persistence information of a subscription in sorcery.
int ast_sip_register_publish_handler(struct ast_sip_publish_handler *handler)
Register a publish handler.
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 int cli_show_subscriptions_outbound(struct sip_subscription_tree *sub_tree, void *arg)
static int cli_complete_subscription_common(struct sip_subscription_tree *sub_tree, struct cli_sub_complete_parms *cli)
void ast_sip_unregister_subscription_handler(struct ast_sip_subscription_handler *handler)
Unregister a subscription handler.
static pjsip_generic_string_hdr * generate_content_id_hdr(pj_pool_t *pool, const struct ast_sip_subscription *sub)
Create a Content-ID header.
static struct ast_cli_entry cli_commands[]
const char * ast_sip_subscription_get_body_type(struct ast_sip_subscription *sub)
Get the body type used for this subscription.
static int generate_initial_notify(struct ast_sip_subscription *sub)
static int allocate_tdata_buffer(pjsip_tx_data *tdata)
Pre-allocate a buffer for the transmission.
static int sip_publication_respond(struct ast_sip_publication *pub, int status_code, pjsip_rx_data *rdata)
static struct ast_sip_subscription_handler * find_sub_handler_for_event_name(const char *event_name)
static int ami_show_resource_lists(struct mansession *s, const struct message *m)
void ast_sip_pubsub_unregister_body_generator(struct ast_sip_pubsub_body_generator *generator)
Unregister a body generator with the pubsub core.
static unsigned int cli_subscription_expiry(struct sip_subscription_tree *sub_tree)
int ast_sip_register_subscription_handler(struct ast_sip_subscription_handler *handler)
Register a subscription handler.
static void pubsub_on_client_refresh(pjsip_evsub *sub)
static int publication_hash_fn(const void *obj, const int flags)
static int resource_list_apply_handler(const struct ast_sorcery *sorcery, void *obj)
void ast_sip_pubsub_unregister_body_supplement(struct ast_sip_pubsub_body_supplement *supplement)
Unregister a body generator with the pubsub core.
static int sub_tree_subscription_terminate_cb(void *data)
#define CLI_SHOW_SUB_FORMAT_HEADER
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.
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, pjsip_rx_data *rdata)
Build a resource tree.
static int schedule_notification(struct sip_subscription_tree *sub_tree)
static int pubsub_on_refresh_timeout(void *userdata)
static int publication_cmp_fn(void *obj, void *arg, int flags)
static struct ast_sched_context * sched
Scheduler used for automatically expiring publications.
sip_persistence_update_type
@ SUBSCRIPTION_PERSISTENCE_CREATED
@ SUBSCRIPTION_PERSISTENCE_SEND_REQUEST
@ SUBSCRIPTION_PERSISTENCE_REFRESHED
@ SUBSCRIPTION_PERSISTENCE_RECREATED
static int exceptional_accept(const pj_str_t *accept)
Is the Accept header from the SUBSCRIBE in the list of exceptions?
static void pubsub_on_server_timeout(pjsip_evsub *sub)
sip_subscription_tree_state
The state of the subscription tree.
@ SIP_SUB_TREE_TERMINATE_PENDING
@ SIP_SUB_TREE_TERMINATED
@ SIP_SUB_TREE_NORMAL
@ SIP_SUB_TREE_TERMINATE_IN_PROGRESS
static int resource_event_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static int cli_complete_subscription_outbound(struct sip_subscription_tree *sub_tree, void *arg)
void ast_sip_subscription_destroy(struct ast_sip_subscription *sub)
Alert the pubsub core that the subscription is ready for destruction.
static int serialized_pubsub_on_refresh_timeout(void *userdata)
void ast_sip_subscription_remove_datastore(struct ast_sip_subscription *subscription, const char *name)
Remove a subscription datastore from the subscription.
static int serialized_send_notify(void *userdata)
static void clean_sub_tree(pjsip_evsub *evsub)
Callback sequence for subscription terminate:
sip_publish_type
The types of PUBLISH messages defined in RFC 3903.
@ SIP_PUBLISH_REMOVE
Remove.
@ SIP_PUBLISH_REFRESH
Refresh.
@ SIP_PUBLISH_UNKNOWN
Unknown.
@ SIP_PUBLISH_MODIFY
Modify.
@ SIP_PUBLISH_INITIAL
Initial.
static void * subscription_persistence_alloc(const char *name)
Allocator for subscription persistence.
static int serialized_pubsub_on_client_refresh(void *userdata)
static int sub_persistence_recreate(void *obj)
const struct ast_json * ast_sip_subscription_get_persistence_data(const struct ast_sip_subscription *subscription)
Retrieve persistence data for a subscription.
#define MAX_REGEX_ERROR_LEN
static int cli_list_subscriptions_detail(struct sip_subscription_tree *sub_tree, struct cli_sub_parms *cli)
static int esc_etag_counter
static char * cli_complete_subscription_callid(struct ast_cli_args *a)
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.
struct ast_datastore * ast_sip_subscription_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
static const pj_str_t str_event_name
static int format_ami_resource_lists(void *obj, void *arg, int flags)
#define DEFAULT_PUBLISH_EXPIRES
Default expiration time for PUBLISH if one is not specified.
static pj_bool_t pubsub_on_rx_publish_request(pjsip_rx_data *rdata)
static void shutdown_subscriptions(struct ast_sip_subscription *sub)
static void * rlmi_clone_data(pj_pool_t *pool, const void *data, unsigned len)
static int ami_show_subscriptions_outbound(struct mansession *s, const struct message *m)
static int cli_show_subscription_outbound(struct sip_subscription_tree *sub_tree, void *arg)
static void remove_subscription(struct sip_subscription_tree *obj)
static int persistence_endpoint_struct2str(const void *obj, const intptr_t *args, char **buf)
struct ast_taskprocessor * ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub)
Get the serializer for the subscription.
static pjsip_require_hdr * create_require_eventlist(pj_pool_t *pool)
Shortcut method to create a Require: eventlist header.
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.
int ast_sip_pubsub_register_body_supplement(struct ast_sip_pubsub_body_supplement *supplement)
Register a body generator with the pubsub core.
void ast_sip_publication_remove_datastore(struct ast_sip_publication *publication, const char *name)
Remove a publication datastore from the publication.
static void * publication_resource_alloc(const char *name)
Allocator for publication resource.
static int resource_endpoint_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
void ast_sip_unregister_publish_handler(struct ast_sip_publish_handler *handler)
Unregister a publish handler.
static int cli_show_subscription_inbound(struct sip_subscription_tree *sub_tree, void *arg)
static int ami_show_subscriptions_inbound(struct mansession *s, const struct message *m)
static int cli_list_subscriptions_outbound(struct sip_subscription_tree *sub_tree, void *arg)
int ast_sip_subscription_add_datastore(struct ast_sip_subscription *subscription, struct ast_datastore *datastore)
Add a datastore to a SIP subscription.
static int parse_simple_message_summary(char *body, struct simple_message_summary *summary)
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.
static char * sub_tree_state_description[]
#define AMI_SHOW_SUBSCRIPTIONS_INBOUND
#define NEW_SUBSCRIBE(notifier, endpoint, resource, rdata)
const char * ast_sip_subscription_get_body_subtype(struct ast_sip_subscription *sub)
Get the body subtype used for this subscription.
#define AMI_SHOW_SUBSCRIPTIONS_OUTBOUND
const char * ast_sip_subscription_get_resource_name(struct ast_sip_subscription *sub)
Get the name of the subscribed resource.
static int rlmi_print_body(struct pjsip_msg_body *msg_body, char *buf, pj_size_t size)
static struct ast_sip_subscription * allocate_subscription(const struct ast_sip_subscription_handler *handler, const char *resource, const char *display_name, struct sip_subscription_tree *tree)
#define PUBLICATIONS_BUCKETS
Number of buckets for publications (on a per handler)
static int have_visited(const char *resource, struct resources *visited)
Determine if this resource has been visited already.
static pjsip_evsub_user pubsub_cb
static int sched_cb(const void *data)
#define RESOURCE_LIST_INIT_SIZE
static struct pjsip_module pubsub_module
static void sub_add_handler(struct ast_sip_subscription_handler *handler)
pjsip_sip_uri * ast_sip_subscription_get_sip_uri(struct ast_sip_subscription *sub)
Retrieve the local sip uri for this subscription.
void ast_sip_subscription_set_persistence_data(struct ast_sip_subscription *subscription, struct ast_json *persistence_data)
Set persistence data for a subscription.
static char * cli_show_subscriptions_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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 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 int cli_show_subscriptions_inbound(struct sip_subscription_tree *sub_tree, void *arg)
static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state)
Send a NOTIFY request to a subscriber.
static void destroy_subscriptions(struct ast_sip_subscription *root)
int ast_sip_pubsub_register_body_generator(struct ast_sip_pubsub_body_generator *generator)
Register a body generator with the pubsub core.
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, pjsip_rx_data *rdata)
Build child nodes for a given parent.
static int persistence_generator_data_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
Opaque structure representing an RFC 3265 SIP subscription.
static void add_subscription(struct sip_subscription_tree *obj)
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 void free_body_parts(struct body_part_list *parts)
Destroy a list of body parts.
static int subscription_unreference_dialog(void *obj)
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)
static int publish_expire(const void *data)
static int persistence_expires_struct2str(const void *obj, const intptr_t *args, char **buf)
static pjsip_msg_body * create_multipart_body(pj_pool_t *pool)
Create and initialize the PJSIP multipart body structure for a resource list subscription.
static int ami_subscription_detail(struct sip_subscription_tree *sub_tree, struct ast_sip_ami *ami, const char *event)
static pj_bool_t pubsub_on_rx_mwi_notify_request(pjsip_rx_data *rdata)
static int apply_list_configuration(struct ast_sorcery *sorcery)
static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
PJSIP callback when underlying SIP subscription changes state.
pjsip_dialog * ast_sip_subscription_get_dialog(struct ast_sip_subscription *sub)
Get the pjsip dialog that is associated with this subscription.
static int list_item_to_str(const void *obj, const intptr_t *args, char **buf)
static int list_item_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static int persistence_endpoint_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
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 struct ast_sip_pubsub_body_generator * find_body_generator(char accept[AST_SIP_MAX_ACCEPT][64], size_t num_accept, const char *body_type)
static int ast_sip_pubsub_has_eventlist_support(pjsip_rx_data *rdata)
Check if the rdata has a Supported header containing 'eventlist'.
static struct ast_sip_publish_handler * find_pub_handler(const char *event)
static void tree_node_destroy(struct tree_node *node)
Destructor for a tree node.
static int persistence_tag_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
struct ast_datastore * ast_sip_publication_get_datastore(struct ast_sip_publication *publication, const char *name)
Retrieve a publication datastore.
static void subscription_persistence_remove(struct sip_subscription_tree *sub_tree)
Function which removes persistence of a subscription from sorcery.
static int persistence_expires_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj)
static int publish_expire_callback(void *data)
static void resource_list_destructor(void *obj)
static int cmp_strings(char *s1, char *s2)
Compare strings for equality checking for NULL.
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.
struct ast_sip_endpoint * ast_sip_publication_get_endpoint(struct ast_sip_publication *pub)
Given a publication, get the associated endpoint.
#define MOD_DATA_MSG
static pj_bool_t pubsub_on_rx_notify_request(pjsip_rx_data *rdata)
static int item_in_vector(const struct resource_list *list, const char *item)
static void destroy_subscription(struct ast_sip_subscription *sub)
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.
#define CLI_LIST_SUB_FORMAT_HEADER
static void sub_tree_transport_cb(void *data)
int ast_sip_pubsub_is_body_generator_registered(const char *type, const char *subtype)
Is a body generator registered for the given type/subtype.
static int load_module(void)
static int persistence_tag_struct2str(const void *obj, const intptr_t *args, char **buf)
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.
static int for_each_subscription(on_subscription_t on_subscription, void *arg)
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 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 void resource_tree_destroy(struct resource_tree *tree)
Destroy a resource tree.
static int initial_notify_task(void *obj)
static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pjsip_dialog *dlg)
static pj_bool_t pubsub_on_rx_subscribe_request(pjsip_rx_data *rdata)
static int sip_subscription_accept(struct sip_subscription_tree *sub_tree, pjsip_rx_data *rdata, int response)
static int cli_show_subscription_common(struct sip_subscription_tree *sub_tree, struct cli_sub_parms *cli)
static struct ast_sip_pubsub_body_generator * find_body_generator_accept(const char *accept)
static int unload_module(void)
static void * resource_list_alloc(const char *name)
static int cmp_subscription_childrens(struct ast_sip_subscription *s1, struct ast_sip_subscription *s2)
compares the childrens of two ast_sip_subscription s1 and s2
static int persistence_generator_data_struct2str(const void *obj, const intptr_t *args, char **buf)
static char * cli_list_subscriptions_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void publication_resource_destroy(void *obj)
Destructor for publication resource.
static struct ast_sip_pubsub_body_generator * find_body_generator_type_subtype(const char *type, const char *subtype)
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 char * cli_show_subscription_inout(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
const pjsip_method pjsip_publish_method
Defined method for PUBLISH.
static void set_state_terminated(struct ast_sip_subscription *sub)
#define MOD_DATA_PERSISTENCE
const char * ast_sip_publication_get_resource(const struct ast_sip_publication *pub)
Given a publication, get the resource the publication is to.
struct ao2_container * ast_sip_publication_get_datastores(const struct ast_sip_publication *publication)
Get the datastores container for a publication.
static int cli_list_subscriptions_inbound(struct sip_subscription_tree *sub_tree, void *arg)
static void publish_add_handler(struct ast_sip_publish_handler *handler)
static struct ast_sip_publication * publish_request_initial(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, struct ast_sip_publish_handler *handler)
static int ami_subscription_detail_outbound(struct sip_subscription_tree *sub_tree, void *arg)
static void sip_subscription_to_ami(struct sip_subscription_tree *sub_tree, struct ast_str **buf)
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.
static struct tree_node * tree_node_alloc(const char *resource, struct resources *visited, unsigned int full_state, const char *display_name)
Allocate a tree node.
#define CLI_LIST_SUB_FORMAT_ENTRY
static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata)
static pjsip_media_type rlmi_media_type
static void subscription_persistence_destroy(void *obj)
Destructor for subscription persistence.
int ast_sip_subscription_is_terminated(const struct ast_sip_subscription *sub)
Get whether the subscription has been terminated or not.
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.
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 cli_complete_subscription_inbound(struct sip_subscription_tree *sub_tree, void *arg)
void * ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, const char *header)
Get a header value for a subscription.
int ast_sip_publication_add_datastore(struct ast_sip_publication *publication, struct ast_datastore *datastore)
Add a datastore to a SIP publication.
static const char * sip_subscription_roles_map[]
static struct body_part * allocate_body_part(pj_pool_t *pool, const struct ast_sip_subscription *sub)
Allocate and initialize a body part structure.
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 resource_list * retrieve_resource_list(const char *resource, const char *event)
Helper function for retrieving a resource list for a given event.
const char * accept_exceptions[]
Accept headers that are exceptions to the rule.
static void publication_destroy_fn(void *obj)
Internal destructor for publications.
static struct sip_subscription_tree * allocate_subscription_tree(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
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.
#define DEFAULT_EXPIRES
Default expiration for subscriptions.
static int destroy_subscriptions_task(void *obj)
struct ao2_container * ast_sip_subscription_get_datastores(const struct ast_sip_subscription *subscription)
Get the datastores container for a subscription.
struct ast_datastore * ast_sip_subscription_get_datastore(struct ast_sip_subscription *subscription, const char *name)
Retrieve a subscription datastore.
@ AST_SIP_PUBLISH_STATE_ACTIVE
@ AST_SIP_PUBLISH_STATE_TERMINATED
@ AST_SIP_PUBLISH_STATE_INITIALIZED
#define AST_SIP_MAX_ACCEPT
#define AST_SIP_DEVICE_FEATURE_SYNC_DATA
ast_sip_subscription_role
Role for the subscription that is being created.
@ AST_SIP_SUBSCRIBER
@ AST_SIP_NOTIFIER
#define NULL
Definition: resample.c:96
Scheduler Routines (derived from cheops)
#define AST_SCHED_DEL_UNREF(sched, id, refcall)
schedule task to get deleted and call unref function
Definition: sched.h:82
#define AST_SCHED_REPLACE_UNREF(id, sched, when, callback, data, unrefcall, addfailcall, refcall)
Definition: sched.h:152
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:567
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:271
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
Definition: sched.c:197
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:238
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
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
@ AST_RETRIEVE_FLAG_MULTIPLE
Return all matching objects.
Definition: sorcery.h:120
@ AST_RETRIEVE_FLAG_ALL
Perform no matching, return all objects.
Definition: sorcery.h:123
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_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
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:837
#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
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
#define ast_sorcery_apply_config(sorcery, name)
Definition: sorcery.h:455
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition: sorcery.c:1744
int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
Update an object.
Definition: sorcery.c:2145
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition: sorcery.h:476
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
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
int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
Delete an object.
Definition: sorcery.c:2233
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
@ STASIS_SUBSCRIPTION_FILTER_SELECTIVE
Definition: stasis.h:297
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:1023
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:1077
#define stasis_subscribe_pool(topic, callback, data)
Definition: stasis.h:680
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_subscription * stasis_unsubscribe(struct stasis_subscription *subscription)
Cancel a subscription.
Definition: stasis.c:971
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:786
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:1139
static force_inline char * ast_str_to_lower(char *str)
Convert a string to all lower-case.
Definition: strings.h:1321
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
#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:87
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
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:1113
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:703
char * ast_read_line_from_buffer(char **buffer)
Read lines from a string buffer.
Definition: strings.c:371
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
char * ast_generate_random_string(char *buf, size_t size)
Create a pseudo-random string of a fixed length.
Definition: strings.c:226
int ast_get_timeval(const char *src, struct timeval *tv, struct timeval _default, int *consumed)
Parse a time (float) string.
Definition: utils.c:2419
Generic container type.
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Structure for a data store type.
Definition: datastore.h:31
Structure for a data store object.
Definition: datastore.h:64
const char * uid
Definition: datastore.h:65
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
JSON parsing error information.
Definition: json.h:887
struct ast_json * json
Definition: json.h:1083
Abstract JSON element (object, array, string, int, ...).
struct ast_module * self
Definition: module.h:342
struct ast_party_name name
Subscriber name.
Definition: channel.h:340
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:279
char * str
Subscriber name (Malloced)
Definition: channel.h:264
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
AMI variable container.
Definition: res_pjsip.h:3035
struct mansession * s
Definition: res_pjsip.h:3037
void * arg
Definition: res_pjsip.h:3043
const struct message * m
Definition: res_pjsip.h:3039
Data used to create bodies for NOTIFY/PUBLISH requests.
const char * body_type
Contact associated with an address of record.
Definition: res_pjsip.h:384
const ast_string_field uri
Definition: res_pjsip.h:406
Party identification options for endpoints.
Definition: res_pjsip.h:759
struct ast_party_id self
Definition: res_pjsip.h:760
An entity with which Asterisk communicates.
Definition: res_pjsip.h:951
struct ast_sip_endpoint_id_configuration id
Definition: res_pjsip.h:990
const ast_string_field aors
Definition: res_pjsip.h:980
int(* new_subscribe)(struct ast_sip_endpoint *endpoint, const char *resource)
Called when a SUBSCRIBE arrives attempting to establish a new subscription.
int(* refresh_subscribe)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata)
Called when a SUBSCRIBE arrives for an already active subscription.
int(* notify_created)(struct ast_sip_subscription *sub, pjsip_tx_data *tdata)
Optional callback to execute before sending outgoing NOTIFY requests. Because res_pjsip_pubsub create...
Structure representing a publication resource.
SORCERY_OBJECT(details)
Sorcery object details.
char * endpoint
Optional name of an endpoint that is only allowed to publish to this resource.
struct ast_variable * events
Mapping for event types to configuration.
Structure representing a SIP publication.
unsigned int expires
Expiration time of the publication.
struct ast_sip_publish_handler * handler
Handler for this publication.
struct ast_sip_endpoint * endpoint
The endpoint with which the subscription is communicating.
int sched_id
Scheduled item for expiration of publication.
char * event_configuration_name
The name of the event type configuration.
char data[0]
Data containing the above.
struct ao2_container * datastores
char * resource
The resource the publication is to.
int entity_tag
Entity tag for the publication.
Callbacks that publication handlers will define.
struct ast_sip_publish_handler * next
struct ao2_container * publications
Publications.
const char * event_name
The name of the event this handler deals with.
int(* publication_state_change)(struct ast_sip_publication *pub, pjsip_msg_body *body, enum ast_sip_publish_state state)
Published resource has changed states.
Pubsub body generator.
void(* to_string)(void *body, struct ast_str **str)
Convert the body to a string.
void(* destroy_body)(void *body)
Deallocate resources created for the body.
struct ast_sip_pubsub_body_generator::@261 list
const char * type
Content type In "plain/text", "plain" is the type.
const char * subtype
Content subtype In "plain/text", "text" is the subtype.
void *(* allocate_body)(void *data)
allocate body structure.
int(* generate_body_content)(void *body, void *data)
Add content to the body of a SIP request.
struct ast_sip_pubsub_body_supplement::@262 list
int(* supplement_body)(void *body, void *data)
Add additional content to a SIP request body.
const char * type
Content type In "plain/text", "plain" is the type.
const char * subtype
Content subtype In "plain/text", "text" is the subtype.
struct ast_sip_notifier * notifier
void(* to_ami)(struct ast_sip_subscription *sub, struct ast_str **buf)
Converts the subscriber to AMI.
struct ast_sip_subscription_handler * next
const char * accept[AST_SIP_MAX_ACCEPT]
Structure representing a "virtual" SIP subscription.
struct ast_sip_subscription::@463 children
struct sip_subscription_tree * tree
struct ast_sip_pubsub_body_generator * body_generator
const struct ast_sip_subscription_handler * handler
struct ast_json * persistence_data
struct ast_str * body_text
struct ao2_container * datastores
pjsip_evsub_state subscription_state
Full structure for sorcery.
Definition: sorcery.c:230
Support for dynamic strings.
Definition: strings.h:623
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Type declaration for container of body part structures.
A multipart body part and meta-information.
pjsip_evsub_state state
const char * display_name
const char * resource
pjsip_generic_string_hdr * cid
pjsip_sip_uri * uri
pjsip_multipart_part * part
struct ast_cli_args * a
struct ast_str * buf
struct ast_cli_entry * e
struct ast_cli_args * a
Definition: astman.c:222
struct sip_subscription_tree * sub_tree
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:1750
Definition: test_heap.c:38
struct subscription_persistence * persistence
Resource list configuration item.
SORCERY_OBJECT(details)
unsigned int full_state
struct resources items
unsigned int notification_batch_interval
unsigned int resource_display_name
A resource tree.
struct tree_node * root
unsigned int notification_batch_interval
A vector of strings commonly used throughout this module.
Definition: sched.c:76
struct sched_id * sched_id
Definition: sched.c:79
char message_account[PJSIP_MAX_URL_SIZE]
A tree of SIP subscriptions.
struct subscription_persistence * persistence
struct ast_sip_endpoint * endpoint
char transport_key[IP6ADDR_COLON_PORT_BUFLEN]
enum ast_sip_subscription_role role
struct sip_subscription_tree * next
unsigned int send_scheduled_notify
enum sip_subscription_tree_state state
unsigned int generate_initial_notify
struct ast_sip_subscription * root
struct ast_taskprocessor * serializer
unsigned int notification_batch_interval
struct ast_sip_sched_task * expiration_task
Structure used for persisting an inbound subscription.
struct ast_json * generator_data
char src_name[PJ_INET6_ADDRSTRLEN]
char contact_uri[PJSIP_MAX_URL_SIZE]
char packet[PJSIP_MAX_PKT_LEN]
char local_name[PJ_INET6_ADDRSTRLEN]
A node for a resource tree.
char * display_name
unsigned int full_state
struct tree_node::@464 children
char resource[0]
structure to hold users read from users.conf
int value
Definition: syslog.c:37
An API for managing task processing threads that can be shared across modules.
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
void ast_taskprocessor_build_name(char *buf, unsigned int size, const char *format,...)
Build a taskprocessor name with a sequence number on the end.
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
Definition: taskprocessor.h:61
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
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
static struct aco_type item
Definition: test_config.c:1463
const char * args
static struct test_val a
static void test_handler(void *data, const char *app_name, struct ast_json *message)
int ast_time_t_to_string(time_t time, char *buf, size_t length)
Converts to a string representation of a time_t as decimal seconds since the epoch....
Definition: time.c:152
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:282
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
#define AST_TIME_T_LEN
Definition: time.h:45
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
Definition: time.h:235
int error(const char *format,...)
Definition: utils/frame.c:999
#define ast_test_flag(p, flag)
Definition: utils.h:63
#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:941
#define ast_assert(a)
Definition: utils.h:739
#define MIN(a, b)
Definition: utils.h:231
#define ARRAY_LEN(a)
Definition: utils.h:666
Universally unique identifier support.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680