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