repo_gin.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package repo
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. "github.com/G-Node/gogs/internal/conf"
  13. "github.com/G-Node/gogs/internal/context"
  14. "github.com/G-Node/gogs/internal/tool"
  15. "github.com/G-Node/libgin/libgin"
  16. "github.com/gogs/git-module"
  17. log "gopkg.in/clog.v1"
  18. "gopkg.in/yaml.v2"
  19. )
  20. func serveAnnexedData(ctx *context.Context, name string, buf []byte) error {
  21. keyparts := strings.Split(strings.TrimSpace(string(buf)), "/")
  22. key := keyparts[len(keyparts)-1]
  23. contentPath, err := git.NewCommand("annex", "contentlocation", key).RunInDir(ctx.Repo.Repository.RepoPath())
  24. if err != nil {
  25. log.Error(2, "Failed to find content location for file %q with key %q", name, key)
  26. return err
  27. }
  28. // always trim space from output for git command
  29. contentPath = bytes.TrimSpace(contentPath)
  30. return serveAnnexedKey(ctx, name, string(contentPath))
  31. }
  32. func serveAnnexedKey(ctx *context.Context, name string, contentPath string) error {
  33. fullContentPath := filepath.Join(ctx.Repo.Repository.RepoPath(), contentPath)
  34. annexfp, err := os.Open(fullContentPath)
  35. if err != nil {
  36. log.Error(2, "Failed to open annex file at %q: %s", fullContentPath, err.Error())
  37. return err
  38. }
  39. defer annexfp.Close()
  40. annexReader := bufio.NewReader(annexfp)
  41. info, err := annexfp.Stat()
  42. if err != nil {
  43. log.Error(2, "Failed to stat file at %q: %s", fullContentPath, err.Error())
  44. return err
  45. }
  46. buf, _ := annexReader.Peek(1024)
  47. ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", info.Size()))
  48. if !tool.IsTextFile(buf) {
  49. if !tool.IsImageFile(buf) {
  50. ctx.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"")
  51. ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
  52. }
  53. } else if !conf.Repository.EnableRawFileRenderMode || !ctx.QueryBool("render") {
  54. ctx.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8")
  55. }
  56. log.Trace("Serving annex content for %q: %q", name, contentPath)
  57. if ctx.Req.Method == http.MethodHead {
  58. // Skip content copy when request method is HEAD
  59. log.Trace("Returning header: %+v", ctx.Resp.Header())
  60. return nil
  61. }
  62. _, err = io.Copy(ctx.Resp, annexReader)
  63. return err
  64. }
  65. func readDataciteFile(c *context.Context) {
  66. log.Trace("Reading datacite.yml file")
  67. entry, err := c.Repo.Commit.Blob("/datacite.yml")
  68. if err != nil || entry == nil {
  69. log.Error(2, "datacite.yml blob could not be retrieved: %v", err)
  70. c.Data["HasDataCite"] = false
  71. return
  72. }
  73. buf, err := entry.Bytes()
  74. if err != nil {
  75. log.Error(2, "datacite.yml data could not be read: %v", err)
  76. c.Data["HasDataCite"] = false
  77. return
  78. }
  79. doiInfo := libgin.DOIRegInfo{}
  80. err = yaml.Unmarshal(buf, &doiInfo)
  81. if err != nil {
  82. log.Error(2, "datacite.yml data could not be unmarshalled: %v", err)
  83. c.Data["HasDataCite"] = false
  84. return
  85. }
  86. c.Data["DOIInfo"] = &doiInfo
  87. }
  88. // resolveAnnexedContent takes a buffer with the contents of a git-annex
  89. // pointer file and an io.Reader for the underlying file and returns the
  90. // corresponding buffer and a bufio.Reader for the underlying content file.
  91. // The returned byte slice and bufio.Reader can be used to replace the buffer
  92. // and io.Reader sent in through the caller so that any existing code can use
  93. // the two variables without modifications.
  94. // Any errors that occur during processing are stored in the provided context.
  95. // The FileSize of the annexed content is also saved in the context (c.Data["FileSize"]).
  96. func resolveAnnexedContent(c *context.Context, buf []byte) ([]byte, error) {
  97. if !tool.IsAnnexedFile(buf) {
  98. // not an annex pointer file; return as is
  99. return buf, nil
  100. }
  101. log.Trace("Annexed file requested: Resolving content for %q", bytes.TrimSpace(buf))
  102. keyparts := strings.Split(strings.TrimSpace(string(buf)), "/")
  103. key := keyparts[len(keyparts)-1]
  104. contentPath, err := git.NewCommand("annex", "contentlocation", key).RunInDir(c.Repo.Repository.RepoPath())
  105. if err != nil {
  106. log.Error(2, "Failed to find content location for key %q", key)
  107. c.Data["IsAnnexedFile"] = true
  108. return buf, err
  109. }
  110. // always trim space from output for git command
  111. contentPath = bytes.TrimSpace(contentPath)
  112. afp, err := os.Open(filepath.Join(c.Repo.Repository.RepoPath(), string(contentPath)))
  113. if err != nil {
  114. log.Trace("Could not open annex file: %v", err)
  115. c.Data["IsAnnexedFile"] = true
  116. return buf, err
  117. }
  118. info, err := afp.Stat()
  119. if err != nil {
  120. log.Trace("Could not stat annex file: %v", err)
  121. c.Data["IsAnnexedFile"] = true
  122. return buf, err
  123. }
  124. annexDataReader := bufio.NewReader(afp)
  125. annexBuf := make([]byte, 1024)
  126. n, _ := annexDataReader.Read(annexBuf)
  127. annexBuf = annexBuf[:n]
  128. c.Data["FileSize"] = info.Size()
  129. log.Trace("Annexed file size: %d B", info.Size())
  130. return annexBuf, nil
  131. }
  132. func GitConfig(c *context.Context) {
  133. log.Trace("RepoPath: %+v", c.Repo.Repository.RepoPath())
  134. configFilePath := path.Join(c.Repo.Repository.RepoPath(), "config")
  135. log.Trace("Serving file %q", configFilePath)
  136. if _, err := os.Stat(configFilePath); err != nil {
  137. c.ServerError("GitConfig", err)
  138. return
  139. }
  140. c.ServeFileContent(configFilePath, "config")
  141. }
  142. func AnnexGetKey(c *context.Context) {
  143. filename := c.Params(":keyfile")
  144. key := c.Params(":key")
  145. contentPath := filepath.Join("annex/objects", c.Params(":hashdira"), c.Params(":hashdirb"), key, filename)
  146. log.Trace("Git annex requested key %q: %q", key, contentPath)
  147. err := serveAnnexedKey(c, filename, contentPath)
  148. if err != nil {
  149. c.ServerError("AnnexGetKey", err)
  150. }
  151. }