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