3 * Copyright (C) 2002, Linux Support Services, Inc.
5 * Distributed under the terms of the GNU General Public License
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <sys/socket.h>
15 #include <sys/select.h>
23 #include "asterisk/md5.h"
24 #include "asterisk/manager.h"
28 #define MAX_HEADERS 80
32 * 2005.05.27 - different versions of newt define the type of the buffer
33 * for the 5th argument to newtEntry() as char ** or const char ** . To
34 * let the code compile cleanly with -Werror, we cast it to void * through
37 #define _NEWT_CAST (void *)
39 static struct ast_mansession {
40 struct sockaddr_in sin;
46 static struct ast_chan {
53 struct ast_chan *next;
56 static struct ast_chan *find_chan(char *name)
58 struct ast_chan *prev = NULL, *chan = chans;
60 if (!strcmp(name, chan->name))
65 chan = malloc(sizeof(struct ast_chan));
67 memset(chan, 0, sizeof(struct ast_chan));
68 strncpy(chan->name, name, sizeof(chan->name) - 1);
77 static void del_chan(char *name)
79 struct ast_chan *prev = NULL, *chan = chans;
81 if (!strcmp(name, chan->name)) {
83 prev->next = chan->next;
93 static void fdprintf(int fd, char *fmt, ...)
98 vsnprintf(stuff, sizeof(stuff), fmt, ap);
100 write(fd, stuff, strlen(stuff));
103 static char *get_header(struct message *m, char *var)
107 snprintf(cmp, sizeof(cmp), "%s: ", var);
108 for (x=0;x<m->hdrcount;x++)
109 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
110 return m->headers[x] + strlen(cmp);
114 static int event_newstate(struct ast_mansession *s, struct message *m)
116 struct ast_chan *chan;
117 chan = find_chan(get_header(m, "Channel"));
118 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
122 static int event_newexten(struct ast_mansession *s, struct message *m)
124 struct ast_chan *chan;
125 chan = find_chan(get_header(m, "Channel"));
126 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
127 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
128 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
132 static int event_newchannel(struct ast_mansession *s, struct message *m)
134 struct ast_chan *chan;
135 chan = find_chan(get_header(m, "Channel"));
136 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
137 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
141 static int event_status(struct ast_mansession *s, struct message *m)
143 struct ast_chan *chan;
144 chan = find_chan(get_header(m, "Channel"));
145 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
146 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
147 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
148 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
149 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
153 static int event_hangup(struct ast_mansession *s, struct message *m)
155 del_chan(get_header(m, "Channel"));
159 static int event_ignore(struct ast_mansession *s, struct message *m)
164 static int event_rename(struct ast_mansession *s, struct message *m)
166 struct ast_chan *chan;
167 chan = find_chan(get_header(m, "Oldname"));
168 strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
171 static struct event {
173 int (*func)(struct ast_mansession *s, struct message *m);
175 { "Newstate", event_newstate },
176 { "Newchannel", event_newchannel },
177 { "Newexten", event_newexten },
178 { "Hangup", event_hangup },
179 { "Rename", event_rename },
180 { "Status", event_status },
181 { "Link", event_ignore },
182 { "Unlink", event_ignore },
183 { "StatusComplete", event_ignore }
186 static int process_message(struct ast_mansession *s, struct message *m)
190 strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
191 if (!strlen(event)) {
192 fprintf(stderr, "Missing event in request");
195 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
196 if (!strcasecmp(event, events[x].event)) {
197 if (events[x].func(s, m))
202 if (x >= sizeof(events) / sizeof(events[0]))
203 fprintf(stderr, "Ignoring unknown event '%s'", event);
205 for (x=0;x<m->hdrcount;x++) {
206 printf("Header: %s\n", m->headers[x]);
212 static void rebuild_channels(newtComponent c)
215 struct ast_chan *chan;
219 prev = newtListboxGetCurrent(c);
223 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
224 if (strlen(chan->exten))
225 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
227 chan->exten, chan->context, chan->priority);
229 snprintf(tmp, sizeof(tmp), "%-30s %8s",
231 newtListboxAppendEntry(c, tmp, chan);
236 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
237 newtListboxSetCurrentByKey(c, prev);
240 static int has_input(struct ast_mansession *s)
243 for (x=1;x<s->inlen;x++)
244 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
249 static int get_input(struct ast_mansession *s, char *output)
251 /* output must have at least sizeof(s->inbuf) space */
254 struct timeval tv = {0, 0};
256 for (x=1;x<s->inlen;x++) {
257 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
258 /* Copy output data up to and including \r\n */
259 memcpy(output, s->inbuf, x + 1);
260 /* Add trailing \0 */
262 /* Move remaining data back to the front */
263 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
268 if (s->inlen >= sizeof(s->inbuf) - 1) {
269 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
274 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
276 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
277 } else if (res > 0) {
278 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
282 s->inbuf[s->inlen] = '\0';
289 static int input_check(struct ast_mansession *s, struct message **mout)
291 static struct message m;
298 res = get_input(s, m.headers[m.hdrcount]);
301 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
304 /* Strip trailing \r\n */
305 if (strlen(m.headers[m.hdrcount]) < 2)
307 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
308 if (!strlen(m.headers[m.hdrcount])) {
309 if (mout && strlen(get_header(&m, "Response"))) {
313 if (process_message(s, &m))
315 memset(&m, 0, sizeof(&m));
316 } else if (m.hdrcount < MAX_HEADERS - 1)
318 } else if (res < 0) {
326 static struct message *wait_for_response(int timeout)
333 tv.tv_sec = timeout / 1000;
334 tv.tv_usec = (timeout % 1000) * 1000;
335 FD_SET(session.fd, &fds);
336 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
339 if (input_check(&session, &m) < 0) {
348 static int manager_action(char *action, char *fmt, ...)
350 struct ast_mansession *s;
355 fdprintf(s->fd, "Action: %s\r\n", action);
357 vsnprintf(tmp, sizeof(tmp), fmt, ap);
359 write(s->fd, tmp, strlen(tmp));
360 fdprintf(s->fd, "\r\n");
364 static int show_message(char *title, char *msg)
369 struct newtExitStruct es;
371 newtCenteredWindow(60,7, title);
373 label = newtLabel(4,1,msg);
374 ok = newtButton(27, 3, "OK");
375 form = newtForm(NULL, NULL, 0);
376 newtFormAddComponents(form, label, ok, NULL);
377 newtFormRun(form, &es);
379 newtFormDestroy(form);
383 static newtComponent showform;
384 static int show_doing(char *title, char *tmp)
386 struct newtExitStruct es;
388 showform = newtForm(NULL, NULL, 0);
389 newtCenteredWindow(70,4, title);
390 label = newtLabel(3,1,tmp);
391 newtFormAddComponents(showform,label, NULL);
392 newtFormSetTimer(showform, 200);
393 newtFormRun(showform, &es);
397 static int hide_doing(void)
400 newtFormDestroy(showform);
404 static void try_status(void)
407 manager_action("Status", "");
408 m = wait_for_response(10000);
410 show_message("Status Failed", "Timeout waiting for response");
411 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
412 show_message("Status Failed Failed", get_header(m, "Message"));
417 static void try_hangup(newtComponent c)
419 struct ast_chan *chan;
422 chan = newtListboxGetCurrent(c);
424 manager_action("Hangup", "Channel: %s\r\n", chan->name);
425 m = wait_for_response(10000);
427 show_message("Hangup Failed", "Timeout waiting for response");
428 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
429 show_message("Hangup Failed", get_header(m, "Message"));
435 static int get_user_input(char *msg, char *buf, int buflen)
439 newtComponent cancel;
440 newtComponent inpfield;
443 struct newtExitStruct es;
445 newtCenteredWindow(60,7, msg);
447 inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0);
448 ok = newtButton(22, 3, "OK");
449 cancel = newtButton(32, 3, "Cancel");
450 form = newtForm(NULL, NULL, 0);
451 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
452 newtFormRun(form, &es);
453 strncpy(buf, input, buflen - 1);
459 newtFormDestroy(form);
463 static void try_redirect(newtComponent c)
465 struct ast_chan *chan;
472 chan = newtListboxGetCurrent(c);
474 strncpy(channame, chan->name, sizeof(channame) - 1);
475 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
476 if (get_user_input(tmp, dest, sizeof(dest)))
478 if ((context = strchr(dest, '@'))) {
481 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
483 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
485 m = wait_for_response(10000);
487 show_message("Hangup Failed", "Timeout waiting for response");
488 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
489 show_message("Hangup Failed", get_header(m, "Message"));
495 static int manage_calls(char *host)
499 newtComponent hangup;
500 newtComponent redirect;
501 newtComponent channels;
502 struct newtExitStruct es;
505 /* If there's one thing you learn from this code, it is this...
506 Never, ever fly Air France. Their customer service is absolutely
507 the worst. I've never heard the words "That's not my problem" as
508 many times as I have from their staff -- It should, without doubt
509 be their corporate motto if it isn't already. Don't bother giving
510 them business because you're just a pain in their side and they
511 will be sure to let you know the first time you speak to them.
513 If you ever want to make me happy just tell me that you, too, will
514 never fly Air France again either (in spite of their excellent
516 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
517 newtCenteredWindow(74, 20, tmp);
518 form = newtForm(NULL, NULL, 0);
519 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
520 newtFormSetTimer(form, 100);
521 quit = newtButton(62, 16, "Quit");
522 redirect = newtButton(35, 16, "Redirect");
523 hangup = newtButton(50, 16, "Hangup");
524 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
525 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
526 newtListboxSetWidth(channels, 72);
528 show_doing("Getting Status", "Retrieving system status...");
533 newtFormRun(form, &es);
534 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
535 if (input_check(&session, NULL)) {
536 show_message("Disconnected", "Disconnected from remote host");
539 } else if (es.reason == NEWT_EXIT_COMPONENT) {
542 if (es.u.co == hangup) {
543 try_hangup(channels);
544 } else if (es.u.co == redirect) {
545 try_redirect(channels);
548 rebuild_channels(channels);
550 newtFormDestroy(form);
554 static int login(char *hostname)
557 newtComponent cancel;
559 newtComponent username;
560 newtComponent password;
562 newtComponent ulabel;
563 newtComponent plabel;
567 struct newtExitStruct es;
572 session.fd = socket(AF_INET, SOCK_STREAM, 0);
573 if (session.fd < 0) {
574 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
575 show_message("Socket failed", tmp);
579 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
580 show_doing("Connecting....", tmp);
583 hp = gethostbyname(hostname);
585 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
586 show_message("Host lookup failed", tmp);
590 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
591 show_doing("Connecting...", tmp);
593 session.sin.sin_family = AF_INET;
594 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
595 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
597 if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
598 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
599 show_message("Connect Failed", tmp);
605 login = newtButton(5, 6, "Login");
606 cancel = newtButton(25, 6, "Cancel");
607 newtCenteredWindow(40, 10, "Asterisk Manager Login");
608 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
609 label = newtLabel(4,1, tmp);
611 ulabel = newtLabel(4,2,"Username:");
612 plabel = newtLabel(4,3,"Password:");
614 username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
615 password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
617 form = newtForm(NULL, NULL, 0);
618 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
619 newtFormRun(form, &es);
620 if (es.reason == NEWT_EXIT_COMPONENT) {
621 if (es.u.co == login) {
622 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
623 show_doing("Logging in", tmp);
624 /* Check to see if the remote host supports MD5 Authentication */
625 manager_action("Challenge", "AuthType: MD5\r\n");
626 m = wait_for_response(10000);
627 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
628 char *challenge = get_header(m, "Challenge");
631 char md5key[256] = "";
632 struct MD5Context md5;
633 unsigned char digest[16];
635 MD5Update(&md5, challenge, strlen(challenge));
636 MD5Update(&md5, pass, strlen(pass));
637 MD5Final(digest, &md5);
639 len += sprintf(md5key + len, "%2.2x", digest[x]);
640 manager_action("Login",
645 m = wait_for_response(10000);
647 if (!strcasecmp(get_header(m, "Response"), "Success")) {
650 show_message("Login Failed", get_header(m, "Message"));
653 memset(m, 0, sizeof(m));
654 manager_action("Login",
658 m = wait_for_response(10000);
661 if (!strcasecmp(get_header(m, "Response"), "Success")) {
664 show_message("Login Failed", get_header(m, "Message"));
670 newtFormDestroy(form);
674 int main(int argc, char *argv[])
677 fprintf(stderr, "Usage: astman <host>\n");
682 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
683 newtPushHelpLine("Welcome to the Asterisk Manager!");
684 if (login(argv[1])) {
688 manage_calls(argv[1]);