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