Browse Source

Add REHASH command

tags/v0.3.0
Daniel Oaks 7 years ago
parent
commit
6cd71e1b9e
5 changed files with 158 additions and 24 deletions
  1. 1
    0
      CHANGELOG.md
  2. 5
    0
      irc/commands.go
  3. 82
    11
      irc/isupport.go
  4. 69
    12
      irc/server.go
  5. 1
    1
      oragono.go

+ 1
- 0
CHANGELOG.md View File

@@ -10,6 +10,7 @@ New release of Oragono!
10 10
 ### Security
11 11
 
12 12
 ### Added
13
+* Added `REHASH` command.
13 14
 
14 15
 ### Changed
15 16
 

+ 5
- 0
irc/commands.go View File

@@ -164,6 +164,11 @@ var Commands = map[string]Command{
164 164
 		handler:   regHandler,
165 165
 		minParams: 3,
166 166
 	},
167
+	"REHASH": {
168
+		handler:   rehashHandler,
169
+		minParams: 0,
170
+		oper:      true,
171
+	},
167 172
 	"TIME": {
168 173
 		handler:   timeHandler,
169 174
 		minParams: 0,

+ 82
- 11
irc/isupport.go View File

@@ -5,6 +5,9 @@ package irc
5 5
 
6 6
 import "fmt"
7 7
 
8
+const isupportSupportedString = "are supported by this server"
9
+const maxISupportLength = 400 // Max length of a single ISUPPORT token line
10
+
8 11
 // ISupportList holds a list of ISUPPORT tokens
9 12
 type ISupportList struct {
10 13
 	Tokens      map[string]*string
@@ -29,22 +32,90 @@ func (il *ISupportList) AddNoValue(name string) {
29 32
 	il.Tokens[name] = nil
30 33
 }
31 34
 
35
+// getTokenString gets the appropriate string for a token+value.
36
+func getTokenString(name string, value *string) string {
37
+	if value == nil {
38
+		return name
39
+	}
40
+	return fmt.Sprintf("%s=%s", name, *value)
41
+}
42
+
43
+// GetDifference returns the difference between two token lists.
44
+func (il *ISupportList) GetDifference(newil *ISupportList) [][]string {
45
+	replies := make([][]string, 0)
46
+	var length int     // Length of the current cache
47
+	var cache []string // Token list cache
48
+
49
+	// append removed tokens
50
+	for name := range il.Tokens {
51
+		_, exists := newil.Tokens[name]
52
+		if exists {
53
+			continue
54
+		}
55
+
56
+		token := fmt.Sprintf("-%s", name)
57
+
58
+		if len(token)+length <= maxISupportLength {
59
+			// account for the space separating tokens
60
+			if len(cache) > 0 {
61
+				length++
62
+			}
63
+			cache = append(cache, token)
64
+			length += len(token)
65
+		}
66
+
67
+		if len(cache) == 13 || len(token)+length >= maxISupportLength {
68
+			cache = append(cache, isupportSupportedString)
69
+			replies = append(replies, cache)
70
+			cache = make([]string, 0)
71
+			length = 0
72
+		}
73
+	}
74
+
75
+	// append added tokens
76
+	for name, value := range newil.Tokens {
77
+		newval, exists := il.Tokens[name]
78
+		if exists && *value == *newval {
79
+			continue
80
+		}
81
+
82
+		token := getTokenString(name, value)
83
+
84
+		if len(token)+length <= maxISupportLength {
85
+			// account for the space separating tokens
86
+			if len(cache) > 0 {
87
+				length++
88
+			}
89
+			cache = append(cache, token)
90
+			length += len(token)
91
+		}
92
+
93
+		if len(cache) == 13 || len(token)+length >= maxISupportLength {
94
+			cache = append(cache, isupportSupportedString)
95
+			replies = append(replies, cache)
96
+			cache = make([]string, 0)
97
+			length = 0
98
+		}
99
+	}
100
+
101
+	if len(cache) > 0 {
102
+		cache = append(cache, isupportSupportedString)
103
+		replies = append(replies, cache)
104
+	}
105
+
106
+	return replies
107
+}
108
+
32 109
 // RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply
33 110
 func (il *ISupportList) RegenerateCachedReply() {
34 111
 	il.CachedReply = make([][]string, 0)
35
-	maxlen := 400      // Max length of a single ISUPPORT token line
36 112
 	var length int     // Length of the current cache
37 113
 	var cache []string // Token list cache
38 114
 
39 115
 	for name, value := range il.Tokens {
40
-		var token string
41
-		if value == nil {
42
-			token = name
43
-		} else {
44
-			token = fmt.Sprintf("%s=%s", name, *value)
45
-		}
116
+		token := getTokenString(name, value)
46 117
 
47
-		if len(token)+length <= maxlen {
118
+		if len(token)+length <= maxISupportLength {
48 119
 			// account for the space separating tokens
49 120
 			if len(cache) > 0 {
50 121
 				length++
@@ -53,8 +124,8 @@ func (il *ISupportList) RegenerateCachedReply() {
53 124
 			length += len(token)
54 125
 		}
55 126
 
56
-		if len(cache) == 13 || len(token)+length >= maxlen {
57
-			cache = append(cache, "are supported by this server")
127
+		if len(cache) == 13 || len(token)+length >= maxISupportLength {
128
+			cache = append(cache, isupportSupportedString)
58 129
 			il.CachedReply = append(il.CachedReply, cache)
59 130
 			cache = make([]string, 0)
60 131
 			length = 0
@@ -62,7 +133,7 @@ func (il *ISupportList) RegenerateCachedReply() {
62 133
 	}
63 134
 
64 135
 	if len(cache) > 0 {
65
-		cache = append(cache, "are supported by this server")
136
+		cache = append(cache, isupportSupportedString)
66 137
 		il.CachedReply = append(il.CachedReply, cache)
67 138
 	}
68 139
 }

+ 69
- 12
irc/server.go View File

@@ -39,6 +39,7 @@ type Server struct {
39 39
 	channels            ChannelNameMap
40 40
 	clients             *ClientLookupSet
41 41
 	commands            chan Command
42
+	configFilename      string
42 43
 	ctime               time.Time
43 44
 	store               buntdb.DB
44 45
 	idle                chan *Client
@@ -47,6 +48,7 @@ type Server struct {
47 48
 	motdLines           []string
48 49
 	name                string
49 50
 	nameCasefolded      string
51
+	networkName         string
50 52
 	newConns            chan clientConn
51 53
 	operators           map[string][]byte
52 54
 	password            []byte
@@ -72,7 +74,8 @@ type clientConn struct {
72 74
 	IsTLS bool
73 75
 }
74 76
 
75
-func NewServer(config *Config) *Server {
77
+// NewServer returns a new Oragono server.
78
+func NewServer(configFilename string, config *Config) *Server {
76 79
 	casefoldedName, err := Casefold(config.Server.Name)
77 80
 	if err != nil {
78 81
 		log.Println(fmt.Sprintf("Server name isn't valid: [%s]", config.Server.Name), err.Error())
@@ -80,12 +83,13 @@ func NewServer(config *Config) *Server {
80 83
 	}
81 84
 
82 85
 	server := &Server{
83
-		accounts: make(map[string]*ClientAccount),
84
-		channels: make(ChannelNameMap),
85
-		clients:  NewClientLookupSet(),
86
-		commands: make(chan Command),
87
-		ctime:    time.Now(),
88
-		idle:     make(chan *Client),
86
+		accounts:       make(map[string]*ClientAccount),
87
+		channels:       make(ChannelNameMap),
88
+		clients:        NewClientLookupSet(),
89
+		commands:       make(chan Command),
90
+		configFilename: configFilename,
91
+		ctime:          time.Now(),
92
+		idle:           make(chan *Client),
89 93
 		limits: Limits{
90 94
 			AwayLen:        int(config.Limits.AwayLen),
91 95
 			ChannelLen:     int(config.Limits.ChannelLen),
@@ -97,6 +101,7 @@ func NewServer(config *Config) *Server {
97 101
 		monitoring:     make(map[string][]Client),
98 102
 		name:           config.Server.Name,
99 103
 		nameCasefolded: casefoldedName,
104
+		networkName:    config.Network.Name,
100 105
 		newConns:       make(chan clientConn),
101 106
 		operators:      config.Operators(),
102 107
 		signals:        make(chan os.Signal, len(SERVER_SIGNALS)),
@@ -171,6 +176,13 @@ func NewServer(config *Config) *Server {
171 176
 	// Attempt to clean up when receiving these signals.
172 177
 	signal.Notify(server.signals, SERVER_SIGNALS...)
173 178
 
179
+	server.setISupport()
180
+
181
+	return server
182
+}
183
+
184
+// setISupport sets up our RPL_ISUPPORT reply.
185
+func (server *Server) setISupport() {
174 186
 	// add RPL_ISUPPORT tokens
175 187
 	server.isupport = NewISupportList()
176 188
 	server.isupport.Add("AWAYLEN", strconv.Itoa(server.limits.AwayLen))
@@ -184,7 +196,7 @@ func NewServer(config *Config) *Server {
184 196
 	// server.isupport.Add("MAXLIST", "") //TODO(dan): Support max list length?
185 197
 	// server.isupport.Add("MODES", "")   //TODO(dan): Support max modes?
186 198
 	server.isupport.Add("MONITOR", strconv.Itoa(server.limits.MonitorEntries))
187
-	server.isupport.Add("NETWORK", config.Network.Name)
199
+	server.isupport.Add("NETWORK", server.networkName)
188 200
 	server.isupport.Add("NICKLEN", strconv.Itoa(server.limits.NickLen))
189 201
 	server.isupport.Add("PREFIX", "(qaohv)~&@%+")
190 202
 	// server.isupport.Add("STATUSMSG", "@+") //TODO(dan): Support STATUSMSG
@@ -207,8 +219,6 @@ func NewServer(config *Config) *Server {
207 219
 	}
208 220
 
209 221
 	server.isupport.RegenerateCachedReply()
210
-
211
-	return server
212 222
 }
213 223
 
214 224
 func loadChannelList(channel *Channel, list string, maskMode ChannelMode) {
@@ -220,7 +230,7 @@ func loadChannelList(channel *Channel, list string, maskMode ChannelMode) {
220 230
 
221 231
 func (server *Server) Shutdown() {
222 232
 	//TODO(dan): Make sure we disallow new nicks
223
-	for _, client := range server.clients.byNick {
233
+	for _, client := range server.clients.ByNick {
224 234
 		client.Notice("Server is shutting down")
225 235
 	}
226 236
 
@@ -511,13 +521,16 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
511 521
 	for i, name := range channels {
512 522
 		casefoldedName, err := CasefoldChannel(name)
513 523
 		if err != nil {
514
-			log.Println("ISN'T CHANNEL NAME:", name)
515 524
 			client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel")
516 525
 			continue
517 526
 		}
518 527
 
519 528
 		channel := server.channels.Get(casefoldedName)
520 529
 		if channel == nil {
530
+			if len(casefoldedName) > server.limits.ChannelLen {
531
+				client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel")
532
+				continue
533
+			}
521 534
 			channel = NewChannel(server, name, true)
522 535
 		}
523 536
 
@@ -780,6 +793,50 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
780 793
 	return false
781 794
 }
782 795
 
796
+// REHASH
797
+func rehashHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
798
+	config, err := LoadConfig(server.configFilename)
799
+
800
+	if err != nil {
801
+		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "REHASH", fmt.Sprintf("Error rehashing config file: %s", err.Error()))
802
+		return false
803
+	}
804
+
805
+	//TODO(dan): burst CAP DEL for sasl
806
+
807
+	// set server options
808
+	server.limits = Limits{
809
+		AwayLen:        int(config.Limits.AwayLen),
810
+		ChannelLen:     int(config.Limits.ChannelLen),
811
+		KickLen:        int(config.Limits.KickLen),
812
+		MonitorEntries: int(config.Limits.MonitorEntries),
813
+		NickLen:        int(config.Limits.NickLen),
814
+		TopicLen:       int(config.Limits.TopicLen),
815
+	}
816
+	server.operators = config.Operators()
817
+	server.checkIdent = config.Server.CheckIdent
818
+
819
+	// registration
820
+	accountReg := NewAccountRegistration(config.Registration.Accounts)
821
+	server.accountRegistration = &accountReg
822
+
823
+	// set RPL_ISUPPORT
824
+	oldISupportList := server.isupport
825
+	server.setISupport()
826
+	newISupportReplies := oldISupportList.GetDifference(server.isupport)
827
+
828
+	// push new info to all of our clients
829
+	for _, sClient := range server.clients.ByNick {
830
+		for _, tokenline := range newISupportReplies {
831
+			// ugly trickery ahead
832
+			sClient.Send(nil, client.server.name, RPL_ISUPPORT, append([]string{sClient.nick}, tokenline...)...)
833
+		}
834
+	}
835
+
836
+	client.Send(nil, server.name, RPL_REHASHING, client.nick, "ircd.yaml", "Rehashing")
837
+	return false
838
+}
839
+
783 840
 // AWAY [<message>]
784 841
 func awayHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
785 842
 	var isAway bool

+ 1
- 1
oragono.go View File

@@ -83,7 +83,7 @@ Options:
83 83
 		}
84 84
 	} else if arguments["run"].(bool) {
85 85
 		irc.Log.SetLevel(config.Server.Log)
86
-		server := irc.NewServer(config)
86
+		server := irc.NewServer(configfile, config)
87 87
 		if !arguments["--quiet"].(bool) {
88 88
 			log.Println(irc.SemVer, "running")
89 89
 			defer log.Println(irc.SemVer, "exiting")

Loading…
Cancel
Save