Merged revisions 115884 via svnmerge from
[asterisk-bristuff.git] / utils / astman.c
blobf674105a5234207d6a1f7cd35c6ee952c0405d65
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"
26 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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 <errno.h>
38 #include <unistd.h>
39 #include <stdlib.h>
41 #include "asterisk/md5.h"
42 #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 #if !defined(LOW_MEMORY)
96 int ast_add_profile(const char *, uint64_t scale);
97 int ast_add_profile(const char *s, uint64_t scale)
99 return -1;
102 int64_t ast_profile(int, int64_t);
103 int64_t ast_profile(int key, int64_t val)
105 return 0;
107 int64_t ast_mark(int, int start1_stop0);
108 int64_t ast_mark(int key, int start1_stop0)
110 return 0;
112 #endif /* LOW_MEMORY */
114 /* end of dummy functions */
116 static struct ast_chan *find_chan(char *name)
118 struct ast_chan *chan;
119 AST_LIST_TRAVERSE(&chans, chan, list) {
120 if (!strcmp(name, chan->name))
121 return chan;
123 chan = malloc(sizeof(struct ast_chan));
124 if (chan) {
125 memset(chan, 0, sizeof(struct ast_chan));
126 strncpy(chan->name, name, sizeof(chan->name) - 1);
127 AST_LIST_INSERT_TAIL(&chans, chan, list);
129 return chan;
132 static void del_chan(char *name)
134 struct ast_chan *chan;
136 AST_LIST_TRAVERSE_SAFE_BEGIN(&chans, chan, list) {
137 if (!strcmp(name, chan->name)) {
138 AST_LIST_REMOVE_CURRENT(list);
139 free(chan);
140 return;
143 AST_LIST_TRAVERSE_SAFE_END;
147 static void __attribute__((format (printf, 2, 3))) fdprintf(int fd, char *fmt, ...)
149 char stuff[4096];
150 va_list ap;
151 va_start(ap, fmt);
152 vsnprintf(stuff, sizeof(stuff), fmt, ap);
153 va_end(ap);
154 write(fd, stuff, strlen(stuff));
157 static char *get_header(struct message *m, char *var)
159 char cmp[80];
160 int x;
161 snprintf(cmp, sizeof(cmp), "%s: ", var);
162 for (x=0;x<m->hdrcount;x++)
163 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
164 return m->headers[x] + strlen(cmp);
165 return "";
168 static int event_newstate(struct ast_mansession *s, struct message *m)
170 struct ast_chan *chan;
171 chan = find_chan(get_header(m, "Channel"));
172 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
173 return 0;
176 static int event_newexten(struct ast_mansession *s, struct message *m)
178 struct ast_chan *chan;
179 chan = find_chan(get_header(m, "Channel"));
180 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
181 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
182 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
183 return 0;
186 static int event_newchannel(struct ast_mansession *s, struct message *m)
188 struct ast_chan *chan;
189 chan = find_chan(get_header(m, "Channel"));
190 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
191 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
192 return 0;
195 static int event_status(struct ast_mansession *s, struct message *m)
197 struct ast_chan *chan;
198 chan = find_chan(get_header(m, "Channel"));
199 strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
200 strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
201 strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
202 strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
203 strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
204 return 0;
207 static int event_hangup(struct ast_mansession *s, struct message *m)
209 del_chan(get_header(m, "Channel"));
210 return 0;
213 static int event_ignore(struct ast_mansession *s, struct message *m)
215 return 0;
218 static int event_rename(struct ast_mansession *s, struct message *m)
220 struct ast_chan *chan;
221 chan = find_chan(get_header(m, "Oldname"));
222 strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
223 return 0;
225 static struct event {
226 char *event;
227 int (*func)(struct ast_mansession *s, struct message *m);
228 } events[] = {
229 { "Newstate", event_newstate },
230 { "Newchannel", event_newchannel },
231 { "Newexten", event_newexten },
232 { "Hangup", event_hangup },
233 { "Rename", event_rename },
234 { "Status", event_status },
235 { "Link", event_ignore },
236 { "Unlink", event_ignore },
237 { "StatusComplete", event_ignore },
238 { "Dial", event_ignore },
239 { "PeerStatus", event_ignore },
240 { "MessageWaiting", event_ignore },
241 { "Newcallerid", event_ignore },
242 { "AGIExec", event_ignore},
243 { "VarSet", event_ignore},
244 { "MeetmeTalking", event_ignore},
245 { "MeetmeJoin", event_ignore},
246 { "MeetmeLeave", event_ignore},
247 { "MeetmeEnd", event_ignore},
248 { "MeetmeMute", event_ignore},
249 { "Masquerade", event_ignore},
252 static int process_message(struct ast_mansession *s, struct message *m)
254 int x;
255 char event[80] = "";
256 strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
257 if (!strlen(event)) {
258 fprintf(stderr, "Missing event in request");
259 return 0;
261 for (x=0;x<sizeof(events) / sizeof(events[0]);x++) {
262 if (!strcasecmp(event, events[x].event)) {
263 if (events[x].func(s, m))
264 return -1;
265 break;
268 if (x >= sizeof(events) / sizeof(events[0]))
269 fprintf(stderr, "Ignoring unknown event '%s'", event);
270 #if 0
271 for (x=0;x<m->hdrcount;x++) {
272 printf("Header: %s\n", m->headers[x]);
274 #endif
275 return 0;
278 static void rebuild_channels(newtComponent c)
280 void *prev = NULL;
281 struct ast_chan *chan;
282 char tmpn[42];
283 char tmp[256];
284 int x=0;
285 prev = newtListboxGetCurrent(c);
286 newtListboxClear(c);
287 AST_LIST_TRAVERSE(&chans, chan, list) {
288 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
289 if (strlen(chan->exten))
290 snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
291 tmpn, chan->state,
292 chan->exten, chan->context, chan->priority);
293 else
294 snprintf(tmp, sizeof(tmp), "%-30s %8s",
295 tmpn, chan->state);
296 newtListboxAppendEntry(c, tmp, chan);
297 x++;
299 if (!x)
300 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
301 newtListboxSetCurrentByKey(c, prev);
304 static int has_input(struct ast_mansession *s)
306 int x;
307 for (x=1;x<s->inlen;x++)
308 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
309 return 1;
310 return 0;
313 static int get_input(struct ast_mansession *s, char *output)
315 /* output must have at least sizeof(s->inbuf) space */
316 int res;
317 int x;
318 struct timeval tv = {0, 0};
319 fd_set fds;
320 for (x=1;x<s->inlen;x++) {
321 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
322 /* Copy output data up to and including \r\n */
323 memcpy(output, s->inbuf, x + 1);
324 /* Add trailing \0 */
325 output[x+1] = '\0';
326 /* Move remaining data back to the front */
327 memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
328 s->inlen -= (x + 1);
329 return 1;
332 if (s->inlen >= sizeof(s->inbuf) - 1) {
333 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
334 s->inlen = 0;
336 FD_ZERO(&fds);
337 FD_SET(s->fd, &fds);
338 res = select(s->fd + 1, &fds, NULL, NULL, &tv);
339 if (res < 0) {
340 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
341 } else if (res > 0) {
342 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
343 if (res < 1)
344 return -1;
345 s->inlen += res;
346 s->inbuf[s->inlen] = '\0';
347 } else {
348 return 2;
350 return 0;
353 static int input_check(struct ast_mansession *s, struct message **mout)
355 static struct message m;
356 int res;
358 if (mout)
359 *mout = NULL;
361 for(;;) {
362 res = get_input(s, m.headers[m.hdrcount]);
363 if (res == 1) {
364 #if 0
365 fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
366 fgetc(stdin);
367 #endif
368 /* Strip trailing \r\n */
369 if (strlen(m.headers[m.hdrcount]) < 2)
370 continue;
371 m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
372 if (!strlen(m.headers[m.hdrcount])) {
373 if (mout && strlen(get_header(&m, "Response"))) {
374 *mout = &m;
375 return 0;
377 if (process_message(s, &m))
378 break;
379 memset(&m, 0, sizeof(&m));
380 } else if (m.hdrcount < MAX_HEADERS - 1)
381 m.hdrcount++;
382 } else if (res < 0) {
383 return -1;
384 } else if (res == 2)
385 return 0;
387 return -1;
390 static struct message *wait_for_response(int timeout)
392 struct message *m;
393 struct timeval tv;
394 int res;
395 fd_set fds;
396 for (;;) {
397 tv.tv_sec = timeout / 1000;
398 tv.tv_usec = (timeout % 1000) * 1000;
399 FD_SET(session.fd, &fds);
400 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
401 if (res < 1)
402 break;
403 if (input_check(&session, &m) < 0) {
404 return NULL;
406 if (m)
407 return m;
409 return NULL;
413 static int __attribute__((format (printf, 2, 3))) manager_action(char *action, char *fmt, ...)
415 struct ast_mansession *s;
416 char tmp[4096];
417 va_list ap;
419 s = &session;
420 fdprintf(s->fd, "Action: %s\r\n", action);
421 va_start(ap, fmt);
422 vsnprintf(tmp, sizeof(tmp), fmt, ap);
423 va_end(ap);
424 write(s->fd, tmp, strlen(tmp));
425 fdprintf(s->fd, "\r\n");
426 return 0;
429 static int show_message(char *title, char *msg)
431 newtComponent form;
432 newtComponent label;
433 newtComponent ok;
434 struct newtExitStruct es;
436 newtCenteredWindow(60,7, title);
438 label = newtLabel(4,1,msg);
439 ok = newtButton(27, 3, "OK");
440 form = newtForm(NULL, NULL, 0);
441 newtFormAddComponents(form, label, ok, NULL);
442 newtFormRun(form, &es);
443 newtPopWindow();
444 newtFormDestroy(form);
445 return 0;
448 static newtComponent showform;
449 static int show_doing(char *title, char *tmp)
451 struct newtExitStruct es;
452 newtComponent label;
453 showform = newtForm(NULL, NULL, 0);
454 newtCenteredWindow(70,4, title);
455 label = newtLabel(3,1,tmp);
456 newtFormAddComponents(showform,label, NULL);
457 newtFormSetTimer(showform, 200);
458 newtFormRun(showform, &es);
459 return 0;
462 static int hide_doing(void)
464 newtPopWindow();
465 newtFormDestroy(showform);
466 return 0;
469 static void try_status(void)
471 struct message *m;
472 manager_action("Status", "%s", "");
473 m = wait_for_response(10000);
474 if (!m) {
475 show_message("Status Failed", "Timeout waiting for response");
476 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
477 show_message("Status Failed Failed", get_header(m, "Message"));
482 static void try_hangup(newtComponent c)
484 struct ast_chan *chan;
485 struct message *m;
487 chan = newtListboxGetCurrent(c);
488 if (chan) {
489 manager_action("Hangup", "Channel: %s\r\n", chan->name);
490 m = wait_for_response(10000);
491 if (!m) {
492 show_message("Hangup Failed", "Timeout waiting for response");
493 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
494 show_message("Hangup Failed", get_header(m, "Message"));
500 static int get_user_input(char *msg, char *buf, int buflen)
502 newtComponent form;
503 newtComponent ok;
504 newtComponent cancel;
505 newtComponent inpfield;
506 const char *input;
507 int res = -1;
508 struct newtExitStruct es;
510 newtCenteredWindow(60,7, msg);
512 inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0);
513 ok = newtButton(22, 3, "OK");
514 cancel = newtButton(32, 3, "Cancel");
515 form = newtForm(NULL, NULL, 0);
516 newtFormAddComponents(form, inpfield, ok, cancel, NULL);
517 newtFormRun(form, &es);
518 strncpy(buf, input, buflen - 1);
519 if (es.u.co == ok)
520 res = 0;
521 else
522 res = -1;
523 newtPopWindow();
524 newtFormDestroy(form);
525 return res;
528 static void try_redirect(newtComponent c)
530 struct ast_chan *chan;
531 char dest[256];
532 struct message *m;
533 char channame[256];
534 char tmp[80];
535 char *context;
537 chan = newtListboxGetCurrent(c);
538 if (chan) {
539 strncpy(channame, chan->name, sizeof(channame) - 1);
540 snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame);
541 if (get_user_input(tmp, dest, sizeof(dest)))
542 return;
543 if ((context = strchr(dest, '@'))) {
544 *context = '\0';
545 context++;
546 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
547 } else {
548 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
550 m = wait_for_response(10000);
551 if (!m) {
552 show_message("Hangup Failed", "Timeout waiting for response");
553 } else if (strcasecmp(get_header(m, "Response"), "Success")) {
554 show_message("Hangup Failed", get_header(m, "Message"));
560 static int manage_calls(char *host)
562 newtComponent form;
563 newtComponent quit;
564 newtComponent hangup;
565 newtComponent redirect;
566 newtComponent channels;
567 struct newtExitStruct es;
568 char tmp[80];
570 /* Mark: If there's one thing you learn from this code, it is this...
571 Never, ever fly Air France. Their customer service is absolutely
572 the worst. I've never heard the words "That's not my problem" as
573 many times as I have from their staff -- It should, without doubt
574 be their corporate motto if it isn't already. Don't bother giving
575 them business because you're just a pain in their side and they
576 will be sure to let you know the first time you speak to them.
578 If you ever want to make me happy just tell me that you, too, will
579 never fly Air France again either (in spite of their excellent
580 cuisine).
582 Update by oej: The merger with KLM has transferred this
583 behaviour to KLM as well.
584 Don't bother giving them business either...
586 Only if you want to travel randomly without luggage, you
587 might pick either of them.
590 snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
591 newtCenteredWindow(74, 20, tmp);
592 form = newtForm(NULL, NULL, 0);
593 newtFormWatchFd(form, session.fd, NEWT_FD_READ);
594 newtFormSetTimer(form, 100);
595 quit = newtButton(62, 16, "Quit");
596 redirect = newtButton(35, 16, "Redirect");
597 hangup = newtButton(50, 16, "Hangup");
598 channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
599 newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
600 newtListboxSetWidth(channels, 72);
602 show_doing("Getting Status", "Retrieving system status...");
603 try_status();
604 hide_doing();
606 for(;;) {
607 newtFormRun(form, &es);
608 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
609 if (input_check(&session, NULL)) {
610 show_message("Disconnected", "Disconnected from remote host");
611 break;
613 } else if (es.reason == NEWT_EXIT_COMPONENT) {
614 if (es.u.co == quit)
615 break;
616 if (es.u.co == hangup) {
617 try_hangup(channels);
618 } else if (es.u.co == redirect) {
619 try_redirect(channels);
622 rebuild_channels(channels);
624 newtFormDestroy(form);
625 return 0;
628 static int login(char *hostname)
630 newtComponent form;
631 newtComponent cancel;
632 newtComponent login;
633 newtComponent username;
634 newtComponent password;
635 newtComponent label;
636 newtComponent ulabel;
637 newtComponent plabel;
638 const char *user;
639 const char *pass;
640 struct message *m;
641 struct newtExitStruct es;
642 char tmp[55];
643 struct hostent *hp;
644 int res = -1;
646 session.fd = socket(AF_INET, SOCK_STREAM, 0);
647 if (session.fd < 0) {
648 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
649 show_message("Socket failed", tmp);
650 return -1;
653 snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
654 show_doing("Connecting....", tmp);
657 hp = gethostbyname(hostname);
658 if (!hp) {
659 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
660 show_message("Host lookup failed", tmp);
661 return -1;
663 hide_doing();
664 snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
665 show_doing("Connecting...", tmp);
667 session.sin.sin_family = AF_INET;
668 session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
669 memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
671 if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
672 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
673 show_message("Connect Failed", tmp);
674 return -1;
677 hide_doing();
679 login = newtButton(5, 6, "Login");
680 cancel = newtButton(25, 6, "Cancel");
681 newtCenteredWindow(40, 10, "Asterisk Manager Login");
682 snprintf(tmp, sizeof(tmp), "Host: %s", hostname);
683 label = newtLabel(4,1, tmp);
685 ulabel = newtLabel(4,2,"Username:");
686 plabel = newtLabel(4,3,"Password:");
688 username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
689 password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
691 form = newtForm(NULL, NULL, 0);
692 newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
693 newtFormRun(form, &es);
694 if (es.reason == NEWT_EXIT_COMPONENT) {
695 if (es.u.co == login) {
696 snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
697 show_doing("Logging in", tmp);
698 /* Check to see if the remote host supports MD5 Authentication */
699 manager_action("Challenge", "AuthType: MD5\r\n");
700 m = wait_for_response(10000);
701 if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
702 char *challenge = get_header(m, "Challenge");
703 int x;
704 int len = 0;
705 char md5key[256] = "";
706 struct MD5Context md5;
707 unsigned char digest[16];
708 MD5Init(&md5);
709 MD5Update(&md5, (unsigned char *)challenge, strlen(challenge));
710 MD5Update(&md5, (unsigned char *)pass, strlen(pass));
711 MD5Final(digest, &md5);
712 for (x=0; x<16; x++)
713 len += sprintf(md5key + len, "%2.2x", digest[x]);
714 manager_action("Login",
715 "AuthType: MD5\r\n"
716 "Username: %s\r\n"
717 "Key: %s\r\n",
718 user, md5key);
719 m = wait_for_response(10000);
720 hide_doing();
721 if (!strcasecmp(get_header(m, "Response"), "Success")) {
722 res = 0;
723 } else {
724 show_message("Login Failed", get_header(m, "Message"));
726 } else {
727 memset(m, 0, sizeof(m));
728 manager_action("Login",
729 "Username: %s\r\n"
730 "Secret: %s\r\n",
731 user, pass);
732 m = wait_for_response(10000);
733 hide_doing();
734 if (m) {
735 if (!strcasecmp(get_header(m, "Response"), "Success")) {
736 res = 0;
737 } else {
738 show_message("Login Failed", get_header(m, "Message"));
744 newtFormDestroy(form);
745 return res;
748 int main(int argc, char *argv[])
750 if (argc < 2) {
751 fprintf(stderr, "Usage: astman <host>\n");
752 exit(1);
754 newtInit();
755 newtCls();
756 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
757 newtPushHelpLine("Welcome to the Asterisk Manager!");
758 if (login(argv[1])) {
759 newtFinished();
760 exit(1);
762 manage_calls(argv[1]);
763 newtFinished();
764 return 0;