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