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