Changeset 660 in code for trunk


Ignore:
Timestamp:
Nov 2, 2021, 5:25:43 PM (4 years ago)
Author:
contact
Message:

Add support for WHOX

This adds support for WHOX, without bothering about flags and mask2
because Solanum and Ergo [1] don't support it either.

The motivation is to allow clients to reliably query account names.

It's not possible to use WHOX tokens to route replies to the right
client, because RPL_ENDOFWHO doesn't contain it.

[1]: https://github.com/ergochat/ergo/pull/1184

Closes: https://todo.sr.ht/~emersion/soju/135

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/downstream.go

    r659 r660  
    237237        "USERLEN":       true,
    238238        "UTF8ONLY":      true,
     239        "WHOX":          true,
    239240}
    240241
     
    11561157        if dc.network != nil {
    11571158                isupport = append(isupport, fmt.Sprintf("BOUNCER_NETID=%v", dc.network.ID))
     1159        }
     1160
     1161        if dc.network == nil && dc.caps["soju.im/bouncer-networks"] {
     1162                isupport = append(isupport, "WHOX")
    11581163        }
    11591164
     
    18831888                        }
    18841889                }
     1890        // For WHOX docs, see:
     1891        // - http://faerion.sourceforge.net/doc/irc/whox.var
     1892        // - https://github.com/quakenet/snircd/blob/master/doc/readme.who
     1893        // Note, many features aren't widely implemented, such as flags and mask2
    18851894        case "WHO":
    18861895                if len(msg.Params) == 0 {
     
    18941903                }
    18951904
    1896                 // TODO: support WHO masks
    1897                 entity := msg.Params[0]
    1898                 entityCM := casemapASCII(entity)
    1899 
    1900                 if dc.network == nil && entityCM == dc.nickCM {
     1905                // Clients will use the first mask to match RPL_ENDOFWHO
     1906                endOfWhoToken := msg.Params[0]
     1907
     1908                // TODO: add support for WHOX mask2
     1909                mask := msg.Params[0]
     1910                var options string
     1911                if len(msg.Params) > 1 {
     1912                        options = msg.Params[1]
     1913                }
     1914
     1915                optionsParts := strings.SplitN(options, "%", 2)
     1916                // TODO: add support for WHOX flags in optionsParts[0]
     1917                var fields, whoxToken string
     1918                if len(optionsParts) == 2 {
     1919                        optionsParts := strings.SplitN(optionsParts[1], ",", 2)
     1920                        fields = strings.ToLower(optionsParts[0])
     1921                        if len(optionsParts) == 2 && strings.Contains(fields, "t") {
     1922                                whoxToken = optionsParts[1]
     1923                        }
     1924                }
     1925
     1926                // TODO: support mixed bouncer/upstream WHO queries
     1927                maskCM := casemapASCII(mask)
     1928                if dc.network == nil && maskCM == dc.nickCM {
    19011929                        // TODO: support AWAY (H/G) in self WHO reply
    19021930                        flags := "H"
     
    19041932                                flags += "*"
    19051933                        }
    1906                         dc.SendMessage(&irc.Message{
    1907                                 Prefix:  dc.srv.prefix(),
    1908                                 Command: irc.RPL_WHOREPLY,
    1909                                 Params:  []string{dc.nick, "*", dc.user.Username, dc.hostname, dc.srv.Hostname, dc.nick, flags, "0 " + dc.realname},
    1910                         })
     1934                        info := whoxInfo{
     1935                                Token:    whoxToken,
     1936                                Username: dc.user.Username,
     1937                                Hostname: dc.hostname,
     1938                                Server:   dc.srv.Hostname,
     1939                                Nickname: dc.nick,
     1940                                Flags:    flags,
     1941                                Realname: dc.realname,
     1942                        }
     1943                        dc.SendMessage(generateWHOXReply(dc.srv.prefix(), dc.nick, fields, &info))
    19111944                        dc.SendMessage(&irc.Message{
    19121945                                Prefix:  dc.srv.prefix(),
    19131946                                Command: irc.RPL_ENDOFWHO,
    1914                                 Params:  []string{dc.nick, dc.nick, "End of /WHO list"},
     1947                                Params:  []string{dc.nick, endOfWhoToken, "End of /WHO list"},
    19151948                        })
    19161949                        return nil
    19171950                }
    1918                 if entityCM == serviceNickCM {
    1919                         dc.SendMessage(&irc.Message{
    1920                                 Prefix:  dc.srv.prefix(),
    1921                                 Command: irc.RPL_WHOREPLY,
    1922                                 Params:  []string{serviceNick, "*", servicePrefix.User, servicePrefix.Host, dc.srv.Hostname, serviceNick, "H*", "0 " + serviceRealname},
    1923                         })
     1951                if maskCM == serviceNickCM {
     1952                        info := whoxInfo{
     1953                                Token:    whoxToken,
     1954                                Username: servicePrefix.User,
     1955                                Hostname: servicePrefix.Host,
     1956                                Server:   dc.srv.Hostname,
     1957                                Nickname: serviceNick,
     1958                                Flags:    "H*",
     1959                                Realname: serviceRealname,
     1960                        }
     1961                        dc.SendMessage(generateWHOXReply(dc.srv.prefix(), dc.nick, fields, &info))
    19241962                        dc.SendMessage(&irc.Message{
    19251963                                Prefix:  dc.srv.prefix(),
    19261964                                Command: irc.RPL_ENDOFWHO,
    1927                                 Params:  []string{dc.nick, serviceNick, "End of /WHO list"},
     1965                                Params:  []string{dc.nick, endOfWhoToken, "End of /WHO list"},
    19281966                        })
    19291967                        return nil
    19301968                }
    19311969
    1932                 uc, upstreamName, err := dc.unmarshalEntity(entity)
     1970                // TODO: properly support WHO masks
     1971                uc, upstreamMask, err := dc.unmarshalEntity(mask)
    19331972                if err != nil {
    19341973                        return err
    19351974                }
    19361975
    1937                 var params []string
    1938                 if len(msg.Params) == 2 {
    1939                         params = []string{upstreamName, msg.Params[1]}
    1940                 } else {
    1941                         params = []string{upstreamName}
     1976                params := []string{upstreamMask}
     1977                if options != "" {
     1978                        params = append(params, options)
    19421979                }
    19431980
  • trunk/irc.go

    r636 r660  
    1818        rpl_creationtime  = "329"
    1919        rpl_topicwhotime  = "333"
     20        rpl_whospcrpl     = "354"
    2021        err_invalidcapcmd = "410"
    2122)
     
    683684        }
    684685}
     686
     687type whoxInfo struct {
     688        Token    string
     689        Username string
     690        Hostname string
     691        Server   string
     692        Nickname string
     693        Flags    string
     694        Account  string
     695        Realname string
     696}
     697
     698func generateWHOXReply(prefix *irc.Prefix, nick, fields string, info *whoxInfo) *irc.Message {
     699        if fields == "" {
     700                return &irc.Message{
     701                        Prefix:  prefix,
     702                        Command: irc.RPL_WHOREPLY,
     703                        Params:  []string{nick, "*", info.Username, info.Hostname, info.Server, info.Nickname, info.Flags, "0 " + info.Realname},
     704                }
     705        }
     706
     707        fieldSet := make(map[byte]bool)
     708        for i := 0; i < len(fields); i++ {
     709                fieldSet[fields[i]] = true
     710        }
     711
     712        var params []string
     713        if fieldSet['t'] {
     714                params = append(params, info.Token)
     715        }
     716        if fieldSet['c'] {
     717                params = append(params, "*")
     718        }
     719        if fieldSet['u'] {
     720                params = append(params, info.Username)
     721        }
     722        if fieldSet['i'] {
     723                params = append(params, "255.255.255.255")
     724        }
     725        if fieldSet['h'] {
     726                params = append(params, info.Hostname)
     727        }
     728        if fieldSet['s'] {
     729                params = append(params, info.Server)
     730        }
     731        if fieldSet['n'] {
     732                params = append(params, info.Nickname)
     733        }
     734        if fieldSet['f'] {
     735                params = append(params, info.Flags)
     736        }
     737        if fieldSet['d'] {
     738                params = append(params, "0")
     739        }
     740        if fieldSet['l'] { // idle time
     741                params = append(params, "0")
     742        }
     743        if fieldSet['a'] {
     744                account := "0" // WHOX uses "0" to mean "no account"
     745                if info.Account != "" && info.Account != "*" {
     746                        account = info.Account
     747                }
     748                params = append(params, account)
     749        }
     750        if fieldSet['o'] {
     751                params = append(params, "0")
     752        }
     753        if fieldSet['r'] {
     754                params = append(params, info.Realname)
     755        }
     756
     757        return &irc.Message{
     758                Prefix:  prefix,
     759                Command: rpl_whospcrpl,
     760                Params:  append([]string{nick}, params...),
     761        }
     762}
  • trunk/upstream.go

    r655 r660  
    14531453        case irc.RPL_YOURHOST, irc.RPL_CREATED:
    14541454                // Ignore
     1455        case rpl_whospcrpl:
     1456                // Not supported in multi-upstream mode, forward as-is
     1457                uc.forEachDownstream(func(dc *downstreamConn) {
     1458                        dc.SendMessage(msg)
     1459                })
    14551460        case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
    14561461                fallthrough
Note: See TracChangeset for help on using the changeset viewer.