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