Commit 560d69c6 authored by Keefer Rourke's avatar Keefer Rourke

fix attachment uploads; fix post deletion

Posts via cURL with attachments were failing due to misunderstanding
of golang multipart.File types and how they differ from os.*File types.
Attachments are now properly uploaded to a dynamically created post
directories.

NB: Post directory permissions are set to 755

Post deletion would leave behind old attachments; this is no longer the
case.
parent 51fe7da3
# ---> Tokumei
tokumei
posts.db
public/*
# ---> Vim
[._]*.s[a-w][a-z]
......
......@@ -20,6 +20,7 @@ package mimetype
import (
"errors"
"fmt"
"io/ioutil"
"mime"
"os"
......@@ -73,6 +74,8 @@ var (
// erroneous to write PDF data to a web application but supply this file with a
// .txt extension; since this is misleading to users, it is an error.
func GetFileType(path string) (*FileType, error) {
fmt.Println(path)
fmt.Println("here!!")
// verify the file exists and is not a directory
if f, err := os.Stat(path); os.IsNotExist(err) || f.IsDir() {
return nil, ErrBadFile
......@@ -94,6 +97,7 @@ func GetFileType(path string) (*FileType, error) {
return &ret, ErrBadFile
}
mtype, err := magicLookup(b, ext)
fmt.Println(mtype)
verified := false
if err != nil && err != ErrUnsupportedFile {
return nil, err
......
......@@ -12,12 +12,16 @@ package posts
import (
"database/sql"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"sort"
"strings"
_ "github.com/mattn/go-sqlite3" // sql driver
"gitlab.com/tokumei/tokumei/globals"
"gitlab.com/tokumei/tokumei/timedate"
"golang.org/x/crypto/bcrypt"
)
......@@ -462,5 +466,12 @@ func removePost(tx *sql.Tx, id int64) error {
log.Println(err)
return err
}
// if successful removing post from database, delete any attachments
if len(p.AttachmentUri) > 0 {
postdir := fmt.Sprintf("%s/%d", globals.POSTDIR, id)
os.RemoveAll(filepath.FromSlash(postdir))
}
return nil
}
......@@ -169,7 +169,7 @@ func (p *Post) Finalize() (password string, err error) {
p.Id += 1
// check attachment files exist if present then move to public dir
dir := fmt.Sprintf("%s/%d", globals.POSTDIR, p.Id)
dir := filepath.FromSlash(fmt.Sprintf("%s/%d", globals.POSTDIR, p.Id))
if p.tempfiles != nil {
for _, tmpf := range p.tempfiles {
if fstat, err := os.Stat(tmpf); os.IsNotExist(err) || !fstat.Mode().IsRegular() {
......@@ -180,7 +180,7 @@ func (p *Post) Finalize() (password string, err error) {
return "", err
}
// create destination file
err = os.MkdirAll(filepath.FromSlash(dir), os.ModeDir)
err = os.MkdirAll(dir, 0755)
if err != nil {
return "", err
}
......@@ -193,9 +193,11 @@ func (p *Post) Finalize() (password string, err error) {
return "", err
}
src.Close()
os.Remove(tmpf)
// add proper attachment path to list of URIs
p.AttachmentUri = append(p.AttachmentUri, "/"+filepath.ToSlash(attachment.Name()))
uri := strings.TrimPrefix(filepath.ToSlash(attachment.Name()), "public")
p.AttachmentUri = append(p.AttachmentUri, uri)
}
}
p.isFinal = true
......
......@@ -63,11 +63,12 @@ func listenForPosts() {
for {
p := <-postChan
if p != nil {
fmt.Print("got post:", p)
delcode, err := p.Finalize()
if err != nil {
log.Printf("post queue: %s\n", err.Error())
continue
}
fmt.Print("got post:", p)
err = posts.AddPost(p, delcode)
if err != nil {
log.Println(err)
......
......@@ -11,11 +11,15 @@
package srv
import (
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
......@@ -186,28 +190,45 @@ func makePostFromReq(r *http.Request) (*posts.Post, error) {
// check attachment num constraints ~
if len(fhdrs) > MAX_ATTACHMENTS || len(fhdrs) < MIN_ATTACHMENTS {
log.Println("post rejected because message has invalid number of attachments")
return nil, errUnprocessableEntity
}
// TODO(krourke/kfarwell) discard original file names; strip metadata
// this is particularly challenging and may require we write a new package
for _, fh := range fhdrs {
f, err := fh.Open()
fmt.Println(fh.Filename)
// discard uploaded filename; use md5sum of filename
ext := filepath.Ext(fh.Filename)
hasher := md5.New()
io.WriteString(hasher, fh.Filename)
filename := hex.EncodeToString(hasher.Sum(nil)) + ext
// copy file to temporary location
attachedFile, err := fh.Open()
if err != nil {
log.Printf("could not open post attachment file: %s\n", err.Error())
return nil, errInternalServerError
}
f, err := os.Create(filepath.FromSlash("/tmp/" + filename))
if err != nil {
log.Printf("could not create temporary file from attachment: %s\n", err.Error())
return nil, errInternalServerError
}
io.Copy(f, attachedFile)
attachedFile.Close()
// check file is allowed mimetype and reject unverified files
if ftyp, err := mimetype.GetFileType(f.(*os.File).Name()); err != nil {
if ftyp, err := mimetype.GetFileType(f.Name()); err != nil {
log.Printf("rejected file %s because file type could not be determined\n", filename)
log.Println(err)
return nil, errUnsupportedMediaType
} else if !ftyp.VerifiedSignature {
log.Printf("rejected file %s because file type could not be verified\n", f.(*os.File).Name())
log.Printf("rejected file %s because file type could not be verified\n", filename)
return nil, errUnsupportedMediaType
} else if ftyp.Size > Conf.PostConf.MaxFileSize {
log.Printf("rejected file %s because file is too larger than %dB\n", Conf.PostConf.MaxFileSize)
return nil, errRequestEntityTooLarge
}
// get file name and append to attachment slice
attachments = append(attachments, f.(*os.File).Name())
attachments = append(attachments, f.Name())
f.Close()
}
// make a new Post
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment