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