url.go 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. package common
  2. import (
  3. "errors"
  4. "net/url"
  5. "strings"
  6. )
  7. // EnsureURLScheme prepends https:// to a URL that carries no scheme, so
  8. // subscription apps and browsers don't resolve it relative to the panel's own
  9. // domain (e.g. "t.me/support" turning into "https://panel.example/t.me/support").
  10. // Values with an explicit scheme (https://, tg://, mailto:, tel:) and empty
  11. // strings pass through untouched.
  12. func EnsureURLScheme(raw string) string {
  13. trimmed := strings.TrimSpace(raw)
  14. if trimmed == "" {
  15. return ""
  16. }
  17. if strings.Contains(trimmed, "://") ||
  18. strings.HasPrefix(trimmed, "mailto:") ||
  19. strings.HasPrefix(trimmed, "tel:") {
  20. return trimmed
  21. }
  22. return "https://" + trimmed
  23. }
  24. // ParseRemoteRoutingURL classifies a routing settings value: one single-line
  25. // absolute HTTPS URL is a remote source (canonicalized); anything else is inline.
  26. func ParseRemoteRoutingURL(raw string) (string, bool, error) {
  27. trimmed := strings.TrimSpace(raw)
  28. if trimmed == "" || strings.ContainsAny(trimmed, "\r\n") {
  29. return "", false, nil
  30. }
  31. if !strings.HasPrefix(strings.ToLower(trimmed), "https://") {
  32. return "", false, nil
  33. }
  34. u, err := url.Parse(trimmed)
  35. if err != nil || u.Host == "" || u.Hostname() == "" {
  36. return "", true, errors.New("must be an absolute HTTPS URL")
  37. }
  38. if u.User != nil {
  39. return "", true, errors.New("must not contain URL credentials")
  40. }
  41. u.Scheme = "https"
  42. u.Fragment = ""
  43. return u.String(), true, nil
  44. }