Repository: AlistGo/alist (language: go)

Issue:
迅雷云盘WebDAV上传报错 但其实文件已经上传完成
### Alist Version / Alist 版本

Docker: alist:latest

### Describe the bug / 问题描述

作者你好！我是用 Docker 搭建的，网页端上传正常，本地目录的WebDAV上传也正常，但是用WebDAV向迅雷云盘上传文件报错：

![alist_webdav_error2](https://user-images.githubusercontent.com/4454570/158163400-67d92314-b414-4fb8-b204-c64444dffdba.jpg)

虽然报错，但是去云盘里看，发现已经正常上传了。
我换用 RaiDrive 挂载 WebDAV 测试，貌似也出现了这个问题。

### Reproduction / 复现链接

上传文件报错时的日志：

![alist_webdav_error](https://user-images.githubusercontent.com/4454570/158164147-7ae5b387-96b2-4750-8e84-11a29a2549a3.jpg)


### Logs / 日志

_No response_
疑似不能使用ssl连接mysql
### Alist Version / Alist 版本

v2.2.0

### Describe the bug / 问题描述

hello，希望使用Mysql做数据库，已经修改data/config.json文件，但是mysql必须要用ssl连接方式，我设置了"ssl_mode": "VERIFY_IDENTITY",运行依旧提示”failed to connect database:Error 1105: unknown error: Code: UNAVAILABLE
server does not allow insecure connections, client must use SSL/TLS“，请问是程序本身不支持ssl模式连接mysql吗？还是说我语法错误呢？因为不是很了解go语言的程序，python中是设置为"VERIFY_IDENTITY"，麻烦作者查看下！
+ 运行环境：linux-amd64
+ 运行方式：使用手动部署，下载[Releases]中的文件运行

### Reproduction / 复现链接

_No response_

### Logs / 日志

```shell
INFO[2022-03-15 08:47:57] reading config file: data/config.json        
INFO[2022-03-15 08:47:57] init cron...                                 
INFO[2022-03-15 08:47:57] init model...                                
FATA[2022-03-15 08:47:58] failed to connect database:Error 1105: unknown error: Code: UNAVAILABLE
server does not allow insecure connections, client must use SSL/TLS
```



Discussion / hints:
Thanks for opening your first issue here! Be sure to follow the issue template!



Fix patch:
diff --git a/README.md b/README.md
index 5c69fcfcdb0..b3ab3133035 100755
--- a/README.md
+++ b/README.md
@@ -69,6 +69,10 @@ Available at: <https://alist.nn.ci>.
 
 <https://alist-doc.nn.ci/en/>
 
+## Special sponsors
+- [Find Resources - Aliyundrive Resource Search Engine](https://zhaoziyuan.la/)
+- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
+
 ## License
 
 The `AList` is open-source software licensed under the AGPL-3.0 license.
diff --git a/README_cn.md b/README_cn.md
index 30b3599c10a..8790892e9bd 100644
--- a/README_cn.md
+++ b/README_cn.md
@@ -69,6 +69,10 @@
 
 <https://alist-doc.nn.ci/>
 
+## 特别赞助
+- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
+- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
+
 ##  许可
 
 `AList` 是在 AGPL-3.0 许可下许可的开源软件。
diff --git a/bootstrap/model.go b/bootstrap/model.go
index 028ef8d1b6e..94453e10524 100644
--- a/bootstrap/model.go
+++ b/bootstrap/model.go
@@ -50,8 +50,8 @@ func InitModel() {
 		}
 	case "mysql":
 		{
-			dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
-				databaseConfig.User, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Name)
+			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())
diff --git a/conf/config.go b/conf/config.go
index 280cd5df968..7d4c0fe0072 100644
--- a/conf/config.go
+++ b/conf/config.go
@@ -45,7 +45,6 @@ func DefaultConfig() *Config {
 			Port:        0,
 			TablePrefix: "x_",
 			DBFile:      "data/data.db",
-			SslMode:     "disable",
 		},
 		Cache: CacheConfig{
 			Expiration:      60,
diff --git a/drivers/123/driver.go b/drivers/123/driver.go
index 0491ab36a8e..27ffcefb578 100644
--- a/drivers/123/driver.go
+++ b/drivers/123/driver.go
@@ -140,7 +140,7 @@ func (driver Pan123) Link(args base.Args, account *model.Account) (*base.Link, e
 	}
 	var resp Pan123DownResp
 	var headers map[string]string
-	if args.IP != "" && args.IP != "::1" {
+	if !utils.IsLocalIPAddr(args.IP) {
 		headers = map[string]string{
 			//"X-Real-IP":       "1.1.1.1",
 			"X-Forwarded-For": args.IP,
diff --git a/drivers/189/189.go b/drivers/189/189.go
index 1adf8818644..79d0bbc2a35 100644
--- a/drivers/189/189.go
+++ b/drivers/189/189.go
@@ -17,6 +17,7 @@ import (
 	"io"
 	"math"
 	"net/http"
+	"net/http/cookiejar"
 	"path/filepath"
 	"regexp"
 	"strconv"
@@ -105,6 +106,9 @@ func (driver Cloud189) Login(account *model.Account) error {
 		client.SetRetryCount(3)
 		client.SetHeader("Referer", "https://cloud.189.cn/")
 	}
+	// clear cookie
+	jar, _ := cookiejar.New(nil)
+	client.SetCookieJar(jar)
 	url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action"
 	b := ""
 	lt := ""
diff --git a/drivers/189pc/189.go b/drivers/189pc/189.go
index e14d0081371..9dcf1c68c17 100644
--- a/drivers/189pc/189.go
+++ b/drivers/189pc/189.go
@@ -16,7 +16,6 @@ import (
 	"github.com/Xhofe/alist/utils"
 	"github.com/go-resty/resty/v2"
 	"github.com/google/uuid"
-	jsoniter "github.com/json-iterator/go"
 	log "github.com/sirupsen/logrus"
 )
 
@@ -110,7 +109,7 @@ func (s *State) login(account *model.Account) error {
 	if err != nil {
 		return err
 	}
-	toUrl := jsoniter.Get(res.Body(), "toUrl").ToString()
+	toUrl := utils.Json.Get(res.Body(), "toUrl").ToString()
 	if toUrl == "" {
 		log.Error(res.String())
 		return fmt.Errorf(res.String())
@@ -179,10 +178,10 @@ func (s *State) getLoginParam() (*LoginParam, error) {
 		if err != nil {
 			return nil, err
 		}
-		if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 {
-			return nil, errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString())
+		if utils.Json.Get(vRes.Body(), "status").ToInt() != 200 {
+			return nil, errors.New("ocr error:" + utils.Json.Get(vRes.Body(), "msg").ToString())
 		}
-		param.vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString()
+		param.vCodeRS = utils.Json.Get(vRes.Body(), "result").ToString()
 		log.Debugln("code: ", param.vCodeRS)
 	}
 	return param, nil
@@ -308,9 +307,9 @@ func (s *State) Request(method string, fullUrl string, params url.Values, callba
 	}
 
 	if account != nil {
-		switch jsoniter.Get(res.Body(), "res_code").ToInt64() {
+		switch utils.Json.Get(res.Body(), "res_code").ToInt64() {
 		case 11, 18:
-			if err := s.refreshSession(account); err != nil {
+			if err := s.RefreshSession(account); err != nil {
 				return nil, err
 			}
 			return s.Request(method, fullUrl, params, callback, account)
@@ -324,8 +323,8 @@ func (s *State) Request(method string, fullUrl string, params url.Values, callba
 		}
 	}
 
-	if jsoniter.Get(res.Body(), "res_code").ToInt64() != 0 {
-		return res, fmt.Errorf(jsoniter.Get(res.Body(), "res_message").ToString())
+	if utils.Json.Get(res.Body(), "res_code").ToInt64() != 0 {
+		return res, fmt.Errorf(utils.Json.Get(res.Body(), "res_message").ToString())
 	}
 	return res, nil
 }
diff --git a/drivers/189pc/driver.go b/drivers/189pc/driver.go
index bb01775be88..1faf28d4c34 100644
--- a/drivers/189pc/driver.go
+++ b/drivers/189pc/driver.go
@@ -3,7 +3,6 @@ package _189
 import (
 	"crypto/md5"
 	"encoding/hex"
-	"encoding/json"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -64,9 +63,10 @@ func (driver Cloud189) Items() []base.Item {
 			Values:   "Personal,Family",
 		},
 		{
-			Name:  "site_id",
-			Label: "family id",
-			Type:  base.TypeString,
+			Name:     "site_id",
+			Label:    "family id",
+			Type:     base.TypeString,
+			Required: true,
 		},
 		{
 			Name:     "order_by",
@@ -96,13 +96,41 @@ func (driver Cloud189) Save(account *model.Account, old *model.Account) error {
 
 	state := GetState(account)
 	if !state.IsLogin() {
-		return state.Login(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("GET", 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 == "/" {
@@ -329,7 +357,7 @@ func (driver Cloud189) Move(src string, dst string, account *model.Account) erro
 	_, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) {
 		r.SetFormData(clientSuffix()).SetFormData(map[string]string{
 			"type": "MOVE",
-			"taskInfos": string(MustToBytes(json.Marshal(
+			"taskInfos": string(MustToBytes(utils.Json.Marshal(
 				[]*BatchTaskInfo{
 					{
 						FileId:   srcFile.Id,
@@ -445,7 +473,7 @@ func (driver Cloud189) Copy(src string, dst string, account *model.Account) erro
 	_, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) {
 		r.SetFormData(clientSuffix()).SetFormData(map[string]string{
 			"type": "COPY",
-			"taskInfos": string(MustToBytes(json.Marshal(
+			"taskInfos": string(MustToBytes(utils.Json.Marshal(
 				[]*BatchTaskInfo{
 					{
 						FileId:   srcFile.Id,
@@ -475,7 +503,7 @@ func (driver Cloud189) Delete(path string, account *model.Account) error {
 	_, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) {
 		r.SetFormData(clientSuffix()).SetFormData(map[string]string{
 			"type": "DELETE",
-			"taskInfos": string(MustToBytes(json.Marshal(
+			"taskInfos": string(MustToBytes(utils.Json.Marshal(
 				[]*BatchTaskInfo{
 					{
 						FileId:   srcFile.Id,
@@ -546,7 +574,7 @@ func (driver Cloud189) uploadFamily(file *model.FileStream, parentFile *model.Fi
 	}
 
 	if createUpload.FileDataExists != 1 {
-		if err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
+		if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
 			return err
 		}
 	}
@@ -601,7 +629,7 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi
 	}
 
 	if createUpload.FileDataExists != 1 {
-		if err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
+		if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
 			return err
 		}
 	}
@@ -618,40 +646,39 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi
 	return err
 }
 
-func (driver Cloud189) uploadFileData(file *model.FileStream, tempFile *os.File, createUpload CreateUploadFileResult, account *model.Account) error {
-	var uploadFileState *UploadFileStatusResult
-	var err error
-	for i := 0; i < 10; i++ {
-		if uploadFileState, err = driver.getUploadFileState(createUpload.UploadFileId, account); err != nil {
-			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 nil
-		}
+	if uploadFileState.FileDataExists == 1 || uploadFileState.DataSize == int64(file.Size) {
+		return uploadFileState.UploadFileId, nil
+	}
 
-		if _, err = tempFile.Seek(uploadFileState.DataSize, io.SeekStart); err != nil {
-			return err
-		}
+	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())
+	_, 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{
-				"ResumePolicy":           "1",
-				"Edrive-UploadFileId":    fmt.Sprint(createUpload.UploadFileId),
-				"Edrive-UploadFileRange": fmt.Sprintf("bytes=%d-%d", uploadFileState.DataSize, file.Size),
-				"Expect":                 "100-continue",
+				"familyId":     account.SiteId,
+				"UploadFileId": fmt.Sprint(uploadFileState.UploadFileId),
 			})
-			if isFamily(account) {
-				r.SetHeader("FamilyId", account.SiteId)
-			}
-			r.SetBody(tempFile)
-		}, account)
-		if err == nil {
-			break
+		} else {
+			r.SetHeader("Edrive-UploadFileId", fmt.Sprint(uploadFileState.UploadFileId))
 		}
-	}
-	return err
+		r.SetBody(tempFile)
+	}, account)
+	return uploadFileState.UploadFileId, err
 }
 
 func (driver Cloud189) getUploadFileState(uploadFileId int64, account *model.Account) (*UploadFileStatusResult, error) {
diff --git a/drivers/189pc/type.go b/drivers/189pc/type.go
index d155744426b..12e54087757 100644
--- a/drivers/189pc/type.go
+++ b/drivers/189pc/type.go
@@ -60,6 +60,19 @@ type appSessionResp struct {
 	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 {
diff --git a/drivers/alidrive/driver.go b/drivers/alidrive/driver.go
index 0ddcf68b837..8ef0c7b07ce 100644
--- a/drivers/alidrive/driver.go
+++ b/drivers/alidrive/driver.go
@@ -412,7 +412,7 @@ func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) er
 	buf := make([]byte, 1024)
 	n, _ := file.Read(buf[:])
 	reqBody := base.Json{
-		"check_name_mode": "auto_rename",
+		"check_name_mode": "overwrite",
 		"drive_id":        account.DriveId,
 		"name":            file.GetFileName(),
 		"parent_file_id":  parentFile.Id,
diff --git a/drivers/baidu/driver.go b/drivers/baidu/driver.go
index 7493d1803a3..e1a33f5a078 100644
--- a/drivers/baidu/driver.go
+++ b/drivers/baidu/driver.go
@@ -76,6 +76,9 @@ func (driver Baidu) Items() []base.Item {
 }
 
 func (driver Baidu) Save(account *model.Account, old *model.Account) error {
+	if account == nil {
+		return nil
+	}
 	return driver.RefreshToken(account)
 }
 
diff --git a/drivers/xunlei/driver.go b/drivers/xunlei/driver.go
index 8ecde6523ec..6d39ad226d1 100644
--- a/drivers/xunlei/driver.go
+++ b/drivers/xunlei/driver.go
@@ -4,12 +4,12 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
-	"net/url"
 	"os"
 	"path/filepath"
 	"strconv"
 
 	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+	"github.com/go-resty/resty/v2"
 
 	"github.com/Xhofe/alist/conf"
 	"github.com/Xhofe/alist/drivers/base"
@@ -95,12 +95,13 @@ func (driver XunLeiCloud) File(path string, account *model.Account) (*model.File
 }
 
 func (driver XunLeiCloud) 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(utils.ParsePath(path), account)
+	file, err := driver.File(path, account)
 	if err != nil {
 		return nil, err
 	}
@@ -108,8 +109,16 @@ func (driver XunLeiCloud) Files(path string, account *model.Account) ([]model.Fi
 	files := make([]model.File, 0)
 	for {
 		var fileList FileList
-		u := fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files?parent_id=%s&page_token=%s&with_audit=true&filters=%s", file.Id, fileList.NextPageToken, url.QueryEscape(`{"phase": {"eq": "PHASE_TYPE_COMPLETE"}, "trashed":{"eq":false}}`))
-		if err = GetState(account).Request("GET", u, nil, &fileList, account); err != nil {
+		_, err = GetState(account).Request("GET", FILE_API_URL, func(r *resty.Request) {
+			r.SetQueryParams(map[string]string{
+				"parent_id":  file.Id,
+				"page_token": fileList.NextPageToken,
+				"with_audit": "true",
+				"filters":    `{"phase": {"eq": "PHASE_TYPE_COMPLETE"}, "trashed":{"eq":false}}`,
+			})
+			r.SetResult(&fileList)
+		}, account)
+		if err != nil {
 			return nil, err
 		}
 		for _, file := range fileList.Files {
@@ -153,7 +162,12 @@ func (driver XunLeiCloud) Link(args base.Args, account *model.Account) (*base.Li
 		return nil, base.ErrNotFile
 	}
 	var lFile Files
-	if err = GetState(account).Request("GET", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s?&with_audit=true", file.Id), nil, &lFile, account); err != nil {
+	_, err = GetState(account).Request("GET", FILE_API_URL+"/{id}", func(r *resty.Request) {
+		r.SetPathParam("id", file.Id)
+		r.SetQueryParam("with_audit", "true")
+		r.SetResult(&lFile)
+	}, account)
+	if err != nil {
 		return nil, err
 	}
 	return &base.Link{
@@ -194,7 +208,14 @@ func (driver XunLeiCloud) MakeDir(path string, account *model.Account) error {
 	if !parentFile.IsDir() {
 		return base.ErrNotFolder
 	}
-	return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files", &base.Json{"kind": FOLDER, "name": name, "parent_id": parentFile.Id}, nil, account)
+	_, err = GetState(account).Request("POST", FILE_API_URL, func(r *resty.Request) {
+		r.SetBody(&base.Json{
+			"kind":      FOLDER,
+			"name":      name,
+			"parent_id": parentFile.Id,
+		})
+	}, account)
+	return err
 }
 
 func (driver XunLeiCloud) Move(src string, dst string, account *model.Account) error {
@@ -207,7 +228,14 @@ func (driver XunLeiCloud) Move(src string, dst string, account *model.Account) e
 	if err != nil {
 		return err
 	}
-	return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files:batchMove", &base.Json{"to": base.Json{"parent_id": dstDirFile.Id}, "ids": []string{srcFile.Id}}, nil, account)
+
+	_, err = GetState(account).Request("POST", FILE_API_URL+":batchMove", func(r *resty.Request) {
+		r.SetBody(&base.Json{
+			"to":  base.Json{"parent_id": dstDirFile.Id},
+			"ids": []string{srcFile.Id},
+		})
+	}, account)
+	return err
 }
 
 func (driver XunLeiCloud) Copy(src string, dst string, account *model.Account) error {
@@ -220,7 +248,13 @@ func (driver XunLeiCloud) Copy(src string, dst string, account *model.Account) e
 	if err != nil {
 		return err
 	}
-	return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files:batchCopy", &base.Json{"to": base.Json{"parent_id": dstDirFile.Id}, "ids": []string{srcFile.Id}}, nil, account)
+	_, err = GetState(account).Request("POST", FILE_API_URL+":batchCopy", func(r *resty.Request) {
+		r.SetBody(&base.Json{
+			"to":  base.Json{"parent_id": dstDirFile.Id},
+			"ids": []string{srcFile.Id},
+		})
+	}, account)
+	return err
 }
 
 func (driver XunLeiCloud) Delete(path string, account *model.Account) error {
@@ -228,7 +262,11 @@ func (driver XunLeiCloud) Delete(path string, account *model.Account) error {
 	if err != nil {
 		return err
 	}
-	return GetState(account).Request("PATCH", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s/trash", srcFile.Id), &base.Json{}, nil, account)
+	_, err = GetState(account).Request("PATCH", FILE_API_URL+"/{id}/trash", func(r *resty.Request) {
+		r.SetPathParam("id", srcFile.Id)
+		r.SetBody(&base.Json{})
+	}, account)
+	return err
 }
 
 func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account) error {
@@ -246,7 +284,6 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
 		return err
 	}
 
-	defer tempFile.Close()
 	defer os.Remove(tempFile.Name())
 
 	gcid, err := getGcid(io.TeeReader(file, tempFile), int64(file.Size))
@@ -254,29 +291,37 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
 		return err
 	}
 
-	var rep UploadTaskResponse
-	err = GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files", &base.Json{
-		"kind":        FILE,
-		"parent_id":   parentFile.Id,
-		"name":        file.Name,
-		"size":        fmt.Sprint(file.Size),
-		"hash":        gcid,
-		"upload_type": UPLOAD_TYPE_RESUMABLE,
-	}, &rep, account)
-	if err != nil {
-		return err
-	}
+	tempFile.Close()
 
-	param := rep.Resumable.Params
-	client, err := oss.New(param.Endpoint, param.AccessKeyID, param.AccessKeySecret, oss.SecurityToken(param.SecurityToken), oss.EnableMD5(true), oss.HTTPClient(xunleiClient.GetClient()))
+	var resp UploadTaskResponse
+	_, err = GetState(account).Request("POST", FILE_API_URL, func(r *resty.Request) {
+		r.SetBody(&base.Json{
+			"kind":        FILE,
+			"parent_id":   parentFile.Id,
+			"name":        file.Name,
+			"size":        fmt.Sprint(file.Size),
+			"hash":        gcid,
+			"upload_type": UPLOAD_TYPE_RESUMABLE,
+		})
+		r.SetResult(&resp)
+	}, account)
 	if err != nil {
 		return err
 	}
-	bucket, err := client.Bucket(param.Bucket)
-	if err != nil {
-		return err
+
+	param := resp.Resumable.Params
+	if resp.UploadType == UPLOAD_TYPE_RESUMABLE {
+		client, err := oss.New(param.Endpoint, param.AccessKeyID, param.AccessKeySecret, oss.SecurityToken(param.SecurityToken), oss.EnableMD5(true))
+		if err != nil {
+			return err
+		}
+		bucket, err := client.Bucket(param.Bucket)
+		if err != nil {
+			return err
+		}
+		return bucket.UploadFile(param.Key, tempFile.Name(), 1<<22, oss.Routines(3), oss.Checkpoint(true, ""), oss.Expires(param.Expiration))
 	}
-	return bucket.UploadFile(param.Key, tempFile.Name(), 4*1024*1024, oss.Routines(3), oss.Checkpoint(true, ""), oss.Expires(param.Expiration))
+	return nil
 }
 
 func (driver XunLeiCloud) Rename(src string, dst string, account *model.Account) error {
@@ -285,8 +330,11 @@ func (driver XunLeiCloud) Rename(src string, dst string, account *model.Account)
 	if err != nil {
 		return err
 	}
-
-	return GetState(account).Request("PATCH", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s", srcFile.Id), &base.Json{"name": dstName}, nil, account)
+	_, err = GetState(account).Request("PATCH", FILE_API_URL+"/{id}", func(r *resty.Request) {
+		r.SetPathParam("id", srcFile.Id)
+		r.SetBody(&base.Json{"name": dstName})
+	}, account)
+	return err
 }
 
 var _ base.Driver = (*XunLeiCloud)(nil)
diff --git a/drivers/xunlei/util.go b/drivers/xunlei/util.go
index 6b64ab3cc3d..6c5315c9da3 100644
--- a/drivers/xunlei/util.go
+++ b/drivers/xunlei/util.go
@@ -37,6 +37,12 @@ var Algorithms = []string{
 	"T78dnANexYRbiZy",
 }
 
+const (
+	API_URL        = "https://api-pan.xunlei.com/drive/v1"
+	FILE_API_URL   = API_URL + "/files"
+	XLUSER_API_URL = "https://xluser-ssl.xunlei.com/v1"
+)
+
 const (
 	FOLDER = "drive#folder"
 	FILE   = "drive#file"
diff --git a/drivers/xunlei/xunlei.go b/drivers/xunlei/xunlei.go
index 67d9b8018bc..cd9f32f5f46 100644
--- a/drivers/xunlei/xunlei.go
+++ b/drivers/xunlei/xunlei.go
@@ -2,6 +2,7 @@ package xunlei
 
 import (
 	"fmt"
+	"net/http"
 	"sync"
 	"time"
 
@@ -12,7 +13,7 @@ import (
 	log "github.com/sirupsen/logrus"
 )
 
-var xunleiClient = resty.New().SetTimeout(120 * time.Second)
+var xunleiClient = resty.New().SetHeaders(map[string]string{"Accept": "application/json;charset=UTF-8"})
 
 // 一个账户只允许登陆一次
 var userStateCache = struct {
@@ -102,16 +103,16 @@ func (s *State) newCaptchaToken(action string, meta map[string]string, account *
 	var e Erron
 	var resp CaptchaTokenResponse
 	_, err := xunleiClient.R().
-		SetHeader("X-Device-Id", driverID).
 		SetBody(&creq).
 		SetError(&e).
 		SetResult(&resp).
-		Post("https://xluser-ssl.xunlei.com/v1/shield/captcha/init?client_id=" + CLIENT_ID)
+		SetHeader("X-Device-Id", driverID).
+		SetQueryParam("client_id", CLIENT_ID).
+		Post(XLUSER_API_URL + "/shield/captcha/init")
 	if err != nil {
 		return "", err
 	}
 	if e.ErrorCode != 0 {
-		log.Debugf("%+v\n %+v", e, account)
 		return "", fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
 	}
 	if resp.Url != "" {
@@ -120,7 +121,6 @@ func (s *State) newCaptchaToken(action string, meta map[string]string, account *
 
 	s.captchaTokenExpiresTime = (ctime + resp.ExpiresIn*1000) - 30000
 	s.captchaToken = resp.CaptchaToken
-	log.Debugf("%+v\n %+v", s.captchaToken, account)
 	return s.captchaToken, nil
 }
 
@@ -136,7 +136,7 @@ func (s *State) refreshToken_(account *model.Account) error {
 			"client_secret": CLIENT_SECRET,
 		}).
 		SetHeader("X-Device-Id", utils.GetMD5Encode(account.Username)).SetQueryParam("client_id", CLIENT_ID).
-		Post("https://xluser-ssl.xunlei.com/v1/auth/token")
+		Post(XLUSER_API_URL + "/auth/token")
 	if err != nil {
 		return err
 	}
@@ -152,7 +152,6 @@ func (s *State) refreshToken_(account *model.Account) error {
 		s.userID = resp.UserID
 		return nil
 	default:
-		log.Debugf("%+v\n %+v", e, account)
 		return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
 	}
 }
@@ -160,7 +159,7 @@ func (s *State) refreshToken_(account *model.Account) error {
 func (s *State) login(account *model.Account) error {
 	s.init()
 	ctime := time.Now().UnixMilli()
-	url := "https://xluser-ssl.xunlei.com/v1/auth/signin"
+	url := XLUSER_API_URL + "/auth/signin"
 	captchaToken, err := s.newCaptchaToken(getAction("POST", url), map[string]string{"username": account.Username}, account)
 	if err != nil {
 		return err
@@ -190,7 +189,6 @@ func (s *State) login(account *model.Account) error {
 	defer model.SaveAccount(account)
 	if e.ErrorCode != 0 {
 		account.Status = e.Error
-		log.Debugf("%+v\n %+v", e, account)
 		return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
 	}
 	account.Status = "work"
@@ -199,67 +197,68 @@ func (s *State) login(account *model.Account) error {
 	s.accessToken = resp.AccessToken
 	s.refreshToken = resp.RefreshToken
 	s.userID = resp.UserID
-	log.Debugf("%+v\n %+v", resp, account)
 	return nil
 }
 
-func (s *State) Request(method string, url string, body interface{}, resp interface{}, account *model.Account) error {
+func (s *State) Request(method string, url string, callback func(*resty.Request), account *model.Account) (*resty.Response, error) {
 	s.Lock()
 	token, err := s.getToken(account)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	captchaToken, err := s.getCaptchaToken(getAction(method, url), account)
 	if err != nil {
-		return err
+		return nil, err
 	}
-	s.Unlock()
-	var e Erron
+
 	req := xunleiClient.R().
-		SetError(&e).
-		SetHeader("X-Device-Id", utils.GetMD5Encode(account.Username)).
-		SetHeader("Authorization", token).
-		SetHeader("X-Captcha-Token", captchaToken).
+		SetHeaders(map[string]string{
+			"X-Device-Id":     utils.GetMD5Encode(account.Username),
+			"Authorization":   token,
+			"X-Captcha-Token": captchaToken,
+		}).
 		SetQueryParam("client_id", CLIENT_ID)
 
-	if body != nil {
-		req.SetBody(body)
-	}
-	if resp != nil {
-		req.SetResult(resp)
-	}
+	callback(req)
+	s.Unlock()
 
+	var res *resty.Response
 	switch method {
 	case "GET":
-		_, err = req.Get(url)
+		res, err = req.Get(url)
 	case "POST":
-		_, err = req.Post(url)
+		res, err = req.Post(url)
 	case "DELETE":
-		_, err = req.Delete(url)
+		res, err = req.Delete(url)
 	case "PATCH":
-		_, err = req.Patch(url)
+		res, err = req.Patch(url)
 	case "PUT":
-		_, err = req.Put(url)
+		res, err = req.Put(url)
 	default:
-		return base.ErrNotSupport
+		return nil, base.ErrNotSupport
 	}
 
 	if err != nil {
-		return err
+		return nil, err
 	}
+	log.Debug(res.String())
 
+	var e Erron
+	utils.Json.Unmarshal(res.Body(), &e)
 	switch e.ErrorCode {
-	case 0:
-		return nil
 	case 9:
 		s.newCaptchaToken(getAction(method, url), nil, account)
 		fallthrough
 	case 4122, 4121:
-		return s.Request(method, url, body, resp, account)
+		return s.Request(method, url, callback, account)
+	case 0:
+		if res.StatusCode() == http.StatusOK {
+			return res, nil
+		}
+		return nil, fmt.Errorf(res.String())
 	default:
-		log.Debugf("%+v\n %+v", e, account)
-		return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
+		return nil, fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
 	}
 }
 
diff --git a/server/webdav/file.go b/server/webdav/file.go
index 479a16dfd9f..995cc26b75d 100644
--- a/server/webdav/file.go
+++ b/server/webdav/file.go
@@ -156,29 +156,27 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, rawPath string) error
 	return operate.MakeDir(driver, account, path_, true)
 }
 
-func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath string) error {
+func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath string) (FileInfo, error) {
 	rawPath = utils.ParsePath(rawPath)
 	if model.AccountsCount() > 1 && rawPath == "/" {
-		return ErrNotImplemented
+		return nil, ErrNotImplemented
 	}
 	account, path_, driver, err := common.ParsePath(rawPath)
 	if err != nil {
-		return err
+		return nil, err
 	}
-	//fileSize, err := strconv.ParseUint(r.Header.Get("Content-Length"), 10, 64)
 	fileSize := uint64(r.ContentLength)
-	//if err != nil {
-	//	return err
-	//}
 	filePath, fileName := filepath.Split(path_)
 	now := time.Now()
+	fi := &model.File{
+		Name:      fileName,
+		Size:      0,
+		UpdatedAt: &now,
+	}
 	if fileSize == 0 {
-		upFileMap[rawPath] = &model.File{
-			Name:      fileName,
-			Size:      0,
-			UpdatedAt: &now,
-		}
-		return nil
+		// 如果文件大小为0，默认成功
+		upFileMap[rawPath] = fi
+		return fi, nil
 	} else {
 		delete(upFileMap, rawPath)
 	}
@@ -189,7 +187,7 @@ func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath strin
 		Name:       fileName,
 		ParentPath: filePath,
 	}
-	return operate.Upload(driver, account, &fileData, true)
+	return fi, operate.Upload(driver, account, &fileData, true)
 }
 
 func (fs *FileSystem) Delete(rawPath string) error {
diff --git a/server/webdav/webdav.go b/server/webdav/webdav.go
index 3db500e351c..b556605414f 100644
--- a/server/webdav/webdav.go
+++ b/server/webdav/webdav.go
@@ -94,7 +94,6 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, fs *FileSyst
 	}
 }
 
-// OK
 func (h *Handler) lock(now time.Time, root string, fs *FileSystem) (token string, status int, err error) {
 	token, err = h.LockSystem.Create(now, LockDetails{
 		Root:      root,
@@ -110,7 +109,6 @@ func (h *Handler) lock(now time.Time, root string, fs *FileSystem) (token string
 	return token, 0, nil
 }
 
-// ok
 func (h *Handler) confirmLocks(r *http.Request, src, dst string, fs *FileSystem) (release func(), status int, err error) {
 	hdr := r.Header.Get("If")
 	if hdr == "" {
@@ -189,7 +187,6 @@ func (h *Handler) confirmLocks(r *http.Request, src, dst string, fs *FileSystem)
 	return nil, http.StatusPreconditionFailed, ErrLocked
 }
 
-//OK
 func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
 	reqPath, status, err := h.stripPrefix(r.URL.Path)
 	if err != nil {
@@ -213,7 +210,6 @@ func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *File
 	return 0, nil
 }
 
-// OK
 func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
 
 	reqPath, status, err := h.stripPrefix(r.URL.Path)
@@ -248,7 +244,6 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *
 	return 0, nil
 }
 
-// OK
 func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
 
 	reqPath, status, err := h.stripPrefix(r.URL.Path)
@@ -267,7 +262,6 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *FileS
 	return http.StatusNoContent, nil
 }
 
-// OK
 func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
 	reqPath, status, err := h.stripPrefix(r.URL.Path)
 	if err != nil {
@@ -283,12 +277,13 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSyst
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	err = fs.Upload(ctx, r, reqPath)
+	fi, err := fs.Upload(ctx, r, reqPath)
 	if err != nil {
 		return http.StatusMethodNotAllowed, err
 	}
 
-	_, fi := isPathExist(ctx, fs, reqPath)
+	//_, fi := isPathExist(ctx, fs, reqPath)
+	// 为防止有些网盘上传有延时，这里认为上传没有报错就已经成功了,不再去判断是否存在
 	etag, err := findETag(ctx, fs, h.LockSystem, reqPath, fi)
 	if err != nil {
 		return http.StatusInternalServerError, err
@@ -297,7 +292,6 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSyst
 	return http.StatusCreated, nil
 }
 
-// OK
 func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
 	reqPath, status, err := h.stripPrefix(r.URL.Path)
 	if err != nil {
@@ -325,7 +319,6 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *FileSy
 	return http.StatusCreated, nil
 }
 
-// OK
 func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
 
 	hdr := r.Header.Get("Destination")
@@ -410,7 +403,6 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *Fil
 	return moveFiles(ctx, fs, src, dst, r.Header.Get("Overwrite") == "T")
 }
 
-// OK
 func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request, fs *FileSystem) (retStatus int, retErr error) {
 
 	duration, err := parseTimeout(r.Header.Get("Timeout"))
@@ -506,7 +498,6 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request, fs *FileSys
 	return 0, nil
 }
 
-// OK
 func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
 
 	// http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the
@@ -531,7 +522,6 @@ func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request, fs *FileS
 	}
 }
 
-// OK
 func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
 	reqPath, status, err := h.stripPrefix(r.URL.Path)
 	if err != nil {
diff --git a/utils/check.go b/utils/check.go
new file mode 100644
index 00000000000..65d62ee8500
--- /dev/null
+++ b/utils/check.go
@@ -0,0 +1,28 @@
+package utils
+
+import (
+	"net"
+)
+
+func IsLocalIPAddr(ip string) bool {
+	return IsLocalIP(net.ParseIP(ip))
+}
+
+func IsLocalIP(ip net.IP) bool {
+	if ip == nil {
+		return false
+	}
+	if ip.IsLoopback() {
+		return true
+	}
+
+	ip4 := ip.To4()
+	if ip4 == nil {
+		return false
+	}
+
+	return ip4[0] == 10 || // 10.0.0.0/8
+		(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
+		(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
+		(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
+}


Test patch: