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