Asterisk - The Open Source Telephony Project GIT-master-77d630f
manager.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2006, Digium, Inc.
5 *
6 * Mark Spencer <markster@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/*! \file
20 *
21 * \brief The Asterisk Management Interface - AMI
22 *
23 * \author Mark Spencer <markster@digium.com>
24 *
25 * OpenSSL http://www.openssl.org - for AMI/SSL
26 *
27 * At the moment this file contains a number of functions, namely:
28 *
29 * - data structures storing AMI state
30 * - AMI-related API functions, used by internal asterisk components
31 * - handlers for AMI-related CLI functions
32 * - handlers for AMI functions (available through the AMI socket)
33 * - the code for the main AMI listener thread and individual session threads
34 * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
35 *
36 * \ref amiconf
37 */
38
39/*! \li \ref manager.c uses the configuration file \ref manager.conf
40 * \addtogroup configuration_file
41 */
42
43/*! \page manager.conf manager.conf
44 * \verbinclude manager.conf.sample
45 */
46
47/*** MODULEINFO
48 <support_level>core</support_level>
49 ***/
50
51#include "asterisk.h"
52
53#include "asterisk/paths.h" /* use various ast_config_AST_* */
54#include <ctype.h>
55#include <sys/time.h>
56#include <signal.h>
57#include <sys/mman.h>
58#include <sys/types.h>
59#include <regex.h>
60
61#include "asterisk/channel.h"
62#include "asterisk/file.h"
63#include "asterisk/manager.h"
64#include "asterisk/module.h"
65#include "asterisk/config.h"
66#include "asterisk/callerid.h"
67#include "asterisk/core_local.h"
68#include "asterisk/lock.h"
69#include "asterisk/cli.h"
70#include "asterisk/app.h"
71#include "asterisk/mwi.h"
72#include "asterisk/pbx.h"
73#include "asterisk/md5.h"
74#include "asterisk/acl.h"
75#include "asterisk/utils.h"
76#include "asterisk/tcptls.h"
77#include "asterisk/http.h"
81#include "asterisk/term.h"
82#include "asterisk/astobj2.h"
83#include "asterisk/features.h"
85#include "asterisk/aoc.h"
86#include "asterisk/strings.h"
92#include "asterisk/test.h"
93#include "asterisk/json.h"
94#include "asterisk/bridge.h"
97#include "asterisk/rtp_engine.h"
99#include "asterisk/translate.h"
101#include "asterisk/message.h"
102
103/*! \addtogroup Group_AMI AMI functions
104*/
105/*! @{
106 Doxygen group */
107
122
128};
129
130/*!
131 * Linked list of events.
132 * Global events are appended to the list by append_event().
133 * The usecount is the number of stored pointers to the element,
134 * excluding the list pointers. So an element that is only in
135 * the list has a usecount of 0, not 1.
136 *
137 * Clients have a pointer to the last event processed, and for each
138 * of these clients we track the usecount of the elements.
139 * If we have a pointer to an entry in the list, it is safe to navigate
140 * it forward because elements will not be deleted, but only appended.
141 * The worst that can happen is seeing the pointer still NULL.
142 *
143 * When the usecount of an element drops to 0, and the element is the
144 * first in the list, we can remove it. Removal is done within the
145 * main thread, which is woken up for the purpose.
146 *
147 * For simplicity of implementation, we make sure the list is never empty.
148 */
149struct eventqent {
150 int usecount; /*!< # of clients who still need the event */
152 unsigned int seq; /*!< sequence number */
153 struct timeval tv; /*!< When event was allocated */
156 char eventdata[1]; /*!< really variable size, allocated by append_event() */
157};
158
160
161static int displayconnects = 1;
162static int allowmultiplelogin = 1;
164static int httptimeout = 60;
165static int broken_events_action = 0;
166static int manager_enabled = 0;
167static int subscribed = 0;
168static int webmanager_enabled = 0;
169static int manager_debug = 0; /*!< enable some debugging code in the manager */
170static int authtimeout;
171static int authlimit;
174
175#define DEFAULT_REALM "asterisk"
176static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
177
178static int unauth_sessions = 0;
180
181/*! \brief A \ref stasis_topic that all topics AMI cares about will be forwarded to */
183
184/*! \brief The \ref stasis_message_router for all \ref stasis messages */
186
187/*! \brief The \ref stasis_subscription for forwarding the RTP topic to the AMI topic */
189
190/*! \brief The \ref stasis_subscription for forwarding the Security topic to the AMI topic */
192
193/*!
194 * \brief Set to true (non-zero) to globally allow all dangerous AMI actions to run
195 */
197
198#ifdef TEST_FRAMEWORK
199/*! \brief The \ref stasis_subscription for forwarding the Test topic to the AMI topic */
200static struct stasis_forward *test_suite_forwarder;
201#endif
202
203#define MGR_SHOW_TERMINAL_WIDTH 80
204
205#define MAX_VARS 128
206
207/*! \brief Fake event class used to end sessions at shutdown */
208#define EVENT_FLAG_SHUTDOWN -1
209
210/*! \brief
211 * Descriptor for a manager session, either on the AMI socket or over HTTP.
212 *
213 * \note
214 * AMI session have managerid == 0; the entry is created upon a connect,
215 * and destroyed with the socket.
216 * HTTP sessions have managerid != 0, the value is used as a search key
217 * to lookup sessions (using the mansession_id cookie, or nonce key from
218 * Digest Authentication http header).
219 */
220#define MAX_BLACKLIST_CMD_LEN 2
221static const struct {
222 const char *words[AST_MAX_CMD_LEN];
223} command_blacklist[] = {
224 {{ "module", "load", NULL }},
225 {{ "module", "unload", NULL }},
226 {{ "restart", "gracefully", NULL }},
228
229static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message);
230
232{
233 if (!acl_change_sub) {
238 }
239}
240
242{
244}
245
246/* In order to understand what the heck is going on with the
247 * mansession_session and mansession structs, we need to have a bit of a history
248 * lesson.
249 *
250 * In the beginning, there was the mansession. The mansession contained data that was
251 * intrinsic to a manager session, such as the time that it started, the name of the logged-in
252 * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
253 * sessions, these were used to represent the TCP socket over which the AMI session was taking
254 * place. It makes perfect sense for these fields to be a part of the session-specific data since
255 * the session actually defines this information.
256 *
257 * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
258 * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
259 * but rather to the action that is being executed. Because a single session may execute many commands
260 * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
261 * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
262 * has had a chance to properly close its handles.
263 *
264 * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
265 * from being run at the same time in a single session. Some manager actions may block for a long time, thus
266 * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
267 * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
268 * part of the action instead.
269 *
270 * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
271 * contain the action-specific information, such as which file to write to. In order to maintain expectations
272 * of action handlers and not have to change the public API of the manager code, we would need to name this
273 * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
274 * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
275 * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
276 * data.
277 */
279 /*! \todo XXX need to document which fields it is protecting */
280 struct ast_sockaddr addr; /*!< address we are connecting from */
281 struct ast_iostream *stream; /*!< AMI stream */
282 int inuse; /*!< number of HTTP sessions using this entry */
283 int needdestroy; /*!< Whether an HTTP session should be destroyed */
284 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
285 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
286 time_t sessionstart; /*!< Session start time */
287 struct timeval sessionstart_tv; /*!< Session start time */
288 time_t sessiontimeout; /*!< Session timeout if HTTP */
289 char username[80]; /*!< Logged in username */
290 char challenge[10]; /*!< Authentication challenge */
291 int authenticated; /*!< Authentication status */
292 int readperm; /*!< Authorization for reading */
293 int writeperm; /*!< Authorization for writing */
294 char inbuf[1025]; /*!< Buffer - we use the extra byte to add a '\\0' and simplify parsing */
295 int inlen; /*!< number of buffered bytes */
296 struct ao2_container *includefilters; /*!< Manager event filters - include list */
297 struct ao2_container *excludefilters; /*!< Manager event filters - exclude list */
298 struct ast_variable *chanvars; /*!< Channel variables to set for originate */
299 int send_events; /*!< XXX what ? */
300 struct eventqent *last_ev; /*!< last event processed. */
301 int writetimeout; /*!< Timeout for ast_carefulwrite() */
302 time_t authstart;
303 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
304 time_t noncetime; /*!< Timer for nonce value expiration */
305 unsigned long oldnonce; /*!< Stale nonce value */
306 unsigned long nc; /*!< incremental nonce counter */
307 unsigned int kicked:1; /*!< Flag set if session is forcibly kicked */
308 ast_mutex_t notify_lock; /*!< Lock for notifying this session of events */
311};
312
317
318/*! \brief In case you didn't read that giant block of text above the mansession_session struct, the
319 * \ref mansession is named this solely to keep the API the same in Asterisk. This structure really
320 * represents data that is different from Manager action to Manager action. The mansession_session pointer
321 * contained within points to session-specific data.
322 */
328 unsigned int write_error:1;
331};
332
333/*! Active manager connection sessions container. */
334static AO2_GLOBAL_OBJ_STATIC(mgr_sessions);
335
336/*! \brief user descriptor, as read from the config file.
337 *
338 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
339 * lines which are not supported here, and readperm/writeperm/writetimeout
340 * are not stored.
341 */
343 char username[80];
344 char *secret; /*!< Secret for logging in */
345 int readperm; /*!< Authorization for reading */
346 int writeperm; /*!< Authorization for writing */
347 int writetimeout; /*!< Per user Timeout for ast_carefulwrite() */
348 int displayconnects; /*!< XXX unused */
349 int allowmultiplelogin; /*!< Per user option*/
350 int keep; /*!< mark entries created on a reload */
351 struct ao2_container *includefilters; /*!< Manager event filters - include list */
352 struct ao2_container *excludefilters; /*!< Manager event filters - exclude list */
353 struct ast_acl_list *acl; /*!< ACL setting */
354 char *a1_hash; /*!< precalculated A1 for Digest auth */
355 struct ast_variable *chanvars; /*!< Channel variables to set for originate */
357};
358
359/*! \brief list of users found in the config file */
361
362/*! \brief list of actions registered */
364
365/*! \brief list of hooks registered */
367
368#ifdef AST_XML_DOCS
369/*! \brief A container of event documentation nodes */
370static AO2_GLOBAL_OBJ_STATIC(event_docs);
371#endif
372
373static int __attribute__((format(printf, 9, 0))) __manager_event_sessions(
374 struct ao2_container *sessions,
375 int category,
376 const char *event,
377 int chancount,
378 struct ast_channel **chans,
379 const char *file,
380 int line,
381 const char *func,
382 const char *fmt,
383 ...);
384
392};
393
394static char *match_type_names[] = {
395 [FILTER_MATCH_REGEX] = "regex",
396 [FILTER_MATCH_EXACT] = "exact",
397 [FILTER_MATCH_STARTS_WITH] = "starts_with",
398 [FILTER_MATCH_ENDS_WITH] = "ends_with",
399 [FILTER_MATCH_CONTAINS] = "contains",
400 [FILTER_MATCH_NONE] = "none",
401};
402
405 regex_t *regex_filter;
408 unsigned int event_name_hash;
411};
412
413static enum add_filter_result manager_add_filter(const char *criteria,
414 const char *filter_pattern, struct ao2_container *includefilters,
415 struct ao2_container *excludefilters);
416
417static int should_send_event(struct ao2_container *includefilters,
418 struct ao2_container *excludefilters, struct eventqent *eqe);
419
420/*!
421 * @{ \brief Define AMI message types.
422 */
424/*! @} */
425
426/*!
427 * \internal
428 * \brief Find a registered action object.
429 *
430 * \param name Name of AMI action to find.
431 *
432 * \return Reffed action found or NULL
433 */
434static struct manager_action *action_find(const char *name)
435{
436 struct manager_action *act;
437
439 AST_RWLIST_TRAVERSE(&actions, act, list) {
440 if (!strcasecmp(name, act->action)) {
441 ao2_t_ref(act, +1, "found action object");
442 break;
443 }
444 }
446
447 return act;
448}
449
451{
452 return manager_topic;
453}
454
456{
457 return stasis_router;
458}
459
460static void manager_json_value_str_append(struct ast_json *value, const char *key,
461 struct ast_str **res)
462{
463 switch (ast_json_typeof(value)) {
464 case AST_JSON_STRING:
465 ast_str_append(res, 0, "%s: %s\r\n", key, ast_json_string_get(value));
466 break;
467 case AST_JSON_INTEGER:
468 ast_str_append(res, 0, "%s: %jd\r\n", key, ast_json_integer_get(value));
469 break;
470 case AST_JSON_TRUE:
471 ast_str_append(res, 0, "%s: True\r\n", key);
472 break;
473 case AST_JSON_FALSE:
474 ast_str_append(res, 0, "%s: False\r\n", key);
475 break;
476 default:
477 ast_str_append(res, 0, "%s: \r\n", key);
478 break;
479 }
480}
481
482static void manager_json_to_ast_str(struct ast_json *obj, const char *key,
483 struct ast_str **res, key_exclusion_cb exclusion_cb);
484
485static void manager_json_array_with_key(struct ast_json *obj, const char* key,
486 size_t index, struct ast_str **res,
487 key_exclusion_cb exclusion_cb)
488{
489 struct ast_str *key_str = ast_str_alloca(64);
490 ast_str_set(&key_str, 0, "%s(%zu)", key, index);
492 res, exclusion_cb);
493}
494
495static void manager_json_obj_with_key(struct ast_json *obj, const char* key,
496 const char *parent_key, struct ast_str **res,
497 key_exclusion_cb exclusion_cb)
498{
499 if (parent_key) {
500 struct ast_str *key_str = ast_str_alloca(64);
501 ast_str_set(&key_str, 0, "%s/%s", parent_key, key);
503 res, exclusion_cb);
504 return;
505 }
506
507 manager_json_to_ast_str(obj, key, res, exclusion_cb);
508}
509
510void manager_json_to_ast_str(struct ast_json *obj, const char *key,
511 struct ast_str **res, key_exclusion_cb exclusion_cb)
512{
513 struct ast_json_iter *i;
514
515 /* If obj or res is not given, just return */
516 if (!obj || !res) {
517 return;
518 }
519
520 if (!*res && !(*res = ast_str_create(1024))) {
521 return;
522 }
523
524 if (exclusion_cb && key && exclusion_cb(key)) {
525 return;
526 }
527
528 if (ast_json_typeof(obj) != AST_JSON_OBJECT &&
530 manager_json_value_str_append(obj, key, res);
531 return;
532 }
533
534 if (ast_json_typeof(obj) == AST_JSON_ARRAY) {
535 size_t j;
536 for (j = 0; j < ast_json_array_size(obj); ++j) {
538 key, j, res, exclusion_cb);
539 }
540 return;
541 }
542
543 for (i = ast_json_object_iter(obj); i;
544 i = ast_json_object_iter_next(obj, i)) {
547 key, res, exclusion_cb);
548 }
549}
550
552{
553 struct ast_str *res = ast_str_create(1024);
554
555 if (!ast_json_is_null(blob)) {
556 manager_json_to_ast_str(blob, NULL, &res, exclusion_cb);
557 }
558
559 return res;
560}
561
562#define manager_event_sessions(sessions, category, event, contents , ...) \
563 __manager_event_sessions(sessions, category, event, 0, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__, contents , ## __VA_ARGS__)
564
565#define any_manager_listeners(sessions) \
566 ((sessions && ao2_container_count(sessions)) || !AST_RWLIST_EMPTY(&manager_hooks))
567
568static void manager_default_msg_cb(void *data, struct stasis_subscription *sub,
569 struct stasis_message *message)
570{
571 struct ao2_container *sessions;
572 struct ast_manager_event_blob *ev;
573
575 /* Not an AMI message; disregard */
576 return;
577 }
578
579 sessions = ao2_global_obj_ref(mgr_sessions);
581 /* Nobody is listening */
583 return;
584 }
585
587 if (!ev) {
588 /* Conversion failure */
590 return;
591 }
592
594 "%s", ev->extra_fields);
595 ao2_ref(ev, -1);
597}
598
599static void manager_generic_msg_cb(void *data, struct stasis_subscription *sub,
600 struct stasis_message *message)
601{
602 struct ast_json_payload *payload;
603 int class_type;
604 const char *type;
605 struct ast_json *event;
606 struct ast_str *event_buffer;
607 struct ao2_container *sessions;
608
609 sessions = ao2_global_obj_ref(mgr_sessions);
611 /* Nobody is listening */
613 return;
614 }
615
616 payload = stasis_message_data(message);
617 class_type = ast_json_integer_get(ast_json_object_get(payload->json, "class_type"));
619 event = ast_json_object_get(payload->json, "event");
620
622 if (!event_buffer) {
623 ast_log(AST_LOG_WARNING, "Error while creating payload for event %s\n", type);
625 return;
626 }
627
629 "%s", ast_str_buffer(event_buffer));
630 ast_free(event_buffer);
632}
633
634void ast_manager_publish_event(const char *type, int class_type, struct ast_json *obj)
635{
636 RAII_VAR(struct ast_json *, event_info, NULL, ast_json_unref);
637 RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
639
640 if (!obj || !ast_manager_get_generic_type()) {
641 return;
642 }
643
644 ast_json_ref(obj);
645 event_info = ast_json_pack("{s: s, s: i, s: o}",
646 "type", type,
647 "class_type", class_type,
648 "event", obj);
649 if (!event_info) {
650 return;
651 }
652
653 payload = ast_json_payload_create(event_info);
654 if (!payload) {
655 return;
656 }
658 if (!message) {
659 return;
660 }
662}
663
664/*! \brief Add a custom hook to be called when an event is fired */
666{
670}
671
672/*! \brief Delete a custom hook to be called when an event is fired */
674{
676 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
678}
679
681{
682 return manager_enabled;
683}
684
686{
688}
689
690/*!
691 * Grab a reference to the last event, update usecount as needed.
692 * Can handle a NULL pointer.
693 */
694static struct eventqent *grab_last(void)
695{
696 struct eventqent *ret;
697
700 /* the list is never empty now, but may become so when
701 * we optimize it in the future, so be prepared.
702 */
703 if (ret) {
705 }
707 return ret;
708}
709
710/*!
711 * Purge unused events. Remove elements from the head
712 * as long as their usecount is 0 and there is a next element.
713 */
714static void purge_events(void)
715{
716 struct eventqent *ev;
717 struct timeval now = ast_tvnow();
718
720 while ( (ev = AST_RWLIST_FIRST(&all_events)) &&
721 ev->usecount == 0 && AST_RWLIST_NEXT(ev, eq_next)) {
723 ast_free(ev);
724 }
725
727 /* Never release the last event */
728 if (!AST_RWLIST_NEXT(ev, eq_next)) {
729 break;
730 }
731
732 /* 2.5 times whatever the HTTP timeout is (maximum 2.5 hours) is the maximum time that we will definitely cache an event */
733 if (ev->usecount == 0 && ast_tvdiff_sec(now, ev->tv) > (httptimeout > 3600 ? 3600 : httptimeout) * 2.5) {
735 ast_free(ev);
736 }
737 }
740}
741
742/*!
743 * helper functions to convert back and forth between
744 * string and numeric representation of set of flags
745 */
746static const struct permalias {
747 int num;
748 const char *label;
749} perms[] = {
750 { EVENT_FLAG_SYSTEM, "system" },
751 { EVENT_FLAG_CALL, "call" },
752 { EVENT_FLAG_LOG, "log" },
753 { EVENT_FLAG_VERBOSE, "verbose" },
754 { EVENT_FLAG_COMMAND, "command" },
755 { EVENT_FLAG_AGENT, "agent" },
756 { EVENT_FLAG_USER, "user" },
757 { EVENT_FLAG_CONFIG, "config" },
758 { EVENT_FLAG_DTMF, "dtmf" },
759 { EVENT_FLAG_REPORTING, "reporting" },
760 { EVENT_FLAG_CDR, "cdr" },
761 { EVENT_FLAG_DIALPLAN, "dialplan" },
762 { EVENT_FLAG_ORIGINATE, "originate" },
763 { EVENT_FLAG_AGI, "agi" },
764 { EVENT_FLAG_CC, "cc" },
765 { EVENT_FLAG_AOC, "aoc" },
766 { EVENT_FLAG_TEST, "test" },
767 { EVENT_FLAG_SECURITY, "security" },
768 { EVENT_FLAG_MESSAGE, "message" },
769 { INT_MAX, "all" },
770 { 0, "none" },
772
773/*! Maximum string length of the AMI authority permission string buildable from perms[]. */
774#define MAX_AUTH_PERM_STRING 150
775
776/*! \brief Checks to see if a string which can be used to evaluate functions should be rejected */
777static int function_capable_string_allowed_with_auths(const char *evaluating, int writepermlist)
778{
779 if (!(writepermlist & EVENT_FLAG_SYSTEM)
780 && (
781 strstr(evaluating, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
782 strstr(evaluating, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
783 )) {
784 return 0;
785 }
786 return 1;
787}
788
789/*! \brief Convert authority code to a list of options for a user. This will only
790 * display those authority codes that have an explicit match on authority */
791static const char *user_authority_to_str(int authority, struct ast_str **res)
792{
793 int i;
794 char *sep = "";
795
796 ast_str_reset(*res);
797 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
798 if ((authority & perms[i].num) == perms[i].num) {
799 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
800 sep = ",";
801 }
802 }
803
804 if (ast_str_strlen(*res) == 0) {
805 /* replace empty string with something sensible */
806 ast_str_append(res, 0, "<none>");
807 }
808
809 return ast_str_buffer(*res);
810}
811
812
813/*! \brief Convert authority code to a list of options. Note that the EVENT_FLAG_ALL
814 * authority will always be returned. */
815static const char *authority_to_str(int authority, struct ast_str **res)
816{
817 int i;
818 char *sep = "";
819
820 ast_str_reset(*res);
821 if (authority != EVENT_FLAG_SHUTDOWN) {
822 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
823 if (authority & perms[i].num) {
824 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
825 sep = ",";
826 }
827 }
828 }
829
830 if (ast_str_strlen(*res) == 0) {
831 /* replace empty string with something sensible */
832 ast_str_append(res, 0, "<none>");
833 }
834
835 return ast_str_buffer(*res);
836}
837
838/*! Tells you if smallstr exists inside bigstr
839 which is delim by delim and uses no buf or stringsep
840 ast_instring("this|that|more","this",'|') == 1;
841
842 feel free to move this to app.c -anthm */
843static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
844{
845 const char *val = bigstr, *next;
846
847 do {
848 if ((next = strchr(val, delim))) {
849 if (!strncmp(val, smallstr, (next - val))) {
850 return 1;
851 } else {
852 continue;
853 }
854 } else {
855 return !strcmp(smallstr, val);
856 }
857 } while (*(val = (next + 1)));
858
859 return 0;
860}
861
862static int get_perm(const char *instr)
863{
864 int x = 0, ret = 0;
865
866 if (!instr) {
867 return 0;
868 }
869
870 for (x = 0; x < ARRAY_LEN(perms); x++) {
871 if (ast_instring(instr, perms[x].label, ',')) {
872 ret |= perms[x].num;
873 }
874 }
875
876 return ret;
877}
878
879/*!
880 * A number returns itself, false returns 0, true returns all flags,
881 * other strings return the flags that are set.
882 */
883static int strings_to_mask(const char *string)
884{
885 const char *p;
886
887 if (ast_strlen_zero(string)) {
888 return -1;
889 }
890
891 for (p = string; *p; p++) {
892 if (*p < '0' || *p > '9') {
893 break;
894 }
895 }
896 if (!*p) { /* all digits */
897 return atoi(string);
898 }
899 if (ast_false(string)) {
900 return 0;
901 }
902 if (ast_true(string)) { /* all permissions */
903 int x, ret = 0;
904 for (x = 0; x < ARRAY_LEN(perms); x++) {
905 ret |= perms[x].num;
906 }
907 return ret;
908 }
909 return get_perm(string);
910}
911
912/*! \brief Unreference manager session object.
913 If no more references, then go ahead and delete it */
915{
916 int refcount = ao2_ref(s, -1);
917 if (manager_debug) {
918 ast_debug(1, "Mansession: %p refcount now %d\n", s, refcount - 1);
919 }
920 return NULL;
921}
922
923static void event_filter_destructor(void *obj)
924{
925 struct event_filter_entry *entry = obj;
926 if (entry->regex_filter) {
927 regfree(entry->regex_filter);
928 ast_free(entry->regex_filter);
929 }
930 ast_free(entry->event_name);
931 ast_free(entry->header_name);
932 ast_free(entry->string_filter);
933}
934
935static void session_destructor(void *obj)
936{
937 struct mansession_session *session = obj;
938 struct eventqent *eqe = session->last_ev;
939 struct ast_datastore *datastore;
940
941 /* Get rid of each of the data stores on the session */
942 while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
943 /* Free the data store */
944 ast_datastore_free(datastore);
945 }
946
947 if (eqe) {
949 }
950 if (session->chanvars) {
952 }
953
954 if (session->includefilters) {
955 ao2_t_ref(session->includefilters, -1, "decrement ref for include container, should be last one");
956 }
957
958 if (session->excludefilters) {
959 ao2_t_ref(session->excludefilters, -1, "decrement ref for exclude container, should be last one");
960 }
961
962 ast_mutex_destroy(&session->notify_lock);
963}
964
965/*! \brief Allocate manager session structure and add it to the list of sessions */
967{
968 struct ao2_container *sessions;
969 struct mansession_session *newsession;
970
971 newsession = ao2_alloc(sizeof(*newsession), session_destructor);
972 if (!newsession) {
973 return NULL;
974 }
975
978 if (!newsession->includefilters || !newsession->excludefilters) {
979 ao2_ref(newsession, -1);
980 return NULL;
981 }
982
983 newsession->waiting_thread = AST_PTHREADT_NULL;
984 newsession->writetimeout = 100;
985 newsession->send_events = -1;
986 ast_sockaddr_copy(&newsession->addr, addr);
987
988 ast_mutex_init(&newsession->notify_lock);
989
990 sessions = ao2_global_obj_ref(mgr_sessions);
991 if (sessions) {
992 ao2_link(sessions, newsession);
993 ao2_ref(sessions, -1);
994 }
995
996 return newsession;
997}
998
999static int mansession_cmp_fn(void *obj, void *arg, int flags)
1000{
1001 struct mansession_session *s = obj;
1002 char *str = arg;
1003 return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
1004}
1005
1007{
1008 struct ao2_container *sessions;
1009
1010 sessions = ao2_global_obj_ref(mgr_sessions);
1011 if (sessions) {
1012 ao2_unlink(sessions, s);
1013 ao2_ref(sessions, -1);
1014 }
1016}
1017
1018
1019static int check_manager_session_inuse(const char *name)
1020{
1021 struct ao2_container *sessions;
1023 int inuse = 0;
1024
1025 sessions = ao2_global_obj_ref(mgr_sessions);
1026 if (sessions) {
1027 session = ao2_find(sessions, (char *) name, 0);
1028 ao2_ref(sessions, -1);
1029 if (session) {
1031 inuse = 1;
1032 }
1033 }
1034 return inuse;
1035}
1036
1037
1038/*!
1039 * lookup an entry in the list of registered users.
1040 * must be called with the list lock held.
1041 */
1043{
1044 struct ast_manager_user *user = NULL;
1045
1046 AST_RWLIST_TRAVERSE(&users, user, list) {
1047 if (!strcasecmp(user->username, name)) {
1048 break;
1049 }
1050 }
1051
1052 return user;
1053}
1054
1055/*! \brief Get displayconnects config option.
1056 * \param session manager session to get parameter from.
1057 * \return displayconnects config option value.
1058 */
1060{
1061 struct ast_manager_user *user = NULL;
1062 int ret = 0;
1063
1065 if ((user = get_manager_by_name_locked(session->username))) {
1066 ret = user->displayconnects;
1067 }
1069
1070 return ret;
1071}
1072
1073#ifdef AST_XML_DOCS
1074static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance);
1075#endif
1076
1077static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1078{
1079 struct manager_action *cur;
1080 struct ast_str *authority;
1081 int num;
1082 int l;
1083 const char *auth_str;
1084
1085 switch (cmd) {
1086 case CLI_INIT:
1087 e->command = "manager show command";
1088 e->usage =
1089 "Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
1090 " Shows the detailed description for a specific Asterisk manager interface command.\n";
1091 return NULL;
1092 case CLI_GENERATE:
1093 l = strlen(a->word);
1095 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1096 if (!strncasecmp(a->word, cur->action, l)) {
1098 break;
1099 }
1100 }
1101 }
1103 return NULL;
1104 }
1105 if (a->argc < 4) {
1106 return CLI_SHOWUSAGE;
1107 }
1108
1110
1112 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1113 for (num = 3; num < a->argc; num++) {
1114 if (!strcasecmp(cur->action, a->argv[num])) {
1115 auth_str = authority_to_str(cur->authority, &authority);
1116
1117#ifdef AST_XML_DOCS
1118 if (cur->docsrc == AST_XML_DOC) {
1119 char *synopsis = ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1);
1120 char *since = ast_xmldoc_printable(S_OR(cur->since, "Not available"), 1);
1121 char *description = ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1);
1122 char *syntax = ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1);
1123 char *arguments = ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1);
1124 char *privilege = ast_xmldoc_printable(S_OR(auth_str, "Not available"), 1);
1125 char *seealso = ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1);
1126 char *responses = ast_xmldoc_printable("None", 1);
1127
1128 if (!synopsis || !since || !description || !syntax || !arguments
1129 || !privilege || !seealso || !responses) {
1131 ast_free(since);
1132 ast_free(description);
1133 ast_free(syntax);
1134 ast_free(arguments);
1135 ast_free(privilege);
1136 ast_free(seealso);
1137 ast_free(responses);
1138 ast_cli(a->fd, "Allocation failure.\n");
1140
1141 return CLI_FAILURE;
1142 }
1143
1144 ast_cli(a->fd, "\n"
1145 "%s -= Info about Manager Command '%s' =- %s\n\n"
1146 COLORIZE_FMT "\n"
1147 "%s\n\n"
1148 COLORIZE_FMT "\n"
1149 "%s\n\n"
1150 COLORIZE_FMT "\n"
1151 "%s\n\n"
1152 COLORIZE_FMT "\n"
1153 "%s\n\n"
1154 COLORIZE_FMT "\n"
1155 "%s\n\n"
1156 COLORIZE_FMT "\n"
1157 "%s\n\n"
1158 COLORIZE_FMT "\n"
1159 "%s\n\n"
1160 COLORIZE_FMT "\n",
1162 COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
1163 COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
1164 COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
1165 COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
1166 COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
1167 COLORIZE(COLOR_MAGENTA, 0, "[Privilege]"), privilege,
1168 COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso,
1169 COLORIZE(COLOR_MAGENTA, 0, "[List Responses]")
1170 );
1171
1172 if (!cur->list_responses) {
1173 ast_cli(a->fd, "%s\n\n", responses);
1174 } else {
1175 struct ast_xml_doc_item *temp;
1176 for (temp = cur->list_responses; temp; temp = AST_LIST_NEXT(temp, next)) {
1177 ast_cli(a->fd, "Event: %s\n", temp->name);
1178 print_event_instance(a, temp);
1179 }
1180 }
1181 ast_cli(a->fd,
1182 COLORIZE_FMT "\n",
1183 COLORIZE(COLOR_MAGENTA, 0, "[End List Responses]")
1184 );
1185
1186 ast_cli(a->fd, "\n"
1187 COLORIZE_FMT "\n",
1188 COLORIZE(COLOR_MAGENTA, 0, "[Final Response]")
1189 );
1190 if (!cur->final_response) {
1191 ast_cli(a->fd, "%s\n\n", responses);
1192 } else {
1193 ast_cli(a->fd, "Event: %s\n", cur->final_response->name);
1195 }
1196 ast_cli(a->fd,
1197 COLORIZE_FMT "\n",
1198 COLORIZE(COLOR_MAGENTA, 0, "[End Final Response]")
1199 );
1200
1202 ast_free(since);
1206 ast_free(privilege);
1208 ast_free(responses);
1209 } else
1210#endif
1211 {
1212 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
1213 cur->action, cur->synopsis,
1214 auth_str,
1215 S_OR(cur->description, ""));
1216 }
1217 }
1218 }
1219 }
1221
1222 return CLI_SUCCESS;
1223}
1224
1225static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1226{
1227 switch (cmd) {
1228 case CLI_INIT:
1229 e->command = "manager set debug [on|off]";
1230 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
1231 return NULL;
1232 case CLI_GENERATE:
1233 return NULL;
1234 }
1235
1236 if (a->argc == 3) {
1237 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
1238 } else if (a->argc == 4) {
1239 if (!strcasecmp(a->argv[3], "on")) {
1240 manager_debug = 1;
1241 } else if (!strcasecmp(a->argv[3], "off")) {
1242 manager_debug = 0;
1243 } else {
1244 return CLI_SHOWUSAGE;
1245 }
1246 }
1247 return CLI_SUCCESS;
1248}
1249
1250static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1251{
1252 struct ast_manager_user *user = NULL;
1253 int l;
1254 struct ast_str *rauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
1255 struct ast_str *wauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
1256 struct ast_variable *v;
1257
1258 switch (cmd) {
1259 case CLI_INIT:
1260 e->command = "manager show user";
1261 e->usage =
1262 " Usage: manager show user <user>\n"
1263 " Display all information related to the manager user specified.\n";
1264 return NULL;
1265 case CLI_GENERATE:
1266 l = strlen(a->word);
1267 if (a->pos != 3) {
1268 return NULL;
1269 }
1271 AST_RWLIST_TRAVERSE(&users, user, list) {
1272 if (!strncasecmp(a->word, user->username, l)) {
1273 if (ast_cli_completion_add(ast_strdup(user->username))) {
1274 break;
1275 }
1276 }
1277 }
1279 return NULL;
1280 }
1281
1282 if (a->argc != 4) {
1283 return CLI_SHOWUSAGE;
1284 }
1285
1287
1288 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
1289 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
1291 return CLI_SUCCESS;
1292 }
1293
1294 ast_cli(a->fd, "\n");
1295 ast_cli(a->fd,
1296 " username: %s\n"
1297 " secret: %s\n"
1298 " ACL: %s\n"
1299 " read perm: %s\n"
1300 " write perm: %s\n"
1301 " displayconnects: %s\n"
1302 "allowmultiplelogin: %s\n",
1303 S_OR(user->username, "(N/A)"),
1304 (user->secret ? "<Set>" : "(N/A)"),
1305 ((user->acl && !ast_acl_list_is_empty(user->acl)) ? "yes" : "no"),
1306 user_authority_to_str(user->readperm, &rauthority),
1307 user_authority_to_str(user->writeperm, &wauthority),
1308 (user->displayconnects ? "yes" : "no"),
1309 (user->allowmultiplelogin ? "yes" : "no"));
1310 ast_cli(a->fd, " Variables: \n");
1311 for (v = user->chanvars ; v ; v = v->next) {
1312 ast_cli(a->fd, " %s = %s\n", v->name, v->value);
1313 }
1314 if (!ast_acl_list_is_empty(user->acl)) {
1315 ast_acl_output(a->fd, user->acl, NULL);
1316 }
1317
1319
1320 return CLI_SUCCESS;
1321}
1322
1323static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1324{
1325 struct ast_manager_user *user = NULL;
1326 int count_amu = 0;
1327 switch (cmd) {
1328 case CLI_INIT:
1329 e->command = "manager show users";
1330 e->usage =
1331 "Usage: manager show users\n"
1332 " Prints a listing of all managers that are currently configured on that\n"
1333 " system.\n";
1334 return NULL;
1335 case CLI_GENERATE:
1336 return NULL;
1337 }
1338 if (a->argc != 3) {
1339 return CLI_SHOWUSAGE;
1340 }
1341
1343
1344 /* If there are no users, print out something along those lines */
1345 if (AST_RWLIST_EMPTY(&users)) {
1346 ast_cli(a->fd, "There are no manager users.\n");
1348 return CLI_SUCCESS;
1349 }
1350
1351 ast_cli(a->fd, "\nusername\n--------\n");
1352
1353 AST_RWLIST_TRAVERSE(&users, user, list) {
1354 ast_cli(a->fd, "%s\n", user->username);
1355 count_amu++;
1356 }
1357
1359
1360 ast_cli(a->fd,"-------------------\n"
1361 "%d manager users configured.\n", count_amu);
1362 return CLI_SUCCESS;
1363}
1364
1365/*! \brief CLI command manager list commands */
1366static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1367{
1368 struct manager_action *cur;
1369 int name_len = 1;
1370 int space_remaining;
1371#define HSMC_FORMAT " %-*.*s %-.*s\n"
1372 switch (cmd) {
1373 case CLI_INIT:
1374 e->command = "manager show commands";
1375 e->usage =
1376 "Usage: manager show commands\n"
1377 " Prints a listing of all the available Asterisk manager interface commands.\n";
1378 return NULL;
1379 case CLI_GENERATE:
1380 return NULL;
1381 }
1382
1384 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1385 int incoming_len = strlen(cur->action);
1386 if (incoming_len > name_len) {
1387 name_len = incoming_len;
1388 }
1389 }
1390
1391 space_remaining = MGR_SHOW_TERMINAL_WIDTH - name_len - 4;
1392 if (space_remaining < 0) {
1393 space_remaining = 0;
1394 }
1395
1396 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "Action", space_remaining, "Synopsis");
1397 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "------", space_remaining, "--------");
1398
1399 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1400 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, cur->action, space_remaining, cur->synopsis);
1401 }
1403
1404 return CLI_SUCCESS;
1405}
1406
1407/*! \brief CLI command manager kick session */
1408static char *handle_kickmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1409{
1410 struct ao2_container *sessions;
1412 struct ao2_iterator i;
1413 int fd = -1;
1414 int found = 0;
1415
1416 switch (cmd) {
1417 case CLI_INIT:
1418 e->command = "manager kick session";
1419 e->usage =
1420 "Usage: manager kick session <file descriptor>\n"
1421 " Kick an active Asterisk Manager Interface session\n";
1422 return NULL;
1423 case CLI_GENERATE:
1424 return NULL;
1425 }
1426
1427 if (a->argc != 4) {
1428 return CLI_SHOWUSAGE;
1429 }
1430
1431 fd = atoi(a->argv[3]);
1432 if (fd <= 0) { /* STDOUT won't be a valid AMI fd either */
1433 ast_cli(a->fd, "Invalid AMI file descriptor: %s\n", a->argv[3]);
1434 return CLI_FAILURE;
1435 }
1436
1437 sessions = ao2_global_obj_ref(mgr_sessions);
1438 if (sessions) {
1440 ao2_ref(sessions, -1);
1441 while ((session = ao2_iterator_next(&i))) {
1443 if (session->stream) {
1444 if (ast_iostream_get_fd(session->stream) == fd) {
1445 if (session->kicked) {
1446 ast_cli(a->fd, "Manager session using file descriptor %d has already been kicked\n", fd);
1449 break;
1450 }
1451 fd = ast_iostream_get_fd(session->stream);
1452 found = fd;
1453 ast_cli(a->fd, "Kicking manager session connected using file descriptor %d\n", fd);
1454 ast_mutex_lock(&session->notify_lock);
1455 session->kicked = 1;
1456 if (session->waiting_thread != AST_PTHREADT_NULL) {
1457 pthread_kill(session->waiting_thread, SIGURG);
1458 }
1459 ast_mutex_unlock(&session->notify_lock);
1462 break;
1463 }
1464 }
1467 }
1469 }
1470
1471 if (!found) {
1472 ast_cli(a->fd, "No manager session found using file descriptor %d\n", fd);
1473 }
1474 return CLI_SUCCESS;
1475}
1476
1477/*! \brief CLI command manager list connected */
1478static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1479{
1480 struct ao2_container *sessions;
1482 time_t now = time(NULL);
1483#define HSMCONN_FORMAT1 " %-15.15s %-55.55s %-10.10s %-10.10s %-8.8s %-8.8s %-10.10s %-10.10s\n"
1484#define HSMCONN_FORMAT2 " %-15.15s %-55.55s %-10d %-10d %-8d %-8d %-10.10d %-10.10d\n"
1485 int count = 0;
1486 struct ao2_iterator i;
1487
1488 switch (cmd) {
1489 case CLI_INIT:
1490 e->command = "manager show connected";
1491 e->usage =
1492 "Usage: manager show connected\n"
1493 " Prints a listing of the users that are currently connected to the\n"
1494 "Asterisk manager interface.\n";
1495 return NULL;
1496 case CLI_GENERATE:
1497 return NULL;
1498 }
1499
1500 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "ReadPerms", "WritePerms");
1501
1502 sessions = ao2_global_obj_ref(mgr_sessions);
1503 if (sessions) {
1505 ao2_ref(sessions, -1);
1506 while ((session = ao2_iterator_next(&i))) {
1508 ast_cli(a->fd, HSMCONN_FORMAT2, session->username,
1510 (int) (session->sessionstart),
1511 (int) (now - session->sessionstart),
1512 session->stream ? ast_iostream_get_fd(session->stream) : -1,
1513 session->inuse,
1514 session->readperm,
1515 session->writeperm);
1516 count++;
1519 }
1521 }
1522 ast_cli(a->fd, "%d users connected.\n", count);
1523
1524 return CLI_SUCCESS;
1525}
1526
1527/*! \brief CLI command manager list eventq */
1528/* Should change to "manager show connected" */
1529static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1530{
1531 struct eventqent *s;
1532 switch (cmd) {
1533 case CLI_INIT:
1534 e->command = "manager show eventq";
1535 e->usage =
1536 "Usage: manager show eventq\n"
1537 " Prints a listing of all events pending in the Asterisk manger\n"
1538 "event queue.\n";
1539 return NULL;
1540 case CLI_GENERATE:
1541 return NULL;
1542 }
1544 AST_RWLIST_TRAVERSE(&all_events, s, eq_next) {
1545 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
1546 ast_cli(a->fd, "Category: %d\n", s->category);
1547 ast_cli(a->fd, "Event:\n%s", s->eventdata);
1548 }
1550
1551 return CLI_SUCCESS;
1552}
1553
1554static int reload_module(void);
1555
1556/*! \brief CLI command manager reload */
1557static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1558{
1559 switch (cmd) {
1560 case CLI_INIT:
1561 e->command = "manager reload";
1562 e->usage =
1563 "Usage: manager reload\n"
1564 " Reloads the manager configuration.\n";
1565 return NULL;
1566 case CLI_GENERATE:
1567 return NULL;
1568 }
1569 if (a->argc > 2) {
1570 return CLI_SHOWUSAGE;
1571 }
1572 reload_module();
1573 return CLI_SUCCESS;
1574}
1575
1576static struct eventqent *advance_event(struct eventqent *e)
1577{
1578 struct eventqent *next;
1579
1581 if ((next = AST_RWLIST_NEXT(e, eq_next))) {
1584 }
1586 return next;
1587}
1588
1589#define GET_HEADER_FIRST_MATCH 0
1590#define GET_HEADER_LAST_MATCH 1
1591#define GET_HEADER_SKIP_EMPTY 2
1592
1593/*!
1594 * \brief Return a matching header value.
1595 *
1596 * \details
1597 * Generic function to return either the first or the last
1598 * matching header from a list of variables, possibly skipping
1599 * empty strings.
1600 *
1601 * \note At the moment there is only one use of this function in
1602 * this file, so we make it static.
1603 *
1604 * \note Never returns NULL.
1605 */
1606static const char *__astman_get_header(const struct message *m, char *var, int mode)
1607{
1608 int x, l = strlen(var);
1609 const char *result = "";
1610
1611 if (!m) {
1612 return result;
1613 }
1614
1615 for (x = 0; x < m->hdrcount; x++) {
1616 const char *h = m->headers[x];
1617 if (!strncasecmp(var, h, l) && h[l] == ':') {
1618 const char *value = h + l + 1;
1619 value = ast_skip_blanks(value); /* ignore leading spaces in the value */
1620 /* found a potential candidate */
1621 if ((mode & GET_HEADER_SKIP_EMPTY) && ast_strlen_zero(value)) {
1622 continue; /* not interesting */
1623 }
1624 if (mode & GET_HEADER_LAST_MATCH) {
1625 result = value; /* record the last match so far */
1626 } else {
1627 return value;
1628 }
1629 }
1630 }
1631
1632 return result;
1633}
1634
1635/*!
1636 * \brief Return the first matching variable from an array.
1637 *
1638 * \note This is the legacy function and is implemented in
1639 * therms of __astman_get_header().
1640 *
1641 * \note Never returns NULL.
1642 */
1643const char *astman_get_header(const struct message *m, char *var)
1644{
1646}
1647
1648/*!
1649 * \brief Append additional headers into the message structure from params.
1650 *
1651 * \note You likely want to initialize m->hdrcount to 0 before calling this.
1652 */
1653static void astman_append_headers(struct message *m, const struct ast_variable *params)
1654{
1655 const struct ast_variable *v;
1656
1657 for (v = params; v && m->hdrcount < ARRAY_LEN(m->headers); v = v->next) {
1658 if (ast_asprintf((char**)&m->headers[m->hdrcount], "%s: %s", v->name, v->value) > -1) {
1659 ++m->hdrcount;
1660 }
1661 }
1662}
1663
1664/*!
1665 * \brief Free headers inside message structure, but not the message structure itself.
1666 */
1667static void astman_free_headers(struct message *m)
1668{
1669 while (m->hdrcount) {
1670 --m->hdrcount;
1671 ast_free((void *) m->headers[m->hdrcount]);
1672 m->headers[m->hdrcount] = NULL;
1673 }
1674}
1675
1676/*!
1677 * \internal
1678 * \brief Process one "Variable:" header value string.
1679 *
1680 * \param head Current list of AMI variables to get new values added.
1681 * \param hdr_val Header value string to process.
1682 *
1683 * \return New variable list head.
1684 */
1685static struct ast_variable *man_do_variable_value(struct ast_variable *head, const char *hdr_val)
1686{
1687 char *parse;
1689 AST_APP_ARG(vars)[64];
1690 );
1691
1692 hdr_val = ast_skip_blanks(hdr_val); /* ignore leading spaces in the value */
1693 parse = ast_strdupa(hdr_val);
1694
1695 /* Break the header value string into name=val pair items. */
1697 if (args.argc) {
1698 int y;
1699
1700 /* Process each name=val pair item. */
1701 for (y = 0; y < args.argc; y++) {
1702 struct ast_variable *cur;
1703 char *var;
1704 char *val;
1705
1706 if (!args.vars[y]) {
1707 continue;
1708 }
1709 var = val = args.vars[y];
1710 strsep(&val, "=");
1711
1712 /* XXX We may wish to trim whitespace from the strings. */
1713 if (!val || ast_strlen_zero(var)) {
1714 continue;
1715 }
1716
1717 /* Create new variable list node and prepend it to the list. */
1718 cur = ast_variable_new(var, val, "");
1719 if (cur) {
1720 cur->next = head;
1721 head = cur;
1722 }
1723 }
1724 }
1725
1726 return head;
1727}
1728
1730{
1732}
1733
1736{
1737 int varlen;
1738 int x;
1739 struct ast_variable *head = NULL;
1740
1741 static const char var_hdr[] = "Variable:";
1742
1743 /* Process all "Variable:" headers. */
1744 varlen = strlen(var_hdr);
1745 for (x = 0; x < m->hdrcount; x++) {
1746 if (strncasecmp(var_hdr, m->headers[x], varlen)) {
1747 continue;
1748 }
1749 head = man_do_variable_value(head, m->headers[x] + varlen);
1750 }
1751
1752 if (order == ORDER_NATURAL) {
1753 head = ast_variables_reverse(head);
1754 }
1755
1756 return head;
1757}
1758
1759/*! \brief access for hooks to send action messages to ami */
1760int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
1761{
1762 const char *action;
1763 int ret = 0;
1764 struct manager_action *act_found;
1765 struct mansession s = {.session = NULL, };
1766 struct message m = { 0 };
1767 char *dup_str;
1768 char *src;
1769 int x = 0;
1770 int curlen;
1771
1772 if (hook == NULL) {
1773 return -1;
1774 }
1775
1776 /* Create our own copy of the AMI action msg string. */
1777 src = dup_str = ast_strdup(msg);
1778 if (!dup_str) {
1779 return -1;
1780 }
1781
1782 /* convert msg string to message struct */
1783 curlen = strlen(src);
1784 for (x = 0; x < curlen; x++) {
1785 int cr; /* set if we have \r */
1786 if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n')
1787 cr = 2; /* Found. Update length to include \r\n */
1788 else if (src[x] == '\n')
1789 cr = 1; /* also accept \n only */
1790 else
1791 continue;
1792 /* don't keep empty lines */
1793 if (x && m.hdrcount < ARRAY_LEN(m.headers)) {
1794 /* ... but trim \r\n and terminate the header string */
1795 src[x] = '\0';
1796 m.headers[m.hdrcount++] = src;
1797 }
1798 x += cr;
1799 curlen -= x; /* remaining size */
1800 src += x; /* update pointer */
1801 x = -1; /* reset loop */
1802 }
1803
1804 action = astman_get_header(&m, "Action");
1805
1806 do {
1807 if (!strcasecmp(action, "login")) {
1808 break;
1809 }
1810
1811 act_found = action_find(action);
1812 if (!act_found) {
1813 break;
1814 }
1815
1816 /*
1817 * we have to simulate a session for this action request
1818 * to be able to pass it down for processing
1819 * This is necessary to meet the previous design of manager.c
1820 */
1821 s.hook = hook;
1822
1823 ret = -1;
1824 ao2_lock(act_found);
1825 if (act_found->registered && act_found->func) {
1826 struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
1827
1828 ao2_unlock(act_found);
1829 /* If the action is in a module it must be running. */
1830 if (!act_found->module || mod_ref) {
1831 ret = act_found->func(&s, &m);
1832 ast_module_unref(mod_ref);
1833 }
1834 } else {
1835 ao2_unlock(act_found);
1836 }
1837 ao2_t_ref(act_found, -1, "done with found action object");
1838 } while (0);
1839
1840 ast_free(dup_str);
1841 return ret;
1842}
1843
1844/*!
1845 * helper function to send a string to the socket.
1846 * Return -1 on error (e.g. buffer full).
1847 */
1848static int send_string(struct mansession *s, char *string)
1849{
1850 struct ast_iostream *stream;
1851 int len, res;
1852
1853 /* It's a result from one of the hook's action invocation */
1854 if (s->hook) {
1855 /*
1856 * to send responses, we're using the same function
1857 * as for receiving events. We call the event "HookResponse"
1858 */
1859 s->hook->helper(EVENT_FLAG_HOOKRESPONSE, "HookResponse", string);
1860 return 0;
1861 }
1862
1863 stream = s->stream ? s->stream : s->session->stream;
1864
1865 len = strlen(string);
1867 res = ast_iostream_write(stream, string, len);
1869
1870 if (res < len) {
1871 s->write_error = 1;
1872 }
1873
1874 return res;
1875}
1876
1877/*!
1878 * \brief thread local buffer for astman_append
1879 *
1880 * \note This can not be defined within the astman_append() function
1881 * because it declares a couple of functions that get used to
1882 * initialize the thread local storage key.
1883 */
1885
1887
1888/*! \brief initial allocated size for the astman_append_buf and astman_send_*_va */
1889#define ASTMAN_APPEND_BUF_INITSIZE 256
1890
1891static void astman_flush(struct mansession *s, struct ast_str *buf)
1892{
1893 if (s->hook || (s->tcptls_session && s->tcptls_session->stream)) {
1895 } else {
1896 ast_verbose("No connection stream in astman_append, should not happen\n");
1897 }
1898}
1899
1900/*!
1901 * utility functions for creating AMI replies
1902 */
1903void astman_append(struct mansession *s, const char *fmt, ...)
1904{
1905 int res;
1906 va_list ap;
1907 struct ast_str *buf;
1908
1910 return;
1911 }
1912
1913 va_start(ap, fmt);
1914 res = ast_str_set_va(&buf, 0, fmt, ap);
1915 va_end(ap);
1916 if (res == AST_DYNSTR_BUILD_FAILED) {
1917 return;
1918 }
1919
1920 if (s->hook || (s->tcptls_session != NULL && s->tcptls_session->stream != NULL)) {
1922 } else {
1923 ast_verbose("No connection stream in astman_append, should not happen\n");
1924 }
1925}
1926
1927/*! \note NOTE: XXX this comment is unclear and possibly wrong.
1928 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
1929 hold the session lock _or_ be running in an action callback (in which case s->session->busy will
1930 be non-zero). In either of these cases, there is no need to lock-protect the session's
1931 fd, since no other output will be sent (events will be queued), and no input will
1932 be read until either the current action finishes or get_input() obtains the session
1933 lock.
1934 */
1935
1936/*! \todo XXX MSG_MOREDATA should go to a header file. */
1937#define MSG_MOREDATA ((char *)astman_send_response)
1938
1939/*! \brief send a response with an optional message,
1940 * and terminate it with an empty line.
1941 * m is used only to grab the 'ActionID' field.
1942 *
1943 * Use the explicit constant MSG_MOREDATA to remove the empty line.
1944 * XXX MSG_MOREDATA should go to a header file.
1945 */
1946static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
1947{
1948 const char *id = astman_get_header(m, "ActionID");
1949 struct ast_str *buf;
1950
1952 if (!buf) {
1953 return;
1954 }
1955
1956 ast_str_set(&buf, 0, "Response: %s\r\n", resp);
1957
1958 if (!ast_strlen_zero(id)) {
1959 ast_str_append(&buf, 0, "ActionID: %s\r\n", id);
1960 }
1961
1962 if (listflag) {
1963 /* Start, complete, cancelled */
1964 ast_str_append(&buf, 0, "EventList: %s\r\n", listflag);
1965 }
1966
1967 if (msg != MSG_MOREDATA) {
1968 if (msg) {
1969 ast_str_append(&buf, 0, "Message: %s\r\n", msg);
1970 }
1971 ast_str_append(&buf, 0, "\r\n");
1972 }
1973
1974 astman_flush(s, buf);
1975}
1976
1977void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
1978{
1979 astman_send_response_full(s, m, resp, msg, NULL);
1980}
1981
1982void astman_send_error(struct mansession *s, const struct message *m, char *error)
1983{
1984 astman_send_response_full(s, m, "Error", error, NULL);
1985}
1986
1987void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt, ...)
1988{
1989 int res;
1990 va_list ap;
1991 struct ast_str *buf;
1992 char *msg;
1993
1995 return;
1996 }
1997
1998 va_start(ap, fmt);
1999 res = ast_str_set_va(&buf, 0, fmt, ap);
2000 va_end(ap);
2001 if (res == AST_DYNSTR_BUILD_FAILED) {
2002 return;
2003 }
2004
2005 /* astman_append will use the same underlying buffer, so copy the message out
2006 * before sending the response */
2007 msg = ast_str_buffer(buf);
2008 if (msg) {
2009 msg = ast_strdupa(msg);
2010 }
2011 astman_send_response_full(s, m, "Error", msg, NULL);
2012}
2013
2014void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
2015{
2016 astman_send_response_full(s, m, "Success", msg, NULL);
2017}
2018
2019static void astman_start_ack(struct mansession *s, const struct message *m)
2020{
2021 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
2022}
2023
2024void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
2025{
2026 astman_send_response_full(s, m, "Success", msg, listflag);
2027}
2028
2029static struct ast_str *astman_send_list_complete_start_common(struct mansession *s, const struct message *m, const char *event_name, int count)
2030{
2031 const char *id = astman_get_header(m, "ActionID");
2032 struct ast_str *buf;
2033
2035 if (!buf) {
2036 return NULL;
2037 }
2038
2039 ast_str_set(&buf, 0, "Event: %s\r\n", event_name);
2040 if (!ast_strlen_zero(id)) {
2041 ast_str_append(&buf, 0, "ActionID: %s\r\n", id);
2042 }
2043 ast_str_append(&buf, 0,
2044 "EventList: Complete\r\n"
2045 "ListItems: %d\r\n",
2046 count);
2047
2048 return buf;
2049}
2050
2051static void astman_send_list_complete(struct mansession *s, const struct message *m, const char *event_name, int count)
2052{
2053 struct ast_str *buf = astman_send_list_complete_start_common(s, m, event_name, count);
2054 if (buf) {
2055 ast_str_append(&buf, 0, "\r\n");
2056 astman_flush(s, buf);
2057 }
2058}
2059
2060void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
2061{
2062 struct ast_str *buf = astman_send_list_complete_start_common(s, m, event_name, count);
2063 if (buf) {
2064 astman_flush(s, buf);
2065 }
2066}
2067
2069{
2070 astman_append(s, "\r\n");
2071}
2072
2073/*! \brief Lock the 'mansession' structure. */
2074static void mansession_lock(struct mansession *s)
2075{
2076 ast_mutex_lock(&s->lock);
2077}
2078
2079/*! \brief Unlock the 'mansession' structure. */
2080static void mansession_unlock(struct mansession *s)
2081{
2083}
2084
2085/*! \brief
2086 Rather than braindead on,off this now can also accept a specific int mask value
2087 or a ',' delim list of mask strings (the same as manager.conf) -anthm
2088*/
2089static int set_eventmask(struct mansession *s, const char *eventmask)
2090{
2091 int maskint = strings_to_mask(eventmask);
2092
2093 ao2_lock(s->session);
2094 if (maskint >= 0) {
2095 s->session->send_events = maskint;
2096 }
2097 ao2_unlock(s->session);
2098
2099 return maskint;
2100}
2101
2103{
2106}
2107
2108static void report_invalid_user(const struct mansession *s, const char *username)
2109{
2110 char session_id[32];
2111 struct ast_security_event_inval_acct_id inval_acct_id = {
2114 .common.service = "AMI",
2115 .common.account_id = username,
2116 .common.session_tv = &s->session->sessionstart_tv,
2117 .common.local_addr = {
2118 .addr = &s->tcptls_session->parent->local_address,
2119 .transport = mansession_get_transport(s),
2120 },
2121 .common.remote_addr = {
2122 .addr = &s->session->addr,
2123 .transport = mansession_get_transport(s),
2124 },
2125 .common.session_id = session_id,
2126 };
2127
2128 snprintf(session_id, sizeof(session_id), "%p", s);
2129
2130 ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
2131}
2132
2133static void report_failed_acl(const struct mansession *s, const char *username)
2134{
2135 char session_id[32];
2136 struct ast_security_event_failed_acl failed_acl_event = {
2138 .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
2139 .common.service = "AMI",
2140 .common.account_id = username,
2141 .common.session_tv = &s->session->sessionstart_tv,
2142 .common.local_addr = {
2143 .addr = &s->tcptls_session->parent->local_address,
2144 .transport = mansession_get_transport(s),
2145 },
2146 .common.remote_addr = {
2147 .addr = &s->session->addr,
2148 .transport = mansession_get_transport(s),
2149 },
2150 .common.session_id = session_id,
2151 };
2152
2153 snprintf(session_id, sizeof(session_id), "%p", s->session);
2154
2155 ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
2156}
2157
2158static void report_inval_password(const struct mansession *s, const char *username)
2159{
2160 char session_id[32];
2161 struct ast_security_event_inval_password inval_password = {
2164 .common.service = "AMI",
2165 .common.account_id = username,
2166 .common.session_tv = &s->session->sessionstart_tv,
2167 .common.local_addr = {
2168 .addr = &s->tcptls_session->parent->local_address,
2169 .transport = mansession_get_transport(s),
2170 },
2171 .common.remote_addr = {
2172 .addr = &s->session->addr,
2173 .transport = mansession_get_transport(s),
2174 },
2175 .common.session_id = session_id,
2176 };
2177
2178 snprintf(session_id, sizeof(session_id), "%p", s->session);
2179
2180 ast_security_event_report(AST_SEC_EVT(&inval_password));
2181}
2182
2183static void report_auth_success(const struct mansession *s)
2184{
2185 char session_id[32];
2186 struct ast_security_event_successful_auth successful_auth = {
2189 .common.service = "AMI",
2190 .common.account_id = s->session->username,
2191 .common.session_tv = &s->session->sessionstart_tv,
2192 .common.local_addr = {
2193 .addr = &s->tcptls_session->parent->local_address,
2194 .transport = mansession_get_transport(s),
2195 },
2196 .common.remote_addr = {
2197 .addr = &s->session->addr,
2198 .transport = mansession_get_transport(s),
2199 },
2200 .common.session_id = session_id,
2201 };
2202
2203 snprintf(session_id, sizeof(session_id), "%p", s->session);
2204
2205 ast_security_event_report(AST_SEC_EVT(&successful_auth));
2206}
2207
2208static void report_req_not_allowed(const struct mansession *s, const char *action)
2209{
2210 char session_id[32];
2211 char request_type[64];
2212 struct ast_security_event_req_not_allowed req_not_allowed = {
2215 .common.service = "AMI",
2216 .common.account_id = s->session->username,
2217 .common.session_tv = &s->session->sessionstart_tv,
2218 .common.local_addr = {
2219 .addr = &s->tcptls_session->parent->local_address,
2220 .transport = mansession_get_transport(s),
2221 },
2222 .common.remote_addr = {
2223 .addr = &s->session->addr,
2224 .transport = mansession_get_transport(s),
2225 },
2226 .common.session_id = session_id,
2227
2228 .request_type = request_type,
2229 };
2230
2231 snprintf(session_id, sizeof(session_id), "%p", s->session);
2232 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2233
2234 ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
2235}
2236
2237static void report_req_bad_format(const struct mansession *s, const char *action)
2238{
2239 char session_id[32];
2240 char request_type[64];
2241 struct ast_security_event_req_bad_format req_bad_format = {
2244 .common.service = "AMI",
2245 .common.account_id = s->session->username,
2246 .common.session_tv = &s->session->sessionstart_tv,
2247 .common.local_addr = {
2248 .addr = &s->tcptls_session->parent->local_address,
2249 .transport = mansession_get_transport(s),
2250 },
2251 .common.remote_addr = {
2252 .addr = &s->session->addr,
2253 .transport = mansession_get_transport(s),
2254 },
2255 .common.session_id = session_id,
2256
2257 .request_type = request_type,
2258 };
2259
2260 snprintf(session_id, sizeof(session_id), "%p", s->session);
2261 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2262
2263 ast_security_event_report(AST_SEC_EVT(&req_bad_format));
2264}
2265
2267 const char *response, const char *expected_response)
2268{
2269 char session_id[32];
2270 struct ast_security_event_chal_resp_failed chal_resp_failed = {
2273 .common.service = "AMI",
2274 .common.account_id = s->session->username,
2275 .common.session_tv = &s->session->sessionstart_tv,
2276 .common.local_addr = {
2277 .addr = &s->tcptls_session->parent->local_address,
2278 .transport = mansession_get_transport(s),
2279 },
2280 .common.remote_addr = {
2281 .addr = &s->session->addr,
2282 .transport = mansession_get_transport(s),
2283 },
2284 .common.session_id = session_id,
2285
2286 .challenge = s->session->challenge,
2287 .response = response,
2288 .expected_response = expected_response,
2289 };
2290
2291 snprintf(session_id, sizeof(session_id), "%p", s->session);
2292
2293 ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
2294}
2295
2296static void report_session_limit(const struct mansession *s)
2297{
2298 char session_id[32];
2300 .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
2302 .common.service = "AMI",
2303 .common.account_id = s->session->username,
2304 .common.session_tv = &s->session->sessionstart_tv,
2305 .common.local_addr = {
2306 .addr = &s->tcptls_session->parent->local_address,
2307 .transport = mansession_get_transport(s),
2308 },
2309 .common.remote_addr = {
2310 .addr = &s->session->addr,
2311 .transport = mansession_get_transport(s),
2312 },
2313 .common.session_id = session_id,
2314 };
2315
2316 snprintf(session_id, sizeof(session_id), "%p", s->session);
2317
2319}
2320
2321/*
2322 * Here we start with action_ handlers for AMI actions,
2323 * and the internal functions used by them.
2324 * Generally, the handlers are called action_foo()
2325 */
2326
2327/* helper function for action_login() */
2328static int authenticate(struct mansession *s, const struct message *m)
2329{
2330 const char *username = astman_get_header(m, "Username");
2331 const char *password = astman_get_header(m, "Secret");
2332 int error = -1;
2333 struct ast_manager_user *user = NULL;
2334 regex_t *regex_filter;
2335 struct ao2_iterator filter_iter;
2336
2337 if (ast_strlen_zero(username)) { /* missing username */
2338 return -1;
2339 }
2340
2341 /* locate user in locked state */
2343
2344 if (!(user = get_manager_by_name_locked(username))) {
2345 report_invalid_user(s, username);
2346 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2347 } else if (user->acl && (ast_apply_acl(user->acl, &s->session->addr, "Manager User ACL: ") == AST_SENSE_DENY)) {
2348 report_failed_acl(s, username);
2349 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2350 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
2351 const char *key = astman_get_header(m, "Key");
2352 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
2353 int x;
2354 int len = 0;
2355 char md5key[256] = "";
2356 struct MD5Context md5;
2357 unsigned char digest[16];
2358
2359 MD5Init(&md5);
2360 MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
2361 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
2362 MD5Final(digest, &md5);
2363 for (x = 0; x < 16; x++)
2364 len += sprintf(md5key + len, "%02hhx", digest[x]);
2365 if (!strcmp(md5key, key)) {
2366 error = 0;
2367 } else {
2368 report_failed_challenge_response(s, key, md5key);
2369 }
2370 } else {
2371 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
2372 S_OR(s->session->challenge, ""));
2373 }
2374 } else if (user->secret) {
2375 if (!strcmp(password, user->secret)) {
2376 error = 0;
2377 } else {
2378 report_inval_password(s, username);
2379 }
2380 }
2381
2382 if (error) {
2383 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2385 return -1;
2386 }
2387
2388 /* auth complete */
2389
2390 /* All of the user parameters are copied to the session so that in the event
2391 * of a reload and a configuration change, the session parameters are not
2392 * changed. */
2393 ast_copy_string(s->session->username, username, sizeof(s->session->username));
2394 s->session->readperm = user->readperm;
2395 s->session->writeperm = user->writeperm;
2396 s->session->writetimeout = user->writetimeout;
2397 if (user->chanvars) {
2398 s->session->chanvars = ast_variables_dup(user->chanvars);
2399 }
2400
2401 filter_iter = ao2_iterator_init(user->includefilters, 0);
2402 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
2403 ao2_t_link(s->session->includefilters, regex_filter, "add include user filter to session");
2404 ao2_t_ref(regex_filter, -1, "remove iterator ref");
2405 }
2406 ao2_iterator_destroy(&filter_iter);
2407
2408 filter_iter = ao2_iterator_init(user->excludefilters, 0);
2409 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
2410 ao2_t_link(s->session->excludefilters, regex_filter, "add exclude user filter to session");
2411 ao2_t_ref(regex_filter, -1, "remove iterator ref");
2412 }
2413 ao2_iterator_destroy(&filter_iter);
2414
2415 s->session->sessionstart = time(NULL);
2417 set_eventmask(s, astman_get_header(m, "Events"));
2418
2420
2422 return 0;
2423}
2424
2425static int action_ping(struct mansession *s, const struct message *m)
2426{
2427 const char *actionid = astman_get_header(m, "ActionID");
2428 struct timeval now = ast_tvnow();
2429
2430 astman_append(s, "Response: Success\r\n");
2431 if (!ast_strlen_zero(actionid)){
2432 astman_append(s, "ActionID: %s\r\n", actionid);
2433 }
2435 s,
2436 "Ping: Pong\r\n"
2437 "Timestamp: %ld.%06lu\r\n"
2438 "\r\n",
2439 (long) now.tv_sec, (unsigned long) now.tv_usec);
2440 return 0;
2441}
2442
2443void astman_live_dangerously(int new_live_dangerously)
2444{
2445 if (new_live_dangerously && !live_dangerously)
2446 {
2447 ast_log(LOG_WARNING, "Manager Configuration load protection disabled.\n");
2448 }
2449
2450 if (!new_live_dangerously && live_dangerously)
2451 {
2452 ast_log(LOG_NOTICE, "Manager Configuration load protection enabled.\n");
2453 }
2454 live_dangerously = new_live_dangerously;
2455}
2456
2457/**
2458 * \brief Check if a file is restricted or not
2459 *
2460 * \return 0 on success
2461 * \return 1 on restricted file
2462 * \return -1 on failure
2463 */
2464static int is_restricted_file(const char *filename)
2465{
2466 char *stripped_filename;
2467 RAII_VAR(char *, path, NULL, ast_free);
2468 RAII_VAR(char *, real_path, NULL, ast_std_free);
2469
2470 if (live_dangerously) {
2471 return 0;
2472 }
2473
2474 stripped_filename = ast_strip(ast_strdupa(filename));
2475
2476 /* If the file path starts with '/', don't prepend ast_config_AST_CONFIG_DIR */
2477 if (stripped_filename[0] == '/') {
2478 real_path = realpath(stripped_filename, NULL);
2479 } else {
2480 if (ast_asprintf(&path, "%s/%s", ast_config_AST_CONFIG_DIR, stripped_filename) == -1) {
2481 return -1;
2482 }
2483 real_path = realpath(path, NULL);
2484 }
2485
2486 if (!real_path) {
2487 return -1;
2488 }
2489
2490 if (!ast_begins_with(real_path, ast_config_AST_CONFIG_DIR)) {
2491 return 1;
2492 }
2493
2494 return 0;
2495}
2496
2497static int action_getconfig(struct mansession *s, const struct message *m)
2498{
2499 struct ast_config *cfg;
2500 const char *fn = astman_get_header(m, "Filename");
2501 const char *category = astman_get_header(m, "Category");
2502 const char *filter = astman_get_header(m, "Filter");
2503 const char *category_name;
2504 int catcount = 0;
2505 int lineno = 0;
2506 int ret = 0;
2507 struct ast_category *cur_category = NULL;
2508 struct ast_variable *v;
2509 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2510
2511 if (ast_strlen_zero(fn)) {
2512 astman_send_error(s, m, "Filename not specified");
2513 return 0;
2514 }
2515
2516 ret = is_restricted_file(fn);
2517 if (ret == 1) {
2518 astman_send_error(s, m, "File requires escalated privileges");
2519 return 0;
2520 } else if (ret == -1) {
2521 astman_send_error(s, m, "Config file not found");
2522 return 0;
2523 }
2524
2525 cfg = ast_config_load2(fn, "manager", config_flags);
2526 if (cfg == CONFIG_STATUS_FILEMISSING) {
2527 astman_send_error(s, m, "Config file not found");
2528 return 0;
2529 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2530 astman_send_error(s, m, "Config file has invalid format");
2531 return 0;
2532 }
2533
2534 astman_start_ack(s, m);
2535 while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
2536 struct ast_str *templates;
2537
2538 category_name = ast_category_get_name(cur_category);
2539 lineno = 0;
2540 astman_append(s, "Category-%06d: %s\r\n", catcount, category_name);
2541
2542 if (ast_category_is_template(cur_category)) {
2543 astman_append(s, "IsTemplate-%06d: %d\r\n", catcount, 1);
2544 }
2545
2546 if ((templates = ast_category_get_templates(cur_category))
2547 && ast_str_strlen(templates) > 0) {
2548 astman_append(s, "Templates-%06d: %s\r\n", catcount, ast_str_buffer(templates));
2550 }
2551
2552 for (v = ast_category_first(cur_category); v; v = v->next) {
2553 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
2554 }
2555
2556 catcount++;
2557 }
2558
2559 if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2560 astman_append(s, "No categories found\r\n");
2561 }
2562
2563 ast_config_destroy(cfg);
2564 astman_append(s, "\r\n");
2565
2566 return 0;
2567}
2568
2569static int action_listcategories(struct mansession *s, const struct message *m)
2570{
2571 struct ast_config *cfg;
2572 const char *fn = astman_get_header(m, "Filename");
2573 const char *match = astman_get_header(m, "Match");
2574 struct ast_category *category = NULL;
2575 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2576 int catcount = 0;
2577 int ret = 0;
2578
2579 if (ast_strlen_zero(fn)) {
2580 astman_send_error(s, m, "Filename not specified");
2581 return 0;
2582 }
2583
2584 ret = is_restricted_file(fn);
2585 if (ret == 1) {
2586 astman_send_error(s, m, "File requires escalated privileges");
2587 return 0;
2588 } else if (ret == -1) {
2589 astman_send_error(s, m, "Config file not found");
2590 return 0;
2591 }
2592
2593 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2594 astman_send_error(s, m, "Config file not found");
2595 return 0;
2596 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2597 astman_send_error(s, m, "Config file has invalid format");
2598 return 0;
2599 }
2600
2601 astman_start_ack(s, m);
2602 while ((category = ast_category_browse_filtered(cfg, NULL, category, match))) {
2603 astman_append(s, "Category-%06d: %s\r\n", catcount, ast_category_get_name(category));
2604 catcount++;
2605 }
2606
2607 if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2608 astman_append(s, "Error: no categories found\r\n");
2609 }
2610
2611 ast_config_destroy(cfg);
2612 astman_append(s, "\r\n");
2613
2614 return 0;
2615}
2616
2617/*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
2618static void json_escape(char *out, const char *in)
2619{
2620 for (; *in; in++) {
2621 if (*in == '\\' || *in == '\"') {
2622 *out++ = '\\';
2623 }
2624 *out++ = *in;
2625 }
2626 *out = '\0';
2627}
2628
2629/*!
2630 * \internal
2631 * \brief Append a JSON escaped string to the manager stream.
2632 *
2633 * \param s AMI stream to append a string.
2634 * \param str String to append to the stream after JSON escaping it.
2635 */
2636static void astman_append_json(struct mansession *s, const char *str)
2637{
2638 char *buf;
2639
2640 buf = ast_alloca(2 * strlen(str) + 1);
2642 astman_append(s, "%s", buf);
2643}
2644
2645static int action_getconfigjson(struct mansession *s, const struct message *m)
2646{
2647 struct ast_config *cfg;
2648 const char *fn = astman_get_header(m, "Filename");
2649 const char *filter = astman_get_header(m, "Filter");
2650 const char *category = astman_get_header(m, "Category");
2651 struct ast_category *cur_category = NULL;
2652 const char *category_name;
2653 struct ast_variable *v;
2654 int comma1 = 0;
2655 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2656
2657 if (ast_strlen_zero(fn)) {
2658 astman_send_error(s, m, "Filename not specified");
2659 return 0;
2660 }
2661
2662 if (is_restricted_file(fn)) {
2663 astman_send_error(s, m, "File requires escalated privileges");
2664 return 0;
2665 }
2666
2667 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2668 astman_send_error(s, m, "Config file not found");
2669 return 0;
2670 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2671 astman_send_error(s, m, "Config file has invalid format");
2672 return 0;
2673 }
2674
2675 astman_start_ack(s, m);
2676 astman_append(s, "JSON: {");
2677 while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
2678 int comma2 = 0;
2679 struct ast_str *templates;
2680
2681 category_name = ast_category_get_name(cur_category);
2682 astman_append(s, "%s\"", comma1 ? "," : "");
2683 astman_append_json(s, category_name);
2684 astman_append(s, "\":{");
2685 comma1 = 1;
2686
2687 if (ast_category_is_template(cur_category)) {
2688 astman_append(s, "\"istemplate\":1");
2689 comma2 = 1;
2690 }
2691
2692 if ((templates = ast_category_get_templates(cur_category))
2693 && ast_str_strlen(templates) > 0) {
2694 astman_append(s, "%s", comma2 ? "," : "");
2695 astman_append(s, "\"templates\":\"%s\"", ast_str_buffer(templates));
2697 comma2 = 1;
2698 }
2699
2700 for (v = ast_category_first(cur_category); v; v = v->next) {
2701 astman_append(s, "%s\"", comma2 ? "," : "");
2702 astman_append_json(s, v->name);
2703 astman_append(s, "\":\"");
2705 astman_append(s, "\"");
2706 comma2 = 1;
2707 }
2708
2709 astman_append(s, "}");
2710 }
2711 astman_append(s, "}\r\n\r\n");
2712
2713 ast_config_destroy(cfg);
2714
2715 return 0;
2716}
2717
2718/*! \brief helper function for action_updateconfig */
2719static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
2720{
2721 int x;
2722 char hdr[40];
2723 const char *action, *cat, *var, *value, *match, *line, *options;
2724 struct ast_variable *v;
2725 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
2726 enum error_type result = 0;
2727
2728 for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
2729 unsigned int object = 0;
2730 char *dupoptions;
2731 int allowdups = 0;
2732 int istemplate = 0;
2733 int ignoreerror = 0;
2734 RAII_VAR(char *, inherit, NULL, ast_free);
2735 RAII_VAR(char *, catfilter, NULL, ast_free);
2736 char *token;
2737 int foundvar = 0;
2738 int foundcat = 0;
2739 struct ast_category *category = NULL;
2740
2741 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
2742 action = astman_get_header(m, hdr);
2743 if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
2744 break; /* this could cause problems if actions come in misnumbered */
2745
2746 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
2747 cat = astman_get_header(m, hdr);
2748 if (ast_strlen_zero(cat)) { /* every action needs a category */
2750 break;
2751 }
2752
2753 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
2754 var = astman_get_header(m, hdr);
2755
2756 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
2757 value = astman_get_header(m, hdr);
2758
2759 if (!ast_strlen_zero(value) && *value == '>') {
2760 object = 1;
2761 value++;
2762 }
2763
2764 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
2765 match = astman_get_header(m, hdr);
2766
2767 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
2768 line = astman_get_header(m, hdr);
2769
2770 snprintf(hdr, sizeof(hdr), "Options-%06d", x);
2771 options = astman_get_header(m, hdr);
2772 if (!ast_strlen_zero(options)) {
2773 char copy[strlen(options) + 1];
2774 strcpy(copy, options); /* safe */
2775 dupoptions = copy;
2776 while ((token = ast_strsep(&dupoptions, ',', AST_STRSEP_STRIP))) {
2777 if (!strcasecmp("allowdups", token)) {
2778 allowdups = 1;
2779 continue;
2780 }
2781 if (!strcasecmp("template", token)) {
2782 istemplate = 1;
2783 continue;
2784 }
2785 if (!strcasecmp("ignoreerror", token)) {
2786 ignoreerror = 1;
2787 continue;
2788 }
2789 if (ast_begins_with(token, "inherit")) {
2790 char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2791 c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2792 if (c) {
2793 inherit = ast_strdup(c);
2794 }
2795 continue;
2796 }
2797 if (ast_begins_with(token, "catfilter")) {
2798 char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2799 c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2800 if (c) {
2801 catfilter = ast_strdup(c);
2802 }
2803 continue;
2804 }
2805 }
2806 }
2807
2808 if (!strcasecmp(action, "newcat")) {
2809 struct ast_category *template;
2810 char *tmpl_name = NULL;
2811
2812 if (!allowdups) {
2813 if (ast_category_get(cfg, cat, "TEMPLATES=include")) {
2814 if (ignoreerror) {
2815 continue;
2816 } else {
2817 result = FAILURE_NEWCAT; /* already exist */
2818 break;
2819 }
2820 }
2821 }
2822
2823 if (istemplate) {
2824 category = ast_category_new_template(cat, dfn, -1);
2825 } else {
2826 category = ast_category_new(cat, dfn, -1);
2827 }
2828
2829 if (!category) {
2831 break;
2832 }
2833
2834 if (inherit) {
2835 while ((tmpl_name = ast_strsep(&inherit, ',', AST_STRSEP_STRIP))) {
2836 if ((template = ast_category_get(cfg, tmpl_name, "TEMPLATES=restrict"))) {
2837 if (ast_category_inherit(category, template)) {
2839 break;
2840 }
2841 } else {
2842 ast_category_destroy(category);
2843 category = NULL;
2844 result = FAILURE_TEMPLATE; /* template not found */
2845 break;
2846 }
2847 }
2848 }
2849
2850 if (category != NULL) {
2851 if (ast_strlen_zero(match)) {
2852 ast_category_append(cfg, category);
2853 } else {
2854 if (ast_category_insert(cfg, category, match)) {
2855 ast_category_destroy(category);
2857 break;
2858 }
2859 }
2860 }
2861 } else if (!strcasecmp(action, "renamecat")) {
2862 if (ast_strlen_zero(value)) {
2864 break;
2865 }
2866
2867 foundcat = 0;
2868 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2869 ast_category_rename(category, value);
2870 foundcat = 1;
2871 }
2872
2873 if (!foundcat) {
2875 break;
2876 }
2877 } else if (!strcasecmp(action, "delcat")) {
2878 foundcat = 0;
2879 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2880 category = ast_category_delete(cfg, category);
2881 foundcat = 1;
2882 }
2883
2884 if (!foundcat && !ignoreerror) {
2886 break;
2887 }
2888 } else if (!strcasecmp(action, "emptycat")) {
2889 foundcat = 0;
2890 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2891 ast_category_empty(category);
2892 foundcat = 1;
2893 }
2894
2895 if (!foundcat) {
2897 break;
2898 }
2899 } else if (!strcasecmp(action, "update")) {
2900 if (ast_strlen_zero(var)) {
2902 break;
2903 }
2904
2905 foundcat = 0;
2906 foundvar = 0;
2907 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2908 if (!ast_variable_update(category, var, value, match, object)) {
2909 foundvar = 1;
2910 }
2911 foundcat = 1;
2912 }
2913
2914 if (!foundcat) {
2916 break;
2917 }
2918
2919 if (!foundvar) {
2921 break;
2922 }
2923 } else if (!strcasecmp(action, "delete")) {
2924 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
2926 break;
2927 }
2928
2929 foundcat = 0;
2930 foundvar = 0;
2931 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2932 if (!ast_variable_delete(category, var, match, line)) {
2933 foundvar = 1;
2934 }
2935 foundcat = 1;
2936 }
2937
2938 if (!foundcat) {
2940 break;
2941 }
2942
2943 if (!foundvar && !ignoreerror) {
2945 break;
2946 }
2947 } else if (!strcasecmp(action, "append")) {
2948 if (ast_strlen_zero(var)) {
2950 break;
2951 }
2952
2953 foundcat = 0;
2954 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2955 if (!(v = ast_variable_new(var, value, dfn))) {
2957 break;
2958 }
2959 if (object || (match && !strcasecmp(match, "object"))) {
2960 v->object = 1;
2961 }
2962 ast_variable_append(category, v);
2963 foundcat = 1;
2964 }
2965
2966 if (!foundcat) {
2968 break;
2969 }
2970 } else if (!strcasecmp(action, "insert")) {
2971 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
2973 break;
2974 }
2975
2976 foundcat = 0;
2977 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2978 if (!(v = ast_variable_new(var, value, dfn))) {
2980 break;
2981 }
2982 ast_variable_insert(category, v, line);
2983 foundcat = 1;
2984 }
2985
2986 if (!foundcat) {
2988 break;
2989 }
2990 }
2991 else {
2992 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
2994 break;
2995 }
2996 }
2997 ast_free(str1);
2998 ast_free(str2);
2999 return result;
3000}
3001
3002static int action_updateconfig(struct mansession *s, const struct message *m)
3003{
3004 struct ast_config *cfg;
3005 const char *sfn = astman_get_header(m, "SrcFilename");
3006 const char *dfn = astman_get_header(m, "DstFilename");
3007 int res;
3008 const char *rld = astman_get_header(m, "Reload");
3009 int preserve_effective_context = CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT;
3010 const char *preserve_effective_context_string = astman_get_header(m, "PreserveEffectiveContext");
3011 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
3012 enum error_type result;
3013
3014 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
3015 astman_send_error(s, m, "Filename not specified");
3016 return 0;
3017 }
3018 if (is_restricted_file(sfn) || is_restricted_file(dfn)) {
3019 astman_send_error(s, m, "File requires escalated privileges");
3020 return 0;
3021 }
3022 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
3023 astman_send_error(s, m, "Config file not found");
3024 return 0;
3025 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
3026 astman_send_error(s, m, "Config file has invalid format");
3027 return 0;
3028 }
3029 result = handle_updates(s, m, cfg, dfn);
3030 if (!result) {
3031 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
3032 if (!ast_strlen_zero(preserve_effective_context_string) && !ast_true(preserve_effective_context_string)) {
3033 preserve_effective_context = CONFIG_SAVE_FLAG_NONE;
3034 }
3035 res = ast_config_text_file_save2(dfn, cfg, "Manager", preserve_effective_context);
3036 ast_config_destroy(cfg);
3037 if (res) {
3038 astman_send_error(s, m, "Save of config failed");
3039 return 0;
3040 }
3041 astman_send_ack(s, m, NULL);
3042 if (!ast_strlen_zero(rld)) {
3043 if (ast_true(rld)) {
3044 ast_module_reload(NULL); /* Reload everything */
3045 } else if (!ast_false(rld)) {
3046 ast_module_reload(rld); /* Reload the specific module */
3047 }
3048 }
3049 } else {
3050 ast_config_destroy(cfg);
3051 switch(result) {
3052 case UNKNOWN_ACTION:
3053 astman_send_error(s, m, "Unknown action command");
3054 break;
3055 case UNKNOWN_CATEGORY:
3056 astman_send_error(s, m, "Given category does not exist");
3057 break;
3059 astman_send_error(s, m, "Category not specified");
3060 break;
3062 astman_send_error(s, m, "Problem with category, value, or line (if required)");
3063 break;
3064 case FAILURE_ALLOCATION:
3065 astman_send_error(s, m, "Memory allocation failure, this should not happen");
3066 break;
3067 case FAILURE_NEWCAT:
3068 astman_send_error(s, m, "Create category did not complete successfully");
3069 break;
3070 case FAILURE_DELCAT:
3071 astman_send_error(s, m, "Delete category did not complete successfully");
3072 break;
3073 case FAILURE_EMPTYCAT:
3074 astman_send_error(s, m, "Empty category did not complete successfully");
3075 break;
3076 case FAILURE_UPDATE:
3077 astman_send_error(s, m, "Update did not complete successfully");
3078 break;
3079 case FAILURE_DELETE:
3080 astman_send_error(s, m, "Delete did not complete successfully");
3081 break;
3082 case FAILURE_APPEND:
3083 astman_send_error(s, m, "Append did not complete successfully");
3084 break;
3085 case FAILURE_TEMPLATE:
3086 astman_send_error(s, m, "Template category not found");
3087 break;
3088 }
3089 }
3090 return 0;
3091}
3092
3093static int action_createconfig(struct mansession *s, const struct message *m)
3094{
3095 int fd;
3096 const char *fn = astman_get_header(m, "Filename");
3097 char *stripped_filename;
3098 RAII_VAR(char *, filepath, NULL, ast_free);
3099 RAII_VAR(char *, real_dir, NULL, ast_std_free);
3100 RAII_VAR(char *, real_path, NULL, ast_free);
3101 char *filename;
3102
3103 if (ast_strlen_zero(fn)) {
3104 astman_send_error(s, m, "Filename not specified");
3105 return 0;
3106 }
3107
3108 stripped_filename = ast_strip(ast_strdupa(fn));
3109
3110 /* If the file name is relative, prepend ast_config_AST_CONFIG_DIR */
3111 if (stripped_filename[0] != '/') {
3112 if (ast_asprintf(&filepath, "%s/%s", ast_config_AST_CONFIG_DIR, stripped_filename) == -1) {
3113 return -1;
3114 }
3115 } else {
3116 filepath = ast_strdup(stripped_filename);
3117 }
3118
3119 /*
3120 * We can't call is_restricted_file() here because it uses realpath() and...
3121 *
3122 * realpath() and other functions that canonicalize paths won't work with
3123 * a filename that doesn't exist, so we need to separate the directory
3124 * from the filename and canonicalize the directory first. We have to do
3125 * the separation manually because dirname() and basename() aren't all
3126 * that friendly to multi-threaded programs and there are different
3127 * versions of basename for glibc and POSIX.
3128 */
3129
3130 filename = strrchr(filepath, '/');
3131 if (!filename) {
3132 astman_send_error(s, m, "Filename is invalid");
3133 return 0;
3134 }
3135 *filename = '\0';
3136 filename++;
3137
3138 /* filepath just has the directory now so canonicalize it. */
3139 real_dir = realpath(filepath, NULL);
3140 if (ast_strlen_zero(real_dir)) {
3141 astman_send_error(s, m, strerror(errno));
3142 return 0;
3143 }
3144
3145 /* Check if the directory is restricted. */
3147 astman_send_error(s, m, "File requires escalated privileges");
3148 return 0;
3149 }
3150
3151 /* Create the final file path. */
3152 if (ast_asprintf(&real_path, "%s/%s", real_dir, filename) == -1) {
3153 astman_send_error(s, m, strerror(errno));
3154 return -1;
3155 }
3156
3157 if ((fd = open(real_path, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
3158 close(fd);
3159 astman_send_ack(s, m, "New configuration file created successfully");
3160 } else {
3161 astman_send_error(s, m, strerror(errno));
3162 }
3163
3164 return 0;
3165}
3166
3167static int action_waitevent(struct mansession *s, const struct message *m)
3168{
3169 const char *timeouts = astman_get_header(m, "Timeout");
3170 int timeout = -1;
3171 int x;
3172 int needexit = 0;
3173 const char *id = astman_get_header(m, "ActionID");
3174 char idText[256];
3175
3176 if (!ast_strlen_zero(id)) {
3177 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
3178 } else {
3179 idText[0] = '\0';
3180 }
3181
3182 if (!ast_strlen_zero(timeouts)) {
3183 sscanf(timeouts, "%30i", &timeout);
3184 if (timeout < -1) {
3185 timeout = -1;
3186 }
3187 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
3188 }
3189
3192 pthread_kill(s->session->waiting_thread, SIGURG);
3193 }
3195
3196 ao2_lock(s->session);
3197
3198 if (s->session->managerid) { /* AMI-over-HTTP session */
3199 /*
3200 * Make sure the timeout is within the expire time of the session,
3201 * as the client will likely abort the request if it does not see
3202 * data coming after some amount of time.
3203 */
3204 time_t now = time(NULL);
3205 int max = s->session->sessiontimeout - now - 10;
3206
3207 if (max < 0) { /* We are already late. Strange but possible. */
3208 max = 0;
3209 }
3210 if (timeout < 0 || timeout > max) {
3211 timeout = max;
3212 }
3213 if (!s->session->send_events) { /* make sure we record events */
3214 s->session->send_events = -1;
3215 }
3216 }
3217 ao2_unlock(s->session);
3218
3220 s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
3222 ast_debug(1, "Starting waiting for an event!\n");
3223
3224 for (x = 0; x < timeout || timeout < 0; x++) {
3225 ao2_lock(s->session);
3226 if (AST_RWLIST_NEXT(s->session->last_ev, eq_next)) {
3227 needexit = 1;
3228 }
3229 if (s->session->needdestroy) {
3230 needexit = 1;
3231 }
3232 ao2_unlock(s->session);
3233 /* We can have multiple HTTP session point to the same mansession entry.
3234 * The way we deal with it is not very nice: newcomers kick out the previous
3235 * HTTP session. XXX this needs to be improved.
3236 */
3238 if (s->session->waiting_thread != pthread_self()) {
3239 needexit = 1;
3240 }
3242 if (needexit) {
3243 break;
3244 }
3245 if (s->session->managerid == 0) { /* AMI session */
3247 break;
3248 }
3249 } else { /* HTTP session */
3250 sleep(1);
3251 }
3252 }
3253 ast_debug(1, "Finished waiting for an event!\n");
3254
3256 if (s->session->waiting_thread == pthread_self()) {
3257 struct eventqent *eqe = s->session->last_ev;
3258
3261
3262 ao2_lock(s->session);
3263 astman_send_response(s, m, "Success", "Waiting for Event completed.");
3264 while ((eqe = advance_event(eqe))) {
3265 if (((s->session->readperm & eqe->category) == eqe->category)
3266 && ((s->session->send_events & eqe->category) == eqe->category)
3268 astman_append(s, "%s", eqe->eventdata);
3269 }
3270 s->session->last_ev = eqe;
3271 }
3272 astman_append(s,
3273 "Event: WaitEventComplete\r\n"
3274 "%s"
3275 "\r\n", idText);
3276 ao2_unlock(s->session);
3277 } else {
3279 ast_debug(1, "Abandoning event request!\n");
3280 }
3281
3282 return 0;
3283}
3284
3285static int action_listcommands(struct mansession *s, const struct message *m)
3286{
3287 struct manager_action *cur;
3289
3290 astman_start_ack(s, m);
3292 AST_RWLIST_TRAVERSE(&actions, cur, list) {
3293 if ((s->session->writeperm & cur->authority) || cur->authority == 0) {
3294 astman_append(s, "%s: %s (Priv: %s)\r\n",
3295 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
3296 }
3297 }
3299 astman_append(s, "\r\n");
3300
3301 return 0;
3302}
3303
3304static int action_events(struct mansession *s, const struct message *m)
3305{
3306 const char *mask = astman_get_header(m, "EventMask");
3307 int res, x;
3308 const char *id = astman_get_header(m, "ActionID");
3309 char id_text[256];
3310
3311 if (!ast_strlen_zero(id)) {
3312 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
3313 } else {
3314 id_text[0] = '\0';
3315 }
3316
3317 res = set_eventmask(s, mask);
3319 /* if this option is set we should not return a response on
3320 * error, or when all events are set */
3321
3322 if (res > 0) {
3323 for (x = 0; x < ARRAY_LEN(perms); x++) {
3324 if (!strcasecmp(perms[x].label, "all") && res == perms[x].num) {
3325 return 0;
3326 }
3327 }
3328 astman_append(s, "Response: Success\r\n%s"
3329 "Events: On\r\n\r\n", id_text);
3330 } else if (res == 0)
3331 astman_append(s, "Response: Success\r\n%s"
3332 "Events: Off\r\n\r\n", id_text);
3333 return 0;
3334 }
3335
3336 if (res > 0)
3337 astman_append(s, "Response: Success\r\n%s"
3338 "Events: On\r\n\r\n", id_text);
3339 else if (res == 0)
3340 astman_append(s, "Response: Success\r\n%s"
3341 "Events: Off\r\n\r\n", id_text);
3342 else
3343 astman_send_error(s, m, "Invalid event mask");
3344
3345 return 0;
3346}
3347
3348static int action_logoff(struct mansession *s, const struct message *m)
3349{
3350 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
3351 return -1;
3352}
3353
3354static int action_login(struct mansession *s, const struct message *m)
3355{
3356
3357 /* still authenticated - don't process again */
3358 if (s->session->authenticated) {
3359 astman_send_ack(s, m, "Already authenticated");
3360 return 0;
3361 }
3362
3363 if (authenticate(s, m)) {
3364 sleep(1);
3365 astman_send_error(s, m, "Authentication failed");
3366 return -1;
3367 }
3368 s->session->authenticated = 1;
3371 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_sockaddr_stringify_addr(&s->session->addr));
3372 }
3373 astman_send_ack(s, m, "Authentication accepted");
3376 && ast_fully_booted) {
3378 const char *cat_str = authority_to_str(EVENT_FLAG_SYSTEM, &auth);
3379 long uptime = 0;
3380 long lastreloaded = 0;
3381 struct timeval tmp;
3382 struct timeval curtime = ast_tvnow();
3383
3384 if (ast_startuptime.tv_sec) {
3385 tmp = ast_tvsub(curtime, ast_startuptime);
3386 uptime = tmp.tv_sec;
3387 }
3388
3389 if (ast_lastreloadtime.tv_sec) {
3390 tmp = ast_tvsub(curtime, ast_lastreloadtime);
3391 lastreloaded = tmp.tv_sec;
3392 }
3393
3394 astman_append(s, "Event: FullyBooted\r\n"
3395 "Privilege: %s\r\n"
3396 "Uptime: %ld\r\n"
3397 "LastReload: %ld\r\n"
3398 "Status: Fully Booted\r\n\r\n", cat_str, uptime, lastreloaded);
3399 }
3400 return 0;
3401}
3402
3403static int action_challenge(struct mansession *s, const struct message *m)
3404{
3405 const char *authtype = astman_get_header(m, "AuthType");
3406
3407 if (!strcasecmp(authtype, "MD5")) {
3409 snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
3410 }
3411 mansession_lock(s);
3412 astman_start_ack(s, m);
3413 astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
3415 } else {
3416 astman_send_error(s, m, "Must specify AuthType");
3417 }
3418 return 0;
3419}
3420
3422 const struct message *m, manager_hangup_handler_t hangup_handler,
3423 manager_hangup_cause_validator_t cause_validator)
3424{
3425 struct ast_channel *c = NULL;
3426 int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
3427 const char *id = astman_get_header(m, "ActionID");
3428 const char *name_or_regex = astman_get_header(m, "Channel");
3429 const char *cause = astman_get_header(m, "Cause");
3430 char idText[256];
3431 regex_t regexbuf;
3432 struct ast_channel_iterator *iter = NULL;
3433 struct ast_str *regex_string;
3434 int channels_matched = 0;
3435
3436 if (ast_strlen_zero(name_or_regex)) {
3437 astman_send_error(s, m, "No channel specified");
3438 return 0;
3439 }
3440
3441 if (!ast_strlen_zero(id)) {
3442 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
3443 } else {
3444 idText[0] = '\0';
3445 }
3446
3447 if (cause_validator) {
3448 causecode = cause_validator(name_or_regex, cause);
3449 } else if (!ast_strlen_zero(cause)) {
3450 char *endptr;
3451 causecode = strtol(cause, &endptr, 10);
3452 if (causecode < 0 || causecode > 127 || *endptr != '\0') {
3453 ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause);
3454 /* keep going, better to hangup without cause than to not hang up at all */
3455 causecode = 0; /* do not set channel's hangupcause */
3456 }
3457 }
3458
3459 /************************************************/
3460 /* Regular explicit match channel byname hangup */
3461
3462 if (name_or_regex[0] != '/') {
3463 if (!(c = ast_channel_get_by_name(name_or_regex))) {
3464 ast_log(LOG_NOTICE, "Request to hangup non-existent channel: %s\n",
3465 name_or_regex);
3466 astman_send_error(s, m, "No such channel");
3467 return 0;
3468 }
3469
3470 ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
3471 (s->session->managerid ? "HTTP " : ""),
3472 s->session->username,
3475
3476 hangup_handler(c, causecode);
3478
3479 astman_send_ack(s, m, "Channel Hungup");
3480
3481 return 0;
3482 }
3483
3484 /***********************************************/
3485 /* find and hangup any channels matching regex */
3486
3487 regex_string = ast_str_create(strlen(name_or_regex));
3488 if (!regex_string) {
3489 astman_send_error(s, m, "Memory Allocation Failure");
3490 return 0;
3491 }
3492
3493 /* Make "/regex/" into "regex" */
3494 if (ast_regex_string_to_regex_pattern(name_or_regex, &regex_string) != 0) {
3495 astman_send_error(s, m, "Regex format invalid, Channel param should be /regex/");
3496 ast_free(regex_string);
3497 return 0;
3498 }
3499
3500 /* if regex compilation fails, hangup fails */
3501 if (regcomp(&regexbuf, ast_str_buffer(regex_string), REG_EXTENDED | REG_NOSUB)) {
3502 astman_send_error_va(s, m, "Regex compile failed on: %s", name_or_regex);
3503 ast_free(regex_string);
3504 return 0;
3505 }
3506
3507 astman_send_listack(s, m, "Channels hung up will follow", "start");
3508
3510 if (iter) {
3511 for (; (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
3512 if (regexec(&regexbuf, ast_channel_name(c), 0, NULL, 0)) {
3513 continue;
3514 }
3515
3516 ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
3517 (s->session->managerid ? "HTTP " : ""),
3518 s->session->username,
3521
3522 hangup_handler(c, causecode);
3523 channels_matched++;
3524
3525 astman_append(s,
3526 "Event: ChannelHungup\r\n"
3527 "Channel: %s\r\n"
3528 "%s"
3529 "\r\n", ast_channel_name(c), idText);
3530 }
3532 }
3533
3534 regfree(&regexbuf);
3535 ast_free(regex_string);
3536
3537 astman_send_list_complete(s, m, "ChannelsHungupListComplete", channels_matched);
3538
3539 return 0;
3540}
3541
3542static int action_hangup(struct mansession *s, const struct message *m)
3543{
3544 return ast_manager_hangup_helper(s, m,
3546}
3547
3548static int action_setvar(struct mansession *s, const struct message *m)
3549{
3550 struct ast_channel *c = NULL;
3551 const char *name = astman_get_header(m, "Channel");
3552 const char *varname = astman_get_header(m, "Variable");
3553 const char *varval = astman_get_header(m, "Value");
3554 int res = 0;
3555
3556 if (ast_strlen_zero(varname)) {
3557 astman_send_error(s, m, "No variable specified");
3558 return 0;
3559 }
3560
3561 if (!ast_strlen_zero(name)) {
3562 if (!(c = ast_channel_get_by_name(name))) {
3563 astman_send_error(s, m, "No such channel");
3564 return 0;
3565 }
3566 }
3567
3568 res = pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
3569
3570 if (c) {
3572 }
3573 if (res == 0) {
3574 astman_send_ack(s, m, "Variable Set");
3575 } else {
3576 astman_send_error(s, m, "Variable not set");
3577 }
3578 return 0;
3579}
3580
3581static int action_getvar(struct mansession *s, const struct message *m)
3582{
3583 struct ast_channel *c = NULL;
3584 const char *name = astman_get_header(m, "Channel");
3585 const char *varname = astman_get_header(m, "Variable");
3586 char *varval;
3587 char workspace[1024];
3588
3589 if (ast_strlen_zero(varname)) {
3590 astman_send_error(s, m, "No variable specified");
3591 return 0;
3592 }
3593
3594 /* We don't want users with insufficient permissions using certain functions. */
3596 astman_send_error(s, m, "GetVar Access Forbidden: Variable");
3597 return 0;
3598 }
3599
3600 if (!ast_strlen_zero(name)) {
3601 if (!(c = ast_channel_get_by_name(name))) {
3602 astman_send_error(s, m, "No such channel");
3603 return 0;
3604 }
3605 }
3606
3607 workspace[0] = '\0';
3608 if (varname[strlen(varname) - 1] == ')') {
3609 if (!c) {
3611 if (c) {
3612 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
3613 } else
3614 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
3615 } else {
3616 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
3617 }
3618 varval = workspace;
3619 } else {
3620 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
3621 }
3622
3623 if (c) {
3625 }
3626
3627 astman_start_ack(s, m);
3628 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, S_OR(varval, ""));
3629
3630 return 0;
3631}
3632
3633static void generate_status(struct mansession *s, struct ast_channel *chan, char **vars, int varc, int all_variables, char *id_text, int *count)
3634{
3635 struct timeval now;
3636 long elapsed_seconds;
3637 struct ast_bridge *bridge;
3638 RAII_VAR(struct ast_str *, variable_str, NULL, ast_free);
3639 struct ast_str *write_transpath = ast_str_alloca(256);
3640 struct ast_str *read_transpath = ast_str_alloca(256);
3641 struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
3642 struct ast_party_id effective_id;
3643 int i;
3644 RAII_VAR(struct ast_channel_snapshot *, snapshot,
3646 ao2_cleanup);
3647 RAII_VAR(struct ast_str *, snapshot_str, NULL, ast_free);
3648
3649 if (!snapshot) {
3650 return;
3651 }
3652
3653 snapshot_str = ast_manager_build_channel_state_string(snapshot);
3654 if (!snapshot_str) {
3655 return;
3656 }
3657
3658 if (all_variables) {
3659 variable_str = ast_str_create(2048);
3660 } else {
3661 variable_str = ast_str_create(1024);
3662 }
3663 if (!variable_str) {
3664 return;
3665 }
3666
3667 now = ast_tvnow();
3668 elapsed_seconds = ast_tvdiff_sec(now, ast_channel_creationtime(chan));
3669
3670 /* Even if all_variables has been specified, explicitly requested variables
3671 * may be global variables or dialplan functions */
3672 for (i = 0; i < varc; i++) {
3673 char valbuf[512], *ret = NULL;
3674
3675 if (vars[i][strlen(vars[i]) - 1] == ')') {
3676 if (ast_func_read(chan, vars[i], valbuf, sizeof(valbuf)) < 0) {
3677 valbuf[0] = '\0';
3678 }
3679 ret = valbuf;
3680 } else {
3681 pbx_retrieve_variable(chan, vars[i], &ret, valbuf, sizeof(valbuf), NULL);
3682 }
3683
3684 ast_str_append(&variable_str, 0, "Variable: %s=%s\r\n", vars[i], ret);
3685 }
3686
3687 /* Walk all channel variables and add them */
3688 if (all_variables) {
3689 struct ast_var_t *variables;
3690
3691 AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
3692 ast_str_append(&variable_str, 0, "Variable: %s=%s\r\n",
3693 ast_var_name(variables), ast_var_value(variables));
3694 }
3695 }
3696
3697 bridge = ast_channel_get_bridge(chan);
3698 effective_id = ast_channel_connected_effective_id(chan);
3699
3700 astman_append(s,
3701 "Event: Status\r\n"
3702 "Privilege: Call\r\n"
3703 "%s"
3704 "Type: %s\r\n"
3705 "DNID: %s\r\n"
3706 "EffectiveConnectedLineNum: %s\r\n"
3707 "EffectiveConnectedLineName: %s\r\n"
3708 "TimeToHangup: %ld\r\n"
3709 "BridgeID: %s\r\n"
3710 "Application: %s\r\n"
3711 "Data: %s\r\n"
3712 "Nativeformats: %s\r\n"
3713 "Readformat: %s\r\n"
3714 "Readtrans: %s\r\n"
3715 "Writeformat: %s\r\n"
3716 "Writetrans: %s\r\n"
3717 "Callgroup: %llu\r\n"
3718 "Pickupgroup: %llu\r\n"
3719 "Seconds: %ld\r\n"
3720 "%s"
3721 "%s"
3722 "\r\n",
3723 ast_str_buffer(snapshot_str),
3724 ast_channel_tech(chan)->type,
3725 S_OR(ast_channel_dialed(chan)->number.str, ""),
3726 S_COR(effective_id.number.valid, effective_id.number.str, "<unknown>"),
3727 S_COR(effective_id.name.valid, effective_id.name.str, "<unknown>"),
3728 (long)ast_channel_whentohangup(chan)->tv_sec,
3729 bridge ? bridge->uniqueid : "",
3730 ast_channel_appl(chan),
3731 ast_channel_data(chan),
3734 ast_translate_path_to_str(ast_channel_readtrans(chan), &read_transpath),
3736 ast_translate_path_to_str(ast_channel_writetrans(chan), &write_transpath),
3739 (long)elapsed_seconds,
3740 ast_str_buffer(variable_str),
3741 id_text);
3742 ++*count;
3743
3744 ao2_cleanup(bridge);
3745}
3746
3747/*! \brief Manager "status" command to show channels */
3748static int action_status(struct mansession *s, const struct message *m)
3749{
3750 const char *name = astman_get_header(m, "Channel");
3751 const char *chan_variables = astman_get_header(m, "Variables");
3752 const char *all_chan_variables = astman_get_header(m, "AllVariables");
3753 int all_variables = 0;
3754 const char *id = astman_get_header(m, "ActionID");
3755 char *variables = ast_strdupa(S_OR(chan_variables, ""));
3756 struct ast_channel *chan;
3757 int channels = 0;
3758 int all = ast_strlen_zero(name); /* set if we want all channels */
3759 char id_text[256];
3760 struct ast_channel_iterator *it_chans = NULL;
3762 AST_APP_ARG(name)[100];
3763 );
3764
3765 if (!ast_strlen_zero(all_chan_variables)) {
3766 all_variables = ast_true(all_chan_variables);
3767 }
3768
3770 astman_send_error(s, m, "Status Access Forbidden: Variables");
3771 return 0;
3772 }
3773
3774 if (all) {
3775 if (!(it_chans = ast_channel_iterator_all_new())) {
3776 astman_send_error(s, m, "Memory Allocation Failure");
3777 return 1;
3778 }
3779 chan = ast_channel_iterator_next(it_chans);
3780 } else {
3782 if (!chan) {
3783 astman_send_error(s, m, "No such channel");
3784 return 0;
3785 }
3786 }
3787
3788 astman_send_listack(s, m, "Channel status will follow", "start");
3789
3790 if (!ast_strlen_zero(id)) {
3791 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
3792 } else {
3793 id_text[0] = '\0';
3794 }
3795
3796 if (!ast_strlen_zero(chan_variables)) {
3797 AST_STANDARD_APP_ARGS(vars, variables);
3798 }
3799
3800 /* if we look by name, we break after the first iteration */
3801 for (; chan; all ? chan = ast_channel_iterator_next(it_chans) : 0) {
3802 ast_channel_lock(chan);
3803
3804 generate_status(s, chan, vars.name, vars.argc, all_variables, id_text, &channels);
3805
3806 ast_channel_unlock(chan);
3807 chan = ast_channel_unref(chan);
3808 }
3809
3810 if (it_chans) {
3812 }
3813
3814 astman_send_list_complete_start(s, m, "StatusComplete", channels);
3815 astman_append(s, "Items: %d\r\n", channels);
3817
3818 return 0;
3819}
3820
3821/*!
3822 * \brief Queue a given read action containing a payload onto a channel
3823 *
3824 * This queues a READ_ACTION control frame that contains a given "payload", or
3825 * data to be triggered and handled on the channel's read side. This ensures
3826 * the "action" is handled by the channel's media reading thread.
3827 *
3828 * \param chan The channel to queue the action on
3829 * \param payload The read action's payload
3830 * \param payload_size The size of the given payload
3831 * \param action The type of read action to queue
3832 *
3833 * \retval -1 on error
3834 * \retval 0 on success
3835 */
3836static int queue_read_action_payload(struct ast_channel *chan, const unsigned char *payload,
3837 size_t payload_size, enum ast_frame_read_action action)
3838{
3840 size_t obj_size;
3841 int res;
3842
3843 obj_size = payload_size + sizeof(*obj);
3844
3845 obj = ast_malloc(obj_size);
3846 if (!obj) {
3847 return -1;
3848 }
3849
3850 obj->action = action;
3851 obj->payload_size = payload_size;
3852 memcpy(obj->payload, payload, payload_size);
3853
3854 res = ast_queue_control_data(chan, AST_CONTROL_READ_ACTION, obj, obj_size);
3855
3856 ast_free(obj);
3857 return res;
3858}
3859
3860/*!
3861 * \brief Queue a read action to send a text message
3862 *
3863 * \param chan The channel to queue the action on
3864 * \param body The body of the message
3865 *
3866 * \retval -1 on error
3867 * \retval 0 on success
3868 */
3869static int queue_sendtext(struct ast_channel *chan, const char *body)
3870{
3871 return queue_read_action_payload(chan, (const unsigned char *)body,
3872 strlen(body) + 1, AST_FRAME_READ_ACTION_SEND_TEXT);
3873}
3874
3875/*!
3876 * \brief Queue a read action to send a text data message
3877 *
3878 * \param chan The channel to queue the action on
3879 * \param body The body of the message
3880 * \param content_type The message's content type
3881 *
3882 * \retval -1 on error
3883 * \retval 0 on success
3884 */
3885static int queue_sendtext_data(struct ast_channel *chan, const char *body,
3886 const char *content_type)
3887{
3888 int res;
3889 struct ast_msg_data *obj;
3890
3892 NULL, NULL, content_type, body);
3893 if (!obj) {
3894 return -1;
3895 }
3896
3897 res = queue_read_action_payload(chan, (const unsigned char *)obj,
3899
3900 ast_free(obj);
3901 return res;
3902}
3903
3904static int action_sendtext(struct mansession *s, const struct message *m)
3905{
3906 struct ast_channel *c;
3907 const char *name = astman_get_header(m, "Channel");
3908 const char *textmsg = astman_get_header(m, "Message");
3909 const char *content_type = astman_get_header(m, "Content-Type");
3910 int res;
3911
3912 if (ast_strlen_zero(name)) {
3913 astman_send_error(s, m, "No channel specified");
3914 return 0;
3915 }
3916
3917 if (ast_strlen_zero(textmsg)) {
3918 astman_send_error(s, m, "No Message specified");
3919 return 0;
3920 }
3921
3923 if (!c) {
3924 astman_send_error(s, m, "No such channel");
3925 return 0;
3926 }
3927
3928 /*
3929 * If the "extra" data is not available, then send using "string" only.
3930 * Doing such maintains backward compatibilities.
3931 */
3932 res = ast_strlen_zero(content_type) ? queue_sendtext(c, textmsg) :
3933 queue_sendtext_data(c, textmsg, content_type);
3934
3936
3937 if (res >= 0) {
3938 astman_send_ack(s, m, "Success");
3939 } else {
3940 astman_send_error(s, m, "Failure");
3941 }
3942
3943 return 0;
3944}
3945
3946static int async_goto_with_discard_bridge_after(struct ast_channel *chan, const char *context, const char *exten, int priority)
3947{
3949 return ast_async_goto(chan, context, exten, priority);
3950}
3951
3952/*! \brief action_redirect: The redirect manager command */
3953static int action_redirect(struct mansession *s, const struct message *m)
3954{
3955 char buf[256];
3956 const char *name = astman_get_header(m, "Channel");
3957 const char *name2 = astman_get_header(m, "ExtraChannel");
3958 const char *exten = astman_get_header(m, "Exten");
3959 const char *exten2 = astman_get_header(m, "ExtraExten");
3960 const char *context = astman_get_header(m, "Context");
3961 const char *context2 = astman_get_header(m, "ExtraContext");
3962 const char *priority = astman_get_header(m, "Priority");
3963 const char *priority2 = astman_get_header(m, "ExtraPriority");
3964 struct ast_channel *chan;
3965 struct ast_channel *chan2;
3966 int pi = 0;
3967 int pi2 = 0;
3968 int res;
3969 int chan1_wait = 0;
3970 int chan2_wait = 0;
3971
3972 if (ast_strlen_zero(name)) {
3973 astman_send_error(s, m, "Channel not specified");
3974 return 0;
3975 }
3976
3977 if (ast_strlen_zero(context)) {
3978 astman_send_error(s, m, "Context not specified");
3979 return 0;
3980 }
3981 if (ast_strlen_zero(exten)) {
3982 astman_send_error(s, m, "Exten not specified");
3983 return 0;
3984 }
3986 astman_send_error(s, m, "Priority not specified");
3987 return 0;
3988 }
3989 if (sscanf(priority, "%30d", &pi) != 1) {
3991 }
3992 if (pi < 1) {
3993 astman_send_error(s, m, "Priority is invalid");
3994 return 0;
3995 }
3996
3997 if (!ast_strlen_zero(name2) && !ast_strlen_zero(context2)) {
3998 /* We have an ExtraChannel and an ExtraContext */
3999 if (ast_strlen_zero(exten2)) {
4000 astman_send_error(s, m, "ExtraExten not specified");
4001 return 0;
4002 }
4003 if (ast_strlen_zero(priority2)) {
4004 astman_send_error(s, m, "ExtraPriority not specified");
4005 return 0;
4006 }
4007 if (sscanf(priority2, "%30d", &pi2) != 1) {
4008 pi2 = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL);
4009 }
4010 if (pi2 < 1) {
4011 astman_send_error(s, m, "ExtraPriority is invalid");
4012 return 0;
4013 }
4014 }
4015
4017 if (!chan) {
4018 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
4019 astman_send_error(s, m, buf);
4020 return 0;
4021 }
4022 if (ast_check_hangup_locked(chan)) {
4023 astman_send_error(s, m, "Redirect failed, channel not up.");
4024 chan = ast_channel_unref(chan);
4025 return 0;
4026 }
4027
4028 if (ast_strlen_zero(name2)) {
4029 /* Single channel redirect in progress. */
4030 res = async_goto_with_discard_bridge_after(chan, context, exten, pi);
4031 if (!res) {
4032 astman_send_ack(s, m, "Redirect successful");
4033 } else {
4034 astman_send_error(s, m, "Redirect failed");
4035 }
4036 chan = ast_channel_unref(chan);
4037 return 0;
4038 }
4039
4040 chan2 = ast_channel_get_by_name(name2);
4041 if (!chan2) {
4042 snprintf(buf, sizeof(buf), "ExtraChannel does not exist: %s", name2);
4043 astman_send_error(s, m, buf);
4044 chan = ast_channel_unref(chan);
4045 return 0;
4046 }
4047 if (ast_check_hangup_locked(chan2)) {
4048 astman_send_error(s, m, "Redirect failed, extra channel not up.");
4049 chan2 = ast_channel_unref(chan2);
4050 chan = ast_channel_unref(chan);
4051 return 0;
4052 }
4053
4054 /* Dual channel redirect in progress. */
4055 ast_channel_lock(chan);
4056 if (ast_channel_is_bridged(chan)) {
4058 chan1_wait = 1;
4059 }
4060 ast_channel_unlock(chan);
4061
4062 ast_channel_lock(chan2);
4063 if (ast_channel_is_bridged(chan2)) {
4065 chan2_wait = 1;
4066 }
4067 ast_channel_unlock(chan2);
4068
4069 res = async_goto_with_discard_bridge_after(chan, context, exten, pi);
4070 if (!res) {
4071 if (!ast_strlen_zero(context2)) {
4072 res = async_goto_with_discard_bridge_after(chan2, context2, exten2, pi2);
4073 } else {
4074 res = async_goto_with_discard_bridge_after(chan2, context, exten, pi);
4075 }
4076 if (!res) {
4077 astman_send_ack(s, m, "Dual Redirect successful");
4078 } else {
4079 astman_send_error(s, m, "Secondary redirect failed");
4080 }
4081 } else {
4082 astman_send_error(s, m, "Redirect failed");
4083 }
4084
4085 /* Release the bridge wait. */
4086 if (chan1_wait) {
4088 }
4089 if (chan2_wait) {
4091 }
4092
4093 chan2 = ast_channel_unref(chan2);
4094 chan = ast_channel_unref(chan);
4095 return 0;
4096}
4097
4098static int action_blind_transfer(struct mansession *s, const struct message *m)
4099{
4100 const char *name = astman_get_header(m, "Channel");
4101 const char *exten = astman_get_header(m, "Exten");
4102 const char *context = astman_get_header(m, "Context");
4103 struct ast_channel *chan;
4104
4105 if (ast_strlen_zero(name)) {
4106 astman_send_error(s, m, "No channel specified");
4107 return 0;
4108 }
4109
4110 if (ast_strlen_zero(exten)) {
4111 astman_send_error(s, m, "No extension specified");
4112 return 0;
4113 }
4114
4116 if (!chan) {
4117 astman_send_error(s, m, "Channel specified does not exist");
4118 return 0;
4119 }
4120
4121 if (ast_strlen_zero(context)) {
4123 }
4124
4125 switch (ast_bridge_transfer_blind(1, chan, exten, context, NULL, NULL)) {
4127 astman_send_error(s, m, "Transfer not permitted");
4128 break;
4130 astman_send_error(s, m, "Transfer invalid");
4131 break;
4133 astman_send_error(s, m, "Transfer failed");
4134 break;
4136 astman_send_ack(s, m, "Transfer succeeded");
4137 break;
4138 }
4139
4140 ast_channel_unref(chan);
4141 return 0;
4142}
4143
4144static int action_atxfer(struct mansession *s, const struct message *m)
4145{
4146 const char *name = astman_get_header(m, "Channel");
4147 const char *exten = astman_get_header(m, "Exten");
4148 const char *context = astman_get_header(m, "Context");
4149 struct ast_channel *chan = NULL;
4150 char feature_code[AST_FEATURE_MAX_LEN];
4151 const char *digit;
4152
4153 if (ast_strlen_zero(name)) {
4154 astman_send_error(s, m, "No channel specified");
4155 return 0;
4156 }
4157 if (ast_strlen_zero(exten)) {
4158 astman_send_error(s, m, "No extension specified");
4159 return 0;
4160 }
4161
4162 if (!(chan = ast_channel_get_by_name(name))) {
4163 astman_send_error(s, m, "Channel specified does not exist");
4164 return 0;
4165 }
4166
4167 ast_channel_lock(chan);
4168 if (ast_get_builtin_feature(chan, "atxfer", feature_code, sizeof(feature_code)) ||
4169 ast_strlen_zero(feature_code)) {
4170 ast_channel_unlock(chan);
4171 astman_send_error(s, m, "No attended transfer feature code found");
4172 ast_channel_unref(chan);
4173 return 0;
4174 }
4175 ast_channel_unlock(chan);
4176
4177 if (!ast_strlen_zero(context)) {
4178 pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
4179 }
4180
4181 for (digit = feature_code; *digit; ++digit) {
4182 struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
4183 ast_queue_frame(chan, &f);
4184 }
4185
4186 for (digit = exten; *digit; ++digit) {
4187 struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
4188 ast_queue_frame(chan, &f);
4189 }
4190
4191 chan = ast_channel_unref(chan);
4192
4193 astman_send_ack(s, m, "Atxfer successfully queued");
4194
4195 return 0;
4196}
4197
4198static int action_cancel_atxfer(struct mansession *s, const struct message *m)
4199{
4200 const char *name = astman_get_header(m, "Channel");
4201 struct ast_channel *chan = NULL;
4202 char *feature_code;
4203 const char *digit;
4204
4205 if (ast_strlen_zero(name)) {
4206 astman_send_error(s, m, "No channel specified");
4207 return 0;
4208 }
4209
4210 if (!(chan = ast_channel_get_by_name(name))) {
4211 astman_send_error(s, m, "Channel specified does not exist");
4212 return 0;
4213 }
4214
4215 ast_channel_lock(chan);
4216 feature_code = ast_get_chan_features_atxferabort(chan);
4217 ast_channel_unlock(chan);
4218
4219 if (!feature_code) {
4220 astman_send_error(s, m, "No disconnect feature code found");
4221 ast_channel_unref(chan);
4222 return 0;
4223 }
4224
4225 for (digit = feature_code; *digit; ++digit) {
4226 struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
4227 ast_queue_frame(chan, &f);
4228 }
4229 ast_free(feature_code);
4230
4231 chan = ast_channel_unref(chan);
4232
4233 astman_send_ack(s, m, "CancelAtxfer successfully queued");
4234
4235 return 0;
4236}
4237
4238
4239static int check_blacklist(const char *cmd)
4240{
4241 char *cmd_copy, *cur_cmd;
4242 char *cmd_words[AST_MAX_CMD_LEN] = { NULL, };
4243 int i;
4244
4245 cmd_copy = ast_strdupa(cmd);
4246 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
4247 cur_cmd = ast_strip(cur_cmd);
4248 if (ast_strlen_zero(cur_cmd)) {
4249 i--;
4250 continue;
4251 }
4252
4253 cmd_words[i] = cur_cmd;
4254 }
4255
4256 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
4257 int j, match = 1;
4258
4259 for (j = 0; command_blacklist[i].words[j]; j++) {
4260 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
4261 match = 0;
4262 break;
4263 }
4264 }
4265
4266 if (match) {
4267 return 1;
4268 }
4269 }
4270
4271 return 0;
4272}
4273
4274/*! \brief Manager command "command" - execute CLI command */
4275static int action_command(struct mansession *s, const struct message *m)
4276{
4277 const char *cmd = astman_get_header(m, "Command");
4278 char *buf = NULL, *final_buf = NULL, *delim, *output;
4279 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
4280 int fd, ret;
4281 off_t len;
4282
4283 if (ast_strlen_zero(cmd)) {
4284 astman_send_error(s, m, "No command provided");
4285 return 0;
4286 }
4287
4288 if (check_blacklist(cmd)) {
4289 astman_send_error(s, m, "Command blacklisted");
4290 return 0;
4291 }
4292
4293 if ((fd = mkstemp(template)) < 0) {
4294 astman_send_error_va(s, m, "Failed to create temporary file: %s", strerror(errno));
4295 return 0;
4296 }
4297
4298 ret = ast_cli_command(fd, cmd);
4299 astman_send_response_full(s, m, ret == RESULT_SUCCESS ? "Success" : "Error", MSG_MOREDATA, NULL);
4300
4301 /* Determine number of characters available */
4302 if ((len = lseek(fd, 0, SEEK_END)) < 0) {
4303 astman_append(s, "Message: Failed to determine number of characters: %s\r\n", strerror(errno));
4304 goto action_command_cleanup;
4305 }
4306
4307 /* This has a potential to overflow the stack. Hence, use the heap. */
4308 buf = ast_malloc(len + 1);
4309 final_buf = ast_malloc(len + 1);
4310
4311 if (!buf || !final_buf) {
4312 astman_append(s, "Message: Memory allocation failure\r\n");
4313 goto action_command_cleanup;
4314 }
4315
4316 if (lseek(fd, 0, SEEK_SET) < 0) {
4317 astman_append(s, "Message: Failed to set position on temporary file: %s\r\n", strerror(errno));
4318 goto action_command_cleanup;
4319 }
4320
4321 if (read(fd, buf, len) < 0) {
4322 astman_append(s, "Message: Failed to read from temporary file: %s\r\n", strerror(errno));
4323 goto action_command_cleanup;
4324 }
4325
4326 buf[len] = '\0';
4327 term_strip(final_buf, buf, len);
4328 final_buf[len] = '\0';
4329
4330 /* Trim trailing newline */
4331 if (len && final_buf[len - 1] == '\n') {
4332 final_buf[len - 1] = '\0';
4333 }
4334
4335 astman_append(s, "Message: Command output follows\r\n");
4336
4337 delim = final_buf;
4338 while ((output = strsep(&delim, "\n"))) {
4339 astman_append(s, "Output: %s\r\n", output);
4340 }
4341
4342action_command_cleanup:
4343 astman_append(s, "\r\n");
4344
4345 close(fd);
4346 unlink(template);
4347
4348 ast_free(buf);
4349 ast_free(final_buf);
4350
4351 return 0;
4352}
4353
4354/*! \brief helper function for originate */
4357 struct ast_format_cap *cap; /*!< Codecs used for a call */
4360 AST_STRING_FIELD(tech);
4361 /*! data can contain a channel name, extension number, username, password, etc. */
4362 AST_STRING_FIELD(data);
4364 AST_STRING_FIELD(appdata);
4365 AST_STRING_FIELD(cid_name);
4366 AST_STRING_FIELD(cid_num);
4368 AST_STRING_FIELD(exten);
4369 AST_STRING_FIELD(idtext);
4370 AST_STRING_FIELD(account);
4371 AST_STRING_FIELD(channelid);
4372 AST_STRING_FIELD(otherchannelid);
4376};
4377
4378/*!
4379 * \internal
4380 *
4381 * \param doomed Struct to destroy.
4382 */
4384{
4385 ao2_cleanup(doomed->cap);
4386 ast_variables_destroy(doomed->vars);
4388 ast_free(doomed);
4389}
4390
4391static void *fast_originate(void *data)
4392{
4393 struct fast_originate_helper *in = data;
4394 int res;
4395 int reason = 0;
4396 struct ast_channel *chan = NULL, *chans[1];
4397 char requested_channel[AST_CHANNEL_NAME];
4398 struct ast_assigned_ids assignedids = {
4399 .uniqueid = in->channelid,
4400 .uniqueid2 = in->otherchannelid
4401 };
4402
4403 if (!ast_strlen_zero(in->app)) {
4404 res = ast_pbx_outgoing_app(in->tech, in->cap, in->data,
4405 in->timeout, in->app, in->appdata, &reason,
4407 S_OR(in->cid_num, NULL),
4408 S_OR(in->cid_name, NULL),
4409 in->vars, in->account, &chan, &assignedids);
4410 } else {
4411 res = ast_pbx_outgoing_exten(in->tech, in->cap, in->data,
4412 in->timeout, in->context, in->exten, in->priority, &reason,
4414 S_OR(in->cid_num, NULL),
4415 S_OR(in->cid_name, NULL),
4416 in->vars, in->account, &chan, in->early_media, &assignedids);
4417 }
4418
4419 if (!chan) {
4420 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
4421 }
4422 /* Tell the manager what happened with the channel */
4423 chans[0] = chan;
4424 if (!ast_strlen_zero(in->app)) {
4425 ast_manager_event_multichan(EVENT_FLAG_CALL, "OriginateResponse", chan ? 1 : 0, chans,
4426 "%s"
4427 "Response: %s\r\n"
4428 "Channel: %s\r\n"
4429 "Application: %s\r\n"
4430 "Data: %s\r\n"
4431 "Reason: %d\r\n"
4432 "Uniqueid: %s\r\n"
4433 "CallerIDNum: %s\r\n"
4434 "CallerIDName: %s\r\n",
4435 in->idtext, res ? "Failure" : "Success",
4436 chan ? ast_channel_name(chan) : requested_channel,
4437 in->app, in->appdata, reason,
4438 chan ? ast_channel_uniqueid(chan) : S_OR(in->channelid, "<unknown>"),
4439 S_OR(in->cid_num, "<unknown>"),
4440 S_OR(in->cid_name, "<unknown>")
4441 );
4442 } else {
4443 ast_manager_event_multichan(EVENT_FLAG_CALL, "OriginateResponse", chan ? 1 : 0, chans,
4444 "%s"
4445 "Response: %s\r\n"
4446 "Channel: %s\r\n"
4447 "Context: %s\r\n"
4448 "Exten: %s\r\n"
4449 "Reason: %d\r\n"
4450 "Uniqueid: %s\r\n"
4451 "CallerIDNum: %s\r\n"
4452 "CallerIDName: %s\r\n",
4453 in->idtext, res ? "Failure" : "Success",
4454 chan ? ast_channel_name(chan) : requested_channel,
4455 in->context, in->exten, reason,
4456 chan ? ast_channel_uniqueid(chan) : S_OR(in->channelid, "<unknown>"),
4457 S_OR(in->cid_num, "<unknown>"),
4458 S_OR(in->cid_name, "<unknown>")
4459 );
4460 }
4461
4462 /* Locked and ref'd by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
4463 if (chan) {
4464 ast_channel_unlock(chan);
4465 ast_channel_unref(chan);
4466 }
4468 return NULL;
4469}
4470
4471static int aocmessage_get_unit_entry(const struct message *m, struct ast_aoc_unit_entry *entry, unsigned int entry_num)
4472{
4473 const char *unitamount;
4474 const char *unittype;
4475 struct ast_str *str = ast_str_alloca(32);
4476
4477 memset(entry, 0, sizeof(*entry));
4478
4479 ast_str_set(&str, 0, "UnitAmount(%u)", entry_num);
4480 unitamount = astman_get_header(m, ast_str_buffer(str));
4481
4482 ast_str_set(&str, 0, "UnitType(%u)", entry_num);
4483 unittype = astman_get_header(m, ast_str_buffer(str));
4484
4485 if (!ast_strlen_zero(unitamount) && (sscanf(unitamount, "%30u", &entry->amount) == 1)) {
4486 entry->valid_amount = 1;
4487 }
4488
4489 if (!ast_strlen_zero(unittype) && sscanf(unittype, "%30u", &entry->type) == 1) {
4490 entry->valid_type = 1;
4491 }
4492
4493 return 0;
4494}
4495
4496static struct ast_aoc_decoded *action_aoc_de_message(struct mansession *s, const struct message *m)
4497{
4498 const char *msgtype = astman_get_header(m, "MsgType");
4499 const char *chargetype = astman_get_header(m, "ChargeType");
4500 const char *currencyname = astman_get_header(m, "CurrencyName");
4501 const char *currencyamount = astman_get_header(m, "CurrencyAmount");
4502 const char *mult = astman_get_header(m, "CurrencyMultiplier");
4503 const char *totaltype = astman_get_header(m, "TotalType");
4504 const char *aocbillingid = astman_get_header(m, "AOCBillingId");
4505 const char *association_id= astman_get_header(m, "ChargingAssociationId");
4506 const char *association_num = astman_get_header(m, "ChargingAssociationNumber");
4507 const char *association_plan = astman_get_header(m, "ChargingAssociationPlan");
4508
4509 enum ast_aoc_type _msgtype;
4510 enum ast_aoc_charge_type _chargetype;
4512 enum ast_aoc_total_type _totaltype = AST_AOC_TOTAL;
4513 enum ast_aoc_billing_id _billingid = AST_AOC_BILLING_NA;
4514 unsigned int _currencyamount = 0;
4515 int _association_id = 0;
4516 unsigned int _association_plan = 0;
4517
4518 struct ast_aoc_decoded *decoded = NULL;
4519
4520 if (ast_strlen_zero(chargetype)) {
4521 astman_send_error(s, m, "ChargeType not specified");
4522 goto aocmessage_cleanup;
4523 }
4524
4525 _msgtype = strcasecmp(msgtype, "d") ? AST_AOC_E : AST_AOC_D;
4526
4527 if (!strcasecmp(chargetype, "NA")) {
4528 _chargetype = AST_AOC_CHARGE_NA;
4529 } else if (!strcasecmp(chargetype, "Free")) {
4530 _chargetype = AST_AOC_CHARGE_FREE;
4531 } else if (!strcasecmp(chargetype, "Currency")) {
4532 _chargetype = AST_AOC_CHARGE_CURRENCY;
4533 } else if (!strcasecmp(chargetype, "Unit")) {
4534 _chargetype = AST_AOC_CHARGE_UNIT;
4535 } else {
4536 astman_send_error(s, m, "Invalid ChargeType");
4537 goto aocmessage_cleanup;
4538 }
4539
4540 if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
4541
4542 if (ast_strlen_zero(currencyamount) || (sscanf(currencyamount, "%30u", &_currencyamount) != 1)) {
4543 astman_send_error(s, m, "Invalid CurrencyAmount, CurrencyAmount is a required when ChargeType is Currency");
4544 goto aocmessage_cleanup;
4545 }
4546
4547 if (ast_strlen_zero(mult)) {
4548 astman_send_error(s, m, "ChargeMultiplier unspecified, ChargeMultiplier is required when ChargeType is Currency.");
4549 goto aocmessage_cleanup;
4550 } else if (!strcasecmp(mult, "onethousandth")) {
4552 } else if (!strcasecmp(mult, "onehundredth")) {
4554 } else if (!strcasecmp(mult, "onetenth")) {
4555 _mult = AST_AOC_MULT_ONETENTH;
4556 } else if (!strcasecmp(mult, "one")) {
4557 _mult = AST_AOC_MULT_ONE;
4558 } else if (!strcasecmp(mult, "ten")) {
4559 _mult = AST_AOC_MULT_TEN;
4560 } else if (!strcasecmp(mult, "hundred")) {
4561 _mult = AST_AOC_MULT_HUNDRED;
4562 } else if (!strcasecmp(mult, "thousand")) {
4563 _mult = AST_AOC_MULT_THOUSAND;
4564 } else {
4565 astman_send_error(s, m, "Invalid ChargeMultiplier");
4566 goto aocmessage_cleanup;
4567 }
4568 }
4569
4570 /* create decoded object and start setting values */
4571 if (!(decoded = ast_aoc_create(_msgtype, _chargetype, 0))) {
4572 astman_send_error(s, m, "Message Creation Failed");
4573 goto aocmessage_cleanup;
4574 }
4575
4576 if (_msgtype == AST_AOC_D) {
4577 if (!ast_strlen_zero(totaltype) && !strcasecmp(totaltype, "subtotal")) {
4578 _totaltype = AST_AOC_SUBTOTAL;
4579 }
4580
4581 if (ast_strlen_zero(aocbillingid)) {
4582 /* ignore this is optional */
4583 } else if (!strcasecmp(aocbillingid, "Normal")) {
4584 _billingid = AST_AOC_BILLING_NORMAL;
4585 } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
4586 _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
4587 } else if (!strcasecmp(aocbillingid, "CreditCard")) {
4588 _billingid = AST_AOC_BILLING_CREDIT_CARD;
4589 } else {
4590 astman_send_error(s, m, "Invalid AOC-D AOCBillingId");
4591 goto aocmessage_cleanup;
4592 }
4593 } else {
4594 if (ast_strlen_zero(aocbillingid)) {
4595 /* ignore this is optional */
4596 } else if (!strcasecmp(aocbillingid, "Normal")) {
4597 _billingid = AST_AOC_BILLING_NORMAL;
4598 } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
4599 _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
4600 } else if (!strcasecmp(aocbillingid, "CreditCard")) {
4601 _billingid = AST_AOC_BILLING_CREDIT_CARD;
4602 } else if (!strcasecmp(aocbillingid, "CallFwdUnconditional")) {
4604 } else if (!strcasecmp(aocbillingid, "CallFwdBusy")) {
4605 _billingid = AST_AOC_BILLING_CALL_FWD_BUSY;
4606 } else if (!strcasecmp(aocbillingid, "CallFwdNoReply")) {
4608 } else if (!strcasecmp(aocbillingid, "CallDeflection")) {
4610 } else if (!strcasecmp(aocbillingid, "CallTransfer")) {
4611 _billingid = AST_AOC_BILLING_CALL_TRANSFER;
4612 } else {
4613 astman_send_error(s, m, "Invalid AOC-E AOCBillingId");
4614 goto aocmessage_cleanup;
4615 }
4616
4617 if (!ast_strlen_zero(association_id) && (sscanf(association_id, "%30d", &_association_id) != 1)) {
4618 astman_send_error(s, m, "Invalid ChargingAssociationId");
4619 goto aocmessage_cleanup;
4620 }
4621 if (!ast_strlen_zero(association_plan) && (sscanf(association_plan, "%30u", &_association_plan) != 1)) {
4622 astman_send_error(s, m, "Invalid ChargingAssociationPlan");
4623 goto aocmessage_cleanup;
4624 }
4625
4626 if (_association_id) {
4627 ast_aoc_set_association_id(decoded, _association_id);
4628 } else if (!ast_strlen_zero(association_num)) {
4629 ast_aoc_set_association_number(decoded, association_num, _association_plan);
4630 }
4631 }
4632
4633 if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
4634 ast_aoc_set_currency_info(decoded, _currencyamount, _mult, ast_strlen_zero(currencyname) ? NULL : currencyname);
4635 } else if (_chargetype == AST_AOC_CHARGE_UNIT) {
4636 struct ast_aoc_unit_entry entry;
4637 int i;
4638
4639 /* multiple unit entries are possible, lets get them all */
4640 for (i = 0; i < 32; i++) {
4641 if (aocmessage_get_unit_entry(m, &entry, i)) {
4642 break; /* that's the end then */
4643 }
4644
4645 ast_aoc_add_unit_entry(decoded, entry.valid_amount, entry.amount, entry.valid_type, entry.type);
4646 }
4647
4648 /* at least one unit entry is required */
4649 if (!i) {
4650 astman_send_error(s, m, "Invalid UnitAmount(0), At least one valid unit entry is required when ChargeType is set to Unit");
4651 goto aocmessage_cleanup;
4652 }
4653
4654 }
4655
4656 ast_aoc_set_billing_id(decoded, _billingid);
4657 ast_aoc_set_total_type(decoded, _totaltype);
4658
4659 return decoded;
4660
4661aocmessage_cleanup:
4662
4663 ast_aoc_destroy_decoded(decoded);
4664 return NULL;
4665}
4666
4667static int action_aoc_s_submessage(struct mansession *s, const struct message *m,
4668 struct ast_aoc_decoded *decoded)
4669{
4670 const char *chargeditem = __astman_get_header(m, "ChargedItem", GET_HEADER_LAST_MATCH);
4671 const char *ratetype = __astman_get_header(m, "RateType", GET_HEADER_LAST_MATCH);
4672 const char *currencyname = __astman_get_header(m, "CurrencyName", GET_HEADER_LAST_MATCH);
4673 const char *currencyamount = __astman_get_header(m, "CurrencyAmount", GET_HEADER_LAST_MATCH);
4674 const char *mult = __astman_get_header(m, "CurrencyMultiplier", GET_HEADER_LAST_MATCH);
4675 const char *time = __astman_get_header(m, "Time", GET_HEADER_LAST_MATCH);
4676 const char *timescale = __astman_get_header(m, "TimeScale", GET_HEADER_LAST_MATCH);
4677 const char *granularity = __astman_get_header(m, "Granularity", GET_HEADER_LAST_MATCH);
4678 const char *granularitytimescale = __astman_get_header(m, "GranularityTimeScale", GET_HEADER_LAST_MATCH);
4679 const char *chargingtype = __astman_get_header(m, "ChargingType", GET_HEADER_LAST_MATCH);
4680 const char *volumeunit = __astman_get_header(m, "VolumeUnit", GET_HEADER_LAST_MATCH);
4681 const char *code = __astman_get_header(m, "Code", GET_HEADER_LAST_MATCH);
4682
4683 enum ast_aoc_s_charged_item _chargeditem;
4684 enum ast_aoc_s_rate_type _ratetype;
4686 unsigned int _currencyamount = 0;
4687 unsigned int _code;
4688 unsigned int _time = 0;
4689 enum ast_aoc_time_scale _scale = 0;
4690 unsigned int _granularity = 0;
4691 enum ast_aoc_time_scale _granularity_time_scale = AST_AOC_TIME_SCALE_MINUTE;
4692 int _step = 0;
4693 enum ast_aoc_volume_unit _volumeunit = 0;
4694
4695 if (ast_strlen_zero(chargeditem)) {
4696 astman_send_error(s, m, "ChargedItem not specified");
4697 goto aocmessage_cleanup;
4698 }
4699
4700 if (ast_strlen_zero(ratetype)) {
4701 astman_send_error(s, m, "RateType not specified");
4702 goto aocmessage_cleanup;
4703 }
4704
4705 if (!strcasecmp(chargeditem, "NA")) {
4706 _chargeditem = AST_AOC_CHARGED_ITEM_NA;
4707 } else if (!strcasecmp(chargeditem, "SpecialArrangement")) {
4709 } else if (!strcasecmp(chargeditem, "BasicCommunication")) {
4711 } else if (!strcasecmp(chargeditem, "CallAttempt")) {
4712 _chargeditem = AST_AOC_CHARGED_ITEM_CALL_ATTEMPT;
4713 } else if (!strcasecmp(chargeditem, "CallSetup")) {
4714 _chargeditem = AST_AOC_CHARGED_ITEM_CALL_SETUP;
4715 } else if (!strcasecmp(chargeditem, "UserUserInfo")) {
4717 } else if (!strcasecmp(chargeditem, "SupplementaryService")) {
4719 } else {
4720 astman_send_error(s, m, "Invalid ChargedItem");
4721 goto aocmessage_cleanup;
4722 }
4723
4724 if (!strcasecmp(ratetype, "NA")) {
4725 _ratetype = AST_AOC_RATE_TYPE_NA;
4726 } else if (!strcasecmp(ratetype, "Free")) {
4727 _ratetype = AST_AOC_RATE_TYPE_FREE;
4728 } else if (!strcasecmp(ratetype, "FreeFromBeginning")) {
4730 } else if (!strcasecmp(ratetype, "Duration")) {
4731 _ratetype = AST_AOC_RATE_TYPE_DURATION;
4732 } else if (!strcasecmp(ratetype, "Flat")) {
4733 _ratetype = AST_AOC_RATE_TYPE_FLAT;
4734 } else if (!strcasecmp(ratetype, "Volume")) {
4735 _ratetype = AST_AOC_RATE_TYPE_VOLUME;
4736 } else if (!strcasecmp(ratetype, "SpecialCode")) {
4738 } else {
4739 astman_send_error(s, m, "Invalid RateType");
4740 goto aocmessage_cleanup;
4741 }
4742
4743 if (_ratetype > AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING) {
4744 if (ast_strlen_zero(currencyamount) || (sscanf(currencyamount, "%30u",
4745 &_currencyamount) != 1)) {
4746 astman_send_error(s, m, "Invalid CurrencyAmount, CurrencyAmount is a required when RateType is non-free");
4747 goto aocmessage_cleanup;
4748 }
4749
4750 if (ast_strlen_zero(mult)) {
4751 astman_send_error(s, m, "ChargeMultiplier unspecified, ChargeMultiplier is required when ChargeType is Currency.");
4752 goto aocmessage_cleanup;
4753 } else if (!strcasecmp(mult, "onethousandth")) {
4755 } else if (!strcasecmp(mult, "onehundredth")) {
4757 } else if (!strcasecmp(mult, "onetenth")) {
4758 _mult = AST_AOC_MULT_ONETENTH;
4759 } else if (!strcasecmp(mult, "one")) {
4760 _mult = AST_AOC_MULT_ONE;
4761 } else if (!strcasecmp(mult, "ten")) {
4762 _mult = AST_AOC_MULT_TEN;
4763 } else if (!strcasecmp(mult, "hundred")) {
4764 _mult = AST_AOC_MULT_HUNDRED;
4765 } else if (!strcasecmp(mult, "thousand")) {
4766 _mult = AST_AOC_MULT_THOUSAND;
4767 } else {
4768 astman_send_error(s, m, "Invalid ChargeMultiplier");
4769 goto aocmessage_cleanup;
4770 }
4771 }
4772
4773 if (_ratetype == AST_AOC_RATE_TYPE_DURATION) {
4774 if (ast_strlen_zero(timescale)) {
4775 astman_send_error(s, m, "TimeScale unspecified, TimeScale is required when RateType is Duration.");
4776 goto aocmessage_cleanup;
4777 } else if (!strcasecmp(timescale, "onehundredthsecond")) {
4779 } else if (!strcasecmp(timescale, "onetenthsecond")) {
4781 } else if (!strcasecmp(timescale, "second")) {
4783 } else if (!strcasecmp(timescale, "tenseconds")) {
4785 } else if (!strcasecmp(timescale, "minute")) {
4787 } else if (!strcasecmp(timescale, "hour")) {
4788 _scale = AST_AOC_TIME_SCALE_HOUR;
4789 } else if (!strcasecmp(timescale, "day")) {
4790 _scale = AST_AOC_TIME_SCALE_DAY;
4791 } else {
4792 astman_send_error(s, m, "Invalid TimeScale");
4793 goto aocmessage_cleanup;
4794 }
4795
4796 if (ast_strlen_zero(time) || (sscanf(time, "%30u", &_time) != 1)) {
4797 astman_send_error(s, m, "Invalid Time, Time is a required when RateType is Duration");
4798 goto aocmessage_cleanup;
4799 }
4800
4801 if (!ast_strlen_zero(granularity)) {
4802 if ((sscanf(time, "%30u", &_granularity) != 1)) {
4803 astman_send_error(s, m, "Invalid Granularity");
4804 goto aocmessage_cleanup;
4805 }
4806
4807 if (ast_strlen_zero(granularitytimescale)) {
4808 astman_send_error(s, m, "Invalid GranularityTimeScale, GranularityTimeScale is a required when Granularity is specified");
4809 } else if (!strcasecmp(granularitytimescale, "onehundredthsecond")) {
4810 _granularity_time_scale = AST_AOC_TIME_SCALE_HUNDREDTH_SECOND;
4811 } else if (!strcasecmp(granularitytimescale, "onetenthsecond")) {
4812 _granularity_time_scale = AST_AOC_TIME_SCALE_TENTH_SECOND;
4813 } else if (!strcasecmp(granularitytimescale, "second")) {
4814 _granularity_time_scale = AST_AOC_TIME_SCALE_SECOND;
4815 } else if (!strcasecmp(granularitytimescale, "tenseconds")) {
4816 _granularity_time_scale = AST_AOC_TIME_SCALE_TEN_SECOND;
4817 } else if (!strcasecmp(granularitytimescale, "minute")) {
4818 _granularity_time_scale = AST_AOC_TIME_SCALE_MINUTE;
4819 } else if (!strcasecmp(granularitytimescale, "hour")) {
4820 _granularity_time_scale = AST_AOC_TIME_SCALE_HOUR;
4821 } else if (!strcasecmp(granularitytimescale, "day")) {
4822 _granularity_time_scale = AST_AOC_TIME_SCALE_DAY;
4823 } else {
4824 astman_send_error(s, m, "Invalid GranularityTimeScale");
4825 goto aocmessage_cleanup;
4826 }
4827 }
4828
4829 if (ast_strlen_zero(chargingtype) || strcasecmp(chargingtype, "continuouscharging") == 0) {
4830 _step = 0;
4831 } else if (strcasecmp(chargingtype, "stepfunction") == 0 ) {
4832 _step = 1;
4833 } else {
4834 astman_send_error(s, m, "Invalid ChargingType");
4835 goto aocmessage_cleanup;
4836 }
4837 }
4838
4839 if (_ratetype == AST_AOC_RATE_TYPE_VOLUME) {
4840 if (ast_strlen_zero(volumeunit)) {
4841 astman_send_error(s, m, "VolumeUnit unspecified, VolumeUnit is required when RateType is Volume.");
4842 goto aocmessage_cleanup;
4843 } else if (!strcasecmp(timescale, "octet")) {
4844 _volumeunit = AST_AOC_VOLUME_UNIT_OCTET;
4845 } else if (!strcasecmp(timescale, "segment")) {
4846 _volumeunit = AST_AOC_VOLUME_UNIT_SEGMENT;
4847 } else if (!strcasecmp(timescale, "message")) {
4848 _volumeunit = AST_AOC_VOLUME_UNIT_MESSAGE;
4849 }else {
4850 astman_send_error(s, m, "Invalid VolumeUnit");
4851 goto aocmessage_cleanup;
4852 }
4853 }
4854
4856 || _ratetype == AST_AOC_RATE_TYPE_SPECIAL_CODE) {
4857 if (ast_strlen_zero(code) || (sscanf(code, "%30u", &_code) != 1)) {
4858 astman_send_error(s, m, "Invalid Code, Code is a required when ChargedItem is SpecialArrangement and when RateType is SpecialCode");
4859 goto aocmessage_cleanup;
4860 }
4861 }
4862
4863 if (_chargeditem == AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT) {
4864 ast_aoc_s_add_special_arrangement(decoded, _code);
4865 } else if (_ratetype == AST_AOC_RATE_TYPE_DURATION) {
4866 ast_aoc_s_add_rate_duration(decoded, _chargeditem, _currencyamount, _mult,
4867 currencyname, _time, _scale, _granularity, _granularity_time_scale, _step);
4868 } else if (_ratetype == AST_AOC_RATE_TYPE_FLAT) {
4869 ast_aoc_s_add_rate_flat(decoded, _chargeditem, _currencyamount, _mult,
4870 currencyname);
4871 } else if (_ratetype == AST_AOC_RATE_TYPE_VOLUME) {
4872 ast_aoc_s_add_rate_volume(decoded, _chargeditem, _volumeunit, _currencyamount,
4873 _mult, currencyname);
4874 } else if (_ratetype == AST_AOC_RATE_TYPE_SPECIAL_CODE) {
4875 ast_aoc_s_add_rate_special_charge_code(decoded, _chargeditem, _code);
4876 } else if (_ratetype == AST_AOC_RATE_TYPE_FREE) {
4877 ast_aoc_s_add_rate_free(decoded, _chargeditem, 0);
4878 } else if (_ratetype == AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING) {
4879 ast_aoc_s_add_rate_free(decoded, _chargeditem, 1);
4880 } else if (_ratetype == AST_AOC_RATE_TYPE_NA) {
4881 ast_aoc_s_add_rate_na(decoded, _chargeditem);
4882 }
4883
4884 return 0;
4885
4886aocmessage_cleanup:
4887
4888 return -1;
4889}
4890
4892 const struct message *m)
4893{
4894 struct ast_aoc_decoded *decoded = NULL;
4895 int hdrlen;
4896 int x;
4897 static const char hdr[] = "ChargedItem:";
4898 struct message sm = { 0 };
4899 int rates = 0;
4900
4901 if (!(decoded = ast_aoc_create(AST_AOC_S, 0, 0))) {
4902 astman_send_error(s, m, "Message Creation Failed");
4903 goto aocmessage_cleanup;
4904 }
4905
4906 hdrlen = strlen(hdr);
4907 for (x = 0; x < m->hdrcount; x++) {
4908 if (strncasecmp(hdr, m->headers[x], hdrlen) == 0) {
4909 if (rates > ast_aoc_s_get_count(decoded)) {
4910 if (action_aoc_s_submessage(s, &sm, decoded) == -1) {
4911 goto aocmessage_cleanup;
4912 }
4913 }
4914 ++rates;
4915 }
4916
4917 sm.headers[sm.hdrcount] = m->headers[x];
4918 ++sm.hdrcount;
4919 }
4920 if (rates > ast_aoc_s_get_count(decoded)) {
4921 if (action_aoc_s_submessage(s, &sm, decoded) == -1) {
4922 goto aocmessage_cleanup;
4923 }
4924 }
4925
4926 return decoded;
4927
4928aocmessage_cleanup:
4929
4930 ast_aoc_destroy_decoded(decoded);
4931 return NULL;
4932}
4933
4934static int action_aocmessage(struct mansession *s, const struct message *m)
4935{
4936 const char *msgtype = astman_get_header(m, "MsgType");
4937 const char *channel = astman_get_header(m, "Channel");
4938 const char *pchannel = astman_get_header(m, "ChannelPrefix");
4939
4940 struct ast_channel *chan = NULL;
4941
4942 struct ast_aoc_decoded *decoded = NULL;
4943 struct ast_aoc_encoded *encoded = NULL;
4944 size_t encoded_size = 0;
4945
4946 if (ast_strlen_zero(channel) && ast_strlen_zero(pchannel)) {
4947 astman_send_error(s, m, "Channel and PartialChannel are not specified. Specify at least one of these.");
4948 goto aocmessage_cleanup;
4949 }
4950
4951 if (!(chan = ast_channel_get_by_name(channel)) && !ast_strlen_zero(pchannel)) {
4952 chan = ast_channel_get_by_name_prefix(pchannel, strlen(pchannel));
4953 }
4954
4955 if (!chan) {
4956 astman_send_error(s, m, "No such channel");
4957 goto aocmessage_cleanup;
4958 }
4959
4960 if (strcasecmp(msgtype, "d") == 0 || strcasecmp(msgtype, "e") == 0) {
4961 decoded = action_aoc_de_message(s, m);
4962 }
4963 else if (strcasecmp(msgtype, "s") == 0) {
4964 decoded = action_aoc_s_message(s, m);
4965 }
4966 else {
4967 astman_send_error(s, m, "Invalid MsgType");
4968 goto aocmessage_cleanup;
4969 }
4970
4971 if (!decoded) {
4972 goto aocmessage_cleanup;
4973 }
4974
4975 if ((encoded = ast_aoc_encode(decoded, &encoded_size, chan))
4976 && !ast_indicate_data(chan, AST_CONTROL_AOC, encoded, encoded_size)) {
4977 astman_send_ack(s, m, "AOC Message successfully queued on channel");
4978 } else {
4979 astman_send_error(s, m, "Error encoding AOC message, could not queue onto channel");
4980 }
4981
4982aocmessage_cleanup:
4983
4984 ast_aoc_destroy_decoded(decoded);
4985 ast_aoc_destroy_encoded(encoded);
4986
4987 if (chan) {
4988 chan = ast_channel_unref(chan);
4989 }
4990 return 0;
4991}
4992
4994 const char *search;
4996 int (*searchfn)(const char *app, const char *data, const char *search);
4997};
4998
4999/*!
5000 * \internal
5001 * \brief Check if the application is allowed for Originate
5002 *
5003 * \param app The "app" parameter
5004 * \param data The "appdata" parameter (ignored)
5005 * \param search The search string
5006 * \retval 1 Match
5007 * \retval 0 No match
5008 */
5009static int app_match(const char *app, const char *data, const char *search)
5010{
5011 /*
5012 * We use strcasestr so we don't have to trim any blanks
5013 * from the front or back of the string.
5014 */
5015 return !!(strcasestr(app, search));
5016}
5017
5018/*!
5019 * \internal
5020 * \brief Check if the appdata is allowed for Originate
5021 *
5022 * \param app The "app" parameter (ignored)
5023 * \param data The "appdata" parameter
5024 * \param search The search string
5025 * \retval 1 Match
5026 * \retval 0 No match
5027 */
5028static int appdata_match(const char *app, const char *data, const char *search)
5029{
5030 if (ast_strlen_zero(data)) {
5031 return 0;
5032 }
5033 return !!(strstr(data, search));
5034}
5035
5036/*!
5037 * \internal
5038 * \brief Check if the Queue application is allowed for Originate
5039 *
5040 * It's only allowed if there's no AGI parameter set
5041 *
5042 * \param app The "app" parameter
5043 * \param data The "appdata" parameter
5044 * \param search The search string
5045 * \retval 1 Match
5046 * \retval 0 No match
5047 */
5048static int queue_match(const char *app, const char *data, const char *search)
5049{
5050 char *parse;
5052 AST_APP_ARG(queuename);
5055 AST_APP_ARG(announceoverride);
5056 AST_APP_ARG(queuetimeoutstr);
5057 AST_APP_ARG(agi);
5058 AST_APP_ARG(gosub);
5060 AST_APP_ARG(position);
5061 );
5062
5063 if (!strcasestr(app, "queue") || ast_strlen_zero(data)) {
5064 return 0;
5065 }
5066
5067 parse = ast_strdupa(data);
5069
5070 /*
5071 * The Queue application is fine unless the AGI parameter is set.
5072 * If it is, we need to check the user's permissions.
5073 */
5074 return !ast_strlen_zero(args.agi);
5075}
5076
5077/*
5078 * The Originate application and application data are passed
5079 * to each searchfn in the list. If a searchfn returns true
5080 * and the user's permissions don't include the permissions specified
5081 * in the list entry, the Originate action will be denied.
5082 *
5083 * If no searchfn returns true, the Originate action is allowed.
5084 */
5086 /*
5087 * The app_match function checks if the search string is
5088 * anywhere in the app parameter. The check is case-insensitive.
5089 */
5090 { "agi", EVENT_FLAG_SYSTEM, app_match },
5091 { "dbdeltree", EVENT_FLAG_SYSTEM, app_match },
5092 { "exec", EVENT_FLAG_SYSTEM, app_match },
5093 { "externalivr", EVENT_FLAG_SYSTEM, app_match },
5094 { "mixmonitor", EVENT_FLAG_SYSTEM, app_match },
5095 { "originate", EVENT_FLAG_SYSTEM, app_match },
5096 { "reload", EVENT_FLAG_SYSTEM, app_match },
5097 { "system", EVENT_FLAG_SYSTEM, app_match },
5098 /*
5099 * Since the queue_match function specifically checks
5100 * for the presence of the AGI parameter, we'll allow
5101 * the call if the user has either the AGI or SYSTEM
5102 * permission.
5103 */
5105 /*
5106 * The appdata_match function checks if the search string is
5107 * anywhere in the appdata parameter. Unlike app_match,
5108 * the check is case-sensitive. These are generally
5109 * dialplan functions.
5110 */
5111 { "CURL", EVENT_FLAG_SYSTEM, appdata_match },
5113 { "EVAL", EVENT_FLAG_SYSTEM, appdata_match },
5114 { "FILE", EVENT_FLAG_SYSTEM, appdata_match },
5115 { "ODBC", EVENT_FLAG_SYSTEM, appdata_match },
5116 { "REALTIME", EVENT_FLAG_SYSTEM, appdata_match },
5117 { "SHELL", EVENT_FLAG_SYSTEM, appdata_match },
5118 { NULL, 0 },
5119};
5120
5121static int is_originate_app_permitted(const char *app, const char *data,
5122 int permission)
5123{
5124 int i;
5125
5126 for (i = 0; originate_app_permissions[i].search; i++) {
5127 if (originate_app_permissions[i].searchfn(app, data, originate_app_permissions[i].search)) {
5129 }
5130 }
5131
5132 return 1;
5133}
5134
5135#ifdef TEST_FRAMEWORK
5136#define ALL_PERMISSIONS (INT_MAX)
5137#define NO_PERMISSIONS (0)
5138AST_TEST_DEFINE(originate_permissions_test)
5139{
5141
5142 switch (cmd) {
5143 case TEST_INIT:
5144 info->name = "originate_permissions_test";
5145 info->category = "/main/manager/";
5146 info->summary = "Test permissions for originate action";
5147 info->description =
5148 "Make sure that dialplan apps/functions that need special "
5149 "permissions are prohibited if the user doesn't have the permission.";
5150 return AST_TEST_NOT_RUN;
5151 case TEST_EXECUTE:
5152 break;
5153 }
5154
5155 /*
5156 * Check application matching. We don't need to check every one.
5157 * The code is the same.
5158 */
5159
5160 ast_test_validate(test, is_originate_app_permitted("exec",
5161 NULL, EVENT_FLAG_SYSTEM), "exec permission check failed");
5162 ast_test_validate(test, is_originate_app_permitted("exec",
5163 NULL, EVENT_FLAG_SYSTEM | EVENT_FLAG_AGI), "exec check permission failed");
5164 ast_test_validate(test, is_originate_app_permitted("exec",
5165 NULL, ALL_PERMISSIONS), "exec check permission failed");
5166 ast_test_validate(test, !is_originate_app_permitted("exec",
5167 NULL, EVENT_FLAG_AGI), "exec permission check failed");
5168 ast_test_validate(test, !is_originate_app_permitted("exec",
5169 NULL, EVENT_FLAG_VERBOSE), "exec permission check failed");
5170 ast_test_validate(test, !is_originate_app_permitted("exec",
5171 NULL, NO_PERMISSIONS), "exec permission check failed");
5172
5173 /*
5174 * If queue is used with the AGI parameter but without the SYSTEM or AGI
5175 * permission, it should be denied. Queue param order:
5176 * queuename,options,url,announceoverride,queuetimeoutstr,AGI,gosub,rule,position
5177 * The values of the options aren't checked. They just have to be present.
5178 */
5179
5180 /* AGI not specified should always be allowed */
5181 ast_test_validate(test, is_originate_app_permitted("queue",
5182 NULL, NO_PERMISSIONS), "Queue permission check failed");
5183 ast_test_validate(test, is_originate_app_permitted("queue",
5184 "somequeue,CcdHh,someURL,tt-monkeys,100,,gosub,rule,666",
5185 EVENT_FLAG_ORIGINATE | EVENT_FLAG_HOOKRESPONSE ), "Queue permission check failed");
5186
5187 /* AGI specified with SYSTEM or AGI permission should be allowed */
5188 ast_test_validate(test, is_originate_app_permitted("queue",
5189 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5190 EVENT_FLAG_SYSTEM | EVENT_FLAG_HOOKRESPONSE ), "Queue permission check failed");
5191 ast_test_validate(test, is_originate_app_permitted("queue",
5192 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5193 EVENT_FLAG_AGI | EVENT_FLAG_HOOKRESPONSE ), "Queue permission check failed");
5194 ast_test_validate(test, is_originate_app_permitted("queue",
5195 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5196 ALL_PERMISSIONS), "Queue permission check failed");
5197
5198 /* AGI specified without SYSTEM or AGI permission should be denied */
5199 ast_test_validate(test, !is_originate_app_permitted("queue",
5200 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5201 NO_PERMISSIONS), "Queue permission check failed");
5202 ast_test_validate(test, !is_originate_app_permitted("queue",
5203 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5204 EVENT_FLAG_ORIGINATE | EVENT_FLAG_HOOKRESPONSE ), "Queue permission check failed");
5205
5206 /*
5207 * Check appdata. The function name can appear anywhere in appdata.
5208 */
5209 ast_test_validate(test, is_originate_app_permitted("someapp",
5210 "aaaDBbbb", EVENT_FLAG_SYSTEM), "exec permission check failed");
5211 ast_test_validate(test, is_originate_app_permitted("someapp",
5212 "aaa DB bbb", ALL_PERMISSIONS), "exec permission check failed");
5213 ast_test_validate(test, !is_originate_app_permitted("someapp",
5214 "aaaDBbbb", NO_PERMISSIONS), "exec permission check failed");
5215 ast_test_validate(test, !is_originate_app_permitted("someapp",
5216 "aaa DB bbb", NO_PERMISSIONS), "exec permission check failed");
5217 /* The check is case-sensitive so although DB is a match, db isn't. */
5218 ast_test_validate(test, is_originate_app_permitted("someapp",
5219 "aaa db bbb", NO_PERMISSIONS), "exec permission check failed");
5220
5221 return res;
5222}
5223#undef ALL_PERMISSIONS
5224#undef NO_PERMISSIONS
5225#endif
5226
5227static int action_originate(struct mansession *s, const struct message *m)
5228{
5229 const char *name = astman_get_header(m, "Channel");
5230 const char *exten = astman_get_header(m, "Exten");
5231 const char *context = astman_get_header(m, "Context");
5232 const char *priority = astman_get_header(m, "Priority");
5233 const char *timeout = astman_get_header(m, "Timeout");
5234 const char *callerid = astman_get_header(m, "CallerID");
5235 const char *account = astman_get_header(m, "Account");
5236 const char *app = astman_get_header(m, "Application");
5237 const char *appdata = astman_get_header(m, "Data");
5238 const char *async = astman_get_header(m, "Async");
5239 const char *id = astman_get_header(m, "ActionID");
5240 const char *codecs = astman_get_header(m, "Codecs");
5241 const char *early_media = astman_get_header(m, "Earlymedia");
5242 struct ast_assigned_ids assignedids = {
5243 .uniqueid = astman_get_header(m, "ChannelId"),
5244 .uniqueid2 = astman_get_header(m, "OtherChannelId"),
5245 };
5246 const char *gosub = astman_get_header(m, "PreDialGoSub");
5247
5248 struct ast_variable *vars = NULL;
5249 char *tech, *data;
5250 char *l = NULL, *n = NULL;
5251 int pi = 0;
5252 int res;
5253 int to = 30000;
5254 int reason = 0;
5255 char tmp[256];
5256 char tmp2[256];
5258 pthread_t th;
5259 int bridge_early = 0;
5260
5261 if (!cap) {
5262 astman_send_error(s, m, "Internal Error. Memory allocation failure.");
5263 return 0;
5264 }
5266
5267 if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid))
5268 || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) {
5269 astman_send_error_va(s, m, "Uniqueid length exceeds maximum of %d\n",
5271 res = 0;
5272 goto fast_orig_cleanup;
5273 }
5274
5275 if (ast_strlen_zero(name)) {
5276 astman_send_error(s, m, "Channel not specified");
5277 res = 0;
5278 goto fast_orig_cleanup;
5279 }
5280 if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
5281 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
5282 astman_send_error(s, m, "Invalid priority");
5283 res = 0;
5284 goto fast_orig_cleanup;
5285 }
5286 }
5287 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
5288 astman_send_error(s, m, "Invalid timeout");
5289 res = 0;
5290 goto fast_orig_cleanup;
5291 }
5292 ast_copy_string(tmp, name, sizeof(tmp));
5293 tech = tmp;
5294 data = strchr(tmp, '/');
5295 if (!data) {
5296 astman_send_error(s, m, "Invalid channel");
5297 res = 0;
5298 goto fast_orig_cleanup;
5299 }
5300 *data++ = '\0';
5301 ast_copy_string(tmp2, callerid, sizeof(tmp2));
5302 ast_callerid_parse(tmp2, &n, &l);
5303 if (n) {
5304 if (ast_strlen_zero(n)) {
5305 n = NULL;
5306 }
5307 }
5308 if (l) {
5310 if (ast_strlen_zero(l)) {
5311 l = NULL;
5312 }
5313 }
5314 if (!ast_strlen_zero(codecs)) {
5317 }
5318
5319 if (!ast_strlen_zero(app) && s->session) {
5320 if (!is_originate_app_permitted(app, appdata, s->session->writeperm)) {
5321 astman_send_error(s, m, "Originate Access Forbidden: app or data blacklisted");
5322 res = 0;
5323 goto fast_orig_cleanup;
5324 }
5325 }
5326
5327 /* Check early if the extension exists. If not, we need to bail out here. */
5328 if (exten && context && pi) {
5329 if (! ast_exists_extension(NULL, context, exten, pi, l)) {
5330 /* The extension does not exist. */
5331 astman_send_error(s, m, "Extension does not exist.");
5332 res = 0;
5333 goto fast_orig_cleanup;
5334 }
5335 }
5336
5337 /* Allocate requested channel variables */
5338 vars = astman_get_variables(m);
5339 if (s->session && s->session->chanvars) {
5340 struct ast_variable *v, *old;
5341 old = vars;
5342 vars = NULL;
5343
5344 /* The variables in the AMI originate action are appended at the end of the list, to override any user variables that apply */
5345
5347 if (old) {
5348 for (v = vars; v->next; v = v->next );
5349 v->next = old; /* Append originate variables at end of list */
5350 }
5351 }
5352
5353 /* For originate async - we can bridge in early media stage */
5354 bridge_early = ast_true(early_media);
5355
5356 if (ast_true(async)) {
5357 struct fast_originate_helper *fast;
5358
5359 fast = ast_calloc(1, sizeof(*fast));
5360 if (!fast || ast_string_field_init(fast, 252)) {
5361 ast_free(fast);
5363 res = -1;
5364 } else {
5365 if (!ast_strlen_zero(id)) {
5366 ast_string_field_build(fast, idtext, "ActionID: %s\r\n", id);
5367 }
5368 ast_string_field_set(fast, tech, tech);
5369 ast_string_field_set(fast, data, data);
5371 ast_string_field_set(fast, appdata, appdata);
5372 ast_string_field_set(fast, cid_num, l);
5373 ast_string_field_set(fast, cid_name, n);
5375 ast_string_field_set(fast, exten, exten);
5376 ast_string_field_set(fast, account, account);
5377 ast_string_field_set(fast, channelid, assignedids.uniqueid);
5378 ast_string_field_set(fast, otherchannelid, assignedids.uniqueid2);
5379 fast->vars = vars;
5380 fast->cap = cap;
5381 cap = NULL; /* transferred originate helper the capabilities structure. It is now responsible for freeing it. */
5382 fast->timeout = to;
5383 fast->early_media = bridge_early;
5384 fast->priority = pi;
5387 res = -1;
5388 } else {
5389 res = 0;
5390 }
5391 }
5392 } else if (!ast_strlen_zero(app)) {
5393 res = ast_pbx_outgoing_app(tech, cap, data, to, app, appdata, &reason,
5394 AST_OUTGOING_WAIT, l, n, vars, account, NULL,
5395 assignedids.uniqueid ? &assignedids : NULL);
5397 } else {
5398 if (exten && context && pi) {
5399 res = ast_pbx_outgoing_exten_predial(tech, cap, data, to,
5400 context, exten, pi, &reason, AST_OUTGOING_WAIT,
5401 l, n, vars, account, NULL, bridge_early,
5402 assignedids.uniqueid ? &assignedids : NULL , gosub);
5404 } else {
5405 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
5407 res = 0;
5408 goto fast_orig_cleanup;
5409 }
5410 }
5411 if (!res) {
5412 astman_send_ack(s, m, "Originate successfully queued");
5413 } else {
5414 astman_send_error(s, m, "Originate failed");
5415 }
5416
5417fast_orig_cleanup:
5418 ao2_cleanup(cap);
5419 return 0;
5420}
5421
5422static int action_mailboxstatus(struct mansession *s, const struct message *m)
5423{
5424 const char *mailbox = astman_get_header(m, "Mailbox");
5425 int ret;
5426
5427 if (ast_strlen_zero(mailbox)) {
5428 astman_send_error(s, m, "Mailbox not specified");
5429 return 0;
5430 }
5432 astman_start_ack(s, m);
5433 astman_append(s, "Message: Mailbox Status\r\n"
5434 "Mailbox: %s\r\n"
5435 "Waiting: %d\r\n\r\n", mailbox, ret);
5436 return 0;
5437}
5438
5439static int action_mailboxcount(struct mansession *s, const struct message *m)
5440{
5441 const char *mailbox = astman_get_header(m, "Mailbox");
5442 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
5443
5444 if (ast_strlen_zero(mailbox)) {
5445 astman_send_error(s, m, "Mailbox not specified");
5446 return 0;
5447 }
5448 ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
5449 astman_start_ack(s, m);
5450 astman_append(s, "Message: Mailbox Message Count\r\n"
5451 "Mailbox: %s\r\n"
5452 "UrgMessages: %d\r\n"
5453 "NewMessages: %d\r\n"
5454 "OldMessages: %d\r\n"
5455 "\r\n",
5456 mailbox, urgentmsgs, newmsgs, oldmsgs);
5457 return 0;
5458}
5459
5460static int action_extensionstate(struct mansession *s, const struct message *m)
5461{
5462 const char *exten = astman_get_header(m, "Exten");
5463 const char *context = astman_get_header(m, "Context");
5464 char hint[256];
5465 int status;
5466
5467 if (ast_strlen_zero(exten)) {
5468 astman_send_error(s, m, "Extension not specified");
5469 return 0;
5470 }
5471 if (ast_strlen_zero(context)) {
5472 context = "default";
5473 }
5475 hint[0] = '\0';
5476 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
5477 astman_start_ack(s, m);
5478 astman_append(s, "Message: Extension Status\r\n"
5479 "Exten: %s\r\n"
5480 "Context: %s\r\n"
5481 "Hint: %s\r\n"
5482 "Status: %d\r\n"
5483 "StatusText: %s\r\n"
5484 "\r\n",
5485 exten, context, hint, status,
5487 return 0;
5488}
5489
5490static int action_presencestate(struct mansession *s, const struct message *m)
5491{
5492 const char *provider = astman_get_header(m, "Provider");
5494 char *subtype;
5495 char *message;
5496
5498 astman_send_error(s, m, "No provider specified");
5499 return 0;
5500 }
5501
5503 if (state == AST_PRESENCE_INVALID) {
5504 astman_send_error_va(s, m, "Invalid provider %s or provider in invalid state", provider);
5505 return 0;
5506 }
5507
5508 astman_start_ack(s, m);
5509 astman_append(s, "Message: Presence State\r\n"
5510 "State: %s\r\n", ast_presence_state2str(state));
5511
5512 if (!ast_strlen_zero(subtype)) {
5513 astman_append(s, "Subtype: %s\r\n", subtype);
5514 }
5515
5516 if (!ast_strlen_zero(message)) {
5517 /* XXX The Message header here is deprecated as it
5518 * duplicates the action response header 'Message'.
5519 * Remove it in the next major revision of AMI.
5520 */
5521 astman_append(s, "Message: %s\r\n"
5522 "PresenceMessage: %s\r\n",
5523 message, message);
5524 }
5525 astman_append(s, "\r\n");
5526
5527 ast_free(subtype);
5529
5530 return 0;
5531}
5532
5533static int action_timeout(struct mansession *s, const struct message *m)
5534{
5535 struct ast_channel *c;
5536 const char *name = astman_get_header(m, "Channel");
5537 double timeout = atof(astman_get_header(m, "Timeout"));
5538 struct timeval when = { timeout, 0 };
5539
5540 if (ast_strlen_zero(name)) {
5541 astman_send_error(s, m, "No channel specified");
5542 return 0;
5543 }
5544
5545 if (!timeout || timeout < 0) {
5546 astman_send_error(s, m, "No timeout specified");
5547 return 0;
5548 }
5549
5550 if (!(c = ast_channel_get_by_name(name))) {
5551 astman_send_error(s, m, "No such channel");
5552 return 0;
5553 }
5554
5555 when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
5556
5561
5562 astman_send_ack(s, m, "Timeout Set");
5563
5564 return 0;
5565}
5566
5567/*!
5568 * \brief Test eventdata against a filter entry
5569 *
5570 * \param entry The event_filter entry to match with
5571 * \param eventdata The data to match against
5572 * \retval 0 if no match
5573 * \retval 1 if match
5574 */
5575static int match_eventdata(struct event_filter_entry *entry, const char *eventdata)
5576{
5577 switch(entry->match_type) {
5578 case FILTER_MATCH_REGEX:
5579 return regexec(entry->regex_filter, eventdata, 0, NULL, 0) == 0;
5581 return ast_begins_with(eventdata, entry->string_filter);
5583 return ast_ends_with(eventdata, entry->string_filter);
5585 return strstr(eventdata, entry->string_filter) != NULL;
5586 case FILTER_MATCH_EXACT:
5587 return strcmp(eventdata, entry->string_filter) == 0;
5588 case FILTER_MATCH_NONE:
5589 return 1;
5590 }
5591
5592 return 0;
5593}
5594
5595static int filter_cmp_fn(void *obj, void *arg, void *data, int flags)
5596{
5597 struct eventqent *eqe = arg;
5598 struct event_filter_entry *filter_entry = obj;
5599 char *line_buffer_start = NULL;
5600 char *line_buffer = NULL;
5601 char *line = NULL;
5602 int match = 0;
5603 int *result = data;
5604
5605 if (filter_entry->event_name_hash) {
5606 if (eqe->event_name_hash != filter_entry->event_name_hash) {
5607 goto done;
5608 }
5609 }
5610
5611 /* We're looking at the entire event data */
5612 if (!filter_entry->header_name) {
5613 match = match_eventdata(filter_entry, eqe->eventdata);
5614 goto done;
5615 }
5616
5617 /* We're looking at a specific header */
5618 line_buffer_start = ast_strdup(eqe->eventdata);
5619 line_buffer = line_buffer_start;
5620 if (!line_buffer_start) {
5621 goto done;
5622 }
5623
5624 while ((line = ast_read_line_from_buffer(&line_buffer))) {
5625 if (ast_begins_with(line, filter_entry->header_name)) {
5626 line += strlen(filter_entry->header_name);
5627 line = ast_skip_blanks(line);
5628 if (ast_strlen_zero(line)) {
5629 continue;
5630 }
5631 match = match_eventdata(filter_entry, line);
5632 if (match) {
5633 ast_free(line_buffer_start);
5634 line_buffer_start = NULL;
5635 break;
5636 }
5637 }
5638 }
5639
5640 ast_free(line_buffer_start);
5641
5642done:
5643
5644 *result = match;
5645 return match ? CMP_MATCH | CMP_STOP : 0;
5646}
5647
5648static int should_send_event(struct ao2_container *includefilters,
5649 struct ao2_container *excludefilters, struct eventqent *eqe)
5650{
5651 int result = 0;
5652
5653 if (manager_debug) {
5654 ast_verbose("<-- Examining AMI event (%u): -->\n%s\n", eqe->event_name_hash, eqe->eventdata);
5655 } else {
5656 ast_debug(4, "Examining AMI event (%u):\n%s\n", eqe->event_name_hash, eqe->eventdata);
5657 }
5658 if (!ao2_container_count(includefilters) && !ao2_container_count(excludefilters)) {
5659 return 1; /* no filtering means match all */
5660 } else if (ao2_container_count(includefilters) && !ao2_container_count(excludefilters)) {
5661 /* include filters only: implied exclude all filter processed first, then include filters */
5662 ao2_t_callback_data(includefilters, OBJ_NODATA, filter_cmp_fn, eqe, &result, "find filter in includefilters container");
5663 return result;
5664 } else if (!ao2_container_count(includefilters) && ao2_container_count(excludefilters)) {
5665 /* exclude filters only: implied include all filter processed first, then exclude filters */
5666 ao2_t_callback_data(excludefilters, OBJ_NODATA, filter_cmp_fn, eqe, &result, "find filter in excludefilters container");
5667 return !result;
5668 } else {
5669 /* include and exclude filters: implied exclude all filter processed first, then include filters, and lastly exclude filters */
5670 ao2_t_callback_data(includefilters, OBJ_NODATA, filter_cmp_fn, eqe, &result, "find filter in session filter container");
5671 if (result) {
5672 result = 0;
5673 ao2_t_callback_data(excludefilters, OBJ_NODATA, filter_cmp_fn, eqe, &result, "find filter in session filter container");
5674 return !result;
5675 }
5676 }
5677
5678 return result;
5679}
5680
5681/*!
5682 * \brief Manager command to add an event filter to a manager session
5683 * \see For more details look at manager_add_filter
5684 */
5685static int action_filter(struct mansession *s, const struct message *m)
5686{
5687 const char *match_criteria = astman_get_header(m, "MatchCriteria");
5688 const char *filter = astman_get_header(m, "Filter");
5689 const char *operation = astman_get_header(m, "Operation");
5690 int res;
5691
5692 if (!strcasecmp(operation, "Add")) {
5693 char *criteria;
5694 int have_match = !ast_strlen_zero(match_criteria);
5695
5696 /* Create an eventfilter expression.
5697 * eventfilter[(match_criteria)]
5698 */
5699 res = ast_asprintf(&criteria, "eventfilter%s%s%s",
5700 S_COR(have_match, "(", ""), S_OR(match_criteria, ""),
5701 S_COR(have_match, ")", ""));
5702 if (res <= 0) {
5703 astman_send_error(s, m, "Internal Error. Failed to allocate storage for filter type");
5704 return 0;
5705 }
5706
5708 ast_std_free(criteria);
5709 if (res != FILTER_SUCCESS) {
5710 if (res == FILTER_ALLOC_FAILED) {
5711 astman_send_error(s, m, "Internal Error. Failed to allocate regex for filter");
5712 return 0;
5713 } else if (res == FILTER_COMPILE_FAIL) {
5714 astman_send_error(s, m,
5715 "Filter did not compile. Check the syntax of the filter given.");
5716 return 0;
5717 } else if (res == FILTER_FORMAT_ERROR) {
5718 astman_send_error(s, m,
5719 "Filter was formatted incorrectly. Check the syntax of the filter given.");
5720 return 0;
5721 } else {
5722 astman_send_error(s, m, "Internal Error. Failed adding filter.");
5723 return 0;
5724 }
5725 }
5726
5727 astman_send_ack(s, m, "Success");
5728 return 0;
5729 }
5730
5731 astman_send_error(s, m, "Unknown operation");
5732 return 0;
5733}
5734
5735/*!
5736 * \brief Add an event filter to a manager session
5737 *
5738 * \param criteria See examples in manager.conf.sample
5739 * \param filter_pattern Filter pattern
5740 * \param includefilters, excludefilters
5741 *
5742 * \return FILTER_ALLOC_FAILED Memory allocation failure
5743 * \return FILTER_COMPILE_FAIL If the filter did not compile
5744 * \return FILTER_FORMAT_ERROR If the criteria weren't formatted correctly
5745 * \return FILTER_SUCCESS Success
5746 *
5747 *
5748 * Examples:
5749 * See examples in manager.conf.sample
5750 *
5751 */
5753 const char *criteria, const char *filter_pattern,
5754 struct ao2_container *includefilters, struct ao2_container *excludefilters)
5755{
5756 RAII_VAR(struct event_filter_entry *, filter_entry,
5757 ao2_t_alloc(sizeof(*filter_entry), event_filter_destructor, "event_filter allocation"),
5758 ao2_cleanup);
5759 char *options_start = NULL;
5760 SCOPE_ENTER(3, "manager_add_filter(%s, %s, %p, %p)", criteria, filter_pattern, includefilters, excludefilters);
5761
5762 if (!filter_entry) {
5763 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_ALLOC_FAILED, LOG_WARNING, "Unable to allocate filter_entry");
5764 }
5765
5766 /*
5767 * At a minimum, criteria must be "eventfilter" but may contain additional
5768 * constraints.
5769 */
5770 if (ast_strlen_zero(criteria)) {
5772 }
5773
5774 /*
5775 * filter_pattern could be empty but it should never be NULL.
5776 */
5777 if (!filter_pattern) {
5778 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "Filter pattern was NULL");
5779 }
5780
5781 /*
5782 * For a legacy filter, if the first character of filter_pattern is
5783 * '!' then it's an exclude filter. It's also accepted as an alternative
5784 * to specifying "action(exclude)" for an advanced filter. If
5785 * "action" is specified however, it will take precedence.
5786 */
5787 if (filter_pattern[0] == '!') {
5788 filter_entry->is_excludefilter = 1;
5789 filter_pattern++;
5790 }
5791
5792 /*
5793 * This is the default
5794 */
5795 filter_entry->match_type = FILTER_MATCH_REGEX;
5796
5797 /*
5798 * If the criteria has a '(' in it, then it's an advanced filter.
5799 */
5800 options_start = strstr(criteria, "(");
5801
5802 /*
5803 * If it's a legacy filter, there MUST be a filter pattern.
5804 */
5805 if (!options_start && ast_strlen_zero(filter_pattern)) {
5807 "'%s = %s': Legacy filter with no filter pattern specified\n",
5808 criteria, filter_pattern);
5809 }
5810
5811 if (options_start) {
5812 /*
5813 * This is an advanced filter
5814 */
5815 char *temp = ast_strdupa(options_start + 1); /* skip over the leading '(' */
5816 char *saveptr = NULL;
5817 char *option = NULL;
5818 enum found_options {
5819 action_found = (1 << 0),
5820 name_found = (1 << 1),
5821 header_found = (1 << 2),
5822 method_found = (1 << 3),
5823 };
5824 enum found_options options_found = 0;
5825
5826 filter_entry->match_type = FILTER_MATCH_NONE;
5827
5828 ast_strip(temp);
5829 if (ast_strlen_zero(temp) || !ast_ends_with(temp, ")")) {
5831 "'%s = %s': Filter options not formatted correctly\n",
5832 criteria, filter_pattern);
5833 }
5834
5835 /*
5836 * These can actually be in any order...
5837 * action(include|exclude),name(<event_name>),header(<header_name>),method(<match_method>)
5838 * At least one of action, name, or header is required.
5839 */
5840 while ((option = strtok_r(temp, " ,)", &saveptr))) {
5841 if (!strncmp(option, "action", 6)) {
5842 char *method = strstr(option, "(");
5843 if (ast_strlen_zero(method)) {
5844 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'action' parameter not formatted correctly\n",
5845 criteria, filter_pattern);
5846 }
5847 method++;
5849 if (!strcmp(method, "include")) {
5850 filter_entry->is_excludefilter = 0;
5851 } else if (!strcmp(method, "exclude")) {
5852 filter_entry->is_excludefilter = 1;
5853 } else {
5854 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'action' option '%s' is unknown\n",
5855 criteria, filter_pattern, method);
5856 }
5857 options_found |= action_found;
5858 } else if (!strncmp(option, "name", 4)) {
5859 char *event_name = strstr(option, "(");
5860 event_name++;
5861 ast_strip(event_name);
5862 if (ast_strlen_zero(event_name)) {
5863 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'name' parameter not formatted correctly\n",
5864 criteria, filter_pattern);
5865 }
5866 filter_entry->event_name = ast_strdup(event_name);
5867 filter_entry->event_name_hash = ast_str_hash(event_name);
5868 options_found |= name_found;
5869 } else if (!strncmp(option, "header", 6)) {
5870 char *header_name = strstr(option, "(");
5871 header_name++;
5872 ast_strip(header_name);
5873 if (ast_strlen_zero(header_name)) {
5874 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'header' parameter not formatted correctly\n",
5875 criteria, filter_pattern);
5876 }
5877 if (!ast_ends_with(header_name, ":")) {
5878 filter_entry->header_name = ast_malloc(strlen(header_name) + 2);
5879 if (!filter_entry->header_name) {
5880 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_ALLOC_FAILED, LOG_ERROR, "Unable to allocate memory for header_name");
5881 }
5882 sprintf(filter_entry->header_name, "%s:", header_name); /* Safe */
5883 } else {
5884 filter_entry->header_name = ast_strdup(header_name);
5885 }
5886 options_found |= header_found;
5887 } else if (!strncmp(option, "method", 6)) {
5888 char *method = strstr(option, "(");
5889 method++;
5891 if (ast_strlen_zero(method)) {
5892 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'method' parameter not formatted correctly\n",
5893 criteria, filter_pattern);
5894 }
5895 if (!strcmp(method, "regex")) {
5896 filter_entry->match_type = FILTER_MATCH_REGEX;
5897 } else if (!strcmp(method, "exact")) {
5898 filter_entry->match_type = FILTER_MATCH_EXACT;
5899 } else if (!strcmp(method, "starts_with")) {
5900 filter_entry->match_type = FILTER_MATCH_STARTS_WITH;
5901 } else if (!strcmp(method, "ends_with")) {
5902 filter_entry->match_type = FILTER_MATCH_ENDS_WITH;
5903 } else if (!strcmp(method, "contains")) {
5904 filter_entry->match_type = FILTER_MATCH_CONTAINS;
5905 } else if (!strcmp(method, "none")) {
5906 filter_entry->match_type = FILTER_MATCH_NONE;
5907 } else {
5908 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'method' option '%s' is unknown\n",
5909 criteria, filter_pattern, method);
5910 }
5911 options_found |= method_found;
5912 } else {
5913 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': Filter option '%s' is unknown\n",
5914 criteria, filter_pattern, option);
5915 }
5916 temp = NULL;
5917 }
5918 if (!options_found) {
5920 "'%s = %s': No action, name, header, or method option found\n",
5921 criteria, filter_pattern);
5922 }
5923 if (ast_strlen_zero(filter_pattern) && filter_entry->match_type != FILTER_MATCH_NONE) {
5925 "'%s = %s': method can't be '%s' with no filter pattern\n",
5926 criteria, filter_pattern, match_type_names[filter_entry->match_type]);
5927 }
5928 if (!ast_strlen_zero(filter_pattern) && filter_entry->match_type == FILTER_MATCH_NONE) {
5930 "'%s = %s': method can't be 'none' with a filter pattern\n",
5931 criteria, filter_pattern);
5932 }
5933 if (!(options_found & name_found) && !(options_found & header_found) &&
5934 filter_entry->match_type == FILTER_MATCH_NONE) {
5936 "'%s = %s': No name or header option found and no filter pattern\n",
5937 criteria, filter_pattern);
5938 }
5939 }
5940
5941 if (!ast_strlen_zero(filter_pattern)) {
5942 if (filter_entry->match_type == FILTER_MATCH_REGEX) {
5943 filter_entry->regex_filter = ast_calloc(1, sizeof(regex_t));
5944 if (!filter_entry->regex_filter) {
5945 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_ALLOC_FAILED, LOG_ERROR, "Unable to allocate memory for regex_filter");
5946 }
5947 if (regcomp(filter_entry->regex_filter, filter_pattern, REG_EXTENDED | REG_NOSUB)) {
5948 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_COMPILE_FAIL, LOG_WARNING, "Unable to compile regex filter for '%s'", filter_pattern);
5949 }
5950 } else {
5951 filter_entry->string_filter = ast_strdup(filter_pattern);
5952 }
5953 }
5954
5955 ast_debug(2, "Event filter:\n"
5956 "conf entry: %s = %s\n"
5957 "event_name: %s (hash: %d)\n"
5958 "test_header: %s\n"
5959 "match_type: %s\n"
5960 "regex_filter: %p\n"
5961 "string filter: %s\n"
5962 "is excludefilter: %d\n",
5963 criteria, filter_pattern,
5964 S_OR(filter_entry->event_name, "<not used>"),
5965 filter_entry->event_name_hash,
5966 S_OR(filter_entry->header_name, "<not used>"),
5967 match_type_names[filter_entry->match_type],
5968 filter_entry->regex_filter,
5969 filter_entry->string_filter,
5970 filter_entry->is_excludefilter);
5971
5972 if (filter_entry->is_excludefilter) {
5973 ao2_t_link(excludefilters, filter_entry, "link new filter into exclude user container");
5974 } else {
5975 ao2_t_link(includefilters, filter_entry, "link new filter into include user container");
5976 }
5977
5978 SCOPE_EXIT_RTN_VALUE(FILTER_SUCCESS, "Filter added successfully");
5979}
5980
5981#ifdef TEST_FRAMEWORK
5982
5983struct test_filter_data {
5984 const char *criteria;
5985 const char *filter;
5986 enum add_filter_result expected_add_filter_result;
5987 struct event_filter_entry expected_filter_entry;
5988 const char *test_event_name;
5989 const char *test_event_payload;
5990 int expected_should_send_event;
5991};
5992
5993static char *add_filter_result_enums[] = {
5994 [FILTER_SUCCESS] = "FILTER_SUCCESS",
5995 [FILTER_ALLOC_FAILED] = "FILTER_ALLOC_FAILED",
5996 [FILTER_COMPILE_FAIL] = "FILTER_COMPILE_FAIL",
5997 [FILTER_FORMAT_ERROR] = "FILTER_FORMAT_ERROR",
5998};
5999
6000#define TEST_EVENT_NEWCHANNEL "Newchannel", "Event: Newchannel\r\nChannel: XXX\r\nSomeheader: YYY\r\n"
6001#define TEST_EVENT_VARSET "VarSet", "Event: VarSet\r\nChannel: ABC\r\nSomeheader: XXX\r\n"
6002#define TEST_EVENT_NONE "", ""
6003
6004static struct test_filter_data parsing_filter_tests[] = {
6005 /* Valid filters */
6006 { "eventfilter", "XXX", FILTER_SUCCESS,
6007 { FILTER_MATCH_REGEX, NULL, NULL, NULL, 0, NULL, 0}, TEST_EVENT_NEWCHANNEL, 1},
6008 { "eventfilter", "!XXX", FILTER_SUCCESS,
6009 { FILTER_MATCH_REGEX, NULL, NULL, NULL, 0, NULL, 1}, TEST_EVENT_VARSET, 0},
6010 { "eventfilter(name(VarSet),method(none))", "", FILTER_SUCCESS,
6011 { FILTER_MATCH_NONE, NULL, NULL, "VarSet", 0, NULL, 0}, TEST_EVENT_VARSET, 1},
6012 { "eventfilter(name(Newchannel),method(regex))", "X[XYZ]X", FILTER_SUCCESS,
6013 { FILTER_MATCH_REGEX, NULL, NULL, "Newchannel", 0, NULL, 0}, TEST_EVENT_NEWCHANNEL, 1},
6014 { "eventfilter(name(Newchannel),method(regex))", "X[abc]X", FILTER_SUCCESS,
6015 { FILTER_MATCH_REGEX, NULL, NULL, "Newchannel", 0, NULL, 0}, TEST_EVENT_NEWCHANNEL, 0},
6016 { "eventfilter(action(exclude),name(Newchannel),method(regex))", "X[XYZ]X", FILTER_SUCCESS,
6017 { FILTER_MATCH_REGEX, NULL, NULL, "Newchannel", 0, NULL, 1}, TEST_EVENT_NEWCHANNEL, 0},
6018 { "eventfilter(action(exclude),name(Newchannel),method(regex))", "X[abc]X", FILTER_SUCCESS,
6019 { FILTER_MATCH_REGEX, NULL, NULL, "Newchannel", 0, NULL, 1}, TEST_EVENT_NEWCHANNEL, 1},
6020 { "eventfilter(action(include),name(VarSet),header(Channel),method(starts_with))", "AB", FILTER_SUCCESS,
6021 { FILTER_MATCH_STARTS_WITH, NULL, NULL, "VarSet", 0, "Channel:", 0}, TEST_EVENT_VARSET, 1},
6022 { "eventfilter(action(include),name(VarSet),header(Channel),method(ends_with))", "BC", FILTER_SUCCESS,
6023 { FILTER_MATCH_ENDS_WITH, NULL, NULL, "VarSet", 0, "Channel:", 0}, TEST_EVENT_VARSET, 1},
6024 { "eventfilter(action(include),name(VarSet),header(Channel),method(exact))", "ABC", FILTER_SUCCESS,
6025 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Channel:", 0}, TEST_EVENT_VARSET, 1},
6026 { "eventfilter(action(include),name(VarSet),header(Channel),method(exact))", "XXX", FILTER_SUCCESS,
6027 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Channel:", 0}, TEST_EVENT_VARSET, 0},
6028 { "eventfilter(name(VarSet),header(Channel),method(exact))", "!ZZZ", FILTER_SUCCESS,
6029 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Channel:", 1}, TEST_EVENT_VARSET, 1},
6030 { "eventfilter(action(exclude),name(VarSet),header(Channel),method(exact))", "ZZZ", FILTER_SUCCESS,
6031 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Channel:", 1}, TEST_EVENT_VARSET, 1},
6032 { "eventfilter(action(include),name(VarSet),header(Someheader),method(exact))", "!XXX", FILTER_SUCCESS,
6033 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Someheader:", 0}, TEST_EVENT_VARSET, 1},
6034
6035 /* Invalid filters */
6036 { "eventfilter(action(include)", "", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6037 { "eventfilter(action(inlude)", "", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6038 { "eventfilter(nnnn(yyy)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6039 { "eventfilter(eader(VarSet)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6040 { "eventfilter(ethod(contains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6041 { "eventfilter(nnnn(yyy),header(VarSet),method(contains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6042 { "eventfilter(name(yyy),heder(VarSet),method(contains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6043 { "eventfilter(name(yyy),header(VarSet),mehod(contains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6044 { "eventfilter(name(yyy),header(VarSet),method(coains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6045 { "eventfilter(method(yyy))", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6046 { "eventfilter", "", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6047 { "eventfilter", "!", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6048 { "eventfilter()", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6049 { "eventfilter", "XX[X", FILTER_COMPILE_FAIL, { 0, }, TEST_EVENT_NONE, 0},
6050 { "eventfilter(method(regex))", "XX[X", FILTER_COMPILE_FAIL, { 0, }, TEST_EVENT_NONE, 0},
6051};
6052
6053/*
6054 * This is a bit different than ast_strings_equal in that
6055 * it will return 1 if both strings are NULL.
6056 */
6057static int strings_equal(const char *str1, const char *str2)
6058{
6059 if ((!str1 && str2) || (str1 && !str2)) {
6060 return 0;
6061 }
6062
6063 return str1 == str2 || !strcmp(str1, str2);
6064}
6065
6066AST_TEST_DEFINE(eventfilter_test_creation)
6067{
6069 RAII_VAR(struct ao2_container *, includefilters, NULL, ao2_cleanup);
6070 RAII_VAR(struct ao2_container *, excludefilters, NULL, ao2_cleanup);
6071 int i = 0;
6072
6073 switch (cmd) {
6074 case TEST_INIT:
6075 info->name = "eventfilter_test_creation";
6076 info->category = "/main/manager/";
6077 info->summary = "Test eventfilter creation";
6078 info->description =
6079 "This creates various eventfilters and tests to make sure they were created successfully.";
6080 return AST_TEST_NOT_RUN;
6081 case TEST_EXECUTE:
6082 break;
6083 }
6084
6087 if (!includefilters || !excludefilters) {
6088 ast_test_status_update(test, "Failed to allocate filter containers.\n");
6089 return AST_TEST_FAIL;
6090 }
6091
6092 for (i = 0; i < ARRAY_LEN(parsing_filter_tests); i++) {
6093 struct event_filter_entry *filter_entry;
6094 enum add_filter_result add_filter_res;
6095 int send_event = 0;
6096 struct eventqent *eqe = NULL;
6097 int include_container_count = 0;
6098 int exclude_container_count = 0;
6099
6100 /* We need to clear the containers before each test */
6101 ao2_callback(includefilters, OBJ_UNLINK | OBJ_NODATA, NULL, NULL);
6102 ao2_callback(excludefilters, OBJ_UNLINK | OBJ_NODATA, NULL, NULL);
6103
6104 add_filter_res = manager_add_filter(parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6105 includefilters, excludefilters);
6106
6107 /* If you're adding a new test, enable this to see the full results */
6108#if 0
6109 ast_test_debug(test, "Add filter result '%s = %s': Expected: %s Actual: %s %s\n",
6110 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6111 add_filter_result_enums[parsing_filter_tests[i].expected_add_filter_result],
6112 add_filter_result_enums[add_filter_res],
6113 add_filter_res != parsing_filter_tests[i].expected_add_filter_result ? "FAIL" : "PASS");
6114#endif
6115
6116 if (add_filter_res != parsing_filter_tests[i].expected_add_filter_result) {
6118 "Unexpected add filter result '%s = %s'. Expected result: %s Actual result: %s\n",
6119 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6120 add_filter_result_enums[parsing_filter_tests[i].expected_add_filter_result],
6121 add_filter_result_enums[add_filter_res]);
6122 res = AST_TEST_FAIL;
6123 continue;
6124 }
6125
6126 if (parsing_filter_tests[i].expected_add_filter_result != FILTER_SUCCESS) {
6127 /*
6128 * We don't need to test filters that we know aren't going
6129 * to be parsed successfully.
6130 */
6131 continue;
6132 }
6133
6134 /* We need to set the event name hash on the test data */
6135 if (parsing_filter_tests[i].expected_filter_entry.event_name) {
6136 parsing_filter_tests[i].expected_filter_entry.event_name_hash =
6137 ast_str_hash(parsing_filter_tests[i].expected_filter_entry.event_name);
6138 }
6139
6140 include_container_count = ao2_container_count(includefilters);
6141 exclude_container_count = ao2_container_count(excludefilters);
6142
6143 if (parsing_filter_tests[i].expected_filter_entry.is_excludefilter) {
6144 if (exclude_container_count != 1 || include_container_count != 0) {
6146 "Invalid container counts for exclude filter '%s = %s'. Exclude: %d Include: %d. Should be 1 and 0\n",
6147 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6148 exclude_container_count, include_container_count);
6149 res = AST_TEST_FAIL;
6150 continue;
6151 }
6152 /* There can only be one entry in the container so ao2_find is fine */
6153 filter_entry = ao2_find(excludefilters, NULL, OBJ_SEARCH_OBJECT);
6154 } else {
6155 if (include_container_count != 1 || exclude_container_count != 0) {
6157 "Invalid container counts for include filter '%s = %s'. Include: %d Exclude: %d. Should be 1 and 0\n",
6158 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6159 include_container_count, exclude_container_count);
6160 res = AST_TEST_FAIL;
6161 continue;
6162 }
6163 /* There can only be one entry in the container so ao2_find is fine */
6164 filter_entry = ao2_find(includefilters, NULL, OBJ_SEARCH_OBJECT);
6165 }
6166
6167 if (!filter_entry) {
6169 "Failed to find filter entry for '%s = %s' in %s filter container\n",
6170 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6171 parsing_filter_tests[i].expected_filter_entry.is_excludefilter ? "exclude" : "include");
6172 res = AST_TEST_FAIL;
6173 goto loop_cleanup;
6174 }
6175
6176 if (filter_entry->match_type != parsing_filter_tests[i].expected_filter_entry.match_type) {
6178 "Failed to match filter type for '%s = %s'. Expected: %s Actual: %s\n",
6179 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6180 match_type_names[parsing_filter_tests[i].expected_filter_entry.match_type],
6181 match_type_names[filter_entry->match_type]);
6182 res = AST_TEST_FAIL;
6183 goto loop_cleanup;
6184 }
6185
6186 if (!strings_equal(filter_entry->event_name, parsing_filter_tests[i].expected_filter_entry.event_name)) {
6188 "Failed to match event name for '%s = %s'. Expected: '%s' Actual: '%s'\n",
6189 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6190 parsing_filter_tests[i].expected_filter_entry.event_name, filter_entry->event_name);
6191 res = AST_TEST_FAIL;
6192 goto loop_cleanup;
6193 }
6194
6195 if (filter_entry->event_name_hash != parsing_filter_tests[i].expected_filter_entry.event_name_hash) {
6197 "Event name hashes failed to match for '%s = %s'. Expected: %u Actual: %u\n",
6198 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6199 parsing_filter_tests[i].expected_filter_entry.event_name_hash, filter_entry->event_name_hash);
6200 res = AST_TEST_FAIL;
6201 goto loop_cleanup;
6202 }
6203
6204 if (!strings_equal(filter_entry->header_name, parsing_filter_tests[i].expected_filter_entry.header_name)) {
6206 "Failed to match header name for '%s = %s'. Expected: '%s' Actual: '%s'\n",
6207 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6208 parsing_filter_tests[i].expected_filter_entry.header_name, filter_entry->header_name);
6209 res = AST_TEST_FAIL;
6210 goto loop_cleanup;
6211 }
6212
6213 switch (parsing_filter_tests[i].expected_filter_entry.match_type) {
6214 case FILTER_MATCH_REGEX:
6215 if (!filter_entry->regex_filter) {
6217 "Failed to compile regex filter for '%s = %s'\n",
6218 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter);
6219 res = AST_TEST_FAIL;
6220 goto loop_cleanup;
6221 }
6222 break;
6223 case FILTER_MATCH_NONE:
6224 if (filter_entry->regex_filter || !ast_strlen_zero(filter_entry->string_filter)) {
6226 "Unexpected regex filter or string for '%s = %s' with match_type 'none'\n",
6227 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter);
6228 res = AST_TEST_FAIL;
6229 goto loop_cleanup;
6230 }
6231 break;
6235 case FILTER_MATCH_EXACT:
6236 if (filter_entry->regex_filter || ast_strlen_zero(filter_entry->string_filter)) {
6238 "Unexpected regex filter or empty string for '%s = %s' with match_type '%s'\n",
6239 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6240 match_type_names[parsing_filter_tests[i].expected_filter_entry.match_type]);
6241 res = AST_TEST_FAIL;
6242 goto loop_cleanup;
6243 }
6244 break;
6245 default:
6246 res = AST_TEST_FAIL;
6247 goto loop_cleanup;
6248 }
6249
6250 /*
6251 * This is a basic test of whether a single event matches a single filter.
6252 */
6253 eqe = ast_calloc(1, sizeof(*eqe) + strlen(parsing_filter_tests[i].test_event_payload) + 1);
6254 if (!eqe) {
6255 ast_test_status_update(test, "Failed to allocate eventqent\n");
6256 res = AST_TEST_FAIL;
6257 ao2_ref(filter_entry, -1);
6258 break;
6259 }
6260 strcpy(eqe->eventdata, parsing_filter_tests[i].test_event_payload); /* Safe */
6261 eqe->event_name_hash = ast_str_hash(parsing_filter_tests[i].test_event_name);
6262 send_event = should_send_event(includefilters, excludefilters, eqe);
6263 if (send_event != parsing_filter_tests[i].expected_should_send_event) {
6264 char *escaped = ast_escape_c_alloc(parsing_filter_tests[i].test_event_payload);
6266 "Should send event failed to match for '%s = %s' payload '%s'. Expected: %s Actual: %s\n",
6267 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter, escaped,
6268 AST_YESNO(parsing_filter_tests[i].expected_should_send_event), AST_YESNO(send_event));
6269 ast_free(escaped);
6270 res = AST_TEST_FAIL;
6271 }
6272loop_cleanup:
6273 ast_free(eqe);
6274 ao2_cleanup(filter_entry);
6275
6276 }
6277 ast_test_status_update(test, "Tested %d filters\n", i);
6278
6279 return res;
6280}
6281
6282struct test_filter_matching {
6283 const char *criteria;
6284 const char *pattern;
6285};
6286
6287/*
6288 * These filters are used to test the precedence of include and exclude
6289 * filters. When there are both include and exclude filters, the include
6290 * filters are matched first. If the event doesn't match an include filter,
6291 * it's discarded. If it does match, the exclude filter list is searched and
6292 * if a match is found, the event is discarded.
6293 */
6294
6295/*
6296 * The order of the filters in the array doesn't really matter. The
6297 * include and exclude filters are in separate containers and in each
6298 * container, traversal stops when a match is found.
6299 */
6300static struct test_filter_matching filters_for_matching[] = {
6301 { "eventfilter(name(VarSet),method(none))", ""},
6302 { "eventfilter(name(Newchannel),method(regex))", "X[XYZ]X"},
6303 { "eventfilter(name(Newchannel),method(regex))", "X[abc]X"},
6304 { "eventfilter(name(Newchannel),header(Someheader),method(regex))", "ZZZ"},
6305 { "eventfilter(action(exclude),name(Newchannel),method(regex))", "X[a]X"},
6306 { "eventfilter(action(exclude),name(Newchannel),method(regex))", "X[Z]X"},
6307 { "eventfilter(action(exclude),name(VarSet),header(Channel),method(regex))", "YYY"},
6308};
6309
6310struct test_event_matching{
6311 const char *event_name;
6312 const char *payload;
6313 int expected_should_send_event;
6314};
6315
6316static struct test_event_matching events_for_matching[] = {
6317 { "Newchannel", "Event: Newchannel\r\nChannel: XXX\r\nSomeheader: YYY\r\n", 1 },
6318 { "Newchannel", "Event: Newchannel\r\nChannel: XZX\r\nSomeheader: YYY\r\n", 0 },
6319 { "Newchannel", "Event: Newchannel\r\nChannel: XaX\r\nSomeheader: YYY\r\n", 0 },
6320 { "Newchannel", "Event: Newchannel\r\nChannel: XbX\r\nSomeheader: YYY\r\n", 1 },
6321 { "Newchannel", "Event: Newchannel\r\nChannel: XcX\r\nSomeheader: YYY\r\n", 1 },
6322 { "Newchannel", "Event: Newchannel\r\nChannel: YYY\r\nSomeheader: YYY\r\n", 0 },
6323 { "Newchannel", "Event: Newchannel\r\nChannel: YYY\r\nSomeheader: ZZZ\r\n", 1 },
6324 { "VarSet", "Event: VarSet\r\nChannel: XXX\r\nSomeheader: YYY\r\n", 1 },
6325 { "VarSet", "Event: VarSet\r\nChannel: YYY\r\nSomeheader: YYY\r\n", 0 },
6326};
6327
6328AST_TEST_DEFINE(eventfilter_test_matching)
6329{
6331 RAII_VAR(struct ao2_container *, includefilters, NULL, ao2_cleanup);
6332 RAII_VAR(struct ao2_container *, excludefilters, NULL, ao2_cleanup);
6333 int i = 0;
6334
6335 switch (cmd) {
6336 case TEST_INIT:
6337 info->name = "eventfilter_test_matching";
6338 info->category = "/main/manager/";
6339 info->summary = "Test eventfilter matching";
6340 info->description =
6341 "This creates various eventfilters and tests to make sure they were matched successfully.";
6342 return AST_TEST_NOT_RUN;
6343 case TEST_EXECUTE:
6344 break;
6345 }
6346
6349 if (!includefilters || !excludefilters) {
6350 ast_test_status_update(test, "Failed to allocate filter containers.\n");
6351 return AST_TEST_FAIL;
6352 }
6353
6354 /* Load all the expected SUCCESS filters */
6355 for (i = 0; i < ARRAY_LEN(filters_for_matching); i++) {
6356 enum add_filter_result add_filter_res;
6357
6358 add_filter_res = manager_add_filter(filters_for_matching[i].criteria,
6359 filters_for_matching[i].pattern, includefilters, excludefilters);
6360
6361 if (add_filter_res != FILTER_SUCCESS) {
6363 "Unexpected add filter result '%s = %s'. Expected result: %s Actual result: %s\n",
6364 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6365 add_filter_result_enums[FILTER_SUCCESS],
6366 add_filter_result_enums[add_filter_res]);
6367 res = AST_TEST_FAIL;
6368 break;
6369 }
6370 }
6371 ast_test_debug(test, "Loaded %d filters\n", i);
6372
6373 if (res != AST_TEST_PASS) {
6374 return res;
6375 }
6376
6377 /* Now test them */
6378 for (i = 0; i < ARRAY_LEN(events_for_matching); i++) {
6379 int send_event = 0;
6380 struct eventqent *eqe = NULL;
6381
6382 eqe = ast_calloc(1, sizeof(*eqe) + strlen(events_for_matching[i].payload) + 1);
6383 if (!eqe) {
6384 ast_test_status_update(test, "Failed to allocate eventqent\n");
6385 res = AST_TEST_FAIL;
6386 break;
6387 }
6388 strcpy(eqe->eventdata, events_for_matching[i].payload); /* Safe */
6389 eqe->event_name_hash = ast_str_hash(events_for_matching[i].event_name);
6390 send_event = should_send_event(includefilters, excludefilters, eqe);
6391 if (send_event != events_for_matching[i].expected_should_send_event) {
6392 char *escaped = ast_escape_c_alloc(events_for_matching[i].payload);
6394 "Should send event failed to match for '%s'. Expected: %s Actual: %s\n",
6395 escaped,
6396 AST_YESNO(events_for_matching[i].expected_should_send_event), AST_YESNO(send_event));
6397 ast_free(escaped);
6398 res = AST_TEST_FAIL;
6399 }
6400 ast_free(eqe);
6401 }
6402 ast_test_debug(test, "Tested %d events\n", i);
6403
6404 return res;
6405}
6406#endif
6407
6408/*!
6409 * Send any applicable events to the client listening on this socket.
6410 * Wait only for a finite time on each event, and drop all events whether
6411 * they are successfully sent or not.
6412 */
6413static int process_events(struct mansession *s)
6414{
6415 int ret = 0;
6416
6417 ao2_lock(s->session);
6418 if (s->session->stream != NULL) {
6419 struct eventqent *eqe = s->session->last_ev;
6420
6421 while ((eqe = advance_event(eqe))) {
6422 if (eqe->category == EVENT_FLAG_SHUTDOWN) {
6423 ast_debug(3, "Received CloseSession event\n");
6424 ret = -1;
6425 }
6426 if (!ret && s->session->authenticated &&
6427 (s->session->readperm & eqe->category) == eqe->category &&
6428 (s->session->send_events & eqe->category) == eqe->category) {
6430 if (send_string(s, eqe->eventdata) < 0 || s->write_error)
6431 ret = -1; /* don't send more */
6432 }
6433 }
6434 s->session->last_ev = eqe;
6435 }
6436 }
6437 ao2_unlock(s->session);
6438 return ret;
6439}
6440
6441static int action_userevent(struct mansession *s, const struct message *m)
6442{
6443 const char *event = astman_get_header(m, "UserEvent");
6444 struct ast_str *body = ast_str_thread_get(&userevent_buf, 16);
6445 int x;
6446
6447 ast_str_reset(body);
6448
6449 for (x = 0; x < m->hdrcount; x++) {
6450 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:")) &&
6451 strncasecmp("Action:", m->headers[x], strlen("Action:"))) {
6452 ast_str_append(&body, 0, "%s\r\n", m->headers[x]);
6453 }
6454 }
6455
6456 astman_send_ack(s, m, "Event Sent");
6457 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, ast_str_buffer(body));
6458 return 0;
6459}
6460
6461/*! \brief Show PBX core settings information */
6462static int action_coresettings(struct mansession *s, const struct message *m)
6463{
6464 const char *actionid = astman_get_header(m, "ActionID");
6465 char idText[150];
6466
6467 if (!ast_strlen_zero(actionid)) {
6468 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
6469 } else {
6470 idText[0] = '\0';
6471 }
6472
6473 astman_append(s, "Response: Success\r\n"
6474 "%s"
6475 "AMIversion: %s\r\n"
6476 "AsteriskVersion: %s\r\n"
6477 "SystemName: %s\r\n"
6478 "CoreMaxCalls: %d\r\n"
6479 "CoreMaxLoadAvg: %f\r\n"
6480 "CoreRunUser: %s\r\n"
6481 "CoreRunGroup: %s\r\n"
6482 "CoreMaxFilehandles: %d\r\n"
6483 "CoreRealTimeEnabled: %s\r\n"
6484 "CoreCDRenabled: %s\r\n"
6485 "CoreHTTPenabled: %s\r\n"
6486 "SoundsSearchCustomDir: %s\r\n"
6487 "\r\n",
6488 idText,
6501 );
6502 return 0;
6503}
6504
6505/*! \brief Show PBX core status information */
6506static int action_corestatus(struct mansession *s, const struct message *m)
6507{
6508 const char *actionid = astman_get_header(m, "ActionID");
6509 char idText[150];
6510 char startuptime[150], startupdate[150];
6511 char reloadtime[150], reloaddate[150];
6512 struct ast_tm tm;
6513
6514 if (!ast_strlen_zero(actionid)) {
6515 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
6516 } else {
6517 idText[0] = '\0';
6518 }
6519
6521 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
6522 ast_strftime(startupdate, sizeof(startupdate), "%Y-%m-%d", &tm);
6524 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
6525 ast_strftime(reloaddate, sizeof(reloaddate), "%Y-%m-%d", &tm);
6526
6527 astman_append(s, "Response: Success\r\n"
6528 "%s"
6529 "CoreStartupDate: %s\r\n"
6530 "CoreStartupTime: %s\r\n"
6531 "CoreReloadDate: %s\r\n"
6532 "CoreReloadTime: %s\r\n"
6533 "CoreCurrentCalls: %d\r\n"
6534 "CoreProcessedCalls: %d\r\n"
6535 "\r\n",
6536 idText,
6537 startupdate,
6538 startuptime,
6539 reloaddate,
6540 reloadtime,
6543 );
6544 return 0;
6545}
6546
6547/*! \brief Send a reload event */
6548static int action_reload(struct mansession *s, const struct message *m)
6549{
6550 const char *module = astman_get_header(m, "Module");
6552
6553 switch (res) {
6555 astman_send_error(s, m, "No such module");
6556 break;
6558 astman_send_error(s, m, "Module does not support reload");
6559 break;
6561 astman_send_error(s, m, "An unknown error occurred");
6562 break;
6564 astman_send_error(s, m, "A reload is in progress");
6565 break;
6567 astman_send_error(s, m, "Module not initialized");
6568 break;
6571 /* Treat a queued request as success */
6572 astman_send_ack(s, m, "Module Reloaded");
6573 break;
6574 }
6575 return 0;
6576}
6577
6578/*! \brief Manager command "CoreShowChannels" - List currently defined channels
6579 * and some information about them. */
6580static int action_coreshowchannels(struct mansession *s, const struct message *m)
6581{
6582 const char *actionid = astman_get_header(m, "ActionID");
6583 char idText[256];
6584 int numchans = 0;
6585 struct ao2_container *channels;
6586 struct ao2_iterator it_chans;
6587 struct ast_channel_snapshot *cs;
6588
6589 if (!ast_strlen_zero(actionid)) {
6590 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
6591 } else {
6592 idText[0] = '\0';
6593 }
6594
6596
6597 astman_send_listack(s, m, "Channels will follow", "start");
6598
6599 it_chans = ao2_iterator_init(channels, 0);
6600 for (; (cs = ao2_iterator_next(&it_chans)); ao2_ref(cs, -1)) {
6602 char durbuf[16] = "";
6603
6604 if (!built) {
6605 continue;
6606 }
6607
6608 if (!ast_tvzero(cs->base->creationtime)) {
6609 int duration, durh, durm, durs;
6610
6611 duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->base->creationtime) / 1000);
6612 durh = duration / 3600;
6613 durm = (duration % 3600) / 60;
6614 durs = duration % 60;
6615 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
6616 }
6617
6618 astman_append(s,
6619 "Event: CoreShowChannel\r\n"
6620 "%s"
6621 "%s"
6622 "Application: %s\r\n"
6623 "ApplicationData: %s\r\n"
6624 "Duration: %s\r\n"
6625 "BridgeId: %s\r\n"
6626 "\r\n",
6627 idText,
6628 ast_str_buffer(built),
6629 cs->dialplan->appl,
6630 cs->dialplan->data,
6631 durbuf,
6632 cs->bridge->id);
6633
6634 numchans++;
6635
6636 ast_free(built);
6637 }
6638 ao2_iterator_destroy(&it_chans);
6639
6640 astman_send_list_complete(s, m, "CoreShowChannelsComplete", numchans);
6641
6642 ao2_ref(channels, -1);
6643 return 0;
6644}
6645
6646/*! \brief Helper function to add a channel name to the vector */
6647static int coreshowchannelmap_add_to_map(struct ao2_container *c, const char *s)
6648{
6649 char *str;
6650
6651 str = ast_strdup(s);
6652 if (!str) {
6653 ast_log(LOG_ERROR, "Unable to append channel to channel map\n");
6654 return 1;
6655 }
6656
6657 /* If this is a duplicate, it will be ignored */
6659
6660 return 0;
6661}
6662
6663/*! \brief Recursive function to get all channels in a bridge. Follow local channels as well */
6665 struct ast_channel_snapshot *channel_snapshot, struct ast_bridge_snapshot *bridge_snapshot)
6666{
6667 int res = 0;
6668 struct ao2_iterator iter;
6669 char *current_channel_uid;
6670
6671 iter = ao2_iterator_init(bridge_snapshot->channels, 0);
6672 while ((current_channel_uid = ao2_iterator_next(&iter))) {
6673 struct ast_channel_snapshot *current_channel_snapshot;
6674 int add_channel_res;
6675
6676 /* Don't add the original channel to the list - it's either already in there,
6677 * or it's the channel we want the map for */
6678 if (!strcmp(current_channel_uid, channel_snapshot->base->uniqueid)) {
6679 ao2_ref(current_channel_uid, -1);
6680 continue;
6681 }
6682
6683 current_channel_snapshot = ast_channel_snapshot_get_latest(current_channel_uid);
6684 if (!current_channel_snapshot) {
6685 ast_debug(5, "Unable to get channel snapshot\n");
6686 ao2_ref(current_channel_uid, -1);
6687 continue;
6688 }
6689
6690 add_channel_res = coreshowchannelmap_add_to_map(channel_map, current_channel_snapshot->base->name);
6691 if (add_channel_res) {
6692 res = 1;
6693 ao2_ref(current_channel_snapshot, -1);
6694 ao2_ref(current_channel_uid, -1);
6695 break;
6696 }
6697
6698 /* If this is a local channel that we haven't seen yet, let's go ahead and find out what else is connected to it */
6699 if (ast_begins_with(current_channel_snapshot->base->name, "Local")) {
6700 struct ast_channel_snapshot *other_local_snapshot;
6701 struct ast_bridge_snapshot *other_bridge_snapshot;
6702 int size = strlen(current_channel_snapshot->base->name);
6703 char other_local[size + 1];
6704
6705 /* Don't copy the trailing number - set it to 1 or 2, whichever one it currently is not */
6706 ast_copy_string(other_local, current_channel_snapshot->base->name, size);
6707 other_local[size - 1] = ast_ends_with(current_channel_snapshot->base->name, "1") ? '2' : '1';
6708 other_local[size] = '\0';
6709
6710 other_local_snapshot = ast_channel_snapshot_get_latest_by_name(other_local);
6711 if (!other_local_snapshot) {
6712 ast_debug(5, "Unable to get other local channel snapshot\n");
6713 ao2_ref(current_channel_snapshot, -1);
6714 ao2_ref(current_channel_uid, -1);
6715 continue;
6716 }
6717
6718 if (coreshowchannelmap_add_to_map(channel_map, other_local_snapshot->base->name)) {
6719 res = 1;
6720 ao2_ref(current_channel_snapshot, -1);
6721 ao2_ref(current_channel_uid, -1);
6722 ao2_ref(other_local_snapshot, -1);
6723 break;
6724 }
6725
6726 other_bridge_snapshot = ast_bridge_get_snapshot_by_uniqueid(other_local_snapshot->bridge->id);
6727 if (other_bridge_snapshot) {
6728 res = coreshowchannelmap_add_connected_channels(channel_map, other_local_snapshot, other_bridge_snapshot);
6729 }
6730
6731 ao2_ref(current_channel_snapshot, -1);
6732 ao2_ref(current_channel_uid, -1);
6733 ao2_ref(other_local_snapshot, -1);
6734 ao2_ref(other_bridge_snapshot, -1);
6735
6736 if (res) {
6737 break;
6738 }
6739 }
6740 }
6741 ao2_iterator_destroy(&iter);
6742
6743 return res;
6744}
6745
6746/*! \brief Manager command "CoreShowChannelMap" - Lists all channels connected to
6747 * the specified channel. */
6748static int action_coreshowchannelmap(struct mansession *s, const struct message *m)
6749{
6750 const char *actionid = astman_get_header(m, "ActionID");
6751 const char *channel_name = astman_get_header(m, "Channel");
6752 char *current_channel_name;
6753 char id_text[256];
6754 int total = 0;
6755 struct ao2_container *channel_map;
6756 struct ao2_iterator i;
6757 RAII_VAR(struct ast_bridge_snapshot *, bridge_snapshot, NULL, ao2_cleanup);
6758 RAII_VAR(struct ast_channel_snapshot *, channel_snapshot, NULL, ao2_cleanup);
6759
6760 if (!ast_strlen_zero(actionid)) {
6761 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
6762 } else {
6763 id_text[0] = '\0';
6764 }
6765
6766 if (ast_strlen_zero(channel_name)) {
6767 astman_send_error(s, m, "CoreShowChannelMap requires a channel.\n");
6768 return 0;
6769 }
6770
6771 channel_snapshot = ast_channel_snapshot_get_latest_by_name(channel_name);
6772 if (!channel_snapshot) {
6773 astman_send_error(s, m, "Could not get channel snapshot\n");
6774 return 0;
6775 }
6776
6777 if (ast_strlen_zero(channel_snapshot->bridge->id)) {
6778 astman_send_listack(s, m, "Channel map will follow", "start");
6779 astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", 0);
6781 return 0;
6782 }
6783
6784 bridge_snapshot = ast_bridge_get_snapshot_by_uniqueid(channel_snapshot->bridge->id);
6785 if (!bridge_snapshot) {
6786 astman_send_listack(s, m, "Channel map will follow", "start");
6787 astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", 0);
6789 return 0;
6790 }
6791
6793 if (!channel_map) {
6794 astman_send_error(s, m, "Could not create channel map\n");
6795 return 0;
6796 }
6797
6798 astman_send_listack(s, m, "Channel map will follow", "start");
6799
6800 if (coreshowchannelmap_add_connected_channels(channel_map, channel_snapshot, bridge_snapshot)) {
6801 astman_send_error(s, m, "Could not complete channel map\n");
6802 ao2_ref(channel_map, -1);
6803 return 0;
6804 }
6805
6806 i = ao2_iterator_init(channel_map, 0);
6807 while ((current_channel_name = ao2_iterator_next(&i))) {
6808 astman_append(s,
6809 "Event: CoreShowChannelMap\r\n"
6810 "%s"
6811 "Channel: %s\r\n"
6812 "ConnectedChannel: %s\r\n\r\n",
6813 id_text,
6814 channel_name,
6815 current_channel_name);
6816 total++;
6817 }
6819
6820 ao2_ref(channel_map, -1);
6821 astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", total);
6823
6824 return 0;
6825}
6826
6827/*! \brief Manager command "LoggerRotate" - reloads and rotates the logger in
6828 * the same manner as the CLI command 'logger rotate'. */
6829static int action_loggerrotate(struct mansession *s, const struct message *m)
6830{
6831 if (ast_logger_rotate()) {
6832 astman_send_error(s, m, "Failed to reload the logger and rotate log files");
6833 return 0;
6834 }
6835
6836 astman_send_ack(s, m, "Reloaded the logger and rotated log files");
6837 return 0;
6838}
6839
6840/*! \brief Manager function to check if module is loaded */
6841static int manager_modulecheck(struct mansession *s, const struct message *m)
6842{
6843 const char *module = astman_get_header(m, "Module");
6844 const char *id = astman_get_header(m, "ActionID");
6845
6846 ast_debug(1, "**** ModuleCheck .so file %s\n", module);
6847 if (!ast_module_check(module)) {
6848 astman_send_error(s, m, "Module not loaded");
6849 return 0;
6850 }
6851
6852 astman_append(s, "Response: Success\r\n");
6853
6854 if (!ast_strlen_zero(id)) {
6855 astman_append(s, "ActionID: %s\r\n", id);
6856 }
6857
6858#if !defined(LOW_MEMORY)
6859 /* When we switched from subversion to git we lost the ability to
6860 * retrieve the 'ASTERISK_FILE_VERSION' from that file, but we retain
6861 * the response header here for backwards compatibility. */
6862 astman_append(s, "Version: \r\n");
6863#endif
6864
6865 astman_append(s, "\r\n");
6866
6867 return 0;
6868}
6869
6870/**
6871 * \brief Check if the given file path is in the modules dir or not
6872 *
6873 * \note When the module is being loaded / reloaded / unloaded, the modules dir is
6874 * automatically prepended
6875 *
6876 * \return 1 if inside modules dir
6877 * \return 0 if outside modules dir
6878 * \return -1 on failure
6879 */
6880static int file_in_modules_dir(const char *filename)
6881{
6882 char *stripped_filename;
6883 RAII_VAR(char *, path, NULL, ast_free);
6884 RAII_VAR(char *, real_path, NULL, ast_free);
6885
6886 /* Don't bother checking */
6887 if (live_dangerously) {
6888 return 1;
6889 }
6890
6891 stripped_filename = ast_strip(ast_strdupa(filename));
6892
6893 /* Always prepend the modules dir since that is what the code does for ModuleLoad */
6894 if (ast_asprintf(&path, "%s/%s", ast_config_AST_MODULE_DIR, stripped_filename) == -1) {
6895 return -1;
6896 }
6897
6898 real_path = realpath(path, NULL);
6899 if (!real_path) {
6900 return -1;
6901 }
6902
6903 return ast_begins_with(real_path, ast_config_AST_MODULE_DIR);
6904}
6905
6906static int manager_moduleload(struct mansession *s, const struct message *m)
6907{
6908 int res;
6909 const char *module = astman_get_header(m, "Module");
6910 const char *loadtype = astman_get_header(m, "LoadType");
6911 const char *recursive = astman_get_header(m, "Recursive");
6912
6913 if (!loadtype || strlen(loadtype) == 0) {
6914 astman_send_error(s, m, "Incomplete ModuleLoad action.");
6915 }
6916 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0) {
6917 astman_send_error(s, m, "Need module name");
6918 }
6919
6920 res = file_in_modules_dir(module);
6921 if (res == 0) {
6922 astman_send_error(s, m, "Module must be in the configured modules directory.");
6923 return 0;
6924 } else if (res == -1) {
6925 astman_send_error(s, m, "Module not found.");
6926 return 0;
6927 }
6928
6929 if (!strcasecmp(loadtype, "load")) {
6930 res = ast_load_resource(module);
6931 if (res) {
6932 astman_send_error(s, m, "Could not load module.");
6933 } else {
6934 astman_send_ack(s, m, "Module loaded.");
6935 }
6936 } else if (!strcasecmp(loadtype, "unload")) {
6937 res = ast_unload_resource(module, AST_FORCE_SOFT);
6938 if (res) {
6939 astman_send_error(s, m, "Could not unload module.");
6940 } else {
6941 astman_send_ack(s, m, "Module unloaded.");
6942 }
6943 } else if (!strcasecmp(loadtype, "refresh")) {
6944 res = ast_refresh_resource(module, AST_FORCE_SOFT, !ast_strlen_zero(recursive) && ast_true(recursive));
6945 if (res) {
6946 astman_send_error(s, m, "Could not refresh module.");
6947 } else {
6948 astman_send_ack(s, m, "Module unloaded and loaded.");
6949 }
6950 } else if (!strcasecmp(loadtype, "reload")) {
6951 /* TODO: Unify the ack/error messages here with action_reload */
6952 if (!ast_strlen_zero(module)) {
6953 enum ast_module_reload_result reload_res = ast_module_reload(module);
6954
6955 switch (reload_res) {
6957 astman_send_error(s, m, "No such module.");
6958 break;
6960 astman_send_error(s, m, "Module does not support reload action.");
6961 break;
6963 astman_send_error(s, m, "An unknown error occurred");
6964 break;
6966 astman_send_error(s, m, "A reload is in progress");
6967 break;
6969 astman_send_error(s, m, "Module not initialized");
6970 break;
6973 /* Treat a queued request as success */
6974 astman_send_ack(s, m, "Module reloaded.");
6975 break;
6976 }
6977 } else {
6978 ast_module_reload(NULL); /* Reload all modules */
6979 astman_send_ack(s, m, "All modules reloaded");
6980 }
6981 } else {
6982 astman_send_error(s, m, "Incomplete ModuleLoad action.");
6983 }
6984 return 0;
6985}
6986
6987static void log_action(const struct message *m, const char *action)
6988{
6989 struct ast_str *buf;
6990 int x;
6991
6992 if (!manager_debug) {
6993 return;
6994 }
6995
6996 buf = ast_str_create(256);
6997 if (!buf) {
6998 return;
6999 }
7000
7001 for (x = 0; x < m->hdrcount; ++x) {
7002 if (!strncasecmp(m->headers[x], "Secret", 6)) {
7003 ast_str_append(&buf, 0, "Secret: <redacted from logging>\n");
7004 } else {
7005 ast_str_append(&buf, 0, "%s\n", m->headers[x]);
7006 }
7007 }
7008
7009 ast_verbose("<--- Examining AMI action: -->\n%s\n", ast_str_buffer(buf));
7010 ast_free(buf);
7011}
7012
7013/*
7014 * Done with the action handlers here, we start with the code in charge
7015 * of accepting connections and serving them.
7016 * accept_thread() forks a new thread for each connection, session_do(),
7017 * which in turn calls get_input() repeatedly until a full message has
7018 * been accumulated, and then invokes process_message() to pass it to
7019 * the appropriate handler.
7020 */
7021
7022/*! \brief
7023 * Process an AMI message, performing desired action.
7024 * Return 0 on success, -1 on error that require the session to be destroyed.
7025 */
7026static int process_message(struct mansession *s, const struct message *m)
7027{
7028 int ret = 0;
7029 struct manager_action *act_found;
7030 struct ast_manager_user *user = NULL;
7031 const char *username;
7032 const char *action;
7033
7034 action = __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY);
7035 if (ast_strlen_zero(action)) {
7036 report_req_bad_format(s, "NONE");
7037 mansession_lock(s);
7038 astman_send_error(s, m, "Missing action in request");
7040 return 0;
7041 }
7042
7043 log_action(m, action);
7044
7045 if (ast_shutting_down()) {
7046 ast_log(LOG_ERROR, "Unable to process manager action '%s'. Asterisk is shutting down.\n", action);
7047 mansession_lock(s);
7048 astman_send_error(s, m, "Asterisk is shutting down");
7050 return 0;
7051 }
7052
7053 if (!s->session->authenticated
7054 && strcasecmp(action, "Login")
7055 && strcasecmp(action, "Logoff")
7056 && strcasecmp(action, "Challenge")) {
7057 if (!s->session->authenticated) {
7058 report_req_not_allowed(s, action);
7059 }
7060 mansession_lock(s);
7061 astman_send_error(s, m, "Permission denied");
7063 return 0;
7064 }
7065
7066 if (!s->session->authenticated
7067 && (!strcasecmp(action, "Login")
7068 || !strcasecmp(action, "Challenge"))) {
7069 username = astman_get_header(m, "Username");
7070
7071 if (!ast_strlen_zero(username) && check_manager_session_inuse(username)) {
7073 user = get_manager_by_name_locked(username);
7074 if (user && !user->allowmultiplelogin) {
7077 sleep(1);
7078 mansession_lock(s);
7079 astman_send_error(s, m, "Login Already In Use");
7081 return -1;
7082 }
7084 }
7085 }
7086
7087 act_found = action_find(action);
7088 if (act_found) {
7089 /* Found the requested AMI action. */
7090 int acted = 0;
7091
7092 if ((s->session->writeperm & act_found->authority)
7093 || act_found->authority == 0) {
7094 /* We have the authority to execute the action. */
7095 ret = -1;
7096 ao2_lock(act_found);
7097 if (act_found->registered && act_found->func) {
7098 struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
7099
7100 ao2_unlock(act_found);
7101 if (mod_ref || !act_found->module) {
7102 ast_debug(1, "Running action '%s'\n", act_found->action);
7103 ret = act_found->func(s, m);
7104 acted = 1;
7105 ast_module_unref(mod_ref);
7106 }
7107 } else {
7108 ao2_unlock(act_found);
7109 }
7110 }
7111 if (!acted) {
7112 /*
7113 * We did not execute the action because access was denied, it
7114 * was no longer registered, or no action was really registered.
7115 * Complain about it and leave.
7116 */
7117 report_req_not_allowed(s, action);
7118 mansession_lock(s);
7119 astman_send_error(s, m, "Permission denied");
7121 }
7122 ao2_t_ref(act_found, -1, "done with found action object");
7123 } else {
7124 char buf[512];
7125
7126 report_req_bad_format(s, action);
7127 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
7128 mansession_lock(s);
7129 astman_send_error(s, m, buf);
7131 }
7132 if (ret) {
7133 return ret;
7134 }
7135 /* Once done with our message, deliver any pending events unless the
7136 requester doesn't want them as part of this response.
7137 */
7138 if (ast_strlen_zero(astman_get_header(m, "SuppressEvents"))) {
7139 return process_events(s);
7140 } else {
7141 return ret;
7142 }
7143}
7144
7145/*!
7146 * Read one full line (including crlf) from the manager socket.
7147 * \note \verbatim
7148 * \r\n is the only valid terminator for the line.
7149 * (Note that, later, '\0' will be considered as the end-of-line marker,
7150 * so everything between the '\0' and the '\r\n' will not be used).
7151 * Also note that we assume output to have at least "maxlen" space.
7152 * \endverbatim
7153 */
7154static int get_input(struct mansession *s, char *output)
7155{
7156 int res, x;
7157 int maxlen = sizeof(s->session->inbuf) - 1;
7158 char *src = s->session->inbuf;
7159 int timeout = -1;
7160 time_t now;
7161
7162 /*
7163 * Look for \r\n within the buffer. If found, copy to the output
7164 * buffer and return, trimming the \r\n (not used afterwards).
7165 */
7166 for (x = 0; x < s->session->inlen; x++) {
7167 int cr; /* set if we have \r */
7168 if (src[x] == '\r' && x+1 < s->session->inlen && src[x + 1] == '\n') {
7169 cr = 2; /* Found. Update length to include \r\n */
7170 } else if (src[x] == '\n') {
7171 cr = 1; /* also accept \n only */
7172 } else {
7173 continue;
7174 }
7175 memmove(output, src, x); /*... but trim \r\n */
7176 output[x] = '\0'; /* terminate the string */
7177 x += cr; /* number of bytes used */
7178 s->session->inlen -= x; /* remaining size */
7179 memmove(src, src + x, s->session->inlen); /* remove used bytes */
7180 return 1;
7181 }
7182 if (s->session->inlen >= maxlen) {
7183 /* no crlf found, and buffer full - sorry, too long for us
7184 * keep the last character in case we are in the middle of a CRLF. */
7185 ast_log(LOG_WARNING, "Discarding message from %s. Line too long: %.25s...\n", ast_sockaddr_stringify_addr(&s->session->addr), src);
7186 src[0] = src[s->session->inlen - 1];
7187 s->session->inlen = 1;
7189 }
7190 res = 0;
7191 while (res == 0) {
7192 /* calculate a timeout if we are not authenticated */
7193 if (!s->session->authenticated) {
7194 if(time(&now) == -1) {
7195 ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
7196 return -1;
7197 }
7198
7199 timeout = (authtimeout - (now - s->session->authstart)) * 1000;
7200 if (timeout < 0) {
7201 /* we have timed out */
7202 return 0;
7203 }
7204 }
7205
7207 if (s->session->pending_event) {
7208 s->session->pending_event = 0;
7210 return 0;
7211 }
7212 s->session->waiting_thread = pthread_self();
7214
7216
7220 }
7221 if (res < 0) {
7222 if (s->session->kicked) {
7223 ast_debug(1, "Manager session has been kicked\n");
7224 return -1;
7225 }
7226 /* If we get a signal from some other thread (typically because
7227 * there are new events queued), return 0 to notify the caller.
7228 */
7229 if (errno == EINTR || errno == EAGAIN) {
7230 return 0;
7231 }
7232 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
7233 return -1;
7234 }
7235
7236 ao2_lock(s->session);
7237 res = ast_iostream_read(s->session->stream, src + s->session->inlen, maxlen - s->session->inlen);
7238 if (res < 1) {
7239 res = -1; /* error return */
7240 } else {
7241 s->session->inlen += res;
7242 src[s->session->inlen] = '\0';
7243 res = 0;
7244 }
7245 ao2_unlock(s->session);
7246 return res;
7247}
7248
7249/*!
7250 * \internal
7251 * \brief Error handling for sending parse errors. This function handles locking, and clearing the
7252 * parse error flag.
7253 *
7254 * \param s AMI session to process action request.
7255 * \param m Message that's in error.
7256 * \param error Error message to send.
7257 */
7258static void handle_parse_error(struct mansession *s, struct message *m, char *error)
7259{
7260 mansession_lock(s);
7261 astman_send_error(s, m, error);
7262 s->parsing = MESSAGE_OKAY;
7264}
7265
7266/*!
7267 * \internal
7268 * \brief Read and process an AMI action request.
7269 *
7270 * \param s AMI session to process action request.
7271 *
7272 * \retval 0 Retain AMI connection for next command.
7273 * \retval -1 Drop AMI connection due to logoff or connection error.
7274 */
7275static int do_message(struct mansession *s)
7276{
7277 struct message m = { 0 };
7278 char header_buf[sizeof(s->session->inbuf)] = { '\0' };
7279 int res;
7280 int hdr_loss;
7281 time_t now;
7282
7283 hdr_loss = 0;
7284 for (;;) {
7285 /* Check if any events are pending and do them if needed */
7286 if (process_events(s)) {
7287 res = -1;
7288 break;
7289 }
7290 res = get_input(s, header_buf);
7291 if (res == 0) {
7292 /* No input line received. */
7293 if (!s->session->authenticated) {
7294 if (time(&now) == -1) {
7295 ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
7296 res = -1;
7297 break;
7298 }
7299
7300 if (now - s->session->authstart > authtimeout) {
7301 if (displayconnects) {
7302 ast_verb(2, "Client from %s, failed to authenticate in %d seconds\n", ast_sockaddr_stringify_addr(&s->session->addr), authtimeout);
7303 }
7304 res = -1;
7305 break;
7306 }
7307 }
7308 continue;
7309 } else if (res > 0) {
7310 /* Input line received. */
7311 if (ast_strlen_zero(header_buf)) {
7312 if (hdr_loss) {
7313 mansession_lock(s);
7314 astman_send_error(s, &m, "Too many lines in message or allocation failure");
7316 res = 0;
7317 } else {
7318 switch (s->parsing) {
7319 case MESSAGE_OKAY:
7320 res = process_message(s, &m) ? -1 : 0;
7321 break;
7323 handle_parse_error(s, &m, "Failed to parse message: line too long");
7324 res = 0;
7325 break;
7326 }
7327 }
7328 break;
7329 } else if (m.hdrcount < ARRAY_LEN(m.headers)) {
7330 m.headers[m.hdrcount] = ast_strdup(header_buf);
7331 if (!m.headers[m.hdrcount]) {
7332 /* Allocation failure. */
7333 hdr_loss = 1;
7334 } else {
7335 ++m.hdrcount;
7336 }
7337 } else {
7338 /* Too many lines in message. */
7339 hdr_loss = 1;
7340 }
7341 } else {
7342 /* Input error. */
7343 break;
7344 }
7345 }
7346
7348
7349 return res;
7350}
7351
7352/*! \brief The body of the individual manager session.
7353 * Call get_input() to read one line at a time
7354 * (or be woken up on new events), collect the lines in a
7355 * message until found an empty line, and execute the request.
7356 * In any case, deliver events asynchronously through process_events()
7357 * (called from here if no line is available, or at the end of
7358 * process_message(). )
7359 */
7360static void *session_do(void *data)
7361{
7362 struct ast_tcptls_session_instance *ser = data;
7364 struct mansession s = {
7365 .tcptls_session = data,
7366 };
7367 int res;
7368 int arg = 1;
7369 struct ast_sockaddr ser_remote_address_tmp;
7370
7373 goto done;
7374 }
7375
7376 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
7377 session = build_mansession(&ser_remote_address_tmp);
7378
7379 if (session == NULL) {
7381 goto done;
7382 }
7383
7384 /* here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
7385 * This is necessary to prevent delays (caused by buffering) as we
7386 * write to the socket in bits and pieces. */
7387 if (setsockopt(ast_iostream_get_fd(ser->stream), IPPROTO_TCP, TCP_NODELAY, (char *) &arg, sizeof(arg)) < 0) {
7388 ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on manager connection: %s\n", strerror(errno));
7389 }
7391
7393 /* Hook to the tail of the event queue */
7394 session->last_ev = grab_last();
7395
7396 ast_mutex_init(&s.lock);
7397
7398 /* these fields duplicate those in the 'ser' structure */
7399 session->stream = s.stream = ser->stream;
7400 ast_sockaddr_copy(&session->addr, &ser_remote_address_tmp);
7401 s.session = session;
7402
7403 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
7404
7405 if(time(&session->authstart) == -1) {
7406 ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
7410 goto done;
7411 }
7413
7414 /*
7415 * We cannot let the stream exclusively wait for data to arrive.
7416 * We have to wake up the task to send async events.
7417 */
7419
7421 ast_tvnow(), authtimeout * 1000);
7422
7423 astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
7424 for (;;) {
7425 if ((res = do_message(&s)) < 0 || s.write_error || session->kicked) {
7426 break;
7427 }
7428 if (session->authenticated) {
7430 }
7431 }
7432 /* session is over, explain why and terminate */
7433 if (session->authenticated) {
7435 ast_verb(2, "Manager '%s' %s from %s\n", session->username, session->kicked ? "kicked" : "logged off", ast_sockaddr_stringify_addr(&session->addr));
7436 }
7437 } else {
7439 if (displayconnects) {
7440 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_sockaddr_stringify_addr(&session->addr));
7441 }
7442 }
7443
7445
7447done:
7448 ao2_ref(ser, -1);
7449 ser = NULL;
7450 return NULL;
7451}
7452
7453/*! \brief remove at most n_max stale session from the list. */
7454static int purge_sessions(int n_max)
7455{
7456 struct ao2_container *sessions;
7458 time_t now = time(NULL);
7459 struct ao2_iterator i;
7460 int purged = 0;
7461
7462 sessions = ao2_global_obj_ref(mgr_sessions);
7463 if (!sessions) {
7464 return 0;
7465 }
7467 ao2_ref(sessions, -1);
7468
7469 /* The order of operations is significant */
7470 while (n_max > 0 && (session = ao2_iterator_next(&i))) {
7472 if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
7473 if (session->authenticated
7474 && VERBOSITY_ATLEAST(2)
7476 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
7477 session->username, ast_sockaddr_stringify_addr(&session->addr));
7478 }
7481 n_max--;
7482 purged++;
7483 } else {
7486 }
7487 }
7489 return purged;
7490}
7491
7492/*! \brief
7493 * events are appended to a queue from where they
7494 * can be dispatched to clients.
7495 */
7496static int append_event(const char *str, int event_name_hash, int category)
7497{
7498 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
7499 static int seq; /* sequence number */
7500
7501 if (!tmp) {
7502 return -1;
7503 }
7504
7505 /* need to init all fields, because ast_malloc() does not */
7506 tmp->usecount = 0;
7507 tmp->category = category;
7508 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
7509 tmp->tv = ast_tvnow();
7510 tmp->event_name_hash = event_name_hash;
7511 AST_RWLIST_NEXT(tmp, eq_next) = NULL;
7512 strcpy(tmp->eventdata, str);
7513
7515 AST_RWLIST_INSERT_TAIL(&all_events, tmp, eq_next);
7517
7518 return 0;
7519}
7520
7521static void append_channel_vars(struct ast_str **pbuf, struct ast_channel *chan)
7522{
7523 struct varshead *vars;
7524 struct ast_var_t *var;
7525
7526 vars = ast_channel_get_manager_vars(chan);
7527 if (!vars) {
7528 return;
7529 }
7530
7531 AST_LIST_TRAVERSE(vars, var, entries) {
7532 ast_str_append(pbuf, 0, "ChanVariable(%s): %s=%s\r\n", ast_channel_name(chan), var->name, var->value);
7533 }
7534 ao2_ref(vars, -1);
7535}
7536
7537/* XXX see if can be moved inside the function */
7539#define MANAGER_EVENT_BUF_INITSIZE 256
7540
7541static int __attribute__((format(printf, 9, 0))) __manager_event_sessions_va(
7542 struct ao2_container *sessions,
7543 int category,
7544 const char *event,
7545 int chancount,
7546 struct ast_channel **chans,
7547 const char *file,
7548 int line,
7549 const char *func,
7550 const char *fmt,
7551 va_list ap)
7552{
7554 const char *cat_str;
7555 struct timeval now;
7556 struct ast_str *buf;
7557 int i;
7558 int event_name_hash;
7559
7562 ast_debug(3, "AMI Event '%s' is globally disabled, skipping\n", event);
7563 /* Event is globally disabled */
7564 return -1;
7565 }
7566 }
7567
7569 if (!buf) {
7570 return -1;
7571 }
7572
7573 cat_str = authority_to_str(category, &auth);
7574 ast_str_set(&buf, 0,
7575 "Event: %s\r\n"
7576 "Privilege: %s\r\n",
7577 event, cat_str);
7578
7579 if (timestampevents) {
7580 now = ast_tvnow();
7581 ast_str_append(&buf, 0,
7582 "Timestamp: %ld.%06lu\r\n",
7583 (long)now.tv_sec, (unsigned long) now.tv_usec);
7584 }
7585 if (manager_debug) {
7586 static int seq;
7587
7588 ast_str_append(&buf, 0,
7589 "SequenceNumber: %d\r\n",
7591 ast_str_append(&buf, 0,
7592 "File: %s\r\n"
7593 "Line: %d\r\n"
7594 "Func: %s\r\n",
7595 file, line, func);
7596 }
7598 ast_str_append(&buf, 0,
7599 "SystemName: %s\r\n",
7601 }
7602
7603 ast_str_append_va(&buf, 0, fmt, ap);
7604 for (i = 0; i < chancount; i++) {
7606 }
7607
7608 ast_str_append(&buf, 0, "\r\n");
7609
7610 event_name_hash = ast_str_hash(event);
7611
7612 append_event(ast_str_buffer(buf), event_name_hash, category);
7613
7614 /* Wake up any sleeping sessions */
7615 if (sessions) {
7616 struct ao2_iterator iter;
7618
7619 iter = ao2_iterator_init(sessions, 0);
7620 while ((session = ao2_iterator_next(&iter))) {
7621 ast_mutex_lock(&session->notify_lock);
7622 if (session->waiting_thread != AST_PTHREADT_NULL) {
7623 pthread_kill(session->waiting_thread, SIGURG);
7624 } else {
7625 /* We have an event to process, but the mansession is
7626 * not waiting for it. We still need to indicate that there
7627 * is an event waiting so that get_input processes the pending
7628 * event instead of polling.
7629 */
7630 session->pending_event = 1;
7631 }
7632 ast_mutex_unlock(&session->notify_lock);
7634 }
7635 ao2_iterator_destroy(&iter);
7636 }
7637
7638 if (category != EVENT_FLAG_SHUTDOWN && !AST_RWLIST_EMPTY(&manager_hooks)) {
7639 struct manager_custom_hook *hook;
7640
7642 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
7643 hook->helper(category, event, ast_str_buffer(buf));
7644 }
7646 }
7647
7648 return 0;
7649}
7650
7651static int __attribute__((format(printf, 9, 0))) __manager_event_sessions(
7652 struct ao2_container *sessions,
7653 int category,
7654 const char *event,
7655 int chancount,
7656 struct ast_channel **chans,
7657 const char *file,
7658 int line,
7659 const char *func,
7660 const char *fmt,
7661 ...)
7662{
7663 va_list ap;
7664 int res;
7665
7666 va_start(ap, fmt);
7668 chancount, chans, file, line, func, fmt, ap);
7669 va_end(ap);
7670 return res;
7671}
7672
7673int __ast_manager_event_multichan(int category, const char *event, int chancount,
7674 struct ast_channel **chans, const char *file, int line, const char *func,
7675 const char *fmt, ...)
7676{
7677 struct ao2_container *sessions = ao2_global_obj_ref(mgr_sessions);
7678 va_list ap;
7679 int res;
7680
7682 /* Nobody is listening */
7684 return 0;
7685 }
7686
7687 va_start(ap, fmt);
7689 file, line, func, fmt, ap);
7690 va_end(ap);
7692 return res;
7693}
7694
7695/*! \brief
7696 * support functions to register/unregister AMI action handlers,
7697 */
7698int ast_manager_unregister(const char *action)
7699{
7700 struct manager_action *cur;
7701
7704 if (!strcasecmp(action, cur->action)) {
7706 break;
7707 }
7708 }
7711
7712 if (cur) {
7713 /*
7714 * We have removed the action object from the container so we
7715 * are no longer in a hurry.
7716 */
7717 ao2_lock(cur);
7718 cur->registered = 0;
7719 ao2_unlock(cur);
7720
7721 ao2_t_ref(cur, -1, "action object removed from list");
7722 ast_verb(5, "Manager unregistered action %s\n", action);
7723 }
7724
7725 return 0;
7726}
7727
7728static int manager_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
7729{
7730 /* Notify managers of change */
7731 char hint[512];
7732
7733 hint[0] = '\0';
7734 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
7735
7736 switch(info->reason) {
7738 manager_event(EVENT_FLAG_CALL, "ExtensionStatus",
7739 "Exten: %s\r\n"
7740 "Context: %s\r\n"
7741 "Hint: %s\r\n"
7742 "Status: %d\r\n"
7743 "StatusText: %s\r\n",
7744 exten,
7745 context,
7746 hint,
7747 info->exten_state,
7748 ast_extension_state2str(info->exten_state));
7749 break;
7751 manager_event(EVENT_FLAG_CALL, "PresenceStatus",
7752 "Exten: %s\r\n"
7753 "Context: %s\r\n"
7754 "Hint: %s\r\n"
7755 "Status: %s\r\n"
7756 "Subtype: %s\r\n"
7757 "Message: %s\r\n",
7758 exten,
7759 context,
7760 hint,
7761 ast_presence_state2str(info->presence_state),
7762 info->presence_subtype,
7763 info->presence_message);
7764 break;
7765 }
7766 return 0;
7767}
7768
7770{
7771 struct manager_action *cur, *prev = NULL;
7772
7774 AST_RWLIST_TRAVERSE(&actions, cur, list) {
7775 int ret;
7776
7777 ret = strcasecmp(cur->action, act->action);
7778 if (ret == 0) {
7779 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
7781 return -1;
7782 }
7783 if (ret > 0) { /* Insert these alphabetically */
7784 break;
7785 }
7786 prev = cur;
7787 }
7788
7789 ao2_t_ref(act, +1, "action object added to list");
7790 act->registered = 1;
7791 if (prev) {
7792 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
7793 } else {
7794 AST_RWLIST_INSERT_HEAD(&actions, act, list);
7795 }
7796
7797 ast_verb(5, "Manager registered action %s\n", act->action);
7798
7800
7801 return 0;
7802}
7803
7804/*!
7805 * \internal
7806 * \brief Destroy the registered AMI action object.
7807 *
7808 * \param obj Object to destroy.
7809 */
7810static void action_destroy(void *obj)
7811{
7812 struct manager_action *doomed = obj;
7813
7814 if (doomed->synopsis) {
7815 /* The string fields were initialized. */
7817 }
7818 ao2_cleanup(doomed->final_response);
7819 ao2_cleanup(doomed->list_responses);
7820}
7821
7822/*! \brief register a new command with manager, including online help. This is
7823 the preferred way to register a manager command */
7824int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), struct ast_module *module, const char *synopsis, const char *description)
7825{
7826 struct manager_action *cur;
7827
7828 cur = ao2_t_alloc(sizeof(*cur), action_destroy, action);
7829 if (!cur) {
7830 return -1;
7831 }
7832 if (ast_string_field_init(cur, 128)) {
7833 ao2_t_ref(cur, -1, "action object creation failed");
7834 return -1;
7835 }
7836
7837 if (ast_string_field_init_extended(cur, since)) {
7838 ao2_t_ref(cur, -1, "action object creation failed");
7839 return -1;
7840 }
7841
7842 cur->action = action;
7843 cur->authority = auth;
7844 cur->func = func;
7845 cur->module = module;
7846#ifdef AST_XML_DOCS
7847 if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
7848 char *tmpxml;
7849
7850 tmpxml = ast_xmldoc_build_since("manager", action, NULL);
7851 ast_string_field_set(cur, since, tmpxml);
7852 ast_free(tmpxml);
7853
7854 tmpxml = ast_xmldoc_build_synopsis("manager", action, NULL);
7855 ast_string_field_set(cur, synopsis, tmpxml);
7856 ast_free(tmpxml);
7857
7858 tmpxml = ast_xmldoc_build_syntax("manager", action, NULL);
7859 ast_string_field_set(cur, syntax, tmpxml);
7860 ast_free(tmpxml);
7861
7862 tmpxml = ast_xmldoc_build_description("manager", action, NULL);
7863 ast_string_field_set(cur, description, tmpxml);
7864 ast_free(tmpxml);
7865
7866 tmpxml = ast_xmldoc_build_seealso("manager", action, NULL);
7867 ast_string_field_set(cur, seealso, tmpxml);
7868 ast_free(tmpxml);
7869
7870 tmpxml = ast_xmldoc_build_arguments("manager", action, NULL);
7871 ast_string_field_set(cur, arguments, tmpxml);
7872 ast_free(tmpxml);
7873
7874 cur->final_response = ast_xmldoc_build_final_response("manager", action, NULL);
7875 cur->list_responses = ast_xmldoc_build_list_responses("manager", action, NULL);
7876
7877 cur->docsrc = AST_XML_DOC;
7878 } else
7879#endif
7880 {
7882 ast_string_field_set(cur, description, description);
7883#ifdef AST_XML_DOCS
7884 cur->docsrc = AST_STATIC_DOC;
7885#endif
7886 }
7887 if (ast_manager_register_struct(cur)) {
7888 ao2_t_ref(cur, -1, "action object registration failed");
7889 return -1;
7890 }
7891
7892 ao2_t_ref(cur, -1, "action object registration successful");
7893 return 0;
7894}
7895/*! @}
7896 END Doxygen group */
7897
7898/*
7899 * The following are support functions for AMI-over-http.
7900 * The common entry point is generic_http_callback(),
7901 * which extracts HTTP header and URI fields and reformats
7902 * them into AMI messages, locates a proper session
7903 * (using the mansession_id Cookie or GET variable),
7904 * and calls process_message() as for regular AMI clients.
7905 * When done, the output (which goes to a temporary file)
7906 * is read back into a buffer and reformatted as desired,
7907 * then fed back to the client over the original socket.
7908 */
7909
7914};
7915
7916static const char * const contenttype[] = {
7917 [FORMAT_RAW] = "plain",
7918 [FORMAT_HTML] = "html",
7919 [FORMAT_XML] = "xml",
7920};
7921
7922/*!
7923 * locate an http session in the list. The search key (ident) is
7924 * the value of the mansession_id cookie (0 is not valid and means
7925 * a session on the AMI socket).
7926 */
7927static struct mansession_session *find_session(uint32_t ident, int incinuse)
7928{
7929 struct ao2_container *sessions;
7931 struct ao2_iterator i;
7932
7933 if (ident == 0) {
7934 return NULL;
7935 }
7936
7937 sessions = ao2_global_obj_ref(mgr_sessions);
7938 if (!sessions) {
7939 return NULL;
7940 }
7942 ao2_ref(sessions, -1);
7943 while ((session = ao2_iterator_next(&i))) {
7945 if (session->managerid == ident && !session->needdestroy) {
7946 ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
7947 break;
7948 }
7951 }
7953
7954 return session;
7955}
7956
7957/*!
7958 * locate an http session in the list.
7959 * The search keys (nonce) and (username) is value from received
7960 * "Authorization" http header.
7961 * As well as in find_session() function, the value of the nonce can't be zero.
7962 * (0 meansi, that the session used for AMI socket connection).
7963 * Flag (stale) is set, if client used valid, but old, nonce value.
7964 *
7965 */
7966static struct mansession_session *find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
7967{
7969 struct ao2_container *sessions;
7970 struct ao2_iterator i;
7971
7972 if (nonce == 0 || username == NULL || stale == NULL) {
7973 return NULL;
7974 }
7975
7976 sessions = ao2_global_obj_ref(mgr_sessions);
7977 if (!sessions) {
7978 return NULL;
7979 }
7981 ao2_ref(sessions, -1);
7982 while ((session = ao2_iterator_next(&i))) {
7984 if (!strcasecmp(session->username, username) && session->managerid == nonce) {
7985 *stale = 0;
7986 break;
7987 } else if (!strcasecmp(session->username, username) && session->oldnonce == nonce) {
7988 *stale = 1;
7989 break;
7990 }
7993 }
7995
7996 return session;
7997}
7998
7999int astman_is_authed(uint32_t ident)
8000{
8001 int authed;
8003
8004 if (!(session = find_session(ident, 0)))
8005 return 0;
8006
8007 authed = (session->authenticated != 0);
8008
8011
8012 return authed;
8013}
8014
8015int astman_verify_session_readpermissions(uint32_t ident, int perm)
8016{
8017 int result = 0;
8019 struct ao2_container *sessions;
8020 struct ao2_iterator i;
8021
8022 if (ident == 0) {
8023 return 0;
8024 }
8025
8026 sessions = ao2_global_obj_ref(mgr_sessions);
8027 if (!sessions) {
8028 return 0;
8029 }
8031 ao2_ref(sessions, -1);
8032 while ((session = ao2_iterator_next(&i))) {
8034 if ((session->managerid == ident) && (session->readperm & perm)) {
8035 result = 1;
8038 break;
8039 }
8042 }
8044
8045 return result;
8046}
8047
8048int astman_verify_session_writepermissions(uint32_t ident, int perm)
8049{
8050 int result = 0;
8052 struct ao2_container *sessions;
8053 struct ao2_iterator i;
8054
8055 if (ident == 0) {
8056 return 0;
8057 }
8058
8059 sessions = ao2_global_obj_ref(mgr_sessions);
8060 if (!sessions) {
8061 return 0;
8062 }
8064 ao2_ref(sessions, -1);
8065 while ((session = ao2_iterator_next(&i))) {
8067 if ((session->managerid == ident) && (session->writeperm & perm)) {
8068 result = 1;
8071 break;
8072 }
8075 }
8077
8078 return result;
8079}
8080
8081/*
8082 * convert to xml with various conversion:
8083 * mode & 1 -> lowercase;
8084 * mode & 2 -> replace non-alphanumeric chars with underscore
8085 */
8086static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
8087{
8088 /* store in a local buffer to avoid calling ast_str_append too often */
8089 char buf[256];
8090 char *dst = buf;
8091 const char *save = src;
8092 int space = sizeof(buf);
8093 /* repeat until done and nothing to flush */
8094 for ( ; *src || dst != buf ; src++) {
8095 if (*src == '\0' || space < 10) { /* flush */
8096 *dst++ = '\0';
8097 ast_str_append(out, 0, "%s", buf);
8098 dst = buf;
8099 space = sizeof(buf);
8100 if (*src == '\0') {
8101 break;
8102 }
8103 }
8104
8105 if (mode & 2) {
8106 if (save == src && isdigit(*src)) {
8107 /* The first character of an XML attribute cannot be a digit */
8108 *dst++ = '_';
8109 *dst++ = *src;
8110 space -= 2;
8111 continue;
8112 } else if (!isalnum(*src)) {
8113 /* Replace non-alphanumeric with an underscore */
8114 *dst++ = '_';
8115 space--;
8116 continue;
8117 }
8118 }
8119 switch (*src) {
8120 case '<':
8121 strcpy(dst, "&lt;");
8122 dst += 4;
8123 space -= 4;
8124 break;
8125 case '>':
8126 strcpy(dst, "&gt;");
8127 dst += 4;
8128 space -= 4;
8129 break;
8130 case '\"':
8131 strcpy(dst, "&quot;");
8132 dst += 6;
8133 space -= 6;
8134 break;
8135 case '\'':
8136 strcpy(dst, "&apos;");
8137 dst += 6;
8138 space -= 6;
8139 break;
8140 case '&':
8141 strcpy(dst, "&amp;");
8142 dst += 5;
8143 space -= 5;
8144 break;
8145
8146 default:
8147 *dst++ = mode ? tolower(*src) : *src;
8148 space--;
8149 }
8150 }
8151}
8152
8154 char *varname;
8156};
8157
8158static int variable_count_hash_fn(const void *vvc, const int flags)
8159{
8160 const struct variable_count *vc = vvc;
8161
8162 return ast_str_hash(vc->varname);
8163}
8164
8165static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
8166{
8167 /* Due to the simplicity of struct variable_count, it makes no difference
8168 * if you pass in objects or strings, the same operation applies. This is
8169 * due to the fact that the hash occurs on the first element, which means
8170 * the address of both the struct and the string are exactly the same. */
8171 struct variable_count *vc = obj;
8172 char *str = vstr;
8173 return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
8174}
8175
8176/*! \brief Convert the input into XML or HTML.
8177 * The input is supposed to be a sequence of lines of the form
8178 * Name: value
8179 * optionally followed by a blob of unformatted text.
8180 * A blank line is a section separator. Basically, this is a
8181 * mixture of the format of Manager Interface and CLI commands.
8182 * The unformatted text is considered as a single value of a field
8183 * named 'Opaque-data'.
8184 *
8185 * At the moment the output format is the following (but it may
8186 * change depending on future requirements so don't count too
8187 * much on it when writing applications):
8188 *
8189 * General: the unformatted text is used as a value of
8190 * XML output: to be completed
8191 *
8192 * \verbatim
8193 * Each section is within <response type="object" id="xxx">
8194 * where xxx is taken from ajaxdest variable or defaults to unknown
8195 * Each row is reported as an attribute Name="value" of an XML
8196 * entity named from the variable ajaxobjtype, default to "generic"
8197 * \endverbatim
8198 *
8199 * HTML output:
8200 * each Name-value pair is output as a single row of a two-column table.
8201 * Sections (blank lines in the input) are separated by a <HR>
8202 *
8203 */
8204static void xml_translate(struct ast_str **out, char *in, struct ast_variable *get_vars, enum output_format format)
8205{
8206 struct ast_variable *v;
8207 const char *dest = NULL;
8208 char *var, *val;
8209 const char *objtype = NULL;
8210 int in_data = 0; /* parsing data */
8211 int inobj = 0;
8212 int xml = (format == FORMAT_XML);
8213 struct variable_count *vc = NULL;
8214 struct ao2_container *vco = NULL;
8215
8216 if (xml) {
8217 /* dest and objtype need only for XML format */
8218 for (v = get_vars; v; v = v->next) {
8219 if (!strcasecmp(v->name, "ajaxdest")) {
8220 dest = v->value;
8221 } else if (!strcasecmp(v->name, "ajaxobjtype")) {
8222 objtype = v->value;
8223 }
8224 }
8225 if (ast_strlen_zero(dest)) {
8226 dest = "unknown";
8227 }
8228 if (ast_strlen_zero(objtype)) {
8229 objtype = "generic";
8230 }
8231 }
8232
8233 /* we want to stop when we find an empty line */
8234 while (in && *in) {
8235 val = strsep(&in, "\r\n"); /* mark start and end of line */
8236 if (in && *in == '\n') { /* remove trailing \n if any */
8237 in++;
8238 }
8240 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
8241 if (ast_strlen_zero(val)) {
8242 /* empty line */
8243 if (in_data) {
8244 /* close data in Opaque mode */
8245 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
8246 in_data = 0;
8247 }
8248
8249 if (inobj) {
8250 /* close block */
8251 ast_str_append(out, 0, xml ? " /></response>\n" :
8252 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
8253 inobj = 0;
8254 ao2_ref(vco, -1);
8255 vco = NULL;
8256 }
8257 continue;
8258 }
8259
8260 if (!inobj) {
8261 /* start new block */
8262 if (xml) {
8263 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
8264 }
8267 inobj = 1;
8268 }
8269
8270 if (in_data) {
8271 /* Process data field in Opaque mode. This is a
8272 * followup, so we re-add line feeds. */
8273 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
8274 xml_copy_escape(out, val, 0); /* data field */
8275 continue;
8276 }
8277
8278 /* We expect "Name: value" line here */
8279 var = strsep(&val, ":");
8280 if (val) {
8281 /* found the field name */
8284 } else {
8285 /* field name not found, switch to opaque mode */
8286 val = var;
8287 var = "Opaque-data";
8288 in_data = 1;
8289 }
8290
8291
8292 ast_str_append(out, 0, xml ? " " : "<tr><td>");
8293 if ((vc = ao2_find(vco, var, 0))) {
8294 vc->count++;
8295 } else {
8296 /* Create a new entry for this one */
8297 vc = ao2_alloc(sizeof(*vc), NULL);
8298 vc->varname = var;
8299 vc->count = 1;
8300 ao2_link(vco, vc);
8301 }
8302
8303 xml_copy_escape(out, var, xml ? 1 | 2 : 0); /* data name */
8304 if (vc->count > 1) {
8305 ast_str_append(out, 0, "-%d", vc->count);
8306 }
8307 ao2_ref(vc, -1);
8308 ast_str_append(out, 0, xml ? "='" : "</td><td>");
8309 xml_copy_escape(out, val, 0); /* data field */
8310 if (!in_data || !*in) {
8311 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
8312 }
8313 }
8314
8315 if (inobj) {
8316 ast_str_append(out, 0, xml ? " /></response>\n" :
8317 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
8318 ao2_ref(vco, -1);
8319 }
8320}
8321
8322static void close_mansession_file(struct mansession *s)
8323{
8324 if (s->stream) {
8326 s->stream = NULL;
8327 } else {
8328 ast_log(LOG_ERROR, "Attempted to close file/file descriptor on mansession without a valid file or file descriptor.\n");
8329 }
8330}
8331
8332static void process_output(struct mansession *s, struct ast_str **out, struct ast_variable *params, enum output_format format)
8333{
8334 char *buf;
8335 off_t l;
8336 int fd;
8337
8338 if (!s->stream)
8339 return;
8340
8341 /* Ensure buffer is NULL-terminated */
8342 ast_iostream_write(s->stream, "", 1);
8343
8344 fd = ast_iostream_get_fd(s->stream);
8345
8346 l = lseek(fd, 0, SEEK_CUR);
8347 if (l > 0) {
8348 if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0))) {
8349 ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n");
8350 } else {
8351 if (format == FORMAT_XML || format == FORMAT_HTML) {
8352 xml_translate(out, buf, params, format);
8353 } else {
8354 ast_str_append(out, 0, "%s", buf);
8355 }
8356 munmap(buf, l);
8357 }
8358 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
8359 xml_translate(out, "", params, format);
8360 }
8361
8363}
8364
8367 enum output_format format,
8368 const struct ast_sockaddr *remote_address, const char *uri,
8369 struct ast_variable *get_params,
8370 struct ast_variable *headers)
8371{
8372 struct mansession s = { .session = NULL, .tcptls_session = ser };
8374 uint32_t ident;
8375 int fd;
8376 int blastaway = 0;
8377 struct ast_variable *params = get_params;
8378 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
8379 struct ast_str *http_header = NULL, *out = NULL;
8380 struct message m = { 0 };
8381
8383 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
8384 return 0;
8385 }
8386
8387 ident = ast_http_manid_from_vars(headers);
8388
8389 if (!(session = find_session(ident, 1))) {
8390
8391 /**/
8392 /* Create new session.
8393 * While it is not in the list we don't need any locking
8394 */
8395 if (!(session = build_mansession(remote_address))) {
8397 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8398 return 0;
8399 }
8401 session->send_events = 0;
8402 session->inuse = 1;
8403 /*!
8404 * \note There is approximately a 1 in 1.8E19 chance that the following
8405 * calculation will produce 0, which is an invalid ID, but due to the
8406 * properties of the rand() function (and the constancy of s), that
8407 * won't happen twice in a row.
8408 */
8409 while ((session->managerid = ast_random() ^ (unsigned long) session) == 0) {
8410 }
8411 session->last_ev = grab_last();
8412 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
8413 }
8415
8416 http_header = ast_str_create(128);
8417 out = ast_str_create(2048);
8418
8419 ast_mutex_init(&s.lock);
8420
8421 if (http_header == NULL || out == NULL) {
8423 ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)");
8424 goto generic_callback_out;
8425 }
8426
8427 s.session = session;
8428 fd = mkstemp(template); /* create a temporary file for command output */
8429 unlink(template);
8430 if (fd <= -1) {
8431 ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)");
8432 goto generic_callback_out;
8433 }
8435 if (!s.stream) {
8436 ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
8437 ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)");
8438 close(fd);
8439 goto generic_callback_out;
8440 }
8441
8442 if (method == AST_HTTP_POST) {
8443 params = ast_http_get_post_vars(ser, headers);
8444 if (!params) {
8445 switch (errno) {
8446 case EFBIG:
8447 ast_http_error(ser, 413, "Request Entity Too Large", "Body too large");
8449 goto generic_callback_out;
8450 case ENOMEM:
8452 ast_http_error(ser, 500, "Server Error", "Out of memory");
8454 goto generic_callback_out;
8455 case EIO:
8456 ast_http_error(ser, 400, "Bad Request", "Error parsing request body");
8458 goto generic_callback_out;
8459 }
8460 }
8461 }
8462
8463 astman_append_headers(&m, params);
8464
8465 if (process_message(&s, &m)) {
8466 if (session->authenticated) {
8468 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr));
8469 }
8470 } else {
8471 if (displayconnects) {
8472 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_sockaddr_stringify_addr(&session->addr));
8473 }
8474 }
8475 session->needdestroy = 1;
8476 }
8477
8479
8480 ast_str_append(&http_header, 0,
8481 "Content-type: text/%s\r\n"
8482 "Set-Cookie: mansession_id=\"%08x\"; Version=1; Max-Age=%d\r\n"
8483 "Pragma: SuppressEvents\r\n",
8484 contenttype[format],
8485 session->managerid, httptimeout);
8486
8487 if (format == FORMAT_XML) {
8488 ast_str_append(&out, 0, "<ajax-response>\n");
8489 } else if (format == FORMAT_HTML) {
8490 /*
8491 * When handling AMI-over-HTTP in HTML format, we provide a simple form for
8492 * debugging purposes. This HTML code should not be here, we
8493 * should read from some config file...
8494 */
8495
8496#define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
8497#define TEST_STRING \
8498 "<form action=\"manager\" method=\"post\">\n\
8499 Action: <select name=\"action\">\n\
8500 <option value=\"\">-----&gt;</option>\n\
8501 <option value=\"login\">login</option>\n\
8502 <option value=\"command\">Command</option>\n\
8503 <option value=\"waitevent\">waitevent</option>\n\
8504 <option value=\"listcommands\">listcommands</option>\n\
8505 </select>\n\
8506 or <input name=\"action\"><br/>\n\
8507 CLI Command <input name=\"command\"><br>\n\
8508 user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
8509 <input type=\"submit\">\n</form>\n"
8510
8511 ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
8512 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
8513 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
8515 }
8516
8517 process_output(&s, &out, params, format);
8518
8519 if (format == FORMAT_XML) {
8520 ast_str_append(&out, 0, "</ajax-response>\n");
8521 } else if (format == FORMAT_HTML) {
8522 ast_str_append(&out, 0, "</table></body>\r\n");
8523 }
8524
8526 /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */
8527 session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
8528
8529 if (session->needdestroy) {
8530 if (session->inuse == 1) {
8531 ast_debug(1, "Need destroy, doing it now!\n");
8532 blastaway = 1;
8533 } else {
8534 ast_debug(1, "Need destroy, but can't do it yet!\n");
8535 ast_mutex_lock(&session->notify_lock);
8536 if (session->waiting_thread != AST_PTHREADT_NULL) {
8537 pthread_kill(session->waiting_thread, SIGURG);
8538 }
8539 ast_mutex_unlock(&session->notify_lock);
8540 session->inuse--;
8541 }
8542 } else {
8543 session->inuse--;
8544 }
8546
8547 ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
8548 http_header = NULL;
8549 out = NULL;
8550
8551generic_callback_out:
8553
8554 /* Clear resource */
8555
8556 if (method == AST_HTTP_POST && params) {
8557 ast_variables_destroy(params);
8558 }
8559 ast_free(http_header);
8560 ast_free(out);
8561
8562 if (session) {
8563 if (blastaway) {
8565 } else {
8566 if (session->stream) {
8567 ast_iostream_close(session->stream);
8568 session->stream = NULL;
8569 }
8571 }
8572 }
8573
8574 return 0;
8575}
8576
8579 enum output_format format,
8580 const struct ast_sockaddr *remote_address, const char *uri,
8581 struct ast_variable *get_params,
8582 struct ast_variable *headers)
8583{
8585 struct mansession s = { .session = NULL, .tcptls_session = ser };
8586 struct ast_variable *v, *params = get_params;
8587 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
8588 struct ast_str *http_header = NULL, *out = NULL;
8589 size_t result_size;
8590 struct message m = { 0 };
8591 int fd;
8592
8593 time_t time_now = time(NULL);
8594 unsigned long nonce = 0, nc;
8595 struct ast_http_digest d = { NULL, };
8596 struct ast_manager_user *user = NULL;
8597 int stale = 0;
8598 char resp_hash[256]="";
8599 /* Cache for user data */
8600 char u_username[80];
8601 int u_readperm;
8602 int u_writeperm;
8603 int u_writetimeout;
8604 int u_displayconnects;
8605
8607 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
8608 return 0;
8609 }
8610
8611 /* Find "Authorization: " header */
8612 for (v = headers; v; v = v->next) {
8613 if (!strcasecmp(v->name, "Authorization")) {
8614 break;
8615 }
8616 }
8617
8618 if (!v || ast_strlen_zero(v->value)) {
8619 goto out_401; /* Authorization Header not present - send auth request */
8620 }
8621
8622 /* Digest found - parse */
8623 if (ast_string_field_init(&d, 128)) {
8625 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8626 return 0;
8627 }
8628
8629 if (ast_parse_digest(v->value, &d, 0, 1)) {
8630 /* Error in Digest - send new one */
8631 nonce = 0;
8632 goto out_401;
8633 }
8634 if (sscanf(d.nonce, "%30lx", &nonce) != 1) {
8635 ast_log(LOG_WARNING, "Received incorrect nonce in Digest <%s>\n", d.nonce);
8636 nonce = 0;
8637 goto out_401;
8638 }
8639
8641 user = get_manager_by_name_locked(d.username);
8642 if(!user) {
8644 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_sockaddr_stringify_addr(&session->addr), d.username);
8645 nonce = 0;
8646 goto out_401;
8647 }
8648
8649 /* --- We have User for this auth, now check ACL */
8650 if (user->acl && !ast_apply_acl(user->acl, remote_address, "Manager User ACL:")) {
8652 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&session->addr), d.username);
8654 ast_http_error(ser, 403, "Permission denied", "Permission denied");
8655 return 0;
8656 }
8657
8658 /* --- We have auth, so check it */
8659
8660 /* compute the expected response to compare with what we received */
8661 {
8662 char *a2;
8663 /* ast_md5_hash outputs 32 characters plus NULL terminator. */
8664 char a2_hash[33];
8665 char resp[256];
8666
8667 /* XXX Now request method are hardcoded in A2 */
8668 if (ast_asprintf(&a2, "%s:%s", ast_get_http_method(method), d.uri) < 0) {
8671 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8672 return 0;
8673 }
8674
8675 ast_md5_hash(a2_hash, a2);
8676 ast_free(a2);
8677
8678 if (d.qop) {
8679 /* RFC 2617 */
8680 snprintf(resp, sizeof(resp), "%s:%08lx:%s:%s:auth:%s", user->a1_hash, nonce, d.nc, d.cnonce, a2_hash);
8681 } else {
8682 /* RFC 2069 */
8683 snprintf(resp, sizeof(resp), "%s:%08lx:%s", user->a1_hash, nonce, a2_hash);
8684 }
8685 ast_md5_hash(resp_hash, resp);
8686 }
8687
8688 if (strncasecmp(d.response, resp_hash, strlen(resp_hash))) {
8689 /* Something was wrong, so give the client to try with a new challenge */
8691 nonce = 0;
8692 goto out_401;
8693 }
8694
8695 /*
8696 * User are pass Digest authentication.
8697 * Now, cache the user data and unlock user list.
8698 */
8699 ast_copy_string(u_username, user->username, sizeof(u_username));
8700 u_readperm = user->readperm;
8701 u_writeperm = user->writeperm;
8702 u_displayconnects = user->displayconnects;
8703 u_writetimeout = user->writetimeout;
8705
8706 if (!(session = find_session_by_nonce(d.username, nonce, &stale))) {
8707 /*
8708 * Create new session.
8709 * While it is not in the list we don't need any locking
8710 */
8711 if (!(session = build_mansession(remote_address))) {
8713 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8714 return 0;
8715 }
8717
8718 ast_copy_string(session->username, u_username, sizeof(session->username));
8719 session->managerid = nonce;
8720 session->last_ev = grab_last();
8721 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
8722
8723 session->readperm = u_readperm;
8724 session->writeperm = u_writeperm;
8725 session->writetimeout = u_writetimeout;
8726
8727 if (u_displayconnects) {
8728 ast_verb(2, "HTTP Manager '%s' logged in from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr));
8729 }
8730 session->noncetime = session->sessionstart = time_now;
8731 session->authenticated = 1;
8732 } else if (stale) {
8733 /*
8734 * Session found, but nonce is stale.
8735 *
8736 * This could be because an old request (w/old nonce) arrived.
8737 *
8738 * This may be as the result of http proxy usage (separate delay or
8739 * multipath) or in a situation where a page was refreshed too quickly
8740 * (seen in Firefox).
8741 *
8742 * In this situation, we repeat the 401 auth with the current nonce
8743 * value.
8744 */
8745 nonce = session->managerid;
8747 stale = 1;
8748 goto out_401;
8749 } else {
8750 sscanf(d.nc, "%30lx", &nc);
8751 if (session->nc >= nc || ((time_now - session->noncetime) > 62) ) {
8752 /*
8753 * Nonce time expired (> 2 minutes) or something wrong with nonce
8754 * counter.
8755 *
8756 * Create new nonce key and resend Digest auth request. Old nonce
8757 * is saved for stale checking...
8758 */
8759 session->nc = 0; /* Reset nonce counter */
8760 session->oldnonce = session->managerid;
8761 nonce = session->managerid = ast_random();
8762 session->noncetime = time_now;
8764 stale = 1;
8765 goto out_401;
8766 } else {
8767 session->nc = nc; /* All OK, save nonce counter */
8768 }
8769 }
8770
8771
8772 /* Reset session timeout. */
8773 session->sessiontimeout = time(NULL) + (httptimeout > 5 ? httptimeout : 5);
8775
8776 ast_mutex_init(&s.lock);
8777 s.session = session;
8778 fd = mkstemp(template); /* create a temporary file for command output */
8779 unlink(template);
8780 if (fd <= -1) {
8781 ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)");
8782 goto auth_callback_out;
8783 }
8785 if (!s.stream) {
8786 ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
8787 ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)");
8788 close(fd);
8789 goto auth_callback_out;
8790 }
8791
8792 if (method == AST_HTTP_POST) {
8793 params = ast_http_get_post_vars(ser, headers);
8794 if (!params) {
8795 switch (errno) {
8796 case EFBIG:
8797 ast_http_error(ser, 413, "Request Entity Too Large", "Body too large");
8799 goto auth_callback_out;
8800 case ENOMEM:
8802 ast_http_error(ser, 500, "Server Error", "Out of memory");
8804 goto auth_callback_out;
8805 case EIO:
8806 ast_http_error(ser, 400, "Bad Request", "Error parsing request body");
8808 goto auth_callback_out;
8809 }
8810 }
8811 }
8812
8813 astman_append_headers(&m, params);
8814
8815 if (process_message(&s, &m)) {
8816 if (u_displayconnects) {
8817 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr));
8818 }
8819
8820 session->needdestroy = 1;
8821 }
8822
8824
8825 result_size = lseek(ast_iostream_get_fd(s.stream), 0, SEEK_CUR); /* Calculate approx. size of result */
8826
8827 http_header = ast_str_create(80);
8828 out = ast_str_create(result_size * 2 + 512);
8829 if (http_header == NULL || out == NULL) {
8831 ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)");
8833 goto auth_callback_out;
8834 }
8835
8836 ast_str_append(&http_header, 0, "Content-type: text/%s\r\n", contenttype[format]);
8837
8838 if (format == FORMAT_XML) {
8839 ast_str_append(&out, 0, "<ajax-response>\n");
8840 } else if (format == FORMAT_HTML) {
8841 ast_str_append(&out, 0,
8842 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
8843 "<html><head>\r\n"
8844 "<title>Asterisk&trade; Manager Interface</title>\r\n"
8845 "</head><body style=\"background-color: #ffffff;\">\r\n"
8846 "<form method=\"POST\">\r\n"
8847 "<table align=\"center\" style=\"background-color: #f1f1f1;\" width=\"500\">\r\n"
8848 "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\"><h1>Manager Tester</h1></th></tr>\r\n"
8849 "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\">Action: <input name=\"action\" /> Cmd: <input name=\"command\" /><br>"
8850 "<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n");
8851 }
8852
8853 process_output(&s, &out, params, format);
8854
8855 if (format == FORMAT_XML) {
8856 ast_str_append(&out, 0, "</ajax-response>\n");
8857 } else if (format == FORMAT_HTML) {
8858 ast_str_append(&out, 0, "</table></form></body></html>\r\n");
8859 }
8860
8861 ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
8862 http_header = NULL;
8863 out = NULL;
8864
8865auth_callback_out:
8867
8868 /* Clear resources and unlock manager session */
8869 if (method == AST_HTTP_POST && params) {
8870 ast_variables_destroy(params);
8871 }
8872
8873 ast_free(http_header);
8874 ast_free(out);
8875
8877 if (session->stream) {
8878 ast_iostream_close(session->stream);
8879 session->stream = NULL;
8880 }
8882
8883 if (session->needdestroy) {
8884 ast_debug(1, "Need destroy, doing it now!\n");
8886 }
8888 return 0;
8889
8890out_401:
8891 if (!nonce) {
8892 nonce = ast_random();
8893 }
8894
8895 ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL);
8897 return 0;
8898}
8899
8900static int manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
8901{
8902 int retval;
8903 struct ast_sockaddr ser_remote_address_tmp;
8904
8905 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8906 retval = generic_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
8907 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8908 return retval;
8909}
8910
8911static int mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
8912{
8913 int retval;
8914 struct ast_sockaddr ser_remote_address_tmp;
8915
8916 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8917 retval = generic_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
8918 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8919 return retval;
8920}
8921
8922static int rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
8923{
8924 int retval;
8925 struct ast_sockaddr ser_remote_address_tmp;
8926
8927 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8928 retval = generic_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
8929 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8930 return retval;
8931}
8932
8933static struct ast_http_uri rawmanuri = {
8934 .description = "Raw HTTP Manager Event Interface",
8935 .uri = "rawman",
8936 .callback = rawman_http_callback,
8937 .data = NULL,
8938 .key = __FILE__,
8939};
8940
8941static struct ast_http_uri manageruri = {
8942 .description = "HTML Manager Event Interface",
8943 .uri = "manager",
8944 .callback = manager_http_callback,
8945 .data = NULL,
8946 .key = __FILE__,
8947};
8948
8950 .description = "XML Manager Event Interface",
8951 .uri = "mxml",
8952 .callback = mxml_http_callback,
8953 .data = NULL,
8954 .key = __FILE__,
8955};
8956
8957
8958/* Callback with Digest authentication */
8959static int auth_manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
8960{
8961 int retval;
8962 struct ast_sockaddr ser_remote_address_tmp;
8963
8964 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8965 retval = auth_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
8966 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8967 return retval;
8968}
8969
8970static int auth_mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
8971{
8972 int retval;
8973 struct ast_sockaddr ser_remote_address_tmp;
8974
8975 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8976 retval = auth_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
8977 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8978 return retval;
8979}
8980
8981static int auth_rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
8982{
8983 int retval;
8984 struct ast_sockaddr ser_remote_address_tmp;
8985
8986 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8987 retval = auth_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
8988 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8989 return retval;
8990}
8991
8992static struct ast_http_uri arawmanuri = {
8993 .description = "Raw HTTP Manager Event Interface w/Digest authentication",
8994 .uri = "arawman",
8995 .has_subtree = 0,
8996 .callback = auth_rawman_http_callback,
8997 .data = NULL,
8998 .key = __FILE__,
8999};
9000
9001static struct ast_http_uri amanageruri = {
9002 .description = "HTML Manager Event Interface w/Digest authentication",
9003 .uri = "amanager",
9004 .has_subtree = 0,
9005 .callback = auth_manager_http_callback,
9006 .data = NULL,
9007 .key = __FILE__,
9008};
9009
9011 .description = "XML Manager Event Interface w/Digest authentication",
9012 .uri = "amxml",
9013 .has_subtree = 0,
9014 .callback = auth_mxml_http_callback,
9015 .data = NULL,
9016 .key = __FILE__,
9017};
9018
9019/*! \brief Get number of logged in sessions for a login name */
9020static int get_manager_sessions_cb(void *obj, void *arg, void *data, int flags)
9021{
9022 struct mansession_session *session = obj;
9023 const char *login = (char *)arg;
9024 int *no_sessions = data;
9025
9026 if (strcasecmp(session->username, login) == 0) {
9027 (*no_sessions)++;
9028 }
9029
9030 return 0;
9031}
9032
9033
9034/*! \brief ${AMI_CLIENT()} Dialplan function - reads manager client data */
9035static int function_amiclient(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9036{
9037 struct ast_manager_user *user = NULL;
9038
9041 AST_APP_ARG(param);
9042 );
9043
9044
9045 if (ast_strlen_zero(data) ) {
9046 ast_log(LOG_WARNING, "AMI_CLIENT() requires two arguments: AMI_CLIENT(<name>[,<arg>])\n");
9047 return -1;
9048 }
9050 args.name = ast_strip(args.name);
9051 args.param = ast_strip(args.param);
9052
9054 if (!(user = get_manager_by_name_locked(args.name))) {
9056 ast_log(LOG_ERROR, "There's no manager user called : \"%s\"\n", args.name);
9057 return -1;
9058 }
9060
9061 if (!strcasecmp(args.param, "sessions")) {
9062 int no_sessions = 0;
9063 struct ao2_container *sessions;
9064
9065 sessions = ao2_global_obj_ref(mgr_sessions);
9066 if (sessions) {
9067 ao2_callback_data(sessions, 0, get_manager_sessions_cb, /*login name*/ data, &no_sessions);
9068 ao2_ref(sessions, -1);
9069 }
9070 snprintf(buf, len, "%d", no_sessions);
9071 } else {
9072 ast_log(LOG_ERROR, "Invalid arguments provided to function AMI_CLIENT: %s\n", args.param);
9073 return -1;
9074
9075 }
9076
9077 return 0;
9078}
9079
9080
9081/*! \brief description of AMI_CLIENT dialplan function */
9083 .name = "AMI_CLIENT",
9084 .read = function_amiclient,
9085 .read_max = 12,
9086};
9087
9088static int webregged = 0;
9089
9090/*! \brief cleanup code called at each iteration of server_root,
9091 * guaranteed to happen every 5 seconds at most
9092 */
9093static void purge_old_stuff(void *data)
9094{
9095 struct ast_tcptls_session_args *ser = data;
9096 /* purge_sessions will return the number of sessions actually purged,
9097 * up to a maximum of it's arguments, purge one at a time, keeping a
9098 * purge interval of 1ms as long as we purged a session, otherwise
9099 * revert to a purge check every 5s
9100 */
9101 if (purge_sessions(1) == 1) {
9102 ser->poll_timeout = 1;
9103 } else {
9104 ser->poll_timeout = 5000;
9105 }
9106 purge_events();
9107}
9108
9111 .accept_fd = -1,
9112 .master = AST_PTHREADT_NULL,
9113 .tls_cfg = NULL,
9114 .poll_timeout = 5000, /* wake up every 5 seconds */
9115 .periodic_fn = purge_old_stuff,
9116 .name = "AMI server",
9117 .accept_fn = ast_tcptls_server_root, /* thread doing the accept() */
9118 .worker_fn = session_do, /* thread handling the session */
9119};
9120
9122 .accept_fd = -1,
9123 .master = AST_PTHREADT_NULL,
9124 .tls_cfg = &ami_tls_cfg,
9125 .poll_timeout = -1, /* the other does the periodic cleanup */
9126 .name = "AMI TLS server",
9127 .accept_fn = ast_tcptls_server_root, /* thread doing the accept() */
9128 .worker_fn = session_do, /* thread handling the session */
9129};
9130
9131/*! \brief CLI command manager show settings */
9132static char *handle_manager_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9133{
9134 switch (cmd) {
9135 case CLI_INIT:
9136 e->command = "manager show settings";
9137 e->usage =
9138 "Usage: manager show settings\n"
9139 " Provides detailed list of the configuration of the Manager.\n";
9140 return NULL;
9141 case CLI_GENERATE:
9142 return NULL;
9143 }
9144#define FORMAT " %-25.25s %-15.55s\n"
9145#define FORMAT2 " %-25.25s %-15d\n"
9146#define FORMAT3 " %-25.25s %s\n"
9147 if (a->argc != 3) {
9148 return CLI_SHOWUSAGE;
9149 }
9150 ast_cli(a->fd, "\nGlobal Settings:\n");
9151 ast_cli(a->fd, "----------------\n");
9152 ast_cli(a->fd, FORMAT, "Manager (AMI):", AST_CLI_YESNO(manager_enabled));
9153 ast_cli(a->fd, FORMAT, "Web Manager (AMI/HTTP):", AST_CLI_YESNO(webmanager_enabled));
9154 ast_cli(a->fd, FORMAT, "TCP Bindaddress:", manager_enabled != 0 ? ast_sockaddr_stringify(&ami_desc.local_address) : "Disabled");
9155 ast_cli(a->fd, FORMAT2, "HTTP Timeout (seconds):", httptimeout);
9156 ast_cli(a->fd, FORMAT, "TLS Enable:", AST_CLI_YESNO(ami_tls_cfg.enabled));
9157 ast_cli(a->fd, FORMAT, "TLS Bindaddress:", ami_tls_cfg.enabled != 0 ? ast_sockaddr_stringify(&amis_desc.local_address) : "Disabled");
9158 ast_cli(a->fd, FORMAT, "TLS Certfile:", ami_tls_cfg.certfile);
9159 ast_cli(a->fd, FORMAT, "TLS Privatekey:", ami_tls_cfg.pvtfile);
9160 ast_cli(a->fd, FORMAT, "TLS Cipher:", ami_tls_cfg.cipher);
9161 ast_cli(a->fd, FORMAT, "Allow multiple login:", AST_CLI_YESNO(allowmultiplelogin));
9162 ast_cli(a->fd, FORMAT, "Display connects:", AST_CLI_YESNO(displayconnects));
9163 ast_cli(a->fd, FORMAT, "Timestamp events:", AST_CLI_YESNO(timestampevents));
9164 ast_cli(a->fd, FORMAT3, "Channel vars:", S_OR(manager_channelvars, ""));
9165 ast_cli(a->fd, FORMAT3, "Disabled events:", S_OR(manager_disabledevents, ""));
9166 ast_cli(a->fd, FORMAT, "Debug:", AST_CLI_YESNO(manager_debug));
9167#undef FORMAT
9168#undef FORMAT2
9169#undef FORMAT3
9170
9171 return CLI_SUCCESS;
9172}
9173
9174#ifdef AST_XML_DOCS
9175
9176static int ast_xml_doc_item_cmp_fn(const void *a, const void *b)
9177{
9178 struct ast_xml_doc_item **item_a = (struct ast_xml_doc_item **)a;
9179 struct ast_xml_doc_item **item_b = (struct ast_xml_doc_item **)b;
9180 return strcmp((*item_a)->name, (*item_b)->name);
9181}
9182
9183static char *handle_manager_show_events(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9184{
9185 struct ao2_container *events;
9186 struct ao2_iterator *it_events;
9187 struct ast_xml_doc_item *item;
9188 struct ast_xml_doc_item **items;
9189 struct ast_str *buffer;
9190 int i = 0, totalitems = 0;
9191
9192 switch (cmd) {
9193 case CLI_INIT:
9194 e->command = "manager show events";
9195 e->usage =
9196 "Usage: manager show events\n"
9197 " Prints a listing of the available Asterisk manager interface events.\n";
9198 return NULL;
9199 case CLI_GENERATE:
9200 return NULL;
9201 }
9202 if (a->argc != 3) {
9203 return CLI_SHOWUSAGE;
9204 }
9205
9206 buffer = ast_str_create(128);
9207 if (!buffer) {
9208 return CLI_SUCCESS;
9209 }
9210
9211 events = ao2_global_obj_ref(event_docs);
9212 if (!events) {
9213 ast_cli(a->fd, "No manager event documentation loaded\n");
9214 ast_free(buffer);
9215 return CLI_SUCCESS;
9216 }
9217
9219 if (!(it_events = ao2_callback(events, OBJ_MULTIPLE | OBJ_NOLOCK, NULL, NULL))) {
9221 ast_log(AST_LOG_ERROR, "Unable to create iterator for events container\n");
9222 ast_free(buffer);
9223 ao2_ref(events, -1);
9224 return CLI_SUCCESS;
9225 }
9226 if (!(items = ast_calloc(sizeof(struct ast_xml_doc_item *), ao2_container_count(events)))) {
9228 ast_log(AST_LOG_ERROR, "Unable to create temporary sorting array for events\n");
9229 ao2_iterator_destroy(it_events);
9230 ast_free(buffer);
9231 ao2_ref(events, -1);
9232 return CLI_SUCCESS;
9233 }
9235
9236 while ((item = ao2_iterator_next(it_events))) {
9237 items[totalitems++] = item;
9238 ao2_ref(item, -1);
9239 }
9240
9241 qsort(items, totalitems, sizeof(struct ast_xml_doc_item *), ast_xml_doc_item_cmp_fn);
9242
9243 ast_cli(a->fd, "Events:\n");
9244 ast_cli(a->fd, " -------------------- -------------------- -------------------- \n");
9245 for (i = 0; i < totalitems; i++) {
9246 ast_str_append(&buffer, 0, " %-20.20s", items[i]->name);
9247 if ((i + 1) % 3 == 0) {
9248 ast_cli(a->fd, "%s\n", ast_str_buffer(buffer));
9249 ast_str_set(&buffer, 0, "%s", "");
9250 }
9251 }
9252 if ((i + 1) % 3 != 0) {
9253 ast_cli(a->fd, "%s\n", ast_str_buffer(buffer));
9254 }
9255
9256 ao2_iterator_destroy(it_events);
9257 ast_free(items);
9258 ao2_ref(events, -1);
9259 ast_free(buffer);
9260
9261 return CLI_SUCCESS;
9262}
9263
9264static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance)
9265{
9266 char *since, *syntax, *description, *synopsis, *seealso, *arguments;
9267
9268 synopsis = ast_xmldoc_printable(AS_OR(instance->synopsis, "Not available"), 1);
9269 since = ast_xmldoc_printable(AS_OR(instance->since, "Not available"), 1);
9270 description = ast_xmldoc_printable(AS_OR(instance->description, "Not available"), 1);
9271 syntax = ast_xmldoc_printable(AS_OR(instance->syntax, "Not available"), 1);
9272 arguments = ast_xmldoc_printable(AS_OR(instance->arguments, "Not available"), 1);
9273 seealso = ast_xmldoc_printable(AS_OR(instance->seealso, "Not available"), 1);
9274
9275 if (!synopsis || !since || !description || !syntax || !arguments || !seealso) {
9276 ast_cli(a->fd, "Error: Memory allocation failed\n");
9277 goto free_docs;
9278 }
9279
9280 ast_cli(a->fd, "\n"
9281 "%s -= Info about Manager Event '%s' =- %s\n\n"
9282 COLORIZE_FMT "\n"
9283 "%s\n\n"
9284 COLORIZE_FMT "\n"
9285 "%s\n\n"
9286 COLORIZE_FMT "\n"
9287 "%s\n\n"
9288 COLORIZE_FMT "\n"
9289 "%s\n\n"
9290 COLORIZE_FMT "\n"
9291 "%s\n\n"
9292 COLORIZE_FMT "\n"
9293 "%s\n\n",
9295 COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
9296 COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
9297 COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
9298 COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
9299 COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
9300 COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
9301 );
9302
9303free_docs:
9305 ast_free(since);
9306 ast_free(description);
9307 ast_free(syntax);
9308 ast_free(arguments);
9309 ast_free(seealso);
9310}
9311
9312static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9313{
9315 struct ao2_iterator it_events;
9316 struct ast_xml_doc_item *item, *temp;
9317 int length;
9318
9319 if (cmd == CLI_INIT) {
9320 e->command = "manager show event";
9321 e->usage =
9322 "Usage: manager show event <eventname>\n"
9323 " Provides a detailed description a Manager interface event.\n";
9324 return NULL;
9325 }
9326
9327 events = ao2_global_obj_ref(event_docs);
9328 if (!events) {
9329 ast_cli(a->fd, "No manager event documentation loaded\n");
9330 return CLI_SUCCESS;
9331 }
9332
9333 if (cmd == CLI_GENERATE) {
9334 if (a->pos != 3) {
9335 return NULL;
9336 }
9337
9338 length = strlen(a->word);
9339 it_events = ao2_iterator_init(events, 0);
9340 while ((item = ao2_iterator_next(&it_events))) {
9341 if (!strncasecmp(a->word, item->name, length)) {
9343 ao2_ref(item, -1);
9344 break;
9345 }
9346 }
9347 ao2_ref(item, -1);
9348 }
9349 ao2_iterator_destroy(&it_events);
9350
9351 return NULL;
9352 }
9353
9354 if (a->argc != 4) {
9355 return CLI_SHOWUSAGE;
9356 }
9357
9358 if (!(item = ao2_find(events, a->argv[3], OBJ_KEY))) {
9359 ast_cli(a->fd, "Could not find event '%s'\n", a->argv[3]);
9360 return CLI_SUCCESS;
9361 }
9362
9363 ast_cli(a->fd, "Event: %s\n", a->argv[3]);
9364 for (temp = item; temp; temp = AST_LIST_NEXT(temp, next)) {
9365 print_event_instance(a, temp);
9366 }
9367
9368 ao2_ref(item, -1);
9369 return CLI_SUCCESS;
9370}
9371
9372#endif
9373
9374static struct ast_cli_entry cli_manager[] = {
9375 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
9376 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
9377 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
9378 AST_CLI_DEFINE(handle_kickmanconn, "Kick a connected manager interface connection"),
9379 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
9380 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
9381 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
9382 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
9383 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
9384 AST_CLI_DEFINE(handle_manager_show_settings, "Show manager global settings"),
9385#ifdef AST_XML_DOCS
9386 AST_CLI_DEFINE(handle_manager_show_events, "List manager interface events"),
9387 AST_CLI_DEFINE(handle_manager_show_event, "Show a manager interface event"),
9388#endif
9389};
9390
9391/*!
9392 * \internal
9393 * \brief Load the config channelvars variable.
9394 *
9395 * \param var Config variable to load.
9396 */
9398{
9399 char *parse = NULL;
9401 AST_APP_ARG(vars)[MAX_VARS];
9402 );
9403
9406
9407 /* parse the setting */
9410
9412}
9413
9414/*!
9415 * \internal
9416 * \brief Load the config disabledevents variable.
9417 *
9418 * \param var Config variable to load.
9419 */
9421{
9424}
9425
9426/*!
9427 * \internal
9428 * \brief Free a user record. Should already be removed from the list
9429 */
9431{
9432 ast_free(user->a1_hash);
9433 ast_free(user->secret);
9434 if (user->includefilters) {
9435 ao2_t_ref(user->includefilters, -1, "decrement ref for include container, should be last one");
9436 }
9437 if (user->excludefilters) {
9438 ao2_t_ref(user->excludefilters, -1, "decrement ref for exclude container, should be last one");
9439 }
9440 user->acl = ast_free_acl_list(user->acl);
9441 ast_variables_destroy(user->chanvars);
9442 ast_free(user);
9443}
9444
9445/*!
9446 * \internal
9447 * \brief Clean up resources on Asterisk shutdown
9448 */
9449static void manager_shutdown(void)
9450{
9451 struct ast_manager_user *user;
9452
9453#ifdef TEST_FRAMEWORK
9454 AST_TEST_UNREGISTER(eventfilter_test_creation);
9455 AST_TEST_UNREGISTER(eventfilter_test_matching);
9456 AST_TEST_UNREGISTER(originate_permissions_test);
9457#endif
9458
9459 /* This event is not actually transmitted, but causes all TCP sessions to be closed */
9460 manager_event(EVENT_FLAG_SHUTDOWN, "CloseSession", "CloseSession: true\r\n");
9461
9462 ast_manager_unregister("Ping");
9463 ast_manager_unregister("Events");
9464 ast_manager_unregister("Logoff");
9465 ast_manager_unregister("Login");
9466 ast_manager_unregister("Challenge");
9467 ast_manager_unregister("Hangup");
9468 ast_manager_unregister("Status");
9469 ast_manager_unregister("Setvar");
9470 ast_manager_unregister("Getvar");
9471 ast_manager_unregister("GetConfig");
9472 ast_manager_unregister("GetConfigJSON");
9473 ast_manager_unregister("UpdateConfig");
9474 ast_manager_unregister("CreateConfig");
9475 ast_manager_unregister("ListCategories");
9476 ast_manager_unregister("Redirect");
9477 ast_manager_unregister("Atxfer");
9478 ast_manager_unregister("CancelAtxfer");
9479 ast_manager_unregister("Originate");
9480 ast_manager_unregister("Command");
9481 ast_manager_unregister("ExtensionState");
9482 ast_manager_unregister("PresenceState");
9483 ast_manager_unregister("AbsoluteTimeout");
9484 ast_manager_unregister("MailboxStatus");
9485 ast_manager_unregister("MailboxCount");
9486 ast_manager_unregister("ListCommands");
9487 ast_manager_unregister("SendText");
9488 ast_manager_unregister("UserEvent");
9489 ast_manager_unregister("WaitEvent");
9490 ast_manager_unregister("CoreSettings");
9491 ast_manager_unregister("CoreStatus");
9492 ast_manager_unregister("Reload");
9493 ast_manager_unregister("LoggerRotate");
9494 ast_manager_unregister("CoreShowChannels");
9495 ast_manager_unregister("CoreShowChannelMap");
9496 ast_manager_unregister("ModuleLoad");
9497 ast_manager_unregister("ModuleCheck");
9498 ast_manager_unregister("AOCMessage");
9499 ast_manager_unregister("Filter");
9500 ast_manager_unregister("BlindTransfer");
9503
9504#ifdef AST_XML_DOCS
9505 ao2_t_global_obj_release(event_docs, "Dispose of event_docs");
9506#endif
9507
9508#ifdef TEST_FRAMEWORK
9509 stasis_forward_cancel(test_suite_forwarder);
9510 test_suite_forwarder = NULL;
9511#endif
9512
9513 if (stasis_router) {
9516 }
9524
9527
9538
9539 ao2_global_obj_release(mgr_sessions);
9540
9541 while ((user = AST_LIST_REMOVE_HEAD(&users, list))) {
9543 }
9545
9548}
9549
9550
9551/*! \brief Initialize all \ref stasis topics and routers used by the various
9552 * sub-components of AMI
9553 */
9555{
9556 int res = 0;
9557
9559 if (!rtp_topic_forwarder) {
9560 return -1;
9561 }
9562
9565 return -1;
9566 }
9567
9569 if (!stasis_router) {
9570 return -1;
9571 }
9574
9577
9580
9581 if (res != 0) {
9582 return -1;
9583 }
9584 return 0;
9585}
9586
9587static int subscribe_all(void)
9588{
9590 ast_log(AST_LOG_ERROR, "Failed to initialize manager subscriptions\n");
9591 return -1;
9592 }
9593 if (manager_system_init()) {
9594 ast_log(AST_LOG_ERROR, "Failed to initialize manager system handling\n");
9595 return -1;
9596 }
9597 if (manager_channels_init()) {
9598 ast_log(AST_LOG_ERROR, "Failed to initialize manager channel handling\n");
9599 return -1;
9600 }
9601 if (manager_mwi_init()) {
9602 ast_log(AST_LOG_ERROR, "Failed to initialize manager MWI handling\n");
9603 return -1;
9604 }
9605 if (manager_bridging_init()) {
9606 return -1;
9607 }
9608 if (manager_endpoints_init()) {
9609 ast_log(AST_LOG_ERROR, "Failed to initialize manager endpoints handling\n");
9610 return -1;
9611 }
9612
9613 subscribed = 1;
9614 return 0;
9615}
9616
9617static void manager_set_defaults(void)
9618{
9619 manager_enabled = 0;
9620 displayconnects = 1;
9622 authtimeout = 30;
9623 authlimit = 50;
9624 manager_debug = 0; /* Debug disabled by default */
9625
9626 /* default values */
9628 sizeof(global_realm));
9631
9632 ami_tls_cfg.enabled = 0;
9643}
9644
9645static int __init_manager(int reload, int by_external_config)
9646{
9647 struct ast_config *cfg = NULL;
9648 const char *val;
9649 char *cat = NULL;
9650 int newhttptimeout = 60;
9651 struct ast_manager_user *user = NULL;
9652 struct ast_variable *var;
9653 struct ast_flags config_flags = { (reload && !by_external_config) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9654 char a1[337];
9655 char a1_hash[256];
9656 struct ast_sockaddr ami_desc_local_address_tmp;
9657 struct ast_sockaddr amis_desc_local_address_tmp;
9658 int tls_was_enabled = 0;
9659 int acl_subscription_flag = 0;
9660
9661 if (!reload) {
9662 struct ao2_container *sessions;
9663#ifdef AST_XML_DOCS
9664 struct ao2_container *temp_event_docs;
9665#endif
9666 int res;
9667
9669 if (res != 0) {
9670 return -1;
9671 }
9672 manager_topic = stasis_topic_create("manager:core");
9673 if (!manager_topic) {
9674 return -1;
9675 }
9676
9677 /* Register default actions */
9717
9718#ifdef TEST_FRAMEWORK
9719 test_suite_forwarder = stasis_forward_all(ast_test_suite_topic(), manager_topic);
9720#endif
9721
9725
9726 /* Append placeholder event so master_eventq never runs dry */
9727 if (append_event("Event: Placeholder\r\n\r\n",
9728 ast_str_hash("Placeholder"), 0)) {
9729 return -1;
9730 }
9731
9732#ifdef AST_XML_DOCS
9733 temp_event_docs = ast_xmldoc_build_documentation("managerEvent");
9734 if (temp_event_docs) {
9735 ao2_t_global_obj_replace_unref(event_docs, temp_event_docs, "Toss old event docs");
9736 ao2_t_ref(temp_event_docs, -1, "Remove creation ref - container holds only ref now");
9737 }
9738#endif
9739
9740 /* If you have a NULL hash fn, you only need a single bucket */
9742 if (!sessions) {
9743 return -1;
9744 }
9746 ao2_ref(sessions, -1);
9747
9748 /* Initialize all settings before first configuration load. */
9750 }
9751
9752 cfg = ast_config_load2("manager.conf", "manager", config_flags);
9753 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9754 return 0;
9755 } else if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
9756 ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid.\n");
9757 return 0;
9758 }
9759
9760 /* If this wasn't performed due to a forced reload (because those can be created by ACL change events, we need to unsubscribe to ACL change events. */
9761 if (!by_external_config) {
9763 }
9764
9765 if (reload) {
9766 /* Reset all settings before reloading configuration */
9767 tls_was_enabled = ami_tls_cfg.enabled;
9769 }
9770
9771 ast_sockaddr_parse(&ami_desc_local_address_tmp, "[::]", 0);
9772 ast_sockaddr_set_port(&ami_desc_local_address_tmp, DEFAULT_MANAGER_PORT);
9773
9774 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
9775 val = var->value;
9776
9777 /* read tls config options while preventing unsupported options from being set */
9778 if (strcasecmp(var->name, "tlscafile")
9779 && strcasecmp(var->name, "tlscapath")
9780 && strcasecmp(var->name, "tlscadir")
9781 && strcasecmp(var->name, "tlsverifyclient")
9782 && strcasecmp(var->name, "tlsdontverifyserver")
9783 && strcasecmp(var->name, "tlsclientmethod")
9784 && strcasecmp(var->name, "sslclientmethod")
9785 && !ast_tls_read_conf(&ami_tls_cfg, &amis_desc, var->name, val)) {
9786 continue;
9787 }
9788
9789 if (!strcasecmp(var->name, "enabled")) {
9791 } else if (!strcasecmp(var->name, "webenabled")) {
9793 } else if (!strcasecmp(var->name, "port")) {
9794 int bindport;
9795 if (ast_parse_arg(val, PARSE_UINT32|PARSE_IN_RANGE, &bindport, 1024, 65535)) {
9796 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
9797 }
9798 ast_sockaddr_set_port(&ami_desc_local_address_tmp, bindport);
9799 } else if (!strcasecmp(var->name, "bindaddr")) {
9800 /* remember port if it has already been set */
9801 int setport = ast_sockaddr_port(&ami_desc_local_address_tmp);
9802
9804 ast_log(LOG_WARNING, "Invalid address '%s' specified, default '%s' will be used\n", val,
9805 ast_sockaddr_stringify_addr(&ami_desc_local_address_tmp));
9806 } else {
9807 ast_sockaddr_parse(&ami_desc_local_address_tmp, val, PARSE_PORT_IGNORE);
9808 }
9809
9810 if (setport) {
9811 ast_sockaddr_set_port(&ami_desc_local_address_tmp, setport);
9812 }
9813
9814 } else if (!strcasecmp(var->name, "brokeneventsaction")) {
9816 } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
9818 } else if (!strcasecmp(var->name, "displayconnects")) {
9820 } else if (!strcasecmp(var->name, "timestampevents")) {
9822 } else if (!strcasecmp(var->name, "debug")) {
9824 } else if (!strcasecmp(var->name, "httptimeout")) {
9825 newhttptimeout = atoi(val);
9826 } else if (!strcasecmp(var->name, "authtimeout")) {
9827 int timeout = atoi(var->value);
9828
9829 if (timeout < 1) {
9830 ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", var->value);
9831 } else {
9832 authtimeout = timeout;
9833 }
9834 } else if (!strcasecmp(var->name, "authlimit")) {
9835 int limit = atoi(var->value);
9836
9837 if (limit < 1) {
9838 ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", var->value);
9839 } else {
9840 authlimit = limit;
9841 }
9842 } else if (!strcasecmp(var->name, "channelvars")) {
9844 } else if (!strcasecmp(var->name, "disabledevents")) {
9846 } else {
9847 ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
9848 var->name, val);
9849 }
9850 }
9851
9852 if (manager_enabled && !subscribed) {
9853 if (subscribe_all() != 0) {
9854 ast_log(LOG_ERROR, "Manager subscription error\n");
9855 return -1;
9856 }
9857 }
9858
9859 ast_sockaddr_copy(&amis_desc_local_address_tmp, &amis_desc.local_address);
9860
9861 /* if the amis address has not been set, default is the same as non secure ami */
9862 if (ast_sockaddr_isnull(&amis_desc_local_address_tmp)) {
9863 ast_sockaddr_copy(&amis_desc_local_address_tmp, &ami_desc_local_address_tmp);
9864 }
9865
9866 /* if the amis address was not set, it will have non-secure ami port set; if
9867 amis address was set, we need to check that a port was set or not, if not
9868 use the default tls port */
9869 if (ast_sockaddr_port(&amis_desc_local_address_tmp) == 0 ||
9870 (ast_sockaddr_port(&ami_desc_local_address_tmp) == ast_sockaddr_port(&amis_desc_local_address_tmp))) {
9871
9872 ast_sockaddr_set_port(&amis_desc_local_address_tmp, DEFAULT_MANAGER_TLS_PORT);
9873 }
9874
9875 if (manager_enabled) {
9876 ast_sockaddr_copy(&ami_desc.local_address, &ami_desc_local_address_tmp);
9877 ast_sockaddr_copy(&amis_desc.local_address, &amis_desc_local_address_tmp);
9878 }
9879
9881
9882 while ((cat = ast_category_browse(cfg, cat))) {
9883 struct ast_acl_list *oldacl;
9884
9885 if (!strcasecmp(cat, "general")) {
9886 continue;
9887 }
9888
9889 /* Look for an existing entry, if none found - create one and add it to the list */
9890 if (!(user = get_manager_by_name_locked(cat))) {
9891 if (!(user = ast_calloc(1, sizeof(*user)))) {
9892 break;
9893 }
9894 /* Copy name over */
9895 ast_copy_string(user->username, cat, sizeof(user->username));
9896
9897 user->acl = NULL;
9898 user->readperm = 0;
9899 user->writeperm = 0;
9900 /* Default displayconnect from [general] */
9901 user->displayconnects = displayconnects;
9902 /* Default allowmultiplelogin from [general] */
9903 user->allowmultiplelogin = allowmultiplelogin;
9904 user->writetimeout = 100;
9907 if (!user->includefilters || !user->excludefilters) {
9909 break;
9910 }
9911
9912 /* Insert into list */
9914 } else {
9915 ao2_t_callback(user->includefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all include filters");
9916 ao2_t_callback(user->excludefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all exclude filters");
9917 }
9918
9919 /* Make sure we keep this user and don't destroy it during cleanup */
9920 user->keep = 1;
9921 oldacl = user->acl;
9922 user->acl = NULL;
9923 ast_variables_destroy(user->chanvars);
9924
9925 var = ast_variable_browse(cfg, cat);
9926 for (; var; var = var->next) {
9927 if (!strcasecmp(var->name, "secret")) {
9928 ast_free(user->secret);
9929 user->secret = ast_strdup(var->value);
9930 } else if (!strcasecmp(var->name, "deny") ||
9931 !strcasecmp(var->name, "permit") ||
9932 !strcasecmp(var->name, "acl")) {
9933 int acl_error = 0;
9934
9935 ast_append_acl(var->name, var->value, &user->acl, &acl_error, &acl_subscription_flag);
9936 if (acl_error) {
9937 ast_log(LOG_ERROR, "Invalid ACL '%s' for manager user '%s' on line %d. Deleting user\n",
9938 var->value, user->username, var->lineno);
9939 user->keep = 0;
9940 }
9941 } else if (!strcasecmp(var->name, "read") ) {
9942 user->readperm = get_perm(var->value);
9943 } else if (!strcasecmp(var->name, "write") ) {
9944 user->writeperm = get_perm(var->value);
9945 } else if (!strcasecmp(var->name, "displayconnects") ) {
9946 user->displayconnects = ast_true(var->value);
9947 } else if (!strcasecmp(var->name, "allowmultiplelogin") ) {
9948 user->allowmultiplelogin = ast_true(var->value);
9949 } else if (!strcasecmp(var->name, "writetimeout")) {
9950 int value = atoi(var->value);
9951 if (value < 100) {
9952 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
9953 } else {
9954 user->writetimeout = value;
9955 }
9956 } else if (!strcasecmp(var->name, "setvar")) {
9957 struct ast_variable *tmpvar;
9958 char varbuf[256];
9959 char *varval;
9960 char *varname;
9961
9962 ast_copy_string(varbuf, var->value, sizeof(varbuf));
9963 varname = varbuf;
9964
9965 if ((varval = strchr(varname,'='))) {
9966 *varval++ = '\0';
9967 if ((tmpvar = ast_variable_new(varname, varval, ""))) {
9968 tmpvar->next = user->chanvars;
9969 user->chanvars = tmpvar;
9970 }
9971 }
9972 } else if (ast_begins_with(var->name, "eventfilter")) {
9973 const char *value = var->value;
9974 manager_add_filter(var->name, value, user->includefilters, user->excludefilters);
9975 } else {
9976 ast_debug(1, "%s is an unknown option.\n", var->name);
9977 }
9978 }
9979
9980 oldacl = ast_free_acl_list(oldacl);
9981 }
9982 ast_config_destroy(cfg);
9983
9984 /* Check the flag for named ACL event subscription and if we need to, register a subscription. */
9985 if (acl_subscription_flag && !by_external_config) {
9987 }
9988
9989 /* Perform cleanup - essentially prune out old users that no longer exist */
9991 if (user->keep) { /* valid record. clear flag for the next round */
9992 user->keep = 0;
9993
9994 /* Calculate A1 for Digest auth */
9995 snprintf(a1, sizeof(a1), "%s:%s:%s", user->username, global_realm, user->secret);
9996 ast_md5_hash(a1_hash,a1);
9997 ast_free(user->a1_hash);
9998 user->a1_hash = ast_strdup(a1_hash);
9999 continue;
10000 }
10001 /* We do not need to keep this user so take them out of the list */
10003 ast_debug(4, "Pruning user '%s'\n", user->username);
10005 }
10007
10009
10011 if (!webregged) {
10015
10019 webregged = 1;
10020 }
10021 } else {
10022 if (webregged) {
10026
10030 webregged = 0;
10031 }
10032 }
10033
10034 if (newhttptimeout > 0) {
10035 httptimeout = newhttptimeout;
10036 }
10037
10039 if (tls_was_enabled && !ami_tls_cfg.enabled) {
10041 } else if (ast_ssl_setup(amis_desc.tls_cfg)) {
10043 }
10044
10045 return 0;
10046}
10047
10048static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub,
10049 struct stasis_message *message)
10050{
10052 return;
10053 }
10054
10055 /* For now, this is going to be performed simply and just execute a forced reload. */
10056 ast_log(LOG_NOTICE, "Reloading manager in response to ACL change event.\n");
10057 __init_manager(1, 1);
10058}
10059
10060static int unload_module(void)
10061{
10062 return 0;
10063}
10064
10065static int load_module(void)
10066{
10067 int rc = 0;
10070#ifdef TEST_FRAMEWORK
10071 AST_TEST_REGISTER(eventfilter_test_creation);
10072 AST_TEST_REGISTER(eventfilter_test_matching);
10073 AST_TEST_REGISTER(originate_permissions_test);
10074#endif
10075 return rc;
10076}
10077
10078static int reload_module(void)
10079{
10081}
10082
10083int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
10084{
10085 AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
10086
10087 return 0;
10088}
10089
10090int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
10091{
10092 return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
10093}
10094
10095struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
10096{
10097 struct ast_datastore *datastore = NULL;
10098
10099 if (info == NULL)
10100 return NULL;
10101
10103 if (datastore->info != info) {
10104 continue;
10105 }
10106
10107 if (uid == NULL) {
10108 /* matched by type only */
10109 break;
10110 }
10111
10112 if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
10113 /* Matched by type AND uid */
10114 break;
10115 }
10116 }
10118
10119 return datastore;
10120}
10121
10122int ast_str_append_event_header(struct ast_str **fields_string,
10123 const char *header, const char *value)
10124{
10125 if (!*fields_string) {
10126 *fields_string = ast_str_create(128);
10127 if (!*fields_string) {
10128 return -1;
10129 }
10130 }
10131
10132 return (ast_str_append(fields_string, 0, "%s: %s\r\n", header, value) < 0) ? -1 : 0;
10133}
10134
10135static void manager_event_blob_dtor(void *obj)
10136{
10137 struct ast_manager_event_blob *ev = obj;
10138
10140}
10141
10143__attribute__((format(printf, 3, 4)))
10145 int event_flags,
10146 const char *manager_event,
10147 const char *extra_fields_fmt,
10148 ...)
10149{
10150 struct ast_manager_event_blob *ev;
10151 va_list argp;
10152
10153 ast_assert(extra_fields_fmt != NULL);
10155
10157 if (!ev) {
10158 return NULL;
10159 }
10160
10161 if (ast_string_field_init(ev, 20)) {
10162 ao2_ref(ev, -1);
10163 return NULL;
10164 }
10165
10168
10169 va_start(argp, extra_fields_fmt);
10170 ast_string_field_ptr_build_va(ev, &ev->extra_fields, extra_fields_fmt, argp);
10171 va_end(argp);
10172
10173 return ev;
10174}
10175
10177 .support_level = AST_MODULE_SUPPORT_CORE,
10178 .load = load_module,
10179 .unload = unload_module,
10181 .load_pri = AST_MODPRI_CORE,
10182 .requires = "extconfig,acl,http",
Access Control of various sorts.
enum ast_acl_sense ast_apply_acl(struct ast_acl_list *acl_list, const struct ast_sockaddr *addr, const char *purpose)
Apply a set of rules to a given IP address.
Definition: acl.c:799
struct stasis_message_type * ast_named_acl_change_type(void)
a stasis_message_type for changes against a named ACL or the set of all named ACLs
void ast_acl_output(int fd, struct ast_acl_list *acl, const char *prefix)
output an ACL to the provided fd
Definition: acl.c:1115
void ast_append_acl(const char *sense, const char *stuff, struct ast_acl_list **path, int *error, int *named_acl_flag)
Add a rule to an ACL struct.
Definition: acl.c:429
@ AST_SENSE_DENY
Definition: acl.h:37
int ast_acl_list_is_empty(struct ast_acl_list *acl_list)
Determines if an ACL is empty or if it contains entries.
Definition: acl.c:540
struct ast_acl_list * ast_free_acl_list(struct ast_acl_list *acl)
Free a list of ACLs.
Definition: acl.c:233
integer order
Definition: analys.c:66
Generic Advice of Charge encode and decode routines.
void * ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
free an ast_aoc_encoded object
Definition: aoc.c:322
int ast_aoc_s_add_rate_duration(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, unsigned int amount, enum ast_aoc_currency_multiplier multiplier, const char *currency_name, unsigned long time, enum ast_aoc_time_scale time_scale, unsigned long granularity_time, enum ast_aoc_time_scale granularity_time_scale, int step_function)
Add AOC-S duration rate entry.
Definition: aoc.c:779
ast_aoc_s_charged_item
Definition: aoc.h:145
@ AST_AOC_CHARGED_ITEM_BASIC_COMMUNICATION
Definition: aoc.h:148
@ AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT
Definition: aoc.h:147
@ AST_AOC_CHARGED_ITEM_NA
Definition: aoc.h:146
@ AST_AOC_CHARGED_ITEM_USER_USER_INFO
Definition: aoc.h:151
@ AST_AOC_CHARGED_ITEM_CALL_SETUP
Definition: aoc.h:150
@ AST_AOC_CHARGED_ITEM_SUPPLEMENTARY_SERVICE
Definition: aoc.h:152
@ AST_AOC_CHARGED_ITEM_CALL_ATTEMPT
Definition: aoc.h:149
ast_aoc_charge_type
Definition: aoc.h:69
@ AST_AOC_CHARGE_CURRENCY
Definition: aoc.h:72
@ AST_AOC_CHARGE_FREE
Definition: aoc.h:71
@ AST_AOC_CHARGE_UNIT
Definition: aoc.h:73
@ AST_AOC_CHARGE_NA
Definition: aoc.h:70
ast_aoc_time_scale
Definition: aoc.h:87
@ AST_AOC_TIME_SCALE_TEN_SECOND
Definition: aoc.h:91
@ AST_AOC_TIME_SCALE_DAY
Definition: aoc.h:94
@ AST_AOC_TIME_SCALE_TENTH_SECOND
Definition: aoc.h:89
@ AST_AOC_TIME_SCALE_MINUTE
Definition: aoc.h:92
@ AST_AOC_TIME_SCALE_SECOND
Definition: aoc.h:90
@ AST_AOC_TIME_SCALE_HOUR
Definition: aoc.h:93
@ AST_AOC_TIME_SCALE_HUNDREDTH_SECOND
Definition: aoc.h:88
ast_aoc_volume_unit
Definition: aoc.h:125
@ AST_AOC_VOLUME_UNIT_OCTET
Definition: aoc.h:126
@ AST_AOC_VOLUME_UNIT_SEGMENT
Definition: aoc.h:127
@ AST_AOC_VOLUME_UNIT_MESSAGE
Definition: aoc.h:128
int ast_aoc_s_add_rate_special_charge_code(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, unsigned int code)
Add AOC-S special rate entry.
Definition: aoc.c:853
ast_aoc_currency_multiplier
Defines the currency multiplier for an aoc message.
Definition: aoc.h:34
@ AST_AOC_MULT_TEN
Definition: aoc.h:39
@ AST_AOC_MULT_ONEHUNDREDTH
Definition: aoc.h:36
@ AST_AOC_MULT_HUNDRED
Definition: aoc.h:40
@ AST_AOC_MULT_ONETENTH
Definition: aoc.h:37
@ AST_AOC_MULT_ONETHOUSANDTH
Definition: aoc.h:35
@ AST_AOC_MULT_THOUSAND
Definition: aoc.h:41
@ AST_AOC_MULT_ONE
Definition: aoc.h:38
struct ast_aoc_decoded * ast_aoc_create(const enum ast_aoc_type msg_type, const enum ast_aoc_charge_type charge_type, const enum ast_aoc_request requests)
creates a ast_aoc_decode object of a specific message type
Definition: aoc.c:285
void * ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
free an ast_aoc_decoded object
Definition: aoc.c:316
int ast_aoc_set_association_number(struct ast_aoc_decoded *decoded, const char *num, uint8_t plan)
set the charging association number for an AOC-E message
Definition: aoc.c:1065
int ast_aoc_add_unit_entry(struct ast_aoc_decoded *decoded, const unsigned int amount_is_present, const unsigned int amount, const unsigned int type_is_present, const unsigned int type)
Adds a unit entry into the list of units.
Definition: aoc.c:986
ast_aoc_billing_id
Defines the billing id options for an aoc message.
Definition: aoc.h:49
@ AST_AOC_BILLING_CALL_FWD_BUSY
Definition: aoc.h:55
@ AST_AOC_BILLING_CALL_FWD_NO_REPLY
Definition: aoc.h:56
@ AST_AOC_BILLING_NORMAL
Definition: aoc.h:51
@ AST_AOC_BILLING_CALL_DEFLECTION
Definition: aoc.h:57
@ AST_AOC_BILLING_CREDIT_CARD
Definition: aoc.h:53
@ AST_AOC_BILLING_CALL_TRANSFER
Definition: aoc.h:58
@ AST_AOC_BILLING_CALL_FWD_UNCONDITIONAL
Definition: aoc.h:54
@ AST_AOC_BILLING_REVERSE_CHARGE
Definition: aoc.h:52
@ AST_AOC_BILLING_NA
Definition: aoc.h:50
int ast_aoc_s_add_rate_free(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, int from_beginning)
Add AOC-S indicating charge item is free.
Definition: aoc.c:866
int ast_aoc_set_billing_id(struct ast_aoc_decoded *decoded, const enum ast_aoc_billing_id id)
set the billing id for a AOC-D or AST_AOC_E message
Definition: aoc.c:1033
int ast_aoc_s_add_special_arrangement(struct ast_aoc_decoded *decoded, unsigned int code)
Add AOC-S special arrangement entry.
Definition: aoc.c:889
int ast_aoc_set_currency_info(struct ast_aoc_decoded *decoded, const unsigned int amount, const enum ast_aoc_currency_multiplier multiplier, const char *name)
Sets the currency values for a AOC-D or AOC-E message.
Definition: aoc.c:928
int ast_aoc_set_association_id(struct ast_aoc_decoded *decoded, const int id)
set the charging association id for an AST_AOC_E message
Definition: aoc.c:1049
int ast_aoc_s_add_rate_flat(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, unsigned int amount, enum ast_aoc_currency_multiplier multiplier, const char *currency_name)
Add AOC-S flat rate entry.
Definition: aoc.c:810
struct ast_aoc_encoded * ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
encodes a decoded aoc structure so it can be passed on the wire
Definition: aoc.c:659
int ast_aoc_set_total_type(struct ast_aoc_decoded *decoded, const enum ast_aoc_total_type type)
Sets the type of total for a AOC-D message.
Definition: aoc.c:916
int ast_aoc_s_add_rate_volume(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item, enum ast_aoc_volume_unit volume_unit, unsigned int amount, enum ast_aoc_currency_multiplier multiplier, const char *currency_name)
Add AOC-S volume rate entry.
Definition: aoc.c:831
ast_aoc_type
Definition: aoc.h:62
@ AST_AOC_S
Definition: aoc.h:64
@ AST_AOC_D
Definition: aoc.h:65
@ AST_AOC_E
Definition: aoc.h:66
unsigned int ast_aoc_s_get_count(struct ast_aoc_decoded *decoded)
get the number rates associated with an AOC-S message
Definition: aoc.c:765
int ast_aoc_s_add_rate_na(struct ast_aoc_decoded *decoded, enum ast_aoc_s_charged_item charged_item)
Add AOC-S entry indicating charge item is not available.
Definition: aoc.c:878
ast_aoc_total_type
Definition: aoc.h:82
@ AST_AOC_TOTAL
Definition: aoc.h:83
@ AST_AOC_SUBTOTAL
Definition: aoc.h:84
ast_aoc_s_rate_type
Definition: aoc.h:155
@ AST_AOC_RATE_TYPE_VOLUME
Definition: aoc.h:161
@ AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING
Definition: aoc.h:158
@ AST_AOC_RATE_TYPE_SPECIAL_CODE
Definition: aoc.h:162
@ AST_AOC_RATE_TYPE_NA
Definition: aoc.h:156
@ AST_AOC_RATE_TYPE_DURATION
Definition: aoc.h:159
@ AST_AOC_RATE_TYPE_FLAT
Definition: aoc.h:160
@ AST_AOC_RATE_TYPE_FREE
Definition: aoc.h:157
static const char app[]
Definition: app_adsiprog.c:56
static const struct adsi_event events[]
Definition: app_adsiprog.c:88
char digit
jack_status_t status
Definition: app_jack.c:149
const char * str
Definition: app_jack.c:150
static volatile unsigned int seq
Definition: app_sms.c:126
static int copy(char *infile, char *outfile)
Utility function to copy a file.
#define var
Definition: ast_expr2f.c:605
Asterisk version information.
const char * ast_get_version(void)
Retrieve the Asterisk version string.
char * strsep(char **str, const char *delims)
char * strcasestr(const char *, const char *)
Asterisk main include file. File version handling, generic pbx functions.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
int ast_shutting_down(void)
Definition: asterisk.c:1888
#define AST_FILE_MODE
Definition: asterisk.h:32
static struct ast_mansession session
void ast_std_free(void *ptr)
Definition: astmm.c:1734
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#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_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_t_ref(o, delta, tag)
Definition: astobj2.h:460
#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
@ CMP_STOP
Definition: astobj2.h:1028
#define OBJ_KEY
Definition: astobj2.h:1151
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition: astobj2.h:367
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
Definition: astobj2.h:901
#define ao2_t_global_obj_replace_unref(holder, obj, tag)
Definition: astobj2.h:904
#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
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_callback_data(container, flags, cb_fn, arg, data)
Definition: astobj2.h:1723
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
#define ao2_t_link(container, obj, tag)
Definition: astobj2.h:1534
#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_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#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_t_callback_data(container, flags, cb_fn, arg, data, tag)
ao2_callback_data() is a generic function that applies cb_fn() to all objects in a container....
Definition: astobj2.h:1721
#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.
#define ao2_t_alloc(data_size, destructor_fn, debug_msg)
Definition: astobj2.h:407
@ OBJ_SEARCH_OBJECT
The arg parameter is an object of the same type.
Definition: astobj2.h:1087
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition: astobj2.h:1063
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
#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_t_global_obj_release(holder, tag)
Definition: astobj2.h:861
#define ao2_t_callback(c, flags, cb_fn, arg, tag)
Definition: astobj2.h:1696
#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_OBJ_REJECT
Reject duplicate objects in container.
Definition: astobj2.h:1201
Bridging API.
enum ast_transfer_result ast_bridge_transfer_blind(int is_external, struct ast_channel *transferer, const char *exten, const char *context, transfer_channel_cb new_channel_cb, void *user_data)
Blind transfer target to the extension and context provided.
Definition: bridge.c:4504
@ AST_BRIDGE_TRANSFER_NOT_PERMITTED
Definition: bridge.h:1106
@ AST_BRIDGE_TRANSFER_SUCCESS
Definition: bridge.h:1104
@ AST_BRIDGE_TRANSFER_INVALID
Definition: bridge.h:1108
@ AST_BRIDGE_TRANSFER_FAIL
Definition: bridge.h:1110
After Bridge Execution API.
void ast_bridge_discard_after_goto(struct ast_channel *chan)
Discard channel after bridge goto location.
Definition: bridge_after.c:384
static struct prometheus_metrics_provider provider
Definition: bridges.c:201
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
int ast_callerid_parse(char *instr, char **name, char **location)
Destructively parse inbuf into name and location (or number)
Definition: callerid.c:1162
void ast_shrink_phone_number(char *n)
Shrink a phone number in place to just digits (more accurately it just removes ()'s,...
Definition: callerid.c:1101
enum cc_state state
Definition: ccss.c:399
int ast_cdr_is_enabled(void)
Return TRUE if CDR subsystem is enabled.
Definition: cdr.c:2994
static int priority
static PGresult * result
Definition: cel_pgsql.c:84
static int match(struct ast_sockaddr *addr, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition: chan_iax2.c:2388
static const char type[]
Definition: chan_ooh323.c:109
static struct unistimsession * sessions
static int chancount
Definition: channel.c:96
General Asterisk PBX channel definitions.
struct varshead * ast_channel_get_manager_vars(struct ast_channel *chan)
Gets the variables for a given channel, as specified by ast_channel_set_manager_vars().
Definition: channel.c:7938
const char * ast_channel_name(const struct ast_channel *chan)
#define AST_MAX_PUBLIC_UNIQUEID
Definition: channel.h:147
const char * ast_channel_data(const struct ast_channel *chan)
void ast_channel_clear_flag(struct ast_channel *chan, unsigned int flag)
Clear a flag on a channel.
Definition: channel.c:11048
struct varshead * ast_channel_varshead(struct ast_channel *chan)
struct ast_channel_iterator * ast_channel_iterator_destroy(struct ast_channel_iterator *i)
Destroy a channel iterator.
Definition: channel.c:1330
void ast_channel_set_manager_vars(size_t varc, char **vars)
Sets the variables to be stored in the manager_vars field of all snapshots.
Definition: channel.c:7842
#define ast_channel_lock(chan)
Definition: channel.h:2972
struct ast_trans_pvt * ast_channel_readtrans(const struct ast_channel *chan)
struct ast_format_cap * ast_channel_nativeformats(const struct ast_channel *chan)
struct ast_trans_pvt * ast_channel_writetrans(const struct ast_channel *chan)
ast_group_t ast_channel_pickupgroup(const struct ast_channel *chan)
struct ast_channel * ast_channel_iterator_next(struct ast_channel_iterator *i)
Get the next channel for a channel iterator.
Definition: channel.c:1369
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
@ AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT
Definition: channel.h:1055
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f)
Queue one or more frames to a channel's frame queue.
Definition: channel.c:1170
const char * ast_channel_uniqueid(const struct ast_channel *chan)
const char * ast_channel_context(const struct ast_channel *chan)
int ast_check_hangup_locked(struct ast_channel *chan)
Definition: channel.c:459
struct ast_channel * ast_channel_get_by_name_prefix(const char *name, size_t name_len)
Find a channel by a name prefix.
Definition: channel.c:1381
void ast_channel_softhangup_withcause_locked(struct ast_channel *chan, int causecode)
Lock the given channel, then request softhangup on the channel with the given causecode.
Definition: channel.c:468
int ast_queue_control_data(struct ast_channel *chan, enum ast_control_frame_type control, const void *data, size_t datalen)
Queue a control frame with payload.
Definition: channel.c:1277
const char * ast_channel_appl(const struct ast_channel *chan)
struct timeval ast_channel_creationtime(struct ast_channel *chan)
struct ast_channel * ast_channel_get_by_name(const char *search)
Find a channel by name or uniqueid.
Definition: channel.c:1398
int ast_active_channels(void)
returns number of active/allocated channels
Definition: channel.c:499
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
Definition: channel.c:10559
struct ast_bridge * ast_channel_get_bridge(const struct ast_channel *chan)
Get the bridge associated with a channel.
Definition: channel.c:10548
struct ast_party_dialed * ast_channel_dialed(struct ast_channel *chan)
int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
Indicates condition of channel, with payload.
Definition: channel.c:4621
#define AST_CHANNEL_NAME
Definition: channel.h:173
struct timeval * ast_channel_whentohangup(struct ast_channel *chan)
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:3008
struct ast_format * ast_channel_writeformat(struct ast_channel *chan)
ast_group_t ast_channel_callgroup(const struct ast_channel *chan)
struct ast_party_id ast_channel_connected_effective_id(struct ast_channel *chan)
void ast_channel_setwhentohangup_tv(struct ast_channel *chan, struct timeval offset)
Set when to hang a channel up.
Definition: channel.c:510
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1328
const struct ast_channel_tech * ast_channel_tech(const struct ast_channel *chan)
struct ast_channel_iterator * ast_channel_iterator_all_new(void)
Create a new channel iterator.
Definition: channel.c:1361
#define ast_channel_unlock(chan)
Definition: channel.h:2973
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
const char * ast_var_name(const struct ast_var_t *var)
Definition: chanvars.c:60
const char * ast_var_value(const struct ast_var_t *var)
Definition: chanvars.c:80
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define AST_CLI_YESNO(x)
Return Yes or No depending on the argument.
Definition: cli.h:71
#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:2737
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define RESULT_SUCCESS
Definition: cli.h:40
ast_cli_command
calling arguments for new-style handlers.
Definition: cli.h:151
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define AST_MAX_CMD_LEN
Definition: cli.h:48
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
static struct ao2_container * codecs
Registered codecs.
Definition: codec.c:48
@ AST_MEDIA_TYPE_UNKNOWN
Definition: codec.h:31
static struct channel_usage channels
Local proxy channel special access.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void ast_verbose(const char *fmt,...)
Definition: extconf.c:2204
#define max(a, b)
Definition: f2c.h:198
Call Parking and Pickup API Includes code and algorithms from the Zapata library.
Generic File Format Support. Should be included by clients of the file handling routines....
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
Media Format Cache API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
#define AST_FORMAT_CAP_NAMES_LEN
Definition: format_cap.h:324
int ast_format_cap_update_by_allow_disallow(struct ast_format_cap *cap, const char *list, int allowing)
Parse an "allow" or "deny" list and modify a format capabilities structure accordingly.
Definition: format_cap.c:320
void ast_format_cap_remove_by_type(struct ast_format_cap *cap, enum ast_media_type type)
Remove all formats matching a specific format type.
Definition: format_cap.c:523
@ AST_FORMAT_CAP_FLAG_DEFAULT
Definition: format_cap.h:38
const char * ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf)
Get the names of codecs of a set of formats.
Definition: format_cap.c:734
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
static const char name[]
Definition: format_mp3.c:68
static char * synopsis
Definition: func_enum.c:166
static int md5(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_md5.c:55
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
Definition: func_strings.c:899
static int action_setvar(struct mansession *s, const struct message *m)
Definition: manager.c:3548
static int action_createconfig(struct mansession *s, const struct message *m)
Definition: manager.c:3093
static void purge_events(void)
Definition: manager.c:714
static char * handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: manager.c:1077
static char * match_type_names[]
Definition: manager.c:394
static int manager_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
Definition: manager.c:7728
static void mansession_unlock(struct mansession *s)
Unlock the 'mansession' structure.
Definition: manager.c:2080
static int action_loggerrotate(struct mansession *s, const struct message *m)
Manager command "LoggerRotate" - reloads and rotates the logger in the same manner as the CLI command...
Definition: manager.c:6829
int __ast_manager_event_multichan(int category, const char *event, int chancount, struct ast_channel **chans, const char *file, int line, const char *func, const char *fmt,...)
Definition: manager.c:7673
static int match_eventdata(struct event_filter_entry *entry, const char *eventdata)
Test eventdata against a filter entry.
Definition: manager.c:5575
#define GET_HEADER_LAST_MATCH
Definition: manager.c:1590
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:2024
static int purge_sessions(int n_max)
remove at most n_max stale session from the list.
Definition: manager.c:7454
static int process_message(struct mansession *s, const struct message *m)
Process an AMI message, performing desired action. Return 0 on success, -1 on error that require the ...
Definition: manager.c:7026
static int strings_to_mask(const char *string)
Definition: manager.c:883
static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance)
Definition: manager.c:9264
static int __manager_event_sessions(struct ao2_container *sessions, int category, const char *event, int chancount, struct ast_channel **chans, const char *file, int line, const char *func, const char *fmt,...)
Definition: manager.c:7651
static struct ast_manager_user * get_manager_by_name_locked(const char *name)
Definition: manager.c:1042
static void acl_change_stasis_subscribe(void)
Definition: manager.c:231
static char * handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command manager list eventq.
Definition: manager.c:1529
static void log_action(const struct message *m, const char *action)
Definition: manager.c:6987
static struct mansession_session * build_mansession(const struct ast_sockaddr *addr)
Allocate manager session structure and add it to the list of sessions.
Definition: manager.c:966
static void report_invalid_user(const struct mansession *s, const char *username)
Definition: manager.c:2108
static int append_event(const char *str, int event_name_hash, int category)
events are appended to a queue from where they can be dispatched to clients.
Definition: manager.c:7496
static int manager_enabled
Definition: manager.c:166
int ast_manager_check_enabled(void)
Check if AMI is enabled.
Definition: manager.c:680
static void astman_free_headers(struct message *m)
Free headers inside message structure, but not the message structure itself.
Definition: manager.c:1667
static int action_sendtext(struct mansession *s, const struct message *m)
Definition: manager.c:3904
static int action_mailboxcount(struct mansession *s, const struct message *m)
Definition: manager.c:5439
static void manager_json_to_ast_str(struct ast_json *obj, const char *key, struct ast_str **res, key_exclusion_cb exclusion_cb)
Definition: manager.c:510
static int ast_manager_register_struct(struct manager_action *act)
Definition: manager.c:7769
static int queue_read_action_payload(struct ast_channel *chan, const unsigned char *payload, size_t payload_size, enum ast_frame_read_action action)
Queue a given read action containing a payload onto a channel.
Definition: manager.c:3836
static AO2_GLOBAL_OBJ_STATIC(mgr_sessions)
static struct manager_action * action_find(const char *name)
Definition: manager.c:434
static int action_getconfigjson(struct mansession *s, const struct message *m)
Definition: manager.c:2645
int ast_webmanager_check_enabled(void)
Check if AMI/HTTP is enabled.
Definition: manager.c:685
void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
Send response in manager transaction.
Definition: manager.c:1977
static void report_failed_acl(const struct mansession *s, const char *username)
Definition: manager.c:2133
static int is_originate_app_permitted(const char *app, const char *data, int permission)
Definition: manager.c:5121
static int action_listcategories(struct mansession *s, const struct message *m)
Definition: manager.c:2569
static int action_hangup(struct mansession *s, const struct message *m)
Definition: manager.c:3542
static void mansession_lock(struct mansession *s)
Lock the 'mansession' structure.
Definition: manager.c:2074
static int action_listcommands(struct mansession *s, const struct message *m)
Definition: manager.c:3285
static void destroy_fast_originate_helper(struct fast_originate_helper *doomed)
Definition: manager.c:4383
static int action_atxfer(struct mansession *s, const struct message *m)
Definition: manager.c:4144
static struct ast_threadstorage manager_event_buf
Definition: manager.c:7538
static int action_timeout(struct mansession *s, const struct message *m)
Definition: manager.c:5533
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:1982
static struct stasis_subscription * acl_change_sub
Definition: manager.c:179
static void generate_status(struct mansession *s, struct ast_channel *chan, char **vars, int varc, int all_variables, char *id_text, int *count)
Definition: manager.c:3633
static int do_message(struct mansession *s)
Definition: manager.c:7275
static struct originate_permissions_entry originate_app_permissions[]
Definition: manager.c:5085
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:1987
static void manager_default_msg_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Definition: manager.c:568
static int action_coresettings(struct mansession *s, const struct message *m)
Show PBX core settings information.
Definition: manager.c:6462
static int mansession_cmp_fn(void *obj, void *arg, int flags)
Definition: manager.c:999
static int displayconnects
Definition: manager.c:161
static int action_filter(struct mansession *s, const struct message *m)
Manager command to add an event filter to a manager session.
Definition: manager.c:5685
static int manager_debug
Definition: manager.c:169
static int action_mailboxstatus(struct mansession *s, const struct message *m)
Definition: manager.c:5422
static void append_channel_vars(struct ast_str **pbuf, struct ast_channel *chan)
Definition: manager.c:7521
static int action_login(struct mansession *s, const struct message *m)
Definition: manager.c:3354
static int action_getvar(struct mansession *s, const struct message *m)
Definition: manager.c:3581
static int get_input(struct mansession *s, char *output)
Definition: manager.c:7154
static int action_blind_transfer(struct mansession *s, const struct message *m)
Definition: manager.c:4098
static const char * user_authority_to_str(int authority, struct ast_str **res)
Convert authority code to a list of options for a user. This will only display those authority codes ...
Definition: manager.c:791
static int app_match(const char *app, const char *data, const char *search)
Definition: manager.c:5009
static struct mansession_session * unref_mansession(struct mansession_session *s)
Unreference manager session object. If no more references, then go ahead and delete it.
Definition: manager.c:914
static int queue_match(const char *app, const char *data, const char *search)
Definition: manager.c:5048
#define ASTMAN_APPEND_BUF_INITSIZE
initial allocated size for the astman_append_buf and astman_send_*_va
Definition: manager.c:1889
void ast_manager_publish_event(const char *type, int class_type, struct ast_json *obj)
Publish an event to AMI.
Definition: manager.c:634
static void session_destroy(struct mansession_session *s)
Definition: manager.c:1006
static int filter_cmp_fn(void *obj, void *arg, void *data, int flags)
Definition: manager.c:5595
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition: manager.c:2060
static int appdata_match(const char *app, const char *data, const char *search)
Definition: manager.c:5028
#define MAX_VARS
Definition: manager.c:205
struct stasis_message_router * ast_manager_get_message_router(void)
Get the stasis_message_router for AMI.
Definition: manager.c:455
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition: manager.c:450
static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
send a response with an optional message, and terminate it with an empty line. m is used only to grab...
Definition: manager.c:1946
static void astman_append_json(struct mansession *s, const char *str)
Definition: manager.c:2636
#define MGR_SHOW_TERMINAL_WIDTH
Definition: manager.c:203
static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
helper function for action_updateconfig
Definition: manager.c:2719
static int action_extensionstate(struct mansession *s, const struct message *m)
Definition: manager.c:5460
static char * handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command manager list connected.
Definition: manager.c:1478
static int action_getconfig(struct mansession *s, const struct message *m)
Definition: manager.c:2497
static void event_filter_destructor(void *obj)
Definition: manager.c:923
static void * session_do(void *data)
The body of the individual manager session. Call get_input() to read one line at a time (or be woken ...
Definition: manager.c:7360
static struct eventqent * advance_event(struct eventqent *e)
Definition: manager.c:1576
static struct ast_threadstorage astman_append_buf
Definition: manager.c:1884
error_type
Definition: manager.c:108
static int coreshowchannelmap_add_connected_channels(struct ao2_container *channel_map, struct ast_channel_snapshot *channel_snapshot, struct ast_bridge_snapshot *bridge_snapshot)
Recursive function to get all channels in a bridge. Follow local channels as well.
Definition: manager.c:6664
static enum add_filter_result manager_add_filter(const char *criteria, const char *filter_pattern, struct ao2_container *includefilters, struct ao2_container *excludefilters)
Add an event filter to a manager session.
Definition: manager.c:5752
static void session_destructor(void *obj)
Definition: manager.c:935
static struct stasis_message_router * stasis_router
The stasis_message_router for all Stasis Message Bus API messages.
Definition: manager.c:185
static void json_escape(char *out, const char *in)
Definition: manager.c:2618
static int manager_displayconnects(struct mansession_session *session)
Get displayconnects config option.
Definition: manager.c:1059
static int action_logoff(struct mansession *s, const struct message *m)
Definition: manager.c:3348
static char * handle_kickmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command manager kick session.
Definition: manager.c:1408
static int action_updateconfig(struct mansession *s, const struct message *m)
Definition: manager.c:3002
static void astman_start_ack(struct mansession *s, const struct message *m)
Definition: manager.c:2019
static const char * __astman_get_header(const struct message *m, char *var, int mode)
Return a matching header value.
Definition: manager.c:1606
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2014
static int file_in_modules_dir(const char *filename)
Check if the given file path is in the modules dir or not.
Definition: manager.c:6880
STASIS_MESSAGE_TYPE_DEFN(ast_manager_get_generic_type)
Define AMI message types.
static void handle_parse_error(struct mansession *s, struct message *m, char *error)
Definition: manager.c:7258
static int manager_moduleload(struct mansession *s, const struct message *m)
Definition: manager.c:6906
static void acl_change_stasis_unsubscribe(void)
Definition: manager.c:241
static int reload_module(void)
Definition: manager.c:10078
static void manager_json_array_with_key(struct ast_json *obj, const char *key, size_t index, struct ast_str **res, key_exclusion_cb exclusion_cb)
Definition: manager.c:485
static void astman_append_headers(struct message *m, const struct ast_variable *params)
Append additional headers into the message structure from params.
Definition: manager.c:1653
static void report_failed_challenge_response(const struct mansession *s, const char *response, const char *expected_response)
Definition: manager.c:2266
#define any_manager_listeners(sessions)
Definition: manager.c:565
static int action_reload(struct mansession *s, const struct message *m)
Send a reload event.
Definition: manager.c:6548
struct ast_variable * astman_get_variables(const struct message *m)
Get a linked list of the Variable: headers.
Definition: manager.c:1729
static int live_dangerously
Set to true (non-zero) to globally allow all dangerous AMI actions to run.
Definition: manager.c:196
static int is_restricted_file(const char *filename)
Check if a file is restricted or not.
Definition: manager.c:2464
struct ast_str * ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb)
Convert a JSON object into an AMI compatible string.
Definition: manager.c:551
static struct eventqent * grab_last(void)
Definition: manager.c:694
static const struct @377 command_blacklist[]
add_filter_result
Definition: manager.c:123
static int should_send_event(struct ao2_container *includefilters, struct ao2_container *excludefilters, struct eventqent *eqe)
Definition: manager.c:5648
static int authlimit
Definition: manager.c:171
static struct stasis_forward * rtp_topic_forwarder
The stasis_subscription for forwarding the RTP topic to the AMI topic.
Definition: manager.c:188
static int action_aocmessage(struct mansession *s, const struct message *m)
Definition: manager.c:4934
static char * handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: manager.c:1323
static int check_manager_session_inuse(const char *name)
Definition: manager.c:1019
static int check_blacklist(const char *cmd)
Definition: manager.c:4239
static int action_events(struct mansession *s, const struct message *m)
Definition: manager.c:3304
void ast_manager_unregister_hook(struct manager_custom_hook *hook)
Delete a custom hook to be called when an event is fired.
Definition: manager.c:673
static void astman_flush(struct mansession *s, struct ast_str *buf)
Definition: manager.c:1891
static const char * authority_to_str(int authority, struct ast_str **res)
Convert authority code to a list of options. Note that the EVENT_FLAG_ALL authority will always be re...
Definition: manager.c:815
static int action_redirect(struct mansession *s, const struct message *m)
action_redirect: The redirect manager command
Definition: manager.c:3953
#define MSG_MOREDATA
Definition: manager.c:1937
static struct stasis_forward * security_topic_forwarder
The stasis_subscription for forwarding the Security topic to the AMI topic.
Definition: manager.c:191
static int action_presencestate(struct mansession *s, const struct message *m)
Definition: manager.c:5490
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:1734
static char * manager_channelvars
Definition: manager.c:172
void ast_manager_register_hook(struct manager_custom_hook *hook)
Add a custom hook to be called when an event is fired.
Definition: manager.c:665
#define DEFAULT_REALM
Definition: manager.c:175
static int allowmultiplelogin
Definition: manager.c:162
static void manager_json_value_str_append(struct ast_json *value, const char *key, struct ast_str **res)
Definition: manager.c:460
static int queue_sendtext_data(struct ast_channel *chan, const char *body, const char *content_type)
Queue a read action to send a text data message.
Definition: manager.c:3885
const char * astman_get_header(const struct message *m, char *var)
Return the first matching variable from an array.
Definition: manager.c:1643
#define GET_HEADER_SKIP_EMPTY
Definition: manager.c:1591
static struct ast_threadstorage userevent_buf
Definition: manager.c:1886
static enum ast_transport mansession_get_transport(const struct mansession *s)
Definition: manager.c:2102
static int function_capable_string_allowed_with_auths(const char *evaluating, int writepermlist)
Checks to see if a string which can be used to evaluate functions should be rejected.
Definition: manager.c:777
static char * handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: manager.c:1250
static int action_cancel_atxfer(struct mansession *s, const struct message *m)
Definition: manager.c:4198
static void report_req_not_allowed(const struct mansession *s, const char *action)
Definition: manager.c:2208
static char global_realm[MAXHOSTNAMELEN]
Definition: manager.c:176
static int action_originate(struct mansession *s, const struct message *m)
Definition: manager.c:5227
static int send_string(struct mansession *s, char *string)
Definition: manager.c:1848
#define MAX_AUTH_PERM_STRING
Definition: manager.c:774
static int action_userevent(struct mansession *s, const struct message *m)
Definition: manager.c:6441
static int action_command(struct mansession *s, const struct message *m)
Manager command "command" - execute CLI command.
Definition: manager.c:4275
static void report_inval_password(const struct mansession *s, const char *username)
Definition: manager.c:2158
static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
Definition: manager.c:843
#define MAX_BLACKLIST_CMD_LEN
Descriptor for a manager session, either on the AMI socket or over HTTP.
Definition: manager.c:220
int ast_manager_register2(const char *action, int auth, int(*func)(struct mansession *s, const struct message *m), struct ast_module *module, const char *synopsis, const char *description)
register a new command with manager, including online help. This is the preferred way to register a m...
Definition: manager.c:7824
static int process_events(struct mansession *s)
Definition: manager.c:6413
static int broken_events_action
Definition: manager.c:165
static void report_session_limit(const struct mansession *s)
Definition: manager.c:2296
static int unauth_sessions
Definition: manager.c:178
static int set_eventmask(struct mansession *s, const char *eventmask)
Rather than braindead on,off this now can also accept a specific int mask value or a ',...
Definition: manager.c:2089
static int timestampevents
Definition: manager.c:163
static int subscribed
Definition: manager.c:167
static void manager_generic_msg_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Definition: manager.c:599
static struct ast_aoc_decoded * action_aoc_s_message(struct mansession *s, const struct message *m)
Definition: manager.c:4891
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:2068
static char * handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command manager reload.
Definition: manager.c:1557
static struct ast_aoc_decoded * action_aoc_de_message(struct mansession *s, const struct message *m)
Definition: manager.c:4496
static int authenticate(struct mansession *s, const struct message *m)
Definition: manager.c:2328
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:1903
static void manager_json_obj_with_key(struct ast_json *obj, const char *key, const char *parent_key, struct ast_str **res, key_exclusion_cb exclusion_cb)
Definition: manager.c:495
static int authtimeout
Definition: manager.c:170
static char * handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command manager list commands.
Definition: manager.c:1366
#define EVENT_FLAG_SHUTDOWN
Fake event class used to end sessions at shutdown.
Definition: manager.c:208
static int manager_modulecheck(struct mansession *s, const struct message *m)
Manager function to check if module is loaded.
Definition: manager.c:6841
static void astman_send_list_complete(struct mansession *s, const struct message *m, const char *event_name, int count)
Definition: manager.c:2051
static int async_goto_with_discard_bridge_after(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: manager.c:3946
void astman_live_dangerously(int new_live_dangerously)
Enable/disable the inclusion of 'dangerous' configurations outside of the ast_config_AST_CONFIG_DIR.
Definition: manager.c:2443
int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
access for hooks to send action messages to ami
Definition: manager.c:1760
static int action_aoc_s_submessage(struct mansession *s, const struct message *m, struct ast_aoc_decoded *decoded)
Definition: manager.c:4667
static int webmanager_enabled
Definition: manager.c:168
static struct ast_str * astman_send_list_complete_start_common(struct mansession *s, const struct message *m, const char *event_name, int count)
Definition: manager.c:2029
static int action_status(struct mansession *s, const struct message *m)
Manager "status" command to show channels.
Definition: manager.c:3748
static int get_perm(const char *instr)
Definition: manager.c:862
static int action_coreshowchannels(struct mansession *s, const struct message *m)
Manager command "CoreShowChannels" - List currently defined channels and some information about them.
Definition: manager.c:6580
static struct ast_variable * man_do_variable_value(struct ast_variable *head, const char *hdr_val)
Definition: manager.c:1685
static int action_corestatus(struct mansession *s, const struct message *m)
Show PBX core status information.
Definition: manager.c:6506
static int queue_sendtext(struct ast_channel *chan, const char *body)
Queue a read action to send a text message.
Definition: manager.c:3869
static void action_destroy(void *obj)
Definition: manager.c:7810
static int action_challenge(struct mansession *s, const struct message *m)
Definition: manager.c:3403
static struct stasis_topic * manager_topic
A stasis_topic that all topics AMI cares about will be forwarded to.
Definition: manager.c:182
static int action_waitevent(struct mansession *s, const struct message *m)
Definition: manager.c:3167
static void report_req_bad_format(const struct mansession *s, const char *action)
Definition: manager.c:2237
static int action_coreshowchannelmap(struct mansession *s, const struct message *m)
Manager command "CoreShowChannelMap" - Lists all channels connected to the specified channel.
Definition: manager.c:6748
mansession_message_parsing
Definition: manager.c:313
static char * handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: manager.c:1225
static int action_ping(struct mansession *s, const struct message *m)
Definition: manager.c:2425
#define GET_HEADER_FIRST_MATCH
Definition: manager.c:1589
static int aocmessage_get_unit_entry(const struct message *m, struct ast_aoc_unit_entry *entry, unsigned int entry_num)
Definition: manager.c:4471
static int coreshowchannelmap_add_to_map(struct ao2_container *c, const char *s)
Helper function to add a channel name to the vector.
Definition: manager.c:6647
int ast_manager_hangup_helper(struct mansession *s, const struct message *m, manager_hangup_handler_t hangup_handler, manager_hangup_cause_validator_t cause_validator)
A manager helper function that hangs up a channel using a supplied channel type specific hangup funct...
Definition: manager.c:3421
static int httptimeout
Definition: manager.c:164
static void report_auth_success(const struct mansession *s)
Definition: manager.c:2183
static void * fast_originate(void *data)
Definition: manager.c:4391
static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Definition: manager.c:10048
#define MANAGER_EVENT_BUF_INITSIZE
Definition: manager.c:7539
static const struct permalias perms[]
#define manager_event_sessions(sessions, category, event, contents,...)
Definition: manager.c:562
static char * manager_disabledevents
Definition: manager.c:173
event_filter_match_type
Definition: manager.c:385
static int __manager_event_sessions_va(struct ao2_container *sessions, int category, const char *event, int chancount, struct ast_channel **chans, const char *file, int line, const char *func, const char *fmt, va_list ap)
Definition: manager.c:7541
int ast_manager_unregister(const char *action)
support functions to register/unregister AMI action handlers,
Definition: manager.c:7698
@ FAILURE_EMPTYCAT
Definition: manager.c:116
@ FAILURE_UPDATE
Definition: manager.c:117
@ FAILURE_NEWCAT
Definition: manager.c:114
@ FAILURE_DELETE
Definition: manager.c:118
@ FAILURE_APPEND
Definition: manager.c:119
@ UNKNOWN_CATEGORY
Definition: manager.c:110
@ FAILURE_DELCAT
Definition: manager.c:115
@ FAILURE_ALLOCATION
Definition: manager.c:113
@ FAILURE_TEMPLATE
Definition: manager.c:120
@ UNSPECIFIED_CATEGORY
Definition: manager.c:111
@ UNSPECIFIED_ARGUMENT
Definition: manager.c:112
@ UNKNOWN_ACTION
Definition: manager.c:109
@ FILTER_SUCCESS
Definition: manager.c:124
@ FILTER_FORMAT_ERROR
Definition: manager.c:127
@ FILTER_COMPILE_FAIL
Definition: manager.c:126
@ FILTER_ALLOC_FAILED
Definition: manager.c:125
@ MESSAGE_OKAY
Definition: manager.c:314
@ MESSAGE_LINE_TOO_LONG
Definition: manager.c:315
@ FILTER_MATCH_ENDS_WITH
Definition: manager.c:389
@ FILTER_MATCH_CONTAINS
Definition: manager.c:390
@ FILTER_MATCH_STARTS_WITH
Definition: manager.c:388
@ FILTER_MATCH_REGEX
Definition: manager.c:386
@ FILTER_MATCH_NONE
Definition: manager.c:391
@ FILTER_MATCH_EXACT
Definition: manager.c:387
#define SCOPE_EXIT_RTN_VALUE(__return_value,...)
#define SCOPE_EXIT_LOG_RTN_VALUE(__value, __log_level,...)
#define SCOPE_ENTER(level,...)
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object,...
struct ast_channel_snapshot * ast_channel_snapshot_get_latest_by_name(const char *name)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object,...
struct ao2_container * ast_channel_cache_by_name(void)
Secondary channel cache, indexed by name.
struct ast_msg_data * ast_msg_data_alloc2(enum ast_msg_data_source_type source_type, const char *to, const char *from, const char *content_type, const char *body)
Allocates an ast_msg_data structure.
size_t ast_msg_data_get_length(struct ast_msg_data *msg)
Get length of the structure.
@ AST_MSG_DATA_SOURCE_TYPE_UNKNOWN
Definition: message.h:447
int ast_option_maxfiles
Definition: options.c:82
int ast_option_maxcalls
Definition: options.c:80
double ast_option_maxload
Definition: options.c:78
static int session_limit
Definition: http.c:106
Support for Private Asterisk HTTP Servers.
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, int fd, unsigned int static_content)
Generic function for sending HTTP/1.1 response.
Definition: http.c:472
struct ast_variable * ast_http_get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Get post variables from client Request Entity-Body, if content type is application/x-www-form-urlenco...
Definition: http.c:1405
ast_http_method
HTTP Request methods known by Asterisk.
Definition: http.h:58
@ AST_HTTP_POST
Definition: http.h:61
@ AST_HTTP_GET
Definition: http.h:60
@ AST_HTTP_HEAD
Definition: http.h:62
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:721
uint32_t ast_http_manid_from_vars(struct ast_variable *headers) attribute_pure
Return manager id, if exist, from request headers.
Definition: http.c:233
const char * ast_get_http_method(enum ast_http_method method) attribute_pure
Return http method name string.
Definition: http.c:193
void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm, const unsigned long nonce, const unsigned long opaque, int stale, const char *text)
Send http "401 Unauthorized" response and close socket.
Definition: http.c:638
void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser)
Request the HTTP connection be closed after this HTTP request.
Definition: http.c:853
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text)
Send HTTP error message and close socket.
Definition: http.c:664
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:689
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
int ast_app_has_voicemail(const char *mailboxes, const char *folder)
Determine if a given mailbox has any voicemail If folder is NULL, defaults to "INBOX"....
Definition: main/app.c:582
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
int ast_app_inboxcount2(const char *mailboxes, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Determine number of urgent/new/old messages in a mailbox.
Definition: main/app.c:619
Configuration File Parser.
void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
Definition: main/config.c:469
int ast_category_inherit(struct ast_category *existing, const struct ast_category *base)
Applies base (template) to category.
Definition: main/config.c:1568
const char * ast_category_get_name(const struct ast_category *category)
Return the name of the category.
Definition: main/config.c:1211
struct ast_category * ast_category_new_template(const char *name, const char *in_file, int lineno)
Create a category making it a template.
Definition: main/config.c:1178
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: main/config.c:3541
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3324
struct ast_category * ast_category_delete(struct ast_config *cfg, struct ast_category *cat)
Delete a category.
Definition: main/config.c:1695
int ast_config_text_file_save2(const char *filename, const struct ast_config *cfg, const char *generator, uint32_t flags)
Save a config text file.
Definition: main/config.c:2927
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: main/config.c:1563
int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
Definition: main/config.c:1600
#define CONFIG_STATUS_FILEMISSING
int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
Inserts new category.
Definition: main/config.c:1266
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
Definition: extconf.c:2831
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1175
int ast_variable_update(struct ast_category *category, const char *variable, const char *value, const char *match, unsigned int object)
Update variable value within a config.
Definition: main/config.c:1645
struct ast_variable * ast_variables_reverse(struct ast_variable *var)
Reverse a variable list.
Definition: main/config.c:651
int ast_realtime_enabled(void)
Check if there's any realtime engines loaded.
Definition: main/config.c:3776
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category.
Definition: extconf.c:2786
#define ast_variable_new(name, value, filename)
struct ast_str * ast_category_get_templates(const struct ast_category *category)
Return the template names this category inherits from.
Definition: main/config.c:1221
#define CONFIG_STATUS_FILEUNCHANGED
@ CONFIG_SAVE_FLAG_NONE
@ CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT
#define CONFIG_STATUS_FILEINVALID
int ast_parse_arg(const char *arg, enum ast_parse_flags flags, void *p_result,...)
The argument parsing routine.
Definition: main/config.c:4059
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Definition: main/config.c:629
void ast_category_destroy(struct ast_category *cat)
Definition: extconf.c:2843
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1287
int ast_category_empty(struct ast_category *category)
Removes and destroys all variables in a category.
Definition: main/config.c:1727
int ast_category_is_template(const struct ast_category *category)
Check if category is a template.
Definition: main/config.c:1216
struct ast_variable * ast_category_first(struct ast_category *cat)
given a pointer to a category, return the root variable.
Definition: main/config.c:1358
void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
Definition: main/config.c:585
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1260
@ CONFIG_FLAG_NOCACHE
@ CONFIG_FLAG_WITHCOMMENTS
@ CONFIG_FLAG_FILEUNCHANGED
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1213
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
Definition: main/config.c:1205
struct ast_category * ast_category_browse_filtered(struct ast_config *config, const char *category_name, struct ast_category *prev, const char *filter)
Browse categories with filters.
Definition: main/config.c:1536
#define AST_FEATURE_MAX_LEN
int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len)
Get the DTMF code for a builtin feature.
char * ast_get_chan_features_atxferabort(struct ast_channel *chan)
Get the transfer configuration option atxferabort.
ast_frame_read_action
Actions to indicate to, and be handled on channel read.
@ AST_FRAME_READ_ACTION_SEND_TEXT
@ AST_FRAME_READ_ACTION_SEND_TEXT_DATA
#define AST_FRAME_DTMF
@ AST_CONTROL_READ_ACTION
@ AST_CONTROL_AOC
int ast_logger_rotate(void)
Reload logger while rotating log files.
Definition: logger.c:1307
#define AST_LOG_WARNING
#define AST_LOG_ERROR
#define ast_debug(level,...)
Log a DEBUG message.
#define VERBOSITY_ATLEAST(level)
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
void ast_iostream_set_timeout_inactivity(struct ast_iostream *stream, int timeout)
Set the iostream inactivity timeout timer.
Definition: iostream.c:122
struct ast_iostream * ast_iostream_from_fd(int *fd)
Create an iostream from a file descriptor.
Definition: iostream.c:611
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition: iostream.c:385
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream's file descriptor.
Definition: iostream.c:85
void ast_iostream_set_exclusive_input(struct ast_iostream *stream, int exclusive_input)
Set the iostream if it can exclusively depend upon the set timeouts.
Definition: iostream.c:149
ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
Read data from an iostream.
Definition: iostream.c:284
void ast_iostream_set_timeout_sequence(struct ast_iostream *stream, struct timeval start, int timeout)
Set the iostream I/O sequence timeout timer.
Definition: iostream.c:140
void ast_iostream_nonblock(struct ast_iostream *stream)
Make an iostream non-blocking.
Definition: iostream.c:104
int ast_iostream_close(struct ast_iostream *stream)
Close an iostream.
Definition: iostream.c:539
void ast_iostream_set_timeout_disable(struct ast_iostream *stream)
Disable the iostream timeout timer.
Definition: iostream.c:114
Asterisk JSON abstraction layer.
struct ast_json * ast_json_object_iter_value(struct ast_json_iter *iter)
Get the value from an iterator.
Definition: json.c:455
enum ast_json_type ast_json_typeof(const struct ast_json *value)
Get the type of value.
Definition: json.c:78
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_json_iter * ast_json_object_iter_next(struct ast_json *object, struct ast_json_iter *iter)
Get the next iterator.
Definition: json.c:447
struct ast_json * ast_json_array_get(const struct ast_json *array, size_t index)
Get an element from an array.
Definition: json.c:370
struct ast_json_payload * ast_json_payload_create(struct ast_json *json)
Create an ao2 object to pass json blobs as data payloads for stasis.
Definition: json.c:756
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
struct ast_json_iter * ast_json_object_iter(struct ast_json *object)
Get an iterator pointing to the first field in a JSON object.
Definition: json.c:439
@ AST_JSON_STRING
Definition: json.h:166
@ AST_JSON_ARRAY
Definition: json.h:165
@ AST_JSON_OBJECT
Definition: json.h:164
@ AST_JSON_FALSE
Definition: json.h:170
@ AST_JSON_INTEGER
Definition: json.h:167
@ AST_JSON_TRUE
Definition: json.h:169
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
const char * ast_json_object_iter_key(struct ast_json_iter *iter)
Get the key from an iterator.
Definition: json.c:451
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition: json.c:332
size_t ast_json_array_size(const struct ast_json *array)
Get the size of a JSON array.
Definition: json.c:366
int ast_json_is_null(const struct ast_json *value)
Check if value is JSON null.
Definition: json.c:273
A set of macros to manage forward-linked lists.
#define AST_RWLIST_EMPTY
Definition: linkedlists.h:452
#define AST_RWLIST_REMOVE_CURRENT
Definition: linkedlists.h:570
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:545
#define AST_RWLIST_LAST
Definition: linkedlists.h:431
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized.
Definition: linkedlists.h:333
#define AST_RWLIST_REMOVE_HEAD
Definition: linkedlists.h:844
#define AST_RWLIST_INSERT_AFTER
Definition: linkedlists.h:702
#define AST_RWLIST_NEXT
Definition: linkedlists.h:441
#define AST_RWLIST_REMOVE
Definition: linkedlists.h:885
#define AST_RWLIST_FIRST
Definition: linkedlists.h:423
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:617
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:718
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:741
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:415
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
Asterisk locking-related definitions:
#define AST_PTHREADT_NULL
Definition: lock.h:73
#define ast_mutex_init(pmutex)
Definition: lock.h:193
#define ast_mutex_unlock(a)
Definition: lock.h:197
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:764
#define ast_mutex_destroy(a)
Definition: lock.h:195
#define ast_mutex_lock(a)
Definition: lock.h:196
int errno
static int auth_mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Definition: manager.c:8970
static int manager_subscriptions_init(void)
Initialize all Stasis Message Bus API topics and routers used by the various sub-components of AMI.
Definition: manager.c:9554
#define ROW_FMT
static struct ast_tls_config ami_tls_cfg
Definition: manager.c:9109
static void close_mansession_file(struct mansession *s)
Definition: manager.c:8322
static int ast_xml_doc_item_cmp_fn(const void *a, const void *b)
Definition: manager.c:9176
static struct ast_tcptls_session_args ami_desc
Definition: manager.c:9110
static struct ast_http_uri managerxmluri
Definition: manager.c:8949
const char * words[AST_MAX_CMD_LEN]
Definition: manager.c:222
static char * handle_manager_show_events(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: manager.c:9183
static int manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Definition: manager.c:8900
int astman_verify_session_writepermissions(uint32_t ident, int perm)
Verify a session's write permissions against a permission mask.
Definition: manager.c:8048
static int __init_manager(int reload, int by_external_config)
Definition: manager.c:9645
static struct ast_tcptls_session_args amis_desc
Definition: manager.c:9121
static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
Definition: manager.c:8086
#define FORMAT3
static struct ast_custom_function managerclient_function
description of AMI_CLIENT dialplan function
Definition: manager.c:9082
static void purge_old_stuff(void *data)
cleanup code called at each iteration of server_root, guaranteed to happen every 5 seconds at most
Definition: manager.c:9093
struct ast_datastore * astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a session.
Definition: manager.c:10095
static struct mansession_session * find_session(uint32_t ident, int incinuse)
Definition: manager.c:7927
static int mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Definition: manager.c:8911
int ast_str_append_event_header(struct ast_str **fields_string, const char *header, const char *value)
append an event header to an ast string
Definition: manager.c:10122
#define HSMCONN_FORMAT1
int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
Add a datastore to a session.
Definition: manager.c:10083
static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
Definition: manager.c:8165
static char * handle_manager_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command manager show settings.
Definition: manager.c:9132
static void manager_event_blob_dtor(void *obj)
Definition: manager.c:10135
static void load_channelvars(struct ast_variable *var)
Definition: manager.c:9397
struct ast_manager_event_blob * ast_manager_event_blob_create(int event_flags, const char *manager_event, const char *extra_fields_fmt,...)
Construct a ast_manager_event_blob.
Definition: manager.c:10144
static struct mansession_session * find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
Definition: manager.c:7966
int astman_verify_session_readpermissions(uint32_t ident, int perm)
Verify a session's read permissions against a permission mask.
Definition: manager.c:8015
static struct ast_http_uri manageruri
Definition: manager.c:8941
static void manager_set_defaults(void)
Definition: manager.c:9617
static void manager_shutdown(void)
Definition: manager.c:9449
#define HSMC_FORMAT
static void process_output(struct mansession *s, struct ast_str **out, struct ast_variable *params, enum output_format format)
Definition: manager.c:8332
static int rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Definition: manager.c:8922
static char * handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: manager.c:9312
static int function_amiclient(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
${AMI_CLIENT()} Dialplan function - reads manager client data
Definition: manager.c:9035
static int auth_rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Definition: manager.c:8981
static int load_module(void)
Definition: manager.c:10065
static struct ast_http_uri arawmanuri
Definition: manager.c:8992
static void xml_translate(struct ast_str **out, char *in, struct ast_variable *get_vars, enum output_format format)
Convert the input into XML or HTML. The input is supposed to be a sequence of lines of the form Name:...
Definition: manager.c:8204
static int variable_count_hash_fn(const void *vvc, const int flags)
Definition: manager.c:8158
#define FORMAT
int astman_is_authed(uint32_t ident)
Determine if a manager session ident is authenticated.
Definition: manager.c:7999
static struct ast_http_uri rawmanuri
Definition: manager.c:8933
static struct ast_cli_entry cli_manager[]
Definition: manager.c:9374
#define FORMAT2
static int unload_module(void)
Definition: manager.c:10060
static void manager_free_user(struct ast_manager_user *user)
Definition: manager.c:9430
static int auth_manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Definition: manager.c:8959
output_format
Definition: manager.c:7910
@ FORMAT_RAW
Definition: manager.c:7911
@ FORMAT_HTML
Definition: manager.c:7912
@ FORMAT_XML
Definition: manager.c:7913
static int auth_http_callback(struct ast_tcptls_session_instance *ser, enum ast_http_method method, enum output_format format, const struct ast_sockaddr *remote_address, const char *uri, struct ast_variable *get_params, struct ast_variable *headers)
Definition: manager.c:8577
static struct ast_http_uri amanageruri
Definition: manager.c:9001
#define TEST_STRING
static int webregged
Definition: manager.c:9088
static int get_manager_sessions_cb(void *obj, void *arg, void *data, int flags)
Get number of logged in sessions for a login name.
Definition: manager.c:9020
static int subscribe_all(void)
Definition: manager.c:9587
static const char *const contenttype[]
Definition: manager.c:7916
static struct ast_http_uri amanagerxmluri
Definition: manager.c:9010
static int generic_http_callback(struct ast_tcptls_session_instance *ser, enum ast_http_method method, enum output_format format, const struct ast_sockaddr *remote_address, const char *uri, struct ast_variable *get_params, struct ast_variable *headers)
Definition: manager.c:8365
int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
Remove a datastore from a session.
Definition: manager.c:10090
static void load_disabledevents(struct ast_variable *var)
Definition: manager.c:9420
#define HSMCONN_FORMAT2
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define EVENT_FLAG_VERBOSE
Definition: manager.h:78
#define EVENT_FLAG_REPORTING
Definition: manager.h:84
#define ast_manager_register_xml_core(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:203
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition: manager.h:254
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
int manager_mwi_init(void)
Initialize support for AMI MWI events.
Definition: manager_mwi.c:160
#define EVENT_FLAG_CONFIG
Definition: manager.h:82
int manager_bridging_init(void)
Initialize support for AMI channel events.
#define EVENT_FLAG_SYSTEM
Definition: manager.h:75
#define EVENT_FLAG_TEST
Definition: manager.h:92
variable_orders
Definition: manager.h:288
@ ORDER_NATURAL
Definition: manager.h:289
@ ORDER_REVERSE
Definition: manager.h:290
#define EVENT_FLAG_DTMF
Definition: manager.h:83
#define EVENT_FLAG_SECURITY
Definition: manager.h:93
int manager_endpoints_init(void)
Initialize support for AMI endpoint events.
void(* manager_hangup_handler_t)(struct ast_channel *chan, int causecode)
Callback used by ast_manager_hangup_helper that will actually hangup a channel.
Definition: manager.h:632
int(* key_exclusion_cb)(const char *key)
Callback used to determine whether a key should be skipped when converting a JSON object to a manager...
Definition: manager.h:456
struct stasis_message_type * ast_manager_get_generic_type(void)
Get the stasis_message_type for generic messages.
#define AMI_VERSION
Definition: manager.h:57
#define DEFAULT_MANAGER_PORT
Definition: manager.h:58
int manager_system_init(void)
Initialize support for AMI system events.
#define EVENT_FLAG_HOOKRESPONSE
Definition: manager.h:89
#define ast_manager_event_multichan(category, event, nchans, chans, contents,...)
Definition: manager.h:261
#define DEFAULT_MANAGER_TLS_PORT
Definition: manager.h:59
#define EVENT_FLAG_CALL
Definition: manager.h:76
#define EVENT_FLAG_MESSAGE
Definition: manager.h:95
#define EVENT_FLAG_COMMAND
Definition: manager.h:79
int manager_channels_init(void)
Initialize support for AMI channel events.
#define EVENT_FLAG_AGI
Definition: manager.h:88
#define EVENT_FLAG_USER
Definition: manager.h:81
#define EVENT_FLAG_AGENT
Definition: manager.h:80
#define EVENT_FLAG_AOC
Definition: manager.h:91
#define EVENT_FLAG_DIALPLAN
Definition: manager.h:86
#define EVENT_FLAG_ORIGINATE
Definition: manager.h:87
#define EVENT_FLAG_CDR
Definition: manager.h:85
struct ast_str * ast_manager_build_channel_state_string_prefix(const struct ast_channel_snapshot *snapshot, const char *prefix)
Generate the AMI message body from a channel snapshot.
int(* manager_hangup_cause_validator_t)(const char *channel_name, const char *cause)
Callback used by ast_manager_hangup_helper that will validate the cause code.
Definition: manager.h:643
#define EVENT_FLAG_CC
Definition: manager.h:90
#define EVENT_FLAG_LOG
Definition: manager.h:77
MD5 digest functions.
void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len)
Definition: md5.c:72
void MD5Init(struct MD5Context *context)
Definition: md5.c:57
void MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], struct MD5Context *context)
Definition: md5.c:120
Out-of-call text message support.
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define ast_module_unref(mod)
Release a reference to the module.
Definition: module.h:483
int ast_module_check(const char *name)
Check if module with the name given is loaded.
Definition: loader.c:2832
int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode)
Unload a module.
Definition: loader.c:1457
enum ast_module_load_result ast_load_resource(const char *resource_name)
Load a module.
Definition: loader.c:1987
int ast_refresh_resource(const char *resource_name, enum ast_module_unload_mode force, int recursive)
Unload and load a module again.
Definition: loader.c:1416
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CORE
Definition: module.h:338
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_FORCE_SOFT
Definition: module.h:62
ast_module_reload_result
Possible return types for ast_module_reload.
Definition: module.h:109
@ AST_MODULE_RELOAD_IN_PROGRESS
Definition: module.h:114
@ AST_MODULE_RELOAD_QUEUED
Definition: module.h:111
@ AST_MODULE_RELOAD_SUCCESS
Definition: module.h:110
@ AST_MODULE_RELOAD_ERROR
Definition: module.h:113
@ AST_MODULE_RELOAD_NOT_IMPLEMENTED
Definition: module.h:116
@ AST_MODULE_RELOAD_NOT_FOUND
Definition: module.h:112
@ AST_MODULE_RELOAD_UNINITIALIZED
Definition: module.h:115
#define ast_module_running_ref(mod)
Hold a reference to the module if it is running.
Definition: module.h:469
@ AST_MODULE_LOAD_FAILURE
Module could not be loaded properly.
Definition: module.h:102
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
enum ast_module_reload_result ast_module_reload(const char *name)
Reload asterisk modules.
Definition: loader.c:1730
Asterisk MWI API.
def info(msg)
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
static void ast_sockaddr_copy(struct ast_sockaddr *dst, const struct ast_sockaddr *src)
Copies the data from one ast_sockaddr to another.
Definition: netsock2.h:167
ast_transport
Definition: netsock2.h:59
@ AST_TRANSPORT_TLS
Definition: netsock2.h:62
@ AST_TRANSPORT_TCP
Definition: netsock2.h:61
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
static int ast_sockaddr_isnull(const struct ast_sockaddr *addr)
Checks if the ast_sockaddr is null. "null" in this sense essentially means uninitialized,...
Definition: netsock2.h:127
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
static char * ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only.
Definition: netsock2.h:286
static void ast_sockaddr_setnull(struct ast_sockaddr *addr)
Sets address addr to null.
Definition: netsock2.h:138
#define MAXHOSTNAMELEN
Definition: network.h:69
#define ast_fully_booted
Definition: options.h:127
struct timeval ast_lastreloadtime
Definition: asterisk.c:344
struct timeval ast_startuptime
Definition: asterisk.c:343
#define ast_opt_sounds_search_custom
Definition: options.h:148
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_RUN_GROUP
Definition: options.c:170
const char * ast_config_AST_RUN_USER
Definition: options.c:169
const char * ast_config_AST_MODULE_DIR
Definition: options.c:154
const char * ast_config_AST_CONFIG_DIR
Definition: options.c:152
const char * ast_config_AST_SYSTEM_NAME
Definition: options.c:171
Core PBX routines and definitions.
int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
Find the priority of an extension that has the specified label.
Definition: pbx.c:4201
int ast_pbx_outgoing_exten_predial(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, int early_media, const struct ast_assigned_ids *assignedids, const char *predial_callee)
Definition: pbx.c:7951
const char * ast_extension_state2str(int extension_state)
Return string representation of the state of an extension.
Definition: pbx.c:3147
@ AST_OUTGOING_WAIT
Definition: pbx.h:1144
int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *app, const char *appdata, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, const struct ast_assigned_ids *assignedids)
Synchronously or asynchronously make an outbound call and execute an application on the channel.
Definition: pbx.c:8005
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4196
int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, int early_media, const struct ast_assigned_ids *assignedids)
Synchronously or asynchronously make an outbound call and send it to a particular extension.
Definition: pbx.c:7941
int ast_processed_calls(void)
Retrieve the total number of calls processed through the PBX since last restart.
Definition: pbx.c:4786
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
executes a read operation on a function
int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
Register a custom function.
void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
Retrieve the value of a builtin variable or variable from the channel variable stack.
@ AST_HINT_UPDATE_DEVICE
Definition: pbx.h:91
@ AST_HINT_UPDATE_PRESENCE
Definition: pbx.h:93
int ast_extension_state_add(const char *context, const char *exten, ast_state_cb_type change_cb, void *data)
Add watcher for extension states.
Definition: pbx.c:3844
int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
If an extension hint exists, return non-zero.
Definition: pbx.c:4158
int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
Set the channel to next execute the specified dialplan location.
Definition: pbx.c:6994
int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
Uses hint and devicestate callback to get the state of an extension.
Definition: pbx.c:3191
Presence state management.
ast_presence_state
Definition: presencestate.h:26
@ AST_PRESENCE_INVALID
Definition: presencestate.h:39
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
static struct templates templates
static int total
Definition: res_adsi.c:970
static char url[512]
static char user[512]
static int reload(void)
struct stasis_forward * sub
Definition: res_corosync.c:240
const char * method
Definition: res_pjsip.c:1279
#define NULL
Definition: resample.c:96
Pluggable RTP Architecture.
struct stasis_topic * ast_rtp_topic(void)
Stasis Message Bus API topic for RTP and RTCP related messages
Definition: rtp_engine.c:3752
Security Event Reporting API.
int ast_security_event_report(const struct ast_security_event_common *sec)
Report a security event.
struct stasis_topic * ast_security_topic(void)
A stasis_topic which publishes messages for security related issues.
#define AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION
Event descriptor version.
#define AST_SECURITY_EVENT_SESSION_LIMIT_VERSION
Event descriptor version.
#define AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION
Event descriptor version.
#define AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION
Event descriptor version.
#define AST_SECURITY_EVENT_FAILED_ACL_VERSION
Event descriptor version.
#define AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION
Event descriptor version.
#define AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION
Event descriptor version.
@ AST_SECURITY_EVENT_INVAL_PASSWORD
An attempt at basic password authentication failed.
@ AST_SECURITY_EVENT_SESSION_LIMIT
Session limit reached.
@ AST_SECURITY_EVENT_FAILED_ACL
Failed ACL.
@ AST_SECURITY_EVENT_REQ_NOT_ALLOWED
A request was made that is not allowed.
@ AST_SECURITY_EVENT_CHAL_RESP_FAILED
An attempt at challenge/response authentication failed.
@ AST_SECURITY_EVENT_REQ_BAD_FORMAT
Request received with bad formatting.
@ AST_SECURITY_EVENT_SUCCESSFUL_AUTH
FYI FWIW, Successful authentication has occurred.
@ AST_SECURITY_EVENT_INVAL_ACCT_ID
Invalid Account ID.
#define AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION
Event descriptor version.
#define AST_SEC_EVT(e)
struct ast_manager_event_blob * stasis_message_to_ami(struct stasis_message *msg)
Build the AMI representation of the message.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition: stasis.h:1515
@ STASIS_SUBSCRIPTION_FILTER_SELECTIVE
Definition: stasis.h:297
struct stasis_forward * stasis_forward_cancel(struct stasis_forward *forward)
Definition: stasis.c:1615
struct stasis_topic * stasis_topic_create(const char *name)
Create a new topic.
Definition: stasis.c:684
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition: stasis.c:1090
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition: stasis.c:1144
@ STASIS_SUBSCRIPTION_FORMATTER_AMI
Definition: stasis.h:311
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1493
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition: stasis.c:1201
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
struct stasis_forward * stasis_forward_all(struct stasis_topic *from_topic, struct stasis_topic *to_topic)
Create a subscription which forwards all messages from one topic to another.
Definition: stasis.c:1645
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1578
#define stasis_subscribe(topic, callback, data)
Definition: stasis.h:649
int stasis_message_can_be_ami(struct stasis_message *msg)
Determine if the given message can be converted to AMI.
struct ast_bridge_snapshot * ast_bridge_get_snapshot_by_uniqueid(const char *bridge_id)
Returns the current snapshot for the bridge.
int stasis_message_router_set_congestion_limits(struct stasis_message_router *router, long low_water, long high_water)
Set the high and low alert water marks of the stasis message router.
#define stasis_message_router_create(topic)
Create a new message router object.
int stasis_message_router_add(struct stasis_message_router *router, struct stasis_message_type *message_type, stasis_subscription_cb callback, void *data)
Add a route to a message router.
void stasis_message_router_unsubscribe_and_join(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic, blocking until the final message has been processed.
void stasis_message_router_set_formatters_default(struct stasis_message_router *router, stasis_subscription_cb callback, void *data, enum stasis_subscription_message_formatters formatters)
Sets the default route of a router with formatters.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define ast_string_field_ptr_build_va(x, ptr, fmt, args)
Set a field to a complex (built) value with prebuilt va_lists.
Definition: stringfields.h:573
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
#define ast_string_field_init_extended(x, field)
Initialize an extended string field.
Definition: stringfields.h:401
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
String manipulation functions.
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
int ast_str_set_va(struct ast_str **buf, ssize_t max_len, const char *fmt, va_list ap)
Set a dynamic string from a va_list.
Definition: strings.h:1030
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2235
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
@ AST_DYNSTR_BUILD_FAILED
Definition: strings.h:943
static int force_inline attribute_pure ast_ends_with(const char *str, const char *suffix)
Checks whether a string ends with another.
Definition: strings.h:116
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
@ AST_STRSEP_STRIP
Definition: strings.h:255
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition: utils.c:2252
#define ast_str_alloca(init_len)
Definition: strings.h:848
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
int ast_in_delimited_string(const char *needle, const char *haystack, char delim)
Check if there is an exact match for 'needle' between delimiters in 'haystack'.
Definition: strings.c:466
#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
int ast_str_append_va(struct ast_str **buf, ssize_t max_len, const char *fmt, va_list ap)
Append to a dynamic string using a va_list.
Definition: strings.h:1048
char * ast_read_line_from_buffer(char **buffer)
Read lines from a string buffer.
Definition: strings.c:385
#define AST_YESNO(x)
return Yes or No depending on the argument.
Definition: strings.h:143
char * ast_trim_blanks(char *str)
Trims trailing whitespace characters from a string.
Definition: strings.h:186
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
char * ast_escape_c_alloc(const char *s)
Escape standard 'C' sequences in the given string.
Definition: utils.c:2176
struct ao2_container * ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets)
Allocates a hash container for bare strings.
Definition: strings.c:200
int ast_str_container_add(struct ao2_container *str_container, const char *add)
Adds a string to a string container allocated by ast_str_container_alloc.
Definition: strings.c:205
#define AS_OR(a, b)
Definition: strings.h:49
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition: utils.c:1871
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:161
int ast_regex_string_to_regex_pattern(const char *regex_string, struct ast_str **regex_pattern)
Given a string regex_string in the form of "/regex/", convert it into the form of "regex".
Definition: utils.c:2215
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
Definition: md5.h:30
const char * name
list of actions registered
Definition: manager.c:363
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
Wrapper for an ast_acl linked list.
Definition: acl.h:76
Definition: aoc.h:178
unsigned int amount
Definition: aoc.h:180
unsigned int type
Definition: aoc.h:182
char valid_type
Definition: aoc.h:181
char valid_amount
Definition: aoc.h:179
Structure to pass both assignedid values to channel drivers.
Definition: channel.h:606
const char * uniqueid2
Definition: channel.h:608
const char * uniqueid
Definition: channel.h:607
Structure that contains a snapshot of information about a bridge.
Definition: bridge.h:318
struct ao2_container * channels
Definition: bridge.h:335
Structure that contains information about a bridge.
Definition: bridge.h:353
const ast_string_field uniqueid
Definition: bridge.h:405
struct ast_category * prev
Definition: main/config.c:245
const ast_string_field uniqueid
const ast_string_field name
const ast_string_field data
const ast_string_field appl
Structure representing a snapshot of channel state.
struct ast_channel_snapshot_dialplan * dialplan
struct ast_channel_snapshot_bridge * bridge
struct ast_channel_snapshot_base * base
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
enum ast_frame_read_action action
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
Structure for a data store object.
Definition: datastore.h:64
const struct ast_datastore_info * info
Definition: datastore.h:67
struct ast_datastore::@219 entry
const char * uid
Definition: datastore.h:65
Structure used to handle boolean flags.
Definition: utils.h:217
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
Data structure associated with a single frame of data.
Definition of a URI handler.
Definition: http.h:102
const char * description
Definition: http.h:104
const char * uri
Definition: http.h:105
void * data
Definition: http.h:116
Iterator for JSON object key/values.
struct ast_json * json
Definition: json.h:1083
Abstract JSON element (object, array, string, int, ...).
Struct containing info for an AMI event to send out.
Definition: manager.h:503
const ast_string_field extra_fields
Definition: manager.h:508
const char * manager_event
Definition: manager.h:505
user descriptor, as read from the config file.
Definition: manager.c:342
struct ast_variable * chanvars
Definition: manager.c:355
struct ao2_container * excludefilters
Definition: manager.c:352
int displayconnects
Definition: manager.c:348
char username[80]
Definition: manager.c:343
int allowmultiplelogin
Definition: manager.c:349
struct ast_acl_list * acl
Definition: manager.c:353
char * secret
Definition: manager.c:344
char * a1_hash
Definition: manager.c:354
struct ao2_container * includefilters
Definition: manager.c:351
Structure used to transport a message through the frame core.
Structure for mutex and tracking information.
Definition: lock.h:142
Information needed to identify an endpoint in a call.
Definition: channel.h:340
struct ast_party_name name
Subscriber name.
Definition: channel.h:342
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:344
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:281
char * str
Subscriber name (Malloced)
Definition: channel.h:266
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:299
char * str
Subscriber phone number (Malloced)
Definition: channel.h:293
An attempt at challenge/response auth failed.
const char * response
Response received.
struct ast_security_event_common common
Common security event descriptor elements.
const char * expected_response
Response expected to be received.
enum ast_security_event_type event_type
The security event sub-type.
Checking against an IP access control list failed.
struct ast_security_event_common common
Common security event descriptor elements.
Invalid account ID specified (invalid username, for example)
struct ast_security_event_common common
Common security event descriptor elements.
An attempt at basic password auth failed.
struct ast_security_event_common common
Common security event descriptor elements.
Invalid formatting of request.
struct ast_security_event_common common
Common security event descriptor elements.
const char * request_type
Request type that was made.
Request denied because it's not allowed.
struct ast_security_event_common common
Common security event descriptor elements.
const char * request_type
Request type that was made.
Request denied because of a session limit.
struct ast_security_event_common common
Common security event descriptor elements.
Socket address structure.
Definition: netsock2.h:97
Support for dynamic strings.
Definition: strings.h:623
arguments for the accepting thread
Definition: tcptls.h:130
struct ast_sockaddr local_address
Definition: tcptls.h:131
struct ast_tls_config * tls_cfg
Definition: tcptls.h:135
describes a server instance
Definition: tcptls.h:151
struct ast_iostream * stream
Definition: tcptls.h:162
struct ast_sockaddr remote_address
Definition: tcptls.h:153
struct ast_tcptls_session_args * parent
Definition: tcptls.h:154
int enabled
Definition: tcptls.h:89
char * certfile
Definition: tcptls.h:90
char * cipher
Definition: tcptls.h:92
char * pvtfile
Definition: tcptls.h:91
char * capath
Definition: tcptls.h:94
char * cafile
Definition: tcptls.h:93
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Struct that contains the XML documentation for a particular item. Note that this is an ao2 ref counte...
Definition: xmldoc.h:56
struct ast_str * syntax
Definition: xmldoc.h:58
struct ast_xml_doc_item * next
Definition: xmldoc.h:80
struct ast_str * arguments
Definition: xmldoc.h:62
struct ast_str * since
Definition: xmldoc.h:82
struct ast_str * description
Definition: xmldoc.h:66
const ast_string_field name
Definition: xmldoc.h:74
struct ast_str * seealso
Definition: xmldoc.h:60
struct ast_str * synopsis
Definition: xmldoc.h:64
Definition: astman.c:88
Definition: manager.c:403
char * string_filter
Definition: manager.c:406
int is_excludefilter
Definition: manager.c:410
unsigned int event_name_hash
Definition: manager.c:408
char * event_name
Definition: manager.c:407
enum event_filter_match_type match_type
Definition: manager.c:404
regex_t * regex_filter
Definition: manager.c:405
char * header_name
Definition: manager.c:409
Definition: astman.c:222
unsigned int seq
Definition: manager.c:152
int category
Definition: manager.c:151
struct timeval tv
Definition: manager.c:153
int usecount
Definition: manager.c:150
int event_name_hash
Definition: manager.c:154
helper function for originate
Definition: manager.c:4355
struct ast_variable * vars
Definition: manager.c:4375
struct ast_format_cap * cap
Definition: manager.c:4357
const ast_string_field description
Definition: manager.h:163
struct ast_module * module
Definition: manager.h:172
struct ast_xml_doc_item * final_response
Definition: manager.h:167
const ast_string_field synopsis
Definition: manager.h:163
enum ast_doc_src docsrc
Definition: manager.h:174
int(* func)(struct mansession *s, const struct message *m)
Definition: manager.h:171
const char * action
Definition: manager.h:156
const ast_string_field seealso
Definition: manager.h:163
const ast_string_field syntax
Definition: manager.h:163
unsigned int registered
TRUE if the AMI action is registered and the callback can be called.
Definition: manager.h:183
const ast_string_field arguments
Definition: manager.h:163
struct ast_xml_doc_item * list_responses
Definition: manager.h:165
manager_hook_t helper
Definition: manager.h:117
list of hooks registered
Definition: manager.c:366
struct ast_variable * chanvars
Definition: manager.c:298
unsigned int kicked
Definition: manager.c:307
struct ao2_container * excludefilters
Definition: manager.c:297
struct ast_sockaddr addr
Definition: manager.c:280
uint32_t managerid
Definition: manager.c:285
ast_mutex_t notify_lock
Definition: manager.c:308
pthread_t waiting_thread
Definition: manager.c:284
char challenge[10]
Definition: manager.c:290
struct ast_iostream * stream
Definition: manager.c:281
struct timeval sessionstart_tv
Definition: manager.c:287
char username[80]
Definition: manager.c:289
time_t sessionstart
Definition: manager.c:286
unsigned long oldnonce
Definition: manager.c:305
time_t authstart
Definition: manager.c:302
time_t noncetime
Definition: manager.c:304
unsigned long nc
Definition: manager.c:306
struct mansession_session::@378 list
struct eventqent * last_ev
Definition: manager.c:300
time_t sessiontimeout
Definition: manager.c:288
struct mansession_session::mansession_datastores datastores
char inbuf[1025]
Definition: manager.c:294
struct ao2_container * includefilters
Definition: manager.c:296
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:323
struct ast_iostream * stream
Definition: manager.c:325
unsigned int write_error
Definition: manager.c:328
struct manager_custom_hook * hook
Definition: manager.c:329
struct mansession_session * session
Definition: manager.c:324
enum mansession_message_parsing parsing
Definition: manager.c:327
ast_mutex_t lock
Definition: manager.c:330
struct ast_tcptls_session_instance * tcptls_session
Definition: manager.c:326
unsigned int hdrcount
Definition: manager.h:150
const char * headers[AST_MAX_MANHEADERS]
Definition: manager.h:151
Number structure.
Definition: app_followme.c:157
Definition: manager.c:4993
int permission
Definition: manager.c:4995
const char * search
Definition: manager.c:4994
int num
Definition: manager.c:747
const char * label
Definition: manager.c:748
Forwarding information.
Definition: stasis.c:1598
structure to hold users read from phoneprov_users.conf
list of users found in the config file
Definition: ast_expr2.c:325
char * varname
Definition: manager.c:8154
int value
Definition: syslog.c:37
An API for managing task processing threads that can be shared across modules.
#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL
Definition: taskprocessor.h:64
Generic support for tcp/tls servers in Asterisk.
void * ast_tcptls_server_root(void *)
Definition: tcptls.c:280
#define AST_CERTFILE
Definition: tcptls.h:63
void ast_tcptls_server_stop(struct ast_tcptls_session_args *desc)
Shutdown a running server if there is one.
Definition: tcptls.c:933
int ast_ssl_setup(struct ast_tls_config *cfg)
Set up an SSL server.
Definition: tcptls.c:577
void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a ...
Definition: tcptls.c:768
int ast_tls_read_conf(struct ast_tls_config *tls_cfg, struct ast_tcptls_session_args *tls_desc, const char *varname, const char *value)
Used to parse conf files containing tls/ssl options.
Definition: tcptls.c:959
Handy terminal functions for vt* terms.
const char * ast_term_reset(void)
Returns the terminal reset code.
Definition: term.c:357
#define COLOR_MAGENTA
Definition: term.h:60
const char * ast_term_color(int fgcolor, int bgcolor)
Return a color sequence string.
Definition: term.c:341
char * term_strip(char *outbuf, const char *inbuf, int maxout)
Remove colorings from a specified string.
Definition: term.c:362
#define COLORIZE(fg, bg, str)
Definition: term.h:72
#define COLORIZE_FMT
Shortcut macros for coloring a set of text.
Definition: term.h:71
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define ast_test_debug(test, fmt,...)
Definition: test.h:130
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
int done
Definition: test_amihooks.c:48
static struct aco_type item
Definition: test_config.c:1463
const char * args
static struct test_options options
static struct test_val b
static struct test_val a
static struct test_val d
static struct test_val c
Definitions to aid in the use of thread local storage.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
int64_t ast_tvdiff_sec(struct timeval end, struct timeval start)
Computes the difference (in seconds) between two struct timeval instances.
Definition: time.h:73
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Definition: time.h:117
struct timeval ast_tvsub(struct timeval a, struct timeval b)
Returns the difference of two timevals a - b.
Definition: extconf.c:2295
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Support for translation of data formats. translate.c.
const char * ast_translate_path_to_str(struct ast_trans_pvt *t, struct ast_str **str)
Puts a string representation of the translation path into outbuf.
Definition: translate.c:930
FILE * out
Definition: utils/frame.c:33
int error(const char *format,...)
Definition: utils/frame.c:999
FILE * in
Definition: utils/frame.c:33
Utility functions.
#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:978
#define ast_assert(a)
Definition: utils.h:776
int ast_wait_for_input(int fd, int ms)
Definition: utils.c:1734
int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request, int pedantic)
Parse digest authorization header.
Definition: utils.c:2674
long int ast_random(void)
Definition: utils.c:2348
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:625
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:703
void ast_md5_hash(char *output, const char *input)
Produces MD5 hash based on input string.
Definition: utils.c:250
struct ast_xml_doc_item * ast_xmldoc_build_list_responses(const char *type, const char *name, const char *module)
Generate the [list responses] tag based on type of node ('application', 'function' or 'agi') and name...
Definition: xmldoc.c:2583
char * ast_xmldoc_build_description(const char *type, const char *name, const char *module)
Generate description documentation from XML.
Definition: xmldoc.c:2361
char * ast_xmldoc_build_syntax(const char *type, const char *name, const char *module)
Get the syntax for a specified application or function.
Definition: xmldoc.c:1252
char * ast_xmldoc_build_arguments(const char *type, const char *name, const char *module)
Generate the [arguments] tag based on type of node ('application', 'function' or 'agi') and name.
Definition: xmldoc.c:2174
char * ast_xmldoc_build_synopsis(const char *type, const char *name, const char *module)
Generate synopsis documentation from XML.
Definition: xmldoc.c:2338
char * ast_xmldoc_build_since(const char *type, const char *name, const char *module)
Parse the <since> node content.
Definition: xmldoc.c:1792
struct ao2_container * ast_xmldoc_build_documentation(const char *type)
Build the documentation for a particular source type.
Definition: xmldoc.c:2783
@ AST_XML_DOC
Definition: xmldoc.h:31
@ AST_STATIC_DOC
Definition: xmldoc.h:32
char * ast_xmldoc_build_seealso(const char *type, const char *name, const char *module)
Parse the <see-also> node content.
Definition: xmldoc.c:1707
char * ast_xmldoc_printable(const char *bwinput, int withcolors)
Colorize and put delimiters (instead of tags) to the xmldoc output.
Definition: xmldoc.c:241
struct ast_xml_doc_item * ast_xmldoc_build_final_response(const char *type, const char *name, const char *module)
Generate the [final response] tag based on type of node ('application', 'function' or 'agi') and name...
Definition: xmldoc.c:2653