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"
45 #define MAX_HEADERS 80
49 * 2005.05.27 - different versions of newt define the type of the buffer
50 * for the 5th argument to newtEntry() as char ** or const char ** . To
51 * let the code compile cleanly with -Werror, we cast it to void * through
54 #define _NEWT_CAST (void *)
56 static struct ast_mansession {
57 struct sockaddr_in sin;
63 static struct ast_chan {
70 struct ast_chan *next;
73 /* dummy functions to be compatible with the Asterisk core for md5.c */
74 void ast_register_file_version(const char *file, const char *version);
75 void ast_register_file_version(const char *file, const char *version)
79 void ast_unregister_file_version(const char *file);
80 void ast_unregister_file_version(const char *file)
84 static struct ast_chan *find_chan(char *name)
86 struct ast_chan *prev = NULL, *chan = chans;
88 if (!strcmp(name, chan->name))
93 chan = malloc(sizeof(struct ast_chan));
95 memset(chan, 0, sizeof(struct ast_chan));
96 strncpy(chan->name, name, sizeof(chan->name) - 1);
105 static void del_chan(char *name)
107 struct ast_chan *prev = NULL, *chan = chans;
109 if (!strcmp(name, chan->name)) {
111 prev->next = chan->next;
121 static void fdprintf(int fd, char *fmt, ...)
126 vsnprintf(stuff, sizeof(stuff), fmt, ap);
128 write(fd, stuff, strlen(stuff));
131 static char *get_header(struct message *m, char *var)
135 snprintf(cmp, sizeof(cmp), "%s: ", var);
136 for (x=0;x<m->hdrcount;x++)
137 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
138 return m->headers[x] + strlen(cmp);
142 static int event_newstate(struct ast_mansession *s, struct message *m)
144 struct ast_chan *chan;
145 chan = find_chan(get_header(m, "Channel"));
146 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
150 static int event_newexten(struct ast_mansession *s, struct message *m)
152 struct ast_chan *chan;
153 chan = find_chan(get_header(m, "Channel"));
154 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
155 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
156 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
160 static int event_newchannel(struct ast_mansession *s, struct message *m)
162 struct ast_chan *chan;
163 chan = find_chan(get_header(m, "Channel"));
164 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
165 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
169 static int event_status(struct ast_mansession *s, struct message *m)
171 struct ast_chan *chan;
172 chan = find_chan(get_header(m, "Channel"));
173 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
174 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
175 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
176 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
177 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
181 static int event_hangup(struct ast_mansession *s, struct message *m)
183 del_chan(get_header(m, "Channel"));
187 static int event_ignore(struct ast_mansession *s, struct message *m)
192 static int event_rename(struct ast_mansession *s, struct message *m)
194 struct ast_chan *chan;
195 chan = find_chan(get_header(m, "Oldname"));
196 strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
199 static struct event {
201 int (*func)(struct ast_mansession *s, struct message *m);
203 { "Newstate", event_newstate },
204 { "Newchannel", event_newchannel },
205 { "Newexten", event_newexten },
206 { "Hangup", event_hangup },
207 { "Rename", event_rename },
208 { "Status", event_status },
209 { "Link", event_ignore },
210 { "Unlink", event_ignore },
211 { "StatusComplete", event_ignore }
214 static int process_message(struct ast_mansession *s, struct message *m)
218 strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
219 if (!strlen(event)) {
220 fprintf(stderr, "Missing event in request");
223 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
224 if (!strcasecmp(event, events[x].event)) {
225 if (events[x].func(s, m))
230 if (x >= sizeof(events) / sizeof(events[0]))
231 fprintf(stderr, "Ignoring unknown event '%s'", event);
233 for (x=0;x<m->hdrcount;x++) {
234 printf("Header: %s\n", m->headers[x]);
240 static void rebuild_channels(newtComponent c)
243 struct ast_chan *chan;
247 prev = newtListboxGetCurrent(c);
251 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
252 if (strlen(chan->exten))
253 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
255 chan->exten, chan->context, chan->priority);
257 snprintf(tmp, sizeof(tmp), "%-30s %8s",
259 newtListboxAppendEntry(c, tmp, chan);
264 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
265 newtListboxSetCurrentByKey(c, prev);
268 static int has_input(struct ast_mansession *s)
271 for (x=1;x<s->inlen;x++)
272 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
277 static int get_input(struct ast_mansession *s, char *output)
279 /* output must have at least sizeof(s->inbuf) space */
282 struct timeval tv = {0, 0};
284 for (x=1;x<s->inlen;x++) {
285 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
286 /* Copy output data up to and including \r\n */
287 memcpy(output, s->inbuf, x + 1);
288 /* Add trailing \0 */
290 /* Move remaining data back to the front */
291 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
296 if (s->inlen >= sizeof(s->inbuf) - 1) {
297 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
302 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
304 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
305 } else if (res > 0) {
306 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
310 s->inbuf[s->inlen] = '\0';
317 static int input_check(struct ast_mansession *s, struct message **mout)
319 static struct message m;
326 res = get_input(s, m.headers[m.hdrcount]);
329 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
332 /* Strip trailing \r\n */
333 if (strlen(m.headers[m.hdrcount]) < 2)
335 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
336 if (!strlen(m.headers[m.hdrcount])) {
337 if (mout && strlen(get_header(&m, "Response"))) {
341 if (process_message(s, &m))
343 memset(&m, 0, sizeof(&m));
344 } else if (m.hdrcount < MAX_HEADERS - 1)
346 } else if (res < 0) {
354 static struct message *wait_for_response(int timeout)
361 tv.tv_sec = timeout / 1000;
362 tv.tv_usec = (timeout % 1000) * 1000;
363 FD_SET(session.fd, &fds);
364 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
367 if (input_check(&session, &m) < 0) {
376 static int manager_action(char *action, char *fmt, ...)
378 struct ast_mansession *s;
383 fdprintf(s->fd, "Action: %s\r\n", action);
385 vsnprintf(tmp, sizeof(tmp), fmt, ap);
387 write(s->fd, tmp, strlen(tmp));
388 fdprintf(s->fd, "\r\n");
392 static int show_message(char *title, char *msg)
397 struct newtExitStruct es;
399 newtCenteredWindow(60,7, title);
401 label = newtLabel(4,1,msg);
402 ok = newtButton(27, 3, "OK");
403 form = newtForm(NULL, NULL, 0);
404 newtFormAddComponents(form, label, ok, NULL);
405 newtFormRun(form, &es);
407 newtFormDestroy(form);
411 static newtComponent showform;
412 static int show_doing(char *title, char *tmp)
414 struct newtExitStruct es;
416 showform = newtForm(NULL, NULL, 0);
417 newtCenteredWindow(70,4, title);
418 label = newtLabel(3,1,tmp);
419 newtFormAddComponents(showform,label, NULL);
420 newtFormSetTimer(showform, 200);
421 newtFormRun(showform, &es);
425 static int hide_doing(void)
428 newtFormDestroy(showform);
432 static void try_status(void)
435 manager_action("Status", "");
436 m = wait_for_response(10000);
438 show_message("Status Failed", "Timeout waiting for response");
439 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
440 show_message("Status Failed Failed", get_header(m, "Message"));
445 static void try_hangup(newtComponent c)
447 struct ast_chan *chan;
450 chan = newtListboxGetCurrent(c);
452 manager_action("Hangup", "Channel: %s\r\n", chan->name);
453 m = wait_for_response(10000);
455 show_message("Hangup Failed", "Timeout waiting for response");
456 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
457 show_message("Hangup Failed", get_header(m, "Message"));
463 static int get_user_input(char *msg, char *buf, int buflen)
467 newtComponent cancel;
468 newtComponent inpfield;
471 struct newtExitStruct es;
473 newtCenteredWindow(60,7, msg);
475 inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0);
476 ok = newtButton(22, 3, "OK");
477 cancel = newtButton(32, 3, "Cancel");
478 form = newtForm(NULL, NULL, 0);
479 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
480 newtFormRun(form, &es);
481 strncpy(buf, input, buflen - 1);
487 newtFormDestroy(form);
491 static void try_redirect(newtComponent c)
493 struct ast_chan *chan;
500 chan = newtListboxGetCurrent(c);
502 strncpy(channame, chan->name, sizeof(channame) - 1);
503 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
504 if (get_user_input(tmp, dest, sizeof(dest)))
506 if ((context = strchr(dest, '@'))) {
509 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
511 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
513 m = wait_for_response(10000);
515 show_message("Hangup Failed", "Timeout waiting for response");
516 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
517 show_message("Hangup Failed", get_header(m, "Message"));
523 static int manage_calls(char *host)
527 newtComponent hangup;
528 newtComponent redirect;
529 newtComponent channels;
530 struct newtExitStruct es;
533 /* If there's one thing you learn from this code, it is this...
534 Never, ever fly Air France. Their customer service is absolutely
535 the worst. I've never heard the words "That's not my problem" as
536 many times as I have from their staff -- It should, without doubt
537 be their corporate motto if it isn't already. Don't bother giving
538 them business because you're just a pain in their side and they
539 will be sure to let you know the first time you speak to them.
541 If you ever want to make me happy just tell me that you, too, will
542 never fly Air France again either (in spite of their excellent
544 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
545 newtCenteredWindow(74, 20, tmp);
546 form = newtForm(NULL, NULL, 0);
547 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
548 newtFormSetTimer(form, 100);
549 quit = newtButton(62, 16, "Quit");
550 redirect = newtButton(35, 16, "Redirect");
551 hangup = newtButton(50, 16, "Hangup");
552 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
553 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
554 newtListboxSetWidth(channels, 72);
556 show_doing("Getting Status", "Retrieving system status...");
561 newtFormRun(form, &es);
562 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
563 if (input_check(&session, NULL)) {
564 show_message("Disconnected", "Disconnected from remote host");
567 } else if (es.reason == NEWT_EXIT_COMPONENT) {
570 if (es.u.co == hangup) {
571 try_hangup(channels);
572 } else if (es.u.co == redirect) {
573 try_redirect(channels);
576 rebuild_channels(channels);
578 newtFormDestroy(form);
582 static int login(char *hostname)
585 newtComponent cancel;
587 newtComponent username;
588 newtComponent password;
590 newtComponent ulabel;
591 newtComponent plabel;
595 struct newtExitStruct es;
600 session.fd = socket(AF_INET, SOCK_STREAM, 0);
601 if (session.fd < 0) {
602 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
603 show_message("Socket failed", tmp);
607 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
608 show_doing("Connecting....", tmp);
611 hp = gethostbyname(hostname);
613 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
614 show_message("Host lookup failed", tmp);
618 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
619 show_doing("Connecting...", tmp);
621 session.sin.sin_family = AF_INET;
622 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
623 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
625 if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
626 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
627 show_message("Connect Failed", tmp);
633 login = newtButton(5, 6, "Login");
634 cancel = newtButton(25, 6, "Cancel");
635 newtCenteredWindow(40, 10, "Asterisk Manager Login");
636 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
637 label = newtLabel(4,1, tmp);
639 ulabel = newtLabel(4,2,"Username:");
640 plabel = newtLabel(4,3,"Password:");
642 username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
643 password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
645 form = newtForm(NULL, NULL, 0);
646 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
647 newtFormRun(form, &es);
648 if (es.reason == NEWT_EXIT_COMPONENT) {
649 if (es.u.co == login) {
650 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
651 show_doing("Logging in", tmp);
652 /* Check to see if the remote host supports MD5 Authentication */
653 manager_action("Challenge", "AuthType: MD5\r\n");
654 m = wait_for_response(10000);
655 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
656 char *challenge = get_header(m, "Challenge");
659 char md5key[256] = "";
660 struct MD5Context md5;
661 unsigned char digest[16];
663 MD5Update(&md5, challenge, strlen(challenge));
664 MD5Update(&md5, pass, strlen(pass));
665 MD5Final(digest, &md5);
667 len += sprintf(md5key + len, "%2.2x", digest[x]);
668 manager_action("Login",
673 m = wait_for_response(10000);
675 if (!strcasecmp(get_header(m, "Response"), "Success")) {
678 show_message("Login Failed", get_header(m, "Message"));
681 memset(m, 0, sizeof(m));
682 manager_action("Login",
686 m = wait_for_response(10000);
689 if (!strcasecmp(get_header(m, "Response"), "Success")) {
692 show_message("Login Failed", get_header(m, "Message"));
698 newtFormDestroy(form);
702 int main(int argc, char *argv[])
705 fprintf(stderr, "Usage: astman <host>\n");
710 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
711 newtPushHelpLine("Welcome to the Asterisk Manager!");
712 if (login(argv[1])) {
716 manage_calls(argv[1]);