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