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