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