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