Update On Sat Jan 3 19:40:33 CET 2026

This commit is contained in:
github-action[bot]
2026-01-03 19:40:34 +01:00
parent 7b95cb7ecd
commit 72b5fdaa97
74 changed files with 1491 additions and 619 deletions
+1
View File
@@ -1231,3 +1231,4 @@ Update On Tue Dec 30 19:43:26 CET 2025
Update On Wed Dec 31 19:39:51 CET 2025
Update On Thu Jan 1 19:42:21 CET 2026
Update On Fri Jan 2 19:40:09 CET 2026
Update On Sat Jan 3 19:40:25 CET 2026
+1 -1
View File
@@ -53,7 +53,7 @@ func main() {
df := func() {}
app := cli.NewApp()
app.Name = "Brook"
app.Version = "20250808"
app.Version = "20260101"
app.Usage = "A cross-platform programmable network tool"
app.Authors = []*cli.Author{
{
+2 -2
View File
@@ -2,7 +2,7 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.18",
"mihomo_alpha": "alpha-1f8bee9",
"mihomo_alpha": "alpha-4d76703",
"clash_rs": "v0.9.3",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.9.3-alpha+sha.626fbd4"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
}
},
"updated_at": "2025-12-31T22:21:37.371Z"
"updated_at": "2026-01-02T22:21:48.399Z"
}
+8
View File
@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
## [2.53.1](https://github.com/filebrowser/filebrowser/compare/v2.53.0...v2.53.1) (2026-01-03)
### Bug Fixes
* download path encoding file paths ([#5655](https://github.com/filebrowser/filebrowser/issues/5655)) ([ffa893e](https://github.com/filebrowser/filebrowser/commit/ffa893e9ac387a49dba5917a41df7c3b7ce120fc))
* request a password to change sensitive user data ([#5629](https://github.com/filebrowser/filebrowser/issues/5629)) ([b8151a0](https://github.com/filebrowser/filebrowser/commit/b8151a038a1ea55afae8073b439b74e364cac12f))
## [2.53.0](https://github.com/filebrowser/filebrowser/compare/v2.52.0...v2.53.0) (2025-12-29)
+17 -16
View File
@@ -6,22 +6,23 @@ import (
)
var (
ErrEmptyKey = errors.New("empty key")
ErrExist = errors.New("the resource already exists")
ErrNotExist = errors.New("the resource does not exist")
ErrEmptyPassword = errors.New("password is empty")
ErrEasyPassword = errors.New("password is too easy")
ErrEmptyUsername = errors.New("username is empty")
ErrEmptyRequest = errors.New("empty request")
ErrScopeIsRelative = errors.New("scope is a relative path")
ErrInvalidDataType = errors.New("invalid data type")
ErrIsDirectory = errors.New("file is directory")
ErrInvalidOption = errors.New("invalid option")
ErrInvalidAuthMethod = errors.New("invalid auth method")
ErrPermissionDenied = errors.New("permission denied")
ErrInvalidRequestParams = errors.New("invalid request params")
ErrSourceIsParent = errors.New("source is parent")
ErrRootUserDeletion = errors.New("user with id 1 can't be deleted")
ErrEmptyKey = errors.New("empty key")
ErrExist = errors.New("the resource already exists")
ErrNotExist = errors.New("the resource does not exist")
ErrEmptyPassword = errors.New("password is empty")
ErrEasyPassword = errors.New("password is too easy")
ErrEmptyUsername = errors.New("username is empty")
ErrEmptyRequest = errors.New("empty request")
ErrScopeIsRelative = errors.New("scope is a relative path")
ErrInvalidDataType = errors.New("invalid data type")
ErrIsDirectory = errors.New("file is directory")
ErrInvalidOption = errors.New("invalid option")
ErrInvalidAuthMethod = errors.New("invalid auth method")
ErrPermissionDenied = errors.New("permission denied")
ErrInvalidRequestParams = errors.New("invalid request params")
ErrSourceIsParent = errors.New("source is parent")
ErrRootUserDeletion = errors.New("user with id 1 can't be deleted")
ErrCurrentPasswordIncorrect = errors.New("the current password is incorrect")
)
type ErrShortPassword struct {
+1 -1
View File
@@ -71,5 +71,5 @@
"vite-plugin-compression2": "^2.3.1",
"vue-tsc": "^3.1.3"
},
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6"
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
}
+102 -91
View File
@@ -82,7 +82,7 @@ importers:
version: 4.5.5(@vueuse/core@14.1.0(vue@3.5.26(typescript@5.9.3)))(@vueuse/integrations@14.1.0(focus-trap@7.6.2)(jwt-decode@4.0.0)(vue@3.5.26(typescript@5.9.3)))(focus-trap@7.6.2)(vue@3.5.26(typescript@5.9.3))
vue-i18n:
specifier: ^11.1.10
version: 11.2.7(vue@3.5.26(typescript@5.9.3))
version: 11.2.8(vue@3.5.26(typescript@5.9.3))
vue-lazyload:
specifier: ^3.0.0
version: 3.0.0
@@ -98,7 +98,7 @@ importers:
devDependencies:
'@intlify/unplugin-vue-i18n':
specifier: ^11.0.1
version: 11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
version: 11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
'@tsconfig/node24':
specifier: ^24.0.2
version: 24.0.3
@@ -110,7 +110,7 @@ importers:
version: 24.10.4
'@typescript-eslint/eslint-plugin':
specifier: ^8.37.0
version: 8.50.1(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)
version: 8.51.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)
'@vitejs/plugin-legacy':
specifier: ^7.2.1
version: 7.2.1(terser@5.44.1)(vite@7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2))
@@ -983,6 +983,12 @@ packages:
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
'@eslint-community/eslint-utils@4.9.1':
resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
'@eslint-community/regexpp@4.12.2':
resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
@@ -1043,20 +1049,20 @@ packages:
vue-i18n:
optional: true
'@intlify/core-base@11.2.7':
resolution: {integrity: sha512-+Ra9I/LAzXDnmv/IrTO03WMCiLya7pHRmGJvNl9fKwx/W4REJ0xaMk2PxCRqnxcBsX443amEMdebQ3R1geiuIw==}
'@intlify/core-base@11.2.8':
resolution: {integrity: sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==}
engines: {node: '>= 16'}
'@intlify/message-compiler@11.2.7':
resolution: {integrity: sha512-TFamC+GzJAotAFwUNvbtRVBgvuSn2nCwKNresmPUHv3IIVMmXJt7QQJj/DORI1h8hs46ZF6L0Fs2xBohSOE4iQ==}
'@intlify/message-compiler@11.2.8':
resolution: {integrity: sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ==}
engines: {node: '>= 16'}
'@intlify/shared@11.2.2':
resolution: {integrity: sha512-OtCmyFpSXxNu/oET/aN6HtPCbZ01btXVd0f3w00YsHOb13Kverk1jzA2k47pAekM55qbUw421fvPF1yxZ+gicw==}
engines: {node: '>= 16'}
'@intlify/shared@11.2.7':
resolution: {integrity: sha512-uvlkvc/0uQ4FDlHQZccpUnmcOwNcaI3i+69ck2YJ+GqM35AoVbuS63b+YfirV4G0SZh64Ij2UMcFRMmB4nr95w==}
'@intlify/shared@11.2.8':
resolution: {integrity: sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==}
engines: {node: '>= 16'}
'@intlify/unplugin-vue-i18n@11.0.3':
@@ -1283,11 +1289,11 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/eslint-plugin@8.50.1':
resolution: {integrity: sha512-PKhLGDq3JAg0Jk/aK890knnqduuI/Qj+udH7wCf0217IGi4gt+acgCyPVe79qoT+qKUvHMDQkwJeKW9fwl8Cyw==}
'@typescript-eslint/eslint-plugin@8.51.0':
resolution: {integrity: sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.50.1
'@typescript-eslint/parser': ^8.51.0
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
@@ -1310,8 +1316,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/project-service@8.50.1':
resolution: {integrity: sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg==}
'@typescript-eslint/project-service@8.51.0':
resolution: {integrity: sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -1324,8 +1330,8 @@ packages:
resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/scope-manager@8.50.1':
resolution: {integrity: sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==}
'@typescript-eslint/scope-manager@8.51.0':
resolution: {integrity: sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.37.0':
@@ -1340,8 +1346,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/tsconfig-utils@8.50.1':
resolution: {integrity: sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw==}
'@typescript-eslint/tsconfig-utils@8.51.0':
resolution: {integrity: sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -1353,8 +1359,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/type-utils@8.50.1':
resolution: {integrity: sha512-7J3bf022QZE42tYMO6SL+6lTPKFk/WphhRPe9Tw/el+cEwzLz1Jjz2PX3GtGQVxooLDKeMVmMt7fWpYRdG5Etg==}
'@typescript-eslint/type-utils@8.51.0':
resolution: {integrity: sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -1368,8 +1374,8 @@ packages:
resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.50.1':
resolution: {integrity: sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==}
'@typescript-eslint/types@8.51.0':
resolution: {integrity: sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.37.0':
@@ -1384,8 +1390,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/typescript-estree@8.50.1':
resolution: {integrity: sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==}
'@typescript-eslint/typescript-estree@8.51.0':
resolution: {integrity: sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -1397,8 +1403,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/utils@8.50.1':
resolution: {integrity: sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ==}
'@typescript-eslint/utils@8.51.0':
resolution: {integrity: sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -1412,8 +1418,8 @@ packages:
resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/visitor-keys@8.50.1':
resolution: {integrity: sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==}
'@typescript-eslint/visitor-keys@8.51.0':
resolution: {integrity: sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@videojs/http-streaming@3.17.2':
@@ -2490,8 +2496,8 @@ packages:
systemjs@6.15.1:
resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==}
tabbable@6.3.0:
resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==}
tabbable@6.4.0:
resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==}
tar-mini@0.2.0:
resolution: {integrity: sha512-+qfUHz700DWnRutdUsxRRVZ38G1Qr27OetwaMYTdg8hcPxf46U0S1Zf76dQMWRBmusOt2ZCK5kbIaiLkoGO7WQ==}
@@ -2509,8 +2515,8 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
ts-api-utils@2.3.0:
resolution: {integrity: sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==}
ts-api-utils@2.4.0:
resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
engines: {node: '>=18.12'}
peerDependencies:
typescript: '>=4.8.4'
@@ -2669,8 +2675,8 @@ packages:
focus-trap: '>=7.2.0'
vue: '>=3.2.0'
vue-i18n@11.2.7:
resolution: {integrity: sha512-LPv8bAY5OA0UvFEXl4vBQOBqJzRrlExy92tWgRuwW7tbykHf7CH71G2Y4TM2OwGcIS4+hyqKHS2EVBqaYwPY9Q==}
vue-i18n@11.2.8:
resolution: {integrity: sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==}
engines: {node: '>= 16'}
peerDependencies:
vue: ^3.0.0
@@ -3558,6 +3564,11 @@ snapshots:
eslint: 9.39.2
eslint-visitor-keys: 3.4.3
'@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)':
dependencies:
eslint: 9.39.2
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
'@eslint/config-array@0.21.1':
@@ -3610,9 +3621,9 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
'@intlify/bundle-utils@11.0.3(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))':
'@intlify/bundle-utils@11.0.3(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))':
dependencies:
'@intlify/message-compiler': 11.2.7
'@intlify/message-compiler': 11.2.8
'@intlify/shared': 11.2.2
acorn: 8.15.0
esbuild: 0.25.12
@@ -3622,28 +3633,28 @@ snapshots:
source-map-js: 1.2.1
yaml-eslint-parser: 1.3.2
optionalDependencies:
vue-i18n: 11.2.7(vue@3.5.26(typescript@5.9.3))
vue-i18n: 11.2.8(vue@3.5.26(typescript@5.9.3))
'@intlify/core-base@11.2.7':
'@intlify/core-base@11.2.8':
dependencies:
'@intlify/message-compiler': 11.2.7
'@intlify/shared': 11.2.7
'@intlify/message-compiler': 11.2.8
'@intlify/shared': 11.2.8
'@intlify/message-compiler@11.2.7':
'@intlify/message-compiler@11.2.8':
dependencies:
'@intlify/shared': 11.2.7
'@intlify/shared': 11.2.8
source-map-js: 1.2.1
'@intlify/shared@11.2.2': {}
'@intlify/shared@11.2.7': {}
'@intlify/shared@11.2.8': {}
'@intlify/unplugin-vue-i18n@11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
'@intlify/unplugin-vue-i18n@11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2)
'@intlify/bundle-utils': 11.0.3(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))
'@intlify/bundle-utils': 11.0.3(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))
'@intlify/shared': 11.2.2
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
'@rollup/pluginutils': 5.3.0(rollup@4.54.0)
'@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
@@ -3654,7 +3665,7 @@ snapshots:
unplugin: 2.3.11
vue: 3.5.26(typescript@5.9.3)
optionalDependencies:
vue-i18n: 11.2.7(vue@3.5.26(typescript@5.9.3))
vue-i18n: 11.2.8(vue@3.5.26(typescript@5.9.3))
transitivePeerDependencies:
- '@vue/compiler-dom'
- eslint
@@ -3662,14 +3673,14 @@ snapshots:
- supports-color
- typescript
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
dependencies:
'@babel/parser': 7.28.5
optionalDependencies:
'@intlify/shared': 11.2.2
'@vue/compiler-dom': 3.5.26
vue: 3.5.26(typescript@5.9.3)
vue-i18n: 11.2.7(vue@3.5.26(typescript@5.9.3))
vue-i18n: 11.2.8(vue@3.5.26(typescript@5.9.3))
'@jridgewell/gen-mapping@0.3.13':
dependencies:
@@ -3822,23 +3833,23 @@ snapshots:
graphemer: 1.4.0
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.3.0(typescript@5.9.3)
ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/eslint-plugin@8.50.1(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/eslint-plugin@8.51.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.37.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.50.1
'@typescript-eslint/type-utils': 8.50.1(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/utils': 8.50.1(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.50.1
'@typescript-eslint/scope-manager': 8.51.0
'@typescript-eslint/type-utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.51.0
eslint: 9.39.2
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.3.0(typescript@5.9.3)
ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -3873,10 +3884,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.50.1(typescript@5.9.3)':
'@typescript-eslint/project-service@8.51.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3)
'@typescript-eslint/types': 8.50.1
'@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3)
'@typescript-eslint/types': 8.51.0
debug: 4.4.3
typescript: 5.9.3
transitivePeerDependencies:
@@ -3892,10 +3903,10 @@ snapshots:
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/visitor-keys': 8.49.0
'@typescript-eslint/scope-manager@8.50.1':
'@typescript-eslint/scope-manager@8.51.0':
dependencies:
'@typescript-eslint/types': 8.50.1
'@typescript-eslint/visitor-keys': 8.50.1
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/visitor-keys': 8.51.0
'@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.9.3)':
dependencies:
@@ -3905,7 +3916,7 @@ snapshots:
dependencies:
typescript: 5.9.3
'@typescript-eslint/tsconfig-utils@8.50.1(typescript@5.9.3)':
'@typescript-eslint/tsconfig-utils@8.51.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
@@ -3916,19 +3927,19 @@ snapshots:
'@typescript-eslint/utils': 8.37.0(eslint@9.39.2)(typescript@5.9.3)
debug: 4.4.3
eslint: 9.39.2
ts-api-utils: 2.3.0(typescript@5.9.3)
ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/type-utils@8.50.1(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/type-utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.50.1
'@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3)
'@typescript-eslint/utils': 8.50.1(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3)
debug: 4.4.3
eslint: 9.39.2
ts-api-utils: 2.3.0(typescript@5.9.3)
ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -3937,7 +3948,7 @@ snapshots:
'@typescript-eslint/types@8.49.0': {}
'@typescript-eslint/types@8.50.1': {}
'@typescript-eslint/types@8.51.0': {}
'@typescript-eslint/typescript-estree@8.37.0(typescript@5.9.3)':
dependencies:
@@ -3950,7 +3961,7 @@ snapshots:
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.7.3
ts-api-utils: 2.3.0(typescript@5.9.3)
ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -3965,22 +3976,22 @@ snapshots:
minimatch: 9.0.5
semver: 7.7.3
tinyglobby: 0.2.15
ts-api-utils: 2.3.0(typescript@5.9.3)
ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/typescript-estree@8.50.1(typescript@5.9.3)':
'@typescript-eslint/typescript-estree@8.51.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/project-service': 8.50.1(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3)
'@typescript-eslint/types': 8.50.1
'@typescript-eslint/visitor-keys': 8.50.1
'@typescript-eslint/project-service': 8.51.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3)
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/visitor-keys': 8.51.0
debug: 4.4.3
minimatch: 9.0.5
semver: 7.7.3
tinyglobby: 0.2.15
ts-api-utils: 2.3.0(typescript@5.9.3)
ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -3996,12 +4007,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.50.1(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2)
'@typescript-eslint/scope-manager': 8.50.1
'@typescript-eslint/types': 8.50.1
'@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3)
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2)
'@typescript-eslint/scope-manager': 8.51.0
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3)
eslint: 9.39.2
typescript: 5.9.3
transitivePeerDependencies:
@@ -4017,9 +4028,9 @@ snapshots:
'@typescript-eslint/types': 8.49.0
eslint-visitor-keys: 4.2.1
'@typescript-eslint/visitor-keys@8.50.1':
'@typescript-eslint/visitor-keys@8.51.0':
dependencies:
'@typescript-eslint/types': 8.50.1
'@typescript-eslint/types': 8.51.0
eslint-visitor-keys: 4.2.1
'@videojs/http-streaming@3.17.2(video.js@8.23.4)':
@@ -4670,7 +4681,7 @@ snapshots:
focus-trap@7.6.2:
dependencies:
tabbable: 6.3.0
tabbable: 6.4.0
fraction.js@5.3.4: {}
@@ -5138,7 +5149,7 @@ snapshots:
systemjs@6.15.1: {}
tabbable@6.3.0: {}
tabbable@6.4.0: {}
tar-mini@0.2.0: {}
@@ -5158,7 +5169,7 @@ snapshots:
dependencies:
is-number: 7.0.0
ts-api-utils@2.3.0(typescript@5.9.3):
ts-api-utils@2.4.0(typescript@5.9.3):
dependencies:
typescript: 5.9.3
@@ -5313,10 +5324,10 @@ snapshots:
focus-trap: 7.6.2
vue: 3.5.26(typescript@5.9.3)
vue-i18n@11.2.7(vue@3.5.26(typescript@5.9.3)):
vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)):
dependencies:
'@intlify/core-base': 11.2.7
'@intlify/shared': 11.2.7
'@intlify/core-base': 11.2.8
'@intlify/shared': 11.2.8
'@vue/devtools-api': 6.6.4
vue: 3.5.26(typescript@5.9.3)
+2 -2
View File
@@ -41,12 +41,12 @@ export function download(
let url = `${baseURL}/api/public/dl/${hash}`;
if (files.length === 1) {
url += encodeURIComponent(files[0]) + "?";
url += files[0] + "?";
} else {
let arg = "";
for (const file of files) {
arg += encodeURIComponent(file) + ",";
arg += file + ",";
}
arg = arg.substring(0, arg.length - 1);
+8 -2
View File
@@ -8,12 +8,13 @@ export async function get(id: number) {
return fetchJSON<IUser>(`/api/users/${id}`, {});
}
export async function create(user: IUser) {
export async function create(user: IUser, currentPassword: string) {
const res = await fetchURL(`/api/users`, {
method: "POST",
body: JSON.stringify({
what: "user",
which: [],
current_password: currentPassword,
data: user,
}),
});
@@ -25,12 +26,17 @@ export async function create(user: IUser) {
throw new StatusError(await res.text(), res.status);
}
export async function update(user: Partial<IUser>, which = ["all"]) {
export async function update(
user: Partial<IUser>,
which = ["all"],
currentPassword: string | null = null
) {
await fetchURL(`/api/users/${user.id}`, {
method: "PUT",
body: JSON.stringify({
what: "user",
which: which,
...(currentPassword != null ? { current_password: currentPassword } : {}),
data: user,
}),
});
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "User Management",
"userUpdated": "User updated!",
"username": "Username",
"users": "Users"
"users": "Users",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Help",
+1
View File
@@ -5,6 +5,7 @@ interface ISettings {
minimumPasswordLength: number;
userHomeBasePath: string;
defaults: SettingsDefaults;
authMethod: string;
rules: any[];
branding: SettingsBranding;
tus: SettingsTus;
@@ -69,6 +69,15 @@
v-model="passwordConf"
name="passwordConf"
/>
<input
v-if="isCurrentPasswordRequired"
:class="passwordClass"
type="password"
:placeholder="t('settings.currentPassword')"
v-model="currentPassword"
name="current_password"
autocomplete="current-password"
/>
</div>
<div class="card-action">
@@ -87,7 +96,7 @@
<script setup lang="ts">
import { useAuthStore } from "@/stores/auth";
import { useLayoutStore } from "@/stores/layout";
import { users as api } from "@/api";
import { users as api, settings } from "@/api";
import AceEditorTheme from "@/components/settings/AceEditorTheme.vue";
import Languages from "@/components/settings/Languages.vue";
import { computed, inject, onMounted, ref } from "vue";
@@ -102,6 +111,8 @@ const $showError = inject<IToastError>("$showError")!;
const password = ref<string>("");
const passwordConf = ref<string>("");
const currentPassword = ref<string>("");
const isCurrentPasswordRequired = ref<boolean>(false);
const hideDotfiles = ref<boolean>(false);
const singleClick = ref<boolean>(false);
const dateFormat = ref<boolean>(false);
@@ -131,6 +142,9 @@ onMounted(async () => {
dateFormat.value = authStore.user.dateFormat;
aceEditorTheme.value = authStore.user.aceEditorTheme;
layoutStore.loading = false;
const { authMethod } = await settings.get();
isCurrentPasswordRequired.value = authMethod == "json";
return true;
});
@@ -140,6 +154,7 @@ const updatePassword = async (event: Event) => {
if (
password.value !== passwordConf.value ||
password.value === "" ||
currentPassword.value === "" ||
authStore.user === null
) {
return;
@@ -151,7 +166,7 @@ const updatePassword = async (event: Event) => {
id: authStore.user.id,
password: password.value,
};
await api.update(data, ["password"]);
await api.update(data, ["password"], currentPassword.value);
authStore.updateUser(data);
$showSuccess(t("settings.passwordUpdated"));
} catch (e: any) {
@@ -15,6 +15,19 @@
:isDefault="false"
:isNew="isNew"
/>
<p v-if="isCurrentPasswordRequired">
<label for="currentPassword">{{
t("settings.currentPassword")
}}</label>
<input
class="input input--block"
type="password"
v-model="currentPassword"
id="currentPassword"
autocomplete="current-password"
/>
</p>
</div>
<div class="card-action">
@@ -63,6 +76,8 @@ const error = ref<StatusError>();
const originalUser = ref<IUser>();
const user = ref<IUser>();
const createUserDir = ref<boolean>(false);
const currentPassword = ref<string>("");
const isCurrentPasswordRequired = ref<boolean>(false);
const $showError = inject<IToastError>("$showError")!;
const $showSuccess = inject<IToastSuccess>("$showSuccess")!;
@@ -90,7 +105,12 @@ const fetchData = async () => {
try {
if (isNew.value) {
const { defaults, createUserDir: _createUserDir } = await settings.get();
const {
authMethod,
defaults,
createUserDir: _createUserDir,
} = await settings.get();
isCurrentPasswordRequired.value = authMethod == "json";
createUserDir.value = _createUserDir;
user.value = {
...defaults,
@@ -101,6 +121,8 @@ const fetchData = async () => {
id: 0,
};
} else {
const { authMethod } = await settings.get();
isCurrentPasswordRequired.value = authMethod == "json";
const id = Array.isArray(route.params.id)
? route.params.id.join("")
: route.params.id;
@@ -151,11 +173,11 @@ const save = async (event: Event) => {
...user.value,
};
const loc = await api.create(newUser);
const loc = await api.create(newUser, currentPassword.value);
router.push({ path: loc || "/settings/users" });
$showSuccess(t("settings.userCreated"));
} else {
await api.update(user.value);
await api.update(user.value, ["all"], currentPassword.value);
if (user.value.id === authStore.user?.id) {
authStore.updateUser(user.value);
+1 -1
View File
@@ -17,7 +17,7 @@ require (
github.com/mholt/archives v0.1.5
github.com/mitchellh/go-homedir v1.1.0
github.com/samber/lo v1.52.0
github.com/shirou/gopsutil/v4 v4.25.11
github.com/shirou/gopsutil/v4 v4.25.12
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
+2 -2
View File
@@ -204,8 +204,8 @@ github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDc
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/shirou/gopsutil/v4 v4.25.11 h1:X53gB7muL9Gnwwo2evPSE+SfOrltMoR6V3xJAXZILTY=
github.com/shirou/gopsutil/v4 v4.25.11/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
+3 -2
View File
@@ -11,8 +11,9 @@ import (
)
type modifyRequest struct {
What string `json:"what"` // Answer to: what data type?
Which []string `json:"which"` // Answer to: which fields?
What string `json:"what"` // Answer to: what data type?
Which []string `json:"which"` // Answer to: which fields?
CurrentPassword string `json:"current_password"` // Answer to: user logged password
}
func NewHandler(
+2
View File
@@ -15,6 +15,7 @@ type settingsData struct {
MinimumPasswordLength uint `json:"minimumPasswordLength"`
UserHomeBasePath string `json:"userHomeBasePath"`
Defaults settings.UserDefaults `json:"defaults"`
AuthMethod settings.AuthMethod `json:"authMethod"`
Rules []rules.Rule `json:"rules"`
Branding settings.Branding `json:"branding"`
Tus settings.Tus `json:"tus"`
@@ -30,6 +31,7 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
MinimumPasswordLength: d.settings.MinimumPasswordLength,
UserHomeBasePath: d.settings.UserHomeBasePath,
Defaults: d.settings.Defaults,
AuthMethod: d.settings.AuthMethod,
Rules: d.settings.Rules,
Branding: d.settings.Branding,
Tus: d.settings.Tus,
+28
View File
@@ -12,6 +12,7 @@ import (
"golang.org/x/text/cases"
"golang.org/x/text/language"
"github.com/filebrowser/filebrowser/v2/auth"
fberrors "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/users"
)
@@ -117,6 +118,12 @@ var userPostHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
return http.StatusBadRequest, err
}
if d.settings.AuthMethod == auth.MethodJSONAuth {
if !users.CheckPwd(req.CurrentPassword, d.user.Password) {
return http.StatusBadRequest, fberrors.ErrCurrentPasswordIncorrect
}
}
if len(req.Which) != 0 {
return http.StatusBadRequest, nil
}
@@ -153,6 +160,27 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
return http.StatusBadRequest, err
}
if d.settings.AuthMethod == auth.MethodJSONAuth {
var sensibleFields = map[string]struct{}{
"all": {},
"username": {},
"password": {},
"scope": {},
"lockPassword": {},
"commands": {},
"perm": {},
}
for _, field := range req.Which {
if _, ok := sensibleFields[field]; ok {
if !users.CheckPwd(req.CurrentPassword, d.user.Password) {
return http.StatusBadRequest, fberrors.ErrCurrentPasswordIncorrect
}
break
}
}
}
if req.Data.ID != d.raw.(uint) {
return http.StatusBadRequest, nil
}
@@ -10,7 +10,7 @@ include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=linux-atm
PKG_VERSION:=2.5.2
PKG_RELEASE:=7
PKG_RELEASE:=8
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=@SF/$(PKG_NAME)
@@ -21,6 +21,7 @@ PKG_BUILD_PARALLEL:=1
PKG_LICENSE:=GPL-2.0+
PKG_CPE_ID:=cpe:/a:linux-atm:linux-atm
PKG_FIXUP:=autoreconf
PKG_FLAGS:=nonshared
include $(INCLUDE_DIR)/package.mk
@@ -98,6 +99,8 @@ endef
$(foreach t,$(ATM_DEBUG_TOOLS),$(eval $(call GenAtmPlugin,atm-$(t),$(t))))
TARGET_CFLAGS += -I$(LINUX_DIR)/user_headers/include
define Build/Configure
$(call Build/Configure/Default)
# prevent autoheader invocation
@@ -1,3 +1,45 @@
--- a/src/mpoad/mpcd.8
+++ b/src/mpoad/mpcd.8
@@ -28,7 +28,7 @@ mpcd \- ATM MPOA (Multi\-Protocol Over A
.B ]]
.SH DESCRIPTION
MPOA client
-.SM(MPC) is responsible for creating and receiving
+.SM (MPC) is responsible for creating and receiving
internetwork layer shortcuts. Using these shortcuts MPCs forward
unicast internetwork layer packets effectively over ATM without need
for routing protocols.
@@ -43,7 +43,7 @@ accepts shortcuts and packets arriving o
shortcuts is done with the help of
.SM MPOA
server
-.SM(MPS).
+.SM (MPS).
.PP
Just as the Linux
.SM LAN
--- a/src/led/zeppelin.8
+++ b/src/led/zeppelin.8
@@ -99,7 +99,7 @@ Ring and ATM parts of the ELAN, so using
recommended. Token Ring support has received less testing than its
Ethernet counterpart.
.SH FILES
-.IP \fI/var/run/lec[interface number].pid\fP
+.IP \fI/var/run/lec[interface\ number].pid\fP
The file containing the process id of zeppelin.
.SH BUGS
John Bonham died 1980 and Led Zeppelin broke.
--- a/src/sigd/atmsigd.conf.4
+++ b/src/sigd/atmsigd.conf.4
@@ -125,7 +125,7 @@ a comment. The `#' character cannot be e
.P
If an option is specified in \fBatmsigd.conf\fP and on the command
line, the command line has priority.
-.COMPATIBILITY
+.SH COMPATIBILITY
Certain options used by past versions of \fBatmsigd\fP but no longer documented
on the man page are still recognized and supported, but they also yield a
warning message. Future versions of \fBatmsigd\fP will not recognize those
--- a/src/arpd/io.c
+++ b/src/arpd/io.c
@@ -277,7 +277,8 @@ static void accept_new(void)
@@ -226,45 +268,52 @@
if (trans) {
/* set send socket buffer if we are transmitting */
--- a/src/mpoad/mpcd.8
+++ b/src/mpoad/mpcd.8
@@ -28,7 +28,7 @@ mpcd \- ATM MPOA (Multi\-Protocol Over A
.B ]]
.SH DESCRIPTION
MPOA client
-.SM(MPC) is responsible for creating and receiving
+.SM (MPC) is responsible for creating and receiving
internetwork layer shortcuts. Using these shortcuts MPCs forward
unicast internetwork layer packets effectively over ATM without need
for routing protocols.
@@ -43,7 +43,7 @@ accepts shortcuts and packets arriving o
shortcuts is done with the help of
.SM MPOA
server
-.SM(MPS).
+.SM (MPS).
.PP
Just as the Linux
.SM LAN
--- a/src/led/zeppelin.8
+++ b/src/led/zeppelin.8
@@ -99,7 +99,7 @@ Ring and ATM parts of the ELAN, so using
recommended. Token Ring support has received less testing than its
Ethernet counterpart.
.SH FILES
-.IP \fI/var/run/lec[interface number].pid\fP
+.IP \fI/var/run/lec[interface\ number].pid\fP
The file containing the process id of zeppelin.
.SH BUGS
John Bonham died 1980 and Led Zeppelin broke.
--- a/src/sigd/atmsigd.conf.4
+++ b/src/sigd/atmsigd.conf.4
@@ -125,7 +125,7 @@ a comment. The `#' character cannot be e
.P
If an option is specified in \fBatmsigd.conf\fP and on the command
line, the command line has priority.
-.COMPATIBILITY
+.SH COMPATIBILITY
Certain options used by past versions of \fBatmsigd\fP but no longer documented
on the man page are still recognized and supported, but they also yield a
warning message. Future versions of \fBatmsigd\fP will not recognize those
@@ -663,7 +664,7 @@ int no_check = 0;
exit(0);
usage:
- fprintf(stderr, Usage);
+ fprintf(stderr, "%s", Usage);
exit(1);
}
--- a/src/arpd/arp.c
+++ b/src/arpd/arp.c
@@ -17,6 +17,7 @@
#include <netinet/in.h> /* for ntohs, etc. */
#define _LINUX_NETDEVICE_H /* very crude hack for glibc2 */
#include <linux/types.h>
+#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <atm.h>
--- a/src/arpd/itf.c
+++ b/src/arpd/itf.c
@@ -14,6 +14,7 @@
#include <sys/socket.h>
#define _LINUX_NETDEVICE_H /* glibc2 */
#include <linux/types.h>
+#include <linux/if.h>
#include <linux/if_arp.h>
#include "atmd.h"
--- a/src/maint/atmdump.c
+++ b/src/maint/atmdump.c
@@ -14,6 +14,7 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
+#include <linux/sockios.h>
#include <netinet/in.h> /* for htonl and ntohl */
#include <atm.h>
--- a/src/maint/saaldump.c
+++ b/src/maint/saaldump.c
@@ -15,6 +15,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <linux/sockios.h>
#include <atm.h>
#include "pdu.h"
@@ -28,8 +28,8 @@ in Linux 4.20.
#include <sys/socket.h>
-#define _LINUX_NETDEVICE_H /* glibc2 */
#include <linux/types.h>
#include <linux/if.h>
#include <linux/if_arp.h>
--- a/src/arpd/io.c
+++ b/src/arpd/io.c
@@ -21,7 +21,6 @@
@@ -48,5 +48,5 @@ in Linux 4.20.
#include <netinet/in.h> /* for ntohs, etc. */
-#define _LINUX_NETDEVICE_H /* very crude hack for glibc2 */
#include <linux/types.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
@@ -1,11 +0,0 @@
--- a/src/test/ttcp.c
+++ b/src/test/ttcp.c
@@ -664,7 +664,7 @@ int no_check = 0;
exit(0);
usage:
- fprintf(stderr, Usage);
+ fprintf(stderr, "%s", Usage);
exit(1);
}
@@ -0,0 +1,82 @@
--- a/src/arpd/io.c
+++ b/src/arpd/io.c
@@ -615,7 +615,7 @@ int ip_itf_info(int number,uint32_t *ip,
int get_local(int fd,struct sockaddr_atmsvc *addr)
{
int result;
- size_t length;
+ socklen_t length;
length = sizeof(struct sockaddr_atmsvc);
result = getsockname(fd,(struct sockaddr *) addr,&length);
--- a/src/led/conn.c
+++ b/src/led/conn.c
@@ -405,7 +405,7 @@ Conn_t *accept_conn(Conn_t *conn)
{
Conn_t *new;
struct sockaddr_atmsvc addr;
- size_t len;
+ socklen_t len;
int fd;
char buff[MAX_ATM_ADDR_LEN+1];
@@ -538,7 +538,7 @@ static int handle_accept(Conn_t *conn)
*/
static int handle_data(Conn_t *conn)
{
- char buff[MAX_CTRL_FRAME];
+ unsigned char buff[MAX_CTRL_FRAME];
int retval;
retval = recv_frame(conn, buff, sizeof(buff));
--- a/src/led/frames.c
+++ b/src/led/frames.c
@@ -312,7 +312,7 @@ static void handle_ready_ind(Conn_t *con
* dependant handler functions.
* Returns < 0 for serious error
*/
-int handle_frame(Conn_t *conn, char *buff, int size)
+int handle_frame(Conn_t *conn, unsigned char *buff, int size)
{
struct ctrl_frame *frame;
--- a/src/led/frames.h
+++ b/src/led/frames.h
@@ -13,7 +13,7 @@ int validate_frame(unsigned char *buff,
void send_ready_ind(Conn_t *conn);
void send_register_req(void);
-int handle_frame(Conn_t *conn, char *buff, int size);
+int handle_frame(Conn_t *conn, unsigned char *buff, int size);
uint32_t send_flush_req(Conn_t *conn);
void parse_tlvs(uint16_t opcode, unsigned char *tlvp, int numtlvs, int sizeoftlvs);
--- a/src/led/join.c
+++ b/src/led/join.c
@@ -43,7 +43,7 @@ static int read_join_rsp(char *buff, int
static int parse_join_rsp(unsigned char *buff, int size);
static int get_bus_addr(struct sockaddr_atmsvc *addr);
-static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, char *buff, int buffsize);
+static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, unsigned char *buff, int buffsize);
/*
* 5.1, Initial state
@@ -693,7 +693,7 @@ static int get_bus_addr(struct sockaddr_
fd_set rfds;
struct timeval tv;
int n = 0, retval, timeout;
- char buff[MAX_CTRL_FRAME];
+ unsigned char buff[MAX_CTRL_FRAME];
timeout = 4; /* wait response for 4 seconds */
lec_params.c7c_current_timeout = 1;
@@ -740,7 +740,7 @@ static int get_bus_addr(struct sockaddr_
* Tries to read BUS ATM address in *addr
* returns < 0 for error, 0 for not found > 0 for success
*/
-static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, char *buff, int buffsize)
+static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, unsigned char *buff, int buffsize)
{
int frame_size;
struct ctrl_frame *frame;
@@ -0,0 +1,40 @@
--- a/src/lane/load.c
+++ b/src/lane/load.c
@@ -498,8 +498,8 @@ load_vars(const char *file)
break;
case BOOLEAN:
Debug_unit(&load_unit, "Variable is boolean: %s",
- g_return.bool==BL_TRUE?"True":"False");
- set_var_bool(curr_unit, varname, g_return.bool);
+ g_return.boolean==BL_TRUE?"True":"False");
+ set_var_bool(curr_unit, varname, g_return.boolean);
break;
case INTEGER:
Debug_unit(&load_unit, "Variable is integer: %d", g_return.intti);
--- a/src/lane/load_lex.h
+++ b/src/lane/load_lex.h
@@ -24,7 +24,7 @@
#define END 0
typedef struct {
- Bool_t bool;
+ Bool_t boolean;
int intti;
AtmAddr_t *atmaddress;
LaneDestination_t *destaddr;
--- a/src/lane/load_lex.l
+++ b/src/lane/load_lex.l
@@ -44,11 +44,11 @@ H [0-9a-fA-F]
return ATMADDRESS;
}
True |
-true {g_return.bool = BL_TRUE;
+true {g_return.boolean = BL_TRUE;
return BOOLEAN;
}
False |
-false {g_return.bool = BL_FALSE;
+false {g_return.boolean = BL_FALSE;
return BOOLEAN;
}
\#.* {}
@@ -1,21 +0,0 @@
--- a/src/maint/saaldump.c 2020-03-29 22:58:01.089711789 +0200
+++ b/src/maint/saaldump.c 2020-03-29 22:59:17.564639387 +0200
@@ -6,6 +6,7 @@
#include <config.h>
#endif
+#include <linux/sockios.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
--- a/src/maint/atmdump.c 2020-03-29 22:58:18.573694469 +0200
+++ b/src/maint/atmdump.c 2020-03-29 22:58:49.956729365 +0200
@@ -6,6 +6,7 @@
#include <config.h>
#endif
+#include <linux/sockios.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
@@ -1,17 +0,0 @@
--- a/src/maint/Makefile.am
+++ b/src/maint/Makefile.am
@@ -1,5 +1,5 @@
BOOTPGMS=atmaddr esi
-SYSPGMS=atmloop atmtcp enitune zntune hediag # nstune
+SYSPGMS=atmloop atmtcp enitune hediag # nstune
USRPGMS=atmdiag atmdump sonetdiag saaldump
INCLUDES=-I$(srcdir)/../q2931 -I$(srcdir)/../saal -I.
@@ -14,7 +14,6 @@ esi_SOURCES = esi.c
atmloop_SOURCES = atmloop.c
atmtcp_SOURCES = atmtcp.c
enitune_SOURCES = enitune.c
-zntune_SOURCES = zntune.c
#nstune_SOURCES = nstune.c
atmdiag_SOURCES = atmdiag.c
+1 -1
View File
@@ -32,7 +32,7 @@ PROJECT_NAME=$(shell basename "${ROOT}")
# - pkg/version/current.go
#
# Use `tools/bump_version.sh` script to change all those files at one shot.
VERSION="3.26.0"
VERSION="3.26.1"
# With .ONESHELL, each recipe is executed in a single shell instance.
# This allows `cd` to affect subsequent commands in the same recipe.
@@ -1,5 +1,5 @@
Package: mieru
Version: 3.26.0
Version: 3.26.1
Section: net
Priority: optional
Architecture: amd64
@@ -1,5 +1,5 @@
Name: mieru
Version: 3.26.0
Version: 3.26.1
Release: 1%{?dist}
Summary: Mieru proxy client
License: GPLv3+
@@ -1,5 +1,5 @@
Package: mieru
Version: 3.26.0
Version: 3.26.1
Section: net
Priority: optional
Architecture: arm64
@@ -1,5 +1,5 @@
Name: mieru
Version: 3.26.0
Version: 3.26.1
Release: 1%{?dist}
Summary: Mieru proxy client
License: GPLv3+
@@ -1,5 +1,5 @@
Package: mita
Version: 3.26.0
Version: 3.26.1
Section: net
Priority: optional
Architecture: amd64
+1 -1
View File
@@ -1,5 +1,5 @@
Name: mita
Version: 3.26.0
Version: 3.26.1
Release: 1%{?dist}
Summary: Mieru proxy server
License: GPLv3+
@@ -1,5 +1,5 @@
Package: mita
Version: 3.26.0
Version: 3.26.1
Section: net
Priority: optional
Architecture: arm64
+1 -1
View File
@@ -1,5 +1,5 @@
Name: mita
Version: 3.26.0
Version: 3.26.1
Release: 1%{?dist}
Summary: Mieru proxy server
License: GPLv3+
+8 -8
View File
@@ -18,32 +18,32 @@ Or you can manually install and configure proxy server using the steps below.
```sh
# Debian / Ubuntu - X86_64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita_3.26.0_amd64.deb
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita_3.26.1_amd64.deb
# Debian / Ubuntu - ARM 64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita_3.26.0_arm64.deb
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita_3.26.1_arm64.deb
# RedHat / CentOS / Rocky Linux - X86_64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita-3.26.0-1.x86_64.rpm
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita-3.26.1-1.x86_64.rpm
# RedHat / CentOS / Rocky Linux - ARM 64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita-3.26.0-1.aarch64.rpm
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita-3.26.1-1.aarch64.rpm
```
## Install mita package
```sh
# Debian / Ubuntu - X86_64
sudo dpkg -i mita_3.26.0_amd64.deb
sudo dpkg -i mita_3.26.1_amd64.deb
# Debian / Ubuntu - ARM 64
sudo dpkg -i mita_3.26.0_arm64.deb
sudo dpkg -i mita_3.26.1_arm64.deb
# RedHat / CentOS / Rocky Linux - X86_64
sudo rpm -Uvh --force mita-3.26.0-1.x86_64.rpm
sudo rpm -Uvh --force mita-3.26.1-1.x86_64.rpm
# RedHat / CentOS / Rocky Linux - ARM 64
sudo rpm -Uvh --force mita-3.26.0-1.aarch64.rpm
sudo rpm -Uvh --force mita-3.26.1-1.aarch64.rpm
```
Those instructions can also be used to upgrade the version of mita software package.
+8 -8
View File
@@ -18,32 +18,32 @@ sudo python3 setup.py --lang=zh
```sh
# Debian / Ubuntu - X86_64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita_3.26.0_amd64.deb
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita_3.26.1_amd64.deb
# Debian / Ubuntu - ARM 64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita_3.26.0_arm64.deb
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita_3.26.1_arm64.deb
# RedHat / CentOS / Rocky Linux - X86_64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita-3.26.0-1.x86_64.rpm
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita-3.26.1-1.x86_64.rpm
# RedHat / CentOS / Rocky Linux - ARM 64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.0/mita-3.26.0-1.aarch64.rpm
curl -LSO https://github.com/enfein/mieru/releases/download/v3.26.1/mita-3.26.1-1.aarch64.rpm
```
## 安装 mita 软件包
```sh
# Debian / Ubuntu - X86_64
sudo dpkg -i mita_3.26.0_amd64.deb
sudo dpkg -i mita_3.26.1_amd64.deb
# Debian / Ubuntu - ARM 64
sudo dpkg -i mita_3.26.0_arm64.deb
sudo dpkg -i mita_3.26.1_arm64.deb
# RedHat / CentOS / Rocky Linux - X86_64
sudo rpm -Uvh --force mita-3.26.0-1.x86_64.rpm
sudo rpm -Uvh --force mita-3.26.1-1.x86_64.rpm
# RedHat / CentOS / Rocky Linux - ARM 64
sudo rpm -Uvh --force mita-3.26.0-1.aarch64.rpm
sudo rpm -Uvh --force mita-3.26.1-1.aarch64.rpm
```
上述指令也可以用来升级 mita 软件包的版本。
+1 -1
View File
@@ -16,5 +16,5 @@
package version
const (
AppVersion = "3.26.0"
AppVersion = "3.26.1"
)
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=filebrowser
PKG_VERSION:=2.53.0
PKG_VERSION:=2.53.1
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/filebrowser/filebrowser/tar.gz/v${PKG_VERSION}?
PKG_HASH:=7abd0260e8a5fd1fe29477939e2613b0b9164b55b1706d3cc2682048354b38d1
PKG_HASH:=68f55a90cf25c4e147dc50d45de2a619f4b24e9c2f6fa7c7de05130ca0b4e123
PKG_LICENSE:=Apache-2.0
PKG_LICENSE_FILES:=LICENSE
@@ -869,23 +869,29 @@ function gen_config(var)
fallbackTag = fallback_node_tag,
strategy = strategy
})
if _node.balancingStrategy == "leastPing" and not observatory then
observatory = {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
enableConcurrency = true
}
elseif _node.balancingStrategy == "leastLoad" and not burstObservatory then
burstObservatory = {
subjectSelector = { "blc-" },
pingConfig = {
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
sampling = 3,
timeout = "5s"
}
}
if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag then
if _node.balancingStrategy == "leastLoad" then
if not burstObservatory then
burstObservatory = {
subjectSelector = { "blc-" },
pingConfig = {
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
sampling = 3,
timeout = "5s"
}
}
end
else
if not observatory then
observatory = {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
enableConcurrency = true
}
end
end
end
local inbound_tag = gen_loopback(loopback_tag, loopback_dst)
table.insert(rules, { inboundTag = { inbound_tag }, balancerTag = balancer_tag })
+18 -18
View File
@@ -140,7 +140,7 @@ version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.60.2",
"windows-sys 0.61.2",
]
[[package]]
@@ -151,7 +151,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.60.2",
"windows-sys 0.61.2",
]
[[package]]
@@ -545,18 +545,18 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.53"
version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.53"
version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
dependencies = [
"anstream",
"anstyle",
@@ -931,7 +931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -1436,7 +1436,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots 1.0.4",
"webpki-roots 1.0.5",
]
[[package]]
@@ -1806,9 +1806,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.178"
version = "0.2.179"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
[[package]]
name = "libloading"
@@ -2500,7 +2500,7 @@ dependencies = [
"once_cell",
"socket2 0.5.10",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -2655,7 +2655,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots 1.0.4",
"webpki-roots 1.0.5",
]
[[package]]
@@ -2767,7 +2767,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3172,7 +3172,7 @@ dependencies = [
"tokio-rustls",
"trait-variant",
"tun",
"webpki-roots 1.0.4",
"webpki-roots 1.0.5",
"windows-sys 0.61.2",
"zstd",
]
@@ -3442,7 +3442,7 @@ dependencies = [
"getrandom 0.3.4",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -4030,14 +4030,14 @@ version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.4",
"webpki-roots 1.0.5",
]
[[package]]
name = "webpki-roots"
version = "1.0.4"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c"
dependencies = [
"rustls-pki-types",
]
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=brook
PKG_VERSION:=20260101
PKG_VERSION:=20260101.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=70e8310f31cef3b80e1696f364f12b2ab2aa0f1fb9fde00f25bda7620c21f096
PKG_HASH:=8ddba4ed9ae9d10928e169f8121c6791e0e3c2907fa27d6e0055fe434f6e700e
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
PKG_LICENSE:=GPL-3.0
@@ -869,23 +869,29 @@ function gen_config(var)
fallbackTag = fallback_node_tag,
strategy = strategy
})
if _node.balancingStrategy == "leastPing" and not observatory then
observatory = {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
enableConcurrency = true
}
elseif _node.balancingStrategy == "leastLoad" and not burstObservatory then
burstObservatory = {
subjectSelector = { "blc-" },
pingConfig = {
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
sampling = 3,
timeout = "5s"
}
}
if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag then
if _node.balancingStrategy == "leastLoad" then
if not burstObservatory then
burstObservatory = {
subjectSelector = { "blc-" },
pingConfig = {
destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
interval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
sampling = 3,
timeout = "5s"
}
}
end
else
if not observatory then
observatory = {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
probeInterval = (api.format_go_time(_node.probeInterval) ~= "0s") and api.format_go_time(_node.probeInterval) or "1m",
enableConcurrency = true
}
end
end
end
local inbound_tag = gen_loopback(loopback_tag, loopback_dst)
table.insert(rules, { inboundTag = { inbound_tag }, balancerTag = balancer_tag })
+2 -2
View File
@@ -21,13 +21,13 @@ define Download/geoip
HASH:=6878dbacfb1fcb1ee022f63ed6934bcefc95a3c4ba10c88f1131fb88dbf7c337
endef
GEOSITE_VER:=20260102111739
GEOSITE_VER:=20260103044301
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE)
HASH:=a535bde603510e96f2623ba081865a63ea2aae6d23c14c3a4430805bcd300f4f
HASH:=4998c4fd23f556e588658e4dbd153c728a0da913c3d336d8000ba5920a189ab3
endef
GEOSITE_IRAN_VER:=202512290048
+1 -1
View File
@@ -18,7 +18,7 @@ import (
)
var (
version = "5.42.0"
version = "5.43.0"
build = "Custom"
codename = "V2Fly, a community-driven edition of V2Ray."
intro = "A unified platform for anti-censorship."
+3 -3
View File
@@ -14,14 +14,14 @@
<PackageVersion Include="Downloader" Version="4.0.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1.1" />
<PackageVersion Include="QRCoder" Version="1.7.0" />
<PackageVersion Include="ReactiveUI" Version="22.3.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="22.3.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.2" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.2" />
<PackageVersion Include="NLog" Version="6.0.7" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
@@ -197,7 +197,7 @@ public partial class CoreConfigV2rayService
if (item.OutboundTag == Global.DirectTag)
{
if (normalizedDomain.StartsWith("geosite:"))
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
{
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
}
@@ -208,7 +208,7 @@ public partial class CoreConfigV2rayService
}
else if (item.OutboundTag != Global.BlockTag)
{
if (normalizedDomain.StartsWith("geosite:"))
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
{
proxyGeositeList.Add(normalizedDomain);
}
@@ -564,7 +564,7 @@ public partial class CoreConfigV2rayService
var fragmentOutbound = new Outbounds4Ray
{
protocol = "freedom",
tag = $"{Global.ProxyTag}3",
tag = $"frag-{Global.ProxyTag}",
settings = new()
{
fragment = new()
@@ -198,6 +198,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
{
if (!it.AllowTest)
{
await UpdateFunc(it.IndexId, ResUI.SpeedtestingSkip);
continue;
}
+1
View File
@@ -158,6 +158,7 @@ dependencies {
// Data and Storage Libraries
implementation(libs.mmkv.static)
implementation(libs.gson)
implementation(libs.okhttp)
// Reactive and Utility Libraries
implementation(libs.kotlinx.coroutines.android)
@@ -146,6 +146,9 @@
<activity
android:name=".ui.CheckUpdateActivity"
android:exported="false" />
<activity
android:name=".ui.BackupActivity"
android:exported="false" />
<activity
android:name=".ui.AboutActivity"
android:exported="false" />
@@ -0,0 +1,9 @@
package com.v2ray.ang.dto
data class WebDavConfig(
val baseUrl: String,
val username: String? = null,
val password: String? = null,
val remoteBasePath: String = "/",
val timeoutSeconds: Long = 30
)
@@ -4,6 +4,7 @@ import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig.PREF_IS_BOOTED
import com.v2ray.ang.AppConfig.PREF_ROUTING_RULESET
import com.v2ray.ang.dto.AssetUrlItem
import com.v2ray.ang.dto.WebDavConfig
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.dto.RulesetItem
import com.v2ray.ang.dto.ServerAffiliationInfo
@@ -27,6 +28,7 @@ object MmkvManager {
private const val KEY_SELECTED_SERVER = "SELECTED_SERVER"
private const val KEY_ANG_CONFIGS = "ANG_CONFIGS"
private const val KEY_SUB_IDS = "SUB_IDS"
private const val KEY_WEBDAV_CONFIG = "WEBDAV_CONFIG"
//private val profileStorage by lazy { MMKV.mmkvWithID(ID_PROFILE_CONFIG, MMKV.MULTI_PROCESS_MODE) }
private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
@@ -468,6 +470,7 @@ object MmkvManager {
//endregion
//region settings
/**
* Encodes the settings.
*
@@ -588,4 +591,22 @@ object MmkvManager {
//endregion
//region WebDAV
/**
* Encodes the WebDAV config as JSON into settings storage.
*/
fun encodeWebDavConfig(config: WebDavConfig): Boolean {
return settingsStorage.encode(KEY_WEBDAV_CONFIG, JsonUtil.toJson(config))
}
/**
* Decodes the WebDAV config from settings storage.
*/
fun decodeWebDavConfig(): WebDavConfig? {
val json = settingsStorage.decodeString(KEY_WEBDAV_CONFIG) ?: return null
return JsonUtil.fromJson(json, WebDavConfig::class.java)
}
//endregion
}
@@ -0,0 +1,187 @@
package com.v2ray.ang.handler
import android.util.Log
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.WebDavConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Credentials
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
import java.io.FileOutputStream
import java.net.URL
import java.util.concurrent.TimeUnit
object WebDavManager {
private var cfg: WebDavConfig? = null
private var client: OkHttpClient? = null
/**
* Initialize the WebDAV manager with a configuration and build an OkHttp client.
*
* @param config WebDavConfig containing baseUrl, credentials, remoteBasePath and timeoutSeconds.
*/
fun init(config: WebDavConfig) {
cfg = config
client = OkHttpClient.Builder()
.connectTimeout(config.timeoutSeconds, TimeUnit.SECONDS)
.readTimeout(config.timeoutSeconds, TimeUnit.SECONDS)
.writeTimeout(config.timeoutSeconds, TimeUnit.SECONDS)
.callTimeout(config.timeoutSeconds, TimeUnit.SECONDS)
.build()
}
/**
* Upload a local file to a remote relative path under the configured remoteBasePath.
* The provided `remoteRelativePath` should be relative (e.g. "backup_ng.zip").
* The method will attempt to create parent directories via MKCOL before PUT.
*
* @param localFile File to upload.
* @param remoteRelativePath Remote path relative to configured remoteBasePath.
* @return true if upload succeeded (HTTP 2xx), false otherwise.
*/
suspend fun uploadFile(localFile: File, remoteRelativePath: String): Boolean = withContext(Dispatchers.IO) {
try {
val cl = client ?: return@withContext false
val remote = buildRemoteUrl(remoteRelativePath)
// Ensure parent directories exist
val dirPath = remote.substringBeforeLast('/')
if (dirPath != remote) {
ensureRemoteDirs(dirPath)
}
// Determine content type based on file extension
val mediaType = when (localFile.extension.lowercase()) {
"zip" -> "application/zip"
"json" -> "application/json"
"txt" -> "text/plain"
else -> "application/octet-stream"
}.toMediaTypeOrNull()
val body = localFile.asRequestBody(mediaType)
val req = applyAuth(Request.Builder().url(remote).put(body)).build()
cl.newCall(req).execute().use { resp ->
val success = resp.isSuccessful
if (success) {
Log.i(AppConfig.TAG, "WebDAV upload success: $remoteRelativePath")
} else {
Log.e(AppConfig.TAG, "WebDAV upload failed: $remoteRelativePath (HTTP ${resp.code})")
}
return@withContext success
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "WebDAV upload exception: $remoteRelativePath", e)
return@withContext false
}
}
/**
* Download a remote file (relative to configured remoteBasePath) into a local file.
*
* @param remoteRelativePath Remote path relative to configured remoteBasePath.
* @param destFile Local destination file to write to.
* @return true if download and write succeeded, false otherwise.
*/
suspend fun downloadFile(remoteRelativePath: String, destFile: File): Boolean = withContext(Dispatchers.IO) {
try {
val cl = client ?: return@withContext false
val remote = buildRemoteUrl(remoteRelativePath)
val req = applyAuth(Request.Builder().url(remote).get()).build()
cl.newCall(req).execute().use { resp ->
if (!resp.isSuccessful) {
Log.e(AppConfig.TAG, "WebDAV download failed: $remoteRelativePath (HTTP ${resp.code})")
return@withContext false
}
val body = resp.body
if (body == null) {
Log.e(AppConfig.TAG, "WebDAV download failed: $remoteRelativePath (empty response body)")
return@withContext false
}
body.byteStream().use { input ->
destFile.parentFile?.mkdirs()
FileOutputStream(destFile).use { fos ->
input.copyTo(fos)
}
}
Log.i(AppConfig.TAG, "WebDAV download success: $remoteRelativePath")
return@withContext true
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "WebDAV download exception: $remoteRelativePath", e)
return@withContext false
}
}
/**
* Build a full remote URL by combining the configured base URL, the configured
* remote base path and a relative path provided by the caller.
*
* Example: baseUrl="https://example.com/remote.php/dav", remoteBasePath="backups",
* remoteRelativePath="backup_ng.zip" => "https://example.com/remote.php/dav/backups/backup_ng.zip"
*
* @param remoteRelativePath A path relative to the configured remoteBasePath (no leading slash required).
* @return Full URL string used for HTTP operations.
*/
private fun buildRemoteUrl(remoteRelativePath: String): String {
val base = cfg?.baseUrl?.trimEnd('/') ?: ""
val basePath = cfg?.remoteBasePath?.trim('/') ?: ""
val rel = remoteRelativePath.trimStart('/')
return if (basePath.isEmpty()) "$base/$rel" else "$base/$basePath/$rel"
}
/**
* Apply HTTP Basic authentication headers to the given request builder when
* username is configured in `cfg`.
*
* @param builder OkHttp Request.Builder to modify.
* @return The same builder instance with Authorization header applied if credentials exist.
*/
private fun applyAuth(builder: Request.Builder): Request.Builder {
val username = cfg?.username
val password = cfg?.password
if (!username.isNullOrEmpty()) {
builder.header("Authorization", Credentials.basic(username, password ?: ""))
}
return builder
}
/**
* Ensure that each directory segment in the given directory URL exists on the
* WebDAV server. This issues MKCOL requests for each segment in a best-effort
* manner and ignores errors for segments that already exist.
*
* @param dirUrl Absolute URL to the directory that should exist (e.g. https://.../backups)
*/
private fun ensureRemoteDirs(dirUrl: String) {
try {
val cl = client ?: return
val url = URL(dirUrl)
val segments = url.path.split("/").filter { it.isNotEmpty() }
var accum = ""
for (seg in segments) {
accum += "/$seg"
val mkUrl = URL(url.protocol, url.host, if (url.port == -1) -1 else url.port, accum).toString()
try {
val req = applyAuth(Request.Builder().url(mkUrl).method("MKCOL", null)).build()
cl.newCall(req).execute().use { resp ->
// 201 Created or 405 Method Not Allowed (already exists) are acceptable
if (resp.code != 201 && resp.code != 405 && resp.code != 409) {
Log.w(AppConfig.TAG, "WebDAV MKCOL $mkUrl returned ${resp.code}")
}
}
} catch (ignored: Exception) {
// best-effort, continue
}
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "WebDAV ensureRemoteDirs error", e)
}
}
}
@@ -1,126 +1,22 @@
package com.v2ray.ang.ui
import android.Manifest
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityAboutBinding
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.toastError
import com.v2ray.ang.extension.toastSuccess
import com.v2ray.ang.handler.SpeedtestManager
import com.v2ray.ang.util.Utils
import com.v2ray.ang.util.ZipUtil
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
class AboutActivity : BaseActivity() {
private val binding by lazy { ActivityAboutBinding.inflate(layoutInflater) }
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
try {
showFileChooser()
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
}
} else {
toast(R.string.toast_permission_denied)
}
}
private val createBackupFile =
registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { uri ->
if (uri != null) {
try {
val ret = backupConfigurationToCache()
if (ret.first) {
// Copy the cached zip file to user-selected location
contentResolver.openOutputStream(uri)?.use { output ->
File(ret.second).inputStream().use { input ->
input.copyTo(output)
}
}
// Clean up cache file
File(ret.second).delete()
toastSuccess(R.string.toast_success)
} else {
toastError(R.string.toast_failure)
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to backup configuration", e)
toastError(R.string.toast_failure)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
title = getString(R.string.title_about)
binding.layoutBackup.setOnClickListener {
val dateFormatted = SimpleDateFormat(
"yyyy-MM-dd-HH-mm-ss",
Locale.getDefault()
).format(System.currentTimeMillis())
val defaultFileName = "${getString(R.string.app_name)}_${dateFormatted}.zip"
// Let user choose where to save the backup file
createBackupFile.launch(defaultFileName)
}
binding.layoutShare.setOnClickListener {
val ret = backupConfigurationToCache()
if (ret.first) {
startActivity(
Intent.createChooser(
Intent(Intent.ACTION_SEND).setType("application/zip")
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(
Intent.EXTRA_STREAM,
FileProvider.getUriForFile(
this, BuildConfig.APPLICATION_ID + ".cache", File(ret.second)
)
), getString(R.string.title_configuration_share)
)
)
} else {
toastError(R.string.toast_failure)
}
}
binding.layoutRestore.setOnClickListener {
val permission =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
if (ContextCompat.checkSelfPermission(this, permission) == android.content.pm.PackageManager.PERMISSION_GRANTED) {
try {
showFileChooser()
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
}
} else {
requestPermissionLauncher.launch(permission)
}
}
binding.layoutSoureCcode.setOnClickListener {
Utils.openUri(this, AppConfig.APP_URL)
}
@@ -154,78 +50,4 @@ class AboutActivity : BaseActivity() {
binding.tvAppId.text = it
}
}
/**
* Backup configuration to cache directory
* Returns Pair<success, zipFilePath>
*/
private fun backupConfigurationToCache(): Pair<Boolean, String> {
val dateFormatted = SimpleDateFormat(
"yyyy-MM-dd-HH-mm-ss",
Locale.getDefault()
).format(System.currentTimeMillis())
val folderName = "${getString(R.string.app_name)}_${dateFormatted}"
val backupDir = this.cacheDir.absolutePath + "/$folderName"
val outputZipFilePath = "${this.cacheDir.absolutePath}/$folderName.zip"
val count = MMKV.backupAllToDirectory(backupDir)
if (count <= 0) {
return Pair(false, "")
}
if (ZipUtil.zipFromFolder(backupDir, outputZipFilePath)) {
return Pair(true, outputZipFilePath)
} else {
return Pair(false, "")
}
}
private fun restoreConfiguration(zipFile: File): Boolean {
val backupDir = this.cacheDir.absolutePath + "/${System.currentTimeMillis()}"
if (!ZipUtil.unzipToFolder(zipFile, backupDir)) {
return false
}
val count = MMKV.restoreAllFromDirectory(backupDir)
return count > 0
}
private fun showFileChooser() {
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
type = "*/*"
addCategory(Intent.CATEGORY_OPENABLE)
}
try {
chooseFile.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
} catch (ex: android.content.ActivityNotFoundException) {
Log.e(AppConfig.TAG, "File chooser activity not found", ex)
toast(R.string.toast_require_file_manager)
}
}
private val chooseFile =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val uri = result.data?.data
if (result.resultCode == RESULT_OK && uri != null) {
try {
val targetFile =
File(this.cacheDir.absolutePath, "${System.currentTimeMillis()}.zip")
contentResolver.openInputStream(uri).use { input ->
targetFile.outputStream().use { fileOut ->
input?.copyTo(fileOut)
}
}
if (restoreConfiguration(targetFile)) {
toastSuccess(R.string.toast_success)
} else {
toastError(R.string.toast_failure)
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Error during file restore", e)
toastError(R.string.toast_failure)
}
}
}
}
@@ -0,0 +1,361 @@
package com.v2ray.ang.ui
import android.Manifest
import android.app.AlertDialog
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.BuildConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.toastError
import com.v2ray.ang.extension.toastSuccess
import com.v2ray.ang.handler.WebDavManager
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.dto.WebDavConfig
import androidx.lifecycle.lifecycleScope
import com.v2ray.ang.databinding.ActivityBackupBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import com.v2ray.ang.databinding.DialogWebdavBinding
import com.v2ray.ang.util.ZipUtil
import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
class BackupActivity : BaseActivity() {
private val binding by lazy { ActivityBackupBinding.inflate(layoutInflater) }
private val config_backup_options: Array<out String> by lazy {
resources.getStringArray(R.array.config_backup_options)
}
companion object {
private const val BACKUP_FILE_NAME = "backup_ng.zip"
}
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
try {
showFileChooser()
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
}
} else {
toast(R.string.toast_permission_denied)
}
}
private val createBackupFile =
registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { uri ->
if (uri != null) {
try {
val ret = backupConfigurationToCache()
if (ret.first) {
// Copy the cached zip file to user-selected location
contentResolver.openOutputStream(uri)?.use { output ->
File(ret.second).inputStream().use { input ->
input.copyTo(output)
}
}
// Clean up cache file
File(ret.second).delete()
toastSuccess(R.string.toast_success)
} else {
toastError(R.string.toast_failure)
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to backup configuration", e)
toastError(R.string.toast_failure)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
title = getString(R.string.title_configuration_backup_restore)
binding.layoutBackup.setOnClickListener {
AlertDialog.Builder(this)
.setTitle(R.string.title_configuration_backup)
.setItems(config_backup_options) { dialog, which ->
when (which) {
0 -> backupViaLocal()
1 -> backupViaWebDav()
}
}
.show()
}
binding.layoutShare.setOnClickListener {
val ret = backupConfigurationToCache()
if (ret.first) {
startActivity(
Intent.createChooser(
Intent(Intent.ACTION_SEND).setType("application/zip")
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(
Intent.EXTRA_STREAM,
FileProvider.getUriForFile(
this, BuildConfig.APPLICATION_ID + ".cache", File(ret.second)
)
), getString(R.string.title_configuration_share)
)
)
} else {
toastError(R.string.toast_failure)
}
}
binding.layoutRestore.setOnClickListener {
AlertDialog.Builder(this)
.setTitle(R.string.title_configuration_restore)
.setItems(config_backup_options) { dialog, which ->
when (which) {
0 -> restoreViaLocal()
1 -> restoreViaWebDav()
}
}
.show()
}
binding.layoutWebdavConfigSetting.setOnClickListener {
showWebDavSettingsDialog()
}
}
/**
* Backup configuration to cache directory
* Returns Pair<success, zipFilePath>
*/
private fun backupConfigurationToCache(): Pair<Boolean, String> {
val dateFormatted = SimpleDateFormat(
"yyyy-MM-dd-HH-mm-ss",
Locale.getDefault()
).format(System.currentTimeMillis())
val folderName = "${getString(R.string.app_name)}_${dateFormatted}"
val backupDir = this.cacheDir.absolutePath + "/$folderName"
val outputZipFilePath = "${this.cacheDir.absolutePath}/$folderName.zip"
val count = MMKV.backupAllToDirectory(backupDir)
if (count <= 0) {
return Pair(false, "")
}
if (ZipUtil.zipFromFolder(backupDir, outputZipFilePath)) {
return Pair(true, outputZipFilePath)
} else {
return Pair(false, "")
}
}
private fun restoreConfiguration(zipFile: File): Boolean {
val backupDir = this.cacheDir.absolutePath + "/${System.currentTimeMillis()}"
if (!ZipUtil.unzipToFolder(zipFile, backupDir)) {
return false
}
val count = MMKV.restoreAllFromDirectory(backupDir)
return count > 0
}
private fun showFileChooser() {
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
type = "*/*"
addCategory(Intent.CATEGORY_OPENABLE)
}
try {
chooseFile.launch(Intent.createChooser(intent, getString(R.string.title_file_chooser)))
} catch (ex: ActivityNotFoundException) {
Log.e(AppConfig.TAG, "File chooser activity not found", ex)
toast(R.string.toast_require_file_manager)
}
}
private val chooseFile =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val uri = result.data?.data
if (result.resultCode == RESULT_OK && uri != null) {
try {
val targetFile =
File(this.cacheDir.absolutePath, "${System.currentTimeMillis()}.zip")
contentResolver.openInputStream(uri).use { input ->
targetFile.outputStream().use { fileOut ->
input?.copyTo(fileOut)
}
}
if (restoreConfiguration(targetFile)) {
toastSuccess(R.string.toast_success)
} else {
toastError(R.string.toast_failure)
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Error during file restore", e)
toastError(R.string.toast_failure)
}
}
}
private fun backupViaLocal() {
val dateFormatted = SimpleDateFormat(
"yyyy-MM-dd-HH-mm-ss",
Locale.getDefault()
).format(System.currentTimeMillis())
val defaultFileName = "${getString(R.string.app_name)}_${dateFormatted}.zip"
createBackupFile.launch(defaultFileName)
}
private fun restoreViaLocal() {
val permission =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
try {
showFileChooser()
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to show file chooser", e)
}
} else {
requestPermissionLauncher.launch(permission)
}
}
private fun backupViaWebDav() {
val saved = MmkvManager.decodeWebDavConfig()
if (saved == null || saved.baseUrl.isEmpty()) {
toastError(R.string.title_webdav_config_setting_unknown)
return
}
binding.pbWaiting.show()
lifecycleScope.launch(Dispatchers.IO) {
var tempFile: File? = null
try {
val ret = backupConfigurationToCache()
if (!ret.first) {
withContext(Dispatchers.Main) {
toastError(R.string.toast_failure)
}
return@launch
}
tempFile = File(ret.second)
WebDavManager.init(saved)
val ok = try {
WebDavManager.uploadFile(tempFile, BACKUP_FILE_NAME)
} catch (e: Exception) {
Log.e(AppConfig.TAG, "WebDAV upload error", e)
false
}
withContext(Dispatchers.Main) {
if (ok) toastSuccess(R.string.toast_success) else toastError(R.string.toast_failure)
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "WebDAV backup error", e)
withContext(Dispatchers.Main) {
toastError(R.string.toast_failure)
}
} finally {
try {
tempFile?.delete()
} catch (_: Exception) {
}
withContext(Dispatchers.Main) {
binding.pbWaiting.hide()
}
}
}
}
private fun restoreViaWebDav() {
val saved = MmkvManager.decodeWebDavConfig()
if (saved == null || saved.baseUrl.isEmpty()) {
toastError(R.string.title_webdav_config_setting_unknown)
return
}
binding.pbWaiting.show()
lifecycleScope.launch(Dispatchers.IO) {
var target: File? = null
try {
target = File(cacheDir, "download_${System.currentTimeMillis()}.zip")
WebDavManager.init(saved)
val ok = WebDavManager.downloadFile(BACKUP_FILE_NAME, target)
if (!ok) {
withContext(Dispatchers.Main) {
toastError(R.string.toast_failure)
}
return@launch
}
val restored = restoreConfiguration(target)
withContext(Dispatchers.Main) {
if (restored) {
setResult(RESULT_OK, Intent().putExtra("config_restored", true))
toastSuccess(R.string.toast_success)
} else {
toastError(R.string.toast_failure)
}
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "WebDAV download error", e)
withContext(Dispatchers.Main) { toastError(R.string.toast_failure) }
} finally {
try {
target?.delete()
} catch (_: Exception) {
}
withContext(Dispatchers.Main) {
binding.pbWaiting.hide()
}
}
}
}
private fun showWebDavSettingsDialog() {
val dialogBinding = DialogWebdavBinding.inflate(layoutInflater)
MmkvManager.decodeWebDavConfig()?.let { cfg ->
dialogBinding.etWebdavUrl.setText(cfg.baseUrl)
dialogBinding.etWebdavUser.setText(cfg.username ?: "")
dialogBinding.etWebdavPass.setText(cfg.password ?: "")
dialogBinding.etWebdavRemotePath.setText(cfg.remoteBasePath ?: "/")
}
AlertDialog.Builder(this)
.setTitle(R.string.title_webdav_config_setting)
.setView(dialogBinding.root)
.setPositiveButton(R.string.menu_item_save_config) { _, _ ->
val url = dialogBinding.etWebdavUrl.text.toString().trim()
val user = dialogBinding.etWebdavUser.text.toString().trim().ifEmpty { null }
val pass = dialogBinding.etWebdavPass.text.toString()
val remotePath = dialogBinding.etWebdavRemotePath.text.toString().trim().ifEmpty { "/" }
val cfg = WebDavConfig(baseUrl = url, username = user, password = pass, remoteBasePath = remotePath)
MmkvManager.encodeWebDavConfig(cfg)
toastSuccess(R.string.toast_success)
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
}
@@ -56,7 +56,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
startV2Ray()
}
}
private val requestSubSettingActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
private val requestActivityLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
initGroupTab()
}
private val tabGroupListener = object : TabLayout.OnTabSelectedListener {
@@ -672,9 +672,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here.
when (item.itemId) {
R.id.sub_setting -> requestSubSettingActivity.launch(Intent(this, SubSettingActivity::class.java))
R.id.sub_setting -> requestActivityLauncher.launch(Intent(this, SubSettingActivity::class.java))
R.id.per_app_proxy_settings -> startActivity(Intent(this, PerAppProxyActivity::class.java))
R.id.routing_setting -> requestSubSettingActivity.launch(Intent(this, RoutingSettingActivity::class.java))
R.id.routing_setting -> requestActivityLauncher.launch(Intent(this, RoutingSettingActivity::class.java))
R.id.user_asset_setting -> startActivity(Intent(this, UserAssetActivity::class.java))
R.id.settings -> startActivity(
Intent(this, SettingsActivity::class.java)
@@ -684,6 +684,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
R.id.promotion -> Utils.openUri(this, "${Utils.decode(AppConfig.APP_PROMOTION_URL)}?t=${System.currentTimeMillis()}")
R.id.logcat -> startActivity(Intent(this, LogcatActivity::class.java))
R.id.check_for_update -> startActivity(Intent(this, CheckUpdateActivity::class.java))
R.id.backup_restore -> requestActivityLauncher.launch(Intent(this, BackupActivity::class.java))
R.id.about -> startActivity(Intent(this, AboutActivity::class.java))
}
@@ -9,99 +9,11 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_backup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="@dimen/padding_spacing_dp16">
<ImageView
android:layout_width="@dimen/image_size_dp24"
android:layout_height="@dimen/image_size_dp24"
app:srcCompat="@drawable/ic_backup_24dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="@dimen/padding_spacing_dp16">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_configuration_backup"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/layout_share"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="@dimen/padding_spacing_dp16">
<ImageView
android:layout_width="@dimen/image_size_dp24"
android:layout_height="@dimen/image_size_dp24"
app:srcCompat="@drawable/ic_share_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/padding_spacing_dp16"
android:text="@string/title_configuration_share"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_restore"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="@dimen/padding_spacing_dp16">
<ImageView
android:layout_width="@dimen/image_size_dp24"
android:layout_height="@dimen/image_size_dp24"
app:srcCompat="@drawable/ic_restore_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/padding_spacing_dp16"
android:text="@string/title_configuration_restore"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top"
android:orientation="vertical"
android:paddingTop="@dimen/padding_spacing_dp16">
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_soure_ccode"
@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/pb_waiting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="invisible"
app:indicatorColor="@color/color_fab_active" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_backup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="@dimen/padding_spacing_dp16">
<ImageView
android:layout_width="@dimen/image_size_dp24"
android:layout_height="@dimen/image_size_dp24"
app:srcCompat="@drawable/ic_backup_24dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="@dimen/padding_spacing_dp16">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_configuration_backup"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/layout_share"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="@dimen/padding_spacing_dp16">
<ImageView
android:layout_width="@dimen/image_size_dp24"
android:layout_height="@dimen/image_size_dp24"
app:srcCompat="@drawable/ic_share_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/padding_spacing_dp16"
android:text="@string/title_configuration_share"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_restore"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="@dimen/padding_spacing_dp16">
<ImageView
android:layout_width="@dimen/image_size_dp24"
android:layout_height="@dimen/image_size_dp24"
app:srcCompat="@drawable/ic_restore_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/padding_spacing_dp16"
android:text="@string/title_configuration_restore"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:orientation="vertical"
android:paddingTop="@dimen/padding_spacing_dp16">
<LinearLayout
android:id="@+id/layout_webdav_config_setting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="@dimen/padding_spacing_dp16">
<ImageView
android:layout_width="@dimen/image_size_dp24"
android:layout_height="@dimen/image_size_dp24"
app:srcCompat="@drawable/ic_settings_24dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="@dimen/padding_spacing_dp16">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_webdav_config_setting"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/padding_spacing_dp16">
<EditText
android:id="@+id/et_webdav_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/title_webdav_url"
android:inputType="textUri"
android:layout_marginTop="@dimen/padding_spacing_dp8" />
<EditText
android:id="@+id/et_webdav_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/title_webdav_user"
android:layout_marginTop="@dimen/padding_spacing_dp8" />
<EditText
android:id="@+id/et_webdav_pass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/title_webdav_pass"
android:inputType="textPassword"
android:layout_marginTop="@dimen/padding_spacing_dp8" />
<EditText
android:id="@+id/et_webdav_remote_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/title_webdav_remote_path"
android:layout_marginTop="@dimen/padding_spacing_dp8" />
</LinearLayout>
</ScrollView>
@@ -39,6 +39,10 @@
android:id="@+id/check_for_update"
android:icon="@drawable/ic_check_update_24dp"
android:title="@string/update_check_for_update" />
<item
android:id="@+id/backup_restore"
android:icon="@drawable/ic_restore_24dp"
android:title="@string/title_configuration_backup_restore" />
<item
android:id="@+id/about"
android:icon="@drawable/ic_about_24dp"
@@ -238,9 +238,6 @@
<string name="title_source_code">الكود المصدري</string>
<string name="title_oss_license">Open Source licenses</string>
<string name="title_tg_channel">قناة Telegram</string>
<string name="title_configuration_backup">نسخ التكوين احتياطيًا</string>
<string name="title_configuration_restore">استعادة التكوين</string>
<string name="title_configuration_share">مشاركة التكوين</string>
<string name="title_pref_promotion">ترويج</string>
@@ -339,6 +336,18 @@
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<!-- BackupActivity -->
<string name="title_configuration_backup_restore">Backup &amp; Restore</string>
<string name="title_configuration_backup">نسخ التكوين احتياطيًا</string>
<string name="title_configuration_restore">استعادة التكوين</string>
<string name="title_configuration_share">مشاركة التكوين</string>
<string name="title_webdav_config_setting">WebDAV Settings</string>
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
<string name="title_webdav_url">WebDAV server URL</string>
<string name="title_webdav_user">Username</string>
<string name="title_webdav_pass">Password</string>
<string name="title_webdav_remote_path">Remote directory (optional)</string>
<string-array name="share_method">
<item>رمز استجابة سريعة (QRcode)</item>
<item>تصدير إلى الحافظة</item>
@@ -384,6 +393,10 @@
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
<string name="pre_resolving_domain">Pre-resolving domain…</string>
<string-array name="config_backup_options">
<item>Local</item>
<item>WebDAV</item>
</string-array>
</resources>
@@ -238,9 +238,6 @@
<string name="title_source_code">সোর্স কোড</string>
<string name="title_oss_license">Open Source licenses</string>
<string name="title_tg_channel">টেলিগ্রাম চ্যানেল</string>
<string name="title_configuration_backup">কনফিগারেশন ব্যাকআপ</string>
<string name="title_configuration_restore">কনফিগারেশন পুনরুদ্ধার</string>
<string name="title_configuration_share">কনফিগারেশন শেয়ার করুন</string>
<string name="title_pref_promotion">প্রচার</string>
@@ -338,6 +335,18 @@
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<!-- BackupActivity -->
<string name="title_configuration_backup_restore">Backup &amp; Restore</string>
<string name="title_configuration_backup">কনফিগারেশন ব্যাকআপ</string>
<string name="title_configuration_restore">কনফিগারেশন পুনরুদ্ধার</string>
<string name="title_configuration_share">কনফিগারেশন শেয়ার করুন</string>
<string name="title_webdav_config_setting">WebDAV Settings</string>
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
<string name="title_webdav_url">WebDAV server URL</string>
<string name="title_webdav_user">Username</string>
<string name="title_webdav_pass">Password</string>
<string name="title_webdav_remote_path">Remote directory (optional)</string>
<string-array name="share_method">
<item>QR কোড</item>
<item>ক্লিপবোর্ডে রপ্তানি করুন</item>
@@ -389,6 +398,10 @@
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
<string name="pre_resolving_domain">Pre-resolving domain…</string>
<string-array name="config_backup_options">
<item>Local</item>
<item>WebDAV</item>
</string-array>
</resources>
@@ -238,9 +238,6 @@
<string name="title_source_code">کود بونچک</string>
<string name="title_oss_license">موجوزا کود بونچک</string>
<string name="title_tg_channel">تورگه تلگرام</string>
<string name="title_configuration_backup">لادراری گرؽڌن ز کانفیگ</string>
<string name="title_configuration_restore">وورگندن کانفیگ</string>
<string name="title_configuration_share">یک رسۊوی کانفیگ</string>
<string name="title_pref_promotion">تبلیقات</string>
@@ -347,6 +344,18 @@
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<!-- BackupActivity -->
<string name="title_configuration_backup_restore">Backup &amp; Restore</string>
<string name="title_configuration_backup">لادراری گرؽڌن ز کانفیگ</string>
<string name="title_configuration_restore">وورگندن کانفیگ</string>
<string name="title_configuration_share">یک رسۊوی کانفیگ</string>
<string name="title_webdav_config_setting">WebDAV Settings</string>
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
<string name="title_webdav_url">WebDAV server URL</string>
<string name="title_webdav_user">Username</string>
<string name="title_webdav_pass">Password</string>
<string name="title_webdav_remote_path">Remote directory (optional)</string>
<string-array name="share_method">
<item>QRcode</item>
<item>و در کشیڌن من کلیپ بورد</item>
@@ -399,6 +408,10 @@
<item>کم ترین پینگ</item>
<item>کم ترین بار(لود)</item>
</string-array>
<string name="pre_resolving_domain">پؽش هل دامنه…</string>
<string-array name="config_backup_options">
<item>Local</item>
<item>WebDAV</item>
</string-array>
</resources>
@@ -237,9 +237,6 @@
<string name="title_source_code">کد منبع</string>
<string name="title_oss_license">مجوز های منبع باز</string>
<string name="title_tg_channel">کانال تلگرام</string>
<string name="title_configuration_backup">پشتیبان گیری از پیکربندی</string>
<string name="title_configuration_restore">بازیابی پیکربندی</string>
<string name="title_configuration_share">اشتراک گذاری پیکربندی</string>
<string name="title_pref_promotion">تبلیغات</string>
<string name="title_pref_auto_update_subscription">به‌ روزرسانی خودکار اشتراک ها</string>
@@ -344,6 +341,18 @@
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<!-- BackupActivity -->
<string name="title_configuration_backup_restore">Backup &amp; Restore</string>
<string name="title_configuration_backup">پشتیبان گیری از پیکربندی</string>
<string name="title_configuration_restore">بازیابی پیکربندی</string>
<string name="title_configuration_share">اشتراک گذاری پیکربندی</string>
<string name="title_webdav_config_setting">WebDAV Settings</string>
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
<string name="title_webdav_url">WebDAV server URL</string>
<string name="title_webdav_user">Username</string>
<string name="title_webdav_pass">Password</string>
<string name="title_webdav_remote_path">Remote directory (optional)</string>
<string-array name="share_method">
<item>QRcode</item>
<item>خروجی گرفتن در کلیپ‌ بورد</item>
@@ -398,6 +407,10 @@
<item>کمترین پینگ</item>
<item>کمترین بار(لود)</item>
</string-array>
<string name="pre_resolving_domain">Pre-resolving domain…</string>
<string-array name="config_backup_options">
<item>Local</item>
<item>WebDAV</item>
</string-array>
</resources>
@@ -237,9 +237,6 @@
<string name="title_source_code">Исходный код</string>
<string name="title_oss_license">Лицензии открытого исходного кода</string>
<string name="title_tg_channel">Telegram-канал</string>
<string name="title_configuration_backup">Резервирование конфигурации</string>
<string name="title_configuration_restore">Восстановление конфигурации</string>
<string name="title_configuration_share">Поделиться конфигурацией</string>
<string name="title_pref_promotion">Содействие</string>
@@ -346,6 +343,18 @@
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<!-- BackupActivity -->
<string name="title_configuration_backup_restore">Backup &amp; Restore</string>
<string name="title_configuration_backup">Резервирование конфигурации</string>
<string name="title_configuration_restore">Восстановление конфигурации</string>
<string name="title_configuration_share">Поделиться конфигурацией</string>
<string name="title_webdav_config_setting">WebDAV Settings</string>
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
<string name="title_webdav_url">WebDAV server URL</string>
<string name="title_webdav_user">Username</string>
<string name="title_webdav_pass">Password</string>
<string name="title_webdav_remote_path">Remote directory (optional)</string>
<string-array name="share_method">
<item>QR-код</item>
<item>Экспорт в буфер обмена</item>
@@ -398,6 +407,10 @@
<item>Наименьшая задержка</item>
<item>Наименьшая нагрузка</item>
</string-array>
<string name="pre_resolving_domain">Предварительное определение домена…</string>
<string-array name="config_backup_options">
<item>Local</item>
<item>WebDAV</item>
</string-array>
</resources>
@@ -239,9 +239,6 @@
<string name="title_source_code">Mã nguồn</string>
<string name="title_oss_license">Open Source licenses</string>
<string name="title_tg_channel">Kênh Telegram</string>
<string name="title_configuration_backup">Sao lưu cấu hình</string>
<string name="title_configuration_restore">Khôi phục cấu hình</string>
<string name="title_configuration_share">Chia sẻ cấu hình</string>
<string name="title_pref_promotion">Quảng bá server</string>
<string name="title_pref_auto_update_subscription">Tự động cập nhật các gói đăng ký</string>
@@ -340,6 +337,18 @@
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<!-- BackupActivity -->
<string name="title_configuration_backup_restore">Backup &amp; Restore</string>
<string name="title_configuration_backup">Sao lưu cấu hình</string>
<string name="title_configuration_restore">Khôi phục cấu hình</string>
<string name="title_configuration_share">Chia sẻ cấu hình</string>
<string name="title_webdav_config_setting">WebDAV Settings</string>
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
<string name="title_webdav_url">WebDAV server URL</string>
<string name="title_webdav_user">Username</string>
<string name="title_webdav_pass">Password</string>
<string name="title_webdav_remote_path">Remote directory (optional)</string>
<string-array name="share_method">
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
<item>Sao chép vào Clipboard</item>
@@ -386,6 +395,10 @@
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
<string name="pre_resolving_domain">Pre-resolving domain…</string>
<string-array name="config_backup_options">
<item>Local</item>
<item>WebDAV</item>
</string-array>
</resources>
@@ -235,9 +235,6 @@
<string name="title_source_code">源代码</string>
<string name="title_oss_license">Open Source licenses</string>
<string name="title_tg_channel">Telegram 频道</string>
<string name="title_configuration_backup">备份配置</string>
<string name="title_configuration_restore">还原配置</string>
<string name="title_configuration_share">分享配置</string>
<string name="title_pref_promotion">推广</string>
@@ -338,6 +335,18 @@
<string name="title_policy_group_subscription_id">来自订阅分组</string>
<string name="title_policy_group_subscription_filter">别名正则过滤</string>
<!-- BackupActivity -->
<string name="title_configuration_backup_restore">备份 &amp; 还原</string>
<string name="title_configuration_backup">备份配置</string>
<string name="title_configuration_restore">还原配置</string>
<string name="title_configuration_share">分享配置</string>
<string name="title_webdav_config_setting">WebDAV 设置</string>
<string name="title_webdav_config_setting_unknown">请先设置 WebDAV</string>
<string name="title_webdav_url">WebDAV 服务器地址</string>WebDAV server address
<string name="title_webdav_user">用户名</string>
<string name="title_webdav_pass">密码</string>
<string name="title_webdav_remote_path">远程文件夹名(可选)</string>
<string-array name="share_method">
<item>二维码</item>
<item>导出至剪贴板</item>
@@ -390,6 +399,10 @@
<item>最低延迟</item>
<item>最稳定</item>
</string-array>
<string name="pre_resolving_domain">预解析域名中…</string>
<string-array name="config_backup_options">
<item>本地</item>
<item>WebDAV</item>
</string-array>
</resources>
@@ -236,9 +236,6 @@
<string name="title_source_code">原始碼</string>
<string name="title_oss_license">Open Source licenses</string>
<string name="title_tg_channel">Telegram 頻道</string>
<string name="title_configuration_backup">備份設定</string>
<string name="title_configuration_restore">還原設定</string>
<string name="title_configuration_share">分享設定</string>
<string name="title_pref_promotion">推廣</string>
@@ -338,6 +335,18 @@
<string name="title_policy_group_subscription_id">來自訂閱分組</string>
<string name="title_policy_group_subscription_filter">別名正規過濾</string>
<!-- BackupActivity -->
<string name="title_configuration_backup_restore">Backup &amp; Restore</string>
<string name="title_configuration_backup">備份設定</string>
<string name="title_configuration_restore">還原設定</string>
<string name="title_configuration_share">分享設定</string>
<string name="title_webdav_config_setting">WebDAV 設定</string>
<string name="title_webdav_config_setting_unknown">請先設定 WebDAV</string>
<string name="title_webdav_url">WebDAV 伺服器位址</string>
<string name="title_webdav_user">使用者名稱</string>
<string name="title_webdav_pass">密碼</string>
<string name="title_webdav_remote_path">遠端目錄(可選)</string>
<string-array name="share_method">
<item>二維碼</item>
<item>匯出至剪貼簿</item>
@@ -390,6 +399,10 @@
<item>最低延遲</item>
<item>最少卡頓</item>
</string-array>
<string name="pre_resolving_domain">預解析域名…</string>
<string-array name="config_backup_options">
<item>本地</item>
<item>WebDAV</item>
</string-array>
</resources>
@@ -241,9 +241,6 @@
<string name="title_source_code">Source code</string>
<string name="title_oss_license">Open Source licenses</string>
<string name="title_tg_channel">Telegram channel</string>
<string name="title_configuration_backup">Backup config</string>
<string name="title_configuration_restore">Restore config</string>
<string name="title_configuration_share">Share config</string>
<string name="title_pref_promotion">Promotion</string>
@@ -350,6 +347,18 @@
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<!-- BackupActivity -->
<string name="title_configuration_backup_restore">Backup &amp; Restore</string>
<string name="title_configuration_backup">Backup config</string>
<string name="title_configuration_restore">Restore config</string>
<string name="title_configuration_share">Share config</string>
<string name="title_webdav_config_setting">WebDAV Settings</string>
<string name="title_webdav_config_setting_unknown">Please configure WebDAV first.</string>
<string name="title_webdav_url">WebDAV server URL</string>
<string name="title_webdav_user">Username</string>
<string name="title_webdav_pass">Password</string>
<string name="title_webdav_remote_path">Remote directory (optional)</string>
<string-array name="share_method">
<item>QRcode</item>
<item>Export to clipboard</item>
@@ -404,6 +413,9 @@
<item>Least Load</item>
</string-array>
<string name="pre_resolving_domain">Pre-resolving domain…</string>
<string-array name="config_backup_options">
<item>Local</item>
<item>WebDAV</item>
</string-array>
</resources>
@@ -13,6 +13,7 @@ activity = "1.12.2"
constraintlayout = "2.2.1"
mmkvStatic = "1.3.16"
gson = "2.13.2"
okhttp = "5.3.2"
quickieFoss = "1.14.0"
kotlinxCoroutinesAndroid = "1.10.2"
kotlinxCoroutinesCore = "1.10.2"
@@ -41,6 +42,7 @@ androidx-activity = { group = "androidx.activity", name = "activity", version.re
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
mmkv-static = { module = "com.tencent:mmkv-static", version.ref = "mmkvStatic" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
quickie-foss = { module = "com.github.T8RIN.QuickieExtended:quickie-foss", version.ref = "quickieFoss" }
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
+2 -2
View File
@@ -1,5 +1,5 @@
from .common import InfoExtractor
from ..utils import ExtractorError, urlencode_postdata
from ..utils import ExtractorError, UserNotLive, urlencode_postdata
class BigoIE(InfoExtractor):
@@ -40,7 +40,7 @@ class BigoIE(InfoExtractor):
info = info_raw.get('data') or {}
if not info.get('alive'):
raise ExtractorError('This user is offline.', expected=True)
raise UserNotLive(video_id=user_id)
formats, subs = self._extract_m3u8_formats_and_subtitles(
info.get('hls_src'), user_id, 'mp4', 'm3u8')
+4
View File
@@ -680,6 +680,10 @@ class TwitchPlaylistBaseIE(TwitchBaseIE):
}],
f'Downloading {self._NODE_KIND}s GraphQL page {page_num}',
fatal=False)
# Avoid extracting random/unrelated entries when channel_name doesn't exist
# See https://github.com/yt-dlp/yt-dlp/issues/15450
if traverse_obj(page, (0, 'data', 'user', 'id', {str})) == '':
raise ExtractorError(f'Channel "{channel_name}" not found', expected=True)
if not page:
break
edges = try_get(