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