From b76060570ec11c6cc675aaeed08ad02da0dd6332 Mon Sep 17 00:00:00 2001 From: Noah Hsu Date: Mon, 6 Jun 2022 16:28:37 +0800 Subject: [PATCH] refactor: init v3 --- .github/workflows/build.yml | 50 - .github/workflows/build_docker.yml | 44 - .github/workflows/release.yml | 72 - .github/workflows/release_docker.yml | 45 - CONTRIBUTING.md | 6 +- Dockerfile | 14 - README.md | 58 +- README_cn.md | 91 -- alist.go | 59 - bootstrap/account.go | 33 - bootstrap/cache.go | 22 - bootstrap/conf.go | 71 - bootstrap/cron.go | 14 - bootstrap/log.go | 36 - bootstrap/model.go | 84 - bootstrap/setting.go | 323 ---- build.sh | 157 -- cmd/alist.go | 7 + conf/config.go | 54 - conf/const.go | 11 - conf/var.go | 104 -- drivers/123/123.go | 178 --- drivers/123/driver.go | 460 ------ drivers/123/types.go | 74 - drivers/139/139.go | 175 --- drivers/139/driver.go | 457 ------ drivers/139/family.go | 74 - drivers/139/types.go | 187 --- drivers/139/util.go | 70 - drivers/189/189.go | 620 -------- drivers/189/driver.go | 382 ----- drivers/189/types.go | 91 -- drivers/189/util.go | 199 --- drivers/189pc/189.go | 318 ---- drivers/189pc/driver.go | 923 ----------- drivers/189pc/type.go | 180 --- drivers/189pc/util.go | 177 --- drivers/alidrive/alidrive.go | 209 --- drivers/alidrive/driver.go | 566 ------- drivers/alidrive/types.go | 53 - drivers/alist/alist.go | 44 - drivers/alist/driver.go | 192 --- drivers/all.go | 52 - drivers/baidu/baidu.go | 185 --- drivers/baidu/driver.go | 399 ----- drivers/baidu/types.go | 143 -- drivers/baidu/util.go | 18 - drivers/baiduphoto/baidu.go | 259 --- drivers/baiduphoto/driver.go | 502 ------ drivers/baiduphoto/types.go | 126 -- drivers/baiduphoto/util.go | 84 - drivers/base/base.go | 61 - drivers/base/cache.go | 58 - drivers/base/driver.go | 181 --- drivers/base/types.go | 55 - drivers/ftp/driver.go | 262 ---- drivers/ftp/ftp.go | 36 - drivers/google/driver.go | 289 ---- drivers/google/googledrive.go | 175 --- drivers/google/types.go | 38 - drivers/lanzou/driver.go | 202 --- drivers/lanzou/lanzou.go | 271 ---- drivers/lanzou/types.go | 47 - drivers/mediatrack/driver.go | 317 ---- drivers/mediatrack/mediatrack.go | 183 --- drivers/native/driver.go | 269 ---- drivers/native/native.go | 74 - drivers/onedrive/driver.go | 279 ---- drivers/onedrive/onedrive.go | 320 ---- drivers/operate/operate.go | 111 -- drivers/pikpak/driver.go | 330 ---- drivers/pikpak/pikpak.go | 232 --- drivers/quark/driver.go | 327 ---- drivers/quark/quark.go | 272 ---- drivers/quark/types.go | 134 -- drivers/quark/util.go | 31 - drivers/s3/driver.go | 304 ---- drivers/s3/s3.go | 195 --- drivers/s3/util.go | 10 - drivers/sftp/driver.go | 220 --- drivers/sftp/sftp.go | 110 -- drivers/sftp/types.go | 18 - drivers/sftp/util.go | 3 - drivers/shandian/driver.go | 278 ---- drivers/shandian/shandian.go | 150 -- drivers/teambition/driver.go | 286 ---- drivers/teambition/teambition.go | 237 --- drivers/teambition/types.go | 63 - drivers/teambition/util.go | 18 - drivers/template/driver.go | 151 -- drivers/template/template.go | 90 -- drivers/template/types.go | 18 - drivers/template/util.go | 3 - drivers/uss/driver.go | 237 --- drivers/uss/uss.go | 82 - drivers/webdav/driver.go | 224 --- drivers/webdav/odrvcookie/cookie.go | 47 - drivers/webdav/odrvcookie/fetch.go | 206 --- drivers/webdav/util.go | 7 - drivers/webdav/webdav.go | 35 - drivers/xunlei/driver.go | 419 ----- drivers/xunlei/types.go | 179 --- drivers/xunlei/util.go | 63 - drivers/xunlei/xunlei.go | 282 ---- drivers/yandex/driver.go | 225 --- drivers/yandex/types.go | 74 - drivers/yandex/util.go | 1 - drivers/yandex/yandex.go | 154 -- go.mod | 82 +- go.sum | 764 --------- model/account.go | 287 ---- model/file.go | 90 -- model/file_stream.go | 35 - model/meta.go | 47 - model/search_file.go | 35 - model/setting.go | 142 -- model/util.go | 13 - pkg/gowebdav/.gitignore | 21 - pkg/gowebdav/.travis.yml | 10 - pkg/gowebdav/LICENSE | 27 - pkg/gowebdav/Makefile | 33 - pkg/gowebdav/README.md | 564 ------- pkg/gowebdav/basicAuth.go | 34 - pkg/gowebdav/client.go | 447 ------ pkg/gowebdav/cmd/gowebdav/README.md | 103 -- pkg/gowebdav/cmd/gowebdav/main.go | 263 ---- pkg/gowebdav/digestAuth.go | 146 -- pkg/gowebdav/doc.go | 3 - pkg/gowebdav/errors.go | 49 - pkg/gowebdav/file.go | 77 - pkg/gowebdav/netrc.go | 54 - pkg/gowebdav/requests.go | 214 --- pkg/gowebdav/utils.go | 118 -- pkg/gowebdav/utils_test.go | 67 - public/public.go | 6 - server/common/check.go | 51 - server/common/common.go | 108 -- server/common/files.go | 51 - server/common/proxy.go | 102 -- server/controllers/account.go | 106 -- server/controllers/cache.go | 16 - server/controllers/down.go | 32 - server/controllers/driver.go | 11 - server/controllers/file/copy.go | 61 - server/controllers/file/delete.go | 50 - server/controllers/file/folder.go | 39 - server/controllers/file/mkdir.go | 34 - server/controllers/file/move.go | 67 - server/controllers/file/raname.go | 36 - server/controllers/file/refresh.go | 34 - server/controllers/file/upload.go | 65 - server/controllers/link.go | 47 - server/controllers/meta.go | 61 - server/controllers/other.go | 80 - server/controllers/path.go | 168 -- server/controllers/proxy.go | 101 -- server/controllers/search.go | 31 - server/controllers/setting.go | 71 - server/middlewares/account.go | 15 - server/middlewares/auth.go | 26 - server/middlewares/down.go | 27 - server/middlewares/path.go | 40 - server/router.go | 74 - server/static.go | 61 - server/webdav.go | 70 - server/webdav/file.go | 344 ---- server/webdav/if.go | 173 -- server/webdav/internal/xml/README | 11 - server/webdav/internal/xml/marshal.go | 1223 --------------- server/webdav/internal/xml/read.go | 692 -------- server/webdav/internal/xml/typeinfo.go | 371 ----- server/webdav/internal/xml/xml.go | 1998 ------------------------ server/webdav/lock.go | 445 ------ server/webdav/prop.go | 413 ----- server/webdav/webdav.go | 718 --------- server/webdav/xml.go | 519 ------ utils/check.go | 28 - utils/code.go | 59 - utils/cookie/cookie.go | 59 - utils/file.go | 133 -- utils/json.go | 5 - utils/md5.go | 32 - utils/random.go | 25 - utils/slice.go | 10 - utils/version.go | 33 - 185 files changed, 14 insertions(+), 30438 deletions(-) delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/build_docker.yml delete mode 100644 .github/workflows/release.yml delete mode 100644 .github/workflows/release_docker.yml delete mode 100644 Dockerfile delete mode 100644 README_cn.md delete mode 100644 alist.go delete mode 100644 bootstrap/account.go delete mode 100644 bootstrap/cache.go delete mode 100644 bootstrap/conf.go delete mode 100644 bootstrap/cron.go delete mode 100644 bootstrap/log.go delete mode 100644 bootstrap/model.go delete mode 100644 bootstrap/setting.go delete mode 100644 build.sh create mode 100644 cmd/alist.go delete mode 100644 conf/config.go delete mode 100644 conf/const.go delete mode 100644 conf/var.go delete mode 100644 drivers/123/123.go delete mode 100644 drivers/123/driver.go delete mode 100644 drivers/123/types.go delete mode 100644 drivers/139/139.go delete mode 100644 drivers/139/driver.go delete mode 100644 drivers/139/family.go delete mode 100644 drivers/139/types.go delete mode 100644 drivers/139/util.go delete mode 100644 drivers/189/189.go delete mode 100644 drivers/189/driver.go delete mode 100644 drivers/189/types.go delete mode 100644 drivers/189/util.go delete mode 100644 drivers/189pc/189.go delete mode 100644 drivers/189pc/driver.go delete mode 100644 drivers/189pc/type.go delete mode 100644 drivers/189pc/util.go delete mode 100644 drivers/alidrive/alidrive.go delete mode 100644 drivers/alidrive/driver.go delete mode 100644 drivers/alidrive/types.go delete mode 100644 drivers/alist/alist.go delete mode 100644 drivers/alist/driver.go delete mode 100644 drivers/all.go delete mode 100644 drivers/baidu/baidu.go delete mode 100644 drivers/baidu/driver.go delete mode 100644 drivers/baidu/types.go delete mode 100644 drivers/baidu/util.go delete mode 100644 drivers/baiduphoto/baidu.go delete mode 100644 drivers/baiduphoto/driver.go delete mode 100644 drivers/baiduphoto/types.go delete mode 100644 drivers/baiduphoto/util.go delete mode 100644 drivers/base/base.go delete mode 100644 drivers/base/cache.go delete mode 100644 drivers/base/driver.go delete mode 100644 drivers/base/types.go delete mode 100644 drivers/ftp/driver.go delete mode 100644 drivers/ftp/ftp.go delete mode 100644 drivers/google/driver.go delete mode 100644 drivers/google/googledrive.go delete mode 100644 drivers/google/types.go delete mode 100644 drivers/lanzou/driver.go delete mode 100644 drivers/lanzou/lanzou.go delete mode 100644 drivers/lanzou/types.go delete mode 100644 drivers/mediatrack/driver.go delete mode 100644 drivers/mediatrack/mediatrack.go delete mode 100644 drivers/native/driver.go delete mode 100644 drivers/native/native.go delete mode 100644 drivers/onedrive/driver.go delete mode 100644 drivers/onedrive/onedrive.go delete mode 100644 drivers/operate/operate.go delete mode 100644 drivers/pikpak/driver.go delete mode 100644 drivers/pikpak/pikpak.go delete mode 100644 drivers/quark/driver.go delete mode 100644 drivers/quark/quark.go delete mode 100644 drivers/quark/types.go delete mode 100644 drivers/quark/util.go delete mode 100644 drivers/s3/driver.go delete mode 100644 drivers/s3/s3.go delete mode 100644 drivers/s3/util.go delete mode 100644 drivers/sftp/driver.go delete mode 100644 drivers/sftp/sftp.go delete mode 100644 drivers/sftp/types.go delete mode 100644 drivers/sftp/util.go delete mode 100644 drivers/shandian/driver.go delete mode 100644 drivers/shandian/shandian.go delete mode 100644 drivers/teambition/driver.go delete mode 100644 drivers/teambition/teambition.go delete mode 100644 drivers/teambition/types.go delete mode 100644 drivers/teambition/util.go delete mode 100644 drivers/template/driver.go delete mode 100644 drivers/template/template.go delete mode 100644 drivers/template/types.go delete mode 100644 drivers/template/util.go delete mode 100644 drivers/uss/driver.go delete mode 100644 drivers/uss/uss.go delete mode 100644 drivers/webdav/driver.go delete mode 100644 drivers/webdav/odrvcookie/cookie.go delete mode 100644 drivers/webdav/odrvcookie/fetch.go delete mode 100644 drivers/webdav/util.go delete mode 100644 drivers/webdav/webdav.go delete mode 100644 drivers/xunlei/driver.go delete mode 100644 drivers/xunlei/types.go delete mode 100644 drivers/xunlei/util.go delete mode 100644 drivers/xunlei/xunlei.go delete mode 100644 drivers/yandex/driver.go delete mode 100644 drivers/yandex/types.go delete mode 100644 drivers/yandex/util.go delete mode 100644 drivers/yandex/yandex.go delete mode 100644 go.sum delete mode 100644 model/account.go delete mode 100644 model/file.go delete mode 100644 model/file_stream.go delete mode 100644 model/meta.go delete mode 100644 model/search_file.go delete mode 100644 model/setting.go delete mode 100644 model/util.go delete mode 100644 pkg/gowebdav/.gitignore delete mode 100644 pkg/gowebdav/.travis.yml delete mode 100644 pkg/gowebdav/LICENSE delete mode 100644 pkg/gowebdav/Makefile delete mode 100644 pkg/gowebdav/README.md delete mode 100644 pkg/gowebdav/basicAuth.go delete mode 100644 pkg/gowebdav/client.go delete mode 100644 pkg/gowebdav/cmd/gowebdav/README.md delete mode 100644 pkg/gowebdav/cmd/gowebdav/main.go delete mode 100644 pkg/gowebdav/digestAuth.go delete mode 100644 pkg/gowebdav/doc.go delete mode 100644 pkg/gowebdav/errors.go delete mode 100644 pkg/gowebdav/file.go delete mode 100644 pkg/gowebdav/netrc.go delete mode 100644 pkg/gowebdav/requests.go delete mode 100644 pkg/gowebdav/utils.go delete mode 100644 pkg/gowebdav/utils_test.go delete mode 100644 public/public.go delete mode 100644 server/common/check.go delete mode 100644 server/common/common.go delete mode 100644 server/common/files.go delete mode 100644 server/common/proxy.go delete mode 100644 server/controllers/account.go delete mode 100644 server/controllers/cache.go delete mode 100644 server/controllers/down.go delete mode 100644 server/controllers/driver.go delete mode 100644 server/controllers/file/copy.go delete mode 100644 server/controllers/file/delete.go delete mode 100644 server/controllers/file/folder.go delete mode 100644 server/controllers/file/mkdir.go delete mode 100644 server/controllers/file/move.go delete mode 100644 server/controllers/file/raname.go delete mode 100644 server/controllers/file/refresh.go delete mode 100644 server/controllers/file/upload.go delete mode 100644 server/controllers/link.go delete mode 100644 server/controllers/meta.go delete mode 100644 server/controllers/other.go delete mode 100644 server/controllers/path.go delete mode 100644 server/controllers/proxy.go delete mode 100644 server/controllers/search.go delete mode 100644 server/controllers/setting.go delete mode 100644 server/middlewares/account.go delete mode 100644 server/middlewares/auth.go delete mode 100644 server/middlewares/down.go delete mode 100644 server/middlewares/path.go delete mode 100644 server/router.go delete mode 100644 server/static.go delete mode 100644 server/webdav.go delete mode 100644 server/webdav/file.go delete mode 100644 server/webdav/if.go delete mode 100644 server/webdav/internal/xml/README delete mode 100644 server/webdav/internal/xml/marshal.go delete mode 100644 server/webdav/internal/xml/read.go delete mode 100644 server/webdav/internal/xml/typeinfo.go delete mode 100644 server/webdav/internal/xml/xml.go delete mode 100644 server/webdav/lock.go delete mode 100644 server/webdav/prop.go delete mode 100644 server/webdav/webdav.go delete mode 100644 server/webdav/xml.go delete mode 100644 utils/check.go delete mode 100644 utils/code.go delete mode 100644 utils/cookie/cookie.go delete mode 100644 utils/file.go delete mode 100644 utils/json.go delete mode 100644 utils/md5.go delete mode 100644 utils/random.go delete mode 100644 utils/slice.go delete mode 100644 utils/version.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 9fecea17..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: build - -on: - push: - branches: [ '**' ] - pull_request: - branches: [ '**' ] - -jobs: - build: - strategy: - matrix: - platform: [ubuntu-latest] - go-version: [1.18] - name: Build - runs-on: ${{ matrix.platform }} - steps: - - name: Setup Go - uses: actions/setup-go@v2 - with: - go-version: ${{ matrix.go-version }} - - - name: Set up Node - uses: actions/setup-node@v2 - with: - node-version: '16' - - - name: Checkout - uses: actions/checkout@v2 - with: - path: alist - - - name: Install dependencies - run: | - docker pull techknowlogick/xgo:latest - go install src.techknowlogick.com/xgo@latest - sudo apt install upx - - - name: Build - run: | - mv alist/build.sh . - bash build.sh web - mv dist/* alist/public - bash build.sh build - - - name: Upload artifact - uses: actions/upload-artifact@v2 - with: - name: artifact - path: alist/build \ No newline at end of file diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml deleted file mode 100644 index 5b3db5cb..00000000 --- a/.github/workflows/build_docker.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: build_docker - -on: - push: - branches: [ v2 ] - -jobs: - build_docker: - name: Docker - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Docker meta - id: meta - uses: docker/metadata-action@v3 - with: - images: xhofe/alist - - name: Set up Node - uses: actions/setup-node@v2 - with: - node-version: '16' - - name: Build web - run: | - bash build.sh web - mv dist/* public - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: xhofe - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - id: docker_build - uses: docker/build-push-action@v2 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 4edfa8c1..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: release - -on: - push: - tags: - - '*' - -jobs: - changelog: - name: Create Release - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Changelog - uses: Bullrich/generate-release-changelog@master - id: Changelog - env: - REPO: ${{ github.repository }} - - name: Create Release - id: create_release - uses: actions/create-release@latest - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - body: | - ${{ steps.Changelog.outputs.changelog }} - draft: false - prerelease: false - release: - needs: changelog - strategy: - matrix: - platform: [ubuntu-latest] - go-version: [1.18] - name: Release - runs-on: ${{ matrix.platform }} - steps: - - name: Setup Go - uses: actions/setup-go@v2 - with: - go-version: ${{ matrix.go-version }} - - - name: Checkout - uses: actions/checkout@v2 - with: - ref: v2 - path: alist - persist-credentials: false - fetch-depth: 0 - - - name: Install upx - run: | - docker pull techknowlogick/xgo:latest - go install src.techknowlogick.com/xgo@latest - sudo apt install upx - - - name: Build - run: | - mv alist/build.sh . - bash build.sh cdn - mv dist/* alist/public - bash build.sh release - - - name: Release - uses: softprops/action-gh-release@v1 - with: - files: alist/build/compress/* \ No newline at end of file diff --git a/.github/workflows/release_docker.yml b/.github/workflows/release_docker.yml deleted file mode 100644 index 66f8b7e1..00000000 --- a/.github/workflows/release_docker.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: release_docker - -on: - push: - tags: - - '*' - -jobs: - docker_release: - name: Docker - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Docker meta - id: meta - uses: docker/metadata-action@v3 - with: - images: xhofe/alist - - name: Set up Node - uses: actions/setup-node@v2 - with: - node-version: '16' - - name: Build web - run: | - bash build.sh cdn - mv dist/* public - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: xhofe - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - id: docker_build - uses: docker/build-push-action@v2 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55ad7910..fdcc4b13 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,12 +17,12 @@ Clone `alist` and `alist-web` anywhere: $ git clone https://github.com/Xhofe/alist.git $ git clone https://github.com/Xhofe/alist-web.git ``` -You should switch to the dev branch for development. +You should switch to the `main` branch for development. ## Preview your change ### backend ```shell -$ go run alist.go +$ go run cmd/alist.go ``` ### frontend ```shell @@ -102,4 +102,4 @@ The rest of the commit message is then used for this. ## Submit a pull request Push your branch to your `alist` fork and open a pull request against the -`dev` branch. +`main` branch. diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 2219a7ab..00000000 --- a/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM alpine:edge as builder -LABEL stage=go-builder -WORKDIR /app/ -COPY ./ ./ -RUN apk add --no-cache bash git go gcc musl-dev; \ - bash build.sh docker - -FROM alpine:edge -LABEL MAINTAINER="i@nn.ci" -VOLUME /opt/alist/data/ -WORKDIR /opt/alist/ -COPY --from=builder /app/bin/alist ./ -EXPOSE 5244 -CMD [ "./alist", "-docker" ] \ No newline at end of file diff --git a/README.md b/README.md index f7d51acc..da4ce43f 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
- logo -

🗂️Another file list program that supports multiple storage, powered by Gin and React.

+ logo +

🗂️A file list program that supports multiple storage, powered by Gin and React.

latest version discussions Build status @@ -13,64 +13,12 @@ --- -English | [中文](./README_cn.md) | [Contributors](./CONTRIBUTORS.md) | [Contributing](./CONTRIBUTING.md) - -## Features - -- [x] Multiple storage - - [x] Local storage - - [x] [Aliyundrive](https://www.aliyundrive.com/) - - [x] OneDrive / Sharepoint ([global](https://www.office.com/), [cn](https://portal.partner.microsoftonline.cn),de,us) - - [x] [189cloud](https://cloud.189.cn) (Personal, Family) - - [x] [GoogleDrive](https://drive.google.com/) - - [x] [123pan](https://www.123pan.com/) - - [x] [Lanzou](https://pc.woozooo.com/) - - [x] [Alist](https://github.com/Xhofe/alist) - - [x] FTP - - [x] [PikPak](https://www.mypikpak.com/) - - [x] [ShandianPan](https://shandianpan.com/) - - [x] [S3](https://aws.amazon.com/s3/) - - [x] WebDav(Support OneDrive/SharePoint without API) - - [x] Teambition([China](https://www.teambition.com/ ),[International](https://us.teambition.com/ )) - - [x] [Mediatrack](https://www.mediatrack.cn/) - - [x] [139yun](https://yun.139.com/) (Personal, Family) - - [x] [Yandex.Disk](https://disk.yandex.com/) - - [x] [Baidu Disk](http://pan.baidu.com/) - - [x] [Quark](https://pan.quark.cn) - - [x] [XunleiCloud](https://pan.xunlei.com/) - - [x] SFTP - - [x] [Baidu.Photo](https://photo.baidu.com/) -- [x] Easy to deploy and out-of-the-box -- [x] File preview (PDF, markdown, code, plain text, ...) -- [x] Image preview in gallery mode -- [x] Video and audio preview (mp4, mp3, ...) -- [x] Office documents preview (docx, pptx, xlsx, ...) -- [x] `README.md` preview rendering -- [x] File permalink copy and direct file download -- [x] Dark mode -- [x] I18n -- [x] Protected routes (password protection and authentication) -- [x] WebDav (see https://alist-doc.nn.ci/en/docs/webdav for details) -- [x] [Docker Deploy](https://hub.docker.com/r/xhofe/alist) -- [x] Cloudflare workers proxy -- [x] File/Folder package download -- [x] Support video list playback and subtitles(ass,srt,vtt) -- [x] Web upload(Can allow visitors to upload), delete, mkdir, rename, move and copy +[Contributors](./CONTRIBUTORS.md) | [Contributing](./CONTRIBUTING.md) ## Discussion Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports and feature request only.** -## Demo - -Available at: . - -![demo](https://store.heytapimage.com/cdo-portal/feedback/202202/20/b271627971e29f0c7c9d59935b6ef381.png) - -## Document - - - ## Special sponsors - [Find Resources - Aliyundrive Resource Search Engine](https://zhaoziyuan.la/) - [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/) diff --git a/README_cn.md b/README_cn.md deleted file mode 100644 index 1d387624..00000000 --- a/README_cn.md +++ /dev/null @@ -1,91 +0,0 @@ -
- logo -

🗂️一个支持多存储的文件列表程序,使用 Gin 和 React 。

- latest version - discussions - Build status - Downloads - License - - donate - -
- ---- - -[English](./README.md) | 中文 | [Contributors](./CONTRIBUTORS.md) | [Contributing](./CONTRIBUTING.md) - -## 支持 - -- [x] 多种存储 - - [x] 本地存储 - - [x] [阿里云盘](https://www.aliyundrive.com/) - - [x] OneDrive / Sharepoint([国际版](https://www.office.com/), [世纪互联](https://portal.partner.microsoftonline.cn),de,us) - - [x] [天翼云盘](https://cloud.189.cn) (个人云, 家庭云) - - [x] [GoogleDrive](https://drive.google.com/) - - [x] [123云盘](https://www.123pan.com/) - - [x] [蓝奏云](https://pc.woozooo.com/) - - [x] [Alist](https://github.com/Xhofe/alist) - - [x] FTP - - [x] [PikPak](https://www.mypikpak.com/) - - [x] [闪电盘](https://shandianpan.com/) - - [x] [S3](https://aws.amazon.com/cn/s3/) - - [x] WebDav(支持无API的OneDrive/SharePoint) - - [x] Teambition([中国](https://www.teambition.com/ ),[国际](https://us.teambition.com/ )) - - [x] [分秒帧](https://www.mediatrack.cn/) - - [x] [和彩云](https://yun.139.com/) (个人云, 家庭云) - - [x] [Yandex.Disk](https://disk.yandex.com/) - - [x] [百度网盘](http://pan.baidu.com/) - - [x] [夸克网盘](https://pan.quark.cn) - - [x] [迅雷云盘](https://pan.xunlei.com/) - - [x] SFTP - - [x] [一刻相册](https://photo.baidu.com/) -- [x] 部署方便,开箱即用 -- [x] 文件预览(PDF、markdown、代码、纯文本……) -- [x] 画廊模式下的图像预览 -- [x] 视频和音频预览(mp4、mp3 等) -- [x] Office 文档预览(docx、pptx、xlsx、...) -- [x] `README.md` 预览渲染 -- [x] 文件永久链接复制和直接文件下载 -- [x] 黑暗模式 -- [x] 国际化 -- [x] 受保护的路由(密码保护和身份验证) -- [x] WebDav(具体见https://alist-doc.nn.ci/docs/webdav ) -- [x] [Docker 部署](https://hub.docker.com/r/xhofe/alist) -- [x] Cloudflare workers 中转 -- [x] 文件/文件夹打包下载 -- [x] 支持视频列表播放和字幕(ass,srt,vtt) -- [x] 网页上传(可以允许访客上传),删除,新建文件夹,重命名,移动,复制 - -## 讨论 - -一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) ,**issue仅针对错误报告和功能请求。** - -## 演示 - -。 - -![演示](https://store.heytapimage.com/cdo-portal/feedback/202202/20/b271627971e29f0c7c9d59935b6ef381.png) - -## 文档 - - - -## 特别赞助 -- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/) -- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/) - -## 许可 - -`AList` 是在 AGPL-3.0 许可下许可的开源软件。 - -## 免责声明 -- 本程序为免费开源项目,旨在分享网盘文件,方便下载以及学习golang,使用时请遵守相关法律法规,请勿滥用; -- 本程序通过调用官方sdk/接口实现,无破坏官方接口行为; -- 本程序仅做302重定向/流量转发,不拦截、存储、篡改任何用户数据; -- 在使用本程序之前,你应了解并承担相应的风险,包括但不限于账号被ban,下载限速等,与本程序无关; -- 如有侵权,请通过[邮件](mailto:i@nn.ci)与我联系,会及时处理。 - ---- - -> [@博客](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@Telegram群](https://t.me/alist_chat) · [@QQ群](https://jq.qq.com/?_wv=1027&k=YJJj2Gwb) \ No newline at end of file diff --git a/alist.go b/alist.go deleted file mode 100644 index 84514efa..00000000 --- a/alist.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "fmt" - "github.com/Xhofe/alist/bootstrap" - "github.com/Xhofe/alist/conf" - _ "github.com/Xhofe/alist/drivers" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/server" - "github.com/gin-gonic/gin" - log "github.com/sirupsen/logrus" -) - -func Init() bool { - bootstrap.InitConf() - bootstrap.InitCron() - bootstrap.InitModel() - if conf.Password { - pass, err := model.GetSettingByKey("password") - if err != nil { - log.Errorf(err.Error()) - return false - } - fmt.Printf("your password: %s\n", pass.Value) - return false - } - server.InitIndex() - bootstrap.InitSettings() - bootstrap.InitAccounts() - bootstrap.InitCache() - return true -} - -func main() { - if conf.Version { - fmt.Printf("Built At: %s\nGo Version: %s\nAuthor: %s\nCommit ID: %s\nVersion: %s\nWebVersion: %s\n", - conf.BuiltAt, conf.GoVersion, conf.GitAuthor, conf.GitCommit, conf.GitTag, conf.WebTag) - return - } - if !Init() { - return - } - if !conf.Debug { - gin.SetMode(gin.ReleaseMode) - } - r := gin.Default() - server.InitApiRouter(r) - base := fmt.Sprintf("%s:%d", conf.Conf.Address, conf.Conf.Port) - log.Infof("start server @ %s", base) - var err error - if conf.Conf.Scheme.Https { - err = r.RunTLS(base, conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile) - } else { - err = r.Run(base) - } - if err != nil { - log.Errorf("failed to start: %s", err.Error()) - } -} diff --git a/bootstrap/account.go b/bootstrap/account.go deleted file mode 100644 index 8766e43a..00000000 --- a/bootstrap/account.go +++ /dev/null @@ -1,33 +0,0 @@ -package bootstrap - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/drivers/operate" - "github.com/Xhofe/alist/model" - log "github.com/sirupsen/logrus" -) - -func InitAccounts() { - log.Infof("init accounts...") - var accounts []model.Account - if err := conf.DB.Find(&accounts).Error; err != nil { - log.Fatalf("failed sync init accounts") - } - for i, account := range accounts { - model.RegisterAccount(account) - driver, ok := base.GetDriver(account.Type) - if !ok { - log.Errorf("no [%s] driver", account.Type) - } else { - log.Infof("start init account: [%s], type: [%s]", account.Name, account.Type) - //err := driver.Save(&accounts[i], nil) - err := operate.Save(driver, &accounts[i], nil) - if err != nil { - log.Errorf("init account [%s] error:[%s]", account.Name, err.Error()) - } else { - log.Infof("success init account: %s, type: %s", account.Name, account.Type) - } - } - } -} diff --git a/bootstrap/cache.go b/bootstrap/cache.go deleted file mode 100644 index 7d9dfba1..00000000 --- a/bootstrap/cache.go +++ /dev/null @@ -1,22 +0,0 @@ -package bootstrap - -import ( - "github.com/Xhofe/alist/conf" - "github.com/eko/gocache/v2/cache" - "github.com/eko/gocache/v2/store" - goCache "github.com/patrickmn/go-cache" - log "github.com/sirupsen/logrus" - "time" -) - -// InitCache init cache -func InitCache() { - log.Infof("init cache...") - c := conf.Conf.Cache - if c.Expiration == 0 { - c.Expiration, c.CleanupInterval = 60, 120 - } - goCacheClient := goCache.New(time.Duration(c.Expiration)*time.Minute, time.Duration(c.CleanupInterval)*time.Minute) - goCacheStore := store.NewGoCache(goCacheClient, nil) - conf.Cache = cache.New(goCacheStore) -} diff --git a/bootstrap/conf.go b/bootstrap/conf.go deleted file mode 100644 index 5c9a1b37..00000000 --- a/bootstrap/conf.go +++ /dev/null @@ -1,71 +0,0 @@ -package bootstrap - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/utils" - "github.com/caarlos0/env/v6" - log "github.com/sirupsen/logrus" - "io/ioutil" - "os" - "path/filepath" -) - -// InitConf init config -func InitConf() { - log.Infof("reading config file: %s", conf.ConfigFile) - if !utils.Exists(conf.ConfigFile) { - log.Infof("config file not exists, creating default config file") - _, err := utils.CreatNestedFile(conf.ConfigFile) - if err != nil { - log.Fatalf("failed to create config file") - } - conf.Conf = conf.DefaultConfig() - if !utils.WriteToJson(conf.ConfigFile, conf.Conf) { - log.Fatalf("failed to create default config file") - } - } else { - config, err := ioutil.ReadFile(conf.ConfigFile) - if err != nil { - log.Fatalf("reading config file error:%s", err.Error()) - } - conf.Conf = conf.DefaultConfig() - err = utils.Json.Unmarshal(config, conf.Conf) - if err != nil { - log.Fatalf("load config error: %s", err.Error()) - } - log.Debugf("config:%+v", conf.Conf) - // update config.json struct - confBody, err := utils.Json.MarshalIndent(conf.Conf, "", " ") - if err != nil { - log.Fatalf("marshal config error:%s", err.Error()) - } - err = ioutil.WriteFile(conf.ConfigFile, confBody, 0777) - if err != nil { - log.Fatalf("update config struct error: %s", err.Error()) - } - } - if !conf.Conf.Force { - confFromEnv() - } - err := os.RemoveAll(filepath.Join(conf.Conf.TempDir)) - if err != nil { - log.Errorln("failed delete temp file:", err) - } - err = os.MkdirAll(conf.Conf.TempDir, 0700) - if err != nil { - log.Fatalf("create temp dir error: %s", err.Error()) - } - log.Debugf("config: %+v", conf.Conf) -} - -func confFromEnv() { - prefix := "ALIST_" - if conf.Docker { - prefix = "" - } - if err := env.Parse(conf.Conf, env.Options{ - Prefix: prefix, - }); err != nil { - log.Fatalf("load config from env error: %s", err.Error()) - } -} diff --git a/bootstrap/cron.go b/bootstrap/cron.go deleted file mode 100644 index 6effc3d5..00000000 --- a/bootstrap/cron.go +++ /dev/null @@ -1,14 +0,0 @@ -package bootstrap - -import ( - "github.com/Xhofe/alist/conf" - "github.com/robfig/cron/v3" - log "github.com/sirupsen/logrus" -) - -// InitCron init cron -func InitCron() { - log.Infof("init cron...") - conf.Cron = cron.New() - conf.Cron.Start() -} diff --git a/bootstrap/log.go b/bootstrap/log.go deleted file mode 100644 index 45bb418b..00000000 --- a/bootstrap/log.go +++ /dev/null @@ -1,36 +0,0 @@ -package bootstrap - -import ( - "flag" - "github.com/Xhofe/alist/conf" - log "github.com/sirupsen/logrus" -) - -// InitLog init log -func InitLog() { - if conf.Debug { - log.SetLevel(log.DebugLevel) - log.SetReportCaller(true) - } - if conf.Password || conf.Version { - log.SetLevel(log.WarnLevel) - } - log.SetFormatter(&log.TextFormatter{ - //DisableColors: true, - ForceColors: true, - EnvironmentOverrideColors: true, - TimestampFormat: "2006-01-02 15:04:05", - FullTimestamp: true, - }) - log.Infof("init log...") -} - -func init() { - flag.StringVar(&conf.ConfigFile, "conf", "data/config.json", "config file") - flag.BoolVar(&conf.Debug, "debug", false, "start with debug mode") - flag.BoolVar(&conf.Version, "version", false, "print version info") - flag.BoolVar(&conf.Password, "password", false, "print current password") - flag.BoolVar(&conf.Docker, "docker", false, "is using docker") - flag.Parse() - InitLog() -} diff --git a/bootstrap/model.go b/bootstrap/model.go deleted file mode 100644 index e199f116..00000000 --- a/bootstrap/model.go +++ /dev/null @@ -1,84 +0,0 @@ -package bootstrap - -import ( - "fmt" - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/model" - log "github.com/sirupsen/logrus" - "gorm.io/driver/mysql" - "gorm.io/driver/postgres" - "gorm.io/driver/sqlite" - "gorm.io/gorm" - "gorm.io/gorm/logger" - "gorm.io/gorm/schema" - log2 "log" - "os" - "strings" - "time" -) - -func InitModel() { - log.Infof("init model...") - var err error - databaseConfig := conf.Conf.Database - newLogger := logger.New( - log2.New(os.Stdout, "\r\n", log2.LstdFlags), - logger.Config{ - SlowThreshold: time.Second, - LogLevel: logger.Silent, - IgnoreRecordNotFoundError: true, - Colorful: true, - }, - ) - gormConfig := &gorm.Config{ - NamingStrategy: schema.NamingStrategy{ - TablePrefix: databaseConfig.TablePrefix, - }, - Logger: newLogger, - } - switch databaseConfig.Type { - case "sqlite3": - { - if !(strings.HasSuffix(databaseConfig.DBFile, ".db") && len(databaseConfig.DBFile) > 3) { - log.Fatalf("db name error.") - } - db, err := gorm.Open(sqlite.Open(databaseConfig.DBFile), gormConfig) - if err != nil { - log.Fatalf("failed to connect database:%s", err.Error()) - } - conf.DB = db - } - case "mysql": - { - dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&tls=%s", - databaseConfig.User, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Name, databaseConfig.SslMode) - db, err := gorm.Open(mysql.Open(dsn), gormConfig) - if err != nil { - log.Fatalf("failed to connect database:%s", err.Error()) - } - conf.DB = db - } - case "postgres": - { - dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=Asia/Shanghai", - databaseConfig.Host, databaseConfig.User, databaseConfig.Password, databaseConfig.Name, databaseConfig.Port, databaseConfig.SslMode) - db, err := gorm.Open(postgres.Open(dsn), gormConfig) - if err != nil { - log.Errorf("failed to connect database:%s", err.Error()) - } - conf.DB = db - } - default: - log.Fatalf("not supported database type: %s", databaseConfig.Type) - } - log.Infof("auto migrate model...") - if databaseConfig.Type == "mysql" { - err = conf.DB.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4"). - AutoMigrate(&model.SettingItem{}, &model.Account{}, &model.Meta{}, &model.SearchFile{}) - } else { - err = conf.DB.AutoMigrate(&model.SettingItem{}, &model.Account{}, &model.Meta{}, &model.SearchFile{}) - } - if err != nil { - log.Fatalf("failed to auto migrate: %s", err.Error()) - } -} diff --git a/bootstrap/setting.go b/bootstrap/setting.go deleted file mode 100644 index 0927682f..00000000 --- a/bootstrap/setting.go +++ /dev/null @@ -1,323 +0,0 @@ -package bootstrap - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - log "github.com/sirupsen/logrus" - "gorm.io/gorm" - "strings" -) - -func InitSettings() { - log.Infof("init settings...") - - err := model.SaveSetting(model.Version) - if err != nil { - log.Fatalf("failed write setting: %s", err.Error()) - } - - settings := []model.SettingItem{ - { - Key: "title", - Value: "Alist", - Description: "title", - Type: "string", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "password", - Value: utils.RandomStr(8), - Description: "password", - Type: "string", - Access: model.PRIVATE, - Group: model.BACK, - }, - { - Key: "logo", - Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/can_circle.svg", - Description: "logo", - Type: "string", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "favicon", - Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg", - Description: "favicon", - Type: "string", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "icon color", - Value: "#1890ff", - Description: "icon's color", - Type: "string", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "announcement", - Value: "This is a test announcement.", - Description: "announcement message (support markdown)", - Type: "text", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "text types", - Value: strings.Join(conf.TextTypes, ","), - Type: "string", - Description: "text type extensions", - Group: model.FRONT, - }, - { - Key: "audio types", - Value: strings.Join(conf.AudioTypes, ","), - Type: "string", - Description: "audio type extensions", - Group: model.FRONT, - }, - { - Key: "video types", - Value: strings.Join(conf.VideoTypes, ","), - Type: "string", - Description: "video type extensions", - Group: model.FRONT, - }, - { - Key: "d_proxy types", - Value: strings.Join(conf.DProxyTypes, ","), - Type: "string", - Description: "/d but proxy", - Access: model.PRIVATE, - Group: model.BACK, - }, - { - Key: "hide files", - Value: "/\\/README.md/i", - Type: "text", - Description: "hide files, support RegExp, one per line", - Group: model.FRONT, - }, - { - Key: "music cover", - Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/circle_center.svg", - Description: "music cover image", - Type: "string", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "site beian", - Description: "chinese beian info", - Type: "string", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "global readme url", - Description: "Default display when directory has no readme", - Type: "string", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "pdf viewer url", - Type: "string", - Value: "https://alist-org.github.io/pdf.js/web/viewer.html?file=$url", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "autoplay video", - Value: "false", - Type: "bool", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "autoplay audio", - Value: "false", - Type: "bool", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "check parent folder", - Value: "false", - Type: "bool", - Description: "check parent folder password", - Access: model.PRIVATE, - Group: model.BACK, - }, - { - Key: "customize head", - Value: "", - Type: "text", - Description: "Customize head, placed at the beginning of the head", - Access: model.PRIVATE, - Group: model.FRONT, - }, - { - Key: "customize body", - Value: "", - Type: "text", - Description: "Customize script, placed at the end of the body", - Access: model.PRIVATE, - Group: model.FRONT, - }, - { - Key: "home emoji", - Value: "🏠", - Type: "string", - Description: "emoji in front of home in nav", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "animation", - Value: "true", - Type: "bool", - Description: "when there are a lot of files, the animation will freeze when opening", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "check down link", - Value: "false", - Type: "bool", - Description: "check down link password, your link will be 'https://alist.com/d/filename?pw=xxx'", - Access: model.PUBLIC, - Group: model.BACK, - }, - { - Key: "WebDAV username", - Value: "admin", - Description: "WebDAV username", - Type: "string", - Access: model.PRIVATE, - Group: model.BACK, - }, - { - Key: "WebDAV password", - Value: utils.RandomStr(8), - Description: "WebDAV password", - Type: "string", - Access: model.PRIVATE, - Group: model.BACK, - }, - { - Key: "artplayer whitelist", - Value: "*", - Description: "refer to https://artplayer.org/document/options#whitelist", - Type: "string", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "artplayer autoSize", - Value: "true", - Description: "refer to https://artplayer.org/document/options#autosize", - Type: "bool", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "Visitor WebDAV username", - Value: "guest", - Description: "Visitor WebDAV username", - Type: "string", - Access: model.PRIVATE, - Group: model.BACK, - }, - { - Key: "Visitor WebDAV password", - Value: "guest", - Description: "Visitor WebDAV password", - Type: "string", - Access: model.PRIVATE, - Group: model.BACK, - }, - { - Key: "load type", - Value: "all", - Type: "select", - Values: "all,load more,auto load more,pagination", - Description: "Not recommended to choose to auto load more, it has bugs now", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "default page size", - Value: "30", - Type: "number", - Access: model.PUBLIC, - Group: model.FRONT, - }, - { - Key: "ocr api", - Value: "https://api.nn.ci/ocr/file/json", - Description: "Used to identify verification codes", - Type: "string", - Access: model.PRIVATE, - Group: model.BACK, - }, - { - Key: "enable search", - Value: "false", - Type: "bool", - Access: model.PUBLIC, - Group: model.BACK, - Description: "Experimental function, not recommended as it's still under development", - }, - { - Key: "Aria2 RPC url", - Value: "http://localhost:6800/jsonrpc", - Description: "Aria2 RPC url, e.g. 'http://aria2.example.com:6800/jsonrpc'", - Type: "string", - Access: model.PRIVATE, - Group: model.BACK, - }, - { - Key: "Aria2 RPC secret", - Value: "", - Description: "Aria2 RPC secret, e.g. '123456'", - Type: "string", - Access: model.PRIVATE, - Group: model.BACK, - }, - } - for i, _ := range settings { - v := settings[i] - v.Version = conf.GitTag - o, err := model.GetSettingByKey(v.Key) - if err != nil { - if err == gorm.ErrRecordNotFound { - err = model.SaveSetting(v) - if v.Key == "password" { - log.Infof("Initial password: %s", conf.C.Sprintf(v.Value)) - } - if err != nil { - log.Fatalf("failed write setting: %s", err.Error()) - } - } else { - log.Fatalf("can't get setting: %s", err.Error()) - } - } else { - //o.Version = conf.GitTag - //err = model.SaveSetting(*o) - v.Value = o.Value - err = model.SaveSetting(v) - if err != nil { - log.Fatalf("failed write setting: %s", err.Error()) - } - if v.Key == "password" { - log.Infof("Your password: %s", conf.C.Sprintf(v.Value)) - } - } - } - model.LoadSettings() -} diff --git a/build.sh b/build.sh deleted file mode 100644 index 980a5282..00000000 --- a/build.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/bin/bash - -# 构建前端,在当前目录产生一个dist文件夹 -BUILD_WEB() { - git clone https://github.com/alist-org/alist-web.git - cd alist-web - yarn - yarn build - sed -i -e "s/\/CDN_URL\//\//g" dist/index.html - sed -i -e "s/assets/\/assets/g" dist/index.html - rm -f dist/index.html-e - mv dist .. - cd .. || exit - rm -rf alist-web -} - -CDN_WEB() { - curl -L https://github.com/alist-org/alist-web/releases/latest/download/dist.tar.gz -o dist.tar.gz - tar -zxvf dist.tar.gz - rm -f dist.tar.gz -} - -# 在DOCKER中构建 -BUILD_DOCKER() { - appName="alist" - builtAt="$(date +'%F %T %z')" - goVersion=$(go version | sed 's/go version //') - gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD) - gitCommit=$(git log --pretty=format:"%h" -1) - gitTag=$(git describe --long --tags --dirty --always) - webTag=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist-web/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g') - ldflags="\ --w -s \ --X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \ --X 'github.com/Xhofe/alist/conf.GoVersion=$goVersion' \ --X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \ --X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \ --X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \ --X 'github.com/Xhofe/alist/conf.WebTag=$webTag' \ - " - go build -o ./bin/alist -ldflags="$ldflags" -tags=jsoniter alist.go -} - -BUILD() { - cd alist - appName="alist" - builtAt="$(date +'%F %T %z')" - goVersion=$(go version | sed 's/go version //') - gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD) - gitCommit=$(git log --pretty=format:"%h" -1) - gitTag=$(git describe --long --tags --dirty --always) - webTag=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist-web/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g') - echo "build version: $gitTag" - - ldflags="\ --w -s \ --X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \ --X 'github.com/Xhofe/alist/conf.GoVersion=$goVersion' \ --X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \ --X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \ --X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \ --X 'github.com/Xhofe/alist/conf.WebTag=$webTag' \ -" - rm -rf .git/ - if [ "$1" == "release" ]; then - xgo -out "$appName" -ldflags="$ldflags" -tags=jsoniter . - else - xgo -targets=linux/amd64,windows/amd64,darwin/amd64 -out "$appName" -ldflags="$ldflags" -tags=jsoniter . - fi - mkdir -p "build" - mv alist-* build - if [ "$1" != "release" ]; then - cd build - upx -9 ./alist-linux* - upx -9 ./alist-windows* - find . -type f -print0 | xargs -0 md5sum >md5.txt - cat md5.txt - cd .. || exit - fi - cd .. || exit -} - -BUILD_MUSL() { - BASE="https://musl.cc/" - FILES=(x86_64-linux-musl-cross aarch64-linux-musl-cross arm-linux-musleabihf-cross mips-linux-musl-cross mips64-linux-musl-cross mips64el-linux-musl-cross mipsel-linux-musl-cross powerpc64le-linux-musl-cross s390x-linux-musl-cross) - for i in "${FILES[@]}"; do - url="${BASE}${i}.tgz" - curl -L -o "${i}.tgz" "${url}" - sudo tar xf "${i}.tgz" --strip-components 1 -C /usr/local - done - cd alist - appName="alist" - builtAt="$(date +'%F %T %z')" - goVersion=$(go version | sed 's/go version //') - gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD) - gitCommit=$(git log --pretty=format:"%h" -1) - gitTag=$(git describe --long --tags --dirty --always) - webTag=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist-web/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g') - ldflags="\ --w -s --extldflags '-static -fpic' \ --X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \ --X 'github.com/Xhofe/alist/conf.GoVersion=$goVersion' \ --X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \ --X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \ --X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \ --X 'github.com/Xhofe/alist/conf.WebTag=$webTag' \ - " - OS_ARCHES=(linux-musl-amd64 linux-musl-arm64 linux-musl-arm linux-musl-mips linux-musl-mips64 linux-musl-mips64le linux-musl-mipsle linux-musl-ppc64le linux-musl-s390x) - CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc arm-linux-musleabihf-gcc mips-linux-musl-gcc mips64-linux-musl-gcc mips64el-linux-musl-gcc mipsel-linux-musl-gcc powerpc64le-linux-musl-gcc s390x-linux-musl-gcc) - for i in "${!OS_ARCHES[@]}"; do - os_arch=${OS_ARCHES[$i]} - cgo_cc=${CGO_ARGS[$i]} - echo building for ${os_arch} - export GOOS=${os_arch%%-*} - export GOARCH=${os_arch##*-} - export CC=${cgo_cc} - export CGO_ENABLED=1 - go build -o ./build/$appName-$os_arch -ldflags="$ldflags" -tags=jsoniter alist.go - done - cd .. || exit -} - -RELEASE() { - cd alist/build - upx -9 ./alist-linux-amd64 - upx -9 ./alist-windows* - find . -type f -print0 | xargs -0 md5sum >md5.txt - cat md5.txt - mkdir compress - mv md5.txt compress - for i in $(find . -type f -name "$appName-linux-*"); do - tar -czvf compress/"$i".tar.gz "$i" - done - for i in $(find . -type f -name "$appName-darwin-*"); do - tar -czvf compress/"$i".tar.gz "$i" - done - for i in $(find . -type f -name "$appName-windows-*"); do - zip compress/$(echo $i | sed 's/\.[^.]*$//').zip "$i" - done - cd ../.. || exit -} - -if [ "$1" = "web" ]; then - BUILD_WEB -elif [ "$1" = "cdn" ]; then - CDN_WEB -elif [ "$1" = "docker" ]; then - BUILD_DOCKER -elif [ "$1" = "build" ]; then - BUILD build -elif [ "$1" = "release" ]; then - BUILD_MUSL - BUILD release - RELEASE -else - echo -e "${RED_COLOR} Parameter error ${RES}" -fi \ No newline at end of file diff --git a/cmd/alist.go b/cmd/alist.go new file mode 100644 index 00000000..91cca4fb --- /dev/null +++ b/cmd/alist.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Hello, world.") +} diff --git a/conf/config.go b/conf/config.go deleted file mode 100644 index 7d4c0fe0..00000000 --- a/conf/config.go +++ /dev/null @@ -1,54 +0,0 @@ -package conf - -type Database struct { - Type string `json:"type" env:"DB_TYPE"` - Host string `json:"host" env:"DB_HOST"` - Port int `json:"port" env:"DB_PORT"` - User string `json:"user" env:"DB_USER"` - Password string `json:"password" env:"DB_PASS"` - Name string `json:"name" env:"DB_NAME"` - DBFile string `json:"db_file" env:"DB_FILE"` - TablePrefix string `json:"table_prefix" env:"DB_TABLE_PREFIX"` - SslMode string `json:"ssl_mode" env:"DB_SLL_MODE"` -} - -type Scheme struct { - Https bool `json:"https" env:"HTTPS"` - CertFile string `json:"cert_file" env:"CERT_FILE"` - KeyFile string `json:"key_file" env:"KEY_FILE"` -} - -type CacheConfig struct { - Expiration int64 `json:"expiration" env:"CACHE_EXPIRATION"` - CleanupInterval int64 `json:"cleanup_interval" env:"CLEANUP_INTERVAL"` -} - -type Config struct { - Force bool `json:"force"` - Address string `json:"address" env:"ADDR"` - Port int `json:"port" env:"PORT"` - Assets string `json:"assets" env:"ASSETS"` - Database Database `json:"database"` - Scheme Scheme `json:"scheme"` - Cache CacheConfig `json:"cache"` - TempDir string `json:"temp_dir" env:"TEMP_DIR"` -} - -func DefaultConfig() *Config { - return &Config{ - Address: "0.0.0.0", - Port: 5244, - Assets: "https://npm.elemecdn.com/alist-web@$version/dist", - TempDir: "data/temp", - Database: Database{ - Type: "sqlite3", - Port: 0, - TablePrefix: "x_", - DBFile: "data/data.db", - }, - Cache: CacheConfig{ - Expiration: 60, - CleanupInterval: 120, - }, - } -} diff --git a/conf/const.go b/conf/const.go deleted file mode 100644 index 32336dc6..00000000 --- a/conf/const.go +++ /dev/null @@ -1,11 +0,0 @@ -package conf - -const ( - UNKNOWN = iota - FOLDER - OFFICE - VIDEO - AUDIO - TEXT - IMAGE -) \ No newline at end of file diff --git a/conf/var.go b/conf/var.go deleted file mode 100644 index 4efc92ea..00000000 --- a/conf/var.go +++ /dev/null @@ -1,104 +0,0 @@ -package conf - -import ( - "context" - "github.com/eko/gocache/v2/cache" - "github.com/fatih/color" - "github.com/robfig/cron/v3" - "gorm.io/gorm" - "strconv" -) - -var ( - BuiltAt string - GoVersion string - GitAuthor string - GitCommit string - GitTag string = "dev" - WebTag string -) - -var ( - ConfigFile string // config file - Conf *Config - Debug bool - Version bool - Password bool - Docker bool - - DB *gorm.DB - Cache *cache.Cache - Ctx = context.TODO() - Cron *cron.Cron - - C = color.New(color.FgHiBlue, color.Bold, color.BgHiWhite, color.Underline) -) - -var ( - TextTypes = []string{"txt", "htm", "html", "xml", "java", "properties", "sql", - "js", "md", "json", "conf", "ini", "vue", "php", "py", "bat", "gitignore", "yml", - "go", "sh", "c", "cpp", "h", "hpp", "tsx", "vtt", "srt", "ass"} - DProxyTypes = []string{"m3u8"} - OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"} - VideoTypes = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm", "flv", "m4v"} - AudioTypes = []string{"mp3", "flac", "ogg", "m4a", "wav", "opus"} - ImageTypes = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg", "ico", "swf", "webp"} -) - -var settingsMap = make(map[string]string) - -func Set(key string, value string) { - settingsMap[key] = value -} - -func GetStr(key string) string { - value, ok := settingsMap[key] - if !ok { - return "" - } - return value -} - -func GetBool(key string) bool { - value, ok := settingsMap[key] - if !ok { - return false - } - return value == "true" -} - -func GetInt(key string, defaultV int) int { - value, ok := settingsMap[key] - if !ok { - return defaultV - } - v, err := strconv.Atoi(value) - if err != nil { - return defaultV - } - return v -} - -var ( - LoadSettings = []string{ - "check parent folder", "check down link", "WebDAV username", "WebDAV password", - "Visitor WebDAV username", "Visitor WebDAV password", - "default page size", "load type", - "ocr api", "favicon", - "enable search", - } -) - -var ( - RawIndexHtml string - ManageHtml string - IndexHtml string - Token string - - //CheckParent bool - //CheckDown bool - //DavUsername string - //DavPassword string - //VisitorDavUsername string - //VisitorDavPassword string -) diff --git a/drivers/123/123.go b/drivers/123/123.go deleted file mode 100644 index 87f8cafe..00000000 --- a/drivers/123/123.go +++ /dev/null @@ -1,178 +0,0 @@ -package _23 - -import ( - "errors" - "fmt" - "path/filepath" - "strconv" - - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - jsoniter "github.com/json-iterator/go" - log "github.com/sirupsen/logrus" -) - -func (driver Pan123) Login(account *model.Account) error { - url := "https://www.123pan.com/api/user/sign_in" - if account.APIProxyUrl != "" { - url = fmt.Sprintf("%s/%s", account.APIProxyUrl, url) - } - var resp Pan123TokenResp - _, err := base.RestyClient.R(). - SetResult(&resp). - SetBody(base.Json{ - "passport": account.Username, - "password": account.Password, - }).Post(url) - if err != nil { - return err - } - if resp.Code != 200 { - err = fmt.Errorf(resp.Message) - account.Status = resp.Message - } else { - account.Status = "work" - account.AccessToken = resp.Data.Token - } - _ = model.SaveAccount(account) - return err -} - -func (driver Pan123) FormatFile(file *File) *model.File { - f := &model.File{ - Id: strconv.FormatInt(file.FileId, 10), - Name: file.FileName, - Size: file.Size, - Driver: driver.Config().Name, - UpdatedAt: file.UpdateAt, - Thumbnail: file.DownloadUrl, - } - f.Type = file.GetType() - return f -} - -func (driver Pan123) GetFiles(parentId string, account *model.Account) ([]File, error) { - next := "0" - res := make([]File, 0) - for next != "-1" { - var resp Pan123Files - query := map[string]string{ - "driveId": "0", - "limit": "100", - "next": next, - "orderBy": account.OrderBy, - "orderDirection": account.OrderDirection, - "parentFileId": parentId, - "trashed": "false", - } - _, err := driver.Request("https://www.123pan.com/api/file/list/new", - base.Get, nil, query, nil, &resp, false, account) - if err != nil { - return nil, err - } - next = resp.Data.Next - res = append(res, resp.Data.InfoList...) - } - return res, nil -} - -func (driver Pan123) Request(url string, method int, headers, query map[string]string, data *base.Json, resp interface{}, proxy bool, account *model.Account) ([]byte, error) { - rawUrl := url - if account.APIProxyUrl != "" && proxy { - url = fmt.Sprintf("%s/%s", account.APIProxyUrl, url) - } - req := base.RestyClient.R() - req.SetHeader("Authorization", "Bearer "+account.AccessToken) - if headers != nil { - req.SetHeaders(headers) - } - if query != nil { - req.SetQueryParams(query) - } - if data != nil { - req.SetBody(data) - } - if resp != nil { - req.SetResult(resp) - } - var res *resty.Response - var err error - switch method { - case base.Get: - res, err = req.Get(url) - case base.Post: - res, err = req.Post(url) - default: - return nil, base.ErrNotSupport - } - if err != nil { - return nil, err - } - log.Debug(res.String()) - body := res.Body() - code := jsoniter.Get(body, "code").ToInt() - if code != 0 { - if code == 401 { - err := driver.Login(account) - if err != nil { - return nil, err - } - return driver.Request(rawUrl, method, headers, query, data, resp, proxy, account) - } - return nil, errors.New(jsoniter.Get(body, "message").ToString()) - } - return body, nil -} - -//func (driver Pan123) Post(url string, data base.Json, account *model.Account) ([]byte, error) { -// res, err := pan123Client.R(). -// SetHeader("authorization", "Bearer "+account.AccessToken). -// SetBody(data).Post(url) -// if err != nil { -// return nil, err -// } -// body := res.Body() -// if jsoniter.Get(body, "code").ToInt() != 0 { -// return nil, errors.New(jsoniter.Get(body, "message").ToString()) -// } -// return body, nil -//} - -func (driver Pan123) GetFile(path string, account *model.Account) (*File, error) { - dir, name := filepath.Split(path) - dir = utils.ParsePath(dir) - _, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - parentFiles_, _ := base.GetCache(dir, account) - parentFiles, _ := parentFiles_.([]File) - for _, file := range parentFiles { - if file.FileName == name { - //if file.Type != conf.FOLDER { - // return &file, err - //} else { - // return nil, base.ErrNotFile - //} - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -//func HMAC(message string, secret string) string { -// key := []byte(secret) -// h := hmac.New(sha256.New, key) -// h.Write([]byte(message)) -// // fmt.Println(h.Sum(nil)) -// //sha := hex.EncodeToString(h.Sum(nil)) -// // fmt.Println(sha) -// //return sha -// return string(h.Sum(nil)) -//} - -func init() { - base.RegisterDriver(&Pan123{}) -} diff --git a/drivers/123/driver.go b/drivers/123/driver.go deleted file mode 100644 index 47c8b21b..00000000 --- a/drivers/123/driver.go +++ /dev/null @@ -1,460 +0,0 @@ -package _23 - -import ( - "bytes" - "crypto/md5" - "encoding/binary" - "encoding/hex" - "fmt" - "io" - "io/ioutil" - "net/url" - "os" - "path/filepath" - "strconv" - - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3/s3manager" - log "github.com/sirupsen/logrus" -) - -type Pan123 struct{} - -func (driver Pan123) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "123Pan", - } -} - -func (driver Pan123) Items() []base.Item { - return []base.Item{ - { - Name: "username", - Label: "username", - Type: base.TypeString, - Required: true, - Description: "account username/phone number", - }, - { - Name: "password", - Label: "password", - Type: base.TypeString, - Required: true, - Description: "account password", - }, - { - Name: "root_folder", - Label: "root folder file_id", - Type: base.TypeString, - Required: false, - }, - { - Name: "order_by", - Label: "order_by", - Type: base.TypeSelect, - Values: "name,fileId,updateAt,createAt", - Required: true, - Default: "name", - }, - { - Name: "order_direction", - Label: "order_direction", - Type: base.TypeSelect, - Values: "asc,desc", - Required: true, - Default: "asc", - }, - { - Name: "bool_1", - Label: "stream upload", - Type: base.TypeBool, - Description: "io stream upload (test)", - }, - } -} - -func (driver Pan123) Save(account *model.Account, old *model.Account) error { - if account == nil { - return nil - } - if account.RootFolder == "" { - account.RootFolder = "0" - } - err := driver.Login(account) - return err -} - -func (driver Pan123) File(path string, account *model.Account) (*model.File, error) { - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - dir, name := filepath.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver Pan123) Files(path string, account *model.Account) ([]model.File, error) { - path = utils.ParsePath(path) - var rawFiles []File - cache, err := base.GetCache(path, account) - if err == nil { - rawFiles, _ = cache.([]File) - } else { - file, err := driver.File(path, account) - if err != nil { - return nil, err - } - rawFiles, err = driver.GetFiles(file.Id, account) - if err != nil { - return nil, err - } - if len(rawFiles) > 0 { - _ = base.SetCache(path, rawFiles, account) - } - } - files := make([]model.File, 0, len(rawFiles)) - for _, file := range rawFiles { - files = append(files, *driver.FormatFile(&file)) - } - return files, nil -} - -func (driver Pan123) Link(args base.Args, account *model.Account) (*base.Link, error) { - log.Debugf("%+v", args) - file, err := driver.GetFile(utils.ParsePath(args.Path), account) - if err != nil { - return nil, err - } - var resp Pan123DownResp - var headers map[string]string - if !utils.IsLocalIPAddr(args.IP) { - headers = map[string]string{ - //"X-Real-IP": "1.1.1.1", - "X-Forwarded-For": args.IP, - } - } - data := base.Json{ - "driveId": 0, - "etag": file.Etag, - "fileId": file.FileId, - "fileName": file.FileName, - "s3keyFlag": file.S3KeyFlag, - "size": file.Size, - "type": file.Type, - } - _, err = driver.Request("https://www.123pan.com/api/file/download_info", - base.Post, headers, nil, &data, &resp, false, account) - //_, err = pan123Client.R().SetResult(&resp).SetHeader("authorization", "Bearer "+account.AccessToken). - // SetBody().Post("https://www.123pan.com/api/file/download_info") - if err != nil { - return nil, err - } - u, err := url.Parse(resp.Data.DownloadUrl) - if err != nil { - return nil, err - } - u_ := fmt.Sprintf("https://%s%s", u.Host, u.Path) - res, err := base.NoRedirectClient.R().SetQueryParamsFromValues(u.Query()).Get(u_) - if err != nil { - return nil, err - } - log.Debug(res.String()) - link := base.Link{ - Url: resp.Data.DownloadUrl, - } - if res.StatusCode() == 302 { - link.Url = res.Header().Get("location") - } - return &link, nil -} - -func (driver Pan123) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - log.Debugf("pan123 path: %s", path) - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -//func (driver Pan123) Proxy(r *http.Request, account *model.Account) { -// r.Header.Del("origin") -//} - -func (driver Pan123) Preview(path string, account *model.Account) (interface{}, error) { - return nil, base.ErrNotSupport -} - -func (driver Pan123) MakeDir(path string, account *model.Account) error { - dir, name := filepath.Split(path) - parentFile, err := driver.File(dir, account) - if err != nil { - return err - } - if !parentFile.IsDir() { - return base.ErrNotFolder - } - parentFileId, _ := strconv.Atoi(parentFile.Id) - data := base.Json{ - "driveId": 0, - "etag": "", - "fileName": name, - "parentFileId": parentFileId, - "size": 0, - "type": 1, - } - _, err = driver.Request("https://www.123pan.com/api/file/upload_request", - base.Post, nil, nil, &data, nil, false, account) - return err -} - -func (driver Pan123) Move(src string, dst string, account *model.Account) error { - dstDir, _ := filepath.Split(dst) - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - fileId, _ := strconv.Atoi(srcFile.Id) - - dstDirFile, err := driver.File(dstDir, account) - if err != nil { - return err - } - parentFileId, _ := strconv.Atoi(dstDirFile.Id) - data := base.Json{ - "fileIdList": []base.Json{{"FileId": fileId}}, - "parentFileId": parentFileId, - } - _, err = driver.Request("https://www.123pan.com/api/file/mod_pid", - base.Post, nil, nil, &data, nil, false, account) - return err -} - -func (driver Pan123) Rename(src string, dst string, account *model.Account) error { - _, dstName := filepath.Split(dst) - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - fileId, _ := strconv.Atoi(srcFile.Id) - - data := base.Json{ - "driveId": 0, - "fileId": fileId, - "fileName": dstName, - } - _, err = driver.Request("https://www.123pan.com/api/file/rename", - base.Post, nil, nil, &data, nil, false, account) - return err -} - -func (driver Pan123) Copy(src string, dst string, account *model.Account) error { - return base.ErrNotSupport -} - -func (driver Pan123) Delete(path string, account *model.Account) error { - file, err := driver.GetFile(path, account) - if err != nil { - return err - } - log.Debugln("delete 123 file: ", file) - data := base.Json{ - "driveId": 0, - "operation": true, - "fileTrashInfoList": []File{*file}, - } - _, err = driver.Request("https://www.123pan.com/b/api/file/trash", - base.Post, nil, nil, &data, nil, false, account) - return err -} - -func (driver Pan123) Upload(file *model.FileStream, account *model.Account) error { - if file == nil { - return base.ErrEmptyFile - } - parentFile, err := driver.File(file.ParentPath, account) - if err != nil { - return err - } - if !parentFile.IsDir() { - return base.ErrNotFolder - } - - const DEFAULT int64 = 10485760 - var uploadFile io.Reader - h := md5.New() - if account.Bool1 && file.GetSize() > uint64(DEFAULT) { - // 只计算前10MIB - buf := bytes.NewBuffer(make([]byte, 0, DEFAULT)) - if n, err := io.CopyN(io.MultiWriter(buf, h), file, DEFAULT); err != io.EOF && n == 0 { - return err - } - // 增加额外参数防止MD5碰撞 - h.Write([]byte(file.Name)) - num := make([]byte, 8) - binary.BigEndian.PutUint64(num, file.Size) - h.Write(num) - // 拼装 - uploadFile = io.MultiReader(buf, file) - } else { - // 计算完整文件MD5 - tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*") - if err != nil { - return err - } - defer func() { - _ = tempFile.Close() - _ = os.Remove(tempFile.Name()) - }() - - if _, err = io.Copy(io.MultiWriter(tempFile, h), file); err != nil { - return err - } - - _, err = tempFile.Seek(0, io.SeekStart) - if err != nil { - return err - } - uploadFile = tempFile - } - etag := hex.EncodeToString(h.Sum(nil)) - data := base.Json{ - "driveId": 0, - "duplicate": 2, // 2->覆盖 1->重命名 0->默认 - "etag": etag, - "fileName": file.GetFileName(), - "parentFileId": parentFile.Id, - "size": file.GetSize(), - "type": 0, - } - var resp UploadResp - _, err = driver.Request("https://www.123pan.com/api/file/upload_request", - base.Post, map[string]string{"app-version": "1.1"}, nil, &data, &resp, false, account) - //res, err := driver.Post("https://www.123pan.com/api/file/upload_request", data, account) - if err != nil { - return err - } - if resp.Data.Key == "" { - return nil - } - cfg := &aws.Config{ - Credentials: credentials.NewStaticCredentials(resp.Data.AccessKeyId, resp.Data.SecretAccessKey, resp.Data.SessionToken), - Region: aws.String("123pan"), - Endpoint: aws.String("file.123pan.com"), - S3ForcePathStyle: aws.Bool(true), - } - s, err := session.NewSession(cfg) - if err != nil { - return err - } - uploader := s3manager.NewUploader(s) - input := &s3manager.UploadInput{ - Bucket: &resp.Data.Bucket, - Key: &resp.Data.Key, - Body: uploadFile, - } - _, err = uploader.Upload(input) - if err != nil { - return err - } - _, err = driver.Request("https://www.123pan.com/api/file/upload_complete", base.Post, nil, nil, &base.Json{ - "fileId": resp.Data.FileId, - }, nil, false, account) - return err -} - -//type UploadResp struct { -// XMLName xml.Name `xml:"InitiateMultipartUploadResult"` -// Bucket string `xml:"Bucket"` -// Key string `xml:"Key"` -// UploadId string `xml:"UploadId"` -//} - -// TODO unfinished -//func (driver Pan123) Upload(file *model.FileStream, account *model.Account) error { -// return base.ErrNotImplement -// parentFile, err := driver.File(file.ParentPath, account) -// if err != nil { -// return err -// } -// if !parentFile.IsDir() { -// return base.ErrNotFolder -// } -// parentFileId, _ := strconv.Atoi(parentFile.Id) -// data := base.Json{ -// "driveId": 0, -// "duplicate": true, -// "etag": RandStr(32), //maybe file's md5 -// "fileName": file.GetFileName(), -// "parentFileId": parentFileId, -// "size": file.GetSize(), -// "type": 0, -// } -// res, err := driver.Request("https://www.123pan.com/api/file/upload_request", -// base.Post, nil, nil, &data, nil, false, account) -// //res, err := driver.Post("https://www.123pan.com/api/file/upload_request", data, account) -// if err != nil { -// return err -// } -// baseUrl := fmt.Sprintf("https://file.123pan.com/%s/%s", jsoniter.Get(res, "data.Bucket").ToString(), jsoniter.Get(res, "data.Key").ToString()) -// var resp UploadResp -// kSecret := jsoniter.Get(res, "data.SecretAccessKey").ToString() -// nowTimeStr := time.Now().String() -// Date := strings.ReplaceAll(strings.Split(nowTimeStr, "T")[0], "-", "") -// -// StringToSign := fmt.Sprintf("%s\n%s\n%s\n%s", -// "AWS4-HMAC-SHA256", -// nowTimeStr, -// fmt.Sprintf("%s/us-east-1/s3/aws4_request", Date), -// ) -// -// kDate := HMAC("AWS4"+kSecret, Date) -// kRegion := HMAC(kDate, "us-east-1") -// kService := HMAC(kRegion, "s3") -// kSigning := HMAC(kService, "aws4_request") -// _, err = base.RestyClient.R().SetResult(&resp).SetHeaders(map[string]string{ -// "Authorization": fmt.Sprintf("AWS4-HMAC-SHA256 Credential=%s/%s/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=%s", -// jsoniter.Get(res, "data.AccessKeyId"), -// Date, -// hex.EncodeToString([]byte(HMAC(StringToSign, kSigning)))), -// "X-Amz-Content-Sha256": "UNSIGNED-PAYLOAD", -// "X-Amz-Date": nowTimeStr, -// "x-amz-security-token": jsoniter.Get(res, "data.SessionToken").ToString(), -// }).Post(fmt.Sprintf("%s?uploads", baseUrl)) -// if err != nil { -// return err -// } -// return base.ErrNotImplement -//} - -var _ base.Driver = (*Pan123)(nil) diff --git a/drivers/123/types.go b/drivers/123/types.go deleted file mode 100644 index c464628c..00000000 --- a/drivers/123/types.go +++ /dev/null @@ -1,74 +0,0 @@ -package _23 - -import ( - "path" - "time" - - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/utils" -) - -type File struct { - FileName string `json:"FileName"` - Size int64 `json:"Size"` - UpdateAt *time.Time `json:"UpdateAt"` - FileId int64 `json:"FileId"` - Type int `json:"Type"` - Etag string `json:"Etag"` - S3KeyFlag string `json:"S3KeyFlag"` - DownloadUrl string `json:"DownloadUrl"` -} - -func (f File) GetSize() uint64 { - return uint64(f.Size) -} - -func (f File) GetName() string { - return f.FileName -} - -func (f File) GetType() int { - if f.Type == 1 { - return conf.FOLDER - } - return utils.GetFileType(path.Ext(f.FileName)) -} - -type BaseResp struct { - Code int `json:"code"` - Message string `json:"message"` -} - -type Pan123TokenResp struct { - BaseResp - Data struct { - Token string `json:"token"` - } `json:"data"` -} - -type Pan123Files struct { - BaseResp - Data struct { - InfoList []File `json:"InfoList"` - Next string `json:"Next"` - } `json:"data"` -} - -type Pan123DownResp struct { - BaseResp - Data struct { - DownloadUrl string `json:"DownloadUrl"` - } `json:"data"` -} - -type UploadResp struct { - BaseResp - Data struct { - AccessKeyId string `json:"AccessKeyId"` - Bucket string `json:"Bucket"` - Key string `json:"Key"` - SecretAccessKey string `json:"SecretAccessKey"` - SessionToken string `json:"SessionToken"` - FileId int64 `json:"FileId"` - } `json:"data"` -} diff --git a/drivers/139/139.go b/drivers/139/139.go deleted file mode 100644 index fb6a412b..00000000 --- a/drivers/139/139.go +++ /dev/null @@ -1,175 +0,0 @@ -package _39 - -import ( - "errors" - "fmt" - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - jsoniter "github.com/json-iterator/go" - log "github.com/sirupsen/logrus" - "path" - "time" -) - -func (driver Cloud139) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) { - url := "https://yun.139.com" + pathname - req := base.RestyClient.R() - randStr := utils.RandomStr(16) - ts := time.Now().Format("2006-01-02 15:04:05") - log.Debugf("%+v", data) - body, err := utils.Json.Marshal(data) - if err != nil { - return nil, err - } - sign := calSign(string(body), ts, randStr) - svcType := "1" - if isFamily(account) { - svcType = "2" - } - req.SetHeaders(map[string]string{ - "Accept": "application/json, text/plain, */*", - "CMS-DEVICE": "default", - "Cookie": account.AccessToken, - "mcloud-channel": "1000101", - "mcloud-client": "10701", - //"mcloud-route": "001", - "mcloud-sign": fmt.Sprintf("%s,%s,%s", ts, randStr, sign), - //"mcloud-skey":"", - "mcloud-version": "6.6.0", - "Origin": "https://yun.139.com", - "Referer": "https://yun.139.com/w/", - "x-DeviceInfo": "||9|6.6.0|chrome|95.0.4638.69|uwIy75obnsRPIwlJSd7D9GhUvFwG96ce||macos 10.15.2||zh-CN|||", - "x-huawei-channelSrc": "10000034", - "x-inner-ntwk": "2", - "x-m4c-caller": "PC", - "x-m4c-src": "10002", - "x-SvcType": svcType, - }) - if headers != nil { - req.SetHeaders(headers) - } - if query != nil { - req.SetQueryParams(query) - } - if form != nil { - req.SetFormData(form) - } - if data != nil { - req.SetBody(data) - } - var e BaseResp - //var err error - var res *resty.Response - req.SetResult(&e) - switch method { - case base.Get: - res, err = req.Get(url) - case base.Post: - res, err = req.Post(url) - case base.Delete: - res, err = req.Delete(url) - case base.Patch: - res, err = req.Patch(url) - case base.Put: - res, err = req.Put(url) - default: - return nil, base.ErrNotSupport - } - if err != nil { - return nil, err - } - log.Debugln(res.String()) - if !e.Success { - return nil, errors.New(e.Message) - } - if resp != nil { - err = utils.Json.Unmarshal(res.Body(), resp) - if err != nil { - return nil, err - } - } - return res.Body(), nil -} - -func (driver Cloud139) Post(pathname string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) { - return driver.Request(pathname, base.Post, nil, nil, nil, data, resp, account) -} - -func (driver Cloud139) GetFiles(catalogID string, account *model.Account) ([]model.File, error) { - start := 0 - limit := 100 - files := make([]model.File, 0) - for { - data := base.Json{ - "catalogID": catalogID, - "sortDirection": 1, - "startNumber": start + 1, - "endNumber": start + limit, - "filterType": 0, - "catalogSortType": 0, - "contentSortType": 0, - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - } - var resp GetDiskResp - _, err := driver.Post("/orchestration/personalCloud/catalog/v1.0/getDisk", data, &resp, account) - if err != nil { - return nil, err - } - for _, catalog := range resp.Data.GetDiskResult.CatalogList { - f := model.File{ - Id: catalog.CatalogID, - Name: catalog.CatalogName, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: getTime(catalog.UpdateTime), - } - files = append(files, f) - } - for _, content := range resp.Data.GetDiskResult.ContentList { - f := model.File{ - Id: content.ContentID, - Name: content.ContentName, - Size: content.ContentSize, - Type: utils.GetFileType(path.Ext(content.ContentName)), - Driver: driver.Config().Name, - UpdatedAt: getTime(content.UpdateTime), - Thumbnail: content.ThumbnailURL, - //Thumbnail: content.BigthumbnailURL, - } - files = append(files, f) - } - if start+limit >= resp.Data.GetDiskResult.NodeCount { - break - } - start += limit - } - return files, nil -} - -func (driver Cloud139) GetLink(contentId string, account *model.Account) (string, error) { - data := base.Json{ - "appName": "", - "contentID": contentId, - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - } - res, err := driver.Post("/orchestration/personalCloud/uploadAndDownload/v1.0/downloadRequest", - data, nil, account) - if err != nil { - return "", err - } - return jsoniter.Get(res, "data", "downloadURL").ToString(), nil -} - -func init() { - base.RegisterDriver(&Cloud139{}) -} diff --git a/drivers/139/driver.go b/drivers/139/driver.go deleted file mode 100644 index 8864787c..00000000 --- a/drivers/139/driver.go +++ /dev/null @@ -1,457 +0,0 @@ -package _39 - -import ( - "bytes" - "fmt" - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - log "github.com/sirupsen/logrus" - "io" - "math" - "net/http" - "path/filepath" - "strconv" -) - -type Cloud139 struct{} - -func (driver Cloud139) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "139Yun", - LocalSort: true, - } -} - -func (driver Cloud139) Items() []base.Item { - return []base.Item{ - { - Name: "username", - Label: "phone", - Type: base.TypeString, - Required: true, - Description: "phone number", - }, - { - Name: "access_token", - Label: "Cookie", - Type: base.TypeString, - Required: true, - Description: "Unknown expiration time", - }, - { - Name: "internal_type", - Label: "139yun type", - Type: base.TypeSelect, - Required: true, - Values: "Personal,Family", - }, - { - Name: "root_folder", - Label: "root folder file_id", - Type: base.TypeString, - Required: true, - }, - { - Name: "site_id", - Label: "cloud_id", - Type: base.TypeString, - Required: false, - }, - } -} - -func (driver Cloud139) Save(account *model.Account, old *model.Account) error { - if account == nil { - return nil - } - _, err := driver.Request("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Post, nil, nil, nil, base.Json{ - "qryUserExternInfoReq": base.Json{ - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - }, - }, nil, account) - return err -} - -func (driver Cloud139) File(path string, account *model.Account) (*model.File, error) { - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - dir, name := filepath.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver Cloud139) Files(path string, account *model.Account) ([]model.File, error) { - path = utils.ParsePath(path) - var files []model.File - cache, err := base.GetCache(path, account) - if err == nil { - files, _ = cache.([]model.File) - } else { - file, err := driver.File(path, account) - if err != nil { - return nil, err - } - if isFamily(account) { - files, err = driver.familyGetFiles(file.Id, account) - } else { - files, err = driver.GetFiles(file.Id, account) - } - if err != nil { - return nil, err - } - if len(files) > 0 { - _ = base.SetCache(path, files, account) - } - } - return files, nil -} - -func (driver Cloud139) Link(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(args.Path, account) - if err != nil { - return nil, err - } - var u string - //if isFamily(account) { - // u, err = driver.familyLink(file.Id, account) - //} else { - u, err = driver.GetLink(file.Id, account) - //} - if err != nil { - return nil, err - } - return &base.Link{Url: u}, nil -} - -func (driver Cloud139) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - log.Debugf("139 path: %s", path) - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -//func (driver Cloud139) Proxy(r *http.Request, account *model.Account) { -// -//} - -func (driver Cloud139) Preview(path string, account *model.Account) (interface{}, error) { - return nil, base.ErrNotSupport -} - -func (driver Cloud139) MakeDir(path string, account *model.Account) error { - parentFile, err := driver.File(utils.Dir(path), account) - if err != nil { - return err - } - data := base.Json{ - "createCatalogExtReq": base.Json{ - "parentCatalogID": parentFile.Id, - "newCatalogName": utils.Base(path), - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - }, - } - pathname := "/orchestration/personalCloud/catalog/v1.0/createCatalogExt" - if isFamily(account) { - data = base.Json{ - "cloudID": account.SiteId, - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - "docLibName": utils.Base(path), - } - pathname = "/orchestration/familyCloud/cloudCatalog/v1.0/createCloudDoc" - } - _, err = driver.Post(pathname, - data, nil, account) - return err -} - -func (driver Cloud139) Move(src string, dst string, account *model.Account) error { - if isFamily(account) { - return base.ErrNotSupport - } - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - dstParentFile, err := driver.File(utils.Dir(dst), account) - if err != nil { - return err - } - var contentInfoList []string - var catalogInfoList []string - if srcFile.IsDir() { - catalogInfoList = append(catalogInfoList, srcFile.Id) - } else { - contentInfoList = append(contentInfoList, srcFile.Id) - } - data := base.Json{ - "createBatchOprTaskReq": base.Json{ - "taskType": 3, - "actionType": "304", - "taskInfo": base.Json{ - "contentInfoList": contentInfoList, - "catalogInfoList": catalogInfoList, - "newCatalogID": dstParentFile.Id, - }, - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - }, - } - pathname := "/orchestration/personalCloud/batchOprTask/v1.0/createBatchOprTask" - _, err = driver.Post(pathname, data, nil, account) - return err -} - -func (driver Cloud139) Rename(src string, dst string, account *model.Account) error { - if isFamily(account) { - return base.ErrNotSupport - } - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - var data base.Json - var pathname string - if srcFile.IsDir() { - data = base.Json{ - "catalogID": srcFile.Id, - "catalogName": utils.Base(dst), - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - } - pathname = "/orchestration/personalCloud/catalog/v1.0/updateCatalogInfo" - } else { - data = base.Json{ - "contentID": srcFile.Id, - "contentName": utils.Base(dst), - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - } - pathname = "/orchestration/personalCloud/catalog/v1.0/updateContentInfo" - } - _, err = driver.Post(pathname, data, nil, account) - return err -} - -func (driver Cloud139) Copy(src string, dst string, account *model.Account) error { - if isFamily(account) { - return base.ErrNotSupport - } - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - dstParentFile, err := driver.File(utils.Dir(dst), account) - if err != nil { - return err - } - var contentInfoList []string - var catalogInfoList []string - if srcFile.IsDir() { - catalogInfoList = append(catalogInfoList, srcFile.Id) - } else { - contentInfoList = append(contentInfoList, srcFile.Id) - } - data := base.Json{ - "createBatchOprTaskReq": base.Json{ - "taskType": 3, - "actionType": 309, - "taskInfo": base.Json{ - "contentInfoList": contentInfoList, - "catalogInfoList": catalogInfoList, - "newCatalogID": dstParentFile.Id, - }, - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - }, - } - pathname := "/orchestration/personalCloud/batchOprTask/v1.0/createBatchOprTask" - _, err = driver.Post(pathname, data, nil, account) - return err -} - -func (driver Cloud139) Delete(path string, account *model.Account) error { - file, err := driver.File(path, account) - if err != nil { - return err - } - var contentInfoList []string - var catalogInfoList []string - if file.IsDir() { - catalogInfoList = append(catalogInfoList, file.Id) - } else { - contentInfoList = append(contentInfoList, file.Id) - } - data := base.Json{ - "createBatchOprTaskReq": base.Json{ - "taskType": 2, - "actionType": 201, - "taskInfo": base.Json{ - "newCatalogID": "", - "contentInfoList": contentInfoList, - "catalogInfoList": catalogInfoList, - }, - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - }, - } - pathname := "/orchestration/personalCloud/batchOprTask/v1.0/createBatchOprTask" - if isFamily(account) { - data = base.Json{ - "catalogList": catalogInfoList, - "contentList": contentInfoList, - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - "sourceCatalogType": 1002, - "taskType": 2, - } - pathname = "/orchestration/familyCloud/batchOprTask/v1.0/createBatchOprTask" - } - _, err = driver.Post(pathname, data, nil, account) - return err -} - -func (driver Cloud139) Upload(file *model.FileStream, account *model.Account) error { - if file == nil { - return base.ErrEmptyFile - } - parentFile, err := driver.File(file.ParentPath, account) - if err != nil { - return err - } - if !parentFile.IsDir() { - return base.ErrNotFolder - } - data := base.Json{ - "manualRename": 2, - "operation": 0, - "fileCount": 1, - "totalSize": file.Size, - "uploadContentList": []base.Json{{ - "contentName": file.Name, - "contentSize": file.Size, - // "digest": "5a3231986ce7a6b46e408612d385bafa" - }}, - "parentCatalogID": parentFile.Id, - "newCatalogName": "", - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - } - pathname := "/orchestration/personalCloud/uploadAndDownload/v1.0/pcUploadFileRequest" - if isFamily(account) { - data = newJson(base.Json{ - "fileCount": 1, - "manualRename": 2, - "operation": 0, - "path": "", - "seqNo": "", - "totalSize": file.Size, - "uploadContentList": []base.Json{{ - "contentName": file.Name, - "contentSize": file.Size, - // "digest": "5a3231986ce7a6b46e408612d385bafa" - }}, - }, account) - pathname = "/orchestration/familyCloud/content/v1.0/getFileUploadURL" - return base.ErrNotSupport - } - var resp UploadResp - _, err = driver.Post(pathname, data, &resp, account) - if err != nil { - return err - } - var Default uint64 = 10485760 - part := int(math.Ceil(float64(file.Size) / float64(Default))) - var start uint64 = 0 - for i := 0; i < part; i++ { - byteSize := file.Size - start - if byteSize > Default { - byteSize = Default - } - byteData := make([]byte, byteSize) - _, err = io.ReadFull(file, byteData) - if err != nil { - return err - } - req, err := http.NewRequest("POST", resp.Data.UploadResult.RedirectionURL, bytes.NewBuffer(byteData)) - if err != nil { - return err - } - headers := map[string]string{ - "Accept": "*/*", - "Content-Type": "text/plain;name=" + unicode(file.Name), - "contentSize": strconv.FormatUint(file.Size, 10), - "range": fmt.Sprintf("bytes=%d-%d", start, start+byteSize-1), - "content-length": strconv.FormatUint(byteSize, 10), - "uploadtaskID": resp.Data.UploadResult.UploadTaskID, - "rangeType": "0", - "Referer": "https://yun.139.com/", - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.44", - "x-SvcType": "1", - } - for k, v := range headers { - req.Header.Set(k, v) - } - res, err := base.HttpClient.Do(req) - if err != nil { - return err - } - log.Debugf("%+v", res) - res.Body.Close() - start += byteSize - } - return nil -} - -var _ base.Driver = (*Cloud139)(nil) diff --git a/drivers/139/family.go b/drivers/139/family.go deleted file mode 100644 index 05f80853..00000000 --- a/drivers/139/family.go +++ /dev/null @@ -1,74 +0,0 @@ -package _39 - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - jsoniter "github.com/json-iterator/go" - "path" -) - -func (driver Cloud139) familyGetFiles(catalogID string, account *model.Account) ([]model.File, error) { - pageNum := 1 - files := make([]model.File, 0) - for { - data := newJson(base.Json{ - "catalogID": catalogID, - "contentSortType": 0, - "pageInfo": base.Json{ - "pageNum": pageNum, - "pageSize": 100, - }, - "sortDirection": 1, - }, account) - - var resp QueryContentListResp - _, err := driver.Post("/orchestration/familyCloud/content/v1.0/queryContentList", data, &resp, account) - if err != nil { - return nil, err - } - for _, catalog := range resp.Data.CloudCatalogList { - f := model.File{ - Id: catalog.CatalogID, - Name: catalog.CatalogName, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: getTime(catalog.LastUpdateTime), - } - files = append(files, f) - } - for _, content := range resp.Data.CloudContentList { - f := model.File{ - Id: content.ContentID, - Name: content.ContentName, - Size: content.ContentSize, - Type: utils.GetFileType(path.Ext(content.ContentName)), - Driver: driver.Config().Name, - UpdatedAt: getTime(content.LastUpdateTime), - Thumbnail: content.ThumbnailURL, - //Thumbnail: content.BigthumbnailURL, - } - files = append(files, f) - } - if 100*pageNum > resp.Data.TotalCount { - break - } - pageNum++ - } - return files, nil -} - -func (driver Cloud139) familyLink(contentId string, account *model.Account) (string, error) { - data := newJson(base.Json{ - "contentID": contentId, - //"path":"", - }, account) - res, err := driver.Post("/orchestration/familyCloud/content/v1.0/getFileDownLoadURL", - data, nil, account) - if err != nil { - return "", err - } - return jsoniter.Get(res, "data", "downloadURL").ToString(), nil -} diff --git a/drivers/139/types.go b/drivers/139/types.go deleted file mode 100644 index 4973af26..00000000 --- a/drivers/139/types.go +++ /dev/null @@ -1,187 +0,0 @@ -package _39 - -type BaseResp struct { - Success bool `json:"success"` - Code string `json:"code"` - Message string `json:"message"` -} - -type Catalog struct { - CatalogID string `json:"catalogID"` - CatalogName string `json:"catalogName"` - //CatalogType int `json:"catalogType"` - //CreateTime string `json:"createTime"` - UpdateTime string `json:"updateTime"` - //IsShared bool `json:"isShared"` - //CatalogLevel int `json:"catalogLevel"` - //ShareDoneeCount int `json:"shareDoneeCount"` - //OpenType int `json:"openType"` - //ParentCatalogID string `json:"parentCatalogId"` - //DirEtag int `json:"dirEtag"` - //Tombstoned int `json:"tombstoned"` - //ProxyID interface{} `json:"proxyID"` - //Moved int `json:"moved"` - //IsFixedDir int `json:"isFixedDir"` - //IsSynced interface{} `json:"isSynced"` - //Owner string `json:"owner"` - //Modifier interface{} `json:"modifier"` - //Path string `json:"path"` - //ShareType int `json:"shareType"` - //SoftLink interface{} `json:"softLink"` - //ExtProp1 interface{} `json:"extProp1"` - //ExtProp2 interface{} `json:"extProp2"` - //ExtProp3 interface{} `json:"extProp3"` - //ExtProp4 interface{} `json:"extProp4"` - //ExtProp5 interface{} `json:"extProp5"` - //ETagOprType int `json:"ETagOprType"` -} - -type Content struct { - ContentID string `json:"contentID"` - ContentName string `json:"contentName"` - //ContentSuffix string `json:"contentSuffix"` - ContentSize int64 `json:"contentSize"` - //ContentDesc string `json:"contentDesc"` - //ContentType int `json:"contentType"` - //ContentOrigin int `json:"contentOrigin"` - UpdateTime string `json:"updateTime"` - //CommentCount int `json:"commentCount"` - ThumbnailURL string `json:"thumbnailURL"` - //BigthumbnailURL string `json:"bigthumbnailURL"` - //PresentURL string `json:"presentURL"` - //PresentLURL string `json:"presentLURL"` - //PresentHURL string `json:"presentHURL"` - //ContentTAGList interface{} `json:"contentTAGList"` - //ShareDoneeCount int `json:"shareDoneeCount"` - //Safestate int `json:"safestate"` - //Transferstate int `json:"transferstate"` - //IsFocusContent int `json:"isFocusContent"` - //UpdateShareTime interface{} `json:"updateShareTime"` - //UploadTime string `json:"uploadTime"` - //OpenType int `json:"openType"` - //AuditResult int `json:"auditResult"` - //ParentCatalogID string `json:"parentCatalogId"` - //Channel string `json:"channel"` - //GeoLocFlag string `json:"geoLocFlag"` - //Digest string `json:"digest"` - //Version string `json:"version"` - //FileEtag string `json:"fileEtag"` - //FileVersion string `json:"fileVersion"` - //Tombstoned int `json:"tombstoned"` - //ProxyID string `json:"proxyID"` - //Moved int `json:"moved"` - //MidthumbnailURL string `json:"midthumbnailURL"` - //Owner string `json:"owner"` - //Modifier string `json:"modifier"` - //ShareType int `json:"shareType"` - //ExtInfo struct { - // Uploader string `json:"uploader"` - // Address string `json:"address"` - //} `json:"extInfo"` - //Exif struct { - // CreateTime string `json:"createTime"` - // Longitude interface{} `json:"longitude"` - // Latitude interface{} `json:"latitude"` - // LocalSaveTime interface{} `json:"localSaveTime"` - //} `json:"exif"` - //CollectionFlag interface{} `json:"collectionFlag"` - //TreeInfo interface{} `json:"treeInfo"` - //IsShared bool `json:"isShared"` - //ETagOprType int `json:"ETagOprType"` -} - -type GetDiskResp struct { - BaseResp - Data struct { - Result struct { - ResultCode string `json:"resultCode"` - ResultDesc interface{} `json:"resultDesc"` - } `json:"result"` - GetDiskResult struct { - ParentCatalogID string `json:"parentCatalogID"` - NodeCount int `json:"nodeCount"` - CatalogList []Catalog `json:"catalogList"` - ContentList []Content `json:"contentList"` - IsCompleted int `json:"isCompleted"` - } `json:"getDiskResult"` - } `json:"data"` -} - -type UploadResp struct { - BaseResp - Data struct { - Result struct { - ResultCode string `json:"resultCode"` - ResultDesc interface{} `json:"resultDesc"` - } `json:"result"` - UploadResult struct { - UploadTaskID string `json:"uploadTaskID"` - RedirectionURL string `json:"redirectionUrl"` - NewContentIDList []struct { - ContentID string `json:"contentID"` - ContentName string `json:"contentName"` - IsNeedUpload string `json:"isNeedUpload"` - FileEtag int64 `json:"fileEtag"` - FileVersion int64 `json:"fileVersion"` - OverridenFlag int `json:"overridenFlag"` - } `json:"newContentIDList"` - CatalogIDList interface{} `json:"catalogIDList"` - IsSlice interface{} `json:"isSlice"` - } `json:"uploadResult"` - } `json:"data"` -} - -type CloudContent struct { - ContentID string `json:"contentID"` - //Modifier string `json:"modifier"` - //Nickname string `json:"nickname"` - //CloudNickName string `json:"cloudNickName"` - ContentName string `json:"contentName"` - //ContentType int `json:"contentType"` - //ContentSuffix string `json:"contentSuffix"` - ContentSize int64 `json:"contentSize"` - //ContentDesc string `json:"contentDesc"` - //CreateTime string `json:"createTime"` - //Shottime interface{} `json:"shottime"` - LastUpdateTime string `json:"lastUpdateTime"` - ThumbnailURL string `json:"thumbnailURL"` - //MidthumbnailURL string `json:"midthumbnailURL"` - //BigthumbnailURL string `json:"bigthumbnailURL"` - //PresentURL string `json:"presentURL"` - //PresentLURL string `json:"presentLURL"` - //PresentHURL string `json:"presentHURL"` - //ParentCatalogID string `json:"parentCatalogID"` - //Uploader string `json:"uploader"` - //UploaderNickName string `json:"uploaderNickName"` - //TreeInfo interface{} `json:"treeInfo"` - //UpdateTime interface{} `json:"updateTime"` - //ExtInfo struct { - // Uploader string `json:"uploader"` - //} `json:"extInfo"` - //EtagOprType interface{} `json:"etagOprType"` -} - -type CloudCatalog struct { - CatalogID string `json:"catalogID"` - CatalogName string `json:"catalogName"` - //CloudID string `json:"cloudID"` - //CreateTime string `json:"createTime"` - LastUpdateTime string `json:"lastUpdateTime"` - //Creator string `json:"creator"` - //CreatorNickname string `json:"creatorNickname"` -} - -type QueryContentListResp struct { - BaseResp - Data struct { - Result struct { - ResultCode string `json:"resultCode"` - ResultDesc string `json:"resultDesc"` - } `json:"result"` - Path string `json:"path"` - CloudContentList []CloudContent `json:"cloudContentList"` - CloudCatalogList []CloudCatalog `json:"cloudCatalogList"` - TotalCount int `json:"totalCount"` - RecallContent interface{} `json:"recallContent"` - } `json:"data"` -} diff --git a/drivers/139/util.go b/drivers/139/util.go deleted file mode 100644 index 8d513ab3..00000000 --- a/drivers/139/util.go +++ /dev/null @@ -1,70 +0,0 @@ -package _39 - -import ( - "encoding/base64" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "net/url" - "sort" - "strconv" - "strings" - "time" -) - -func encodeURIComponent(str string) string { - r := url.QueryEscape(str) - r = strings.Replace(r, "+", "%20", -1) - return r -} - -func calSign(body, ts, randStr string) string { - body = strings.ReplaceAll(body, "\n", "") - body = strings.ReplaceAll(body, " ", "") - body = encodeURIComponent(body) - strs := strings.Split(body, "") - sort.Strings(strs) - body = strings.Join(strs, "") - body = base64.StdEncoding.EncodeToString([]byte(body)) - res := utils.GetMD5Encode(body) + utils.GetMD5Encode(ts+":"+randStr) - res = strings.ToUpper(utils.GetMD5Encode(res)) - return res -} - -func getTime(t string) *time.Time { - stamp, _ := time.ParseInLocation("20060102150405", t, time.Local) - return &stamp -} - -func isFamily(account *model.Account) bool { - return account.InternalType == "Family" -} - -func unicode(str string) string { - textQuoted := strconv.QuoteToASCII(str) - textUnquoted := textQuoted[1 : len(textQuoted)-1] - return textUnquoted -} - -func MergeMap(mObj ...map[string]interface{}) map[string]interface{} { - newObj := map[string]interface{}{} - for _, m := range mObj { - for k, v := range m { - newObj[k] = v - } - } - return newObj -} - -func newJson(data map[string]interface{}, account *model.Account) map[string]interface{} { - common := map[string]interface{}{ - "catalogType": 3, - "cloudID": account.SiteId, - "cloudType": 1, - "commonAccountInfo": base.Json{ - "account": account.Username, - "accountType": 1, - }, - } - return MergeMap(data, common) -} diff --git a/drivers/189/189.go b/drivers/189/189.go deleted file mode 100644 index fe611adf..00000000 --- a/drivers/189/189.go +++ /dev/null @@ -1,620 +0,0 @@ -package _89 - -import ( - "bytes" - "crypto/md5" - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - "io" - "math" - "net/http" - "regexp" - "strconv" - "strings" - "time" - - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - jsoniter "github.com/json-iterator/go" - log "github.com/sirupsen/logrus" -) - -var client189Map map[string]*resty.Client -var infoMap = make(map[string]Rsa) - -func (driver Cloud189) getClient(account *model.Account) (*resty.Client, error) { - client, ok := client189Map[account.Name] - if ok { - return client, nil - } - err := driver.Login(account) - if err != nil { - return nil, err - } - client, ok = client189Map[account.Name] - if !ok { - return nil, fmt.Errorf("can't find [%s] client", account.Name) - } - return client, nil -} - -func (driver Cloud189) FormatFile(file *Cloud189File) *model.File { - f := &model.File{ - Id: strconv.FormatInt(file.Id, 10), - Name: file.Name, - Size: file.Size, - Driver: driver.Config().Name, - UpdatedAt: nil, - Thumbnail: file.Icon.SmallUrl, - Url: file.Url, - } - loc, _ := time.LoadLocation("Local") - lastOpTime, err := time.ParseInLocation("2006-01-02 15:04:05", file.LastOpTime, loc) - if err == nil { - f.UpdatedAt = &lastOpTime - } - if file.Size == -1 { - f.Size = 0 - } - f.Type = file.GetType() - return f -} - -//func (c Cloud189) GetFile(path string, account *model.Account) (*Cloud189File, error) { -// dir, name := filepath.Split(path) -// dir = utils.ParsePath(dir) -// _, _, err := c.ParentPath(dir, account) -// if err != nil { -// return nil, err -// } -// parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir)) -// parentFiles, _ := parentFiles_.([]Cloud189File) -// for _, file := range parentFiles { -// if file.Name == name { -// if file.Size != -1 { -// return &file, err -// } else { -// return nil, ErrNotFile -// } -// } -// } -// return nil, ErrPathNotFound -//} - -type LoginResp struct { - Msg string `json:"msg"` - Result int `json:"result"` - ToUrl string `json:"toUrl"` -} - -// Login refer to PanIndex -func (driver Cloud189) Login(account *model.Account) error { - client := resty.New() - //client.SetCookieJar(cookieJar) - client.SetTimeout(base.DefaultTimeout) - client.SetRetryCount(3) - client.SetHeader("Referer", "https://cloud.189.cn/") - client.SetHeader("User-Agent", base.UserAgent) - url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action" - b := "" - lt := "" - ltText := regexp.MustCompile(`lt = "(.+?)"`) - var res *resty.Response - var err error - for i := 0; i < 3; i++ { - res, err = client.R().Get(url) - if err != nil { - return err - } - // 已经登陆 - if res.RawResponse.Request.URL.String() == "https://cloud.189.cn/web/main" { - return nil - } - b = res.String() - ltTextArr := ltText.FindStringSubmatch(b) - if len(ltTextArr) > 0 { - lt = ltTextArr[1] - break - } else { - <-time.After(time.Second) - } - } - if lt == "" { - return fmt.Errorf("get page: %s \nstatus: %d \nrequest url: %s\nredirect url: %s", - b, res.StatusCode(), res.RawResponse.Request.URL.String(), res.Header().Get("location")) - } - captchaToken := regexp.MustCompile(`captchaToken' value='(.+?)'`).FindStringSubmatch(b)[1] - returnUrl := regexp.MustCompile(`returnUrl = '(.+?)'`).FindStringSubmatch(b)[1] - paramId := regexp.MustCompile(`paramId = "(.+?)"`).FindStringSubmatch(b)[1] - //reqId := regexp.MustCompile(`reqId = "(.+?)"`).FindStringSubmatch(b)[1] - jRsakey := regexp.MustCompile(`j_rsaKey" value="(\S+)"`).FindStringSubmatch(b)[1] - vCodeID := regexp.MustCompile(`picCaptcha\.do\?token\=([A-Za-z0-9\&\=]+)`).FindStringSubmatch(b)[1] - vCodeRS := "" - if vCodeID != "" { - // need ValidateCode - log.Debugf("try to identify verification codes") - timeStamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10) - u := "https://open.e.189.cn/api/logbox/oauth2/picCaptcha.do?token=" + vCodeID + timeStamp - imgRes, err := client.R().SetHeaders(map[string]string{ - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0", - "Referer": "https://open.e.189.cn/api/logbox/oauth2/unifyAccountLogin.do", - "Sec-Fetch-Dest": "image", - "Sec-Fetch-Mode": "no-cors", - "Sec-Fetch-Site": "same-origin", - }).Get(u) - if err != nil { - return err - } - vRes, err := client.R().SetMultipartField( - "image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())). - Post(conf.GetStr("ocr api")) - if err != nil { - return err - } - if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 { - return errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString()) - } - vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString() - log.Debugln("code: ", vCodeRS) - } - userRsa := RsaEncode([]byte(account.Username), jRsakey, true) - passwordRsa := RsaEncode([]byte(account.Password), jRsakey, true) - url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do" - var loginResp LoginResp - res, err = client.R(). - SetHeaders(map[string]string{ - "lt": lt, - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", - "Referer": "https://open.e.189.cn/", - "accept": "application/json;charset=UTF-8", - }).SetFormData(map[string]string{ - "appKey": "cloud", - "accountType": "01", - "userName": "{RSA}" + userRsa, - "password": "{RSA}" + passwordRsa, - "validateCode": vCodeRS, - "captchaToken": captchaToken, - "returnUrl": returnUrl, - "mailSuffix": "@pan.cn", - "paramId": paramId, - "clientType": "10010", - "dynamicCheck": "FALSE", - "cb_SaveName": "1", - "isOauth2": "false", - }).Post(url) - if err != nil { - return err - } - err = utils.Json.Unmarshal(res.Body(), &loginResp) - if err != nil { - log.Error(err.Error()) - return err - } - if loginResp.Result != 0 { - return fmt.Errorf(loginResp.Msg) - } - _, err = client.R().Get(loginResp.ToUrl) - if err != nil { - log.Errorf(err.Error()) - return err - } - client189Map[account.Name] = client - return nil -} - -func (driver Cloud189) isFamily(account *model.Account) bool { - return account.InternalType == "Family" -} - -func (driver Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud189File, error) { - res := make([]Cloud189File, 0) - pageNum := 1 - - for { - var resp Cloud189Files - body, err := driver.Request("https://cloud.189.cn/api/open/file/listFiles.action", base.Get, map[string]string{ - //"noCache": random(), - "pageSize": "60", - "pageNum": strconv.Itoa(pageNum), - "mediaType": "0", - "folderId": fileId, - "iconOption": "5", - "orderBy": "lastOpTime", //account.OrderBy - "descending": "true", //account.OrderDirection - }, nil, nil, account) - if err != nil { - return nil, err - } - err = utils.Json.Unmarshal(body, &resp) - if err != nil { - return nil, err - } - if resp.ResCode != 0 { - return nil, fmt.Errorf(resp.ResMessage) - } - if resp.FileListAO.Count == 0 { - break - } - for _, folder := range resp.FileListAO.FolderList { - res = append(res, Cloud189File{ - Id: folder.Id, - LastOpTime: folder.LastOpTime, - Name: folder.Name, - Size: -1, - }) - } - res = append(res, resp.FileListAO.FileList...) - pageNum++ - } - return res, nil -} - -func (driver Cloud189) Request(url string, method int, query, form map[string]string, headers map[string]string, account *model.Account) ([]byte, error) { - client, err := driver.getClient(account) - if err != nil { - return nil, err - } - //var resp base.Json - if driver.isFamily(account) { - url = strings.Replace(url, "/api/open", "/api/open/family", 1) - if query != nil { - query["familyId"] = account.SiteId - } - if form != nil { - form["familyId"] = account.SiteId - } - } - var e Cloud189Error - req := client.R().SetError(&e). - SetHeader("Accept", "application/json;charset=UTF-8"). - SetQueryParams(map[string]string{ - "noCache": random(), - }) - if query != nil { - req = req.SetQueryParams(query) - } - if form != nil { - req = req.SetFormData(form) - } - if headers != nil { - req = req.SetHeaders(headers) - } - var res *resty.Response - switch method { - case base.Get: - res, err = req.Get(url) - case base.Post: - res, err = req.Post(url) - default: - return nil, base.ErrNotSupport - } - if err != nil { - return nil, err - } - //log.Debug(res.String()) - if e.ErrorCode != "" { - if e.ErrorCode == "InvalidSessionKey" { - err = driver.Login(account) - if err != nil { - return nil, err - } - return driver.Request(url, method, query, form, nil, account) - } - } - if jsoniter.Get(res.Body(), "res_code").ToInt() != 0 { - err = errors.New(jsoniter.Get(res.Body(), "res_message").ToString()) - } - return res.Body(), err -} - -func (driver Cloud189) GetSessionKey(account *model.Account) (string, error) { - //info, ok := infoMap[account.Name] - //if !ok { - // info = Info{} - // infoMap[account.Name] = info - //} else { - // log.Debugf("hit") - //} - //if info.SessionKey != "" { - // return info.SessionKey, nil - //} - resp, err := driver.Request("https://cloud.189.cn/v2/getUserBriefInfo.action", base.Get, nil, nil, nil, account) - if err != nil { - return "", err - } - sessionKey := jsoniter.Get(resp, "sessionKey").ToString() - //info.SessionKey = sessionKey - return sessionKey, nil -} - -func (driver Cloud189) GetResKey(account *model.Account) (string, string, error) { - rsa, ok := infoMap[account.Name] - if !ok { - rsa = Rsa{} - infoMap[account.Name] = rsa - } - now := time.Now().UnixMilli() - if rsa.Expire > now { - return rsa.PubKey, rsa.PkId, nil - } - resp, err := driver.Request("https://cloud.189.cn/api/security/generateRsaKey.action", base.Get, nil, nil, nil, account) - if err != nil { - return "", "", err - } - pubKey, pkId := jsoniter.Get(resp, "pubKey").ToString(), jsoniter.Get(resp, "pkId").ToString() - rsa.PubKey, rsa.PkId = pubKey, pkId - rsa.Expire = jsoniter.Get(resp, "expire").ToInt64() - return pubKey, pkId, nil -} - -//func (driver Cloud189) UploadRequest1(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) { -// //sessionKey, err := driver.GetSessionKey(account) -// //if err != nil { -// // return nil, err -// //} -// sessionKey := account.DriveId -// pubKey, pkId, err := driver.GetResKey(account) -// log.Debugln(sessionKey, pubKey, pkId) -// if err != nil { -// return nil, err -// } -// xRId := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx") -// pkey := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")[0 : 16+int(16*mathRand.Float32())] -// params := hex.EncodeToString(AesEncrypt([]byte(qs(form)), []byte(pkey[0:16]))) -// date := strconv.FormatInt(time.Now().Unix(), 10) -// a := make(url.Values) -// a.Set("SessionKey", sessionKey) -// a.Set("Operate", http.MethodGet) -// a.Set("RequestURI", uri) -// a.Set("Date", date) -// a.Set("params", params) -// signature := hex.EncodeToString(SHA1(EncodeParam(a), pkey)) -// encryptionText := RsaEncode([]byte(pkey), pubKey, false) -// headers := map[string]string{ -// "signature": signature, -// "sessionKey": sessionKey, -// "encryptionText": encryptionText, -// "pkId": pkId, -// "x-request-id": xRId, -// "x-request-date": date, -// } -// req := base.RestyClient.R().SetHeaders(headers).SetQueryParam("params", params) -// if resp != nil { -// req.SetResult(resp) -// } -// res, err := req.Get("https://upload.cloud.189.cn" + uri) -// if err != nil { -// return nil, err -// } -// //log.Debug(res.String()) -// data := res.Body() -// if jsoniter.Get(data, "code").ToString() != "SUCCESS" { -// return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString()) -// } -// return data, nil -//} -// -//func (driver Cloud189) UploadRequest2(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) { -// c := strconv.FormatInt(time.Now().UnixMilli(), 10) -// r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx") -// l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx") -// l = l[0 : 16+int(16*mathRand.Float32())] -// -// e := qs(form) -// data := AesEncrypt([]byte(e), []byte(l[0:16])) -// h := hex.EncodeToString(data) -// -// sessionKey := account.DriveId -// a := make(url.Values) -// a.Set("SessionKey", sessionKey) -// a.Set("Operate", http.MethodGet) -// a.Set("RequestURI", uri) -// a.Set("Date", c) -// a.Set("params", h) -// g := SHA1(EncodeParam(a), l) -// -// pubKey, pkId, err := driver.GetResKey(account) -// if err != nil { -// return nil, err -// } -// b := RsaEncode([]byte(l), pubKey, false) -// client, err := driver.getClient(account) -// if err != nil { -// return nil, err -// } -// req := client.R() -// req.Header.Set("accept", "application/json;charset=UTF-8") -// req.Header.Set("SessionKey", sessionKey) -// req.Header.Set("Signature", hex.EncodeToString(g)) -// req.Header.Set("X-Request-Date", c) -// req.Header.Set("X-Request-ID", r) -// req.Header.Set("EncryptionText", b) -// req.Header.Set("PkId", pkId) -// if resp != nil { -// req.SetResult(resp) -// } -// res, err := req.Get("https://upload.cloud.189.cn" + uri + "?params=" + h) -// if err != nil { -// return nil, err -// } -// //log.Debug(res.String()) -// data = res.Body() -// if jsoniter.Get(data, "code").ToString() != "SUCCESS" { -// return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString()) -// } -// return data, nil -//} - -func (driver Cloud189) UploadRequest(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) { - c := strconv.FormatInt(time.Now().UnixMilli(), 10) - r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx") - l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx") - l = l[0 : 16+int(16*utils.Rand.Float32())] - - e := qs(form) - data := AesEncrypt([]byte(e), []byte(l[0:16])) - h := hex.EncodeToString(data) - - sessionKey := account.DriveId - signature := hmacSha1(fmt.Sprintf("SessionKey=%s&Operate=GET&RequestURI=%s&Date=%s¶ms=%s", sessionKey, uri, c, h), l) - - pubKey, pkId, err := driver.GetResKey(account) - if err != nil { - return nil, err - } - b := RsaEncode([]byte(l), pubKey, false) - client, err := driver.getClient(account) - if err != nil { - return nil, err - } - req := client.R() - req.Header.Set("accept", "application/json;charset=UTF-8") - req.Header.Set("SessionKey", sessionKey) - req.Header.Set("Signature", signature) - req.Header.Set("X-Request-Date", c) - req.Header.Set("X-Request-ID", r) - req.Header.Set("EncryptionText", b) - req.Header.Set("PkId", pkId) - if resp != nil { - req.SetResult(resp) - } - res, err := req.Get("https://upload.cloud.189.cn" + uri + "?params=" + h) - if err != nil { - return nil, err - } - //log.Debug(res.String()) - data = res.Body() - if jsoniter.Get(data, "code").ToString() != "SUCCESS" { - return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString()) - } - return data, nil -} - -// NewUpload Error: signature check false -func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account) error { - sessionKey, err := driver.GetSessionKey(account) - if err != nil { - account.Status = err.Error() - } else { - account.Status = "work" - account.DriveId = sessionKey - } - _ = model.SaveAccount(account) - const DEFAULT uint64 = 10485760 - var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT))) - var finish uint64 = 0 - parentFile, err := driver.File(file.ParentPath, account) - if err != nil { - return err - } - if !parentFile.IsDir() { - return base.ErrNotFolder - } - res, err := driver.UploadRequest("/person/initMultiUpload", map[string]string{ - "parentFolderId": parentFile.Id, - "fileName": encode(file.Name), - "fileSize": strconv.FormatInt(int64(file.Size), 10), - "sliceSize": strconv.FormatInt(int64(DEFAULT), 10), - "lazyCheck": "1", - }, account, nil) - if err != nil { - return err - } - uploadFileId := jsoniter.Get(res, "data", "uploadFileId").ToString() - //_, err = driver.UploadRequest("/person/getUploadedPartsInfo", map[string]string{ - // "uploadFileId": uploadFileId, - //}, account, nil) - var i int64 - var byteSize uint64 - md5s := make([]string, 0) - md5Sum := md5.New() - for i = 1; i <= count; i++ { - byteSize = file.GetSize() - finish - if DEFAULT < byteSize { - byteSize = DEFAULT - } - //log.Debugf("%d,%d", byteSize, finish) - byteData := make([]byte, byteSize) - n, err := io.ReadFull(file, byteData) - //log.Debug(err, n) - if err != nil { - return err - } - finish += uint64(n) - md5Bytes := getMd5(byteData) - md5Hex := hex.EncodeToString(md5Bytes) - md5Base64 := base64.StdEncoding.EncodeToString(md5Bytes) - md5s = append(md5s, strings.ToUpper(md5Hex)) - md5Sum.Write(byteData) - //log.Debugf("md5Bytes: %+v,md5Str:%s,md5Base64:%s", md5Bytes, md5Hex, md5Base64) - var resp UploadUrlsResp - res, err = driver.UploadRequest("/person/getMultiUploadUrls", map[string]string{ - "partInfo": fmt.Sprintf("%s-%s", strconv.FormatInt(i, 10), md5Base64), - "uploadFileId": uploadFileId, - }, account, &resp) - if err != nil { - return err - } - uploadData := resp.UploadUrls["partNumber_"+strconv.FormatInt(i, 10)] - log.Debugf("uploadData: %+v", uploadData) - requestURL := uploadData.RequestURL - uploadHeaders := strings.Split(decodeURIComponent(uploadData.RequestHeader), "&") - req, _ := http.NewRequest(http.MethodPut, requestURL, bytes.NewReader(byteData)) - for _, v := range uploadHeaders { - i := strings.Index(v, "=") - req.Header.Set(v[0:i], v[i+1:]) - } - - r, err := base.HttpClient.Do(req) - log.Debugf("%+v %+v", r, r.Request.Header) - r.Body.Close() - if err != nil { - return err - } - } - fileMd5 := hex.EncodeToString(md5Sum.Sum(nil)) - sliceMd5 := fileMd5 - if file.GetSize() > DEFAULT { - sliceMd5 = utils.GetMD5Encode(strings.Join(md5s, "\n")) - } - res, err = driver.UploadRequest("/person/commitMultiUploadFile", map[string]string{ - "uploadFileId": uploadFileId, - "fileMd5": fileMd5, - "sliceMd5": sliceMd5, - "lazyCheck": "1", - }, account, nil) - account.DriveId, _ = driver.GetSessionKey(account) - return err -} - -func (driver Cloud189) OldUpload(file *model.FileStream, account *model.Account) error { - //return base.ErrNotImplement - client, err := driver.getClient(account) - if err != nil { - return err - } - parentFile, err := driver.File(file.ParentPath, account) - if err != nil { - return err - } - // api refer to PanIndex - res, err := client.R().SetMultipartFormData(map[string]string{ - "parentId": parentFile.Id, - "sessionKey": account.DriveId, - "opertype": "1", - "fname": file.GetFileName(), - }).SetMultipartField("Filedata", file.GetFileName(), file.GetMIMEType(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction") - if err != nil { - return err - } - if jsoniter.Get(res.Body(), "MD5").ToString() != "" { - return nil - } - log.Debugf(res.String()) - return errors.New(res.String()) -} diff --git a/drivers/189/driver.go b/drivers/189/driver.go deleted file mode 100644 index 1b9ab510..00000000 --- a/drivers/189/driver.go +++ /dev/null @@ -1,382 +0,0 @@ -package _89 - -import ( - "fmt" - "net/http" - "path/filepath" - "strings" - - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - log "github.com/sirupsen/logrus" -) - -type Cloud189 struct{} - -func (driver Cloud189) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "189Cloud", - LocalSort: true, - } -} - -func (driver Cloud189) Items() []base.Item { - return []base.Item{ - { - Name: "username", - Label: "username", - Type: base.TypeString, - Required: true, - Description: "account username/phone number", - }, - { - Name: "password", - Label: "password", - Type: base.TypeString, - Required: true, - Description: "account password", - }, - { - Name: "root_folder", - Label: "root folder file_id", - Type: base.TypeString, - Required: true, - }, - //{ - // Name: "internal_type", - // Label: "189cloud type", - // Type: base.TypeSelect, - // Required: true, - // Values: "Personal,Family", - //}, - //{ - // Name: "site_id", - // Label: "family id", - // Type: base.TypeString, - //}, - //{ - // Name: "order_by", - // Label: "order_by", - // Type: base.TypeSelect, - // Values: "name,size,lastOpTime,createdDate", - // Required: true, - //}, - //{ - // Name: "order_direction", - // Label: "desc", - // Type: base.TypeSelect, - // Values: "true,false", - // Required: true, - //}, - } -} - -func (driver Cloud189) Save(account *model.Account, old *model.Account) error { - if old != nil { - delete(client189Map, old.Name) - } - if account == nil { - return nil - } - if err := driver.Login(account); err != nil { - account.Status = err.Error() - _ = model.SaveAccount(account) - return err - } - sessionKey, err := driver.GetSessionKey(account) - if err != nil { - account.Status = err.Error() - } else { - account.Status = "work" - account.DriveId = sessionKey - } - _ = model.SaveAccount(account) - return err -} - -func (driver Cloud189) File(path string, account *model.Account) (*model.File, error) { - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - dir, name := filepath.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver Cloud189) Files(path string, account *model.Account) ([]model.File, error) { - path = utils.ParsePath(path) - var rawFiles []Cloud189File - cache, err := base.GetCache(path, account) - if err == nil { - rawFiles, _ = cache.([]Cloud189File) - } else { - file, err := driver.File(path, account) - if err != nil { - return nil, err - } - rawFiles, err = driver.GetFiles(file.Id, account) - if err != nil { - return nil, err - } - if len(rawFiles) > 0 { - _ = base.SetCache(path, rawFiles, account) - } - } - files := make([]model.File, 0) - for _, file := range rawFiles { - files = append(files, *driver.FormatFile(&file)) - } - return files, nil -} - -func (driver Cloud189) Link(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(utils.ParsePath(args.Path), account) - if err != nil { - return nil, err - } - if file.Type == conf.FOLDER { - return nil, base.ErrNotFile - } - var resp DownResp - u := "https://cloud.189.cn/api/portal/getFileInfo.action" - body, err := driver.Request(u, base.Get, map[string]string{ - "fileId": file.Id, - }, nil, nil, account) - if err != nil { - return nil, err - } - log.Debugln(string(body)) - err = utils.Json.Unmarshal(body, &resp) - if err != nil { - return nil, err - } - if resp.ResCode != 0 { - return nil, fmt.Errorf(resp.ResMessage) - } - client, err := driver.getClient(account) - if err != nil { - return nil, err - } - client = resty.NewWithClient(client.GetClient()).SetRedirectPolicy( - resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - })) - res, err := client.R().SetHeader("User-Agent", base.UserAgent).Get("https:" + resp.FileDownloadUrl) - if err != nil { - return nil, err - } - log.Debugln(res.Status()) - log.Debugln(res.String()) - link := base.Link{ - Headers: []base.Header{ - {Name: "User-Agent", Value: base.UserAgent}, - //{Name: "Authorization", Value: ""}, - }, - } - log.Debugln("first url:", resp.FileDownloadUrl) - if res.StatusCode() == 302 { - link.Url = res.Header().Get("location") - log.Debugln("second url:", link.Url) - _, _ = client.R().Get(link.Url) - if res.StatusCode() == 302 { - link.Url = res.Header().Get("location") - } - log.Debugln("third url:", link.Url) - } else { - link.Url = resp.FileDownloadUrl - } - link.Url = strings.Replace(link.Url, "http://", "https://", 1) - return &link, nil -} - -func (driver Cloud189) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - log.Debugf("189 path: %s", path) - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -//func (driver Cloud189) Proxy(r *http.Request, account *model.Account) { -// r.Header.Del("Origin") -//} - -func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) { - return nil, base.ErrNotSupport -} - -func (driver Cloud189) MakeDir(path string, account *model.Account) error { - dir, name := filepath.Split(path) - parent, err := driver.File(dir, account) - if err != nil { - return err - } - if !parent.IsDir() { - return base.ErrNotFolder - } - form := map[string]string{ - "parentFolderId": parent.Id, - "folderName": name, - } - _, err = driver.Request("https://cloud.189.cn/api/open/file/createFolder.action", base.Post, nil, form, nil, account) - return err -} - -func (driver Cloud189) Move(src string, dst string, account *model.Account) error { - dstDir, dstName := filepath.Split(dst) - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - dstDirFile, err := driver.File(dstDir, account) - if err != nil { - return err - } - isFolder := 0 - if srcFile.IsDir() { - isFolder = 1 - } - taskInfos := []base.Json{ - { - "fileId": srcFile.Id, - "fileName": dstName, - "isFolder": isFolder, - }, - } - taskInfosBytes, err := utils.Json.Marshal(taskInfos) - if err != nil { - return err - } - form := map[string]string{ - "type": "MOVE", - "targetFolderId": dstDirFile.Id, - "taskInfos": string(taskInfosBytes), - } - _, err = driver.Request("https://cloud.189.cn/api/open/batch/createBatchTask.action", base.Post, nil, form, nil, account) - return err -} - -func (driver Cloud189) Rename(src string, dst string, account *model.Account) error { - _, dstName := filepath.Split(dst) - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - url := "https://cloud.189.cn/api/open/file/renameFile.action" - idKey := "fileId" - nameKey := "destFileName" - if srcFile.IsDir() { - url = "https://cloud.189.cn/api/open/file/renameFolder.action" - idKey = "folderId" - nameKey = "destFolderName" - } - form := map[string]string{ - idKey: srcFile.Id, - nameKey: dstName, - } - _, err = driver.Request(url, base.Post, nil, form, nil, account) - return err -} - -func (driver Cloud189) Copy(src string, dst string, account *model.Account) error { - dstDir, dstName := filepath.Split(dst) - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - dstDirFile, err := driver.File(dstDir, account) - if err != nil { - return err - } - isFolder := 0 - if srcFile.IsDir() { - isFolder = 1 - } - taskInfos := []base.Json{ - { - "fileId": srcFile.Id, - "fileName": dstName, - "isFolder": isFolder, - }, - } - taskInfosBytes, err := utils.Json.Marshal(taskInfos) - if err != nil { - return err - } - form := map[string]string{ - "type": "COPY", - "targetFolderId": dstDirFile.Id, - "taskInfos": string(taskInfosBytes), - } - _, err = driver.Request("https://cloud.189.cn/api/open/batch/createBatchTask.action", base.Post, nil, form, nil, account) - return err -} - -func (driver Cloud189) Delete(path string, account *model.Account) error { - path = utils.ParsePath(path) - file, err := driver.File(path, account) - if err != nil { - return err - } - isFolder := 0 - if file.IsDir() { - isFolder = 1 - } - taskInfos := []base.Json{ - { - "fileId": file.Id, - "fileName": file.Name, - "isFolder": isFolder, - }, - } - taskInfosBytes, err := utils.Json.Marshal(taskInfos) - if err != nil { - return err - } - form := map[string]string{ - "type": "DELETE", - "targetFolderId": "", - "taskInfos": string(taskInfosBytes), - } - _, err = driver.Request("https://cloud.189.cn/api/open/batch/createBatchTask.action", base.Post, nil, form, nil, account) - return err -} - -func (driver Cloud189) Upload(file *model.FileStream, account *model.Account) error { - //return base.ErrNotImplement - if file == nil { - return base.ErrEmptyFile - } - return driver.NewUpload(file, account) - //return driver.OldUpload(file, account) -} - -var _ base.Driver = (*Cloud189)(nil) diff --git a/drivers/189/types.go b/drivers/189/types.go deleted file mode 100644 index 267fdef4..00000000 --- a/drivers/189/types.go +++ /dev/null @@ -1,91 +0,0 @@ -package _89 - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/utils" - "path" -) - -type Cloud189Error struct { - ErrorCode string `json:"errorCode"` - ErrorMsg string `json:"errorMsg"` -} - -type Cloud189File struct { - Id int64 `json:"id"` - LastOpTime string `json:"lastOpTime"` - Name string `json:"name"` - Size int64 `json:"size"` - Icon struct { - SmallUrl string `json:"smallUrl"` - //LargeUrl string `json:"largeUrl"` - } `json:"icon"` - Url string `json:"url"` -} - -func (f Cloud189File) GetSize() uint64 { - if f.Size == -1 { - return 0 - } - return uint64(f.Size) -} - -func (f Cloud189File) GetName() string { - return f.Name -} - -func (f Cloud189File) GetType() int { - if f.Size == -1 { - return conf.FOLDER - } - return utils.GetFileType(path.Ext(f.Name)) -} - -type Cloud189Folder struct { - Id int64 `json:"id"` - LastOpTime string `json:"lastOpTime"` - Name string `json:"name"` -} - -type Cloud189Files struct { - ResCode int `json:"res_code"` - ResMessage string `json:"res_message"` - FileListAO struct { - Count int `json:"count"` - FileList []Cloud189File `json:"fileList"` - FolderList []Cloud189Folder `json:"folderList"` - } `json:"fileListAO"` -} - -type UploadUrlsResp struct { - Code string `json:"code"` - UploadUrls map[string]Part `json:"uploadUrls"` -} - -type Part struct { - RequestURL string `json:"requestURL"` - RequestHeader string `json:"requestHeader"` -} - -//type Info struct { -// SessionKey string -// Rsa Rsa -//} - -type Rsa struct { - Expire int64 `json:"expire"` - PkId string `json:"pkId"` - PubKey string `json:"pubKey"` -} - -type Cloud189Down struct { - ResCode int `json:"res_code"` - ResMessage string `json:"res_message"` - FileDownloadUrl string `json:"fileDownloadUrl"` -} - -type DownResp struct { - ResCode int `json:"res_code"` - ResMessage string `json:"res_message"` - FileDownloadUrl string `json:"downloadUrl"` -} diff --git a/drivers/189/util.go b/drivers/189/util.go deleted file mode 100644 index c079af87..00000000 --- a/drivers/189/util.go +++ /dev/null @@ -1,199 +0,0 @@ -package _89 - -import ( - "bytes" - "crypto/aes" - "crypto/hmac" - "crypto/md5" - "crypto/rand" - "crypto/rsa" - "crypto/sha1" - "crypto/x509" - "encoding/base64" - "encoding/hex" - "encoding/pem" - "fmt" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - log "github.com/sirupsen/logrus" - "net/url" - "regexp" - "strconv" - "strings" -) - -func random() string { - return fmt.Sprintf("0.%17v", utils.Rand.Int63n(100000000000000000)) -} - -func RsaEncode(origData []byte, j_rsakey string, hex bool) string { - publicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + j_rsakey + "\n-----END PUBLIC KEY-----") - block, _ := pem.Decode(publicKey) - pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes) - pub := pubInterface.(*rsa.PublicKey) - b, err := rsa.EncryptPKCS1v15(rand.Reader, pub, origData) - if err != nil { - log.Errorf("err: %s", err.Error()) - } - res := base64.StdEncoding.EncodeToString(b) - if hex { - return b64tohex(res) - } - return res -} - -var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - -var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz" - -func int2char(a int) string { - return strings.Split(BI_RM, "")[a] -} - -func b64tohex(a string) string { - d := "" - e := 0 - c := 0 - for i := 0; i < len(a); i++ { - m := strings.Split(a, "")[i] - if m != "=" { - v := strings.Index(b64map, m) - if 0 == e { - e = 1 - d += int2char(v >> 2) - c = 3 & v - } else if 1 == e { - e = 2 - d += int2char(c<<2 | v>>4) - c = 15 & v - } else if 2 == e { - e = 3 - d += int2char(c) - d += int2char(v >> 2) - c = 3 & v - } else { - e = 0 - d += int2char(c<<2 | v>>4) - d += int2char(15 & v) - } - } - } - if e == 1 { - d += int2char(c << 2) - } - return d -} - -func qs(form map[string]string) string { - f := make(url.Values) - for k, v := range form { - f.Set(k, v) - } - return EncodeParam(f) - //strList := make([]string, 0) - //for k, v := range form { - // strList = append(strList, fmt.Sprintf("%s=%s", k, url.QueryEscape(v))) - //} - //return strings.Join(strList, "&") -} - -func EncodeParam(v url.Values) string { - if v == nil { - return "" - } - var buf strings.Builder - keys := make([]string, 0, len(v)) - for k := range v { - keys = append(keys, k) - } - for _, k := range keys { - vs := v[k] - for _, v := range vs { - if buf.Len() > 0 { - buf.WriteByte('&') - } - buf.WriteString(k) - buf.WriteByte('=') - //if k == "fileName" { - // buf.WriteString(encode(v)) - //} else { - buf.WriteString(v) - //} - } - } - return buf.String() -} - -func encode(str string) string { - //str = strings.ReplaceAll(str, "%", "%25") - //str = strings.ReplaceAll(str, "&", "%26") - //str = strings.ReplaceAll(str, "+", "%2B") - //return str - return url.QueryEscape(str) -} - -func AesEncrypt(data, key []byte) []byte { - block, _ := aes.NewCipher(key) - if block == nil { - return []byte{} - } - data = PKCS7Padding(data, block.BlockSize()) - decrypted := make([]byte, len(data)) - size := block.BlockSize() - for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size { - block.Encrypt(decrypted[bs:be], data[bs:be]) - } - return decrypted -} - -func PKCS7Padding(ciphertext []byte, blockSize int) []byte { - padding := blockSize - len(ciphertext)%blockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(ciphertext, padtext...) -} - -func hmacSha1(data string, secret string) string { - h := hmac.New(sha1.New, []byte(secret)) - h.Write([]byte(data)) - return hex.EncodeToString(h.Sum(nil)) -} - -func getMd5(data []byte) []byte { - h := md5.New() - h.Write(data) - return h.Sum(nil) -} - -func init() { - base.RegisterDriver(&Cloud189{}) - client189Map = make(map[string]*resty.Client) -} - -func decodeURIComponent(str string) string { - r, _ := url.PathUnescape(str) - //r = strings.ReplaceAll(r, " ", "+") - return r -} - -func Random(v string) string { - reg := regexp.MustCompilePOSIX("[xy]") - data := reg.ReplaceAllFunc([]byte(v), func(msg []byte) []byte { - var i int64 - t := int64(16 * utils.Rand.Float32()) - if msg[0] == 120 { - i = t - } else { - i = 3&t | 8 - } - return []byte(strconv.FormatInt(i, 16)) - }) - return string(data) -} - -//func SHA1(v, l string) []byte { -// key := []byte(l) -// mac := hmac.New(sha1.New, key) -// mac.Write([]byte(v)) -// return mac.Sum(nil) -//} diff --git a/drivers/189pc/189.go b/drivers/189pc/189.go deleted file mode 100644 index b6f72f6b..00000000 --- a/drivers/189pc/189.go +++ /dev/null @@ -1,318 +0,0 @@ -package _189 - -import ( - "bytes" - "errors" - "fmt" - "net/http" - "net/http/cookiejar" - "net/url" - "regexp" - "sync" - - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - "github.com/google/uuid" - log "github.com/sirupsen/logrus" -) - -var userStateCache = struct { - sync.Mutex - States map[string]*State -}{States: make(map[string]*State)} - -func GetState(account *model.Account) *State { - userStateCache.Lock() - defer userStateCache.Unlock() - if v, ok := userStateCache.States[account.Username]; ok && v != nil { - return v - } - state := &State{client: resty.New(). - SetHeaders(map[string]string{ - "Accept": "application/json;charset=UTF-8", - "User-Agent": base.UserAgent, - }).SetTimeout(base.DefaultTimeout), - } - userStateCache.States[account.Username] = state - return state -} - -type State struct { - sync.Mutex - client *resty.Client - - RsaPublicKey string - - SessionKey string - SessionSecret string - FamilySessionKey string - FamilySessionSecret string - - AccessToken string - - //怎么刷新的??? - RefreshToken string -} - -func (s *State) login(account *model.Account) error { - // 清除cookie - jar, _ := cookiejar.New(nil) - s.client.SetCookieJar(jar) - - var err error - var res *resty.Response - defer func() { - account.Status = "work" - if err != nil { - account.Status = err.Error() - } - model.SaveAccount(account) - if res != nil { - log.Debug(res.String()) - } - }() - - var param *LoginParam - param, err = s.getLoginParam() - if err != nil { - return err - } - - // 提交登录 - s.RsaPublicKey = fmt.Sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----", param.jRsaKey) - res, err = s.client.R(). - SetHeaders(map[string]string{ - "Referer": AUTH_URL, - "REQID": param.ReqId, - "lt": param.Lt, - }). - SetFormData(map[string]string{ - "appKey": APP_ID, - "accountType": "02", - "userName": "{RSA}" + rsaEncrypt(s.RsaPublicKey, account.Username), - "password": "{RSA}" + rsaEncrypt(s.RsaPublicKey, account.Password), - "validateCode": param.vCodeRS, - "captchaToken": param.CaptchaToken, - "returnUrl": RETURN_URL, - "mailSuffix": "@189.cn", - "dynamicCheck": "FALSE", - "clientType": CLIENT_TYPE, - "cb_SaveName": "1", - "isOauth2": "false", - "state": "", - "paramId": param.ParamId, - }). - Post(AUTH_URL + "/api/logbox/oauth2/loginSubmit.do") - if err != nil { - return err - } - toUrl := utils.Json.Get(res.Body(), "toUrl").ToString() - if toUrl == "" { - log.Error(res.String()) - return fmt.Errorf(res.String()) - } - - // 获取Session - var erron Erron - var sessionResp appSessionResp - res, err = s.client.R(). - SetResult(&sessionResp).SetError(&erron). - SetQueryParams(clientSuffix()). - SetQueryParam("redirectURL", url.QueryEscape(toUrl)). - Post(API_URL + "/getSessionForPC.action") - if err != nil { - return err - } - - if erron.ResCode != "" { - err = fmt.Errorf(erron.ResMessage) - return err - } - if sessionResp.ResCode != 0 { - err = fmt.Errorf(sessionResp.ResMessage) - return err - } - s.SessionKey = sessionResp.SessionKey - s.SessionSecret = sessionResp.SessionSecret - s.FamilySessionKey = sessionResp.FamilySessionKey - s.FamilySessionSecret = sessionResp.FamilySessionSecret - s.AccessToken = sessionResp.AccessToken - s.RefreshToken = sessionResp.RefreshToken - return err -} - -func (s *State) getLoginParam() (*LoginParam, error) { - res, err := s.client.R(). - SetQueryParams(map[string]string{ - "appId": APP_ID, - "clientType": CLIENT_TYPE, - "returnURL": RETURN_URL, - "timeStamp": fmt.Sprint(timestamp()), - }). - Get(WEB_URL + "/api/portal/unifyLoginForPC.action") - if err != nil { - return nil, err - } - log.Debug(res.String()) - param := &LoginParam{ - CaptchaToken: regexp.MustCompile(`'captchaToken' value='(.+?)'`).FindStringSubmatch(res.String())[1], - Lt: regexp.MustCompile(`lt = "(.+?)"`).FindStringSubmatch(res.String())[1], - ParamId: regexp.MustCompile(`paramId = "(.+?)"`).FindStringSubmatch(res.String())[1], - ReqId: regexp.MustCompile(`reqId = "(.+?)"`).FindStringSubmatch(res.String())[1], - jRsaKey: regexp.MustCompile(`"j_rsaKey" value="(.+?)"`).FindStringSubmatch(res.String())[1], - - vCodeID: regexp.MustCompile(`token=([A-Za-z0-9&=]+)`).FindStringSubmatch(res.String())[1], - } - - imgRes, err := s.client.R().Get(fmt.Sprint(AUTH_URL, "/api/logbox/oauth2/picCaptcha.do?token=", param.vCodeID, timestamp())) - if err != nil { - return nil, err - } - if len(imgRes.Body()) > 0 { - vRes, err := resty.New().R(). - SetMultipartField("image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())). - Post(conf.GetStr("ocr api")) - if err != nil { - return nil, err - } - if utils.Json.Get(vRes.Body(), "status").ToInt() != 200 { - return nil, errors.New("ocr error:" + utils.Json.Get(vRes.Body(), "msg").ToString()) - } - param.vCodeRS = utils.Json.Get(vRes.Body(), "result").ToString() - log.Debugln("code: ", param.vCodeRS) - } - return param, nil -} - -func (s *State) refreshSession(account *model.Account) error { - var erron Erron - var userSessionResp UserSessionResp - res, err := s.client.R(). - SetResult(&userSessionResp).SetError(&erron). - SetQueryParams(clientSuffix()). - SetQueryParams(map[string]string{ - "appId": APP_ID, - "accessToken": s.AccessToken, - }). - SetHeader("X-Request-ID", uuid.NewString()). - Get(API_URL + "/getSessionForPC.action") - if err != nil { - return err - } - log.Debug(res.String()) - if erron.ResCode != "" { - return fmt.Errorf(erron.ResMessage) - } - - switch userSessionResp.ResCode { - case 0: - s.SessionKey = userSessionResp.SessionKey - s.SessionSecret = userSessionResp.SessionSecret - s.FamilySessionKey = userSessionResp.FamilySessionKey - s.FamilySessionSecret = userSessionResp.FamilySessionSecret - case 11, 18: - return s.login(account) - default: - account.Status = userSessionResp.ResMessage - _ = model.SaveAccount(account) - return fmt.Errorf(userSessionResp.ResMessage) - } - return nil -} - -func (s *State) IsLogin(account *model.Account) bool { - _, err := s.Request(http.MethodGet, API_URL+"/getUserInfo.action", nil, func(r *resty.Request) { r.SetQueryParams(clientSuffix()) }, account) - return err == nil -} - -func (s *State) Login(account *model.Account) error { - s.Lock() - defer s.Unlock() - return s.login(account) -} - -func (s *State) RefreshSession(account *model.Account) error { - s.Lock() - defer s.Unlock() - return s.refreshSession(account) -} - -func (s *State) Request(method string, fullUrl string, params Params, callback func(*resty.Request), account *model.Account) (*resty.Response, error) { - s.Lock() - dateOfGmt := getHttpDateStr() - sessionKey := s.SessionKey - sessionSecret := s.SessionSecret - if isFamily(account) { - sessionKey = s.FamilySessionKey - sessionSecret = s.FamilySessionSecret - } - - req := s.client.R() - req.SetHeaders(map[string]string{ - "Date": dateOfGmt, - "SessionKey": sessionKey, - "X-Request-ID": uuid.NewString(), - }) - - // 设置params - var paramsData string - if params != nil { - paramsData = AesECBEncrypt(params.Encode(), s.SessionSecret[:16]) - req.SetQueryParam("params", paramsData) - } - req.SetHeader("Signature", signatureOfHmac(sessionSecret, sessionKey, method, fullUrl, dateOfGmt, paramsData)) - - if callback != nil { - callback(req) - } - s.Unlock() - - res, err := req.Execute(method, fullUrl) - if err != nil { - return nil, err - } - log.Debug(res.String()) - - var erron Erron - utils.Json.Unmarshal(res.Body(), &erron) - if erron.ResCode != "" { - return nil, fmt.Errorf(erron.ResMessage) - } - if erron.Code != "" && erron.Code != "SUCCESS" { - if erron.Msg == "" { - if erron.Message == "" { - return nil, fmt.Errorf(res.String()) - } - return nil, fmt.Errorf(erron.Message) - } - return nil, fmt.Errorf(erron.Msg) - } - if erron.ErrorCode != "" { - switch erron.ErrorCode { - case "InvalidSessionKey": - if err := s.RefreshSession(account); err != nil { - return nil, err - } - return s.Request(method, fullUrl, params, callback, account) - } - return nil, fmt.Errorf(erron.ErrorMsg) - } - - switch utils.Json.Get(res.Body(), "res_code").ToInt64() { - case 11, 18: - if err := s.RefreshSession(account); err != nil { - return nil, err - } - return s.Request(method, fullUrl, params, callback, account) - case 0: - if res.StatusCode() == http.StatusOK { - return res, nil - } - return nil, fmt.Errorf(res.String()) - default: - return nil, fmt.Errorf(utils.Json.Get(res.Body(), "res_message").ToString()) - } -} diff --git a/drivers/189pc/driver.go b/drivers/189pc/driver.go deleted file mode 100644 index 5337f79d..00000000 --- a/drivers/189pc/driver.go +++ /dev/null @@ -1,923 +0,0 @@ -package _189 - -import ( - "bytes" - "crypto/md5" - "encoding/base64" - "encoding/hex" - "fmt" - "io" - "io/ioutil" - "math" - "net/http" - "net/url" - "os" - "path/filepath" - "strings" - - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - log "github.com/sirupsen/logrus" -) - -func init() { - base.RegisterDriver(new(Cloud189)) -} - -type Cloud189 struct { -} - -func (driver Cloud189) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "189CloudPC", - } -} - -func (driver Cloud189) Items() []base.Item { - return []base.Item{ - { - Name: "username", - Label: "username", - Type: base.TypeString, - Required: true, - Description: "account username/phone number", - }, - { - Name: "password", - Label: "password", - Type: base.TypeString, - Required: true, - Description: "account password", - }, - { - Name: "root_folder", - Label: "root folder file_id", - Type: base.TypeString, - }, - { - Name: "internal_type", - Label: "189cloud type", - Type: base.TypeSelect, - Required: true, - Values: "Personal,Family", - }, - { - Name: "site_id", - Label: "family id", - Type: base.TypeString, - }, - { - Name: "order_by", - Label: "order_by", - Type: base.TypeSelect, - Values: "filename,filesize,lastOpTime", - Required: true, - }, - { - Name: "order_direction", - Label: "desc", - Type: base.TypeSelect, - Values: "true,false", - Required: true, - }, - { - Name: "bool_1", - Label: "fast upload", - Type: base.TypeBool, - }, - } -} - -func (driver Cloud189) Save(account *model.Account, old *model.Account) error { - if account == nil { - return nil - } - - if !isFamily(account) && account.RootFolder == "" { - account.RootFolder = "-11" - account.SiteId = "" - } - if isFamily(account) && account.RootFolder == "-11" { - account.RootFolder = "" - } - - state := GetState(account) - if !state.IsLogin(account) { - if err := state.Login(account); err != nil { - return err - } - } - - if isFamily(account) { - list, err := driver.getFamilyInfoList(account) - if err != nil { - return err - } - for _, l := range list { - if account.SiteId == "" { - account.SiteId = fmt.Sprint(l.FamilyID) - } - log.Infof("天翼家庭云 用户名:%s FamilyID %d\n", l.RemarkName, l.FamilyID) - } - } - - account.Status = "work" - model.SaveAccount(account) - return nil -} - -func (driver Cloud189) getFamilyInfoList(account *model.Account) ([]FamilyInfoResp, error) { - var resp FamilyInfoListResp - _, err := GetState(account).Request(http.MethodGet, API_URL+"/family/manage/getFamilyList.action", nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()) - r.SetResult(&resp) - }, account) - if err != nil { - return nil, err - } - return resp.FamilyInfoResp, nil -} - -func (driver Cloud189) File(path string, account *model.Account) (*model.File, error) { - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - dir, name := filepath.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver Cloud189) Files(path string, account *model.Account) ([]model.File, error) { - path = utils.ParsePath(path) - cache, err := base.GetCache(path, account) - if err == nil { - files, _ := cache.([]model.File) - return files, nil - } - - file, err := driver.File(path, account) - if err != nil { - return nil, err - } - - fullUrl := API_URL - if isFamily(account) { - fullUrl += "/family/file" - } - fullUrl += "/listFiles.action" - - files := make([]model.File, 0) - client := GetState(account) - for pageNum := 1; ; pageNum++ { - var resp Cloud189FilesResp - _, err = client.Request(http.MethodGet, fullUrl, nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()). - SetQueryParams(map[string]string{ - "folderId": file.Id, - "fileType": "0", - "mediaAttr": "0", - "iconOption": "5", - "pageNum": fmt.Sprint(pageNum), - "pageSize": "130", - }) - if isFamily(account) { - r.SetQueryParams(map[string]string{ - "familyId": account.SiteId, - "orderBy": toFamilyOrderBy(account.OrderBy), - "descending": account.OrderDirection, - }) - } else { - r.SetQueryParams(map[string]string{ - "recursive": "0", - "orderBy": account.OrderBy, - "descending": account.OrderDirection, - }) - } - r.SetResult(&resp) - }, account) - if err != nil { - return nil, err - } - // 获取完毕跳出 - if resp.FileListAO.Count == 0 { - break - } - - for _, folder := range resp.FileListAO.FolderList { - files = append(files, model.File{ - Id: fmt.Sprint(folder.ID), - Name: folder.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: MustParseTime(folder.LastOpTime), - }) - } - for _, file := range resp.FileListAO.FileList { - files = append(files, model.File{ - Id: fmt.Sprint(file.ID), - Name: file.Name, - Size: file.Size, - Type: utils.GetFileType(filepath.Ext(file.Name)), - Driver: driver.Config().Name, - UpdatedAt: MustParseTime(file.LastOpTime), - Thumbnail: file.Icon.SmallUrl, - }) - } - } - if len(files) > 0 { - _ = base.SetCache(path, files, account) - } - return files, nil -} - -func (driver Cloud189) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - log.Debugf("189PC path: %s", path) - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -func (driver Cloud189) Link(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(utils.ParsePath(args.Path), account) - if err != nil { - return nil, err - } - if file.Type == conf.FOLDER { - return nil, base.ErrNotFile - } - - fullUrl := API_URL - if isFamily(account) { - fullUrl += "/family/file" - } - fullUrl += "/getFileDownloadUrl.action" - - var downloadUrl struct { - URL string `json:"fileDownloadUrl"` - } - _, err = GetState(account).Request(http.MethodGet, fullUrl, nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()).SetQueryParam("fileId", file.Id) - if isFamily(account) { - r.SetQueryParams(map[string]string{ - "familyId": account.SiteId, - }) - } else { - r.SetQueryParams(map[string]string{ - "dt": "3", - "flag": "1", - }) - } - r.SetResult(&downloadUrl) - }, account) - if err != nil { - return nil, err - } - return &base.Link{ - Headers: []base.Header{ - {Name: "User-Agent", Value: base.UserAgent}, - }, - Url: strings.ReplaceAll(downloadUrl.URL, "&", "&"), - }, nil -} - -func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) { - return nil, base.ErrNotSupport -} - -func (driver Cloud189) MakeDir(path string, account *model.Account) error { - dir, name := filepath.Split(path) - parentFile, err := driver.File(dir, account) - if err != nil { - return err - } - if !parentFile.IsDir() { - return base.ErrNotFolder - } - - fullUrl := API_URL - if isFamily(account) { - fullUrl += "/family/file" - } - fullUrl += "/createFolder.action" - - _, err = GetState(account).Request(http.MethodPost, fullUrl, nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()).SetQueryParams(map[string]string{ - "folderName": name, - "relativePath": "", - }) - if isFamily(account) { - r.SetQueryParams(map[string]string{ - "familyId": account.SiteId, - "parentId": parentFile.Id, - }) - } else { - r.SetQueryParams(map[string]string{ - "parentFolderId": parentFile.Id, - }) - } - }, account) - return err -} - -func (driver Cloud189) Move(src string, dst string, account *model.Account) error { - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - - dstDirFile, err := driver.File(filepath.Dir(dst), account) - if err != nil { - return err - } - - _, err = GetState(account).Request(http.MethodPost, API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) { - r.SetFormData(clientSuffix()).SetFormData(map[string]string{ - "type": "MOVE", - "taskInfos": string(MustToBytes(utils.Json.Marshal( - []*BatchTaskInfo{ - { - FileId: srcFile.Id, - FileName: srcFile.Name, - IsFolder: BoolToNumber(srcFile.IsDir()), - }, - }))), - "targetFolderId": dstDirFile.Id, - }) - if isFamily(account) { - r.SetFormData(map[string]string{ - "familyId": account.SiteId, - }) - } - }, account) - return err -} - -/* -func (driver Cloud189) Move(src string, dst string, account *model.Account) error { - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - - dstDirFile, err := driver.File(filepath.Dir(dst), account) - if err != nil { - return err - } - - var queryParam map[string]string - fullUrl := API_URL - method := http.MethodPost - if isFamily(account) { - fullUrl += "/family/file" - method = http.MethodGet - } - if srcFile.IsDir() { - fullUrl += "/moveFolder.action" - queryParam = map[string]string{ - "folderId": srcFile.Id, - "destFolderName": srcFile.Name, - } - } else { - fullUrl += "/moveFile.action" - queryParam = map[string]string{ - "fileId": srcFile.Id, - "destFileName": srcFile.Name, - } - } - - _, err = GetState(account).Request(method, fullUrl, nil, func(r *resty.Request) { - r.SetQueryParams(queryParam).SetQueryParams(clientSuffix()) - if isFamily(account) { - r.SetQueryParams(map[string]string{ - "familyId": account.SiteId, - "destParentId": dstDirFile.Id, - }) - } else { - r.SetQueryParam("destParentFolderId", dstDirFile.Id) - } - }, account) - return err -}*/ - -func (driver Cloud189) Rename(src string, dst string, account *model.Account) error { - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - - var queryParam map[string]string - fullUrl := API_URL - method := http.MethodPost - if isFamily(account) { - fullUrl += "/family/file" - method = http.MethodGet - } - if srcFile.IsDir() { - fullUrl += "/renameFolder.action" - queryParam = map[string]string{ - "folderId": srcFile.Id, - "destFolderName": filepath.Base(dst), - } - } else { - fullUrl += "/renameFile.action" - queryParam = map[string]string{ - "fileId": srcFile.Id, - "destFileName": filepath.Base(dst), - } - } - - _, err = GetState(account).Request(method, fullUrl, nil, func(r *resty.Request) { - r.SetQueryParams(queryParam).SetQueryParams(clientSuffix()) - if isFamily(account) { - r.SetQueryParam("familyId", account.SiteId) - } - }, account) - return err -} - -func (driver Cloud189) Copy(src string, dst string, account *model.Account) error { - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - - dstDirFile, err := driver.File(filepath.Dir(dst), account) - if err != nil { - return err - } - - _, err = GetState(account).Request(http.MethodPost, API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) { - r.SetFormData(clientSuffix()).SetFormData(map[string]string{ - "type": "COPY", - "taskInfos": string(MustToBytes(utils.Json.Marshal( - []*BatchTaskInfo{ - { - FileId: srcFile.Id, - FileName: srcFile.Name, - IsFolder: BoolToNumber(srcFile.IsDir()), - }, - }))), - "targetFolderId": dstDirFile.Id, - "targetFileName": filepath.Base(dst), - }) - if isFamily(account) { - r.SetFormData(map[string]string{ - "familyId": account.SiteId, - }) - } - }, account) - return err -} - -func (driver Cloud189) Delete(path string, account *model.Account) error { - path = utils.ParsePath(path) - srcFile, err := driver.File(path, account) - if err != nil { - return err - } - - _, err = GetState(account).Request(http.MethodPost, API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) { - r.SetFormData(clientSuffix()).SetFormData(map[string]string{ - "type": "DELETE", - "taskInfos": string(MustToBytes(utils.Json.Marshal( - []*BatchTaskInfo{ - { - FileId: srcFile.Id, - FileName: srcFile.Name, - IsFolder: BoolToNumber(srcFile.IsDir()), - }, - }))), - }) - - if isFamily(account) { - r.SetFormData(map[string]string{ - "familyId": account.SiteId, - }) - } - }, account) - return err -} - -func (driver Cloud189) Upload(file *model.FileStream, account *model.Account) error { - if file == nil { - return base.ErrEmptyFile - } - - parentFile, err := driver.File(file.ParentPath, account) - if err != nil { - return err - } - if !parentFile.IsDir() { - return base.ErrNotFolder - } - - if account.Bool1 { - return driver.FastUpload(file, parentFile, account) - } - return driver.CommonUpload(file, parentFile, account) - /* - if isFamily(account) { - return driver.uploadFamily(file, parentFile, account) - } - return driver.uploadPerson(file, parentFile, account) - */ -} - -func (driver Cloud189) CommonUpload(file *model.FileStream, parentFile *model.File, account *model.Account) error { - // 初始化上传 - state := GetState(account) - const DEFAULT int64 = 10485760 - count := int(math.Ceil(float64(file.Size) / float64(DEFAULT))) - - params := Params{ - "parentFolderId": parentFile.Id, - "fileName": url.PathEscape(file.Name), - "fileSize": fmt.Sprint(file.Size), - "sliceSize": fmt.Sprint(DEFAULT), - "lazyCheck": "1", - } - - fullUrl := UPLOAD_URL - if isFamily(account) { - params.Set("familyId", account.SiteId) - fullUrl += "/family" - } else { - //params.Set("extend", `{"opScene":"1","relativepath":"","rootfolderid":""}`) - fullUrl += "/person" - } - - var initMultiUpload InitMultiUploadResp - _, err := state.Request(http.MethodGet, fullUrl+"/initMultiUpload", params, func(r *resty.Request) { r.SetQueryParams(clientSuffix()).SetResult(&initMultiUpload) }, account) - if err != nil { - return err - } - - fileMd5 := md5.New() - silceMd5 := md5.New() - silceMd5Hexs := make([]string, 0, count) - byteData := bytes.NewBuffer(make([]byte, DEFAULT)) - for i := 1; i <= count; i++ { - byteData.Reset() - silceMd5.Reset() - if n, err := io.CopyN(io.MultiWriter(fileMd5, silceMd5, byteData), file, DEFAULT); err != io.EOF && n == 0 { - return err - } - md5Bytes := silceMd5.Sum(nil) - silceMd5Hexs = append(silceMd5Hexs, strings.ToUpper(hex.EncodeToString(md5Bytes))) - silceMd5Base64 := base64.StdEncoding.EncodeToString(md5Bytes) - - var uploadUrl UploadUrlsResp - _, err = state.Request(http.MethodGet, fullUrl+"/getMultiUploadUrls", - Params{"partInfo": fmt.Sprintf("%d-%s", i, silceMd5Base64), "uploadFileId": initMultiUpload.Data.UploadFileID}, - func(r *resty.Request) { r.SetQueryParams(clientSuffix()).SetResult(&uploadUrl) }, - account) - if err != nil { - return err - } - - uploadData := uploadUrl.UploadUrls[fmt.Sprint("partNumber_", i)] - req, _ := http.NewRequest(http.MethodPut, uploadData.RequestURL, byteData) - req.Header.Set("User-Agent", "") - for k, v := range ParseHttpHeader(uploadData.RequestHeader) { - req.Header.Set(k, v) - } - for k, v := range clientSuffix() { - req.URL.RawQuery += fmt.Sprintf("&%s=%s", k, v) - } - r, err := base.HttpClient.Do(req) - if err != nil { - return err - } - if r.StatusCode != http.StatusOK { - data, _ := io.ReadAll(r.Body) - r.Body.Close() - return fmt.Errorf(string(data)) - } - r.Body.Close() - } - - fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil))) - sliceMd5Hex := fileMd5Hex - if int64(file.Size) > DEFAULT { - sliceMd5Hex = strings.ToUpper(utils.GetMD5Encode(strings.Join(silceMd5Hexs, "\n"))) - } - - _, err = state.Request(http.MethodGet, fullUrl+"/commitMultiUploadFile", - Params{ - "uploadFileId": initMultiUpload.Data.UploadFileID, - "fileMd5": fileMd5Hex, - "sliceMd5": sliceMd5Hex, - "lazyCheck": "1", - "isLog": "0", - "opertype": "3", - }, - func(r *resty.Request) { r.SetQueryParams(clientSuffix()) }, account) - return err -} - -func (driver Cloud189) FastUpload(file *model.FileStream, parentFile *model.File, account *model.Account) error { - tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*") - if err != nil { - return err - } - defer func() { - _ = tempFile.Close() - _ = os.Remove(tempFile.Name()) - }() - // 初始化上传 - state := GetState(account) - - const DEFAULT int64 = 10485760 - count := int(math.Ceil(float64(file.Size) / float64(DEFAULT))) - - // 优先计算所需信息 - fileMd5 := md5.New() - silceMd5 := md5.New() - silceMd5Hexs := make([]string, 0, count) - silceMd5Base64s := make([]string, 0, count) - for i := 1; i <= count; i++ { - silceMd5.Reset() - if n, err := io.CopyN(io.MultiWriter(fileMd5, silceMd5, tempFile), file, DEFAULT); err != nil && n == 0 { - return err - } - md5Byte := silceMd5.Sum(nil) - silceMd5Hexs = append(silceMd5Hexs, strings.ToUpper(hex.EncodeToString(md5Byte))) - silceMd5Base64s = append(silceMd5Base64s, fmt.Sprint(i, "-", base64.StdEncoding.EncodeToString(md5Byte))) - } - fileMd5Hex := strings.ToUpper(hex.EncodeToString(fileMd5.Sum(nil))) - sliceMd5Hex := fileMd5Hex - if int64(file.Size) > DEFAULT { - sliceMd5Hex = strings.ToUpper(utils.GetMD5Encode(strings.Join(silceMd5Hexs, "\n"))) - } - - params := Params{ - "parentFolderId": parentFile.Id, - "fileName": url.PathEscape(file.Name), - "fileSize": fmt.Sprint(file.Size), - "fileMd5": fileMd5Hex, - "sliceSize": fmt.Sprint(DEFAULT), - "sliceMd5": sliceMd5Hex, - } - - fullUrl := UPLOAD_URL - if isFamily(account) { - params.Set("familyId", account.SiteId) - fullUrl += "/family" - } else { - //params.Set("extend", `{"opScene":"1","relativepath":"","rootfolderid":""}`) - fullUrl += "/person" - } - - var uploadInfo InitMultiUploadResp - _, err = state.Request(http.MethodGet, fullUrl+"/initMultiUpload", params, func(r *resty.Request) { r.SetQueryParams(clientSuffix()).SetResult(&uploadInfo) }, account) - if err != nil { - return err - } - - if uploadInfo.Data.FileDataExists != 1 { - var uploadUrls UploadUrlsResp - _, err := state.Request(http.MethodGet, fullUrl+"/getMultiUploadUrls", - Params{ - "uploadFileId": uploadInfo.Data.UploadFileID, - "partInfo": strings.Join(silceMd5Base64s, ","), - }, - func(r *resty.Request) { r.SetQueryParams(clientSuffix()).SetResult(&uploadUrls) }, - account) - if err != nil { - return err - } - for i := 1; i <= count; i++ { - uploadData := uploadUrls.UploadUrls[fmt.Sprint("partNumber_", i)] - req, _ := http.NewRequest(http.MethodPut, uploadData.RequestURL, io.NewSectionReader(tempFile, int64(i-1)*DEFAULT, DEFAULT)) - req.Header.Set("User-Agent", "") - for k, v := range ParseHttpHeader(uploadData.RequestHeader) { - req.Header.Set(k, v) - } - for k, v := range clientSuffix() { - req.URL.RawQuery += fmt.Sprintf("&%s=%s", k, v) - } - r, err := base.HttpClient.Do(req) - if err != nil { - return err - } - if r.StatusCode != http.StatusOK { - data, _ := io.ReadAll(r.Body) - r.Body.Close() - return fmt.Errorf(string(data)) - } - r.Body.Close() - } - } - - _, err = state.Request(http.MethodGet, fullUrl+"/commitMultiUploadFile", - Params{ - "uploadFileId": uploadInfo.Data.UploadFileID, - "isLog": "0", - "opertype": "3", - }, - func(r *resty.Request) { r.SetQueryParams(clientSuffix()) }, - account) - return err -} - -/* -func (driver Cloud189) uploadFamily(file *model.FileStream, parentFile *model.File, account *model.Account) error { - tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*") - if err != nil { - return err - } - - defer func() { - _ = tempFile.Close() - _ = os.Remove(tempFile.Name()) - }() - - fileMd5 := md5.New() - if _, err = io.Copy(io.MultiWriter(fileMd5, tempFile), file); err != nil { - return err - } - - client := GetState(account) - var createUpload CreateUploadFileResult - _, err = client.Request(http.MethodGet, API_URL+"/family/file/createFamilyFile.action", nil, func(r *resty.Request) { - r.SetQueryParams(map[string]string{ - "fileMd5": hex.EncodeToString(fileMd5.Sum(nil)), - "fileName": file.Name, - "familyId": account.SiteId, - "parentId": parentFile.Id, - "resumePolicy": "1", - "fileSize": fmt.Sprint(file.Size), - }) - r.SetQueryParams(clientSuffix()) - r.SetResult(&createUpload) - }, account) - if err != nil { - return err - } - - if createUpload.FileDataExists != 1 { - if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil { - return err - } - } - - _, err = client.Request(http.MethodGet, createUpload.FileCommitUrl, nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()) - r.SetHeaders(map[string]string{ - "FamilyId": account.SiteId, - "uploadFileId": fmt.Sprint(createUpload.UploadFileId), - "ResumePolicy": "1", - }) - }, account) - return err -} - -func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.File, account *model.Account) error { - tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*") - if err != nil { - return err - } - - defer func() { - _ = tempFile.Close() - _ = os.Remove(tempFile.Name()) - }() - - fileMd5 := md5.New() - if _, err = io.Copy(io.MultiWriter(fileMd5, tempFile), file); err != nil { - return err - } - - client := GetState(account) - var createUpload CreateUploadFileResult - _, err = client.Request(http.MethodPost, API_URL+"/createUploadFile.action", nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()) - r.SetFormData(clientSuffix()).SetFormData(map[string]string{ - "parentFolderId": parentFile.Id, - "baseFileId": "", - "fileName": file.Name, - "size": fmt.Sprint(file.Size), - "md5": hex.EncodeToString(fileMd5.Sum(nil)), - // "lastWrite": param.LastWrite, - // "localPath": strings.ReplaceAll(file.ParentPath, "\\", "/"), - "opertype": "1", - "flag": "1", - "resumePolicy": "1", - "isLog": "0", - "fileExt": "", - }) - r.SetResult(&createUpload) - }, account) - if err != nil { - return err - } - - if createUpload.FileDataExists != 1 { - if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil { - return err - } - } - - _, err = client.Request(http.MethodPost, createUpload.FileCommitUrl, nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()) - r.SetFormData(map[string]string{ - "uploadFileId": fmt.Sprint(createUpload.UploadFileId), - "opertype": "5", //5 覆盖 1 重命名 - "ResumePolicy": "1", - "isLog": "0", - }) - }, account) - return err -} - -func (driver Cloud189) uploadFileData(file *model.FileStream, tempFile *os.File, createUpload CreateUploadFileResult, account *model.Account) (int64, error) { - uploadFileState, err := driver.getUploadFileState(createUpload.UploadFileId, account) - if err != nil { - return 0, err - } - - if uploadFileState.FileDataExists == 1 || uploadFileState.DataSize == int64(file.Size) { - return uploadFileState.UploadFileId, nil - } - - if _, err = tempFile.Seek(uploadFileState.DataSize, io.SeekStart); err != nil { - return 0, err - } - - _, err = GetState(account).Request("PUT", uploadFileState.FileUploadUrl, nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()) - r.SetHeaders(map[string]string{ - "Content-Type": "application/octet-stream", - "ResumePolicy": "1", - "Edrive-UploadFileRange": fmt.Sprintf("bytes=%d-%d", uploadFileState.DataSize, file.Size), - "Expect": "100-continue", - }) - if isFamily(account) { - r.SetHeaders(map[string]string{ - "familyId": account.SiteId, - "UploadFileId": fmt.Sprint(uploadFileState.UploadFileId), - }) - } else { - r.SetHeader("Edrive-UploadFileId", fmt.Sprint(uploadFileState.UploadFileId)) - } - r.SetBody(tempFile) - }, account) - return uploadFileState.UploadFileId, err -} - -func (driver Cloud189) getUploadFileState(uploadFileId int64, account *model.Account) (*UploadFileStatusResult, error) { - fullUrl := API_URL - if isFamily(account) { - fullUrl += "/family/file/getFamilyFileStatus.action" - } else { - fullUrl += "/getUploadFileStatus.action" - } - var uploadFileState UploadFileStatusResult - _, err := GetState(account).Request(http.MethodGet, fullUrl, nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()) - r.SetQueryParams(map[string]string{ - "uploadFileId": fmt.Sprint(uploadFileId), - "resumePolicy": "1", - }) - if isFamily(account) { - r.SetQueryParam("familyId", account.SiteId) - } - r.SetResult(&uploadFileState) - }, account) - if err != nil { - return nil, err - } - return &uploadFileState, nil -}*/ - -var _ base.Driver = (*Cloud189)(nil) diff --git a/drivers/189pc/type.go b/drivers/189pc/type.go deleted file mode 100644 index b8d2c0af..00000000 --- a/drivers/189pc/type.go +++ /dev/null @@ -1,180 +0,0 @@ -package _189 - -import "encoding/xml" - -type LoginParam struct { - CaptchaToken string - Lt string - ParamId string - ReqId string - jRsaKey string - - vCodeID string - vCodeRS string -} - -// 居然有四种返回方式 -type Erron struct { - ResCode string `json:"res_code"` - ResMessage string `json:"res_message"` - - XMLName xml.Name `xml:"error"` - Code string `json:"code" xml:"code"` - Message string `json:"message" xml:"message"` - - // Code string `json:"code"` - Msg string `json:"msg"` - - ErrorCode string `json:"errorCode"` - ErrorMsg string `json:"errorMsg"` -} - -// 刷新session返回 -type UserSessionResp struct { - ResCode int `json:"res_code"` - ResMessage string `json:"res_message"` - - LoginName string `json:"loginName"` - - KeepAlive int `json:"keepAlive"` - GetFileDiffSpan int `json:"getFileDiffSpan"` - GetUserInfoSpan int `json:"getUserInfoSpan"` - - // 个人云 - SessionKey string `json:"sessionKey"` - SessionSecret string `json:"sessionSecret"` - // 家庭云 - FamilySessionKey string `json:"familySessionKey"` - FamilySessionSecret string `json:"familySessionSecret"` -} - -//登录返回 -type appSessionResp struct { - UserSessionResp - - IsSaveName string `json:"isSaveName"` - - // 会话刷新Token - AccessToken string `json:"accessToken"` - //Token刷新 - RefreshToken string `json:"refreshToken"` -} - -type FamilyInfoListResp struct { - FamilyInfoResp []FamilyInfoResp `json:"familyInfoResp"` -} -type FamilyInfoResp struct { - Count int `json:"count"` - CreateTime string `json:"createTime"` - FamilyID int `json:"familyId"` - RemarkName string `json:"remarkName"` - Type int `json:"type"` - UseFlag int `json:"useFlag"` - UserRole int `json:"userRole"` -} - -/*文件部分*/ -// 文件 -type Cloud189File struct { - CreateDate string `json:"createDate"` - FileCata int64 `json:"fileCata"` - Icon struct { - //iconOption 5 - SmallUrl string `json:"smallUrl"` - LargeUrl string `json:"largeUrl"` - - // iconOption 10 - Max600 string `json:"max600"` - MediumURL string `json:"mediumUrl"` - } `json:"icon"` - ID int64 `json:"id"` - LastOpTime string `json:"lastOpTime"` - Md5 string `json:"md5"` - MediaType int `json:"mediaType"` - Name string `json:"name"` - Orientation int64 `json:"orientation"` - Rev string `json:"rev"` - Size int64 `json:"size"` - StarLabel int64 `json:"starLabel"` -} - -// 文件夹 -type Cloud189Folder struct { - ID int64 `json:"id"` - ParentID int64 `json:"parentId"` - Name string `json:"name"` - - FileCata int64 `json:"fileCata"` - FileCount int64 `json:"fileCount"` - - LastOpTime string `json:"lastOpTime"` - CreateDate string `json:"createDate"` - - FileListSize int64 `json:"fileListSize"` - Rev string `json:"rev"` - StarLabel int64 `json:"starLabel"` -} - -type Cloud189FilesResp struct { - //ResCode int `json:"res_code"` - //ResMessage string `json:"res_message"` - FileListAO struct { - Count int `json:"count"` - FileList []Cloud189File `json:"fileList"` - FolderList []Cloud189Folder `json:"folderList"` - } `json:"fileListAO"` -} - -// TaskInfo 任务信息 -type BatchTaskInfo struct { - // FileId 文件ID - FileId string `json:"fileId"` - // FileName 文件名 - FileName string `json:"fileName"` - // IsFolder 是否是文件夹,0-否,1-是 - IsFolder int `json:"isFolder"` - // SrcParentId 文件所在父目录ID - //SrcParentId string `json:"srcParentId"` -} - -/* -type CreateUploadFileResult struct { - // UploadFileId 上传文件请求ID - UploadFileId int64 `json:"uploadFileId"` - // FileUploadUrl 上传文件数据的URL路径 - FileUploadUrl string `json:"fileUploadUrl"` - // FileCommitUrl 上传文件完成后确认路径 - FileCommitUrl string `json:"fileCommitUrl"` - // FileDataExists 文件是否已存在云盘中,0-未存在,1-已存在 - FileDataExists int `json:"fileDataExists"` -} - -type UploadFileStatusResult struct { - // 上传文件的ID - UploadFileId int64 `json:"uploadFileId"` - // 已上传的大小 - DataSize int64 `json:"dataSize"` - FileUploadUrl string `json:"fileUploadUrl"` - FileCommitUrl string `json:"fileCommitUrl"` - FileDataExists int `json:"fileDataExists"` -} -*/ - -type InitMultiUploadResp struct { - //Code string `json:"code"` - Data struct { - UploadType int `json:"uploadType"` - UploadHost string `json:"uploadHost"` - UploadFileID string `json:"uploadFileId"` - FileDataExists int `json:"fileDataExists"` - } `json:"data"` -} -type UploadUrlsResp struct { - Code string `json:"code"` - UploadUrls map[string]Part `json:"uploadUrls"` -} - -type Part struct { - RequestURL string `json:"requestURL"` - RequestHeader string `json:"requestHeader"` -} diff --git a/drivers/189pc/util.go b/drivers/189pc/util.go deleted file mode 100644 index c31e98e8..00000000 --- a/drivers/189pc/util.go +++ /dev/null @@ -1,177 +0,0 @@ -package _189 - -import ( - "bytes" - "crypto/aes" - "crypto/hmac" - "crypto/rand" - "crypto/rsa" - "crypto/sha1" - "crypto/x509" - "encoding/base64" - "encoding/hex" - "encoding/pem" - "fmt" - rand2 "math/rand" - "net/http" - "net/url" - "sort" - "strings" - "time" - - "github.com/Xhofe/alist/model" -) - -const ( - APP_ID = "8025431004" - CLIENT_TYPE = "10020" - VERSION = "6.2" - - WEB_URL = "https://cloud.189.cn" - AUTH_URL = "https://open.e.189.cn" - API_URL = "https://api.cloud.189.cn" - UPLOAD_URL = "https://upload.cloud.189.cn" - - RETURN_URL = "https://m.cloud.189.cn/zhuanti/2020/loginErrorPc/index.html" - - PC = "TELEPC" - MAC = "TELEMAC" - - CHANNEL_ID = "web_cloud.189.cn" -) - -func clientSuffix() map[string]string { - return map[string]string{ - "clientType": PC, - "version": VERSION, - "channelId": CHANNEL_ID, - "rand": fmt.Sprintf("%d_%d", rand2.Int63n(1e5), rand2.Int63n(1e10)), - } -} - -// 带params的SignatureOfHmac HMAC签名 -func signatureOfHmac(sessionSecret, sessionKey, operate, fullUrl, dateOfGmt, param string) string { - u, _ := url.Parse(fullUrl) - mac := hmac.New(sha1.New, []byte(sessionSecret)) - data := fmt.Sprintf("SessionKey=%s&Operate=%s&RequestURI=%s&Date=%s", sessionKey, operate, u.Path, dateOfGmt) - if param != "" { - data += fmt.Sprintf("¶ms=%s", param) - } - mac.Write([]byte(data)) - return strings.ToUpper(hex.EncodeToString(mac.Sum(nil))) -} - -// 获取http规范的时间 -func getHttpDateStr() string { - return time.Now().UTC().Format(http.TimeFormat) -} - -// RAS 加密用户名密码 -func rsaEncrypt(publicKey, origData string) string { - block, _ := pem.Decode([]byte(publicKey)) - pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes) - data, _ := rsa.EncryptPKCS1v15(rand.Reader, pubInterface.(*rsa.PublicKey), []byte(origData)) - return base64ToHex(base64.StdEncoding.EncodeToString(data)) -} - -// aes 加密params -func AesECBEncrypt(data, key string) string { - block, _ := aes.NewCipher([]byte(key)) - paddingData := PKCS7Padding([]byte(data), block.BlockSize()) - decrypted := make([]byte, len(paddingData)) - size := block.BlockSize() - for src, dst := paddingData, decrypted; len(src) > 0; src, dst = src[size:], dst[size:] { - block.Encrypt(dst[:size], src[:size]) - } - return strings.ToUpper(hex.EncodeToString(decrypted)) -} - -func PKCS7Padding(ciphertext []byte, blockSize int) []byte { - padding := blockSize - len(ciphertext)%blockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(ciphertext, padtext...) -} - -// 时间戳 -func timestamp() int64 { - return time.Now().UTC().UnixNano() / 1e6 -} - -func base64ToHex(a string) string { - v, _ := base64.StdEncoding.DecodeString(a) - return strings.ToUpper(hex.EncodeToString(v)) -} - -func isFamily(account *model.Account) bool { - return account.InternalType == "Family" -} - -func toFamilyOrderBy(o string) string { - switch o { - case "filename": - return "1" - case "filesize": - return "2" - case "lastOpTime": - return "3" - default: - return "1" - } -} - -func ParseHttpHeader(str string) map[string]string { - header := make(map[string]string) - for _, value := range strings.Split(str, "&") { - i := strings.Index(value, "=") - header[strings.TrimSpace(value[0:i])] = strings.TrimSpace(value[i+1:]) - } - return header -} - -func MustString(str string, err error) string { - return str -} - -func MustToBytes(b []byte, err error) []byte { - return b -} - -func BoolToNumber(b bool) int { - if b { - return 1 - } - return 0 -} - -func MustParseTime(str string) *time.Time { - loc, _ := time.LoadLocation("Local") - lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05", str, loc) - return &lastOpTime -} - -type Params map[string]string - -func (p Params) Set(k, v string) { - p[k] = v -} - -func (p Params) Encode() string { - if p == nil { - return "" - } - var buf strings.Builder - keys := make([]string, 0, len(p)) - for k := range p { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - if buf.Len() > 0 { - buf.WriteByte('&') - } - buf.WriteString(k) - buf.WriteByte('=') - buf.WriteString(p[k]) - } - return buf.String() -} diff --git a/drivers/alidrive/alidrive.go b/drivers/alidrive/alidrive.go deleted file mode 100644 index 4bb2a0e7..00000000 --- a/drivers/alidrive/alidrive.go +++ /dev/null @@ -1,209 +0,0 @@ -package alidrive - -import ( - "errors" - "fmt" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - jsoniter "github.com/json-iterator/go" - log "github.com/sirupsen/logrus" - "path/filepath" -) - -var aliClient = resty.New() - -func (driver AliDrive) FormatFile(file *AliFile) *model.File { - f := &model.File{ - Id: file.FileId, - Name: file.Name, - Size: file.Size, - UpdatedAt: file.UpdatedAt, - Thumbnail: file.Thumbnail, - Driver: driver.Config().Name, - Url: file.Url, - } - f.Type = file.GetType() - return f -} - -func (driver AliDrive) GetFiles(fileId string, account *model.Account) ([]AliFile, error) { - marker := "first" - res := make([]AliFile, 0) - for marker != "" { - if marker == "first" { - marker = "" - } - var resp AliFiles - var e AliRespError - _, err := aliClient.R(). - SetResult(&resp). - SetError(&e). - SetHeader("authorization", "Bearer\t"+account.AccessToken). - SetBody(base.Json{ - "drive_id": account.DriveId, - "fields": "*", - "image_thumbnail_process": "image/resize,w_400/format,jpeg", - "image_url_process": "image/resize,w_1920/format,jpeg", - "limit": account.Limit, - "marker": marker, - "order_by": account.OrderBy, - "order_direction": account.OrderDirection, - "parent_file_id": fileId, - "video_thumbnail_process": "video/snapshot,t_0,f_jpg,ar_auto,w_300", - "url_expire_sec": 14400, - }).Post("https://api.aliyundrive.com/v2/file/list") - if err != nil { - return nil, err - } - if e.Code != "" { - if e.Code == "AccessTokenInvalid" { - err = driver.RefreshToken(account) - if err != nil { - return nil, err - } else { - _ = model.SaveAccount(account) - return driver.GetFiles(fileId, account) - } - } - return nil, fmt.Errorf("%s", e.Message) - } - marker = resp.NextMarker - res = append(res, resp.Items...) - } - return res, nil -} - -func (driver AliDrive) GetFile(path string, account *model.Account) (*AliFile, error) { - dir, name := filepath.Split(path) - dir = utils.ParsePath(dir) - _, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - parentFiles_, _ := base.GetCache(dir, account) - parentFiles, _ := parentFiles_.([]AliFile) - for _, file := range parentFiles { - if file.Name == name { - if file.Type == "file" { - return &file, err - } else { - return nil, fmt.Errorf("not file") - } - } - } - return nil, base.ErrPathNotFound -} - -func (driver AliDrive) RefreshToken(account *model.Account) error { - url := "https://auth.aliyundrive.com/v2/account/token" - var resp base.TokenResp - var e AliRespError - _, err := aliClient.R(). - //ForceContentType("application/json"). - SetBody(base.Json{"refresh_token": account.RefreshToken, "grant_type": "refresh_token"}). - SetResult(&resp). - SetError(&e). - Post(url) - if err != nil { - account.Status = err.Error() - return err - } - log.Debugf("%+v,%+v", resp, e) - if e.Code != "" { - account.Status = e.Message - return fmt.Errorf("failed to refresh token: %s", e.Message) - } else { - account.Status = "work" - } - account.RefreshToken, account.AccessToken = resp.RefreshToken, resp.AccessToken - return nil -} - -func (driver AliDrive) rename(fileId, name string, account *model.Account) error { - var resp base.Json - var e AliRespError - _, err := aliClient.R().SetResult(&resp).SetError(&e). - SetHeader("authorization", "Bearer\t"+account.AccessToken). - SetBody(base.Json{ - "check_name_mode": "refuse", - "drive_id": account.DriveId, - "file_id": fileId, - "name": name, - }).Post("https://api.aliyundrive.com/v3/file/update") - if err != nil { - return err - } - if e.Code != "" { - if e.Code == "AccessTokenInvalid" { - err = driver.RefreshToken(account) - if err != nil { - return err - } else { - _ = model.SaveAccount(account) - return driver.rename(fileId, name, account) - } - } - return fmt.Errorf("%s", e.Message) - } - if resp["name"] == name { - return nil - } - return fmt.Errorf("%+v", resp) -} - -func (driver AliDrive) batch(srcId, dstId string, url string, account *model.Account) error { - var e AliRespError - res, err := aliClient.R().SetError(&e). - SetHeader("authorization", "Bearer\t"+account.AccessToken). - SetBody(base.Json{ - "requests": []base.Json{ - { - "headers": base.Json{ - "Content-Type": "application/json", - }, - "method": "POST", - "id": srcId, - "body": base.Json{ - "drive_id": account.DriveId, - "file_id": srcId, - "to_drive_id": account.DriveId, - "to_parent_file_id": dstId, - }, - "url": url, - }, - }, - "resource": "file", - }).Post("https://api.aliyundrive.com/v3/batch") - if err != nil { - return err - } - if e.Code != "" { - if e.Code == "AccessTokenInvalid" { - err = driver.RefreshToken(account) - if err != nil { - return err - } else { - _ = model.SaveAccount(account) - return driver.batch(srcId, dstId, url, account) - } - } - return fmt.Errorf("%s", e.Message) - } - status := jsoniter.Get(res.Body(), "responses", 0, "status").ToInt() - if status < 400 && status >= 100 { - return nil - } - return errors.New(res.String()) -} - -func init() { - base.RegisterDriver(&AliDrive{}) - aliClient. - SetTimeout(base.DefaultTimeout). - SetRetryCount(3). - SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"). - SetHeader("content-type", "application/json"). - SetHeader("origin", "https://www.aliyundrive.com") -} diff --git a/drivers/alidrive/driver.go b/drivers/alidrive/driver.go deleted file mode 100644 index be964c90..00000000 --- a/drivers/alidrive/driver.go +++ /dev/null @@ -1,566 +0,0 @@ -package alidrive - -import ( - "bytes" - "crypto/sha1" - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - "io" - "io/ioutil" - "math" - "math/big" - "net/http" - "os" - "path/filepath" - - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/robfig/cron/v3" - log "github.com/sirupsen/logrus" -) - -type AliDrive struct{} - -func (driver AliDrive) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "AliDrive", - } -} - -func (driver AliDrive) Items() []base.Item { - return []base.Item{ - { - Name: "refresh_token", - Label: "refresh token", - Type: base.TypeString, - Required: true, - }, - { - Name: "root_folder", - Label: "root folder file_id", - Type: base.TypeString, - Required: false, - }, - { - Name: "order_by", - Label: "order_by", - Type: base.TypeSelect, - Values: "name,size,updated_at,created_at", - Required: false, - }, - { - Name: "order_direction", - Label: "order_direction", - Type: base.TypeSelect, - Values: "ASC,DESC", - Required: false, - }, - { - Name: "limit", - Label: "limit", - Type: base.TypeNumber, - Required: false, - Description: ">0 and <=200", - }, - { - Name: "bool_1", - Label: "fast upload", - Type: base.TypeBool, - }, - } -} - -func (driver AliDrive) Save(account *model.Account, old *model.Account) error { - if old != nil { - conf.Cron.Remove(cron.EntryID(old.CronId)) - } - if account == nil { - return nil - } - if account.RootFolder == "" { - account.RootFolder = "root" - } - if account.Limit == 0 { - account.Limit = 200 - } - err := driver.RefreshToken(account) - if err != nil { - return err - } - var resp base.Json - _, _ = aliClient.R().SetResult(&resp). - SetBody("{}"). - SetHeader("authorization", "Bearer\t"+account.AccessToken). - Post("https://api.aliyundrive.com/v2/user/get") - log.Debugf("user info: %+v", resp) - account.DriveId = resp["default_drive_id"].(string) - cronId, err := conf.Cron.AddFunc("@every 2h", func() { - id := account.ID - log.Debugf("ali account id: %d", id) - newAccount, err := model.GetAccountById(id) - log.Debugf("ali account: %+v", newAccount) - if err != nil { - return - } - err = driver.RefreshToken(newAccount) - _ = model.SaveAccount(newAccount) - }) - if err != nil { - return err - } - account.CronId = int(cronId) - err = model.SaveAccount(account) - if err != nil { - return err - } - return nil -} - -func (driver AliDrive) File(path string, account *model.Account) (*model.File, error) { - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - dir, name := filepath.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver AliDrive) Files(path string, account *model.Account) ([]model.File, error) { - path = utils.ParsePath(path) - var rawFiles []AliFile - cache, err := base.GetCache(path, account) - if err == nil { - rawFiles, _ = cache.([]AliFile) - } else { - file, err := driver.File(path, account) - if err != nil { - return nil, err - } - rawFiles, err = driver.GetFiles(file.Id, account) - if err != nil { - return nil, err - } - if len(rawFiles) > 0 { - _ = base.SetCache(path, rawFiles, account) - } - } - files := make([]model.File, 0) - for _, file := range rawFiles { - files = append(files, *driver.FormatFile(&file)) - } - return files, nil -} - -func (driver AliDrive) Link(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(args.Path, account) - if err != nil { - return nil, err - } - var resp base.Json - var e AliRespError - _, err = aliClient.R().SetResult(&resp). - SetError(&e). - SetHeader("authorization", "Bearer\t"+account.AccessToken). - SetBody(base.Json{ - "drive_id": account.DriveId, - "file_id": file.Id, - "expire_sec": 14400, - }).Post("https://api.aliyundrive.com/v2/file/get_download_url") - if err != nil { - return nil, err - } - if e.Code != "" { - if e.Code == "AccessTokenInvalid" { - err = driver.RefreshToken(account) - if err != nil { - return nil, err - } else { - _ = model.SaveAccount(account) - return driver.Link(args, account) - } - } - return nil, fmt.Errorf("%s", e.Message) - } - return &base.Link{ - Headers: []base.Header{ - { - Name: "Referer", - Value: "https://www.aliyundrive.com/", - }, - }, - Url: resp["url"].(string), - }, nil -} - -func (driver AliDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - log.Debugf("ali path: %s", path) - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -//func (driver AliDrive) Proxy(r *http.Request, account *model.Account) { -// r.Header.Del("Origin") -// r.Header.Set("Referer", "https://www.aliyundrive.com/") -//} - -func (driver AliDrive) Preview(path string, account *model.Account) (interface{}, error) { - file, err := driver.GetFile(path, account) - if err != nil { - return nil, err - } - // office - var resp base.Json - var e AliRespError - var url string - req := base.Json{ - "drive_id": account.DriveId, - "file_id": file.FileId, - } - switch file.Category { - case "doc": - { - url = "https://api.aliyundrive.com/v2/file/get_office_preview_url" - req["access_token"] = account.AccessToken - } - case "video": - { - url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info" - req["category"] = "live_transcoding" - } - default: - return nil, base.ErrNotSupport - } - _, err = aliClient.R().SetResult(&resp).SetError(&e). - SetHeader("authorization", "Bearer\t"+account.AccessToken). - SetBody(req).Post(url) - if err != nil { - return nil, err - } - if e.Code != "" { - return nil, fmt.Errorf("%s", e.Message) - } - return resp, nil -} - -func (driver AliDrive) MakeDir(path string, account *model.Account) error { - dir, name := filepath.Split(path) - parentFile, err := driver.File(dir, account) - if err != nil { - return err - } - if !parentFile.IsDir() { - return base.ErrNotFolder - } - var resp base.Json - var e AliRespError - _, err = aliClient.R().SetResult(&resp).SetError(&e). - SetHeader("authorization", "Bearer\t"+account.AccessToken). - SetBody(base.Json{ - "check_name_mode": "refuse", - "drive_id": account.DriveId, - "name": name, - "parent_file_id": parentFile.Id, - "type": "folder", - }).Post("https://api.aliyundrive.com/adrive/v2/file/createWithFolders") - if e.Code != "" { - if e.Code == "AccessTokenInvalid" { - err = driver.RefreshToken(account) - if err != nil { - return err - } else { - _ = model.SaveAccount(account) - return driver.MakeDir(path, account) - } - } - return fmt.Errorf("%s", e.Message) - } - if resp["file_name"] == name { - return nil - } - return fmt.Errorf("%+v", resp) -} - -func (driver AliDrive) Move(src string, dst string, account *model.Account) error { - dstDir, _ := filepath.Split(dst) - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - dstDirFile, err := driver.File(dstDir, account) - if err != nil { - return err - } - err = driver.batch(srcFile.Id, dstDirFile.Id, "/file/move", account) - return err -} - -func (driver AliDrive) Rename(src string, dst string, account *model.Account) error { - _, dstName := filepath.Split(dst) - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - err = driver.rename(srcFile.Id, dstName, account) - return err -} - -func (driver AliDrive) Copy(src string, dst string, account *model.Account) error { - dstDir, _ := filepath.Split(dst) - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - dstDirFile, err := driver.File(dstDir, account) - if err != nil { - return err - } - err = driver.batch(srcFile.Id, dstDirFile.Id, "/file/copy", account) - return err -} - -func (driver AliDrive) Delete(path string, account *model.Account) error { - file, err := driver.File(path, account) - if err != nil { - return err - } - var e AliRespError - res, err := aliClient.R().SetError(&e). - SetHeader("authorization", "Bearer\t"+account.AccessToken). - SetBody(base.Json{ - "drive_id": account.DriveId, - "file_id": file.Id, - }).Post("https://api.aliyundrive.com/v2/recyclebin/trash") - if err != nil { - return err - } - if e.Code != "" { - if e.Code == "AccessTokenInvalid" { - err = driver.RefreshToken(account) - if err != nil { - return err - } else { - _ = model.SaveAccount(account) - return driver.Delete(path, account) - } - } - return fmt.Errorf("%s", e.Message) - } - if res.StatusCode() < 400 { - return nil - } - return errors.New(res.String()) -} - -type UploadResp struct { - FileId string `json:"file_id"` - UploadId string `json:"upload_id"` - PartInfoList []struct { - UploadUrl string `json:"upload_url"` - } `json:"part_info_list"` - - RapidUpload bool `json:"rapid_upload"` -} - -func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) error { - if file == nil { - return base.ErrEmptyFile - } - - parentFile, err := driver.File(file.ParentPath, account) - if err != nil { - return err - } - if !parentFile.IsDir() { - return base.ErrNotFolder - } - - const DEFAULT int64 = 10485760 - var count = int(math.Ceil(float64(file.GetSize()) / float64(DEFAULT))) - - partInfoList := make([]base.Json, 0, count) - for i := 1; i <= count; i++ { - partInfoList = append(partInfoList, base.Json{"part_number": i}) - } - - reqBody := base.Json{ - "check_name_mode": "overwrite", - "drive_id": account.DriveId, - "name": file.GetFileName(), - "parent_file_id": parentFile.Id, - "part_info_list": partInfoList, - "size": file.GetSize(), - "type": "file", - } - - if account.Bool1 { - buf := bytes.NewBuffer(make([]byte, 0, 1024)) - io.CopyN(buf, file, 1024) - reqBody["pre_hash"] = utils.GetSHA1Encode(buf.String()) - // 把头部拼接回去 - file.File = struct { - io.Reader - io.Closer - }{ - Reader: io.MultiReader(buf, file.File), - Closer: file.File, - } - } else { - reqBody["content_hash_name"] = "none" - reqBody["proof_version"] = "v1" - } - - var resp UploadResp - var e AliRespError - client := aliClient.R().SetResult(&resp).SetError(&e).SetHeader("authorization", "Bearer\t"+account.AccessToken).SetBody(reqBody) - - _, err = client.Post("https://api.aliyundrive.com/adrive/v2/file/createWithFolders") - if err != nil { - return err - } - if e.Code != "" && e.Code != "PreHashMatched" { - if e.Code == "AccessTokenInvalid" { - err = driver.RefreshToken(account) - if err != nil { - return err - } else { - _ = model.SaveAccount(account) - return driver.Upload(file, account) - } - } - return fmt.Errorf("%s", e.Message) - } - - if account.Bool1 && e.Code == "PreHashMatched" { - tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*") - if err != nil { - return err - } - - defer func() { - _ = tempFile.Close() - _ = os.Remove(tempFile.Name()) - }() - - delete(reqBody, "pre_hash") - h := sha1.New() - if _, err = io.Copy(io.MultiWriter(tempFile, h), file.File); err != nil { - return err - } - reqBody["content_hash"] = hex.EncodeToString(h.Sum(nil)) - reqBody["content_hash_name"] = "sha1" - reqBody["proof_version"] = "v1" - - /* - js 隐性转换太坑不知道有没有bug - var n = e.access_token, - r = new BigNumber('0x'.concat(md5(n).slice(0, 16))), - i = new BigNumber(t.file.size), - o = i ? r.mod(i) : new gt.BigNumber(0); - (t.file.slice(o.toNumber(), Math.min(o.plus(8).toNumber(), t.file.size))) - */ - buf := make([]byte, 8) - r, _ := new(big.Int).SetString(utils.GetMD5Encode(account.AccessToken)[:16], 16) - i := new(big.Int).SetUint64(file.Size) - o := r.Mod(r, i) - n, _ := io.NewSectionReader(tempFile, o.Int64(), 8).Read(buf[:8]) - reqBody["proof_code"] = base64.StdEncoding.EncodeToString(buf[:n]) - - _, err = client.Post("https://api.aliyundrive.com/adrive/v2/file/createWithFolders") - if err != nil { - return err - } - if e.Code != "" && e.Code != "PreHashMatched" { - return fmt.Errorf("%s", e.Message) - } - - if resp.RapidUpload { - return nil - } - - // 秒传失败 - if _, err = tempFile.Seek(0, io.SeekStart); err != nil { - return err - } - file.File = tempFile - } - - for _, partInfo := range resp.PartInfoList { - req, err := http.NewRequest("PUT", partInfo.UploadUrl, io.LimitReader(file.File, DEFAULT)) - if err != nil { - return err - } - res, err := base.HttpClient.Do(req) - if err != nil { - return err - } - log.Debugf("%+v", res) - res.Body.Close() - //res, err := base.BaseClient.R(). - // SetHeader("Content-Type",""). - // SetBody(byteData).Put(resp.PartInfoList[i].UploadUrl) - //if err != nil { - // return err - //} - //log.Debugf("put to %s : %d,%s", resp.PartInfoList[i].UploadUrl, res.StatusCode(),res.String()) - } - var resp2 base.Json - _, err = aliClient.R().SetResult(&resp2).SetError(&e). - SetHeader("authorization", "Bearer\t"+account.AccessToken). - SetBody(base.Json{ - "drive_id": account.DriveId, - "file_id": resp.FileId, - "upload_id": resp.UploadId, - }).Post("https://api.aliyundrive.com/v2/file/complete") - if err != nil { - return err - } - if e.Code != "" && e.Code != "PreHashMatched" { - //if e.Code == "AccessTokenInvalid" { - // err = driver.RefreshToken(account) - // if err != nil { - // return err - // } else { - // _ = model.SaveAccount(account) - // return driver.Upload(file, account) - // } - //} - return fmt.Errorf("%s", e.Message) - } - if resp2["file_id"] == resp.FileId { - return nil - } - return fmt.Errorf("%+v", resp2) -} - -var _ base.Driver = (*AliDrive)(nil) diff --git a/drivers/alidrive/types.go b/drivers/alidrive/types.go deleted file mode 100644 index 93e3f286..00000000 --- a/drivers/alidrive/types.go +++ /dev/null @@ -1,53 +0,0 @@ -package alidrive - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/utils" - "time" -) - -type AliRespError struct { - Code string `json:"code"` - Message string `json:"message"` -} - -type AliFiles struct { - Items []AliFile `json:"items"` - NextMarker string `json:"next_marker"` -} - -type AliFile struct { - DriveId string `json:"drive_id"` - CreatedAt *time.Time `json:"created_at"` - FileExtension string `json:"file_extension"` - FileId string `json:"file_id"` - Type string `json:"type"` - Name string `json:"name"` - Category string `json:"category"` - ParentFileId string `json:"parent_file_id"` - UpdatedAt *time.Time `json:"updated_at"` - Size int64 `json:"size"` - Thumbnail string `json:"thumbnail"` - Url string `json:"url"` -} - -func (f AliFile) GetSize() uint64 { - return uint64(f.Size) -} - -func (f AliFile) GetName() string { - return f.Name -} - -func (f AliFile) GetType() int { - if f.Type == "folder" { - return conf.FOLDER - } - if f.Category == "video" { - return conf.VIDEO - } - if f.Category == "image" { - return conf.IMAGE - } - return utils.GetFileType(f.FileExtension) -} diff --git a/drivers/alist/alist.go b/drivers/alist/alist.go deleted file mode 100644 index 4e287cd1..00000000 --- a/drivers/alist/alist.go +++ /dev/null @@ -1,44 +0,0 @@ -package alist - -import ( - "errors" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" -) - -type BaseResp struct { - Code int `json:"code"` - Message string `json:"message"` -} - -type PathResp struct { - BaseResp - Data struct { - Type string `json:"type"` - //Meta Meta `json:"meta"` - Files []model.File `json:"files"` - } `json:"data"` -} - -type PreviewResp struct { - BaseResp - Data interface{} `json:"data"` -} - -func (driver *Alist) Login(account *model.Account) error { - var resp BaseResp - _, err := base.RestyClient.R().SetResult(&resp). - SetHeader("Authorization", account.AccessToken). - Get(account.SiteUrl + "/api/admin/login") - if err != nil { - return err - } - if resp.Code != 200 { - return errors.New(resp.Message) - } - return nil -} - -func init() { - base.RegisterDriver(&Alist{}) -} diff --git a/drivers/alist/driver.go b/drivers/alist/driver.go deleted file mode 100644 index 3919ff5a..00000000 --- a/drivers/alist/driver.go +++ /dev/null @@ -1,192 +0,0 @@ -package alist - -import ( - "errors" - "fmt" - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "path/filepath" - "strings" - "time" -) - -type Alist struct{} - -func (driver Alist) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "Alist", - NoNeedSetLink: true, - NoCors: true, - } -} - -func (driver Alist) Items() []base.Item { - return []base.Item{ - { - Name: "site_url", - Label: "alist site url", - Type: base.TypeString, - Required: true, - }, - { - Name: "access_token", - Label: "token", - Type: base.TypeString, - Description: "admin token", - Required: true, - }, - { - Name: "root_folder", - Label: "root folder path", - Type: base.TypeString, - Required: false, - }, - } -} - -func (driver Alist) Save(account *model.Account, old *model.Account) error { - if account == nil { - return nil - } - account.SiteUrl = strings.TrimRight(account.SiteUrl, "/") - if account.RootFolder == "" { - account.RootFolder = "/" - } - err := driver.Login(account) - if err == nil { - account.Status = "work" - } else { - account.Status = err.Error() - } - _ = model.SaveAccount(account) - return err -} - -func (driver Alist) File(path string, account *model.Account) (*model.File, error) { - now := time.Now() - if path == "/" { - return &model.File{ - Id: "root", - Name: "root", - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: &now, - }, nil - } - _, files, err := driver.Path(utils.Dir(path), account) - if err != nil { - return nil, err - } - if files == nil { - return nil, base.ErrPathNotFound - } - name := utils.Base(path) - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver Alist) Files(path string, account *model.Account) ([]model.File, error) { - //return nil, base.ErrNotImplement - _, files, err := driver.Path(path, account) - if err != nil { - return nil, err - } - if files == nil { - return nil, base.ErrNotFolder - } - return files, nil -} - -func (driver Alist) Link(args base.Args, account *model.Account) (*base.Link, error) { - path := args.Path - path = utils.ParsePath(path) - name := utils.Base(path) - flag := "d" - if utils.GetFileType(filepath.Ext(path)) == conf.TEXT { - flag = "p" - } - link := base.Link{} - link.Url = fmt.Sprintf("%s/%s%s?sign=%s", account.SiteUrl, flag, path, utils.SignWithToken(name, conf.Token)) - return &link, nil -} - -func (driver Alist) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - path = filepath.Join(account.RootFolder, path) - path = strings.ReplaceAll(path, "\\", "/") - cache, err := base.GetCache(path, account) - if err == nil { - files := cache.([]model.File) - return nil, files, nil - } - var resp PathResp - _, err = base.RestyClient.R().SetResult(&resp). - SetHeader("Authorization", account.AccessToken). - SetBody(base.Json{ - "path": path, - }).Post(account.SiteUrl + "/api/public/path") - if err != nil { - return nil, nil, err - } - if resp.Code != 200 { - return nil, nil, errors.New(resp.Message) - } - if resp.Data.Type == "file" { - return &resp.Data.Files[0], nil, nil - } - if len(resp.Data.Files) > 0 { - _ = base.SetCache(path, resp.Data.Files, account) - } - return nil, resp.Data.Files, nil -} - -//func (driver Alist) Proxy(r *http.Request, account *model.Account) {} - -func (driver Alist) Preview(path string, account *model.Account) (interface{}, error) { - var resp PathResp - _, err := base.RestyClient.R().SetResult(&resp). - SetHeader("Authorization", account.AccessToken). - SetBody(base.Json{ - "path": path, - }).Post(account.SiteUrl + "/api/public/preview") - if err != nil { - return nil, err - } - if resp.Code != 200 { - return nil, errors.New(resp.Message) - } - return resp.Data, nil -} - -func (driver Alist) MakeDir(path string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver Alist) Move(src string, dst string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver Alist) Rename(src string, dst string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver Alist) Copy(src string, dst string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver Alist) Delete(path string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver Alist) Upload(file *model.FileStream, account *model.Account) error { - return base.ErrNotImplement -} - -var _ base.Driver = (*Alist)(nil) diff --git a/drivers/all.go b/drivers/all.go deleted file mode 100644 index cf0fb8e5..00000000 --- a/drivers/all.go +++ /dev/null @@ -1,52 +0,0 @@ -package drivers - -import ( - _ "github.com/Xhofe/alist/drivers/123" - _ "github.com/Xhofe/alist/drivers/139" - _ "github.com/Xhofe/alist/drivers/189" - _ "github.com/Xhofe/alist/drivers/189pc" - _ "github.com/Xhofe/alist/drivers/alidrive" - _ "github.com/Xhofe/alist/drivers/alist" - _ "github.com/Xhofe/alist/drivers/baidu" - "github.com/Xhofe/alist/drivers/base" - _ "github.com/Xhofe/alist/drivers/ftp" - _ "github.com/Xhofe/alist/drivers/google" - _ "github.com/Xhofe/alist/drivers/lanzou" - _ "github.com/Xhofe/alist/drivers/mediatrack" - _ "github.com/Xhofe/alist/drivers/native" - _ "github.com/Xhofe/alist/drivers/onedrive" - _ "github.com/Xhofe/alist/drivers/pikpak" - _ "github.com/Xhofe/alist/drivers/quark" - _ "github.com/Xhofe/alist/drivers/s3" - _ "github.com/Xhofe/alist/drivers/sftp" - _ "github.com/Xhofe/alist/drivers/shandian" - _ "github.com/Xhofe/alist/drivers/teambition" - _ "github.com/Xhofe/alist/drivers/uss" - _ "github.com/Xhofe/alist/drivers/webdav" - _ "github.com/Xhofe/alist/drivers/xunlei" - _ "github.com/Xhofe/alist/drivers/yandex" - _ "github.com/Xhofe/alist/drivers/baiduphoto" - log "github.com/sirupsen/logrus" - "strings" -) - -var NoCors string -var NoUpload string - -func GetConfig() { - for k, v := range base.GetDriversMap() { - if v.Config().NoCors { - NoCors += k + "," - } - if v.Upload(nil, nil) != base.ErrEmptyFile { - NoUpload += k + "," - } - } - NoCors = strings.Trim(NoCors, ",") - NoUpload += "root" -} - -func init() { - log.Debug("all init") - GetConfig() -} diff --git a/drivers/baidu/baidu.go b/drivers/baidu/baidu.go deleted file mode 100644 index fe0b7ca1..00000000 --- a/drivers/baidu/baidu.go +++ /dev/null @@ -1,185 +0,0 @@ -package baidu - -import ( - "fmt" - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - jsoniter "github.com/json-iterator/go" - "path" - "strconv" -) - -func (driver Baidu) RefreshToken(account *model.Account) error { - err := driver.refreshToken(account) - if err != nil && err == base.ErrEmptyToken { - err = driver.refreshToken(account) - } - if err != nil { - account.Status = err.Error() - } - _ = model.SaveAccount(account) - return err -} - -func (driver Baidu) refreshToken(account *model.Account) error { - u := "https://openapi.baidu.com/oauth/2.0/token" - var resp base.TokenResp - var e TokenErrResp - _, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetQueryParams(map[string]string{ - "grant_type": "refresh_token", - "refresh_token": account.RefreshToken, - "client_id": account.ClientId, - "client_secret": account.ClientSecret, - }).Get(u) - if err != nil { - return err - } - if e.Error != "" { - return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription) - } - if resp.RefreshToken == "" { - return base.ErrEmptyToken - } - account.Status = "work" - account.AccessToken, account.RefreshToken = resp.AccessToken, resp.RefreshToken - return nil -} - -func (driver Baidu) Request(fullurl string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) { - req := base.RestyClient.R() - req.SetQueryParam("access_token", account.AccessToken) - if headers != nil { - req.SetHeaders(headers) - } - if query != nil { - req.SetQueryParams(query) - } - if form != nil { - req.SetFormData(form) - } - if data != nil { - req.SetBody(data) - } - if resp != nil { - req.SetResult(resp) - } - var res *resty.Response - var err error - switch method { - case base.Get: - res, err = req.Get(fullurl) - case base.Post: - res, err = req.Post(fullurl) - case base.Patch: - res, err = req.Patch(fullurl) - case base.Delete: - res, err = req.Delete(fullurl) - case base.Put: - res, err = req.Put(fullurl) - default: - return nil, base.ErrNotSupport - } - if err != nil { - return nil, err - } - //log.Debug(res.String()) - errno := jsoniter.Get(res.Body(), "errno").ToInt() - if errno != 0 { - if errno == -6 { - err = driver.RefreshToken(account) - if err != nil { - return nil, err - } - return driver.Request(fullurl, method, headers, query, form, data, resp, account) - } - return nil, fmt.Errorf("errno: %d, refer to https://pan.baidu.com/union/doc/", errno) - } - return res.Body(), nil -} - -func (driver Baidu) Get(pathname string, params map[string]string, resp interface{}, account *model.Account) ([]byte, error) { - return driver.Request("https://pan.baidu.com/rest/2.0"+pathname, base.Get, nil, params, nil, nil, resp, account) -} - -func (driver Baidu) Post(pathname string, params map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) { - return driver.Request("https://pan.baidu.com/rest/2.0"+pathname, base.Post, nil, params, nil, data, resp, account) -} - -func (driver Baidu) manage(opera string, filelist interface{}, account *model.Account) ([]byte, error) { - params := map[string]string{ - "method": "filemanager", - "opera": opera, - } - marshal, err := utils.Json.Marshal(filelist) - if err != nil { - return nil, err - } - data := fmt.Sprintf("async=0&filelist=%s&ondup=newcopy", string(marshal)) - return driver.Post("/xpan/file", params, data, nil, account) -} - -func (driver Baidu) GetFiles(dir string, account *model.Account) ([]model.File, error) { - dir = utils.Join(account.RootFolder, dir) - start := 0 - limit := 200 - params := map[string]string{ - "method": "list", - "dir": dir, - "web": "web", - } - if account.OrderBy != "" { - params["order"] = account.OrderBy - if account.OrderDirection == "desc" { - params["desc"] = "1" - } - } - res := make([]model.File, 0) - for { - params["start"] = strconv.Itoa(start) - params["limit"] = strconv.Itoa(limit) - start += limit - var resp ListResp - _, err := driver.Get("/xpan/file", params, &resp, account) - if err != nil { - return nil, err - } - if len(resp.List) == 0 { - break - } - for _, f := range resp.List { - file := model.File{ - Id: strconv.FormatInt(f.FsId, 10), - Name: f.ServerFilename, - Size: f.Size, - Driver: driver.Config().Name, - UpdatedAt: getTime(f.ServerMtime), - Thumbnail: f.Thumbs.Url3, - } - if f.Isdir == 1 { - file.Type = conf.FOLDER - } else { - file.Type = utils.GetFileType(path.Ext(f.ServerFilename)) - } - res = append(res, file) - } - } - return res, nil -} - -func (driver Baidu) create(path string, size uint64, isdir int, uploadid, block_list string, account *model.Account) ([]byte, error) { - params := map[string]string{ - "method": "create", - } - data := fmt.Sprintf("path=%s&size=%d&isdir=%d", path, size, isdir) - if uploadid != "" { - data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list) - } - return driver.Post("/xpan/file", params, data, nil, account) -} - -func init() { - base.RegisterDriver(&Baidu{}) -} diff --git a/drivers/baidu/driver.go b/drivers/baidu/driver.go deleted file mode 100644 index 1f17d21b..00000000 --- a/drivers/baidu/driver.go +++ /dev/null @@ -1,399 +0,0 @@ -package baidu - -import ( - "bytes" - "crypto/md5" - "encoding/hex" - "fmt" - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - log "github.com/sirupsen/logrus" - "io" - "io/ioutil" - "math" - "os" - "path/filepath" - "strconv" - "strings" -) - -type Baidu struct{} - -func (driver Baidu) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "Baidu.Disk", - } -} - -func (driver Baidu) Items() []base.Item { - return []base.Item{ - { - Name: "refresh_token", - Label: "refresh token", - Type: base.TypeString, - Required: true, - }, - { - Name: "root_folder", - Label: "root folder path", - Type: base.TypeString, - Default: "/", - Required: true, - }, - { - Name: "order_by", - Label: "order_by", - Type: base.TypeSelect, - Default: "name", - Values: "name,time,size", - Required: false, - }, - { - Name: "order_direction", - Label: "order_direction", - Type: base.TypeSelect, - Values: "asc,desc", - Default: "asc", - Required: false, - }, - { - Name: "internal_type", - Label: "download api", - Type: base.TypeSelect, - Required: true, - Values: "official,crack", - Default: "official", - }, - { - Name: "client_id", - Label: "client id", - Default: "iYCeC9g08h5vuP9UqvPHKKSVrKFXGa1v", - Type: base.TypeString, - Required: true, - }, - { - Name: "client_secret", - Label: "client secret", - Default: "jXiFMOPVPCWlO2M5CwWQzffpNPaGTRBG", - Type: base.TypeString, - Required: true, - }, - } -} - -func (driver Baidu) Save(account *model.Account, old *model.Account) error { - if account == nil { - return nil - } - return driver.RefreshToken(account) -} - -func (driver Baidu) File(path string, account *model.Account) (*model.File, error) { - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - dir, name := filepath.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver Baidu) Files(path string, account *model.Account) ([]model.File, error) { - path = utils.ParsePath(path) - cache, err := base.GetCache(path, account) - if err == nil { - files, _ := cache.([]model.File) - return files, nil - } - files, err := driver.GetFiles(path, account) - if err != nil { - return nil, err - } - if len(files) > 0 { - _ = base.SetCache(path, files, account) - } - return files, nil -} - -func (driver Baidu) Link(args base.Args, account *model.Account) (*base.Link, error) { - if account.InternalType == "crack" { - return driver.LinkCrack(args, account) - } - return driver.LinkOfficial(args, account) -} - -func (driver Baidu) LinkOfficial(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(args.Path, account) - if err != nil { - return nil, err - } - if file.IsDir() { - return nil, base.ErrNotFile - } - var resp DownloadResp - params := map[string]string{ - "method": "filemetas", - "fsids": fmt.Sprintf("[%s]", file.Id), - "dlink": "1", - } - _, err = driver.Get("/xpan/multimedia", params, &resp, account) - if err != nil { - return nil, err - } - u := fmt.Sprintf("%s&access_token=%s", resp.List[0].Dlink, account.AccessToken) - res, err := base.NoRedirectClient.R().SetHeader("User-Agent", "pan.baidu.com").Head(u) - if err != nil { - return nil, err - } - //if res.StatusCode() == 302 { - u = res.Header().Get("location") - //} - return &base.Link{ - Url: u, - Headers: []base.Header{ - {Name: "User-Agent", Value: "pan.baidu.com"}, - }}, nil -} - -func (driver Baidu) LinkCrack(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(args.Path, account) - if err != nil { - return nil, err - } - if file.IsDir() { - return nil, base.ErrNotFile - } - var resp DownloadResp2 - param := map[string]string{ - "target": fmt.Sprintf("[\"%s\"]", utils.Join(account.RootFolder, args.Path)), - "dlink": "1", - "web": "5", - "origin": "dlna", - } - _, err = driver.Request("https://pan.baidu.com/api/filemetas", base.Get, nil, param, nil, nil, &resp, account) - if err != nil { - return nil, err - } - return &base.Link{ - Url: resp.Info[0].Dlink, - Headers: []base.Header{ - {Name: "User-Agent", Value: "pan.baidu.com"}, - }}, nil -} - -func (driver Baidu) Path(path string, account *model.Account) (*model.File, []model.File, error) { - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -//func (driver Baidu) Proxy(r *http.Request, account *model.Account) { -// r.Header.Set("User-Agent", "pan.baidu.com") -//} - -func (driver Baidu) Preview(path string, account *model.Account) (interface{}, error) { - return nil, base.ErrNotSupport -} - -func (driver Baidu) MakeDir(path string, account *model.Account) error { - _, err := driver.create(utils.Join(account.RootFolder, path), 0, 1, "", "", account) - return err -} - -func (driver Baidu) Move(src string, dst string, account *model.Account) error { - path := utils.Join(account.RootFolder, src) - dest, newname := utils.Split(utils.Join(account.RootFolder, dst)) - data := []base.Json{ - { - "path": path, - "dest": dest, - "newname": newname, - }, - } - _, err := driver.manage("move", data, account) - return err -} - -func (driver Baidu) Rename(src string, dst string, account *model.Account) error { - path := utils.Join(account.RootFolder, src) - newname := utils.Base(dst) - data := []base.Json{ - { - "path": path, - "newname": newname, - }, - } - _, err := driver.manage("rename", data, account) - return err -} - -func (driver Baidu) Copy(src string, dst string, account *model.Account) error { - path := utils.Join(account.RootFolder, src) - dest, newname := utils.Split(utils.Join(account.RootFolder, dst)) - data := []base.Json{ - { - "path": path, - "dest": dest, - "newname": newname, - }, - } - _, err := driver.manage("copy", data, account) - return err -} - -func (driver Baidu) Delete(path string, account *model.Account) error { - path = utils.Join(account.RootFolder, path) - data := []string{path} - _, err := driver.manage("delete", data, account) - return err -} - -func (driver Baidu) Upload(file *model.FileStream, account *model.Account) error { - if file == nil { - return base.ErrEmptyFile - } - tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*") - if err != nil { - return err - } - defer func() { - _ = tempFile.Close() - _ = os.Remove(tempFile.Name()) - }() - _, err = io.Copy(tempFile, file) - if err != nil { - return err - } - _, err = tempFile.Seek(0, io.SeekStart) - if err != nil { - return err - } - var Default uint64 = 4 * 1024 * 1024 - defaultByteData := make([]byte, Default) - count := int(math.Ceil(float64(file.GetSize()) / float64(Default))) - var SliceSize uint64 = 256 * 1024 - // cal md5 - h1 := md5.New() - h2 := md5.New() - block_list := make([]string, 0) - content_md5 := "" - slice_md5 := "" - left := file.GetSize() - for i := 0; i < count; i++ { - byteSize := Default - var byteData []byte - if left < Default { - byteSize = left - byteData = make([]byte, byteSize) - } else { - byteData = defaultByteData - } - left -= byteSize - _, err = io.ReadFull(tempFile, byteData) - if err != nil { - return err - } - h1.Write(byteData) - h2.Write(byteData) - block_list = append(block_list, fmt.Sprintf("\"%s\"", hex.EncodeToString(h2.Sum(nil)))) - h2.Reset() - } - content_md5 = hex.EncodeToString(h1.Sum(nil)) - _, err = tempFile.Seek(0, io.SeekStart) - if err != nil { - return err - } - if file.GetSize() <= SliceSize { - slice_md5 = content_md5 - } else { - sliceData := make([]byte, SliceSize) - _, err = io.ReadFull(tempFile, sliceData) - if err != nil { - return err - } - h2.Write(sliceData) - slice_md5 = hex.EncodeToString(h2.Sum(nil)) - _, err = tempFile.Seek(0, io.SeekStart) - if err != nil { - return err - } - } - path := encodeURIComponent(utils.Join(account.RootFolder, file.ParentPath, file.Name)) - block_list_str := fmt.Sprintf("[%s]", strings.Join(block_list, ",")) - data := fmt.Sprintf("path=%s&size=%d&isdir=0&autoinit=1&block_list=%s&content-md5=%s&slice-md5=%s", - path, file.GetSize(), - block_list_str, - content_md5, slice_md5) - params := map[string]string{ - "method": "precreate", - } - var precreateResp PrecreateResp - _, err = driver.Post("/xpan/file", params, data, &precreateResp, account) - if err != nil { - return err - } - log.Debugf("%+v", precreateResp) - if precreateResp.ReturnType == 2 { - return nil - } - params = map[string]string{ - "method": "upload", - "access_token": account.AccessToken, - "type": "tmpfile", - "path": path, - "uploadid": precreateResp.Uploadid, - } - left = file.GetSize() - for _, partseq := range precreateResp.BlockList { - byteSize := Default - var byteData []byte - if left < Default { - byteSize = left - byteData = make([]byte, byteSize) - } else { - byteData = defaultByteData - } - left -= byteSize - _, err = io.ReadFull(tempFile, byteData) - if err != nil { - return err - } - u := "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2" - params["partseq"] = strconv.Itoa(partseq) - res, err := base.RestyClient.R().SetQueryParams(params).SetFileReader("file", file.Name, bytes.NewReader(byteData)).Post(u) - if err != nil { - return err - } - log.Debugln(res.String()) - } - _, err = driver.create(path, file.GetSize(), 0, precreateResp.Uploadid, block_list_str, account) - return err -} - -var _ base.Driver = (*Baidu)(nil) diff --git a/drivers/baidu/types.go b/drivers/baidu/types.go deleted file mode 100644 index 5b4d2a71..00000000 --- a/drivers/baidu/types.go +++ /dev/null @@ -1,143 +0,0 @@ -package baidu - -type TokenErrResp struct { - ErrorDescription string `json:"error_description"` - Error string `json:"error"` -} - -type File struct { - //TkbindId int `json:"tkbind_id"` - //OwnerType int `json:"owner_type"` - //Category int `json:"category"` - //RealCategory string `json:"real_category"` - FsId int64 `json:"fs_id"` - ServerMtime int64 `json:"server_mtime"` - //OperId int `json:"oper_id"` - //ServerCtime int `json:"server_ctime"` - Thumbs struct { - //Icon string `json:"icon"` - Url3 string `json:"url3"` - //Url2 string `json:"url2"` - //Url1 string `json:"url1"` - } `json:"thumbs"` - //Wpfile int `json:"wpfile"` - //LocalMtime int `json:"local_mtime"` - Size int64 `json:"size"` - //ExtentTinyint7 int `json:"extent_tinyint7"` - Path string `json:"path"` - //Share int `json:"share"` - //ServerAtime int `json:"server_atime"` - //Pl int `json:"pl"` - //LocalCtime int `json:"local_ctime"` - ServerFilename string `json:"server_filename"` - //Md5 string `json:"md5"` - //OwnerId int `json:"owner_id"` - //Unlist int `json:"unlist"` - Isdir int `json:"isdir"` -} - -type ListResp struct { - Errno int `json:"errno"` - GuidInfo string `json:"guid_info"` - List []File `json:"list"` - RequestId int64 `json:"request_id"` - Guid int `json:"guid"` -} - -type DownloadResp struct { - Errmsg string `json:"errmsg"` - Errno int `json:"errno"` - List []struct { - //Category int `json:"category"` - //DateTaken int `json:"date_taken,omitempty"` - Dlink string `json:"dlink"` - //Filename string `json:"filename"` - //FsId int64 `json:"fs_id"` - //Height int `json:"height,omitempty"` - //Isdir int `json:"isdir"` - //Md5 string `json:"md5"` - //OperId int `json:"oper_id"` - //Path string `json:"path"` - //ServerCtime int `json:"server_ctime"` - //ServerMtime int `json:"server_mtime"` - //Size int `json:"size"` - //Thumbs struct { - // Icon string `json:"icon,omitempty"` - // Url1 string `json:"url1,omitempty"` - // Url2 string `json:"url2,omitempty"` - // Url3 string `json:"url3,omitempty"` - //} `json:"thumbs"` - //Width int `json:"width,omitempty"` - } `json:"list"` - //Names struct { - //} `json:"names"` - RequestId string `json:"request_id"` -} - -type DownloadResp2 struct { - Errno int `json:"errno"` - Info []struct { - //ExtentTinyint4 int `json:"extent_tinyint4"` - //ExtentTinyint1 int `json:"extent_tinyint1"` - //Bitmap string `json:"bitmap"` - //Category int `json:"category"` - //Isdir int `json:"isdir"` - //Videotag int `json:"videotag"` - Dlink string `json:"dlink"` - //OperID int64 `json:"oper_id"` - //PathMd5 int `json:"path_md5"` - //Wpfile int `json:"wpfile"` - //LocalMtime int `json:"local_mtime"` - /*Thumbs struct { - Icon string `json:"icon"` - URL3 string `json:"url3"` - URL2 string `json:"url2"` - URL1 string `json:"url1"` - } `json:"thumbs"`*/ - //PlaySource int `json:"play_source"` - //Share int `json:"share"` - //FileKey string `json:"file_key"` - //Errno int `json:"errno"` - //LocalCtime int `json:"local_ctime"` - //Rotate int `json:"rotate"` - //Metadata time.Time `json:"metadata"` - //Height int `json:"height"` - //SampleRate int `json:"sample_rate"` - //Width int `json:"width"` - //OwnerType int `json:"owner_type"` - //Privacy int `json:"privacy"` - //ExtentInt3 int64 `json:"extent_int3"` - //RealCategory string `json:"real_category"` - //SrcLocation string `json:"src_location"` - //MetaInfo string `json:"meta_info"` - //ID string `json:"id"` - //Duration int `json:"duration"` - //FileSize string `json:"file_size"` - //Channels int `json:"channels"` - //UseSegment int `json:"use_segment"` - //ServerCtime int `json:"server_ctime"` - //Resolution string `json:"resolution"` - //OwnerID int `json:"owner_id"` - //ExtraInfo string `json:"extra_info"` - //Size int `json:"size"` - //FsID int64 `json:"fs_id"` - //ExtentTinyint3 int `json:"extent_tinyint3"` - //Md5 string `json:"md5"` - //Path string `json:"path"` - //FrameRate int `json:"frame_rate"` - //ExtentTinyint2 int `json:"extent_tinyint2"` - //ServerFilename string `json:"server_filename"` - //ServerMtime int `json:"server_mtime"` - //TkbindID int `json:"tkbind_id"` - } `json:"info"` - RequestID int64 `json:"request_id"` -} - -type PrecreateResp struct { - Path string `json:"path"` - Uploadid string `json:"uploadid"` - ReturnType int `json:"return_type"` - BlockList []int `json:"block_list"` - Errno int `json:"errno"` - RequestId int64 `json:"request_id"` -} diff --git a/drivers/baidu/util.go b/drivers/baidu/util.go deleted file mode 100644 index bf41c7f1..00000000 --- a/drivers/baidu/util.go +++ /dev/null @@ -1,18 +0,0 @@ -package baidu - -import ( - "net/url" - "strings" - "time" -) - -func getTime(t int64) *time.Time { - tm := time.Unix(t, 0) - return &tm -} - -func encodeURIComponent(str string) string { - r := url.QueryEscape(str) - r = strings.ReplaceAll(r, "+", "%20") - return r -} diff --git a/drivers/baiduphoto/baidu.go b/drivers/baiduphoto/baidu.go deleted file mode 100644 index c192939b..00000000 --- a/drivers/baiduphoto/baidu.go +++ /dev/null @@ -1,259 +0,0 @@ -package baiduphoto - -import ( - "fmt" - "net/http" - - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" - log "github.com/sirupsen/logrus" -) - -func (driver Baidu) RefreshToken(account *model.Account) error { - err := driver.refreshToken(account) - if err != nil && err == base.ErrEmptyToken { - err = driver.refreshToken(account) - } - if err != nil { - account.Status = err.Error() - } - _ = model.SaveAccount(account) - return err -} - -func (driver Baidu) refreshToken(account *model.Account) error { - u := "https://openapi.baidu.com/oauth/2.0/token" - var resp base.TokenResp - var e TokenErrResp - _, err := base.RestyClient.R(). - SetResult(&resp). - SetError(&e). - SetQueryParams(map[string]string{ - "grant_type": "refresh_token", - "refresh_token": account.RefreshToken, - "client_id": account.ClientId, - "client_secret": account.ClientSecret, - }).Get(u) - if err != nil { - return err - } - if e.ErrorMsg != "" { - return &e - } - if resp.RefreshToken == "" { - return base.ErrEmptyToken - } - account.Status = "work" - account.AccessToken, account.RefreshToken = resp.AccessToken, resp.RefreshToken - return nil -} - -func (driver Baidu) Request(method string, url string, callback func(*resty.Request), account *model.Account) (*resty.Response, error) { - req := base.RestyClient.R() - req.SetQueryParam("access_token", account.AccessToken) - if callback != nil { - callback(req) - } - - res, err := req.Execute(method, url) - if err != nil { - return nil, err - } - log.Debug(res.String()) - - var erron Erron - if err = utils.Json.Unmarshal(res.Body(), &erron); err != nil { - return nil, err - } - - switch erron.Errno { - case 0: - return res, nil - case -6: - if err = driver.RefreshToken(account); err != nil { - return nil, err - } - default: - return nil, fmt.Errorf("errno: %d, refer to https://photo.baidu.com/union/doc", erron.Errno) - } - return driver.Request(method, url, callback, account) -} - -// 获取所有根文件 -func (driver Baidu) GetAllFile(account *model.Account) (files []File, err error) { - var cursor string - - for { - var resp FileListResp - _, err = driver.Request(http.MethodGet, FILE_API_URL_V1+"/list", func(r *resty.Request) { - r.SetQueryParams(map[string]string{ - "need_thumbnail": "1", - "need_filter_hidden": "0", - "cursor": cursor, - }) - r.SetResult(&resp) - }, account) - if err != nil { - return - } - - cursor = resp.Cursor - files = append(files, resp.List...) - - if !resp.HasNextPage() { - return - } - } -} - -// 获取所有相册 -func (driver Baidu) GetAllAlbum(account *model.Account) (albums []Album, err error) { - var cursor string - for { - var resp AlbumListResp - _, err = driver.Request(http.MethodGet, ALBUM_API_URL+"/list", func(r *resty.Request) { - r.SetQueryParams(map[string]string{ - "need_amount": "1", - "limit": "100", - "cursor": cursor, - }) - r.SetResult(&resp) - }, account) - if err != nil { - return - } - if albums == nil { - albums = make([]Album, 0, resp.TotalCount) - } - - cursor = resp.Cursor - albums = append(albums, resp.List...) - - if !resp.HasNextPage() { - return - } - } -} - -// 获取相册中所有文件 -func (driver Baidu) GetAllAlbumFile(albumID string, account *model.Account) (files []AlbumFile, err error) { - var cursor string - for { - var resp AlbumFileListResp - _, err = driver.Request(http.MethodGet, ALBUM_API_URL+"/listfile", func(r *resty.Request) { - r.SetQueryParams(map[string]string{ - "album_id": splitID(albumID)[0], - "need_amount": "1", - "limit": "1000", - "cursor": cursor, - }) - r.SetResult(&resp) - }, account) - if err != nil { - return - } - if files == nil { - files = make([]AlbumFile, 0, resp.TotalCount) - } - - cursor = resp.Cursor - files = append(files, resp.List...) - - if !resp.HasNextPage() { - return - } - } -} - -// 创建相册 -func (driver Baidu) CreateAlbum(name string, account *model.Account) error { - if !checkName(name) { - return ErrNotSupportName - } - _, err := driver.Request(http.MethodPost, ALBUM_API_URL+"/create", func(r *resty.Request) { - r.SetQueryParams(map[string]string{ - "title": name, - "tid": getTid(), - "source": "0", - }) - }, account) - return err -} - -// 相册改名 -func (driver Baidu) SetAlbumName(albumID string, name string, account *model.Account) error { - if !checkName(name) { - return ErrNotSupportName - } - - e := splitID(albumID) - _, err := driver.Request(http.MethodPost, ALBUM_API_URL+"/settitle", func(r *resty.Request) { - r.SetFormData(map[string]string{ - "title": name, - "album_id": e[0], - "tid": e[1], - }) - }, account) - return err -} - -// 删除相册 -func (driver Baidu) DeleteAlbum(albumID string, account *model.Account) error { - e := splitID(albumID) - _, err := driver.Request(http.MethodPost, ALBUM_API_URL+"/delete", func(r *resty.Request) { - r.SetFormData(map[string]string{ - "album_id": e[0], - "tid": e[1], - "delete_origin_image": "0", // 是否删除原图 0 不删除 - }) - }, account) - return err -} - -// 删除相册文件 -func (driver Baidu) DeleteAlbumFile(albumID string, account *model.Account, fileIDs ...string) error { - e := splitID(albumID) - _, err := driver.Request(http.MethodPost, ALBUM_API_URL+"/delfile", func(r *resty.Request) { - r.SetFormData(map[string]string{ - "album_id": e[0], - "tid": e[1], - "list": fsidsFormat(fileIDs...), - "del_origin": "0", // 是否删除原图 0 不删除 1 删除 - }) - }, account) - return err -} - -// 增加相册文件 -func (driver Baidu) AddAlbumFile(albumID string, account *model.Account, fileIDs ...string) error { - e := splitID(albumID) - _, err := driver.Request(http.MethodGet, ALBUM_API_URL+"/addfile", func(r *resty.Request) { - r.SetQueryParams(map[string]string{ - "album_id": e[0], - "tid": e[1], - "list": fsidsFormatNotUk(fileIDs...), - }) - }, account) - return err -} - -// 保存相册文件为根文件 -func (driver Baidu) CopyAlbumFile(albumID string, account *model.Account, fileID string) (*CopyFile, error) { - var resp CopyFileResp - e := splitID(fileID) - _, err := driver.Request(http.MethodPost, ALBUM_API_URL+"/copyfile", func(r *resty.Request) { - r.SetFormData(map[string]string{ - "album_id": splitID(albumID)[0], - "tid": e[2], - "uk": e[1], - "list": fsidsFormatNotUk(fileID), - }) - r.SetResult(&resp) - }, account) - if err != nil { - return nil, err - } - return &resp.List[0], err -} diff --git a/drivers/baiduphoto/driver.go b/drivers/baiduphoto/driver.go deleted file mode 100644 index 833ef89f..00000000 --- a/drivers/baiduphoto/driver.go +++ /dev/null @@ -1,502 +0,0 @@ -package baiduphoto - -import ( - "crypto/md5" - "encoding/hex" - "fmt" - "io" - "io/ioutil" - "math" - "net/http" - "os" - - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/go-resty/resty/v2" -) - -type Baidu struct{} - -func init() { - base.RegisterDriver(new(Baidu)) -} - -func (driver Baidu) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "Baidu.Photo", - LocalSort: true, - } -} - -func (driver Baidu) Items() []base.Item { - return []base.Item{ - { - Name: "refresh_token", - Label: "refresh token", - Type: base.TypeString, - Required: true, - }, - { - Name: "root_folder", - Label: "album_id", - Type: base.TypeString, - }, - { - Name: "internal_type", - Label: "download api", - Type: base.TypeSelect, - Required: true, - Values: "file,album", - Default: "album", - }, - { - Name: "client_id", - Label: "client id", - Default: "iYCeC9g08h5vuP9UqvPHKKSVrKFXGa1v", - Type: base.TypeString, - Required: true, - }, - { - Name: "client_secret", - Label: "client secret", - Default: "jXiFMOPVPCWlO2M5CwWQzffpNPaGTRBG", - Type: base.TypeString, - Required: true, - }, - } -} - -func (driver Baidu) Save(account *model.Account, old *model.Account) error { - if account == nil { - return nil - } - return driver.RefreshToken(account) -} - -func (driver Baidu) File(path string, account *model.Account) (*model.File, error) { - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - - dir, name := utils.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver Baidu) Files(path string, account *model.Account) ([]model.File, error) { - path = utils.ParsePath(path) - var files []model.File - cache, err := base.GetCache(path, account) - if err == nil { - files, _ = cache.([]model.File) - return files, nil - } - - file, err := driver.File(path, account) - if err != nil { - return nil, err - } - - if IsAlbum(file) { - albumFiles, err := driver.GetAllAlbumFile(file.Id, account) - if err != nil { - return nil, err - } - files = make([]model.File, 0, len(albumFiles)) - for _, file := range albumFiles { - var thumbnail string - if len(file.Thumburl) > 0 { - thumbnail = file.Thumburl[0] - } - files = append(files, model.File{ - Id: joinID(file.Fsid, file.Uk, file.Tid), - Name: file.Name(), - Size: file.Size, - Type: utils.GetFileType(utils.Ext(file.Path)), - Driver: driver.Config().Name, - UpdatedAt: getTime(file.Mtime), - Thumbnail: thumbnail, - }) - } - } else if IsRoot(file) { - albums, err := driver.GetAllAlbum(account) - if err != nil { - return nil, err - } - - files = make([]model.File, 0, len(albums)) - for _, album := range albums { - files = append(files, model.File{ - Id: joinID(album.AlbumID, album.Tid), - Name: album.Title, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: getTime(album.Mtime), - }) - } - } else { - return nil, base.ErrNotSupport - } - - if len(files) > 0 { - _ = base.SetCache(path, files, account) - } - return files, nil -} - -func (driver Baidu) Link(args base.Args, account *model.Account) (*base.Link, error) { - if account.InternalType == "file" { - return driver.LinkFile(args, account) - } - return driver.LinkAlbum(args, account) -} - -func (driver Baidu) LinkAlbum(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(args.Path, account) - if err != nil { - return nil, err - } - if !IsAlbumFile(file) { - return nil, base.ErrNotSupport - } - - album, err := driver.File(utils.Dir(utils.ParsePath(args.Path)), account) - if err != nil { - return nil, err - } - - e := splitID(file.Id) - res, err := base.NoRedirectClient.R(). - SetQueryParams(map[string]string{ - "access_token": account.AccessToken, - "album_id": splitID(album.Id)[0], - "tid": e[2], - "fsid": e[0], - "uk": e[1], - }). - Head(ALBUM_API_URL + "/download") - if err != nil { - return nil, err - } - return &base.Link{ - Headers: []base.Header{ - {Name: "User-Agent", Value: base.UserAgent}, - }, - Url: res.Header().Get("location"), - }, nil -} - -func (driver Baidu) LinkFile(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(args.Path, account) - if err != nil { - return nil, err - } - - if !IsAlbumFile(file) { - return nil, base.ErrNotSupport - } - - album, err := driver.File(utils.Dir(utils.ParsePath(args.Path)), account) - if err != nil { - return nil, err - } - // 拷贝到根目录 - cfile, err := driver.CopyAlbumFile(album.Id, account, file.Id) - if err != nil { - return nil, err - } - - res, err := driver.Request(http.MethodGet, FILE_API_URL_V2+"/download", func(r *resty.Request) { - r.SetQueryParams(map[string]string{ - "fsid": fmt.Sprint(cfile.Fsid), - }) - }, account) - if err != nil { - return nil, err - } - return &base.Link{ - Headers: []base.Header{ - {Name: "User-Agent", Value: base.UserAgent}, - }, - Url: utils.Json.Get(res.Body(), "dlink").ToString(), - }, nil -} - -func (driver Baidu) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -func (driver Baidu) Preview(path string, account *model.Account) (interface{}, error) { - return nil, base.ErrNotSupport -} - -func (driver Baidu) Rename(src string, dst string, account *model.Account) error { - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - - if IsAlbum(srcFile) { - return driver.SetAlbumName(srcFile.Id, utils.Base(dst), account) - } - return base.ErrNotSupport -} - -func (driver Baidu) MakeDir(path string, account *model.Account) error { - dir, name := utils.Split(path) - parentFile, err := driver.File(dir, account) - if err != nil { - return err - } - - if !IsRoot(parentFile) { - return base.ErrNotSupport - } - return driver.CreateAlbum(name, account) -} - -func (driver Baidu) Move(src string, dst string, account *model.Account) error { - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - - if IsAlbumFile(srcFile) { - // 移动相册文件 - dstAlbum, err := driver.File(utils.Dir(dst), account) - if err != nil { - return err - } - if !IsAlbum(dstAlbum) { - return base.ErrNotSupport - } - - srcAlbum, err := driver.File(utils.Dir(src), account) - if err != nil { - return err - } - - newFile, err := driver.CopyAlbumFile(srcAlbum.Id, account, srcFile.Id) - if err != nil { - return err - } - err = driver.DeleteAlbumFile(srcAlbum.Id, account, srcFile.Id) - if err != nil { - return err - } - err = driver.AddAlbumFile(dstAlbum.Id, account, joinID(newFile.Fsid)) - if err != nil { - return err - } - return nil - } - return base.ErrNotSupport -} - -func (driver Baidu) Copy(src string, dst string, account *model.Account) error { - srcFile, err := driver.File(src, account) - if err != nil { - return err - } - - if IsAlbumFile(srcFile) { - // 复制相册文件 - dstAlbum, err := driver.File(utils.Dir(dst), account) - if err != nil { - return err - } - if !IsAlbum(dstAlbum) { - return base.ErrNotSupport - } - - srcAlbum, err := driver.File(utils.Dir(src), account) - if err != nil { - return err - } - - newFile, err := driver.CopyAlbumFile(srcAlbum.Id, account, srcFile.Id) - if err != nil { - return err - } - err = driver.AddAlbumFile(dstAlbum.Id, account, joinID(newFile.Fsid)) - if err != nil { - return err - } - return nil - } - return base.ErrNotSupport -} - -func (driver Baidu) Delete(path string, account *model.Account) error { - file, err := driver.File(path, account) - if err != nil { - return err - } - - // 删除相册 - if IsAlbum(file) { - return driver.DeleteAlbum(file.Id, account) - } - - // 生成相册文件 - if IsAlbumFile(file) { - // 删除相册文件 - album, err := driver.File(utils.Dir(path), account) - if err != nil { - return err - } - return driver.DeleteAlbumFile(album.Id, account, file.Id) - } - return base.ErrNotSupport -} - -func (driver Baidu) Upload(file *model.FileStream, account *model.Account) error { - if file == nil { - return base.ErrEmptyFile - } - - parentFile, err := driver.File(file.ParentPath, account) - if err != nil { - return err - } - - if !IsAlbum(parentFile) { - return base.ErrNotSupport - } - - tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*") - if err != nil { - return err - } - defer func() { - tempFile.Close() - os.Remove(tempFile.Name()) - }() - - // 计算需要的数据 - const DEFAULT = 1 << 22 - const SliceSize = 1 << 18 - count := int(math.Ceil(float64(file.Size) / float64(DEFAULT))) - - sliceMD5List := make([]string, 0, count) - fileMd5 := md5.New() - sliceMd5 := md5.New() - for i := 1; i <= count; i++ { - if n, err := io.CopyN(io.MultiWriter(fileMd5, sliceMd5, tempFile), file, DEFAULT); err != io.EOF && n == 0 { - return err - } - sliceMD5List = append(sliceMD5List, hex.EncodeToString(sliceMd5.Sum(nil))) - sliceMd5.Reset() - } - - if _, err = tempFile.Seek(0, io.SeekStart); err != nil { - return err - } - - content_md5 := hex.EncodeToString(fileMd5.Sum(nil)) - slice_md5 := content_md5 - if file.GetSize() > SliceSize { - sliceData := make([]byte, SliceSize) - if _, err = io.ReadFull(tempFile, sliceData); err != nil { - return err - } - sliceMd5.Write(sliceData) - slice_md5 = hex.EncodeToString(sliceMd5.Sum(nil)) - if _, err = tempFile.Seek(0, io.SeekStart); err != nil { - return err - } - } - - // 开始执行上传 - params := map[string]string{ - "autoinit": "1", - "isdir": "0", - "rtype": "1", - "ctype": "11", - "path": utils.ParsePath(file.Name), - "size": fmt.Sprint(file.Size), - "slice-md5": slice_md5, - "content-md5": content_md5, - "block_list": MustString(utils.Json.MarshalToString(sliceMD5List)), - } - - // 预上传 - var precreateResp PrecreateResp - _, err = driver.Request(http.MethodPost, FILE_API_URL_V1+"/precreate", func(r *resty.Request) { - r.SetFormData(params) - r.SetResult(&precreateResp) - }, account) - if err != nil { - return err - } - - switch precreateResp.ReturnType { - case 1: // 上传文件 - uploadParams := map[string]string{ - "method": "upload", - "path": params["path"], - "uploadid": precreateResp.UploadID, - } - - for i := 0; i < count; i++ { - uploadParams["partseq"] = fmt.Sprint(i) - _, err = driver.Request(http.MethodPost, "https://c3.pcs.baidu.com/rest/2.0/pcs/superfile2", func(r *resty.Request) { - r.SetQueryParams(uploadParams) - r.SetFileReader("file", file.Name, io.LimitReader(tempFile, DEFAULT)) - }, account) - if err != nil { - return err - } - } - fallthrough - case 2: // 创建文件 - params["uploadid"] = precreateResp.UploadID - _, err = driver.Request(http.MethodPost, FILE_API_URL_V1+"/create", func(r *resty.Request) { - r.SetFormData(params) - r.SetResult(&precreateResp) - }, account) - if err != nil { - return err - } - fallthrough - case 3: // 增加到相册 - err = driver.AddAlbumFile(parentFile.Id, account, joinID(precreateResp.Data.FsID)) - if err != nil { - return err - } - } - return nil -} - -var _ base.Driver = (*Baidu)(nil) diff --git a/drivers/baiduphoto/types.go b/drivers/baiduphoto/types.go deleted file mode 100644 index 926329d6..00000000 --- a/drivers/baiduphoto/types.go +++ /dev/null @@ -1,126 +0,0 @@ -package baiduphoto - -import ( - "fmt" - - "github.com/Xhofe/alist/utils" -) - -type TokenErrResp struct { - ErrorDescription string `json:"error_description"` - ErrorMsg string `json:"error"` -} - -func (e *TokenErrResp) Error() string { - return fmt.Sprint(e.ErrorMsg, " : ", e.ErrorDescription) -} - -type Erron struct { - Errno int `json:"errno"` - RequestID int `json:"request_id"` -} - -type Page struct { - HasMore int `json:"has_more"` - Cursor string `json:"cursor"` -} - -func (p Page) HasNextPage() bool { - return p.HasMore == 1 -} - -type ( - FileListResp struct { - Page - List []File `json:"list"` - } - - File struct { - Fsid int64 `json:"fsid"` // 文件ID - Path string `json:"path"` // 文件路径 - Size int64 `json:"size"` - Ctime int64 `json:"ctime"` // 创建时间 s - Mtime int64 `json:"mtime"` // 修改时间 s - Thumburl []string `json:"thumburl"` - } -) - -func (f File) Name() string { - return utils.Base(f.Path) -} - -/*相册部分*/ -type ( - AlbumListResp struct { - Page - List []Album `json:"list"` - Reset int64 `json:"reset"` - TotalCount int64 `json:"total_count"` - } - - Album struct { - AlbumID string `json:"album_id"` - Tid int64 `json:"tid"` - Title string `json:"title"` - JoinTime int64 `json:"join_time"` - CreateTime int64 `json:"create_time"` - Mtime int64 `json:"mtime"` - } - - AlbumFileListResp struct { - Page - List []AlbumFile `json:"list"` - Reset int64 `json:"reset"` - TotalCount int64 `json:"total_count"` - } - - AlbumFile struct { - File - Tid int64 `json:"tid"` - Uk int64 `json:"uk"` - } -) - -type ( - CopyFileResp struct { - List []CopyFile `json:"list"` - } - CopyFile struct { - FromFsid int64 `json:"from_fsid"` // 源ID - Fsid int64 `json:"fsid"` // 目标ID - Path string `json:"path"` - ShootTime int `json:"shoot_time"` - } -) - -/*上传部分*/ -type ( - UploadFile struct { - FsID int64 `json:"fs_id"` - Size int64 `json:"size"` - Md5 string `json:"md5"` - ServerFilename string `json:"server_filename"` - Path string `json:"path"` - Ctime int `json:"ctime"` - Mtime int `json:"mtime"` - Isdir int `json:"isdir"` - Category int `json:"category"` - ServerMd5 string `json:"server_md5"` - ShootTime int `json:"shoot_time"` - } - - CreateFileResp struct { - Data UploadFile `json:"data"` - } - - PrecreateResp struct { - ReturnType int `json:"return_type"` //存在返回2 不存在返回1 已经保存3 - //存在返回 - CreateFileResp - - //不存在返回 - Path string `json:"path"` - UploadID string `json:"uploadid"` - Blocklist []int64 `json:"block_list"` - } -) diff --git a/drivers/baiduphoto/util.go b/drivers/baiduphoto/util.go deleted file mode 100644 index 2c0542c7..00000000 --- a/drivers/baiduphoto/util.go +++ /dev/null @@ -1,84 +0,0 @@ -package baiduphoto - -import ( - "errors" - "fmt" - "math" - "math/rand" - "regexp" - "strings" - "time" - - "github.com/Xhofe/alist/model" -) - -const ( - API_URL = "https://photo.baidu.com/youai" - ALBUM_API_URL = API_URL + "/album/v1" - FILE_API_URL_V1 = API_URL + "/file/v1" - FILE_API_URL_V2 = API_URL + "/file/v2" -) - -var ( - ErrNotSupportName = errors.New("only chinese and english, numbers and underscores are supported, and the length is no more than 20") -) - -//Tid生成 -func getTid() string { - return fmt.Sprintf("3%d%.0f", time.Now().Unix(), math.Floor(9000000*rand.Float64()+1000000)) -} - -// 检查名称 -func checkName(name string) bool { - return len(name) <= 20 && regexp.MustCompile("[\u4e00-\u9fa5A-Za-z0-9_]").MatchString(name) -} - -func getTime(t int64) *time.Time { - tm := time.Unix(t, 0) - return &tm -} - -func fsidsFormat(ids ...string) string { - var buf []string - for _, id := range ids { - e := strings.Split(id, "|") - buf = append(buf, fmt.Sprintf("{\"fsid\":%s,\"uk\":%s}", e[0], e[1])) - } - return fmt.Sprintf("[%s]", strings.Join(buf, ",")) -} - -func fsidsFormatNotUk(ids ...string) string { - var buf []string - for _, id := range ids { - buf = append(buf, fmt.Sprintf("{\"fsid\":%s}", strings.Split(id, "|")[0])) - } - return fmt.Sprintf("[%s]", strings.Join(buf, ",")) -} - -func splitID(id string) []string { - return strings.SplitN(id, "|", 3)[:3] -} - -func joinID(ids ...interface{}) string { - idsStr := make([]string, 0, len(ids)) - for _, id := range ids { - idsStr = append(idsStr, fmt.Sprint(id)) - } - return strings.Join(idsStr, "|") -} - -func IsAlbum(file *model.File) bool { - return file.Id != "" && file.IsDir() -} - -func IsAlbumFile(file *model.File) bool { - return file.Id != "" && !file.IsDir() -} - -func IsRoot(file *model.File) bool { - return file.Id == "" && file.IsDir() -} - -func MustString(str string, err error) string { - return str -} diff --git a/drivers/base/base.go b/drivers/base/base.go deleted file mode 100644 index 68027f41..00000000 --- a/drivers/base/base.go +++ /dev/null @@ -1,61 +0,0 @@ -package base - -import "github.com/Xhofe/alist/model" - -type Base struct{} - -func (b Base) Config() DriverConfig { - return DriverConfig{} -} - -func (b Base) Items() []Item { - return nil -} - -func (b Base) Save(account *model.Account, old *model.Account) error { - return ErrNotImplement -} - -func (b Base) File(path string, account *model.Account) (*model.File, error) { - return nil, ErrNotImplement -} - -func (b Base) Files(path string, account *model.Account) ([]model.File, error) { - return nil, ErrNotImplement -} - -func (b Base) Link(args Args, account *model.Account) (*Link, error) { - return nil, ErrNotImplement -} - -func (b Base) Path(path string, account *model.Account) (*model.File, []model.File, error) { - return nil, nil, ErrNotImplement -} - -func (b Base) Preview(path string, account *model.Account) (interface{}, error) { - return nil, ErrNotImplement -} - -func (b Base) MakeDir(path string, account *model.Account) error { - return ErrNotImplement -} - -func (b Base) Move(src string, dst string, account *model.Account) error { - return ErrNotImplement -} - -func (b Base) Rename(src string, dst string, account *model.Account) error { - return ErrNotImplement -} - -func (b Base) Copy(src string, dst string, account *model.Account) error { - return ErrNotImplement -} - -func (b Base) Delete(path string, account *model.Account) error { - return ErrNotImplement -} - -func (b Base) Upload(file *model.FileStream, account *model.Account) error { - return ErrNotImplement -} diff --git a/drivers/base/cache.go b/drivers/base/cache.go deleted file mode 100644 index b0d34189..00000000 --- a/drivers/base/cache.go +++ /dev/null @@ -1,58 +0,0 @@ -package base - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - log "github.com/sirupsen/logrus" - "strings" -) - -func KeyCache(path string, account *model.Account) string { - //path = utils.ParsePath(path) - key := utils.ParsePath(utils.Join(account.Name, path)) - log.Debugln("cache key: ", key) - return key -} - -func SaveSearchFiles[T model.ISearchFile](key string, obj []T) { - if strings.Contains(key, ".balance") { - return - } - err := model.DeleteSearchFilesByPath(key) - if err != nil { - log.Errorln("failed create search files", err) - return - } - files := make([]model.SearchFile, len(obj)) - for i := 0; i < len(obj); i++ { - files[i] = model.SearchFile{ - Path: key, - Name: obj[i].GetName(), - Size: obj[i].GetSize(), - Type: obj[i].GetType(), - } - } - err = model.CreateSearchFiles(files) - if err != nil { - log.Errorln("failed create search files", err) - } -} - -func SetCache[T model.ISearchFile](path string, obj []T, account *model.Account) error { - key := KeyCache(path, account) - if conf.GetBool("enable search") { - go SaveSearchFiles(key, obj) - } - return conf.Cache.Set(conf.Ctx, key, obj, nil) -} - -func GetCache(path string, account *model.Account) (interface{}, error) { - return conf.Cache.Get(conf.Ctx, KeyCache(path, account)) -} - -func DeleteCache(path string, account *model.Account) error { - err := conf.Cache.Delete(conf.Ctx, KeyCache(path, account)) - log.Debugf("delete cache %s: %+v", path, err) - return err -} diff --git a/drivers/base/driver.go b/drivers/base/driver.go deleted file mode 100644 index a4d6fb05..00000000 --- a/drivers/base/driver.go +++ /dev/null @@ -1,181 +0,0 @@ -package base - -import ( - "github.com/Xhofe/alist/model" - "github.com/go-resty/resty/v2" - log "github.com/sirupsen/logrus" - "net/http" - "time" -) - -type DriverConfig struct { - Name string - OnlyProxy bool // 必须使用代理(本机或者其他机器) - OnlyLocal bool // 必须本机返回的 - ApiProxy bool // 使用API中转的 - NoNeedSetLink bool // 不需要设置链接的 - NoCors bool // 不可以跨域 - LocalSort bool // 本地排序 -} - -type Args struct { - Path string - IP string - Header http.Header -} - -type Driver interface { - // Config 配置 - Config() DriverConfig - // Items 账号所需参数 - Items() []Item - // Save 保存时处理 - Save(account *model.Account, old *model.Account) error - // File 取文件 - File(path string, account *model.Account) (*model.File, error) - // Files 取文件夹 - Files(path string, account *model.Account) ([]model.File, error) - // Link 取链接 - Link(args Args, account *model.Account) (*Link, error) - // Path 取路径(文件或文件夹) - Path(path string, account *model.Account) (*model.File, []model.File, error) - // Deprecated Proxy 代理处理 - //Proxy(r *http.Request, account *model.Account) - // Preview 预览 - Preview(path string, account *model.Account) (interface{}, error) - // MakeDir 创建文件夹 - MakeDir(path string, account *model.Account) error - // Move 移动/改名 - Move(src string, dst string, account *model.Account) error - // Rename 改名 - Rename(src string, dst string, account *model.Account) error - // Copy 拷贝 - Copy(src string, dst string, account *model.Account) error - // Delete 删除 - Delete(path string, account *model.Account) error - // Upload 上传 - Upload(file *model.FileStream, account *model.Account) error - // TODO - //Search(path string, keyword string, account *model.Account) ([]*model.File, error) -} - -type Item struct { - Name string `json:"name"` - Label string `json:"label"` - Type string `json:"type"` - Default string `json:"default"` - Values string `json:"values"` - Required bool `json:"required"` - Description string `json:"description"` -} - -var driversMap = map[string]Driver{} - -func RegisterDriver(driver Driver) { - log.Infof("register driver: [%s]", driver.Config().Name) - driversMap[driver.Config().Name] = driver -} - -func GetDriver(name string) (driver Driver, ok bool) { - driver, ok = driversMap[name] - return -} - -func GetDriversMap() map[string]Driver { - return driversMap -} - -func GetDrivers() map[string][]Item { - res := make(map[string][]Item) - for k, v := range driversMap { - webdavDirect := Item{ - Name: "webdav_direct", - Label: "webdav direct", - Type: TypeBool, - Required: true, - Description: "Transfer the WebDAV of this account through the native", - } - if v.Config().OnlyProxy { - res[k] = append([]Item{ - webdavDirect, - }, v.Items()...) - } else { - res[k] = append([]Item{ - { - Name: "proxy", - Label: "proxy", - Type: TypeBool, - Required: true, - Description: "web proxy", - }, - { - Name: "webdav_proxy", - Label: "webdav proxy", - Type: TypeBool, - Required: true, - Description: "Transfer the WebDAV of this account through the server", - }, - webdavDirect, - }, v.Items()...) - } - res[k] = append([]Item{ - { - Name: "down_proxy_url", - Label: "down_proxy_url", - Type: TypeText, - }, - { - Name: "extract_folder", - Label: "extract_folder", - Values: "front,back", - Type: TypeSelect, - }, - }, res[k]...) - if v.Config().ApiProxy { - res[k] = append([]Item{ - { - Name: "api_proxy_url", - Label: "api_proxy_url", - Type: TypeString, - }, - }, res[k]...) - } - if v.Config().LocalSort { - res[k] = append(res[k], []Item{ - { - Name: "order_by", - Label: "order_by", - Type: TypeSelect, - Values: "name,size,updated_at", - Required: false, - }, - { - Name: "order_direction", - Label: "order_direction", - Type: TypeSelect, - Values: "ASC,DESC", - Required: false, - }, - }...) - } - } - return res -} - -var NoRedirectClient *resty.Client -var RestyClient = resty.New() -var HttpClient = &http.Client{} -var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" -var DefaultTimeout = time.Second * 20 - -func init() { - NoRedirectClient = resty.New().SetRedirectPolicy( - resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }), - ) - NoRedirectClient.SetHeader("user-agent", UserAgent) - RestyClient.SetHeader("user-agent", UserAgent) - RestyClient.SetRetryCount(3) - RestyClient.SetTimeout(DefaultTimeout) -} diff --git a/drivers/base/types.go b/drivers/base/types.go deleted file mode 100644 index 81539ad1..00000000 --- a/drivers/base/types.go +++ /dev/null @@ -1,55 +0,0 @@ -package base - -import ( - "errors" - "io" - "net/http" -) - -var ( - ErrPathNotFound = errors.New("path not found") - ErrNotFile = errors.New("not file") - ErrNotImplement = errors.New("not implement") - ErrNotSupport = errors.New("not support") - ErrNotFolder = errors.New("not a folder") - ErrEmptyFile = errors.New("empty file") - ErrRelativePath = errors.New("access using relative path is not allowed") - ErrEmptyToken = errors.New("empty token") -) - -const ( - TypeString = "string" - TypeSelect = "select" - TypeBool = "bool" - TypeNumber = "number" - TypeText = "text" -) - -const ( - Get = iota - Post - Put - Delete - Patch -) - -type Json map[string]interface{} - -type TokenResp struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` -} - -type Header struct { - Name string `json:"name"` - Value string `json:"value"` -} - -type Link struct { - Url string `json:"url"` - Headers []Header `json:"headers"` - Data io.ReadCloser - FilePath string `json:"path"` // for native - Status int - Header http.Header -} diff --git a/drivers/ftp/driver.go b/drivers/ftp/driver.go deleted file mode 100644 index 53f1a4e7..00000000 --- a/drivers/ftp/driver.go +++ /dev/null @@ -1,262 +0,0 @@ -package ftp - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/jlaffaye/ftp" - log "github.com/sirupsen/logrus" - "path/filepath" -) - -type FTP struct{} - -func (driver FTP) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "FTP", - OnlyProxy: true, - OnlyLocal: true, - NoNeedSetLink: true, - LocalSort: true, - } -} - -func (driver FTP) Items() []base.Item { - return []base.Item{ - { - Name: "site_url", - Label: "ftp host url", - Type: base.TypeString, - Required: true, - }, - { - Name: "username", - Label: "username", - Type: base.TypeString, - Required: true, - }, - { - Name: "password", - Label: "password", - Type: base.TypeString, - Required: true, - }, - { - Name: "root_folder", - Label: "root folder path", - Type: base.TypeString, - Required: false, - }, - } -} - -func (driver FTP) Save(account *model.Account, old *model.Account) error { - if old != nil { - conn, ok := connMap[old.Name] - if ok { - err := conn.Quit() - log.Error("ftp:", err) - delete(connMap, old.Name) - } - } - if account == nil { - return nil - } - if account.RootFolder == "" { - account.RootFolder = "/" - } - _, err := driver.Login(account) - if err != nil { - account.Status = err.Error() - } else { - account.Status = "work" - } - _ = model.SaveAccount(account) - return err -} - -func (driver FTP) File(path string, account *model.Account) (*model.File, error) { - log.Debugf("file: %s", path) - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - dir, name := filepath.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver FTP) Files(path string, account *model.Account) ([]model.File, error) { - log.Debugf("files: %s", path) - path = utils.ParsePath(path) - cache, err := base.GetCache(path, account) - if err == nil { - files, _ := cache.([]model.File) - return files, nil - } - realPath := utils.Join(account.RootFolder, path) - conn, err := driver.Login(account) - if err != nil { - return nil, err - } - //defer func() { _ = conn.Quit() }() - entries, err := conn.List(realPath) - if err != nil { - return nil, err - } - res := make([]model.File, 0) - for i, _ := range entries { - entry := entries[i] - if entry.Name == "." || entry.Name == ".." { - continue - } - f := model.File{ - Name: entry.Name, - Size: int64(entry.Size), - UpdatedAt: &entry.Time, - Driver: driver.Config().Name, - } - if entry.Type == ftp.EntryTypeFolder { - f.Type = conf.FOLDER - } else { - f.Type = utils.GetFileType(filepath.Ext(entry.Name)) - } - res = append(res, f) - } - if len(res) > 0 { - _ = base.SetCache(path, res, account) - } - return res, nil -} - -func (driver FTP) Link(args base.Args, account *model.Account) (*base.Link, error) { - path := args.Path - path = utils.ParsePath(path) - realPath := utils.Join(account.RootFolder, path) - conn, err := driver.Login(account) - if err != nil { - return nil, err - } - //defer func() { _ = conn.Quit() }() - resp, err := conn.Retr(realPath) - if err != nil { - return nil, err - } - //defer func() { _ = resp.Close() }() - //data, err := ioutil.ReadAll(resp) - //if err != nil { - // return nil, err - //} - return &base.Link{ - Data: resp, - }, nil -} - -func (driver FTP) Path(path string, account *model.Account) (*model.File, []model.File, error) { - log.Debugf("ftp path: %s", path) - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - //file.Url, _ = driver.Link(path, account) - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -//func (driver FTP) Proxy(r *http.Request, account *model.Account) { -// -//} - -func (driver FTP) Preview(path string, account *model.Account) (interface{}, error) { - return nil, base.ErrNotSupport -} - -func (driver FTP) MakeDir(path string, account *model.Account) error { - path = utils.ParsePath(path) - realPath := utils.Join(account.RootFolder, path) - conn, err := driver.Login(account) - if err != nil { - return err - } - //defer func() { _ = conn.Quit() }() - err = conn.MakeDir(realPath) - return err -} - -func (driver FTP) Move(src string, dst string, account *model.Account) error { - realSrc := utils.Join(account.RootFolder, src) - realDst := utils.Join(account.RootFolder, dst) - conn, err := driver.Login(account) - if err != nil { - return err - } - //defer func() { _ = conn.Quit() }() - err = conn.Rename(realSrc, realDst) - return err -} - -func (driver FTP) Rename(src string, dst string, account *model.Account) error { - return driver.Move(src, dst, account) -} - -func (driver FTP) Copy(src string, dst string, account *model.Account) error { - return base.ErrNotSupport -} - -func (driver FTP) Delete(path string, account *model.Account) error { - path = utils.ParsePath(path) - file, err := driver.File(path, account) - if err != nil { - return err - } - realPath := utils.Join(account.RootFolder, path) - conn, err := driver.Login(account) - if err != nil { - return err - } - //defer func() { _ = conn.Quit() }() - if file.IsDir() { - err = conn.RemoveDirRecur(realPath) - } else { - err = conn.Delete(realPath) - } - return err -} - -func (driver FTP) Upload(file *model.FileStream, account *model.Account) error { - if file == nil { - return base.ErrEmptyFile - } - realPath := utils.Join(account.RootFolder, file.ParentPath, file.Name) - conn, err := driver.Login(account) - if err != nil { - return err - } - //defer func() { _ = conn.Quit() }() - err = conn.Stor(realPath, file) - return err -} - -var _ base.Driver = (*FTP)(nil) diff --git a/drivers/ftp/ftp.go b/drivers/ftp/ftp.go deleted file mode 100644 index e3f7353b..00000000 --- a/drivers/ftp/ftp.go +++ /dev/null @@ -1,36 +0,0 @@ -package ftp - -import ( - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/jlaffaye/ftp" -) - -var connMap map[string]*ftp.ServerConn - -func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) { - conn, ok := connMap[account.Name] - if ok { - _, err := conn.CurrentDir() - if err == nil { - return conn, nil - } else { - delete(connMap, account.Name) - } - } - conn, err := ftp.Connect(account.SiteUrl) - if err != nil { - return nil, err - } - err = conn.Login(account.Username, account.Password) - if err != nil { - return nil, err - } - connMap[account.Name] = conn - return conn, nil -} - -func init() { - base.RegisterDriver(&FTP{}) - connMap = make(map[string]*ftp.ServerConn) -} diff --git a/drivers/google/driver.go b/drivers/google/driver.go deleted file mode 100644 index bb99fc76..00000000 --- a/drivers/google/driver.go +++ /dev/null @@ -1,289 +0,0 @@ -package google - -import ( - "fmt" - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - log "github.com/sirupsen/logrus" - "io/ioutil" - "path/filepath" -) - -type GoogleDrive struct{} - -func (driver GoogleDrive) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "GoogleDrive", - OnlyProxy: true, - ApiProxy: true, - NoNeedSetLink: true, - } -} - -func (driver GoogleDrive) Items() []base.Item { - return []base.Item{ - { - Name: "client_id", - Label: "client id", - Type: base.TypeString, - Required: true, - Default: "202264815644.apps.googleusercontent.com", - }, - { - Name: "client_secret", - Label: "client secret", - Type: base.TypeString, - Required: true, - Default: "X4Z3ca8xfWDb1Voo-F9a7ZxJ", - }, - { - Name: "refresh_token", - Label: "refresh token", - Type: base.TypeString, - Required: true, - }, - { - Name: "root_folder", - Label: "root folder file_id", - Type: base.TypeString, - Required: false, - }, - { - Name: "order_by", - Label: "order_by", - Type: base.TypeString, - Required: false, - Description: "such as: folder,name,modifiedTime", - }, - { - Name: "order_direction", - Label: "order_direction", - Type: base.TypeSelect, - Values: "asc,desc", - Required: false, - }, - } -} - -func (driver GoogleDrive) Save(account *model.Account, old *model.Account) error { - if account == nil { - return nil - } - account.Proxy = true - err := driver.RefreshToken(account) - if err != nil { - account.Status = err.Error() - _ = model.SaveAccount(account) - return err - } - if account.RootFolder == "" { - account.RootFolder = "root" - } - account.Status = "work" - _ = model.SaveAccount(account) - return nil -} - -func (driver GoogleDrive) File(path string, account *model.Account) (*model.File, error) { - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - dir, name := filepath.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver GoogleDrive) Files(path string, account *model.Account) ([]model.File, error) { - path = utils.ParsePath(path) - var rawFiles []File - cache, err := base.GetCache(path, account) - if err == nil { - rawFiles, _ = cache.([]File) - } else { - file, err := driver.File(path, account) - if err != nil { - return nil, err - } - rawFiles, err = driver.GetFiles(file.Id, account) - if err != nil { - return nil, err - } - if len(rawFiles) > 0 { - _ = base.SetCache(path, rawFiles, account) - } - } - files := make([]model.File, 0) - for _, file := range rawFiles { - files = append(files, *driver.FormatFile(&file, account)) - } - return files, nil -} - -func (driver GoogleDrive) Link(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(args.Path, account) - if err != nil { - return nil, err - } - if file.Type == conf.FOLDER { - return nil, base.ErrNotFile - } - url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.Id) - _, err = driver.Request(url, base.Get, nil, nil, nil, nil, nil, account) - if err != nil { - return nil, err - } - link := base.Link{ - Url: url + "&alt=media", - Headers: []base.Header{ - { - Name: "Authorization", - Value: "Bearer " + account.AccessToken, - }, - }, - } - return &link, nil -} - -func (driver GoogleDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - log.Debugf("google path: %s", path) - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -//func (driver GoogleDrive) Proxy(r *http.Request, account *model.Account) { -// r.Header.Add("Authorization", "Bearer "+account.AccessToken) -//} - -func (driver GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) { - return nil, base.ErrNotSupport -} - -func (driver GoogleDrive) MakeDir(path string, account *model.Account) error { - parentFile, err := driver.File(utils.Dir(path), account) - if err != nil { - return err - } - data := base.Json{ - "name": utils.Base(path), - "parents": []string{parentFile.Id}, - "mimeType": "application/vnd.google-apps.folder", - } - _, err = driver.Request("https://www.googleapis.com/drive/v3/files", base.Post, nil, nil, nil, &data, nil, account) - return err -} - -func (driver GoogleDrive) Move(src string, dst string, account *model.Account) error { - srcFile, err := driver.File(src, account) - url := "https://www.googleapis.com/drive/v3/files/" + srcFile.Id - if err != nil { - return err - } - dstParentFile, err := driver.File(utils.Dir(dst), account) - if err != nil { - return err - } - query := map[string]string{ - "addParents": dstParentFile.Id, - "removeParents": "root", - } - _, err = driver.Request(url, base.Patch, nil, query, nil, nil, nil, account) - return err -} - -func (driver GoogleDrive) Rename(src string, dst string, account *model.Account) error { - srcFile, err := driver.File(src, account) - url := "https://www.googleapis.com/drive/v3/files/" + srcFile.Id - if err != nil { - return err - } - data := base.Json{ - "name": utils.Base(dst), - } - _, err = driver.Request(url, base.Patch, nil, nil, nil, &data, nil, account) - return err -} - -func (driver GoogleDrive) Copy(src string, dst string, account *model.Account) error { - return base.ErrNotSupport -} - -func (driver GoogleDrive) Delete(path string, account *model.Account) error { - file, err := driver.File(path, account) - url := "https://www.googleapis.com/drive/v3/files/" + file.Id - if err != nil { - return err - } - _, err = driver.Request(url, base.Delete, nil, nil, nil, nil, nil, account) - return err -} - -func (driver GoogleDrive) Upload(file *model.FileStream, account *model.Account) error { - if file == nil { - return base.ErrEmptyFile - } - parentFile, err := driver.File(file.ParentPath, account) - if err != nil { - return err - } - data := base.Json{ - "name": file.Name, - "parents": []string{parentFile.Id}, - } - var e Error - url := "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true" - if account.APIProxyUrl != "" { - url = fmt.Sprintf("%s/%s", account.APIProxyUrl, url) - } - res, err := base.NoRedirectClient.R().SetHeader("Authorization", "Bearer "+account.AccessToken). - SetError(&e).SetBody(data). - Post(url) - if err != nil { - return err - } - if e.Error.Code != 0 { - if e.Error.Code == 401 { - err = driver.RefreshToken(account) - if err != nil { - _ = model.SaveAccount(account) - return err - } - return driver.Upload(file, account) - } - return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors) - } - putUrl := res.Header().Get("location") - byteData, _ := ioutil.ReadAll(file) - _, err = driver.Request(putUrl, base.Put, nil, nil, nil, byteData, nil, account) - return err -} - -var _ base.Driver = (*GoogleDrive)(nil) diff --git a/drivers/google/googledrive.go b/drivers/google/googledrive.go deleted file mode 100644 index c3c47b49..00000000 --- a/drivers/google/googledrive.go +++ /dev/null @@ -1,175 +0,0 @@ -package google - -import ( - "fmt" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/go-resty/resty/v2" - log "github.com/sirupsen/logrus" -) - -type TokenError struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description"` -} - -func (driver GoogleDrive) RefreshToken(account *model.Account) error { - url := "https://www.googleapis.com/oauth2/v4/token" - if account.APIProxyUrl != "" { - url = fmt.Sprintf("%s/%s", account.APIProxyUrl, url) - } - var resp base.TokenResp - var e TokenError - res, err := base.RestyClient.R().SetResult(&resp).SetError(&e). - SetFormData(map[string]string{ - "client_id": account.ClientId, - "client_secret": account.ClientSecret, - "refresh_token": account.RefreshToken, - "grant_type": "refresh_token", - }).Post(url) - if err != nil { - return err - } - log.Debug(res.String()) - if e.Error != "" { - return fmt.Errorf(e.Error) - } - account.AccessToken = resp.AccessToken - account.Status = "work" - return nil -} - -func (driver GoogleDrive) FormatFile(file *File, account *model.Account) *model.File { - f := &model.File{ - Id: file.Id, - Name: file.Name, - Driver: driver.Config().Name, - UpdatedAt: file.ModifiedTime, - Url: "", - } - f.Size = int64(file.GetSize()) - f.Type = file.GetType() - if file.ThumbnailLink != "" { - if account.APIProxyUrl != "" { - f.Thumbnail = fmt.Sprintf("%s/%s", account.APIProxyUrl, file.ThumbnailLink) - } else { - f.Thumbnail = file.ThumbnailLink - } - } - return f -} - -type Files struct { - NextPageToken string `json:"nextPageToken"` - Files []File `json:"files"` -} - -type Error struct { - Error struct { - Errors []struct { - Domain string `json:"domain"` - Reason string `json:"reason"` - Message string `json:"message"` - LocationType string `json:"location_type"` - Location string `json:"location"` - } - Code int `json:"code"` - Message string `json:"message"` - } `json:"error"` -} - -func (driver GoogleDrive) GetFiles(id string, account *model.Account) ([]File, error) { - pageToken := "first" - res := make([]File, 0) - for pageToken != "" { - if pageToken == "first" { - pageToken = "" - } - var resp Files - orderBy := "folder,name,modifiedTime desc" - if account.OrderBy != "" { - orderBy = account.OrderBy + " " + account.OrderDirection - } - query := map[string]string{ - "orderBy": orderBy, - "fields": "files(id,name,mimeType,size,modifiedTime,thumbnailLink),nextPageToken", - "pageSize": "1000", - "q": fmt.Sprintf("'%s' in parents and trashed = false", id), - //"includeItemsFromAllDrives": "true", - //"supportsAllDrives": "true", - "pageToken": pageToken, - } - _, err := driver.Request("https://www.googleapis.com/drive/v3/files", - base.Get, nil, query, nil, nil, &resp, account) - if err != nil { - return nil, err - } - pageToken = resp.NextPageToken - res = append(res, resp.Files...) - } - return res, nil -} - -func (driver GoogleDrive) Request(url string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) { - rawUrl := url - if account.APIProxyUrl != "" { - url = fmt.Sprintf("%s/%s", account.APIProxyUrl, url) - } - req := base.RestyClient.R() - req.SetHeader("Authorization", "Bearer "+account.AccessToken) - req.SetQueryParam("includeItemsFromAllDrives", "true") - req.SetQueryParam("supportsAllDrives", "true") - if headers != nil { - req.SetHeaders(headers) - } - if query != nil { - req.SetQueryParams(query) - } - if form != nil { - req.SetFormData(form) - } - if data != nil { - req.SetBody(data) - } - if resp != nil { - req.SetResult(resp) - } - var res *resty.Response - var err error - var e Error - req.SetError(&e) - switch method { - case base.Get: - res, err = req.Get(url) - case base.Post: - res, err = req.Post(url) - case base.Delete: - res, err = req.Delete(url) - case base.Patch: - res, err = req.Patch(url) - case base.Put: - res, err = req.Put(url) - default: - return nil, base.ErrNotSupport - } - if err != nil { - return nil, err - } - log.Debug(res.String()) - if e.Error.Code != 0 { - if e.Error.Code == 401 { - err = driver.RefreshToken(account) - if err != nil { - _ = model.SaveAccount(account) - return nil, err - } - return driver.Request(rawUrl, method, headers, query, form, data, resp, account) - } - return nil, fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors) - } - return res.Body(), nil -} - -func init() { - base.RegisterDriver(&GoogleDrive{}) -} diff --git a/drivers/google/types.go b/drivers/google/types.go deleted file mode 100644 index 67752a2b..00000000 --- a/drivers/google/types.go +++ /dev/null @@ -1,38 +0,0 @@ -package google - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/utils" - "path" - "strconv" - "time" -) - -type File struct { - Id string `json:"id"` - Name string `json:"name"` - MimeType string `json:"mimeType"` - ModifiedTime *time.Time `json:"modifiedTime"` - Size string `json:"size"` - ThumbnailLink string `json:"thumbnailLink"` -} - -func (f File) GetSize() uint64 { - if f.GetType() == conf.FOLDER { - return 0 - } - size, _ := strconv.ParseUint(f.Size, 10, 64) - return size -} - -func (f File) GetName() string { - return f.Name -} - -func (f File) GetType() int { - mimeType := f.MimeType - if mimeType == "application/vnd.google-apps.folder" || mimeType == "application/vnd.google-apps.shortcut" { - return conf.FOLDER - } - return utils.GetFileType(path.Ext(f.Name)) -} diff --git a/drivers/lanzou/driver.go b/drivers/lanzou/driver.go deleted file mode 100644 index acba5c70..00000000 --- a/drivers/lanzou/driver.go +++ /dev/null @@ -1,202 +0,0 @@ -package lanzou - -import ( - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - log "github.com/sirupsen/logrus" - "path/filepath" -) - -type Lanzou struct{} - -func (driver Lanzou) Config() base.DriverConfig { - return base.DriverConfig{ - Name: "Lanzou", - NoCors: true, - } -} - -func (driver Lanzou) Items() []base.Item { - return []base.Item{ - { - Name: "internal_type", - Label: "lanzou type", - Type: base.TypeSelect, - Required: true, - Values: "cookie,url", - }, - { - Name: "access_token", - Label: "cookie", - Type: base.TypeString, - Description: "about 15 days valid", - Required: true, - }, - { - Name: "site_url", - Label: "share url", - Type: base.TypeString, - Required: true, - }, - { - Name: "root_folder", - Label: "root folder file_id", - Type: base.TypeString, - }, - { - Name: "password", - Label: "share password", - Type: base.TypeString, - }, - } -} - -func (driver Lanzou) Save(account *model.Account, old *model.Account) error { - if account == nil { - return nil - } - if account.InternalType == "cookie" { - if account.RootFolder == "" { - account.RootFolder = "-1" - } - } - account.Status = "work" - _ = model.SaveAccount(account) - return nil -} - -func (driver Lanzou) File(path string, account *model.Account) (*model.File, error) { - path = utils.ParsePath(path) - if path == "/" { - return &model.File{ - Id: account.RootFolder, - Name: account.Name, - Size: 0, - Type: conf.FOLDER, - Driver: driver.Config().Name, - UpdatedAt: account.UpdatedAt, - }, nil - } - dir, name := filepath.Split(path) - files, err := driver.Files(dir, account) - if err != nil { - return nil, err - } - for _, file := range files { - if file.Name == name { - return &file, nil - } - } - return nil, base.ErrPathNotFound -} - -func (driver Lanzou) Files(path string, account *model.Account) ([]model.File, error) { - path = utils.ParsePath(path) - var rawFiles []LanZouFile - cache, err := base.GetCache(path, account) - if err == nil { - rawFiles, _ = cache.([]LanZouFile) - } else { - file, err := driver.File(path, account) - if err != nil { - return nil, err - } - rawFiles, err = driver.GetFiles(file.Id, account) - if err != nil { - return nil, err - } - if len(rawFiles) > 0 { - _ = base.SetCache(path, rawFiles, account) - } - } - files := make([]model.File, 0) - for _, file := range rawFiles { - files = append(files, *driver.FormatFile(&file)) - } - return files, nil -} - -func (driver Lanzou) Link(args base.Args, account *model.Account) (*base.Link, error) { - file, err := driver.File(args.Path, account) - if err != nil { - return nil, err - } - log.Debugf("down file: %+v", file) - downId := file.Id - pwd := "" - if account.InternalType == "cookie" { - downId, pwd, err = driver.GetDownPageId(file.Id, account) - if err != nil { - return nil, err - } - } - var url string - //if pwd != "" { - //url, err = driver.GetLinkWithPassword(downId, pwd, account) - //} else { - url, err = driver.GetLink(downId, pwd, account) - //} - if err != nil { - return nil, err - } - link := base.Link{ - Url: url, - Headers: []base.Header{ - {Name: "User-Agent", Value: base.UserAgent}, - }, - } - return &link, nil -} - -func (driver Lanzou) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - log.Debugf("lanzou path: %s", path) - file, err := driver.File(path, account) - if err != nil { - return nil, nil, err - } - if !file.IsDir() { - return file, nil, nil - } - files, err := driver.Files(path, account) - if err != nil { - return nil, nil, err - } - return nil, files, nil -} - -//func (driver Lanzou) Proxy(r *http.Request, account *model.Account) { -// r.Header.Del("Origin") -//} - -func (driver Lanzou) Preview(path string, account *model.Account) (interface{}, error) { - return nil, base.ErrNotSupport -} - -func (driver *Lanzou) MakeDir(path string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver *Lanzou) Move(src string, dst string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver *Lanzou) Rename(src string, dst string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver *Lanzou) Copy(src string, dst string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver *Lanzou) Delete(path string, account *model.Account) error { - return base.ErrNotImplement -} - -func (driver *Lanzou) Upload(file *model.FileStream, account *model.Account) error { - return base.ErrNotImplement -} - -var _ base.Driver = (*Lanzou)(nil) diff --git a/drivers/lanzou/lanzou.go b/drivers/lanzou/lanzou.go deleted file mode 100644 index 9e43b676..00000000 --- a/drivers/lanzou/lanzou.go +++ /dev/null @@ -1,271 +0,0 @@ -package lanzou - -import ( - "fmt" - "github.com/Xhofe/alist/drivers/base" - "github.com/Xhofe/alist/model" - log "github.com/sirupsen/logrus" - "net/url" - "regexp" - "strconv" - "time" -) - -func (driver *Lanzou) FormatFile(file *LanZouFile) *model.File { - now := time.Now() - f := &model.File{ - Id: file.Id, - Name: file.Name, - Driver: driver.Config().Name, - SizeStr: file.Size, - TimeStr: file.Time, - UpdatedAt: &now, - } - if file.Folder { - f.Id = file.FolId - } else { - f.Name = file.NameAll - } - f.Type = file.GetType() - return f -} - -type LanZouFilesResp struct { - Zt int `json:"zt"` - Info interface{} `json:"info"` - Text []LanZouFile `json:"text"` -} - -func (driver *Lanzou) GetFiles(folderId string, account *model.Account) ([]LanZouFile, error) { - if account.InternalType == "cookie" { - files := make([]LanZouFile, 0) - var resp LanZouFilesResp - // folders - res, err := base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken). - SetFormData(map[string]string{ - "task": "47", - "folder_id": folderId, - }).Post("https://pc.woozooo.com/doupload.php") - if err != nil { - return nil, err - } - log.Debug(res.String()) - if resp.Zt != 1 && resp.Zt != 2 { - return nil, fmt.Errorf("%v", resp.Info) - } - for _, file := range resp.Text { - file.Folder = true - files = append(files, file) - } - // files - pg := 1 - for { - _, err = base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken). - SetFormData(map[string]string{ - "task": "5", - "folder_id": folderId, - "pg": strconv.Itoa(pg), - }).Post("https://pc.woozooo.com/doupload.php") - if err != nil { - return nil, err - } - if resp.Zt != 1 { - return nil, fmt.Errorf("%v", resp.Info) - } - if len(resp.Text) == 0 { - break - } - files = append(files, resp.Text...) - pg++ - } - return files, nil - } else { - return driver.GetFilesByUrl(account) - } -} - -func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error) { - files := make([]LanZouFile, 0) - shareUrl := account.SiteUrl - u, err := url.Parse(shareUrl) - if err != nil { - return nil, err - } - res, err := base.RestyClient.R().Get(shareUrl) - if err != nil { - return nil, err - } - lxArr := regexp.MustCompile(`'lx':(.+?),`).FindStringSubmatch(res.String()) - if len(lxArr) == 0 { - return nil, fmt.Errorf("get empty page") - } - lx := lxArr[1] - fid := regexp.MustCompile(`'fid':(.+?),`).FindStringSubmatch(res.String())[1] - uid := regexp.MustCompile(`'uid':'(.+?)',`).FindStringSubmatch(res.String())[1] - rep := regexp.MustCompile(`'rep':'(.+?)',`).FindStringSubmatch(res.String())[1] - up := regexp.MustCompile(`'up':(.+?),`).FindStringSubmatch(res.String())[1] - ls := "" - if account.Password != "" { - ls = regexp.MustCompile(`'ls':(.+?),`).FindStringSubmatch(res.String())[1] - } - tName := regexp.MustCompile(`'t':(.+?),`).FindStringSubmatch(res.String())[1] - kName := regexp.MustCompile(`'k':(.+?),`).FindStringSubmatch(res.String())[1] - t := regexp.MustCompile(`var ` + tName + ` = '(.+?)';`).FindStringSubmatch(res.String())[1] - k := regexp.MustCompile(`var ` + kName + ` = '(.+?)';`).FindStringSubmatch(res.String())[1] - pg := 1 - for { - var resp LanZouFilesResp - res, err = base.RestyClient.R().SetResult(&resp).SetFormData(map[string]string{ - "lx": lx, - "fid": fid, - "uid": uid, - "pg": strconv.Itoa(pg), - "rep": rep, - "t": t, - "k": k, - "up": up, - "ls": ls, - "pwd": account.Password, - }).Post(fmt.Sprintf("https://%s/filemoreajax.php", u.Host)) - if err != nil { - log.Debug(err) - break - } - log.Debug(res.String()) - if resp.Zt != 1 { - return nil, fmt.Errorf("%v", resp.Info) - } - if len(resp.Text) == 0 { - break - } - pg++ - time.Sleep(time.Second) - files = append(files, resp.Text...) - } - return files, nil -} - -//type LanzouDownInfo struct { -// FId string `json:"f_id"` -// IsNewd string `json:"is_newd"` -//} - -// GetDownPageId 获取下载页面的ID -func (driver *Lanzou) GetDownPageId(fileId string, account *model.Account) (string, string, error) { - var resp DownPageResp - res, err := base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken). - SetFormData(map[string]string{ - "task": "22", - "file_id": fileId, - }).Post("https://pc.woozooo.com/doupload.php") - if err != nil { - return "", "", err - } - log.Debug(res.String()) - if resp.Zt != 1 { - return "", "", fmt.Errorf("%v", resp.Info) - } - return resp.Info.FId, resp.Info.Pwd, nil -} - -type LanzouLinkResp struct { - Dom string `json:"dom"` - Url string `json:"url"` - Zt int `json:"zt"` -} - -func (driver *Lanzou) GetLink(downId string, pwd string, account *model.Account) (string, error) { - shareUrl := account.SiteUrl - u, err := url.Parse(shareUrl) - if err != nil { - return "", err - } - log.Debugln(fmt.Sprintf("https://%s/%s", u.Host, downId)) - res, err := base.RestyClient.R().Get(fmt.Sprintf("https://%s/%s", u.Host, downId)) - if err != nil { - return "", err - } - iframe := regexp.MustCompile(`