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