Quellcode durchsuchen

fix #1676, take 2

Ensure the pagination window is full by making sure that every history item
gets a replay line in CHATHISTORY output, even TAGMSG.
tags/v2.8.0-rc1
Shivaram Lingamneni vor 2 Jahren
Ursprung
Commit
4749d7e776
10 geänderte Dateien mit 51 neuen und 79 gelöschten Zeilen
  1. 1
    1
      default.yaml
  2. 20
    15
      irc/channel.go
  3. 7
    2
      irc/client.go
  4. 5
    5
      irc/handlers.go
  5. 14
    0
      irc/history/queries.go
  6. 0
    23
      irc/utils/crypto.go
  7. 0
    29
      irc/utils/crypto_test.go
  8. 2
    2
      irc/znc.go
  9. 1
    1
      irctest
  10. 1
    1
      traditional.yaml

+ 1
- 1
default.yaml Datei anzeigen

962
         # if `default` is false, store TAGMSG containing any of these tags:
962
         # if `default` is false, store TAGMSG containing any of these tags:
963
         whitelist:
963
         whitelist:
964
             - "+draft/react"
964
             - "+draft/react"
965
-            - "react"
965
+            - "+react"
966
 
966
 
967
         # if `default` is true, don't store TAGMSG containing any of these tags:
967
         # if `default` is true, don't store TAGMSG containing any of these tags:
968
         #blacklist:
968
         #blacklist:

+ 20
- 15
irc/channel.go Datei anzeigen

952
 		}
952
 		}
953
 	}
953
 	}
954
 	if 0 < numItems {
954
 	if 0 < numItems {
955
-		channel.replayHistoryItems(rb, items, true)
955
+		channel.replayHistoryItems(rb, items, false)
956
 		rb.Flush(true)
956
 		rb.Flush(true)
957
 	}
957
 	}
958
 }
958
 }
1035
 	client.server.logger.Debug("channels", fmt.Sprintf("%s left channel %s", details.nick, chname))
1035
 	client.server.logger.Debug("channels", fmt.Sprintf("%s left channel %s", details.nick, chname))
1036
 }
1036
 }
1037
 
1037
 
1038
-func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.Item, autoreplay bool) {
1038
+func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.Item, chathistoryCommand bool) {
1039
 	// send an empty batch if necessary, as per the CHATHISTORY spec
1039
 	// send an empty batch if necessary, as per the CHATHISTORY spec
1040
 	chname := channel.Name()
1040
 	chname := channel.Name()
1041
 	client := rb.target
1041
 	client := rb.target
1043
 	extendedJoin := rb.session.capabilities.Has(caps.ExtendedJoin)
1043
 	extendedJoin := rb.session.capabilities.Has(caps.ExtendedJoin)
1044
 	var playJoinsAsPrivmsg bool
1044
 	var playJoinsAsPrivmsg bool
1045
 	if !eventPlayback {
1045
 	if !eventPlayback {
1046
-		switch client.AccountSettings().ReplayJoins {
1047
-		case ReplayJoinsCommandsOnly:
1048
-			playJoinsAsPrivmsg = !autoreplay
1049
-		case ReplayJoinsAlways:
1046
+		if chathistoryCommand {
1050
 			playJoinsAsPrivmsg = true
1047
 			playJoinsAsPrivmsg = true
1051
-		case ReplayJoinsNever:
1052
-			playJoinsAsPrivmsg = false
1048
+		} else {
1049
+			switch client.AccountSettings().ReplayJoins {
1050
+			case ReplayJoinsCommandsOnly:
1051
+				playJoinsAsPrivmsg = false
1052
+			case ReplayJoinsAlways:
1053
+				playJoinsAsPrivmsg = true
1054
+			}
1053
 		}
1055
 		}
1054
 	}
1056
 	}
1055
 
1057
 
1066
 		case history.Tagmsg:
1068
 		case history.Tagmsg:
1067
 			if eventPlayback {
1069
 			if eventPlayback {
1068
 				rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.IsBot, item.Tags, "TAGMSG", chname, item.Message)
1070
 				rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.IsBot, item.Tags, "TAGMSG", chname, item.Message)
1071
+			} else if chathistoryCommand {
1072
+				// #1676, we have to send something here or else it breaks pagination
1073
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, fmt.Sprintf(client.t("%s sent a TAGMSG"), nick))
1069
 			}
1074
 			}
1070
 		case history.Join:
1075
 		case history.Join:
1071
 			if eventPlayback {
1076
 			if eventPlayback {
1084
 				} else {
1089
 				} else {
1085
 					message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName)
1090
 					message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName)
1086
 				}
1091
 				}
1087
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1092
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1088
 			}
1093
 			}
1089
 		case history.Part:
1094
 		case history.Part:
1090
 			if eventPlayback {
1095
 			if eventPlayback {
1094
 					continue // #474
1099
 					continue // #474
1095
 				}
1100
 				}
1096
 				message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message)
1101
 				message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message)
1097
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1102
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1098
 			}
1103
 			}
1099
 		case history.Kick:
1104
 		case history.Kick:
1100
 			if eventPlayback {
1105
 			if eventPlayback {
1101
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "KICK", chname, item.Params[0], item.Message.Message)
1106
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "KICK", chname, item.Params[0], item.Message.Message)
1102
 			} else {
1107
 			} else {
1103
 				message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message)
1108
 				message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message)
1104
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1109
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1105
 			}
1110
 			}
1106
 		case history.Quit:
1111
 		case history.Quit:
1107
 			if eventPlayback {
1112
 			if eventPlayback {
1111
 					continue // #474
1116
 					continue // #474
1112
 				}
1117
 				}
1113
 				message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message)
1118
 				message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message)
1114
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1119
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1115
 			}
1120
 			}
1116
 		case history.Nick:
1121
 		case history.Nick:
1117
 			if eventPlayback {
1122
 			if eventPlayback {
1118
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "NICK", item.Params[0])
1123
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "NICK", item.Params[0])
1119
 			} else {
1124
 			} else {
1120
 				message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0])
1125
 				message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0])
1121
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1126
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1122
 			}
1127
 			}
1123
 		case history.Topic:
1128
 		case history.Topic:
1124
 			if eventPlayback {
1129
 			if eventPlayback {
1125
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "TOPIC", chname, item.Message.Message)
1130
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "TOPIC", chname, item.Message.Message)
1126
 			} else {
1131
 			} else {
1127
 				message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message)
1132
 				message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message)
1128
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1133
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1129
 			}
1134
 			}
1130
 		case history.Mode:
1135
 		case history.Mode:
1131
 			params := make([]string, len(item.Message.Split)+1)
1136
 			params := make([]string, len(item.Message.Split)+1)
1137
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "MODE", params...)
1142
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "MODE", params...)
1138
 			} else {
1143
 			} else {
1139
 				message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " "))
1144
 				message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " "))
1140
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1145
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1141
 			}
1146
 			}
1142
 		}
1147
 		}
1143
 	}
1148
 	}

+ 7
- 2
irc/client.go Datei anzeigen

853
 	session.Send(nil, "", "PING", session.client.Nick())
853
 	session.Send(nil, "", "PING", session.client.Nick())
854
 }
854
 }
855
 
855
 
856
-func (client *Client) replayPrivmsgHistory(rb *ResponseBuffer, items []history.Item, target string) {
856
+func (client *Client) replayPrivmsgHistory(rb *ResponseBuffer, items []history.Item, target string, chathistoryCommand bool) {
857
 	var batchID string
857
 	var batchID string
858
 	details := client.Details()
858
 	details := client.Details()
859
 	nick := details.nick
859
 	nick := details.nick
883
 			if hasEventPlayback {
883
 			if hasEventPlayback {
884
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "INVITE", nick, item.Message.Message)
884
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "INVITE", nick, item.Message.Message)
885
 			} else {
885
 			} else {
886
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", fmt.Sprintf(client.t("%[1]s invited you to channel %[2]s"), NUHToNick(item.Nick), item.Message.Message))
886
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", fmt.Sprintf(client.t("%[1]s invited you to channel %[2]s"), NUHToNick(item.Nick), item.Message.Message))
887
 			}
887
 			}
888
 			continue
888
 			continue
889
 		case history.Privmsg:
889
 		case history.Privmsg:
893
 		case history.Tagmsg:
893
 		case history.Tagmsg:
894
 			if hasEventPlayback && hasTags {
894
 			if hasEventPlayback && hasTags {
895
 				command = "TAGMSG"
895
 				command = "TAGMSG"
896
+			} else if chathistoryCommand {
897
+				// #1676: send something for TAGMSG; we can't discard it entirely
898
+				// because it'll break pagination
899
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", fmt.Sprintf(client.t("%[1]s sent you a TAGMSG"), NUHToNick(item.Nick)))
896
 			} else {
900
 			} else {
897
 				continue
901
 				continue
898
 			}
902
 			}
899
 		default:
903
 		default:
904
+			// see #1676, this shouldn't happen
900
 			continue
905
 			continue
901
 		}
906
 		}
902
 		var tags map[string]string
907
 		var tags map[string]string

+ 5
- 5
irc/handlers.go Datei anzeigen

611
 						target.Time.Format(IRCv3TimestampFormat))
611
 						target.Time.Format(IRCv3TimestampFormat))
612
 				}
612
 				}
613
 			} else if channel != nil {
613
 			} else if channel != nil {
614
-				channel.replayHistoryItems(rb, items, false)
614
+				channel.replayHistoryItems(rb, items, true)
615
 			} else {
615
 			} else {
616
-				client.replayPrivmsgHistory(rb, items, target)
616
+				client.replayPrivmsgHistory(rb, items, target, true)
617
 			}
617
 			}
618
 		}
618
 		}
619
 	}()
619
 	}()
640
 		}
640
 		}
641
 		identifier, value := strings.ToLower(pieces[0]), pieces[1]
641
 		identifier, value := strings.ToLower(pieces[0]), pieces[1]
642
 		if identifier == "msgid" {
642
 		if identifier == "msgid" {
643
-			msgid, err = value, nil
643
+			msgid, err = history.NormalizeMsgid(value), nil
644
 			return
644
 			return
645
 		} else if identifier == "timestamp" {
645
 		} else if identifier == "timestamp" {
646
 			timestamp, err = time.Parse(IRCv3TimestampFormat, value)
646
 			timestamp, err = time.Parse(IRCv3TimestampFormat, value)
1122
 
1122
 
1123
 	if len(items) != 0 {
1123
 	if len(items) != 0 {
1124
 		if channel != nil {
1124
 		if channel != nil {
1125
-			channel.replayHistoryItems(rb, items, false)
1125
+			channel.replayHistoryItems(rb, items, true)
1126
 		} else {
1126
 		} else {
1127
-			client.replayPrivmsgHistory(rb, items, "")
1127
+			client.replayPrivmsgHistory(rb, items, "", true)
1128
 		}
1128
 		}
1129
 	}
1129
 	}
1130
 	return false
1130
 	return false

+ 14
- 0
irc/history/queries.go Datei anzeigen

4
 package history
4
 package history
5
 
5
 
6
 import (
6
 import (
7
+	"strings"
7
 	"time"
8
 	"time"
8
 )
9
 )
9
 
10
 
77
 	}
78
 	}
78
 	return after, before, ascending
79
 	return after, before, ascending
79
 }
80
 }
81
+
82
+// maps regular msgids from JOIN, etc. to a msgid suitable for attaching
83
+// to a HistServ message describing the JOIN. See #491 for some history.
84
+func HistservMungeMsgid(msgid string) string {
85
+	return "_" + msgid
86
+}
87
+
88
+// strips munging from a msgid. future schemes may not support a well-defined
89
+// mapping of munged msgids to true msgids, but munged msgids should always contain
90
+// a _, with metadata in front and data (possibly the true msgid) after.
91
+func NormalizeMsgid(msgid string) string {
92
+	return strings.TrimPrefix(msgid, "_")
93
+}

+ 0
- 23
irc/utils/crypto.go Datei anzeigen

42
 	return B32Encoder.EncodeToString(buf[:])
42
 	return B32Encoder.EncodeToString(buf[:])
43
 }
43
 }
44
 
44
 
45
-// "munge" a secret token to a new value. requirements:
46
-// 1. MUST be roughly as unlikely to collide with `GenerateSecretToken` outputs
47
-// as those outputs are with each other
48
-// 2. SHOULD be deterministic (motivation: if a JOIN line has msgid x,
49
-// create a deterministic msgid y for the fake HistServ PRIVMSG that "replays" it)
50
-// 3. SHOULD be in the same "namespace" as `GenerateSecretToken` outputs
51
-// (same length and character set)
52
-func MungeSecretToken(token string) (result string) {
53
-	bytes, err := B32Encoder.DecodeString(token)
54
-	if err != nil {
55
-		// this should never happen
56
-		return GenerateSecretToken()
57
-	}
58
-	// add 1 with carrying
59
-	for i := len(bytes) - 1; 0 <= i; i -= 1 {
60
-		bytes[i] += 1
61
-		if bytes[i] != 0 {
62
-			break
63
-		} // else: overflow, carry to the next place
64
-	}
65
-	return B32Encoder.EncodeToString(bytes)
66
-}
67
-
68
 // securely check if a supplied token matches a stored token
45
 // securely check if a supplied token matches a stored token
69
 func SecretTokensMatch(storedToken string, suppliedToken string) bool {
46
 func SecretTokensMatch(storedToken string, suppliedToken string) bool {
70
 	// XXX fix a potential gotcha: if the stored token is uninitialized,
47
 	// XXX fix a potential gotcha: if the stored token is uninitialized,

+ 0
- 29
irc/utils/crypto_test.go Datei anzeigen

47
 	}
47
 	}
48
 }
48
 }
49
 
49
 
50
-func TestMunging(t *testing.T) {
51
-	count := 131072
52
-	set := make(map[string]bool)
53
-	var token string
54
-	for i := 0; i < count; i++ {
55
-		token = GenerateSecretToken()
56
-		set[token] = true
57
-	}
58
-	// all tokens generated thus far should be unique
59
-	assertEqual(len(set), count, t)
60
-
61
-	// iteratively munge the last generated token an additional `count` times
62
-	mungedToken := token
63
-	for i := 0; i < count; i++ {
64
-		mungedToken = MungeSecretToken(mungedToken)
65
-		assertEqual(len(mungedToken), len(token), t)
66
-		set[mungedToken] = true
67
-	}
68
-	// munged tokens should not collide with generated tokens, or each other
69
-	assertEqual(len(set), count*2, t)
70
-}
71
-
72
 func BenchmarkGenerateSecretToken(b *testing.B) {
50
 func BenchmarkGenerateSecretToken(b *testing.B) {
73
 	for i := 0; i < b.N; i++ {
51
 	for i := 0; i < b.N; i++ {
74
 		GenerateSecretToken()
52
 		GenerateSecretToken()
75
 	}
53
 	}
76
 }
54
 }
77
 
55
 
78
-func BenchmarkMungeSecretToken(b *testing.B) {
79
-	t := GenerateSecretToken()
80
-	for i := 0; i < b.N; i++ {
81
-		t = MungeSecretToken(t)
82
-	}
83
-}
84
-
85
 func TestCertfpComparisons(t *testing.T) {
56
 func TestCertfpComparisons(t *testing.T) {
86
 	opensslFP := "3D:6B:11:BF:B4:05:C3:F8:4B:38:CD:30:38:FB:EC:01:71:D5:03:54:79:04:07:88:4C:A5:5D:23:41:85:66:C9"
57
 	opensslFP := "3D:6B:11:BF:B4:05:C3:F8:4B:38:CD:30:38:FB:EC:01:71:D5:03:54:79:04:07:88:4C:A5:5D:23:41:85:66:C9"
87
 	oragonoFP := "3d6b11bfb405c3f84b38cd3038fbec0171d50354790407884ca55d23418566c9"
58
 	oragonoFP := "3d6b11bfb405c3f84b38cd3038fbec0171d50354790407884ca55d23418566c9"

+ 2
- 2
irc/znc.go Datei anzeigen

196
 	zncMax := client.server.Config().History.ZNCMax
196
 	zncMax := client.server.Config().History.ZNCMax
197
 	items, err := sequence.Between(history.Selector{Time: start}, history.Selector{Time: end}, zncMax)
197
 	items, err := sequence.Between(history.Selector{Time: start}, history.Selector{Time: end}, zncMax)
198
 	if err == nil && len(items) != 0 {
198
 	if err == nil && len(items) != 0 {
199
-		client.replayPrivmsgHistory(rb, items, target)
199
+		client.replayPrivmsgHistory(rb, items, target, false)
200
 	}
200
 	}
201
 }
201
 }
202
 
202
 
204
 	zncMax := client.server.Config().History.ZNCMax
204
 	zncMax := client.server.Config().History.ZNCMax
205
 	items, err := client.privmsgsBetween(start, end, maxDMTargetsForAutoplay, zncMax)
205
 	items, err := client.privmsgsBetween(start, end, maxDMTargetsForAutoplay, zncMax)
206
 	if err == nil && len(items) != 0 {
206
 	if err == nil && len(items) != 0 {
207
-		client.replayPrivmsgHistory(rb, items, "")
207
+		client.replayPrivmsgHistory(rb, items, "", false)
208
 	}
208
 	}
209
 }
209
 }
210
 
210
 

+ 1
- 1
irctest

1
-Subproject commit 33f0702c260ea716a4ad0f24821a50d44c91fca1
1
+Subproject commit 5e4ae7c99965801cd91d974637ad344a47b5414f

+ 1
- 1
traditional.yaml Datei anzeigen

935
         # if `default` is false, store TAGMSG containing any of these tags:
935
         # if `default` is false, store TAGMSG containing any of these tags:
936
         whitelist:
936
         whitelist:
937
             - "+draft/react"
937
             - "+draft/react"
938
-            - "react"
938
+            - "+react"
939
 
939
 
940
         # if `default` is true, don't store TAGMSG containing any of these tags:
940
         # if `default` is true, don't store TAGMSG containing any of these tags:
941
         #blacklist:
941
         #blacklist:

Laden…
Abbrechen
Speichern