소스 검색

implement a channel forwarding mode

Fixes #1260
tags/v2.5.0-rc1
Shivaram Lingamneni 3 년 전
부모
커밋
ba72d3acfc
10개의 변경된 파일101개의 추가작업 그리고 28개의 파일을 삭제
  1. 7
    0
      distrib/atheme/atheme2json.py
  2. 30
    15
      irc/channel.go
  3. 5
    5
      irc/channelmanager.go
  4. 7
    0
      irc/channelreg.go
  5. 6
    0
      irc/getters.go
  6. 10
    3
      irc/handlers.go
  7. 6
    0
      irc/import.go
  8. 24
    1
      irc/modes.go
  9. 5
    4
      irc/modes/modes.go
  10. 1
    0
      irc/numerics.go

+ 7
- 0
distrib/atheme/atheme2json.py 파일 보기

@@ -94,6 +94,13 @@ def convert(infile):
94 94
                 out['channels'][chname]['topicSetBy'] = parts[3]
95 95
             elif category == 'private:topic:ts':
96 96
                 out['channels'][chname]['topicSetAt'] = to_unixnano(parts[3])
97
+            elif category == 'private:mlockext':
98
+                # the channel forward mode is +L on insp/unreal, +f on charybdis
99
+                # charybdis has a +L ("large banlist") taking no argument
100
+                # and unreal has a +f ("flood limit") taking two colon-delimited numbers,
101
+                # so check for an argument that starts with a #
102
+                if parts[3].startswith('L#') or parts[3].startswith('f#'):
103
+                    out['channels'][chname]['forward'] = parts[3][1:]
97 104
         elif category == 'CA':
98 105
             # channel access lists
99 106
             # CA #mychannel shivaram +AFORafhioqrstv 1600134478 shivaram

+ 30
- 15
irc/channel.go 파일 보기

@@ -28,6 +28,7 @@ type Channel struct {
28 28
 	flags             modes.ModeSet
29 29
 	lists             map[modes.Mode]*UserMaskSet
30 30
 	key               string
31
+	forward           string
31 32
 	members           MemberSet
32 33
 	membersCache      []*Client // allow iteration over channel members without holding the lock
33 34
 	name              string
@@ -133,6 +134,7 @@ func (channel *Channel) applyRegInfo(chanReg RegisteredChannel) {
133 134
 	channel.key = chanReg.Key
134 135
 	channel.userLimit = chanReg.UserLimit
135 136
 	channel.settings = chanReg.Settings
137
+	channel.forward = chanReg.Forward
136 138
 
137 139
 	for _, mode := range chanReg.Modes {
138 140
 		channel.flags.SetMode(mode, true)
@@ -163,6 +165,7 @@ func (channel *Channel) ExportRegistration(includeFlags uint) (info RegisteredCh
163 165
 
164 166
 	if includeFlags&IncludeModes != 0 {
165 167
 		info.Key = channel.key
168
+		info.Forward = channel.forward
166 169
 		info.Modes = channel.flags.AllModes()
167 170
 		info.UserLimit = channel.userLimit
168 171
 	}
@@ -612,20 +615,27 @@ func (channel *Channel) modeStrings(client *Client) (result []string) {
612 615
 	isMember := hasPrivs || channel.members[client] != nil
613 616
 	showKey := isMember && (channel.key != "")
614 617
 	showUserLimit := channel.userLimit > 0
618
+	showForward := channel.forward != ""
615 619
 
616
-	mods := "+"
620
+	var mods strings.Builder
621
+	mods.WriteRune('+')
617 622
 
618 623
 	// flags with args
619 624
 	if showKey {
620
-		mods += modes.Key.String()
625
+		mods.WriteRune(rune(modes.Key))
621 626
 	}
622 627
 	if showUserLimit {
623
-		mods += modes.UserLimit.String()
628
+		mods.WriteRune(rune(modes.UserLimit))
629
+	}
630
+	if showForward {
631
+		mods.WriteRune(rune(modes.Forward))
624 632
 	}
625 633
 
626
-	mods += channel.flags.String()
634
+	for _, m := range channel.flags.AllModes() {
635
+		mods.WriteRune(rune(m))
636
+	}
627 637
 
628
-	result = []string{mods}
638
+	result = []string{mods.String()}
629 639
 
630 640
 	// args for flags with args: The order must match above to keep
631 641
 	// positional arguments in place.
@@ -635,6 +645,9 @@ func (channel *Channel) modeStrings(client *Client) (result []string) {
635 645
 	if showUserLimit {
636 646
 		result = append(result, strconv.Itoa(channel.userLimit))
637 647
 	}
648
+	if showForward {
649
+		result = append(result, channel.forward)
650
+	}
638 651
 
639 652
 	return
640 653
 }
@@ -694,7 +707,7 @@ func (channel *Channel) AddHistoryItem(item history.Item, account string) (err e
694 707
 }
695 708
 
696 709
 // Join joins the given client to this channel (if they can be joined).
697
-func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) error {
710
+func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) (joinErr error, forward string) {
698 711
 	details := client.Details()
699 712
 
700 713
 	channel.stateMutex.RLock()
@@ -707,11 +720,12 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
707 720
 	chcount := len(channel.members)
708 721
 	_, alreadyJoined := channel.members[client]
709 722
 	persistentMode := channel.accountToUMode[details.account]
723
+	forward = channel.forward
710 724
 	channel.stateMutex.RUnlock()
711 725
 
712 726
 	if alreadyJoined {
713 727
 		// no message needs to be sent
714
-		return nil
728
+		return nil, ""
715 729
 	}
716 730
 
717 731
 	// 0. SAJOIN always succeeds
@@ -723,32 +737,33 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
723 737
 		client.CheckInvited(chcfname, createdAt)
724 738
 	if !hasPrivs {
725 739
 		if limit != 0 && chcount >= limit {
726
-			return errLimitExceeded
740
+			return errLimitExceeded, forward
727 741
 		}
728 742
 
729 743
 		if chkey != "" && !utils.SecretTokensMatch(chkey, key) {
730
-			return errWrongChannelKey
744
+			return errWrongChannelKey, forward
731 745
 		}
732 746
 
733 747
 		if channel.flags.HasMode(modes.InviteOnly) &&
734 748
 			!channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) {
735
-			return errInviteOnly
749
+			return errInviteOnly, forward
736 750
 		}
737 751
 
738 752
 		if channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) &&
739 753
 			!channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) &&
740 754
 			!channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) {
741
-			return errBanned
755
+			// do not forward people who are banned:
756
+			return errBanned, ""
742 757
 		}
743 758
 
744 759
 		if details.account == "" &&
745 760
 			(channel.flags.HasMode(modes.RegisteredOnly) || channel.server.Defcon() <= 2) {
746
-			return errRegisteredOnly
761
+			return errRegisteredOnly, forward
747 762
 		}
748 763
 	}
749 764
 
750 765
 	if joinErr := client.addChannel(channel, rb == nil); joinErr != nil {
751
-		return joinErr
766
+		return joinErr, ""
752 767
 	}
753 768
 
754 769
 	client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", details.nick, chname))
@@ -795,7 +810,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
795 810
 	}
796 811
 
797 812
 	if rb == nil {
798
-		return nil
813
+		return nil, ""
799 814
 	}
800 815
 
801 816
 	var modestr string
@@ -858,7 +873,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
858 873
 	rb.Flush(true)
859 874
 
860 875
 	channel.autoReplayHistory(client, rb, message.Msgid)
861
-	return nil
876
+	return nil, ""
862 877
 }
863 878
 
864 879
 func (channel *Channel) autoReplayHistory(client *Client, rb *ResponseBuffer, skipMsgid string) {

+ 5
- 5
irc/channelmanager.go 파일 보기

@@ -83,12 +83,12 @@ func (cm *ChannelManager) Get(name string) (channel *Channel) {
83 83
 }
84 84
 
85 85
 // Join causes `client` to join the channel named `name`, creating it if necessary.
86
-func (cm *ChannelManager) Join(client *Client, name string, key string, isSajoin bool, rb *ResponseBuffer) error {
86
+func (cm *ChannelManager) Join(client *Client, name string, key string, isSajoin bool, rb *ResponseBuffer) (err error, forward string) {
87 87
 	server := client.server
88 88
 	casefoldedName, err := CasefoldChannel(name)
89 89
 	skeleton, skerr := Skeleton(name)
90 90
 	if err != nil || skerr != nil || len(casefoldedName) > server.Config().Limits.ChannelLen {
91
-		return errNoSuchChannel
91
+		return errNoSuchChannel, ""
92 92
 	}
93 93
 
94 94
 	channel, err := func() (*Channel, error) {
@@ -128,15 +128,15 @@ func (cm *ChannelManager) Join(client *Client, name string, key string, isSajoin
128 128
 	}()
129 129
 
130 130
 	if err != nil {
131
-		return err
131
+		return err, ""
132 132
 	}
133 133
 
134 134
 	channel.EnsureLoaded()
135
-	err = channel.Join(client, key, isSajoin, rb)
135
+	err, forward = channel.Join(client, key, isSajoin, rb)
136 136
 
137 137
 	cm.maybeCleanup(channel, true)
138 138
 
139
-	return err
139
+	return
140 140
 }
141 141
 
142 142
 func (cm *ChannelManager) maybeCleanup(channel *Channel, afterJoin bool) {

+ 7
- 0
irc/channelreg.go 파일 보기

@@ -35,6 +35,7 @@ const (
35 35
 	keyChannelAccountToUMode = "channel.accounttoumode %s"
36 36
 	keyChannelUserLimit      = "channel.userlimit %s"
37 37
 	keyChannelSettings       = "channel.settings %s"
38
+	keyChannelForward        = "channel.forward %s"
38 39
 
39 40
 	keyChannelPurged = "channel.purged %s"
40 41
 )
@@ -56,6 +57,7 @@ var (
56 57
 		keyChannelAccountToUMode,
57 58
 		keyChannelUserLimit,
58 59
 		keyChannelSettings,
60
+		keyChannelForward,
59 61
 	}
60 62
 )
61 63
 
@@ -94,6 +96,8 @@ type RegisteredChannel struct {
94 96
 	Modes []modes.Mode
95 97
 	// Key represents the channel key / password
96 98
 	Key string
99
+	// Forward is the forwarding/overflow (+f) channel
100
+	Forward string
97 101
 	// UserLimit is the user limit (0 for no limit)
98 102
 	UserLimit int
99 103
 	// AccountToUMode maps user accounts to their persistent channel modes (e.g., +q, +h)
@@ -208,6 +212,7 @@ func (reg *ChannelRegistry) LoadChannel(nameCasefolded string) (info RegisteredC
208 212
 		password, _ := tx.Get(fmt.Sprintf(keyChannelPassword, channelKey))
209 213
 		modeString, _ := tx.Get(fmt.Sprintf(keyChannelModes, channelKey))
210 214
 		userLimitString, _ := tx.Get(fmt.Sprintf(keyChannelUserLimit, channelKey))
215
+		forward, _ := tx.Get(fmt.Sprintf(keyChannelForward, channelKey))
211 216
 		banlistString, _ := tx.Get(fmt.Sprintf(keyChannelBanlist, channelKey))
212 217
 		exceptlistString, _ := tx.Get(fmt.Sprintf(keyChannelExceptlist, channelKey))
213 218
 		invitelistString, _ := tx.Get(fmt.Sprintf(keyChannelInvitelist, channelKey))
@@ -249,6 +254,7 @@ func (reg *ChannelRegistry) LoadChannel(nameCasefolded string) (info RegisteredC
249 254
 			AccountToUMode: accountToUMode,
250 255
 			UserLimit:      int(userLimit),
251 256
 			Settings:       settings,
257
+			Forward:        forward,
252 258
 		}
253 259
 		return nil
254 260
 	})
@@ -361,6 +367,7 @@ func (reg *ChannelRegistry) saveChannel(tx *buntdb.Tx, channelInfo RegisteredCha
361 367
 		modeString := modes.Modes(channelInfo.Modes).String()
362 368
 		tx.Set(fmt.Sprintf(keyChannelModes, channelKey), modeString, nil)
363 369
 		tx.Set(fmt.Sprintf(keyChannelUserLimit, channelKey), strconv.Itoa(channelInfo.UserLimit), nil)
370
+		tx.Set(fmt.Sprintf(keyChannelForward, channelKey), channelInfo.Forward, nil)
364 371
 	}
365 372
 
366 373
 	if includeFlags&IncludeLists != 0 {

+ 6
- 0
irc/getters.go 파일 보기

@@ -517,3 +517,9 @@ func (channel *Channel) SetSettings(settings ChannelSettings) {
517 517
 	channel.stateMutex.Unlock()
518 518
 	channel.MarkDirty(IncludeSettings)
519 519
 }
520
+
521
+func (channel *Channel) setForward(forward string) {
522
+	channel.stateMutex.Lock()
523
+	channel.forward = forward
524
+	channel.stateMutex.Unlock()
525
+}

+ 10
- 3
irc/handlers.go 파일 보기

@@ -1192,9 +1192,16 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1192 1192
 		if len(keys) > i {
1193 1193
 			key = keys[i]
1194 1194
 		}
1195
-		err := server.channels.Join(client, name, key, false, rb)
1195
+		err, forward := server.channels.Join(client, name, key, false, rb)
1196 1196
 		if err != nil {
1197
-			sendJoinError(client, name, rb, err)
1197
+			if forward != "" {
1198
+				rb.Add(nil, server.name, ERR_LINKCHANNEL, client.Nick(), utils.SafeErrorParam(name), forward, client.t("Forwarding to another channel"))
1199
+				name = forward
1200
+				err, _ = server.channels.Join(client, name, key, false, rb)
1201
+			}
1202
+			if err != nil {
1203
+				sendJoinError(client, name, rb, err)
1204
+			}
1198 1205
 		}
1199 1206
 	}
1200 1207
 	return false
@@ -1255,7 +1262,7 @@ func sajoinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
1255 1262
 
1256 1263
 	channels := strings.Split(channelString, ",")
1257 1264
 	for _, chname := range channels {
1258
-		err := server.channels.Join(target, chname, "", true, rb)
1265
+		err, _ := server.channels.Join(target, chname, "", true, rb)
1259 1266
 		if err != nil {
1260 1267
 			sendJoinError(client, chname, rb, err)
1261 1268
 		}

+ 6
- 0
irc/import.go 파일 보기

@@ -44,6 +44,7 @@ type channelImport struct {
44 44
 	Modes        string
45 45
 	Key          string
46 46
 	Limit        int
47
+	Forward      string
47 48
 }
48 49
 
49 50
 type databaseImport struct {
@@ -187,6 +188,11 @@ func doImportDBGeneric(config *Config, dbImport databaseImport, credsType Creden
187 188
 		if chInfo.Limit > 0 {
188 189
 			tx.Set(fmt.Sprintf(keyChannelUserLimit, cfchname), strconv.Itoa(chInfo.Limit), nil)
189 190
 		}
191
+		if chInfo.Forward != "" {
192
+			if _, err := CasefoldChannel(chInfo.Forward); err == nil {
193
+				tx.Set(fmt.Sprintf(keyChannelForward, cfchname), chInfo.Forward, nil)
194
+			}
195
+		}
190 196
 	}
191 197
 
192 198
 	if warnSkeletons {

+ 24
- 1
irc/modes.go 파일 보기

@@ -12,6 +12,7 @@ import (
12 12
 
13 13
 	"github.com/oragono/oragono/irc/modes"
14 14
 	"github.com/oragono/oragono/irc/sno"
15
+	"github.com/oragono/oragono/irc/utils"
15 16
 )
16 17
 
17 18
 var (
@@ -254,6 +255,28 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
254 255
 				applied = append(applied, change)
255 256
 			}
256 257
 
258
+		case modes.Forward:
259
+			switch change.Op {
260
+			case modes.Add:
261
+				ch := client.server.channels.Get(change.Arg)
262
+				if ch == nil {
263
+					rb.Add(nil, client.server.name, ERR_INVALIDMODEPARAM, details.nick, utils.SafeErrorParam(change.Arg), fmt.Sprintf(client.t("No such channel")))
264
+				} else if ch == channel {
265
+					rb.Add(nil, client.server.name, ERR_INVALIDMODEPARAM, details.nick, utils.SafeErrorParam(change.Arg), fmt.Sprintf(client.t("You can't forward a channel to itself")))
266
+				} else {
267
+					if !ch.ClientIsAtLeast(client, modes.ChannelOperator) {
268
+						rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, details.nick, ch.Name(), client.t("You must be a channel operator in the channel you are forwarding to"))
269
+					} else {
270
+						change.Arg = ch.Name()
271
+						channel.setForward(change.Arg)
272
+						applied = append(applied, change)
273
+					}
274
+				}
275
+			case modes.Remove:
276
+				channel.setForward("")
277
+				applied = append(applied, change)
278
+			}
279
+
257 280
 		case modes.Key:
258 281
 			switch change.Op {
259 282
 			case modes.Add:
@@ -302,7 +325,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
302 325
 		case modes.BanMask, modes.ExceptMask, modes.InviteMask:
303 326
 			includeFlags |= IncludeLists
304 327
 		case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
305
-			// these are never persisted currently, but might be in the future (see discussion on #729)
328
+			// these are persisted on the client object, via (*Channel).applyModeToMember
306 329
 		default:
307 330
 			includeFlags |= IncludeModes
308 331
 		}

+ 5
- 4
irc/modes/modes.go 파일 보기

@@ -24,7 +24,7 @@ var (
24 24
 	SupportedChannelModes = Modes{
25 25
 		BanMask, ChanRoleplaying, ExceptMask, InviteMask, InviteOnly, Key,
26 26
 		Moderated, NoOutside, OpOnlyTopic, RegisteredOnly, RegisteredOnlySpeak,
27
-		Secret, UserLimit, NoCTCP, Auditorium, OpModerated,
27
+		Secret, UserLimit, NoCTCP, Auditorium, OpModerated, Forward,
28 28
 	}
29 29
 )
30 30
 
@@ -130,6 +130,7 @@ const (
130 130
 	UserLimit           Mode = 'l' // flag arg
131 131
 	NoCTCP              Mode = 'C' // flag
132 132
 	OpModerated         Mode = 'U' // flag
133
+	Forward             Mode = 'f' // flag arg
133 134
 )
134 135
 
135 136
 var (
@@ -277,7 +278,7 @@ func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
277 278
 				} else {
278 279
 					continue
279 280
 				}
280
-			case UserLimit:
281
+			case UserLimit, Forward:
281 282
 				// don't require value when removing
282 283
 				if change.Op == Add {
283 284
 					if len(params) > skipArgs {
@@ -445,7 +446,7 @@ func RplMyInfo() (param1, param2, param3 string) {
445 446
 	sort.Sort(ByCodepoint(channelModes))
446 447
 
447 448
 	// XXX enumerate these by hand, i can't see any way to DRY this
448
-	channelParametrizedModes := Modes{BanMask, ExceptMask, InviteMask, Key, UserLimit}
449
+	channelParametrizedModes := Modes{BanMask, ExceptMask, InviteMask, Key, UserLimit, Forward}
449 450
 	channelParametrizedModes = append(channelParametrizedModes, ChannelUserModes...)
450 451
 	sort.Sort(ByCodepoint(channelParametrizedModes))
451 452
 
@@ -459,7 +460,7 @@ func ChanmodesToken() (result string) {
459 460
 	// type B: modes with parameters
460 461
 	B := Modes{Key}
461 462
 	// type C: modes that take a parameter only when set, never when unset
462
-	C := Modes{UserLimit}
463
+	C := Modes{UserLimit, Forward}
463 464
 	// type D: modes without parameters
464 465
 	D := Modes{InviteOnly, Moderated, NoOutside, OpOnlyTopic, ChanRoleplaying, Secret, NoCTCP, RegisteredOnly, RegisteredOnlySpeak, Auditorium, OpModerated}
465 466
 

+ 1
- 0
irc/numerics.go 파일 보기

@@ -149,6 +149,7 @@ const (
149 149
 	ERR_YOUWILLBEBANNED           = "466"
150 150
 	ERR_KEYSET                    = "467"
151 151
 	ERR_INVALIDUSERNAME           = "468"
152
+	ERR_LINKCHANNEL               = "470"
152 153
 	ERR_CHANNELISFULL             = "471"
153 154
 	ERR_UNKNOWNMODE               = "472"
154 155
 	ERR_INVITEONLYCHAN            = "473"

Loading…
취소
저장