- Doxygen additions
[asterisk/asterisk.git] / manager.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief The Asterisk Management Interface - AMI
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * Channel Management and more
26  * 
27  * \ref amiconf
28  */
29
30 /*! \addtogroup Group_AMI AMI functions 
31 */
32 /*! @{ 
33  Doxygen group */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <netdb.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <netinet/tcp.h>
45 #include <arpa/inet.h>
46 #include <signal.h>
47 #include <errno.h>
48 #include <unistd.h>
49
50 #include "asterisk.h"
51
52 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
53
54 #include "asterisk/channel.h"
55 #include "asterisk/file.h"
56 #include "asterisk/manager.h"
57 #include "asterisk/config.h"
58 #include "asterisk/callerid.h"
59 #include "asterisk/lock.h"
60 #include "asterisk/logger.h"
61 #include "asterisk/options.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/http.h"
69
70 struct fast_originate_helper {
71         char tech[AST_MAX_MANHEADER_LEN];
72         char data[AST_MAX_MANHEADER_LEN];
73         int timeout;
74         char app[AST_MAX_APP];
75         char appdata[AST_MAX_MANHEADER_LEN];
76         char cid_name[AST_MAX_MANHEADER_LEN];
77         char cid_num[AST_MAX_MANHEADER_LEN];
78         char context[AST_MAX_CONTEXT];
79         char exten[AST_MAX_EXTENSION];
80         char idtext[AST_MAX_MANHEADER_LEN];
81         char account[AST_MAX_ACCOUNT_CODE];
82         int priority;
83         struct ast_variable *vars;
84 };
85
86 struct eventqent {
87         int usecount;
88         int category;
89         ast_mutex_t lock;
90         struct eventqent *next;
91         char eventdata[1];
92 };
93
94 static int enabled = 0;
95 static int portno = DEFAULT_MANAGER_PORT;
96 static int asock = -1;
97 static int displayconnects = 1;
98 static int timestampevents = 0;
99 static int httptimeout = 60;
100
101 static pthread_t t;
102 AST_MUTEX_DEFINE_STATIC(sessionlock);
103 static int block_sockets = 0;
104 static int num_sessions = 0;
105 struct eventqent *master_eventq = NULL;
106
107 static struct permalias {
108         int num;
109         char *label;
110 } perms[] = {
111         { EVENT_FLAG_SYSTEM, "system" },
112         { EVENT_FLAG_CALL, "call" },
113         { EVENT_FLAG_LOG, "log" },
114         { EVENT_FLAG_VERBOSE, "verbose" },
115         { EVENT_FLAG_COMMAND, "command" },
116         { EVENT_FLAG_AGENT, "agent" },
117         { EVENT_FLAG_USER, "user" },
118         { -1, "all" },
119         { 0, "none" },
120 };
121
122 static struct mansession {
123         /*! Execution thread */
124         pthread_t t;
125         /*! Thread lock -- don't use in action callbacks, it's already taken care of  */
126         ast_mutex_t __lock;
127         /*! socket address */
128         struct sockaddr_in sin;
129         /*! TCP socket */
130         int fd;
131         /*! Whether or not we're busy doing an action */
132         int busy;
133         /*! Whether or not we're "dead" */
134         int dead;
135         /*! Whether an HTTP manager is in use */
136         int inuse;
137         /*! Whether an HTTP session should be destroyed */
138         int needdestroy;
139         /*! Whether an HTTP session has someone waiting on events */
140         pthread_t waiting_thread;
141         /*! Unique manager identifer */
142         unsigned long managerid;
143         /*! Session timeout if HTTP */
144         time_t sessiontimeout;
145         /*! Output from manager interface */
146         char *outputstr;
147         /*! Logged in username */
148         char username[80];
149         /*! Authentication challenge */
150         char challenge[10];
151         /*! Authentication status */
152         int authenticated;
153         /*! Authorization for reading */
154         int readperm;
155         /*! Authorization for writing */
156         int writeperm;
157         /*! Buffer */
158         char inbuf[AST_MAX_MANHEADER_LEN];
159         int inlen;
160         int send_events;
161         /* Queued events that we've not had the ability to send yet */
162         struct eventqent *eventq;
163         /* Timeout for ast_carefulwrite() */
164         int writetimeout;
165         struct mansession *next;
166 } *sessions = NULL;
167
168 static struct manager_action *first_action = NULL;
169 AST_MUTEX_DEFINE_STATIC(actionlock);
170
171 /*! If you are calling ast_carefulwrite, it is assumed that you are calling
172    it on a file descriptor that _DOES_ have NONBLOCK set.  This way,
173    there is only one system call made to do a write, unless we actually
174    have a need to wait.  This way, we get better performance. */
175 int ast_carefulwrite(int fd, char *s, int len, int timeoutms) 
176 {
177         /* Try to write string, but wait no more than ms milliseconds
178            before timing out */
179         int res=0;
180         struct pollfd fds[1];
181         while(len) {
182                 res = write(fd, s, len);
183                 if ((res < 0) && (errno != EAGAIN)) {
184                         return -1;
185                 }
186                 if (res < 0) res = 0;
187                 len -= res;
188                 s += res;
189                 res = 0;
190                 if (len) {
191                         fds[0].fd = fd;
192                         fds[0].events = POLLOUT;
193                         /* Wait until writable again */
194                         res = poll(fds, 1, timeoutms);
195                         if (res < 1)
196                                 return -1;
197                 }
198         }
199         return res;
200 }
201
202 /*! authority_to_str: Convert authority code to string with serveral options */
203 static char *authority_to_str(int authority, char *res, int reslen)
204 {
205         int running_total = 0, i;
206         memset(res, 0, reslen);
207         for (i=0; i<sizeof(perms) / sizeof(perms[0]) - 1; i++) {
208                 if (authority & perms[i].num) {
209                         if (*res) {
210                                 strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
211                                 running_total++;
212                         }
213                         strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
214                         running_total += strlen(perms[i].label);
215                 }
216         }
217         if (ast_strlen_zero(res)) {
218                 ast_copy_string(res, "<none>", reslen);
219         }
220         return res;
221 }
222
223 static char *complete_show_mancmd(const char *line, const char *word, int pos, int state)
224 {
225         struct manager_action *cur;
226         int which = 0;
227         char *ret = NULL;
228
229         ast_mutex_lock(&actionlock);
230         for (cur = first_action; cur; cur = cur->next) { /* Walk the list of actions */
231                 if (!strncasecmp(word, cur->action, strlen(word)) && ++which > state) {
232                         ret = ast_strdup(cur->action);
233                         break;  /* make sure we exit even if ast_strdup() returns NULL */
234                 }
235         }
236         ast_mutex_unlock(&actionlock);
237         return ret;
238 }
239
240 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int lower)
241 {
242         while (*src && (*maxlen > 6)) {
243                 switch(*src) {
244                 case '<':
245                         strcpy(*dst, "&lt;");
246                         (*dst) += 4;
247                         *maxlen -= 4;
248                         break;
249                 case '>':
250                         strcpy(*dst, "&gt;");
251                         (*dst) += 4;
252                         *maxlen -= 4;
253                         break;
254                 case '\"':
255                         strcpy(*dst, "&quot;");
256                         (*dst) += 6;
257                         *maxlen -= 6;
258                         break;
259                 case '\'':
260                         strcpy(*dst, "&apos;");
261                         (*dst) += 6;
262                         *maxlen -= 6;
263                         break;
264                 case '&':
265                         strcpy(*dst, "&amp;");
266                         (*dst) += 4;
267                         *maxlen -= 4;
268                         break;          
269                 default:
270                         *(*dst)++ = lower ? tolower(*src) : *src;
271                         (*maxlen)--;
272                 }
273                 src++;
274         }
275 }
276
277 static char *xml_translate(char *in, struct ast_variable *vars)
278 {
279         struct ast_variable *v;
280         char *dest=NULL;
281         char *out, *tmp, *var, *val;
282         char *objtype=NULL;
283         int colons = 0;
284         int breaks = 0;
285         size_t len;
286         int count = 1;
287         int escaped = 0;
288         int inobj = 0;
289         int x;
290         v = vars;
291
292         while(v) {
293                 if (!dest && !strcasecmp(v->name, "ajaxdest"))
294                         dest = v->value;
295                 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype")) 
296                         objtype = v->value;
297                 v = v->next;
298         }
299         if (!dest)
300                 dest = "unknown";
301         if (!objtype)
302                 objtype = "generic";
303         for (x=0; in[x]; x++) {
304                 if (in[x] == ':')
305                         colons++;
306                 else if (in[x] == '\n')
307                         breaks++;
308                 else if (strchr("&\"<>", in[x]))
309                         escaped++;
310         }
311         len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
312         out = malloc(len);
313         if (!out)
314                 return 0;
315         tmp = out;
316         while(*in) {
317                 var = in;
318                 while (*in && (*in >= 32))
319                         in++;
320                 if (*in) {
321                         if ((count > 3) && inobj) {
322                                 ast_build_string(&tmp, &len, " /></response>\n");
323                                 inobj = 0;
324                         }
325                         count = 0;
326                         while (*in && (*in < 32)) {
327                                 *in = '\0';
328                                 in++;
329                                 count++;
330                         }
331                         val = strchr(var, ':');
332                         if (val) {
333                                 *val = '\0';
334                                 val++;
335                                 if (*val == ' ')
336                                         val++;
337                                 if (!inobj) {
338                                         ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
339                                         inobj = 1;
340                                 }
341                                 ast_build_string(&tmp, &len, " ");                              
342                                 xml_copy_escape(&tmp, &len, var, 1);
343                                 ast_build_string(&tmp, &len, "='");
344                                 xml_copy_escape(&tmp, &len, val, 0);
345                                 ast_build_string(&tmp, &len, "'");
346                         }
347                 }
348         }
349         if (inobj)
350                 ast_build_string(&tmp, &len, " /></response>\n");
351         return out;
352 }
353
354 static char *html_translate(char *in)
355 {
356         int x;
357         int colons = 0;
358         int breaks = 0;
359         size_t len;
360         int count=1;
361         char *tmp, *var, *val, *out;
362
363         for (x=0; in[x]; x++) {
364                 if (in[x] == ':')
365                         colons++;
366                 if (in[x] == '\n')
367                         breaks++;
368         }
369         len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
370         out = malloc(len);
371         if (!out)
372                 return 0;
373         tmp = out;
374         while(*in) {
375                 var = in;
376                 while (*in && (*in >= 32))
377                         in++;
378                 if (*in) {
379                         if ((count % 4) == 0){
380                                 ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
381                         }
382                         count = 0;
383                         while (*in && (*in < 32)) {
384                                 *in = '\0';
385                                 in++;
386                                 count++;
387                         }
388                         val = strchr(var, ':');
389                         if (val) {
390                                 *val = '\0';
391                                 val++;
392                                 if (*val == ' ')
393                                         val++;
394                                 ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
395                         }
396                 }
397         }
398         return out;
399 }
400
401 void astman_append(struct mansession *s, const char *fmt, ...)
402 {
403         char *stuff;
404         int res;
405         va_list ap;
406         char *tmp;
407
408         va_start(ap, fmt);
409         res = vasprintf(&stuff, fmt, ap);
410         va_end(ap);
411         if (res == -1) {
412                 ast_log(LOG_ERROR, "Memory allocation failure\n");
413                 return;
414         } 
415         if (s->fd > -1)
416                 ast_carefulwrite(s->fd, stuff, strlen(stuff), s->writetimeout);
417         else {
418                 tmp = realloc(s->outputstr, (s->outputstr ? strlen(s->outputstr) : 0) + strlen(stuff) + 1);
419                 if (tmp) {
420                         if (!s->outputstr)
421                                 tmp[0] = '\0';
422                         s->outputstr = tmp;
423                         strcat(s->outputstr, stuff);
424                 }
425         }
426         free(stuff);
427 }
428
429 static int handle_showmancmd(int fd, int argc, char *argv[])
430 {
431         struct manager_action *cur = first_action;
432         char authority[80];
433         int num;
434
435         if (argc != 4)
436                 return RESULT_SHOWUSAGE;
437         ast_mutex_lock(&actionlock);
438         while (cur) { /* Walk the list of actions */
439                 for (num = 3; num < argc; num++) {
440                         if (!strcasecmp(cur->action, argv[num])) {
441                                 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 : "");
442                         }
443                 }
444                 cur = cur->next;
445         }
446
447         ast_mutex_unlock(&actionlock);
448         return RESULT_SUCCESS;
449 }
450
451 /*! \brief  handle_showmancmds: CLI command */
452 /* Should change to "manager show commands" */
453 static int handle_showmancmds(int fd, int argc, char *argv[])
454 {
455         struct manager_action *cur = first_action;
456         char authority[80];
457         char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
458
459         ast_mutex_lock(&actionlock);
460         ast_cli(fd, format, "Action", "Privilege", "Synopsis");
461         ast_cli(fd, format, "------", "---------", "--------");
462         while (cur) { /* Walk the list of actions */
463                 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
464                 cur = cur->next;
465         }
466
467         ast_mutex_unlock(&actionlock);
468         return RESULT_SUCCESS;
469 }
470
471 /*! \brief  handle_showmanconn: CLI command show manager connected */
472 /* Should change to "manager show connected" */
473 static int handle_showmanconn(int fd, int argc, char *argv[])
474 {
475         struct mansession *s;
476         char iabuf[INET_ADDRSTRLEN];
477         char *format = "  %-15.15s  %-15.15s\n";
478         ast_mutex_lock(&sessionlock);
479         s = sessions;
480         ast_cli(fd, format, "Username", "IP Address");
481         while (s) {
482                 ast_cli(fd, format,s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
483                 s = s->next;
484         }
485
486         ast_mutex_unlock(&sessionlock);
487         return RESULT_SUCCESS;
488 }
489
490 /*! \brief  handle_showmanconn: CLI command show manager connected */
491 /* Should change to "manager show connected" */
492 static int handle_showmaneventq(int fd, int argc, char *argv[])
493 {
494         struct eventqent *s;
495         ast_mutex_lock(&sessionlock);
496         s = master_eventq;
497         while (s) {
498                 ast_cli(fd, "Usecount: %d\n",s->usecount);
499                 ast_cli(fd, "Category: %d\n", s->category);
500                 ast_cli(fd, "Event:\n%s", s->eventdata);
501                 s = s->next;
502         }
503         ast_mutex_unlock(&sessionlock);
504         return RESULT_SUCCESS;
505 }
506
507 static char showmancmd_help[] = 
508 "Usage: show manager command <actionname>\n"
509 "       Shows the detailed description for a specific Asterisk manager interface command.\n";
510
511 static char showmancmds_help[] = 
512 "Usage: show manager commands\n"
513 "       Prints a listing of all the available Asterisk manager interface commands.\n";
514
515 static char showmanconn_help[] = 
516 "Usage: show manager connected\n"
517 "       Prints a listing of the users that are currently connected to the\n"
518 "Asterisk manager interface.\n";
519
520 static char showmaneventq_help[] = 
521 "Usage: show manager eventq\n"
522 "       Prints a listing of all events pending in the Asterisk manger\n"
523 "event queue.\n";
524
525 static struct ast_cli_entry show_mancmd_cli =
526         { { "show", "manager", "command", NULL },
527         handle_showmancmd, "Show a manager interface command", showmancmd_help, complete_show_mancmd };
528
529 static struct ast_cli_entry show_mancmds_cli =
530         { { "show", "manager", "commands", NULL },
531         handle_showmancmds, "List manager interface commands", showmancmds_help };
532
533 static struct ast_cli_entry show_manconn_cli =
534         { { "show", "manager", "connected", NULL },
535         handle_showmanconn, "Show connected manager interface users", showmanconn_help };
536
537 static struct ast_cli_entry show_maneventq_cli =
538         { { "show", "manager", "eventq", NULL },
539         handle_showmaneventq, "Show manager interface queued events", showmaneventq_help };
540
541 static void unuse_eventqent(struct eventqent *e)
542 {
543         /* XXX Need to atomically decrement the users.  Change this to atomic_dec
544                one day when we have such a beast XXX */
545         int val;
546         ast_mutex_lock(&e->lock);
547         e->usecount--;
548         val = !e->usecount && e->next;
549         ast_mutex_unlock(&e->lock);
550         /* Wake up sleeping beauty */
551         if (val)
552                 pthread_kill(t, SIGURG);
553 }
554
555 static void free_session(struct mansession *s)
556 {
557         struct eventqent *eqe;
558         if (s->fd > -1)
559                 close(s->fd);
560         if (s->outputstr)
561                 free(s->outputstr);
562         ast_mutex_destroy(&s->__lock);
563         while(s->eventq) {
564                 eqe = s->eventq;
565                 s->eventq = s->eventq->next;
566                 unuse_eventqent(eqe);
567         }
568         free(s);
569 }
570
571 static void destroy_session(struct mansession *s)
572 {
573         struct mansession *cur, *prev = NULL;
574         ast_mutex_lock(&sessionlock);
575         cur = sessions;
576         while(cur) {
577                 if (cur == s)
578                         break;
579                 prev = cur;
580                 cur = cur->next;
581         }
582         if (cur) {
583                 if (prev)
584                         prev->next = cur->next;
585                 else
586                         sessions = cur->next;
587                 free_session(s);
588                 num_sessions--;
589         } else
590                 ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
591         ast_mutex_unlock(&sessionlock);
592         
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"
1190                         "CallerIDName: %s\r\n"
1191                         "Account: %s\r\n"
1192                         "State: %s\r\n"
1193                         "Context: %s\r\n"
1194                         "Extension: %s\r\n"
1195                         "Priority: %d\r\n"
1196                         "Seconds: %ld\r\n"
1197                         "%s"
1198                         "Uniqueid: %s\r\n"
1199                         "%s"
1200                         "\r\n",
1201                         c->name, 
1202                         c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
1203                         c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
1204                         c->accountcode,
1205                         ast_state2str(c->_state), c->context,
1206                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
1207                 } else {
1208                         astman_append(s,
1209                         "Event: Status\r\n"
1210                         "Privilege: Call\r\n"
1211                         "Channel: %s\r\n"
1212                         "CallerID: %s\r\n"
1213                         "CallerIDName: %s\r\n"
1214                         "Account: %s\r\n"
1215                         "State: %s\r\n"
1216                         "%s"
1217                         "Uniqueid: %s\r\n"
1218                         "%s"
1219                         "\r\n",
1220                         c->name, 
1221                         c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
1222                         c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
1223                         c->accountcode,
1224                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
1225                 }
1226                 ast_mutex_unlock(&c->lock);
1227                 if (!all)
1228                         break;
1229                 c = ast_channel_walk_locked(c);
1230         }
1231         astman_append(s,
1232         "Event: StatusComplete\r\n"
1233         "%s"
1234         "\r\n",idText);
1235         return 0;
1236 }
1237
1238 static char mandescr_redirect[] = 
1239 "Description: Redirect (transfer) a call.\n"
1240 "Variables: (Names marked with * are required)\n"
1241 "       *Channel: Channel to redirect\n"
1242 "       ExtraChannel: Second call leg to transfer (optional)\n"
1243 "       *Exten: Extension to transfer to\n"
1244 "       *Context: Context to transfer to\n"
1245 "       *Priority: Priority to transfer to\n"
1246 "       ActionID: Optional Action id for message matching.\n";
1247
1248 /*! \brief  action_redirect: The redirect manager command */
1249 static int action_redirect(struct mansession *s, struct message *m)
1250 {
1251         char *name = astman_get_header(m, "Channel");
1252         char *name2 = astman_get_header(m, "ExtraChannel");
1253         char *exten = astman_get_header(m, "Exten");
1254         char *context = astman_get_header(m, "Context");
1255         char *priority = astman_get_header(m, "Priority");
1256         struct ast_channel *chan, *chan2 = NULL;
1257         int pi = 0;
1258         int res;
1259
1260         if (ast_strlen_zero(name)) {
1261                 astman_send_error(s, m, "Channel not specified");
1262                 return 0;
1263         }
1264         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1265                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1266                         astman_send_error(s, m, "Invalid priority\n");
1267                         return 0;
1268                 }
1269         }
1270         /* XXX watch out, possible deadlock!!! */
1271         chan = ast_get_channel_by_name_locked(name);
1272         if (!chan) {
1273                 char buf[BUFSIZ];
1274                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1275                 astman_send_error(s, m, buf);
1276                 return 0;
1277         }
1278         if (!ast_strlen_zero(name2))
1279                 chan2 = ast_get_channel_by_name_locked(name2);
1280         res = ast_async_goto(chan, context, exten, pi);
1281         if (!res) {
1282                 if (!ast_strlen_zero(name2)) {
1283                         if (chan2)
1284                                 res = ast_async_goto(chan2, context, exten, pi);
1285                         else
1286                                 res = -1;
1287                         if (!res)
1288                                 astman_send_ack(s, m, "Dual Redirect successful");
1289                         else
1290                                 astman_send_error(s, m, "Secondary redirect failed");
1291                 } else
1292                         astman_send_ack(s, m, "Redirect successful");
1293         } else
1294                 astman_send_error(s, m, "Redirect failed");
1295         if (chan)
1296                 ast_mutex_unlock(&chan->lock);
1297         if (chan2)
1298                 ast_mutex_unlock(&chan2->lock);
1299         return 0;
1300 }
1301
1302 static char mandescr_command[] = 
1303 "Description: Run a CLI command.\n"
1304 "Variables: (Names marked with * are required)\n"
1305 "       *Command: Asterisk CLI command to run\n"
1306 "       ActionID: Optional Action id for message matching.\n";
1307
1308 /*! \brief  action_command: Manager command "command" - execute CLI command */
1309 static int action_command(struct mansession *s, struct message *m)
1310 {
1311         char *cmd = astman_get_header(m, "Command");
1312         char *id = astman_get_header(m, "ActionID");
1313         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1314         if (!ast_strlen_zero(id))
1315                 astman_append(s, "ActionID: %s\r\n", id);
1316         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1317         ast_cli_command(s->fd, cmd);
1318         astman_append(s, "--END COMMAND--\r\n\r\n");
1319         return 0;
1320 }
1321
1322 static void *fast_originate(void *data)
1323 {
1324         struct fast_originate_helper *in = data;
1325         int res;
1326         int reason = 0;
1327         struct ast_channel *chan = NULL;
1328
1329         if (!ast_strlen_zero(in->app)) {
1330                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
1331                         S_OR(in->cid_num, NULL), 
1332                         S_OR(in->cid_name, NULL),
1333                         in->vars, in->account, &chan);
1334         } else {
1335                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
1336                         S_OR(in->cid_num, NULL), 
1337                         S_OR(in->cid_name, NULL),
1338                         in->vars, in->account, &chan);
1339         }   
1340         
1341         /* Tell the manager what happened with the channel */
1342         manager_event(EVENT_FLAG_CALL,
1343                 res ? "OriginateSuccess" : "OriginateFailure",
1344                 "%s"
1345                 "Channel: %s/%s\r\n"
1346                 "Context: %s\r\n"
1347                 "Exten: %s\r\n"
1348                 "Reason: %d\r\n"
1349                 "Uniqueid: %s\r\n"
1350                 "CallerID: %s\r\n"
1351                 "CallerIDName: %s\r\n",
1352                 in->idtext, in->tech, in->data, in->context, in->exten, reason, 
1353                 chan ? chan->uniqueid : "<null>",
1354                 in->cid_num ? in->cid_num : "<unknown>",
1355                 in->cid_name ? in->cid_name : "<unknown>"
1356                 );
1357
1358         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1359         if (chan)
1360                 ast_mutex_unlock(&chan->lock);
1361         free(in);
1362         return NULL;
1363 }
1364
1365 static char mandescr_originate[] = 
1366 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1367 "  Application/Data\n"
1368 "Variables: (Names marked with * are required)\n"
1369 "       *Channel: Channel name to call\n"
1370 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
1371 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
1372 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
1373 "       Application: Application to use\n"
1374 "       Data: Data to use (requires 'Application')\n"
1375 "       Timeout: How long to wait for call to be answered (in ms)\n"
1376 "       CallerID: Caller ID to be set on the outgoing channel\n"
1377 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1378 "       Account: Account code\n"
1379 "       Async: Set to 'true' for fast origination\n";
1380
1381 static int action_originate(struct mansession *s, struct message *m)
1382 {
1383         char *name = astman_get_header(m, "Channel");
1384         char *exten = astman_get_header(m, "Exten");
1385         char *context = astman_get_header(m, "Context");
1386         char *priority = astman_get_header(m, "Priority");
1387         char *timeout = astman_get_header(m, "Timeout");
1388         char *callerid = astman_get_header(m, "CallerID");
1389         char *account = astman_get_header(m, "Account");
1390         char *app = astman_get_header(m, "Application");
1391         char *appdata = astman_get_header(m, "Data");
1392         char *async = astman_get_header(m, "Async");
1393         char *id = astman_get_header(m, "ActionID");
1394         struct ast_variable *vars = astman_get_variables(m);
1395         char *tech, *data;
1396         char *l=NULL, *n=NULL;
1397         int pi = 0;
1398         int res;
1399         int to = 30000;
1400         int reason = 0;
1401         char tmp[256];
1402         char tmp2[256];
1403         
1404         pthread_t th;
1405         pthread_attr_t attr;
1406         if (!name) {
1407                 astman_send_error(s, m, "Channel not specified");
1408                 return 0;
1409         }
1410         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1411                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1412                         astman_send_error(s, m, "Invalid priority\n");
1413                         return 0;
1414                 }
1415         }
1416         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1417                 astman_send_error(s, m, "Invalid timeout\n");
1418                 return 0;
1419         }
1420         ast_copy_string(tmp, name, sizeof(tmp));
1421         tech = tmp;
1422         data = strchr(tmp, '/');
1423         if (!data) {
1424                 astman_send_error(s, m, "Invalid channel\n");
1425                 return 0;
1426         }
1427         *data++ = '\0';
1428         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1429         ast_callerid_parse(tmp2, &n, &l);
1430         if (n) {
1431                 if (ast_strlen_zero(n))
1432                         n = NULL;
1433         }
1434         if (l) {
1435                 ast_shrink_phone_number(l);
1436                 if (ast_strlen_zero(l))
1437                         l = NULL;
1438         }
1439         if (ast_true(async)) {
1440                 struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
1441                 if (!fast) {
1442                         res = -1;
1443                 } else {
1444                         memset(fast, 0, sizeof(struct fast_originate_helper));
1445                         if (!ast_strlen_zero(id))
1446                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1447                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1448                         ast_copy_string(fast->data, data, sizeof(fast->data));
1449                         ast_copy_string(fast->app, app, sizeof(fast->app));
1450                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1451                         if (l)
1452                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1453                         if (n)
1454                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1455                         fast->vars = vars;      
1456                         ast_copy_string(fast->context, context, sizeof(fast->context));
1457                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1458                         ast_copy_string(fast->account, account, sizeof(fast->account));
1459                         fast->timeout = to;
1460                         fast->priority = pi;
1461                         pthread_attr_init(&attr);
1462                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1463                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1464                                 res = -1;
1465                         } else {
1466                                 res = 0;
1467                         }
1468                 }
1469         } else if (!ast_strlen_zero(app)) {
1470                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1471         } else {
1472                 if (exten && context && pi)
1473                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1474                 else {
1475                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1476                         return 0;
1477                 }
1478         }   
1479         if (!res)
1480                 astman_send_ack(s, m, "Originate successfully queued");
1481         else
1482                 astman_send_error(s, m, "Originate failed");
1483         return 0;
1484 }
1485
1486 /*!     \brief Help text for manager command mailboxstatus
1487  */
1488 static char mandescr_mailboxstatus[] = 
1489 "Description: Checks a voicemail account for status.\n"
1490 "Variables: (Names marked with * are required)\n"
1491 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1492 "       ActionID: Optional ActionID for message matching.\n"
1493 "Returns number of messages.\n"
1494 "       Message: Mailbox Status\n"
1495 "       Mailbox: <mailboxid>\n"
1496 "       Waiting: <count>\n"
1497 "\n";
1498
1499 static int action_mailboxstatus(struct mansession *s, struct message *m)
1500 {
1501         char *mailbox = astman_get_header(m, "Mailbox");
1502         char *id = astman_get_header(m,"ActionID");
1503         char idText[256] = "";
1504         int ret;
1505         if (ast_strlen_zero(mailbox)) {
1506                 astman_send_error(s, m, "Mailbox not specified");
1507                 return 0;
1508         }
1509         if (!ast_strlen_zero(id))
1510                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1511         ret = ast_app_has_voicemail(mailbox, NULL);
1512         astman_append(s, "Response: Success\r\n"
1513                                    "%s"
1514                                    "Message: Mailbox Status\r\n"
1515                                    "Mailbox: %s\r\n"
1516                                    "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1517         return 0;
1518 }
1519
1520 static char mandescr_mailboxcount[] = 
1521 "Description: Checks a voicemail account for new messages.\n"
1522 "Variables: (Names marked with * are required)\n"
1523 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1524 "       ActionID: Optional ActionID for message matching.\n"
1525 "Returns number of new and old messages.\n"
1526 "       Message: Mailbox Message Count\n"
1527 "       Mailbox: <mailboxid>\n"
1528 "       NewMessages: <count>\n"
1529 "       OldMessages: <count>\n"
1530 "\n";
1531 static int action_mailboxcount(struct mansession *s, struct message *m)
1532 {
1533         char *mailbox = astman_get_header(m, "Mailbox");
1534         char *id = astman_get_header(m,"ActionID");
1535         char idText[256] = "";
1536         int newmsgs = 0, oldmsgs = 0;
1537         if (ast_strlen_zero(mailbox)) {
1538                 astman_send_error(s, m, "Mailbox not specified");
1539                 return 0;
1540         }
1541         ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
1542         if (!ast_strlen_zero(id)) {
1543                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1544         }
1545         astman_append(s, "Response: Success\r\n"
1546                                    "%s"
1547                                    "Message: Mailbox Message Count\r\n"
1548                                    "Mailbox: %s\r\n"
1549                                    "NewMessages: %d\r\n"
1550                                    "OldMessages: %d\r\n" 
1551                                    "\r\n",
1552                                     idText,mailbox, newmsgs, oldmsgs);
1553         return 0;
1554 }
1555
1556 static char mandescr_extensionstate[] = 
1557 "Description: Report the extension state for given extension.\n"
1558 "  If the extension has a hint, will use devicestate to check\n"
1559 "  the status of the device connected to the extension.\n"
1560 "Variables: (Names marked with * are required)\n"
1561 "       *Exten: Extension to check state on\n"
1562 "       *Context: Context for extension\n"
1563 "       ActionId: Optional ID for this transaction\n"
1564 "Will return an \"Extension Status\" message.\n"
1565 "The response will include the hint for the extension and the status.\n";
1566
1567 static int action_extensionstate(struct mansession *s, struct message *m)
1568 {
1569         char *exten = astman_get_header(m, "Exten");
1570         char *context = astman_get_header(m, "Context");
1571         char *id = astman_get_header(m,"ActionID");
1572         char idText[256] = "";
1573         char hint[256] = "";
1574         int status;
1575         if (ast_strlen_zero(exten)) {
1576                 astman_send_error(s, m, "Extension not specified");
1577                 return 0;
1578         }
1579         if (ast_strlen_zero(context))
1580                 context = "default";
1581         status = ast_extension_state(NULL, context, exten);
1582         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1583         if (!ast_strlen_zero(id)) {
1584                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
1585         }
1586         astman_append(s, "Response: Success\r\n"
1587                                    "%s"
1588                                    "Message: Extension Status\r\n"
1589                                    "Exten: %s\r\n"
1590                                    "Context: %s\r\n"
1591                                    "Hint: %s\r\n"
1592                                    "Status: %d\r\n\r\n",
1593                                    idText,exten, context, hint, status);
1594         return 0;
1595 }
1596
1597 static char mandescr_timeout[] = 
1598 "Description: Hangup a channel after a certain time.\n"
1599 "Variables: (Names marked with * are required)\n"
1600 "       *Channel: Channel name to hangup\n"
1601 "       *Timeout: Maximum duration of the call (sec)\n"
1602 "Acknowledges set time with 'Timeout Set' message\n";
1603
1604 static int action_timeout(struct mansession *s, struct message *m)
1605 {
1606         struct ast_channel *c = NULL;
1607         char *name = astman_get_header(m, "Channel");
1608         int timeout = atoi(astman_get_header(m, "Timeout"));
1609         if (ast_strlen_zero(name)) {
1610                 astman_send_error(s, m, "No channel specified");
1611                 return 0;
1612         }
1613         if (!timeout) {
1614                 astman_send_error(s, m, "No timeout specified");
1615                 return 0;
1616         }
1617         c = ast_get_channel_by_name_locked(name);
1618         if (!c) {
1619                 astman_send_error(s, m, "No such channel");
1620                 return 0;
1621         }
1622         ast_channel_setwhentohangup(c, timeout);
1623         ast_mutex_unlock(&c->lock);
1624         astman_send_ack(s, m, "Timeout Set");
1625         return 0;
1626 }
1627
1628 static int process_events(struct mansession *s)
1629 {
1630         struct eventqent *eqe;
1631         int ret = 0;
1632         ast_mutex_lock(&s->__lock);
1633         if (s->fd > -1) {
1634                 s->busy--;
1635                 if (!s->eventq)
1636                         s->eventq = master_eventq;
1637                 while(s->eventq->next) {
1638                         eqe = s->eventq->next;
1639                         if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1640                             ((s->send_events & eqe->category) == eqe->category)) {
1641                                 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata, strlen(eqe->eventdata), s->writetimeout) < 0)
1642                                         ret = -1;
1643                         }
1644                         unuse_eventqent(s->eventq);
1645                         s->eventq = eqe;
1646                 }
1647         }
1648         ast_mutex_unlock(&s->__lock);
1649         return ret;
1650 }
1651
1652 static int process_message(struct mansession *s, struct message *m)
1653 {
1654         char action[80] = "";
1655         struct manager_action *tmp = first_action;
1656         char *id = astman_get_header(m,"ActionID");
1657         char idText[256] = "";
1658         char iabuf[INET_ADDRSTRLEN];
1659         int ret = 0;
1660
1661         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1662         ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
1663
1664         if (ast_strlen_zero(action)) {
1665                 astman_send_error(s, m, "Missing action in request");
1666                 return 0;
1667         }
1668         if (!ast_strlen_zero(id)) {
1669                 snprintf(idText, sizeof(idText), "ActionID: %s\r\n",id);
1670         }
1671         if (!s->authenticated) {
1672                 if (!strcasecmp(action, "Challenge")) {
1673                         char *authtype;
1674                         authtype = astman_get_header(m, "AuthType");
1675                         if (!strcasecmp(authtype, "MD5")) {
1676                                 if (ast_strlen_zero(s->challenge))
1677                                         snprintf(s->challenge, sizeof(s->challenge), "%d", rand());
1678                                 ast_mutex_lock(&s->__lock);
1679                                 astman_append(s, "Response: Success\r\n"
1680                                                 "%s"
1681                                                 "Challenge: %s\r\n\r\n",
1682                                                 idText,s->challenge);
1683                                 ast_mutex_unlock(&s->__lock);
1684                                 return 0;
1685                         } else {
1686                                 astman_send_error(s, m, "Must specify AuthType");
1687                                 return 0;
1688                         }
1689                 } else if (!strcasecmp(action, "Login")) {
1690                         if (authenticate(s, m)) {
1691                                 sleep(1);
1692                                 astman_send_error(s, m, "Authentication failed");
1693                                 return -1;
1694                         } else {
1695                                 s->authenticated = 1;
1696                                 if (option_verbose > 1) {
1697                                         if ( displayconnects ) {
1698                                                 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));
1699                                         }
1700                                 }
1701                                 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));
1702                                 astman_send_ack(s, m, "Authentication accepted");
1703                         }
1704                 } else if (!strcasecmp(action, "Logoff")) {
1705                         astman_send_ack(s, m, "See ya");
1706                         return -1;
1707                 } else
1708                         astman_send_error(s, m, "Authentication Required");
1709         } else {
1710                 ast_mutex_lock(&s->__lock);
1711                 s->busy++;
1712                 ast_mutex_unlock(&s->__lock);
1713                 while( tmp ) {          
1714                         if (!strcasecmp(action, tmp->action)) {
1715                                 if ((s->writeperm & tmp->authority) == tmp->authority) {
1716                                         if (tmp->func(s, m))
1717                                                 ret = -1;
1718                                 } else {
1719                                         astman_send_error(s, m, "Permission denied");
1720                                 }
1721                                 break;
1722                         }
1723                         tmp = tmp->next;
1724                 }
1725                 if (!tmp)
1726                         astman_send_error(s, m, "Invalid/unknown command");
1727         }
1728         if (ret)
1729                 return ret;
1730         return process_events(s);
1731 }
1732
1733 static int get_input(struct mansession *s, char *output)
1734 {
1735         /* output must have at least sizeof(s->inbuf) space */
1736         int res;
1737         int x;
1738         struct pollfd fds[1];
1739         char iabuf[INET_ADDRSTRLEN];
1740         for (x=1;x<s->inlen;x++) {
1741                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1742                         /* Copy output data up to and including \r\n */
1743                         memcpy(output, s->inbuf, x + 1);
1744                         /* Add trailing \0 */
1745                         output[x+1] = '\0';
1746                         /* Move remaining data back to the front */
1747                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
1748                         s->inlen -= (x + 1);
1749                         return 1;
1750                 }
1751         } 
1752         if (s->inlen >= sizeof(s->inbuf) - 1) {
1753                 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);
1754                 s->inlen = 0;
1755         }
1756         fds[0].fd = s->fd;
1757         fds[0].events = POLLIN;
1758         do {
1759                 ast_mutex_lock(&s->__lock);
1760                 s->waiting_thread = pthread_self();
1761                 ast_mutex_unlock(&s->__lock);
1762
1763                 res = poll(fds, 1, -1);
1764
1765                 ast_mutex_lock(&s->__lock);
1766                 s->waiting_thread = AST_PTHREADT_NULL;
1767                 ast_mutex_unlock(&s->__lock);
1768                 if (res < 0) {
1769                         if (errno == EINTR) {
1770                                 if (s->dead)
1771                                         return -1;
1772                                 return 0;
1773                         }
1774                         ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
1775                         return -1;
1776                 } else if (res > 0) {
1777                         ast_mutex_lock(&s->__lock);
1778                         res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
1779                         ast_mutex_unlock(&s->__lock);
1780                         if (res < 1)
1781                                 return -1;
1782                         break;
1783                 }
1784         } while(1);
1785         s->inlen += res;
1786         s->inbuf[s->inlen] = '\0';
1787         return 0;
1788 }
1789
1790 static void *session_do(void *data)
1791 {
1792         struct mansession *s = data;
1793         struct message m;
1794         char iabuf[INET_ADDRSTRLEN];
1795         int res;
1796         
1797         ast_mutex_lock(&s->__lock);
1798         astman_append(s, "Asterisk Call Manager/1.0\r\n");
1799         ast_mutex_unlock(&s->__lock);
1800         memset(&m, 0, sizeof(m));
1801         for (;;) {
1802                 res = get_input(s, m.headers[m.hdrcount]);
1803                 if (res > 0) {
1804                         /* Strip trailing \r\n */
1805                         if (strlen(m.headers[m.hdrcount]) < 2)
1806                                 continue;
1807                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
1808                         if (ast_strlen_zero(m.headers[m.hdrcount])) {
1809                                 if (process_message(s, &m))
1810                                         break;
1811                                 memset(&m, 0, sizeof(m));
1812                         } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
1813                                 m.hdrcount++;
1814                 } else if (res < 0) {
1815                         break;
1816                 } else if (s->eventq->next) {
1817                         if (process_events(s))
1818                                 break;
1819                 }
1820         }
1821         if (s->authenticated) {
1822                 if (option_verbose > 1) {
1823                         if (displayconnects) 
1824                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));    
1825                 }
1826                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1827         } else {
1828                 if (option_verbose > 1) {
1829                         if ( displayconnects )
1830                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1831                 }
1832                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1833         }
1834         destroy_session(s);
1835         return NULL;
1836 }
1837
1838 static void *accept_thread(void *ignore)
1839 {
1840         int as;
1841         struct sockaddr_in sin;
1842         socklen_t sinlen;
1843         struct eventqent *eqe;
1844         struct mansession *s, *prev=NULL, *next;
1845         struct protoent *p;
1846         int arg = 1;
1847         int flags;
1848         pthread_attr_t attr;
1849         time_t now;
1850         struct pollfd pfds[1];
1851         char iabuf[INET_ADDRSTRLEN];
1852
1853         pthread_attr_init(&attr);
1854         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1855
1856         for (;;) {
1857                 time(&now);
1858                 ast_mutex_lock(&sessionlock);
1859                 prev = NULL;
1860                 s = sessions;
1861                 while(s) {
1862                         next = s->next;
1863                         if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
1864                                 num_sessions--;
1865                                 if (prev)
1866                                         prev->next = next;
1867                                 else
1868                                         sessions = next;
1869                                 if (s->authenticated && (option_verbose > 1) && displayconnects) {
1870                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
1871                                                 s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1872                                 }
1873                                 free_session(s);
1874                         } else
1875                                 prev = s;
1876                         s = next;
1877                 }
1878                 /* Purge master event queue of old, unused events, but make sure we
1879                    always keep at least one in the queue */
1880                 eqe = master_eventq;
1881                 while (master_eventq->next && !master_eventq->usecount) {
1882                         eqe = master_eventq;
1883                         master_eventq = master_eventq->next;
1884                         free(eqe);
1885                 }
1886                 ast_mutex_unlock(&sessionlock);
1887
1888                 sinlen = sizeof(sin);
1889                 pfds[0].fd = asock;
1890                 pfds[0].events = POLLIN;
1891                 /* Wait for something to happen, but timeout every few seconds so
1892                    we can ditch any old manager sessions */
1893                 if (poll(pfds, 1, 5000) < 1)
1894                         continue;
1895                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
1896                 if (as < 0) {
1897                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
1898                         continue;
1899                 }
1900                 p = getprotobyname("tcp");
1901                 if (p) {
1902                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
1903                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
1904                         }
1905                 }
1906                 s = malloc(sizeof(struct mansession));
1907                 if (!s) {
1908                         ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
1909                         continue;
1910                 } 
1911                 memset(s, 0, sizeof(struct mansession));
1912                 memcpy(&s->sin, &sin, sizeof(sin));
1913                 s->writetimeout = 100;
1914                 s->waiting_thread = AST_PTHREADT_NULL;
1915
1916                 if(! block_sockets) {
1917                         /* For safety, make sure socket is non-blocking */
1918                         flags = fcntl(as, F_GETFL);
1919                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
1920                 }
1921                 ast_mutex_init(&s->__lock);
1922                 s->fd = as;
1923                 s->send_events = -1;
1924                 ast_mutex_lock(&sessionlock);
1925                 num_sessions++;
1926                 s->next = sessions;
1927                 sessions = s;
1928                 /* Find the last place in the master event queue and hook ourselves
1929                    in there */
1930                 s->eventq = master_eventq;
1931                 while(s->eventq->next)
1932                         s->eventq = s->eventq->next;
1933                 ast_mutex_lock(&s->eventq->lock);
1934                 s->eventq->usecount++;
1935                 ast_mutex_unlock(&s->eventq->lock);
1936                 ast_mutex_unlock(&sessionlock);
1937                 if (ast_pthread_create(&s->t, &attr, session_do, s))
1938                         destroy_session(s);
1939         }
1940         pthread_attr_destroy(&attr);
1941         return NULL;
1942 }
1943
1944 static int append_event(const char *str, int category)
1945 {
1946         struct eventqent *tmp, *prev=NULL;
1947         tmp = malloc(sizeof(struct eventqent) + strlen(str));
1948         if (tmp) {
1949                 ast_mutex_init(&tmp->lock);
1950                 tmp->next = NULL;
1951                 tmp->category = category;
1952                 strcpy(tmp->eventdata, str);
1953                 if (master_eventq) {
1954                         prev = master_eventq;
1955                         while(prev->next) 
1956                                 prev = prev->next;
1957                         prev->next = tmp;
1958                 } else {
1959                         master_eventq = tmp;
1960                 }
1961                 tmp->usecount = num_sessions;
1962                 return 0;
1963         }
1964         return -1;
1965 }
1966
1967 /*! \brief  manager_event: Send AMI event to client */
1968 int manager_event(int category, const char *event, const char *fmt, ...)
1969 {
1970         struct mansession *s;
1971         char auth[80];
1972         char tmp[4096] = "";
1973         char *tmp_next = tmp;
1974         size_t tmp_left = sizeof(tmp) - 2;
1975         va_list ap;
1976         struct timeval now;
1977
1978         /* Abort if there aren't any manager sessions */
1979         if (!num_sessions)
1980                 return 0;
1981
1982         ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
1983                          event, authority_to_str(category, auth, sizeof(auth)));
1984         if (timestampevents) {
1985                 now = ast_tvnow();
1986                 ast_build_string(&tmp_next, &tmp_left, "Timestamp: %ld.%06lu\r\n",
1987                                  now.tv_sec, (unsigned long) now.tv_usec);
1988         }
1989         va_start(ap, fmt);
1990         ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
1991         va_end(ap);
1992         *tmp_next++ = '\r';
1993         *tmp_next++ = '\n';
1994         *tmp_next = '\0';
1995         
1996         ast_mutex_lock(&sessionlock);
1997         /* Append even to master list and wake up any sleeping sessions */
1998         append_event(tmp, category);
1999         for (s = sessions; s; s = s->next) {
2000                 ast_mutex_lock(&s->__lock);
2001                 if (s->waiting_thread != AST_PTHREADT_NULL)
2002                         pthread_kill(s->waiting_thread, SIGURG);
2003                 ast_mutex_unlock(&s->__lock);
2004         }
2005         ast_mutex_unlock(&sessionlock);
2006
2007         return 0;
2008 }
2009
2010 int ast_manager_unregister( char *action ) 
2011 {
2012         struct manager_action *cur = first_action, *prev = first_action;
2013
2014         ast_mutex_lock(&actionlock);
2015         while( cur ) {          
2016                 if (!strcasecmp(action, cur->action)) {
2017                         prev->next = cur->next;
2018                         free(cur);
2019                         if (option_verbose > 1) 
2020                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2021                         ast_mutex_unlock(&actionlock);
2022                         return 0;
2023                 }
2024                 prev = cur;
2025                 cur = cur->next;
2026         }
2027         ast_mutex_unlock(&actionlock);
2028         return 0;
2029 }
2030
2031 static int manager_state_cb(char *context, char *exten, int state, void *data)
2032 {
2033         /* Notify managers of change */
2034         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2035         return 0;
2036 }
2037
2038 static int ast_manager_register_struct(struct manager_action *act)
2039 {
2040         struct manager_action *cur = first_action, *prev = NULL;
2041         int ret;
2042
2043         ast_mutex_lock(&actionlock);
2044         while(cur) { /* Walk the list of actions */
2045                 ret = strcasecmp(cur->action, act->action);
2046                 if (ret == 0) {
2047                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2048                         ast_mutex_unlock(&actionlock);
2049                         return -1;
2050                 } else if (ret > 0) {
2051                         /* Insert these alphabetically */
2052                         if (prev) {
2053                                 act->next = prev->next;
2054                                 prev->next = act;
2055                         } else {
2056                                 act->next = first_action;
2057                                 first_action = act;
2058                         }
2059                         break;
2060                 }
2061                 prev = cur; 
2062                 cur = cur->next;
2063         }
2064         
2065         if (!cur) {
2066                 if (prev)
2067                         prev->next = act;
2068                 else
2069                         first_action = act;
2070                 act->next = NULL;
2071         }
2072
2073         if (option_verbose > 1) 
2074                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2075         ast_mutex_unlock(&actionlock);
2076         return 0;
2077 }
2078
2079 /*! \brief register a new command with manager, including online help. This is 
2080         the preferred way to register a manager command */
2081 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2082 {
2083         struct manager_action *cur;
2084
2085         cur = malloc(sizeof(struct manager_action));
2086         if (!cur) {
2087                 ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
2088                 return -1;
2089         }
2090         cur->action = action;
2091         cur->authority = auth;
2092         cur->func = func;
2093         cur->synopsis = synopsis;
2094         cur->description = description;
2095         cur->next = NULL;
2096
2097         ast_manager_register_struct(cur);
2098
2099         return 0;
2100 }
2101 /*! @}
2102  END Doxygen group */
2103
2104 static struct mansession *find_session(unsigned long ident)
2105 {
2106         struct mansession *s;
2107         ast_mutex_lock(&sessionlock);
2108         s = sessions;
2109         while(s) {
2110                 ast_mutex_lock(&s->__lock);
2111                 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2112                         s->inuse++;
2113                         break;
2114                 }
2115                 ast_mutex_unlock(&s->__lock);
2116                 s = s->next;
2117         }
2118         ast_mutex_unlock(&sessionlock);
2119         return s;
2120 }
2121
2122
2123 static void vars2msg(struct message *m, struct ast_variable *vars)
2124 {
2125         int x;
2126         for (x=0;vars && (x<AST_MAX_MANHEADERS);x++,vars = vars->next) {
2127                 if (!vars)
2128                         break;
2129                 m->hdrcount = x + 1;
2130                 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2131         }
2132 }
2133
2134 #define FORMAT_RAW      0
2135 #define FORMAT_HTML     1
2136 #define FORMAT_XML      2
2137
2138 static char *contenttype[] = { "plain", "html", "xml" };
2139
2140 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2141 {
2142         struct mansession *s=NULL;
2143         unsigned long ident=0;
2144         char workspace[256];
2145         char cookie[128];
2146         char iabuf[INET_ADDRSTRLEN];
2147         size_t len = sizeof(workspace);
2148         int blastaway = 0;
2149         char *c = workspace;
2150         char *retval=NULL;
2151         struct message m;
2152         struct ast_variable *v;
2153         
2154         v = params;
2155         while(v) {
2156                 if (!strcasecmp(v->name, "mansession_id")) {
2157                         sscanf(v->value, "%lx", &ident);
2158                         break;
2159                 }
2160                 v = v->next;
2161         }
2162         s = find_session(ident);
2163
2164         if (!s) {
2165                 /* Create new session */
2166                 s = calloc(1, sizeof(struct mansession));
2167                 memcpy(&s->sin, requestor, sizeof(s->sin));
2168                 s->fd = -1;
2169                 s->waiting_thread = AST_PTHREADT_NULL;
2170                 s->send_events = 0;
2171                 ast_mutex_init(&s->__lock);
2172                 ast_mutex_lock(&s->__lock);
2173                 ast_mutex_lock(&sessionlock);
2174                 s->inuse = 1;
2175                 s->managerid = rand() | (unsigned long)s;
2176                 s->next = sessions;
2177                 sessions = s;
2178                 num_sessions++;
2179                 /* Hook into the last spot in the event queue */
2180                 s->eventq = master_eventq;
2181                 while(s->eventq->next)
2182                         s->eventq = s->eventq->next;
2183                 ast_mutex_lock(&s->eventq->lock);
2184                 s->eventq->usecount++;
2185                 ast_mutex_unlock(&s->eventq->lock);
2186                 ast_mutex_unlock(&sessionlock);
2187         }
2188
2189         /* Reset HTTP timeout.  If we're not yet authenticated, keep it extremely short */
2190         time(&s->sessiontimeout);
2191         if (!s->authenticated && (httptimeout > 5))
2192                 s->sessiontimeout += 5;
2193         else
2194                 s->sessiontimeout += httptimeout;
2195         ast_mutex_unlock(&s->__lock);
2196         
2197         memset(&m, 0, sizeof(m));
2198         if (s) {
2199                 char tmp[80];
2200                 ast_build_string(&c, &len, "Content-type: text/%s\n", contenttype[format]);
2201                 sprintf(tmp, "%08lx", s->managerid);
2202                 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2203                 if (format == FORMAT_HTML)
2204                         ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Test Interface</title>");
2205                 vars2msg(&m, params);
2206                 if (format == FORMAT_XML) {
2207                         ast_build_string(&c, &len, "<ajax-response>\n");
2208                 } else if (format == FORMAT_HTML) {
2209                         ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2210                         ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2211                 }
2212                 if (process_message(s, &m)) {
2213                         if (s->authenticated) {
2214                                 if (option_verbose > 1) {
2215                                         if (displayconnects) 
2216                                                 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));    
2217                                 }
2218                                 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2219                         } else {
2220                                 if (option_verbose > 1) {
2221                                         if (displayconnects)
2222                                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2223                                 }
2224                                 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2225                         }
2226                         s->needdestroy = 1;
2227                 }
2228                 if (s->outputstr) {
2229                         char *tmp;
2230                         if (format == FORMAT_XML)
2231                                 tmp = xml_translate(s->outputstr, params);
2232                         else if (format == FORMAT_HTML)
2233                                 tmp = html_translate(s->outputstr);
2234                         else
2235                                 tmp = s->outputstr;
2236                         if (tmp) {
2237                                 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2238                                 if (retval) {
2239                                         strcpy(retval, workspace);
2240                                         strcpy(retval + strlen(retval), tmp);
2241                                         c = retval + strlen(retval);
2242                                         len = 120;
2243                                 }
2244                                 free(tmp);
2245                         }
2246                         if (tmp != s->outputstr)
2247                                 free(s->outputstr);
2248                         s->outputstr = NULL;
2249                 }
2250                 /* Still okay because c would safely be pointing to workspace even
2251                    if retval failed to allocate above */
2252                 if (format == FORMAT_XML) {
2253                         ast_build_string(&c, &len, "</ajax-response>\n");
2254                 } else if (format == FORMAT_HTML)
2255                         ast_build_string(&c, &len, "</table></body>\r\n");
2256         } else {
2257                 *status = 500;
2258                 *title = strdup("Server Error");
2259         }
2260         ast_mutex_lock(&s->__lock);
2261         if (s->needdestroy) {
2262                 if (s->inuse == 1) {
2263                         ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2264                         blastaway = 1;
2265                 } else {
2266                         ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2267                         if (s->waiting_thread != AST_PTHREADT_NULL)
2268                                 pthread_kill(s->waiting_thread, SIGURG);
2269                         s->inuse--;
2270                 }
2271         } else
2272                 s->inuse--;
2273         ast_mutex_unlock(&s->__lock);
2274         
2275         if (blastaway)
2276                 destroy_session(s);
2277         if (*status != 200)
2278                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
2279         return retval;
2280 }
2281
2282 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2283 {
2284         return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2285 }
2286
2287 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2288 {
2289         return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2290 }
2291
2292 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2293 {
2294         return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2295 }
2296
2297 struct ast_http_uri rawmanuri = {
2298         .description = "Raw HTTP Manager Event Interface",
2299         .uri = "rawman",
2300         .has_subtree = 0,
2301         .callback = rawman_http_callback,
2302 };
2303
2304 struct ast_http_uri manageruri = {
2305         .description = "HTML Manager Event Interface",
2306         .uri = "manager",
2307         .has_subtree = 0,
2308         .callback = manager_http_callback,
2309 };
2310
2311 struct ast_http_uri managerxmluri = {
2312         .description = "XML Manager Event Interface",
2313         .uri = "mxml",
2314         .has_subtree = 0,
2315         .callback = mxml_http_callback,
2316 };
2317
2318 static int registered = 0;
2319 static int webregged = 0;
2320
2321 int init_manager(void)
2322 {
2323         struct ast_config *cfg;
2324         char *val;
2325         int oldportno = portno;
2326         static struct sockaddr_in ba;
2327         int x = 1;
2328         int flags;
2329         int webenabled=0;
2330         int newhttptimeout = 60;
2331         if (!registered) {
2332                 /* Register default actions */
2333                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2334                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2335                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2336                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2337                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2338                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2339                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2340                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2341                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2342                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2343                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2344                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2345                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2346                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2347                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2348                 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2349
2350                 ast_cli_register(&show_mancmd_cli);
2351                 ast_cli_register(&show_mancmds_cli);
2352                 ast_cli_register(&show_manconn_cli);
2353                 ast_cli_register(&show_maneventq_cli);
2354                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2355                 registered = 1;
2356                 /* Append placeholder event so master_eventq never runs dry */
2357                 append_event("Event: Placeholder\r\n\r\n", 0);
2358         }
2359         portno = DEFAULT_MANAGER_PORT;
2360         displayconnects = 1;
2361         cfg = ast_config_load("manager.conf");
2362         if (!cfg) {
2363                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
2364                 return 0;
2365         }
2366         val = ast_variable_retrieve(cfg, "general", "enabled");
2367         if (val)
2368                 enabled = ast_true(val);
2369
2370         val = ast_variable_retrieve(cfg, "general", "block-sockets");
2371         if(val)
2372                 block_sockets = ast_true(val);
2373
2374         val = ast_variable_retrieve(cfg, "general", "webenabled");
2375         if (val)
2376                 webenabled = ast_true(val);
2377
2378         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2379                 if (sscanf(val, "%d", &portno) != 1) {
2380                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2381                         portno = DEFAULT_MANAGER_PORT;
2382                 }
2383         }
2384
2385         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2386                 displayconnects = ast_true(val);
2387
2388         if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2389                 timestampevents = ast_true(val);
2390
2391         if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2392                 newhttptimeout = atoi(val);
2393
2394         memset(&ba, 0, sizeof(ba));
2395         ba.sin_family = AF_INET;
2396         ba.sin_port = htons(portno);
2397
2398         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2399                 if (!inet_aton(val, &ba.sin_addr)) { 
2400                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2401                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2402                 }
2403         }
2404         
2405         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2406 #if 0
2407                 /* Can't be done yet */
2408                 close(asock);
2409                 asock = -1;
2410 #else
2411                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2412 #endif
2413         }
2414         ast_config_destroy(cfg);
2415         
2416         if (webenabled && enabled) {
2417                 if (!webregged) {
2418                         ast_http_uri_link(&rawmanuri);
2419                         ast_http_uri_link(&manageruri);
2420                         ast_http_uri_link(&managerxmluri);
2421                         webregged = 1;
2422                 }
2423         } else {
2424                 if (webregged) {
2425                         ast_http_uri_unlink(&rawmanuri);
2426                         ast_http_uri_unlink(&manageruri);
2427                         ast_http_uri_unlink(&managerxmluri);
2428                         webregged = 0;
2429                 }
2430         }
2431
2432         if (newhttptimeout > 0)
2433                 httptimeout = newhttptimeout;
2434         
2435         /* If not enabled, do nothing */
2436         if (!enabled) {
2437                 return 0;
2438         }
2439         if (asock < 0) {
2440                 asock = socket(AF_INET, SOCK_STREAM, 0);
2441                 if (asock < 0) {
2442                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2443                         return -1;
2444                 }
2445                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2446                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2447                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2448                         close(asock);
2449                         asock = -1;
2450                         return -1;
2451                 }
2452                 if (listen(asock, 2)) {
2453                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2454                         close(asock);
2455                         asock = -1;
2456                         return -1;
2457                 }
2458                 flags = fcntl(asock, F_GETFL);
2459                 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2460                 if (option_verbose)
2461                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2462                 ast_pthread_create(&t, NULL, accept_thread, NULL);
2463         }
2464         return 0;
2465 }
2466
2467 int reload_manager(void)
2468 {
2469         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2470         return init_manager();
2471 }