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