URI: 
       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 }