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