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