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