2019-03-30 04:31:31 +04:00
// Yandex Disk File Pusher
// Copyright (c) 2019 Vladimir "fat0troll" Hodakov
package yandexv1
import (
"encoding/json"
2019-03-30 07:00:15 +04:00
"github.com/schollz/progressbar/v2"
2019-03-30 04:31:31 +04:00
"net/http"
"net/url"
"os"
2019-03-30 07:00:15 +04:00
"path/filepath"
2019-03-30 04:31:31 +04:00
"strconv"
"strings"
)
func sendCode ( code int ) {
baseURL := "https://oauth.yandex.ru/token"
client := http . Client { }
form := url . Values { }
form . Set ( "grant_type" , "authorization_code" )
form . Set ( "code" , strconv . Itoa ( code ) )
form . Set ( "device_id" , c . Config . DeviceID )
2019-03-30 07:06:15 +04:00
form . Set ( "device_name" , DefaultDeviceName )
form . Set ( "client_id" , YandexAppID )
form . Set ( "client_secret" , YandexAppPw )
2019-03-30 04:31:31 +04:00
req , _ := http . NewRequest ( "POST" , baseURL , strings . NewReader ( form . Encode ( ) ) )
req . Header . Add ( "Content-Type" , "application/x-www-form-urlencoded" )
req . Header . Add ( "Content-Length" , strconv . Itoa ( len ( form . Encode ( ) ) ) )
resp , err := client . Do ( req )
if err != nil {
dlog . Fatal ( ) . Err ( err ) . Msg ( "Failed to send request" )
}
defer resp . Body . Close ( )
if resp . StatusCode == http . StatusOK {
err = json . NewDecoder ( resp . Body ) . Decode ( & c . Config . Token )
if err != nil {
dlog . Error ( ) . Err ( err ) . Msg ( "Failed to decode response" )
}
c . SaveConfig ( )
dlog . Info ( ) . Msg ( "You've authorized successfully. Now you can use this app to upload files to your Yandex.Disk" )
dlog . Info ( ) . Msg ( "See -h for details" )
} else {
errorData := TokenError { }
err = json . NewDecoder ( resp . Body ) . Decode ( & errorData )
if err != nil {
dlog . Error ( ) . Err ( err ) . Msg ( "Failed to decode response" )
}
2019-03-30 07:00:15 +04:00
dlog . Error ( ) . Str ( "error" , errorData . Error ) . Str ( "description" , errorData . ErrorDescription ) . Msg ( "Got error from Yandex, not authorized. Please retry authorization" )
2019-03-30 04:31:31 +04:00
authorize ( )
}
os . Exit ( 0 )
}
2019-03-30 04:44:51 +04:00
2019-03-30 07:00:15 +04:00
func uploadFile ( uploadPath string , filePath string , overwriteFile bool ) {
uploadRequestURL := "https://cloud-api.yandex.net/v1/disk/resources/upload"
// Checking file existence before requesting
normalizedFilePath , _ := filepath . Abs ( filePath )
fileInfo , err := os . Stat ( normalizedFilePath )
if err != nil {
if os . IsNotExist ( err ) {
dlog . Fatal ( ) . Err ( err ) . Msg ( "File for uploading not found" )
} else {
dlog . Fatal ( ) . Err ( err ) . Msg ( "Failed to stat uploading file" )
}
}
2019-03-30 07:06:15 +04:00
if fileInfo . Size ( ) > ( MaxUploadSize - 1 ) {
2019-03-30 07:00:15 +04:00
dlog . Fatal ( ) . Msg ( "Requested file is too big" )
}
if ! fileInfo . Mode ( ) . IsRegular ( ) {
dlog . Fatal ( ) . Msg ( "Only regular files uploading is supported right now" )
}
client := http . Client { }
uploadInfo := UploadInfo { }
// The first request will get from Yandex upload URL
req , _ := http . NewRequest ( "GET" , uploadRequestURL , nil )
req . Header . Add ( "Content-Type" , "application/json" )
req . Header . Add ( "Authorization" , "OAuth " + c . Config . Token . AccessToken )
query := url . Values { }
query . Add ( "path" , "disk:/" + uploadPath + "/" + fileInfo . Name ( ) )
query . Add ( "overwrite" , strconv . FormatBool ( overwriteFile ) )
req . URL . RawQuery = query . Encode ( )
resp , err := client . Do ( req )
if err != nil {
dlog . Fatal ( ) . Err ( err ) . Msg ( "Failed to send request" )
}
defer resp . Body . Close ( )
if resp . StatusCode == http . StatusOK {
err = json . NewDecoder ( resp . Body ) . Decode ( & uploadInfo )
if err != nil {
dlog . Error ( ) . Err ( err ) . Msg ( "Failed to decode response" )
}
} else {
errorData := UploadError { }
err = json . NewDecoder ( resp . Body ) . Decode ( & errorData )
if err != nil {
dlog . Error ( ) . Err ( err ) . Msg ( "Failed to decode response" )
}
dlog . Info ( ) . Str ( "error" , errorData . Error ) . Str ( "description" , errorData . Description ) . Msg ( "Failed to upload file" )
os . Exit ( 1 )
}
file , _ := os . Open ( normalizedFilePath )
bar := progressbar . NewOptions (
int ( fileInfo . Size ( ) ) ,
progressbar . OptionSetBytes ( int ( fileInfo . Size ( ) ) ) ,
progressbar . OptionSetRenderBlankState ( true ) ,
2019-04-02 00:42:25 +04:00
progressbar . OptionClearOnFinish ( ) ,
2019-03-30 07:00:15 +04:00
)
progressReader := & progressReader {
r : file ,
progressbar : bar ,
}
if uploadInfo . URL == "" {
2019-12-30 04:39:34 +04:00
dlog . Fatal ( ) . Msg ( "Got empty upload URL. Report a bug at https://source.hodakov.me/fat0troll/yapusher/issues because this situation is impossible." )
2019-03-30 07:00:15 +04:00
}
uploadReq , _ := http . NewRequest ( "PUT" , uploadInfo . URL , progressReader )
uploadResp , err := client . Do ( uploadReq )
if err != nil {
dlog . Fatal ( ) . Err ( err ) . Msg ( "Failed to send upload request" )
}
defer uploadResp . Body . Close ( )
switch uploadResp . StatusCode {
case http . StatusCreated :
dlog . Info ( ) . Msg ( "File uploaded successfully" )
case http . StatusAccepted :
dlog . Info ( ) . Msg ( "File uploaded successfully, but it will take time for Yandex.Disk to handle it internally. Be patient and don't try to upload single file many times" )
case http . StatusRequestEntityTooLarge :
2019-12-30 04:39:34 +04:00
dlog . Fatal ( ) . Msg ( "File upload is too large. Report a bug at https://source.hodakov.me/fat0troll/yapusher/issues because this situation should be handled before upload attempt." )
2019-03-30 07:00:15 +04:00
case http . StatusInsufficientStorage :
dlog . Fatal ( ) . Msg ( "There is no space left on your Yandex.Disk." )
default :
dlog . Fatal ( ) . Msg ( "Failed to upload file (error on Yandex's side). Try again later." )
}
os . Exit ( 0 )
}