84ec2cc81a5ab9ea7ecad98f6902bfbb93b5bddf
[asterisk/asterisk.git] / main / manager.c
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  * \extref 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 /*! \addtogroup Group_AMI AMI functions
40 */
41 /*! @{
42  Doxygen group */
43
44 #include "asterisk.h"
45
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
47
48 #include "asterisk/_private.h"
49 #include "asterisk/paths.h"     /* use various ast_config_AST_* */
50 #include <ctype.h>
51 #include <sys/time.h>
52 #include <signal.h>
53 #include <sys/mman.h>
54
55 #include "asterisk/channel.h"
56 #include "asterisk/file.h"
57 #include "asterisk/manager.h"
58 #include "asterisk/module.h"
59 #include "asterisk/config.h"
60 #include "asterisk/callerid.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/tcptls.h"
69 #include "asterisk/http.h"
70 #include "asterisk/ast_version.h"
71 #include "asterisk/threadstorage.h"
72 #include "asterisk/linkedlists.h"
73 #include "asterisk/version.h"
74 #include "asterisk/term.h"
75 #include "asterisk/astobj2.h"
76 #include "asterisk/features.h"
77
78 enum error_type {
79         UNKNOWN_ACTION = 1,
80         UNKNOWN_CATEGORY,
81         UNSPECIFIED_CATEGORY,
82         UNSPECIFIED_ARGUMENT,
83         FAILURE_ALLOCATION,
84         FAILURE_DELCAT,
85         FAILURE_EMPTYCAT,
86         FAILURE_UPDATE,
87         FAILURE_DELETE,
88         FAILURE_APPEND
89 };
90
91
92 /*!
93  * Linked list of events.
94  * Global events are appended to the list by append_event().
95  * The usecount is the number of stored pointers to the element,
96  * excluding the list pointers. So an element that is only in
97  * the list has a usecount of 0, not 1.
98  *
99  * Clients have a pointer to the last event processed, and for each
100  * of these clients we track the usecount of the elements.
101  * If we have a pointer to an entry in the list, it is safe to navigate
102  * it forward because elements will not be deleted, but only appended.
103  * The worst that can happen is seeing the pointer still NULL.
104  *
105  * When the usecount of an element drops to 0, and the element is the
106  * first in the list, we can remove it. Removal is done within the
107  * main thread, which is woken up for the purpose.
108  *
109  * For simplicity of implementation, we make sure the list is never empty.
110  */
111 struct eventqent {
112         int usecount;           /*!< # of clients who still need the event */
113         int category;
114         unsigned int seq;       /*!< sequence number */
115         AST_LIST_ENTRY(eventqent) eq_next;
116         char eventdata[1];      /*!< really variable size, allocated by append_event() */
117 };
118
119 static AST_LIST_HEAD_STATIC(all_events, eventqent);
120
121 static int displayconnects = 1;
122 static int allowmultiplelogin = 1;
123 static int timestampevents;
124 static int httptimeout = 60;
125 static int manager_enabled = 0;
126 static int webmanager_enabled = 0;
127
128 static int block_sockets;
129 static int num_sessions;
130
131 static int manager_debug;       /*!< enable some debugging code in the manager */
132
133 /*! \brief
134  * Descriptor for a manager session, either on the AMI socket or over HTTP.
135  *
136  * \note
137  * AMI session have managerid == 0; the entry is created upon a connect,
138  * and destroyed with the socket.
139  * HTTP sessions have managerid != 0, the value is used as a search key
140  * to lookup sessions (using the mansession_id cookie).
141  */
142 static const char *command_blacklist[] = {
143         "module load",
144         "module unload",
145 };
146
147 struct mansession {
148         pthread_t ms_t;         /*!< Execution thread, basically useless */
149         ast_mutex_t __lock;     /*!< Thread lock -- don't use in action callbacks, it's already taken care of  */
150                                 /* XXX need to document which fields it is protecting */
151         struct sockaddr_in sin; /*!< address we are connecting from */
152         FILE *f;                /*!< fdopen() on the underlying fd */
153         int fd;                 /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
154         int inuse;              /*!< number of HTTP sessions using this entry */
155         int needdestroy;        /*!< Whether an HTTP session should be destroyed */
156         pthread_t waiting_thread;       /*!< Sleeping thread using this descriptor */
157         unsigned long managerid;        /*!< Unique manager identifier, 0 for AMI sessions */
158         time_t sessionstart;    /*!< Session start time */
159         time_t sessiontimeout;  /*!< Session timeout if HTTP */
160         char username[80];      /*!< Logged in username */
161         char challenge[10];     /*!< Authentication challenge */
162         int authenticated;      /*!< Authentication status */
163         int readperm;           /*!< Authorization for reading */
164         int writeperm;          /*!< Authorization for writing */
165         char inbuf[1025];       /*!< Buffer */
166                                 /* we use the extra byte to add a '\0' and simplify parsing */
167         int inlen;              /*!< number of buffered bytes */
168         int send_events;        /*!<  XXX what ? */
169         struct eventqent *last_ev;      /*!< last event processed. */
170         int writetimeout;       /*!< Timeout for ast_carefulwrite() */
171         AST_LIST_ENTRY(mansession) list;
172 };
173
174 #define NEW_EVENT(m)    (AST_LIST_NEXT(m->last_ev, eq_next))
175
176 static AST_LIST_HEAD_STATIC(sessions, mansession);
177
178 /*! \brief user descriptor, as read from the config file.
179  *
180  * \note It is still missing some fields -- e.g. we can have multiple permit and deny
181  * lines which are not supported here, and readperm/writeperm/writetimeout
182  * are not stored.
183  */
184 struct ast_manager_user {
185         char username[80];
186         char *secret;
187         struct ast_ha *ha;              /*!< ACL setting */
188         int readperm;                   /*! Authorization for reading */
189         int writeperm;                  /*! Authorization for writing */
190         int writetimeout;               /*! Per user Timeout for ast_carefulwrite() */
191         int displayconnects;    /*!< XXX unused */
192         int keep;       /*!< mark entries created on a reload */
193         AST_RWLIST_ENTRY(ast_manager_user) list;
194 };
195
196 /*! \brief list of users found in the config file */
197 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
198
199 /*! \brief list of actions registered */
200 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
201
202 /*! \brief list of hooks registered */
203 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
204
205 /*! \brief Add a custom hook to be called when an event is fired */
206 void ast_manager_register_hook(struct manager_custom_hook *hook)
207 {
208         AST_RWLIST_WRLOCK(&manager_hooks);
209         AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
210         AST_RWLIST_UNLOCK(&manager_hooks);
211         return;
212 }
213
214 /*! \brief Delete a custom hook to be called when an event is fired */
215 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
216 {
217         AST_RWLIST_WRLOCK(&manager_hooks);
218         AST_RWLIST_REMOVE(&manager_hooks, hook, list);
219         AST_RWLIST_UNLOCK(&manager_hooks);
220         return;
221 }
222
223 /*! \brief
224  * Event list management functions.
225  * We assume that the event list always has at least one element,
226  * and the delete code will not remove the last entry even if the
227  * 
228  */
229 #if 0
230 static time_t __deb(time_t start, const char *msg)
231 {
232         time_t now = time(NULL);
233         ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
234         if (start != 0 && now - start > 5)
235                 ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
236         return now;
237 }
238
239 static void LOCK_EVENTS(void)
240 {
241         time_t start = __deb(0, "about to lock events");
242         AST_LIST_LOCK(&all_events);
243         __deb(start, "done lock events");
244 }
245
246 static void UNLOCK_EVENTS(void)
247 {
248         __deb(0, "about to unlock events");
249         AST_LIST_UNLOCK(&all_events);
250 }
251
252 static void LOCK_SESS(void)
253 {
254         time_t start = __deb(0, "about to lock sessions");
255         AST_LIST_LOCK(&sessions);
256         __deb(start, "done lock sessions");
257 }
258
259 static void UNLOCK_SESS(void)
260 {
261         __deb(0, "about to unlock sessions");
262         AST_LIST_UNLOCK(&sessions);
263 }
264 #endif
265
266 int check_manager_enabled()
267 {
268         return manager_enabled;
269 }
270
271 int check_webmanager_enabled()
272 {
273         return (webmanager_enabled && manager_enabled);
274 }
275
276 /*!
277  * Grab a reference to the last event, update usecount as needed.
278  * Can handle a NULL pointer.
279  */
280 static struct eventqent *grab_last(void)
281 {
282         struct eventqent *ret;
283
284         AST_LIST_LOCK(&all_events);
285         ret = AST_LIST_LAST(&all_events);
286         /* the list is never empty now, but may become so when
287          * we optimize it in the future, so be prepared.
288          */
289         if (ret)
290                 ast_atomic_fetchadd_int(&ret->usecount, 1);
291         AST_LIST_UNLOCK(&all_events);
292         return ret;
293 }
294
295 /*!
296  * Purge unused events. Remove elements from the head
297  * as long as their usecount is 0 and there is a next element.
298  */
299 static void purge_events(void)
300 {
301         struct eventqent *ev;
302
303         AST_LIST_LOCK(&all_events);
304         while ( (ev = AST_LIST_FIRST(&all_events)) &&
305             ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
306                 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
307                 ast_free(ev);
308         }
309         AST_LIST_UNLOCK(&all_events);
310 }
311
312 /*!
313  * helper functions to convert back and forth between
314  * string and numeric representation of set of flags
315  */
316 static struct permalias {
317         int num;
318         char *label;
319 } perms[] = {
320         { EVENT_FLAG_SYSTEM, "system" },
321         { EVENT_FLAG_CALL, "call" },
322         { EVENT_FLAG_LOG, "log" },
323         { EVENT_FLAG_VERBOSE, "verbose" },
324         { EVENT_FLAG_COMMAND, "command" },
325         { EVENT_FLAG_AGENT, "agent" },
326         { EVENT_FLAG_USER, "user" },
327         { EVENT_FLAG_CONFIG, "config" },
328         { EVENT_FLAG_DTMF, "dtmf" },
329         { EVENT_FLAG_REPORTING, "reporting" },
330         { EVENT_FLAG_CDR, "cdr" },
331         { EVENT_FLAG_DIALPLAN, "dialplan" },
332         { EVENT_FLAG_ORIGINATE, "originate" },
333         { -1, "all" },
334         { 0, "none" },
335 };
336
337 /*! \brief Convert authority code to a list of options */
338 static char *authority_to_str(int authority, struct ast_str **res)
339 {
340         int i;
341         char *sep = "";
342
343         (*res)->used = 0;
344         for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
345                 if (authority & perms[i].num) {
346                         ast_str_append(res, 0, "%s%s", sep, perms[i].label);
347                         sep = ",";
348                 }
349         }
350
351         if ((*res)->used == 0)  /* replace empty string with something sensible */
352                 ast_str_append(res, 0, "<none>");
353
354         return (*res)->str;
355 }
356
357 /*! Tells you if smallstr exists inside bigstr
358    which is delim by delim and uses no buf or stringsep
359    ast_instring("this|that|more","this",'|') == 1;
360
361    feel free to move this to app.c -anthm */
362 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
363 {
364         const char *val = bigstr, *next;
365
366         do {
367                 if ((next = strchr(val, delim))) {
368                         if (!strncmp(val, smallstr, (next - val)))
369                                 return 1;
370                         else
371                                 continue;
372                 } else
373                         return !strcmp(smallstr, val);
374         } while (*(val = (next + 1)));
375
376         return 0;
377 }
378
379 static int get_perm(const char *instr)
380 {
381         int x = 0, ret = 0;
382
383         if (!instr)
384                 return 0;
385
386         for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
387                 if (ast_instring(instr, perms[x].label, ','))
388                         ret |= perms[x].num;
389         }
390
391         return ret;
392 }
393
394 /*!
395  * A number returns itself, false returns 0, true returns all flags,
396  * other strings return the flags that are set.
397  */
398 static int strings_to_mask(const char *string)
399 {
400         const char *p;
401
402         if (ast_strlen_zero(string))
403                 return -1;
404
405         for (p = string; *p; p++)
406                 if (*p < '0' || *p > '9')
407                         break;
408         if (!p) /* all digits */
409                 return atoi(string);
410         if (ast_false(string))
411                 return 0;
412         if (ast_true(string)) { /* all permissions */
413                 int x, ret = 0;
414                 for (x = 0; x<sizeof(perms) / sizeof(perms[0]); x++)
415                         ret |= perms[x].num;
416                 return ret;
417         }
418         return get_perm(string);
419 }
420
421 static int check_manager_session_inuse(const char *name)
422 {
423         struct mansession *session = NULL;
424
425         AST_LIST_LOCK(&sessions);
426         AST_LIST_TRAVERSE(&sessions, session, list) {
427                 if (!strcasecmp(session->username, name)) 
428                         break;
429         }
430         AST_LIST_UNLOCK(&sessions);
431
432         return session ? 1 : 0;
433 }
434
435
436 /*!
437  * lookup an entry in the list of registered users.
438  * must be called with the list lock held.
439  */
440 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
441 {
442         struct ast_manager_user *user = NULL;
443
444         AST_RWLIST_TRAVERSE(&users, user, list)
445                 if (!strcasecmp(user->username, name))
446                         break;
447         return user;
448 }
449
450 /*! \brief Get displayconnects config option.
451  *  \param s manager session to get parameter from.
452  *  \return displayconnects config option value.
453  */
454 static int manager_displayconnects (struct mansession *s)
455 {
456         struct ast_manager_user *user = NULL;
457         int ret = 0;
458
459         AST_RWLIST_RDLOCK(&users);
460         if ((user = get_manager_by_name_locked (s->username)))
461                 ret = user->displayconnects;
462         AST_RWLIST_UNLOCK(&users);
463         
464         return ret;
465 }
466
467 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
468 {
469         struct manager_action *cur;
470         struct ast_str *authority;
471         int num, l, which;
472         char *ret = NULL;
473         switch (cmd) {
474         case CLI_INIT:
475                 e->command = "manager show command";
476                 e->usage = 
477                         "Usage: manager show command <actionname>\n"
478                         "       Shows the detailed description for a specific Asterisk manager interface command.\n";
479                 return NULL;
480         case CLI_GENERATE:
481                 l = strlen(a->word);
482                 which = 0;
483                 AST_RWLIST_RDLOCK(&actions);
484                 AST_RWLIST_TRAVERSE(&actions, cur, list) {
485                         if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
486                                 ret = ast_strdup(cur->action);
487                                 break;  /* make sure we exit even if ast_strdup() returns NULL */
488                         }
489                 }
490                 AST_RWLIST_UNLOCK(&actions);
491                 return ret;
492         }
493         authority = ast_str_alloca(80);
494         if (a->argc != 4)
495                 return CLI_SHOWUSAGE;
496
497         AST_RWLIST_RDLOCK(&actions);
498         AST_RWLIST_TRAVERSE(&actions, cur, list) {
499                 for (num = 3; num < a->argc; num++) {
500                         if (!strcasecmp(cur->action, a->argv[num])) {
501                                 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
502                                         cur->action, cur->synopsis,
503                                         authority_to_str(cur->authority, &authority),
504                                         S_OR(cur->description, ""));
505                         }
506                 }
507         }
508         AST_RWLIST_UNLOCK(&actions);
509
510         return CLI_SUCCESS;
511 }
512
513 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
514 {
515         switch (cmd) {
516         case CLI_INIT:
517                 e->command = "manager debug [on|off]";
518                 e->usage = "Usage: manager debug [on|off]\n     Show, enable, disable debugging of the manager code.\n";
519                 return NULL;
520         case CLI_GENERATE:
521                 return NULL;    
522         }
523         if (a->argc == 2)
524                 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
525         else if (a->argc == 3) {
526                 if (!strcasecmp(a->argv[2], "on"))
527                         manager_debug = 1;
528                 else if (!strcasecmp(a->argv[2], "off"))
529                         manager_debug = 0;
530                 else
531                         return CLI_SHOWUSAGE;
532         }
533         return CLI_SUCCESS;
534 }
535
536 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
537 {
538         struct ast_manager_user *user = NULL;
539         int l, which;
540         char *ret = NULL;
541         struct ast_str *rauthority = ast_str_alloca(80);
542         struct ast_str *wauthority = ast_str_alloca(80);
543
544         switch (cmd) {
545         case CLI_INIT:
546                 e->command = "manager show user";
547                 e->usage = 
548                         " Usage: manager show user <user>\n"
549                         "        Display all information related to the manager user specified.\n";
550                 return NULL;
551         case CLI_GENERATE:
552                 l = strlen(a->word);
553                 which = 0;
554                 if (a->pos != 3)
555                         return NULL;
556                 AST_RWLIST_RDLOCK(&users);
557                 AST_RWLIST_TRAVERSE(&users, user, list) {
558                         if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
559                                 ret = ast_strdup(user->username);
560                                 break;
561                         }
562                 }
563                 AST_RWLIST_UNLOCK(&users);
564                 return ret;
565         }
566
567         if (a->argc != 4)
568                 return CLI_SHOWUSAGE;
569
570         AST_RWLIST_RDLOCK(&users);
571
572         if (!(user = get_manager_by_name_locked(a->argv[3]))) {
573                 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
574                 AST_RWLIST_UNLOCK(&users);
575                 return CLI_SUCCESS;
576         }
577
578         ast_cli(a->fd, "\n");
579         ast_cli(a->fd,
580                 "       username: %s\n"
581                 "         secret: %s\n"
582                 "            acl: %s\n"
583                 "      read perm: %s\n"
584                 "     write perm: %s\n"
585                 "displayconnects: %s\n",
586                 (user->username ? user->username : "(N/A)"),
587                 (user->secret ? "<Set>" : "(N/A)"),
588                 (user->ha ? "yes" : "no"),
589                 authority_to_str(user->readperm, &rauthority),
590                 authority_to_str(user->writeperm, &wauthority),
591                 (user->displayconnects ? "yes" : "no"));
592
593         AST_RWLIST_UNLOCK(&users);
594
595         return CLI_SUCCESS;
596 }
597
598
599 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
600 {
601         struct ast_manager_user *user = NULL;
602         int count_amu = 0;
603         switch (cmd) {
604         case CLI_INIT:
605                 e->command = "manager show users";
606                 e->usage = 
607                         "Usage: manager show users\n"
608                         "       Prints a listing of all managers that are currently configured on that\n"
609                         " system.\n";
610                 return NULL;
611         case CLI_GENERATE:
612                 return NULL;
613         }
614         if (a->argc != 3)
615                 return CLI_SHOWUSAGE;
616
617         AST_RWLIST_RDLOCK(&users);
618
619         /* If there are no users, print out something along those lines */
620         if (AST_RWLIST_EMPTY(&users)) {
621                 ast_cli(a->fd, "There are no manager users.\n");
622                 AST_RWLIST_UNLOCK(&users);
623                 return CLI_SUCCESS;
624         }
625
626         ast_cli(a->fd, "\nusername\n--------\n");
627
628         AST_RWLIST_TRAVERSE(&users, user, list) {
629                 ast_cli(a->fd, "%s\n", user->username);
630                 count_amu++;
631         }
632
633         AST_RWLIST_UNLOCK(&users);
634
635         ast_cli(a->fd, "-------------------\n");
636         ast_cli(a->fd, "%d manager users configured.\n", count_amu);
637
638         return CLI_SUCCESS;
639 }
640
641
642 /*! \brief  CLI command  manager list commands */
643 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
644 {
645         struct manager_action *cur;
646         struct ast_str *authority;
647         static const char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
648         switch (cmd) {
649         case CLI_INIT:
650                 e->command = "manager show commands";
651                 e->usage = 
652                         "Usage: manager show commands\n"
653                         "       Prints a listing of all the available Asterisk manager interface commands.\n";
654                 return NULL;
655         case CLI_GENERATE:
656                 return NULL;    
657         }       
658         authority = ast_str_alloca(80);
659         ast_cli(a->fd, format, "Action", "Privilege", "Synopsis");
660         ast_cli(a->fd, format, "------", "---------", "--------");
661
662         AST_RWLIST_RDLOCK(&actions);
663         AST_RWLIST_TRAVERSE(&actions, cur, list)
664                 ast_cli(a->fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
665         AST_RWLIST_UNLOCK(&actions);
666
667         return CLI_SUCCESS;
668 }
669
670 /*! \brief CLI command manager list connected */
671 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
672 {
673         struct mansession *s;
674         time_t now = time(NULL);
675         static const char *format = "  %-15.15s  %-15.15s  %-10.10s  %-10.10s  %-8.8s  %-8.8s  %-5.5s  %-5.5s\n";
676         static const char *format2 = "  %-15.15s  %-15.15s  %-10d  %-10d  %-8d  %-8d  %-5.5d  %-5.5d\n";
677         int count = 0;
678         switch (cmd) {
679         case CLI_INIT:
680                 e->command = "manager show connected";
681                 e->usage = 
682                         "Usage: manager show connected\n"
683                         "       Prints a listing of the users that are currently connected to the\n"
684                         "Asterisk manager interface.\n";
685                 return NULL;
686         case CLI_GENERATE:
687                 return NULL;    
688         }
689
690         ast_cli(a->fd, format, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
691
692         AST_LIST_LOCK(&sessions);
693         AST_LIST_TRAVERSE(&sessions, s, list) {
694                 ast_cli(a->fd, format2, s->username, ast_inet_ntoa(s->sin.sin_addr), (int)(s->sessionstart), (int)(now - s->sessionstart), s->fd, s->inuse, s->readperm, s->writeperm);
695                 count++;
696         }
697         AST_LIST_UNLOCK(&sessions);
698
699         ast_cli(a->fd, "%d users connected.\n", count);
700
701         return CLI_SUCCESS;
702 }
703
704 /*! \brief CLI command manager list eventq */
705 /* Should change to "manager show connected" */
706 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
707 {
708         struct eventqent *s;
709         switch (cmd) {
710         case CLI_INIT:
711                 e->command = "manager show eventq";
712                 e->usage = 
713                         "Usage: manager show eventq\n"
714                         "       Prints a listing of all events pending in the Asterisk manger\n"
715                         "event queue.\n";
716                 return NULL;
717         case CLI_GENERATE:
718                 return NULL;
719         }
720         AST_LIST_LOCK(&all_events);
721         AST_LIST_TRAVERSE(&all_events, s, eq_next) {
722                 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
723                 ast_cli(a->fd, "Category: %d\n", s->category);
724                 ast_cli(a->fd, "Event:\n%s", s->eventdata);
725         }
726         AST_LIST_UNLOCK(&all_events);
727
728         return CLI_SUCCESS;
729 }
730
731 /*! \brief CLI command manager reload */
732 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
733 {
734         switch (cmd) {
735         case CLI_INIT:
736                 e->command = "manager reload";
737                 e->usage =
738                         "Usage: manager reload\n"
739                         "       Reloads the manager configuration.\n";
740                 return NULL;
741         case CLI_GENERATE:
742                 return NULL;
743         }
744         if (a->argc > 2)
745                 return CLI_SHOWUSAGE;
746         reload_manager();
747         return CLI_SUCCESS;
748 }
749
750
751 static struct ast_cli_entry cli_manager[] = {
752         AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
753         AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
754         AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
755         AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
756         AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
757         AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
758         AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
759         AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
760 };
761
762 /*
763  * Decrement the usecount for the event; if it goes to zero,
764  * (why check for e->next ?) wakeup the
765  * main thread, which is in charge of freeing the record.
766  * Returns the next record.
767  */
768 static struct eventqent *unref_event(struct eventqent *e)
769 {
770         ast_atomic_fetchadd_int(&e->usecount, -1);
771         return AST_LIST_NEXT(e, eq_next);
772 }
773
774 static void ref_event(struct eventqent *e)
775 {
776         ast_atomic_fetchadd_int(&e->usecount, 1);
777 }
778
779 /*
780  * destroy a session, leaving the usecount
781  */
782 static void free_session(struct mansession *s)
783 {
784         struct eventqent *eqe = s->last_ev;
785         if (s->f != NULL)
786                 fclose(s->f);
787         ast_mutex_destroy(&s->__lock);
788         ast_free(s);
789         unref_event(eqe);
790 }
791
792 static void destroy_session(struct mansession *s)
793 {
794         AST_LIST_LOCK(&sessions);
795         AST_LIST_REMOVE(&sessions, s, list);
796         ast_atomic_fetchadd_int(&num_sessions, -1);
797         free_session(s);
798         AST_LIST_UNLOCK(&sessions);
799 }
800
801 const char *astman_get_header(const struct message *m, char *var)
802 {
803         int x, l = strlen(var);
804
805         for (x = 0; x < m->hdrcount; x++) {
806                 const char *h = m->headers[x];
807                 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
808                         return h + l + 2;
809         }
810
811         return "";
812 }
813
814 struct ast_variable *astman_get_variables(const struct message *m)
815 {
816         int varlen, x, y;
817         struct ast_variable *head = NULL, *cur;
818
819         AST_DECLARE_APP_ARGS(args,
820                 AST_APP_ARG(vars)[32];
821         );
822
823         varlen = strlen("Variable: ");
824
825         for (x = 0; x < m->hdrcount; x++) {
826                 char *parse, *var, *val;
827
828                 if (strncasecmp("Variable: ", m->headers[x], varlen))
829                         continue;
830                 parse = ast_strdupa(m->headers[x] + varlen);
831
832                 AST_STANDARD_APP_ARGS(args, parse);
833                 if (!args.argc)
834                         continue;
835                 for (y = 0; y < args.argc; y++) {
836                         if (!args.vars[y])
837                                 continue;
838                         var = val = ast_strdupa(args.vars[y]);
839                         strsep(&val, "=");
840                         if (!val || ast_strlen_zero(var))
841                                 continue;
842                         cur = ast_variable_new(var, val, "");
843                         cur->next = head;
844                         head = cur;
845                 }
846         }
847
848         return head;
849 }
850
851 /*!
852  * helper function to send a string to the socket.
853  * Return -1 on error (e.g. buffer full).
854  */
855 static int send_string(struct mansession *s, char *string)
856 {
857         int len = strlen(string);       /* residual length */
858         char *src = string;
859         struct timeval start = ast_tvnow();
860         int n = 0;
861
862         for (;;) {
863                 int elapsed;
864                 struct pollfd fd;
865                 n = fwrite(src, 1, len, s->f);  /* try to write the string, non blocking */
866                 if (n == len /* ok */ || n < 0 /* error */)
867                         break;
868                 len -= n;       /* skip already written data */
869                 src += n;
870                 fd.fd = s->fd;
871                 fd.events = POLLOUT;
872                 n = -1;         /* error marker */
873                 elapsed = ast_tvdiff_ms(ast_tvnow(), start);
874                 if (elapsed > s->writetimeout)
875                         break;
876                 if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
877                         break;
878         }
879         fflush(s->f);
880         return n < 0 ? -1 : 0;
881 }
882
883 /*!
884  * \brief thread local buffer for astman_append
885  *
886  * \note This can not be defined within the astman_append() function
887  *       because it declares a couple of functions that get used to
888  *       initialize the thread local storage key.
889  */
890 AST_THREADSTORAGE(astman_append_buf);
891 /*! \brief initial allocated size for the astman_append_buf */
892 #define ASTMAN_APPEND_BUF_INITSIZE   256
893
894 /*!
895  * utility functions for creating AMI replies
896  */
897 void astman_append(struct mansession *s, const char *fmt, ...)
898 {
899         va_list ap;
900         struct ast_str *buf;
901
902         if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
903                 return;
904
905         va_start(ap, fmt);
906         ast_str_set_va(&buf, 0, fmt, ap);
907         va_end(ap);
908
909         if (s->f != NULL)
910                 send_string(s, buf->str);
911         else
912                 ast_verbose("fd == -1 in astman_append, should not happen\n");
913 }
914
915 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
916    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
917    hold the session lock _or_ be running in an action callback (in which case s->busy will
918    be non-zero). In either of these cases, there is no need to lock-protect the session's
919    fd, since no other output will be sent (events will be queued), and no input will
920    be read until either the current action finishes or get_input() obtains the session
921    lock.
922  */
923
924 /*! \brief send a response with an optional message,
925  * and terminate it with an empty line.
926  * m is used only to grab the 'ActionID' field.
927  *
928  * Use the explicit constant MSG_MOREDATA to remove the empty line.
929  * XXX MSG_MOREDATA should go to a header file.
930  */
931 #define MSG_MOREDATA    ((char *)astman_send_response)
932 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
933 {
934         const char *id = astman_get_header(m, "ActionID");
935
936         astman_append(s, "Response: %s\r\n", resp);
937         if (!ast_strlen_zero(id))
938                 astman_append(s, "ActionID: %s\r\n", id);
939         if (listflag)
940                 astman_append(s, "Eventlist: %s\r\n", listflag);        /* Start, complete, cancelled */
941         if (msg == MSG_MOREDATA)
942                 return;
943         else if (msg)
944                 astman_append(s, "Message: %s\r\n\r\n", msg);
945         else
946                 astman_append(s, "\r\n");
947 }
948
949 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
950 {
951         astman_send_response_full(s, m, resp, msg, NULL);
952 }
953
954 void astman_send_error(struct mansession *s, const struct message *m, char *error)
955 {
956         astman_send_response_full(s, m, "Error", error, NULL);
957 }
958
959 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
960 {
961         astman_send_response_full(s, m, "Success", msg, NULL);
962 }
963
964 static void astman_start_ack(struct mansession *s, const struct message *m)
965 {
966         astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
967 }
968
969 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
970 {
971         astman_send_response_full(s, m, "Success", msg, listflag);
972 }
973
974
975 /*! \brief
976    Rather than braindead on,off this now can also accept a specific int mask value
977    or a ',' delim list of mask strings (the same as manager.conf) -anthm
978 */
979 static int set_eventmask(struct mansession *s, const char *eventmask)
980 {
981         int maskint = strings_to_mask(eventmask);
982
983         ast_mutex_lock(&s->__lock);
984         if (maskint >= 0)
985                 s->send_events = maskint;
986         ast_mutex_unlock(&s->__lock);
987
988         return maskint;
989 }
990
991 /*
992  * Here we start with action_ handlers for AMI actions,
993  * and the internal functions used by them.
994  * Generally, the handlers are called action_foo()
995  */
996
997 /* helper function for action_login() */
998 static int authenticate(struct mansession *s, const struct message *m)
999 {
1000         const char *username = astman_get_header(m, "Username");
1001         const char *password = astman_get_header(m, "Secret");
1002         int error = -1;
1003         struct ast_manager_user *user = NULL;
1004
1005         if (ast_strlen_zero(username))  /* missing username */
1006                 return -1;
1007
1008         /* locate user in locked state */
1009         AST_RWLIST_WRLOCK(&users);
1010
1011         if (!(user = get_manager_by_name_locked(username))) {
1012                 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1013         } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
1014                 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1015         } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
1016                 const char *key = astman_get_header(m, "Key");
1017                 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) && user->secret) {
1018                         int x;
1019                         int len = 0;
1020                         char md5key[256] = "";
1021                         struct MD5Context md5;
1022                         unsigned char digest[16];
1023
1024                         MD5Init(&md5);
1025                         MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
1026                         MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
1027                         MD5Final(digest, &md5);
1028                         for (x = 0; x < 16; x++)
1029                                 len += sprintf(md5key + len, "%2.2x", digest[x]);
1030                         if (!strcmp(md5key, key))
1031                                 error = 0;
1032                 } else {
1033                         ast_debug(1, "MD5 authentication is not possible.  challenge: '%s'\n", 
1034                                 S_OR(s->challenge, ""));
1035                 }
1036         } else if (password && user->secret && !strcmp(password, user->secret))
1037                 error = 0;
1038
1039         if (error) {
1040                 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
1041                 AST_RWLIST_UNLOCK(&users);
1042                 return -1;
1043         }
1044
1045         /* auth complete */
1046         
1047         ast_copy_string(s->username, username, sizeof(s->username));
1048         s->readperm = user->readperm;
1049         s->writeperm = user->writeperm;
1050         s->writetimeout = user->writetimeout;
1051         s->sessionstart = time(NULL);
1052         set_eventmask(s, astman_get_header(m, "Events"));
1053         
1054         AST_RWLIST_UNLOCK(&users);
1055         return 0;
1056 }
1057
1058 /*! \brief Manager PING */
1059 static char mandescr_ping[] =
1060 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the\n"
1061 "  manager connection open.\n"
1062 "Variables: NONE\n";
1063
1064 static int action_ping(struct mansession *s, const struct message *m)
1065 {
1066         astman_send_response(s, m, "Success", "Ping: Pong\r\n");
1067         return 0;
1068 }
1069
1070 static char mandescr_getconfig[] =
1071 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1072 "file by category and contents or optionally by specified category only.\n"
1073 "Variables: (Names marked with * are required)\n"
1074 "   *Filename: Configuration filename (e.g. foo.conf)\n"
1075 "   Category: Category in configuration file\n";
1076
1077 static int action_getconfig(struct mansession *s, const struct message *m)
1078 {
1079         struct ast_config *cfg;
1080         const char *fn = astman_get_header(m, "Filename");
1081         const char *category = astman_get_header(m, "Category");
1082         int catcount = 0;
1083         int lineno = 0;
1084         char *cur_category = NULL;
1085         struct ast_variable *v;
1086         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1087
1088         if (ast_strlen_zero(fn)) {
1089                 astman_send_error(s, m, "Filename not specified");
1090                 return 0;
1091         }
1092         if (!(cfg = ast_config_load(fn, config_flags))) {
1093                 astman_send_error(s, m, "Config file not found");
1094                 return 0;
1095         }
1096
1097         astman_start_ack(s, m);
1098         while ((cur_category = ast_category_browse(cfg, cur_category))) {
1099                 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
1100                         lineno = 0;
1101                         astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
1102                         for (v = ast_variable_browse(cfg, cur_category); v; v = v->next)
1103                                 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1104                         catcount++;
1105                 }
1106         }
1107         if (!ast_strlen_zero(category) && catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1108                 astman_append(s, "No categories found");
1109         ast_config_destroy(cfg);
1110         astman_append(s, "\r\n");
1111
1112         return 0;
1113 }
1114
1115 static char mandescr_listcategories[] =
1116 "Description: A 'ListCategories' action will dump the categories in\n"
1117 "a given file.\n"
1118 "Variables:\n"
1119 "   Filename: Configuration filename (e.g. foo.conf)\n";
1120
1121 static int action_listcategories(struct mansession *s, const struct message *m)
1122 {
1123         struct ast_config *cfg;
1124         const char *fn = astman_get_header(m, "Filename");
1125         char *category = NULL;
1126         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1127         int catcount = 0;
1128
1129         if (ast_strlen_zero(fn)) {
1130                 astman_send_error(s, m, "Filename not specified");
1131                 return 0;
1132         }
1133         if (!(cfg = ast_config_load(fn, config_flags))) {
1134                 astman_send_error(s, m, "Config file not found or file has invalid syntax");
1135                 return 0;
1136         }
1137         astman_start_ack(s, m);
1138         while ((category = ast_category_browse(cfg, category))) {
1139                 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1140                 catcount++;
1141         }
1142         if (catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1143                 astman_append(s, "Error: no categories found");
1144         ast_config_destroy(cfg);
1145         astman_append(s, "\r\n");
1146
1147         return 0;
1148 }
1149
1150
1151         
1152
1153 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1154 static void json_escape(char *out, const char *in)
1155 {
1156         for (; *in; in++) {
1157                 if (*in == '\\' || *in == '\"')
1158                         *out++ = '\\';
1159                 *out++ = *in;
1160         }
1161         *out = '\0';
1162 }
1163
1164 static char mandescr_getconfigjson[] =
1165 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1166 "file by category and contents in JSON format.  This only makes sense to be used\n"
1167 "using rawman over the HTTP interface.\n"
1168 "Variables:\n"
1169 "   Filename: Configuration filename (e.g. foo.conf)\n";
1170
1171 static int action_getconfigjson(struct mansession *s, const struct message *m)
1172 {
1173         struct ast_config *cfg;
1174         const char *fn = astman_get_header(m, "Filename");
1175         char *category = NULL;
1176         struct ast_variable *v;
1177         int comma1 = 0;
1178         char *buf = NULL;
1179         unsigned int buf_len = 0;
1180         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1181
1182         if (ast_strlen_zero(fn)) {
1183                 astman_send_error(s, m, "Filename not specified");
1184                 return 0;
1185         }
1186
1187         if (!(cfg = ast_config_load(fn, config_flags))) {
1188                 astman_send_error(s, m, "Config file not found");
1189                 return 0;
1190         }
1191
1192         buf_len = 512;
1193         buf = alloca(buf_len);
1194
1195         astman_start_ack(s, m);
1196         astman_append(s, "JSON: {");
1197         while ((category = ast_category_browse(cfg, category))) {
1198                 int comma2 = 0;
1199                 if (buf_len < 2 * strlen(category) + 1) {
1200                         buf_len *= 2;
1201                         buf = alloca(buf_len);
1202                 }
1203                 json_escape(buf, category);
1204                 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1205                 if (!comma1)
1206                         comma1 = 1;
1207                 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1208                         if (comma2)
1209                                 astman_append(s, ",");
1210                         if (buf_len < 2 * strlen(v->name) + 1) {
1211                                 buf_len *= 2;
1212                                 buf = alloca(buf_len);
1213                         }
1214                         json_escape(buf, v->name);
1215                         astman_append(s, "\"%s", buf);
1216                         if (buf_len < 2 * strlen(v->value) + 1) {
1217                                 buf_len *= 2;
1218                                 buf = alloca(buf_len);
1219                         }
1220                         json_escape(buf, v->value);
1221                         astman_append(s, "%s\"", buf);
1222                         if (!comma2)
1223                                 comma2 = 1;
1224                 }
1225                 astman_append(s, "]");
1226         }
1227         astman_append(s, "}\r\n\r\n");
1228
1229         ast_config_destroy(cfg);
1230
1231         return 0;
1232 }
1233
1234 /* helper function for action_updateconfig */
1235 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1236 {
1237         int x;
1238         char hdr[40];
1239         const char *action, *cat, *var, *value, *match, *line;
1240         struct ast_category *category;
1241         struct ast_variable *v;
1242
1243         for (x = 0; x < 100000; x++) {
1244                 unsigned int object = 0;
1245
1246                 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1247                 action = astman_get_header(m, hdr);
1248                 if (ast_strlen_zero(action))
1249                         break;
1250                 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1251                 cat = astman_get_header(m, hdr);
1252                 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1253                 var = astman_get_header(m, hdr);
1254                 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1255                 value = astman_get_header(m, hdr);
1256                 if (!ast_strlen_zero(value) && *value == '>') {
1257                         object = 1;
1258                         value++;
1259                 }
1260                 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1261                 match = astman_get_header(m, hdr);
1262                 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
1263                 line = astman_get_header(m, hdr);
1264                 if (!strcasecmp(action, "newcat")) {
1265                         if (ast_strlen_zero(cat))
1266                                 return UNSPECIFIED_CATEGORY;
1267                         if (!(category = ast_category_new(cat, dfn, -1)))
1268                                 return FAILURE_ALLOCATION;
1269                         if (ast_strlen_zero(match)) {
1270                                 ast_category_append(cfg, category);
1271                         } else
1272                                 ast_category_insert(cfg, category, match);
1273                 } else if (!strcasecmp(action, "renamecat")) {
1274                         if (ast_strlen_zero(cat) || ast_strlen_zero(value))
1275                                 return UNSPECIFIED_ARGUMENT;
1276                         if (!(category = ast_category_get(cfg, cat)))
1277                                 return UNKNOWN_CATEGORY;
1278                         ast_category_rename(category, value);
1279                 } else if (!strcasecmp(action, "delcat")) {
1280                         if (ast_strlen_zero(cat))
1281                                 return UNSPECIFIED_CATEGORY;
1282                         if (ast_category_delete(cfg, cat))
1283                                 return FAILURE_DELCAT;
1284                 } else if (!strcasecmp(action, "emptycat")) {
1285                         if (ast_strlen_zero(cat))
1286                                 return UNSPECIFIED_CATEGORY;
1287                         if (ast_category_empty(cfg, cat))
1288                                 return FAILURE_EMPTYCAT;
1289                 } else if (!strcasecmp(action, "update")) {
1290                         if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1291                                 return UNSPECIFIED_ARGUMENT;
1292                         if (!(category = ast_category_get(cfg,cat)))
1293                                 return UNKNOWN_CATEGORY;
1294                         if (ast_variable_update(category, var, value, match, object))
1295                                 return FAILURE_UPDATE;
1296                 } else if (!strcasecmp(action, "delete")) {
1297                         if (ast_strlen_zero(cat) || (ast_strlen_zero(var) && ast_strlen_zero(line)))
1298                                 return UNSPECIFIED_ARGUMENT;
1299                         if (!(category = ast_category_get(cfg, cat)))
1300                                 return UNKNOWN_CATEGORY;
1301                         if (ast_variable_delete(category, var, match, line))
1302                                 return FAILURE_DELETE;
1303                 } else if (!strcasecmp(action, "append")) {
1304                         if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1305                                 return UNSPECIFIED_ARGUMENT;
1306                         if (!(category = ast_category_get(cfg, cat)))
1307                                 return UNKNOWN_CATEGORY;        
1308                         if (!(v = ast_variable_new(var, value, dfn)))
1309                                 return FAILURE_ALLOCATION;
1310                         if (object || (match && !strcasecmp(match, "object")))
1311                                 v->object = 1;
1312                         ast_variable_append(category, v);
1313                 } else if (!strcasecmp(action, "insert")) {
1314                         if (ast_strlen_zero(cat) || ast_strlen_zero(var) || ast_strlen_zero(line))
1315                                 return UNSPECIFIED_ARGUMENT;
1316                         if (!(category = ast_category_get(cfg, cat)))
1317                                 return UNKNOWN_CATEGORY;
1318                         if (!(v = ast_variable_new(var, value, dfn)))
1319                                 return FAILURE_ALLOCATION;
1320                         ast_variable_insert(category, v, line);
1321                 }
1322                 else {
1323                         ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
1324                         return UNKNOWN_ACTION;
1325                 }
1326         }
1327         return 0;
1328 }
1329
1330 static char mandescr_updateconfig[] =
1331 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1332 "configuration elements in Asterisk configuration files.\n"
1333 "Variables (X's represent 6 digit number beginning with 000000):\n"
1334 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
1335 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
1336 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
1337 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
1338 "   Cat-XXXXXX:    Category to operate on\n"
1339 "   Var-XXXXXX:    Variable to work on\n"
1340 "   Value-XXXXXX:  Value to work on\n"
1341 "   Match-XXXXXX:  Extra match required to match line\n"
1342 "   Line-XXXXXX:   Line in category to operate on (used with delete and insert actions)\n";
1343
1344 static int action_updateconfig(struct mansession *s, const struct message *m)
1345 {
1346         struct ast_config *cfg;
1347         const char *sfn = astman_get_header(m, "SrcFilename");
1348         const char *dfn = astman_get_header(m, "DstFilename");
1349         int res;
1350         const char *rld = astman_get_header(m, "Reload");
1351         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1352         enum error_type result;
1353
1354         if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1355                 astman_send_error(s, m, "Filename not specified");
1356                 return 0;
1357         }
1358         if (!(cfg = ast_config_load(sfn, config_flags))) {
1359                 astman_send_error(s, m, "Config file not found");
1360                 return 0;
1361         }
1362         result = handle_updates(s, m, cfg, dfn);
1363         if (!result) {
1364                 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1365                 res = config_text_file_save(dfn, cfg, "Manager");
1366                 ast_config_destroy(cfg);
1367                 if (res) {
1368                         astman_send_error(s, m, "Save of config failed");
1369                         return 0;
1370                 }
1371                 astman_send_ack(s, m, NULL);
1372                 if (!ast_strlen_zero(rld)) {
1373                         if (ast_true(rld))
1374                                 rld = NULL;
1375                         ast_module_reload(rld);
1376                 }
1377         } else {
1378                 ast_config_destroy(cfg);
1379                 switch(result) {
1380                 case UNKNOWN_ACTION:
1381                         astman_send_error(s, m, "Unknown action command");
1382                         break;
1383                 case UNKNOWN_CATEGORY:
1384                         astman_send_error(s, m, "Given category does not exist");
1385                         break;
1386                 case UNSPECIFIED_CATEGORY:
1387                         astman_send_error(s, m, "Category not specified");
1388                         break;
1389                 case UNSPECIFIED_ARGUMENT:
1390                         astman_send_error(s, m, "Problem with category, value, or line (if required)");
1391                         break;
1392                 case FAILURE_ALLOCATION:
1393                         astman_send_error(s, m, "Memory allocation failure, this should not happen");
1394                         break;
1395                 case FAILURE_DELCAT:
1396                         astman_send_error(s, m, "Delete category did not complete successfully");
1397                         break;
1398                 case FAILURE_EMPTYCAT:
1399                         astman_send_error(s, m, "Empty category did not complete successfully");
1400                         break;
1401                 case FAILURE_UPDATE:
1402                         astman_send_error(s, m, "Update did not complete successfully");
1403                         break;
1404                 case FAILURE_DELETE:
1405                         astman_send_error(s, m, "Delete did not complete successfully");
1406                         break;
1407                 case FAILURE_APPEND:
1408                         astman_send_error(s, m, "Append did not complete successfully");
1409                         break;
1410                 }
1411         }
1412         return 0;
1413 }
1414
1415 static char mandescr_createconfig[] =
1416 "Description: A 'CreateConfig' action will create an empty file in the\n"
1417 "configuration directory. This action is intended to be used before an\n"
1418 "UpdateConfig action.\n"
1419 "Variables\n"
1420 "   Filename:   The configuration filename to create (e.g. foo.conf)\n";
1421
1422 static int action_createconfig(struct mansession *s, const struct message *m)
1423 {
1424         int fd;
1425         const char *fn = astman_get_header(m, "Filename");
1426         struct ast_str *filepath = ast_str_alloca(PATH_MAX);
1427         ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
1428         ast_str_append(&filepath, 0, "%s", fn);
1429
1430         if ((fd = open(filepath->str, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
1431                 close(fd);
1432                 astman_send_ack(s, m, "New configuration file created successfully");
1433         } else 
1434                 astman_send_error(s, m, strerror(errno));
1435
1436         return 0;
1437 }
1438
1439 /*! \brief Manager WAITEVENT */
1440 static char mandescr_waitevent[] =
1441 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
1442 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
1443 "session, events will be generated and queued.\n"
1444 "Variables: \n"
1445 "   Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1446
1447 static int action_waitevent(struct mansession *s, const struct message *m)
1448 {
1449         const char *timeouts = astman_get_header(m, "Timeout");
1450         int timeout = -1;
1451         int x;
1452         int needexit = 0;
1453         const char *id = astman_get_header(m, "ActionID");
1454         char idText[256];
1455
1456         if (!ast_strlen_zero(id))
1457                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1458         else
1459                 idText[0] = '\0';
1460
1461         if (!ast_strlen_zero(timeouts)) {
1462                 sscanf(timeouts, "%i", &timeout);
1463                 if (timeout < -1)
1464                         timeout = -1;
1465                 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1466         }
1467
1468         ast_mutex_lock(&s->__lock);
1469         if (s->waiting_thread != AST_PTHREADT_NULL)
1470                 pthread_kill(s->waiting_thread, SIGURG);
1471
1472         if (s->managerid) { /* AMI-over-HTTP session */
1473                 /*
1474                  * Make sure the timeout is within the expire time of the session,
1475                  * as the client will likely abort the request if it does not see
1476                  * data coming after some amount of time.
1477                  */
1478                 time_t now = time(NULL);
1479                 int max = s->sessiontimeout - now - 10;
1480
1481                 if (max < 0)    /* We are already late. Strange but possible. */
1482                         max = 0;
1483                 if (timeout < 0 || timeout > max)
1484                         timeout = max;
1485                 if (!s->send_events)    /* make sure we record events */
1486                         s->send_events = -1;
1487         }
1488         ast_mutex_unlock(&s->__lock);
1489
1490         /* XXX should this go inside the lock ? */
1491         s->waiting_thread = pthread_self();     /* let new events wake up this thread */
1492         ast_debug(1, "Starting waiting for an event!\n");
1493
1494         for (x = 0; x < timeout || timeout < 0; x++) {
1495                 ast_mutex_lock(&s->__lock);
1496                 if (NEW_EVENT(s))
1497                         needexit = 1;
1498                 /* We can have multiple HTTP session point to the same mansession entry.
1499                  * The way we deal with it is not very nice: newcomers kick out the previous
1500                  * HTTP session. XXX this needs to be improved.
1501                  */
1502                 if (s->waiting_thread != pthread_self())
1503                         needexit = 1;
1504                 if (s->needdestroy)
1505                         needexit = 1;
1506                 ast_mutex_unlock(&s->__lock);
1507                 if (needexit)
1508                         break;
1509                 if (s->managerid == 0) {        /* AMI session */
1510                         if (ast_wait_for_input(s->fd, 1000))
1511                                 break;
1512                 } else {        /* HTTP session */
1513                         sleep(1);
1514                 }
1515         }
1516         ast_debug(1, "Finished waiting for an event!\n");
1517         ast_mutex_lock(&s->__lock);
1518         if (s->waiting_thread == pthread_self()) {
1519                 struct eventqent *eqe;
1520                 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1521                 while ( (eqe = NEW_EVENT(s)) ) {
1522                         ref_event(eqe);
1523                         if (((s->readperm & eqe->category) == eqe->category) &&
1524                             ((s->send_events & eqe->category) == eqe->category)) {
1525                                 astman_append(s, "%s", eqe->eventdata);
1526                         }
1527                         s->last_ev = unref_event(s->last_ev);
1528                 }
1529                 astman_append(s,
1530                         "Event: WaitEventComplete\r\n"
1531                         "%s"
1532                         "\r\n", idText);
1533                 s->waiting_thread = AST_PTHREADT_NULL;
1534         } else {
1535                 ast_debug(1, "Abandoning event request!\n");
1536         }
1537         ast_mutex_unlock(&s->__lock);
1538         return 0;
1539 }
1540
1541 static char mandescr_listcommands[] =
1542 "Description: Returns the action name and synopsis for every\n"
1543 "  action that is available to the user\n"
1544 "Variables: NONE\n";
1545
1546 /*! \note The actionlock is read-locked by the caller of this function */
1547 static int action_listcommands(struct mansession *s, const struct message *m)
1548 {
1549         struct manager_action *cur;
1550         struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1551
1552         astman_start_ack(s, m);
1553         AST_RWLIST_TRAVERSE(&actions, cur, list) {
1554                 if (s->writeperm & cur->authority || cur->authority == 0)
1555                         astman_append(s, "%s: %s (Priv: %s)\r\n",
1556                                 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1557         }
1558         astman_append(s, "\r\n");
1559
1560         return 0;
1561 }
1562
1563 static char mandescr_events[] =
1564 "Description: Enable/Disable sending of events to this manager\n"
1565 "  client.\n"
1566 "Variables:\n"
1567 "       EventMask: 'on' if all events should be sent,\n"
1568 "               'off' if no events should be sent,\n"
1569 "               'system,call,log' to select which flags events should have to be sent.\n";
1570
1571 static int action_events(struct mansession *s, const struct message *m)
1572 {
1573         const char *mask = astman_get_header(m, "EventMask");
1574         int res;
1575
1576         res = set_eventmask(s, mask);
1577         if (res > 0)
1578                 astman_send_response(s, m, "Success", "Events: On\r\n");
1579         else if (res == 0)
1580                 astman_send_response(s, m, "Success", "Events: Off\r\n");
1581
1582         return 0;
1583 }
1584
1585 static char mandescr_logoff[] =
1586 "Description: Logoff this manager session\n"
1587 "Variables: NONE\n";
1588
1589 static int action_logoff(struct mansession *s, const struct message *m)
1590 {
1591         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1592         return -1;
1593 }
1594
1595 static int action_login(struct mansession *s, const struct message *m)
1596 {
1597         if (authenticate(s, m)) {
1598                 sleep(1);
1599                 astman_send_error(s, m, "Authentication failed");
1600                 return -1;
1601         }
1602         s->authenticated = 1;
1603         if (manager_displayconnects(s))
1604                 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1605         ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1606         astman_send_ack(s, m, "Authentication accepted");
1607         return 0;
1608 }
1609
1610 static int action_challenge(struct mansession *s, const struct message *m)
1611 {
1612         const char *authtype = astman_get_header(m, "AuthType");
1613
1614         if (!strcasecmp(authtype, "MD5")) {
1615                 if (ast_strlen_zero(s->challenge))
1616                         snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1617                 ast_mutex_lock(&s->__lock);
1618                 astman_start_ack(s, m);
1619                 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1620                 ast_mutex_unlock(&s->__lock);
1621         } else {
1622                 astman_send_error(s, m, "Must specify AuthType");
1623         }
1624         return 0;
1625 }
1626
1627 static char mandescr_hangup[] =
1628 "Description: Hangup a channel\n"
1629 "Variables: \n"
1630 "       Channel: The channel name to be hungup\n";
1631
1632 static int action_hangup(struct mansession *s, const struct message *m)
1633 {
1634         struct ast_channel *c = NULL;
1635         const char *name = astman_get_header(m, "Channel");
1636         if (ast_strlen_zero(name)) {
1637                 astman_send_error(s, m, "No channel specified");
1638                 return 0;
1639         }
1640         c = ast_get_channel_by_name_locked(name);
1641         if (!c) {
1642                 astman_send_error(s, m, "No such channel");
1643                 return 0;
1644         }
1645         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1646         ast_channel_unlock(c);
1647         astman_send_ack(s, m, "Channel Hungup");
1648         return 0;
1649 }
1650
1651 static char mandescr_setvar[] =
1652 "Description: Set a global or local channel variable.\n"
1653 "Variables: (Names marked with * are required)\n"
1654 "       Channel: Channel to set variable for\n"
1655 "       *Variable: Variable name\n"
1656 "       *Value: Value\n";
1657
1658 static int action_setvar(struct mansession *s, const struct message *m)
1659 {
1660         struct ast_channel *c = NULL;
1661         const char *name = astman_get_header(m, "Channel");
1662         const char *varname = astman_get_header(m, "Variable");
1663         const char *varval = astman_get_header(m, "Value");
1664
1665         if (ast_strlen_zero(varname)) {
1666                 astman_send_error(s, m, "No variable specified");
1667                 return 0;
1668         }
1669
1670         if (!ast_strlen_zero(name)) {
1671                 c = ast_get_channel_by_name_locked(name);
1672                 if (!c) {
1673                         astman_send_error(s, m, "No such channel");
1674                         return 0;
1675                 }
1676         }
1677
1678         pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1679
1680         if (c)
1681                 ast_channel_unlock(c);
1682
1683         astman_send_ack(s, m, "Variable Set");
1684
1685         return 0;
1686 }
1687
1688 static char mandescr_getvar[] =
1689 "Description: Get the value of a global or local channel variable.\n"
1690 "Variables: (Names marked with * are required)\n"
1691 "       Channel: Channel to read variable from\n"
1692 "       *Variable: Variable name\n"
1693 "       ActionID: Optional Action id for message matching.\n";
1694
1695 static int action_getvar(struct mansession *s, const struct message *m)
1696 {
1697         struct ast_channel *c = NULL;
1698         const char *name = astman_get_header(m, "Channel");
1699         const char *varname = astman_get_header(m, "Variable");
1700         char *varval;
1701         char workspace[1024] = "";
1702
1703         if (ast_strlen_zero(varname)) {
1704                 astman_send_error(s, m, "No variable specified");
1705                 return 0;
1706         }
1707
1708         if (!ast_strlen_zero(name)) {
1709                 c = ast_get_channel_by_name_locked(name);
1710                 if (!c) {
1711                         astman_send_error(s, m, "No such channel");
1712                         return 0;
1713                 }
1714         }
1715
1716         if (varname[strlen(varname) - 1] == ')') {
1717                 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1718                 varval = workspace;
1719         } else {
1720                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1721         }
1722
1723         if (c)
1724                 ast_channel_unlock(c);
1725         astman_start_ack(s, m);
1726         astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1727
1728         return 0;
1729 }
1730
1731
1732 /*! \brief Manager "status" command to show channels */
1733 /* Needs documentation... */
1734 static int action_status(struct mansession *s, const struct message *m)
1735 {
1736         const char *name = astman_get_header(m, "Channel");
1737         struct ast_channel *c;
1738         char bridge[256];
1739         struct timeval now = ast_tvnow();
1740         long elapsed_seconds = 0;
1741         int channels = 0;
1742         int all = ast_strlen_zero(name); /* set if we want all channels */
1743         const char *id = astman_get_header(m, "ActionID");
1744         char idText[256];
1745
1746         if (!ast_strlen_zero(id))
1747                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1748         else
1749                 idText[0] = '\0';
1750
1751         if (all)
1752                 c = ast_channel_walk_locked(NULL);
1753         else {
1754                 c = ast_get_channel_by_name_locked(name);
1755                 if (!c) {
1756                         astman_send_error(s, m, "No such channel");
1757                         return 0;
1758                 }
1759         }
1760         astman_send_ack(s, m, "Channel status will follow");
1761
1762         /* if we look by name, we break after the first iteration */
1763         while (c) {
1764                 channels++;
1765                 if (c->_bridge)
1766                         snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1767                 else
1768                         bridge[0] = '\0';
1769                 if (c->pbx) {
1770                         if (c->cdr) {
1771                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1772                         }
1773                         astman_append(s,
1774                         "Event: Status\r\n"
1775                         "Privilege: Call\r\n"
1776                         "Channel: %s\r\n"
1777                         "CallerIDNum: %s\r\n"
1778                         "CallerIDName: %s\r\n"
1779                         "Accountcode: %s\r\n"
1780                         "ChannelState: %d\r\n"
1781                         "ChannelStateDesc: %s\r\n"
1782                         "Context: %s\r\n"
1783                         "Extension: %s\r\n"
1784                         "Priority: %d\r\n"
1785                         "Seconds: %ld\r\n"
1786                         "%s"
1787                         "Uniqueid: %s\r\n"
1788                         "%s"
1789                         "\r\n",
1790                         c->name,
1791                         S_OR(c->cid.cid_num, ""),
1792                         S_OR(c->cid.cid_name, ""),
1793                         c->accountcode,
1794                         c->_state,
1795                         ast_state2str(c->_state), c->context,
1796                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1797                 } else {
1798                         astman_append(s,
1799                         "Event: Status\r\n"
1800                         "Privilege: Call\r\n"
1801                         "Channel: %s\r\n"
1802                         "CallerIDNum: %s\r\n"
1803                         "CallerIDName: %s\r\n"
1804                         "Account: %s\r\n"
1805                         "State: %s\r\n"
1806                         "%s"
1807                         "Uniqueid: %s\r\n"
1808                         "%s"
1809                         "\r\n",
1810                         c->name,
1811                         S_OR(c->cid.cid_num, "<unknown>"),
1812                         S_OR(c->cid.cid_name, "<unknown>"),
1813                         c->accountcode,
1814                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
1815                 }
1816                 ast_channel_unlock(c);
1817                 if (!all)
1818                         break;
1819                 c = ast_channel_walk_locked(c);
1820         }
1821         astman_append(s,
1822         "Event: StatusComplete\r\n"
1823         "%s"
1824         "Items: %d\r\n"
1825         "\r\n", idText, channels);
1826         return 0;
1827 }
1828
1829 static char mandescr_sendtext[] =
1830 "Description: Sends A Text Message while in a call.\n"
1831 "Variables: (Names marked with * are required)\n"
1832 "       *Channel: Channel to send message to\n"
1833 "       *Message: Message to send\n"
1834 "       ActionID: Optional Action id for message matching.\n";
1835
1836 static int action_sendtext(struct mansession *s, const struct message *m)
1837 {
1838         struct ast_channel *c = NULL;
1839         const char *name = astman_get_header(m, "Channel");
1840         const char *textmsg = astman_get_header(m, "Message");
1841         int res = 0;
1842
1843         if (ast_strlen_zero(name)) {
1844                 astman_send_error(s, m, "No channel specified");
1845                 return 0;
1846         }
1847
1848         if (ast_strlen_zero(textmsg)) {
1849                 astman_send_error(s, m, "No Message specified");
1850                 return 0;
1851         }
1852
1853         c = ast_get_channel_by_name_locked(name);
1854         if (!c) {
1855                 astman_send_error(s, m, "No such channel");
1856                 return 0;
1857         }
1858
1859         res = ast_sendtext(c, textmsg);
1860         ast_channel_unlock(c);
1861         
1862         if (res > 0)
1863                 astman_send_ack(s, m, "Success");
1864         else
1865                 astman_send_error(s, m, "Failure");
1866         
1867         return res;
1868 }
1869
1870 static char mandescr_redirect[] =
1871 "Description: Redirect (transfer) a call.\n"
1872 "Variables: (Names marked with * are required)\n"
1873 "       *Channel: Channel to redirect\n"
1874 "       ExtraChannel: Second call leg to transfer (optional)\n"
1875 "       *Exten: Extension to transfer to\n"
1876 "       *Context: Context to transfer to\n"
1877 "       *Priority: Priority to transfer to\n"
1878 "       ActionID: Optional Action id for message matching.\n";
1879
1880 /*! \brief  action_redirect: The redirect manager command */
1881 static int action_redirect(struct mansession *s, const struct message *m)
1882 {
1883         const char *name = astman_get_header(m, "Channel");
1884         const char *name2 = astman_get_header(m, "ExtraChannel");
1885         const char *exten = astman_get_header(m, "Exten");
1886         const char *context = astman_get_header(m, "Context");
1887         const char *priority = astman_get_header(m, "Priority");
1888         struct ast_channel *chan, *chan2 = NULL;
1889         int pi = 0;
1890         int res;
1891
1892         if (ast_strlen_zero(name)) {
1893                 astman_send_error(s, m, "Channel not specified");
1894                 return 0;
1895         }
1896         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1897                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1898                         astman_send_error(s, m, "Invalid priority\n");
1899                         return 0;
1900                 }
1901         }
1902         /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1903         chan = ast_get_channel_by_name_locked(name);
1904         if (!chan) {
1905                 char buf[256];
1906                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1907                 astman_send_error(s, m, buf);
1908                 return 0;
1909         }
1910         if (ast_check_hangup(chan)) {
1911                 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1912                 ast_channel_unlock(chan);
1913                 return 0;
1914         }
1915         if (!ast_strlen_zero(name2))
1916                 chan2 = ast_get_channel_by_name_locked(name2);
1917         if (chan2 && ast_check_hangup(chan2)) {
1918                 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1919                 ast_channel_unlock(chan);
1920                 ast_channel_unlock(chan2);
1921                 return 0;
1922         }
1923         res = ast_async_goto(chan, context, exten, pi);
1924         if (!res) {
1925                 if (!ast_strlen_zero(name2)) {
1926                         if (chan2)
1927                                 res = ast_async_goto(chan2, context, exten, pi);
1928                         else
1929                                 res = -1;
1930                         if (!res)
1931                                 astman_send_ack(s, m, "Dual Redirect successful");
1932                         else
1933                                 astman_send_error(s, m, "Secondary redirect failed");
1934                 } else
1935                         astman_send_ack(s, m, "Redirect successful");
1936         } else
1937                 astman_send_error(s, m, "Redirect failed");
1938         if (chan)
1939                 ast_channel_unlock(chan);
1940         if (chan2)
1941                 ast_channel_unlock(chan2);
1942         return 0;
1943 }
1944
1945 static char mandescr_atxfer[] =
1946 "Description: Attended transfer.\n"
1947 "Variables: (Names marked with * are required)\n"
1948 "       *Channel: Transferer's channel\n"
1949 "       *Exten: Extension to transfer to\n"
1950 "       *Context: Context to transfer to\n"
1951 "       *Priority: Priority to transfer to\n"
1952 "       ActionID: Optional Action id for message matching.\n";
1953
1954 static int action_atxfer(struct mansession *s, const struct message *m)
1955 {
1956         const char *name = astman_get_header(m, "Channel");
1957         const char *exten = astman_get_header(m, "Exten");
1958         const char *context = astman_get_header(m, "Context");
1959         const char *priority = astman_get_header(m, "Priority");
1960         struct ast_channel *chan = NULL;
1961         struct ast_call_feature *atxfer_feature = NULL;
1962         char *feature_code = NULL;
1963         int priority_int = 0;
1964
1965         if (ast_strlen_zero(name)) { 
1966                 astman_send_error(s, m, "No channel specified\n");
1967                 return 0;
1968         }
1969         if (ast_strlen_zero(exten)) {
1970                 astman_send_error(s, m, "No extension specified\n");
1971                 return 0;
1972         }
1973         if (ast_strlen_zero(context)) {
1974                 astman_send_error(s, m, "No context specified\n");
1975                 return 0;
1976         }
1977         if (ast_strlen_zero(priority)) {
1978                 astman_send_error(s, m, "No priority specified\n");
1979                 return 0;
1980         }
1981
1982         if (sscanf(priority, "%d", &priority_int) != 1 && (priority_int = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1983                 astman_send_error(s, m, "Invalid Priority\n");
1984                 return 0;
1985         }
1986
1987         if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
1988                 astman_send_error(s, m, "No attended transfer feature found\n");
1989                 return 0;
1990         }
1991
1992         if (!(chan = ast_get_channel_by_name_locked(name))) {
1993                 astman_send_error(s, m, "Channel specified does not exist\n");
1994                 return 0;
1995         }
1996
1997         for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
1998                 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
1999                 ast_queue_frame(chan, &f);
2000         }
2001
2002         for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
2003                 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2004                 ast_queue_frame(chan, &f);
2005         }
2006
2007         astman_send_ack(s, m, "Atxfer successfully queued\n");
2008         ast_channel_unlock(chan);
2009
2010         return 0;
2011 }
2012
2013 static char mandescr_command[] =
2014 "Description: Run a CLI command.\n"
2015 "Variables: (Names marked with * are required)\n"
2016 "       *Command: Asterisk CLI command to run\n"
2017 "       ActionID: Optional Action id for message matching.\n";
2018
2019 /*! \brief  Manager command "command" - execute CLI command */
2020 static int action_command(struct mansession *s, const struct message *m)
2021 {
2022         const char *cmd = astman_get_header(m, "Command");
2023         const char *id = astman_get_header(m, "ActionID");
2024         char *buf, *final_buf;
2025         char template[] = "/tmp/ast-ami-XXXXXX";        /* template for temporary file */
2026         int fd = mkstemp(template), i = 0;
2027         off_t l;
2028
2029         for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
2030                 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
2031                         astman_send_error(s, m, "Command blacklisted");
2032                         return 0;
2033                 }
2034         }
2035
2036         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
2037         if (!ast_strlen_zero(id))
2038                 astman_append(s, "ActionID: %s\r\n", id);
2039         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
2040         ast_cli_command(fd, cmd);       /* XXX need to change this to use a FILE * */
2041         l = lseek(fd, 0, SEEK_END);     /* how many chars available */
2042
2043         /* This has a potential to overflow the stack.  Hence, use the heap. */
2044         buf = ast_calloc(1, l + 1);
2045         final_buf = ast_calloc(1, l + 1);
2046         if (buf) {
2047                 lseek(fd, 0, SEEK_SET);
2048                 read(fd, buf, l);
2049                 buf[l] = '\0';
2050                 if (final_buf) {
2051                         term_strip(final_buf, buf, l);
2052                         final_buf[l] = '\0';
2053                 }
2054                 astman_append(s, S_OR(final_buf, buf));
2055                 ast_free(buf);
2056         }
2057         close(fd);
2058         unlink(template);
2059         astman_append(s, "--END COMMAND--\r\n\r\n");
2060         if (final_buf)
2061                 ast_free(final_buf);
2062         return 0;
2063 }
2064
2065 /* helper function for originate */
2066 struct fast_originate_helper {
2067         char tech[AST_MAX_EXTENSION];
2068         char data[AST_MAX_EXTENSION];
2069         int timeout;
2070         char app[AST_MAX_APP];
2071         char appdata[AST_MAX_EXTENSION];
2072         char cid_name[AST_MAX_EXTENSION];
2073         char cid_num[AST_MAX_EXTENSION];
2074         char context[AST_MAX_CONTEXT];
2075         char exten[AST_MAX_EXTENSION];
2076         char idtext[AST_MAX_EXTENSION];
2077         char account[AST_MAX_ACCOUNT_CODE];
2078         int priority;
2079         struct ast_variable *vars;
2080 };
2081
2082 static void *fast_originate(void *data)
2083 {
2084         struct fast_originate_helper *in = data;
2085         int res;
2086         int reason = 0;
2087         struct ast_channel *chan = NULL;
2088         char requested_channel[AST_CHANNEL_NAME];
2089
2090         if (!ast_strlen_zero(in->app)) {
2091                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
2092                         S_OR(in->cid_num, NULL),
2093                         S_OR(in->cid_name, NULL),
2094                         in->vars, in->account, &chan);
2095         } else {
2096                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
2097                         S_OR(in->cid_num, NULL),
2098                         S_OR(in->cid_name, NULL),
2099                         in->vars, in->account, &chan);
2100         }
2101
2102         if (!chan)
2103                 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);     
2104         /* Tell the manager what happened with the channel */
2105         manager_event(EVENT_FLAG_CALL, "OriginateResponse",
2106                 "%s"
2107                 "Response: %s\r\n"
2108                 "Channel: %s\r\n"
2109                 "Context: %s\r\n"
2110                 "Exten: %s\r\n"
2111                 "Reason: %d\r\n"
2112                 "Uniqueid: %s\r\n"
2113                 "CallerIDNum: %s\r\n"
2114                 "CallerIDName: %s\r\n",
2115                 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason, 
2116                 chan ? chan->uniqueid : "<null>",
2117                 S_OR(in->cid_num, "<unknown>"),
2118                 S_OR(in->cid_name, "<unknown>")
2119                 );
2120
2121         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2122         if (chan)
2123                 ast_channel_unlock(chan);
2124         ast_free(in);
2125         return NULL;
2126 }
2127
2128 static char mandescr_originate[] =
2129 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
2130 "  Application/Data\n"
2131 "Variables: (Names marked with * are required)\n"
2132 "       *Channel: Channel name to call\n"
2133 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
2134 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
2135 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
2136 "       Application: Application to use\n"
2137 "       Data: Data to use (requires 'Application')\n"
2138 "       Timeout: How long to wait for call to be answered (in ms)\n"
2139 "       CallerID: Caller ID to be set on the outgoing channel\n"
2140 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
2141 "       Account: Account code\n"
2142 "       Async: Set to 'true' for fast origination\n";
2143
2144 static int action_originate(struct mansession *s, const struct message *m)
2145 {
2146         const char *name = astman_get_header(m, "Channel");
2147         const char *exten = astman_get_header(m, "Exten");
2148         const char *context = astman_get_header(m, "Context");
2149         const char *priority = astman_get_header(m, "Priority");
2150         const char *timeout = astman_get_header(m, "Timeout");
2151         const char *callerid = astman_get_header(m, "CallerID");
2152         const char *account = astman_get_header(m, "Account");
2153         const char *app = astman_get_header(m, "Application");
2154         const char *appdata = astman_get_header(m, "Data");
2155         const char *async = astman_get_header(m, "Async");
2156         const char *id = astman_get_header(m, "ActionID");
2157         struct ast_variable *vars = astman_get_variables(m);
2158         char *tech, *data;
2159         char *l = NULL, *n = NULL;
2160         int pi = 0;
2161         int res;
2162         int to = 30000;
2163         int reason = 0;
2164         char tmp[256];
2165         char tmp2[256];
2166
2167         pthread_t th;
2168         if (!name) {
2169                 astman_send_error(s, m, "Channel not specified");
2170                 return 0;
2171         }
2172         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2173                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2174                         astman_send_error(s, m, "Invalid priority\n");
2175                         return 0;
2176                 }
2177         }
2178         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2179                 astman_send_error(s, m, "Invalid timeout\n");
2180                 return 0;
2181         }
2182         ast_copy_string(tmp, name, sizeof(tmp));
2183         tech = tmp;
2184         data = strchr(tmp, '/');
2185         if (!data) {
2186                 astman_send_error(s, m, "Invalid channel\n");
2187                 return 0;
2188         }
2189         *data++ = '\0';
2190         ast_copy_string(tmp2, callerid, sizeof(tmp2));
2191         ast_callerid_parse(tmp2, &n, &l);
2192         if (n) {
2193                 if (ast_strlen_zero(n))
2194                         n = NULL;
2195         }
2196         if (l) {
2197                 ast_shrink_phone_number(l);
2198                 if (ast_strlen_zero(l))
2199                         l = NULL;
2200         }
2201         if (ast_true(async)) {
2202                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2203                 if (!fast) {
2204                         res = -1;
2205                 } else {
2206                         if (!ast_strlen_zero(id))
2207                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2208                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2209                         ast_copy_string(fast->data, data, sizeof(fast->data));
2210                         ast_copy_string(fast->app, app, sizeof(fast->app));
2211                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2212                         if (l)
2213                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2214                         if (n)
2215                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2216                         fast->vars = vars;
2217                         ast_copy_string(fast->context, context, sizeof(fast->context));
2218                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2219                         ast_copy_string(fast->account, account, sizeof(fast->account));
2220                         fast->timeout = to;
2221                         fast->priority = pi;
2222                         if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2223                                 res = -1;
2224                         } else {
2225                                 res = 0;
2226                         }
2227                 }
2228         } else if (!ast_strlen_zero(app)) {
2229                 /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
2230                 if (!(s->writeperm & EVENT_FLAG_SYSTEM)
2231                         && (
2232                                 strcasestr(app, "system") == 0 || /* System(rm -rf /)
2233                                                                      TrySystem(rm -rf /)       */
2234                                 strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
2235                                                                      TryExec(System(rm -rf /)) */
2236                                 strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
2237                                                                      EAGI(/bin/rm,-rf /)       */
2238                                 strstr(appdata, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
2239                                 strstr(appdata, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
2240                                 )) {
2241                         astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
2242                         return 0;
2243                 }
2244                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2245         } else {
2246                 if (exten && context && pi)
2247                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2248                 else {
2249                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2250                         return 0;
2251                 }
2252         }
2253         if (!res)
2254                 astman_send_ack(s, m, "Originate successfully queued");
2255         else
2256                 astman_send_error(s, m, "Originate failed");
2257         return 0;
2258 }
2259
2260 /*! \brief Help text for manager command mailboxstatus
2261  */
2262 static char mandescr_mailboxstatus[] =
2263 "Description: Checks a voicemail account for status.\n"
2264 "Variables: (Names marked with * are required)\n"
2265 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2266 "       ActionID: Optional ActionID for message matching.\n"
2267 "Returns number of messages.\n"
2268 "       Message: Mailbox Status\n"
2269 "       Mailbox: <mailboxid>\n"
2270 "       Waiting: <count>\n"
2271 "\n";
2272
2273 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2274 {
2275         const char *mailbox = astman_get_header(m, "Mailbox");
2276         int ret;
2277
2278         if (ast_strlen_zero(mailbox)) {
2279                 astman_send_error(s, m, "Mailbox not specified");
2280                 return 0;
2281         }
2282         ret = ast_app_has_voicemail(mailbox, NULL);
2283         astman_start_ack(s, m);
2284         astman_append(s, "Message: Mailbox Status\r\n"
2285                          "Mailbox: %s\r\n"
2286                          "Waiting: %d\r\n\r\n", mailbox, ret);
2287         return 0;
2288 }
2289
2290 static char mandescr_mailboxcount[] =
2291 "Description: Checks a voicemail account for new messages.\n"
2292 "Variables: (Names marked with * are required)\n"
2293 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2294 "       ActionID: Optional ActionID for message matching.\n"
2295 "Returns number of new and old messages.\n"
2296 "       Message: Mailbox Message Count\n"
2297 "       Mailbox: <mailboxid>\n"
2298 "       NewMessages: <count>\n"
2299 "       OldMessages: <count>\n"
2300 "\n";
2301 static int action_mailboxcount(struct mansession *s, const struct message *m)
2302 {
2303         const char *mailbox = astman_get_header(m, "Mailbox");
2304         int newmsgs = 0, oldmsgs = 0;
2305
2306         if (ast_strlen_zero(mailbox)) {
2307                 astman_send_error(s, m, "Mailbox not specified");
2308                 return 0;
2309         }
2310         ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2311         astman_start_ack(s, m);
2312         astman_append(s,   "Message: Mailbox Message Count\r\n"
2313                            "Mailbox: %s\r\n"
2314                            "NewMessages: %d\r\n"
2315                            "OldMessages: %d\r\n"
2316                            "\r\n",
2317                            mailbox, newmsgs, oldmsgs);
2318         return 0;
2319 }
2320
2321 static char mandescr_extensionstate[] =
2322 "Description: Report the extension state for given extension.\n"
2323 "  If the extension has a hint, will use devicestate to check\n"
2324 "  the status of the device connected to the extension.\n"
2325 "Variables: (Names marked with * are required)\n"
2326 "       *Exten: Extension to check state on\n"
2327 "       *Context: Context for extension\n"
2328 "       ActionId: Optional ID for this transaction\n"
2329 "Will return an \"Extension Status\" message.\n"
2330 "The response will include the hint for the extension and the status.\n";
2331
2332 static int action_extensionstate(struct mansession *s, const struct message *m)
2333 {
2334         const char *exten = astman_get_header(m, "Exten");
2335         const char *context = astman_get_header(m, "Context");
2336         char hint[256] = "";
2337         int status;
2338         if (ast_strlen_zero(exten)) {
2339                 astman_send_error(s, m, "Extension not specified");
2340                 return 0;
2341         }
2342         if (ast_strlen_zero(context))
2343                 context = "default";
2344         status = ast_extension_state(NULL, context, exten);
2345         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2346         astman_start_ack(s, m);
2347         astman_append(s,   "Message: Extension Status\r\n"
2348                            "Exten: %s\r\n"
2349                            "Context: %s\r\n"
2350                            "Hint: %s\r\n"
2351                            "Status: %d\r\n\r\n",
2352                            exten, context, hint, status);
2353         return 0;
2354 }
2355
2356 static char mandescr_timeout[] =
2357 "Description: Hangup a channel after a certain time.\n"
2358 "Variables: (Names marked with * are required)\n"
2359 "       *Channel: Channel name to hangup\n"
2360 "       *Timeout: Maximum duration of the call (sec)\n"
2361 "Acknowledges set time with 'Timeout Set' message\n";
2362
2363 static int action_timeout(struct mansession *s, const struct message *m)
2364 {
2365         struct ast_channel *c;
2366         const char *name = astman_get_header(m, "Channel");
2367         int timeout = atoi(astman_get_header(m, "Timeout"));
2368
2369         if (ast_strlen_zero(name)) {
2370                 astman_send_error(s, m, "No channel specified");
2371                 return 0;
2372         }
2373         if (!timeout) {
2374                 astman_send_error(s, m, "No timeout specified");
2375                 return 0;
2376         }
2377         c = ast_get_channel_by_name_locked(name);
2378         if (!c) {
2379                 astman_send_error(s, m, "No such channel");
2380                 return 0;
2381         }
2382         ast_channel_setwhentohangup(c, timeout);
2383         ast_channel_unlock(c);
2384         astman_send_ack(s, m, "Timeout Set");
2385         return 0;
2386 }
2387
2388 /*!
2389  * Send any applicable events to the client listening on this socket.
2390  * Wait only for a finite time on each event, and drop all events whether
2391  * they are successfully sent or not.
2392  */
2393 static int process_events(struct mansession *s)
2394 {
2395         int ret = 0;
2396
2397         ast_mutex_lock(&s->__lock);
2398         if (s->f != NULL) {
2399                 struct eventqent *eqe;
2400
2401                 while ( (eqe = NEW_EVENT(s)) ) {
2402                         ref_event(eqe);
2403                         if (!ret && s->authenticated &&
2404                             (s->readperm & eqe->category) == eqe->category &&
2405                             (s->send_events & eqe->category) == eqe->category) {
2406                                 if (send_string(s, eqe->eventdata) < 0)
2407                                         ret = -1;       /* don't send more */
2408                         }
2409                         s->last_ev = unref_event(s->last_ev);
2410                 }
2411         }
2412         ast_mutex_unlock(&s->__lock);
2413         return ret;
2414 }
2415
2416 static char mandescr_userevent[] =
2417 "Description: Send an event to manager sessions.\n"
2418 "Variables: (Names marked with * are required)\n"
2419 "       *UserEvent: EventStringToSend\n"
2420 "       Header1: Content1\n"
2421 "       HeaderN: ContentN\n";
2422
2423 static int action_userevent(struct mansession *s, const struct message *m)
2424 {
2425         const char *event = astman_get_header(m, "UserEvent");
2426         char body[2048] = "";
2427         int x, bodylen = 0;
2428         for (x = 0; x < m->hdrcount; x++) {
2429                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2430                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2431                         bodylen += strlen(m->headers[x]);
2432                         ast_copy_string(body + bodylen, "\r\n", 3);
2433                         bodylen += 2;
2434                 }
2435         }
2436
2437         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2438         return 0;
2439 }
2440
2441 static char mandescr_coresettings[] =
2442 "Description: Query for Core PBX settings.\n"
2443 "Variables: (Names marked with * are optional)\n"
2444 "       *ActionID: ActionID of this transaction\n";
2445
2446 /*! \brief Show PBX core settings information */
2447 static int action_coresettings(struct mansession *s, const struct message *m)
2448 {
2449         const char *actionid = astman_get_header(m, "ActionID");
2450         char idText[150];
2451
2452         if (!ast_strlen_zero(actionid))
2453                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2454         else
2455                 idText[0] = '\0';
2456
2457         astman_append(s, "Response: Success\r\n"
2458                         "%s"
2459                         "AMIversion: %s\r\n"
2460                         "AsteriskVersion: %s\r\n"
2461                         "SystemName: %s\r\n"
2462                         "CoreMaxCalls: %d\r\n"
2463                         "CoreMaxLoadAvg: %f\r\n"
2464                         "CoreRunUser: %s\r\n"
2465                         "CoreRunGroup: %s\r\n"
2466                         "CoreMaxFilehandles: %d\r\n" 
2467                         "CoreRealTimeEnabled: %s\r\n"
2468                         "CoreCDRenabled: %s\r\n"
2469                         "CoreHTTPenabled: %s\r\n"
2470                         "\r\n",
2471                         idText,
2472                         AMI_VERSION,
2473                         ast_get_version(), 
2474                         ast_config_AST_SYSTEM_NAME,
2475                         option_maxcalls,
2476                         option_maxload,
2477                         ast_config_AST_RUN_USER,
2478                         ast_config_AST_RUN_GROUP,
2479                         option_maxfiles,
2480                         ast_realtime_enabled() ? "Yes" : "No",
2481                         check_cdr_enabled() ? "Yes" : "No",
2482                         check_webmanager_enabled() ? "Yes" : "No"
2483                         );
2484         return 0;
2485 }
2486
2487 static char mandescr_corestatus[] =
2488 "Description: Query for Core PBX status.\n"
2489 "Variables: (Names marked with * are optional)\n"
2490 "       *ActionID: ActionID of this transaction\n";
2491
2492 /*! \brief Show PBX core status information */
2493 static int action_corestatus(struct mansession *s, const struct message *m)
2494 {
2495         const char *actionid = astman_get_header(m, "ActionID");
2496         char idText[150];
2497         char startuptime[150];
2498         char reloadtime[150];
2499         struct ast_tm tm;
2500
2501         if (!ast_strlen_zero(actionid))
2502                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2503         else
2504                 idText[0] = '\0';
2505
2506         ast_localtime(&ast_startuptime, &tm, NULL);
2507         ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2508         ast_localtime(&ast_lastreloadtime, &tm, NULL);
2509         ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2510
2511         astman_append(s, "Response: Success\r\n"
2512                         "%s"
2513                         "CoreStartupTime: %s\r\n"
2514                         "CoreReloadTime: %s\r\n"
2515                         "CoreCurrentCalls: %d\r\n"
2516                         "\r\n",
2517                         idText,
2518                         startuptime,
2519                         reloadtime,
2520                         ast_active_channels()
2521                         );
2522         return 0;
2523 }
2524
2525 static char mandescr_reload[] =
2526 "Description: Send a reload event.\n"
2527 "Variables: (Names marked with * are optional)\n"
2528 "       *ActionID: ActionID of this transaction\n"
2529 "       *Module: Name of the module to reload\n";
2530
2531 /*! \brief Send a reload event */
2532 static int action_reload(struct mansession *s, const struct message *m)
2533 {
2534         const char *module = astman_get_header(m, "Module");
2535         int res = ast_module_reload(S_OR(module, NULL));
2536
2537         if (res == 2)
2538                 astman_send_ack(s, m, "Module Reloaded");
2539         else
2540                 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2541         return 0;
2542 }
2543
2544 static char mandescr_coreshowchannels[] =
2545 "Description: List currently defined channels and some information\n"
2546 "             about them.\n"
2547 "Variables:\n"
2548 "          ActionID: Optional Action id for message matching.\n";
2549
2550 /*! \brief  Manager command "CoreShowChannels" - List currently defined channels 
2551  *          and some information about them. */
2552 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2553 {
2554         const char *actionid = astman_get_header(m, "ActionID");
2555         char actionidtext[256];
2556         struct ast_channel *c = NULL;
2557         int numchans = 0;
2558         int duration, durh, durm, durs;
2559
2560         if (!ast_strlen_zero(actionid))
2561                 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2562         else
2563                 actionidtext[0] = '\0';
2564
2565         astman_send_listack(s, m, "Channels will follow", "start");     
2566
2567         while ((c = ast_channel_walk_locked(c)) != NULL) {
2568                 struct ast_channel *bc = ast_bridged_channel(c);
2569                 char durbuf[10] = "";
2570
2571                 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2572                         duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2573                         durh = duration / 3600;
2574                         durm = (duration % 3600) / 60;
2575                         durs = duration % 60;
2576                         snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2577                 }
2578
2579                 astman_append(s,
2580                         "Channel: %s\r\n"
2581                         "UniqueID: %s\r\n"
2582                         "Context: %s\r\n"
2583                         "Extension: %s\r\n"
2584                         "Priority: %d\r\n"
2585                         "ChannelState: %d\r\n"
2586                         "ChannelStateDesc: %s\r\n"
2587                         "Application: %s\r\n"
2588                         "ApplicationData: %s\r\n"
2589                         "CallerIDnum: %s\r\n"
2590                         "Duration: %s\r\n"
2591                         "AccountCode: %s\r\n"
2592                         "BridgedChannel: %s\r\n"
2593                         "BridgedUniqueID: %s\r\n"
2594                         "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2595                         c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2596                         S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2597                 ast_channel_unlock(c);
2598                 numchans++;
2599         }
2600
2601         astman_append(s,
2602                 "Event: CoreShowChannelsComplete\r\n"
2603                 "EventList: Complete\r\n"
2604                 "ListItems: %d\r\n"
2605                 "%s"
2606                 "\r\n", numchans, actionidtext);
2607
2608         return 0;
2609 }
2610
2611 static char mandescr_modulecheck[] = 
2612 "Description: Checks if Asterisk module is loaded\n"
2613 "Variables: \n"
2614 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
2615 "  Module: <name>          Asterisk module name (not including extension)\n"
2616 "\n"
2617 "Will return Success/Failure\n"
2618 "For success returns, the module revision number is included.\n";
2619
2620 /* Manager function to check if module is loaded */
2621 static int manager_modulecheck(struct mansession *s, const struct message *m)
2622 {
2623         int res;
2624         const char *module = astman_get_header(m, "Module");
2625         const char *id = astman_get_header(m, "ActionID");
2626         char idText[256];
2627         const char *version;
2628         char filename[PATH_MAX];
2629         char *cut;
2630
2631         ast_copy_string(filename, module, sizeof(filename));
2632         if ((cut = strchr(filename, '.'))) {
2633                 *cut = '\0';
2634         } else {
2635                 cut = filename + strlen(filename);
2636         }
2637         snprintf(cut, sizeof(filename) - cut - 1, ".so");
2638         ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2639         res = ast_module_check(filename);
2640         if (!res) {
2641                 astman_send_error(s, m, "Module not loaded");
2642                 return 0;
2643         }
2644         snprintf(cut, sizeof(filename) - cut - 1, ".c");
2645         ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2646         version = ast_file_version_find(filename);
2647
2648         if (!ast_strlen_zero(id))
2649                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2650         else
2651                 idText[0] = '\0';
2652         astman_append(s, "Response: Success\r\n%s", idText);
2653         astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2654         return 0;
2655 }
2656
2657 static char mandescr_moduleload[] = 
2658 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2659 "Variables: \n"
2660 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
2661 "  Module: <name>          Asterisk module name (including .so extension)\n"
2662 "                          or subsystem identifier:\n"
2663 "                               cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2664 "  LoadType: load | unload | reload\n"
2665 "                          The operation to be done on module\n"
2666 " If no module is specified for a reload loadtype, all modules are reloaded";
2667
2668 static int manager_moduleload(struct mansession *s, const struct message *m)
2669 {
2670         int res;
2671         const char *module = astman_get_header(m, "Module");
2672         const char *loadtype = astman_get_header(m, "LoadType");
2673
2674         if (!loadtype || strlen(loadtype) == 0)
2675                 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2676         if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2677                 astman_send_error(s, m, "Need module name");
2678
2679         if (!strcasecmp(loadtype, "load")) {
2680                 res = ast_load_resource(module);
2681                 if (res)
2682                         astman_send_error(s, m, "Could not load module.");
2683                 else
2684                         astman_send_ack(s, m, "Module loaded.");
2685         } else if (!strcasecmp(loadtype, "unload")) {
2686                 res = ast_unload_resource(module, AST_FORCE_SOFT);
2687                 if (res)
2688                         astman_send_error(s, m, "Could not unload module.");
2689                 else
2690                         astman_send_ack(s, m, "Module unloaded.");
2691         } else if (!strcasecmp(loadtype, "reload")) {
2692                 if (module != NULL) {
2693                         res = ast_module_reload(module);
2694                         if (res == 0)
2695                                 astman_send_error(s, m, "No such module.");
2696                         else if (res == 1)
2697                                 astman_send_error(s, m, "Module does not support reload action.");
2698                         else
2699                                 astman_send_ack(s, m, "Module reloaded.");
2700                 } else {
2701                         ast_module_reload(NULL);        /* Reload all modules */
2702                         astman_send_ack(s, m, "All modules reloaded");
2703                 }
2704         } else 
2705                 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2706         return 0;
2707 }
2708
2709 /*
2710  * Done with the action handlers here, we start with the code in charge
2711  * of accepting connections and serving them.
2712  * accept_thread() forks a new thread for each connection, session_do(),
2713  * which in turn calls get_input() repeatedly until a full message has
2714  * been accumulated, and then invokes process_message() to pass it to
2715  * the appropriate handler.
2716  */
2717
2718 /*
2719  * Process an AMI message, performing desired action.
2720  * Return 0 on success, -1 on error that require the session to be destroyed.
2721  */
2722 static int process_message(struct mansession *s, const struct message *m)
2723 {
2724         char action[80] = "";
2725         int ret = 0;
2726         struct manager_action *tmp;
2727         const char *user = astman_get_header(m, "Username");
2728
2729         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2730         ast_debug(1, "Manager received command '%s'\n", action);
2731
2732         if (ast_strlen_zero(action)) {
2733                 ast_mutex_lock(&s->__lock);
2734                 astman_send_error(s, m, "Missing action in request");
2735                 ast_mutex_unlock(&s->__lock);
2736                 return 0;
2737         }
2738
2739         if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2740                 ast_mutex_lock(&s->__lock);
2741                 astman_send_error(s, m, "Permission denied");
2742                 ast_mutex_unlock(&s->__lock);
2743                 return 0;
2744         }
2745
2746         if (!allowmultiplelogin && !s->authenticated && user &&
2747                 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2748                 if (check_manager_session_inuse(user)) {
2749                         sleep(1);
2750                         ast_mutex_lock(&s->__lock);
2751                         astman_send_error(s, m, "Login Already In Use");
2752                         ast_mutex_unlock(&s->__lock);
2753                         return -1;
2754                 }
2755         }
2756
2757         AST_RWLIST_RDLOCK(&actions);
2758         AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2759                 if (strcasecmp(action, tmp->action))
2760                         continue;
2761                 if (s->writeperm & tmp->authority || tmp->authority == 0)
2762                         ret = tmp->func(s, m);
2763                 else
2764                         astman_send_error(s, m, "Permission denied");
2765                 break;
2766         }
2767         AST_RWLIST_UNLOCK(&actions);
2768
2769         if (!tmp) {
2770                 char buf[512];
2771                 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2772                 ast_mutex_lock(&s->__lock);
2773                 astman_send_error(s, m, buf);
2774                 ast_mutex_unlock(&s->__lock);
2775         }
2776         if (ret)
2777                 return ret;
2778         /* Once done with our message, deliver any pending events */
2779         return process_events(s);
2780 }
2781
2782 /*!
2783  * Read one full line (including crlf) from the manager socket.
2784  * \note \verbatim
2785  * \r\n is the only valid terminator for the line.
2786  * (Note that, later, '\0' will be considered as the end-of-line marker,
2787  * so everything between the '\0' and the '\r\n' will not be used).
2788  * Also note that we assume output to have at least "maxlen" space.
2789  * \endverbatim
2790  */
2791 static int get_input(struct mansession *s, char *output)
2792 {
2793         int res, x;
2794         int maxlen = sizeof(s->inbuf) - 1;
2795         char *src = s->inbuf;
2796
2797         /*
2798          * Look for \r\n within the buffer. If found, copy to the output
2799          * buffer and return, trimming the \r\n (not used afterwards).
2800          */
2801         for (x = 0; x < s->inlen; x++) {
2802                 int cr; /* set if we have \r */
2803                 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2804                         cr = 2; /* Found. Update length to include \r\n */
2805                 else if (src[x] == '\n')
2806                         cr = 1; /* also accept \n only */
2807                 else
2808                         continue;
2809                 memmove(output, src, x);        /*... but trim \r\n */
2810                 output[x] = '\0';               /* terminate the string */
2811                 x += cr;                        /* number of bytes used */
2812                 s->inlen -= x;                  /* remaining size */
2813                 memmove(src, src + x, s->inlen); /* remove used bytes */
2814                 return 1;
2815         }
2816         if (s->inlen >= maxlen) {
2817                 /* no crlf found, and buffer full - sorry, too long for us */
2818                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2819                 s->inlen = 0;
2820         }
2821         res = 0;
2822         while (res == 0) {
2823                 /* XXX do we really need this locking ? */
2824                 ast_mutex_lock(&s->__lock);
2825                 s->waiting_thread = pthread_self();
2826                 ast_mutex_unlock(&s->__lock);
2827
2828                 res = ast_wait_for_input(s->fd, -1);    /* return 0 on timeout ? */
2829
2830                 ast_mutex_lock(&s->__lock);
2831                 s->waiting_thread = AST_PTHREADT_NULL;
2832                 ast_mutex_unlock(&s->__lock);
2833         }
2834         if (res < 0) {
2835                 /* If we get a signal from some other thread (typically because
2836                  * there are new events queued), return 0 to notify the caller.
2837                  */
2838                 if (errno == EINTR)
2839                         return 0;
2840                 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2841                 return -1;
2842         }
2843         ast_mutex_lock(&s->__lock);
2844         res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2845         if (res < 1)
2846                 res = -1;       /* error return */
2847         else {
2848                 s->inlen += res;
2849                 src[s->inlen] = '\0';
2850                 res = 0;
2851         }
2852         ast_mutex_unlock(&s->__lock);
2853         return res;
2854 }
2855
2856 static int do_message(struct mansession *s)
2857 {
2858         struct message m = { 0 };
2859         char header_buf[sizeof(s->inbuf)] = { '\0' };
2860         int res;
2861
2862         for (;;) {
2863                 /* Check if any events are pending and do them if needed */
2864                 if (process_events(s))
2865                         return -1;
2866                 res = get_input(s, header_buf);
2867                 if (res == 0) {
2868                         continue;
2869                 } else if (res > 0) {
2870                         if (ast_strlen_zero(header_buf))
2871                                 return process_message(s, &m) ? -1 : 0;
2872                         else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2873                                 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2874                 } else {
2875                         return res;
2876                 }
2877         }
2878 }
2879
2880 /*! \brief The body of the individual manager session.
2881  * Call get_input() to read one line at a time
2882  * (or be woken up on new events), collect the lines in a
2883  * message until found an empty line, and execute the request.
2884  * In any case, deliver events asynchronously through process_events()
2885  * (called from here if no line is available, or at the end of
2886  * process_message(). )
2887  */
2888 static void *session_do(void *data)
2889 {
2890         struct ast_tcptls_session_instance *ser = data;
2891         struct mansession *s = ast_calloc(1, sizeof(*s));
2892         int flags;
2893         int res;
2894
2895         if (s == NULL)
2896                 goto done;
2897
2898         s->writetimeout = 100;
2899         s->waiting_thread = AST_PTHREADT_NULL;
2900
2901         flags = fcntl(ser->fd, F_GETFL);
2902         if (!block_sockets) /* make sure socket is non-blocking */
2903                 flags |= O_NONBLOCK;
2904         else
2905                 flags &= ~O_NONBLOCK;
2906         fcntl(ser->fd, F_SETFL, flags);
2907
2908         ast_mutex_init(&s->__lock);
2909         s->send_events = -1;
2910         /* these fields duplicate those in the 'ser' structure */
2911         s->fd = ser->fd;
2912         s->f = ser->f;
2913         s->sin = ser->requestor;
2914
2915         AST_LIST_LOCK(&sessions);
2916         AST_LIST_INSERT_HEAD(&sessions, s, list);
2917         ast_atomic_fetchadd_int(&num_sessions, 1);
2918         AST_LIST_UNLOCK(&sessions);
2919         /* Hook to the tail of the event queue */
2920         s->last_ev = grab_last();
2921         s->f = ser->f;
2922         astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);  /* welcome prompt */
2923         for (;;) {
2924                 if ((res = do_message(s)) < 0)
2925                         break;
2926         }
2927         /* session is over, explain why and terminate */
2928         if (s->authenticated) {
2929                         if (manager_displayconnects(s))
2930                         ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2931                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2932         } else {
2933                         if (displayconnects)
2934                         ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2935                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2936         }
2937
2938         /* It is possible under certain circumstances for this session thread
2939            to complete its work and exit *before* the thread that created it
2940            has finished executing the ast_pthread_create_background() function.
2941            If this occurs, some versions of glibc appear to act in a buggy
2942            fashion and attempt to write data into memory that it thinks belongs
2943            to the thread but is in fact not owned by the thread (or may have
2944            been freed completely).
2945
2946            Causing this thread to yield to other threads at least one time
2947            appears to work around this bug.
2948         */
2949         usleep(1);
2950
2951         destroy_session(s);
2952
2953 done:
2954         ser = ast_tcptls_session_instance_destroy(ser);
2955         return NULL;
2956 }
2957
2958 /*! \brief remove at most n_max stale session from the list. */
2959 static void purge_sessions(int n_max)
2960 {
2961         struct mansession *s;
2962         time_t now = time(NULL);
2963
2964         AST_LIST_LOCK(&sessions);
2965         AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2966                 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2967                         AST_LIST_REMOVE_CURRENT(list);
2968                         ast_atomic_fetchadd_int(&num_sessions, -1);
2969                         if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
2970                                 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2971                                         s->username, ast_inet_ntoa(s->sin.sin_addr));
2972                         }
2973                         free_session(s);        /* XXX outside ? */
2974                         if (--n_max <= 0)
2975                                 break;
2976                 }
2977         }
2978         AST_LIST_TRAVERSE_SAFE_END;
2979         AST_LIST_UNLOCK(&sessions);
2980 }
2981
2982 /*
2983  * events are appended to a queue from where they
2984  * can be dispatched to clients.
2985  */
2986 static int append_event(const char *str, int category)
2987 {
2988         struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2989         static int seq; /* sequence number */
2990
2991         if (!tmp)
2992                 return -1;
2993
2994         /* need to init all fields, because ast_malloc() does not */
2995         tmp->usecount = 0;
2996         tmp->category = category;
2997         tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2998         AST_LIST_NEXT(tmp, eq_next) = NULL;
2999         strcpy(tmp->eventdata, str);
3000
3001         AST_LIST_LOCK(&all_events);
3002         AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3003         AST_LIST_UNLOCK(&all_events);
3004
3005         return 0;
3006 }
3007
3008 /* XXX see if can be moved inside the function */
3009 AST_THREADSTORAGE(manager_event_buf);
3010 #define MANAGER_EVENT_BUF_INITSIZE   256
3011
3012 /*! \brief  manager_event: Send AMI event to client */
3013 int __manager_event(int category, const char *event,
3014         const char *file, int line, const char *func, const char *fmt, ...)
3015 {
3016         struct mansession *s;
3017         struct manager_custom_hook *hook;
3018         struct ast_str *auth = ast_str_alloca(80);
3019         const char *cat_str;
3020         va_list ap;
3021         struct timeval now;
3022         struct ast_str *buf;
3023
3024         /* Abort if there aren't any manager sessions */
3025         if (!num_sessions)
3026                 return 0;
3027
3028         if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
3029                 return -1;
3030
3031         cat_str = authority_to_str(category, &auth);
3032         ast_str_set(&buf, 0,
3033                         "Event: %s\r\nPrivilege: %s\r\n",
3034                          event, cat_str);
3035
3036         if (timestampevents) {
3037                 now = ast_tvnow();
3038                 ast_str_append(&buf, 0,
3039                                 "Timestamp: %ld.%06lu\r\n",
3040                                  now.tv_sec, (unsigned long) now.tv_usec);
3041         }
3042         if (manager_debug) {
3043                 static int seq;
3044                 ast_str_append(&buf, 0,
3045                                 "SequenceNumber: %d\r\n",
3046                                  ast_atomic_fetchadd_int(&seq, 1));
3047                 ast_str_append(&buf, 0,
3048                                 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
3049         }
3050
3051         va_start(ap, fmt);
3052         ast_str_append_va(&buf, 0, fmt, ap);
3053         va_end(ap);
3054
3055         ast_str_append(&buf, 0, "\r\n");
3056
3057         append_event(buf->str, category);
3058
3059         /* Wake up any sleeping sessions */
3060         AST_LIST_LOCK(&sessions);
3061         AST_LIST_TRAVERSE(&sessions, s, list) {
3062                 ast_mutex_lock(&s->__lock);
3063                 if (s->waiting_thread != AST_PTHREADT_NULL)
3064                         pthread_kill(s->waiting_thread, SIGURG);
3065                 ast_mutex_unlock(&s->__lock);
3066         }
3067         AST_LIST_UNLOCK(&sessions);
3068
3069         AST_RWLIST_RDLOCK(&manager_hooks);
3070         AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
3071                 hook->helper(category, event, buf->str);
3072         }
3073         AST_RWLIST_UNLOCK(&manager_hooks);
3074
3075         return 0;
3076 }
3077
3078 /*
3079  * support functions to register/unregister AMI action handlers,
3080  */
3081 int ast_manager_unregister(char *action)
3082 {
3083         struct manager_action *cur;
3084
3085         AST_RWLIST_WRLOCK(&actions);
3086         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
3087                 if (!strcasecmp(action, cur->action)) {
3088                         AST_RWLIST_REMOVE_CURRENT(list);
3089                         ast_free(cur);
3090                         ast_verb(2, "Manager unregistered action %s\n", action);
3091                         break;
3092                 }
3093         }
3094         AST_RWLIST_TRAVERSE_SAFE_END;
3095         AST_RWLIST_UNLOCK(&actions);
3096
3097         return 0;
3098 }
3099
3100 static int manager_state_cb(char *context, char *exten, int state, void *data)
3101 {
3102         /* Notify managers of change */
3103         char hint[512];
3104         ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
3105
3106         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
3107         return 0;
3108 }
3109
3110 static int ast_manager_register_struct(struct manager_action *act)
3111 {
3112         struct manager_action *cur, *prev = NULL;
3113
3114         AST_RWLIST_WRLOCK(&actions);
3115         AST_RWLIST_TRAVERSE(&actions, cur, list) {
3116                 int ret = strcasecmp(cur->action, act->action);
3117                 if (ret == 0) {
3118                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
3119                         AST_RWLIST_UNLOCK(&actions);
3120                         return -1;
3121                 }
3122                 if (ret > 0) { /* Insert these alphabetically */
3123                         prev = cur;
3124                         break;
3125                 }
3126         }
3127         
3128         if (prev)       
3129                 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
3130         else
3131                 AST_RWLIST_INSERT_HEAD(&actions, act, list);
3132
3133         ast_verb(2, "Manager registered action %s\n", act->action);
3134
3135         AST_RWLIST_UNLOCK(&actions);
3136
3137         return 0;
3138 }
3139
3140 /*! \brief register a new command with manager, including online help. This is
3141         the preferred way to register a manager command */
3142 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
3143 {
3144         struct manager_action *cur = NULL;
3145
3146         if (!(cur = ast_calloc(1, sizeof(*cur))))
3147                 return -1;
3148
3149         cur->action = action;
3150         cur->authority = auth;
3151         cur->func = func;
3152         cur->synopsis = synopsis;
3153         cur->description = description;
3154
3155         ast_manager_register_struct(cur);
3156
3157         return 0;
3158 }
3159 /*! @}
3160  END Doxygen group */
3161
3162 /*
3163  * The following are support functions for AMI-over-http.
3164  * The common entry point is generic_http_callback(),
3165  * which extracts HTTP header and URI fields and reformats
3166  * them into AMI messages, locates a proper session
3167  * (using the mansession_id Cookie or GET variable),
3168  * and calls process_message() as for regular AMI clients.
3169  * When done, the output (which goes to a temporary file)
3170  * is read back into a buffer and reformatted as desired,
3171  * then fed back to the client over the original socket.
3172  */
3173
3174 enum output_format {
3175         FORMAT_RAW,
3176         FORMAT_HTML,
3177         FORMAT_XML,
3178 };
3179
3180 static char *contenttype[] = {
3181         [FORMAT_RAW] = "plain",
3182         [FORMAT_HTML] = "html",
3183         [FORMAT_XML] =  "xml",
3184 };
3185
3186 /*!
3187  * locate an http session in the list. The search key (ident) is
3188  * the value of the mansession_id cookie (0 is not valid and means
3189  * a session on the AMI socket).
3190  */
3191 static struct mansession *find_session(unsigned long ident)
3192 {
3193         struct mansession *s;
3194
3195         if (ident == 0)
3196                 return NULL;
3197
3198         AST_LIST_LOCK(&sessions);
3199         AST_LIST_TRAVERSE(&sessions, s, list) {
3200                 ast_mutex_lock(&s->__lock);
3201                 if (s->managerid == ident && !s->needdestroy) {
3202                         ast_atomic_fetchadd_int(&s->inuse, 1);
3203                         break;
3204                 }
3205                 ast_mutex_unlock(&s->__lock);
3206         }
3207         AST_LIST_UNLOCK(&sessions);
3208
3209         return s;
3210 }
3211
3212 int astman_verify_session_readpermissions(unsigned long ident, int perm)
3213 {
3214         int result = 0;
3215         struct mansession *s;
3216
3217         AST_LIST_LOCK(&sessions);
3218         AST_LIST_TRAVERSE(&sessions, s, list) {
3219                 ast_mutex_lock(&s->__lock);
3220                 if ((s->managerid == ident) && (s->readperm & perm)) {
3221                         result = 1;
3222                         ast_mutex_unlock(&s->__lock);
3223                         break;
3224                 }
3225                 ast_mutex_unlock(&s->__lock);
3226         }
3227         AST_LIST_UNLOCK(&sessions);
3228         return result;
3229 }
3230
3231 int astman_verify_session_writepermissions(unsigned long ident, int perm)
3232 {
3233         int result = 0;
3234         struct mansession *s;
3235
3236         AST_LIST_LOCK(&sessions);
3237         AST_LIST_TRAVERSE(&sessions, s, list) {
3238                 ast_mutex_lock(&s->__lock);
3239                 if ((s->managerid == ident) && (s->writeperm & perm)) {
3240                         result = 1;
3241                         ast_mutex_unlock(&s->__lock);
3242                         break;
3243                 }
3244                 ast_mutex_unlock(&s->__lock);
3245         }
3246         AST_LIST_UNLOCK(&sessions);
3247         return result;
3248 }
3249
3250 /*
3251  * convert to xml with various conversion:
3252  * mode & 1     -> lowercase;
3253  * mode & 2     -> replace non-alphanumeric chars with underscore
3254  */
3255 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
3256 {
3257         /* store in a local buffer to avoid calling ast_str_append too often */
3258         char buf[256];
3259         char *dst = buf;
3260         int space = sizeof(buf);
3261         /* repeat until done and nothing to flush */
3262         for ( ; *src || dst != buf ; src++) {
3263                 if (*src == '\0' || space < 10) {       /* flush */
3264                         *dst++ = '\0';
3265                         ast_str_append(out, 0, "%s", buf);
3266                         dst = buf;
3267                         space = sizeof(buf);
3268                         if (*src == '\0')
3269                                 break;
3270                 }
3271                         
3272                 if ( (mode & 2) && !isalnum(*src)) {
3273                         *dst++ = '_';
3274                         space--;
3275                         continue;
3276                 }
3277                 switch (*src) {
3278                 case '<':
3279                         strcpy(dst, "&lt;");
3280                         dst += 4;
3281                         space -= 4;
3282                         break;
3283                 case '>':
3284                         strcpy(dst, "&gt;");
3285                         dst += 4;
3286                         space -= 4;
3287                         break;
3288                 case '\"':
3289                         strcpy(dst, "&quot;");
3290                         dst += 6;
3291                         space -= 6;
3292                         break;
3293                 case '\'':
3294                         strcpy(dst, "&apos;");
3295                         dst += 6;
3296                         space -= 6;
3297                         break;
3298                 case '&':
3299                         strcpy(dst, "&amp;");
3300                         dst += 5;
3301                         space -= 5;
3302                         break;
3303
3304                 default:
3305                         *dst++ = mode ? tolower(*src) : *src;
3306                         space--;
3307                 }
3308         }
3309 }
3310
3311 struct variable_count {
3312         char *varname;
3313         int count;
3314 };
3315
3316 static int compress_char(char c)
3317 {
3318         c &= 0x7f;
3319         if (c < 32)
3320                 return 0;
3321         else if (c >= 'a' && c <= 'z')
3322                 return c - 64;
3323         else if (c > 'z')
3324                 return '_';
3325         else
3326                 return c - 32;
3327 }
3328
3329 static int variable_count_hash_fn(const void *vvc, const int flags)
3330 {
3331         const struct variable_count *vc = vvc;
3332         int res = 0, i;
3333         for (i = 0; i < 5; i++) {
3334                 if (vc->varname[i] == '\0')
3335                         break;
3336                 res += compress_char(vc->varname[i]) << (i * 6);
3337         }
3338         return res;
3339 }
3340
3341 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
3342 {
3343         /* Due to the simplicity of struct variable_count, it makes no difference
3344          * if you pass in objects or strings, the same operation applies. This is
3345          * due to the fact that the hash occurs on the first element, which means
3346          * the address of both the struct and the string are exactly the same. */
3347         struct variable_count *vc = obj;
3348         char *str = vstr;
3349         return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
3350 }
3351
3352 /*! \brief Convert the input into XML or HTML.
3353  * The input is supposed to be a sequence of lines of the form
3354  *      Name: value
3355  * optionally followed by a blob of unformatted text.
3356  * A blank line is a section separator. Basically, this is a
3357  * mixture of the format of Manager Interface and CLI commands.
3358  * The unformatted text is considered as a single value of a field
3359  * named 'Opaque-data'.
3360  *
3361  * At the moment the output format is the following (but it may
3362  * change depending on future requirements so don't count too
3363  * much on it when writing applications):
3364  *
3365  * General: the unformatted text is used as a value of
3366  * XML output:  to be completed
3367  * 
3368  * \verbatim
3369  *   Each section is within <response type="object" id="xxx">
3370  *   where xxx is taken from ajaxdest variable or defaults to unknown
3371  *   Each row is reported as an attribute Name="value" of an XML
3372  *   entity named from the variable ajaxobjtype, default to "generic"
3373  * \endverbatim
3374  *
3375  * HTML output:
3376  *   each Name-value pair is output as a single row of a two-column table.
3377  *   Sections (blank lines in the input) are separated by a <HR>
3378  *
3379  */
3380 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
3381 {
3382         struct ast_variable *v;
3383         const char *dest = NULL;
3384         char *var, *val;
3385         const char *objtype = NULL;
3386         int in_data = 0;        /* parsing data */
3387         int inobj = 0;
3388         int xml = (format == FORMAT_XML);
3389         struct variable_count *vc = NULL;
3390         struct ao2_container *vco = NULL;
3391
3392         for (v = vars; v; v = v->next) {
3393                 if (!dest && !strcasecmp(v->name, "ajaxdest"))
3394                         dest = v->value;
3395                 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
3396                         objtype = v->value;
3397         }
3398         if (!dest)
3399                 dest = "unknown";
3400         if (!objtype)
3401                 objtype = "generic";
3402
3403         /* we want to stop when we find an empty line */
3404         while (in && *in) {
3405                 val = strsep(&in, "\r\n");      /* mark start and end of line */
3406                 if (in && *in == '\n')          /* remove trailing \n if any */
3407                         in++;
3408                 ast_trim_blanks(val);
3409                 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
3410                 if (ast_strlen_zero(val)) {
3411                         if (in_data) { /* close data */
3412                                 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3413                                 in_data = 0;
3414                         }
3415                         ast_str_append(out, 0, xml ? " /></response>\n" :
3416                                 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3417                         inobj = 0;
3418                         ao2_ref(vco, -1);
3419                         vco = NULL;
3420                         continue;
3421                 }
3422
3423                 /* we expect Name: value lines */
3424                 if (in_data) {
3425                         var = NULL;
3426                 } else {
3427                         var = strsep(&val, ":");
3428                         if (val) {      /* found the field name */
3429                                 val = ast_skip_blanks(val);
3430                                 ast_trim_blanks(var);
3431                         } else {                /* field name not found, move to opaque mode */
3432                                 val = var;
3433                                 var = "Opaque-data";
3434                         }
3435                 }
3436
3437                 if (!inobj) {
3438                         if (xml)
3439                                 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
3440                         else
3441                                 ast_str_append(out, 0, "<body>\n");
3442                         vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
3443                         inobj = 1;
3444                 }
3445
3446                 if (!in_data) { /* build appropriate line start */
3447                         ast_str_append(out, 0, xml ? " " : "<tr><td>");
3448                         if ((vc = ao2_find(vco, var, 0)))
3449                                 vc->count++;
3450                         else {
3451                                 /* Create a new entry for this one */
3452                                 vc = ao2_alloc(sizeof(*vc), NULL);
3453                                 vc->varname = var;
3454                                 vc->count = 1;
3455                                 ao2_link(vco, vc);
3456                         }
3457                         xml_copy_escape(out, var, xml ? 1 | 2 : 0);
3458                         if (vc->count > 1)
3459                                 ast_str_append(out, 0, "-%d", vc->count);
3460                         ao2_ref(vc, -1);
3461                         ast_str_append(out, 0, xml ? "='" : "</td><td>");
3462                         if (!strcmp(var, "Opaque-data"))
3463                                 in_data = 1;
3464                 }
3465                 xml_copy_escape(out, val, 0);   /* data field */
3466                 if (!in_data)
3467                         ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3468                 else
3469                         ast_str_append(out, 0, xml ? "\n" : "<br>\n");
3470         }
3471         if (inobj) {
3472                 ast_str_append(out, 0, xml ? " /></response>\n" :
3473                         "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3474                 ao2_ref(vco, -1);
3475         }
3476 }
3477
3478 static struct ast_str *generic_http_callback(enum output_format format,
3479                                              struct sockaddr_in *requestor, const char *uri,
3480                                              struct ast_variable *params, int *status,
3481                                              char **title, int *contentlength)
3482 {
3483         struct mansession *s = NULL;
3484         unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
3485         int blastaway = 0;
3486         struct ast_variable *v;
3487         char template[] = "/tmp/ast-http-XXXXXX";       /* template for temporary file */
3488         struct ast_str *out = NULL;
3489         struct message m = { 0 };
3490         unsigned int x;
3491         size_t hdrlen;
3492
3493         for (v = params; v; v = v->next) {
3494                 if (!strcasecmp(v->name, "mansession_id")) {
3495                         sscanf(v->value, "%lx", &ident);
3496                         break;
3497                 }
3498         }
3499
3500         if (!(s = find_session(ident))) {
3501                 /* Create new session.
3502                  * While it is not in the list we don't need any locking
3503                  */
3504                 if (!(s = ast_calloc(1, sizeof(*s)))) {
3505                         *status = 500;
3506                         goto generic_callback_out;
3507                 }
3508                 s->sin = *requestor;
3509                 s->fd = -1;
3510                 s->waiting_thread = AST_PTHREADT_NULL;
3511                 s->send_events = 0;
3512                 ast_mutex_init(&s->__lock);
3513                 ast_mutex_lock(&s->__lock);
3514                 s->inuse = 1;
3515                 /*!\note There is approximately a 1 in 1.8E19 chance that the following
3516                  * calculation will produce 0, which is an invalid ID, but due to the
3517                  * properties of the rand() function (and the constantcy of s), that
3518                  * won't happen twice in a row.
3519                  */
3520                 while ((s->managerid = rand() ^ (unsigned long) s) == 0);
3521                 s->last_ev = grab_last();
3522                 AST_LIST_LOCK(&sessions);
3523                 AST_LIST_INSERT_HEAD(&sessions, s, list);
3524                 ast_atomic_fetchadd_int(&num_sessions, 1);
3525                 AST_LIST_UNLOCK(&sessions);
3526         }
3527
3528         ast_mutex_unlock(&s->__lock);
3529
3530         if (!(out = ast_str_create(1024))) {
3531                 *status = 500;
3532                 goto generic_callback_out;
3533         }
3534
3535         s->fd = mkstemp(template);      /* create a temporary file for command output */
3536         unlink(template);
3537         s->f = fdopen(s->fd, "w+");
3538
3539         for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
3540                 hdrlen = strlen(v->name) + strlen(v->value) + 3;
3541                 m.headers[m.hdrcount] = alloca(hdrlen);
3542                 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
3543                 m.hdrcount = x + 1;
3544         }
3545
3546         if (process_message(s, &m)) {
3547                 if (s->authenticated) {
3548                                 if (manager_displayconnects(s))
3549                                 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3550                         ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3551                 } else {
3552                                 if (displayconnects)
3553                                 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3554                         ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3555                 }
3556                 s->needdestroy = 1;
3557         }
3558
3559         ast_str_append(&out, 0,
3560                        "Content-type: text/%s\r\n"
3561                        "Cache-Control: no-cache;\r\n"
3562                        "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
3563                        "\r\n",
3564                         contenttype[format],
3565                         s->managerid, httptimeout);
3566
3567         if (format == FORMAT_XML) {
3568                 ast_str_append(&out, 0, "<ajax-response>\n");
3569         } else if (format == FORMAT_HTML) {
3570
3571 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
3572 #define TEST_STRING \
3573         "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
3574         user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
3575         <input type=\"submit\"></form>"
3576
3577                 ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
3578                 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
3579                 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
3580                 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
3581         }
3582
3583         if (s->f != NULL) {     /* have temporary output */
3584                 char *buf;
3585                 size_t l = ftell(s->f);
3586                 
3587                 if (l) {
3588                         if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
3589                                 if (format == FORMAT_XML || format == FORMAT_HTML)
3590                                         xml_translate(&out, buf, params, format);
3591                                 else
3592                                         ast_str_append(&out, 0, buf);
3593                                 munmap(buf, l);
3594                         }
3595                 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
3596                         xml_translate(&out, "", params, format);
3597                 }
3598                 fclose(s->f);
3599                 s->f = NULL;
3600                 s->fd = -1;
3601         }
3602
3603         if (format == FORMAT_XML) {
3604                 ast_str_append(&out, 0, "</ajax-response>\n");
3605         } else if (format == FORMAT_HTML)
3606                 ast_str_append(&out, 0, "</table></body>\r\n");
3607
3608         ast_mutex_lock(&s->__lock);
3609         /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
3610         s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
3611
3612         if (s->needdestroy) {
3613                 if (s->inuse == 1) {
3614                         ast_debug(1, "Need destroy, doing it now!\n");
3615                         blastaway = 1;
3616                 } else {
3617                         ast_debug(1, "Need destroy, but can't do it yet!\n");
3618                         if (s->waiting_thread != AST_PTHREADT_NULL)
3619                                 pthread_kill(s->waiting_thread, SIGURG);
3620                         s->inuse--;
3621                 }
3622         } else
3623                 s->inuse--;
3624         ast_mutex_unlock(&s->__lock);
3625
3626         if (blastaway)
3627                 destroy_session(s);
3628 generic_callback_out:
3629         if (*status != 200)
3630                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
3631         return out;
3632 }
3633
3634 static struct ast_str *manager_http_callback(struct ast_tcptls_session_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
3635 {
3636         return generic_http_callback(FORMAT_HTML, &ser->requestor, uri, params, status, title, contentlength);
3637 }
3638
3639 static struct ast_str *mxml_http_callback(struct ast_tcptls_session_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
3640 {
3641         return generic_http_callback(FORMAT_XML, &ser->requestor, uri, params, status, title, contentlength);
3642 }
3643
3644 static struct ast_str *rawman_http_callback(struct ast_tcptls_session_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
3645 {
3646         return generic_http_callback(FORMAT_RAW, &ser->requestor, uri, params, status, title, contentlength);
3647 }
3648
3649 struct ast_http_uri rawmanuri = {
3650         .description = "Raw HTTP Manager Event Interface",
3651         .uri = "rawman",
3652         .has_subtree = 0,
3653         .callback = rawman_http_callback,
3654 };
3655
3656 struct ast_http_uri manageruri = {
3657         .description = "HTML Manager Event Interface",
3658         .uri = "manager",
3659         .has_subtree = 0,
3660         .callback = manager_http_callback,
3661 };
3662
3663 struct ast_http_uri managerxmluri = {
3664         .description = "XML Manager Event Interface",
3665         .uri = "mxml",
3666         .has_subtree = 0,
3667         .callback = mxml_http_callback,
3668 };
3669
3670 static int registered = 0;
3671 static int webregged = 0;
3672
3673 /*! \brief cleanup code called at each iteration of server_root,
3674  * guaranteed to happen every 5 seconds at most
3675  */
3676 static void purge_old_stuff(void *data)
3677 {
3678         purge_sessions(1);
3679         purge_events();
3680 }
3681
3682 struct ast_tls_config ami_tls_cfg;
3683 static struct server_args ami_desc = {
3684         .accept_fd = -1,
3685         .master = AST_PTHREADT_NULL,
3686         .tls_cfg = NULL, 
3687         .poll_timeout = 5000,   /* wake up every 5 seconds */
3688         .periodic_fn = purge_old_stuff,
3689         .name = "AMI server",
3690         .accept_fn = ast_tcptls_server_root,    /* thread doing the accept() */
3691         .worker_fn = session_do,        /* thread handling the session */
3692 };
3693
3694 static struct server_args amis_desc = {
3695         .accept_fd = -1,
3696         .master = AST_PTHREADT_NULL,
3697         .tls_cfg = &ami_tls_cfg, 
3698         .poll_timeout = -1,     /* the other does the periodic cleanup */
3699         .name = "AMI TLS server",
3700         .accept_fn = ast_tcptls_server_root,    /* thread doing the accept() */
3701         .worker_fn = session_do,        /* thread handling the session */
3702 };
3703
3704 static int __init_manager(int reload)
3705 {
3706         struct ast_config *ucfg = NULL, *cfg = NULL;
3707         const char *val;
3708         char *cat = NULL;
3709         int newhttptimeout = 60;
3710         int have_sslbindaddr = 0;
3711         struct hostent *hp;
3712         struct ast_hostent ahp;
3713         struct ast_manager_user *user = NULL;
3714         struct ast_variable *var;
3715         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
3716
3717         if (!registered) {
3718                 /* Register default actions */
3719                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
3720                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
3721                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
3722                 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
3723                 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
3724                 ast_manager_register2("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
3725                 ast_manager_register("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status, "Lists channel status" );
3726                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
3727                 ast_manager_register2("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar, "Gets a Channel Variable", mandescr_getvar );
3728                 ast_manager_register2("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
3729                 ast_manager_register2("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson, "Retrieve configuration (JSON format)", mandescr_getconfigjson);
3730                 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
3731                 ast_manager_register2("CreateConfig", EVENT_FLAG_CONFIG, action_createconfig, "Creates an empty file in the configuration directory", mandescr_createconfig);
3732                 ast_manager_register2("ListCategories", EVENT_FLAG_CONFIG, action_listcategories, "List categories in configuration file", mandescr_listcategories);
3733                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
3734                 ast_manager_register2("Atxfer", EVENT_FLAG_CALL, action_atxfer, "Attended transfer", mandescr_atxfer);
3735                 ast_manager_register2("Originate", EVENT_FLAG_ORIGINATE, action_originate, "Originate Call", mandescr_originate);
3736                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
3737                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
3738                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
3739                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
3740                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
3741     &n