import QtQuick 2.2 import "PropfindParser.js" as Prop // Minimal WebDAV client over XMLHttpRequest. One request at a time // (busy flag); 30 s watchdog turns hangs into a failed() signal. Item { id: dav property bool busy: false signal listReady(var entries) signal fileReady(string content) signal writeDone() signal deleteDone() signal connOk() signal failed(string message) property var currentXhr: null Timer { id: watchdog interval: 30000 repeat: false onTriggered: { if (dav.currentXhr) { dav.currentXhr.abort() dav.currentXhr = null } dav.busy = false dav.failed(qsTr("Network timeout")) } } function authHeader() { var user = appSettings.username if (!user) return "" return "Basic " + Qt.btoa(user + ":" + appSettings.password) } function encodeRel(rel) { if (!rel) return rel var parts = rel.split("/") var out = [] for (var i = 0; i < parts.length; i++) out.push(encodeURIComponent(parts[i])) return out.join("/") } function fullUrl(rel) { var base = String(appSettings.serverUrl).replace(/\s+$/, "") if (!base) return "" if (rel && rel !== "") { if (base.charAt(base.length - 1) !== "/") base += "/" base += encodeRel(rel) } return base } function request(method, url, body, onDone) { if (busy) { failed(qsTr("Request already in progress")) return } busy = true var xhr = new XMLHttpRequest() currentXhr = xhr xhr.open(method, url, true) var auth = authHeader() if (auth) xhr.setRequestHeader("Authorization", auth) if (method === "PROPFIND") xhr.setRequestHeader("Depth", "1") if (method === "PUT") xhr.setRequestHeader("Content-Type", "text/markdown; charset=utf-8") xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return watchdog.stop() if (currentXhr === xhr) { currentXhr = null busy = false onDone(xhr) } // else: aborted by the watchdog — already signalled } xhr.send(body || "") watchdog.restart() } function listDir(relPath) { request("PROPFIND", fullUrl(relPath || ""), "", function (xhr) { if (xhr.status >= 200 && xhr.status < 300) { var entries = Prop.parse(xhr.responseText, appSettings.serverUrl) listReady(entries) } else { failed(statusMessage(xhr.status)) } }) } function readFile(relPath) { request("GET", fullUrl(relPath), "", function (xhr) { if (xhr.status >= 200 && xhr.status < 300) { fileReady(xhr.responseText) } else { failed(statusMessage(xhr.status)) } }) } function writeFile(relPath, content) { request("PUT", fullUrl(relPath), content, function (xhr) { if (xhr.status >= 200 && xhr.status < 300) { writeDone() } else { failed(statusMessage(xhr.status)) } }) } function deleteFile(relPath) { request("DELETE", fullUrl(relPath), "", function (xhr) { if (xhr.status >= 200 && xhr.status < 300) { deleteDone() } else { failed(statusMessage(xhr.status)) } }) } function testConnection() { request("PROPFIND", fullUrl(""), "", function (xhr) { if (xhr.status >= 200 && xhr.status < 300) { connOk() } else { failed(statusMessage(xhr.status)) } }) } function statusMessage(status) { if (status === 401) return qsTr("Invalid credentials (401)") if (status === 403) return qsTr("Access denied (403)") if (status === 404) return qsTr("Folder not found (404) — check the URL") if (status === 405) return qsTr("WebDAV not available (405) — the URL must point to a DAV folder") if (status === 0) return qsTr("Server unreachable") if (status >= 500) return qsTr("Server error (%1)").arg(status) return qsTr("HTTP error %1").arg(status) } }