alias.go - hugo - [fork] hugo port for 9front
HTML git clone https://git.drkhsh.at/hugo.git
DIR Log
DIR Files
DIR Refs
DIR Submodules
DIR README
DIR LICENSE
---
alias.go (5361B)
---
1 // Copyright 2019 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package hugolib
15
16 import (
17 "bytes"
18 "context"
19 "errors"
20 "fmt"
21 "io"
22 "path"
23 "path/filepath"
24 "runtime"
25 "strings"
26
27 "github.com/gohugoio/hugo/common/loggers"
28 "github.com/gohugoio/hugo/output"
29 "github.com/gohugoio/hugo/publisher"
30 "github.com/gohugoio/hugo/resources/page"
31 "github.com/gohugoio/hugo/tpl"
32 "github.com/gohugoio/hugo/tpl/tplimpl"
33 )
34
35 type aliasHandler struct {
36 ts *tplimpl.TemplateStore
37 log loggers.Logger
38 allowRoot bool
39 }
40
41 func newAliasHandler(ts *tplimpl.TemplateStore, l loggers.Logger, allowRoot bool) aliasHandler {
42 return aliasHandler{ts, l, allowRoot}
43 }
44
45 type aliasPage struct {
46 Permalink string
47 page.Page
48 }
49
50 func (a aliasHandler) renderAlias(permalink string, p page.Page) (io.Reader, error) {
51 var templateDesc tplimpl.TemplateDescriptor
52 var base string = ""
53 if ps, ok := p.(*pageState); ok {
54 base, templateDesc = ps.GetInternalTemplateBasePathAndDescriptor()
55 }
56 templateDesc.LayoutFromUser = ""
57 templateDesc.Kind = ""
58 templateDesc.OutputFormat = output.AliasHTMLFormat.Name
59 templateDesc.MediaType = output.AliasHTMLFormat.MediaType.Type
60
61 q := tplimpl.TemplateQuery{
62 Path: base,
63 Category: tplimpl.CategoryLayout,
64 Desc: templateDesc,
65 }
66
67 t := a.ts.LookupPagesLayout(q)
68 if t == nil {
69 return nil, errors.New("no alias template found")
70 }
71
72 data := aliasPage{
73 permalink,
74 p,
75 }
76
77 ctx := tpl.Context.Page.Set(context.Background(), p)
78
79 buffer := new(bytes.Buffer)
80 err := a.ts.ExecuteWithContext(ctx, t, buffer, data)
81 if err != nil {
82 return nil, err
83 }
84 return buffer, nil
85 }
86
87 func (s *Site) writeDestAlias(path, permalink string, outputFormat output.Format, p page.Page) (err error) {
88 return s.publishDestAlias(false, path, permalink, outputFormat, p)
89 }
90
91 func (s *Site) publishDestAlias(allowRoot bool, path, permalink string, outputFormat output.Format, p page.Page) (err error) {
92 handler := newAliasHandler(s.GetTemplateStore(), s.Log, allowRoot)
93
94 targetPath, err := handler.targetPathAlias(path)
95 if err != nil {
96 return err
97 }
98
99 aliasContent, err := handler.renderAlias(permalink, p)
100 if err != nil {
101 return err
102 }
103
104 pd := publisher.Descriptor{
105 Src: aliasContent,
106 TargetPath: targetPath,
107 StatCounter: &s.PathSpec.ProcessingStats.Aliases,
108 OutputFormat: outputFormat,
109 }
110
111 if s.conf.RelativeURLs || s.conf.CanonifyURLs {
112 pd.AbsURLPath = s.absURLPath(targetPath)
113 }
114
115 return s.publisher.Publish(pd)
116 }
117
118 func (a aliasHandler) targetPathAlias(src string) (string, error) {
119 originalAlias := src
120 if len(src) <= 0 {
121 return "", fmt.Errorf("alias \"\" is an empty string")
122 }
123
124 alias := path.Clean(filepath.ToSlash(src))
125
126 if !a.allowRoot && alias == "/" {
127 return "", fmt.Errorf("alias \"%s\" resolves to website root directory", originalAlias)
128 }
129
130 components := strings.Split(alias, "/")
131
132 // Validate against directory traversal
133 if components[0] == ".." {
134 return "", fmt.Errorf("alias \"%s\" traverses outside the website root directory", originalAlias)
135 }
136
137 // Handle Windows file and directory naming restrictions
138 // See "Naming Files, Paths, and Namespaces" on MSDN
139 // https://msdn.microsoft.com/en-us/library/aa365247%28v=VS.85%29.aspx?f=255&MSPPError=-2147217396
140 msgs := []string{}
141 reservedNames := []string{"CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}
142
143 if strings.ContainsAny(alias, ":*?\"<>|") {
144 msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains invalid characters on Windows: : * ? \" < > |", originalAlias))
145 }
146 for _, ch := range alias {
147 if ch < ' ' {
148 msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains ASCII control code (0x00 to 0x1F), invalid on Windows: : * ? \" < > |", originalAlias))
149 continue
150 }
151 }
152 for _, comp := range components {
153 if strings.HasSuffix(comp, " ") || strings.HasSuffix(comp, ".") {
154 msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains component with a trailing space or period, problematic on Windows", originalAlias))
155 }
156 for _, r := range reservedNames {
157 if comp == r {
158 msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains component with reserved name \"%s\" on Windows", originalAlias, r))
159 }
160 }
161 }
162 if len(msgs) > 0 {
163 if runtime.GOOS == "windows" {
164 for _, m := range msgs {
165 a.log.Errorln(m)
166 }
167 return "", fmt.Errorf("cannot create \"%s\": Windows filename restriction", originalAlias)
168 }
169 for _, m := range msgs {
170 a.log.Infoln(m)
171 }
172 }
173
174 // Add the final touch
175 alias = strings.TrimPrefix(alias, "/")
176 if strings.HasSuffix(alias, "/") {
177 alias = alias + "index.html"
178 } else if !strings.HasSuffix(alias, ".html") {
179 alias = alias + "/" + "index.html"
180 }
181
182 return filepath.FromSlash(alias), nil
183 }