res_pjsip_outbound_publishing: After unloading the library won't load again
[asterisk/asterisk.git] / res / res_pjsip_outbound_publish.c
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_pjsip</depend>
22         <support_level>core</support_level>
23  ***/
24
25 #include "asterisk.h"
26
27 #include <pjsip.h>
28 #include <pjsip_simple.h>
29
30 #include "asterisk/res_pjsip.h"
31 #include "asterisk/res_pjsip_outbound_publish.h"
32 #include "asterisk/module.h"
33 #include "asterisk/taskprocessor.h"
34 #include "asterisk/datastore.h"
35
36 /*** DOCUMENTATION
37         <configInfo name="res_pjsip_outbound_publish" language="en_US">
38                 <synopsis>SIP resource for outbound publish</synopsis>
39                 <description><para>
40                         <emphasis>Outbound Publish</emphasis>
41                         </para>
42                         <para>This module allows <literal>res_pjsip</literal> to publish to other SIP servers.</para>
43                 </description>
44                 <configFile name="pjsip.conf">
45                         <configObject name="outbound-publish">
46                                 <synopsis>The configuration for outbound publish</synopsis>
47                                 <description><para>
48                                         Publish is <emphasis>COMPLETELY</emphasis> separate from the rest of
49                                         <literal>pjsip.conf</literal>. A minimal configuration consists of
50                                         setting a <literal>server_uri</literal> and <literal>event</literal>.
51                                 </para></description>
52                                 <configOption name="expiration" default="3600">
53                                         <synopsis>Expiration time for publications in seconds</synopsis>
54                                 </configOption>
55                                 <configOption name="outbound_auth" default="">
56                                         <synopsis>Authentication object to be used for outbound publishes.</synopsis>
57                                 </configOption>
58                                 <configOption name="outbound_proxy" default="">
59                                         <synopsis>SIP URI of the outbound proxy used to send publishes</synopsis>
60                                 </configOption>
61                                 <configOption name="server_uri">
62                                         <synopsis>SIP URI of the server and entity to publish to</synopsis>
63                                         <description><para>
64                                                 This is the URI at which to find the entity and server to send the outbound PUBLISH to.
65                                                 This URI is used as the request URI of the outbound PUBLISH request from Asterisk.
66                                         </para></description>
67                                 </configOption>
68                                 <configOption name="from_uri">
69                                         <synopsis>SIP URI to use in the From header</synopsis>
70                                         <description><para>
71                                                 This is the URI that will be placed into the From header of outgoing PUBLISH
72                                                 messages. If no URI is specified then the URI provided in <literal>server_uri</literal>
73                                                 will be used.
74                                         </para></description>
75                                 </configOption>
76                                 <configOption name="to_uri">
77                                         <synopsis>SIP URI to use in the To header</synopsis>
78                                         <description><para>
79                                                 This is the URI that will be placed into the To header of outgoing PUBLISH
80                                                 messages. If no URI is specified then the URI provided in <literal>server_uri</literal>
81                                                 will be used.
82                                         </para></description>
83                                 </configOption>
84                                 <configOption name="event" default="">
85                                         <synopsis>Event type of the PUBLISH.</synopsis>
86                                 </configOption>
87                                 <configOption name="max_auth_attempts" default="5">
88                                         <synopsis>Maximum number of authentication attempts before stopping the publication.</synopsis>
89                                 </configOption>
90                                 <configOption name="transport">
91                                         <synopsis>Transport used for outbound publish</synopsis>
92                                         <description>
93                                                 <note><para>A <replaceable>transport</replaceable> configured in
94                                                 <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>
95                                         </description>
96                                 </configOption>
97                                 <configOption name="type">
98                                         <synopsis>Must be of type 'outbound-publish'.</synopsis>
99                                 </configOption>
100                         </configObject>
101                 </configFile>
102         </configInfo>
103  ***/
104
105 /*! \brief Queued outbound publish message */
106 struct sip_outbound_publish_message {
107         /*! \brief Optional body */
108         struct ast_sip_body body;
109         /*! \brief Linked list information */
110         AST_LIST_ENTRY(sip_outbound_publish_message) entry;
111         /*! \brief Extra space for body contents */
112         char body_contents[0];
113 };
114
115 /*! \brief Outbound publish information */
116 struct ast_sip_outbound_publish {
117         /*! \brief Sorcery object details */
118         SORCERY_OBJECT(details);
119         /*! \brief Stringfields */
120         AST_DECLARE_STRING_FIELDS(
121                 /*! \brief URI for the entity and server */
122                 AST_STRING_FIELD(server_uri);
123                 /*! \brief URI for the From header */
124                 AST_STRING_FIELD(from_uri);
125                 /*! \brief URI for the To header */
126                 AST_STRING_FIELD(to_uri);
127                 /*! \brief Explicit transport to use for publish */
128                 AST_STRING_FIELD(transport);
129                 /*! \brief Outbound proxy to use */
130                 AST_STRING_FIELD(outbound_proxy);
131                 /*! \brief The event type to publish */
132                 AST_STRING_FIELD(event);
133         );
134         /*! \brief Requested expiration time */
135         unsigned int expiration;
136         /*! \brief Maximum number of auth attempts before stopping the publish client */
137         unsigned int max_auth_attempts;
138         /*! \brief Configured authentication credentials */
139         struct ast_sip_auth_vector outbound_auths;
140 };
141
142 /*! \brief Outbound publish client state information (persists for lifetime that publish should exist) */
143 struct ast_sip_outbound_publish_client {
144         /*! \brief Underlying publish client */
145         pjsip_publishc *client;
146         /*! \brief Timer entry for refreshing publish */
147         pj_timer_entry timer;
148         /*! \brief Publisher datastores set up by handlers */
149         struct ao2_container *datastores;
150         /*! \brief The number of auth attempts done */
151         unsigned int auth_attempts;
152         /*! \brief Queue of outgoing publish messages to send*/
153         AST_LIST_HEAD_NOLOCK(, sip_outbound_publish_message) queue;
154         /*! \brief The message currently being sent */
155         struct sip_outbound_publish_message *sending;
156         /*! \brief Publish client has been fully started and event type informed */
157         unsigned int started;
158         /*! \brief Publish client should be destroyed */
159         unsigned int destroy;
160         /*! \brief Outbound publish information */
161         struct ast_sip_outbound_publish *publish;
162         /*! \brief The name of the transport to be used for the publish */
163         char *transport_name;
164 };
165
166 /*! \brief Outbound publish state information (persists for lifetime of a publish) */
167 struct ast_sip_outbound_publish_state {
168         /*! \brief Outbound publish client */
169         struct ast_sip_outbound_publish_client *client;
170         /* publish state id lookup key - same as publish configuration id */
171         char id[0];
172 };
173
174 /*! \brief Unloading data */
175 struct unloading_data {
176         int is_unloading;
177         int count;
178         ast_mutex_t lock;
179         ast_cond_t cond;
180 } unloading;
181
182 /*! \brief Default number of client state container buckets */
183 #define DEFAULT_STATE_BUCKETS 31
184 static AO2_GLOBAL_OBJ_STATIC(current_states);
185 /*! \brief Used on [re]loads to hold new state data */
186 static struct ao2_container *new_states;
187
188 /*! \brief hashing function for state objects */
189 static int outbound_publish_state_hash(const void *obj, const int flags)
190 {
191         const struct ast_sip_outbound_publish_state *object;
192         const char *key;
193
194         switch (flags & OBJ_SEARCH_MASK) {
195         case OBJ_SEARCH_KEY:
196                 key = obj;
197                 break;
198         case OBJ_SEARCH_OBJECT:
199                 object = obj;
200                 key = object->id;
201                 break;
202         default:
203                 ast_assert(0);
204                 return 0;
205         }
206         return ast_str_hash(key);
207 }
208
209 /*! \brief comparator function for client objects */
210 static int outbound_publish_state_cmp(void *obj, void *arg, int flags)
211 {
212         const struct ast_sip_outbound_publish_state *object_left = obj;
213         const struct ast_sip_outbound_publish_state *object_right = arg;
214         const char *right_key = arg;
215         int cmp;
216
217         switch (flags & OBJ_SEARCH_MASK) {
218         case OBJ_SEARCH_OBJECT:
219                 right_key = object_right->id;
220                 /* Fall through */
221         case OBJ_SEARCH_KEY:
222                 cmp = strcmp(object_left->id, right_key);
223                 break;
224         case OBJ_SEARCH_PARTIAL_KEY:
225                 /* Not supported by container. */
226                 ast_assert(0);
227                 return 0;
228         default:
229                 cmp = 0;
230                 break;
231         }
232         if (cmp) {
233                 return 0;
234         }
235         return CMP_MATCH;
236 }
237
238 static struct ao2_container *get_publishes_and_update_state(void)
239 {
240         struct ao2_container *container;
241
242         container = ast_sorcery_retrieve_by_fields(
243                 ast_sip_get_sorcery(), "outbound-publish",
244                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
245
246         if (!new_states) {
247                 return container;
248         }
249
250         ao2_global_obj_replace_unref(current_states, new_states);
251         ao2_cleanup(new_states);
252         new_states = NULL;
253
254         return container;
255 }
256
257 AST_RWLIST_HEAD_STATIC(publisher_handlers, ast_sip_event_publisher_handler);
258
259 static void sub_add_handler(struct ast_sip_event_publisher_handler *handler)
260 {
261         AST_RWLIST_INSERT_TAIL(&publisher_handlers, handler, next);
262         ast_module_ref(ast_module_info->self);
263 }
264
265 static struct ast_sip_event_publisher_handler *find_publisher_handler_for_event_name(const char *event_name)
266 {
267         struct ast_sip_event_publisher_handler *iter;
268
269         AST_RWLIST_TRAVERSE(&publisher_handlers, iter, next) {
270                 if (!strcmp(iter->event_name, event_name)) {
271                         break;
272                 }
273         }
274         return iter;
275 }
276
277 /*! \brief Helper function which cancels the refresh timer on a client */
278 static void cancel_publish_refresh(struct ast_sip_outbound_publish_client *client)
279 {
280         if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client->timer)) {
281                 /* The timer was successfully cancelled, drop the refcount of the client */
282                 ao2_ref(client, -1);
283         }
284 }
285
286 /*! \brief Helper function which sets up the timer to send publication */
287 static void schedule_publish_refresh(struct ast_sip_outbound_publish_client *client, int expiration)
288 {
289         struct ast_sip_outbound_publish *publish = ao2_bump(client->publish);
290         pj_time_val delay = { .sec = 0, };
291
292         cancel_publish_refresh(client);
293
294         if (expiration > 0) {
295                 delay.sec = expiration - PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH;
296         }
297         if (publish->expiration && ((delay.sec > publish->expiration) || !delay.sec)) {
298                 delay.sec = publish->expiration;
299         }
300         if (delay.sec < PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH) {
301                 delay.sec = PJSIP_PUBLISHC_DELAY_BEFORE_REFRESH;
302         }
303
304         ao2_ref(client, +1);
305         if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client->timer, &delay) != PJ_SUCCESS) {
306                 ast_log(LOG_WARNING, "Failed to pass timed publish refresh to scheduler\n");
307                 ao2_ref(client, -1);
308         }
309         ao2_ref(publish, -1);
310 }
311
312 /*! \brief Publish client timer callback function */
313 static void sip_outbound_publish_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
314 {
315         struct ast_sip_outbound_publish_client *client = entry->user_data;
316
317         ao2_lock(client);
318         if (AST_LIST_EMPTY(&client->queue)) {
319                 /* If there are no outstanding messages send an empty PUBLISH message so our publication doesn't expire */
320                 ast_sip_publish_client_send(client, NULL);
321         }
322         ao2_unlock(client);
323
324         ao2_ref(client, -1);
325 }
326
327 /*! \brief Task for cancelling a refresh timer */
328 static int cancel_refresh_timer_task(void *data)
329 {
330         struct ast_sip_outbound_publish_client *client = data;
331
332         cancel_publish_refresh(client);
333         ao2_ref(client, -1);
334
335         return 0;
336 }
337
338 /*! \brief Task for sending an unpublish */
339 static int send_unpublish_task(void *data)
340 {
341         struct ast_sip_outbound_publish_client *client = data;
342         pjsip_tx_data *tdata;
343
344         if (pjsip_publishc_unpublish(client->client, &tdata) == PJ_SUCCESS) {
345                 if (!ast_strlen_zero(client->transport_name)) {
346                         pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
347                         ast_sip_set_tpselector_from_transport_name(client->transport_name, &selector);
348                         pjsip_tx_data_set_transport(tdata, &selector);
349                 }
350
351                 pjsip_publishc_send(client->client, tdata);
352         }
353
354         ao2_ref(client, -1);
355
356         return 0;
357 }
358
359 /*! \brief Helper function which starts or stops publish clients when applicable */
360 static void sip_outbound_publish_synchronize(struct ast_sip_event_publisher_handler *removed)
361 {
362         RAII_VAR(struct ao2_container *, publishes, get_publishes_and_update_state(), ao2_cleanup);
363         struct ao2_container *states;
364         struct ao2_iterator i;
365         struct ast_sip_outbound_publish_state *state;
366
367         if (!publishes) {
368                 return;
369         }
370
371         states = ao2_global_obj_ref(current_states);
372         if (!states) {
373                 return;
374         }
375
376         i = ao2_iterator_init(states, 0);
377         while ((state = ao2_iterator_next(&i))) {
378                 struct ast_sip_outbound_publish *publish = ao2_bump(state->client->publish);
379                 struct ast_sip_event_publisher_handler *handler = find_publisher_handler_for_event_name(publish->event);
380
381                 if (!state->client->started) {
382                         /* If the publisher client has not yet been started try to start it */
383                         if (!handler) {
384                                 ast_debug(2, "Could not find handler for event '%s' for outbound publish client '%s'\n",
385                                           publish->event, ast_sorcery_object_get_id(publish));
386                         } else if (handler->start_publishing(publish, state->client)) {
387                                 ast_log(LOG_ERROR, "Failed to start outbound publish with event '%s' for client '%s'\n",
388                                         publish->event, ast_sorcery_object_get_id(publish));
389                         } else {
390                                 state->client->started = 1;
391                         }
392                 } else if (!handler && removed && !strcmp(publish->event, removed->event_name)) {
393                         /* If the publisher client has been started but it is going away stop it */
394                         removed->stop_publishing(state->client);
395                         state->client->started = 0;
396                         if (ast_sip_push_task(NULL, cancel_refresh_timer_task, ao2_bump(state->client))) {
397                                 ast_log(LOG_WARNING, "Could not stop refresh timer on client '%s'\n",
398                                         ast_sorcery_object_get_id(publish));
399                                 ao2_ref(state->client, -1);
400                         }
401                 }
402                 ao2_ref(publish, -1);
403                 ao2_ref(state, -1);
404         }
405         ao2_iterator_destroy(&i);
406         ao2_ref(states, -1);
407 }
408
409 struct ast_sip_outbound_publish_client *ast_sip_publish_client_get(const char *name)
410 {
411         RAII_VAR(struct ao2_container *, states,
412                  ao2_global_obj_ref(current_states), ao2_cleanup);
413         RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup);
414
415         if (!states) {
416                 return NULL;
417         }
418
419         state = ao2_find(states, name, OBJ_SEARCH_KEY);
420         if (!state) {
421                 return NULL;
422         }
423
424         ao2_ref(state->client, +1);
425         return state->client;
426 }
427
428 const char *ast_sip_publish_client_get_from_uri(struct ast_sip_outbound_publish_client *client)
429 {
430         struct ast_sip_outbound_publish *publish = client->publish;
431
432         return S_OR(publish->from_uri, S_OR(publish->server_uri, ""));
433 }
434
435 const char *ast_sip_publish_client_get_to_uri(struct ast_sip_outbound_publish_client *client)
436 {
437         struct ast_sip_outbound_publish *publish = client->publish;
438
439         return S_OR(publish->to_uri, S_OR(publish->server_uri, ""));
440 }
441
442 int ast_sip_register_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
443 {
444         struct ast_sip_event_publisher_handler *existing;
445         SCOPED_LOCK(lock, &publisher_handlers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
446
447         if (!handler->start_publishing || !handler->stop_publishing) {
448                 ast_log(LOG_ERROR, "Handler does not implement required callbacks. Cannot register\n");
449                 return -1;
450         } else if (ast_strlen_zero(handler->event_name)) {
451                 ast_log(LOG_ERROR, "No event package specified for event publisher handler. Cannot register\n");
452                 return -1;
453         }
454
455         existing = find_publisher_handler_for_event_name(handler->event_name);
456         if (existing) {
457                 ast_log(LOG_ERROR, "Unable to register event publisher handler for event %s. "
458                                 "A handler is already registered\n", handler->event_name);
459                 return -1;
460         }
461
462         sub_add_handler(handler);
463
464         sip_outbound_publish_synchronize(NULL);
465
466         return 0;
467 }
468
469 void ast_sip_unregister_event_publisher_handler(struct ast_sip_event_publisher_handler *handler)
470 {
471         struct ast_sip_event_publisher_handler *iter;
472         SCOPED_LOCK(lock, &publisher_handlers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
473         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&publisher_handlers, iter, next) {
474                 if (handler == iter) {
475                         AST_RWLIST_REMOVE_CURRENT(next);
476                         ast_module_unref(ast_module_info->self);
477                         break;
478                 }
479         }
480         AST_RWLIST_TRAVERSE_SAFE_END;
481
482         sip_outbound_publish_synchronize(handler);
483 }
484
485 /*! \brief Destructor function for publish information */
486 static void sip_outbound_publish_destroy(void *obj)
487 {
488         struct ast_sip_outbound_publish *publish = obj;
489
490         ast_sip_auth_vector_destroy(&publish->outbound_auths);
491
492         ast_string_field_free_memory(publish);
493 }
494
495 /*! \brief Allocator function for publish information */
496 static void *sip_outbound_publish_alloc(const char *name)
497 {
498         struct ast_sip_outbound_publish *publish = ast_sorcery_generic_alloc(sizeof(*publish),
499                 sip_outbound_publish_destroy);
500
501         if (!publish || ast_string_field_init(publish, 256)) {
502                 ao2_cleanup(publish);
503                 return NULL;
504         }
505
506         return publish;
507 }
508
509 static void sip_outbound_publish_datastore_destroy(void *obj)
510 {
511         struct ast_datastore *datastore = obj;
512
513         /* Using the destroy function (if present) destroy the data */
514         if (datastore->info->destroy != NULL && datastore->data != NULL) {
515                 datastore->info->destroy(datastore->data);
516                 datastore->data = NULL;
517         }
518
519         ast_free((void *) datastore->uid);
520         datastore->uid = NULL;
521 }
522
523 struct ast_datastore *ast_sip_publish_client_alloc_datastore(const struct ast_datastore_info *info, const char *uid)
524 {
525         RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
526         const char *uid_ptr = uid;
527         char uuid_buf[AST_UUID_STR_LEN];
528
529         if (!info) {
530                 return NULL;
531         }
532
533         datastore = ao2_alloc(sizeof(*datastore), sip_outbound_publish_datastore_destroy);
534         if (!datastore) {
535                 return NULL;
536         }
537
538         datastore->info = info;
539         if (ast_strlen_zero(uid)) {
540                 /* They didn't provide an ID so we'll provide one ourself */
541                 uid_ptr = ast_uuid_generate_str(uuid_buf, sizeof(uuid_buf));
542         }
543
544         datastore->uid = ast_strdup(uid_ptr);
545         if (!datastore->uid) {
546                 return NULL;
547         }
548
549         ao2_ref(datastore, +1);
550         return datastore;
551 }
552
553 int ast_sip_publish_client_add_datastore(struct ast_sip_outbound_publish_client *client,
554         struct ast_datastore *datastore)
555 {
556         ast_assert(datastore != NULL);
557         ast_assert(datastore->info != NULL);
558         ast_assert(!ast_strlen_zero(datastore->uid));
559
560         if (!ao2_link(client->datastores, datastore)) {
561                 return -1;
562         }
563         return 0;
564 }
565
566 struct ast_datastore *ast_sip_publish_client_get_datastore(struct ast_sip_outbound_publish_client *client,
567         const char *name)
568 {
569         return ao2_find(client->datastores, name, OBJ_SEARCH_KEY);
570 }
571
572 void ast_sip_publish_client_remove_datastore(struct ast_sip_outbound_publish_client *client,
573         const char *name)
574 {
575         ao2_find(client->datastores, name, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA);
576 }
577
578 static int sip_publish_client_service_queue(void *data)
579 {
580         RAII_VAR(struct ast_sip_outbound_publish_client *, client, data, ao2_cleanup);
581         SCOPED_AO2LOCK(lock, client);
582         struct sip_outbound_publish_message *message;
583         pjsip_tx_data *tdata;
584         pj_status_t status;
585
586         if (client->destroy || client->sending || !(message = AST_LIST_FIRST(&client->queue))) {
587                 return 0;
588         }
589
590         if (pjsip_publishc_publish(client->client, PJ_FALSE, &tdata) != PJ_SUCCESS) {
591                 goto fatal;
592         }
593
594         if (!ast_strlen_zero(message->body.type) && !ast_strlen_zero(message->body.subtype) &&
595                 ast_sip_add_body(tdata, &message->body)) {
596                 pjsip_tx_data_dec_ref(tdata);
597                 goto fatal;
598         }
599
600         if (!ast_strlen_zero(client->transport_name)) {
601                 pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
602                 ast_sip_set_tpselector_from_transport_name(client->transport_name, &selector);
603                 pjsip_tx_data_set_transport(tdata, &selector);
604         }
605
606         status = pjsip_publishc_send(client->client, tdata);
607         if (status == PJ_EBUSY) {
608                 /* We attempted to send the message but something else got there first */
609                 goto service;
610         } else if (status != PJ_SUCCESS) {
611                 goto fatal;
612         }
613
614         client->sending = message;
615
616         return 0;
617
618 fatal:
619         AST_LIST_REMOVE_HEAD(&client->queue, entry);
620         ast_free(message);
621
622 service:
623         if (ast_sip_push_task(NULL, sip_publish_client_service_queue, ao2_bump(client))) {
624                 ao2_ref(client, -1);
625         }
626         return -1;
627 }
628
629 int ast_sip_publish_client_send(struct ast_sip_outbound_publish_client *client,
630         const struct ast_sip_body *body)
631 {
632         SCOPED_AO2LOCK(lock, client);
633         struct sip_outbound_publish_message *message;
634         size_t type_len = 0, subtype_len = 0, body_text_len = 0;
635         int res;
636
637         if (!client->client) {
638                 return -1;
639         }
640
641         /* If a body is present we need more space for the contents of it */
642         if (body) {
643                 type_len = strlen(body->type) + 1;
644                 subtype_len = strlen(body->subtype) + 1;
645                 body_text_len = strlen(body->body_text) + 1;
646         }
647
648         message = ast_calloc(1, sizeof(*message) + type_len + subtype_len + body_text_len);
649         if (!message) {
650                 return -1;
651         }
652
653         if (body) {
654                 char *dst = message->body_contents;
655
656                 message->body.type = strcpy(dst, body->type);
657                 dst += type_len;
658                 message->body.subtype = strcpy(dst, body->subtype);
659                 dst += subtype_len;
660                 message->body.body_text = strcpy(dst, body->body_text);
661         }
662
663         AST_LIST_INSERT_TAIL(&client->queue, message, entry);
664
665         res = ast_sip_push_task(NULL, sip_publish_client_service_queue, ao2_bump(client));
666         if (res) {
667                 ao2_ref(client, -1);
668         }
669
670         return res;
671 }
672
673 /*! \brief Destructor function for publish client */
674 static void sip_outbound_publish_client_destroy(void *obj)
675 {
676         struct ast_sip_outbound_publish_client *client = obj;
677         struct sip_outbound_publish_message *message;
678
679         /* You might be tempted to think "the publish client isn't being destroyed" but it actually is - just elsewhere */
680
681         while ((message = AST_LIST_REMOVE_HEAD(&client->queue, entry))) {
682                 ast_free(message);
683         }
684
685         ao2_cleanup(client->datastores);
686         ao2_cleanup(client->publish);
687         ast_free(client->transport_name);
688
689         /* if unloading the module and all objects have been unpublished
690            send the signal to finish unloading */
691         if (unloading.is_unloading) {
692                 ast_mutex_lock(&unloading.lock);
693                 if (--unloading.count == 0) {
694                         ast_cond_signal(&unloading.cond);
695                 }
696                 ast_mutex_unlock(&unloading.lock);
697         }
698 }
699
700 static int explicit_publish_destroy(void *data)
701 {
702         struct ast_sip_outbound_publish_client *client = data;
703
704         /*
705          * If there is no pjsip publishing client then we obviously don't need
706          * to destroy it. Also, the ref for the Asterisk publishing client that
707          * pjsip had would not exist or should already be gone as well.
708          */
709         if (client->client) {
710                 pjsip_publishc_destroy(client->client);
711                 ao2_ref(client, -1);
712         }
713
714         return 0;
715 }
716
717 /*! \brief Helper function which cancels and un-publishes a no longer used client */
718 static int cancel_and_unpublish(struct ast_sip_outbound_publish_client *client)
719 {
720         struct ast_sip_event_publisher_handler *handler;
721         SCOPED_AO2LOCK(lock, client);
722
723         if (!client->started) {
724                 /* If the client was never started, there's nothing to unpublish, so just
725                  * destroy the publication and remove its reference to the client.
726                  */
727                 ast_sip_push_task(NULL, explicit_publish_destroy, client);
728                 return 0;
729         }
730
731         handler = find_publisher_handler_for_event_name(client->publish->event);
732         if (handler) {
733                 handler->stop_publishing(client);
734         }
735
736         client->started = 0;
737         if (ast_sip_push_task(NULL, cancel_refresh_timer_task, ao2_bump(client))) {
738                 ast_log(LOG_WARNING, "Could not stop refresh timer on outbound publish '%s'\n",
739                         ast_sorcery_object_get_id(client->publish));
740                 ao2_ref(client, -1);
741         }
742
743         /* If nothing is being sent right now send the unpublish - the destroy will happen in the subsequent callback */
744         if (!client->sending) {
745                 if (ast_sip_push_task(NULL, send_unpublish_task, ao2_bump(client))) {
746                         ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
747                                 ast_sorcery_object_get_id(client->publish));
748                         ao2_ref(client, -1);
749                 }
750         }
751         client->destroy = 1;
752         return 0;
753 }
754
755 /*! \brief Destructor function for publish state */
756 static void sip_outbound_publish_state_destroy(void *obj)
757 {
758         struct ast_sip_outbound_publish_state *state = obj;
759
760         cancel_and_unpublish(state->client);
761         ao2_cleanup(state->client);
762 }
763
764 /*!
765  * \internal
766  * \brief Check if a publish can be reused
767  *
768  * This checks if the existing outbound publish's configuration differs from a newly-applied
769  * outbound publish.
770  *
771  * \param existing The pre-existing outbound publish
772  * \param applied The newly-created publish
773  */
774 static int can_reuse_publish(struct ast_sip_outbound_publish *existing, struct ast_sip_outbound_publish *applied)
775 {
776         int i;
777
778         if (strcmp(existing->server_uri, applied->server_uri) || strcmp(existing->from_uri, applied->from_uri) ||
779                 strcmp(existing->to_uri, applied->to_uri) || strcmp(existing->outbound_proxy, applied->outbound_proxy) ||
780                 strcmp(existing->event, applied->event) ||
781                 AST_VECTOR_SIZE(&existing->outbound_auths) != AST_VECTOR_SIZE(&applied->outbound_auths)) {
782                 return 0;
783         }
784
785         for (i = 0; i < AST_VECTOR_SIZE(&existing->outbound_auths); ++i) {
786                 if (strcmp(AST_VECTOR_GET(&existing->outbound_auths, i), AST_VECTOR_GET(&applied->outbound_auths, i))) {
787                         return 0;
788                 }
789         }
790
791         return 1;
792 }
793
794 static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param);
795
796 /*! \brief Helper function that allocates a pjsip publish client and configures it */
797 static int sip_outbound_publish_client_alloc(void *data)
798 {
799         struct ast_sip_outbound_publish_client *client = data;
800         RAII_VAR(struct ast_sip_outbound_publish *, publish, NULL, ao2_cleanup);
801         pjsip_publishc_opt opt = {
802                 .queue_request = PJ_FALSE,
803         };
804         pj_str_t event, server_uri, to_uri, from_uri;
805         pj_status_t status;
806
807         if (client->client) {
808                 return 0;
809         } else if (pjsip_publishc_create(ast_sip_get_pjsip_endpoint(), &opt, ao2_bump(client), sip_outbound_publish_callback,
810                 &client->client) != PJ_SUCCESS) {
811                 ao2_ref(client, -1);
812                 return -1;
813         }
814
815         publish = ao2_bump(client->publish);
816
817         if (!ast_strlen_zero(publish->outbound_proxy)) {
818                 pjsip_route_hdr route_set, *route;
819                 static const pj_str_t ROUTE_HNAME = { "Route", 5 };
820
821                 pj_list_init(&route_set);
822
823                 if (!(route = pjsip_parse_hdr(pjsip_publishc_get_pool(client->client), &ROUTE_HNAME,
824                         (char*)publish->outbound_proxy, strlen(publish->outbound_proxy), NULL))) {
825                         pjsip_publishc_destroy(client->client);
826                         return -1;
827                 }
828                 pj_list_insert_nodes_before(&route_set, route);
829
830                 pjsip_publishc_set_route_set(client->client, &route_set);
831         }
832
833         pj_cstr(&event, publish->event);
834         pj_cstr(&server_uri, publish->server_uri);
835         pj_cstr(&to_uri, S_OR(publish->to_uri, publish->server_uri));
836         pj_cstr(&from_uri, S_OR(publish->from_uri, publish->server_uri));
837
838         status = pjsip_publishc_init(client->client, &event, &server_uri, &from_uri, &to_uri,
839                 publish->expiration);
840         if (status == PJSIP_EINVALIDURI) {
841                 pj_pool_t *pool;
842                 pj_str_t tmp;
843                 pjsip_uri *uri;
844
845                 pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "URI Validation", 256, 256);
846                 if (!pool) {
847                         ast_log(LOG_ERROR, "Could not create pool for URI validation on outbound publish '%s'\n",
848                                 ast_sorcery_object_get_id(publish));
849                         pjsip_publishc_destroy(client->client);
850                         return -1;
851                 }
852
853                 pj_strdup2_with_null(pool, &tmp, publish->server_uri);
854                 uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
855                 if (!uri) {
856                         ast_log(LOG_ERROR, "Invalid server URI '%s' specified on outbound publish '%s'\n",
857                                 publish->server_uri, ast_sorcery_object_get_id(publish));
858                 }
859
860                 if (!ast_strlen_zero(publish->to_uri)) {
861                         pj_strdup2_with_null(pool, &tmp, publish->to_uri);
862                         uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
863                         if (!uri) {
864                                 ast_log(LOG_ERROR, "Invalid to URI '%s' specified on outbound publish '%s'\n",
865                                         publish->to_uri, ast_sorcery_object_get_id(publish));
866                         }
867                 }
868
869                 if (!ast_strlen_zero(publish->from_uri)) {
870                         pj_strdup2_with_null(pool, &tmp, publish->from_uri);
871                         uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0);
872                         if (!uri) {
873                                 ast_log(LOG_ERROR, "Invalid from URI '%s' specified on outbound publish '%s'\n",
874                                         publish->from_uri, ast_sorcery_object_get_id(publish));
875                         }
876                 }
877
878                 pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
879                 pjsip_publishc_destroy(client->client);
880                 return -1;
881         } else if (status != PJ_SUCCESS) {
882                 pjsip_publishc_destroy(client->client);
883                 return -1;
884         }
885
886         return 0;
887 }
888
889 /*! \brief Callback function for publish client responses */
890 static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
891 {
892 #define DESTROY_CLIENT() do {                      \
893         pjsip_publishc_destroy(client->client); \
894         client->client = NULL; \
895         ao2_ref(client, -1); } while (0)
896
897         RAII_VAR(struct ast_sip_outbound_publish_client *, client, ao2_bump(param->token), ao2_cleanup);
898         RAII_VAR(struct ast_sip_outbound_publish *, publish, ao2_bump(client->publish), ao2_cleanup);
899         SCOPED_AO2LOCK(lock, client);
900         pjsip_tx_data *tdata;
901
902         if (client->destroy) {
903                 if (client->sending) {
904                         client->sending = NULL;
905
906                         if (!ast_sip_push_task(NULL, send_unpublish_task, ao2_bump(client))) {
907                                 return;
908                         }
909                         ast_log(LOG_WARNING, "Could not send unpublish message on outbound publish '%s'\n",
910                                 ast_sorcery_object_get_id(publish));
911                         ao2_ref(client, -1);
912                 }
913                 /* Once the destroy is called this callback will not get called any longer, so drop the client ref */
914                 DESTROY_CLIENT();
915                 return;
916         }
917
918         if (param->code == 401 || param->code == 407) {
919                 pjsip_transaction *tsx = pjsip_rdata_get_tsx(param->rdata);
920
921                 if (!ast_sip_create_request_with_auth(&publish->outbound_auths,
922                                 param->rdata, tsx->last_tx, &tdata)) {
923                         if (!ast_strlen_zero(client->transport_name)) {
924                                 pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
925                                 ast_sip_set_tpselector_from_transport_name(client->transport_name, &selector);
926                                 pjsip_tx_data_set_transport(tdata, &selector);
927                         }
928                         pjsip_publishc_send(client->client, tdata);
929                 }
930                 client->auth_attempts++;
931
932                 if (client->auth_attempts == publish->max_auth_attempts) {
933                         DESTROY_CLIENT();
934                         ast_log(LOG_ERROR, "Reached maximum number of PUBLISH authentication attempts on outbound publish '%s'\n",
935                                 ast_sorcery_object_get_id(publish));
936
937                         goto end;
938                 }
939                 return;
940         }
941
942         client->auth_attempts = 0;
943
944         if (param->code == 412) {
945                 DESTROY_CLIENT();
946                 if (sip_outbound_publish_client_alloc(client)) {
947                         ast_log(LOG_ERROR, "Failed to create a new outbound publish client for '%s' on 412 response\n",
948                                 ast_sorcery_object_get_id(publish));
949                         goto end;
950                 }
951
952                 /* Setting this to NULL will cause a new PUBLISH to get created and sent for the same underlying body */
953                 client->sending = NULL;
954         } else if (param->code == 423) {
955                 /* Update the expiration with the new expiration time if available */
956                 pjsip_expires_hdr *expires;
957
958                 expires = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_MIN_EXPIRES, NULL);
959                 if (!expires || !expires->ivalue) {
960                         DESTROY_CLIENT();
961                         ast_log(LOG_ERROR, "Received 423 response on outbound publish '%s' without a Min-Expires header\n",
962                                 ast_sorcery_object_get_id(publish));
963                         goto end;
964                 }
965
966                 pjsip_publishc_update_expires(client->client, expires->ivalue);
967                 client->sending = NULL;
968         } else if (client->sending) {
969                 /* Remove the message currently being sent so that when the queue is serviced another will get sent */
970                 AST_LIST_REMOVE_HEAD(&client->queue, entry);
971                 ast_free(client->sending);
972                 client->sending = NULL;
973                 if (!param->rdata) {
974                         ast_log(LOG_NOTICE, "No response received for outbound publish '%s'\n",
975                                 ast_sorcery_object_get_id(publish));
976                 }
977         }
978
979         if (AST_LIST_EMPTY(&client->queue)) {
980                 schedule_publish_refresh(client, param->expiration);
981         }
982
983 end:
984         if (!client->client) {
985                 struct sip_outbound_publish_message *message;
986
987                 while ((message = AST_LIST_REMOVE_HEAD(&client->queue, entry))) {
988                         ast_free(message);
989                 }
990         } else {
991                 if (ast_sip_push_task(NULL, sip_publish_client_service_queue, ao2_bump(client))) {
992                         ao2_ref(client, -1);
993                 }
994         }
995 }
996
997 #define DATASTORE_BUCKETS 53
998
999 static int datastore_hash(const void *obj, int flags)
1000 {
1001         const struct ast_datastore *datastore;
1002         const char *uid;
1003
1004         switch (flags & OBJ_SEARCH_MASK) {
1005         case OBJ_SEARCH_KEY:
1006                 uid = obj;
1007                 break;
1008         case OBJ_SEARCH_OBJECT:
1009                 datastore = obj;
1010                 uid = datastore->uid;
1011                 break;
1012         default:
1013                 /* Hash can only work on something with a full key. */
1014                 ast_assert(0);
1015                 return 0;
1016         }
1017
1018         return ast_str_hash(uid);
1019 }
1020
1021 static int datastore_cmp(void *obj, void *arg, int flags)
1022 {
1023         const struct ast_datastore *object_left = obj;
1024         const struct ast_datastore *object_right = arg;
1025         const char *right_key = arg;
1026         int cmp;
1027
1028         switch (flags & OBJ_SEARCH_MASK) {
1029         case OBJ_SEARCH_OBJECT:
1030                 right_key = object_right->uid;
1031                 /* Fall through */
1032         case OBJ_SEARCH_KEY:
1033                 cmp = strcmp(object_left->uid, right_key);
1034                 break;
1035         case OBJ_SEARCH_PARTIAL_KEY:
1036         cmp = strncmp(object_left->uid, right_key, strlen(right_key));
1037                 break;
1038         default:
1039                 /*
1040                  * What arg points to is specific to this traversal callback
1041                  * and has no special meaning to astobj2.
1042                  */
1043                 cmp = 0;
1044                 break;
1045         }
1046         if (cmp) {
1047                 return 0;
1048         }
1049         /*
1050          * At this point the traversal callback is identical to a sorted
1051          * container.
1052          */
1053         return CMP_MATCH;
1054 }
1055
1056 /*! \brief Allocator function for publish client */
1057 static struct ast_sip_outbound_publish_state *sip_outbound_publish_state_alloc(
1058         struct ast_sip_outbound_publish *publish)
1059 {
1060         const char *id = ast_sorcery_object_get_id(publish);
1061         struct ast_sip_outbound_publish_state *state =
1062                 ao2_alloc(sizeof(*state) + strlen(id) + 1, sip_outbound_publish_state_destroy);
1063
1064         if (!state) {
1065                 return NULL;
1066         }
1067
1068         state->client = ao2_alloc(sizeof(*state->client), sip_outbound_publish_client_destroy);
1069         if (!state->client) {
1070                 ao2_ref(state, -1);
1071                 return NULL;
1072         }
1073
1074         state->client->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp);
1075         if (!state->client->datastores) {
1076                 ao2_ref(state, -1);
1077                 return NULL;
1078         }
1079
1080         state->client->timer.user_data = state->client;
1081         state->client->timer.cb = sip_outbound_publish_timer_cb;
1082         state->client->transport_name = ast_strdup(publish->transport);
1083         state->client->publish = ao2_bump(publish);
1084
1085         strcpy(state->id, id);
1086         return state;
1087 }
1088
1089 /*! \brief Apply function which finds or allocates a state structure */
1090 static int sip_outbound_publish_apply(const struct ast_sorcery *sorcery, void *obj)
1091 {
1092         RAII_VAR(struct ao2_container *, states, ao2_global_obj_ref(current_states), ao2_cleanup);
1093         RAII_VAR(struct ast_sip_outbound_publish_state *, state, NULL, ao2_cleanup);
1094         struct ast_sip_outbound_publish *applied = obj;
1095
1096         if (ast_strlen_zero(applied->server_uri)) {
1097                 ast_log(LOG_ERROR, "No server URI specified on outbound publish '%s'\n",
1098                         ast_sorcery_object_get_id(applied));
1099                 return -1;
1100         } else if (ast_strlen_zero(applied->event)) {
1101                 ast_log(LOG_ERROR, "No event type specified for outbound publish '%s'\n",
1102                         ast_sorcery_object_get_id(applied));
1103                 return -1;
1104         }
1105
1106         if (!new_states) {
1107                 /* make sure new_states has been allocated as we will be adding to it */
1108                 new_states = ao2_container_alloc_options(
1109                         AO2_ALLOC_OPT_LOCK_NOLOCK, DEFAULT_STATE_BUCKETS,
1110                         outbound_publish_state_hash, outbound_publish_state_cmp);
1111
1112                 if (!new_states) {
1113                         ast_log(LOG_ERROR, "Unable to allocate new states container\n");
1114                         return -1;
1115                 }
1116         }
1117
1118         if (states) {
1119                 state = ao2_find(states, ast_sorcery_object_get_id(obj), OBJ_SEARCH_KEY);
1120                 if (state) {
1121                         if (can_reuse_publish(state->client->publish, applied)) {
1122                                 ao2_replace(state->client->publish, applied);
1123                         } else {
1124                                 ao2_ref(state, -1);
1125                                 state = NULL;
1126                         }
1127                 }
1128         }
1129
1130         if (!state) {
1131                 state = sip_outbound_publish_state_alloc(applied);
1132                 if (!state) {
1133                         ast_log(LOG_ERROR, "Unable to create state for outbound publish '%s'\n",
1134                                 ast_sorcery_object_get_id(applied));
1135                         return -1;
1136                 };
1137         }
1138
1139         if (ast_sip_push_task_synchronous(NULL, sip_outbound_publish_client_alloc, state->client)) {
1140                 ast_log(LOG_ERROR, "Unable to create client for outbound publish '%s'\n",
1141                         ast_sorcery_object_get_id(applied));
1142                 return -1;
1143         }
1144
1145         ao2_link(new_states, state);
1146         return 0;
1147 }
1148
1149 static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
1150 {
1151         struct ast_sip_outbound_publish *publish = obj;
1152
1153         return ast_sip_auth_vector_init(&publish->outbound_auths, var->value);
1154 }
1155
1156 static int load_module(void)
1157 {
1158         CHECK_PJSIP_MODULE_LOADED();
1159
1160         ast_sorcery_apply_config(ast_sip_get_sorcery(), "res_pjsip_outbound_publish");
1161         ast_sorcery_apply_default(ast_sip_get_sorcery(), "outbound-publish", "config", "pjsip.conf,criteria=type=outbound-publish");
1162
1163         if (ast_sorcery_object_register(ast_sip_get_sorcery(), "outbound-publish", sip_outbound_publish_alloc, NULL,
1164                 sip_outbound_publish_apply)) {
1165                 ast_log(LOG_ERROR, "Unable to register 'outbound-publish' type with sorcery\n");
1166                 return AST_MODULE_LOAD_DECLINE;
1167         }
1168
1169         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "type", "", OPT_NOOP_T, 0, 0);
1170         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_outbound_publish, server_uri));
1171         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "from_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_outbound_publish, from_uri));
1172         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "event", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_outbound_publish, event));
1173         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "to_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_outbound_publish, to_uri));
1174         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_outbound_publish, outbound_proxy));
1175         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_outbound_publish, expiration));
1176         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));
1177         ast_sorcery_object_field_register(ast_sip_get_sorcery(), "outbound-publish", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_outbound_publish, transport));
1178         ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "outbound-publish", "outbound_auth", "", outbound_auth_handler, NULL, NULL, 0, 0);
1179
1180         ast_sorcery_reload_object(ast_sip_get_sorcery(), "outbound-publish");
1181
1182         AST_RWLIST_RDLOCK(&publisher_handlers);
1183         sip_outbound_publish_synchronize(NULL);
1184         AST_RWLIST_UNLOCK(&publisher_handlers);
1185
1186         pjsip_publishc_init_module(ast_sip_get_pjsip_endpoint());
1187
1188         return AST_MODULE_LOAD_SUCCESS;
1189 }
1190
1191 static int reload_module(void)
1192 {
1193         ast_sorcery_reload_object(ast_sip_get_sorcery(), "outbound-publish");
1194
1195         AST_RWLIST_RDLOCK(&publisher_handlers);
1196         sip_outbound_publish_synchronize(NULL);
1197         AST_RWLIST_UNLOCK(&publisher_handlers);
1198         return 0;
1199 }
1200
1201 static int unload_module(void)
1202 {
1203         struct timeval start = ast_tvnow();
1204         struct timespec end = {
1205                 .tv_sec = start.tv_sec + 10,
1206                 .tv_nsec = start.tv_usec * 1000
1207         };
1208         int res = 0;
1209         struct ao2_container *states = ao2_global_obj_ref(current_states);
1210
1211         if (!states || !(unloading.count = ao2_container_count(states))) {
1212                 return 0;
1213         }
1214         ao2_ref(states, -1);
1215
1216         ast_mutex_init(&unloading.lock);
1217         ast_cond_init(&unloading.cond, NULL);
1218         ast_mutex_lock(&unloading.lock);
1219
1220         unloading.is_unloading = 1;
1221         ao2_global_obj_release(current_states);
1222
1223         /* wait for items to unpublish */
1224         ast_verb(5, "Waiting to complete unpublishing task(s)\n");
1225         while (unloading.count && !res) {
1226                 res = ast_cond_timedwait(&unloading.cond, &unloading.lock, &end);
1227         }
1228         ast_mutex_unlock(&unloading.lock);
1229
1230         ast_mutex_destroy(&unloading.lock);
1231         ast_cond_destroy(&unloading.cond);
1232
1233         if (res) {
1234                 ast_verb(5, "At least %d items were unable to unpublish "
1235                         "in the allowed time\n", unloading.count);
1236         } else {
1237                 ast_verb(5, "All items successfully unpublished\n");
1238                 ast_sorcery_object_unregister(ast_sip_get_sorcery(), "outbound-publish");
1239         }
1240
1241         return res;
1242 }
1243
1244 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Outbound Publish Support",
1245         .load = load_module,
1246         .reload = reload_module,
1247         .unload = unload_module,
1248         .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1249 );