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