mirror of
https://github.com/Terranom674/Piwigo_Bratonien_Tools.git
synced 2026-09-20 11:04:34 +00:00
Compare commits
27 Commits
577d1ebf81
...
bf03fea647
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf03fea647 | ||
|
|
b3aef2744f | ||
|
|
bcf98c63ef | ||
|
|
fa3ce09589 | ||
|
|
fde03e66f9 | ||
|
|
b780636791 | ||
|
|
8785a1ccc5 | ||
|
|
554778601f | ||
|
|
c1665e53ad | ||
|
|
047045ad23 | ||
|
|
0355f4a555 | ||
|
|
a71c07cbce | ||
|
|
69213adabf | ||
|
|
272930ead5 | ||
|
|
5a115e3b4a | ||
|
|
6791b65e50 | ||
|
|
2238845579 | ||
|
|
4c862c77b3 | ||
|
|
370572c77d | ||
|
|
74911e473c | ||
|
|
65cd8898d3 | ||
|
|
e46196c143 | ||
|
|
6a71532839 | ||
|
|
3cd647ea6b | ||
|
|
639e50afb5 | ||
|
|
17f7fa8d52 | ||
|
|
af527e496b |
@@ -179,6 +179,76 @@ function bratonien_tools_nc_connector_piwigo_api_request($api_key_id, $api_key_s
|
||||
return bratonien_tools_nc_connector_api_payload($decoded);
|
||||
}
|
||||
|
||||
function bratonien_tools_nc_connector_validate_fallback_credentials($username, $password)
|
||||
{
|
||||
if (!function_exists('curl_init'))
|
||||
{
|
||||
throw new RuntimeException('cURL ist in PHP nicht verfuegbar. Der Piwigo-Fallback kann nicht geprueft werden.');
|
||||
}
|
||||
|
||||
$username = trim((string)$username);
|
||||
$password = (string)$password;
|
||||
if ($username === '' || $password === '')
|
||||
{
|
||||
throw new RuntimeException('Piwigo-Benutzername und Passwort muessen angegeben werden.');
|
||||
}
|
||||
|
||||
$cookie_file = tempnam(sys_get_temp_dir(), 'br-pwg-auth-');
|
||||
if ($cookie_file === false)
|
||||
{
|
||||
throw new RuntimeException('Temporare Piwigo-Sitzung konnte nicht angelegt werden.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$url = rtrim(get_absolute_root_url(true), '/').'/ws.php?format=json';
|
||||
$request = function(array $fields) use ($url, $cookie_file)
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, array(
|
||||
CURLOPT_RETURNTRANSFER=>true,
|
||||
CURLOPT_POST=>true,
|
||||
CURLOPT_POSTFIELDS=>http_build_query($fields),
|
||||
CURLOPT_COOKIEJAR=>$cookie_file,
|
||||
CURLOPT_COOKIEFILE=>$cookie_file,
|
||||
CURLOPT_CONNECTTIMEOUT=>10,
|
||||
CURLOPT_TIMEOUT=>20,
|
||||
CURLOPT_FOLLOWLOCATION=>false,
|
||||
CURLOPT_USERAGENT=>'Bratonien-Tools-NC-Connector/'.(function_exists('bratonien_tools_current_version') ? bratonien_tools_current_version() : 'dev'),
|
||||
));
|
||||
$body = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
$error = curl_error($ch);
|
||||
$http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$type = (string)curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
curl_close($ch);
|
||||
if ($body === false || $errno !== 0) throw new RuntimeException('Piwigo-Fallback konnte nicht geprueft werden: '.$error);
|
||||
if ($http < 200 || $http >= 300) throw new RuntimeException('Piwigo-Fallback-Pruefung antwortete mit HTTP '.$http.'.');
|
||||
$decoded = bratonien_tools_nc_connector_decode_api_response((string)$body, $type);
|
||||
if (($decoded['stat'] ?? '') !== 'ok')
|
||||
{
|
||||
$message = trim((string)($decoded['message'] ?? $decoded['err'] ?? 'Piwigo hat die Anmeldung abgelehnt.'));
|
||||
throw new RuntimeException($message !== '' ? $message : 'Piwigo hat die Anmeldung abgelehnt.');
|
||||
}
|
||||
return bratonien_tools_nc_connector_api_payload($decoded);
|
||||
};
|
||||
|
||||
$request(array('method'=>'pwg.session.login', 'username'=>$username, 'password'=>$password));
|
||||
$status = $request(array('method'=>'pwg.session.getStatus'));
|
||||
if (!is_array($status)) throw new RuntimeException('Piwigo hat keinen auswertbaren Benutzerstatus geliefert.');
|
||||
$role = strtolower(trim((string)($status['status'] ?? '')));
|
||||
if (!in_array($role, array('admin','webmaster'), true))
|
||||
{
|
||||
throw new RuntimeException('Der Piwigo-Fallback funktioniert, gehoert aber keinem Administrator/Webmaster.');
|
||||
}
|
||||
return array('username'=>(string)($status['username'] ?? $username), 'status'=>$role);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@unlink($cookie_file);
|
||||
}
|
||||
}
|
||||
|
||||
function bratonien_tools_nc_connector_piwigo_api_test()
|
||||
{
|
||||
$api_key_id = trim((string)($_POST['nc_piwigo_api_key_id'] ?? ''));
|
||||
|
||||
@@ -189,6 +189,18 @@ function bratonien_tools_nc_wizard_finish_dispatch()
|
||||
throw new RuntimeException('Da die Piwigo-API übersprungen wurde, ist für diese Verbindung ein Fallback-Zugang erforderlich.');
|
||||
}
|
||||
|
||||
if ($fallback_user !== '')
|
||||
{
|
||||
try
|
||||
{
|
||||
bratonien_tools_nc_connector_validate_fallback_credentials($fallback_user, $fallback_password);
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
throw new RuntimeException('Der Piwigo-Fallback wurde nicht gespeichert: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$result = bratonien_tools_nc_connector_create_webdav_placeholder_from_wizard();
|
||||
unset($_SESSION['bratonien_nc_wizard']);
|
||||
return $result;
|
||||
|
||||
267
include/webdav_image_runtime.inc.php
Normal file
267
include/webdav_image_runtime.inc.php
Normal file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
if (!defined('PHPWG_ROOT_PATH'))
|
||||
{
|
||||
die('Hacking attempt!');
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_image_source_info($image_id)
|
||||
{
|
||||
static $cache = array();
|
||||
$image_id = (int)$image_id;
|
||||
if ($image_id < 1) return null;
|
||||
if (array_key_exists($image_id, $cache)) return $cache[$image_id];
|
||||
|
||||
$result = pwg_query('SELECT path, coi FROM '.IMAGES_TABLE.' WHERE id='.$image_id.' LIMIT 1');
|
||||
if (!pwg_db_num_rows($result)) return $cache[$image_id] = null;
|
||||
$row = pwg_db_fetch_assoc($result);
|
||||
$path = (string)($row['path'] ?? '');
|
||||
if ($path === '') return $cache[$image_id] = null;
|
||||
|
||||
$absolute = $path;
|
||||
if (strpos($absolute, '/') !== 0)
|
||||
{
|
||||
$absolute = PHPWG_ROOT_PATH.ltrim(preg_replace('#^\./#', '', $absolute), '/');
|
||||
}
|
||||
$resolved = realpath($absolute);
|
||||
if ($resolved === false) return $cache[$image_id] = null;
|
||||
|
||||
$normalized = str_replace('\\', '/', $resolved);
|
||||
if (!preg_match('#/nc-webdav-source/connection-([0-9]+)/root-([0-9]+)/(.*)$#', $normalized, $match))
|
||||
{
|
||||
return $cache[$image_id] = null;
|
||||
}
|
||||
|
||||
$connection_id = (int)$match[1];
|
||||
$root_fileid = (int)$match[2];
|
||||
$relative_path = trim((string)$match[3], '/');
|
||||
if ($relative_path === '') return $cache[$image_id] = null;
|
||||
|
||||
$table = defined('BRATONIEN_TOOLS_NC_CONNECTIONS_TABLE')
|
||||
? BRATONIEN_TOOLS_NC_CONNECTIONS_TABLE
|
||||
: $GLOBALS['prefixeTable'].'bratonien_tools_nc_connections';
|
||||
$connection_result = pwg_query('SELECT config_json FROM `'.$table.'` WHERE id='.$connection_id.' LIMIT 1');
|
||||
if (!pwg_db_num_rows($connection_result)) return $cache[$image_id] = null;
|
||||
$connection_row = pwg_db_fetch_assoc($connection_result);
|
||||
$config = json_decode((string)$connection_row['config_json'], true);
|
||||
if (!is_array($config) || (string)($config['source_mode'] ?? '') !== 'webdav-placeholder')
|
||||
{
|
||||
return $cache[$image_id] = null;
|
||||
}
|
||||
|
||||
$root_path = '';
|
||||
$roots = isset($config['roots']) && is_array($config['roots']) ? $config['roots'] : array();
|
||||
foreach ($roots as $root)
|
||||
{
|
||||
if ((int)($root['fileid'] ?? 0) === $root_fileid)
|
||||
{
|
||||
$root_path = trim((string)($root['webdav_path'] ?? ''), '/');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($root_path === '') return $cache[$image_id] = null;
|
||||
|
||||
$webdav_path = $root_path.'/'.$relative_path;
|
||||
$content_type = '';
|
||||
$size = 0;
|
||||
$etag = '';
|
||||
|
||||
// Metadata is optional. Routing must not depend on the root-owned runtime map.
|
||||
$state_dir = rtrim((string)($config['state_dir'] ?? ''), '/');
|
||||
if ($state_dir !== '')
|
||||
{
|
||||
$mapping_file = $state_dir.'/webdav-map.json';
|
||||
if (is_readable($mapping_file))
|
||||
{
|
||||
$mapping = json_decode((string)file_get_contents($mapping_file), true);
|
||||
if (is_array($mapping) && isset($mapping['files']) && is_array($mapping['files']))
|
||||
{
|
||||
$entry = $mapping['files'][$resolved] ?? $mapping['files'][$normalized] ?? null;
|
||||
if (is_array($entry) && (string)($entry['kind'] ?? '') === 'file')
|
||||
{
|
||||
$webdav_path = trim((string)($entry['webdav_path'] ?? $webdav_path), '/');
|
||||
$content_type = (string)($entry['content_type'] ?? '');
|
||||
$size = (int)($entry['size'] ?? 0);
|
||||
$etag = (string)($entry['etag'] ?? '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $cache[$image_id] = array(
|
||||
'image_id'=>$image_id,
|
||||
'connection_id'=>$connection_id,
|
||||
'webdav_path'=>$webdav_path,
|
||||
'content_type'=>$content_type,
|
||||
'size'=>$size,
|
||||
'etag'=>$etag,
|
||||
'coi'=>$row['coi'] ?? null,
|
||||
);
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_image_url($image_id, $preview=false)
|
||||
{
|
||||
$info = bratonien_tools_webdav_image_source_info($image_id);
|
||||
if (!$info) return null;
|
||||
$url = get_root_url().'plugins/'.BRATONIEN_TOOLS_ID.'/webdav-image.php?id='.(int)$image_id;
|
||||
if ($preview) $url .= '&preview=1';
|
||||
if ($info['etag'] !== '') $url .= '&v='.rawurlencode(substr(sha1($info['etag']), 0, 12));
|
||||
return $url;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_preview_path(array $info)
|
||||
{
|
||||
$connection_id = (int)($info['connection_id'] ?? 0);
|
||||
$webdav_path = trim((string)($info['webdav_path'] ?? ''), '/');
|
||||
if ($connection_id < 1 || $webdav_path === '') return null;
|
||||
$path = PHPWG_ROOT_PATH.'_data/bratonien-tools/nc-webdav-preview/connection-'.$connection_id.'/'.sha1($webdav_path).'.webp';
|
||||
return is_file($path) && is_readable($path) ? $path : null;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_custom_derivative_params($key)
|
||||
{
|
||||
if (!class_exists('DerivativeParams') || !class_exists('SizingParams')) return null;
|
||||
$tokens = explode('_', (string)$key);
|
||||
if (!$tokens) return null;
|
||||
|
||||
$token = array_shift($tokens);
|
||||
$crop = 0;
|
||||
$min_size = null;
|
||||
$parse_size = function($value)
|
||||
{
|
||||
$parts = explode('x', (string)$value, 2);
|
||||
if (count($parts) === 1)
|
||||
{
|
||||
$size = max(1, (int)$parts[0]);
|
||||
return array($size, $size);
|
||||
}
|
||||
return array(max(1, (int)$parts[0]), max(1, (int)$parts[1]));
|
||||
};
|
||||
|
||||
if (isset($token[0]) && $token[0] === 's')
|
||||
{
|
||||
$size = $parse_size(substr($token, 1));
|
||||
}
|
||||
elseif (isset($token[0]) && $token[0] === 'e')
|
||||
{
|
||||
$crop = 1;
|
||||
$size = $min_size = $parse_size(substr($token, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count($tokens) < 2) return null;
|
||||
$size = $parse_size($token);
|
||||
$crop_token = array_shift($tokens);
|
||||
$min_size = $parse_size(array_shift($tokens));
|
||||
$crop = function_exists('char_to_fraction') ? char_to_fraction($crop_token) : 0;
|
||||
}
|
||||
|
||||
$params = new DerivativeParams(new SizingParams($size, $crop, $min_size));
|
||||
if (class_exists('ImageStdParams')) ImageStdParams::apply_global($params);
|
||||
return $params;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_derivative_variants()
|
||||
{
|
||||
$variants = array();
|
||||
if (!class_exists('ImageStdParams')) return $variants;
|
||||
|
||||
foreach (ImageStdParams::get_defined_type_map() as $type => $params)
|
||||
{
|
||||
$variants['standard:'.$type] = $params;
|
||||
}
|
||||
foreach (ImageStdParams::$custom as $custom_key => $last_used)
|
||||
{
|
||||
$params = bratonien_tools_webdav_custom_derivative_params($custom_key);
|
||||
if ($params) $variants['custom:'.$custom_key] = $params;
|
||||
}
|
||||
return $variants;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_generate_derivative($params, $src_image)
|
||||
{
|
||||
if (!is_object($src_image) || empty($src_image->id)) return false;
|
||||
$info = bratonien_tools_webdav_image_source_info((int)$src_image->id);
|
||||
if (!$info) return false;
|
||||
$preview = bratonien_tools_webdav_preview_path($info);
|
||||
if (!$preview) return false;
|
||||
|
||||
if (!class_exists('DerivativeImage')) require_once(PHPWG_ROOT_PATH.'include/derivative.inc.php');
|
||||
if (!class_exists('pwg_image')) require_once(PHPWG_ROOT_PATH.'admin/include/image.class.php');
|
||||
|
||||
$derivative = new DerivativeImage($params, $src_image);
|
||||
if ($derivative->same_as_source())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$target = $derivative->get_path();
|
||||
if ($target === '' || strpos($target, PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR) !== 0) return false;
|
||||
|
||||
$preview_mtime = @filemtime($preview) ?: 0;
|
||||
if (is_file($target) && is_readable($target) && (@filemtime($target) ?: 0) >= $preview_mtime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$directory = dirname($target);
|
||||
if (!is_dir($directory) && !mkdir($directory, 0755, true) && !is_dir($directory)) return false;
|
||||
|
||||
$image = new pwg_image($preview);
|
||||
try
|
||||
{
|
||||
$original_size = array($image->get_width(), $image->get_height());
|
||||
$crop_rect = null;
|
||||
$scaled_size = null;
|
||||
$params->sizing->compute($original_size, $info['coi'] ?? null, $crop_rect, $scaled_size);
|
||||
if ($crop_rect)
|
||||
{
|
||||
$image->crop($crop_rect->width(), $crop_rect->height(), $crop_rect->l, $crop_rect->t);
|
||||
}
|
||||
if ($scaled_size)
|
||||
{
|
||||
$image->resize($scaled_size[0], $scaled_size[1]);
|
||||
}
|
||||
if (!empty($params->sharpen))
|
||||
{
|
||||
$image->sharpen($params->sharpen);
|
||||
}
|
||||
$image->write($target);
|
||||
}
|
||||
finally
|
||||
{
|
||||
$image->destroy();
|
||||
}
|
||||
|
||||
@chmod($target, 0644);
|
||||
clearstatcache(true, $target);
|
||||
return is_file($target) && is_readable($target);
|
||||
}
|
||||
|
||||
function bratonien_tools_filter_webdav_src_url($url, $src_image)
|
||||
{
|
||||
if (!is_object($src_image) || empty($src_image->id)) return $url;
|
||||
$webdav_url = bratonien_tools_webdav_image_url((int)$src_image->id, false);
|
||||
return $webdav_url ?: $url;
|
||||
}
|
||||
|
||||
function bratonien_tools_filter_webdav_derivative_url($url, $params, $src_image, $rel_url)
|
||||
{
|
||||
if (!is_object($src_image) || empty($src_image->id)) return $url;
|
||||
$info = bratonien_tools_webdav_image_source_info((int)$src_image->id);
|
||||
if (!$info) return $url;
|
||||
|
||||
try
|
||||
{
|
||||
if (bratonien_tools_webdav_generate_derivative($params, $src_image))
|
||||
{
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
error_log('Bratonien WebDAV derivative #'.(int)$src_image->id.': '.$e->getMessage());
|
||||
}
|
||||
|
||||
$webdav_url = bratonien_tools_webdav_image_url((int)$src_image->id, true);
|
||||
return $webdav_url ?: $url;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Bratonien Tools
|
||||
Version: 0.9.5.12
|
||||
Version: 0.9.5.21
|
||||
Description: Erweiterbare Administrationswerkzeuge fuer die Bratonien-Piwigo-Installation.
|
||||
Plugin URI: https://github.com/Terranom674/Piwigo_Bratonien_Tools
|
||||
Author: Bratonien
|
||||
@@ -16,6 +16,7 @@ define('BRATONIEN_TOOLS_ID', basename(dirname(__FILE__)));
|
||||
define('BRATONIEN_TOOLS_PATH', PHPWG_PLUGINS_PATH . BRATONIEN_TOOLS_ID . '/');
|
||||
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/watermark_runtime.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/webdav_image_runtime.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/public_selection.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/picture_navigation.inc.php');
|
||||
require_once(BRATONIEN_TOOLS_PATH . 'include/batch_titles.inc.php');
|
||||
@@ -26,6 +27,8 @@ require_once(BRATONIEN_TOOLS_PATH . 'include/nc_productive_ws.inc.php');
|
||||
|
||||
add_event_handler('get_admin_plugin_menu_links', 'bratonien_tools_admin_menu');
|
||||
add_event_handler('get_derivative_url', 'bratonien_tools_filter_derivative_url', EVENT_HANDLER_PRIORITY_NEUTRAL, 4);
|
||||
add_event_handler('get_src_image_url', 'bratonien_tools_filter_webdav_src_url', EVENT_HANDLER_PRIORITY_NEUTRAL + 50, 2);
|
||||
add_event_handler('get_derivative_url', 'bratonien_tools_filter_webdav_derivative_url', EVENT_HANDLER_PRIORITY_NEUTRAL + 50, 4);
|
||||
add_event_handler('loc_end_element_set_global', 'bratonien_tools_batch_titles_register_action');
|
||||
add_event_handler('element_set_global_action', 'bratonien_tools_batch_titles_apply', EVENT_HANDLER_PRIORITY_NEUTRAL, 2);
|
||||
add_event_handler('init', 'bratonien_tools_prepare_connector_private_import', EVENT_HANDLER_PRIORITY_NEUTRAL - 30);
|
||||
|
||||
148
runtime/lib/build-webdav-derivatives.php
Normal file
148
runtime/lib/build-webdav-derivatives.php
Normal file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
if (PHP_SAPI !== 'cli')
|
||||
{
|
||||
fwrite(STDERR, "CLI only\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$options = getopt('', array('piwigo-root:', 'connection-id:'));
|
||||
$piwigo_root = rtrim((string)($options['piwigo-root'] ?? ''), '/');
|
||||
$connection_id = (int)($options['connection-id'] ?? 0);
|
||||
if ($piwigo_root === '' || $connection_id < 1)
|
||||
{
|
||||
fwrite(STDERR, "Parameter --piwigo-root und --connection-id werden benoetigt.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!is_dir($piwigo_root))
|
||||
{
|
||||
fwrite(STDERR, "Piwigo-Root wurde nicht gefunden.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
define('PHPWG_ROOT_PATH', $piwigo_root.'/');
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||
$_SERVER['SERVER_ADDR'] = '127.0.0.1';
|
||||
$_SERVER['SERVER_NAME'] = 'localhost';
|
||||
$_SERVER['HTTP_HOST'] = 'localhost';
|
||||
$_SERVER['SERVER_PORT'] = '80';
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$_SERVER['REQUEST_URI'] = '/';
|
||||
$_SERVER['SCRIPT_NAME'] = '/plugins/bratonien_tools/runtime/lib/build-webdav-derivatives.php';
|
||||
$_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'];
|
||||
$_SERVER['QUERY_STRING'] = '';
|
||||
$_SERVER['HTTP_USER_AGENT'] = 'Bratonien-WebDAV-Derivative-Builder/0.9.5.21';
|
||||
$_SERVER['HTTPS'] = 'off';
|
||||
|
||||
require_once(PHPWG_ROOT_PATH.'include/common.inc.php');
|
||||
require_once(PHPWG_ROOT_PATH.'include/derivative.inc.php');
|
||||
require_once(PHPWG_ROOT_PATH.'admin/include/image.class.php');
|
||||
|
||||
if (!function_exists('bratonien_tools_webdav_image_source_info') || !function_exists('bratonien_tools_webdav_generate_derivative'))
|
||||
{
|
||||
fwrite(STDERR, "Bratonien WebDAV-Bildruntime ist nicht aktiv.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$variants = bratonien_tools_webdav_derivative_variants();
|
||||
if (!$variants)
|
||||
{
|
||||
throw new RuntimeException('Keine Piwigo-Derivate konfiguriert.');
|
||||
}
|
||||
|
||||
$images = 0;
|
||||
$generated = 0;
|
||||
$identity = 0;
|
||||
$metadata_repaired = 0;
|
||||
$errors = 0;
|
||||
|
||||
$result = pwg_query('SELECT * FROM '.IMAGES_TABLE.' ORDER BY id');
|
||||
while ($row = pwg_db_fetch_assoc($result))
|
||||
{
|
||||
$image_id = (int)$row['id'];
|
||||
$info = bratonien_tools_webdav_image_source_info($image_id);
|
||||
if (!$info || (int)$info['connection_id'] !== $connection_id) continue;
|
||||
|
||||
$images++;
|
||||
$preview = bratonien_tools_webdav_preview_path($info);
|
||||
if (!$preview)
|
||||
{
|
||||
$errors++;
|
||||
fwrite(STDERR, 'Bild #'.$image_id.": vorbereitetes WebDAV-Preview fehlt.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
$size = @getimagesize($preview);
|
||||
if (!is_array($size) || empty($size[0]) || empty($size[1]))
|
||||
{
|
||||
$errors++;
|
||||
fwrite(STDERR, 'Bild #'.$image_id.": Abmessungen des WebDAV-Previews konnten nicht gelesen werden.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
$preview_width = (int)$size[0];
|
||||
$preview_height = (int)$size[1];
|
||||
if ((int)($row['width'] ?? 0) !== $preview_width || (int)($row['height'] ?? 0) !== $preview_height || (int)($row['rotation'] ?? 0) !== 0)
|
||||
{
|
||||
pwg_query(
|
||||
'UPDATE '.IMAGES_TABLE.
|
||||
' SET width='.$preview_width.', height='.$preview_height.', rotation=0'.
|
||||
' WHERE id='.$image_id
|
||||
);
|
||||
$row['width'] = $preview_width;
|
||||
$row['height'] = $preview_height;
|
||||
$row['rotation'] = 0;
|
||||
$metadata_repaired++;
|
||||
}
|
||||
|
||||
$src = new SrcImage($row);
|
||||
foreach ($variants as $variant_name => $params)
|
||||
{
|
||||
try
|
||||
{
|
||||
$probe = new DerivativeImage($params, $src);
|
||||
if ($probe->same_as_source())
|
||||
{
|
||||
$identity++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bratonien_tools_webdav_generate_derivative($params, $src))
|
||||
{
|
||||
$generated++;
|
||||
}
|
||||
else
|
||||
{
|
||||
$errors++;
|
||||
fwrite(STDERR, 'Bild #'.$image_id.' '.$variant_name.": Derivat konnte nicht erzeugt werden.\n");
|
||||
}
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
$errors++;
|
||||
fwrite(STDERR, 'Bild #'.$image_id.' '.$variant_name.': '.$e->getMessage()."\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($metadata_repaired > 0)
|
||||
{
|
||||
update_category('all');
|
||||
invalidate_user_cache(true);
|
||||
}
|
||||
|
||||
echo 'WebDAV-Derivate: bilder='.$images.
|
||||
' varianten='.count($variants).
|
||||
' erzeugt='.$generated.
|
||||
' identisch='.$identity.
|
||||
' metadaten_repariert='.$metadata_repaired.
|
||||
' fehler='.$errors."\n";
|
||||
exit($errors > 0 ? 1 : 0);
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
fwrite(STDERR, 'WebDAV-Derivate: '.$e->getMessage()."\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -139,6 +139,39 @@ function decrypt_blob($blob, $hex_key)
|
||||
return (string)$plain;
|
||||
}
|
||||
|
||||
function ensure_webdav_site(mysqli $db, $prefixeTable, $piwigo_root, array $connection_config, $connection_id)
|
||||
{
|
||||
if ((string)($connection_config['source_mode'] ?? '') !== 'webdav-placeholder') return 1;
|
||||
|
||||
$gallery_root = rtrim((string)($connection_config['parallel_gallery_root'] ?? ''), '/');
|
||||
if ($gallery_root === '') fail_sync('WebDAV-Galeriewurzel fehlt in der Verbindungskonfiguration.');
|
||||
$piwigo_root = rtrim((string)$piwigo_root, '/');
|
||||
if (strpos($gallery_root, $piwigo_root.'/') !== 0)
|
||||
{
|
||||
fail_sync('WebDAV-Galeriewurzel liegt ausserhalb der Piwigo-Installation.');
|
||||
}
|
||||
if (!is_dir($gallery_root)) fail_sync('WebDAV-Galeriewurzel existiert nicht: '.$gallery_root);
|
||||
|
||||
$relative = ltrim(substr($gallery_root, strlen($piwigo_root)), '/');
|
||||
if ($relative === '') fail_sync('WebDAV-Galeriewurzel darf nicht dem Piwigo-Hauptverzeichnis entsprechen.');
|
||||
$site_url = './'.rtrim($relative, '/').'/';
|
||||
$escaped = $db->real_escape_string($site_url);
|
||||
$result = $db->query("SELECT id FROM `{$prefixeTable}sites` WHERE galleries_url='{$escaped}' LIMIT 1");
|
||||
if (!$result) fail_sync('Piwigo-Site konnte nicht gelesen werden: '.$db->error);
|
||||
if ($result->num_rows)
|
||||
{
|
||||
return (int)$result->fetch_assoc()['id'];
|
||||
}
|
||||
|
||||
if (!$db->query("INSERT INTO `{$prefixeTable}sites` (galleries_url) VALUES ('{$escaped}')"))
|
||||
{
|
||||
fail_sync('Piwigo-Site fuer WebDAV-Verbindung #'.$connection_id.' konnte nicht angelegt werden: '.$db->error);
|
||||
}
|
||||
$site_id = (int)$db->insert_id;
|
||||
if ($site_id < 1) fail_sync('Piwigo-Site fuer WebDAV-Verbindung #'.$connection_id.' erhielt keine gueltige ID.');
|
||||
return $site_id;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$options = getopt('', array('piwigo-root:', 'connection-id:', 'base-url:'));
|
||||
@@ -173,6 +206,8 @@ try
|
||||
$connection_credentials = json_decode($connection_plain, true);
|
||||
if (!is_array($connection_credentials)) $connection_credentials = array();
|
||||
|
||||
$site_id = ensure_webdav_site($db, $prefixeTable, $piwigo_root, $connection_config, $connection_id);
|
||||
|
||||
$api = array('key_id'=>'', 'key_secret'=>'');
|
||||
$connection_scoped = (string)($connection_config['piwigo_auth'] ?? '') === 'connection-scoped' || array_key_exists('api_enabled', $connection_config);
|
||||
if ($connection_scoped)
|
||||
@@ -185,7 +220,6 @@ try
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nur fuer bereits aktive Altverbindungen: bisheriger globaler API-Zugang.
|
||||
$api_result = $db->query("SELECT value FROM `{$prefixeTable}config` WHERE param='bratonien_nc_piwigo_api' LIMIT 1");
|
||||
if ($api_result && $api_result->num_rows)
|
||||
{
|
||||
@@ -216,11 +250,17 @@ try
|
||||
'Accept: application/json, text/xml;q=0.9',
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
);
|
||||
decode_ws(http_request($base_url.'/ws.php?format=json', array('method'=>'bratonien.nc.syncProductive', 'site_id'=>1), $headers));
|
||||
$orphan = decode_ws(http_request($base_url.'/ws.php?format=json', array('method'=>'bratonien.nc.syncOrphans', 'site_id'=>1, 'simulate'=>0), $headers));
|
||||
decode_ws(http_request($base_url.'/ws.php?format=json', array('method'=>'bratonien.nc.syncProductive', 'site_id'=>$site_id), $headers));
|
||||
$orphan = decode_ws(http_request($base_url.'/ws.php?format=json', array('method'=>'bratonien.nc.syncOrphans', 'site_id'=>$site_id, 'simulate'=>0), $headers));
|
||||
// Entfernt alte technische bratonien-webdav-N Wrapper aus Site 1,
|
||||
// nachdem deren generierte Verzeichnisse beim Reconcile entfernt wurden.
|
||||
if ($site_id !== 1)
|
||||
{
|
||||
decode_ws(http_request($base_url.'/ws.php?format=json', array('method'=>'bratonien.nc.syncOrphans', 'site_id'=>1, 'simulate'=>0), $headers));
|
||||
}
|
||||
$added = (int)($orphan['added_orphans'] ?? 0);
|
||||
$deleted = (int)($orphan['deleted_orphans'] ?? 0);
|
||||
echo "Piwigo-Synchronisierung per API erfolgreich\n";
|
||||
echo "Piwigo-Synchronisierung per API erfolgreich (Site $site_id)\n";
|
||||
echo "Piwigo-Orphans synchronisiert: +$added / -$deleted\n";
|
||||
exit(0);
|
||||
}
|
||||
@@ -245,15 +285,19 @@ try
|
||||
{
|
||||
decode_ws(http_request($base_url.'/ws.php?format=json', array('method'=>'pwg.session.login', 'username'=>$fallback_user, 'password'=>$fallback_password), array(), $cookie_file));
|
||||
http_request(
|
||||
$base_url.'/admin.php?page=site_update&site=1',
|
||||
$base_url.'/admin.php?page=site_update&site='.$site_id,
|
||||
array('sync'=>'files','display_info'=>1,'privacy_level'=>0,'sync_meta'=>1,'simulate'=>0,'subcats-included'=>1,'bratonien_connector'=>1,'submit'=>1),
|
||||
array(),
|
||||
$cookie_file
|
||||
);
|
||||
$orphan = decode_ws(http_request($base_url.'/ws.php?format=json', array('method'=>'bratonien.nc.syncOrphans', 'site_id'=>1, 'simulate'=>0), array(), $cookie_file));
|
||||
$orphan = decode_ws(http_request($base_url.'/ws.php?format=json', array('method'=>'bratonien.nc.syncOrphans', 'site_id'=>$site_id, 'simulate'=>0), array(), $cookie_file));
|
||||
if ($site_id !== 1)
|
||||
{
|
||||
decode_ws(http_request($base_url.'/ws.php?format=json', array('method'=>'bratonien.nc.syncOrphans', 'site_id'=>1, 'simulate'=>0), array(), $cookie_file));
|
||||
}
|
||||
$added = (int)($orphan['added_orphans'] ?? 0);
|
||||
$deleted = (int)($orphan['deleted_orphans'] ?? 0);
|
||||
echo "Piwigo-Datenbanksynchronisierung per Benutzername/Passwort-Fallback erfolgreich\n";
|
||||
echo "Piwigo-Datenbanksynchronisierung per Benutzername/Passwort-Fallback erfolgreich (Site $site_id)\n";
|
||||
echo "Piwigo-Orphans synchronisiert: +$added / -$deleted\n";
|
||||
}
|
||||
finally
|
||||
|
||||
179
runtime/lib/precache-webdav-previews.php
Normal file
179
runtime/lib/precache-webdav-previews.php
Normal file
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
if (PHP_SAPI !== 'cli')
|
||||
{
|
||||
fwrite(STDERR, "CLI only\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
function fail_preview($message)
|
||||
{
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
|
||||
function quote_webdav_path($path)
|
||||
{
|
||||
$parts = array_values(array_filter(explode('/', trim((string)$path, '/')), 'strlen'));
|
||||
return implode('/', array_map('rawurlencode', $parts));
|
||||
}
|
||||
|
||||
function fetch_remote_blob($url, $user, $password)
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, array(
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => false,
|
||||
CURLOPT_CONNECTTIMEOUT => 10,
|
||||
CURLOPT_TIMEOUT => 120,
|
||||
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
|
||||
CURLOPT_USERPWD => $user.':'.$password,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_USERAGENT => 'Bratonien-Tools-WebDAV-Precache/0.9.5.19',
|
||||
));
|
||||
$body = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
$error = curl_error($ch);
|
||||
$http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($body === false || $errno !== 0) fail_preview('WebDAV-Bild konnte nicht geladen werden: '.$error);
|
||||
if ($http < 200 || $http >= 300) fail_preview('WebDAV-Bild antwortete mit HTTP '.$http.'.');
|
||||
return (string)$body;
|
||||
}
|
||||
|
||||
function write_preview($blob, $target)
|
||||
{
|
||||
$dir = dirname($target);
|
||||
if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) fail_preview('Preview-Verzeichnis konnte nicht angelegt werden.');
|
||||
|
||||
if (class_exists('Imagick'))
|
||||
{
|
||||
$image = new Imagick();
|
||||
$image->readImageBlob($blob);
|
||||
if (method_exists($image, 'autoOrientImage')) @$image->autoOrientImage();
|
||||
$image->setIteratorIndex(0);
|
||||
$image->thumbnailImage(1600, 1600, true, true);
|
||||
$image->setImageFormat('webp');
|
||||
$image->setImageCompressionQuality(85);
|
||||
if (!$image->writeImage($target)) fail_preview('Imagick konnte das Preview nicht schreiben.');
|
||||
$image->clear();
|
||||
$image->destroy();
|
||||
@chmod($target, 0644);
|
||||
return;
|
||||
}
|
||||
|
||||
if (function_exists('imagecreatefromstring') && function_exists('imagewebp'))
|
||||
{
|
||||
$source = @imagecreatefromstring($blob);
|
||||
if (!$source) fail_preview('GD konnte das Bild nicht dekodieren.');
|
||||
$width = imagesx($source);
|
||||
$height = imagesy($source);
|
||||
if ($width < 1 || $height < 1)
|
||||
{
|
||||
imagedestroy($source);
|
||||
fail_preview('Bildabmessungen sind ungültig.');
|
||||
}
|
||||
$scale = min(1, 1600 / $width, 1600 / $height);
|
||||
$target_width = max(1, (int)round($width * $scale));
|
||||
$target_height = max(1, (int)round($height * $scale));
|
||||
$preview = imagecreatetruecolor($target_width, $target_height);
|
||||
imagealphablending($preview, false);
|
||||
imagesavealpha($preview, true);
|
||||
$transparent = imagecolorallocatealpha($preview, 0, 0, 0, 127);
|
||||
imagefilledrectangle($preview, 0, 0, $target_width, $target_height, $transparent);
|
||||
imagecopyresampled($preview, $source, 0, 0, 0, 0, $target_width, $target_height, $width, $height);
|
||||
if (!imagewebp($preview, $target, 85))
|
||||
{
|
||||
imagedestroy($preview);
|
||||
imagedestroy($source);
|
||||
fail_preview('GD konnte das Preview nicht schreiben.');
|
||||
}
|
||||
imagedestroy($preview);
|
||||
imagedestroy($source);
|
||||
@chmod($target, 0644);
|
||||
return;
|
||||
}
|
||||
|
||||
fail_preview('Weder Imagick noch GD/WebP ist für die Preview-Erzeugung verfügbar.');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$options = getopt('', array('mapping:', 'base-url:', 'user:', 'password-file:', 'cache-dir:'));
|
||||
$mapping_file = (string)($options['mapping'] ?? '');
|
||||
$base_url = rtrim((string)($options['base-url'] ?? ''), '/');
|
||||
$user = trim((string)($options['user'] ?? ''));
|
||||
$password_file = (string)($options['password-file'] ?? '');
|
||||
$cache_dir = rtrim((string)($options['cache-dir'] ?? ''), '/');
|
||||
|
||||
if ($mapping_file === '' || !is_readable($mapping_file)) fail_preview('WebDAV-Mapping ist nicht lesbar.');
|
||||
if ($base_url === '' || $user === '' || $password_file === '' || !is_readable($password_file) || $cache_dir === '') fail_preview('Preview-Parameter sind unvollständig.');
|
||||
if (!function_exists('curl_init')) fail_preview('PHP-cURL ist nicht verfügbar.');
|
||||
|
||||
$password = rtrim((string)file_get_contents($password_file), "\r\n");
|
||||
if ($password === '') fail_preview('Nextcloud-Passwort fehlt.');
|
||||
|
||||
$mapping = json_decode((string)file_get_contents($mapping_file), true);
|
||||
if (!is_array($mapping) || !isset($mapping['files']) || !is_array($mapping['files'])) fail_preview('WebDAV-Mapping ist ungültig.');
|
||||
|
||||
if (!is_dir($cache_dir) && !mkdir($cache_dir, 0755, true) && !is_dir($cache_dir)) fail_preview('Preview-Cache konnte nicht angelegt werden.');
|
||||
@chmod($cache_dir, 0755);
|
||||
$state_file = $cache_dir.'/state.json';
|
||||
$old_state = array();
|
||||
if (is_readable($state_file))
|
||||
{
|
||||
$decoded = json_decode((string)file_get_contents($state_file), true);
|
||||
if (is_array($decoded)) $old_state = $decoded;
|
||||
}
|
||||
|
||||
$new_state = array();
|
||||
$generated = 0;
|
||||
$cached = 0;
|
||||
$errors = 0;
|
||||
|
||||
foreach ($mapping['files'] as $entry)
|
||||
{
|
||||
if (!is_array($entry) || (string)($entry['kind'] ?? '') !== 'file') continue;
|
||||
$webdav_path = trim((string)($entry['webdav_path'] ?? ''), '/');
|
||||
if ($webdav_path === '') continue;
|
||||
$etag = (string)($entry['etag'] ?? '');
|
||||
$key = sha1($webdav_path);
|
||||
$target = $cache_dir.'/'.$key.'.webp';
|
||||
$new_state[$key] = array('path'=>$webdav_path, 'etag'=>$etag);
|
||||
|
||||
if (is_file($target) && isset($old_state[$key]) && (string)($old_state[$key]['etag'] ?? '') === $etag)
|
||||
{
|
||||
$cached++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$url = $base_url.'/remote.php/dav/files/'.rawurlencode($user).'/'.quote_webdav_path($webdav_path);
|
||||
$blob = fetch_remote_blob($url, $user, $password);
|
||||
write_preview($blob, $target);
|
||||
$generated++;
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
$errors++;
|
||||
fwrite(STDERR, $webdav_path.': '.$e->getMessage()."\n");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (glob($cache_dir.'/*.webp') ?: array() as $file)
|
||||
{
|
||||
$key = basename($file, '.webp');
|
||||
if (!isset($new_state[$key])) @unlink($file);
|
||||
}
|
||||
|
||||
file_put_contents($state_file, json_encode($new_state, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)."\n", LOCK_EX);
|
||||
@chmod($state_file, 0644);
|
||||
|
||||
echo 'WebDAV-Previews: erzeugt='.$generated.' vorhanden='.$cached.' fehler='.$errors."\n";
|
||||
exit($errors > 0 ? 1 : 0);
|
||||
}
|
||||
catch (Throwable $e)
|
||||
{
|
||||
fwrite(STDERR, 'WebDAV-Preview-Cache: '.$e->getMessage()."\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -35,12 +35,54 @@ function webdav_shell_value($value)
|
||||
return escapeshellarg((string)$value);
|
||||
}
|
||||
|
||||
function webdav_remove_generated_tree($path, $allowedRoot)
|
||||
{
|
||||
$path = rtrim((string)$path, '/');
|
||||
$allowedRoot = rtrim((string)$allowedRoot, '/');
|
||||
if ($path === '' || $allowedRoot === '' || strpos($path, $allowedRoot.'/') !== 0 || !file_exists($path)) return;
|
||||
if (is_link($path) || is_file($path))
|
||||
{
|
||||
@unlink($path);
|
||||
return;
|
||||
}
|
||||
$items = scandir($path);
|
||||
if (is_array($items))
|
||||
{
|
||||
foreach ($items as $item)
|
||||
{
|
||||
if ($item === '.' || $item === '..') continue;
|
||||
webdav_remove_generated_tree($path.'/'.$item, $allowedRoot);
|
||||
}
|
||||
}
|
||||
@rmdir($path);
|
||||
}
|
||||
|
||||
function webdav_source_fingerprint($baseUrl, $user, array $roots)
|
||||
{
|
||||
$normalized = array();
|
||||
foreach ($roots as $root)
|
||||
{
|
||||
$normalized[] = array(
|
||||
'fileid'=>(int)($root['fileid'] ?? 0),
|
||||
'path'=>trim((string)($root['webdav_path'] ?? ''), '/'),
|
||||
);
|
||||
}
|
||||
usort($normalized, function($a, $b)
|
||||
{
|
||||
$cmp = $a['fileid'] <=> $b['fileid'];
|
||||
return $cmp !== 0 ? $cmp : strcmp($a['path'], $b['path']);
|
||||
});
|
||||
return hash('sha256', strtolower(rtrim((string)$baseUrl, '/'))."\n".strtolower(trim((string)$user))."\n".json_encode($normalized));
|
||||
}
|
||||
|
||||
$pluginRoot = dirname(__DIR__);
|
||||
$piwigoRoot = dirname($pluginRoot, 2);
|
||||
$dbConfig = $piwigoRoot.'/local/config/database.inc.php';
|
||||
$configDir = '/etc/bratonien-tools/nc-connector';
|
||||
$stateRoot = '/var/lib/bratonien-tools/nc-connector';
|
||||
$publicSourceRoot = rtrim($piwigoRoot, '/').'/_data/bratonien-tools/nc-webdav-source';
|
||||
$publicGalleryRoot = rtrim($piwigoRoot, '/').'/_data/bratonien-tools/nc-webdav-gallery';
|
||||
$legacyGalleryRoot = rtrim($piwigoRoot, '/').'/galleries';
|
||||
|
||||
try
|
||||
{
|
||||
@@ -62,15 +104,19 @@ try
|
||||
$hexKey = trim((string)$keyResult->fetch_assoc()['value']);
|
||||
|
||||
$table = $prefixeTable.'bratonien_tools_nc_connections';
|
||||
$rows = $db->query("SELECT id,name,adapter,config_json,secret_blob FROM `{$table}` ORDER BY id");
|
||||
$rows = $db->query("SELECT id,name,adapter,config_json,secret_blob FROM `{$table}` ORDER BY id DESC");
|
||||
if (!$rows) fail_webdav_reconcile('Connector-Verbindungen konnten nicht gelesen werden: '.$db->error);
|
||||
|
||||
if (!is_dir($configDir) && !mkdir($configDir, 0700, true)) fail_webdav_reconcile('Runtime-Konfigurationsverzeichnis konnte nicht angelegt werden.');
|
||||
foreach (array($configDir=>$configDir, $publicSourceRoot=>$publicSourceRoot, $publicGalleryRoot=>$publicGalleryRoot) as $dir=>$unused)
|
||||
{
|
||||
if (!is_dir($dir) && !mkdir($dir, $dir === $configDir ? 0700 : 0755, true)) fail_webdav_reconcile('Runtime-Verzeichnis konnte nicht angelegt werden: '.$dir);
|
||||
}
|
||||
@chmod($configDir, 0700);
|
||||
if (!is_dir($publicSourceRoot) && !mkdir($publicSourceRoot, 0755, true)) fail_webdav_reconcile('WebDAV-Platzhalterbereich konnte nicht angelegt werden.');
|
||||
@chmod($publicSourceRoot, 0755);
|
||||
@chmod($publicGalleryRoot, 0755);
|
||||
|
||||
$known = array();
|
||||
$seenFingerprints = array();
|
||||
|
||||
while ($row = $rows->fetch_assoc())
|
||||
{
|
||||
@@ -81,7 +127,6 @@ try
|
||||
if ((string)($config['source_mode'] ?? '') !== 'webdav-placeholder') continue;
|
||||
if (empty($config['parallel_test'])) continue;
|
||||
|
||||
$known[$id] = true;
|
||||
try
|
||||
{
|
||||
$baseUrl = rtrim(trim((string)($config['nextcloud_url'] ?? '')), '/');
|
||||
@@ -94,13 +139,35 @@ try
|
||||
$password = $credentials['nextcloud_password'];
|
||||
if ($user === '' || $password === '') fail_webdav_reconcile('Nextcloud-Zugangsdaten fehlen.');
|
||||
|
||||
$fingerprint = webdav_source_fingerprint($baseUrl, $user, $roots);
|
||||
if (isset($seenFingerprints[$fingerprint]))
|
||||
{
|
||||
fwrite(STDERR, "NC WebDAV #{$id}: identische Quelle bereits durch Verbindung #".$seenFingerprints[$fingerprint]." abgedeckt; doppelte Runtime wird unterdrueckt.\n");
|
||||
foreach (glob($configDir.'/webdav-connection-'.$id.'.*') ?: array() as $stale) @unlink($stale);
|
||||
webdav_remove_generated_tree($legacyGalleryRoot.'/bratonien-webdav-'.$id, $legacyGalleryRoot);
|
||||
webdav_remove_generated_tree($publicGalleryRoot.'/connection-'.$id, $publicGalleryRoot);
|
||||
continue;
|
||||
}
|
||||
$seenFingerprints[$fingerprint] = $id;
|
||||
$known[$id] = true;
|
||||
|
||||
$stateDir = rtrim((string)($config['state_dir'] ?? ''), '/');
|
||||
if ($stateDir === '') $stateDir = $stateRoot.'/connection-'.$id;
|
||||
if (!is_dir($stateDir) && !mkdir($stateDir, 0750, true)) fail_webdav_reconcile('State-Verzeichnis konnte nicht angelegt werden.');
|
||||
@chmod($stateDir, 0750);
|
||||
|
||||
$legacyDefault = $legacyGalleryRoot.'/bratonien-webdav-'.$id;
|
||||
$galleryRoot = rtrim((string)($config['parallel_gallery_root'] ?? ''), '/');
|
||||
if ($galleryRoot === '') $galleryRoot = rtrim($piwigoRoot, '/').'/galleries/bratonien-webdav-'.$id;
|
||||
if ($galleryRoot === '' || $galleryRoot === $legacyDefault || strpos($galleryRoot, $legacyGalleryRoot.'/bratonien-webdav-') === 0)
|
||||
{
|
||||
$galleryRoot = $publicGalleryRoot.'/connection-'.$id;
|
||||
}
|
||||
if (!is_dir($galleryRoot) && !mkdir($galleryRoot, 0755, true)) fail_webdav_reconcile('WebDAV-Galeriebereich konnte nicht angelegt werden.');
|
||||
@chmod($galleryRoot, 0755);
|
||||
|
||||
// Alte technische Wrapper unter ./galleries duerfen nicht als Piwigo-Alben auftauchen.
|
||||
webdav_remove_generated_tree($legacyDefault, $legacyGalleryRoot);
|
||||
|
||||
$sourceDir = $publicSourceRoot.'/connection-'.$id;
|
||||
if (!is_dir($sourceDir) && !mkdir($sourceDir, 0755, true)) fail_webdav_reconcile('WebDAV-Platzhalterquelle konnte nicht angelegt werden.');
|
||||
@chmod($sourceDir, 0755);
|
||||
@@ -143,10 +210,6 @@ try
|
||||
'GALLERY_ROOT='.webdav_shell_value($galleryRoot),
|
||||
'STATE_DIR='.webdav_shell_value($stateDir),
|
||||
'STATUS_FILE='.webdav_shell_value($statusFile),
|
||||
// Der WebDAV-Zweig registriert seinen fertigen Shadow Tree selbst.
|
||||
// Damit ist er nicht von der Aktivitaets-Gate einer bestehenden
|
||||
// lokalen Verbindung abhaengig. Bestehende Verbindungen bleiben
|
||||
// unveraendert und koennen parallel weiterlaufen.
|
||||
'PIWIGO_SYNC_ENABLED=1',
|
||||
);
|
||||
file_put_contents($configPath, implode("\n", $lines)."\n", LOCK_EX);
|
||||
@@ -155,6 +218,7 @@ try
|
||||
$config['state_dir'] = $stateDir;
|
||||
$config['status_file'] = $statusFile;
|
||||
$config['parallel_gallery_root'] = $galleryRoot;
|
||||
$config['source_fingerprint'] = $fingerprint;
|
||||
$config['runtime'] = array(
|
||||
'mode'=>'parallel-webdav',
|
||||
'config'=>$configPath,
|
||||
|
||||
@@ -32,17 +32,18 @@ exec 9>"$LOCK_FILE"
|
||||
flock -n 9 || exit 0
|
||||
|
||||
write_status() {
|
||||
local state="$1" message="$2" detail="${3:-}"
|
||||
python3 - "$STATUS_FILE" "$PIWIGO_ROOT" "$CONNECTION_ID" "$state" "$message" "$detail" <<'PY'
|
||||
local state="$1" message="$2" detail="${3:-}" auth_mode="${4:-webdav}" api_state="${5:-not_run}" api_message="${6:-}" fallback_state="${7:-not_run}" fallback_message="${8:-}"
|
||||
python3 - "$STATUS_FILE" "$PIWIGO_ROOT" "$CONNECTION_ID" "$state" "$message" "$detail" "$auth_mode" "$api_state" "$api_message" "$fallback_state" "$fallback_message" <<'PY'
|
||||
import json, os, sys, tempfile, time
|
||||
status_file, piwigo_root, connection_id, state, message, detail = sys.argv[1:]
|
||||
(status_file, piwigo_root, connection_id, state, message, detail, auth_mode,
|
||||
api_state, api_message, fallback_state, fallback_message) = sys.argv[1:]
|
||||
payload = {
|
||||
"state": state,
|
||||
"message": message,
|
||||
"timestamp": int(time.time()),
|
||||
"auth_mode": "webdav",
|
||||
"api": {"state": "not_run", "message": ""},
|
||||
"fallback": {"state": "not_run", "message": ""},
|
||||
"auth_mode": auth_mode,
|
||||
"api": {"state": api_state, "message": api_message},
|
||||
"fallback": {"state": fallback_state, "message": fallback_message},
|
||||
"error_detail": detail if state == "error" else "",
|
||||
}
|
||||
public_file = os.path.join(piwigo_root.rstrip('/'), '_data', 'bratonien-tools', 'nc-connector-status', f'connection-{connection_id}.json')
|
||||
@@ -92,8 +93,31 @@ python3 "$SCRIPT_DIR/lib/shadow_tree.py" \
|
||||
--destination "$GALLERY_ROOT" \
|
||||
--state "$SHADOW_MAP_FILE"
|
||||
|
||||
trap - ERR
|
||||
PREVIEW_CACHE="$PIWIGO_ROOT/_data/bratonien-tools/nc-webdav-preview/connection-$CONNECTION_ID"
|
||||
PREVIEW_OUTPUT=""
|
||||
PREVIEW_EXIT=0
|
||||
if PREVIEW_OUTPUT="$(php "$SCRIPT_DIR/lib/precache-webdav-previews.php" \
|
||||
--mapping="$WEBDAV_MAPPING_FILE" \
|
||||
--base-url="$WEBDAV_BASE_URL" \
|
||||
--user="$WEBDAV_USER" \
|
||||
--password-file="$WEBDAV_PASSWORD_FILE" \
|
||||
--cache-dir="$PREVIEW_CACHE" 2>&1)"; then
|
||||
PREVIEW_EXIT=0
|
||||
else
|
||||
PREVIEW_EXIT=$?
|
||||
fi
|
||||
[[ -z "$PREVIEW_OUTPUT" ]] || printf '%s\n' "$PREVIEW_OUTPUT"
|
||||
if [[ "$PREVIEW_EXIT" -ne 0 ]]; then
|
||||
DETAIL="Exit-Code: $PREVIEW_EXIT"
|
||||
if [[ -n "$PREVIEW_OUTPUT" ]]; then
|
||||
DETAIL+="; Ausgabe: $(printf '%s\n' "$PREVIEW_OUTPUT" | compact_output)"
|
||||
fi
|
||||
write_status error "WebDAV-Vorschaubilder konnten beim Einlesen nicht erzeugt werden" "$DETAIL"
|
||||
exit "$PREVIEW_EXIT"
|
||||
fi
|
||||
|
||||
if [[ "${PIWIGO_SYNC_ENABLED:-0}" == "1" ]]; then
|
||||
trap - ERR
|
||||
PIWIGO_OUTPUT=""
|
||||
PIWIGO_EXIT=0
|
||||
if PIWIGO_OUTPUT="$(php "$SCRIPT_DIR/lib/piwigo-sync.php" \
|
||||
@@ -111,11 +135,71 @@ if [[ "${PIWIGO_SYNC_ENABLED:-0}" == "1" ]]; then
|
||||
if [[ -n "$PIWIGO_OUTPUT" ]]; then
|
||||
DETAIL+="; Ausgabe: $(printf '%s\n' "$PIWIGO_OUTPUT" | compact_output)"
|
||||
fi
|
||||
write_status error "Piwigo-Synchronisierung des WebDAV-Shadow-Trees fehlgeschlagen" "$DETAIL"
|
||||
|
||||
if grep -qi 'Invalid username/password' <<<"$PIWIGO_OUTPUT"; then
|
||||
write_status error \
|
||||
"Piwigo-Fallback fehlgeschlagen: Benutzername oder Passwort ist ungültig" \
|
||||
"$DETAIL" \
|
||||
"fallback" \
|
||||
"not_configured" \
|
||||
"Für diese Verbindung ist keine nutzbare API konfiguriert" \
|
||||
"error" \
|
||||
"Piwigo hat Benutzername oder Passwort des Fallback-Zugangs abgelehnt"
|
||||
elif grep -qi 'Kein gespeicherter Benutzername/Passwort-Fallback\|Kein verbindungseigener Piwigo-Zugang' <<<"$PIWIGO_OUTPUT"; then
|
||||
write_status error \
|
||||
"Piwigo-Zugang fehlt: API und Fallback sind nicht nutzbar" \
|
||||
"$DETAIL" \
|
||||
"failed" \
|
||||
"not_configured" \
|
||||
"Für diese Verbindung ist keine nutzbare API konfiguriert" \
|
||||
"not_configured" \
|
||||
"Kein vollständiger Fallback-Zugang gespeichert"
|
||||
else
|
||||
write_status error "Piwigo-Synchronisierung des WebDAV-Shadow-Trees fehlgeschlagen" "$DETAIL"
|
||||
fi
|
||||
exit "$PIWIGO_EXIT"
|
||||
fi
|
||||
|
||||
write_status ok "WebDAV-Shadow-Tree und Piwigo-Synchronisierung erfolgreich"
|
||||
DERIVATIVE_OUTPUT=""
|
||||
DERIVATIVE_EXIT=0
|
||||
if DERIVATIVE_OUTPUT="$(php "$SCRIPT_DIR/lib/build-webdav-derivatives.php" \
|
||||
--piwigo-root="$PIWIGO_ROOT" \
|
||||
--connection-id="$CONNECTION_ID" 2>&1)"; then
|
||||
DERIVATIVE_EXIT=0
|
||||
else
|
||||
DERIVATIVE_EXIT=$?
|
||||
fi
|
||||
[[ -z "$DERIVATIVE_OUTPUT" ]] || printf '%s\n' "$DERIVATIVE_OUTPUT"
|
||||
if [[ "$DERIVATIVE_EXIT" -ne 0 ]]; then
|
||||
DETAIL="Exit-Code: $DERIVATIVE_EXIT"
|
||||
if [[ -n "$DERIVATIVE_OUTPUT" ]]; then
|
||||
DETAIL+="; Ausgabe: $(printf '%s\n' "$DERIVATIVE_OUTPUT" | compact_output)"
|
||||
fi
|
||||
write_status error "Piwigo-Derivate für WebDAV-Bilder konnten nicht erzeugt werden" "$DETAIL"
|
||||
exit "$DERIVATIVE_EXIT"
|
||||
fi
|
||||
|
||||
if grep -q 'Piwigo-Synchronisierung per API erfolgreich' <<<"$PIWIGO_OUTPUT"; then
|
||||
write_status ok \
|
||||
"WebDAV eingelesen, Piwigo synchronisiert und Derivate erzeugt" \
|
||||
"" \
|
||||
"api" \
|
||||
"ok" \
|
||||
"Piwigo-API erfolgreich" \
|
||||
"not_needed" \
|
||||
"Fallback wurde nicht benötigt"
|
||||
elif grep -q 'Piwigo-Datenbanksynchronisierung per Benutzername/Passwort-Fallback erfolgreich' <<<"$PIWIGO_OUTPUT"; then
|
||||
write_status ok \
|
||||
"WebDAV eingelesen, Piwigo über Fallback synchronisiert und Derivate erzeugt" \
|
||||
"" \
|
||||
"fallback" \
|
||||
"not_used" \
|
||||
"API war nicht nutzbar" \
|
||||
"ok" \
|
||||
"Benutzername/Passwort-Fallback erfolgreich"
|
||||
else
|
||||
write_status ok "WebDAV eingelesen, Piwigo synchronisiert und Derivate erzeugt"
|
||||
fi
|
||||
else
|
||||
write_status ok "WebDAV-Shadow-Tree erfolgreich; Registrierung erfolgt im selben Minutenlauf über den bestehenden produktiven Piwigo-Sync"
|
||||
write_status ok "WebDAV eingelesen und Vorschaubilder erzeugt; Registrierung erfolgt über den bestehenden produktiven Piwigo-Sync"
|
||||
fi
|
||||
|
||||
@@ -80,6 +80,14 @@
|
||||
if(dialog){
|
||||
dialog.addEventListener('cancel',function(event){event.preventDefault();event.stopImmediatePropagation();closeAfterReset();},true);
|
||||
dialog.addEventListener('click',function(event){if(event.target===dialog){event.preventDefault();event.stopImmediatePropagation();closeAfterReset();}},true);
|
||||
|
||||
// Every wizard POST explicitly preserves the open state before the
|
||||
// browser leaves the page. Validation errors therefore return to the
|
||||
// same wizard step instead of closing/resetting the dialog.
|
||||
[].slice.call(dialog.querySelectorAll('form[data-bratonien-wizard-form]')).forEach(function(form){
|
||||
form.addEventListener('submit',function(){setOpen(true);},true);
|
||||
});
|
||||
|
||||
try{if(sessionStorage.getItem(storageKey)==='1')showWizard();}catch(e){}
|
||||
}
|
||||
}
|
||||
|
||||
173
webdav-image.php
Normal file
173
webdav-image.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
define('PHPWG_ROOT_PATH', '../../');
|
||||
include_once(PHPWG_ROOT_PATH.'include/common.inc.php');
|
||||
|
||||
if (!defined('BRATONIEN_TOOLS_PATH'))
|
||||
{
|
||||
define('BRATONIEN_TOOLS_ID', basename(__DIR__));
|
||||
define('BRATONIEN_TOOLS_PATH', PHPWG_ROOT_PATH.'plugins/'.BRATONIEN_TOOLS_ID.'/');
|
||||
}
|
||||
require_once(BRATONIEN_TOOLS_PATH.'include/webdav_image_runtime.inc.php');
|
||||
|
||||
function bratonien_tools_webdav_image_abort($status, $message)
|
||||
{
|
||||
http_response_code((int)$status);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
header('Cache-Control: no-store');
|
||||
echo $message;
|
||||
exit;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_image_decrypt_secret($blob, $hex_key)
|
||||
{
|
||||
$hex_key = trim((string)$hex_key);
|
||||
if (!preg_match('/^[a-f0-9]{64}$/', $hex_key)) return null;
|
||||
$outer = base64_decode(trim((string)$blob), true);
|
||||
$payload = is_string($outer) ? json_decode($outer, true) : null;
|
||||
if (!is_array($payload) || (int)($payload['v'] ?? 0) !== 1) return null;
|
||||
$iv = base64_decode((string)($payload['iv'] ?? ''), true);
|
||||
$tag = base64_decode((string)($payload['tag'] ?? ''), true);
|
||||
$cipher = base64_decode((string)($payload['data'] ?? ''), true);
|
||||
if (!is_string($iv) || !is_string($tag) || !is_string($cipher)) return null;
|
||||
$plain = openssl_decrypt($cipher, 'aes-256-gcm', hex2bin($hex_key), OPENSSL_RAW_DATA, $iv, $tag);
|
||||
if ($plain === false) return null;
|
||||
$decoded = json_decode((string)$plain, true);
|
||||
return is_array($decoded) ? $decoded : null;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_image_quote_path($path)
|
||||
{
|
||||
$parts = array_values(array_filter(explode('/', trim((string)$path, '/')), 'strlen'));
|
||||
return implode('/', array_map('rawurlencode', $parts));
|
||||
}
|
||||
|
||||
$image_id = (int)($_GET['id'] ?? 0);
|
||||
if ($image_id < 1) bratonien_tools_webdav_image_abort(400, 'Bild-ID fehlt.');
|
||||
|
||||
$permission_condition = get_sql_condition_FandF(array('forbidden_categories'=>'category_id'), null, true);
|
||||
$access_result = pwg_query('SELECT 1 FROM '.IMAGE_CATEGORY_TABLE.' WHERE image_id='.$image_id.' AND '.$permission_condition.' LIMIT 1');
|
||||
if (!pwg_db_num_rows($access_result)) bratonien_tools_webdav_image_abort(403, 'Kein Zugriff auf dieses Bild.');
|
||||
|
||||
$source = bratonien_tools_webdav_image_source_info($image_id);
|
||||
if (!$source) bratonien_tools_webdav_image_abort(404, 'Keine WebDAV-Quelle für dieses Bild gefunden.');
|
||||
|
||||
if (!empty($_GET['preview']))
|
||||
{
|
||||
$preview = bratonien_tools_webdav_preview_path($source);
|
||||
if ($preview)
|
||||
{
|
||||
$mtime = @filemtime($preview) ?: time();
|
||||
$etag = sha1($preview.'|'.$mtime.'|'.(@filesize($preview) ?: 0));
|
||||
header('Content-Type: image/webp');
|
||||
header('Content-Length: '.(string)filesize($preview));
|
||||
header('ETag: "'.$etag.'"');
|
||||
header('Cache-Control: private, max-age=86400, must-revalidate');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
$client_etag = trim((string)($_SERVER['HTTP_IF_NONE_MATCH'] ?? ''), " \t\r\n\"");
|
||||
if ($client_etag !== '' && hash_equals($etag, $client_etag))
|
||||
{
|
||||
http_response_code(304);
|
||||
exit;
|
||||
}
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'HEAD') readfile($preview);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$table = $GLOBALS['prefixeTable'].'bratonien_tools_nc_connections';
|
||||
$result = pwg_query('SELECT config_json, secret_blob FROM `'.$table.'` WHERE id='.(int)$source['connection_id'].' LIMIT 1');
|
||||
if (!pwg_db_num_rows($result)) bratonien_tools_webdav_image_abort(404, 'WebDAV-Verbindung nicht gefunden.');
|
||||
$row = pwg_db_fetch_assoc($result);
|
||||
$config = json_decode((string)$row['config_json'], true);
|
||||
if (!is_array($config)) bratonien_tools_webdav_image_abort(500, 'WebDAV-Konfiguration ist ungültig.');
|
||||
|
||||
$key_result = pwg_query("SELECT value FROM ".$GLOBALS['prefixeTable']."config WHERE param='bratonien_nc_connector_secret' LIMIT 1");
|
||||
if (!pwg_db_num_rows($key_result)) bratonien_tools_webdav_image_abort(500, 'Connector-Schlüssel fehlt.');
|
||||
$key_row = pwg_db_fetch_assoc($key_result);
|
||||
$credentials = bratonien_tools_webdav_image_decrypt_secret((string)$row['secret_blob'], (string)$key_row['value']);
|
||||
if (!is_array($credentials)) bratonien_tools_webdav_image_abort(500, 'WebDAV-Zugangsdaten konnten nicht gelesen werden.');
|
||||
|
||||
$base_url = rtrim((string)($config['nextcloud_url'] ?? ''), '/');
|
||||
$user = trim((string)($credentials['nextcloud_user'] ?? ''));
|
||||
$password = (string)($credentials['nextcloud_password'] ?? '');
|
||||
$webdav_path = trim((string)$source['webdav_path'], '/');
|
||||
if ($base_url === '' || $user === '' || $password === '' || $webdav_path === '')
|
||||
{
|
||||
bratonien_tools_webdav_image_abort(500, 'WebDAV-Bildquelle ist unvollständig.');
|
||||
}
|
||||
|
||||
$etag = trim((string)($source['etag'] ?? ''));
|
||||
if ($etag !== '')
|
||||
{
|
||||
header('ETag: "'.str_replace('"', '', $etag).'"');
|
||||
$client_etag = trim((string)($_SERVER['HTTP_IF_NONE_MATCH'] ?? ''), " \t\r\n\"");
|
||||
if ($client_etag !== '' && hash_equals($etag, $client_etag))
|
||||
{
|
||||
http_response_code(304);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
header('Cache-Control: private, max-age=300, must-revalidate');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
|
||||
$url = $base_url.'/remote.php/dav/files/'.rawurlencode($user).'/'.bratonien_tools_webdav_image_quote_path($webdav_path);
|
||||
$ch = curl_init($url);
|
||||
$options = array(
|
||||
CURLOPT_FOLLOWLOCATION => false,
|
||||
CURLOPT_CONNECTTIMEOUT => 10,
|
||||
CURLOPT_TIMEOUT => 120,
|
||||
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
|
||||
CURLOPT_USERPWD => $user.':'.$password,
|
||||
CURLOPT_RETURNTRANSFER => false,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_USERAGENT => 'Bratonien-Tools-WebDAV-Image/0.9.5.19',
|
||||
CURLOPT_HEADERFUNCTION => function($ch, $line)
|
||||
{
|
||||
$length = strlen($line);
|
||||
$trimmed = trim($line);
|
||||
if ($trimmed === '') return $length;
|
||||
if (preg_match('#^HTTP/\S+\s+([0-9]{3})#i', $trimmed, $m))
|
||||
{
|
||||
http_response_code((int)$m[1]);
|
||||
return $length;
|
||||
}
|
||||
$colon = strpos($line, ':');
|
||||
if ($colon === false) return $length;
|
||||
$name = strtolower(trim(substr($line, 0, $colon)));
|
||||
$value = trim(substr($line, $colon + 1));
|
||||
if (in_array($name, array('content-type','content-length','content-range','accept-ranges','last-modified'), true))
|
||||
{
|
||||
header($name.': '.$value, true);
|
||||
}
|
||||
return $length;
|
||||
},
|
||||
CURLOPT_WRITEFUNCTION => function($ch, $data)
|
||||
{
|
||||
echo $data;
|
||||
return strlen($data);
|
||||
},
|
||||
);
|
||||
if (!empty($_SERVER['HTTP_RANGE']))
|
||||
{
|
||||
$range = trim((string)$_SERVER['HTTP_RANGE']);
|
||||
if (preg_match('/^bytes=(.+)$/i', $range, $m)) $options[CURLOPT_RANGE] = $m[1];
|
||||
}
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'HEAD')
|
||||
{
|
||||
$options[CURLOPT_NOBODY] = true;
|
||||
}
|
||||
curl_setopt_array($ch, $options);
|
||||
$ok = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
$http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($ok === false || $errno !== 0)
|
||||
{
|
||||
if (!headers_sent()) bratonien_tools_webdav_image_abort(502, 'Nextcloud-Bild konnte nicht geladen werden.');
|
||||
exit;
|
||||
}
|
||||
if ($http < 200 || $http >= 400)
|
||||
{
|
||||
exit;
|
||||
}
|
||||
Reference in New Issue
Block a user