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