Asterisk - The Open Source Telephony Project GIT-master-66c01d8
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 <since>
52 <version>13.0.0</version>
53 </since>
54 <synopsis>The configuration for inbound Asterisk event publication</synopsis>
55 <description><para>
56 Publish is <emphasis>COMPLETELY</emphasis> separate from the rest of
57 <literal>pjsip.conf</literal>.
58 </para></description>
59 <configOption name="devicestate_publish">
60 <since>
61 <version>13.0.0</version>
62 </since>
63 <synopsis>Optional name of a publish item that can be used to publish a request for full device state information.</synopsis>
64 </configOption>
65 <configOption name="mailboxstate_publish">
66 <since>
67 <version>13.0.0</version>
68 </since>
69 <synopsis>Optional name of a publish item that can be used to publish a request for full mailbox state information.</synopsis>
70 </configOption>
71 <configOption name="device_state" default="no">
72 <since>
73 <version>13.0.0</version>
74 </since>
75 <synopsis>Whether we should permit incoming device state events.</synopsis>
76 </configOption>
77 <configOption name="device_state_filter">
78 <since>
79 <version>13.0.0</version>
80 </since>
81 <synopsis>Optional regular expression used to filter what devices we accept events for.</synopsis>
82 </configOption>
83 <configOption name="mailbox_state" default="no">
84 <since>
85 <version>13.0.0</version>
86 </since>
87 <synopsis>Whether we should permit incoming mailbox state events.</synopsis>
88 </configOption>
89 <configOption name="mailbox_state_filter">
90 <since>
91 <version>13.0.0</version>
92 </since>
93 <synopsis>Optional regular expression used to filter what mailboxes we accept events for.</synopsis>
94 </configOption>
95 <configOption name="type">
96 <since>
97 <version>13.0.0</version>
98 </since>
99 <synopsis>Must be of type 'asterisk-publication'.</synopsis>
100 </configOption>
101 </configObject>
102 </configFile>
103 </configInfo>
104 ***/
105
106/*! \brief Structure which contains Asterisk device state publisher state information */
108 /*! \brief The publish client to send PUBLISH messages on */
110 /*! \brief Device state subscription */
112 /*! \brief Regex used for filtering outbound device state */
114 /*! \brief Device state should be filtered */
116};
117
118/*! \brief Structure which contains Asterisk mailbox publisher state information */
120 /*! \brief The publish client to send PUBLISH messages on */
122 /*! \brief Mailbox state subscription */
124 /*! \brief Regex used for filtering outbound mailbox state */
126 /*! \brief Mailbox state should be filtered */
128};
129
130/*! \brief Structure which contains Asterisk publication information */
132 /*! \brief Sorcery object details */
134 /*! \brief Stringfields */
136 /*! \brief Optional name of a device state publish item, used to request the remote side update us */
138 /*! \brief Optional name of a mailbox state publish item, used to request the remote side update us */
140 );
141 /*! \brief Accept inbound device state events */
142 unsigned int device_state;
143 /*! \brief Regex used for filtering inbound device state */
145 /*! \brief Device state should be filtered */
147 /*! \brief Accept inbound mailbox state events */
148 unsigned int mailbox_state;
149 /*! \brief Regex used for filtering inbound mailbox state */
151 /*! \brief Mailbox state should be filtered */
153};
154
155/*! \brief Destroy callback for Asterisk devicestate publisher state information from datastore */
157{
158 struct asterisk_devicestate_publisher_state *publisher_state = obj;
159
160 ao2_cleanup(publisher_state->client);
161
162 if (publisher_state->device_state_filter) {
163 regfree(&publisher_state->device_state_regex);
164 }
165}
166
167/*! \brief Datastore for attaching devicestate publisher state information */
169 .type = "asterisk-devicestate-publisher",
171};
172
173/*! \brief Destroy callback for Asterisk mwi publisher state information from datastore */
175{
176 struct asterisk_mwi_publisher_state *publisher_state = obj;
177
178 ao2_cleanup(publisher_state->client);
179
180 if (publisher_state->mailbox_state_filter) {
181 regfree(&publisher_state->mailbox_state_regex);
182 }
183}
184
185/*! \brief Datastore for attaching devicestate publisher state information */
187 .type = "asterisk-mwi-publisher",
189};
190
191/*!
192 * \brief Callback function for device state events
193 * \param data void pointer to ast_client structure
194 * \param sub, msg
195 */
196static void asterisk_publisher_devstate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
197{
198 struct ast_datastore *datastore = data;
199 struct asterisk_devicestate_publisher_state *publisher_state = datastore->data;
200 struct ast_device_state_message *dev_state;
201 char eid_str[20];
202 struct ast_json *json;
203 char *text;
204 struct ast_sip_body body = {
205 .type = "application",
206 .subtype = "json",
207 };
208
210 return;
211 }
212
213 dev_state = stasis_message_data(msg);
214 if (!dev_state->eid || ast_eid_cmp(&ast_eid_default, dev_state->eid)) {
215 /* If the event is aggregate or didn't originate from this server, don't send it out. */
216 return;
217 }
218
219 if (publisher_state->device_state_filter && regexec(&publisher_state->device_state_regex, dev_state->device, 0, NULL, 0)) {
220 /* Outgoing device state has been filtered and the device name does not match */
221 return;
222 }
223
224 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
225 json = ast_json_pack(
226 "{ s: s, s: s, s: s, s: i, s:s }",
227 "type", "devicestate",
228 "device", dev_state->device,
229 "state", ast_devstate_str(dev_state->state),
230 "cachable", dev_state->cachable,
231 "eid", eid_str);
232 if (!json) {
233 return;
234 }
235
237 if (!text) {
238 ast_json_unref(json);
239 return;
240 }
241 body.body_text = text;
242
243 ast_sip_publish_client_send(publisher_state->client, &body);
244
246 ast_json_unref(json);
247}
248
249/*!
250 * \brief Callback function for mailbox state events
251 * \param data void pointer to ast_client structure
252 * \param sub, msg
253 */
254static void asterisk_publisher_mwistate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
255{
256 struct ast_datastore *datastore = data;
257 struct asterisk_mwi_publisher_state *publisher_state = datastore->data;
258 struct ast_mwi_state *mwi_state;
259 char eid_str[20];
260 struct ast_json *json;
261 char *text;
262 struct ast_sip_body body = {
263 .type = "application",
264 .subtype = "json",
265 };
266
268 return;
269 }
270
271 mwi_state = stasis_message_data(msg);
272 if (ast_eid_cmp(&ast_eid_default, &mwi_state->eid)) {
273 /* If the event is aggregate or didn't originate from this server, don't send it out. */
274 return;
275 }
276
277 if (publisher_state->mailbox_state_filter && regexec(&publisher_state->mailbox_state_regex, mwi_state->uniqueid, 0, NULL, 0)) {
278 /* Outgoing mailbox state has been filtered and the uniqueid does not match */
279 return;
280 }
281
282 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
283 json = ast_json_pack(
284 "{ s: s, s: s, s: i, s: i, s:s }",
285 "type", "mailboxstate",
286 "uniqueid", mwi_state->uniqueid,
287 "old", mwi_state->old_msgs,
288 "new", mwi_state->new_msgs,
289 "eid", eid_str);
290 if (!json) {
291 return;
292 }
293
295 if (!text) {
296 ast_json_unref(json);
297 return;
298 }
299 body.body_text = text;
300
301 ast_sip_publish_client_send(publisher_state->client, &body);
302
304 ast_json_unref(json);
305}
306
307static int cached_devstate_cb(void *obj, void *arg, int flags)
308{
309 struct stasis_message *msg = obj;
310 struct ast_datastore *datastore = arg;
311 struct asterisk_devicestate_publisher_state *publisher_state = datastore->data;
312
314
315 return 0;
316}
317
318static int cached_mwistate_cb(void *obj, void *arg, int flags)
319{
320 struct stasis_message *msg = obj;
321 struct ast_datastore *datastore = arg;
322 struct asterisk_mwi_publisher_state *publisher_state = datastore->data;
323
325
326 return 0;
327}
328
329static int build_regex(regex_t *regex, const char *text)
330{
331 int res;
332
333 if ((res = regcomp(regex, text, REG_EXTENDED | REG_ICASE | REG_NOSUB))) {
334 size_t len = regerror(res, regex, NULL, 0);
335 char buf[len];
336 regerror(res, regex, buf, len);
337 ast_log(LOG_ERROR, "Could not compile regex '%s': %s\n", text, buf);
338 return -1;
339 }
340
341 return 0;
342}
343
346{
347 RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
348 struct asterisk_devicestate_publisher_state *publisher_state;
349 const char *value;
350 struct ao2_container *cached;
351
353 "asterisk-devicestate-publisher");
354 if (!datastore) {
355 return -1;
356 }
357
358 publisher_state = ast_calloc(1, sizeof(struct asterisk_devicestate_publisher_state));
359 if (!publisher_state) {
360 return -1;
361 }
362 datastore->data = publisher_state;
363
364 value = ast_sorcery_object_get_extended(configuration, "device_state_filter");
365 if (!ast_strlen_zero(value)) {
366 if (build_regex(&publisher_state->device_state_regex, value)) {
367 return -1;
368 }
369 publisher_state->device_state_filter = 1;
370 }
371
372 publisher_state->client = ao2_bump(client);
373
374 if (ast_sip_publish_client_add_datastore(client, datastore)) {
375 return -1;
376 }
377
380 if (!publisher_state->device_state_subscription) {
381 ast_sip_publish_client_remove_datastore(client, "asterisk-devicestate-publisher");
382 ao2_ref(datastore, -1);
383 return -1;
384 }
388
390 ao2_callback(cached, OBJ_NODATA, cached_devstate_cb, datastore);
391 ao2_ref(cached, -1);
392
393 return 0;
394}
395
397{
398 RAII_VAR(struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client, "asterisk-devicestate-publisher"),
400 struct asterisk_devicestate_publisher_state *publisher_state;
401
402 if (!datastore) {
403 return 0;
404 }
405
406 publisher_state = datastore->data;
407 if (publisher_state->device_state_subscription) {
409 ao2_ref(datastore, -1);
410 }
411
412 ast_sip_publish_client_remove_datastore(client, "asterisk-devicestate-publisher");
413
414 return 0;
415}
416
418 .event_name = "asterisk-devicestate",
419 .start_publishing = asterisk_start_devicestate_publishing,
420 .stop_publishing = asterisk_stop_devicestate_publishing,
421};
422
424 struct ast_sip_outbound_publish_client *client)
425{
426 RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
427 struct asterisk_mwi_publisher_state *publisher_state;
428 const char *value;
429 struct ao2_container *cached;
430
432 if (!datastore) {
433 return -1;
434 }
435
436 publisher_state = ast_calloc(1, sizeof(struct asterisk_mwi_publisher_state));
437 if (!publisher_state) {
438 return -1;
439 }
440 datastore->data = publisher_state;
441
442 value = ast_sorcery_object_get_extended(configuration, "mailbox_state_filter");
443 if (!ast_strlen_zero(value)) {
444 if (build_regex(&publisher_state->mailbox_state_regex, value)) {
445 return -1;
446 }
447 publisher_state->mailbox_state_filter = 1;
448 }
449
450 publisher_state->client = ao2_bump(client);
451
452 if (ast_sip_publish_client_add_datastore(client, datastore)) {
453 return -1;
454 }
455
458 if (!publisher_state->mailbox_state_subscription) {
459 ast_sip_publish_client_remove_datastore(client, "asterisk-mwi-publisher");
460 ao2_ref(datastore, -1);
461 return -1;
462 }
466
468 ao2_callback(cached, OBJ_NODATA, cached_mwistate_cb, datastore);
469 ao2_ref(cached, -1);
470
471 return 0;
472}
473
475{
476 RAII_VAR(struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client, "asterisk-mwi-publisher"),
478 struct asterisk_mwi_publisher_state *publisher_state;
479
480 if (!datastore) {
481 return 0;
482 }
483
484 publisher_state = datastore->data;
485 if (publisher_state->mailbox_state_subscription) {
487 ao2_ref(datastore, -1);
488 }
489
490 ast_sip_publish_client_remove_datastore(client, "asterisk-mwi-publisher");
491
492 return 0;
493}
494
496 .event_name = "asterisk-mwi",
497 .start_publishing = asterisk_start_mwi_publishing,
498 .stop_publishing = asterisk_stop_mwi_publishing,
499};
500
501static int asterisk_publication_new(struct ast_sip_endpoint *endpoint, const char *resource, const char *event_configuration)
502{
504 event_configuration), ao2_cleanup);
505
506 /* If no inbound Asterisk publication configuration exists reject the PUBLISH */
507 if (!config) {
508 return 404;
509 }
510
511 return 200;
512}
513
515 struct ast_eid *pubsub_eid, struct ast_json *json)
516{
517 const char *device = ast_json_string_get(ast_json_object_get(json, "device"));
518 const char *state = ast_json_string_get(ast_json_object_get(json, "state"));
519 int cachable = ast_json_integer_get(ast_json_object_get(json, "cachable"));
520
521 if (!config->device_state) {
522 ast_debug(2, "Received device state event for resource '%s' but it is not configured to accept them\n",
524 return 0;
525 }
526
527 if (ast_strlen_zero(device) || ast_strlen_zero(state)) {
528 ast_debug(1, "Received incomplete device state event for resource '%s'\n",
530 return -1;
531 }
532
533 if (config->device_state_filter && regexec(&config->device_state_regex, device, 0, NULL, 0)) {
534 ast_debug(2, "Received device state on resource '%s' for device '%s' but it has been filtered out\n",
536 return 0;
537 }
538
541 pubsub_eid);
542
543 return 0;
544}
545
547 struct ast_eid *pubsub_eid, struct ast_json *json)
548{
549 const char *uniqueid = ast_json_string_get(ast_json_object_get(json, "uniqueid"));
550 int old_msgs = ast_json_integer_get(ast_json_object_get(json, "old"));
551 int new_msgs = ast_json_integer_get(ast_json_object_get(json, "new"));
552 char *item_id;
553 const char *mailbox;
554
555 if (!config->mailbox_state) {
556 ast_debug(2, "Received mailbox state event for resource '%s' but it is not configured to accept them\n",
558 return 0;
559 }
560
561 if (ast_strlen_zero(uniqueid)) {
562 ast_debug(1, "Received incomplete mailbox state event for resource '%s'\n",
564 return -1;
565 }
566
567 if (config->mailbox_state_filter && regexec(&config->mailbox_state_regex, uniqueid, 0, NULL, 0)) {
568 ast_debug(2, "Received mailbox state on resource '%s' for uniqueid '%s' but it has been filtered out\n",
570 return 0;
571 }
572
573 item_id = ast_strdupa(uniqueid);
574 mailbox = strsep(&item_id, "@");
575
576 ast_publish_mwi_state_full(mailbox, item_id, new_msgs, old_msgs, NULL, pubsub_eid);
577
578 return 0;
579}
580
582 struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
583{
584 struct ast_sip_outbound_publish_client *client;
585 struct ast_datastore *datastore;
586 struct ao2_container *cached;
587
588 if (ast_strlen_zero(config->devicestate_publish)) {
589 return 0;
590 }
591
592 client = ast_sip_publish_client_get(config->devicestate_publish);
593 if (!client) {
594 ast_log(LOG_ERROR, "Received refresh request for devicestate on publication '%s' but publish '%s' is not available\n",
595 ast_sorcery_object_get_id(config), config->devicestate_publish);
596 return 0;
597 }
598
599 datastore = ast_sip_publish_client_get_datastore(client, "asterisk-devicestate-publisher");
600 if (!datastore) {
601 ao2_ref(client, -1);
602 return 0;
603 }
604
606 if (cached) {
607 ao2_callback(cached, OBJ_NODATA, cached_devstate_cb, datastore);
608 ao2_ref(cached, -1);
609 }
610 ao2_ref(client, -1);
611 ao2_ref(datastore, -1);
612
613 return 0;
614}
615
616static int asterisk_publication_devicestate_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body,
618{
621 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
622 const char *eid, *type;
623 struct ast_eid pubsub_eid;
624 int res = -1;
625
626 /* If no configuration exists for this publication it has most likely been removed, so drop this immediately */
627 if (!config) {
628 return -1;
629 }
630
631 /* If no body exists this is a refresh and can be ignored */
632 if (!body) {
633 return 0;
634 }
635
636 /* We only accept JSON for content */
637 if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
638 ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
640 return -1;
641 }
642
643 json = ast_json_load_buf(body->data, body->len, NULL);
644 if (!json) {
645 ast_debug(1, "Received unparseable JSON event for resource '%s'\n",
647 return -1;
648 }
649
651 if (!eid) {
652 ast_debug(1, "Received event without eid for resource '%s'\n",
654 return -1;
655 }
656 ast_str_to_eid(&pubsub_eid, eid);
657
659 if (!type) {
660 ast_debug(1, "Received event without type for resource '%s'\n",
662 return -1;
663 } else if (!strcmp(type, "devicestate")) {
664 res = asterisk_publication_devicestate(pub, config, &pubsub_eid, json);
665 } else if (!strcmp(type, "refresh")) {
666 res = asterisk_publication_devicestate_refresh(pub, config, &pubsub_eid, json);
667 }
668
669 return res;
670}
671
673 struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
674{
675 struct ast_sip_outbound_publish_client *client;
676 struct ast_datastore *datastore;
677 struct ao2_container *cached;
678
679 if (ast_strlen_zero(config->mailboxstate_publish)) {
680 return 0;
681 }
682
683 client = ast_sip_publish_client_get(config->mailboxstate_publish);
684 if (!client) {
685 ast_log(LOG_ERROR, "Received refresh request for mwi state on publication '%s' but publish '%s' is not available\n",
686 ast_sorcery_object_get_id(config), config->mailboxstate_publish);
687 return 0;
688 }
689
690 datastore = ast_sip_publish_client_get_datastore(client, "asterisk-mwi-publisher");
691 if (!datastore) {
692 ao2_ref(client, -1);
693 return 0;
694 }
695
697 if (cached) {
698 ao2_callback(cached, OBJ_NODATA, cached_mwistate_cb, datastore);
699 ao2_ref(cached, -1);
700 }
701 ao2_ref(client, -1);
702 ao2_ref(datastore, -1);
703
704 return 0;
705}
706
707static int asterisk_publication_mwi_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body,
709{
712 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
713 const char *eid, *type;
714 struct ast_eid pubsub_eid;
715 int res = -1;
716
717 /* If no configuration exists for this publication it has most likely been removed, so drop this immediately */
718 if (!config) {
719 return -1;
720 }
721
722 /* If no body exists this is a refresh and can be ignored */
723 if (!body) {
724 return 0;
725 }
726
727 /* We only accept JSON for content */
728 if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
729 ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
731 return -1;
732 }
733
734 json = ast_json_load_buf(body->data, body->len, NULL);
735 if (!json) {
736 ast_debug(1, "Received unparseable JSON event for resource '%s'\n",
738 return -1;
739 }
740
742 if (!eid) {
743 ast_debug(1, "Received event without eid for resource '%s'\n",
745 return -1;
746 }
747 ast_str_to_eid(&pubsub_eid, eid);
748
750 if (!type) {
751 ast_debug(1, "Received event without type for resource '%s'\n",
753 return -1;
754 } else if (!strcmp(type, "mailboxstate")) {
755 res = asterisk_publication_mailboxstate(pub, config, &pubsub_eid, json);
756 } else if (!strcmp(type, "refresh")) {
757 res = asterisk_publication_mwi_refresh(pub, config, &pubsub_eid, json);
758 }
759
760 return res;
761}
762
763static int send_refresh_cb(void *obj, void *arg, int flags)
764{
766 struct ast_sip_outbound_publish_client *client;
767
768 if (!ast_strlen_zero(config->devicestate_publish)) {
769 client = ast_sip_publish_client_get(config->devicestate_publish);
770 if (client) {
771 ast_sip_publish_client_send(client, arg);
772 ao2_ref(client, -1);
773 }
774 }
775
776 if (!ast_strlen_zero(config->mailboxstate_publish)) {
777 client = ast_sip_publish_client_get(config->mailboxstate_publish);
778 if (client) {
779 ast_sip_publish_client_send(client, arg);
780 ao2_ref(client, -1);
781 }
782 }
783
784 return 0;
785}
786
787/*! \brief Internal function to send refresh requests to all publications */
789{
791 char eid_str[20];
792 struct ast_json *json;
793 char *text;
794 struct ast_sip_body body = {
795 .type = "application",
796 .subtype = "json",
797 };
798
799 if (!publications) {
800 return;
801 }
802
803 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
804 json = ast_json_pack(
805 "{ s: s, s: s }",
806 "type", "refresh",
807 "eid", eid_str);
808 if (!json) {
809 ao2_ref(publications, -1);
810 return;
811 }
812
814 if (!text) {
815 ast_json_unref(json);
816 ao2_ref(publications, -1);
817 return;
818 }
819 body.body_text = text;
820
821 ao2_callback(publications, OBJ_NODATA, send_refresh_cb, &body);
822
824 ast_json_unref(json);
825 ao2_ref(publications, -1);
826}
827
829 .event_name = "asterisk-devicestate",
830 .new_publication = asterisk_publication_new,
831 .publication_state_change = asterisk_publication_devicestate_state_change,
832};
833
835 .event_name = "asterisk-mwi",
836 .new_publication = asterisk_publication_new,
837 .publication_state_change = asterisk_publication_mwi_state_change,
838};
839
840/*! \brief Destructor function for Asterisk publication configuration */
842{
844
846}
847
848/*! \brief Allocator function for Asterisk publication configuration */
850{
853
854 if (!config || ast_string_field_init(config, 256)) {
856 return NULL;
857 }
858
859 return config;
860}
861
862static int regex_filter_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
863{
865 int res = -1;
866
867 if (ast_strlen_zero(var->value)) {
868 return 0;
869 }
870
871 if (!strcmp(var->name, "device_state_filter")) {
872 if (!(res = build_regex(&config->device_state_regex, var->value))) {
873 config->device_state_filter = 1;
874 }
875 } else if (!strcmp(var->name, "mailbox_state_filter")) {
876 if (!(res = build_regex(&config->mailbox_state_regex, var->value))) {
877 config->mailbox_state_filter = 1;
878 }
879 }
880
881 return res;
882}
883
884static int load_module(void)
885{
887 ast_log(LOG_ERROR, "Entity ID is not set.\n");
889 }
890
891 ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_publish_asterisk");
892 ast_sorcery_apply_default(ast_sip_get_sorcery(), "asterisk-publication", "config", "pjsip.conf,criteria=type=asterisk-publication");
893
895 ast_log(LOG_ERROR, "Unable to register 'asterisk-publication' type with sorcery\n");
897 }
898
899 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "type", "", OPT_NOOP_T, 0, 0);
902 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));
903 ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "asterisk-publication", "device_state_filter", "", regex_filter_handler, NULL, NULL, 0, 0);
904 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));
905 ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "asterisk-publication", "mailbox_state_filter", "", regex_filter_handler, NULL, NULL, 0, 0);
906 ast_sorcery_reload_object(ast_sip_get_sorcery(), "asterisk-publication");
907
909 ast_log(LOG_WARNING, "Unable to register event publication handler %s\n",
912 }
914 ast_log(LOG_WARNING, "Unable to register event publication handler %s\n",
918 }
920 ast_log(LOG_WARNING, "Unable to register event publisher handler %s\n",
925 }
927 ast_log(LOG_WARNING, "Unable to register event publisher handler %s\n",
933 }
934
936
938}
939
940static int reload_module(void)
941{
942 ast_sorcery_reload_object(ast_sip_get_sorcery(), "asterisk-publication");
944 return 0;
945}
946
947static int unload_module(void)
948{
953 ast_sorcery_object_unregister(ast_sip_get_sorcery(), "asterisk-publication");
954 return 0;
955}
956
957AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Asterisk Event PUBLISH Support",
958 .support_level = AST_MODULE_SUPPORT_CORE,
959 .load = load_module,
961 .unload = unload_module,
962 .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5,
963 .requires = "res_pjsip,res_pjsip_outbound_publish,res_pjsip_pubsub",
char * text
Definition: app_queue.c:1809
#define var
Definition: ast_expr2f.c:605
char * strsep(char **str, const char *delims)
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:258
struct stasis_cache * ast_device_state_cache(void)
Backend cache for ast_device_state_topic_cached()
Definition: devicestate.c:676
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
Definition: devicestate.c:671
enum ast_device_state ast_devstate_val(const char *val)
Convert device state from text to integer value.
Definition: devicestate.c:263
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:712
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.
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:1050
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:1104
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:1161
int stasis_subscription_is_subscribed(const struct stasis_subscription *sub)
Returns whether a subscription is currently subscribed.
Definition: stasis.c:1177
#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:2444
const char * type
Definition: res_pjsip.h:2446
const char * body_text
Definition: res_pjsip.h:2450
An entity with which Asterisk communicates.
Definition: res_pjsip.h:1051
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