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