|
@@ -2775,7 +2775,120 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
|
2775
|
2775
|
return true
|
2776
|
2776
|
}
|
2777
|
2777
|
|
2778
|
|
-// WHO [<mask> [o]]
|
|
2778
|
+const WhoFieldMinimum = int('a') // lowest rune value
|
|
2779
|
+const WhoFieldMaximum = int('z')
|
|
2780
|
+
|
|
2781
|
+type WhoFields [WhoFieldMaximum - WhoFieldMinimum + 1]bool
|
|
2782
|
+
|
|
2783
|
+func (fields *WhoFields) Set(field rune) bool {
|
|
2784
|
+ index := int(field)
|
|
2785
|
+ if WhoFieldMinimum <= index && index <= WhoFieldMaximum {
|
|
2786
|
+ fields[int(field)-WhoFieldMinimum] = true
|
|
2787
|
+ return true
|
|
2788
|
+ } else {
|
|
2789
|
+ return false
|
|
2790
|
+ }
|
|
2791
|
+}
|
|
2792
|
+func (fields *WhoFields) Has(field rune) bool {
|
|
2793
|
+ return fields[int(field)-WhoFieldMinimum]
|
|
2794
|
+}
|
|
2795
|
+
|
|
2796
|
+// rplWhoReply returns the WHO(X) reply between one user and another channel/user.
|
|
2797
|
+// who format:
|
|
2798
|
+// <channel> <user> <host> <server> <nick> <H|G>[*][~|&|@|%|+][B] :<hopcount> <real name>
|
|
2799
|
+// whox format:
|
|
2800
|
+// <type> <channel> <user> <ip> <host> <server> <nick> <H|G>[*][~|&|@|%|+][B] <hops> <idle> <account> <rank> :<real name>
|
|
2801
|
+func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *ResponseBuffer, isWhox bool, fields WhoFields, whoType string) {
|
|
2802
|
+ params := []string{client.Nick()}
|
|
2803
|
+
|
|
2804
|
+ details := target.Details()
|
|
2805
|
+
|
|
2806
|
+ if fields.Has('t') {
|
|
2807
|
+ params = append(params, whoType)
|
|
2808
|
+ }
|
|
2809
|
+ if fields.Has('c') {
|
|
2810
|
+ fChannel := "*"
|
|
2811
|
+ if channel != nil {
|
|
2812
|
+ fChannel = channel.name
|
|
2813
|
+ }
|
|
2814
|
+ params = append(params, fChannel)
|
|
2815
|
+ }
|
|
2816
|
+ if fields.Has('u') {
|
|
2817
|
+ params = append(params, details.username)
|
|
2818
|
+ }
|
|
2819
|
+ if fields.Has('i') {
|
|
2820
|
+ fIP := "255.255.255.255"
|
|
2821
|
+ if client.HasMode(modes.Operator) || client == target {
|
|
2822
|
+ // you can only see a target's IP if they're you or you're an oper
|
|
2823
|
+ fIP = target.IPString()
|
|
2824
|
+ }
|
|
2825
|
+ params = append(params, fIP)
|
|
2826
|
+ }
|
|
2827
|
+ if fields.Has('h') {
|
|
2828
|
+ params = append(params, details.hostname)
|
|
2829
|
+ }
|
|
2830
|
+ if fields.Has('s') {
|
|
2831
|
+ params = append(params, target.server.name)
|
|
2832
|
+ }
|
|
2833
|
+ if fields.Has('n') {
|
|
2834
|
+ params = append(params, details.nick)
|
|
2835
|
+ }
|
|
2836
|
+ if fields.Has('f') { // "flags" (away + oper state + channel status prefix + bot)
|
|
2837
|
+ var flags strings.Builder
|
|
2838
|
+ if target.Away() {
|
|
2839
|
+ flags.WriteRune('G') // Gone
|
|
2840
|
+ } else {
|
|
2841
|
+ flags.WriteRune('H') // Here
|
|
2842
|
+ }
|
|
2843
|
+
|
|
2844
|
+ if target.HasMode(modes.Operator) {
|
|
2845
|
+ flags.WriteRune('*')
|
|
2846
|
+ }
|
|
2847
|
+
|
|
2848
|
+ if channel != nil {
|
|
2849
|
+ flags.WriteString(channel.ClientPrefixes(target, false))
|
|
2850
|
+ }
|
|
2851
|
+
|
|
2852
|
+ if target.HasMode(modes.Bot) {
|
|
2853
|
+ flags.WriteRune('B')
|
|
2854
|
+ }
|
|
2855
|
+
|
|
2856
|
+ params = append(params, flags.String())
|
|
2857
|
+
|
|
2858
|
+ }
|
|
2859
|
+ if fields.Has('d') { // server hops from us to target
|
|
2860
|
+ params = append(params, "0")
|
|
2861
|
+ }
|
|
2862
|
+ if fields.Has('l') {
|
|
2863
|
+ params = append(params, fmt.Sprintf("%d", target.IdleSeconds()))
|
|
2864
|
+ }
|
|
2865
|
+ if fields.Has('a') {
|
|
2866
|
+ fAccount := "0"
|
|
2867
|
+ if details.accountName != "*" {
|
|
2868
|
+ // WHOX uses "0" to mean "no account"
|
|
2869
|
+ fAccount = details.accountName
|
|
2870
|
+ }
|
|
2871
|
+ params = append(params, fAccount)
|
|
2872
|
+ }
|
|
2873
|
+ if fields.Has('o') { // target's channel power level
|
|
2874
|
+ //TODO: implement this
|
|
2875
|
+ params = append(params, "0")
|
|
2876
|
+ }
|
|
2877
|
+ if fields.Has('r') {
|
|
2878
|
+ params = append(params, details.realname)
|
|
2879
|
+ }
|
|
2880
|
+
|
|
2881
|
+ numeric := RPL_WHOSPCRPL
|
|
2882
|
+ if !isWhox {
|
|
2883
|
+ numeric = RPL_WHOREPLY
|
|
2884
|
+ // if this isn't WHOX, stick hops + realname at the end
|
|
2885
|
+ params = append(params, "0 "+details.realname)
|
|
2886
|
+ }
|
|
2887
|
+
|
|
2888
|
+ rb.Add(nil, client.server.name, numeric, params...)
|
|
2889
|
+}
|
|
2890
|
+
|
|
2891
|
+// WHO <mask> [<filter>%<fields>,<type>]
|
2779
|
2892
|
func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
2780
|
2893
|
mask := msg.Params[0]
|
2781
|
2894
|
var err error
|
|
@@ -2793,6 +2906,26 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
|
2793
|
2906
|
return false
|
2794
|
2907
|
}
|
2795
|
2908
|
|
|
2909
|
+ sFields := "cuhsnf"
|
|
2910
|
+ whoType := "0"
|
|
2911
|
+ isWhox := false
|
|
2912
|
+ if len(msg.Params) > 1 && strings.Contains(msg.Params[1], "%") {
|
|
2913
|
+ isWhox = true
|
|
2914
|
+ whoxData := msg.Params[1]
|
|
2915
|
+ fieldStart := strings.Index(whoxData, "%")
|
|
2916
|
+ sFields = whoxData[fieldStart+1:]
|
|
2917
|
+
|
|
2918
|
+ typeIndex := strings.Index(sFields, ",")
|
|
2919
|
+ if typeIndex > -1 && typeIndex < (len(sFields)-1) { // make sure there's , and a value after it
|
|
2920
|
+ whoType = sFields[typeIndex+1:]
|
|
2921
|
+ sFields = strings.ToLower(sFields[:typeIndex])
|
|
2922
|
+ }
|
|
2923
|
+ }
|
|
2924
|
+ var fields WhoFields
|
|
2925
|
+ for _, field := range sFields {
|
|
2926
|
+ fields.Set(field)
|
|
2927
|
+ }
|
|
2928
|
+
|
2796
|
2929
|
//TODO(dan): is this used and would I put this param in the Modern doc?
|
2797
|
2930
|
// if not, can we remove it?
|
2798
|
2931
|
//var operatorOnly bool
|
|
@@ -2810,7 +2943,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
|
2810
|
2943
|
if !channel.flags.HasMode(modes.Secret) || isJoined || isOper {
|
2811
|
2944
|
for _, member := range channel.Members() {
|
2812
|
2945
|
if !member.HasMode(modes.Invisible) || isJoined || isOper {
|
2813
|
|
- client.rplWhoReply(channel, member, rb)
|
|
2946
|
+ client.rplWhoReply(channel, member, rb, isWhox, fields, whoType)
|
2814
|
2947
|
}
|
2815
|
2948
|
}
|
2816
|
2949
|
}
|
|
@@ -2838,7 +2971,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
|
2838
|
2971
|
|
2839
|
2972
|
for mclient := range server.clients.FindAll(mask) {
|
2840
|
2973
|
if isOper || !mclient.HasMode(modes.Invisible) || isFriend(mclient) {
|
2841
|
|
- client.rplWhoReply(nil, mclient, rb)
|
|
2974
|
+ client.rplWhoReply(nil, mclient, rb, isWhox, fields, whoType)
|
2842
|
2975
|
}
|
2843
|
2976
|
}
|
2844
|
2977
|
}
|