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
31 static struct ast_mansession {
32 struct sockaddr_in sin;
38 static struct ast_chan {
45 struct ast_chan *next;
48 static struct ast_chan *find_chan(char *name)
50 struct ast_chan *prev = NULL, *chan = chans;
52 if (!strcmp(name, chan->name))
57 chan = malloc(sizeof(struct ast_chan));
59 memset(chan, 0, sizeof(struct ast_chan));
60 strncpy(chan->name, name, sizeof(chan->name) - 1);
69 static void del_chan(char *name)
71 struct ast_chan *prev = NULL, *chan = chans;
73 if (!strcmp(name, chan->name)) {
75 prev->next = chan->next;
85 static void fdprintf(int fd, char *fmt, ...)
90 vsnprintf(stuff, sizeof(stuff), fmt, ap);
92 write(fd, stuff, strlen(stuff));
95 static char *get_header(struct message *m, char *var)
99 snprintf(cmp, sizeof(cmp), "%s: ", var);
100 for (x=0;x<m->hdrcount;x++)
101 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
102 return m->headers[x] + strlen(cmp);
106 static int event_newstate(struct ast_mansession *s, struct message *m)
108 struct ast_chan *chan;
109 chan = find_chan(get_header(m, "Channel"));
110 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
114 static int event_newexten(struct ast_mansession *s, struct message *m)
116 struct ast_chan *chan;
117 chan = find_chan(get_header(m, "Channel"));
118 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
119 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
120 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
124 static int event_newchannel(struct ast_mansession *s, struct message *m)
126 struct ast_chan *chan;
127 chan = find_chan(get_header(m, "Channel"));
128 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
129 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
133 static int event_status(struct ast_mansession *s, struct message *m)
135 struct ast_chan *chan;
136 chan = find_chan(get_header(m, "Channel"));
137 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
138 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
139 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
140 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
141 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
145 static int event_hangup(struct ast_mansession *s, struct message *m)
147 del_chan(get_header(m, "Channel"));
151 static int event_ignore(struct ast_mansession *s, struct message *m)
156 static int event_rename(struct ast_mansession *s, struct message *m)
158 struct ast_chan *chan;
159 chan = find_chan(get_header(m, "Oldname"));
160 strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
163 static struct event {
165 int (*func)(struct ast_mansession *s, struct message *m);
167 { "Newstate", event_newstate },
168 { "Newchannel", event_newchannel },
169 { "Newexten", event_newexten },
170 { "Hangup", event_hangup },
171 { "Rename", event_rename },
172 { "Status", event_status },
173 { "Link", event_ignore },
174 { "Unlink", event_ignore },
175 { "StatusComplete", event_ignore }
178 static int process_message(struct ast_mansession *s, struct message *m)
182 strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
183 if (!strlen(event)) {
184 fprintf(stderr, "Missing event in request");
187 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
188 if (!strcasecmp(event, events[x].event)) {
189 if (events[x].func(s, m))
194 if (x >= sizeof(events) / sizeof(events[0]))
195 fprintf(stderr, "Ignoring unknown event '%s'", event);
197 for (x=0;x<m->hdrcount;x++) {
198 printf("Header: %s\n", m->headers[x]);
204 static void rebuild_channels(newtComponent c)
207 struct ast_chan *chan;
211 prev = newtListboxGetCurrent(c);
215 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
216 if (strlen(chan->exten))
217 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
219 chan->exten, chan->context, chan->priority);
221 snprintf(tmp, sizeof(tmp), "%-30s %8s",
223 newtListboxAppendEntry(c, tmp, chan);
228 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
229 newtListboxSetCurrentByKey(c, prev);
232 static int has_input(struct ast_mansession *s)
235 for (x=1;x<s->inlen;x++)
236 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
241 static int get_input(struct ast_mansession *s, char *output)
243 /* output must have at least sizeof(s->inbuf) space */
246 struct timeval tv = {0, 0};
248 for (x=1;x<s->inlen;x++) {
249 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
250 /* Copy output data up to and including \r\n */
251 memcpy(output, s->inbuf, x + 1);
252 /* Add trailing \0 */
254 /* Move remaining data back to the front */
255 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
260 if (s->inlen >= sizeof(s->inbuf) - 1) {
261 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
266 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
268 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
269 } else if (res > 0) {
270 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
274 s->inbuf[s->inlen] = '\0';
281 static int input_check(struct ast_mansession *s, struct message **mout)
283 static struct message m;
290 res = get_input(s, m.headers[m.hdrcount]);
293 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
296 /* Strip trailing \r\n */
297 if (strlen(m.headers[m.hdrcount]) < 2)
299 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
300 if (!strlen(m.headers[m.hdrcount])) {
301 if (mout && strlen(get_header(&m, "Response"))) {
305 if (process_message(s, &m))
307 memset(&m, 0, sizeof(&m));
308 } else if (m.hdrcount < MAX_HEADERS - 1)
310 } else if (res < 0) {
318 static struct message *wait_for_response(int timeout)
325 tv.tv_sec = timeout / 1000;
326 tv.tv_usec = (timeout % 1000) * 1000;
327 FD_SET(session.fd, &fds);
328 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
331 if (input_check(&session, &m) < 0) {
340 static int manager_action(char *action, char *fmt, ...)
342 struct ast_mansession *s;
347 fdprintf(s->fd, "Action: %s\r\n", action);
349 vsnprintf(tmp, sizeof(tmp), fmt, ap);
351 write(s->fd, tmp, strlen(tmp));
352 fdprintf(s->fd, "\r\n");
356 static int show_message(char *title, char *msg)
361 struct newtExitStruct es;
363 newtCenteredWindow(60,7, title);
365 label = newtLabel(4,1,msg);
366 ok = newtButton(27, 3, "OK");
367 form = newtForm(NULL, NULL, 0);
368 newtFormAddComponents(form, label, ok, NULL);
369 newtFormRun(form, &es);
371 newtFormDestroy(form);
375 static newtComponent showform;
376 static int show_doing(char *title, char *tmp)
378 struct newtExitStruct es;
380 showform = newtForm(NULL, NULL, 0);
381 newtCenteredWindow(70,4, title);
382 label = newtLabel(3,1,tmp);
383 newtFormAddComponents(showform,label, NULL);
384 newtFormSetTimer(showform, 200);
385 newtFormRun(showform, &es);
389 static int hide_doing(void)
392 newtFormDestroy(showform);
396 static void try_status(void)
399 manager_action("Status", "");
400 m = wait_for_response(10000);
402 show_message("Status Failed", "Timeout waiting for response");
403 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
404 show_message("Status Failed Failed", get_header(m, "Message"));
409 static void try_hangup(newtComponent c)
411 struct ast_chan *chan;
414 chan = newtListboxGetCurrent(c);
416 manager_action("Hangup", "Channel: %s\r\n", chan->name);
417 m = wait_for_response(10000);
419 show_message("Hangup Failed", "Timeout waiting for response");
420 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
421 show_message("Hangup Failed", get_header(m, "Message"));
427 static int get_user_input(char *msg, char *buf, int buflen)
431 newtComponent cancel;
432 newtComponent inpfield;
435 struct newtExitStruct es;
437 newtCenteredWindow(60,7, msg);
439 inpfield = newtEntry(5, 2, "", 50, &input, 0);
440 ok = newtButton(22, 3, "OK");
441 cancel = newtButton(32, 3, "Cancel");
442 form = newtForm(NULL, NULL, 0);
443 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
444 newtFormRun(form, &es);
445 strncpy(buf, input, buflen - 1);
451 newtFormDestroy(form);
455 static void try_redirect(newtComponent c)
457 struct ast_chan *chan;
464 chan = newtListboxGetCurrent(c);
466 strncpy(channame, chan->name, sizeof(channame) - 1);
467 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
468 if (get_user_input(tmp, dest, sizeof(dest)))
470 if ((context = strchr(dest, '@'))) {
473 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
475 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
477 m = wait_for_response(10000);
479 show_message("Hangup Failed", "Timeout waiting for response");
480 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
481 show_message("Hangup Failed", get_header(m, "Message"));
487 static int manage_calls(char *host)
491 newtComponent hangup;
492 newtComponent redirect;
493 newtComponent channels;
494 struct newtExitStruct es;
497 /* If there's one thing you learn from this code, it is this...
498 Never, ever fly Air France. Their customer service is absolutely
499 the worst. I've never heard the words "That's not my problem" as
500 many times as I have from their staff -- It should, without doubt
501 be their corporate motto if it isn't already. Don't bother giving
502 them business because you're just a pain in their side and they
503 will be sure to let you know the first time you speak to them.
505 If you ever want to make me happy just tell me that you, too, will
506 never fly Air France again either (in spite of their excellent
508 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
509 newtCenteredWindow(74, 20, tmp);
510 form = newtForm(NULL, NULL, 0);
511 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
512 newtFormSetTimer(form, 100);
513 quit = newtButton(62, 16, "Quit");
514 redirect = newtButton(35, 16, "Redirect");
515 hangup = newtButton(50, 16, "Hangup");
516 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
517 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
518 newtListboxSetWidth(channels, 72);
520 show_doing("Getting Status", "Retrieving system status...");
525 newtFormRun(form, &es);
526 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
527 if (input_check(&session, NULL)) {
528 show_message("Disconnected", "Disconnected from remote host");
531 } else if (es.reason == NEWT_EXIT_COMPONENT) {
534 if (es.u.co == hangup) {
535 try_hangup(channels);
536 } else if (es.u.co == redirect) {
537 try_redirect(channels);
540 rebuild_channels(channels);
542 newtFormDestroy(form);
546 static int login(char *hostname)
549 newtComponent cancel;
551 newtComponent username;
552 newtComponent password;
554 newtComponent ulabel;
555 newtComponent plabel;
559 struct newtExitStruct es;
564 session.fd = socket(AF_INET, SOCK_STREAM, 0);
565 if (session.fd < 0) {
566 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
567 show_message("Socket failed", tmp);
571 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
572 show_doing("Connecting....", tmp);
575 hp = gethostbyname(hostname);
577 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
578 show_message("Host lookup failed", tmp);
582 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
583 show_doing("Connecting...", tmp);
585 session.sin.sin_family = AF_INET;
586 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
587 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
589 if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
590 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
591 show_message("Connect Failed", tmp);
597 login = newtButton(5, 6, "Login");
598 cancel = newtButton(25, 6, "Cancel");
599 newtCenteredWindow(40, 10, "Asterisk Manager Login");
600 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
601 label = newtLabel(4,1, tmp);
603 ulabel = newtLabel(4,2,"Username:");
604 plabel = newtLabel(4,3,"Password:");
606 username = newtEntry(14, 2, "", 20, &user, 0);
607 password = newtEntry(14, 3, "", 20, &pass, NEWT_FLAG_HIDDEN);
609 form = newtForm(NULL, NULL, 0);
610 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
611 newtFormRun(form, &es);
612 if (es.reason == NEWT_EXIT_COMPONENT) {
613 if (es.u.co == login) {
614 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
615 show_doing("Logging in", tmp);
616 /* Check to see if the remote host supports MD5 Authentication */
617 manager_action("Challenge", "AuthType: MD5\r\n");
618 m = wait_for_response(10000);
619 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
620 char *challenge = get_header(m, "Challenge");
623 char md5key[256] = "";
624 struct MD5Context md5;
625 unsigned char digest[16];
627 MD5Update(&md5, challenge, strlen(challenge));
628 MD5Update(&md5, pass, strlen(pass));
629 MD5Final(digest, &md5);
631 len += sprintf(md5key + len, "%2.2x", digest[x]);
632 manager_action("Login",
637 m = wait_for_response(10000);
639 if (!strcasecmp(get_header(m, "Response"), "Success")) {
642 show_message("Login Failed", get_header(m, "Message"));
645 memset(m, 0, sizeof(m));
646 manager_action("Login",
650 m = wait_for_response(10000);
653 if (!strcasecmp(get_header(m, "Response"), "Success")) {
656 show_message("Login Failed", get_header(m, "Message"));
662 newtFormDestroy(form);
666 int main(int argc, char *argv[])
669 fprintf(stderr, "Usage: astman <host>\n");
674 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
675 newtPushHelpLine("Welcome to the Asterisk Manager!");
676 if (login(argv[1])) {
680 manage_calls(argv[1]);