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