pull/1822/head
kunfei 3 years ago
parent d131863547
commit 4c38acd664
  1. 57
      app/src/main/java/io/legado/app/help/storage/AppWebDav.kt
  2. 19
      app/src/main/java/io/legado/app/lib/webdav/Authorization.kt
  3. 9
      app/src/main/java/io/legado/app/lib/webdav/HttpAuth.kt
  4. 132
      app/src/main/java/io/legado/app/lib/webdav/WebDav.kt

@ -12,7 +12,7 @@ import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.config.AppConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.webdav.HttpAuth
import io.legado.app.lib.webdav.Authorization
import io.legado.app.lib.webdav.WebDav
import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.IO
@ -31,7 +31,9 @@ object AppWebDav {
val syncBookProgress get() = appCtx.getPrefBoolean(PreferKey.syncBookProgress, true)
var isOk = false
var authorization: Authorization? = null
val isOk get() = authorization != null
init {
runBlocking {
@ -61,14 +63,15 @@ object AppWebDav {
suspend fun upConfig() {
kotlin.runCatching {
isOk = false
authorization = null
val account = appCtx.getPrefString(PreferKey.webDavAccount)
val password = appCtx.getPrefString(PreferKey.webDavPassword)
if (!account.isNullOrBlank() && !password.isNullOrBlank()) {
HttpAuth.auth = HttpAuth.Auth(account, password)
WebDav(rootWebDavUrl).makeAsDir()
WebDav(bookProgressUrl).makeAsDir()
isOk = true
val mAuthorization = Authorization(account, password)
WebDav(rootWebDavUrl, mAuthorization).makeAsDir()
WebDav(bookProgressUrl, mAuthorization).makeAsDir()
authorization = mAuthorization
}
}
}
@ -77,18 +80,16 @@ object AppWebDav {
private suspend fun getWebDavFileNames(): ArrayList<String> {
val url = rootWebDavUrl
val names = arrayListOf<String>()
if (isOk) {
var files = WebDav(url).listFiles()
authorization?.let {
var files = WebDav(url, it).listFiles()
files = files.reversed()
files.forEach {
val name = it.displayName
files.forEach { webDav ->
val name = webDav.displayName
if (name?.startsWith("backup") == true) {
names.add(name)
}
}
} else {
throw NoStackTraceException("webDav没有配置")
}
} ?: throw NoStackTraceException("webDav没有配置")
return names
}
@ -115,8 +116,8 @@ object AppWebDav {
}
private suspend fun restoreWebDav(name: String) {
rootWebDavUrl.let {
val webDav = WebDav(it + name)
authorization?.let {
val webDav = WebDav(rootWebDavUrl + name, it)
webDav.downloadTo(zipFilePath, true)
@Suppress("BlockingMethodInNonBlockingContext")
ZipUtils.unzipFile(zipFilePath, Backup.backupPath)
@ -126,16 +127,16 @@ object AppWebDav {
}
suspend fun hasBackUp(): Boolean {
if (isOk) {
authorization?.let {
val url = "${rootWebDavUrl}${backupFileName}"
return WebDav(url).exists()
return WebDav(url, it).exists()
}
return false
}
suspend fun backUpWebDav(path: String) {
try {
if (isOk && NetworkUtils.isAvailable()) {
authorization?.let {
val paths = arrayListOf(*Backup.backupFileNames)
for (i in 0 until paths.size) {
paths[i] = path + File.separator + paths[i]
@ -143,7 +144,7 @@ object AppWebDav {
FileUtils.delete(zipFilePath)
if (ZipUtils.zipFiles(paths, zipFilePath)) {
val putUrl = "${rootWebDavUrl}${backupFileName}"
WebDav(putUrl).upload(zipFilePath)
WebDav(putUrl, it).upload(zipFilePath)
}
}
} catch (e: Exception) {
@ -153,14 +154,14 @@ object AppWebDav {
suspend fun exportWebDav(byteArray: ByteArray, fileName: String) {
try {
if (isOk && NetworkUtils.isAvailable()) {
authorization?.let {
// 默认导出到legado文件夹下exports目录
val exportsWebDavUrl = rootWebDavUrl + EncoderUtils.escape("exports") + "/"
// 在legado文件夹创建exports目录,如果不存在的话
WebDav(exportsWebDavUrl).makeAsDir()
WebDav(exportsWebDavUrl, it).makeAsDir()
// 如果导出的本地文件存在,开始上传
val putUrl = exportsWebDavUrl + fileName
WebDav(putUrl).upload(byteArray, "text/plain")
WebDav(putUrl, it).upload(byteArray, "text/plain")
}
} catch (e: Exception) {
Handler(Looper.getMainLooper()).post {
@ -170,14 +171,14 @@ object AppWebDav {
}
fun uploadBookProgress(book: Book) {
if (!isOk) return
val authorization = authorization ?: return
if (!syncBookProgress) return
if (!NetworkUtils.isAvailable()) return
Coroutine.async {
val bookProgress = BookProgress(book)
val json = GSON.toJson(bookProgress)
val url = getProgressUrl(book)
WebDav(url).upload(json.toByteArray(), "application/json")
WebDav(url, authorization).upload(json.toByteArray(), "application/json")
}
}
@ -189,9 +190,9 @@ object AppWebDav {
* 获取书籍进度
*/
suspend fun getBookProgress(book: Book): BookProgress? {
if (isOk && NetworkUtils.isAvailable()) {
authorization?.let {
val url = getProgressUrl(book)
WebDav(url).download()?.let { byteArray ->
WebDav(url, it).download()?.let { byteArray ->
val json = String(byteArray)
if (json.isJson()) {
return GSON.fromJsonObject<BookProgress>(json).getOrNull()
@ -202,7 +203,7 @@ object AppWebDav {
}
suspend fun downloadAllBookProgress() {
if (!isOk) return
authorization ?: return
appDb.bookDao.all.forEach { book ->
getBookProgress(book)?.let { bookProgress ->
if (bookProgress.durChapterIndex > book.durChapterIndex ||

@ -0,0 +1,19 @@
package io.legado.app.lib.webdav
import okhttp3.Credentials
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
data class Authorization(
val username: String,
val password: String,
val charset: Charset = StandardCharsets.ISO_8859_1
) {
val data: String = Credentials.basic(username, password, charset)
override fun toString(): String {
return data
}
}

@ -1,9 +0,0 @@
package io.legado.app.lib.webdav
object HttpAuth {
var auth: Auth? = null
class Auth internal constructor(val user: String, val pass: String)
}

@ -1,17 +1,17 @@
package io.legado.app.lib.webdav
import android.util.Log
import io.legado.app.constant.AppLog
import io.legado.app.help.http.newCallResponse
import io.legado.app.help.http.newCallResponseBody
import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.text
import io.legado.app.utils.printOnDebug
import okhttp3.Credentials
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.intellij.lang.annotations.Language
import org.jsoup.Jsoup
import java.io.File
import java.io.InputStream
import java.net.MalformedURLException
@ -19,7 +19,7 @@ import java.net.URL
import java.net.URLEncoder
@Suppress("unused", "MemberVisibilityCanBePrivate")
class WebDav(urlStr: String) {
class WebDav(urlStr: String, val authorization: Authorization) {
companion object {
// 指定返回哪些属性
@Language("xml")
@ -88,24 +88,20 @@ class WebDav(urlStr: String) {
} else {
String.format(DIR, requestProps.toString() + "\n")
}
val url = httpUrl
val auth = HttpAuth.auth
if (url != null && auth != null) {
return kotlin.runCatching {
okHttpClient.newCallResponseBody {
url(url)
addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
addHeader("Depth", "1")
// 添加RequestBody对象,可以只返回的属性。如果设为null,则会返回全部属性
// 注意:尽量手动指定需要返回的属性。若返回全部属性,可能后由于Prop.java里没有该属性名,而崩溃。
val requestBody = requestPropsStr.toRequestBody("text/plain".toMediaType())
method("PROPFIND", requestBody)
}.text()
}.onFailure { e ->
e.printOnDebug()
}.getOrNull()
}
return null
val url = httpUrl ?: return null
return kotlin.runCatching {
okHttpClient.newCallResponseBody {
url(url)
addHeader("Authorization", authorization.data)
addHeader("Depth", "1")
// 添加RequestBody对象,可以只返回的属性。如果设为null,则会返回全部属性
// 注意:尽量手动指定需要返回的属性。若返回全部属性,可能后由于Prop.java里没有该属性名,而崩溃。
val requestBody = requestPropsStr.toRequestBody("text/plain".toMediaType())
method("PROPFIND", requestBody)
}.text()
}.onFailure { e ->
e.printOnDebug()
}.getOrNull()
}
private fun parseDir(s: String): List<WebDav> {
@ -120,7 +116,7 @@ class WebDav(urlStr: String) {
val fileName = href.substring(href.lastIndexOf("/") + 1)
val webDavFile: WebDav
try {
webDavFile = WebDav(baseUrl + fileName)
webDavFile = WebDav(baseUrl + fileName, authorization)
webDavFile.displayName = fileName
webDavFile.contentType = element
.getElementsByTag("d:getcontenttype")
@ -157,23 +153,19 @@ class WebDav(urlStr: String) {
* @return 是否创建成功
*/
suspend fun makeAsDir(): Boolean {
val url = httpUrl
val auth = HttpAuth.auth
if (url != null && auth != null) {
//防止报错
return kotlin.runCatching {
if (!exists()) {
okHttpClient.newCallResponseBody {
url(url)
method("MKCOL", null)
addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
}.close()
}
}.onFailure {
AppLog.put(it.localizedMessage)
}.isSuccess
}
return false
val url = httpUrl ?: return false
//防止报错
return kotlin.runCatching {
if (!exists()) {
okHttpClient.newCallResponseBody {
url(url)
method("MKCOL", null)
addHeader("Authorization", authorization.data)
}.close()
}
}.onFailure {
AppLog.put(it.localizedMessage)
}.isSuccess
}
/**
@ -208,49 +200,41 @@ class WebDav(urlStr: String) {
if (!file.exists()) return false
// 务必注意RequestBody不要嵌套,不然上传时内容可能会被追加多余的文件信息
val fileBody = file.asRequestBody(contentType.toMediaType())
val url = httpUrl
val auth = HttpAuth.auth
if (url != null && auth != null) {
return kotlin.runCatching {
okHttpClient.newCallResponseBody {
url(url)
put(fileBody)
addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
}.close()
}.isSuccess
}
return false
val url = httpUrl ?: return false
return kotlin.runCatching {
okHttpClient.newCallResponse {
url(url)
put(fileBody)
addHeader("Authorization", authorization.data)
}.body?.string()?.let {
Log.d("webDav", it)
}
}.onFailure {
it.printOnDebug()
}.isSuccess
}
suspend fun upload(byteArray: ByteArray, contentType: String): Boolean {
// 务必注意RequestBody不要嵌套,不然上传时内容可能会被追加多余的文件信息
val fileBody = byteArray.toRequestBody(contentType.toMediaType())
val url = httpUrl
val auth = HttpAuth.auth
if (url != null && auth != null) {
return kotlin.runCatching {
okHttpClient.newCallResponseBody {
url(url)
put(fileBody)
addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
}.close()
}.isSuccess
}
return false
val url = httpUrl ?: return false
return kotlin.runCatching {
okHttpClient.newCallResponseBody {
url(url)
put(fileBody)
addHeader("Authorization", authorization.data)
}.close()
}.isSuccess
}
private suspend fun getInputStream(): InputStream? {
val url = httpUrl
val auth = HttpAuth.auth
if (url != null && auth != null) {
return kotlin.runCatching {
okHttpClient.newCallResponseBody {
url(url)
addHeader("Authorization", Credentials.basic(auth.user, auth.pass))
}.byteStream()
}.getOrNull()
}
return null
val url = httpUrl ?: return null
return kotlin.runCatching {
okHttpClient.newCallResponseBody {
url(url)
addHeader("Authorization", authorization.data)
}.byteStream()
}.getOrNull()
}
}
Loading…
Cancel
Save