Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*** 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 */
113  AST_STRING_FIELD(devicestate_publish);
114  /*! \brief Optional name of a mailbox state publish item, used to request the remote side update us */
115  AST_STRING_FIELD(mailboxstate_publish);
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 ast_event
170  * \param data void pointer to ast_client structure
171  * \return void
172  */
173 static void asterisk_publisher_devstate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
174 {
175  struct ast_datastore *datastore = data;
176  struct asterisk_devicestate_publisher_state *publisher_state = datastore->data;
177  struct ast_device_state_message *dev_state;
178  char eid_str[20];
179  struct ast_json *json;
180  char *text;
181  struct ast_sip_body body = {
182  .type = "application",
183  .subtype = "json",
184  };
185 
187  return;
188  }
189 
190  dev_state = stasis_message_data(msg);
191  if (!dev_state->eid || ast_eid_cmp(&ast_eid_default, dev_state->eid)) {
192  /* If the event is aggregate or didn't originate from this server, don't send it out. */
193  return;
194  }
195 
196  if (publisher_state->device_state_filter && regexec(&publisher_state->device_state_regex, dev_state->device, 0, NULL, 0)) {
197  /* Outgoing device state has been filtered and the device name does not match */
198  return;
199  }
200 
201  ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
202  json = ast_json_pack(
203  "{ s: s, s: s, s: s, s: i, s:s }",
204  "type", "devicestate",
205  "device", dev_state->device,
206  "state", ast_devstate_str(dev_state->state),
207  "cachable", dev_state->cachable,
208  "eid", eid_str);
209  if (!json) {
210  return;
211  }
212 
213  text = ast_json_dump_string(json);
214  if (!text) {
215  ast_json_unref(json);
216  return;
217  }
218  body.body_text = text;
219 
220  ast_sip_publish_client_send(publisher_state->client, &body);
221 
222  ast_json_free(text);
223  ast_json_unref(json);
224 }
225 
226 /*!
227  * \brief Callback function for mailbox state events
228  * \param ast_event
229  * \param data void pointer to ast_client structure
230  * \return void
231  */
232 static void asterisk_publisher_mwistate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
233 {
234  struct ast_datastore *datastore = data;
235  struct asterisk_mwi_publisher_state *publisher_state = datastore->data;
236  struct ast_mwi_state *mwi_state;
237  char eid_str[20];
238  struct ast_json *json;
239  char *text;
240  struct ast_sip_body body = {
241  .type = "application",
242  .subtype = "json",
243  };
244 
246  return;
247  }
248 
249  mwi_state = stasis_message_data(msg);
250  if (ast_eid_cmp(&ast_eid_default, &mwi_state->eid)) {
251  /* If the event is aggregate or didn't originate from this server, don't send it out. */
252  return;
253  }
254 
255  if (publisher_state->mailbox_state_filter && regexec(&publisher_state->mailbox_state_regex, mwi_state->uniqueid, 0, NULL, 0)) {
256  /* Outgoing mailbox state has been filtered and the uniqueid does not match */
257  return;
258  }
259 
260  ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
261  json = ast_json_pack(
262  "{ s: s, s: s, s: i, s: i, s:s }",
263  "type", "mailboxstate",
264  "uniqueid", mwi_state->uniqueid,
265  "old", mwi_state->old_msgs,
266  "new", mwi_state->new_msgs,
267  "eid", eid_str);
268  if (!json) {
269  return;
270  }
271 
272  text = ast_json_dump_string(json);
273  if (!text) {
274  ast_json_unref(json);
275  return;
276  }
277  body.body_text = text;
278 
279  ast_sip_publish_client_send(publisher_state->client, &body);
280 
281  ast_json_free(text);
282  ast_json_unref(json);
283 }
284 
285 static int cached_devstate_cb(void *obj, void *arg, int flags)
286 {
287  struct stasis_message *msg = obj;
288  struct ast_datastore *datastore = arg;
289  struct asterisk_devicestate_publisher_state *publisher_state = datastore->data;
290 
291  asterisk_publisher_devstate_cb(arg, publisher_state->device_state_subscription, msg);
292 
293  return 0;
294 }
295 
296 static int cached_mwistate_cb(void *obj, void *arg, int flags)
297 {
298  struct stasis_message *msg = obj;
299  struct ast_datastore *datastore = arg;
300  struct asterisk_mwi_publisher_state *publisher_state = datastore->data;
301 
303 
304  return 0;
305 }
306 
307 static int build_regex(regex_t *regex, const char *text)
308 {
309  int res;
310 
311  if ((res = regcomp(regex, text, REG_EXTENDED | REG_ICASE | REG_NOSUB))) {
312  size_t len = regerror(res, regex, NULL, 0);
313  char buf[len];
314  regerror(res, regex, buf, len);
315  ast_log(LOG_ERROR, "Could not compile regex '%s': %s\n", text, buf);
316  return -1;
317  }
318 
319  return 0;
320 }
321 
324 {
325  RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
326  struct asterisk_devicestate_publisher_state *publisher_state;
327  const char *value;
328  struct ao2_container *cached;
329 
330  datastore = ast_sip_publish_client_alloc_datastore(&asterisk_devicestate_publisher_state_datastore,
331  "asterisk-devicestate-publisher");
332  if (!datastore) {
333  return -1;
334  }
335 
336  publisher_state = ast_calloc(1, sizeof(struct asterisk_devicestate_publisher_state));
337  if (!publisher_state) {
338  return -1;
339  }
340  datastore->data = publisher_state;
341 
342  value = ast_sorcery_object_get_extended(configuration, "device_state_filter");
343  if (!ast_strlen_zero(value)) {
344  if (build_regex(&publisher_state->device_state_regex, value)) {
345  return -1;
346  }
347  publisher_state->device_state_filter = 1;
348  }
349 
350  publisher_state->client = ao2_bump(client);
351 
352  if (ast_sip_publish_client_add_datastore(client, datastore)) {
353  return -1;
354  }
355 
358  if (!publisher_state->device_state_subscription) {
359  ast_sip_publish_client_remove_datastore(client, "asterisk-devicestate-publisher");
360  ao2_ref(datastore, -1);
361  return -1;
362  }
366 
368  ao2_callback(cached, OBJ_NODATA, cached_devstate_cb, datastore);
369  ao2_ref(cached, -1);
370 
371  return 0;
372 }
373 
375 {
376  RAII_VAR(struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client, "asterisk-devicestate-publisher"),
377  ao2_cleanup);
378  struct asterisk_devicestate_publisher_state *publisher_state;
379 
380  if (!datastore) {
381  return 0;
382  }
383 
384  publisher_state = datastore->data;
385  if (publisher_state->device_state_subscription) {
387  ao2_ref(datastore, -1);
388  }
389 
390  ast_sip_publish_client_remove_datastore(client, "asterisk-devicestate-publisher");
391 
392  return 0;
393 }
394 
396  .event_name = "asterisk-devicestate",
397  .start_publishing = asterisk_start_devicestate_publishing,
398  .stop_publishing = asterisk_stop_devicestate_publishing,
399 };
400 
403 {
404  RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
405  struct asterisk_mwi_publisher_state *publisher_state;
406  const char *value;
407  struct ao2_container *cached;
408 
409  datastore = ast_sip_publish_client_alloc_datastore(&asterisk_mwi_publisher_state_datastore, "asterisk-mwi-publisher");
410  if (!datastore) {
411  return -1;
412  }
413 
414  publisher_state = ast_calloc(1, sizeof(struct asterisk_mwi_publisher_state));
415  if (!publisher_state) {
416  return -1;
417  }
418  datastore->data = publisher_state;
419 
420  value = ast_sorcery_object_get_extended(configuration, "mailbox_state_filter");
421  if (!ast_strlen_zero(value)) {
422  if (build_regex(&publisher_state->mailbox_state_regex, value)) {
423  return -1;
424  }
425  publisher_state->mailbox_state_filter = 1;
426  }
427 
428  publisher_state->client = ao2_bump(client);
429 
430  if (ast_sip_publish_client_add_datastore(client, datastore)) {
431  return -1;
432  }
433 
436  if (!publisher_state->mailbox_state_subscription) {
437  ast_sip_publish_client_remove_datastore(client, "asterisk-mwi-publisher");
438  ao2_ref(datastore, -1);
439  return -1;
440  }
444 
446  ao2_callback(cached, OBJ_NODATA, cached_mwistate_cb, datastore);
447  ao2_ref(cached, -1);
448 
449  return 0;
450 }
451 
453 {
454  RAII_VAR(struct ast_datastore *, datastore, ast_sip_publish_client_get_datastore(client, "asterisk-mwi-publisher"),
455  ao2_cleanup);
456  struct asterisk_mwi_publisher_state *publisher_state;
457 
458  if (!datastore) {
459  return 0;
460  }
461 
462  publisher_state = datastore->data;
463  if (publisher_state->mailbox_state_subscription) {
465  ao2_ref(datastore, -1);
466  }
467 
468  ast_sip_publish_client_remove_datastore(client, "asterisk-mwi-publisher");
469 
470  return 0;
471 }
472 
474  .event_name = "asterisk-mwi",
475  .start_publishing = asterisk_start_mwi_publishing,
476  .stop_publishing = asterisk_stop_mwi_publishing,
477 };
478 
479 static int asterisk_publication_new(struct ast_sip_endpoint *endpoint, const char *resource, const char *event_configuration)
480 {
482  event_configuration), ao2_cleanup);
483 
484  /* If no inbound Asterisk publication configuration exists reject the PUBLISH */
485  if (!config) {
486  return 404;
487  }
488 
489  return 200;
490 }
491 
493  struct ast_eid *pubsub_eid, struct ast_json *json)
494 {
495  const char *device = ast_json_string_get(ast_json_object_get(json, "device"));
496  const char *state = ast_json_string_get(ast_json_object_get(json, "state"));
497  int cachable = ast_json_integer_get(ast_json_object_get(json, "cachable"));
498 
499  if (!config->device_state) {
500  ast_debug(2, "Received device state event for resource '%s' but it is not configured to accept them\n",
501  ast_sorcery_object_get_id(config));
502  return 0;
503  }
504 
505  if (ast_strlen_zero(device) || ast_strlen_zero(state)) {
506  ast_debug(1, "Received incomplete device state event for resource '%s'\n",
507  ast_sorcery_object_get_id(config));
508  return -1;
509  }
510 
511  if (config->device_state_filter && regexec(&config->device_state_regex, device, 0, NULL, 0)) {
512  ast_debug(2, "Received device state on resource '%s' for device '%s' but it has been filtered out\n",
513  ast_sorcery_object_get_id(config), device);
514  return 0;
515  }
516 
519  pubsub_eid);
520 
521  return 0;
522 }
523 
525  struct ast_eid *pubsub_eid, struct ast_json *json)
526 {
527  const char *uniqueid = ast_json_string_get(ast_json_object_get(json, "uniqueid"));
528  int old_msgs = ast_json_integer_get(ast_json_object_get(json, "old"));
529  int new_msgs = ast_json_integer_get(ast_json_object_get(json, "new"));
530  char *item_id;
531  const char *mailbox;
532 
533  if (!config->mailbox_state) {
534  ast_debug(2, "Received mailbox state event for resource '%s' but it is not configured to accept them\n",
535  ast_sorcery_object_get_id(config));
536  return 0;
537  }
538 
539  if (ast_strlen_zero(uniqueid)) {
540  ast_debug(1, "Received incomplete mailbox state event for resource '%s'\n",
541  ast_sorcery_object_get_id(config));
542  return -1;
543  }
544 
545  if (config->mailbox_state_filter && regexec(&config->mailbox_state_regex, uniqueid, 0, NULL, 0)) {
546  ast_debug(2, "Received mailbox state on resource '%s' for uniqueid '%s' but it has been filtered out\n",
547  ast_sorcery_object_get_id(config), uniqueid);
548  return 0;
549  }
550 
551  item_id = ast_strdupa(uniqueid);
552  mailbox = strsep(&item_id, "@");
553 
554  ast_publish_mwi_state_full(mailbox, item_id, new_msgs, old_msgs, NULL, pubsub_eid);
555 
556  return 0;
557 }
558 
560  struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
561 {
563  struct ast_datastore *datastore;
564  struct ao2_container *cached;
565 
566  if (ast_strlen_zero(config->devicestate_publish)) {
567  return 0;
568  }
569 
571  if (!client) {
572  ast_log(LOG_ERROR, "Received refresh request for devicestate on publication '%s' but publish '%s' is not available\n",
574  return 0;
575  }
576 
577  datastore = ast_sip_publish_client_get_datastore(client, "asterisk-devicestate-publisher");
578  if (!datastore) {
579  ao2_ref(client, -1);
580  return 0;
581  }
582 
584  if (cached) {
585  ao2_callback(cached, OBJ_NODATA, cached_devstate_cb, datastore);
586  ao2_ref(cached, -1);
587  }
588  ao2_ref(client, -1);
589  ao2_ref(datastore, -1);
590 
591  return 0;
592 }
593 
594 static int asterisk_publication_devicestate_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body,
596 {
599  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
600  const char *eid, *type;
601  struct ast_eid pubsub_eid;
602  int res = -1;
603 
604  /* If no configuration exists for this publication it has most likely been removed, so drop this immediately */
605  if (!config) {
606  return -1;
607  }
608 
609  /* If no body exists this is a refresh and can be ignored */
610  if (!body) {
611  return 0;
612  }
613 
614  /* We only accept JSON for content */
615  if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
616  ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
618  return -1;
619  }
620 
621  json = ast_json_load_buf(body->data, body->len, NULL);
622  if (!json) {
623  ast_debug(1, "Received unparseable JSON event for resource '%s'\n",
625  return -1;
626  }
627 
628  eid = ast_json_string_get(ast_json_object_get(json, "eid"));
629  if (!eid) {
630  ast_debug(1, "Received event without eid for resource '%s'\n",
632  return -1;
633  }
634  ast_str_to_eid(&pubsub_eid, eid);
635 
636  type = ast_json_string_get(ast_json_object_get(json, "type"));
637  if (!type) {
638  ast_debug(1, "Received event without type for resource '%s'\n",
640  return -1;
641  } else if (!strcmp(type, "devicestate")) {
642  res = asterisk_publication_devicestate(pub, config, &pubsub_eid, json);
643  } else if (!strcmp(type, "refresh")) {
644  res = asterisk_publication_devicestate_refresh(pub, config, &pubsub_eid, json);
645  }
646 
647  return res;
648 }
649 
651  struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
652 {
654  struct ast_datastore *datastore;
655  struct ao2_container *cached;
656 
657  if (ast_strlen_zero(config->mailboxstate_publish)) {
658  return 0;
659  }
660 
662  if (!client) {
663  ast_log(LOG_ERROR, "Received refresh request for mwi state on publication '%s' but publish '%s' is not available\n",
665  return 0;
666  }
667 
668  datastore = ast_sip_publish_client_get_datastore(client, "asterisk-mwi-publisher");
669  if (!datastore) {
670  ao2_ref(client, -1);
671  return 0;
672  }
673 
675  if (cached) {
676  ao2_callback(cached, OBJ_NODATA, cached_mwistate_cb, datastore);
677  ao2_ref(cached, -1);
678  }
679  ao2_ref(client, -1);
680  ao2_ref(datastore, -1);
681 
682  return 0;
683 }
684 
685 static int asterisk_publication_mwi_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body,
687 {
690  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
691  const char *eid, *type;
692  struct ast_eid pubsub_eid;
693  int res = -1;
694 
695  /* If no configuration exists for this publication it has most likely been removed, so drop this immediately */
696  if (!config) {
697  return -1;
698  }
699 
700  /* If no body exists this is a refresh and can be ignored */
701  if (!body) {
702  return 0;
703  }
704 
705  /* We only accept JSON for content */
706  if (!ast_sip_is_content_type(&body->content_type, "application", "json")) {
707  ast_debug(2, "Received unsupported content type for Asterisk event on resource '%s'\n",
709  return -1;
710  }
711 
712  json = ast_json_load_buf(body->data, body->len, NULL);
713  if (!json) {
714  ast_debug(1, "Received unparseable JSON event for resource '%s'\n",
716  return -1;
717  }
718 
719  eid = ast_json_string_get(ast_json_object_get(json, "eid"));
720  if (!eid) {
721  ast_debug(1, "Received event without eid for resource '%s'\n",
723  return -1;
724  }
725  ast_str_to_eid(&pubsub_eid, eid);
726 
727  type = ast_json_string_get(ast_json_object_get(json, "type"));
728  if (!type) {
729  ast_debug(1, "Received event without type for resource '%s'\n",
731  return -1;
732  } else if (!strcmp(type, "mailboxstate")) {
733  res = asterisk_publication_mailboxstate(pub, config, &pubsub_eid, json);
734  } else if (!strcmp(type, "refresh")) {
735  res = asterisk_publication_mwi_refresh(pub, config, &pubsub_eid, json);
736  }
737 
738  return res;
739 }
740 
741 static int send_refresh_cb(void *obj, void *arg, int flags)
742 {
743  struct asterisk_publication_config *config = obj;
745 
746  if (!ast_strlen_zero(config->devicestate_publish)) {
748  if (client) {
749  ast_sip_publish_client_send(client, arg);
750  ao2_ref(client, -1);
751  }
752  }
753 
754  if (!ast_strlen_zero(config->mailboxstate_publish)) {
756  if (client) {
757  ast_sip_publish_client_send(client, arg);
758  ao2_ref(client, -1);
759  }
760  }
761 
762  return 0;
763 }
764 
765 /*! \brief Internal function to send refresh requests to all publications */
767 {
769  char eid_str[20];
770  struct ast_json *json;
771  char *text;
772  struct ast_sip_body body = {
773  .type = "application",
774  .subtype = "json",
775  };
776 
777  if (!publications) {
778  return;
779  }
780 
781  ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
782  json = ast_json_pack(
783  "{ s: s, s: s }",
784  "type", "refresh",
785  "eid", eid_str);
786  if (!json) {
787  ao2_ref(publications, -1);
788  return;
789  }
790 
791  text = ast_json_dump_string(json);
792  if (!text) {
793  ast_json_unref(json);
794  ao2_ref(publications, -1);
795  return;
796  }
797  body.body_text = text;
798 
799  ao2_callback(publications, OBJ_NODATA, send_refresh_cb, &body);
800 
801  ast_json_free(text);
802  ast_json_unref(json);
803  ao2_ref(publications, -1);
804 }
805 
807  .event_name = "asterisk-devicestate",
808  .new_publication = asterisk_publication_new,
809  .publication_state_change = asterisk_publication_devicestate_state_change,
810 };
811 
813  .event_name = "asterisk-mwi",
814  .new_publication = asterisk_publication_new,
815  .publication_state_change = asterisk_publication_mwi_state_change,
816 };
817 
818 /*! \brief Destructor function for Asterisk publication configuration */
820 {
821  struct asterisk_publication_config *config = obj;
822 
824 }
825 
826 /*! \brief Allocator function for Asterisk publication configuration */
827 static void *asterisk_publication_config_alloc(const char *name)
828 {
831 
832  if (!config || ast_string_field_init(config, 256)) {
833  ao2_cleanup(config);
834  return NULL;
835  }
836 
837  return config;
838 }
839 
840 static int regex_filter_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
841 {
842  struct asterisk_publication_config *config = obj;
843  int res = -1;
844 
845  if (ast_strlen_zero(var->value)) {
846  return 0;
847  }
848 
849  if (!strcmp(var->name, "device_state_filter")) {
850  if (!(res = build_regex(&config->device_state_regex, var->value))) {
851  config->device_state_filter = 1;
852  }
853  } else if (!strcmp(var->name, "mailbox_state_filter")) {
854  if (!(res = build_regex(&config->mailbox_state_regex, var->value))) {
855  config->mailbox_state_filter = 1;
856  }
857  }
858 
859  return res;
860 }
861 
862 static int load_module(void)
863 {
865  ast_log(LOG_ERROR, "Entity ID is not set.\n");
867  }
868 
869  ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_publish_asterisk");
870  ast_sorcery_apply_default(ast_sip_get_sorcery(), "asterisk-publication", "config", "pjsip.conf,criteria=type=asterisk-publication");
871 
873  ast_log(LOG_ERROR, "Unable to register 'asterisk-publication' type with sorcery\n");
875  }
876 
877  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "asterisk-publication", "type", "", OPT_NOOP_T, 0, 0);
880  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));
881  ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "asterisk-publication", "device_state_filter", "", regex_filter_handler, NULL, NULL, 0, 0);
882  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));
883  ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "asterisk-publication", "mailbox_state_filter", "", regex_filter_handler, NULL, NULL, 0, 0);
884  ast_sorcery_reload_object(ast_sip_get_sorcery(), "asterisk-publication");
885 
886  if (ast_sip_register_publish_handler(&asterisk_devicestate_publication_handler)) {
887  ast_log(LOG_WARNING, "Unable to register event publication handler %s\n",
888  asterisk_devicestate_publication_handler.event_name);
890  }
891  if (ast_sip_register_publish_handler(&asterisk_mwi_publication_handler)) {
892  ast_log(LOG_WARNING, "Unable to register event publication handler %s\n",
893  asterisk_mwi_publication_handler.event_name);
894  ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
896  }
897  if (ast_sip_register_event_publisher_handler(&asterisk_devicestate_publisher_handler)) {
898  ast_log(LOG_WARNING, "Unable to register event publisher handler %s\n",
899  asterisk_devicestate_publisher_handler.event_name);
900  ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
901  ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
903  }
904  if (ast_sip_register_event_publisher_handler(&asterisk_mwi_publisher_handler)) {
905  ast_log(LOG_WARNING, "Unable to register event publisher handler %s\n",
906  asterisk_mwi_publisher_handler.event_name);
907  ast_sip_unregister_event_publisher_handler(&asterisk_mwi_publisher_handler);
908  ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
909  ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
911  }
912 
914 
916 }
917 
918 static int reload_module(void)
919 {
920  ast_sorcery_reload_object(ast_sip_get_sorcery(), "asterisk-publication");
922  return 0;
923 }
924 
925 static int unload_module(void)
926 {
927  ast_sip_unregister_publish_handler(&asterisk_devicestate_publication_handler);
928  ast_sip_unregister_publish_handler(&asterisk_mwi_publication_handler);
929  ast_sip_unregister_event_publisher_handler(&asterisk_devicestate_publisher_handler);
930  ast_sip_unregister_event_publisher_handler(&asterisk_mwi_publisher_handler);
931  ast_sorcery_object_unregister(ast_sip_get_sorcery(), "asterisk-publication");
932  return 0;
933 }
934 
935 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Asterisk Event PUBLISH Support",
936  .support_level = AST_MODULE_SUPPORT_CORE,
937  .load = load_module,
939  .unload = unload_module,
940  .load_pri = AST_MODPRI_CHANNEL_DEPEND + 5,
941  .requires = "res_pjsip,res_pjsip_outbound_publish,res_pjsip_pubsub",
942 );
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)
const char * type
Definition: datastore.h:32
const char * event_name
The name of the event this handler deals with.
static const char type[]
Definition: chan_ooh323.c:109
const char * body_text
Definition: res_pjsip.h:2033
Structure which contains Asterisk mailbox publisher state information.
Asterisk main include file. File version handling, generic pbx functions.
static const struct ast_datastore_info asterisk_devicestate_publisher_state_datastore
Datastore for attaching devicestate publisher state information.
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
unsigned int mailbox_state
Accept inbound mailbox state events.
static int send_refresh_cb(void *obj, void *arg, int flags)
char * config
Definition: conf2ael.c:66
const ast_string_field mailboxstate_publish
Optional name of a mailbox state publish item, used to request the remote side update us...
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition: main/utils.c:2587
static int build_regex(regex_t *regex, const char *text)
unsigned int mailbox_state_filter
Mailbox state should be filtered.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
struct stasis_cache * ast_mwi_state_cache(void)
Backend cache for ast_mwi_topic_cached().
Definition: mwi.c:90
#define LOG_WARNING
Definition: logger.h:274
regex_t device_state_regex
Regex used for filtering outbound device state.
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
void ast_json_free(void *p)
Asterisk&#39;s custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
enum ast_device_state state
Definition: devicestate.h:250
int stasis_subscription_is_subscribed(const struct stasis_subscription *sub)
Returns whether a subscription is currently subscribed.
Definition: stasis.c:1152
Structure for variables, used for configurations and for channel variables.
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)
#define var
Definition: ast_expr2f.c:614
static void asterisk_mwi_publisher_state_destroy(void *obj)
Destroy callback for Asterisk mwi publisher state information from datastore.
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:763
Perform no matching, return all objects.
Definition: sorcery.h:123
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
Type for a default handler that should do nothing.
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition: stasis.c:1079
Structure for a data store type.
Definition: datastore.h:31
struct ast_sip_outbound_publish_client * ast_sip_publish_client_get(const char *name)
Find a publish client using its name.
enum ast_devstate_cache cachable
Definition: devicestate.h:252
int ast_sip_register_publish_handler(struct ast_sip_publish_handler *handler)
Register a publish handler.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:337
#define ast_sorcery_apply_config(sorcery, name)
Definition: sorcery.h:456
Return all matching objects.
Definition: sorcery.h:120
char * text
Definition: app_queue.c:1508
static int asterisk_stop_devicestate_publishing(struct ast_sip_outbound_publish_client *client)
Structure for a data store object.
Definition: datastore.h:68
struct stasis_topic * ast_mwi_topic_all(void)
Get the Stasis Message Bus API topic for MWI messages.
Definition: mwi.c:85
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
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
static int asterisk_start_mwi_publishing(struct ast_sip_outbound_publish *configuration, struct ast_sip_outbound_publish_client *client)
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.
An Entity ID is essentially a MAC address, brief and unique.
Definition: utils.h:786
static void * asterisk_publication_config_alloc(const char *name)
Allocator function for Asterisk publication configuration.
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
Definition: main/utils.c:2842
regex_t mailbox_state_regex
Regex used for filtering inbound mailbox state.
#define ast_strlen_zero(foo)
Definition: strings.h:52
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
#define ao2_bump(obj)
Definition: astobj2.h:491
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 * device_state_subscription
Device state subscription.
static void asterisk_publisher_mwistate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
Callback function for mailbox state events.
static char mailbox[AST_MAX_MAILBOX_UNIQUEID]
Definition: chan_mgcp.c:204
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#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
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
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.
enum ast_device_state ast_devstate_val(const char *val)
Convert device state from text to integer value.
Definition: devicestate.c:260
void ast_sip_unregister_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
Unregister a publish handler.
Outbound publish client state information (persists for lifetime of a publish)
struct ast_sip_publish_handler asterisk_devicestate_publication_handler
int ast_sorcery_object_unregister(struct ast_sorcery *sorcery, const char *type)
Unregister an object type.
Definition: sorcery.c:1061
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
struct ast_sip_event_publisher_handler asterisk_devicestate_publisher_handler
const struct ast_eid * eid
The EID of the server where this message originated.
Definition: devicestate.h:248
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
ast_sip_publish_state
const char * type
Definition: res_pjsip.h:2029
static int asterisk_publication_devicestate_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body, enum ast_sip_publish_state state)
Callbacks that event publisher handlers will define.
struct ast_datastore * ast_sip_publish_client_get_datastore(struct ast_sip_outbound_publish_client *client, const char *name)
Retrieve an event publisher datastore.
const char * event_name
The name of the event this handler deals with.
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:299
#define ao2_ref(o, delta)
Definition: astobj2.h:464
const char * ast_sip_publication_get_event_configuration(const struct ast_sip_publication *pub)
Given a publication, get the configuration name for the event type in use.
static int asterisk_publication_mailboxstate(struct ast_sip_publication *pub, struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
int old_msgs
Definition: mwi.h:462
unsigned int device_state_filter
Device state should be filtered.
int ast_str_to_eid(struct ast_eid *eid, const char *s)
Convert a string into an EID.
Definition: main/utils.c:2825
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
struct ast_sip_event_publisher_handler asterisk_mwi_publisher_handler
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
struct ast_sip_publish_handler asterisk_mwi_publication_handler
An entity with which Asterisk communicates.
Definition: res_pjsip.h:812
static int cached_mwistate_cb(void *obj, void *arg, int flags)
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:838
static int asterisk_publication_new(struct ast_sip_endpoint *endpoint, const char *resource, const char *event_configuration)
#define stasis_subscribe(topic, callback, data)
Definition: stasis.h:652
static void asterisk_publication_send_refresh(void)
Internal function to send refresh requests to all publications.
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:388
int ast_sip_is_content_type(pjsip_media_type *content_type, char *type, char *subtype)
Checks if the given content type matches type/subtype.
Definition: res_pjsip.c:5259
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 ast_sip_outbound_publish_client * client
The publish client to send PUBLISH messages on.
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
#define LOG_ERROR
Definition: logger.h:285
struct stasis_cache * ast_device_state_cache(void)
Backend cache for ast_device_state_topic_cached()
Definition: devicestate.c:673
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.
#define SORCERY_OBJECT(details)
Macro which must be used at the beginning of each sorcery capable object.
Definition: sorcery.h:356
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
Type for default option handler for bools (ast_true/ast_false)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition: sorcery.h:477
struct ast_datastore * ast_sip_publish_client_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
Outbound publish information.
static int asterisk_publication_mwi_state_change(struct ast_sip_publication *pub, pjsip_msg_body *body, enum ast_sip_publish_state state)
Callbacks that publication handlers will define.
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
Definition: devicestate.c:668
static void asterisk_publication_config_destroy(void *obj)
Destructor function for Asterisk publication configuration.
unsigned int device_state_filter
Device state should be filtered.
static const char name[]
Definition: cdr_mysql.c:74
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
Definition: func_strings.c:948
static int reload(void)
Definition: cdr_mysql.c:741
static int regex_filter_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
Structure which contains Asterisk publication information.
static int unload_module(void)
static void asterisk_devicestate_publisher_state_destroy(void *obj)
Destroy callback for Asterisk devicestate publisher state information from datastore.
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
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
struct stasis_message_type * ast_mwi_state_type(void)
Get the Stasis Message Bus API message type for MWI messages.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
struct stasis_subscription * mailbox_state_subscription
Mailbox state subscription.
Support for logging to various files, console and syslog Configuration in file logger.conf.
Structure which contains Asterisk device state publisher state information.
void ast_sip_unregister_publish_handler(struct ast_sip_publish_handler *handler)
Unregister a publish handler.
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
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_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
struct ast_eid ast_eid_default
Global EID.
Definition: options.c:93
void * data
Definition: datastore.h:70
char * strsep(char **str, const char *delims)
static int cached_devstate_cb(void *obj, void *arg, int flags)
static const struct ast_datastore_info asterisk_mwi_publisher_state_datastore
Datastore for attaching devicestate publisher state information.
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
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
int new_msgs
Definition: mwi.h:461
const ast_string_field uniqueid
Definition: mwi.h:460
Asterisk MWI API.
static int asterisk_publication_devicestate(struct ast_sip_publication *pub, struct asterisk_publication_config *config, struct ast_eid *pubsub_eid, struct ast_json *json)
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
struct stasis_forward * sub
Definition: res_corosync.c:240
int ast_sip_register_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
Register an event publisher handler.
static int load_module(void)
Abstract JSON element (object, array, string, int, ...).
Type for default option handler for stringfields.
Structure representing a SIP publication.
static int asterisk_stop_mwi_publishing(struct ast_sip_outbound_publish_client *client)
The structure that contains device state.
Definition: devicestate.h:240
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
static int asterisk_start_devicestate_publishing(struct ast_sip_outbound_publish *configuration, struct ast_sip_outbound_publish_client *client)
const ast_string_field devicestate_publish
Optional name of a device state publish item struct ast_string_field_mgr __field_mgr used to request ...
int ast_eid_is_empty(const struct ast_eid *eid)
Check if EID is empty.
Definition: main/utils.c:2847
Generic container type.
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
static int reload_module(void)
The structure that contains MWI state.
Definition: mwi.h:457
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition: json.c:322
unsigned int device_state
Accept inbound device state events.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
struct ast_eid eid
Definition: mwi.h:465
SIP body description.
Definition: res_pjsip.h:2027
regex_t device_state_regex
Regex used for filtering inbound device state.
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
regex_t mailbox_state_regex
Regex used for filtering outbound mailbox state.
static void asterisk_publisher_devstate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
Callback function for device state events.