mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-30 07:41:39 +00:00 
			
		
		
		
	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: https://origsvn.digium.com/svn/asterisk/trunk@16850 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		
							
								
								
									
										7
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								Makefile
									
									
									
									
									
								
							| @@ -566,6 +566,13 @@ clean: clean-depend | |||||||
|  |  | ||||||
| datafiles: all | datafiles: all | ||||||
| 	if [ x`$(ID) -un` = xroot ]; then sh build_tools/mkpkgconfig $(DESTDIR)/usr/lib/pkgconfig; fi | 	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/digits | ||||||
| 	mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/sounds/priv-callerintros | 	mkdir -p $(DESTDIR)$(ASTVARLIBDIR)/sounds/priv-callerintros | ||||||
| 	for x in sounds/digits/*.gsm; do \ | 	for x in sounds/digits/*.gsm; do \ | ||||||
|   | |||||||
| @@ -4,15 +4,20 @@ | |||||||
| ; | ; | ||||||
| [general] | [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 | bindaddr=127.0.0.1 | ||||||
| ; | ; | ||||||
| ; Port to bind to | ; Port to bind to (default is 8088) | ||||||
| ; | ; | ||||||
| bindport=8088 | bindport=8088 | ||||||
| ; | ; | ||||||
|   | |||||||
| @@ -13,11 +13,18 @@ | |||||||
| ; ---------------------------- SECURITY NOTE ------------------------------- | ; ---------------------------- SECURITY NOTE ------------------------------- | ||||||
| ; Note that you should not enable the AMI on a public IP address. If needed, | ; 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 | ; 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] | [general] | ||||||
| enabled = no | enabled = no | ||||||
|  | ;webenabled = yes | ||||||
| port = 5038 | port = 5038 | ||||||
|  | ;httptimeout = 60 | ||||||
| bindaddr = 0.0.0.0 | bindaddr = 0.0.0.0 | ||||||
| ;displayconnects = yes | ;displayconnects = yes | ||||||
| ; | ; | ||||||
|   | |||||||
							
								
								
									
										91
									
								
								doc/ajam.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								doc/ajam.txt
									
									
									
									
									
										Normal 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 | ||||||
|  |  | ||||||
							
								
								
									
										204
									
								
								http.c
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								http.c
									
									
									
									
									
								
							| @@ -33,16 +33,20 @@ | |||||||
| #include <netinet/in.h> | #include <netinet/in.h> | ||||||
| #include <sys/time.h> | #include <sys/time.h> | ||||||
| #include <sys/socket.h> | #include <sys/socket.h> | ||||||
|  | #include <sys/stat.h> | ||||||
| #include <sys/signal.h> | #include <sys/signal.h> | ||||||
| #include <arpa/inet.h> | #include <arpa/inet.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <pthread.h> | #include <pthread.h> | ||||||
|  |  | ||||||
|  | #include "asterisk.h" | ||||||
| #include "asterisk/cli.h" | #include "asterisk/cli.h" | ||||||
| #include "asterisk/http.h" | #include "asterisk/http.h" | ||||||
| #include "asterisk/utils.h" | #include "asterisk/utils.h" | ||||||
| #include "asterisk/strings.h" | #include "asterisk/strings.h" | ||||||
|  | #include "asterisk/options.h" | ||||||
|  | #include "asterisk/config.h" | ||||||
|  |  | ||||||
| #define MAX_PREFIX 80 | #define MAX_PREFIX 80 | ||||||
| #define DEFAULT_PREFIX "asterisk" | #define DEFAULT_PREFIX "asterisk" | ||||||
| @@ -61,6 +65,100 @@ static pthread_t master = AST_PTHREADT_NULL; | |||||||
| static char prefix[MAX_PREFIX]; | static char prefix[MAX_PREFIX]; | ||||||
| static int prefix_len = 0; | static int prefix_len = 0; | ||||||
| static struct sockaddr_in oldsin; | 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) | static char *httpstatus_callback(struct sockaddr_in *req, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength) | ||||||
| @@ -86,9 +184,17 @@ 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"); | 	ast_build_string(&c, &reslen, "<tr><td colspan=\"2\"><hr></td></tr>\r\n"); | ||||||
| 	v = vars; | 	v = vars; | ||||||
| 	while(v) { | 	while(v) { | ||||||
|  | 		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); | 			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; | 		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"); | 	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"); | ||||||
| 	return strdup(result); | 	return strdup(result); | ||||||
| } | } | ||||||
| @@ -100,6 +206,13 @@ static struct ast_http_uri statusuri = { | |||||||
| 	.has_subtree = 0, | 	.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 *ast_http_error(int status, const char *title, const char *extra_header, const char *text) | ||||||
| { | { | ||||||
| 	char *c = NULL; | 	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 *c; | ||||||
| 	char *turi; | 	char *turi; | ||||||
| @@ -176,9 +289,9 @@ static char *handle_uri(struct sockaddr_in *sin, char *uri, int *status, char ** | |||||||
| 			if (val) { | 			if (val) { | ||||||
| 				*val = '\0'; | 				*val = '\0'; | ||||||
| 				val++; | 				val++; | ||||||
|  | 				ast_uri_decode(val); | ||||||
| 			} else  | 			} else  | ||||||
| 				val = ""; | 				val = ""; | ||||||
| 			ast_uri_decode(val); |  | ||||||
| 			ast_uri_decode(var); | 			ast_uri_decode(var); | ||||||
| 			if ((v = ast_variable_new(var, val))) { | 			if ((v = ast_variable_new(var, val))) { | ||||||
| 				if (vars) | 				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); | 	ast_uri_decode(uri); | ||||||
| 	if (!strncasecmp(uri, prefix, prefix_len)) { | 	if (!strncasecmp(uri, prefix, prefix_len)) { | ||||||
| 		uri += 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) | static void *ast_httpd_helper_thread(void *data) | ||||||
| { | { | ||||||
| 	char buf[4096]; | 	char buf[4096]; | ||||||
|  | 	char cookie[4096]; | ||||||
| 	char timebuf[256]; | 	char timebuf[256]; | ||||||
| 	struct ast_http_server_instance *ser = data; | 	struct ast_http_server_instance *ser = data; | ||||||
|  | 	struct ast_variable *var, *prev=NULL, *vars=NULL; | ||||||
| 	char *uri, *c, *title=NULL; | 	char *uri, *c, *title=NULL; | ||||||
|  | 	char *vname, *vval; | ||||||
| 	int status = 200, contentlength = 0; | 	int status = 200, contentlength = 0; | ||||||
| 	time_t t; | 	time_t t; | ||||||
|  |  | ||||||
| @@ -252,24 +373,67 @@ static void *ast_httpd_helper_thread(void *data) | |||||||
| 				*c = '\0'; | 				*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 (*uri) { | ||||||
| 			if (!strcasecmp(buf, "get"))  | 			if (!strcasecmp(buf, "get"))  | ||||||
| 				c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength); | 				c = handle_uri(&ser->requestor, uri, &status, &title, &contentlength, &vars); | ||||||
| 			else  | 			else  | ||||||
| 				c = ast_http_error(501, "Not Implemented", NULL, "Attempt to use unimplemented / unsupported method");\ | 				c = ast_http_error(501, "Not Implemented", NULL, "Attempt to use unimplemented / unsupported method");\ | ||||||
| 		} else  | 		} else  | ||||||
| 			c = ast_http_error(400, "Bad Request", NULL, "Invalid Request"); | 			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) | 		if (!c) | ||||||
| 			c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error"); | 			c = ast_http_error(500, "Internal Error", NULL, "Internal Server Error"); | ||||||
| 		if (c) { | 		if (c) { | ||||||
| 			time(&t); | 			time(&t); | ||||||
| 			strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&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, "Server: Asterisk\r\n"); | ||||||
| 			ast_cli(ser->fd, "Date: %s\r\n", timebuf); | 			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, "Connection: close\r\n"); | ||||||
|  | 			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); | 				ast_cli(ser->fd, "%s", c); | ||||||
| 			free(c); | 			free(c); | ||||||
| 		} | 		} | ||||||
| @@ -297,11 +461,10 @@ static void *http_root(void *data) | |||||||
| 				ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); | 				ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
| 		if (!(ser = ast_calloc(1, sizeof(*ser)))) { | 		ser = ast_calloc(1, sizeof(*ser)); | ||||||
| 			close(fd); | 		if (ser) { | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 			ser->fd = fd; | 			ser->fd = fd; | ||||||
|  | 			memcpy(&ser->requestor, &sin, sizeof(ser->requestor)); | ||||||
| 			if ((ser->f = fdopen(ser->fd, "w+"))) { | 			if ((ser->f = fdopen(ser->fd, "w+"))) { | ||||||
| 				if (ast_pthread_create(&launched, NULL, ast_httpd_helper_thread, ser)) { | 				if (ast_pthread_create(&launched, NULL, ast_httpd_helper_thread, ser)) { | ||||||
| 					ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); | 					ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); | ||||||
| @@ -313,10 +476,26 @@ static void *http_root(void *data) | |||||||
| 				close(ser->fd); | 				close(ser->fd); | ||||||
| 				free(ser); | 				free(ser); | ||||||
| 			} | 			} | ||||||
|  | 		} else { | ||||||
|  | 			close(ser->fd); | ||||||
|  | 			free(ser); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return NULL; | 	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) | static void http_server_start(struct sockaddr_in *sin) | ||||||
| { | { | ||||||
| 	char iabuf[INET_ADDRSTRLEN]; | 	char iabuf[INET_ADDRSTRLEN]; | ||||||
| @@ -383,6 +562,7 @@ static int __ast_http_load(int reload) | |||||||
| 	struct ast_config *cfg; | 	struct ast_config *cfg; | ||||||
| 	struct ast_variable *v; | 	struct ast_variable *v; | ||||||
| 	int enabled=0; | 	int enabled=0; | ||||||
|  | 	int newenablestatic=0; | ||||||
| 	struct sockaddr_in sin; | 	struct sockaddr_in sin; | ||||||
| 	struct hostent *hp; | 	struct hostent *hp; | ||||||
| 	struct ast_hostent ahp; | 	struct ast_hostent ahp; | ||||||
| @@ -396,6 +576,8 @@ static int __ast_http_load(int reload) | |||||||
| 		while(v) { | 		while(v) { | ||||||
| 			if (!strcasecmp(v->name, "enabled")) | 			if (!strcasecmp(v->name, "enabled")) | ||||||
| 				enabled = ast_true(v->value); | 				enabled = ast_true(v->value); | ||||||
|  | 			else if (!strcasecmp(v->name, "enablestatic")) | ||||||
|  | 				newenablestatic = ast_true(v->value); | ||||||
| 			else if (!strcasecmp(v->name, "bindport")) | 			else if (!strcasecmp(v->name, "bindport")) | ||||||
| 				sin.sin_port = ntohs(atoi(v->value)); | 				sin.sin_port = ntohs(atoi(v->value)); | ||||||
| 			else if (!strcasecmp(v->name, "bindaddr")) { | 			else if (!strcasecmp(v->name, "bindaddr")) { | ||||||
| @@ -416,6 +598,7 @@ static int __ast_http_load(int reload) | |||||||
| 		ast_copy_string(prefix, newprefix, sizeof(prefix)); | 		ast_copy_string(prefix, newprefix, sizeof(prefix)); | ||||||
| 		prefix_len = strlen(prefix); | 		prefix_len = strlen(prefix); | ||||||
| 	} | 	} | ||||||
|  | 	enablestatic = newenablestatic; | ||||||
| 	http_server_start(&sin); | 	http_server_start(&sin); | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| @@ -462,6 +645,7 @@ static struct ast_cli_entry http_cli[] = { | |||||||
| int ast_http_init(void) | int ast_http_init(void) | ||||||
| { | { | ||||||
| 	ast_http_uri_link(&statusuri); | 	ast_http_uri_link(&statusuri); | ||||||
|  | 	ast_http_uri_link(&staticuri); | ||||||
| 	ast_cli_register_multiple(http_cli, sizeof(http_cli) / sizeof(http_cli[0])); | 	ast_cli_register_multiple(http_cli, sizeof(http_cli) / sizeof(http_cli[0])); | ||||||
| 	return __ast_http_load(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 */ | /* Destroy an HTTP server */ | ||||||
| void ast_http_uri_unlink(struct ast_http_uri *urihandler); | 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_init(void); | ||||||
| int ast_http_reload(void); | int ast_http_reload(void); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										560
									
								
								manager.c
									
									
									
									
									
								
							
							
						
						
									
										560
									
								
								manager.c
									
									
									
									
									
								
							| @@ -35,6 +35,7 @@ | |||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <ctype.h> | ||||||
| #include <sys/time.h> | #include <sys/time.h> | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #include <netdb.h> | #include <netdb.h> | ||||||
| @@ -64,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | |||||||
| #include "asterisk/md5.h" | #include "asterisk/md5.h" | ||||||
| #include "asterisk/acl.h" | #include "asterisk/acl.h" | ||||||
| #include "asterisk/utils.h" | #include "asterisk/utils.h" | ||||||
|  | #include "asterisk/http.h" | ||||||
|  |  | ||||||
| struct fast_originate_helper { | struct fast_originate_helper { | ||||||
| 	char tech[AST_MAX_MANHEADER_LEN]; | 	char tech[AST_MAX_MANHEADER_LEN]; | ||||||
| @@ -86,6 +88,7 @@ static int portno = DEFAULT_MANAGER_PORT; | |||||||
| static int asock = -1; | static int asock = -1; | ||||||
| static int displayconnects = 1; | static int displayconnects = 1; | ||||||
| static int timestampevents = 0; | static int timestampevents = 0; | ||||||
|  | static int httptimeout = 60; | ||||||
|  |  | ||||||
| static pthread_t t; | static pthread_t t; | ||||||
| AST_MUTEX_DEFINE_STATIC(sessionlock); | AST_MUTEX_DEFINE_STATIC(sessionlock); | ||||||
| @@ -119,6 +122,18 @@ static struct mansession { | |||||||
| 	int busy; | 	int busy; | ||||||
| 	/*! Whether or not we're "dead" */ | 	/*! Whether or not we're "dead" */ | ||||||
| 	int 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 */ | 	/*! Logged in username */ | ||||||
| 	char username[80]; | 	char username[80]; | ||||||
| 	/*! Authentication challenge */ | 	/*! Authentication challenge */ | ||||||
| @@ -212,11 +227,168 @@ static char *complete_show_mancmd(const char *line, const char *word, int pos, i | |||||||
| 	return ret; | 	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, ...) | void astman_append(struct mansession *s, const char *fmt, ...) | ||||||
| { | { | ||||||
| 	char *stuff; | 	char *stuff; | ||||||
| 	int res; | 	int res; | ||||||
| 	va_list ap; | 	va_list ap; | ||||||
|  | 	char *tmp; | ||||||
|  |  | ||||||
| 	va_start(ap, fmt); | 	va_start(ap, fmt); | ||||||
| 	res = vasprintf(&stuff, fmt, ap); | 	res = vasprintf(&stuff, fmt, ap); | ||||||
| @@ -224,7 +396,17 @@ void astman_append(struct mansession *s, const char *fmt, ...) | |||||||
| 	if (res == -1) { | 	if (res == -1) { | ||||||
| 		ast_log(LOG_ERROR, "Memory allocation failure\n"); | 		ast_log(LOG_ERROR, "Memory allocation failure\n"); | ||||||
| 	} else { | 	} else { | ||||||
|  | 		if (s->fd > -1) | ||||||
| 			ast_carefulwrite(s->fd, stuff, strlen(stuff), 100); | 			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); | 		free(stuff); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -320,6 +502,8 @@ static void free_session(struct mansession *s) | |||||||
| 	struct eventqent *eqe; | 	struct eventqent *eqe; | ||||||
| 	if (s->fd > -1) | 	if (s->fd > -1) | ||||||
| 		close(s->fd); | 		close(s->fd); | ||||||
|  | 	if (s->outputstr) | ||||||
|  | 		free(s->outputstr); | ||||||
| 	ast_mutex_destroy(&s->__lock); | 	ast_mutex_destroy(&s->__lock); | ||||||
| 	while(s->eventq) { | 	while(s->eventq) { | ||||||
| 		eqe = s->eventq; | 		eqe = s->eventq; | ||||||
| @@ -606,7 +790,7 @@ static int authenticate(struct mansession *s, struct message *m) | |||||||
| 							return -1; | 							return -1; | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				} else if (password && !strcasecmp(password, pass)) { | 				} else if (password && !strcmp(password, pass)) { | ||||||
| 					break; | 					break; | ||||||
| 				} else { | 				} else { | ||||||
| 					ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), user); | 					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 */ | /*! \brief PING: Manager PING */ | ||||||
| static char mandescr_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" | "  manager connection open.\n" | ||||||
| "Variables: NONE\n"; | "Variables: NONE\n"; | ||||||
|  |  | ||||||
| @@ -643,6 +827,94 @@ static int action_ping(struct mansession *s, struct message *m) | |||||||
| 	return 0; | 	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[] =  | static char mandescr_listcommands[] =  | ||||||
| "Description: Returns the action name and synopsis for every\n" | "Description: Returns the action name and synopsis for every\n" | ||||||
| "  action that is available to the user\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; | 				s->authenticated = 1; | ||||||
| 				if (option_verbose > 1) { | 				if (option_verbose > 1) { | ||||||
| 					if ( displayconnects ) { | 					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"); | 				astman_send_ack(s, m, "Authentication accepted"); | ||||||
| 			} | 			} | ||||||
| 		} else if (!strcasecmp(action, "Logoff")) { | 		} else if (!strcasecmp(action, "Logoff")) { | ||||||
| @@ -1353,7 +1625,7 @@ static int process_message(struct mansession *s, struct message *m) | |||||||
| 		int ret=0; | 		int ret=0; | ||||||
| 		struct eventqent *eqe; | 		struct eventqent *eqe; | ||||||
| 		ast_mutex_lock(&s->__lock); | 		ast_mutex_lock(&s->__lock); | ||||||
| 		s->busy = 1; | 		s->busy++; | ||||||
| 		ast_mutex_unlock(&s->__lock); | 		ast_mutex_unlock(&s->__lock); | ||||||
| 		while( tmp ) { 		 | 		while( tmp ) { 		 | ||||||
| 			if (!strcasecmp(action, tmp->action)) { | 			if (!strcasecmp(action, tmp->action)) { | ||||||
| @@ -1370,7 +1642,8 @@ static int process_message(struct mansession *s, struct message *m) | |||||||
| 		if (!tmp) | 		if (!tmp) | ||||||
| 			astman_send_error(s, m, "Invalid/unknown command"); | 			astman_send_error(s, m, "Invalid/unknown command"); | ||||||
| 		ast_mutex_lock(&s->__lock); | 		ast_mutex_lock(&s->__lock); | ||||||
| 		s->busy = 0; | 		if (s->fd > -1) { | ||||||
|  | 			s->busy--; | ||||||
| 			while(s->eventq) { | 			while(s->eventq) { | ||||||
| 				if (ast_carefulwrite(s->fd, s->eventq->eventdata, strlen(s->eventq->eventdata), s->writetimeout) < 0) { | 				if (ast_carefulwrite(s->fd, s->eventq->eventdata, strlen(s->eventq->eventdata), s->writetimeout) < 0) { | ||||||
| 					ret = -1; | 					ret = -1; | ||||||
| @@ -1380,6 +1653,7 @@ static int process_message(struct mansession *s, struct message *m) | |||||||
| 				s->eventq = s->eventq->next; | 				s->eventq = s->eventq->next; | ||||||
| 				free(eqe); | 				free(eqe); | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
| 		ast_mutex_unlock(&s->__lock); | 		ast_mutex_unlock(&s->__lock); | ||||||
| 		return ret; | 		return ret; | ||||||
| 	} | 	} | ||||||
| @@ -1484,17 +1758,48 @@ static void *accept_thread(void *ignore) | |||||||
| 	int as; | 	int as; | ||||||
| 	struct sockaddr_in sin; | 	struct sockaddr_in sin; | ||||||
| 	socklen_t sinlen; | 	socklen_t sinlen; | ||||||
| 	struct mansession *s; | 	struct mansession *s, *prev=NULL, *next; | ||||||
| 	struct protoent *p; | 	struct protoent *p; | ||||||
| 	int arg = 1; | 	int arg = 1; | ||||||
| 	int flags; | 	int flags; | ||||||
| 	pthread_attr_t attr; | 	pthread_attr_t attr; | ||||||
|  | 	time_t now; | ||||||
|  | 	struct pollfd pfds[1]; | ||||||
|  | 	char iabuf[INET_ADDRSTRLEN]; | ||||||
|  |  | ||||||
| 	pthread_attr_init(&attr); | 	pthread_attr_init(&attr); | ||||||
| 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | ||||||
|  |  | ||||||
| 	for (;;) { | 	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); | 		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); | 		as = accept(asock, (struct sockaddr *)&sin, &sinlen); | ||||||
| 		if (as < 0) { | 		if (as < 0) { | ||||||
| 			ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno)); | 			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)); | 		memset(s, 0, sizeof(struct mansession)); | ||||||
| 		memcpy(&s->sin, &sin, sizeof(sin)); | 		memcpy(&s->sin, &sin, sizeof(sin)); | ||||||
| 		s->writetimeout = 100; | 		s->writetimeout = 100; | ||||||
|  | 		s->waiting_thread = AST_PTHREADT_NULL; | ||||||
|  |  | ||||||
| 		if(! block_sockets) { | 		if(! block_sockets) { | ||||||
| 			/* For safety, make sure socket is non-blocking */ | 			/* 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); | 		ast_mutex_lock(&s->__lock); | ||||||
| 		if (s->busy) { | 		if (s->busy) { | ||||||
| 			append_event(s, tmp); | 			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) { | 			if (ast_carefulwrite(s->fd, tmp, tmp_next - tmp, s->writetimeout) < 0) { | ||||||
| 				ast_log(LOG_WARNING, "Disconnecting slow (or gone) manager session!\n"); | 				ast_log(LOG_WARNING, "Disconnecting slow (or gone) manager session!\n"); | ||||||
| 				s->dead = 1; | 				s->dead = 1; | ||||||
| @@ -1701,7 +2009,211 @@ int ast_manager_register2(const char *action, int auth, int (*func)(struct manse | |||||||
| /*! @} | /*! @} | ||||||
|  END Doxygen group */ |  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 registered = 0; | ||||||
|  | static int webregged = 0; | ||||||
|  |  | ||||||
| int init_manager(void) | int init_manager(void) | ||||||
| { | { | ||||||
| @@ -1710,6 +2222,9 @@ int init_manager(void) | |||||||
| 	int oldportno = portno; | 	int oldportno = portno; | ||||||
| 	static struct sockaddr_in ba; | 	static struct sockaddr_in ba; | ||||||
| 	int x = 1; | 	int x = 1; | ||||||
|  | 	int flags; | ||||||
|  | 	int webenabled=0; | ||||||
|  | 	int newhttptimeout = 60; | ||||||
| 	if (!registered) { | 	if (!registered) { | ||||||
| 		/* Register default actions */ | 		/* Register default actions */ | ||||||
| 		ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping); | 		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("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("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("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_mancmd_cli); | ||||||
| 		ast_cli_register(&show_mancmds_cli); | 		ast_cli_register(&show_mancmds_cli); | ||||||
| @@ -1750,6 +2266,10 @@ int init_manager(void) | |||||||
| 	if(val) | 	if(val) | ||||||
| 		block_sockets = ast_true(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 ((val = ast_variable_retrieve(cfg, "general", "port"))) { | ||||||
| 		if (sscanf(val, "%d", &portno) != 1) { | 		if (sscanf(val, "%d", &portno) != 1) { | ||||||
| 			ast_log(LOG_WARNING, "Invalid port number '%s'\n", val); | 			ast_log(LOG_WARNING, "Invalid port number '%s'\n", val); | ||||||
| @@ -1763,6 +2283,9 @@ int init_manager(void) | |||||||
| 	if ((val = ast_variable_retrieve(cfg, "general", "timestampevents"))) | 	if ((val = ast_variable_retrieve(cfg, "general", "timestampevents"))) | ||||||
| 		timestampevents = ast_true(val); | 		timestampevents = ast_true(val); | ||||||
|  |  | ||||||
|  | 	if ((val = ast_variable_retrieve(cfg, "general", "httptimeout"))) | ||||||
|  | 		newhttptimeout = atoi(val); | ||||||
|  | 	 | ||||||
| 	ba.sin_family = AF_INET; | 	ba.sin_family = AF_INET; | ||||||
| 	ba.sin_port = htons(portno); | 	ba.sin_port = htons(portno); | ||||||
| 	memset(&ba.sin_addr, 0, sizeof(ba.sin_addr)); | 	memset(&ba.sin_addr, 0, sizeof(ba.sin_addr)); | ||||||
| @@ -1785,6 +2308,25 @@ int init_manager(void) | |||||||
| 	} | 	} | ||||||
| 	ast_config_destroy(cfg); | 	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 not enabled, do nothing */ | ||||||
| 	if (!enabled) { | 	if (!enabled) { | ||||||
| 		return 0; | 		return 0; | ||||||
| @@ -1808,6 +2350,8 @@ int init_manager(void) | |||||||
| 			asock = -1; | 			asock = -1; | ||||||
| 			return -1; | 			return -1; | ||||||
| 		} | 		} | ||||||
|  | 		flags = fcntl(asock, F_GETFL); | ||||||
|  | 		fcntl(asock, F_SETFL, flags | O_NONBLOCK); | ||||||
| 		if (option_verbose) | 		if (option_verbose) | ||||||
| 			ast_verbose("Asterisk Management interface listening on port %d\n", portno); | 			ast_verbose("Asterisk Management interface listening on port %d\n", portno); | ||||||
| 		ast_pthread_create(&t, NULL, accept_thread, NULL); | 		ast_pthread_create(&t, NULL, accept_thread, NULL); | ||||||
|   | |||||||
							
								
								
									
										215
									
								
								static-http/ajamdemo.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								static-http/ajamdemo.html
									
									
									
									
									
										Normal 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™ 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> | ||||||
							
								
								
									
										34
									
								
								static-http/astman.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								static-http/astman.css
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										256
									
								
								static-http/astman.js
									
									
									
									
									
										Normal 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() + " <" + 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(); | ||||||
							
								
								
									
										1781
									
								
								static-http/prototype.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1781
									
								
								static-http/prototype.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user