diff --git a/dashboard1/changes.txt b/dashboard1/changes.txt
index afdc9d9..d532e94 100644
--- a/dashboard1/changes.txt
+++ b/dashboard1/changes.txt
@@ -1,3 +1,16 @@
+xlx db v2.4.5
+Fix liveircddb page broken by CSP security headers
+
+ - "pgs/ircddb_proxy.php" (NEW)
+ * Added transparent proxy for live.ircddb.net to resolve CSP and mixed-content issues
+ * Proxies all requests through local server, rewriting URLs to maintain functionality
+ * Supports both HTTP and HTTPS dashboard deployments
+ * Defaults to live.ircddb.net:8080, configurable via $PageOptions['IRCDDB']['URL']
+ * Default page ircddblive5.html, configurable via $PageOptions['IRCDDB']['Page']
+
+ - "pgs/liveircddb.php"
+ * Changed iframe source from direct external URL to local proxy
+
xlx db v2.4.4
SECURITY UPDATE - Minor upgrade to further improve dashboard security
diff --git a/dashboard1/pgs/config.inc.php b/dashboard1/pgs/config.inc.php
index cec04f9..e78e793 100644
--- a/dashboard1/pgs/config.inc.php
+++ b/dashboard1/pgs/config.inc.php
@@ -17,7 +17,7 @@ $VNStat = array();
$PageOptions['ContactEmail'] = 'your_email'; // Support E-Mail address
-$PageOptions['DashboardVersion'] = '2.4.4'; // Dashboard Version
+$PageOptions['DashboardVersion'] = '2.4.5'; // Dashboard Version
$PageOptions['PageRefreshActive'] = true; // Activate automatic refresh
$PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds
@@ -50,7 +50,9 @@ $PageOptions['MetaRobots'] = 'index,follow'; //
$PageOptions['UserPage']['ShowFilter'] = true; // Show Filter on Users page
$PageOptions['Traffic']['Show'] = false; // Enable vnstat traffic statistics
-$PageOptions['IRCDDB']['Show'] = true; // Show liveircddb, set it to false if you are running your db in https
+$PageOptions['IRCDDB']['Show'] = true; // Show liveircddb menu option
+// $PageOptions['IRCDDB']['URL'] = 'http://live.ircddb.net:8080'; // Optional: Override ircddb server URL
+// $PageOptions['IRCDDB']['Page'] = 'ircddblive5.html'; // Optional: Override ircddb page
$PageOptions['CustomTXT'] = ''; // custom text in your header
diff --git a/dashboard1/pgs/ircddb_proxy.php b/dashboard1/pgs/ircddb_proxy.php
new file mode 100644
index 0000000..fee4fb0
--- /dev/null
+++ b/dashboard1/pgs/ircddb_proxy.php
@@ -0,0 +1,186 @@
+ $upstreamUrl,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_MAXREDIRS => 3,
+ CURLOPT_TIMEOUT => 15,
+ CURLOPT_CONNECTTIMEOUT => 5,
+ CURLOPT_USERAGENT => 'XLX-Dashboard-Proxy/1.0',
+ CURLOPT_HEADER => true,
+ ]);
+
+ $response = curl_exec($ch);
+ $error = curl_error($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+
+ curl_close($ch);
+
+ if ($response !== false && $httpCode > 0) {
+ // Split headers and body
+ $headers = substr($response, 0, $headerSize);
+ $body = substr($response, $headerSize);
+
+ // Parse Content-Type from headers
+ $headerLines = explode("\r\n", $headers);
+ foreach ($headerLines as $header) {
+ if (stripos($header, 'Content-Type:') === 0) {
+ $contentType = trim(substr($header, 13));
+ break;
+ }
+ }
+ }
+} else {
+ // Fallback to file_get_contents with stream context
+ $context = stream_context_create([
+ 'http' => [
+ 'method' => 'GET',
+ 'header' => "User-Agent: XLX-Dashboard-Proxy/1.0\r\n",
+ 'timeout' => 15,
+ 'follow_location' => true,
+ 'max_redirects' => 3,
+ ]
+ ]);
+
+ $body = @file_get_contents($upstreamUrl, false, $context);
+
+ if ($body !== false && isset($http_response_header)) {
+ // Parse response headers
+ foreach ($http_response_header as $header) {
+ // Get HTTP status code
+ if (preg_match('/^HTTP\/\d+\.\d+\s+(\d+)/', $header, $matches)) {
+ $httpCode = (int)$matches[1];
+ }
+ // Get Content-Type
+ if (stripos($header, 'Content-Type:') === 0) {
+ $contentType = trim(substr($header, 13));
+ }
+ }
+ } else {
+ $error = 'Failed to fetch upstream content';
+ }
+}
+
+// Handle errors
+if ($body === false) {
+ http_response_code(502);
+ die('Upstream server unavailable: ' . htmlspecialchars($error));
+}
+
+// Forward the HTTP status code
+http_response_code($httpCode);
+
+// Set content type
+header('Content-Type: ' . $contentType);
+
+// Determine if we need to rewrite URLs in this content
+$rewriteContent = (
+ stripos($contentType, 'text/html') !== false ||
+ stripos($contentType, 'text/javascript') !== false ||
+ stripos($contentType, 'application/javascript') !== false ||
+ stripos($contentType, 'application/x-javascript') !== false
+);
+
+if ($rewriteContent && !empty($body)) {
+ // Rewrite relative URLs to go through this proxy
+ // Handle src="file.js", href="file.css", url("file"), etc.
+
+ // Rewrite src attributes
+ $body = preg_replace(
+ '/src\s*=\s*"([^"\/][^"]*)"/',
+ 'src="ircddb_proxy.php?path=$1"',
+ $body
+ );
+ $body = preg_replace(
+ "/src\s*=\s*'([^'\/][^']*)'/",
+ "src='ircddb_proxy.php?path=\$1'",
+ $body
+ );
+
+ // Rewrite href attributes (for CSS, etc.)
+ $body = preg_replace(
+ '/href\s*=\s*"([^"\/][^"]*\.(css|ico))"/',
+ 'href="ircddb_proxy.php?path=$1"',
+ $body
+ );
+ $body = preg_replace(
+ "/href\s*=\s*'([^'\/][^']*\.(css|ico))'/",
+ "href='ircddb_proxy.php?path=\$1'",
+ $body
+ );
+
+ // Rewrite AJAX calls in JavaScript (the jj.yaws or jj3.yaws polling)
+ // The original livelog.js uses: url: "jj.yaws", data: "p=" + lastNum
+ // jQuery appends data as query string, so we need: url: "ircddb_proxy.php?path=jj.yaws"
+ // Then jQuery will make it: ircddb_proxy.php?path=jj.yaws&p=123
+ $body = preg_replace(
+ '/url:\s*"(jj[0-9]*\.yaws)"/',
+ 'url: "ircddb_proxy.php?path=$1"',
+ $body
+ );
+}
+
+// Output the content
+echo $body;
diff --git a/dashboard1/pgs/liveircddb.php b/dashboard1/pgs/liveircddb.php
index 13f5a90..a4a858f 100644
--- a/dashboard1/pgs/liveircddb.php
+++ b/dashboard1/pgs/liveircddb.php
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/dashboard2/changes.txt b/dashboard2/changes.txt
index 40d6de0..327f769 100644
--- a/dashboard2/changes.txt
+++ b/dashboard2/changes.txt
@@ -1,3 +1,20 @@
+xlx db v2.3.10
+Fix liveircddb page broken by CSP security headers
+
+ - "pgs/ircddb_proxy.php" (NEW)
+ * Added transparent proxy for live.ircddb.net to resolve CSP and mixed-content issues
+ * Proxies all requests through local server, rewriting URLs to maintain functionality
+ * Supports both HTTP and HTTPS dashboard deployments
+ * Defaults to live.ircddb.net:8080, configurable via $PageOptions['IRCDDB']['URL']
+ * Default page ircddblive5.html, configurable via $PageOptions['IRCDDB']['Page']
+
+ - "pgs/liveircddb.php"
+ * Changed iframe source from direct external URL to local proxy
+
+ - "pgs/config.inc.php"
+ * Added $PageOptions['IRCDDB']['Show'] option
+ * Added commented out optional override options for URL and Page
+
xlx db v2.3.9
SECURITY UPDATE - Minor upgrade to further improve dashboard security
diff --git a/dashboard2/pgs/config.inc.php b/dashboard2/pgs/config.inc.php
index 1bb5b25..124b786 100644
--- a/dashboard2/pgs/config.inc.php
+++ b/dashboard2/pgs/config.inc.php
@@ -16,7 +16,7 @@ $PageOptions = array();
$PageOptions['ContactEmail'] = 'your_email'; // Support E-Mail address
-$PageOptions['DashboardVersion'] = '2.3.9'; // Dashboard Version
+$PageOptions['DashboardVersion'] = '2.3.10'; // Dashboard Version
$PageOptions['PageRefreshActive'] = true; // Activate automatic refresh
$PageOptions['PageRefreshDelay'] = '10000'; // Page refresh time in miliseconds
@@ -50,6 +50,10 @@ $PageOptions['MetaRobots'] = 'index,follow';
$PageOptions['UserPage']['ShowFilter'] = true; // Show Filter on Users page
+$PageOptions['IRCDDB']['Show'] = true; // Show liveircddb menu option
+// $PageOptions['IRCDDB']['URL'] = 'http://live.ircddb.net:8080'; // Optional: Override ircddb server URL
+// $PageOptions['IRCDDB']['Page'] = 'ircddblive5.html'; // Optional: Override ircddb page
+
$Service['PIDFile'] = '/var/log/xlxd.pid';
$Service['XMLFile'] = '/var/log/xlxd.xml';
diff --git a/dashboard2/pgs/ircddb_proxy.php b/dashboard2/pgs/ircddb_proxy.php
new file mode 100644
index 0000000..fee4fb0
--- /dev/null
+++ b/dashboard2/pgs/ircddb_proxy.php
@@ -0,0 +1,186 @@
+ $upstreamUrl,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_MAXREDIRS => 3,
+ CURLOPT_TIMEOUT => 15,
+ CURLOPT_CONNECTTIMEOUT => 5,
+ CURLOPT_USERAGENT => 'XLX-Dashboard-Proxy/1.0',
+ CURLOPT_HEADER => true,
+ ]);
+
+ $response = curl_exec($ch);
+ $error = curl_error($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+
+ curl_close($ch);
+
+ if ($response !== false && $httpCode > 0) {
+ // Split headers and body
+ $headers = substr($response, 0, $headerSize);
+ $body = substr($response, $headerSize);
+
+ // Parse Content-Type from headers
+ $headerLines = explode("\r\n", $headers);
+ foreach ($headerLines as $header) {
+ if (stripos($header, 'Content-Type:') === 0) {
+ $contentType = trim(substr($header, 13));
+ break;
+ }
+ }
+ }
+} else {
+ // Fallback to file_get_contents with stream context
+ $context = stream_context_create([
+ 'http' => [
+ 'method' => 'GET',
+ 'header' => "User-Agent: XLX-Dashboard-Proxy/1.0\r\n",
+ 'timeout' => 15,
+ 'follow_location' => true,
+ 'max_redirects' => 3,
+ ]
+ ]);
+
+ $body = @file_get_contents($upstreamUrl, false, $context);
+
+ if ($body !== false && isset($http_response_header)) {
+ // Parse response headers
+ foreach ($http_response_header as $header) {
+ // Get HTTP status code
+ if (preg_match('/^HTTP\/\d+\.\d+\s+(\d+)/', $header, $matches)) {
+ $httpCode = (int)$matches[1];
+ }
+ // Get Content-Type
+ if (stripos($header, 'Content-Type:') === 0) {
+ $contentType = trim(substr($header, 13));
+ }
+ }
+ } else {
+ $error = 'Failed to fetch upstream content';
+ }
+}
+
+// Handle errors
+if ($body === false) {
+ http_response_code(502);
+ die('Upstream server unavailable: ' . htmlspecialchars($error));
+}
+
+// Forward the HTTP status code
+http_response_code($httpCode);
+
+// Set content type
+header('Content-Type: ' . $contentType);
+
+// Determine if we need to rewrite URLs in this content
+$rewriteContent = (
+ stripos($contentType, 'text/html') !== false ||
+ stripos($contentType, 'text/javascript') !== false ||
+ stripos($contentType, 'application/javascript') !== false ||
+ stripos($contentType, 'application/x-javascript') !== false
+);
+
+if ($rewriteContent && !empty($body)) {
+ // Rewrite relative URLs to go through this proxy
+ // Handle src="file.js", href="file.css", url("file"), etc.
+
+ // Rewrite src attributes
+ $body = preg_replace(
+ '/src\s*=\s*"([^"\/][^"]*)"/',
+ 'src="ircddb_proxy.php?path=$1"',
+ $body
+ );
+ $body = preg_replace(
+ "/src\s*=\s*'([^'\/][^']*)'/",
+ "src='ircddb_proxy.php?path=\$1'",
+ $body
+ );
+
+ // Rewrite href attributes (for CSS, etc.)
+ $body = preg_replace(
+ '/href\s*=\s*"([^"\/][^"]*\.(css|ico))"/',
+ 'href="ircddb_proxy.php?path=$1"',
+ $body
+ );
+ $body = preg_replace(
+ "/href\s*=\s*'([^'\/][^']*\.(css|ico))'/",
+ "href='ircddb_proxy.php?path=\$1'",
+ $body
+ );
+
+ // Rewrite AJAX calls in JavaScript (the jj.yaws or jj3.yaws polling)
+ // The original livelog.js uses: url: "jj.yaws", data: "p=" + lastNum
+ // jQuery appends data as query string, so we need: url: "ircddb_proxy.php?path=jj.yaws"
+ // Then jQuery will make it: ircddb_proxy.php?path=jj.yaws&p=123
+ $body = preg_replace(
+ '/url:\s*"(jj[0-9]*\.yaws)"/',
+ 'url: "ircddb_proxy.php?path=$1"',
+ $body
+ );
+}
+
+// Output the content
+echo $body;
diff --git a/dashboard2/pgs/liveircddb.php b/dashboard2/pgs/liveircddb.php
index 0e1dc37..5e77f4d 100644
--- a/dashboard2/pgs/liveircddb.php
+++ b/dashboard2/pgs/liveircddb.php
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file