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