- Timestamp:
- May 25, 2021, 2:42:51 PM (4 years ago)
- Location:
- trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/doc/ext/bouncer-networks.md
r533 r535 43 43 `BOUNCER` command. 44 44 45 The `bouncer-networks` capability MUST be negociated. This allows the server and 46 client to behave differently when the client is aware of the bouncer networks. 47 48 The `bouncer-networks-notify` capability MAY be negociated. This allows the 49 client to signal that it is capable of receiving and correctly processing 50 bouncer network notifications. 51 45 52 ### `RPL_ISUPPORT` token 46 53 … … 128 135 ### Network notifications 129 136 130 When a network attributes are updated, the bouncer MUST broadcast a 131 `BOUNCER NETWORK` message to all connected clients with the updated attributes: 137 If the client has negociated the `bouncer-networks-notify` capability, the 138 server MUST send an initial batch of `BOUNCER NETWORK` messages with the current 139 list of network, and MUST send notification messages whenever a network is 140 added, updated or removed. 141 142 If the client has not negociated the `bouncer-networks-notify` capability, the 143 server MUST NOT send implicit `BOUNCER NETWORK` messages. 144 145 When network attributes are updated, the bouncer MUST broadcast a 146 `BOUNCER NETWORK` message with the updated attributes to all connected clients 147 with the `bouncer-networks-notify` capability enabled: 132 148 133 149 BOUNCER NETWORK <netid> <attributes> 134 150 151 The notification SHOULD NOT contain attributes that haven't been updated. 152 135 153 When a network is removed, the bouncer MUST broadcast a `BOUNCER NETWORK` 136 message to all connected clients: 154 message with the special argument `*` to all connected clients with the 155 `bouncer-networks-notify` capability enabled: 137 156 138 157 BOUNCER NETWORK <netid> * … … 232 251 C: NICK emersion 233 252 C: USER emersion 0 0 :Simon 234 S: CAP * LS :sasl=PLAIN bouncer-networks 253 S: CAP * LS :sasl=PLAIN bouncer-networks bouncer-networks-notify 235 254 C: CAP REQ :sasl bouncer-networks 236 255 [SASL authentication] … … 249 268 250 269 C: BOUNCER ADDNETWORK name=OFTC;host=irc.oftc.net 251 S: BOUNCER NETWORK 44 status=connecting270 S: BOUNCER NETWORK 44 name=OFTC;host=irc.oftc.net;status=connecting 252 271 S: BOUNCER ADDNETWORK 44 253 272 S: BOUNCER NETWORK 44 status=connected -
trunk/downstream.go
r534 r535 58 58 }} 59 59 60 func parseBouncerNetID(s string) (int64, error) {60 func parseBouncerNetID(subcommand, s string) (int64, error) { 61 61 id, err := strconv.ParseInt(s, 10, 64) 62 62 if err != nil { 63 63 return 0, ircError{&irc.Message{ 64 64 Command: "FAIL", 65 Params: []string{"BOUNCER", "INVALID_NETID", s , "Invalid network ID"},65 Params: []string{"BOUNCER", "INVALID_NETID", subcommand, s, "Invalid network ID"}, 66 66 }} 67 67 } 68 68 return id, nil 69 } 70 71 func getNetworkAttrs(network *network) irc.Tags { 72 state := "disconnected" 73 if uc := network.conn; uc != nil { 74 state = "connected" 75 } 76 77 attrs := irc.Tags{ 78 "name": irc.TagValue(network.GetName()), 79 "state": irc.TagValue(state), 80 "nickname": irc.TagValue(network.Nick), 81 } 82 83 if network.Username != "" { 84 attrs["username"] = irc.TagValue(network.Username) 85 } 86 if network.Realname != "" { 87 attrs["realname"] = irc.TagValue(network.Realname) 88 } 89 90 if u, err := network.URL(); err == nil { 91 hasHostPort := true 92 switch u.Scheme { 93 case "ircs": 94 attrs["tls"] = irc.TagValue("1") 95 case "irc+insecure": 96 attrs["tls"] = irc.TagValue("0") 97 default: 98 hasHostPort = false 99 } 100 if host, port, err := net.SplitHostPort(u.Host); err == nil && hasHostPort { 101 attrs["host"] = irc.TagValue(host) 102 attrs["port"] = irc.TagValue(port) 103 } else if hasHostPort { 104 attrs["host"] = irc.TagValue(u.Host) 105 } 106 } 107 108 return attrs 69 109 } 70 110 … … 76 116 // capabilities. 77 117 var permanentDownstreamCaps = map[string]string{ 78 "batch": "", 79 "soju.im/bouncer-networks": "", 80 "cap-notify": "", 81 "echo-message": "", 82 "invite-notify": "", 83 "message-tags": "", 84 "sasl": "PLAIN", 85 "server-time": "", 118 "batch": "", 119 "cap-notify": "", 120 "echo-message": "", 121 "invite-notify": "", 122 "message-tags": "", 123 "sasl": "PLAIN", 124 "server-time": "", 125 126 "soju.im/bouncer-networks": "", 127 "soju.im/bouncer-networks-notify": "", 86 128 } 87 129 … … 599 641 } 600 642 601 id, err := parseBouncerNetID( idStr)643 id, err := parseBouncerNetID(subcommand, idStr) 602 644 if err != nil { 603 645 return err … … 1022 1064 dc.updateNick() 1023 1065 dc.updateSupportedCaps() 1066 1067 if dc.caps["soju.im/bouncer-networks-notify"] { 1068 dc.SendMessage(&irc.Message{ 1069 Prefix: dc.srv.prefix(), 1070 Command: "BATCH", 1071 Params: []string{"+networks", "bouncer-networks"}, 1072 }) 1073 dc.user.forEachNetwork(func(network *network) { 1074 idStr := fmt.Sprintf("%v", network.ID) 1075 attrs := getNetworkAttrs(network) 1076 dc.SendMessage(&irc.Message{ 1077 Tags: irc.Tags{"batch": irc.TagValue("networks")}, 1078 Prefix: dc.srv.prefix(), 1079 Command: "BOUNCER", 1080 Params: []string{"NETWORK", idStr, attrs.String()}, 1081 }) 1082 }) 1083 dc.SendMessage(&irc.Message{ 1084 Prefix: dc.srv.prefix(), 1085 Command: "BATCH", 1086 Params: []string{"-networks"}, 1087 }) 1088 } 1024 1089 1025 1090 dc.forEachUpstream(func(uc *upstreamConn) { … … 1986 2051 }) 1987 2052 dc.user.forEachNetwork(func(network *network) { 1988 id := fmt.Sprintf("%v", network.ID) 1989 1990 state := "disconnected" 1991 if uc := network.conn; uc != nil { 1992 state = "connected" 1993 } 1994 1995 attrs := irc.Tags{ 1996 "name": irc.TagValue(network.GetName()), 1997 "state": irc.TagValue(state), 1998 "nickname": irc.TagValue(network.Nick), 1999 } 2000 2001 if network.Username != "" { 2002 attrs["username"] = irc.TagValue(network.Username) 2003 } 2004 if network.Realname != "" { 2005 attrs["realname"] = irc.TagValue(network.Realname) 2006 } 2007 2008 if u, err := network.URL(); err == nil { 2009 hasHostPort := true 2010 switch u.Scheme { 2011 case "ircs": 2012 attrs["tls"] = irc.TagValue("1") 2013 case "irc+insecure": 2014 attrs["tls"] = irc.TagValue("0") 2015 default: 2016 hasHostPort = false 2017 } 2018 if host, port, err := net.SplitHostPort(u.Host); err == nil && hasHostPort { 2019 attrs["host"] = irc.TagValue(host) 2020 attrs["port"] = irc.TagValue(port) 2021 } else if hasHostPort { 2022 attrs["host"] = irc.TagValue(u.Host) 2023 } 2024 } 2025 2053 idStr := fmt.Sprintf("%v", network.ID) 2054 attrs := getNetworkAttrs(network) 2026 2055 dc.SendMessage(&irc.Message{ 2027 2056 Tags: irc.Tags{"batch": irc.TagValue("networks")}, 2028 2057 Prefix: dc.srv.prefix(), 2029 2058 Command: "BOUNCER", 2030 Params: []string{"NETWORK", id , attrs.String()},2059 Params: []string{"NETWORK", idStr, attrs.String()}, 2031 2060 }) 2032 2061 }) … … 2096 2125 return err 2097 2126 } 2098 id, err := parseBouncerNetID( idStr)2127 id, err := parseBouncerNetID(subcommand, idStr) 2099 2128 if err != nil { 2100 2129 return err … … 2106 2135 return ircError{&irc.Message{ 2107 2136 Command: "FAIL", 2108 Params: []string{"BOUNCER", "INVALID_NETID", idStr, "Invalid network ID"},2137 Params: []string{"BOUNCER", "INVALID_NETID", subcommand, idStr, "Invalid network ID"}, 2109 2138 }} 2110 2139 } … … 2149 2178 return err 2150 2179 } 2151 id, err := parseBouncerNetID( idStr)2180 id, err := parseBouncerNetID(subcommand, idStr) 2152 2181 if err != nil { 2153 2182 return err … … 2158 2187 return ircError{&irc.Message{ 2159 2188 Command: "FAIL", 2160 Params: []string{"BOUNCER", "INVALID_NETID", idStr, "Invalid network ID"},2189 Params: []string{"BOUNCER", "INVALID_NETID", subcommand, idStr, "Invalid network ID"}, 2161 2190 }} 2162 2191 } -
trunk/user.go
r532 r535 519 519 dc.updateSupportedCaps() 520 520 521 if dc.caps["soju.im/bouncer-networks "] {521 if dc.caps["soju.im/bouncer-networks-notify"] { 522 522 dc.SendMessage(&irc.Message{ 523 523 Prefix: dc.srv.prefix(), … … 658 658 dc.updateSupportedCaps() 659 659 660 if dc.caps["soju.im/bouncer-networks "] {660 if dc.caps["soju.im/bouncer-networks-notify"] { 661 661 dc.SendMessage(&irc.Message{ 662 662 Prefix: dc.srv.prefix(), … … 726 726 u.addNetwork(network) 727 727 728 // TODO: broadcast network status729 728 idStr := fmt.Sprintf("%v", network.ID) 729 attrs := getNetworkAttrs(network) 730 730 u.forEachDownstream(func(dc *downstreamConn) { 731 if dc.caps["soju.im/bouncer-networks "] {731 if dc.caps["soju.im/bouncer-networks-notify"] { 732 732 dc.SendMessage(&irc.Message{ 733 733 Prefix: dc.srv.prefix(), 734 734 Command: "BOUNCER", 735 Params: []string{"NETWORK", idStr, "network=" + network.GetName()},735 Params: []string{"NETWORK", idStr, attrs.String()}, 736 736 }) 737 737 } … … 791 791 u.addNetwork(updatedNetwork) 792 792 793 // TODO: broadcast BOUNCER NETWORK notifications 793 // TODO: only broadcast attributes that have changed 794 idStr := fmt.Sprintf("%v", updatedNetwork.ID) 795 attrs := getNetworkAttrs(updatedNetwork) 796 u.forEachDownstream(func(dc *downstreamConn) { 797 if dc.caps["soju.im/bouncer-networks-notify"] { 798 dc.SendMessage(&irc.Message{ 799 Prefix: dc.srv.prefix(), 800 Command: "BOUNCER", 801 Params: []string{"NETWORK", idStr, attrs.String()}, 802 }) 803 } 804 }) 794 805 795 806 return updatedNetwork, nil … … 810 821 idStr := fmt.Sprintf("%v", network.ID) 811 822 u.forEachDownstream(func(dc *downstreamConn) { 812 if dc.caps["soju.im/bouncer-networks "] {823 if dc.caps["soju.im/bouncer-networks-notify"] { 813 824 dc.SendMessage(&irc.Message{ 814 825 Prefix: dc.srv.prefix(),
Note:
See TracChangeset
for help on using the changeset viewer.