diff --git a/runtime/lib/piwigo-sync.php b/runtime/lib/piwigo-sync.php index 7863d01..16d4a8c 100644 --- a/runtime/lib/piwigo-sync.php +++ b/runtime/lib/piwigo-sync.php @@ -14,19 +14,13 @@ function fail_sync($message) function http_error_detail($body) { $body = trim((string)$body); - if ($body === '') - { - return ''; - } + if ($body === '') return ''; $decoded = json_decode($body, true); if (is_array($decoded)) { $detail = (string)($decoded['message'] ?? $decoded['err'] ?? ''); - if ($detail !== '') - { - return $detail; - } + if ($detail !== '') return $detail; } if (function_exists('simplexml_load_string')) @@ -38,19 +32,12 @@ function http_error_detail($body) if ($xml !== false) { $detail = trim((string)($xml->message ?? $xml->err ?? '')); - if ($detail !== '') - { - return $detail; - } + if ($detail !== '') return $detail; } } $plain = trim(preg_replace('/\s+/', ' ', strip_tags($body))); - if ($plain === '') - { - return ''; - } - return mb_substr($plain, 0, 500); + return $plain === '' ? '' : mb_substr($plain, 0, 500); } function http_request($url, array $fields, array $headers = array(), $cookie_file = null) @@ -65,10 +52,7 @@ function http_request($url, array $fields, array $headers = array(), $cookie_fil CURLOPT_FOLLOWLOCATION => false, CURLOPT_USERAGENT => 'Bratonien-NC-Connector', ); - if ($headers) - { - $options[CURLOPT_HTTPHEADER] = $headers; - } + if ($headers) $options[CURLOPT_HTTPHEADER] = $headers; if ($cookie_file !== null) { $options[CURLOPT_COOKIEJAR] = $cookie_file; @@ -81,42 +65,29 @@ function http_request($url, array $fields, array $headers = array(), $cookie_fil $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); - if ($body === false || $errno !== 0) - { - fail_sync('HTTP-Aufruf fehlgeschlagen: '.$error); - } + if ($body === false || $errno !== 0) fail_sync('HTTP-Aufruf fehlgeschlagen: '.$error); if ($http < 200 || $http >= 300) { $detail = http_error_detail((string)$body); fail_sync('HTTP-Aufruf antwortete mit Status '.$http.($detail !== '' ? ': '.$detail : '.')); } - return (string)$body; } function xml_value_sync(SimpleXMLElement $node) { $children = $node->children(); - if (count($children) === 0) - { - return (string)$node; - } + if (count($children) === 0) return (string)$node; $result = array(); foreach ($children as $name => $child) { $value = xml_value_sync($child); if (array_key_exists($name, $result)) { - if (!is_array($result[$name]) || !array_is_list($result[$name])) - { - $result[$name] = array($result[$name]); - } + if (!is_array($result[$name]) || !array_is_list($result[$name])) $result[$name] = array($result[$name]); $result[$name][] = $value; } - else - { - $result[$name] = $value; - } + else $result[$name] = $value; } return $result; } @@ -126,34 +97,22 @@ function decode_ws($body) $decoded = json_decode((string)$body, true); if (!is_array($decoded)) { - if (!function_exists('simplexml_load_string')) - { - fail_sync('Piwigo lieferte XML, aber SimpleXML ist nicht verfuegbar.'); - } + if (!function_exists('simplexml_load_string')) fail_sync('Piwigo lieferte XML, aber SimpleXML ist nicht verfuegbar.'); $previous = libxml_use_internal_errors(true); $xml = simplexml_load_string((string)$body); libxml_clear_errors(); libxml_use_internal_errors($previous); - if ($xml === false || $xml->getName() !== 'rsp') - { - fail_sync('Piwigo lieferte weder gueltiges JSON noch eine gueltige XML-Webservice-Antwort.'); - } + if ($xml === false || $xml->getName() !== 'rsp') fail_sync('Piwigo lieferte weder gueltiges JSON noch eine gueltige XML-Webservice-Antwort.'); $decoded = array('stat'=>(string)$xml['stat']); foreach ($xml->children() as $name => $child) { $value = xml_value_sync($child); if (array_key_exists($name, $decoded)) { - if (!is_array($decoded[$name]) || !array_is_list($decoded[$name])) - { - $decoded[$name] = array($decoded[$name]); - } + if (!is_array($decoded[$name]) || !array_is_list($decoded[$name])) $decoded[$name] = array($decoded[$name]); $decoded[$name][] = $value; } - else - { - $decoded[$name] = $value; - } + else $decoded[$name] = $value; } } if (($decoded['stat'] ?? '') !== 'ok') @@ -161,36 +120,22 @@ function decode_ws($body) $message = (string)($decoded['message'] ?? $decoded['err'] ?? 'Piwigo-Aufruf wurde abgelehnt.'); fail_sync($message); } - - if (array_key_exists('result', $decoded)) - { - return $decoded['result']; - } - + if (array_key_exists('result', $decoded)) return $decoded['result']; unset($decoded['stat']); return $decoded; } function decrypt_blob($blob, $hex_key) { - if (!preg_match('/^[a-f0-9]{64}$/', (string)$hex_key)) - { - fail_sync('Connector-Schluessel ist ungueltig.'); - } + if (!preg_match('/^[a-f0-9]{64}$/', (string)$hex_key)) fail_sync('Connector-Schluessel ist ungueltig.'); $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) - { - fail_sync('Gespeicherte Zugangsdaten haben ein unbekanntes Format.'); - } + if (!is_array($payload) || (int)($payload['v'] ?? 0) !== 1) fail_sync('Gespeicherte Zugangsdaten haben ein unbekanntes Format.'); $iv = base64_decode((string)($payload['iv'] ?? ''), true); $tag = base64_decode((string)($payload['tag'] ?? ''), true); $cipher = base64_decode((string)($payload['data'] ?? ''), true); $plain = openssl_decrypt($cipher, 'aes-256-gcm', hex2bin($hex_key), OPENSSL_RAW_DATA, $iv, $tag); - if ($plain === false) - { - fail_sync('Gespeicherte Zugangsdaten konnten nicht entschluesselt werden.'); - } + if ($plain === false) fail_sync('Gespeicherte Zugangsdaten konnten nicht entschluesselt werden.'); return (string)$plain; } @@ -200,77 +145,64 @@ try $piwigo_root = rtrim((string)($options['piwigo-root'] ?? ''), '/'); $connection_id = (int)($options['connection-id'] ?? 0); $base_url = rtrim((string)($options['base-url'] ?? 'http://127.0.0.1'), '/'); - - if ($piwigo_root === '' || $connection_id < 1) - { - fail_sync('Parameter --piwigo-root und --connection-id werden benoetigt.'); - } - if (!function_exists('curl_init')) - { - fail_sync('PHP-cURL ist nicht verfuegbar.'); - } + if ($piwigo_root === '' || $connection_id < 1) fail_sync('Parameter --piwigo-root und --connection-id werden benoetigt.'); + if (!function_exists('curl_init')) fail_sync('PHP-cURL ist nicht verfuegbar.'); $db_config = $piwigo_root.'/local/config/database.inc.php'; - if (!is_readable($db_config)) - { - fail_sync('Piwigo-Datenbankkonfiguration ist nicht lesbar.'); - } - + if (!is_readable($db_config)) fail_sync('Piwigo-Datenbankkonfiguration ist nicht lesbar.'); $conf = array(); $prefixeTable = 'piwigo_'; require $db_config; - foreach (array('db_host','db_user','db_password','db_base') as $key) - { - if (!isset($conf[$key])) - { - fail_sync('Piwigo-Datenbankkonfiguration ist unvollstaendig: '.$key); - } - } + foreach (array('db_host','db_user','db_password','db_base') as $key) if (!isset($conf[$key])) fail_sync('Piwigo-Datenbankkonfiguration ist unvollstaendig: '.$key); $db = new mysqli($conf['db_host'], $conf['db_user'], $conf['db_password'], $conf['db_base']); - if ($db->connect_errno) - { - fail_sync('Piwigo-Datenbank ist nicht erreichbar: '.$db->connect_error); - } + if ($db->connect_errno) fail_sync('Piwigo-Datenbank ist nicht erreichbar: '.$db->connect_error); $db->set_charset('utf8mb4'); $key_result = $db->query("SELECT value FROM `{$prefixeTable}config` WHERE param='bratonien_nc_connector_secret' LIMIT 1"); - if (!$key_result || !$key_result->num_rows) - { - fail_sync('Connector-Schluessel wurde nicht gefunden.'); - } + if (!$key_result || !$key_result->num_rows) fail_sync('Connector-Schluessel wurde nicht gefunden.'); $hex_key = (string)$key_result->fetch_assoc()['value']; + $connection_table = $prefixeTable.'bratonien_tools_nc_connections'; + $connection_result = $db->query('SELECT config_json, secret_blob FROM `'.$connection_table.'` WHERE id='.$connection_id.' LIMIT 1'); + if (!$connection_result || !$connection_result->num_rows) fail_sync('Connector-Verbindung #'.$connection_id.' wurde nicht gefunden.'); + $connection_row = $connection_result->fetch_assoc(); + $connection_config = json_decode((string)$connection_row['config_json'], true); + if (!is_array($connection_config)) $connection_config = array(); + $connection_plain = decrypt_blob((string)$connection_row['secret_blob'], $hex_key); + $connection_credentials = json_decode($connection_plain, true); + if (!is_array($connection_credentials)) $connection_credentials = array(); + $api = array('key_id'=>'', 'key_secret'=>''); - $api_result = $db->query("SELECT value FROM `{$prefixeTable}config` WHERE param='bratonien_nc_piwigo_api' LIMIT 1"); - if ($api_result && $api_result->num_rows) + $connection_scoped = (string)($connection_config['piwigo_auth'] ?? '') === 'connection-scoped' || array_key_exists('api_enabled', $connection_config); + if ($connection_scoped) { - $api_blob = (string)$api_result->fetch_assoc()['value']; - if ($api_blob !== '') + if (!empty($connection_config['api_enabled'])) { - $api_plain = decrypt_blob($api_blob, $hex_key); - $api_decoded = json_decode($api_plain, true); - if (is_array($api_decoded)) + $api['key_id'] = trim((string)($connection_credentials['api_key_id'] ?? '')); + $api['key_secret'] = trim((string)($connection_credentials['api_key_secret'] ?? '')); + } + } + 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) + { + $api_blob = (string)$api_result->fetch_assoc()['value']; + if ($api_blob !== '') { - $api['key_id'] = (string)($api_decoded['key_id'] ?? ''); - $api['key_secret'] = (string)($api_decoded['key_secret'] ?? ''); + $api_plain = decrypt_blob($api_blob, $hex_key); + $api_decoded = json_decode($api_plain, true); + if (is_array($api_decoded)) + { + $api['key_id'] = (string)($api_decoded['key_id'] ?? ''); + $api['key_secret'] = (string)($api_decoded['key_secret'] ?? ''); + } } } } - $connection_table = $prefixeTable.'bratonien_tools_nc_connections'; - $connection_result = $db->query('SELECT secret_blob FROM `'.$connection_table.'` WHERE id='.$connection_id.' LIMIT 1'); - if (!$connection_result || !$connection_result->num_rows) - { - fail_sync('Connector-Verbindung #'.$connection_id.' wurde nicht gefunden.'); - } - $connection_blob = (string)$connection_result->fetch_assoc()['secret_blob']; - $connection_plain = decrypt_blob($connection_blob, $hex_key); - $connection_credentials = json_decode($connection_plain, true); - if (!is_array($connection_credentials)) - { - $connection_credentials = array(); - } $fallback_user = (string)($connection_credentials['piwigo_user'] ?? ''); $fallback_password = (string)($connection_credentials['piwigo_password'] ?? ''); @@ -284,16 +216,8 @@ 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'=>1), $headers)); + $orphan = 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"; @@ -308,55 +232,27 @@ try } else { - $api_error = 'Keine gespeicherten API-Zugangsdaten.'; + $api_error = $connection_scoped ? 'Fuer diese Verbindung ist keine API konfiguriert.' : 'Keine gespeicherten API-Zugangsdaten.'; fwrite(STDERR, "Piwigo-API nicht nutzbar: ".$api_error."\n"); } - if ($fallback_user === '' || $fallback_password === '') - { - fail_sync('Kein gespeicherter Benutzername/Passwort-Fallback vorhanden.'); - } + if ($fallback_user === '' || $fallback_password === '') fail_sync('Kein gespeicherter Benutzername/Passwort-Fallback fuer diese Verbindung vorhanden.'); $cookie_file = tempnam(sys_get_temp_dir(), 'br-nc-sync-'); - if ($cookie_file === false) - { - fail_sync('Temporare Piwigo-Sitzung konnte nicht angelegt werden.'); - } + if ($cookie_file === false) fail_sync('Temporare Piwigo-Sitzung konnte nicht angelegt werden.'); 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 - )); - + 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', - array( - 'sync'=>'files', - 'display_info'=>1, - 'privacy_level'=>0, - 'sync_meta'=>1, - 'simulate'=>0, - 'subcats-included'=>1, - 'bratonien_connector'=>1, - 'submit'=>1, - ), + 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'=>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-Orphans synchronisiert: +$added / -$deleted\n"; }