Merged revisions 114051 via svnmerge from
[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 #if !defined(LOW_MEMORY)
2629         const char *version;
2630 #endif
2631         char filename[PATH_MAX];
2632         char *cut;
2633
2634         ast_copy_string(filename, module, sizeof(filename));
2635         if ((cut = strchr(filename, '.'))) {
2636                 *cut = '\0';
2637         } else {
2638                 cut = filename + strlen(filename);
2639         }
2640         snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
2641         ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2642         res = ast_module_check(filename);
2643         if (!res) {
2644                 astman_send_error(s, m, "Module not loaded");
2645                 return 0;
2646         }
2647         snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
2648         ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2649 #if !defined(LOW_MEMORY)
2650         version = ast_file_version_find(filename);
2651 #endif
2652
2653         if (!ast_strlen_zero(id))
2654                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2655         else
2656                 idText[0] = '\0';
2657         astman_append(s, "Response: Success\r\n%s", idText);
2658 #if !defined(LOW_MEMORY)
2659         astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2660 #endif
2661         return 0;
2662 }
2663
2664 static char mandescr_moduleload[] = 
2665 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2666 "Variables: \n"
2667 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
2668 "  Module: <name>          Asterisk module name (including .so extension)\n"
2669 "                          or subsystem identifier:\n"
2670 "                               cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2671 "  LoadType: load | unload | reload\n"
2672 "                          The operation to be done on module\n"
2673 " If no module is specified for a reload loadtype, all modules are reloaded";
2674
2675 static int manager_moduleload(struct mansession *s, const struct message *m)
2676 {
2677         int res;
2678         const char *module = astman_get_header(m, "Module");
2679         const char *loadtype = astman_get_header(m, "LoadType");
2680
2681         if (!loadtype || strlen(loadtype) == 0)
2682                 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2683         if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2684                 astman_send_error(s, m, "Need module name");
2685
2686         if (!strcasecmp(loadtype, "load")) {
2687                 res = ast_load_resource(module);
2688                 if (res)
2689                         astman_send_error(s, m, "Could not load module.");
2690                 else
2691                         astman_send_ack(s, m, "Module loaded.");
2692         } else if (!strcasecmp(loadtype, "unload")) {
2693                 res = ast_unload_resource(module, AST_FORCE_SOFT);
2694                 if (res)
2695                         astman_send_error(s, m, "Could not unload module.");
2696                 else
2697                         astman_send_ack(s, m, "Module unloaded.");
2698         } else if (!strcasecmp(loadtype, "reload")) {
2699                 if (module != NULL) {
2700                         res = ast_module_reload(module);
2701                         if (res == 0)
2702                                 astman_send_error(s, m, "No such module.");
2703                         else if (res == 1)
2704                                 astman_send_error(s, m, "Module does not support reload action.");
2705                         else
2706                                 astman_send_ack(s, m, "Module reloaded.");
2707                 } else {
2708                         ast_module_reload(NULL);        /* Reload all modules */
2709                         astman_send_ack(s, m, "All modules reloaded");
2710                 }
2711         } else 
2712                 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2713         return 0;
2714 }
2715
2716 /*
2717  * Done with the action handlers here, we start with the code in charge
2718  * of accepting connections and serving them.
2719  * accept_thread() forks a new thread for each connection, session_do(),
2720  * which in turn calls get_input() repeatedly until a full message has
2721  * been accumulated, and then invokes process_message() to pass it to
2722  * the appropriate handler.
2723  */
2724
2725 /*
2726  * Process an AMI message, performing desired action.
2727  * Return 0 on success, -1 on error that require the session to be destroyed.
2728  */
2729 static int process_message(struct mansession *s, const struct message *m)
2730 {
2731         char action[80] = "";
2732         int ret = 0;
2733         struct manager_action *tmp;
2734         const char *user = astman_get_header(m, "Username");
2735
2736         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
2737         ast_debug(1, "Manager received command '%s'\n", action);
2738
2739         if (ast_strlen_zero(action)) {
2740                 ast_mutex_lock(&s->__lock);
2741                 astman_send_error(s, m, "Missing action in request");
2742                 ast_mutex_unlock(&s->__lock);
2743                 return 0;
2744         }
2745
2746         if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2747                 ast_mutex_lock(&s->__lock);
2748                 astman_send_error(s, m, "Permission denied");
2749                 ast_mutex_unlock(&s->__lock);
2750                 return 0;
2751         }
2752
2753         if (!allowmultiplelogin && !s->authenticated && user &&
2754                 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2755                 if (check_manager_session_inuse(user)) {
2756                         sleep(1);
2757                         ast_mutex_lock(&s->__lock);
2758                         astman_send_error(s, m, "Login Already In Use");
2759                         ast_mutex_unlock(&s->__lock);
2760                         return -1;
2761                 }
2762         }
2763
2764         AST_RWLIST_RDLOCK(&actions);
2765         AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2766                 if (strcasecmp(action, tmp->action))
2767                         continue;
2768                 if (s->writeperm & tmp->authority || tmp->authority == 0)
2769                         ret = tmp->func(s, m);
2770                 else
2771                         astman_send_error(s, m, "Permission denied");
2772                 break;
2773         }
2774         AST_RWLIST_UNLOCK(&actions);
2775
2776         if (!tmp) {
2777                 char buf[512];
2778                 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2779                 ast_mutex_lock(&s->__lock);
2780                 astman_send_error(s, m, buf);
2781                 ast_mutex_unlock(&s->__lock);
2782         }
2783         if (ret)
2784                 return ret;
2785         /* Once done with our message, deliver any pending events */
2786         return process_events(s);
2787 }
2788
2789 /*!
2790  * Read one full line (including crlf) from the manager socket.
2791  * \note \verbatim
2792  * \r\n is the only valid terminator for the line.
2793  * (Note that, later, '\0' will be considered as the end-of-line marker,
2794  * so everything between the '\0' and the '\r\n' will not be used).
2795  * Also note that we assume output to have at least "maxlen" space.
2796  * \endverbatim
2797  */
2798 static int get_input(struct mansession *s, char *output)
2799 {
2800         int res, x;
2801         int maxlen = sizeof(s->inbuf) - 1;
2802         char *src = s->inbuf;
2803
2804         /*
2805          * Look for \r\n within the buffer. If found, copy to the output
2806          * buffer and return, trimming the \r\n (not used afterwards).
2807          */
2808         for (x = 0; x < s->inlen; x++) {
2809                 int cr; /* set if we have \r */
2810                 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2811                         cr = 2; /* Found. Update length to include \r\n */
2812                 else if (src[x] == '\n')
2813                         cr = 1; /* also accept \n only */
2814                 else
2815                         continue;
2816                 memmove(output, src, x);        /*... but trim \r\n */
2817                 output[x] = '\0';               /* terminate the string */
2818                 x += cr;                        /* number of bytes used */
2819                 s->inlen -= x;                  /* remaining size */
2820                 memmove(src, src + x, s->inlen); /* remove used bytes */
2821                 return 1;
2822         }
2823         if (s->inlen >= maxlen) {
2824                 /* no crlf found, and buffer full - sorry, too long for us */
2825                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
2826                 s->inlen = 0;
2827         }
2828         res = 0;
2829         while (res == 0) {
2830                 /* XXX do we really need this locking ? */
2831                 ast_mutex_lock(&s->__lock);
2832                 if (s->pending_event) {
2833                         s->pending_event = 0;
2834                         ast_mutex_unlock(&s->__lock);
2835                         return 0;
2836                 }
2837                 s->waiting_thread = pthread_self();
2838                 ast_mutex_unlock(&s->__lock);
2839
2840                 res = ast_wait_for_input(s->fd, -1);    /* return 0 on timeout ? */
2841
2842                 ast_mutex_lock(&s->__lock);
2843                 s->waiting_thread = AST_PTHREADT_NULL;
2844                 ast_mutex_unlock(&s->__lock);
2845         }
2846         if (res < 0) {
2847                 /* If we get a signal from some other thread (typically because
2848                  * there are new events queued), return 0 to notify the caller.
2849                  */
2850                 if (errno == EINTR)
2851                         return 0;
2852                 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2853                 return -1;
2854         }
2855         ast_mutex_lock(&s->__lock);
2856         res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
2857         if (res < 1)
2858                 res = -1;       /* error return */
2859         else {
2860                 s->inlen += res;
2861                 src[s->inlen] = '\0';
2862                 res = 0;
2863         }
2864         ast_mutex_unlock(&s->__lock);
2865         return res;
2866 }
2867
2868 static int do_message(struct mansession *s)
2869 {
2870         struct message m = { 0 };
2871         char header_buf[sizeof(s->inbuf)] = { '\0' };
2872         int res;
2873
2874         for (;;) {
2875                 /* Check if any events are pending and do them if needed */
2876                 if (process_events(s))
2877                         return -1;
2878                 res = get_input(s, header_buf);
2879                 if (res == 0) {
2880                         continue;
2881                 } else if (res > 0) {
2882                         if (ast_strlen_zero(header_buf))
2883                                 return process_message(s, &m) ? -1 : 0;
2884                         else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
2885                                 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
2886                 } else {
2887                         return res;
2888                 }
2889         }
2890 }
2891
2892 /*! \brief The body of the individual manager session.
2893  * Call get_input() to read one line at a time
2894  * (or be woken up on new events), collect the lines in a
2895  * message until found an empty line, and execute the request.
2896  * In any case, deliver events asynchronously through process_events()
2897  * (called from here if no line is available, or at the end of
2898  * process_message(). )
2899  */
2900 static void *session_do(void *data)
2901 {
2902         struct ast_tcptls_session_instance *ser = data;
2903         struct mansession *s = ast_calloc(1, sizeof(*s));
2904         int flags;
2905         int res;
2906
2907         if (s == NULL)
2908                 goto done;
2909
2910         s->writetimeout = 100;
2911         s->waiting_thread = AST_PTHREADT_NULL;
2912
2913         flags = fcntl(ser->fd, F_GETFL);
2914         if (!block_sockets) /* make sure socket is non-blocking */
2915                 flags |= O_NONBLOCK;
2916         else
2917                 flags &= ~O_NONBLOCK;
2918         fcntl(ser->fd, F_SETFL, flags);
2919
2920         ast_mutex_init(&s->__lock);
2921         s->send_events = -1;
2922         /* these fields duplicate those in the 'ser' structure */
2923         s->fd = ser->fd;
2924         s->f = ser->f;
2925         s->sin = ser->requestor;
2926
2927         AST_LIST_LOCK(&sessions);
2928         AST_LIST_INSERT_HEAD(&sessions, s, list);
2929         ast_atomic_fetchadd_int(&num_sessions, 1);
2930         AST_LIST_UNLOCK(&sessions);
2931         /* Hook to the tail of the event queue */
2932         s->last_ev = grab_last();
2933         s->f = ser->f;
2934         astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);  /* welcome prompt */
2935         for (;;) {
2936                 if ((res = do_message(s)) < 0)
2937                         break;
2938         }
2939         /* session is over, explain why and terminate */
2940         if (s->authenticated) {
2941                         if (manager_displayconnects(s))
2942                         ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2943                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2944         } else {
2945                         if (displayconnects)
2946                         ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2947                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2948         }
2949
2950         /* It is possible under certain circumstances for this session thread
2951            to complete its work and exit *before* the thread that created it
2952            has finished executing the ast_pthread_create_background() function.
2953            If this occurs, some versions of glibc appear to act in a buggy
2954            fashion and attempt to write data into memory that it thinks belongs
2955            to the thread but is in fact not owned by the thread (or may have
2956            been freed completely).
2957
2958            Causing this thread to yield to other threads at least one time
2959            appears to work around this bug.
2960         */
2961         usleep(1);
2962
2963         destroy_session(s);
2964
2965 done:
2966         ser = ast_tcptls_session_instance_destroy(ser);
2967         return NULL;
2968 }
2969
2970 /*! \brief remove at most n_max stale session from the list. */
2971 static void purge_sessions(int n_max)
2972 {
2973         struct mansession *s;
2974         time_t now = time(NULL);
2975
2976         AST_LIST_LOCK(&sessions);
2977         AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2978                 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2979                         AST_LIST_REMOVE_CURRENT(list);
2980                         ast_atomic_fetchadd_int(&num_sessions, -1);
2981                         if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
2982                                 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
2983                                         s->username, ast_inet_ntoa(s->sin.sin_addr));
2984                         }
2985                         free_session(s);        /* XXX outside ? */
2986                         if (--n_max <= 0)
2987                                 break;
2988                 }
2989         }
2990         AST_LIST_TRAVERSE_SAFE_END;
2991         AST_LIST_UNLOCK(&sessions);
2992 }
2993
2994 /*
2995  * events are appended to a queue from where they
2996  * can be dispatched to clients.
2997  */
2998 static int append_event(const char *str, int category)
2999 {
3000         struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
3001         static int seq; /* sequence number */
3002
3003         if (!tmp)
3004                 return -1;
3005
3006         /* need to init all fields, because ast_malloc() does not */
3007         tmp->usecount = 0;
3008         tmp->category = category;
3009         tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
3010         AST_LIST_NEXT(tmp, eq_next) = NULL;
3011         strcpy(tmp->eventdata, str);
3012
3013         AST_LIST_LOCK(&all_events);
3014         AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3015         AST_LIST_UNLOCK(&all_events);
3016
3017         return 0;
3018 }
3019
3020 /* XXX see if can be moved inside the function */
3021 AST_THREADSTORAGE(manager_event_buf);
3022 #define MANAGER_EVENT_BUF_INITSIZE   256
3023
3024 /*! \brief  manager_event: Send AMI event to client */
3025 int __manager_event(int category, const char *event,
3026         const char *file, int line, const char *func, const char *fmt, ...)
3027 {
3028         struct mansession *s;
3029         struct manager_custom_hook *hook;
3030         struct ast_str *auth = ast_str_alloca(80);
3031         const char *cat_str;
3032         va_list ap;
3033         struct timeval now;
3034         struct ast_str *buf;
3035
3036         /* Abort if there aren't any manager sessions */
3037         if (!num_sessions)
3038                 return 0;
3039
3040         if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
3041                 return -1;
3042
3043         cat_str = authority_to_str(category, &auth);
3044         ast_str_set(&buf, 0,
3045                         "Event: %s\r\nPrivilege: %s\r\n",
3046                          event, cat_str);
3047
3048         if (timestampevents) {
3049                 now = ast_tvnow();
3050                 ast_str_append(&buf, 0,
3051                                 "Timestamp: %ld.%06lu\r\n",
3052                                  now.tv_sec, (unsigned long) now.tv_usec);
3053         }
3054         if (manager_debug) {
3055                 static int seq;
3056                 ast_str_append(&buf, 0,
3057                                 "SequenceNumber: %d\r\n",
3058                                  ast_atomic_fetchadd_int(&seq, 1));
3059                 ast_str_append(&buf, 0,
3060                                 "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
3061         }
3062
3063         va_start(ap, fmt);
3064         ast_str_append_va(&buf, 0, fmt, ap);
3065         va_end(ap);
3066
3067         ast_str_append(&buf, 0, "\r\n");
3068
3069         append_event(buf->str, category);
3070
3071         /* Wake up any sleeping sessions */
3072         AST_LIST_LOCK(&sessions);
3073         AST_LIST_TRAVERSE(&sessions, s, list) {
3074                 ast_mutex_lock(&s->__lock);
3075                 if (s->waiting_thread != AST_PTHREADT_NULL)
3076                         pthread_kill(s->waiting_thread, SIGURG);
3077                 else
3078                         /* We have an event to process, but the mansession is
3079                          * not waiting for it. We still need to indicate that there
3080                          * is an event waiting so that get_input processes the pending
3081                          * event instead of polling.
3082                          */
3083                         s->pending_event = 1;
3084                 ast_mutex_unlock(&s->__lock);
3085         }
3086         AST_LIST_UNLOCK(&sessions);
3087
3088         AST_RWLIST_RDLOCK(&manager_hooks);
3089         AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
3090                 hook->helper(category, event, buf->str);
3091         }
3092         AST_RWLIST_UNLOCK(&manager_hooks);
3093
3094         return 0;
3095 }
3096
3097 /*
3098  * support functions to register/unregister AMI action handlers,
3099  */
3100 int ast_manager_unregister(char *action)
3101 {
3102         struct manager_action *cur;
3103
3104         AST_RWLIST_WRLOCK(&actions);
3105         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
3106                 if (!strcasecmp(action, cur->action)) {
3107                         AST_RWLIST_REMOVE_CURRENT(list);
3108                         ast_free(cur);
3109                         ast_verb(2, "Manager unregistered action %s\n", action);
3110                         break;
3111                 }
3112         }
3113         AST_RWLIST_TRAVERSE_SAFE_END;
3114         AST_RWLIST_UNLOCK(&actions);
3115
3116         return 0;
3117 }
3118
3119 static int manager_state_cb(char *context, char *exten, int state, void *data)
3120 {
3121         /* Notify managers of change */
3122         char hint[512];
3123         ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
3124
3125         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
3126         return 0;
3127 }
3128
3129 static int ast_manager_register_struct(struct manager_action *act)
3130 {
3131         struct manager_action *cur, *prev = NULL;
3132
3133         AST_RWLIST_WRLOCK(&actions);
3134         AST_RWLIST_TRAVERSE(&actions, cur, list) {
3135                 int ret = strcasecmp(cur->action, act->action);
3136                 if (ret == 0) {
3137                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
3138                         AST_RWLIST_UNLOCK(&actions);
3139                         return -1;
3140                 }
3141                 if (ret > 0) { /* Insert these alphabetically */
3142                         prev = cur;
3143                         break;
3144                 }
3145         }
3146         
3147         if (prev)       
3148                 AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
3149         else
3150                 AST_RWLIST_INSERT_HEAD(&actions, act, list);
3151
3152         ast_verb(2, "Manager registered action %s\n", act->action);
3153
3154         AST_RWLIST_UNLOCK(&actions);
3155
3156         return 0;
3157 }
3158
3159 /*! \brief register a new command with manager, including online help. This is
3160         the preferred way to register a manager command */
3161 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
3162 {
3163         struct manager_action *cur = NULL;
3164
3165         if (!(cur = ast_calloc(1, sizeof(*cur))))
3166                 return -1;
3167
3168         cur->action = action;
3169         cur->authority = auth;
3170         cur->func = func;
3171         cur->synopsis = synopsis;
3172         cur->description = description;
3173
3174         ast_manager_register_struct(cur);
3175
3176         return 0;
3177 }
3178 /*! @}
3179  END Doxygen group */
3180
3181 /*
3182  * The following are support functions for AMI-over-http.
3183  * The common entry point is generic_http_callback(),
3184  * which extracts HTTP header and URI fields and reformats
3185  * them into AMI messages, locates a proper session
3186  * (using the mansession_id Cookie or GET variable),
3187  * and calls process_message() as for regular AMI clients.
3188  * When done, the output (which goes to a temporary file)
3189  * is read back into a buffer and reformatted as desired,
3190  * then fed back to the client over the original socket.
3191  */
3192
3193 enum output_format {
3194         FORMAT_RAW,
3195         FORMAT_HTML,
3196         FORMAT_XML,
3197 };
3198
3199 static char *contenttype[] = {
3200         [FORMAT_RAW] = "plain",
3201         [FORMAT_HTML] = "html",
3202         [FORMAT_XML] =  "xml",
3203 };
3204
3205 /*!
3206  * locate an http session in the list. The search key (ident) is
3207  * the value of the mansession_id cookie (0 is not valid and means
3208  * a session on the AMI socket).
3209  */
3210 static struct mansession *find_session(unsigned long ident)
3211 {
3212         struct mansession *s;
3213
3214         if (ident == 0)
3215                 return NULL;
3216
3217         AST_LIST_LOCK(&sessions);
3218         AST_LIST_TRAVERSE(&sessions, s, list) {
3219                 ast_mutex_lock(&s->__lock);
3220                 if (s->managerid == ident && !s->needdestroy) {
3221                         ast_atomic_fetchadd_int(&s->inuse, 1);
3222                         break;
3223                 }
3224                 ast_mutex_unlock(&s->__lock);
3225         }
3226         AST_LIST_UNLOCK(&sessions);
3227
3228         return s;
3229 }
3230
3231 int astman_verify_session_readpermissions(unsigned long ident, int perm)
3232 {
3233         int result = 0;
3234         struct mansession *s;
3235
3236         AST_LIST_LOCK(&sessions);
3237         AST_LIST_TRAVERSE(&sessions, s, list) {
3238                 ast_mutex_lock(&s->__lock);
3239                 if ((s->managerid == ident) && (s->readperm & perm)) {
3240                         result = 1;
3241                         ast_mutex_unlock(&s->__lock);
3242                         break;
3243                 }
3244                 ast_mutex_unlock(&s->__lock);
3245         }
3246         AST_LIST_UNLOCK(&sessions);
3247         return result;
3248 }
3249
3250 int astman_verify_session_writepermissions(unsigned long ident, int perm)
3251 {
3252         int result = 0;
3253         struct mansession *s;
3254
3255         AST_LIST_LOCK(&sessions);
3256         AST_LIST_TRAVERSE(&sessions, s, list) {
3257                 ast_mutex_lock(&s->__lock);
3258                 if ((s->managerid == ident) && (s->writeperm & perm)) {
3259                         result = 1;
3260                         ast_mutex_unlock(&s->__lock);