@@ -5,13 +5,11 @@ package files
55
66import (
77 "context"
8- "fmt"
98 "net/url"
109 "path"
1110
1211 repo_model "code.gitea.io/gitea/models/repo"
1312 "code.gitea.io/gitea/modules/git"
14- "code.gitea.io/gitea/modules/gitrepo"
1513 "code.gitea.io/gitea/modules/setting"
1614 api "code.gitea.io/gitea/modules/structs"
1715 "code.gitea.io/gitea/modules/util"
@@ -34,54 +32,48 @@ func (ct *ContentType) String() string {
3432 return string (* ct )
3533}
3634
35+ type GetContentsOrListOptions struct {
36+ TreePath string
37+ IncludeSingleFileContent bool // include the file's content when the tree path is a file
38+ }
39+
3740// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
3841// directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
39- func GetContentsOrList (ctx context.Context , repo * repo_model.Repository , refCommit * utils.RefCommit , treePath string ) (any , error ) {
40- if repo .IsEmpty {
41- return make ([]any , 0 ), nil
42+ func GetContentsOrList (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , opts GetContentsOrListOptions ) (ret api.ContentsExtResponse , _ error ) {
43+ entry , err := prepareGetContentsEntry (refCommit , & opts .TreePath )
44+ if repo .IsEmpty && opts .TreePath == "" {
45+ return api.ContentsExtResponse {DirContents : make ([]* api.ContentsResponse , 0 )}, nil
4246 }
43-
44- // Check that the path given in opts.treePath is valid (not a git path)
45- cleanTreePath := CleanGitTreePath (treePath )
46- if cleanTreePath == "" && treePath != "" {
47- return nil , ErrFilenameInvalid {
48- Path : treePath ,
49- }
50- }
51- treePath = cleanTreePath
52-
53- // Get the commit object for the ref
54- commit := refCommit .Commit
55-
56- entry , err := commit .GetTreeEntryByPath (treePath )
5747 if err != nil {
58- return nil , err
48+ return ret , err
5949 }
6050
51+ // get file contents
6152 if entry .Type () != "tree" {
62- return GetContents (ctx , repo , refCommit , treePath , false )
53+ ret .FileContents , err = getFileContentsByEntryInternal (ctx , repo , gitRepo , refCommit , entry , opts )
54+ return ret , err
6355 }
6456
65- // We are in a directory, so we return a list of FileContentResponse objects
66- var fileList []* api.ContentsResponse
67-
68- gitTree , err := commit .SubTree (treePath )
57+ // list directory contents
58+ gitTree , err := refCommit .Commit .SubTree (opts .TreePath )
6959 if err != nil {
70- return nil , err
60+ return ret , err
7161 }
7262 entries , err := gitTree .ListEntries ()
7363 if err != nil {
74- return nil , err
64+ return ret , err
7565 }
66+ ret .DirContents = make ([]* api.ContentsResponse , 0 , len (entries ))
7667 for _ , e := range entries {
77- subTreePath := path .Join (treePath , e .Name ())
78- fileContentResponse , err := GetContents (ctx , repo , refCommit , subTreePath , true )
68+ // never include file content when listing a directory
69+ subTreePath := path .Join (opts .TreePath , e .Name ())
70+ fileContentResponse , err := GetFileContents (ctx , repo , gitRepo , refCommit , GetContentsOrListOptions {TreePath : subTreePath , IncludeSingleFileContent : false })
7971 if err != nil {
80- return nil , err
72+ return ret , err
8173 }
82- fileList = append (fileList , fileContentResponse )
74+ ret . DirContents = append (ret . DirContents , fileContentResponse )
8375 }
84- return fileList , nil
76+ return ret , nil
8577}
8678
8779// GetObjectTypeFromTreeEntry check what content is behind it
@@ -100,35 +92,36 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType {
10092 }
10193}
10294
103- // GetContents gets the metadata on a file's contents. Ref can be a branch, commit or tag
104- func GetContents (ctx context.Context , repo * repo_model.Repository , refCommit * utils.RefCommit , treePath string , forList bool ) (* api.ContentsResponse , error ) {
95+ func prepareGetContentsEntry (refCommit * utils.RefCommit , treePath * string ) (* git.TreeEntry , error ) {
10596 // Check that the path given in opts.treePath is valid (not a git path)
106- cleanTreePath := CleanGitTreePath (treePath )
107- if cleanTreePath == "" && treePath != "" {
108- return nil , ErrFilenameInvalid {
109- Path : treePath ,
110- }
97+ cleanTreePath := CleanGitTreePath (* treePath )
98+ if cleanTreePath == "" && * treePath != "" {
99+ return nil , ErrFilenameInvalid {Path : * treePath }
111100 }
112- treePath = cleanTreePath
101+ * treePath = cleanTreePath
113102
114- gitRepo , closer , err := gitrepo .RepositoryFromContextOrOpen (ctx , repo )
115- if err != nil {
116- return nil , err
103+ // Only allow safe ref types
104+ refType := refCommit .RefName .RefType ()
105+ if refType != git .RefTypeBranch && refType != git .RefTypeTag && refType != git .RefTypeCommit {
106+ return nil , util .NewNotExistErrorf ("no commit found for the ref [ref: %s]" , refCommit .RefName )
117107 }
118- defer closer .Close ()
119108
120- commit := refCommit .Commit
121- entry , err := commit .GetTreeEntryByPath (treePath )
109+ return refCommit .Commit .GetTreeEntryByPath (* treePath )
110+ }
111+
112+ // GetFileContents gets the metadata on a file's contents. Ref can be a branch, commit or tag
113+ func GetFileContents (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , opts GetContentsOrListOptions ) (* api.ContentsResponse , error ) {
114+ entry , err := prepareGetContentsEntry (refCommit , & opts .TreePath )
122115 if err != nil {
123116 return nil , err
124117 }
118+ return getFileContentsByEntryInternal (ctx , repo , gitRepo , refCommit , entry , opts )
119+ }
125120
121+ func getFileContentsByEntryInternal (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , entry * git.TreeEntry , opts GetContentsOrListOptions ) (* api.ContentsResponse , error ) {
126122 refType := refCommit .RefName .RefType ()
127- if refType != git .RefTypeBranch && refType != git .RefTypeTag && refType != git .RefTypeCommit {
128- return nil , fmt .Errorf ("no commit found for the ref [ref: %s]" , refCommit .RefName )
129- }
130-
131- selfURL , err := url .Parse (repo .APIURL () + "/contents/" + util .PathEscapeSegments (treePath ) + "?ref=" + url .QueryEscape (refCommit .InputRef ))
123+ commit := refCommit .Commit
124+ selfURL , err := url .Parse (repo .APIURL () + "/contents/" + util .PathEscapeSegments (opts .TreePath ) + "?ref=" + url .QueryEscape (refCommit .InputRef ))
132125 if err != nil {
133126 return nil , err
134127 }
@@ -139,15 +132,15 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
139132 return nil , err
140133 }
141134
142- lastCommit , err := commit . GetCommitByPath (treePath )
135+ lastCommit , err := refCommit . Commit . GetCommitByPath (opts . TreePath )
143136 if err != nil {
144137 return nil , err
145138 }
146139
147140 // All content types have these fields in populated
148141 contentsResponse := & api.ContentsResponse {
149142 Name : entry .Name (),
150- Path : treePath ,
143+ Path : opts . TreePath ,
151144 SHA : entry .ID .String (),
152145 LastCommitSHA : lastCommit .ID .String (),
153146 Size : entry .Size (),
@@ -170,7 +163,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
170163 if entry .IsRegular () || entry .IsExecutable () {
171164 contentsResponse .Type = string (ContentTypeRegular )
172165 // if it is listing the repo root dir, don't waste system resources on reading content
173- if ! forList {
166+ if opts . IncludeSingleFileContent {
174167 blobResponse , err := GetBlobBySHA (ctx , repo , gitRepo , entry .ID .String ())
175168 if err != nil {
176169 return nil , err
@@ -190,7 +183,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
190183 contentsResponse .Target = & targetFromContent
191184 } else if entry .IsSubModule () {
192185 contentsResponse .Type = string (ContentTypeSubmodule )
193- submodule , err := commit .GetSubModule (treePath )
186+ submodule , err := commit .GetSubModule (opts . TreePath )
194187 if err != nil {
195188 return nil , err
196189 }
@@ -200,15 +193,15 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
200193 }
201194 // Handle links
202195 if entry .IsRegular () || entry .IsLink () || entry .IsExecutable () {
203- downloadURL , err := url .Parse (repo .HTMLURL () + "/raw/" + refCommit .RefName .RefWebLinkPath () + "/" + util .PathEscapeSegments (treePath ))
196+ downloadURL , err := url .Parse (repo .HTMLURL () + "/raw/" + refCommit .RefName .RefWebLinkPath () + "/" + util .PathEscapeSegments (opts . TreePath ))
204197 if err != nil {
205198 return nil , err
206199 }
207200 downloadURLString := downloadURL .String ()
208201 contentsResponse .DownloadURL = & downloadURLString
209202 }
210203 if ! entry .IsSubModule () {
211- htmlURL , err := url .Parse (repo .HTMLURL () + "/src/" + refCommit .RefName .RefWebLinkPath () + "/" + util .PathEscapeSegments (treePath ))
204+ htmlURL , err := url .Parse (repo .HTMLURL () + "/src/" + refCommit .RefName .RefWebLinkPath () + "/" + util .PathEscapeSegments (opts . TreePath ))
212205 if err != nil {
213206 return nil , err
214207 }
0 commit comments