2023-09-11 15:43:09 +00:00
|
|
|
/*
|
2024-01-14 18:39:14 +00:00
|
|
|
Copyright © 2024 Seednode <seednode@seedno.de>
|
2023-09-11 15:43:09 +00:00
|
|
|
*/
|
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
package images
|
2023-09-11 15:43:09 +00:00
|
|
|
|
|
|
|
import (
|
2023-09-11 17:09:08 +00:00
|
|
|
"errors"
|
2023-09-11 15:43:09 +00:00
|
|
|
"fmt"
|
2023-09-11 17:09:08 +00:00
|
|
|
"image"
|
2023-09-11 15:43:09 +00:00
|
|
|
_ "image/gif"
|
|
|
|
_ "image/jpeg"
|
|
|
|
_ "image/png"
|
2023-10-04 22:33:03 +00:00
|
|
|
"math/rand"
|
2024-01-30 19:07:59 +00:00
|
|
|
"net/http"
|
2023-09-11 17:09:08 +00:00
|
|
|
"os"
|
2023-09-12 21:32:19 +00:00
|
|
|
"strings"
|
2023-09-11 15:43:09 +00:00
|
|
|
|
|
|
|
_ "golang.org/x/image/bmp"
|
|
|
|
_ "golang.org/x/image/webp"
|
2023-09-13 04:35:17 +00:00
|
|
|
"seedno.de/seednode/roulette/types"
|
2023-09-11 15:43:09 +00:00
|
|
|
)
|
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
type dimensions struct {
|
|
|
|
width int
|
|
|
|
height int
|
2023-09-11 17:09:08 +00:00
|
|
|
}
|
|
|
|
|
2023-10-04 22:33:03 +00:00
|
|
|
type Format struct {
|
2024-02-06 22:10:06 +00:00
|
|
|
NoButtons bool
|
|
|
|
Fun bool
|
2023-10-04 22:33:03 +00:00
|
|
|
}
|
2023-09-13 01:56:39 +00:00
|
|
|
|
2024-01-30 19:07:59 +00:00
|
|
|
func (t Format) CSP(w http.ResponseWriter) string {
|
2024-01-30 19:27:45 +00:00
|
|
|
nonce := types.GetNonce()
|
2024-01-30 19:07:59 +00:00
|
|
|
|
|
|
|
w.Header().Add("Content-Security-Policy", fmt.Sprintf("default-src 'self' 'nonce-%s';", nonce))
|
|
|
|
|
|
|
|
return nonce
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t Format) CSS() string {
|
2023-09-13 01:56:39 +00:00
|
|
|
var css strings.Builder
|
|
|
|
|
|
|
|
css.WriteString(`html,body{margin:0;padding:0;height:100%;}`)
|
2024-02-06 22:10:06 +00:00
|
|
|
if t.NoButtons {
|
2023-10-20 22:18:35 +00:00
|
|
|
css.WriteString(`a{color:inherit;display:block;height:100%;width:100%;text-decoration:none;}`)
|
|
|
|
} else {
|
|
|
|
css.WriteString(`a{color:inherit;display:block;height:97%;width:100%;text-decoration:none;}`)
|
|
|
|
}
|
2024-01-30 23:28:13 +00:00
|
|
|
css.WriteString(`table{margin-left:auto;margin-right:auto;}`)
|
2024-05-28 23:30:01 +00:00
|
|
|
css.WriteString(`img{margin:auto;display:block;max-width:96%;max-height:96%;`)
|
2023-10-04 22:33:03 +00:00
|
|
|
css.WriteString(`object-fit:scale-down;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)`)
|
|
|
|
if t.Fun {
|
|
|
|
rotate := rand.Intn(360)
|
|
|
|
|
|
|
|
css.WriteString(fmt.Sprintf(" rotate(%ddeg);", rotate))
|
|
|
|
css.WriteString(fmt.Sprintf("-ms-transform:rotate(%ddeg);", rotate))
|
|
|
|
css.WriteString(fmt.Sprintf("-webkit-transform:rotate(%ddeg);", rotate))
|
|
|
|
css.WriteString(fmt.Sprintf("-moz-transform:rotate(%ddeg);", rotate))
|
|
|
|
css.WriteString(fmt.Sprintf("-o-transform:rotate(%ddeg)", rotate))
|
|
|
|
}
|
|
|
|
css.WriteString(`;}`)
|
2023-09-13 01:56:39 +00:00
|
|
|
|
|
|
|
return css.String()
|
|
|
|
}
|
|
|
|
|
2023-09-15 19:28:21 +00:00
|
|
|
func (t Format) Title(rootUrl, fileUri, filePath, fileName, prefix, mime string) (string, error) {
|
2023-09-13 01:56:39 +00:00
|
|
|
dimensions, err := ImageDimensions(filePath)
|
|
|
|
if err != nil {
|
2023-09-15 19:28:21 +00:00
|
|
|
return "", err
|
2023-09-13 01:56:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf(`<title>%s (%dx%d)</title>`,
|
|
|
|
fileName,
|
2023-09-13 04:35:17 +00:00
|
|
|
dimensions.width,
|
2023-09-15 19:28:21 +00:00
|
|
|
dimensions.height), nil
|
2023-09-13 01:56:39 +00:00
|
|
|
}
|
|
|
|
|
2024-01-30 19:07:59 +00:00
|
|
|
func (t Format) Body(rootUrl, fileUri, filePath, fileName, prefix, mime, nonce string) (string, error) {
|
2023-09-13 01:56:39 +00:00
|
|
|
dimensions, err := ImageDimensions(filePath)
|
|
|
|
if err != nil {
|
2023-09-15 19:28:21 +00:00
|
|
|
return "", err
|
2023-09-11 15:43:09 +00:00
|
|
|
}
|
2023-09-13 01:56:39 +00:00
|
|
|
|
2024-01-30 19:07:59 +00:00
|
|
|
var w strings.Builder
|
|
|
|
|
2024-01-30 23:20:45 +00:00
|
|
|
w.WriteString(fmt.Sprintf(`<a href="%s"><img nonce=%q src="%s" width="%d" height="%d" type="%s" alt="Roulette selected: %s"></a>`,
|
2023-09-14 04:24:29 +00:00
|
|
|
rootUrl,
|
2024-01-30 19:07:59 +00:00
|
|
|
nonce,
|
2023-09-13 01:56:39 +00:00
|
|
|
fileUri,
|
2023-09-13 04:35:17 +00:00
|
|
|
dimensions.width,
|
|
|
|
dimensions.height,
|
2023-09-13 01:56:39 +00:00
|
|
|
mime,
|
2024-01-30 19:07:59 +00:00
|
|
|
fileName))
|
|
|
|
|
|
|
|
return w.String(), nil
|
2023-09-13 01:56:39 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
func (t Format) Extensions() map[string]string {
|
2023-09-13 01:56:39 +00:00
|
|
|
return map[string]string{
|
|
|
|
`.apng`: `image/apng`,
|
|
|
|
`.avif`: `image/avif`,
|
|
|
|
`.bmp`: `image/bmp`,
|
|
|
|
`.gif`: `image/gif`,
|
|
|
|
`.jpg`: `image/jpeg`,
|
|
|
|
`.jpeg`: `image/jpeg`,
|
|
|
|
`.jfif`: `image/jpeg`,
|
|
|
|
`.pjp`: `image/jpeg`,
|
|
|
|
`.pjpeg`: `image/jpeg`,
|
|
|
|
`.png`: `image/png`,
|
|
|
|
`.webp`: `image/webp`,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
func (t Format) Validate(filePath string) bool {
|
2023-09-13 01:56:39 +00:00
|
|
|
return true
|
2023-09-11 15:43:09 +00:00
|
|
|
}
|
2023-09-11 17:09:08 +00:00
|
|
|
|
2023-10-09 14:47:17 +00:00
|
|
|
func (t Format) MediaType(extension string) string {
|
2023-09-14 22:37:22 +00:00
|
|
|
extensions := t.Extensions()
|
|
|
|
|
|
|
|
value, exists := extensions[extension]
|
|
|
|
if exists {
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
func ImageDimensions(path string) (*dimensions, error) {
|
2023-09-11 17:09:08 +00:00
|
|
|
file, err := os.Open(path)
|
|
|
|
switch {
|
|
|
|
case errors.Is(err, os.ErrNotExist):
|
|
|
|
fmt.Printf("File %s does not exist\n", path)
|
2024-01-30 21:22:15 +00:00
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
return &dimensions{}, nil
|
2023-09-11 17:09:08 +00:00
|
|
|
case err != nil:
|
|
|
|
fmt.Printf("File %s open returned error: %s\n", path, err)
|
2024-01-30 21:22:15 +00:00
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
return &dimensions{}, err
|
2023-09-11 17:09:08 +00:00
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
2023-09-11 21:05:38 +00:00
|
|
|
decodedConfig, _, err := image.DecodeConfig(file)
|
2023-09-11 17:09:08 +00:00
|
|
|
switch {
|
|
|
|
case errors.Is(err, image.ErrFormat):
|
|
|
|
fmt.Printf("File %s has invalid image format\n", path)
|
2024-01-30 21:22:15 +00:00
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
return &dimensions{width: 0, height: 0}, nil
|
2023-09-11 17:09:08 +00:00
|
|
|
case err != nil:
|
|
|
|
fmt.Printf("File %s decode returned error: %s\n", path, err)
|
2024-01-30 21:22:15 +00:00
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
return &dimensions{}, err
|
2023-09-11 17:09:08 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 04:35:17 +00:00
|
|
|
return &dimensions{width: decodedConfig.Width, height: decodedConfig.Height}, nil
|
|
|
|
}
|
|
|
|
|
2023-09-15 20:13:45 +00:00
|
|
|
func (t Format) Type() string {
|
|
|
|
return "embed"
|
|
|
|
}
|
|
|
|
|
2023-09-13 14:26:15 +00:00
|
|
|
func init() {
|
2023-09-29 19:36:11 +00:00
|
|
|
types.SupportedFormats.Register(Format{})
|
2023-09-11 17:09:08 +00:00
|
|
|
}
|