Browse Source

fix #1676

Fix various pagination issues with CHATHISTORY; also undo #491
(msgid munging).
tags/v2.8.0-rc1
Shivaram Lingamneni 2 years ago
parent
commit
5bbee02fe6

+ 1
- 1
default.yaml View File

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:

+ 9
- 9
irc/channel.go View File

917
 	}
917
 	}
918
 
918
 
919
 	if hasAutoreplayTimestamps {
919
 	if hasAutoreplayTimestamps {
920
-		_, seq, _ := channel.server.GetHistorySequence(channel, client, "")
920
+		_, seq, _ := channel.server.GetHistorySequence(channel, client, "", 0)
921
 		if seq != nil {
921
 		if seq != nil {
922
 			zncMax := channel.server.Config().History.ZNCMax
922
 			zncMax := channel.server.Config().History.ZNCMax
923
 			items, _ = seq.Between(history.Selector{Time: start}, history.Selector{Time: end}, zncMax)
923
 			items, _ = seq.Between(history.Selector{Time: start}, history.Selector{Time: end}, zncMax)
935
 			replayLimit = channel.server.Config().History.AutoreplayOnJoin
935
 			replayLimit = channel.server.Config().History.AutoreplayOnJoin
936
 		}
936
 		}
937
 		if 0 < replayLimit {
937
 		if 0 < replayLimit {
938
-			_, seq, _ := channel.server.GetHistorySequence(channel, client, "")
938
+			_, seq, _ := channel.server.GetHistorySequence(channel, client, "", 0)
939
 			if seq != nil {
939
 			if seq != nil {
940
 				items, _ = seq.Between(history.Selector{}, history.Selector{}, replayLimit)
940
 				items, _ = seq.Between(history.Selector{}, history.Selector{}, replayLimit)
941
 			}
941
 			}
1084
 				} else {
1084
 				} else {
1085
 					message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName)
1085
 					message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName)
1086
 				}
1086
 				}
1087
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1087
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1088
 			}
1088
 			}
1089
 		case history.Part:
1089
 		case history.Part:
1090
 			if eventPlayback {
1090
 			if eventPlayback {
1094
 					continue // #474
1094
 					continue // #474
1095
 				}
1095
 				}
1096
 				message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message)
1096
 				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)
1097
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1098
 			}
1098
 			}
1099
 		case history.Kick:
1099
 		case history.Kick:
1100
 			if eventPlayback {
1100
 			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)
1101
 				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 {
1102
 			} else {
1103
 				message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message)
1103
 				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)
1104
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1105
 			}
1105
 			}
1106
 		case history.Quit:
1106
 		case history.Quit:
1107
 			if eventPlayback {
1107
 			if eventPlayback {
1111
 					continue // #474
1111
 					continue // #474
1112
 				}
1112
 				}
1113
 				message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message)
1113
 				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)
1114
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1115
 			}
1115
 			}
1116
 		case history.Nick:
1116
 		case history.Nick:
1117
 			if eventPlayback {
1117
 			if eventPlayback {
1118
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "NICK", item.Params[0])
1118
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "NICK", item.Params[0])
1119
 			} else {
1119
 			} else {
1120
 				message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0])
1120
 				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)
1121
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1122
 			}
1122
 			}
1123
 		case history.Topic:
1123
 		case history.Topic:
1124
 			if eventPlayback {
1124
 			if eventPlayback {
1125
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "TOPIC", chname, item.Message.Message)
1125
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "TOPIC", chname, item.Message.Message)
1126
 			} else {
1126
 			} else {
1127
 				message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message)
1127
 				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)
1128
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1129
 			}
1129
 			}
1130
 		case history.Mode:
1130
 		case history.Mode:
1131
 			params := make([]string, len(item.Message.Split)+1)
1131
 			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...)
1137
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "MODE", params...)
1138
 			} else {
1138
 			} else {
1139
 				message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " "))
1139
 				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)
1140
+				rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message)
1141
 			}
1141
 			}
1142
 		}
1142
 		}
1143
 	}
1143
 	}

+ 4
- 4
irc/client.go View File

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:
1713
 	var base, extras []history.TargetListing
1713
 	var base, extras []history.TargetListing
1714
 	var chcfnames []string
1714
 	var chcfnames []string
1715
 	for _, channel := range client.Channels() {
1715
 	for _, channel := range client.Channels() {
1716
-		_, seq, err := client.server.GetHistorySequence(channel, client, "")
1716
+		_, seq, err := client.server.GetHistorySequence(channel, client, "", 0)
1717
 		if seq == nil || err != nil {
1717
 		if seq == nil || err != nil {
1718
 			continue
1718
 			continue
1719
 		}
1719
 		}
1734
 		extras = append(extras, persistentExtras...)
1734
 		extras = append(extras, persistentExtras...)
1735
 	}
1735
 	}
1736
 
1736
 
1737
-	_, cSeq, err := client.server.GetHistorySequence(nil, client, "")
1737
+	_, cSeq, err := client.server.GetHistorySequence(nil, client, "", 0)
1738
 	if err == nil && cSeq != nil {
1738
 	if err == nil && cSeq != nil {
1739
 		correspondents, err := cSeq.ListCorrespondents(start, end, limit)
1739
 		correspondents, err := cSeq.ListCorrespondents(start, end, limit)
1740
 		if err == nil {
1740
 		if err == nil {
1758
 		if strings.HasPrefix(target.CfName, "#") {
1758
 		if strings.HasPrefix(target.CfName, "#") {
1759
 			continue
1759
 			continue
1760
 		}
1760
 		}
1761
-		_, seq, err := client.server.GetHistorySequence(nil, client, target.CfName)
1761
+		_, seq, err := client.server.GetHistorySequence(nil, client, target.CfName, 0)
1762
 		if err == nil && seq != nil {
1762
 		if err == nil && seq != nil {
1763
 			items, err := seq.Between(start, end, messageLimit)
1763
 			items, err := seq.Between(start, end, messageLimit)
1764
 			if err == nil {
1764
 			if err == nil {

+ 12
- 2
irc/handlers.go View File

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)
725
 	if listTargets {
725
 	if listTargets {
726
 		targets, err = client.listTargets(start, end, limit)
726
 		targets, err = client.listTargets(start, end, limit)
727
 	} else {
727
 	} else {
728
-		channel, sequence, err = server.GetHistorySequence(nil, client, target)
728
+		// see #1676; for CHATHISTORY we need to make the paging window as exact as possible,
729
+		// hence filtering out undisplayable messages on the backend, in order to send a full
730
+		// paging window if possible
731
+		var flags history.ExcludeFlags
732
+		if !rb.session.capabilities.Has(caps.EventPlayback) {
733
+			flags |= history.ExcludeTagmsg
734
+		}
735
+		if client.AccountSettings().ReplayJoins == ReplayJoinsNever {
736
+			flags |= history.ExcludeJoins
737
+		}
738
+		channel, sequence, err = server.GetHistorySequence(nil, client, target, flags)
729
 		if err != nil || sequence == nil {
739
 		if err != nil || sequence == nil {
730
 			return
740
 			return
731
 		}
741
 		}

+ 19
- 5
irc/history/history.go View File

53
 	return item.Message.Msgid == msgid
53
 	return item.Message.Msgid == msgid
54
 }
54
 }
55
 
55
 
56
+func (item *Item) IsExcluded(excludeFlags ExcludeFlags) bool {
57
+	switch item.Type {
58
+	case Tagmsg:
59
+		return excludeFlags&ExcludeTagmsg != 0
60
+	case Join, Part, Quit:
61
+		return excludeFlags&ExcludeJoins != 0
62
+	default:
63
+		return false
64
+	}
65
+}
66
+
56
 type Predicate func(item *Item) (matches bool)
67
 type Predicate func(item *Item) (matches bool)
57
 
68
 
58
 func Reverse(results []Item) {
69
 func Reverse(results []Item) {
155
 // with an indication of whether the results are complete or are missing items
166
 // with an indication of whether the results are complete or are missing items
156
 // because some of that period was discarded. A zero value of `before` is considered
167
 // because some of that period was discarded. A zero value of `before` is considered
157
 // higher than all other times.
168
 // higher than all other times.
158
-func (list *Buffer) betweenHelper(start, end Selector, cutoff time.Time, pred Predicate, limit int) (results []Item, complete bool, err error) {
169
+func (list *Buffer) betweenHelper(start, end Selector, cutoff time.Time, pred Predicate, limit int, excludeFlags ExcludeFlags) (results []Item, complete bool, err error) {
159
 	var ascending bool
170
 	var ascending bool
160
 
171
 
161
 	defer func() {
172
 	defer func() {
195
 	satisfies := func(item *Item) bool {
206
 	satisfies := func(item *Item) bool {
196
 		return (after.IsZero() || item.Message.Time.After(after)) &&
207
 		return (after.IsZero() || item.Message.Time.After(after)) &&
197
 			(before.IsZero() || item.Message.Time.Before(before)) &&
208
 			(before.IsZero() || item.Message.Time.Before(before)) &&
198
-			(pred == nil || pred(item))
209
+			(pred == nil || pred(item)) &&
210
+			!item.IsExcluded(excludeFlags)
199
 	}
211
 	}
200
 
212
 
201
 	return list.matchInternal(satisfies, ascending, limit), complete, nil
213
 	return list.matchInternal(satisfies, ascending, limit), complete, nil
279
 	list   *Buffer
291
 	list   *Buffer
280
 	pred   Predicate
292
 	pred   Predicate
281
 	cutoff time.Time
293
 	cutoff time.Time
294
+	flags  ExcludeFlags
282
 }
295
 }
283
 
296
 
284
-func (list *Buffer) MakeSequence(correspondent string, cutoff time.Time) Sequence {
297
+func (list *Buffer) MakeSequence(correspondent string, cutoff time.Time, flags ExcludeFlags) Sequence {
285
 	var pred Predicate
298
 	var pred Predicate
286
 	if correspondent != "" {
299
 	if correspondent != "" {
287
 		pred = func(item *Item) bool {
300
 		pred = func(item *Item) bool {
292
 		list:   list,
305
 		list:   list,
293
 		pred:   pred,
306
 		pred:   pred,
294
 		cutoff: cutoff,
307
 		cutoff: cutoff,
308
+		flags:  flags,
295
 	}
309
 	}
296
 }
310
 }
297
 
311
 
298
 func (seq *bufferSequence) Between(start, end Selector, limit int) (results []Item, err error) {
312
 func (seq *bufferSequence) Between(start, end Selector, limit int) (results []Item, err error) {
299
-	results, _, err = seq.list.betweenHelper(start, end, seq.cutoff, seq.pred, limit)
313
+	results, _, err = seq.list.betweenHelper(start, end, seq.cutoff, seq.pred, limit, seq.flags)
300
 	return
314
 	return
301
 }
315
 }
302
 
316
 
377
 // latest returns the items most recently added, up to `limit`. If `limit` is 0,
391
 // latest returns the items most recently added, up to `limit`. If `limit` is 0,
378
 // it returns all items.
392
 // it returns all items.
379
 func (list *Buffer) latest(limit int) (results []Item) {
393
 func (list *Buffer) latest(limit int) (results []Item) {
380
-	results, _, _ = list.betweenHelper(Selector{}, Selector{}, time.Time{}, nil, limit)
394
+	results, _, _ = list.betweenHelper(Selector{}, Selector{}, time.Time{}, nil, limit, 0)
381
 	return
395
 	return
382
 }
396
 }
383
 
397
 

+ 2
- 2
irc/history/history_test.go View File

15
 )
15
 )
16
 
16
 
17
 func betweenTimestamps(buf *Buffer, start, end time.Time, limit int) (result []Item, complete bool) {
17
 func betweenTimestamps(buf *Buffer, start, end time.Time, limit int) (result []Item, complete bool) {
18
-	result, complete, _ = buf.betweenHelper(Selector{Time: start}, Selector{Time: end}, time.Time{}, nil, limit)
18
+	result, complete, _ = buf.betweenHelper(Selector{Time: start}, Selector{Time: end}, time.Time{}, nil, limit, 0)
19
 	return
19
 	return
20
 }
20
 }
21
 
21
 
45
 	})
45
 	})
46
 	since, complete = betweenTimestamps(buf, pastTime, time.Now(), 0)
46
 	since, complete = betweenTimestamps(buf, pastTime, time.Now(), 0)
47
 	if len(since) != 1 {
47
 	if len(since) != 1 {
48
-		t.Error("should be able to store items in a nonempty buffer")
48
+		t.Errorf("should be able to store items in a nonempty buffer: expected %d, got %d", 1, len(since))
49
 	}
49
 	}
50
 	if !complete {
50
 	if !complete {
51
 		t.Error("results should be complete")
51
 		t.Error("results should be complete")

+ 21
- 0
irc/history/queries.go View File

4
 package history
4
 package history
5
 
5
 
6
 import (
6
 import (
7
+	"strings"
7
 	"time"
8
 	"time"
8
 )
9
 )
9
 
10
 
11
+type ExcludeFlags uint
12
+
13
+const (
14
+	ExcludeTagmsg ExcludeFlags = 1 << iota
15
+	ExcludeJoins
16
+)
17
+
10
 // Selector represents a parameter to a CHATHISTORY command
18
 // Selector represents a parameter to a CHATHISTORY command
11
 type Selector struct {
19
 type Selector struct {
12
 	Msgid string
20
 	Msgid string
77
 	}
85
 	}
78
 	return after, before, ascending
86
 	return after, before, ascending
79
 }
87
 }
88
+
89
+// maps regular msgids from JOIN, etc. to a msgid suitable for attaching
90
+// to a HistServ message describing the JOIN. See #491 for some history.
91
+func HistservMungeMsgid(msgid string) string {
92
+	return "_" + msgid
93
+}
94
+
95
+// strips munging from a msgid. future schemes may not support a well-defined
96
+// mapping of munged msgids to true msgids, but munged msgids should always contain
97
+// a _, with metadata in front and data (possibly the true msgid) after.
98
+func NormalizeMsgid(msgid string) string {
99
+	return strings.TrimPrefix(msgid, "_")
100
+}

+ 1
- 1
irc/histserv.go View File

199
 
199
 
200
 // handles parameter parsing and history queries for /HISTORY and /HISTSERV PLAY
200
 // handles parameter parsing and history queries for /HISTORY and /HISTSERV PLAY
201
 func easySelectHistory(server *Server, client *Client, params []string) (items []history.Item, channel *Channel, err error) {
201
 func easySelectHistory(server *Server, client *Client, params []string) (items []history.Item, channel *Channel, err error) {
202
-	channel, sequence, err := server.GetHistorySequence(nil, client, params[0])
202
+	channel, sequence, err := server.GetHistorySequence(nil, client, params[0], 0)
203
 
203
 
204
 	if sequence == nil || err != nil {
204
 	if sequence == nil || err != nil {
205
 		return nil, nil, errNoSuchChannel
205
 		return nil, nil, errNoSuchChannel

+ 53
- 1
irc/mysql/history.go View File

40
 	keySchemaMinorVersion = "db.minorversion"
40
 	keySchemaMinorVersion = "db.minorversion"
41
 	cleanupRowLimit       = 50
41
 	cleanupRowLimit       = 50
42
 	cleanupPauseTime      = 10 * time.Minute
42
 	cleanupPauseTime      = 10 * time.Minute
43
+
44
+	// if we don't fill the pagination window due to exclusions,
45
+	// retry with an expanded window at most this many times
46
+	maxPaginationRetries = 3
43
 )
47
 )
44
 
48
 
45
 type e struct{}
49
 type e struct{}
1033
 	target        string
1037
 	target        string
1034
 	correspondent string
1038
 	correspondent string
1035
 	cutoff        time.Time
1039
 	cutoff        time.Time
1040
+	excludeFlags  history.ExcludeFlags
1036
 }
1041
 }
1037
 
1042
 
1038
 func (s *mySQLHistorySequence) Between(start, end history.Selector, limit int) (results []history.Item, err error) {
1043
 func (s *mySQLHistorySequence) Between(start, end history.Selector, limit int) (results []history.Item, err error) {
1044
+	if s.excludeFlags == 0 {
1045
+		return s.baseBetween(start, end, limit)
1046
+	} else {
1047
+		return s.betweenWithRetries(start, end, limit)
1048
+	}
1049
+}
1050
+
1051
+func (s *mySQLHistorySequence) baseBetween(start, end history.Selector, limit int) (results []history.Item, err error) {
1039
 	ctx, cancel := context.WithTimeout(context.Background(), s.mysql.getTimeout())
1052
 	ctx, cancel := context.WithTimeout(context.Background(), s.mysql.getTimeout())
1040
 	defer cancel()
1053
 	defer cancel()
1041
 
1054
 
1058
 	return results, err
1071
 	return results, err
1059
 }
1072
 }
1060
 
1073
 
1074
+func (s *mySQLHistorySequence) betweenWithRetries(start, end history.Selector, limit int) (results []history.Item, err error) {
1075
+	applyExclusions := func(currentResults []history.Item, excludeFlags history.ExcludeFlags, trueLimit int) (filteredResults []history.Item) {
1076
+		filteredResults = make([]history.Item, 0, len(currentResults))
1077
+		for _, item := range currentResults {
1078
+			if !item.IsExcluded(excludeFlags) {
1079
+				filteredResults = append(filteredResults, item)
1080
+			}
1081
+			if len(filteredResults) == trueLimit {
1082
+				break
1083
+			}
1084
+		}
1085
+		return
1086
+	}
1087
+
1088
+	i := 1
1089
+	for {
1090
+		currentLimit := limit * i
1091
+		currentResults, err := s.baseBetween(start, end, currentLimit)
1092
+		if err != nil {
1093
+			return nil, err
1094
+		}
1095
+		results = applyExclusions(currentResults, s.excludeFlags, limit)
1096
+		// we're done in any of these three cases:
1097
+		// (1) we filled the window (2) we ran out of results on the backend (3) we can't retry anymore
1098
+		if len(results) == limit || len(currentResults) < currentLimit || i == maxPaginationRetries {
1099
+			return results, nil
1100
+		}
1101
+		i++
1102
+	}
1103
+}
1104
+
1061
 func (s *mySQLHistorySequence) Around(start history.Selector, limit int) (results []history.Item, err error) {
1105
 func (s *mySQLHistorySequence) Around(start history.Selector, limit int) (results []history.Item, err error) {
1106
+	// temporarily clear the exclude flags when running GenericAround, since we don't care about
1107
+	// the exactness of the paging window at all
1108
+	oldExcludeFlags := s.excludeFlags
1109
+	s.excludeFlags = 0
1110
+	defer func() {
1111
+		s.excludeFlags = oldExcludeFlags
1112
+	}()
1062
 	return history.GenericAround(s, start, limit)
1113
 	return history.GenericAround(s, start, limit)
1063
 }
1114
 }
1064
 
1115
 
1083
 	return false
1134
 	return false
1084
 }
1135
 }
1085
 
1136
 
1086
-func (mysql *MySQL) MakeSequence(target, correspondent string, cutoff time.Time) history.Sequence {
1137
+func (mysql *MySQL) MakeSequence(target, correspondent string, cutoff time.Time, excludeFlags history.ExcludeFlags) history.Sequence {
1087
 	return &mySQLHistorySequence{
1138
 	return &mySQLHistorySequence{
1088
 		target:        target,
1139
 		target:        target,
1089
 		correspondent: correspondent,
1140
 		correspondent: correspondent,
1090
 		mysql:         mysql,
1141
 		mysql:         mysql,
1091
 		cutoff:        cutoff,
1142
 		cutoff:        cutoff,
1143
+		excludeFlags:  excludeFlags,
1092
 	}
1144
 	}
1093
 }
1145
 }

+ 3
- 3
irc/server.go View File

862
 // suitable for ListCorrespondents (i.e., this function is still used to
862
 // suitable for ListCorrespondents (i.e., this function is still used to
863
 // decide whether the ringbuf or mysql is authoritative about the client's
863
 // decide whether the ringbuf or mysql is authoritative about the client's
864
 // message history).
864
 // message history).
865
-func (server *Server) GetHistorySequence(providedChannel *Channel, client *Client, query string) (channel *Channel, sequence history.Sequence, err error) {
865
+func (server *Server) GetHistorySequence(providedChannel *Channel, client *Client, query string, excludeFlags history.ExcludeFlags) (channel *Channel, sequence history.Sequence, err error) {
866
 	config := server.Config()
866
 	config := server.Config()
867
 	// 4 cases: {persistent, ephemeral} x {normal, conversation}
867
 	// 4 cases: {persistent, ephemeral} x {normal, conversation}
868
 	// with ephemeral history, target is implicit in the choice of `hist`,
868
 	// with ephemeral history, target is implicit in the choice of `hist`,
940
 	}
940
 	}
941
 
941
 
942
 	if hist != nil {
942
 	if hist != nil {
943
-		sequence = hist.MakeSequence(correspondent, cutoff)
943
+		sequence = hist.MakeSequence(correspondent, cutoff, excludeFlags)
944
 	} else if target != "" {
944
 	} else if target != "" {
945
-		sequence = server.historyDB.MakeSequence(target, correspondent, cutoff)
945
+		sequence = server.historyDB.MakeSequence(target, correspondent, cutoff, excludeFlags)
946
 	}
946
 	}
947
 	return
947
 	return
948
 }
948
 }

+ 0
- 23
irc/utils/crypto.go View File

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 View File

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"

+ 1
- 1
irc/znc.go View File

189
 }
189
 }
190
 
190
 
191
 func zncPlayPrivmsgsFrom(client *Client, rb *ResponseBuffer, target string, start, end time.Time) {
191
 func zncPlayPrivmsgsFrom(client *Client, rb *ResponseBuffer, target string, start, end time.Time) {
192
-	_, sequence, err := client.server.GetHistorySequence(nil, client, target)
192
+	_, sequence, err := client.server.GetHistorySequence(nil, client, target, 0)
193
 	if sequence == nil || err != nil {
193
 	if sequence == nil || err != nil {
194
 		return
194
 		return
195
 	}
195
 	}

Loading…
Cancel
Save