Let's also include aclocal.m4
[asterisk-bristuff.git] / utils / astman.c
blobf259f6884debe06a51092d1eafcb748cb076f6fe
1 /*
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.
21 * ASTerisk MANager
25 #include "asterisk.h"
27 #include <newt.h>
28 #include <stdio.h>
29 #include <sys/time.h>
30 #include <netdb.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
35 #include <fcntl.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <stdlib.h>
42 #include "asterisk/md5.h"
43 #include "asterisk/linkedlists.h"
45 #undef gethostbyname
47 #define MAX_HEADERS 80
48 #define MAX_LEN 256
51 * 2005.05.27 - different versions of newt define the type of the buffer
52 * for the 5th argument to newtEntry() as char ** or const char ** . To
53 * let the code compile cleanly with -Werror, we cast it to void * through
54 * _NEWT_CAST.
56 #define _NEWT_CAST (void *)
58 #define DEFAULT_MANAGER_PORT 5038
60 struct message {
61 unsigned int hdrcount;
62 char headers[MAX_HEADERS][MAX_LEN];
65 static struct ast_mansession {
66 struct sockaddr_in sin;
67 int fd;
68 char inbuf[MAX_LEN];
69 int inlen;
70 } session;
72 struct ast_chan {
73 char name[80];
74 char exten[20];
75 char context[20];
76 char priority[20];
77 char callerid[40];
78 char state[10];
79 AST_LIST_ENTRY(ast_chan) list;
82 static AST_LIST_HEAD_NOLOCK_STATIC(chans, ast_chan);
84 /* dummy functions to be compatible with the Asterisk core for md5.c */
85 void ast_register_file_version(const char *file, const char *version);
86 void ast_register_file_version(const char *file, const char *version)
90 void ast_unregister_file_version(const char *file);
91 void ast_unregister_file_version(const char *file)
95 int ast_add_profile(const char *, uint64_t scale);
96 int ast_add_profile(const char *s, uint64_t scale)
98 return -1;
101 int64_t ast_profile(int, int64_t);
102 int64_t ast_profile(int key, int64_t val)
104 return 0;
106 int64_t ast_mark(int, int start1_stop0);
107 int64_t ast_mark(int key, int start1_stop0)
109 return 0;
112 /* end of dummy functions */
114 static struct ast_chan *find_chan(char *name)
116 struct ast_chan *chan;
117 AST_LIST_TRAVERSE(&chans, chan, list) {
118 if (!strcmp(name, chan->name))
119 return chan;
121 chan = malloc(sizeof(struct ast_chan));
122 if (chan) {
123 memset(chan, 0, sizeof(struct ast_chan));
124 strncpy(chan->name, name, sizeof(chan->name) - 1);
125 AST_LIST_INSERT_TAIL(&chans, chan, list);
127 return chan;
130 static void del_chan(char *name)
132 struct ast_chan *chan;
133 AST_LIST_TRAVERSE_SAFE_BEGIN(&chans, chan, list) {
134 if (!strcmp(name, chan->name)) {
135 AST_LIST_REMOVE_CURRENT(&chans, list);
136 free(chan);
137 return;
140 AST_LIST_TRAVERSE_SAFE_END
143 static void fdprintf(int fd, char *fmt, ...)
145 char stuff[4096];
146 va_list ap;
147 va_start(ap, fmt);
148 vsnprintf(stuff, sizeof(stuff), fmt, ap);
149 va_end(ap);
150 write(fd, stuff, strlen(stuff));
153 static char *get_header(struct message *m, char *var)
155 char cmp[80];
156 int x;
157 snprintf(cmp, sizeof(cmp), "%s: ", var);
158 for (x=0;x<m->hdrcount;x++)
159 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
160 return m->headers[x] + strlen(cmp);
161 return "";
164 static int event_newstate(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 return 0;
172 static int event_newexten(struct ast_mansession *s, struct message *m)
174 struct ast_chan *chan;
175 chan = find_chan(get_header(m, "Channel"));
176 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
177 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
178 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
179 return 0;
182 static int event_newchannel(struct ast_mansession *s, struct message *m)
184 struct ast_chan *chan;
185 chan = find_chan(get_header(m, "Channel"));
186 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
187 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
188 return 0;
191 static int event_status(struct ast_mansession *s, struct message *m)
193 struct ast_chan *chan;
194 chan = find_chan(get_header(m, "Channel"));
195 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
196 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
197 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
198 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
199 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
200 return 0;
203 static int event_hangup(struct ast_mansession *s, struct message *m)
205 del_chan(get_header(m, "Channel"));
206 return 0;
209 static int event_ignore(struct ast_mansession *s, struct message *m)
211 return 0;
214 static int event_rename(struct ast_mansession *s, struct message *m)
216 struct ast_chan *chan;
217 chan = find_chan(get_header(m, "Oldname"));
218 strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
219 return 0;
221 static struct event {
222 char *event;
223 int (*func)(struct ast_mansession *s, struct message *m);
224 } events[] = {
225 { "Newstate", event_newstate },
226 { "Newchannel", event_newchannel },
227 { "Newexten", event_newexten },
228 { "Hangup", event_hangup },
229 { "Rename", event_rename },
230 { "Status", event_status },
231 { "Link", event_ignore },
232 { "Unlink", event_ignore },
233 { "StatusComplete", event_ignore }
236 static int process_message(struct ast_mansession *s, struct message *m)
238 int x;
239 char event[80] = "";
240 strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
241 if (!strlen(event)) {
242 fprintf(stderr, "Missing event in request");
243 return 0;
245 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
246 if (!strcasecmp(event, events[x].event)) {
247 if (events[x].func(s, m))
248 return -1;
249 break;
252 if (x >= sizeof(events) / sizeof(events[0]))
253 fprintf(stderr, "Ignoring unknown event '%s'", event);
254 #if 0
255 for (x=0;x<m->hdrcount;x++) {
256 printf("Header: %s\n", m->headers[x]);
258 #endif
259 return 0;
262 static void rebuild_channels(newtComponent c)
264 void *prev = NULL;
265 struct ast_chan *chan;
266 char tmpn[42];
267 char tmp[256];
268 int x=0;
269 prev = newtListboxGetCurrent(c);
270 newtListboxClear(c);
271 AST_LIST_TRAVERSE(&chans, chan, list) {
272 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
273 if (strlen(chan->exten))
274 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
275 tmpn, chan->state,
276 chan->exten, chan->context, chan->priority);
277 else
278 snprintf(tmp, sizeof(tmp), "%-30s %8s",
279 tmpn, chan->state);
280 newtListboxAppendEntry(c, tmp, chan);
281 x++;
283 if (!x)
284 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
285 newtListboxSetCurrentByKey(c, prev);
288 static int has_input(struct ast_mansession *s)
290 int x;
291 for (x=1;x<s->inlen;x++)
292 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
293 return 1;
294 return 0;
297 static int get_input(struct ast_mansession *s, char *output)
299 /* output must have at least sizeof(s->inbuf) space */
300 int res;
301 int x;
302 struct timeval tv = {0, 0};
303 fd_set fds;
304 for (x=1;x<s->inlen;x++) {
305 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
306 /* Copy output data up to and including \r\n */
307 memcpy(output, s->inbuf, x + 1);
308 /* Add trailing \0 */
309 output[x+1] = '\0';
310 /* Move remaining data back to the front */
311 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
312 s->inlen -= (x + 1);
313 return 1;
316 if (s->inlen >= sizeof(s->inbuf) - 1) {
317 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
318 s->inlen = 0;
320 FD_ZERO(&fds);
321 FD_SET(s->fd, &fds);
322 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
323 if (res < 0) {
324 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
325 } else if (res > 0) {
326 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
327 if (res < 1)
328 return -1;
329 s->inlen += res;
330 s->inbuf[s->inlen] = '\0';
331 } else {
332 return 2;
334 return 0;
337 static int input_check(struct ast_mansession *s, struct message **mout)
339 static struct message m;
340 int res;
342 if (mout)
343 *mout = NULL;
345 for(;;) {
346 res = get_input(s, m.headers[m.hdrcount]);
347 if (res == 1) {
348 #if 0
349 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
350 fgetc(stdin);
351 #endif
352 /* Strip trailing \r\n */
353 if (strlen(m.headers[m.hdrcount]) < 2)
354 continue;
355 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
356 if (!strlen(m.headers[m.hdrcount])) {
357 if (mout && strlen(get_header(&m, "Response"))) {
358 *mout = &m;
359 return 0;
361 if (process_message(s, &m))
362 break;
363 memset(&m, 0, sizeof(&m));
364 } else if (m.hdrcount < MAX_HEADERS - 1)
365 m.hdrcount++;
366 } else if (res < 0) {
367 return -1;
368 } else if (res == 2)
369 return 0;
371 return -1;
374 static struct message *wait_for_response(int timeout)
376 struct message *m;
377 struct timeval tv;
378 int res;
379 fd_set fds;
380 for (;;) {
381 tv.tv_sec = timeout / 1000;
382 tv.tv_usec = (timeout % 1000) * 1000;
383 FD_SET(session.fd, &fds);
384 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
385 if (res < 1)
386 break;
387 if (input_check(&session, &m) < 0) {
388 return NULL;
390 if (m)
391 return m;
393 return NULL;
396 static int manager_action(char *action, char *fmt, ...)
398 struct ast_mansession *s;
399 char tmp[4096];
400 va_list ap;
402 s = &session;
403 fdprintf(s->fd, "Action: %s\r\n", action);
404 va_start(ap, fmt);
405 vsnprintf(tmp, sizeof(tmp), fmt, ap);
406 va_end(ap);
407 write(s->fd, tmp, strlen(tmp));
408 fdprintf(s->fd, "\r\n");
409 return 0;
412 static int show_message(char *title, char *msg)
414 newtComponent form;
415 newtComponent label;
416 newtComponent ok;
417 struct newtExitStruct es;
419 newtCenteredWindow(60,7, title);
421 label = newtLabel(4,1,msg);
422 ok = newtButton(27, 3, "OK");
423 form = newtForm(NULL, NULL, 0);
424 newtFormAddComponents(form, label, ok, NULL);
425 newtFormRun(form, &es);
426 newtPopWindow();
427 newtFormDestroy(form);
428 return 0;
431 static newtComponent showform;
432 static int show_doing(char *title, char *tmp)
434 struct newtExitStruct es;
435 newtComponent label;
436 showform = newtForm(NULL, NULL, 0);
437 newtCenteredWindow(70,4, title);
438 label = newtLabel(3,1,tmp);
439 newtFormAddComponents(showform,label, NULL);
440 newtFormSetTimer(showform, 200);
441 newtFormRun(showform, &es);
442 return 0;
445 static int hide_doing(void)
447 newtPopWindow();
448 newtFormDestroy(showform);
449 return 0;
452 static void try_status(void)
454 struct message *m;
455 manager_action("Status", "");
456 m = wait_for_response(10000);
457 if (!m) {
458 show_message("Status Failed", "Timeout waiting for response");
459 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
460 show_message("Status Failed Failed", get_header(m, "Message"));
465 static void try_hangup(newtComponent c)
467 struct ast_chan *chan;
468 struct message *m;
470 chan = newtListboxGetCurrent(c);
471 if (chan) {
472 manager_action("Hangup", "Channel: %s\r\n", chan->name);
473 m = wait_for_response(10000);
474 if (!m) {
475 show_message("Hangup Failed", "Timeout waiting for response");
476 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
477 show_message("Hangup Failed", get_header(m, "Message"));
483 static int get_user_input(char *msg, char *buf, int buflen)
485 newtComponent form;
486 newtComponent ok;
487 newtComponent cancel;
488 newtComponent inpfield;
489 const char *input;
490 int res = -1;
491 struct newtExitStruct es;
493 newtCenteredWindow(60,7, msg);
495 inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0);
496 ok = newtButton(22, 3, "OK");
497 cancel = newtButton(32, 3, "Cancel");
498 form = newtForm(NULL, NULL, 0);
499 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
500 newtFormRun(form, &es);
501 strncpy(buf, input, buflen - 1);
502 if (es.u.co == ok)
503 res = 0;
504 else
505 res = -1;
506 newtPopWindow();
507 newtFormDestroy(form);
508 return res;
511 static void try_redirect(newtComponent c)
513 struct ast_chan *chan;
514 char dest[256];
515 struct message *m;
516 char channame[256];
517 char tmp[80];
518 char *context;
520 chan = newtListboxGetCurrent(c);
521 if (chan) {
522 strncpy(channame, chan->name, sizeof(channame) - 1);
523 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
524 if (get_user_input(tmp, dest, sizeof(dest)))
525 return;
526 if ((context = strchr(dest, '@'))) {
527 *context = '\0';
528 context++;
529 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
530 } else {
531 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
533 m = wait_for_response(10000);
534 if (!m) {
535 show_message("Hangup Failed", "Timeout waiting for response");
536 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
537 show_message("Hangup Failed", get_header(m, "Message"));
543 static int manage_calls(char *host)
545 newtComponent form;
546 newtComponent quit;
547 newtComponent hangup;
548 newtComponent redirect;
549 newtComponent channels;
550 struct newtExitStruct es;
551 char tmp[80];
553 /* Mark: If there's one thing you learn from this code, it is this...
554 Never, ever fly Air France. Their customer service is absolutely
555 the worst. I've never heard the words "That's not my problem" as
556 many times as I have from their staff -- It should, without doubt
557 be their corporate motto if it isn't already. Don't bother giving
558 them business because you're just a pain in their side and they
559 will be sure to let you know the first time you speak to them.
561 If you ever want to make me happy just tell me that you, too, will
562 never fly Air France again either (in spite of their excellent
563 cuisine).
565 Update by oej: The merger with KLM has transferred this
566 behaviour to KLM as well.
567 Don't bother giving them business either...
569 Only if you want to travel randomly without luggage, you
570 might pick either of them.
573 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
574 newtCenteredWindow(74, 20, tmp);
575 form = newtForm(NULL, NULL, 0);
576 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
577 newtFormSetTimer(form, 100);
578 quit = newtButton(62, 16, "Quit");
579 redirect = newtButton(35, 16, "Redirect");
580 hangup = newtButton(50, 16, "Hangup");
581 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
582 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
583 newtListboxSetWidth(channels, 72);
585 show_doing("Getting Status", "Retrieving system status...");
586 try_status();
587 hide_doing();
589 for(;;) {
590 newtFormRun(form, &es);
591 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
592 if (input_check(&session, NULL)) {
593 show_message("Disconnected", "Disconnected from remote host");
594 break;
596 } else if (es.reason == NEWT_EXIT_COMPONENT) {
597 if (es.u.co == quit)
598 break;
599 if (es.u.co == hangup) {
600 try_hangup(channels);
601 } else if (es.u.co == redirect) {
602 try_redirect(channels);
605 rebuild_channels(channels);
607 newtFormDestroy(form);
608 return 0;
611 static int login(char *hostname)
613 newtComponent form;
614 newtComponent cancel;
615 newtComponent login;
616 newtComponent username;
617 newtComponent password;
618 newtComponent label;
619 newtComponent ulabel;
620 newtComponent plabel;
621 const char *user;
622 const char *pass;
623 struct message *m;
624 struct newtExitStruct es;
625 char tmp[55];
626 struct hostent *hp;
627 int res = -1;
629 session.fd = socket(AF_INET, SOCK_STREAM, 0);
630 if (session.fd < 0) {
631 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
632 show_message("Socket failed", tmp);
633 return -1;
636 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
637 show_doing("Connecting....", tmp);
640 hp = gethostbyname(hostname);
641 if (!hp) {
642 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
643 show_message("Host lookup failed", tmp);
644 return -1;
646 hide_doing();
647 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
648 show_doing("Connecting...", tmp);
650 session.sin.sin_family = AF_INET;
651 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
652 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
654 if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
655 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
656 show_message("Connect Failed", tmp);
657 return -1;
660 hide_doing();
662 login = newtButton(5, 6, "Login");
663 cancel = newtButton(25, 6, "Cancel");
664 newtCenteredWindow(40, 10, "Asterisk Manager Login");
665 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
666 label = newtLabel(4,1, tmp);
668 ulabel = newtLabel(4,2,"Username:");
669 plabel = newtLabel(4,3,"Password:");
671 username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
672 password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
674 form = newtForm(NULL, NULL, 0);
675 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
676 newtFormRun(form, &es);
677 if (es.reason == NEWT_EXIT_COMPONENT) {
678 if (es.u.co == login) {
679 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
680 show_doing("Logging in", tmp);
681 /* Check to see if the remote host supports MD5 Authentication */
682 manager_action("Challenge", "AuthType: MD5\r\n");
683 m = wait_for_response(10000);
684 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
685 char *challenge = get_header(m, "Challenge");
686 int x;
687 int len = 0;
688 char md5key[256] = "";
689 struct MD5Context md5;
690 unsigned char digest[16];
691 MD5Init(&md5);
692 MD5Update(&md5, (unsigned char *)challenge, strlen(challenge));
693 MD5Update(&md5, (unsigned char *)pass, strlen(pass));
694 MD5Final(digest, &md5);
695 for (x=0; x<16; x++)
696 len += sprintf(md5key + len, "%2.2x", digest[x]);
697 manager_action("Login",
698 "AuthType: MD5\r\n"
699 "Username: %s\r\n"
700 "Key: %s\r\n",
701 user, md5key);
702 m = wait_for_response(10000);
703 hide_doing();
704 if (!strcasecmp(get_header(m, "Response"), "Success")) {
705 res = 0;
706 } else {
707 show_message("Login Failed", get_header(m, "Message"));
709 } else {
710 memset(m, 0, sizeof(m));
711 manager_action("Login",
712 "Username: %s\r\n"
713 "Secret: %s\r\n",
714 user, pass);
715 m = wait_for_response(10000);
716 hide_doing();
717 if (m) {
718 if (!strcasecmp(get_header(m, "Response"), "Success")) {
719 res = 0;
720 } else {
721 show_message("Login Failed", get_header(m, "Message"));
727 newtFormDestroy(form);
728 return res;
731 int main(int argc, char *argv[])
733 if (argc < 2) {
734 fprintf(stderr, "Usage: astman <host>\n");
735 exit(1);
737 newtInit();
738 newtCls();
739 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
740 newtPushHelpLine("Welcome to the Asterisk Manager!");
741 if (login(argv[1])) {
742 newtFinished();
743 exit(1);
745 manage_calls(argv[1]);
746 newtFinished();
747 return 0;