9f38aaac9b830482738d1c534cb036df6493115f
[asterisk/asterisk.git] / res / res_pjsip / pjsip_options.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Matt Jordan <mjordan@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 #include "asterisk.h"
20
21 #include <pjsip.h>
22 #include <pjsip_ua.h>
23 #include <pjlib.h>
24
25 #include "asterisk/res_pjsip.h"
26 #include "asterisk/channel.h"
27 #include "asterisk/pbx.h"
28 #include "asterisk/astobj2.h"
29 #include "asterisk/cli.h"
30 #include "asterisk/time.h"
31 #include "include/res_pjsip_private.h"
32
33 #define DEFAULT_LANGUAGE "en"
34 #define DEFAULT_ENCODING "text/plain"
35 #define QUALIFIED_BUCKETS 211
36
37 /*!
38  * \internal
39  * \brief Create a ast_sip_contact_status object.
40  */
41 static void *contact_status_alloc(const char *name)
42 {
43         struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), NULL);
44
45         if (!status) {
46                 ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n");
47                 return NULL;
48         }
49
50         status->status = UNAVAILABLE;
51
52         return status;
53 }
54
55 /*!
56  * \internal
57  * \brief Retrieve a ast_sip_contact_status object from sorcery creating
58  *        one if not found.
59  */
60 static struct ast_sip_contact_status *find_or_create_contact_status(const struct ast_sip_contact *contact)
61 {
62         struct ast_sip_contact_status *status = ast_sorcery_retrieve_by_id(
63                 ast_sip_get_sorcery(), CONTACT_STATUS,
64                 ast_sorcery_object_get_id(contact));
65
66         if (status) {
67                 return status;
68         }
69
70         if (!(status = ast_sorcery_alloc(
71                       ast_sip_get_sorcery(), CONTACT_STATUS,
72                       ast_sorcery_object_get_id(contact)))) {
73
74                 ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s\n",
75                         contact->uri);
76                 return NULL;
77         }
78
79         if (ast_sorcery_create(ast_sip_get_sorcery(), status)) {
80                 ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n",
81                         contact->uri);
82                 return NULL;
83         }
84
85         return status;
86 }
87
88 /*!
89  * \internal
90  * \brief Update an ast_sip_contact_status's elements.
91  */
92 static void update_contact_status(const struct ast_sip_contact *contact,
93                                   enum ast_sip_contact_status_type value)
94 {
95         RAII_VAR(struct ast_sip_contact_status *, status,
96                  find_or_create_contact_status(contact), ao2_cleanup);
97
98         RAII_VAR(struct ast_sip_contact_status *, update, ast_sorcery_alloc(
99                       ast_sip_get_sorcery(), CONTACT_STATUS,
100                       ast_sorcery_object_get_id(status)), ao2_cleanup);
101
102         if (!update) {
103                 ast_log(LOG_ERROR, "Unable to create update ast_sip_contact_status for contact %s\n",
104                         contact->uri);
105                 return;
106         }
107
108         update->status = value;
109
110         /* if the contact is available calculate the rtt as
111            the diff between the last start time and "now" */
112         update->rtt = update->status ?
113                 ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0;
114
115         update->rtt_start = ast_tv(0, 0);
116
117         if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
118                 ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
119                         contact->uri);
120         }
121 }
122
123 /*!
124  * \internal
125  * \brief Initialize the start time on a contact status so the round
126  *        trip time can be calculated upon a valid response.
127  */
128 static void init_start_time(const struct ast_sip_contact *contact)
129 {
130         RAII_VAR(struct ast_sip_contact_status *, status,
131                  find_or_create_contact_status(contact), ao2_cleanup);
132
133         RAII_VAR(struct ast_sip_contact_status *, update, ast_sorcery_alloc(
134                       ast_sip_get_sorcery(), CONTACT_STATUS,
135                       ast_sorcery_object_get_id(status)), ao2_cleanup);
136
137         if (!update) {
138                 ast_log(LOG_ERROR, "Unable to create update ast_sip_contact_status for contact %s\n",
139                         contact->uri);
140                 return;
141         }
142
143         update->rtt_start = ast_tvnow();
144
145         if (ast_sorcery_update(ast_sip_get_sorcery(), update)) {
146                 ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n",
147                         contact->uri);
148         }
149 }
150
151 /*!
152  * \internal
153  * \brief Match a container contact object with the contact sorcery id looking for.
154  *
155  * \param obj pointer to the (user-defined part) of an object.
156  * \param arg callback argument from ao2_callback()
157  * \param flags flags from ao2_callback()
158  *
159  * \return Values are a combination of enum _cb_results.
160  */
161 static int match_contact_id(void *obj, void *arg, int flags)
162 {
163         struct ast_sip_contact *contact = obj;
164         const char *looking_for = arg;
165
166         return strcmp(ast_sorcery_object_get_id(contact), looking_for) ? 0 : CMP_MATCH;
167 }
168
169 /*!
170  * \internal
171  * \brief For an endpoint try to match the given contact sorcery id.
172  */
173 static int on_endpoint(void *obj, void *arg, int flags)
174 {
175         struct ast_sip_endpoint *endpoint = obj;
176         struct ast_sip_contact *contact;
177         char *looking_for = arg;
178         char *aor_name;
179         char *aors;
180
181         if (!arg || ast_strlen_zero(endpoint->aors)) {
182                 return 0;
183         }
184
185         aors = ast_strdupa(endpoint->aors);
186
187         while ((aor_name = strsep(&aors, ","))) {
188                 RAII_VAR(struct ast_sip_aor *, aor,
189                          ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
190                 RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
191
192                 if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
193                         continue;
194                 }
195
196                 contact = ao2_callback(contacts, 0, match_contact_id, looking_for);
197                 if (contact) {
198                         ao2_ref(contact,  -1);
199                         return CMP_MATCH;
200                 }
201         }
202
203         return 0;
204 }
205
206 /*!
207  * \internal
208  * \brief Find an endpoint associated with the given contact.
209  */
210 static struct ast_sip_endpoint *find_an_endpoint(struct ast_sip_contact *contact)
211 {
212         RAII_VAR(struct ao2_container *, endpoints, ast_sip_get_endpoints(), ao2_cleanup);
213         char *looking_for = (char *) ast_sorcery_object_get_id(contact);
214
215         return ao2_callback(endpoints, 0, on_endpoint, looking_for);
216 }
217
218 /*!
219  * \internal
220  * \brief Receive an response to the qualify contact request.
221  */
222 static void qualify_contact_cb(void *token, pjsip_event *e)
223 {
224         struct ast_sip_contact *contact = token;
225
226         switch(e->body.tsx_state.type) {
227         case PJSIP_EVENT_TRANSPORT_ERROR:
228         case PJSIP_EVENT_TIMER:
229                 update_contact_status(contact, UNAVAILABLE);
230                 break;
231         default:
232                 update_contact_status(contact, AVAILABLE);
233                 break;
234         }
235         ao2_cleanup(contact);
236 }
237
238 /*!
239  * \internal
240  * \brief Attempt to qualify the contact
241  *
242  * \details Sends a SIP OPTIONS request to the given contact in order to make
243  *         sure that contact is available.
244  */
245 static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact)
246 {
247         pjsip_tx_data *tdata;
248         RAII_VAR(struct ast_sip_endpoint *, endpoint_local, NULL, ao2_cleanup);
249
250         if (contact->authenticate_qualify) {
251                 endpoint_local = ao2_bump(endpoint);
252                 if (!endpoint_local) {
253                         /*
254                          * Find the "first" endpoint to completely qualify the contact - any
255                          * endpoint that is associated with the contact should do.
256                          */
257                         endpoint_local = find_an_endpoint(contact);
258                         if (!endpoint_local) {
259                                 ast_log(LOG_ERROR, "Unable to find an endpoint to qualify contact %s\n",
260                                         contact->uri);
261                                 return -1;
262                         }
263                 }
264         }
265
266         if (ast_sip_create_request("OPTIONS", NULL, NULL, NULL, contact, &tdata)) {
267                 ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n",
268                         contact->uri);
269                 return -1;
270         }
271
272         /* If an outbound proxy is specified set it on this request */
273         if (!ast_strlen_zero(contact->outbound_proxy) &&
274                 ast_sip_set_outbound_proxy(tdata, contact->outbound_proxy)) {
275                 pjsip_tx_data_dec_ref(tdata);
276                 ast_log(LOG_ERROR, "Unable to apply outbound proxy on request to qualify contact %s\n",
277                         contact->uri);
278                 return -1;
279         }
280
281         init_start_time(contact);
282
283         ao2_ref(contact, +1);
284         if (ast_sip_send_request(tdata, NULL, endpoint_local, contact, qualify_contact_cb)
285                 != PJ_SUCCESS) {
286                 ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n",
287                         contact->uri);
288                 ao2_ref(contact, -1);
289                 return -1;
290         }
291
292         return 0;
293 }
294
295 /*!
296  * \internal
297  * \brief Scheduling context for sending QUALIFY request at specified intervals.
298  */
299 static struct ast_sched_context *sched;
300
301 /*!
302  * \internal
303  * \brief Container to hold all actively scheduled qualifies.
304  */
305 static struct ao2_container *sched_qualifies;
306
307 /*!
308  * \internal
309  * \brief Structure to hold qualify contact scheduling information.
310  */
311 struct sched_data {
312         /*! The scheduling id */
313         int id;
314         /*! The the contact being checked */
315         struct ast_sip_contact *contact;
316 };
317
318 /*!
319  * \internal
320  * \brief Destroy the scheduled data and remove from scheduler.
321  */
322 static void sched_data_destructor(void *obj)
323 {
324         struct sched_data *data = obj;
325         ao2_cleanup(data->contact);
326 }
327 /*!
328  * \internal
329  * \brief Create the scheduling data object.
330  */
331 static struct sched_data *sched_data_create(struct ast_sip_contact *contact)
332 {
333         struct sched_data *data = ao2_alloc(sizeof(*data), sched_data_destructor);
334
335         if (!data) {
336                 ast_log(LOG_ERROR, "Unable to create schedule qualify data\n");
337                 return NULL;
338         }
339
340         data->contact = contact;
341         ao2_ref(data->contact, +1);
342
343         return data;
344 }
345
346 /*!
347  * \internal
348  * \brief Send a qualify contact request within a threaded task.
349  */
350 static int qualify_contact_task(void *obj)
351 {
352         RAII_VAR(struct ast_sip_contact *, contact, obj, ao2_cleanup);
353         return qualify_contact(NULL, contact);
354 }
355
356 /*!
357  * \internal
358  * \brief Send a scheduled qualify contact request.
359  */
360 static int qualify_contact_sched(const void *obj)
361 {
362         struct sched_data *data = (struct sched_data *)obj;
363
364         ao2_ref(data->contact, +1);
365         if (ast_sip_push_task(NULL, qualify_contact_task, data->contact)) {
366                 ao2_ref(data->contact, -1);
367                 ao2_cleanup(data);
368                 return 0;
369         }
370
371         return data->contact->qualify_frequency * 1000;
372 }
373
374 /*!
375  * \internal
376  * \brief Set up a scheduled qualify contact check.
377  */
378 static void schedule_qualify(struct ast_sip_contact *contact)
379 {
380         RAII_VAR(struct sched_data *, data, sched_data_create(contact), ao2_cleanup);
381
382         if (!data) {
383                 return;
384         }
385
386         ao2_ref(data, +1);
387         if ((data->id = ast_sched_add_variable(
388                     sched, contact->qualify_frequency * 1000,
389                     qualify_contact_sched, data, 1)) < 0) {
390
391                 ao2_ref(data, -1);
392                 ast_log(LOG_ERROR, "Unable to schedule qualify for contact %s\n",
393                         contact->uri);
394                 return;
395         }
396
397         ao2_link(sched_qualifies, data);
398 }
399
400 /*!
401  * \internal
402  * \brief Remove the contact from the scheduler.
403  */
404 static void unschedule_qualify(struct ast_sip_contact *contact)
405 {
406         struct sched_data *data;
407
408         data = ao2_find(sched_qualifies, contact, OBJ_UNLINK | OBJ_SEARCH_KEY);
409         if (!data) {
410                 return;
411         }
412
413         AST_SCHED_DEL_UNREF(sched, data->id, ao2_cleanup(data));
414         ao2_ref(data, -1);
415 }
416
417 /*!
418  * \internal
419  * \brief Qualify the given contact and set up scheduling if configured.
420  */
421 static void qualify_and_schedule(struct ast_sip_contact *contact)
422 {
423         unschedule_qualify(contact);
424
425         if (contact->qualify_frequency) {
426                 ao2_ref(contact, +1);
427                 ast_sip_push_task(NULL, qualify_contact_task, contact);
428
429                 schedule_qualify(contact);
430         }
431 }
432
433 /*!
434  * \internal
435  * \brief A new contact has been created make sure it is available.
436  */
437 static void contact_created(const void *obj)
438 {
439         qualify_and_schedule((struct ast_sip_contact *)obj);
440 }
441
442 /*!
443  * \internal
444  * \brief A contact has been deleted remove status tracking.
445  */
446 static void contact_deleted(const void *obj)
447 {
448         struct ast_sip_contact *contact = (struct ast_sip_contact *)obj;
449         RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup);
450
451         unschedule_qualify(contact);
452
453         if (!(status = ast_sorcery_retrieve_by_id(
454                       ast_sip_get_sorcery(), CONTACT_STATUS,
455                       ast_sorcery_object_get_id(contact)))) {
456                 return;
457         }
458
459         if (ast_sorcery_delete(ast_sip_get_sorcery(), status)) {
460                 ast_log(LOG_ERROR, "Unable to delete ast_sip_contact_status for contact %s\n",
461                         contact->uri);
462         }
463 }
464
465 static const struct ast_sorcery_observer contact_observer = {
466         .created = contact_created,
467         .deleted = contact_deleted
468 };
469
470 static pj_bool_t options_start(void)
471 {
472         if (!(sched = ast_sched_context_create()) ||
473             ast_sched_start_thread(sched)) {
474                 return -1;
475         }
476
477         return PJ_SUCCESS;
478 }
479
480 static pj_bool_t options_stop(void)
481 {
482         ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_observer);
483
484         ao2_t_ref(sched_qualifies, -1, "Remove scheduled qualifies on module stop");
485
486         if (sched) {
487                 ast_sched_context_destroy(sched);
488         }
489
490         return PJ_SUCCESS;
491 }
492
493 static pj_status_t send_options_response(pjsip_rx_data *rdata, int code)
494 {
495         pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint();
496         pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
497         pjsip_transaction *trans = pjsip_rdata_get_tsx(rdata);
498         pjsip_tx_data *tdata;
499         const pjsip_hdr *hdr;
500         pjsip_response_addr res_addr;
501         pj_status_t status;
502
503         /* Make the response object */
504         if ((status = ast_sip_create_response(rdata, code, NULL, &tdata) != PJ_SUCCESS)) {
505                 ast_log(LOG_ERROR, "Unable to create response (%d)\n", status);
506                 return status;
507         }
508
509         /* Add appropriate headers */
510         if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) {
511                 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
512         }
513         if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) {
514                 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
515         }
516         if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) {
517                 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr));
518         }
519
520         /*
521          * XXX TODO: pjsip doesn't care a lot about either of these headers -
522          * while it provides specific methods to create them, they are defined
523          * to be the standard string header creation. We never did add them
524          * in chan_sip, although RFC 3261 says they SHOULD. Hard coded here.
525          */
526         ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING);
527         ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE);
528
529         if (dlg && trans) {
530                 status = pjsip_dlg_send_response(dlg, trans, tdata);
531         } else {
532                 /* Get where to send request. */
533                 if ((status = pjsip_get_response_addr(
534                              tdata->pool, rdata, &res_addr)) != PJ_SUCCESS) {
535                         ast_log(LOG_ERROR, "Unable to get response address (%d)\n",
536                                 status);
537
538                         pjsip_tx_data_dec_ref(tdata);
539                         return status;
540                 }
541                 status = ast_sip_send_response(&res_addr, tdata,
542                                                    ast_pjsip_rdata_get_endpoint(rdata));
543         }
544
545         if (status != PJ_SUCCESS) {
546                 ast_log(LOG_ERROR, "Unable to send response (%d)\n", status);
547         }
548
549         return status;
550 }
551
552 static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
553 {
554         RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
555         pjsip_uri *ruri;
556         pjsip_sip_uri *sip_ruri;
557         char exten[AST_MAX_EXTENSION];
558
559         if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
560                              &pjsip_options_method)) {
561                 return PJ_FALSE;
562         }
563
564         if (!(endpoint = ast_pjsip_rdata_get_endpoint(rdata))) {
565                 return PJ_FALSE;
566         }
567
568         ruri = rdata->msg_info.msg->line.req.uri;
569         if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) {
570                 send_options_response(rdata, 416);
571                 return -1;
572         }
573
574         sip_ruri = pjsip_uri_get_uri(ruri);
575         ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten));
576
577         if (ast_shutting_down()) {
578                 send_options_response(rdata, 503);
579         } else if (!ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) {
580                 send_options_response(rdata, 404);
581         } else {
582                 send_options_response(rdata, 200);
583         }
584         return PJ_TRUE;
585 }
586
587 static pjsip_module options_module = {
588         .name = {"Options Module", 14},
589         .id = -1,
590         .priority = PJSIP_MOD_PRIORITY_APPLICATION,
591         .start = options_start,
592         .stop = options_stop,
593         .on_rx_request = options_on_rx_request,
594 };
595
596 /*!
597  * \internal
598  * \brief Send qualify request to the given contact.
599  */
600 static int cli_on_contact(void *obj, void *arg, void *data, int flags)
601 {
602         struct ast_sip_contact *contact = obj;
603         struct ast_sip_endpoint *endpoint = data;
604         int *cli_fd = arg;
605
606         ast_cli(*cli_fd, " contact %s\n", contact->uri);
607         qualify_contact(endpoint, contact);
608         return 0;
609 }
610
611 /*!
612  * \brief Data pushed to threadpool to qualify endpoints from the CLI
613  */
614 struct qualify_data {
615         /*! Endpoint that is being qualified */
616         struct ast_sip_endpoint *endpoint;
617         /*! CLI File descriptor for printing messages */
618         int cli_fd;
619 };
620
621 static struct qualify_data *qualify_data_alloc(struct ast_sip_endpoint *endpoint, int cli_fd)
622 {
623         struct qualify_data *qual_data;
624
625         qual_data = ast_malloc(sizeof(*qual_data));
626         if (!qual_data) {
627                 return NULL;
628         }
629
630         qual_data->endpoint = ao2_bump(endpoint);
631         qual_data->cli_fd = cli_fd;
632         return qual_data;
633 }
634
635 static void qualify_data_destroy(struct qualify_data *qual_data)
636 {
637         ao2_cleanup(qual_data->endpoint);
638         ast_free(qual_data);
639 }
640
641 /*!
642  * \internal
643  * \brief For an endpoint iterate over and qualify all aors/contacts
644  */
645 static int cli_qualify_contacts(void *data)
646 {
647         char *aor_name, *aors;
648         RAII_VAR(struct qualify_data *, qual_data, data, qualify_data_destroy);
649         struct ast_sip_endpoint *endpoint = qual_data->endpoint;
650         int cli_fd = qual_data->cli_fd;
651         const char *endpoint_name = ast_sorcery_object_get_id(endpoint);
652
653         if (ast_strlen_zero(endpoint->aors)) {
654                 ast_cli(cli_fd, "Endpoint %s has no AoR's configured\n",
655                         endpoint_name);
656                 return 0;
657         }
658
659         aors = ast_strdupa(endpoint->aors);
660
661         while ((aor_name = strsep(&aors, ","))) {
662                 RAII_VAR(struct ast_sip_aor *, aor,
663                          ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
664                 RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
665
666                 if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
667                         continue;
668                 }
669
670                 ast_cli(cli_fd, "Sending qualify to endpoint %s\n", endpoint_name);
671                 ao2_callback_data(contacts, OBJ_NODATA, cli_on_contact, &cli_fd, endpoint);
672         }
673         return 0;
674 }
675
676 static char *cli_qualify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
677 {
678         RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
679         const char *endpoint_name;
680         struct qualify_data *qual_data;
681
682         switch (cmd) {
683         case CLI_INIT:
684                 e->command = "pjsip qualify";
685                 e->usage =
686                         "Usage: pjsip qualify <endpoint>\n"
687                         "       Send a SIP OPTIONS request to all contacts on the endpoint.\n";
688                 return NULL;
689         case CLI_GENERATE:
690                 return NULL;
691         }
692
693         if (a->argc != 3) {
694                 return CLI_SHOWUSAGE;
695         }
696
697         endpoint_name = a->argv[2];
698
699         if (!(endpoint = ast_sorcery_retrieve_by_id(
700                       ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
701                 ast_cli(a->fd, "Unable to retrieve endpoint %s\n", endpoint_name);
702                 return CLI_FAILURE;
703         }
704
705         qual_data = qualify_data_alloc(endpoint, a->fd);
706         if (!qual_data) {
707                 return CLI_FAILURE;
708         }
709
710         if (ast_sip_push_task(NULL, cli_qualify_contacts, qual_data)) {
711                 qualify_data_destroy(qual_data);
712                 return CLI_FAILURE;
713         }
714
715         return CLI_SUCCESS;
716 }
717
718 /*!
719  * \internal
720  * \brief Send qualify request to the given contact.
721  */
722 static int ami_contact_cb(void *obj, void *arg, int flags)
723 {
724         struct ast_sip_contact *contact = obj;
725         ao2_ref(contact, +1);
726         if (ast_sip_push_task(NULL, qualify_contact_task, contact)) {
727                 ao2_cleanup(contact);
728         }
729         return 0;
730 }
731
732 static int ami_sip_qualify(struct mansession *s, const struct message *m)
733 {
734         const char *endpoint_name = astman_get_header(m, "Endpoint");
735         RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
736         char *aor_name, *aors;
737
738         if (ast_strlen_zero(endpoint_name)) {
739                 astman_send_error(s, m, "Endpoint parameter missing.");
740                 return 0;
741         }
742
743         endpoint = ast_sorcery_retrieve_by_id(
744                 ast_sip_get_sorcery(),
745                 "endpoint",
746                 endpoint_name);
747         if (!endpoint) {
748                 astman_send_error(s, m, "Unable to retrieve endpoint\n");
749                 return 0;
750         }
751
752         /* send a qualify for all contacts registered with the endpoint */
753         if (ast_strlen_zero(endpoint->aors)) {
754                 astman_send_error(s, m, "No AoRs configured for endpoint\n");
755                 return 0;
756         }
757
758         aors = ast_strdupa(endpoint->aors);
759
760         while ((aor_name = strsep(&aors, ","))) {
761                 RAII_VAR(struct ast_sip_aor *, aor,
762                          ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
763                 RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
764
765                 if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
766                         continue;
767                 }
768
769                 ao2_callback(contacts, OBJ_NODATA, ami_contact_cb, NULL);
770         }
771
772         astman_send_ack(s, m, "Endpoint found, will qualify");
773         return 0;
774 }
775
776 static struct ast_cli_entry cli_options[] = {
777         AST_CLI_DEFINE(cli_qualify, "Send an OPTIONS request to a PJSIP endpoint")
778 };
779
780 static int sched_qualifies_hash_fn(const void *obj, int flags)
781 {
782         const struct sched_data *object;
783         const struct ast_sip_contact *key;
784
785         switch (flags & OBJ_SEARCH_MASK) {
786         case OBJ_SEARCH_KEY:
787                 key = obj;
788                 break;
789         case OBJ_SEARCH_OBJECT:
790                 object = obj;
791                 key = object->contact;
792                 break;
793         default:
794                 /* Hash can only work on something with a full key. */
795                 ast_assert(0);
796                 return 0;
797         }
798         return ast_str_hash(ast_sorcery_object_get_id(key));
799 }
800
801 static int sched_qualifies_cmp_fn(void *obj, void *arg, int flags)
802 {
803         const struct sched_data *object_left = obj;
804         const struct sched_data *object_right = arg;
805         struct ast_sip_contact *right_key = arg;
806         int cmp;
807
808         switch (flags & OBJ_SEARCH_MASK) {
809         case OBJ_SEARCH_OBJECT:
810                 right_key = object_right->contact;
811                 /* Fall through */
812         case OBJ_SEARCH_KEY:
813                 cmp = strcmp(ast_sorcery_object_get_id(object_left->contact),
814                         ast_sorcery_object_get_id(right_key));
815                 break;
816         case OBJ_SEARCH_PARTIAL_KEY:
817                 /* Not supported by container. */
818                 ast_assert(0);
819                 return 0;
820         default:
821                 /*
822                  * What arg points to is specific to this traversal callback
823                  * and has no special meaning to astobj2.
824                  */
825                 cmp = 0;
826                 break;
827         }
828         if (cmp) {
829                 return 0;
830         }
831         /*
832          * At this point the traversal callback is identical to a sorted
833          * container.
834          */
835         return CMP_MATCH;
836 }
837
838 int ast_sip_initialize_sorcery_qualify(void)
839 {
840         struct ast_sorcery *sorcery = ast_sip_get_sorcery();
841
842         /* initialize sorcery ast_sip_contact_status resource */
843         ast_sorcery_apply_default(sorcery, CONTACT_STATUS, "memory", NULL);
844
845         if (ast_sorcery_internal_object_register(sorcery, CONTACT_STATUS,
846                                         contact_status_alloc, NULL, NULL)) {
847                 ast_log(LOG_ERROR, "Unable to register ast_sip_contact_status in sorcery\n");
848                 return -1;
849         }
850
851         ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt", "0", OPT_UINT_T,
852                                           1, FLDSET(struct ast_sip_contact_status, status));
853         ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt", "0", OPT_UINT_T,
854                                           1, FLDSET(struct ast_sip_contact_status, rtt));
855
856         return 0;
857 }
858
859 static int qualify_and_schedule_cb(void *obj, void *arg, int flags)
860 {
861         struct ast_sip_contact *contact = obj;
862         struct ast_sip_aor *aor = arg;
863
864         contact->qualify_frequency = aor->qualify_frequency;
865         contact->authenticate_qualify = aor->authenticate_qualify;
866
867         qualify_and_schedule(contact);
868
869         return 0;
870 }
871
872 /*!
873  * \internal
874  * \brief Qualify and schedule an endpoint's contacts
875  *
876  * \details For the given endpoint retrieve its list of aors, qualify all
877  *         contacts, and schedule for checks if configured.
878  */
879 static int qualify_and_schedule_all_cb(void *obj, void *arg, int flags)
880 {
881         struct ast_sip_endpoint *endpoint = obj;
882         char *aor_name, *aors;
883
884         if (ast_strlen_zero(endpoint->aors)) {
885                 return 0;
886         }
887
888         aors = ast_strdupa(endpoint->aors);
889
890         while ((aor_name = strsep(&aors, ","))) {
891                 RAII_VAR(struct ast_sip_aor *, aor,
892                          ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
893                 struct ao2_container *contacts;
894
895                 if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
896                         continue;
897                 }
898
899                 ao2_callback(contacts, OBJ_NODATA, qualify_and_schedule_cb, aor);
900                 ao2_ref(contacts, -1);
901         }
902
903         return 0;
904 }
905
906 static void qualify_and_schedule_all(void)
907 {
908         struct ao2_container *endpoints = ast_sip_get_endpoints();
909
910         if (!endpoints) {
911                 return;
912         }
913
914         ao2_callback(endpoints, OBJ_NODATA,
915                      qualify_and_schedule_all_cb, NULL);
916         ao2_ref(endpoints, -1);
917 }
918
919 static const char *status_map [] = {
920         [UNAVAILABLE] = "Unreachable",
921         [AVAILABLE] = "Reachable",
922 };
923
924 static int format_contact_status(void *obj, void *arg, int flags)
925 {
926         struct ast_sip_contact_wrapper *wrapper = obj;
927         struct ast_sip_contact *contact = wrapper->contact;
928         struct ast_sip_ami *ami = arg;
929         struct ast_sip_contact_status *status;
930         struct ast_str *buf;
931         const struct ast_sip_endpoint *endpoint = ami->arg;
932
933         buf = ast_sip_create_ami_event("ContactStatusDetail", ami);
934         if (!buf) {
935                 return -1;
936         }
937
938         status = ast_sorcery_retrieve_by_id(
939                 ast_sip_get_sorcery(), CONTACT_STATUS,
940                 ast_sorcery_object_get_id(contact));
941
942         ast_str_append(&buf, 0, "AOR: %s\r\n", wrapper->aor_id);
943         ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri);
944         if (status) {
945                 ast_str_append(&buf, 0, "Status: %s\r\n", status_map[status->status]);
946                 ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt);
947         } else {
948                 ast_str_append(&buf, 0, "Status: Unknown\r\n");
949                 ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n");
950         }
951         ast_str_append(&buf, 0, "EndpointName: %s\r\n",
952                         ast_sorcery_object_get_id(endpoint));
953         astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
954
955         ast_free(buf);
956         ao2_cleanup(status);
957         return 0;
958 }
959
960 static int format_contact_status_for_aor(void *obj, void *arg, int flags)
961 {
962         struct ast_sip_aor *aor = obj;
963
964         return ast_sip_for_each_contact(aor, format_contact_status, arg);
965 }
966
967 static int format_ami_contact_status(const struct ast_sip_endpoint *endpoint,
968                 struct ast_sip_ami *ami)
969 {
970         ami->arg = (void *)endpoint;
971         return ast_sip_for_each_aor(endpoint->aors, format_contact_status_for_aor, ami);
972 }
973
974 static struct ast_sip_endpoint_formatter contact_status_formatter = {
975         .format_ami = format_ami_contact_status
976 };
977
978 int ast_res_pjsip_init_options_handling(int reload)
979 {
980         const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
981
982         if (reload) {
983                 qualify_and_schedule_all();
984                 return 0;
985         }
986
987         if (!(sched_qualifies = ao2_t_container_alloc(
988                 QUALIFIED_BUCKETS, sched_qualifies_hash_fn, sched_qualifies_cmp_fn,
989                 "Create container for scheduled qualifies"))) {
990                 return -1;
991         }
992
993         if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module) != PJ_SUCCESS) {
994                 options_stop();
995                 return -1;
996         }
997
998         if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_OPTIONS) != PJ_SUCCESS) {
999                 pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module);
1000                 return -1;
1001         }
1002
1003         if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_observer)) {
1004                 ast_log(LOG_WARNING, "Unable to add contact observer\n");
1005                 return -1;
1006         }
1007
1008         ast_sip_register_endpoint_formatter(&contact_status_formatter);
1009
1010         qualify_and_schedule_all();
1011         ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
1012         ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify, NULL, NULL, NULL);
1013
1014         return 0;
1015 }
1016
1017 void ast_res_pjsip_cleanup_options_handling(void)
1018 {
1019         ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
1020         ast_manager_unregister("PJSIPQualify");
1021         ast_sip_unregister_endpoint_formatter(&contact_status_formatter);
1022 }