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