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