Asterisk - The Open Source Telephony Project GIT-master-f36a736
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 "CoreProcessedCalls: %d\r\n"
6447 "\r\n",
6448 idText,
6449 startupdate,
6450 startuptime,
6451 reloaddate,
6452 reloadtime,
6455 );
6456 return 0;
6457}
6458
6459/*! \brief Send a reload event */
6460static int action_reload(struct mansession *s, const struct message *m)
6461{
6462 const char *module = astman_get_header(m, "Module");
6464
6465 switch (res) {
6467 astman_send_error(s, m, "No such module");
6468 break;
6470 astman_send_error(s, m, "Module does not support reload");
6471 break;
6473 astman_send_error(s, m, "An unknown error occurred");
6474 break;
6476 astman_send_error(s, m, "A reload is in progress");
6477 break;
6479 astman_send_error(s, m, "Module not initialized");
6480 break;
6483 /* Treat a queued request as success */
6484 astman_send_ack(s, m, "Module Reloaded");
6485 break;
6486 }
6487 return 0;
6488}
6489
6490/*! \brief Manager command "CoreShowChannels" - List currently defined channels
6491 * and some information about them. */
6492static int action_coreshowchannels(struct mansession *s, const struct message *m)
6493{
6494 const char *actionid = astman_get_header(m, "ActionID");
6495 char idText[256];
6496 int numchans = 0;
6497 struct ao2_container *channels;
6498 struct ao2_iterator it_chans;
6499 struct ast_channel_snapshot *cs;
6500
6501 if (!ast_strlen_zero(actionid)) {
6502 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
6503 } else {
6504 idText[0] = '\0';
6505 }
6506
6508
6509 astman_send_listack(s, m, "Channels will follow", "start");
6510
6511 it_chans = ao2_iterator_init(channels, 0);
6512 for (; (cs = ao2_iterator_next(&it_chans)); ao2_ref(cs, -1)) {
6514 char durbuf[16] = "";
6515
6516 if (!built) {
6517 continue;
6518 }
6519
6520 if (!ast_tvzero(cs->base->creationtime)) {
6521 int duration, durh, durm, durs;
6522
6523 duration = (int)(ast_tvdiff_ms(ast_tvnow(), cs->base->creationtime) / 1000);
6524 durh = duration / 3600;
6525 durm = (duration % 3600) / 60;
6526 durs = duration % 60;
6527 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
6528 }
6529
6530 astman_append(s,
6531 "Event: CoreShowChannel\r\n"
6532 "%s"
6533 "%s"
6534 "Application: %s\r\n"
6535 "ApplicationData: %s\r\n"
6536 "Duration: %s\r\n"
6537 "BridgeId: %s\r\n"
6538 "\r\n",
6539 idText,
6540 ast_str_buffer(built),
6541 cs->dialplan->appl,
6542 cs->dialplan->data,
6543 durbuf,
6544 cs->bridge->id);
6545
6546 numchans++;
6547
6548 ast_free(built);
6549 }
6550 ao2_iterator_destroy(&it_chans);
6551
6552 astman_send_list_complete(s, m, "CoreShowChannelsComplete", numchans);
6553
6554 ao2_ref(channels, -1);
6555 return 0;
6556}
6557
6558/*! \brief Helper function to add a channel name to the vector */
6559static int coreshowchannelmap_add_to_map(struct ao2_container *c, const char *s)
6560{
6561 char *str;
6562
6563 str = ast_strdup(s);
6564 if (!str) {
6565 ast_log(LOG_ERROR, "Unable to append channel to channel map\n");
6566 return 1;
6567 }
6568
6569 /* If this is a duplicate, it will be ignored */
6571
6572 return 0;
6573}
6574
6575/*! \brief Recursive function to get all channels in a bridge. Follow local channels as well */
6577 struct ast_channel_snapshot *channel_snapshot, struct ast_bridge_snapshot *bridge_snapshot)
6578{
6579 int res = 0;
6580 struct ao2_iterator iter;
6581 char *current_channel_uid;
6582
6583 iter = ao2_iterator_init(bridge_snapshot->channels, 0);
6584 while ((current_channel_uid = ao2_iterator_next(&iter))) {
6585 struct ast_channel_snapshot *current_channel_snapshot;
6586 int add_channel_res;
6587
6588 /* Don't add the original channel to the list - it's either already in there,
6589 * or it's the channel we want the map for */
6590 if (!strcmp(current_channel_uid, channel_snapshot->base->uniqueid)) {
6591 ao2_ref(current_channel_uid, -1);
6592 continue;
6593 }
6594
6595 current_channel_snapshot = ast_channel_snapshot_get_latest(current_channel_uid);
6596 if (!current_channel_snapshot) {
6597 ast_debug(5, "Unable to get channel snapshot\n");
6598 ao2_ref(current_channel_uid, -1);
6599 continue;
6600 }
6601
6602 add_channel_res = coreshowchannelmap_add_to_map(channel_map, current_channel_snapshot->base->name);
6603 if (add_channel_res) {
6604 res = 1;
6605 ao2_ref(current_channel_snapshot, -1);
6606 ao2_ref(current_channel_uid, -1);
6607 break;
6608 }
6609
6610 /* 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 */
6611 if (ast_begins_with(current_channel_snapshot->base->name, "Local")) {
6612 struct ast_channel_snapshot *other_local_snapshot;
6613 struct ast_bridge_snapshot *other_bridge_snapshot;
6614 int size = strlen(current_channel_snapshot->base->name);
6615 char other_local[size + 1];
6616
6617 /* Don't copy the trailing number - set it to 1 or 2, whichever one it currently is not */
6618 ast_copy_string(other_local, current_channel_snapshot->base->name, size);
6619 other_local[size - 1] = ast_ends_with(current_channel_snapshot->base->name, "1") ? '2' : '1';
6620 other_local[size] = '\0';
6621
6622 other_local_snapshot = ast_channel_snapshot_get_latest_by_name(other_local);
6623 if (!other_local_snapshot) {
6624 ast_debug(5, "Unable to get other local channel snapshot\n");
6625 ao2_ref(current_channel_snapshot, -1);
6626 ao2_ref(current_channel_uid, -1);
6627 continue;
6628 }
6629
6630 if (coreshowchannelmap_add_to_map(channel_map, other_local_snapshot->base->name)) {
6631 res = 1;
6632 ao2_ref(current_channel_snapshot, -1);
6633 ao2_ref(current_channel_uid, -1);
6634 ao2_ref(other_local_snapshot, -1);
6635 break;
6636 }
6637
6638 other_bridge_snapshot = ast_bridge_get_snapshot_by_uniqueid(other_local_snapshot->bridge->id);
6639 if (other_bridge_snapshot) {
6640 res = coreshowchannelmap_add_connected_channels(channel_map, other_local_snapshot, other_bridge_snapshot);
6641 }
6642
6643 ao2_ref(current_channel_snapshot, -1);
6644 ao2_ref(current_channel_uid, -1);
6645 ao2_ref(other_local_snapshot, -1);
6646 ao2_ref(other_bridge_snapshot, -1);
6647
6648 if (res) {
6649 break;
6650 }
6651 }
6652 }
6653 ao2_iterator_destroy(&iter);
6654
6655 return res;
6656}
6657
6658/*! \brief Manager command "CoreShowChannelMap" - Lists all channels connected to
6659 * the specified channel. */
6660static int action_coreshowchannelmap(struct mansession *s, const struct message *m)
6661{
6662 const char *actionid = astman_get_header(m, "ActionID");
6663 const char *channel_name = astman_get_header(m, "Channel");
6664 char *current_channel_name;
6665 char id_text[256];
6666 int total = 0;
6667 struct ao2_container *channel_map;
6668 struct ao2_iterator i;
6669 RAII_VAR(struct ast_bridge_snapshot *, bridge_snapshot, NULL, ao2_cleanup);
6670 RAII_VAR(struct ast_channel_snapshot *, channel_snapshot, NULL, ao2_cleanup);
6671
6672 if (!ast_strlen_zero(actionid)) {
6673 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
6674 } else {
6675 id_text[0] = '\0';
6676 }
6677
6678 if (ast_strlen_zero(channel_name)) {
6679 astman_send_error(s, m, "CoreShowChannelMap requires a channel.\n");
6680 return 0;
6681 }
6682
6683 channel_snapshot = ast_channel_snapshot_get_latest_by_name(channel_name);
6684 if (!channel_snapshot) {
6685 astman_send_error(s, m, "Could not get channel snapshot\n");
6686 return 0;
6687 }
6688
6689 if (ast_strlen_zero(channel_snapshot->bridge->id)) {
6690 astman_send_listack(s, m, "Channel map will follow", "start");
6691 astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", 0);
6693 return 0;
6694 }
6695
6696 bridge_snapshot = ast_bridge_get_snapshot_by_uniqueid(channel_snapshot->bridge->id);
6697 if (!bridge_snapshot) {
6698 astman_send_listack(s, m, "Channel map will follow", "start");
6699 astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", 0);
6701 return 0;
6702 }
6703
6705 if (!channel_map) {
6706 astman_send_error(s, m, "Could not create channel map\n");
6707 return 0;
6708 }
6709
6710 astman_send_listack(s, m, "Channel map will follow", "start");
6711
6712 if (coreshowchannelmap_add_connected_channels(channel_map, channel_snapshot, bridge_snapshot)) {
6713 astman_send_error(s, m, "Could not complete channel map\n");
6714 ao2_ref(channel_map, -1);
6715 return 0;
6716 }
6717
6718 i = ao2_iterator_init(channel_map, 0);
6719 while ((current_channel_name = ao2_iterator_next(&i))) {
6720 astman_append(s,
6721 "Event: CoreShowChannelMap\r\n"
6722 "%s"
6723 "Channel: %s\r\n"
6724 "ConnectedChannel: %s\r\n\r\n",
6725 id_text,
6726 channel_name,
6727 current_channel_name);
6728 total++;
6729 }
6731
6732 ao2_ref(channel_map, -1);
6733 astman_send_list_complete_start(s, m, "CoreShowChannelMapComplete", total);
6735
6736 return 0;
6737}
6738
6739/*! \brief Manager command "LoggerRotate" - reloads and rotates the logger in
6740 * the same manner as the CLI command 'logger rotate'. */
6741static int action_loggerrotate(struct mansession *s, const struct message *m)
6742{
6743 if (ast_logger_rotate()) {
6744 astman_send_error(s, m, "Failed to reload the logger and rotate log files");
6745 return 0;
6746 }
6747
6748 astman_send_ack(s, m, "Reloaded the logger and rotated log files");
6749 return 0;
6750}
6751
6752/*! \brief Manager function to check if module is loaded */
6753static int manager_modulecheck(struct mansession *s, const struct message *m)
6754{
6755 const char *module = astman_get_header(m, "Module");
6756 const char *id = astman_get_header(m, "ActionID");
6757
6758 ast_debug(1, "**** ModuleCheck .so file %s\n", module);
6759 if (!ast_module_check(module)) {
6760 astman_send_error(s, m, "Module not loaded");
6761 return 0;
6762 }
6763
6764 astman_append(s, "Response: Success\r\n");
6765
6766 if (!ast_strlen_zero(id)) {
6767 astman_append(s, "ActionID: %s\r\n", id);
6768 }
6769
6770#if !defined(LOW_MEMORY)
6771 /* When we switched from subversion to git we lost the ability to
6772 * retrieve the 'ASTERISK_FILE_VERSION' from that file, but we retain
6773 * the response header here for backwards compatibility. */
6774 astman_append(s, "Version: \r\n");
6775#endif
6776
6777 astman_append(s, "\r\n");
6778
6779 return 0;
6780}
6781
6782/**
6783 * \brief Check if the given file path is in the modules dir or not
6784 *
6785 * \note When the module is being loaded / reloaded / unloaded, the modules dir is
6786 * automatically prepended
6787 *
6788 * \return 1 if inside modules dir
6789 * \return 0 if outside modules dir
6790 * \return -1 on failure
6791 */
6792static int file_in_modules_dir(const char *filename)
6793{
6794 char *stripped_filename;
6795 RAII_VAR(char *, path, NULL, ast_free);
6796 RAII_VAR(char *, real_path, NULL, ast_free);
6797
6798 /* Don't bother checking */
6799 if (live_dangerously) {
6800 return 1;
6801 }
6802
6803 stripped_filename = ast_strip(ast_strdupa(filename));
6804
6805 /* Always prepend the modules dir since that is what the code does for ModuleLoad */
6806 if (ast_asprintf(&path, "%s/%s", ast_config_AST_MODULE_DIR, stripped_filename) == -1) {
6807 return -1;
6808 }
6809
6810 real_path = realpath(path, NULL);
6811 if (!real_path) {
6812 return -1;
6813 }
6814
6815 return ast_begins_with(real_path, ast_config_AST_MODULE_DIR);
6816}
6817
6818static int manager_moduleload(struct mansession *s, const struct message *m)
6819{
6820 int res;
6821 const char *module = astman_get_header(m, "Module");
6822 const char *loadtype = astman_get_header(m, "LoadType");
6823 const char *recursive = astman_get_header(m, "Recursive");
6824
6825 if (!loadtype || strlen(loadtype) == 0) {
6826 astman_send_error(s, m, "Incomplete ModuleLoad action.");
6827 }
6828 if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0) {
6829 astman_send_error(s, m, "Need module name");
6830 }
6831
6832 res = file_in_modules_dir(module);
6833 if (res == 0) {
6834 astman_send_error(s, m, "Module must be in the configured modules directory.");
6835 return 0;
6836 } else if (res == -1) {
6837 astman_send_error(s, m, "Module not found.");
6838 return 0;
6839 }
6840
6841 if (!strcasecmp(loadtype, "load")) {
6842 res = ast_load_resource(module);
6843 if (res) {
6844 astman_send_error(s, m, "Could not load module.");
6845 } else {
6846 astman_send_ack(s, m, "Module loaded.");
6847 }
6848 } else if (!strcasecmp(loadtype, "unload")) {
6849 res = ast_unload_resource(module, AST_FORCE_SOFT);
6850 if (res) {
6851 astman_send_error(s, m, "Could not unload module.");
6852 } else {
6853 astman_send_ack(s, m, "Module unloaded.");
6854 }
6855 } else if (!strcasecmp(loadtype, "refresh")) {
6856 res = ast_refresh_resource(module, AST_FORCE_SOFT, !ast_strlen_zero(recursive) && ast_true(recursive));
6857 if (res) {
6858 astman_send_error(s, m, "Could not refresh module.");
6859 } else {
6860 astman_send_ack(s, m, "Module unloaded and loaded.");
6861 }
6862 } else if (!strcasecmp(loadtype, "reload")) {
6863 /* TODO: Unify the ack/error messages here with action_reload */
6864 if (!ast_strlen_zero(module)) {
6865 enum ast_module_reload_result reload_res = ast_module_reload(module);
6866
6867 switch (reload_res) {
6869 astman_send_error(s, m, "No such module.");
6870 break;
6872 astman_send_error(s, m, "Module does not support reload action.");
6873 break;
6875 astman_send_error(s, m, "An unknown error occurred");
6876 break;
6878 astman_send_error(s, m, "A reload is in progress");
6879 break;
6881 astman_send_error(s, m, "Module not initialized");
6882 break;
6885 /* Treat a queued request as success */
6886 astman_send_ack(s, m, "Module reloaded.");
6887 break;
6888 }
6889 } else {
6890 ast_module_reload(NULL); /* Reload all modules */
6891 astman_send_ack(s, m, "All modules reloaded");
6892 }
6893 } else {
6894 astman_send_error(s, m, "Incomplete ModuleLoad action.");
6895 }
6896 return 0;
6897}
6898
6899static void log_action(const struct message *m, const char *action)
6900{
6901 struct ast_str *buf;
6902 int x;
6903
6904 if (!manager_debug) {
6905 return;
6906 }
6907
6908 buf = ast_str_create(256);
6909 if (!buf) {
6910 return;
6911 }
6912
6913 for (x = 0; x < m->hdrcount; ++x) {
6914 if (!strncasecmp(m->headers[x], "Secret", 6)) {
6915 ast_str_append(&buf, 0, "Secret: <redacted from logging>\n");
6916 } else {
6917 ast_str_append(&buf, 0, "%s\n", m->headers[x]);
6918 }
6919 }
6920
6921 ast_verbose("<--- Examining AMI action: -->\n%s\n", ast_str_buffer(buf));
6922 ast_free(buf);
6923}
6924
6925/*
6926 * Done with the action handlers here, we start with the code in charge
6927 * of accepting connections and serving them.
6928 * accept_thread() forks a new thread for each connection, session_do(),
6929 * which in turn calls get_input() repeatedly until a full message has
6930 * been accumulated, and then invokes process_message() to pass it to
6931 * the appropriate handler.
6932 */
6933
6934/*! \brief
6935 * Process an AMI message, performing desired action.
6936 * Return 0 on success, -1 on error that require the session to be destroyed.
6937 */
6938static int process_message(struct mansession *s, const struct message *m)
6939{
6940 int ret = 0;
6941 struct manager_action *act_found;
6942 struct ast_manager_user *user = NULL;
6943 const char *username;
6944 const char *action;
6945
6946 action = __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY);
6947 if (ast_strlen_zero(action)) {
6948 report_req_bad_format(s, "NONE");
6949 mansession_lock(s);
6950 astman_send_error(s, m, "Missing action in request");
6952 return 0;
6953 }
6954
6955 log_action(m, action);
6956
6957 if (ast_shutting_down()) {
6958 ast_log(LOG_ERROR, "Unable to process manager action '%s'. Asterisk is shutting down.\n", action);
6959 mansession_lock(s);
6960 astman_send_error(s, m, "Asterisk is shutting down");
6962 return 0;
6963 }
6964
6965 if (!s->session->authenticated
6966 && strcasecmp(action, "Login")
6967 && strcasecmp(action, "Logoff")
6968 && strcasecmp(action, "Challenge")) {
6969 if (!s->session->authenticated) {
6970 report_req_not_allowed(s, action);
6971 }
6972 mansession_lock(s);
6973 astman_send_error(s, m, "Permission denied");
6975 return 0;
6976 }
6977
6978 if (!s->session->authenticated
6979 && (!strcasecmp(action, "Login")
6980 || !strcasecmp(action, "Challenge"))) {
6981 username = astman_get_header(m, "Username");
6982
6983 if (!ast_strlen_zero(username) && check_manager_session_inuse(username)) {
6985 user = get_manager_by_name_locked(username);
6986 if (user && !user->allowmultiplelogin) {
6989 sleep(1);
6990 mansession_lock(s);
6991 astman_send_error(s, m, "Login Already In Use");
6993 return -1;
6994 }
6996 }
6997 }
6998
6999 act_found = action_find(action);
7000 if (act_found) {
7001 /* Found the requested AMI action. */
7002 int acted = 0;
7003
7004 if ((s->session->writeperm & act_found->authority)
7005 || act_found->authority == 0) {
7006 /* We have the authority to execute the action. */
7007 ret = -1;
7008 ao2_lock(act_found);
7009 if (act_found->registered && act_found->func) {
7010 struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
7011
7012 ao2_unlock(act_found);
7013 if (mod_ref || !act_found->module) {
7014 ast_debug(1, "Running action '%s'\n", act_found->action);
7015 ret = act_found->func(s, m);
7016 acted = 1;
7017 ast_module_unref(mod_ref);
7018 }
7019 } else {
7020 ao2_unlock(act_found);
7021 }
7022 }
7023 if (!acted) {
7024 /*
7025 * We did not execute the action because access was denied, it
7026 * was no longer registered, or no action was really registered.
7027 * Complain about it and leave.
7028 */
7029 report_req_not_allowed(s, action);
7030 mansession_lock(s);
7031 astman_send_error(s, m, "Permission denied");
7033 }
7034 ao2_t_ref(act_found, -1, "done with found action object");
7035 } else {
7036 char buf[512];
7037
7038 report_req_bad_format(s, action);
7039 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
7040 mansession_lock(s);
7041 astman_send_error(s, m, buf);
7043 }
7044 if (ret) {
7045 return ret;
7046 }
7047 /* Once done with our message, deliver any pending events unless the
7048 requester doesn't want them as part of this response.
7049 */
7050 if (ast_strlen_zero(astman_get_header(m, "SuppressEvents"))) {
7051 return process_events(s);
7052 } else {
7053 return ret;
7054 }
7055}
7056
7057/*!
7058 * Read one full line (including crlf) from the manager socket.
7059 * \note \verbatim
7060 * \r\n is the only valid terminator for the line.
7061 * (Note that, later, '\0' will be considered as the end-of-line marker,
7062 * so everything between the '\0' and the '\r\n' will not be used).
7063 * Also note that we assume output to have at least "maxlen" space.
7064 * \endverbatim
7065 */
7066static int get_input(struct mansession *s, char *output)
7067{
7068 int res, x;
7069 int maxlen = sizeof(s->session->inbuf) - 1;
7070 char *src = s->session->inbuf;
7071 int timeout = -1;
7072 time_t now;
7073
7074 /*
7075 * Look for \r\n within the buffer. If found, copy to the output
7076 * buffer and return, trimming the \r\n (not used afterwards).
7077 */
7078 for (x = 0; x < s->session->inlen; x++) {
7079 int cr; /* set if we have \r */
7080 if (src[x] == '\r' && x+1 < s->session->inlen && src[x + 1] == '\n') {
7081 cr = 2; /* Found. Update length to include \r\n */
7082 } else if (src[x] == '\n') {
7083 cr = 1; /* also accept \n only */
7084 } else {
7085 continue;
7086 }
7087 memmove(output, src, x); /*... but trim \r\n */
7088 output[x] = '\0'; /* terminate the string */
7089 x += cr; /* number of bytes used */
7090 s->session->inlen -= x; /* remaining size */
7091 memmove(src, src + x, s->session->inlen); /* remove used bytes */
7092 return 1;
7093 }
7094 if (s->session->inlen >= maxlen) {
7095 /* no crlf found, and buffer full - sorry, too long for us
7096 * keep the last character in case we are in the middle of a CRLF. */
7097 ast_log(LOG_WARNING, "Discarding message from %s. Line too long: %.25s...\n", ast_sockaddr_stringify_addr(&s->session->addr), src);
7098 src[0] = src[s->session->inlen - 1];
7099 s->session->inlen = 1;
7101 }
7102 res = 0;
7103 while (res == 0) {
7104 /* calculate a timeout if we are not authenticated */
7105 if (!s->session->authenticated) {
7106 if(time(&now) == -1) {
7107 ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
7108 return -1;
7109 }
7110
7111 timeout = (authtimeout - (now - s->session->authstart)) * 1000;
7112 if (timeout < 0) {
7113 /* we have timed out */
7114 return 0;
7115 }
7116 }
7117
7119 if (s->session->pending_event) {
7120 s->session->pending_event = 0;
7122 return 0;
7123 }
7124 s->session->waiting_thread = pthread_self();
7126
7128
7132 }
7133 if (res < 0) {
7134 if (s->session->kicked) {
7135 ast_debug(1, "Manager session has been kicked\n");
7136 return -1;
7137 }
7138 /* If we get a signal from some other thread (typically because
7139 * there are new events queued), return 0 to notify the caller.
7140 */
7141 if (errno == EINTR || errno == EAGAIN) {
7142 return 0;
7143 }
7144 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
7145 return -1;
7146 }
7147
7148 ao2_lock(s->session);
7149 res = ast_iostream_read(s->session->stream, src + s->session->inlen, maxlen - s->session->inlen);
7150 if (res < 1) {
7151 res = -1; /* error return */
7152 } else {
7153 s->session->inlen += res;
7154 src[s->session->inlen] = '\0';
7155 res = 0;
7156 }
7157 ao2_unlock(s->session);
7158 return res;
7159}
7160
7161/*!
7162 * \internal
7163 * \brief Error handling for sending parse errors. This function handles locking, and clearing the
7164 * parse error flag.
7165 *
7166 * \param s AMI session to process action request.
7167 * \param m Message that's in error.
7168 * \param error Error message to send.
7169 */
7170static void handle_parse_error(struct mansession *s, struct message *m, char *error)
7171{
7172 mansession_lock(s);
7173 astman_send_error(s, m, error);
7174 s->parsing = MESSAGE_OKAY;
7176}
7177
7178/*!
7179 * \internal
7180 * \brief Read and process an AMI action request.
7181 *
7182 * \param s AMI session to process action request.
7183 *
7184 * \retval 0 Retain AMI connection for next command.
7185 * \retval -1 Drop AMI connection due to logoff or connection error.
7186 */
7187static int do_message(struct mansession *s)
7188{
7189 struct message m = { 0 };
7190 char header_buf[sizeof(s->session->inbuf)] = { '\0' };
7191 int res;
7192 int hdr_loss;
7193 time_t now;
7194
7195 hdr_loss = 0;
7196 for (;;) {
7197 /* Check if any events are pending and do them if needed */
7198 if (process_events(s)) {
7199 res = -1;
7200 break;
7201 }
7202 res = get_input(s, header_buf);
7203 if (res == 0) {
7204 /* No input line received. */
7205 if (!s->session->authenticated) {
7206 if (time(&now) == -1) {
7207 ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
7208 res = -1;
7209 break;
7210 }
7211
7212 if (now - s->session->authstart > authtimeout) {
7213 if (displayconnects) {
7214 ast_verb(2, "Client from %s, failed to authenticate in %d seconds\n", ast_sockaddr_stringify_addr(&s->session->addr), authtimeout);
7215 }
7216 res = -1;
7217 break;
7218 }
7219 }
7220 continue;
7221 } else if (res > 0) {
7222 /* Input line received. */
7223 if (ast_strlen_zero(header_buf)) {
7224 if (hdr_loss) {
7225 mansession_lock(s);
7226 astman_send_error(s, &m, "Too many lines in message or allocation failure");
7228 res = 0;
7229 } else {
7230 switch (s->parsing) {
7231 case MESSAGE_OKAY:
7232 res = process_message(s, &m) ? -1 : 0;
7233 break;
7235 handle_parse_error(s, &m, "Failed to parse message: line too long");
7236 res = 0;
7237 break;
7238 }
7239 }
7240 break;
7241 } else if (m.hdrcount < ARRAY_LEN(m.headers)) {
7242 m.headers[m.hdrcount] = ast_strdup(header_buf);
7243 if (!m.headers[m.hdrcount]) {
7244 /* Allocation failure. */
7245 hdr_loss = 1;
7246 } else {
7247 ++m.hdrcount;
7248 }
7249 } else {
7250 /* Too many lines in message. */
7251 hdr_loss = 1;
7252 }
7253 } else {
7254 /* Input error. */
7255 break;
7256 }
7257 }
7258
7260
7261 return res;
7262}
7263
7264/*! \brief The body of the individual manager session.
7265 * Call get_input() to read one line at a time
7266 * (or be woken up on new events), collect the lines in a
7267 * message until found an empty line, and execute the request.
7268 * In any case, deliver events asynchronously through process_events()
7269 * (called from here if no line is available, or at the end of
7270 * process_message(). )
7271 */
7272static void *session_do(void *data)
7273{
7274 struct ast_tcptls_session_instance *ser = data;
7276 struct mansession s = {
7277 .tcptls_session = data,
7278 };
7279 int res;
7280 int arg = 1;
7281 struct ast_sockaddr ser_remote_address_tmp;
7282
7285 goto done;
7286 }
7287
7288 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
7289 session = build_mansession(&ser_remote_address_tmp);
7290
7291 if (session == NULL) {
7293 goto done;
7294 }
7295
7296 /* here we set TCP_NODELAY on the socket to disable Nagle's algorithm.
7297 * This is necessary to prevent delays (caused by buffering) as we
7298 * write to the socket in bits and pieces. */
7299 if (setsockopt(ast_iostream_get_fd(ser->stream), IPPROTO_TCP, TCP_NODELAY, (char *) &arg, sizeof(arg)) < 0) {
7300 ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on manager connection: %s\n", strerror(errno));
7301 }
7303
7305 /* Hook to the tail of the event queue */
7306 session->last_ev = grab_last();
7307
7308 ast_mutex_init(&s.lock);
7309
7310 /* these fields duplicate those in the 'ser' structure */
7311 session->stream = s.stream = ser->stream;
7312 ast_sockaddr_copy(&session->addr, &ser_remote_address_tmp);
7313 s.session = session;
7314
7315 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
7316
7317 if(time(&session->authstart) == -1) {
7318 ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
7322 goto done;
7323 }
7325
7326 /*
7327 * We cannot let the stream exclusively wait for data to arrive.
7328 * We have to wake up the task to send async events.
7329 */
7331
7333 ast_tvnow(), authtimeout * 1000);
7334
7335 astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
7336 for (;;) {
7337 if ((res = do_message(&s)) < 0 || s.write_error || session->kicked) {
7338 break;
7339 }
7340 if (session->authenticated) {
7342 }
7343 }
7344 /* session is over, explain why and terminate */
7345 if (session->authenticated) {
7347 ast_verb(2, "Manager '%s' %s from %s\n", session->username, session->kicked ? "kicked" : "logged off", ast_sockaddr_stringify_addr(&session->addr));
7348 }
7349 } else {
7351 if (displayconnects) {
7352 ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_sockaddr_stringify_addr(&session->addr));
7353 }
7354 }
7355
7357
7359done:
7360 ao2_ref(ser, -1);
7361 ser = NULL;
7362 return NULL;
7363}
7364
7365/*! \brief remove at most n_max stale session from the list. */
7366static int purge_sessions(int n_max)
7367{
7368 struct ao2_container *sessions;
7370 time_t now = time(NULL);
7371 struct ao2_iterator i;
7372 int purged = 0;
7373
7374 sessions = ao2_global_obj_ref(mgr_sessions);
7375 if (!sessions) {
7376 return 0;
7377 }
7379 ao2_ref(sessions, -1);
7380 while ((session = ao2_iterator_next(&i)) && n_max > 0) {
7382 if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) {
7383 if (session->authenticated
7384 && VERBOSITY_ATLEAST(2)
7386 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
7387 session->username, ast_sockaddr_stringify_addr(&session->addr));
7388 }
7391 n_max--;
7392 purged++;
7393 } else {
7396 }
7397 }
7399 return purged;
7400}
7401
7402/*! \brief
7403 * events are appended to a queue from where they
7404 * can be dispatched to clients.
7405 */
7406static int append_event(const char *str, int event_name_hash, int category)
7407{
7408 struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
7409 static int seq; /* sequence number */
7410
7411 if (!tmp) {
7412 return -1;
7413 }
7414
7415 /* need to init all fields, because ast_malloc() does not */
7416 tmp->usecount = 0;
7417 tmp->category = category;
7418 tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
7419 tmp->tv = ast_tvnow();
7420 tmp->event_name_hash = event_name_hash;
7421 AST_RWLIST_NEXT(tmp, eq_next) = NULL;
7422 strcpy(tmp->eventdata, str);
7423
7427
7428 return 0;
7429}
7430
7431static void append_channel_vars(struct ast_str **pbuf, struct ast_channel *chan)
7432{
7433 struct varshead *vars;
7434 struct ast_var_t *var;
7435
7436 vars = ast_channel_get_manager_vars(chan);
7437 if (!vars) {
7438 return;
7439 }
7440
7441 AST_LIST_TRAVERSE(vars, var, entries) {
7442 ast_str_append(pbuf, 0, "ChanVariable(%s): %s=%s\r\n", ast_channel_name(chan), var->name, var->value);
7443 }
7444 ao2_ref(vars, -1);
7445}
7446
7447/* XXX see if can be moved inside the function */
7449#define MANAGER_EVENT_BUF_INITSIZE 256
7450
7451static int __attribute__((format(printf, 9, 0))) __manager_event_sessions_va(
7452 struct ao2_container *sessions,
7453 int category,
7454 const char *event,
7455 int chancount,
7456 struct ast_channel **chans,
7457 const char *file,
7458 int line,
7459 const char *func,
7460 const char *fmt,
7461 va_list ap)
7462{
7464 const char *cat_str;
7465 struct timeval now;
7466 struct ast_str *buf;
7467 int i;
7468 int event_name_hash;
7469
7472 ast_debug(3, "AMI Event '%s' is globally disabled, skipping\n", event);
7473 /* Event is globally disabled */
7474 return -1;
7475 }
7476 }
7477
7479 if (!buf) {
7480 return -1;
7481 }
7482
7483 cat_str = authority_to_str(category, &auth);
7484 ast_str_set(&buf, 0,
7485 "Event: %s\r\n"
7486 "Privilege: %s\r\n",
7487 event, cat_str);
7488
7489 if (timestampevents) {
7490 now = ast_tvnow();
7491 ast_str_append(&buf, 0,
7492 "Timestamp: %ld.%06lu\r\n",
7493 (long)now.tv_sec, (unsigned long) now.tv_usec);
7494 }
7495 if (manager_debug) {
7496 static int seq;
7497
7498 ast_str_append(&buf, 0,
7499 "SequenceNumber: %d\r\n",
7501 ast_str_append(&buf, 0,
7502 "File: %s\r\n"
7503 "Line: %d\r\n"
7504 "Func: %s\r\n",
7505 file, line, func);
7506 }
7508 ast_str_append(&buf, 0,
7509 "SystemName: %s\r\n",
7511 }
7512
7513 ast_str_append_va(&buf, 0, fmt, ap);
7514 for (i = 0; i < chancount; i++) {
7516 }
7517
7518 ast_str_append(&buf, 0, "\r\n");
7519
7520 event_name_hash = ast_str_hash(event);
7521
7522 append_event(ast_str_buffer(buf), event_name_hash, category);
7523
7524 /* Wake up any sleeping sessions */
7525 if (sessions) {
7526 struct ao2_iterator iter;
7528
7529 iter = ao2_iterator_init(sessions, 0);
7530 while ((session = ao2_iterator_next(&iter))) {
7531 ast_mutex_lock(&session->notify_lock);
7532 if (session->waiting_thread != AST_PTHREADT_NULL) {
7533 pthread_kill(session->waiting_thread, SIGURG);
7534 } else {
7535 /* We have an event to process, but the mansession is
7536 * not waiting for it. We still need to indicate that there
7537 * is an event waiting so that get_input processes the pending
7538 * event instead of polling.
7539 */
7540 session->pending_event = 1;
7541 }
7542 ast_mutex_unlock(&session->notify_lock);
7544 }
7545 ao2_iterator_destroy(&iter);
7546 }
7547
7548 if (category != EVENT_FLAG_SHUTDOWN && !AST_RWLIST_EMPTY(&manager_hooks)) {
7549 struct manager_custom_hook *hook;
7550
7552 AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
7553 hook->helper(category, event, ast_str_buffer(buf));
7554 }
7556 }
7557
7558 return 0;
7559}
7560
7561static int __attribute__((format(printf, 9, 0))) __manager_event_sessions(
7562 struct ao2_container *sessions,
7563 int category,
7564 const char *event,
7565 int chancount,
7566 struct ast_channel **chans,
7567 const char *file,
7568 int line,
7569 const char *func,
7570 const char *fmt,
7571 ...)
7572{
7573 va_list ap;
7574 int res;
7575
7576 va_start(ap, fmt);
7578 chancount, chans, file, line, func, fmt, ap);
7579 va_end(ap);
7580 return res;
7581}
7582
7583int __ast_manager_event_multichan(int category, const char *event, int chancount,
7584 struct ast_channel **chans, const char *file, int line, const char *func,
7585 const char *fmt, ...)
7586{
7587 struct ao2_container *sessions = ao2_global_obj_ref(mgr_sessions);
7588 va_list ap;
7589 int res;
7590
7592 /* Nobody is listening */
7594 return 0;
7595 }
7596
7597 va_start(ap, fmt);
7599 file, line, func, fmt, ap);
7600 va_end(ap);
7602 return res;
7603}
7604
7605/*! \brief
7606 * support functions to register/unregister AMI action handlers,
7607 */
7608int ast_manager_unregister(const char *action)
7609{
7610 struct manager_action *cur;
7611
7614 if (!strcasecmp(action, cur->action)) {
7616 break;
7617 }
7618 }
7621
7622 if (cur) {
7623 /*
7624 * We have removed the action object from the container so we
7625 * are no longer in a hurry.
7626 */
7627 ao2_lock(cur);
7628 cur->registered = 0;
7629 ao2_unlock(cur);
7630
7631 ao2_t_ref(cur, -1, "action object removed from list");
7632 ast_verb(5, "Manager unregistered action %s\n", action);
7633 }
7634
7635 return 0;
7636}
7637
7638static int manager_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
7639{
7640 /* Notify managers of change */
7641 char hint[512];
7642
7643 hint[0] = '\0';
7644 ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
7645
7646 switch(info->reason) {
7648 manager_event(EVENT_FLAG_CALL, "ExtensionStatus",
7649 "Exten: %s\r\n"
7650 "Context: %s\r\n"
7651 "Hint: %s\r\n"
7652 "Status: %d\r\n"
7653 "StatusText: %s\r\n",
7654 exten,
7655 context,
7656 hint,
7657 info->exten_state,
7658 ast_extension_state2str(info->exten_state));
7659 break;
7661 manager_event(EVENT_FLAG_CALL, "PresenceStatus",
7662 "Exten: %s\r\n"
7663 "Context: %s\r\n"
7664 "Hint: %s\r\n"
7665 "Status: %s\r\n"
7666 "Subtype: %s\r\n"
7667 "Message: %s\r\n",
7668 exten,
7669 context,
7670 hint,
7671 ast_presence_state2str(info->presence_state),
7672 info->presence_subtype,
7673 info->presence_message);
7674 break;
7675 }
7676 return 0;
7677}
7678
7680{
7681 struct manager_action *cur, *prev = NULL;
7682
7684 AST_RWLIST_TRAVERSE(&actions, cur, list) {
7685 int ret;
7686
7687 ret = strcasecmp(cur->action, act->action);
7688 if (ret == 0) {
7689 ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
7691 return -1;
7692 }
7693 if (ret > 0) { /* Insert these alphabetically */
7694 break;
7695 }
7696 prev = cur;
7697 }
7698
7699 ao2_t_ref(act, +1, "action object added to list");
7700 act->registered = 1;
7701 if (prev) {
7702 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
7703 } else {
7704 AST_RWLIST_INSERT_HEAD(&actions, act, list);
7705 }
7706
7707 ast_verb(5, "Manager registered action %s\n", act->action);
7708
7710
7711 return 0;
7712}
7713
7714/*!
7715 * \internal
7716 * \brief Destroy the registered AMI action object.
7717 *
7718 * \param obj Object to destroy.
7719 */
7720static void action_destroy(void *obj)
7721{
7722 struct manager_action *doomed = obj;
7723
7724 if (doomed->synopsis) {
7725 /* The string fields were initialized. */
7727 }
7728 ao2_cleanup(doomed->final_response);
7729 ao2_cleanup(doomed->list_responses);
7730}
7731
7732/*! \brief register a new command with manager, including online help. This is
7733 the preferred way to register a manager command */
7734int 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)
7735{
7736 struct manager_action *cur;
7737
7738 cur = ao2_t_alloc(sizeof(*cur), action_destroy, action);
7739 if (!cur) {
7740 return -1;
7741 }
7742 if (ast_string_field_init(cur, 128)) {
7743 ao2_t_ref(cur, -1, "action object creation failed");
7744 return -1;
7745 }
7746
7747 cur->action = action;
7748 cur->authority = auth;
7749 cur->func = func;
7750 cur->module = module;
7751#ifdef AST_XML_DOCS
7752 if (ast_strlen_zero(synopsis) && ast_strlen_zero(description)) {
7753 char *tmpxml;
7754
7755 tmpxml = ast_xmldoc_build_synopsis("manager", action, NULL);
7756 ast_string_field_set(cur, synopsis, tmpxml);
7757 ast_free(tmpxml);
7758
7759 tmpxml = ast_xmldoc_build_syntax("manager", action, NULL);
7760 ast_string_field_set(cur, syntax, tmpxml);
7761 ast_free(tmpxml);
7762
7763 tmpxml = ast_xmldoc_build_description("manager", action, NULL);
7764 ast_string_field_set(cur, description, tmpxml);
7765 ast_free(tmpxml);
7766
7767 tmpxml = ast_xmldoc_build_seealso("manager", action, NULL);
7768 ast_string_field_set(cur, seealso, tmpxml);
7769 ast_free(tmpxml);
7770
7771 tmpxml = ast_xmldoc_build_arguments("manager", action, NULL);
7772 ast_string_field_set(cur, arguments, tmpxml);
7773 ast_free(tmpxml);
7774
7775 cur->final_response = ast_xmldoc_build_final_response("manager", action, NULL);
7776 cur->list_responses = ast_xmldoc_build_list_responses("manager", action, NULL);
7777
7778 cur->docsrc = AST_XML_DOC;
7779 } else
7780#endif
7781 {
7783 ast_string_field_set(cur, description, description);
7784#ifdef AST_XML_DOCS
7785 cur->docsrc = AST_STATIC_DOC;
7786#endif
7787 }
7788 if (ast_manager_register_struct(cur)) {
7789 ao2_t_ref(cur, -1, "action object registration failed");
7790 return -1;
7791 }
7792
7793 ao2_t_ref(cur, -1, "action object registration successful");
7794 return 0;
7795}
7796/*! @}
7797 END Doxygen group */
7798
7799/*
7800 * The following are support functions for AMI-over-http.
7801 * The common entry point is generic_http_callback(),
7802 * which extracts HTTP header and URI fields and reformats
7803 * them into AMI messages, locates a proper session
7804 * (using the mansession_id Cookie or GET variable),
7805 * and calls process_message() as for regular AMI clients.
7806 * When done, the output (which goes to a temporary file)
7807 * is read back into a buffer and reformatted as desired,
7808 * then fed back to the client over the original socket.
7809 */
7810
7815};
7816
7817static const char * const contenttype[] = {
7818 [FORMAT_RAW] = "plain",
7819 [FORMAT_HTML] = "html",
7820 [FORMAT_XML] = "xml",
7821};
7822
7823/*!
7824 * locate an http session in the list. The search key (ident) is
7825 * the value of the mansession_id cookie (0 is not valid and means
7826 * a session on the AMI socket).
7827 */
7828static struct mansession_session *find_session(uint32_t ident, int incinuse)
7829{
7830 struct ao2_container *sessions;
7832 struct ao2_iterator i;
7833
7834 if (ident == 0) {
7835 return NULL;
7836 }
7837
7838 sessions = ao2_global_obj_ref(mgr_sessions);
7839 if (!sessions) {
7840 return NULL;
7841 }
7843 ao2_ref(sessions, -1);
7844 while ((session = ao2_iterator_next(&i))) {
7846 if (session->managerid == ident && !session->needdestroy) {
7847 ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0);
7848 break;
7849 }
7852 }
7854
7855 return session;
7856}
7857
7858/*!
7859 * locate an http session in the list.
7860 * The search keys (nonce) and (username) is value from received
7861 * "Authorization" http header.
7862 * As well as in find_session() function, the value of the nonce can't be zero.
7863 * (0 meansi, that the session used for AMI socket connection).
7864 * Flag (stale) is set, if client used valid, but old, nonce value.
7865 *
7866 */
7867static struct mansession_session *find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
7868{
7870 struct ao2_container *sessions;
7871 struct ao2_iterator i;
7872
7873 if (nonce == 0 || username == NULL || stale == NULL) {
7874 return NULL;
7875 }
7876
7877 sessions = ao2_global_obj_ref(mgr_sessions);
7878 if (!sessions) {
7879 return NULL;
7880 }
7882 ao2_ref(sessions, -1);
7883 while ((session = ao2_iterator_next(&i))) {
7885 if (!strcasecmp(session->username, username) && session->managerid == nonce) {
7886 *stale = 0;
7887 break;
7888 } else if (!strcasecmp(session->username, username) && session->oldnonce == nonce) {
7889 *stale = 1;
7890 break;
7891 }
7894 }
7896
7897 return session;
7898}
7899
7900int astman_is_authed(uint32_t ident)
7901{
7902 int authed;
7904
7905 if (!(session = find_session(ident, 0)))
7906 return 0;
7907
7908 authed = (session->authenticated != 0);
7909
7912
7913 return authed;
7914}
7915
7916int astman_verify_session_readpermissions(uint32_t ident, int perm)
7917{
7918 int result = 0;
7920 struct ao2_container *sessions;
7921 struct ao2_iterator i;
7922
7923 if (ident == 0) {
7924 return 0;
7925 }
7926
7927 sessions = ao2_global_obj_ref(mgr_sessions);
7928 if (!sessions) {
7929 return 0;
7930 }
7932 ao2_ref(sessions, -1);
7933 while ((session = ao2_iterator_next(&i))) {
7935 if ((session->managerid == ident) && (session->readperm & perm)) {
7936 result = 1;
7939 break;
7940 }
7943 }
7945
7946 return result;
7947}
7948
7949int astman_verify_session_writepermissions(uint32_t ident, int perm)
7950{
7951 int result = 0;
7953 struct ao2_container *sessions;
7954 struct ao2_iterator i;
7955
7956 if (ident == 0) {
7957 return 0;
7958 }
7959
7960 sessions = ao2_global_obj_ref(mgr_sessions);
7961 if (!sessions) {
7962 return 0;
7963 }
7965 ao2_ref(sessions, -1);
7966 while ((session = ao2_iterator_next(&i))) {
7968 if ((session->managerid == ident) && (session->writeperm & perm)) {
7969 result = 1;
7972 break;
7973 }
7976 }
7978
7979 return result;
7980}
7981
7982/*
7983 * convert to xml with various conversion:
7984 * mode & 1 -> lowercase;
7985 * mode & 2 -> replace non-alphanumeric chars with underscore
7986 */
7987static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
7988{
7989 /* store in a local buffer to avoid calling ast_str_append too often */
7990 char buf[256];
7991 char *dst = buf;
7992 const char *save = src;
7993 int space = sizeof(buf);
7994 /* repeat until done and nothing to flush */
7995 for ( ; *src || dst != buf ; src++) {
7996 if (*src == '\0' || space < 10) { /* flush */
7997 *dst++ = '\0';
7998 ast_str_append(out, 0, "%s", buf);
7999 dst = buf;
8000 space = sizeof(buf);
8001 if (*src == '\0') {
8002 break;
8003 }
8004 }
8005
8006 if (mode & 2) {
8007 if (save == src && isdigit(*src)) {
8008 /* The first character of an XML attribute cannot be a digit */
8009 *dst++ = '_';
8010 *dst++ = *src;
8011 space -= 2;
8012 continue;
8013 } else if (!isalnum(*src)) {
8014 /* Replace non-alphanumeric with an underscore */
8015 *dst++ = '_';
8016 space--;
8017 continue;
8018 }
8019 }
8020 switch (*src) {
8021 case '<':
8022 strcpy(dst, "&lt;");
8023 dst += 4;
8024 space -= 4;
8025 break;
8026 case '>':
8027 strcpy(dst, "&gt;");
8028 dst += 4;
8029 space -= 4;
8030 break;
8031 case '\"':
8032 strcpy(dst, "&quot;");
8033 dst += 6;
8034 space -= 6;
8035 break;
8036 case '\'':
8037 strcpy(dst, "&apos;");
8038 dst += 6;
8039 space -= 6;
8040 break;
8041 case '&':
8042 strcpy(dst, "&amp;");
8043 dst += 5;
8044 space -= 5;
8045 break;
8046
8047 default:
8048 *dst++ = mode ? tolower(*src) : *src;
8049 space--;
8050 }
8051 }
8052}
8053
8055 char *varname;
8057};
8058
8059static int variable_count_hash_fn(const void *vvc, const int flags)
8060{
8061 const struct variable_count *vc = vvc;
8062
8063 return ast_str_hash(vc->varname);
8064}
8065
8066static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
8067{
8068 /* Due to the simplicity of struct variable_count, it makes no difference
8069 * if you pass in objects or strings, the same operation applies. This is
8070 * due to the fact that the hash occurs on the first element, which means
8071 * the address of both the struct and the string are exactly the same. */
8072 struct variable_count *vc = obj;
8073 char *str = vstr;
8074 return !strcmp(vc->varname, str) ? CMP_MATCH | CMP_STOP : 0;
8075}
8076
8077/*! \brief Convert the input into XML or HTML.
8078 * The input is supposed to be a sequence of lines of the form
8079 * Name: value
8080 * optionally followed by a blob of unformatted text.
8081 * A blank line is a section separator. Basically, this is a
8082 * mixture of the format of Manager Interface and CLI commands.
8083 * The unformatted text is considered as a single value of a field
8084 * named 'Opaque-data'.
8085 *
8086 * At the moment the output format is the following (but it may
8087 * change depending on future requirements so don't count too
8088 * much on it when writing applications):
8089 *
8090 * General: the unformatted text is used as a value of
8091 * XML output: to be completed
8092 *
8093 * \verbatim
8094 * Each section is within <response type="object" id="xxx">
8095 * where xxx is taken from ajaxdest variable or defaults to unknown
8096 * Each row is reported as an attribute Name="value" of an XML
8097 * entity named from the variable ajaxobjtype, default to "generic"
8098 * \endverbatim
8099 *
8100 * HTML output:
8101 * each Name-value pair is output as a single row of a two-column table.
8102 * Sections (blank lines in the input) are separated by a <HR>
8103 *
8104 */
8105static void xml_translate(struct ast_str **out, char *in, struct ast_variable *get_vars, enum output_format format)
8106{
8107 struct ast_variable *v;
8108 const char *dest = NULL;
8109 char *var, *val;
8110 const char *objtype = NULL;
8111 int in_data = 0; /* parsing data */
8112 int inobj = 0;
8113 int xml = (format == FORMAT_XML);
8114 struct variable_count *vc = NULL;
8115 struct ao2_container *vco = NULL;
8116
8117 if (xml) {
8118 /* dest and objtype need only for XML format */
8119 for (v = get_vars; v; v = v->next) {
8120 if (!strcasecmp(v->name, "ajaxdest")) {
8121 dest = v->value;
8122 } else if (!strcasecmp(v->name, "ajaxobjtype")) {
8123 objtype = v->value;
8124 }
8125 }
8126 if (ast_strlen_zero(dest)) {
8127 dest = "unknown";
8128 }
8129 if (ast_strlen_zero(objtype)) {
8130 objtype = "generic";
8131 }
8132 }
8133
8134 /* we want to stop when we find an empty line */
8135 while (in && *in) {
8136 val = strsep(&in, "\r\n"); /* mark start and end of line */
8137 if (in && *in == '\n') { /* remove trailing \n if any */
8138 in++;
8139 }
8141 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
8142 if (ast_strlen_zero(val)) {
8143 /* empty line */
8144 if (in_data) {
8145 /* close data in Opaque mode */
8146 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
8147 in_data = 0;
8148 }
8149
8150 if (inobj) {
8151 /* close block */
8152 ast_str_append(out, 0, xml ? " /></response>\n" :
8153 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
8154 inobj = 0;
8155 ao2_ref(vco, -1);
8156 vco = NULL;
8157 }
8158 continue;
8159 }
8160
8161 if (!inobj) {
8162 /* start new block */
8163 if (xml) {
8164 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
8165 }
8168 inobj = 1;
8169 }
8170
8171 if (in_data) {
8172 /* Process data field in Opaque mode. This is a
8173 * followup, so we re-add line feeds. */
8174 ast_str_append(out, 0, xml ? "\n" : "<br>\n");
8175 xml_copy_escape(out, val, 0); /* data field */
8176 continue;
8177 }
8178
8179 /* We expect "Name: value" line here */
8180 var = strsep(&val, ":");
8181 if (val) {
8182 /* found the field name */
8185 } else {
8186 /* field name not found, switch to opaque mode */
8187 val = var;
8188 var = "Opaque-data";
8189 in_data = 1;
8190 }
8191
8192
8193 ast_str_append(out, 0, xml ? " " : "<tr><td>");
8194 if ((vc = ao2_find(vco, var, 0))) {
8195 vc->count++;
8196 } else {
8197 /* Create a new entry for this one */
8198 vc = ao2_alloc(sizeof(*vc), NULL);
8199 vc->varname = var;
8200 vc->count = 1;
8201 ao2_link(vco, vc);
8202 }
8203
8204 xml_copy_escape(out, var, xml ? 1 | 2 : 0); /* data name */
8205 if (vc->count > 1) {
8206 ast_str_append(out, 0, "-%d", vc->count);
8207 }
8208 ao2_ref(vc, -1);
8209 ast_str_append(out, 0, xml ? "='" : "</td><td>");
8210 xml_copy_escape(out, val, 0); /* data field */
8211 if (!in_data || !*in) {
8212 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
8213 }
8214 }
8215
8216 if (inobj) {
8217 ast_str_append(out, 0, xml ? " /></response>\n" :
8218 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
8219 ao2_ref(vco, -1);
8220 }
8221}
8222
8223static void close_mansession_file(struct mansession *s)
8224{
8225 if (s->stream) {
8227 s->stream = NULL;
8228 } else {
8229 ast_log(LOG_ERROR, "Attempted to close file/file descriptor on mansession without a valid file or file descriptor.\n");
8230 }
8231}
8232
8233static void process_output(struct mansession *s, struct ast_str **out, struct ast_variable *params, enum output_format format)
8234{
8235 char *buf;
8236 off_t l;
8237 int fd;
8238
8239 if (!s->stream)
8240 return;
8241
8242 /* Ensure buffer is NULL-terminated */
8243 ast_iostream_write(s->stream, "", 1);
8244
8245 fd = ast_iostream_get_fd(s->stream);
8246
8247 l = lseek(fd, 0, SEEK_CUR);
8248 if (l > 0) {
8249 if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0))) {
8250 ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n");
8251 } else {
8252 if (format == FORMAT_XML || format == FORMAT_HTML) {
8253 xml_translate(out, buf, params, format);
8254 } else {
8255 ast_str_append(out, 0, "%s", buf);
8256 }
8257 munmap(buf, l);
8258 }
8259 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
8260 xml_translate(out, "", params, format);
8261 }
8262
8264}
8265
8268 enum output_format format,
8269 const struct ast_sockaddr *remote_address, const char *uri,
8270 struct ast_variable *get_params,
8271 struct ast_variable *headers)
8272{
8273 struct mansession s = { .session = NULL, .tcptls_session = ser };
8275 uint32_t ident;
8276 int fd;
8277 int blastaway = 0;
8278 struct ast_variable *params = get_params;
8279 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
8280 struct ast_str *http_header = NULL, *out = NULL;
8281 struct message m = { 0 };
8282
8284 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
8285 return 0;
8286 }
8287
8288 ident = ast_http_manid_from_vars(headers);
8289
8290 if (!(session = find_session(ident, 1))) {
8291
8292 /**/
8293 /* Create new session.
8294 * While it is not in the list we don't need any locking
8295 */
8296 if (!(session = build_mansession(remote_address))) {
8298 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8299 return 0;
8300 }
8302 session->send_events = 0;
8303 session->inuse = 1;
8304 /*!
8305 * \note There is approximately a 1 in 1.8E19 chance that the following
8306 * calculation will produce 0, which is an invalid ID, but due to the
8307 * properties of the rand() function (and the constancy of s), that
8308 * won't happen twice in a row.
8309 */
8310 while ((session->managerid = ast_random() ^ (unsigned long) session) == 0) {
8311 }
8312 session->last_ev = grab_last();
8313 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
8314 }
8316
8317 http_header = ast_str_create(128);
8318 out = ast_str_create(2048);
8319
8320 ast_mutex_init(&s.lock);
8321
8322 if (http_header == NULL || out == NULL) {
8324 ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)");
8325 goto generic_callback_out;
8326 }
8327
8328 s.session = session;
8329 fd = mkstemp(template); /* create a temporary file for command output */
8330 unlink(template);
8331 if (fd <= -1) {
8332 ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)");
8333 goto generic_callback_out;
8334 }
8336 if (!s.stream) {
8337 ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
8338 ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)");
8339 close(fd);
8340 goto generic_callback_out;
8341 }
8342
8343 if (method == AST_HTTP_POST) {
8344 params = ast_http_get_post_vars(ser, headers);
8345 if (!params) {
8346 switch (errno) {
8347 case EFBIG:
8348 ast_http_error(ser, 413, "Request Entity Too Large", "Body too large");
8350 goto generic_callback_out;
8351 case ENOMEM:
8353 ast_http_error(ser, 500, "Server Error", "Out of memory");
8355 goto generic_callback_out;
8356 case EIO:
8357 ast_http_error(ser, 400, "Bad Request", "Error parsing request body");
8359 goto generic_callback_out;
8360 }
8361 }
8362 }
8363
8364 astman_append_headers(&m, params);
8365
8366 if (process_message(&s, &m)) {
8367 if (session->authenticated) {
8369 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr));
8370 }
8371 } else {
8372 if (displayconnects) {
8373 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_sockaddr_stringify_addr(&session->addr));
8374 }
8375 }
8376 session->needdestroy = 1;
8377 }
8378
8380
8381 ast_str_append(&http_header, 0,
8382 "Content-type: text/%s\r\n"
8383 "Set-Cookie: mansession_id=\"%08x\"; Version=1; Max-Age=%d\r\n"
8384 "Pragma: SuppressEvents\r\n",
8385 contenttype[format],
8386 session->managerid, httptimeout);
8387
8388 if (format == FORMAT_XML) {
8389 ast_str_append(&out, 0, "<ajax-response>\n");
8390 } else if (format == FORMAT_HTML) {
8391 /*
8392 * When handling AMI-over-HTTP in HTML format, we provide a simple form for
8393 * debugging purposes. This HTML code should not be here, we
8394 * should read from some config file...
8395 */
8396
8397#define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
8398#define TEST_STRING \
8399 "<form action=\"manager\" method=\"post\">\n\
8400 Action: <select name=\"action\">\n\
8401 <option value=\"\">-----&gt;</option>\n\
8402 <option value=\"login\">login</option>\n\
8403 <option value=\"command\">Command</option>\n\
8404 <option value=\"waitevent\">waitevent</option>\n\
8405 <option value=\"listcommands\">listcommands</option>\n\
8406 </select>\n\
8407 or <input name=\"action\"><br/>\n\
8408 CLI Command <input name=\"command\"><br>\n\
8409 user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br>\n\
8410 <input type=\"submit\">\n</form>\n"
8411
8412 ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
8413 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
8414 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
8416 }
8417
8418 process_output(&s, &out, params, format);
8419
8420 if (format == FORMAT_XML) {
8421 ast_str_append(&out, 0, "</ajax-response>\n");
8422 } else if (format == FORMAT_HTML) {
8423 ast_str_append(&out, 0, "</table></body>\r\n");
8424 }
8425
8427 /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */
8428 session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5);
8429
8430 if (session->needdestroy) {
8431 if (session->inuse == 1) {
8432 ast_debug(1, "Need destroy, doing it now!\n");
8433 blastaway = 1;
8434 } else {
8435 ast_debug(1, "Need destroy, but can't do it yet!\n");
8436 ast_mutex_lock(&session->notify_lock);
8437 if (session->waiting_thread != AST_PTHREADT_NULL) {
8438 pthread_kill(session->waiting_thread, SIGURG);
8439 }
8440 ast_mutex_unlock(&session->notify_lock);
8441 session->inuse--;
8442 }
8443 } else {
8444 session->inuse--;
8445 }
8447
8448 ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
8449 http_header = NULL;
8450 out = NULL;
8451
8452generic_callback_out:
8454
8455 /* Clear resource */
8456
8457 if (method == AST_HTTP_POST && params) {
8458 ast_variables_destroy(params);
8459 }
8460 ast_free(http_header);
8461 ast_free(out);
8462
8463 if (session) {
8464 if (blastaway) {
8466 } else {
8467 if (session->stream) {
8468 ast_iostream_close(session->stream);
8469 session->stream = NULL;
8470 }
8472 }
8473 }
8474
8475 return 0;
8476}
8477
8480 enum output_format format,
8481 const struct ast_sockaddr *remote_address, const char *uri,
8482 struct ast_variable *get_params,
8483 struct ast_variable *headers)
8484{
8486 struct mansession s = { .session = NULL, .tcptls_session = ser };
8487 struct ast_variable *v, *params = get_params;
8488 char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
8489 struct ast_str *http_header = NULL, *out = NULL;
8490 size_t result_size;
8491 struct message m = { 0 };
8492 int fd;
8493
8494 time_t time_now = time(NULL);
8495 unsigned long nonce = 0, nc;
8496 struct ast_http_digest d = { NULL, };
8497 struct ast_manager_user *user = NULL;
8498 int stale = 0;
8499 char resp_hash[256]="";
8500 /* Cache for user data */
8501 char u_username[80];
8502 int u_readperm;
8503 int u_writeperm;
8504 int u_writetimeout;
8505 int u_displayconnects;
8506
8508 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
8509 return 0;
8510 }
8511
8512 /* Find "Authorization: " header */
8513 for (v = headers; v; v = v->next) {
8514 if (!strcasecmp(v->name, "Authorization")) {
8515 break;
8516 }
8517 }
8518
8519 if (!v || ast_strlen_zero(v->value)) {
8520 goto out_401; /* Authorization Header not present - send auth request */
8521 }
8522
8523 /* Digest found - parse */
8524 if (ast_string_field_init(&d, 128)) {
8526 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8527 return 0;
8528 }
8529
8530 if (ast_parse_digest(v->value, &d, 0, 1)) {
8531 /* Error in Digest - send new one */
8532 nonce = 0;
8533 goto out_401;
8534 }
8535 if (sscanf(d.nonce, "%30lx", &nonce) != 1) {
8536 ast_log(LOG_WARNING, "Received incorrect nonce in Digest <%s>\n", d.nonce);
8537 nonce = 0;
8538 goto out_401;
8539 }
8540
8542 user = get_manager_by_name_locked(d.username);
8543 if(!user) {
8545 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_sockaddr_stringify_addr(&session->addr), d.username);
8546 nonce = 0;
8547 goto out_401;
8548 }
8549
8550 /* --- We have User for this auth, now check ACL */
8551 if (user->acl && !ast_apply_acl(user->acl, remote_address, "Manager User ACL:")) {
8553 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&session->addr), d.username);
8555 ast_http_error(ser, 403, "Permission denied", "Permission denied");
8556 return 0;
8557 }
8558
8559 /* --- We have auth, so check it */
8560
8561 /* compute the expected response to compare with what we received */
8562 {
8563 char *a2;
8564 /* ast_md5_hash outputs 32 characters plus NULL terminator. */
8565 char a2_hash[33];
8566 char resp[256];
8567
8568 /* XXX Now request method are hardcoded in A2 */
8569 if (ast_asprintf(&a2, "%s:%s", ast_get_http_method(method), d.uri) < 0) {
8572 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8573 return 0;
8574 }
8575
8576 ast_md5_hash(a2_hash, a2);
8577 ast_free(a2);
8578
8579 if (d.qop) {
8580 /* RFC 2617 */
8581 snprintf(resp, sizeof(resp), "%s:%08lx:%s:%s:auth:%s", user->a1_hash, nonce, d.nc, d.cnonce, a2_hash);
8582 } else {
8583 /* RFC 2069 */
8584 snprintf(resp, sizeof(resp), "%s:%08lx:%s", user->a1_hash, nonce, a2_hash);
8585 }
8586 ast_md5_hash(resp_hash, resp);
8587 }
8588
8589 if (strncasecmp(d.response, resp_hash, strlen(resp_hash))) {
8590 /* Something was wrong, so give the client to try with a new challenge */
8592 nonce = 0;
8593 goto out_401;
8594 }
8595
8596 /*
8597 * User are pass Digest authentication.
8598 * Now, cache the user data and unlock user list.
8599 */
8600 ast_copy_string(u_username, user->username, sizeof(u_username));
8601 u_readperm = user->readperm;
8602 u_writeperm = user->writeperm;
8603 u_displayconnects = user->displayconnects;
8604 u_writetimeout = user->writetimeout;
8606
8607 if (!(session = find_session_by_nonce(d.username, nonce, &stale))) {
8608 /*
8609 * Create new session.
8610 * While it is not in the list we don't need any locking
8611 */
8612 if (!(session = build_mansession(remote_address))) {
8614 ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)");
8615 return 0;
8616 }
8618
8619 ast_copy_string(session->username, u_username, sizeof(session->username));
8620 session->managerid = nonce;
8621 session->last_ev = grab_last();
8622 AST_LIST_HEAD_INIT_NOLOCK(&session->datastores);
8623
8624 session->readperm = u_readperm;
8625 session->writeperm = u_writeperm;
8626 session->writetimeout = u_writetimeout;
8627
8628 if (u_displayconnects) {
8629 ast_verb(2, "HTTP Manager '%s' logged in from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr));
8630 }
8631 session->noncetime = session->sessionstart = time_now;
8632 session->authenticated = 1;
8633 } else if (stale) {
8634 /*
8635 * Session found, but nonce is stale.
8636 *
8637 * This could be because an old request (w/old nonce) arrived.
8638 *
8639 * This may be as the result of http proxy usage (separate delay or
8640 * multipath) or in a situation where a page was refreshed too quickly
8641 * (seen in Firefox).
8642 *
8643 * In this situation, we repeat the 401 auth with the current nonce
8644 * value.
8645 */
8646 nonce = session->managerid;
8648 stale = 1;
8649 goto out_401;
8650 } else {
8651 sscanf(d.nc, "%30lx", &nc);
8652 if (session->nc >= nc || ((time_now - session->noncetime) > 62) ) {
8653 /*
8654 * Nonce time expired (> 2 minutes) or something wrong with nonce
8655 * counter.
8656 *
8657 * Create new nonce key and resend Digest auth request. Old nonce
8658 * is saved for stale checking...
8659 */
8660 session->nc = 0; /* Reset nonce counter */
8661 session->oldnonce = session->managerid;
8662 nonce = session->managerid = ast_random();
8663 session->noncetime = time_now;
8665 stale = 1;
8666 goto out_401;
8667 } else {
8668 session->nc = nc; /* All OK, save nonce counter */
8669 }
8670 }
8671
8672
8673 /* Reset session timeout. */
8674 session->sessiontimeout = time(NULL) + (httptimeout > 5 ? httptimeout : 5);
8676
8677 ast_mutex_init(&s.lock);
8678 s.session = session;
8679 fd = mkstemp(template); /* create a temporary file for command output */
8680 unlink(template);
8681 if (fd <= -1) {
8682 ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)");
8683 goto auth_callback_out;
8684 }
8686 if (!s.stream) {
8687 ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno));
8688 ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)");
8689 close(fd);
8690 goto auth_callback_out;
8691 }
8692
8693 if (method == AST_HTTP_POST) {
8694 params = ast_http_get_post_vars(ser, headers);
8695 if (!params) {
8696 switch (errno) {
8697 case EFBIG:
8698 ast_http_error(ser, 413, "Request Entity Too Large", "Body too large");
8700 goto auth_callback_out;
8701 case ENOMEM:
8703 ast_http_error(ser, 500, "Server Error", "Out of memory");
8705 goto auth_callback_out;
8706 case EIO:
8707 ast_http_error(ser, 400, "Bad Request", "Error parsing request body");
8709 goto auth_callback_out;
8710 }
8711 }
8712 }
8713
8714 astman_append_headers(&m, params);
8715
8716 if (process_message(&s, &m)) {
8717 if (u_displayconnects) {
8718 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr));
8719 }
8720
8721 session->needdestroy = 1;
8722 }
8723
8725
8726 result_size = lseek(ast_iostream_get_fd(s.stream), 0, SEEK_CUR); /* Calculate approx. size of result */
8727
8728 http_header = ast_str_create(80);
8729 out = ast_str_create(result_size * 2 + 512);
8730 if (http_header == NULL || out == NULL) {
8732 ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)");
8734 goto auth_callback_out;
8735 }
8736
8737 ast_str_append(&http_header, 0, "Content-type: text/%s\r\n", contenttype[format]);
8738
8739 if (format == FORMAT_XML) {
8740 ast_str_append(&out, 0, "<ajax-response>\n");
8741 } else if (format == FORMAT_HTML) {
8742 ast_str_append(&out, 0,
8743 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
8744 "<html><head>\r\n"
8745 "<title>Asterisk&trade; Manager Interface</title>\r\n"
8746 "</head><body style=\"background-color: #ffffff;\">\r\n"
8747 "<form method=\"POST\">\r\n"
8748 "<table align=\"center\" style=\"background-color: #f1f1f1;\" width=\"500\">\r\n"
8749 "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\"><h1>Manager Tester</h1></th></tr>\r\n"
8750 "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\">Action: <input name=\"action\" /> Cmd: <input name=\"command\" /><br>"
8751 "<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n");
8752 }
8753
8754 process_output(&s, &out, params, format);
8755
8756 if (format == FORMAT_XML) {
8757 ast_str_append(&out, 0, "</ajax-response>\n");
8758 } else if (format == FORMAT_HTML) {
8759 ast_str_append(&out, 0, "</table></form></body></html>\r\n");
8760 }
8761
8762 ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
8763 http_header = NULL;
8764 out = NULL;
8765
8766auth_callback_out:
8768
8769 /* Clear resources and unlock manager session */
8770 if (method == AST_HTTP_POST && params) {
8771 ast_variables_destroy(params);
8772 }
8773
8774 ast_free(http_header);
8775 ast_free(out);
8776
8778 if (session->stream) {
8779 ast_iostream_close(session->stream);
8780 session->stream = NULL;
8781 }
8783
8784 if (session->needdestroy) {
8785 ast_debug(1, "Need destroy, doing it now!\n");
8787 }
8789 return 0;
8790
8791out_401:
8792 if (!nonce) {
8793 nonce = ast_random();
8794 }
8795
8796 ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL);
8798 return 0;
8799}
8800
8801static 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)
8802{
8803 int retval;
8804 struct ast_sockaddr ser_remote_address_tmp;
8805
8806 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8807 retval = generic_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
8808 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8809 return retval;
8810}
8811
8812static 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)
8813{
8814 int retval;
8815 struct ast_sockaddr ser_remote_address_tmp;
8816
8817 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8818 retval = generic_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
8819 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8820 return retval;
8821}
8822
8823static 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)
8824{
8825 int retval;
8826 struct ast_sockaddr ser_remote_address_tmp;
8827
8828 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8829 retval = generic_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
8830 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8831 return retval;
8832}
8833
8834static struct ast_http_uri rawmanuri = {
8835 .description = "Raw HTTP Manager Event Interface",
8836 .uri = "rawman",
8837 .callback = rawman_http_callback,
8838 .data = NULL,
8839 .key = __FILE__,
8840};
8841
8842static struct ast_http_uri manageruri = {
8843 .description = "HTML Manager Event Interface",
8844 .uri = "manager",
8845 .callback = manager_http_callback,
8846 .data = NULL,
8847 .key = __FILE__,
8848};
8849
8851 .description = "XML Manager Event Interface",
8852 .uri = "mxml",
8853 .callback = mxml_http_callback,
8854 .data = NULL,
8855 .key = __FILE__,
8856};
8857
8858
8859/* Callback with Digest authentication */
8860static 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)
8861{
8862 int retval;
8863 struct ast_sockaddr ser_remote_address_tmp;
8864
8865 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8866 retval = auth_http_callback(ser, method, FORMAT_HTML, &ser_remote_address_tmp, uri, get_params, headers);
8867 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8868 return retval;
8869}
8870
8871static 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)
8872{
8873 int retval;
8874 struct ast_sockaddr ser_remote_address_tmp;
8875
8876 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8877 retval = auth_http_callback(ser, method, FORMAT_XML, &ser_remote_address_tmp, uri, get_params, headers);
8878 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8879 return retval;
8880}
8881
8882static 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)
8883{
8884 int retval;
8885 struct ast_sockaddr ser_remote_address_tmp;
8886
8887 ast_sockaddr_copy(&ser_remote_address_tmp, &ser->remote_address);
8888 retval = auth_http_callback(ser, method, FORMAT_RAW, &ser_remote_address_tmp, uri, get_params, headers);
8889 ast_sockaddr_copy(&ser->remote_address, &ser_remote_address_tmp);
8890 return retval;
8891}
8892
8893static struct ast_http_uri arawmanuri = {
8894 .description = "Raw HTTP Manager Event Interface w/Digest authentication",
8895 .uri = "arawman",
8896 .has_subtree = 0,
8897 .callback = auth_rawman_http_callback,
8898 .data = NULL,
8899 .key = __FILE__,
8900};
8901
8902static struct ast_http_uri amanageruri = {
8903 .description = "HTML Manager Event Interface w/Digest authentication",
8904 .uri = "amanager",
8905 .has_subtree = 0,
8906 .callback = auth_manager_http_callback,
8907 .data = NULL,
8908 .key = __FILE__,
8909};
8910
8912 .description = "XML Manager Event Interface w/Digest authentication",
8913 .uri = "amxml",
8914 .has_subtree = 0,
8915 .callback = auth_mxml_http_callback,
8916 .data = NULL,
8917 .key = __FILE__,
8918};
8919
8920/*! \brief Get number of logged in sessions for a login name */
8921static int get_manager_sessions_cb(void *obj, void *arg, void *data, int flags)
8922{
8923 struct mansession_session *session = obj;
8924 const char *login = (char *)arg;
8925 int *no_sessions = data;
8926
8927 if (strcasecmp(session->username, login) == 0) {
8928 (*no_sessions)++;
8929 }
8930
8931 return 0;
8932}
8933
8934
8935/*! \brief ${AMI_CLIENT()} Dialplan function - reads manager client data */
8936static int function_amiclient(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
8937{
8938 struct ast_manager_user *user = NULL;
8939
8942 AST_APP_ARG(param);
8943 );
8944
8945
8946 if (ast_strlen_zero(data) ) {
8947 ast_log(LOG_WARNING, "AMI_CLIENT() requires two arguments: AMI_CLIENT(<name>[,<arg>])\n");
8948 return -1;
8949 }
8951 args.name = ast_strip(args.name);
8952 args.param = ast_strip(args.param);
8953
8955 if (!(user = get_manager_by_name_locked(args.name))) {
8957 ast_log(LOG_ERROR, "There's no manager user called : \"%s\"\n", args.name);
8958 return -1;
8959 }
8961
8962 if (!strcasecmp(args.param, "sessions")) {
8963 int no_sessions = 0;
8964 struct ao2_container *sessions;
8965
8966 sessions = ao2_global_obj_ref(mgr_sessions);
8967 if (sessions) {
8968 ao2_callback_data(sessions, 0, get_manager_sessions_cb, /*login name*/ data, &no_sessions);
8969 ao2_ref(sessions, -1);
8970 }
8971 snprintf(buf, len, "%d", no_sessions);
8972 } else {
8973 ast_log(LOG_ERROR, "Invalid arguments provided to function AMI_CLIENT: %s\n", args.param);
8974 return -1;
8975
8976 }
8977
8978 return 0;
8979}
8980
8981
8982/*! \brief description of AMI_CLIENT dialplan function */
8984 .name = "AMI_CLIENT",
8985 .read = function_amiclient,
8986 .read_max = 12,
8987};
8988
8989static int webregged = 0;
8990
8991/*! \brief cleanup code called at each iteration of server_root,
8992 * guaranteed to happen every 5 seconds at most
8993 */
8994static void purge_old_stuff(void *data)
8995{
8996 struct ast_tcptls_session_args *ser = data;
8997 /* purge_sessions will return the number of sessions actually purged,
8998 * up to a maximum of it's arguments, purge one at a time, keeping a
8999 * purge interval of 1ms as long as we purged a session, otherwise
9000 * revert to a purge check every 5s
9001 */
9002 if (purge_sessions(1) == 1) {
9003 ser->poll_timeout = 1;
9004 } else {
9005 ser->poll_timeout = 5000;
9006 }
9007 purge_events();
9008}
9009
9012 .accept_fd = -1,
9013 .master = AST_PTHREADT_NULL,
9014 .tls_cfg = NULL,
9015 .poll_timeout = 5000, /* wake up every 5 seconds */
9016 .periodic_fn = purge_old_stuff,
9017 .name = "AMI server",
9018 .accept_fn = ast_tcptls_server_root, /* thread doing the accept() */
9019 .worker_fn = session_do, /* thread handling the session */
9020};
9021
9023 .accept_fd = -1,
9024 .master = AST_PTHREADT_NULL,
9025 .tls_cfg = &ami_tls_cfg,
9026 .poll_timeout = -1, /* the other does the periodic cleanup */
9027 .name = "AMI TLS server",
9028 .accept_fn = ast_tcptls_server_root, /* thread doing the accept() */
9029 .worker_fn = session_do, /* thread handling the session */
9030};
9031
9032/*! \brief CLI command manager show settings */
9033static char *handle_manager_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9034{
9035 switch (cmd) {
9036 case CLI_INIT:
9037 e->command = "manager show settings";
9038 e->usage =
9039 "Usage: manager show settings\n"
9040 " Provides detailed list of the configuration of the Manager.\n";
9041 return NULL;
9042 case CLI_GENERATE:
9043 return NULL;
9044 }
9045#define FORMAT " %-25.25s %-15.55s\n"
9046#define FORMAT2 " %-25.25s %-15d\n"
9047#define FORMAT3 " %-25.25s %s\n"
9048 if (a->argc != 3) {
9049 return CLI_SHOWUSAGE;
9050 }
9051 ast_cli(a->fd, "\nGlobal Settings:\n");
9052 ast_cli(a->fd, "----------------\n");
9053 ast_cli(a->fd, FORMAT, "Manager (AMI):", AST_CLI_YESNO(manager_enabled));
9054 ast_cli(a->fd, FORMAT, "Web Manager (AMI/HTTP):", AST_CLI_YESNO(webmanager_enabled));
9055 ast_cli(a->fd, FORMAT, "TCP Bindaddress:", manager_enabled != 0 ? ast_sockaddr_stringify(&ami_desc.local_address) : "Disabled");
9056 ast_cli(a->fd, FORMAT2, "HTTP Timeout (seconds):", httptimeout);
9057 ast_cli(a->fd, FORMAT, "TLS Enable:", AST_CLI_YESNO(ami_tls_cfg.enabled));
9058 ast_cli(a->fd, FORMAT, "TLS Bindaddress:", ami_tls_cfg.enabled != 0 ? ast_sockaddr_stringify(&amis_desc.local_address) : "Disabled");
9059 ast_cli(a->fd, FORMAT, "TLS Certfile:", ami_tls_cfg.certfile);
9060 ast_cli(a->fd, FORMAT, "TLS Privatekey:", ami_tls_cfg.pvtfile);
9061 ast_cli(a->fd, FORMAT, "TLS Cipher:", ami_tls_cfg.cipher);
9062 ast_cli(a->fd, FORMAT, "Allow multiple login:", AST_CLI_YESNO(allowmultiplelogin));
9063 ast_cli(a->fd, FORMAT, "Display connects:", AST_CLI_YESNO(displayconnects));
9064 ast_cli(a->fd, FORMAT, "Timestamp events:", AST_CLI_YESNO(timestampevents));
9065 ast_cli(a->fd, FORMAT3, "Channel vars:", S_OR(manager_channelvars, ""));
9066 ast_cli(a->fd, FORMAT3, "Disabled events:", S_OR(manager_disabledevents, ""));
9067 ast_cli(a->fd, FORMAT, "Debug:", AST_CLI_YESNO(manager_debug));
9068#undef FORMAT
9069#undef FORMAT2
9070#undef FORMAT3
9071
9072 return CLI_SUCCESS;
9073}
9074
9075#ifdef AST_XML_DOCS
9076
9077static int ast_xml_doc_item_cmp_fn(const void *a, const void *b)
9078{
9079 struct ast_xml_doc_item **item_a = (struct ast_xml_doc_item **)a;
9080 struct ast_xml_doc_item **item_b = (struct ast_xml_doc_item **)b;
9081 return strcmp((*item_a)->name, (*item_b)->name);
9082}
9083
9084static char *handle_manager_show_events(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9085{
9086 struct ao2_container *events;
9087 struct ao2_iterator *it_events;
9088 struct ast_xml_doc_item *item;
9089 struct ast_xml_doc_item **items;
9090 struct ast_str *buffer;
9091 int i = 0, totalitems = 0;
9092
9093 switch (cmd) {
9094 case CLI_INIT:
9095 e->command = "manager show events";
9096 e->usage =
9097 "Usage: manager show events\n"
9098 " Prints a listing of the available Asterisk manager interface events.\n";
9099 return NULL;
9100 case CLI_GENERATE:
9101 return NULL;
9102 }
9103 if (a->argc != 3) {
9104 return CLI_SHOWUSAGE;
9105 }
9106
9107 buffer = ast_str_create(128);
9108 if (!buffer) {
9109 return CLI_SUCCESS;
9110 }
9111
9112 events = ao2_global_obj_ref(event_docs);
9113 if (!events) {
9114 ast_cli(a->fd, "No manager event documentation loaded\n");
9115 ast_free(buffer);
9116 return CLI_SUCCESS;
9117 }
9118
9120 if (!(it_events = ao2_callback(events, OBJ_MULTIPLE | OBJ_NOLOCK, NULL, NULL))) {
9122 ast_log(AST_LOG_ERROR, "Unable to create iterator for events container\n");
9123 ast_free(buffer);
9124 ao2_ref(events, -1);
9125 return CLI_SUCCESS;
9126 }
9127 if (!(items = ast_calloc(sizeof(struct ast_xml_doc_item *), ao2_container_count(events)))) {
9129 ast_log(AST_LOG_ERROR, "Unable to create temporary sorting array for events\n");
9130 ao2_iterator_destroy(it_events);
9131 ast_free(buffer);
9132 ao2_ref(events, -1);
9133 return CLI_SUCCESS;
9134 }
9136
9137 while ((item = ao2_iterator_next(it_events))) {
9138 items[totalitems++] = item;
9139 ao2_ref(item, -1);
9140 }
9141
9142 qsort(items, totalitems, sizeof(struct ast_xml_doc_item *), ast_xml_doc_item_cmp_fn);
9143
9144 ast_cli(a->fd, "Events:\n");
9145 ast_cli(a->fd, " -------------------- -------------------- -------------------- \n");
9146 for (i = 0; i < totalitems; i++) {
9147 ast_str_append(&buffer, 0, " %-20.20s", items[i]->name);
9148 if ((i + 1) % 3 == 0) {
9149 ast_cli(a->fd, "%s\n", ast_str_buffer(buffer));
9150 ast_str_set(&buffer, 0, "%s", "");
9151 }
9152 }
9153 if ((i + 1) % 3 != 0) {
9154 ast_cli(a->fd, "%s\n", ast_str_buffer(buffer));
9155 }
9156
9157 ao2_iterator_destroy(it_events);
9158 ast_free(items);
9159 ao2_ref(events, -1);
9160 ast_free(buffer);
9161
9162 return CLI_SUCCESS;
9163}
9164
9165static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance)
9166{
9167 char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64], arguments_title[64];
9168
9169 term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
9170 term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
9171 term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
9172 term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
9173 term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
9174
9175 if (!ast_strlen_zero(ast_str_buffer(instance->synopsis))) {
9177 ast_cli(a->fd, "%s%s\n\n", synopsis_title, synopsis);
9179 }
9180 if (!ast_strlen_zero(ast_str_buffer(instance->syntax))) {
9181 char *syntax = ast_xmldoc_printable(ast_str_buffer(instance->syntax), 1);
9182 ast_cli(a->fd, "%s%s\n\n", syntax_title, syntax);
9183 ast_free(syntax);
9184 }
9185 if (!ast_strlen_zero(ast_str_buffer(instance->description))) {
9186 char *description = ast_xmldoc_printable(ast_str_buffer(instance->description), 1);
9187 ast_cli(a->fd, "%s%s\n\n", description_title, description);
9188 ast_free(description);
9189 }
9190 if (!ast_strlen_zero(ast_str_buffer(instance->arguments))) {
9191 char *arguments = ast_xmldoc_printable(ast_str_buffer(instance->arguments), 1);
9192 ast_cli(a->fd, "%s%s\n\n", arguments_title, arguments);
9193 ast_free(arguments);
9194 }
9195 if (!ast_strlen_zero(ast_str_buffer(instance->seealso))) {
9196 char *seealso = ast_xmldoc_printable(ast_str_buffer(instance->seealso), 1);
9197 ast_cli(a->fd, "%s%s\n\n", seealso_title, seealso);
9198 ast_free(seealso);
9199 }
9200}
9201
9202static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9203{
9205 struct ao2_iterator it_events;
9206 struct ast_xml_doc_item *item, *temp;
9207 int length;
9208
9209 if (cmd == CLI_INIT) {
9210 e->command = "manager show event";
9211 e->usage =
9212 "Usage: manager show event <eventname>\n"
9213 " Provides a detailed description a Manager interface event.\n";
9214 return NULL;
9215 }
9216
9217 events = ao2_global_obj_ref(event_docs);
9218 if (!events) {
9219 ast_cli(a->fd, "No manager event documentation loaded\n");
9220 return CLI_SUCCESS;
9221 }
9222
9223 if (cmd == CLI_GENERATE) {
9224 if (a->pos != 3) {
9225 return NULL;
9226 }
9227
9228 length = strlen(a->word);
9229 it_events = ao2_iterator_init(events, 0);
9230 while ((item = ao2_iterator_next(&it_events))) {
9231 if (!strncasecmp(a->word, item->name, length)) {
9233 ao2_ref(item, -1);
9234 break;
9235 }
9236 }
9237 ao2_ref(item, -1);
9238 }
9239 ao2_iterator_destroy(&it_events);
9240
9241 return NULL;
9242 }
9243
9244 if (a->argc != 4) {
9245 return CLI_SHOWUSAGE;
9246 }
9247
9248 if (!(item = ao2_find(events, a->argv[3], OBJ_KEY))) {
9249 ast_cli(a->fd, "Could not find event '%s'\n", a->argv[3]);
9250 return CLI_SUCCESS;
9251 }
9252
9253 ast_cli(a->fd, "Event: %s\n", a->argv[3]);
9254 for (temp = item; temp; temp = AST_LIST_NEXT(temp, next)) {
9255 print_event_instance(a, temp);
9256 }
9257
9258 ao2_ref(item, -1);
9259 return CLI_SUCCESS;
9260}
9261
9262#endif
9263
9264static struct ast_cli_entry cli_manager[] = {
9265 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
9266 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
9267 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
9268 AST_CLI_DEFINE(handle_kickmanconn, "Kick a connected manager interface connection"),
9269 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
9270 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
9271 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
9272 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
9273 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
9274 AST_CLI_DEFINE(handle_manager_show_settings, "Show manager global settings"),
9275#ifdef AST_XML_DOCS
9276 AST_CLI_DEFINE(handle_manager_show_events, "List manager interface events"),
9277 AST_CLI_DEFINE(handle_manager_show_event, "Show a manager interface event"),
9278#endif
9279};
9280
9281/*!
9282 * \internal
9283 * \brief Load the config channelvars variable.
9284 *
9285 * \param var Config variable to load.
9286 */
9288{
9289 char *parse = NULL;
9291 AST_APP_ARG(vars)[MAX_VARS];
9292 );
9293
9296
9297 /* parse the setting */
9300
9302}
9303
9304/*!
9305 * \internal
9306 * \brief Load the config disabledevents variable.
9307 *
9308 * \param var Config variable to load.
9309 */
9311{
9314}
9315
9316/*!
9317 * \internal
9318 * \brief Free a user record. Should already be removed from the list
9319 */
9321{
9322 ast_free(user->a1_hash);
9323 ast_free(user->secret);
9324 if (user->includefilters) {
9325 ao2_t_ref(user->includefilters, -1, "decrement ref for include container, should be last one");
9326 }
9327 if (user->excludefilters) {
9328 ao2_t_ref(user->excludefilters, -1, "decrement ref for exclude container, should be last one");
9329 }
9330 user->acl = ast_free_acl_list(user->acl);
9331 ast_variables_destroy(user->chanvars);
9332 ast_free(user);
9333}
9334
9335/*!
9336 * \internal
9337 * \brief Clean up resources on Asterisk shutdown
9338 */
9339static void manager_shutdown(void)
9340{
9341 struct ast_manager_user *user;
9342
9343#ifdef TEST_FRAMEWORK
9344 AST_TEST_UNREGISTER(eventfilter_test_creation);
9345 AST_TEST_UNREGISTER(eventfilter_test_matching);
9346 AST_TEST_UNREGISTER(originate_permissions_test);
9347#endif
9348
9349 /* This event is not actually transmitted, but causes all TCP sessions to be closed */
9350 manager_event(EVENT_FLAG_SHUTDOWN, "CloseSession", "CloseSession: true\r\n");
9351
9352 ast_manager_unregister("Ping");
9353 ast_manager_unregister("Events");
9354 ast_manager_unregister("Logoff");
9355 ast_manager_unregister("Login");
9356 ast_manager_unregister("Challenge");
9357 ast_manager_unregister("Hangup");
9358 ast_manager_unregister("Status");
9359 ast_manager_unregister("Setvar");
9360 ast_manager_unregister("Getvar");
9361 ast_manager_unregister("GetConfig");
9362 ast_manager_unregister("GetConfigJSON");
9363 ast_manager_unregister("UpdateConfig");
9364 ast_manager_unregister("CreateConfig");
9365 ast_manager_unregister("ListCategories");
9366 ast_manager_unregister("Redirect");
9367 ast_manager_unregister("Atxfer");
9368 ast_manager_unregister("CancelAtxfer");
9369 ast_manager_unregister("Originate");
9370 ast_manager_unregister("Command");
9371 ast_manager_unregister("ExtensionState");
9372 ast_manager_unregister("PresenceState");
9373 ast_manager_unregister("AbsoluteTimeout");
9374 ast_manager_unregister("MailboxStatus");
9375 ast_manager_unregister("MailboxCount");
9376 ast_manager_unregister("ListCommands");
9377 ast_manager_unregister("SendText");
9378 ast_manager_unregister("UserEvent");
9379 ast_manager_unregister("WaitEvent");
9380 ast_manager_unregister("CoreSettings");
9381 ast_manager_unregister("CoreStatus");
9382 ast_manager_unregister("Reload");
9383 ast_manager_unregister("LoggerRotate");
9384 ast_manager_unregister("CoreShowChannels");
9385 ast_manager_unregister("CoreShowChannelMap");
9386 ast_manager_unregister("ModuleLoad");
9387 ast_manager_unregister("ModuleCheck");
9388 ast_manager_unregister("AOCMessage");
9389 ast_manager_unregister("Filter");
9390 ast_manager_unregister("BlindTransfer");
9393
9394#ifdef AST_XML_DOCS
9395 ao2_t_global_obj_release(event_docs, "Dispose of event_docs");
9396#endif
9397
9398#ifdef TEST_FRAMEWORK
9399 stasis_forward_cancel(test_suite_forwarder);
9400 test_suite_forwarder = NULL;
9401#endif
9402
9403 if (stasis_router) {
9406 }
9414
9417
9428
9429 ao2_global_obj_release(mgr_sessions);
9430
9431 while ((user = AST_LIST_REMOVE_HEAD(&users, list))) {
9433 }
9435
9438}
9439
9440
9441/*! \brief Initialize all \ref stasis topics and routers used by the various
9442 * sub-components of AMI
9443 */
9445{
9446 int res = 0;
9447
9449 if (!rtp_topic_forwarder) {
9450 return -1;
9451 }
9452
9455 return -1;
9456 }
9457
9459 if (!stasis_router) {
9460 return -1;
9461 }
9464
9467
9470
9471 if (res != 0) {
9472 return -1;
9473 }
9474 return 0;
9475}
9476
9477static int subscribe_all(void)
9478{
9480 ast_log(AST_LOG_ERROR, "Failed to initialize manager subscriptions\n");
9481 return -1;
9482 }
9483 if (manager_system_init()) {
9484 ast_log(AST_LOG_ERROR, "Failed to initialize manager system handling\n");
9485 return -1;
9486 }
9487 if (manager_channels_init()) {
9488 ast_log(AST_LOG_ERROR, "Failed to initialize manager channel handling\n");
9489 return -1;
9490 }
9491 if (manager_mwi_init()) {
9492 ast_log(AST_LOG_ERROR, "Failed to initialize manager MWI handling\n");
9493 return -1;
9494 }
9495 if (manager_bridging_init()) {
9496 return -1;
9497 }
9498 if (manager_endpoints_init()) {
9499 ast_log(AST_LOG_ERROR, "Failed to initialize manager endpoints handling\n");
9500 return -1;
9501 }
9502
9503 subscribed = 1;
9504 return 0;
9505}
9506
9507static void manager_set_defaults(void)
9508{
9509 manager_enabled = 0;
9510 displayconnects = 1;
9512 authtimeout = 30;
9513 authlimit = 50;
9514 manager_debug = 0; /* Debug disabled by default */
9515
9516 /* default values */
9518 sizeof(global_realm));
9521
9522 ami_tls_cfg.enabled = 0;
9533}
9534
9535static int __init_manager(int reload, int by_external_config)
9536{
9537 struct ast_config *ucfg = NULL, *cfg = NULL;
9538 const char *val;
9539 char *cat = NULL;
9540 int newhttptimeout = 60;
9541 struct ast_manager_user *user = NULL;
9542 struct ast_variable *var;
9543 struct ast_flags config_flags = { (reload && !by_external_config) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9544 char a1[337];
9545 char a1_hash[256];
9546 struct ast_sockaddr ami_desc_local_address_tmp;
9547 struct ast_sockaddr amis_desc_local_address_tmp;
9548 int tls_was_enabled = 0;
9549 int acl_subscription_flag = 0;
9550
9551 if (!reload) {
9552 struct ao2_container *sessions;
9553#ifdef AST_XML_DOCS
9554 struct ao2_container *temp_event_docs;
9555#endif
9556 int res;
9557
9559 if (res != 0) {
9560 return -1;
9561 }
9562 manager_topic = stasis_topic_create("manager:core");
9563 if (!manager_topic) {
9564 return -1;
9565 }
9566
9567 /* Register default actions */
9607
9608#ifdef TEST_FRAMEWORK
9609 test_suite_forwarder = stasis_forward_all(ast_test_suite_topic(), manager_topic);
9610#endif
9611
9615
9616 /* Append placeholder event so master_eventq never runs dry */
9617 if (append_event("Event: Placeholder\r\n\r\n",
9618 ast_str_hash("Placeholder"), 0)) {
9619 return -1;
9620 }
9621
9622#ifdef AST_XML_DOCS
9623 temp_event_docs = ast_xmldoc_build_documentation("managerEvent");
9624 if (temp_event_docs) {
9625 ao2_t_global_obj_replace_unref(event_docs, temp_event_docs, "Toss old event docs");
9626 ao2_t_ref(temp_event_docs, -1, "Remove creation ref - container holds only ref now");
9627 }
9628#endif
9629
9630 /* If you have a NULL hash fn, you only need a single bucket */
9632 if (!sessions) {
9633 return -1;
9634 }
9636 ao2_ref(sessions, -1);
9637
9638 /* Initialize all settings before first configuration load. */
9640 }
9641
9642 cfg = ast_config_load2("manager.conf", "manager", config_flags);
9643 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9644 return 0;
9645 } else if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
9646 ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf, or configuration is invalid.\n");
9647 return 0;
9648 }
9649
9650 /* 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. */
9651 if (!by_external_config) {
9653 }
9654
9655 if (reload) {
9656 /* Reset all settings before reloading configuration */
9657 tls_was_enabled = ami_tls_cfg.enabled;
9659 }
9660
9661 ast_sockaddr_parse(&ami_desc_local_address_tmp, "[::]", 0);
9662 ast_sockaddr_set_port(&ami_desc_local_address_tmp, DEFAULT_MANAGER_PORT);
9663
9664 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
9665 val = var->value;
9666
9667 /* read tls config options while preventing unsupported options from being set */
9668 if (strcasecmp(var->name, "tlscafile")
9669 && strcasecmp(var->name, "tlscapath")
9670 && strcasecmp(var->name, "tlscadir")
9671 && strcasecmp(var->name, "tlsverifyclient")
9672 && strcasecmp(var->name, "tlsdontverifyserver")
9673 && strcasecmp(var->name, "tlsclientmethod")
9674 && strcasecmp(var->name, "sslclientmethod")
9675 && !ast_tls_read_conf(&ami_tls_cfg, &amis_desc, var->name, val)) {
9676 continue;
9677 }
9678
9679 if (!strcasecmp(var->name, "enabled")) {
9681 } else if (!strcasecmp(var->name, "webenabled")) {
9683 } else if (!strcasecmp(var->name, "port")) {
9684 int bindport;
9685 if (ast_parse_arg(val, PARSE_UINT32|PARSE_IN_RANGE, &bindport, 1024, 65535)) {
9686 ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
9687 }
9688 ast_sockaddr_set_port(&ami_desc_local_address_tmp, bindport);
9689 } else if (!strcasecmp(var->name, "bindaddr")) {
9690 /* remember port if it has already been set */
9691 int setport = ast_sockaddr_port(&ami_desc_local_address_tmp);
9692
9694 ast_log(LOG_WARNING, "Invalid address '%s' specified, default '%s' will be used\n", val,
9695 ast_sockaddr_stringify_addr(&ami_desc_local_address_tmp));
9696 } else {
9697 ast_sockaddr_parse(&ami_desc_local_address_tmp, val, PARSE_PORT_IGNORE);
9698 }
9699
9700 if (setport) {
9701 ast_sockaddr_set_port(&ami_desc_local_address_tmp, setport);
9702 }
9703
9704 } else if (!strcasecmp(var->name, "brokeneventsaction")) {
9706 } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
9708 } else if (!strcasecmp(var->name, "displayconnects")) {
9710 } else if (!strcasecmp(var->name, "timestampevents")) {
9712 } else if (!strcasecmp(var->name, "debug")) {
9714 } else if (!strcasecmp(var->name, "httptimeout")) {
9715 newhttptimeout = atoi(val);
9716 } else if (!strcasecmp(var->name, "authtimeout")) {
9717 int timeout = atoi(var->value);
9718
9719 if (timeout < 1) {
9720 ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", var->value);
9721 } else {
9722 authtimeout = timeout;
9723 }
9724 } else if (!strcasecmp(var->name, "authlimit")) {
9725 int limit = atoi(var->value);
9726
9727 if (limit < 1) {
9728 ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", var->value);
9729 } else {
9730 authlimit = limit;
9731 }
9732 } else if (!strcasecmp(var->name, "channelvars")) {
9734 } else if (!strcasecmp(var->name, "disabledevents")) {
9736 } else {
9737 ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
9738 var->name, val);
9739 }
9740 }
9741
9742 if (manager_enabled && !subscribed) {
9743 if (subscribe_all() != 0) {
9744 ast_log(LOG_ERROR, "Manager subscription error\n");
9745 return -1;
9746 }
9747 }
9748
9749 ast_sockaddr_copy(&amis_desc_local_address_tmp, &amis_desc.local_address);
9750
9751 /* if the amis address has not been set, default is the same as non secure ami */
9752 if (ast_sockaddr_isnull(&amis_desc_local_address_tmp)) {
9753 ast_sockaddr_copy(&amis_desc_local_address_tmp, &ami_desc_local_address_tmp);
9754 }
9755
9756 /* if the amis address was not set, it will have non-secure ami port set; if
9757 amis address was set, we need to check that a port was set or not, if not
9758 use the default tls port */
9759 if (ast_sockaddr_port(&amis_desc_local_address_tmp) == 0 ||
9760 (ast_sockaddr_port(&ami_desc_local_address_tmp) == ast_sockaddr_port(&amis_desc_local_address_tmp))) {
9761
9762 ast_sockaddr_set_port(&amis_desc_local_address_tmp, DEFAULT_MANAGER_TLS_PORT);
9763 }
9764
9765 if (manager_enabled) {
9766 ast_sockaddr_copy(&ami_desc.local_address, &ami_desc_local_address_tmp);
9767 ast_sockaddr_copy(&amis_desc.local_address, &amis_desc_local_address_tmp);
9768 }
9769
9771
9772 /* First, get users from users.conf */
9773 ucfg = ast_config_load2("users.conf", "manager", config_flags);
9774 if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED) && ucfg != CONFIG_STATUS_FILEINVALID) {
9775 const char *hasmanager;
9776 int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
9777
9778 while ((cat = ast_category_browse(ucfg, cat))) {
9779 if (!strcasecmp(cat, "general")) {
9780 continue;
9781 }
9782
9783 hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
9784 if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
9785 const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
9786 const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
9787 const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
9788 const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
9789 const char *user_allowmultiplelogin = ast_variable_retrieve(ucfg, cat, "allowmultiplelogin");
9790 const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
9791
9792 /* Look for an existing entry,
9793 * if none found - create one and add it to the list
9794 */
9795 if (!(user = get_manager_by_name_locked(cat))) {
9796 if (!(user = ast_calloc(1, sizeof(*user)))) {
9797 break;
9798 }
9799
9800 /* Copy name over */
9801 ast_copy_string(user->username, cat, sizeof(user->username));
9802 /* Insert into list */
9804 user->acl = NULL;
9805 user->keep = 1;
9806 user->readperm = -1;
9807 user->writeperm = -1;
9808 /* Default displayconnect from [general] */
9809 user->displayconnects = displayconnects;
9810 /* Default allowmultiplelogin from [general] */
9811 user->allowmultiplelogin = allowmultiplelogin;
9812 user->writetimeout = 100;
9813 }
9814
9815 if (!user_secret) {
9816 user_secret = ast_variable_retrieve(ucfg, "general", "secret");
9817 }
9818 if (!user_read) {
9819 user_read = ast_variable_retrieve(ucfg, "general", "read");
9820 }
9821 if (!user_write) {
9822 user_write = ast_variable_retrieve(ucfg, "general", "write");
9823 }
9824 if (!user_displayconnects) {
9825 user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
9826 }
9827 if (!user_allowmultiplelogin) {
9828 user_allowmultiplelogin = ast_variable_retrieve(ucfg, "general", "allowmultiplelogin");
9829 }
9830 if (!user_writetimeout) {
9831 user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
9832 }
9833
9834 if (!ast_strlen_zero(user_secret)) {
9835 ast_free(user->secret);
9836 user->secret = ast_strdup(user_secret);
9837 }
9838
9839 if (user_read) {
9840 user->readperm = get_perm(user_read);
9841 }
9842 if (user_write) {
9843 user->writeperm = get_perm(user_write);
9844 }
9845 if (user_displayconnects) {
9846 user->displayconnects = ast_true(user_displayconnects);
9847 }
9848 if (user_allowmultiplelogin) {
9849 user->allowmultiplelogin = ast_true(user_allowmultiplelogin);
9850 }
9851 if (user_writetimeout) {
9852 int value = atoi(user_writetimeout);
9853 if (value < 100) {
9854 ast_log(LOG_WARNING, "Invalid writetimeout value '%d' in users.conf\n", value);
9855 } else {
9856 user->writetimeout = value;
9857 }
9858 }
9859 }
9860 }
9861 ast_config_destroy(ucfg);
9862 }
9863
9864 /* cat is NULL here in any case */
9865
9866 while ((cat = ast_category_browse(cfg, cat))) {
9867 struct ast_acl_list *oldacl;
9868
9869 if (!strcasecmp(cat, "general")) {
9870 continue;
9871 }
9872
9873 /* Look for an existing entry, if none found - create one and add it to the list */
9874 if (!(user = get_manager_by_name_locked(cat))) {
9875 if (!(user = ast_calloc(1, sizeof(*user)))) {
9876 break;
9877 }
9878 /* Copy name over */
9879 ast_copy_string(user->username, cat, sizeof(user->username));
9880
9881 user->acl = NULL;
9882 user->readperm = 0;
9883 user->writeperm = 0;
9884 /* Default displayconnect from [general] */
9885 user->displayconnects = displayconnects;
9886 /* Default allowmultiplelogin from [general] */
9887 user->allowmultiplelogin = allowmultiplelogin;
9888 user->writetimeout = 100;
9891 if (!user->includefilters || !user->excludefilters) {
9893 break;
9894 }
9895
9896 /* Insert into list */
9898 } else {
9899 ao2_t_callback(user->includefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all include filters");
9900 ao2_t_callback(user->excludefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all exclude filters");
9901 }
9902
9903 /* Make sure we keep this user and don't destroy it during cleanup */
9904 user->keep = 1;
9905 oldacl = user->acl;
9906 user->acl = NULL;
9907 ast_variables_destroy(user->chanvars);
9908
9909 var = ast_variable_browse(cfg, cat);
9910 for (; var; var = var->next) {
9911 if (!strcasecmp(var->name, "secret")) {
9912 ast_free(user->secret);
9913 user->secret = ast_strdup(var->value);
9914 } else if (!strcasecmp(var->name, "deny") ||
9915 !strcasecmp(var->name, "permit") ||
9916 !strcasecmp(var->name, "acl")) {
9917 int acl_error = 0;
9918
9919 ast_append_acl(var->name, var->value, &user->acl, &acl_error, &acl_subscription_flag);
9920 if (acl_error) {
9921 ast_log(LOG_ERROR, "Invalid ACL '%s' for manager user '%s' on line %d. Deleting user\n",
9922 var->value, user->username, var->lineno);
9923 user->keep = 0;
9924 }
9925 } else if (!strcasecmp(var->name, "read") ) {
9926 user->readperm = get_perm(var->value);
9927 } else if (!strcasecmp(var->name, "write") ) {
9928 user->writeperm = get_perm(var->value);
9929 } else if (!strcasecmp(var->name, "displayconnects") ) {
9930 user->displayconnects = ast_true(var->value);
9931 } else if (!strcasecmp(var->name, "allowmultiplelogin") ) {
9932 user->allowmultiplelogin = ast_true(var->value);
9933 } else if (!strcasecmp(var->name, "writetimeout")) {
9934 int value = atoi(var->value);
9935 if (value < 100) {
9936 ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
9937 } else {
9938 user->writetimeout = value;
9939 }
9940 } else if (!strcasecmp(var->name, "setvar")) {
9941 struct ast_variable *tmpvar;
9942 char varbuf[256];
9943 char *varval;
9944 char *varname;
9945
9946 ast_copy_string(varbuf, var->value, sizeof(varbuf));
9947 varname = varbuf;
9948
9949 if ((varval = strchr(varname,'='))) {
9950 *varval++ = '\0';
9951 if ((tmpvar = ast_variable_new(varname, varval, ""))) {
9952 tmpvar->next = user->chanvars;
9953 user->chanvars = tmpvar;
9954 }
9955 }
9956 } else if (ast_begins_with(var->name, "eventfilter")) {
9957 const char *value = var->value;
9958 manager_add_filter(var->name, value, user->includefilters, user->excludefilters);
9959 } else {
9960 ast_debug(1, "%s is an unknown option.\n", var->name);
9961 }
9962 }
9963
9964 oldacl = ast_free_acl_list(oldacl);
9965 }
9966 ast_config_destroy(cfg);
9967
9968 /* Check the flag for named ACL event subscription and if we need to, register a subscription. */
9969 if (acl_subscription_flag && !by_external_config) {
9971 }
9972
9973 /* Perform cleanup - essentially prune out old users that no longer exist */
9975 if (user->keep) { /* valid record. clear flag for the next round */
9976 user->keep = 0;
9977
9978 /* Calculate A1 for Digest auth */
9979 snprintf(a1, sizeof(a1), "%s:%s:%s", user->username, global_realm, user->secret);
9980 ast_md5_hash(a1_hash,a1);
9981 ast_free(user->a1_hash);
9982 user->a1_hash = ast_strdup(a1_hash);
9983 continue;
9984 }
9985 /* We do not need to keep this user so take them out of the list */
9987 ast_debug(4, "Pruning user '%s'\n", user->username);
9989 }
9991
9993
9995 if (!webregged) {
9999
10003 webregged = 1;
10004 }
10005 } else {
10006 if (webregged) {
10010
10014 webregged = 0;
10015 }
10016 }
10017
10018 if (newhttptimeout > 0) {
10019 httptimeout = newhttptimeout;
10020 }
10021
10023 if (tls_was_enabled && !ami_tls_cfg.enabled) {
10025 } else if (ast_ssl_setup(amis_desc.tls_cfg)) {
10027 }
10028
10029 return 0;
10030}
10031
10032static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub,
10033 struct stasis_message *message)
10034{
10036 return;
10037 }
10038
10039 /* For now, this is going to be performed simply and just execute a forced reload. */
10040 ast_log(LOG_NOTICE, "Reloading manager in response to ACL change event.\n");
10041 __init_manager(1, 1);
10042}
10043
10044static int unload_module(void)
10045{
10046 return 0;
10047}
10048
10049static int load_module(void)
10050{
10051 int rc = 0;
10054#ifdef TEST_FRAMEWORK
10055 AST_TEST_REGISTER(eventfilter_test_creation);
10056 AST_TEST_REGISTER(eventfilter_test_matching);
10057 AST_TEST_REGISTER(originate_permissions_test);
10058#endif
10059 return rc;
10060}
10061
10062static int reload_module(void)
10063{
10065}
10066
10067int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
10068{
10069 AST_LIST_INSERT_HEAD(&s->session->datastores, datastore, entry);
10070
10071 return 0;
10072}
10073
10074int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
10075{
10076 return AST_LIST_REMOVE(&s->session->datastores, datastore, entry) ? 0 : -1;
10077}
10078
10079struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid)
10080{
10081 struct ast_datastore *datastore = NULL;
10082
10083 if (info == NULL)
10084 return NULL;
10085
10087 if (datastore->info != info) {
10088 continue;
10089 }
10090
10091 if (uid == NULL) {
10092 /* matched by type only */
10093 break;
10094 }
10095
10096 if ((datastore->uid != NULL) && !strcasecmp(uid, datastore->uid)) {
10097 /* Matched by type AND uid */
10098 break;
10099 }
10100 }
10102
10103 return datastore;
10104}
10105
10106int ast_str_append_event_header(struct ast_str **fields_string,
10107 const char *header, const char *value)
10108{
10109 if (!*fields_string) {
10110 *fields_string = ast_str_create(128);
10111 if (!*fields_string) {
10112 return -1;
10113 }
10114 }
10115
10116 return (ast_str_append(fields_string, 0, "%s: %s\r\n", header, value) < 0) ? -1 : 0;
10117}
10118
10119static void manager_event_blob_dtor(void *obj)
10120{
10121 struct ast_manager_event_blob *ev = obj;
10122
10124}
10125
10127__attribute__((format(printf, 3, 4)))
10129 int event_flags,
10130 const char *manager_event,
10131 const char *extra_fields_fmt,
10132 ...)
10133{
10134 struct ast_manager_event_blob *ev;
10135 va_list argp;
10136
10137 ast_assert(extra_fields_fmt != NULL);
10139
10141 if (!ev) {
10142 return NULL;
10143 }
10144
10145 if (ast_string_field_init(ev, 20)) {
10146 ao2_ref(ev, -1);
10147 return NULL;
10148 }
10149
10152
10153 va_start(argp, extra_fields_fmt);
10154 ast_string_field_ptr_build_va(ev, &ev->extra_fields, extra_fields_fmt, argp);
10155 va_end(argp);
10156
10157 return ev;
10158}
10159
10161 .support_level = AST_MODULE_SUPPORT_CORE,
10162 .load = load_module,
10163 .unload = unload_module,
10165 .load_pri = AST_MODPRI_CORE,
10166 .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:7638
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:6741
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:7583
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:7366
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:6938
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:9165
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:7561
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:6899
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:7406
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:7679
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:7448
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:7187
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:7431
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:7066
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:7272
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:6576
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:6792
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:7170
static int manager_moduleload(struct mansession *s, const struct message *m)
Definition: manager.c:6818
static void acl_change_stasis_unsubscribe(void)
Definition: manager.c:244
static int reload_module(void)
Definition: manager.c:10062
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:6460
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:7734
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:6753
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:6492
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:7720
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:6660
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:6559
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:10032
#define MANAGER_EVENT_BUF_INITSIZE
Definition: manager.c:7449
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:7451
int ast_manager_unregister(const char *action)
support functions to register/unregister AMI action handlers,
Definition: manager.c:7608
@ 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:1465
const char * ast_category_get_name(const struct ast_category *category)
Return the name of the category.
Definition: main/config.c:1126
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:1093
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:3336
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:1582
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:2740
void ast_category_rename(struct ast_category *cat, const char *name)
Definition: main/config.c:1460
int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
Definition: main/config.c:1497
#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:1181
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:1542
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:3559
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:1136
#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:3842
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:1614
int ast_category_is_template(const struct ast_category *category)
Check if category is a template.
Definition: main/config.c:1131
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:1255
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:1120
@ 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:1433
#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:8871
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:9444
#define ROW_FMT
static struct ast_tls_config ami_tls_cfg
Definition: manager.c:9010
static void close_mansession_file(struct mansession *s)
Definition: manager.c:8223
static int ast_xml_doc_item_cmp_fn(const void *a, const void *b)
Definition: manager.c:9077
static struct ast_tcptls_session_args ami_desc
Definition: manager.c:9011
static struct ast_http_uri managerxmluri
Definition: manager.c:8850
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:9084
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:8801
int astman_verify_session_writepermissions(uint32_t ident, int perm)
Verify a session's write permissions against a permission mask.
Definition: manager.c:7949
static int __init_manager(int reload, int by_external_config)
Definition: manager.c:9535
static struct ast_tcptls_session_args amis_desc
Definition: manager.c:9022
static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
Definition: manager.c:7987
#define FORMAT3
static struct ast_custom_function managerclient_function
description of AMI_CLIENT dialplan function
Definition: manager.c:8983
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:8994
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:10079
static struct mansession_session * find_session(uint32_t ident, int incinuse)
Definition: manager.c:7828
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:8812
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:10106
#define HSMCONN_FORMAT1
int astman_datastore_add(struct mansession *s, struct ast_datastore *datastore)
Add a datastore to a session.
Definition: manager.c:10067
static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
Definition: manager.c:8066
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:9033
static void manager_event_blob_dtor(void *obj)
Definition: manager.c:10119
static void load_channelvars(struct ast_variable *var)
Definition: manager.c:9287
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:10128
static struct mansession_session * find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
Definition: manager.c:7867
int astman_verify_session_readpermissions(uint32_t ident, int perm)
Verify a session's read permissions against a permission mask.
Definition: manager.c:7916
static struct ast_http_uri manageruri
Definition: manager.c:8842
static void manager_set_defaults(void)
Definition: manager.c:9507
static void manager_shutdown(void)
Definition: manager.c:9339
#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:8233
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:8823
static char * handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: manager.c:9202
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:8936
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:8882
static int load_module(void)
Definition: manager.c:10049
static struct ast_http_uri arawmanuri
Definition: manager.c:8893
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:8105
static int variable_count_hash_fn(const void *vvc, const int flags)
Definition: manager.c:8059
#define FORMAT
int astman_is_authed(uint32_t ident)
Determine if a manager session ident is authenticated.
Definition: manager.c:7900
static struct ast_http_uri rawmanuri
Definition: manager.c:8834
static struct ast_cli_entry cli_manager[]
Definition: manager.c:9264
#define FORMAT2
static int unload_module(void)
Definition: manager.c:10044
static void manager_free_user(struct ast_manager_user *user)
Definition: manager.c:9320
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:8860
output_format
Definition: manager.c:7811
@ FORMAT_RAW
Definition: manager.c:7812
@ FORMAT_HTML
Definition: manager.c:7813
@ FORMAT_XML
Definition: manager.c:7814
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:8478
static struct ast_http_uri amanageruri
Definition: manager.c:8902
#define TEST_STRING
static int webregged
Definition: manager.c:8989
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:8921
static int subscribe_all(void)
Definition: manager.c:9477
static const char *const contenttype[]
Definition: manager.c:7817
static struct ast_http_uri amanagerxmluri
Definition: manager.c:8911
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:8266
int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastore)
Remove a datastore from a session.
Definition: manager.c:10074
static void load_disabledevents(struct ast_variable *var)
Definition: manager.c:9310
#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 ast_processed_calls(void)
Retrieve the total number of calls processed through the PBX since last restart.
Definition: pbx.c:4765
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:8055
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