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