2020-12-16 08:01:46 +00:00
|
|
|
|
2020-12-12 08:32:18 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-12-14 15:48:20 +00:00
|
|
|
valid "github.com/asaskevich/govalidator"
|
2020-12-12 08:32:18 +00:00
|
|
|
"github.com/scottleedavis/go-exif-remove"
|
|
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
2020-12-14 04:50:15 +00:00
|
|
|
"github.com/prologic/bitcask"
|
2020-12-12 08:32:18 +00:00
|
|
|
"github.com/twharmon/gouid"
|
2020-12-12 15:24:45 +00:00
|
|
|
"github.com/gin-gonic/gin"
|
2020-12-12 08:32:18 +00:00
|
|
|
"io/ioutil"
|
2020-12-16 08:01:46 +00:00
|
|
|
"crypto/md5"
|
2020-12-12 08:32:18 +00:00
|
|
|
"net/http"
|
|
|
|
"image"
|
2020-12-14 15:48:20 +00:00
|
|
|
"bytes"
|
2020-12-12 08:32:18 +00:00
|
|
|
"fmt"
|
|
|
|
"log"
|
2020-12-12 15:24:45 +00:00
|
|
|
"io"
|
2020-12-12 08:32:18 +00:00
|
|
|
)
|
|
|
|
|
2020-12-14 04:50:15 +00:00
|
|
|
var imgDB *bitcask.Bitcask
|
2020-12-16 08:01:46 +00:00
|
|
|
var md5DB *bitcask.Bitcask
|
2020-12-14 04:50:15 +00:00
|
|
|
var urlDB *bitcask.Bitcask
|
|
|
|
var txtDB *bitcask.Bitcask
|
2020-12-12 08:32:18 +00:00
|
|
|
var errLog *log.Logger
|
2020-12-16 08:01:46 +00:00
|
|
|
var baseUrl string = "https://tcp.ac/"
|
2020-12-12 08:32:18 +00:00
|
|
|
var debugBool bool = true
|
|
|
|
|
2020-12-12 15:24:45 +00:00
|
|
|
func errThrow(c *gin.Context, respcode int, Error string, msg string) {
|
|
|
|
errLog.Println(c.ClientIP() + ": " + Error)
|
2020-12-12 08:32:18 +00:00
|
|
|
if debugBool {
|
2020-12-12 15:24:45 +00:00
|
|
|
c.String(respcode, msg)
|
2020-12-12 08:32:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-14 15:48:20 +00:00
|
|
|
func imgView(c *gin.Context) {
|
|
|
|
rUid := c.Param("uid")
|
|
|
|
fmt.Println("[imgView] Received request")
|
|
|
|
if (valid.IsAlphanumeric(rUid)) {
|
|
|
|
fmt.Println("[imgView][" + rUid + "] Request validated")
|
|
|
|
fBytes, _ := imgDB.Get([]byte(rUid))
|
|
|
|
if fBytes == nil {
|
|
|
|
fmt.Println("[imgView] No data found for: " + rUid)
|
|
|
|
errThrow(c, 404, "404", "File not found")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("[imgView][" + rUid + "] Detecting image type")
|
|
|
|
file := bytes.NewReader(fBytes)
|
|
|
|
imageFormat, ok := checkImage(file)
|
|
|
|
if !ok {
|
|
|
|
errThrow(c, http.StatusBadRequest, "bad request", "content does not appear to be an image")
|
|
|
|
return
|
|
|
|
} else { fmt.Println("[imgView][" + rUid + "] " + imageFormat + " detected") }
|
|
|
|
|
|
|
|
contentType := "image/" + imageFormat
|
|
|
|
|
|
|
|
c.Data(200, contentType, fBytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-12 15:24:45 +00:00
|
|
|
func imgPost(c *gin.Context) {
|
|
|
|
f, err := c.FormFile("upload")
|
2020-12-12 08:32:18 +00:00
|
|
|
if err != nil {
|
2020-12-12 15:24:45 +00:00
|
|
|
errThrow(c, http.StatusBadRequest, err.Error(), "no file detected within request")
|
2020-12-12 08:32:18 +00:00
|
|
|
}
|
|
|
|
|
2020-12-12 15:24:45 +00:00
|
|
|
fmt.Println("[imgPost] detected new upload: " + f.Filename)
|
|
|
|
|
|
|
|
file, err := f.Open()
|
2020-12-12 08:32:18 +00:00
|
|
|
if err != nil {
|
2020-12-12 15:24:45 +00:00
|
|
|
errThrow(c, http.StatusInternalServerError, err.Error(), "error processing file")
|
2020-12-12 08:32:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("[imgPost] verifying file is an image")
|
2020-12-12 15:24:45 +00:00
|
|
|
imageFormat, ok := checkImage(file)
|
2020-12-12 08:32:18 +00:00
|
|
|
if !ok {
|
2020-12-16 08:01:46 +00:00
|
|
|
errThrow(c, http.StatusBadRequest, "400", "input does not appear to be an image")
|
2020-12-12 08:32:18 +00:00
|
|
|
return
|
2020-12-14 04:50:15 +00:00
|
|
|
} else { fmt.Println("[imgPost] " + imageFormat + " detected") }
|
2020-12-12 08:32:18 +00:00
|
|
|
|
|
|
|
fmt.Println("[imgPost] generating uid")
|
2020-12-16 08:01:46 +00:00
|
|
|
uid := gouid.String(4)
|
2020-12-12 08:32:18 +00:00
|
|
|
|
|
|
|
fmt.Println("[imgPost][" + uid + "] dumping byte form of file and scrubbing exif")
|
2020-12-12 15:24:45 +00:00
|
|
|
fbytes, err := ioutil.ReadAll(file)
|
2020-12-12 08:32:18 +00:00
|
|
|
Scrubbed, err := exifremove.Remove(fbytes)
|
2020-12-12 15:24:45 +00:00
|
|
|
if err != nil {
|
|
|
|
errThrow(c, http.StatusInternalServerError, err.Error(), "error scrubbing exif")
|
2020-12-12 08:32:18 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-16 08:01:46 +00:00
|
|
|
fmt.Println("[imgPost][" + uid + "] calculating MD5 hash")
|
|
|
|
|
|
|
|
Hashr := md5.New()
|
|
|
|
Hashr.Write(Scrubbed)
|
|
|
|
hash := Hashr.Sum(nil)
|
|
|
|
|
|
|
|
fmt.Println("[imgPost][" + uid + "] " + string(hash))
|
|
|
|
|
|
|
|
fmt.Println("[imgPost][" + uid + "] Checking for duplicate's in database")
|
|
|
|
|
|
|
|
imgRef, _ := md5DB.Get(hash)
|
|
|
|
|
|
|
|
if imgRef == nil {
|
|
|
|
fmt.Println("[imgPost][" + uid + "] no dupes found, storing md5 hash into md5 database with callback uid")
|
|
|
|
md5DB.Put([]byte(hash),[]byte(uid))
|
|
|
|
} else {
|
|
|
|
fmt.Println("[imgPost][" + uid + "] duplicate file found in md5 database, returning URL for uid: " + string(imgRef))
|
|
|
|
c.String(200,baseUrl + "i/" + string(imgRef))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("[imgPost][" + uid + "] saving file to database")
|
2020-12-12 08:32:18 +00:00
|
|
|
|
2020-12-14 15:48:20 +00:00
|
|
|
err = imgDB.Put([]byte(uid), []byte(Scrubbed))
|
2020-12-14 04:50:15 +00:00
|
|
|
|
2020-12-12 08:32:18 +00:00
|
|
|
if err != nil {
|
2020-12-12 15:24:45 +00:00
|
|
|
errThrow(c, http.StatusInternalServerError, err.Error(), "error saving file")
|
2020-12-14 15:48:20 +00:00
|
|
|
fmt.Println(err.Error())
|
2020-12-12 08:32:18 +00:00
|
|
|
return
|
2020-12-16 08:01:46 +00:00
|
|
|
} else {
|
|
|
|
fmt.Println("[imgPost][" + uid + "] saved to database successfully, returning new URL")
|
|
|
|
}
|
2020-12-12 08:32:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func checkImage(r io.ReadSeeker) (string, bool) {
|
|
|
|
_, fmt, err := image.Decode(r)
|
|
|
|
_, err2 := r.Seek(0, 0)
|
|
|
|
if err != nil || err2 != nil {
|
|
|
|
return "", false
|
2020-12-14 15:48:20 +00:00
|
|
|
}
|
2020-12-12 08:32:18 +00:00
|
|
|
return fmt, true
|
|
|
|
}
|
|
|
|
|
|
|
|
func getSize(s io.Seeker) (size int64, err error) {
|
|
|
|
if _, err = s.Seek(0, 0); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2 == from the end of the file
|
|
|
|
if size, err = s.Seek(0, 2); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = s.Seek(0, 0)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-12 15:24:45 +00:00
|
|
|
//////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
func txtPost(c *gin.Context) {
|
|
|
|
return
|
|
|
|
}
|
2020-12-12 08:32:18 +00:00
|
|
|
|
2020-12-12 15:24:45 +00:00
|
|
|
//////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
func urlPost(c *gin.Context) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-12-14 04:50:15 +00:00
|
|
|
//////////////////////////////////////////////////////
|
2020-12-12 15:24:45 +00:00
|
|
|
|
|
|
|
func init() {
|
2020-12-14 04:50:15 +00:00
|
|
|
|
|
|
|
fmt.Println("Initializing...")
|
|
|
|
|
|
|
|
//////////// init logging ////////////
|
|
|
|
fmt.Println("Starting error logger")
|
|
|
|
Logger := &lumberjack.Logger{
|
2020-12-12 15:24:45 +00:00
|
|
|
Filename: "error.log",
|
|
|
|
MaxSize: 50, // megabytes
|
|
|
|
MaxBackups: 8,
|
|
|
|
MaxAge: 28, // days
|
|
|
|
Compress: true,
|
|
|
|
}
|
|
|
|
|
2020-12-14 04:50:15 +00:00
|
|
|
errLog = log.New(Logger, "", log.Ldate|log.Ltime|log.Lshortfile)
|
|
|
|
/////////////////////////////////////
|
|
|
|
|
|
|
|
/////////// init databases //////////
|
2020-12-14 15:48:20 +00:00
|
|
|
opts := []bitcask.Option {
|
|
|
|
bitcask.WithMaxValueSize(16 / 1024 / 1024),
|
|
|
|
}
|
|
|
|
imgDB, _ = bitcask.Open("img.db", opts...)
|
2020-12-14 04:50:15 +00:00
|
|
|
fmt.Println("Opening img.db")
|
|
|
|
|
2020-12-16 08:01:46 +00:00
|
|
|
md5DB, _ = bitcask.Open("md5.db", opts...) // this will probably only be for images
|
|
|
|
fmt.Println("Opening md5.db")
|
|
|
|
|
2020-12-14 04:50:15 +00:00
|
|
|
txtDB, _ = bitcask.Open("txt.db")
|
|
|
|
fmt.Println("Opening txt.db")
|
|
|
|
|
|
|
|
urlDB, _ = bitcask.Open("url.db")
|
|
|
|
fmt.Println("Opening url.db")
|
|
|
|
////////////////////////////////////
|
2020-12-12 15:24:45 +00:00
|
|
|
}
|
2020-12-12 08:32:18 +00:00
|
|
|
|
2020-12-14 04:50:15 +00:00
|
|
|
|
|
|
|
|
2020-12-12 08:32:18 +00:00
|
|
|
func main() {
|
2020-12-12 15:24:45 +00:00
|
|
|
router := gin.Default()
|
2020-12-12 08:32:18 +00:00
|
|
|
|
2020-12-14 04:50:15 +00:00
|
|
|
router.MaxMultipartMemory = 16 << 20
|
2020-12-12 17:28:23 +00:00
|
|
|
|
2020-12-12 15:24:45 +00:00
|
|
|
imgR := router.Group("/i")
|
|
|
|
{
|
|
|
|
imgR.POST("/put", imgPost)
|
2020-12-14 15:48:20 +00:00
|
|
|
imgR.GET("/:uid", imgView)
|
2020-12-12 15:24:45 +00:00
|
|
|
}
|
2020-12-12 08:32:18 +00:00
|
|
|
|
2020-12-12 15:24:45 +00:00
|
|
|
txtR := router.Group("/t")
|
|
|
|
{
|
|
|
|
txtR.POST("/put", txtPost)
|
|
|
|
}
|
|
|
|
|
|
|
|
urlR := router.Group("/u")
|
|
|
|
{
|
|
|
|
urlR.POST("/put", urlPost)
|
|
|
|
}
|
2020-12-12 08:32:18 +00:00
|
|
|
|
2020-12-12 15:24:45 +00:00
|
|
|
router.Run(":8081")
|
2020-12-12 08:32:18 +00:00
|
|
|
}
|