You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

564 lines
13 KiB

  1. package server
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "mime"
  9. "net/http"
  10. "os"
  11. "path/filepath"
  12. "strings"
  13. "github.com/aws/aws-sdk-go/aws"
  14. "github.com/aws/aws-sdk-go/aws/awserr"
  15. "github.com/aws/aws-sdk-go/aws/session"
  16. "github.com/aws/aws-sdk-go/service/s3"
  17. "github.com/aws/aws-sdk-go/service/s3/s3manager"
  18. "golang.org/x/net/context"
  19. "golang.org/x/oauth2"
  20. "golang.org/x/oauth2/google"
  21. "google.golang.org/api/drive/v3"
  22. "google.golang.org/api/googleapi"
  23. )
  24. type Storage interface {
  25. Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error)
  26. Head(token string, filename string) (contentType string, contentLength uint64, err error)
  27. Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error
  28. Delete(token string, filename string) error
  29. IsNotExist(err error) bool
  30. Type() string
  31. }
  32. type LocalStorage struct {
  33. Storage
  34. basedir string
  35. logger *log.Logger
  36. }
  37. func NewLocalStorage(basedir string, logger *log.Logger) (*LocalStorage, error) {
  38. return &LocalStorage{basedir: basedir, logger: logger}, nil
  39. }
  40. func (s *LocalStorage) Type() string {
  41. return "local"
  42. }
  43. func (s *LocalStorage) Head(token string, filename string) (contentType string, contentLength uint64, err error) {
  44. path := filepath.Join(s.basedir, token, filename)
  45. var fi os.FileInfo
  46. if fi, err = os.Lstat(path); err != nil {
  47. return
  48. }
  49. contentLength = uint64(fi.Size())
  50. contentType = mime.TypeByExtension(filepath.Ext(filename))
  51. return
  52. }
  53. func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) {
  54. path := filepath.Join(s.basedir, token, filename)
  55. // content type , content length
  56. if reader, err = os.Open(path); err != nil {
  57. return
  58. }
  59. var fi os.FileInfo
  60. if fi, err = os.Lstat(path); err != nil {
  61. return
  62. }
  63. contentLength = uint64(fi.Size())
  64. contentType = mime.TypeByExtension(filepath.Ext(filename))
  65. return
  66. }
  67. func (s *LocalStorage) Delete(token string, filename string) (err error) {
  68. metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename))
  69. os.Remove(metadata)
  70. path := filepath.Join(s.basedir, token, filename)
  71. err = os.Remove(path)
  72. return
  73. }
  74. func (s *LocalStorage) IsNotExist(err error) bool {
  75. if err == nil {
  76. return false
  77. }
  78. return os.IsNotExist(err)
  79. }
  80. func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
  81. var f io.WriteCloser
  82. var err error
  83. path := filepath.Join(s.basedir, token)
  84. if err = os.MkdirAll(path, 0700); err != nil && !os.IsExist(err) {
  85. return err
  86. }
  87. if f, err = os.OpenFile(filepath.Join(path, filename), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
  88. return err
  89. }
  90. defer f.Close()
  91. if _, err = io.Copy(f, reader); err != nil {
  92. return err
  93. }
  94. return nil
  95. }
  96. type S3Storage struct {
  97. Storage
  98. bucket string
  99. session *session.Session
  100. s3 *s3.S3
  101. logger *log.Logger
  102. noMultipart bool
  103. }
  104. func NewS3Storage(accessKey, secretKey, bucketName, region, endpoint string, logger *log.Logger, disableMultipart bool, forcePathStyle bool) (*S3Storage, error) {
  105. sess := getAwsSession(accessKey, secretKey, region, endpoint, forcePathStyle)
  106. return &S3Storage{bucket: bucketName, s3: s3.New(sess), session: sess, logger: logger, noMultipart: disableMultipart}, nil
  107. }
  108. func (s *S3Storage) Type() string {
  109. return "s3"
  110. }
  111. func (s *S3Storage) Head(token string, filename string) (contentType string, contentLength uint64, err error) {
  112. key := fmt.Sprintf("%s/%s", token, filename)
  113. headRequest := &s3.HeadObjectInput{
  114. Bucket: aws.String(s.bucket),
  115. Key: aws.String(key),
  116. }
  117. // content type , content length
  118. response, err := s.s3.HeadObject(headRequest)
  119. if err != nil {
  120. return
  121. }
  122. if response.ContentType != nil {
  123. contentType = *response.ContentType
  124. }
  125. if response.ContentLength != nil {
  126. contentLength = uint64(*response.ContentLength)
  127. }
  128. return
  129. }
  130. func (s *S3Storage) IsNotExist(err error) bool {
  131. if err == nil {
  132. return false
  133. }
  134. if aerr, ok := err.(awserr.Error); ok {
  135. switch aerr.Code() {
  136. case s3.ErrCodeNoSuchKey:
  137. return true
  138. }
  139. }
  140. return false
  141. }
  142. func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) {
  143. key := fmt.Sprintf("%s/%s", token, filename)
  144. getRequest := &s3.GetObjectInput{
  145. Bucket: aws.String(s.bucket),
  146. Key: aws.String(key),
  147. }
  148. response, err := s.s3.GetObject(getRequest)
  149. if err != nil {
  150. return
  151. }
  152. if response.ContentType != nil {
  153. contentType = *response.ContentType
  154. }
  155. if response.ContentLength != nil {
  156. contentLength = uint64(*response.ContentLength)
  157. }
  158. reader = response.Body
  159. return
  160. }
  161. func (s *S3Storage) Delete(token string, filename string) (err error) {
  162. metadata := fmt.Sprintf("%s/%s.metadata", token, filename)
  163. deleteRequest := &s3.DeleteObjectInput{
  164. Bucket: aws.String(s.bucket),
  165. Key: aws.String(metadata),
  166. }
  167. _, err = s.s3.DeleteObject(deleteRequest)
  168. if err != nil {
  169. return
  170. }
  171. key := fmt.Sprintf("%s/%s", token, filename)
  172. deleteRequest = &s3.DeleteObjectInput{
  173. Bucket: aws.String(s.bucket),
  174. Key: aws.String(key),
  175. }
  176. _, err = s.s3.DeleteObject(deleteRequest)
  177. return
  178. }
  179. func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
  180. key := fmt.Sprintf("%s/%s", token, filename)
  181. s.logger.Printf("Uploading file %s to S3 Bucket", filename)
  182. var concurrency int
  183. if !s.noMultipart {
  184. concurrency = 20
  185. } else {
  186. concurrency = 1
  187. }
  188. // Create an uploader with the session and custom options
  189. uploader := s3manager.NewUploader(s.session, func(u *s3manager.Uploader) {
  190. u.Concurrency = concurrency // default is 5
  191. u.LeavePartsOnError = false
  192. })
  193. _, err = uploader.Upload(&s3manager.UploadInput{
  194. Bucket: aws.String(s.bucket),
  195. Key: aws.String(key),
  196. Body: reader,
  197. })
  198. return
  199. }
  200. type GDrive struct {
  201. service *drive.Service
  202. rootId string
  203. basedir string
  204. localConfigPath string
  205. chunkSize int
  206. logger *log.Logger
  207. }
  208. func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) {
  209. b, err := ioutil.ReadFile(clientJsonFilepath)
  210. if err != nil {
  211. return nil, err
  212. }
  213. // If modifying these scopes, delete your previously saved client_secret.json.
  214. config, err := google.ConfigFromJSON(b, drive.DriveScope, drive.DriveMetadataScope)
  215. if err != nil {
  216. return nil, err
  217. }
  218. srv, err := drive.New(getGDriveClient(config, localConfigPath, logger))
  219. if err != nil {
  220. return nil, err
  221. }
  222. chunkSize = chunkSize * 1024 * 1024
  223. storage := &GDrive{service: srv, basedir: basedir, rootId: "", localConfigPath: localConfigPath, chunkSize: chunkSize, logger: logger}
  224. err = storage.setupRoot()
  225. if err != nil {
  226. return nil, err
  227. }
  228. return storage, nil
  229. }
  230. const GDriveRootConfigFile = "root_id.conf"
  231. const GDriveTokenJsonFile = "token.json"
  232. const GDriveDirectoryMimeType = "application/vnd.google-apps.folder"
  233. func (s *GDrive) setupRoot() error {
  234. rootFileConfig := filepath.Join(s.localConfigPath, GDriveRootConfigFile)
  235. rootId, err := ioutil.ReadFile(rootFileConfig)
  236. if err != nil && !os.IsNotExist(err) {
  237. return err
  238. }
  239. if string(rootId) != "" {
  240. s.rootId = string(rootId)
  241. return nil
  242. }
  243. dir := &drive.File{
  244. Name: s.basedir,
  245. MimeType: GDriveDirectoryMimeType,
  246. }
  247. di, err := s.service.Files.Create(dir).Fields("id").Do()
  248. if err != nil {
  249. return err
  250. }
  251. s.rootId = di.Id
  252. err = ioutil.WriteFile(rootFileConfig, []byte(s.rootId), os.FileMode(0600))
  253. if err != nil {
  254. return err
  255. }
  256. return nil
  257. }
  258. func (s *GDrive) hasChecksum(f *drive.File) bool {
  259. return f.Md5Checksum != ""
  260. }
  261. func (s *GDrive) list(nextPageToken string, q string) (*drive.FileList, error) {
  262. return s.service.Files.List().Fields("nextPageToken, files(id, name, mimeType)").Q(q).PageToken(nextPageToken).Do()
  263. }
  264. func (s *GDrive) findId(filename string, token string) (string, error) {
  265. filename = strings.Replace(filename, `'`, `\'`, -1)
  266. filename = strings.Replace(filename, `"`, `\"`, -1)
  267. fileId, tokenId, nextPageToken := "", "", ""
  268. q := fmt.Sprintf("'%s' in parents and name='%s' and mimeType='%s' and trashed=false", s.rootId, token, GDriveDirectoryMimeType)
  269. l, err := s.list(nextPageToken, q)
  270. if err != nil {
  271. return "", err
  272. }
  273. for 0 < len(l.Files) {
  274. for _, fi := range l.Files {
  275. tokenId = fi.Id
  276. break
  277. }
  278. if l.NextPageToken == "" {
  279. break
  280. }
  281. l, err = s.list(l.NextPageToken, q)
  282. }
  283. if filename == "" {
  284. return tokenId, nil
  285. } else if tokenId == "" {
  286. return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
  287. }
  288. q = fmt.Sprintf("'%s' in parents and name='%s' and mimeType!='%s' and trashed=false", tokenId, filename, GDriveDirectoryMimeType)
  289. l, err = s.list(nextPageToken, q)
  290. if err != nil {
  291. return "", err
  292. }
  293. for 0 < len(l.Files) {
  294. for _, fi := range l.Files {
  295. fileId = fi.Id
  296. break
  297. }
  298. if l.NextPageToken == "" {
  299. break
  300. }
  301. l, err = s.list(l.NextPageToken, q)
  302. }
  303. if fileId == "" {
  304. return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
  305. }
  306. return fileId, nil
  307. }
  308. func (s *GDrive) Type() string {
  309. return "gdrive"
  310. }
  311. func (s *GDrive) Head(token string, filename string) (contentType string, contentLength uint64, err error) {
  312. var fileId string
  313. fileId, err = s.findId(filename, token)
  314. if err != nil {
  315. return
  316. }
  317. var fi *drive.File
  318. if fi, err = s.service.Files.Get(fileId).Fields("mimeType", "size").Do(); err != nil {
  319. return
  320. }
  321. contentLength = uint64(fi.Size)
  322. contentType = fi.MimeType
  323. return
  324. }
  325. func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) {
  326. var fileId string
  327. fileId, err = s.findId(filename, token)
  328. if err != nil {
  329. return
  330. }
  331. var fi *drive.File
  332. fi, err = s.service.Files.Get(fileId).Fields("mimeType", "size", "md5Checksum").Do()
  333. if !s.hasChecksum(fi) {
  334. err = fmt.Errorf("Cannot find file %s/%s", token, filename)
  335. return
  336. }
  337. contentLength = uint64(fi.Size)
  338. contentType = fi.MimeType
  339. ctx := context.Background()
  340. var res *http.Response
  341. res, err = s.service.Files.Get(fileId).Context(ctx).Download()
  342. if err != nil {
  343. return
  344. }
  345. reader = res.Body
  346. return
  347. }
  348. func (s *GDrive) Delete(token string, filename string) (err error) {
  349. metadata, _ := s.findId(fmt.Sprintf("%s.metadata", filename), token)
  350. s.service.Files.Delete(metadata).Do()
  351. var fileId string
  352. fileId, err = s.findId(filename, token)
  353. if err != nil {
  354. return
  355. }
  356. err = s.service.Files.Delete(fileId).Do()
  357. return
  358. }
  359. func (s *GDrive) IsNotExist(err error) bool {
  360. if err != nil {
  361. if e, ok := err.(*googleapi.Error); ok {
  362. return e.Code == http.StatusNotFound
  363. }
  364. }
  365. return false
  366. }
  367. func (s *GDrive) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
  368. dirId, err := s.findId("", token)
  369. if err != nil {
  370. return err
  371. }
  372. if dirId == "" {
  373. dir := &drive.File{
  374. Name: token,
  375. Parents: []string{s.rootId},
  376. MimeType: GDriveDirectoryMimeType,
  377. }
  378. di, err := s.service.Files.Create(dir).Fields("id").Do()
  379. if err != nil {
  380. return err
  381. }
  382. dirId = di.Id
  383. }
  384. // Instantiate empty drive file
  385. dst := &drive.File{
  386. Name: filename,
  387. Parents: []string{dirId},
  388. MimeType: contentType,
  389. }
  390. ctx := context.Background()
  391. _, err = s.service.Files.Create(dst).Context(ctx).Media(reader, googleapi.ChunkSize(s.chunkSize)).Do()
  392. if err != nil {
  393. return err
  394. }
  395. return nil
  396. }
  397. // Retrieve a token, saves the token, then returns the generated client.
  398. func getGDriveClient(config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client {
  399. tokenFile := filepath.Join(localConfigPath, GDriveTokenJsonFile)
  400. tok, err := gDriveTokenFromFile(tokenFile)
  401. if err != nil {
  402. tok = getGDriveTokenFromWeb(config, logger)
  403. saveGDriveToken(tokenFile, tok, logger)
  404. }
  405. return config.Client(context.Background(), tok)
  406. }
  407. // Request a token from the web, then returns the retrieved token.
  408. func getGDriveTokenFromWeb(config *oauth2.Config, logger *log.Logger) *oauth2.Token {
  409. authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
  410. fmt.Printf("Go to the following link in your browser then type the "+
  411. "authorization code: \n%v\n", authURL)
  412. var authCode string
  413. if _, err := fmt.Scan(&authCode); err != nil {
  414. logger.Fatalf("Unable to read authorization code %v", err)
  415. }
  416. tok, err := config.Exchange(context.TODO(), authCode)
  417. if err != nil {
  418. logger.Fatalf("Unable to retrieve token from web %v", err)
  419. }
  420. return tok
  421. }
  422. // Retrieves a token from a local file.
  423. func gDriveTokenFromFile(file string) (*oauth2.Token, error) {
  424. f, err := os.Open(file)
  425. defer f.Close()
  426. if err != nil {
  427. return nil, err
  428. }
  429. tok := &oauth2.Token{}
  430. err = json.NewDecoder(f).Decode(tok)
  431. return tok, err
  432. }
  433. // Saves a token to a file path.
  434. func saveGDriveToken(path string, token *oauth2.Token, logger *log.Logger) {
  435. logger.Printf("Saving credential file to: %s\n", path)
  436. f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
  437. defer f.Close()
  438. if err != nil {
  439. logger.Fatalf("Unable to cache oauth token: %v", err)
  440. }
  441. json.NewEncoder(f).Encode(token)
  442. }