Merge "bridges: Remove reliance on stasis caching"
[asterisk/asterisk.git] / utils / astman.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*
20  *
21  * ASTerisk MANager
22  *
23  */
24
25 /*** MODULEINFO
26         <support_level>extended</support_level>
27  ***/
28
29 #define ASTMM_LIBC ASTMM_IGNORE
30 #include "asterisk.h"
31
32 #include <newt.h>
33 #include <stdio.h>
34 #include <sys/time.h>
35 #include <netdb.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <sys/socket.h>
39 #include <sys/select.h>
40 #include <fcntl.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45
46 #include "asterisk/md5.h"
47 #include "asterisk/linkedlists.h"
48
49 #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
50
51 #undef gethostbyname
52
53 #define MAX_HEADERS 80
54 #define MAX_LEN 256
55
56 /*
57  * 2005.05.27 - different versions of newt define the type of the buffer
58  * for the 5th argument to newtEntry() as char ** or const char ** . To
59  * let the code compile cleanly with -Werror, we cast it to void * through
60  * _NEWT_CAST.
61  */
62 #define _NEWT_CAST (void *)
63
64 #define DEFAULT_MANAGER_PORT 5038
65
66 struct message {
67         unsigned int hdrcount;
68         char headers[MAX_HEADERS][MAX_LEN];
69 };
70
71 static struct ast_mansession {
72         struct sockaddr_in sin;
73         int fd;
74         char inbuf[MAX_LEN];
75         int inlen;
76 } session;
77
78 struct ast_chan {
79         char name[80];
80         char exten[20];
81         char context[20];
82         char priority[20];
83         char callerid[40];
84         char state[10];
85         AST_LIST_ENTRY(ast_chan) list;
86 };
87
88 static AST_LIST_HEAD_NOLOCK_STATIC(chans, ast_chan);
89
90 /* dummy functions to be compatible with the Asterisk core for md5.c */
91 int ast_add_profile(const char *, uint64_t scale);
92 int ast_add_profile(const char *s, uint64_t scale)
93 {
94         return -1;
95 }
96
97 int64_t ast_profile(int, int64_t);
98 int64_t ast_profile(int key, int64_t val)
99 {
100         return 0;
101 }
102 int64_t ast_mark(int, int start1_stop0);
103 int64_t ast_mark(int key, int start1_stop0)
104 {
105         return 0;
106 }
107
108 /* end of dummy functions */
109
110 static struct ast_chan *find_chan(char *name)
111 {
112         struct ast_chan *chan;
113         AST_LIST_TRAVERSE(&chans, chan, list) {
114                 if (!strcmp(name, chan->name))
115                         return chan;
116         }
117         chan = malloc(sizeof(struct ast_chan));
118         if (chan) {
119                 memset(chan, 0, sizeof(struct ast_chan));
120                 strncpy(chan->name, name, sizeof(chan->name) - 1);
121                 AST_LIST_INSERT_TAIL(&chans, chan, list);
122         }
123         return chan;
124 }
125
126 static void del_chan(char *name)
127 {
128         struct ast_chan *chan;
129
130         AST_LIST_TRAVERSE_SAFE_BEGIN(&chans, chan, list) {
131                 if (!strcmp(name, chan->name)) {
132                         AST_LIST_REMOVE_CURRENT(list);
133                         free(chan);
134                         return;
135                 }
136         }
137         AST_LIST_TRAVERSE_SAFE_END;
138 }
139
140
141 static void __attribute__((format(printf, 2, 3))) fdprintf(int fd, char *fmt, ...)
142 {
143         char stuff[4096];
144         va_list ap;
145
146         va_start(ap, fmt);
147         vsnprintf(stuff, sizeof(stuff), fmt, ap);
148         va_end(ap);
149         if (write(fd, stuff, strlen(stuff)) < 0) {
150                 fprintf(stderr, "write() failed: %s\n", strerror(errno));
151         }
152 }
153
154 static char *get_header(struct message *m, char *var)
155 {
156         char cmp[80];
157         int x;
158         snprintf(cmp, sizeof(cmp), "%s: ", var);
159         for (x=0;x<m->hdrcount;x++)
160                 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
161                         return m->headers[x] + strlen(cmp);
162         return "";
163 }
164
165 static int event_newstate(struct ast_mansession *s, struct message *m)
166 {
167         struct ast_chan *chan;
168         chan = find_chan(get_header(m, "Channel"));
169         strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
170         return 0;
171 }
172
173 static int event_newexten(struct ast_mansession *s, struct message *m)
174 {
175         struct ast_chan *chan;
176         chan = find_chan(get_header(m, "Channel"));
177         strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
178         strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
179         strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
180         return 0;
181 }
182
183 static int event_newchannel(struct ast_mansession *s, struct message *m)
184 {
185         struct ast_chan *chan;
186         chan = find_chan(get_header(m, "Channel"));
187         strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
188         strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
189         return 0;
190 }
191
192 static int event_status(struct ast_mansession *s, struct message *m)
193 {
194         struct ast_chan *chan;
195         chan = find_chan(get_header(m, "Channel"));
196         strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1);
197         strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1);
198         strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1);
199         strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1);
200         strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1);
201         return 0;
202 }
203
204 static int event_hangup(struct ast_mansession *s, struct message *m)
205 {
206         del_chan(get_header(m, "Channel"));
207         return 0;
208 }
209
210 static int event_ignore(struct ast_mansession *s, struct message *m)
211 {
212         return 0;
213 }
214
215 static int event_rename(struct ast_mansession *s, struct message *m)
216 {
217         struct ast_chan *chan;
218         chan = find_chan(get_header(m, "Oldname"));
219         strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1);
220         return 0;
221 }
222 static struct event {
223         char *event;
224         int (*func)(struct ast_mansession *s, struct message *m);
225 } events[] = {
226         { "Newstate", event_newstate },
227         { "Newchannel", event_newchannel },
228         { "Newexten", event_newexten },
229         { "Hangup", event_hangup },
230         { "Rename", event_rename },
231         { "Status", event_status },
232         { "Link", event_ignore },
233         { "Unlink", event_ignore },
234         { "StatusComplete", event_ignore },
235         { "Dial", event_ignore },
236         { "PeerStatus", event_ignore },
237         { "MessageWaiting", event_ignore },
238         { "Newcallerid", event_ignore },
239         { "AGIExec", event_ignore},
240         { "VarSet", event_ignore},
241         { "MeetmeTalking", event_ignore},
242         { "MeetmeJoin", event_ignore},
243         { "MeetmeLeave", event_ignore},
244         { "MeetmeEnd", event_ignore},
245         { "MeetmeMute", event_ignore},
246         { "Masquerade", event_ignore},
247 };
248
249 static int process_message(struct ast_mansession *s, struct message *m)
250 {
251         int x;
252         char event[80] = "";
253         strncpy(event, get_header(m, "Event"), sizeof(event) - 1);
254         if (!strlen(event)) {
255                 fprintf(stderr, "Missing event in request");
256                 return 0;
257         }
258         for (x = 0; x < ARRAY_LEN(events); x++) {
259                 if (!strcasecmp(event, events[x].event)) {
260                         if (events[x].func(s, m))
261                                 return -1;
262                         break;
263                 }
264         }
265         if (x >= ARRAY_LEN(events))
266                 fprintf(stderr, "Ignoring unknown event '%s'", event);
267 #if 0
268         for (x=0;x<m->hdrcount;x++) {
269                 printf("Header: %s\n", m->headers[x]);
270         }
271 #endif
272         return 0;
273 }
274
275 static void rebuild_channels(newtComponent c)
276 {
277         void *prev = NULL;
278         struct ast_chan *chan;
279         char tmpn[sizeof(chan->name) + sizeof(chan->callerid) + 3 - 1];
280         char tmp[256];
281         int x=0;
282         prev = newtListboxGetCurrent(c);
283         newtListboxClear(c);
284         AST_LIST_TRAVERSE(&chans, chan, list) {
285                 snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid);
286                 if (strlen(chan->exten))
287                         snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s",
288                                 tmpn, chan->state,
289                                 chan->exten, chan->context, chan->priority);
290                 else
291                         snprintf(tmp, sizeof(tmp), "%-30s %8s",
292                                 tmpn, chan->state);
293                 newtListboxAppendEntry(c, tmp, chan);
294                 x++;
295         }
296         if (!x)
297                 newtListboxAppendEntry(c, " << No Active Channels >> ", NULL);
298         newtListboxSetCurrentByKey(c, prev);
299 }
300
301 static int has_input(struct ast_mansession *s)
302 {
303         int x;
304         for (x=1;x<s->inlen;x++)
305                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r'))
306                         return 1;
307         return 0;
308 }
309
310 static int get_input(struct ast_mansession *s, char *output)
311 {
312         /* output must have at least sizeof(s->inbuf) space */
313         int res;
314         int x;
315         struct timeval tv = {0, 0};
316         fd_set fds;
317         for (x=1;x<s->inlen;x++) {
318                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
319                         /* Copy output data up to and including \r\n */
320                         memcpy(output, s->inbuf, x + 1);
321                         /* Add trailing \0 */
322                         output[x+1] = '\0';
323                         /* Move remaining data back to the front */
324                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
325                         s->inlen -= (x + 1);
326                         return 1;
327                 }
328         }
329         if (s->inlen >= sizeof(s->inbuf) - 1) {
330                 fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
331                 s->inlen = 0;
332         }
333         FD_ZERO(&fds);
334         FD_SET(s->fd, &fds);
335         res = select(s->fd + 1, &fds, NULL, NULL, &tv);
336         if (res < 0) {
337                 fprintf(stderr, "Select returned error: %s\n", strerror(errno));
338         } else if (res > 0) {
339                 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
340                 if (res < 1)
341                         return -1;
342                 s->inlen += res;
343                 s->inbuf[s->inlen] = '\0';
344         } else {
345                 return 2;
346         }
347         return 0;
348 }
349
350 static int input_check(struct ast_mansession *s, struct message **mout)
351 {
352         static struct message m;
353         int res;
354
355         if (mout)
356                 *mout = NULL;
357
358         for(;;) {
359                 res = get_input(s, m.headers[m.hdrcount]);
360                 if (res == 1) {
361 #if 0
362                         fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]);
363                         fgetc(stdin);
364 #endif
365                         /* Strip trailing \r\n */
366                         if (strlen(m.headers[m.hdrcount]) < 2)
367                                 continue;
368                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
369                         if (!strlen(m.headers[m.hdrcount])) {
370                                 if (mout && strlen(get_header(&m, "Response"))) {
371                                         *mout = &m;
372                                         return 0;
373                                 }
374                                 if (process_message(s, &m))
375                                         break;
376                                 memset(&m, 0, sizeof(m));
377                         } else if (m.hdrcount < MAX_HEADERS - 1)
378                                 m.hdrcount++;
379                 } else if (res < 0) {
380                         return -1;
381                 } else if (res == 2)
382                         return 0;
383         }
384         return -1;
385 }
386
387 static struct message *wait_for_response(int timeout)
388 {
389         struct message *m;
390         struct timeval tv;
391         int res;
392         fd_set fds;
393         for (;;) {
394                 tv.tv_sec = timeout / 1000;
395                 tv.tv_usec = (timeout % 1000) * 1000;
396                 FD_SET(session.fd, &fds);
397                 res = select(session.fd + 1, &fds, NULL, NULL, &tv);
398                 if (res < 1)
399                         break;
400                 if (input_check(&session, &m) < 0) {
401                         return NULL;
402                 }
403                 if (m)
404                         return m;
405         }
406         return NULL;
407 }
408
409
410 static int __attribute__((format(printf, 2, 3))) manager_action(char *action, char *fmt, ...)
411 {
412         struct ast_mansession *s;
413         char tmp[4096];
414         va_list ap;
415         int res;
416
417         s = &session;
418         fdprintf(s->fd, "Action: %s\r\n", action);
419         va_start(ap, fmt);
420         vsnprintf(tmp, sizeof(tmp), fmt, ap);
421         va_end(ap);
422         if ((res = write(s->fd, tmp, strlen(tmp))) < 0) {
423                 fprintf(stderr, "write() failed: %s\n", strerror(errno));
424         }
425         fdprintf(s->fd, "\r\n");
426         return 0;
427 }
428
429 static int show_message(char *title, char *msg)
430 {
431         newtComponent form;
432         newtComponent label;
433         newtComponent ok;
434         struct newtExitStruct es;
435
436         newtCenteredWindow(60,7, title);
437
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;
446 }
447
448 static newtComponent showform;
449 static int show_doing(char *title, char *tmp)
450 {
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;
460 }
461
462 static int hide_doing(void)
463 {
464         newtPopWindow();
465         newtFormDestroy(showform);
466         return 0;
467 }
468
469 static void try_status(void)
470 {
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"));
478         }
479 }
480
481
482 static void try_hangup(newtComponent c)
483 {
484         struct ast_chan *chan;
485         struct message *m;
486
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"));
495                 }
496         }
497
498 }
499
500 static int get_user_input(char *msg, char *buf, int buflen)
501 {
502         newtComponent form;
503         newtComponent ok;
504         newtComponent cancel;
505         newtComponent inpfield;
506         const char *input;
507         int res = -1;
508         struct newtExitStruct es;
509
510         newtCenteredWindow(60,7, msg);
511
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;
526 }
527
528 static void try_redirect(newtComponent c)
529 {
530         struct ast_chan *chan;
531         char dest[256];
532         struct message *m;
533         static const char tmp_prefix[] = "Enter new extension for ";
534         char channame[256];
535         char tmp[sizeof(tmp_prefix) + sizeof(channame)];
536         char *context;
537
538         chan = newtListboxGetCurrent(c);
539         if (chan) {
540                 strncpy(channame, chan->name, sizeof(channame) - 1);
541                 snprintf(tmp, sizeof(tmp), "%s%s", tmp_prefix, channame);
542                 if (get_user_input(tmp, dest, sizeof(dest)))
543                         return;
544                 if ((context = strchr(dest, '@'))) {
545                         *context = '\0';
546                         context++;
547                         manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest);
548                 } else {
549                         manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest);
550                 }
551                 m = wait_for_response(10000);
552                 if (!m) {
553                         show_message("Hangup Failed", "Timeout waiting for response");
554                 } else if (strcasecmp(get_header(m, "Response"), "Success"))  {
555                         show_message("Hangup Failed", get_header(m, "Message"));
556                 }
557         }
558
559 }
560
561 static int manage_calls(char *host)
562 {
563         newtComponent form;
564         newtComponent quit;
565         newtComponent hangup;
566         newtComponent redirect;
567         newtComponent channels;
568         struct newtExitStruct es;
569         char tmp[80];
570
571         /* Mark: If there's one thing you learn from this code, it is this...
572            Never, ever fly Air France.  Their customer service is absolutely
573            the worst.  I've never heard the words "That's not my problem" as
574            many times as I have from their staff -- It should, without doubt
575            be their corporate motto if it isn't already.  Don't bother giving
576            them business because you're just a pain in their side and they
577            will be sure to let you know the first time you speak to them.
578
579            If you ever want to make me happy just tell me that you, too, will
580            never fly Air France again either (in spite of their excellent
581            cuisine).
582
583            Update by oej: The merger with KLM has transferred this
584            behaviour to KLM as well.
585            Don't bother giving them business either...
586
587            Only if you want to travel randomly without luggage, you
588            might pick either of them.
589
590         */
591         snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
592         newtCenteredWindow(74, 20, tmp);
593         form = newtForm(NULL, NULL, 0);
594         newtFormWatchFd(form, session.fd, NEWT_FD_READ);
595         newtFormSetTimer(form, 100);
596         quit = newtButton(62, 16, "Quit");
597         redirect = newtButton(35, 16, "Redirect");
598         hangup = newtButton(50, 16, "Hangup");
599         channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
600         newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
601         newtListboxSetWidth(channels, 72);
602
603         show_doing("Getting Status", "Retrieving system status...");
604         try_status();
605         hide_doing();
606
607         for(;;) {
608                 newtFormRun(form, &es);
609                 if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
610                         if (input_check(&session, NULL)) {
611                                 show_message("Disconnected", "Disconnected from remote host");
612                                 break;
613                         }
614                 } else if (es.reason == NEWT_EXIT_COMPONENT) {
615                         if (es.u.co == quit)
616                                 break;
617                         if (es.u.co == hangup) {
618                                 try_hangup(channels);
619                         } else if (es.u.co == redirect) {
620                                 try_redirect(channels);
621                         }
622                 }
623                 rebuild_channels(channels);
624         }
625         newtFormDestroy(form);
626         return 0;
627 }
628
629 static int manager_login(char *hostname)
630 {
631         newtComponent form;
632         newtComponent cancel;
633         newtComponent login;
634         newtComponent username;
635         newtComponent password;
636         newtComponent label;
637         newtComponent ulabel;
638         newtComponent plabel;
639         const char *user;
640         const char *pass;
641         struct message *m;
642         struct newtExitStruct es;
643         char tmp[55];
644         struct hostent *hp;
645         int res = -1;
646
647         session.fd = socket(AF_INET, SOCK_STREAM, 0);
648         if (session.fd < 0) {
649                 snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno));
650                 show_message("Socket failed", tmp);
651                 return -1;
652         }
653
654         snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname);
655         show_doing("Connecting....", tmp);
656
657
658         hp = gethostbyname(hostname);
659         if (!hp) {
660                 snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname);
661                 show_message("Host lookup failed", tmp);
662                 return -1;
663         }
664         hide_doing();
665         snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname);
666         show_doing("Connecting...", tmp);
667
668         session.sin.sin_family = AF_INET;
669         session.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
670         memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr));
671
672         if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) {
673                 snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno));
674                 show_message("Connect Failed", tmp);
675                 return -1;
676         }
677
678         hide_doing();
679
680         login = newtButton(5, 6, "Login");
681         cancel = newtButton(25, 6, "Cancel");
682         newtCenteredWindow(40, 10, "Asterisk Manager Login");
683         snprintf(tmp, sizeof(tmp), "Host:     %s", hostname);
684         label = newtLabel(4,1, tmp);
685
686         ulabel = newtLabel(4,2,"Username:");
687         plabel = newtLabel(4,3,"Password:");
688
689         username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0);
690         password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN);
691
692         form = newtForm(NULL, NULL, 0);
693         newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL);
694         newtFormRun(form, &es);
695         if (es.reason == NEWT_EXIT_COMPONENT) {
696                 if (es.u.co == login) {
697                         snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user);
698                         show_doing("Logging in", tmp);
699                         /* Check to see if the remote host supports MD5 Authentication */
700                         manager_action("Challenge", "AuthType: MD5\r\n");
701                         m = wait_for_response(10000);
702                         if (m && !strcasecmp(get_header(m, "Response"), "Success")) {
703                                 char *challenge = get_header(m, "Challenge");
704                                 int x;
705                                 int len = 0;
706                                 char md5key[256] = "";
707                                 struct MD5Context md5;
708                                 unsigned char digest[16];
709                                 MD5Init(&md5);
710                                 MD5Update(&md5, (unsigned char *)challenge, strlen(challenge));
711                                 MD5Update(&md5, (unsigned char *)pass, strlen(pass));
712                                 MD5Final(digest, &md5);
713                                 for (x=0; x<16; x++)
714                                         len += sprintf(md5key + len, "%02hhx", digest[x]);
715                                 manager_action("Login",
716                                                 "AuthType: MD5\r\n"
717                                                 "Username: %s\r\n"
718                                                 "Key: %s\r\n",
719                                                 user, md5key);
720                                 m = wait_for_response(10000);
721                                 hide_doing();
722                                 if (!strcasecmp(get_header(m, "Response"), "Success")) {
723                                         res = 0;
724                                 } else {
725                                         show_message("Login Failed", get_header(m, "Message"));
726                                 }
727                         } else {
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"));
739                                         }
740                                 }
741                         }
742                 }
743         }
744         newtFormDestroy(form);
745         return res;
746 }
747
748 int main(int argc, char *argv[])
749 {
750         if (argc < 2) {
751                 fprintf(stderr, "Usage: astman <host>\n");
752                 exit(1);
753         }
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 (manager_login(argv[1])) {
759                 newtFinished();
760                 exit(1);
761         }
762         manage_calls(argv[1]);
763         newtFinished();
764         return 0;
765 }