mirror of
https://github.com/GwynethLlewelyn/goswi.git
synced 2026-08-14 00:58:00 +00:00
194 lines
5.7 KiB
Go
194 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"math"
|
|
osUser "os/user"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-contrib/sessions"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// checkErrFatal logs a fatal error and does whatever log.Fatal() is supposed to do.
|
|
func checkErrFatal(err error) {
|
|
if err != nil {
|
|
pc, file, line, ok := runtime.Caller(1)
|
|
config.LogFatal(filepath.Base(file), ":", line, ":", pc, ok, " - panic:", err)
|
|
}
|
|
}
|
|
|
|
// checkErrPanic logs a fatal error and panics.
|
|
func checkErrPanic(err error) {
|
|
if err != nil {
|
|
pc, file, line, ok := runtime.Caller(1)
|
|
config.LogPanic(filepath.Base(file), ":", line, ":", pc, ok, " - panic:", err)
|
|
}
|
|
}
|
|
|
|
// checkErr checks if there is an error, and if yes, it logs it out and continues.
|
|
//
|
|
// This is for 'normal' situations when we want to get a log if something goes wrong but do not need to terminate execution.
|
|
func checkErr(err error) {
|
|
if err != nil {
|
|
pc, file, line, ok := runtime.Caller(1)
|
|
config.LogError(filepath.Base(file), ":", line, ":", pc, ok, " - error:", err)
|
|
}
|
|
}
|
|
|
|
// expandPath expands the tilde as the user's home directory.
|
|
//
|
|
// Found at http://stackoverflow.com/a/43578461/1035977
|
|
func expandPath(path string) (string, error) {
|
|
if len(path) == 0 || path[0] != '~' {
|
|
return path, nil
|
|
}
|
|
|
|
usr, err := osUser.Current()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(usr.HomeDir, path[1:]), nil
|
|
}
|
|
|
|
/**
|
|
* Cryptographic helper functions
|
|
**/
|
|
|
|
// GetMD5Hash calculated the MD5 hash of any string. See aviv's solution on SO: https://stackoverflow.com/a/25286918/1035977
|
|
func GetMD5Hash(text string) string {
|
|
hash := md5.Sum([]byte(text))
|
|
return hex.EncodeToString(hash[:])
|
|
}
|
|
|
|
// generateSessionToken uses the same approach as OpenSimulator, which is to return a newly created UUID.
|
|
func generateSessionToken() string {
|
|
return uuid.New().String()
|
|
}
|
|
|
|
// randomBase64String is Steven Soroka's simple solution to generate a cryptographically secure random string with base64 encoding (see https://stackoverflow.com/a/55860599/1035977) (gwyneth 20200706)
|
|
func randomBase64String(l int) string {
|
|
buff := make([]byte, int(math.Round(float64(l)/float64(1.33333333333))))
|
|
rand.Read(buff)
|
|
str := base64.RawURLEncoding.EncodeToString(buff)
|
|
return str[:l] // strip 1 extra character we get from odd length results
|
|
}
|
|
|
|
// isValidExtension looks up a file extension and checks if it is valid for using inside HTML <img>.
|
|
// It's a switch because it's more efficient: https://stackoverflow.com/a/52710077/1035977
|
|
func isValidExtension(lookup string) bool {
|
|
switch strings.ToLower(lookup) {
|
|
// A full list of valid extensions is here: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img
|
|
// I've added .mp4 for the sake of convenience (gwyneth 20200722)
|
|
case
|
|
".bmp",
|
|
".cur",
|
|
".ico",
|
|
".jfif",
|
|
".pjp",
|
|
".pjpeg",
|
|
".apng",
|
|
".gif",
|
|
".jpeg",
|
|
".jpg",
|
|
".mp4",
|
|
".png",
|
|
".avif", // new popular file format
|
|
".heic", // popular with macOS users
|
|
".svg",
|
|
".svgz", // compressed <kl
|
|
".webp":
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// MergeMaps adds lots of map[string]any together, returning the merged map[string]any.
|
|
// It overwrites duplicate keys, maps to the right overwriting whatever keys are on the left.
|
|
// This allows for setting 'default' arguments later below, which can be overridden.
|
|
// See https://play.golang.org/p/8a9cXdSL_o3 as well as https://stackoverflow.com/a/39406305/1035977.
|
|
func MergeMaps(maps ...map[string]any) map[string]any {
|
|
result := make(map[string]any)
|
|
for _, m := range maps {
|
|
for k, v := range m {
|
|
result[k] = v
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Functions used inside templates.
|
|
**/
|
|
|
|
// bitTest applies a mask to a flag and returns true if the bit is set in the mask, false otherwise.
|
|
func bitTest(flag int, mask int) bool {
|
|
return (flag & mask) != 0
|
|
}
|
|
|
|
// formatAsDate is a function for the templating system.
|
|
func formatAsDate(t time.Time) string {
|
|
year, month, day := t.Date()
|
|
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
|
|
}
|
|
|
|
// formatAsYear is another function for the templating system.
|
|
func formatAsYear(t time.Time) string {
|
|
year, _, _ := t.Date()
|
|
return fmt.Sprintf("%d", year)
|
|
}
|
|
|
|
/**
|
|
* Auxiliary functions for the Gin Gonic environment.
|
|
**/
|
|
|
|
// environment pushes a lot of stuff into the common environment
|
|
func environment(c *gin.Context, env gin.H) gin.H {
|
|
session := sessions.Default(c)
|
|
var sidebarCollapsed = "" // false by default
|
|
if session.Get("sidebarCollapsed") == "true" {
|
|
sidebarCollapsed = "true"
|
|
} else if *config["sidebarCollapsed"] == "true" {
|
|
sidebarCollapsed = "true"
|
|
}
|
|
|
|
var data = gin.H{
|
|
/* common environment */
|
|
"now": formatAsYear(time.Now()),
|
|
"author": *config["author"],
|
|
"description": *config["description"],
|
|
"logo": *config["logo"],
|
|
"logoTitle": *config["logoTitle"],
|
|
"sidebarCollapsed": sidebarCollapsed,
|
|
"titleCommon": *config["titleCommon"],
|
|
"StatsDir": *config["gridstats"],
|
|
/* session data */
|
|
"Username": session.Get("Username"),
|
|
"UUID": session.Get("UUID"),
|
|
"Libravatar": session.Get("Libravatar"),
|
|
"Token": session.Get("Token"),
|
|
"Email": session.Get("Email"),
|
|
"RememberMe": session.Get("RememberMe"),
|
|
"Messages": session.Get("Messages"), // these are offline IMs
|
|
"numberMessages": session.Get("numberMessages"),
|
|
"FeedMessages": session.Get("FeedMessages"),
|
|
"numberFeedMessages": session.Get("numberFeedMessages"),
|
|
}
|
|
|
|
retMap := MergeMaps(data, env)
|
|
|
|
// Messages will only exist if there is a user online.
|
|
if retMap["Username"] != nil && retMap["Messages"] != nil {
|
|
config.LogTracef("environment(): All messages for user %q: %+v\n", retMap["Username"], retMap["Messages"])
|
|
}
|
|
return retMap
|
|
}
|