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