2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <sys/socket.h>
32 #include <sys/select.h>
40 #include "asterisk/md5.h"
41 #include "asterisk/manager.h"
42 #include "asterisk/linkedlists.h"
46 #define MAX_HEADERS 80
50 * 2005.05.27 - different versions of newt define the type of the buffer
51 * for the 5th argument to newtEntry() as char ** or const char ** . To
52 * let the code compile cleanly with -Werror, we cast it to void * through
55 #define _NEWT_CAST (void *)
57 static struct ast_mansession {
58 struct sockaddr_in sin;
71 AST_LIST_ENTRY(ast_chan) list;
74 static AST_LIST_HEAD_NOLOCK_STATIC(chans, ast_chan);
76 /* dummy functions to be compatible with the Asterisk core for md5.c */
77 void ast_register_file_version(const char *file, const char *version);
78 void ast_register_file_version(const char *file, const char *version)
82 void ast_unregister_file_version(const char *file);
83 void ast_unregister_file_version(const char *file)
87 static struct ast_chan *find_chan(char *name)
89 struct ast_chan *chan;
90 AST_LIST_TRAVERSE(&chans, chan, list) {
91 if (!strcmp(name, chan->name))
94 chan = malloc(sizeof(struct ast_chan));
96 memset(chan, 0, sizeof(struct ast_chan));
97 strncpy(chan->name, name, sizeof(chan->name) - 1);
98 AST_LIST_INSERT_TAIL(&chans, chan, list);
103 static void del_chan(char *name)
105 struct ast_chan *chan;
106 AST_LIST_TRAVERSE_SAFE_BEGIN(&chans, chan, list) {
107 if (!strcmp(name, chan->name)) {
108 AST_LIST_REMOVE_CURRENT(&chans, list);
113 AST_LIST_TRAVERSE_SAFE_END
116 static void fdprintf(int fd, char *fmt, ...)
121 vsnprintf(stuff, sizeof(stuff), fmt, ap);
123 write(fd, stuff, strlen(stuff));
126 static char *get_header(struct message *m, char *var)
130 snprintf(cmp, sizeof(cmp), "%s: ", var);
131 for (x=0;x<m->hdrcount;x++)
132 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
133 return m->headers[x] + strlen(cmp);
137 static int event_newstate(struct ast_mansession *s, struct message *m)
139 struct ast_chan *chan;
140 chan = find_chan(get_header(m, "Channel"));
141 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
145 static int event_newexten(struct ast_mansession *s, struct message *m)
147 struct ast_chan *chan;
148 chan = find_chan(get_header(m, "Channel"));
149 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
150 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
151 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
155 static int event_newchannel(struct ast_mansession *s, struct message *m)
157 struct ast_chan *chan;
158 chan = find_chan(get_header(m, "Channel"));
159 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
160 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
164 static int event_status(struct ast_mansession *s, struct message *m)
166 struct ast_chan *chan;
167 chan = find_chan(get_header(m, "Channel"));
168 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
169 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
170 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
171 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
172 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
176 static int event_hangup(struct ast_mansession *s, struct message *m)
178 del_chan(get_header(m, "Channel"));
182 static int event_ignore(struct ast_mansession *s, struct message *m)
187 static int event_rename(struct ast_mansession *s, struct message *m)
189 struct ast_chan *chan;
190 chan = find_chan(get_header(m, "Oldname"));
191 strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
194 static struct event {
196 int (*func)(struct ast_mansession *s, struct message *m);
198 { "Newstate", event_newstate },
199 { "Newchannel", event_newchannel },
200 { "Newexten", event_newexten },
201 { "Hangup", event_hangup },
202 { "Rename", event_rename },
203 { "Status", event_status },
204 { "Link", event_ignore },
205 { "Unlink", event_ignore },
206 { "StatusComplete", event_ignore }
209 static int process_message(struct ast_mansession *s, struct message *m)
213 strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
214 if (!strlen(event)) {
215 fprintf(stderr, "Missing event in request");
218 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
219 if (!strcasecmp(event, events[x].event)) {
220 if (events[x].func(s, m))
225 if (x >= sizeof(events) / sizeof(events[0]))
226 fprintf(stderr, "Ignoring unknown event '%s'", event);
228 for (x=0;x<m->hdrcount;x++) {
229 printf("Header: %s\n", m->headers[x]);
235 static void rebuild_channels(newtComponent c)
238 struct ast_chan *chan;
242 prev = newtListboxGetCurrent(c);
244 AST_LIST_TRAVERSE(&chans, chan, list) {
245 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
246 if (strlen(chan->exten))
247 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
249 chan->exten, chan->context, chan->priority);
251 snprintf(tmp, sizeof(tmp), "%-30s %8s",
253 newtListboxAppendEntry(c, tmp, chan);
257 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
258 newtListboxSetCurrentByKey(c, prev);
261 static int has_input(struct ast_mansession *s)
264 for (x=1;x<s->inlen;x++)
265 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
270 static int get_input(struct ast_mansession *s, char *output)
272 /* output must have at least sizeof(s->inbuf) space */
275 struct timeval tv = {0, 0};
277 for (x=1;x<s->inlen;x++) {
278 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
279 /* Copy output data up to and including \r\n */
280 memcpy(output, s->inbuf, x + 1);
281 /* Add trailing \0 */
283 /* Move remaining data back to the front */
284 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
289 if (s->inlen >= sizeof(s->inbuf) - 1) {
290 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
295 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
297 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
298 } else if (res > 0) {
299 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
303 s->inbuf[s->inlen] = '\0';
310 static int input_check(struct ast_mansession *s, struct message **mout)
312 static struct message m;
319 res = get_input(s, m.headers[m.hdrcount]);
322 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
325 /* Strip trailing \r\n */
326 if (strlen(m.headers[m.hdrcount]) < 2)
328 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
329 if (!strlen(m.headers[m.hdrcount])) {
330 if (mout && strlen(get_header(&m, "Response"))) {
334 if (process_message(s, &m))
336 memset(&m, 0, sizeof(&m));
337 } else if (m.hdrcount < MAX_HEADERS - 1)
339 } else if (res < 0) {
347 static struct message *wait_for_response(int timeout)
354 tv.tv_sec = timeout / 1000;
355 tv.tv_usec = (timeout % 1000) * 1000;
356 FD_SET(session.fd, &fds);
357 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
360 if (input_check(&session, &m) < 0) {
369 static int manager_action(char *action, char *fmt, ...)
371 struct ast_mansession *s;
376 fdprintf(s->fd, "Action: %s\r\n", action);
378 vsnprintf(tmp, sizeof(tmp), fmt, ap);
380 write(s->fd, tmp, strlen(tmp));
381 fdprintf(s->fd, "\r\n");
385 static int show_message(char *title, char *msg)
390 struct newtExitStruct es;
392 newtCenteredWindow(60,7, title);
394 label = newtLabel(4,1,msg);
395 ok = newtButton(27, 3, "OK");
396 form = newtForm(NULL, NULL, 0);
397 newtFormAddComponents(form, label, ok, NULL);
398 newtFormRun(form, &es);
400 newtFormDestroy(form);
404 static newtComponent showform;
405 static int show_doing(char *title, char *tmp)
407 struct newtExitStruct es;
409 showform = newtForm(NULL, NULL, 0);
410 newtCenteredWindow(70,4, title);
411 label = newtLabel(3,1,tmp);
412 newtFormAddComponents(showform,label, NULL);
413 newtFormSetTimer(showform, 200);
414 newtFormRun(showform, &es);
418 static int hide_doing(void)
421 newtFormDestroy(showform);
425 static void try_status(void)
428 manager_action("Status", "");
429 m = wait_for_response(10000);
431 show_message("Status Failed", "Timeout waiting for response");
432 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
433 show_message("Status Failed Failed", get_header(m, "Message"));
438 static void try_hangup(newtComponent c)
440 struct ast_chan *chan;
443 chan = newtListboxGetCurrent(c);
445 manager_action("Hangup", "Channel: %s\r\n", chan->name);
446 m = wait_for_response(10000);
448 show_message("Hangup Failed", "Timeout waiting for response");
449 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
450 show_message("Hangup Failed", get_header(m, "Message"));
456 static int get_user_input(char *msg, char *buf, int buflen)
460 newtComponent cancel;
461 newtComponent inpfield;
464 struct newtExitStruct es;
466 newtCenteredWindow(60,7, msg);
468 inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0);
469 ok = newtButton(22, 3, "OK");
470 cancel = newtButton(32, 3, "Cancel");
471 form = newtForm(NULL, NULL, 0);
472 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
473 newtFormRun(form, &es);
474 strncpy(buf, input, buflen - 1);
480 newtFormDestroy(form);
484 static void try_redirect(newtComponent c)
486 struct ast_chan *chan;
493 chan = newtListboxGetCurrent(c);
495 strncpy(channame, chan->name, sizeof(channame) - 1);
496 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
497 if (get_user_input(tmp, dest, sizeof(dest)))
499 if ((context = strchr(dest, '@'))) {
502 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
504 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
506 m = wait_for_response(10000);
508 show_message("Hangup Failed", "Timeout waiting for response");
509 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
510 show_message("Hangup Failed", get_header(m, "Message"));
516 static int manage_calls(char *host)
520 newtComponent hangup;
521 newtComponent redirect;
522 newtComponent channels;
523 struct newtExitStruct es;
526 /* If there's one thing you learn from this code, it is this...
527 Never, ever fly Air France. Their customer service is absolutely
528 the worst. I've never heard the words "That's not my problem" as
529 many times as I have from their staff -- It should, without doubt
530 be their corporate motto if it isn't already. Don't bother giving
531 them business because you're just a pain in their side and they
532 will be sure to let you know the first time you speak to them.
534 If you ever want to make me happy just tell me that you, too, will
535 never fly Air France again either (in spite of their excellent
537 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
538 newtCenteredWindow(74, 20, tmp);
539 form = newtForm(NULL, NULL, 0);
540 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
541 newtFormSetTimer(form, 100);
542 quit = newtButton(62, 16, "Quit");
543 redirect = newtButton(35, 16, "Redirect");
544 hangup = newtButton(50, 16, "Hangup");
545 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
546 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
547 newtListboxSetWidth(channels, 72);
549 show_doing("Getting Status", "Retrieving system status...");
554 newtFormRun(form, &es);
555 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
556 if (input_check(&session, NULL)) {
557 show_message("Disconnected", "Disconnected from remote host");
560 } else if (es.reason == NEWT_EXIT_COMPONENT) {
563 if (es.u.co == hangup) {
564 try_hangup(channels);
565 } else if (es.u.co == redirect) {
566 try_redirect(channels);
569 rebuild_channels(channels);
571 newtFormDestroy(form);
575 static int login(char *hostname)
578 newtComponent cancel;
580 newtComponent username;
581 newtComponent password;
583 newtComponent ulabel;
584 newtComponent plabel;
588 struct newtExitStruct es;
593 session.fd = socket(AF_INET, SOCK_STREAM, 0);
594 if (session.fd < 0) {
595 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
596 show_message("Socket failed", tmp);
600 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
601 show_doing("Connecting....", tmp);
604 hp = gethostbyname(hostname);
606 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
607 show_message("Host lookup failed", tmp);
611 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
612 show_doing("Connecting...", tmp);
614 session.sin.sin_family = AF_INET;
615 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
616 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
618 if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
619 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
620 show_message("Connect Failed", tmp);
626 login = newtButton(5, 6, "Login");
627 cancel = newtButton(25, 6, "Cancel");
628 newtCenteredWindow(40, 10, "Asterisk Manager Login");
629 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
630 label = newtLabel(4,1, tmp);
632 ulabel = newtLabel(4,2,"Username:");
633 plabel = newtLabel(4,3,"Password:");
635 username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
636 password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
638 form = newtForm(NULL, NULL, 0);
639 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
640 newtFormRun(form, &es);
641 if (es.reason == NEWT_EXIT_COMPONENT) {
642 if (es.u.co == login) {
643 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
644 show_doing("Logging in", tmp);
645 /* Check to see if the remote host supports MD5 Authentication */
646 manager_action("Challenge", "AuthType: MD5\r\n");
647 m = wait_for_response(10000);
648 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
649 char *challenge = get_header(m, "Challenge");
652 char md5key[256] = "";
653 struct MD5Context md5;
654 unsigned char digest[16];
656 MD5Update(&md5, (unsigned char *)challenge, strlen(challenge));
657 MD5Update(&md5, (unsigned char *)pass, strlen(pass));
658 MD5Final(digest, &md5);
660 len += sprintf(md5key + len, "%2.2x", digest[x]);
661 manager_action("Login",
666 m = wait_for_response(10000);
668 if (!strcasecmp(get_header(m, "Response"), "Success")) {
671 show_message("Login Failed", get_header(m, "Message"));
674 memset(m, 0, sizeof(m));
675 manager_action("Login",
679 m = wait_for_response(10000);
682 if (!strcasecmp(get_header(m, "Response"), "Success")) {
685 show_message("Login Failed", get_header(m, "Message"));
691 newtFormDestroy(form);
695 int main(int argc, char *argv[])
698 fprintf(stderr, "Usage: astman <host>\n");
703 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
704 newtPushHelpLine("Welcome to the Asterisk Manager!");
705 if (login(argv[1])) {
709 manage_calls(argv[1]);