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