Asterisk - The Open Source Telephony Project GIT-master-7e7a603
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
32#include "asterisk/res_pjsip.h"
34#include "asterisk/module.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
124static 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 */
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 */
173 /*! \brief Stringfields */
175 /*! \brief URI for the entity and server */
177 /*! \brief URI for the From header */
179 /*! \brief URI for the To header */
181 /*! \brief Explicit transport to use for publish */
183 /*! \brief Outbound proxy to use */
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 */
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
269static AO2_GLOBAL_OBJ_STATIC(current_states);
270/*! \brief Used on [re]loads to hold new state data */
272
273/*! \brief hashing function for state objects */
274static 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;
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 */
295static 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) {
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
329 ast_sip_get_sorcery(), "outbound-publish",
331
332 if (!new_states) {
333 return container;
334 }
335
339
340 return container;
341}
342
344
346{
348}
349
351{
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 */
363static 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 */
373static void schedule_publish_refresh(struct sip_outbound_publisher *publisher, int expiration)
374{
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
398static int publisher_client_send(void *obj, void *arg, void *data, int flags);
399
400/*! \brief Publish client timer callback function */
401static 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 */
417static 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
427static 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 */
440static 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
467static 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))) {
488 struct ast_sip_outbound_publish *publish = ao2_bump(state->client->publish);
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",
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",
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);
504 ao2_callback(state->client->publishers, OBJ_NODATA, cancel_and_unpublish, NULL);
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);
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) {
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
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
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
627 existing = find_publisher_handler_for_event_name(handler->event_name);
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
635
637
638 return 0;
639}
640
642{
646 if (handler == iter) {
648 break;
649 }
650 }
652
654}
655
656/*! \brief Destructor function for publish information */
657static void sip_outbound_publish_destroy(void *obj)
658{
659 struct ast_sip_outbound_publish *publish = obj;
660
661 ast_sip_auth_vector_destroy(&publish->outbound_auths);
662
664}
665
666/*! \brief Allocator function for publish information */
667static void *sip_outbound_publish_alloc(const char *name)
668{
671
672 if (!publish || ast_string_field_init(publish, 256)) {
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
785fatal:
786 AST_LIST_REMOVE_HEAD(&publisher->queue, entry);
788
789service:
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
796static 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
833
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",
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",
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",
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
932static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param);
933
934/*! \brief Helper function that allocates a pjsip publish client and configures it */
935static 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
1003static 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 */
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))) {
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,
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 =
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
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
1132static 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 */
1152static 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{
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 */
1239static 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) ||
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 */
1260static 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 */
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) {
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) {
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) {
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
1349end:
1350 if (!publisher->client) {
1352
1353 while ((message = AST_LIST_REMOVE_HEAD(&publisher->queue, entry))) {
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
1365static 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
1387static 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",
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,
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",
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,
1481 return -1;
1482 } else if (!ast_strlen_zero(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,
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 */
1545static 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) {
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
1614static 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
1622static int unload_module(void)
1623{
1624 int remaining;
1625
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");
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
1643
1644 return 0;
1645}
1646
1647static 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
1653 if (!shutdown_group) {
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();
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
1688}
1689
1690static 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,
1704 .unload = unload_module,
1705 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1706 .requires = "res_pjproject,res_pjsip",
jack_status_t status
Definition: app_jack.c:146
ast_mutex_t lock
Definition: app_sla.c:331
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
Definition: astobj2.h:901
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1723
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_SEARCH_PARTIAL_KEY
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Definition: astobj2.h:1116
@ OBJ_SEARCH_OBJECT
The arg parameter is an object of the same type.
Definition: astobj2.h:1087
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_SEARCH_MASK
Search option field mask.
Definition: astobj2.h:1072
@ OBJ_UNLINK
Definition: astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
static int tmp()
Definition: bt_open.c:389
enum ast_cc_service_type service
Definition: ccss.c:383
enum cc_state state
Definition: ccss.c:393
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
@ OPT_UINT_T
Type for default option handler for unsigned integers.
@ OPT_NOOP_T
Type for a default handler that should do nothing.
@ OPT_BOOL_T
Type for default option handler for bools (ast_true/ast_false)
@ OPT_STRINGFIELD_T
Type for default option handler for stringfields.
Asterisk datastore objects.
char * end
Definition: eagi_proxy.c:73
static const char name[]
Definition: format_mp3.c:68
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:2099
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:2089
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:2165
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
#define AST_RWLIST_REMOVE_CURRENT
Definition: linkedlists.h:570
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:545
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#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:333
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:617
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:741
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
#define ast_rwlock_wrlock(a)
Definition: lock.h:236
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:604
#define AST_RWLOCK_DEFINE_STATIC(rwlock)
Definition: lock.h:543
#define SCOPED_WRLOCK(varname, lock)
scoped lock specialization for write locks
Definition: lock.h:599
#define SCOPED_LOCK(varname, lock, lockfunc, unlockfunc)
Scoped Locks.
Definition: lock.h:583
#define ast_rwlock_unlock(a)
Definition: lock.h:234
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:316
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:326
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
def info(msg)
static int reload(void)
unsigned char publish
Definition: res_corosync.c:241
struct ao2_container * container
Definition: res_fax.c:501
int ast_pjproject_get_buildopt(char *option, char *format_string,...)
Retrieve a pjproject build option.
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:214
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:520
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:2052
void ast_sip_tpselector_unref(pjsip_tpselector *selector)
Unreference a pjsip_tpselector.
Definition: res_pjsip.c:923
void ast_sip_auth_vector_destroy(struct ast_sip_auth_vector *vector)
Free contents of an auth vector.
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:893
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
int ast_sip_auth_vector_init(struct ast_sip_auth_vector *vector, const char *auth_names)
Initialize an auth vector with the configured values.
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.
static struct ast_sip_event_publisher_handler * find_publisher_handler_for_event_name(const char *event_name)
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.
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.
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.
static int can_reuse_publish(struct ast_sip_outbound_publish *existing, struct ast_sip_outbound_publish *applied)
void ast_sip_unregister_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
Unregister a publish handler.
static void sub_add_handler(struct ast_sip_event_publisher_handler *handler)
static int current_state_reusable(struct ast_sip_outbound_publish *publish, struct ast_sip_outbound_publish_state *current_state)
static void stop_publishing(struct ast_sip_outbound_publish_client *client, struct ast_sip_event_publisher_handler *handler)
static ast_rwlock_t load_lock
Used for locking while loading/reloading.
static void sip_outbound_publish_synchronize(struct ast_sip_event_publisher_handler *removed)
Helper function which starts or stops publish clients when applicable.
static struct ast_serializer_shutdown_group * shutdown_group
AO2_STRING_FIELD_CMP_FN(sip_outbound_publisher, user)
static struct ast_sip_outbound_publish_state * sip_publish_state_get(const char *id)
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)
static int outbound_publish_state_hash(const void *obj, const int flags)
hashing function for state objects
static void sip_outbound_publish_destroy(void *obj)
Destructor function for publish information.
#define DATASTORE_BUCKETS
static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
Callback function for publish client responses.
static int sip_outbound_publisher_reinit_all(void *data)
static void sip_outbound_publish_datastore_destroy(void *obj)
#define DEFAULT_STATE_BUCKETS
Default number of client state container buckets.
struct ast_datastore * ast_sip_publish_client_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
Alternative for ast_datastore_alloc()
static AO2_GLOBAL_OBJ_STATIC(current_states)
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 outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
#define MAX_UNLOAD_TIMEOUT_TIME
static struct sip_outbound_publisher * sip_outbound_publisher_alloc(struct ast_sip_outbound_publish_client *client, const char *user)
static int pjsip_max_url_size
static int outbound_publish_state_cmp(void *obj, void *arg, int flags)
comparator function for client objects
static int cancel_refresh_timer_task(void *data)
Task for cancelling a refresh timer.
AO2_STRING_FIELD_HASH_FN(sip_outbound_publisher, user)
static int reload_module(void)
static int explicit_publish_destroy(void *data)
static void schedule_publish_refresh(struct sip_outbound_publisher *publisher, int expiration)
Helper function which sets up the timer to send publication.
int ast_sip_register_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
Register an event publisher handler.
static int validate_publish_config(struct ast_sip_outbound_publish *publish)
static int sip_publisher_service_queue(void *data)
static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj)
Apply function which finds or allocates a state structure.
const char * ast_sip_publish_client_get_from_uri(struct ast_sip_outbound_publish_client *client)
Get the From URI the client will use.
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)
static void sip_outbound_publisher_destroy(void *obj)
Destructor function for publish client.
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.
struct ast_sip_outbound_publish_client * ast_sip_publish_client_get(const char *name)
Find a publish client using its name.
static struct ast_sip_outbound_publish_state * sip_outbound_publish_state_alloc(struct ast_sip_outbound_publish *publish)
Allocator function for publish client.
static void sip_outbound_publish_client_destroy(void *obj)
Destructor function for publish client.
static void sip_outbound_publish_state_destroy(void *obj)
Destructor function for publish state.
static int sip_outbound_publisher_reinit(void *obj, void *arg, int flags)
static void * sip_outbound_publish_alloc(const char *name)
Allocator function for publish information.
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 struct ao2_container * new_states
Used on [re]loads to hold new state data.
static struct sip_outbound_publisher * sip_outbound_publish_client_add_publisher(struct ast_sip_outbound_publish_client *client, const char *user)
static int load_module(void)
static int datastore_cmp(void *obj, void *arg, int flags)
const char * ast_sip_publish_client_get_to_uri(struct ast_sip_outbound_publish_client *client)
Get the To URI the client will use.
static void sip_outbound_publish_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
Publish client timer callback function.
struct ast_datastore * ast_sip_publish_client_get_datastore(struct ast_sip_outbound_publish_client *client, const char *name)
Retrieve an event publisher datastore.
static int sip_outbound_publisher_set_uri(pj_pool_t *pool, const char *uri, const char *user, pj_str_t *res_uri)
#define DESTROY_CLIENT()
static int unload_module(void)
#define ADD_TO_NEW_STATES(__obj)
static int datastore_hash(const void *obj, int flags)
static int send_unpublish_task(void *data)
Task for sending an unpublish.
static void set_transport(struct sip_outbound_publisher *publisher, pjsip_tx_data *tdata)
static int publisher_client_send(void *obj, void *arg, void *data, int flags)
static struct ao2_container * get_publishes_and_update_state(void)
static int sip_outbound_publisher_init(void *data)
Helper function that allocates a pjsip publish client and configures it.
static int cancel_and_unpublish(void *obj, void *arg, int flags)
Helper function which cancels and un-publishes a no longer used client.
static struct ast_sorcery * sorcery
int ast_sip_validate_uri_length(const char *uri)
Definition: location.c:528
#define NULL
Definition: resample.c:96
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
@ AST_RETRIEVE_FLAG_MULTIPLE
Return all matching objects.
Definition: sorcery.h:120
@ AST_RETRIEVE_FLAG_ALL
Perform no matching, return all objects.
Definition: sorcery.h:123
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:837
#define ast_sorcery_object_field_register_custom(sorcery, type, name, default_val, config_handler, sorcery_handler, multiple_handler, flags,...)
Register a field within an object with custom handlers.
Definition: sorcery.h:1005
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
int ast_sorcery_object_unregister(struct ast_sorcery *sorcery, const char *type)
Unregister an object type.
Definition: sorcery.c:1061
#define ast_sorcery_apply_config(sorcery, name)
Definition: sorcery.h:455
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition: sorcery.h:476
void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
Inform any wizards of a specific object type to reload persistent objects.
Definition: sorcery.c:1442
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition: sorcery.c:1897
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Structure for a data store type.
Definition: datastore.h:31
void(* destroy)(void *data)
Definition: datastore.h:34
Structure for a data store object.
Definition: datastore.h:64
const struct ast_datastore_info * info
Definition: datastore.h:67
const char * uid
Definition: datastore.h:65
void * data
Definition: datastore.h:66
SIP body description.
Definition: res_pjsip.h:2325
const char * type
Definition: res_pjsip.h:2327
const char * body_text
Definition: res_pjsip.h:2331
const char * subtype
Definition: res_pjsip.h:2329
Callbacks that event publisher handlers will define.
struct ast_sip_event_publisher_handler * next
const char * event_name
The name of the event this handler deals with.
Outbound publish client state information (persists for lifetime of a publish)
struct ao2_container * datastores
Publisher datastores set up by handlers.
unsigned int started
Publishing has been fully started and event type informed.
struct ast_sip_outbound_publish * publish
Outbound publish information.
struct ao2_container * publishers
Container of all the client publishing objects.
Outbound publish state information (persists for lifetime of a publish)
struct ast_sip_outbound_publish_client * client
Outbound publish client.
Outbound publish information.
struct ast_sip_auth_vector outbound_auths
Configured authentication credentials.
SORCERY_OBJECT(details)
Sorcery object details.
unsigned int expiration
Requested expiration time.
const ast_string_field outbound_proxy
unsigned int multi_user
The publishing client is used for multiple users when true.
unsigned int max_auth_attempts
Maximum number of auth attempts before stopping the publish client.
Full structure for sorcery.
Definition: sorcery.c:230
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
Structure for variables, used for configurations and for channel variables.
Definition: search.h:40
Definition: astman.c:222
Queued outbound publish message.
struct ast_sip_body body
Optional body.
char body_contents[0]
Extra space for body contents.
struct sip_outbound_publish_message::@460 entry
Linked list information.
char * from_uri
The From URI for this specific publisher.
char user[0]
User, if any, associated with the publisher.
pjsip_publishc * client
Underlying publish client.
struct ast_sip_outbound_publish_client * owner
The client object that 'owns' this client.
unsigned int auth_attempts
The number of auth attempts done.
struct sip_outbound_publish_message * sending
The message currently being sent.
unsigned int destroy
Publish client should be destroyed.
struct ast_taskprocessor * serializer
Serializer for stuff and things.
char * to_uri
The To URI for this specific publisher.
pj_timer_entry timer
Timer entry for refreshing publish.
struct sip_outbound_publisher::@461 queue
Queue of outgoing publish messages to send.
structure to hold users read from users.conf
An API for managing task processing threads that can be shared across modules.
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
void ast_taskprocessor_build_name(char *buf, unsigned int size, const char *format,...)
Build a taskprocessor name with a sequence number on the end.
#define AST_TASKPROCESSOR_MAX_NAME
Suggested maximum taskprocessor name length (less null terminator).
Definition: taskprocessor.h:61
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
static char server_uri[512]
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
struct ast_serializer_shutdown_group * ast_serializer_shutdown_group_alloc(void)
Create a serializer group shutdown control object.
Definition: threadpool.c:1229
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
#define AST_UUID_STR_LEN
Definition: uuid.h:27
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:141
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680