diff --git a/.idea/misc.xml b/.idea/misc.xml index 8a37a45..443bdfd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -31,6 +31,7 @@ + diff --git a/app/build.gradle b/app/build.gradle index d971909..f7282fc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -294,6 +294,10 @@ dependencies { //https://github.com/fengyuecanzhu/Maple implementation("me.fycz.maple:maple:2.0") + + //加解密类库,有些书源使用 + //noinspection GradleDependency,GradlePackageUpdate + implementation('cn.hutool:hutool-crypto:5.8.10') } greendao { diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/CacheManager.kt b/app/src/main/java/xyz/fycz/myreader/greendao/service/CacheManager.kt index 5b0fa0d..0219964 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/service/CacheManager.kt +++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/CacheManager.kt @@ -19,6 +19,7 @@ package xyz.fycz.myreader.greendao.service import android.database.Cursor +import androidx.collection.LruCache import xyz.fycz.myreader.application.App import xyz.fycz.myreader.greendao.DbManager import xyz.fycz.myreader.greendao.entity.Cache @@ -31,6 +32,7 @@ import java.lang.Exception object CacheManager { private val queryTTFMap = hashMapOf>() + private val memoryLruCache = object : LruCache(100) {} /** * saveTime 单位为秒 @@ -49,6 +51,19 @@ object CacheManager { } } + fun putMemory(key: String, value: String) { + memoryLruCache.put(key, value) + } + + //从内存中获取数据 使用lruCache + fun getFromMemory(key: String): String? { + return memoryLruCache.get(key) + } + + fun deleteMemory(key: String) { + memoryLruCache.remove(key) + } + fun get(key: String): String? { var str: String? = null try { @@ -94,6 +109,14 @@ object CacheManager { return null } + fun putFile(key: String, value: String, saveTime: Int = 0) { + ACache.get().put(key, value, saveTime) + } + + fun getFile(key: String): String? { + return ACache.get().getAsString(key) + } + fun delete(key: String) { DbManager.getDaoSession().cacheDao.deleteByKey(key) ACache.get(App.getmContext()).remove(key) diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsEncodeUtils.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsEncodeUtils.kt new file mode 100644 index 0000000..8d804aa --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsEncodeUtils.kt @@ -0,0 +1,496 @@ +/* + * This file is part of FYReader. + * FYReader is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FYReader is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FYReader. If not, see . + * + * Copyright (C) 2020 - 2022 fengyuecanzhu + */ + +package xyz.fycz.myreader.model.third3.analyzeRule + +import android.util.Base64 +import cn.hutool.crypto.digest.DigestUtil +import cn.hutool.crypto.digest.HMac +import cn.hutool.crypto.symmetric.SymmetricCrypto +import xyz.fycz.myreader.util.utils.MD5Utils + +/** + * js加解密扩展类, 在js中通过java变量调用 + * 添加方法,请更新文档/legado/app/src/main/assets/help/JsHelp.md + */ +interface JsEncodeUtils { + + fun md5Encode(str: String): String { + return MD5Utils.md5Encode(str) + } + + fun md5Encode16(str: String): String { + return MD5Utils.md5Encode16(str) + } + + + //******************对称加密解密************************// + + /** + * 在js中这样使用 + * java.createSymmetricCrypto(transformation, key, iv).decrypt(data) + * java.createSymmetricCrypto(transformation, key, iv).decryptStr(data) + + * java.createSymmetricCrypto(transformation, key, iv).encrypt(data) + * java.createSymmetricCrypto(transformation, key, iv).encryptBase64(data) + * java.createSymmetricCrypto(transformation, key, iv).encryptHex(data) + */ + + /* 调用SymmetricCrypto key为null时使用随机密钥*/ + fun createSymmetricCrypto( + transformation: String, + key: ByteArray?, + iv: ByteArray? + ): SymmetricCrypto { + val symmetricCrypto = SymmetricCrypto(transformation, key) + return if (iv != null && iv.isNotEmpty()) symmetricCrypto.setIv(iv) else symmetricCrypto + } + + fun createSymmetricCrypto( + transformation: String, + key: ByteArray + ): SymmetricCrypto { + return createSymmetricCrypto(transformation, key, null) + } + + fun createSymmetricCrypto( + transformation: String, + key: String + ): SymmetricCrypto { + return createSymmetricCrypto(transformation, key, null) + } + + fun createSymmetricCrypto( + transformation: String, + key: String, + iv: String? + ): SymmetricCrypto { + return createSymmetricCrypto( + transformation, key.encodeToByteArray(), iv?.encodeToByteArray() + ) + } + + //******************对称加密解密old************************// + + /////AES + /** + * AES 解码为 ByteArray + * @param str 传入的AES加密的数据 + * @param key AES 解密的key + * @param transformation AES加密的方式 + * @param iv ECB模式的偏移向量 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decrypt(str)") + ) + fun aesDecodeToByteArray( + str: String, key: String, transformation: String, iv: String + ): ByteArray? { + return createSymmetricCrypto(transformation, key, iv).decrypt(str) + } + + /** + * AES 解码为 String + * @param str 传入的AES加密的数据 + * @param key AES 解密的key + * @param transformation AES加密的方式 + * @param iv ECB模式的偏移向量 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(str)") + ) + fun aesDecodeToString( + str: String, key: String, transformation: String, iv: String + ): String? { + return createSymmetricCrypto(transformation, key, iv).decryptStr(str) + } + + /** + * AES解码为String,算法参数经过Base64加密 + * + * @param data 加密的字符串 + * @param key Base64后的密钥 + * @param mode 模式 + * @param padding 补码方式 + * @param iv Base64后的加盐 + * @return 解密后的字符串 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)") + ) + fun aesDecodeArgsBase64Str( + data: String, + key: String, + mode: String, + padding: String, + iv: String + ): String? { + return createSymmetricCrypto( + "AES/${mode}/${padding}", + Base64.decode(key, Base64.NO_WRAP), + Base64.decode(iv, Base64.NO_WRAP) + ).decryptStr(data) + } + + /** + * 已经base64的AES 解码为 ByteArray + * @param str 传入的AES Base64加密的数据 + * @param key AES 解密的key + * @param transformation AES加密的方式 + * @param iv ECB模式的偏移向量 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decrypt(str)") + ) + fun aesBase64DecodeToByteArray( + str: String, key: String, transformation: String, iv: String + ): ByteArray? { + return createSymmetricCrypto(transformation, key, iv).decrypt(str) + } + + /** + * 已经base64的AES 解码为 String + * @param str 传入的AES Base64加密的数据 + * @param key AES 解密的key + * @param transformation AES加密的方式 + * @param iv ECB模式的偏移向量 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(str)") + ) + fun aesBase64DecodeToString( + str: String, key: String, transformation: String, iv: String + ): String? { + return createSymmetricCrypto(transformation, key, iv).decryptStr(str) + } + + /** + * 加密aes为ByteArray + * @param data 传入的原始数据 + * @param key AES加密的key + * @param transformation AES加密的方式 + * @param iv ECB模式的偏移向量 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decrypt(data)") + ) + fun aesEncodeToByteArray( + data: String, key: String, transformation: String, iv: String + ): ByteArray? { + return createSymmetricCrypto(transformation, key, iv).decrypt(data) + } + + /** + * 加密aes为String + * @param data 传入的原始数据 + * @param key AES加密的key + * @param transformation AES加密的方式 + * @param iv ECB模式的偏移向量 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)") + ) + fun aesEncodeToString( + data: String, key: String, transformation: String, iv: String + ): String? { + return createSymmetricCrypto(transformation, key, iv).decryptStr(data) + } + + /** + * 加密aes后Base64化的ByteArray + * @param data 传入的原始数据 + * @param key AES加密的key + * @param transformation AES加密的方式 + * @param iv ECB模式的偏移向量 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data).toByteArray()") + ) + fun aesEncodeToBase64ByteArray( + data: String, key: String, transformation: String, iv: String + ): ByteArray? { + return createSymmetricCrypto(transformation, key, iv).encryptBase64(data).toByteArray() + } + + /** + * 加密aes后Base64化的String + * @param data 传入的原始数据 + * @param key AES加密的key + * @param transformation AES加密的方式 + * @param iv ECB模式的偏移向量 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)") + ) + fun aesEncodeToBase64String( + data: String, key: String, transformation: String, iv: String + ): String? { + return createSymmetricCrypto(transformation, key, iv).encryptBase64(data) + } + + + /** + * AES加密并转为Base64,算法参数经过Base64加密 + * + * @param data 被加密的字符串 + * @param key Base64后的密钥 + * @param mode 模式 + * @param padding 补码方式 + * @param iv Base64后的加盐 + * @return 加密后的Base64 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)") + ) + fun aesEncodeArgsBase64Str( + data: String, + key: String, + mode: String, + padding: String, + iv: String + ): String? { + return createSymmetricCrypto("AES/${mode}/${padding}", key, iv).encryptBase64(data) + } + + /////DES + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)") + ) + fun desDecodeToString( + data: String, key: String, transformation: String, iv: String + ): String? { + return createSymmetricCrypto(transformation, key, iv).decryptStr(data) + } + + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)") + ) + fun desBase64DecodeToString( + data: String, key: String, transformation: String, iv: String + ): String? { + return createSymmetricCrypto(transformation, key, iv).decryptStr(data) + } + + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).encrypt(data)") + ) + fun desEncodeToString( + data: String, key: String, transformation: String, iv: String + ): String? { + return String(createSymmetricCrypto(transformation, key, iv).encrypt(data)) + } + + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)") + ) + fun desEncodeToBase64String( + data: String, key: String, transformation: String, iv: String + ): String? { + return createSymmetricCrypto(transformation, key, iv).encryptBase64(data) + } + + //////3DES + /** + * 3DES解密 + * + * @param data 加密的字符串 + * @param key 密钥 + * @param mode 模式 + * @param padding 补码方式 + * @param iv 加盐 + * @return 解密后的字符串 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)") + ) + fun tripleDESDecodeStr( + data: String, + key: String, + mode: String, + padding: String, + iv: String + ): String? { + return createSymmetricCrypto("DESede/${mode}/${padding}", key, iv).decryptStr(data) + } + + /** + * 3DES解密,算法参数经过Base64加密 + * + * @param data 加密的字符串 + * @param key Base64后的密钥 + * @param mode 模式 + * @param padding 补码方式 + * @param iv Base64后的加盐 + * @return 解密后的字符串 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)") + ) + fun tripleDESDecodeArgsBase64Str( + data: String, + key: String, + mode: String, + padding: String, + iv: String + ): String? { + return createSymmetricCrypto( + "DESede/${mode}/${padding}", + Base64.decode(key, Base64.NO_WRAP), + iv.encodeToByteArray() + ).decryptStr(data) + } + + + /** + * 3DES加密并转为Base64 + * + * @param data 被加密的字符串 + * @param key 密钥 + * @param mode 模式 + * @param padding 补码方式 + * @param iv 加盐 + * @return 加密后的Base64 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)") + ) + fun tripleDESEncodeBase64Str( + data: String, + key: String, + mode: String, + padding: String, + iv: String + ): String? { + return createSymmetricCrypto("DESede/${mode}/${padding}", key, iv) + .encryptBase64(data) + } + + /** + * 3DES加密并转为Base64,算法参数经过Base64加密 + * + * @param data 被加密的字符串 + * @param key Base64后的密钥 + * @param mode 模式 + * @param padding 补码方式 + * @param iv Base64后的加盐 + * @return 加密后的Base64 + */ + @Deprecated( + "过于繁琐弃用", + ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)") + ) + fun tripleDESEncodeArgsBase64Str( + data: String, + key: String, + mode: String, + padding: String, + iv: String + ): String? { + return createSymmetricCrypto( + "DESede/${mode}/${padding}", + Base64.decode(key, Base64.NO_WRAP), + iv.encodeToByteArray() + ).encryptBase64(data) + } + +//******************消息摘要/散列消息鉴别码************************// + + /** + * 生成摘要,并转为16进制字符串 + * + * @param data 被摘要数据 + * @param algorithm 签名算法 + * @return 16进制字符串 + */ + fun digestHex( + data: String, + algorithm: String, + ): String { + return DigestUtil.digester(algorithm).digestHex(data) + } + + /** + * 生成摘要,并转为Base64字符串 + * + * @param data 被摘要数据 + * @param algorithm 签名算法 + * @return Base64字符串 + */ + fun digestBase64Str( + data: String, + algorithm: String, + ): String { + return Base64.encodeToString(DigestUtil.digester(algorithm).digest(data), Base64.NO_WRAP) + } + + /** + * 生成散列消息鉴别码,并转为16进制字符串 + * + * @param data 被摘要数据 + * @param algorithm 签名算法 + * @param key 密钥 + * @return 16进制字符串 + */ + @Suppress("FunctionName") + fun HMacHex( + data: String, + algorithm: String, + key: String + ): String { + return HMac(algorithm, key.toByteArray()).digestHex(data) + } + + /** + * 生成散列消息鉴别码,并转为Base64字符串 + * + * @param data 被摘要数据 + * @param algorithm 签名算法 + * @param key 密钥 + * @return Base64字符串 + */ + @Suppress("FunctionName") + fun HMacBase64( + data: String, + algorithm: String, + key: String + ): String { + return Base64.encodeToString( + HMac(algorithm, key.toByteArray()).digest(data), + Base64.NO_WRAP + ) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsExtensions.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsExtensions.kt index 948c090..98bd8b9 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsExtensions.kt +++ b/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsExtensions.kt @@ -22,6 +22,7 @@ import android.net.Uri import android.util.Base64 import android.util.Log import androidx.annotation.Keep +import cn.hutool.core.util.HexUtil import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking @@ -34,7 +35,9 @@ import xyz.fycz.myreader.greendao.service.CacheManager import xyz.fycz.myreader.greendao.service.CookieStore import xyz.fycz.myreader.model.third3.BaseSource import xyz.fycz.myreader.model.third3.Debug +import xyz.fycz.myreader.model.third3.NoStackTraceException import xyz.fycz.myreader.model.third3.http.* +import xyz.fycz.myreader.util.ToastUtils import xyz.fycz.myreader.util.ZipUtils import xyz.fycz.myreader.util.utils.* import java.io.ByteArrayInputStream @@ -54,7 +57,7 @@ import java.util.zip.ZipInputStream */ @Keep @Suppress("unused") -interface JsExtensions { +interface JsExtensions : JsEncodeUtils { val TAG: String? get() = JsExtensions::class.simpleName @@ -143,11 +146,50 @@ interface JsExtensions { html = html, javaScript = js, headerMap = getSource()?.getHeaderMap(true), - tag = getSource()?.getKey() + tag = getSource()?.getKey() ).getStrResponse().body } } + /** + * 可从网络,本地文件(阅读私有缓存目录和书籍保存位置支持相对路径)导入JavaScript脚本 + */ + fun importScript(path: String): String { + val result = when { + path.startsWith("http") -> cacheFile(path) ?: "" + path.isUri() -> String(DocumentUtil.readBytes(App.getmContext(), Uri.parse(path))) + path.startsWith("/storage") -> FileUtils.readText(path) + else -> readTxtFile(path) + } + if (result.isBlank()) throw NoStackTraceException("$path 内容获取失败或者为空") + return result + } + + /** + * 缓存以文本方式保存的文件 如.js .txt等 + * @param urlStr 网络文件的链接 + * @return 返回缓存后的文件内容 + */ + fun cacheFile(urlStr: String): String? { + return cacheFile(urlStr, 0) + } + + /** + * 缓存以文本方式保存的文件 如.js .txt等 + * @param saveTime 缓存时间,单位:秒 + */ + fun cacheFile(urlStr: String, saveTime: Int): String? { + val key = md5Encode16(urlStr) + val cache = CacheManager.getFile(key) + if (cache.isNullOrBlank()) { + log("首次下载 $urlStr") + val value = ajax(urlStr) ?: return null + CacheManager.putFile(key, value, saveTime) + return value + } + return cache + } + /** * 实现16进制字符串转文件 * @param content 需要转成文件的16进制字符串 @@ -174,20 +216,45 @@ interface JsExtensions { * js实现重定向拦截,网络访问get */ fun get(urlStr: String, headers: Map): Connection.Response { - return Jsoup.connect(urlStr) + val response = Jsoup.connect(urlStr) .sslSocketFactory(SSLHelper.unsafeSSLSocketFactory) .ignoreContentType(true) .followRedirects(false) .headers(headers) .method(Connection.Method.GET) .execute() + val cookies = response.cookies() + CookieStore.mapToCookie(cookies)?.let { + val domain = NetworkUtils.getSubDomain(urlStr) + CacheManager.putMemory("${domain}_cookieJar", it) + } + return response + } + + /** + * js实现重定向拦截,网络访问head,不返回Response Body更省流量 + */ + fun head(urlStr: String, headers: Map): Connection.Response { + val response = Jsoup.connect(urlStr) + .sslSocketFactory(SSLHelper.unsafeSSLSocketFactory) + .ignoreContentType(true) + .followRedirects(false) + .headers(headers) + .method(Connection.Method.HEAD) + .execute() + val cookies = response.cookies() + CookieStore.mapToCookie(cookies)?.let { + val domain = NetworkUtils.getSubDomain(urlStr) + CacheManager.putMemory("${domain}_cookieJar", it) + } + return response } /** * 网络访问post */ fun post(urlStr: String, body: String, headers: Map): Connection.Response { - return Jsoup.connect(urlStr) + val response = Jsoup.connect(urlStr) .sslSocketFactory(SSLHelper.unsafeSSLSocketFactory) .ignoreContentType(true) .followRedirects(false) @@ -195,6 +262,12 @@ interface JsExtensions { .headers(headers) .method(Connection.Method.POST) .execute() + val cookies = response.cookies() + CookieStore.mapToCookie(cookies)?.let { + val domain = NetworkUtils.getSubDomain(urlStr) + CacheManager.putMemory("${domain}_cookieJar", it) + } + return response } /** @@ -243,12 +316,19 @@ interface JsExtensions { return EncoderUtils.base64Encode(str, flags) } - fun md5Encode(str: String): String { - return MD5Utils.md5Encode(str) + /* HexString 解码为字节数组 */ + fun hexDecodeToByteArray(hex: String): ByteArray? { + return HexUtil.decodeHex(hex) + } + + /* hexString 解码为utf8String*/ + fun hexDecodeToString(hex: String): String? { + return HexUtil.decodeHexStr(hex) } - fun md5Encode16(str: String): String { - return MD5Utils.md5Encode16(str) + /* utf8 编码为hexString */ + fun hexEncodeToString(utf8: String): String? { + return HexUtil.encodeHexStr(utf8) } /** @@ -501,174 +581,48 @@ interface JsExtensions { } /** - * 输出调试日志 + * 弹窗提示 */ - fun log(msg: String): String { - getSource()?.let { - Debug.log(it.getKey(), msg) - } ?: Debug.log(msg) - if (App.isDebug()) { - Log.d(TAG + "-" + getSource()?.getKey(), msg) - } - return msg + fun toast(msg: Any?) { + ToastUtils.showInfo("${getSource()?.getTag()}: ${msg.toString()}") } /** - * 生成UUID + * 弹窗提示 停留时间较长 */ - fun randomUUID(): String { - return UUID.randomUUID().toString() + fun longToast(msg: Any?) { + toast(msg) } /** - * AES 解码为 ByteArray - * @param str 传入的AES加密的数据 - * @param key AES 解密的key - * @param transformation AES加密的方式 - * @param iv ECB模式的偏移向量 - */ - fun aesDecodeToByteArray( - str: String, key: String, transformation: String, iv: String - ): ByteArray? { - return try { - EncoderUtils.decryptAES( - data = str.encodeToByteArray(), - key = key.encodeToByteArray(), - transformation, - iv.encodeToByteArray() - ) - } catch (e: Exception) { - Log.e(TAG, e.toString()) - log(e.localizedMessage ?: "aesDecodeToByteArrayERROR") - null - } - } - - /** - * AES 解码为 String - * @param str 传入的AES加密的数据 - * @param key AES 解密的key - * @param transformation AES加密的方式 - * @param iv ECB模式的偏移向量 - */ - - fun aesDecodeToString( - str: String, key: String, transformation: String, iv: String - ): String? { - return aesDecodeToByteArray(str, key, transformation, iv)?.let { String(it) } - } - - /** - * 已经base64的AES 解码为 ByteArray - * @param str 传入的AES Base64加密的数据 - * @param key AES 解密的key - * @param transformation AES加密的方式 - * @param iv ECB模式的偏移向量 - */ - - fun aesBase64DecodeToByteArray( - str: String, key: String, transformation: String, iv: String - ): ByteArray? { - return try { - EncoderUtils.decryptBase64AES( - str.encodeToByteArray(), - key.encodeToByteArray(), - transformation, - iv.encodeToByteArray() - ) - } catch (e: Exception) { - Log.e(TAG, e.toString()) - log(e.localizedMessage ?: "aesDecodeToByteArrayERROR") - null - } - } - - /** - * 已经base64的AES 解码为 String - * @param str 传入的AES Base64加密的数据 - * @param key AES 解密的key - * @param transformation AES加密的方式 - * @param iv ECB模式的偏移向量 - */ - - fun aesBase64DecodeToString( - str: String, key: String, transformation: String, iv: String - ): String? { - return aesBase64DecodeToByteArray(str, key, transformation, iv)?.let { String(it) } - } - - /** - * 加密aes为ByteArray - * @param data 传入的原始数据 - * @param key AES加密的key - * @param transformation AES加密的方式 - * @param iv ECB模式的偏移向量 + * 输出调试日志 */ - fun aesEncodeToByteArray( - data: String, key: String, transformation: String, iv: String - ): ByteArray? { - return try { - EncoderUtils.encryptAES( - data.encodeToByteArray(), - key = key.encodeToByteArray(), - transformation, - iv.encodeToByteArray() - ) - } catch (e: Exception) { - Log.e(TAG, e.toString()) - log(e.localizedMessage ?: "aesEncodeToByteArrayERROR") - null + fun log(msg: String): String { + getSource()?.let { + Debug.log(it.getKey(), msg) + } ?: Debug.log(msg) + if (App.isDebug()) { + Log.d(TAG + "-" + getSource()?.getKey(), msg) } + return msg } /** - * 加密aes为String - * @param data 传入的原始数据 - * @param key AES加密的key - * @param transformation AES加密的方式 - * @param iv ECB模式的偏移向量 - */ - fun aesEncodeToString( - data: String, key: String, transformation: String, iv: String - ): String? { - return aesEncodeToByteArray(data, key, transformation, iv)?.let { String(it) } - } - - /** - * 加密aes后Base64化的ByteArray - * @param data 传入的原始数据 - * @param key AES加密的key - * @param transformation AES加密的方式 - * @param iv ECB模式的偏移向量 + * 输出对象类型 */ - fun aesEncodeToBase64ByteArray( - data: String, key: String, transformation: String, iv: String - ): ByteArray? { - return try { - EncoderUtils.encryptAES2Base64( - data.encodeToByteArray(), - key.encodeToByteArray(), - transformation, - iv.encodeToByteArray() - ) - } catch (e: Exception) { - Log.e(TAG, e.toString()) - log(e.localizedMessage ?: "aesEncodeToBase64ByteArrayERROR") - null + fun logType(any: Any?) { + if (any == null) { + log("null") + } else { + log(any.javaClass.name) } } /** - * 加密aes后Base64化的String - * @param data 传入的原始数据 - * @param key AES加密的key - * @param transformation AES加密的方式 - * @param iv ECB模式的偏移向量 + * 生成UUID */ - fun aesEncodeToBase64String( - data: String, key: String, transformation: String, iv: String - ): String? { - return aesEncodeToBase64ByteArray(data, key, transformation, iv)?.let { String(it) } + fun randomUUID(): String { + return UUID.randomUUID().toString() } fun android(): String { diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/StringExtensions.kt b/app/src/main/java/xyz/fycz/myreader/util/utils/StringExtensions.kt index 0182804..87d191a 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/utils/StringExtensions.kt +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/StringExtensions.kt @@ -23,6 +23,7 @@ package xyz.fycz.myreader.util.utils import android.icu.text.Collator import android.icu.util.ULocale import android.net.Uri +import android.text.Editable import java.io.File import java.util.* @@ -30,14 +31,19 @@ fun String?.safeTrim() = if (this.isNullOrBlank()) null else this.trim() fun String?.isContentScheme(): Boolean = this?.startsWith("content://") == true +fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this) + fun String.parseToUri(): Uri { - return if (isContentScheme()) { - Uri.parse(this) - } else { + return if (isUri()) Uri.parse(this) else { Uri.fromFile(File(this)) } } +fun String?.isUri(): Boolean { + this ?: return false + return this.startsWith("file://", true) || isContentScheme() +} + fun String?.isAbsUrl() = this?.let { it.startsWith("http://", true) || it.startsWith("https://", true) @@ -65,8 +71,22 @@ fun String?.isJsonArray(): Boolean = str.startsWith("[") && str.endsWith("]") } ?: false -fun String.splitNotBlank(vararg delimiter: String): Array = run { - this.split(*delimiter).map { it.trim() }.filterNot { it.isBlank() }.toTypedArray() +fun String?.isXml(): Boolean = + this?.run { + val str = this.trim() + str.startsWith("<") && str.endsWith(">") + } ?: false + +fun String?.isTrue(nullIsTrue: Boolean = false): Boolean { + if (this.isNullOrBlank() || this == "null") { + return nullIsTrue + } + return !this.trim().matches("(?i)^(false|no|not|0)$".toRegex()) +} + +fun String.splitNotBlank(vararg delimiter: String, limit: Int = 0): Array = run { + this.split(*delimiter, limit = limit).map { it.trim() }.filterNot { it.isBlank() } + .toTypedArray() } fun String.splitNotBlank(regex: Regex, limit: Int = 0): Array = run { @@ -97,3 +117,4 @@ fun String.toStringArray(): Array { } } +