package xmpp import ( "fmt" "strings" "unicode" ) type Jid struct { Node string Domain string Resource string } func NewJid(sjid string) (*Jid, error) { jid := new(Jid) if sjid == "" { return jid, fmt.Errorf("jid cannot be empty") } s1 := strings.SplitN(sjid, "@", 2) if len(s1) == 1 { // This is a server or component JID jid.Domain = s1[0] } else { // JID has a local username part if s1[0] == "" { return jid, fmt.Errorf("invalid jid '%s", sjid) } jid.Node = s1[0] if s1[1] == "" { return jid, fmt.Errorf("domain cannot be empty") } jid.Domain = s1[1] } // Extract resource from domain field s2 := strings.SplitN(jid.Domain, "/", 2) if len(s2) == 2 { // If len = 1, domain is already correct, and resource is already empty jid.Domain = s2[0] jid.Resource = s2[1] } if !isUsernameValid(jid.Node) { return jid, fmt.Errorf("invalid Node in JID '%s'", sjid) } if !isDomainValid(jid.Domain) { return jid, fmt.Errorf("invalid domain in JID '%s'", sjid) } return jid, nil } func (j *Jid) Full() string { return j.Node + "@" + j.Domain + "/" + j.Resource } func (j *Jid) Bare() string { return j.Node + "@" + j.Domain } // ============================================================================ // Helpers, for parsing / validation func isUsernameValid(username string) bool { invalidRunes := []rune{'@', '/', '\'', '"', ':', '<', '>'} return strings.IndexFunc(username, isInvalid(invalidRunes)) < 0 } func isDomainValid(domain string) bool { if len(domain) == 0 { return false } 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 }