Asterisk - The Open Source Telephony Project GIT-master-f36a736
res_pjsip_publish_asterisk.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2014, Digium, Inc.
5 *
6 * Joshua Colp <jcolp@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/*** MODULEINFO
20 <depend>pjproject</depend>
21 <depend>res_pjsip</depend>
22 <depend>res_pjsip_outbound_publish</depend>
23 <depend>res_pjsip_pubsub</depend>
24 <support_level>core</support_level>
25 ***/
26
27#include "asterisk.h"
28
29#include <regex.h>
30
31#include <pjsip.h>
32#include <pjsip_simple.h>
33
34#include "asterisk/res_pjsip.h"
37#include "asterisk/module.h"
38#include "asterisk/logger.h"
39#include "asterisk/mwi.h"
40
41/*** DOCUMENTATION
42 <configInfo name="res_pjsip_publish_asterisk" language="en_US">
43 <synopsis>SIP resource for inbound and outbound Asterisk event publications</synopsis>
44 <description><para>
45 <emphasis>Inbound and outbound Asterisk event publication</emphasis>
46 </para>
47 <para>This module allows <literal>res_pjsip</literal> to send and receive Asterisk event publications.</para>
48 </description>
49 <configFile name="pjsip.conf">
50 <configObject name="asterisk-publication">
51 <synopsis>The configuration for inbound Asterisk event publication</synopsis>
52 <description><para>
53 Publish is <emphasis>COMPLETELY</emphasis> separate from the rest of
54 <literal>pjsip.conf</literal>.
55 </para></description>
56 <configOption name="devicestate_publish">
57 <synopsis>Optional name of a publish item that can be used to publish a request for full device state information.</synopsis>
58 </configOption>
59 <configOption name="mailboxstate_publish">
60 <synopsis>Optional name of a publish item that can be used to publish a request for full mailbox state information.</synopsis>
61 </configOption>
62 <configOption name="device_state" default="no">
63 <synopsis>Whether we should permit incoming device state events.</synopsis>
64 </configOption>
65 <configOption name="device_state_filter">
66 <synopsis>Optional regular expression used to filter what devices we accept events for.</synopsis>
67 </configOption>
68 <configOption name="mailbox_state" default="no">
69 <synopsis>Whether we should permit incoming mailbox state events.</synopsis>
70 </configOption>
71 <configOption name="mailbox_state_filter">
72 <synopsis>Optional regular expression used to filter what mailboxes we accept events for.</synopsis>
73 </configOption>
74 <configOption name="type">
75 <synopsis>Must be of type 'asterisk-publication'.</synopsis>
76 </configOption>
77 </configObject>
78 </configFile>
79 </configInfo>
80 ***/
81
82/*! \brief Structure which contains Asterisk device state publisher state information */
84 /*! \brief The publish client to send PUBLISH messages on */
86 /*! \brief Device state subscription */
88 /*! \brief Regex used for filtering outbound device state */
90 /*! \brief Device state should be filtered */
91 unsigned int device_state_filter;
92};
93
94/*! \brief Structure which contains Asterisk mailbox publisher state information */
96 /*! \brief The publish client to send PUBLISH messages on */
98 /*! \brief Mailbox state subscription */
100 /*! \brief Regex used for filtering outbound mailbox state */
102 /*! \brief Mailbox state should be filtered */
104};
105
106/*! \brief Structure which contains Asterisk publication information */
108 /*! \brief Sorcery object details */
110 /*! \brief Stringfields */
112 /*! \brief Optional name of a device state publish item, used to request the remote side update us */
114 /*! \brief Optional name of a mailbox state publish item, used to request the remote side update us */
116 );
117 /*! \brief Accept inbound device state events */
118 unsigned int device_state;
119 /*! \brief Regex used for filtering inbound device state */
121 /*! \brief Device state should be filtered */
123 /*! \brief Accept inbound mailbox state events */
124 unsigned int mailbox_state;
125 /*! \brief Regex used for filtering inbound mailbox state */
127 /*! \brief Mailbox state should be filtered */
129};
130
131/*! \brief Destroy callback for Asterisk devicestate publisher state information from datastore */
133{
134 struct asterisk_devicestate_publisher_state *publisher_state = obj;
135
136 ao2_cleanup(publisher_state->client);
137
138 if (publisher_state->device_state_filter) {
139 regfree(&publisher_state->device_state_regex);
140 }
141}
142
143/*! \brief Datastore for attaching devicestate publisher state information */
145 .type = "asterisk-devicestate-publisher",
147};
148
149/*! \brief Destroy callback for Asterisk mwi publisher state information from datastore */
151{
152 struct asterisk_mwi_publisher_state *publisher_state = obj;
153
154 ao2_cleanup(publisher_state->client);
155
156 if (publisher_state->mailbox_state_filter) {
157 regfree(&publisher_state->mailbox_state_regex);
158 }
159}
160
161/*! \brief Datastore for attaching devicestate publisher state information */
163 .type = "asterisk-mwi-publisher",
165};
166
167/*!
168 * \brief Callback function for device state events
169 * \param data void pointer to ast_client structure
170 * \param sub, msg
171 */
172static void asterisk_publisher_devstate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
173{
174 struct ast_datastore *datastore = data;
175 struct asterisk_devicestate_publisher_state *publisher_state = datastore->data;
176 struct ast_device_state_message *dev_state;
177 char eid_str[20];
178 struct ast_json *json;
179 char *text;
180 struct ast_sip_body body = {
181 .type = "application",
182 .subtype = "json",
183 };
184
186 return;
187 }
188
189 dev_state = stasis_message_data(msg);
190 if (!dev_state->eid || ast_eid_cmp(&ast_eid_default, dev_state->eid)) {
191 /* If the event is aggregate or didn't originate from this server, don't send it out. */
192 return;
193 }
194
195 if (publisher_state->device_state_filter && regexec(&publisher_state->device_state_regex, dev_state->device, 0, NULL, 0)) {
196 /* Outgoing device state has been filtered and the device name does not match */
197 return;
198 }
199
200 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
201 json = ast_json_pack(
202 "{ s: s, s: s, s: s, s: i, s:s }",
203 "type", "devicestate",
204 "device", dev_state->device,
205 "state", ast_devstate_str(dev_state->state),
206 "cachable", dev_state->cachable,
207 "eid", eid_str);
208 if (!json) {
209 return;
210 }
211
213 if (!text) {
214 ast_json_unref(json);
215 return;
216 }
217 body.body_text = text;
218
219 ast_sip_publish_client_send(publisher_state->client, &body);
220
222 ast_json_unref(json);
223}
224
225/*!
226 * \brief Callback function for mailbox state events
227 * \param data void pointer to ast_client structure
228 * \param sub, msg
229 */
230static void asterisk_publisher_mwistate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
231{
232 struct ast_datastore *datastore = data;
233 struct asterisk_mwi_publisher_state *publisher_state = datastore->data;
234 struct ast_mwi_state *mwi_state;
235 char eid_str[20];
236 struct ast_json *json;
237 char *text;
238 struct ast_sip_body body = {
239 .type = "application",
240 .subtype = "json",
241 };
242
244 return;
245 }
246
247 mwi_state = stasis_message_data(msg);
248 if (ast_eid_cmp(&ast_eid_default, &mwi_state->eid)) {
249 /* If the event is aggregate or didn't originate from this server, don't send it out. */
250 return;
251 }
252
253 if (publisher_state->mailbox_state_filter && regexec(&publisher_state->mailbox_state_regex, mwi_state->uniqueid, 0, NULL, 0)) {
254 /* Outgoing mailbox state has been filtered and the uniqueid does not match */
255 return;
256 }
257
258 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
259 json = ast_json_pack(
260 "{ s: s, s: s, s: i, s: i, s:s }",
261 "type", "mailboxstate",
262 "uniqueid", mwi_state->uniqueid,
263 "old", mwi_state->old_msgs,
264 "new", mwi_state->new_msgs,
265 "eid", eid_str);
266 if (!json) {
267 return;
268 }
269
271 if (!text) {
272 ast_json_unref(json);
273 return;
274 }
275 body.body_text = text;
276
277 ast_sip_publish_client_send(publisher_state->client, &body);
278
280 ast_json_unref(json);
281}
282
283static int cached_devstate_cb(void *obj, void *arg, int flags)
284{
285 struct stasis_message *msg = obj;
286 struct ast_datastore *datastore = arg;
287 struct asterisk_devicestate_publisher_state *publisher_state = datastore->data;
288
290
291 return 0;
292}
293
294static int cached_mwistate_cb(void *obj, void *arg, int flags)
295{
296 struct stasis_message *msg = obj;
297 struct ast_datastore *datastore = arg;
298 struct asterisk_mwi_publisher_state *publisher_state = datastore->data;
299
301
302 return 0;
303}
304
305static int build_regex(regex_t *regex, const char *text)
306{
307 int res;
308
309 if ((res = regcomp(regex, text, REG_EXTENDED | REG_ICASE | REG_NOSUB))) {
310 size_t len = regerror(res, regex, NULL, 0);
311 char buf[len];
312 regerror(res, regex, buf, len);
313 ast_log(LOG_ERROR, "Could not compile regex '%s': %s\n", text, buf);
314 return -1;
315 }
316
317 return 0;
318}
319
322{
323 RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
324 struct asterisk_devicestate_publisher_state *publisher_state;
325 const char *value;
326 struct ao2_container *cached;
327
329 "asterisk-devicestate-publisher");
330 if (!datastore) {
331 return -1;
332 }
333
334 publisher_state = ast_calloc(1, sizeof(struct asterisk_devicestate_publisher_state));
335 if (!publisher_state) {
336 return -1;
337 }
338 datastore->data = publisher_state;
339
340 value = ast_sorcery_object_get_extended(configuration, "device_state_filter");
341 if (!ast_strlen_zero(value)) {
342 if (build_regex(&publisher_state->device_state_regex, value)) {
343 return -1;
344 }
345 publisher_state->device_state_filter = 1;
346 }
347
348 publisher_state->client = ao2_bump(client);
349
350 if (ast_sip_publish_client_add_datastore(client, datastore)) {
351 return -1;
352 }
353
356 if (!publisher_state->device_state_subscription) {
357 ast_sip_publish_client_remove_datastore(client, "asterisk-devicestate-publisher");
358 ao2_ref(datastore, -1);
359 return -1;
360 }
364
366 ao2_callback(cached, OBJ_NODATA, cached_devstate_cb, datastore);
367 ao2_ref(cached, -1);
368
369 return 0;
370}
371
373{
374 RAII_VAR(struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client, "asterisk-devicestate-publisher"),
376 struct asterisk_devicestate_publisher_state *publisher_state;
377
378 if (!datastore) {
379 return 0;
380 }
381
382 publisher_state = datastore->data;
383 if (publisher_state->device_state_subscription) {
385 ao2_ref(datastore, -1);
386 }
387
388 ast_sip_publish_client_remove_datastore(client, "asterisk-devicestate-publisher");
389
390 return 0;
391}
392
394 .event_name = "asterisk-devicestate",
395 .start_publishing = asterisk_start_devicestate_publishing,
396 .stop_publishing = asterisk_stop_devicestate_publishing,
397};
398
400 struct ast_sip_outbound_publish_client *client)
401{
402 RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
403 struct asterisk_mwi_publisher_state *publisher_state;
404 const char *value;
405 struct ao2_container *cached;
406
408 if (!datastore) {
409 return -1;
410 }
411
412 publisher_state = ast_calloc(1, sizeof(struct asterisk_mwi_publisher_state));
413 if (!publisher_state) {
414 return -1;
415 }
416 datastore->data = publisher_state;
417
418 value = ast_sorcery_object_get_extended(configuration, "mailbox_state_filter");
419 if (!ast_strlen_zero(value)) {
420 if (build_regex(&publisher_state->mailbox_state_regex, value)) {
421 return -1;
422 }
423 publisher_state->mailbox_state_filter = 1;
424 }
425
426 publisher_state->client = ao2_bump(client);
427
428 if (ast_sip_publish_client_add_datastore(client, datastore)) {
429 return -1;
430 }
431
434 if (!publisher_state->mailbox_state_subscription) {
435 ast_sip_publish_client_remove_datastore(client, "asterisk-mwi-publisher");
436 ao2_ref(datastore, -1);
437 return -1;
438 }
442
444 ao2_callback(cached, OBJ_NODATA, cached_mwistate_cb, datastore);
445 ao2_ref(cached, -1);
446
447 return 0;
448}
449
451{
452 RAII_VAR(struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client, "asterisk-mwi-publisher"),
454 struct asterisk_mwi_publisher_state *publisher_state;
455
456 if (!datastore) {
457 return 0;
458 }
459
460 publisher_state = datastore->data;
461 if (publisher_state->mailbox_state_subscription) {
463 ao2_ref(datastore, -1);
464 }
465
466 ast_sip_publish_client_remove_datastore(client, "asterisk-mwi-publisher");
467
468 return 0;
469}
470
472 .event_name = "asterisk-mwi",
473 .start_publishing = asterisk_start_mwi_publishing,
474 .stop_publishing = asterisk_stop_mwi_publishing,
475};
476
477static int asterisk_publication_new(struct ast_sip_endpoint *endpoint, const char *resource, const char *event_configuration)
478{
480 event_configuration), ao2_cleanup);
481
482 /* If no inbound Asterisk publication configuration exists reject the PUBLISH */
483 if (!config) {
484 return 404;
485 }
486
487 return 200;
488}
489
491 struct ast_eid *pubsub_eid, struct ast_json *json)
492{
493 const char *device = ast_json_string_get(ast_json_object_get(json, "device"));
494 const char *state = ast_json_string_get(ast_json_object_get(json, "state"));
495 int cachable = ast_json_integer_get(ast_json_object_get(json, "cachable"));
496
497 if (!config->device_state) {
498 ast_debug(2, "Received device state event for resource '%s' but it is not configured to accept them\n",
500 return 0;
501 }
502
503 if (ast_strlen_zero(device) || ast_strlen_zero(state)) {
504 ast_debug(1, "Received incomplete device state event for resource '%s'\n",
506 return -1;
507 }
508
509 if (config->device_state_filter && regexec(&config->device_state_regex, device, 0, NULL, 0)) {
510 ast_debug(2, "Received device state on resource '%s' for device '%s' but it has been filtered out\n",
512 return 0;
513 }
514
517 pubsub_eid);
518
519 return 0;
520}
521
523 struct ast_eid *pubsub_eid, struct ast_json *json)
524{
525 const char *uniqueid = ast_json_string_get(ast_json_object_get(json, "uniqueid"));
526 int old_msgs = ast_json_integer_get(ast_json_object_get(json, "old"));
527 int new_msgs = ast_json_integer_get(ast_json_object_get(json, "new"));
528 char *item_id;
529 const char *mailbox;
530
531 if (!config->mailbox_state) {
532 ast_debug(2, "Received mailbox state event for resource '%s' but it is not configured to accept them\n",
534 return 0;
535 }
536
537 if (ast_strlen_zero(uniqueid)) {
538 ast_debug(1, "Received incomplete mailbox state event for resource '%s'\n",
540 return -1;
541 }
542
543 if (config->mailbox_state_filter && regexec(&config->mailbox_state_regex, uniqueid, 0, NULL, 0)) {
544 ast_debug(2, "Received mailbox state on resource '%s' for uniqueid '%s' but it has been filtered out\n",
546 return 0;
547 }
548
549 item_id = ast_strdupa(uniqueid);
550 mailbox = strsep(&item_id, "@");
551
552 ast_publish_mwi_state_full(mailbox, item_id, new_msgs, old_msgs, NULL, pubsub_eid);
553
554 return 0;
555}
556
558 struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
559{
560 struct ast_sip_outbound_publish_client *client;
561 struct ast_datastore *datastore;
562 struct ao2_container *cached;
563
564 if (ast_strlen_zero(config->devicestate_publish)) {
565 return 0;
566 }
567
568 client = ast_sip_publish_client_get(config->devicestate_publish);
569 if (!client) {
570 ast_log(LOG_ERROR, "Received refresh request for devicestate on publication '%s' but publish '%s' is not available\n",
571 ast_sorcery_object_get_id(config), config->devicestate_publish);
572 return 0;
573 }
574
575 datastore = ast_sip_publish_client_get_datastore(client, "asterisk-devicestate-publisher");
576 if (!datastore) {
577 ao2_ref(client, -1);
578 return 0;
579 }
580
582 if (cached) {
583 ao2_callback(cached, OBJ_NODATA, cached_devstate_cb, datastore);
584 ao2_ref(cached, -1);
585 }
586 ao2_ref(client, -1);
587 ao2_ref(datastore, -1);
588
589 return 0;
590}
591
592static int asterisk_publication_devicestate_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body,
594{
597 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
598 const char *eid, *type;
599 struct ast_eid pubsub_eid;
600 int res = -1;
601
602 /* If no configuration exists for this publication it has most likely been removed, so drop this immediately */
603 if (!config) {
604 return -1;
605 }
606
607 /* If no body exists this is a refresh and can be ignored */
608 if (!body) {
609 return 0;
610 }
611
612 /* We only accept JSON for content */
613 if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
614 ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
616 return -1;
617 }
618
619 json = ast_json_load_buf(body->data, body->len, NULL);
620 if (!json) {
621 ast_debug(1, "Received unparseable JSON event for resource '%s'\n",
623 return -1;
624 }
625
627 if (!eid) {
628 ast_debug(1, "Received event without eid for resource '%s'\n",
630 return -1;
631 }
632 ast_str_to_eid(&pubsub_eid, eid);
633
635 if (!type) {
636 ast_debug(1, "Received event without type for resource '%s'\n",
638 return -1;
639 } else if (!strcmp(type, "devicestate")) {
640 res = asterisk_publication_devicestate(pub, config, &pubsub_eid, json);
641 } else if (!strcmp(type, "refresh")) {
642 res = asterisk_publication_devicestate_refresh(pub, config, &pubsub_eid, json);
643 }
644
645 return res;
646}
647
649 struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
650{
651 struct ast_sip_outbound_publish_client *client;
652 struct ast_datastore *datastore;
653 struct ao2_container *cached;
654
655 if (ast_strlen_zero(config->mailboxstate_publish)) {
656 return 0;
657 }
658
659 client = ast_sip_publish_client_get(config->mailboxstate_publish);
660 if (!client) {
661 ast_log(LOG_ERROR, "Received refresh request for mwi state on publication '%s' but publish '%s' is not available\n",
662 ast_sorcery_object_get_id(config), config->mailboxstate_publish);
663 return 0;
664 }
665
666 datastore = ast_sip_publish_client_get_datastore(client, "asterisk-mwi-publisher");
667 if (!datastore) {
668 ao2_ref(client, -1);
669 return 0;
670 }
671
673 if (cached) {
674 ao2_callback(cached, OBJ_NODATA, cached_mwistate_cb, datastore);
675 ao2_ref(cached, -1);
676 }
677 ao2_ref(client, -1);
678 ao2_ref(datastore, -1);
679
680 return 0;
681}
682
683static int asterisk_publication_mwi_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body,
685{
688 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
689 const char *eid, *type;
690 struct ast_eid pubsub_eid;
691 int res = -1;
692
693 /* If no configuration exists for this publication it has most likely been removed, so drop this immediately */
694 if (!config) {
695 return -1;
696 }
697
698 /* If no body exists this is a refresh and can be ignored */
699 if (!body) {
700 return 0;
701 }
702
703 /* We only accept JSON for content */
704 if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
705 ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
707 return -1;
708 }
709
710 json = ast_json_load_buf(body->data, body->len, NULL);
711 if (!json) {
712 ast_debug(1, "Received unparseable JSON event for resource '%s'\n",
714 return -1;
715 }
716
718 if (!eid) {
719 ast_debug(1, "Received event without eid for resource '%s'\n",
721 return -1;
722 }
723 ast_str_to_eid(&pubsub_eid, eid);
724
726 if (!type) {
727 ast_debug(1, "Received event without type for resource '%s'\n",
729 return -1;
730 } else if (!strcmp(type, "mailboxstate")) {
731 res = asterisk_publication_mailboxstate(pub, config, &pubsub_eid, json);
732 } else if (!strcmp(type, "refresh")) {
733 res = asterisk_publication_mwi_refresh(pub, config, &pubsub_eid, json);
734 }
735
736 return res;
737}
738
739static int send_refresh_cb(void *obj, void *arg, int flags)
740{
742 struct ast_sip_outbound_publish_client *client;
743
744 if (!ast_strlen_zero(config->devicestate_publish)) {
745 client = ast_sip_publish_client_get(config->devicestate_publish);
746 if (client) {
747 ast_sip_publish_client_send(client, arg);
748 ao2_ref(client, -1);
749 }
750 }
751
752 if (!ast_strlen_zero(config->mailboxstate_publish)) {
753 client = ast_sip_publish_client_get(config->mailboxstate_publish);
754 if (client) {
755 ast_sip_publish_client_send(client, arg);
756 ao2_ref(client, -1);
757 }
758 }
759
760 return 0;
761}
762
763/*! \brief Internal function to send refresh requests to all publications */
765{
767 char eid_str[20];
768 struct ast_json *json;
769 char *text;
770 struct ast_sip_body body = {
771 .type = "application",
772 .subtype = "json",
773 };
774
775 if (!publications) {
776 return;
777 }
778
779 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
780 json = ast_json_pack(
781 "{ s: s, s: s }",
782 "type", "refresh",
783 "eid", eid_str);
784 if (!json) {
785 ao2_ref(publications, -1);
786 return;
787 }
788
790 if (!text) {
791 ast_json_unref(json);
792 ao2_ref(publications, -1);
793 return;
794 }
795 body.body_text = text;
796
797 ao2_callback(publications, OBJ_NODATA, send_refresh_cb, &body);
798
800 ast_json_unref(json);
801 ao2_ref(publications, -1);
802}
803
805 .event_name = "asterisk-devicestate",
806 .new_publication = asterisk_publication_new,
807 .publication_state_change = asterisk_publication_devicestate_state_change,
808};
809
811 .event_name = "asterisk-mwi",
812 .new_publication = asterisk_publication_new,
813 .publication_state_change = asterisk_publication_mwi_state_change,
814};
815
816/*! \brief Destructor function for Asterisk publication configuration */
818{
820
822}
823
824/*! \brief Allocator function for Asterisk publication configuration */
826{
829
830 if (!config || ast_string_field_init(config, 256)) {
832 return NULL;
833 }
834
835 return config;
836}
837
838static int regex_filter_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
839{
841 int res = -1;
842
843 if (ast_strlen_zero(var->value)) {
844 return 0;
845 }
846
847 if (!strcmp(var->name, "device_state_filter")) {
848 if (!(res = build_regex(&config->device_state_regex, var->value))) {
849 config->device_state_filter = 1;
850 }
851 } else if (!strcmp(var->name, "mailbox_state_filter")) {
852 if (!(res = build_regex(&config->mailbox_state_regex, var->value))) {
853 config->mailbox_state_filter = 1;
854 }
855 }
856
857 return res;
858}
859
860static int load_module(void)
861{
863 ast_log(LOG_ERROR, "Entity ID is not set.\n");
865 }
866
867 ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_publish_asterisk");
868 ast_sorcery_apply_default(ast_sip_get_sorcery(), "asterisk-publication", "config", "pjsip.conf,criteria=type=asterisk-publication");
869
871 ast_log(LOG_ERROR, "Unable to register 'asterisk-publication' type with sorcery\n");
873 }
874
875 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "type", "", OPT_NOOP_T, 0, 0);
878 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "device_state", "no", OPT_BOOL_T, 1, FLDSET(struct asterisk_publication_config, device_state));
879 ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "asterisk-publication", "device_state_filter", "", regex_filter_handler, NULL, NULL, 0, 0);
880 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "mailbox_state", "no", OPT_BOOL_T, 1, FLDSET(struct asterisk_publication_config, mailbox_state));
881 ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "asterisk-publication", "mailbox_state_filter", "", regex_filter_handler, NULL, NULL, 0, 0);
882 ast_sorcery_reload_object(ast_sip_get_sorcery(), "asterisk-publication");
883
885 ast_log(LOG_WARNING, "Unable to register event publication handler %s\n",
888 }
890 ast_log(LOG_WARNING, "Unable to register event publication handler %s\n",
894 }
896 ast_log(LOG_WARNING, "Unable to register event publisher handler %s\n",
901 }
903 ast_log(LOG_WARNING, "Unable to register event publisher handler %s\n",
909 }
910
912
914}
915
916static int reload_module(void)
917{
918 ast_sorcery_reload_object(ast_sip_get_sorcery(), "asterisk-publication");
920 return 0;
921}
922
923static int unload_module(void)
924{
929 ast_sorcery_object_unregister(ast_sip_get_sorcery(), "asterisk-publication");
930 return 0;
931}
932
933AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Asterisk Event PUBLISH Support",
934 .support_level = AST_MODULE_SUPPORT_CORE,
935 .load = load_module,
937 .unload = unload_module,
938 .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5,
939 .requires = "res_pjsip,res_pjsip_outbound_publish,res_pjsip_pubsub",
char * text
Definition: app_queue.c:1668
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
#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
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#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
static const char type[]
Definition: chan_ooh323.c:109
static const char config[]
Definition: chan_ooh323.c:111
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
@ 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_STRINGFIELD_T
Type for default option handler for stringfields.
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
@ AST_DEVSTATE_CACHABLE
Definition: devicestate.h:70
@ AST_DEVSTATE_NOT_CACHABLE
Definition: devicestate.h:69
const char * ast_devstate_str(enum ast_device_state devstate) attribute_pure
Convert device state to text string that is easier to parse.
Definition: devicestate.c:255
struct stasis_cache * ast_device_state_cache(void)
Backend cache for ast_device_state_topic_cached()
Definition: devicestate.c:673
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
Definition: devicestate.c:668
enum ast_device_state ast_devstate_val(const char *val)
Convert device state from text to integer value.
Definition: devicestate.c:260
int ast_publish_device_state_full(const char *device, enum ast_device_state state, enum ast_devstate_cache cachable, struct ast_eid *eid)
Publish a device state update with EID.
Definition: devicestate.c:709
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)
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
char * strsep(char **str, const char *delims)
Support for logging to various files, console and syslog Configuration in file logger....
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#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
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:810
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
struct ast_json * ast_json_load_buf(const char *buffer, size_t buflen, struct ast_json_error *error)
Parse buffer with known length into a JSON object or array.
Definition: json.c:585
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
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition: json.c:332
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ 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.
struct stasis_message_type * ast_mwi_state_type(void)
Get the Stasis Message Bus API message type for MWI messages.
struct stasis_cache * ast_mwi_state_cache(void)
Backend cache for ast_mwi_topic_cached().
Definition: mwi.c:94
int ast_publish_mwi_state_full(const char *mailbox, const char *context, int new_msgs, int old_msgs, const char *channel_id, struct ast_eid *eid)
Publish a MWI state update via stasis with all parameters.
Definition: mwi.c:393
struct stasis_topic * ast_mwi_topic_all(void)
Get the Stasis Message Bus API topic for MWI messages.
Definition: mwi.c:89
static int reload(void)
struct stasis_forward * sub
Definition: res_corosync.c:240
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
int ast_sip_publish_client_add_datastore(struct ast_sip_outbound_publish_client *client, struct ast_datastore *datastore)
Add a datastore to a SIP event publisher.
void ast_sip_publish_client_remove_datastore(struct ast_sip_outbound_publish_client *client, const char *name)
Remove a publication datastore from an event publisher.
void ast_sip_unregister_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
Unregister a publish handler.
struct ast_datastore * ast_sip_publish_client_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
int ast_sip_publish_client_send(struct ast_sip_outbound_publish_client *client, const struct ast_sip_body *body)
Send an outgoing PUBLISH message using a client.
int ast_sip_register_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
Register an event publisher handler.
struct ast_sip_outbound_publish_client * ast_sip_publish_client_get(const char *name)
Find a publish client using its name.
struct ast_datastore * ast_sip_publish_client_get_datastore(struct ast_sip_outbound_publish_client *client, const char *name)
Retrieve an event publisher datastore.
struct ast_sip_publish_handler asterisk_devicestate_publication_handler
struct ast_sip_event_publisher_handler asterisk_mwi_publisher_handler
static void asterisk_publication_config_destroy(void *obj)
Destructor function for Asterisk publication configuration.
static void asterisk_devicestate_publisher_state_destroy(void *obj)
Destroy callback for Asterisk devicestate publisher state information from datastore.
static int asterisk_publication_mailboxstate(struct ast_sip_publication *pub, struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
static int asterisk_stop_devicestate_publishing(struct ast_sip_outbound_publish_client *client)
static int asterisk_start_mwi_publishing(struct ast_sip_outbound_publish *configuration, struct ast_sip_outbound_publish_client *client)
static void asterisk_publisher_mwistate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
Callback function for mailbox state events.
static int asterisk_publication_devicestate_refresh(struct ast_sip_publication *pub, struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
static int asterisk_stop_mwi_publishing(struct ast_sip_outbound_publish_client *client)
static void asterisk_publisher_devstate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
Callback function for device state events.
static void asterisk_publication_send_refresh(void)
Internal function to send refresh requests to all publications.
static void * asterisk_publication_config_alloc(const char *name)
Allocator function for Asterisk publication configuration.
static int regex_filter_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static int reload_module(void)
static int asterisk_start_devicestate_publishing(struct ast_sip_outbound_publish *configuration, struct ast_sip_outbound_publish_client *client)
static int asterisk_publication_devicestate(struct ast_sip_publication *pub, struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
static int cached_devstate_cb(void *obj, void *arg, int flags)
static int cached_mwistate_cb(void *obj, void *arg, int flags)
static int build_regex(regex_t *regex, const char *text)
struct ast_sip_event_publisher_handler asterisk_devicestate_publisher_handler
static int load_module(void)
static int asterisk_publication_devicestate_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body, enum ast_sip_publish_state state)
static int unload_module(void)
static int asterisk_publication_new(struct ast_sip_endpoint *endpoint, const char *resource, const char *event_configuration)
static int asterisk_publication_mwi_refresh(struct ast_sip_publication *pub, struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
static const struct ast_datastore_info asterisk_devicestate_publisher_state_datastore
Datastore for attaching devicestate publisher state information.
static const struct ast_datastore_info asterisk_mwi_publisher_state_datastore
Datastore for attaching devicestate publisher state information.
static int send_refresh_cb(void *obj, void *arg, int flags)
struct ast_sip_publish_handler asterisk_mwi_publication_handler
static void asterisk_mwi_publisher_state_destroy(void *obj)
Destroy callback for Asterisk mwi publisher state information from datastore.
static int asterisk_publication_mwi_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body, enum ast_sip_publish_state state)
ast_sip_publish_state
int ast_sip_register_publish_handler(struct ast_sip_publish_handler *handler)
Register a publish handler.
void ast_sip_unregister_publish_handler(struct ast_sip_publish_handler *handler)
Unregister a publish handler.
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.
#define NULL
Definition: resample.c:96
const char * ast_sorcery_object_get_extended(const void *object, const char *name)
Get an extended field value from a sorcery object.
Definition: sorcery.c:2335
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
@ 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
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
int ast_sorcery_object_unregister(struct ast_sorcery *sorcery, const char *type)
Unregister an object type.
Definition: sorcery.c:1061
#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
#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
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
struct ao2_container * stasis_cache_dump(struct stasis_cache *cache, struct stasis_message_type *type)
Dump cached items to a subscription for the ast_eid_default entity.
Definition: stasis_cache.c:736
@ 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:1024
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:1078
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition: stasis.c:1135
int stasis_subscription_is_subscribed(const struct stasis_subscription *sub)
Returns whether a subscription is currently subscribed.
Definition: stasis.c:1151
#define stasis_subscribe(topic, callback, data)
Definition: stasis.h:649
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Generic container type.
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
The structure that contains device state.
Definition: devicestate.h:238
enum ast_device_state state
Definition: devicestate.h:248
const struct ast_eid * eid
The EID of the server where this message originated.
Definition: devicestate.h:246
enum ast_devstate_cache cachable
Definition: devicestate.h:250
An Entity ID is essentially a MAC address, brief and unique.
Definition: utils.h:813
unsigned char eid[6]
Definition: utils.h:814
Abstract JSON element (object, array, string, int, ...).
The structure that contains MWI state.
Definition: mwi.h:455
int old_msgs
Definition: mwi.h:460
int new_msgs
Definition: mwi.h:459
const ast_string_field uniqueid
Definition: mwi.h:458
struct ast_eid eid
Definition: mwi.h:463
SIP body description.
Definition: res_pjsip.h:2324
const char * type
Definition: res_pjsip.h:2326
const char * body_text
Definition: res_pjsip.h:2330
An entity with which Asterisk communicates.
Definition: res_pjsip.h:958
Callbacks that event publisher handlers will define.
const char * event_name
The name of the event this handler deals with.
Outbound publish client state information (persists for lifetime of a publish)
Outbound publish information.
Structure representing a SIP publication.
Callbacks that publication handlers will define.
const char * event_name
The name of the event this handler deals with.
Structure for variables, used for configurations and for channel variables.
Structure which contains Asterisk device state publisher state information.
regex_t device_state_regex
Regex used for filtering outbound device state.
struct stasis_subscription * device_state_subscription
Device state subscription.
struct ast_sip_outbound_publish_client * client
The publish client to send PUBLISH messages on.
unsigned int device_state_filter
Device state should be filtered.
Structure which contains Asterisk mailbox publisher state information.
struct stasis_subscription * mailbox_state_subscription
Mailbox state subscription.
regex_t mailbox_state_regex
Regex used for filtering outbound mailbox state.
struct ast_sip_outbound_publish_client * client
The publish client to send PUBLISH messages on.
unsigned int mailbox_state_filter
Mailbox state should be filtered.
Structure which contains Asterisk publication information.
SORCERY_OBJECT(details)
Sorcery object details.
regex_t device_state_regex
Regex used for filtering inbound device state.
regex_t mailbox_state_regex
Regex used for filtering inbound mailbox state.
const ast_string_field mailboxstate_publish
unsigned int mailbox_state_filter
Mailbox state should be filtered.
unsigned int mailbox_state
Accept inbound mailbox state events.
unsigned int device_state
Accept inbound device state events.
const ast_string_field devicestate_publish
unsigned int device_state_filter
Device state should be filtered.
int value
Definition: syslog.c:37
#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
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
Definition: utils.c:3094
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition: utils.c:2839
int ast_eid_is_empty(const struct ast_eid *eid)
Check if EID is empty.
Definition: utils.c:3099
struct ast_eid ast_eid_default
Global EID.
Definition: options.c:93
int ast_str_to_eid(struct ast_eid *eid, const char *s)
Convert a string into an EID.
Definition: utils.c:3077