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