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