github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/backend/fichier/api.go (about) 1 package fichier 2 3 import ( 4 "context" 5 "io" 6 "net/http" 7 "regexp" 8 "strconv" 9 "time" 10 11 "github.com/ncw/rclone/fs" 12 "github.com/ncw/rclone/fs/fserrors" 13 "github.com/ncw/rclone/lib/rest" 14 "github.com/pkg/errors" 15 ) 16 17 // retryErrorCodes is a slice of error codes that we will retry 18 var retryErrorCodes = []int{ 19 429, // Too Many Requests. 20 500, // Internal Server Error 21 502, // Bad Gateway 22 503, // Service Unavailable 23 504, // Gateway Timeout 24 509, // Bandwidth Limit Exceeded 25 } 26 27 // shouldRetry returns a boolean as to whether this resp and err 28 // deserve to be retried. It returns the err as a convenience 29 func shouldRetry(resp *http.Response, err error) (bool, error) { 30 return fserrors.ShouldRetry(err) || fserrors.ShouldRetryHTTP(resp, retryErrorCodes), err 31 } 32 33 var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString 34 35 func (f *Fs) getDownloadToken(url string) (*GetTokenResponse, error) { 36 request := DownloadRequest{ 37 URL: url, 38 Single: 1, 39 } 40 opts := rest.Opts{ 41 Method: "POST", 42 Path: "/download/get_token.cgi", 43 } 44 45 var token GetTokenResponse 46 err := f.pacer.Call(func() (bool, error) { 47 resp, err := f.rest.CallJSON(&opts, &request, &token) 48 return shouldRetry(resp, err) 49 }) 50 if err != nil { 51 return nil, errors.Wrap(err, "couldn't list files") 52 } 53 54 return &token, nil 55 } 56 57 func fileFromSharedFile(file *SharedFile) File { 58 return File{ 59 URL: file.Link, 60 Filename: file.Filename, 61 Size: file.Size, 62 } 63 } 64 65 func (f *Fs) listSharedFiles(ctx context.Context, id string) (entries fs.DirEntries, err error) { 66 opts := rest.Opts{ 67 Method: "GET", 68 RootURL: "https://1fichier.com/dir/", 69 Path: id, 70 Parameters: map[string][]string{"json": {"1"}}, 71 } 72 73 var sharedFiles SharedFolderResponse 74 err = f.pacer.Call(func() (bool, error) { 75 resp, err := f.rest.CallJSON(&opts, nil, &sharedFiles) 76 return shouldRetry(resp, err) 77 }) 78 if err != nil { 79 return nil, errors.Wrap(err, "couldn't list files") 80 } 81 82 entries = make([]fs.DirEntry, len(sharedFiles)) 83 84 for i, sharedFile := range sharedFiles { 85 entries[i] = f.newObjectFromFile(ctx, "", fileFromSharedFile(&sharedFile)) 86 } 87 88 return entries, nil 89 } 90 91 func (f *Fs) listFiles(directoryID int) (filesList *FilesList, err error) { 92 // fs.Debugf(f, "Requesting files for dir `%s`", directoryID) 93 request := ListFilesRequest{ 94 FolderID: directoryID, 95 } 96 97 opts := rest.Opts{ 98 Method: "POST", 99 Path: "/file/ls.cgi", 100 } 101 102 filesList = &FilesList{} 103 err = f.pacer.Call(func() (bool, error) { 104 resp, err := f.rest.CallJSON(&opts, &request, filesList) 105 return shouldRetry(resp, err) 106 }) 107 if err != nil { 108 return nil, errors.Wrap(err, "couldn't list files") 109 } 110 111 return filesList, nil 112 } 113 114 func (f *Fs) listFolders(directoryID int) (foldersList *FoldersList, err error) { 115 // fs.Debugf(f, "Requesting folders for id `%s`", directoryID) 116 117 request := ListFolderRequest{ 118 FolderID: directoryID, 119 } 120 121 opts := rest.Opts{ 122 Method: "POST", 123 Path: "/folder/ls.cgi", 124 } 125 126 foldersList = &FoldersList{} 127 err = f.pacer.Call(func() (bool, error) { 128 resp, err := f.rest.CallJSON(&opts, &request, foldersList) 129 return shouldRetry(resp, err) 130 }) 131 if err != nil { 132 return nil, errors.Wrap(err, "couldn't list folders") 133 } 134 135 // fs.Debugf(f, "Got FoldersList for id `%s`", directoryID) 136 137 return foldersList, err 138 } 139 140 func (f *Fs) listDir(ctx context.Context, dir string) (entries fs.DirEntries, err error) { 141 err = f.dirCache.FindRoot(ctx, false) 142 if err != nil { 143 return nil, err 144 } 145 146 directoryID, err := f.dirCache.FindDir(ctx, dir, false) 147 if err != nil { 148 return nil, err 149 } 150 151 folderID, err := strconv.Atoi(directoryID) 152 if err != nil { 153 return nil, err 154 } 155 156 files, err := f.listFiles(folderID) 157 if err != nil { 158 return nil, err 159 } 160 161 folders, err := f.listFolders(folderID) 162 if err != nil { 163 return nil, err 164 } 165 166 entries = make([]fs.DirEntry, len(files.Items)+len(folders.SubFolders)) 167 168 for i, item := range files.Items { 169 entries[i] = f.newObjectFromFile(ctx, dir, item) 170 } 171 172 for i, folder := range folders.SubFolders { 173 createDate, err := time.Parse("2006-01-02 15:04:05", folder.CreateDate) 174 if err != nil { 175 return nil, err 176 } 177 178 folder.Name = restoreReservedChars(folder.Name) 179 fullPath := getRemote(dir, folder.Name) 180 folderID := strconv.Itoa(folder.ID) 181 182 entries[len(files.Items)+i] = fs.NewDir(fullPath, createDate).SetID(folderID) 183 184 // fs.Debugf(f, "Put Path `%s` for id `%d` into dircache", fullPath, folder.ID) 185 f.dirCache.Put(fullPath, folderID) 186 } 187 188 return entries, nil 189 } 190 191 func (f *Fs) newObjectFromFile(ctx context.Context, dir string, item File) *Object { 192 return &Object{ 193 fs: f, 194 remote: getRemote(dir, item.Filename), 195 file: item, 196 } 197 } 198 199 func getRemote(dir, fileName string) string { 200 if dir == "" { 201 return fileName 202 } 203 204 return dir + "/" + fileName 205 } 206 207 func (f *Fs) makeFolder(leaf string, folderID int) (response *MakeFolderResponse, err error) { 208 name := replaceReservedChars(leaf) 209 // fs.Debugf(f, "Creating folder `%s` in id `%s`", name, directoryID) 210 211 request := MakeFolderRequest{ 212 FolderID: folderID, 213 Name: name, 214 } 215 216 opts := rest.Opts{ 217 Method: "POST", 218 Path: "/folder/mkdir.cgi", 219 } 220 221 response = &MakeFolderResponse{} 222 err = f.pacer.Call(func() (bool, error) { 223 resp, err := f.rest.CallJSON(&opts, &request, response) 224 return shouldRetry(resp, err) 225 }) 226 if err != nil { 227 return nil, errors.Wrap(err, "couldn't create folder") 228 } 229 230 // fs.Debugf(f, "Created Folder `%s` in id `%s`", name, directoryID) 231 232 return response, err 233 } 234 235 func (f *Fs) removeFolder(name string, folderID int) (response *GenericOKResponse, err error) { 236 // fs.Debugf(f, "Removing folder with id `%s`", directoryID) 237 238 request := &RemoveFolderRequest{ 239 FolderID: folderID, 240 } 241 242 opts := rest.Opts{ 243 Method: "POST", 244 Path: "/folder/rm.cgi", 245 } 246 247 response = &GenericOKResponse{} 248 var resp *http.Response 249 err = f.pacer.Call(func() (bool, error) { 250 resp, err = f.rest.CallJSON(&opts, request, response) 251 return shouldRetry(resp, err) 252 }) 253 if err != nil { 254 return nil, errors.Wrap(err, "couldn't remove folder") 255 } 256 if response.Status != "OK" { 257 return nil, errors.New("Can't remove non-empty dir") 258 } 259 260 // fs.Debugf(f, "Removed Folder with id `%s`", directoryID) 261 262 return response, nil 263 } 264 265 func (f *Fs) deleteFile(url string) (response *GenericOKResponse, err error) { 266 request := &RemoveFileRequest{ 267 Files: []RmFile{ 268 {url}, 269 }, 270 } 271 272 opts := rest.Opts{ 273 Method: "POST", 274 Path: "/file/rm.cgi", 275 } 276 277 response = &GenericOKResponse{} 278 err = f.pacer.Call(func() (bool, error) { 279 resp, err := f.rest.CallJSON(&opts, request, response) 280 return shouldRetry(resp, err) 281 }) 282 283 if err != nil { 284 return nil, errors.Wrap(err, "couldn't remove file") 285 } 286 287 // fs.Debugf(f, "Removed file with url `%s`", url) 288 289 return response, nil 290 } 291 292 func (f *Fs) getUploadNode() (response *GetUploadNodeResponse, err error) { 293 // fs.Debugf(f, "Requesting Upload node") 294 295 opts := rest.Opts{ 296 Method: "GET", 297 ContentType: "application/json", // 1Fichier API is bad 298 Path: "/upload/get_upload_server.cgi", 299 } 300 301 response = &GetUploadNodeResponse{} 302 err = f.pacer.Call(func() (bool, error) { 303 resp, err := f.rest.CallJSON(&opts, nil, response) 304 return shouldRetry(resp, err) 305 }) 306 if err != nil { 307 return nil, errors.Wrap(err, "didnt got an upload node") 308 } 309 310 // fs.Debugf(f, "Got Upload node") 311 312 return response, err 313 } 314 315 func (f *Fs) uploadFile(in io.Reader, size int64, fileName, folderID, uploadID, node string) (response *http.Response, err error) { 316 // fs.Debugf(f, "Uploading File `%s`", fileName) 317 318 if len(uploadID) > 10 || !isAlphaNumeric(uploadID) { 319 return nil, errors.New("Invalid UploadID") 320 } 321 322 opts := rest.Opts{ 323 Method: "POST", 324 Path: "/upload.cgi", 325 Parameters: map[string][]string{ 326 "id": {uploadID}, 327 }, 328 NoResponse: true, 329 Body: in, 330 ContentLength: &size, 331 MultipartContentName: "file[]", 332 MultipartFileName: fileName, 333 MultipartParams: map[string][]string{ 334 "did": {folderID}, 335 }, 336 } 337 338 if node != "" { 339 opts.RootURL = "https://" + node 340 } 341 342 err = f.pacer.CallNoRetry(func() (bool, error) { 343 resp, err := f.rest.CallJSON(&opts, nil, nil) 344 return shouldRetry(resp, err) 345 }) 346 347 if err != nil { 348 return nil, errors.Wrap(err, "couldn't upload file") 349 } 350 351 // fs.Debugf(f, "Uploaded File `%s`", fileName) 352 353 return response, err 354 } 355 356 func (f *Fs) endUpload(uploadID string, nodeurl string) (response *EndFileUploadResponse, err error) { 357 // fs.Debugf(f, "Ending File Upload `%s`", uploadID) 358 359 if len(uploadID) > 10 || !isAlphaNumeric(uploadID) { 360 return nil, errors.New("Invalid UploadID") 361 } 362 363 opts := rest.Opts{ 364 Method: "GET", 365 Path: "/end.pl", 366 RootURL: "https://" + nodeurl, 367 Parameters: map[string][]string{ 368 "xid": {uploadID}, 369 }, 370 ExtraHeaders: map[string]string{ 371 "JSON": "1", 372 }, 373 } 374 375 response = &EndFileUploadResponse{} 376 err = f.pacer.Call(func() (bool, error) { 377 resp, err := f.rest.CallJSON(&opts, nil, response) 378 return shouldRetry(resp, err) 379 }) 380 381 if err != nil { 382 return nil, errors.Wrap(err, "couldn't finish file upload") 383 } 384 385 return response, err 386 }