Asterisk - The Open Source Telephony Project  GIT-master-a24979a
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 */
103  unsigned int mailbox_state_filter;
104 };
105 
106 /*! \brief Structure which contains Asterisk publication information */
108  /*! \brief Sorcery object details */
109  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 */
122  unsigned int device_state_filter;
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 */
128  unsigned int mailbox_state_filter;
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  */
172 static 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 
212  text = ast_json_dump_string(json);
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  */
230 static 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 
270  text = ast_json_dump_string(json);
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 
283 static 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 
289  asterisk_publisher_devstate_cb(arg, publisher_state->device_state_subscription, msg);
290 
291  return 0;
292 }
293 
294 static 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 
305 static 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"),
375  ao2_cleanup);
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"),
453  ao2_cleanup);
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 
477 static 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",
545  ast_sorcery_object_get_id(config), uniqueid);
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 
592 static 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 
683 static 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 
739 static int send_refresh_cb(void *obj, void *arg, int flags)
740 {
741  struct asterisk_publication_config *config = obj;
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 
789  text = ast_json_dump_string(json);
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 {
819  struct asterisk_publication_config *config = obj;
820 
822 }
823 
824 /*! \brief Allocator function for Asterisk publication configuration */
825 static void *asterisk_publication_config_alloc(const char *name)
826 {
829 
830  if (!config || ast_string_field_init(config, 256)) {
832  return NULL;
833  }
834 
835  return config;
836 }
837 
838 static int regex_filter_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
839 {
840  struct asterisk_publication_config *config = obj;
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 
860 static 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 
916 static int reload_module(void)
917 {
918  ast_sorcery_reload_object(ast_sip_get_sorcery(), "asterisk-publication");
920  return 0;
921 }
922 
923 static int unload_module(void)
924 {
929  ast_sorcery_object_unregister(ast_sip_get_sorcery(), "asterisk-publication");
930  return 0;
931 }
932 
933 AST_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",
940 );
char * text
Definition: app_queue.c:1641
#define var
Definition: ast_expr2f.c:614
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 char mailbox[AST_MAX_MAILBOX_UNIQUEID]
Definition: chan_mgcp.c:207
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.
@ AST_DEVSTATE_CACHABLE
Definition: devicestate.h:70
@ AST_DEVSTATE_NOT_CACHABLE
Definition: devicestate.h:69
struct stasis_cache * ast_device_state_cache(void)
Backend cache for ast_device_state_topic_cached()
Definition: devicestate.c:673
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_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
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:783
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
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:564
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition: json.c:322
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#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.
struct stasis_topic * ast_mwi_topic_all(void)
Get the Stasis Message Bus API topic for MWI messages.
Definition: mwi.c:89
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_message_type * ast_mwi_state_type(void)
Get the Stasis Message Bus API message type for MWI messages.
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:2082
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.
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_datastore * ast_sip_publish_client_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
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_outbound_publish_client * ast_sip_publish_client_get(const char *name)
Find a publish client using its name.
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 void * asterisk_publication_config_alloc(const char *name)
Allocator function for Asterisk publication configuration.
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 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
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
@ 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_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
#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
int ast_sorcery_object_unregister(struct ast_sorcery *sorcery, const char *type)
Unregister an object type.
Definition: sorcery.c:1061
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
#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_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
#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
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:2330
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition: stasis.c:1136
@ 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:1025
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition: stasis.c:1079
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
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
int stasis_subscription_is_subscribed(const struct stasis_subscription *sub)
Returns whether a subscription is currently subscribed.
Definition: stasis.c:1152
#define stasis_subscribe(topic, callback, data)
Definition: stasis.h:649
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
#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:808
unsigned char eid[6]
Definition: utils.h:809
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:2108
const char * type
Definition: res_pjsip.h:2110
const char * body_text
Definition: res_pjsip.h:2114
An entity with which Asterisk communicates.
Definition: res_pjsip.h:854
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:936
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
Definition: main/utils.c:2990
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition: main/utils.c:2735
int ast_eid_is_empty(const struct ast_eid *eid)
Check if EID is empty.
Definition: main/utils.c:2995
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: main/utils.c:2973