146 lines
4.4 KiB
QML
146 lines
4.4 KiB
QML
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)
|
|
}
|
|
}
|