"Fossies" - the Fresh Open Source Software Archive

Member "hugo-0.113.0/hugolib/alias.go" (5 Jun 2023, 4984 Bytes) of package /linux/www/hugo-0.113.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "alias.go": 0.111.3_vs_0.112.0.

    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 
   29     "github.com/gohugoio/hugo/output"
   30     "github.com/gohugoio/hugo/publisher"
   31     "github.com/gohugoio/hugo/resources/page"
   32     "github.com/gohugoio/hugo/tpl"
   33 )
   34 
   35 type aliasHandler struct {
   36     t         tpl.TemplateHandler
   37     log       loggers.Logger
   38     allowRoot bool
   39 }
   40 
   41 func newAliasHandler(t tpl.TemplateHandler, l loggers.Logger, allowRoot bool) aliasHandler {
   42     return aliasHandler{t, 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 templ tpl.Template
   52     var found bool
   53 
   54     templ, found = a.t.Lookup("alias.html")
   55     if !found {
   56         // TODO(bep) consolidate
   57         templ, found = a.t.Lookup("_internal/alias.html")
   58         if !found {
   59             return nil, errors.New("no alias template found")
   60         }
   61     }
   62 
   63     data := aliasPage{
   64         permalink,
   65         p,
   66     }
   67 
   68     ctx := tpl.SetPageInContext(context.Background(), p)
   69 
   70     buffer := new(bytes.Buffer)
   71     err := a.t.ExecuteWithContext(ctx, templ, buffer, data)
   72     if err != nil {
   73         return nil, err
   74     }
   75     return buffer, nil
   76 }
   77 
   78 func (s *Site) writeDestAlias(path, permalink string, outputFormat output.Format, p page.Page) (err error) {
   79     return s.publishDestAlias(false, path, permalink, outputFormat, p)
   80 }
   81 
   82 func (s *Site) publishDestAlias(allowRoot bool, path, permalink string, outputFormat output.Format, p page.Page) (err error) {
   83     handler := newAliasHandler(s.Tmpl(), s.Log, allowRoot)
   84 
   85     targetPath, err := handler.targetPathAlias(path)
   86     if err != nil {
   87         return err
   88     }
   89 
   90     aliasContent, err := handler.renderAlias(permalink, p)
   91     if err != nil {
   92         return err
   93     }
   94 
   95     pd := publisher.Descriptor{
   96         Src:          aliasContent,
   97         TargetPath:   targetPath,
   98         StatCounter:  &s.PathSpec.ProcessingStats.Aliases,
   99         OutputFormat: outputFormat,
  100     }
  101 
  102     if s.conf.RelativeURLs || s.conf.CanonifyURLs {
  103         pd.AbsURLPath = s.absURLPath(targetPath)
  104     }
  105 
  106     return s.publisher.Publish(pd)
  107 }
  108 
  109 func (a aliasHandler) targetPathAlias(src string) (string, error) {
  110     originalAlias := src
  111     if len(src) <= 0 {
  112         return "", fmt.Errorf("alias \"\" is an empty string")
  113     }
  114 
  115     alias := path.Clean(filepath.ToSlash(src))
  116 
  117     if !a.allowRoot && alias == "/" {
  118         return "", fmt.Errorf("alias \"%s\" resolves to website root directory", originalAlias)
  119     }
  120 
  121     components := strings.Split(alias, "/")
  122 
  123     // Validate against directory traversal
  124     if components[0] == ".." {
  125         return "", fmt.Errorf("alias \"%s\" traverses outside the website root directory", originalAlias)
  126     }
  127 
  128     // Handle Windows file and directory naming restrictions
  129     // See "Naming Files, Paths, and Namespaces" on MSDN
  130     // https://msdn.microsoft.com/en-us/library/aa365247%28v=VS.85%29.aspx?f=255&MSPPError=-2147217396
  131     msgs := []string{}
  132     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"}
  133 
  134     if strings.ContainsAny(alias, ":*?\"<>|") {
  135         msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains invalid characters on Windows: : * ? \" < > |", originalAlias))
  136     }
  137     for _, ch := range alias {
  138         if ch < ' ' {
  139             msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains ASCII control code (0x00 to 0x1F), invalid on Windows: : * ? \" < > |", originalAlias))
  140             continue
  141         }
  142     }
  143     for _, comp := range components {
  144         if strings.HasSuffix(comp, " ") || strings.HasSuffix(comp, ".") {
  145             msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains component with a trailing space or period, problematic on Windows", originalAlias))
  146         }
  147         for _, r := range reservedNames {
  148             if comp == r {
  149                 msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains component with reserved name \"%s\" on Windows", originalAlias, r))
  150             }
  151         }
  152     }
  153     if len(msgs) > 0 {
  154         if runtime.GOOS == "windows" {
  155             for _, m := range msgs {
  156                 a.log.Errorln(m)
  157             }
  158             return "", fmt.Errorf("cannot create \"%s\": Windows filename restriction", originalAlias)
  159         }
  160         for _, m := range msgs {
  161             a.log.Infoln(m)
  162         }
  163     }
  164 
  165     // Add the final touch
  166     alias = strings.TrimPrefix(alias, "/")
  167     if strings.HasSuffix(alias, "/") {
  168         alias = alias + "index.html"
  169     } else if !strings.HasSuffix(alias, ".html") {
  170         alias = alias + "/" + "index.html"
  171     }
  172 
  173     return filepath.FromSlash(alias), nil
  174 }