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