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