Fix manifest encoding (#14114)
The previous URL encoding would encode spaces to '+' for the app name which is incorrect. Use base64 encoding instead which does not have such issues.
This commit is contained in:
parent
e0c753e770
commit
cd5278a44c
3 changed files with 105 additions and 10 deletions
|
@ -7,8 +7,8 @@ package setting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
|
@ -104,6 +104,7 @@ var (
|
||||||
GracefulHammerTime time.Duration
|
GracefulHammerTime time.Duration
|
||||||
StartupTimeout time.Duration
|
StartupTimeout time.Duration
|
||||||
StaticURLPrefix string
|
StaticURLPrefix string
|
||||||
|
AbsoluteAssetURL string
|
||||||
|
|
||||||
SSH = struct {
|
SSH = struct {
|
||||||
Disabled bool `ini:"DISABLE_SSH"`
|
Disabled bool `ini:"DISABLE_SSH"`
|
||||||
|
@ -294,7 +295,7 @@ var (
|
||||||
CSRFCookieName = "_csrf"
|
CSRFCookieName = "_csrf"
|
||||||
CSRFCookieHTTPOnly = true
|
CSRFCookieHTTPOnly = true
|
||||||
|
|
||||||
ManifestData template.URL
|
ManifestData string
|
||||||
|
|
||||||
// Mirror settings
|
// Mirror settings
|
||||||
Mirror struct {
|
Mirror struct {
|
||||||
|
@ -600,6 +601,11 @@ func NewContext() {
|
||||||
Domain = urlHostname
|
Domain = urlHostname
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix)
|
||||||
|
|
||||||
|
manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL)
|
||||||
|
ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes)
|
||||||
|
|
||||||
var defaultLocalURL string
|
var defaultLocalURL string
|
||||||
switch Protocol {
|
switch Protocol {
|
||||||
case UnixSocket:
|
case UnixSocket:
|
||||||
|
@ -645,8 +651,6 @@ func NewContext() {
|
||||||
LandingPageURL = LandingPageHome
|
LandingPageURL = LandingPageHome
|
||||||
}
|
}
|
||||||
|
|
||||||
ManifestData = makeManifestData()
|
|
||||||
|
|
||||||
if len(SSH.Domain) == 0 {
|
if len(SSH.Domain) == 0 {
|
||||||
SSH.Domain = Domain
|
SSH.Domain = Domain
|
||||||
}
|
}
|
||||||
|
@ -1045,12 +1049,74 @@ func loadOrGenerateInternalToken(sec *ini.Section) string {
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeManifestData() template.URL {
|
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
|
||||||
name := url.QueryEscape(AppName)
|
func MakeAbsoluteAssetURL(appURL string, staticURLPrefix string) string {
|
||||||
prefix := url.QueryEscape(StaticURLPrefix)
|
parsedPrefix, err := url.Parse(strings.TrimSuffix(staticURLPrefix, "/"))
|
||||||
subURL := url.QueryEscape(AppSubURL) + "/"
|
if err != nil {
|
||||||
|
log.Fatal("Unable to parse STATIC_URL_PREFIX: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return template.URL(`data:application/json,{"short_name":"` + name + `","name":"` + name + `","icons":[{"src":"` + prefix + `/img/logo-lg.png","type":"image/png","sizes":"880x880"},{"src":"` + prefix + `/img/logo-sm.png","type":"image/png","sizes":"120x120"},{"src":"` + prefix + `/img/logo-512.png","type":"image/png","sizes":"512x512"},{"src":"` + prefix + `/img/logo-192.png","type":"image/png","sizes":"192x192"}],"start_url":"` + subURL + `","scope":"` + subURL + `","background_color":"%23FAFAFA","display":"standalone"}`)
|
if err == nil && parsedPrefix.Hostname() == "" {
|
||||||
|
if staticURLPrefix == "" {
|
||||||
|
return strings.TrimSuffix(appURL, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticURLPrefix is just a path
|
||||||
|
return strings.TrimSuffix(appURL, "/") + strings.TrimSuffix(staticURLPrefix, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSuffix(staticURLPrefix, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeManifestData generates web app manifest JSON
|
||||||
|
func MakeManifestData(appName string, appURL string, absoluteAssetURL string) []byte {
|
||||||
|
type manifestIcon struct {
|
||||||
|
Src string `json:"src"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Sizes string `json:"sizes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type manifestJSON struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ShortName string `json:"short_name"`
|
||||||
|
StartURL string `json:"start_url"`
|
||||||
|
Icons []manifestIcon `json:"icons"`
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := json.Marshal(&manifestJSON{
|
||||||
|
Name: appName,
|
||||||
|
ShortName: appName,
|
||||||
|
StartURL: appURL,
|
||||||
|
Icons: []manifestIcon{
|
||||||
|
{
|
||||||
|
Src: absoluteAssetURL + "/img/logo-lg.png",
|
||||||
|
Type: "image/png",
|
||||||
|
Sizes: "880x880",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Src: absoluteAssetURL + "/img/logo-512.png",
|
||||||
|
Type: "image/png",
|
||||||
|
Sizes: "512x512",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Src: absoluteAssetURL + "/img/logo-192.png",
|
||||||
|
Type: "image/png",
|
||||||
|
Sizes: "192x192",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Src: absoluteAssetURL + "/img/logo-sm.png",
|
||||||
|
Type: "image/png",
|
||||||
|
Sizes: "120x120",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error("unable to marshal manifest JSON. Error: %v", err)
|
||||||
|
return make([]byte, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServices initializes the services
|
// NewServices initializes the services
|
||||||
|
|
29
modules/setting/setting_test.go
Normal file
29
modules/setting/setting_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2020 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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakeAbsoluteAssetURL(t *testing.T) {
|
||||||
|
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL("https://localhost:1234", "https://localhost:2345"))
|
||||||
|
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL("https://localhost:1234/", "https://localhost:2345"))
|
||||||
|
assert.Equal(t, "https://localhost:2345", MakeAbsoluteAssetURL("https://localhost:1234/", "https://localhost:2345/"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234", "/foo"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234/", "/foo"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/foo", MakeAbsoluteAssetURL("https://localhost:1234/", "/foo/"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/foo/bar", MakeAbsoluteAssetURL("https://localhost:1234/foo", "/bar"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/foo/bar", MakeAbsoluteAssetURL("https://localhost:1234/foo/", "/bar"))
|
||||||
|
assert.Equal(t, "https://localhost:1234/foo/bar", MakeAbsoluteAssetURL("https://localhost:1234/foo/", "/bar/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMakeManifestData(t *testing.T) {
|
||||||
|
jsonBytes := MakeManifestData(`Example App '\"`, "https://example.com", "https://example.com/foo/bar")
|
||||||
|
assert.True(t, json.Valid(jsonBytes))
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<title>{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}} </title>
|
<title>{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}} </title>
|
||||||
<link rel="manifest" href="{{.ManifestData}}"/>
|
<link rel="manifest" href="data:{{.ManifestData}}"/>
|
||||||
<meta name="theme-color" content="{{ThemeColorMetaTag}}">
|
<meta name="theme-color" content="{{ThemeColorMetaTag}}">
|
||||||
<meta name="default-theme" content="{{DefaultTheme}}" />
|
<meta name="default-theme" content="{{DefaultTheme}}" />
|
||||||
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}" />
|
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}" />
|
||||||
|
|
Reference in a new issue