diff --git a/include/nc_transport.inc.php b/include/nc_transport.inc.php index b9eba6a..6791ee1 100644 --- a/include/nc_transport.inc.php +++ b/include/nc_transport.inc.php @@ -94,3 +94,247 @@ function bratonien_tools_nc_transport_apply_curl(array &$options, $url) $options[CURLOPT_RESOLVE] = array($entry); } } + +function bratonien_tools_nc_transport_http($url, $username = '', $password = '', array $headers = array()) +{ + if (!function_exists('curl_init')) throw new RuntimeException('Der Server kann Nextcloud derzeit nicht per HTTP prüfen.'); + + $ch = curl_init($url); + $options = array( + CURLOPT_RETURNTRANSFER=>true, + CURLOPT_FOLLOWLOCATION=>true, + CURLOPT_MAXREDIRS=>3, + CURLOPT_CONNECTTIMEOUT=>8, + CURLOPT_TIMEOUT=>15, + CURLOPT_HTTPHEADER=>array_merge(array('Accept: application/json'), $headers), + CURLOPT_USERAGENT=>'Bratonien-Tools-NC-Wizard/0.9.7.1', + ); + bratonien_tools_nc_transport_apply_curl($options, $url); + + if ($username !== '') + { + $options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; + $options[CURLOPT_USERPWD] = $username.':'.$password; + } + + curl_setopt_array($ch, $options); + $body = curl_exec($ch); + $errno = curl_errno($ch); + $error = curl_error($ch); + $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($body === false || $errno !== 0) + { + throw new RuntimeException('Verbindung fehlgeschlagen'.($error !== '' ? ': '.$error : '.')); + } + + return array('status'=>$status, 'body'=>(string)$body); +} + +function bratonien_tools_nc_transport_webdav_list(array $state, $path = '') +{ + if (empty($state['scan_ok']) || trim((string)$state['base_url']) === '' || trim((string)$state['username']) === '' || (string)$state['_password'] === '') + { + throw new RuntimeException('Die Nextcloud-Sitzung des Assistenten ist nicht vollständig.'); + } + if (!function_exists('curl_init')) throw new RuntimeException('cURL ist für die Verzeichnisauswahl nicht verfügbar.'); + + $path = trim((string)$path, '/'); + if ($path !== '' && preg_match('#(^|/)\.\.(/|$)#', $path)) throw new RuntimeException('Ungültiger Verzeichnispfad.'); + + $segments = $path === '' ? array() : array_map('rawurlencode', explode('/', $path)); + $user = rawurlencode((string)$state['username']); + $url = rtrim((string)$state['base_url'], '/').'/remote.php/dav/files/'.$user.'/'.implode('/', $segments); + if (substr($url, -1) !== '/') $url .= '/'; + + $body = ''; + $ch = curl_init($url); + $options = array( + CURLOPT_RETURNTRANSFER=>true, + CURLOPT_CUSTOMREQUEST=>'PROPFIND', + CURLOPT_POSTFIELDS=>$body, + CURLOPT_HTTPHEADER=>array('Depth: 1','Content-Type: application/xml; charset=utf-8'), + CURLOPT_HTTPAUTH=>CURLAUTH_BASIC, + CURLOPT_USERPWD=>(string)$state['username'].':'.(string)$state['_password'], + CURLOPT_CONNECTTIMEOUT=>8, + CURLOPT_TIMEOUT=>20, + ); + bratonien_tools_nc_transport_apply_curl($options, $url); + curl_setopt_array($ch, $options); + $response = curl_exec($ch); + $errno = curl_errno($ch); + $error = curl_error($ch); + $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($response === false || $errno !== 0) throw new RuntimeException('Nextcloud-Verzeichnisse konnten nicht geladen werden'.($error !== '' ? ': '.$error : '.')); + if ($status === 401 || $status === 403) throw new RuntimeException('Nextcloud hat den Zugriff auf dieses Verzeichnis abgelehnt.'); + if ($status !== 207) throw new RuntimeException('Nextcloud-Verzeichnisabfrage antwortete mit HTTP '.$status.'.'); + + libxml_use_internal_errors(true); + $xml = simplexml_load_string((string)$response); + if ($xml === false) throw new RuntimeException('Nextcloud hat eine ungültige WebDAV-Antwort geliefert.'); + $xml->registerXPathNamespace('d', 'DAV:'); + $xml->registerXPathNamespace('oc', 'http://owncloud.org/ns'); + + $children = array(); + $fileids = array(); + $current_fileid = 0; + $base_path = (string)parse_url($url, PHP_URL_PATH); + + foreach ($xml->xpath('//d:response') as $item) + { + $item->registerXPathNamespace('d', 'DAV:'); + $item->registerXPathNamespace('oc', 'http://owncloud.org/ns'); + $hrefs = $item->xpath('d:href'); + $collections = $item->xpath('d:propstat/d:prop/d:resourcetype/d:collection'); + $ids = $item->xpath('d:propstat/d:prop/oc:fileid'); + if (!$hrefs || !$collections || !$ids) continue; + + $fileid = (int)trim((string)$ids[0]); + if ($fileid < 1) continue; + $href = rawurldecode((string)$hrefs[0]); + $href_path = (string)parse_url($href, PHP_URL_PATH); + + if (rtrim($href_path, '/') === rtrim($base_path, '/')) + { + $current_fileid = $fileid; + continue; + } + + $name = basename(rtrim($href_path, '/')); + if ($name === '') continue; + $child_path = $path === '' ? $name : $path.'/'.$name; + $children[$child_path] = $name; + $fileids[$child_path] = $fileid; + } + natcasesort($children); + + if ($current_fileid < 1) throw new RuntimeException('Nextcloud hat für das aktuelle Verzeichnis keine eindeutige Datei-ID geliefert.'); + + $parent = ''; + if ($path !== '') + { + $parts = explode('/', $path); + array_pop($parts); + $parent = implode('/', $parts); + } + + return array( + 'current'=>$path, + 'parent'=>$parent, + 'children'=>$children, + 'current_fileid'=>$current_fileid, + 'fileids'=>$fileids, + ); +} + +function bratonien_tools_nc_transport_refresh_directory_state(array &$state, $path = null) +{ + if ($path === null) $path = (string)($state['directory_path'] ?? ''); + $listing = bratonien_tools_nc_transport_webdav_list($state, $path); + $state['directory_path'] = (string)$listing['current']; + $state['directory_parent'] = (string)$listing['parent']; + $state['directory_children'] = (array)$listing['children']; + $state['directory_current_fileid'] = (int)$listing['current_fileid']; + $state['directory_fileids'] = (array)$listing['fileids']; + if (!isset($state['directory_selected']) || !is_array($state['directory_selected'])) $state['directory_selected'] = array(); + if (!isset($state['directory_selected_fileids']) || !is_array($state['directory_selected_fileids'])) $state['directory_selected_fileids'] = array(); +} + +function bratonien_tools_nc_transport_wizard_directory_browse() +{ + $state = bratonien_tools_nc_wizard_state(); + if ((int)$state['step'] !== 2 || (string)$state['technical_stage'] !== 'mounts' || empty($state['directory_selection_ready'])) throw new RuntimeException('Die Verzeichnisauswahl ist in diesem Fenster nicht verfügbar.'); + $path = trim((string)($_POST['nc_wizard_directory_path'] ?? ''), '/'); + bratonien_tools_nc_transport_refresh_directory_state($state, $path); + bratonien_tools_nc_wizard_store($state); + return array('message'=>'Verzeichnis geöffnet.'); +} + +function bratonien_tools_nc_transport_edit_start() +{ + $id = (int)($_POST['connection_id'] ?? 0); + $connection = bratonien_tools_nc_connector_connection($id, true); + if (!$connection) throw new RuntimeException('Connector-Verbindung wurde nicht gefunden.'); + + $config = isset($connection['config']) && is_array($connection['config']) ? $connection['config'] : array(); + $is_webdav = (string)$connection['adapter'] === 'remote' + && (string)($config['source_mode'] ?? '') === 'webdav-placeholder'; + if (!$is_webdav) return bratonien_tools_nc_connector_edit_start(); + + $credentials = bratonien_tools_nc_connector_scoped_secret($connection); + $base_url = trim((string)($config['nextcloud_url'] ?? '')); + $username = trim((string)($credentials['nextcloud_user'] ?? '')); + if ($username === '') $username = trim((string)($config['nextcloud_access_user'] ?? $config['access_user'] ?? '')); + $password = (string)($credentials['nextcloud_password'] ?? ''); + + $roots = isset($config['roots']) && is_array($config['roots']) ? array_values($config['roots']) : array(); + $selected = array(); + $selected_ids = array(); + foreach ($roots as $root) + { + $path = trim((string)($root['webdav_path'] ?? ''), '/'); + $fileid = (int)($root['fileid'] ?? 0); + if ($fileid < 1) continue; + $selected[] = $path; + $selected_ids[$path] = $fileid; + } + + $state = bratonien_tools_nc_wizard_state(); + $state = array_merge($state, array( + 'editing_connection_id'=>$id, + 'editing_adapter'=>(string)$connection['adapter'], + 'editing_mode'=>'update', + 'connection_name'=>(string)$connection['name'], + 'host_input'=>$base_url, + 'base_url'=>$base_url, + 'username'=>$username, + '_password'=>$password, + '_fallback_user'=>(string)($credentials['piwigo_user'] ?? ''), + '_fallback_password'=>(string)($credentials['piwigo_password'] ?? ''), + '_api_key_id'=>(string)($credentials['api_key_id'] ?? ''), + '_api_key_secret'=>(string)($credentials['api_key_secret'] ?? ''), + 'api_status'=>trim((string)($credentials['api_key_id'] ?? '')) !== '' && trim((string)($credentials['api_key_secret'] ?? '')) !== '' ? 'ok' : 'pending', + 'roots'=>$roots, + 'directory_selected'=>$selected, + 'directory_selected_fileids'=>$selected_ids, + 'source_mode'=>'webdav-placeholder', + 'transport'=>'webdav', + )); + + if ($base_url !== '' && $username !== '' && $password !== '') + { + $state['step'] = 2; + $state['scan_ok'] = true; + $state['technical_stage'] = 'mounts'; + $state['technical_source'] = 'WebDAV'; + $state['technical_error'] = ''; + $state['technical_complete'] = false; + $state['directory_selection_ready'] = true; + $state['directory_path'] = ''; + $state['directory_parent'] = ''; + $state['directory_children'] = array(); + $state['directory_current_fileid'] = 0; + + try + { + bratonien_tools_nc_transport_refresh_directory_state($state, ''); + } + catch (Throwable $e) + { + $state['step'] = 1; + $state['scan_ok'] = false; + $state['technical_error'] = $e->getMessage(); + } + } + else + { + $state['step'] = 1; + $state['scan_ok'] = false; + } + + bratonien_tools_nc_wizard_store($state); + return array('message'=>'Verbindung #'.$id.' wurde zum Bearbeiten geöffnet.'); +}