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