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