Formatting fixes
[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) += 4;
237                         *maxlen -= 4;
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                 s = malloc(sizeof(struct mansession));
1913                 if (!s) {
1914                         ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
1915                         continue;
1916                 } 
1917                 memset(s, 0, sizeof(struct mansession));
1918                 memcpy(&s->sin, &sin, sizeof(sin));
1919                 s->writetimeout = 100;
1920                 s->waiting_thread = AST_PTHREADT_NULL;
1921
1922                 if (!block_sockets) {
1923                         /* For safety, make sure socket is non-blocking */
1924                         flags = fcntl(as, F_GETFL);
1925                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
1926                 }
1927                 ast_mutex_init(&s->__lock);
1928                 s->fd = as;
1929                 s->send_events = -1;
1930                 ast_mutex_lock(&sessionlock);
1931                 num_sessions++;
1932                 s->next = sessions;
1933                 sessions = s;
1934                 /* Find the last place in the master event queue and hook ourselves
1935                    in there */
1936                 s->eventq = master_eventq;
1937                 while(s->eventq->next)
1938                         s->eventq = s->eventq->next;
1939                 ast_mutex_lock(&s->eventq->lock);
1940                 s->eventq->usecount++;
1941                 ast_mutex_unlock(&s->eventq->lock);
1942                 ast_mutex_unlock(&sessionlock);
1943                 if (ast_pthread_create(&s->t, &attr, session_do, s))
1944                         destroy_session(s);
1945         }
1946         pthread_attr_destroy(&attr);
1947         return NULL;
1948 }
1949
1950 static int append_event(const char *str, int category)
1951 {
1952         struct eventqent *tmp, *prev = NULL;
1953         tmp = malloc(sizeof(struct eventqent) + strlen(str));
1954         if (tmp) {
1955                 ast_mutex_init(&tmp->lock);
1956                 tmp->next = NULL;
1957                 tmp->category = category;
1958                 strcpy(tmp->eventdata, str);
1959                 if (master_eventq) {
1960                         prev = master_eventq;
1961                         while (prev->next) 
1962                                 prev = prev->next;
1963                         prev->next = tmp;
1964                 } else {
1965                         master_eventq = tmp;
1966                 }
1967                 tmp->usecount = num_sessions;
1968                 return 0;
1969         }
1970         return -1;
1971 }
1972
1973 /*! \brief  manager_event: Send AMI event to client */
1974 int manager_event(int category, const char *event, const char *fmt, ...)
1975 {
1976         struct mansession *s;
1977         char auth[80];
1978         char tmp[4096] = "";
1979         char *tmp_next = tmp;
1980         size_t tmp_left = sizeof(tmp) - 2;
1981         va_list ap;
1982         struct timeval now;
1983
1984         /* Abort if there aren't any manager sessions */
1985         if (!num_sessions)
1986                 return 0;
1987
1988         ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
1989                          event, authority_to_str(category, auth, sizeof(auth)));
1990         if (timestampevents) {
1991                 now = ast_tvnow();
1992                 ast_build_string(&tmp_next, &tmp_left, "Timestamp: %ld.%06lu\r\n",
1993                                  now.tv_sec, (unsigned long) now.tv_usec);
1994         }
1995         va_start(ap, fmt);
1996         ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
1997         va_end(ap);
1998         *tmp_next++ = '\r';
1999         *tmp_next++ = '\n';
2000         *tmp_next = '\0';
2001         
2002         ast_mutex_lock(&sessionlock);
2003         /* Append even to master list and wake up any sleeping sessions */
2004         append_event(tmp, category);
2005         for (s = sessions; s; s = s->next) {
2006                 ast_mutex_lock(&s->__lock);
2007                 if (s->waiting_thread != AST_PTHREADT_NULL)
2008                         pthread_kill(s->waiting_thread, SIGURG);
2009                 ast_mutex_unlock(&s->__lock);
2010         }
2011         ast_mutex_unlock(&sessionlock);
2012
2013         return 0;
2014 }
2015
2016 int ast_manager_unregister( char *action ) 
2017 {
2018         struct manager_action *cur = first_action, *prev = first_action;
2019
2020         ast_mutex_lock(&actionlock);
2021         while (cur) {
2022                 if (!strcasecmp(action, cur->action)) {
2023                         prev->next = cur->next;
2024                         free(cur);
2025                         if (option_verbose > 1) 
2026                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2027                         ast_mutex_unlock(&actionlock);
2028                         return 0;
2029                 }
2030                 prev = cur;
2031                 cur = cur->next;
2032         }
2033         ast_mutex_unlock(&actionlock);
2034         return 0;
2035 }
2036
2037 static int manager_state_cb(char *context, char *exten, int state, void *data)
2038 {
2039         /* Notify managers of change */
2040         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2041         return 0;
2042 }
2043
2044 static int ast_manager_register_struct(struct manager_action *act)
2045 {
2046         struct manager_action *cur = first_action, *prev = NULL;
2047         int ret;
2048
2049         ast_mutex_lock(&actionlock);
2050         while (cur) { /* Walk the list of actions */
2051                 ret = strcasecmp(cur->action, act->action);
2052                 if (ret == 0) {
2053                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2054                         ast_mutex_unlock(&actionlock);
2055                         return -1;
2056                 } else if (ret > 0) {
2057                         /* Insert these alphabetically */
2058                         if (prev) {
2059                                 act->next = prev->next;
2060                                 prev->next = act;
2061                         } else {
2062                                 act->next = first_action;
2063                                 first_action = act;
2064                         }
2065                         break;
2066                 }
2067                 prev = cur; 
2068                 cur = cur->next;
2069         }
2070         
2071         if (!cur) {
2072                 if (prev)
2073                         prev->next = act;
2074                 else
2075                         first_action = act;
2076                 act->next = NULL;
2077         }
2078
2079         if (option_verbose > 1) 
2080                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2081         ast_mutex_unlock(&actionlock);
2082         return 0;
2083 }
2084
2085 /*! \brief register a new command with manager, including online help. This is 
2086         the preferred way to register a manager command */
2087 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2088 {
2089         struct manager_action *cur;
2090
2091         cur = malloc(sizeof(struct manager_action));
2092         if (!cur) {
2093                 ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
2094                 return -1;
2095         }
2096         cur->action = action;
2097         cur->authority = auth;
2098         cur->func = func;
2099         cur->synopsis = synopsis;
2100         cur->description = description;
2101         cur->next = NULL;
2102
2103         ast_manager_register_struct(cur);
2104
2105         return 0;
2106 }
2107 /*! @}
2108  END Doxygen group */
2109
2110 static struct mansession *find_session(unsigned long ident)
2111 {
2112         struct mansession *s;
2113         ast_mutex_lock(&sessionlock);
2114         s = sessions;
2115         while (s) {
2116                 ast_mutex_lock(&s->__lock);
2117                 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2118                         s->inuse++;
2119                         break;
2120                 }
2121                 ast_mutex_unlock(&s->__lock);
2122                 s = s->next;
2123         }
2124         ast_mutex_unlock(&sessionlock);
2125         return s;
2126 }
2127
2128
2129 static void vars2msg(struct message *m, struct ast_variable *vars)
2130 {
2131         int x;
2132         for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2133                 if (!vars)
2134                         break;
2135                 m->hdrcount = x + 1;
2136                 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2137         }
2138 }
2139
2140 #define FORMAT_RAW      0
2141 #define FORMAT_HTML     1
2142 #define FORMAT_XML      2
2143
2144 static char *contenttype[] = { "plain", "html", "xml" };
2145
2146 static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2147 {
2148         struct mansession *s = NULL;
2149         unsigned long ident = 0;
2150         char workspace[256];
2151         char cookie[128];
2152         char iabuf[INET_ADDRSTRLEN];
2153         size_t len = sizeof(workspace);
2154         int blastaway = 0;
2155         char *c = workspace;
2156         char *retval = NULL;
2157         struct message m;
2158         struct ast_variable *v;
2159
2160         v = params;
2161         while (v) {
2162                 if (!strcasecmp(v->name, "mansession_id")) {
2163                         sscanf(v->value, "%lx", &ident);
2164                         break;
2165                 }
2166                 v = v->next;
2167         }
2168         s = find_session(ident);
2169
2170         if (!s) {
2171                 /* Create new session */
2172                 s = calloc(1, sizeof(struct mansession));
2173                 memcpy(&s->sin, requestor, sizeof(s->sin));
2174                 s->fd = -1;
2175                 s->waiting_thread = AST_PTHREADT_NULL;
2176                 s->send_events = 0;
2177                 ast_mutex_init(&s->__lock);
2178                 ast_mutex_lock(&s->__lock);
2179                 ast_mutex_lock(&sessionlock);
2180                 s->inuse = 1;
2181                 s->managerid = rand() | (unsigned long)s;
2182                 s->next = sessions;
2183                 sessions = s;
2184                 num_sessions++;
2185                 /* Hook into the last spot in the event queue */
2186                 s->eventq = master_eventq;
2187                 while (s->eventq->next)
2188                         s->eventq = s->eventq->next;
2189                 ast_mutex_lock(&s->eventq->lock);
2190                 s->eventq->usecount++;
2191                 ast_mutex_unlock(&s->eventq->lock);
2192                 ast_mutex_unlock(&sessionlock);
2193         }
2194
2195         /* Reset HTTP timeout.  If we're not yet authenticated, keep it extremely short */
2196         time(&s->sessiontimeout);
2197         if (!s->authenticated && (httptimeout > 5))
2198                 s->sessiontimeout += 5;
2199         else
2200                 s->sessiontimeout += httptimeout;
2201         ast_mutex_unlock(&s->__lock);
2202         
2203         memset(&m, 0, sizeof(m));
2204         if (s) {
2205                 char tmp[80];
2206                 ast_build_string(&c, &len, "Content-type: text/%s\n", contenttype[format]);
2207                 sprintf(tmp, "%08lx", s->managerid);
2208                 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2209                 if (format == FORMAT_HTML)
2210                         ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Test Interface</title>");
2211                 vars2msg(&m, params);
2212                 if (format == FORMAT_XML) {
2213                         ast_build_string(&c, &len, "<ajax-response>\n");
2214                 } else if (format == FORMAT_HTML) {
2215                         ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2216                         ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
2217                 }
2218                 if (process_message(s, &m)) {
2219                         if (s->authenticated) {
2220                                 if (option_verbose > 1) {
2221                                         if (displayconnects) 
2222                                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));    
2223                                 }
2224                                 ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2225                         } else {
2226                                 if (option_verbose > 1) {
2227                                         if (displayconnects)
2228                                                 ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2229                                 }
2230                                 ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2231                         }
2232                         s->needdestroy = 1;
2233                 }
2234                 if (s->outputstr) {
2235                         char *tmp;
2236                         if (format == FORMAT_XML)
2237                                 tmp = xml_translate(s->outputstr, params);
2238                         else if (format == FORMAT_HTML)
2239                                 tmp = html_translate(s->outputstr);
2240                         else
2241                                 tmp = s->outputstr;
2242                         if (tmp) {
2243                                 retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2244                                 if (retval) {
2245                                         strcpy(retval, workspace);
2246                                         strcpy(retval + strlen(retval), tmp);
2247                                         c = retval + strlen(retval);
2248                                         len = 120;
2249                                 }
2250                                 free(tmp);
2251                         }
2252                         if (tmp != s->outputstr)
2253                                 free(s->outputstr);
2254                         s->outputstr = NULL;
2255                 }
2256                 /* Still okay because c would safely be pointing to workspace even
2257                    if retval failed to allocate above */
2258                 if (format == FORMAT_XML) {
2259                         ast_build_string(&c, &len, "</ajax-response>\n");
2260                 } else if (format == FORMAT_HTML)
2261                         ast_build_string(&c, &len, "</table></body>\r\n");
2262         } else {
2263                 *status = 500;
2264                 *title = strdup("Server Error");
2265         }
2266         ast_mutex_lock(&s->__lock);
2267         if (s->needdestroy) {
2268                 if (s->inuse == 1) {
2269                         ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2270                         blastaway = 1;
2271                 } else {
2272                         ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2273                         if (s->waiting_thread != AST_PTHREADT_NULL)
2274                                 pthread_kill(s->waiting_thread, SIGURG);
2275                         s->inuse--;
2276                 }
2277         } else
2278                 s->inuse--;
2279         ast_mutex_unlock(&s->__lock);
2280         
2281         if (blastaway)
2282                 destroy_session(s);
2283         if (*status != 200)
2284                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); 
2285         return retval;
2286 }
2287
2288 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2289 {
2290         return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2291 }
2292
2293 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2294 {
2295         return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2296 }
2297
2298 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2299 {
2300         return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2301 }
2302
2303 struct ast_http_uri rawmanuri = {
2304         .description = "Raw HTTP Manager Event Interface",
2305         .uri = "rawman",
2306         .has_subtree = 0,
2307         .callback = rawman_http_callback,
2308 };
2309
2310 struct ast_http_uri manageruri = {
2311         .description = "HTML Manager Event Interface",
2312         .uri = "manager",
2313         .has_subtree = 0,
2314         .callback = manager_http_callback,
2315 };
2316
2317 struct ast_http_uri managerxmluri = {
2318         .description = "XML Manager Event Interface",
2319         .uri = "mxml",
2320         .has_subtree = 0,
2321         .callback = mxml_http_callback,
2322 };
2323
2324 static int registered = 0;
2325 static int webregged = 0;
2326
2327 int init_manager(void)
2328 {
2329         struct ast_config *cfg;
2330         char *val;
2331         int oldportno = portno;
2332         static struct sockaddr_in ba;
2333         int x = 1;
2334         int flags;
2335         int webenabled = 0;
2336         int newhttptimeout = 60;
2337         if (!registered) {
2338                 /* Register default actions */
2339                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2340                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2341                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2342                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2343                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2344                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2345                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2346                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2347                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2348                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2349                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2350                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2351                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2352                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2353                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2354                 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2355                 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2356
2357                 ast_cli_register(&show_mancmd_cli);
2358                 ast_cli_register(&show_mancmds_cli);
2359                 ast_cli_register(&show_manconn_cli);
2360                 ast_cli_register(&show_maneventq_cli);
2361                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2362                 registered = 1;
2363                 /* Append placeholder event so master_eventq never runs dry */
2364                 append_event("Event: Placeholder\r\n\r\n", 0);
2365         }
2366         portno = DEFAULT_MANAGER_PORT;
2367         displayconnects = 1;
2368         cfg = ast_config_load("manager.conf");
2369         if (!cfg) {
2370                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
2371                 return 0;
2372         }
2373         val = ast_variable_retrieve(cfg, "general", "enabled");
2374         if (val)
2375                 enabled = ast_true(val);
2376
2377         val = ast_variable_retrieve(cfg, "general", "block-sockets");
2378         if (val)
2379                 block_sockets = ast_true(val);
2380
2381         val = ast_variable_retrieve(cfg, "general", "webenabled");
2382         if (val)
2383                 webenabled = ast_true(val);
2384
2385         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2386                 if (sscanf(val, "%d", &portno) != 1) {
2387                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2388                         portno = DEFAULT_MANAGER_PORT;
2389                 }
2390         }
2391
2392         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2393                 displayconnects = ast_true(val);
2394
2395         if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2396                 timestampevents = ast_true(val);
2397
2398         if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2399                 newhttptimeout = atoi(val);
2400
2401         memset(&ba, 0, sizeof(ba));
2402         ba.sin_family = AF_INET;
2403         ba.sin_port = htons(portno);
2404
2405         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2406                 if (!inet_aton(val, &ba.sin_addr)) { 
2407                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2408                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2409                 }
2410         }
2411         
2412
2413         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2414 #if 0
2415                 /* Can't be done yet */
2416                 close(asock);
2417                 asock = -1;
2418 #else
2419                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2420 #endif
2421         }
2422         ast_config_destroy(cfg);
2423         
2424         if (webenabled && enabled) {
2425                 if (!webregged) {
2426                         ast_http_uri_link(&rawmanuri);
2427                         ast_http_uri_link(&manageruri);
2428                         ast_http_uri_link(&managerxmluri);
2429                         webregged = 1;
2430                 }
2431         } else {
2432                 if (webregged) {
2433                         ast_http_uri_unlink(&rawmanuri);
2434                         ast_http_uri_unlink(&manageruri);
2435                         ast_http_uri_unlink(&managerxmluri);
2436                         webregged = 0;
2437                 }
2438         }
2439
2440         if (newhttptimeout > 0)
2441                 httptimeout = newhttptimeout;
2442
2443         /* If not enabled, do nothing */
2444         if (!enabled)
2445                 return 0;
2446
2447         if (asock < 0) {
2448                 asock = socket(AF_INET, SOCK_STREAM, 0);
2449                 if (asock < 0) {
2450                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2451                         return -1;
2452                 }
2453                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2454                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2455                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2456                         close(asock);
2457                         asock = -1;
2458                         return -1;
2459                 }
2460                 if (listen(asock, 2)) {
2461                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2462                         close(asock);
2463                         asock = -1;
2464                         return -1;
2465                 }
2466                 flags = fcntl(asock, F_GETFL);
2467                 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2468                 if (option_verbose)
2469                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2470                 ast_pthread_create(&t, NULL, accept_thread, NULL);
2471         }
2472         return 0;
2473 }
2474
2475 int reload_manager(void)
2476 {
2477         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
2478         return init_manager();
2479 }