c4bfcec3560d0b2be669492d29e1b014445381e2
[asterisk/asterisk.git] / manager.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, 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  * Channel Management and more
24  * 
25  * \ref amiconf
26  */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <netdb.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <netinet/tcp.h>
37 #include <arpa/inet.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <unistd.h>
41
42 #include "asterisk.h"
43
44 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
45
46 #include "asterisk/channel.h"
47 #include "asterisk/file.h"
48 #include "asterisk/manager.h"
49 #include "asterisk/config.h"
50 #include "asterisk/callerid.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/logger.h"
53 #include "asterisk/options.h"
54 #include "asterisk/cli.h"
55 #include "asterisk/app.h"
56 #include "asterisk/pbx.h"
57 #include "asterisk/md5.h"
58 #include "asterisk/acl.h"
59 #include "asterisk/utils.h"
60
61 struct fast_originate_helper {
62         char tech[256];
63         char data[256];
64         int timeout;
65         char app[256];
66         char appdata[256];
67         char cid_name[256];
68         char cid_num[256];
69         char context[256];
70         char exten[256];
71         char idtext[256];
72         int priority;
73         struct ast_variable *vars;
74 };
75
76 static int enabled = 0;
77 static int portno = DEFAULT_MANAGER_PORT;
78 static int asock = -1;
79 static int displayconnects = 1;
80
81 static pthread_t t;
82 AST_MUTEX_DEFINE_STATIC(sessionlock);
83 static int block_sockets = 0;
84
85 static struct permalias {
86         int num;
87         char *label;
88 } perms[] = {
89         { EVENT_FLAG_SYSTEM, "system" },
90         { EVENT_FLAG_CALL, "call" },
91         { EVENT_FLAG_LOG, "log" },
92         { EVENT_FLAG_VERBOSE, "verbose" },
93         { EVENT_FLAG_COMMAND, "command" },
94         { EVENT_FLAG_AGENT, "agent" },
95         { EVENT_FLAG_USER, "user" },
96         { -1, "all" },
97         { 0, "none" },
98 };
99
100 static struct mansession *sessions = NULL;
101 static struct manager_action *first_action = NULL;
102 AST_MUTEX_DEFINE_STATIC(actionlock);
103
104 /*! If you are calling ast_carefulwrite, it is assumed that you are calling
105    it on a file descriptor that _DOES_ have NONBLOCK set.  This way,
106    there is only one system call made to do a write, unless we actually
107    have a need to wait.  This way, we get better performance. */
108 int ast_carefulwrite(int fd, char *s, int len, int timeoutms) 
109 {
110         /* Try to write string, but wait no more than ms milliseconds
111            before timing out */
112         int res=0;
113         struct pollfd fds[1];
114         while(len) {
115                 res = write(fd, s, len);
116                 if ((res < 0) && (errno != EAGAIN)) {
117                         return -1;
118                 }
119                 if (res < 0) res = 0;
120                 len -= res;
121                 s += res;
122                 res = 0;
123                 if (len) {
124                         fds[0].fd = fd;
125                         fds[0].events = POLLOUT;
126                         /* Wait until writable again */
127                         res = poll(fds, 1, timeoutms);
128                         if (res < 1)
129                                 return -1;
130                 }
131         }
132         return res;
133 }
134
135 /*! authority_to_str: Convert authority code to string with serveral options */
136 static char *authority_to_str(int authority, char *res, int reslen)
137 {
138         int running_total = 0, i;
139         memset(res, 0, reslen);
140         for (i=0; i<sizeof(perms) / sizeof(perms[0]) - 1; i++) {
141                 if (authority & perms[i].num) {
142                         if (*res) {
143                                 strncat(res, ",", (reslen > running_total) ? reslen - running_total : 0);
144                                 running_total++;
145                         }
146                         strncat(res, perms[i].label, (reslen > running_total) ? reslen - running_total : 0);
147                         running_total += strlen(perms[i].label);
148                 }
149         }
150         if (ast_strlen_zero(res)) {
151                 ast_copy_string(res, "<none>", reslen);
152         }
153         return res;
154 }
155
156 static char *complete_show_mancmd(char *line, char *word, int pos, int state)
157 {
158         struct manager_action *cur = first_action;
159         int which = 0;
160
161         ast_mutex_lock(&actionlock);
162         while (cur) { /* Walk the list of actions */
163                 if (!strncasecmp(word, cur->action, strlen(word))) {
164                         if (++which > state) {
165                                 char *ret = strdup(cur->action);
166                                 ast_mutex_unlock(&actionlock);
167                                 return ret;
168                         }
169                 }
170                 cur = cur->next;
171         }
172         ast_mutex_unlock(&actionlock);
173         return NULL;
174 }
175
176 static int handle_showmancmd(int fd, int argc, char *argv[])
177 {
178         struct manager_action *cur = first_action;
179         char authority[80];
180         int num;
181
182         if (argc != 4)
183                 return RESULT_SHOWUSAGE;
184         ast_mutex_lock(&actionlock);
185         while (cur) { /* Walk the list of actions */
186                 for (num = 3; num < argc; num++) {
187                         if (!strcasecmp(cur->action, argv[num])) {
188                                 ast_cli(fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n", cur->action, cur->synopsis, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->description ? cur->description : "");
189                         }
190                 }
191                 cur = cur->next;
192         }
193
194         ast_mutex_unlock(&actionlock);
195         return RESULT_SUCCESS;
196 }
197
198 /*! \brief  handle_showmancmds: CLI command */
199 /* Should change to "manager show commands" */
200 static int handle_showmancmds(int fd, int argc, char *argv[])
201 {
202         struct manager_action *cur = first_action;
203         char authority[80];
204         char *format = "  %-15.15s  %-15.15s  %-55.55s\n";
205
206         ast_mutex_lock(&actionlock);
207         ast_cli(fd, format, "Action", "Privilege", "Synopsis");
208         ast_cli(fd, format, "------", "---------", "--------");
209         while (cur) { /* Walk the list of actions */
210                 ast_cli(fd, format, cur->action, authority_to_str(cur->authority, authority, sizeof(authority) -1), cur->synopsis);
211                 cur = cur->next;
212         }
213
214         ast_mutex_unlock(&actionlock);
215         return RESULT_SUCCESS;
216 }
217
218 /*! \brief  handle_showmanconn: CLI command show manager connected */
219 /* Should change to "manager show connected" */
220 static int handle_showmanconn(int fd, int argc, char *argv[])
221 {
222         struct mansession *s;
223         char iabuf[INET_ADDRSTRLEN];
224         char *format = "  %-15.15s  %-15.15s\n";
225         ast_mutex_lock(&sessionlock);
226         s = sessions;
227         ast_cli(fd, format, "Username", "IP Address");
228         while (s) {
229                 ast_cli(fd, format,s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
230                 s = s->next;
231         }
232
233         ast_mutex_unlock(&sessionlock);
234         return RESULT_SUCCESS;
235 }
236
237 static char showmancmd_help[] = 
238 "Usage: show manager command <actionname>\n"
239 "       Shows the detailed description for a specific Asterisk manager interface command.\n";
240
241 static char showmancmds_help[] = 
242 "Usage: show manager commands\n"
243 "       Prints a listing of all the available Asterisk manager interface commands.\n";
244
245 static char showmanconn_help[] = 
246 "Usage: show manager connected\n"
247 "       Prints a listing of the users that are currently connected to the\n"
248 "Asterisk manager interface.\n";
249
250 static struct ast_cli_entry show_mancmd_cli =
251         { { "show", "manager", "command", NULL },
252         handle_showmancmd, "Show a manager interface command", showmancmd_help, complete_show_mancmd };
253
254 static struct ast_cli_entry show_mancmds_cli =
255         { { "show", "manager", "commands", NULL },
256         handle_showmancmds, "List manager interface commands", showmancmds_help };
257
258 static struct ast_cli_entry show_manconn_cli =
259         { { "show", "manager", "connected", NULL },
260         handle_showmanconn, "Show connected manager interface users", showmanconn_help };
261
262 static void free_session(struct mansession *s)
263 {
264         struct eventqent *eqe;
265         if (s->fd > -1)
266                 close(s->fd);
267         ast_mutex_destroy(&s->__lock);
268         while(s->eventq) {
269                 eqe = s->eventq;
270                 s->eventq = s->eventq->next;
271                 free(eqe);
272         }
273         free(s);
274 }
275
276 static void destroy_session(struct mansession *s)
277 {
278         struct mansession *cur, *prev = NULL;
279         ast_mutex_lock(&sessionlock);
280         cur = sessions;
281         while(cur) {
282                 if (cur == s)
283                         break;
284                 prev = cur;
285                 cur = cur->next;
286         }
287         if (cur) {
288                 if (prev)
289                         prev->next = cur->next;
290                 else
291                         sessions = cur->next;
292                 free_session(s);
293         } else
294                 ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
295         ast_mutex_unlock(&sessionlock);
296         
297 }
298
299 char *astman_get_header(struct message *m, char *var)
300 {
301         char cmp[80];
302         int x;
303         snprintf(cmp, sizeof(cmp), "%s: ", var);
304         for (x=0;x<m->hdrcount;x++)
305                 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
306                         return m->headers[x] + strlen(cmp);
307         return "";
308 }
309
310 struct ast_variable *astman_get_variables(struct message *m)
311 {
312         int varlen, x, y;
313         struct ast_variable *head = NULL, *cur;
314         char *var, *val;
315         unsigned int var_count;
316         char *vars[32];
317         
318         varlen = strlen("Variable: ");  
319
320         for (x = 0; x < m->hdrcount; x++) {
321                 if (strncasecmp("Variable: ", m->headers[x], varlen))
322                         continue;
323
324                 if (!(var = ast_strdupa(m->headers[x] + varlen)))
325                         return head;
326
327                 if ((var_count = ast_app_separate_args(var, '|', vars, sizeof(vars) / sizeof(var[0])))) {
328                         for (y = 0; y < var_count; y++) {
329                                 var = val = vars[y];
330                                 strsep(&val, "=");
331                                 if (!val || ast_strlen_zero(var))
332                                         continue;
333                                 cur = ast_variable_new(var, val);
334                                 if (head) {
335                                         cur->next = head;
336                                         head = cur;
337                                 } else
338                                         head = cur;
339                         }
340                 }
341         }
342
343         return head;
344 }
345
346 /*! NOTE:
347    Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
348    hold the session lock _or_ be running in an action callback (in which case s->busy will
349    be non-zero). In either of these cases, there is no need to lock-protect the session's
350    fd, since no other output will be sent (events will be queued), and no input will
351    be read until either the current action finishes or get_input() obtains the session
352    lock.
353  */
354 void astman_send_error(struct mansession *s, struct message *m, char *error)
355 {
356         char *id = astman_get_header(m,"ActionID");
357
358         ast_cli(s->fd, "Response: Error\r\n");
359         if (!ast_strlen_zero(id))
360                 ast_cli(s->fd, "ActionID: %s\r\n",id);
361         ast_cli(s->fd, "Message: %s\r\n\r\n", error);
362 }
363
364 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
365 {
366         char *id = astman_get_header(m,"ActionID");
367
368         ast_cli(s->fd, "Response: %s\r\n", resp);
369         if (!ast_strlen_zero(id))
370                 ast_cli(s->fd, "ActionID: %s\r\n",id);
371         if (msg)
372                 ast_cli(s->fd, "Message: %s\r\n\r\n", msg);
373         else
374                 ast_cli(s->fd, "\r\n");
375 }
376
377 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
378 {
379         astman_send_response(s, m, "Success", msg);
380 }
381
382 /*! Tells you if smallstr exists inside bigstr
383    which is delim by delim and uses no buf or stringsep
384    ast_instring("this|that|more","this",',') == 1;
385
386    feel free to move this to app.c -anthm */
387 static int ast_instring(char *bigstr, char *smallstr, char delim) 
388 {
389         char *val = bigstr, *next;
390
391         do {
392                 if ((next = strchr(val, delim))) {
393                         if (!strncmp(val, smallstr, (next - val)))
394                                 return 1;
395                         else
396                                 continue;
397                 } else
398                         return !strcmp(smallstr, val);
399
400         } while (*(val = (next + 1)));
401
402         return 0;
403 }
404
405 static int get_perm(char *instr)
406 {
407         int x = 0, ret = 0;
408
409         if (!instr)
410                 return 0;
411
412         for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
413                 if (ast_instring(instr, perms[x].label, ','))
414                         ret |= perms[x].num;
415         
416         return ret;
417 }
418
419 static int ast_is_number(char *string) 
420 {
421         int ret = 1, x = 0;
422
423         if (!string)
424                 return 0;
425
426         for (x=0; x < strlen(string); x++) {
427                 if (!(string[x] >= 48 && string[x] <= 57)) {
428                         ret = 0;
429                         break;
430                 }
431         }
432         
433         return ret ? atoi(string) : 0;
434 }
435
436 static int ast_strings_to_mask(char *string) 
437 {
438         int x, ret = -1;
439         
440         x = ast_is_number(string);
441
442         if (x) {
443                 ret = x;
444         } else if (ast_strlen_zero(string)) {
445                 ret = -1;
446         } else if (ast_false(string)) {
447                 ret = 0;
448         } else if (ast_true(string)) {
449                 ret = 0;
450                 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
451                         ret |= perms[x].num;            
452         } else {
453                 ret = 0;
454                 for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++) {
455                         if (ast_instring(string, perms[x].label, ',')) 
456                                 ret |= perms[x].num;            
457                 }
458         }
459
460         return ret;
461 }
462
463 /*! 
464    Rather than braindead on,off this now can also accept a specific int mask value 
465    or a ',' delim list of mask strings (the same as manager.conf) -anthm
466 */
467
468 static int set_eventmask(struct mansession *s, char *eventmask)
469 {
470         int maskint = ast_strings_to_mask(eventmask);
471
472         ast_mutex_lock(&s->__lock);
473         if (maskint >= 0)       
474                 s->send_events = maskint;
475         ast_mutex_unlock(&s->__lock);
476         
477         return maskint;
478 }
479
480 static int authenticate(struct mansession *s, struct message *m)
481 {
482         struct ast_config *cfg;
483         char iabuf[INET_ADDRSTRLEN];
484         char *cat;
485         char *user = astman_get_header(m, "Username");
486         char *pass = astman_get_header(m, "Secret");
487         char *authtype = astman_get_header(m, "AuthType");
488         char *key = astman_get_header(m, "Key");
489         char *events = astman_get_header(m, "Events");
490         
491         cfg = ast_config_load("manager.conf");
492         if (!cfg)
493                 return -1;
494         cat = ast_category_browse(cfg, NULL);
495         while(cat) {
496                 if (strcasecmp(cat, "general")) {
497                         /* This is a user */
498                         if (!strcasecmp(cat, user)) {
499                                 struct ast_variable *v;
500                                 struct ast_ha *ha = NULL;
501                                 char *password = NULL;
502                                 v = ast_variable_browse(cfg, cat);
503                                 while (v) {
504                                         if (!strcasecmp(v->name, "secret")) {
505                                                 password = v->value;
506                                         } else if (!strcasecmp(v->name, "permit") ||
507                                                    !strcasecmp(v->name, "deny")) {
508                                                 ha = ast_append_ha(v->name, v->value, ha);
509                                         } else if (!strcasecmp(v->name, "writetimeout")) {
510                                                 int val = atoi(v->value);
511
512                                                 if (val < 100)
513                                                         ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", v->value, v->lineno);
514                                                 else
515                                                         s->writetimeout = val;
516                                         }
517                                                 
518                                         v = v->next;
519                                 }
520                                 if (ha && !ast_apply_ha(ha, &(s->sin))) {
521                                         ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
522                                         ast_free_ha(ha);
523                                         ast_config_destroy(cfg);
524                                         return -1;
525                                 } else if (ha)
526                                         ast_free_ha(ha);
527                                 if (!strcasecmp(authtype, "MD5")) {
528                                         if (!ast_strlen_zero(key) && s->challenge) {
529                                                 int x;
530                                                 int len=0;
531                                                 char md5key[256] = "";
532                                                 struct MD5Context md5;
533                                                 unsigned char digest[16];
534                                                 MD5Init(&md5);
535                                                 MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
536                                                 MD5Update(&md5, (unsigned char *) password, strlen(password));
537                                                 MD5Final(digest, &md5);
538                                                 for (x=0;x<16;x++)
539                                                         len += sprintf(md5key + len, "%2.2x", digest[x]);
540                                                 if (!strcmp(md5key, key))
541                                                         break;
542                                                 else {
543                                                         ast_config_destroy(cfg);
544                                                         return -1;
545                                                 }
546                                         }
547                                 } else if (password && !strcasecmp(password, pass)) {
548                                         break;
549                                 } else {
550                                         ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
551                                         ast_config_destroy(cfg);
552                                         return -1;
553                                 }       
554                         }
555                 }
556                 cat = ast_category_browse(cfg, cat);
557         }
558         if (cat) {
559                 ast_copy_string(s->username, cat, sizeof(s->username));
560                 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
561                 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
562                 ast_config_destroy(cfg);
563                 if (events)
564                         set_eventmask(s, events);
565                 return 0;
566         }
567         ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
568         ast_config_destroy(cfg);
569         return -1;
570 }
571
572 static char mandescr_ping[] = 
573 "Description: A 'Ping' action will ellicit a 'Pong' response.  Used to keep the "
574 "  manager connection open.\n"
575 "Variables: NONE\n";
576
577 static int action_ping(struct mansession *s, struct message *m)
578 {
579         astman_send_response(s, m, "Pong", NULL);
580         return 0;
581 }
582
583 static char mandescr_listcommands[] = 
584 "Description: Returns the action name and synopsis for every\n"
585 "  action that is available to the user\n"
586 "Variables: NONE\n";
587
588 static int action_listcommands(struct mansession *s, struct message *m)
589 {
590         struct manager_action *cur = first_action;
591         char idText[256] = "";
592         char temp[BUFSIZ];
593         char *id = astman_get_header(m,"ActionID");
594
595         if (!ast_strlen_zero(id))
596                 snprintf(idText,256,"ActionID: %s\r\n",id);
597         ast_cli(s->fd, "Response: Success\r\n%s", idText);
598         ast_mutex_lock(&actionlock);
599         while (cur) { /* Walk the list of actions */
600                 if ((s->writeperm & cur->authority) == cur->authority)
601                         ast_cli(s->fd, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, temp, sizeof(temp)) );
602                 cur = cur->next;
603         }
604         ast_mutex_unlock(&actionlock);
605         ast_cli(s->fd, "\r\n");
606
607         return 0;
608 }
609
610 static char mandescr_events[] = 
611 "Description: Enable/Disable sending of events to this manager\n"
612 "  client.\n"
613 "Variables:\n"
614 "       EventMask: 'on' if all events should be sent,\n"
615 "               'off' if no events should be sent,\n"
616 "               'system,call,log' to select which flags events should have to be sent.\n";
617
618 static int action_events(struct mansession *s, struct message *m)
619 {
620         char *mask = astman_get_header(m, "EventMask");
621         int res;
622
623         res = set_eventmask(s, mask);
624         if (res > 0)
625                 astman_send_response(s, m, "Events On", NULL);
626         else if (res == 0)
627                 astman_send_response(s, m, "Events Off", NULL);
628
629         return 0;
630 }
631
632 static char mandescr_logoff[] = 
633 "Description: Logoff this manager session\n"
634 "Variables: NONE\n";
635
636 static int action_logoff(struct mansession *s, struct message *m)
637 {
638         astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
639         return -1;
640 }
641
642 static char mandescr_hangup[] = 
643 "Description: Hangup a channel\n"
644 "Variables: \n"
645 "       Channel: The channel name to be hungup\n";
646
647 static int action_hangup(struct mansession *s, struct message *m)
648 {
649         struct ast_channel *c = NULL;
650         char *name = astman_get_header(m, "Channel");
651         if (ast_strlen_zero(name)) {
652                 astman_send_error(s, m, "No channel specified");
653                 return 0;
654         }
655         c = ast_get_channel_by_name_locked(name);
656         if (!c) {
657                 astman_send_error(s, m, "No such channel");
658                 return 0;
659         }
660         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
661         ast_mutex_unlock(&c->lock);
662         astman_send_ack(s, m, "Channel Hungup");
663         return 0;
664 }
665
666 static char mandescr_setvar[] = 
667 "Description: Set a global or local channel variable.\n"
668 "Variables: (Names marked with * are required)\n"
669 "       Channel: Channel to set variable for\n"
670 "       *Variable: Variable name\n"
671 "       *Value: Value\n";
672
673 static int action_setvar(struct mansession *s, struct message *m)
674 {
675         struct ast_channel *c = NULL;
676         char *name = astman_get_header(m, "Channel");
677         char *varname = astman_get_header(m, "Variable");
678         char *varval = astman_get_header(m, "Value");
679         
680         if (ast_strlen_zero(varname)) {
681                 astman_send_error(s, m, "No variable specified");
682                 return 0;
683         }
684
685         if (!ast_strlen_zero(name)) {
686                 c = ast_get_channel_by_name_locked(name);
687                 if (!c) {
688                         astman_send_error(s, m, "No such channel");
689                         return 0;
690                 }
691         }
692         
693         pbx_builtin_setvar_helper(c,varname,varval);
694           
695         ast_mutex_unlock(&c->lock);
696         astman_send_ack(s, m, "Variable Set");
697
698         return 0;
699 }
700
701 static char mandescr_getvar[] = 
702 "Description: Get the value of a global or local channel variable.\n"
703 "Variables: (Names marked with * are required)\n"
704 "       Channel: Channel to read variable from\n"
705 "       *Variable: Variable name\n"
706 "       ActionID: Optional Action id for message matching.\n";
707
708 static int action_getvar(struct mansession *s, struct message *m)
709 {
710         struct ast_channel *c = NULL;
711         char *name = astman_get_header(m, "Channel");
712         char *varname = astman_get_header(m, "Variable");
713         char *id = astman_get_header(m,"ActionID");
714         char *varval;
715         char *varval2=NULL;
716
717         if (!strlen(varname)) {
718                 astman_send_error(s, m, "No variable specified");
719                 return 0;
720         }
721
722         if (strlen(name)) {
723                 c = ast_get_channel_by_name_locked(name);
724                 if (!c) {
725                         astman_send_error(s, m, "No such channel");
726                         return 0;
727                 }
728         }
729         
730         varval=pbx_builtin_getvar_helper(c,varname);
731         if (varval)
732                 varval2 = ast_strdupa(varval);
733         if (!varval2)
734                 varval2 = "";
735         if (c)
736                 ast_mutex_unlock(&c->lock);
737         ast_cli(s->fd, "Response: Success\r\n"
738                 "Variable: %s\r\nValue: %s\r\n" ,varname,varval2);
739         if (!ast_strlen_zero(id))
740                 ast_cli(s->fd, "ActionID: %s\r\n",id);
741         ast_cli(s->fd, "\r\n");
742
743         return 0;
744 }
745
746
747 /*! \brief  action_status: Manager "status" command to show channels */
748 /* Needs documentation... */
749 static int action_status(struct mansession *s, struct message *m)
750 {
751         char *id = astman_get_header(m,"ActionID");
752         char *name = astman_get_header(m,"Channel");
753         char idText[256] = "";
754         struct ast_channel *c;
755         char bridge[256];
756         struct timeval now = ast_tvnow();
757         long elapsed_seconds=0;
758         int all = ast_strlen_zero(name); /* set if we want all channels */
759
760         astman_send_ack(s, m, "Channel status will follow");
761         if (!ast_strlen_zero(id))
762                 snprintf(idText,256,"ActionID: %s\r\n",id);
763         if (all)
764                 c = ast_channel_walk_locked(NULL);
765         else {
766                 c = ast_get_channel_by_name_locked(name);
767                 if (!c) {
768                         astman_send_error(s, m, "No such channel");
769                         return 0;
770                 }
771         }
772         /* if we look by name, we break after the first iteration */
773         while(c) {
774                 if (c->_bridge)
775                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->_bridge->name);
776                 else
777                         bridge[0] = '\0';
778                 if (c->pbx) {
779                         if (c->cdr) {
780                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
781                         }
782                         ast_cli(s->fd,
783                         "Event: Status\r\n"
784                         "Privilege: Call\r\n"
785                         "Channel: %s\r\n"
786                         "CallerID: %s\r\n"
787                         "CallerIDName: %s\r\n"
788                         "Account: %s\r\n"
789                         "State: %s\r\n"
790                         "Context: %s\r\n"
791                         "Extension: %s\r\n"
792                         "Priority: %d\r\n"
793                         "Seconds: %ld\r\n"
794                         "%s"
795                         "Uniqueid: %s\r\n"
796                         "%s"
797                         "\r\n",
798                         c->name, 
799                         c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
800                         c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
801                         c->accountcode,
802                         ast_state2str(c->_state), c->context,
803                         c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
804                 } else {
805                         ast_cli(s->fd,
806                         "Event: Status\r\n"
807                         "Privilege: Call\r\n"
808                         "Channel: %s\r\n"
809                         "CallerID: %s\r\n"
810                         "CallerIDName: %s\r\n"
811                         "Account: %s\r\n"
812                         "State: %s\r\n"
813                         "%s"
814                         "Uniqueid: %s\r\n"
815                         "%s"
816                         "\r\n",
817                         c->name, 
818                         c->cid.cid_num ? c->cid.cid_num : "<unknown>", 
819                         c->cid.cid_name ? c->cid.cid_name : "<unknown>", 
820                         c->accountcode,
821                         ast_state2str(c->_state), bridge, c->uniqueid, idText);
822                 }
823                 ast_mutex_unlock(&c->lock);
824                 if (!all)
825                         break;
826                 c = ast_channel_walk_locked(c);
827         }
828         ast_cli(s->fd,
829         "Event: StatusComplete\r\n"
830         "%s"
831         "\r\n",idText);
832         return 0;
833 }
834
835 static char mandescr_redirect[] = 
836 "Description: Redirect (transfer) a call.\n"
837 "Variables: (Names marked with * are required)\n"
838 "       *Channel: Channel to redirect\n"
839 "       ExtraChannel: Second call leg to transfer (optional)\n"
840 "       *Exten: Extension to transfer to\n"
841 "       *Context: Context to transfer to\n"
842 "       *Priority: Priority to transfer to\n"
843 "       ActionID: Optional Action id for message matching.\n";
844
845 /*! \brief  action_redirect: The redirect manager command */
846 static int action_redirect(struct mansession *s, struct message *m)
847 {
848         char *name = astman_get_header(m, "Channel");
849         char *name2 = astman_get_header(m, "ExtraChannel");
850         char *exten = astman_get_header(m, "Exten");
851         char *context = astman_get_header(m, "Context");
852         char *priority = astman_get_header(m, "Priority");
853         struct ast_channel *chan, *chan2 = NULL;
854         int pi = 0;
855         int res;
856
857         if (ast_strlen_zero(name)) {
858                 astman_send_error(s, m, "Channel not specified");
859                 return 0;
860         }
861         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
862                 astman_send_error(s, m, "Invalid priority\n");
863                 return 0;
864         }
865         chan = ast_get_channel_by_name_locked(name);
866         if (!chan) {
867                 char buf[BUFSIZ];
868                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
869                 astman_send_error(s, m, buf);
870                 return 0;
871         }
872         if (!ast_strlen_zero(name2))
873                 chan2 = ast_get_channel_by_name_locked(name2);
874         res = ast_async_goto(chan, context, exten, pi);
875         if (!res) {
876                 if (!ast_strlen_zero(name2)) {
877                         if (chan2)
878                                 res = ast_async_goto(chan2, context, exten, pi);
879                         else
880                                 res = -1;
881                         if (!res)
882                                 astman_send_ack(s, m, "Dual Redirect successful");
883                         else
884                                 astman_send_error(s, m, "Secondary redirect failed");
885                 } else
886                         astman_send_ack(s, m, "Redirect successful");
887         } else
888                 astman_send_error(s, m, "Redirect failed");
889         if (chan)
890                 ast_mutex_unlock(&chan->lock);
891         if (chan2)
892                 ast_mutex_unlock(&chan2->lock);
893         return 0;
894 }
895
896 static char mandescr_command[] = 
897 "Description: Run a CLI command.\n"
898 "Variables: (Names marked with * are required)\n"
899 "       *Command: Asterisk CLI command to run\n"
900 "       ActionID: Optional Action id for message matching.\n";
901
902 /*! \brief  action_command: Manager command "command" - execute CLI command */
903 static int action_command(struct mansession *s, struct message *m)
904 {
905         char *cmd = astman_get_header(m, "Command");
906         char *id = astman_get_header(m, "ActionID");
907         ast_cli(s->fd, "Response: Follows\r\nPrivilege: Command\r\n");
908         if (!ast_strlen_zero(id))
909                 ast_cli(s->fd, "ActionID: %s\r\n", id);
910         /* FIXME: Wedge a ActionID response in here, waiting for later changes */
911         ast_cli_command(s->fd, cmd);
912         ast_cli(s->fd, "--END COMMAND--\r\n\r\n");
913         return 0;
914 }
915
916 static void *fast_originate(void *data)
917 {
918         struct fast_originate_helper *in = data;
919         int res;
920         int reason = 0;
921         struct ast_channel *chan = NULL;
922
923         if (!ast_strlen_zero(in->app)) {
924                 res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, 
925                         !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
926                         !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
927                         in->vars, &chan);
928         } else {
929                 res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, 
930                         !ast_strlen_zero(in->cid_num) ? in->cid_num : NULL, 
931                         !ast_strlen_zero(in->cid_name) ? in->cid_name : NULL,
932                         in->vars, &chan);
933         }   
934         if (!res)
935                 manager_event(EVENT_FLAG_CALL,
936                         "OriginateSuccess",
937                         "%s"
938                         "Channel: %s/%s\r\n"
939                         "Context: %s\r\n"
940                         "Exten: %s\r\n"
941                         "Reason: %d\r\n"
942                         "Uniqueid: %s\r\n",
943                         in->idtext, in->tech, in->data, in->context, in->exten, reason, chan ? chan->uniqueid : "<null>");
944         else
945                 manager_event(EVENT_FLAG_CALL,
946                         "OriginateFailure",
947                         "%s"
948                         "Channel: %s/%s\r\n"
949                         "Context: %s\r\n"
950                         "Exten: %s\r\n"
951                         "Reason: %d\r\n"
952                         "Uniqueid: %s\r\n",
953                         in->idtext, in->tech, in->data, in->context, in->exten, reason, chan ? chan->uniqueid : "<null>");
954
955         /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
956         if (chan)
957                 ast_mutex_unlock(&chan->lock);
958         free(in);
959         return NULL;
960 }
961
962 static char mandescr_originate[] = 
963 "Description: Generates an outgoing call to a Extension/Context/Priority or\n"
964 "  Application/Data\n"
965 "Variables: (Names marked with * are required)\n"
966 "       *Channel: Channel name to call\n"
967 "       Exten: Extension to use (requires 'Context' and 'Priority')\n"
968 "       Context: Context to use (requires 'Exten' and 'Priority')\n"
969 "       Priority: Priority to use (requires 'Exten' and 'Context')\n"
970 "       Application: Application to use\n"
971 "       Data: Data to use (requires 'Application')\n"
972 "       Timeout: How long to wait for call to be answered (in ms)\n"
973 "       CallerID: Caller ID to be set on the outgoing channel\n"
974 "       Variable: Channel variable to set, multiple Variable: headers are allowed\n"
975 "       Account: Account code\n"
976 "       Async: Set to 'true' for fast origination\n";
977
978 static int action_originate(struct mansession *s, struct message *m)
979 {
980         char *name = astman_get_header(m, "Channel");
981         char *exten = astman_get_header(m, "Exten");
982         char *context = astman_get_header(m, "Context");
983         char *priority = astman_get_header(m, "Priority");
984         char *timeout = astman_get_header(m, "Timeout");
985         char *callerid = astman_get_header(m, "CallerID");
986         char *account = astman_get_header(m, "Account");
987         char *app = astman_get_header(m, "Application");
988         char *appdata = astman_get_header(m, "Data");
989         char *async = astman_get_header(m, "Async");
990         char *id = astman_get_header(m, "ActionID");
991         struct ast_variable *vars = astman_get_variables(m);
992         char *tech, *data;
993         char *l=NULL, *n=NULL;
994         int pi = 0;
995         int res;
996         int to = 30000;
997         int reason = 0;
998         char tmp[256];
999         char tmp2[256];
1000         
1001         pthread_t th;
1002         pthread_attr_t attr;
1003         if (!name) {
1004                 astman_send_error(s, m, "Channel not specified");
1005                 return 0;
1006         }
1007         if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
1008                 astman_send_error(s, m, "Invalid priority\n");
1009                 return 0;
1010         }
1011         if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
1012                 astman_send_error(s, m, "Invalid timeout\n");
1013                 return 0;
1014         }
1015         ast_copy_string(tmp, name, sizeof(tmp));
1016         tech = tmp;
1017         data = strchr(tmp, '/');
1018         if (!data) {
1019                 astman_send_error(s, m, "Invalid channel\n");
1020                 return 0;
1021         }
1022         *data = '\0';
1023         data++;
1024         ast_copy_string(tmp2, callerid, sizeof(tmp2));
1025         ast_callerid_parse(tmp2, &n, &l);
1026         if (n) {
1027                 if (ast_strlen_zero(n))
1028                         n = NULL;
1029         }
1030         if (l) {
1031                 ast_shrink_phone_number(l);
1032                 if (ast_strlen_zero(l))
1033                         l = NULL;
1034         }
1035         if (account) {
1036                 struct ast_variable *newvar;
1037                 newvar = ast_variable_new("CDR(accountcode|r)", account);
1038                 newvar->next = vars;
1039                 vars = newvar;
1040         }
1041         if (ast_true(async)) {
1042                 struct fast_originate_helper *fast = malloc(sizeof(struct fast_originate_helper));
1043                 if (!fast) {
1044                         res = -1;
1045                 } else {
1046                         memset(fast, 0, sizeof(struct fast_originate_helper));
1047                         if (!ast_strlen_zero(id))
1048                                 snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
1049                         ast_copy_string(fast->tech, tech, sizeof(fast->tech));
1050                         ast_copy_string(fast->data, data, sizeof(fast->data));
1051                         ast_copy_string(fast->app, app, sizeof(fast->app));
1052                         ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
1053                         if (l)
1054                                 ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
1055                         if (n)
1056                                 ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
1057                         fast->vars = vars;      
1058                         ast_copy_string(fast->context, context, sizeof(fast->context));
1059                         ast_copy_string(fast->exten, exten, sizeof(fast->exten));
1060                         fast->timeout = to;
1061                         fast->priority = pi;
1062                         pthread_attr_init(&attr);
1063                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1064                         if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
1065                                 res = -1;
1066                         } else {
1067                                 res = 0;
1068                         }
1069                 }
1070         } else if (!ast_strlen_zero(app)) {
1071                 res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, NULL);
1072         } else {
1073                 if (exten && context && pi)
1074                         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, NULL);
1075                 else {
1076                         astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
1077                         return 0;
1078                 }
1079         }   
1080         if (!res)
1081                 astman_send_ack(s, m, "Originate successfully queued");
1082         else
1083                 astman_send_error(s, m, "Originate failed");
1084         return 0;
1085 }
1086
1087 static char mandescr_mailboxstatus[] = 
1088 "Description: Checks a voicemail account for status.\n"
1089 "Variables: (Names marked with * are required)\n"
1090 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1091 "       ActionID: Optional ActionID for message matching.\n"
1092 "Returns number of messages.\n"
1093 "       Message: Mailbox Status\n"
1094 "       Mailbox: <mailboxid>\n"
1095 "       Waiting: <count>\n"
1096 "\n";
1097 static int action_mailboxstatus(struct mansession *s, struct message *m)
1098 {
1099         char *mailbox = astman_get_header(m, "Mailbox");
1100         char *id = astman_get_header(m,"ActionID");
1101         char idText[256] = "";
1102         int ret;
1103         if (ast_strlen_zero(mailbox)) {
1104                 astman_send_error(s, m, "Mailbox not specified");
1105                 return 0;
1106         }
1107         if (!ast_strlen_zero(id))
1108                 snprintf(idText,256,"ActionID: %s\r\n",id);
1109         ret = ast_app_has_voicemail(mailbox, NULL);
1110         ast_cli(s->fd, "Response: Success\r\n"
1111                                    "%s"
1112                                    "Message: Mailbox Status\r\n"
1113                                    "Mailbox: %s\r\n"
1114                                    "Waiting: %d\r\n\r\n", idText, mailbox, ret);
1115         return 0;
1116 }
1117
1118 static char mandescr_mailboxcount[] = 
1119 "Description: Checks a voicemail account for new messages.\n"
1120 "Variables: (Names marked with * are required)\n"
1121 "       *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
1122 "       ActionID: Optional ActionID for message matching.\n"
1123 "Returns number of new and old messages.\n"
1124 "       Message: Mailbox Message Count\n"
1125 "       Mailbox: <mailboxid>\n"
1126 "       NewMessages: <count>\n"
1127 "       OldMessages: <count>\n"
1128 "\n";
1129 static int action_mailboxcount(struct mansession *s, struct message *m)
1130 {
1131         char *mailbox = astman_get_header(m, "Mailbox");
1132         char *id = astman_get_header(m,"ActionID");
1133         char idText[256] = "";
1134         int newmsgs = 0, oldmsgs = 0;
1135         if (ast_strlen_zero(mailbox)) {
1136                 astman_send_error(s, m, "Mailbox not specified");
1137                 return 0;
1138         }
1139         ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
1140         if (!ast_strlen_zero(id)) {
1141                 snprintf(idText,256,"ActionID: %s\r\n",id);
1142         }
1143         ast_cli(s->fd, "Response: Success\r\n"
1144                                    "%s"
1145                                    "Message: Mailbox Message Count\r\n"
1146                                    "Mailbox: %s\r\n"
1147                                    "NewMessages: %d\r\n"
1148                                    "OldMessages: %d\r\n" 
1149                                    "\r\n",
1150                                     idText,mailbox, newmsgs, oldmsgs);
1151         return 0;
1152 }
1153
1154 static char mandescr_extensionstate[] = 
1155 "Description: Report the extension state for given extension.\n"
1156 "  If the extension has a hint, will use devicestate to check\n"
1157 "  the status of the device connected to the extension.\n"
1158 "Variables: (Names marked with * are required)\n"
1159 "       *Exten: Extension to check state on\n"
1160 "       *Context: Context for extension\n"
1161 "       ActionId: Optional ID for this transaction\n"
1162 "Will return an \"Extension Status\" message.\n"
1163 "The response will include the hint for the extension and the status.\n";
1164
1165 static int action_extensionstate(struct mansession *s, struct message *m)
1166 {
1167         char *exten = astman_get_header(m, "Exten");
1168         char *context = astman_get_header(m, "Context");
1169         char *id = astman_get_header(m,"ActionID");
1170         char idText[256] = "";
1171         char hint[256] = "";
1172         int status;
1173         if (ast_strlen_zero(exten)) {
1174                 astman_send_error(s, m, "Extension not specified");
1175                 return 0;
1176         }
1177         if (ast_strlen_zero(context))
1178                 context = "default";
1179         status = ast_extension_state(NULL, context, exten);
1180         ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
1181         if (!ast_strlen_zero(id)) {
1182                 snprintf(idText,256,"ActionID: %s\r\n",id);
1183         }
1184         ast_cli(s->fd, "Response: Success\r\n"
1185                                    "%s"
1186                                    "Message: Extension Status\r\n"
1187                                    "Exten: %s\r\n"
1188                                    "Context: %s\r\n"
1189                                    "Hint: %s\r\n"
1190                                    "Status: %d\r\n\r\n",
1191                                    idText,exten, context, hint, status);
1192         return 0;
1193 }
1194
1195 static char mandescr_timeout[] = 
1196 "Description: Hangup a channel after a certain time.\n"
1197 "Variables: (Names marked with * are required)\n"
1198 "       *Channel: Channel name to hangup\n"
1199 "       *Timeout: Maximum duration of the call (sec)\n"
1200 "Acknowledges set time with 'Timeout Set' message\n";
1201
1202 static int action_timeout(struct mansession *s, struct message *m)
1203 {
1204         struct ast_channel *c = NULL;
1205         char *name = astman_get_header(m, "Channel");
1206         int timeout = atoi(astman_get_header(m, "Timeout"));
1207         if (ast_strlen_zero(name)) {
1208                 astman_send_error(s, m, "No channel specified");
1209                 return 0;
1210         }
1211         if (!timeout) {
1212                 astman_send_error(s, m, "No timeout specified");
1213                 return 0;
1214         }
1215         c = ast_get_channel_by_name_locked(name);
1216         if (!c) {
1217                 astman_send_error(s, m, "No such channel");
1218                 return 0;
1219         }
1220         ast_channel_setwhentohangup(c, timeout);
1221         ast_mutex_unlock(&c->lock);
1222         astman_send_ack(s, m, "Timeout Set");
1223         return 0;
1224 }
1225
1226 static int process_message(struct mansession *s, struct message *m)
1227 {
1228         char action[80] = "";
1229         struct manager_action *tmp = first_action;
1230         char *id = astman_get_header(m,"ActionID");
1231         char idText[256] = "";
1232         char iabuf[INET_ADDRSTRLEN];
1233
1234         ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
1235         ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
1236
1237         if (ast_strlen_zero(action)) {
1238                 astman_send_error(s, m, "Missing action in request");
1239                 return 0;
1240         }
1241         if (!ast_strlen_zero(id)) {
1242                 snprintf(idText,256,"ActionID: %s\r\n",id);
1243         }
1244         if (!s->authenticated) {
1245                 if (!strcasecmp(action, "Challenge")) {
1246                         char *authtype;
1247                         authtype = astman_get_header(m, "AuthType");
1248                         if (!strcasecmp(authtype, "MD5")) {
1249                                 if (ast_strlen_zero(s->challenge))
1250                                         snprintf(s->challenge, sizeof(s->challenge), "%d", rand());
1251                                 ast_mutex_lock(&s->__lock);
1252                                 ast_cli(s->fd, "Response: Success\r\n"
1253                                                 "%s"
1254                                                 "Challenge: %s\r\n\r\n",
1255                                                 idText,s->challenge);
1256                                 ast_mutex_unlock(&s->__lock);
1257                                 return 0;
1258                         } else {
1259                                 astman_send_error(s, m, "Must specify AuthType");
1260                                 return 0;
1261                         }
1262                 } else if (!strcasecmp(action, "Login")) {
1263                         if (authenticate(s, m)) {
1264                                 sleep(1);
1265                                 astman_send_error(s, m, "Authentication failed");
1266                                 return -1;
1267                         } else {
1268                                 s->authenticated = 1;
1269                                 if (option_verbose > 1) {
1270                                         if ( displayconnects ) {
1271                                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1272                                         }
1273                                 }
1274                                 ast_log(LOG_EVENT, "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1275                                 astman_send_ack(s, m, "Authentication accepted");
1276                         }
1277                 } else if (!strcasecmp(action, "Logoff")) {
1278                         astman_send_ack(s, m, "See ya");
1279                         return -1;
1280                 } else
1281                         astman_send_error(s, m, "Authentication Required");
1282         } else {
1283                 int ret=0;
1284                 struct eventqent *eqe;
1285                 ast_mutex_lock(&s->__lock);
1286                 s->busy = 1;
1287                 ast_mutex_unlock(&s->__lock);
1288                 while( tmp ) {          
1289                         if (!strcasecmp(action, tmp->action)) {
1290                                 if ((s->writeperm & tmp->authority) == tmp->authority) {
1291                                         if (tmp->func(s, m))
1292                                                 ret = -1;
1293                                 } else {
1294                                         astman_send_error(s, m, "Permission denied");
1295                                 }
1296                                 break;
1297                         }
1298                         tmp = tmp->next;
1299                 }
1300                 if (!tmp)
1301                         astman_send_error(s, m, "Invalid/unknown command");
1302                 ast_mutex_lock(&s->__lock);
1303                 s->busy = 0;
1304                 while(s->eventq) {
1305                         if (ast_carefulwrite(s->fd, s->eventq->eventdata, strlen(s->eventq->eventdata), s->writetimeout) < 0) {
1306                                 ret = -1;
1307                                 break;
1308                         }
1309                         eqe = s->eventq;
1310                         s->eventq = s->eventq->next;
1311                         free(eqe);
1312                 }
1313                 ast_mutex_unlock(&s->__lock);
1314                 return ret;
1315         }
1316         return 0;
1317 }
1318
1319 static int get_input(struct mansession *s, char *output)
1320 {
1321         /* output must have at least sizeof(s->inbuf) space */
1322         int res;
1323         int x;
1324         struct pollfd fds[1];
1325         char iabuf[INET_ADDRSTRLEN];
1326         for (x=1;x<s->inlen;x++) {
1327                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
1328                         /* Copy output data up to and including \r\n */
1329                         memcpy(output, s->inbuf, x + 1);
1330                         /* Add trailing \0 */
1331                         output[x+1] = '\0';
1332                         /* Move remaining data back to the front */
1333                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
1334                         s->inlen -= (x + 1);
1335                         return 1;
1336                 }
1337         } 
1338         if (s->inlen >= sizeof(s->inbuf) - 1) {
1339                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf);
1340                 s->inlen = 0;
1341         }
1342         fds[0].fd = s->fd;
1343         fds[0].events = POLLIN;
1344         do {
1345                 res = poll(fds, 1, -1);
1346                 if (res < 0) {
1347                         if (errno == EINTR) {
1348                                 if (s->dead)
1349                                         return -1;
1350                                 continue;
1351                         }
1352                         ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
1353                         return -1;
1354                 } else if (res > 0) {
1355                         ast_mutex_lock(&s->__lock);
1356                         res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
1357                         ast_mutex_unlock(&s->__lock);
1358                         if (res < 1)
1359                                 return -1;
1360                         break;
1361                 }
1362         } while(1);
1363         s->inlen += res;
1364         s->inbuf[s->inlen] = '\0';
1365         return 0;
1366 }
1367
1368 static void *session_do(void *data)
1369 {
1370         struct mansession *s = data;
1371         struct message m;
1372         char iabuf[INET_ADDRSTRLEN];
1373         int res;
1374         
1375         ast_mutex_lock(&s->__lock);
1376         ast_cli(s->fd, "Asterisk Call Manager/1.0\r\n");
1377         ast_mutex_unlock(&s->__lock);
1378         memset(&m, 0, sizeof(m));
1379         for (;;) {
1380                 res = get_input(s, m.headers[m.hdrcount]);
1381                 if (res > 0) {
1382                         /* Strip trailing \r\n */
1383                         if (strlen(m.headers[m.hdrcount]) < 2)
1384                                 continue;
1385                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
1386                         if (ast_strlen_zero(m.headers[m.hdrcount])) {
1387                                 if (process_message(s, &m))
1388                                         break;
1389                                 memset(&m, 0, sizeof(m));
1390                         } else if (m.hdrcount < MAX_HEADERS - 1)
1391                                 m.hdrcount++;
1392                 } else if (res < 0)
1393                         break;
1394         }
1395         if (s->authenticated) {
1396                 if (option_verbose > 1) {
1397                         if (displayconnects) 
1398                                 ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));    
1399                 }
1400                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1401         } else {
1402                 if (option_verbose > 1) {
1403                         if ( displayconnects )
1404                                 ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1405                 }
1406                 ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
1407         }
1408         destroy_session(s);
1409         return NULL;
1410 }
1411
1412 static void *accept_thread(void *ignore)
1413 {
1414         int as;
1415         struct sockaddr_in sin;
1416         socklen_t sinlen;
1417         struct mansession *s;
1418         struct protoent *p;
1419         int arg = 1;
1420         int flags;
1421         pthread_attr_t attr;
1422
1423         pthread_attr_init(&attr);
1424         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1425
1426         for (;;) {
1427                 sinlen = sizeof(sin);
1428                 as = accept(asock, (struct sockaddr *)&sin, &sinlen);
1429                 if (as < 0) {
1430                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
1431                         continue;
1432                 }
1433                 p = getprotobyname("tcp");
1434                 if (p) {
1435                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
1436                                 ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
1437                         }
1438                 }
1439                 s = malloc(sizeof(struct mansession));
1440                 if (!s) {
1441                         ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
1442                         continue;
1443                 } 
1444                 memset(s, 0, sizeof(struct mansession));
1445                 memcpy(&s->sin, &sin, sizeof(sin));
1446                 s->writetimeout = 100;
1447
1448                 if(! block_sockets) {
1449                         /* For safety, make sure socket is non-blocking */
1450                         flags = fcntl(as, F_GETFL);
1451                         fcntl(as, F_SETFL, flags | O_NONBLOCK);
1452                 }
1453                 ast_mutex_init(&s->__lock);
1454                 s->fd = as;
1455                 s->send_events = -1;
1456                 ast_mutex_lock(&sessionlock);
1457                 s->next = sessions;
1458                 sessions = s;
1459                 ast_mutex_unlock(&sessionlock);
1460                 if (ast_pthread_create(&s->t, &attr, session_do, s))
1461                         destroy_session(s);
1462         }
1463         pthread_attr_destroy(&attr);
1464         return NULL;
1465 }
1466
1467 static int append_event(struct mansession *s, const char *str)
1468 {
1469         struct eventqent *tmp, *prev=NULL;
1470         tmp = malloc(sizeof(struct eventqent) + strlen(str));
1471         if (tmp) {
1472                 tmp->next = NULL;
1473                 strcpy(tmp->eventdata, str);
1474                 if (s->eventq) {
1475                         prev = s->eventq;
1476                         while(prev->next) 
1477                                 prev = prev->next;
1478                         prev->next = tmp;
1479                 } else {
1480                         s->eventq = tmp;
1481                 }
1482                 return 0;
1483         }
1484         return -1;
1485 }
1486
1487 /*! \brief  manager_event: Send AMI event to client */
1488 int manager_event(int category, char *event, char *fmt, ...)
1489 {
1490         struct mansession *s;
1491         char auth[80];
1492         char tmp[4096] = "";
1493         char *tmp_next = tmp;
1494         size_t tmp_left = sizeof(tmp) - 2;
1495         va_list ap;
1496
1497         ast_mutex_lock(&sessionlock);
1498         for (s = sessions; s; s = s->next) {
1499                 if ((s->readperm & category) != category)
1500                         continue;
1501
1502                 if ((s->send_events & category) != category)
1503                         continue;
1504
1505                 if (ast_strlen_zero(tmp)) {
1506                         ast_build_string(&tmp_next, &tmp_left, "Event: %s\r\nPrivilege: %s\r\n",
1507                                          event, authority_to_str(category, auth, sizeof(auth)));
1508                         va_start(ap, fmt);
1509                         ast_build_string_va(&tmp_next, &tmp_left, fmt, ap);
1510                         va_end(ap);
1511                         *tmp_next++ = '\r';
1512                         *tmp_next++ = '\n';
1513                         *tmp_next = '\0';
1514                 }
1515
1516                 ast_mutex_lock(&s->__lock);
1517                 if (s->busy) {
1518                         append_event(s, tmp);
1519                 } else if (!s->dead) {
1520                         if (ast_carefulwrite(s->fd, tmp, tmp_next - tmp, s->writetimeout) < 0) {
1521                                 ast_log(LOG_WARNING, "Disconnecting slow (or gone) manager session!\n");
1522                                 s->dead = 1;
1523                                 pthread_kill(s->t, SIGURG);
1524                         }
1525                 }
1526                 ast_mutex_unlock(&s->__lock);
1527         }
1528         ast_mutex_unlock(&sessionlock);
1529
1530         return 0;
1531 }
1532
1533 int ast_manager_unregister( char *action ) 
1534 {
1535         struct manager_action *cur = first_action, *prev = first_action;
1536
1537         ast_mutex_lock(&actionlock);
1538         while( cur ) {          
1539                 if (!strcasecmp(action, cur->action)) {
1540                         prev->next = cur->next;
1541                         free(cur);
1542                         if (option_verbose > 1) 
1543                                 ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
1544                         ast_mutex_unlock(&actionlock);
1545                         return 0;
1546                 }
1547                 prev = cur;
1548                 cur = cur->next;
1549         }
1550         ast_mutex_unlock(&actionlock);
1551         return 0;
1552 }
1553
1554 static int manager_state_cb(char *context, char *exten, int state, void *data)
1555 {
1556         /* Notify managers of change */
1557         manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
1558         return 0;
1559 }
1560
1561 static int ast_manager_register_struct(struct manager_action *act)
1562 {
1563         struct manager_action *cur = first_action, *prev = NULL;
1564         int ret;
1565
1566         ast_mutex_lock(&actionlock);
1567         while(cur) { /* Walk the list of actions */
1568                 ret = strcasecmp(cur->action, act->action);
1569                 if (ret == 0) {
1570                         ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
1571                         ast_mutex_unlock(&actionlock);
1572                         return -1;
1573                 } else if (ret > 0) {
1574                         /* Insert these alphabetically */
1575                         if (prev) {
1576                                 act->next = prev->next;
1577                                 prev->next = act;
1578                         } else {
1579                                 act->next = first_action;
1580                                 first_action = act;
1581                         }
1582                         break;
1583                 }
1584                 prev = cur; 
1585                 cur = cur->next;
1586         }
1587         
1588         if (!cur) {
1589                 if (prev)
1590                         prev->next = act;
1591                 else
1592                         first_action = act;
1593                 act->next = NULL;
1594         }
1595
1596         if (option_verbose > 1) 
1597                 ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", act->action);
1598         ast_mutex_unlock(&actionlock);
1599         return 0;
1600 }
1601
1602 int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, struct message *m), const char *synopsis, const char *description)
1603 {
1604         struct manager_action *cur;
1605
1606         cur = malloc(sizeof(struct manager_action));
1607         if (!cur) {
1608                 ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
1609                 ast_mutex_unlock(&actionlock);
1610                 return -1;
1611         }
1612         cur->action = action;
1613         cur->authority = auth;
1614         cur->func = func;
1615         cur->synopsis = synopsis;
1616         cur->description = description;
1617         cur->next = NULL;
1618
1619         ast_manager_register_struct(cur);
1620
1621         return 0;
1622 }
1623
1624 static int registered = 0;
1625
1626 int init_manager(void)
1627 {
1628         struct ast_config *cfg;
1629         char *val;
1630         int oldportno = portno;
1631         static struct sockaddr_in ba;
1632         int x = 1;
1633         if (!registered) {
1634                 /* Register default actions */
1635                 ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
1636                 ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
1637                 ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
1638                 ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
1639                 ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
1640                 ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
1641                 ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
1642                 ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
1643                 ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
1644                 ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
1645                 ast_manager_register2("ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
1646                 ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
1647                 ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
1648                 ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
1649                 ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
1650
1651                 ast_cli_register(&show_mancmd_cli);
1652                 ast_cli_register(&show_mancmds_cli);
1653                 ast_cli_register(&show_manconn_cli);
1654                 ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
1655                 registered = 1;
1656         }
1657         portno = DEFAULT_MANAGER_PORT;
1658         displayconnects = 1;
1659         cfg = ast_config_load("manager.conf");
1660         if (!cfg) {
1661                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
1662                 return 0;
1663         }
1664         memset(&ba, 0, sizeof(ba));
1665         val = ast_variable_retrieve(cfg, "general", "enabled");
1666         if (val)
1667                 enabled = ast_true(val);
1668
1669         val = ast_variable_retrieve(cfg, "general", "block-sockets");
1670         if(val)
1671                 block_sockets = ast_true(val);
1672
1673         if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
1674                 if (sscanf(val, "%d", &portno) != 1) {
1675                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
1676                         portno = DEFAULT_MANAGER_PORT;
1677                 }
1678         } else if ((val = ast_variable_retrieve(cfg, "general", "portno"))) {
1679                 if (sscanf(val, "%d", &portno) != 1) {
1680                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
1681                         portno = DEFAULT_MANAGER_PORT;
1682                 }
1683                 ast_log(LOG_NOTICE, "Use of portno in manager.conf deprecated.  Please use 'port=%s' instead.\n", val);
1684         }
1685         /* Parsing the displayconnects */
1686         if ((val = ast_variable_retrieve(cfg, "general", "displayconnects"))) {
1687                         displayconnects = ast_true(val);;
1688         }
1689                                 
1690         
1691         ba.sin_family = AF_INET;
1692         ba.sin_port = htons(portno);
1693         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
1694         
1695         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
1696                 if (!inet_aton(val, &ba.sin_addr)) { 
1697                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
1698                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
1699                 }
1700         }
1701         
1702         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
1703 #if 0
1704                 /* Can't be done yet */
1705                 close(asock);
1706                 asock = -1;
1707 #else
1708                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
1709 #endif
1710         }
1711         ast_config_destroy(cfg);
1712         
1713         /* If not enabled, do nothing */
1714         if (!enabled) {
1715                 return 0;
1716         }
1717         if (asock < 0) {
1718                 asock = socket(AF_INET, SOCK_STREAM, 0);
1719                 if (asock < 0) {
1720                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
1721                         return -1;
1722                 }
1723                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
1724                 if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
1725                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
1726                         close(asock);
1727                         asock = -1;
1728                         return -1;
1729                 }
1730                 if (listen(asock, 2)) {
1731                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
1732                         close(asock);
1733                         asock = -1;
1734                         return -1;
1735                 }
1736                 if (option_verbose)
1737                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
1738                 ast_pthread_create(&t, NULL, accept_thread, NULL);
1739         }
1740         return 0;
1741 }
1742
1743 int reload_manager(void)
1744 {
1745         manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
1746         return init_manager();
1747 }