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