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:
parent
365090435a
commit
7014c0e5dd
7
Makefile
7
Makefile
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
;
|
||||
|
|
|
@ -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
|
||||
;
|
||||
|
|
|
@ -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
220
http.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
576
manager.c
|
@ -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, "<");
|
||||
(*dst) += 4;
|
||||
*maxlen -= 4;
|
||||
break;
|
||||
case '>':
|
||||
strcpy(*dst, ">");
|
||||
(*dst) += 4;
|
||||
*maxlen -= 4;
|
||||
break;
|
||||
case '\"':
|
||||
strcpy(*dst, """);
|
||||
(*dst) += 6;
|
||||
*maxlen -= 6;
|
||||
break;
|
||||
case '\'':
|
||||
strcpy(*dst, "'");
|
||||
(*dst) += 6;
|
||||
*maxlen -= 6;
|
||||
break;
|
||||
case '&':
|
||||
strcpy(*dst, "&");
|
||||
(*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\"", "&" */
|
||||
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™ 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> 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);
|
||||
|
|
|
@ -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™ AJAM Demo</title>
|
||||
<body onload="localajaminit()">
|
||||
<table align="center" width=600>
|
||||
<tr valign="top"><td>
|
||||
<table align="left">
|
||||
<tr><td colspan="2"><h2>Asterisk™ 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"> </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>
|
|
@ -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;
|
||||
}
|
|
@ -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() + " <" + channels[x].callerid.escapeHTML() + ">";
|
||||
} 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();
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue