Browse Source

Merge pull request #1184 from jesopo/whox

implement WHOX
tags/v2.2.0-rc1
Shivaram Lingamneni 4 years ago
parent
commit
bdfee9cb39
No account linked to committer's email address
4 changed files with 138 additions and 32 deletions
  1. 1
    0
      irc/config.go
  2. 136
    3
      irc/handlers.go
  3. 1
    0
      irc/numerics.go
  4. 0
    29
      irc/server.go

+ 1
- 0
irc/config.go View File

@@ -1240,6 +1240,7 @@ func (config *Config) generateISupport() (err error) {
1240 1240
 	if config.Server.Casemapping == CasemappingPRECIS {
1241 1241
 		isupport.Add("UTF8MAPPING", precisUTF8MappingToken)
1242 1242
 	}
1243
+	isupport.Add("WHOX", "")
1243 1244
 
1244 1245
 	err = isupport.RegenerateCachedReply()
1245 1246
 	return

+ 136
- 3
irc/handlers.go View File

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

+ 1
- 0
irc/numerics.go View File

@@ -86,6 +86,7 @@ const (
86 86
 	RPL_VERSION                   = "351"
87 87
 	RPL_WHOREPLY                  = "352"
88 88
 	RPL_NAMREPLY                  = "353"
89
+	RPL_WHOSPCRPL                 = "354"
89 90
 	RPL_LINKS                     = "364"
90 91
 	RPL_ENDOFLINKS                = "365"
91 92
 	RPL_ENDOFNAMES                = "366"

+ 0
- 29
irc/server.go View File

@@ -441,35 +441,6 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
441 441
 	}
442 442
 }
443 443
 
444
-// rplWhoReply returns the WHO reply between one user and another channel/user.
445
-// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
446
-// :<hopcount> <real name>
447
-func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *ResponseBuffer) {
448
-	channelName := "*"
449
-	flags := ""
450
-
451
-	if target.Away() {
452
-		flags = "G"
453
-	} else {
454
-		flags = "H"
455
-	}
456
-	if target.HasMode(modes.Operator) {
457
-		flags += "*"
458
-	}
459
-
460
-	if channel != nil {
461
-		// TODO is this right?
462
-		flags += channel.ClientPrefixes(target, rb.session.capabilities.Has(caps.MultiPrefix))
463
-		channelName = channel.name
464
-	}
465
-	if target.HasMode(modes.Bot) {
466
-		flags += "B"
467
-	}
468
-	details := target.Details()
469
-	// hardcode a hopcount of 0 for now
470
-	rb.Add(nil, client.server.name, RPL_WHOREPLY, client.Nick(), channelName, details.username, details.hostname, client.server.name, details.nick, flags, "0 "+details.realname)
471
-}
472
-
473 444
 // rehash reloads the config and applies the changes from the config file.
474 445
 func (server *Server) rehash() error {
475 446
 	server.logger.Info("server", "Attempting rehash")

Loading…
Cancel
Save