Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 <since>
51 <version>13.0.0</version>
52 </since>
53 <synopsis>The configuration for outbound publish</synopsis>
54 <description><para>
55 Publish is <emphasis>COMPLETELY</emphasis> separate from the rest of
56 <literal>pjsip.conf</literal>. A minimal configuration consists of
57 setting a <literal>server_uri</literal> and <literal>event</literal>.
58 </para></description>
59 <configOption name="expiration" default="3600">
60 <since>
61 <version>13.0.0</version>
62 </since>
63 <synopsis>Expiration time for publications in seconds</synopsis>
64 </configOption>
65 <configOption name="outbound_auth" default="">
66 <since>
67 <version>13.0.0</version>
68 </since>
69 <synopsis>Authentication object(s) to be used for outbound publishes.</synopsis>
70 <description><para>
71 This is a comma-delimited list of <replaceable>auth</replaceable>
72 sections defined in <filename>pjsip.conf</filename> used to respond
73 to outbound authentication challenges.</para>
74 <note><para>
75 Using the same auth section for inbound and outbound
76 authentication is not recommended. There is a difference in
77 meaning for an empty realm setting between inbound and outbound
78 authentication uses. See the auth realm description for details.
79 </para></note>
80 </description>
81 </configOption>
82 <configOption name="outbound_proxy" default="">
83 <since>
84 <version>13.0.0</version>
85 </since>
86 <synopsis>Full SIP URI of the outbound proxy used to send publishes</synopsis>
87 </configOption>
88 <configOption name="server_uri">
89 <since>
90 <version>13.0.0</version>
91 </since>
92 <synopsis>SIP URI of the server and entity to publish to</synopsis>
93 <description><para>
94 This is the URI at which to find the entity and server to send the outbound PUBLISH to.
95 This URI is used as the request URI of the outbound PUBLISH request from Asterisk.
96 </para></description>
97 </configOption>
98 <configOption name="from_uri">
99 <since>
100 <version>13.0.0</version>
101 </since>
102 <synopsis>SIP URI to use in the From header</synopsis>
103 <description><para>
104 This is the URI that will be placed into the From header of outgoing PUBLISH
105 messages. If no URI is specified then the URI provided in <literal>server_uri</literal>
106 will be used.
107 </para></description>
108 </configOption>
109 <configOption name="to_uri">
110 <since>
111 <version>13.0.0</version>
112 </since>
113 <synopsis>SIP URI to use in the To header</synopsis>
114 <description><para>
115 This is the URI that will be placed into the To header of outgoing PUBLISH
116 messages. If no URI is specified then the URI provided in <literal>server_uri</literal>
117 will be used.
118 </para></description>
119 </configOption>
120 <configOption name="event" default="">
121 <since>
122 <version>13.0.0</version>
123 </since>
124 <synopsis>Event type of the PUBLISH.</synopsis>
125 </configOption>
126 <configOption name="max_auth_attempts" default="5">
127 <since>
128 <version>13.0.0</version>
129 </since>
130 <synopsis>Maximum number of authentication attempts before stopping the publication.</synopsis>
131 </configOption>
132 <configOption name="transport">
133 <since>
134 <version>13.9.0</version>
135 </since>
136 <synopsis>Transport used for outbound publish</synopsis>
137 <description>
138 <note><para>A <replaceable>transport</replaceable> configured in
139 <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>
140 </description>
141 </configOption>
142 <configOption name="multi_user" default="no">
143 <since>
144 <version>14.0.0</version>
145 </since>
146 <synopsis>Enable multi-user support</synopsis>
147 <description><para>When enabled the user portion of the server uri is replaced by a dynamically created user</para></description>
148 </configOption>
149 <configOption name="type">
150 <since>
151 <version>13.0.0</version>
152 </since>
153 <synopsis>Must be of type 'outbound-publish'.</synopsis>
154 </configOption>
155 </configObject>
156 </configFile>
157 </configInfo>
158 ***/
159
160static int pjsip_max_url_size = PJSIP_MAX_URL_SIZE;
161
162/*! \brief Queued outbound publish message */
164 /*! \brief Optional body */
166 /*! \brief Linked list information */
168 /*! \brief Extra space for body contents */
170};
171
172/*
173 * A note about some of the object types used in this module:
174 *
175 * The reason we currently have 4 separate object types that relate to configuration,
176 * publishing, state, and client information is due to object lifetimes and order of
177 * destruction dependencies.
178 *
179 * Separation of concerns is a good thing and of course it makes sense to have a
180 * configuration object type as well as an object type wrapper around pjsip's publishing
181 * client class. There also may be run time state data that needs to be tracked, so
182 * again having something to handle that is prudent. However, it may be tempting to think
183 * "why not combine the state and client object types?" Especially seeing as how they have
184 * a one-to-one relationship. The answer is, it's possible, but it'd make the code a bit
185 * more awkward.
186 *
187 * Currently this module maintains a global container of current state objects. When this
188 * states container is replaced, or deleted, it un-references all contained objects. Any
189 * state with a reference left have probably been carried over from a reload/realtime fetch.
190 * States not carried over are destructed and the associated client (and all its publishers)
191 * get unpublished.
192 *
193 * This "unpublishing" goes through a careful process of unpublishing the client, all its
194 * publishers, and making sure all the appropriate references are removed in a sane order.
195 * This process is essentially kicked off with the destruction of the state. If the state
196 * and client objects were to be merged, where clients became the globally tracked object
197 * type, this "unpublishing" process would never start because of the multiple references
198 * held to the client object over it's lifetime. Meaning the global tracking container
199 * would remove its reference to the client object when done with it, but other sources
200 * would still be holding a reference to it (namely the datastore and publisher(s)).
201 *
202 * Thus at this time it is easier to keep them separate.
203 */
204
205/*! \brief Outbound publish information */
207 /*! \brief Sorcery object details */
209 /*! \brief Stringfields */
211 /*! \brief URI for the entity and server */
213 /*! \brief URI for the From header */
215 /*! \brief URI for the To header */
217 /*! \brief Explicit transport to use for publish */
219 /*! \brief Outbound proxy to use */
221 /*! \brief The event type to publish */
223 );
224 /*! \brief Requested expiration time */
225 unsigned int expiration;
226 /*! \brief Maximum number of auth attempts before stopping the publish client */
227 unsigned int max_auth_attempts;
228 /*! \brief Configured authentication credentials */
230 /*! \brief The publishing client is used for multiple users when true */
231 unsigned int multi_user;
232};
233
235 /*! \brief The client object that 'owns' this client
236
237 \note any potential circular reference problems are accounted
238 for (see publisher alloc for more information)
239 */
241 /*! \brief Underlying publish client */
242 pjsip_publishc *client;
243 /*! \brief The From URI for this specific publisher */
244 char *from_uri;
245 /*! \brief The To URI for this specific publisher */
246 char *to_uri;
247 /*! \brief Timer entry for refreshing publish */
248 pj_timer_entry timer;
249 /*! \brief The number of auth attempts done */
250 unsigned int auth_attempts;
251 /*! \brief Queue of outgoing publish messages to send*/
253 /*! \brief The message currently being sent */
255 /*! \brief Publish client should be destroyed */
256 unsigned int destroy;
257 /*! \brief Serializer for stuff and things */
259 /*! \brief User, if any, associated with the publisher */
260 char user[0];
261};
262
263/*! \brief Outbound publish client state information (persists for lifetime of a publish) */
265 /*! \brief Outbound publish information */
267 /*! \brief Publisher datastores set up by handlers */
269 /*! \brief Container of all the client publishing objects */
271 /*! \brief Publishing has been fully started and event type informed */
272 unsigned int started;
273};
274
275/*! \brief Outbound publish state information (persists for lifetime of a publish) */
277 /*! \brief Outbound publish client */
279 /* publish state id lookup key - same as publish configuration id */
280 char id[0];
281};
282
283/*!
284 * \brief Used for locking while loading/reloading
285 *
286 * Mutli-user configurations make it so publishers can be dynamically added and
287 * removed. Publishers should not be added or removed during a [re]load since
288 * it could cause the current_clients container to be out of sync. Thus the
289 * reason for this lock.
290 */
292
293#define DEFAULT_PUBLISHER_BUCKETS 119
296
297/*! Time needs to be long enough for a transaction to timeout if nothing replies. */
298#define MAX_UNLOAD_TIMEOUT_TIME 35 /* Seconds */
299
300/*! Shutdown group to monitor sip_outbound_registration_client_state serializers. */
302
303/*! \brief Default number of client state container buckets */
304#define DEFAULT_STATE_BUCKETS 31
305static AO2_GLOBAL_OBJ_STATIC(current_states);
306/*! \brief Used on [re]loads to hold new state data */
308
309/*! \brief hashing function for state objects */
310static int outbound_publish_state_hash(const void *obj, const int flags)
311{
312 const struct ast_sip_outbound_publish_state *object;
313 const char *key;
314
315 switch (flags & OBJ_SEARCH_MASK) {
316 case OBJ_SEARCH_KEY:
317 key = obj;
318 break;
320 object = obj;
321 key = object->id;
322 break;
323 default:
324 ast_assert(0);
325 return 0;
326 }
327 return ast_str_hash(key);
328}
329
330/*! \brief comparator function for client objects */
331static int outbound_publish_state_cmp(void *obj, void *arg, int flags)
332{
333 const struct ast_sip_outbound_publish_state *object_left = obj;
334 const struct ast_sip_outbound_publish_state *object_right = arg;
335 const char *right_key = arg;
336 int cmp;
337
338 switch (flags & OBJ_SEARCH_MASK) {
340 right_key = object_right->id;
341 /* Fall through */
342 case OBJ_SEARCH_KEY:
343 cmp = strcmp(object_left->id, right_key);
344 break;
346 /* Not supported by container. */
347 ast_assert(0);
348 return 0;
349 default:
350 cmp = 0;
351 break;
352 }
353 if (cmp) {
354 return 0;
355 }
356 return CMP_MATCH;
357}
358
360{
361 struct ao2_container *container;
363
365 ast_sip_get_sorcery(), "outbound-publish",
367
368 if (!new_states) {
369 return container;
370 }
371
375
376 return container;
377}
378
380
382{
384}
385
387{
389
391 if (!strcmp(iter->event_name, event_name)) {
392 break;
393 }
394 }
395 return iter;
396}
397
398/*! \brief Helper function which cancels the refresh timer on a publisher */
399static void cancel_publish_refresh(struct sip_outbound_publisher *publisher)
400{
401 if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
402 &publisher->timer, 0)) {
403 /* The timer was successfully cancelled, drop the refcount of the publisher */
404 ao2_ref(publisher, -1);
405 }
406}
407
408/*! \brief Helper function which sets up the timer to send publication */
409static void schedule_publish_refresh(struct sip_outbound_publisher *publisher, int expiration)
410{
412 pj_time_val delay = { .sec = 0, };
413
414 cancel_publish_refresh(publisher);
415
416 if (expiration > 0) {
417 delay.sec = expiration - PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH;
418 }
419 if (publish->expiration && ((delay.sec > publish->expiration) || !delay.sec)) {
420 delay.sec = publish->expiration;
421 }
422 if (delay.sec < PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH) {
423 delay.sec = PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH;
424 }
425
426 ao2_ref(publisher, +1);
427 if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &publisher->timer, &delay) != PJ_SUCCESS) {
428 ast_log(LOG_WARNING, "Failed to pass timed publish refresh to scheduler\n");
429 ao2_ref(publisher, -1);
430 }
431 ao2_ref(publish, -1);
432}
433
434static int publisher_client_send(void *obj, void *arg, void *data, int flags);
435
436/*! \brief Publish client timer callback function */
437static void sip_outbound_publish_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
438{
439 struct sip_outbound_publisher *publisher = entry->user_data;
440
441 ao2_lock(publisher);
442 if (AST_LIST_EMPTY(&publisher->queue)) {
443 int res;
444 /* If there are no outstanding messages send an empty PUBLISH message so our publication doesn't expire */
445 publisher_client_send(publisher, NULL, &res, 0);
446 }
447 ao2_unlock(publisher);
448
449 ao2_ref(publisher, -1);
450}
451
452/*! \brief Task for cancelling a refresh timer */
453static int cancel_refresh_timer_task(void *data)
454{
455 struct sip_outbound_publisher *publisher = data;
456
457 cancel_publish_refresh(publisher);
458 ao2_ref(publisher, -1);
459
460 return 0;
461}
462
463static void set_transport(struct sip_outbound_publisher *publisher, pjsip_tx_data *tdata)
464{
465 if (!ast_strlen_zero(publisher->owner->publish->transport)) {
466 pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
467
469 publisher->owner->publish->transport, &selector);
470 pjsip_tx_data_set_transport(tdata, &selector);
471 ast_sip_tpselector_unref(&selector);
472 }
473}
474
475/*! \brief Task for sending an unpublish */
476static int send_unpublish_task(void *data)
477{
478 struct sip_outbound_publisher *publisher = data;
479 pjsip_tx_data *tdata;
480
481 if (pjsip_publishc_unpublish(publisher->client, &tdata) == PJ_SUCCESS) {
482 set_transport(publisher, tdata);
483 pjsip_publishc_send(publisher->client, tdata);
484 }
485
486 ao2_ref(publisher, -1);
487
488 return 0;
489}
490
493{
494 if (!handler) {
496 }
497
498 if (handler) {
499 handler->stop_publishing(client);
500 }
501}
502
503static int cancel_and_unpublish(void *obj, void *arg, int flags);
504
505/*! \brief Helper function which starts or stops publish clients when applicable */
507{
509 struct ao2_container *states;
510 struct ao2_iterator i;
512
513 if (!publishes) {
514 return;
515 }
516
517 states = ao2_global_obj_ref(current_states);
518 if (!states) {
519 return;
520 }
521
522 i = ao2_iterator_init(states, 0);
523 while ((state = ao2_iterator_next(&i))) {
524 struct ast_sip_outbound_publish *publish = ao2_bump(state->client->publish);
526
527 if (!state->client->started) {
528 /* If the publisher client has not yet been started try to start it */
529 if (!handler) {
530 ast_debug(2, "Could not find handler for event '%s' for outbound publish client '%s'\n",
532 } else if (handler->start_publishing(publish, state->client)) {
533 ast_log(LOG_ERROR, "Failed to start outbound publish with event '%s' for client '%s'\n",
535 } else {
536 state->client->started = 1;
537 }
538 } else if (state->client->started && !handler && removed && !strcmp(publish->event, removed->event_name)) {
539 stop_publishing(state->client, removed);
540 ao2_callback(state->client->publishers, OBJ_NODATA, cancel_and_unpublish, NULL);
541 state->client->started = 0;
542 }
543 ao2_ref(publish, -1);
544 ao2_ref(state, -1);
545 }
547 ao2_ref(states, -1);
548}
549
551{
552 struct ao2_container *states = ao2_global_obj_ref(current_states);
554
555 if (!states) {
556 return NULL;
557 }
558
559 res = ao2_find(states, id, OBJ_SEARCH_KEY);
560 ao2_ref(states, -1);
561 return res;
562}
563
565{
567
568 if (!state) {
569 return NULL;
570 }
571
572 ao2_ref(state->client, +1);
573 ao2_ref(state, -1);
574 return state->client;
575}
576
578{
579 struct ast_sip_outbound_publish *publish = client->publish;
580
581 return S_OR(publish->from_uri, S_OR(publish->server_uri, ""));
582}
583
585 struct ast_sip_outbound_publish_client *client, const char *user);
586
588 struct ast_sip_outbound_publish_client *client, const char *user)
589{
590 struct sip_outbound_publisher *publisher;
591
592 /*
593 * Lock before searching since there could be a race between searching and adding.
594 * Just use the load_lock since we might need to lock it anyway (if adding) and
595 * also it simplifies the code (otherwise we'd have to lock the publishers, no-
596 * lock the search and pass a flag to 'add publisher to no-lock the potential link).
597 */
599 publisher = ao2_find(client->publishers, user, OBJ_SEARCH_KEY);
600 if (!publisher) {
603 return NULL;
604 }
605 }
607
608 return publisher;
609}
610
612 char *uri, size_t size)
613{
614 struct sip_outbound_publisher *publisher;
615
617 if (!publisher) {
618 return NULL;
619 }
620
621 ast_copy_string(uri, publisher->from_uri, size);
622 ao2_ref(publisher, -1);
623
624 return uri;
625}
626
628{
629 struct ast_sip_outbound_publish *publish = client->publish;
630
631 return S_OR(publish->to_uri, S_OR(publish->server_uri, ""));
632}
633
635 char *uri, size_t size)
636{
637 struct sip_outbound_publisher *publisher;
638
640 if (!publisher) {
641 return NULL;
642 }
643
644 ast_copy_string(uri, publisher->to_uri, size);
645 ao2_ref(publisher, -1);
646
647 return uri;
648}
649
651{
652 struct ast_sip_event_publisher_handler *existing;
654
655 if (!handler->start_publishing || !handler->stop_publishing) {
656 ast_log(LOG_ERROR, "Handler does not implement required callbacks. Cannot register\n");
657 return -1;
658 } else if (ast_strlen_zero(handler->event_name)) {
659 ast_log(LOG_ERROR, "No event package specified for event publisher handler. Cannot register\n");
660 return -1;
661 }
662
663 existing = find_publisher_handler_for_event_name(handler->event_name);
664 if (existing) {
665 ast_log(LOG_ERROR, "Unable to register event publisher handler for event %s. "
666 "A handler is already registered\n", handler->event_name);
667 return -1;
668 }
669
671
673
674 return 0;
675}
676
678{
682 if (handler == iter) {
684 break;
685 }
686 }
688
690}
691
692/*! \brief Destructor function for publish information */
693static void sip_outbound_publish_destroy(void *obj)
694{
695 struct ast_sip_outbound_publish *publish = obj;
696
697 ast_sip_auth_vector_destroy(&publish->outbound_auths);
698
700}
701
702/*! \brief Allocator function for publish information */
703static void *sip_outbound_publish_alloc(const char *name)
704{
707
708 if (!publish || ast_string_field_init(publish, 256)) {
710 return NULL;
711 }
712
713 return publish;
714}
715
717{
718 struct ast_datastore *datastore = obj;
719
720 /* Using the destroy function (if present) destroy the data */
721 if (datastore->info->destroy != NULL && datastore->data != NULL) {
722 datastore->info->destroy(datastore->data);
723 datastore->data = NULL;
724 }
725
726 ast_free((void *) datastore->uid);
727 datastore->uid = NULL;
728}
729
731{
732 RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
733 const char *uid_ptr = uid;
734 char uuid_buf[AST_UUID_STR_LEN];
735
736 if (!info) {
737 return NULL;
738 }
739
740 datastore = ao2_alloc(sizeof(*datastore), sip_outbound_publish_datastore_destroy);
741 if (!datastore) {
742 return NULL;
743 }
744
745 datastore->info = info;
746 if (ast_strlen_zero(uid)) {
747 /* They didn't provide an ID so we'll provide one ourself */
748 uid_ptr = ast_uuid_generate_str(uuid_buf, sizeof(uuid_buf));
749 }
750
751 datastore->uid = ast_strdup(uid_ptr);
752 if (!datastore->uid) {
753 return NULL;
754 }
755
756 ao2_ref(datastore, +1);
757 return datastore;
758}
759
761 struct ast_datastore *datastore)
762{
763 ast_assert(datastore != NULL);
764 ast_assert(datastore->info != NULL);
765 ast_assert(!ast_strlen_zero(datastore->uid));
766
767 if (!ao2_link(client->datastores, datastore)) {
768 return -1;
769 }
770 return 0;
771}
772
774 const char *name)
775{
776 return ao2_find(client->datastores, name, OBJ_SEARCH_KEY);
777}
778
780 const char *name)
781{
783}
784
786{
787 RAII_VAR(struct sip_outbound_publisher *, publisher, data, ao2_cleanup);
788 SCOPED_AO2LOCK(lock, publisher);
790 pjsip_tx_data *tdata;
791 pj_status_t status;
792
793 if (publisher->destroy || publisher->sending || !(message = AST_LIST_FIRST(&publisher->queue))) {
794 return 0;
795 }
796
797 if (pjsip_publishc_publish(publisher->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {
798 goto fatal;
799 }
800
801 if (!ast_strlen_zero(message->body.type) && !ast_strlen_zero(message->body.subtype) &&
802 ast_sip_add_body(tdata, &message->body)) {
803 pjsip_tx_data_dec_ref(tdata);
804 goto fatal;
805 }
806
807 set_transport(publisher, tdata);
808
809 status = pjsip_publishc_send(publisher->client, tdata);
810 if (status == PJ_EBUSY) {
811 /* We attempted to send the message but something else got there first */
812 goto service;
813 } else if (status != PJ_SUCCESS) {
814 goto fatal;
815 }
816
817 publisher->sending = message;
818
819 return 0;
820
821fatal:
822 AST_LIST_REMOVE_HEAD(&publisher->queue, entry);
824
825service:
826 if (ast_sip_push_task(publisher->serializer, sip_publisher_service_queue, ao2_bump(publisher))) {
827 ao2_ref(publisher, -1);
828 }
829 return -1;
830}
831
832static int publisher_client_send(void *obj, void *arg, void *data, int flags)
833{
834 struct sip_outbound_publisher *publisher = obj;
835 const struct ast_sip_body *body = arg;
837 size_t type_len = 0, subtype_len = 0, body_text_len = 0;
838 int *res = data;
839 SCOPED_AO2LOCK(lock, publisher);
840
841 *res = -1;
842 if (!publisher->client) {
843 return -1;
844 }
845
846 /* If a body is present we need more space for the contents of it */
847 if (body) {
848 type_len = strlen(body->type) + 1;
849 subtype_len = strlen(body->subtype) + 1;
850 body_text_len = strlen(body->body_text) + 1;
851 }
852
853 message = ast_calloc(1, sizeof(*message) + type_len + subtype_len + body_text_len);
854 if (!message) {
855 return -1;
856 }
857
858 if (body) {
859 char *dst = message->body_contents;
860
861 message->body.type = strcpy(dst, body->type);
862 dst += type_len;
863 message->body.subtype = strcpy(dst, body->subtype);
864 dst += subtype_len;
865 message->body.body_text = strcpy(dst, body->body_text);
866 }
867
869
871 if (*res) {
872 ao2_ref(publisher, -1);
873 }
874
875 return *res;
876}
877
879 const struct ast_sip_body *body)
880{
881 SCOPED_AO2LOCK(lock, client);
882 int res = 0;
883
885 publisher_client_send, (void *)body, &res);
886 return res;
887}
888
890 pj_pool_t *pool, const char *uri, const char *user, pj_str_t *res_uri)
891{
892 pj_str_t tmp;
893 pjsip_uri *parsed;
894 pjsip_sip_uri *parsed_uri;
895 int size;
896
897 pj_strdup2_with_null(pool, &tmp, uri);
898 if (!(parsed = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0))) {
899 return -1;
900 }
901
902 if (!(parsed_uri = pjsip_uri_get_uri(parsed))) {
903 return -1;
904 }
905
906 if (!ast_strlen_zero(user)) {
907 pj_strdup2(pool, &parsed_uri->user, user);
908 }
909
910 res_uri->ptr = (char*) pj_pool_alloc(pool, pjsip_max_url_size);
911 if (!res_uri->ptr) {
912 return -1;
913 }
914
915 if ((size = pjsip_uri_print(PJSIP_URI_IN_OTHER, parsed_uri, res_uri->ptr,
916 pjsip_max_url_size - 1)) <= 0) {
917 return -1;
918 }
919 res_uri->ptr[size] = '\0';
920 res_uri->slen = size;
921
922 return 0;
923}
924
926 pj_pool_t *pool, struct sip_outbound_publisher *publisher,
927 pj_str_t *server_uri, pj_str_t *to_uri, pj_str_t *from_uri)
928{
929 struct ast_sip_outbound_publish *publish = publisher->owner->publish;
930
931 if (sip_outbound_publisher_set_uri(pool, publish->server_uri, publisher->user, server_uri)) {
932 ast_log(LOG_ERROR, "Invalid server URI '%s' specified on outbound publish '%s'\n",
934 return -1;
935 }
936
937 if (ast_strlen_zero(publish->to_uri)) {
938 to_uri->ptr = server_uri->ptr;
939 to_uri->slen = server_uri->slen;
940 } else if (sip_outbound_publisher_set_uri(pool, publish->to_uri, publisher->user, to_uri)) {
941 ast_log(LOG_ERROR, "Invalid to URI '%s' specified on outbound publish '%s'\n",
943 return -1;
944 }
945
946 publisher->to_uri = ast_strdup(to_uri->ptr);
947 if (!publisher->to_uri) {
948 return -1;
949 }
950
951 if (ast_strlen_zero(publish->from_uri)) {
952 from_uri->ptr = server_uri->ptr;
953 from_uri->slen = server_uri->slen;
954 } else if (sip_outbound_publisher_set_uri(pool, publish->from_uri, publisher->user, from_uri)) {
955 ast_log(LOG_ERROR, "Invalid from URI '%s' specified on outbound publish '%s'\n",
957 return -1;
958 }
959
960 publisher->from_uri = ast_strdup(from_uri->ptr);
961 if (!publisher->from_uri) {
962 return -1;
963 }
964
965 return 0;
966}
967
968static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param);
969
970/*! \brief Helper function that allocates a pjsip publish client and configures it */
971static int sip_outbound_publisher_init(void *data)
972{
973 struct sip_outbound_publisher *publisher = data;
975 pjsip_publishc_opt opt = {
976 .queue_request = PJ_FALSE,
977 };
978 pj_pool_t *pool;
979 pj_str_t event, server_uri, to_uri, from_uri;
980
981 if (publisher->client) {
982 return 0;
983 }
984
985 if (pjsip_publishc_create(ast_sip_get_pjsip_endpoint(), &opt,
987 &publisher->client) != PJ_SUCCESS) {
988 ao2_ref(publisher, -1);
989 return -1;
990 }
991
992 publish = ao2_bump(publisher->owner->publish);
993
994 if (!ast_strlen_zero(publish->outbound_proxy)) {
995 pjsip_route_hdr route_set, *route;
996 static const pj_str_t ROUTE_HNAME = { "Route", 5 };
997
998 pj_list_init(&route_set);
999
1000 if (!(route = pjsip_parse_hdr(pjsip_publishc_get_pool(publisher->client), &ROUTE_HNAME,
1001 (char*)publish->outbound_proxy, strlen(publish->outbound_proxy), NULL))) {
1002 pjsip_publishc_destroy(publisher->client);
1003 return -1;
1004 }
1005 pj_list_insert_nodes_before(&route_set, route);
1006
1007 pjsip_publishc_set_route_set(publisher->client, &route_set);
1008 }
1009
1010 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "URI Validation",
1012 if (!pool) {
1013 ast_log(LOG_ERROR, "Could not create pool for URI validation on outbound publish '%s'\n",
1015 pjsip_publishc_destroy(publisher->client);
1016 return -1;
1017 }
1018
1019 if (sip_outbound_publisher_set_uris(pool, publisher, &server_uri, &from_uri, &to_uri)) {
1020 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1021 pjsip_publishc_destroy(publisher->client);
1022 return -1;
1023 }
1024
1025 pj_cstr(&event, publish->event);
1026 if (pjsip_publishc_init(publisher->client, &event, &server_uri, &from_uri, &to_uri,
1027 publish->expiration) != PJ_SUCCESS) {
1028 ast_log(LOG_ERROR, "Failed to initialize publishing client on outbound publish '%s'\n",
1030 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1031 pjsip_publishc_destroy(publisher->client);
1032 return -1;
1033 }
1034
1035 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
1036 return 0;
1037}
1038
1039static int sip_outbound_publisher_reinit(void *obj, void *arg, int flags)
1040{
1041 return sip_outbound_publisher_init(obj);
1042}
1043
1045{
1047 return 0;
1048}
1049
1050/*! \brief Destructor function for publish client */
1052{
1053 struct sip_outbound_publisher *publisher = obj;
1055
1056 /* You might be tempted to think "the publish client isn't being destroyed" but it actually is - just elsewhere */
1057
1058 while ((message = AST_LIST_REMOVE_HEAD(&publisher->queue, entry))) {
1060 }
1061
1062 ao2_cleanup(publisher->owner);
1063 ast_free(publisher->from_uri);
1064 ast_free(publisher->to_uri);
1065
1067}
1068
1070 struct ast_sip_outbound_publish_client *client, const char *user)
1071{
1072 struct sip_outbound_publisher *publisher;
1073 char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
1074
1075 publisher = ao2_alloc(sizeof(*publisher) + (user ? strlen(user) : 0) + 1,
1077 if (!publisher) {
1078 return NULL;
1079 }
1080
1081 /*
1082 * Bump the ref to the client. This essentially creates a circular reference,
1083 * but it is needed in order to make sure the client object doesn't get pulled
1084 * out from under us when the publisher stops publishing.
1085 *
1086 * The circular reference is alleviated by calling cancel_and_unpublish for
1087 * each client, from the state's destructor. By calling it there all references
1088 * to the publishers should go to zero, thus calling the publisher's destructor.
1089 * This in turn removes the client reference we added here. The state then removes
1090 * its reference to the client, which should take it to zero.
1091 */
1092 publisher->owner = ao2_bump(client);
1093 publisher->timer.user_data = publisher;
1094 publisher->timer.cb = sip_outbound_publish_timer_cb;
1095 if (user) {
1096 strcpy(publisher->user, user);
1097 } else {
1098 *publisher->user = '\0';
1099 }
1100
1101 ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/outpub/%s",
1103
1104 publisher->serializer = ast_sip_create_serializer_group(tps_name,
1106 if (!publisher->serializer) {
1107 ao2_ref(publisher, -1);
1108 return NULL;
1109 }
1110
1112 ast_log(LOG_ERROR, "Unable to create publisher for outbound publish '%s'\n",
1114 ao2_ref(publisher, -1);
1115 return NULL;
1116 }
1117
1118 return publisher;
1119}
1120
1122 struct ast_sip_outbound_publish_client *client, const char *user)
1123{
1124 struct sip_outbound_publisher *publisher =
1126
1127 if (!publisher) {
1128 return NULL;
1129 }
1130
1131 if (!ao2_link(client->publishers, publisher)) {
1132 /*
1133 * No need to bump the reference here. The task will take care of
1134 * removing the reference.
1135 */
1136 if (ast_sip_push_task(publisher->serializer, cancel_refresh_timer_task, publisher)) {
1137 ao2_ref(publisher, -1);
1138 }
1139 return NULL;
1140 }
1141
1142 return publisher;
1143}
1144
1146 const char *user, const struct ast_sip_body *body)
1147{
1148 struct sip_outbound_publisher *publisher;
1149 int res;
1150
1152 if (!publisher) {
1153 return -1;
1154 }
1155
1156 publisher_client_send(publisher, (void *)body, &res, 0);
1157 ao2_ref(publisher, -1);
1158 return res;
1159}
1160
1162 const char *user)
1163{
1166}
1167
1168static int explicit_publish_destroy(void *data)
1169{
1170 struct sip_outbound_publisher *publisher = data;
1171
1172 /*
1173 * If there is no pjsip publishing client then we obviously don't need
1174 * to destroy it. Also, the ref for the Asterisk publishing client that
1175 * pjsip had would not exist or should already be gone as well.
1176 */
1177 if (publisher->client) {
1178 pjsip_publishc_destroy(publisher->client);
1179 ao2_ref(publisher, -1);
1180 }
1181
1182 ao2_ref(publisher, -1);
1183
1184 return 0;
1185}
1186
1187/*! \brief Helper function which cancels and un-publishes a no longer used client */
1188static int cancel_and_unpublish(void *obj, void *arg, int flags)
1189{
1190 struct sip_outbound_publisher *publisher = obj;
1191 struct ast_sip_outbound_publish_client *client = publisher->owner;
1192
1193 SCOPED_AO2LOCK(lock, publisher);
1194
1195 if (!client->started) {
1196 /* If the publisher was never started, there's nothing to unpublish, so just
1197 * destroy the publication and remove its reference to the publisher.
1198 */
1199 if (ast_sip_push_task(publisher->serializer, explicit_publish_destroy, ao2_bump(publisher))) {
1200 ao2_ref(publisher, -1);
1201 }
1202 return 0;
1203 }
1204
1205 if (ast_sip_push_task(publisher->serializer, cancel_refresh_timer_task, ao2_bump(publisher))) {
1206 ast_log(LOG_WARNING, "Could not stop refresh timer on outbound publish '%s'\n",
1208 ao2_ref(publisher, -1);
1209 }
1210
1211 /* If nothing is being sent right now send the unpublish - the destroy will happen in the subsequent callback */
1212 if (!publisher->sending) {
1213 if (ast_sip_push_task(publisher->serializer, send_unpublish_task, ao2_bump(publisher))) {
1214 ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
1216 ao2_ref(publisher, -1);
1217 }
1218 }
1219 publisher->destroy = 1;
1220 return 0;
1221}
1222
1223/*! \brief Destructor function for publish client */
1225{
1226 struct ast_sip_outbound_publish_client *client = obj;
1227
1228 ao2_cleanup(client->datastores);
1229
1230 /*
1231 * The client's publishers have already been unpublished and destroyed
1232 * by this point, so it is safe to finally remove the reference to the
1233 * publish object. The client needed to hold a reference to it until
1234 * the publishers were done with it.
1235 */
1236 ao2_cleanup(client->publish);
1237}
1238
1239/*! \brief Destructor function for publish state */
1241{
1243
1244 stop_publishing(state->client, NULL);
1245 /*
1246 * Since the state is being destroyed the associated client needs to also
1247 * be destroyed. However simply removing the reference to the client will
1248 * not initiate client destruction since the client's publisher(s) hold a
1249 * reference to the client object as well. So we need to unpublish the
1250 * the client's publishers here, which will remove the publisher's client
1251 * reference during that process.
1252 *
1253 * That being said we don't want to remove the client's reference to the
1254 * publish object just yet. We'll hold off on that until client destruction
1255 * itself. This is because the publishers need access to the client's
1256 * publish object while they are unpublishing.
1257 */
1259 ao2_cleanup(state->client->publishers);
1260
1261 state->client->started = 0;
1262 ao2_cleanup(state->client);
1263}
1264
1265/*!
1266 * \internal
1267 * \brief Check if a publish can be reused
1268 *
1269 * This checks if the existing outbound publish's configuration differs from a newly-applied
1270 * outbound publish.
1271 *
1272 * \param existing The pre-existing outbound publish
1273 * \param applied The newly-created publish
1274 */
1275static int can_reuse_publish(struct ast_sip_outbound_publish *existing, struct ast_sip_outbound_publish *applied)
1276{
1277 int i;
1278
1279 if (strcmp(existing->server_uri, applied->server_uri) || strcmp(existing->from_uri, applied->from_uri) ||
1280 strcmp(existing->to_uri, applied->to_uri) || strcmp(existing->outbound_proxy, applied->outbound_proxy) ||
1281 strcmp(existing->event, applied->event) ||
1283 return 0;
1284 }
1285
1286 for (i = 0; i < AST_VECTOR_SIZE(&existing->outbound_auths); ++i) {
1287 if (strcmp(AST_VECTOR_GET(&existing->outbound_auths, i), AST_VECTOR_GET(&applied->outbound_auths, i))) {
1288 return 0;
1289 }
1290 }
1291
1292 return 1;
1293}
1294
1295/*! \brief Callback function for publish client responses */
1296static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
1297{
1298#define DESTROY_CLIENT() do { \
1299 pjsip_publishc_destroy(publisher->client); \
1300 publisher->client = NULL; \
1301 ao2_ref(publisher, -1); } while (0)
1302
1303 RAII_VAR(struct sip_outbound_publisher *, publisher, ao2_bump(param->token), ao2_cleanup);
1304 RAII_VAR(struct ast_sip_outbound_publish *, publish, ao2_bump(publisher->owner->publish), ao2_cleanup);
1305 SCOPED_AO2LOCK(lock, publisher);
1306 pjsip_tx_data *tdata;
1307
1308 if (publisher->destroy) {
1309 if (publisher->sending) {
1310 publisher->sending = NULL;
1311
1312 if (!ast_sip_push_task(publisher->serializer, send_unpublish_task, ao2_bump(publisher))) {
1313 return;
1314 }
1315 ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
1317 ao2_ref(publisher, -1);
1318 }
1319 /* Once the destroy is called this callback will not get called any longer, so drop the publisher ref */
1321 return;
1322 }
1323
1324 if (param->code == 401 || param->code == 407) {
1325 pjsip_transaction *tsx = pjsip_rdata_get_tsx(param->rdata);
1326
1327 if (!ast_sip_create_request_with_auth(&publish->outbound_auths,
1328 param->rdata, tsx->last_tx, &tdata)) {
1329 set_transport(publisher, tdata);
1330 pjsip_publishc_send(publisher->client, tdata);
1331 }
1332 publisher->auth_attempts++;
1333
1334 if (publisher->auth_attempts == publish->max_auth_attempts) {
1336 ast_log(LOG_ERROR, "Reached maximum number of PUBLISH authentication attempts on outbound publish '%s'\n",
1338
1339 goto end;
1340 }
1341 return;
1342 }
1343
1344 publisher->auth_attempts = 0;
1345
1346 if (param->code == 412) {
1348 if (sip_outbound_publisher_init(publisher)) {
1349 ast_log(LOG_ERROR, "Failed to create a new outbound publish client for '%s' on 412 response\n",
1351 goto end;
1352 }
1353
1354 /* Setting this to NULL will cause a new PUBLISH to get created and sent for the same underlying body */
1355 publisher->sending = NULL;
1356 } else if (param->code == 423) {
1357 /* Update the expiration with the new expiration time if available */
1358 pjsip_expires_hdr *expires;
1359
1360 expires = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_MIN_EXPIRES, NULL);
1361 if (!expires || !expires->ivalue) {
1363 ast_log(LOG_ERROR, "Received 423 response on outbound publish '%s' without a Min-Expires header\n",
1365 goto end;
1366 }
1367
1368 pjsip_publishc_update_expires(publisher->client, expires->ivalue);
1369 publisher->sending = NULL;
1370 } else if (publisher->sending) {
1371 /* Remove the message currently being sent so that when the queue is serviced another will get sent */
1372 AST_LIST_REMOVE_HEAD(&publisher->queue, entry);
1373 ast_free(publisher->sending);
1374 publisher->sending = NULL;
1375 if (!param->rdata) {
1376 ast_log(LOG_NOTICE, "No response received for outbound publish '%s'\n",
1378 }
1379 }
1380
1381 if (AST_LIST_EMPTY(&publisher->queue)) {
1382 schedule_publish_refresh(publisher, param->expiration);
1383 }
1384
1385end:
1386 if (!publisher->client) {
1388
1389 while ((message = AST_LIST_REMOVE_HEAD(&publisher->queue, entry))) {
1391 }
1392 } else {
1393 if (ast_sip_push_task(publisher->serializer, sip_publisher_service_queue, ao2_bump(publisher))) {
1394 ao2_ref(publisher, -1);
1395 }
1396 }
1397}
1398
1399#define DATASTORE_BUCKETS 53
1400
1401static int datastore_hash(const void *obj, int flags)
1402{
1403 const struct ast_datastore *datastore;
1404 const char *uid;
1405
1406 switch (flags & OBJ_SEARCH_MASK) {
1407 case OBJ_SEARCH_KEY:
1408 uid = obj;
1409 break;
1410 case OBJ_SEARCH_OBJECT:
1411 datastore = obj;
1412 uid = datastore->uid;
1413 break;
1414 default:
1415 /* Hash can only work on something with a full key. */
1416 ast_assert(0);
1417 return 0;
1418 }
1419
1420 return ast_str_hash(uid);
1421}
1422
1423static int datastore_cmp(void *obj, void *arg, int flags)
1424{
1425 const struct ast_datastore *object_left = obj;
1426 const struct ast_datastore *object_right = arg;
1427 const char *right_key = arg;
1428 int cmp;
1429
1430 switch (flags & OBJ_SEARCH_MASK) {
1431 case OBJ_SEARCH_OBJECT:
1432 right_key = object_right->uid;
1433 /* Fall through */
1434 case OBJ_SEARCH_KEY:
1435 cmp = strcmp(object_left->uid, right_key);
1436 break;
1438 cmp = strncmp(object_left->uid, right_key, strlen(right_key));
1439 break;
1440 default:
1441 /*
1442 * What arg points to is specific to this traversal callback
1443 * and has no special meaning to astobj2.
1444 */
1445 cmp = 0;
1446 break;
1447 }
1448 if (cmp) {
1449 return 0;
1450 }
1451 /*
1452 * At this point the traversal callback is identical to a sorted
1453 * container.
1454 */
1455 return CMP_MATCH;
1456}
1457
1458/*! \brief Allocator function for publish client */
1461{
1462 const char *id = ast_sorcery_object_get_id(publish);
1464 ao2_alloc(sizeof(*state) + strlen(id) + 1, sip_outbound_publish_state_destroy);
1465
1466 if (!state) {
1467 return NULL;
1468 }
1469
1470 state->client = ao2_alloc(sizeof(*state->client), sip_outbound_publish_client_destroy);
1471 if (!state->client) {
1472 ao2_ref(state, -1);
1473 return NULL;
1474 }
1475
1478 if (!state->client->datastores) {
1479 ao2_ref(state, -1);
1480 return NULL;
1481 }
1482
1485 sip_outbound_publisher_hash_fn, NULL, sip_outbound_publisher_cmp_fn);
1486 if (!state->client->publishers) {
1487 ao2_ref(state, -1);
1488 return NULL;
1489 }
1490
1491 state->client->publish = ao2_bump(publish);
1492
1493 strcpy(state->id, id);
1494 return state;
1495}
1496
1498{
1499 if (ast_strlen_zero(publish->server_uri)) {
1500 ast_log(LOG_ERROR, "No server URI specified on outbound publish '%s'\n",
1502 return -1;
1503 } else if (ast_sip_validate_uri_length(publish->server_uri)) {
1504 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",
1505 publish->server_uri,
1507 return -1;
1508 } else if (ast_strlen_zero(publish->event)) {
1509 ast_log(LOG_ERROR, "No event type specified for outbound publish '%s'\n",
1511 return -1;
1512 } else if (!ast_strlen_zero(publish->from_uri)
1513 && ast_sip_validate_uri_length(publish->from_uri)) {
1514 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",
1515 publish->from_uri,
1517 return -1;
1518 } else if (!ast_strlen_zero(publish->to_uri)
1520 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",
1521 publish->to_uri,
1523 return -1;
1524 }
1525 return 0;
1526}
1527
1529 struct ast_sip_outbound_publish_state *current_state)
1530{
1531 struct ast_sip_outbound_publish *old_publish;
1532
1533 /*
1534 * Don't maintain the old state/client objects if the multi_user option changed.
1535 */
1536 if ((!publish->multi_user && current_state->client->publish->multi_user) ||
1537 (publish->multi_user && !current_state->client->publish->multi_user)) {
1538 return 0;
1539 }
1540
1541
1542 if (!can_reuse_publish(current_state->client->publish, publish)) {
1543 /*
1544 * Something significant has changed in the configuration, so we are
1545 * unable to use the old state object. The current state needs to go
1546 * away and a new one needs to be created.
1547 */
1548 return 0;
1549 }
1550
1551 /*
1552 * We can reuse the current state object so keep it, but swap out the
1553 * underlying publish object with the new one.
1554 */
1555 old_publish = current_state->client->publish;
1556 current_state->client->publish = publish;
1558 current_state->client->publishers)) {
1559 /*
1560 * If the state object fails to re-initialize then swap
1561 * the old publish info back in.
1562 */
1563 current_state->client->publish = publish;
1564 ast_log(LOG_ERROR, "Unable to reinitialize client(s) for outbound publish '%s'\n",
1565 ast_sorcery_object_get_id(current_state->client->publish));
1566 return -1;
1567 }
1568
1569 /*
1570 * Since we swapped out the publish object the new one needs a ref
1571 * while the old one needs to go away.
1572 */
1573 ao2_ref(current_state->client->publish, +1);
1574 ao2_cleanup(old_publish);
1575
1576 /* Tell the caller that the current state object should be used */
1577 return 1;
1578}
1579
1580/*! \brief Apply function which finds or allocates a state structure */
1581static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj)
1582{
1583#define ADD_TO_NEW_STATES(__obj) \
1584 do { if (__obj) { \
1585 ao2_link(new_states, __obj); \
1586 ao2_ref(__obj, -1); } } while (0)
1587
1588 struct ast_sip_outbound_publish *applied = obj;
1589 struct ast_sip_outbound_publish_state *current_state, *new_state;
1590 struct sip_outbound_publisher *publisher = NULL;
1591 int res;
1592
1593 /*
1594 * New states are being loaded or reloaded. We'll need to add the new
1595 * object if created/updated, or keep the old object if an error occurs.
1596 */
1597 if (!new_states) {
1601
1602 if (!new_states) {
1603 ast_log(LOG_ERROR, "Unable to allocate new states container\n");
1604 return -1;
1605 }
1606 }
1607
1608 /* If there is current state we'll want to maintain it if any errors occur */
1609 current_state = sip_publish_state_get(ast_sorcery_object_get_id(applied));
1610
1611 if ((res = validate_publish_config(applied))) {
1612 ADD_TO_NEW_STATES(current_state);
1613 return res;
1614 }
1615
1616 if (current_state && (res = current_state_reusable(applied, current_state))) {
1617 /*
1618 * The current state object was able to be reused, or an error
1619 * occurred. Either way we keep the current state and be done.
1620 */
1621 ADD_TO_NEW_STATES(current_state);
1622 return res == 1 ? 0 : -1;
1623 }
1624
1625 /*
1626 * No current state was found or it was unable to be reused. Either way
1627 * we'll need to create a new state object.
1628 */
1629 new_state = sip_outbound_publish_state_alloc(applied);
1630 if (!new_state) {
1631 ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n",
1632 ast_sorcery_object_get_id(applied));
1633 ADD_TO_NEW_STATES(current_state);
1634 return -1;
1635 };
1636
1637 if (!applied->multi_user &&
1638 !(publisher = sip_outbound_publish_client_add_publisher(new_state->client, NULL))) {
1639 ADD_TO_NEW_STATES(current_state);
1640 ao2_ref(new_state, -1);
1641 return -1;
1642 }
1643 ao2_cleanup(publisher);
1644
1645 ADD_TO_NEW_STATES(new_state);
1646 ao2_cleanup(current_state);
1647 return res;
1648}
1649
1650static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1651{
1652 struct ast_sip_outbound_publish *publish = obj;
1653
1654 return ast_sip_auth_vector_init(&publish->outbound_auths, var->value);
1655}
1656
1657
1658static int unload_module(void)
1659{
1660 int remaining;
1661
1663
1664 ao2_global_obj_release(current_states);
1665
1666 /* Wait for publication serializers to get destroyed. */
1667 ast_debug(2, "Waiting for publication to complete for unload.\n");
1669 if (remaining) {
1670 ast_log(LOG_WARNING, "Unload incomplete. Could not stop %d outbound publications. Try again later.\n",
1671 remaining);
1672 return -1;
1673 }
1674
1675 ast_debug(2, "Successful shutdown.\n");
1676
1679
1680 return 0;
1681}
1682
1683static int load_module(void)
1684{
1685 /* As of pjproject 2.4.5, PJSIP_MAX_URL_SIZE isn't exposed yet but we try anyway. */
1686 ast_pjproject_get_buildopt("PJSIP_MAX_URL_SIZE", "%d", &pjsip_max_url_size);
1687
1689 if (!shutdown_group) {
1691 }
1692
1693 ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_outbound_publish");
1694 ast_sorcery_apply_default(ast_sip_get_sorcery(), "outbound-publish", "config", "pjsip.conf,criteria=type=outbound-publish");
1695
1698 ast_log(LOG_ERROR, "Unable to register 'outbound-publish' type with sorcery\n");
1699 unload_module();
1701 }
1702
1703 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "type", "", OPT_NOOP_T, 0, 0);
1709 ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_outbound_publish, expiration));
1710 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));
1712 ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "outbound-publish", "outbound_auth", "", outbound_auth_handler, NULL, NULL, 0, 0);
1713 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));
1714
1715 ast_sorcery_reload_object(ast_sip_get_sorcery(), "outbound-publish");
1716
1720
1721 pjsip_publishc_init_module(ast_sip_get_pjsip_endpoint());
1722
1724}
1725
1726static int reload_module(void)
1727{
1728 ast_sorcery_reload_object(ast_sip_get_sorcery(), "outbound-publish");
1729
1733 return 0;
1734}
1735
1737 .support_level = AST_MODULE_SUPPORT_CORE,
1738 .load = load_module,
1740 .unload = unload_module,
1741 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1742 .requires = "res_pjproject,res_pjsip",
jack_status_t status
Definition: app_jack.c:149
ast_mutex_t lock
Definition: app_sla.c:337
#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
enum ast_cc_service_type service
Definition: ccss.c:389
enum cc_state state
Definition: ccss.c:399
#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:240
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:608
#define AST_RWLOCK_DEFINE_STATIC(rwlock)
Definition: lock.h:547
#define SCOPED_WRLOCK(varname, lock)
scoped lock specialization for write locks
Definition: lock.h:603
#define SCOPED_LOCK(varname, lock, lockfunc, unlockfunc)
Scoped Locks.
Definition: lock.h:587
#define ast_rwlock_unlock(a)
Definition: lock.h:238
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
def info(msg)
static int reload(void)
unsigned char publish
Definition: res_corosync.c:241
struct ao2_container * container
Definition: res_fax.c:531
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:529
#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:2444
const char * type
Definition: res_pjsip.h:2446
const char * body_text
Definition: res_pjsip.h:2450
const char * subtype
Definition: res_pjsip.h:2448
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: 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::@463 entry
Linked list information.
char * from_uri
The From URI for this specific publisher.
struct sip_outbound_publisher::@464 queue
Queue of outgoing publish messages to send.
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.
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