2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <sys/socket.h>
32 #include <sys/select.h>
40 #include "asterisk/md5.h"
41 #include "asterisk/linkedlists.h"
45 #define MAX_HEADERS 80
49 * 2005.05.27 - different versions of newt define the type of the buffer
50 * for the 5th argument to newtEntry() as char ** or const char ** . To
51 * let the code compile cleanly with -Werror, we cast it to void * through
54 #define _NEWT_CAST (void *)
56 #define DEFAULT_MANAGER_PORT 5038
59 unsigned int hdrcount
;
60 char headers
[MAX_HEADERS
][MAX_LEN
];
63 static struct ast_mansession
{
64 struct sockaddr_in sin
;
77 AST_LIST_ENTRY(ast_chan
) list
;
80 static AST_LIST_HEAD_NOLOCK_STATIC(chans
, ast_chan
);
82 /* dummy functions to be compatible with the Asterisk core for md5.c */
83 void ast_register_file_version(const char *file
, const char *version
);
84 void ast_register_file_version(const char *file
, const char *version
)
88 void ast_unregister_file_version(const char *file
);
89 void ast_unregister_file_version(const char *file
)
93 int ast_add_profile(const char *, uint64_t scale
);
94 int ast_add_profile(const char *s
, uint64_t scale
)
99 int64_t ast_profile(int, int64_t);
100 int64_t ast_profile(int key
, int64_t val
)
104 int64_t ast_mark(int, int start1_stop0
);
105 int64_t ast_mark(int key
, int start1_stop0
)
110 /* end of dummy functions */
112 static struct ast_chan
*find_chan(char *name
)
114 struct ast_chan
*chan
;
115 AST_LIST_TRAVERSE(&chans
, chan
, list
) {
116 if (!strcmp(name
, chan
->name
))
119 chan
= malloc(sizeof(struct ast_chan
));
121 memset(chan
, 0, sizeof(struct ast_chan
));
122 strncpy(chan
->name
, name
, sizeof(chan
->name
) - 1);
123 AST_LIST_INSERT_TAIL(&chans
, chan
, list
);
128 static void del_chan(char *name
)
130 struct ast_chan
*chan
;
131 AST_LIST_TRAVERSE_SAFE_BEGIN(&chans
, chan
, list
) {
132 if (!strcmp(name
, chan
->name
)) {
133 AST_LIST_REMOVE_CURRENT(&chans
, list
);
138 AST_LIST_TRAVERSE_SAFE_END
141 static void fdprintf(int fd
, char *fmt
, ...)
146 vsnprintf(stuff
, sizeof(stuff
), fmt
, ap
);
148 write(fd
, stuff
, strlen(stuff
));
151 static char *get_header(struct message
*m
, char *var
)
155 snprintf(cmp
, sizeof(cmp
), "%s: ", var
);
156 for (x
=0;x
<m
->hdrcount
;x
++)
157 if (!strncasecmp(cmp
, m
->headers
[x
], strlen(cmp
)))
158 return m
->headers
[x
] + strlen(cmp
);
162 static int event_newstate(struct ast_mansession
*s
, struct message
*m
)
164 struct ast_chan
*chan
;
165 chan
= find_chan(get_header(m
, "Channel"));
166 strncpy(chan
->state
, get_header(m
, "State"), sizeof(chan
->state
) - 1);
170 static int event_newexten(struct ast_mansession
*s
, struct message
*m
)
172 struct ast_chan
*chan
;
173 chan
= find_chan(get_header(m
, "Channel"));
174 strncpy(chan
->exten
, get_header(m
, "Extension"), sizeof(chan
->exten
) - 1);
175 strncpy(chan
->context
, get_header(m
, "Context"), sizeof(chan
->context
) - 1);
176 strncpy(chan
->priority
, get_header(m
, "Priority"), sizeof(chan
->priority
) - 1);
180 static int event_newchannel(struct ast_mansession
*s
, struct message
*m
)
182 struct ast_chan
*chan
;
183 chan
= find_chan(get_header(m
, "Channel"));
184 strncpy(chan
->state
, get_header(m
, "State"), sizeof(chan
->state
) - 1);
185 strncpy(chan
->callerid
, get_header(m
, "Callerid"), sizeof(chan
->callerid
) - 1);
189 static int event_status(struct ast_mansession
*s
, struct message
*m
)
191 struct ast_chan
*chan
;
192 chan
= find_chan(get_header(m
, "Channel"));
193 strncpy(chan
->state
, get_header(m
, "State"), sizeof(chan
->state
) - 1);
194 strncpy(chan
->callerid
, get_header(m
, "Callerid"), sizeof(chan
->callerid
) - 1);
195 strncpy(chan
->exten
, get_header(m
, "Extension"), sizeof(chan
->exten
) - 1);
196 strncpy(chan
->context
, get_header(m
, "Context"), sizeof(chan
->context
) - 1);
197 strncpy(chan
->priority
, get_header(m
, "Priority"), sizeof(chan
->priority
) - 1);
201 static int event_hangup(struct ast_mansession
*s
, struct message
*m
)
203 del_chan(get_header(m
, "Channel"));
207 static int event_ignore(struct ast_mansession
*s
, struct message
*m
)
212 static int event_rename(struct ast_mansession
*s
, struct message
*m
)
214 struct ast_chan
*chan
;
215 chan
= find_chan(get_header(m
, "Oldname"));
216 strncpy(chan
->name
, get_header(m
, "Newname"), sizeof(chan
->name
) - 1);
219 static struct event
{
221 int (*func
)(struct ast_mansession
*s
, struct message
*m
);
223 { "Newstate", event_newstate
},
224 { "Newchannel", event_newchannel
},
225 { "Newexten", event_newexten
},
226 { "Hangup", event_hangup
},
227 { "Rename", event_rename
},
228 { "Status", event_status
},
229 { "Link", event_ignore
},
230 { "Unlink", event_ignore
},
231 { "StatusComplete", event_ignore
}
234 static int process_message(struct ast_mansession
*s
, struct message
*m
)
238 strncpy(event
, get_header(m
, "Event"), sizeof(event
) - 1);
239 if (!strlen(event
)) {
240 fprintf(stderr
, "Missing event in request");
243 for (x
=0;x
<sizeof(events
) / sizeof(events
[0]);x
++) {
244 if (!strcasecmp(event
, events
[x
].event
)) {
245 if (events
[x
].func(s
, m
))
250 if (x
>= sizeof(events
) / sizeof(events
[0]))
251 fprintf(stderr
, "Ignoring unknown event '%s'", event
);
253 for (x
=0;x
<m
->hdrcount
;x
++) {
254 printf("Header: %s\n", m
->headers
[x
]);
260 static void rebuild_channels(newtComponent c
)
263 struct ast_chan
*chan
;
267 prev
= newtListboxGetCurrent(c
);
269 AST_LIST_TRAVERSE(&chans
, chan
, list
) {
270 snprintf(tmpn
, sizeof(tmpn
), "%s (%s)", chan
->name
, chan
->callerid
);
271 if (strlen(chan
->exten
))
272 snprintf(tmp
, sizeof(tmp
), "%-30s %8s -> %s@%s:%s",
274 chan
->exten
, chan
->context
, chan
->priority
);
276 snprintf(tmp
, sizeof(tmp
), "%-30s %8s",
278 newtListboxAppendEntry(c
, tmp
, chan
);
282 newtListboxAppendEntry(c
, " << No Active Channels >> ", NULL
);
283 newtListboxSetCurrentByKey(c
, prev
);
286 static int has_input(struct ast_mansession
*s
)
289 for (x
=1;x
<s
->inlen
;x
++)
290 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r'))
295 static int get_input(struct ast_mansession
*s
, char *output
)
297 /* output must have at least sizeof(s->inbuf) space */
300 struct timeval tv
= {0, 0};
302 for (x
=1;x
<s
->inlen
;x
++) {
303 if ((s
->inbuf
[x
] == '\n') && (s
->inbuf
[x
-1] == '\r')) {
304 /* Copy output data up to and including \r\n */
305 memcpy(output
, s
->inbuf
, x
+ 1);
306 /* Add trailing \0 */
308 /* Move remaining data back to the front */
309 memmove(s
->inbuf
, s
->inbuf
+ x
+ 1, s
->inlen
- x
);
314 if (s
->inlen
>= sizeof(s
->inbuf
) - 1) {
315 fprintf(stderr
, "Dumping long line with no return from %s: %s\n", inet_ntoa(s
->sin
.sin_addr
), s
->inbuf
);
320 res
= select(s
->fd
+ 1, &fds
, NULL
, NULL
, &tv
);
322 fprintf(stderr
, "Select returned error: %s\n", strerror(errno
));
323 } else if (res
> 0) {
324 res
= read(s
->fd
, s
->inbuf
+ s
->inlen
, sizeof(s
->inbuf
) - 1 - s
->inlen
);
328 s
->inbuf
[s
->inlen
] = '\0';
335 static int input_check(struct ast_mansession
*s
, struct message
**mout
)
337 static struct message m
;
344 res
= get_input(s
, m
.headers
[m
.hdrcount
]);
347 fprintf(stderr
, "Got header: %s", m
.headers
[m
.hdrcount
]);
350 /* Strip trailing \r\n */
351 if (strlen(m
.headers
[m
.hdrcount
]) < 2)
353 m
.headers
[m
.hdrcount
][strlen(m
.headers
[m
.hdrcount
]) - 2] = '\0';
354 if (!strlen(m
.headers
[m
.hdrcount
])) {
355 if (mout
&& strlen(get_header(&m
, "Response"))) {
359 if (process_message(s
, &m
))
361 memset(&m
, 0, sizeof(&m
));
362 } else if (m
.hdrcount
< MAX_HEADERS
- 1)
364 } else if (res
< 0) {
372 static struct message
*wait_for_response(int timeout
)
379 tv
.tv_sec
= timeout
/ 1000;
380 tv
.tv_usec
= (timeout
% 1000) * 1000;
381 FD_SET(session
.fd
, &fds
);
382 res
= select(session
.fd
+ 1, &fds
, NULL
, NULL
, &tv
);
385 if (input_check(&session
, &m
) < 0) {
394 static int manager_action(char *action
, char *fmt
, ...)
396 struct ast_mansession
*s
;
401 fdprintf(s
->fd
, "Action: %s\r\n", action
);
403 vsnprintf(tmp
, sizeof(tmp
), fmt
, ap
);
405 write(s
->fd
, tmp
, strlen(tmp
));
406 fdprintf(s
->fd
, "\r\n");
410 static int show_message(char *title
, char *msg
)
415 struct newtExitStruct es
;
417 newtCenteredWindow(60,7, title
);
419 label
= newtLabel(4,1,msg
);
420 ok
= newtButton(27, 3, "OK");
421 form
= newtForm(NULL
, NULL
, 0);
422 newtFormAddComponents(form
, label
, ok
, NULL
);
423 newtFormRun(form
, &es
);
425 newtFormDestroy(form
);
429 static newtComponent showform
;
430 static int show_doing(char *title
, char *tmp
)
432 struct newtExitStruct es
;
434 showform
= newtForm(NULL
, NULL
, 0);
435 newtCenteredWindow(70,4, title
);
436 label
= newtLabel(3,1,tmp
);
437 newtFormAddComponents(showform
,label
, NULL
);
438 newtFormSetTimer(showform
, 200);
439 newtFormRun(showform
, &es
);
443 static int hide_doing(void)
446 newtFormDestroy(showform
);
450 static void try_status(void)
453 manager_action("Status", "");
454 m
= wait_for_response(10000);
456 show_message("Status Failed", "Timeout waiting for response");
457 } else if (strcasecmp(get_header(m
, "Response"), "Success")) {
458 show_message("Status Failed Failed", get_header(m
, "Message"));
463 static void try_hangup(newtComponent c
)
465 struct ast_chan
*chan
;
468 chan
= newtListboxGetCurrent(c
);
470 manager_action("Hangup", "Channel: %s\r\n", chan
->name
);
471 m
= wait_for_response(10000);
473 show_message("Hangup Failed", "Timeout waiting for response");
474 } else if (strcasecmp(get_header(m
, "Response"), "Success")) {
475 show_message("Hangup Failed", get_header(m
, "Message"));
481 static int get_user_input(char *msg
, char *buf
, int buflen
)
485 newtComponent cancel
;
486 newtComponent inpfield
;
489 struct newtExitStruct es
;
491 newtCenteredWindow(60,7, msg
);
493 inpfield
= newtEntry(5, 2, "", 50, _NEWT_CAST
&input
, 0);
494 ok
= newtButton(22, 3, "OK");
495 cancel
= newtButton(32, 3, "Cancel");
496 form
= newtForm(NULL
, NULL
, 0);
497 newtFormAddComponents(form
, inpfield
, ok
, cancel
, NULL
);
498 newtFormRun(form
, &es
);
499 strncpy(buf
, input
, buflen
- 1);
505 newtFormDestroy(form
);
509 static void try_redirect(newtComponent c
)
511 struct ast_chan
*chan
;
518 chan
= newtListboxGetCurrent(c
);
520 strncpy(channame
, chan
->name
, sizeof(channame
) - 1);
521 snprintf(tmp
, sizeof(tmp
), "Enter new extension for %s", channame
);
522 if (get_user_input(tmp
, dest
, sizeof(dest
)))
524 if ((context
= strchr(dest
, '@'))) {
527 manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan
->name
,context
,dest
);
529 manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan
->name
, dest
);
531 m
= wait_for_response(10000);
533 show_message("Hangup Failed", "Timeout waiting for response");
534 } else if (strcasecmp(get_header(m
, "Response"), "Success")) {
535 show_message("Hangup Failed", get_header(m
, "Message"));
541 static int manage_calls(char *host
)
545 newtComponent hangup
;
546 newtComponent redirect
;
547 newtComponent channels
;
548 struct newtExitStruct es
;
551 /* Mark: If there's one thing you learn from this code, it is this...
552 Never, ever fly Air France. Their customer service is absolutely
553 the worst. I've never heard the words "That's not my problem" as
554 many times as I have from their staff -- It should, without doubt
555 be their corporate motto if it isn't already. Don't bother giving
556 them business because you're just a pain in their side and they
557 will be sure to let you know the first time you speak to them.
559 If you ever want to make me happy just tell me that you, too, will
560 never fly Air France again either (in spite of their excellent
563 Update by oej: The merger with KLM has transferred this
564 behaviour to KLM as well.
565 Don't bother giving them business either...
567 Only if you want to travel randomly without luggage, you
568 might pick either of them.
571 snprintf(tmp
, sizeof(tmp
), "Asterisk Manager at %s", host
);
572 newtCenteredWindow(74, 20, tmp
);
573 form
= newtForm(NULL
, NULL
, 0);
574 newtFormWatchFd(form
, session
.fd
, NEWT_FD_READ
);
575 newtFormSetTimer(form
, 100);
576 quit
= newtButton(62, 16, "Quit");
577 redirect
= newtButton(35, 16, "Redirect");
578 hangup
= newtButton(50, 16, "Hangup");
579 channels
= newtListbox(1,1,14, NEWT_FLAG_SCROLL
);
580 newtFormAddComponents(form
, channels
, redirect
, hangup
, quit
, NULL
);
581 newtListboxSetWidth(channels
, 72);
583 show_doing("Getting Status", "Retrieving system status...");
588 newtFormRun(form
, &es
);
589 if (has_input(&session
) || (es
.reason
== NEWT_EXIT_FDREADY
)) {
590 if (input_check(&session
, NULL
)) {
591 show_message("Disconnected", "Disconnected from remote host");
594 } else if (es
.reason
== NEWT_EXIT_COMPONENT
) {
597 if (es
.u
.co
== hangup
) {
598 try_hangup(channels
);
599 } else if (es
.u
.co
== redirect
) {
600 try_redirect(channels
);
603 rebuild_channels(channels
);
605 newtFormDestroy(form
);
609 static int login(char *hostname
)
612 newtComponent cancel
;
614 newtComponent username
;
615 newtComponent password
;
617 newtComponent ulabel
;
618 newtComponent plabel
;
622 struct newtExitStruct es
;
627 session
.fd
= socket(AF_INET
, SOCK_STREAM
, 0);
628 if (session
.fd
< 0) {
629 snprintf(tmp
, sizeof(tmp
), "socket() failed: %s\n", strerror(errno
));
630 show_message("Socket failed", tmp
);
634 snprintf(tmp
, sizeof(tmp
), "Looking up %s\n", hostname
);
635 show_doing("Connecting....", tmp
);
638 hp
= gethostbyname(hostname
);
640 snprintf(tmp
, sizeof(tmp
), "No such address: %s\n", hostname
);
641 show_message("Host lookup failed", tmp
);
645 snprintf(tmp
, sizeof(tmp
), "Connecting to %s", hostname
);
646 show_doing("Connecting...", tmp
);
648 session
.sin
.sin_family
= AF_INET
;
649 session
.sin
.sin_port
= htons(DEFAULT_MANAGER_PORT
);
650 memcpy(&session
.sin
.sin_addr
, hp
->h_addr
, sizeof(session
.sin
.sin_addr
));
652 if (connect(session
.fd
,(struct sockaddr
*)&session
.sin
, sizeof(session
.sin
))) {
653 snprintf(tmp
, sizeof(tmp
), "%s failed: %s\n", hostname
, strerror(errno
));
654 show_message("Connect Failed", tmp
);
660 login
= newtButton(5, 6, "Login");
661 cancel
= newtButton(25, 6, "Cancel");
662 newtCenteredWindow(40, 10, "Asterisk Manager Login");
663 snprintf(tmp
, sizeof(tmp
), "Host: %s", hostname
);
664 label
= newtLabel(4,1, tmp
);
666 ulabel
= newtLabel(4,2,"Username:");
667 plabel
= newtLabel(4,3,"Password:");
669 username
= newtEntry(14, 2, "", 20, _NEWT_CAST
&user
, 0);
670 password
= newtEntry(14, 3, "", 20, _NEWT_CAST
&pass
, NEWT_FLAG_HIDDEN
);
672 form
= newtForm(NULL
, NULL
, 0);
673 newtFormAddComponents(form
, username
, password
, login
, cancel
, label
, ulabel
, plabel
,NULL
);
674 newtFormRun(form
, &es
);
675 if (es
.reason
== NEWT_EXIT_COMPONENT
) {
676 if (es
.u
.co
== login
) {
677 snprintf(tmp
, sizeof(tmp
), "Logging in '%s'...", user
);
678 show_doing("Logging in", tmp
);
679 /* Check to see if the remote host supports MD5 Authentication */
680 manager_action("Challenge", "AuthType: MD5\r\n");
681 m
= wait_for_response(10000);
682 if (m
&& !strcasecmp(get_header(m
, "Response"), "Success")) {
683 char *challenge
= get_header(m
, "Challenge");
686 char md5key
[256] = "";
687 struct MD5Context md5
;
688 unsigned char digest
[16];
690 MD5Update(&md5
, (unsigned char *)challenge
, strlen(challenge
));
691 MD5Update(&md5
, (unsigned char *)pass
, strlen(pass
));
692 MD5Final(digest
, &md5
);
694 len
+= sprintf(md5key
+ len
, "%2.2x", digest
[x
]);
695 manager_action("Login",
700 m
= wait_for_response(10000);
702 if (!strcasecmp(get_header(m
, "Response"), "Success")) {
705 show_message("Login Failed", get_header(m
, "Message"));
708 memset(m
, 0, sizeof(m
));
709 manager_action("Login",
713 m
= wait_for_response(10000);
716 if (!strcasecmp(get_header(m
, "Response"), "Success")) {
719 show_message("Login Failed", get_header(m
, "Message"));
725 newtFormDestroy(form
);
729 int main(int argc
, char *argv
[])
732 fprintf(stderr
, "Usage: astman <host>\n");
737 newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc.");
738 newtPushHelpLine("Welcome to the Asterisk Manager!");
739 if (login(argv
[1])) {
743 manage_calls(argv
[1]);