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