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