1 package loaders 2 3 import ( 4 "bytes" 5 "io" 6 "io/ioutil" 7 "log" 8 "os" 9 "path/filepath" 10 11 "github.com/pkg/errors" 12 ) 13 14 // FilesystemLoader represents a local filesystem loader with basic 15 // BaseDirectory capabilities. The access to the local filesystem is unrestricted. 16 type FilesystemLoader struct { 17 root string 18 } 19 20 // MustNewFileSystemLoader creates a new FilesystemLoader instance 21 // and panics if there's any error during instantiation. The parameters 22 // are the same like NewFileSystemLoader. 23 func MustNewFileSystemLoader(root string) *FilesystemLoader { 24 fs, err := NewFileSystemLoader(root) 25 if err != nil { 26 log.Panic(err) 27 } 28 return fs 29 } 30 31 // NewFileSystemLoader creates a new FilesystemLoader and allows 32 // templatesto be loaded from disk (unrestricted). If any base directory 33 // is given (or being set using SetBaseDir), this base directory is being used 34 // for path calculation in template inclusions/imports. Otherwise the path 35 // is calculated based relatively to the including template's path. 36 func NewFileSystemLoader(root string) (*FilesystemLoader, error) { 37 fs := &FilesystemLoader{} 38 if root != "" { 39 if err := fs.SetBaseDir(root); err != nil { 40 return nil, err 41 } 42 } 43 return fs, nil 44 } 45 46 // SetBaseDir sets the template's base directory. This directory will 47 // be used for any relative path in filters, tags and From*-functions to determine 48 // your template. See the comment for NewFileSystemLoader as well. 49 func (fs *FilesystemLoader) SetBaseDir(path string) error { 50 // Make the path absolute 51 if !filepath.IsAbs(path) { 52 abs, err := filepath.Abs(path) 53 if err != nil { 54 return err 55 } 56 path = abs 57 } 58 59 // Check for existence 60 fi, err := os.Stat(path) 61 if err != nil { 62 return err 63 } 64 if !fi.IsDir() { 65 return errors.Errorf("The given path '%s' is not a directory.", path) 66 } 67 68 fs.root = path 69 return nil 70 } 71 72 // Get reads the path's content from your local filesystem. 73 func (fs *FilesystemLoader) Get(path string) (io.Reader, error) { 74 realPath, err := fs.Path(path) 75 if err != nil { 76 return nil, err 77 } 78 buf, err := ioutil.ReadFile(realPath) 79 if err != nil { 80 return nil, err 81 } 82 return bytes.NewReader(buf), nil 83 } 84 85 // Path resolves a filename relative to the base directory. Absolute paths are allowed. 86 // When there's no base dir set, the absolute path to the filename 87 // will be calculated based on either the provided base directory (which 88 // might be a path of a template which includes another template) or 89 // the current working directory. 90 func (fs *FilesystemLoader) Path(name string) (string, error) { 91 if filepath.IsAbs(name) { 92 return name, nil 93 } 94 95 // root := fs.root 96 if fs.root == "" { 97 root, err := os.Getwd() 98 if err != nil { 99 return "", err 100 } 101 return filepath.Join(root, name), nil 102 } else { 103 return filepath.Join(fs.root, name), nil 104 } 105 106 // // Our own base dir has always priority; if there's none 107 // // we use the path provided in base. 108 // var err error 109 // if fs.root == "" { 110 // if base == "" { 111 // base, err = os.Getwd() 112 // if err != nil { 113 // panic(err) 114 // } 115 // return filepath.Join(base, name) 116 // } 117 118 // return filepath.Join(filepath.Dir(base), name) 119 // } 120 121 // return filepath.Join(fs.root, name) 122 } 123 124 // SandboxedFilesystemLoader is still WIP. 125 type SandboxedFilesystemLoader struct { 126 *FilesystemLoader 127 } 128 129 // NewSandboxedFilesystemLoader creates a new sandboxed local file system instance. 130 func NewSandboxedFilesystemLoader(root string) (*SandboxedFilesystemLoader, error) { 131 fs, err := NewFileSystemLoader(root) 132 if err != nil { 133 return nil, err 134 } 135 return &SandboxedFilesystemLoader{ 136 FilesystemLoader: fs, 137 }, nil 138 } 139 140 // Move sandbox to a virtual fs 141 142 /* 143 if len(set.SandboxDirectories) > 0 { 144 defer func() { 145 // Remove any ".." or other crap 146 resolvedPath = filepath.Clean(resolvedPath) 147 148 // Make the path absolute 149 absPath, err := filepath.Abs(resolvedPath) 150 if err != nil { 151 panic(err) 152 } 153 resolvedPath = absPath 154 155 // Check against the sandbox directories (once one pattern matches, we're done and can allow it) 156 for _, pattern := range set.SandboxDirectories { 157 matched, err := filepath.Match(pattern, resolvedPath) 158 if err != nil { 159 panic("Wrong sandbox directory match pattern (see http://golang.org/pkg/path/filepath/#Match).") 160 } 161 if matched { 162 // OK! 163 return 164 } 165 } 166 167 // No pattern matched, we have to log+deny the request 168 set.logf("Access attempt outside of the sandbox directories (blocked): '%s'", resolvedPath) 169 resolvedPath = "" 170 }() 171 } 172 */ 173