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