@@ -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,48 @@ func (ct *ContentType) String() string {
34
32
return string (* ct )
35
33
}
36
34
35
+ type GetContentsOrListOptions struct {
36
+ TreePath string
37
+ IncludeSingleFileContent bool // include the file's content when the tree path is a file
38
+ }
39
+
37
40
// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
38
41
// 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
42
46
}
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
47
if err != nil {
58
- return nil , err
48
+ return ret , err
59
49
}
60
50
51
+ // get file contents
61
52
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
63
55
}
64
56
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 )
69
59
if err != nil {
70
- return nil , err
60
+ return ret , err
71
61
}
72
62
entries , err := gitTree .ListEntries ()
73
63
if err != nil {
74
- return nil , err
64
+ return ret , err
75
65
}
66
+ ret .DirContents = make ([]* api.ContentsResponse , 0 , len (entries ))
76
67
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 })
79
71
if err != nil {
80
- return nil , err
72
+ return ret , err
81
73
}
82
- fileList = append (fileList , fileContentResponse )
74
+ ret . DirContents = append (ret . DirContents , fileContentResponse )
83
75
}
84
- return fileList , nil
76
+ return ret , nil
85
77
}
86
78
87
79
// GetObjectTypeFromTreeEntry check what content is behind it
@@ -100,35 +92,36 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType {
100
92
}
101
93
}
102
94
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 ) {
105
96
// 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 }
111
100
}
112
- treePath = cleanTreePath
101
+ * treePath = cleanTreePath
113
102
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 )
117
107
}
118
- defer closer .Close ()
119
108
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 )
122
115
if err != nil {
123
116
return nil , err
124
117
}
118
+ return getFileContentsByEntryInternal (ctx , repo , gitRepo , refCommit , entry , opts )
119
+ }
125
120
121
+ func getFileContentsByEntryInternal (ctx context.Context , repo * repo_model.Repository , gitRepo * git.Repository , refCommit * utils.RefCommit , entry * git.TreeEntry , opts GetContentsOrListOptions ) (* api.ContentsResponse , error ) {
126
122
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 ))
132
125
if err != nil {
133
126
return nil , err
134
127
}
@@ -139,15 +132,15 @@ 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 (opts . TreePath )
143
136
if err != nil {
144
137
return nil , err
145
138
}
146
139
147
140
// All content types have these fields in populated
148
141
contentsResponse := & api.ContentsResponse {
149
142
Name : entry .Name (),
150
- Path : treePath ,
143
+ Path : opts . TreePath ,
151
144
SHA : entry .ID .String (),
152
145
LastCommitSHA : lastCommit .ID .String (),
153
146
Size : entry .Size (),
@@ -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
@@ -190,7 +183,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
190
183
contentsResponse .Target = & targetFromContent
191
184
} else if entry .IsSubModule () {
192
185
contentsResponse .Type = string (ContentTypeSubmodule )
193
- submodule , err := commit .GetSubModule (treePath )
186
+ submodule , err := commit .GetSubModule (opts . TreePath )
194
187
if err != nil {
195
188
return nil , err
196
189
}
@@ -200,15 +193,15 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, refCommit *ut
200
193
}
201
194
// Handle links
202
195
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 ))
204
197
if err != nil {
205
198
return nil , err
206
199
}
207
200
downloadURLString := downloadURL .String ()
208
201
contentsResponse .DownloadURL = & downloadURLString
209
202
}
210
203
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 ))
212
205
if err != nil {
213
206
return nil , err
214
207
}
0 commit comments