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