dect
/
asterisk
Archived
13
0
Fork 0

Flesh out the remainder of the manager + http changes and create a sample application to partially

demonstrate the capability of manager over http.


git-svn-id: http://svn.digium.com/svn/asterisk/trunk@16850 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
markster 2006-04-01 08:49:54 +00:00
parent 365090435a
commit 7014c0e5dd
11 changed files with 3165 additions and 39 deletions

View File

@ -566,6 +566,13 @@ clean: clean-depend
datafiles: all
if [ x`$(ID) -un` = xroot ]; then sh build_tools/mkpkgconfig $(DESTDIR)/usr/lib/pkgconfig; fi
# Should static HTTP be installed during make samples or even with its own target ala
# webvoicemail? There are portions here that *could* be customized but might also be
# improved a lot. I'll put it here for now.
mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/static-http
for x in static-http/*; do \
install -m 644 $$x $(DESTDIR)$(ASTVARLIBDIR)/static-http ; \
done
mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/sounds/digits
mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/sounds/priv-callerintros
for x in sounds/digits/*.gsm; do \

View File

@ -4,15 +4,20 @@
;
[general]
;
; Whether HTTP interface is enabled or not.
; Whether HTTP interface is enabled or not. Default is no.
;
enabled=no
;enabled=yes
;
; Address to bind to
; Whether Asterisk should serve static content from http-static
; Default is no.
;
;enablestatic=yes
;
; Address to bind to. Default is 0.0.0.0
;
bindaddr=127.0.0.1
;
; Port to bind to
; Port to bind to (default is 8088)
;
bindport=8088
;

View File

@ -13,11 +13,18 @@
; ---------------------------- SECURITY NOTE -------------------------------
; Note that you should not enable the AMI on a public IP address. If needed,
; block this TCP port with iptables (or another FW software) and reach it
; with IPsec, SSH, or SSL vpn tunnel
; with IPsec, SSH, or SSL vpn tunnel. You can also make the manager
; interface available over http if Asterisk's http server is enabled in
; http.conf and if both "enabled" and "webenabled" are set to yes in
; this file. Both default to no. httptimeout provides the maximum
; timeout in seconds before a web based session is discarded. The
; default is 60 seconds.
;
[general]
enabled = no
;webenabled = yes
port = 5038
;httptimeout = 60
bindaddr = 0.0.0.0
;displayconnects = yes
;

91
doc/ajam.txt Normal file
View File

@ -0,0 +1,91 @@
Asynchronous Javascript Asterisk Manger (AJAM)
==============================================
AJAM is a new technology which allows web browsers or other HTTP enabled
applications and web pages to directly access the Asterisk Manger
Interface (AMI) via HTTP. Setting up your server to process AJAM
involves a few steps:
Setup the Asterisk HTTP server
------------------------------
1) Uncomment the line "enabled=yes" in /etc/asterisk/http.conf to enable
Asterisk's builtin micro HTTP server.
2) If you want Asterisk to actually deliver simple HTML pages, CSS,
javascript, etc. you should uncomment "enablestatic=yes"
3) Adjust your "bindaddr" and "bindport" settings as appropriate for
your desired accessibility
4) Adjust your "prefix" if appropriate, which must be the beginning of
any URI on the server to match. The default is "asterisk" and the
rest of these instructions assume that value.
Allow Manager Access via HTTP
-----------------------------
1) Make sure you have both "enabled = yes" and "webenabled = yes" setup
in /etc/asterisk/manager.conf
2) You may also use "httptimeout" to set a default timeout for HTTP
connections.
3) Make sure you have a manager username/secret
Once those configurations are complete you can reload or restart
Asterisk and you should be able to point your web browser to specific
URI's which will allow you to access various web functions. A complete
list can be found by typing "show http" at the Asterisk CLI.
examples:
http://localhost:8088/asterisk/manager?action=login&username=foo&secret=bar
This logs you into the manager interface's "HTML" view. Once you're
logged in, Asterisk stores a cookie on your browser (valid for the
length of httptimeout) which is used to connect to the same session.
http://localhost:8088/asterisk/rawman?action=status
Assuming you've already logged into manager, this URI will give you a
"raw" manager output for the "status" command.
http://localhost:8088/asterisk/mxml?action=status
This will give you the same status view but represented as AJAX data,
theoretically compatible with RICO (http://www.openrico.org).
http://localhost:8088/asterisk/static/ajamdemo.html
If you have enabled static content support and have done a make install,
Asterisk will serve up a demo page which presents a live, but very
basic, "astman" like interface. You can login with your username/secret
for manager and have a basic view of channels as well as transfer and
hangup calls. It's only tested in Firefox, but could probably be made
to run in other browsers as well.
A sample library (astman.js) is included to help ease the creation of
manager HTML interfaces.
Note that for the demo, there is no need for *any* external web server.
Integration with other web servers
----------------------------------
Asterisk's micro HTTP server is *not* designed to replace a general
purpose web server and it is intentionally created to provide only the
minimal interfaces required. Even without the addition of an external
web server, one can use Asterisk's interfaces to implement screen pops
and similar tools pulling data from other web servers using iframes,
div's etc. If you want to integrate CGI's, databases, PHP, etc. you
will likely need to use a more traditional web server like Apache and
link in your Asterisk micro HTTP server with something like this:
ProxyPass /asterisk http://localhost:8088/asterisk
This is a fairly new technology so I'd love to hear if it's useful for
you!
Mark

220
http.c
View File

@ -33,16 +33,20 @@
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include "asterisk.h"
#include "asterisk/cli.h"
#include "asterisk/http.h"
#include "asterisk/utils.h"
#include "asterisk/strings.h"
#include "asterisk/options.h"
#include "asterisk/config.h"
#define MAX_PREFIX 80
#define DEFAULT_PREFIX "asterisk"
@ -61,6 +65,100 @@ static pthread_t master = AST_PTHREADT_NULL;
static char prefix[MAX_PREFIX];
static int prefix_len = 0;
static struct sockaddr_in oldsin;
static int enablestatic=0;
/* Limit the kinds of files we're willing to serve up */
static struct {
char *ext;
char *mtype;
} mimetypes[] = {
{ "png", "image/png" },
{ "jpg", "image/jpeg" },
{ "js", "application/x-javascript" },
{ "wav", "audio/x-wav" },
{ "mp3", "audio/mpeg" },
};
static char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
{
int x;
if (ftype) {
for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
if (!strcasecmp(ftype, mimetypes[x].ext))
return mimetypes[x].mtype;
}
}
snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
return wkspace;
}
static char *static_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
{
char result[4096];
char *c=result;
char *path;
char *ftype, *mtype;
char wkspace[80];
struct stat st;
int len;
int fd;
void *blob;
/* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration
substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
if (!enablestatic || ast_strlen_zero(uri))
goto out403;
/* Disallow any funny filenames at all */
if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0]))
goto out403;
if (strstr(uri, "/.."))
goto out403;
if ((ftype = strrchr(uri, '.')))
ftype++;
mtype=ftype2mtype(ftype, wkspace, sizeof(wkspace));
/* Cap maximum length */
len = strlen(uri) + strlen(ast_config_AST_VAR_DIR) + strlen("/static-http/") + 5;
if (len > 1024)
goto out403;
path = alloca(len);
sprintf(path, "%s/static-http/%s", ast_config_AST_VAR_DIR, uri);
if (stat(path, &st))
goto out404;
if (S_ISDIR(st.st_mode))
goto out404;
fd = open(path, O_RDONLY);
if (fd < 0)
goto out403;
len = st.st_size + strlen(mtype) + 40;
blob = malloc(len);
if (blob) {
c = blob;
sprintf(c, "Content-type: %s\r\n\r\n", mtype);
c += strlen(c);
*contentlength = read(fd, c, st.st_size);
if (*contentlength < 0) {
close(fd);
free(blob);
goto out403;
}
}
return blob;
out404:
*status = 404;
*title = strdup("Not Found");
return ast_http_error(404, "Not Found", NULL, "Nothing to see here. Move along.");
out403:
*status = 403;
*title = strdup("Access Denied");
return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave.");
}
static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
@ -86,7 +184,15 @@ static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struc
ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
v = vars;
while(v) {
ast_build_string(&c, &reslen, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
if (strncasecmp(v->name, "cookie_", 7))
ast_build_string(&c, &reslen, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
v = v->next;
}
ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
v = vars;
while(v) {
if (!strncasecmp(v->name, "cookie_", 7))
ast_build_string(&c, &reslen, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
v = v->next;
}
ast_build_string(&c, &reslen, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
@ -100,6 +206,13 @@ static struct ast_http_uri statusuri = {
.has_subtree = 0,
};
static struct ast_http_uri staticuri = {
.callback = static_callback,
.description = "Asterisk HTTP Static Delivery",
.uri = "static",
.has_subtree = 1,
};
char *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
{
char *c = NULL;
@ -153,7 +266,7 @@ void ast_http_uri_unlink(struct ast_http_uri *urih)
}
}
static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength)
static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **title, int *contentlength, struct ast_variable **cookies)
{
char *c;
char *turi;
@ -176,9 +289,9 @@ static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **
if (val) {
*val = '\0';
val++;
ast_uri_decode(val);
} else
val = "";
ast_uri_decode(val);
ast_uri_decode(var);
if ((v = ast_variable_new(var, val))) {
if (vars)
@ -189,6 +302,11 @@ static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **
}
}
}
if (prev)
prev->next = *cookies;
else
vars = *cookies;
*cookies = NULL;
ast_uri_decode(uri);
if (!strncasecmp(uri, prefix, prefix_len)) {
uri += prefix_len;
@ -227,9 +345,12 @@ static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char **
static void *ast_httpd_helper_thread(void *data)
{
char buf[4096];
char cookie[4096];
char timebuf[256];
struct ast_http_server_instance *ser = data;
struct ast_variable *var, *prev=NULL, *vars=NULL;
char *uri, *c, *title=NULL;
char *vname, *vval;
int status = 200, contentlength = 0;
time_t t;
@ -252,25 +373,68 @@ static void *ast_httpd_helper_thread(void *data)
*c = '\0';
}
}
while (fgets(cookie, sizeof(cookie), ser->f)) {
/* Trim trailing characters */
while(!ast_strlen_zero(cookie) && (cookie[strlen(cookie) - 1] < 33)) {
cookie[strlen(cookie) - 1] = '\0';
}
if (ast_strlen_zero(cookie))
break;
if (!strncasecmp(cookie, "Cookie: ", 8)) {
vname = cookie + 8;
vval = strchr(vname, '=');
if (vval) {
/* Ditch the = and the quotes */
*vval = '\0';
vval++;
if (*vval)
vval++;
if (strlen(vval))
vval[strlen(vval) - 1] = '\0';
var = ast_variable_new(vname, vval);
if (var) {
if (prev)
prev->next = var;
else
vars = var;
prev = var;
}
}
}
}
if (*uri) {
if (!strcasecmp(buf, "get"))
c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength);
c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars);
else
c = ast_http_error(501, "Not Implemented", NULL, "Attempt to use unimplemented / unsupported method");\
} else
c = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
/* If they aren't mopped up already, clean up the cookies */
if (vars)
ast_variables_destroy(vars);
if (!c)
c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error");
if (c) {
time(&t);
strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
ast_cli(ser->fd, "HTTP/1.1 GET %d %s\r\n", status, title ? title : "OK");
ast_cli(ser->fd, "HTTP/1.1 %d %s\r\n", status, title ? title : "OK");
ast_cli(ser->fd, "Server: Asterisk\r\n");
ast_cli(ser->fd, "Date: %s\r\n", timebuf);
if (contentlength)
ast_cli(ser->fd, "Content-length: %d\r\n", contentlength);
ast_cli(ser->fd, "Connection: close\r\n");
ast_cli(ser->fd, "%s", c);
if (contentlength) {
char *tmp;
tmp = strstr(c, "\r\n\r\n");
if (tmp) {
ast_cli(ser->fd, "Content-length: %d\r\n", contentlength);
write(ser->fd, c, (tmp + 4 - c));
write(ser->fd, tmp + 4, contentlength);
}
} else
ast_cli(ser->fd, "%s", c);
free(c);
}
if (title)
@ -297,19 +461,22 @@ static void *http_root(void *data)
ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
continue;
}
if (!(ser = ast_calloc(1, sizeof(*ser)))) {
close(fd);
continue;
}
ser->fd = fd;
if ((ser->f = fdopen(ser->fd, "w+"))) {
if (ast_pthread_create(&launched, NULL, ast_httpd_helper_thread, ser)) {
ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
fclose(ser->f);
ser = ast_calloc(1, sizeof(*ser));
if (ser) {
ser->fd = fd;
memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
if ((ser->f = fdopen(ser->fd, "w+"))) {
if (ast_pthread_create(&launched, NULL, ast_httpd_helper_thread, ser)) {
ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
fclose(ser->f);
free(ser);
}
} else {
ast_log(LOG_WARNING, "fdopen failed!\n");
close(ser->fd);
free(ser);
}
} else {
ast_log(LOG_WARNING, "fdopen failed!\n");
close(ser->fd);
free(ser);
}
@ -317,6 +484,18 @@ static void *http_root(void *data)
return NULL;
}
char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, int buflen)
{
char *c;
c = buf;
ast_build_string(&c, &buflen, "Set-Cookie: %s=\"%s\"; Version=\"1\"", var, val);
if (expires)
ast_build_string(&c, &buflen, "; Max-Age=%d", expires);
ast_build_string(&c, &buflen, "\r\n");
return buf;
}
static void http_server_start(struct sockaddr_in *sin)
{
char iabuf[INET_ADDRSTRLEN];
@ -383,6 +562,7 @@ static int __ast_http_load(int reload)
struct ast_config *cfg;
struct ast_variable *v;
int enabled=0;
int newenablestatic=0;
struct sockaddr_in sin;
struct hostent *hp;
struct ast_hostent ahp;
@ -396,6 +576,8 @@ static int __ast_http_load(int reload)
while(v) {
if (!strcasecmp(v->name, "enabled"))
enabled = ast_true(v->value);
else if (!strcasecmp(v->name, "enablestatic"))
newenablestatic = ast_true(v->value);
else if (!strcasecmp(v->name, "bindport"))
sin.sin_port = ntohs(atoi(v->value));
else if (!strcasecmp(v->name, "bindaddr")) {
@ -416,6 +598,7 @@ static int __ast_http_load(int reload)
ast_copy_string(prefix, newprefix, sizeof(prefix));
prefix_len = strlen(prefix);
}
enablestatic = newenablestatic;
http_server_start(&sin);
return 0;
}
@ -462,6 +645,7 @@ static struct ast_cli_entry http_cli[] = {
int ast_http_init(void)
{
ast_http_uri_link(&statusuri);
ast_http_uri_link(&staticuri);
ast_cli_register_multiple(http_cli, sizeof(http_cli) / sizeof(http_cli[0]));
return __ast_http_load(0);
}

View File

@ -58,6 +58,8 @@ char *ast_http_error(int status, const char *title, const char *extra_header, co
/* Destroy an HTTP server */
void ast_http_uri_unlink(struct ast_http_uri *urihandler);
char *ast_http_setcookie(const char *var, const char *val, int expires, char *buf, int buflen);
int ast_http_init(void);
int ast_http_reload(void);

576
manager.c
View File

@ -35,6 +35,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netdb.h>
@ -64,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/md5.h"
#include "asterisk/acl.h"
#include "asterisk/utils.h"
#include "asterisk/http.h"
struct fast_originate_helper {
char tech[AST_MAX_MANHEADER_LEN];
@ -86,6 +88,7 @@ static int portno = DEFAULT_MANAGER_PORT;
static int asock = -1;
static int displayconnects = 1;
static int timestampevents = 0;
static int httptimeout = 60;
static pthread_t t;
AST_MUTEX_DEFINE_STATIC(sessionlock);
@ -119,6 +122,18 @@ static struct mansession {
int busy;
/*! Whether or not we're "dead" */
int dead;
/*! Whether an HTTP manager is in use */
int inuse;
/*! Whether an HTTP session should be destroyed */
int needdestroy;
/*! Whether an HTTP session has someone waiting on events */
pthread_t waiting_thread;
/*! Unique manager identifer */
unsigned long managerid;
/*! Session timeout if HTTP */
time_t sessiontimeout;
/*! Output from manager interface */
char *outputstr;
/*! Logged in username */
char username[80];
/*! Authentication challenge */
@ -212,11 +227,168 @@ static char *complete_show_mancmd(const char *line, const char *word, int pos, i
return ret;
}
static void xml_copy_escape(char **dst, int *maxlen, const char *src, int lower)
{
while (*src && (*maxlen > 6)) {
switch(*src) {
case '<':
strcpy(*dst, "&lt;");
(*dst) += 4;
*maxlen -= 4;
break;
case '>':
strcpy(*dst, "&gt;");
(*dst) += 4;
*maxlen -= 4;
break;
case '\"':
strcpy(*dst, "&quot;");
(*dst) += 6;
*maxlen -= 6;
break;
case '\'':
strcpy(*dst, "&apos;");
(*dst) += 6;
*maxlen -= 6;
break;
case '&':
strcpy(*dst, "&amp;");
(*dst) += 4;
*maxlen -= 4;
break;
default:
*(*dst)++ = lower ? tolower(*src) : *src;
(*maxlen)--;
}
src++;
}
}
static char *xml_translate(char *in, struct ast_variable *vars)
{
struct ast_variable *v;
char *dest=NULL;
char *out, *tmp, *var, *val;
char *objtype=NULL;
int colons = 0;
int breaks = 0;
int len;
int count = 1;
int escaped = 0;
int inobj = 0;
int x;
v = vars;
while(v) {
if (!dest && !strcasecmp(v->name, "ajaxdest"))
dest = v->value;
else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
objtype = v->value;
v = v->next;
}
if (!dest)
dest = "unknown";
if (!objtype)
objtype = "generic";
for (x=0;in[x];x++) {
if (in[x] == ':')
colons++;
else if (in[x] == '\n')
breaks++;
else if (strchr("&\"<>", in[x]))
escaped++;
}
len = strlen(in) + colons * 5 + breaks * (40 + strlen(dest) + strlen(objtype)) + escaped * 10; /* foo="bar", "<response type=\"object\" id=\"dest\"", "&amp;" */
out = malloc(len);
if (!out)
return 0;
tmp = out;
while(*in) {
var = in;
while (*in && (*in >= 32)) in++;
if (*in) {
if ((count > 3) && inobj) {
ast_build_string(&tmp, &len, " /></response>\n");
inobj = 0;
}
count = 0;
while (*in && (*in < 32)) {
*in = '\0';
in++;
count++;
}
val = strchr(var, ':');
if (val) {
*val = '\0';
val++;
if (*val == ' ')
val++;
if (!inobj) {
ast_build_string(&tmp, &len, "<response type='object' id='%s'><%s", dest, objtype);
inobj = 1;
}
ast_build_string(&tmp, &len, " ");
xml_copy_escape(&tmp, &len, var, 1);
ast_build_string(&tmp, &len, "='");
xml_copy_escape(&tmp, &len, val, 0);
ast_build_string(&tmp, &len, "'");
}
}
}
if (inobj)
ast_build_string(&tmp, &len, " /></response>\n");
return out;
}
static char *html_translate(char *in)
{
int x;
int colons = 0;
int breaks = 0;
int len;
int count=1;
char *tmp, *var, *val, *out;
for (x=0;in[x];x++) {
if (in[x] == ':')
colons++;
if (in[x] == '\n')
breaks++;
}
len = strlen(in) + colons * 40 + breaks * 40; /* <tr><td></td><td></td></tr>, "<tr><td colspan=\"2\"><hr></td></tr> */
out = malloc(len);
if (!out)
return 0;
tmp = out;
while(*in) {
var = in;
while (*in && (*in >= 32)) in++;
if (*in) {
if ((count % 4) == 0){
ast_build_string(&tmp, &len, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
}
count = 0;
while (*in && (*in < 32)) {
*in = '\0';
in++;
count++;
}
val = strchr(var, ':');
if (val) {
*val = '\0';
val++;
if (*val == ' ')
val++;
ast_build_string(&tmp, &len, "<tr><td>%s</td><td>%s</td></tr>\r\n", var, val);
}
}
}
return out;
}
void astman_append(struct mansession *s, const char *fmt, ...)
{
char *stuff;
int res;
va_list ap;
char *tmp;
va_start(ap, fmt);
res = vasprintf(&stuff, fmt, ap);
@ -224,7 +396,17 @@ void astman_append(struct mansession *s, const char *fmt, ...)
if (res == -1) {
ast_log(LOG_ERROR, "Memory allocation failure\n");
} else {
ast_carefulwrite(s->fd, stuff, strlen(stuff), 100);
if (s->fd > -1)
ast_carefulwrite(s->fd, stuff, strlen(stuff), 100);
else {
tmp = realloc(s->outputstr, (s->outputstr ? strlen(s->outputstr) : 0) + strlen(stuff) + 1);
if (tmp) {
if (!s->outputstr)
tmp[0] = '\0';
s->outputstr = tmp;
strcat(s->outputstr, stuff);
}
}
free(stuff);
}
}
@ -320,6 +502,8 @@ static void free_session(struct mansession *s)
struct eventqent *eqe;
if (s->fd > -1)
close(s->fd);
if (s->outputstr)
free(s->outputstr);
ast_mutex_destroy(&s->__lock);
while(s->eventq) {
eqe = s->eventq;
@ -606,7 +790,7 @@ static int authenticate(struct mansession *s, struct message *m)
return -1;
}
}
} else if (password && !strcasecmp(password, pass)) {
} else if (password && !strcmp(password, pass)) {
break;
} else {
ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user);
@ -633,7 +817,7 @@ static int authenticate(struct mansession *s, struct message *m)
/*! \brief PING: Manager PING */
static char mandescr_ping[] =
"Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the "
"Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
" manager connection open.\n"
"Variables: NONE\n";
@ -643,6 +827,94 @@ static int action_ping(struct mansession *s, struct message *m)
return 0;
}
/*! \brief WAITEVENT: Manager WAITEVENT */
static char mandescr_waitevent[] =
"Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
"a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
"session, events will be generated and queued.\n"
"Variables: \n"
" Timeout: Maximum time to wait for events\n";
static int action_waitevent(struct mansession *s, struct message *m)
{
char *timeouts = astman_get_header(m, "Timeout");
int timeout = -1, max;
int x;
int needexit = 0;
time_t now;
struct eventqent *eqe;
char *id = astman_get_header(m,"ActionID");
char idText[256]="";
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
if (!ast_strlen_zero(timeouts)) {
sscanf(timeouts, "%i", &timeout);
}
ast_mutex_lock(&s->__lock);
if (s->waiting_thread != AST_PTHREADT_NULL) {
pthread_kill(s->waiting_thread, SIGURG);
}
if (s->sessiontimeout) {
time(&now);
max = s->sessiontimeout - now - 10;
if (max < 0)
max = 0;
if ((timeout < 0) || (timeout > max))
timeout = max;
if (!s->send_events)
s->send_events = -1;
/* Once waitevent is called, always queue events from now on */
if (s->busy == 1)
s->busy = 2;
}
ast_mutex_unlock(&s->__lock);
s->waiting_thread = pthread_self();
ast_log(LOG_DEBUG, "Starting waiting for an event!\n");
for (x=0;((x<timeout) || (timeout < 0)); x++) {
ast_mutex_lock(&s->__lock);
if (s->eventq)
needexit = 1;
if (s->waiting_thread != pthread_self())
needexit = 1;
if (s->needdestroy)
needexit = 1;
ast_mutex_unlock(&s->__lock);
if (needexit)
break;
if (s->fd > 0) {
if (ast_wait_for_input(s->fd, 1000))
break;
} else {
sleep(1);
}
}
ast_log(LOG_DEBUG, "Finished waiting for an event!\n");
ast_mutex_lock(&s->__lock);
if (s->waiting_thread == pthread_self()) {
astman_send_response(s, m, "Success", "Waiting for Event...");
/* Only show events if we're the most recent waiter */
while(s->eventq) {
astman_append(s, "%s", s->eventq->eventdata);
eqe = s->eventq;
s->eventq = s->eventq->next;
free(eqe);
}
astman_append(s,
"Event: WaitEventComplete\r\n"
"%s"
"\r\n",idText);
s->waiting_thread = AST_PTHREADT_NULL;
} else {
ast_log(LOG_DEBUG, "Abandoning event request!\n");
}
ast_mutex_unlock(&s->__lock);
return 0;
}
static char mandescr_listcommands[] =
"Description: Returns the action name and synopsis for every\n"
" action that is available to the user\n"
@ -1338,10 +1610,10 @@ static int process_message(struct mansession *s, struct message *m)
s->authenticated = 1;
if (option_verbose > 1) {
if ( displayconnects ) {
ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
ast_verbose(VERBOSE_PREFIX_2 "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
}
ast_log(LOG_EVENT, "Manager '%s' logged on from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->sessiontimeout ? "HTTP " : ""), s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
astman_send_ack(s, m, "Authentication accepted");
}
} else if (!strcasecmp(action, "Logoff")) {
@ -1353,7 +1625,7 @@ static int process_message(struct mansession *s, struct message *m)
int ret=0;
struct eventqent *eqe;
ast_mutex_lock(&s->__lock);
s->busy = 1;
s->busy++;
ast_mutex_unlock(&s->__lock);
while( tmp ) {
if (!strcasecmp(action, tmp->action)) {
@ -1370,15 +1642,17 @@ static int process_message(struct mansession *s, struct message *m)
if (!tmp)
astman_send_error(s, m, "Invalid/unknown command");
ast_mutex_lock(&s->__lock);
s->busy = 0;
while(s->eventq) {
if (ast_carefulwrite(s->fd, s->eventq->eventdata, strlen(s->eventq->eventdata), s->writetimeout) < 0) {
ret = -1;
break;
if (s->fd > -1) {
s->busy--;
while(s->eventq) {
if (ast_carefulwrite(s->fd, s->eventq->eventdata, strlen(s->eventq->eventdata), s->writetimeout) < 0) {
ret = -1;
break;
}
eqe = s->eventq;
s->eventq = s->eventq->next;
free(eqe);
}
eqe = s->eventq;
s->eventq = s->eventq->next;
free(eqe);
}
ast_mutex_unlock(&s->__lock);
return ret;
@ -1484,17 +1758,48 @@ static void *accept_thread(void *ignore)
int as;
struct sockaddr_in sin;
socklen_t sinlen;
struct mansession *s;
struct mansession *s, *prev=NULL, *next;
struct protoent *p;
int arg = 1;
int flags;
pthread_attr_t attr;
time_t now;
struct pollfd pfds[1];
char iabuf[INET_ADDRSTRLEN];
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
for (;;) {
time(&now);
ast_mutex_lock(&sessionlock);
prev = NULL;
s = sessions;
while(s) {
next = s->next;
if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
if (prev)
prev->next = next;
else
sessions = next;
if (s->authenticated && (option_verbose > 1) && displayconnects) {
ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' timed out from %s\n",
s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
free_session(s);
} else
prev = s;
s = next;
}
ast_mutex_unlock(&sessionlock);
sinlen = sizeof(sin);
pfds[0].fd = asock;
pfds[0].events = POLLIN;
/* Wait for something to happen, but timeout every few seconds so
we can ditch any old manager sessions */
if (poll(pfds, 1, 5000) < 1)
continue;
as = accept(asock, (struct sockaddr *)&sin, &sinlen);
if (as < 0) {
ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
@ -1514,6 +1819,7 @@ static void *accept_thread(void *ignore)
memset(s, 0, sizeof(struct mansession));
memcpy(&s->sin, &sin, sizeof(sin));
s->writetimeout = 100;
s->waiting_thread = AST_PTHREADT_NULL;
if(! block_sockets) {
/* For safety, make sure socket is non-blocking */
@ -1593,7 +1899,9 @@ int manager_event(int category, const char *event, const char *fmt, ...)
ast_mutex_lock(&s->__lock);
if (s->busy) {
append_event(s, tmp);
} else if (!s->dead) {
if (s->waiting_thread != AST_PTHREADT_NULL)
pthread_kill(s->waiting_thread, SIGURG);
} else if (!s->dead && !s->sessiontimeout) {
if (ast_carefulwrite(s->fd, tmp, tmp_next - tmp, s->writetimeout) < 0) {
ast_log(LOG_WARNING, "Disconnecting slow (or gone) manager session!\n");
s->dead = 1;
@ -1701,7 +2009,211 @@ int ast_manager_register2(const char *action, int auth, int (*func)(struct manse
/*! @}
END Doxygen group */
static struct mansession *find_session(unsigned long ident)
{
struct mansession *s;
ast_mutex_lock(&sessionlock);
s = sessions;
while(s) {
ast_mutex_lock(&s->__lock);
if (s->sessiontimeout && (s->managerid == ident) && !s->needdestroy) {
s->inuse++;
break;
}
ast_mutex_unlock(&s->__lock);
s = s->next;
}
ast_mutex_unlock(&sessionlock);
return s;
}
static void vars2msg(struct message *m, struct ast_variable *vars)
{
int x;
for (x=0;vars && (x<AST_MAX_MANHEADERS);x++,vars = vars->next) {
if (!vars)
break;
m->hdrcount = x + 1;
snprintf(m->headers[x], sizeof(m->headers[x]), "%s: %s", vars->name, vars->value);
}
}
#define FORMAT_RAW 0
#define FORMAT_HTML 1
#define FORMAT_XML 2
static char *contenttype[] = { "plain", "html", "xml" };
static char *generic_http_callback(int format, struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
{
struct mansession *s=NULL;
unsigned long ident=0;
char workspace[256];
char cookie[128];
char iabuf[INET_ADDRSTRLEN];
int len = sizeof(workspace);
int blastaway = 0;
char *c = workspace;
char *retval=NULL;
struct message m;
struct ast_variable *v;
v = params;
while(v) {
if (!strcasecmp(v->name, "mansession_id")) {
sscanf(v->value, "%lx", &ident);
break;
}
v = v->next;
}
s = find_session(ident);
if (!s) {
/* Create new session */
s = calloc(1, sizeof(struct mansession));
memcpy(&s->sin, requestor, sizeof(s->sin));
s->fd = -1;
s->waiting_thread = AST_PTHREADT_NULL;
s->send_events = 0;
ast_mutex_init(&s->__lock);
ast_mutex_lock(&s->__lock);
ast_mutex_lock(&sessionlock);
s->inuse = 1;
s->managerid = rand() | (unsigned long)s;
s->next = sessions;
sessions = s;
ast_mutex_unlock(&sessionlock);
}
/* Reset HTTP timeout */
time(&s->sessiontimeout);
s->sessiontimeout += httptimeout;
ast_mutex_unlock(&s->__lock);
memset(&m, 0, sizeof(m));
if (s) {
char tmp[80];
ast_build_string(&c, &len, "Content-type: text/%s\n", contenttype[format]);
sprintf(tmp, "%08lx", s->managerid);
ast_build_string(&c, &len, "%s\r\n", ast_http_setcookie("mansession_id", tmp, httptimeout, cookie, sizeof(cookie)));
if (format == FORMAT_HTML)
ast_build_string(&c, &len, "<title>Asterisk&trade; Manager Test Interface</title>");
vars2msg(&m, params);
if (format == FORMAT_XML) {
ast_build_string(&c, &len, "<ajax-response>\n");
} else if (format == FORMAT_HTML) {
ast_build_string(&c, &len, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
ast_build_string(&c, &len, "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\"><h1>&nbsp;&nbsp;Manager Tester</h1></td></tr>\r\n");
}
if (process_message(s, &m)) {
if (s->authenticated) {
if (option_verbose > 1) {
if (displayconnects)
ast_verbose(VERBOSE_PREFIX_2 "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
} else {
if (option_verbose > 1) {
if (displayconnects)
ast_verbose(VERBOSE_PREFIX_2 "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
}
s->needdestroy = 1;
}
if (s->outputstr) {
char *tmp;
if (format == FORMAT_XML)
tmp = xml_translate(s->outputstr, params);
else if (format == FORMAT_HTML)
tmp = html_translate(s->outputstr);
else
tmp = s->outputstr;
if (tmp) {
retval = malloc(strlen(workspace) + strlen(tmp) + 128);
if (retval) {
strcpy(retval, workspace);
strcpy(retval + strlen(retval), tmp);
c = retval + strlen(retval);
len = 120;
}
free(tmp);
}
if (tmp != s->outputstr)
free(s->outputstr);
s->outputstr = NULL;
}
/* Still okay because c would safely be pointing to workspace even
if retval failed to allocate above */
if (format == FORMAT_XML) {
ast_build_string(&c, &len, "</ajax-response>\n");
} else if (format == FORMAT_HTML)
ast_build_string(&c, &len, "</table></body>\r\n");
} else {
*status = 500;
*title = strdup("Server Error");
}
ast_mutex_lock(&s->__lock);
if (s->needdestroy) {
if (s->inuse == 1) {
ast_log(LOG_DEBUG, "Need destroy, doing it now!\n");
blastaway = 1;
} else {
ast_log(LOG_DEBUG, "Need destroy, but can't do it yet!\n");
if (s->waiting_thread != AST_PTHREADT_NULL)
pthread_kill(s->waiting_thread, SIGURG);
s->inuse--;
}
} else
s->inuse--;
ast_mutex_unlock(&s->__lock);
if (blastaway)
destroy_session(s);
if (*status != 200)
return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
return retval;
}
static char *manager_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
{
return generic_http_callback(FORMAT_HTML, requestor, uri, params, status, title, contentlength);
}
static char *mxml_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
{
return generic_http_callback(FORMAT_XML, requestor, uri, params, status, title, contentlength);
}
static char *rawman_http_callback(struct sockaddr_in *requestor, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
{
return generic_http_callback(FORMAT_RAW, requestor, uri, params, status, title, contentlength);
}
struct ast_http_uri rawmanuri = {
.description = "Raw HTTP Manager Event Interface",
.uri = "rawman",
.has_subtree = 0,
.callback = rawman_http_callback,
};
struct ast_http_uri manageruri = {
.description = "HTML Manager Event Interface",
.uri = "manager",
.has_subtree = 0,
.callback = manager_http_callback,
};
struct ast_http_uri managerxmluri = {
.description = "XML Manager Event Interface",
.uri = "mxml",
.has_subtree = 0,
.callback = mxml_http_callback,
};
static int registered = 0;
static int webregged = 0;
int init_manager(void)
{
@ -1710,6 +2222,9 @@ int init_manager(void)
int oldportno = portno;
static struct sockaddr_in ba;
int x = 1;
int flags;
int webenabled=0;
int newhttptimeout = 60;
if (!registered) {
/* Register default actions */
ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
@ -1727,6 +2242,7 @@ int init_manager(void)
ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
ast_manager_register2("MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
ast_cli_register(&show_mancmd_cli);
ast_cli_register(&show_mancmds_cli);
@ -1750,6 +2266,10 @@ int init_manager(void)
if(val)
block_sockets = ast_true(val);
val = ast_variable_retrieve(cfg, "general", "webenabled");
if (val)
webenabled = ast_true(val);
if ((val = ast_variable_retrieve(cfg, "general", "port"))) {
if (sscanf(val, "%d", &portno) != 1) {
ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
@ -1762,6 +2282,9 @@ int init_manager(void)
if ((val = ast_variable_retrieve(cfg, "general", "timestampevents")))
timestampevents = ast_true(val);
if ((val = ast_variable_retrieve(cfg, "general", "httptimeout")))
newhttptimeout = atoi(val);
ba.sin_family = AF_INET;
ba.sin_port = htons(portno);
@ -1785,6 +2308,25 @@ int init_manager(void)
}
ast_config_destroy(cfg);
if (webenabled && enabled) {
if (!webregged) {
ast_http_uri_link(&rawmanuri);
ast_http_uri_link(&manageruri);
ast_http_uri_link(&managerxmluri);
webregged = 1;
}
} else {
if (webregged) {
ast_http_uri_unlink(&rawmanuri);
ast_http_uri_unlink(&manageruri);
ast_http_uri_unlink(&managerxmluri);
webregged = 0;
}
}
if (newhttptimeout > 0)
httptimeout = newhttptimeout;
/* If not enabled, do nothing */
if (!enabled) {
return 0;
@ -1808,6 +2350,8 @@ int init_manager(void)
asock = -1;
return -1;
}
flags = fcntl(asock, F_GETFL);
fcntl(asock, F_SETFL, flags | O_NONBLOCK);
if (option_verbose)
ast_verbose("Asterisk Management interface listening on port %d\n", portno);
ast_pthread_create(&t, NULL, accept_thread, NULL);

215
static-http/ajamdemo.html Normal file
View File

@ -0,0 +1,215 @@
<script src="prototype.js"></script>
<script src="astman.js"></script>
<link href="astman.css" media="all" rel="Stylesheet" type="text/css" />
<script>
var logins = new Object;
var logoffs = new Object;
var channels = new Object;
var pongs = new Object;
var loggedon = 0;
var selectedchan = null;
var hungupchan = "";
var transferedchan = "";
var demo = new Object;
function loggedOn() {
if (loggedon)
return;
loggedon = 1;
updateButtons();
$('statusbar').innerHTML = "<i>Retrieving channel status...</i>";
astmanEngine.pollEvents();
astmanEngine.sendRequest('action=status', demo.channels);
}
function clearChannelList() {
$('channellist').innerHTML = "<i class='light'>Not connected</i>";
}
function loggedOff() {
if (!loggedon)
return;
loggedon = 0;
selectedchan = null;
updateButtons();
astmanEngine.channelClear();
clearChannelList();
}
function updateButtons()
{
if ($(selectedchan)) {
$('transfer').disabled = 0;
$('hangup').disabled = 0;
} else {
$('transfer').disabled = 1;
$('hangup').disabled = 1;
selectedchan = null;
}
if (loggedon) {
$('logoff').disabled = 0;
$('login').disabled = 1;
$('refresh').disabled = 0;
} else {
$('logoff').disabled = 1;
$('login').disabled = 0;
$('refresh').disabled = 1;
}
}
demo.channelCallback = function(target) {
selectedchan = target;
updateButtons();
}
demo.channels = function(msgs) {
resp = msgs[0].headers['response'];
if (resp == "Success") {
loggedOn();
} else
loggedOff();
for (i=1;i<msgs.length - 1;i++)
astmanEngine.channelUpdate(msgs[i]);
$('channellist').innerHTML = astmanEngine.channelTable(demo.channelCallback);
$('statusbar').innerHTML = "Ready";
}
demo.logins = function(msgs) {
$('statusbar').innerHTML = msgs[0].headers['message'];
resp = msgs[0].headers['response'];
if (resp == "Success")
loggedOn();
else
loggedOff();
};
demo.logoffs = function(msgs) {
$('statusbar').innerHTML = msgs[0].headers['message'];
loggedOff();
};
demo.hungup = function(msgs) {
$('statusbar').innerHTML = "Hungup " + hungupchan;
}
demo.transferred = function(msgs) {
$('statusbar').innerHTML = "Transferred " + transferredchan;
}
function doHangup() {
hungupchan = selectedchan;
astmanEngine.sendRequest('action=hangup&channel=' + selectedchan, demo.hungup);
}
function doStatus() {
$('statusbar').innerHTML = "<i>Updating channel status...</i>";
astmanEngine.channelClear();
astmanEngine.sendRequest('action=status', demo.channels);
}
function doLogin() {
$('statusbar').innerHTML = "<i>Logging in...</i>";
astmanEngine.sendRequest('action=login&username=' + $('username').value + "&secret=" + $('secret').value, demo.logins);
}
function doTransfer() {
var channel = astmanEngine.channelInfo(selectedchan);
var exten = prompt("Enter new extension for " + selectedchan);
var altchan;
if (exten) {
if (channel.link) {
if (confirm("Transfer " + channel.link + " too?"))
altchan = channel.link;
}
if (altchan) {
transferredchan = selectedchan + " and " + altchan + " to " + exten;
astmanEngine.sendRequest('action=redirect&channel=' + selectedchan + "&priority=1&extrachannel=" + altchan + "&exten=" + exten, demo.transferred);
} else {
transferredchan = selectedchan + " to " + exten;
astmanEngine.sendRequest('action=redirect&channel=' + selectedchan + "&priority=1&exten=" + exten, demo.transferred);
}
}
}
function doLogoff() {
$('statusbar').innerHTML = "<i>Logging off...</i>";
astmanEngine.sendRequest('action=logoff', demo.logoffs);
}
demo.pongs = function(msgs) {
resp = msgs[0].headers['response'];
if (resp == "Pong") {
$('statusbar').innerHTML = "<i>Already connected...</i>";
loggedOn();
} else {
$('statusbar').innerHTML = "<i>Please login...</i>";
loggedOff();
}
}
demo.eventcb = function(msgs) {
var x;
if (loggedon) {
for (i=1;i<msgs.length - 1;i++) {
astmanEngine.channelUpdate(msgs[i]);
}
$('channellist').innerHTML = astmanEngine.channelTable(demo.channelCallback);
astmanEngine.pollEvents();
}
updateButtons();
}
function localajaminit() {
astmanEngine.setURL('../rawman');
astmanEngine.setEventCallback(demo.eventcb);
//astmanEngine.setDebug($('ditto'));
clearChannelList();
astmanEngine.sendRequest('action=ping', demo.pongs);
}
</script>
<title>Asterisk&trade; AJAM Demo</title>
<body onload="localajaminit()">
<table align="center" width=600>
<tr valign="top"><td>
<table align="left">
<tr><td colspan="2"><h2>Asterisk&trade; AJAM Demo</h2></td>
<tr><td>Username:</td><td><input id="username"></td></tr>
<tr><td>Secret:</td><td><input type="password" id="secret"></td></tr>
<tr><td colspan=2 align="center">
<div id="statusbar">
<span style="margin-left: 4px;font-weight:bold">&nbsp;</span>
</div>
</td></tr>
<tr><td><input type="submit" id="login" value="Login" onClick="doLogin()"></td>
<td><input type="submit" id="logoff" value="Logoff" disabled=1 onClick="doLogoff()"></td></tr>
</table>
</td><td valign='bottom'>
<table>
<div style="margin-left:10;margin-right:50;margin-top:10;margin-bottom:20">
<i>This is a demo of the Asynchronous Javascript Asterisk Manager interface. You can login with a
valid, appropriately permissioned manager username and secret.</i>
</div>
<tr>
<td><input type="submit" onClick="doStatus()" id="refresh" value="Refresh"></td>
<td><input type="submit" onClick="doTransfer()" id="transfer" value="Transfer..."></td>
<td><input type="submit" onClick="doHangup()" id="hangup" value="Hangup"></td>
</tr>
</table>
</td></tr>
<tr><td colspan=2>
<div id="channellist" class="chanlist">
</div>
</td></tr>
<tr><td align="center" colspan=2>
<font size=-1><i>
Copyright (C) 2006 Digium, Inc. Asterisk and Digium are trademarks of Digium, Inc.
</i></font>
</td></tr>
</table>
</body>

34
static-http/astman.css Normal file
View File

@ -0,0 +1,34 @@
.chanlist {
border : 1px solid #1f669b;
height : 150px;
overflow : auto;
background-color : #f1f1f1;
width : 600;
}
.chantable {
border : 0px;
background-color : #f1f1f1;
width : 100%;
}
.labels {
background-color : #000000;
color : #ffffff;
}
.chanlisteven {
background-color : #fff8e4;
}
.chanlistodd {
background-color : #f0f5ff;
}
.chanlistselected {
background-color : #ffb13d;
}
.light {
color : #717171;
}

256
static-http/astman.js Normal file
View File

@ -0,0 +1,256 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Javascript routines or accessing manager routines over HTTP.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*
*/
function Astman() {
var me = this;
var channels = new Array;
var lastselect;
var selecttarget;
this.setURL = function(url) {
this.url = url;
};
this.setEventCallback = function(callback) {
this.eventcallback = callback;
};
this.setDebug = function(debug) {
this.debug = debug;
};
this.clickChannel = function(ev) {
var target = ev.target;
// XXX This is icky, we statically use astmanEngine to call the callback XXX
if (me.selecttarget)
me.restoreTarget(me.selecttarget);
while(!target.id || !target.id.length)
target=target.parentNode;
me.selecttarget = target.id;
target.className = "chanlistselected";
me.chancallback(target.id);
};
this.restoreTarget = function(targetname) {
var other;
target = $(targetname);
if (!target)
return;
if (target.previousSibling) {
other = target.previousSibling.previousSibling.className;
} else if (target.nextSibling) {
other = target.nextSibling.nextSibling.className;
}
if (other) {
if (other == "chanlisteven")
target.className = "chanlistodd";
else
target.className = "chanlisteven";
} else
target.className = "chanlistodd";
};
this.channelUpdate = function(msg, channame) {
var fields = new Array("callerid", "calleridname", "context", "extension", "priority", "account", "state", "link", "uniqueid" );
if (!channame || !channame.length)
channame = msg.headers['channel'];
if (!channels[channame])
channels[channame] = new Array();
if (msg.headers.event) {
if (msg.headers.event == "Hangup") {
delete channels[channame];
} else if (msg.headers.event == "Link") {
var chan1 = msg.headers.channel1;
var chan2 = msg.headers.channel2;
if (chan1 && channels[chan1])
channels[chan1].link = chan2;
if (chan2 && channels[chan2])
channels[chan2].link = chan1;
} else if (msg.headers.event == "Unlink") {
var chan1 = msg.headers.channel1;
var chan2 = msg.headers.channel2;
if (chan1 && channels[chan1])
delete channels[chan1].link;
if (chan2 && channels[chan2])
delete channels[chan2].link;
} else if (msg.headers.event == "Rename") {
var oldname = msg.headers.oldname;
var newname = msg.headers.newname;
if (oldname && channels[oldname]) {
channels[newname] = channels[oldname];
delete channels[oldname];
}
} else {
channels[channame]['channel'] = channame;
for (x=0;x<fields.length;x++) {
if (msg.headers[fields[x]])
channels[channame][fields[x]] = msg.headers[fields[x]];
}
}
} else {
channels[channame]['channel'] = channame;
for (x=0;x<fields.length;x++) {
if (msg.headers[fields[x]])
channels[channame][fields[x]] = msg.headers[fields[x]];
}
}
};
this.channelClear = function() {
channels = new Array;
}
this.channelInfo = function(channame) {
return channels[channame];
};
this.channelTable = function(callback) {
var s, x;
var cclass, count=0;
var found = 0;
var fieldlist = new Array("channel", "callerid", "calleridname", "context", "extension", "priority");
me.chancallback = callback;
s = "<table class='chantable' align='center'>\n";
s = s + "\t<tr class='labels' id='labels'><td>Channel</td><td>State</td><td>Caller</td><td>Location</td><td>Link</td></tr>";
count=0;
for (x in channels) {
if (channels[x].channel) {
if (count % 2)
cclass = "chanlistodd";
else
cclass = "chanlisteven";
if (me.selecttarget && (me.selecttarget == x))
cclass = "chanlistselected";
count++;
s = s + "\t<tr class='" + cclass + "' id='" + channels[x].channel + "' onClick='astmanEngine.clickChannel(event)'>";
s = s + "<td>" + channels[x].channel + "</td>";
if (channels[x].state)
s = s + "<td>" + channels[x].state + "</td>";
else
s = s + "<td><i class='light'>unknown</i></td>";
if (channels[x].calleridname && channels[x].callerid && channels[x].calleridname != "<unknown>") {
cid = channels[x].calleridname.escapeHTML() + " &lt;" + channels[x].callerid.escapeHTML() + "&gt;";
} else if (channels[x].calleridname && (channels[x].calleridname != "<unknown>")) {
cid = channels[x].calleridname.escapeHTML();
} else if (channels[x].callerid) {
cid = channels[x].callerid.escapeHTML();
} else {
cid = "<i class='light'>Unknown</i>";
}
s = s + "<td>" + cid + "</td>";
if (channels[x].extension) {
s = s + "<td>" + channels[x].extension + "@" + channels[x].context + ":" + channels[x].priority + "</td>";
} else {
s = s + "<td><i class='light'>None</i></td>";
}
if (channels[x].link) {
s = s + "<td>" + channels[x].link + "</td>";
} else {
s = s + "<td><i class='light'>None</i></td>";
}
s = s + "</tr>\n";
found++;
}
}
if (!found)
s += "<tr><td colspan=" + fieldlist.length + "><i class='light'>No active channels</i></td>\n";
s += "</table>\n";
return s;
};
this.parseResponse = function(t, callback) {
var msgs = new Array();
var inmsg = 0;
var msgnum = 0;
var x,y;
var s = t.responseText;
var allheaders = s.split('\r\n');
if (me.debug)
me.debug.value = "\n";
for (x=0;x<allheaders.length;x++) {
if (allheaders[x].length) {
var fields = allheaders[x].split(': ');
if (!inmsg) {
msgs[msgnum] = new Object();
msgs[msgnum].headers = new Array();
msgs[msgnum].names = new Array();
y=0;
}
msgs[msgnum].headers[fields[0].toLowerCase()] = fields[1];
msgs[msgnum].names[y++] = fields[0].toLowerCase();
if (me.debug)
me.debug.value = me.debug.value + "field " + fields[0] + "/" + fields[1] + "\n";
inmsg=1;
} else {
if (inmsg) {
if (me.debug)
me.debug.value = me.debug.value + " new message\n";
inmsg = 0;
msgnum++;
}
}
}
if (me.debug) {
me.debug.value = me.debug.value + "msgnum is " + msgnum + " and array length is " + msgs.length + "\n";
me.debug.value = me.debug.value + "length is " + msgs.length + "\n";
var x, y;
for (x=0;x<msgs.length;x++) {
for (y=0;y<msgs[x].names.length;y++) {
me.debug.value = me.debug.value + "msg "+ (x + 1) + "/" + msgs[x].names[y] + "/" + msgs[x].headers[msgs[x].names[y]] + "\n";
}
}
}
callback(msgs);
};
this.managerResponse = function(t) {
me.parseResponse(t, me.callback);
};
this.doEvents = function(msgs) {
me.eventcallback(msgs);
};
this.eventResponse = function(t) {
me.parseResponse(t, me.doEvents);
};
this.sendRequest = function(request, callback) {
var tmp;
var opt = {
method: 'get',
asynchronous: true,
onSuccess: this.managerResponse,
onFailure: function(t) {
alert("Error: " + t.status + ": " + t.statusText);
},
};
me.callback = callback;
opt.parameters = request;
tmp = new Ajax.Request(this.url, opt);
};
this.pollEvents = function() {
var tmp;
var opt = {
method: 'get',
asynchronous: true,
onSuccess: this.eventResponse,
onFailure: function(t) {
alert("Event Error: " + t.status + ": " + t.statusText);
},
};
opt.parameters="action=waitevent";
tmp = new Ajax.Request(this.url, opt);
};
};
astmanEngine = new Astman();

1781
static-http/prototype.js vendored Normal file

File diff suppressed because it is too large Load Diff