Asterisk - The Open Source Telephony Project GIT-master-2de1a68
res_pjsip_notify.c
Go to the documentation of this file.
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"
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="Option" required="false">
56 <para>The config section name from <literal>pjsip_notify.conf</literal> to use.</para>
57 <para>One of Option or Variable must be specified.</para>
58 </parameter>
59 <parameter name="Variable" required="false">
60 <para>Appends variables as headers/content to the NOTIFY. If the variable is
61 named <literal>Content</literal>, then the value will compose the body
62 of the message if another variable sets <literal>Content-Type</literal>.
63 <replaceable>name</replaceable>=<replaceable>value</replaceable></para>
64 <para>One of Option or Variable must be specified.</para>
65 </parameter>
66 </syntax>
67 <description>
68 <para>Sends a NOTIFY to an endpoint, an arbitrary URI, or inside a SIP dialog.</para>
69 <para>All parameters for this event must be specified in the body of this
70 request via multiple <literal>Variable: name=value</literal> sequences.</para>
71 <note><para>One (and only one) of <literal>Endpoint</literal>,
72 <literal>URI</literal>, or <literal>Channel</literal> must be specified.
73 If <literal>URI</literal> is used, the default outbound endpoint will be used
74 to send the message. If the default outbound endpoint isn't configured, this command
75 can not send to an arbitrary URI.</para></note>
76 </description>
77 </manager>
78 <configInfo name="res_pjsip_notify" language="en_US">
79 <synopsis>Module that supports sending NOTIFY requests to endpoints from external sources</synopsis>
80 <configFile name="pjsip_notify.conf">
81 <configObject name="general">
82 <synopsis>Unused, but reserved.</synopsis>
83 </configObject>
84 <configObject name="notify">
85 <synopsis>Configuration of a NOTIFY request.</synopsis>
86 <description>
87 <para>Each key-value pair in a <literal>notify</literal>
88 configuration section defines either a SIP header to send
89 in the request or a line of content in the request message
90 body. A key of <literal>Content</literal> is treated
91 as part of the message body and is appended in sequential
92 order; any other header is treated as part of the SIP
93 request.</para>
94 </description>
95 <configOption name="">
96 <synopsis>A key/value pair to add to a NOTIFY request.</synopsis>
97 <description>
98 <para>If the key is <literal>Content</literal>,
99 it will be treated as part of the message body. Otherwise,
100 it will be added as a header in the NOTIFY request.</para>
101 <para>The following headers are reserved and cannot be
102 specified:</para>
103 <enumlist>
104 <enum name="Call-ID" />
105 <enum name="Contact" />
106 <enum name="CSeq" />
107 <enum name="To" />
108 <enum name="From" />
109 <enum name="Record-Route" />
110 <enum name="Route" />
111 <enum name="Via" />
112 </enumlist>
113 </description>
114 </configOption>
115 </configObject>
116 </configFile>
117 </configInfo>
118 ***/
119
120#define CONTENT_TYPE_SIZE 64
121#define CONTENT_SIZE 512
122
123/*!
124 * \internal
125 * \brief The configuration file containing NOTIFY payload types to send.
126 */
127static const char notify_config[] = "pjsip_notify.conf";
128
130 const char *name;
131 const char *value;
132 char buf[0];
133};
134
136 /*! Contains header and/or content information */
138 /*! The name of the notify option */
139 char name[0];
140};
141
142static int notify_option_hash(const void *obj, int flags)
143{
144 const struct notify_option *option = obj;
145 return ast_str_case_hash(flags & OBJ_KEY ? obj : option->name);
146}
147
148static int notify_option_cmp(void *obj, void *arg, int flags)
149{
150 struct notify_option *option1 = obj;
151 struct notify_option *option2 = arg;
152 const char *key = flags & OBJ_KEY ? arg : option2->name;
153
154 return strcasecmp(option1->name, key) ? 0 : CMP_MATCH;
155}
156
157static void notify_option_destroy(void *obj)
158{
159 struct notify_option *option = obj;
160 ao2_cleanup(option->items);
161}
162
163static void *notify_option_alloc(const char *category)
164{
165 int category_size = strlen(category) + 1;
166
167 struct notify_option *option = ao2_alloc(
168 sizeof(*option) + category_size, notify_option_destroy);
169
170 if (!option) {
171 return NULL;
172 }
173
174 ast_copy_string(option->name, category, category_size);
175
176 if (!(option->items = ao2_container_alloc_list(
179 ao2_cleanup(option);
180 return NULL;
181 }
182
183 return option;
184}
185
186static void *notify_option_find(struct ao2_container *container, const char *category)
187{
188 return ao2_find(container, category, OBJ_KEY);
189}
190
191static int notify_option_handler(const struct aco_option *opt,
192 struct ast_variable *var, void *obj)
193{
194 struct notify_option *option = obj;
195
196 int name_size = strlen(var->name) + 1;
197 int value_size = strlen(var->value) + 1;
198
200 ao2_alloc(sizeof(*item) + name_size + value_size,
201 NULL), ao2_cleanup);
202
203 item->name = item->buf;
204 item->value = item->buf + name_size;
205
206 ast_copy_string(item->buf, var->name, name_size);
207 ast_copy_string(item->buf + name_size, var->value, value_size);
208
209 if (!ao2_link(option->items, item)) {
210 return -1;
211 }
212
213 return 0;
214}
215
218};
219
220static void notify_cfg_destroy(void *obj)
221{
222 struct notify_cfg *cfg = obj;
224}
225
226static void *notify_cfg_alloc(void)
227{
228 struct notify_cfg *cfg;
229
230 if (!(cfg = ao2_alloc(sizeof(*cfg), notify_cfg_destroy))) {
231 return NULL;
232 }
233
236 if (!cfg->notify_options) {
237 ao2_cleanup(cfg);
238 return NULL;
239 }
240
241 return cfg;
242}
243
244static struct aco_type notify_option = {
245 .type = ACO_ITEM,
246 .name = "notify",
247 .category_match = ACO_BLACKLIST_EXACT,
248 .category = "general",
249 .item_offset = offsetof(struct notify_cfg, notify_options),
250 .item_alloc = notify_option_alloc,
251 .item_find = notify_option_find
252};
253
255
256static struct aco_file module_conf = {
258 .types = ACO_TYPES(&notify_option),
259};
260
262
264 .files = ACO_FILES(&module_conf)
265);
266
267/*!
268 * \internal
269 * \brief Structure to hold task data for notifications.
270 */
272 /*! The endpoint being notified */
274 /*! The info of headers, types and content */
275 void *info;
276 /*! Function to help build notify request */
277 void (*build_notify)(pjsip_tx_data *, void *);
278};
279
280/*!
281 * \internal
282 * \brief Destroy the notify CLI data releasing any resources.
283 */
284static void notify_cli_data_destroy(void *obj)
285{
286 struct notify_data *data = obj;
287
288 ao2_cleanup(data->endpoint);
289 ao2_cleanup(data->info);
290}
291
292/*!
293 * \internal
294 * \brief Structure to hold task data for notifications (URI variant)
295 */
297 char *uri;
298 void *info;
299 void (*build_notify)(pjsip_tx_data *, void *);
300};
301
302/*!
303 * \internal
304 * \brief Structure to hold task data for notifications (channel variant)
305 */
308 void *info;
309 void (*build_notify)(pjsip_tx_data *, void *);
310};
311
312static void notify_cli_uri_data_destroy(void *obj)
313{
314 struct notify_uri_data *data = obj;
315
316 ast_free(data->uri);
317 ao2_cleanup(data->info);
318}
319
320/*!
321 * \internal
322 * \brief Destroy the notify CLI data releasing any resources (URI variant)
323 */
324static void build_cli_notify(pjsip_tx_data *tdata, void *info);
325
326/*!
327 * \internal
328 * \brief Construct a notify data object for CLI.
329 */
331 struct ast_sip_endpoint *endpoint, void *info)
332{
333 struct notify_data *data = ao2_alloc(sizeof(*data),
335 if (!data) {
336 return NULL;
337 }
338
339 data->endpoint = endpoint;
340 ao2_ref(data->endpoint, +1);
341
342 data->info = info;
343 ao2_ref(data->info, +1);
344
346
347 return data;
348}
349
350/*!
351 * \internal
352 * \brief Construct a notify URI data object for CLI.
353 */
355 const char *uri, void *info)
356{
357 struct notify_uri_data *data = ao2_alloc(sizeof(*data),
359
360 if (!data) {
361 return NULL;
362 }
363
364 data->uri = ast_strdup(uri);
365 if (!data->uri) {
366 ao2_ref(data, -1);
367 return NULL;
368 }
369
370 data->info = info;
371 ao2_ref(data->info, +1);
372
374
375 return data;
376}
377
378/*!
379 * \internal
380 * \brief Destroy the notify AMI data releasing any resources.
381 */
382static void notify_ami_data_destroy(void *obj)
383{
384 struct notify_data *data = obj;
385 struct ast_variable *info = data->info;
386
387 ao2_cleanup(data->endpoint);
389}
390
391/*!
392 * \internal
393 * \brief Destroy the notify AMI URI data releasing any resources.
394 */
395static void notify_ami_uri_data_destroy(void *obj)
396{
397 struct notify_uri_data *data = obj;
398 struct ast_variable *info = data->info;
399
400 ast_free(data->uri);
402}
403
404/*!
405 * \internal
406 * \brief Destroy the notify AMI channel data releasing any resources.
407 */
409{
410 struct notify_channel_data *data = obj;
411 struct ast_variable *info = data->info;
412
413 ao2_cleanup(data->session);
415}
416
417static void build_ami_notify(pjsip_tx_data *tdata, void *info);
418
419/*!
420 * \internal
421 * \brief Construct a notify data object for AMI.
422 */
424 struct ast_sip_endpoint *endpoint, void *info)
425{
426 struct notify_data *data = ao2_alloc(sizeof(*data),
428 if (!data) {
429 return NULL;
430 }
431
432 data->endpoint = endpoint;
433 ao2_ref(data->endpoint, +1);
434
435 data->info = info;
437
438 return data;
439}
440
441/*!
442 * \internal
443 * \brief Construct a notify URI data object for AMI.
444 */
446 const char *uri, void *info)
447{
448 struct notify_uri_data *data = ao2_alloc(sizeof(*data),
450 if (!data) {
451 return NULL;
452 }
453
454 data->uri = ast_strdup(uri);
455 if (!data->uri) {
456 ao2_ref(data, -1);
457 return NULL;
458 }
459
460 data->info = info;
462
463 return data;
464}
465
466/*!
467 * \internal
468 * \brief Construct a notify channel data object for AMI.
469 */
471 struct ast_sip_session *session, void *info)
472{
473 struct notify_channel_data *data;
474
477 if (!data) {
478 return NULL;
479 }
480
481 data->session = session;
482 data->info = info;
484
485 return data;
486}
487
488/*!
489 * \internal
490 * \brief Checks if the given header name is not allowed.
491 *
492 * \details Some headers are not allowed to be set by the user within the
493 * scope of a NOTIFY request. If the given var header name is
494 * found in the "not allowed" list then return true.
495 */
496static int not_allowed(const char *name)
497{
498 int i;
499 static const char *names[] = {
500 "Call-ID",
501 "Contact",
502 "CSeq",
503 "To",
504 "From",
505 "Record-Route",
506 "Route",
507 "Request-URI",
508 "Via",
509 };
510
511 for (i = 0; i < ARRAY_LEN(names); ++i) {
512 if (!strcasecmp(name, names[i])) {
513 return 1;
514 }
515 }
516 return 0;
517}
518
519/*!
520 * \internal
521 * \brief Check if the given header can be added to a message more than once.
522 */
523static int multiple_headers_allowed(const char *name)
524{
525 /* This can be extended to include additional headers */
526 return strcasecmp("Event", name);
527}
528
529/*!
530 * \internal
531 * \brief If a content type was specified add it and the content body to the
532 * NOTIFY request.
533 */
534static void build_notify_body(pjsip_tx_data *tdata, struct ast_str *content_type,
535 struct ast_str *content)
536{
537 if (content_type) {
538 char *p;
539 struct ast_sip_body body;
540
541 if (content) {
542 body.body_text = ast_str_buffer(content);
543 }
544
545 body.type = ast_str_buffer(content_type);
546 if ((p = strchr(body.type, '/'))) {
547 *p++ = '\0';
548 body.subtype = p;
549 }
550 ast_sip_add_body(tdata, &body);
551 }
552}
553
554/*!
555 * \internal
556 * \brief Build the NOTIFY request adding content or header info.
557 */
558static void build_notify(pjsip_tx_data *tdata, const char *name, const char *value,
559 struct ast_str **content_type, struct ast_str **content)
560{
561 if (not_allowed(name)) {
562 ast_log(LOG_WARNING, "Cannot specify %s header, "
563 "ignoring\n", name);
564 return;
565 }
566
567 if (!strcasecmp(name, "Content-type")) {
568 if (!(*content_type)) {
569 *content_type = ast_str_create(CONTENT_TYPE_SIZE);
570 }
571 ast_str_set(content_type, 0,"%s", value);
572 } else if (!strcasecmp(name, "Content")) {
573 if (!(*content)) {
574 *content = ast_str_create(CONTENT_SIZE);
575 }
576
577 if (ast_str_strlen(*content)) {
578 ast_str_append(content, 0, "\r\n");
579 }
580 ast_str_append(content, 0, "%s", value);
581 } else {
582 /* See if there is an existing one */
584 pj_str_t hdr_name;
585 pj_cstr(&hdr_name, name);
586
587 if (pjsip_msg_find_hdr_by_name(tdata->msg, &hdr_name, NULL)) {
588 ast_log(LOG_ERROR, "Only one '%s' header can be added to a NOTIFY, "
589 "ignoring \"%s: %s\"\n", name, name, value);
590 return;
591 }
592 }
593
595 }
596}
597
598/*!
599 * \internal
600 * \brief Build the NOTIFY request from CLI info adding header and content
601 * when specified.
602 */
603static void build_cli_notify(pjsip_tx_data *tdata, void *info)
604{
605 struct notify_option *option = info;
606 RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
607 RAII_VAR(struct ast_str *, content, NULL, ast_free);
608
609 struct notify_option_item *item;
610 struct ao2_iterator i = ao2_iterator_init(option->items, 0);
611
612 while ((item = ao2_iterator_next(&i))) {
613 build_notify(tdata, item->name, item->value,
614 &content_type, &content);
616 }
618
619 build_notify_body(tdata, content_type, content);
620}
621
622/*!
623 * \internal
624 * \brief Build the NOTIFY request from AMI info adding header and content
625 * when specified.
626 */
627static void build_ami_notify(pjsip_tx_data *tdata, void *info)
628{
629 struct ast_variable *vars = info;
630 RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
631 RAII_VAR(struct ast_str *, content, NULL, ast_free);
632 struct ast_variable *i;
633
634 for (i = vars; i; i = i->next) {
635 if (!strcasecmp(i->name, "Content-Length")) {
636 ast_log(LOG_NOTICE, "It is not necessary to specify Content-Length, ignoring.\n");
637 continue;
638 }
639 build_notify(tdata, i->name, i->value,
640 &content_type, &content);
641 }
642
643 build_notify_body(tdata, content_type, content);
644}
645
646/*!
647 * \internal
648 * \brief Build and send a NOTIFY request to a contact.
649 */
650static int notify_contact(void *obj, void *arg, int flags)
651{
652 struct ast_sip_contact *contact = obj;
653 struct notify_data *data = arg;
654 pjsip_tx_data *tdata;
655
656 if (ast_sip_create_request("NOTIFY", NULL, data->endpoint,
657 NULL, contact, &tdata)) {
658 ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
659 "contact %s\n", contact->uri);
660 return -1;
661 }
662
663 ast_sip_add_header(tdata, "Subscription-State", "terminated");
664 data->build_notify(tdata, data->info);
665
666 if (ast_sip_send_request(tdata, NULL, data->endpoint, NULL, NULL)) {
667 ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
668 "contact %s\n", contact->uri);
669 return -1;
670 }
671
672 return 0;
673}
674
675/*!
676 * \internal
677 * \brief Send a NOTIFY request to the endpoint.
678 *
679 * \details Iterates over an endpoint's AORs sending a NOTIFY request
680 * with the appropriate payload information to each contact.
681 */
682static int notify_endpoint(void *obj)
683{
684 RAII_VAR(struct notify_data *, data, obj, ao2_cleanup);
685 char *aor_name, *aors;
686
687 if (ast_strlen_zero(data->endpoint->aors)) {
688 ast_log(LOG_WARNING, "Unable to NOTIFY - "
689 "endpoint has no configured AORs\n");
690 return -1;
691 }
692
693 aors = ast_strdupa(data->endpoint->aors);
694
695 while ((aor_name = ast_strip(strsep(&aors, ",")))) {
696 RAII_VAR(struct ast_sip_aor *, aor,
698 RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
699
700 if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
701 continue;
702 }
703
704 ao2_callback(contacts, OBJ_NODATA, notify_contact, data);
705 }
706
707 return 0;
708}
709
710/*!
711 * \internal
712 * \brief Send a notify request to the URI.
713 */
714static int notify_uri(void *obj)
715{
716 RAII_VAR(struct notify_uri_data *, data, obj, ao2_cleanup);
719 pjsip_tx_data *tdata;
720
721 if (!endpoint) {
722 ast_log(LOG_WARNING, "No default outbound endpoint set, can not send "
723 "NOTIFY requests to arbitrary URIs.\n");
724 return -1;
725 }
726
727 if (ast_strlen_zero(data->uri)) {
728 ast_log(LOG_WARNING, "Unable to NOTIFY - URI is blank.\n");
729 return -1;
730 }
731
732 if (ast_sip_create_request("NOTIFY", NULL, endpoint,
733 data->uri, NULL, &tdata)) {
734 ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
735 "uri %s\n", data->uri);
736 return -1;
737 }
738
739 ast_sip_add_header(tdata, "Subscription-State", "terminated");
740
741 data->build_notify(tdata, data->info);
742
743 if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) {
744 ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
745 "uri %s\n", data->uri);
746 return -1;
747 }
748
749 return 0;
750}
751
752/*!
753 * \internal
754 * \brief Send a notify request to a channel.
755 */
756static int notify_channel(void *obj)
757{
758 RAII_VAR(struct notify_channel_data *, data, obj, ao2_cleanup);
759 pjsip_tx_data *tdata;
760 struct pjsip_dialog *dlg;
761
762 if (!data->session->channel
763 || !data->session->inv_session
764 || data->session->inv_session->state < PJSIP_INV_STATE_EARLY
765 || data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
766 return -1;
767 }
768
769 ast_debug(1, "Sending notify on channel %s\n", ast_channel_name(data->session->channel));
770
771 dlg = data->session->inv_session->dlg;
772
773 if (ast_sip_create_request("NOTIFY", dlg, NULL, NULL, NULL, &tdata)) {
774 return -1;
775 }
776
777 ast_sip_add_header(tdata, "Subscription-State", "terminated");
778 data->build_notify(tdata, data->info);
779
780 if (ast_sip_send_request(tdata, dlg, NULL, NULL, NULL)) {
781 return -1;
782 }
783
784 return 0;
785}
786
794
795typedef struct notify_data *(*task_data_create)(
796 struct ast_sip_endpoint *, void *info);
797
798typedef struct notify_uri_data *(*task_uri_data_create)(
799 const char *uri, void *info);
800
801typedef struct notify_channel_data *(*task_channel_data_create)(
802 struct ast_sip_session *session, void *info);
803
804/*!
805 * \internal
806 * \brief Send a NOTIFY request to the endpoint within a threaded task.
807 */
808static enum notify_result push_notify(const char *endpoint_name, void *info,
809 task_data_create data_create)
810{
812 struct notify_data *data;
813
815 ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
816 return INVALID_ENDPOINT;
817 }
818
819 if (!(data = data_create(endpoint, info))) {
820 return ALLOC_ERROR;
821 }
822
824 ao2_cleanup(data);
825 return TASK_PUSH_ERROR;
826 }
827
828 return SUCCESS;
829}
830
831/*!
832 * \internal
833 * \brief Send a NOTIFY request to the URI within an threaded task.
834 */
835static enum notify_result push_notify_uri(const char *uri, void *info,
836 task_uri_data_create data_create)
837{
838 struct notify_uri_data *data;
839
840 if (!(data = data_create(uri, info))) {
841 return ALLOC_ERROR;
842 }
843
844 if (ast_sip_push_task(NULL, notify_uri, data)) {
845 ao2_cleanup(data);
846 return TASK_PUSH_ERROR;
847 }
848
849 return SUCCESS;
850}
851
852/*!
853 * \internal
854 * \brief Send a NOTIFY request in a channel within an threaded task.
855 */
856static enum notify_result push_notify_channel(const char *channel_name, void *info,
857 task_channel_data_create data_create)
858{
859 struct notify_channel_data *data;
860 struct ast_channel *ch;
861 struct ast_sip_session *session;
862 struct ast_sip_channel_pvt *ch_pvt;
863
864 /* note: this increases the refcount of the channel */
865 ch = ast_channel_get_by_name(channel_name);
866 if (!ch) {
867 ast_debug(1, "No channel found with name %s", channel_name);
868 return INVALID_CHANNEL;
869 }
870
871 if (strcmp(ast_channel_tech(ch)->type, "PJSIP")) {
872 ast_log(LOG_WARNING, "Channel was a non-PJSIP channel: %s\n", channel_name);
874 return INVALID_CHANNEL;
875 }
876
878 ch_pvt = ast_channel_tech_pvt(ch);
879 session = ch_pvt->session;
880
881 if (!session || !session->inv_session
882 || session->inv_session->state < PJSIP_INV_STATE_EARLY
883 || session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
884 ast_debug(1, "No active session for channel %s\n", channel_name);
887 return INVALID_CHANNEL;
888 }
889
890 ao2_ref(session, +1);
892
893 /* don't keep a reference to the channel, we've got a reference to the session */
895
896 /*
897 * data_create will take ownership of the session,
898 * and take care of releasing the ref.
899 */
900 data = data_create(session, info);
901 if (!data) {
902 ao2_ref(session, -1);
903 return ALLOC_ERROR;
904 }
905
906 if (ast_sip_push_task(session->serializer, notify_channel, data)) {
907 ao2_ref(data, -1);
908 return TASK_PUSH_ERROR;
909 }
910
911 return SUCCESS;
912}
913
914/*!
915 * \internal
916 * \brief Do completion on the endpoint.
917 */
918static char *cli_complete_endpoint(const char *word)
919{
920 int wordlen = strlen(word);
921 struct ao2_container * endpoints;
922 struct ast_sip_endpoint *endpoint;
923 struct ao2_iterator i;
924
926 "endpoint", word, wordlen);
927 if (endpoints == NULL) {
928 return NULL;
929 }
930
932 while ((endpoint = ao2_iterator_next(&i))) {
935 ao2_cleanup(endpoint);
936 }
938
939 ao2_ref(endpoints, -1);
940
941 return NULL;
942}
943
944/*!
945 * \internal
946 * \brief Do completion on the notify CLI command.
947 */
948static char *cli_complete_notify(const char *line, const char *word,
949 int pos, int state, int using_uri)
950{
951 char *c = NULL;
952
953 if (pos == 3) {
954 int which = 0;
955 int wordlen = strlen(word);
956
957 RAII_VAR(struct notify_cfg *, cfg,
959 struct notify_option *option;
960
961 /* do completion for notify type */
962 struct ao2_iterator i = ao2_iterator_init(cfg->notify_options, 0);
963 while ((option = ao2_iterator_next(&i))) {
964 if (!strncasecmp(word, option->name, wordlen) && ++which > state) {
965 c = ast_strdup(option->name);
966 }
967
968 ao2_cleanup(option);
969 if (c) {
970 break;
971 }
972 }
974 return c;
975 }
976
977 if (pos == 4) {
978 int wordlen = strlen(word);
979
980 if (ast_strlen_zero(word)) {
981 if (state == 0) {
982 c = ast_strdup("endpoint");
983 } else if (state == 1) {
984 c = ast_strdup("uri");
985 }
986 } else if (state == 0) {
987 if (!strncasecmp(word, "endpoint", wordlen)) {
988 c = ast_strdup("endpoint");
989 } else if (!strncasecmp(word, "uri", wordlen)) {
990 c = ast_strdup("uri");
991 }
992 }
993
994 return c;
995 }
996
997 return pos > 4 && !using_uri ? cli_complete_endpoint(word) : NULL;
998}
999
1000/*!
1001 * \internal
1002 * \brief CLI command to send a SIP notify to an endpoint.
1003 *
1004 * \details Attempts to match the "type" given in the CLI command to a
1005 * configured one. If found, sends a NOTIFY to the endpoint
1006 * with the associated payload.
1007 */
1008static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1009{
1010 RAII_VAR(struct notify_cfg *, cfg, NULL, ao2_cleanup);
1011 RAII_VAR(struct notify_option *, option, NULL, ao2_cleanup);
1012
1013 int i;
1014 int using_uri = 0;
1015
1016 switch (cmd) {
1017 case CLI_INIT:
1018 e->command = "pjsip send notify";
1019 e->usage =
1020 "Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n"
1021 " Send a NOTIFY request to an endpoint\n"
1022 " Message types are defined in pjsip_notify.conf\n";
1023 return NULL;
1024 case CLI_GENERATE:
1025 if (a->argc > 4 && (!strcasecmp(a->argv[4], "uri"))) {
1026 using_uri = 1;
1027 }
1028
1029 return cli_complete_notify(a->line, a->word, a->pos, a->n, using_uri);
1030 }
1031
1032 if (a->argc < 6) {
1033 return CLI_SHOWUSAGE;
1034 }
1035
1036 if (!strcasecmp(a->argv[4], "uri")) {
1037 using_uri = 1;
1038 } else if (strcasecmp(a->argv[4], "endpoint")) {
1039 return CLI_SHOWUSAGE;
1040 }
1041
1043
1044 if (!(option = notify_option_find(cfg->notify_options, a->argv[3])))
1045 {
1046 ast_cli(a->fd, "Unable to find notify type '%s'\n",
1047 a->argv[3]);
1048 return CLI_FAILURE;
1049 }
1050
1051 for (i = 5; i < a->argc; ++i) {
1052 ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n",
1053 a->argv[3], a->argv[i]);
1054
1055 switch (using_uri ? push_notify_uri(a->argv[i], option, notify_cli_uri_data_create) :
1056 push_notify(a->argv[i], option, notify_cli_data_create)) {
1057 case INVALID_ENDPOINT:
1058 ast_cli(a->fd, "Unable to retrieve endpoint %s\n",
1059 a->argv[i]);
1060 break;
1061 case ALLOC_ERROR:
1062 ast_cli(a->fd, "Unable to allocate NOTIFY task data\n");
1063 return CLI_FAILURE;
1064 case TASK_PUSH_ERROR:
1065 ast_cli(a->fd, "Unable to push NOTIFY task\n");
1066 return CLI_FAILURE;
1067 default:
1068 break;
1069 }
1070 }
1071
1072 return CLI_SUCCESS;
1073}
1074
1075static struct ast_cli_entry cli_options[] = {
1076 AST_CLI_DEFINE(cli_notify, "Send a NOTIFY request to a SIP endpoint")
1077};
1078
1083};
1084
1085static void manager_send_response(struct mansession *s, const struct message *m, enum notify_type type, enum notify_result res, struct ast_variable *vars, const char *endpoint_name)
1086{
1087 switch (res) {
1088 case INVALID_CHANNEL:
1089 if (type == NOTIFY_CHANNEL) {
1091 astman_send_error(s, m, "Channel not found");
1092 } else {
1093 /* Shouldn't be possible. */
1094 ast_assert(0);
1095 }
1096 break;
1097 case INVALID_ENDPOINT:
1098 if (type == NOTIFY_ENDPOINT) {
1100 astman_send_error_va(s, m, "Unable to retrieve endpoint %s", endpoint_name);
1101 } else {
1102 /* Shouldn't be possible. */
1103 ast_assert(0);
1104 }
1105 break;
1106 case ALLOC_ERROR:
1108 astman_send_error(s, m, "Unable to allocate NOTIFY task data");
1109 break;
1110 case TASK_PUSH_ERROR:
1111 /* Don't need to destroy vars since it is handled by cleanup in push_notify, push_notify_uri, etc. */
1112 astman_send_error(s, m, "Unable to push Notify task");
1113 break;
1114 case SUCCESS:
1115 astman_send_ack(s, m, "NOTIFY sent");
1116 break;
1117 }
1118}
1119
1120/*!
1121 * \internal
1122 * \brief Completes SIPNotify AMI command in Endpoint mode.
1123 */
1125 const struct message *m, const char *endpoint_name)
1126{
1127 RAII_VAR(struct notify_cfg *, cfg, NULL, ao2_cleanup);
1128 RAII_VAR(struct notify_option *, option, NULL, ao2_cleanup);
1129 struct ast_variable *vars = NULL;
1130 enum notify_result res;
1131 const char *option_name = astman_get_header(m, "Option");
1132
1133 if (!ast_strlen_zero(option_name) && (cfg = ao2_global_obj_ref(globals)) && !(option = notify_option_find(cfg->notify_options, option_name))) {
1134 astman_send_error_va(s, m, "Unable to find notify type '%s'\n", option_name);
1135 return;
1136 }
1137 if (!option) {
1139 }
1140
1141 if (!strncasecmp(endpoint_name, "sip/", 4)) {
1142 endpoint_name += 4;
1143 }
1144
1145 if (!strncasecmp(endpoint_name, "pjsip/", 6)) {
1146 endpoint_name += 6;
1147 }
1148
1149 if (option) {
1150 res = push_notify(endpoint_name, option, notify_cli_data_create); /* The CLI version happens to be suitable for options. */
1151 } else {
1152 res = push_notify(endpoint_name, vars, notify_ami_data_create);
1153 }
1154
1155 manager_send_response(s, m, NOTIFY_ENDPOINT, res, vars, endpoint_name);
1156}
1157
1158/*!
1159 * \internal
1160 * \brief Completes SIPNotify AMI command in URI mode.
1161 */
1162static void manager_notify_uri(struct mansession *s,
1163 const struct message *m, const char *uri)
1164{
1165 RAII_VAR(struct notify_cfg *, cfg, NULL, ao2_cleanup);
1166 RAII_VAR(struct notify_option *, option, NULL, ao2_cleanup);
1167 enum notify_result res;
1168 const char *option_name = astman_get_header(m, "Option");
1169 struct ast_variable *vars = NULL;
1170
1171 if (!ast_strlen_zero(option_name) && (cfg = ao2_global_obj_ref(globals)) && !(option = notify_option_find(cfg->notify_options, option_name))) {
1172 astman_send_error_va(s, m, "Unable to find notify type '%s'\n", option_name);
1173 return;
1174 }
1175 if (!option) {
1177 }
1178
1179 if (option) {
1180 res = push_notify_uri(uri, option, notify_cli_uri_data_create);
1181 } else {
1183 }
1184
1185 manager_send_response(s, m, NOTIFY_URI, res, vars, NULL);
1186}
1187
1188/*!
1189 * \internal
1190 * \brief Completes SIPNotify AMI command in channel mode.
1191 */
1193 const struct message *m, const char *channel)
1194{
1195 enum notify_result res;
1196 struct ast_variable *vars = NULL;
1197
1200
1201 manager_send_response(s, m, NOTIFY_CHANNEL, res, vars, NULL);
1202}
1203
1204/*!
1205 * \internal
1206 * \brief AMI entry point to send a SIP notify to an endpoint.
1207 */
1208static int manager_notify(struct mansession *s, const struct message *m)
1209{
1210 const char *endpoint_name = astman_get_header(m, "Endpoint");
1211 const char *uri = astman_get_header(m, "URI");
1212 const char *channel = astman_get_header(m, "Channel");
1213 const char *variables = astman_get_header(m, "Variable");
1214 const char *option = astman_get_header(m, "Option");
1215 int count = 0;
1216
1217 if (!ast_strlen_zero(endpoint_name)) {
1218 ++count;
1219 }
1220 if (!ast_strlen_zero(uri)) {
1221 ++count;
1222 }
1223 if (!ast_strlen_zero(channel)) {
1224 ++count;
1225 }
1226
1227 if ((!ast_strlen_zero(option) && !ast_strlen_zero(variables)) || (ast_strlen_zero(option) && ast_strlen_zero(variables))) {
1228 astman_send_error(s, m,
1229 "PJSIPNotify requires either an Option or Variable(s)."
1230 "You must use only one of them.");
1231 } else if (1 < count) {
1232 astman_send_error(s, m,
1233 "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel. "
1234 "You must use only one of them.");
1235 } else if (!ast_strlen_zero(endpoint_name)) {
1236 manager_notify_endpoint(s, m, endpoint_name);
1237 } else if (!ast_strlen_zero(uri)) {
1238 manager_notify_uri(s, m, uri);
1239 } else if (!ast_strlen_zero(channel)) {
1240 manager_notify_channel(s, m, channel);
1241 } else {
1242 astman_send_error(s, m,
1243 "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel.");
1244 }
1245
1246 return 0;
1247}
1248
1249static int load_module(void)
1250{
1251 if (aco_info_init(&notify_cfg)) {
1253 }
1254
1256 "", notify_option_handler, 0);
1257
1258 if (aco_process_config(&notify_cfg, 0)) {
1261 }
1262
1265
1267}
1268
1269static int reload_module(void)
1270{
1273 }
1274
1275 return 0;
1276}
1277
1278static int unload_module(void)
1279{
1280 ast_manager_unregister("PJSIPNotify");
1284
1285 return 0;
1286}
1287
1288AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP NOTIFY Support",
1289 .support_level = AST_MODULE_SUPPORT_CORE,
1290 .load = load_module,
1292 .unload = unload_module,
1293 .load_pri = AST_MODPRI_APP_DEPEND,
1294 .requires = "res_pjsip",
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
static struct ast_mansession session
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
#define OBJ_KEY
Definition: astobj2.h:1151
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:404
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition: astobj2.h:1044
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
@ AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW
Allow objects with duplicate keys in container.
Definition: astobj2.h:1181
static struct console_pvt globals
static const char type[]
Definition: chan_ooh323.c:109
const char * ast_channel_name(const struct ast_channel *chan)
void * ast_channel_tech_pvt(const struct ast_channel *chan)
#define ast_channel_lock(chan)
Definition: channel.h:2922
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
const struct ast_channel_tech * ast_channel_tech(const struct ast_channel *chan)
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
#define ast_channel_unlock(chan)
Definition: channel.h:2923
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2758
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
short word
@ ACO_PREFIX
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
@ ACO_PROCESS_ERROR
Their was an error and no changes were applied.
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
#define ACO_FILES(...)
#define aco_option_register_custom(info, name, matchtype, types, default_val, handler, flags)
Register a config option.
@ ACO_ITEM
@ ACO_BLACKLIST_EXACT
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
static const char name[]
Definition: format_mp3.c:68
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3354
void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt,...)
Send error in manager transaction (with va_args support)
Definition: manager.c:3359
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3386
struct ast_variable * astman_get_variables_order(const struct message *m, enum variable_orders order)
Get a linked list of the Variable: headers with order specified.
Definition: manager.c:3106
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3015
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8014
int ast_sip_push_task(struct ast_taskprocessor *serializer, int(*sip_task)(void *), void *task_data)
Pushes a task to SIP servants.
Definition: res_pjsip.c:2099
char * strsep(char **str, const char *delims)
Configuration File Parser.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
static struct ao2_container * endpoints
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define EVENT_FLAG_SYSTEM
Definition: manager.h:75
@ ORDER_NATURAL
Definition: manager.h:288
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_APP_DEPEND
Definition: module.h:328
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
def info(msg)
Core PBX routines and definitions.
static int reload(void)
struct ao2_container * container
Definition: res_fax.c:501
struct ast_sip_aor * ast_sip_location_retrieve_aor(const char *aor_name)
Retrieve a named AOR.
Definition: location.c:147
int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint, void *token, void(*callback)(void *token, pjsip_event *e))
General purpose method for sending a SIP request.
Definition: res_pjsip.c:1979
struct ast_sip_endpoint * ast_sip_default_outbound_endpoint(void)
Retrieve the default outbound endpoint.
int ast_sip_add_body(pjsip_tx_data *tdata, const struct ast_sip_body *body)
Add a body to an outbound SIP message.
Definition: res_pjsip.c:2052
int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint, const char *uri, struct ast_sip_contact *contact, pjsip_tx_data **tdata)
General purpose method for creating a SIP request.
Definition: res_pjsip.c:1435
int ast_sip_add_header(pjsip_tx_data *tdata, const char *name, const char *value)
Add a header to an outbound SIP message.
Definition: res_pjsip.c:2008
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
struct ao2_container * ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor)
Retrieve all contacts currently available for an AOR.
Definition: location.c:247
AO2_GLOBAL_OBJ_STATIC(globals)
static void build_notify_body(pjsip_tx_data *tdata, struct ast_str *content_type, struct ast_str *content)
static void * notify_option_find(struct ao2_container *container, const char *category)
static void notify_ami_channel_data_destroy(void *obj)
static void * notify_cfg_alloc(void)
static char * cli_complete_notify(const char *line, const char *word, int pos, int state, int using_uri)
static int notify_option_cmp(void *obj, void *arg, int flags)
static void * notify_option_alloc(const char *category)
static void notify_ami_data_destroy(void *obj)
static int not_allowed(const char *name)
static enum notify_result push_notify_channel(const char *channel_name, void *info, task_channel_data_create data_create)
struct notify_channel_data *(* task_channel_data_create)(struct ast_sip_session *session, void *info)
static struct aco_type * notify_options[]
static void manager_notify_endpoint(struct mansession *s, const struct message *m, const char *endpoint_name)
CONFIG_INFO_STANDARD(notify_cfg, globals, notify_cfg_alloc,.files=ACO_FILES(&module_conf))
static void build_cli_notify(pjsip_tx_data *tdata, void *info)
static struct notify_data * notify_cli_data_create(struct ast_sip_endpoint *endpoint, void *info)
static struct aco_file module_conf
static struct notify_uri_data * notify_ami_uri_data_create(const char *uri, void *info)
struct notify_uri_data *(* task_uri_data_create)(const char *uri, void *info)
static char * cli_complete_endpoint(const char *word)
static char * cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void manager_notify_channel(struct mansession *s, const struct message *m, const char *channel)
static struct notify_data * notify_ami_data_create(struct ast_sip_endpoint *endpoint, void *info)
static int notify_contact(void *obj, void *arg, int flags)
static void build_notify(pjsip_tx_data *tdata, const char *name, const char *value, struct ast_str **content_type, struct ast_str **content)
static enum notify_result push_notify_uri(const char *uri, void *info, task_uri_data_create data_create)
static int reload_module(void)
static void build_ami_notify(pjsip_tx_data *tdata, void *info)
static int notify_uri(void *obj)
static int notify_channel(void *obj)
static int multiple_headers_allowed(const char *name)
static int manager_notify(struct mansession *s, const struct message *m)
notify_result
@ TASK_PUSH_ERROR
@ ALLOC_ERROR
@ SUCCESS
@ INVALID_ENDPOINT
@ INVALID_CHANNEL
static struct ast_cli_entry cli_options[]
static int notify_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
static int notify_endpoint(void *obj)
static void notify_cli_uri_data_destroy(void *obj)
static void notify_cfg_destroy(void *obj)
static struct notify_channel_data * notify_ami_channel_data_create(struct ast_sip_session *session, void *info)
static int load_module(void)
static void notify_ami_uri_data_destroy(void *obj)
static int unload_module(void)
static struct notify_uri_data * notify_cli_uri_data_create(const char *uri, void *info)
static void manager_send_response(struct mansession *s, const struct message *m, enum notify_type type, enum notify_result res, struct ast_variable *vars, const char *endpoint_name)
static int notify_option_hash(const void *obj, int flags)
static enum notify_result push_notify(const char *endpoint_name, void *info, task_data_create data_create)
#define CONTENT_TYPE_SIZE
static void notify_cli_data_destroy(void *obj)
static void notify_option_destroy(void *obj)
struct notify_data *(* task_data_create)(struct ast_sip_endpoint *, void *info)
static const char notify_config[]
notify_type
@ NOTIFY_CHANNEL
@ NOTIFY_ENDPOINT
@ NOTIFY_URI
static void manager_notify_uri(struct mansession *s, const struct message *m, const char *uri)
#define CONTENT_SIZE
#define NULL
Definition: resample.c:96
Sorcery Data Access Layer API.
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
struct ao2_container * ast_sorcery_retrieve_by_prefix(const struct ast_sorcery *sorcery, const char *type, const char *prefix, const size_t prefix_len)
Retrieve multiple objects whose id begins with the specified prefix.
Definition: sorcery.c:1984
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:1303
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
The representation of a single configuration file to be processed.
const char * filename
Type information about a category-level configurable object.
const char * name
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
A SIP address of record.
Definition: res_pjsip.h:470
SIP body description.
Definition: res_pjsip.h:2313
const char * type
Definition: res_pjsip.h:2315
const char * body_text
Definition: res_pjsip.h:2319
const char * subtype
Definition: res_pjsip.h:2317
A structure which contains a channel implementation and session.
struct ast_sip_session * session
Pointer to session.
Contact associated with an address of record.
Definition: res_pjsip.h:384
const ast_string_field uri
Definition: res_pjsip.h:406
An entity with which Asterisk communicates.
Definition: res_pjsip.h:951
A structure describing a SIP session.
struct ast_sip_endpoint * endpoint
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:1750
struct ao2_container * notify_options
struct ast_sip_session * session
void(* build_notify)(pjsip_tx_data *, void *)
struct ast_sip_endpoint * endpoint
void(* build_notify)(pjsip_tx_data *, void *)
struct ao2_container * items
void(* build_notify)(pjsip_tx_data *, void *)
int value
Definition: syslog.c:37
static struct aco_type item
Definition: test_config.c:1463
static struct test_val a
static struct test_val c
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
#define ARRAY_LEN(a)
Definition: utils.h:666