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