98 lines
2.4 KiB
Go
98 lines
2.4 KiB
Go
package gemini
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"fmt"
|
|
"gemini-grc/logging"
|
|
"net/url"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type URL struct {
|
|
Protocol string `json:"protocol,omitempty"`
|
|
Hostname string `json:"hostname,omitempty"`
|
|
Port int `json:"port,omitempty"`
|
|
Path string `json:"path,omitempty"`
|
|
Descr string `json:"descr,omitempty"`
|
|
Full string `json:"full,omitempty"`
|
|
}
|
|
|
|
func (u *URL) Scan(value interface{}) error {
|
|
if value == nil {
|
|
// Clear the fields in the current GeminiUrl object (not the pointer itself)
|
|
*u = URL{}
|
|
return nil
|
|
}
|
|
b, ok := value.(string)
|
|
if !ok {
|
|
return fmt.Errorf("failed to scan GeminiUrl: expected string, got %T", value)
|
|
}
|
|
parsedURL, err := ParseURL(b, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*u = *parsedURL
|
|
return nil
|
|
}
|
|
|
|
func (u URL) String() string {
|
|
return u.Full
|
|
}
|
|
|
|
func (u URL) StringNoDefaultPort() string {
|
|
if u.Port == 1965 {
|
|
return fmt.Sprintf("%s://%s%s", u.Protocol, u.Hostname, u.Path)
|
|
}
|
|
return u.Full
|
|
}
|
|
|
|
func (u URL) Value() (driver.Value, error) {
|
|
if u.Full == "" {
|
|
return nil, nil
|
|
}
|
|
return u.Full, nil
|
|
}
|
|
|
|
func ParseURL(input string, descr string) (*URL, error) {
|
|
u, err := url.Parse(input)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: Input %s GeminiError %w", ErrURLParse, input, err)
|
|
}
|
|
protocol := u.Scheme
|
|
hostname := u.Hostname()
|
|
strPort := u.Port()
|
|
path := u.Path
|
|
if strPort == "" {
|
|
strPort = "1965"
|
|
}
|
|
port, err := strconv.Atoi(strPort)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: Input %s GeminiError %w", ErrURLParse, input, err)
|
|
}
|
|
full := fmt.Sprintf("%s://%s:%d%s", protocol, hostname, port, path)
|
|
return &URL{Protocol: protocol, Hostname: hostname, Port: port, Path: path, Descr: descr, Full: full}, nil
|
|
}
|
|
|
|
func DeriveAbsoluteURL(currentURL URL, input string) (*URL, error) {
|
|
logging.LogDebug("Calculating redirect URL. Current %s header string %s", currentURL, input)
|
|
// If URL is absolute, return just it
|
|
if strings.Contains(input, "://") {
|
|
return ParseURL(input, "")
|
|
}
|
|
// input is a path. Clean it and construct
|
|
// new path
|
|
var newPath string
|
|
// Handle weird cases found in the wild
|
|
if strings.HasPrefix(input, "/") {
|
|
newPath = path.Clean(input)
|
|
} else if input == "./" || input == "." {
|
|
newPath = path.Join(currentURL.Path, "/")
|
|
} else {
|
|
newPath = path.Join(currentURL.Path, path.Clean(input))
|
|
}
|
|
strURL := fmt.Sprintf("%s://%s:%d%s", currentURL.Protocol, currentURL.Hostname, currentURL.Port, newPath)
|
|
return ParseURL(strURL, "")
|
|
}
|