Asterisk - The Open Source Telephony Project GIT-master-3dee037
manager.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2006, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief The Asterisk Management Interface - AMI
22 *
23 * \author Mark Spencer <markster@digium.com>
24 *
25 * OpenSSL http://www.openssl.org - for AMI/SSL
26 *
27 * At the moment this file contains a number of functions, namely:
28 *
29 * - data structures storing AMI state
30 * - AMI-related API functions, used by internal asterisk components
31 * - handlers for AMI-related CLI functions
32 * - handlers for AMI functions (available through the AMI socket)
33 * - the code for the main AMI listener thread and individual session threads
34 * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
35 *
36 * \ref amiconf
37 */
38
39/*! \li \ref manager.c uses the configuration file \ref manager.conf and \ref users.conf
40 * \addtogroup configuration_file
41 */
42
43/*! \page manager.conf manager.conf
44 * \verbinclude manager.conf.sample
45 */
46
47/*! \page users.conf users.conf
48 * \verbinclude users.conf.sample
49 */
50
51/*** MODULEINFO
52 <support_level>core</support_level>
53 ***/
54
55#include "asterisk.h"
56
57#include "asterisk/paths.h" /* use various ast_config_AST_* */
58#include <ctype.h>
59#include <sys/time.h>
60#include <signal.h>
61#include <sys/mman.h>
62#include <sys/types.h>
63#include <regex.h>
64
65#include "asterisk/channel.h"
66#include "asterisk/file.h"
67#include "asterisk/manager.h"
68#include "asterisk/module.h"
69#include "asterisk/config.h"
70#include "asterisk/callerid.h"
71#include "asterisk/core_local.h"
72#include "asterisk/lock.h"
73#include "asterisk/cli.h"
74#include "asterisk/app.h"
75#include "asterisk/mwi.h"
76#include "asterisk/pbx.h"
77#include "asterisk/md5.h"
78#include "asterisk/acl.h"
79#include "asterisk/utils.h"
80#include "asterisk/tcptls.h"
81#include "asterisk/http.h"
85#include "asterisk/term.h"
86#include "asterisk/astobj2.h"
87#include "asterisk/features.h"
89#include "asterisk/aoc.h"
90#include "asterisk/strings.h"
96#include "asterisk/test.h"
97#include "asterisk/json.h"
98#include "asterisk/bridge.h"
100#include "asterisk/rtp_engine.h"
102#include "asterisk/translate.h"
104#include "asterisk/message.h"
105
106/*! \addtogroup Group_AMI AMI functions
107*/
108/*! @{
109 Doxygen group */
110
125
131};
132
133/*!
134 * Linked list of events.
135 * Global events are appended to the list by append_event().
136 * The usecount is the number of stored pointers to the element,
137 * excluding the list pointers. So an element that is only in
138 * the list has a usecount of 0, not 1.
139 *
140 * Clients have a pointer to the last event processed, and for each
141 * of these clients we track the usecount of the elements.
142 * If we have a pointer to an entry in the list, it is safe to navigate
143 * it forward because elements will not be deleted, but only appended.
144 * The worst that can happen is seeing the pointer still NULL.
145 *
146 * When the usecount of an element drops to 0, and the element is the
147 * first in the list, we can remove it. Removal is done within the
148 * main thread, which is woken up for the purpose.
149 *
150 * For simplicity of implementation, we make sure the list is never empty.
151 */
152struct eventqent {
153 int usecount; /*!< # of clients who still need the event */
155 unsigned int seq; /*!< sequence number */
156 struct timeval tv; /*!< When event was allocated */
159 char eventdata[1]; /*!< really variable size, allocated by append_event() */
160};
161
163
164static int displayconnects = 1;
165static int allowmultiplelogin = 1;
167static int httptimeout = 60;
168static int broken_events_action = 0;
169static int manager_enabled = 0;
170static int subscribed = 0;
171static int webmanager_enabled = 0;
172static int manager_debug = 0; /*!< enable some debugging code in the manager */
173static int authtimeout;
174static int authlimit;
177
178#define DEFAULT_REALM "asterisk"
179static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
180
181static int unauth_sessions = 0;
183
184/*! \brief A \ref stasis_topic that all topics AMI cares about will be forwarded to */
186
187/*! \brief The \ref stasis_message_router for all \ref stasis messages */
189
190/*! \brief The \ref stasis_subscription for forwarding the RTP topic to the AMI topic */
192
193/*! \brief The \ref stasis_subscription for forwarding the Security topic to the AMI topic */
195
196/*!
197 * \brief Set to true (non-zero) to globally allow all dangerous AMI actions to run
198 */
200
201#ifdef TEST_FRAMEWORK
202/*! \brief The \ref stasis_subscription for forwarding the Test topic to the AMI topic */
203static struct stasis_forward *test_suite_forwarder;
204#endif
205
206#define MGR_SHOW_TERMINAL_WIDTH 80
207
208#define MAX_VARS 128
209
210/*! \brief Fake event class used to end sessions at shutdown */
211#define EVENT_FLAG_SHUTDOWN -1
212
213/*! \brief
214 * Descriptor for a manager session, either on the AMI socket or over HTTP.
215 *
216 * \note
217 * AMI session have managerid == 0; the entry is created upon a connect,
218 * and destroyed with the socket.
219 * HTTP sessions have managerid != 0, the value is used as a search key
220 * to lookup sessions (using the mansession_id cookie, or nonce key from
221 * Digest Authentication http header).
222 */
223#define MAX_BLACKLIST_CMD_LEN 2
224static const struct {
225 const char *words[AST_MAX_CMD_LEN];
226} command_blacklist[] = {
227 {{ "module", "load", NULL }},
228 {{ "module", "unload", NULL }},
229 {{ "restart", "gracefully", NULL }},
231
232static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message);
233
235{
236 if (!acl_change_sub) {
241 }
242}
243
245{
247}
248
249/* In order to understand what the heck is going on with the
250 * mansession_session and mansession structs, we need to have a bit of a history
251 * lesson.
252 *
253 * In the beginning, there was the mansession. The mansession contained data that was
254 * intrinsic to a manager session, such as the time that it started, the name of the logged-in
255 * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
256 * sessions, these were used to represent the TCP socket over which the AMI session was taking
257 * place. It makes perfect sense for these fields to be a part of the session-specific data since
258 * the session actually defines this information.
259 *
260 * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
261 * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
262 * but rather to the action that is being executed. Because a single session may execute many commands
263 * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
264 * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
265 * has had a chance to properly close its handles.
266 *
267 * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
268 * from being run at the same time in a single session. Some manager actions may block for a long time, thus
269 * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
270 * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
271 * part of the action instead.
272 *
273 * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
274 * contain the action-specific information, such as which file to write to. In order to maintain expectations
275 * of action handlers and not have to change the public API of the manager code, we would need to name this
276 * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
277 * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
278 * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
279 * data.
280 */
282 /*! \todo XXX need to document which fields it is protecting */
283 struct ast_sockaddr addr; /*!< address we are connecting from */
284 struct ast_iostream *stream; /*!< AMI stream */
285 int inuse; /*!< number of HTTP sessions using this entry */
286 int needdestroy; /*!< Whether an HTTP session should be destroyed */
287 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
288 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
289 time_t sessionstart; /*!< Session start time */
290 struct timeval sessionstart_tv; /*!< Session start time */
291 time_t sessiontimeout; /*!< Session timeout if HTTP */
292 char username[80]; /*!< Logged in username */
293 char challenge[10]; /*!< Authentication challenge */
294 int authenticated; /*!< Authentication status */
295 int readperm; /*!< Authorization for reading */
296 int writeperm; /*!< Authorization for writing */
297 char inbuf[1025]; /*!< Buffer - we use the extra byte to add a '\\0' and simplify parsing */
298 int inlen; /*!< number of buffered bytes */
299 struct ao2_container *includefilters; /*!< Manager event filters - include list */
300 struct ao2_container *excludefilters; /*!< Manager event filters - exclude list */
301 struct ast_variable *chanvars; /*!< Channel variables to set for originate */
302 int send_events; /*!< XXX what ? */
303 struct eventqent *last_ev; /*!< last event processed. */
304 int writetimeout; /*!< Timeout for ast_carefulwrite() */
305 time_t authstart;
306 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
307 time_t noncetime; /*!< Timer for nonce value expiration */
308 unsigned long oldnonce; /*!< Stale nonce value */
309 unsigned long nc; /*!< incremental nonce counter */
310 unsigned int kicked:1; /*!< Flag set if session is forcibly kicked */
311 ast_mutex_t notify_lock; /*!< Lock for notifying this session of events */
314};
315
320
321/*! \brief In case you didn't read that giant block of text above the mansession_session struct, the
322 * \ref mansession is named this solely to keep the API the same in Asterisk. This structure really
323 * represents data that is different from Manager action to Manager action. The mansession_session pointer
324 * contained within points to session-specific data.
325 */
331 unsigned int write_error:1;
334};
335
336/*! Active manager connection sessions container. */
337static AO2_GLOBAL_OBJ_STATIC(mgr_sessions);
338
339/*! \brief user descriptor, as read from the config file.
340 *
341 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
342 * lines which are not supported here, and readperm/writeperm/writetimeout
343 * are not stored.
344 */
346 char username[80];
347 char *secret; /*!< Secret for logging in */
348 int readperm; /*!< Authorization for reading */
349 int writeperm; /*!< Authorization for writing */
350 int writetimeout; /*!< Per user Timeout for ast_carefulwrite() */
351 int displayconnects; /*!< XXX unused */
352 int allowmultiplelogin; /*!< Per user option*/
353 int keep; /*!< mark entries created on a reload */
354 struct ao2_container *includefilters; /*!< Manager event filters - include list */
355 struct ao2_container *excludefilters; /*!< Manager event filters - exclude list */
356 struct ast_acl_list *acl; /*!< ACL setting */
357 char *a1_hash; /*!< precalculated A1 for Digest auth */
358 struct ast_variable *chanvars; /*!< Channel variables to set for originate */
360};
361
362/*! \brief list of users found in the config file */
364
365/*! \brief list of actions registered */
367
368/*! \brief list of hooks registered */
370
371#ifdef AST_XML_DOCS
372/*! \brief A container of event documentation nodes */
373static AO2_GLOBAL_OBJ_STATIC(event_docs);
374#endif
375
376static int __attribute__((format(printf, 9, 0))) __manager_event_sessions(
377 struct ao2_container *sessions,
378 int category,
379 const char *event,
380 int chancount,
381 struct ast_channel **chans,
382 const char *file,
383 int line,
384 const char *func,
385 const char *fmt,
386 ...);
387
395};
396
397static char *match_type_names[] = {
398 [FILTER_MATCH_REGEX] = "regex",
399 [FILTER_MATCH_EXACT] = "exact",
400 [FILTER_MATCH_STARTS_WITH] = "starts_with",
401 [FILTER_MATCH_ENDS_WITH] = "ends_with",
402 [FILTER_MATCH_CONTAINS] = "contains",
403 [FILTER_MATCH_NONE] = "none",
404};
405
408 regex_t *regex_filter;
411 unsigned int event_name_hash;
414};
415
416static enum add_filter_result manager_add_filter(const char *criteria,
417 const char *filter_pattern, struct ao2_container *includefilters,
418 struct ao2_container *excludefilters);
419
420static int should_send_event(struct ao2_container *includefilters,
421 struct ao2_container *excludefilters, struct eventqent *eqe);
422
423/*!
424 * @{ \brief Define AMI message types.
425 */
427/*! @} */
428
429/*!
430 * \internal
431 * \brief Find a registered action object.
432 *
433 * \param name Name of AMI action to find.
434 *
435 * \return Reffed action found or NULL
436 */
437static struct manager_action *action_find(const char *name)
438{
439 struct manager_action *act;
440
442 AST_RWLIST_TRAVERSE(&actions, act, list) {
443 if (!strcasecmp(name, act->action)) {
444 ao2_t_ref(act, +1, "found action object");
445 break;
446 }
447 }
449
450 return act;
451}
452
454{
455 return manager_topic;
456}
457
459{
460 return stasis_router;
461}
462
463static void manager_json_value_str_append(struct ast_json *value, const char *key,
464 struct ast_str **res)
465{
466 switch (ast_json_typeof(value)) {
467 case AST_JSON_STRING:
468 ast_str_append(res, 0, "%s: %s\r\n", key, ast_json_string_get(value));
469 break;
470 case AST_JSON_INTEGER:
471 ast_str_append(res, 0, "%s: %jd\r\n", key, ast_json_integer_get(value));
472 break;
473 case AST_JSON_TRUE:
474 ast_str_append(res, 0, "%s: True\r\n", key);
475 break;
476 case AST_JSON_FALSE:
477 ast_str_append(res, 0, "%s: False\r\n", key);
478 break;
479 default:
480 ast_str_append(res, 0, "%s: \r\n", key);
481 break;
482 }
483}
484
485static void manager_json_to_ast_str(struct ast_json *obj, const char *key,
486 struct ast_str **res, key_exclusion_cb exclusion_cb);
487
488static void manager_json_array_with_key(struct ast_json *obj, const char* key,
489 size_t index, struct ast_str **res,
490 key_exclusion_cb exclusion_cb)
491{
492 struct ast_str *key_str = ast_str_alloca(64);
493 ast_str_set(&key_str, 0, "%s(%zu)", key, index);
495 res, exclusion_cb);
496}
497
498static void manager_json_obj_with_key(struct ast_json *obj, const char* key,
499 const char *parent_key, struct ast_str **res,
500 key_exclusion_cb exclusion_cb)
501{
502 if (parent_key) {
503 struct ast_str *key_str = ast_str_alloca(64);
504 ast_str_set(&key_str, 0, "%s/%s", parent_key, key);
506 res, exclusion_cb);
507 return;
508 }
509
510 manager_json_to_ast_str(obj, key, res, exclusion_cb);
511}
512
513void manager_json_to_ast_str(struct ast_json *obj, const char *key,
514 struct ast_str **res, key_exclusion_cb exclusion_cb)
515{
516 struct ast_json_iter *i;
517
518 /* If obj or res is not given, just return */
519 if (!obj || !res) {
520 return;
521 }
522
523 if (!*res && !(*res = ast_str_create(1024))) {
524 return;
525 }
526
527 if (exclusion_cb && key && exclusion_cb(key)) {
528 return;
529 }
530
531 if (ast_json_typeof(obj) != AST_JSON_OBJECT &&
533 manager_json_value_str_append(obj, key, res);
534 return;
535 }
536
537 if (ast_json_typeof(obj) == AST_JSON_ARRAY) {
538 size_t j;
539 for (j = 0; j < ast_json_array_size(obj); ++j) {
541 key, j, res, exclusion_cb);
542 }
543 return;
544 }
545
546 for (i = ast_json_object_iter(obj); i;
547 i = ast_json_object_iter_next(obj, i)) {
550 key, res, exclusion_cb);
551 }
552}
553
555{
556 struct ast_str *res = ast_str_create(1024);
557
558 if (!ast_json_is_null(blob)) {
559 manager_json_to_ast_str(blob, NULL, &res, exclusion_cb);
560 }
561
562 return res;
563}
564
565#define manager_event_sessions(sessions, category, event, contents , ...) \
566 __manager_event_sessions(sessions, category, event, 0, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__, contents , ## __VA_ARGS__)
567
568#define any_manager_listeners(sessions) \
569 ((sessions && ao2_container_count(sessions)) || !AST_RWLIST_EMPTY(&manager_hooks))
570
571static void manager_default_msg_cb(void *data, struct stasis_subscription *sub,
572 struct stasis_message *message)
573{
574 struct ao2_container *sessions;
575 struct ast_manager_event_blob *ev;
576
578 /* Not an AMI message; disregard */
579 return;
580 }
581
582 sessions = ao2_global_obj_ref(mgr_sessions);
584 /* Nobody is listening */
586 return;
587 }
588
590 if (!ev) {
591 /* Conversion failure */
593 return;
594 }
595
597 "%s", ev->extra_fields);
598 ao2_ref(ev, -1);
600}
601
602static void manager_generic_msg_cb(void *data, struct stasis_subscription *sub,
603 struct stasis_message *message)
604{
605 struct ast_json_payload *payload;
606 int class_type;
607 const char *type;
608 struct ast_json *event;
609 struct ast_str *event_buffer;
610 struct ao2_container *sessions;
611
612 sessions = ao2_global_obj_ref(mgr_sessions);
614 /* Nobody is listening */
616 return;
617 }
618
619 payload = stasis_message_data(message);
620 class_type = ast_json_integer_get(ast_json_object_get(payload->json, "class_type"));
622 event = ast_json_object_get(payload->json, "event");
623
625 if (!event_buffer) {
626 ast_log(AST_LOG_WARNING, "Error while creating payload for event %s\n", type);
628 return;
629 }
630
632 "%s", ast_str_buffer(event_buffer));
633 ast_free(event_buffer);
635}
636
637void ast_manager_publish_event(const char *type, int class_type, struct ast_json *obj)
638{
639 RAII_VAR(struct ast_json *, event_info, NULL, ast_json_unref);
640 RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
642
643 if (!obj || !ast_manager_get_generic_type()) {
644 return;
645 }
646
647 ast_json_ref(obj);
648 event_info = ast_json_pack("{s: s, s: i, s: o}",
649 "type", type,
650 "class_type", class_type,
651 "event", obj);
652 if (!event_info) {
653 return;
654 }
655
656 payload = ast_json_payload_create(event_info);
657 if (!payload) {
658 return;
659 }
661 if (!message) {
662 return;
663 }
665}
666
667/*! \brief Add a custom hook to be called when an event is fired */
669{
673}
674
675/*! \brief Delete a custom hook to be called when an event is fired */
677{
679 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
681}
682
684{
685 return manager_enabled;
686}
687
689{
691}
692
693/*!
694 * Grab a reference to the last event, update usecount as needed.
695 * Can handle a NULL pointer.
696 */
697static struct eventqent *grab_last(void)
698{
699 struct eventqent *ret;
700
703 /* the list is never empty now, but may become so when
704 * we optimize it in the future, so be prepared.
705 */
706 if (ret) {
708 }
710 return ret;
711}
712
713/*!
714 * Purge unused events. Remove elements from the head
715 * as long as their usecount is 0 and there is a next element.
716 */
717static void purge_events(void)
718{
719 struct eventqent *ev;
720 struct timeval now = ast_tvnow();
721
723 while ( (ev = AST_RWLIST_FIRST(&all_events)) &&
724 ev->usecount == 0 && AST_RWLIST_NEXT(ev, eq_next)) {
726 ast_free(ev);
727 }
728
730 /* Never release the last event */
731 if (!AST_RWLIST_NEXT(ev, eq_next)) {
732 break;
733 }
734
735 /* 2.5 times whatever the HTTP timeout is (maximum 2.5 hours) is the maximum time that we will definitely cache an event */
736 if (ev->usecount == 0 && ast_tvdiff_sec(now, ev->tv) > (httptimeout > 3600 ? 3600 : httptimeout) * 2.5) {
738 ast_free(ev);
739 }
740 }
743}
744
745/*!
746 * helper functions to convert back and forth between
747 * string and numeric representation of set of flags
748 */
749static const struct permalias {
750 int num;
751 const char *label;
752} perms[] = {
753 { EVENT_FLAG_SYSTEM, "system" },
754 { EVENT_FLAG_CALL, "call" },
755 { EVENT_FLAG_LOG, "log" },
756 { EVENT_FLAG_VERBOSE, "verbose" },
757 { EVENT_FLAG_COMMAND, "command" },
758 { EVENT_FLAG_AGENT, "agent" },
759 { EVENT_FLAG_USER, "user" },
760 { EVENT_FLAG_CONFIG, "config" },
761 { EVENT_FLAG_DTMF, "dtmf" },
762 { EVENT_FLAG_REPORTING, "reporting" },
763 { EVENT_FLAG_CDR, "cdr" },
764 { EVENT_FLAG_DIALPLAN, "dialplan" },
765 { EVENT_FLAG_ORIGINATE, "originate" },
766 { EVENT_FLAG_AGI, "agi" },
767 { EVENT_FLAG_CC, "cc" },
768 { EVENT_FLAG_AOC, "aoc" },
769 { EVENT_FLAG_TEST, "test" },
770 { EVENT_FLAG_SECURITY, "security" },
771 { EVENT_FLAG_MESSAGE, "message" },
772 { INT_MAX, "all" },
773 { 0, "none" },
775
776/*! Maximum string length of the AMI authority permission string buildable from perms[]. */
777#define MAX_AUTH_PERM_STRING 150
778
779/*! \brief Checks to see if a string which can be used to evaluate functions should be rejected */
780static int function_capable_string_allowed_with_auths(const char *evaluating, int writepermlist)
781{
782 if (!(writepermlist & EVENT_FLAG_SYSTEM)
783 && (
784 strstr(evaluating, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
785 strstr(evaluating, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
786 )) {
787 return 0;
788 }
789 return 1;
790}
791
792/*! \brief Convert authority code to a list of options for a user. This will only
793 * display those authority codes that have an explicit match on authority */
794static const char *user_authority_to_str(int authority, struct ast_str **res)
795{
796 int i;
797 char *sep = "";
798
799 ast_str_reset(*res);
800 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
801 if ((authority & perms[i].num) == perms[i].num) {
802 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
803 sep = ",";
804 }
805 }
806
807 if (ast_str_strlen(*res) == 0) {
808 /* replace empty string with something sensible */
809 ast_str_append(res, 0, "<none>");
810 }
811
812 return ast_str_buffer(*res);
813}
814
815
816/*! \brief Convert authority code to a list of options. Note that the EVENT_FLAG_ALL
817 * authority will always be returned. */
818static const char *authority_to_str(int authority, struct ast_str **res)
819{
820 int i;
821 char *sep = "";
822
823 ast_str_reset(*res);
824 if (authority != EVENT_FLAG_SHUTDOWN) {
825 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
826 if (authority & perms[i].num) {
827 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
828 sep = ",";
829 }
830 }
831 }
832
833 if (ast_str_strlen(*res) == 0) {
834 /* replace empty string with something sensible */
835 ast_str_append(res, 0, "<none>");
836 }
837
838 return ast_str_buffer(*res);
839}
840
841/*! Tells you if smallstr exists inside bigstr
842 which is delim by delim and uses no buf or stringsep
843 ast_instring("this|that|more","this",'|') == 1;
844
845 feel free to move this to app.c -anthm */
846static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
847{
848 const char *val = bigstr, *next;
849
850 do {
851 if ((next = strchr(val, delim))) {
852 if (!strncmp(val, smallstr, (next - val))) {
853 return 1;
854 } else {
855 continue;
856 }
857 } else {
858 return !strcmp(smallstr, val);
859 }
860 } while (*(val = (next + 1)));
861
862 return 0;
863}
864
865static int get_perm(const char *instr)
866{
867 int x = 0, ret = 0;
868
869 if (!instr) {
870 return 0;
871 }
872
873 for (x = 0; x < ARRAY_LEN(perms); x++) {
874 if (ast_instring(instr, perms[x].label, ',')) {
875 ret |= perms[x].num;
876 }
877 }
878
879 return ret;
880}
881
882/*!
883 * A number returns itself, false returns 0, true returns all flags,
884 * other strings return the flags that are set.
885 */
886static int strings_to_mask(const char *string)
887{
888 const char *p;
889
890 if (ast_strlen_zero(string)) {
891 return -1;
892 }
893
894 for (p = string; *p; p++) {
895 if (*p < '0' || *p > '9') {
896 break;
897 }
898 }
899 if (!*p) { /* all digits */
900 return atoi(string);
901 }
902 if (ast_false(string)) {
903 return 0;
904 }
905 if (ast_true(string)) { /* all permissions */
906 int x, ret = 0;
907 for (x = 0; x < ARRAY_LEN(perms); x++) {
908 ret |= perms[x].num;
909 }
910 return ret;
911 }
912 return get_perm(string);
913}
914
915/*! \brief Unreference manager session object.
916 If no more references, then go ahead and delete it */
918{
919 int refcount = ao2_ref(s, -1);
920 if (manager_debug) {
921 ast_debug(1, "Mansession: %p refcount now %d\n", s, refcount - 1);
922 }
923 return NULL;
924}
925
926static void event_filter_destructor(void *obj)
927{
928 struct event_filter_entry *entry = obj;
929 if (entry->regex_filter) {
930 regfree(entry->regex_filter);
931 ast_free(entry->regex_filter);
932 }
933 ast_free(entry->event_name);
934 ast_free(entry->header_name);
935 ast_free(entry->string_filter);
936}
937
938static void session_destructor(void *obj)
939{
940 struct mansession_session *session = obj;
941 struct eventqent *eqe = session->last_ev;
942 struct ast_datastore *datastore;
943
944 /* Get rid of each of the data stores on the session */
945 while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
946 /* Free the data store */
947 ast_datastore_free(datastore);
948 }
949
950 if (eqe) {
952 }
953 if (session->chanvars) {
955 }
956
957 if (session->includefilters) {
958 ao2_t_ref(session->includefilters, -1, "decrement ref for include container, should be last one");
959 }
960
961 if (session->excludefilters) {
962 ao2_t_ref(session->excludefilters, -1, "decrement ref for exclude container, should be last one");
963 }
964
965 ast_mutex_destroy(&session->notify_lock);
966}
967
968/*! \brief Allocate manager session structure and add it to the list of sessions */
970{
971 struct ao2_container *sessions;
972 struct mansession_session *newsession;
973
974 newsession = ao2_alloc(sizeof(*newsession), session_destructor);
975 if (!newsession) {
976 return NULL;
977 }
978
981 if (!newsession->includefilters || !newsession->excludefilters) {
982 ao2_ref(newsession, -1);
983 return NULL;
984 }
985
986 newsession->waiting_thread = AST_PTHREADT_NULL;
987 newsession->writetimeout = 100;
988 newsession->send_events = -1;
989 ast_sockaddr_copy(&newsession->addr, addr);
990
991 ast_mutex_init(&newsession->notify_lock);
992
993 sessions = ao2_global_obj_ref(mgr_sessions);
994 if (sessions) {
995 ao2_link(sessions, newsession);
996 ao2_ref(sessions, -1);
997 }
998
999 return newsession;
1000}
1001
1002static int mansession_cmp_fn(void *obj, void *arg, int flags)
1003{
1004 struct mansession_session *s = obj;
1005 char *str = arg;
1006 return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
1007}
1008
1010{
1011 struct ao2_container *sessions;
1012
1013 sessions = ao2_global_obj_ref(mgr_sessions);
1014 if (sessions) {
1015 ao2_unlink(sessions, s);
1016 ao2_ref(sessions, -1);
1017 }
1019}
1020
1021
1022static int check_manager_session_inuse(const char *name)
1023{
1024 struct ao2_container *sessions;
1026 int inuse = 0;
1027
1028 sessions = ao2_global_obj_ref(mgr_sessions);
1029 if (sessions) {
1030 session = ao2_find(sessions, (char *) name, 0);
1031 ao2_ref(sessions, -1);
1032 if (session) {
1034 inuse = 1;
1035 }
1036 }
1037 return inuse;
1038}
1039
1040
1041/*!
1042 * lookup an entry in the list of registered users.
1043 * must be called with the list lock held.
1044 */
1046{
1047 struct ast_manager_user *user = NULL;
1048
1049 AST_RWLIST_TRAVERSE(&users, user, list) {
1050 if (!strcasecmp(user->username, name)) {
1051 break;
1052 }
1053 }
1054
1055 return user;
1056}
1057
1058/*! \brief Get displayconnects config option.
1059 * \param session manager session to get parameter from.
1060 * \return displayconnects config option value.
1061 */
1063{
1064 struct ast_manager_user *user = NULL;
1065 int ret = 0;
1066
1068 if ((user = get_manager_by_name_locked(session->username))) {
1069 ret = user->displayconnects;
1070 }
1072
1073 return ret;
1074}
1075
1076#ifdef AST_XML_DOCS
1077static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance);
1078#endif
1079
1080static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1081{
1082 struct manager_action *cur;
1083 struct ast_str *authority;
1084 int num;
1085 int l;
1086 const char *auth_str;
1087#ifdef AST_XML_DOCS
1088 char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64];
1089 char arguments_title[64], privilege_title[64], final_response_title[64], list_responses_title[64];
1090#endif
1091
1092 switch (cmd) {
1093 case CLI_INIT:
1094 e->command = "manager show command";
1095 e->usage =
1096 "Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
1097 " Shows the detailed description for a specific Asterisk manager interface command.\n";
1098 return NULL;
1099 case CLI_GENERATE:
1100 l = strlen(a->word);
1102 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1103 if (!strncasecmp(a->word, cur->action, l)) {
1105 break;
1106 }
1107 }
1108 }
1110 return NULL;
1111 }
1112 if (a->argc < 4) {
1113 return CLI_SHOWUSAGE;
1114 }
1115
1117
1118#ifdef AST_XML_DOCS
1119 /* setup the titles */
1120 term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1121 term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
1122 term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1123 term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
1124 term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
1125 term_color(privilege_title, "[Privilege]\n", COLOR_MAGENTA, 0, 40);
1126 term_color(final_response_title, "[Final Response]\n", COLOR_MAGENTA, 0, 40);
1127 term_color(list_responses_title, "[List Responses]\n", COLOR_MAGENTA, 0, 40);
1128#endif
1129
1131 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1132 for (num = 3; num < a->argc; num++) {
1133 if (!strcasecmp(cur->action, a->argv[num])) {
1134 auth_str = authority_to_str(cur->authority, &authority);
1135
1136#ifdef AST_XML_DOCS
1137 if (cur->docsrc == AST_XML_DOC) {
1138 char *syntax = ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1);
1139 char *synopsis = ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1);
1140 char *description = ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1);
1141 char *arguments = ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1);
1142 char *seealso = ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1);
1143 char *privilege = ast_xmldoc_printable(S_OR(auth_str, "Not available"), 1);
1144 char *responses = ast_xmldoc_printable("None", 1);
1145
1146 if (!syntax || !synopsis || !description || !arguments
1147 || !seealso || !privilege || !responses) {
1148 ast_free(syntax);
1150 ast_free(description);
1151 ast_free(arguments);
1152 ast_free(seealso);
1153 ast_free(privilege);
1154 ast_free(responses);
1155 ast_cli(a->fd, "Allocation failure.\n");
1157
1158 return CLI_FAILURE;
1159 }
1160
1161 ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s",
1162 syntax_title, syntax,
1163 synopsis_title, synopsis,
1164 description_title, description,
1165 arguments_title, arguments,
1166 seealso_title, seealso,
1167 privilege_title, privilege,
1168 list_responses_title);
1169
1170 if (!cur->list_responses) {
1171 ast_cli(a->fd, "%s\n\n", responses);
1172 } else {
1173 struct ast_xml_doc_item *temp;
1174 for (temp = cur->list_responses; temp; temp = AST_LIST_NEXT(temp, next)) {
1175 ast_cli(a->fd, "Event: %s\n", temp->name);
1176 print_event_instance(a, temp);
1177 }
1178 }
1179
1180 ast_cli(a->fd, "%s", final_response_title);
1181
1182 if (!cur->final_response) {
1183 ast_cli(a->fd, "%s\n\n", responses);
1184 } else {
1185 ast_cli(a->fd, "Event: %s\n", cur->final_response->name);
1187 }
1188
1194 ast_free(privilege);
1195 ast_free(responses);
1196 } else
1197#endif
1198 {
1199 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
1200 cur->action, cur->synopsis,
1201 auth_str,
1202 S_OR(cur->description, ""));
1203 }
1204 }
1205 }
1206 }
1208
1209 return CLI_SUCCESS;
1210}
1211
1212static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1213{
1214 switch (cmd) {
1215 case CLI_INIT:
1216 e->command = "manager set debug [on|off]";
1217 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
1218 return NULL;
1219 case CLI_GENERATE:
1220 return NULL;
1221 }
1222
1223 if (a->argc == 3) {
1224 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
1225 } else if (a->argc == 4) {
1226 if (!strcasecmp(a->argv[3], "on")) {
1227 manager_debug = 1;
1228 } else if (!strcasecmp(a->argv[3], "off")) {
1229 manager_debug = 0;
1230 } else {
1231 return CLI_SHOWUSAGE;
1232 }
1233 }
1234 return CLI_SUCCESS;
1235}
1236
1237static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1238{
1239 struct ast_manager_user *user = NULL;
1240 int l;
1241 struct ast_str *rauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
1242 struct ast_str *wauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
1243 struct ast_variable *v;
1244
1245 switch (cmd) {
1246 case CLI_INIT:
1247 e->command = "manager show user";
1248 e->usage =
1249 " Usage: manager show user <user>\n"
1250 " Display all information related to the manager user specified.\n";
1251 return NULL;
1252 case CLI_GENERATE:
1253 l = strlen(a->word);
1254 if (a->pos != 3) {
1255 return NULL;
1256 }
1258 AST_RWLIST_TRAVERSE(&users, user, list) {
1259 if (!strncasecmp(a->word, user->username, l)) {
1260 if (ast_cli_completion_add(ast_strdup(user->username))) {
1261 break;
1262 }
1263 }
1264 }
1266 return NULL;
1267 }
1268
1269 if (a->argc != 4) {
1270 return CLI_SHOWUSAGE;
1271 }
1272
1274
1275 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
1276 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
1278 return CLI_SUCCESS;
1279 }
1280
1281 ast_cli(a->fd, "\n");
1282 ast_cli(a->fd,
1283 " username: %s\n"
1284 " secret: %s\n"
1285 " ACL: %s\n"
1286 " read perm: %s\n"
1287 " write perm: %s\n"
1288 " displayconnects: %s\n"
1289 "allowmultiplelogin: %s\n",
1290 S_OR(user->username, "(N/A)"),
1291 (user->secret ? "<Set>" : "(N/A)"),
1292 ((user->acl && !ast_acl_list_is_empty(user->acl)) ? "yes" : "no"),
1293 user_authority_to_str(user->readperm, &rauthority),
1294 user_authority_to_str(user->writeperm, &wauthority),
1295 (user->displayconnects ? "yes" : "no"),
1296 (user->allowmultiplelogin ? "yes" : "no"));
1297 ast_cli(a->fd, " Variables: \n");
1298 for (v = user->chanvars ; v ; v = v->next) {
1299 ast_cli(a->fd, " %s = %s\n", v->name, v->value);
1300 }
1301 if (!ast_acl_list_is_empty(user->acl)) {
1302 ast_acl_output(a->fd, user->acl, NULL);
1303 }
1304
1306
1307 return CLI_SUCCESS;
1308}
1309
1310static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1311{
1312 struct ast_manager_user *user = NULL;
1313 int count_amu = 0;
1314 switch (cmd) {
1315 case CLI_INIT:
1316 e->command = "manager show users";
1317 e->usage =
1318 "Usage: manager show users\n"
1319 " Prints a listing of all managers that are currently configured on that\n"
1320 " system.\n";
1321 return NULL;
1322 case CLI_GENERATE:
1323 return NULL;
1324 }
1325 if (a->argc != 3) {
1326 return CLI_SHOWUSAGE;
1327 }
1328
1330
1331 /* If there are no users, print out something along those lines */
1332 if (AST_RWLIST_EMPTY(&users)) {
1333 ast_cli(a->fd, "There are no manager users.\n");
1335 return CLI_SUCCESS;
1336 }
1337
1338 ast_cli(a->fd, "\nusername\n--------\n");
1339
1340 AST_RWLIST_TRAVERSE(&users, user, list) {
1341 ast_cli(a->fd, "%s\n", user->username);
1342 count_amu++;
1343 }
1344
1346
1347 ast_cli(a->fd,"-------------------\n"
1348 "%d manager users configured.\n", count_amu);
1349 return CLI_SUCCESS;
1350}
1351
1352/*! \brief CLI command manager list commands */
1353static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1354{
1355 struct manager_action *cur;
1356 int name_len = 1;
1357 int space_remaining;
1358#define HSMC_FORMAT " %-*.*s %-.*s\n"
1359 switch (cmd) {
1360 case CLI_INIT:
1361 e->command = "manager show commands";
1362 e->usage =
1363 "Usage: manager show commands\n"
1364 " Prints a listing of all the available Asterisk manager interface commands.\n";
1365 return NULL;
1366 case CLI_GENERATE:
1367 return NULL;
1368 }
1369
1371 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1372 int incoming_len = strlen(cur->action);
1373 if (incoming_len > name_len) {
1374 name_len = incoming_len;
1375 }
1376 }
1377
1378 space_remaining = MGR_SHOW_TERMINAL_WIDTH - name_len - 4;
1379 if (space_remaining < 0) {
1380 space_remaining = 0;
1381 }
1382
1383 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "Action", space_remaining, "Synopsis");
1384 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "------", space_remaining, "--------");
1385
1386 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1387 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, cur->action, space_remaining, cur->synopsis);
1388 }
1390
1391 return CLI_SUCCESS;
1392}
1393
1394/*! \brief CLI command manager kick session */
1395static char *handle_kickmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1396{
1397 struct ao2_container *sessions;
1399 struct ao2_iterator i;
1400 int fd = -1;
1401 int found = 0;
1402
1403 switch (cmd) {
1404 case CLI_INIT:
1405 e->command = "manager kick session";
1406 e->usage =
1407 "Usage: manager kick session <file descriptor>\n"
1408 " Kick an active Asterisk Manager Interface session\n";
1409 return NULL;
1410 case CLI_GENERATE:
1411 return NULL;
1412 }
1413
1414 if (a->argc != 4) {
1415 return CLI_SHOWUSAGE;
1416 }
1417
1418 fd = atoi(a->argv[3]);
1419 if (fd <= 0) { /* STDOUT won't be a valid AMI fd either */
1420 ast_cli(a->fd, "Invalid AMI file descriptor: %s\n", a->argv[3]);
1421 return CLI_FAILURE;
1422 }
1423
1424 sessions = ao2_global_obj_ref(mgr_sessions);
1425 if (sessions) {
1427 ao2_ref(sessions, -1);
1428 while ((session = ao2_iterator_next(&i))) {
1430 if (session->stream) {
1431 if (ast_iostream_get_fd(session->stream) == fd) {
1432 if (session->kicked) {
1433 ast_cli(a->fd, "Manager session using file descriptor %d has already been kicked\n", fd);
1436 break;
1437 }
1438 fd = ast_iostream_get_fd(session->stream);
1439 found = fd;
1440 ast_cli(a->fd, "Kicking manager session connected using file descriptor %d\n", fd);
1441 ast_mutex_lock(&session->notify_lock);
1442 session->kicked = 1;
1443 if (session->waiting_thread != AST_PTHREADT_NULL) {
1444 pthread_kill(session->waiting_thread, SIGURG);
1445 }
1446 ast_mutex_unlock(&session->notify_lock);
1449 break;
1450 }
1451 }
1454 }
1456 }
1457
1458 if (!found) {
1459 ast_cli(a->fd, "No manager session found using file descriptor %d\n", fd);
1460 }
1461 return CLI_SUCCESS;
1462}
1463
1464/*! \brief CLI command manager list connected */
1465static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1466{
1467 struct ao2_container *sessions;
1469 time_t now = time(NULL);
1470#define HSMCONN_FORMAT1 " %-15.15s %-55.55s %-10.10s %-10.10s %-8.8s %-8.8s %-10.10s %-10.10s\n"
1471#define HSMCONN_FORMAT2 " %-15.15s %-55.55s %-10d %-10d %-8d %-8d %-10.10d %-10.10d\n"
1472 int count = 0;
1473 struct ao2_iterator i;
1474
1475 switch (cmd) {
1476 case CLI_INIT:
1477 e->command = "manager show connected";
1478 e->usage =
1479 "Usage: manager show connected\n"
1480 " Prints a listing of the users that are currently connected to the\n"
1481 "Asterisk manager interface.\n";
1482 return NULL;
1483 case CLI_GENERATE:
1484 return NULL;
1485 }
1486
1487 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "ReadPerms", "WritePerms");
1488
1489 sessions = ao2_global_obj_ref(mgr_sessions);
1490 if (sessions) {
1492 ao2_ref(sessions, -1);
1493 while ((session = ao2_iterator_next(&i))) {
1495 ast_cli(a->fd, HSMCONN_FORMAT2, session->username,
1497 (int) (session->sessionstart),
1498 (int) (now - session->sessionstart),
1499 session->stream ? ast_iostream_get_fd(session->stream) : -1,
1500 session->inuse,
1501 session->readperm,
1502 session->writeperm);
1503 count++;
1506 }
1508 }
1509 ast_cli(a->fd, "%d users connected.\n", count);
1510
1511 return CLI_SUCCESS;
1512}
1513
1514/*! \brief CLI command manager list eventq */
1515/* Should change to "manager show connected" */
1516static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1517{
1518 struct eventqent *s;
1519 switch (cmd) {
1520 case CLI_INIT:
1521 e->command = "manager show eventq";
1522 e->usage =
1523 "Usage: manager show eventq\n"
1524 " Prints a listing of all events pending in the Asterisk manger\n"
1525 "event queue.\n";
1526 return NULL;
1527 case CLI_GENERATE:
1528 return NULL;
1529 }
1531 AST_RWLIST_TRAVERSE(&all_events, s, eq_next) {
1532 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
1533 ast_cli(a->fd, "Category: %d\n", s->category);
1534 ast_cli(a->fd, "Event:\n%s", s->eventdata);
1535 }
1537
1538 return CLI_SUCCESS;
1539}
1540
1541static int reload_module(void);
1542
1543/*! \brief CLI command manager reload */
1544static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1545{
1546 switch (cmd) {
1547 case CLI_INIT:
1548 e->command = "manager reload";
1549 e->usage =
1550 "Usage: manager reload\n"
1551 " Reloads the manager configuration.\n";
1552 return NULL;
1553 case CLI_GENERATE:
1554 return NULL;
1555 }
1556 if (a->argc > 2) {
1557 return CLI_SHOWUSAGE;
1558 }
1559 reload_module();
1560 return CLI_SUCCESS;
1561}
1562
1563static struct eventqent *advance_event(struct eventqent *e)
1564{
1565 struct eventqent *next;
1566
1568 if ((next = AST_RWLIST_NEXT(e, eq_next))) {
1571 }
1573 return next;
1574}
1575
1576#define GET_HEADER_FIRST_MATCH 0
1577#define GET_HEADER_LAST_MATCH 1
1578#define GET_HEADER_SKIP_EMPTY 2
1579
1580/*!
1581 * \brief Return a matching header value.
1582 *
1583 * \details
1584 * Generic function to return either the first or the last
1585 * matching header from a list of variables, possibly skipping
1586 * empty strings.
1587 *
1588 * \note At the moment there is only one use of this function in
1589 * this file, so we make it static.
1590 *
1591 * \note Never returns NULL.
1592 */
1593static const char *__astman_get_header(const struct message *m, char *var, int mode)
1594{
1595 int x, l = strlen(var);
1596 const char *result = "";
1597
1598 if (!m) {
1599 return result;
1600 }
1601
1602 for (x = 0; x < m->hdrcount; x++) {
1603 const char *h = m->headers[x];
1604 if (!strncasecmp(var, h, l) && h[l] == ':') {
1605 const char *value = h + l + 1;
1606 value = ast_skip_blanks(value); /* ignore leading spaces in the value */
1607 /* found a potential candidate */
1608 if ((mode & GET_HEADER_SKIP_EMPTY) && ast_strlen_zero(value)) {
1609 continue; /* not interesting */
1610 }
1611 if (mode & GET_HEADER_LAST_MATCH) {
1612 result = value; /* record the last match so far */
1613 } else {
1614 return value;
1615 }
1616 }
1617 }
1618
1619 return result;
1620}
1621
1622/*!
1623 * \brief Return the first matching variable from an array.
1624 *
1625 * \note This is the legacy function and is implemented in
1626 * therms of __astman_get_header().
1627 *
1628 * \note Never returns NULL.
1629 */
1630const char *astman_get_header(const struct message *m, char *var)
1631{
1633}
1634
1635/*!
1636 * \brief Append additional headers into the message structure from params.
1637 *
1638 * \note You likely want to initialize m->hdrcount to 0 before calling this.
1639 */
1640static void astman_append_headers(struct message *m, const struct ast_variable *params)
1641{
1642 const struct ast_variable *v;
1643
1644 for (v = params; v && m->hdrcount < ARRAY_LEN(m->headers); v = v->next) {
1645 if (ast_asprintf((char**)&m->headers[m->hdrcount], "%s: %s", v->name, v->value) > -1) {
1646 ++m->hdrcount;
1647 }
1648 }
1649}
1650
1651/*!
1652 * \brief Free headers inside message structure, but not the message structure itself.
1653 */
1654static void astman_free_headers(struct message *m)
1655{
1656 while (m->hdrcount) {
1657 --m->hdrcount;
1658 ast_free((void *) m->headers[m->hdrcount]);
1659 m->headers[m->hdrcount] = NULL;
1660 }
1661}
1662
1663/*!
1664 * \internal
1665 * \brief Process one "Variable:" header value string.
1666 *
1667 * \param head Current list of AMI variables to get new values added.
1668 * \param hdr_val Header value string to process.
1669 *
1670 * \return New variable list head.
1671 */
1672static struct ast_variable *man_do_variable_value(struct ast_variable *head, const char *hdr_val)
1673{
1674 char *parse;
1676 AST_APP_ARG(vars)[64];
1677 );
1678
1679 hdr_val = ast_skip_blanks(hdr_val); /* ignore leading spaces in the value */
1680 parse = ast_strdupa(hdr_val);
1681
1682 /* Break the header value string into name=val pair items. */
1684 if (args.argc) {
1685 int y;
1686
1687 /* Process each name=val pair item. */
1688 for (y = 0; y < args.argc; y++) {
1689 struct ast_variable *cur;
1690 char *var;
1691 char *val;
1692
1693 if (!args.vars[y]) {
1694 continue;
1695 }
1696 var = val = args.vars[y];
1697 strsep(&val, "=");
1698
1699 /* XXX We may wish to trim whitespace from the strings. */
1700 if (!val || ast_strlen_zero(var)) {
1701 continue;
1702 }
1703
1704 /* Create new variable list node and prepend it to the list. */
1705 cur = ast_variable_new(var, val, "");
1706 if (cur) {
1707 cur->next = head;
1708 head = cur;
1709 }
1710 }
1711 }
1712
1713 return head;
1714}
1715
1717{
1719}
1720
1723{
1724 int varlen;
1725 int x;
1726 struct ast_variable *head = NULL;
1727
1728 static const char var_hdr[] = "Variable:";
1729
1730 /* Process all "Variable:" headers. */
1731 varlen = strlen(var_hdr);
1732 for (x = 0; x < m->hdrcount; x++) {
1733 if (strncasecmp(var_hdr, m->headers[x], varlen)) {
1734 continue;
1735 }
1736 head = man_do_variable_value(head, m->headers[x] + varlen);
1737 }
1738
1739 if (order == ORDER_NATURAL) {
1740 head = ast_variables_reverse(head);
1741 }
1742
1743 return head;
1744}
1745
1746/*! \brief access for hooks to send action messages to ami */
1747int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
1748{
1749 const char *action;
1750 int ret = 0;
1751 struct manager_action *act_found;
1752 struct mansession s = {.session = NULL, };
1753 struct message m = { 0 };
1754 char *dup_str;
1755 char *src;
1756 int x = 0;
1757 int curlen;
1758
1759 if (hook == NULL) {
1760 return -1;
1761 }
1762
1763 /* Create our own copy of the AMI action msg string. */
1764 src = dup_str = ast_strdup(msg);
1765 if (!dup_str) {
1766 return -1;
1767 }
1768
1769 /* convert msg string to message struct */
1770 curlen = strlen(src);
1771 for (x = 0; x < curlen; x++) {
1772 int cr; /* set if we have \r */
1773 if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n')
1774 cr = 2; /* Found. Update length to include \r\n */
1775 else if (src[x] == '\n')
1776 cr = 1; /* also accept \n only */
1777 else
1778 continue;
1779 /* don't keep empty lines */
1780 if (x && m.hdrcount < ARRAY_LEN(m.headers)) {
1781 /* ... but trim \r\n and terminate the header string */
1782 src[x] = '\0';
1783 m.headers[m.hdrcount++] = src;
1784 }
1785 x += cr;
1786 curlen -= x; /* remaining size */
1787 src += x; /* update pointer */
1788 x = -1; /* reset loop */
1789 }
1790
1791 action = astman_get_header(&m, "Action");
1792
1793 do {
1794 if (!strcasecmp(action, "login")) {
1795 break;
1796 }
1797
1798 act_found = action_find(action);
1799 if (!act_found) {
1800 break;
1801 }
1802
1803 /*
1804 * we have to simulate a session for this action request
1805 * to be able to pass it down for processing
1806 * This is necessary to meet the previous design of manager.c
1807 */
1808 s.hook = hook;
1809
1810 ret = -1;
1811 ao2_lock(act_found);
1812 if (act_found->registered && act_found->func) {
1813 struct ast_module *mod_ref = ast_module_running_ref(act_found->module);
1814
1815 ao2_unlock(act_found);
1816 /* If the action is in a module it must be running. */
1817 if (!act_found->module || mod_ref) {
1818 ret = act_found->func(&s, &m);
1819 ast_module_unref(mod_ref);
1820 }
1821 } else {
1822 ao2_unlock(act_found);
1823 }
1824 ao2_t_ref(act_found, -1, "done with found action object");
1825 } while (0);
1826
1827 ast_free(dup_str);
1828 return ret;
1829}
1830
1831/*!
1832 * helper function to send a string to the socket.
1833 * Return -1 on error (e.g. buffer full).
1834 */
1835static int send_string(struct mansession *s, char *string)
1836{
1837 struct ast_iostream *stream;
1838 int len, res;
1839
1840 /* It's a result from one of the hook's action invocation */
1841 if (s->hook) {
1842 /*
1843 * to send responses, we're using the same function
1844 * as for receiving events. We call the event "HookResponse"
1845 */
1846 s->hook->helper(EVENT_FLAG_HOOKRESPONSE, "HookResponse", string);
1847 return 0;
1848 }
1849
1850 stream = s->stream ? s->stream : s->session->stream;
1851
1852 len = strlen(string);
1854 res = ast_iostream_write(stream, string, len);
1856
1857 if (res < len) {
1858 s->write_error = 1;
1859 }
1860
1861 return res;
1862}
1863
1864/*!
1865 * \brief thread local buffer for astman_append
1866 *
1867 * \note This can not be defined within the astman_append() function
1868 * because it declares a couple of functions that get used to
1869 * initialize the thread local storage key.
1870 */
1872
1874
1875/*! \brief initial allocated size for the astman_append_buf and astman_send_*_va */
1876#define ASTMAN_APPEND_BUF_INITSIZE 256
1877
1878static void astman_flush(struct mansession *s, struct ast_str *buf)
1879{
1880 if (s->hook || (s->tcptls_session && s->tcptls_session->stream)) {
1882 } else {
1883 ast_verbose("No connection stream in astman_append, should not happen\n");
1884 }
1885}
1886
1887/*!
1888 * utility functions for creating AMI replies
1889 */
1890void astman_append(struct mansession *s, const char *fmt, ...)
1891{
1892 int res;
1893 va_list ap;
1894 struct ast_str *buf;
1895
1897 return;
1898 }
1899
1900 va_start(ap, fmt);
1901 res = ast_str_set_va(&buf, 0, fmt, ap);
1902 va_end(ap);
1903 if (res == AST_DYNSTR_BUILD_FAILED) {
1904 return;
1905 }
1906
1907 if (s->hook || (s->tcptls_session != NULL && s->tcptls_session->stream != NULL)) {
1909 } else {
1910 ast_verbose("No connection stream in astman_append, should not happen\n");
1911 }
1912}
1913
1914/*! \note NOTE: XXX this comment is unclear and possibly wrong.
1915 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
1916 hold the session lock _or_ be running in an action callback (in which case s->session->busy will
1917 be non-zero). In either of these cases, there is no need to lock-protect the session's
1918 fd, since no other output will be sent (events will be queued), and no input will
1919 be read until either the current action finishes or get_input() obtains the session
1920 lock.
1921 */
1922
1923/*! \todo XXX MSG_MOREDATA should go to a header file. */
1924#define MSG_MOREDATA ((char *)astman_send_response)
1925
1926/*! \brief send a response with an optional message,
1927 * and terminate it with an empty line.
1928 * m is used only to grab the 'ActionID' field.
1929 *
1930 * Use the explicit constant MSG_MOREDATA to remove the empty line.
1931 * XXX MSG_MOREDATA should go to a header file.
1932 */
1933static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
1934{
1935 const char *id = astman_get_header(m, "ActionID");
1936 struct ast_str *buf;
1937
1939 if (!buf) {
1940 return;
1941 }
1942
1943 ast_str_set(&buf, 0, "Response: %s\r\n", resp);
1944
1945 if (!ast_strlen_zero(id)) {
1946 ast_str_append(&buf, 0, "ActionID: %s\r\n", id);
1947 }
1948
1949 if (listflag) {
1950 /* Start, complete, cancelled */
1951 ast_str_append(&buf, 0, "EventList: %s\r\n", listflag);
1952 }
1953
1954 if (msg != MSG_MOREDATA) {
1955 if (msg) {
1956 ast_str_append(&buf, 0, "Message: %s\r\n", msg);
1957 }
1958 ast_str_append(&buf, 0, "\r\n");
1959 }
1960
1961 astman_flush(s, buf);
1962}
1963
1964void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
1965{
1966 astman_send_response_full(s, m, resp, msg, NULL);
1967}
1968
1969void astman_send_error(struct mansession *s, const struct message *m, char *error)
1970{
1971 astman_send_response_full(s, m, "Error", error, NULL);
1972}
1973
1974void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt, ...)
1975{
1976 int res;
1977 va_list ap;
1978 struct ast_str *buf;
1979 char *msg;
1980
1982 return;
1983 }
1984
1985 va_start(ap, fmt);
1986 res = ast_str_set_va(&buf, 0, fmt, ap);
1987 va_end(ap);
1988 if (res == AST_DYNSTR_BUILD_FAILED) {
1989 return;
1990 }
1991
1992 /* astman_append will use the same underlying buffer, so copy the message out
1993 * before sending the response */
1994 msg = ast_str_buffer(buf);
1995 if (msg) {
1996 msg = ast_strdupa(msg);
1997 }
1998 astman_send_response_full(s, m, "Error", msg, NULL);
1999}
2000
2001void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
2002{
2003 astman_send_response_full(s, m, "Success", msg, NULL);
2004}
2005
2006static void astman_start_ack(struct mansession *s, const struct message *m)
2007{
2008 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
2009}
2010
2011void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
2012{
2013 astman_send_response_full(s, m, "Success", msg, listflag);
2014}
2015
2016static struct ast_str *astman_send_list_complete_start_common(struct mansession *s, const struct message *m, const char *event_name, int count)
2017{
2018 const char *id = astman_get_header(m, "ActionID");
2019 struct ast_str *buf;
2020
2022 if (!buf) {
2023 return NULL;
2024 }
2025
2026 ast_str_set(&buf, 0, "Event: %s\r\n", event_name);
2027 if (!ast_strlen_zero(id)) {
2028 ast_str_append(&buf, 0, "ActionID: %s\r\n", id);
2029 }
2030 ast_str_append(&buf, 0,
2031 "EventList: Complete\r\n"
2032 "ListItems: %d\r\n",
2033 count);
2034
2035 return buf;
2036}
2037
2038static void astman_send_list_complete(struct mansession *s, const struct message *m, const char *event_name, int count)
2039{
2040 struct ast_str *buf = astman_send_list_complete_start_common(s, m, event_name, count);
2041 if (buf) {
2042 ast_str_append(&buf, 0, "\r\n");
2043 astman_flush(s, buf);
2044 }
2045}
2046
2047void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
2048{
2049 struct ast_str *buf = astman_send_list_complete_start_common(s, m, event_name, count);
2050 if (buf) {
2051 astman_flush(s, buf);
2052 }
2053}
2054
2056{
2057 astman_append(s, "\r\n");
2058}
2059
2060/*! \brief Lock the 'mansession' structure. */
2061static void mansession_lock(struct mansession *s)
2062{
2063 ast_mutex_lock(&s->lock);
2064}
2065
2066/*! \brief Unlock the 'mansession' structure. */
2067static void mansession_unlock(struct mansession *s)
2068{
2070}
2071
2072/*! \brief
2073 Rather than braindead on,off this now can also accept a specific int mask value
2074 or a ',' delim list of mask strings (the same as manager.conf) -anthm
2075*/
2076static int set_eventmask(struct mansession *s, const char *eventmask)
2077{
2078 int maskint = strings_to_mask(eventmask);
2079
2080 ao2_lock(s->session);
2081 if (maskint >= 0) {
2082 s->session->send_events = maskint;
2083 }
2084 ao2_unlock(s->session);
2085
2086 return maskint;
2087}
2088
2090{
2093}
2094
2095static void report_invalid_user(const struct mansession *s, const char *username)
2096{
2097 char session_id[32];
2098 struct ast_security_event_inval_acct_id inval_acct_id = {
2101 .common.service = "AMI",
2102 .common.account_id = username,
2103 .common.session_tv = &s->session->sessionstart_tv,
2104 .common.local_addr = {
2105 .addr = &s->tcptls_session->parent->local_address,
2106 .transport = mansession_get_transport(s),
2107 },
2108 .common.remote_addr = {
2109 .addr = &s->session->addr,
2110 .transport = mansession_get_transport(s),
2111 },
2112 .common.session_id = session_id,
2113 };
2114
2115 snprintf(session_id, sizeof(session_id), "%p", s);
2116
2117 ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
2118}
2119
2120static void report_failed_acl(const struct mansession *s, const char *username)
2121{
2122 char session_id[32];
2123 struct ast_security_event_failed_acl failed_acl_event = {
2125 .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
2126 .common.service = "AMI",
2127 .common.account_id = username,
2128 .common.session_tv = &s->session->sessionstart_tv,
2129 .common.local_addr = {
2130 .addr = &s->tcptls_session->parent->local_address,
2131 .transport = mansession_get_transport(s),
2132 },
2133 .common.remote_addr = {
2134 .addr = &s->session->addr,
2135 .transport = mansession_get_transport(s),
2136 },
2137 .common.session_id = session_id,
2138 };
2139
2140 snprintf(session_id, sizeof(session_id), "%p", s->session);
2141
2142 ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
2143}
2144
2145static void report_inval_password(const struct mansession *s, const char *username)
2146{
2147 char session_id[32];
2148 struct ast_security_event_inval_password inval_password = {
2151 .common.service = "AMI",
2152 .common.account_id = username,
2153 .common.session_tv = &s->session->sessionstart_tv,
2154 .common.local_addr = {
2155 .addr = &s->tcptls_session->parent->local_address,
2156 .transport = mansession_get_transport(s),
2157 },
2158 .common.remote_addr = {
2159 .addr = &s->session->addr,
2160 .transport = mansession_get_transport(s),
2161 },
2162 .common.session_id = session_id,
2163 };
2164
2165 snprintf(session_id, sizeof(session_id), "%p", s->session);
2166
2167 ast_security_event_report(AST_SEC_EVT(&inval_password));
2168}
2169
2170static void report_auth_success(const struct mansession *s)
2171{
2172 char session_id[32];
2173 struct ast_security_event_successful_auth successful_auth = {
2176 .common.service = "AMI",
2177 .common.account_id = s->session->username,
2178 .common.session_tv = &s->session->sessionstart_tv,
2179 .common.local_addr = {
2180 .addr = &s->tcptls_session->parent->local_address,
2181 .transport = mansession_get_transport(s),
2182 },
2183 .common.remote_addr = {
2184 .addr = &s->session->addr,
2185 .transport = mansession_get_transport(s),
2186 },
2187 .common.session_id = session_id,
2188 };
2189
2190 snprintf(session_id, sizeof(session_id), "%p", s->session);
2191
2192 ast_security_event_report(AST_SEC_EVT(&successful_auth));
2193}
2194
2195static void report_req_not_allowed(const struct mansession *s, const char *action)
2196{
2197 char session_id[32];
2198 char request_type[64];
2199 struct ast_security_event_req_not_allowed req_not_allowed = {
2202 .common.service = "AMI",
2203 .common.account_id = s->session->username,
2204 .common.session_tv = &s->session->sessionstart_tv,
2205 .common.local_addr = {
2206 .addr = &s->tcptls_session->parent->local_address,
2207 .transport = mansession_get_transport(s),
2208 },
2209 .common.remote_addr = {
2210 .addr = &s->session->addr,
2211 .transport = mansession_get_transport(s),
2212 },
2213 .common.session_id = session_id,
2214
2215 .request_type = request_type,
2216 };
2217
2218 snprintf(session_id, sizeof(session_id), "%p", s->session);
2219 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2220
2221 ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
2222}
2223
2224static void report_req_bad_format(const struct mansession *s, const char *action)
2225{
2226 char session_id[32];
2227 char request_type[64];
2228 struct ast_security_event_req_bad_format req_bad_format = {
2231 .common.service = "AMI",
2232 .common.account_id = s->session->username,
2233 .common.session_tv = &s->session->sessionstart_tv,
2234 .common.local_addr = {
2235 .addr = &s->tcptls_session->parent->local_address,
2236 .transport = mansession_get_transport(s),
2237 },
2238 .common.remote_addr = {
2239 .addr = &s->session->addr,
2240 .transport = mansession_get_transport(s),
2241 },
2242 .common.session_id = session_id,
2243
2244 .request_type = request_type,
2245 };
2246
2247 snprintf(session_id, sizeof(session_id), "%p", s->session);
2248 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2249
2250 ast_security_event_report(AST_SEC_EVT(&req_bad_format));
2251}
2252
2254 const char *response, const char *expected_response)
2255{
2256 char session_id[32];
2257 struct ast_security_event_chal_resp_failed chal_resp_failed = {
2260 .common.service = "AMI",
2261 .common.account_id = s->session->username,
2262 .common.session_tv = &s->session->sessionstart_tv,
2263 .common.local_addr = {
2264 .addr = &s->tcptls_session->parent->local_address,
2265 .transport = mansession_get_transport(s),
2266 },
2267 .common.remote_addr = {
2268 .addr = &s->session->addr,
2269 .transport = mansession_get_transport(s),
2270 },
2271 .common.session_id = session_id,
2272
2273 .challenge = s->session->challenge,
2274 .response = response,
2275 .expected_response = expected_response,
2276 };
2277
2278 snprintf(session_id, sizeof(session_id), "%p", s->session);
2279
2280 ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
2281}
2282
2283static void report_session_limit(const struct mansession *s)
2284{
2285 char session_id[32];
2287 .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
2289 .common.service = "AMI",
2290 .common.account_id = s->session->username,
2291 .common.session_tv = &s->session->sessionstart_tv,
2292 .common.local_addr = {
2293 .addr = &s->tcptls_session->parent->local_address,
2294 .transport = mansession_get_transport(s),
2295 },
2296 .common.remote_addr = {
2297 .addr = &s->session->addr,
2298 .transport = mansession_get_transport(s),
2299 },
2300 .common.session_id = session_id,
2301 };
2302
2303 snprintf(session_id, sizeof(session_id), "%p", s->session);
2304
2306}
2307
2308/*
2309 * Here we start with action_ handlers for AMI actions,
2310 * and the internal functions used by them.
2311 * Generally, the handlers are called action_foo()
2312 */
2313
2314/* helper function for action_login() */
2315static int authenticate(struct mansession *s, const struct message *m)
2316{
2317 const char *username = astman_get_header(m, "Username");
2318 const char *password = astman_get_header(m, "Secret");
2319 int error = -1;
2320 struct ast_manager_user *user = NULL;
2321 regex_t *regex_filter;
2322 struct ao2_iterator filter_iter;
2323
2324 if (ast_strlen_zero(username)) { /* missing username */
2325 return -1;
2326 }
2327
2328 /* locate user in locked state */
2330
2331 if (!(user = get_manager_by_name_locked(username))) {
2332 report_invalid_user(s, username);
2333 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2334 } else if (user->acl && (ast_apply_acl(user->acl, &s->session->addr, "Manager User ACL: ") == AST_SENSE_DENY)) {
2335 report_failed_acl(s, username);
2336 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2337 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
2338 const char *key = astman_get_header(m, "Key");
2339 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
2340 int x;
2341 int len = 0;
2342 char md5key[256] = "";
2343 struct MD5Context md5;
2344 unsigned char digest[16];
2345
2346 MD5Init(&md5);
2347 MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
2348 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
2349 MD5Final(digest, &md5);
2350 for (x = 0; x < 16; x++)
2351 len += sprintf(md5key + len, "%02hhx", digest[x]);
2352 if (!strcmp(md5key, key)) {
2353 error = 0;
2354 } else {
2355 report_failed_challenge_response(s, key, md5key);
2356 }
2357 } else {
2358 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
2359 S_OR(s->session->challenge, ""));
2360 }
2361 } else if (user->secret) {
2362 if (!strcmp(password, user->secret)) {
2363 error = 0;
2364 } else {
2365 report_inval_password(s, username);
2366 }
2367 }
2368
2369 if (error) {
2370 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2372 return -1;
2373 }
2374
2375 /* auth complete */
2376
2377 /* All of the user parameters are copied to the session so that in the event
2378 * of a reload and a configuration change, the session parameters are not
2379 * changed. */
2380 ast_copy_string(s->session->username, username, sizeof(s->session->username));
2381 s->session->readperm = user->readperm;
2382 s->session->writeperm = user->writeperm;
2383 s->session->writetimeout = user->writetimeout;
2384 if (user->chanvars) {
2385 s->session->chanvars = ast_variables_dup(user->chanvars);
2386 }
2387
2388 filter_iter = ao2_iterator_init(user->includefilters, 0);
2389 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
2390 ao2_t_link(s->session->includefilters, regex_filter, "add include user filter to session");
2391 ao2_t_ref(regex_filter, -1, "remove iterator ref");
2392 }
2393 ao2_iterator_destroy(&filter_iter);
2394
2395 filter_iter = ao2_iterator_init(user->excludefilters, 0);
2396 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
2397 ao2_t_link(s->session->excludefilters, regex_filter, "add exclude user filter to session");
2398 ao2_t_ref(regex_filter, -1, "remove iterator ref");
2399 }
2400 ao2_iterator_destroy(&filter_iter);
2401
2402 s->session->sessionstart = time(NULL);
2404 set_eventmask(s, astman_get_header(m, "Events"));
2405
2407
2409 return 0;
2410}
2411
2412static int action_ping(struct mansession *s, const struct message *m)
2413{
2414 const char *actionid = astman_get_header(m, "ActionID");
2415 struct timeval now = ast_tvnow();
2416
2417 astman_append(s, "Response: Success\r\n");
2418 if (!ast_strlen_zero(actionid)){
2419 astman_append(s, "ActionID: %s\r\n", actionid);
2420 }
2422 s,
2423 "Ping: Pong\r\n"
2424 "Timestamp: %ld.%06lu\r\n"
2425 "\r\n",
2426 (long) now.tv_sec, (unsigned long) now.tv_usec);
2427 return 0;
2428}
2429
2430void astman_live_dangerously(int new_live_dangerously)
2431{
2432 if (new_live_dangerously && !live_dangerously)
2433 {
2434 ast_log(LOG_WARNING, "Manager Configuration load protection disabled.\n");
2435 }
2436
2437 if (!new_live_dangerously && live_dangerously)
2438 {
2439 ast_log(LOG_NOTICE, "Manager Configuration load protection enabled.\n");
2440 }
2441 live_dangerously = new_live_dangerously;
2442}
2443
2444/**
2445 * \brief Check if a file is restricted or not
2446 *
2447 * \return 0 on success
2448 * \return 1 on restricted file
2449 * \return -1 on failure
2450 */
2451static int restrictedFile(const char *filename)
2452{
2453 char *stripped_filename;
2454 RAII_VAR(char *, path, NULL, ast_free);
2455 RAII_VAR(char *, real_path, NULL, ast_std_free);
2456
2457 if (live_dangerously) {
2458 return 0;
2459 }
2460
2461 stripped_filename = ast_strip(ast_strdupa(filename));
2462
2463 /* If the file path starts with '/', don't prepend ast_config_AST_CONFIG_DIR */
2464 if (stripped_filename[0] == '/') {
2465 real_path = realpath(stripped_filename, NULL);
2466 } else {
2467 if (ast_asprintf(&path, "%s/%s", ast_config_AST_CONFIG_DIR, stripped_filename) == -1) {
2468 return -1;
2469 }
2470 real_path = realpath(path, NULL);
2471 }
2472
2473 if (!real_path) {
2474 return -1;
2475 }
2476
2477 if (!ast_begins_with(real_path, ast_config_AST_CONFIG_DIR)) {
2478 return 1;
2479 }
2480
2481 return 0;
2482}
2483
2484static int action_getconfig(struct mansession *s, const struct message *m)
2485{
2486 struct ast_config *cfg;
2487 const char *fn = astman_get_header(m, "Filename");
2488 const char *category = astman_get_header(m, "Category");
2489 const char *filter = astman_get_header(m, "Filter");
2490 const char *category_name;
2491 int catcount = 0;
2492 int lineno = 0;
2493 int ret = 0;
2494 struct ast_category *cur_category = NULL;
2495 struct ast_variable *v;
2496 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2497
2498 if (ast_strlen_zero(fn)) {
2499 astman_send_error(s, m, "Filename not specified");
2500 return 0;
2501 }
2502
2503 ret = restrictedFile(fn);
2504 if (ret == 1) {
2505 astman_send_error(s, m, "File requires escalated priveledges");
2506 return 0;
2507 } else if (ret == -1) {
2508 astman_send_error(s, m, "Config file not found");
2509 return 0;
2510 }
2511
2512 cfg = ast_config_load2(fn, "manager", config_flags);
2513 if (cfg == CONFIG_STATUS_FILEMISSING) {
2514 astman_send_error(s, m, "Config file not found");
2515 return 0;
2516 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2517 astman_send_error(s, m, "Config file has invalid format");
2518 return 0;
2519 }
2520
2521 astman_start_ack(s, m);
2522 while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
2523 struct ast_str *templates;
2524
2525 category_name = ast_category_get_name(cur_category);
2526 lineno = 0;
2527 astman_append(s, "Category-%06d: %s\r\n", catcount, category_name);
2528
2529 if (ast_category_is_template(cur_category)) {
2530 astman_append(s, "IsTemplate-%06d: %d\r\n", catcount, 1);
2531 }
2532
2533 if ((templates = ast_category_get_templates(cur_category))
2534 && ast_str_strlen(templates) > 0) {
2535 astman_append(s, "Templates-%06d: %s\r\n", catcount, ast_str_buffer(templates));
2537 }
2538
2539 for (v = ast_category_first(cur_category); v; v = v->next) {
2540 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
2541 }
2542
2543 catcount++;
2544 }
2545
2546 if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2547 astman_append(s, "No categories found\r\n");
2548 }
2549
2550 ast_config_destroy(cfg);
2551 astman_append(s, "\r\n");
2552
2553 return 0;
2554}
2555
2556static int action_listcategories(struct mansession *s, const struct message *m)
2557{
2558 struct ast_config *cfg;
2559 const char *fn = astman_get_header(m, "Filename");
2560 const char *match = astman_get_header(m, "Match");
2561 struct ast_category *category = NULL;
2562 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2563 int catcount = 0;
2564
2565 if (ast_strlen_zero(fn)) {
2566 astman_send_error(s, m, "Filename not specified");
2567 return 0;
2568 }
2569
2570 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2571 astman_send_error(s, m, "Config file not found");
2572 return 0;
2573 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2574 astman_send_error(s, m, "Config file has invalid format");
2575 return 0;
2576 }
2577
2578 astman_start_ack(s, m);
2579 while ((category = ast_category_browse_filtered(cfg, NULL, category, match))) {
2580 astman_append(s, "Category-%06d: %s\r\n", catcount, ast_category_get_name(category));
2581 catcount++;
2582 }
2583
2584 if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2585 astman_append(s, "Error: no categories found\r\n");
2586 }
2587
2588 ast_config_destroy(cfg);
2589 astman_append(s, "\r\n");
2590
2591 return 0;
2592}
2593
2594/*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
2595static void json_escape(char *out, const char *in)
2596{
2597 for (; *in; in++) {
2598 if (*in == '\\' || *in == '\"') {
2599 *out++ = '\\';
2600 }
2601 *out++ = *in;
2602 }
2603 *out = '\0';
2604}
2605
2606/*!
2607 * \internal
2608 * \brief Append a JSON escaped string to the manager stream.
2609 *
2610 * \param s AMI stream to append a string.
2611 * \param str String to append to the stream after JSON escaping it.
2612 */
2613static void astman_append_json(struct mansession *s, const char *str)
2614{
2615 char *buf;
2616
2617 buf = ast_alloca(2 * strlen(str) + 1);
2619 astman_append(s, "%s", buf);
2620}
2621
2622static int action_getconfigjson(struct mansession *s, const struct message *m)
2623{
2624 struct ast_config *cfg;
2625 const char *fn = astman_get_header(m, "Filename");
2626 const char *filter = astman_get_header(m, "Filter");
2627 const char *category = astman_get_header(m, "Category");
2628 struct ast_category *cur_category = NULL;
2629 const char *category_name;
2630 struct ast_variable *v;
2631 int comma1 = 0;
2632 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2633
2634 if (ast_strlen_zero(fn)) {
2635 astman_send_error(s, m, "Filename not specified");
2636 return 0;
2637 }
2638
2639 if (restrictedFile(fn)) {
2640 astman_send_error(s, m, "File requires escalated priveledges");
2641 return 0;
2642 }
2643
2644 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2645 astman_send_error(s, m, "Config file not found");
2646 return 0;
2647 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2648 astman_send_error(s, m, "Config file has invalid format");
2649 return 0;
2650 }
2651
2652 astman_start_ack(s, m);
2653 astman_append(s, "JSON: {");
2654 while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
2655 int comma2 = 0;
2656 struct ast_str *templates;
2657
2658 category_name = ast_category_get_name(cur_category);
2659 astman_append(s, "%s\"", comma1 ? "," : "");
2660 astman_append_json(s, category_name);
2661 astman_append(s, "\":{");
2662 comma1 = 1;
2663
2664 if (ast_category_is_template(cur_category)) {
2665 astman_append(s, "\"istemplate\":1");
2666 comma2 = 1;
2667 }
2668
2669 if ((templates = ast_category_get_templates(cur_category))
2670 && ast_str_strlen(templates) > 0) {
2671 astman_append(s, "%s", comma2 ? "," : "");
2672 astman_append(s, "\"templates\":\"%s\"", ast_str_buffer(templates));
2674 comma2 = 1;
2675 }
2676
2677 for (v = ast_category_first(cur_category); v; v = v->next) {
2678 astman_append(s, "%s\"", comma2 ? "," : "");
2679 astman_append_json(s, v->name);
2680 astman_append(s, "\":\"");
2682 astman_append(s, "\"");
2683 comma2 = 1;
2684 }
2685
2686 astman_append(s, "}");
2687 }
2688 astman_append(s, "}\r\n\r\n");
2689
2690 ast_config_destroy(cfg);
2691
2692 return 0;
2693}
2694
2695/*! \brief helper function for action_updateconfig */
2696static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
2697{
2698 int x;
2699 char hdr[40];
2700 const char *action, *cat, *var, *value, *match, *line, *options;
2701 struct ast_variable *v;
2702 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
2703 enum error_type result = 0;
2704
2705 for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
2706 unsigned int object = 0;
2707 char *dupoptions;
2708 int allowdups = 0;
2709 int istemplate = 0;
2710 int ignoreerror = 0;
2711 RAII_VAR(char *, inherit, NULL, ast_free);
2712 RAII_VAR(char *, catfilter, NULL, ast_free);
2713 char *token;
2714 int foundvar = 0;
2715 int foundcat = 0;
2716 struct ast_category *category = NULL;
2717
2718 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
2719 action = astman_get_header(m, hdr);
2720 if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
2721 break; /* this could cause problems if actions come in misnumbered */
2722
2723 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
2724 cat = astman_get_header(m, hdr);
2725 if (ast_strlen_zero(cat)) { /* every action needs a category */
2727 break;
2728 }
2729
2730 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
2731 var = astman_get_header(m, hdr);
2732
2733 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
2734 value = astman_get_header(m, hdr);
2735
2736 if (!ast_strlen_zero(value) && *value == '>') {
2737 object = 1;
2738 value++;
2739 }
2740
2741 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
2742 match = astman_get_header(m, hdr);
2743
2744 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
2745 line = astman_get_header(m, hdr);
2746
2747 snprintf(hdr, sizeof(hdr), "Options-%06d", x);
2748 options = astman_get_header(m, hdr);
2749 if (!ast_strlen_zero(options)) {
2750 char copy[strlen(options) + 1];
2751 strcpy(copy, options); /* safe */
2752 dupoptions = copy;
2753 while ((token = ast_strsep(&dupoptions, ',', AST_STRSEP_STRIP))) {
2754 if (!strcasecmp("allowdups", token)) {
2755 allowdups = 1;
2756 continue;
2757 }
2758 if (!strcasecmp("template", token)) {
2759 istemplate = 1;
2760 continue;
2761 }
2762 if (!strcasecmp("ignoreerror", token)) {
2763 ignoreerror = 1;
2764 continue;
2765 }
2766 if (ast_begins_with(token, "inherit")) {
2767 char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2768 c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2769 if (c) {
2770 inherit = ast_strdup(c);
2771 }
2772 continue;
2773 }
2774 if (ast_begins_with(token, "catfilter")) {
2775 char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2776 c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
2777 if (c) {
2778 catfilter = ast_strdup(c);
2779 }
2780 continue;
2781 }
2782 }
2783 }
2784
2785 if (!strcasecmp(action, "newcat")) {
2786 struct ast_category *template;
2787 char *tmpl_name = NULL;
2788
2789 if (!allowdups) {
2790 if (ast_category_get(cfg, cat, "TEMPLATES=include")) {
2791 if (ignoreerror) {
2792 continue;
2793 } else {
2794 result = FAILURE_NEWCAT; /* already exist */
2795 break;
2796 }
2797 }
2798 }
2799
2800 if (istemplate) {
2801 category = ast_category_new_template(cat, dfn, -1);
2802 } else {
2803 category = ast_category_new(cat, dfn, -1);
2804 }
2805
2806 if (!category) {
2808 break;
2809 }
2810
2811 if (inherit) {
2812 while ((tmpl_name = ast_strsep(&inherit, ',', AST_STRSEP_STRIP))) {
2813 if ((template = ast_category_get(cfg, tmpl_name, "TEMPLATES=restrict"))) {
2814 if (ast_category_inherit(category, template)) {
2816 break;
2817 }
2818 } else {
2819 ast_category_destroy(category);
2820 category = NULL;
2821 result = FAILURE_TEMPLATE; /* template not found */
2822 break;
2823 }
2824 }
2825 }
2826
2827 if (category != NULL) {
2828 if (ast_strlen_zero(match)) {
2829 ast_category_append(cfg, category);
2830 } else {
2831 if (ast_category_insert(cfg, category, match)) {
2832 ast_category_destroy(category);
2834 break;
2835 }
2836 }
2837 }
2838 } else if (!strcasecmp(action, "renamecat")) {
2839 if (ast_strlen_zero(value)) {
2841 break;
2842 }
2843
2844 foundcat = 0;
2845 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2846 ast_category_rename(category, value);
2847 foundcat = 1;
2848 }
2849
2850 if (!foundcat) {
2852 break;
2853 }
2854 } else if (!strcasecmp(action, "delcat")) {
2855 foundcat = 0;
2856 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2857 category = ast_category_delete(cfg, category);
2858 foundcat = 1;
2859 }
2860
2861 if (!foundcat && !ignoreerror) {
2863 break;
2864 }
2865 } else if (!strcasecmp(action, "emptycat")) {
2866 foundcat = 0;
2867 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2868 ast_category_empty(category);
2869 foundcat = 1;
2870 }
2871
2872 if (!foundcat) {
2874 break;
2875 }
2876 } else if (!strcasecmp(action, "update")) {
2877 if (ast_strlen_zero(var)) {
2879 break;
2880 }
2881
2882 foundcat = 0;
2883 foundvar = 0;
2884 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2885 if (!ast_variable_update(category, var, value, match, object)) {
2886 foundvar = 1;
2887 }
2888 foundcat = 1;
2889 }
2890
2891 if (!foundcat) {
2893 break;
2894 }
2895
2896 if (!foundvar) {
2898 break;
2899 }
2900 } else if (!strcasecmp(action, "delete")) {
2901 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
2903 break;
2904 }
2905
2906 foundcat = 0;
2907 foundvar = 0;
2908 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2909 if (!ast_variable_delete(category, var, match, line)) {
2910 foundvar = 1;
2911 }
2912 foundcat = 1;
2913 }
2914
2915 if (!foundcat) {
2917 break;
2918 }
2919
2920 if (!foundvar && !ignoreerror) {
2922 break;
2923 }
2924 } else if (!strcasecmp(action, "append")) {
2925 if (ast_strlen_zero(var)) {
2927 break;
2928 }
2929
2930 foundcat = 0;
2931 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2932 if (!(v = ast_variable_new(var, value, dfn))) {
2934 break;
2935 }
2936 if (object || (match && !strcasecmp(match, "object"))) {
2937 v->object = 1;
2938 }
2939 ast_variable_append(category, v);
2940 foundcat = 1;
2941 }
2942
2943 if (!foundcat) {
2945 break;
2946 }
2947 } else if (!strcasecmp(action, "insert")) {
2948 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
2950 break;
2951 }
2952
2953 foundcat = 0;
2954 while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
2955 if (!(v = ast_variable_new(var, value, dfn))) {
2957 break;
2958 }
2959 ast_variable_insert(category, v, line);
2960 foundcat = 1;
2961 }
2962
2963 if (!foundcat) {
2965 break;
2966 }
2967 }
2968 else {
2969 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
2971 break;
2972 }
2973 }
2974 ast_free(str1);
2975 ast_free(str2);
2976 return result;
2977}
2978
2979static int action_updateconfig(struct mansession *s, const struct message *m)
2980{
2981 struct ast_config *cfg;
2982 const char *sfn = astman_get_header(m, "SrcFilename");
2983 const char *dfn = astman_get_header(m, "DstFilename");
2984 int res;
2985 const char *rld = astman_get_header(m, "Reload");
2986 int preserve_effective_context = CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT;
2987 const char *preserve_effective_context_string = astman_get_header(m, "PreserveEffectiveContext");
2988 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2989 enum error_type result;
2990
2991 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
2992 astman_send_error(s, m, "Filename not specified");
2993 return 0;
2994 }
2995 if (restrictedFile(sfn) || restrictedFile(dfn)) {
2996 astman_send_error(s, m, "File requires escalated priveledges");
2997 return 0;
2998 }
2999 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
3000 astman_send_error(s, m, "Config file not found");
3001 return 0;
3002 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
3003 astman_send_error(s, m, "Config file has invalid format");
3004 return 0;
3005 }
3006 result = handle_updates(s, m, cfg, dfn);
3007 if (!result) {
3008 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
3009 if (!ast_strlen_zero(preserve_effective_context_string) && !ast_true(preserve_effective_context_string)) {
3010 preserve_effective_context = CONFIG_SAVE_FLAG_NONE;
3011 }
3012 res = ast_config_text_file_save2(dfn, cfg, "Manager", preserve_effective_context);
3013 ast_config_destroy(cfg);
3014 if (res) {
3015 astman_send_error(s, m, "Save of config failed");
3016 return 0;
3017 }
3018 astman_send_ack(s, m, NULL);
3019 if (!ast_strlen_zero(rld)) {
3020 if (ast_true(rld)) {
3021 ast_module_reload(NULL); /* Reload everything */
3022 } else if (!ast_false(rld)) {
3023 ast_module_reload(rld); /* Reload the specific module */
3024 }
3025 }
3026 } else {
3027 ast_config_destroy(cfg);
3028 switch(result) {
3029 case UNKNOWN_ACTION:
3030 astman_send_error(s, m, "Unknown action command");
3031 break;
3032 case UNKNOWN_CATEGORY:
3033 astman_send_error(s, m, "Given category does not exist");
3034 break;
3036 astman_send_error(s, m, "Category not specified");
3037 break;
3039 astman_send_error(s, m, "Problem with category, value, or line (if required)");
3040 break;
3041 case FAILURE_ALLOCATION:
3042 astman_send_error(s, m, "Memory allocation failure, this should not happen");
3043 break;
3044 case FAILURE_NEWCAT:
3045 astman_send_error(s, m, "Create category did not complete successfully");
3046 break;
3047 case FAILURE_DELCAT:
3048 astman_send_error(s, m, "Delete category did not complete successfully");
3049 break;
3050 case FAILURE_EMPTYCAT:
3051 astman_send_error(s, m, "Empty category did not complete successfully");
3052 break;
3053 case FAILURE_UPDATE:
3054 astman_send_error(s, m, "Update did not complete successfully");
3055 break;
3056 case FAILURE_DELETE:
3057 astman_send_error(s, m, "Delete did not complete successfully");
3058 break;
3059 case FAILURE_APPEND:
3060 astman_send_error(s, m, "Append did not complete successfully");
3061 break;
3062 case FAILURE_TEMPLATE:
3063 astman_send_error(s, m, "Template category not found");
3064 break;
3065 }
3066 }
3067 return 0;
3068}
3069
3070static int action_createconfig(struct mansession *s, const struct message *m)
3071{
3072 int fd;
3073 const char *fn = astman_get_header(m, "Filename");
3074 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
3075 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
3076 ast_str_append(&filepath, 0, "%s", fn);
3077
3078 if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
3079 close(fd);
3080 astman_send_ack(s, m, "New configuration file created successfully");
3081 } else {
3082 astman_send_error(s, m, strerror(errno));
3083 }
3084
3085 return 0;
3086}
3087
3088static int action_waitevent(struct mansession *s, const struct message *m)
3089{
3090 const char *timeouts = astman_get_header(m, "Timeout");
3091 int timeout = -1;
3092 int x;
3093 int needexit = 0;
3094 const char *id = astman_get_header(m, "ActionID");
3095 char idText[256];
3096
3097 if (!ast_strlen_zero(id)) {
3098 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
3099 } else {
3100 idText[0] = '\0';
3101 }
3102
3103 if (!ast_strlen_zero(timeouts)) {
3104 sscanf(timeouts, "%30i", &timeout);
3105 if (timeout < -1) {
3106 timeout = -1;
3107 }
3108 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
3109 }
3110
3113 pthread_kill(s->session->waiting_thread, SIGURG);
3114 }
3116
3117 ao2_lock(s->session);
3118
3119 if (s->session->managerid) { /* AMI-over-HTTP session */
3120 /*
3121 * Make sure the timeout is within the expire time of the session,
3122 * as the client will likely abort the request if it does not see
3123 * data coming after some amount of time.
3124 */
3125 time_t now = time(NULL);
3126 int max = s->session->sessiontimeout - now - 10;
3127
3128 if (max < 0) { /* We are already late. Strange but possible. */
3129 max = 0;
3130 }
3131 if (timeout < 0 || timeout > max) {
3132 timeout = max;
3133 }
3134 if (!s->session->send_events) { /* make sure we record events */
3135 s->session->send_events = -1;
3136 }
3137 }
3138 ao2_unlock(s->session);
3139
3141 s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
3143 ast_debug(1, "Starting waiting for an event!\n");
3144
3145 for (x = 0; x < timeout || timeout < 0; x++) {
3146 ao2_lock(s->session);
3147 if (AST_RWLIST_NEXT(s->session->last_ev, eq_next)) {
3148 needexit = 1;
3149 }
3150 if (s->session->needdestroy) {
3151 needexit = 1;
3152 }
3153 ao2_unlock(s->session);
3154 /* We can have multiple HTTP session point to the same mansession entry.
3155 * The way we deal with it is not very nice: newcomers kick out the previous
3156 * HTTP session. XXX this needs to be improved.
3157 */
3159 if (s->session->waiting_thread != pthread_self()) {
3160 needexit = 1;
3161 }
3163 if (needexit) {
3164 break;
3165 }
3166 if (s->session->managerid == 0) { /* AMI session */
3168 break;
3169 }
3170 } else { /* HTTP session */
3171 sleep(1);
3172 }
3173 }
3174 ast_debug(1, "Finished waiting for an event!\n");
3175
3177 if (s->session->waiting_thread == pthread_self()) {
3178 struct eventqent *eqe = s->session->last_ev;
3179
3182
3183 ao2_lock(s->session);
3184 astman_send_response(s, m, "Success", "Waiting for Event completed.");
3185 while ((eqe = advance_event(eqe))) {
3186 if (((s->session->readperm & eqe->category) == eqe->category)
3187 && ((s->session->send_events & eqe->category) == eqe->category)
3189 astman_append(s, "%s", eqe->eventdata);
3190 }
3191 s->session->last_ev = eqe;
3192 }
3193 astman_append(s,
3194 "Event: WaitEventComplete\r\n"
3195 "%s"
3196 "\r\n", idText);
3197 ao2_unlock(s->session);
3198 } else {
3200 ast_debug(1, "Abandoning event request!\n");
3201 }
3202
3203 return 0;
3204}
3205
3206static int action_listcommands(struct mansession *s, const struct message *m)
3207{
3208 struct manager_action *cur;
3210
3211 astman_start_ack(s, m);
3213 AST_RWLIST_TRAVERSE(&actions, cur, list) {
3214 if ((s->session->writeperm & cur->authority) || cur->authority == 0) {
3215 astman_append(s, "%s: %s (Priv: %s)\r\n",
3216 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
3217 }
3218 }
3220 astman_append(s, "\r\n");
3221
3222 return 0;
3223}
3224
3225static int action_events(struct mansession *s, const struct message *m)
3226{
3227 const char *mask = astman_get_header(m, "EventMask");
3228 int res, x;
3229 const char *id = astman_get_header(m, "ActionID");
3230 char id_text[256];
3231
3232 if (!ast_strlen_zero(id)) {
3233 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
3234 } else {
3235 id_text[0] = '\0';
3236 }
3237
3238 res = set_eventmask(s, mask);
3240 /* if this option is set we should not return a response on
3241 * error, or when all events are set */
3242
3243 if (res > 0) {
3244 for (x = 0; x < ARRAY_LEN(perms); x++) {
3245 if (!strcasecmp(perms[x].label, "all") && res == perms[x].num) {
3246 return 0;
3247 }
3248 }
3249 astman_append(s, "Response: Success\r\n%s"
3250 "Events: On\r\n\r\n", id_text);
3251 } else if (res == 0)
3252 astman_append(s, "Response: Success\r\n%s"
3253 "Events: Off\r\n\r\n", id_text);
3254 return 0;
3255 }
3256
3257 if (res > 0)
3258 astman_append(s, "Response: Success\r\n%s"
3259 "Events: On\r\n\r\n", id_text);
3260 else if (res == 0)
3261 astman_append(s, "Response: Success\r\n%s"
3262 "Events: Off\r\n\r\n", id_text);
3263 else
3264 astman_send_error(s, m, "Invalid event mask");
3265
3266 return 0;
3267}
3268
3269static int action_logoff(struct mansession *s, const struct message *m)
3270{
3271 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
3272 return -1;
3273}
3274
3275static int action_login(struct mansession *s, const struct message *m)
3276{
3277
3278 /* still authenticated - don't process again */
3279 if (s->session->authenticated) {
3280 astman_send_ack(s, m, "Already authenticated");
3281 return 0;
3282 }
3283
3284 if (authenticate(s, m)) {
3285 sleep(1);
3286 astman_send_error(s, m, "Authentication failed");
3287 return -1;
3288 }
3289 s->session->authenticated = 1;
3292 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_sockaddr_stringify_addr(&s->session->addr));
3293 }
3294 astman_send_ack(s, m, "Authentication accepted");
3299 const char *cat_str = authority_to_str(EVENT_FLAG_SYSTEM, &auth);
3300 long uptime = 0;
3301 long lastreloaded = 0;
3302 struct timeval tmp;
3303 struct timeval curtime = ast_tvnow();
3304
3305 if (ast_startuptime.tv_sec) {
3306 tmp = ast_tvsub(curtime, ast_startuptime);
3307 uptime = tmp.tv_sec;
3308 }
3309
3310 if (ast_lastreloadtime.tv_sec) {
3311 tmp = ast_tvsub(curtime, ast_lastreloadtime);
3312 lastreloaded = tmp.tv_sec;
3313 }
3314
3315 astman_append(s, "Event: FullyBooted\r\n"
3316 "Privilege: %s\r\n"
3317 "Uptime: %ld\r\n"
3318 "LastReload: %ld\r\n"
3319 "Status: Fully Booted\r\n\r\n", cat_str, uptime, lastreloaded);
3320 }
3321 return 0;
3322}
3323
3324static int action_challenge(struct mansession *s, const struct message *m)
3325{
3326 const char *authtype = astman_get_header(m, "AuthType");
3327
3328 if (!strcasecmp(authtype, "MD5")) {
3330 snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
3331 }
3332 mansession_lock(s);
3333 astman_start_ack(s, m);
3334 astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
3336 } else {
3337 astman_send_error(s, m, "Must specify AuthType");
3338 }
3339 return 0;
3340}
3341
3343 const struct message *m, manager_hangup_handler_t hangup_handler,
3344 manager_hangup_cause_validator_t cause_validator)
3345{
3346 struct ast_channel *c = NULL;
3347 int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
3348 const char *id = astman_get_header(m, "ActionID");
3349 const char *name_or_regex = astman_get_header(m, "Channel");
3350 const char *cause = astman_get_header(m, "Cause");
3351 char idText[256];
3352 regex_t regexbuf;
3353 struct ast_channel_iterator *iter = NULL;
3354 struct ast_str *regex_string;
3355 int channels_matched = 0;
3356
3357 if (ast_strlen_zero(name_or_regex)) {
3358 astman_send_error(s, m, "No channel specified");
3359 return 0;
3360 }
3361
3362 if (!ast_strlen_zero(id)) {
3363 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
3364 } else {
3365 idText[0] = '\0';
3366 }
3367
3368 if (cause_validator) {
3369 causecode = cause_validator(name_or_regex, cause);
3370 } else if (!ast_strlen_zero(cause)) {
3371 char *endptr;
3372 causecode = strtol(cause, &endptr, 10);
3373 if (causecode < 0 || causecode > 127 || *endptr != '\0') {
3374 ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause);
3375 /* keep going, better to hangup without cause than to not hang up at all */
3376 causecode = 0; /* do not set channel's hangupcause */
3377 }
3378 }
3379
3380 /************************************************/
3381 /* Regular explicit match channel byname hangup */
3382
3383 if (name_or_regex[0] != '/') {
3384 if (!(c = ast_channel_get_by_name(name_or_regex))) {
3385 ast_log(LOG_NOTICE, "Request to hangup non-existent channel: %s\n",
3386 name_or_regex);
3387 astman_send_error(s, m, "No such channel");
3388 return 0;
3389 }
3390
3391 ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
3392 (s->session->managerid ? "HTTP " : ""),
3393 s->session->username,
3396
3397 hangup_handler(c, causecode);
3399
3400 astman_send_ack(s, m, "Channel Hungup");
3401
3402 return 0;
3403 }
3404
3405 /***********************************************/
3406 /* find and hangup any channels matching regex */
3407
3408 regex_string = ast_str_create(strlen(name_or_regex));
3409 if (!regex_string) {
3410 astman_send_error(s, m, "Memory Allocation Failure");
3411 return 0;
3412 }
3413
3414 /* Make "/regex/" into "regex" */
3415 if (ast_regex_string_to_regex_pattern(name_or_regex, &regex_string) != 0) {
3416 astman_send_error(s, m, "Regex format invalid, Channel param should be /regex/");
3417 ast_free(regex_string);
3418 return 0;
3419 }
3420
3421 /* if regex compilation fails, hangup fails */
3422 if (regcomp(&regexbuf, ast_str_buffer(regex_string), REG_EXTENDED | REG_NOSUB)) {
3423 astman_send_error_va(s, m, "Regex compile failed on: %s", name_or_regex);
3424 ast_free(regex_string);
3425 return 0;
3426 }
3427
3428 astman_send_listack(s, m, "Channels hung up will follow", "start");
3429
3431 if (iter) {
3432 for (; (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
3433 if (regexec(&regexbuf, ast_channel_name(c), 0, NULL, 0)) {
3434 continue;
3435 }
3436
3437 ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
3438 (s->session->managerid ? "HTTP " : ""),
3439 s->session->username,
3442
3443 hangup_handler(c, causecode);
3444 channels_matched++;
3445
3446 astman_append(s,
3447 "Event: ChannelHungup\r\n"
3448 "Channel: %s\r\n"
3449 "%s"
3450 "\r\n", ast_channel_name(c), idText);
3451 }
3453 }
3454
3455 regfree(&regexbuf);
3456 ast_free(regex_string);
3457
3458 astman_send_list_complete(s, m, "ChannelsHungupListComplete", channels_matched);
3459
3460 return 0;
3461}
3462
3463static int action_hangup(struct mansession *s, const struct message *m)
3464{
3465 return ast_manager_hangup_helper(s, m,
3467}
3468
3469static int action_setvar(struct mansession *s, const struct message *m)
3470{
3471 struct ast_channel *c = NULL;
3472 const char *name = astman_get_header(m, "Channel");
3473 const char *varname = astman_get_header(m, "Variable");
3474 const char *varval = astman_get_header(m, "Value");
3475 int res = 0;
3476
3477 if (ast_strlen_zero(varname)) {
3478 astman_send_error(s, m, "No variable specified");
3479 return 0;
3480 }
3481
3482 if (!ast_strlen_zero(name)) {
3483 if (!(c = ast_channel_get_by_name(name))) {
3484 astman_send_error(s, m, "No such channel");
3485 return 0;
3486 }
3487 }
3488
3489 res = pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
3490
3491 if (c) {
3493 }
3494 if (res == 0) {
3495 astman_send_ack(s, m, "Variable Set");
3496 } else {
3497 astman_send_error(s, m, "Variable not set");
3498 }
3499 return 0;
3500}
3501
3502static int action_getvar(struct mansession *s, const struct message *m)
3503{
3504 struct ast_channel *c = NULL;
3505 const char *name = astman_get_header(m, "Channel");
3506 const char *varname = astman_get_header(m, "Variable");
3507 char *varval;
3508 char workspace[1024];
3509
3510 if (ast_strlen_zero(varname)) {
3511 astman_send_error(s, m, "No variable specified");
3512 return 0;
3513 }
3514
3515 /* We don't want users with insufficient permissions using certain functions. */
3517 astman_send_error(s, m, "GetVar Access Forbidden: Variable");
3518 return 0;
3519 }
3520
3521 if (!ast_strlen_zero(name)) {
3522 if (!(c = ast_channel_get_by_name(name))) {
3523 astman_send_error(s, m, "No such channel");
3524 return 0;
3525 }
3526 }
3527
3528 workspace[0] = '\0';
3529 if (varname[strlen(varname) - 1] == ')') {
3530 if (!c) {
3532 if (c) {
3533 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
3534 } else
3535 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
3536 } else {
3537 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
3538 }
3539 varval = workspace;
3540 } else {
3541 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
3542 }
3543
3544 if (c) {
3546 }
3547
3548 astman_start_ack(s, m);
3549 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, S_OR(varval, ""));
3550
3551 return 0;
3552}
3553
3554static void generate_status(struct mansession *s, struct ast_channel *chan, char **vars, int varc, int all_variables, char *id_text, int *count)
3555{
3556 struct timeval now;
3557 long elapsed_seconds;
3558 struct ast_bridge *bridge;
3559 RAII_VAR(struct ast_str *, variable_str, NULL, ast_free);
3560 struct ast_str *write_transpath = ast_str_alloca(256);
3561 struct ast_str *read_transpath = ast_str_alloca(256);
3562 struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
3563 struct ast_party_id effective_id;
3564 int i;
3565 RAII_VAR(struct ast_channel_snapshot *, snapshot,
3567 ao2_cleanup);
3568 RAII_VAR(struct ast_str *, snapshot_str, NULL, ast_free);
3569
3570 if (!snapshot) {
3571 return;
3572 }
3573
3574 snapshot_str = ast_manager_build_channel_state_string(snapshot);
3575 if (!snapshot_str) {
3576 return;
3577 }
3578
3579 if (all_variables) {
3580 variable_str = ast_str_create(2048);
3581 } else {
3582 variable_str = ast_str_create(1024);
3583 }
3584 if (!variable_str) {
3585 return;
3586 }
3587
3588 now = ast_tvnow();
3589 elapsed_seconds = ast_tvdiff_sec(now, ast_channel_creationtime(chan));
3590
3591 /* Even if all_variables has been specified, explicitly requested variables
3592 * may be global variables or dialplan functions */
3593 for (i = 0; i < varc; i++) {
3594 char valbuf[512], *ret = NULL;
3595
3596 if (vars[i][strlen(vars[i]) - 1] == ')') {
3597 if (ast_func_read(chan, vars[i], valbuf, sizeof(valbuf)) < 0) {
3598 valbuf[0] = '\0';
3599 }
3600 ret = valbuf;
3601 } else {
3602 pbx_retrieve_variable(chan, vars[i], &ret, valbuf, sizeof(valbuf), NULL);
3603 }
3604
3605 ast_str_append(&variable_str, 0, "Variable: %s=%s\r\n", vars[i], ret);
3606 }
3607
3608 /* Walk all channel variables and add them */
3609 if (all_variables) {
3610 struct ast_var_t *variables;
3611
3612 AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
3613 ast_str_append(&variable_str, 0, "Variable: %s=%s\r\n",
3614 ast_var_name(variables), ast_var_value(variables));
3615 }
3616 }
3617
3618 bridge = ast_channel_get_bridge(chan);
3619 effective_id = ast_channel_connected_effective_id(chan);
3620
3621 astman_append(s,
3622 "Event: Status\r\n"
3623 "Privilege: Call\r\n"
3624 "%s"
3625 "Type: %s\r\n"
3626 "DNID: %s\r\n"
3627 "EffectiveConnectedLineNum: %s\r\n"
3628 "EffectiveConnectedLineName: %s\r\n"
3629 "TimeToHangup: %ld\r\n"
3630 "BridgeID: %s\r\n"
3631 "Application: %s\r\n"
3632 "Data: %s\r\n"
3633 "Nativeformats: %s\r\n"
3634 "Readformat: %s\r\n"
3635 "Readtrans: %s\r\n"
3636 "Writeformat: %s\r\n"
3637 "Writetrans: %s\r\n"
3638 "Callgroup: %llu\r\n"
3639 "Pickupgroup: %llu\r\n"
3640 "Seconds: %ld\r\n"
3641 "%s"
3642 "%s"
3643 "\r\n",
3644 ast_str_buffer(snapshot_str),
3645 ast_channel_tech(chan)->type,
3646 S_OR(ast_channel_dialed(chan)->number.str, ""),
3647 S_COR(effective_id.number.valid, effective_id.number.str, "<unknown>"),
3648 S_COR(effective_id.name.valid, effective_id.name.str, "<unknown>"),
3649 (long)ast_channel_whentohangup(chan)->tv_sec,
3650 bridge ? bridge->uniqueid : "",
3651 ast_channel_appl(chan),
3652 ast_channel_data(chan),
3655 ast_translate_path_to_str(ast_channel_readtrans(chan), &read_transpath),
3657 ast_translate_path_to_str(ast_channel_writetrans(chan), &write_transpath),
3660 (long)elapsed_seconds,
3661 ast_str_buffer(variable_str),
3662 id_text);
3663 ++*count;
3664
3665 ao2_cleanup(bridge);
3666}
3667
3668/*! \brief Manager "status" command to show channels */
3669static int action_status(struct mansession *s, const struct message *m)
3670{
3671 const char *name = astman_get_header(m, "Channel");
3672 const char *chan_variables = astman_get_header(m, "Variables");
3673 const char *all_chan_variables = astman_get_header(m, "AllVariables");
3674 int all_variables = 0;
3675 const char *id = astman_get_header(m, "ActionID");
3676 char *variables = ast_strdupa(S_OR(chan_variables, ""));
3677 struct ast_channel *chan;
3678 int channels = 0;
3679 int all = ast_strlen_zero(name); /* set if we want all channels */
3680 char id_text[256];
3681 struct ast_channel_iterator *it_chans = NULL;
3683 AST_APP_ARG(name)[100];
3684 );
3685
3686 if (!ast_strlen_zero(all_chan_variables)) {
3687 all_variables = ast_true(all_chan_variables);
3688 }
3689
3691 astman_send_error(s, m, "Status Access Forbidden: Variables");
3692 return 0;
3693 }
3694
3695 if (all) {
3696 if (!(it_chans = ast_channel_iterator_all_new())) {
3697 astman_send_error(s, m, "Memory Allocation Failure");
3698 return 1;
3699 }
3700 chan = ast_channel_iterator_next(it_chans);
3701 } else {
3703 if (!chan) {
3704 astman_send_error(s, m, "No such channel");
3705 return 0;
3706 }
3707 }
3708
3709 astman_send_listack(s, m, "Channel status will follow", "start");
3710
3711 if (!ast_strlen_zero(id)) {
3712 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
3713 } else {
3714 id_text[0] = '\0';
3715 }
3716
3717 if (!ast_strlen_zero(chan_variables)) {
3718 AST_STANDARD_APP_ARGS(vars, variables);
3719 }
3720
3721 /* if we look by name, we break after the first iteration */
3722 for (; chan; all ? chan = ast_channel_iterator_next(it_chans) : 0) {
3723 ast_channel_lock(chan);
3724
3725 generate_status(s, chan, vars.name, vars.argc, all_variables, id_text, &channels);
3726
3727 ast_channel_unlock(chan);
3728 chan = ast_channel_unref(chan);
3729 }
3730
3731 if (it_chans) {
3733 }
3734
3735 astman_send_list_complete_start(s, m, "StatusComplete", channels);
3736 astman_append(s, "Items: %d\r\n", channels);
3738
3739 return 0;
3740}
3741
3742/*!
3743 * \brief Queue a given read action containing a payload onto a channel
3744 *
3745 * This queues a READ_ACTION control frame that contains a given "payload", or
3746 * data to be triggered and handled on the channel's read side. This ensures
3747 * the "action" is handled by the channel's media reading thread.
3748 *
3749 * \param chan The channel to queue the action on
3750 * \param payload The read action's payload
3751 * \param payload_size The size of the given payload
3752 * \param action The type of read action to queue
3753 *
3754 * \retval -1 on error
3755 * \retval 0 on success
3756 */
3757static int queue_read_action_payload(struct ast_channel *chan, const unsigned char *payload,
3758 size_t payload_size, enum ast_frame_read_action action)
3759{
3761 size_t obj_size;
3762 int res;
3763
3764 obj_size = payload_size + sizeof(*obj);
3765
3766 obj = ast_malloc(obj_size);
3767 if (!obj) {
3768 return -1;
3769 }
3770
3771 obj->action = action;
3772 obj->payload_size = payload_size;
3773 memcpy(obj->payload, payload, payload_size);
3774
3775 res = ast_queue_control_data(chan, AST_CONTROL_READ_ACTION, obj, obj_size);
3776
3777 ast_free(obj);
3778 return res;
3779}
3780
3781/*!
3782 * \brief Queue a read action to send a text message
3783 *
3784 * \param chan The channel to queue the action on
3785 * \param body The body of the message
3786 *
3787 * \retval -1 on error
3788 * \retval 0 on success
3789 */
3790static int queue_sendtext(struct ast_channel *chan, const char *body)
3791{
3792 return queue_read_action_payload(chan, (const unsigned char *)body,
3793 strlen(body) + 1, AST_FRAME_READ_ACTION_SEND_TEXT);
3794}
3795
3796/*!
3797 * \brief Queue a read action to send a text data message
3798 *
3799 * \param chan The channel to queue the action on
3800 * \param body The body of the message
3801 * \param content_type The message's content type
3802 *
3803 * \retval -1 on error
3804 * \retval 0 on success
3805 */
3806static int queue_sendtext_data(struct ast_channel *chan, const char *body,
3807 const char *content_type)
3808{
3809 int res;
3810 struct ast_msg_data *obj;
3811
3813 NULL, NULL, content_type, body);
3814 if (!obj) {
3815 return -1;
3816 }
3817
3818 res = queue_read_action_payload(chan, (const unsigned char *)obj,
3820
3821 ast_free(obj);
3822 return res;
3823}
3824
3825static int action_sendtext(struct mansession *s, const struct message *m)
3826{
3827 struct ast_channel *c;
3828 const char *name = astman_get_header(m, "Channel");
3829 const char *textmsg = astman_get_header(m, "Message");
3830 const char *content_type = astman_get_header(m, "Content-Type");
3831 int res;
3832
3833 if (ast_strlen_zero(name)) {
3834 astman_send_error(s, m, "No channel specified");
3835 return 0;
3836 }
3837
3838 if (ast_strlen_zero(textmsg)) {
3839 astman_send_error(s, m, "No Message specified");
3840 return 0;
3841 }
3842
3844 if (!c) {
3845 astman_send_error(s, m, "No such channel");
3846 return 0;
3847 }
3848
3849 /*
3850 * If the "extra" data is not available, then send using "string" only.
3851 * Doing such maintains backward compatibilities.
3852 */
3853 res = ast_strlen_zero(content_type) ? queue_sendtext(c, textmsg) :
3854 queue_sendtext_data(c, textmsg, content_type);
3855
3857
3858 if (res >= 0) {
3859 astman_send_ack(s, m, "Success");
3860 } else {
3861 astman_send_error(s, m, "Failure");
3862 }
3863
3864 return 0;
3865}
3866
3867/*! \brief action_redirect: The redirect manager command */
3868static int action_redirect(struct mansession *s, const struct message *m)
3869{
3870 char buf[256];
3871 const char *name = astman_get_header(m, "Channel");
3872 const char *name2 = astman_get_header(m, "ExtraChannel");
3873 const char *exten = astman_get_header(m, "Exten");
3874 const char *exten2 = astman_get_header(m, "ExtraExten");
3875 const char *context = astman_get_header(m, "Context");
3876 const char *context2 = astman_get_header(m, "ExtraContext");
3877 const char *priority = astman_get_header(m, "Priority");
3878 const char *priority2 = astman_get_header(m, "ExtraPriority");
3879 struct ast_channel *chan;
3880 struct ast_channel *chan2;
3881 int pi = 0;
3882 int pi2 = 0;
3883 int res;
3884 int chan1_wait = 0;
3885 int chan2_wait = 0;
3886
3887 if (ast_strlen_zero(name)) {
3888 astman_send_error(s, m, "Channel not specified");
3889 return 0;
3890 }
3891
3892 if (ast_strlen_zero(context)) {
3893 astman_send_error(s, m, "Context not specified");
3894 return 0;
3895 }
3896 if (ast_strlen_zero(exten)) {
3897 astman_send_error(s, m, "Exten not specified");
3898 return 0;
3899 }
3901 astman_send_error(s, m, "Priority not specified");
3902 return 0;
3903 }
3904 if (sscanf(priority, "%30d", &pi) != 1) {
3906 }
3907 if (pi < 1) {
3908 astman_send_error(s, m, "Priority is invalid");
3909 return 0;
3910 }
3911
3912 if (!ast_strlen_zero(name2) && !ast_strlen_zero(context2)) {
3913 /* We have an ExtraChannel and an ExtraContext */
3914 if (ast_strlen_zero(exten2)) {
3915 astman_send_error(s, m, "ExtraExten not specified");
3916 return 0;
3917 }
3918 if (ast_strlen_zero(priority2)) {
3919 astman_send_error(s, m, "ExtraPriority not specified");
3920 return 0;
3921 }
3922 if (sscanf(priority2, "%30d", &pi2) != 1) {
3923 pi2 = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL);
3924 }
3925 if (pi2 < 1) {
3926 astman_send_error(s, m, "ExtraPriority is invalid");
3927 return 0;
3928 }
3929 }
3930
3932 if (!chan) {
3933 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
3934 astman_send_error(s, m, buf);
3935 return 0;
3936 }
3937 if (ast_check_hangup_locked(chan)) {
3938 astman_send_error(s, m, "Redirect failed, channel not up.");
3939 chan = ast_channel_unref(chan);
3940 return 0;
3941 }
3942
3943 if (ast_strlen_zero(name2)) {
3944 /* Single channel redirect in progress. */
3945 res = ast_async_goto(chan, context, exten, pi);
3946 if (!res) {
3947 astman_send_ack(s, m, "Redirect successful");
3948 } else {
3949 astman_send_error(s, m, "Redirect failed");
3950 }
3951 chan = ast_channel_unref(chan);
3952 return 0;
3953 }
3954
3955 chan2 = ast_channel_get_by_name(name2);
3956 if (!chan2) {
3957 snprintf(buf, sizeof(buf), "ExtraChannel does not exist: %s", name2);
3958 astman_send_error(s, m, buf);
3959 chan = ast_channel_unref(chan);
3960 return 0;
3961 }
3962 if (ast_check_hangup_locked(chan2)) {
3963 astman_send_error(s, m, "Redirect failed, extra channel not up.");
3964 chan2 = ast_channel_unref(chan2);
3965 chan = ast_channel_unref(chan);
3966 return 0;
3967 }
3968
3969 /* Dual channel redirect in progress. */
3970 ast_channel_lock(chan);
3971 if (ast_channel_is_bridged(chan)) {
3973 chan1_wait = 1;
3974 }
3975 ast_channel_unlock(chan);
3976
3977 ast_channel_lock(chan2);
3978 if (ast_channel_is_bridged(chan2)) {
3980 chan2_wait = 1;
3981 }
3982 ast_channel_unlock(chan2);
3983
3984 res = ast_async_goto(chan, context, exten, pi);
3985 if (!res) {
3986 if (!ast_strlen_zero(context2)) {
3987 res = ast_async_goto(chan2, context2, exten2, pi2);
3988 } else {
3989 res = ast_async_goto(chan2, context, exten, pi);
3990 }
3991 if (!res) {
3992 astman_send_ack(s, m, "Dual Redirect successful");
3993 } else {
3994 astman_send_error(s, m, "Secondary redirect failed");
3995 }
3996 } else {
3997 astman_send_error(s, m, "Redirect failed");
3998 }
3999
4000 /* Release the bridge wait. */
4001 if (chan1_wait) {
4003 }
4004 if (chan2_wait) {
4006 }
4007
4008 chan2 = ast_channel_unref(chan2);
4009 chan = ast_channel_unref(chan);
4010 return 0;
4011}
4012
4013static int action_blind_transfer(struct mansession *s, const struct message *m)
4014{
4015 const char *name = astman_get_header(m, "Channel");
4016 const char *exten = astman_get_header(m, "Exten");
4017 const char *context = astman_get_header(m, "Context");
4018 struct ast_channel *chan;
4019
4020 if (ast_strlen_zero(name)) {
4021 astman_send_error(s, m, "No channel specified");
4022 return 0;
4023 }
4024
4025 if (ast_strlen_zero(exten)) {
4026 astman_send_error(s, m, "No extension specified");
4027 return 0;
4028 }
4029
4031 if (!chan) {
4032 astman_send_error(s, m, "Channel specified does not exist");
4033 return 0;
4034 }
4035
4036 if (ast_strlen_zero(context)) {
4038 }
4039
4040 switch (ast_bridge_transfer_blind(1, chan, exten, context, NULL, NULL)) {
4042 astman_send_error(s, m, "Transfer not permitted");
4043 break;
4045 astman_send_error(s, m, "Transfer invalid");
4046 break;
4048 astman_send_error(s, m, "Transfer failed");
4049 break;
4051 astman_send_ack(s, m, "Transfer succeeded");
4052 break;
4053 }
4054
4055 ast_channel_unref(chan);
4056 return 0;
4057}
4058
4059static int action_atxfer(struct mansession *s, const struct message *m)
4060{
4061 const char *name = astman_get_header(m, "Channel");
4062 const char *exten = astman_get_header(m, "Exten");
4063 const char *context = astman_get_header(m, "Context");
4064 struct ast_channel *chan = NULL;
4065 char feature_code[AST_FEATURE_MAX_LEN];
4066 const char *digit;
4067
4068 if (ast_strlen_zero(name)) {
4069 astman_send_error(s, m, "No channel specified");
4070 return 0;
4071 }
4072 if (ast_strlen_zero(exten)) {
4073 astman_send_error(s, m, "No extension specified");
4074 return 0;
4075 }
4076
4077 if (!(chan = ast_channel_get_by_name(name))) {
4078 astman_send_error(s, m, "Channel specified does not exist");
4079 return 0;
4080 }
4081
4082 ast_channel_lock(chan);
4083 if (ast_get_builtin_feature(chan, "atxfer", feature_code, sizeof(feature_code)) ||
4084 ast_strlen_zero(feature_code)) {
4085 ast_channel_unlock(chan);
4086 astman_send_error(s, m, "No attended transfer feature code found");
4087 ast_channel_unref(chan);
4088 return 0;
4089 }
4090 ast_channel_unlock(chan);
4091
4092 if (!ast_strlen_zero(context)) {
4093 pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
4094 }
4095
4096 for (digit = feature_code; *digit; ++digit) {
4097 struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
4098 ast_queue_frame(chan, &f);
4099 }
4100
4101 for (digit = exten; *digit; ++digit) {
4102 struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
4103 ast_queue_frame(chan, &f);
4104 }
4105
4106 chan = ast_channel_unref(chan);
4107
4108 astman_send_ack(s, m, "Atxfer successfully queued");
4109
4110 return 0;
4111}
4112
4113static int action_cancel_atxfer(struct mansession *s, const struct message *m)
4114{
4115 const char *name = astman_get_header(m, "Channel");
4116 struct ast_channel *chan = NULL;
4117 char *feature_code;
4118 const char *digit;
4119
4120 if (ast_strlen_zero(name)) {
4121 astman_send_error(s, m, "No channel specified");
4122 return 0;
4123 }
4124
4125 if (!(chan = ast_channel_get_by_name(name))) {
4126 astman_send_error(s, m, "Channel specified does not exist");
4127 return 0;
4128 }
4129
4130 ast_channel_lock(chan);
4131 feature_code = ast_get_chan_features_atxferabort(chan);
4132 ast_channel_unlock(chan);
4133
4134 if (!feature_code) {
4135 astman_send_error(s, m, "No disconnect feature code found");
4136 ast_channel_unref(chan);
4137 return 0;
4138 }
4139
4140 for (digit = feature_code; *digit; ++digit) {
4141 struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
4142 ast_queue_frame(chan, &f);
4143 }
4144 ast_free(feature_code);
4145
4146 chan = ast_channel_unref(chan);
4147
4148 astman_send_ack(s, m, "CancelAtxfer successfully queued");
4149
4150 return 0;
4151}
4152
4153
4154static int check_blacklist(const char *cmd)
4155{
4156 char *cmd_copy, *cur_cmd;
4157 char *cmd_words[AST_MAX_CMD_LEN] = { NULL, };
4158 int i;
4159
4160 cmd_copy = ast_strdupa(cmd);
4161 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
4162 cur_cmd = ast_strip(cur_cmd);
4163 if (ast_strlen_zero(cur_cmd)) {
4164 i--;
4165 continue;
4166 }
4167
4168 cmd_words[i] = cur_cmd;
4169 }
4170
4171 for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
4172 int j, match = 1;
4173
4174 for (j = 0; command_blacklist[i].words[j]; j++) {
4175 if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
4176 match = 0;
4177 break;
4178 }
4179 }
4180
4181 if (match) {
4182 return 1;
4183 }
4184 }
4185
4186 return 0;
4187}
4188
4189/*! \brief Manager command "command" - execute CLI command */
4190static int action_command(struct mansession *s, const struct message *m)
4191{
4192 const char *cmd = astman_get_header(m, "Command");
4193 char *buf = NULL, *final_buf = NULL, *delim, *output;
4194 char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
4195 int fd, ret;
4196 off_t len;
4197
4198 if (ast_strlen_zero(cmd)) {
4199 astman_send_error(s, m, "No command provided");
4200 return 0;
4201 }
4202
4203 if (check_blacklist(cmd)) {
4204 astman_send_error(s, m, "Command blacklisted");
4205 return 0;
4206 }
4207
4208 if ((fd = mkstemp(template)) < 0) {
4209 astman_send_error_va(s, m, "Failed to create temporary file: %s", strerror(errno));
4210 return 0;
4211 }
4212
4213 ret = ast_cli_command(fd, cmd);
4214 astman_send_response_full(s, m, ret == RESULT_SUCCESS ? "Success" : "Error", MSG_MOREDATA, NULL);
4215
4216 /* Determine number of characters available */
4217 if ((len = lseek(fd, 0, SEEK_END)) < 0) {
4218 astman_append(s, "Message: Failed to determine number of characters: %s\r\n", strerror(errno));
4219 goto action_command_cleanup;
4220 }
4221
4222 /* This has a potential to overflow the stack. Hence, use the heap. */
4223 buf = ast_malloc(len + 1);
4224 final_buf = ast_malloc(len + 1);
4225
4226 if (!buf || !final_buf) {
4227 astman_append(s, "Message: Memory allocation failure\r\n");
4228 goto action_command_cleanup;
4229 }
4230
4231 if (lseek(fd, 0, SEEK_SET) < 0) {
4232 astman_append(s, "Message: Failed to set position on temporary file: %s\r\n", strerror(errno));
4233 goto action_command_cleanup;
4234 }
4235
4236 if (read(fd, buf, len) < 0) {
4237 astman_append(s, "Message: Failed to read from temporary file: %s\r\n", strerror(errno));
4238 goto action_command_cleanup;
4239 }
4240
4241 buf[len] = '\0';
4242 term_strip(final_buf, buf, len);
4243 final_buf[len] = '\0';
4244
4245 /* Trim trailing newline */
4246 if (len && final_buf[len - 1] == '\n') {
4247 final_buf[len - 1] = '\0';
4248 }
4249
4250 astman_append(s, "Message: Command output follows\r\n");
4251
4252 delim = final_buf;
4253 while ((output = strsep(&delim, "\n"))) {
4254 astman_append(s, "Output: %s\r\n", output);
4255 }
4256
4257action_command_cleanup:
4258 astman_append(s, "\r\n");
4259
4260 close(fd);
4261 unlink(template);
4262
4263 ast_free(buf);
4264 ast_free(final_buf);
4265
4266 return 0;
4267}
4268
4269/*! \brief helper function for originate */
4272 struct ast_format_cap *cap; /*!< Codecs used for a call */
4275 AST_STRING_FIELD(tech);
4276 /*! data can contain a channel name, extension number, username, password, etc. */
4277 AST_STRING_FIELD(data);
4279 AST_STRING_FIELD(appdata);
4280 AST_STRING_FIELD(cid_name);
4281 AST_STRING_FIELD(cid_num);
4283 AST_STRING_FIELD(exten);
4284 AST_STRING_FIELD(idtext);
4285 AST_STRING_FIELD(account);
4286 AST_STRING_FIELD(channelid);
4287 AST_STRING_FIELD(otherchannelid);
4291};
4292
4293/*!
4294 * \internal
4295 *
4296 * \param doomed Struct to destroy.
4297 */
4299{
4300 ao2_cleanup(doomed->cap);
4301 ast_variables_destroy(doomed->vars);
4303 ast_free(doomed);
4304}
4305
4306static void *fast_originate(void *data)
4307{
4308 struct fast_originate_helper *in = data;
4309 int res;
4310 int reason = 0;
4311 struct ast_channel *chan = NULL, *chans[1];
4312 char requested_channel[AST_CHANNEL_NAME];
4313 struct ast_assigned_ids assignedids = {
4314 .uniqueid = in->channelid,
4315 .uniqueid2 = in->otherchannelid
4316 };
4317
4318 if (!ast_strlen_zero(in->app)) {
4319 res = ast_pbx_outgoing_app(in->tech, in->cap, in->data,
4320 in->timeout, in->app, in->appdata, &reason,
4322 S_OR(in->cid_num, NULL),
4323 S_OR(in->cid_name, NULL),
4324 in->vars, in->account, &chan, &assignedids);
4325 } else {
4326 res = ast_pbx_outgoing_exten(in->tech, in->cap, in->data,
4327 in->timeout, in->context, in->exten, in->priority, &reason,
4329 S_OR(in->cid_num, NULL),
4330 S_OR(in->cid_name, NULL),
4331 in->vars, in->account, &chan, in->early_media, &assignedids);
4332 }
4333
4334 if (!chan) {
4335 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
4336 }
4337 /* Tell the manager what happened with the channel */
4338 chans[0] = chan;
4339 if (!ast_strlen_zero(in->app)) {
4340 ast_manager_event_multichan(EVENT_FLAG_CALL, "OriginateResponse", chan ? 1 : 0, chans,
4341 "%s"
4342 "Response: %s\r\n"
4343 "Channel: %s\r\n"
4344 "Application: %s\r\n"
4345 "Data: %s\r\n"
4346 "Reason: %d\r\n"
4347 "Uniqueid: %s\r\n"
4348 "CallerIDNum: %s\r\n"
4349 "CallerIDName: %s\r\n",
4350 in->idtext, res ? "Failure" : "Success",
4351 chan ? ast_channel_name(chan) : requested_channel,
4352 in->app, in->appdata, reason,
4353 chan ? ast_channel_uniqueid(chan) : S_OR(in->channelid, "<unknown>"),
4354 S_OR(in->cid_num, "<unknown>"),
4355 S_OR(in->cid_name, "<unknown>")
4356 );
4357 } else {
4358 ast_manager_event_multichan(EVENT_FLAG_CALL, "OriginateResponse", chan ? 1 : 0, chans,
4359 "%s"
4360 "Response: %s\r\n"
4361 "Channel: %s\r\n"
4362 "Context: %s\r\n"
4363 "Exten: %s\r\n"
4364 "Reason: %d\r\n"
4365 "Uniqueid: %s\r\n"
4366 "CallerIDNum: %s\r\n"
4367 "CallerIDName: %s\r\n",
4368 in->idtext, res ? "Failure" : "Success",
4369 chan ? ast_channel_name(chan) : requested_channel,
4370 in->context, in->exten, reason,
4371 chan ? ast_channel_uniqueid(chan) : S_OR(in->channelid, "<unknown>"),
4372 S_OR(in->cid_num, "<unknown>"),
4373 S_OR(in->cid_name, "<unknown>")
4374 );
4375 }
4376
4377 /* Locked and ref'd by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
4378 if (chan) {
4379 ast_channel_unlock(chan);
4380 ast_channel_unref(chan);
4381 }
4383 return NULL;
4384}
4385
4386static int aocmessage_get_unit_entry(const struct message *m, struct ast_aoc_unit_entry *entry, unsigned int entry_num)
4387{
4388 const char *unitamount;
4389 const char *unittype;
4390 struct ast_str *str = ast_str_alloca(32);
4391
4392 memset(entry, 0, sizeof(*entry));
4393
4394 ast_str_set(&str, 0, "UnitAmount(%u)", entry_num);
4395 unitamount = astman_get_header(m, ast_str_buffer(str));
4396
4397 ast_str_set(&str, 0, "UnitType(%u)", entry_num);
4398 unittype = astman_get_header(m, ast_str_buffer(str));
4399
4400 if (!ast_strlen_zero(unitamount) && (sscanf(unitamount, "%30u", &entry->amount) == 1)) {
4401 entry->valid_amount = 1;
4402 }
4403
4404 if (!ast_strlen_zero(unittype) && sscanf(unittype, "%30u", &entry->type) == 1) {
4405 entry->valid_type = 1;
4406 }
4407
4408 return 0;
4409}
4410
4411static struct ast_aoc_decoded *action_aoc_de_message(struct mansession *s, const struct message *m)
4412{
4413 const char *msgtype = astman_get_header(m, "MsgType");
4414 const char *chargetype = astman_get_header(m, "ChargeType");
4415 const char *currencyname = astman_get_header(m, "CurrencyName");
4416 const char *currencyamount = astman_get_header(m, "CurrencyAmount");
4417 const char *mult = astman_get_header(m, "CurrencyMultiplier");
4418 const char *totaltype = astman_get_header(m, "TotalType");
4419 const char *aocbillingid = astman_get_header(m, "AOCBillingId");
4420 const char *association_id= astman_get_header(m, "ChargingAssociationId");
4421 const char *association_num = astman_get_header(m, "ChargingAssociationNumber");
4422 const char *association_plan = astman_get_header(m, "ChargingAssociationPlan");
4423
4424 enum ast_aoc_type _msgtype;
4425 enum ast_aoc_charge_type _chargetype;
4427 enum ast_aoc_total_type _totaltype = AST_AOC_TOTAL;
4428 enum ast_aoc_billing_id _billingid = AST_AOC_BILLING_NA;
4429 unsigned int _currencyamount = 0;
4430 int _association_id = 0;
4431 unsigned int _association_plan = 0;
4432
4433 struct ast_aoc_decoded *decoded = NULL;
4434
4435 if (ast_strlen_zero(chargetype)) {
4436 astman_send_error(s, m, "ChargeType not specified");
4437 goto aocmessage_cleanup;
4438 }
4439
4440 _msgtype = strcasecmp(msgtype, "d") ? AST_AOC_E : AST_AOC_D;
4441
4442 if (!strcasecmp(chargetype, "NA")) {
4443 _chargetype = AST_AOC_CHARGE_NA;
4444 } else if (!strcasecmp(chargetype, "Free")) {
4445 _chargetype = AST_AOC_CHARGE_FREE;
4446 } else if (!strcasecmp(chargetype, "Currency")) {
4447 _chargetype = AST_AOC_CHARGE_CURRENCY;
4448 } else if (!strcasecmp(chargetype, "Unit")) {
4449 _chargetype = AST_AOC_CHARGE_UNIT;
4450 } else {
4451 astman_send_error(s, m, "Invalid ChargeType");
4452 goto aocmessage_cleanup;
4453 }
4454
4455 if (_chargetype == AST_AOC_CHARGE_CURRENCY) {
4456
4457 if (ast_strlen_zero(currencyamount) || (sscanf(currencyamount, "%30u", &_currencyamount) != 1)) {
4458 astman_send_error(s, m, "Invalid CurrencyAmount, CurrencyAmount is a required when ChargeType is Currency");
4459 goto aocmessage_cleanup;
4460 }
4461
4462 if (ast_strlen_zero(mult)) {
4463 astman_send_error(s, m, "ChargeMultiplier unspecified, ChargeMultiplier is required when ChargeType is Currency.");
4464 goto aocmessage_cleanup;
4465 } else if (!strcasecmp(mult, "onethousandth")) {
4467 } else if (!strcasecmp(mult, "onehundredth")) {
4469 } else if (!strcasecmp(mult, "onetenth")) {
4470 _mult = AST_AOC_MULT_ONETENTH;
4471 } else if (!strcasecmp(mult, "one")) {
4472 _mult = AST_AOC_MULT_ONE;
4473 } else if (!strcasecmp(mult, "ten")) {
4474 _mult = AST_AOC_MULT_TEN;
4475 } else if (!strcasecmp(mult, "hundred")) {
4476 _mult = AST_AOC_MULT_HUNDRED;
4477 } else if (!strcasecmp(mult, "thousand")) {
4478 _mult = AST_AOC_MULT_THOUSAND;
4479 } else {
4480 astman_send_error(s, m, "Invalid ChargeMultiplier");
4481 goto aocmessage_cleanup;
4482 }
4483 }
4484
4485 /* create decoded object and start setting values */
4486 if (!(decoded = ast_aoc_create(_msgtype, _chargetype, 0))) {
4487 astman_send_error(s, m, "Message Creation Failed");
4488 goto aocmessage_cleanup;
4489 }
4490
4491 if (_msgtype == AST_AOC_D) {
4492 if (!ast_strlen_zero(totaltype) && !strcasecmp(totaltype, "subtotal")) {
4493 _totaltype = AST_AOC_SUBTOTAL;
4494 }
4495
4496 if (ast_strlen_zero(aocbillingid)) {
4497 /* ignore this is optional */
4498 } else if (!strcasecmp(aocbillingid, "Normal")) {
4499 _billingid = AST_AOC_BILLING_NORMAL;
4500 } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
4501 _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
4502 } else if (!strcasecmp(aocbillingid, "CreditCard")) {
4503 _billingid = AST_AOC_BILLING_CREDIT_CARD;
4504 } else {
4505 astman_send_error(s, m, "Invalid AOC-D AOCBillingId");
4506 goto aocmessage_cleanup;
4507 }
4508 } else {
4509 if (ast_strlen_zero(aocbillingid)) {
4510 /* ignore this is optional */
4511 } else if (!strcasecmp(aocbillingid, "Normal")) {
4512 _billingid = AST_AOC_BILLING_NORMAL;
4513 } else if (!strcasecmp(aocbillingid, "ReverseCharge")) {
4514 _billingid = AST_AOC_BILLING_REVERSE_CHARGE;
4515 } else if (!strcasecmp(aocbillingid, "CreditCard")) {
4516 _billingid = AST_AOC_BILLING_CREDIT_CARD;
4517 } else if (!strcasecmp(aocbillingid, "CallFwdUnconditional")) {
4519 } else if (!strcasecmp(aocbillingid, "CallFwdBusy")) {
4520 _billingid = AST_AOC_BILLING_CALL_FWD_BUSY;
4521 } else