a9147c57074756eab0b5702cd7c30250b9d14da1
[asterisk/asterisk.git] / main / manager.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief The Asterisk Management Interface - AMI
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \extref OpenSSL http://www.openssl.org - for AMI/SSL 
26  *
27  * At the moment this file contains a number of functions, namely:
28  *
29  * - data structures storing AMI state
30  * - AMI-related API functions, used by internal asterisk components
31  * - handlers for AMI-related CLI functions
32  * - handlers for AMI functions (available through the AMI socket)
33  * - the code for the main AMI listener thread and individual session threads
34  * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
35  *
36  * \ref amiconf
37  */
38
39 /*! \addtogroup Group_AMI AMI functions
40 */
41 /*! @{
42  Doxygen group */
43
44 #include "asterisk.h"
45
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
47
48 #include "asterisk/_private.h"
49 #include "asterisk/paths.h"     /* use various ast_config_AST_* */
50 #include <ctype.h>
51 #include <sys/time.h>
52 #include <signal.h>
53 #include <sys/mman.h>
54
55 #include "asterisk/channel.h"
56 #include "asterisk/file.h"
57 #include "asterisk/manager.h"
58 #include "asterisk/module.h"
59 #include "asterisk/config.h"
60 #include "asterisk/callerid.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/tcptls.h"
69 #include "asterisk/http.h"
70 #include "asterisk/ast_version.h"
71 #include "asterisk/threadstorage.h"
72 #include "asterisk/linkedlists.h"
73 #include "asterisk/version.h"
74 #include "asterisk/term.h"
75 #include "asterisk/astobj2.h"
76 #include "asterisk/features.h"
77
78 enum error_type {
79         UNKNOWN_ACTION = 1,
80         UNKNOWN_CATEGORY,
81         UNSPECIFIED_CATEGORY,
82         UNSPECIFIED_ARGUMENT,
83         FAILURE_ALLOCATION,
84         FAILURE_DELCAT,
85         FAILURE_EMPTYCAT,
86         FAILURE_UPDATE,
87         FAILURE_DELETE,
88         FAILURE_APPEND
89 };
90
91
92 /*!
93  * Linked list of events.
94  * Global events are appended to the list by append_event().
95  * The usecount is the number of stored pointers to the element,
96  * excluding the list pointers. So an element that is only in
97  * the list has a usecount of 0, not 1.
98  *
99  * Clients have a pointer to the last event processed, and for each
100  * of these clients we track the usecount of the elements.
101  * If we have a pointer to an entry in the list, it is safe to navigate
102  * it forward because elements will not be deleted, but only appended.
103  * The worst that can happen is seeing the pointer still NULL.
104  *
105  * When the usecount of an element drops to 0, and the element is the
106  * first in the list, we can remove it. Removal is done within the
107  * main thread, which is woken up for the purpose.
108  *
109  * For simplicity of implementation, we make sure the list is never empty.
110  */
111 struct eventqent {
112         int usecount;           /*!< # of clients who still need the event */
113         int category;
114         unsigned int seq;       /*!< sequence number */
115         AST_LIST_ENTRY(eventqent) eq_next;
116         char eventdata[1];      /*!< really variable size, allocated by append_event() */
117 };
118
119 static AST_LIST_HEAD_STATIC(all_events, eventqent);
120
121 static int displayconnects = 1;
122 static int allowmultiplelogin = 1;
123 static int timestampevents;
124 static int httptimeout = 60;
125 static int manager_enabled = 0;
126 static int webmanager_enabled = 0;
127
128 static int block_sockets;
129 static int num_sessions;
130
131 static int manager_debug;       /*!< enable some debugging code in the manager */
132
133 /*! \brief
134  * Descriptor for a manager session, either on the AMI socket or over HTTP.
135  *
136  * \note
137  * AMI session have managerid == 0; the entry is created upon a connect,
138  * and destroyed with the socket.
139  * HTTP sessions have managerid != 0, the value is used as a search key
140  * to lookup sessions (using the mansession_id cookie).
141  */
142 static const char *command_blacklist[] = {
143         "module load",
144         "module unload",
145 };
146
147 struct mansession {
148         pthread_t ms_t;         /*!< Execution thread, basically useless */
149         ast_mutex_t __lock;     /*!< Thread lock -- don't use in action callbacks, it's already taken care of  */
150                                 /* XXX need to document which fields it is protecting */
151         struct sockaddr_in sin; /*!< address we are connecting from */
152         FILE *f;                /*!< fdopen() on the underlying fd */
153         int fd;                 /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
154         int inuse;              /*!< number of HTTP sessions using this entry */
155         int needdestroy;        /*!< Whether an HTTP session should be destroyed */
156         pthread_t waiting_thread;       /*!< Sleeping thread using this descriptor */
157         unsigned long managerid;        /*!< Unique manager identifier, 0 for AMI sessions */
158         time_t sessionstart;    /*!< Session start time */
159         time_t sessiontimeout;  /*!< Session timeout if HTTP */
160         char username[80];      /*!< Logged in username */
161         char challenge[10];     /*!< Authentication challenge */
162         int authenticated;      /*!< Authentication status */
163         int readperm;           /*!< Authorization for reading */
164         int writeperm;          /*!< Authorization for writing */
165         char inbuf[1025];       /*!< Buffer */
166                                 /* we use the extra byte to add a '\0' and simplify parsing */
167         int inlen;              /*!< number of buffered bytes */
168         int send_events;        /*!<  XXX what ? */
169         struct eventqent *last_ev;      /*!< last event processed. */
170         int writetimeout;       /*!< Timeout for ast_carefulwrite() */
171         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_send_response(s, m, "Success", "Ping: Pong\r\n");
1068         return 0;
1069 }
1070
1071 static char mandescr_getconfig[] =
1072 "Description: A 'GetConfig' action will dump the contents of a configuration\n"
1073 "file by category and contents or optionally by specified category only.\n"
1074 "Variables: (Names marked with * are required)\n"
1075 "   *Filename: Configuration filename (e.g. foo.conf)\n"
1076 "   Category: Category in configuration file\n";
1077
1078 static int action_getconfig(struct mansession *s, const struct message *m)
1079 {
1080         struct ast_config *cfg;
1081         const char *fn = astman_get_header(m, "Filename");
1082         const char *category = astman_get_header(m, "Category");
1083         int catcount = 0;
1084         int lineno = 0;
1085         char *cur_category = NULL;
1086         struct ast_variable *v;
1087         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1088
1089         if (ast_strlen_zero(fn)) {
1090                 astman_send_error(s, m, "Filename not specified");
1091                 return 0;
1092         }
1093         if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1094                 astman_send_error(s, m, "Config file not found");
1095                 return 0;
1096         }
1097
1098         astman_start_ack(s, m);
1099         while ((cur_category = ast_category_browse(cfg, cur_category))) {
1100                 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
1101                         lineno = 0;
1102                         astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
1103                         for (v = ast_variable_browse(cfg, cur_category); v; v = v->next)
1104                                 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
1105                         catcount++;
1106                 }
1107         }
1108         if (!ast_strlen_zero(category) && catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1109                 astman_append(s, "No categories found");
1110         ast_config_destroy(cfg);
1111         astman_append(s, "\r\n");
1112
1113         return 0;
1114 }
1115
1116 static char mandescr_listcategories[] =
1117 "Description: A 'ListCategories' action will dump the categories in\n"
1118 "a given file.\n"
1119 "Variables:\n"
1120 "   Filename: Configuration filename (e.g. foo.conf)\n";
1121
1122 static int action_listcategories(struct mansession *s, const struct message *m)
1123 {
1124         struct ast_config *cfg;
1125         const char *fn = astman_get_header(m, "Filename");
1126         char *category = NULL;
1127         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1128         int catcount = 0;
1129
1130         if (ast_strlen_zero(fn)) {
1131                 astman_send_error(s, m, "Filename not specified");
1132                 return 0;
1133         }
1134         if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1135                 astman_send_error(s, m, "Config file not found or file has invalid syntax");
1136                 return 0;
1137         }
1138         astman_start_ack(s, m);
1139         while ((category = ast_category_browse(cfg, category))) {
1140                 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
1141                 catcount++;
1142         }
1143         if (catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */
1144                 astman_append(s, "Error: no categories found");
1145         ast_config_destroy(cfg);
1146         astman_append(s, "\r\n");
1147
1148         return 0;
1149 }
1150
1151
1152         
1153
1154 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
1155 static void json_escape(char *out, const char *in)
1156 {
1157         for (; *in; in++) {
1158                 if (*in == '\\' || *in == '\"')
1159                         *out++ = '\\';
1160                 *out++ = *in;
1161         }
1162         *out = '\0';
1163 }
1164
1165 static char mandescr_getconfigjson[] =
1166 "Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
1167 "file by category and contents in JSON format.  This only makes sense to be used\n"
1168 "using rawman over the HTTP interface.\n"
1169 "Variables:\n"
1170 "   Filename: Configuration filename (e.g. foo.conf)\n";
1171
1172 static int action_getconfigjson(struct mansession *s, const struct message *m)
1173 {
1174         struct ast_config *cfg;
1175         const char *fn = astman_get_header(m, "Filename");
1176         char *category = NULL;
1177         struct ast_variable *v;
1178         int comma1 = 0;
1179         char *buf = NULL;
1180         unsigned int buf_len = 0;
1181         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1182
1183         if (ast_strlen_zero(fn)) {
1184                 astman_send_error(s, m, "Filename not specified");
1185                 return 0;
1186         }
1187
1188         if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
1189                 astman_send_error(s, m, "Config file not found");
1190                 return 0;
1191         }
1192
1193         buf_len = 512;
1194         buf = alloca(buf_len);
1195
1196         astman_start_ack(s, m);
1197         astman_append(s, "JSON: {");
1198         while ((category = ast_category_browse(cfg, category))) {
1199                 int comma2 = 0;
1200                 if (buf_len < 2 * strlen(category) + 1) {
1201                         buf_len *= 2;
1202                         buf = alloca(buf_len);
1203                 }
1204                 json_escape(buf, category);
1205                 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
1206                 if (!comma1)
1207                         comma1 = 1;
1208                 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
1209                         if (comma2)
1210                                 astman_append(s, ",");
1211                         if (buf_len < 2 * strlen(v->name) + 1) {
1212                                 buf_len *= 2;
1213                                 buf = alloca(buf_len);
1214                         }
1215                         json_escape(buf, v->name);
1216                         astman_append(s, "\"%s", buf);
1217                         if (buf_len < 2 * strlen(v->value) + 1) {
1218                                 buf_len *= 2;
1219                                 buf = alloca(buf_len);
1220                         }
1221                         json_escape(buf, v->value);
1222                         astman_append(s, "%s\"", buf);
1223                         if (!comma2)
1224                                 comma2 = 1;
1225                 }
1226                 astman_append(s, "]");
1227         }
1228         astman_append(s, "}\r\n\r\n");
1229
1230         ast_config_destroy(cfg);
1231
1232         return 0;
1233 }
1234
1235 /* helper function for action_updateconfig */
1236 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
1237 {
1238         int x;
1239         char hdr[40];
1240         const char *action, *cat, *var, *value, *match, *line;
1241         struct ast_category *category;
1242         struct ast_variable *v;
1243
1244         for (x = 0; x < 100000; x++) {
1245                 unsigned int object = 0;
1246
1247                 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1248                 action = astman_get_header(m, hdr);
1249                 if (ast_strlen_zero(action))
1250                         break;
1251                 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1252                 cat = astman_get_header(m, hdr);
1253                 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1254                 var = astman_get_header(m, hdr);
1255                 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1256                 value = astman_get_header(m, hdr);
1257                 if (!ast_strlen_zero(value) && *value == '>') {
1258                         object = 1;
1259                         value++;
1260                 }
1261                 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1262                 match = astman_get_header(m, hdr);
1263                 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
1264                 line = astman_get_header(m, hdr);
1265                 if (!strcasecmp(action, "newcat")) {
1266                         if (ast_strlen_zero(cat))
1267                                 return UNSPECIFIED_CATEGORY;
1268                         if (!(category = ast_category_new(cat, dfn, -1)))
1269                                 return FAILURE_ALLOCATION;
1270                         if (ast_strlen_zero(match)) {
1271                                 ast_category_append(cfg, category);
1272                         } else
1273                                 ast_category_insert(cfg, category, match);
1274                 } else if (!strcasecmp(action, "renamecat")) {
1275                         if (ast_strlen_zero(cat) || ast_strlen_zero(value))
1276                                 return UNSPECIFIED_ARGUMENT;
1277                         if (!(category = ast_category_get(cfg, cat)))
1278                                 return UNKNOWN_CATEGORY;
1279                         ast_category_rename(category, value);
1280                 } else if (!strcasecmp(action, "delcat")) {
1281                         if (ast_strlen_zero(cat))
1282                                 return UNSPECIFIED_CATEGORY;
1283                         if (ast_category_delete(cfg, cat))
1284                                 return FAILURE_DELCAT;
1285                 } else if (!strcasecmp(action, "emptycat")) {
1286                         if (ast_strlen_zero(cat))
1287                                 return UNSPECIFIED_CATEGORY;
1288                         if (ast_category_empty(cfg, cat))
1289                                 return FAILURE_EMPTYCAT;
1290                 } else if (!strcasecmp(action, "update")) {
1291                         if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1292                                 return UNSPECIFIED_ARGUMENT;
1293                         if (!(category = ast_category_get(cfg,cat)))
1294                                 return UNKNOWN_CATEGORY;
1295                         if (ast_variable_update(category, var, value, match, object))
1296                                 return FAILURE_UPDATE;
1297                 } else if (!strcasecmp(action, "delete")) {
1298                         if (ast_strlen_zero(cat) || (ast_strlen_zero(var) && ast_strlen_zero(line)))
1299                                 return UNSPECIFIED_ARGUMENT;
1300                         if (!(category = ast_category_get(cfg, cat)))
1301                                 return UNKNOWN_CATEGORY;
1302                         if (ast_variable_delete(category, var, match, line))
1303                                 return FAILURE_DELETE;
1304                 } else if (!strcasecmp(action, "append")) {
1305                         if (ast_strlen_zero(cat) || ast_strlen_zero(var))
1306                                 return UNSPECIFIED_ARGUMENT;
1307                         if (!(category = ast_category_get(cfg, cat)))
1308                                 return UNKNOWN_CATEGORY;        
1309                         if (!(v = ast_variable_new(var, value, dfn)))
1310                                 return FAILURE_ALLOCATION;
1311                         if (object || (match && !strcasecmp(match, "object")))
1312                                 v->object = 1;
1313                         ast_variable_append(category, v);
1314                 } else if (!strcasecmp(action, "insert")) {
1315                         if (ast_strlen_zero(cat) || ast_strlen_zero(var) || ast_strlen_zero(line))
1316                                 return UNSPECIFIED_ARGUMENT;
1317                         if (!(category = ast_category_get(cfg, cat)))
1318                                 return UNKNOWN_CATEGORY;
1319                         if (!(v = ast_variable_new(var, value, dfn)))
1320                                 return FAILURE_ALLOCATION;
1321                         ast_variable_insert(category, v, line);
1322                 }
1323                 else {
1324                         ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
1325                         return UNKNOWN_ACTION;
1326                 }
1327         }
1328         return 0;
1329 }
1330
1331 static char mandescr_updateconfig[] =
1332 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1333 "configuration elements in Asterisk configuration files.\n"
1334 "Variables (X's represent 6 digit number beginning with 000000):\n"
1335 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
1336 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
1337 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
1338 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
1339 "   Cat-XXXXXX:    Category to operate on\n"
1340 "   Var-XXXXXX:    Variable to work on\n"
1341 "   Value-XXXXXX:  Value to work on\n"
1342 "   Match-XXXXXX:  Extra match required to match line\n"
1343 "   Line-XXXXXX:   Line in category to operate on (used with delete and insert actions)\n";
1344
1345 static int action_updateconfig(struct mansession *s, const struct message *m)
1346 {
1347         struct ast_config *cfg;
1348         const char *sfn = astman_get_header(m, "SrcFilename");
1349         const char *dfn = astman_get_header(m, "DstFilename");
1350         int res;
1351         const char *rld = astman_get_header(m, "Reload");
1352         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1353         enum error_type result;
1354
1355         if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1356                 astman_send_error(s, m, "Filename not specified");
1357                 return 0;
1358         }
1359         if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
1360                 astman_send_error(s, m, "Config file not found");
1361                 return 0;
1362         }
1363         result = handle_updates(s, m, cfg, dfn);
1364         if (!result) {
1365                 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1366                 res = config_text_file_save(dfn, cfg, "Manager");
1367                 ast_config_destroy(cfg);
1368                 if (res) {
1369                         astman_send_error(s, m, "Save of config failed");
1370                         return 0;
1371                 }
1372                 astman_send_ack(s, m, NULL);
1373                 if (!ast_strlen_zero(rld)) {
1374                         if (ast_true(rld))
1375                                 rld = NULL;
1376                         ast_module_reload(rld);
1377                 }
1378         } else {
1379                 ast_config_destroy(cfg);
1380                 switch(result) {
1381                 case UNKNOWN_ACTION:
1382                         astman_send_error(s, m, "Unknown action command");
1383                         break;
1384                 case UNKNOWN_CATEGORY:
1385                         astman_send_error(s, m, "Given category does not exist");
1386                         break;
1387                 case UNSPECIFIED_CATEGORY:
1388                         astman_send_error(s, m, "Category not specified");
1389                         break;
1390                 case UNSPECIFIED_ARGUMENT:
1391                         astman_send_error(s, m, "Problem with category, value, or line (if required)");
1392                         break;
1393                 case FAILURE_ALLOCATION:
1394                         astman_send_error(s, m, "Memory allocation failure, this should not happen");
1395                         break;
1396                 case FAILURE_DELCAT:
1397                         astman_send_error(s, m, "Delete category did not complete successfully");
1398                         break;
1399                 case FAILURE_EMPTYCAT:
1400                         astman_send_error(s, m, "Empty category did not complete successfully");
1401                         break;
1402                 case FAILURE_UPDATE:
1403                         astman_send_error(s, m, "Update did not complete successfully");
1404                         break;
1405                 case FAILURE_DELETE:
1406                         astman_send_error(s, m, "Delete did not complete successfully");
1407                         break;
1408                 case FAILURE_APPEND:
1409                         astman_send_error(s, m, "Append did not complete successfully");
1410                         break;
1411                 }
1412         }
1413         return 0;
1414 }
1415
1416 static char mandescr_createconfig[] =
1417 "Description: A 'CreateConfig' action will create an empty file in the\n"
1418 "configuration directory. This action is intended to be used before an\n"
1419 "UpdateConfig action.\n"
1420 "Variables\n"
1421 "   Filename:   The configuration filename to create (e.g. foo.conf)\n";
1422
1423 static int action_createconfig(struct mansession *s, const struct message *m)
1424 {
1425         int fd;
1426         const char *fn = astman_get_header(m, "Filename");
1427         struct ast_str *filepath = ast_str_alloca(PATH_MAX);
1428         ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
1429         ast_str_append(&filepath, 0, "%s", fn);
1430
1431         if ((fd = open(filepath->str, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
1432                 close(fd);
1433                 astman_send_ack(s, m, "New configuration file created successfully");
1434         } else 
1435                 astman_send_error(s, m, strerror(errno));
1436
1437         return 0;
1438 }
1439
1440 /*! \brief Manager WAITEVENT */
1441 static char mandescr_waitevent[] =
1442 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
1443 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
1444 "session, events will be generated and queued.\n"
1445 "Variables: \n"
1446 "   Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1447
1448 static int action_waitevent(struct mansession *s, const struct message *m)
1449 {
1450         const char *timeouts = astman_get_header(m, "Timeout");
1451         int timeout = -1;
1452         int x;
1453         int needexit = 0;
1454         const char *id = astman_get_header(m, "ActionID");
1455         char idText[256];
1456
1457         if (!ast_strlen_zero(id))
1458                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1459         else
1460                 idText[0] = '\0';
1461
1462         if (!ast_strlen_zero(timeouts)) {
1463                 sscanf(timeouts, "%i", &timeout);
1464                 if (timeout < -1)
1465                         timeout = -1;
1466                 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1467         }
1468
1469         ast_mutex_lock(&s->__lock);
1470         if (s->waiting_thread != AST_PTHREADT_NULL)
1471                 pthread_kill(s->waiting_thread, SIGURG);
1472
1473         if (s->managerid) { /* AMI-over-HTTP session */
1474                 /*
1475                  * Make sure the timeout is within the expire time of the session,
1476                  * as the client will likely abort the request if it does not see
1477                  * data coming after some amount of time.
1478                  */
1479                 time_t now = time(NULL);
1480                 int max = s->sessiontimeout - now - 10;
1481
1482                 if (max < 0)    /* We are already late. Strange but possible. */
1483                         max = 0;
1484                 if (timeout < 0 || timeout > max)
1485                         timeout = max;
1486                 if (!s->send_events)    /* make sure we record events */
1487                         s->send_events = -1;
1488         }
1489         ast_mutex_unlock(&s->__lock);
1490
1491         /* XXX should this go inside the lock ? */
1492         s->waiting_thread = pthread_self();     /* let new events wake up this thread */
1493         ast_debug(1, "Starting waiting for an event!\n");
1494
1495         for (x = 0; x < timeout || timeout < 0; x++) {
1496                 ast_mutex_lock(&s->__lock);
1497                 if (NEW_EVENT(s))
1498                         needexit = 1;
1499                 /* We can have multiple HTTP session point to the same mansession entry.
1500                  * The way we deal with it is not very nice: newcomers kick out the previous
1501                  * HTTP session. XXX this needs to be improved.
1502                  */
1503                 if (s->waiting_thread != pthread_self())
1504                         needexit = 1;
1505                 if (s->needdestroy)
1506                         needexit = 1;
1507                 ast_mutex_unlock(&s->__lock);
1508                 if (needexit)
1509                         break;
1510                 if (s->managerid == 0) {        /* AMI session */
1511                         if (ast_wait_for_input(s->fd, 1000))
1512                                 break;
1513                 } else {        /* HTTP session */
1514                         sleep(1);
1515                 }
1516         }
1517         ast_debug(1, "Finished waiting for an event!\n");
1518         ast_mutex_lock(&s->__lock);
1519         if (s->waiting_thread == pthread_self()) {
1520                 struct eventqent *eqe;
1521                 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1522                 while ( (eqe = NEW_EVENT(s)) ) {
1523                         ref_event(eqe);
1524                         if (((s->readperm & eqe->category) == eqe->category) &&
1525                             ((s->send_events & eqe->category) == eqe->category)) {
1526                                 astman_append(s, "%s", eqe->eventdata);
1527                         }
1528                         s->last_ev = unref_event(s->last_ev);
1529                 }
1530                 astman_append(s,
1531                         "Event: WaitEventComplete\r\n"
1532                         "%s"
1533                         "\r\n", idText);
1534                 s->waiting_thread = AST_PTHREADT_NULL;
1535         } else {
1536                 ast_debug(1, "Abandoning event request!\n");
1537         }
1538         ast_mutex_unlock(&s->__lock);
1539         return 0;
1540 }
1541
1542 static char mandescr_listcommands[] =
1543 "Description: Returns the action name and synopsis for every\n"
1544 "  action that is available to the user\n"
1545 "Variables: NONE\n";
1546
1547 /*! \note The actionlock is read-locked by the caller of this function */
1548 static int action_listcommands(struct mansession *s, const struct message *m)
1549 {
1550         struct manager_action *cur;
1551         struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1552
1553         astman_start_ack(s, m);
1554         AST_RWLIST_TRAVERSE(&actions, cur, list) {
1555                 if (s->writeperm & cur->authority || cur->authority == 0)
1556                         astman_append(s, "%s: %s (Priv: %s)\r\n",
1557                                 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1558         }
1559         astman_append(s, "\r\n");
1560
1561         return 0;
1562 }
1563
1564 static char mandescr_events[] =
1565 "Description: Enable/Disable sending of events to this manager\n"
1566 "  client.\n"
1567 "Variables:\n"
1568 "       EventMask: 'on' if all events should be sent,\n"
1569 "               'off' if no events should be sent,\n"
1570 "               'system,call,log' to select which flags events should have to be sent.\n";
1571
1572 static int action_events(struct mansession *s, const struct message *m)
1573 {
1574         const char *mask = astman_get_header(m, "EventMask");
1575         int res;
1576
1577         res = set_eventmask(s, mask);
1578         if (res > 0)
1579                 astman_send_response(s, m, "Success", "Events: On\r\n");
1580         else if (res == 0)
1581                 astman_send_response(s, m, "Success", "Events: Off\r\n");
1582
1583         return 0;
1584 }
1585
1586 static char mandescr_logoff[] =
1587 "Description: Logoff this manager session\n"
1588 "Variables: NONE\n";
1589
1590 static int action_logoff(struct mansession *s, const struct message *m)
1591 {
1592         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1593         return -1;
1594 }
1595
1596 static int action_login(struct mansession *s, const struct message *m)
1597 {
1598         if (authenticate(s, m)) {
1599                 sleep(1);
1600                 astman_send_error(s, m, "Authentication failed");
1601                 return -1;
1602         }
1603         s->authenticated = 1;
1604         if (manager_displayconnects(s))
1605                 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1606         ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1607         astman_send_ack(s, m, "Authentication accepted");
1608         return 0;
1609 }
1610
1611 static int action_challenge(struct mansession *s, const struct message *m)
1612 {
1613         const char *authtype = astman_get_header(m, "AuthType");
1614
1615         if (!strcasecmp(authtype, "MD5")) {
1616                 if (ast_strlen_zero(s->challenge))
1617                         snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1618                 ast_mutex_lock(&s->__lock);
1619                 astman_start_ack(s, m);
1620                 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1621                 ast_mutex_unlock(&s->__lock);
1622         } else {
1623                 astman_send_error(s, m, "Must specify AuthType");
1624         }
1625         return 0;
1626 }
1627
1628 static char mandescr_hangup[] =
1629 "Description: Hangup a channel\n"
1630 "Variables: \n"
1631 "       Channel: The channel name to be hungup\n";
1632
1633 static int action_hangup(struct mansession *s, const struct message *m)
1634 {
1635         struct ast_channel *c = NULL;
1636         const char *name = astman_get_header(m, "Channel");
1637         if (ast_strlen_zero(name)) {
1638                 astman_send_error(s, m, "No channel specified");
1639                 return 0;
1640         }
1641         c = ast_get_channel_by_name_locked(name);
1642         if (!c) {
1643                 astman_send_error(s, m, "No such channel");
1644                 return 0;
1645         }
1646         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1647         ast_channel_unlock(c);
1648         astman_send_ack(s, m, "Channel Hungup");
1649         return 0;
1650 }
1651
1652 static char mandescr_setvar[] =
1653 "Description: Set a global or local channel variable.\n"
1654 "Variables: (Names marked with * are required)\n"
1655 "       Channel: Channel to set variable for\n"
1656 "       *Variable: Variable name\n"
1657 "       *Value: Value\n";
1658
1659 static int action_setvar(struct mansession *s, const struct message *m)
1660 {
1661         struct ast_channel *c = NULL;
1662         const char *name = astman_get_header(m, "Channel");
1663         const char *varname = astman_get_header(m, "Variable");
1664         const char *varval = astman_get_header(m, "Value");
1665
1666         if (ast_strlen_zero(varname)) {
1667                 astman_send_error(s, m, "No variable specified");
1668                 return 0;
1669         }
1670
1671         if (!ast_strlen_zero(name)) {
1672                 c = ast_get_channel_by_name_locked(name);
1673                 if (!c) {
1674                         astman_send_error(s, m, "No such channel");
1675                         return 0;
1676                 }
1677         }
1678
1679         pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1680
1681         if (c)
1682                 ast_channel_unlock(c);
1683
1684         astman_send_ack(s, m, "Variable Set");
1685
1686         return 0;
1687 }
1688
1689 static char mandescr_getvar[] =
1690 "Description: Get the value of a global or local channel variable.\n"
1691 "Variables: (Names marked with * are required)\n"
1692 "       Channel: Channel to read variable from\n"
1693 "       *Variable: Variable name\n"
1694 "       ActionID: Optional Action id for message matching.\n";
1695
1696 static int action_getvar(struct mansession *s, const struct message *m)
1697 {
1698         struct ast_channel *c = NULL;
1699         const char *name = astman_get_header(m, "Channel");
1700         const char *varname = astman_get_header(m, "Variable");
1701         char *varval;
1702         char workspace[1024] = "";
1703
1704         if (ast_strlen_zero(varname)) {
1705                 astman_send_error(s, m, "No variable specified");
1706                 return 0;
1707         }
1708
1709         if (!ast_strlen_zero(name)) {
1710                 c = ast_get_channel_by_name_locked(name);
1711                 if (!c) {
1712                         astman_send_error(s, m, "No such channel");
1713                         return 0;
1714                 }
1715         }
1716
1717         if (varname[strlen(varname) - 1] == ')') {
1718                 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1719                 varval = workspace;
1720         } else {
1721                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1722         }
1723
1724         if (c)
1725                 ast_channel_unlock(c);
1726         astman_start_ack(s, m);
1727         astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1728
1729         return 0;
1730 }
1731
1732
1733 /*! \brief Manager "status" command to show channels */
1734 /* Needs documentation... */
1735 static int action_status(struct mansession *s, const struct message *m)
1736 {
1737         const char *name = astman_get_header(m, "Channel");
1738         struct ast_channel *c;
1739         char bridge[256];
1740         struct timeval now = ast_tvnow();
1741         long elapsed_seconds = 0;
1742         int channels = 0;
1743         int all = ast_strlen_zero(name); /* set if we want all channels */
1744         const char *id = astman_get_header(m, "ActionID");
1745         char idText[256];
1746
1747         if (!ast_strlen_zero(id))
1748                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1749         else
1750                 idText[0] = '\0';
1751
1752         if (all)
1753                 c = ast_channel_walk_locked(NULL);
1754         else {
1755                 c = ast_get_channel_by_name_locked(name);
1756                 if (!c) {
1757                         astman_send_error(s, m, "No such channel");
1758                         return 0;
1759                 }
1760         }
1761         astman_send_ack(s, m, "Channel status will follow");
1762
1763         /* if we look by name, we break after the first iteration */
1764         while (c) {
1765                 channels++;
1766                 if (c->_bridge)
1767                         snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1768                 else
1769                         bridge[0] = '\0';
1770                 if (c->pbx) {
1771                         if (c->cdr) {
1772                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1773                         }
1774                         astman_append(s,
1775                         "Event: Status\r\n"
1776                         "Privilege: Call\r\n"
1777                         "Channel: %s\r\n"
1778                         "CallerIDNum: %s\r\n"
1779                         "CallerIDName: %s\r\n"
1780                         "Accountcode: %s\r\n"
1781                         "ChannelState: %d\r\n"
1782                         "ChannelStateDesc: %s\r\n"
1783                         "Context: %s\r\n"
1784                         "Extension: %s\r\n"
1785                         "Priority: %d\r\n"
1786                         "Seconds: %ld\r\n"
1787                         "%s"
1788                         "Uniqueid: %s\r\n"
1789                         "%s"
1790                         "\r\n",
1791                         c->name,
1792                         S_OR(c->cid.cid_num, ""),
1793                         S_OR(c->cid.cid_name, ""),
1794                         c->accountcode,
1795                         c->_state,
1796                         ast_state2str(c->_state), c->context,
1797                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1798                 } else {
1799                         astman_append(s,
1800                         "Event: Status\r\n"
1801                         "Privilege: Call\r\n"
1802                         "Channel: %s\r\n"
1803                         "CallerIDNum: %s\r\n"
1804                         "CallerIDName: %s\r\n"
1805                         "Account: %s\r\n"
1806                         "State: %s\r\n"
1807                         "%s"
1808                         "Uniqueid: %s\r\n"
1809                         "%s"
1810                         "\r\n",
1811                         c->name,
1812                         S_OR(c->cid.cid_num, "<unknown>"),
1813                         S_OR(c->cid.cid_name, "<unknown>"),
1814                         c->accountcode,
1815                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
1816                 }
1817                 ast_channel_unlock(c);
1818                 if (!all)
1819                         break;
1820                 c = ast_channel_walk_locked(c);
1821         }
1822         astman_append(s,
1823         "Event: StatusComplete\r\n"
1824         "%s"
1825         "Items: %d\r\n"
1826         "\r\n", idText, channels);
1827         return 0;
1828 }
1829
1830 static char mandescr_sendtext[] =
1831 "Description: Sends A Text Message while in a call.\n"
1832 "Variables: (Names marked with * are required)\n"
1833 "       *Channel: Channel to send message to\n"
1834 "       *Message: Message to send\n"
1835 "       ActionID: Optional Action id for message matching.\n";
1836
1837 static int action_sendtext(struct mansession *s, const struct message *m)
1838 {
1839         struct ast_channel *c = NULL;
1840         const char *name = astman_get_header(m, "Channel");
1841         const char *textmsg = astman_get_header(m, "Message");
1842         int res = 0;
1843
1844         if (ast_strlen_zero(name)) {
1845                 astman_send_error(s, m, "No channel specified");
1846                 return 0;
1847         }
1848
1849         if (ast_strlen_zero(textmsg)) {
1850                 astman_send_error(s, m, "No Message specified");
1851                 return 0;
1852         }
1853
1854         c = ast_get_channel_by_name_locked(name);
1855         if (!c) {
1856                 astman_send_error(s, m, "No such channel");
1857                 return 0;
1858         }
1859
1860         res = ast_sendtext(c, textmsg);
1861         ast_channel_unlock(c);
1862         
1863         if (res > 0)
1864                 astman_send_ack(s, m, "Success");
1865         else
1866                 astman_send_error(s, m, "Failure");
1867         
1868         return res;
1869 }
1870
1871 static char mandescr_redirect[] =
1872 "Description: Redirect (transfer) a call.\n"
1873 "Variables: (Names marked with * are required)\n"
1874 "       *Channel: Channel to redirect\n"
1875 "       ExtraChannel: Second call leg to transfer (optional)\n"
1876 "       *Exten: Extension to transfer to\n"
1877 "       *Context: Context to transfer to\n"
1878 "       *Priority: Priority to transfer to\n"
1879 "       ActionID: Optional Action id for message matching.\n";
1880
1881 /*! \brief  action_redirect: The redirect manager command */
1882 static int action_redirect(struct mansession *s, const struct message *m)
1883 {
1884         const char *name = astman_get_header(m, "Channel");
1885         const char *name2 = astman_get_header(m, "ExtraChannel");
1886         const char *exten = astman_get_header(m, "Exten");
1887         const char *context = astman_get_header(m, "Context");
1888         const char *priority = astman_get_header(m, "Priority");
1889         struct ast_channel *chan, *chan2 = NULL;
1890         int pi = 0;
1891         int res;
1892
1893         if (ast_strlen_zero(name)) {
1894                 astman_send_error(s, m, "Channel not specified");
1895                 return 0;
1896         }
1897         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1898                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1899                         astman_send_error(s, m, "Invalid priority\n");
1900                         return 0;
1901                 }
1902         }
1903         /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1904         chan = ast_get_channel_by_name_locked(name);
1905         if (!chan) {
1906                 char buf[256];
1907                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1908                 astman_send_error(s, m, buf);
1909                 return 0;
1910         }
1911         if (ast_check_hangup(chan)) {
1912                 astman_send_error(s, m, "Redirect failed, channel not up.\n");
1913                 ast_channel_unlock(chan);
1914                 return 0;
1915         }
1916         if (!ast_strlen_zero(name2))
1917                 chan2 = ast_get_channel_by_name_locked(name2);
1918         if (chan2 && ast_check_hangup(chan2)) {
1919                 astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
1920                 ast_channel_unlock(chan);
1921                 ast_channel_unlock(chan2);
1922                 return 0;
1923         }
1924         res = ast_async_goto(chan, context, exten, pi);
1925         if (!res) {
1926                 if (!ast_strlen_zero(name2)) {
1927                         if (chan2)
1928                                 res = ast_async_goto(chan2, context, exten, pi);
1929                         else
1930                                 res = -1;
1931                         if (!res)
1932                                 astman_send_ack(s, m, "Dual Redirect successful");
1933                         else
1934                                 astman_send_error(s, m, "Secondary redirect failed");
1935                 } else
1936                         astman_send_ack(s, m, "Redirect successful");
1937         } else
1938                 astman_send_error(s, m, "Redirect failed");
1939         if (chan)
1940                 ast_channel_unlock(chan);
1941         if (chan2)
1942                 ast_channel_unlock(chan2);
1943         return 0;
1944 }
1945
1946 static char mandescr_atxfer[] =
1947 "Description: Attended transfer.\n"
1948 "Variables: (Names marked with * are required)\n"
1949 "       *Channel: Transferer's channel\n"
1950 "       *Exten: Extension to transfer to\n"
1951 "       *Context: Context to transfer to\n"
1952 "       *Priority: Priority to transfer to\n"
1953 "       ActionID: Optional Action id for message matching.\n";
1954
1955 static int action_atxfer(struct mansession *s, const struct message *m)
1956 {
1957         const char *name = astman_get_header(m, "Channel");
1958         const char *exten = astman_get_header(m, "Exten");
1959         const char *context = astman_get_header(m, "Context");
1960         const char *priority = astman_get_header(m, "Priority");
1961         struct ast_channel *chan = NULL;
1962         struct ast_call_feature *atxfer_feature = NULL;
1963         char *feature_code = NULL;
1964         int priority_int = 0;
1965
1966         if (ast_strlen_zero(name)) { 
1967                 astman_send_error(s, m, "No channel specified\n");
1968                 return 0;
1969         }
1970         if (ast_strlen_zero(exten)) {
1971                 astman_send_error(s, m, "No extension specified\n");
1972                 return 0;
1973         }
1974         if (ast_strlen_zero(context)) {
1975                 astman_send_error(s, m, "No context specified\n");
1976                 return 0;
1977         }
1978         if (ast_strlen_zero(priority)) {
1979                 astman_send_error(s, m, "No priority specified\n");
1980                 return 0;
1981         }
1982
1983         if (sscanf(priority, "%d", &priority_int) != 1 && (priority_int = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1984                 astman_send_error(s, m, "Invalid Priority\n");
1985                 return 0;
1986         }
1987
1988         if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
1989                 astman_send_error(s, m, "No attended transfer feature found\n");
1990                 return 0;
1991         }
1992
1993         if (!(chan = ast_get_channel_by_name_locked(name))) {
1994                 astman_send_error(s, m, "Channel specified does not exist\n");
1995                 return 0;
1996         }
1997
1998         for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
1999                 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2000                 ast_queue_frame(chan, &f);
2001         }
2002
2003         for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
2004                 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2005                 ast_queue_frame(chan, &f);
2006         }
2007
2008         astman_send_ack(s, m, "Atxfer successfully queued\n");
2009         ast_channel_unlock(chan);
2010
2011         return 0;
2012 }
2013
2014 static char mandescr_command[] =
2015 "Description: Run a CLI command.\n"
2016 "Variables: (Names marked with * are required)\n"
2017 "       *Command: Asterisk CLI command to run\n"
2018 "       ActionID: Optional Action id for message matching.\n";
2019
2020 /*! \brief  Manager command "command" - execute CLI command */
2021 static int action_command(struct mansession *s, const struct message *m)
2022 {
2023         const char *cmd = astman_get_header(m, "Command");
2024         const char *id = astman_get_header(m, "ActionID");
2025         char *buf, *final_buf;
2026         char template[] = "/tmp/ast-ami-XXXXXX";        /* template for temporary file */
2027         int fd = mkstemp(template), i = 0;
2028         off_t l;
2029
2030         for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
2031                 if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
2032                         astman_send_error(s, m, "Command blacklisted");
2033                         return 0;
2034                 }
2035         }
2036
2037         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
2038         if (!ast_strlen_zero(id))
2039                 astman_append(s, "ActionID: %s\r\n", id);
2040         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
2041         ast_cli_command(fd, cmd);       /* XXX need to change this to use a FILE * */
2042         l = lseek(fd, 0, SEEK_END);     /* how many chars available */
2043
2044         /* This has a potential to overflow the stack.  Hence, use the heap. */
2045         buf = ast_calloc(1, l + 1);
2046         final_buf = ast_calloc(1, l + 1);
2047         if (buf) {
2048                 lseek(fd, 0, SEEK_SET);
2049                 read(fd, buf, l);
2050                 buf[l] = '\0';
2051                 if (final_buf) {
2052                         term_strip(final_buf, buf, l);
2053                         final_buf[l] = '\0';
2054                 }
2055                 astman_append(s, "%s", S_OR(final_buf, buf));
2056                 ast_free(buf);
2057         }
2058         close(fd);
2059         unlink(template);
2060         astman_append(s, "--END COMMAND--\r\n\r\n");
2061         if (final_buf)
2062                 ast_free(final_buf);
2063         return 0;
2064 }
2065
2066 /* helper function for originate */
2067 struct fast_originate_helper {
2068         char tech[AST_MAX_EXTENSION];
2069         char data[AST_MAX_EXTENSION];
2070         int timeout;
2071         char app[AST_MAX_APP];
2072         char appdata[AST_MAX_EXTENSION];
2073         char cid_name[AST_MAX_EXTENSION];
2074         char cid_num[AST_MAX_EXTENSION];
2075         char context[AST_MAX_CONTEXT];
2076         char exten[AST_MAX_EXTENSION];
2077         char idtext[AST_MAX_EXTENSION];
2078         char account[AST_MAX_ACCOUNT_CODE];
2079         int priority;
2080         struct ast_variable *vars;
2081 };
2082
2083 static void *fast_originate(void *data)
2084 {
2085         struct fast_originate_helper *in = data;
2086         int res;
2087         int reason = 0;
2088         struct ast_channel *chan = NULL;
2089         char requested_channel[AST_CHANNEL_NAME];
2090
2091         if (!ast_strlen_zero(in->app)) {
2092                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
2093                         S_OR(in->cid_num, NULL),
2094                         S_OR(in->cid_name, NULL),
2095                         in->vars, in->account, &chan);
2096         } else {
2097                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
2098                         S_OR(in->cid_num, NULL),
2099                         S_OR(in->cid_name, NULL),
2100                         in->vars, in->account, &chan);
2101         }
2102
2103         if (!chan)
2104                 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);     
2105         /* Tell the manager what happened with the channel */
2106         manager_event(EVENT_FLAG_CALL, "OriginateResponse",
2107                 "%s"
2108                 "Response: %s\r\n"
2109                 "Channel: %s\r\n"
2110                 "Context: %s\r\n"
2111                 "Exten: %s\r\n"
2112                 "Reason: %d\r\n"
2113                 "Uniqueid: %s\r\n"
2114                 "CallerIDNum: %s\r\n"
2115                 "CallerIDName: %s\r\n",
2116                 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason, 
2117                 chan ? chan->uniqueid : "<null>",
2118                 S_OR(in->cid_num, "<unknown>"),
2119                 S_OR(in->cid_name, "<unknown>")
2120                 );
2121
2122         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2123         if (chan)
2124                 ast_channel_unlock(chan);
2125         ast_free(in);
2126         return NULL;
2127 }
2128
2129 static char mandescr_originate[] =
2130 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
2131 "  Application/Data\n"
2132 "Variables: (Names marked with * are required)\n"
2133 "       *Channel: Channel name to call\n"
2134 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
2135 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
2136 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
2137 "       Application: Application to use\n"
2138 "       Data: Data to use (requires 'Application')\n"
2139 "       Timeout: How long to wait for call to be answered (in ms)\n"
2140 "       CallerID: Caller ID to be set on the outgoing channel\n"
2141 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
2142 "       Account: Account code\n"
2143 "       Async: Set to 'true' for fast origination\n";
2144
2145 static int action_originate(struct mansession *s, const struct message *m)
2146 {
2147         const char *name = astman_get_header(m, "Channel");
2148         const char *exten = astman_get_header(m, "Exten");
2149         const char *context = astman_get_header(m, "Context");
2150         const char *priority = astman_get_header(m, "Priority");
2151         const char *timeout = astman_get_header(m, "Timeout");
2152         const char *callerid = astman_get_header(m, "CallerID");
2153         const char *account = astman_get_header(m, "Account");
2154         const char *app = astman_get_header(m, "Application");
2155         const char *appdata = astman_get_header(m, "Data");
2156         const char *async = astman_get_header(m, "Async");
2157         const char *id = astman_get_header(m, "ActionID");
2158         struct ast_variable *vars = astman_get_variables(m);
2159         char *tech, *data;
2160         char *l = NULL, *n = NULL;
2161         int pi = 0;
2162         int res;
2163         int to = 30000;
2164         int reason = 0;
2165         char tmp[256];
2166         char tmp2[256];
2167
2168         pthread_t th;
2169         if (!name) {
2170                 astman_send_error(s, m, "Channel not specified");
2171                 return 0;
2172         }
2173         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2174                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2175                         astman_send_error(s, m, "Invalid priority\n");
2176                         return 0;
2177                 }
2178         }
2179         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2180                 astman_send_error(s, m, "Invalid timeout\n");
2181                 return 0;
2182         }
2183         ast_copy_string(tmp, name, sizeof(tmp));
2184         tech = tmp;
2185         data = strchr(tmp, '/');
2186         if (!data) {
2187                 astman_send_error(s, m, "Invalid channel\n");
2188                 return 0;
2189         }
2190         *data++ = '\0';
2191         ast_copy_string(tmp2, callerid, sizeof(tmp2));
2192         ast_callerid_parse(tmp2, &n, &l);
2193         if (n) {
2194                 if (ast_strlen_zero(n))
2195                         n = NULL;
2196         }
2197         if (l) {
2198                 ast_shrink_phone_number(l);
2199                 if (ast_strlen_zero(l))
2200                         l = NULL;
2201         }
2202         if (ast_true(async)) {
2203                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2204                 if (!fast) {
2205                         res = -1;
2206                 } else {
2207                         if (!ast_strlen_zero(id))
2208                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2209                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2210                         ast_copy_string(fast->data, data, sizeof(fast->data));
2211                         ast_copy_string(fast->app, app, sizeof(fast->app));
2212                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2213                         if (l)
2214                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2215                         if (n)
2216                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2217                         fast->vars = vars;
2218                         ast_copy_string(fast->context, context, sizeof(fast->context));
2219                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2220                         ast_copy_string(fast->account, account, sizeof(fast->account));
2221                         fast->timeout = to;
2222                         fast->priority = pi;
2223                         if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2224                                 res = -1;
2225                         } else {
2226                                 res = 0;
2227                         }
2228                 }
2229         } else if (!ast_strlen_zero(app)) {
2230                 /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
2231                 if (!(s->writeperm & EVENT_FLAG_SYSTEM)
2232                         && (
2233                                 strcasestr(app, "system") == 0 || /* System(rm -rf /)
2234                                                                      TrySystem(rm -rf /)       */
2235                                 strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
2236                                                                      TryExec(System(rm -rf /)) */
2237                                 strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
2238                                                                      EAGI(/bin/rm,-rf /)       */
2239                                 strstr(appdata, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
2240                                 strstr(appdata, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
2241                                 )) {
2242                         astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
2243                         return 0;
2244                 }
2245                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2246         } else {
2247                 if (exten && context && pi)
2248                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2249                 else {
2250                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2251                         return 0;
2252                 }
2253         }
2254         if (!res)
2255                 astman_send_ack(s, m, "Originate successfully queued");
2256         else
2257                 astman_send_error(s, m, "Originate failed");
2258         return 0;
2259 }
2260
2261 /*! \brief Help text for manager command mailboxstatus
2262  */
2263 static char mandescr_mailboxstatus[] =
2264 "Description: Checks a voicemail account for status.\n"
2265 "Variables: (Names marked with * are required)\n"
2266 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2267 "       ActionID: Optional ActionID for message matching.\n"
2268 "Returns number of messages.\n"
2269 "       Message: Mailbox Status\n"
2270 "       Mailbox: <mailboxid>\n"
2271 "       Waiting: <count>\n"
2272 "\n";
2273
2274 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2275 {
2276         const char *mailbox = astman_get_header(m, "Mailbox");
2277         int ret;
2278
2279         if (ast_strlen_zero(mailbox)) {
2280                 astman_send_error(s, m, "Mailbox not specified");
2281                 return 0;
2282         }
2283         ret = ast_app_has_voicemail(mailbox, NULL);
2284         astman_start_ack(s, m);
2285         astman_append(s, "Message: Mailbox Status\r\n"
2286                          "Mailbox: %s\r\n"
2287                          "Waiting: %d\r\n\r\n", mailbox, ret);
2288         return 0;
2289 }
2290
2291 static char mandescr_mailboxcount[] =
2292 "Description: Checks a voicemail account for new messages.\n"
2293 "Variables: (Names marked with * are required)\n"
2294 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2295 "       ActionID: Optional ActionID for message matching.\n"
2296 "Returns number of new and old messages.\n"
2297 "       Message: Mailbox Message Count\n"
2298 "       Mailbox: <mailboxid>\n"
2299 "       NewMessages: <count>\n"
2300 "       OldMessages: <count>\n"
2301 "\n";
2302 static int action_mailboxcount(struct mansession *s, const struct message *m)
2303 {
2304         const char *mailbox = astman_get_header(m, "Mailbox");
2305         int newmsgs = 0, oldmsgs = 0;
2306
2307         if (ast_strlen_zero(mailbox)) {
2308                 astman_send_error(s, m, "Mailbox not specified");
2309                 return 0;
2310         }
2311         ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
2312         astman_start_ack(s, m);
2313         astman_append(s,   "Message: Mailbox Message Count\r\n"
2314                            "Mailbox: %s\r\n"
2315                            "NewMessages: %d\r\n"
2316                            "OldMessages: %d\r\n"
2317                            "\r\n",
2318                            mailbox, newmsgs, oldmsgs);
2319         return 0;
2320 }
2321
2322 static char mandescr_extensionstate[] =
2323 "Description: Report the extension state for given extension.\n"
2324 "  If the extension has a hint, will use devicestate to check\n"
2325 "  the status of the device connected to the extension.\n"
2326 "Variables: (Names marked with * are required)\n"
2327 "       *Exten: Extension to check state on\n"
2328 "       *Context: Context for extension\n"
2329 "       ActionId: Optional ID for this transaction\n"
2330 "Will return an \"Extension Status\" message.\n"
2331 "The response will include the hint for the extension and the status.\n";
2332
2333 static int action_extensionstate(struct mansession *s, const struct message *m)
2334 {
2335         const char *exten = astman_get_header(m, "Exten");
2336         const char *context = astman_get_header(m, "Context");
2337         char hint[256] = "";
2338         int status;
2339         if (ast_strlen_zero(exten)) {
2340                 astman_send_error(s, m, "Extension not specified");
2341                 return 0;
2342         }
2343         if (ast_strlen_zero(context))
2344                 context = "default";
2345         status = ast_extension_state(NULL, context, exten);
2346         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2347         astman_start_ack(s, m);
2348         astman_append(s,   "Message: Extension Status\r\n"
2349                            "Exten: %s\r\n"
2350                            "Context: %s\r\n"
2351                            "Hint: %s\r\n"
2352                            "Status: %d\r\n\r\n",
2353                            exten, context, hint, status);
2354         return 0;
2355 }
2356
2357 static char mandescr_timeout[] =
2358 "Description: Hangup a channel after a certain time.\n"
2359 "Variables: (Names marked with * are required)\n"
2360 "       *Channel: Channel name to hangup\n"
2361 "       *Timeout: Maximum duration of the call (sec)\n"
2362 "Acknowledges set time with 'Timeout Set' message\n";
2363
2364 static int action_timeout(struct mansession *s, const struct message *m)
2365 {
2366         struct ast_channel *c;
2367         const char *name = astman_get_header(m, "Channel");
2368         int timeout = atoi(astman_get_header(m, "Timeout"));
2369
2370         if (ast_strlen_zero(name)) {
2371                 astman_send_error(s, m, "No channel specified");
2372                 return 0;
2373         }
2374         if (!timeout) {
2375                 astman_send_error(s, m, "No timeout specified");
2376                 return 0;
2377         }
2378         c = ast_get_channel_by_name_locked(name);
2379         if (!c) {
2380                 astman_send_error(s, m, "No such channel");
2381                 return 0;
2382         }
2383         ast_channel_setwhentohangup(c, timeout);
2384         ast_channel_unlock(c);
2385         astman_send_ack(s, m, "Timeout Set");
2386         return 0;
2387 }
2388
2389 /*!
2390  * Send any applicable events to the client listening on this socket.
2391  * Wait only for a finite time on each event, and drop all events whether
2392  * they are successfully sent or not.
2393  */
2394 static int process_events(struct mansession *s)
2395 {
2396         int ret = 0;
2397
2398         ast_mutex_lock(&s->__lock);
2399         if (s->f != NULL) {
2400                 struct eventqent *eqe;
2401
2402                 while ( (eqe = NEW_EVENT(s)) ) {
2403                         ref_event(eqe);
2404                         if (!ret && s->authenticated &&
2405                             (s->readperm & eqe->category) == eqe->category &&
2406                             (s->send_events & eqe->category) == eqe->category) {
2407                                 if (send_string(s, eqe->eventdata) < 0)
2408                                         ret = -1;       /* don't send more */
2409                         }
2410                         s->last_ev = unref_event(s->last_ev);
2411                 }
2412         }
2413         ast_mutex_unlock(&s->__lock);
2414         return ret;
2415 }
2416
2417 static char mandescr_userevent[] =
2418 "Description: Send an event to manager sessions.\n"
2419 "Variables: (Names marked with * are required)\n"
2420 "       *UserEvent: EventStringToSend\n"
2421 "       Header1: Content1\n"
2422 "       HeaderN: ContentN\n";
2423
2424 static int action_userevent(struct mansession *s, const struct message *m)
2425 {
2426         const char *event = astman_get_header(m, "UserEvent");
2427         char body[2048] = "";
2428         int x, bodylen = 0;
2429         for (x = 0; x < m->hdrcount; x++) {
2430                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2431                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2432                         bodylen += strlen(m->headers[x]);
2433                         ast_copy_string(body + bodylen, "\r\n", 3);
2434                         bodylen += 2;
2435                 }
2436         }
2437
2438         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2439         return 0;
2440 }
2441
2442 static char mandescr_coresettings[] =
2443 "Description: Query for Core PBX settings.\n"
2444 "Variables: (Names marked with * are optional)\n"
2445 "       *ActionID: ActionID of this transaction\n";
2446
2447 /*! \brief Show PBX core settings information */
2448 static int action_coresettings(struct mansession *s, const struct message *m)
2449 {
2450         const char *actionid = astman_get_header(m, "ActionID");
2451         char idText[150];
2452
2453         if (!ast_strlen_zero(actionid))
2454                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2455         else
2456                 idText[0] = '\0';
2457
2458         astman_append(s, "Response: Success\r\n"
2459                         "%s"
2460                         "AMIversion: %s\r\n"
2461                         "AsteriskVersion: %s\r\n"
2462                         "SystemName: %s\r\n"
2463                         "CoreMaxCalls: %d\r\n"
2464                         "CoreMaxLoadAvg: %f\r\n"
2465                         "CoreRunUser: %s\r\n"
2466                         "CoreRunGroup: %s\r\n"
2467                         "CoreMaxFilehandles: %d\r\n" 
2468                         "CoreRealTimeEnabled: %s\r\n"
2469                         "CoreCDRenabled: %s\r\n"
2470                         "CoreHTTPenabled: %s\r\n"
2471                         "\r\n",
2472                         idText,
2473                         AMI_VERSION,
2474                         ast_get_version(), 
2475                         ast_config_AST_SYSTEM_NAME,
2476                         option_maxcalls,
2477                         option_maxload,
2478                         ast_config_AST_RUN_USER,
2479                         ast_config_AST_RUN_GROUP,
2480                         option_maxfiles,
2481                         ast_realtime_enabled() ? "Yes" : "No",
2482                         check_cdr_enabled() ? "Yes" : "No",
2483                         check_webmanager_enabled() ? "Yes" : "No"
2484                         );
2485         return 0;
2486 }
2487
2488 static char mandescr_corestatus[] =
2489 "Description: Query for Core PBX status.\n"
2490 "Variables: (Names marked with * are optional)\n"
2491 "       *ActionID: ActionID of this transaction\n";
2492
2493 /*! \brief Show PBX core status information */
2494 static int action_corestatus(struct mansession *s, const struct message *m)
2495 {
2496         const char *actionid = astman_get_header(m, "ActionID");
2497         char idText[150];
2498         char startuptime[150];
2499         char reloadtime[150];
2500         struct ast_tm tm;
2501
2502         if (!ast_strlen_zero(actionid))
2503                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2504         else
2505                 idText[0] = '\0';
2506
2507         ast_localtime(&ast_startuptime, &tm, NULL);
2508         ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2509         ast_localtime(&ast_lastreloadtime, &tm, NULL);
2510         ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2511
2512         astman_append(s, "Response: Success\r\n"
2513                         "%s"
2514                         "CoreStartupTime: %s\r\n"
2515                         "CoreReloadTime: %s\r\n"
2516                         "CoreCurrentCalls: %d\r\n"
2517                         "\r\n",
2518                         idText,
2519                         startuptime,
2520                         reloadtime,
2521                         ast_active_channels()
2522                         );
2523         return 0;
2524 }
2525
2526 static char mandescr_reload[] =
2527 "Description: Send a reload event.\n"
2528 "Variables: (Names marked with * are optional)\n"
2529 "       *ActionID: ActionID of this transaction\n"
2530 "       *Module: Name of the module to reload\n";
2531
2532 /*! \brief Send a reload event */
2533 static int action_reload(struct mansession *s, const struct message *m)
2534 {
2535         const char *module = astman_get_header(m, "Module");
2536         int res = ast_module_reload(S_OR(module, NULL));
2537
2538         if (res == 2)
2539                 astman_send_ack(s, m, "Module Reloaded");
2540         else
2541                 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2542         return 0;
2543 }
2544
2545 static char mandescr_coreshowchannels[] =
2546 "Description: List currently defined channels and some information\n"
2547 "             about them.\n"
2548 "Variables:\n"
2549 "          ActionID: Optional Action id for message matching.\n";
2550
2551 /*! \brief  Manager command "CoreShowChannels" - List currently defined channels 
2552  *          and some information about them. */
2553 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2554 {
2555         const char *actionid = astman_get_header(m, "ActionID");
2556         char actionidtext[256];
2557         struct ast_channel *c = NULL;
2558         int numchans = 0;
2559         int duration, durh, durm, durs;
2560
2561         if (!ast_strlen_zero(actionid))
2562                 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2563         else
2564                 actionidtext[0] = '\0';
2565
2566         astman_send_listack(s, m, "Channels will follow", "start");     
2567
2568         while ((c = ast_channel_walk_locked(c)) != NULL) {
2569                 struct ast_channel *bc = ast_bridged_channel(c);
2570                 char durbuf[10] = "";
2571
2572                 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2573                         duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2574                         durh = duration / 3600;
2575                         durm = (duration % 3600) / 60;
2576                         durs = duration % 60;
2577                         snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2578                 }
2579
2580                 astman_append(s,
2581                         "Channel: %s\r\n"
2582                         "UniqueID: %s\r\n"
2583                         "Context: %s\r\n"
2584                         "Extension: %s\r\n"
2585                         "Priority: %d\r\n"
2586                         "ChannelState: %d\r\n"
2587                         "ChannelStateDesc: %s\r\n"
2588                         "Application: %s\r\n"
2589                         "ApplicationData: %s\r\n"
2590                         "CallerIDnum: %s\r\n"
2591                         "Duration: %s\r\n"
2592                         "AccountCode: %s\r\n"
2593                         "BridgedChannel: %s\r\n"
2594                         "BridgedUniqueID: %s\r\n"
2595                         "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2596                         c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2597                         S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2598                 ast_channel_unlock(c);
2599                 numchans++;
2600         }
2601
2602         astman_append(s,
2603                 "Event: CoreShowChannelsComplete\r\n"
2604                 "EventList: Complete\r\n"
2605                 "ListItems: %d\r\n"
2606                 "%s"
2607                 "\r\n", numchans, actionidtext);
2608
2609         return 0;
2610 }
2611
2612 static char mandescr_modulecheck[] = 
2613 "Description: Checks if Asterisk module is loaded\n"
2614 "Variables: \n"
2615 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
2616 "  Module: <name>          Asterisk module name (not including extension)\n"
2617 "\n"
2618 "Will return Success/Failure\n"
2619 "For success returns, the module revision number is included.\n";
2620
2621 /* Manager function to check if module is loaded */
2622 static int manager_modulecheck(struct mansession *s, const struct message *m)
2623 {
2624         int res;
2625         const char *module = astman_get_header(m, "Module");
2626         const char *id = astman_get_header(m, "ActionID");
2627         char idText[256];
2628         const char *version;
2629         char filename[PATH_MAX];
2630         char *cut;
2631
2632         ast_copy_string(filename, module, sizeof(filename));
2633         if ((cut = strchr(filename, '.'))) {
2634                 *cut = '\0';
2635         } else {
2636                 cut = filename + strlen(filename);
2637         }
2638         snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
2639         ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2640         res = ast_module_check(filename);
2641         if (!res) {
2642                 astman_send_error(s, m, "Module not loaded");
2643                 return 0;
2644         }
2645         snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
2646         ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2647         version = ast_file_version_find(filename);
2648
2649         if (!ast_strlen_zero(id))
2650                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2651         else
2652                 idText[0] = '\0';
2653         astman_append(s, "Response: Success\r\n%s", idText);
2654         astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2655         return 0;
2656 }
2657
2658 static char mandescr_moduleload[] = 
2659 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2660 "Variables: \n"
2661 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
2662 "  Module: <name>          Asterisk module name (including .so extension)\n"
2663 "                          or subsystem identifier:\n"
2664 "                               cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2665 "  LoadType: load | unload | reload\n"
2666 "                          The operation to be done on module\n"
2667 " If no module is specified for a reload loadtype, all modules are reloaded";
2668
2669 static int manager_moduleload(struct mansession *s, const struct message *m)
2670 {
2671         int res;
2672         const char *module = astman_get_header(m, "Module");
2673         const char *loadtype = astman_get_header(m, "LoadType");
2674
2675         if (!loadtype || strlen(loadtype) == 0)
2676                 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2677         if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2678                 astman_send_error(s, m, "Need module name");
2679
2680         if (!strcasecmp(loadtype, "load")) {
2681                 res = ast_load_resource(module);
2682                 if (res)
2683                         astman_send_error(s, m, "Could not load module.");
2684                 else
2685                         astman_send_ack(s, m, "Module loaded.");
2686         } else if (!strcasecmp(loadtype, "unload")) {
2687                 res = ast_unload_resource(module, AST_FORCE_SOFT);
2688                 if (res)
2689                         astman_send_error(s, m, "Could not unload module.");
2690                 else
2691                         astman_send_ack(s, m, "Module unloaded.");
2692         } else if (!strcasecmp(loadtype, "reload")) {
2693                 if (module != NULL) {
2694                         res = ast_module_reload(module);
2695                         if (res == 0)
2696                                 astman_send_error(s, m, "No such module.");
2697                         else if (res == 1)
2698                                 astman_send_error(s, m, "Module does not support reload action.");
2699                         else
2700                                 astman_send_ack(s, m, "Module reloaded.");
2701                 } else {
2702                         ast_module_reload(NULL);        /* Reload all modules */
2703                         astman_send_ack(s, m, "All modules reloaded");
2704                 }
2705         } else 
2706                 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2707         return 0;
2708 }
2709
2710 /*
2711  * Done with the action handlers here, we start with the code in charge
2712  * of accepting connections and serving them.
2713  * accept_thread() forks a new thread for each connection, session_do(),
2714  * which in turn calls get_input() repeatedly until a full message has
2715  * been accumulated, and then invokes process_message() to pass it to
2716  * the appropriate handler.
2717  */
2718
2719 /*
2720  * Process an AMI message, performing desired action.
2721  * Return 0 on success, -1 on error that require the session to be destroyed.
2722  */
2723 static int process_message(struct mansession *s, const struct message *m)
2724 {
2725         char action[80] = "";
2726         int ret = 0;
2727         struct manager_action *tmp;
2728         const char *user = astman_get_header(m, "Username");
2729
2730         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2731         ast_debug(1, "Manager received command '%s'\n", action);
2732
2733         if (ast_strlen_zero(action)) {
2734                 ast_mutex_lock(&s->__lock);
2735                 astman_send_error(s, m, "Missing action in request");
2736                 ast_mutex_unlock(&s->__lock);
2737                 return 0;
2738         }
2739
2740         if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2741                 ast_mutex_lock(&s->__lock);
2742                 astman_send_error(s, m, "Permission denied");
2743                 ast_mutex_unlock(&s->__lock);
2744                 return 0;
2745         }
2746
2747         if (!allowmultiplelogin && !s->authenticated && user &&
2748                 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2749                 if (check_manager_session_inuse(user)) {
2750                         sleep(1);
2751                         ast_mutex_lock(&s->__lock);
2752                         astman_send_error(s, m, "Login Already In Use");
2753                         ast_mutex_unlock(&s->__lock);
2754                         return -1;
2755                 }
2756         }
2757
2758         AST_RWLIST_RDLOCK(&actions);
2759         AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2760                 if (strcasecmp(action, tmp->action))
2761                         continue;
2762                 if (s->writeperm & tmp->authority || tmp->authority == 0)
2763                         ret = tmp->func(s, m);
2764                 else
2765                         astman_send_error(s, m, "Permission denied");
2766                 break;
2767         }
2768         AST_RWLIST_UNLOCK(&actions);
2769
2770         if (!tmp) {
2771                 char buf[512];
2772                 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2773                 ast_mutex_lock(&s->__lock);
2774                 astman_send_error(s, m, buf);
2775                 ast_mutex_unlock(&s->__lock);
2776         }
2777         if (ret)
2778                 return ret;
2779         /* Once done with our message, deliver any pending events */
2780         return process_events(s);
2781 }
2782
2783 /*!
2784  * Read one full line (including crlf) from the manager socket.
2785  * \note \verbatim
2786  * \r\n is the only valid terminator for the line.
2787  * (Note that, later, '\0' will be considered as the end-of-line marker,
2788  * so everything between the '\0' and the '\r\n' will not be used).
2789  * Also note that we assume output to have at least "maxlen" space.
2790  * \endverbatim
2791  */
2792 static int get_input(struct mansession *s, char *output)
2793 {
2794         int res, x;
2795         int maxlen = sizeof(s->inbuf) - 1;
2796         char *src = s->inbuf;
2797
2798         /*
2799          * Look for \r\n within the buffer. If found, copy to the output
2800          * buffer and return, trimming the \r\n (not used afterwards).
2801          */
2802         for (x = 0; x < s->inlen; x++) {
2803                 int cr; /* set if we have \r */
2804                 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2805                         cr = 2; /* Found. Update length to include \r\n */
2806                 else if (src[x] == '\n')
2807                         cr = 1; /* also accept \n only */
2808                 else
2809                         continue;
2810                 memmove(output, src, x);        /*... but trim \r\n */
2811                 output[x] = '\0';               /* terminate the string */
2812                 x += cr;                        /* number of bytes used */
2813                 s->inlen -= x;                  /* remaining size */
2814                 memmove(src, src + x, s->inlen); /* remove used bytes */
2815                 return 1;
2816         }
2817         if (s->inlen >= maxlen) {
2818                 /* no crlf found, and buffer full - sorry, too long for us */
2819                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2820                 s->inlen = 0;
2821         }
2822         res = 0;
2823         while (res == 0) {
2824                 /* XXX do we really need this locking ? */
2825                 ast_mutex_lock(&s->__lock);
2826                 if (s->pending_event) {
2827                         s->pending_event = 0;
2828                         ast_mutex_unlock(&s->__lock);
2829                         return 0;
2830                 }
2831                 s->waiting_thread = pthread_self();
2832                 ast_mutex_unlock(&s->__lock);
2833
2834                 res = ast_wait_for_input(s->fd, -1);    /* return 0 on timeout ? */
2835
2836                 ast_mutex_lock(&s->__lock);
2837                 s->waiting_thread = AST_PTHREADT_NULL;
2838                 ast_mutex_unlock(&s->__lock);
2839         }
2840         if (res < 0) {
2841                 /* If we get a signal from some other thread (typically because
2842                  * there are new events queued), return 0 to notify the caller.
2843                  */
2844                 if (errno == EINTR)
2845                         return 0;
2846                 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2847                 return -1;
2848         }
2849         ast_mutex_lock(&s->__lock);
2850         res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2851         if (res < 1)
2852                 res = -1;       /* error return */
2853         else {
2854                 s->inlen += res;
2855                 src[s->inlen] = '\0';
2856                 res = 0;
2857         }
2858         ast_mutex_unlock(&s->__lock);
2859         return res;
2860 }
2861
2862 static int do_message(struct mansession *s)
2863 {
2864         struct message m = { 0 };
2865         char header_buf[sizeof(s->inbuf)] = { '\0' };
2866         int res;
2867
2868         for (;;) {
2869                 /* Check if any events are pending and do them if needed */
2870                 if (process_events(s))
2871                         return -1;
2872                 res = get_input(s, header_buf);
2873                 if (res == 0) {
2874                         continue;
2875                 } else if (res > 0) {
2876                         if (ast_strlen_zero(header_buf))
2877                                 return process_message(s, &m) ? -1 : 0;
2878                         else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2879                                 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2880                 } else {
2881                         return res;
2882                 }
2883         }
2884 }
2885
2886 /*! \brief The body of the individual manager session.
2887  * Call get_input() to read one line at a time
2888  * (or be woken up on new events), collect the lines in a
2889  * message until found an empty line, and execute the request.
2890  * In any case, deliver events asynchronously through process_events()
2891  * (called from here if no line is available, or at the end of
2892  * process_message(). )
2893  */
2894 static void *session_do(void *data)
2895 {
2896         struct ast_tcptls_session_instance *ser = data;
2897         struct mansession *s = ast_calloc(1, sizeof(*s));
2898         int flags;
2899         int res;
2900
2901         if (s == NULL)
2902                 goto done;
2903
2904         s->writetimeout = 100;
2905         s->waiting_thread = AST_PTHREADT_NULL;
2906
2907         flags = fcntl(ser->fd, F_GETFL);
2908         if (!block_sockets) /* make sure socket is non-blocking */
2909                 flags |= O_NONBLOCK;
2910         else
2911                 flags &= ~O_NONBLOCK;
2912         fcntl(ser->fd, F_SETFL, flags);
2913
2914         ast_mutex_init(&s->__lock);
2915         s->send_events = -1;
2916         /* these fields duplicate those in the 'ser' structure */
2917         s->fd = ser->fd;
2918         s->f = ser->f;
2919         s->sin = ser->requestor;
2920
2921         AST_LIST_LOCK(&sessions);
2922         AST_LIST_INSERT_HEAD(&sessions, s, list);
2923         ast_atomic_fetchadd_int(&num_sessions, 1);
2924         AST_LIST_UNLOCK(&sessions);
2925         /* Hook to the tail of the event queue */
2926         s->last_ev = grab_last();
2927         s->f = ser->f;
2928         astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);  /* welcome prompt */
2929         for (;;) {
2930                 if ((res = do_message(s)) < 0)
2931                         break;
2932         }
2933         /* session is over, explain why and terminate */
2934         if (s->authenticated) {
2935                         if (manager_displayconnects(s))
2936                         ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2937                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2938         } else {
2939                         if (displayconnects)
2940                         ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2941                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2942         }
2943
2944         /* It is possible under certain circumstances for this session thread
2945            to complete its work and exit *before* the thread that created it
2946            has finished executing the ast_pthread_create_background() function.
2947            If this occurs, some versions of glibc appear to act in a buggy
2948            fashion and attempt to write data into memory that it thinks belongs
2949            to the thread but is in fact not owned by the thread (or may have
2950            been freed completely).
2951
2952            Causing this thread to yield to other threads at least one time
2953            appears to work around this bug.
2954         */
2955         usleep(1);
2956
2957         destroy_session(s);
2958
2959 done:
2960         ser = ast_tcptls_session_instance_destroy(ser);
2961         return NULL;
2962 }
2963
2964 /*! \brief remove at most n_max stale session from the list. */
2965 static void purge_sessions(int n_max)
2966 {
2967         struct mansession *s;
2968         time_t now = time(NULL);
2969
2970         AST_LIST_LOCK(&sessions);
2971         AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2972                 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2973                         AST_LIST_REMOVE_CURRENT(list);
2974                         ast_atomic_fetchadd_int(&num_sessions, -1);
2975                         if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
2976                                 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2977                                         s->username, ast_inet_ntoa(s->sin.sin_addr));
2978                         }
2979                         free_session(s);        /* XXX outside ? */
2980                         if (--n_max <= 0)
2981                                 break;
2982                 }
2983         }
2984         AST_LIST_TRAVERSE_SAFE_END;
2985         AST_LIST_UNLOCK(&sessions);
2986 }
2987
2988 /*
2989  * events are appended to a queue from where they
2990  * can be dispatched to clients.
2991  */
2992 static int append_event(const char *str, int category)
2993 {
2994         struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2995         static int seq; /* sequence number */
2996
2997         if (!tmp)
2998                 return -1;
2999
3000         /* need to init all fields, because ast_malloc() does not */
3001         tmp->usecount = 0;
3002         tmp->category = category;
3003         tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
3004         AST_LIST_NEXT(tmp, eq_next) = NULL;
3005         strcpy(tmp->eventdata, str);
3006
3007         AST_LIST_LOCK(&all_events);
3008         AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3009         AST_LIST_UNLOCK(&all_events);
3010
3011         return 0;
3012 }
3013
3014 /* XXX see if can be moved inside the function */
3015 AST_THREADSTORAGE(manager_event_buf);
3016 #define MANAGER_EVENT_BUF_INITSIZE   256
3017
3018 /*! \brief  manager_event: Send AMI event to client */
3019 int __manager_event(int category, const char *event,
3020         const char *file, int line, const char *func, const char *fmt, ...)
3021 {
3022         struct mansession *s;
3023         struct manager_custom_hook *hook;
3024         struct ast_str *auth = ast_str_alloca(80);
3025         const char *cat_str;
3026         va_list ap;
3027         struct timeval now;
3028         struct ast_str *buf;
3029
3030         /* Abort if there aren't any manager sessions */
3031         if (!num_sessions)
3032                 return 0;
3033
3034         if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
3035                 return -1;
3036
3037         cat_str = authority_to_str(category, &auth);
3038         ast_str_set(&buf, 0,
3039                         "Event: %s\r\nPrivilege: %s\r\n",
3040                          event, cat_str);
3041
3042         if (timestampevents) {
3043                 now = ast_tvnow();
3044                 ast_str_append(&buf, 0,
3045                                 "Timestamp: %ld.%06lu\r\n",
3046                                  now.tv_sec, (unsigned long) now.tv_usec);
3047         }
3048         if (manager_debug) {
3049                 static int seq;
3050                 ast_str_append(&buf, 0,
3051                                 "SequenceNumber: %d\r\n",
3052                                  ast_atomic_fetchadd_int(&seq, 1));
3053                 ast_str_append(&buf, 0,
3054                                 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
3055         }
3056
3057         va_start(ap, fmt);
3058         ast_str_append_va(&buf, 0, fmt, ap);
3059         va_end(ap);
3060
3061         ast_str_append(&buf, 0, "\r\n");
3062
3063         append_event(buf->str, category);
3064
3065         /* Wake up any sleeping sessions */
3066         AST_LIST_LOCK(&sessions);
3067         AST_LIST_TRAVERSE(&sessions, s, list) {
3068                 ast_mutex_lock(&s->__lock);
3069                 if (s->waiting_thread != AST_PTHREADT_NULL)
3070                         pthread_kill(s->waiting_thread, SIGURG);
3071                 else
3072                         /* We have an event to process, but the mansession is
3073                          * not waiting for it. We still need to indicate that there
3074                          * is an event waiting so that get_input processes the pending
3075                          * event instead of polling.
3076                          */
3077                         s->pending_event = 1;
3078                 ast_mutex_unlock(&s->__lock);
3079         }
3080         AST_LIST_UNLOCK(&sessions);
3081
3082         AST_RWLIST_RDLOCK(&manager_hooks);
3083         AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
3084                 hook->helper(category, event, buf->str);
3085         }
3086         AST_RWLIST_UNLOCK(&manager_hooks);
3087
3088         return 0;
3089 }
3090
3091 /*
3092  * support functions to register/unregister AMI action handlers,
3093  */
3094 int ast_manager_unregister(char *action)
3095 {
3096         struct manager_action *cur;
3097
3098         AST_RWLIST_WRLOCK(&actions);
3099         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
3100                 if (!strcasecmp(action, cur->action)) {
3101                         AST_RWLIST_REMOVE_CURRENT(list);
3102                         ast_free(cur);
3103                         ast_verb(2, "Manager unregistered action %s\n", action);
3104                         break;
3105                 }
3106         }
3107         AST_RWLIST_TRAVERSE_SAFE_END;
3108         AST_RWLIST_UNLOCK(&actions);
3109
3110         return 0;
3111 }
3112
3113 static int manager_state_cb(char *context, char *exten, int state, void *data)
3114 {
3115         /* Notify managers of change */
3116         char hint[512];
3117         ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
3118
3119         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
3120         return 0;
3121 }
3122
3123 static int ast_manager_register_struct(struct manager_action *act)
3124 {
3125         struct manager_action *cur, *prev = NULL;
3126
3127         AST_RWLIST_WRLOCK(&actions);
3128         AST_RWLIST_TRAVERSE(&actions, cur, list) {
3129                 int ret = strcasecmp(cur->action, act->action);
3130                 if (ret == 0) {
3131                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
3132                         AST_RWLIST_UNLOCK(&actions);
3133                         return -1;
3134                 }
3135                 if (ret > 0) { /* Insert these alphabetically */
3136                         prev = cur;
3137                         break;
3138                 }
3139         }
3140         
3141         if (prev)       
3142                 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
3143         else
3144                 AST_RWLIST_INSERT_HEAD(&actions, act, list);
3145
3146         ast_verb(2, "Manager registered action %s\n", act->action);
3147
3148         AST_RWLIST_UNLOCK(&actions);
3149
3150         return 0;
3151 }
3152
3153 /*! \brief register a new command with manager, including online help. This is
3154         the preferred way to register a manager command */
3155 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
3156 {
3157         struct manager_action *cur = NULL;
3158
3159         if (!(cur = ast_calloc(1, sizeof(*cur))))
3160                 return -1;
3161
3162         cur->action = action;
3163         cur->authority = auth;
3164         cur->func = func;
3165         cur->synopsis = synopsis;
3166         cur->description = description;
3167
3168         ast_manager_register_struct(cur);
3169
3170         return 0;
3171 }
3172 /*! @}
3173  END Doxygen group */
3174
3175 /*
3176  * The following are support functions for AMI-over-http.
3177  * The common entry point is generic_http_callback(),
3178  * which extracts HTTP header and URI fields and reformats
3179  * them into AMI messages, locates a proper session
3180  * (using the mansession_id Cookie or GET variable),
3181  * and calls process_message() as for regular AMI clients.
3182  * When done, the output (which goes to a temporary file)
3183  * is read back into a buffer and reformatted as desired,
3184  * then fed back to the client over the original socket.
3185  */
3186
3187 enum output_format {
3188         FORMAT_RAW,
3189         FORMAT_HTML,
3190         FORMAT_XML,
3191 };
3192
3193 static char *contenttype[] = {
3194         [FORMAT_RAW] = "plain",
3195         [FORMAT_HTML] = "html",
3196         [FORMAT_XML] =  "xml",
3197 };
3198
3199 /*!
3200  * locate an http session in the list. The search key (ident) is
3201  * the value of the mansession_id cookie (0 is not valid and means
3202  * a session on the AMI socket).
3203  */
3204 static struct mansession *find_session(unsigned long ident)
3205 {
3206         struct mansession *s;
3207
3208         if (ident == 0)
3209                 return NULL;
3210
3211         AST_LIST_LOCK(&sessions);
3212         AST_LIST_TRAVERSE(&sessions, s, list) {
3213                 ast_mutex_lock(&s->__lock);
3214                 if (s->managerid == ident && !s->needdestroy) {
3215                         ast_atomic_fetchadd_int(&s->inuse, 1);
3216                         break;
3217                 }
3218                 ast_mutex_unlock(&s->__lock);
3219         }
3220         AST_LIST_UNLOCK(&sessions);
3221
3222         return s;
3223 }
3224
3225 int astman_verify_session_readpermissions(unsigned long ident, int perm)
3226 {
3227         int result = 0;
3228         struct mansession *s;
3229
3230         AST_LIST_LOCK(&sessions);
3231         AST_LIST_TRAVERSE(&sessions, s, list) {
3232                 ast_mutex_lock(&s->__lock);
3233                 if ((s->managerid == ident) && (s->readperm & perm)) {
3234                         result = 1;
3235                         ast_mutex_unlock(&s->__lock);
3236                         break;
3237                 }
3238                 ast_mutex_unlock(&s->__lock);
3239         }
3240         AST_LIST_UNLOCK(&sessions);
3241         return result;
3242 }
3243
3244 int astman_verify_session_writepermissions(unsigned long ident, int perm)
3245 {
3246         int result = 0;
3247         struct mansession *s;
3248
3249         AST_LIST_LOCK(&sessions);
3250         AST_LIST_TRAVERSE(&sessions, s, list) {
3251                 ast_mutex_lock(&s->__lock);
3252                 if ((s->managerid == ident) && (s->writeperm & perm)) {
3253                         result = 1;
3254                         ast_mutex_unlock(&s->__lock);
3255                         break;
3256                 }
3257                 ast_mutex_unlock(&s->__lock);
3258         }
3259         AST_LIST_UNLOCK(&sessions);
3260         return result;
3261 }
3262
3263 /*
3264  * convert to xml with various conversion:
3265  * mode & 1     -> lowercase;
3266  * mode & 2     -> replace non-alphanumeric chars with underscore
3267  */
3268 static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
3269 {
3270         /* store in a local buffer to avoid calling ast_str_append too often */
3271         char buf[256];
3272         char *dst = buf;
3273         int space = sizeof(buf);
3274         /* repeat until done and nothing to flush */
3275         for ( ; *src || dst != buf ; src++) {
3276                 if (*src == '\0' || space < 10) {       /* flush */
3277                         *dst++ = '\0';
3278                         ast_str_append(out, 0, "%s", buf);
3279                         dst = buf;
3280                         space = sizeof(buf);
3281                         if (*src == '\0')
3282                                 break;
3283                 }
3284                         
3285                 if ( (mode & 2) && !isalnum(*src)) {
3286                         *dst++ = '_';
3287                         space--;
3288                         continue;
3289                 }
3290                 switch (*src) {
3291                 case '<':
3292                         strcpy(dst, "&lt;");
3293                         dst += 4;
3294                         space -= 4;
3295                         break;
3296                 case '>':
3297                         strcpy(dst, "&gt;");
3298                         dst += 4;
3299                         space -= 4;
3300                         break;
3301                 case '\"':
3302                         strcpy(dst, "&quot;");
3303                         dst += 6;
3304                         space -= 6;
3305                         break;
3306                 case '\'':
3307                         strcpy(dst, "&apos;");
3308                         dst += 6;
3309                         space -= 6;
3310                         break;
3311                 case '&':
3312                         strcpy(dst, "&amp;");
3313                         dst += 5;
3314                         space -= 5;
3315                         break;
3316
3317                 default:
3318                         *dst++ = mode ? tolower(*src) : *src;
3319                         space--;
3320                 }
3321         }
3322 }
3323
3324 struct variable_count {
3325         char *varname;
3326         int count;
3327 };
3328
3329 static int compress_char(char c)
3330 {
3331         c &= 0x7f;
3332         if (c < 32)
3333                 return 0;
3334         else if (c >= 'a' && c <= 'z')
3335                 return c - 64;
3336         else if (c > 'z')
3337                 return '_';
3338         else
3339                 return c - 32;
3340 }
3341
3342 static int variable_count_hash_fn(const void *vvc, const int flags)
3343 {
3344         const struct variable_count *vc = vvc;
3345         int res = 0, i;
3346         for (i = 0; i < 5; i++) {
3347                 if (vc->varname[i] == '\0')
3348                         break;
3349                 res += compress_char(vc->varname[i]) << (i * 6);
3350         }
3351         return res;
3352 }
3353
3354 static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
3355 {
3356         /* Due to the simplicity of struct variable_count, it makes no difference
3357          * if you pass in objects or strings, the same operation applies. This is
3358          * due to the fact that the hash occurs on the first element, which means
3359          * the address of both the struct and the string are exactly the same. */
3360         struct variable_count *vc = obj;
3361         char *str = vstr;
3362         return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
3363 }
3364
3365 /*! \brief Convert the input into XML or HTML.
3366  * The input is supposed to be a sequence of lines of the form
3367  *      Name: value
3368  * optionally followed by a blob of unformatted text.
3369  * A blank line is a section separator. Basically, this is a
3370  * mixture of the format of Manager Interface and CLI commands.
3371  * The unformatted text is considered as a single value of a field
3372  * named 'Opaque-data'.
3373  *
3374  * At the moment the output format is the following (but it may
3375  * change depending on future requirements so don't count too
3376  * much on it when writing applications):
3377  *
3378  * General: the unformatted text is used as a value of
3379  * XML output:  to be completed
3380  * 
3381  * \verbatim
3382  *   Each section is within <response type="object" id="xxx">
3383  *   where xxx is taken from ajaxdest variable or defaults to unknown
3384  *   Each row is reported as an attribute Name="value" of an XML
3385  *   entity named from the variable ajaxobjtype, default to "generic"
3386  * \endverbatim
3387  *
3388  * HTML output:
3389  *   each Name-value pair is output as a single row of a two-column table.
3390  *   Sections (blank lines in the input) are separated by a <HR>
3391  *
3392  */
3393 static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
3394 {
3395         struct ast_variable *v;
3396         const char *dest = NULL;
3397         char *var, *val;
3398         const char *objtype = NULL;
3399         int in_data = 0;        /* parsing data */
3400         int inobj = 0;
3401         int xml = (format == FORMAT_XML);
3402         struct variable_count *vc = NULL;
3403         struct ao2_container *vco = NULL;
3404
3405         for (v = vars; v; v = v->next) {
3406                 if (!dest && !strcasecmp(v->name, "ajaxdest"))
3407                         dest = v->value;
3408                 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
3409                         objtype = v->value;
3410         }
3411         if (!dest)
3412                 dest = "unknown";
3413         if (!objtype)
3414                 objtype = "generic";
3415
3416         /* we want to stop when we find an empty line */
3417         while (in && *in) {
3418                 val = strsep(&in, "\r\n");      /* mark start and end of line */
3419                 if (in && *in == '\n')          /* remove trailing \n if any */
3420                         in++;
3421                 ast_trim_blanks(val);
3422                 ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
3423                 if (ast_strlen_zero(val)) {
3424                         if (in_data) { /* close data */
3425                                 ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3426                                 in_data = 0;
3427                         }
3428                         ast_str_append(out, 0, xml ? " /></response>\n" :
3429                                 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3430                         inobj = 0;
3431                         ao2_ref(vco, -1);
3432                         vco = NULL;
3433                         continue;
3434                 }
3435
3436                 /* we expect Name: value lines */
3437                 if (in_data) {
3438                         var = NULL;
3439                 } else {
3440                         var = strsep(&val, ":");
3441                         if (val) {      /* found the field name */
3442                                 val = ast_skip_blanks(val);
3443                                 ast_trim_blanks(var);
3444                         } else {                /* field name not found, move to opaque mode */
3445                                 val = var;
3446                                 var = "Opaque-data";
3447                         }
3448                 }
3449
3450                 if (!inobj) {
3451                         if (xml)
3452                                 ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
3453                         else
3454                                 ast_str_append(out, 0, "<body>\n");
3455                         vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
3456                         inobj = 1;
3457                 }
3458
3459                 if (!in_data) { /* build appropriate line start */
3460                         ast_str_append(out, 0, xml ? " " : "<tr><td>");
3461                         if ((vc = ao2_find(vco, var, 0)))
3462                                 vc->count++;
3463                         else {
3464                                 /* Create a new entry for this one */
3465                                 vc = ao2_alloc(sizeof(*vc), NULL);
3466                                 vc->varname = var;
3467                                 vc->count = 1;
3468                                 ao2_link(vco, vc);
3469                         }
3470                         xml_copy_escape(out, var, xml ? 1 | 2 : 0);
3471                         if (vc->count > 1)
3472                                 ast_str_append(out, 0, "-%d", vc->count);
3473                         ao2_ref(vc, -1);
3474                         ast_str_append(out, 0, xml ? "='" : "</td><td>");
3475                         if (!strcmp(var, "Opaque-data"))
3476                                 in_data = 1;
3477                 }
3478                 xml_copy_escape(out, val, 0);   /* data field */
3479                 if (!in_data)
3480                         ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
3481                 else
3482                         ast_str_append(out, 0, xml ? "\n" : "<br>\n");
3483         }
3484         if (inobj) {
3485                 ast_str_append(out, 0, xml ? " /></response>\n" :
3486                         "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
3487                 ao2_ref(vco, -1);
3488         }
3489 }
3490
3491 static struct ast_str *generic_http_callback(enum output_format format,
3492                                              struct sockaddr_in *requestor, const char *uri, enum ast_http_method method,
3493                                              struct ast_variable *params, int *status,
3494                                              char **title, int *contentlength)
3495 {
3496         struct mansession *s = NULL;
3497         unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
3498         int blastaway = 0;
3499         struct ast_variable *v;
3500         char template[] = "/tmp/ast-http-XXXXXX";       /* template for temporary file */
3501         struct ast_str *out = NULL;
3502         struct message m = { 0 };
3503         unsigned int x;
3504         size_t hdrlen;
3505
3506         for (v = params; v; v = v->next) {
3507                 if (!strcasecmp(v->name, "mansession_id")) {
3508                         sscanf(v->value, "%lx", &ident);
3509                         break;
3510                 }
3511         }
3512
3513         if (!(s = find_session(ident))) {
3514                 /* Create new session.
3515                  * While it is not in the list we don't need any locking
3516                  */
3517                 if (!(s = ast_calloc(1, sizeof(*s)))) {
3518                         *status = 500;
3519                         goto generic_callback_out;
3520                 }
3521                 s->sin = *requestor;
3522                 s->fd = -1;
3523                 s->waiting_thread = AST_PTHREADT_NULL;
3524                 s->send_events = 0;
3525                 ast_mutex_init(&s->__lock);
3526                 ast_mutex_lock(&s->__lock);
3527                 s->inuse = 1;
3528                 /*!\note There is approximately a 1 in 1.8E19 chance that the following
3529                  * calculation will produce 0, which is an invalid ID, but due to the
3530                  * properties of the rand() function (and the constantcy of s), that
3531                  * won't happen twice in a row.
3532                  */
3533                 while ((s->managerid = rand() ^ (unsigned long) s) == 0);
3534                 s->last_ev = grab_last();
3535                 AST_LIST_LOCK(&sessions);
3536                 AST_LIST_INSERT_HEAD(&sessions, s, list);
3537                 ast_atomic_fetchadd_int(&num_sessions, 1);
3538                 AST_LIST_UNLOCK(&sessions);
3539         }
3540
3541         ast_mutex_unlock(&s->__lock);
3542
3543         if (!(out = ast_str_create(1024))) {
3544                 *status = 500;
3545                 goto generic_callback_out;
3546         }
3547
3548         s->fd = mkstemp(template);      /* create a temporary file for command output */
3549         unlink(template);
3550         s->f = fdopen(s->fd, "w+");
3551
3552         for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
3553                 hdrlen = strlen(v->name) + strlen(v->value) + 3;
3554                 m.headers[m.hdrcount] = alloca(hdrlen);
3555                 snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
3556                 m.hdrcount = x + 1;
3557         }
3558
3559         if (process_message(s, &m)) {
3560                 if (s->authenticated) {
3561                                 if (manager_displayconnects(s))
3562                                 ast_verb(2, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3563                         ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3564                 } else {
3565                                 if (displayconnects)
3566                                 ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3567                         ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3568                 }
3569                 s->needdestroy = 1;
3570         }
3571
3572         ast_str_append(&out, 0,
3573                        "Content-type: text/%s\r\n"
3574                        "Cache-Control: no-cache;\r\n"
3575                        "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
3576                        "\r\n",
3577                         contenttype[format],
3578                         s->managerid, httptimeout);
3579
3580         if (format == FORMAT_XML) {
3581                 ast_str_append(&out, 0, "<ajax-response>\n");
3582         } else if (format == FORMAT_HTML) {
3583
3584 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
3585 #define TEST_STRING \
3586         "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
3587         user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
3588         <input type=\"submit\"></form>"
3589
3590                 ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
3591                 ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
3592                 ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
3593                 ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
3594         }
3595
3596         if (s->f != NULL) {     /* have temporary output */
3597                 char *buf;
3598                 size_t l = ftell(s->f);
3599                 
3600                 if (l) {
3601                         if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
3602                                 if (format == FORMAT_XML || format == FORMAT_HTML)
3603                                         xml_translate(&out, buf, params, format);
3604                                 else
3605                                         ast_str_append(&out, 0, "%s", buf);
3606                                 munmap(buf, l);
3607                         }
3608                 } else if (format == FORMAT_XML || format == FORMAT_HTML) {
3609                         xml_translate(&out, "", params, format);
3610                 }
3611                 fclose(s->f);
3612                 s->f = NULL;
3613                 s->fd = -1;
3614         }
3615
3616         if (format == FORMAT_XML) {
3617                 ast_str_append(&out, 0, "</ajax-response>\n");
3618         } else if (format == FORMAT_HTML)
3619                 ast_str_append(&out, 0, "</table></body>\r\n");
3620
3621         ast_mutex_lock(&s->__lock);
3622         /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
3623         s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
3624
3625         if (s->needdestroy) {
3626                 if (s->inuse == 1) {
3627                         ast_debug(1, "Need destroy, doing it now!\n");
3628                         blastaway = 1;
3629                 } else {
3630                         ast_debug(1, "Need destroy, but can't do it yet!\n");
3631                         if (s->waiting_thread != AST_PTHREADT_NULL)
3632                                 pthread_kill(s->waiting_thread, SIGURG);
3633                         s->inuse--;
3634                 }
3635         } else
3636                 s->inuse--;
3637         ast_mutex_unlock(&s->__lock);
3638
3639         if (blastaway)
3640                 destroy_session(s);
3641 generic_callback_out:
3642         if (*status != 200)
3643                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
3644         return out;
3645 }
3646
3647 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)
3648 {
3649         return generic_http_callback(FORMAT_HTML, &ser->requestor, uri, method, params, status, title, contentlength);
3650 }
3651
3652 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)
3653 {
3654         return generic_http_callback(FORMAT_XML, &ser->requestor, uri, method, params, status, title, contentlength);
3655 }
3656
3657 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)
3658 {
3659         return generic_http_callback(FORMAT_RAW, &ser->requestor, uri, method, params, status, title, contentlength);
3660 }
3661
3662 struct ast_http_uri rawmanuri = {
3663         .description = "Raw HTTP Manager Event Interface",
3664         .uri = "rawman",
3665         .callback = rawman_http_callback,
3666         .supports_get = 1,
3667         .data = NULL,
3668         .key = __FILE__,
3669 };
3670
3671 struct ast_http_uri manageruri = {
3672         .description = "HTML Manager Event Interface",
3673         .uri = "manager",
3674         .callback = manager_http_callback,
3675         .supports_get = 1,
3676         .data = NULL,
3677         .key = __FILE__,
3678 };
3679
3680 struct ast_http_uri managerxmluri = {
3681         .description = "XML Manager Event Interface",
3682         .uri = "mxml",
3683         .callback = mxml_http_callback,
3684         .supports_get = 1,
3685         .data = NULL,
3686         .key = __FILE__,
3687 };
3688
3689 static int registered = 0;
3690 static int webregged = 0;
3691
3692 /*! \brief cleanup code called at each iteration of server_root,
3693  * guaranteed to happen every 5 seconds at most
3694  */
3695 static void purge_old_stuff(void *data)
3696 {
3697         purge_sessions(1);
3698         purge_events();
3699 }
3700
3701 struct ast_tls_config ami_tls_cfg;
3702 static struct server_args ami_desc = {
3703         .accept_fd = -1,
3704         .master = AST_PTHREADT_NULL,
3705         .tls_cfg = NULL, 
3706         .poll_timeout = 5000,   /* wake up every 5 seconds */
3707         .periodic_fn = purge_old_stuff,
3708         .name = "AMI server",
3709         .accept_fn = ast_tcptls_server_root,    /* thread doing the accept() */
3710         .worker_fn = session_do,        /* thread handling the session */
3711 };
3712
3713 static struct server_args amis_desc = {
3714         .accept_fd = -1,
3715         .master = AST_PTHREADT_NULL,
3716         .tls_cfg = &ami_tls_cfg, 
3717         .poll_timeout = -1,     /* the other does the periodic cleanup */
3718         .name = "AMI TLS server",
3719         .accept_fn = ast_tcptls_server_root,    /* thread doing the accept() */
3720         .worker_fn = session_do,        /* thread handling the session */
3721 };
3722
3723 static int __init_manager(int reload)
3724 {
3725         struct ast_config *ucfg = NULL, *cfg = NULL;
3726         const char *val;
3727         char *cat = NULL;
3728         int newhttptimeout = 60;
3729         int have_sslbindaddr = 0;
3730         struct hostent *hp;
3731         struct ast_hostent ahp;
3732         struct ast_manager_user *user = NULL;
3733         struct ast_variable *var;
3734         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
3735
3736         manager_enabled = 0;
3737
3738         if (!registered) {
3739                 /* Register default actions */
3740                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
3741                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
3742                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
3743                 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
3744                 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
3745                 ast_manager_register2("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
3746                 ast_manager_register("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status, "Lists channel status" );