Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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="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 
125  const char *name;
126  const char *value;
127  char buf[0];
128 };
129 
131  /*! Contains header and/or content information */
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(
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 {
213 };
214 
215 static void notify_cfg_destroy(void *obj)
216 {
217  struct notify_cfg *cfg = obj;
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 
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 = {
253  .types = ACO_TYPES(&notify_option),
254 };
255 
257 
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 */
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  */
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  */
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  */
326  struct ast_sip_endpoint *endpoint, void *info)
327 {
328  struct notify_data *data = ao2_alloc(sizeof(*data),
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 
341 
342  return data;
343 }
344 
345 /*!
346  * \internal
347  * \brief Construct a notify URI data object for CLI.
348  */
350  const char *uri, void *info)
351 {
352  struct notify_uri_data *data = ao2_alloc(sizeof(*data),
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 
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  */
419  struct ast_sip_endpoint *endpoint, void *info)
420 {
421  struct notify_data *data = ao2_alloc(sizeof(*data),
423  if (!data) {
424  return NULL;
425  }
426 
427  data->endpoint = endpoint;
428  ao2_ref(data->endpoint, +1);
429 
430  data->info = info;
432 
433  return data;
434 }
435 
436 /*!
437  * \internal
438  * \brief Construct a notify URI data object for AMI.
439  */
441  const char *uri, void *info)
442 {
443  struct notify_uri_data *data = ao2_alloc(sizeof(*data),
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;
457 
458  return data;
459 }
460 
461 /*!
462  * \internal
463  * \brief Construct a notify channel data object for AMI.
464  */
466  struct ast_sip_session *session, void *info)
467 {
468  struct notify_channel_data *data;
469 
472  if (!data) {
473  return NULL;
474  }
475 
476  data->session = session;
477  data->info = info;
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 Check if the given header can be added to a message more than once.
517  */
518 static int multiple_headers_allowed(const char *name)
519 {
520  /* This can be extended to include additional headers */
521  return strcasecmp("Event", name);
522 }
523 
524 /*!
525  * \internal
526  * \brief If a content type was specified add it and the content body to the
527  * NOTIFY request.
528  */
529 static void build_notify_body(pjsip_tx_data *tdata, struct ast_str *content_type,
530  struct ast_str *content)
531 {
532  if (content_type) {
533  char *p;
534  struct ast_sip_body body;
535 
536  if (content) {
537  body.body_text = ast_str_buffer(content);
538  }
539 
540  body.type = ast_str_buffer(content_type);
541  if ((p = strchr(body.type, '/'))) {
542  *p++ = '\0';
543  body.subtype = p;
544  }
545  ast_sip_add_body(tdata, &body);
546  }
547 }
548 
549 /*!
550  * \internal
551  * \brief Build the NOTIFY request adding content or header info.
552  */
553 static void build_notify(pjsip_tx_data *tdata, const char *name, const char *value,
554  struct ast_str **content_type, struct ast_str **content)
555 {
556  if (not_allowed(name)) {
557  ast_log(LOG_WARNING, "Cannot specify %s header, "
558  "ignoring\n", name);
559  return;
560  }
561 
562  if (!strcasecmp(name, "Content-type")) {
563  if (!(*content_type)) {
564  *content_type = ast_str_create(CONTENT_TYPE_SIZE);
565  }
566  ast_str_set(content_type, 0,"%s", value);
567  } else if (!strcasecmp(name, "Content")) {
568  if (!(*content)) {
569  *content = ast_str_create(CONTENT_SIZE);
570  }
571 
572  if (ast_str_strlen(*content)) {
573  ast_str_append(content, 0, "\r\n");
574  }
575  ast_str_append(content, 0, "%s", value);
576  } else {
577  /* See if there is an existing one */
578  if (!multiple_headers_allowed(name)) {
579  pj_str_t hdr_name;
580  pj_cstr(&hdr_name, name);
581 
582  if (pjsip_msg_find_hdr_by_name(tdata->msg, &hdr_name, NULL)) {
583  ast_log(LOG_ERROR, "Only one '%s' header can be added to a NOTIFY, "
584  "ignoring \"%s: %s\"\n", name, name, value);
585  return;
586  }
587  }
588 
589  ast_sip_add_header(tdata, name, value);
590  }
591 }
592 
593 /*!
594  * \internal
595  * \brief Build the NOTIFY request from CLI info adding header and content
596  * when specified.
597  */
598 static void build_cli_notify(pjsip_tx_data *tdata, void *info)
599 {
600  struct notify_option *option = info;
601  RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
602  RAII_VAR(struct ast_str *, content, NULL, ast_free);
603 
604  struct notify_option_item *item;
605  struct ao2_iterator i = ao2_iterator_init(option->items, 0);
606 
607  while ((item = ao2_iterator_next(&i))) {
608  build_notify(tdata, item->name, item->value,
609  &content_type, &content);
610  ao2_cleanup(item);
611  }
613 
614  build_notify_body(tdata, content_type, content);
615 }
616 
617 /*!
618  * \internal
619  * \brief Build the NOTIFY request from AMI info adding header and content
620  * when specified.
621  */
622 static void build_ami_notify(pjsip_tx_data *tdata, void *info)
623 {
624  struct ast_variable *vars = info;
625  RAII_VAR(struct ast_str *, content_type, NULL, ast_free);
626  RAII_VAR(struct ast_str *, content, NULL, ast_free);
627  struct ast_variable *i;
628 
629  for (i = vars; i; i = i->next) {
630  if (!strcasecmp(i->name, "Content-Length")) {
631  ast_log(LOG_NOTICE, "It is not necessary to specify Content-Length, ignoring.\n");
632  continue;
633  }
634  build_notify(tdata, i->name, i->value,
635  &content_type, &content);
636  }
637 
638  build_notify_body(tdata, content_type, content);
639 }
640 
641 /*!
642  * \internal
643  * \brief Build and send a NOTIFY request to a contact.
644  */
645 static int notify_contact(void *obj, void *arg, int flags)
646 {
647  struct ast_sip_contact *contact = obj;
648  struct notify_data *data = arg;
649  pjsip_tx_data *tdata;
650 
651  if (ast_sip_create_request("NOTIFY", NULL, data->endpoint,
652  NULL, contact, &tdata)) {
653  ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
654  "contact %s\n", contact->uri);
655  return -1;
656  }
657 
658  ast_sip_add_header(tdata, "Subscription-State", "terminated");
659  data->build_notify(tdata, data->info);
660 
661  if (ast_sip_send_request(tdata, NULL, data->endpoint, NULL, NULL)) {
662  ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
663  "contact %s\n", contact->uri);
664  return -1;
665  }
666 
667  return 0;
668 }
669 
670 /*!
671  * \internal
672  * \brief Send a NOTIFY request to the endpoint.
673  *
674  * \detail Iterates over an endpoint's AORs sending a NOTIFY request
675  * with the appropriate payload information to each contact.
676  */
677 static int notify_endpoint(void *obj)
678 {
679  RAII_VAR(struct notify_data *, data, obj, ao2_cleanup);
680  char *aor_name, *aors;
681 
682  if (ast_strlen_zero(data->endpoint->aors)) {
683  ast_log(LOG_WARNING, "Unable to NOTIFY - "
684  "endpoint has no configured AORs\n");
685  return -1;
686  }
687 
688  aors = ast_strdupa(data->endpoint->aors);
689 
690  while ((aor_name = ast_strip(strsep(&aors, ",")))) {
691  RAII_VAR(struct ast_sip_aor *, aor,
693  RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
694 
695  if (!aor || !(contacts = ast_sip_location_retrieve_aor_contacts(aor))) {
696  continue;
697  }
698 
699  ao2_callback(contacts, OBJ_NODATA, notify_contact, data);
700  }
701 
702  return 0;
703 }
704 
705 /*!
706  * \internal
707  * \brief Send a notify request to the URI.
708  */
709 static int notify_uri(void *obj)
710 {
711  RAII_VAR(struct notify_uri_data *, data, obj, ao2_cleanup);
714  pjsip_tx_data *tdata;
715 
716  if (!endpoint) {
717  ast_log(LOG_WARNING, "No default outbound endpoint set, can not send "
718  "NOTIFY requests to arbitrary URIs.\n");
719  return -1;
720  }
721 
722  if (ast_strlen_zero(data->uri)) {
723  ast_log(LOG_WARNING, "Unable to NOTIFY - URI is blank.\n");
724  return -1;
725  }
726 
727  if (ast_sip_create_request("NOTIFY", NULL, endpoint,
728  data->uri, NULL, &tdata)) {
729  ast_log(LOG_WARNING, "SIP NOTIFY - Unable to create request for "
730  "uri %s\n", data->uri);
731  return -1;
732  }
733 
734  ast_sip_add_header(tdata, "Subscription-State", "terminated");
735 
736  data->build_notify(tdata, data->info);
737 
738  if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) {
739  ast_log(LOG_ERROR, "SIP NOTIFY - Unable to send request for "
740  "uri %s\n", data->uri);
741  return -1;
742  }
743 
744  return 0;
745 }
746 
747 /*!
748  * \internal
749  * \brief Send a notify request to a channel.
750  */
751 static int notify_channel(void *obj)
752 {
753  RAII_VAR(struct notify_channel_data *, data, obj, ao2_cleanup);
754  pjsip_tx_data *tdata;
755  struct pjsip_dialog *dlg;
756 
757  if (!data->session->channel
758  || !data->session->inv_session
759  || data->session->inv_session->state < PJSIP_INV_STATE_EARLY
760  || data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
761  return -1;
762  }
763 
764  ast_debug(1, "Sending notify on channel %s\n", ast_channel_name(data->session->channel));
765 
766  dlg = data->session->inv_session->dlg;
767 
768  if (ast_sip_create_request("NOTIFY", dlg, NULL, NULL, NULL, &tdata)) {
769  return -1;
770  }
771 
772  ast_sip_add_header(tdata, "Subscription-State", "terminated");
773  data->build_notify(tdata, data->info);
774 
775  if (ast_sip_send_request(tdata, dlg, NULL, NULL, NULL)) {
776  return -1;
777  }
778 
779  return 0;
780 }
781 
788 };
789 
790 typedef struct notify_data *(*task_data_create)(
791  struct ast_sip_endpoint *, void *info);
792 
793 typedef struct notify_uri_data *(*task_uri_data_create)(
794  const char *uri, void *info);
795 
796 typedef struct notify_channel_data *(*task_channel_data_create)(
797  struct ast_sip_session *session, void *info);
798 
799 /*!
800  * \internal
801  * \brief Send a NOTIFY request to the endpoint within a threaded task.
802  */
803 static enum notify_result push_notify(const char *endpoint_name, void *info,
804  task_data_create data_create)
805 {
807  struct notify_data *data;
808 
810  ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
811  return INVALID_ENDPOINT;
812  }
813 
814  if (!(data = data_create(endpoint, info))) {
815  return ALLOC_ERROR;
816  }
817 
818  if (ast_sip_push_task(NULL, notify_endpoint, data)) {
819  ao2_cleanup(data);
820  return TASK_PUSH_ERROR;
821  }
822 
823  return SUCCESS;
824 }
825 
826 /*!
827  * \internal
828  * \brief Send a NOTIFY request to the URI within an threaded task.
829  */
830 static enum notify_result push_notify_uri(const char *uri, void *info,
831  task_uri_data_create data_create)
832 {
833  struct notify_uri_data *data;
834 
835  if (!(data = data_create(uri, info))) {
836  return ALLOC_ERROR;
837  }
838 
839  if (ast_sip_push_task(NULL, notify_uri, data)) {
840  ao2_cleanup(data);
841  return TASK_PUSH_ERROR;
842  }
843 
844  return SUCCESS;
845 }
846 
847 /*!
848  * \internal
849  * \brief Send a NOTIFY request in a channel within an threaded task.
850  */
851 static enum notify_result push_notify_channel(const char *channel_name, void *info,
852  task_channel_data_create data_create)
853 {
854  struct notify_channel_data *data;
855  struct ast_channel *ch;
856  struct ast_sip_session *session;
857  struct ast_sip_channel_pvt *ch_pvt;
858 
859  /* note: this increases the refcount of the channel */
860  ch = ast_channel_get_by_name(channel_name);
861  if (!ch) {
862  ast_debug(1, "No channel found with name %s", channel_name);
863  return INVALID_CHANNEL;
864  }
865 
866  if (strcmp(ast_channel_tech(ch)->type, "PJSIP")) {
867  ast_log(LOG_WARNING, "Channel was a non-PJSIP channel: %s\n", channel_name);
868  ast_channel_unref(ch);
869  return INVALID_CHANNEL;
870  }
871 
872  ast_channel_lock(ch);
873  ch_pvt = ast_channel_tech_pvt(ch);
874  session = ch_pvt->session;
875 
876  if (!session || !session->inv_session
877  || session->inv_session->state < PJSIP_INV_STATE_EARLY
878  || session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
879  ast_debug(1, "No active session for channel %s\n", channel_name);
880  ast_channel_unlock(ch);
881  ast_channel_unref(ch);
882  return INVALID_CHANNEL;
883  }
884 
885  ao2_ref(session, +1);
886  ast_channel_unlock(ch);
887 
888  /* don't keep a reference to the channel, we've got a reference to the session */
889  ast_channel_unref(ch);
890 
891  /*
892  * data_create will take ownership of the session,
893  * and take care of releasing the ref.
894  */
895  data = data_create(session, info);
896  if (!data) {
897  ao2_ref(session, -1);
898  return ALLOC_ERROR;
899  }
900 
901  if (ast_sip_push_task(session->serializer, notify_channel, data)) {
902  ao2_ref(data, -1);
903  return TASK_PUSH_ERROR;
904  }
905 
906  return SUCCESS;
907 }
908 
909 /*!
910  * \internal
911  * \brief Do completion on the endpoint.
912  */
913 static char *cli_complete_endpoint(const char *word)
914 {
915  int wordlen = strlen(word);
916  struct ao2_container * endpoints;
917  struct ast_sip_endpoint *endpoint;
918  struct ao2_iterator i;
919 
921  "endpoint", word, wordlen);
922  if (endpoints == NULL) {
923  return NULL;
924  }
925 
926  i = ao2_iterator_init(endpoints, 0);
927  while ((endpoint = ao2_iterator_next(&i))) {
930  ao2_cleanup(endpoint);
931  }
933 
934  ao2_ref(endpoints, -1);
935 
936  return NULL;
937 }
938 
939 /*!
940  * \internal
941  * \brief Do completion on the notify CLI command.
942  */
943 static char *cli_complete_notify(const char *line, const char *word,
944  int pos, int state, int using_uri)
945 {
946  char *c = NULL;
947 
948  if (pos == 3) {
949  int which = 0;
950  int wordlen = strlen(word);
951 
952  RAII_VAR(struct notify_cfg *, cfg,
954  struct notify_option *option;
955 
956  /* do completion for notify type */
957  struct ao2_iterator i = ao2_iterator_init(cfg->notify_options, 0);
958  while ((option = ao2_iterator_next(&i))) {
959  if (!strncasecmp(word, option->name, wordlen) && ++which > state) {
960  c = ast_strdup(option->name);
961  }
962 
963  ao2_cleanup(option);
964  if (c) {
965  break;
966  }
967  }
969  return c;
970  }
971 
972  if (pos == 4) {
973  int wordlen = strlen(word);
974 
975  if (ast_strlen_zero(word)) {
976  if (state == 0) {
977  c = ast_strdup("endpoint");
978  } else if (state == 1) {
979  c = ast_strdup("uri");
980  }
981  } else if (state == 0) {
982  if (!strncasecmp(word, "endpoint", wordlen)) {
983  c = ast_strdup("endpoint");
984  } else if (!strncasecmp(word, "uri", wordlen)) {
985  c = ast_strdup("uri");
986  }
987  }
988 
989  return c;
990  }
991 
992  return pos > 4 && !using_uri ? cli_complete_endpoint(word) : NULL;
993 }
994 
995 /*!
996  * \internal
997  * \brief CLI command to send a SIP notify to an endpoint.
998  *
999  * \details Attempts to match the "type" given in the CLI command to a
1000  * configured one. If found, sends a NOTIFY to the endpoint
1001  * with the associated payload.
1002  */
1003 static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1004 {
1005  RAII_VAR(struct notify_cfg *, cfg, NULL, ao2_cleanup);
1006  RAII_VAR(struct notify_option *, option, NULL, ao2_cleanup);
1007 
1008  int i;
1009  int using_uri = 0;
1010 
1011  switch (cmd) {
1012  case CLI_INIT:
1013  e->command = "pjsip send notify";
1014  e->usage =
1015  "Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n"
1016  " Send a NOTIFY request to an endpoint\n"
1017  " Message types are defined in pjsip_notify.conf\n";
1018  return NULL;
1019  case CLI_GENERATE:
1020  if (a->argc > 4 && (!strcasecmp(a->argv[4], "uri"))) {
1021  using_uri = 1;
1022  }
1023 
1024  return cli_complete_notify(a->line, a->word, a->pos, a->n, using_uri);
1025  }
1026 
1027  if (a->argc < 6) {
1028  return CLI_SHOWUSAGE;
1029  }
1030 
1031  if (!strcasecmp(a->argv[4], "uri")) {
1032  using_uri = 1;
1033  } else if (strcasecmp(a->argv[4], "endpoint")) {
1034  return CLI_SHOWUSAGE;
1035  }
1036 
1037  cfg = ao2_global_obj_ref(globals);
1038 
1039  if (!(option = notify_option_find(cfg->notify_options, a->argv[3])))
1040  {
1041  ast_cli(a->fd, "Unable to find notify type '%s'\n",
1042  a->argv[3]);
1043  return CLI_FAILURE;
1044  }
1045 
1046  for (i = 5; i < a->argc; ++i) {
1047  ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n",
1048  a->argv[3], a->argv[i]);
1049 
1050  switch (using_uri ? push_notify_uri(a->argv[i], option, notify_cli_uri_data_create) :
1051  push_notify(a->argv[i], option, notify_cli_data_create)) {
1052  case INVALID_ENDPOINT:
1053  ast_cli(a->fd, "Unable to retrieve endpoint %s\n",
1054  a->argv[i]);
1055  break;
1056  case ALLOC_ERROR:
1057  ast_cli(a->fd, "Unable to allocate NOTIFY task data\n");
1058  return CLI_FAILURE;
1059  case TASK_PUSH_ERROR:
1060  ast_cli(a->fd, "Unable to push NOTIFY task\n");
1061  return CLI_FAILURE;
1062  default:
1063  break;
1064  }
1065  }
1066 
1067  return CLI_SUCCESS;
1068 }
1069 
1070 static struct ast_cli_entry cli_options[] = {
1071  AST_CLI_DEFINE(cli_notify, "Send a NOTIFY request to a SIP endpoint")
1072 };
1073 
1074 /*!
1075  * \interanl
1076  * \brief Completes SIPNotify AMI command in Endpoint mode.
1077  */
1078 static void manager_notify_endpoint(struct mansession *s,
1079  const struct message *m, const char *endpoint_name)
1080 {
1082 
1083  if (!strncasecmp(endpoint_name, "sip/", 4)) {
1084  endpoint_name += 4;
1085  }
1086 
1087  if (!strncasecmp(endpoint_name, "pjsip/", 6)) {
1088  endpoint_name += 6;
1089  }
1090 
1091  switch (push_notify(endpoint_name, vars, notify_ami_data_create)) {
1092  case INVALID_CHANNEL:
1093  /* Shouldn't be possible. */
1094  ast_assert(0);
1095  break;
1096  case INVALID_ENDPOINT:
1097  ast_variables_destroy(vars);
1098  astman_send_error_va(s, m, "Unable to retrieve endpoint %s",
1099  endpoint_name);
1100  break;
1101  case ALLOC_ERROR:
1102  ast_variables_destroy(vars);
1103  astman_send_error(s, m, "Unable to allocate NOTIFY task data");
1104  break;
1105  case TASK_PUSH_ERROR:
1106  /* Don't need to destroy vars since it is handled by cleanup in push_notify */
1107  astman_send_error(s, m, "Unable to push NOTIFY task");
1108  break;
1109  case SUCCESS:
1110  astman_send_ack(s, m, "NOTIFY sent");
1111  break;
1112  }
1113 }
1114 
1115 /*!
1116  * \internal
1117  * \brief Completes SIPNotify AMI command in URI mode.
1118  */
1119 static void manager_notify_uri(struct mansession *s,
1120  const struct message *m, const char *uri)
1121 {
1123 
1124  switch (push_notify_uri(uri, vars, notify_ami_uri_data_create)) {
1125  case INVALID_CHANNEL:
1126  /* Shouldn't be possible. */
1127  ast_assert(0);
1128  break;
1129  case INVALID_ENDPOINT:
1130  /* Shouldn't be possible. */
1131  ast_assert(0);
1132  break;
1133  case ALLOC_ERROR:
1134  ast_variables_destroy(vars);
1135  astman_send_error(s, m, "Unable to allocate NOTIFY task data");
1136  break;
1137  case TASK_PUSH_ERROR:
1138  /* Don't need to destroy vars since it is handled by cleanup in push_notify_uri */
1139  astman_send_error(s, m, "Unable to push Notify task");
1140  break;
1141  case SUCCESS:
1142  astman_send_ack(s, m, "NOTIFY sent");
1143  break;
1144  }
1145 }
1146 
1147 /*!
1148  * \internal
1149  * \brief Completes SIPNotify AMI command in channel mode.
1150  */
1151 static void manager_notify_channel(struct mansession *s,
1152  const struct message *m, const char *channel)
1153 {
1155 
1156  switch (push_notify_channel(channel, vars, notify_ami_channel_data_create)) {
1157  case INVALID_CHANNEL:
1158  ast_variables_destroy(vars);
1159  astman_send_error(s, m, "Channel not found");
1160  break;
1161  case INVALID_ENDPOINT:
1162  /* Shouldn't be possible. */
1163  ast_assert(0);
1164  break;
1165  case ALLOC_ERROR:
1166  ast_variables_destroy(vars);
1167  astman_send_error(s, m, "Unable to allocate NOTIFY task data");
1168  break;
1169  case TASK_PUSH_ERROR:
1170  /* Don't need to destroy vars since it is handled by cleanup in push_notify_channel */
1171  astman_send_error(s, m, "Unable to push Notify task");
1172  break;
1173  case SUCCESS:
1174  astman_send_ack(s, m, "NOTIFY sent");
1175  break;
1176  }
1177 }
1178 
1179 /*!
1180  * \internal
1181  * \brief AMI entry point to send a SIP notify to an endpoint.
1182  */
1183 static int manager_notify(struct mansession *s, const struct message *m)
1184 {
1185  const char *endpoint_name = astman_get_header(m, "Endpoint");
1186  const char *uri = astman_get_header(m, "URI");
1187  const char *channel = astman_get_header(m, "Channel");
1188  int count = 0;
1189 
1190  if (!ast_strlen_zero(endpoint_name)) {
1191  ++count;
1192  }
1193  if (!ast_strlen_zero(uri)) {
1194  ++count;
1195  }
1196  if (!ast_strlen_zero(channel)) {
1197  ++count;
1198  }
1199 
1200  if (1 < count) {
1201  astman_send_error(s, m,
1202  "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel. "
1203  "You must use only one of them.");
1204  } else if (!ast_strlen_zero(endpoint_name)) {
1205  manager_notify_endpoint(s, m, endpoint_name);
1206  } else if (!ast_strlen_zero(uri)) {
1207  manager_notify_uri(s, m, uri);
1208  } else if (!ast_strlen_zero(channel)) {
1209  manager_notify_channel(s, m, channel);
1210  } else {
1211  astman_send_error(s, m,
1212  "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel.");
1213  }
1214 
1215  return 0;
1216 }
1217 
1218 static int load_module(void)
1219 {
1220  if (aco_info_init(&notify_cfg)) {
1221  return AST_MODULE_LOAD_DECLINE;
1222  }
1223 
1224  aco_option_register_custom(&notify_cfg, "", ACO_PREFIX, notify_options,
1225  "", notify_option_handler, 0);
1226 
1227  if (aco_process_config(&notify_cfg, 0)) {
1229  return AST_MODULE_LOAD_DECLINE;
1230  }
1231 
1232  ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options));
1234 
1235  return AST_MODULE_LOAD_SUCCESS;
1236 }
1237 
1238 static int reload_module(void)
1239 {
1241  return AST_MODULE_LOAD_DECLINE;
1242  }
1243 
1244  return 0;
1245 }
1246 
1247 static int unload_module(void)
1248 {
1249  ast_manager_unregister("PJSIPNotify");
1250  ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options));
1253 
1254  return 0;
1255 }
1256 
1257 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "CLI/AMI PJSIP NOTIFY Support",
1258  .support_level = AST_MODULE_SUPPORT_CORE,
1259  .load = load_module,
1260  .reload = reload_module,
1261  .unload = unload_module,
1262  .load_pri = AST_MODPRI_APP_DEPEND,
1263  .requires = "res_pjsip",
1264 );
static char * cli_complete_notify(const char *line, const char *word, int pos, int state, int using_uri)
struct ast_variable * next
static const char type[]
Definition: chan_ooh323.c:109
const char * body_text
Definition: res_pjsip.h:2033
#define ast_channel_lock(chan)
Definition: channel.h:2945
Main Channel structure associated with a channel.
struct ast_sip_endpoint * endpoint
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
static int not_allowed(const char *name)
static int multiple_headers_allowed(const char *name)
Asterisk main include file. File version handling, generic pbx functions.
A SIP address of record.
Definition: res_pjsip.h:361
struct ao2_container * notify_options
static void build_notify_body(pjsip_tx_data *tdata, struct ast_str *content_type, struct ast_str *content)
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static struct aco_file module_conf
static int notify_option_hash(const void *obj, int flags)
static void * notify_cfg_alloc(void)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
#define aco_option_register_custom(info, name, matchtype, types, default_val, handler, flags)
Register a config option.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
void * ast_channel_tech_pvt(const struct ast_channel *chan)
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2981
static int reload_module(void)
#define OBJ_KEY
Definition: astobj2.h:1155
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
struct ao2_container * items
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
struct ast_sip_session * session
Allow objects with duplicate keys in container.
Definition: astobj2.h:1185
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Definition: astobj2.h:1335
Structure for variables, used for configurations and for channel variables.
#define CONTENT_TYPE_SIZE
static int load_module(void)
#define var
Definition: ast_expr2f.c:614
Definition: cli.h:152
A structure which contains a channel implementation and session.
static void build_ami_notify(pjsip_tx_data *tdata, void *info)
struct ast_sip_session * session
Pointer to session.
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:1091
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
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.
static struct aco_type item
Definition: test_config.c:1463
static void manager_notify_endpoint(struct mansession *s, const struct message *m, const char *endpoint_name)
Completes SIPNotify AMI command in Endpoint mode.
#define ao2_global_obj_ref(holder)
Definition: astobj2.h:925
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
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
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:406
#define ast_assert(a)
Definition: utils.h:695
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3191
static struct test_val c
Definition: muted.c:95
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
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
void(* build_notify)(pjsip_tx_data *, void *)
struct ast_sip_endpoint * ast_sip_default_outbound_endpoint(void)
Retrieve the default outbound endpoint.
#define NULL
Definition: resample.c:96
The representation of a single configuration file to be processed.
AO2_GLOBAL_OBJ_STATIC(globals)
static void build_notify(pjsip_tx_data *tdata, const char *name, const char *value, struct ast_str **content_type, struct ast_str **content)
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
static struct notify_data * notify_cli_data_create(struct ast_sip_endpoint *endpoint, void *info)
struct pjsip_inv_session * inv_session
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
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:3164
A structure describing a SIP session.
static void notify_cfg_destroy(void *obj)
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:5034
const char * line
Definition: cli.h:162
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:5063
static int notify_uri(void *obj)
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:2820
#define ast_strlen_zero(foo)
Definition: strings.h:52
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
CONFIG_INFO_STANDARD(notify_cfg, globals, notify_cfg_alloc,.files=ACO_FILES(&module_conf))
static struct ast_cli_entry cli_options[]
struct ast_sip_endpoint * endpoint
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:1065
Configuration File Parser.
#define EVENT_FLAG_SYSTEM
Definition: manager.h:71
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static void notify_ami_channel_data_destroy(void *obj)
static struct notify_uri_data * notify_cli_uri_data_create(const char *uri, void *info)
static struct aco_type * notify_options[]
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
static void notify_ami_data_destroy(void *obj)
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:5091
#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:911
const int fd
Definition: cli.h:159
const char * type
Definition: res_pjsip.h:2029
static struct ast_mansession session
const int n
Definition: cli.h:165
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
static struct ao2_container * endpoints
#define ao2_ref(o, delta)
Definition: astobj2.h:464
In case you didn&#39;t read that giant block of text above the mansession_session struct, the struct mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1625
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
struct ao2_container * container
Definition: res_fax.c:502
static struct console_pvt globals
static int notify_contact(void *obj, void *arg, int flags)
static int notify_endpoint(void *obj)
An entity with which Asterisk communicates.
Definition: res_pjsip.h:812
Core PBX routines and definitions.
struct ast_taskprocessor * serializer
Their was an error and no changes were applied.
struct ast_sip_aor * ast_sip_location_retrieve_aor(const char *aor_name)
Retrieve a named AOR.
Definition: location.c:147
const char *const * argv
Definition: cli.h:161
static void notify_ami_uri_data_destroy(void *obj)
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7258
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static enum notify_result push_notify(const char *endpoint_name, void *info, task_data_create data_create)
void(* build_notify)(pjsip_tx_data *, void *)
#define LOG_ERROR
Definition: logger.h:285
static void notify_cli_data_destroy(void *obj)
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
static void manager_notify_channel(struct mansession *s, const struct message *m, const char *channel)
notify_result
#define CLI_SHOWUSAGE
Definition: cli.h:45
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:5138
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
static enum notify_result push_notify_uri(const char *uri, void *info, task_uri_data_create data_create)
#define ao2_global_obj_release(holder)
Definition: astobj2.h:865
def info(msg)
Contact associated with an address of record.
Definition: res_pjsip.h:281
const char * subtype
Definition: res_pjsip.h:2031
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
#define CONTENT_SIZE
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
#define LOG_NOTICE
Definition: logger.h:263
static void notify_option_destroy(void *obj)
struct notify_uri_data *(* task_uri_data_create)(const char *uri, void *info)
void(* build_notify)(pjsip_tx_data *, void *)
#define ast_channel_unlock(chan)
Definition: channel.h:2946
static void * notify_option_find(struct ao2_container *container, const char *category)
#define CLI_FAILURE
Definition: cli.h:46
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
static int reload(void)
Definition: cdr_mysql.c:741
static void notify_cli_uri_data_destroy(void *obj)
const char * word
Definition: cli.h:163
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int notify_option_cmp(void *obj, void *arg, int flags)
static void manager_notify_uri(struct mansession *s, const struct message *m, const char *uri)
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
static int manager_notify(struct mansession *s, const struct message *m)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
const char * usage
Definition: cli.h:177
static int notify_option_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
#define CLI_SUCCESS
Definition: cli.h:44
static void * notify_option_alloc(const char *category)
#define ACO_FILES(...)
struct notify_data *(* task_data_create)(struct ast_sip_endpoint *, void *info)
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
const char * name
char * strsep(char **str, const char *delims)
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:2911
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
Type information about a category-level configurable object.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
const char * ast_channel_name(const struct ast_channel *chan)
const int pos
Definition: cli.h:164
const char * filename
static struct notify_data * notify_ami_data_create(struct ast_sip_endpoint *endpoint, void *info)
static enum notify_result push_notify_channel(const char *channel_name, void *info, task_channel_data_create data_create)
static int unload_module(void)
static int notify_channel(void *obj)
static struct notify_channel_data * notify_ami_channel_data_create(struct ast_sip_session *session, void *info)
Generic container type.
struct notify_channel_data *(* task_channel_data_create)(struct ast_sip_session *session, void *info)
static void build_cli_notify(pjsip_tx_data *tdata, void *info)
static char * cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2726
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:186
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
const ast_string_field uri
Definition: res_pjsip.h:303
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3159
Asterisk module definitions.
static struct notify_uri_data * notify_ami_uri_data_create(const char *uri, void *info)
static char * cli_complete_endpoint(const char *word)
const struct ast_channel_tech * ast_channel_tech(const struct ast_channel *chan)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
SIP body description.
Definition: res_pjsip.h:2027
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:1250
static const char notify_config[]
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
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:4490
short word
Sorcery Data Access Layer API.
static struct test_val a
#define ao2_link(container, obj)
Definition: astobj2.h:1549