mirror of
https://github.com/lyx0/yaf.git
synced 2024-11-13 19:49:53 +01:00
e0afb453a5
EXIF scrubbing can be enabled via the `ScrubExif` config key. When
enabled, all standard EXIF tags (as defined by the IFD mappings in the
go-exif library) are removed on uploaded JPEG and PNG images.
The `ExifAllowedIds` and `ExifAllowedPaths` config keys can be used to
selectively allow specific tags to survive the scrubbing. This can be
useful if you want to preserve image orientation information for
example. The IDs for standard tags can be found in [1].
The path specification for `ExifAllowedPaths` relies on the format
implemented in go-exif which is "documented" in machine-readable format
in [2]. Multiple paths can be specified, separated by a space. The
path format is as follows:
1. For tags in the main section: `IFD/<GroupName>/<FieldName>`.
Examples: `IFD/Orientation`, `IFD/Exif/Flash`,
`IFD/GPSInfo/GPSTimeStamp`. You will probably want to use both
[1] and [2] in combination if you plan to specify allowed tags by
path.
2. Tags in the thumbnail section follow the same format but paths
start with `IFD1/` instead of `IFD`.
[1]: https://exiv2.org/tags.html
[2]: a6301f85c8/assets/tags.yaml
138 lines
3.4 KiB
Go
138 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"math/rand"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/leon-richardt/jaf/exifscrubber"
|
|
)
|
|
|
|
type uploadHandler struct {
|
|
config *Config
|
|
exifScrubber *exifscrubber.ExifScrubber
|
|
}
|
|
|
|
func (handler *uploadHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer r.Body.Close()
|
|
|
|
uploadFile, header, err := r.FormFile("file")
|
|
if err != nil {
|
|
http.Error(w, "could not read uploaded file: "+err.Error(), http.StatusBadRequest)
|
|
log.Println(" could not read uploaded file: " + err.Error())
|
|
return
|
|
}
|
|
|
|
fileData, err := io.ReadAll(uploadFile)
|
|
uploadFile.Close()
|
|
if err != nil {
|
|
http.Error(w, "could not read attached file: "+err.Error(), http.StatusInternalServerError)
|
|
log.Println(" could not read attached file: " + err.Error())
|
|
return
|
|
}
|
|
|
|
// Scrub EXIF, if requested and detectable by us
|
|
if handler.config.ScrubExif {
|
|
scrubbedData, err := handler.exifScrubber.ScrubExif(fileData[:])
|
|
|
|
if err == nil {
|
|
// If scrubbing was successful, update what to write to file
|
|
fileData = scrubbedData
|
|
} else {
|
|
// Unknown file types (not PNG or JPEG) are allowed to contain EXIF, as we don't know
|
|
// how to handle them. Handling of other errors depends on configuration.
|
|
if err != exifscrubber.ErrUnknownFileType {
|
|
if handler.config.ExifAbortOnError {
|
|
log.Printf("could not scrub EXIF from file, aborting upload: %s", err.Error())
|
|
http.Error(
|
|
w,
|
|
"could not scrub EXIF from file: "+err.Error(),
|
|
http.StatusInternalServerError,
|
|
)
|
|
return
|
|
}
|
|
|
|
// An error occured but we are configured to proceed with the upload anyway
|
|
log.Printf(
|
|
"could not scrub EXIF from file but proceeding with upload as configured: %s",
|
|
err.Error(),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
_, fileExtension := splitFileName(header.Filename)
|
|
link, err := generateLink(handler, fileData[:], fileExtension)
|
|
if err != nil {
|
|
http.Error(w, "could not save file: "+err.Error(), http.StatusInternalServerError)
|
|
log.Println(" could not save file: " + err.Error())
|
|
return
|
|
}
|
|
|
|
// Implicitly means code 200
|
|
w.Write([]byte(link))
|
|
}
|
|
|
|
// Generates a valid link to uploadFile with the specified file extension.
|
|
// Returns the link or an error in case of failure.
|
|
// Does not close the passed file pointer.
|
|
func generateLink(handler *uploadHandler, fileData []byte, fileExtension string) (string, error) {
|
|
// Find an unused file name
|
|
var fullFileName string
|
|
var savePath string
|
|
for {
|
|
fileStem := createRandomFileName(handler.config.LinkLength)
|
|
fullFileName = fileStem + fileExtension
|
|
savePath = handler.config.FileDir + fullFileName
|
|
|
|
if !fileExists(savePath) {
|
|
break
|
|
}
|
|
}
|
|
|
|
link := handler.config.LinkPrefix + fullFileName
|
|
|
|
err := saveFile(fileData[:], savePath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return link, nil
|
|
}
|
|
|
|
func saveFile(fileData []byte, name string) error {
|
|
err := os.WriteFile(name, fileData, 0o644)
|
|
return err
|
|
}
|
|
|
|
func fileExists(fileName string) bool {
|
|
_, err := os.Stat(fileName)
|
|
|
|
return !errors.Is(err, os.ErrNotExist)
|
|
}
|
|
|
|
func createRandomFileName(length int) string {
|
|
chars := make([]byte, length)
|
|
|
|
for i := 0; i < length; i++ {
|
|
index := rand.Intn(len(allowedChars))
|
|
chars[i] = allowedChars[index]
|
|
}
|
|
|
|
return string(chars)
|
|
}
|
|
|
|
func splitFileName(name string) (string, string) {
|
|
extIndex := strings.LastIndex(name, ".")
|
|
|
|
if extIndex == -1 {
|
|
// No dot at all
|
|
return name, ""
|
|
}
|
|
|
|
return name[:extIndex], name[extIndex:]
|
|
}
|