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