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.

content.go 5.8KB

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