package html import ( "net" "net/url" "strconv" "strings" "unicode/utf8" "github.com/go-faster/errors" "go.mau.fi/mautrix-telegram/pkg/gotd/ascii" "go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message/entity" ) func isIPv6(str string) bool { ip := net.ParseIP(str) return strings.Contains(str, ":") && ip != nil } func validateHostname(u *url.URL) error { // TODO(tdakkota): make sure that it is correct ipv6 := isIPv6(u.Host) if !strings.ContainsRune(u.Host, '.') && ipv6 { return errors.New("wrong HTTP URL") } if ipv6 { return nil } allowedSymbol := func(c rune) bool { return ascii.IsLatinLetter(c) || ascii.IsDigit(c) || (c >= '&' && c <= '.') || c == '_' || c == '!' || c == '$' || c == '~' || c == ';' || c == '=' || c > utf8.RuneSelf } for _, c := range u.Host { if !allowedSymbol(c) { return errors.Errorf("disallowed character %c in URL host", c) } } return nil } func getURLFormatter(rawURL string, resolver entity.UserResolver) (entity.Formatter, error) { const defaultProtocol = "http" if rawURL == "" { return nil, errors.New("empty URL") } // FIXME(tdakkota): move normalization to deeplink package when it's done? u, err := url.Parse(rawURL) if err != nil { return nil, err } if u.Scheme == "tg" && u.Host == "user" { id, err := strconv.ParseInt(u.Query().Get("id"), 10, 64) if err != nil { return nil, errors.Wrapf(err, "invalid user ID %q", id) } user, err := resolver(id) if err != nil { return nil, errors.Wrapf(err, "can't resolve user %q", id) } return entity.MentionName(user), nil } if u.Scheme == "" { u.Scheme = defaultProtocol u.Host = u.Path u.Path = "/" rawURL = u.String() } if err := validateHostname(u); err != nil { return nil, err } return entity.TextURL(rawURL), nil }