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.

217 lines
5.9 KiB

  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repofiles
  5. import (
  6. "fmt"
  7. "net/url"
  8. "path"
  9. "strings"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/git"
  12. api "code.gitea.io/gitea/modules/structs"
  13. )
  14. // ContentType repo content type
  15. type ContentType string
  16. // The string representations of different content types
  17. const (
  18. // ContentTypeRegular regular content type (file)
  19. ContentTypeRegular ContentType = "file"
  20. // ContentTypeDir dir content type (dir)
  21. ContentTypeDir ContentType = "dir"
  22. // ContentLink link content type (symlink)
  23. ContentTypeLink ContentType = "symlink"
  24. // ContentTag submodule content type (submodule)
  25. ContentTypeSubmodule ContentType = "submodule"
  26. )
  27. // String gets the string of ContentType
  28. func (ct *ContentType) String() string {
  29. return string(*ct)
  30. }
  31. // GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree
  32. // directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
  33. func GetContentsOrList(repo *models.Repository, treePath, ref string) (interface{}, error) {
  34. if repo.IsEmpty {
  35. return make([]interface{}, 0), nil
  36. }
  37. if ref == "" {
  38. ref = repo.DefaultBranch
  39. }
  40. origRef := ref
  41. // Check that the path given in opts.treePath is valid (not a git path)
  42. cleanTreePath := CleanUploadFileName(treePath)
  43. if cleanTreePath == "" && treePath != "" {
  44. return nil, models.ErrFilenameInvalid{
  45. Path: treePath,
  46. }
  47. }
  48. treePath = cleanTreePath
  49. gitRepo, err := git.OpenRepository(repo.RepoPath())
  50. if err != nil {
  51. return nil, err
  52. }
  53. defer gitRepo.Close()
  54. // Get the commit object for the ref
  55. commit, err := gitRepo.GetCommit(ref)
  56. if err != nil {
  57. return nil, err
  58. }
  59. entry, err := commit.GetTreeEntryByPath(treePath)
  60. if err != nil {
  61. return nil, err
  62. }
  63. if entry.Type() != "tree" {
  64. return GetContents(repo, treePath, origRef, false)
  65. }
  66. // We are in a directory, so we return a list of FileContentResponse objects
  67. var fileList []*api.ContentsResponse
  68. gitTree, err := commit.SubTree(treePath)
  69. if err != nil {
  70. return nil, err
  71. }
  72. entries, err := gitTree.ListEntries()
  73. if err != nil {
  74. return nil, err
  75. }
  76. for _, e := range entries {
  77. subTreePath := path.Join(treePath, e.Name())
  78. fileContentResponse, err := GetContents(repo, subTreePath, origRef, true)
  79. if err != nil {
  80. return nil, err
  81. }
  82. fileList = append(fileList, fileContentResponse)
  83. }
  84. return fileList, nil
  85. }
  86. // GetContents gets the meta data on a file's contents. Ref can be a branch, commit or tag
  87. func GetContents(repo *models.Repository, treePath, ref string, forList bool) (*api.ContentsResponse, error) {
  88. if ref == "" {
  89. ref = repo.DefaultBranch
  90. }
  91. origRef := ref
  92. // Check that the path given in opts.treePath is valid (not a git path)
  93. cleanTreePath := CleanUploadFileName(treePath)
  94. if cleanTreePath == "" && treePath != "" {
  95. return nil, models.ErrFilenameInvalid{
  96. Path: treePath,
  97. }
  98. }
  99. treePath = cleanTreePath
  100. gitRepo, err := git.OpenRepository(repo.RepoPath())
  101. if err != nil {
  102. return nil, err
  103. }
  104. defer gitRepo.Close()
  105. // Get the commit object for the ref
  106. commit, err := gitRepo.GetCommit(ref)
  107. if err != nil {
  108. return nil, err
  109. }
  110. commitID := commit.ID.String()
  111. if len(ref) >= 4 && strings.HasPrefix(commitID, ref) {
  112. ref = commit.ID.String()
  113. }
  114. entry, err := commit.GetTreeEntryByPath(treePath)
  115. if err != nil {
  116. return nil, err
  117. }
  118. refType := gitRepo.GetRefType(ref)
  119. if refType == "invalid" {
  120. return nil, fmt.Errorf("no commit found for the ref [ref: %s]", ref)
  121. }
  122. selfURL, err := url.Parse(fmt.Sprintf("%s/contents/%s?ref=%s", repo.APIURL(), treePath, origRef))
  123. if err != nil {
  124. return nil, err
  125. }
  126. selfURLString := selfURL.String()
  127. // All content types have these fields in populated
  128. contentsResponse := &api.ContentsResponse{
  129. Name: entry.Name(),
  130. Path: treePath,
  131. SHA: entry.ID.String(),
  132. Size: entry.Size(),
  133. URL: &selfURLString,
  134. Links: &api.FileLinksResponse{
  135. Self: &selfURLString,
  136. },
  137. }
  138. // Now populate the rest of the ContentsResponse based on entry type
  139. if entry.IsRegular() {
  140. contentsResponse.Type = string(ContentTypeRegular)
  141. if blobResponse, err := GetBlobBySHA(repo, entry.ID.String()); err != nil {
  142. return nil, err
  143. } else if !forList {
  144. // We don't show the content if we are getting a list of FileContentResponses
  145. contentsResponse.Encoding = &blobResponse.Encoding
  146. contentsResponse.Content = &blobResponse.Content
  147. }
  148. } else if entry.IsDir() {
  149. contentsResponse.Type = string(ContentTypeDir)
  150. } else if entry.IsLink() {
  151. contentsResponse.Type = string(ContentTypeLink)
  152. // The target of a symlink file is the content of the file
  153. targetFromContent, err := entry.Blob().GetBlobContent()
  154. if err != nil {
  155. return nil, err
  156. }
  157. contentsResponse.Target = &targetFromContent
  158. } else if entry.IsSubModule() {
  159. contentsResponse.Type = string(ContentTypeSubmodule)
  160. submodule, err := commit.GetSubModule(treePath)
  161. if err != nil {
  162. return nil, err
  163. }
  164. contentsResponse.SubmoduleGitURL = &submodule.URL
  165. }
  166. // Handle links
  167. if entry.IsRegular() || entry.IsLink() {
  168. downloadURL, err := url.Parse(fmt.Sprintf("%s/raw/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath))
  169. if err != nil {
  170. return nil, err
  171. }
  172. downloadURLString := downloadURL.String()
  173. contentsResponse.DownloadURL = &downloadURLString
  174. }
  175. if !entry.IsSubModule() {
  176. htmlURL, err := url.Parse(fmt.Sprintf("%s/src/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath))
  177. if err != nil {
  178. return nil, err
  179. }
  180. htmlURLString := htmlURL.String()
  181. contentsResponse.HTMLURL = &htmlURLString
  182. contentsResponse.Links.HTMLURL = &htmlURLString
  183. gitURL, err := url.Parse(fmt.Sprintf("%s/git/blobs/%s", repo.APIURL(), entry.ID.String()))
  184. if err != nil {
  185. return nil, err
  186. }
  187. gitURLString := gitURL.String()
  188. contentsResponse.GitURL = &gitURLString
  189. contentsResponse.Links.GitURL = &gitURLString
  190. }
  191. return contentsResponse, nil
  192. }