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