added base path filter
This commit is contained in:
parent
98b53d3a33
commit
ec5a376733
15 changed files with 104 additions and 44 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
package core
|
package core
|
||||||
|
|
||||||
var (
|
const (
|
||||||
IndexURI = "/"
|
IndexURI = "/"
|
||||||
TotpURI = "/totp"
|
TotpURI = "/totp"
|
||||||
LoginURI = "/login"
|
LoginURI = "/login"
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
const (
|
|
||||||
timeFmt = "2006-01-02 15:04:05"
|
|
||||||
fileFmt = "20060102_150405"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Now() string {
|
const (
|
||||||
return time.Now().Format(timeFmt)
|
fileFmt = "20060102_150405"
|
||||||
}
|
)
|
||||||
|
|
||||||
func FileName() string {
|
func FileName() string {
|
||||||
return time.Now().Format(fileFmt)
|
return time.Now().Format(fileFmt)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ type Result struct {
|
||||||
HTML template.HTML
|
HTML template.HTML
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Results []Result
|
||||||
|
|
||||||
type Path struct {
|
type Path struct {
|
||||||
Name string
|
Name string
|
||||||
Abs string
|
Abs string
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,10 @@ func (fs *Filesystem) NewFile(r io.Reader, path, orig, name string) (newfile str
|
||||||
name = orig
|
name = orig
|
||||||
}
|
}
|
||||||
newfile = filepath.Join(path, name)
|
newfile = filepath.Join(path, name)
|
||||||
|
err = checkPath(newfile)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
f, err := os.OpenFile(filepath.Join(fs.Base, newfile), os.O_RDWR|os.O_CREATE, FileMode)
|
f, err := os.OpenFile(filepath.Join(fs.Base, newfile), os.O_RDWR|os.O_CREATE, FileMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -64,7 +68,12 @@ func (fs *Filesystem) NewFile(r io.Reader, path, orig, name string) (newfile str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) Mkdir(path, name string) error {
|
func (fs *Filesystem) Mkdir(path, name string) error {
|
||||||
return os.Mkdir(filepath.Join(fs.Base, Clean(path), Clean(name)), DirMode)
|
dirpath := filepath.Join(Clean(path), Clean(name))
|
||||||
|
err := checkPath(dirpath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Mkdir(filepath.Join(fs.Base, dirpath), DirMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) Move(path, dest, name string) (string, error) {
|
func (fs *Filesystem) Move(path, dest, name string) (string, error) {
|
||||||
|
|
@ -75,7 +84,11 @@ func (fs *Filesystem) Move(path, dest, name string) (string, error) {
|
||||||
if fullpath == fs.Base {
|
if fullpath == fs.Base {
|
||||||
return "", errors.New("the root path can not be moved")
|
return "", errors.New("the root path can not be moved")
|
||||||
}
|
}
|
||||||
newfile := Clean(filepath.Join(dest, name))
|
newfile := filepath.Join(Clean(dest), Clean(name))
|
||||||
|
err := checkPath(newfile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
return newfile, os.Rename(fullpath, filepath.Join(fs.Base, newfile))
|
return newfile, os.Rename(fullpath, filepath.Join(fs.Base, newfile))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import "path/filepath"
|
import (
|
||||||
|
"errors"
|
||||||
|
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
const sep = "/"
|
const sep = "/"
|
||||||
|
|
||||||
|
|
@ -12,3 +17,27 @@ func File(path string) string {
|
||||||
_, f := filepath.Split(Clean(path))
|
_, f := filepath.Split(Clean(path))
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reserved = []string{
|
||||||
|
core.TotpURI,
|
||||||
|
core.LoginURI,
|
||||||
|
core.LogoutURI,
|
||||||
|
core.RawPrefix,
|
||||||
|
core.IndexPrefix,
|
||||||
|
core.UploadPrefix,
|
||||||
|
core.NewPrefix,
|
||||||
|
core.MovePrefix,
|
||||||
|
core.DeletePrefix,
|
||||||
|
core.Favicon,
|
||||||
|
core.BootstrapCSS,
|
||||||
|
core.CustomCSS,
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPath(path string) error {
|
||||||
|
for _, p := range reserved {
|
||||||
|
if strings.HasPrefix(path, p) {
|
||||||
|
return errors.New("base path '" + p + "' is reserved")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ func (i *Index) Delete(id string) error {
|
||||||
return i.i.Delete(id)
|
return i.i.Delete(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Index) Search(search string) (results []core.Result, err error) {
|
func (i *Index) Search(search string) (results core.Results, err error) {
|
||||||
req := bleve.NewSearchRequest(bleve.NewQueryStringQuery(search))
|
req := bleve.NewSearchRequest(bleve.NewQueryStringQuery(search))
|
||||||
req.Highlight = bleve.NewHighlightWithStyle("html")
|
req.Highlight = bleve.NewHighlightWithStyle("html")
|
||||||
req.Size = maxSearchResult
|
req.Size = maxSearchResult
|
||||||
|
|
@ -98,18 +98,17 @@ func (i *Index) Search(search string) (results []core.Result, err error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for fragField, frags := range hit.Fragments {
|
for fragField, frags := range hit.Fragments {
|
||||||
if fragField == "text" || fragField == "created" {
|
|
||||||
for _, f := range frags {
|
|
||||||
r.HTML += template.HTML(f)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if fragField == "keyword" {
|
if fragField == "keyword" {
|
||||||
var k string
|
var k string
|
||||||
for _, f := range frags {
|
for _, f := range frags {
|
||||||
k += f
|
k += f
|
||||||
}
|
}
|
||||||
r.Keyword = strings.ReplaceAll(strings.ReplaceAll(k, "<mark>", ""), "</mark>", "")
|
r.Keyword = strings.ReplaceAll(strings.ReplaceAll(k, "<mark>", ""), "</mark>", "")
|
||||||
|
}
|
||||||
|
if fragField == "text" || fragField == "created" {
|
||||||
|
for _, f := range frags {
|
||||||
|
r.HTML += template.HTML(f)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package scan
|
|
||||||
|
|
||||||
type PDF struct {
|
|
||||||
cmd string
|
|
||||||
args []string
|
|
||||||
}
|
|
||||||
|
|
@ -17,6 +17,19 @@ import (
|
||||||
|
|
||||||
const pdfExt = ".pdf"
|
const pdfExt = ".pdf"
|
||||||
|
|
||||||
|
type PDF struct {
|
||||||
|
cmd string
|
||||||
|
args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultLangs = []string{"eng", "deu"}
|
||||||
|
|
||||||
|
type Tesseract struct {
|
||||||
|
cmd string
|
||||||
|
args []string
|
||||||
|
env []string
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
wsr = regexp.MustCompile(`[\t\f ]+`)
|
wsr = regexp.MustCompile(`[\t\f ]+`)
|
||||||
nlr = regexp.MustCompile(`[\n]{3,}`)
|
nlr = regexp.MustCompile(`[\n]{3,}`)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package scan
|
|
||||||
|
|
||||||
var defaultLangs = []string{"eng", "deu"}
|
|
||||||
|
|
||||||
type Tesseract struct {
|
|
||||||
cmd string
|
|
||||||
args []string
|
|
||||||
env []string
|
|
||||||
}
|
|
||||||
|
|
@ -15,8 +15,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//TODO add missing CSRF checks
|
|
||||||
|
|
||||||
const noSuchFile = "no such file or directory"
|
const noSuchFile = "no such file or directory"
|
||||||
|
|
||||||
type notFoundHandler struct {
|
type notFoundHandler struct {
|
||||||
|
|
@ -95,7 +93,6 @@ func authHandler(h ctxHandler) ctxHandler {
|
||||||
h(ctx)
|
h(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//ctx.Forbidden()
|
|
||||||
ctx.Redirect(core.LoginURI, http.StatusFound)
|
ctx.Redirect(core.LoginURI, http.StatusFound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,6 +226,9 @@ func uploadHandler(ctx *Context) {
|
||||||
case "GET":
|
case "GET":
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
case "POST":
|
case "POST":
|
||||||
|
if !ctx.CheckXsrf() {
|
||||||
|
return
|
||||||
|
}
|
||||||
f, orig, err := ctx.File("file")
|
f, orig, err := ctx.File("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
|
|
@ -255,6 +255,9 @@ func newDirHandler(ctx *Context) {
|
||||||
case "GET":
|
case "GET":
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
case "POST":
|
case "POST":
|
||||||
|
if !ctx.CheckXsrf() {
|
||||||
|
return
|
||||||
|
}
|
||||||
err := ctx.Srv.FS.Mkdir(path, ctx.Form("name"))
|
err := ctx.Srv.FS.Mkdir(path, ctx.Form("name"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
|
|
@ -298,6 +301,9 @@ func moveHandler(ctx *Context) {
|
||||||
case "GET":
|
case "GET":
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
case "POST":
|
case "POST":
|
||||||
|
if !ctx.CheckXsrf() {
|
||||||
|
return
|
||||||
|
}
|
||||||
name := ctx.Form("name")
|
name := ctx.Form("name")
|
||||||
dest := ctx.Form("destination")
|
dest := ctx.Form("destination")
|
||||||
newfile, err := ctx.Srv.FS.Move(path, dest, name)
|
newfile, err := ctx.Srv.FS.Move(path, dest, name)
|
||||||
|
|
@ -347,6 +353,9 @@ func deleteHandler(ctx *Context) {
|
||||||
case "GET":
|
case "GET":
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
case "POST":
|
case "POST":
|
||||||
|
if !ctx.CheckXsrf() {
|
||||||
|
return
|
||||||
|
}
|
||||||
files, err := ctx.Srv.FS.Delete(path)
|
files, err := ctx.Srv.FS.Delete(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(err)
|
ctx.Error(err)
|
||||||
|
|
@ -405,6 +414,11 @@ func dirHandler(ctx *Context) {
|
||||||
}
|
}
|
||||||
ctx.Data.Paths = fs.Paths(path)
|
ctx.Data.Paths = fs.Paths(path)
|
||||||
ctx.Exec()
|
ctx.Exec()
|
||||||
|
default:
|
||||||
|
ctx.Data = webData{
|
||||||
|
Title: "Viewer",
|
||||||
|
}
|
||||||
|
ctx.Error(noSuchFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -638,5 +652,5 @@ func logoutHandler(ctx *Context) {
|
||||||
log.Println("logout:", user)
|
log.Println("logout:", user)
|
||||||
ctx.Srv.JWT.Invalidate(&ctx.Token)
|
ctx.Srv.JWT.Invalidate(&ctx.Token)
|
||||||
ctx.SetCookie(nil, 0)
|
ctx.SetCookie(nil, 0)
|
||||||
ctx.Redirect(core.IndexURI, http.StatusFound)
|
ctx.Redirect(core.LoginURI, http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ const (
|
||||||
</ol>
|
</ol>
|
||||||
<div class="btn-group btn-breadcrumb">
|
<div class="btn-group btn-breadcrumb">
|
||||||
<a href="/upload{{.Path}}" class="btn btn-sm btn-success">Upload</a>
|
<a href="/upload{{.Path}}" class="btn btn-sm btn-success">Upload</a>
|
||||||
<a href="/new{{.Path}}" class="btn btn-sm btn-primary">New Directory</a>
|
<a href="/new{{.Path}}" class="btn btn-sm btn-primary">New</a>
|
||||||
<a href="/move{{.Path}}" class="btn btn-sm btn-primary">Move</a>
|
<a href="/move{{.Path}}" class="btn btn-sm btn-primary">Move</a>
|
||||||
<a href="/delete{{.Path}}" class="btn btn-sm btn-danger">Delete</a>
|
<a href="/delete{{.Path}}" class="btn btn-sm btn-danger">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -139,7 +139,7 @@ const (
|
||||||
<title>DocStore | {{.Title}}</title>
|
<title>DocStore | {{.Title}}</title>
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link>
|
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-L50A9TBaaysKaYOQWW5+H5nHTNLFLH4+F7Au3o4enAM="></link>
|
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-VMvzVve7KY7ia4G8uBrASNEZ/Qc/5jCqVV3WWU+Y9ao="></link>
|
||||||
<meta name="theme-color" content="#375a7f">
|
<meta name="theme-color" content="#375a7f">
|
||||||
<meta name="description" content="{{.Title}}">
|
<meta name="description" content="{{.Title}}">
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -365,7 +365,7 @@ const (
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col">
|
<div class="col col-nopad">
|
||||||
<form class="form-horizontal" action="/search" method="post">
|
<form class="form-horizontal" action="/search" method="post">
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
@ -397,7 +397,7 @@ const (
|
||||||
<br><pre class="wrap">{{$item.HTML}}</pre>
|
<br><pre class="wrap">{{$item.HTML}}</pre>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td>{{if $item.Keyword}}<span class="badge badge-primary">{{$item.Keyword}}</span>{{end}}</td>
|
<td>{{$item.Keyword}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
@ -661,6 +661,10 @@ html, body, .container, .content {
|
||||||
.full-width {
|
.full-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.col-nopad {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
.relative {
|
.relative {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,10 @@ html, body, .container, .content {
|
||||||
.full-width {
|
.full-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.col-nopad {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
.relative {
|
.relative {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
</ol>
|
</ol>
|
||||||
<div class="btn-group btn-breadcrumb">
|
<div class="btn-group btn-breadcrumb">
|
||||||
<a href="/upload{{.Path}}" class="btn btn-sm btn-success">Upload</a>
|
<a href="/upload{{.Path}}" class="btn btn-sm btn-success">Upload</a>
|
||||||
<a href="/new{{.Path}}" class="btn btn-sm btn-primary">New Directory</a>
|
<a href="/new{{.Path}}" class="btn btn-sm btn-primary">New</a>
|
||||||
<a href="/move{{.Path}}" class="btn btn-sm btn-primary">Move</a>
|
<a href="/move{{.Path}}" class="btn btn-sm btn-primary">Move</a>
|
||||||
<a href="/delete{{.Path}}" class="btn btn-sm btn-danger">Delete</a>
|
<a href="/delete{{.Path}}" class="btn btn-sm btn-danger">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<title>DocStore | {{.Title}}</title>
|
<title>DocStore | {{.Title}}</title>
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link>
|
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link>
|
||||||
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-L50A9TBaaysKaYOQWW5+H5nHTNLFLH4+F7Au3o4enAM="></link>
|
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-VMvzVve7KY7ia4G8uBrASNEZ/Qc/5jCqVV3WWU+Y9ao="></link>
|
||||||
<meta name="theme-color" content="#375a7f">
|
<meta name="theme-color" content="#375a7f">
|
||||||
<meta name="description" content="{{.Title}}">
|
<meta name="description" content="{{.Title}}">
|
||||||
</head>
|
</head>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col">
|
<div class="col col-nopad">
|
||||||
<form class="form-horizontal" action="/search" method="post">
|
<form class="form-horizontal" action="/search" method="post">
|
||||||
<input type="hidden" name="token" value="{{.Token}}">
|
<input type="hidden" name="token" value="{{.Token}}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
<br><pre class="wrap">{{$item.HTML}}</pre>
|
<br><pre class="wrap">{{$item.HTML}}</pre>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td>{{if $item.Keyword}}<span class="badge badge-primary">{{$item.Keyword}}</span>{{end}}</td>
|
<td>{{$item.Keyword}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue