Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/google_drive/util.go
1986 views
1
package google_drive
2
3
import (
4
"context"
5
"crypto/x509"
6
"encoding/pem"
7
"fmt"
8
"net/http"
9
"os"
10
"regexp"
11
"strconv"
12
"time"
13
14
"github.com/alist-org/alist/v3/drivers/base"
15
"github.com/alist-org/alist/v3/internal/driver"
16
"github.com/alist-org/alist/v3/internal/model"
17
"github.com/alist-org/alist/v3/pkg/http_range"
18
"github.com/alist-org/alist/v3/pkg/utils"
19
"github.com/go-resty/resty/v2"
20
"github.com/golang-jwt/jwt/v4"
21
log "github.com/sirupsen/logrus"
22
)
23
24
// do others that not defined in Driver interface
25
26
type googleDriveServiceAccount struct {
27
//Type string `json:"type"`
28
//ProjectID string `json:"project_id"`
29
//PrivateKeyID string `json:"private_key_id"`
30
PrivateKey string `json:"private_key"`
31
ClientEMail string `json:"client_email"`
32
//ClientID string `json:"client_id"`
33
//AuthURI string `json:"auth_uri"`
34
TokenURI string `json:"token_uri"`
35
//AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"`
36
//ClientX509CertURL string `json:"client_x509_cert_url"`
37
}
38
39
func (d *GoogleDrive) refreshToken() error {
40
// googleDriveServiceAccountFile gdsaFile
41
gdsaFile, gdsaFileErr := os.Stat(d.RefreshToken)
42
if gdsaFileErr == nil {
43
gdsaFileThis := d.RefreshToken
44
if gdsaFile.IsDir() {
45
if len(d.ServiceAccountFileList) <= 0 {
46
gdsaReadDir, gdsaDirErr := os.ReadDir(d.RefreshToken)
47
if gdsaDirErr != nil {
48
log.Error("read dir fail")
49
return gdsaDirErr
50
}
51
var gdsaFileList []string
52
for _, fi := range gdsaReadDir {
53
if !fi.IsDir() {
54
match, _ := regexp.MatchString("^.*\\.json$", fi.Name())
55
if !match {
56
continue
57
}
58
gdsaDirText := d.RefreshToken
59
if d.RefreshToken[len(d.RefreshToken)-1:] != "/" {
60
gdsaDirText = d.RefreshToken + "/"
61
}
62
gdsaFileList = append(gdsaFileList, gdsaDirText+fi.Name())
63
}
64
}
65
d.ServiceAccountFileList = gdsaFileList
66
gdsaFileThis = d.ServiceAccountFileList[d.ServiceAccountFile]
67
d.ServiceAccountFile++
68
} else {
69
if d.ServiceAccountFile < len(d.ServiceAccountFileList) {
70
d.ServiceAccountFile++
71
} else {
72
d.ServiceAccountFile = 0
73
}
74
gdsaFileThis = d.ServiceAccountFileList[d.ServiceAccountFile]
75
}
76
}
77
78
gdsaFileThisContent, err := os.ReadFile(gdsaFileThis)
79
if err != nil {
80
return err
81
}
82
83
// Now let's unmarshal the data into `payload`
84
var jsonData googleDriveServiceAccount
85
err = utils.Json.Unmarshal(gdsaFileThisContent, &jsonData)
86
if err != nil {
87
return err
88
}
89
90
gdsaScope := "https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.metadata https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/drive.scripts"
91
92
timeNow := time.Now()
93
var timeStart int64 = timeNow.Unix()
94
var timeEnd int64 = timeNow.Add(time.Minute * 60).Unix()
95
96
// load private key from string
97
privateKeyPem, _ := pem.Decode([]byte(jsonData.PrivateKey))
98
privateKey, _ := x509.ParsePKCS8PrivateKey(privateKeyPem.Bytes)
99
100
jwtToken := jwt.NewWithClaims(jwt.SigningMethodRS256,
101
jwt.MapClaims{
102
"iss": jsonData.ClientEMail,
103
"scope": gdsaScope,
104
"aud": jsonData.TokenURI,
105
"exp": timeEnd,
106
"iat": timeStart,
107
})
108
assertion, err := jwtToken.SignedString(privateKey)
109
if err != nil {
110
return err
111
}
112
113
var resp base.TokenResp
114
var e TokenError
115
res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).
116
SetFormData(map[string]string{
117
"assertion": assertion,
118
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
119
}).Post(jsonData.TokenURI)
120
if err != nil {
121
return err
122
}
123
log.Debug(res.String())
124
if e.Error != "" {
125
return fmt.Errorf(e.Error)
126
}
127
d.AccessToken = resp.AccessToken
128
return nil
129
} else if os.IsExist(gdsaFileErr) {
130
return gdsaFileErr
131
}
132
url := "https://www.googleapis.com/oauth2/v4/token"
133
var resp base.TokenResp
134
var e TokenError
135
res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).
136
SetFormData(map[string]string{
137
"client_id": d.ClientID,
138
"client_secret": d.ClientSecret,
139
"refresh_token": d.RefreshToken,
140
"grant_type": "refresh_token",
141
}).Post(url)
142
if err != nil {
143
return err
144
}
145
log.Debug(res.String())
146
if e.Error != "" {
147
return fmt.Errorf(e.Error)
148
}
149
d.AccessToken = resp.AccessToken
150
return nil
151
}
152
153
func (d *GoogleDrive) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
154
req := base.RestyClient.R()
155
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
156
req.SetQueryParam("includeItemsFromAllDrives", "true")
157
req.SetQueryParam("supportsAllDrives", "true")
158
if callback != nil {
159
callback(req)
160
}
161
if resp != nil {
162
req.SetResult(resp)
163
}
164
var e Error
165
req.SetError(&e)
166
res, err := req.Execute(method, url)
167
if err != nil {
168
return nil, err
169
}
170
if e.Error.Code != 0 {
171
if e.Error.Code == 401 {
172
err = d.refreshToken()
173
if err != nil {
174
return nil, err
175
}
176
return d.request(url, method, callback, resp)
177
}
178
return nil, fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
179
}
180
return res.Body(), nil
181
}
182
183
func (d *GoogleDrive) getFiles(id string) ([]File, error) {
184
pageToken := "first"
185
res := make([]File, 0)
186
for pageToken != "" {
187
if pageToken == "first" {
188
pageToken = ""
189
}
190
var resp Files
191
orderBy := "folder,name,modifiedTime desc"
192
if d.OrderBy != "" {
193
orderBy = d.OrderBy + " " + d.OrderDirection
194
}
195
query := map[string]string{
196
"orderBy": orderBy,
197
"fields": "files(id,name,mimeType,size,modifiedTime,createdTime,thumbnailLink,shortcutDetails,md5Checksum,sha1Checksum,sha256Checksum),nextPageToken",
198
"pageSize": "1000",
199
"q": fmt.Sprintf("'%s' in parents and trashed = false", id),
200
//"includeItemsFromAllDrives": "true",
201
//"supportsAllDrives": "true",
202
"pageToken": pageToken,
203
}
204
_, err := d.request("https://www.googleapis.com/drive/v3/files", http.MethodGet, func(req *resty.Request) {
205
req.SetQueryParams(query)
206
}, &resp)
207
if err != nil {
208
return nil, err
209
}
210
pageToken = resp.NextPageToken
211
res = append(res, resp.Files...)
212
}
213
return res, nil
214
}
215
216
func (d *GoogleDrive) chunkUpload(ctx context.Context, stream model.FileStreamer, url string) error {
217
var defaultChunkSize = d.ChunkSize * 1024 * 1024
218
var offset int64 = 0
219
for offset < stream.GetSize() {
220
if utils.IsCanceled(ctx) {
221
return ctx.Err()
222
}
223
chunkSize := stream.GetSize() - offset
224
if chunkSize > defaultChunkSize {
225
chunkSize = defaultChunkSize
226
}
227
reader, err := stream.RangeRead(http_range.Range{Start: offset, Length: chunkSize})
228
if err != nil {
229
return err
230
}
231
reader = driver.NewLimitedUploadStream(ctx, reader)
232
_, err = d.request(url, http.MethodPut, func(req *resty.Request) {
233
req.SetHeaders(map[string]string{
234
"Content-Length": strconv.FormatInt(chunkSize, 10),
235
"Content-Range": fmt.Sprintf("bytes %d-%d/%d", offset, offset+chunkSize-1, stream.GetSize()),
236
}).SetBody(reader).SetContext(ctx)
237
}, nil)
238
if err != nil {
239
return err
240
}
241
offset += chunkSize
242
}
243
return nil
244
}
245
246