res_pjsip: Strip spaces from items parsed from comma-separated lists
[asterisk/asterisk.git] / res / res_pjsip_notify.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Kevin Harwell <kharwell@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
29 #include "asterisk/cli.h"
30 #include "asterisk/config.h"
31 #include "asterisk/manager.h"
32 #include "asterisk/module.h"
33 #include "asterisk/pbx.h"
34 #include "asterisk/res_pjsip.h"
35 #include "asterisk/sorcery.h"
36
37 /*** DOCUMENTATION
38         <manager name="PJSIPNotify" language="en_US">
39                 <synopsis>
40                         Send a NOTIFY to either an endpoint or an arbitrary URI.
41                 </synopsis>
42                 <syntax>
43                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
44                         <parameter name="Endpoint" required="false">
45                                 <para>The endpoint to which to send the NOTIFY.</para>
46                         </parameter>
47                         <parameter name="URI" required="false">
48                                 <para>Abritrary URI to which to send the NOTIFY.</para>
49                         </parameter>
50                         <parameter name="Variable" required="true">
51                                 <para>Appends variables as headers/content to the NOTIFY. If the variable is
52                                 named <literal>Content</literal>, then the value will compose the body
53                                 of the message if another variable sets <literal>Content-Type</literal>.
54                                 <replaceable>name</replaceable>=<replaceable>value</replaceable></para>
55                         </parameter>
56                 </syntax>
57                 <description>
58                         <para>Sends a NOTIFY to an endpoint or an arbitrary URI.</para>
59                         <para>All parameters for this event must be specified in the body of this
60                         request via multiple <literal>Variable: name=value</literal> sequences.</para>
61                         <note><para>One (and only one) of <literal>Endpoint</literal> or
62                         <literal>URI</literal> must be specified. If <literal>URI</literal> is used,
63                         the     default outbound endpoint will be used to send the message. If the default
64                         outbound endpoint isn't configured, this command can not send to an arbitrary
65                         URI.</para></note>
66                 </description>
67         </manager>
68         <configInfo name="res_pjsip_notify" language="en_US">
69                 <synopsis>Module that supports sending NOTIFY requests to endpoints from external sources</synopsis>
70                 <configFile name="pjsip_notify.conf">
71                         <configObject name="general">
72                                 <synopsis>Unused, but reserved.</synopsis>
73                         </configObject>
74                         <configObject name="notify">
75                                 <synopsis>Configuration of a NOTIFY request.</synopsis>
76                                 <description>
77                                         <para>Each key-value pair in a <literal>notify</literal>
78                                         configuration section defines either a SIP header to send
79                                         in the request or a line of content in the request message
80                                         body. A key of <literal>Content</literal> is treated
81                                         as part of the message body and is appended in sequential
82                                         order; any other header is treated as part of the SIP
83                                         request.</para>
84                                 </description>
85                                 <configOption name="^.*$">
86                                         <synopsis>A key/value pair to add to a NOTIFY request.</synopsis>
87                                         <description>
88                                                 <para>If the key is <literal>Content</literal>,
89                                                 it will be treated as part of the message body. Otherwise,
90                                                 it will be added as a header in the NOTIFY request.</para>
91                                                 <para>The following headers are reserved and cannot be
92                                                 specified:</para>
93                                                 <enumlist>
94                                                         <enum name="Call-ID" />
95                                                         <enum name="Contact" />
96                                                         <enum name="CSeq" />
97                                                         <enum name="To" />
98                                                         <enum name="From" />
99                                                         <enum name="Record-Route" />
100                                                         <enum name="Route" />
101                                                         <enum name="Via" />
102                                                 </enumlist>
103                                         </description>
104                                 </configOption>
105                         </configObject>
106                 </configFile>
107         </configInfo>
108  ***/
109
110 #define CONTENT_TYPE_SIZE 64
111 #define CONTENT_SIZE 512
112
113 /*!
114  * \internal
115  * \brief The configuration file containing NOTIFY payload types to send.
116  */
117 static const char notify_config[] = "pjsip_notify.conf";
118
119 struct notify_option_item {
120         const char *name;
121         const char *value;
122         char buf[0];
123 };
124
125 struct notify_option {
126         /*! Contains header and/or content information */
127         struct ao2_container *items;
128         /*! The name of the notify option */
129         char name[0];
130 };
131
132 static int notify_option_hash(const void *obj, int flags)
133 {
134         const struct notify_option *option = obj;
135         return ast_str_case_hash(flags & OBJ_KEY ? obj : option->name);
136 }
137
138 static int notify_option_cmp(void *obj, void *arg, int flags)
139 {
140         struct notify_option *option1 = obj;
141         struct notify_option *option2 = arg;
142         const char *key = flags & OBJ_KEY ? arg : option2->name;
143
144         return strcasecmp(option1->name, key) ? 0 : CMP_MATCH;
145 }
146
147 static void notify_option_destroy(void *obj)
148 {
149         struct notify_option *option = obj;
150         ao2_cleanup(option->items);
151 }
152
153 static void *notify_option_alloc(const char *category)
154 {
155         int category_size = strlen(category) + 1;
156
157         struct notify_option *option = ao2_alloc(
158                 sizeof(*option) + category_size, notify_option_destroy);
159
160         if (!option) {
161                 return NULL;
162         }
163
164         ast_copy_string(option->name, category, category_size);
165
166         if (!(option->items = ao2_container_alloc_list(
167                       AO2_ALLOC_OPT_LOCK_NOLOCK,
168                       AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW, NULL, NULL))) {
169                 ao2_cleanup(option);
170                 return NULL;
171         }
172
173         return option;
174 }
175
176 static void *notify_option_find(struct ao2_container *container, const char *category)
177 {
178         return ao2_find(container, category, OBJ_KEY);
179 }
180
181 static int notify_option_handler(const struct aco_option *opt,
182                                  struct ast_variable *var, void *obj)
183 {
184         struct notify_option *option = obj;
185
186         int name_size = strlen(var->name) + 1;
187         int value_size = strlen(var->value) + 1;
188
189         RAII_VAR(struct notify_option_item *, item,
190                  ao2_alloc(sizeof(*item) + name_size + value_size,
191                            NULL), ao2_cleanup);
192
193         item->name = item->buf;
194         item->value = item->buf + name_size;
195
196         ast_copy_string(item->buf, var->name, name_size);
197         ast_copy_string(item->buf + name_size, var->value, value_size);
198
199         if (!ao2_link(option->items, item)) {
200                 return -1;
201         }
202
203         return 0;
204 }
205
206 struct notify_cfg {
207         struct ao2_container *notify_options;
208 };
209
210 static void notify_cfg_destroy(void *obj)
211 {
212         struct notify_cfg *cfg = obj;
213         ao2_cleanup(cfg->notify_options);
214 }
215
216 static void *notify_cfg_alloc(void)
217 {
218         struct notify_cfg *cfg;
219
220         if (!(cfg = ao2_alloc(sizeof(*cfg), notify_cfg_destroy))) {
221                 return NULL;
222         }
223
224         if (!(cfg->notify_options = ao2_container_alloc_options(
225                       AO2_ALLOC_OPT_LOCK_NOLOCK, 20, notify_option_hash,
226                       notify_option_cmp))) {
227                 ao2_cleanup(cfg);
228                 return NULL;
229         }
230
231         return cfg;
232 }
233
234 static struct aco_type notify_option = {
235         .type = ACO_ITEM,
236         .name = "notify",
237         .category_match = ACO_BLACKLIST,
238         .category = "^general$",
239         .item_offset = offsetof(struct notify_cfg, notify_options),
240         .item_alloc = notify_option_alloc,
241         .item_find = notify_option_find
242 };
243
244 static struct aco_type *notify_options[] = ACO_TYPES(&notify_option);
245
246 static struct aco_file module_conf = {
247         .filename = notify_config,
248         .types = ACO_TYPES(&notify_option),
249 };
250
251 AO2_GLOBAL_OBJ_STATIC(globals);
252
253 CONFIG_INFO_STANDARD(notify_cfg, globals, notify_cfg_alloc,
254         .files = ACO_FILES(&module_conf)
255 );
256
257 /*!
258  * \internal
259  * \brief Structure to hold task data for notifications.
260  */
261 struct notify_data {
262         /*! The endpoint being notified */
263         struct ast_sip_endpoint *endpoint;
264         /*! The info of headers, types and content */
265         void *info;
266         /*! Function to help build notify request */
267         void (*build_notify)(pjsip_tx_data *, void *);
268 };
269
270 /*!
271  * \internal
272  * \brief Destroy the notify CLI data releasing any resources.
273  */
274 static void notify_cli_data_destroy(void *obj)
275 {
276         struct notify_data *data = obj;
277
278         ao2_cleanup(data->endpoint);
279         ao2_cleanup(data->info);
280 }
281
282 /*!
283  * \internal
284  * \brief Structure to hold task data for notifications (URI variant)
285  */
286 struct notify_uri_data {
287         char *uri;
288         void *info;
289         void (*build_notify)(pjsip_tx_data *, void *);
290 };
291
292 static void notify_cli_uri_data_destroy(void *obj)
293 {
294         struct notify_uri_data *data = obj;
295
296         ast_free(data->uri);
297         ao2_cleanup(data->info);
298 }
299
300 /*!
301  * \internal
302  * \brief Destroy the notify CLI data releasing any resources (URI variant)
303  */
304 static void build_cli_notify(pjsip_tx_data *tdata, void *info);
305
306 /*!
307  * \internal
308  * \brief Construct a notify data object for CLI.
309  */
310 static struct notify_data* notify_cli_data_create(
311         struct ast_sip_endpoint *endpoint, void *info)
312 {
313         struct notify_data *data = ao2_alloc(sizeof(*data),
314                                              notify_cli_data_destroy);
315         if (!data) {
316                 return NULL;
317         }
318
319         data->endpoint = endpoint;
320         ao2_ref(data->endpoint, +1);
321
322         data->info = info;
323         ao2_ref(data->info, +1);
324
325         data->build_notify = build_cli_notify;
326
327         return data;
328 }
329
330 /*!
331  * \internal
332  * \brief Construct a notify URI data object for CLI.
333  */
334 static struct notify_uri_data* notify_cli_uri_data_create(
335         const char *uri, void *info)
336 {
337         struct notify_uri_data *data = ao2_alloc(sizeof(*data),
338                 notify_cli_uri_data_destroy);
339
340         if (!data) {
341                 return NULL;
342         }
343
344         data->uri = ast_strdup(uri);
345         if (!data->uri) {
346                 ao2_ref(data, -1);
347                 return NULL;
348         }
349
350         data->info = info;
351         ao2_ref(data->info, +1);
352
353         data->build_notify = build_cli_notify;
354
355         return data;
356 }
357
358 /*!
359  * \internal
360  * \brief Destroy the notify AMI data releasing any resources.
361  */
362 static void notify_ami_data_destroy(void *obj)
363 {
364         struct notify_data *data = obj;
365         struct ast_variable *info = data->info;
366
367         ao2_cleanup(data->endpoint);
368         ast_variables_destroy(info);
369 }
370
371 /*!
372  * \internal
373  * \brief Destroy the notify AMI URI data releasing any resources.
374  */
375 static void notify_ami_uri_data_destroy(void *obj)
376 {
377         struct notify_uri_data *data = obj;
378         struct ast_variable *info = data->info;
379
380         ast_free(data->uri);
381         ast_variables_destroy(info);
382 }
383
384 static void build_ami_notify(pjsip_tx_data *tdata, void *info);
385
386 /*!
387  * \internal
388  * \brief Construct a notify data object for AMI.
389  */
390 static struct notify_data* notify_ami_data_create(
391         struct ast_sip_endpoint *endpoint, void *info)
392 {
393         struct notify_data *data = ao2_alloc(sizeof(*data),
394                                              notify_ami_data_destroy);
395         if (!data) {
396                 return NULL;
397         }
398
399         data->endpoint = endpoint;
400         ao2_ref(data->endpoint, +1);
401
402         data->info = info;
403         data->build_notify = build_ami_notify;
404
405         return data;
406 }
407
408 /*!
409  * \internal
410  * \brief Construct a notify URI data object for AMI.
411  */
412 static struct notify_uri_data* notify_ami_uri_data_create(
413         const char *uri, void *info)
414 {
415         struct notify_uri_data *data = ao2_alloc(sizeof(*data),
416                                                         notify_ami_uri_data_destroy);
417         if (!data) {
418                 return NULL;
419         }
420
421         data->uri = ast_strdup(uri);
422         if (!data->uri) {
423                 ao2_ref(data, -1);
424                 return NULL;
425         }
426
427         data->info = info;
428         data->build_notify = build_ami_notify;
429
430         return data;
431 }
432
433 /*!
434  * \internal
435  * \brief Checks if the given header name is not allowed.
436  *
437  * \details Some headers are not allowed to be set by the user within the
438  *          scope of a NOTIFY request.  If the given var header name is
439  *          found in the "not allowed" list then return true.
440  */
441 static int not_allowed(const char *name)
442 {
443         int i;
444         static const char *names[] = {
445                 "Call-ID",
446                 "Contact",
447                 "CSeq",
448                 "To",
449                 "From",
450                 "Record-Route",
451                 "Route",
452                 "Request-URI",
453                 "Via",
454         };
455
456         for (i = 0; i < ARRAY_LEN(names); ++i) {
457                 if (!strcasecmp(name, names[i])) {
458                         return 1;
459                 }
460         }
461         return 0;
462 }
463
464 /*!
465  * \internal
466  * \brief If a content type was specified add it and the content body to the
467  *        NOTIFY request.
468  */
469 static void build_notify_body(pjsip_tx_data *tdata, struct ast_str *content_type,
470                               struct ast_str *content)
471 {
472         if (content_type) {
473                 char *p;
474                 struct ast_sip_body body;
475
476                 if (content) {
477                         body.body_text = ast_str_buffer(content);
478                 }
479
480                 body.type = ast_str_buffer(content_type);
481                 if ((p = strchr(body.type, '/'))) {
482                         *p++ = '\0';
483                         body.subtype = p;
484                 }
485                 ast_sip_add_body(tdata, &body);
486         }
487 }
488
489 /*!
490  * \internal
491  * \brief Build the NOTIFY request adding content or header info.
492  */
493 static void build_notify(pjsip_tx_data *tdata, const char *name, const char *value,
494                          struct ast_str **content_type, struct ast_str **content)
495 {
496         if (not_allowed(name)) {
497                 ast_log(LOG_WARNING, "Cannot specify %s header, "
498                         "ignoring\n", name);
499                 return;
500         }
501
502         if (!strcasecmp(name, "Content-type")) {
503                 if (!(*content_type)) {
504                         *content_type = ast_str_create(CONTENT_TYPE_SIZE);
505                 }
506                 ast_str_set(content_type, 0,"%s", value);
507         } else if (!strcasecmp(name, "Content")) {
508                 if (!(*content)) {
509                         *content = ast_str_create(CONTENT_SIZE);
510                 }
511
512                 if (ast_str_strlen(*content)) {
513                         ast_str_append(content, 0, "\r\n");
514                 }
515                 ast_str_append(content, 0, "%s", value);
516         } else {
517                 ast_sip_add_header(tdata, name, value);
518         }
519 }
520
521 /*!
522  * \internal
523  * \brief Build the NOTIFY request from CLI info adding header and content
524  *        when specified.
525  */
526 static void build_cli_notify(pjsip_tx_data *tdata, void *info)
527 {
528         struct notify_option *option = info;
529         RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
530         RAII_VAR(struct ast_str *, content, NULL, ast_free);
531
532         struct notify_option_item *item;
533         struct ao2_iterator i = ao2_iterator_init(option->items, 0);
534
535         while ((item = ao2_iterator_next(&i))) {
536                 build_notify(tdata, item->name, item->value,
537                              &content_type, &content);
538                 ao2_cleanup(item);
539         }
540         ao2_iterator_destroy(&i);
541
542         build_notify_body(tdata, content_type, content);
543 }
544
545 /*!
546  * \internal
547  * \brief Build the NOTIFY request from AMI info adding header and content
548  *        when specified.
549  */
550 static void build_ami_notify(pjsip_tx_data *tdata, void *info)
551 {
552         struct ast_variable *vars = info;
553         RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
554         RAII_VAR(struct ast_str *, content, NULL, ast_free);
555         struct ast_variable *i;
556
557         for (i = vars; i; i = i->next) {
558                 if (!strcasecmp(i->name, "Content-Length")) {
559                         ast_log(LOG_NOTICE, "It is not necessary to specify Content-Length, ignoring.\n");
560                         continue;
561                 }
562                 build_notify(tdata, i->name, i->value,
563                              &content_type, &content);
564         }
565
566         build_notify_body(tdata, content_type, content);
567 }
568
569 /*!
570  * \internal
571  * \brief Build and send a NOTIFY request to a contact.
572  */
573 static int notify_contact(void *obj, void *arg, int flags)
574 {
575         struct ast_sip_contact *contact = obj;
576         struct notify_data *data = arg;
577         pjsip_tx_data *tdata;
578
579         if (ast_sip_create_request("NOTIFY", NULL, data->endpoint,
580                                    NULL, contact, &tdata)) {
581                 ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
582                         "contact %s\n", contact->uri);
583                 return -1;
584         }
585
586         ast_sip_add_header(tdata, "Subscription-State", "terminated");
587         data->build_notify(tdata, data->info);
588
589         if (ast_sip_send_request(tdata, NULL, data->endpoint, NULL, NULL)) {
590                 ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
591                         "contact %s\n", contact->uri);
592                 return -1;
593         }
594
595         return 0;
596 }
597
598 /*!
599  * \internal
600  * \brief Send a NOTIFY request to the endpoint.
601  *
602  * \detail Iterates over an endpoint's AORs sending a NOTIFY request
603  *         with the appropriate payload information to each contact.
604  */
605 static int notify_endpoint(void *obj)
606 {
607         RAII_VAR(struct notify_data *, data, obj, ao2_cleanup);
608         char *aor_name, *aors;
609
610         if (ast_strlen_zero(data->endpoint->aors)) {
611                 ast_log(LOG_WARNING, "Unable to NOTIFY - "
612                         "endpoint has no configured AORs\n");
613                 return -1;
614         }
615
616         aors = ast_strdupa(data->endpoint->aors);
617
618         while ((aor_name = ast_strip(strsep(&aors, ",")))) {
619                 RAII_VAR(struct ast_sip_aor *, aor,
620                          ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
621                 RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
622
623                 if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
624                         continue;
625                 }
626
627                 ao2_callback(contacts, OBJ_NODATA, notify_contact, data);
628         }
629
630         return 0;
631 }
632
633 /*!
634  * \internal
635  * \brief Send a notify request to the URI.
636  */
637 static int notify_uri(void *obj)
638 {
639         RAII_VAR(struct notify_uri_data *, data, obj, ao2_cleanup);
640         RAII_VAR(struct ast_sip_endpoint *, endpoint,
641                 ast_sip_default_outbound_endpoint(), ao2_cleanup);
642         pjsip_tx_data *tdata;
643
644         if (!endpoint) {
645                 ast_log(LOG_WARNING, "No default outbound endpoint set, can not send "
646                         "NOTIFY requests to arbitrary URIs.\n");
647                 return -1;
648         }
649
650         if (ast_strlen_zero(data->uri)) {
651                 ast_log(LOG_WARNING, "Unable to NOTIFY - URI is blank.\n");
652                 return -1;
653         }
654
655         if (ast_sip_create_request("NOTIFY", NULL, endpoint,
656                                    data->uri, NULL, &tdata)) {
657                 ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
658                         "uri %s\n",     data->uri);
659                 return -1;
660         }
661
662         ast_sip_add_header(tdata, "Subscription-State", "terminated");
663
664         data->build_notify(tdata, data->info);
665
666         if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) {
667                 ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
668                         "uri %s\n",     data->uri);
669                 return -1;
670         }
671
672         return 0;
673 }
674
675 enum notify_result {
676         SUCCESS,
677         INVALID_ENDPOINT,
678         ALLOC_ERROR,
679         TASK_PUSH_ERROR
680 };
681
682 typedef struct notify_data *(*task_data_create)(
683         struct ast_sip_endpoint *, void *info);
684
685 typedef struct notify_uri_data *(*task_uri_data_create)(
686         const char *uri, void *info);
687 /*!
688  * \internal
689  * \brief Send a NOTIFY request to the endpoint within a threaded task.
690  */
691 static enum notify_result push_notify(const char *endpoint_name, void *info,
692                                       task_data_create data_create)
693 {
694         RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
695         struct notify_data *data;
696
697         if (!(endpoint = ast_sorcery_retrieve_by_id(
698                       ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
699                 return INVALID_ENDPOINT;
700         }
701
702         if (!(data = data_create(endpoint, info))) {
703                 return ALLOC_ERROR;
704         }
705
706         if (ast_sip_push_task(NULL, notify_endpoint, data)) {
707                 ao2_cleanup(data);
708                 return TASK_PUSH_ERROR;
709         }
710
711         return SUCCESS;
712 }
713
714 /*!
715  * \internal
716  * \brief Send a NOTIFY request to the URI within an threaded task.
717  */
718 static enum notify_result push_notify_uri(const char *uri, void *info,
719         task_uri_data_create data_create)
720 {
721         struct notify_uri_data *data;
722
723         if (!(data = data_create(uri, info))) {
724                 return ALLOC_ERROR;
725         }
726
727         if (ast_sip_push_task(NULL, notify_uri, data)) {
728                 ao2_cleanup(data);
729                 return TASK_PUSH_ERROR;
730         }
731
732         return SUCCESS;
733 }
734
735 /*!
736  * \internal
737  * \brief Do completion on the endpoint.
738  */
739 static char *cli_complete_endpoint(const char *word, int state)
740 {
741         char *result = NULL;
742         int wordlen = strlen(word);
743         int which = 0;
744
745         struct ast_sip_endpoint *endpoint;
746         RAII_VAR(struct ao2_container *, endpoints,
747                  ast_sip_get_endpoints(), ao2_cleanup);
748
749         struct ao2_iterator i = ao2_iterator_init(endpoints, 0);
750         while ((endpoint = ao2_iterator_next(&i))) {
751                 const char *name = ast_sorcery_object_get_id(endpoint);
752                 if (!strncasecmp(word, name, wordlen) && ++which > state) {
753                         result = ast_strdup(name);
754                 }
755
756                 ao2_cleanup(endpoint);
757                 if (result) {
758                         break;
759                 }
760         }
761         ao2_iterator_destroy(&i);
762         return result;
763 }
764
765 /*!
766  * \internal
767  * \brief Do completion on the notify CLI command.
768  */
769 static char *cli_complete_notify(const char *line, const char *word,
770                                  int pos, int state, int using_uri)
771 {
772         char *c = NULL;
773
774         if (pos == 3) {
775                 int which = 0;
776                 int wordlen = strlen(word);
777
778                 RAII_VAR(struct notify_cfg *, cfg,
779                          ao2_global_obj_ref(globals), ao2_cleanup);
780                 struct notify_option *option;
781
782                 /* do completion for notify type */
783                 struct ao2_iterator i = ao2_iterator_init(cfg->notify_options, 0);
784                 while ((option = ao2_iterator_next(&i))) {
785                         if (!strncasecmp(word, option->name, wordlen) && ++which > state) {
786                                 c = ast_strdup(option->name);
787                         }
788
789                         ao2_cleanup(option);
790                         if (c) {
791                                 break;
792                         }
793                 }
794                 ao2_iterator_destroy(&i);
795                 return c;
796         }
797
798         if (pos == 4) {
799                 int wordlen = strlen(word);
800
801                 if (ast_strlen_zero(word)) {
802                     if (state == 0) {
803                         c = ast_strdup("endpoint");
804                     } else if (state == 1) {
805                         c = ast_strdup("uri");
806                     }
807                 } else if (state == 0) {
808                     if (!strncasecmp(word, "endpoint", wordlen)) {
809                         c = ast_strdup("endpoint");
810                     } else if (!strncasecmp(word, "uri", wordlen)) {
811                         c = ast_strdup("uri");
812                     }
813                 }
814
815                 return c;
816         }
817
818         return pos > 4 && !using_uri ? cli_complete_endpoint(word, state) : NULL;
819 }
820
821 /*!
822  * \internal
823  * \brief CLI command to send a SIP notify to an endpoint.
824  *
825  * \details Attempts to match the "type" given in the CLI command to a
826  *          configured one.  If found, sends a NOTIFY to the endpoint
827  *          with the associated payload.
828  */
829 static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
830 {
831         RAII_VAR(struct notify_cfg *, cfg, NULL, ao2_cleanup);
832         RAII_VAR(struct notify_option *, option, NULL, ao2_cleanup);
833
834         int i;
835         int using_uri = 0;
836
837         switch (cmd) {
838         case CLI_INIT:
839                 e->command = "pjsip send notify";
840                 e->usage =
841                         "Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n"
842                         "       Send a NOTIFY request to an endpoint\n"
843                         "       Message types are defined in pjsip_notify.conf\n";
844                 return NULL;
845         case CLI_GENERATE:
846                 if (a->argc > 4 && (!strcasecmp(a->argv[4], "uri"))) {
847                         using_uri = 1;
848                 }
849
850                 return cli_complete_notify(a->line, a->word, a->pos, a->n, using_uri);
851         }
852
853         if (a->argc < 6) {
854                 return CLI_SHOWUSAGE;
855         }
856
857         if (!strcasecmp(a->argv[4], "uri")) {
858                 using_uri = 1;
859         } else if (strcasecmp(a->argv[4], "endpoint")) {
860                 return CLI_SHOWUSAGE;
861         }
862
863         cfg = ao2_global_obj_ref(globals);
864
865         if (!(option = notify_option_find(cfg->notify_options, a->argv[3])))
866         {
867                 ast_cli(a->fd, "Unable to find notify type '%s'\n",
868                         a->argv[3]);
869                 return CLI_FAILURE;
870         }
871
872         for (i = 5; i < a->argc; ++i) {
873                 ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n",
874                         a->argv[3], a->argv[i]);
875
876                 switch (using_uri ? push_notify_uri(a->argv[i], option, notify_cli_uri_data_create) :
877                                     push_notify(a->argv[i], option, notify_cli_data_create)) {
878                 case INVALID_ENDPOINT:
879                         ast_cli(a->fd, "Unable to retrieve endpoint %s\n",
880                                 a->argv[i]);
881                         break;
882                 case ALLOC_ERROR:
883                         ast_cli(a->fd, "Unable to allocate NOTIFY task data\n");
884                         return CLI_FAILURE;
885                 case TASK_PUSH_ERROR:
886                         ast_cli(a->fd, "Unable to push NOTIFY task\n");
887                         return CLI_FAILURE;
888                 default:
889                         break;
890                 }
891         }
892
893         return CLI_SUCCESS;
894 }
895
896 static struct ast_cli_entry cli_options[] = {
897         AST_CLI_DEFINE(cli_notify, "Send a NOTIFY request to a SIP endpoint")
898 };
899
900 /*!
901  * \interanl
902  * \brief Completes SIPNotify AMI command in Endpoint mode.
903  */
904 static void manager_notify_endpoint(struct mansession *s,
905         const struct message *m, const char *endpoint_name)
906 {
907         struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
908
909         if (!strncasecmp(endpoint_name, "sip/", 4)) {
910                 endpoint_name += 4;
911         }
912
913         if (!strncasecmp(endpoint_name, "pjsip/", 6)) {
914                 endpoint_name += 6;
915         }
916
917         switch (push_notify(endpoint_name, vars, notify_ami_data_create)) {
918         case INVALID_ENDPOINT:
919                 ast_variables_destroy(vars);
920                 astman_send_error_va(s, m, "Unable to retrieve endpoint %s",
921                         endpoint_name);
922                 break;
923         case ALLOC_ERROR:
924                 ast_variables_destroy(vars);
925                 astman_send_error(s, m, "Unable to allocate NOTIFY task data");
926                 break;
927         case TASK_PUSH_ERROR:
928                 /* Don't need to destroy vars since it is handled by cleanup in push_notify */
929                 astman_send_error(s, m, "Unable to push NOTIFY task");
930                 break;
931         case SUCCESS:
932                 astman_send_ack(s, m, "NOTIFY sent");
933                 break;
934         }
935 }
936
937 /*!
938  * \internal
939  * \brief Completes SIPNotify AMI command in URI mode.
940  */
941 static void manager_notify_uri(struct mansession *s,
942         const struct message *m, const char *uri)
943 {
944         struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
945
946         switch (push_notify_uri(uri, vars, notify_ami_uri_data_create)) {
947         case INVALID_ENDPOINT:
948                 /* Shouldn't be possible. */
949                 ast_assert(0);
950                 break;
951         case ALLOC_ERROR:
952                 ast_variables_destroy(vars);
953                 astman_send_error(s, m, "Unable to allocate NOTIFY task data");
954                 break;
955         case TASK_PUSH_ERROR:
956                 /* Don't need to destroy vars since it is handled by cleanup in push_notify_uri */
957                 astman_send_error(s, m, "Unable to push Notify task");
958                 break;
959         case SUCCESS:
960                 astman_send_ack(s, m, "NOTIFY sent");
961                 break;
962         }
963 }
964
965 /*!
966  * \internal
967  * \brief AMI entry point to send a SIP notify to an endpoint.
968  */
969 static int manager_notify(struct mansession *s, const struct message *m)
970 {
971         const char *endpoint_name = astman_get_header(m, "Endpoint");
972         const char *uri = astman_get_header(m, "URI");
973
974         if (!ast_strlen_zero(endpoint_name) && !ast_strlen_zero(uri)) {
975                 astman_send_error(s, m, "PJSIPNotify action can not handle a request specifying "
976                         "both 'URI' and 'Endpoint'. You must use only one of the two.\n");
977         } else if (!ast_strlen_zero(endpoint_name)) {
978                 manager_notify_endpoint(s, m, endpoint_name);
979         } else if (!ast_strlen_zero(uri)) {
980                 manager_notify_uri(s, m, uri);
981         } else {
982                 astman_send_error(s, m, "PJSIPNotify requires either an endpoint name or a SIP URI.");
983         }
984
985         return 0;
986 }
987
988 static int load_module(void)
989 {
990         CHECK_PJSIP_MODULE_LOADED();
991
992         if (aco_info_init(&notify_cfg)) {
993                 return AST_MODULE_LOAD_DECLINE;
994         }
995
996         aco_option_register_custom(&notify_cfg, "^.*$", ACO_REGEX, notify_options,
997                                    "", notify_option_handler, 0);
998
999         if (aco_process_config(&notify_cfg, 0)) {
1000                 aco_info_destroy(&notify_cfg);
1001                 return AST_MODULE_LOAD_DECLINE;
1002         }
1003
1004         ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
1005         ast_manager_register_xml("PJSIPNotify", EVENT_FLAG_SYSTEM, manager_notify);
1006
1007         return AST_MODULE_LOAD_SUCCESS;
1008 }
1009
1010 static int reload_module(void)
1011 {
1012         if (aco_process_config(&notify_cfg, 1) == ACO_PROCESS_ERROR) {
1013                 return AST_MODULE_LOAD_DECLINE;
1014         }
1015
1016         return 0;
1017 }
1018
1019 static int unload_module(void)
1020 {
1021         ast_manager_unregister("PJSIPNotify");
1022         ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
1023         aco_info_destroy(&notify_cfg);
1024         ao2_global_obj_release(globals);
1025
1026         return 0;
1027 }
1028
1029 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP NOTIFY Support",
1030         .support_level = AST_MODULE_SUPPORT_CORE,
1031         .load = load_module,
1032         .reload = reload_module,
1033         .unload = unload_module,
1034         .load_pri = AST_MODPRI_APP_DEPEND,
1035 );