@@ -5,13 +5,11 @@ package files
5
5
6
6
import (
7
7
"context"
8
- "fmt"
9
8
"net/url"
10
9
"path"
11
10
12
11
repo_model "code.gitea.io/gitea/models/repo"
13
12
"code.gitea.io/gitea/modules/git"
14
- "code.gitea.io/gitea/modules/gitrepo"
15
13
"code.gitea.io/gitea/modules/setting"
16
14
api "code.gitea.io/gitea/modules/structs"
17
15
"code.gitea.io/gitea/modules/util"
@@ -34,54 +32,47 @@ func (ct *ContentType) String() string {
34
32
return string (* ct )
35
33
}
36
34
35
+ type GetContentsOrListOptions struct {
36
+ IncludeSingleFileContent bool // include the file's content when the tree path is a file
37
+ }
38
+
37
39
// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
38
40
// 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
41
+ func GetContentsOrList (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , treePath string , opts GetContentsOrListOptions ) (ret api.ContentsExtResponse , _ error ) {
42
+ entry , err := prepareGetContentsEntry (refCommit , & treePath )
43
+ if repo .IsEmpty && treePath == "" {
44
+ return api.ContentsExtResponse {DirContents : make ([]* api.ContentsResponse , 0 )}, nil
42
45
}
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 )
57
46
if err != nil {
58
- return nil , err
47
+ return ret , err
59
48
}
60
49
50
+ // get file contents
61
51
if entry .Type () != "tree" {
62
- return GetContents (ctx , repo , refCommit , treePath , false )
52
+ ret .FileContents , err = getFileContentsByEntryInternal (ctx , repo , gitRepo , refCommit , entry , opts )
53
+ return ret , err
63
54
}
64
55
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 )
56
+ // list directory contents
57
+ gitTree , err := refCommit .Commit .SubTree (treePath )
69
58
if err != nil {
70
- return nil , err
59
+ return ret , err
71
60
}
72
61
entries , err := gitTree .ListEntries ()
73
62
if err != nil {
74
- return nil , err
63
+ return ret , err
75
64
}
65
+ opts = GetContentsOrListOptions {IncludeSingleFileContent : false } // never include file content when listing a directory
66
+ ret .DirContents = make ([]* api.ContentsResponse , 0 , len (entries ))
76
67
for _ , e := range entries {
77
68
subTreePath := path .Join (treePath , e .Name ())
78
- fileContentResponse , err := GetContents (ctx , repo , refCommit , subTreePath , true )
69
+ fileContentResponse , err := GetFileContents (ctx , repo , gitRepo , refCommit , subTreePath , opts )
79
70
if err != nil {
80
- return nil , err
71
+ return ret , err
81
72
}
82
- fileList = append (fileList , fileContentResponse )
73
+ ret . DirContents = append (ret . DirContents , fileContentResponse )
83
74
}
84
- return fileList , nil
75
+ return ret , nil
85
76
}
86
77
87
78
// GetObjectTypeFromTreeEntry check what content is behind it
@@ -100,34 +91,36 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType {
100
91
}
101
92
}
102
93
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 ) {
94
+ func prepareGetContentsEntry (refCommit * utils.RefCommit , treePath * string ) (* git.TreeEntry , error ) {
105
95
// 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
- }
96
+ cleanTreePath := CleanGitTreePath (* treePath )
97
+ if cleanTreePath == "" && * treePath != "" {
98
+ return nil , ErrFilenameInvalid {Path : * treePath }
111
99
}
112
- treePath = cleanTreePath
100
+ * treePath = cleanTreePath
113
101
114
- gitRepo , closer , err := gitrepo .RepositoryFromContextOrOpen (ctx , repo )
115
- if err != nil {
116
- return nil , err
102
+ // Only allow safe ref types
103
+ refType := refCommit .RefName .RefType ()
104
+ if refType != git .RefTypeBranch && refType != git .RefTypeTag && refType != git .RefTypeCommit {
105
+ return nil , util .NewNotExistErrorf ("no commit found for the ref [ref: %s]" , refCommit .RefName )
117
106
}
118
- defer closer .Close ()
119
107
120
- commit := refCommit .Commit
121
- entry , err := commit .GetTreeEntryByPath (treePath )
108
+ return refCommit .Commit .GetTreeEntryByPath (* treePath )
109
+ }
110
+
111
+ // GetFileContents gets the metadata on a file's contents. Ref can be a branch, commit or tag
112
+ func GetFileContents (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , treePath string , opts GetContentsOrListOptions ) (* api.ContentsResponse , error ) {
113
+ entry , err := prepareGetContentsEntry (refCommit , & treePath )
122
114
if err != nil {
123
115
return nil , err
124
116
}
117
+ return getFileContentsByEntryInternal (ctx , repo , gitRepo , refCommit , entry , opts )
118
+ }
125
119
120
+ func getFileContentsByEntryInternal (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , entry * git.TreeEntry , opts GetContentsOrListOptions ) (* api.ContentsResponse , error ) {
126
121
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
-
122
+ commit := refCommit .Commit
123
+ treePath := entry .Name ()
131
124
selfURL , err := url .Parse (repo .APIURL () + "/contents/" + util .PathEscapeSegments (treePath ) + "?ref=" + url .QueryEscape (refCommit .InputRef ))
132
125
if err != nil {
133
126
return nil , err
@@ -139,7 +132,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
139
132
return nil , err
140
133
}
141
134
142
- lastCommit , err := commit .GetCommitByPath (treePath )
135
+ lastCommit , err := refCommit . Commit .GetCommitByPath (treePath )
143
136
if err != nil {
144
137
return nil , err
145
138
}
@@ -170,7 +163,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
170
163
if entry .IsRegular () || entry .IsExecutable () {
171
164
contentsResponse .Type = string (ContentTypeRegular )
172
165
// if it is listing the repo root dir, don't waste system resources on reading content
173
- if ! forList {
166
+ if opts . IncludeSingleFileContent {
174
167
blobResponse , err := GetBlobBySHA (ctx , repo , gitRepo , entry .ID .String ())
175
168
if err != nil {
176
169
return nil , err
0 commit comments