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