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