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