Fixes problem where manager users loaded from users.conf would be
[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, *match, *line, *tmp;
1274         char *var, *value;
1275         struct ast_category *category;
1276         struct ast_variable *v;
1277         struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
1278         enum error_type result = 0;
1279
1280         for (x = 0; x < 100000; x++) {
1281                 unsigned int object = 0;
1282
1283                 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
1284                 action = astman_get_header(m, hdr);
1285                 if (ast_strlen_zero(action))
1286                         break;
1287                 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
1288                 cat = astman_get_header(m, hdr);
1289                 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
1290                 if ((tmp = astman_get_header(m, hdr))) {
1291                         ast_str_make_space(&str1, strlen(tmp) + 1);
1292                         strcpy(str1->str, tmp);
1293                         var = str1->str;
1294                         ast_uri_decode(var);
1295                 } else {
1296                         var = "";
1297                 }
1298                 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
1299                 if ((tmp = astman_get_header(m, hdr))) {
1300                         ast_str_make_space(&str2, strlen(tmp) + 1);
1301                         strcpy(str2->str, tmp);
1302                         value = str2->str;
1303                         ast_uri_decode(value);
1304                 } else {
1305                         value = "";
1306                 }
1307                 if (!ast_strlen_zero(value) && *value == '>') {
1308                         object = 1;
1309                         value++;
1310                 }
1311                 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
1312                 match = astman_get_header(m, hdr);
1313                 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
1314                 line = astman_get_header(m, hdr);
1315                 if (!strcasecmp(action, "newcat")) {
1316                         if (ast_strlen_zero(cat)) {
1317                                 result = UNSPECIFIED_CATEGORY;
1318                                 break;
1319                         }
1320                         if (!(category = ast_category_new(cat, dfn, -1))) {
1321                                 result = FAILURE_ALLOCATION;
1322                                 break;
1323                         }
1324                         if (ast_strlen_zero(match)) {
1325                                 ast_category_append(cfg, category);
1326                         } else
1327                                 ast_category_insert(cfg, category, match);
1328                 } else if (!strcasecmp(action, "renamecat")) {
1329                         if (ast_strlen_zero(cat) || ast_strlen_zero(value)) {
1330                                 result = UNSPECIFIED_ARGUMENT;
1331                                 break;
1332                         }
1333                         if (!(category = ast_category_get(cfg, cat))) {
1334                                 result = UNKNOWN_CATEGORY;
1335                                 break;
1336                         }
1337                         ast_category_rename(category, value);
1338                 } else if (!strcasecmp(action, "delcat")) {
1339                         if (ast_strlen_zero(cat)) {
1340                                 result = UNSPECIFIED_CATEGORY;
1341                                 break;
1342                         }
1343                         if (ast_category_delete(cfg, cat)) {
1344                                 result = FAILURE_DELCAT;
1345                                 break;
1346                         }
1347                 } else if (!strcasecmp(action, "emptycat")) {
1348                         if (ast_strlen_zero(cat)) {
1349                                 result = UNSPECIFIED_CATEGORY;
1350                                 break;
1351                         }
1352                         if (ast_category_empty(cfg, cat)) {
1353                                 result = FAILURE_EMPTYCAT;
1354                                 break;
1355                         }
1356                 } else if (!strcasecmp(action, "update")) {
1357                         if (ast_strlen_zero(cat) || ast_strlen_zero(var)) {
1358                                 result = UNSPECIFIED_ARGUMENT;
1359                                 break;
1360                         }
1361                         if (!(category = ast_category_get(cfg,cat))) {
1362                                 result = UNKNOWN_CATEGORY;
1363                                 break;
1364                         }
1365                         if (ast_variable_update(category, var, value, match, object)) {
1366                                 result = FAILURE_UPDATE;
1367                                 break;
1368                         }
1369                 } else if (!strcasecmp(action, "delete")) {
1370                         if (ast_strlen_zero(cat) || (ast_strlen_zero(var) && ast_strlen_zero(line))) {
1371                                 result = UNSPECIFIED_ARGUMENT;
1372                                 break;
1373                         }
1374                         if (!(category = ast_category_get(cfg, cat))) {
1375                                 result = UNKNOWN_CATEGORY;
1376                                 break;
1377                         }
1378                         if (ast_variable_delete(category, var, match, line)) {
1379                                 result = FAILURE_DELETE;
1380                                 break;
1381                         }
1382                 } else if (!strcasecmp(action, "append")) {
1383                         if (ast_strlen_zero(cat) || ast_strlen_zero(var)) {
1384                                 result = UNSPECIFIED_ARGUMENT;
1385                                 break;
1386                         }
1387                         if (!(category = ast_category_get(cfg, cat))) {
1388                                 result = UNKNOWN_CATEGORY;      
1389                                 break;
1390                         }
1391                         if (!(v = ast_variable_new(var, value, dfn))) {
1392                                 result = FAILURE_ALLOCATION;
1393                                 break;
1394                         }
1395                         if (object || (match && !strcasecmp(match, "object")))
1396                                 v->object = 1;
1397                         ast_variable_append(category, v);
1398                 } else if (!strcasecmp(action, "insert")) {
1399                         if (ast_strlen_zero(cat) || ast_strlen_zero(var) || ast_strlen_zero(line)) {
1400                                 result = UNSPECIFIED_ARGUMENT;
1401                                 break;
1402                         }
1403                         if (!(category = ast_category_get(cfg, cat))) {
1404                                 result = UNKNOWN_CATEGORY;
1405                                 break;
1406                         }
1407                         if (!(v = ast_variable_new(var, value, dfn))) {
1408                                 result = FAILURE_ALLOCATION;
1409                                 break;
1410                         }
1411                         ast_variable_insert(category, v, line);
1412                 }
1413                 else {
1414                         ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
1415                         result = UNKNOWN_ACTION;
1416                         break;
1417                 }
1418         }
1419         ast_free(str1);
1420         ast_free(str2);
1421         return result;
1422 }
1423
1424 static char mandescr_updateconfig[] =
1425 "Description: A 'UpdateConfig' action will modify, create, or delete\n"
1426 "configuration elements in Asterisk configuration files.\n"
1427 "Variables (X's represent 6 digit number beginning with 000000):\n"
1428 "   SrcFilename:   Configuration filename to read(e.g. foo.conf)\n"
1429 "   DstFilename:   Configuration filename to write(e.g. foo.conf)\n"
1430 "   Reload:        Whether or not a reload should take place (or name of specific module)\n"
1431 "   Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,EmptyCat,Update,Delete,Append,Insert)\n"
1432 "   Cat-XXXXXX:    Category to operate on\n"
1433 "   Var-XXXXXX:    Variable to work on\n"
1434 "   Value-XXXXXX:  Value to work on\n"
1435 "   Match-XXXXXX:  Extra match required to match line\n"
1436 "   Line-XXXXXX:   Line in category to operate on (used with delete and insert actions)\n";
1437
1438 static int action_updateconfig(struct mansession *s, const struct message *m)
1439 {
1440         struct ast_config *cfg;
1441         const char *sfn = astman_get_header(m, "SrcFilename");
1442         const char *dfn = astman_get_header(m, "DstFilename");
1443         int res;
1444         const char *rld = astman_get_header(m, "Reload");
1445         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
1446         enum error_type result;
1447
1448         if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
1449                 astman_send_error(s, m, "Filename not specified");
1450                 return 0;
1451         }
1452         if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
1453                 astman_send_error(s, m, "Config file not found");
1454                 return 0;
1455         }
1456         result = handle_updates(s, m, cfg, dfn);
1457         if (!result) {
1458                 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
1459                 res = config_text_file_save(dfn, cfg, "Manager");
1460                 ast_config_destroy(cfg);
1461                 if (res) {
1462                         astman_send_error(s, m, "Save of config failed");
1463                         return 0;
1464                 }
1465                 astman_send_ack(s, m, NULL);
1466                 if (!ast_strlen_zero(rld)) {
1467                         if (ast_true(rld))
1468                                 rld = NULL;
1469                         ast_module_reload(rld);
1470                 }
1471         } else {
1472                 ast_config_destroy(cfg);
1473                 switch(result) {
1474                 case UNKNOWN_ACTION:
1475                         astman_send_error(s, m, "Unknown action command");
1476                         break;
1477                 case UNKNOWN_CATEGORY:
1478                         astman_send_error(s, m, "Given category does not exist");
1479                         break;
1480                 case UNSPECIFIED_CATEGORY:
1481                         astman_send_error(s, m, "Category not specified");
1482                         break;
1483                 case UNSPECIFIED_ARGUMENT:
1484                         astman_send_error(s, m, "Problem with category, value, or line (if required)");
1485                         break;
1486                 case FAILURE_ALLOCATION:
1487                         astman_send_error(s, m, "Memory allocation failure, this should not happen");
1488                         break;
1489                 case FAILURE_DELCAT:
1490                         astman_send_error(s, m, "Delete category did not complete successfully");
1491                         break;
1492                 case FAILURE_EMPTYCAT:
1493                         astman_send_error(s, m, "Empty category did not complete successfully");
1494                         break;
1495                 case FAILURE_UPDATE:
1496                         astman_send_error(s, m, "Update did not complete successfully");
1497                         break;
1498                 case FAILURE_DELETE:
1499                         astman_send_error(s, m, "Delete did not complete successfully");
1500                         break;
1501                 case FAILURE_APPEND:
1502                         astman_send_error(s, m, "Append did not complete successfully");
1503                         break;
1504                 }
1505         }
1506         return 0;
1507 }
1508
1509 static char mandescr_createconfig[] =
1510 "Description: A 'CreateConfig' action will create an empty file in the\n"
1511 "configuration directory. This action is intended to be used before an\n"
1512 "UpdateConfig action.\n"
1513 "Variables\n"
1514 "   Filename:   The configuration filename to create (e.g. foo.conf)\n";
1515
1516 static int action_createconfig(struct mansession *s, const struct message *m)
1517 {
1518         int fd;
1519         const char *fn = astman_get_header(m, "Filename");
1520         struct ast_str *filepath = ast_str_alloca(PATH_MAX);
1521         ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
1522         ast_str_append(&filepath, 0, "%s", fn);
1523
1524         if ((fd = open(filepath->str, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
1525                 close(fd);
1526                 astman_send_ack(s, m, "New configuration file created successfully");
1527         } else 
1528                 astman_send_error(s, m, strerror(errno));
1529
1530         return 0;
1531 }
1532
1533 /*! \brief Manager WAITEVENT */
1534 static char mandescr_waitevent[] =
1535 "Description: A 'WaitEvent' action will ellicit a 'Success' response.  Whenever\n"
1536 "a manager event is queued.  Once WaitEvent has been called on an HTTP manager\n"
1537 "session, events will be generated and queued.\n"
1538 "Variables: \n"
1539 "   Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
1540
1541 static int action_waitevent(struct mansession *s, const struct message *m)
1542 {
1543         const char *timeouts = astman_get_header(m, "Timeout");
1544         int timeout = -1;
1545         int x;
1546         int needexit = 0;
1547         const char *id = astman_get_header(m, "ActionID");
1548         char idText[256];
1549
1550         if (!ast_strlen_zero(id))
1551                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1552         else
1553                 idText[0] = '\0';
1554
1555         if (!ast_strlen_zero(timeouts)) {
1556                 sscanf(timeouts, "%i", &timeout);
1557                 if (timeout < -1)
1558                         timeout = -1;
1559                 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
1560         }
1561
1562         ast_mutex_lock(&s->__lock);
1563         if (s->waiting_thread != AST_PTHREADT_NULL)
1564                 pthread_kill(s->waiting_thread, SIGURG);
1565
1566         if (s->managerid) { /* AMI-over-HTTP session */
1567                 /*
1568                  * Make sure the timeout is within the expire time of the session,
1569                  * as the client will likely abort the request if it does not see
1570                  * data coming after some amount of time.
1571                  */
1572                 time_t now = time(NULL);
1573                 int max = s->sessiontimeout - now - 10;
1574
1575                 if (max < 0)    /* We are already late. Strange but possible. */
1576                         max = 0;
1577                 if (timeout < 0 || timeout > max)
1578                         timeout = max;
1579                 if (!s->send_events)    /* make sure we record events */
1580                         s->send_events = -1;
1581         }
1582         ast_mutex_unlock(&s->__lock);
1583
1584         /* XXX should this go inside the lock ? */
1585         s->waiting_thread = pthread_self();     /* let new events wake up this thread */
1586         ast_debug(1, "Starting waiting for an event!\n");
1587
1588         for (x = 0; x < timeout || timeout < 0; x++) {
1589                 ast_mutex_lock(&s->__lock);
1590                 if (NEW_EVENT(s))
1591                         needexit = 1;
1592                 /* We can have multiple HTTP session point to the same mansession entry.
1593                  * The way we deal with it is not very nice: newcomers kick out the previous
1594                  * HTTP session. XXX this needs to be improved.
1595                  */
1596                 if (s->waiting_thread != pthread_self())
1597                         needexit = 1;
1598                 if (s->needdestroy)
1599                         needexit = 1;
1600                 ast_mutex_unlock(&s->__lock);
1601                 if (needexit)
1602                         break;
1603                 if (s->managerid == 0) {        /* AMI session */
1604                         if (ast_wait_for_input(s->fd, 1000))
1605                                 break;
1606                 } else {        /* HTTP session */
1607                         sleep(1);
1608                 }
1609         }
1610         ast_debug(1, "Finished waiting for an event!\n");
1611         ast_mutex_lock(&s->__lock);
1612         if (s->waiting_thread == pthread_self()) {
1613                 struct eventqent *eqe;
1614                 astman_send_response(s, m, "Success", "Waiting for Event completed.");
1615                 while ( (eqe = NEW_EVENT(s)) ) {
1616                         ref_event(eqe);
1617                         if (((s->readperm & eqe->category) == eqe->category) &&
1618                             ((s->send_events & eqe->category) == eqe->category)) {
1619                                 astman_append(s, "%s", eqe->eventdata);
1620                         }
1621                         s->last_ev = unref_event(s->last_ev);
1622                 }
1623                 astman_append(s,
1624                         "Event: WaitEventComplete\r\n"
1625                         "%s"
1626                         "\r\n", idText);
1627                 s->waiting_thread = AST_PTHREADT_NULL;
1628         } else {
1629                 ast_debug(1, "Abandoning event request!\n");
1630         }
1631         ast_mutex_unlock(&s->__lock);
1632         return 0;
1633 }
1634
1635 static char mandescr_listcommands[] =
1636 "Description: Returns the action name and synopsis for every\n"
1637 "  action that is available to the user\n"
1638 "Variables: NONE\n";
1639
1640 /*! \note The actionlock is read-locked by the caller of this function */
1641 static int action_listcommands(struct mansession *s, const struct message *m)
1642 {
1643         struct manager_action *cur;
1644         struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
1645
1646         astman_start_ack(s, m);
1647         AST_RWLIST_TRAVERSE(&actions, cur, list) {
1648                 if (s->writeperm & cur->authority || cur->authority == 0)
1649                         astman_append(s, "%s: %s (Priv: %s)\r\n",
1650                                 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
1651         }
1652         astman_append(s, "\r\n");
1653
1654         return 0;
1655 }
1656
1657 static char mandescr_events[] =
1658 "Description: Enable/Disable sending of events to this manager\n"
1659 "  client.\n"
1660 "Variables:\n"
1661 "       EventMask: 'on' if all events should be sent,\n"
1662 "               'off' if no events should be sent,\n"
1663 "               'system,call,log' to select which flags events should have to be sent.\n";
1664
1665 static int action_events(struct mansession *s, const struct message *m)
1666 {
1667         const char *mask = astman_get_header(m, "EventMask");
1668         int res;
1669
1670         res = set_eventmask(s, mask);
1671         if (res > 0)
1672                 astman_append(s, "Response: Success\r\n"
1673                                  "Events: On\r\n");
1674         else if (res == 0)
1675                 astman_append(s, "Response: Success\r\n"
1676                                  "Events: Off\r\n");
1677         return 0;
1678 }
1679
1680 static char mandescr_logoff[] =
1681 "Description: Logoff this manager session\n"
1682 "Variables: NONE\n";
1683
1684 static int action_logoff(struct mansession *s, const struct message *m)
1685 {
1686         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
1687         return -1;
1688 }
1689
1690 static int action_login(struct mansession *s, const struct message *m)
1691 {
1692         if (authenticate(s, m)) {
1693                 sleep(1);
1694                 astman_send_error(s, m, "Authentication failed");
1695                 return -1;
1696         }
1697         s->authenticated = 1;
1698         if (manager_displayconnects(s))
1699                 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1700         ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
1701         astman_send_ack(s, m, "Authentication accepted");
1702         return 0;
1703 }
1704
1705 static int action_challenge(struct mansession *s, const struct message *m)
1706 {
1707         const char *authtype = astman_get_header(m, "AuthType");
1708
1709         if (!strcasecmp(authtype, "MD5")) {
1710                 if (ast_strlen_zero(s->challenge))
1711                         snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
1712                 ast_mutex_lock(&s->__lock);
1713                 astman_start_ack(s, m);
1714                 astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
1715                 ast_mutex_unlock(&s->__lock);
1716         } else {
1717                 astman_send_error(s, m, "Must specify AuthType");
1718         }
1719         return 0;
1720 }
1721
1722 static char mandescr_hangup[] =
1723 "Description: Hangup a channel\n"
1724 "Variables: \n"
1725 "       Channel: The channel name to be hungup\n";
1726
1727 static int action_hangup(struct mansession *s, const struct message *m)
1728 {
1729         struct ast_channel *c = NULL;
1730         const char *name = astman_get_header(m, "Channel");
1731         if (ast_strlen_zero(name)) {
1732                 astman_send_error(s, m, "No channel specified");
1733                 return 0;
1734         }
1735         c = ast_get_channel_by_name_locked(name);
1736         if (!c) {
1737                 astman_send_error(s, m, "No such channel");
1738                 return 0;
1739         }
1740         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
1741         ast_channel_unlock(c);
1742         astman_send_ack(s, m, "Channel Hungup");
1743         return 0;
1744 }
1745
1746 static char mandescr_setvar[] =
1747 "Description: Set a global or local channel variable.\n"
1748 "Variables: (Names marked with * are required)\n"
1749 "       Channel: Channel to set variable for\n"
1750 "       *Variable: Variable name\n"
1751 "       *Value: Value\n";
1752
1753 static int action_setvar(struct mansession *s, const struct message *m)
1754 {
1755         struct ast_channel *c = NULL;
1756         const char *name = astman_get_header(m, "Channel");
1757         const char *varname = astman_get_header(m, "Variable");
1758         const char *varval = astman_get_header(m, "Value");
1759
1760         if (ast_strlen_zero(varname)) {
1761                 astman_send_error(s, m, "No variable specified");
1762                 return 0;
1763         }
1764
1765         if (!ast_strlen_zero(name)) {
1766                 c = ast_get_channel_by_name_locked(name);
1767                 if (!c) {
1768                         astman_send_error(s, m, "No such channel");
1769                         return 0;
1770                 }
1771         }
1772
1773         pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
1774
1775         if (c)
1776                 ast_channel_unlock(c);
1777
1778         astman_send_ack(s, m, "Variable Set");
1779
1780         return 0;
1781 }
1782
1783 static char mandescr_getvar[] =
1784 "Description: Get the value of a global or local channel variable.\n"
1785 "Variables: (Names marked with * are required)\n"
1786 "       Channel: Channel to read variable from\n"
1787 "       *Variable: Variable name\n"
1788 "       ActionID: Optional Action id for message matching.\n";
1789
1790 static int action_getvar(struct mansession *s, const struct message *m)
1791 {
1792         struct ast_channel *c = NULL;
1793         const char *name = astman_get_header(m, "Channel");
1794         const char *varname = astman_get_header(m, "Variable");
1795         char *varval;
1796         char workspace[1024] = "";
1797
1798         if (ast_strlen_zero(varname)) {
1799                 astman_send_error(s, m, "No variable specified");
1800                 return 0;
1801         }
1802
1803         if (!ast_strlen_zero(name)) {
1804                 c = ast_get_channel_by_name_locked(name);
1805                 if (!c) {
1806                         astman_send_error(s, m, "No such channel");
1807                         return 0;
1808                 }
1809         }
1810
1811         if (varname[strlen(varname) - 1] == ')') {
1812                 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
1813                 varval = workspace;
1814         } else {
1815                 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
1816         }
1817
1818         if (c)
1819                 ast_channel_unlock(c);
1820         astman_start_ack(s, m);
1821         astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
1822
1823         return 0;
1824 }
1825
1826 static char mandescr_status[] = 
1827 "Description: Lists channel status along with requested channel vars.\n"
1828 "Variables: (Names marked with * are required)\n"
1829 "       *Channel: Name of the channel to query for status\n"
1830 "       Variables: Comma ',' separated list of variables to include\n"
1831 "       ActionID: Optional ID for this transaction\n"
1832 "Will return the status information of each channel along with the\n"
1833 "value for the specified channel variables.\n";
1834  
1835
1836 /*! \brief Manager "status" command to show channels */
1837 /* Needs documentation... */
1838 static int action_status(struct mansession *s, const struct message *m)
1839 {
1840         const char *name = astman_get_header(m, "Channel");
1841         const char *cvariables = astman_get_header(m, "Variables");
1842         char *variables = ast_strdupa(S_OR(cvariables, ""));
1843         struct ast_channel *c;
1844         char bridge[256];
1845         struct timeval now = ast_tvnow();
1846         long elapsed_seconds = 0;
1847         int channels = 0;
1848         int all = ast_strlen_zero(name); /* set if we want all channels */
1849         const char *id = astman_get_header(m, "ActionID");
1850         char idText[256];
1851         AST_DECLARE_APP_ARGS(vars,
1852                 AST_APP_ARG(name)[100];
1853         );
1854         struct ast_str *str = ast_str_create(1000);
1855
1856         if (!ast_strlen_zero(id))
1857                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1858         else
1859                 idText[0] = '\0';
1860
1861         if (all)
1862                 c = ast_channel_walk_locked(NULL);
1863         else {
1864                 c = ast_get_channel_by_name_locked(name);
1865                 if (!c) {
1866                         astman_send_error(s, m, "No such channel");
1867                         return 0;
1868                 }
1869         }
1870         astman_send_ack(s, m, "Channel status will follow");
1871
1872         if (!ast_strlen_zero(cvariables)) {
1873                 AST_STANDARD_APP_ARGS(vars, variables);
1874         }
1875
1876         /* if we look by name, we break after the first iteration */
1877         while (c) {
1878                 if (!ast_strlen_zero(cvariables)) {
1879                         int i;
1880                         ast_str_reset(str);
1881                         for (i = 0; i < vars.argc; i++) {
1882                                 char valbuf[512], *ret = NULL;
1883
1884                                 if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
1885                                         if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
1886                                                 valbuf[0] = '\0';
1887                                         }
1888                                         ret = valbuf;
1889                                 } else {
1890                                         pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
1891                                 }
1892
1893                                 ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
1894                         }
1895                 }
1896
1897                 channels++;
1898                 if (c->_bridge)
1899                         snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
1900                 else
1901                         bridge[0] = '\0';
1902                 if (c->pbx) {
1903                         if (c->cdr) {
1904                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1905                         }
1906                         astman_append(s,
1907                         "Event: Status\r\n"
1908                         "Privilege: Call\r\n"
1909                         "Channel: %s\r\n"
1910                         "CallerIDNum: %s\r\n"
1911                         "CallerIDName: %s\r\n"
1912                         "Accountcode: %s\r\n"
1913                         "ChannelState: %d\r\n"
1914                         "ChannelStateDesc: %s\r\n"
1915                         "Context: %s\r\n"
1916                         "Extension: %s\r\n"
1917                         "Priority: %d\r\n"
1918                         "Seconds: %ld\r\n"
1919                         "%s"
1920                         "Uniqueid: %s\r\n"
1921                         "%s"
1922                         "%s"
1923                         "\r\n",
1924                         c->name,
1925                         S_OR(c->cid.cid_num, ""),
1926                         S_OR(c->cid.cid_name, ""),
1927                         c->accountcode,
1928                         c->_state,
1929                         ast_state2str(c->_state), c->context,
1930                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, str->str, idText);
1931                 } else {
1932                         astman_append(s,
1933                         "Event: Status\r\n"
1934                         "Privilege: Call\r\n"
1935                         "Channel: %s\r\n"
1936                         "CallerIDNum: %s\r\n"
1937                         "CallerIDName: %s\r\n"
1938                         "Account: %s\r\n"
1939                         "State: %s\r\n"
1940                         "%s"
1941                         "Uniqueid: %s\r\n"
1942                         "%s"
1943                         "%s"
1944                         "\r\n",
1945                         c->name,
1946                         S_OR(c->cid.cid_num, "<unknown>"),
1947                         S_OR(c->cid.cid_name, "<unknown>"),
1948                         c->accountcode,
1949                         ast_state2str(c->_state), bridge, c->uniqueid, str->str, idText);
1950                 }
1951                 ast_channel_unlock(c);
1952                 if (!all)
1953                         break;
1954                 c = ast_channel_walk_locked(c);
1955         }
1956         astman_append(s,
1957         "Event: StatusComplete\r\n"
1958         "%s"
1959         "Items: %d\r\n"
1960         "\r\n", idText, channels);
1961         ast_free(str);
1962         return 0;
1963 }
1964
1965 static char mandescr_sendtext[] =
1966 "Description: Sends A Text Message while in a call.\n"
1967 "Variables: (Names marked with * are required)\n"
1968 "       *Channel: Channel to send message to\n"
1969 "       *Message: Message to send\n"
1970 "       ActionID: Optional Action id for message matching.\n";
1971
1972 static int action_sendtext(struct mansession *s, const struct message *m)
1973 {
1974         struct ast_channel *c = NULL;
1975         const char *name = astman_get_header(m, "Channel");
1976         const char *textmsg = astman_get_header(m, "Message");
1977         int res = 0;
1978
1979         if (ast_strlen_zero(name)) {
1980                 astman_send_error(s, m, "No channel specified");
1981                 return 0;
1982         }
1983
1984         if (ast_strlen_zero(textmsg)) {
1985                 astman_send_error(s, m, "No Message specified");
1986                 return 0;
1987         }
1988
1989         c = ast_get_channel_by_name_locked(name);
1990         if (!c) {
1991                 astman_send_error(s, m, "No such channel");
1992                 return 0;
1993         }
1994
1995         res = ast_sendtext(c, textmsg);
1996         ast_channel_unlock(c);
1997         
1998         if (res > 0)
1999                 astman_send_ack(s, m, "Success");
2000         else
2001                 astman_send_error(s, m, "Failure");
2002         
2003         return res;
2004 }
2005
2006 static char mandescr_redirect[] =
2007 "Description: Redirect (transfer) a call.\n"
2008 "Variables: (Names marked with * are required)\n"
2009 "       *Channel: Channel to redirect\n"
2010 "       ExtraChannel: Second call leg to transfer (optional)\n"
2011 "       *Exten: Extension to transfer to\n"
2012 "       *Context: Context to transfer to\n"
2013 "       *Priority: Priority to transfer to\n"
2014 "       ActionID: Optional Action id for message matching.\n";
2015
2016 /*! \brief  action_redirect: The redirect manager command */
2017 static int action_redirect(struct mansession *s, const struct message *m)
2018 {
2019         const char *name = astman_get_header(m, "Channel");
2020         const char *name2 = astman_get_header(m, "ExtraChannel");
2021         const char *exten = astman_get_header(m, "Exten");
2022         const char *context = astman_get_header(m, "Context");
2023         const char *priority = astman_get_header(m, "Priority");
2024         struct ast_channel *chan, *chan2 = NULL;
2025         int pi = 0;
2026         int res;
2027
2028         if (ast_strlen_zero(name)) {
2029                 astman_send_error(s, m, "Channel not specified");
2030                 return 0;
2031         }
2032         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2033                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2034                         astman_send_error(s, m, "Invalid priority");
2035                         return 0;
2036                 }
2037         }
2038         /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
2039         chan = ast_get_channel_by_name_locked(name);
2040         if (!chan) {
2041                 char buf[256];
2042                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
2043                 astman_send_error(s, m, buf);
2044                 return 0;
2045         }
2046         if (ast_check_hangup(chan)) {
2047                 astman_send_error(s, m, "Redirect failed, channel not up.");
2048                 ast_channel_unlock(chan);
2049                 return 0;
2050         }
2051         if (!ast_strlen_zero(name2))
2052                 chan2 = ast_get_channel_by_name_locked(name2);
2053         if (chan2 && ast_check_hangup(chan2)) {
2054                 astman_send_error(s, m, "Redirect failed, extra channel not up.");
2055                 ast_channel_unlock(chan);
2056                 ast_channel_unlock(chan2);
2057                 return 0;
2058         }
2059         res = ast_async_goto(chan, context, exten, pi);
2060         if (!res) {
2061                 if (!ast_strlen_zero(name2)) {
2062                         if (chan2)
2063                                 res = ast_async_goto(chan2, context, exten, pi);
2064                         else
2065                                 res = -1;
2066                         if (!res)
2067                                 astman_send_ack(s, m, "Dual Redirect successful");
2068                         else
2069                                 astman_send_error(s, m, "Secondary redirect failed");
2070                 } else
2071                         astman_send_ack(s, m, "Redirect successful");
2072         } else
2073                 astman_send_error(s, m, "Redirect failed");
2074         if (chan)
2075                 ast_channel_unlock(chan);
2076         if (chan2)
2077                 ast_channel_unlock(chan2);
2078         return 0;
2079 }
2080
2081 static char mandescr_atxfer[] =
2082 "Description: Attended transfer.\n"
2083 "Variables: (Names marked with * are required)\n"
2084 "       *Channel: Transferer's channel\n"
2085 "       *Exten: Extension to transfer to\n"
2086 "       *Context: Context to transfer to\n"
2087 "       *Priority: Priority to transfer to\n"
2088 "       ActionID: Optional Action id for message matching.\n";
2089
2090 static int action_atxfer(struct mansession *s, const struct message *m)
2091 {
2092         const char *name = astman_get_header(m, "Channel");
2093         const char *exten = astman_get_header(m, "Exten");
2094         const char *context = astman_get_header(m, "Context");
2095         const char *priority = astman_get_header(m, "Priority");
2096         struct ast_channel *chan = NULL;
2097         struct ast_call_feature *atxfer_feature = NULL;
2098         char *feature_code = NULL;
2099         int priority_int = 0;
2100
2101         if (ast_strlen_zero(name)) { 
2102                 astman_send_error(s, m, "No channel specified");
2103                 return 0;
2104         }
2105         if (ast_strlen_zero(exten)) {
2106                 astman_send_error(s, m, "No extension specified");
2107                 return 0;
2108         }
2109         if (ast_strlen_zero(context)) {
2110                 astman_send_error(s, m, "No context specified");
2111                 return 0;
2112         }
2113         if (ast_strlen_zero(priority)) {
2114                 astman_send_error(s, m, "No priority specified");
2115                 return 0;
2116         }
2117
2118         if (sscanf(priority, "%d", &priority_int) != 1 && (priority_int = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2119                 astman_send_error(s, m, "Invalid Priority");
2120                 return 0;
2121         }
2122
2123         if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
2124                 astman_send_error(s, m, "No attended transfer feature found");
2125                 return 0;
2126         }
2127
2128         if (!(chan = ast_get_channel_by_name_locked(name))) {
2129                 astman_send_error(s, m, "Channel specified does not exist");
2130                 return 0;
2131         }
2132
2133         for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
2134                 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2135                 ast_queue_frame(chan, &f);
2136         }
2137
2138         for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
2139                 struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
2140                 ast_queue_frame(chan, &f);
2141         }
2142
2143         astman_send_ack(s, m, "Atxfer successfully queued");
2144         ast_channel_unlock(chan);
2145
2146         return 0;
2147 }
2148
2149 static int check_blacklist(const char *cmd)
2150 {
2151         char *cmd_copy, *cur_cmd;
2152         char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
2153         int i;
2154
2155         cmd_copy = ast_strdupa(cmd);
2156         for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
2157                 cur_cmd = ast_strip(cur_cmd);
2158                 if (ast_strlen_zero(cur_cmd)) {
2159                         i--;
2160                         continue;
2161                 }
2162
2163                 cmd_words[i] = cur_cmd;
2164         }
2165
2166         for (i = 0; i < ARRAY_LEN(command_blacklist); i++) {
2167                 int j, match = 1;
2168
2169                 for (j = 0; command_blacklist[i].words[j]; j++) {
2170                         if (ast_strlen_zero(cmd_words[j]) || strcasecmp(cmd_words[j], command_blacklist[i].words[j])) {
2171                                 match = 0;
2172                                 break;
2173                         }
2174                 }
2175
2176                 if (match) {
2177                         return 1;
2178                 }
2179         }
2180
2181         return 0;
2182 }
2183
2184 static char mandescr_command[] =
2185 "Description: Run a CLI command.\n"
2186 "Variables: (Names marked with * are required)\n"
2187 "       *Command: Asterisk CLI command to run\n"
2188 "       ActionID: Optional Action id for message matching.\n";
2189
2190 /*! \brief  Manager command "command" - execute CLI command */
2191 static int action_command(struct mansession *s, const struct message *m)
2192 {
2193         const char *cmd = astman_get_header(m, "Command");
2194         const char *id = astman_get_header(m, "ActionID");
2195         char *buf, *final_buf;
2196         char template[] = "/tmp/ast-ami-XXXXXX";        /* template for temporary file */
2197         int fd = mkstemp(template);
2198         off_t l;
2199
2200         if (ast_strlen_zero(cmd)) {
2201                 astman_send_error(s, m, "No command provided");
2202                 return 0;
2203         }
2204
2205         if (check_blacklist(cmd)) {
2206                 astman_send_error(s, m, "Command blacklisted");
2207                 return 0;
2208         }
2209
2210         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
2211         if (!ast_strlen_zero(id))
2212                 astman_append(s, "ActionID: %s\r\n", id);
2213         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
2214         ast_cli_command(fd, cmd);       /* XXX need to change this to use a FILE * */
2215         l = lseek(fd, 0, SEEK_END);     /* how many chars available */
2216
2217         /* This has a potential to overflow the stack.  Hence, use the heap. */
2218         buf = ast_calloc(1, l + 1);
2219         final_buf = ast_calloc(1, l + 1);
2220         if (buf) {
2221                 lseek(fd, 0, SEEK_SET);
2222                 read(fd, buf, l);
2223                 buf[l] = '\0';
2224                 if (final_buf) {
2225                         term_strip(final_buf, buf, l);
2226                         final_buf[l] = '\0';
2227                 }
2228                 astman_append(s, "%s", S_OR(final_buf, buf));
2229                 ast_free(buf);
2230         }
2231         close(fd);
2232         unlink(template);
2233         astman_append(s, "--END COMMAND--\r\n\r\n");
2234         if (final_buf)
2235                 ast_free(final_buf);
2236         return 0;
2237 }
2238
2239 /*! \brief helper function for originate */
2240 struct fast_originate_helper {
2241         char tech[AST_MAX_EXTENSION];
2242         char data[AST_MAX_EXTENSION];
2243         int timeout;
2244         int format;                             /*!< Codecs used for a call */
2245         char app[AST_MAX_APP];
2246         char appdata[AST_MAX_EXTENSION];
2247         char cid_name[AST_MAX_EXTENSION];
2248         char cid_num[AST_MAX_EXTENSION];
2249         char context[AST_MAX_CONTEXT];
2250         char exten[AST_MAX_EXTENSION];
2251         char idtext[AST_MAX_EXTENSION];
2252         char account[AST_MAX_ACCOUNT_CODE];
2253         int priority;
2254         struct ast_variable *vars;
2255 };
2256
2257 static void *fast_originate(void *data)
2258 {
2259         struct fast_originate_helper *in = data;
2260         int res;
2261         int reason = 0;
2262         struct ast_channel *chan = NULL;
2263         char requested_channel[AST_CHANNEL_NAME];
2264
2265         if (!ast_strlen_zero(in->app)) {
2266                 res = ast_pbx_outgoing_app(in->tech, in->format, in->data, in->timeout, in->app, in->appdata, &reason, 1,
2267                         S_OR(in->cid_num, NULL),
2268                         S_OR(in->cid_name, NULL),
2269                         in->vars, in->account, &chan);
2270         } else {
2271                 res = ast_pbx_outgoing_exten(in->tech, in->format, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
2272                         S_OR(in->cid_num, NULL),
2273                         S_OR(in->cid_name, NULL),
2274                         in->vars, in->account, &chan);
2275         }
2276
2277         if (!chan)
2278                 snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);     
2279         /* Tell the manager what happened with the channel */
2280         manager_event(EVENT_FLAG_CALL, "OriginateResponse",
2281                 "%s"
2282                 "Response: %s\r\n"
2283                 "Channel: %s\r\n"
2284                 "Context: %s\r\n"
2285                 "Exten: %s\r\n"
2286                 "Reason: %d\r\n"
2287                 "Uniqueid: %s\r\n"
2288                 "CallerIDNum: %s\r\n"
2289                 "CallerIDName: %s\r\n",
2290                 in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason, 
2291                 chan ? chan->uniqueid : "<null>",
2292                 S_OR(in->cid_num, "<unknown>"),
2293                 S_OR(in->cid_name, "<unknown>")
2294                 );
2295
2296         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
2297         if (chan)
2298                 ast_channel_unlock(chan);
2299         ast_free(in);
2300         return NULL;
2301 }
2302
2303 static char mandescr_originate[] =
2304 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
2305 "  Application/Data\n"
2306 "Variables: (Names marked with * are required)\n"
2307 "       *Channel: Channel name to call\n"
2308 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
2309 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
2310 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
2311 "       Application: Application to use\n"
2312 "       Data: Data to use (requires 'Application')\n"
2313 "       Timeout: How long to wait for call to be answered (in ms)\n"
2314 "       CallerID: Caller ID to be set on the outgoing channel\n"
2315 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
2316 "       Account: Account code\n"
2317 "       Async: Set to 'true' for fast origination\n";
2318
2319 static int action_originate(struct mansession *s, const struct message *m)
2320 {
2321         const char *name = astman_get_header(m, "Channel");
2322         const char *exten = astman_get_header(m, "Exten");
2323         const char *context = astman_get_header(m, "Context");
2324         const char *priority = astman_get_header(m, "Priority");
2325         const char *timeout = astman_get_header(m, "Timeout");
2326         const char *callerid = astman_get_header(m, "CallerID");
2327         const char *account = astman_get_header(m, "Account");
2328         const char *app = astman_get_header(m, "Application");
2329         const char *appdata = astman_get_header(m, "Data");
2330         const char *async = astman_get_header(m, "Async");
2331         const char *id = astman_get_header(m, "ActionID");
2332         const char *codecs = astman_get_header(m, "Codecs");
2333         struct ast_variable *vars = astman_get_variables(m);
2334         char *tech, *data;
2335         char *l = NULL, *n = NULL;
2336         int pi = 0;
2337         int res;
2338         int to = 30000;
2339         int reason = 0;
2340         char tmp[256];
2341         char tmp2[256];
2342         int format = AST_FORMAT_SLINEAR;
2343
2344         pthread_t th;
2345         if (!name) {
2346                 astman_send_error(s, m, "Channel not specified");
2347                 return 0;
2348         }
2349         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2350                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2351                         astman_send_error(s, m, "Invalid priority");
2352                         return 0;
2353                 }
2354         }
2355         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
2356                 astman_send_error(s, m, "Invalid timeout");
2357                 return 0;
2358         }
2359         ast_copy_string(tmp, name, sizeof(tmp));
2360         tech = tmp;
2361         data = strchr(tmp, '/');
2362         if (!data) {
2363                 astman_send_error(s, m, "Invalid channel");
2364                 return 0;
2365         }
2366         *data++ = '\0';
2367         ast_copy_string(tmp2, callerid, sizeof(tmp2));
2368         ast_callerid_parse(tmp2, &n, &l);
2369         if (n) {
2370                 if (ast_strlen_zero(n))
2371                         n = NULL;
2372         }
2373         if (l) {
2374                 ast_shrink_phone_number(l);
2375                 if (ast_strlen_zero(l))
2376                         l = NULL;
2377         }
2378         if (!ast_strlen_zero(codecs)) {
2379                 format = 0;
2380                 ast_parse_allow_disallow(NULL, &format, codecs, 1);
2381         }
2382         if (ast_true(async)) {
2383                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
2384                 if (!fast) {
2385                         res = -1;
2386                 } else {
2387                         if (!ast_strlen_zero(id))
2388                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
2389                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
2390                         ast_copy_string(fast->data, data, sizeof(fast->data));
2391                         ast_copy_string(fast->app, app, sizeof(fast->app));
2392                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
2393                         if (l)
2394                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
2395                         if (n)
2396                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
2397                         fast->vars = vars;
2398                         ast_copy_string(fast->context, context, sizeof(fast->context));
2399                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
2400                         ast_copy_string(fast->account, account, sizeof(fast->account));
2401                         fast->format = format;
2402                         fast->timeout = to;
2403                         fast->priority = pi;
2404                         if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
2405                                 ast_free(fast);
2406                                 res = -1;
2407                         } else {
2408                                 res = 0;
2409                         }
2410                 }
2411         } else if (!ast_strlen_zero(app)) {
2412                 /* To run the System application (or anything else that goes to shell), you must have the additional System privilege */
2413                 if (!(s->writeperm & EVENT_FLAG_SYSTEM)
2414                         && (
2415                                 strcasestr(app, "system") == 0 || /* System(rm -rf /)
2416                                                                      TrySystem(rm -rf /)       */
2417                                 strcasestr(app, "exec") ||        /* Exec(System(rm -rf /))
2418                                                                      TryExec(System(rm -rf /)) */
2419                                 strcasestr(app, "agi") ||         /* AGI(/bin/rm,-rf /)
2420                                                                      EAGI(/bin/rm,-rf /)       */
2421                                 strstr(appdata, "SHELL") ||       /* NoOp(${SHELL(rm -rf /)})  */
2422                                 strstr(appdata, "EVAL")           /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
2423                                 )) {
2424                         astman_send_error(s, m, "Originate with certain 'Application' arguments requires the additional System privilege, which you do not have.");
2425                         return 0;
2426                 }
2427                 res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
2428         } else {
2429                 if (exten && context && pi)
2430                         res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
2431                 else {
2432                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
2433                         return 0;
2434                 }
2435         }
2436         if (!res)
2437                 astman_send_ack(s, m, "Originate successfully queued");
2438         else
2439                 astman_send_error(s, m, "Originate failed");
2440         return 0;
2441 }
2442
2443 /*! \brief Help text for manager command mailboxstatus
2444  */
2445 static char mandescr_mailboxstatus[] =
2446 "Description: Checks a voicemail account for status.\n"
2447 "Variables: (Names marked with * are required)\n"
2448 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2449 "       ActionID: Optional ActionID for message matching.\n"
2450 "Returns number of messages.\n"
2451 "       Message: Mailbox Status\n"
2452 "       Mailbox: <mailboxid>\n"
2453 "       Waiting: <count>\n"
2454 "\n";
2455
2456 static int action_mailboxstatus(struct mansession *s, const struct message *m)
2457 {
2458         const char *mailbox = astman_get_header(m, "Mailbox");
2459         int ret;
2460
2461         if (ast_strlen_zero(mailbox)) {
2462                 astman_send_error(s, m, "Mailbox not specified");
2463                 return 0;
2464         }
2465         ret = ast_app_has_voicemail(mailbox, NULL);
2466         astman_start_ack(s, m);
2467         astman_append(s, "Message: Mailbox Status\r\n"
2468                          "Mailbox: %s\r\n"
2469                          "Waiting: %d\r\n\r\n", mailbox, ret);
2470         return 0;
2471 }
2472
2473 static char mandescr_mailboxcount[] =
2474 "Description: Checks a voicemail account for new messages.\n"
2475 "Variables: (Names marked with * are required)\n"
2476 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
2477 "       ActionID: Optional ActionID for message matching.\n"
2478 "Returns number of urgent, new and old messages.\n"
2479 "       Message: Mailbox Message Count\n"
2480 "       Mailbox: <mailboxid>\n"
2481 "       UrgentMessages: <count>\n"
2482 "       NewMessages: <count>\n"
2483 "       OldMessages: <count>\n"
2484 "\n";
2485 static int action_mailboxcount(struct mansession *s, const struct message *m)
2486 {
2487         const char *mailbox = astman_get_header(m, "Mailbox");
2488         int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;;
2489
2490         if (ast_strlen_zero(mailbox)) {
2491                 astman_send_error(s, m, "Mailbox not specified");
2492                 return 0;
2493         }
2494         ast_app_inboxcount2(mailbox, &urgentmsgs, &newmsgs, &oldmsgs);
2495         astman_start_ack(s, m);
2496         astman_append(s,   "Message: Mailbox Message Count\r\n"
2497                            "Mailbox: %s\r\n"
2498                            "UrgMessages: %d\r\n"
2499                            "NewMessages: %d\r\n"
2500                            "OldMessages: %d\r\n"
2501                            "\r\n",
2502                            mailbox, urgentmsgs, newmsgs, oldmsgs);
2503         return 0;
2504 }
2505
2506 static char mandescr_extensionstate[] =
2507 "Description: Report the extension state for given extension.\n"
2508 "  If the extension has a hint, will use devicestate to check\n"
2509 "  the status of the device connected to the extension.\n"
2510 "Variables: (Names marked with * are required)\n"
2511 "       *Exten: Extension to check state on\n"
2512 "       *Context: Context for extension\n"
2513 "       ActionId: Optional ID for this transaction\n"
2514 "Will return an \"Extension Status\" message.\n"
2515 "The response will include the hint for the extension and the status.\n";
2516
2517 static int action_extensionstate(struct mansession *s, const struct message *m)
2518 {
2519         const char *exten = astman_get_header(m, "Exten");
2520         const char *context = astman_get_header(m, "Context");
2521         char hint[256] = "";
2522         int status;
2523         if (ast_strlen_zero(exten)) {
2524                 astman_send_error(s, m, "Extension not specified");
2525                 return 0;
2526         }
2527         if (ast_strlen_zero(context))
2528                 context = "default";
2529         status = ast_extension_state(NULL, context, exten);
2530         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
2531         astman_start_ack(s, m);
2532         astman_append(s,   "Message: Extension Status\r\n"
2533                            "Exten: %s\r\n"
2534                            "Context: %s\r\n"
2535                            "Hint: %s\r\n"
2536                            "Status: %d\r\n\r\n",
2537                            exten, context, hint, status);
2538         return 0;
2539 }
2540
2541 static char mandescr_timeout[] =
2542 "Description: Hangup a channel after a certain time.\n"
2543 "Variables: (Names marked with * are required)\n"
2544 "       *Channel: Channel name to hangup\n"
2545 "       *Timeout: Maximum duration of the call (sec)\n"
2546 "Acknowledges set time with 'Timeout Set' message\n";
2547
2548 static int action_timeout(struct mansession *s, const struct message *m)
2549 {
2550         struct ast_channel *c;
2551         const char *name = astman_get_header(m, "Channel");
2552         double timeout = atof(astman_get_header(m, "Timeout"));
2553         struct timeval tv = { timeout, 0 };
2554
2555         if (ast_strlen_zero(name)) {
2556                 astman_send_error(s, m, "No channel specified");
2557                 return 0;
2558         }
2559         if (!timeout || timeout < 0) {
2560                 astman_send_error(s, m, "No timeout specified");
2561                 return 0;
2562         }
2563         c = ast_get_channel_by_name_locked(name);
2564         if (!c) {
2565                 astman_send_error(s, m, "No such channel");
2566                 return 0;
2567         }
2568
2569         tv.tv_usec = (timeout - tv.tv_sec) * 1000000.0;
2570         ast_channel_setwhentohangup_tv(c, tv);
2571         ast_channel_unlock(c);
2572         astman_send_ack(s, m, "Timeout Set");
2573         return 0;
2574 }
2575
2576 /*!
2577  * Send any applicable events to the client listening on this socket.
2578  * Wait only for a finite time on each event, and drop all events whether
2579  * they are successfully sent or not.
2580  */
2581 static int process_events(struct mansession *s)
2582 {
2583         int ret = 0;
2584
2585         ast_mutex_lock(&s->__lock);
2586         if (s->f != NULL) {
2587                 struct eventqent *eqe;
2588
2589                 while ( (eqe = NEW_EVENT(s)) ) {
2590                         ref_event(eqe);
2591                         if (!ret && s->authenticated &&
2592                             (s->readperm & eqe->category) == eqe->category &&
2593                             (s->send_events & eqe->category) == eqe->category) {
2594                                 if (send_string(s, eqe->eventdata) < 0)
2595                                         ret = -1;       /* don't send more */
2596                         }
2597                         s->last_ev = unref_event(s->last_ev);
2598                 }
2599         }
2600         ast_mutex_unlock(&s->__lock);
2601         return ret;
2602 }
2603
2604 static char mandescr_userevent[] =
2605 "Description: Send an event to manager sessions.\n"
2606 "Variables: (Names marked with * are required)\n"
2607 "       *UserEvent: EventStringToSend\n"
2608 "       Header1: Content1\n"
2609 "       HeaderN: ContentN\n";
2610
2611 static int action_userevent(struct mansession *s, const struct message *m)
2612 {
2613         const char *event = astman_get_header(m, "UserEvent");
2614         char body[2048] = "";
2615         int x, bodylen = 0;
2616         for (x = 0; x < m->hdrcount; x++) {
2617                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
2618                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
2619                         bodylen += strlen(m->headers[x]);
2620                         ast_copy_string(body + bodylen, "\r\n", 3);
2621                         bodylen += 2;
2622                 }
2623         }
2624
2625         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
2626         return 0;
2627 }
2628
2629 static char mandescr_coresettings[] =
2630 "Description: Query for Core PBX settings.\n"
2631 "Variables: (Names marked with * are optional)\n"
2632 "       *ActionID: ActionID of this transaction\n";
2633
2634 /*! \brief Show PBX core settings information */
2635 static int action_coresettings(struct mansession *s, const struct message *m)
2636 {
2637         const char *actionid = astman_get_header(m, "ActionID");
2638         char idText[150];
2639
2640         if (!ast_strlen_zero(actionid))
2641                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2642         else
2643                 idText[0] = '\0';
2644
2645         astman_append(s, "Response: Success\r\n"
2646                         "%s"
2647                         "AMIversion: %s\r\n"
2648                         "AsteriskVersion: %s\r\n"
2649                         "SystemName: %s\r\n"
2650                         "CoreMaxCalls: %d\r\n"
2651                         "CoreMaxLoadAvg: %f\r\n"
2652                         "CoreRunUser: %s\r\n"
2653                         "CoreRunGroup: %s\r\n"
2654                         "CoreMaxFilehandles: %d\r\n" 
2655                         "CoreRealTimeEnabled: %s\r\n"
2656                         "CoreCDRenabled: %s\r\n"
2657                         "CoreHTTPenabled: %s\r\n"
2658                         "\r\n",
2659                         idText,
2660                         AMI_VERSION,
2661                         ast_get_version(), 
2662                         ast_config_AST_SYSTEM_NAME,
2663                         option_maxcalls,
2664                         option_maxload,
2665                         ast_config_AST_RUN_USER,
2666                         ast_config_AST_RUN_GROUP,
2667                         option_maxfiles,
2668                         ast_realtime_enabled() ? "Yes" : "No",
2669                         check_cdr_enabled() ? "Yes" : "No",
2670                         check_webmanager_enabled() ? "Yes" : "No"
2671                         );
2672         return 0;
2673 }
2674
2675 static char mandescr_corestatus[] =
2676 "Description: Query for Core PBX status.\n"
2677 "Variables: (Names marked with * are optional)\n"
2678 "       *ActionID: ActionID of this transaction\n";
2679
2680 /*! \brief Show PBX core status information */
2681 static int action_corestatus(struct mansession *s, const struct message *m)
2682 {
2683         const char *actionid = astman_get_header(m, "ActionID");
2684         char idText[150];
2685         char startuptime[150];
2686         char reloadtime[150];
2687         struct ast_tm tm;
2688
2689         if (!ast_strlen_zero(actionid))
2690                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
2691         else
2692                 idText[0] = '\0';
2693
2694         ast_localtime(&ast_startuptime, &tm, NULL);
2695         ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
2696         ast_localtime(&ast_lastreloadtime, &tm, NULL);
2697         ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
2698
2699         astman_append(s, "Response: Success\r\n"
2700                         "%s"
2701                         "CoreStartupTime: %s\r\n"
2702                         "CoreReloadTime: %s\r\n"
2703                         "CoreCurrentCalls: %d\r\n"
2704                         "\r\n",
2705                         idText,
2706                         startuptime,
2707                         reloadtime,
2708                         ast_active_channels()
2709                         );
2710         return 0;
2711 }
2712
2713 static char mandescr_reload[] =
2714 "Description: Send a reload event.\n"
2715 "Variables: (Names marked with * are optional)\n"
2716 "       *ActionID: ActionID of this transaction\n"
2717 "       *Module: Name of the module to reload\n";
2718
2719 /*! \brief Send a reload event */
2720 static int action_reload(struct mansession *s, const struct message *m)
2721 {
2722         const char *module = astman_get_header(m, "Module");
2723         int res = ast_module_reload(S_OR(module, NULL));
2724
2725         if (res == 2)
2726                 astman_send_ack(s, m, "Module Reloaded");
2727         else
2728                 astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
2729         return 0;
2730 }
2731
2732 static char mandescr_coreshowchannels[] =
2733 "Description: List currently defined channels and some information\n"
2734 "             about them.\n"
2735 "Variables:\n"
2736 "          ActionID: Optional Action id for message matching.\n";
2737
2738 /*! \brief  Manager command "CoreShowChannels" - List currently defined channels 
2739  *          and some information about them. */
2740 static int action_coreshowchannels(struct mansession *s, const struct message *m)
2741 {
2742         const char *actionid = astman_get_header(m, "ActionID");
2743         char actionidtext[256];
2744         struct ast_channel *c = NULL;
2745         int numchans = 0;
2746         int duration, durh, durm, durs;
2747
2748         if (!ast_strlen_zero(actionid))
2749                 snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
2750         else
2751                 actionidtext[0] = '\0';
2752
2753         astman_send_listack(s, m, "Channels will follow", "start");     
2754
2755         while ((c = ast_channel_walk_locked(c)) != NULL) {
2756                 struct ast_channel *bc = ast_bridged_channel(c);
2757                 char durbuf[10] = "";
2758
2759                 if (c->cdr && !ast_tvzero(c->cdr->start)) {
2760                         duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
2761                         durh = duration / 3600;
2762                         durm = (duration % 3600) / 60;
2763                         durs = duration % 60;
2764                         snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
2765                 }
2766
2767                 astman_append(s,
2768                         "Channel: %s\r\n"
2769                         "UniqueID: %s\r\n"
2770                         "Context: %s\r\n"
2771                         "Extension: %s\r\n"
2772                         "Priority: %d\r\n"
2773                         "ChannelState: %d\r\n"
2774                         "ChannelStateDesc: %s\r\n"
2775                         "Application: %s\r\n"
2776                         "ApplicationData: %s\r\n"
2777                         "CallerIDnum: %s\r\n"
2778                         "Duration: %s\r\n"
2779                         "AccountCode: %s\r\n"
2780                         "BridgedChannel: %s\r\n"
2781                         "BridgedUniqueID: %s\r\n"
2782                         "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
2783                         c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
2784                         S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
2785                 ast_channel_unlock(c);
2786                 numchans++;
2787         }
2788
2789         astman_append(s,
2790                 "Event: CoreShowChannelsComplete\r\n"
2791                 "EventList: Complete\r\n"
2792                 "ListItems: %d\r\n"
2793                 "%s"
2794                 "\r\n", numchans, actionidtext);
2795
2796         return 0;
2797 }
2798
2799 static char mandescr_modulecheck[] = 
2800 "Description: Checks if Asterisk module is loaded\n"
2801 "Variables: \n"
2802 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
2803 "  Module: <name>          Asterisk module name (not including extension)\n"
2804 "\n"
2805 "Will return Success/Failure\n"
2806 "For success returns, the module revision number is included.\n";
2807
2808 /* Manager function to check if module is loaded */
2809 static int manager_modulecheck(struct mansession *s, const struct message *m)
2810 {
2811         int res;
2812         const char *module = astman_get_header(m, "Module");
2813         const char *id = astman_get_header(m, "ActionID");
2814         char idText[256];
2815 #if !defined(LOW_MEMORY)
2816         const char *version;
2817 #endif
2818         char filename[PATH_MAX];
2819         char *cut;
2820
2821         ast_copy_string(filename, module, sizeof(filename));
2822         if ((cut = strchr(filename, '.'))) {
2823                 *cut = '\0';
2824         } else {
2825                 cut = filename + strlen(filename);
2826         }
2827         snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".so");
2828         ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
2829         res = ast_module_check(filename);
2830         if (!res) {
2831                 astman_send_error(s, m, "Module not loaded");
2832                 return 0;
2833         }
2834         snprintf(cut, (sizeof(filename) - strlen(filename)) - 1, ".c");
2835         ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
2836 #if !defined(LOW_MEMORY)
2837         version = ast_file_version_find(filename);
2838 #endif
2839
2840         if (!ast_strlen_zero(id))
2841                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2842         else
2843                 idText[0] = '\0';
2844         astman_append(s, "Response: Success\r\n%s", idText);
2845 #if !defined(LOW_MEMORY)
2846         astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
2847 #endif
2848         return 0;
2849 }
2850
2851 static char mandescr_moduleload[] = 
2852 "Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
2853 "Variables: \n"
2854 "  ActionID: <id>          Action ID for this transaction. Will be returned.\n"
2855 "  Module: <name>          Asterisk module name (including .so extension)\n"
2856 "                          or subsystem identifier:\n"
2857 "                               cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
2858 "  LoadType: load | unload | reload\n"
2859 "                          The operation to be done on module\n"
2860 " If no module is specified for a reload loadtype, all modules are reloaded";
2861
2862 static int manager_moduleload(struct mansession *s, const struct message *m)
2863 {
2864         int res;
2865         const char *module = astman_get_header(m, "Module");
2866         const char *loadtype = astman_get_header(m, "LoadType");
2867
2868         if (!loadtype || strlen(loadtype) == 0)
2869                 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2870         if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
2871                 astman_send_error(s, m, "Need module name");
2872
2873         if (!strcasecmp(loadtype, "load")) {
2874                 res = ast_load_resource(module);
2875                 if (res)
2876                         astman_send_error(s, m, "Could not load module.");
2877                 else
2878                         astman_send_ack(s, m, "Module loaded.");
2879         } else if (!strcasecmp(loadtype, "unload")) {
2880                 res = ast_unload_resource(module, AST_FORCE_SOFT);
2881                 if (res)
2882                         astman_send_error(s, m, "Could not unload module.");
2883                 else
2884                         astman_send_ack(s, m, "Module unloaded.");
2885         } else if (!strcasecmp(loadtype, "reload")) {
2886                 if (module != NULL) {
2887                         res = ast_module_reload(module);
2888                         if (res == 0)
2889                                 astman_send_error(s, m, "No such module.");
2890                         else if (res == 1)
2891                                 astman_send_error(s, m, "Module does not support reload action.");
2892                         else
2893                                 astman_send_ack(s, m, "Module reloaded.");
2894                 } else {
2895                         ast_module_reload(NULL);        /* Reload all modules */
2896                         astman_send_ack(s, m, "All modules reloaded");
2897                 }
2898         } else 
2899                 astman_send_error(s, m, "Incomplete ModuleLoad action.");
2900         return 0;
2901 }
2902
2903 /*
2904  * Done with the action handlers here, we start with the code in charge
2905  * of accepting connections and serving them.
2906  * accept_thread() forks a new thread for each connection, session_do(),
2907  * which in turn calls get_input() repeatedly until a full message has
2908  * been accumulated, and then invokes process_message() to pass it to
2909  * the appropriate handler.
2910  */
2911
2912 /*
2913  * Process an AMI message, performing desired action.
2914  * Return 0 on success, -1 on error that require the session to be destroyed.
2915  */
2916 static int process_message(struct mansession *s, const struct message *m)
2917 {
2918         char action[80] = "";
2919         int ret = 0;
2920         struct manager_action *tmp;
2921         const char *user = astman_get_header(m, "Username");
2922
2923         ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
2924         ast_debug(1, "Manager received command '%s'\n", action);
2925
2926         if (ast_strlen_zero(action)) {
2927                 ast_mutex_lock(&s->__lock);
2928                 astman_send_error(s, m, "Missing action in request");
2929                 ast_mutex_unlock(&s->__lock);
2930                 return 0;
2931         }
2932
2933         if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
2934                 ast_mutex_lock(&s->__lock);
2935                 astman_send_error(s, m, "Permission denied");
2936                 ast_mutex_unlock(&s->__lock);
2937                 return 0;
2938         }
2939
2940         if (!allowmultiplelogin && !s->authenticated && user &&
2941                 (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
2942                 if (check_manager_session_inuse(user)) {
2943                         sleep(1);
2944                         ast_mutex_lock(&s->__lock);
2945                         astman_send_error(s, m, "Login Already In Use");
2946                         ast_mutex_unlock(&s->__lock);
2947                         return -1;
2948                 }
2949         }
2950
2951         AST_RWLIST_RDLOCK(&actions);
2952         AST_RWLIST_TRAVERSE(&actions, tmp, list) {
2953                 if (strcasecmp(action, tmp->action))
2954                         continue;
2955                 if (s->writeperm & tmp->authority || tmp->authority == 0)
2956                         ret = tmp->func(s, m);
2957                 else
2958                         astman_send_error(s, m, "Permission denied");
2959                 break;
2960         }
2961         AST_RWLIST_UNLOCK(&actions);
2962
2963         if (!tmp) {
2964                 char buf[512];
2965                 snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
2966                 ast_mutex_lock(&s->__lock);
2967                 astman_send_error(s, m, buf);
2968                 ast_mutex_unlock(&s->__lock);
2969         }
2970         if (ret)
2971                 return ret;
2972         /* Once done with our message, deliver any pending events */
2973         return process_events(s);
2974 }
2975
2976 /*!
2977  * Read one full line (including crlf) from the manager socket.
2978  * \note \verbatim
2979  * \r\n is the only valid terminator for the line.
2980  * (Note that, later, '\0' will be considered as the end-of-line marker,
2981  * so everything between the '\0' and the '\r\n' will not be used).
2982  * Also note that we assume output to have at least "maxlen" space.
2983  * \endverbatim
2984  */
2985 static int get_input(struct mansession *s, char *output)
2986 {
2987         int res, x;
2988         int maxlen = sizeof(s->inbuf) - 1;
2989         char *src = s->inbuf;
2990
2991         /*
2992          * Look for \r\n within the buffer. If found, copy to the output
2993          * buffer and return, trimming the \r\n (not used afterwards).
2994          */
2995         for (x = 0; x < s->inlen; x++) {
2996                 int cr; /* set if we have \r */
2997                 if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
2998                         cr = 2; /* Found. Update length to include \r\n */
2999                 else if (src[x] == '\n')
3000                         cr = 1; /* also accept \n only */
3001                 else
3002                         continue;
3003                 memmove(output, src, x);        /*... but trim \r\n */
3004                 output[x] = '\0';               /* terminate the string */
3005                 x += cr;                        /* number of bytes used */
3006                 s->inlen -= x;                  /* remaining size */
3007                 memmove(src, src + x, s->inlen); /* remove used bytes */
3008                 return 1;
3009         }
3010         if (s->inlen >= maxlen) {
3011                 /* no crlf found, and buffer full - sorry, too long for us */
3012                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
3013                 s->inlen = 0;
3014         }
3015         res = 0;
3016         while (res == 0) {
3017                 /* XXX do we really need this locking ? */
3018                 ast_mutex_lock(&s->__lock);
3019                 if (s->pending_event) {
3020                         s->pending_event = 0;
3021                         ast_mutex_unlock(&s->__lock);
3022                         return 0;
3023                 }
3024                 s->waiting_thread = pthread_self();
3025                 ast_mutex_unlock(&s->__lock);
3026
3027                 res = ast_wait_for_input(s->fd, -1);    /* return 0 on timeout ? */
3028
3029                 ast_mutex_lock(&s->__lock);
3030                 s->waiting_thread = AST_PTHREADT_NULL;
3031                 ast_mutex_unlock(&s->__lock);
3032         }
3033         if (res < 0) {
3034                 /* If we get a signal from some other thread (typically because
3035                  * there are new events queued), return 0 to notify the caller.
3036                  */
3037                 if (errno == EINTR || errno == EAGAIN)
3038                         return 0;
3039                 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
3040                 return -1;
3041         }
3042         ast_mutex_lock(&s->__lock);
3043         res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
3044         if (res < 1)
3045                 res = -1;       /* error return */
3046         else {
3047                 s->inlen += res;
3048                 src[s->inlen] = '\0';
3049                 res = 0;
3050         }
3051         ast_mutex_unlock(&s->__lock);
3052         return res;
3053 }
3054
3055 static int do_message(struct mansession *s)
3056 {
3057         struct message m = { 0 };
3058         char header_buf[sizeof(s->inbuf)] = { '\0' };
3059         int res;
3060
3061         for (;;) {
3062                 /* Check if any events are pending and do them if needed */
3063                 if (process_events(s))
3064                         return -1;
3065                 res = get_input(s, header_buf);
3066                 if (res == 0) {
3067                         continue;
3068                 } else if (res > 0) {
3069                         if (ast_strlen_zero(header_buf))
3070                                 return process_message(s, &m) ? -1 : 0;
3071                         else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
3072                                 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
3073                 } else {
3074                         return res;
3075                 }
3076         }
3077 }
3078
3079 /*! \brief The body of the individual manager session.
3080  * Call get_input() to read one line at a time
3081  * (or be woken up on new events), collect the lines in a
3082  * message until found an empty line, and execute the request.
3083  * In any case, deliver events asynchronously through process_events()
3084  * (called from here if no line is available, or at the end of
3085  * process_message(). )
3086  */
3087 static void *session_do(void *data)
3088 {
3089         struct ast_tcptls_session_instance *ser = data;
3090         struct mansession *s = ast_calloc(1, sizeof(*s));
3091         int flags;
3092         int res;
3093
3094         if (s == NULL)
3095                 goto done;
3096
3097         s->writetimeout = 100;
3098         s->waiting_thread = AST_PTHREADT_NULL;
3099
3100         flags = fcntl(ser->fd, F_GETFL);
3101         if (!block_sockets) /* make sure socket is non-blocking */
3102                 flags |= O_NONBLOCK;
3103         else
3104                 flags &= ~O_NONBLOCK;
3105         fcntl(ser->fd, F_SETFL, flags);
3106
3107         ast_mutex_init(&s->__lock);
3108         s->send_events = -1;
3109         /* these fields duplicate those in the 'ser' structure */
3110         s->fd = ser->fd;
3111         s->f = ser->f;
3112         s->sin = ser->requestor;
3113
3114         AST_LIST_LOCK(&sessions);
3115         AST_LIST_INSERT_HEAD(&sessions, s, list);
3116         ast_atomic_fetchadd_int(&num_sessions, 1);
3117         AST_LIST_UNLOCK(&sessions);
3118         /* Hook to the tail of the event queue */
3119         s->last_ev = grab_last();
3120         s->f = ser->f;
3121         astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION);  /* welcome prompt */
3122         for (;;) {
3123                 if ((res = do_message(s)) < 0)
3124                         break;
3125         }
3126         /* session is over, explain why and terminate */
3127         if (s->authenticated) {
3128                         if (manager_displayconnects(s))
3129                         ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3130                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
3131         } else {
3132                         if (displayconnects)
3133                         ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
3134                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
3135         }
3136
3137         /* It is possible under certain circumstances for this session thread
3138            to complete its work and exit *before* the thread that created it
3139            has finished executing the ast_pthread_create_background() function.
3140            If this occurs, some versions of glibc appear to act in a buggy
3141            fashion and attempt to write data into memory that it thinks belongs
3142            to the thread but is in fact not owned by the thread (or may have
3143            been freed completely).
3144
3145            Causing this thread to yield to other threads at least one time
3146            appears to work around this bug.
3147         */
3148         usleep(1);
3149
3150         destroy_session(s);
3151
3152 done:
3153         ao2_ref(ser, -1);
3154         ser = NULL;
3155         return NULL;
3156 }
3157
3158 /*! \brief remove at most n_max stale session from the list. */
3159 static void purge_sessions(int n_max)
3160 {
3161         struct mansession *s;
3162         time_t now = time(NULL);
3163
3164         AST_LIST_LOCK(&sessions);
3165         AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
3166                 if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
3167                         AST_LIST_REMOVE_CURRENT(list);
3168                         ast_atomic_fetchadd_int(&num_sessions, -1);
3169                         if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
3170                                 ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
3171                                         s->username, ast_inet_ntoa(s->sin.sin_addr));
3172                         }
3173                         free_session(s);        /* XXX outside ? */
3174                         if (--n_max <= 0)
3175                                 break;
3176                 }
3177         }
3178         AST_LIST_TRAVERSE_SAFE_END;
3179         AST_LIST_UNLOCK(&sessions);
3180 }
3181
3182 /*
3183  * events are appended to a queue from where they
3184  * can be dispatched to clients.
3185  */
3186 static int append_event(const char *str, int category)
3187 {
3188         struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
3189         static int seq; /* sequence number */
3190
3191         if (!tmp)
3192                 return -1;
3193
3194         /* need to init all fields, because ast_malloc() does not */
3195         tmp->usecount = 0;
3196         tmp->category = category;
3197         tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
3198         AST_LIST_NEXT(tmp, eq_next) = NULL;
3199         strcpy(tmp->eventdata, str);
3200
3201         AST_LIST_LOCK(&all_events);
3202         AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
3203         AST_LIST_UNLOCK(&all_events);
3204
3205         return 0;
3206 }
3207
3208 /* XXX see if can be moved inside the function */
3209 AST_THREADSTORAGE(manager_event_buf);
3210 #define MANAGER_EVENT_BUF_INITSIZE   256
3211
3212 /*! \brief  manager_event: Send AMI event to client */
3213 int __manager_event(int category, const char *event,
3214         const char *file, int line, const char *func, const char *fmt, ...)
3215 {
3216         struct mansession *s;
3217         struct manager_custom_hook *hook;
3218         struct ast_str *auth = ast_str_alloca(80);
3219         const char *cat_str;
3220         va_list ap;
3221         struct timeval now;
3222         struct ast_str *buf;
3223
3224         /* Abort if there aren't any manager sessions */
3225         if (!num_sessions)
3226                 return 0;
3227
3228         if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
3229                 return -1;
3230
3231         cat_str = authority_to_str(category, &auth);
3232         ast_str_set(&buf, 0,
3233                         "Event: %s\r\nPrivilege: %s\r\n",