repo_gin.go 6.1 KB

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