feat: create v2 for module support
All checks were successful
Deploy / deploy (push) Successful in 1m21s
All checks were successful
Deploy / deploy (push) Successful in 1m21s
This commit is contained in:
188
v2/paste/paste.go
Normal file
188
v2/paste/paste.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package paste
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.sour.is/paste/src/pkg/readutil"
|
||||
"golang.org/x/sys/unix"
|
||||
"sour.is/x/toolbox/log"
|
||||
)
|
||||
|
||||
type Paste struct {
|
||||
store string
|
||||
}
|
||||
|
||||
func New(store string) (p *Paste, err error) {
|
||||
p = &Paste{}
|
||||
p.store = store
|
||||
|
||||
if !chkStore(p.store) {
|
||||
err = fmt.Errorf("paste: store location [%s] does not exist or is not writable", p.store)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func chkStore(path string) bool {
|
||||
file, err := os.Stat(path)
|
||||
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
err = os.MkdirAll(path, 0744)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
file, err = os.Stat(path)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !file.IsDir() {
|
||||
return false
|
||||
}
|
||||
|
||||
if unix.Access(path, unix.W_OK&unix.R_OK) != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func chkFile(path string) bool {
|
||||
file, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
return false
|
||||
}
|
||||
|
||||
if unix.Access(path, unix.W_OK&unix.R_OK) != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func chkGone(path string) bool {
|
||||
file, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if file.Size() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Paste) Get(w io.Writer, id string) error {
|
||||
fname := filepath.Join(p.store, id)
|
||||
|
||||
if !chkFile(fname) {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
if chkGone(fname) {
|
||||
return ErrGone
|
||||
}
|
||||
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %w", ErrReadingContent, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
pr := readutil.NewPreviewReader(f)
|
||||
|
||||
keep := true
|
||||
scanner := bufio.NewScanner(pr)
|
||||
for scanner.Scan() {
|
||||
txt := scanner.Text()
|
||||
log.Debug(txt)
|
||||
|
||||
// End of header found
|
||||
if txt == "" {
|
||||
break
|
||||
}
|
||||
|
||||
if strings.HasPrefix(txt, "exp:") {
|
||||
now := time.Now().Unix()
|
||||
exp, err := strconv.ParseInt(strings.TrimSpace(strings.TrimPrefix(txt, "exp:")), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %w", ErrReadingContent, err)
|
||||
}
|
||||
|
||||
log.Debugf("%d > %d", now, exp)
|
||||
if now > exp {
|
||||
deleteFile(fname)
|
||||
return ErrGone
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(txt, "burn:") {
|
||||
burn := strings.TrimSpace(strings.TrimPrefix(txt, "burn:"))
|
||||
|
||||
if burn == "true" {
|
||||
keep = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = io.Copy(w, pr.Drain())
|
||||
|
||||
if !keep {
|
||||
deleteFile(fname)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Paste) Set(r io.ReadCloser) (string, error) {
|
||||
defer r.Close()
|
||||
|
||||
rdr := io.LimitReader(r, 1048576)
|
||||
|
||||
s256 := sha256.New()
|
||||
tmp, _ := os.CreateTemp(p.store, "arch-")
|
||||
defer os.Remove(tmp.Name())
|
||||
|
||||
m := io.MultiWriter(s256, tmp)
|
||||
if _, err := io.Copy(m, rdr); err != nil {
|
||||
return "", fmt.Errorf("%w: %w", ErrBadInput, err)
|
||||
}
|
||||
tmp.Close()
|
||||
|
||||
id := base64.RawURLEncoding.EncodeToString(s256.Sum(nil)[12:])
|
||||
fname := filepath.Join(p.store, id)
|
||||
|
||||
_ = os.Rename(tmp.Name(), fname)
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func deleteFile(path string) {
|
||||
_ = ioutil.WriteFile(path, nil, 0644)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrGone = errors.New("gone")
|
||||
ErrReadingContent = errors.New("reading content")
|
||||
ErrBadInput = errors.New("bad input")
|
||||
)
|
||||
Reference in New Issue
Block a user