Skip to content

Commit bb4f6d7

Browse files
committed
feat(serverHandler): allow specify filename for download or archive
1 parent 01b3425 commit bb4f6d7

File tree

13 files changed

+223
-97
lines changed

13 files changed

+223
-97
lines changed

doc/en-US/api.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,18 @@ Example:
5050
wget --recursive -nc -nH -np 'http://localhost/dir/?download'
5151
```
5252

53-
# Add download param to file
53+
# Add download param to files
5454
```
5555
GET <path>?download[&sort=key]
5656
```
57-
Add download parameter to file links in the page, which
58-
make them downloadable instead of displaying content.
57+
Add download parameter to file links in directory list page, which
58+
makes them downloadable instead of displaying content.
5959

6060
# Download a file
6161
Notify user agent download a file rather than displaying its content,
6262
by outputting `Content-Disposition` header.
6363
```
64-
GET <path/to/file>?download
64+
GET <path/to/file>?download[=filename]
6565
```
6666

6767
Example:
@@ -72,12 +72,12 @@ curl 'http://localhost/ghfs/file?download'
7272
# Get contents of specified path as archive file
7373
Only work when "archive" is enabled.
7474
```
75-
GET <path>?tar
76-
GET <path>?tgz
77-
GET <path>?zip
78-
POST <path>?tar
79-
POST <path>?tgz
80-
POST <path>?zip
75+
GET <path>?tar[=filename]
76+
GET <path>?tgz[=filename]
77+
GET <path>?zip[=filename]
78+
POST <path>?tar[=filename]
79+
POST <path>?tgz[=filename]
80+
POST <path>?zip[=filename]
8181
```
8282

8383
Example:

doc/zh-CN/api.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ GET <path>?download[&sort=key]
5555
```
5656
为页面中的文件链接添加下载参数,使其可被下载,而不是显示其内容。
5757

58-
# 下载文件
58+
# 下载单个文件
5959
通过输出`Content-Disposition`头,通知用户代理下载文件而不是显示其内容。
6060
```
61-
GET <path/to/file>?download
61+
GET <path/to/file>?download[=filename]
6262
```
6363

6464
举例:
@@ -69,12 +69,12 @@ curl 'http://localhost/ghfs/file?download'
6969
# 以打包文件形式获取指定路径下的内容
7070
仅在“archive”选项启用时有效。
7171
```
72-
GET <path>?tar
73-
GET <path>?tgz
74-
GET <path>?zip
75-
POST <path>?tar
76-
POST <path>?tgz
77-
POST <path>?zip
72+
GET <path>?tar[=filename]
73+
GET <path>?tgz[=filename]
74+
GET <path>?zip[=filename]
75+
POST <path>?tar[=filename]
76+
POST <path>?tgz[=filename]
77+
POST <path>?zip[=filename]
7878
```
7979

8080
举例:

src/serverHandler/archive.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -142,21 +142,10 @@ func (h *aliasHandler) archiveFiles(
142142
session *sessionContext,
143143
data *responseData,
144144
selections []string,
145-
fileSuffix string,
146145
contentType string,
147146
cbWriteFile archiveCallback,
148147
) {
149-
var itemName string
150-
_, hasAlias := h.aliases.byUrlPath(session.vhostReqPath)
151-
if hasAlias {
152-
itemName = path.Base(session.vhostReqPath)
153-
}
154-
if len(itemName) == 0 || itemName == "/" {
155-
itemName = data.ItemName
156-
}
157-
158-
targetFilename := itemName + fileSuffix
159-
writeArchiveHeader(w, contentType, targetFilename)
148+
writeArchiveHeader(w, contentType, session.outFileName)
160149

161150
if !NeedResponseBody(r.Method) {
162151
return
@@ -170,7 +159,7 @@ func (h *aliasHandler) archiveFiles(
170159
data.Item != nil, // not empty root
171160
selections,
172161
func(f *os.File, fInfo os.FileInfo, relPath string) error {
173-
h.logArchive(targetFilename, relPath, r)
162+
h.logArchive(session.outFileName, relPath, r)
174163
err := cbWriteFile(f, fInfo, relPath)
175164
h.logError(err)
176165
return err

src/serverHandler/archiveTar.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ func (h *aliasHandler) tar(w http.ResponseWriter, r *http.Request, session *sess
7777
session,
7878
data,
7979
selections,
80-
".tar",
81-
"application/octet-stream",
80+
"application/x-tar",
8281
func(f *os.File, fInfo os.FileInfo, relPath string) error {
8382
return writeTar(tw, f, fInfo, relPath)
8483
},
@@ -120,8 +119,7 @@ func (h *aliasHandler) tgz(w http.ResponseWriter, r *http.Request, session *sess
120119
session,
121120
data,
122121
selections,
123-
".tar.gz",
124-
"application/octet-stream",
122+
"application/gzip",
125123
func(f *os.File, fInfo os.FileInfo, relPath string) error {
126124
return writeTar(tw, f, fInfo, relPath)
127125
},

src/serverHandler/archiveZip.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ func (h *aliasHandler) zip(w http.ResponseWriter, r *http.Request, session *sess
6161
session,
6262
data,
6363
selections,
64-
".zip",
6564
"application/zip",
6665
func(f *os.File, fInfo os.FileInfo, relPath string) error {
6766
return writeZip(zipWriter, f, fInfo, relPath)

src/serverHandler/auth.go

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,18 @@ func (h *aliasHandler) verifyAuth(r *http.Request, vhostReqPath, reqFsPath strin
5555
}
5656

5757
func (h *aliasHandler) extractNoAuthUrl(r *http.Request, session *sessionContext, data *responseData) string {
58-
if session.query.Has(authQueryParam) {
59-
returnUrl := session.query.Get(authQueryParam)
60-
61-
if len(returnUrl) > 0 {
62-
url, err := url.QueryUnescape(returnUrl)
63-
if err == nil {
64-
returnUrl = url
65-
}
66-
}
67-
68-
if len(returnUrl) > 0 {
58+
returnUrl, hasReturnUrl := getQueryValue(session.query, authQueryParam)
59+
if hasReturnUrl && len(returnUrl) > 0 {
60+
var err error
61+
returnUrl, err = url.QueryUnescape(returnUrl)
62+
if err == nil {
6963
return returnUrl
7064
}
7165
}
7266

73-
referrer := r.Header.Get("Referer")
74-
if len(referrer) > 0 {
75-
return referrer
67+
returnUrl = r.Header.Get("Referer")
68+
if len(returnUrl) > 0 {
69+
return returnUrl
7670
}
7771

7872
return session.prefixReqPath + data.Context.QueryString()

src/serverHandler/content.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ func (h *aliasHandler) content(w http.ResponseWriter, r *http.Request, session *
1010
header.Set("Vary", session.vary)
1111
header.Set("X-Content-Type-Options", "nosniff")
1212
if data.IsDownload {
13-
filename := url.PathEscape(data.ItemName)
13+
filename := url.PathEscape(session.outFileName)
1414
header.Set("Content-Disposition", "attachment; filename="+filename+"; filename*=UTF-8''"+filename)
1515
}
1616

src/serverHandler/sessionData.go

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ type sessionContext struct {
8484

8585
query url.Values
8686

87+
outFileName string
88+
8789
errors []error
8890
}
8991

@@ -110,7 +112,6 @@ type responseData struct {
110112
RootRelPath string
111113

112114
Item os.FileInfo
113-
ItemName string
114115
SubItems []os.FileInfo
115116
AliasSubItems []os.FileInfo
116117
SubItemsHtml []itemHtml
@@ -267,7 +268,12 @@ func getSubItemPrefix(currDirRelPath, rawRequestPath string, tailSlash bool) str
267268
}
268269
}
269270

270-
func getItemName(info os.FileInfo, r *http.Request) (itemName string) {
271+
func getItemName(info os.FileInfo, r *http.Request, prefixReqPath string) (itemName string) {
272+
itemName = path.Base(prefixReqPath)
273+
if len(itemName) > 0 && itemName != "/" {
274+
return
275+
}
276+
271277
if info != nil {
272278
itemName = info.Name()
273279
}
@@ -359,10 +365,7 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
359365
fsPath := filepath.Clean(h.dir + reqPath)
360366

361367
query := r.URL.Query()
362-
queryPrefix := r.URL.RawQuery
363-
if querySepIndex := strings.IndexByte(queryPrefix, '&'); querySepIndex >= 0 {
364-
queryPrefix = queryPrefix[:querySepIndex]
365-
}
368+
queryPrefix := getQueryPrefix(r.URL.RawQuery)
366369

367370
status := http.StatusOK
368371

@@ -376,39 +379,6 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
376379

377380
headers := h.getHeaders(vhostReqPath, fsPath, authSuccess)
378381

379-
isUpload := false
380-
isMkdir := false
381-
isDelete := false
382-
isMutate := false
383-
switch queryPrefix {
384-
case "upload":
385-
isUpload = true
386-
isMutate = true
387-
case "mkdir":
388-
isMkdir = true
389-
isMutate = true
390-
case "delete":
391-
isDelete = true
392-
isMutate = true
393-
}
394-
395-
isArchive := false
396-
var arFmt archiveFormat
397-
switch queryPrefix {
398-
case "tar":
399-
isArchive = true
400-
arFmt = tarFmt
401-
case "tgz":
402-
isArchive = true
403-
arFmt = tgzFmt
404-
case "zip":
405-
isArchive = true
406-
arFmt = zipFmt
407-
}
408-
409-
isSimple := query.Has("simple")
410-
isDownload := query.Has("download")
411-
412382
accepts := acceptHeaders.ParseAccepts(r.Header.Get("Accept"))
413383
acceptIndex, _, _ := accepts.GetPreferredValue(acceptContentTypes)
414384
wantJson := acceptIndex == acceptJsonIndex
@@ -463,7 +433,60 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
463433

464434
canIndex = canIndex && allowAccess
465435

466-
itemName := getItemName(item, r)
436+
itemName := getItemName(item, r, prefixReqPath)
437+
438+
var outFileName string
439+
440+
isUpload := false
441+
isMkdir := false
442+
isDelete := false
443+
isMutate := false
444+
switch queryPrefix {
445+
case "upload":
446+
isUpload = true
447+
isMutate = true
448+
case "mkdir":
449+
isMkdir = true
450+
isMutate = true
451+
case "delete":
452+
isDelete = true
453+
isMutate = true
454+
}
455+
456+
isArchive := false
457+
var arFmt archiveFormat
458+
switch queryPrefix {
459+
case "tar":
460+
isArchive = true
461+
arFmt = tarFmt
462+
outFileName = ".tar"
463+
case "tgz":
464+
isArchive = true
465+
arFmt = tgzFmt
466+
outFileName = ".tar.tz"
467+
case "zip":
468+
isArchive = true
469+
arFmt = zipFmt
470+
outFileName = ".zip"
471+
}
472+
if isArchive {
473+
arName, _ := getQueryValue(query, queryPrefix)
474+
if len(arName) > 0 {
475+
outFileName = arName
476+
} else {
477+
outFileName = itemName + outFileName
478+
}
479+
}
480+
481+
_, isSimple := query["simple"]
482+
dlName, isDownload := getQueryValue(query, "download")
483+
if isDownload {
484+
if len(dlName) > 0 {
485+
outFileName = dlName
486+
} else {
487+
outFileName = itemName
488+
}
489+
}
467490

468491
subItems, _readdirErr := readdir(file, item, canIndex && !isMutate && !isArchive && NeedResponseBody(r.Method))
469492
if _readdirErr != nil {
@@ -536,6 +559,8 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
536559
isArchive: isArchive,
537560
archiveFormat: arFmt,
538561

562+
outFileName: outFileName,
563+
539564
file: file,
540565

541566
query: query,
@@ -565,7 +590,6 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
565590
RootRelPath: rootRelPath,
566591

567592
Item: item,
568-
ItemName: itemName,
569593
SubItems: subItems,
570594
AliasSubItems: aliasSubItems,
571595
SubItemsHtml: nil,

src/serverHandler/util.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,40 @@ import (
44
"mjpclab.dev/ghfs/src/user"
55
"mjpclab.dev/ghfs/src/util"
66
"net/http"
7+
"net/url"
78
"os"
89
"path"
910
"regexp"
1011
"strings"
1112
)
1213

14+
func getQueryPrefix(rawQuery string) (queryPrefix string) {
15+
if len(rawQuery) == 0 {
16+
return
17+
}
18+
19+
queryPrefix = rawQuery
20+
21+
var querySepIndex int
22+
if querySepIndex = strings.IndexByte(queryPrefix, '&'); querySepIndex >= 0 {
23+
queryPrefix = queryPrefix[:querySepIndex]
24+
}
25+
if querySepIndex = strings.IndexByte(queryPrefix, '='); querySepIndex >= 0 {
26+
queryPrefix = queryPrefix[:querySepIndex]
27+
}
28+
29+
return
30+
}
31+
32+
func getQueryValue(urlValues url.Values, key string) (value string, ok bool) {
33+
var values []string
34+
values, ok = urlValues[key]
35+
if len(values) > 0 {
36+
value = values[len(values)-1]
37+
}
38+
return
39+
}
40+
1341
func pathUsernamesToPathUids(users *user.List, pathsUsernames [][]string) pathIntsList {
1442
list := make(pathIntsList, 0, len(pathsUsernames))
1543
for _, pathUsernames := range pathsUsernames {

0 commit comments

Comments
 (0)