Asterisk - The Open Source Telephony Project  GIT-master-44aef04
res_pjsip_outbound_publish.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_pjproject</depend>
22  <depend>res_pjsip</depend>
23  <support_level>core</support_level>
24  ***/
25 
26 #include "asterisk.h"
27 
28 #include <pjsip.h>
29 #include <pjsip_simple.h>
30 
31 #include "asterisk/res_pjproject.h"
32 #include "asterisk/res_pjsip.h"
34 #include "asterisk/module.h"
35 #include "asterisk/taskprocessor.h"
36 #include "asterisk/threadpool.h"
37 #include "asterisk/datastore.h"
39 
40 /*** DOCUMENTATION
41  <configInfo name="res_pjsip_outbound_publish" language="en_US">
42  <synopsis>SIP resource for outbound publish</synopsis>
43  <description><para>
44  <emphasis>Outbound Publish</emphasis>
45  </para>
46  <para>This module allows <literal>res_pjsip</literal> to publish to other SIP servers.</para>
47  </description>
48  <configFile name="pjsip.conf">
49  <configObject name="outbound-publish">
50  <synopsis>The configuration for outbound publish</synopsis>
51  <description><para>
52  Publish is <emphasis>COMPLETELY</emphasis> separate from the rest of
53  <literal>pjsip.conf</literal>. A minimal configuration consists of
54  setting a <literal>server_uri</literal> and <literal>event</literal>.
55  </para></description>
56  <configOption name="expiration" default="3600">
57  <synopsis>Expiration time for publications in seconds</synopsis>
58  </configOption>
59  <configOption name="outbound_auth" default="">
60  <synopsis>Authentication object(s) to be used for outbound publishes.</synopsis>
61  <description><para>
62  This is a comma-delimited list of <replaceable>auth</replaceable>
63  sections defined in <filename>pjsip.conf</filename> used to respond
64  to outbound authentication challenges.</para>
65  <note><para>
66  Using the same auth section for inbound and outbound
67  authentication is not recommended. There is a difference in
68  meaning for an empty realm setting between inbound and outbound
69  authentication uses. See the auth realm description for details.
70  </para></note>
71  </description>
72  </configOption>
73  <configOption name="outbound_proxy" default="">
74  <synopsis>Full SIP URI of the outbound proxy used to send publishes</synopsis>
75  </configOption>
76  <configOption name="server_uri">
77  <synopsis>SIP URI of the server and entity to publish to</synopsis>
78  <description><para>
79  This is the URI at which to find the entity and server to send the outbound PUBLISH to.
80  This URI is used as the request URI of the outbound PUBLISH request from Asterisk.
81  </para></description>
82  </configOption>
83  <configOption name="from_uri">
84  <synopsis>SIP URI to use in the From header</synopsis>
85  <description><para>
86  This is the URI that will be placed into the From header of outgoing PUBLISH
87  messages. If no URI is specified then the URI provided in <literal>server_uri</literal>
88  will be used.
89  </para></description>
90  </configOption>
91  <configOption name="to_uri">
92  <synopsis>SIP URI to use in the To header</synopsis>
93  <description><para>
94  This is the URI that will be placed into the To header of outgoing PUBLISH
95  messages. If no URI is specified then the URI provided in <literal>server_uri</literal>
96  will be used.
97  </para></description>
98  </configOption>
99  <configOption name="event" default="">
100  <synopsis>Event type of the PUBLISH.</synopsis>
101  </configOption>
102  <configOption name="max_auth_attempts" default="5">
103  <synopsis>Maximum number of authentication attempts before stopping the publication.</synopsis>
104  </configOption>
105  <configOption name="transport">
106  <synopsis>Transport used for outbound publish</synopsis>
107  <description>
108  <note><para>A <replaceable>transport</replaceable> configured in
109  <literal>pjsip.conf</literal>. As with other <literal>res_pjsip</literal> modules, this will use the first available transport of the appropriate type if unconfigured.</para></note>
110  </description>
111  </configOption>
112  <configOption name="multi_user" default="no">
113  <synopsis>Enable multi-user support</synopsis>
114  <description><para>When enabled the user portion of the server uri is replaced by a dynamically created user</para></description>
115  </configOption>
116  <configOption name="type">
117  <synopsis>Must be of type 'outbound-publish'.</synopsis>
118  </configOption>
119  </configObject>
120  </configFile>
121  </configInfo>
122  ***/
123 
124 static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE;
125 
126 /*! \brief Queued outbound publish message */
128  /*! \brief Optional body */
130  /*! \brief Linked list information */
132  /*! \brief Extra space for body contents */
133  char body_contents[0];
134 };
135 
136 /*
137  * A note about some of the object types used in this module:
138  *
139  * The reason we currently have 4 separate object types that relate to configuration,
140  * publishing, state, and client information is due to object lifetimes and order of
141  * destruction dependencies.
142  *
143  * Separation of concerns is a good thing and of course it makes sense to have a
144  * configuration object type as well as an object type wrapper around pjsip's publishing
145  * client class. There also may be run time state data that needs to be tracked, so
146  * again having something to handle that is prudent. However, it may be tempting to think
147  * "why not combine the state and client object types?" Especially seeing as how they have
148  * a one-to-one relationship. The answer is, it's possible, but it'd make the code a bit
149  * more awkward.
150  *
151  * Currently this module maintains a global container of current state objects. When this
152  * states container is replaced, or deleted, it un-references all contained objects. Any
153  * state with a reference left have probably been carried over from a reload/realtime fetch.
154  * States not carried over are destructed and the associated client (and all its publishers)
155  * get unpublished.
156  *
157  * This "unpublishing" goes through a careful process of unpublishing the client, all its
158  * publishers, and making sure all the appropriate references are removed in a sane order.
159  * This process is essentially kicked off with the destruction of the state. If the state
160  * and client objects were to be merged, where clients became the globally tracked object
161  * type, this "unpublishing" process would never start because of the multiple references
162  * held to the client object over it's lifetime. Meaning the global tracking container
163  * would remove its reference to the client object when done with it, but other sources
164  * would still be holding a reference to it (namely the datastore and publisher(s)).
165  *
166  * Thus at this time it is easier to keep them separate.
167  */
168 
169 /*! \brief Outbound publish information */
171  /*! \brief Sorcery object details */
172  SORCERY_OBJECT(details);
173  /*! \brief Stringfields */
175  /*! \brief URI for the entity and server */
177  /*! \brief URI for the From header */
178  AST_STRING_FIELD(from_uri);
179  /*! \brief URI for the To header */
180  AST_STRING_FIELD(to_uri);
181  /*! \brief Explicit transport to use for publish */
182  AST_STRING_FIELD(transport);
183  /*! \brief Outbound proxy to use */
184  AST_STRING_FIELD(outbound_proxy);
185  /*! \brief The event type to publish */
187  );
188  /*! \brief Requested expiration time */
189  unsigned int expiration;
190  /*! \brief Maximum number of auth attempts before stopping the publish client */
191  unsigned int max_auth_attempts;
192  /*! \brief Configured authentication credentials */
193  struct ast_sip_auth_vector outbound_auths;
194  /*! \brief The publishing client is used for multiple users when true */
195  unsigned int multi_user;
196 };
197 
199  /*! \brief The client object that 'owns' this client
200 
201  \note any potential circular reference problems are accounted
202  for (see publisher alloc for more information)
203  */
205  /*! \brief Underlying publish client */
206  pjsip_publishc *client;
207  /*! \brief The From URI for this specific publisher */
208  char *from_uri;
209  /*! \brief The To URI for this specific publisher */
210  char *to_uri;
211  /*! \brief Timer entry for refreshing publish */
212  pj_timer_entry timer;
213  /*! \brief The number of auth attempts done */
214  unsigned int auth_attempts;
215  /*! \brief Queue of outgoing publish messages to send*/
217  /*! \brief The message currently being sent */
219  /*! \brief Publish client should be destroyed */
220  unsigned int destroy;
221  /*! \brief Serializer for stuff and things */
223  /*! \brief User, if any, associated with the publisher */
224  char user[0];
225 };
226 
227 /*! \brief Outbound publish client state information (persists for lifetime of a publish) */
229  /*! \brief Outbound publish information */
231  /*! \brief Publisher datastores set up by handlers */
233  /*! \brief Container of all the client publishing objects */
235  /*! \brief Publishing has been fully started and event type informed */
236  unsigned int started;
237 };
238 
239 /*! \brief Outbound publish state information (persists for lifetime of a publish) */
241  /*! \brief Outbound publish client */
243  /* publish state id lookup key - same as publish configuration id */
244  char id[0];
245 };
246 
247 /*!
248  * \brief Used for locking while loading/reloading
249  *
250  * Mutli-user configurations make it so publishers can be dynamically added and
251  * removed. Publishers should not be added or removed during a [re]load since
252  * it could cause the current_clients container to be out of sync. Thus the
253  * reason for this lock.
254  */
256 
257 #define DEFAULT_PUBLISHER_BUCKETS 119
260 
261 /*! Time needs to be long enough for a transaction to timeout if nothing replies. */
262 #define MAX_UNLOAD_TIMEOUT_TIME 35 /* Seconds */
263 
264 /*! Shutdown group to monitor sip_outbound_registration_client_state serializers. */
266 
267 /*! \brief Default number of client state container buckets */
268 #define DEFAULT_STATE_BUCKETS 31
269 static AO2_GLOBAL_OBJ_STATIC(current_states);
270 /*! \brief Used on [re]loads to hold new state data */
271 static struct ao2_container *new_states;
272 
273 /*! \brief hashing function for state objects */
274 static int outbound_publish_state_hash(const void *obj, const int flags)
275 {
276  const struct ast_sip_outbound_publish_state *object;
277  const char *key;
278 
279  switch (flags & OBJ_SEARCH_MASK) {
280  case OBJ_SEARCH_KEY:
281  key = obj;
282  break;
283  case OBJ_SEARCH_OBJECT:
284  object = obj;
285  key = object->id;
286  break;
287  default:
288  ast_assert(0);
289  return 0;
290  }
291  return ast_str_hash(key);
292 }
293 
294 /*! \brief comparator function for client objects */
295 static int outbound_publish_state_cmp(void *obj, void *arg, int flags)
296 {
297  const struct ast_sip_outbound_publish_state *object_left = obj;
298  const struct ast_sip_outbound_publish_state *object_right = arg;
299  const char *right_key = arg;
300  int cmp;
301 
302  switch (flags & OBJ_SEARCH_MASK) {
303  case OBJ_SEARCH_OBJECT:
304  right_key = object_right->id;
305  /* Fall through */
306  case OBJ_SEARCH_KEY:
307  cmp = strcmp(object_left->id, right_key);
308  break;
310  /* Not supported by container. */
311  ast_assert(0);
312  return 0;
313  default:
314  cmp = 0;
315  break;
316  }
317  if (cmp) {
318  return 0;
319  }
320  return CMP_MATCH;
321 }
322 
324 {
325  struct ao2_container *container;
327 
328  container = ast_sorcery_retrieve_by_fields(
329  ast_sip_get_sorcery(), "outbound-publish",
331 
332  if (!new_states) {
333  return container;
334  }
335 
336  ao2_global_obj_replace_unref(current_states, new_states);
337  ao2_cleanup(new_states);
338  new_states = NULL;
339 
340  return container;
341 }
342 
344 
346 {
348 }
349 
351 {
352  struct ast_sip_event_publisher_handler *iter;
353 
355  if (!strcmp(iter->event_name, event_name)) {
356  break;
357  }
358  }
359  return iter;
360 }
361 
362 /*! \brief Helper function which cancels the refresh timer on a publisher */
363 static void cancel_publish_refresh(struct sip_outbound_publisher *publisher)
364 {
365  if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
366  &publisher->timer, 0)) {
367  /* The timer was successfully cancelled, drop the refcount of the publisher */
368  ao2_ref(publisher, -1);
369  }
370 }
371 
372 /*! \brief Helper function which sets up the timer to send publication */
373 static void schedule_publish_refresh(struct sip_outbound_publisher *publisher, int expiration)
374 {
375  struct ast_sip_outbound_publish *publish = ao2_bump(publisher->owner->publish);
376  pj_time_val delay = { .sec = 0, };
377 
378  cancel_publish_refresh(publisher);
379 
380  if (expiration > 0) {
381  delay.sec = expiration - PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH;
382  }
383  if (publish->expiration && ((delay.sec > publish->expiration) || !delay.sec)) {
384  delay.sec = publish->expiration;
385  }
386  if (delay.sec < PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH) {
387  delay.sec = PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH;
388  }
389 
390  ao2_ref(publisher, +1);
391  if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &publisher->timer, &delay) != PJ_SUCCESS) {
392  ast_log(LOG_WARNING, "Failed to pass timed publish refresh to scheduler\n");
393  ao2_ref(publisher, -1);
394  }
395  ao2_ref(publish, -1);
396 }
397 
398 static int publisher_client_send(void *obj, void *arg, void *data, int flags);
399 
400 /*! \brief Publish client timer callback function */
401 static void sip_outbound_publish_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
402 {
403  struct sip_outbound_publisher *publisher = entry->user_data;
404 
405  ao2_lock(publisher);
406  if (AST_LIST_EMPTY(&publisher->queue)) {
407  int res;
408  /* If there are no outstanding messages send an empty PUBLISH message so our publication doesn't expire */
409  publisher_client_send(publisher, NULL, &res, 0);
410  }
411  ao2_unlock(publisher);
412 
413  ao2_ref(publisher, -1);
414 }
415 
416 /*! \brief Task for cancelling a refresh timer */
417 static int cancel_refresh_timer_task(void *data)
418 {
419  struct sip_outbound_publisher *publisher = data;
420 
421  cancel_publish_refresh(publisher);
422  ao2_ref(publisher, -1);
423 
424  return 0;
425 }
426 
427 static void set_transport(struct sip_outbound_publisher *publisher, pjsip_tx_data *tdata)
428 {
429  if (!ast_strlen_zero(publisher->owner->publish->transport)) {
430  pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
431 
433  publisher->owner->publish->transport, &selector);
434  pjsip_tx_data_set_transport(tdata, &selector);
435  ast_sip_tpselector_unref(&selector);
436  }
437 }
438 
439 /*! \brief Task for sending an unpublish */
440 static int send_unpublish_task(void *data)
441 {
442  struct sip_outbound_publisher *publisher = data;
443  pjsip_tx_data *tdata;
444 
445  if (pjsip_publishc_unpublish(publisher->client, &tdata) == PJ_SUCCESS) {
446  set_transport(publisher, tdata);
447  pjsip_publishc_send(publisher->client, tdata);
448  }
449 
450  ao2_ref(publisher, -1);
451 
452  return 0;
453 }
454 
457 {
458  if (!handler) {
460  }
461 
462  if (handler) {
463  handler->stop_publishing(client);
464  }
465 }
466 
467 static int cancel_and_unpublish(void *obj, void *arg, int flags);
468 
469 /*! \brief Helper function which starts or stops publish clients when applicable */
471 {
473  struct ao2_container *states;
474  struct ao2_iterator i;
476 
477  if (!publishes) {
478  return;
479  }
480 
481  states = ao2_global_obj_ref(current_states);
482  if (!states) {
483  return;
484  }
485 
486  i = ao2_iterator_init(states, 0);
487  while ((state = ao2_iterator_next(&i))) {
490 
491  if (!state->client->started) {
492  /* If the publisher client has not yet been started try to start it */
493  if (!handler) {
494  ast_debug(2, "Could not find handler for event '%s' for outbound publish client '%s'\n",
495  publish->event, ast_sorcery_object_get_id(publish));
496  } else if (handler->start_publishing(publish, state->client)) {
497  ast_log(LOG_ERROR, "Failed to start outbound publish with event '%s' for client '%s'\n",
498  publish->event, ast_sorcery_object_get_id(publish));
499  } else {
500  state->client->started = 1;
501  }
502  } else if (state->client->started && !handler && removed && !strcmp(publish->event, removed->event_name)) {
503  stop_publishing(state->client, removed);
505  state->client->started = 0;
506  }
507  ao2_ref(publish, -1);
508  ao2_ref(state, -1);
509  }
511  ao2_ref(states, -1);
512 }
513 
515 {
516  struct ao2_container *states = ao2_global_obj_ref(current_states);
517  struct ast_sip_outbound_publish_state *res;
518 
519  if (!states) {
520  return NULL;
521  }
522 
523  res = ao2_find(states, id, OBJ_SEARCH_KEY);
524  ao2_ref(states, -1);
525  return res;
526 }
527 
529 {
531 
532  if (!state) {
533  return NULL;
534  }
535 
536  ao2_ref(state->client, +1);
537  ao2_ref(state, -1);
538  return state->client;
539 }
540 
542 {
543  struct ast_sip_outbound_publish *publish = client->publish;
544 
545  return S_OR(publish->from_uri, S_OR(publish->server_uri, ""));
546 }
547 
549  struct ast_sip_outbound_publish_client *client, const char *user);
550 
552  struct ast_sip_outbound_publish_client *client, const char *user)
553 {
554  struct sip_outbound_publisher *publisher;
555 
556  /*
557  * Lock before searching since there could be a race between searching and adding.
558  * Just use the load_lock since we might need to lock it anyway (if adding) and
559  * also it simplifies the code (otherwise we'd have to lock the publishers, no-
560  * lock the search and pass a flag to 'add publisher to no-lock the potential link).
561  */
563  publisher = ao2_find(client->publishers, user, OBJ_SEARCH_KEY);
564  if (!publisher) {
565  if (!(publisher = sip_outbound_publish_client_add_publisher(client, user))) {
567  return NULL;
568  }
569  }
571 
572  return publisher;
573 }
574 
576  char *uri, size_t size)
577 {
578  struct sip_outbound_publisher *publisher;
579 
580  publisher = sip_outbound_publish_client_get_publisher(client, user);
581  if (!publisher) {
582  return NULL;
583  }
584 
585  ast_copy_string(uri, publisher->from_uri, size);
586  ao2_ref(publisher, -1);
587 
588  return uri;
589 }
590 
592 {
593  struct ast_sip_outbound_publish *publish = client->publish;
594 
595  return S_OR(publish->to_uri, S_OR(publish->server_uri, ""));
596 }
597 
599  char *uri, size_t size)
600 {
601  struct sip_outbound_publisher *publisher;
602 
603  publisher = sip_outbound_publish_client_get_publisher(client, user);
604  if (!publisher) {
605  return NULL;
606  }
607 
608  ast_copy_string(uri, publisher->to_uri, size);
609  ao2_ref(publisher, -1);
610 
611  return uri;
612 }
613 
615 {
616  struct ast_sip_event_publisher_handler *existing;
618 
619  if (!handler->start_publishing || !handler->stop_publishing) {
620  ast_log(LOG_ERROR, "Handler does not implement required callbacks. Cannot register\n");
621  return -1;
622  } else if (ast_strlen_zero(handler->event_name)) {
623  ast_log(LOG_ERROR, "No event package specified for event publisher handler. Cannot register\n");
624  return -1;
625  }
626 
628  if (existing) {
629  ast_log(LOG_ERROR, "Unable to register event publisher handler for event %s. "
630  "A handler is already registered\n", handler->event_name);
631  return -1;
632  }
633 
634  sub_add_handler(handler);
635 
637 
638  return 0;
639 }
640 
642 {
643  struct ast_sip_event_publisher_handler *iter;
646  if (handler == iter) {
648  break;
649  }
650  }
652 
654 }
655 
656 /*! \brief Destructor function for publish information */
657 static void sip_outbound_publish_destroy(void *obj)
658 {
659  struct ast_sip_outbound_publish *publish = obj;
660 
662 
664 }
665 
666 /*! \brief Allocator function for publish information */
667 static void *sip_outbound_publish_alloc(const char *name)
668 {
671 
672  if (!publish || ast_string_field_init(publish, 256)) {
673  ao2_cleanup(publish);
674  return NULL;
675  }
676 
677  return publish;
678 }
679 
681 {
682  struct ast_datastore *datastore = obj;
683 
684  /* Using the destroy function (if present) destroy the data */
685  if (datastore->info->destroy != NULL && datastore->data != NULL) {
686  datastore->info->destroy(datastore->data);
687  datastore->data = NULL;
688  }
689 
690  ast_free((void *) datastore->uid);
691  datastore->uid = NULL;
692 }
693 
695 {
696  RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
697  const char *uid_ptr = uid;
698  char uuid_buf[AST_UUID_STR_LEN];
699 
700  if (!info) {
701  return NULL;
702  }
703 
704  datastore = ao2_alloc(sizeof(*datastore), sip_outbound_publish_datastore_destroy);
705  if (!datastore) {
706  return NULL;
707  }
708 
709  datastore->info = info;
710  if (ast_strlen_zero(uid)) {
711  /* They didn't provide an ID so we'll provide one ourself */
712  uid_ptr = ast_uuid_generate_str(uuid_buf, sizeof(uuid_buf));
713  }
714 
715  datastore->uid = ast_strdup(uid_ptr);
716  if (!datastore->uid) {
717  return NULL;
718  }
719 
720  ao2_ref(datastore, +1);
721  return datastore;
722 }
723 
725  struct ast_datastore *datastore)
726 {
727  ast_assert(datastore != NULL);
728  ast_assert(datastore->info != NULL);
729  ast_assert(!ast_strlen_zero(datastore->uid));
730 
731  if (!ao2_link(client->datastores, datastore)) {
732  return -1;
733  }
734  return 0;
735 }
736 
738  const char *name)
739 {
740  return ao2_find(client->datastores, name, OBJ_SEARCH_KEY);
741 }
742 
744  const char *name)
745 {
747 }
748 
750 {
751  RAII_VAR(struct sip_outbound_publisher *, publisher, data, ao2_cleanup);
752  SCOPED_AO2LOCK(lock, publisher);
754  pjsip_tx_data *tdata;
755  pj_status_t status;
756 
757  if (publisher->destroy || publisher->sending || !(message = AST_LIST_FIRST(&publisher->queue))) {
758  return 0;
759  }
760 
761  if (pjsip_publishc_publish(publisher->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {
762  goto fatal;
763  }
764 
765  if (!ast_strlen_zero(message->body.type) && !ast_strlen_zero(message->body.subtype) &&
766  ast_sip_add_body(tdata, &message->body)) {
767  pjsip_tx_data_dec_ref(tdata);
768  goto fatal;
769  }
770 
771  set_transport(publisher, tdata);
772 
773  status = pjsip_publishc_send(publisher->client, tdata);
774  if (status == PJ_EBUSY) {
775  /* We attempted to send the message but something else got there first */
776  goto service;
777  } else if (status != PJ_SUCCESS) {
778  goto fatal;
779  }
780 
781  publisher->sending = message;
782 
783  return 0;
784 
785 fatal:
786  AST_LIST_REMOVE_HEAD(&publisher->queue, entry);
787  ast_free(message);
788 
789 service:
790  if (ast_sip_push_task(publisher->serializer, sip_publisher_service_queue, ao2_bump(publisher))) {
791  ao2_ref(publisher, -1);
792  }
793  return -1;
794 }
795 
796 static int publisher_client_send(void *obj, void *arg, void *data, int flags)
797 {
798  struct sip_outbound_publisher *publisher = obj;
799  const struct ast_sip_body *body = arg;
801  size_t type_len = 0, subtype_len = 0, body_text_len = 0;
802  int *res = data;
803  SCOPED_AO2LOCK(lock, publisher);
804 
805  *res = -1;
806  if (!publisher->client) {
807  return -1;
808  }
809 
810  /* If a body is present we need more space for the contents of it */
811  if (body) {
812  type_len = strlen(body->type) + 1;
813  subtype_len = strlen(body->subtype) + 1;
814  body_text_len = strlen(body->body_text) + 1;
815  }
816 
817  message = ast_calloc(1, sizeof(*message) + type_len + subtype_len + body_text_len);
818  if (!message) {
819  return -1;
820  }
821 
822  if (body) {
823  char *dst = message->body_contents;
824 
825  message->body.type = strcpy(dst, body->type);
826  dst += type_len;
827  message->body.subtype = strcpy(dst, body->subtype);
828  dst += subtype_len;
829  message->body.body_text = strcpy(dst, body->body_text);
830  }
831 
832  AST_LIST_INSERT_TAIL(&publisher->queue, message, entry);
833 
834  *res = ast_sip_push_task(publisher->serializer, sip_publisher_service_queue, ao2_bump(publisher));
835  if (*res) {
836  ao2_ref(publisher, -1);
837  }
838 
839  return *res;
840 }
841 
843  const struct ast_sip_body *body)
844 {
845  SCOPED_AO2LOCK(lock, client);
846  int res = 0;
847 
849  publisher_client_send, (void *)body, &res);
850  return res;
851 }
852 
854  pj_pool_t *pool, const char *uri, const char *user, pj_str_t *res_uri)
855 {
856  pj_str_t tmp;
857  pjsip_uri *parsed;
858  pjsip_sip_uri *parsed_uri;
859  int size;
860 
861  pj_strdup2_with_null(pool, &tmp, uri);
862  if (!(parsed = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0))) {
863  return -1;
864  }
865 
866  if (!(parsed_uri = pjsip_uri_get_uri(parsed))) {
867  return -1;
868  }
869 
870  if (!ast_strlen_zero(user)) {
871  pj_strdup2(pool, &parsed_uri->user, user);
872  }
873 
874  res_uri->ptr = (char*) pj_pool_alloc(pool, pjsip_max_url_size);
875  if (!res_uri->ptr) {
876  return -1;
877  }
878 
879  if ((size = pjsip_uri_print(PJSIP_URI_IN_OTHER, parsed_uri, res_uri->ptr,
880  pjsip_max_url_size - 1)) <= 0) {
881  return -1;
882  }
883  res_uri->ptr[size] = '\0';
884  res_uri->slen = size;
885 
886  return 0;
887 }
888 
890  pj_pool_t *pool, struct sip_outbound_publisher *publisher,
891  pj_str_t *server_uri, pj_str_t *to_uri, pj_str_t *from_uri)
892 {
893  struct ast_sip_outbound_publish *publish = publisher->owner->publish;
894 
895  if (sip_outbound_publisher_set_uri(pool, publish->server_uri, publisher->user, server_uri)) {
896  ast_log(LOG_ERROR, "Invalid server URI '%s' specified on outbound publish '%s'\n",
897  publish->server_uri, ast_sorcery_object_get_id(publish));
898  return -1;
899  }
900 
901  if (ast_strlen_zero(publish->to_uri)) {
902  to_uri->ptr = server_uri->ptr;
903  to_uri->slen = server_uri->slen;
904  } else if (sip_outbound_publisher_set_uri(pool, publish->to_uri, publisher->user, to_uri)) {
905  ast_log(LOG_ERROR, "Invalid to URI '%s' specified on outbound publish '%s'\n",
906  publish->to_uri, ast_sorcery_object_get_id(publish));
907  return -1;
908  }
909 
910  publisher->to_uri = ast_strdup(to_uri->ptr);
911  if (!publisher->to_uri) {
912  return -1;
913  }
914 
915  if (ast_strlen_zero(publish->from_uri)) {
916  from_uri->ptr = server_uri->ptr;
917  from_uri->slen = server_uri->slen;
918  } else if (sip_outbound_publisher_set_uri(pool, publish->from_uri, publisher->user, from_uri)) {
919  ast_log(LOG_ERROR, "Invalid from URI '%s' specified on outbound publish '%s'\n",
920  publish->from_uri, ast_sorcery_object_get_id(publish));
921  return -1;
922  }
923 
924  publisher->from_uri = ast_strdup(from_uri->ptr);
925  if (!publisher->from_uri) {
926  return -1;
927  }
928 
929  return 0;
930 }
931 
932 static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param);
933 
934 /*! \brief Helper function that allocates a pjsip publish client and configures it */
935 static int sip_outbound_publisher_init(void *data)
936 {
937  struct sip_outbound_publisher *publisher = data;
939  pjsip_publishc_opt opt = {
940  .queue_request = PJ_FALSE,
941  };
942  pj_pool_t *pool;
943  pj_str_t event, server_uri, to_uri, from_uri;
944 
945  if (publisher->client) {
946  return 0;
947  }
948 
949  if (pjsip_publishc_create(ast_sip_get_pjsip_endpoint(), &opt,
951  &publisher->client) != PJ_SUCCESS) {
952  ao2_ref(publisher, -1);
953  return -1;
954  }
955 
956  publish = ao2_bump(publisher->owner->publish);
957 
958  if (!ast_strlen_zero(publish->outbound_proxy)) {
959  pjsip_route_hdr route_set, *route;
960  static const pj_str_t ROUTE_HNAME = { "Route", 5 };
961 
962  pj_list_init(&route_set);
963 
964  if (!(route = pjsip_parse_hdr(pjsip_publishc_get_pool(publisher->client), &ROUTE_HNAME,
965  (char*)publish->outbound_proxy, strlen(publish->outbound_proxy), NULL))) {
966  pjsip_publishc_destroy(publisher->client);
967  return -1;
968  }
969  pj_list_insert_nodes_before(&route_set, route);
970 
971  pjsip_publishc_set_route_set(publisher->client, &route_set);
972  }
973 
974  pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "URI Validation",
976  if (!pool) {
977  ast_log(LOG_ERROR, "Could not create pool for URI validation on outbound publish '%s'\n",
979  pjsip_publishc_destroy(publisher->client);
980  return -1;
981  }
982 
983  if (sip_outbound_publisher_set_uris(pool, publisher, &server_uri, &from_uri, &to_uri)) {
984  pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
985  pjsip_publishc_destroy(publisher->client);
986  return -1;
987  }
988 
989  pj_cstr(&event, publish->event);
990  if (pjsip_publishc_init(publisher->client, &event, &server_uri, &from_uri, &to_uri,
991  publish->expiration) != PJ_SUCCESS) {
992  ast_log(LOG_ERROR, "Failed to initialize publishing client on outbound publish '%s'\n",
994  pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
995  pjsip_publishc_destroy(publisher->client);
996  return -1;
997  }
998 
999  pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1000  return 0;
1001 }
1002 
1003 static int sip_outbound_publisher_reinit(void *obj, void *arg, int flags)
1004 {
1005  return sip_outbound_publisher_init(obj);
1006 }
1007 
1009 {
1011  return 0;
1012 }
1013 
1014 /*! \brief Destructor function for publish client */
1015 static void sip_outbound_publisher_destroy(void *obj)
1016 {
1017  struct sip_outbound_publisher *publisher = obj;
1019 
1020  /* You might be tempted to think "the publish client isn't being destroyed" but it actually is - just elsewhere */
1021 
1022  while ((message = AST_LIST_REMOVE_HEAD(&publisher->queue, entry))) {
1023  ast_free(message);
1024  }
1025 
1026  ao2_cleanup(publisher->owner);
1027  ast_free(publisher->from_uri);
1028  ast_free(publisher->to_uri);
1029 
1031 }
1032 
1034  struct ast_sip_outbound_publish_client *client, const char *user)
1035 {
1036  struct sip_outbound_publisher *publisher;
1037  char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
1038 
1039  publisher = ao2_alloc(sizeof(*publisher) + (user ? strlen(user) : 0) + 1,
1041  if (!publisher) {
1042  return NULL;
1043  }
1044 
1045  /*
1046  * Bump the ref to the client. This essentially creates a circular reference,
1047  * but it is needed in order to make sure the client object doesn't get pulled
1048  * out from under us when the publisher stops publishing.
1049  *
1050  * The circular reference is alleviated by calling cancel_and_unpublish for
1051  * each client, from the state's destructor. By calling it there all references
1052  * to the publishers should go to zero, thus calling the publisher's destructor.
1053  * This in turn removes the client reference we added here. The state then removes
1054  * its reference to the client, which should take it to zero.
1055  */
1056  publisher->owner = ao2_bump(client);
1057  publisher->timer.user_data = publisher;
1058  publisher->timer.cb = sip_outbound_publish_timer_cb;
1059  if (user) {
1060  strcpy(publisher->user, user);
1061  } else {
1062  *publisher->user = '\0';
1063  }
1064 
1065  ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/outpub/%s",
1067 
1068  publisher->serializer = ast_sip_create_serializer_group(tps_name,
1069  shutdown_group);
1070  if (!publisher->serializer) {
1071  ao2_ref(publisher, -1);
1072  return NULL;
1073  }
1074 
1076  ast_log(LOG_ERROR, "Unable to create publisher for outbound publish '%s'\n",
1078  ao2_ref(publisher, -1);
1079  return NULL;
1080  }
1081 
1082  return publisher;
1083 }
1084 
1086  struct ast_sip_outbound_publish_client *client, const char *user)
1087 {
1088  struct sip_outbound_publisher *publisher =
1089  sip_outbound_publisher_alloc(client, user);
1090 
1091  if (!publisher) {
1092  return NULL;
1093  }
1094 
1095  if (!ao2_link(client->publishers, publisher)) {
1096  /*
1097  * No need to bump the reference here. The task will take care of
1098  * removing the reference.
1099  */
1100  if (ast_sip_push_task(publisher->serializer, cancel_refresh_timer_task, publisher)) {
1101  ao2_ref(publisher, -1);
1102  }
1103  return NULL;
1104  }
1105 
1106  return publisher;
1107 }
1108 
1110  const char *user, const struct ast_sip_body *body)
1111 {
1112  struct sip_outbound_publisher *publisher;
1113  int res;
1114 
1115  publisher = sip_outbound_publish_client_get_publisher(client, user);
1116  if (!publisher) {
1117  return -1;
1118  }
1119 
1120  publisher_client_send(publisher, (void *)body, &res, 0);
1121  ao2_ref(publisher, -1);
1122  return res;
1123 }
1124 
1126  const char *user)
1127 {
1130 }
1131 
1132 static int explicit_publish_destroy(void *data)
1133 {
1134  struct sip_outbound_publisher *publisher = data;
1135 
1136  /*
1137  * If there is no pjsip publishing client then we obviously don't need
1138  * to destroy it. Also, the ref for the Asterisk publishing client that
1139  * pjsip had would not exist or should already be gone as well.
1140  */
1141  if (publisher->client) {
1142  pjsip_publishc_destroy(publisher->client);
1143  ao2_ref(publisher, -1);
1144  }
1145 
1146  ao2_ref(publisher, -1);
1147 
1148  return 0;
1149 }
1150 
1151 /*! \brief Helper function which cancels and un-publishes a no longer used client */
1152 static int cancel_and_unpublish(void *obj, void *arg, int flags)
1153 {
1154  struct sip_outbound_publisher *publisher = obj;
1155  struct ast_sip_outbound_publish_client *client = publisher->owner;
1156 
1157  SCOPED_AO2LOCK(lock, publisher);
1158 
1159  if (!client->started) {
1160  /* If the publisher was never started, there's nothing to unpublish, so just
1161  * destroy the publication and remove its reference to the publisher.
1162  */
1163  if (ast_sip_push_task(publisher->serializer, explicit_publish_destroy, ao2_bump(publisher))) {
1164  ao2_ref(publisher, -1);
1165  }
1166  return 0;
1167  }
1168 
1169  if (ast_sip_push_task(publisher->serializer, cancel_refresh_timer_task, ao2_bump(publisher))) {
1170  ast_log(LOG_WARNING, "Could not stop refresh timer on outbound publish '%s'\n",
1172  ao2_ref(publisher, -1);
1173  }
1174 
1175  /* If nothing is being sent right now send the unpublish - the destroy will happen in the subsequent callback */
1176  if (!publisher->sending) {
1177  if (ast_sip_push_task(publisher->serializer, send_unpublish_task, ao2_bump(publisher))) {
1178  ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
1180  ao2_ref(publisher, -1);
1181  }
1182  }
1183  publisher->destroy = 1;
1184  return 0;
1185 }
1186 
1187 /*! \brief Destructor function for publish client */
1189 {
1190  struct ast_sip_outbound_publish_client *client = obj;
1191 
1192  ao2_cleanup(client->datastores);
1193 
1194  /*
1195  * The client's publishers have already been unpublished and destroyed
1196  * by this point, so it is safe to finally remove the reference to the
1197  * publish object. The client needed to hold a reference to it until
1198  * the publishers were done with it.
1199  */
1200  ao2_cleanup(client->publish);
1201 }
1202 
1203 /*! \brief Destructor function for publish state */
1205 {
1206  struct ast_sip_outbound_publish_state *state = obj;
1207 
1208  stop_publishing(state->client, NULL);
1209  /*
1210  * Since the state is being destroyed the associated client needs to also
1211  * be destroyed. However simply removing the reference to the client will
1212  * not initiate client destruction since the client's publisher(s) hold a
1213  * reference to the client object as well. So we need to unpublish the
1214  * the client's publishers here, which will remove the publisher's client
1215  * reference during that process.
1216  *
1217  * That being said we don't want to remove the client's reference to the
1218  * publish object just yet. We'll hold off on that until client destruction
1219  * itself. This is because the publishers need access to the client's
1220  * publish object while they are unpublishing.
1221  */
1223  ao2_cleanup(state->client->publishers);
1224 
1225  state->client->started = 0;
1226  ao2_cleanup(state->client);
1227 }
1228 
1229 /*!
1230  * \internal
1231  * \brief Check if a publish can be reused
1232  *
1233  * This checks if the existing outbound publish's configuration differs from a newly-applied
1234  * outbound publish.
1235  *
1236  * \param existing The pre-existing outbound publish
1237  * \param applied The newly-created publish
1238  */
1239 static int can_reuse_publish(struct ast_sip_outbound_publish *existing, struct ast_sip_outbound_publish *applied)
1240 {
1241  int i;
1242 
1243  if (strcmp(existing->server_uri, applied->server_uri) || strcmp(existing->from_uri, applied->from_uri) ||
1244  strcmp(existing->to_uri, applied->to_uri) || strcmp(existing->outbound_proxy, applied->outbound_proxy) ||
1245  strcmp(existing->event, applied->event) ||
1246  AST_VECTOR_SIZE(&existing->outbound_auths) != AST_VECTOR_SIZE(&applied->outbound_auths)) {
1247  return 0;
1248  }
1249 
1250  for (i = 0; i < AST_VECTOR_SIZE(&existing->outbound_auths); ++i) {
1251  if (strcmp(AST_VECTOR_GET(&existing->outbound_auths, i), AST_VECTOR_GET(&applied->outbound_auths, i))) {
1252  return 0;
1253  }
1254  }
1255 
1256  return 1;
1257 }
1258 
1259 /*! \brief Callback function for publish client responses */
1260 static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
1261 {
1262 #define DESTROY_CLIENT() do { \
1263  pjsip_publishc_destroy(publisher->client); \
1264  publisher->client = NULL; \
1265  ao2_ref(publisher, -1); } while (0)
1266 
1267  RAII_VAR(struct sip_outbound_publisher *, publisher, ao2_bump(param->token), ao2_cleanup);
1268  RAII_VAR(struct ast_sip_outbound_publish *, publish, ao2_bump(publisher->owner->publish), ao2_cleanup);
1269  SCOPED_AO2LOCK(lock, publisher);
1270  pjsip_tx_data *tdata;
1271 
1272  if (publisher->destroy) {
1273  if (publisher->sending) {
1274  publisher->sending = NULL;
1275 
1276  if (!ast_sip_push_task(publisher->serializer, send_unpublish_task, ao2_bump(publisher))) {
1277  return;
1278  }
1279  ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
1281  ao2_ref(publisher, -1);
1282  }
1283  /* Once the destroy is called this callback will not get called any longer, so drop the publisher ref */
1284  DESTROY_CLIENT();
1285  return;
1286  }
1287 
1288  if (param->code == 401 || param->code == 407) {
1289  pjsip_transaction *tsx = pjsip_rdata_get_tsx(param->rdata);
1290 
1291  if (!ast_sip_create_request_with_auth(&publish->outbound_auths,
1292  param->rdata, tsx->last_tx, &tdata)) {
1293  set_transport(publisher, tdata);
1294  pjsip_publishc_send(publisher->client, tdata);
1295  }
1296  publisher->auth_attempts++;
1297 
1298  if (publisher->auth_attempts == publish->max_auth_attempts) {
1299  DESTROY_CLIENT();
1300  ast_log(LOG_ERROR, "Reached maximum number of PUBLISH authentication attempts on outbound publish '%s'\n",
1302 
1303  goto end;
1304  }
1305  return;
1306  }
1307 
1308  publisher->auth_attempts = 0;
1309 
1310  if (param->code == 412) {
1311  DESTROY_CLIENT();
1312  if (sip_outbound_publisher_init(publisher)) {
1313  ast_log(LOG_ERROR, "Failed to create a new outbound publish client for '%s' on 412 response\n",
1315  goto end;
1316  }
1317 
1318  /* Setting this to NULL will cause a new PUBLISH to get created and sent for the same underlying body */
1319  publisher->sending = NULL;
1320  } else if (param->code == 423) {
1321  /* Update the expiration with the new expiration time if available */
1322  pjsip_expires_hdr *expires;
1323 
1324  expires = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_MIN_EXPIRES, NULL);
1325  if (!expires || !expires->ivalue) {
1326  DESTROY_CLIENT();
1327  ast_log(LOG_ERROR, "Received 423 response on outbound publish '%s' without a Min-Expires header\n",
1329  goto end;
1330  }
1331 
1332  pjsip_publishc_update_expires(publisher->client, expires->ivalue);
1333  publisher->sending = NULL;
1334  } else if (publisher->sending) {
1335  /* Remove the message currently being sent so that when the queue is serviced another will get sent */
1336  AST_LIST_REMOVE_HEAD(&publisher->queue, entry);
1337  ast_free(publisher->sending);
1338  publisher->sending = NULL;
1339  if (!param->rdata) {
1340  ast_log(LOG_NOTICE, "No response received for outbound publish '%s'\n",
1342  }
1343  }
1344 
1345  if (AST_LIST_EMPTY(&publisher->queue)) {
1346  schedule_publish_refresh(publisher, param->expiration);
1347  }
1348 
1349 end:
1350  if (!publisher->client) {
1352 
1353  while ((message = AST_LIST_REMOVE_HEAD(&publisher->queue, entry))) {
1354  ast_free(message);
1355  }
1356  } else {
1357  if (ast_sip_push_task(publisher->serializer, sip_publisher_service_queue, ao2_bump(publisher))) {
1358  ao2_ref(publisher, -1);
1359  }
1360  }
1361 }
1362 
1363 #define DATASTORE_BUCKETS 53
1364 
1365 static int datastore_hash(const void *obj, int flags)
1366 {
1367  const struct ast_datastore *datastore;
1368  const char *uid;
1369 
1370  switch (flags & OBJ_SEARCH_MASK) {
1371  case OBJ_SEARCH_KEY:
1372  uid = obj;
1373  break;
1374  case OBJ_SEARCH_OBJECT:
1375  datastore = obj;
1376  uid = datastore->uid;
1377  break;
1378  default:
1379  /* Hash can only work on something with a full key. */
1380  ast_assert(0);
1381  return 0;
1382  }
1383 
1384  return ast_str_hash(uid);
1385 }
1386 
1387 static int datastore_cmp(void *obj, void *arg, int flags)
1388 {
1389  const struct ast_datastore *object_left = obj;
1390  const struct ast_datastore *object_right = arg;
1391  const char *right_key = arg;
1392  int cmp;
1393 
1394  switch (flags & OBJ_SEARCH_MASK) {
1395  case OBJ_SEARCH_OBJECT:
1396  right_key = object_right->uid;
1397  /* Fall through */
1398  case OBJ_SEARCH_KEY:
1399  cmp = strcmp(object_left->uid, right_key);
1400  break;
1402  cmp = strncmp(object_left->uid, right_key, strlen(right_key));
1403  break;
1404  default:
1405  /*
1406  * What arg points to is specific to this traversal callback
1407  * and has no special meaning to astobj2.
1408  */
1409  cmp = 0;
1410  break;
1411  }
1412  if (cmp) {
1413  return 0;
1414  }
1415  /*
1416  * At this point the traversal callback is identical to a sorted
1417  * container.
1418  */
1419  return CMP_MATCH;
1420 }
1421 
1422 /*! \brief Allocator function for publish client */
1425 {
1426  const char *id = ast_sorcery_object_get_id(publish);
1428  ao2_alloc(sizeof(*state) + strlen(id) + 1, sip_outbound_publish_state_destroy);
1429 
1430  if (!state) {
1431  return NULL;
1432  }
1433 
1434  state->client = ao2_alloc(sizeof(*state->client), sip_outbound_publish_client_destroy);
1435  if (!state->client) {
1436  ao2_ref(state, -1);
1437  return NULL;
1438  }
1439 
1442  if (!state->client->datastores) {
1443  ao2_ref(state, -1);
1444  return NULL;
1445  }
1446 
1449  sip_outbound_publisher_hash_fn, NULL, sip_outbound_publisher_cmp_fn);
1450  if (!state->client->publishers) {
1451  ao2_ref(state, -1);
1452  return NULL;
1453  }
1454 
1455  state->client->publish = ao2_bump(publish);
1456 
1457  strcpy(state->id, id);
1458  return state;
1459 }
1460 
1462 {
1463  if (ast_strlen_zero(publish->server_uri)) {
1464  ast_log(LOG_ERROR, "No server URI specified on outbound publish '%s'\n",
1465  ast_sorcery_object_get_id(publish));
1466  return -1;
1467  } else if (ast_sip_validate_uri_length(publish->server_uri)) {
1468  ast_log(LOG_ERROR, "Server URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s' on outbound publish '%s'\n",
1469  publish->server_uri,
1470  ast_sorcery_object_get_id(publish));
1471  return -1;
1472  } else if (ast_strlen_zero(publish->event)) {
1473  ast_log(LOG_ERROR, "No event type specified for outbound publish '%s'\n",
1474  ast_sorcery_object_get_id(publish));
1475  return -1;
1476  } else if (!ast_strlen_zero(publish->from_uri)
1477  && ast_sip_validate_uri_length(publish->from_uri)) {
1478  ast_log(LOG_ERROR, "From URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s' on outbound publish '%s'\n",
1479  publish->from_uri,
1480  ast_sorcery_object_get_id(publish));
1481  return -1;
1482  } else if (!ast_strlen_zero(publish->to_uri)
1483  && ast_sip_validate_uri_length(publish->to_uri)) {
1484  ast_log(LOG_ERROR, "To URI or hostname length exceeds pjproject limit or is not a sip(s) uri: '%s' on outbound publish '%s'\n",
1485  publish->to_uri,
1486  ast_sorcery_object_get_id(publish));
1487  return -1;
1488  }
1489  return 0;
1490 }
1491 
1493  struct ast_sip_outbound_publish_state *current_state)
1494 {
1495  struct ast_sip_outbound_publish *old_publish;
1496 
1497  /*
1498  * Don't maintain the old state/client objects if the multi_user option changed.
1499  */
1500  if ((!publish->multi_user && current_state->client->publish->multi_user) ||
1501  (publish->multi_user && !current_state->client->publish->multi_user)) {
1502  return 0;
1503  }
1504 
1505 
1506  if (!can_reuse_publish(current_state->client->publish, publish)) {
1507  /*
1508  * Something significant has changed in the configuration, so we are
1509  * unable to use the old state object. The current state needs to go
1510  * away and a new one needs to be created.
1511  */
1512  return 0;
1513  }
1514 
1515  /*
1516  * We can reuse the current state object so keep it, but swap out the
1517  * underlying publish object with the new one.
1518  */
1519  old_publish = current_state->client->publish;
1520  current_state->client->publish = publish;
1522  current_state->client->publishers)) {
1523  /*
1524  * If the state object fails to re-initialize then swap
1525  * the old publish info back in.
1526  */
1527  current_state->client->publish = publish;
1528  ast_log(LOG_ERROR, "Unable to reinitialize client(s) for outbound publish '%s'\n",
1529  ast_sorcery_object_get_id(current_state->client->publish));
1530  return -1;
1531  }
1532 
1533  /*
1534  * Since we swapped out the publish object the new one needs a ref
1535  * while the old one needs to go away.
1536  */
1537  ao2_ref(current_state->client->publish, +1);
1538  ao2_cleanup(old_publish);
1539 
1540  /* Tell the caller that the current state object should be used */
1541  return 1;
1542 }
1543 
1544 /*! \brief Apply function which finds or allocates a state structure */
1545 static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj)
1546 {
1547 #define ADD_TO_NEW_STATES(__obj) \
1548  do { if (__obj) { \
1549  ao2_link(new_states, __obj); \
1550  ao2_ref(__obj, -1); } } while (0)
1551 
1552  struct ast_sip_outbound_publish *applied = obj;
1553  struct ast_sip_outbound_publish_state *current_state, *new_state;
1554  struct sip_outbound_publisher *publisher = NULL;
1555  int res;
1556 
1557  /*
1558  * New states are being loaded or reloaded. We'll need to add the new
1559  * object if created/updated, or keep the old object if an error occurs.
1560  */
1561  if (!new_states) {
1562  new_states = ao2_container_alloc_hash(
1565 
1566  if (!new_states) {
1567  ast_log(LOG_ERROR, "Unable to allocate new states container\n");
1568  return -1;
1569  }
1570  }
1571 
1572  /* If there is current state we'll want to maintain it if any errors occur */
1573  current_state = sip_publish_state_get(ast_sorcery_object_get_id(applied));
1574 
1575  if ((res = validate_publish_config(applied))) {
1576  ADD_TO_NEW_STATES(current_state);
1577  return res;
1578  }
1579 
1580  if (current_state && (res = current_state_reusable(applied, current_state))) {
1581  /*
1582  * The current state object was able to be reused, or an error
1583  * occurred. Either way we keep the current state and be done.
1584  */
1585  ADD_TO_NEW_STATES(current_state);
1586  return res == 1 ? 0 : -1;
1587  }
1588 
1589  /*
1590  * No current state was found or it was unable to be reused. Either way
1591  * we'll need to create a new state object.
1592  */
1593  new_state = sip_outbound_publish_state_alloc(applied);
1594  if (!new_state) {
1595  ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n",
1596  ast_sorcery_object_get_id(applied));
1597  ADD_TO_NEW_STATES(current_state);
1598  return -1;
1599  };
1600 
1601  if (!applied->multi_user &&
1602  !(publisher = sip_outbound_publish_client_add_publisher(new_state->client, NULL))) {
1603  ADD_TO_NEW_STATES(current_state);
1604  ao2_ref(new_state, -1);
1605  return -1;
1606  }
1607  ao2_cleanup(publisher);
1608 
1609  ADD_TO_NEW_STATES(new_state);
1610  ao2_cleanup(current_state);
1611  return res;
1612 }
1613 
1614 static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1615 {
1616  struct ast_sip_outbound_publish *publish = obj;
1617 
1618  return ast_sip_auth_vector_init(&publish->outbound_auths, var->value);
1619 }
1620 
1621 
1622 static int unload_module(void)
1623 {
1624  int remaining;
1625 
1626  ast_sorcery_object_unregister(ast_sip_get_sorcery(), "outbound-publish");
1627 
1628  ao2_global_obj_release(current_states);
1629 
1630  /* Wait for publication serializers to get destroyed. */
1631  ast_debug(2, "Waiting for publication to complete for unload.\n");
1632  remaining = ast_serializer_shutdown_group_join(shutdown_group, MAX_UNLOAD_TIMEOUT_TIME);
1633  if (remaining) {
1634  ast_log(LOG_WARNING, "Unload incomplete. Could not stop %d outbound publications. Try again later.\n",
1635  remaining);
1636  return -1;
1637  }
1638 
1639  ast_debug(2, "Successful shutdown.\n");
1640 
1641  ao2_cleanup(shutdown_group);
1642  shutdown_group = NULL;
1643 
1644  return 0;
1645 }
1646 
1647 static int load_module(void)
1648 {
1649  /* As of pjproject 2.4.5, PJSIP_MAX_URL_SIZE isn't exposed yet but we try anyway. */
1650  ast_pjproject_get_buildopt("PJSIP_MAX_URL_SIZE", "%d", &pjsip_max_url_size);
1651 
1652  shutdown_group = ast_serializer_shutdown_group_alloc();
1653  if (!shutdown_group) {
1654  return AST_MODULE_LOAD_DECLINE;
1655  }
1656 
1657  ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_outbound_publish");
1658  ast_sorcery_apply_default(ast_sip_get_sorcery(), "outbound-publish", "config", "pjsip.conf,criteria=type=outbound-publish");
1659 
1662  ast_log(LOG_ERROR, "Unable to register 'outbound-publish' type with sorcery\n");
1663  unload_module();
1664  return AST_MODULE_LOAD_DECLINE;
1665  }
1666 
1667  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "type", "", OPT_NOOP_T, 0, 0);
1673  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_outbound_publish, expiration));
1674  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "max_auth_attempts", "5", OPT_UINT_T, 0, FLDSET(struct ast_sip_outbound_publish, max_auth_attempts));
1676  ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "outbound-publish", "outbound_auth", "", outbound_auth_handler, NULL, NULL, 0, 0);
1677  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "multi_user", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_outbound_publish, multi_user));
1678 
1679  ast_sorcery_reload_object(ast_sip_get_sorcery(), "outbound-publish");
1680 
1684 
1685  pjsip_publishc_init_module(ast_sip_get_pjsip_endpoint());
1686 
1687  return AST_MODULE_LOAD_SUCCESS;
1688 }
1689 
1690 static int reload_module(void)
1691 {
1692  ast_sorcery_reload_object(ast_sip_get_sorcery(), "outbound-publish");
1693 
1697  return 0;
1698 }
1699 
1701  .support_level = AST_MODULE_SUPPORT_CORE,
1702  .load = load_module,
1703  .reload = reload_module,
1704  .unload = unload_module,
1705  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1706  .requires = "res_pjproject,res_pjsip",
1707 );
struct sip_outbound_publisher::@476 queue
Queue of outgoing publish messages to send.
enum sip_cc_notify_state state
Definition: chan_sip.c:957
const char * body_text
Definition: res_pjsip.h:2020
static int publisher_client_send(void *obj, void *arg, void *data, int flags)
static int sip_outbound_publisher_reinit_all(void *data)
void ast_taskprocessor_build_name(char *buf, unsigned int size, const char *format,...)
Build a taskprocessor name with a sequence number on the end.
int ast_sip_auth_vector_init(struct ast_sip_auth_vector *vector, const char *auth_names)
Initialize an auth vector with the configured values.
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
#define AST_RWLOCK_DEFINE_STATIC(rwlock)
Definition: lock.h:541
AO2_STRING_FIELD_HASH_FN(sip_outbound_publisher, user)
static int unload_module(void)
#define DEFAULT_STATE_BUCKETS
Default number of client state container buckets.
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
Definition: linkedlists.h:332
struct sip_outbound_publish_message::@475 entry
Linked list information.
unsigned int started
Publishing has been fully started and event type informed.
#define AST_UUID_STR_LEN
Definition: uuid.h:27
static ast_rwlock_t load_lock
Used for locking while loading/reloading.
#define SCOPED_WRLOCK(varname, lock)
scoped lock specialization for write locks
Definition: lock.h:597
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1105
static int datastore_hash(const void *obj, int flags)
void ast_sip_unregister_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
Unregister a publish handler.
static int sip_outbound_publisher_set_uris(pj_pool_t *pool, struct sip_outbound_publisher *publisher, pj_str_t *server_uri, pj_str_t *to_uri, pj_str_t *from_uri)
struct ast_sip_auth_vector outbound_auths
Configured authentication credentials.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
void ast_sip_auth_vector_destroy(struct ast_sip_auth_vector *vector)
Free contents of an auth vector.
#define LOG_WARNING
Definition: logger.h:274
static void sip_outbound_publish_state_destroy(void *obj)
Destructor function for publish state.
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
struct ast_serializer_shutdown_group * ast_serializer_shutdown_group_alloc(void)
Create a serializer group shutdown control object.
Definition: threadpool.c:1229
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.
static int tmp()
Definition: bt_open.c:389
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
int(* start_publishing)(struct ast_sip_outbound_publish *configuration, struct ast_sip_outbound_publish_client *client)
Called when a publisher should start publishing.
const ast_string_field outbound_proxy
Outbound proxy to use.
#define DESTROY_CLIENT()
Structure for variables, used for configurations and for channel variables.
char user[0]
User, if any, associated with the publisher.
#define var
Definition: ast_expr2f.c:614
static int explicit_publish_destroy(void *data)
Perform no matching, return all objects.
Definition: sorcery.h:123
enum ast_cc_service_type service
Definition: chan_sip.c:947
static void stop_publishing(struct ast_sip_outbound_publish_client *client, struct ast_sip_event_publisher_handler *handler)
Full structure for sorcery.
Definition: sorcery.c:230
Type for a default handler that should do nothing.
void ast_sip_publish_client_remove(struct ast_sip_outbound_publish_client *client, const char *user)
Remove the user from the client (stopping it from publishing)
Structure for a data store type.
Definition: datastore.h:31
Definition: astman.c:222
struct ast_datastore * ast_sip_publish_client_get_datastore(struct ast_sip_outbound_publish_client *client, const char *name)
Retrieve an event publisher datastore.
#define ao2_global_obj_ref(holder)
Definition: astobj2.h:925
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#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
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
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 ast_assert(a)
Definition: utils.h:650
#define ao2_unlock(a)
Definition: astobj2.h:730
struct ast_sip_outbound_publish_client * client
Outbound publish client.
int ast_pjproject_get_buildopt(char *option, char *format_string,...)
Retrieve a pjproject build option.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Structure for a data store object.
Definition: datastore.h:68
#define NULL
Definition: resample.c:96
AO2_STRING_FIELD_CMP_FN(sip_outbound_publisher, user)
char * end
Definition: eagi_proxy.c:73
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
Definition: taskprocessor.h:60
int ast_serializer_shutdown_group_join(struct ast_serializer_shutdown_group *shutdown_group, int timeout)
Wait for the serializers in the group to shutdown with timeout.
Definition: threadpool.c:1241
int ast_sip_push_task_wait_servant(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Push a task to SIP servants and wait for it to complete.
Definition: res_pjsip.c:5089
static void sip_outbound_publish_datastore_destroy(void *obj)
#define ast_rwlock_unlock(a)
Definition: lock.h:232
struct ast_sip_body body
Optional body.
Outbound publish state information (persists for lifetime of a publish)
static int sip_outbound_publisher_set_uri(pj_pool_t *pool, const char *uri, const char *user, pj_str_t *res_uri)
static struct ast_sip_outbound_publish_state * sip_outbound_publish_state_alloc(struct ast_sip_outbound_publish *publish)
Allocator function for publish client.
static int sip_publisher_service_queue(void *data)
char * to_uri
The To URI for this specific publisher.
unsigned int multi_user
The publishing client is used for multiple users when true.
const char * uid
Definition: datastore.h:69
unsigned char publish
Definition: res_corosync.c:241
static int sip_outbound_publisher_reinit(void *obj, void *arg, int flags)
Asterisk datastore objects.
#define ao2_bump(obj)
Definition: astobj2.h:491
int(* stop_publishing)(struct ast_sip_outbound_publish_client *client)
Called when a publisher should stop publishing.
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
static int current_state_reusable(struct ast_sip_outbound_publish *publish, struct ast_sip_outbound_publish_state *current_state)
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#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
static void sip_outbound_publish_destroy(void *obj)
Destructor function for publish information.
const char * ast_sip_publish_client_get_user_from_uri(struct ast_sip_outbound_publish_client *client, const char *user, char *uri, size_t size)
Get the From URI the client will use for a specific user.
char body_contents[0]
Extra space for body contents.
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Definition: astobj2.h:1120
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
struct sip_outbound_publish_message * next
Outbound publish client state information (persists for lifetime of a publish)
int ast_sorcery_object_unregister(struct ast_sorcery *sorcery, const char *type)
Unregister an object type.
Definition: sorcery.c:1061
int ast_sip_add_body(pjsip_tx_data *tdata, const struct ast_sip_body *body)
Add a body to an outbound SIP message.
Definition: res_pjsip.c:4976
#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:851
Type for default option handler for unsigned integers.
static int send_unpublish_task(void *data)
Task for sending an unpublish.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
const char * type
Definition: res_pjsip.h:2016
Callbacks that event publisher handlers will define.
ast_mutex_t lock
Definition: app_meetme.c:1091
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
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
static int datastore_cmp(void *obj, void *arg, int flags)
#define ao2_ref(o, delta)
Definition: astobj2.h:464
const char * ast_sip_publish_client_get_to_uri(struct ast_sip_outbound_publish_client *client)
Get the To URI the client will use.
const struct ast_datastore_info * info
Definition: datastore.h:71
void(* destroy)(void *data)
Definition: datastore.h:34
#define ao2_lock(a)
Definition: astobj2.h:718
#define AST_RWLIST_REMOVE_CURRENT
Definition: linkedlists.h:569
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
static void * sip_outbound_publish_alloc(const char *name)
Allocator function for publish information.
#define DATASTORE_BUCKETS
#define SCOPED_LOCK(varname, lock, lockfunc, unlockfunc)
Scoped Locks.
Definition: lock.h:581
#define ADD_TO_NEW_STATES(__obj)
struct ao2_container * container
Definition: res_fax.c:502
struct ast_taskprocessor * ast_sip_create_serializer_group(const char *name, struct ast_serializer_shutdown_group *shutdown_group)
Create a new serializer for SIP tasks.
Definition: res_pjsip.c:5013
int ast_sip_create_request_with_auth(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge, pjsip_tx_data *tdata, pjsip_tx_data **new_request)
Create a response to an authentication challenge.
Definition: res_pjsip.c:3308
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
static int pjsip_max_url_size
struct ao2_container * datastores
Publisher datastores set up by handlers.
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
static struct ast_sip_event_publisher_handler * find_publisher_handler_for_event_name(const char *event_name)
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:838
static void sip_outbound_publish_client_destroy(void *obj)
Destructor function for publish client.
static struct sip_outbound_publisher * sip_outbound_publish_client_add_publisher(struct ast_sip_outbound_publish_client *client, const char *user)
pj_timer_entry timer
Timer entry for refreshing publish.
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:544
struct ast_taskprocessor * serializer
Serializer for stuff and things.
static void cancel_publish_refresh(struct sip_outbound_publisher *publisher)
Helper function which cancels the refresh timer on a publisher.
static struct sip_outbound_publisher * sip_outbound_publish_client_get_publisher(struct ast_sip_outbound_publish_client *client, const char *user)
static void sip_outbound_publish_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
Publish client timer callback function.
static struct ast_serializer_shutdown_group * shutdown_group
static int cancel_and_unpublish(void *obj, void *arg, int flags)
Helper function which cancels and un-publishes a no longer used client.
char * from_uri
The From URI for this specific publisher.
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.
static int sip_outbound_publisher_init(void *data)
Helper function that allocates a pjsip publish client and configures it.
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
struct ast_sip_outbound_publish_client * owner
The client object that &#39;owns&#39; this client.
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
const ast_string_field transport
Explicit transport to use for publish.
int ast_sip_register_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
Register an event publisher handler.
#define SORCERY_OBJECT(details)
Macro which must be used at the beginning of each sorcery capable object.
Definition: sorcery.h:356
static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj)
Apply function which finds or allocates a state structure.
static void sub_add_handler(struct ast_sip_event_publisher_handler *handler)
const char * ast_sip_publish_client_get_user_to_uri(struct ast_sip_outbound_publish_client *client, const char *user, char *uri, size_t size)
Get the To URI the client will use for a specific user.
pjsip_publishc * client
Underlying publish client.
static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static int outbound_publish_state_hash(const void *obj, const int flags)
hashing function for state objects
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:5023
unsigned int max_auth_attempts
Maximum number of auth attempts before stopping the publish client.
#define ao2_global_obj_release(holder)
Definition: astobj2.h:865
Type for default option handler for bools (ast_true/ast_false)
def info(msg)
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition: sorcery.h:477
int ast_sip_publish_client_user_send(struct ast_sip_outbound_publish_client *client, const char *user, const struct ast_sip_body *body)
Send an outgoing PUBLISH message based on the user.
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1743
const char * subtype
Definition: res_pjsip.h:2018
unsigned int auth_attempts
The number of auth attempts done.
Outbound publish information.
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
const ast_string_field server_uri
URI for the entity and server.
Queued outbound publish message.
#define LOG_NOTICE
Definition: logger.h:263
struct ast_sip_outbound_publish * publish
Outbound publish information.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define ast_strlen_zero(a)
Definition: muted.c:73
struct ast_datastore * ast_sip_publish_client_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:602
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static char server_uri[512]
static int reload(void)
Definition: cdr_mysql.c:741
static int load_module(void)
static struct sip_outbound_publisher * sip_outbound_publisher_alloc(struct ast_sip_outbound_publish_client *client, const char *user)
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:143
static struct ao2_container * get_publishes_and_update_state(void)
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
static void schedule_publish_refresh(struct sip_outbound_publisher *publisher, int expiration)
Helper function which sets up the timer to send publication.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
An API for managing task processing threads that can be shared across modules.
const char * ast_sip_publish_client_get_from_uri(struct ast_sip_outbound_publish_client *client)
Get the From URI the client will use.
structure to hold users read from users.conf
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
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:3614
#define ast_rwlock_wrlock(a)
Definition: lock.h:234
const ast_string_field event
The event type to publish.
#define ao2_global_obj_replace_unref(holder, obj)
Definition: astobj2.h:908
struct ao2_container * publishers
Container of all the client publishing objects.
void * data
Definition: datastore.h:70
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:740
static struct ast_sip_outbound_publish_state * sip_publish_state_get(const char *id)
The arg parameter is an object of the same type.
Definition: astobj2.h:1091
struct sip_outbound_publish_message * sending
The message currently being sent.
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
static void sip_outbound_publish_synchronize(struct ast_sip_event_publisher_handler *removed)
Helper function which starts or stops publish clients when applicable.
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
int ast_sip_validate_uri_length(const char *uri)
Definition: location.c:525
static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
Callback function for publish client responses.
static struct ast_sorcery * sorcery
static int reload_module(void)
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const ast_string_field from_uri
URI for the From header.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
static struct ao2_container * new_states
Used on [re]loads to hold new state data.
static void handler(const char *name, int response_code, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Definition: test_ari.c:59
struct ast_sip_outbound_publish_client * ast_sip_publish_client_get(const char *name)
Find a publish client using its name.
Type for default option handler for stringfields.
unsigned int expiration
Requested expiration time.
unsigned int destroy
Publish client should be destroyed.
Definition: search.h:40
static int can_reuse_publish(struct ast_sip_outbound_publish *existing, struct ast_sip_outbound_publish *applied)
static int validate_publish_config(struct ast_sip_outbound_publish *publish)
static void sip_outbound_publisher_destroy(void *obj)
Destructor function for publish client.
Generic container type.
const ast_string_field to_uri
URI for the To header.
Search option field mask.
Definition: astobj2.h:1076
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
void ast_sip_tpselector_unref(pjsip_tpselector *selector)
Unreference a pjsip_tpselector.
Definition: res_pjsip.c:3872
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define MAX_UNLOAD_TIMEOUT_TIME
static AO2_GLOBAL_OBJ_STATIC(current_states)
static int cancel_refresh_timer_task(void *data)
Task for cancelling a refresh timer.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
static void set_transport(struct sip_outbound_publisher *publisher, pjsip_tx_data *tdata)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
SIP body description.
Definition: res_pjsip.h:2014
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:616
static int outbound_publish_state_cmp(void *obj, void *arg, int flags)
comparator function for client objects
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
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
jack_status_t status
Definition: app_jack.c:146
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1206
int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip_tpselector *selector)
Sets pjsip_tpselector from ast_sip_transport.
Definition: res_pjsip.c:3842
#define ao2_link(container, obj)
Definition: astobj2.h:1549