1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-05 16:56:23 -07:00
gochan/pkg/gcutil/util.go
2023-12-19 14:33:40 -08:00

203 lines
5.3 KiB
Go

package gcutil
import (
"crypto/md5"
"crypto/sha1" // skipcq GSC-G505
"encoding/json"
"errors"
"fmt"
"io"
"math/rand"
"net"
"net/http"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/aquilax/tripcode"
"golang.org/x/crypto/bcrypt"
x_html "golang.org/x/net/html"
)
const (
// DefaultMaxAge is used for cookies that have an invalid or unset max age (default is 1 month)
DefaultMaxAge = 60 * 60 * 24 * 31
)
var (
// ErrNotImplemented should be used for unimplemented functionality when necessary, not for bugs
ErrNotImplemented = errors.New("not implemented")
)
// BcryptSum generates and returns a checksum using the bcrypt hashing function
func BcryptSum(str string) string {
digest, err := bcrypt.GenerateFromPassword([]byte(str), 10)
if err == nil {
return string(digest)
}
return ""
}
// Md5Sum generates and returns a checksum using the MD5 hashing function
func Md5Sum(str string) string {
hash := md5.New() // skipcq: GSC-G401
io.WriteString(hash, str)
return fmt.Sprintf("%x", hash.Sum(nil))
}
// Sha1Sum generates and returns a checksum using the SHA-1 hashing function
func Sha1Sum(str string) string {
hash := sha1.New() // skipcq: GSC-G401
io.WriteString(hash, str)
return fmt.Sprintf("%x", hash.Sum(nil))
}
// CloseHandle closes the given closer object only if it is non-nil
func CloseHandle(handle io.Closer) {
if handle != nil {
handle.Close()
}
}
// DeleteMatchingFiles deletes files in a folder (root) that match a given regular expression.
// Returns the number of files that were deleted, and any error encountered.
func DeleteMatchingFiles(root, match string) (filesDeleted int, err error) {
files, err := os.ReadDir(root)
if err != nil {
return 0, err
}
for _, f := range files {
match, _ := regexp.MatchString(match, f.Name())
if match {
os.Remove(filepath.Join(root, f.Name()))
filesDeleted++
}
}
return filesDeleted, err
}
// FindResource searches for a file in the given paths and returns the first one it finds
// or a blank string if none of the paths exist
func FindResource(paths ...string) string {
var err error
for _, filepath := range paths {
if _, err = os.Stat(filepath); err == nil {
return filepath
}
}
return ""
}
// GetFileParts returns the base filename, the filename sans-extension, and the extension
// sans-filename
func GetFileParts(filename string) (string, string, string) {
base := path.Base(filename)
var noExt string
var ext string
lastIndex := strings.LastIndex(base, ".")
if lastIndex > -1 {
noExt = base[:strings.LastIndex(base, ".")]
ext = path.Ext(base)[1:]
}
return base, noExt, ext
}
// GetFormattedFilesize returns a human readable filesize
func GetFormattedFilesize(size float64) string {
if size < 1000 {
return fmt.Sprintf("%dB", int(size))
} else if size <= 100000 {
return fmt.Sprintf("%fKB", size/1024)
} else if size <= 100000000 {
return fmt.Sprintf("%fMB", size/1024.0/1024.0)
}
return fmt.Sprintf("%0.2fGB", size/1024.0/1024.0/1024.0)
}
// GetRealIP checks the HTTP_CF_CONNCTING_IP and X-Forwarded-For HTTP headers to get a
// potentially obfuscated IP address, before getting the requests reported remote address
// if neither header is set
func GetRealIP(request *http.Request) string {
if request.Header.Get("HTTP_CF_CONNECTING_IP") != "" {
return request.Header.Get("HTTP_CF_CONNECTING_IP")
}
if request.Header.Get("X-Forwarded-For") != "" {
return request.Header.Get("X-Forwarded-For")
}
remoteHost, _, err := net.SplitHostPort(request.RemoteAddr)
if err != nil {
return request.RemoteAddr
}
return remoteHost
}
// HackyStringToInt parses a string to an int, or 0 if error
func HackyStringToInt(text string) int {
value, _ := strconv.Atoi(text)
return value
}
// MarshalJSON creates a JSON string with the given data and returns the string and any errors
func MarshalJSON(data interface{}, indent bool) (string, error) {
var jsonBytes []byte
var err error
if indent {
jsonBytes, err = json.MarshalIndent(data, "", " ")
} else {
jsonBytes, err = json.Marshal(data)
}
if err != nil {
jsonBytes, _ = json.Marshal(map[string]string{"error": err.Error()})
}
return string(jsonBytes), err
}
// ParseName takes a name string from a request object and returns the name and tripcode parts
func ParseName(name string) (string, string) {
var namePart string
var tripcodePart string
if !strings.Contains(name, "#") {
namePart = name
} else if strings.Index(name, "#") == 0 {
tripcodePart = tripcode.Tripcode(name[1:])
} else if strings.Index(name, "#") > 0 {
postNameArr := strings.SplitN(name, "#", 2)
namePart = postNameArr[0]
tripcodePart = tripcode.Tripcode(postNameArr[1])
}
return namePart, tripcodePart
}
// RandomString returns a randomly generated string of the given length
func RandomString(length int) string {
var str string
for i := 0; i < length; i++ {
num := rand.Intn(127) // skipcq: GSC-G404
if num < 32 {
num += 32
}
str += fmt.Sprintf("%c", num)
}
return str
}
func StripHTML(htmlIn string) string {
dom := x_html.NewTokenizer(strings.NewReader(htmlIn))
for tokenType := dom.Next(); tokenType != x_html.ErrorToken; {
if tokenType != x_html.TextToken {
tokenType = dom.Next()
continue
}
txtContent := strings.TrimSpace(x_html.UnescapeString(string(dom.Text())))
if len(txtContent) > 0 {
return x_html.EscapeString(txtContent)
}
tokenType = dom.Next()
}
return ""
}