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