Index: db/zm_create.sql.in
===================================================================
--- db/zm_create.sql.in	(revision 2943)
+++ db/zm_create.sql.in	(working copy)
@@ -311,7 +311,7 @@
   `Host` varchar(64) NOT NULL default '',
   `Port` varchar(8) NOT NULL default '',
   `SubPath` varchar(255) NOT NULL default '',
-  `Path` varchar(64) NOT NULL default '',
+  `Path` varchar(128) NOT NULL default '',
   `Width` smallint(5) unsigned NOT NULL default '0',
   `Height` smallint(5) unsigned NOT NULL default '0',
   `Palette` int(10) unsigned NOT NULL default '0',
@@ -847,4 +847,5 @@
 insert into Config set Id = 180, Name = 'ZM_DYN_NEXT_REMINDER', Value = '', Type = 'string', DefaultValue = '', Hint = 'string', Pattern = '(?-xism:^(.+)$)', Format = ' $1 ', Prompt = 'When the earliest time to remind about versions will be', Help = '', Category = 'dynamic', Readonly = '1', Requires = '';
 insert into Config set Id = 181, Name = 'ZM_DYN_DONATE_REMINDER_TIME', Value = '0', Type = 'integer', DefaultValue = '0', Hint = 'integer', Pattern = '(?-xism:^(\d+)$)', Format = ' $1 ', Prompt = 'When the earliest time to remind about donations will be', Help = '', Category = 'dynamic', Readonly = '1', Requires = '';
 insert into Config set Id = 182, Name = 'ZM_DYN_SHOW_DONATE_REMINDER', Value = '1', Type = 'boolean', DefaultValue = 'yes', Hint = 'yes|no', Pattern = '(?i-xsm:^([yn]))', Format = ' ($1 =~ /^y/) ? \"yes\" : \"no\" ', Prompt = 'Remind about donations or not', Help = '', Category = 'dynamic', Readonly = '1', Requires = '';
+insert into Config set Id = 183, Name = 'ZM_REDUCTION_FPS_RESET_TIME', Value = '0', Type = 'integer', DefaultValue = '0', Hint = 'integer', Pattern = '(?-xism:^(\d+)$)', Format = ' $1 ', Prompt = 'After how many seconds should the maxfps of a stream go back to normal after it has been throttled', Help = 'When a stream cannot send frames as fast as requested (generally due to too slow of a connection) it will throttle it back, unfortunately if the cause of this was only temporary the FPS will never pickup again.  By setting this to a value greater than 0 it will automatically go back to the original FPS after X seconds from when the last throttle took place.', Category = 'image', Readonly = '0', Requires = '';
 
Index: scripts/ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm.in
===================================================================
--- scripts/ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm.in	(revision 2943)
+++ scripts/ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm.in	(working copy)
@@ -1662,6 +1662,15 @@
 		readonly => 1,
 		category => "dynamic",
 	},
+	{
+		name => "ZM_REDUCTION_FPS_RESET_TIME",
+		default => "0",
+		description => "After how many seconds should the maxfps of a stream go back to normal after it has been throttled",
+		help => "When a stream cannot send frames as fast as requested (generally due to too slow of a connection) it will throttle it back, unfortunately if the cause of this was only temporary the FPS will never pickup again.  By setting this to a value greater than 0 it will automatically go back to the original FPS after X seconds from when the last throttle took place.",
+		type => $types{integer},
+		readonly => 0,
+		category => "image",
+	},
 );
 
 our %options_hash = map { ( $_->{name}, $_ ) } @options;
Index: src/zm_config_defines.h
===================================================================
--- src/zm_config_defines.h	(revision 2943)
+++ src/zm_config_defines.h	(working copy)
@@ -184,10 +184,12 @@
 #define ZM_DYN_NEXT_REMINDER 180
 #define ZM_DYN_DONATE_REMINDER_TIME 181
 #define ZM_DYN_SHOW_DONATE_REMINDER 182
+#define ZM_REDUCTION_FPS_RESET_TIME 183
 
 
-#define ZM_MAX_CFG_ID 182
 
+#define ZM_MAX_CFG_ID 183
+
 #define ZM_CFG_DECLARE_LIST \
 	const char *lang_default;\
 	bool opt_use_auth;\
@@ -372,8 +374,9 @@
 	const char *dyn_next_reminder;\
 	int dyn_donate_reminder_time;\
 	bool dyn_show_donate_reminder;\
+	int reduction_fps_reset_time;\
+	
 
-
 #define ZM_CFG_ASSIGN_LIST \
 	lang_default = (const char *)config.Item( ZM_LANG_DEFAULT );\
 	opt_use_auth = (bool)config.Item( ZM_OPT_USE_AUTH );\
@@ -558,5 +561,6 @@
 	dyn_next_reminder = (const char *)config.Item( ZM_DYN_NEXT_REMINDER );\
 	dyn_donate_reminder_time = (int)config.Item( ZM_DYN_DONATE_REMINDER_TIME );\
 	dyn_show_donate_reminder = (bool)config.Item( ZM_DYN_SHOW_DONATE_REMINDER );\
+	reduction_fps_reset_time = (int)config.Item( ZM_REDUCTION_FPS_RESET_TIME );\
 
 
Index: src/zm_stream.h
===================================================================
--- src/zm_stream.h	(revision 2943)
+++ src/zm_stream.h	(working copy)
@@ -68,6 +68,7 @@
     int scale;
     int zoom;
     double maxfps;
+	double original_maxfps;
     int bitrate;
     unsigned short x, y;
 
@@ -118,6 +119,7 @@
         scale = DEFAULT_SCALE;
         zoom = DEFAULT_ZOOM;
         maxfps = DEFAULT_MAXFPS;
+		original_maxfps = maxfps;
         bitrate = DEFAULT_BITRATE;
 
         paused = false;
@@ -161,6 +163,7 @@
 	void setStreamMaxFPS( double p_maxfps )
     {
         maxfps = p_maxfps;
+		original_maxfps = maxfps;
     }
 	void setStreamBitrate( int p_bitrate )
     {
Index: src/zm_monitor.h
===================================================================
--- src/zm_monitor.h	(revision 2943)
+++ src/zm_monitor.h	(working copy)
@@ -390,6 +390,7 @@
     int temp_image_buffer_count;
     int temp_read_index;
     int temp_write_index;
+	double last_reduction_time;
 
 protected:
     time_t ttl;
Index: src/zm_ffmpeg_camera.cpp
===================================================================
--- src/zm_ffmpeg_camera.cpp	(revision 2943)
+++ src/zm_ffmpeg_camera.cpp	(working copy)
@@ -171,7 +171,10 @@
         }
         av_free_packet( &packet );
     }
-    return (0);
+	if (frameComplete)
+		return ( 0 );
+	else
+	    return( -1 );
 }
 
 int FfmpegCamera::PostCapture()
Index: src/zm_event.cpp
===================================================================
--- src/zm_event.cpp	(revision 2943)
+++ src/zm_event.cpp	(working copy)
@@ -101,7 +101,7 @@
             stat( path, &statbuf );
             if ( errno == ENOENT || errno == ENOTDIR )
             {
-                if ( mkdir( path, 0755 ) )
+                if ( mkdir( path, 0755 ) < 0 )
                 {
                     Fatal( "Can't mkdir %s: %s", path, strerror(errno));
                 }
@@ -132,7 +132,7 @@
         stat( path, &statbuf );
         if ( errno == ENOENT || errno == ENOTDIR )
         {
-            if ( mkdir( path, 0755 ) )
+            if ( mkdir( path, 0755 ) < 0 )
             {
                 Error( "Can't mkdir %s: %s", path, strerror(errno));
             }
Index: src/zm_monitor.cpp
===================================================================
--- src/zm_monitor.cpp	(revision 2943)
+++ src/zm_monitor.cpp	(working copy)
@@ -19,6 +19,7 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/file.h>
 #include <arpa/inet.h>
 #include <glob.h>
 
@@ -470,7 +471,7 @@
         stat( path, &statbuf );
         if ( errno == ENOENT || errno == ENOTDIR )
         {
-            if ( mkdir( path, 0755 ) )
+            if ( mkdir( path, 0755 ) < 0 )
             {
                 Error( "Can't make %s: %s", path, strerror(errno));
             }
@@ -482,7 +483,7 @@
         stat( path, &statbuf );
         if ( errno == ENOENT || errno == ENOTDIR )
         {
-            if ( mkdir( path, 0755 ) )
+            if ( mkdir( path, 0755 ) < 0 )
             {
                 Error( "Can't make %s: %s", path, strerror(errno));
             }
@@ -2866,7 +2867,7 @@
         if ( create_path && errno == ENOENT )
         {
             Debug( 3, "Swap path '%s' missing, creating", path );
-            if ( mkdir( path, 0755 ) )
+            if ( mkdir( path, 0755 ) < 0 )
             {
                 Error( "Can't mkdir %s: %s", path, strerror(errno));
                 return( false );
@@ -3242,6 +3243,7 @@
         int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime );
         if ( frameSendTime > 1000/maxfps )
         {
+			last_reduction_time = TV_2_FLOAT(frameEndTime);
             maxfps /= 2;
             Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps );
         }
@@ -3328,9 +3330,17 @@
         int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime );
         if ( frameSendTime > 1000/maxfps )
         {
+			last_reduction_time = TV_2_FLOAT(frameEndTime);
             maxfps /= 1.5;
             Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps );
         }
+		if (config.reduction_fps_reset_time != 0 && last_reduction_time != -1.0 && TV_2_FLOAT(frameEndTime) - last_reduction_time > config.reduction_fps_reset_time)
+		{
+			Debug(2, "Reset FPS to original maxfps of %f from current of %f",original_maxfps, maxfps);
+			maxfps = original_maxfps;
+			last_reduction_time = -1.0;
+
+		}
     }
     last_frame_sent = TV_2_FLOAT( now );
     return( true );
@@ -3344,7 +3354,44 @@
         monitor->SingleImage( scale );
         return;
     }
+    char swap_path[PATH_MAX] = "";
+	int lock_fd = 0;
+	last_reduction_time = -1;
+    bool buffered_playback = false;
 
+	if ( connkey && playback_buffer > 0 ) {
+        Debug( 2, "Checking swap image location" );
+        Debug( 3, "Checking swap image path" );
+        strncpy( swap_path, config.path_swap, sizeof(swap_path) );
+        if ( checkSwapPath( swap_path, false ) )
+        {
+            snprintf( &(swap_path[strlen(swap_path)]), sizeof(swap_path)-strlen(swap_path), "/zmswap-m%d", monitor->Id() );
+            if ( checkSwapPath( swap_path, true ) )
+            {
+                snprintf( &(swap_path[strlen(swap_path)]), sizeof(swap_path)-strlen(swap_path), "/zmswap-q%06d", connkey );
+                if ( checkSwapPath( swap_path, true ) )
+                {
+                    buffered_playback = true;
+                }
+            }
+        }
+	}
+	char sock_path_lock[PATH_MAX];
+	sock_path_lock[0] = 0;
+	if (connkey)
+	{
+		snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
+
+		lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
+		if (lock_fd <= 0 || flock(lock_fd, LOCK_SH|LOCK_NB) != 0)
+		{
+			Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
+
+			close(lock_fd);
+			lock_fd = 0;
+		}
+	}
+
     openComms();
 
     checkInitialised();
@@ -3366,27 +3413,9 @@
     temp_read_index = temp_image_buffer_count;
     temp_write_index = temp_image_buffer_count;
 
-    char swap_path[PATH_MAX] = "";
-    bool buffered_playback = false;
 
     if ( connkey && playback_buffer > 0 )
     {
-        Debug( 2, "Checking swap image location" );
-        Debug( 3, "Checking swap image path" );
-        strncpy( swap_path, config.path_swap, sizeof(swap_path) );
-        if ( checkSwapPath( swap_path, false ) )
-        {
-            snprintf( &(swap_path[strlen(swap_path)]), sizeof(swap_path)-strlen(swap_path), "/zmswap-m%d", monitor->Id() );
-            if ( checkSwapPath( swap_path, true ) )
-            {
-                snprintf( &(swap_path[strlen(swap_path)]), sizeof(swap_path)-strlen(swap_path), "/zmswap-q%06d", connkey );
-                if ( checkSwapPath( swap_path, true ) )
-                {
-                    buffered_playback = true;
-                }
-            }
-        }
-
         if ( !buffered_playback )
         {
             Error( "Unable to validate swap image path, disabling buffered playback" );
@@ -3580,60 +3609,83 @@
             break;
         }
     }
-    if ( buffered_playback )
+	char first_lock_char = loc_sock_path[0];
+	if (lock_fd > 0)
+	{
+		loc_sock_path[0] = '\0';
+		flock(lock_fd,LOCK_UN|LOCK_NB);
+	}
+	closeComms();
+	bool is_in_use = false;
+	if (lock_fd > 0)
+	{
+		sleep(10);//wait 10 seconds for someone else to grab a lock on it
+		if (lock_fd > 0 && flock(lock_fd,LOCK_EX|LOCK_NB) == 0){
+			loc_sock_path[0] = first_lock_char;
+            unlink( loc_sock_path );
+			close(lock_fd);
+			unlink( sock_path_lock );
+		}
+		else
+		{
+			is_in_use = true;
+			Debug(3, "Unable to get lock to delete swap folder/socket, leaving it alone" );
+		}
+	}
+
+    if (! is_in_use && buffered_playback )
     {
-        char swap_path[PATH_MAX] = "";
+		char swap_path[PATH_MAX] = "";
+		snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", config.path_swap, monitor->Id(), connkey );
+		Debug( 1, "Cleaning swap files from %s", swap_path );
+		struct stat stat_buf;
+		if ( stat( swap_path, &stat_buf ) < 0 )
+		{
+			if ( errno != ENOENT )
+			{
+				Error( "Can't stat '%s': %s", swap_path, strerror(errno) );
+			}
+		}
+		else if ( !S_ISDIR(stat_buf.st_mode) )
+		{
+			Error( "Swap image path '%s' is not a directory", swap_path );
+		}
+		else
+		{
+			char glob_pattern[PATH_MAX] = "";
 
-        snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", config.path_swap, monitor->Id(), connkey );
-        Debug( 1, "Cleaning swap files from %s", swap_path );
-        struct stat stat_buf;
-        if ( stat( swap_path, &stat_buf ) < 0 )
-        {
-            if ( errno != ENOENT )
-            {
-                Error( "Can't stat '%s': %s", swap_path, strerror(errno) );
-            }
-        }
-        else if ( !S_ISDIR(stat_buf.st_mode) )
-        {
-            Error( "Swap image path '%s' is not a directory", swap_path );
-        }
-        else
-        {
-            char glob_pattern[PATH_MAX] = "";
-
-            snprintf( glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path );
-            glob_t pglob;
-            int glob_status = glob( glob_pattern, 0, 0, &pglob );
-            if ( glob_status != 0 )
-            {
-                if ( glob_status < 0 )
-                {
-                    Error( "Can't glob '%s': %s", glob_pattern, strerror(errno) );
-                }
-                else
-                {
-                    Debug( 1, "Can't glob '%s': %d", glob_pattern, glob_status );
-                }
-            }
-            else
-            {
-                for ( int i = 0; i < pglob.gl_pathc; i++ )
-                {
-                    if ( unlink( pglob.gl_pathv[i] ) < 0 )
-                    {
-                        Error( "Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno) );
-                    }
-                }
-            }
-            globfree( &pglob );
-            if ( rmdir( swap_path ) < 0 )
-            {
-                Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) );
-            }
-        }
-    }
-    closeComms();
+				snprintf( glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path );
+			glob_t pglob;
+			int glob_status = glob( glob_pattern, 0, 0, &pglob );
+			if ( glob_status != 0 )
+			{
+				if ( glob_status < 0 )
+				{
+					Error( "Can't glob '%s': %s", glob_pattern, strerror(errno) );
+				}
+				else
+				{
+					Debug( 1, "Can't glob '%s': %d", glob_pattern, glob_status );
+				}
+			}
+			else
+			{
+				for ( int i = 0; i < pglob.gl_pathc; i++ )
+				{
+					if ( unlink( pglob.gl_pathv[i] ) < 0 )
+					{
+						Error( "Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno) );
+					}
+				}
+			}
+			globfree( &pglob );
+			if ( rmdir( swap_path ) < 0 )
+			{
+				Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) );
+			}
+		}
+   }
+    
 }
 
 void Monitor::SingleImage( int scale)
Index: web/ajax/stream.php
===================================================================
--- web/ajax/stream.php	(revision 2943)
+++ web/ajax/stream.php	(working copy)
@@ -46,11 +46,14 @@
 }
 
 $remSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf("%06d",$_REQUEST['connkey']).'s.sock';
-$max_socket_tries = 3;
+$max_socket_tries = 5;
 while ( !file_exists($remSockFile) && $max_socket_tries-- ) //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second.
     sleep(1);
 
-if ( !@socket_sendto( $socket, $msg, strlen($msg), 0, $remSockFile ) )
+$max_socket_refuse_trys = 2;
+while (! ($socket_res = @socket_sendto( $socket, $msg, strlen($msg), 0, $remSockFile )) &&  socket_last_error() == 111 && $max_socket_refuse_trys--)
+	sleep(1);
+if ( ! $socket_res)
 {
     ajaxError( "socket_sendto( $remSockFile ) failed: ".socket_strerror(socket_last_error()) );
 }
Index: web/ajax/status.php
===================================================================
--- web/ajax/status.php	(revision 2943)
+++ web/ajax/status.php	(working copy)
@@ -247,21 +247,31 @@
                 foreach( $entitySpec['selector'] as $selector )
                 {
                     if ( is_array( $selector ) )
-                        $where[] = $selector['selector']." = ".dbEscape($_REQUEST['id'][$index]);
+                        $where[] = $selector['selector']." = ".validInt($_REQUEST['id'][$index]);
                     else
-                        $where[] = $selector." = ".dbEscape($_REQUEST['id'][$index]);
+                        $where[] = $selector." = ".validInt($_REQUEST['id'][$index]);
                     $index++;
                 }
                 $sql .= " where ".join( " and ", $where );
             }
             if ( $groupSql )
                 $sql .= " group by ".join( ",", array_unique( $groupSql ) );
+			
             if ( !empty($_REQUEST['sort']) )
-                $sql .= " order by ".dbEscape($_REQUEST['sort']);
+			{
+				$arr = split(' ',$_REQUEST['sort']);
+				$col = validCol($arr[0]);
+				$dir = "";
+				if (count($arr) == 2){
+					if ($arr[1] == "desc")
+						$dir = $arr[1];
+				}
+                $sql .= " order by $col $dir";
+			}
             if ( !empty($entitySpec['limit']) )
                 $limit = $entitySpec['limit'];
             elseif ( !empty($_REQUEST['count']) )
-                $limit = dbEscape($_REQUEST['count']);
+                $limit = validInt($_REQUEST['count']);
             if ( !empty( $limit ) )
                 $sql .= " limit ".$limit;
             if ( isset($limit) && $limit == 1 )
Index: web/includes/functions.php
===================================================================
--- web/includes/functions.php	(revision 2943)
+++ web/includes/functions.php	(working copy)
@@ -2155,6 +2155,10 @@
 
 function jsonEncode( &$value )
 {
+	global $xml_encode_not_json;
+	if ($xml_encode_not_json)
+		return xmlEncode($value);
+
     if ( function_exists('json_encode') )
         return( json_encode( $value ) );
 
@@ -2190,6 +2194,46 @@
     }
 }
 
+function xmlEncode ($mixed,$domElement=null,$DOMDocument=null){
+    if(is_null($DOMDocument)){
+        $DOMDocument=new DOMDocument;
+        $DOMDocument->formatOutput=true;
+		$node=$DOMDocument->createElement("Response");
+		$DOMDocument->appendChild($node);
+        xmlEncode($mixed,$node,$DOMDocument);
+        return $DOMDocument->saveXML();
+    }
+    else{
+        if(is_array($mixed)){
+            foreach($mixed as $index=>$mixedElement){
+                if(is_int($index)){
+                    if($index==0){
+                        $node=$domElement;
+                    }
+                    else{
+                        $node=$DOMDocument->createElement($domElement->tagName);
+                        $domElement->parentNode->appendChild($node);
+                    }
+                }
+                else{
+                    $plural=$DOMDocument->createElement($index);
+                    $domElement->appendChild($plural);
+                    $node=$plural;
+                    if(rtrim($index,'s')!==$index && $index != "status" && $index != "fps"){
+                        $singular=$DOMDocument->createElement(rtrim($index,'s'));
+                        $plural->appendChild($singular);
+                        $node=$singular;
+                    }
+                }
+                xmlEncode($mixedElement,$node,$DOMDocument);
+            }
+        }
+        else{
+            $domElement->appendChild($DOMDocument->createTextNode($mixed));
+        }
+    }
+}
+
 define( 'HTTP_STATUS_OK', 200 );
 define( 'HTTP_STATUS_BAD_REQUEST', 400 );
 define( 'HTTP_STATUS_FORBIDDEN', 403 );
@@ -2282,6 +2326,12 @@
     return( preg_replace( '/\D/', '', $input ) );
 }
 
+function validCol( $input )
+{
+    return( preg_replace( '/[^a-zA-Z0-9\-_\.]/', '', $input ) );
+}
+
+
 function validNum( $input )
 {
     return( preg_replace( '/[^\d.-]/', '', $input ) );
Index: web/skins/classic/js/skin.js
===================================================================
--- web/skins/classic/js/skin.js	(revision 2943)
+++ web/skins/classic/js/skin.js	(working copy)
@@ -34,8 +34,8 @@
         error:function() {}
     };
 }
-if ( !console.debug )//IE8 has console but doesn't have console.debug so lets alias it.
-    console.debug = console.log;
+if (! window.console.debug )//IE8 has console but doesn't have console.debug so lets alias it.
+    window.console.debug = console.log;
 
 // Javascript window sizes
 var popupSizes = {
Index: web/skins/classic/views/js/video.js
===================================================================
--- web/skins/classic/views/js/video.js	(revision 2943)
+++ web/skins/classic/views/js/video.js	(working copy)
@@ -12,7 +12,7 @@
 
 function generateVideoProgress()
 {
-    var tickerText = $('videoProgressTicker').get('text');
+    var tickerText = $('videoProgressTicker').innerHTML;
     if ( tickerText.length < 1 || tickerText.length > 4 )
         $('videoProgressTicker').set( 'text', '.' );
     else
Index: web/skins/classic/views/js/export.js
===================================================================
--- web/skins/classic/views/js/export.js	(revision 2943)
+++ web/skins/classic/views/js/export.js	(working copy)
@@ -21,7 +21,7 @@
 
 function exportProgress()
 {
-    var tickerText = $('exportProgressTicker').get('text');
+    var tickerText = $('exportProgressTicker').innerHTML;
     if ( tickerText.length < 1 || tickerText.length > 4 )
         $('exportProgressTicker').set( 'text', '.' );
     else
Index: web/skins/classic/views/js/event.js
===================================================================
--- web/skins/classic/views/js/event.js	(revision 2943)
+++ web/skins/classic/views/js/event.js	(working copy)
@@ -684,7 +684,7 @@
 
 function showFrameStats()
 {
-    var fid = $('eventImageNo').get('text');
+    var fid = $('eventImageNo').innerHTML;
     createPopup( '?view=stats&eid='+event.Id+'&fid='+fid, 'zmStats', 'stats', event.Width, event.Height );
 }
 
Index: web/skins/classic/views/api.php
===================================================================
--- web/skins/classic/views/api.php	(revision 0)
+++ web/skins/classic/views/api.php	(revision 0)
@@ -0,0 +1,86 @@
+<?php
+header('Content-type: text/xml');
+header('Pragma: public');        
+header('Cache-control: private');
+header('Expires: -1');
+echo('<?xml version="1.0" encoding="utf-8"?>');
+?>
+<response>
+<?php
+function make_tag($name,$value)
+{
+	return "<$name>" . $value . "</$name>";
+}
+
+if ($_REQUEST['action'] == "zone_info")
+{
+    if ( !canView( "Monitors" ) )
+        ajaxError( 'Unrecognised action or insufficient permissions' );
+	$mid = validInt($_REQUEST['mid']);
+	$monitor = dbFetchMonitor( $mid );
+	$sql = "select * from Zones where MonitorId = '".$mid."' order by Area desc";
+	foreach( dbFetchAll( $sql ) as $zone )
+	{
+		$zone_str = "";
+		$zone_str .= make_tag("id",$zone['Id']);
+		$zone_str .= make_tag("name",$zone['Name']);
+		$zone_str .= make_tag("method",$zone['CheckMethod']);
+		$zone_str .= make_tag("color",(($zone['AlarmRGB']>>16)&0xff) . "," . (($zone['AlarmRGB']>>8)&0xff) . "," . (($zone['AlarmRGB'])&0xff) );
+		$zone_str .= make_tag("MinPixelThreshold",$zone['MinPixelThreshold']);
+		$zone_str .= make_tag("MaxPixelThreshold",$zone['MaxPixelThreshold']);
+		$zone_str .= make_tag("MinAlarmPixels",$zone['MinAlarmPixels']);
+		$zone_str .= make_tag("MaxAlarmPixels",$zone['MaxAlarmPixels']);
+		$zone_str .= make_tag("MinFilterPixels",$zone['MinFilterPixels']);
+		$zone_str .= make_tag("MaxFilterPixels",$zone['MaxFilterPixels']);
+		$zone_str .= make_tag("MinBlobPixels",$zone['MinBlobPixels']);
+		$zone_str .= make_tag("MaxBlobPixels",$zone['MaxBlobPixels']);
+		$zone_str .= make_tag("MinBlobs",$zone['MinBlobs']);
+		$zone_str .= make_tag("MaxBlobs",$zone['MaxBlobs']);
+		$zone_str .= make_tag("cord",$zone['Coords']);
+		print make_tag("zone",$zone_str);
+	}
+}
+else if ($_REQUEST['action'] == "list_monitors")
+{
+    if ( !canView( "Monitors" ) )
+        ajaxError( 'Unrecognised action or insufficient permissions' );
+	$monitors = dbFetchAll( "select * from Monitors order by Sequence asc" );
+	for ( $i = 0; $i < count($monitors); $i++ )
+	{
+		$monitor = $monitors[$i];
+		$monitor_str = "";
+		$monitor_str .= make_tag("id",$monitor['Id']);
+		$monitor_str .= make_tag("name",$monitor['Name']);
+		$monitor_str .= make_tag("method",$monitor['Function']);
+		$monitor_str .= make_tag("enabled",$monitor['Enabled']);
+		$monitor_str .= make_tag("width",$monitor['Width']);
+		$monitor_str .= make_tag("height",$monitor['Height']);
+		print make_tag("monitor",$monitor_str);
+	}
+}
+else if ($_REQUEST['action'] == "get_connkey")
+{
+	print make_tag("connkey",generateConnKey());
+}
+else if ($_REQUEST['action'] == "event_stats"){
+    if ( !canView( "Events" ) )
+        ajaxError( 'Unrecognised action or insufficient permissions' );
+	$eid = validInt($_REQUEST['eid']);
+	$stats = dbFetchAll( "select S.*,E.*,Z.Name as ZoneName,Z.Units,Z.Area,M.Name as MonitorName,M.Width,M.Height from Stats as S left join Events as E on S.EventId = E.Id left join Zones as Z on S.ZoneId = Z.Id left join Monitors as M on E.MonitorId = M.Id where S.EventId = '".$eid."' order by S.FrameId, S.ZoneId" );
+	for ( $i = 0; $i < count($stats); $i++ )
+	{
+		$stat = $stats[$i];
+		$stat_str = "";
+		$stat_str .= make_tag("FrameId",$stat["FrameId"]);
+		$stat_str .= make_tag("PixelDiff",$stat["PixelDiff"]);
+		$stat_str .= make_tag("AlarmPixels",$stat["AlarmPixels"]);
+		$stat_str .= make_tag("FilterPixels",$stat["FilterPixels"]);
+		$stat_str .= make_tag("BlobPixels",$stat["BlobPixels"]);
+		$stat_str .= make_tag("Blobs",$stat["Blobs"]);
+		$stat_str .= make_tag("ZoneName",$stat["ZoneName"]);
+		print make_tag("stat",$stat_str);
+
+	}
+}
+?>
+</response>
\ No newline at end of file
Index: web/index.php
===================================================================
--- web/index.php	(revision 2943)
+++ web/index.php	(working copy)
@@ -55,6 +55,8 @@
     $protocol = 'http';
 }
 define( "ZM_BASE_URL", $protocol.'://'.$_SERVER['HTTP_HOST'] );
+if ( isset($_REQUEST['usexml']) && $_REQUEST['usexml'] == '1')
+	$xml_encode_not_json = 1;
 
 if ( isset($_GET['skin']) )
     $skin = $_GET['skin'];
@@ -124,7 +126,7 @@
         foreach ( $includeFiles as $includeFile )
         {
             if ( !file_exists( $includeFile ) )
-                die( "View '$view' does not exist" );
+                die( "View '$view' does not exist: " .  $includeFile );
             require_once $includeFile;
         }
     }
