Asterisk - The Open Source Telephony Project GIT-master-80b953f
Loading...
Searching...
No Matches
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
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
240
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 */
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
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
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
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
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 */
671
672/*! \brief Delete a custom hook to be called when an event is fired */
679
681{
682 return manager_enabled;
683}
684
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
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 *provided_by = ast_xmldoc_printable(S_OR(cur->provided_by, "Not available"), 1);
1121 char *since = ast_xmldoc_printable(S_OR(cur->since, "Not available"), 1);
1122 char *description = ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1);
1123 char *syntax = ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1);
1124 char *arguments = ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1);
1125 char *privilege = ast_xmldoc_printable(S_OR(auth_str, "Not available"), 1);
1126 char *seealso = ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1);
1127 char *responses = ast_xmldoc_printable("None", 1);
1128
1129 if (!synopsis || !provided_by || !since || !description || !syntax || !arguments
1130 || !privilege || !seealso || !responses) {
1132 ast_free(provided_by);
1133 ast_free(since);
1134 ast_free(description);
1135 ast_free(syntax);
1136 ast_free(arguments);
1137 ast_free(privilege);
1138 ast_free(seealso);
1139 ast_free(responses);
1140 ast_cli(a->fd, "Allocation failure.\n");
1142
1143 return CLI_FAILURE;
1144 }
1145
1146 ast_cli(a->fd, "\n"
1147 "%s -= Info about Manager Command '%s' =- %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"
1161 "%s\n\n"
1162 COLORIZE_FMT "\n"
1163 "%s\n\n"
1164 COLORIZE_FMT "\n",
1166 COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
1167 COLORIZE(COLOR_MAGENTA, 0, "[Provided By]"), provided_by,
1168 COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
1169 COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
1170 COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
1171 COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
1172 COLORIZE(COLOR_MAGENTA, 0, "[Privilege]"), privilege,
1173 COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso,
1174 COLORIZE(COLOR_MAGENTA, 0, "[List Responses]")
1175 );
1176
1177 if (!cur->list_responses) {
1178 ast_cli(a->fd, "%s\n\n", responses);
1179 } else {
1180 struct ast_xml_doc_item *temp;
1181 for (temp = cur->list_responses; temp; temp = AST_LIST_NEXT(temp, next)) {
1182 ast_cli(a->fd, "Event: %s\n", temp->name);
1183 print_event_instance(a, temp);
1184 }
1185 }
1186 ast_cli(a->fd,
1187 COLORIZE_FMT "\n",
1188 COLORIZE(COLOR_MAGENTA, 0, "[End List Responses]")
1189 );
1190
1191 ast_cli(a->fd, "\n"
1192 COLORIZE_FMT "\n",
1193 COLORIZE(COLOR_MAGENTA, 0, "[Final Response]")
1194 );
1195 if (!cur->final_response) {
1196 ast_cli(a->fd, "%s\n\n", responses);
1197 } else {
1198 ast_cli(a->fd, "Event: %s\n", cur->final_response->name);
1200 }
1201 ast_cli(a->fd,
1202 COLORIZE_FMT "\n",
1203 COLORIZE(COLOR_MAGENTA, 0, "[End Final Response]")
1204 );
1205
1208 ast_free(since);
1212 ast_free(privilege);
1214 ast_free(responses);
1215 } else
1216#endif
1217 {
1218 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
1219 cur->action, cur->synopsis,
1220 auth_str,
1221 S_OR(cur->description, ""));
1222 }
1223 }
1224 }
1225 }
1227
1228 return CLI_SUCCESS;
1229}
1230
1231static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1232{
1233 switch (cmd) {
1234 case CLI_INIT:
1235 e->command = "manager set debug [on|off]";
1236 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
1237 return NULL;
1238 case CLI_GENERATE:
1239 return NULL;
1240 }
1241
1242 if (a->argc == 3) {
1243 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
1244 } else if (a->argc == 4) {
1245 if (!strcasecmp(a->argv[3], "on")) {
1246 manager_debug = 1;
1247 } else if (!strcasecmp(a->argv[3], "off")) {
1248 manager_debug = 0;
1249 } else {
1250 return CLI_SHOWUSAGE;
1251 }
1252 }
1253 return CLI_SUCCESS;
1254}
1255
1256static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1257{
1258 struct ast_manager_user *user = NULL;
1259 int l;
1260 struct ast_str *rauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
1261 struct ast_str *wauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
1262 struct ast_variable *v;
1263
1264 switch (cmd) {
1265 case CLI_INIT:
1266 e->command = "manager show user";
1267 e->usage =
1268 " Usage: manager show user <user>\n"
1269 " Display all information related to the manager user specified.\n";
1270 return NULL;
1271 case CLI_GENERATE:
1272 l = strlen(a->word);
1273 if (a->pos != 3) {
1274 return NULL;
1275 }
1277 AST_RWLIST_TRAVERSE(&users, user, list) {
1278 if (!strncasecmp(a->word, user->username, l)) {
1279 if (ast_cli_completion_add(ast_strdup(user->username))) {
1280 break;
1281 }
1282 }
1283 }
1285 return NULL;
1286 }
1287
1288 if (a->argc != 4) {
1289 return CLI_SHOWUSAGE;
1290 }
1291
1293
1294 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
1295 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
1297 return CLI_SUCCESS;
1298 }
1299
1300 ast_cli(a->fd, "\n");
1301 ast_cli(a->fd,
1302 " username: %s\n"
1303 " secret: %s\n"
1304 " ACL: %s\n"
1305 " read perm: %s\n"
1306 " write perm: %s\n"
1307 " displayconnects: %s\n"
1308 "allowmultiplelogin: %s\n",
1309 S_OR(user->username, "(N/A)"),
1310 (user->secret ? "<Set>" : "(N/A)"),
1311 ((user->acl && !ast_acl_list_is_empty(user->acl)) ? "yes" : "no"),
1312 user_authority_to_str(user->readperm, &rauthority),
1313 user_authority_to_str(user->writeperm, &wauthority),
1314 (user->displayconnects ? "yes" : "no"),
1315 (user->allowmultiplelogin ? "yes" : "no"));
1316 ast_cli(a->fd, " Variables: \n");
1317 for (v = user->chanvars ; v ; v = v->next) {
1318 ast_cli(a->fd, " %s = %s\n", v->name, v->value);
1319 }
1320 if (!ast_acl_list_is_empty(user->acl)) {
1321 ast_acl_output(a->fd, user->acl, NULL);
1322 }
1323
1325
1326 return CLI_SUCCESS;
1327}
1328
1329static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1330{
1331 struct ast_manager_user *user = NULL;
1332 int count_amu = 0;
1333 switch (cmd) {
1334 case CLI_INIT:
1335 e->command = "manager show users";
1336 e->usage =
1337 "Usage: manager show users\n"
1338 " Prints a listing of all managers that are currently configured on that\n"
1339 " system.\n";
1340 return NULL;
1341 case CLI_GENERATE:
1342 return NULL;
1343 }
1344 if (a->argc != 3) {
1345 return CLI_SHOWUSAGE;
1346 }
1347
1349
1350 /* If there are no users, print out something along those lines */
1351 if (AST_RWLIST_EMPTY(&users)) {
1352 ast_cli(a->fd, "There are no manager users.\n");
1354 return CLI_SUCCESS;
1355 }
1356
1357 ast_cli(a->fd, "\nusername\n--------\n");
1358
1360 ast_cli(a->fd, "%s\n", user->username);
1361 count_amu++;
1362 }
1363
1365
1366 ast_cli(a->fd,"-------------------\n"
1367 "%d manager users configured.\n", count_amu);
1368 return CLI_SUCCESS;
1369}
1370
1371/*! \brief CLI command manager list commands */
1372static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1373{
1374 struct manager_action *cur;
1375 int name_len = 1;
1376 int space_remaining;
1377#define HSMC_FORMAT " %-*.*s %-.*s\n"
1378 switch (cmd) {
1379 case CLI_INIT:
1380 e->command = "manager show commands";
1381 e->usage =
1382 "Usage: manager show commands\n"
1383 " Prints a listing of all the available Asterisk manager interface commands.\n";
1384 return NULL;
1385 case CLI_GENERATE:
1386 return NULL;
1387 }
1388
1391 int incoming_len = strlen(cur->action);
1392 if (incoming_len > name_len) {
1393 name_len = incoming_len;
1394 }
1395 }
1396
1397 space_remaining = MGR_SHOW_TERMINAL_WIDTH - name_len - 4;
1398 if (space_remaining < 0) {
1399 space_remaining = 0;
1400 }
1401
1402 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "Action", space_remaining, "Synopsis");
1403 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "------", space_remaining, "--------");
1404
1406 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, cur->action, space_remaining, cur->synopsis);
1407 }
1409
1410 return CLI_SUCCESS;
1411}
1412
1413/*! \brief CLI command manager kick session */
1414static char *handle_kickmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1415{
1416 struct ao2_container *sessions;
1418 struct ao2_iterator i;
1419 int fd = -1;
1420 int found = 0;
1421
1422 switch (cmd) {
1423 case CLI_INIT:
1424 e->command = "manager kick session";
1425 e->usage =
1426 "Usage: manager kick session <file descriptor>\n"
1427 " Kick an active Asterisk Manager Interface session\n";
1428 return NULL;
1429 case CLI_GENERATE:
1430 return NULL;
1431 }
1432
1433 if (a->argc != 4) {
1434 return CLI_SHOWUSAGE;
1435 }
1436
1437 fd = atoi(a->argv[3]);
1438 if (fd <= 0) { /* STDOUT won't be a valid AMI fd either */
1439 ast_cli(a->fd, "Invalid AMI file descriptor: %s\n", a->argv[3]);
1440 return CLI_FAILURE;
1441 }
1442
1443 sessions = ao2_global_obj_ref(mgr_sessions);
1444 if (sessions) {
1446 ao2_ref(sessions, -1);
1447 while ((session = ao2_iterator_next(&i))) {
1449 if (session->stream) {
1450 if (ast_iostream_get_fd(session->stream) == fd) {
1451 if (session->kicked) {
1452 ast_cli(a->fd, "Manager session using file descriptor %d has already been kicked\n", fd);
1455 break;
1456 }
1457 fd = ast_iostream_get_fd(session->stream);
1458 found = fd;
1459 ast_cli(a->fd, "Kicking manager session connected using file descriptor %d\n", fd);
1460 ast_mutex_lock(&session->notify_lock);
1461 session->kicked = 1;
1462 if (session->waiting_thread != AST_PTHREADT_NULL) {
1463 pthread_kill(session->waiting_thread, SIGURG);
1464 }
1465 ast_mutex_unlock(&session->notify_lock);
1468 break;
1469 }
1470 }
1473 }
1475 }
1476
1477 if (!found) {
1478 ast_cli(a->fd, "No manager session found using file descriptor %d\n", fd);
1479 }
1480 return CLI_SUCCESS;
1481}
1482
1483/*! \brief CLI command manager list connected */
1484static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1485{
1486 struct ao2_container *sessions;
1488 time_t now = time(NULL);
1489#define HSMCONN_FORMAT1 " %-15.15s %-55.55s %-10.10s %-10.10s %-8.8s %-8.8s %-10.10s %-10.10s\n"
1490#define HSMCONN_FORMAT2 " %-15.15s %-55.55s %-10d %-10d %-8d %-8d %-10.10d %-10.10d\n"
1491 int count = 0;
1492 struct ao2_iterator i;
1493
1494 switch (cmd) {
1495 case CLI_INIT:
1496 e->command = "manager show connected";
1497 e->usage =
1498 "Usage: manager show connected\n"
1499 " Prints a listing of the users that are currently connected to the\n"
1500 "Asterisk manager interface.\n";
1501 return NULL;
1502 case CLI_GENERATE:
1503 return NULL;
1504 }
1505
1506 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "ReadPerms", "WritePerms");
1507
1508 sessions = ao2_global_obj_ref(mgr_sessions);
1509 if (sessions) {
1511 ao2_ref(sessions, -1);
1512 while ((session = ao2_iterator_next(&i))) {
1514 ast_cli(a->fd, HSMCONN_FORMAT2, session->username,
1516 (int) (session->sessionstart),
1517 (int) (now - session->sessionstart),
1518 session->stream ? ast_iostream_get_fd(session->stream) : -1,
1519 session->inuse,
1520 session->readperm,
1521 session->writeperm);
1522 count++;
1525 }
1527 }
1528 ast_cli(a->fd, "%d users connected.\n", count);
1529
1530 return CLI_SUCCESS;
1531}
1532
1533/*! \brief CLI command manager list eventq */
1534/* Should change to "manager show connected" */
1535static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1536{
1537 struct eventqent *s;
1538 switch (cmd) {
1539 case CLI_INIT:
1540 e->command = "manager show eventq";
1541 e->usage =
1542 "Usage: manager show eventq\n"
1543 " Prints a listing of all events pending in the Asterisk manger\n"
1544 "event queue.\n";
1545 return NULL;
1546 case CLI_GENERATE:
1547 return NULL;
1548 }
1551 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
1552 ast_cli(a->fd, "Category: %d\n", s->category);
1553 ast_cli(a->fd, "Event:\n%s", s->eventdata);
1554 }
1556
1557 return CLI_SUCCESS;
1558}
1559
1560static int reload_module(void);
1561
1562/*! \brief CLI command manager reload */
1563static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1564{
1565 switch (cmd) {
1566 case CLI_INIT:
1567 e->command = "manager reload";
1568 e->usage =
1569 "Usage: manager reload\n"
1570 " Reloads the manager configuration.\n";
1571 return NULL;
1572 case CLI_GENERATE:
1573 return NULL;
1574 }
1575 if (a->argc > 2) {
1576 return CLI_SHOWUSAGE;
1577 }
1578 reload_module();
1579 return CLI_SUCCESS;
1580}
1581
1582static struct eventqent *advance_event(struct eventqent *e)
1583{
1584 struct eventqent *next;
1585
1587 if ((next = AST_RWLIST_NEXT(e, eq_next))) {
1590 }
1592 return next;
1593}
1594
1595#define GET_HEADER_FIRST_MATCH 0
1596#define GET_HEADER_LAST_MATCH 1
1597#define GET_HEADER_SKIP_EMPTY 2
1598
1599/*!
1600 * \brief Return a matching header value.
1601 *
1602 * \details
1603 * Generic function to return either the first or the last
1604 * matching header from a list of variables, possibly skipping
1605 * empty strings.
1606 *
1607 * \note At the moment there is only one use of this function in
1608 * this file, so we make it static.
1609 *
1610 * \note Never returns NULL.
1611 */
1612static const char *__astman_get_header(const struct message *m, char *var, int mode)
1613{
1614 int x, l = strlen(var);
1615 const char *result = "";
1616
1617 if (!m) {
1618 return result;
1619 }
1620
1621 for (x = 0; x < m->hdrcount; x++) {
1622 const char *h = m->headers[x];
1623 if (!strncasecmp(var, h, l) && h[l] == ':') {
1624 const char *value = h + l + 1;
1625 value = ast_skip_blanks(value); /* ignore leading spaces in the value */
1626 /* found a potential candidate */
1627 if ((mode & GET_HEADER_SKIP_EMPTY) && ast_strlen_zero(value)) {
1628 continue; /* not interesting */
1629 }
1630 if (mode & GET_HEADER_LAST_MATCH) {
1631 result = value; /* record the last match so far */
1632 } else {
1633 return value;
1634 }
1635 }
1636 }
1637
1638 return result;
1639}
1640
1641/*!
1642 * \brief Return the first matching variable from an array.
1643 *
1644 * \note This is the legacy function and is implemented in
1645 * therms of __astman_get_header().
1646 *
1647 * \note Never returns NULL.
1648 */
1649const char *astman_get_header(const struct message *m, char *var)
1650{
1652}
1653
1654/*!
1655 * \brief Append additional headers into the message structure from params.
1656 *
1657 * \note You likely want to initialize m->hdrcount to 0 before calling this.
1658 */
1659static void astman_append_headers(struct message *m, const struct ast_variable *params)
1660{
1661 const struct ast_variable *v;
1662
1663 for (v = params; v && m->hdrcount < ARRAY_LEN(m->headers); v = v->next) {
1664 if (ast_asprintf((char**)&m->headers[m->hdrcount], "%s: %s", v->name, v->value) > -1) {
1665 ++m->hdrcount;
1666 }
1667 }
1668}
1669
1670/*!
1671 * \brief Free headers inside message structure, but not the message structure itself.
1672 */
1673static void astman_free_headers(struct message *m)
1674{
1675 while (m->hdrcount) {
1676 --m->hdrcount;
1677 ast_free((void *) m->headers[m->hdrcount]);
1678 m->headers[m->hdrcount] = NULL;
1679 }
1680}
1681
1682/*!
1683 * \internal
1684 * \brief Process one "Variable:" header value string.
1685 *
1686 * \param head Current list of AMI variables to get new values added.
1687 * \param hdr_val Header value string to process.
1688 *
1689 * \return New variable list head.
1690 */
1691static struct ast_variable *man_do_variable_value(struct ast_variable *head, const char *hdr_val)
1692{
1693 char *parse;
1695 AST_APP_ARG(vars)[64];
1696 );
1697
1698 hdr_val = ast_skip_blanks(hdr_val); /* ignore leading spaces in the value */
1699 parse = ast_strdupa(hdr_val);
1700
1701 /* Break the header value string into name=val pair items. */
1703 if (args.argc) {
1704 int y;
1705
1706 /* Process each name=val pair item. */
1707 for (y = 0; y < args.argc; y++) {
1708 struct ast_variable *cur;
1709 char *var;
1710 char *val;
1711
1712 if (!args.vars[y]) {
1713 continue;
1714 }
1715 var = val = args.vars[y];
1716 strsep(&val, "=");
1717
1718 /* XXX We may wish to trim whitespace from the strings. */
1719 if (!val || ast_strlen_zero(var)) {
1720 continue;
1721 }
1722
1723 /* Create new variable list node and prepend it to the list. */
1724 cur = ast_variable_new(var, val, "");
1725 if (cur) {
1726 cur->next = head;
1727 head = cur;
1728 }
1729 }
1730 }
1731
1732 return head;
1733}
1734
1736{
1738}
1739
1742{
1743 int varlen;
1744 int x;
1745 struct ast_variable *head = NULL;
1746
1747 static const char var_hdr[] = "Variable:";
1748
1749 /* Process all "Variable:" headers. */
1750 varlen = strlen(var_hdr);
1751 for (x = 0; x < m->hdrcount; x++) {
1752 if (strncasecmp(var_hdr, m->headers[x], varlen)) {
1753 continue;
1754 }
1755 head = man_do_variable_value(head, m->headers[x] + varlen);
1756 }
1757
1758 if (order == ORDER_NATURAL) {
1759 head = ast_variables_reverse(head);
1760 }
1761
1762 return head;
1763}
1764
1765/*! \brief access for hooks to send action messages to ami */
1766int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
1767{
1768 const char *action;
1769 int ret = 0;
1770 struct manager_action *act_found;
1771 struct mansession s = {.session = NULL, };
1772 struct message m = { 0 };
1773 char *dup_str;
1774 char *src;
1775 int x = 0;
1776 int curlen;
1777
1778 if (hook == NULL) {
1779 return -1;
1780 }
1781
1782 /* Create our own copy of the AMI action msg string. */
1783 src = dup_str = ast_strdup(msg);
1784 if (!dup_str) {
1785 return -1;
1786 }
1787
1788 /* convert msg string to message struct */
1789 curlen = strlen(src);
1790 for (x = 0; x < curlen; x++) {
1791 int cr; /* set if we have \r */
1792 if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n')
1793 cr = 2; /* Found. Update length to include \r\n */
1794 else if (src[x] == '\n')
1795 cr = 1; /* also accept \n only */
1796 else
1797 continue;
1798 /* don't keep empty lines */
1799 if (x && m.hdrcount < ARRAY_LEN(m.headers)) {
1800 /* ... but trim \r\n and terminate the header string */
1801 src[x] = '\0';
1802 m.headers[m.hdrcount++] = src;
1803 }
1804 x += cr;
1805 curlen -= x; /* remaining size */
1806 src += x; /* update pointer */
1807 x = -1; /* reset loop */
1808 }
1809
1810 action = astman_get_header(&m, "Action");
1811
1812 do {
1813 if (!strcasecmp(action, "login")) {
1814 break;
1815 }
1816
1817 act_found = action_find(action);
1818 if (!act_found) {
1819 break;
1820 }
1821
1822 /*
1823 * we have to simulate a session for this action request
1824 * to be able to pass it down for processing
1825 * This is necessary to meet the previous design of manager.c
1826 */
1827 s.hook = hook;
1828
1829 ret = -1;
1830 ao2_lock(act_found);
1831 if (act_found->registered && act_found->func) {
1832 struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
1833
1834 ao2_unlock(act_found);
1835 /* If the action is in a module it must be running. */
1836 if (!act_found->module || mod_ref) {
1837 ret = act_found->func(&s, &m);
1838 ast_module_unref(mod_ref);
1839 }
1840 } else {
1841 ao2_unlock(act_found);
1842 }
1843 ao2_t_ref(act_found, -1, "done with found action object");
1844 } while (0);
1845
1846 ast_free(dup_str);
1847 return ret;
1848}
1849
1850/*!
1851 * helper function to send a string to the socket.
1852 * Return -1 on error (e.g. buffer full).
1853 */
1854static int send_string(struct mansession *s, char *string)
1855{
1856 struct ast_iostream *stream;
1857 int len, res;
1858
1859 /* It's a result from one of the hook's action invocation */
1860 if (s->hook) {
1861 /*
1862 * to send responses, we're using the same function
1863 * as for receiving events. We call the event "HookResponse"
1864 */
1865 s->hook->helper(EVENT_FLAG_HOOKRESPONSE, "HookResponse", string);
1866 return 0;
1867 }
1868
1869 stream = s->stream ? s->stream : s->session->stream;
1870
1871 len = strlen(string);
1873 res = ast_iostream_write(stream, string, len);
1875
1876 if (res < len) {
1877 s->write_error = 1;
1878 }
1879
1880 return res;
1881}
1882
1883/*!
1884 * \brief thread local buffer for astman_append
1885 *
1886 * \note This can not be defined within the astman_append() function
1887 * because it declares a couple of functions that get used to
1888 * initialize the thread local storage key.
1889 */
1891
1893
1894/*! \brief initial allocated size for the astman_append_buf and astman_send_*_va */
1895#define ASTMAN_APPEND_BUF_INITSIZE 256
1896
1897static void astman_flush(struct mansession *s, struct ast_str *buf)
1898{
1899 if (s->hook || (s->tcptls_session && s->tcptls_session->stream)) {
1901 } else {
1902 ast_verbose("No connection stream in astman_append, should not happen\n");
1903 }
1904}
1905
1906/*!
1907 * utility functions for creating AMI replies
1908 */
1909void astman_append(struct mansession *s, const char *fmt, ...)
1910{
1911 int res;
1912 va_list ap;
1913 struct ast_str *buf;
1914
1915 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
1916 return;
1917 }
1918
1919 va_start(ap, fmt);
1920 res = ast_str_set_va(&buf, 0, fmt, ap);
1921 va_end(ap);
1922 if (res == AST_DYNSTR_BUILD_FAILED) {
1923 return;
1924 }
1925
1926 if (s->hook || (s->tcptls_session != NULL && s->tcptls_session->stream != NULL)) {
1928 } else {
1929 ast_verbose("No connection stream in astman_append, should not happen\n");
1930 }
1931}
1932
1933/*! \note NOTE: XXX this comment is unclear and possibly wrong.
1934 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
1935 hold the session lock _or_ be running in an action callback (in which case s->session->busy will
1936 be non-zero). In either of these cases, there is no need to lock-protect the session's
1937 fd, since no other output will be sent (events will be queued), and no input will
1938 be read until either the current action finishes or get_input() obtains the session
1939 lock.
1940 */
1941
1942/*! \todo XXX MSG_MOREDATA should go to a header file. */
1943#define MSG_MOREDATA ((char *)astman_send_response)
1944
1945/*! \brief send a response with an optional message,
1946 * and terminate it with an empty line.
1947 * m is used only to grab the 'ActionID' field.
1948 *
1949 * Use the explicit constant MSG_MOREDATA to remove the empty line.
1950 * XXX MSG_MOREDATA should go to a header file.
1951 */
1952static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
1953{
1954 const char *id = astman_get_header(m, "ActionID");
1955 struct ast_str *buf;
1956
1957 buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE);
1958 if (!buf) {
1959 return;
1960 }
1961
1962 ast_str_set(&buf, 0, "Response: %s\r\n", resp);
1963
1964 if (!ast_strlen_zero(id)) {
1965 ast_str_append(&buf, 0, "ActionID: %s\r\n", id);
1966 }
1967
1968 if (listflag) {
1969 /* Start, complete, cancelled */
1970 ast_str_append(&buf, 0, "EventList: %s\r\n", listflag);
1971 }
1972
1973 if (msg != MSG_MOREDATA) {
1974 if (msg) {
1975 ast_str_append(&buf, 0, "Message: %s\r\n", msg);
1976 }
1977 ast_str_append(&buf, 0, "\r\n");
1978 }
1979
1980 astman_flush(s, buf);
1981}
1982
1983void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
1984{
1985 astman_send_response_full(s, m, resp, msg, NULL);
1986}
1987
1988void astman_send_error(struct mansession *s, const struct message *m, char *error)
1989{
1990 astman_send_response_full(s, m, "Error", error, NULL);
1991}
1992
1993void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt, ...)
1994{
1995 int res;
1996 va_list ap;
1997 struct ast_str *buf;
1998 char *msg;
1999
2000 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
2001 return;
2002 }
2003
2004 va_start(ap, fmt);
2005 res = ast_str_set_va(&buf, 0, fmt, ap);
2006 va_end(ap);
2007 if (res == AST_DYNSTR_BUILD_FAILED) {
2008 return;
2009 }
2010
2011 /* astman_append will use the same underlying buffer, so copy the message out
2012 * before sending the response */
2013 msg = ast_str_buffer(buf);
2014 if (msg) {
2015 msg = ast_strdupa(msg);
2016 }
2017 astman_send_response_full(s, m, "Error", msg, NULL);
2018}
2019
2020void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
2021{
2022 astman_send_response_full(s, m, "Success", msg, NULL);
2023}
2024
2025static void astman_start_ack(struct mansession *s, const struct message *m)
2026{
2027 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
2028}
2029
2030void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
2031{
2032 astman_send_response_full(s, m, "Success", msg, listflag);
2033}
2034
2035static struct ast_str *astman_send_list_complete_start_common(struct mansession *s, const struct message *m, const char *event_name, int count)
2036{
2037 const char *id = astman_get_header(m, "ActionID");
2038 struct ast_str *buf;
2039
2040 buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE);
2041 if (!buf) {
2042 return NULL;
2043 }
2044
2045 ast_str_set(&buf, 0, "Event: %s\r\n", event_name);
2046 if (!ast_strlen_zero(id)) {
2047 ast_str_append(&buf, 0, "ActionID: %s\r\n", id);
2048 }
2049 ast_str_append(&buf, 0,
2050 "EventList: Complete\r\n"
2051 "ListItems: %d\r\n",
2052 count);
2053
2054 return buf;
2055}
2056
2057static void astman_send_list_complete(struct mansession *s, const struct message *m, const char *event_name, int count)
2058{
2059 struct ast_str *buf = astman_send_list_complete_start_common(s, m, event_name, count);
2060 if (buf) {
2061 ast_str_append(&buf, 0, "\r\n");
2062 astman_flush(s, buf);
2063 }
2064}
2065
2066void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
2067{
2068 struct ast_str *buf = astman_send_list_complete_start_common(s, m, event_name, count);
2069 if (buf) {
2070 astman_flush(s, buf);
2071 }
2072}
2073
2075{
2076 astman_append(s, "\r\n");
2077}
2078
2079/*! \brief Lock the 'mansession' structure. */
2080static void mansession_lock(struct mansession *s)
2081{
2082 ast_mutex_lock(&s->lock);
2083}
2084
2085/*! \brief Unlock the 'mansession' structure. */
2086static void mansession_unlock(struct mansession *s)
2087{
2089}
2090
2091/*! \brief
2092 Rather than braindead on,off this now can also accept a specific int mask value
2093 or a ',' delim list of mask strings (the same as manager.conf) -anthm
2094*/
2095static int set_eventmask(struct mansession *s, const char *eventmask)
2096{
2097 int maskint = strings_to_mask(eventmask);
2098
2099 ao2_lock(s->session);
2100 if (maskint >= 0) {
2101 s->session->send_events = maskint;
2102 }
2103 ao2_unlock(s->session);
2104
2105 return maskint;
2106}
2107
2109{
2112}
2113
2114static void report_invalid_user(const struct mansession *s, const char *username)
2115{
2116 char session_id[32];
2117 struct ast_security_event_inval_acct_id inval_acct_id = {
2120 .common.service = "AMI",
2121 .common.account_id = username,
2122 .common.session_tv = &s->session->sessionstart_tv,
2123 .common.local_addr = {
2124 .addr = &s->tcptls_session->parent->local_address,
2125 .transport = mansession_get_transport(s),
2126 },
2127 .common.remote_addr = {
2128 .addr = &s->session->addr,
2129 .transport = mansession_get_transport(s),
2130 },
2131 .common.session_id = session_id,
2132 };
2133
2134 snprintf(session_id, sizeof(session_id), "%p", s);
2135
2136 ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
2137}
2138
2139static void report_failed_acl(const struct mansession *s, const char *username)
2140{
2141 char session_id[32];
2142 struct ast_security_event_failed_acl failed_acl_event = {
2144 .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
2145 .common.service = "AMI",
2146 .common.account_id = username,
2147 .common.session_tv = &s->session->sessionstart_tv,
2148 .common.local_addr = {
2149 .addr = &s->tcptls_session->parent->local_address,
2150 .transport = mansession_get_transport(s),
2151 },
2152 .common.remote_addr = {
2153 .addr = &s->session->addr,
2154 .transport = mansession_get_transport(s),
2155 },
2156 .common.session_id = session_id,
2157 };
2158
2159 snprintf(session_id, sizeof(session_id), "%p", s->session);
2160
2161 ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
2162}
2163
2164static void report_inval_password(const struct mansession *s, const char *username)
2165{
2166 char session_id[32];
2167 struct ast_security_event_inval_password inval_password = {
2170 .common.service = "AMI",
2171 .common.account_id = username,
2172 .common.session_tv = &s->session->sessionstart_tv,
2173 .common.local_addr = {
2174 .addr = &s->tcptls_session->parent->local_address,
2175 .transport = mansession_get_transport(s),
2176 },
2177 .common.remote_addr = {
2178 .addr = &s->session->addr,
2179 .transport = mansession_get_transport(s),
2180 },
2181 .common.session_id = session_id,
2182 };
2183
2184 snprintf(session_id, sizeof(session_id), "%p", s->session);
2185
2186 ast_security_event_report(AST_SEC_EVT(&inval_password));
2187}
2188
2189static void report_auth_success(const struct mansession *s)
2190{
2191 char session_id[32];
2192 struct ast_security_event_successful_auth successful_auth = {
2195 .common.service = "AMI",
2196 .common.account_id = s->session->username,
2197 .common.session_tv = &s->session->sessionstart_tv,
2198 .common.local_addr = {
2199 .addr = &s->tcptls_session->parent->local_address,
2200 .transport = mansession_get_transport(s),
2201 },
2202 .common.remote_addr = {
2203 .addr = &s->session->addr,
2204 .transport = mansession_get_transport(s),
2205 },
2206 .common.session_id = session_id,
2207 };
2208
2209 snprintf(session_id, sizeof(session_id), "%p", s->session);
2210
2211 ast_security_event_report(AST_SEC_EVT(&successful_auth));
2212}
2213
2214static void report_req_not_allowed(const struct mansession *s, const char *action)
2215{
2216 char session_id[32];
2217 char request_type[64];
2218 struct ast_security_event_req_not_allowed req_not_allowed = {
2221 .common.service = "AMI",
2222 .common.account_id = s->session->username,
2223 .common.session_tv = &s->session->sessionstart_tv,
2224 .common.local_addr = {
2225 .addr = &s->tcptls_session->parent->local_address,
2226 .transport = mansession_get_transport(s),
2227 },
2228 .common.remote_addr = {
2229 .addr = &s->session->addr,
2230 .transport = mansession_get_transport(s),
2231 },
2232 .common.session_id = session_id,
2233
2234 .request_type = request_type,
2235 };
2236
2237 snprintf(session_id, sizeof(session_id), "%p", s->session);
2238 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2239
2240 ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
2241}
2242
2243static void report_req_bad_format(const struct mansession *s, const char *action)
2244{
2245 char session_id[32];
2246 char request_type[64];
2247 struct ast_security_event_req_bad_format req_bad_format = {
2250 .common.service = "AMI",
2251 .common.account_id = s->session->username,
2252 .common.session_tv = &s->session->sessionstart_tv,
2253 .common.local_addr = {
2254 .addr = &s->tcptls_session->parent->local_address,
2255 .transport = mansession_get_transport(s),
2256 },
2257 .common.remote_addr = {
2258 .addr = &s->session->addr,
2259 .transport = mansession_get_transport(s),
2260 },
2261 .common.session_id = session_id,
2262
2263 .request_type = request_type,
2264 };
2265
2266 snprintf(session_id, sizeof(session_id), "%p", s->session);
2267 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2268
2269 ast_security_event_report(AST_SEC_EVT(&req_bad_format));
2270}
2271
2273 const char *response, const char *expected_response)
2274{
2275 char session_id[32];
2276 struct ast_security_event_chal_resp_failed chal_resp_failed = {
2279 .common.service = "AMI",
2280 .common.account_id = s->session->username,
2281 .common.session_tv = &s->session->sessionstart_tv,
2282 .common.local_addr = {
2283 .addr = &s->tcptls_session->parent->local_address,
2284 .transport = mansession_get_transport(s),
2285 },
2286 .common.remote_addr = {
2287 .addr = &s->session->addr,
2288 .transport = mansession_get_transport(s),
2289 },
2290 .common.session_id = session_id,
2291
2292 .challenge = s->session->challenge,
2293 .response = response,
2294 .expected_response = expected_response,
2295 };
2296
2297 snprintf(session_id, sizeof(session_id), "%p", s->session);
2298
2299 ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
2300}
2301
2302static void report_session_limit(const struct mansession *s)
2303{
2304 char session_id[32];
2308 .common.service = "AMI",
2309 .common.account_id = s->session->username,
2310 .common.session_tv = &s->session->sessionstart_tv,
2311 .common.local_addr = {
2312 .addr = &s->tcptls_session->parent->local_address,
2313 .transport = mansession_get_transport(s),
2314 },
2315 .common.remote_addr = {
2316 .addr = &s->session->addr,
2317 .transport = mansession_get_transport(s),
2318 },
2319 .common.session_id = session_id,
2320 };
2321
2322 snprintf(session_id, sizeof(session_id), "%p", s->session);
2323
2325}
2326
2327/*
2328 * Here we start with action_ handlers for AMI actions,
2329 * and the internal functions used by them.
2330 * Generally, the handlers are called action_foo()
2331 */
2332
2333/* helper function for action_login() */
2334static int authenticate(struct mansession *s, const struct message *m)
2335{
2336 const char *username = astman_get_header(m, "Username");
2337 const char *password = astman_get_header(m, "Secret");
2338 int error = -1;
2339 struct ast_manager_user *user = NULL;
2340 regex_t *regex_filter;
2341 struct ao2_iterator filter_iter;
2342
2343 if (ast_strlen_zero(username)) { /* missing username */
2344 return -1;
2345 }
2346
2347 /* locate user in locked state */
2349
2350 if (!(user = get_manager_by_name_locked(username))) {
2351 report_invalid_user(s, username);
2352 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2353 } else if (user->acl && (ast_apply_acl(user->acl, &s->session->addr, "Manager User ACL: ") == AST_SENSE_DENY)) {
2354 report_failed_acl(s, username);
2355 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2356 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
2357 const char *key = astman_get_header(m, "Key");
2358 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
2359 int x;
2360 int len = 0;
2361 char md5key[256] = "";
2362 struct MD5Context md5;
2363 unsigned char digest[16];
2364
2365 MD5Init(&md5);
2366 MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
2367 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
2368 MD5Final(digest, &md5);
2369 for (x = 0; x < 16; x++)
2370 len += sprintf(md5key + len, "%02hhx", digest[x]);
2371 if (!strcmp(md5key, key)) {
2372 error = 0;
2373 } else {
2374 report_failed_challenge_response(s, key, md5key);
2375 }
2376 } else {
2377 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
2378 S_OR(s->session->challenge, ""));
2379 }
2380 } else if (user->secret) {
2381 if (!strcmp(password, user->secret)) {
2382 error = 0;
2383 } else {
2384 report_inval_password(s, username);
2385 }
2386 }
2387
2388 if (error) {
2389 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2391 return -1;
2392 }
2393
2394 /* auth complete */
2395
2396 /* All of the user parameters are copied to the session so that in the event
2397 * of a reload and a configuration change, the session parameters are not
2398 * changed. */
2399 ast_copy_string(s->session->username, username, sizeof(s->session->username));
2400 s->session->readperm = user->readperm;
2401 s->session->writeperm = user->writeperm;
2402 s->session->writetimeout = user->writetimeout;
2403 if (user->chanvars) {
2404 s->session->chanvars = ast_variables_dup(user->chanvars);
2405 }
2406
2407 filter_iter = ao2_iterator_init(user->includefilters, 0);
2408 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
2409 ao2_t_link(s->session->includefilters, regex_filter, "add include user filter to session");
2410 ao2_t_ref(regex_filter, -1, "remove iterator ref");
2411 }
2412 ao2_iterator_destroy(&filter_iter);
2413
2414 filter_iter = ao2_iterator_init(user->excludefilters, 0);
2415 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
2416 ao2_t_link(s->session->excludefilters, regex_filter, "add exclude user filter to session");
2417 ao2_t_ref(regex_filter, -1, "remove iterator ref");
2418 }
2419 ao2_iterator_destroy(&filter_iter);
2420
2421 s->session->sessionstart = time(NULL);
2423 set_eventmask(s, astman_get_header(m, "Events"));
2424
2426
2428 return 0;
2429}
2430
2431static int action_ping(struct mansession *s, const struct message *m)
2432{
2433 const char *actionid = astman_get_header(m, "ActionID");
2434 struct timeval now = ast_tvnow();
2435
2436 astman_append(s, "Response: Success\r\n");
2437 if (!ast_strlen_zero(actionid)){
2438 astman_append(s, "ActionID: %s\r\n", actionid);
2439 }
2441 s,
2442 "Ping: Pong\r\n"
2443 "Timestamp: %ld.%06lu\r\n"
2444 "\r\n",
2445 (long) now.tv_sec, (unsigned long) now.tv_usec);
2446 return 0;
2447}
2448
2449void astman_live_dangerously(int new_live_dangerously)
2450{
2451 if (new_live_dangerously && !live_dangerously)
2452 {
2453 ast_log(LOG_WARNING, "Manager Configuration load protection disabled.\n");
2454 }
2455
2456 if (!new_live_dangerously && live_dangerously)
2457 {
2458 ast_log(LOG_NOTICE, "Manager Configuration load protection enabled.\n");
2459 }
2460 live_dangerously = new_live_dangerously;
2461}
2462
2463/**
2464 * \brief Check if a file is restricted or not
2465 *
2466 * \return 0 on success
2467 * \return 1 on restricted file
2468 * \return -1 on failure
2469 */
2470static int is_restricted_file(const char *filename)
2471{
2472 char *stripped_filename;
2473 RAII_VAR(char *, path, NULL, ast_free);
2474 RAII_VAR(char *, real_path, NULL, ast_std_free);
2475
2476 if (live_dangerously) {
2477 return 0;
2478 }
2479
2480 stripped_filename = ast_strip(ast_strdupa(filename));
2481
2482 /* If the file path starts with '/', don't prepend ast_config_AST_CONFIG_DIR */
2483 if (stripped_filename[0] == '/') {
2484 real_path = realpath(stripped_filename, NULL);
2485 } else {
2486 if (ast_asprintf(&path, "%s/%s", ast_config_AST_CONFIG_DIR, stripped_filename) == -1) {
2487 return -1;
2488 }
2489 real_path = realpath(path, NULL);
2490 }
2491
2492 if (!real_path) {
2493 return -1;
2494 }
2495
2496 if (!ast_begins_with(real_path, ast_config_AST_CONFIG_DIR)) {
2497 return 1;
2498 }
2499
2500 return 0;
2501}
2502
2503static int action_getconfig(struct mansession *s, const struct message *m)
2504{
2505 struct ast_config *cfg;
2506 const char *fn = astman_get_header(m, "Filename");
2507 const char *category = astman_get_header(m, "Category");
2508 const char *filter = astman_get_header(m, "Filter");
2509 const char *category_name;
2510 int catcount = 0;
2511 int lineno = 0;
2512 int ret = 0;
2513 struct ast_category *cur_category = NULL;
2514 struct ast_variable *v;
2515 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2516
2517 if (ast_strlen_zero(fn)) {
2518 astman_send_error(s, m, "Filename not specified");
2519 return 0;
2520 }
2521
2522 ret = is_restricted_file(fn);
2523 if (ret == 1) {
2524 astman_send_error(s, m, "File requires escalated privileges");
2525 return 0;
2526 } else if (ret == -1) {
2527 astman_send_error(s, m, "Config file not found");
2528 return 0;
2529 }
2530
2531 cfg = ast_config_load2(fn, "manager", config_flags);
2532 if (cfg == CONFIG_STATUS_FILEMISSING) {
2533 astman_send_error(s, m, "Config file not found");
2534 return 0;
2535 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2536 astman_send_error(s, m, "Config file has invalid format");
2537 return 0;
2538 }
2539
2540 astman_start_ack(s, m);
2541 while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
2542 struct ast_str *templates;
2543
2544 category_name = ast_category_get_name(cur_category);
2545 lineno = 0;
2546 astman_append(s, "Category-%06d: %s\r\n", catcount, category_name);
2547
2548 if (ast_category_is_template(cur_category)) {
2549 astman_append(s, "IsTemplate-%06d: %d\r\n", catcount, 1);
2550 }
2551
2552 if ((templates = ast_category_get_templates(cur_category))
2553 && ast_str_strlen(templates) > 0) {
2554 astman_append(s, "Templates-%06d: %s\r\n", catcount, ast_str_buffer(templates));
2556 }
2557
2558 for (v = ast_category_first(cur_category); v; v = v->next) {
2559 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
2560 }
2561
2562 catcount++;
2563 }
2564
2565 if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2566 astman_append(s, "No categories found\r\n");
2567 }
2568
2569 ast_config_destroy(cfg);
2570 astman_append(s, "\r\n");
2571
2572 return 0;
2573}
2574
2575static int action_listcategories(struct mansession *s, const struct message *m)
2576{
2577 struct ast_config *cfg;
2578 const char *fn = astman_get_header(m, "Filename");
2579 const char *match = astman_get_header(m, "Match");
2580 struct ast_category *category = NULL;
2581 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2582 int catcount = 0;
2583 int ret = 0;
2584
2585 if (ast_strlen_zero(fn)) {
2586 astman_send_error(s, m, "Filename not specified");
2587 return 0;
2588 }
2589
2590 ret = is_restricted_file(fn);
2591 if (ret == 1) {
2592 astman_send_error(s, m, "File requires escalated privileges");
2593 return 0;
2594 } else if (ret == -1) {
2595 astman_send_error(s, m, "Config file not found");
2596 return 0;
2597 }
2598
2599 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2600 astman_send_error(s, m, "Config file not found");
2601 return 0;
2602 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2603 astman_send_error(s, m, "Config file has invalid format");
2604 return 0;
2605 }
2606
2607 astman_start_ack(s, m);
2608 while ((category = ast_category_browse_filtered(cfg, NULL, category, match))) {
2609 astman_append(s, "Category-%06d: %s\r\n", catcount, ast_category_get_name(category));
2610 catcount++;
2611 }
2612
2613 if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2614 astman_append(s, "Error: no categories found\r\n");
2615 }
2616
2617 ast_config_destroy(cfg);
2618 astman_append(s, "\r\n");
2619
2620 return 0;
2621}
2622
2623/*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
2624static void json_escape(char *out, const char *in)
2625{
2626 for (; *in; in++) {
2627 if (*in == '\\' || *in == '\"') {
2628 *out++ = '\\';
2629 }
2630 *out++ = *in;
2631 }
2632 *out = '\0';
2633}
2634
2635/*!
2636 * \internal
2637 * \brief Append a JSON escaped string to the manager stream.
2638 *
2639 * \param s AMI stream to append a string.
2640 * \param str String to append to the stream after JSON escaping it.
2641 */
2642static void astman_append_json(struct mansession *s, const char *str)
2643{
2644 char *buf;
2645
2646 buf = ast_alloca(2 * strlen(str) + 1);
2648 astman_append(s, "%s", buf);
2649}
2650
2651static int action_getconfigjson(struct mansession *s, const struct message *m)
2652{
2653 struct ast_config *cfg;
2654 const char *fn = astman_get_header(m, "Filename");
2655 const char *filter = astman_get_header(m, "Filter");
2656 const char *category = astman_get_header(m, "Category");
2657 struct ast_category *cur_category = NULL;
2658 const char *category_name;
2659 struct ast_variable *v;
2660 int comma1 = 0;
2661 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2662
2663 if (ast_strlen_zero(fn)) {
2664 astman_send_error(s, m, "Filename not specified");
2665 return 0;
2666 }
2667
2668 if (is_restricted_file(fn)) {
2669 astman_send_error(s, m, "File requires escalated privileges");
2670 return 0;
2671 }
2672
2673 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2674 astman_send_error(s, m, "Config file not found");
2675 return 0;
2676 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2677 astman_send_error(s, m, "Config file has invalid format");
2678 return 0;
2679 }
2680
2681 astman_start_ack(s, m);
2682 astman_append(s, "JSON: {");
2683 while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
2684 int comma2 = 0;
2685 struct ast_str *templates;
2686
2687 category_name = ast_category_get_name(cur_category);
2688 astman_append(s, "%s\"", comma1 ? "," : "");
2689 astman_append_json(s, category_name);
2690 astman_append(s, "\":{");
2691 comma1 = 1;
2692
2693 if (ast_category_is_template(cur_category)) {
2694 astman_append(s, "\"istemplate\":1");
2695 comma2 = 1;
2696 }
2697
2698 if ((templates = ast_category_get_templates(cur_category))
2699 && ast_str_strlen(templates) > 0) {
2700 astman_append(s, "%s", comma2 ? "," : "");
2701 astman_append(s, "\"templates\":\"%s\"", ast_str_buffer(templates));
2703 comma2 = 1;
2704 }
2705
2706 for (v = ast_category_first(cur_category); v; v = v->next) {
2707 astman_append(s, "%s\"", comma2 ? "," : "");
2708 astman_append_json(s, v->name);
2709 astman_append(s, "\":\"");
2711 astman_append(s, "\"");
2712 comma2 = 1;
2713 }
2714
2715 astman_append(s, "}");
2716 }
2717 astman_append(s, "}\r\n\r\n");
2718
2719 ast_config_destroy(cfg);
2720
2721 return 0;
2722}
2723
2724/*! \brief helper function for action_updateconfig */
2725static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
2726{
2727 int x;
2728 char hdr[40];
2729 const char *action, *cat, *var, *value, *match, *line, *options;
2730 struct ast_variable *v;
2731 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
2732 enum error_type result = 0;
2733
2734 for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
2735 unsigned int object = 0;
2736 char *dupoptions;
2737 int allowdups = 0;
2738 int istemplate = 0;
2739 int ignoreerror = 0;
2740 RAII_VAR(char *, inherit, NULL, ast_free);
2741 RAII_VAR(char *, catfilter, NULL, ast_free);
2742 char *token;
2743 int foundvar = 0;
2744 int foundcat = 0;
2745 struct ast_category *category = NULL;
2746
2747 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
2748 action = astman_get_header(m, hdr);
2749 if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
2750 break; /* this could cause problems if actions come in misnumbered */
2751
2752 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
2753 cat = astman_get_header(m, hdr);
2754 if (ast_strlen_zero(cat)) { /* every action needs a category */
2756 break;
2757 }
2758
2759 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
2760 var = astman_get_header(m, hdr);
2761
2762 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
2763 value = astman_get_header(m, hdr);
2764
2765 if (!ast_strlen_zero(value) && *value == '>') {
2766 object = 1;
2767 value++;
2768 }
2769
2770 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
2771 match = astman_get_header(m, hdr);
2772
2773 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
2774 line = astman_get_header(m, hdr);
2775
2776 snprintf(hdr, sizeof(hdr), "Options-%06d", x);
2777 options = astman_get_header(m, hdr);
2778 if (!ast_strlen_zero(options)) {
2779 char copy[strlen(options) + 1];
2780 strcpy(copy, options); /* safe */
2781 dupoptions = copy;
2782 while ((token = ast_strsep(&dupoptions, ',', AST_STRSEP_STRIP))) {
2783 if (!strcasecmp("allowdups", token)) {
2784 allowdups = 1;
2785 continue;
2786 }
2787 if (!strcasecmp("template", token)) {
2788 istemplate = 1;
2789 continue;
2790 }
2791 if (!strcasecmp("ignoreerror", token)) {
2792 ignoreerror = 1;
2793 continue;
2794 }
2795 if (ast_begins_with(token, "inherit")) {
2796 char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2797 c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2798 if (c) {
2799 inherit = ast_strdup(c);
2800 }
2801 continue;
2802 }
2803 if (ast_begins_with(token, "catfilter")) {
2804 char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2805 c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2806 if (c) {
2807 catfilter = ast_strdup(c);
2808 }
2809 continue;
2810 }
2811 }
2812 }
2813
2814 if (!strcasecmp(action, "newcat")) {
2815 struct ast_category *template;
2816 char *tmpl_name = NULL;
2817
2818 if (!allowdups) {
2819 if (ast_category_get(cfg, cat, "TEMPLATES=include")) {
2820 if (ignoreerror) {
2821 continue;
2822 } else {
2823 result = FAILURE_NEWCAT; /* already exist */
2824 break;
2825 }
2826 }
2827 }
2828
2829 if (istemplate) {
2830 category = ast_category_new_template(cat, dfn, -1);
2831 } else {
2832 category = ast_category_new(cat, dfn, -1);
2833 }
2834
2835 if (!category) {
2837 break;
2838 }
2839
2840 if (inherit) {
2841 while ((tmpl_name = ast_strsep(&inherit, ',', AST_STRSEP_STRIP))) {
2842 if ((template = ast_category_get(cfg, tmpl_name, "TEMPLATES=restrict"))) {
2843 if (ast_category_inherit(category, template)) {
2845 break;
2846 }
2847 } else {
2848 ast_category_destroy(category);
2849 category = NULL;
2850 result = FAILURE_TEMPLATE; /* template not found */
2851 break;
2852 }
2853 }
2854 }
2855
2856 if (category != NULL) {
2857 if (ast_strlen_zero(match)) {
2858 ast_category_append(cfg, category);
2859 } else {
2860 if (ast_category_insert(cfg, category, match)) {
2861 ast_category_destroy(category);
2863 break;
2864 }
2865 }
2866 }
2867 } else if (!strcasecmp(action, "renamecat")) {
2868 if (ast_strlen_zero(value)) {
2870 break;
2871 }
2872
2873 foundcat = 0;
2874 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2875 ast_category_rename(category, value);
2876 foundcat = 1;
2877 }
2878
2879 if (!foundcat) {
2881 break;
2882 }
2883 } else if (!strcasecmp(action, "delcat")) {
2884 foundcat = 0;
2885 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2886 category = ast_category_delete(cfg, category);
2887 foundcat = 1;
2888 }
2889
2890 if (!foundcat && !ignoreerror) {
2892 break;
2893 }
2894 } else if (!strcasecmp(action, "emptycat")) {
2895 foundcat = 0;
2896 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2897 ast_category_empty(category);
2898 foundcat = 1;
2899 }
2900
2901 if (!foundcat) {
2903 break;
2904 }
2905 } else if (!strcasecmp(action, "update")) {
2906 if (ast_strlen_zero(var)) {
2908 break;
2909 }
2910
2911 foundcat = 0;
2912 foundvar = 0;
2913 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2914 if (!ast_variable_update(category, var, value, match, object)) {
2915 foundvar = 1;
2916 }
2917 foundcat = 1;
2918 }
2919
2920 if (!foundcat) {
2922 break;
2923 }
2924
2925 if (!foundvar) {
2927 break;
2928 }
2929 } else if (!strcasecmp(action, "delete")) {
2930 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
2932 break;
2933 }
2934
2935 foundcat = 0;
2936 foundvar = 0;
2937 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2938 if (!ast_variable_delete(category, var, match, line)) {
2939 foundvar = 1;
2940 }
2941 foundcat = 1;
2942 }
2943
2944 if (!foundcat) {
2946 break;
2947 }
2948
2949 if (!foundvar && !ignoreerror) {
2951 break;
2952 }
2953 } else if (!strcasecmp(action, "append")) {
2954 if (ast_strlen_zero(var)) {
2956 break;
2957 }
2958
2959 foundcat = 0;
2960 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2961 if (!(v = ast_variable_new(var, value, dfn))) {
2963 break;
2964 }
2965 if (object || (match && !strcasecmp(match, "object"))) {
2966 v->object = 1;
2967 }
2968 ast_variable_append(category, v);
2969 foundcat = 1;
2970 }
2971
2972 if (!foundcat) {
2974 break;
2975 }
2976 } else if (!strcasecmp(action, "insert")) {
2977 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
2979 break;
2980 }
2981
2982 foundcat = 0;
2983 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2984 if (!(v = ast_variable_new(var, value, dfn))) {
2986 break;
2987 }
2988 ast_variable_insert(category, v, line);
2989 foundcat = 1;
2990 }
2991
2992 if (!foundcat) {
2994 break;
2995 }
2996 }
2997 else {
2998 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
3000 break;
3001 }
3002 }
3003 ast_free(str1);
3004 ast_free(str2);
3005 return result;
3006}
3007
3008static int action_updateconfig(struct mansession *s, const struct message *m)
3009{
3010 struct ast_config *cfg;
3011 const char *sfn = astman_get_header(m, "SrcFilename");
3012 const char *dfn = astman_get_header(m, "DstFilename");
3013 int res;
3014 const char *rld = astman_get_header(m, "Reload");
3015 int preserve_effective_context = CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT;
3016 const char *preserve_effective_context_string = astman_get_header(m, "PreserveEffectiveContext");
3017 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
3018 enum error_type result;
3019
3020 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
3021 astman_send_error(s, m, "Filename not specified");
3022 return 0;
3023 }
3024 if (is_restricted_file(sfn) || is_restricted_file(dfn)) {
3025 astman_send_error(s, m, "File requires escalated privileges");
3026 return 0;
3027 }
3028 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
3029 astman_send_error(s, m, "Config file not found");
3030 return 0;
3031 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
3032 astman_send_error(s, m, "Config file has invalid format");
3033 return 0;
3034 }
3035 result = handle_updates(s, m, cfg, dfn);
3036 if (!result) {
3037 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
3038 if (!ast_strlen_zero(preserve_effective_context_string) && !ast_true(preserve_effective_context_string)) {
3039 preserve_effective_context = CONFIG_SAVE_FLAG_NONE;
3040 }
3041 res = ast_config_text_file_save2(dfn, cfg, "Manager", preserve_effective_context);
3042 ast_config_destroy(cfg);
3043 if (res) {
3044 astman_send_error(s, m, "Save of config failed");
3045 return 0;
3046 }
3047 astman_send_ack(s, m, NULL);
3048 if (!ast_strlen_zero(rld)) {
3049 if (ast_true(rld)) {
3050 ast_module_reload(NULL); /* Reload everything */
3051 } else if (!ast_false(rld)) {
3052 ast_module_reload(rld); /* Reload the specific module */
3053 }
3054 }
3055 } else {
3056 ast_config_destroy(cfg);
3057 switch(result) {
3058 case UNKNOWN_ACTION:
3059 astman_send_error(s, m, "Unknown action command");
3060 break;
3061 case UNKNOWN_CATEGORY:
3062 astman_send_error(s, m, "Given category does not exist");
3063 break;
3065 astman_send_error(s, m, "Category not specified");
3066 break;
3068 astman_send_error(s, m, "Problem with category, value, or line (if required)");
3069 break;
3070 case FAILURE_ALLOCATION:
3071 astman_send_error(s, m, "Memory allocation failure, this should not happen");
3072 break;
3073 case FAILURE_NEWCAT:
3074 astman_send_error(s, m, "Create category did not complete successfully");
3075 break;
3076 case FAILURE_DELCAT:
3077 astman_send_error(s, m, "Delete category did not complete successfully");
3078 break;
3079 case FAILURE_EMPTYCAT:
3080 astman_send_error(s, m, "Empty category did not complete successfully");
3081 break;
3082 case FAILURE_UPDATE:
3083 astman_send_error(s, m, "Update did not complete successfully");
3084 break;
3085 case FAILURE_DELETE:
3086 astman_send_error(s, m, "Delete did not complete successfully");
3087 break;
3088 case FAILURE_APPEND:
3089 astman_send_error(s, m, "Append did not complete successfully");
3090 break;
3091 case FAILURE_TEMPLATE:
3092 astman_send_error(s, m, "Template category not found");
3093 break;
3094 }
3095 }
3096 return 0;
3097}
3098
3099static int action_createconfig(struct mansession *s, const struct message *m)
3100{
3101 int fd;
3102 const char *fn = astman_get_header(m, "Filename");
3103 char *stripped_filename;
3104 RAII_VAR(char *, filepath, NULL, ast_free);
3105 RAII_VAR(char *, real_dir, NULL, ast_std_free);
3106 RAII_VAR(char *, real_path, NULL, ast_free);
3107 char *filename;
3108
3109 if (ast_strlen_zero(fn)) {
3110 astman_send_error(s, m, "Filename not specified");
3111 return 0;
3112 }
3113
3114 stripped_filename = ast_strip(ast_strdupa(fn));
3115
3116 /* If the file name is relative, prepend ast_config_AST_CONFIG_DIR */
3117 if (stripped_filename[0] != '/') {
3118 if (ast_asprintf(&filepath, "%s/%s", ast_config_AST_CONFIG_DIR, stripped_filename) == -1) {
3119 return -1;
3120 }
3121 } else {
3122 filepath = ast_strdup(stripped_filename);
3123 }
3124
3125 /*
3126 * We can't call is_restricted_file() here because it uses realpath() and...
3127 *
3128 * realpath() and other functions that canonicalize paths won't work with
3129 * a filename that doesn't exist, so we need to separate the directory
3130 * from the filename and canonicalize the directory first. We have to do
3131 * the separation manually because dirname() and basename() aren't all
3132 * that friendly to multi-threaded programs and there are different
3133 * versions of basename for glibc and POSIX.
3134 */
3135
3136 filename = strrchr(filepath, '/');
3137 if (!filename) {
3138 astman_send_error(s, m, "Filename is invalid");
3139 return 0;
3140 }
3141 *filename = '\0';
3142 filename++;
3143
3144 /* filepath just has the directory now so canonicalize it. */
3145 real_dir = realpath(filepath, NULL);
3146 if (ast_strlen_zero(real_dir)) {
3147 astman_send_error(s, m, strerror(errno));
3148 return 0;
3149 }
3150
3151 /* Check if the directory is restricted. */
3153 astman_send_error(s, m, "File requires escalated privileges");
3154 return 0;
3155 }
3156
3157 /* Create the final file path. */
3158 if (ast_asprintf(&real_path, "%s/%s", real_dir, filename) == -1) {
3159 astman_send_error(s, m, strerror(errno));
3160 return -1;
3161 }
3162
3163 if ((fd = open(real_path, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
3164 close(fd);
3165 astman_send_ack(s, m, "New configuration file created successfully");
3166 } else {
3167 astman_send_error(s, m, strerror(errno));
3168 }
3169
3170 return 0;
3171}
3172
3173static int action_waitevent(struct mansession *s, const struct message *m)
3174{
3175 const char *timeouts = astman_get_header(m, "Timeout");
3176 int timeout = -1;
3177 int x;
3178 int needexit = 0;
3179 const char *id = astman_get_header(m, "ActionID");
3180 char idText[256];
3181
3182 if (!ast_strlen_zero(id)) {
3183 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
3184 } else {
3185 idText[0] = '\0';
3186 }
3187
3188 if (!ast_strlen_zero(timeouts)) {
3189 sscanf(timeouts, "%30i", &timeout);
3190 if (timeout < -1) {
3191 timeout = -1;
3192 }
3193 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
3194 }
3195
3198 pthread_kill(s->session->waiting_thread, SIGURG);
3199 }
3201
3202 ao2_lock(s->session);
3203
3204 if (s->session->managerid) { /* AMI-over-HTTP session */
3205 /*
3206 * Make sure the timeout is within the expire time of the session,
3207 * as the client will likely abort the request if it does not see
3208 * data coming after some amount of time.
3209 */
3210 time_t now = time(NULL);
3211 int max = s->session->sessiontimeout - now - 10;
3212
3213 if (max < 0) { /* We are already late. Strange but possible. */
3214 max = 0;
3215 }
3216 if (timeout < 0 || timeout > max) {
3217 timeout = max;
3218 }
3219 if (!s->session->send_events) { /* make sure we record events */
3220 s->session->send_events = -1;
3221 }
3222 }
3223 ao2_unlock(s->session);
3224
3226 s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
3228 ast_debug(1, "Starting waiting for an event!\n");
3229
3230 for (x = 0; x < timeout || timeout < 0; x++) {
3231 ao2_lock(s->session);
3232 if (AST_RWLIST_NEXT(s->session->last_ev, eq_next)) {
3233 needexit = 1;
3234 }
3235 if (s->session->needdestroy) {
3236 needexit = 1;
3237 }
3238 ao2_unlock(s->session);
3239 /* We can have multiple HTTP session point to the same mansession entry.
3240 * The way we deal with it is not very nice: newcomers kick out the previous
3241 * HTTP session. XXX this needs to be improved.
3242 */
3244 if (s->session->waiting_thread != pthread_self()) {
3245 needexit = 1;
3246 }
3248 if (needexit) {
3249 break;
3250 }
3251 if (s->session->managerid == 0) { /* AMI session */
3253 break;
3254 }
3255 } else { /* HTTP session */
3256 sleep(1);
3257 }
3258 }
3259 ast_debug(1, "Finished waiting for an event!\n");
3260
3262 if (s->session->waiting_thread == pthread_self()) {
3263 struct eventqent *eqe = s->session->last_ev;
3264
3267
3268 ao2_lock(s->session);
3269 astman_send_response(s, m, "Success", "Waiting for Event completed.");
3270 while ((eqe = advance_event(eqe))) {
3271 if (((s->session->readperm & eqe->category) == eqe->category)
3272 && ((s->session->send_events & eqe->category) == eqe->category)
3274 astman_append(s, "%s", eqe->eventdata);
3275 }
3276 s->session->last_ev = eqe;
3277 }
3278 astman_append(s,
3279 "Event: WaitEventComplete\r\n"
3280 "%s"
3281 "\r\n", idText);
3282 ao2_unlock(s->session);
3283 } else {
3285 ast_debug(1, "Abandoning event request!\n");
3286 }
3287
3288 return 0;
3289}
3290
3291static int action_listcommands(struct mansession *s, const struct message *m)
3292{
3293 struct manager_action *cur;
3295
3296 astman_start_ack(s, m);
3298 AST_RWLIST_TRAVERSE(&actions, cur, list) {
3299 if ((s->session->writeperm & cur->authority) || cur->authority == 0) {
3300 astman_append(s, "%s: %s (Priv: %s)\r\n",
3301 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
3302 }
3303 }
3305 astman_append(s, "\r\n");
3306
3307 return 0;
3308}
3309
3310static int action_events(struct mansession *s, const struct message *m)
3311{
3312 const char *mask = astman_get_header(m, "EventMask");
3313 int res, x;
3314 const char *id = astman_get_header(m, "ActionID");
3315 char id_text[256];
3316
3317 if (!ast_strlen_zero(id)) {
3318 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
3319 } else {
3320 id_text[0] = '\0';
3321 }
3322
3323 res = set_eventmask(s, mask);
3325 /* if this option is set we should not return a response on
3326 * error, or when all events are set */
3327
3328 if (res > 0) {
3329 for (x = 0; x < ARRAY_LEN(perms); x++) {
3330 if (!strcasecmp(perms[x].label, "all") && res == perms[x].num) {
3331 return 0;
3332 }
3333 }
3334 astman_append(s, "Response: Success\r\n%s"
3335 "Events: On\r\n\r\n", id_text);
3336 } else if (res == 0)
3337 astman_append(s, "Response: Success\r\n%s"
3338 "Events: Off\r\n\r\n", id_text);
3339 return 0;
3340 }
3341
3342 if (res > 0)
3343 astman_append(s, "Response: Success\r\n%s"
3344 "Events: On\r\n\r\n", id_text);
3345 else if (res == 0)
3346 astman_append(s, "Response: Success\r\n%s"
3347 "Events: Off\r\n\r\n", id_text);
3348 else
3349 astman_send_error(s, m, "Invalid event mask");
3350
3351 return 0;
3352}
3353
3354static int action_logoff(struct mansession *s, const struct message *m)
3355{
3356 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
3357 return -1;
3358}
3359
3360static int action_login(struct mansession *s, const struct message *m)
3361{
3362
3363 /* still authenticated - don't process again */
3364 if (s->session->authenticated) {
3365 astman_send_ack(s, m, "Already authenticated");
3366 return 0;
3367 }
3368
3369 if (authenticate(s, m)) {
3370 sleep(1);
3371 astman_send_error(s, m, "Authentication failed");
3372 return -1;
3373 }
3374 s->session->authenticated = 1;
3377 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_sockaddr_stringify_addr(&s->session->addr));
3378 }
3379 astman_send_ack(s, m, "Authentication accepted");
3382 && ast_fully_booted) {
3384 const char *cat_str = authority_to_str(EVENT_FLAG_SYSTEM, &auth);
3385 long uptime = 0;
3386 long lastreloaded = 0;
3387 struct timeval tmp;
3388 struct timeval curtime = ast_tvnow();
3389
3390 if (ast_startuptime.tv_sec) {
3391 tmp = ast_tvsub(curtime, ast_startuptime);
3392 uptime = tmp.tv_sec;
3393 }
3394
3395 if (ast_lastreloadtime.tv_sec) {
3396 tmp = ast_tvsub(curtime, ast_lastreloadtime);
3397 lastreloaded = tmp.tv_sec;
3398 }
3399
3400 astman_append(s, "Event: FullyBooted\r\n"
3401 "Privilege: %s\r\n"
3402 "Uptime: %ld\r\n"
3403 "LastReload: %ld\r\n"
3404 "Status: Fully Booted\r\n\r\n", cat_str, uptime, lastreloaded);
3405 }
3406 return 0;
3407}
3408
3409static int action_challenge(struct mansession *s, const struct message *m)
3410{
3411 const char *authtype = astman_get_header(m, "AuthType");
3412
3413 if (!strcasecmp(authtype, "MD5")) {
3415 snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
3416 }
3417 mansession_lock(s);
3418 astman_start_ack(s, m);
3419 astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
3421 } else {
3422 astman_send_error(s, m, "Must specify AuthType");
3423 }
3424 return 0;
3425}
3426
3428 const struct message *m, manager_hangup_handler_t hangup_handler,
3429 manager_hangup_cause_validator_t cause_validator)
3430{
3431 struct ast_channel *c = NULL;
3432 int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
3433 const char *id = astman_get_header(m, "ActionID");
3434 const char *name_or_regex = astman_get_header(m, "Channel");
3435 const char *cause = astman_get_header(m, "Cause");
3436 char idText[256];
3437 regex_t regexbuf;
3438 struct ast_channel_iterator *iter = NULL;
3439 struct ast_str *regex_string;
3440 int channels_matched = 0;
3441
3442 if (ast_strlen_zero(name_or_regex)) {
3443 astman_send_error(s, m, "No channel specified");
3444 return 0;
3445 }
3446
3447 if (!ast_strlen_zero(id)) {
3448 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
3449 } else {
3450 idText[0] = '\0';
3451 }
3452
3453 if (cause_validator) {
3454 causecode = cause_validator(name_or_regex, cause);
3455 } else if (!ast_strlen_zero(cause)) {
3456 char *endptr;
3457 causecode = strtol(cause, &endptr, 10);
3458 if (causecode < 0 || causecode > 127 || *endptr != '\0') {
3459 ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause);
3460 /* keep going, better to hangup without cause than to not hang up at all */
3461 causecode = 0; /* do not set channel's hangupcause */
3462 }
3463 }
3464
3465 /************************************************/
3466 /* Regular explicit match channel byname hangup */
3467
3468 if (name_or_regex[0] != '/') {
3469 if (!(c = ast_channel_get_by_name(name_or_regex))) {
3470 ast_log(LOG_NOTICE, "Request to hangup non-existent channel: %s\n",
3471 name_or_regex);
3472 astman_send_error(s, m, "No such channel");
3473 return 0;
3474 }
3475
3476 ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
3477 (s->session->managerid ? "HTTP " : ""),
3478 s->session->username,
3481
3482 hangup_handler(c, causecode);
3484
3485 astman_send_ack(s, m, "Channel Hungup");
3486
3487 return 0;
3488 }
3489
3490 /***********************************************/
3491 /* find and hangup any channels matching regex */
3492
3493 regex_string = ast_str_create(strlen(name_or_regex));
3494 if (!regex_string) {
3495 astman_send_error(s, m, "Memory Allocation Failure");
3496 return 0;
3497 }
3498
3499 /* Make "/regex/" into "regex" */
3500 if (ast_regex_string_to_regex_pattern(name_or_regex, &regex_string) != 0) {
3501 astman_send_error(s, m, "Regex format invalid, Channel param should be /regex/");
3502 ast_free(regex_string);
3503 return 0;
3504 }
3505
3506 /* if regex compilation fails, hangup fails */
3507 if (regcomp(&regexbuf, ast_str_buffer(regex_string), REG_EXTENDED | REG_NOSUB)) {
3508 astman_send_error_va(s, m, "Regex compile failed on: %s", name_or_regex);
3509 ast_free(regex_string);
3510 return 0;
3511 }
3512
3513 astman_send_listack(s, m, "Channels hung up will follow", "start");
3514
3516 if (iter) {
3517 for (; (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
3518 if (regexec(&regexbuf, ast_channel_name(c), 0, NULL, 0)) {
3519 continue;
3520 }
3521
3522 ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
3523 (s->session->managerid ? "HTTP " : ""),
3524 s->session->username,
3527
3528 hangup_handler(c, causecode);
3529 channels_matched++;
3530
3531 astman_append(s,
3532 "Event: ChannelHungup\r\n"
3533 "Channel: %s\r\n"
3534 "%s"
3535 "\r\n", ast_channel_name(c), idText);
3536 }
3538 }
3539
3540 regfree(&regexbuf);
3541 ast_free(regex_string);
3542
3543 astman_send_list_complete(s, m, "ChannelsHungupListComplete", channels_matched);
3544
3545 return 0;
3546}
3547
3548static int action_hangup(struct mansession *s, const struct message *m)
3549{
3550 return ast_manager_hangup_helper(s, m,
3552}
3553
3554static int action_setvar(struct mansession *s, const struct message *m)
3555{
3556 struct ast_channel *c = NULL;
3557 const char *name = astman_get_header(m, "Channel");
3558 const char *varname = astman_get_header(m, "Variable");
3559 const char *varval = astman_get_header(m, "Value");
3560 int res = 0;
3561
3562 if (ast_strlen_zero(varname)) {
3563 astman_send_error(s, m, "No variable specified");
3564 return 0;
3565 }
3566
3567 if (!ast_strlen_zero(name)) {
3568 if (!(c = ast_channel_get_by_name(name))) {
3569 astman_send_error(s, m, "No such channel");
3570 return 0;
3571 }
3572 }
3573
3574 res = pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
3575
3576 if (c) {
3578 }
3579 if (res == 0) {
3580 astman_send_ack(s, m, "Variable Set");
3581 } else {
3582 astman_send_error(s, m, "Variable not set");
3583 }
3584 return 0;
3585}
3586
3587static int action_getvar(struct mansession *s, const struct message *m)
3588{
3589 struct ast_channel *c = NULL;
3590 const char *name = astman_get_header(m, "Channel");
3591 const char *varname = astman_get_header(m, "Variable");
3592 char *varval;
3593 char workspace[1024];
3594
3595 if (ast_strlen_zero(varname)) {
3596 astman_send_error(s, m, "No variable specified");
3597 return 0;
3598 }
3599
3600 /* We don't want users with insufficient permissions using certain functions. */
3602 astman_send_error(s, m, "GetVar Access Forbidden: Variable");
3603 return 0;
3604 }
3605
3606 if (!ast_strlen_zero(name)) {
3607 if (!(c = ast_channel_get_by_name(name))) {
3608 astman_send_error(s, m, "No such channel");
3609 return 0;
3610 }
3611 }
3612
3613 workspace[0] = '\0';
3614 if (varname[strlen(varname) - 1] == ')') {
3615 if (!c) {
3617 if (c) {
3618 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
3619 } else
3620 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
3621 } else {
3622 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
3623 }
3624 varval = workspace;
3625 } else {
3626 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
3627 }
3628
3629 if (c) {
3631 }
3632
3633 astman_start_ack(s, m);
3634 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, S_OR(varval, ""));
3635
3636 return 0;
3637}
3638
3639static void generate_status(struct mansession *s, struct ast_channel *chan, char **vars, int varc, int all_variables, char *id_text, int *count)
3640{
3641 struct timeval now;
3642 long elapsed_seconds;
3643 struct ast_bridge *bridge;
3644 RAII_VAR(struct ast_str *, variable_str, NULL, ast_free);
3645 struct ast_str *write_transpath = ast_str_alloca(256);
3646 struct ast_str *read_transpath = ast_str_alloca(256);
3647 struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
3648 struct ast_party_id effective_id;
3649 int i;
3650 RAII_VAR(struct ast_channel_snapshot *, snapshot,
3652 ao2_cleanup);
3653 RAII_VAR(struct ast_str *, snapshot_str, NULL, ast_free);
3654
3655 if (!snapshot) {
3656 return;
3657 }
3658
3659 snapshot_str = ast_manager_build_channel_state_string(snapshot);
3660 if (!snapshot_str) {
3661 return;
3662 }
3663
3664 if (all_variables) {
3665 variable_str = ast_str_create(2048);
3666 } else {
3667 variable_str = ast_str_create(1024);
3668 }
3669 if (!variable_str) {
3670 return;
3671 }
3672
3673 now = ast_tvnow();
3674 elapsed_seconds = ast_tvdiff_sec(now, ast_channel_creationtime(chan));
3675
3676 /* Even if all_variables has been specified, explicitly requested variables
3677 * may be global variables or dialplan functions */
3678 for (i = 0; i < varc; i++) {
3679 char valbuf[512], *ret = NULL;
3680
3681 if (vars[i][strlen(vars[i]) - 1] == ')') {
3682 if (ast_func_read(chan, vars[i], valbuf, sizeof(valbuf)) < 0) {
3683 valbuf[0] = '\0';
3684 }
3685 ret = valbuf;
3686 } else {
3687 pbx_retrieve_variable(chan, vars[i], &ret, valbuf, sizeof(valbuf), NULL);
3688 }
3689
3690 ast_str_append(&variable_str, 0, "Variable: %s=%s\r\n", vars[i], ret);
3691 }
3692
3693 /* Walk all channel variables and add them */
3694 if (all_variables) {
3695 struct ast_var_t *variables;
3696
3697 AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
3698 ast_str_append(&variable_str, 0, "Variable: %s=%s\r\n",
3699 ast_var_name(variables), ast_var_value(variables));
3700 }
3701 }
3702
3703 bridge = ast_channel_get_bridge(chan);
3704 effective_id = ast_channel_connected_effective_id(chan);
3705
3706 astman_append(s,
3707 "Event: Status\r\n"
3708 "Privilege: Call\r\n"
3709 "%s"
3710 "Type: %s\r\n"
3711 "DNID: %s\r\n"
3712 "EffectiveConnectedLineNum: %s\r\n"
3713 "EffectiveConnectedLineName: %s\r\n"
3714 "TimeToHangup: %ld\r\n"
3715 "BridgeID: %s\r\n"
3716 "Application: %s\r\n"
3717 "Data: %s\r\n"
3718 "Nativeformats: %s\r\n"
3719 "Readformat: %s\r\n"
3720 "Readtrans: %s\r\n"
3721 "Writeformat: %s\r\n"
3722 "Writetrans: %s\r\n"
3723 "Callgroup: %llu\r\n"
3724 "Pickupgroup: %llu\r\n"
3725 "Seconds: %ld\r\n"
3726 "%s"
3727 "%s"
3728 "\r\n",
3729 ast_str_buffer(snapshot_str),
3730 ast_channel_tech(chan)->type,
3731 S_OR(ast_channel_dialed(chan)->number.str, ""),
3732 S_COR(effective_id.number.valid, effective_id.number.str, "<unknown>"),
3733 S_COR(effective_id.name.valid, effective_id.name.str, "<unknown>"),
3734 (long)ast_channel_whentohangup(chan)->tv_sec,
3735 bridge ? bridge->uniqueid : "",
3736 ast_channel_appl(chan),
3737 ast_channel_data(chan),
3740 ast_translate_path_to_str(ast_channel_readtrans(chan), &read_transpath),
3742 ast_translate_path_to_str(ast_channel_writetrans(chan), &write_transpath),
3745 (long)elapsed_seconds,
3746 ast_str_buffer(variable_str),
3747 id_text);
3748 ++*count;
3749
3750 ao2_cleanup(bridge);
3751}
3752
3753/*! \brief Manager "status" command to show channels */
3754static int action_status(struct mansession *s, const struct message *m)
3755{
3756 const char *name = astman_get_header(m, "Channel");
3757 const char *chan_variables = astman_get_header(m, "Variables");
3758 const char *all_chan_variables = astman_get_header(m, "AllVariables");
3759 int all_variables = 0;
3760 const char *id = astman_get_header(m, "ActionID");
3761 char *variables = ast_strdupa(S_OR(chan_variables, ""));
3762 struct ast_channel *chan;
3763 int channels = 0;
3764 int all = ast_strlen_zero(name); /* set if we want all channels */
3765 char id_text[256];
3766 struct ast_channel_iterator *it_chans = NULL;
3768 AST_APP_ARG(name)[100];
3769 );
3770
3771 if (!ast_strlen_zero(all_chan_variables)) {
3772 all_variables = ast_true(all_chan_variables);
3773 }
3774
3776 astman_send_error(s, m, "Status Access Forbidden: Variables");
3777 return 0;
3778 }
3779
3780 if (all) {
3781 if (!(it_chans = ast_channel_iterator_all_new())) {
3782 astman_send_error(s, m, "Memory Allocation Failure");
3783 return 1;
3784 }
3785 chan = ast_channel_iterator_next(it_chans);
3786 } else {
3788 if (!chan) {
3789 astman_send_error(s, m, "No such channel");
3790 return 0;
3791 }
3792 }
3793
3794 astman_send_listack(s, m, "Channel status will follow", "start");
3795
3796 if (!ast_strlen_zero(id)) {
3797 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
3798 } else {
3799 id_text[0] = '\0';
3800 }
3801
3802 if (!ast_strlen_zero(chan_variables)) {
3803 AST_STANDARD_APP_ARGS(vars, variables);
3804 }
3805
3806 /* if we look by name, we break after the first iteration */
3807 for (; chan; all ? chan = ast_channel_iterator_next(it_chans) : 0) {
3808 ast_channel_lock(chan);
3809
3810 generate_status(s, chan, vars.name, vars.argc, all_variables, id_text, &channels);
3811
3812 ast_channel_unlock(chan);
3813 chan = ast_channel_unref(chan);
3814 }
3815
3816 if (it_chans) {
3818 }
3819
3820 astman_send_list_complete_start(s, m, "StatusComplete", channels);
3821 astman_append(s, "Items: %d\r\n", channels);
3823
3824 return 0;
3825}
3826
3827/*!
3828 * \brief Queue a given read action containing a payload onto a channel
3829 *
3830 * This queues a READ_ACTION control frame that contains a given "payload", or
3831 * data to be triggered and handled on the channel's read side. This ensures
3832 * the "action" is handled by the channel's media reading thread.
3833 *
3834 * \param chan The channel to queue the action on
3835 * \param payload The read action's payload
3836 * \param payload_size The size of the given payload
3837 * \param action The type of read action to queue
3838 *
3839 * \retval -1 on error
3840 * \retval 0 on success
3841 */
3842static int queue_read_action_payload(struct ast_channel *chan, const unsigned char *payload,
3843 size_t payload_size, enum ast_frame_read_action action)
3844{
3846 size_t obj_size;
3847 int res;
3848
3849 obj_size = payload_size + sizeof(*obj);
3850
3851 obj = ast_malloc(obj_size);
3852 if (!obj) {
3853 return -1;
3854 }
3855
3856 obj->action = action;
3857 obj->payload_size = payload_size;
3858 memcpy(obj->payload, payload, payload_size);
3859
3860 res = ast_queue_control_data(chan, AST_CONTROL_READ_ACTION, obj, obj_size);
3861
3862 ast_free(obj);
3863 return res;
3864}
3865
3866/*!
3867 * \brief Queue a read action to send a text message
3868 *
3869 * \param chan The channel to queue the action on
3870 * \param body The body of the message
3871 *
3872 * \retval -1 on error
3873 * \retval 0 on success
3874 */
3875static int queue_sendtext(struct ast_channel *chan, const char *body)
3876{
3877 return queue_read_action_payload(chan, (const unsigned char *)body,
3878 strlen(body) + 1, AST_FRAME_READ_ACTION_SEND_TEXT);
3879}
3880
3881/*!
3882 * \brief Queue a read action to send a text data message
3883 *
3884 * \param chan The channel to queue the action on
3885 * \param body The body of the message
3886 * \param content_type The message's content type
3887 *
3888 * \retval -1 on error
3889 * \retval 0 on success
3890 */
3891static int queue_sendtext_data(struct ast_channel *chan, const char *body,
3892 const char *content_type)
3893{
3894 int res;
3895 struct ast_msg_data *obj;
3896
3898 NULL, NULL, content_type, body);
3899 if (!obj) {
3900 return -1;
3901 }
3902
3903 res = queue_read_action_payload(chan, (const unsigned char *)obj,
3905
3906 ast_free(obj);
3907 return res;
3908}
3909
3910static int action_sendtext(struct mansession *s, const struct message *m)
3911{
3912 struct ast_channel *c;
3913 const char *name = astman_get_header(m, "Channel");
3914 const char *textmsg = astman_get_header(m, "Message");
3915 const char *content_type = astman_get_header(m, "Content-Type");
3916 int res;
3917
3918 if (ast_strlen_zero(name)) {
3919 astman_send_error(s, m, "No channel specified");
3920 return 0;
3921 }
3922
3923 if (ast_strlen_zero(textmsg)) {
3924 astman_send_error(s, m, "No Message specified");
3925 return 0;
3926 }
3927
3929 if (!c) {
3930 astman_send_error(s, m, "No such channel");
3931 return 0;
3932 }
3933
3934 /*
3935 * If the "extra" data is not available, then send using "string" only.
3936 * Doing such maintains backward compatibilities.
3937 */
3938 res = ast_strlen_zero(content_type) ? queue_sendtext(c, textmsg) :
3939 queue_sendtext_data(c, textmsg, content_type);
3940
3942
3943 if (res >= 0) {
3944 astman_send_ack(s, m, "Success");
3945 } else {
3946 astman_send_error(s, m, "Failure");
3947 }
3948
3949 return 0;
3950}
3951
3952static int async_goto_with_discard_bridge_after(struct ast_channel *chan, const char *context, const char *exten, int priority)
3953{
3955 return ast_async_goto(chan, context, exten, priority);
3956}
3957
3958/*! \brief action_redirect: The redirect manager command */
3959static int action_redirect(struct mansession *s, const struct message *m)
3960{
3961 char buf[256];
3962 const char *name = astman_get_header(m, "Channel");
3963 const char *name2 = astman_get_header(m, "ExtraChannel");
3964 const char *exten = astman_get_header(m, "Exten");
3965 const char *exten2 = astman_get_header(m, "ExtraExten");
3966 const char *context = astman_get_header(m, "Context");
3967 const char *context2 = astman_get_header(m, "ExtraContext");
3968 const char *priority = astman_get_header(m, "Priority");
3969 const char *priority2 = astman_get_header(m, "ExtraPriority");
3970 struct ast_channel *chan;
3971 struct ast_channel *chan2;
3972 int pi = 0;
3973 int pi2 = 0;
3974 int res;
3975 int chan1_wait = 0;
3976 int chan2_wait = 0;
3977
3978 if (ast_strlen_zero(name)) {
3979 astman_send_error(s, m, "Channel not specified");
3980 return 0;
3981 }
3982
3983 if (ast_strlen_zero(context)) {
3984 astman_send_error(s, m, "Context not specified");
3985 return 0;
3986 }
3987 if (ast_strlen_zero(exten)) {
3988 astman_send_error(s, m, "Exten not specified");
3989 return 0;
3990 }
3992 astman_send_error(s, m, "Priority not specified");
3993 return 0;
3994 }
3995 if (sscanf(priority, "%30d", &pi) != 1) {
3996 pi = ast_findlabel_extension(NULL, context, exten, priority, NULL);
3997 }
3998 if (pi < 1) {
3999 astman_send_error(s, m, "Priority is invalid");
4000 return 0;
4001 }
4002
4003 if (!ast_strlen_zero(name2) && !ast_strlen_zero(context2)) {
4004 /* We have an ExtraChannel and an ExtraContext */
4005 if (ast_strlen_zero(exten2)) {
4006 astman_send_error(s, m, "ExtraExten not specified");
4007 return 0;
4008 }
4009 if (ast_strlen_zero(priority2)) {
4010 astman_send_error(s, m, "ExtraPriority not specified");
4011 return 0;
4012 }
4013 if (sscanf(priority2, "%30d", &pi2) != 1) {
4014 pi2 = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL);
4015 }
4016 if (pi2 < 1) {
4017 astman_send_error(s, m, "ExtraPriority is invalid");
4018 return 0;
4019 }
4020 }
4021
4023 if (!chan) {
4024 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
4025 astman_send_error(s, m, buf);
4026 return 0;
4027 }
4028 if (ast_check_hangup_locked(chan)) {
4029 astman_send_error(s, m, "Redirect failed, channel not up.");
4030 chan = ast_channel_unref(chan);
4031 return 0;
4032 }
4033
4034 if (ast_strlen_zero(name2)) {
4035 /* Single channel redirect in progress. */
4036 res = async_goto_with_discard_bridge_after(chan, context, exten, pi);
4037 if (!res) {
4038 astman_send_ack(s, m, "Redirect successful");
4039 } else {
4040 astman_send_error(s, m, "Redirect failed");
4041 }
4042 chan = ast_channel_unref(chan);
4043 return 0;
4044 }
4045
4046 chan2 = ast_channel_get_by_name(name2);
4047 if (!chan2) {
4048 snprintf(buf, sizeof(buf), "ExtraChannel does not exist: %s", name2);
4049 astman_send_error(s, m, buf);
4050 chan = ast_channel_unref(chan);
4051 return 0;
4052 }
4053 if (ast_check_hangup_locked(chan2)) {
4054 astman_send_error(s, m, "Redirect failed, extra channel not up.");
4055 chan2 = ast_channel_unref(chan2);
4056 chan = ast_channel_unref(chan);
4057 return 0;
4058 }
4059
4060 /* Dual channel redirect in progress. */
4061 ast_channel_lock(chan);
4062 if (ast_channel_is_bridged(chan)) {
4064 chan1_wait = 1;
4065 }
4066 ast_channel_unlock(chan);
4067
4068 ast_channel_lock(chan2);
4069 if (ast_channel_is_bridged(chan2)) {
4071 chan2_wait = 1;
4072 }
4073 ast_channel_unlock(chan2);
4074
4075 res = async_goto_with_discard_bridge_after(chan, context, exten, pi);
4076 if (!res) {
4077 if (!ast_strlen_zero(context2)) {
4078 res = async_goto_with_discard_bridge_after(chan2, context2, exten2, pi2);
4079 } else {
4080 res = async_goto_with_discard_bridge_after(chan2, context, exten, pi);
4081 }
4082 if (!res) {
4083 astman_send_ack(s, m, "Dual Redirect successful");
4084 } else {
4085 astman_send_error(s, m, "Secondary redirect failed");
4086 }
4087 } else {
4088 astman_send_error(s, m, "Redirect failed");
4089 }
4090
4091 /* Release the bridge wait. */
4092 if (chan1_wait) {
4094 }
4095 if (chan2_wait) {
4097 }
4098
4099 chan2 = ast_channel_unref(chan2);
4100 chan = ast_channel_unref(chan);
4101 return 0;
4102}
4103
4104static int action_blind_transfer(struct mansession *s, const struct message *m)
4105{
4106 const char *name = astman_get_header(m, "Channel");
4107 const char *exten = astman_get_header(m, "Exten");
4108 const char *context = astman_get_header(m, "Context");
4109 struct ast_channel *chan;
4110
4111 if (ast_strlen_zero(name)) {
4112 astman_send_error(s, m, "No channel specified");
4113 return 0;
4114 }
4115
4116 if (ast_strlen_zero(exten)) {
4117 astman_send_error(s, m, "No extension specified");
4118 return 0;
4119 }
4120
4122 if (!chan) {
4123 astman_send_error(s, m, "Channel specified does not exist");
4124 return 0;
4125 }
4126
4127 if (ast_strlen_zero(context)) {
4128 context = ast_channel_context(chan);
4129 }
4130
4131 switch (ast_bridge_transfer_blind(1, chan, exten, context, NULL, NULL)) {
4133 astman_send_error(s, m, "Transfer not permitted");
4134 break;
4136 astman_send_error(s, m, "Transfer invalid");
4137 break;
4139 astman_send_error(s, m, "Transfer failed");
4140 break;
4142 astman_send_ack(s, m, "Transfer succeeded");
4143 break;
4144 }
4145
4146 ast_channel_unref(chan);
4147 return 0;
4148}
4149
4150static int action_atxfer(struct mansession *s, const struct message *m)
4151{
4152 const char *name = astman_get_header(m, "Channel");
4153 const char *exten = astman_get_header(m, "Exten");
4154 const char *context = astman_get_header(m, "Context");
4155 struct ast_channel *chan = NULL;
4156 char feature_code[AST_FEATURE_MAX_LEN];
4157 const char *digit;
4158
4159 if (ast_strlen_zero(name)) {
4160 astman_send_error(s, m, "No channel specified");
4161 return 0;
4162 }
4163 if (ast_strlen_zero(exten)) {
4164 astman_send_error(s, m, "No extension specified");
4165 return 0;
4166 }
4167
4168 if (!(chan = ast_channel_get_by_name(name))) {
4169 astman_send_error(s, m, "Channel specified does not exist");
4170 return 0;
4171 }
4172
4173 ast_channel_lock(chan);
4174 if (ast_get_builtin_feature(chan, "atxfer", feature_code, sizeof(feature_code)) ||
4175 ast_strlen_zero(feature_code)) {
4176 ast_channel_unlock(chan);
4177 astman_send_error(s, m, "No attended transfer feature code found");
4178 ast_channel_unref(chan);
4179 return 0;
4180 }
4181 ast_channel_unlock(chan);
4182
4183 if (!ast_strlen_zero(context)) {
4184 pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
4185 }
4186
4187 for (digit = feature_code; *digit; ++digit) {
4188 struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
4189 ast_queue_frame(chan, &f);
4190 }
4191
4192 for (digit = exten; *digit; ++digit) {
4193 struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
4194 ast_queue_frame(chan, &f);
4195 }
4196
4197 chan = ast_channel_unref(chan);
4198
4199 astman_send_ack(s, m, "Atxfer successfully queued");
4200
4201 return 0;
4202}
4203
4204static int action_cancel_atxfer(struct mansession *s, const struct message *m)
4205{
4206 const char *name = astman_get_header(m, "Channel");
4207 struct ast_channel *chan = NULL;
4208 char *feature_code;
4209 const char *digit;
4210
4211 if (ast_strlen_zero(name)) {
4212 astman_send_error(s, m, "No channel specified");
4213 return 0;
4214 }
4215
4216 if (!(chan = ast_channel_get_by_name(name))) {
4217 astman_send_error(s, m, "Channel specified does not exist");
4218 return 0;
4219 }
4220
4221 ast_channel_lock(chan);
4222 feature_code = ast_get_chan_features_atxferabort(chan);
4223 ast_channel_unlock(chan);
4224
4225 if (!feature_code) {
4226 astman_send_error(s, m, "No disconnect feature code found");
4227 ast_channel_unref(chan);
4228 return 0;
4229 }
4230
4231 for (digit = feature_code; *digit; ++digit) {
4232 struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
4233 ast_queue_frame(chan, &f);
4234 }
4235 ast_free(feature_code);
4236
4237 chan = ast_channel_unref(chan);
4238
4239 astman_send_ack(s, m, "CancelAtxfer successfully queued");
4240
4241 return 0;
4242}
4243
4244
4245static int check_blacklist(const char *cmd)
4246{
4247 char *cmd_copy, *cur_cmd;
4248 char *cmd_words[AST_MAX_CMD_LEN] = { NULL, };
4249 int i;
4250
4251 cmd_copy = ast_strdupa(cmd);
4252 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
4253 cur_cmd = ast_strip(cur_cmd);
4254 if (ast_strlen_zero(cur_cmd)) {
4255 i--;
4256 continue;
4257 }
4258
4259 cmd_words[i] = cur_cmd;
4260 }
4261
4262 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
4263 int j, match = 1;
4264
4265 for (j = 0; command_blacklist[i].words[j]; j++) {
4266 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
4267 match = 0;
4268 break;
4269 }
4270 }
4271
4272 if (match) {
4273 return 1;
4274 }
4275 }
4276
4277 return 0;
4278}
4279
4280/*! \brief Manager command "command" - execute CLI command */
4281static int action_command(struct mansession *s, const struct message *m)
4282{
4283 const char *cmd = astman_get_header(m, "Command");
4284 char *buf = NULL, *final_buf = NULL, *delim, *output;
4285 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
4286 int fd, ret;
4287 off_t len;
4288
4289 if (ast_strlen_zero(cmd)) {
4290 astman_send_error(s, m, "No command provided");
4291 return 0;
4292 }
4293
4294 if (check_blacklist(cmd)) {
4295 astman_send_error(s, m, "Command blacklisted");
4296 return 0;
4297 }
4298
4299 if ((fd = mkstemp(template)) < 0) {
4300 astman_send_error_va(s, m, "Failed to create temporary file: %s", strerror(errno));
4301 return 0;
4302 }
4303
4304 ret = ast_cli_command(fd, cmd);
4305 astman_send_response_full(s, m, ret == RESULT_SUCCESS ? "Success" : "Error", MSG_MOREDATA, NULL);
4306
4307 /* Determine number of characters available */
4308 if ((len = lseek(fd, 0, SEEK_END)) < 0) {
4309 astman_append(s, "Message: Failed to determine number of characters: %s\r\n", strerror(errno));
4310 goto action_command_cleanup;
4311 }
4312
4313 /* This has a potential to overflow the stack. Hence, use the heap. */
4314 buf = ast_malloc(len + 1);
4315 final_buf = ast_malloc(len + 1);
4316
4317 if (!buf || !final_buf) {
4318 astman_append(s, "Message: Memory allocation failure\r\n");
4319 goto action_command_cleanup;
4320 }
4321
4322 if (lseek(fd, 0, SEEK_SET) < 0) {
4323 astman_append(s, "Message: Failed to set position on temporary file: %s\r\n", strerror(errno));
4324 goto action_command_cleanup;
4325 }
4326
4327 if (read(fd, buf, len) < 0) {
4328 astman_append(s, "Message: Failed to read from temporary file: %s\r\n", strerror(errno));
4329 goto action_command_cleanup;
4330 }
4331
4332 buf[len] = '\0';
4333 term_strip(final_buf, buf, len);
4334 final_buf[len] = '\0';
4335
4336 /* Trim trailing newline */
4337 if (len && final_buf[len - 1] == '\n') {
4338 final_buf[len - 1] = '\0';
4339 }
4340
4341 astman_append(s, "Message: Command output follows\r\n");
4342
4343 delim = final_buf;
4344 while ((output = strsep(&delim, "\n"))) {
4345 astman_append(s, "Output: %s\r\n", output);
4346 }
4347
4348action_command_cleanup:
4349 astman_append(s, "\r\n");
4350
4351 close(fd);
4352 unlink(template);
4353
4354 ast_free(buf);
4355 ast_free(final_buf);
4356
4357 return 0;
4358}
4359
4360/*! \brief helper function for originate */
4363 struct ast_format_cap *cap; /*!< Codecs used for a call */
4366 AST_STRING_FIELD(tech);
4367 /*! data can contain a channel name, extension number, username, password, etc. */
4368 AST_STRING_FIELD(data);
4370 AST_STRING_FIELD(appdata);
4371 AST_STRING_FIELD(cid_name);
4372 AST_STRING_FIELD(cid_num);
4373 AST_STRING_FIELD(context);
4374 AST_STRING_FIELD(exten);
4375 AST_STRING_FIELD(idtext);
4376 AST_STRING_FIELD(account);
4377 AST_STRING_FIELD(channelid);
4378 AST_STRING_FIELD(otherchannelid);
4382};
4383
4384/*!
4385 * \internal
4386 *
4387 * \param doomed Struct to destroy.
4388 */
4390{
4391 ao2_cleanup(doomed->cap);
4392 ast_variables_destroy(doomed->vars);
4394 ast_free(doomed);
4395}
4396
4397static void *fast_originate(void *data)
4398{
4399 struct fast_originate_helper *in = data;
4400 int res;
4401 int reason = 0;
4402 struct ast_channel *chan = NULL, *chans[1];
4403 char requested_channel[AST_CHANNEL_NAME];
4404 struct ast_assigned_ids assignedids = {
4405 .uniqueid = in->channelid,
4406 .uniqueid2 = in->otherchannelid
4407 };
4408
4409 if (!ast_strlen_zero(in->app)) {
4410 res = ast_pbx_outgoing_app(in->tech, in->cap, in->data,
4411 in->timeout, in->app, in->appdata, &reason,
4413 S_OR(in->cid_num, NULL),
4414 S_OR(in->cid_name, NULL),
4415 in->vars, in->account, &chan, &assignedids);
4416 } else {
4417 res = ast_pbx_outgoing_exten(in->tech, in->cap, in->data,
4418 in->timeout, in->context, in->exten, in->priority, &reason,
4420 S_OR(in->cid_num, NULL),
4421 S_OR(in->cid_name, NULL),
4422 in->vars, in->account, &chan, in->early_media, &assignedids);
4423 }
4424
4425 if (!chan) {
4426 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
4427 }
4428 /* Tell the manager what happened with the channel */
4429 chans[0] = chan;
4430 if (!ast_strlen_zero(in->app)) {
4431 ast_manager_event_multichan(EVENT_FLAG_CALL, "OriginateResponse", chan ? 1 : 0, chans,
4432 "%s"
4433 "Response: %s\r\n"
4434 "Channel: %s\r\n"
4435 "Application: %s\r\n"
4436 "Data: %s\r\n"
4437 "Reason: %d\r\n"
4438 "Uniqueid: %s\r\n"
4439 "CallerIDNum: %s\r\n"
4440 "CallerIDName: %s\r\n",
4441 in->idtext, res ? "Failure" : "Success",
4442 chan ? ast_channel_name(chan) : requested_channel,
4443 in->app, in->appdata, reason,
4444 chan ? ast_channel_uniqueid(chan) : S_OR(in->channelid, "<unknown>"),
4445 S_OR(in->cid_num, "<unknown>"),
4446 S_OR(in->cid_name, "<unknown>")
4447 );
4448 } else {
4449 ast_manager_event_multichan(EVENT_FLAG_CALL, "OriginateResponse", chan ? 1 : 0, chans,
4450 "%s"
4451 "Response: %s\r\n"
4452 "Channel: %s\r\n"
4453 "Context: %s\r\n"
4454 "Exten: %s\r\n"
4455 "Reason: %d\r\n"
4456 "Uniqueid: %s\r\n"
4457 "CallerIDNum: %s\r\n"
4458 "CallerIDName: %s\r\n",
4459 in->idtext, res ? "Failure" : "Success",
4460 chan ? ast_channel_name(chan) : requested_channel,
4461 in->context, in->exten, reason,
4462 chan ? ast_channel_uniqueid(chan) : S_OR(in->channelid, "<unknown>"),
4463 S_OR(in->cid_num, "<unknown>"),
4464 S_OR(in->cid_name, "<unknown>")
4465 );
4466 }
4467
4468 /* Locked and ref'd by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
4469 if (chan) {
4470 ast_channel_unlock(chan);
4471 ast_channel_unref(chan);
4472 }
4474 return NULL;
4475}
4476
4477static int aocmessage_get_unit_entry(const struct message *m, struct ast_aoc_unit_entry *entry, unsigned int entry_num)
4478{
4479 const char *unitamount;
4480 const char *unittype;
4481 struct ast_str *str = ast_str_alloca(32);
4482
4483 memset(entry, 0, sizeof(*entry));
4484
4485 ast_str_set(&str, 0, "UnitAmount(%u)", entry_num);
4486 unitamount = astman_get_header(m, ast_str_buffer(str));
4487
4488 ast_str_set(&str, 0, "UnitType(%u)", entry_num);
4489 unittype = astman_get_header(m, ast_str_buffer(str));
4490
4491 if (!ast_strlen_zero(unitamount) && (sscanf(unitamount, "%30u", &entry->amount) == 1)) {
4492 entry->valid_amount = 1;
4493 }
4494
4495 if (!ast_strlen_zero(unittype) && sscanf(unittype, "%30u", &entry->type) == 1) {
4496 entry->valid_type = 1;
4497 }
4498
4499 return 0;
4500}
4501
4502static struct ast_aoc_decoded *action_aoc_de_message(struct mansession *s, const struct message *m)
4503{
4504 const char *msgtype = astman_get_header(m, "MsgType");
4505 const char *chargetype = astman_get_header(m, "ChargeType");
4506 const char *currencyname = astman_get_header(m, "CurrencyName");
4507 const char *currencyamount = astman_get_header(m, "CurrencyAmount");
4508 const char *mult = astman_get_header(m, "CurrencyMultiplier");
4509 const char *totaltype = astman_get_header(m, "TotalType");
4510 const char *aocbillingid = astman_get_header(m, "AOCBillingId");
4511 const char *association_id= astman_get_header(m, "ChargingAssociationId");
4512 const char *association_num = astman_get_header(m, "ChargingAssociationNumber");
4513 const char *association_plan = astman_get_header(m, "ChargingAssociationPlan");
4514
4515 enum ast_aoc_type _msgtype;
4516 enum ast_aoc_charge_type _chargetype;
4518 enum ast_aoc_total_type _totaltype = AST_AOC_TOTAL;
4519 enum ast_aoc_billing_id _billingid = AST_AOC_BILLING_NA;
4520 unsigned int _currencyamount = 0;
4521 int _association_id = 0;
4522 unsigned int _association_plan = 0;
4523
4524 struct ast_aoc_decoded *decoded = NULL;
4525
4526 if (ast_strlen_zero(chargetype)) {
4527 astman_send_error(s, m, "ChargeType not specified");
4528 goto aocmessage_cleanup;
4529 }
4530
4531 _msgtype = strcasecmp(msgtype, "d") ? AST_AOC_E : AST_AOC_D;
4532
4533 if (!strcasecmp(chargetype, "NA")) {
4534 _chargetype = AST_AOC_CHARGE_NA;
4535 } else if (!strcasecmp(chargetype, "Free")) {
4536 _chargetype = AST_AOC_CHARGE_FREE;
4537 } else if (!strcasecmp(chargetype, "Currency")) {
4538 _chargetype = AST_AOC_CHARGE_CURRENCY;
4539 } else if (!strcasecmp(chargetype, "Unit")) {
4540 _chargetype = AST_AOC_CHARGE_UNIT;
4541 } else {
4542 astman_send_error(s, m, "Invalid ChargeType");
4543 goto aocmessage_cleanup;
4544 }
4545
4546 if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
4547
4548 if (ast_strlen_zero(currencyamount) || (sscanf(currencyamount, "%30u", &_currencyamount) != 1)) {
4549 astman_send_error(s, m, "Invalid CurrencyAmount, CurrencyAmount is a required when ChargeType is Currency");
4550 goto aocmessage_cleanup;
4551 }
4552
4553 if (ast_strlen_zero(mult)) {
4554 astman_send_error(s, m, "ChargeMultiplier unspecified, ChargeMultiplier is required when ChargeType is Currency.");
4555 goto aocmessage_cleanup;
4556 } else if (!strcasecmp(mult, "onethousandth")) {
4558 } else if (!strcasecmp(mult, "onehundredth")) {
4560 } else if (!strcasecmp(mult, "onetenth")) {
4561 _mult = AST_AOC_MULT_ONETENTH;
4562 } else if (!strcasecmp(mult, "one")) {
4563 _mult = AST_AOC_MULT_ONE;
4564 } else if (!strcasecmp(mult, "ten")) {
4565 _mult = AST_AOC_MULT_TEN;
4566 } else if (!strcasecmp(mult, "hundred")) {
4567 _mult = AST_AOC_MULT_HUNDRED;
4568 } else if (!strcasecmp(mult, "thousand")) {
4569 _mult = AST_AOC_MULT_THOUSAND;
4570 } else {
4571 astman_send_error(s, m, "Invalid ChargeMultiplier");
4572 goto aocmessage_cleanup;
4573 }
4574 }
4575
4576 /* create decoded object and start setting values */
4577 if (!(decoded = ast_aoc_create(_msgtype, _chargetype, 0))) {
4578 astman_send_error(s, m, "Message Creation Failed");
4579 goto aocmessage_cleanup;
4580 }
4581
4582 if (_msgtype == AST_AOC_D) {
4583 if (!ast_strlen_zero(totaltype) && !strcasecmp(totaltype, "subtotal")) {
4584 _totaltype = AST_AOC_SUBTOTAL;
4585 }
4586
4587 if (ast_strlen_zero(aocbillingid)) {
4588 /* ignore this is optional */
4589 } else if (!strcasecmp(aocbillingid, "Normal")) {
4590 _billingid = AST_AOC_BILLING_NORMAL;
4591 } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
4592 _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
4593 } else if (!strcasecmp(aocbillingid, "CreditCard")) {
4594 _billingid = AST_AOC_BILLING_CREDIT_CARD;
4595 } else {
4596 astman_send_error(s, m, "Invalid AOC-D AOCBillingId");
4597 goto aocmessage_cleanup;
4598 }
4599 } else {
4600 if (ast_strlen_zero(aocbillingid)) {
4601 /* ignore this is optional */
4602 } else if (!strcasecmp(aocbillingid, "Normal")) {
4603 _billingid = AST_AOC_BILLING_NORMAL;
4604 } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
4605 _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
4606 } else if (!strcasecmp(aocbillingid, "CreditCard")) {
4607 _billingid = AST_AOC_BILLING_CREDIT_CARD;
4608 } else if (!strcasecmp(aocbillingid, "CallFwdUnconditional")) {
4610 } else if (!strcasecmp(aocbillingid, "CallFwdBusy")) {
4611 _billingid = AST_AOC_BILLING_CALL_FWD_BUSY;
4612 } else if (!strcasecmp(aocbillingid, "CallFwdNoReply")) {
4614 } else if (!strcasecmp(aocbillingid, "CallDeflection")) {
4616 } else if (!strcasecmp(aocbillingid, "CallTransfer")) {
4617 _billingid = AST_AOC_BILLING_CALL_TRANSFER;
4618 } else {
4619 astman_send_error(s, m, "Invalid AOC-E AOCBillingId");
4620 goto aocmessage_cleanup;
4621 }
4622
4623 if (!ast_strlen_zero(association_id) && (sscanf(association_id, "%30d", &_association_id) != 1)) {
4624 astman_send_error(s, m, "Invalid ChargingAssociationId");
4625 goto aocmessage_cleanup;
4626 }
4627 if (!ast_strlen_zero(association_plan) && (sscanf(association_plan, "%30u", &_association_plan) != 1)) {
4628 astman_send_error(s, m, "Invalid ChargingAssociationPlan");
4629 goto aocmessage_cleanup;
4630 }
4631
4632 if (_association_id) {
4633 ast_aoc_set_association_id(decoded, _association_id);
4634 } else if (!ast_strlen_zero(association_num)) {
4635 ast_aoc_set_association_number(decoded, association_num, _association_plan);
4636 }
4637 }
4638
4639 if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
4640 ast_aoc_set_currency_info(decoded, _currencyamount, _mult, ast_strlen_zero(currencyname) ? NULL : currencyname);
4641 } else if (_chargetype == AST_AOC_CHARGE_UNIT) {
4642 struct ast_aoc_unit_entry entry;
4643 int i;
4644
4645 /* multiple unit entries are possible, lets get them all */
4646 for (i = 0; i < 32; i++) {
4647 if (aocmessage_get_unit_entry(m, &entry, i)) {
4648 break; /* that's the end then */
4649 }
4650
4651 ast_aoc_add_unit_entry(decoded, entry.valid_amount, entry.amount, entry.valid_type, entry.type);
4652 }
4653
4654 /* at least one unit entry is required */
4655 if (!i) {
4656 astman_send_error(s, m, "Invalid UnitAmount(0), At least one valid unit entry is required when ChargeType is set to Unit");
4657 goto aocmessage_cleanup;
4658 }
4659
4660 }
4661
4662 ast_aoc_set_billing_id(decoded, _billingid);
4663 ast_aoc_set_total_type(decoded, _totaltype);
4664
4665 return decoded;
4666
4667aocmessage_cleanup:
4668
4669 ast_aoc_destroy_decoded(decoded);
4670 return NULL;
4671}
4672
4673static int action_aoc_s_submessage(struct mansession *s, const struct message *m,
4674 struct ast_aoc_decoded *decoded)
4675{
4676 const char *chargeditem = __astman_get_header(m, "ChargedItem", GET_HEADER_LAST_MATCH);
4677 const char *ratetype = __astman_get_header(m, "RateType", GET_HEADER_LAST_MATCH);
4678 const char *currencyname = __astman_get_header(m, "CurrencyName", GET_HEADER_LAST_MATCH);
4679 const char *currencyamount = __astman_get_header(m, "CurrencyAmount", GET_HEADER_LAST_MATCH);
4680 const char *mult = __astman_get_header(m, "CurrencyMultiplier", GET_HEADER_LAST_MATCH);
4681 const char *time = __astman_get_header(m, "Time", GET_HEADER_LAST_MATCH);
4682 const char *timescale = __astman_get_header(m, "TimeScale", GET_HEADER_LAST_MATCH);
4683 const char *granularity = __astman_get_header(m, "Granularity", GET_HEADER_LAST_MATCH);
4684 const char *granularitytimescale = __astman_get_header(m, "GranularityTimeScale", GET_HEADER_LAST_MATCH);
4685 const char *chargingtype = __astman_get_header(m, "ChargingType", GET_HEADER_LAST_MATCH);
4686 const char *volumeunit = __astman_get_header(m, "VolumeUnit", GET_HEADER_LAST_MATCH);
4687 const char *code = __astman_get_header(m, "Code", GET_HEADER_LAST_MATCH);
4688
4689 enum ast_aoc_s_charged_item _chargeditem;
4690 enum ast_aoc_s_rate_type _ratetype;
4692 unsigned int _currencyamount = 0;
4693 unsigned int _code;
4694 unsigned int _time = 0;
4695 enum ast_aoc_time_scale _scale = 0;
4696 unsigned int _granularity = 0;
4697 enum ast_aoc_time_scale _granularity_time_scale = AST_AOC_TIME_SCALE_MINUTE;
4698 int _step = 0;
4699 enum ast_aoc_volume_unit _volumeunit = 0;
4700
4701 if (ast_strlen_zero(chargeditem)) {
4702 astman_send_error(s, m, "ChargedItem not specified");
4703 goto aocmessage_cleanup;
4704 }
4705
4706 if (ast_strlen_zero(ratetype)) {
4707 astman_send_error(s, m, "RateType not specified");
4708 goto aocmessage_cleanup;
4709 }
4710
4711 if (!strcasecmp(chargeditem, "NA")) {
4712 _chargeditem = AST_AOC_CHARGED_ITEM_NA;
4713 } else if (!strcasecmp(chargeditem, "SpecialArrangement")) {
4715 } else if (!strcasecmp(chargeditem, "BasicCommunication")) {
4717 } else if (!strcasecmp(chargeditem, "CallAttempt")) {
4718 _chargeditem = AST_AOC_CHARGED_ITEM_CALL_ATTEMPT;
4719 } else if (!strcasecmp(chargeditem, "CallSetup")) {
4720 _chargeditem = AST_AOC_CHARGED_ITEM_CALL_SETUP;
4721 } else if (!strcasecmp(chargeditem, "UserUserInfo")) {
4723 } else if (!strcasecmp(chargeditem, "SupplementaryService")) {
4725 } else {
4726 astman_send_error(s, m, "Invalid ChargedItem");
4727 goto aocmessage_cleanup;
4728 }
4729
4730 if (!strcasecmp(ratetype, "NA")) {
4731 _ratetype = AST_AOC_RATE_TYPE_NA;
4732 } else if (!strcasecmp(ratetype, "Free")) {
4733 _ratetype = AST_AOC_RATE_TYPE_FREE;
4734 } else if (!strcasecmp(ratetype, "FreeFromBeginning")) {
4736 } else if (!strcasecmp(ratetype, "Duration")) {
4737 _ratetype = AST_AOC_RATE_TYPE_DURATION;
4738 } else if (!strcasecmp(ratetype, "Flat")) {
4739 _ratetype = AST_AOC_RATE_TYPE_FLAT;
4740 } else if (!strcasecmp(ratetype, "Volume")) {
4741 _ratetype = AST_AOC_RATE_TYPE_VOLUME;
4742 } else if (!strcasecmp(ratetype, "SpecialCode")) {
4744 } else {
4745 astman_send_error(s, m, "Invalid RateType");
4746 goto aocmessage_cleanup;
4747 }
4748
4749 if (_ratetype > AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING) {
4750 if (ast_strlen_zero(currencyamount) || (sscanf(currencyamount, "%30u",
4751 &_currencyamount) != 1)) {
4752 astman_send_error(s, m, "Invalid CurrencyAmount, CurrencyAmount is a required when RateType is non-free");
4753 goto aocmessage_cleanup;
4754 }
4755
4756 if (ast_strlen_zero(mult)) {
4757 astman_send_error(s, m, "ChargeMultiplier unspecified, ChargeMultiplier is required when ChargeType is Currency.");
4758 goto aocmessage_cleanup;
4759 } else if (!strcasecmp(mult, "onethousandth")) {
4761 } else if (!strcasecmp(mult, "onehundredth")) {
4763 } else if (!strcasecmp(mult, "onetenth")) {
4764 _mult = AST_AOC_MULT_ONETENTH;
4765 } else if (!strcasecmp(mult, "one")) {
4766 _mult = AST_AOC_MULT_ONE;
4767 } else if (!strcasecmp(mult, "ten")) {
4768 _mult = AST_AOC_MULT_TEN;
4769 } else if (!strcasecmp(mult, "hundred")) {
4770 _mult = AST_AOC_MULT_HUNDRED;
4771 } else if (!strcasecmp(mult, "thousand")) {
4772 _mult = AST_AOC_MULT_THOUSAND;
4773 } else {
4774 astman_send_error(s, m, "Invalid ChargeMultiplier");
4775 goto aocmessage_cleanup;
4776 }
4777 }
4778
4779 if (_ratetype == AST_AOC_RATE_TYPE_DURATION) {
4780 if (ast_strlen_zero(timescale)) {
4781 astman_send_error(s, m, "TimeScale unspecified, TimeScale is required when RateType is Duration.");
4782 goto aocmessage_cleanup;
4783 } else if (!strcasecmp(timescale, "onehundredthsecond")) {
4785 } else if (!strcasecmp(timescale, "onetenthsecond")) {
4787 } else if (!strcasecmp(timescale, "second")) {
4789 } else if (!strcasecmp(timescale, "tenseconds")) {
4791 } else if (!strcasecmp(timescale, "minute")) {
4793 } else if (!strcasecmp(timescale, "hour")) {
4794 _scale = AST_AOC_TIME_SCALE_HOUR;
4795 } else if (!strcasecmp(timescale, "day")) {
4796 _scale = AST_AOC_TIME_SCALE_DAY;
4797 } else {
4798 astman_send_error(s, m, "Invalid TimeScale");
4799 goto aocmessage_cleanup;
4800 }
4801
4802 if (ast_strlen_zero(time) || (sscanf(time, "%30u", &_time) != 1)) {
4803 astman_send_error(s, m, "Invalid Time, Time is a required when RateType is Duration");
4804 goto aocmessage_cleanup;
4805 }
4806
4807 if (!ast_strlen_zero(granularity)) {
4808 if ((sscanf(time, "%30u", &_granularity) != 1)) {
4809 astman_send_error(s, m, "Invalid Granularity");
4810 goto aocmessage_cleanup;
4811 }
4812
4813 if (ast_strlen_zero(granularitytimescale)) {
4814 astman_send_error(s, m, "Invalid GranularityTimeScale, GranularityTimeScale is a required when Granularity is specified");
4815 } else if (!strcasecmp(granularitytimescale, "onehundredthsecond")) {
4816 _granularity_time_scale = AST_AOC_TIME_SCALE_HUNDREDTH_SECOND;
4817 } else if (!strcasecmp(granularitytimescale, "onetenthsecond")) {
4818 _granularity_time_scale = AST_AOC_TIME_SCALE_TENTH_SECOND;
4819 } else if (!strcasecmp(granularitytimescale, "second")) {
4820 _granularity_time_scale = AST_AOC_TIME_SCALE_SECOND;
4821 } else if (!strcasecmp(granularitytimescale, "tenseconds")) {
4822 _granularity_time_scale = AST_AOC_TIME_SCALE_TEN_SECOND;
4823 } else if (!strcasecmp(granularitytimescale, "minute")) {
4824 _granularity_time_scale = AST_AOC_TIME_SCALE_MINUTE;
4825 } else if (!strcasecmp(granularitytimescale, "hour")) {
4826 _granularity_time_scale = AST_AOC_TIME_SCALE_HOUR;
4827 } else if (!strcasecmp(granularitytimescale, "day")) {
4828 _granularity_time_scale = AST_AOC_TIME_SCALE_DAY;
4829 } else {
4830 astman_send_error(s, m, "Invalid GranularityTimeScale");
4831 goto aocmessage_cleanup;
4832 }
4833 }
4834
4835 if (ast_strlen_zero(chargingtype) || strcasecmp(chargingtype, "continuouscharging") == 0) {
4836 _step = 0;
4837 } else if (strcasecmp(chargingtype, "stepfunction") == 0 ) {
4838 _step = 1;
4839 } else {
4840 astman_send_error(s, m, "Invalid ChargingType");
4841 goto aocmessage_cleanup;
4842 }
4843 }
4844
4845 if (_ratetype == AST_AOC_RATE_TYPE_VOLUME) {
4846 if (ast_strlen_zero(volumeunit)) {
4847 astman_send_error(s, m, "VolumeUnit unspecified, VolumeUnit is required when RateType is Volume.");
4848 goto aocmessage_cleanup;
4849 } else if (!strcasecmp(timescale, "octet")) {
4850 _volumeunit = AST_AOC_VOLUME_UNIT_OCTET;
4851 } else if (!strcasecmp(timescale, "segment")) {
4852 _volumeunit = AST_AOC_VOLUME_UNIT_SEGMENT;
4853 } else if (!strcasecmp(timescale, "message")) {
4854 _volumeunit = AST_AOC_VOLUME_UNIT_MESSAGE;
4855 }else {
4856 astman_send_error(s, m, "Invalid VolumeUnit");
4857 goto aocmessage_cleanup;
4858 }
4859 }
4860
4862 || _ratetype == AST_AOC_RATE_TYPE_SPECIAL_CODE) {
4863 if (ast_strlen_zero(code) || (sscanf(code, "%30u", &_code) != 1)) {
4864 astman_send_error(s, m, "Invalid Code, Code is a required when ChargedItem is SpecialArrangement and when RateType is SpecialCode");
4865 goto aocmessage_cleanup;
4866 }
4867 }
4868
4869 if (_chargeditem == AST_AOC_CHARGED_ITEM_SPECIAL_ARRANGEMENT) {
4870 ast_aoc_s_add_special_arrangement(decoded, _code);
4871 } else if (_ratetype == AST_AOC_RATE_TYPE_DURATION) {
4872 ast_aoc_s_add_rate_duration(decoded, _chargeditem, _currencyamount, _mult,
4873 currencyname, _time, _scale, _granularity, _granularity_time_scale, _step);
4874 } else if (_ratetype == AST_AOC_RATE_TYPE_FLAT) {
4875 ast_aoc_s_add_rate_flat(decoded, _chargeditem, _currencyamount, _mult,
4876 currencyname);
4877 } else if (_ratetype == AST_AOC_RATE_TYPE_VOLUME) {
4878 ast_aoc_s_add_rate_volume(decoded, _chargeditem, _volumeunit, _currencyamount,
4879 _mult, currencyname);
4880 } else if (_ratetype == AST_AOC_RATE_TYPE_SPECIAL_CODE) {
4881 ast_aoc_s_add_rate_special_charge_code(decoded, _chargeditem, _code);
4882 } else if (_ratetype == AST_AOC_RATE_TYPE_FREE) {
4883 ast_aoc_s_add_rate_free(decoded, _chargeditem, 0);
4884 } else if (_ratetype == AST_AOC_RATE_TYPE_FREE_FROM_BEGINNING) {
4885 ast_aoc_s_add_rate_free(decoded, _chargeditem, 1);
4886 } else if (_ratetype == AST_AOC_RATE_TYPE_NA) {
4887 ast_aoc_s_add_rate_na(decoded, _chargeditem);
4888 }
4889
4890 return 0;
4891
4892aocmessage_cleanup:
4893
4894 return -1;
4895}
4896
4898 const struct message *m)
4899{
4900 struct ast_aoc_decoded *decoded = NULL;
4901 int hdrlen;
4902 int x;
4903 static const char hdr[] = "ChargedItem:";
4904 struct message sm = { 0 };
4905 int rates = 0;
4906
4907 if (!(decoded = ast_aoc_create(AST_AOC_S, 0, 0))) {
4908 astman_send_error(s, m, "Message Creation Failed");
4909 goto aocmessage_cleanup;
4910 }
4911
4912 hdrlen = strlen(hdr);
4913 for (x = 0; x < m->hdrcount; x++) {
4914 if (strncasecmp(hdr, m->headers[x], hdrlen) == 0) {
4915 if (rates > ast_aoc_s_get_count(decoded)) {
4916 if (action_aoc_s_submessage(s, &sm, decoded) == -1) {
4917 goto aocmessage_cleanup;
4918 }
4919 }
4920 ++rates;
4921 }
4922
4923 sm.headers[sm.hdrcount] = m->headers[x];
4924 ++sm.hdrcount;
4925 }
4926 if (rates > ast_aoc_s_get_count(decoded)) {
4927 if (action_aoc_s_submessage(s, &sm, decoded) == -1) {
4928 goto aocmessage_cleanup;
4929 }
4930 }
4931
4932 return decoded;
4933
4934aocmessage_cleanup:
4935
4936 ast_aoc_destroy_decoded(decoded);
4937 return NULL;
4938}
4939
4940static int action_aocmessage(struct mansession *s, const struct message *m)
4941{
4942 const char *msgtype = astman_get_header(m, "MsgType");
4943 const char *channel = astman_get_header(m, "Channel");
4944 const char *pchannel = astman_get_header(m, "ChannelPrefix");
4945
4946 struct ast_channel *chan = NULL;
4947
4948 struct ast_aoc_decoded *decoded = NULL;
4949 struct ast_aoc_encoded *encoded = NULL;
4950 size_t encoded_size = 0;
4951
4952 if (ast_strlen_zero(channel) && ast_strlen_zero(pchannel)) {
4953 astman_send_error(s, m, "Channel and PartialChannel are not specified. Specify at least one of these.");
4954 goto aocmessage_cleanup;
4955 }
4956
4957 if (!(chan = ast_channel_get_by_name(channel)) && !ast_strlen_zero(pchannel)) {
4958 chan = ast_channel_get_by_name_prefix(pchannel, strlen(pchannel));
4959 }
4960
4961 if (!chan) {
4962 astman_send_error(s, m, "No such channel");
4963 goto aocmessage_cleanup;
4964 }
4965
4966 if (strcasecmp(msgtype, "d") == 0 || strcasecmp(msgtype, "e") == 0) {
4967 decoded = action_aoc_de_message(s, m);
4968 }
4969 else if (strcasecmp(msgtype, "s") == 0) {
4970 decoded = action_aoc_s_message(s, m);
4971 }
4972 else {
4973 astman_send_error(s, m, "Invalid MsgType");
4974 goto aocmessage_cleanup;
4975 }
4976
4977 if (!decoded) {
4978 goto aocmessage_cleanup;
4979 }
4980
4981 if ((encoded = ast_aoc_encode(decoded, &encoded_size, chan))
4982 && !ast_indicate_data(chan, AST_CONTROL_AOC, encoded, encoded_size)) {
4983 astman_send_ack(s, m, "AOC Message successfully queued on channel");
4984 } else {
4985 astman_send_error(s, m, "Error encoding AOC message, could not queue onto channel");
4986 }
4987
4988aocmessage_cleanup:
4989
4990 ast_aoc_destroy_decoded(decoded);
4991 ast_aoc_destroy_encoded(encoded);
4992
4993 if (chan) {
4994 chan = ast_channel_unref(chan);
4995 }
4996 return 0;
4997}
4998
5000 const char *search;
5002 int (*searchfn)(const char *app, const char *data, const char *search);
5003};
5004
5005/*!
5006 * \internal
5007 * \brief Check if the application is allowed for Originate
5008 *
5009 * \param app The "app" parameter
5010 * \param data The "appdata" parameter (ignored)
5011 * \param search The search string
5012 * \retval 1 Match
5013 * \retval 0 No match
5014 */
5015static int app_match(const char *app, const char *data, const char *search)
5016{
5017 /*
5018 * We use strcasestr so we don't have to trim any blanks
5019 * from the front or back of the string.
5020 */
5021 return !!(strcasestr(app, search));
5022}
5023
5024/*!
5025 * \internal
5026 * \brief Check if the appdata is allowed for Originate
5027 *
5028 * \param app The "app" parameter (ignored)
5029 * \param data The "appdata" parameter
5030 * \param search The search string
5031 * \retval 1 Match
5032 * \retval 0 No match
5033 */
5034static int appdata_match(const char *app, const char *data, const char *search)
5035{
5036 if (ast_strlen_zero(data)) {
5037 return 0;
5038 }
5039 return !!(strstr(data, search));
5040}
5041
5042/*!
5043 * \internal
5044 * \brief Check if the Queue application is allowed for Originate
5045 *
5046 * It's only allowed if there's no AGI parameter set
5047 *
5048 * \param app The "app" parameter
5049 * \param data The "appdata" parameter
5050 * \param search The search string
5051 * \retval 1 Match
5052 * \retval 0 No match
5053 */
5054static int queue_match(const char *app, const char *data, const char *search)
5055{
5056 char *parse;
5058 AST_APP_ARG(queuename);
5061 AST_APP_ARG(announceoverride);
5062 AST_APP_ARG(queuetimeoutstr);
5063 AST_APP_ARG(agi);
5064 AST_APP_ARG(gosub);
5066 AST_APP_ARG(position);
5067 );
5068
5069 if (!strcasestr(app, "queue") || ast_strlen_zero(data)) {
5070 return 0;
5071 }
5072
5073 parse = ast_strdupa(data);
5075
5076 /*
5077 * The Queue application is fine unless the AGI parameter is set.
5078 * If it is, we need to check the user's permissions.
5079 */
5080 return !ast_strlen_zero(args.agi);
5081}
5082
5083/*
5084 * The Originate application and application data are passed
5085 * to each searchfn in the list. If a searchfn returns true
5086 * and the user's permissions don't include the permissions specified
5087 * in the list entry, the Originate action will be denied.
5088 *
5089 * If no searchfn returns true, the Originate action is allowed.
5090 */
5092 /*
5093 * The app_match function checks if the search string is
5094 * anywhere in the app parameter. The check is case-insensitive.
5095 */
5096 { "agi", EVENT_FLAG_SYSTEM, app_match },
5097 { "dbdeltree", EVENT_FLAG_SYSTEM, app_match },
5098 { "exec", EVENT_FLAG_SYSTEM, app_match },
5099 { "externalivr", EVENT_FLAG_SYSTEM, app_match },
5100 { "mixmonitor", EVENT_FLAG_SYSTEM, app_match },
5101 { "originate", EVENT_FLAG_SYSTEM, app_match },
5102 { "reload", EVENT_FLAG_SYSTEM, app_match },
5103 { "system", EVENT_FLAG_SYSTEM, app_match },
5104 /*
5105 * Since the queue_match function specifically checks
5106 * for the presence of the AGI parameter, we'll allow
5107 * the call if the user has either the AGI or SYSTEM
5108 * permission.
5109 */
5111 /*
5112 * The appdata_match function checks if the search string is
5113 * anywhere in the appdata parameter. Unlike app_match,
5114 * the check is case-sensitive. These are generally
5115 * dialplan functions.
5116 */
5117 { "CURL", EVENT_FLAG_SYSTEM, appdata_match },
5119 { "EVAL", EVENT_FLAG_SYSTEM, appdata_match },
5120 { "FILE", EVENT_FLAG_SYSTEM, appdata_match },
5121 { "ODBC", EVENT_FLAG_SYSTEM, appdata_match },
5122 { "REALTIME", EVENT_FLAG_SYSTEM, appdata_match },
5123 { "SHELL", EVENT_FLAG_SYSTEM, appdata_match },
5124 { NULL, 0 },
5125};
5126
5127static int is_originate_app_permitted(const char *app, const char *data,
5128 int permission)
5129{
5130 int i;
5131
5132 for (i = 0; originate_app_permissions[i].search; i++) {
5133 if (originate_app_permissions[i].searchfn(app, data, originate_app_permissions[i].search)) {
5135 }
5136 }
5137
5138 return 1;
5139}
5140
5141#ifdef TEST_FRAMEWORK
5142#define ALL_PERMISSIONS (INT_MAX)
5143#define NO_PERMISSIONS (0)
5144AST_TEST_DEFINE(originate_permissions_test)
5145{
5147
5148 switch (cmd) {
5149 case TEST_INIT:
5150 info->name = "originate_permissions_test";
5151 info->category = "/main/manager/";
5152 info->summary = "Test permissions for originate action";
5153 info->description =
5154 "Make sure that dialplan apps/functions that need special "
5155 "permissions are prohibited if the user doesn't have the permission.";
5156 return AST_TEST_NOT_RUN;
5157 case TEST_EXECUTE:
5158 break;
5159 }
5160
5161 /*
5162 * Check application matching. We don't need to check every one.
5163 * The code is the same.
5164 */
5165
5166 ast_test_validate(test, is_originate_app_permitted("exec",
5167 NULL, EVENT_FLAG_SYSTEM), "exec permission check failed");
5168 ast_test_validate(test, is_originate_app_permitted("exec",
5169 NULL, EVENT_FLAG_SYSTEM | EVENT_FLAG_AGI), "exec check permission failed");
5170 ast_test_validate(test, is_originate_app_permitted("exec",
5171 NULL, ALL_PERMISSIONS), "exec check permission failed");
5172 ast_test_validate(test, !is_originate_app_permitted("exec",
5173 NULL, EVENT_FLAG_AGI), "exec permission check failed");
5174 ast_test_validate(test, !is_originate_app_permitted("exec",
5175 NULL, EVENT_FLAG_VERBOSE), "exec permission check failed");
5176 ast_test_validate(test, !is_originate_app_permitted("exec",
5177 NULL, NO_PERMISSIONS), "exec permission check failed");
5178
5179 /*
5180 * If queue is used with the AGI parameter but without the SYSTEM or AGI
5181 * permission, it should be denied. Queue param order:
5182 * queuename,options,url,announceoverride,queuetimeoutstr,AGI,gosub,rule,position
5183 * The values of the options aren't checked. They just have to be present.
5184 */
5185
5186 /* AGI not specified should always be allowed */
5187 ast_test_validate(test, is_originate_app_permitted("queue",
5188 NULL, NO_PERMISSIONS), "Queue permission check failed");
5189 ast_test_validate(test, is_originate_app_permitted("queue",
5190 "somequeue,CcdHh,someURL,tt-monkeys,100,,gosub,rule,666",
5191 EVENT_FLAG_ORIGINATE | EVENT_FLAG_HOOKRESPONSE ), "Queue permission check failed");
5192
5193 /* AGI specified with SYSTEM or AGI permission should be allowed */
5194 ast_test_validate(test, is_originate_app_permitted("queue",
5195 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5196 EVENT_FLAG_SYSTEM | EVENT_FLAG_HOOKRESPONSE ), "Queue permission check failed");
5197 ast_test_validate(test, is_originate_app_permitted("queue",
5198 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5199 EVENT_FLAG_AGI | EVENT_FLAG_HOOKRESPONSE ), "Queue permission check failed");
5200 ast_test_validate(test, is_originate_app_permitted("queue",
5201 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5202 ALL_PERMISSIONS), "Queue permission check failed");
5203
5204 /* AGI specified without SYSTEM or AGI permission should be denied */
5205 ast_test_validate(test, !is_originate_app_permitted("queue",
5206 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5207 NO_PERMISSIONS), "Queue permission check failed");
5208 ast_test_validate(test, !is_originate_app_permitted("queue",
5209 "somequeue,CcdHh,someURL,tt-monkeys,100,SomeAGIScript,gosub,rule,666",
5210 EVENT_FLAG_ORIGINATE | EVENT_FLAG_HOOKRESPONSE ), "Queue permission check failed");
5211
5212 /*
5213 * Check appdata. The function name can appear anywhere in appdata.
5214 */
5215 ast_test_validate(test, is_originate_app_permitted("someapp",
5216 "aaaDBbbb", EVENT_FLAG_SYSTEM), "exec permission check failed");
5217 ast_test_validate(test, is_originate_app_permitted("someapp",
5218 "aaa DB bbb", ALL_PERMISSIONS), "exec permission check failed");
5219 ast_test_validate(test, !is_originate_app_permitted("someapp",
5220 "aaaDBbbb", NO_PERMISSIONS), "exec permission check failed");
5221 ast_test_validate(test, !is_originate_app_permitted("someapp",
5222 "aaa DB bbb", NO_PERMISSIONS), "exec permission check failed");
5223 /* The check is case-sensitive so although DB is a match, db isn't. */
5224 ast_test_validate(test, is_originate_app_permitted("someapp",
5225 "aaa db bbb", NO_PERMISSIONS), "exec permission check failed");
5226
5227 return res;
5228}
5229#undef ALL_PERMISSIONS
5230#undef NO_PERMISSIONS
5231#endif
5232
5233static int action_originate(struct mansession *s, const struct message *m)
5234{
5235 const char *name = astman_get_header(m, "Channel");
5236 const char *exten = astman_get_header(m, "Exten");
5237 const char *context = astman_get_header(m, "Context");
5238 const char *priority = astman_get_header(m, "Priority");
5239 const char *timeout = astman_get_header(m, "Timeout");
5240 const char *callerid = astman_get_header(m, "CallerID");
5241 const char *account = astman_get_header(m, "Account");
5242 const char *app = astman_get_header(m, "Application");
5243 const char *appdata = astman_get_header(m, "Data");
5244 const char *async = astman_get_header(m, "Async");
5245 const char *id = astman_get_header(m, "ActionID");
5246 const char *codecs = astman_get_header(m, "Codecs");
5247 const char *early_media = astman_get_header(m, "Earlymedia");
5248 struct ast_assigned_ids assignedids = {
5249 .uniqueid = astman_get_header(m, "ChannelId"),
5250 .uniqueid2 = astman_get_header(m, "OtherChannelId"),
5251 };
5252 const char *gosub = astman_get_header(m, "PreDialGoSub");
5253
5254 struct ast_variable *vars = NULL;
5255 char *tech, *data;
5256 char *l = NULL, *n = NULL;
5257 int pi = 0;
5258 int res;
5259 int to = 30000;
5260 int reason = 0;
5261 char tmp[256];
5262 char tmp2[256];
5264 pthread_t th;
5265 int bridge_early = 0;
5266
5267 if (!cap) {
5268 astman_send_error(s, m, "Internal Error. Memory allocation failure.");
5269 return 0;
5270 }
5272
5273 if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid))
5274 || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) {
5275 astman_send_error_va(s, m, "Uniqueid length exceeds maximum of %d\n",
5277 res = 0;
5278 goto fast_orig_cleanup;
5279 }
5280
5281 if (ast_strlen_zero(name)) {
5282 astman_send_error(s, m, "Channel not specified");
5283 res = 0;
5284 goto fast_orig_cleanup;
5285 }
5286 if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
5287 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
5288 astman_send_error(s, m, "Invalid priority");
5289 res = 0;
5290 goto fast_orig_cleanup;
5291 }
5292 }
5293 if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%30d", &to) != 1)) {
5294 astman_send_error(s, m, "Invalid timeout");
5295 res = 0;
5296 goto fast_orig_cleanup;
5297 }
5298 ast_copy_string(tmp, name, sizeof(tmp));
5299 tech = tmp;
5300 data = strchr(tmp, '/');
5301 if (!data) {
5302 astman_send_error(s, m, "Invalid channel");
5303 res = 0;
5304 goto fast_orig_cleanup;
5305 }
5306 *data++ = '\0';
5307 ast_copy_string(tmp2, callerid, sizeof(tmp2));
5308 ast_callerid_parse(tmp2, &n, &l);
5309 if (n) {
5310 if (ast_strlen_zero(n)) {
5311 n = NULL;
5312 }
5313 }
5314 if (l) {
5316 if (ast_strlen_zero(l)) {
5317 l = NULL;
5318 }
5319 }
5320 if (!ast_strlen_zero(codecs)) {
5323 }
5324
5325 if (!ast_strlen_zero(app) && s->session) {
5326 if (!is_originate_app_permitted(app, appdata, s->session->writeperm)) {
5327 astman_send_error(s, m, "Originate Access Forbidden: app or data blacklisted");
5328 res = 0;
5329 goto fast_orig_cleanup;
5330 }
5331 }
5332
5333 /* Check early if the extension exists. If not, we need to bail out here. */
5334 if (exten && context && pi) {
5335 if (! ast_exists_extension(NULL, context, exten, pi, l)) {
5336 /* The extension does not exist. */
5337 astman_send_error(s, m, "Extension does not exist.");
5338 res = 0;
5339 goto fast_orig_cleanup;
5340 }
5341 }
5342
5343 /* Allocate requested channel variables */
5344 vars = astman_get_variables(m);
5345 if (s->session && s->session->chanvars) {
5346 struct ast_variable *v, *old;
5347 old = vars;
5348 vars = NULL;
5349
5350 /* The variables in the AMI originate action are appended at the end of the list, to override any user variables that apply */
5351
5353 if (old) {
5354 for (v = vars; v->next; v = v->next );
5355 v->next = old; /* Append originate variables at end of list */
5356 }
5357 }
5358
5359 /* For originate async - we can bridge in early media stage */
5360 bridge_early = ast_true(early_media);
5361
5362 if (ast_true(async)) {
5363 struct fast_originate_helper *fast;
5364
5365 fast = ast_calloc(1, sizeof(*fast));
5366 if (!fast || ast_string_field_init(fast, 252)) {
5367 ast_free(fast);
5369 res = -1;
5370 } else {
5371 if (!ast_strlen_zero(id)) {
5372 ast_string_field_build(fast, idtext, "ActionID: %s\r\n", id);
5373 }
5374 ast_string_field_set(fast, tech, tech);
5375 ast_string_field_set(fast, data, data);
5377 ast_string_field_set(fast, appdata, appdata);
5378 ast_string_field_set(fast, cid_num, l);
5379 ast_string_field_set(fast, cid_name, n);
5380 ast_string_field_set(fast, context, context);
5381 ast_string_field_set(fast, exten, exten);
5382 ast_string_field_set(fast, account, account);
5383 ast_string_field_set(fast, channelid, assignedids.uniqueid);
5384 ast_string_field_set(fast, otherchannelid, assignedids.uniqueid2);
5385 fast->vars = vars;
5386 fast->cap = cap;
5387 cap = NULL; /* transferred originate helper the capabilities structure. It is now responsible for freeing it. */
5388 fast->timeout = to;
5389 fast->early_media = bridge_early;
5390 fast->priority = pi;
5393 res = -1;
5394 } else {
5395 res = 0;
5396 }
5397 }
5398 } else if (!ast_strlen_zero(app)) {
5399 res = ast_pbx_outgoing_app(tech, cap, data, to, app, appdata, &reason,
5400 AST_OUTGOING_WAIT, l, n, vars, account, NULL,
5401 assignedids.uniqueid ? &assignedids : NULL);
5403 } else {
5404 if (exten && context && pi) {
5405 res = ast_pbx_outgoing_exten_predial(tech, cap, data, to,
5406 context, exten, pi, &reason, AST_OUTGOING_WAIT,
5407 l, n, vars, account, NULL, bridge_early,
5408 assignedids.uniqueid ? &assignedids : NULL , gosub);
5410 } else {
5411 astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
5413 res = 0;
5414 goto fast_orig_cleanup;
5415 }
5416 }
5417 if (!res) {
5418 astman_send_ack(s, m, "Originate successfully queued");
5419 } else {
5420 astman_send_error(s, m, "Originate failed");
5421 }
5422
5423fast_orig_cleanup:
5424 ao2_cleanup(cap);
5425 return 0;
5426}
5427
5428static int action_mailboxstatus(struct mansession *s, const struct message *m)
5429{
5430 const char *mailbox = astman_get_header(m, "Mailbox");
5431 int ret;
5432
5433 if (ast_strlen_zero(mailbox)) {
5434 astman_send_error(s, m, "Mailbox not specified");
5435 return 0;
5436 }
5437 ret = ast_app_has_voicemail(mailbox, NULL);
5438 astman_start_ack(s, m);
5439 astman_append(s, "Message: Mailbox Status\r\n"
5440 "Mailbox: %s\r\n"
5441 "Waiting: %d\r\n\r\n", mailbox, ret);
5442 return 0;
5443}
5444
5445static int action_mailboxcount(struct mansession *s, const struct message *m)
5446{
5447 const char *mailbox = astman_get_header(m, "Mailbox");
5448 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
5449
5450 if (ast_strlen_zero(mailbox)) {
5451 astman_send_error(s, m, "Mailbox not specified");
5452 return 0;
5453 }
5454 ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
5455 astman_start_ack(s, m);
5456 astman_append(s, "Message: Mailbox Message Count\r\n"
5457 "Mailbox: %s\r\n"
5458 "UrgMessages: %d\r\n"
5459 "NewMessages: %d\r\n"
5460 "OldMessages: %d\r\n"
5461 "\r\n",
5462 mailbox, urgentmsgs, newmsgs, oldmsgs);
5463 return 0;
5464}
5465
5466static int action_extensionstate(struct mansession *s, const struct message *m)
5467{
5468 const char *exten = astman_get_header(m, "Exten");
5469 const char *context = astman_get_header(m, "Context");
5470 char hint[256];
5471 int status;
5472
5473 if (ast_strlen_zero(exten)) {
5474 astman_send_error(s, m, "Extension not specified");
5475 return 0;
5476 }
5477 if (ast_strlen_zero(context)) {
5478 context = "default";
5479 }
5480 status = ast_extension_state(NULL, context, exten);
5481 hint[0] = '\0';
5482 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
5483 astman_start_ack(s, m);
5484 astman_append(s, "Message: Extension Status\r\n"
5485 "Exten: %s\r\n"
5486 "Context: %s\r\n"
5487 "Hint: %s\r\n"
5488 "Status: %d\r\n"
5489 "StatusText: %s\r\n"
5490 "\r\n",
5491 exten, context, hint, status,
5493 return 0;
5494}
5495
5496static int action_presencestate(struct mansession *s, const struct message *m)
5497{
5498 const char *provider = astman_get_header(m, "Provider");
5500 char *subtype;
5501 char *message;
5502
5504 astman_send_error(s, m, "No provider specified");
5505 return 0;
5506 }
5507
5509 if (state == AST_PRESENCE_INVALID) {
5510 astman_send_error_va(s, m, "Invalid provider %s or provider in invalid state", provider);
5511 return 0;
5512 }
5513
5514 astman_start_ack(s, m);
5515 astman_append(s, "Message: Presence State\r\n"
5516 "State: %s\r\n", ast_presence_state2str(state));
5517
5518 if (!ast_strlen_zero(subtype)) {
5519 astman_append(s, "Subtype: %s\r\n", subtype);
5520 }
5521
5522 if (!ast_strlen_zero(message)) {
5523 /* XXX The Message header here is deprecated as it
5524 * duplicates the action response header 'Message'.
5525 * Remove it in the next major revision of AMI.
5526 */
5527 astman_append(s, "Message: %s\r\n"
5528 "PresenceMessage: %s\r\n",
5529 message, message);
5530 }
5531 astman_append(s, "\r\n");
5532
5533 ast_free(subtype);
5535
5536 return 0;
5537}
5538
5539static int action_timeout(struct mansession *s, const struct message *m)
5540{
5541 struct ast_channel *c;
5542 const char *name = astman_get_header(m, "Channel");
5543 double timeout = atof(astman_get_header(m, "Timeout"));
5544 struct timeval when = { timeout, 0 };
5545
5546 if (ast_strlen_zero(name)) {
5547 astman_send_error(s, m, "No channel specified");
5548 return 0;
5549 }
5550
5551 if (!timeout || timeout < 0) {
5552 astman_send_error(s, m, "No timeout specified");
5553 return 0;
5554 }
5555
5556 if (!(c = ast_channel_get_by_name(name))) {
5557 astman_send_error(s, m, "No such channel");
5558 return 0;
5559 }
5560
5561 when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
5562
5567
5568 astman_send_ack(s, m, "Timeout Set");
5569
5570 return 0;
5571}
5572
5573/*!
5574 * \brief Test eventdata against a filter entry
5575 *
5576 * \param entry The event_filter entry to match with
5577 * \param eventdata The data to match against
5578 * \retval 0 if no match
5579 * \retval 1 if match
5580 */
5581static int match_eventdata(struct event_filter_entry *entry, const char *eventdata)
5582{
5583 switch(entry->match_type) {
5584 case FILTER_MATCH_REGEX:
5585 return regexec(entry->regex_filter, eventdata, 0, NULL, 0) == 0;
5587 return ast_begins_with(eventdata, entry->string_filter);
5589 return ast_ends_with(eventdata, entry->string_filter);
5591 return strstr(eventdata, entry->string_filter) != NULL;
5592 case FILTER_MATCH_EXACT:
5593 return strcmp(eventdata, entry->string_filter) == 0;
5594 case FILTER_MATCH_NONE:
5595 return 1;
5596 }
5597
5598 return 0;
5599}
5600
5601static int filter_cmp_fn(void *obj, void *arg, void *data, int flags)
5602{
5603 struct eventqent *eqe = arg;
5604 struct event_filter_entry *filter_entry = obj;
5605 char *line_buffer_start = NULL;
5606 char *line_buffer = NULL;
5607 char *line = NULL;
5608 int match = 0;
5609 int *result = data;
5610
5611 if (filter_entry->event_name_hash) {
5612 if (eqe->event_name_hash != filter_entry->event_name_hash) {
5613 goto done;
5614 }
5615 }
5616
5617 /* We're looking at the entire event data */
5618 if (!filter_entry->header_name) {
5619 match = match_eventdata(filter_entry, eqe->eventdata);
5620 goto done;
5621 }
5622
5623 /* We're looking at a specific header */
5624 line_buffer_start = ast_strdup(eqe->eventdata);
5625 line_buffer = line_buffer_start;
5626 if (!line_buffer_start) {
5627 goto done;
5628 }
5629
5630 while ((line = ast_read_line_from_buffer(&line_buffer))) {
5631 if (ast_begins_with(line, filter_entry->header_name)) {
5632 line += strlen(filter_entry->header_name);
5633 line = ast_skip_blanks(line);
5634 if (ast_strlen_zero(line)) {
5635 continue;
5636 }
5637 match = match_eventdata(filter_entry, line);
5638 if (match) {
5639 ast_free(line_buffer_start);
5640 line_buffer_start = NULL;
5641 break;
5642 }
5643 }
5644 }
5645
5646 ast_free(line_buffer_start);
5647
5648done:
5649
5650 *result = match;
5651 return match ? CMP_MATCH | CMP_STOP : 0;
5652}
5653
5654static int should_send_event(struct ao2_container *includefilters,
5655 struct ao2_container *excludefilters, struct eventqent *eqe)
5656{
5657 int result = 0;
5658
5659 if (manager_debug) {
5660 ast_verbose("<-- Examining AMI event (%u): -->\n%s\n", eqe->event_name_hash, eqe->eventdata);
5661 } else {
5662 ast_debug(4, "Examining AMI event (%u):\n%s\n", eqe->event_name_hash, eqe->eventdata);
5663 }
5664 if (!ao2_container_count(includefilters) && !ao2_container_count(excludefilters)) {
5665 return 1; /* no filtering means match all */
5666 } else if (ao2_container_count(includefilters) && !ao2_container_count(excludefilters)) {
5667 /* include filters only: implied exclude all filter processed first, then include filters */
5668 ao2_t_callback_data(includefilters, OBJ_NODATA, filter_cmp_fn, eqe, &result, "find filter in includefilters container");
5669 return result;
5670 } else if (!ao2_container_count(includefilters) && ao2_container_count(excludefilters)) {
5671 /* exclude filters only: implied include all filter processed first, then exclude filters */
5672 ao2_t_callback_data(excludefilters, OBJ_NODATA, filter_cmp_fn, eqe, &result, "find filter in excludefilters container");
5673 return !result;
5674 } else {
5675 /* include and exclude filters: implied exclude all filter processed first, then include filters, and lastly exclude filters */
5676 ao2_t_callback_data(includefilters, OBJ_NODATA, filter_cmp_fn, eqe, &result, "find filter in session filter container");
5677 if (result) {
5678 result = 0;
5679 ao2_t_callback_data(excludefilters, OBJ_NODATA, filter_cmp_fn, eqe, &result, "find filter in session filter container");
5680 return !result;
5681 }
5682 }
5683
5684 return result;
5685}
5686
5687/*!
5688 * \brief Manager command to add an event filter to a manager session
5689 * \see For more details look at manager_add_filter
5690 */
5691static int action_filter(struct mansession *s, const struct message *m)
5692{
5693 const char *match_criteria = astman_get_header(m, "MatchCriteria");
5694 const char *filter = astman_get_header(m, "Filter");
5695 const char *operation = astman_get_header(m, "Operation");
5696 int res;
5697
5698 if (!strcasecmp(operation, "Add")) {
5699 char *criteria;
5700 int have_match = !ast_strlen_zero(match_criteria);
5701
5702 /* Create an eventfilter expression.
5703 * eventfilter[(match_criteria)]
5704 */
5705 res = ast_asprintf(&criteria, "eventfilter%s%s%s",
5706 S_COR(have_match, "(", ""), S_OR(match_criteria, ""),
5707 S_COR(have_match, ")", ""));
5708 if (res <= 0) {
5709 astman_send_error(s, m, "Internal Error. Failed to allocate storage for filter type");
5710 return 0;
5711 }
5712
5714 ast_free(criteria);
5715 if (res != FILTER_SUCCESS) {
5716 if (res == FILTER_ALLOC_FAILED) {
5717 astman_send_error(s, m, "Internal Error. Failed to allocate regex for filter");
5718 return 0;
5719 } else if (res == FILTER_COMPILE_FAIL) {
5720 astman_send_error(s, m,
5721 "Filter did not compile. Check the syntax of the filter given.");
5722 return 0;
5723 } else if (res == FILTER_FORMAT_ERROR) {
5724 astman_send_error(s, m,
5725 "Filter was formatted incorrectly. Check the syntax of the filter given.");
5726 return 0;
5727 } else {
5728 astman_send_error(s, m, "Internal Error. Failed adding filter.");
5729 return 0;
5730 }
5731 }
5732
5733 astman_send_ack(s, m, "Success");
5734 return 0;
5735 }
5736
5737 astman_send_error(s, m, "Unknown operation");
5738 return 0;
5739}
5740
5741/*!
5742 * \brief Add an event filter to a manager session
5743 *
5744 * \param criteria See examples in manager.conf.sample
5745 * \param filter_pattern Filter pattern
5746 * \param includefilters, excludefilters
5747 *
5748 * \return FILTER_ALLOC_FAILED Memory allocation failure
5749 * \return FILTER_COMPILE_FAIL If the filter did not compile
5750 * \return FILTER_FORMAT_ERROR If the criteria weren't formatted correctly
5751 * \return FILTER_SUCCESS Success
5752 *
5753 *
5754 * Examples:
5755 * See examples in manager.conf.sample
5756 *
5757 */
5759 const char *criteria, const char *filter_pattern,
5760 struct ao2_container *includefilters, struct ao2_container *excludefilters)
5761{
5762 RAII_VAR(struct event_filter_entry *, filter_entry,
5763 ao2_t_alloc(sizeof(*filter_entry), event_filter_destructor, "event_filter allocation"),
5764 ao2_cleanup);
5765 const char *options_start = NULL;
5766 SCOPE_ENTER(3, "manager_add_filter(%s, %s, %p, %p)", criteria, filter_pattern, includefilters, excludefilters);
5767
5768 if (!filter_entry) {
5769 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_ALLOC_FAILED, LOG_WARNING, "Unable to allocate filter_entry");
5770 }
5771
5772 /*
5773 * At a minimum, criteria must be "eventfilter" but may contain additional
5774 * constraints.
5775 */
5776 if (ast_strlen_zero(criteria)) {
5778 }
5779
5780 /*
5781 * filter_pattern could be empty but it should never be NULL.
5782 */
5783 if (!filter_pattern) {
5784 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "Filter pattern was NULL");
5785 }
5786
5787 /*
5788 * For a legacy filter, if the first character of filter_pattern is
5789 * '!' then it's an exclude filter. It's also accepted as an alternative
5790 * to specifying "action(exclude)" for an advanced filter. If
5791 * "action" is specified however, it will take precedence.
5792 */
5793 if (filter_pattern[0] == '!') {
5794 filter_entry->is_excludefilter = 1;
5795 filter_pattern++;
5796 }
5797
5798 /*
5799 * This is the default
5800 */
5801 filter_entry->match_type = FILTER_MATCH_REGEX;
5802
5803 /*
5804 * If the criteria has a '(' in it, then it's an advanced filter.
5805 */
5806 options_start = strstr(criteria, "(");
5807
5808 /*
5809 * If it's a legacy filter, there MUST be a filter pattern.
5810 */
5811 if (!options_start && ast_strlen_zero(filter_pattern)) {
5813 "'%s = %s': Legacy filter with no filter pattern specified\n",
5814 criteria, filter_pattern);
5815 }
5816
5817 if (options_start) {
5818 /*
5819 * This is an advanced filter
5820 */
5821 char *temp = ast_strdupa(options_start + 1); /* skip over the leading '(' */
5822 char *saveptr = NULL;
5823 char *option = NULL;
5824 enum found_options {
5825 action_found = (1 << 0),
5826 name_found = (1 << 1),
5827 header_found = (1 << 2),
5828 method_found = (1 << 3),
5829 };
5830 enum found_options options_found = 0;
5831
5832 filter_entry->match_type = FILTER_MATCH_NONE;
5833
5834 ast_strip(temp);
5835 if (ast_strlen_zero(temp) || !ast_ends_with(temp, ")")) {
5837 "'%s = %s': Filter options not formatted correctly\n",
5838 criteria, filter_pattern);
5839 }
5840
5841 /*
5842 * These can actually be in any order...
5843 * action(include|exclude),name(<event_name>),header(<header_name>),method(<match_method>)
5844 * At least one of action, name, or header is required.
5845 */
5846 while ((option = strtok_r(temp, " ,)", &saveptr))) {
5847 if (!strncmp(option, "action", 6)) {
5848 char *method = strstr(option, "(");
5849 if (ast_strlen_zero(method)) {
5850 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'action' parameter not formatted correctly\n",
5851 criteria, filter_pattern);
5852 }
5853 method++;
5855 if (!strcmp(method, "include")) {
5856 filter_entry->is_excludefilter = 0;
5857 } else if (!strcmp(method, "exclude")) {
5858 filter_entry->is_excludefilter = 1;
5859 } else {
5860 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'action' option '%s' is unknown\n",
5861 criteria, filter_pattern, method);
5862 }
5863 options_found |= action_found;
5864 } else if (!strncmp(option, "name", 4)) {
5865 char *event_name = strstr(option, "(");
5866 event_name++;
5867 ast_strip(event_name);
5868 if (ast_strlen_zero(event_name)) {
5869 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'name' parameter not formatted correctly\n",
5870 criteria, filter_pattern);
5871 }
5872 filter_entry->event_name = ast_strdup(event_name);
5873 filter_entry->event_name_hash = ast_str_hash(event_name);
5874 options_found |= name_found;
5875 } else if (!strncmp(option, "header", 6)) {
5876 char *header_name = strstr(option, "(");
5877 header_name++;
5878 ast_strip(header_name);
5879 if (ast_strlen_zero(header_name)) {
5880 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'header' parameter not formatted correctly\n",
5881 criteria, filter_pattern);
5882 }
5883 if (!ast_ends_with(header_name, ":")) {
5884 filter_entry->header_name = ast_malloc(strlen(header_name) + 2);
5885 if (!filter_entry->header_name) {
5886 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_ALLOC_FAILED, LOG_ERROR, "Unable to allocate memory for header_name");
5887 }
5888 sprintf(filter_entry->header_name, "%s:", header_name); /* Safe */
5889 } else {
5890 filter_entry->header_name = ast_strdup(header_name);
5891 }
5892 options_found |= header_found;
5893 } else if (!strncmp(option, "method", 6)) {
5894 char *method = strstr(option, "(");
5895 method++;
5897 if (ast_strlen_zero(method)) {
5898 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'method' parameter not formatted correctly\n",
5899 criteria, filter_pattern);
5900 }
5901 if (!strcmp(method, "regex")) {
5902 filter_entry->match_type = FILTER_MATCH_REGEX;
5903 } else if (!strcmp(method, "exact")) {
5904 filter_entry->match_type = FILTER_MATCH_EXACT;
5905 } else if (!strcmp(method, "starts_with")) {
5906 filter_entry->match_type = FILTER_MATCH_STARTS_WITH;
5907 } else if (!strcmp(method, "ends_with")) {
5908 filter_entry->match_type = FILTER_MATCH_ENDS_WITH;
5909 } else if (!strcmp(method, "contains")) {
5910 filter_entry->match_type = FILTER_MATCH_CONTAINS;
5911 } else if (!strcmp(method, "none")) {
5912 filter_entry->match_type = FILTER_MATCH_NONE;
5913 } else {
5914 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': 'method' option '%s' is unknown\n",
5915 criteria, filter_pattern, method);
5916 }
5917 options_found |= method_found;
5918 } else {
5919 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_FORMAT_ERROR, LOG_WARNING, "'%s = %s': Filter option '%s' is unknown\n",
5920 criteria, filter_pattern, option);
5921 }
5922 temp = NULL;
5923 }
5924 if (!options_found) {
5926 "'%s = %s': No action, name, header, or method option found\n",
5927 criteria, filter_pattern);
5928 }
5929 if (ast_strlen_zero(filter_pattern) && filter_entry->match_type != FILTER_MATCH_NONE) {
5931 "'%s = %s': method can't be '%s' with no filter pattern\n",
5932 criteria, filter_pattern, match_type_names[filter_entry->match_type]);
5933 }
5934 if (!ast_strlen_zero(filter_pattern) && filter_entry->match_type == FILTER_MATCH_NONE) {
5936 "'%s = %s': method can't be 'none' with a filter pattern\n",
5937 criteria, filter_pattern);
5938 }
5939 if (!(options_found & name_found) && !(options_found & header_found) &&
5940 filter_entry->match_type == FILTER_MATCH_NONE) {
5942 "'%s = %s': No name or header option found and no filter pattern\n",
5943 criteria, filter_pattern);
5944 }
5945 }
5946
5947 if (!ast_strlen_zero(filter_pattern)) {
5948 if (filter_entry->match_type == FILTER_MATCH_REGEX) {
5949 filter_entry->regex_filter = ast_calloc(1, sizeof(regex_t));
5950 if (!filter_entry->regex_filter) {
5951 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_ALLOC_FAILED, LOG_ERROR, "Unable to allocate memory for regex_filter");
5952 }
5953 if (regcomp(filter_entry->regex_filter, filter_pattern, REG_EXTENDED | REG_NOSUB)) {
5954 SCOPE_EXIT_LOG_RTN_VALUE(FILTER_COMPILE_FAIL, LOG_WARNING, "Unable to compile regex filter for '%s'", filter_pattern);
5955 }
5956 } else {
5957 filter_entry->string_filter = ast_strdup(filter_pattern);
5958 }
5959 }
5960
5961 ast_debug(2, "Event filter:\n"
5962 "conf entry: %s = %s\n"
5963 "event_name: %s (hash: %d)\n"
5964 "test_header: %s\n"
5965 "match_type: %s\n"
5966 "regex_filter: %p\n"
5967 "string filter: %s\n"
5968 "is excludefilter: %d\n",
5969 criteria, filter_pattern,
5970 S_OR(filter_entry->event_name, "<not used>"),
5971 filter_entry->event_name_hash,
5972 S_OR(filter_entry->header_name, "<not used>"),
5973 match_type_names[filter_entry->match_type],
5974 filter_entry->regex_filter,
5975 filter_entry->string_filter,
5976 filter_entry->is_excludefilter);
5977
5978 if (filter_entry->is_excludefilter) {
5979 ao2_t_link(excludefilters, filter_entry, "link new filter into exclude user container");
5980 } else {
5981 ao2_t_link(includefilters, filter_entry, "link new filter into include user container");
5982 }
5983
5984 SCOPE_EXIT_RTN_VALUE(FILTER_SUCCESS, "Filter added successfully");
5985}
5986
5987#ifdef TEST_FRAMEWORK
5988
5989struct test_filter_data {
5990 const char *criteria;
5991 const char *filter;
5992 enum add_filter_result expected_add_filter_result;
5993 struct event_filter_entry expected_filter_entry;
5994 const char *test_event_name;
5995 const char *test_event_payload;
5996 int expected_should_send_event;
5997};
5998
5999static char *add_filter_result_enums[] = {
6000 [FILTER_SUCCESS] = "FILTER_SUCCESS",
6001 [FILTER_ALLOC_FAILED] = "FILTER_ALLOC_FAILED",
6002 [FILTER_COMPILE_FAIL] = "FILTER_COMPILE_FAIL",
6003 [FILTER_FORMAT_ERROR] = "FILTER_FORMAT_ERROR",
6004};
6005
6006#define TEST_EVENT_NEWCHANNEL "Newchannel", "Event: Newchannel\r\nChannel: XXX\r\nSomeheader: YYY\r\n"
6007#define TEST_EVENT_VARSET "VarSet", "Event: VarSet\r\nChannel: ABC\r\nSomeheader: XXX\r\n"
6008#define TEST_EVENT_NONE "", ""
6009
6010static struct test_filter_data parsing_filter_tests[] = {
6011 /* Valid filters */
6012 { "eventfilter", "XXX", FILTER_SUCCESS,
6013 { FILTER_MATCH_REGEX, NULL, NULL, NULL, 0, NULL, 0}, TEST_EVENT_NEWCHANNEL, 1},
6014 { "eventfilter", "!XXX", FILTER_SUCCESS,
6015 { FILTER_MATCH_REGEX, NULL, NULL, NULL, 0, NULL, 1}, TEST_EVENT_VARSET, 0},
6016 { "eventfilter(name(VarSet),method(none))", "", FILTER_SUCCESS,
6017 { FILTER_MATCH_NONE, NULL, NULL, "VarSet", 0, NULL, 0}, TEST_EVENT_VARSET, 1},
6018 { "eventfilter(name(Newchannel),method(regex))", "X[XYZ]X", FILTER_SUCCESS,
6019 { FILTER_MATCH_REGEX, NULL, NULL, "Newchannel", 0, NULL, 0}, TEST_EVENT_NEWCHANNEL, 1},
6020 { "eventfilter(name(Newchannel),method(regex))", "X[abc]X", FILTER_SUCCESS,
6021 { FILTER_MATCH_REGEX, NULL, NULL, "Newchannel", 0, NULL, 0}, TEST_EVENT_NEWCHANNEL, 0},
6022 { "eventfilter(action(exclude),name(Newchannel),method(regex))", "X[XYZ]X", FILTER_SUCCESS,
6023 { FILTER_MATCH_REGEX, NULL, NULL, "Newchannel", 0, NULL, 1}, TEST_EVENT_NEWCHANNEL, 0},
6024 { "eventfilter(action(exclude),name(Newchannel),method(regex))", "X[abc]X", FILTER_SUCCESS,
6025 { FILTER_MATCH_REGEX, NULL, NULL, "Newchannel", 0, NULL, 1}, TEST_EVENT_NEWCHANNEL, 1},
6026 { "eventfilter(action(include),name(VarSet),header(Channel),method(starts_with))", "AB", FILTER_SUCCESS,
6027 { FILTER_MATCH_STARTS_WITH, NULL, NULL, "VarSet", 0, "Channel:", 0}, TEST_EVENT_VARSET, 1},
6028 { "eventfilter(action(include),name(VarSet),header(Channel),method(ends_with))", "BC", FILTER_SUCCESS,
6029 { FILTER_MATCH_ENDS_WITH, NULL, NULL, "VarSet", 0, "Channel:", 0}, TEST_EVENT_VARSET, 1},
6030 { "eventfilter(action(include),name(VarSet),header(Channel),method(exact))", "ABC", FILTER_SUCCESS,
6031 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Channel:", 0}, TEST_EVENT_VARSET, 1},
6032 { "eventfilter(action(include),name(VarSet),header(Channel),method(exact))", "XXX", FILTER_SUCCESS,
6033 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Channel:", 0}, TEST_EVENT_VARSET, 0},
6034 { "eventfilter(name(VarSet),header(Channel),method(exact))", "!ZZZ", FILTER_SUCCESS,
6035 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Channel:", 1}, TEST_EVENT_VARSET, 1},
6036 { "eventfilter(action(exclude),name(VarSet),header(Channel),method(exact))", "ZZZ", FILTER_SUCCESS,
6037 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Channel:", 1}, TEST_EVENT_VARSET, 1},
6038 { "eventfilter(action(include),name(VarSet),header(Someheader),method(exact))", "!XXX", FILTER_SUCCESS,
6039 { FILTER_MATCH_EXACT, NULL, NULL, "VarSet", 0, "Someheader:", 0}, TEST_EVENT_VARSET, 1},
6040
6041 /* Invalid filters */
6042 { "eventfilter(action(include)", "", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6043 { "eventfilter(action(inlude)", "", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6044 { "eventfilter(nnnn(yyy)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6045 { "eventfilter(eader(VarSet)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6046 { "eventfilter(ethod(contains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6047 { "eventfilter(nnnn(yyy),header(VarSet),method(contains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6048 { "eventfilter(name(yyy),heder(VarSet),method(contains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6049 { "eventfilter(name(yyy),header(VarSet),mehod(contains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6050 { "eventfilter(name(yyy),header(VarSet),method(coains)", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6051 { "eventfilter(method(yyy))", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6052 { "eventfilter", "", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6053 { "eventfilter", "!", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6054 { "eventfilter()", "XXX", FILTER_FORMAT_ERROR, { 0, }, TEST_EVENT_NONE, 0},
6055 { "eventfilter", "XX[X", FILTER_COMPILE_FAIL, { 0, }, TEST_EVENT_NONE, 0},
6056 { "eventfilter(method(regex))", "XX[X", FILTER_COMPILE_FAIL, { 0, }, TEST_EVENT_NONE, 0},
6057};
6058
6059/*
6060 * This is a bit different than ast_strings_equal in that
6061 * it will return 1 if both strings are NULL.
6062 */
6063static int strings_equal(const char *str1, const char *str2)
6064{
6065 if ((!str1 && str2) || (str1 && !str2)) {
6066 return 0;
6067 }
6068
6069 return str1 == str2 || !strcmp(str1, str2);
6070}
6071
6072AST_TEST_DEFINE(eventfilter_test_creation)
6073{
6075 RAII_VAR(struct ao2_container *, includefilters, NULL, ao2_cleanup);
6076 RAII_VAR(struct ao2_container *, excludefilters, NULL, ao2_cleanup);
6077 int i = 0;
6078
6079 switch (cmd) {
6080 case TEST_INIT:
6081 info->name = "eventfilter_test_creation";
6082 info->category = "/main/manager/";
6083 info->summary = "Test eventfilter creation";
6084 info->description =
6085 "This creates various eventfilters and tests to make sure they were created successfully.";
6086 return AST_TEST_NOT_RUN;
6087 case TEST_EXECUTE:
6088 break;
6089 }
6090
6093 if (!includefilters || !excludefilters) {
6094 ast_test_status_update(test, "Failed to allocate filter containers.\n");
6095 return AST_TEST_FAIL;
6096 }
6097
6098 for (i = 0; i < ARRAY_LEN(parsing_filter_tests); i++) {
6099 struct event_filter_entry *filter_entry;
6100 enum add_filter_result add_filter_res;
6101 int send_event = 0;
6102 struct eventqent *eqe = NULL;
6103 int include_container_count = 0;
6104 int exclude_container_count = 0;
6105
6106 /* We need to clear the containers before each test */
6107 ao2_callback(includefilters, OBJ_UNLINK | OBJ_NODATA, NULL, NULL);
6108 ao2_callback(excludefilters, OBJ_UNLINK | OBJ_NODATA, NULL, NULL);
6109
6110 add_filter_res = manager_add_filter(parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6111 includefilters, excludefilters);
6112
6113 /* If you're adding a new test, enable this to see the full results */
6114#if 0
6115 ast_test_debug(test, "Add filter result '%s = %s': Expected: %s Actual: %s %s\n",
6116 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6117 add_filter_result_enums[parsing_filter_tests[i].expected_add_filter_result],
6118 add_filter_result_enums[add_filter_res],
6119 add_filter_res != parsing_filter_tests[i].expected_add_filter_result ? "FAIL" : "PASS");
6120#endif
6121
6122 if (add_filter_res != parsing_filter_tests[i].expected_add_filter_result) {
6124 "Unexpected add filter result '%s = %s'. Expected result: %s Actual result: %s\n",
6125 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6126 add_filter_result_enums[parsing_filter_tests[i].expected_add_filter_result],
6127 add_filter_result_enums[add_filter_res]);
6128 res = AST_TEST_FAIL;
6129 continue;
6130 }
6131
6132 if (parsing_filter_tests[i].expected_add_filter_result != FILTER_SUCCESS) {
6133 /*
6134 * We don't need to test filters that we know aren't going
6135 * to be parsed successfully.
6136 */
6137 continue;
6138 }
6139
6140 /* We need to set the event name hash on the test data */
6141 if (parsing_filter_tests[i].expected_filter_entry.event_name) {
6142 parsing_filter_tests[i].expected_filter_entry.event_name_hash =
6143 ast_str_hash(parsing_filter_tests[i].expected_filter_entry.event_name);
6144 }
6145
6146 include_container_count = ao2_container_count(includefilters);
6147 exclude_container_count = ao2_container_count(excludefilters);
6148
6149 if (parsing_filter_tests[i].expected_filter_entry.is_excludefilter) {
6150 if (exclude_container_count != 1 || include_container_count != 0) {
6152 "Invalid container counts for exclude filter '%s = %s'. Exclude: %d Include: %d. Should be 1 and 0\n",
6153 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6154 exclude_container_count, include_container_count);
6155 res = AST_TEST_FAIL;
6156 continue;
6157 }
6158 /* There can only be one entry in the container so ao2_find is fine */
6159 filter_entry = ao2_find(excludefilters, NULL, OBJ_SEARCH_OBJECT);
6160 } else {
6161 if (include_container_count != 1 || exclude_container_count != 0) {
6163 "Invalid container counts for include filter '%s = %s'. Include: %d Exclude: %d. Should be 1 and 0\n",
6164 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6165 include_container_count, exclude_container_count);
6166 res = AST_TEST_FAIL;
6167 continue;
6168 }
6169 /* There can only be one entry in the container so ao2_find is fine */
6170 filter_entry = ao2_find(includefilters, NULL, OBJ_SEARCH_OBJECT);
6171 }
6172
6173 if (!filter_entry) {
6175 "Failed to find filter entry for '%s = %s' in %s filter container\n",
6176 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6177 parsing_filter_tests[i].expected_filter_entry.is_excludefilter ? "exclude" : "include");
6178 res = AST_TEST_FAIL;
6179 goto loop_cleanup;
6180 }
6181
6182 if (filter_entry->match_type != parsing_filter_tests[i].expected_filter_entry.match_type) {
6184 "Failed to match filter type for '%s = %s'. Expected: %s Actual: %s\n",
6185 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6186 match_type_names[parsing_filter_tests[i].expected_filter_entry.match_type],
6187 match_type_names[filter_entry->match_type]);
6188 res = AST_TEST_FAIL;
6189 goto loop_cleanup;
6190 }
6191
6192 if (!strings_equal(filter_entry->event_name, parsing_filter_tests[i].expected_filter_entry.event_name)) {
6194 "Failed to match event name for '%s = %s'. Expected: '%s' Actual: '%s'\n",
6195 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6196 parsing_filter_tests[i].expected_filter_entry.event_name, filter_entry->event_name);
6197 res = AST_TEST_FAIL;
6198 goto loop_cleanup;
6199 }
6200
6201 if (filter_entry->event_name_hash != parsing_filter_tests[i].expected_filter_entry.event_name_hash) {
6203 "Event name hashes failed to match for '%s = %s'. Expected: %u Actual: %u\n",
6204 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6205 parsing_filter_tests[i].expected_filter_entry.event_name_hash, filter_entry->event_name_hash);
6206 res = AST_TEST_FAIL;
6207 goto loop_cleanup;
6208 }
6209
6210 if (!strings_equal(filter_entry->header_name, parsing_filter_tests[i].expected_filter_entry.header_name)) {
6212 "Failed to match header name for '%s = %s'. Expected: '%s' Actual: '%s'\n",
6213 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6214 parsing_filter_tests[i].expected_filter_entry.header_name, filter_entry->header_name);
6215 res = AST_TEST_FAIL;
6216 goto loop_cleanup;
6217 }
6218
6219 switch (parsing_filter_tests[i].expected_filter_entry.match_type) {
6220 case FILTER_MATCH_REGEX:
6221 if (!filter_entry->regex_filter) {
6223 "Failed to compile regex filter for '%s = %s'\n",
6224 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter);
6225 res = AST_TEST_FAIL;
6226 goto loop_cleanup;
6227 }
6228 break;
6229 case FILTER_MATCH_NONE:
6230 if (filter_entry->regex_filter || !ast_strlen_zero(filter_entry->string_filter)) {
6232 "Unexpected regex filter or string for '%s = %s' with match_type 'none'\n",
6233 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter);
6234 res = AST_TEST_FAIL;
6235 goto loop_cleanup;
6236 }
6237 break;
6241 case FILTER_MATCH_EXACT:
6242 if (filter_entry->regex_filter || ast_strlen_zero(filter_entry->string_filter)) {
6244 "Unexpected regex filter or empty string for '%s = %s' with match_type '%s'\n",
6245 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6246 match_type_names[parsing_filter_tests[i].expected_filter_entry.match_type]);
6247 res = AST_TEST_FAIL;
6248 goto loop_cleanup;
6249 }
6250 break;
6251 default:
6252 res = AST_TEST_FAIL;
6253 goto loop_cleanup;
6254 }
6255
6256 /*
6257 * This is a basic test of whether a single event matches a single filter.
6258 */
6259 eqe = ast_calloc(1, sizeof(*eqe) + strlen(parsing_filter_tests[i].test_event_payload) + 1);
6260 if (!eqe) {
6261 ast_test_status_update(test, "Failed to allocate eventqent\n");
6262 res = AST_TEST_FAIL;
6263 ao2_ref(filter_entry, -1);
6264 break;
6265 }
6266 strcpy(eqe->eventdata, parsing_filter_tests[i].test_event_payload); /* Safe */
6267 eqe->event_name_hash = ast_str_hash(parsing_filter_tests[i].test_event_name);
6268 send_event = should_send_event(includefilters, excludefilters, eqe);
6269 if (send_event != parsing_filter_tests[i].expected_should_send_event) {
6270 char *escaped = ast_escape_c_alloc(parsing_filter_tests[i].test_event_payload);
6272 "Should send event failed to match for '%s = %s' payload '%s'. Expected: %s Actual: %s\n",
6273 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter, escaped,
6274 AST_YESNO(parsing_filter_tests[i].expected_should_send_event), AST_YESNO(send_event));
6275 ast_free(escaped);
6276 res = AST_TEST_FAIL;
6277 }
6278loop_cleanup:
6279 ast_free(eqe);
6280 ao2_cleanup(filter_entry);
6281
6282 }
6283 ast_test_status_update(test, "Tested %d filters\n", i);
6284
6285 return res;
6286}
6287
6288struct test_filter_matching {
6289 const char *criteria;
6290 const char *pattern;
6291};
6292
6293/*
6294 * These filters are used to test the precedence of include and exclude
6295 * filters. When there are both include and exclude filters, the include
6296 * filters are matched first. If the event doesn't match an include filter,
6297 * it's discarded. If it does match, the exclude filter list is searched and
6298 * if a match is found, the event is discarded.
6299 */
6300
6301/*
6302 * The order of the filters in the array doesn't really matter. The
6303 * include and exclude filters are in separate containers and in each
6304 * container, traversal stops when a match is found.
6305 */
6306static struct test_filter_matching filters_for_matching[] = {
6307 { "eventfilter(name(VarSet),method(none))", ""},
6308 { "eventfilter(name(Newchannel),method(regex))", "X[XYZ]X"},
6309 { "eventfilter(name(Newchannel),method(regex))", "X[abc]X"},
6310 { "eventfilter(name(Newchannel),header(Someheader),method(regex))", "ZZZ"},
6311 { "eventfilter(action(exclude),name(Newchannel),method(regex))", "X[a]X"},
6312 { "eventfilter(action(exclude),name(Newchannel),method(regex))", "X[Z]X"},
6313 { "eventfilter(action(exclude),name(VarSet),header(Channel),method(regex))", "YYY"},
6314};
6315
6316struct test_event_matching{
6317 const char *event_name;
6318 const char *payload;
6319 int expected_should_send_event;
6320};
6321
6322static struct test_event_matching events_for_matching[] = {
6323 { "Newchannel", "Event: Newchannel\r\nChannel: XXX\r\nSomeheader: YYY\r\n", 1 },
6324 { "Newchannel", "Event: Newchannel\r\nChannel: XZX\r\nSomeheader: YYY\r\n", 0 },
6325 { "Newchannel", "Event: Newchannel\r\nChannel: XaX\r\nSomeheader: YYY\r\n", 0 },
6326 { "Newchannel", "Event: Newchannel\r\nChannel: XbX\r\nSomeheader: YYY\r\n", 1 },
6327 { "Newchannel", "Event: Newchannel\r\nChannel: XcX\r\nSomeheader: YYY\r\n", 1 },
6328 { "Newchannel", "Event: Newchannel\r\nChannel: YYY\r\nSomeheader: YYY\r\n", 0 },
6329 { "Newchannel", "Event: Newchannel\r\nChannel: YYY\r\nSomeheader: ZZZ\r\n", 1 },
6330 { "VarSet", "Event: VarSet\r\nChannel: XXX\r\nSomeheader: YYY\r\n", 1 },
6331 { "VarSet", "Event: VarSet\r\nChannel: YYY\r\nSomeheader: YYY\r\n", 0 },
6332};
6333
6334AST_TEST_DEFINE(eventfilter_test_matching)
6335{
6337 RAII_VAR(struct ao2_container *, includefilters, NULL, ao2_cleanup);
6338 RAII_VAR(struct ao2_container *, excludefilters, NULL, ao2_cleanup);
6339 int i = 0;
6340
6341 switch (cmd) {
6342 case TEST_INIT:
6343 info->name = "eventfilter_test_matching";
6344 info->category = "/main/manager/";
6345 info->summary = "Test eventfilter matching";
6346 info->description =
6347 "This creates various eventfilters and tests to make sure they were matched successfully.";
6348 return AST_TEST_NOT_RUN;
6349 case TEST_EXECUTE:
6350 break;
6351 }
6352
6355 if (!includefilters || !excludefilters) {
6356 ast_test_status_update(test, "Failed to allocate filter containers.\n");
6357 return AST_TEST_FAIL;
6358 }
6359
6360 /* Load all the expected SUCCESS filters */
6361 for (i = 0; i < ARRAY_LEN(filters_for_matching); i++) {
6362 enum add_filter_result add_filter_res;
6363
6364 add_filter_res = manager_add_filter(filters_for_matching[i].criteria,
6365 filters_for_matching[i].pattern, includefilters, excludefilters);
6366
6367 if (add_filter_res != FILTER_SUCCESS) {
6369 "Unexpected add filter result '%s = %s'. Expected result: %s Actual result: %s\n",
6370 parsing_filter_tests[i].criteria, parsing_filter_tests[i].filter,
6371 add_filter_result_enums[FILTER_SUCCESS],
6372 add_filter_result_enums[add_filter_res]);
6373 res = AST_TEST_FAIL;
6374 break;
6375 }
6376 }
6377 ast_test_debug(test, "Loaded %d filters\n", i);
6378
6379 if (res != AST_TEST_PASS) {
6380 return res;
6381 }
6382
6383 /* Now test them */
6384 for (i = 0; i < ARRAY_LEN(events_for_matching); i++) {
6385 int send_event = 0;
6386 struct eventqent *eqe = NULL;
6387
6388 eqe = ast_calloc(1, sizeof(*eqe) + strlen(events_for_matching[i].payload) + 1);
6389 if (!eqe) {
6390 ast_test_status_update(test, "Failed to allocate eventqent\n");
6391 res = AST_TEST_FAIL;
6392 break;
6393 }
6394 strcpy(eqe->eventdata, events_for_matching[i].payload); /* Safe */
6395 eqe->event_name_hash = ast_str_hash(events_for_matching[i].event_name);
6396 send_event = should_send_event(includefilters, excludefilters, eqe);
6397 if (send_event != events_for_matching[i].expected_should_send_event) {
6398 char *escaped = ast_escape_c_alloc(events_for_matching[i].payload);
6400 "Should send event failed to match for '%s'. Expected: %s Actual: %s\n",
6401 escaped,
6402 AST_YESNO(events_for_matching[i].expected_should_send_event), AST_YESNO(send_event));
6403 ast_free(escaped);
6404 res = AST_TEST_FAIL;
6405 }
6406 ast_free(eqe);
6407 }
6408 ast_test_debug(test, "Tested %d events\n", i);
6409
6410 return res;
6411}
6412#endif
6413
6414/*!
6415 * Send any applicable events to the client listening on this socket.
6416 * Wait only for a finite time on each event, and drop all events whether
6417 * they are successfully sent or not.
6418 */
6419static int process_events(struct mansession *s)
6420{
6421 int ret = 0;
6422
6423 ao2_lock(s->session);
6424 if (s->session->stream != NULL) {
6425 struct eventqent *eqe = s->session->last_ev;
6426
6427 while ((eqe = advance_event(eqe))) {
6428 if (eqe->category == EVENT_FLAG_SHUTDOWN) {
6429 ast_debug(3, "Received CloseSession event\n");
6430 ret = -1;
6431 }
6432 if (!ret && s->session->authenticated &&
6433 (s->session->readperm & eqe->category) == eqe->category &&
6434 (s->session->send_events & eqe->category) == eqe->category) {
6436 if (send_string(s, eqe->eventdata) < 0 || s->write_error)
6437 ret = -1; /* don't send more */
6438 }
6439 }
6440 s->session->last_ev = eqe;
6441 }
6442 }
6443 ao2_unlock(s->session);
6444 return ret;
6445}
6446
6447static int action_userevent(struct mansession *s, const struct message *m)
6448{
6449 const char *event = astman_get_header(m, "UserEvent");
6450 struct ast_str *body = ast_str_thread_get(&userevent_buf, 16);
6451 int x;
6452
6453 ast_str_reset(body);
6454
6455 for (x = 0; x < m->hdrcount; x++) {
6456 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:")) &&
6457 strncasecmp("Action:", m->headers[x], strlen("Action:"))) {
6458 ast_str_append(&body, 0, "%s\r\n", m->headers[x]);
6459 }
6460 }
6461
6462 astman_send_ack(s, m, "Event Sent");
6463 manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, ast_str_buffer(body));
6464 return 0;
6465}
6466
6467/*! \brief Show PBX core settings information */
6468static int action_coresettings(struct mansession *s, const struct message *m)
6469{
6470 const char *actionid = astman_get_header(m, "ActionID");
6471 char idText[150];
6472
6473 if (!ast_strlen_zero(actionid)) {
6474 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
6475 } else {
6476 idText[0] = '\0';
6477 }
6478
6479 astman_append(s, "Response: Success\r\n"
6480 "%s"
6481 "AMIversion: %s\r\n"
6482 "AsteriskVersion: %s\r\n"
6483 "SystemName: %s\r\n"
6484 "CoreMaxCalls: %d\r\n"
6485 "CoreMaxLoadAvg: %f\r\n"
6486 "CoreRunUser: %s\r\n"
6487 "CoreRunGroup: %s\r\n"
6488 "CoreMaxFilehandles: %d\r\n"
6489 "CoreRealTimeEnabled: %s\r\n"
6490 "CoreCDRenabled: %s\r\n"
6491 "CoreHTTPenabled: %s\r\n"
6492 "SoundsSearchCustomDir: %s\r\n"
6493 "\r\n",
6494 idText,
6507 );
6508 return 0;
6509}
6510
6511/*! \brief Show PBX core status information */
6512static int action_corestatus(struct mansession *s, const struct message *m)
6513{
6514 const char *actionid = astman_get_header(m, "ActionID");
6515 char idText[150];
6516 char startuptime[150], startupdate[150];
6517 char reloadtime[150], reloaddate[150];
6518 struct ast_tm tm;
6519
6520 if (!ast_strlen_zero(actionid)) {
6521 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
6522 } else {
6523 idText[0] = '\0';
6524 }
6525
6527 ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
6528 ast_strftime(startupdate, sizeof(startupdate), "%Y-%m-%d", &tm);
6530 ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
6531 ast_strftime(reloaddate, sizeof(reloaddate), "%Y-%m-%d", &tm);
6532
6533 astman_append(s, "Response: Success\r\n"
6534 "%s"
6535 "CoreStartupDate: %s\r\n"
6536 "CoreStartupTime: %s\r\n"
6537 "CoreReloadDate: %s\r\n"
6538 "CoreReloadTime: %s\r\n"
6539 "CoreCurrentCalls: %d\r\n"
6540 "CoreProcessedCalls: %d\r\n"
6541 "\r\n",
6542 idText,
6543 startupdate,
6544 startuptime,
6545 reloaddate,
6546 reloadtime,
6549 );
6550 return 0;
6551}
6552
6553/*! \brief Send a reload event */
6554static int action_reload(struct mansession *s, const struct message *m)
6555{
6556 const char *module = astman_get_header(m, "Module");
6558
6559 switch (res) {
6561 astman_send_error(s, m, "No such module");
6562 break;
6564 astman_send_error(s, m, "Module does not support reload");
6565 break;
6567 astman_send_error(s, m, "An unknown error occurred");
6568 break;
6570 astman_send_error(s, m, "A reload is in progress");
6571 break;
6573 astman_send_error(s, m, "Module not initialized");
6574 break;
6577 /* Treat a queued request as success */
6578 astman_send_ack(s, m, "Module Reloaded");
6579 break;
6580 }
6581 return 0;
6582}
6583
6584/*! \brief Manager command "CoreShowChannels" - List currently defined channels
6585 * and some information about them. */
6586static int action_coreshowchannels(struct mansession *s, const struct message *m)
6587{
6588 const char *actionid = astman_get_header(m, "ActionID");
6589 char idText[256];
6590 int numchans = 0;
6591 struct ao2_container *channels;
6592 struct ao2_iterator it_chans;
6593 struct ast_channel_snapshot *cs;
6594
6595 if (!ast_strlen_zero(actionid)) {
6596 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
6597 } else {
6598 idText[0] = '\0';
6599 }
6600
6602
6603 astman_send_listack(s, m, "Channels will follow", "start");
6604
6605 it_chans = ao2_iterator_init(channels, 0);
6606 for (; (cs = ao2_iterator_next(&it_chans)); ao2_ref(cs, -1)) {
6608 char durbuf[16] = "";
6609
6610 if (!built) {
6611 continue;
6612 }
6613
6614 if (!ast_tvzero(cs->base->creationtime)) {
6615 int duration, durh, durm, durs;
6616
6617 duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->base->creationtime) / 1000);
6618 durh = duration / 3600;
6619 durm = (duration % 3600) / 60;
6620 durs = duration % 60;
6621 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
6622 }
6623
6624 astman_append(s,
6625 "Event: CoreShowChannel\r\n"
6626 "%s"
6627 "%s"
6628 "Application: %s\r\n"
6629 "ApplicationData: %s\r\n"
6630 "Duration: %s\r\n"
6631 "BridgeId: %s\r\n"
6632 "\r\n",
6633 idText,
6634 ast_str_buffer(built),
6635 cs->dialplan->appl,
6636 cs->dialplan->data,
6637 durbuf,
6638 cs->bridge->id);
6639
6640 numchans++;
6641
6642 ast_free(built);
6643 }
6644 ao2_iterator_destroy(&it_chans);
6645
6646 astman_send_list_complete(s, m, "CoreShowChannelsComplete", numchans);
6647
6648 ao2_ref(channels, -1);
6649 return 0;
6650}
6651
6652/*! \brief Helper function to add a channel name to the vector */
6653static int coreshowchannelmap_add_to_map(struct ao2_container *c, const char *s)
6654{
6655 char *str;
6656
6657 str = ast_strdup(s);
6658 if (!str) {
6659 ast_log(LOG_ERROR, "Unable to append channel to channel map\n");
6660 return 1;
6661 }
6662
6663 /* If this is a duplicate, it will be ignored */
6665
6666 return 0;
6667}
6668
6669/*! \brief Recursive function to get all channels in a bridge. Follow local channels as well */
6671 struct ast_channel_snapshot *channel_snapshot, struct ast_bridge_snapshot *bridge_snapshot)
6672{
6673 int res = 0;
6674 struct ao2_iterator iter;
6675 char *current_channel_uid;
6676
6677 iter = ao2_iterator_init(bridge_snapshot->channels, 0);
6678 while ((current_channel_uid = ao2_iterator_next(&iter))) {
6679 struct ast_channel_snapshot *current_channel_snapshot;
6680 int add_channel_res;
6681
6682 /* Don't add the original channel to the list - it's either already in there,
6683 * or it's the channel we want the map for */
6684 if (!strcmp(current_channel_uid, channel_snapshot->base->uniqueid)) {
6685 ao2_ref(current_channel_uid, -1);
6686 continue;
6687 }
6688
6689 current_channel_snapshot = ast_channel_snapshot_get_latest(current_channel_uid);
6690 if (!current_channel_snapshot) {
6691 ast_debug(5, "Unable to get channel snapshot\n");
6692 ao2_ref(current_channel_uid, -1);
6693 continue;
6694 }
6695
6696 add_channel_res = coreshowchannelmap_add_to_map(channel_map, current_channel_snapshot->base->name);
6697 if (add_channel_res) {
6698 res = 1;
6699 ao2_ref(current_channel_snapshot, -1);
6700 ao2_ref(current_channel_uid, -1);
6701 break;
6702 }
6703
6704 /* 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 */
6705 if (ast_begins_with(current_channel_snapshot->base->name, "Local")) {
6706 struct ast_channel_snapshot *other_local_snapshot;
6707 struct ast_bridge_snapshot *other_bridge_snapshot;
6708 int size = strlen(current_channel_snapshot->base->name);
6709 char other_local[size + 1];
6710
6711 /* Don't copy the trailing number - set it to 1 or 2, whichever one it currently is not */
6712 ast_copy_string(other_local, current_channel_snapshot->base->name, size);
6713 other_local[size - 1] = ast_ends_with(current_channel_snapshot->base->name, "1") ? '2' : '1';
6714 other_local[size] = '\0';
6715
6716 other_local_snapshot = ast_channel_snapshot_get_latest_by_name(other_local);
6717 if (!other_local_snapshot) {
6718 ast_debug(5, "Unable to get other local channel snapshot\n");
6719 ao2_ref(current_channel_snapshot, -1);
6720 ao2_ref(current_channel_uid, -1);
6721 continue;
6722 }
6723
6724 if (coreshowchannelmap_add_to_map(channel_map, other_local_snapshot->base->name)) {
6725 res = 1;
6726 ao2_ref(current_channel_snapshot, -1);
6727 ao2_ref(current_channel_uid, -1);
6728 ao2_ref(other_local_snapshot, -1);
6729 break;
6730 }
6731
6732 other_bridge_snapshot = ast_bridge_get_snapshot_by_uniqueid(other_local_snapshot->bridge->id);
6733 if (other_bridge_snapshot) {
6734 res = coreshowchannelmap_add_connected_channels(channel_map, other_local_snapshot, other_bridge_snapshot);
6735 }
6736
6737 ao2_ref(current_channel_snapshot, -1);
6738 ao2_ref(current_channel_uid, -1);
6739 ao2_ref(other_local_snapshot, -1);
6740 ao2_ref(other_bridge_snapshot, -1);
6741
6742 if (res) {
6743 break;
6744 }
6745 }
6746 }
6747 ao2_iterator_destroy(&iter);
6748
6749 return res;
6750}
6751
6752/*! \brief Manager command "CoreShowChannelMap" - Lists all channels connected to
6753 * the specified channel. */
6754static int action_coreshowchannelmap(struct mansession *s, const struct message *m)
6755{
6756 const char *actionid = astman_get_header(m, "ActionID");
6757 const char *channel_name = astman_get_header(m, "Channel");
6758 char *current_channel_name;
6759 char id_text[256];
6760 int total = 0;
6761 struct ao2_container *channel_map;
6762 struct ao2_iterator i;
6763 RAII_VAR(struct ast_bridge_snapshot *, bridge_snapshot, NULL, ao2_cleanup);
6764 RAII_VAR(struct ast_channel_snapshot *, channel_snapshot, NULL, ao2_cleanup);
6765
6766 if (!ast_strlen_zero(actionid)) {
6767 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
6768 } else {
6769 id_text[0] = '\0';
6770 }
6771
6772 if (ast_strlen_zero(channel_name)) {
6773 astman_send_error(s, m, "CoreShowChannelMap requires a channel.\n");
6774 return 0;
6775 }
6776
6777 channel_snapshot = ast_channel_snapshot_get_latest_by_name(channel_name);
6778 if (!channel_snapshot) {
6779 astman_send_error(s, m, "Could not get channel snapshot\n");
6780 return 0;
6781 }
6782
6783 if (ast_strlen_zero(channel_snapshot->bridge->id)) {
6784 astman_send_listack(s, m, "Channel map will follow", "start");
6785 astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", 0);
6787 return 0;
6788 }
6789
6790 bridge_snapshot = ast_bridge_get_snapshot_by_uniqueid(channel_snapshot->bridge->id);
6791 if (!bridge_snapshot) {
6792 astman_send_listack(s, m, "Channel map will follow", "start");
6793 astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", 0);
6795 return 0;
6796 }
6797
6799 if (!channel_map) {
6800 astman_send_error(s, m, "Could not create channel map\n");
6801 return 0;
6802 }
6803
6804 astman_send_listack(s, m, "Channel map will follow", "start");
6805
6806 if (coreshowchannelmap_add_connected_channels(channel_map, channel_snapshot, bridge_snapshot)) {
6807 astman_send_error(s, m, "Could not complete channel map\n");
6808 ao2_ref(channel_map, -1);
6809 return 0;
6810 }
6811
6812 i = ao2_iterator_init(channel_map, 0);
6813 while ((current_channel_name = ao2_iterator_next(&i))) {
6814 astman_append(s,
6815 "Event: CoreShowChannelMap\r\n"
6816 "%s"
6817 "Channel: %s\r\n"
6818 "ConnectedChannel: %s\r\n\r\n",
6819 id_text,
6820 channel_name,
6821 current_channel_name);
6822 total++;
6823 }
6825
6826 ao2_ref(channel_map, -1);
6827 astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", total);
6829
6830 return 0;
6831}
6832
6833/*! \brief Manager command "LoggerRotate" - reloads and rotates the logger in
6834 * the same manner as the CLI command 'logger rotate'. */
6835static int action_loggerrotate(struct mansession *s, const struct message *m)
6836{
6837 if (ast_logger_rotate()) {
6838 astman_send_error(s, m, "Failed to reload the logger and rotate log files");
6839 return 0;
6840 }
6841
6842 astman_send_ack(s, m, "Reloaded the logger and rotated log files");
6843 return 0;
6844}
6845
6846/*! \brief Manager function to check if module is loaded */
6847static int manager_modulecheck(struct mansession *s, const struct message *m)
6848{
6849 const char *module = astman_get_header(m, "Module");
6850 const char *id = astman_get_header(m, "ActionID");
6851
6852 ast_debug(1, "**** ModuleCheck .so file %s\n", module);
6853 if (!ast_module_check(module)) {
6854 astman_send_error(s, m, "Module not loaded");
6855 return 0;
6856 }
6857
6858 astman_append(s, "Response: Success\r\n");
6859
6860 if (!ast_strlen_zero(id)) {
6861 astman_append(s, "ActionID: %s\r\n", id);
6862 }
6863
6864#if !defined(LOW_MEMORY)
6865 /* When we switched from subversion to git we lost the ability to
6866 * retrieve the 'ASTERISK_FILE_VERSION' from that file, but we retain
6867 * the response header here for backwards compatibility. */
6868 astman_append(s, "Version: \r\n");
6869#endif
6870
6871 astman_append(s, "\r\n");
6872
6873 return 0;
6874}
6875
6876/**
6877 * \brief Check if the given file path is in the modules dir or not
6878 *
6879 * \note When the module is being loaded / reloaded / unloaded, the modules dir is
6880 * automatically prepended
6881 *
6882 * \return 1 if inside modules dir
6883 * \return 0 if outside modules dir
6884 * \return -1 on failure
6885 */
6886static int file_in_modules_dir(const char *filename)
6887{
6888 char *stripped_filename;
6889 RAII_VAR(char *, path, NULL, ast_free);
6890 RAII_VAR(char *, real_path, NULL, ast_free);
6891
6892 /* Don't bother checking */
6893 if (live_dangerously) {
6894 return 1;
6895 }
6896
6897 stripped_filename = ast_strip(ast_strdupa(filename));
6898
6899 /* Always prepend the modules dir since that is what the code does for ModuleLoad */
6900 if (ast_asprintf(&path, "%s/%s", ast_config_AST_MODULE_DIR, stripped_filename) == -1) {
6901 return -1;
6902 }
6903
6904 real_path = realpath(path, NULL);
6905 if (!real_path) {
6906 return -1;
6907 }
6908
6909 return ast_begins_with(real_path, ast_config_AST_MODULE_DIR);
6910}
6911
6912static int manager_moduleload(struct mansession *s, const struct message *m)
6913{
6914 int res;
6915 const char *module = astman_get_header(m, "Module");
6916 const char *loadtype = astman_get_header(m, "LoadType");
6917 const char *recursive = astman_get_header(m, "Recursive");
6918
6919 if (!loadtype || strlen(loadtype) == 0) {
6920 astman_send_error(s, m, "Incomplete ModuleLoad action.");
6921 }
6922 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0) {
6923 astman_send_error(s, m, "Need module name");
6924 }
6925
6926 res = file_in_modules_dir(module);
6927 if (res == 0) {
6928 astman_send_error(s, m, "Module must be in the configured modules directory.");
6929 return 0;
6930 } else if (res == -1) {
6931 astman_send_error(s, m, "Module not found.");
6932 return 0;
6933 }
6934
6935 if (!strcasecmp(loadtype, "load")) {
6936 res = ast_load_resource(module);
6937 if (res) {
6938 astman_send_error(s, m, "Could not load module.");
6939 } else {
6940 astman_send_ack(s, m, "Module loaded.");
6941 }
6942 } else if (!strcasecmp(loadtype, "unload")) {
6943 res = ast_unload_resource(module, AST_FORCE_SOFT);
6944 if (res) {
6945 astman_send_error(s, m, "Could not unload module.");
6946 } else {
6947 astman_send_ack(s, m, "Module unloaded.");
6948 }
6949 } else if (!strcasecmp(loadtype, "refresh")) {
6950 res = ast_refresh_resource(module, AST_FORCE_SOFT, !ast_strlen_zero(recursive) && ast_true(recursive));
6951 if (res) {
6952 astman_send_error(s, m, "Could not refresh module.");
6953 } else {
6954 astman_send_ack(s, m, "Module unloaded and loaded.");
6955 }
6956 } else if (!strcasecmp(loadtype, "reload")) {
6957 /* TODO: Unify the ack/error messages here with action_reload */
6958 if (!ast_strlen_zero(module)) {
6959 enum ast_module_reload_result reload_res = ast_module_reload(module);
6960
6961 switch (reload_res) {
6963 astman_send_error(s, m, "No such module.");
6964 break;
6966 astman_send_error(s, m, "Module does not support reload action.");
6967 break;
6969 astman_send_error(s, m, "An unknown error occurred");
6970 break;
6972 astman_send_error(s, m, "A reload is in progress");
6973 break;
6975 astman_send_error(s, m, "Module not initialized");
6976 break;
6979 /* Treat a queued request as success */
6980 astman_send_ack(s, m, "Module reloaded.");
6981 break;
6982 }
6983 } else {
6984 ast_module_reload(NULL); /* Reload all modules */
6985 astman_send_ack(s, m, "All modules reloaded");
6986 }
6987 } else {
6988 astman_send_error(s, m, "Incomplete ModuleLoad action.");
6989 }
6990 return 0;
6991}
6992
6993static void log_action(const struct message *m, const char *action)
6994{
6995 struct ast_str *buf;
6996 int x;
6997
6998 if (!manager_debug) {
6999 return;
7000 }
7001
7002 buf = ast_str_create(256);
7003 if (!buf) {
7004 return;
7005 }
7006
7007 for (x = 0; x < m->hdrcount; ++x) {
7008 if (!strncasecmp(m->headers[x], "Secret", 6)) {
7009 ast_str_append(&buf, 0, "Secret: <redacted from logging>\n");
7010 } else {
7011 ast_str_append(&buf, 0, "%s\n", m->headers[x]);
7012 }
7013 }
7014
7015 ast_verbose("<--- Examining AMI action: -->\n%s\n", ast_str_buffer(buf));
7016 ast_free(buf);
7017}
7018
7019/*
7020 * Done with the action handlers here, we start with the code in charge
7021 * of accepting connections and serving them.
7022 * accept_thread() forks a new thread for each connection, session_do(),
7023 * which in turn calls get_input() repeatedly until a full message has
7024 * been accumulated, and then invokes process_message() to pass it to
7025 * the appropriate handler.
7026 */
7027
7028/*! \brief
7029 * Process an AMI message, performing desired action.
7030 * Return 0 on success, -1 on error that require the session to be destroyed.
7031 */
7032static int process_message(struct mansession *s, const struct message *m)
7033{
7034 int ret = 0;
7035 struct manager_action *act_found;
7036 struct ast_manager_user *user = NULL;
7037 const char *username;
7038 const char *action;
7039
7040 action = __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY);
7041 if (ast_strlen_zero(action)) {
7042 report_req_bad_format(s, "NONE");
7043 mansession_lock(s);
7044 astman_send_error(s, m, "Missing action in request");
7046 return 0;
7047 }
7048
7049 log_action(m, action);
7050
7051 if (ast_shutting_down()) {
7052 ast_log(LOG_ERROR, "Unable to process manager action '%s'. Asterisk is shutting down.\n", action);
7053 mansession_lock(s);
7054 astman_send_error(s, m, "Asterisk is shutting down");
7056 return 0;
7057 }
7058
7059 if (!s->session->authenticated
7060 && strcasecmp(action, "Login")
7061 && strcasecmp(action, "Logoff")
7062 && strcasecmp(action, "Challenge")) {
7063 if (!s->session->authenticated) {
7064 report_req_not_allowed(s, action);
7065 }
7066 mansession_lock(s);
7067 astman_send_error(s, m, "Permission denied");
7069 return 0;
7070 }
7071
7072 if (!s->session->authenticated
7073 && (!strcasecmp(action, "Login")
7074 || !strcasecmp(action, "Challenge"))) {
7075 username = astman_get_header(m, "Username");
7076
7077 if (!ast_strlen_zero(username) && check_manager_session_inuse(username)) {
7079 user = get_manager_by_name_locked(username);
7080 if (user && !user->allowmultiplelogin) {
7083 sleep(1);
7084 mansession_lock(s);
7085 astman_send_error(s, m, "Login Already In Use");
7087 return -1;
7088 }
7090 }
7091 }
7092
7093 act_found = action_find(action);
7094 if (act_found) {
7095 /* Found the requested AMI action. */
7096 int acted = 0;
7097
7098 if ((s->session->writeperm & act_found->authority)
7099 || act_found->authority == 0) {
7100 /* We have the authority to execute the action. */
7101 ret = -1;
7102 ao2_lock(act_found);
7103 if (act_found->registered && act_found->func) {
7104 struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
7105
7106 ao2_unlock(act_found);
7107 if (mod_ref || !act_found->module) {
7108 ast_debug(1, "Running action '%s'\n", act_found->action);
7109 ret = act_found->func(s, m);
7110 acted = 1;
7111 ast_module_unref(mod_ref);
7112 }
7113 } else {
7114 ao2_unlock(act_found);
7115 }
7116 }
7117 if (!acted) {
7118 /*
7119 * We did not execute the action because access was denied, it
7120 * was no longer registered, or no action was really registered.
7121 * Complain about it and leave.
7122 */
7123 report_req_not_allowed(s, action);
7124 mansession_lock(s);
7125 astman_send_error(s, m, "Permission denied");
7127 }
7128 ao2_t_ref(act_found, -1, "done with found action object");
7129 } else {
7130 char buf[512];
7131
7132 report_req_bad_format(s, action);
7133 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
7134 mansession_lock(s);
7135 astman_send_error(s, m, buf);
7137 }
7138 if (ret) {
7139 return ret;
7140 }
7141 /* Once done with our message, deliver any pending events unless the
7142 requester doesn't want them as part of this response.
7143 */
7144 if (ast_strlen_zero(astman_get_header(m, "SuppressEvents"))) {
7145 return process_events(s);
7146 } else {
7147 return ret;
7148 }
7149}
7150
7151/*!
7152 * Read one full line (including crlf) from the manager socket.
7153 * \note \verbatim
7154 * \r\n is the only valid terminator for the line.
7155 * (Note that, later, '\0' will be considered as the end-of-line marker,
7156 * so everything between the '\0' and the '\r\n' will not be used).
7157 * Also note that we assume output to have at least "maxlen" space.
7158 * \endverbatim
7159 */
7160static int get_input(struct mansession *s, char *output)
7161{
7162 int res, x;
7163 int maxlen = sizeof(s->session->inbuf) - 1;
7164 char *src = s->session->inbuf;
7165 int timeout = -1;
7166 time_t now;
7167
7168 /*
7169 * Look for \r\n within the buffer. If found, copy to the output
7170 * buffer and return, trimming the \r\n (not used afterwards).
7171 */
7172 for (x = 0; x < s->session->inlen; x++) {
7173 int cr; /* set if we have \r */
7174 if (src[x] == '\r' && x+1 < s->session->inlen && src[x + 1] == '\n') {
7175 cr = 2; /* Found. Update length to include \r\n */
7176 } else if (src[x] == '\n') {
7177 cr = 1; /* also accept \n only */
7178 } else {
7179 continue;
7180 }
7181 memmove(output, src, x); /*... but trim \r\n */
7182 output[x] = '\0'; /* terminate the string */
7183 x += cr; /* number of bytes used */
7184 s->session->inlen -= x; /* remaining size */
7185 memmove(src, src + x, s->session->inlen); /* remove used bytes */
7186 return 1;
7187 }
7188 if (s->session->inlen >= maxlen) {
7189 /* no crlf found, and buffer full - sorry, too long for us
7190 * keep the last character in case we are in the middle of a CRLF. */
7191 ast_log(LOG_WARNING, "Discarding message from %s. Line too long: %.25s...\n", ast_sockaddr_stringify_addr(&s->session->addr), src);
7192 src[0] = src[s->session->inlen - 1];
7193 s->session->inlen = 1;
7195 }
7196 res = 0;
7197 while (res == 0) {
7198 /* calculate a timeout if we are not authenticated */
7199 if (!s->session->authenticated) {
7200 if(time(&now) == -1) {
7201 ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
7202 return -1;
7203 }
7204
7205 timeout = (authtimeout - (now - s->session->authstart)) * 1000;
7206 if (timeout < 0) {
7207 /* we have timed out */
7208 return 0;
7209 }
7210 }
7211
7213 if (s->session->pending_event) {
7214 s->session->pending_event = 0;
7216 return 0;
7217 }
7218 s->session->waiting_thread = pthread_self();
7220
7222
7226 }
7227 if (res < 0) {
7228 if (s->session->kicked) {
7229 ast_debug(1, "Manager session has been kicked\n");
7230 return -1;
7231 }
7232 /* If we get a signal from some other thread (typically because
7233 * there are new events queued), return 0 to notify the caller.
7234 */
7235 if (errno == EINTR || errno == EAGAIN) {
7236 return 0;
7237 }
7238 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
7239 return -1;
7240 }
7241
7242 ao2_lock(s->session);
7243 res = ast_iostream_read(s->session->stream, src + s->session->inlen, maxlen - s->session->inlen);
7244 if (res < 1) {
7245 res = -1; /* error return */
7246 } else {
7247 s->session->inlen += res;
7248 src[s->session->inlen] = '\0';
7249 res = 0;
7250 }
7251 ao2_unlock(s->session);
7252 return res;
7253}
7254
7255/*!
7256 * \internal
7257 * \brief Error handling for sending parse errors. This function handles locking, and clearing the
7258 * parse error flag.
7259 *
7260 * \param s AMI session to process action request.
7261 * \param m Message that's in error.
7262 * \param error Error message to send.
7263 */
7264static void handle_parse_error(struct mansession *s, struct message *m, char *error)
7265{
7266 mansession_lock(s);
7267 astman_send_error(s, m, error);
7268 s->parsing = MESSAGE_OKAY;
7270}
7271
7272/*!
7273 * \internal
7274 * \brief Read and process an AMI action request.
7275 *
7276 * \param s AMI session to process action request.
7277 *
7278 * \retval 0 Retain AMI connection for next command.
7279 * \retval -1 Drop AMI connection due to logoff or connection error.
7280 */
7281static int do_message(struct mansession *s)
7282{
7283 struct message m = { 0 };
7284 char header_buf[sizeof(s->session->inbuf)] = { '\0' };
7285 int res;
7286 int hdr_loss;
7287 time_t now;
7288
7289 hdr_loss = 0;
7290 for (;;) {
7291 /* Check if any events are pending and do them if needed */
7292 if (process_events(s)) {
7293 res = -1;
7294 break;
7295 }
7296 res = get_input(s, header_buf);
7297 if (res == 0) {
7298 /* No input line received. */
7299 if (!s->session->authenticated) {
7300 if (time(&now) == -1) {
7301 ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
7302 res = -1;
7303 break;
7304 }
7305
7306 if (now - s->session->authstart > authtimeout) {
7307 if (displayconnects) {
7308 ast_verb(2, "Client from %s, failed to authenticate in %d seconds\n", ast_sockaddr_stringify_addr(&s->session->addr), authtimeout);
7309 }
7310 res = -1;
7311 break;
7312 }
7313 }
7314 continue;
7315 } else if (res > 0) {
7316 /* Input line received. */
7317 if (ast_strlen_zero(header_buf)) {
7318 if (hdr_loss) {
7319 mansession_lock(s);
7320 astman_send_error(s, &m, "Too many lines in message or allocation failure");
7322 res = 0;
7323 } else {
7324 switch (s->parsing) {
7325 case MESSAGE_OKAY:
7326 res = process_message(s, &m) ? -1 : 0;
7327 break;
7329 handle_parse_error(s, &m, "Failed to parse message: line too long");
7330 res = 0;
7331 break;
7332 }
7333 }
7334 break;
7335 } else if (m.hdrcount < ARRAY_LEN(m.headers)) {
7336 m.headers[m.hdrcount] = ast_strdup(header_buf);
7337 if (!m.headers[m.hdrcount]) {
7338 /* Allocation failure. */
7339 hdr_loss = 1;
7340 } else {
7341 ++m.hdrcount;
7342 }
7343 } else {
7344 /* Too many lines in message. */
7345 hdr_loss = 1;
7346 }
7347 } else {
7348 /* Input error. */
7349 break;
7350 }
7351 }
7352
7354
7355 return res;
7356}
7357
7358/*! \brief The body of the individual manager session.
7359 * Call get_input() to read one line at a time
7360 * (or be woken up on new events), collect the lines in a
7361 * message until found an empty line, and execute the request.
7362 * In any case, deliver events asynchronously through process_events()
7363 * (called from here if no line is available, or at the end of
7364 * process_message(). )
7365 */
7366static void *session_do(void *data)
7367{
7368 struct ast_tcptls_session_instance *ser = data;
7370 struct mansession s = {
7371 .tcptls_session = data,
7372 };
7373 int res;
7374 int arg = 1;
7375 struct ast_sockaddr ser_remote_address_tmp;
7376
7379 goto done;
7380 }
7381
7382 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
7383 session = build_mansession(&ser_remote_address_tmp);
7384
7385 if (session == NULL) {
7387 goto done;
7388 }
7389
7390 /* here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
7391 * This is necessary to prevent delays (caused by buffering) as we
7392 * write to the socket in bits and pieces. */
7393 if (setsockopt(ast_iostream_get_fd(ser->stream), IPPROTO_TCP, TCP_NODELAY, (char *) &arg, sizeof(arg)) < 0) {
7394 ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on manager connection: %s\n", strerror(errno));
7395 }
7397
7399 /* Hook to the tail of the event queue */
7400 session->last_ev = grab_last();
7401
7402 ast_mutex_init(&s.lock);
7403
7404 /* these fields duplicate those in the 'ser' structure */
7405 session->stream = s.stream = ser->stream;
7406 ast_sockaddr_copy(&session->addr, &ser_remote_address_tmp);
7407 s.session = session;
7408
7409 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
7410
7411 if(time(&session->authstart) == -1) {
7412 ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
7416 goto done;
7417 }
7419
7420 /*
7421 * We cannot let the stream exclusively wait for data to arrive.
7422 * We have to wake up the task to send async events.
7423 */
7425
7427 ast_tvnow(), authtimeout * 1000);
7428
7429 astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
7430 for (;;) {
7431 if ((res = do_message(&s)) < 0 || s.write_error || session->kicked) {
7432 break;
7433 }
7434 if (session->authenticated) {
7436 }
7437 }
7438 /* session is over, explain why and terminate */
7439 if (session->authenticated) {
7441 ast_verb(2, "Manager '%s' %s from %s\n", session->username, session->kicked ? "kicked" : "logged off", ast_sockaddr_stringify_addr(&session->addr));
7442 }
7443 } else {
7445 if (displayconnects) {
7446 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_sockaddr_stringify_addr(&session->addr));
7447 }
7448 }
7449
7451
7453done:
7454 ao2_ref(ser, -1);
7455 ser = NULL;
7456 return NULL;
7457}
7458
7459/*! \brief remove at most n_max stale session from the list. */
7460static int purge_sessions(int n_max)
7461{
7462 struct ao2_container *sessions;
7464 time_t now = time(NULL);
7465 struct ao2_iterator i;
7466 int purged = 0;
7467
7468 sessions = ao2_global_obj_ref(mgr_sessions);
7469 if (!sessions) {
7470 return 0;
7471 }
7473 ao2_ref(sessions, -1);
7474
7475 /* The order of operations is significant */
7476 while (n_max > 0 && (session = ao2_iterator_next(&i))) {
7478 if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
7479 if (session->authenticated
7480 && VERBOSITY_ATLEAST(2)
7482 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
7483 session->username, ast_sockaddr_stringify_addr(&session->addr));
7484 }
7487 n_max--;
7488 purged++;
7489 } else {
7492 }
7493 }
7495 return purged;
7496}
7497
7498/*! \brief
7499 * events are appended to a queue from where they
7500 * can be dispatched to clients.
7501 */
7502static int append_event(const char *str, int event_name_hash, int category)
7503{
7504 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
7505 static int seq; /* sequence number */
7506
7507 if (!tmp) {
7508 return -1;
7509 }
7510
7511 /* need to init all fields, because ast_malloc() does not */
7512 tmp->usecount = 0;
7513 tmp->category = category;
7514 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
7515 tmp->tv = ast_tvnow();
7516 tmp->event_name_hash = event_name_hash;
7517 AST_RWLIST_NEXT(tmp, eq_next) = NULL;
7518 strcpy(tmp->eventdata, str);
7519
7521 AST_RWLIST_INSERT_TAIL(&all_events, tmp, eq_next);
7523
7524 return 0;
7525}
7526
7527static void append_channel_vars(struct ast_str **pbuf, struct ast_channel *chan)
7528{
7529 struct varshead *vars;
7530 struct ast_var_t *var;
7531
7532 vars = ast_channel_get_manager_vars(chan);
7533 if (!vars) {
7534 return;
7535 }
7536
7537 AST_LIST_TRAVERSE(vars, var, entries) {
7538 ast_str_append(pbuf, 0, "ChanVariable(%s): %s=%s\r\n", ast_channel_name(chan), var->name, var->value);
7539 }
7540 ao2_ref(vars, -1);
7541}
7542
7543/* XXX see if can be moved inside the function */
7544AST_THREADSTORAGE(manager_event_buf);
7545#define MANAGER_EVENT_BUF_INITSIZE 256
7546
7547static int __attribute__((format(printf, 9, 0))) __manager_event_sessions_va(
7548 struct ao2_container *sessions,
7549 int category,
7550 const char *event,
7551 int chancount,
7552 struct ast_channel **chans,
7553 const char *file,
7554 int line,
7555 const char *func,
7556 const char *fmt,
7557 va_list ap)
7558{
7560 const char *cat_str;
7561 struct timeval now;
7562 struct ast_str *buf;
7563 int i;
7564 int event_name_hash;
7565
7568 ast_debug(3, "AMI Event '%s' is globally disabled, skipping\n", event);
7569 /* Event is globally disabled */
7570 return -1;
7571 }
7572 }
7573
7574 buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE);
7575 if (!buf) {
7576 return -1;
7577 }
7578
7579 cat_str = authority_to_str(category, &auth);
7580 ast_str_set(&buf, 0,
7581 "Event: %s\r\n"
7582 "Privilege: %s\r\n",
7583 event, cat_str);
7584
7585 if (timestampevents) {
7586 now = ast_tvnow();
7587 ast_str_append(&buf, 0,
7588 "Timestamp: %ld.%06lu\r\n",
7589 (long)now.tv_sec, (unsigned long) now.tv_usec);
7590 }
7591 if (manager_debug) {
7592 static int seq;
7593
7594 ast_str_append(&buf, 0,
7595 "SequenceNumber: %d\r\n",
7597 ast_str_append(&buf, 0,
7598 "File: %s\r\n"
7599 "Line: %d\r\n"
7600 "Func: %s\r\n",
7601 file, line, func);
7602 }
7604 ast_str_append(&buf, 0,
7605 "SystemName: %s\r\n",
7607 }
7608
7609 ast_str_append_va(&buf, 0, fmt, ap);
7610 for (i = 0; i < chancount; i++) {
7612 }
7613
7614 ast_str_append(&buf, 0, "\r\n");
7615
7616 event_name_hash = ast_str_hash(event);
7617
7618 append_event(ast_str_buffer(buf), event_name_hash, category);
7619
7620 /* Wake up any sleeping sessions */
7621 if (sessions) {
7622 struct ao2_iterator iter;
7624
7625 iter = ao2_iterator_init(sessions, 0);
7626 while ((session = ao2_iterator_next(&iter))) {
7627 ast_mutex_lock(&session->notify_lock);
7628 if (session->waiting_thread != AST_PTHREADT_NULL) {
7629 pthread_kill(session->waiting_thread, SIGURG);
7630 } else {
7631 /* We have an event to process, but the mansession is
7632 * not waiting for it. We still need to indicate that there
7633 * is an event waiting so that get_input processes the pending
7634 * event instead of polling.
7635 */
7636 session->pending_event = 1;
7637 }
7638 ast_mutex_unlock(&session->notify_lock);
7640 }
7641 ao2_iterator_destroy(&iter);
7642 }
7643
7644 if (category != EVENT_FLAG_SHUTDOWN && !AST_RWLIST_EMPTY(&manager_hooks)) {
7645 struct manager_custom_hook *hook;
7646
7648 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
7649 hook->helper(category, event, ast_str_buffer(buf));
7650 }
7652 }
7653
7654 return 0;
7655}
7656
7657static int __attribute__((format(printf, 9, 0))) __manager_event_sessions(
7658 struct ao2_container *sessions,
7659 int category,
7660 const char *event,
7661 int chancount,
7662 struct ast_channel **chans,
7663 const char *file,
7664 int line,
7665 const char *func,
7666 const char *fmt,
7667 ...)
7668{
7669 va_list ap;
7670 int res;
7671
7672 va_start(ap, fmt);
7674 chancount, chans, file, line, func, fmt, ap);
7675 va_end(ap);
7676 return res;
7677}
7678
7679int __ast_manager_event_multichan(int category, const char *event, int chancount,
7680 struct ast_channel **chans, const char *file, int line, const char *func,
7681 const char *fmt, ...)
7682{
7683 struct ao2_container *sessions = ao2_global_obj_ref(mgr_sessions);
7684 va_list ap;
7685 int res;
7686
7688 /* Nobody is listening */
7690 return 0;
7691 }
7692
7693 va_start(ap, fmt);
7695 file, line, func, fmt, ap);
7696 va_end(ap);
7698 return res;
7699}
7700
7701/*! \brief
7702 * support functions to register/unregister AMI action handlers,
7703 */
7704int ast_manager_unregister(const char *action)
7705{
7706 struct manager_action *cur;
7707
7710 if (!strcasecmp(action, cur->action)) {
7712 break;
7713 }
7714 }
7717
7718 if (cur) {
7719 /*
7720 * We have removed the action object from the container so we
7721 * are no longer in a hurry.
7722 */
7723 ao2_lock(cur);
7724 cur->registered = 0;
7725 ao2_unlock(cur);
7726
7727 ao2_t_ref(cur, -1, "action object removed from list");
7728 ast_verb(5, "Manager unregistered action %s\n", action);
7729 }
7730
7731 return 0;
7732}
7733
7734static int manager_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
7735{
7736 /* Notify managers of change */
7737 char hint[512];
7738
7739 hint[0] = '\0';
7740 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
7741
7742 switch(info->reason) {
7744 manager_event(EVENT_FLAG_CALL, "ExtensionStatus",
7745 "Exten: %s\r\n"
7746 "Context: %s\r\n"
7747 "Hint: %s\r\n"
7748 "Status: %d\r\n"
7749 "StatusText: %s\r\n",
7750 exten,
7751 context,
7752 hint,
7753 info->exten_state,
7754 ast_extension_state2str(info->exten_state));
7755 break;
7757 manager_event(EVENT_FLAG_CALL, "PresenceStatus",
7758 "Exten: %s\r\n"
7759 "Context: %s\r\n"
7760 "Hint: %s\r\n"
7761 "Status: %s\r\n"
7762 "Subtype: %s\r\n"
7763 "Message: %s\r\n",
7764 exten,
7765 context,
7766 hint,
7767 ast_presence_state2str(info->presence_state),
7768 info->presence_subtype,
7769 info->presence_message);
7770 break;
7771 }
7772 return 0;
7773}
7774
7776{
7777 struct manager_action *cur, *prev = NULL;
7778
7780 AST_RWLIST_TRAVERSE(&actions, cur, list) {
7781 int ret;
7782
7783 ret = strcasecmp(cur->action, act->action);
7784 if (ret == 0) {
7785 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
7787 return -1;
7788 }
7789 if (ret > 0) { /* Insert these alphabetically */
7790 break;
7791 }
7792 prev = cur;
7793 }
7794
7795 ao2_t_ref(act, +1, "action object added to list");
7796 act->registered = 1;
7797 if (prev) {
7798 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
7799 } else {
7800 AST_RWLIST_INSERT_HEAD(&actions, act, list);
7801 }
7802
7803 ast_verb(5, "Manager registered action %s\n", act->action);
7804
7806
7807 return 0;
7808}
7809
7810/*!
7811 * \internal
7812 * \brief Destroy the registered AMI action object.
7813 *
7814 * \param obj Object to destroy.
7815 */
7816static void action_destroy(void *obj)
7817{
7818 struct manager_action *doomed = obj;
7819
7820 if (doomed->synopsis) {
7821 /* The string fields were initialized. */
7823 }
7824 ao2_cleanup(doomed->final_response);
7825 ao2_cleanup(doomed->list_responses);
7826}
7827
7828/*! \brief register a new command with manager, including online help. This is
7829 the preferred way to register a manager command */
7830int 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)
7831{
7832 struct manager_action *cur;
7833
7834 cur = ao2_t_alloc(sizeof(*cur), action_destroy, action);
7835 if (!cur) {
7836 return -1;
7837 }
7838 if (ast_string_field_init(cur, 128)) {
7839 ao2_t_ref(cur, -1, "action object creation failed");
7840 return -1;
7841 }
7842
7843 if (ast_string_field_init_extended(cur, since)) {
7844 ao2_t_ref(cur, -1, "action object creation failed");
7845 return -1;
7846 }
7847
7848 if (ast_string_field_init_extended(cur, provided_by)) {
7849 ao2_t_ref(cur, -1, "action object creation failed");
7850 return -1;
7851 }
7852
7853 cur->action = action;
7854 cur->authority = auth;
7855 cur->func = func;
7856 cur->module = module;
7857#ifdef AST_XML_DOCS
7858 if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
7859 char *tmpxml;
7860
7861 tmpxml = ast_xmldoc_build_since("manager", action, NULL);
7862 ast_string_field_set(cur, since, tmpxml);
7863 ast_free(tmpxml);
7864
7865 tmpxml = ast_xmldoc_build_synopsis("manager", action, NULL);
7866 ast_string_field_set(cur, synopsis, tmpxml);
7867 ast_free(tmpxml);
7868
7869 tmpxml = ast_xmldoc_build_provided_by("manager", action, NULL);
7870 ast_string_field_set(cur, provided_by, tmpxml);
7871 ast_free(tmpxml);
7872
7873 tmpxml = ast_xmldoc_build_syntax("manager", action, NULL);
7874 ast_string_field_set(cur, syntax, tmpxml);
7875 ast_free(tmpxml);
7876
7877 tmpxml = ast_xmldoc_build_description("manager", action, NULL);
7878 ast_string_field_set(cur, description, tmpxml);
7879 ast_free(tmpxml);
7880
7881 tmpxml = ast_xmldoc_build_seealso("manager", action, NULL);
7882 ast_string_field_set(cur, seealso, tmpxml);
7883 ast_free(tmpxml);
7884
7885 tmpxml = ast_xmldoc_build_arguments("manager", action, NULL);
7886 ast_string_field_set(cur, arguments, tmpxml);
7887 ast_free(tmpxml);
7888
7889 cur->final_response = ast_xmldoc_build_final_response("manager", action, NULL);
7890 cur->list_responses = ast_xmldoc_build_list_responses("manager", action, NULL);
7891
7892 cur->docsrc = AST_XML_DOC;
7893 } else
7894#endif
7895 {
7897 ast_string_field_set(cur, description, description);
7898#ifdef AST_XML_DOCS
7899 cur->docsrc = AST_STATIC_DOC;
7900#endif
7901 }
7902 if (ast_manager_register_struct(cur)) {
7903 ao2_t_ref(cur, -1, "action object registration failed");
7904 return -1;
7905 }
7906
7907 ao2_t_ref(cur, -1, "action object registration successful");
7908 return 0;
7909}
7910/*! @}
7911 END Doxygen group */
7912
7913/*
7914 * The following are support functions for AMI-over-http.
7915 * The common entry point is generic_http_callback(),
7916 * which extracts HTTP header and URI fields and reformats
7917 * them into AMI messages, locates a proper session
7918 * (using the mansession_id Cookie or GET variable),
7919 * and calls process_message() as for regular AMI clients.
7920 * When done, the output (which goes to a temporary file)
7921 * is read back into a buffer and reformatted as desired,
7922 * then fed back to the client over the original socket.
7923 */
7924
7930
7931static const char * const contenttype[] = {
7932 [FORMAT_RAW] = "plain",
7933 [FORMAT_HTML] = "html",
7934 [FORMAT_XML] = "xml",
7935};
7936
7937/*!
7938 * locate an http session in the list. The search key (ident) is
7939 * the value of the mansession_id cookie (0 is not valid and means
7940 * a session on the AMI socket).
7941 */
7942static struct mansession_session *find_session(uint32_t ident, int incinuse)
7943{
7944 struct ao2_container *sessions;
7946 struct ao2_iterator i;
7947
7948 if (ident == 0) {
7949 return NULL;
7950 }
7951
7952 sessions = ao2_global_obj_ref(mgr_sessions);
7953 if (!sessions) {
7954 return NULL;
7955 }
7957 ao2_ref(sessions, -1);
7958 while ((session = ao2_iterator_next(&i))) {
7960 if (session->managerid == ident && !session->needdestroy) {
7961 ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
7962 break;
7963 }
7966 }
7968
7969 return session;
7970}
7971
7972/*!
7973 * locate an http session in the list.
7974 * The search keys (nonce) and (username) is value from received
7975 * "Authorization" http header.
7976 * As well as in find_session() function, the value of the nonce can't be zero.
7977 * (0 meansi, that the session used for AMI socket connection).
7978 * Flag (stale) is set, if client used valid, but old, nonce value.
7979 *
7980 */
7981static struct mansession_session *find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
7982{
7984 struct ao2_container *sessions;
7985 struct ao2_iterator i;
7986
7987 if (nonce == 0 || username == NULL || stale == NULL) {
7988 return NULL;
7989 }
7990
7991 sessions = ao2_global_obj_ref(mgr_sessions);
7992 if (!sessions) {
7993 return NULL;
7994 }
7996 ao2_ref(sessions, -1);
7997 while ((session = ao2_iterator_next(&i))) {
7999 if (!strcasecmp(session->username, username) && session->managerid == nonce) {
8000 *stale = 0;
8001 break;
8002 } else if (!strcasecmp(session->username, username) && session->oldnonce == nonce) {
8003 *stale = 1;
8004 break;
8005 }
8008 }
8010
8011 return session;
8012}
8013
8014int astman_is_authed(uint32_t ident)
8015{
8016 int authed;
8018
8019 if (!(session = find_session(ident, 0)))
8020 return 0;
8021
8022 authed = (session->authenticated != 0);
8023
8026
8027 return authed;
8028}
8029
8030int astman_verify_session_readpermissions(uint32_t ident, int perm)
8031{
8032 int result = 0;
8034 struct ao2_container *sessions;
8035 struct ao2_iterator i;
8036
8037 if (ident == 0) {
8038 return 0;
8039 }
8040
8041 sessions = ao2_global_obj_ref(mgr_sessions);
8042 if (!sessions) {
8043 return 0;
8044 }
8046 ao2_ref(sessions, -1);
8047 while ((session = ao2_iterator_next(&i))) {
8049 if ((session->managerid == ident) && (session->readperm & perm)) {
8050 result = 1;
8053 break;
8054 }
8057 }
8059
8060 return result;
8061}
8062
8063int astman_verify_session_writepermissions(uint32_t ident, int perm)
8064{
8065 int result = 0;
8067 struct ao2_container *sessions;
8068 struct ao2_iterator i;
8069
8070 if (ident == 0) {
8071 return 0;
8072 }
8073
8074 sessions = ao2_global_obj_ref(mgr_sessions);
8075 if (!sessions) {
8076 return 0;
8077 }
8079 ao2_ref(sessions, -1);
8080 while ((session = ao2_iterator_next(&i))) {
8082 if ((session->managerid == ident) && (session->writeperm & perm)) {
8083 result = 1;
8086 break;
8087 }
8090 }
8092
8093 return result;
8094}
8095
8096/*
8097 * convert to xml with various conversion:
8098 * mode & 1 -> lowercase;
8099 * mode & 2 -> replace non-alphanumeric chars with underscore
8100 */
8101static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
8102{
8103 /* store in a local buffer to avoid calling ast_str_append too often */
8104 char buf[256];
8105 char *dst = buf;
8106 const char *save = src;
8107 int space = sizeof(buf);
8108 /* repeat until done and nothing to flush */
8109 for ( ; *src || dst != buf ; src++) {
8110 if (*src == '\0' || space < 10) { /* flush */
8111 *dst++ = '\0';
8112 ast_str_append(out, 0, "%s", buf);
8113 dst = buf;
8114 space = sizeof(buf);
8115 if (*src == '\0') {
8116 break;
8117 }
8118 }
8119
8120 if (mode & 2) {
8121 if (save == src && isdigit(*src)) {
8122 /* The first character of an XML attribute cannot be a digit */
8123 *dst++ = '_';
8124 *dst++ = *src;
8125 space -= 2;
8126 continue;
8127 } else if (!isalnum(*src)) {
8128 /* Replace non-alphanumeric with an underscore */
8129 *dst++ = '_';
8130 space--;
8131 continue;
8132 }
8133 }
8134 switch (*src) {
8135 case '<':
8136 strcpy(dst, "&lt;");
8137 dst += 4;
8138 space -= 4;
8139 break;
8140 case '>':
8141 strcpy(dst, "&gt;");
8142 dst += 4;
8143 space -= 4;
8144 break;
8145 case '\"':
8146 strcpy(dst, "&quot;");
8147 dst += 6;
8148 space -= 6;
8149 break;
8150 case '\'':
8151 strcpy(dst, "&apos;");
8152 dst += 6;
8153 space -= 6;
8154 break;
8155 case '&':
8156 strcpy(dst, "&amp;");
8157 dst += 5;
8158 space -= 5;
8159 break;
8160
8161 default:
8162 *dst++ = mode ? tolower(*src) : *src;
8163 space--;
8164 }
8165 }
8166}
8167
8169 char *varname;
8171};
8172
8173static int variable_count_hash_fn(const void *vvc, const int flags)
8174{
8175 const struct variable_count *vc = vvc;
8176
8177 return ast_str_hash(vc->varname);
8178}
8179
8180static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
8181{
8182 /* Due to the simplicity of struct variable_count, it makes no difference
8183 * if you pass in objects or strings, the same operation applies. This is
8184 * due to the fact that the hash occurs on the first element, which means
8185 * the address of both the struct and the string are exactly the same. */
8186 struct variable_count *vc = obj;
8187 char *str = vstr;
8188 return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
8189}
8190
8191/*! \brief Convert the input into XML or HTML.
8192 * The input is supposed to be a sequence of lines of the form
8193 * Name: value
8194 * optionally followed by a blob of unformatted text.
8195 * A blank line is a section separator. Basically, this is a
8196 * mixture of the format of Manager Interface and CLI commands.
8197 * The unformatted text is considered as a single value of a field
8198 * named 'Opaque-data'.
8199 *
8200 * At the moment the output format is the following (but it may
8201 * change depending on future requirements so don't count too
8202 * much on it when writing applications):
8203 *
8204 * General: the unformatted text is used as a value of
8205 * XML output: to be completed
8206 *
8207 * \verbatim
8208 * Each section is within <response type="object" id="xxx">
8209 * where xxx is taken from ajaxdest variable or defaults to unknown
8210 * Each row is reported as an attribute Name="value" of an XML
8211 * entity named from the variable ajaxobjtype, default to "generic"
8212 * \endverbatim
8213 *
8214 * HTML output:
8215 * each Name-value pair is output as a single row of a two-column table.
8216 * Sections (blank lines in the input) are separated by a <HR>
8217 *
8218 */
8219static void xml_translate(struct ast_str **out, char *in, struct ast_variable *get_vars, enum output_format format)
8220{
8221 struct ast_variable *v;
8222 const char *dest = NULL;
8223 char *var, *val;
8224 const char *objtype = NULL;
8225 int in_data = 0; /* parsing data */
8226 int inobj = 0;
8227 int xml = (format == FORMAT_XML);
8228 struct variable_count *vc = NULL;
8229 struct ao2_container *vco = NULL;
8230
8231 if (xml) {
8232 /* dest and objtype need only for XML format */
8233 for (v = get_vars; v; v = v->next) {
8234 if (!strcasecmp(v->name, "ajaxdest")) {
8235 dest = v->value;
8236 } else if (!strcasecmp(v->name, "ajaxobjtype")) {
8237 objtype = v->value;
8238 }
8239 }
8240 if (ast_strlen_zero(dest)) {
8241 dest = "unknown";
8242 }
8243 if (ast_strlen_zero(objtype)) {
8244 objtype = "generic";
8245 }
8246 }
8247
8248 /* we want to stop when we find an empty line */
8249 while (in && *in) {
8250 val = strsep(&in, "\r\n"); /* mark start and end of line */
8251 if (in && *in == '\n') { /* remove trailing \n if any */
8252 in++;
8253 }
8255 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
8256 if (ast_strlen_zero(val)) {
8257 /* empty line */
8258 if (in_data) {
8259 /* close data in Opaque mode */
8260 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
8261 in_data = 0;
8262 }
8263
8264 if (inobj) {
8265 /* close block */
8266 ast_str_append(out, 0, xml ? " /></response>\n" :
8267 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
8268 inobj = 0;
8269 ao2_ref(vco, -1);
8270 vco = NULL;
8271 }
8272 continue;
8273 }
8274
8275 if (!inobj) {
8276 /* start new block */
8277 if (xml) {
8278 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
8279 }
8282 inobj = 1;
8283 }
8284
8285 if (in_data) {
8286 /* Process data field in Opaque mode. This is a
8287 * followup, so we re-add line feeds. */
8288 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
8289 xml_copy_escape(out, val, 0); /* data field */
8290 continue;
8291 }
8292
8293 /* We expect "Name: value" line here */
8294 var = strsep(&val, ":");
8295 if (val) {
8296 /* found the field name */
8299 } else {
8300 /* field name not found, switch to opaque mode */
8301 val = var;
8302 var = "Opaque-data";
8303 in_data = 1;
8304 }
8305
8306
8307 ast_str_append(out, 0, xml ? " " : "<tr><td>");
8308 if ((vc = ao2_find(vco, var, 0))) {
8309 vc->count++;
8310 } else {
8311 /* Create a new entry for this one */
8312 vc = ao2_alloc(sizeof(*vc), NULL);
8313 vc->varname = var;
8314 vc->count = 1;
8315 ao2_link(vco, vc);
8316 }
8317
8318 xml_copy_escape(out, var, xml ? 1 | 2 : 0); /* data name */
8319 if (vc->count > 1) {
8320 ast_str_append(out, 0, "-%d", vc->count);
8321 }
8322 ao2_ref(vc, -1);
8323 ast_str_append(out, 0, xml ? "='" : "</td><td>");
8324 xml_copy_escape(out, val, 0); /* data field */
8325 if (!in_data || !*in) {
8326 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
8327 }
8328 }
8329
8330 if (inobj) {
8331 ast_str_append(out, 0, xml ? " /></response>\n" :
8332 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
8333 ao2_ref(vco, -1);
8334 }
8335}
8336
8337static void close_mansession_file(struct mansession *s)
8338{
8339 if (s->stream) {
8341 s->stream = NULL;
8342 } else {
8343 ast_log(LOG_ERROR, "Attempted to close file/file descriptor on mansession without a valid file or file descriptor.\n");
8344 }
8345}
8346
8347static void process_output(struct mansession *s, struct ast_str **out, struct ast_variable *params, enum output_format format)
8348{
8349 char *buf;
8350 off_t l;
8351 int fd;
8352
8353 if (!s->stream)
8354 return;
8355
8356 /* Ensure buffer is NULL-terminated */
8357 ast_iostream_write(s->stream, "", 1);
8358
8359 fd = ast_iostream_get_fd(s->stream);
8360
8361 l = lseek(fd, 0, SEEK_CUR);
8362 if (l > 0) {
8363 if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0))) {
8364 ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n");
8365 } else {
8366 if (format == FORMAT_XML || format == FORMAT_HTML) {
8367 xml_translate(out, buf, params, format);
8368 } else {
8369 ast_str_append(out, 0, "%s", buf);
8370 }
8371 munmap(buf, l);
8372 }
8373 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
8374 xml_translate(out, "", params, format);
8375 }
8376
8378}
8379
8382 enum output_format format,
8383 const struct ast_sockaddr *remote_address, const char *uri,
8384 struct ast_variable *get_params,
8385 struct ast_variable *headers)
8386{
8387 struct mansession s = { .session = NULL, .tcptls_session = ser };
8389 uint32_t ident;
8390 int fd;
8391 int blastaway = 0;
8392 struct ast_variable *params = get_params;
8393 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
8394 struct ast_str *http_header = NULL, *out = NULL;
8395 struct message m = { 0 };
8396
8398 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
8399 return 0;
8400 }
8401
8402 ident = ast_http_manid_from_vars(headers);
8403
8404 if (!(session = find_session(ident, 1))) {
8405
8406 /**/
8407 /* Create new session.
8408 * While it is not in the list we don't need any locking
8409 */
8410 if (!(session = build_mansession(remote_address))) {
8412 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8413 return 0;
8414 }
8416 session->send_events = 0;
8417 session->inuse = 1;
8418 /*!
8419 * \note There is approximately a 1 in 1.8E19 chance that the following
8420 * calculation will produce 0, which is an invalid ID, but due to the
8421 * properties of the rand() function (and the constancy of s), that
8422 * won't happen twice in a row.
8423 */
8424 while ((session->managerid = ast_random() ^ (unsigned long) session) == 0) {
8425 }
8426 session->last_ev = grab_last();
8427 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
8428 }
8430
8431 http_header = ast_str_create(128);
8432 out = ast_str_create(2048);
8433
8434 ast_mutex_init(&s.lock);
8435
8436 if (http_header == NULL || out == NULL) {
8438 ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)");
8439 goto generic_callback_out;
8440 }
8441
8442 s.session = session;
8443 fd = mkstemp(template); /* create a temporary file for command output */
8444 unlink(template);
8445 if (fd <= -1) {
8446 ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)");
8447 goto generic_callback_out;
8448 }
8450 if (!s.stream) {
8451 ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
8452 ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)");
8453 close(fd);
8454 goto generic_callback_out;
8455 }
8456
8457 if (method == AST_HTTP_POST) {
8458 params = ast_http_get_post_vars(ser, headers);
8459 if (!params) {
8460 switch (errno) {
8461 case EFBIG:
8462 ast_http_error(ser, 413, "Request Entity Too Large", "Body too large");
8464 goto generic_callback_out;
8465 case ENOMEM:
8467 ast_http_error(ser, 500, "Server Error", "Out of memory");
8469 goto generic_callback_out;
8470 case EIO:
8471 ast_http_error(ser, 400, "Bad Request", "Error parsing request body");
8473 goto generic_callback_out;
8474 }
8475 }
8476 }
8477
8478 astman_append_headers(&m, params);
8479
8480 if (process_message(&s, &m)) {
8481 if (session->authenticated) {
8483 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr));
8484 }
8485 } else {
8486 if (displayconnects) {
8487 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_sockaddr_stringify_addr(&session->addr));
8488 }
8489 }
8490 session->needdestroy = 1;
8491 }
8492
8494
8495 ast_str_append(&http_header, 0,
8496 "Content-type: text/%s\r\n"
8497 "Set-Cookie: mansession_id=\"%08x\"; Version=1; Max-Age=%d\r\n"
8498 "Pragma: SuppressEvents\r\n",
8499 contenttype[format],
8500 session->managerid, httptimeout);
8501
8502 if (format == FORMAT_XML) {
8503 ast_str_append(&out, 0, "<ajax-response>\n");
8504 } else if (format == FORMAT_HTML) {
8505 /*
8506 * When handling AMI-over-HTTP in HTML format, we provide a simple form for
8507 * debugging purposes. This HTML code should not be here, we
8508 * should read from some config file...
8509 */
8510
8511#define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
8512#define TEST_STRING \
8513 "<form action=\"manager\" method=\"post\">\n\
8514 Action: <select name=\"action\">\n\
8515 <option value=\"\">-----&gt;</option>\n\
8516 <option value=\"login\">login</option>\n\
8517 <option value=\"command\">Command</option>\n\
8518 <option value=\"waitevent\">waitevent</option>\n\
8519 <option value=\"listcommands\">listcommands</option>\n\
8520 </select>\n\
8521 or <input name=\"action\"><br/>\n\
8522 CLI Command <input name=\"command\"><br>\n\
8523 user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
8524 <input type=\"submit\">\n</form>\n"
8525
8526 ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
8527 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
8528 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
8530 }
8531
8532 process_output(&s, &out, params, format);
8533
8534 if (format == FORMAT_XML) {
8535 ast_str_append(&out, 0, "</ajax-response>\n");
8536 } else if (format == FORMAT_HTML) {
8537 ast_str_append(&out, 0, "</table></body>\r\n");
8538 }
8539
8541 /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */
8542 session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
8543
8544 if (session->needdestroy) {
8545 if (session->inuse == 1) {
8546 ast_debug(1, "Need destroy, doing it now!\n");
8547 blastaway = 1;
8548 } else {
8549 ast_debug(1, "Need destroy, but can't do it yet!\n");
8550 ast_mutex_lock(&session->notify_lock);
8551 if (session->waiting_thread != AST_PTHREADT_NULL) {
8552 pthread_kill(session->waiting_thread, SIGURG);
8553 }
8554 ast_mutex_unlock(&session->notify_lock);
8555 session->inuse--;
8556 }
8557 } else {
8558 session->inuse--;
8559 }
8561
8562 ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
8563 http_header = NULL;
8564 out = NULL;
8565
8566generic_callback_out:
8568
8569 /* Clear resource */
8570
8571 if (method == AST_HTTP_POST && params) {
8572 ast_variables_destroy(params);
8573 }
8574 ast_free(http_header);
8575 ast_free(out);
8576
8577 if (session) {
8578 if (blastaway) {
8580 } else {
8581 if (session->stream) {
8582 ast_iostream_close(session->stream);
8583 session->stream = NULL;
8584 }
8586 }
8587 }
8588
8589 return 0;
8590}
8591
8594 enum output_format format,
8595 const struct ast_sockaddr *remote_address, const char *uri,
8596 struct ast_variable *get_params,
8597 struct ast_variable *headers)
8598{
8600 struct mansession s = { .session = NULL, .tcptls_session = ser };
8601 struct ast_variable *v, *params = get_params;
8602 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
8603 struct ast_str *http_header = NULL, *out = NULL;
8604 size_t result_size;
8605 struct message m = { 0 };
8606 int fd;
8607
8608 time_t time_now = time(NULL);
8609 unsigned long nonce = 0, nc;
8610 struct ast_http_digest d = { NULL, };
8611 struct ast_manager_user *user = NULL;
8612 int stale = 0;
8613 char resp_hash[256]="";
8614 /* Cache for user data */
8615 char u_username[80];
8616 int u_readperm;
8617 int u_writeperm;
8618 int u_writetimeout;
8619 int u_displayconnects;
8620
8622 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
8623 return 0;
8624 }
8625
8626 /* Find "Authorization: " header */
8627 for (v = headers; v; v = v->next) {
8628 if (!strcasecmp(v->name, "Authorization")) {
8629 break;
8630 }
8631 }
8632
8633 if (!v || ast_strlen_zero(v->value)) {
8634 goto out_401; /* Authorization Header not present - send auth request */
8635 }
8636
8637 /* Digest found - parse */
8638 if (ast_string_field_init(&d, 128)) {
8640 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8641 return 0;
8642 }
8643
8644 if (ast_parse_digest(v->value, &d, 0, 1)) {
8645 /* Error in Digest - send new one */
8646 nonce = 0;
8647 goto out_401;
8648 }
8649 if (sscanf(d.nonce, "%30lx", &nonce) != 1) {
8650 ast_log(LOG_WARNING, "Received incorrect nonce in Digest <%s>\n", d.nonce);
8651 nonce = 0;
8652 goto out_401;
8653 }
8654
8656 user = get_manager_by_name_locked(d.username);
8657 if(!user) {
8659 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_sockaddr_stringify_addr(&session->addr), d.username);
8660 nonce = 0;
8661 goto out_401;
8662 }
8663
8664 /* --- We have User for this auth, now check ACL */
8665 if (user->acl && !ast_apply_acl(user->acl, remote_address, "Manager User ACL:")) {
8667 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&session->addr), d.username);
8669 ast_http_error(ser, 403, "Permission denied", "Permission denied");
8670 return 0;
8671 }
8672
8673 /* --- We have auth, so check it */
8674
8675 /* compute the expected response to compare with what we received */
8676 {
8677 char *a2;
8678 /* ast_md5_hash outputs 32 characters plus NULL terminator. */
8679 char a2_hash[33];
8680 char resp[256];
8681
8682 /* XXX Now request method are hardcoded in A2 */
8683 if (ast_asprintf(&a2, "%s:%s", ast_get_http_method(method), d.uri) < 0) {
8686 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8687 return 0;
8688 }
8689
8690 ast_md5_hash(a2_hash, a2);
8691 ast_free(a2);
8692
8693 if (d.qop) {
8694 /* RFC 2617 */
8695 snprintf(resp, sizeof(resp), "%s:%08lx:%s:%s:auth:%s", user->a1_hash, nonce, d.nc, d.cnonce, a2_hash);
8696 } else {
8697 /* RFC 2069 */
8698 snprintf(resp, sizeof(resp), "%s:%08lx:%s", user->a1_hash, nonce, a2_hash);
8699 }
8700 ast_md5_hash(resp_hash, resp);
8701 }
8702
8703 if (strncasecmp(d.response, resp_hash, strlen(resp_hash))) {
8704 /* Something was wrong, so give the client to try with a new challenge */
8706 nonce = 0;
8707 goto out_401;
8708 }
8709
8710 /*
8711 * User are pass Digest authentication.
8712 * Now, cache the user data and unlock user list.
8713 */
8714 ast_copy_string(u_username, user->username, sizeof(u_username));
8715 u_readperm = user->readperm;
8716 u_writeperm = user->writeperm;
8717 u_displayconnects = user->displayconnects;
8718 u_writetimeout = user->writetimeout;
8720
8721 if (!(session = find_session_by_nonce(d.username, nonce, &stale))) {
8722 /*
8723 * Create new session.
8724 * While it is not in the list we don't need any locking
8725 */
8726 if (!(session = build_mansession(remote_address))) {
8728 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8729 return 0;
8730 }
8732
8733 ast_copy_string(session->username, u_username, sizeof(session->username));
8734 session->managerid = nonce;
8735 session->last_ev = grab_last();
8736 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
8737
8738 session->readperm = u_readperm;
8739 session->writeperm = u_writeperm;
8740 session->writetimeout = u_writetimeout;
8741
8742 if (u_displayconnects) {
8743 ast_verb(2, "HTTP Manager '%s' logged in from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr));
8744 }
8745 session->noncetime = session->sessionstart = time_now;
8746 session->authenticated = 1;
8747 } else if (stale) {
8748 /*
8749 * Session found, but nonce is stale.
8750 *
8751 * This could be because an old request (w/old nonce) arrived.
8752 *
8753 * This may be as the result of http proxy usage (separate delay or
8754 * multipath) or in a situation where a page was refreshed too quickly
8755 * (seen in Firefox).
8756 *
8757 * In this situation, we repeat the 401 auth with the current nonce
8758 * value.
8759 */
8760 nonce = session->managerid;
8762 stale = 1;
8763 goto out_401;
8764 } else {
8765 sscanf(d.nc, "%30lx", &nc);
8766 if (session->nc >= nc || ((time_now - session->noncetime) > 62) ) {
8767 /*
8768 * Nonce time expired (> 2 minutes) or something wrong with nonce
8769 * counter.
8770 *
8771 * Create new nonce key and resend Digest auth request. Old nonce
8772 * is saved for stale checking...
8773 */
8774 session->nc = 0; /* Reset nonce counter */
8775 session->oldnonce = session->managerid;
8776 nonce = session->managerid = ast_random();
8777 session->noncetime = time_now;
8779 stale = 1;
8780 goto out_401;
8781 } else {
8782 session->nc = nc; /* All OK, save nonce counter */
8783 }
8784 }
8785
8786
8787 /* Reset session timeout. */
8788 session->sessiontimeout = time(NULL) + (httptimeout > 5 ? httptimeout : 5);
8790
8791 ast_mutex_init(&s.lock);
8792 s.session = session;
8793 fd = mkstemp(template); /* create a temporary file for command output */
8794 unlink(template);
8795 if (fd <= -1) {
8796 ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)");
8797 goto auth_callback_out;
8798 }
8800 if (!s.stream) {
8801 ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
8802 ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)");
8803 close(fd);
8804 goto auth_callback_out;
8805 }
8806
8807 if (method == AST_HTTP_POST) {
8808 params = ast_http_get_post_vars(ser, headers);
8809 if (!params) {
8810 switch (errno) {
8811 case EFBIG:
8812 ast_http_error(ser, 413, "Request Entity Too Large", "Body too large");
8814 goto auth_callback_out;
8815 case ENOMEM:
8817 ast_http_error(ser, 500, "Server Error", "Out of memory");
8819 goto auth_callback_out;
8820 case EIO:
8821 ast_http_error(ser, 400, "Bad Request", "Error parsing request body");
8823 goto auth_callback_out;
8824 }
8825 }
8826 }
8827
8828 astman_append_headers(&m, params);
8829
8830 if (process_message(&s, &m)) {
8831 if (u_displayconnects) {
8832 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr));
8833 }
8834
8835 session->needdestroy = 1;
8836 }
8837
8839
8840 result_size = lseek(ast_iostream_get_fd(s.stream), 0, SEEK_CUR); /* Calculate approx. size of result */
8841
8842 http_header = ast_str_create(80);
8843 out = ast_str_create(result_size * 2 + 512);
8844 if (http_header == NULL || out == NULL) {
8846 ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)");
8848 goto auth_callback_out;
8849 }
8850
8851 ast_str_append(&http_header, 0, "Content-type: text/%s\r\n", contenttype[format]);
8852
8853 if (format == FORMAT_XML) {
8854 ast_str_append(&out, 0, "<ajax-response>\n");
8855 } else if (format == FORMAT_HTML) {
8856 ast_str_append(&out, 0,
8857 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
8858 "<html><head>\r\n"
8859 "<title>Asterisk&trade; Manager Interface</title>\r\n"
8860 "</head><body style=\"background-color: #ffffff;\">\r\n"
8861 "<form method=\"POST\">\r\n"
8862 "<table align=\"center\" style=\"background-color: #f1f1f1;\" width=\"500\">\r\n"
8863 "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\"><h1>Manager Tester</h1></th></tr>\r\n"
8864 "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\">Action: <input name=\"action\" /> Cmd: <input name=\"command\" /><br>"
8865 "<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n");
8866 }
8867
8868 process_output(&s, &out, params, format);
8869
8870 if (format == FORMAT_XML) {
8871 ast_str_append(&out, 0, "</ajax-response>\n");
8872 } else if (format == FORMAT_HTML) {
8873 ast_str_append(&out, 0, "</table></form></body></html>\r\n");
8874 }
8875
8876 ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
8877 http_header = NULL;
8878 out = NULL;
8879
8880auth_callback_out:
8882
8883 /* Clear resources and unlock manager session */
8884 if (method == AST_HTTP_POST && params) {
8885 ast_variables_destroy(params);
8886 }
8887
8888 ast_free(http_header);
8889 ast_free(out);
8890
8892 if (session->stream) {
8893 ast_iostream_close(session->stream);
8894 session->stream = NULL;
8895 }
8897
8898 if (session->needdestroy) {
8899 ast_debug(1, "Need destroy, doing it now!\n");
8901 }
8903 return 0;
8904
8905out_401:
8906 if (!nonce) {
8907 nonce = ast_random();
8908 }
8909
8910 ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL);
8912 return 0;
8913}
8914
8915static 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)
8916{
8917 int retval;
8918 struct ast_sockaddr ser_remote_address_tmp;
8919
8920 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8921 retval = generic_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
8922 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8923 return retval;
8924}
8925
8926static 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)
8927{
8928 int retval;
8929 struct ast_sockaddr ser_remote_address_tmp;
8930
8931 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8932 retval = generic_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
8933 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8934 return retval;
8935}
8936
8937static 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)
8938{
8939 int retval;
8940 struct ast_sockaddr ser_remote_address_tmp;
8941
8942 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8943 retval = generic_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
8944 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8945 return retval;
8946}
8947
8948static struct ast_http_uri rawmanuri = {
8949 .description = "Raw HTTP Manager Event Interface",
8950 .uri = "rawman",
8951 .callback = rawman_http_callback,
8952 .data = NULL,
8953 .key = __FILE__,
8954};
8955
8956static struct ast_http_uri manageruri = {
8957 .description = "HTML Manager Event Interface",
8958 .uri = "manager",
8959 .callback = manager_http_callback,
8960 .data = NULL,
8961 .key = __FILE__,
8962};
8963
8965 .description = "XML Manager Event Interface",
8966 .uri = "mxml",
8967 .callback = mxml_http_callback,
8968 .data = NULL,
8969 .key = __FILE__,
8970};
8971
8972
8973/* Callback with Digest authentication */
8974static 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)
8975{
8976 int retval;
8977 struct ast_sockaddr ser_remote_address_tmp;
8978
8979 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8980 retval = auth_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
8981 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8982 return retval;
8983}
8984
8985static 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)
8986{
8987 int retval;
8988 struct ast_sockaddr ser_remote_address_tmp;
8989
8990 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8991 retval = auth_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
8992 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8993 return retval;
8994}
8995
8996static 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)
8997{
8998 int retval;
8999 struct ast_sockaddr ser_remote_address_tmp;
9000
9001 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
9002 retval = auth_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
9003 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
9004 return retval;
9005}
9006
9007static struct ast_http_uri arawmanuri = {
9008 .description = "Raw HTTP Manager Event Interface w/Digest authentication",
9009 .uri = "arawman",
9010 .has_subtree = 0,
9011 .callback = auth_rawman_http_callback,
9012 .data = NULL,
9013 .key = __FILE__,
9014};
9015
9016static struct ast_http_uri amanageruri = {
9017 .description = "HTML Manager Event Interface w/Digest authentication",
9018 .uri = "amanager",
9019 .has_subtree = 0,
9020 .callback = auth_manager_http_callback,
9021 .data = NULL,
9022 .key = __FILE__,
9023};
9024
9026 .description = "XML Manager Event Interface w/Digest authentication",
9027 .uri = "amxml",
9028 .has_subtree = 0,
9029 .callback = auth_mxml_http_callback,
9030 .data = NULL,
9031 .key = __FILE__,
9032};
9033
9034/*! \brief Get number of logged in sessions for a login name */
9035static int get_manager_sessions_cb(void *obj, void *arg, void *data, int flags)
9036{
9037 struct mansession_session *session = obj;
9038 const char *login = (char *)arg;
9039 int *no_sessions = data;
9040
9041 if (strcasecmp(session->username, login) == 0) {
9042 (*no_sessions)++;
9043 }
9044
9045 return 0;
9046}
9047
9048
9049/*! \brief ${AMI_CLIENT()} Dialplan function - reads manager client data */
9050static int function_amiclient(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9051{
9052 struct ast_manager_user *user = NULL;
9053
9056 AST_APP_ARG(param);
9057 );
9058
9059
9060 if (ast_strlen_zero(data) ) {
9061 ast_log(LOG_WARNING, "AMI_CLIENT() requires two arguments: AMI_CLIENT(<name>[,<arg>])\n");
9062 return -1;
9063 }
9065 args.name = ast_strip(args.name);
9066 args.param = ast_strip(args.param);
9067
9069 if (!(user = get_manager_by_name_locked(args.name))) {
9071 ast_log(LOG_ERROR, "There's no manager user called : \"%s\"\n", args.name);
9072 return -1;
9073 }
9075
9076 if (!strcasecmp(args.param, "sessions")) {
9077 int no_sessions = 0;
9078 struct ao2_container *sessions;
9079
9080 sessions = ao2_global_obj_ref(mgr_sessions);
9081 if (sessions) {
9082 ao2_callback_data(sessions, 0, get_manager_sessions_cb, /*login name*/ data, &no_sessions);
9083 ao2_ref(sessions, -1);
9084 }
9085 snprintf(buf, len, "%d", no_sessions);
9086 } else {
9087 ast_log(LOG_ERROR, "Invalid arguments provided to function AMI_CLIENT: %s\n", args.param);
9088 return -1;
9089
9090 }
9091
9092 return 0;
9093}
9094
9095
9096/*! \brief description of AMI_CLIENT dialplan function */
9098 .name = "AMI_CLIENT",
9099 .read = function_amiclient,
9100 .read_max = 12,
9101};
9102
9103static int webregged = 0;
9104
9105/*! \brief cleanup code called at each iteration of server_root,
9106 * guaranteed to happen every 5 seconds at most
9107 */
9108static void purge_old_stuff(void *data)
9109{
9110 struct ast_tcptls_session_args *ser = data;
9111 /* purge_sessions will return the number of sessions actually purged,
9112 * up to a maximum of it's arguments, purge one at a time, keeping a
9113 * purge interval of 1ms as long as we purged a session, otherwise
9114 * revert to a purge check every 5s
9115 */
9116 if (purge_sessions(1) == 1) {
9117 ser->poll_timeout = 1;
9118 } else {
9119 ser->poll_timeout = 5000;
9120 }
9121 purge_events();
9122}
9123
9126 .accept_fd = -1,
9127 .master = AST_PTHREADT_NULL,
9128 .tls_cfg = NULL,
9129 .poll_timeout = 5000, /* wake up every 5 seconds */
9130 .periodic_fn = purge_old_stuff,
9131 .name = "AMI server",
9132 .accept_fn = ast_tcptls_server_root, /* thread doing the accept() */
9133 .worker_fn = session_do, /* thread handling the session */
9134};
9135
9137 .accept_fd = -1,
9138 .master = AST_PTHREADT_NULL,
9139 .tls_cfg = &ami_tls_cfg,
9140 .poll_timeout = -1, /* the other does the periodic cleanup */
9141 .name = "AMI TLS server",
9142 .accept_fn = ast_tcptls_server_root, /* thread doing the accept() */
9143 .worker_fn = session_do, /* thread handling the session */
9144};
9145
9146/*! \brief CLI command manager show settings */
9147static char *handle_manager_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9148{
9149 switch (cmd) {
9150 case CLI_INIT:
9151 e->command = "manager show settings";
9152 e->usage =
9153 "Usage: manager show settings\n"
9154 " Provides detailed list of the configuration of the Manager.\n";
9155 return NULL;
9156 case CLI_GENERATE:
9157 return NULL;
9158 }
9159#define FORMAT " %-25.25s %-15.55s\n"
9160#define FORMAT2 " %-25.25s %-15d\n"
9161#define FORMAT3 " %-25.25s %s\n"
9162 if (a->argc != 3) {
9163 return CLI_SHOWUSAGE;
9164 }
9165 ast_cli(a->fd, "\nGlobal Settings:\n");
9166 ast_cli(a->fd, "----------------\n");
9167 ast_cli(a->fd, FORMAT, "Manager (AMI):", AST_CLI_YESNO(manager_enabled));
9168 ast_cli(a->fd, FORMAT, "Web Manager (AMI/HTTP):", AST_CLI_YESNO(webmanager_enabled));
9169 ast_cli(a->fd, FORMAT, "TCP Bindaddress:", manager_enabled != 0 ? ast_sockaddr_stringify(&ami_desc.local_address) : "Disabled");
9170 ast_cli(a->fd, FORMAT2, "HTTP Timeout (seconds):", httptimeout);
9171 ast_cli(a->fd, FORMAT, "TLS Enable:", AST_CLI_YESNO(ami_tls_cfg.enabled));
9172 ast_cli(a->fd, FORMAT, "TLS Bindaddress:", ami_tls_cfg.enabled != 0 ? ast_sockaddr_stringify(&amis_desc.local_address) : "Disabled");
9173 ast_cli(a->fd, FORMAT, "TLS Certfile:", ami_tls_cfg.certfile);
9174 ast_cli(a->fd, FORMAT, "TLS Privatekey:", ami_tls_cfg.pvtfile);
9175 ast_cli(a->fd, FORMAT, "TLS Cipher:", ami_tls_cfg.cipher);
9176 ast_cli(a->fd, FORMAT, "Allow multiple login:", AST_CLI_YESNO(allowmultiplelogin));
9177 ast_cli(a->fd, FORMAT, "Display connects:", AST_CLI_YESNO(displayconnects));
9178 ast_cli(a->fd, FORMAT, "Timestamp events:", AST_CLI_YESNO(timestampevents));
9179 ast_cli(a->fd, FORMAT3, "Channel vars:", S_OR(manager_channelvars, ""));
9180 ast_cli(a->fd, FORMAT3, "Disabled events:", S_OR(manager_disabledevents, ""));
9181 ast_cli(a->fd, FORMAT, "Debug:", AST_CLI_YESNO(manager_debug));
9182#undef FORMAT
9183#undef FORMAT2
9184#undef FORMAT3
9185
9186 return CLI_SUCCESS;
9187}
9188
9189#ifdef AST_XML_DOCS
9190
9191/* Sort function for ast_xml_doc_item by name field */
9193
9194static int event_max_name_len_cb(void *obj, void *arg, void *data, int flags)
9195{
9196 struct ast_xml_doc_item *item = obj;
9197 int *max_len = data;
9198 int len = strlen(item->name);
9199
9200 if (len > *max_len) {
9201 *max_len = len;
9202 }
9203
9204 return 0;
9205}
9206
9207static char *handle_manager_show_events(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9208{
9209 struct ao2_container *events;
9210 struct ao2_container *sorted_events;
9211 struct ao2_iterator it_events;
9212 struct ast_xml_doc_item *item;
9213 struct ast_str *buffer;
9214 int col = 0;
9215 int maxlen = 0;
9216 const char *dashes = "--------------------------------------------------------------------------------";
9217
9218 switch (cmd) {
9219 case CLI_INIT:
9220 e->command = "manager show events";
9221 e->usage =
9222 "Usage: manager show events\n"
9223 " Prints a listing of the available Asterisk manager interface events.\n";
9224 return NULL;
9225 case CLI_GENERATE:
9226 return NULL;
9227 }
9228 if (a->argc != 3) {
9229 return CLI_SHOWUSAGE;
9230 }
9231
9232 buffer = ast_str_create(128);
9233 if (!buffer) {
9234 return CLI_SUCCESS;
9235 }
9236
9237 events = ao2_global_obj_ref(event_docs);
9238 if (!events) {
9239 ast_cli(a->fd, "No manager event documentation loaded\n");
9240 ast_free(buffer);
9241 return CLI_SUCCESS;
9242 }
9243
9247 ast_xml_doc_item_sort_fn, NULL);
9248 if (!sorted_events) {
9250 ast_log(AST_LOG_ERROR, "Unable to create sorted container for events\n");
9251 ast_free(buffer);
9252 ao2_ref(events, -1);
9253 return CLI_SUCCESS;
9254 }
9255 ao2_container_dup(sorted_events, events, 0);
9257 ao2_ref(events, -1);
9258
9259 ao2_callback_data(sorted_events, OBJ_NODATA, event_max_name_len_cb, NULL, &maxlen);
9260 it_events = ao2_iterator_init(sorted_events, AO2_ITERATOR_DONTLOCK);
9261
9262 ast_cli(a->fd, "Events:\n");
9263 ast_cli(a->fd, " %.*s %.*s %.*s \n", maxlen, dashes, maxlen, dashes, maxlen, dashes);
9264
9265 while ((item = ao2_iterator_next(&it_events))) {
9266 ast_str_append(&buffer, 0, " %-*s", maxlen, item->name);
9267 if (++col % 3 == 0) {
9268 ast_cli(a->fd, "%s\n", ast_str_buffer(buffer));
9269 ast_str_set(&buffer, 0, "%s", "");
9270 }
9271 ao2_ref(item, -1);
9272 }
9273 ao2_iterator_destroy(&it_events);
9274
9275 if (col % 3 != 0) {
9276 ast_cli(a->fd, "%s\n", ast_str_buffer(buffer));
9277 }
9278 ast_cli(a->fd, "\n%d events registered.\n", col);
9279
9280 ao2_ref(sorted_events, -1);
9281 ast_free(buffer);
9282
9283 return CLI_SUCCESS;
9284}
9285
9286static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance)
9287{
9288 char *since, *syntax, *provided_by, *description, *synopsis, *seealso, *arguments;
9289
9290 synopsis = ast_xmldoc_printable(AS_OR(instance->synopsis, "Not available"), 1);
9291 provided_by = ast_xmldoc_printable(AS_OR(instance->provided_by, "Not available"), 1);
9292 since = ast_xmldoc_printable(AS_OR(instance->since, "Not available"), 1);
9293 description = ast_xmldoc_printable(AS_OR(instance->description, "Not available"), 1);
9294 syntax = ast_xmldoc_printable(AS_OR(instance->syntax, "Not available"), 1);
9295 arguments = ast_xmldoc_printable(AS_OR(instance->arguments, "Not available"), 1);
9296 seealso = ast_xmldoc_printable(AS_OR(instance->seealso, "Not available"), 1);
9297
9298 if (!synopsis || !provided_by || !since || !description || !syntax || !arguments || !seealso) {
9299 ast_cli(a->fd, "Error: Memory allocation failed\n");
9300 goto free_docs;
9301 }
9302
9303 ast_cli(a->fd, "\n"
9304 "%s -= Info about Manager Event '%s' =- %s\n\n"
9305 COLORIZE_FMT "\n"
9306 "%s\n\n"
9307 COLORIZE_FMT "\n"
9308 "%s\n\n"
9309 COLORIZE_FMT "\n"
9310 "%s\n\n"
9311 COLORIZE_FMT "\n"
9312 "%s\n\n"
9313 COLORIZE_FMT "\n"
9314 "%s\n\n"
9315 COLORIZE_FMT "\n"
9316 "%s\n\n"
9317 COLORIZE_FMT "\n"
9318 "%s\n\n",
9320 COLORIZE(COLOR_MAGENTA, 0, "[Synopsis]"), synopsis,
9321 COLORIZE(COLOR_MAGENTA, 0, "[Provided By]"), provided_by,
9322 COLORIZE(COLOR_MAGENTA, 0, "[Since]"), since,
9323 COLORIZE(COLOR_MAGENTA, 0, "[Description]"), description,
9324 COLORIZE(COLOR_MAGENTA, 0, "[Syntax]"), syntax,
9325 COLORIZE(COLOR_MAGENTA, 0, "[Arguments]"), arguments,
9326 COLORIZE(COLOR_MAGENTA, 0, "[See Also]"), seealso
9327 );
9328
9329free_docs:
9331 ast_free(since);
9332 ast_free(description);
9333 ast_free(syntax);
9334 ast_free(arguments);
9335 ast_free(seealso);
9336}
9337
9338static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9339{
9341 struct ao2_iterator it_events;
9342 struct ast_xml_doc_item *item, *temp;
9343 int length;
9344
9345 if (cmd == CLI_INIT) {
9346 e->command = "manager show event";
9347 e->usage =
9348 "Usage: manager show event <eventname>\n"
9349 " Provides a detailed description a Manager interface event.\n";
9350 return NULL;
9351 }
9352
9353 events = ao2_global_obj_ref(event_docs);
9354 if (!events) {
9355 ast_cli(a->fd, "No manager event documentation loaded\n");
9356 return CLI_SUCCESS;
9357 }
9358
9359 if (cmd == CLI_GENERATE) {
9360 if (a->pos != 3) {
9361 return NULL;
9362 }
9363
9364 length = strlen(a->word);
9365 it_events = ao2_iterator_init(events, 0);
9366 while ((item = ao2_iterator_next(&it_events))) {
9367 if (!strncasecmp(a->word, item->name, length)) {
9369 ao2_ref(item, -1);
9370 break;
9371 }
9372 }
9373 ao2_ref(item, -1);
9374 }
9375 ao2_iterator_destroy(&it_events);
9376
9377 return NULL;
9378 }
9379
9380 if (a->argc != 4) {
9381 return CLI_SHOWUSAGE;
9382 }
9383
9384 if (!(item = ao2_find(events, a->argv[3], OBJ_KEY))) {
9385 ast_cli(a->fd, "Could not find event '%s'\n", a->argv[3]);
9386 return CLI_SUCCESS;
9387 }
9388
9389 ast_cli(a->fd, "Event: %s\n", a->argv[3]);
9390 for (temp = item; temp; temp = AST_LIST_NEXT(temp, next)) {
9391 print_event_instance(a, temp);
9392 }
9393
9394 ao2_ref(item, -1);
9395 return CLI_SUCCESS;
9396}
9397
9398#endif
9399
9400static struct ast_cli_entry cli_manager[] = {
9401 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
9402 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
9403 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
9404 AST_CLI_DEFINE(handle_kickmanconn, "Kick a connected manager interface connection"),
9405 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
9406 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
9407 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
9408 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
9409 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
9410 AST_CLI_DEFINE(handle_manager_show_settings, "Show manager global settings"),
9411#ifdef AST_XML_DOCS
9412 AST_CLI_DEFINE(handle_manager_show_events, "List manager interface events"),
9413 AST_CLI_DEFINE(handle_manager_show_event, "Show a manager interface event"),
9414#endif
9415};
9416
9417/*!
9418 * \internal
9419 * \brief Load the config channelvars variable.
9420 *
9421 * \param var Config variable to load.
9422 */
9424{
9425 char *parse = NULL;
9427 AST_APP_ARG(vars)[MAX_VARS];
9428 );
9429
9432
9433 /* parse the setting */
9436
9438}
9439
9440/*!
9441 * \internal
9442 * \brief Load the config disabledevents variable.
9443 *
9444 * \param var Config variable to load.
9445 */
9451
9452/*!
9453 * \internal
9454 * \brief Free a user record. Should already be removed from the list
9455 */
9457{
9458 ast_free(user->a1_hash);
9459 ast_free(user->secret);
9460 if (user->includefilters) {
9461 ao2_t_ref(user->includefilters, -1, "decrement ref for include container, should be last one");
9462 }
9463 if (user->excludefilters) {
9464 ao2_t_ref(user->excludefilters, -1, "decrement ref for exclude container, should be last one");
9465 }
9466 user->acl = ast_free_acl_list(user->acl);
9467 ast_variables_destroy(user->chanvars);
9468 ast_free(user);
9469}
9470
9471/*!
9472 * \internal
9473 * \brief Clean up resources on Asterisk shutdown
9474 */
9475static void manager_shutdown(void)
9476{
9477 struct ast_manager_user *user;
9478
9479#ifdef TEST_FRAMEWORK
9480 AST_TEST_UNREGISTER(eventfilter_test_creation);
9481 AST_TEST_UNREGISTER(eventfilter_test_matching);
9482 AST_TEST_UNREGISTER(originate_permissions_test);
9483#endif
9484
9485 /* This event is not actually transmitted, but causes all TCP sessions to be closed */
9486 manager_event(EVENT_FLAG_SHUTDOWN, "CloseSession", "CloseSession: true\r\n");
9487
9488 ast_manager_unregister("Ping");
9489 ast_manager_unregister("Events");
9490 ast_manager_unregister("Logoff");
9491 ast_manager_unregister("Login");
9492 ast_manager_unregister("Challenge");
9493 ast_manager_unregister("Hangup");
9494 ast_manager_unregister("Status");
9495 ast_manager_unregister("Setvar");
9496 ast_manager_unregister("Getvar");
9497 ast_manager_unregister("GetConfig");
9498 ast_manager_unregister("GetConfigJSON");
9499 ast_manager_unregister("UpdateConfig");
9500 ast_manager_unregister("CreateConfig");
9501 ast_manager_unregister("ListCategories");
9502 ast_manager_unregister("Redirect");
9503 ast_manager_unregister("Atxfer");
9504 ast_manager_unregister("CancelAtxfer");
9505 ast_manager_unregister("Originate");
9506 ast_manager_unregister("Command");
9507 ast_manager_unregister("ExtensionState");
9508 ast_manager_unregister("PresenceState");
9509 ast_manager_unregister("AbsoluteTimeout");
9510 ast_manager_unregister("MailboxStatus");
9511 ast_manager_unregister("MailboxCount");
9512 ast_manager_unregister("ListCommands");
9513 ast_manager_unregister("SendText");
9514 ast_manager_unregister("UserEvent");
9515 ast_manager_unregister("WaitEvent");
9516 ast_manager_unregister("CoreSettings");
9517 ast_manager_unregister("CoreStatus");
9518 ast_manager_unregister("Reload");
9519 ast_manager_unregister("LoggerRotate");
9520 ast_manager_unregister("CoreShowChannels");
9521 ast_manager_unregister("CoreShowChannelMap");
9522 ast_manager_unregister("ModuleLoad");
9523 ast_manager_unregister("ModuleCheck");
9524 ast_manager_unregister("AOCMessage");
9525 ast_manager_unregister("Filter");
9526 ast_manager_unregister("BlindTransfer");
9529
9530#ifdef AST_XML_DOCS
9531 ao2_t_global_obj_release(event_docs, "Dispose of event_docs");
9532#endif
9533
9534#ifdef TEST_FRAMEWORK
9535 stasis_forward_cancel(test_suite_forwarder);
9536 test_suite_forwarder = NULL;
9537#endif
9538
9539 if (stasis_router) {
9542 }
9550
9553
9564
9565 ao2_global_obj_release(mgr_sessions);
9566
9567 while ((user = AST_LIST_REMOVE_HEAD(&users, list))) {
9569 }
9571
9574}
9575
9576
9577/*! \brief Initialize all \ref stasis topics and routers used by the various
9578 * sub-components of AMI
9579 */
9612
9613static int subscribe_all(void)
9614{
9616 ast_log(AST_LOG_ERROR, "Failed to initialize manager subscriptions\n");
9617 return -1;
9618 }
9619 if (manager_system_init()) {
9620 ast_log(AST_LOG_ERROR, "Failed to initialize manager system handling\n");
9621 return -1;
9622 }
9623 if (manager_channels_init()) {
9624 ast_log(AST_LOG_ERROR, "Failed to initialize manager channel handling\n");
9625 return -1;
9626 }
9627 if (manager_mwi_init()) {
9628 ast_log(AST_LOG_ERROR, "Failed to initialize manager MWI handling\n");
9629 return -1;
9630 }
9631 if (manager_bridging_init()) {
9632 return -1;
9633 }
9634 if (manager_endpoints_init()) {
9635 ast_log(AST_LOG_ERROR, "Failed to initialize manager endpoints handling\n");
9636 return -1;
9637 }
9638
9639 subscribed = 1;
9640 return 0;
9641}
9642
9670
9671static int __init_manager(int reload, int by_external_config)
9672{
9673 struct ast_config *cfg = NULL;
9674 const char *val;
9675 char *cat = NULL;
9676 int newhttptimeout = 60;
9677 struct ast_manager_user *user = NULL;
9678 struct ast_variable *var;
9679 struct ast_flags config_flags = { (reload && !by_external_config) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9680 char a1[337];
9681 char a1_hash[256];
9682 struct ast_sockaddr ami_desc_local_address_tmp;
9683 struct ast_sockaddr amis_desc_local_address_tmp;
9684 int tls_was_enabled = 0;
9685 int acl_subscription_flag = 0;
9686
9687 if (!reload) {
9688 struct ao2_container *sessions;
9689#ifdef AST_XML_DOCS
9690 struct ao2_container *temp_event_docs;
9691#endif
9692 int res;
9693
9695 if (res != 0) {
9696 return -1;
9697 }
9698 manager_topic = stasis_topic_create("manager:core");
9699 if (!manager_topic) {
9700 return -1;
9701 }
9702
9703 /* Register default actions */
9743
9744#ifdef TEST_FRAMEWORK
9745 test_suite_forwarder = stasis_forward_all(ast_test_suite_topic(), manager_topic);
9746#endif
9747
9751
9752 /* Append placeholder event so master_eventq never runs dry */
9753 if (append_event("Event: Placeholder\r\n\r\n",
9754 ast_str_hash("Placeholder"), 0)) {
9755 return -1;
9756 }
9757
9758#ifdef AST_XML_DOCS
9759 temp_event_docs = ast_xmldoc_build_documentation("managerEvent");
9760 if (temp_event_docs) {
9761 ao2_t_global_obj_replace_unref(event_docs, temp_event_docs, "Toss old event docs");
9762 ao2_t_ref(temp_event_docs, -1, "Remove creation ref - container holds only ref now");
9763 }
9764#endif
9765
9766 /* If you have a NULL hash fn, you only need a single bucket */
9768 if (!sessions) {
9769 return -1;
9770 }
9772 ao2_ref(sessions, -1);
9773
9774 /* Initialize all settings before first configuration load. */
9776 }
9777
9778 cfg = ast_config_load2("manager.conf", "manager", config_flags);
9779 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9780 return 0;
9781 } else if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
9782 ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid.\n");
9783 return 0;
9784 }
9785
9786 /* 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. */
9787 if (!by_external_config) {
9789 }
9790
9791 if (reload) {
9792 /* Reset all settings before reloading configuration */
9793 tls_was_enabled = ami_tls_cfg.enabled;
9795 }
9796
9797 ast_sockaddr_parse(&ami_desc_local_address_tmp, "[::]", 0);
9798 ast_sockaddr_set_port(&ami_desc_local_address_tmp, DEFAULT_MANAGER_PORT);
9799
9800 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
9801 val = var->value;
9802
9803 /* read tls config options while preventing unsupported options from being set */
9804 if (strcasecmp(var->name, "tlscafile")
9805 && strcasecmp(var->name, "tlscapath")
9806 && strcasecmp(var->name, "tlscadir")
9807 && strcasecmp(var->name, "tlsverifyclient")
9808 && strcasecmp(var->name, "tlsdontverifyserver")
9809 && strcasecmp(var->name, "tlsclientmethod")
9810 && strcasecmp(var->name, "sslclientmethod")
9812 continue;
9813 }
9814
9815 if (!strcasecmp(var->name, "enabled")) {
9817 } else if (!strcasecmp(var->name, "webenabled")) {
9819 } else if (!strcasecmp(var->name, "port")) {
9820 int bindport;
9821 if (ast_parse_arg(val, PARSE_UINT32|PARSE_IN_RANGE, &bindport, 1024, 65535)) {
9822 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
9823 }
9824 ast_sockaddr_set_port(&ami_desc_local_address_tmp, bindport);
9825 } else if (!strcasecmp(var->name, "bindaddr")) {
9826 /* remember port if it has already been set */
9827 int setport = ast_sockaddr_port(&ami_desc_local_address_tmp);
9828
9830 ast_log(LOG_WARNING, "Invalid address '%s' specified, default '%s' will be used\n", val,
9831 ast_sockaddr_stringify_addr(&ami_desc_local_address_tmp));
9832 } else {
9833 ast_sockaddr_parse(&ami_desc_local_address_tmp, val, PARSE_PORT_IGNORE);
9834 }
9835
9836 if (setport) {
9837 ast_sockaddr_set_port(&ami_desc_local_address_tmp, setport);
9838 }
9839
9840 } else if (!strcasecmp(var->name, "brokeneventsaction")) {
9842 } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
9844 } else if (!strcasecmp(var->name, "displayconnects")) {
9846 } else if (!strcasecmp(var->name, "timestampevents")) {
9848 } else if (!strcasecmp(var->name, "debug")) {
9850 } else if (!strcasecmp(var->name, "httptimeout")) {
9851 newhttptimeout = atoi(val);
9852 } else if (!strcasecmp(var->name, "authtimeout")) {
9853 int timeout = atoi(var->value);
9854
9855 if (timeout < 1) {
9856 ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", var->value);
9857 } else {
9858 authtimeout = timeout;
9859 }
9860 } else if (!strcasecmp(var->name, "authlimit")) {
9861 int limit = atoi(var->value);
9862
9863 if (limit < 1) {
9864 ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", var->value);
9865 } else {
9866 authlimit = limit;
9867 }
9868 } else if (!strcasecmp(var->name, "channelvars")) {
9870 } else if (!strcasecmp(var->name, "disabledevents")) {
9872 } else {
9873 ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
9874 var->name, val);
9875 }
9876 }
9877
9878 if (manager_enabled && !subscribed) {
9879 if (subscribe_all() != 0) {
9880 ast_log(LOG_ERROR, "Manager subscription error\n");
9881 return -1;
9882 }
9883 }
9884
9885 ast_sockaddr_copy(&amis_desc_local_address_tmp, &amis_desc.local_address);
9886
9887 /* if the amis address has not been set, default is the same as non secure ami */
9888 if (ast_sockaddr_isnull(&amis_desc_local_address_tmp)) {
9889 ast_sockaddr_copy(&amis_desc_local_address_tmp, &ami_desc_local_address_tmp);
9890 }
9891
9892 /* if the amis address was not set, it will have non-secure ami port set; if
9893 amis address was set, we need to check that a port was set or not, if not
9894 use the default tls port */
9895 if (ast_sockaddr_port(&amis_desc_local_address_tmp) == 0 ||
9896 (ast_sockaddr_port(&ami_desc_local_address_tmp) == ast_sockaddr_port(&amis_desc_local_address_tmp))) {
9897
9898 ast_sockaddr_set_port(&amis_desc_local_address_tmp, DEFAULT_MANAGER_TLS_PORT);
9899 }
9900
9901 if (manager_enabled) {
9902 ast_sockaddr_copy(&ami_desc.local_address, &ami_desc_local_address_tmp);
9903 ast_sockaddr_copy(&amis_desc.local_address, &amis_desc_local_address_tmp);
9904 }
9905
9907
9908 while ((cat = ast_category_browse(cfg, cat))) {
9909 struct ast_acl_list *oldacl;
9910
9911 if (!strcasecmp(cat, "general")) {
9912 continue;
9913 }
9914
9915 /* Look for an existing entry, if none found - create one and add it to the list */
9916 if (!(user = get_manager_by_name_locked(cat))) {
9917 if (!(user = ast_calloc(1, sizeof(*user)))) {
9918 break;
9919 }
9920 /* Copy name over */
9921 ast_copy_string(user->username, cat, sizeof(user->username));
9922
9923 user->acl = NULL;
9924 user->readperm = 0;
9925 user->writeperm = 0;
9926 /* Default displayconnect from [general] */
9927 user->displayconnects = displayconnects;
9928 /* Default allowmultiplelogin from [general] */
9929 user->allowmultiplelogin = allowmultiplelogin;
9930 user->writetimeout = 100;
9933 if (!user->includefilters || !user->excludefilters) {
9935 break;
9936 }
9937
9938 /* Insert into list */
9940 } else {
9941 ao2_t_callback(user->includefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all include filters");
9942 ao2_t_callback(user->excludefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all exclude filters");
9943 }
9944
9945 /* Make sure we keep this user and don't destroy it during cleanup */
9946 user->keep = 1;
9947 oldacl = user->acl;
9948 user->acl = NULL;
9949 ast_variables_destroy(user->chanvars);
9950
9951 var = ast_variable_browse(cfg, cat);
9952 for (; var; var = var->next) {
9953 if (!strcasecmp(var->name, "secret")) {
9954 ast_free(user->secret);
9955 user->secret = ast_strdup(var->value);
9956 } else if (!strcasecmp(var->name, "deny") ||
9957 !strcasecmp(var->name, "permit") ||
9958 !strcasecmp(var->name, "acl")) {
9959 int acl_error = 0;
9960
9961 ast_append_acl(var->name, var->value, &user->acl, &acl_error, &acl_subscription_flag);
9962 if (acl_error) {
9963 ast_log(LOG_ERROR, "Invalid ACL '%s' for manager user '%s' on line %d. Deleting user\n",
9964 var->value, user->username, var->lineno);
9965 user->keep = 0;
9966 }
9967 } else if (!strcasecmp(var->name, "read") ) {
9968 user->readperm = get_perm(var->value);
9969 } else if (!strcasecmp(var->name, "write") ) {
9970 user->writeperm = get_perm(var->value);
9971 } else if (!strcasecmp(var->name, "displayconnects") ) {
9972 user->displayconnects = ast_true(var->value);
9973 } else if (!strcasecmp(var->name, "allowmultiplelogin") ) {
9974 user->allowmultiplelogin = ast_true(var->value);
9975 } else if (!strcasecmp(var->name, "writetimeout")) {
9976 int value = atoi(var->value);
9977 if (value < 100) {
9978 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
9979 } else {
9980 user->writetimeout = value;
9981 }
9982 } else if (!strcasecmp(var->name, "setvar")) {
9983 struct ast_variable *tmpvar;
9984 char varbuf[256];
9985 char *varval;
9986 char *varname;
9987
9988 ast_copy_string(varbuf, var->value, sizeof(varbuf));
9989 varname = varbuf;
9990
9991 if ((varval = strchr(varname,'='))) {
9992 *varval++ = '\0';
9993 if ((tmpvar = ast_variable_new(varname, varval, ""))) {
9994 tmpvar->next = user->chanvars;
9995 user->chanvars = tmpvar;
9996 }
9997 }
9998 } else if (ast_begins_with(var->name, "eventfilter")) {
9999 const char *value = var->value;
10000 manager_add_filter(var->name, value, user->includefilters, user->excludefilters);
10001 } else {
10002 ast_debug(1, "%s is an unknown option.\n", var->name);
10003 }
10004 }
10005
10006 oldacl = ast_free_acl_list(oldacl);
10007 }
10008 ast_config_destroy(cfg);
10009
10010 /* Check the flag for named ACL event subscription and if we need to, register a subscription. */
10011 if (acl_subscription_flag && !by_external_config) {
10013 }
10014
10015 /* Perform cleanup - essentially prune out old users that no longer exist */
10017 if (user->keep) { /* valid record. clear flag for the next round */
10018 user->keep = 0;
10019
10020 /* Calculate A1 for Digest auth */
10021 snprintf(a1, sizeof(a1), "%s:%s:%s", user->username, global_realm, user->secret);
10022 ast_md5_hash(a1_hash,a1);
10023 ast_free(user->a1_hash);
10024 user->a1_hash = ast_strdup(a1_hash);
10025 continue;
10026 }
10027 /* We do not need to keep this user so take them out of the list */
10029 ast_debug(4, "Pruning user '%s'\n", user->username);
10031 }
10033
10035
10037 if (!webregged) {
10041
10045 webregged = 1;
10046 }
10047 } else {
10048 if (webregged) {
10052
10056 webregged = 0;
10057 }
10058 }
10059
10060 if (newhttptimeout > 0) {
10061 httptimeout = newhttptimeout;
10062 }
10063
10065 if (tls_was_enabled && !ami_tls_cfg.enabled) {
10067 } else if (ast_ssl_setup(amis_desc.tls_cfg)) {
10069 }
10070
10071 return 0;
10072}
10073
10074static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub,
10075 struct stasis_message *message)
10076{
10078 return;
10079 }
10080
10081 /* For now, this is going to be performed simply and just execute a forced reload. */
10082 ast_log(LOG_NOTICE, "Reloading manager in response to ACL change event.\n");
10083 __init_manager(1, 1);
10084}
10085
10086static int unload_module(void)
10087{
10088 return 0;
10089}
10090
10091static int load_module(void)
10092{
10093 int rc = 0;
10096#ifdef TEST_FRAMEWORK
10097 AST_TEST_REGISTER(eventfilter_test_creation);
10098 AST_TEST_REGISTER(eventfilter_test_matching);
10099 AST_TEST_REGISTER(originate_permissions_test);
10100#endif
10101 return rc;
10102}
10103
10104static int reload_module(void)
10105{
10107}
10108
10109int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
10110{
10111 AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
10112
10113 return 0;
10114}
10115
10116int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
10117{
10118 return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
10119}
10120
10121struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
10122{
10123 struct ast_datastore *datastore = NULL;
10124
10125 if (info == NULL)
10126 return NULL;
10127
10129 if (datastore->info != info) {
10130 continue;
10131 }
10132
10133 if (uid == NULL) {
10134 /* matched by type only */
10135 break;
10136 }
10137
10138 if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
10139 /* Matched by type AND uid */
10140 break;
10141 }
10142 }
10144
10145 return datastore;
10146}
10147
10148int ast_str_append_event_header(struct ast_str **fields_string,
10149 const char *header, const char *value)
10150{
10151 if (!*fields_string) {
10152 *fields_string = ast_str_create(128);
10153 if (!*fields_string) {
10154 return -1;
10155 }
10156 }
10157
10158 return (ast_str_append(fields_string, 0, "%s: %s\r\n", header, value) < 0) ? -1 : 0;
10159}
10160
10161static void manager_event_blob_dtor(void *obj)
10162{
10163 struct ast_manager_event_blob *ev = obj;
10164
10166}
10167
10169__attribute__((format(printf, 3, 4)))
10171 int event_flags,
10172 const char *manager_event,
10173 const char *extra_fields_fmt,
10174 ...)
10175{
10176 struct ast_manager_event_blob *ev;
10177 va_list argp;
10178
10179 ast_assert(extra_fields_fmt != NULL);
10181
10183 if (!ev) {
10184 return NULL;
10185 }
10186
10187 if (ast_string_field_init(ev, 20)) {
10188 ao2_ref(ev, -1);
10189 return NULL;
10190 }
10191
10194
10195 va_start(argp, extra_fields_fmt);
10196 ast_string_field_ptr_build_va(ev, &ev->extra_fields, extra_fields_fmt, argp);
10197 va_end(argp);
10198
10199 return ev;
10200}
10201
10203 .support_level = AST_MODULE_SUPPORT_CORE,
10204 .load = load_module,
10205 .unload = unload_module,
10207 .load_pri = AST_MODPRI_CORE,
10208 .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
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
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[]
static const struct adsi_event events[]
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:1889
#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
int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
Copy all object references in the src container into the dest container.
#define ao2_t_ref(o, delta, tag)
Definition astobj2.h:460
#define ao2_iterator_next(iter)
Definition astobj2.h:1911
#define AO2_GLOBAL_OBJ_STATIC(name)
Define a global object holder to be used to hold an ao2 object, statically initialized.
Definition astobj2.h:847
#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
@ AO2_ITERATOR_DONTLOCK
Assume that the ao2_container is already locked.
Definition astobj2.h:1852
#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_STRING_FIELD_SORT_FN(stype, field)
Creates a sort function for a structure string field.
Definition astobj2.h:2064
#define ao2_container_alloc_rbtree(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a red-black tree container.
Definition astobj2.h:1349
#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_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
@ AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE
Replace objects with duplicate keys in container.
Definition astobj2.h:1211
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.
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
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[]
static struct unistimsession * sessions
#define send_event(_instance, _event,...)
Use this macro to create and send events passing in any event-specific parameters.
static int chancount
Definition channel.c:97
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:8008
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:11122
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:1349
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:7912
#define ast_channel_lock(chan)
Definition channel.h:2982
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:1388
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
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:1171
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:460
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:1400
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:469
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:1296
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:1417
int ast_active_channels(void)
returns number of active/allocated channels
Definition channel.c:500
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
Definition channel.c:10633
struct ast_bridge * ast_channel_get_bridge(const struct ast_channel *chan)
Get the bridge associated with a channel.
Definition channel.c:10622
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:4688
#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:3018
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:511
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition channel.h:1328
@ AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT
Definition channel.h:1055
struct ast_channel_iterator * ast_channel_iterator_all_new(void)
Create a new channel iterator.
Definition channel.c:1380
#define ast_channel_unlock(chan)
Definition channel.h:2983
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
#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:2845
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
#define RESULT_SUCCESS
Definition cli.h:40
#define ast_cli_command(fd, s)
Definition cli.h:232
@ 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
#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.
#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)
static int action_setvar(struct mansession *s, const struct message *m)
Definition manager.c:3554
static int action_createconfig(struct mansession *s, const struct message *m)
Definition manager.c:3099
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:7734
static void mansession_unlock(struct mansession *s)
Unlock the 'mansession' structure.
Definition manager.c:2086
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:6835
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:7679
static int match_eventdata(struct event_filter_entry *entry, const char *eventdata)
Test eventdata against a filter entry.
Definition manager.c:5581
#define GET_HEADER_LAST_MATCH
Definition manager.c:1596
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:2030
static int purge_sessions(int n_max)
remove at most n_max stale session from the list.
Definition manager.c:7460
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:7032
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:9286
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:7657
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:1535
static void log_action(const struct message *m, const char *action)
Definition manager.c:6993
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:2114
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:7502
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:1673
static int action_sendtext(struct mansession *s, const struct message *m)
Definition manager.c:3910
static int action_mailboxcount(struct mansession *s, const struct message *m)
Definition manager.c:5445
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:7775
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:3842
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:2651
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:1983
static void report_failed_acl(const struct mansession *s, const char *username)
Definition manager.c:2139
static int is_originate_app_permitted(const char *app, const char *data, int permission)
Definition manager.c:5127
static int action_listcategories(struct mansession *s, const struct message *m)
Definition manager.c:2575
static int action_hangup(struct mansession *s, const struct message *m)
Definition manager.c:3548
static void mansession_lock(struct mansession *s)
Lock the 'mansession' structure.
Definition manager.c:2080
static int action_listcommands(struct mansession *s, const struct message *m)
Definition manager.c:3291
static void destroy_fast_originate_helper(struct fast_originate_helper *doomed)
Definition manager.c:4389
static int action_atxfer(struct mansession *s, const struct message *m)
Definition manager.c:4150
static int action_timeout(struct mansession *s, const struct message *m)
Definition manager.c:5539
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition manager.c:1988
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:3639
static int do_message(struct mansession *s)
Definition manager.c:7281
static struct originate_permissions_entry originate_app_permissions[]
Definition manager.c:5091
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:1993
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:6468
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:5691
static int manager_debug
Definition manager.c:169
static int action_mailboxstatus(struct mansession *s, const struct message *m)
Definition manager.c:5428
static void append_channel_vars(struct ast_str **pbuf, struct ast_channel *chan)
Definition manager.c:7527
static int action_login(struct mansession *s, const struct message *m)
Definition manager.c:3360
static int action_getvar(struct mansession *s, const struct message *m)
Definition manager.c:3587
static int get_input(struct mansession *s, char *output)
Definition manager.c:7160
static int action_blind_transfer(struct mansession *s, const struct message *m)
Definition manager.c:4104
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:5015
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:5054
#define ASTMAN_APPEND_BUF_INITSIZE
initial allocated size for the astman_append_buf and astman_send_*_va
Definition manager.c:1895
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:5601
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:2066
static int appdata_match(const char *app, const char *data, const char *search)
Definition manager.c:5034
#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:1952
static void astman_append_json(struct mansession *s, const char *str)
Definition manager.c:2642
#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:2725
static int action_extensionstate(struct mansession *s, const struct message *m)
Definition manager.c:5466
static char * handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command manager list connected.
Definition manager.c:1484
static int action_getconfig(struct mansession *s, const struct message *m)
Definition manager.c:2503
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:7366
static struct eventqent * advance_event(struct eventqent *e)
Definition manager.c:1582
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:6670
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:5758
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:2624
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:3354
static char * handle_kickmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command manager kick session.
Definition manager.c:1414
static int action_updateconfig(struct mansession *s, const struct message *m)
Definition manager.c:3008
static void astman_start_ack(struct mansession *s, const struct message *m)
Definition manager.c:2025
static const char * __astman_get_header(const struct message *m, char *var, int mode)
Return a matching header value.
Definition manager.c:1612
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition manager.c:2020
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:6886
static void handle_parse_error(struct mansession *s, struct message *m, char *error)
Definition manager.c:7264
static int manager_moduleload(struct mansession *s, const struct message *m)
Definition manager.c:6912
static void acl_change_stasis_unsubscribe(void)
Definition manager.c:241
static int reload_module(void)
Definition manager.c:10104
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:1659
static void report_failed_challenge_response(const struct mansession *s, const char *response, const char *expected_response)
Definition manager.c:2272
#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:6554
struct ast_variable * astman_get_variables(const struct message *m)
Get a linked list of the Variable: headers.
Definition manager.c:1735
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:2470
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
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:5654
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:4940
static char * handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition manager.c:1329
static int check_manager_session_inuse(const char *name)
Definition manager.c:1019
static int check_blacklist(const char *cmd)
Definition manager.c:4245
static int action_events(struct mansession *s, const struct message *m)
Definition manager.c:3310
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:1897
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:3959
#define MSG_MOREDATA
Definition manager.c:1943
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:5496
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:1740
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:3891
const char * astman_get_header(const struct message *m, char *var)
Return the first matching variable from an array.
Definition manager.c:1649
#define GET_HEADER_SKIP_EMPTY
Definition manager.c:1597
static enum ast_transport mansession_get_transport(const struct mansession *s)
Definition manager.c:2108
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:1256
static int action_cancel_atxfer(struct mansession *s, const struct message *m)
Definition manager.c:4204
static void report_req_not_allowed(const struct mansession *s, const char *action)
Definition manager.c:2214
static char global_realm[MAXHOSTNAMELEN]
Definition manager.c:176
static int action_originate(struct mansession *s, const struct message *m)
Definition manager.c:5233
static int send_string(struct mansession *s, char *string)
Definition manager.c:1854
#define MAX_AUTH_PERM_STRING
Definition manager.c:774
static int action_userevent(struct mansession *s, const struct message *m)
Definition manager.c:6447
static int action_command(struct mansession *s, const struct message *m)
Manager command "command" - execute CLI command.
Definition manager.c:4281
static void report_inval_password(const struct mansession *s, const char *username)
Definition manager.c:2164
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:7830
static int process_events(struct mansession *s)
Definition manager.c:6419
static int broken_events_action
Definition manager.c:165
static void report_session_limit(const struct mansession *s)
Definition manager.c:2302
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:2095
static const struct @396 command_blacklist[]
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:4897
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition manager.c:2074
static char * handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command manager reload.
Definition manager.c:1563
static struct ast_aoc_decoded * action_aoc_de_message(struct mansession *s, const struct message *m)
Definition manager.c:4502
static int authenticate(struct mansession *s, const struct message *m)
Definition manager.c:2334
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1909
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:1372
#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:6847
static void astman_send_list_complete(struct mansession *s, const struct message *m, const char *event_name, int count)
Definition manager.c:2057
static int async_goto_with_discard_bridge_after(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition manager.c:3952
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:2449
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:1766
static int action_aoc_s_submessage(struct mansession *s, const struct message *m, struct ast_aoc_decoded *decoded)
Definition manager.c:4673
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:2035
static int action_status(struct mansession *s, const struct message *m)
Manager "status" command to show channels.
Definition manager.c:3754
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:6586
static struct ast_variable * man_do_variable_value(struct ast_variable *head, const char *hdr_val)
Definition manager.c:1691
static int action_corestatus(struct mansession *s, const struct message *m)
Show PBX core status information.
Definition manager.c:6512
static int queue_sendtext(struct ast_channel *chan, const char *body)
Queue a read action to send a text message.
Definition manager.c:3875
static void action_destroy(void *obj)
Definition manager.c:7816
static int action_challenge(struct mansession *s, const struct message *m)
Definition manager.c:3409
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:3173
static void report_req_bad_format(const struct mansession *s, const char *action)
Definition manager.c:2243
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:6754
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:1231
static int action_ping(struct mansession *s, const struct message *m)
Definition manager.c:2431
#define GET_HEADER_FIRST_MATCH
Definition manager.c:1595
static int aocmessage_get_unit_entry(const struct message *m, struct ast_aoc_unit_entry *entry, unsigned int entry_num)
Definition manager.c:4477
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:6653
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:3427
static int httptimeout
Definition manager.c:164
static void report_auth_success(const struct mansession *s)
Definition manager.c:2189
static void * fast_originate(void *data)
Definition manager.c:4397
static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Definition manager.c:10074
#define MANAGER_EVENT_BUF_INITSIZE
Definition manager.c:7545
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:7547
int ast_manager_unregister(const char *action)
support functions to register/unregister AMI action handlers,
Definition manager.c:7704
@ 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:107
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:522
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:1455
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:771
uint32_t ast_http_manid_from_vars(struct ast_variable *headers) attribute_pure
Return manager id, if exist, from request headers.
Definition http.c:247
const char * ast_get_http_method(enum ast_http_method method) attribute_pure
Return http method name string.
Definition http.c:207
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:903
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:714
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition http.c:739
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)
int ast_category_inherit(struct ast_category *existing, const struct ast_category *base)
Applies base (template) to category.
const char * ast_category_get_name(const struct ast_category *category)
Return the name of the category.
struct ast_category * ast_category_new_template(const char *name, const char *in_file, int lineno)
Create a category making it a template.
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
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.
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.
void ast_category_rename(struct ast_category *cat, const char *name)
int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
#define CONFIG_STATUS_FILEMISSING
int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
Inserts new category.
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.
struct ast_variable * ast_variables_reverse(struct ast_variable *var)
Reverse a variable list.
int ast_realtime_enabled(void)
Check if there's any realtime engines loaded.
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.
#define CONFIG_STATUS_FILEUNCHANGED
@ CONFIG_FLAG_NOCACHE
@ CONFIG_FLAG_WITHCOMMENTS
@ CONFIG_FLAG_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.
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
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.
int ast_category_is_template(const struct ast_category *category)
Check if category is a template.
struct ast_variable * ast_category_first(struct ast_category *cat)
given a pointer to a category, return the root variable.
void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
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.
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.
#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
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
#define ast_verbose(...)
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
#define AST_RWLIST_REMOVE_CURRENT
#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.
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
#define AST_RWLIST_LAST
#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.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized.
#define AST_RWLIST_REMOVE_HEAD
#define AST_RWLIST_INSERT_AFTER
#define AST_RWLIST_NEXT
#define AST_RWLIST_REMOVE
#define AST_RWLIST_FIRST
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_RWLIST_TRAVERSE_SAFE_END
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
#define AST_RWLIST_TRAVERSE
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
#define AST_RWLIST_INSERT_HEAD
#define AST_RWLIST_INSERT_TAIL
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_RWLIST_ENTRY
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
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:8985
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:9580
#define ROW_FMT
static struct ast_tls_config ami_tls_cfg
Definition manager.c:9124
static void close_mansession_file(struct mansession *s)
Definition manager.c:8337
static struct ast_tcptls_session_args ami_desc
Definition manager.c:9125
static struct ast_http_uri managerxmluri
Definition manager.c:8964
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:9207
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:8915
int astman_verify_session_writepermissions(uint32_t ident, int perm)
Verify a session's write permissions against a permission mask.
Definition manager.c:8063
static int __init_manager(int reload, int by_external_config)
Definition manager.c:9671
static struct ast_tcptls_session_args amis_desc
Definition manager.c:9136
static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
Definition manager.c:8101
#define FORMAT3
static struct ast_custom_function managerclient_function
description of AMI_CLIENT dialplan function
Definition manager.c:9097
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:9108
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:10121
static struct mansession_session * find_session(uint32_t ident, int incinuse)
Definition manager.c:7942
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:8926
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:10148
#define HSMCONN_FORMAT1
static int event_max_name_len_cb(void *obj, void *arg, void *data, int flags)
Definition manager.c:9194
int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
Add a datastore to a session.
Definition manager.c:10109
static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
Definition manager.c:8180
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:9147
static void manager_event_blob_dtor(void *obj)
Definition manager.c:10161
static void load_channelvars(struct ast_variable *var)
Definition manager.c:9423
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:10170
static struct mansession_session * find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
Definition manager.c:7981
int astman_verify_session_readpermissions(uint32_t ident, int perm)
Verify a session's read permissions against a permission mask.
Definition manager.c:8030
static struct ast_http_uri manageruri
Definition manager.c:8956
static void manager_set_defaults(void)
Definition manager.c:9643
static void manager_shutdown(void)
Definition manager.c:9475
#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:8347
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:8937
static char * handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition manager.c:9338
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:9050
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:8996
static int load_module(void)
Definition manager.c:10091
static struct ast_http_uri arawmanuri
Definition manager.c:9007
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:8219
static int variable_count_hash_fn(const void *vvc, const int flags)
Definition manager.c:8173
#define FORMAT
int astman_is_authed(uint32_t ident)
Determine if a manager session ident is authenticated.
Definition manager.c:8014
static struct ast_http_uri rawmanuri
Definition manager.c:8948
static struct ast_cli_entry cli_manager[]
Definition manager.c:9400
#define FORMAT2
static int unload_module(void)
Definition manager.c:10086
static void manager_free_user(struct ast_manager_user *user)
Definition manager.c:9456
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:8974
output_format
Definition manager.c:7925
@ FORMAT_RAW
Definition manager.c:7926
@ FORMAT_HTML
Definition manager.c:7927
@ FORMAT_XML
Definition manager.c:7928
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:8592
static struct ast_http_uri amanageruri
Definition manager.c:9016
#define TEST_STRING
static int webregged
Definition manager.c:9103
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:9035
static int subscribe_all(void)
Definition manager.c:9613
static const char *const contenttype[]
Definition manager.c:7931
static struct ast_http_uri amanagerxmluri
Definition manager.c:9025
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:8380
int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
Remove a datastore from a session.
Definition manager.c:10116
static void load_disabledevents(struct ast_variable *var)
Definition manager.c:9446
#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:204
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition manager.h:255
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.
#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:289
@ ORDER_NATURAL
Definition manager.h:290
@ ORDER_REVERSE
Definition manager.h:291
#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:633
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:457
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:262
#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:644
#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.
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:345
struct timeval ast_startuptime
Definition asterisk.c:344
#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:4216
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:7970
const char * ast_extension_state2str(int extension_state)
Return string representation of the state of an extension.
Definition pbx.c:3162
@ AST_OUTGOING_WAIT
Definition pbx.h:1145
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:8024
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:4211
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:7960
int ast_processed_calls(void)
Retrieve the total number of calls processed through the PBX since last restart.
Definition pbx.c:4801
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:3859
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:4173
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:7013
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:3206
Presence state management.
ast_presence_state
@ AST_PRESENCE_INVALID
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
static int total
Definition res_adsi.c:970
static struct stasis_subscription * sub
Statsd channel stats. Exmaple of how to subscribe to Stasis events.
static char url[512]
static int reload(void)
const char * method
Definition res_pjsip.c:1273
static struct @521 args
#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
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.
#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
#define STASIS_MESSAGE_TYPE_DEFN(name,...)
Boiler-plate messaging macro for defining public message types.
Definition stasis.h:1440
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.
#define ast_string_field_ptr_build_va(x, ptr, fmt, args)
Set a field to a complex (built) value with prebuilt va_lists.
#define AST_STRING_FIELD(name)
Declare a string field.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
#define ast_string_field_init_extended(x, field)
Initialize an extended string field.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
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
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
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition strings.h:730
#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:2233
#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
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:2250
#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
@ AST_DYNSTR_BUILD_FAILED
Definition strings.h:943
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
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
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
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:2174
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:1869
char *attribute_pure 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:2213
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
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
char * file
The file name from whence this declaration was read.
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
Structure to describe a channel "technology", ie a channel driver See for examples:
Definition channel.h:648
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
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
const char * uid
Definition datastore.h:65
struct ast_datastore::@223 entry
Structure used to handle boolean flags.
Definition utils.h:220
Format capabilities structure, holds formats + preference order + etc.
Definition format_cap.c:54
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
HTTP authentication information.
Definition http.h:125
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
struct timeval start
Definition iostream.c:41
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:504
const ast_string_field extra_fields
Definition manager.h:509
const char * manager_event
Definition manager.h:506
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
char username[80]
Definition manager.c:343
struct ast_manager_user::@399 list
int allowmultiplelogin
Definition manager.c:349
struct ast_acl_list * acl
Definition manager.c:353
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.
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
const char * name
Definition tcptls.h:143
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
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_str * provided_by
Definition xmldoc.h:84
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
unsigned int seq
Definition manager.c:152
int category
Definition manager.c:151
struct eventqent::@397 eq_next
struct timeval tv
Definition manager.c:153
int usecount
Definition manager.c:150
int event_name_hash
Definition manager.c:154
struct eventqent * next
Definition manager.c:155
char eventdata[1]
Definition manager.c:156
helper function for originate
Definition manager.c:4361
struct ast_variable * vars
Definition manager.c:4381
struct ast_format_cap * cap
Definition manager.c:4363
struct ast_module *enum ast_doc_src docsrc
Definition manager.h:174
struct manager_action::@249 list
const ast_string_field description
Definition manager.h:163
struct ast_xml_doc_item * final_response
Definition manager.h:167
const ast_string_field synopsis
Definition manager.h:163
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 mansession_session::@398 list
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
unsigned long nc
Definition manager.c:306
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 manager.c:4999
int permission
Definition manager.c:5001
const char * search
Definition manager.c:5000
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
char * varname
Definition manager.c:8169
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
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
static struct aco_type item
static struct test_options options
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.
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,...)
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:981
#define ast_assert(a)
Definition utils.h:779
int ast_wait_for_input(int fd, int ms)
Definition utils.c:1732
int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request, int pedantic)
Parse digest authorization header.
Definition utils.c:2672
long int ast_random(void)
Definition utils.c:2346
#define ast_pthread_create_detached(a, b, c, d)
Definition utils.h:628
#define ast_set_flag(p, flag)
Definition utils.h:71
#define ARRAY_LEN(a)
Definition utils.h:706
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:2648
char * ast_xmldoc_build_description(const char *type, const char *name, const char *module)
Generate description documentation from XML.
Definition xmldoc.c:2418
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:2231
char * ast_xmldoc_build_synopsis(const char *type, const char *name, const char *module)
Generate synopsis documentation from XML.
Definition xmldoc.c:2395
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:2851
@ 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_build_provided_by(const char *type, const char *name, const char *module)
Generate provided-by documentation from XML.
Definition xmldoc.c:1844
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:2718