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