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