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