template_loader.go 6.0 KB

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