0b87cdb554353edeadfd0d48a6c7f6b74ec4644e
[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 list commands\n"
556 "       Prints a listing of all the available Asterisk manager interface commands.\n";
557
558 static char showmanconn_help[] =
559 "Usage: manager list 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 list 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 list 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", "list", "commands", NULL },
583         handle_showmancmds, "List manager interface commands",
584         showmancmds_help },
585
586         { { "manager", "list", "connected", NULL },
587         handle_showmanconn, "List connected manager interface users",
588         showmanconn_help },
589
590         { { "manager", "list", "eventq", NULL },
591         handle_showmaneventq, "List manager interface queued events",
592         showmaneventq_help },
593
594         { { "manager", "list", "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         if (!ast_strlen_zero(textmsg)) {
1451                 astman_send_error(s, m, "No Message specified");
1452                 return 0;
1453         }
1454
1455         c = ast_get_channel_by_name_locked(name);
1456         if (!c) {
1457                 astman_send_error(s, m, "No such channel");
1458                 return 0;
1459         }
1460
1461         ast_mutex_unlock(&c->lock);
1462         res = ast_sendtext(c, textmsg);
1463         if (res > 0) {
1464                 astman_send_ack(s, m, "Success");
1465         } else {
1466                 astman_send_error(s, m, "Failure");
1467         }
1468         return res;
1469 }
1470
1471 static char mandescr_redirect[] =
1472 "Description: Redirect (transfer) a call.\n"
1473 "Variables: (Names marked with * are required)\n"
1474 "       *Channel: Channel to redirect\n"
1475 "       ExtraChannel: Second call leg to transfer (optional)\n"
1476 "       *Exten: Extension to transfer to\n"
1477 "       *Context: Context to transfer to\n"
1478 "       *Priority: Priority to transfer to\n"
1479 "       ActionID: Optional Action id for message matching.\n";
1480
1481 /*! \brief  action_redirect: The redirect manager command */
1482 static int action_redirect(struct mansession *s, struct message *m)
1483 {
1484         char *name = astman_get_header(m, "Channel");
1485         char *name2 = astman_get_header(m, "ExtraChannel");
1486         char *exten = astman_get_header(m, "Exten");
1487         char *context = astman_get_header(m, "Context");
1488         char *priority = astman_get_header(m, "Priority");
1489         struct ast_channel *chan, *chan2 = NULL;
1490         int pi = 0;
1491         int res;
1492
1493         if (ast_strlen_zero(name)) {
1494                 astman_send_error(s, m, "Channel not specified");
1495                 return 0;
1496         }
1497         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1498                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1499                         astman_send_error(s, m, "Invalid priority\n");
1500                         return 0;
1501                 }
1502         }
1503         /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
1504         chan = ast_get_channel_by_name_locked(name);
1505         if (!chan) {
1506                 char buf[BUFSIZ];
1507                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
1508                 astman_send_error(s, m, buf);
1509                 return 0;
1510         }
1511         if (!ast_strlen_zero(name2))
1512                 chan2 = ast_get_channel_by_name_locked(name2);
1513         res = ast_async_goto(chan, context, exten, pi);
1514         if (!res) {
1515                 if (!ast_strlen_zero(name2)) {
1516                         if (chan2)
1517                                 res = ast_async_goto(chan2, context, exten, pi);
1518                         else
1519                                 res = -1;
1520                         if (!res)
1521                                 astman_send_ack(s, m, "Dual Redirect successful");
1522                         else
1523                                 astman_send_error(s, m, "Secondary redirect failed");
1524                 } else
1525                         astman_send_ack(s, m, "Redirect successful");
1526         } else
1527                 astman_send_error(s, m, "Redirect failed");
1528         if (chan)
1529                 ast_channel_unlock(chan);
1530         if (chan2)
1531                 ast_channel_unlock(chan2);
1532         return 0;
1533 }
1534
1535 static char mandescr_command[] =
1536 "Description: Run a CLI command.\n"
1537 "Variables: (Names marked with * are required)\n"
1538 "       *Command: Asterisk CLI command to run\n"
1539 "       ActionID: Optional Action id for message matching.\n";
1540
1541 /*! \brief  Manager command "command" - execute CLI command */
1542 static int action_command(struct mansession *s, struct message *m)
1543 {
1544         char *cmd = astman_get_header(m, "Command");
1545         char *id = astman_get_header(m, "ActionID");
1546         astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
1547         if (!ast_strlen_zero(id))
1548                 astman_append(s, "ActionID: %s\r\n", id);
1549         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
1550         ast_cli_command(s->fd, cmd);
1551         astman_append(s, "--END COMMAND--\r\n\r\n");
1552         return 0;
1553 }
1554
1555 /* helper function for originate */
1556 struct fast_originate_helper {
1557         char tech[AST_MAX_MANHEADER_LEN];
1558         char data[AST_MAX_MANHEADER_LEN];
1559         int timeout;
1560         char app[AST_MAX_APP];
1561         char appdata[AST_MAX_MANHEADER_LEN];
1562         char cid_name[AST_MAX_MANHEADER_LEN];
1563         char cid_num[AST_MAX_MANHEADER_LEN];
1564         char context[AST_MAX_CONTEXT];
1565         char exten[AST_MAX_EXTENSION];
1566         char idtext[AST_MAX_MANHEADER_LEN];
1567         char account[AST_MAX_ACCOUNT_CODE];
1568         int priority;
1569         struct ast_variable *vars;
1570 };
1571
1572 static void *fast_originate(void *data)
1573 {
1574         struct fast_originate_helper *in = data;
1575         int res;
1576         int reason = 0;
1577         struct ast_channel *chan = NULL;
1578
1579         if (!ast_strlen_zero(in->app)) {
1580                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
1581                         S_OR(in->cid_num, NULL),
1582                         S_OR(in->cid_name, NULL),
1583                         in->vars, in->account, &chan);
1584         } else {
1585                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
1586                         S_OR(in->cid_num, NULL),
1587                         S_OR(in->cid_name, NULL),
1588                         in->vars, in->account, &chan);
1589         }
1590
1591         /* Tell the manager what happened with the channel */
1592         manager_event(EVENT_FLAG_CALL,
1593                 res ? "OriginateFailure" : "OriginateSuccess",
1594                 "%s"
1595                 "Channel: %s/%s\r\n"
1596                 "Context: %s\r\n"
1597                 "Exten: %s\r\n"
1598                 "Reason: %d\r\n"
1599                 "Uniqueid: %s\r\n"
1600                 "CallerIDNum: %s\r\n"
1601                 "CallerIDName: %s\r\n",
1602                 in->idtext, in->tech, in->data, in->context, in->exten, reason,
1603                 chan ? chan->uniqueid : "<null>",
1604                 S_OR(in->cid_num, "<unknown>"),
1605                 S_OR(in->cid_name, "<unknown>")
1606                 );
1607
1608         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
1609         if (chan)
1610                 ast_channel_unlock(chan);
1611         free(in);
1612         return NULL;
1613 }
1614
1615 static char mandescr_originate[] =
1616 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
1617 "  Application/Data\n"
1618 "Variables: (Names marked with * are required)\n"
1619 "       *Channel: Channel name to call\n"
1620 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
1621 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
1622 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
1623 "       Application: Application to use\n"
1624 "       Data: Data to use (requires 'Application')\n"
1625 "       Timeout: How long to wait for call to be answered (in ms)\n"
1626 "       CallerID: Caller ID to be set on the outgoing channel\n"
1627 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
1628 "       Account: Account code\n"
1629 "       Async: Set to 'true' for fast origination\n";
1630
1631 static int action_originate(struct mansession *s, struct message *m)
1632 {
1633         char *name = astman_get_header(m, "Channel");
1634         char *exten = astman_get_header(m, "Exten");
1635         char *context = astman_get_header(m, "Context");
1636         char *priority = astman_get_header(m, "Priority");
1637         char *timeout = astman_get_header(m, "Timeout");
1638         char *callerid = astman_get_header(m, "CallerID");
1639         char *account = astman_get_header(m, "Account");
1640         char *app = astman_get_header(m, "Application");
1641         char *appdata = astman_get_header(m, "Data");
1642         char *async = astman_get_header(m, "Async");
1643         char *id = astman_get_header(m, "ActionID");
1644         struct ast_variable *vars = astman_get_variables(m);
1645         char *tech, *data;
1646         char *l = NULL, *n = NULL;
1647         int pi = 0;
1648         int res;
1649         int to = 30000;
1650         int reason = 0;
1651         char tmp[256];
1652         char tmp2[256];
1653
1654         pthread_t th;
1655         pthread_attr_t attr;
1656         if (!name) {
1657                 astman_send_error(s, m, "Channel not specified");
1658                 return 0;
1659         }
1660         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1661                 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
1662                         astman_send_error(s, m, "Invalid priority\n");
1663                         return 0;
1664                 }
1665         }
1666         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1667                 astman_send_error(s, m, "Invalid timeout\n");
1668                 return 0;
1669         }
1670         ast_copy_string(tmp, name, sizeof(tmp));
1671         tech = tmp;
1672         data = strchr(tmp, '/');
1673         if (!data) {
1674                 astman_send_error(s, m, "Invalid channel\n");
1675                 return 0;
1676         }
1677         *data++ = '\0';
1678         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1679         ast_callerid_parse(tmp2, &n, &l);
1680         if (n) {
1681                 if (ast_strlen_zero(n))
1682                         n = NULL;
1683         }
1684         if (l) {
1685                 ast_shrink_phone_number(l);
1686                 if (ast_strlen_zero(l))
1687                         l = NULL;
1688         }
1689         if (ast_true(async)) {
1690                 struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
1691                 if (!fast) {
1692                         res = -1;
1693                 } else {
1694                         if (!ast_strlen_zero(id))
1695                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1696                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1697                         ast_copy_string(fast->data, data, sizeof(fast->data));
1698                         ast_copy_string(fast->app, app, sizeof(fast->app));
1699                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1700                         if (l)
1701                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1702                         if (n)
1703                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1704                         fast->vars = vars;
1705                         ast_copy_string(fast->context, context, sizeof(fast->context));
1706                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1707                         ast_copy_string(fast->account, account, sizeof(fast->account));
1708                         fast->timeout = to;
1709                         fast->priority = pi;
1710                         pthread_attr_init(&attr);
1711                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1712                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1713                                 res = -1;
1714                         } else {
1715                                 res = 0;
1716                         }
1717                 }
1718         } else if (!ast_strlen_zero(app)) {
1719                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
1720         } else {
1721                 if (exten && context && pi)
1722                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
1723                 else {
1724                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1725                         return 0;
1726                 }
1727         }
1728         if (!res)
1729                 astman_send_ack(s, m, "Originate successfully queued");
1730         else
1731                 astman_send_error(s, m, "Originate failed");
1732         return 0;
1733 }
1734
1735 /*! \brief Help text for manager command mailboxstatus
1736  */
1737 static char mandescr_mailboxstatus[] =
1738 "Description: Checks a voicemail account for status.\n"
1739 "Variables: (Names marked with * are required)\n"
1740 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1741 "       ActionID: Optional ActionID for message matching.\n"
1742 "Returns number of messages.\n"
1743 "       Message: Mailbox Status\n"
1744 "       Mailbox: <mailboxid>\n"
1745 "       Waiting: <count>\n"
1746 "\n";
1747
1748 static int action_mailboxstatus(struct mansession *s, struct message *m)
1749 {
1750         char *mailbox = astman_get_header(m, "Mailbox");
1751         int ret;
1752
1753         if (ast_strlen_zero(mailbox)) {
1754                 astman_send_error(s, m, "Mailbox not specified");
1755                 return 0;
1756         }
1757         ret = ast_app_has_voicemail(mailbox, NULL);
1758         astman_start_ack(s, m);
1759         astman_append(s, "Message: Mailbox Status\r\n"
1760                          "Mailbox: %s\r\n"
1761                          "Waiting: %d\r\n\r\n", mailbox, ret);
1762         return 0;
1763 }
1764
1765 static char mandescr_mailboxcount[] =
1766 "Description: Checks a voicemail account for new messages.\n"
1767 "Variables: (Names marked with * are required)\n"
1768 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1769 "       ActionID: Optional ActionID for message matching.\n"
1770 "Returns number of new and old messages.\n"
1771 "       Message: Mailbox Message Count\n"
1772 "       Mailbox: <mailboxid>\n"
1773 "       NewMessages: <count>\n"
1774 "       OldMessages: <count>\n"
1775 "\n";
1776 static int action_mailboxcount(struct mansession *s, struct message *m)
1777 {
1778         char *mailbox = astman_get_header(m, "Mailbox");
1779         int newmsgs = 0, oldmsgs = 0;
1780
1781         if (ast_strlen_zero(mailbox)) {
1782                 astman_send_error(s, m, "Mailbox not specified");
1783                 return 0;
1784         }
1785         ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
1786         astman_start_ack(s, m);
1787         astman_append(s,   "Message: Mailbox Message Count\r\n"
1788                            "Mailbox: %s\r\n"
1789                            "NewMessages: %d\r\n"
1790                            "OldMessages: %d\r\n"
1791                            "\r\n",
1792                            mailbox, newmsgs, oldmsgs);
1793         return 0;
1794 }
1795
1796 static char mandescr_extensionstate[] =
1797 "Description: Report the extension state for given extension.\n"
1798 "  If the extension has a hint, will use devicestate to check\n"
1799 "  the status of the device connected to the extension.\n"
1800 "Variables: (Names marked with * are required)\n"
1801 "       *Exten: Extension to check state on\n"
1802 "       *Context: Context for extension\n"
1803 "       ActionId: Optional ID for this transaction\n"
1804 "Will return an \"Extension Status\" message.\n"
1805 "The response will include the hint for the extension and the status.\n";
1806
1807 static int action_extensionstate(struct mansession *s, struct message *m)
1808 {
1809         char *exten = astman_get_header(m, "Exten");
1810         char *context = astman_get_header(m, "Context");
1811         char hint[256] = "";
1812         int status;
1813         if (ast_strlen_zero(exten)) {
1814                 astman_send_error(s, m, "Extension not specified");
1815                 return 0;
1816         }
1817         if (ast_strlen_zero(context))
1818                 context = "default";
1819         status = ast_extension_state(NULL, context, exten);
1820         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1821         astman_start_ack(s, m);
1822         astman_append(s,   "Message: Extension Status\r\n"
1823                            "Exten: %s\r\n"
1824                            "Context: %s\r\n"
1825                            "Hint: %s\r\n"
1826                            "Status: %d\r\n\r\n",
1827                            exten, context, hint, status);
1828         return 0;
1829 }
1830
1831 static char mandescr_timeout[] =
1832 "Description: Hangup a channel after a certain time.\n"
1833 "Variables: (Names marked with * are required)\n"
1834 "       *Channel: Channel name to hangup\n"
1835 "       *Timeout: Maximum duration of the call (sec)\n"
1836 "Acknowledges set time with 'Timeout Set' message\n";
1837
1838 static int action_timeout(struct mansession *s, struct message *m)
1839 {
1840         struct ast_channel *c;
1841         char *name = astman_get_header(m, "Channel");
1842         int timeout = atoi(astman_get_header(m, "Timeout"));
1843
1844         if (ast_strlen_zero(name)) {
1845                 astman_send_error(s, m, "No channel specified");
1846                 return 0;
1847         }
1848         if (!timeout) {
1849                 astman_send_error(s, m, "No timeout specified");
1850                 return 0;
1851         }
1852         c = ast_get_channel_by_name_locked(name);
1853         if (!c) {
1854                 astman_send_error(s, m, "No such channel");
1855                 return 0;
1856         }
1857         ast_channel_setwhentohangup(c, timeout);
1858         ast_channel_unlock(c);
1859         astman_send_ack(s, m, "Timeout Set");
1860         return 0;
1861 }
1862
1863 static int process_events(struct mansession *s)
1864 {
1865         int ret = 0;
1866
1867         ast_mutex_lock(&s->__lock);
1868         if (s->fd > -1) {
1869                 struct eventqent *eqe;
1870
1871                 while ( (eqe = NEW_EVENT(s)) ) {
1872                         ref_event(eqe);
1873                         if ((s->authenticated && (s->readperm & eqe->category) == eqe->category) &&
1874                             ((s->send_events & eqe->category) == eqe->category)) {
1875                                 if (!ret && ast_carefulwrite(s->fd, eqe->eventdata,
1876                                                 strlen(eqe->eventdata), s->writetimeout) < 0)
1877                                         ret = -1;
1878                         }
1879                         s->last_ev = unref_event(s->last_ev);
1880                 }
1881         }
1882         ast_mutex_unlock(&s->__lock);
1883         return ret;
1884 }
1885
1886 static char mandescr_userevent[] =
1887 "Description: Send an event to manager sessions.\n"
1888 "Variables: (Names marked with * are required)\n"
1889 "       *UserEvent: EventStringToSend\n"
1890 "       Header1: Content1\n"
1891 "       HeaderN: ContentN\n";
1892
1893 static int action_userevent(struct mansession *s, struct message *m)
1894 {
1895         char *event = astman_get_header(m, "UserEvent");
1896         char body[2048] = "";
1897         int x, bodylen = 0;
1898         for (x = 0; x < m->hdrcount; x++) {
1899                 if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
1900                         ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
1901                         bodylen += strlen(m->headers[x]);
1902                         ast_copy_string(body + bodylen, "\r\n", 3);
1903                         bodylen += 2;
1904                 }
1905         }
1906
1907         manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
1908         return 0;
1909 }
1910
1911 /*
1912  * Done with the action handlers here, we start with the code in charge
1913  * of accepting connections and serving them.
1914  * accept_thread() forks a new thread for each connection, session_do(),
1915  * which in turn calls get_input() repeatedly until a full message has
1916  * been accumulated, and then invokes process_message() to pass it to
1917  * the appropriate handler.
1918  */
1919
1920 /*
1921  * Process an AMI message, performing desired action.
1922  * Return 0 on success, -1 on error that require the session to be destroyed.
1923  */
1924 static int process_message(struct mansession *s, struct message *m)
1925 {
1926         char action[80] = "";
1927         int ret = 0;
1928         struct manager_action *tmp;
1929
1930         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1931         if (option_debug)
1932                 ast_log(LOG_DEBUG, "Manager received command '%s'\n", action);
1933
1934         if (ast_strlen_zero(action)) {
1935                 astman_send_error(s, m, "Missing action in request");
1936                 return 0;
1937         }
1938
1939         /* XXX should we protect the list navigation ? */
1940         for (tmp = first_action ; tmp; tmp = tmp->next) {
1941                 if (!strcasecmp(action, tmp->action)) {
1942                         if ((s->writeperm & tmp->authority) == tmp->authority) {
1943                                 if (tmp->func(s, m))    /* error */
1944                                         return -1;
1945                         } else {
1946                                 astman_send_error(s, m, "Permission denied");
1947                         }
1948                         break;
1949                 }
1950         }
1951         if (!tmp)
1952                 astman_send_error(s, m, "Invalid/unknown command");
1953         if (ret)
1954                 return ret;
1955         return process_events(s);
1956 }
1957
1958 /*!
1959  * Read one full line (including crlf) from the manager socket.
1960  * \r\n is the only valid terminator for the line.
1961  * (Note that, later, '\0' will be considered as the end-of-line marker,
1962  * so everything between the '\0' and the '\r\n' will not be used).
1963  * Also note that we assume output to have at least "maxlen" space.
1964  */
1965 static int get_input(struct mansession *s, char *output)
1966 {
1967         struct pollfd fds[1];
1968         int res, x;
1969         int maxlen = sizeof(s->inbuf) - 1;
1970         char *src = s->inbuf;
1971
1972         /*
1973          * Look for \r\n within the buffer. If found, copy to the output
1974          * buffer and return, trimming the \r\n (not used afterwards).
1975          */
1976         for (x = 1; x < s->inlen; x++) {
1977                 if (src[x] != '\n' || src[x-1] != '\r')
1978                         continue;
1979                 x++;    /* Found. Update length to include \r\n */
1980                 memmove(output, src, x-2);      /*... but trim \r\n */
1981                 output[x-2] = '\0';             /* terminate the string */
1982                 s->inlen -= x;                  /* remaining size */
1983                 memmove(src, src + x, s->inlen); /* remove used bytes */
1984                 return 1;
1985         }
1986         if (s->inlen >= maxlen) {
1987                 /* no crlf found, and buffer full - sorry, too long for us */
1988                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
1989                 s->inlen = 0;
1990         }
1991         fds[0].fd = s->fd;
1992         fds[0].events = POLLIN;
1993         res = 0;
1994         while (res == 0) {
1995                 /* XXX do we really need this locking ? */
1996                 ast_mutex_lock(&s->__lock);
1997                 s->waiting_thread = pthread_self();
1998                 ast_mutex_unlock(&s->__lock);
1999
2000                 res = poll(fds, 1, -1); /* return 0 on timeout ? */
2001
2002                 ast_mutex_lock(&s->__lock);
2003                 s->waiting_thread = AST_PTHREADT_NULL;
2004                 ast_mutex_unlock(&s->__lock);
2005         }
2006         if (res < 0) {
2007                 if (errno == EINTR)
2008                         return 0;
2009                 ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
2010                 return -1;
2011         }
2012         ast_mutex_lock(&s->__lock);
2013         res = read(s->fd, src + s->inlen, maxlen - s->inlen);
2014         if (res < 1)
2015                 res = -1;       /* error return */
2016         else {
2017                 s->inlen += res;
2018                 src[s->inlen] = '\0';
2019                 res = 0;
2020         }
2021         ast_mutex_unlock(&s->__lock);
2022         return res;
2023 }
2024
2025 /*! \brief The body of the individual manager session.
2026  */
2027 static void *session_do(void *data)
2028 {
2029         struct mansession *s = data;
2030         struct message m;       /* XXX watch out, this is 20k of memory! */
2031
2032         ast_mutex_lock(&s->__lock);
2033         astman_append(s, "Asterisk Call Manager/1.0\r\n");      /* welcome prompt */
2034         ast_mutex_unlock(&s->__lock);
2035         memset(&m, 0, sizeof(m));
2036         for (;;) {
2037                 char *buf = m.headers[m.hdrcount];
2038                 int res = get_input(s, buf);
2039                 if (res < 0)    /* error */
2040                         break;
2041                 if (res > 0) {  /* got one line */
2042                         if (ast_strlen_zero(buf)) {     /* empty line, terminator */
2043                                 if (process_message(s, &m))
2044                                         break;
2045                                 memset(&m, 0, sizeof(m));
2046                         } else if (m.hdrcount < AST_MAX_MANHEADERS - 1)
2047                                 m.hdrcount++;
2048                 } else if (process_events(s))
2049                         break;
2050         }
2051         /* session is over, explain why and terminate */
2052         if (s->authenticated) {
2053                 if (option_verbose > 1) {
2054                         if (displayconnects)
2055                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2056                 }
2057                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2058         } else {
2059                 if (option_verbose > 1) {
2060                         if (displayconnects)
2061                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2062                 }
2063                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2064         }
2065         destroy_session(s);
2066         return NULL;
2067 }
2068
2069 /*! \brief The thread accepting connections on the manager interface port.
2070  * As a side effect, it purges stale sessions, one per each iteration,
2071  * which is at least every 5 seconds.
2072  */
2073 static void *accept_thread(void *ignore)
2074 {
2075         pthread_attr_t attr;
2076
2077         pthread_attr_init(&attr);
2078         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2079
2080         for (;;) {
2081                 struct mansession *s;
2082                 time_t now = time(NULL);
2083                 int as;
2084                 struct sockaddr_in sin;
2085                 socklen_t sinlen;
2086                 struct protoent *p;
2087                 int flags;
2088                 struct pollfd pfds[1];
2089
2090                 AST_LIST_LOCK(&sessions);
2091                 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
2092                         if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
2093                                 ast_verbose("destroy session[2] %lx now %lu to %lu\n",
2094                                         s->managerid, (unsigned long)now, (unsigned long)s->sessiontimeout);
2095                                 AST_LIST_REMOVE_CURRENT(&sessions, list);
2096                                 ast_atomic_fetchadd_int(&num_sessions, -1);
2097                                 if (s->authenticated && (option_verbose > 1) && displayconnects) {
2098                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
2099                                                 s->username, ast_inet_ntoa(s->sin.sin_addr));
2100                                 }
2101                                 free_session(s);        /* XXX outside ? */
2102                                 break;
2103                         }
2104                 }
2105                 AST_LIST_TRAVERSE_SAFE_END
2106                 AST_LIST_UNLOCK(&sessions);
2107                 purge_unused();
2108
2109                 sinlen = sizeof(sin);
2110                 pfds[0].fd = asock;
2111                 pfds[0].events = POLLIN;
2112                 /* Wait for something to happen, but timeout every few seconds so
2113                    we can ditch any old manager sessions */
2114                 if (poll(pfds, 1, 5000) < 1)
2115                         continue;
2116                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
2117                 if (as < 0) {
2118                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2119                         continue;
2120                 }
2121                 p = getprotobyname("tcp");
2122                 if (p) {
2123                         int arg = 1;
2124                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2125                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2126                         }
2127                 }
2128                 s = ast_calloc(1, sizeof(*s));  /* allocate a new record */
2129                 if (!s) {
2130                         close(as);
2131                         continue;
2132                 }
2133
2134
2135                 s->sin = sin;
2136                 s->writetimeout = 100;
2137                 s->waiting_thread = AST_PTHREADT_NULL;
2138
2139                 flags = fcntl(as, F_GETFL);
2140                 if (!block_sockets) /* For safety, make sure socket is non-blocking */
2141                         flags |= O_NONBLOCK;
2142                 else
2143                         flags &= ~O_NONBLOCK;
2144                 fcntl(as, F_SETFL, flags);
2145
2146                 ast_mutex_init(&s->__lock);
2147                 s->fd = as;
2148                 s->send_events = -1;
2149
2150                 ast_atomic_fetchadd_int(&num_sessions, 1);
2151                 AST_LIST_LOCK(&sessions);
2152                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2153                 AST_LIST_UNLOCK(&sessions);
2154                 /* Hook to the tail of the event queue */
2155                 s->last_ev = grab_last();
2156                 if (ast_pthread_create_background(&s->ms_t, &attr, session_do, s))
2157                         destroy_session(s);
2158         }
2159         pthread_attr_destroy(&attr);
2160         return NULL;
2161 }
2162
2163 /*
2164  * events are appended to a queue from where they
2165  * can be dispatched to clients.
2166  */
2167 static int append_event(const char *str, int category)
2168 {
2169         struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
2170         static int seq; /* sequence number */
2171
2172         if (!tmp)
2173                 return -1;
2174
2175         /* need to init all fields, because ast_malloc() does not */
2176         tmp->usecount = 0;
2177         tmp->category = category;
2178         tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
2179         AST_LIST_NEXT(tmp, eq_next) = NULL;
2180         strcpy(tmp->eventdata, str);
2181
2182         AST_LIST_LOCK(&all_events);
2183         AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
2184         AST_LIST_UNLOCK(&all_events);
2185
2186         return 0;
2187 }
2188
2189 /*! \brief  manager_event: Send AMI event to client */
2190 int manager_event(int category, const char *event, const char *fmt, ...)
2191 {
2192         struct mansession *s;
2193         char auth[80];
2194         va_list ap;
2195         struct timeval now;
2196         struct ast_dynamic_str *buf;
2197
2198         /* Abort if there aren't any manager sessions */
2199         if (!num_sessions)
2200                 return 0;
2201
2202         if (!(buf = ast_dynamic_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
2203                 return -1;
2204
2205         ast_dynamic_str_thread_set(&buf, 0, &manager_event_buf,
2206                         "Event: %s\r\nPrivilege: %s\r\n",
2207                          event, authority_to_str(category, auth, sizeof(auth)));
2208
2209         if (timestampevents) {
2210                 now = ast_tvnow();
2211                 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2212                                 "Timestamp: %ld.%06lu\r\n",
2213                                  now.tv_sec, (unsigned long) now.tv_usec);
2214         }
2215         if (numberevents) {
2216                 static int seq;
2217                 ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf,
2218                                 "SequenceNumber: %d\r\n",
2219                                  ast_atomic_fetchadd_int(&seq, 1));
2220         }
2221
2222         va_start(ap, fmt);
2223         ast_dynamic_str_thread_append_va(&buf, 0, &manager_event_buf, fmt, ap);
2224         va_end(ap);
2225
2226         ast_dynamic_str_thread_append(&buf, 0, &manager_event_buf, "\r\n");
2227
2228         append_event(buf->str, category);
2229
2230         /* Wake up any sleeping sessions */
2231         AST_LIST_LOCK(&sessions);
2232         AST_LIST_TRAVERSE(&sessions, s, list) {
2233                 ast_mutex_lock(&s->__lock);
2234                 if (s->waiting_thread != AST_PTHREADT_NULL)
2235                         pthread_kill(s->waiting_thread, SIGURG);
2236                 ast_mutex_unlock(&s->__lock);
2237         }
2238         AST_LIST_UNLOCK(&sessions);
2239
2240         return 0;
2241 }
2242
2243 /*
2244  * support functions to register/unregister AMI action handlers,
2245  */
2246 int ast_manager_unregister(char *action)
2247 {
2248         struct manager_action *cur = first_action, *prev = first_action;
2249
2250         ast_mutex_lock(&actionlock);
2251         for (cur = first_action, prev = NULL; cur; prev = cur, cur = cur->next) {
2252                 if (!strcasecmp(action, cur->action)) {
2253                         if (prev)
2254                                 prev->next = cur->next;
2255                         else
2256                                 first_action = cur->next;
2257                         free(cur);
2258                         if (option_verbose > 1)
2259                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
2260                         break;
2261                 }
2262         }
2263         ast_mutex_unlock(&actionlock);
2264         return 0;
2265 }
2266
2267 static int manager_state_cb(char *context, char *exten, int state, void *data)
2268 {
2269         /* Notify managers of change */
2270         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
2271         return 0;
2272 }
2273
2274 static int ast_manager_register_struct(struct manager_action *act)
2275 {
2276         struct manager_action *cur, *prev = NULL;
2277         int ret;
2278
2279         ast_mutex_lock(&actionlock);
2280         for (cur = first_action; cur; prev = cur, cur = cur->next) {
2281                 ret = strcasecmp(cur->action, act->action);
2282                 if (ret == 0) {
2283                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
2284                         ast_mutex_unlock(&actionlock);
2285                         return -1;
2286                 }
2287                 if (ret > 0)    /* Insert these alphabetically */
2288                         break;
2289         }
2290         if (prev)
2291                 prev->next = act;
2292         else
2293                 first_action = act;
2294         act->next = cur;
2295
2296         if (option_verbose > 1)
2297                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
2298         ast_mutex_unlock(&actionlock);
2299         return 0;
2300 }
2301
2302 /*! \brief register a new command with manager, including online help. This is
2303         the preferred way to register a manager command */
2304 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
2305 {
2306         struct manager_action *cur;
2307
2308         cur = ast_malloc(sizeof(*cur));
2309         if (!cur)
2310                 return -1;
2311
2312         cur->action = action;
2313         cur->authority = auth;
2314         cur->func = func;
2315         cur->synopsis = synopsis;
2316         cur->description = description;
2317         cur->next = NULL;
2318
2319         ast_manager_register_struct(cur);
2320
2321         return 0;
2322 }
2323 /*! @}
2324  END Doxygen group */
2325
2326 /*
2327  * The following are support functions for AMI-over-http.
2328  * The common entry point is generic_http_callback(),
2329  * which extracts HTTP header and URI fields and reformats
2330  * them into AMI messages, locates a proper session
2331  * (using the mansession_id Cookie or GET variable),
2332  * and calls process_message() as for regular AMI clients.
2333  * When done, the output (which goes to a temporary file)
2334  * is read back into a buffer and reformatted as desired,
2335  * then fed back to the client over the original socket.
2336  */
2337
2338 enum output_format {
2339         FORMAT_RAW,
2340         FORMAT_HTML,
2341         FORMAT_XML,
2342 };
2343
2344 static char *contenttype[] = {
2345         [FORMAT_RAW] = "plain",
2346         [FORMAT_HTML] = "html",
2347         [FORMAT_XML] =  "xml",
2348 };
2349
2350 /* locate an http session in the list using the cookie as a key */
2351 static struct mansession *find_session(unsigned long ident)
2352 {
2353         struct mansession *s;
2354
2355         if (ident == 0)
2356                 return NULL;
2357
2358         AST_LIST_LOCK(&sessions);
2359         AST_LIST_TRAVERSE(&sessions, s, list) {
2360                 ast_mutex_lock(&s->__lock);
2361                 if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
2362                         ast_atomic_fetchadd_int(&s->inuse, 1);
2363                         break;
2364                 }
2365                 ast_mutex_unlock(&s->__lock);
2366         }
2367         AST_LIST_UNLOCK(&sessions);
2368
2369         return s;
2370 }
2371
2372 static void vars2msg(struct message *m, struct ast_variable *vars)
2373 {
2374         int x;
2375         for (x = 0; vars && (x < AST_MAX_MANHEADERS); x++, vars = vars->next) {
2376                 if (!vars)
2377                         break;
2378                 m->hdrcount = x + 1;
2379                 snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
2380         }
2381 }
2382
2383 /*
2384  * convert to xml with various conversion:
2385  * mode & 1     -> lowercase;
2386  * mode & 2     -> replace non-alphanumeric chars with underscore
2387  */
2388 static void xml_copy_escape(char **dst, size_t *maxlen, const char *src, int mode)
2389 {
2390         for ( ; *src && *maxlen > 6; src++) {
2391                 if ( (mode & 2) && !isalnum(*src)) {
2392                         *(*dst)++ = '_';
2393                         (*maxlen)--;
2394                         continue;
2395                 }
2396                 switch (*src) {
2397                 case '<':
2398                         strcpy(*dst, "&lt;");
2399                         (*dst) += 4;
2400                         *maxlen -= 4;
2401                         break;
2402                 case '>':
2403                         strcpy(*dst, "&gt;");
2404                         (*dst) += 4;
2405                         *maxlen -= 4;
2406                         break;
2407                 case '\"':
2408                         strcpy(*dst, "&quot;");
2409                         (*dst) += 6;
2410                         *maxlen -= 6;
2411                         break;
2412                 case '\'':
2413                         strcpy(*dst, "&apos;");
2414                         (*dst) += 6;
2415                         *maxlen -= 6;
2416                         break;
2417                 case '&':
2418                         strcpy(*dst, "&amp;");
2419                         (*dst) += 5;
2420                         *maxlen -= 5;
2421                         break;
2422
2423                 default:
2424                         *(*dst)++ = mode ? tolower(*src) : *src;
2425                         (*maxlen)--;
2426                 }
2427         }
2428 }
2429
2430 /*! \brief Convert the input into XML or HTML.
2431  * The input is supposed to be a sequence of lines of the form
2432  *      Name: value
2433  * optionally followed by a blob of unformatted text.
2434  * A blank line is a section separator. Basically, this is a
2435  * mixture of the format of Manager Interface and CLI commands.
2436  * The unformatted text is considered as a single value of a field
2437  * named 'Opaque-data'.
2438  *
2439  * At the moment the output format is the following (but it may
2440  * change depending on future requirements so don't count too
2441  * much on it when writing applications):
2442  *
2443  * General: the unformatted text is used as a value of
2444  * XML output:  to be completed
2445  *   Each section is within <response type="object" id="xxx">
2446  *   where xxx is taken from ajaxdest variable or defaults to unknown
2447  *   Each row is reported as an attribute Name="value" of an XML
2448  *   entity named from the variable ajaxobjtype, default to "generic"
2449  *
2450  * HTML output:
2451  *   each Name-value pair is output as a single row of a two-column table.
2452  *   Sections (blank lines in the input) are separated by a <HR>
2453  *
2454  */
2455 static char *xml_translate(char *in, struct ast_variable *vars, enum output_format format)
2456 {
2457         struct ast_variable *v;
2458         char *dest = NULL;
2459         char *out, *tmp, *var, *val;
2460         char *objtype = NULL;
2461         int colons = 0;
2462         int breaks = 0;
2463         size_t len;
2464         int in_data = 0;        /* parsing data */
2465         int escaped = 0;
2466         int inobj = 0;
2467         int x;
2468         int xml = (format == FORMAT_XML);
2469
2470         for (v = vars; v; v = v->next) {
2471                 if (!dest && !strcasecmp(v->name, "ajaxdest"))
2472                         dest = v->value;
2473                 else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
2474                         objtype = v->value;
2475         }
2476         if (!dest)
2477                 dest = "unknown";
2478         if (!objtype)
2479                 objtype = "generic";
2480
2481         /* determine how large is the response.
2482          * This is a heuristic - counting colons (for headers),
2483          * newlines (for extra arguments), and escaped chars.
2484          * XXX needs to be checked carefully for overflows.
2485          * Even better, use some code that allows extensible strings.
2486          */
2487         for (x = 0; in[x]; x++) {
2488                 if (in[x] == ':')
2489                         colons++;
2490                 else if (in[x] == '\n')
2491                         breaks++;
2492                 else if (strchr("&\"<>", in[x]))
2493                         escaped++;
2494         }
2495         len = (size_t) (strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10); /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
2496         out = ast_malloc(len);
2497         if (!out)
2498                 return NULL;
2499         tmp = out;
2500         /* we want to stop when we find an empty line */
2501         while (in && *in) {
2502                 val = strsep(&in, "\r\n");      /* mark start and end of line */
2503                 if (in && *in == '\n')          /* remove trailing \n if any */
2504                         in++;
2505                 ast_trim_blanks(val);
2506                 if (0)
2507                         ast_verbose("inobj %d in_data %d line <%s>\n", inobj, in_data, val);
2508                 if (ast_strlen_zero(val)) {
2509                         if (in_data) { /* close data */
2510                                 ast_build_string(&tmp, &len, xml ? "'" : "</td></tr>\n");
2511                                 in_data = 0;
2512                         }
2513                         ast_build_string(&tmp, &len, xml ? " /></response>\n" :
2514                                 "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
2515                         inobj = 0;
2516                         continue;
2517                 }
2518                 /* we expect Name: value lines */
2519                 if (in_data) {
2520                         var = NULL;
2521                 } else {
2522                         var = strsep(&val, ":");
2523                         if (val) {      /* found the field name */
2524                                 val = ast_skip_blanks(val);
2525                                 ast_trim_blanks(var);
2526                         } else {                /* field name not found, move to opaque mode */
2527                                 val = var;
2528                                 var = "Opaque-data";
2529                         }
2530                 }
2531                 if (!inobj) {
2532                         if (xml)
2533                                 ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
2534                         else
2535                                 ast_build_string(&tmp, &len, "<body>\n");
2536                         inobj = 1;
2537                 }
2538                 if (!in_data) { /* build appropriate line start */
2539                         ast_build_string(&tmp, &len, xml ? " " : "<tr><td>");
2540                         xml_copy_escape(&tmp, &len, var, xml ? 1 | 2 : 0);
2541                         ast_build_string(&tmp, &len, xml ? "='" : "</td><td>");
2542                         if (!strcmp(var, "Opaque-data"))
2543                                 in_data = 1;
2544                 }
2545                 xml_copy_escape(&tmp, &len, val, 0);    /* data field */
2546                 if (!in_data)
2547                         ast_build_string(&tmp, &len, xml ? "'" : "</td></tr>\n");
2548                 else
2549                         ast_build_string(&tmp, &len, xml ? "\n" : "<br>\n");
2550         }
2551         if (inobj)
2552                 ast_build_string(&tmp, &len, xml ? " /></response>\n" :
2553                         "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
2554         return out;
2555 }
2556
2557 static char *generic_http_callback(enum output_format format,
2558         struct sockaddr_in *requestor, const char *uri,
2559         struct ast_variable *params, int *status,
2560         char **title, int *contentlength)
2561 {
2562         struct mansession *s = NULL;
2563         unsigned long ident = 0;
2564         char workspace[1024];
2565         size_t len = sizeof(workspace);
2566         int blastaway = 0;
2567         char *c = workspace;
2568         char *retval = NULL;
2569         struct message m;
2570         struct ast_variable *v;
2571         char template[] = "/tmp/ast-http-XXXXXX";       /* template for temporary file */
2572
2573         for (v = params; v; v = v->next) {
2574                 if (!strcasecmp(v->name, "mansession_id")) {
2575                         sscanf(v->value, "%lx", &ident);
2576                         break;
2577                 }
2578         }
2579
2580         if (!(s = find_session(ident))) {
2581                 /* Create new session.
2582                  * While it is not in the list we don't need any locking
2583                  */
2584                 if (!(s = ast_calloc(1, sizeof(*s)))) {
2585                         *status = 500;
2586                         goto generic_callback_out;
2587                 }
2588                 s->sin = *requestor;
2589                 s->fd = -1;
2590                 s->waiting_thread = AST_PTHREADT_NULL;
2591                 s->send_events = 0;
2592                 ast_mutex_init(&s->__lock);
2593                 ast_mutex_lock(&s->__lock);
2594                 s->inuse = 1;
2595                 s->managerid = rand() | 1;      /* make sure it is non-zero */
2596                 s->last_ev = grab_last();
2597                 AST_LIST_LOCK(&sessions);
2598                 AST_LIST_INSERT_HEAD(&sessions, s, list);
2599                 AST_LIST_UNLOCK(&sessions);
2600                 ast_atomic_fetchadd_int(&num_sessions, 1);
2601         }
2602
2603         ast_mutex_unlock(&s->__lock);
2604         memset(&m, 0, sizeof(m));
2605         {
2606                 char tmp[80];
2607                 char cookie[128];
2608
2609                 ast_build_string(&c, &len, "Content-type: text/%s\r\n", contenttype[format]);
2610                 ast_build_string(&c, &len, "Cache-Control: no-cache;\r\n");
2611                 sprintf(tmp, "%08lx", s->managerid);
2612                 ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
2613         }
2614
2615         if (format == FORMAT_HTML)
2616                 ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Test Interface</title>");
2617         vars2msg(&m, params);
2618
2619         if (format == FORMAT_XML) {
2620                 ast_build_string(&c, &len, "<ajax-response>\n");
2621         } else if (format == FORMAT_HTML) {
2622
2623 #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
2624 #define TEST_STRING \
2625         "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
2626         user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
2627         <input type=\"submit\"></form>"
2628
2629                 ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
2630                 ast_build_string(&c, &len, ROW_FMT, "<h1>Manager Tester</h1>");
2631                 ast_build_string(&c, &len, ROW_FMT, TEST_STRING);
2632         }
2633
2634         s->fd = mkstemp(template);      /* create a temporary file for command output */
2635
2636         if (process_message(s, &m)) {
2637                 if (s->authenticated) {
2638                         if (option_verbose > 1) {
2639                                 if (displayconnects)
2640                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2641                         }
2642                         ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
2643                 } else {
2644                         if (option_verbose > 1) {
2645                                 if (displayconnects)
2646                                         ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
2647                         }
2648                         ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
2649                 }
2650                 s->needdestroy = 1;
2651         }
2652         if (s->fd > -1) {       /* have temporary output */
2653                 char *buf;
2654                 off_t len = lseek(s->fd, 0, SEEK_END);  /* how many chars available */
2655
2656                 if (len > 0 && (buf = ast_calloc(1, len+1))) {
2657                         if (!s->outputstr)
2658                                 s->outputstr = ast_calloc(1, sizeof(*s->outputstr));
2659                         if (s->outputstr) {
2660                                 lseek(s->fd, 0, SEEK_SET);
2661                                 read(s->fd, buf, len);
2662                                 if (0)
2663                                         ast_verbose("--- fd %d has %d bytes ---\n%s\n---\n", s->fd, (int)len, buf);
2664                                 ast_dynamic_str_append(&s->outputstr, 0, "%s", buf);
2665                         }
2666                         free(buf);
2667                 }
2668                 close(s->fd);
2669                 s->fd = -1;
2670                 unlink(template);
2671         }
2672
2673         if (s->outputstr) {
2674                 char *tmp;
2675                 if (format == FORMAT_XML || format == FORMAT_HTML)
2676                         tmp = xml_translate(s->outputstr->str, params, format);
2677                 else
2678                         tmp = s->outputstr->str;
2679                 if (tmp) {
2680                         retval = malloc(strlen(workspace) + strlen(tmp) + 128);
2681                         if (retval) {
2682                                 strcpy(retval, workspace);
2683                                 strcpy(retval + strlen(retval), tmp);
2684                                 c = retval + strlen(retval);
2685                                 len = 120;
2686                         }
2687                 }
2688                 if (tmp != s->outputstr->str)
2689                         free(tmp);
2690                 free(s->outputstr);
2691                 s->outputstr = NULL;
2692         }
2693         /* Still okay because c would safely be pointing to workspace even
2694            if retval failed to allocate above */
2695         if (format == FORMAT_XML) {
2696                 ast_build_string(&c, &len, "</ajax-response>\n");
2697         } else if (format == FORMAT_HTML)
2698                 ast_build_string(&c, &len, "</table></body>\r\n");
2699
2700         ast_mutex_lock(&s->__lock);
2701         /* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
2702         s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
2703         if (0)
2704                 ast_verbose("die in %d seconds\n",
2705                         (int)(s->sessiontimeout - time(NULL)) );
2706         if (s->needdestroy) {
2707                 if (s->inuse == 1) {
2708                         if (option_debug)
2709                                 ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
2710                         blastaway = 1;
2711                 } else {
2712                         if (option_debug)
2713                                 ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
2714                         if (s->waiting_thread != AST_PTHREADT_NULL)
2715                                 pthread_kill(s->waiting_thread, SIGURG);
2716                         s->inuse--;
2717                 }
2718         } else
2719                 s->inuse--;
2720         ast_mutex_unlock(&s->__lock);
2721
2722         if (blastaway)
2723                 destroy_session(s);
2724 generic_callback_out:
2725         if (*status != 200)
2726                 return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
2727         return retval;
2728 }
2729
2730 static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2731 {
2732         return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
2733 }
2734
2735 static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2736 {
2737         return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
2738 }
2739
2740 static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
2741 {
2742         return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
2743 }
2744
2745 struct ast_http_uri rawmanuri = {
2746         .description = "Raw HTTP Manager Event Interface",
2747         .uri = "rawman",
2748         .has_subtree = 0,
2749         .callback = rawman_http_callback,
2750 };
2751
2752 struct ast_http_uri manageruri = {
2753         .description = "HTML Manager Event Interface",
2754         .uri = "manager",
2755         .has_subtree = 0,
2756         .callback = manager_http_callback,
2757 };
2758
2759 struct ast_http_uri managerxmluri = {
2760         .description = "XML Manager Event Interface",
2761         .uri = "mxml",
2762         .has_subtree = 0,
2763         .callback = mxml_http_callback,
2764 };
2765
2766 static int registered = 0;
2767 static int webregged = 0;
2768
2769 int init_manager(void)
2770 {
2771         struct ast_config *cfg = NULL;
2772         const char *val;
2773         char *cat = NULL;
2774         int oldportno = portno;
2775         static struct sockaddr_in ba;
2776         int x = 1;
2777         int flags;
2778         int webenabled = 0;
2779         int newhttptimeout = 60;
2780         struct ast_manager_user *user = NULL;
2781
2782         if (!registered) {
2783                 /* Register default actions */
2784                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
2785                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
2786                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
2787                 ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
2788                 ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
2789                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
2790                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
2791                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
2792                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
2793                 ast_manager_register2("GetConfig", EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
2794                 ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
2795                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
2796                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
2797                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
2798                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
2799                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
2800                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
2801                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
2802                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
2803                 ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
2804                 ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
2805                 ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
2806
2807                 ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
2808                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
2809                 registered = 1;
2810                 /* Append placeholder event so master_eventq never runs dry */
2811                 append_event("Event: Placeholder\r\n\r\n", 0);
2812         }
2813         portno = DEFAULT_MANAGER_PORT;
2814         displayconnects = 1;
2815         cfg = ast_config_load("manager.conf");
2816         if (!cfg) {
2817                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
2818                 return 0;
2819         }
2820         val = ast_variable_retrieve(cfg, "general", "enabled");
2821         if (val)
2822                 enabled = ast_true(val);
2823
2824         val = ast_variable_retrieve(cfg, "general", "block-sockets");
2825         if (val)
2826                 block_sockets = ast_true(val);
2827
2828         val = ast_variable_retrieve(cfg, "general", "webenabled");
2829         if (val)
2830                 webenabled = ast_true(val);
2831
2832         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
2833                 if (sscanf(val, "%d", &portno) != 1) {
2834                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
2835                         portno = DEFAULT_MANAGER_PORT;
2836                 }
2837         }
2838
2839         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects")))
2840                 displayconnects = ast_true(val);
2841
2842         if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
2843                 timestampevents = ast_true(val);
2844
2845         if ((val = ast_variable_retrieve(cfg, "general", "numberevents")))
2846                 numberevents = ast_true(val);
2847
2848         if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
2849                 newhttptimeout = atoi(val);
2850
2851         memset(&ba, 0, sizeof(ba));
2852         ba.sin_family = AF_INET;
2853         ba.sin_port = htons(portno);
2854
2855         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
2856                 if (!inet_aton(val, &ba.sin_addr)) {
2857                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
2858                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
2859                 }
2860         }
2861
2862
2863         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
2864 #if 0
2865                 /* Can't be done yet */
2866                 close(asock);
2867                 asock = -1;
2868 #else
2869                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
2870 #endif
2871         }
2872
2873         AST_LIST_LOCK(&users);
2874
2875         while ((cat = ast_category_browse(cfg, cat))) {
2876                 struct ast_variable *var = NULL;
2877
2878                 if (!strcasecmp(cat, "general"))
2879                         continue;
2880
2881                 /* Look for an existing entry, if none found - create one and add it to the list */
2882                 if (!(user = get_manager_by_name_locked(cat))) {
2883                         if (!(user = ast_calloc(1, sizeof(*user))))
2884                                 break;
2885                         /* Copy name over */
2886                         ast_copy_string(user->username, cat, sizeof(user->username));
2887                         /* Insert into list */
2888                         AST_LIST_INSERT_TAIL(&users, user, list);
2889                 }
2890
2891                 /* Make sure we keep this user and don't destroy it during cleanup */
2892                 user->keep = 1;
2893
2894                 var = ast_variable_browse(cfg, cat);
2895                 while (var) {
2896                         if (!strcasecmp(var->name, "secret")) {
2897                                 if (user->secret)
2898                                         free(user->secret);
2899                                 user->secret = ast_strdup(var->value);
2900                         } else if (!strcasecmp(var->name, "deny") ) {
2901                                 if (user->deny)
2902                                         free(user->deny);
2903                                 user->deny = ast_strdup(var->value);
2904                         } else if (!strcasecmp(var->name, "permit") ) {
2905                                 if (user->permit)
2906                                         free(user->permit);
2907                                 user->permit = ast_strdup(var->value);
2908                         }  else if (!strcasecmp(var->name, "read") ) {
2909                                 if (user->read)
2910                                         free(user->read);
2911                                 user->read = ast_strdup(var->value);
2912                         }  else if (!strcasecmp(var->name, "write") ) {
2913                                 if (user->write)
2914                                         free(user->write);
2915                                 user->write = ast_strdup(var->value);
2916                         }  else if (!strcasecmp(var->name, "displayconnects") )
2917                                 user->displayconnects = ast_true(var->value);
2918                         else {
2919                                 if (option_debug)
2920                                         ast_log(LOG_DEBUG, "%s is an unknown option.\n", var->name);
2921                         }
2922                         var = var->next;
2923                 }
2924         }
2925
2926         /* Perform cleanup - essentially prune out old users that no longer exist */
2927         AST_LIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
2928                 if (user->keep) {       /* valid record. clear flag for the next round */
2929                         user->keep = 0;
2930                         continue;
2931                 }
2932                 /* We do not need to keep this user so take them out of the list */
2933                 AST_LIST_REMOVE_CURRENT(&users, list);
2934                 /* Free their memory now */
2935                 if (user->secret)
2936                         free(user->secret);
2937                 if (user->deny)
2938                         free(user->deny);
2939                 if (user->permit)
2940                         free(user->permit);
2941                 if (user->read)
2942                         free(user->read);
2943                 if (user->write)
2944                         free(user->write);
2945                 free(user);
2946         }
2947         AST_LIST_TRAVERSE_SAFE_END
2948
2949         AST_LIST_UNLOCK(&users);
2950
2951         ast_config_destroy(cfg);
2952
2953         if (webenabled && enabled) {
2954                 if (!webregged) {
2955                         ast_http_uri_link(&rawmanuri);
2956                         ast_http_uri_link(&manageruri);
2957                         ast_http_uri_link(&managerxmluri);
2958                         webregged = 1;
2959                 }
2960         } else {
2961                 if (webregged) {
2962                         ast_http_uri_unlink(&rawmanuri);
2963                         ast_http_uri_unlink(&manageruri);
2964                         ast_http_uri_unlink(&managerxmluri);
2965                         webregged = 0;
2966                 }
2967         }
2968
2969         if (newhttptimeout > 0)
2970                 httptimeout = newhttptimeout;
2971
2972         /* If not enabled, do nothing */
2973         if (!enabled)
2974                 return 0;
2975
2976         if (asock < 0) {
2977                 asock = socket(AF_INET, SOCK_STREAM, 0);
2978                 if (asock < 0) {
2979                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
2980                         return -1;
2981                 }
2982                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
2983                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
2984                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
2985                         close(asock);
2986                         asock = -1;
2987                         return -1;
2988                 }
2989                 if (listen(asock, 2)) {
2990                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
2991                         close(asock);
2992                         asock = -1;
2993                         return -1;
2994                 }
2995                 flags = fcntl(asock, F_GETFL);
2996                 fcntl(asock, F_SETFL, flags | O_NONBLOCK);
2997                 if (option_verbose)
2998                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
2999                 ast_pthread_create_background(&accept_thread_ptr, NULL, accept_thread, NULL);
3000         }
3001         return 0;
3002 }
3003
3004 int reload_manager(void)
3005 {
3006         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
3007         return init_manager();
3008 }