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