2020-01-09 14:33:11 +00:00
|
|
|
package stanza
|
2016-01-06 15:51:12 +00:00
|
|
|
|
|
|
|
import (
|
2019-06-07 09:40:10 +00:00
|
|
|
"fmt"
|
2016-01-06 15:51:12 +00:00
|
|
|
"strings"
|
2019-06-05 08:02:24 +00:00
|
|
|
"unicode"
|
2016-01-06 15:51:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Jid struct {
|
2019-06-07 13:56:41 +00:00
|
|
|
Node string
|
|
|
|
Domain string
|
|
|
|
Resource string
|
2016-01-06 15:51:12 +00:00
|
|
|
}
|
|
|
|
|
2019-06-05 08:02:24 +00:00
|
|
|
func NewJid(sjid string) (*Jid, error) {
|
|
|
|
jid := new(Jid)
|
|
|
|
|
2019-06-07 09:40:10 +00:00
|
|
|
if sjid == "" {
|
|
|
|
return jid, fmt.Errorf("jid cannot be empty")
|
|
|
|
}
|
2019-06-05 08:02:24 +00:00
|
|
|
|
2019-06-07 09:40:10 +00:00
|
|
|
s1 := strings.SplitN(sjid, "@", 2)
|
2020-01-09 14:33:11 +00:00
|
|
|
if len(s1) == 1 { // This is a server or component Jid
|
2019-06-07 13:56:41 +00:00
|
|
|
jid.Domain = s1[0]
|
2020-01-09 14:33:11 +00:00
|
|
|
} else { // Jid has a local username part
|
2019-06-07 09:40:10 +00:00
|
|
|
if s1[0] == "" {
|
|
|
|
return jid, fmt.Errorf("invalid jid '%s", sjid)
|
|
|
|
}
|
2019-06-07 13:56:41 +00:00
|
|
|
jid.Node = s1[0]
|
2019-06-07 09:40:10 +00:00
|
|
|
if s1[1] == "" {
|
|
|
|
return jid, fmt.Errorf("domain cannot be empty")
|
|
|
|
}
|
2019-06-07 13:56:41 +00:00
|
|
|
jid.Domain = s1[1]
|
2019-06-05 08:02:24 +00:00
|
|
|
}
|
|
|
|
|
2019-06-07 09:40:10 +00:00
|
|
|
// Extract resource from domain field
|
2019-06-07 13:56:41 +00:00
|
|
|
s2 := strings.SplitN(jid.Domain, "/", 2)
|
2019-06-07 09:40:10 +00:00
|
|
|
if len(s2) == 2 { // If len = 1, domain is already correct, and resource is already empty
|
2019-06-07 13:56:41 +00:00
|
|
|
jid.Domain = s2[0]
|
|
|
|
jid.Resource = s2[1]
|
2016-01-06 15:51:12 +00:00
|
|
|
}
|
|
|
|
|
2019-06-07 13:56:41 +00:00
|
|
|
if !isUsernameValid(jid.Node) {
|
2020-01-09 14:33:11 +00:00
|
|
|
return jid, fmt.Errorf("invalid Node in Jid '%s'", sjid)
|
2019-06-07 09:40:10 +00:00
|
|
|
}
|
2019-06-07 13:56:41 +00:00
|
|
|
if !isDomainValid(jid.Domain) {
|
2020-01-09 14:33:11 +00:00
|
|
|
return jid, fmt.Errorf("invalid domain in Jid '%s'", sjid)
|
2019-06-07 09:40:10 +00:00
|
|
|
}
|
|
|
|
|
2019-06-05 08:02:24 +00:00
|
|
|
return jid, nil
|
|
|
|
}
|
|
|
|
|
2019-06-07 14:25:18 +00:00
|
|
|
func (j *Jid) Full() string {
|
2019-11-05 23:27:37 +00:00
|
|
|
if j.Resource == "" {
|
|
|
|
return j.Bare()
|
|
|
|
} else if j.Node == "" {
|
2022-05-24 20:26:48 +00:00
|
|
|
return j.Domain + "/" + j.Resource
|
2019-11-05 23:27:37 +00:00
|
|
|
} else {
|
|
|
|
return j.Node + "@" + j.Domain + "/" + j.Resource
|
|
|
|
}
|
2019-06-07 14:25:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (j *Jid) Bare() string {
|
2019-11-05 23:27:37 +00:00
|
|
|
if j.Node == "" {
|
|
|
|
return j.Domain
|
|
|
|
} else {
|
|
|
|
return j.Node + "@" + j.Domain
|
|
|
|
}
|
2019-06-07 14:25:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// Helpers, for parsing / validation
|
|
|
|
|
2019-06-05 08:02:24 +00:00
|
|
|
func isUsernameValid(username string) bool {
|
|
|
|
invalidRunes := []rune{'@', '/', '\'', '"', ':', '<', '>'}
|
|
|
|
return strings.IndexFunc(username, isInvalid(invalidRunes)) < 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func isDomainValid(domain string) bool {
|
2019-06-07 09:40:10 +00:00
|
|
|
if len(domain) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-06-05 08:02:24 +00:00
|
|
|
invalidRunes := []rune{'@', '/'}
|
|
|
|
return strings.IndexFunc(domain, isInvalid(invalidRunes)) < 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func isInvalid(invalidRunes []rune) func(c rune) bool {
|
|
|
|
isInvalid := func(c rune) bool {
|
|
|
|
if unicode.IsSpace(c) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, r := range invalidRunes {
|
|
|
|
if c == r {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return isInvalid
|
2016-01-06 15:51:12 +00:00
|
|
|
}
|