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.9KB

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