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.

download.go 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package repo
  6. import (
  7. "fmt"
  8. "io"
  9. "path"
  10. "strings"
  11. "code.gitea.io/gitea/modules/base"
  12. "code.gitea.io/gitea/modules/charset"
  13. "code.gitea.io/gitea/modules/context"
  14. "code.gitea.io/gitea/modules/git"
  15. "code.gitea.io/gitea/modules/lfs"
  16. "code.gitea.io/gitea/modules/log"
  17. )
  18. // ServeData download file from io.Reader
  19. func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error {
  20. buf := make([]byte, 1024)
  21. n, err := reader.Read(buf)
  22. if err != nil && err != io.EOF {
  23. return err
  24. }
  25. if n >= 0 {
  26. buf = buf[:n]
  27. }
  28. ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400")
  29. if size >= 0 {
  30. ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size))
  31. } else {
  32. log.Error("ServeData called to serve data: %s with size < 0: %d", name, size)
  33. }
  34. name = path.Base(name)
  35. // Google Chrome dislike commas in filenames, so let's change it to a space
  36. name = strings.ReplaceAll(name, ",", " ")
  37. if base.IsTextFile(buf) || ctx.QueryBool("render") {
  38. cs, err := charset.DetectEncoding(buf)
  39. if err != nil {
  40. log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err)
  41. cs = "utf-8"
  42. }
  43. ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs))
  44. } else if base.IsImageFile(buf) || base.IsPDFFile(buf) {
  45. ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name))
  46. ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
  47. if base.IsSVGImageFile(buf) {
  48. ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
  49. ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
  50. ctx.Resp.Header().Set("Content-Type", base.SVGMimeType)
  51. }
  52. } else {
  53. ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name))
  54. ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
  55. }
  56. _, err = ctx.Resp.Write(buf)
  57. if err != nil {
  58. return err
  59. }
  60. _, err = io.Copy(ctx.Resp, reader)
  61. return err
  62. }
  63. // ServeBlob download a git.Blob
  64. func ServeBlob(ctx *context.Context, blob *git.Blob) error {
  65. dataRc, err := blob.DataAsync()
  66. if err != nil {
  67. return err
  68. }
  69. defer func() {
  70. if err = dataRc.Close(); err != nil {
  71. log.Error("ServeBlob: Close: %v", err)
  72. }
  73. }()
  74. return ServeData(ctx, ctx.Repo.TreePath, blob.Size(), dataRc)
  75. }
  76. // ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
  77. func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error {
  78. dataRc, err := blob.DataAsync()
  79. if err != nil {
  80. return err
  81. }
  82. defer func() {
  83. if err = dataRc.Close(); err != nil {
  84. log.Error("ServeBlobOrLFS: Close: %v", err)
  85. }
  86. }()
  87. pointer, _ := lfs.ReadPointer(dataRc)
  88. if pointer.IsValid() {
  89. meta, _ := ctx.Repo.Repository.GetLFSMetaObjectByOid(pointer.Oid)
  90. if meta == nil {
  91. return ServeBlob(ctx, blob)
  92. }
  93. lfsDataRc, err := lfs.ReadMetaObject(meta.Pointer)
  94. if err != nil {
  95. return err
  96. }
  97. defer func() {
  98. if err = lfsDataRc.Close(); err != nil {
  99. log.Error("ServeBlobOrLFS: Close: %v", err)
  100. }
  101. }()
  102. return ServeData(ctx, ctx.Repo.TreePath, meta.Size, lfsDataRc)
  103. }
  104. return ServeBlob(ctx, blob)
  105. }
  106. // SingleDownload download a file by repos path
  107. func SingleDownload(ctx *context.Context) {
  108. blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
  109. if err != nil {
  110. if git.IsErrNotExist(err) {
  111. ctx.NotFound("GetBlobByPath", nil)
  112. } else {
  113. ctx.ServerError("GetBlobByPath", err)
  114. }
  115. return
  116. }
  117. if err = ServeBlob(ctx, blob); err != nil {
  118. ctx.ServerError("ServeBlob", err)
  119. }
  120. }
  121. // SingleDownloadOrLFS download a file by repos path redirecting to LFS if necessary
  122. func SingleDownloadOrLFS(ctx *context.Context) {
  123. blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath)
  124. if err != nil {
  125. if git.IsErrNotExist(err) {
  126. ctx.NotFound("GetBlobByPath", nil)
  127. } else {
  128. ctx.ServerError("GetBlobByPath", err)
  129. }
  130. return
  131. }
  132. if err = ServeBlobOrLFS(ctx, blob); err != nil {
  133. ctx.ServerError("ServeBlobOrLFS", err)
  134. }
  135. }
  136. // DownloadByID download a file by sha1 ID
  137. func DownloadByID(ctx *context.Context) {
  138. blob, err := ctx.Repo.GitRepo.GetBlob(ctx.Params("sha"))
  139. if err != nil {
  140. if git.IsErrNotExist(err) {
  141. ctx.NotFound("GetBlob", nil)
  142. } else {
  143. ctx.ServerError("GetBlob", err)
  144. }
  145. return
  146. }
  147. if err = ServeBlob(ctx, blob); err != nil {
  148. ctx.ServerError("ServeBlob", err)
  149. }
  150. }
  151. // DownloadByIDOrLFS download a file by sha1 ID taking account of LFS
  152. func DownloadByIDOrLFS(ctx *context.Context) {
  153. blob, err := ctx.Repo.GitRepo.GetBlob(ctx.Params("sha"))
  154. if err != nil {
  155. if git.IsErrNotExist(err) {
  156. ctx.NotFound("GetBlob", nil)
  157. } else {
  158. ctx.ServerError("GetBlob", err)
  159. }
  160. return
  161. }
  162. if err = ServeBlobOrLFS(ctx, blob); err != nil {
  163. ctx.ServerError("ServeBlob", err)
  164. }
  165. }