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