From 1d76b098b41f71105299407076ab0f7d84d18a2e Mon Sep 17 00:00:00 2001 From: Adam Szatyin Date: Tue, 23 Mar 2021 23:20:08 +0100 Subject: [PATCH 1/5] Fix APK's Content-Type header --- modules/base/tool.go | 5 +++++ modules/base/tool_test.go | 6 ++++++ routers/repo/download.go | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/modules/base/tool.go b/modules/base/tool.go index d721d47e9d773..6f33e0666d60a 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -332,6 +332,11 @@ func IsAudioFile(data []byte) bool { return strings.Contains(DetectContentType(data), "audio/") } +// IsZipFile detects if data is a zip format +func IsZipFile(data []byte) bool { + return strings.Contains(DetectContentType(data), "application/zip") +} + // EntryIcon returns the octicon class for displaying files/directories func EntryIcon(entry *git.TreeEntry) string { switch { diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go index b6baeb8c3ce70..d8b17e20e9376 100644 --- a/modules/base/tool_test.go +++ b/modules/base/tool_test.go @@ -329,6 +329,12 @@ func TestIsAudioFile(t *testing.T) { assert.False(t, IsAudioFile([]byte("plain text"))) } +func TestIsZipFile(t *testing.T) { + zip, _ := base64.StdEncoding.DecodeString("UEsDBAoAAAAAANG0d1IAAAAAAAAAAAAAAAAIABwAdGV4dC50eHRVVAkAA9pfWmDaX1pgdXgLAAEE9QEAAAQUAAAAUEsBAh4DCgAAAAAA0bR3UgAAAAAAAAAAAAAAAAgAGAAAAAAAAAAAAKSBAAAAAHRleHQudHh0VVQFAAPaX1pgdXgLAAEE9QEAAAQUAAAAUEsFBgAAAAABAAEATgAAAEIAAAAAAA==") + assert.True(t, IsZipFile(zip)) + assert.False(t, IsZipFile([]byte("plain text"))) +} + // TODO: Test EntryIcon func TestSetupGiteaRoot(t *testing.T) { diff --git a/routers/repo/download.go b/routers/repo/download.go index dafa62d0d9b8d..24357315b8666 100644 --- a/routers/repo/download.go +++ b/routers/repo/download.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "path" + "path/filepath" "strings" "code.gitea.io/gitea/modules/base" @@ -61,6 +62,9 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) } else { ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") + if filepath.Ext(name) == ".apk" && base.IsZipFile(buf) { + ctx.Resp.Header().Set("Content-Type", "application/vnd.android.package-archive") + } } _, err = ctx.Resp.Write(buf) From a7b42e69a12d670b54e663205e60f109ba8c2aa7 Mon Sep 17 00:00:00 2001 From: Adam Szatyin Date: Wed, 24 Mar 2021 00:35:56 +0100 Subject: [PATCH 2/5] Fix case sensitive comparison --- routers/repo/download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/repo/download.go b/routers/repo/download.go index 24357315b8666..9944e185280c9 100644 --- a/routers/repo/download.go +++ b/routers/repo/download.go @@ -62,7 +62,7 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) } else { ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") - if filepath.Ext(name) == ".apk" && base.IsZipFile(buf) { + if strings.EqualFold(".apk", filepath.Ext(name)) && base.IsZipFile(buf) { ctx.Resp.Header().Set("Content-Type", "application/vnd.android.package-archive") } } From 6c2f2769a029989a5975f7be322c09289448b12c Mon Sep 17 00:00:00 2001 From: Adam Szatyin Date: Wed, 14 Apr 2021 23:39:04 +0200 Subject: [PATCH 3/5] Add custom mime type mapping for downloadable files --- custom/conf/app.example.ini | 5 +++++ modules/setting/mime_type_map.go | 29 +++++++++++++++++++++++++++++ modules/setting/setting.go | 1 + routers/repo/download.go | 7 +++++-- 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 modules/setting/mime_type_map.go diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index bdcf6aaa17c3f..61e146f0067ac 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -2037,3 +2037,8 @@ PATH = ;; ;; Minio enabled ssl only available when STORAGE_TYPE is `minio` ;MINIO_USE_SSL = false + +; Custom mime type mapping for downloadable files +;[download.mimetype.mapping] +;.apk=application/vnd.android.package-archive +;.js=application/javascript diff --git a/modules/setting/mime_type_map.go b/modules/setting/mime_type_map.go new file mode 100644 index 0000000000000..183d5a0a9439f --- /dev/null +++ b/modules/setting/mime_type_map.go @@ -0,0 +1,29 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package setting + +var ( + // MimeTypeMap defines custom mime type mapping settings + MimeTypeMap = struct { + Enabled bool + Map map[string]string + }{ + Enabled: false, + Map: map[string]string{}, + } +) + +func newMimeTypeMap() { + sec := Cfg.Section("download.mimetype.mapping") + keys := sec.Keys() + m := make(map[string]string, len(keys)) + for _, key := range keys { + m[key.Name()] = key.Value() + } + MimeTypeMap.Map = m + if len(keys) > 0 { + MimeTypeMap.Enabled = true + } +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index aef0d867006b4..4244b55939b20 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -1177,4 +1177,5 @@ func NewServices() { newTaskService() NewQueueService() newProject() + newMimeTypeMap() } diff --git a/routers/repo/download.go b/routers/repo/download.go index 9944e185280c9..87639329dd4a2 100644 --- a/routers/repo/download.go +++ b/routers/repo/download.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" ) // ServeData download file from io.Reader @@ -62,8 +63,10 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) } else { ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") - if strings.EqualFold(".apk", filepath.Ext(name)) && base.IsZipFile(buf) { - ctx.Resp.Header().Set("Content-Type", "application/vnd.android.package-archive") + if setting.MimeTypeMap.Enabled { + if mimetype, ok := setting.MimeTypeMap.Map[filepath.Ext(name)]; ok { + ctx.Resp.Header().Set("Content-Type", mimetype) + } } } From f346b3f85d05a6c3490fbfb62716cb202ec49f67 Mon Sep 17 00:00:00 2001 From: Adam Szatyin Date: Thu, 15 Apr 2021 23:51:01 +0200 Subject: [PATCH 4/5] Add documentation for MIME type mapping --- docs/content/doc/advanced/config-cheat-sheet.en-us.md | 9 +++++++++ modules/base/tool.go | 5 ----- modules/base/tool_test.go | 6 ------ modules/setting/mime_type_map.go | 4 +++- routers/repo/download.go | 3 ++- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index eb169035ee8f5..176e60dc370ab 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -966,6 +966,15 @@ MINIO_USE_SSL = false And used by `[attachment]`, `[lfs]` and etc. as `STORAGE_TYPE`. +## MIME type mapping (`download.mimetype.mapping`) + +Configuration for set the expected MIME type based on file extensions of downloadable files. Configuration presents in key-value pairs and file extensions starts with leading `.`. + +The following configuration set `Content-Type: application/javascript` header when downloading files with `.js` file extension. +```ini +.js=application/javascript +``` + ## Other (`other`) - `SHOW_FOOTER_BRANDING`: **false**: Show Gitea branding in the footer. diff --git a/modules/base/tool.go b/modules/base/tool.go index 6f33e0666d60a..d721d47e9d773 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -332,11 +332,6 @@ func IsAudioFile(data []byte) bool { return strings.Contains(DetectContentType(data), "audio/") } -// IsZipFile detects if data is a zip format -func IsZipFile(data []byte) bool { - return strings.Contains(DetectContentType(data), "application/zip") -} - // EntryIcon returns the octicon class for displaying files/directories func EntryIcon(entry *git.TreeEntry) string { switch { diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go index d8b17e20e9376..b6baeb8c3ce70 100644 --- a/modules/base/tool_test.go +++ b/modules/base/tool_test.go @@ -329,12 +329,6 @@ func TestIsAudioFile(t *testing.T) { assert.False(t, IsAudioFile([]byte("plain text"))) } -func TestIsZipFile(t *testing.T) { - zip, _ := base64.StdEncoding.DecodeString("UEsDBAoAAAAAANG0d1IAAAAAAAAAAAAAAAAIABwAdGV4dC50eHRVVAkAA9pfWmDaX1pgdXgLAAEE9QEAAAQUAAAAUEsBAh4DCgAAAAAA0bR3UgAAAAAAAAAAAAAAAAgAGAAAAAAAAAAAAKSBAAAAAHRleHQudHh0VVQFAAPaX1pgdXgLAAEE9QEAAAQUAAAAUEsFBgAAAAABAAEATgAAAEIAAAAAAA==") - assert.True(t, IsZipFile(zip)) - assert.False(t, IsZipFile([]byte("plain text"))) -} - // TODO: Test EntryIcon func TestSetupGiteaRoot(t *testing.T) { diff --git a/modules/setting/mime_type_map.go b/modules/setting/mime_type_map.go index 183d5a0a9439f..390da5e4f7a0b 100644 --- a/modules/setting/mime_type_map.go +++ b/modules/setting/mime_type_map.go @@ -4,6 +4,8 @@ package setting +import "strings" + var ( // MimeTypeMap defines custom mime type mapping settings MimeTypeMap = struct { @@ -20,7 +22,7 @@ func newMimeTypeMap() { keys := sec.Keys() m := make(map[string]string, len(keys)) for _, key := range keys { - m[key.Name()] = key.Value() + m[strings.ToLower(key.Name())] = key.Value() } MimeTypeMap.Map = m if len(keys) > 0 { diff --git a/routers/repo/download.go b/routers/repo/download.go index 87639329dd4a2..4917c233ae4de 100644 --- a/routers/repo/download.go +++ b/routers/repo/download.go @@ -64,7 +64,8 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") if setting.MimeTypeMap.Enabled { - if mimetype, ok := setting.MimeTypeMap.Map[filepath.Ext(name)]; ok { + fileExtension := strings.ToLower(filepath.Ext(name)) + if mimetype, ok := setting.MimeTypeMap.Map[fileExtension]; ok { ctx.Resp.Header().Set("Content-Type", mimetype) } } From 753abcdd99f2ace6f640eaf6579bf07ae3e2ff16 Mon Sep 17 00:00:00 2001 From: Adam Szatyin Date: Mon, 10 May 2021 17:50:52 +0200 Subject: [PATCH 5/5] Rename download.mimetype.mapping configuration to repository.mimetype_mapping --- custom/conf/app.example.ini | 15 +++++++++------ .../doc/advanced/config-cheat-sheet.en-us.md | 18 +++++++++--------- modules/setting/mime_type_map.go | 2 +- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 61e146f0067ac..bc1f10b5a973b 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -903,6 +903,15 @@ PATH = ;; - approved: only sign when merging an approved pr to a protected branch ;MERGES = pubkey, twofa, basesigned, commitssigned +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;[repository.mimetype_mapping] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Custom MIME type mapping for downloadable files +;.apk=application/vnd.android.package-archive + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[project] @@ -912,7 +921,6 @@ PATH = ;PROJECT_BOARD_BASIC_KANBAN_TYPE = To Do, In Progress, Done ;PROJECT_BOARD_BUG_TRIAGE_TYPE = Needs Triage, High Priority, Low Priority, Closed - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[cors] @@ -2037,8 +2045,3 @@ PATH = ;; ;; Minio enabled ssl only available when STORAGE_TYPE is `minio` ;MINIO_USE_SSL = false - -; Custom mime type mapping for downloadable files -;[download.mimetype.mapping] -;.apk=application/vnd.android.package-archive -;.js=application/javascript diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 176e60dc370ab..cfc5bcfcec697 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -143,6 +143,15 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. - `LOCAL_COPY_PATH`: **tmp/local-repo**: Path for temporary local repository copies. Defaults to `tmp/local-repo` +## Repository - MIME type mapping (`repository.mimetype_mapping`) + +Configuration for set the expected MIME type based on file extensions of downloadable files. Configuration presents in key-value pairs and file extensions starts with leading `.`. + +The following configuration set `Content-Type: application/vnd.android.package-archive` header when downloading files with `.apk` file extension. +```ini +.apk=application/vnd.android.package-archive +``` + ## CORS (`cors`) - `ENABLED`: **false**: enable cors headers (disabled by default) @@ -966,15 +975,6 @@ MINIO_USE_SSL = false And used by `[attachment]`, `[lfs]` and etc. as `STORAGE_TYPE`. -## MIME type mapping (`download.mimetype.mapping`) - -Configuration for set the expected MIME type based on file extensions of downloadable files. Configuration presents in key-value pairs and file extensions starts with leading `.`. - -The following configuration set `Content-Type: application/javascript` header when downloading files with `.js` file extension. -```ini -.js=application/javascript -``` - ## Other (`other`) - `SHOW_FOOTER_BRANDING`: **false**: Show Gitea branding in the footer. diff --git a/modules/setting/mime_type_map.go b/modules/setting/mime_type_map.go index 390da5e4f7a0b..5c1fc7f71a41b 100644 --- a/modules/setting/mime_type_map.go +++ b/modules/setting/mime_type_map.go @@ -18,7 +18,7 @@ var ( ) func newMimeTypeMap() { - sec := Cfg.Section("download.mimetype.mapping") + sec := Cfg.Section("repository.mimetype_mapping") keys := sec.Keys() m := make(map[string]string, len(keys)) for _, key := range keys {