Browse Source

Only stage some metadata changes for now, not the vendor update

metadata
Daniel Oaks 2 years ago
parent
commit
060d06ba6a
8 changed files with 263 additions and 10 deletions
  1. 44
    0
      default.yaml
  2. 15
    10
      irc/client.go
  3. 4
    0
      irc/commands.go
  4. 54
    0
      irc/config.go
  5. 90
    0
      irc/handlers.go
  6. 8
    0
      irc/help.go
  7. 4
    0
      irc/metadata.go
  8. 44
    0
      traditional.yaml

+ 44
- 0
default.yaml View File

@@ -364,6 +364,50 @@ server:
364 364
     # the default value of 512. DO NOT change this on a public server:
365 365
     # max-line-len: 512
366 366
 
367
+# metadata options
368
+metadata:
369
+    # maximum number of keys users can have set.
370
+    # this excludes admin-set keys which users cannot set or view
371
+    max-keys: 16
372
+
373
+    # maximum number of keys users can subscribe to at one time.
374
+    # opers can subscribe to more than this though
375
+    max-subs: 16
376
+
377
+    users:
378
+        # uncomment this if you ONLY want these keys to be settable
379
+        # allowed-keys:
380
+        #     - avatar
381
+        #     - color
382
+        #     - display-name
383
+        #     - homepage
384
+        #     - status
385
+
386
+        # uncomment this if you want all BUT these keys to be settable
387
+        # blocked-keys:
388
+        #     - example-bad-key
389
+        #     - another-bad-example
390
+
391
+        # these keys are restricted to opers
392
+        restricted-keys:
393
+            - admin:*
394
+            - server:*
395
+
396
+    channels:
397
+        # uncomment this if you ONLY want these keys to be settable
398
+        # allowed-keys:
399
+        #     - url
400
+
401
+        # uncomment this if you want all BUT these keys to be settable
402
+        # blocked-keys:
403
+        #     - example-bad-key
404
+        #     - another-bad-example
405
+
406
+        # these keys are restricted to opers
407
+        restricted-keys:
408
+            - admin:*
409
+            - server:*
410
+
367 411
 # account options
368 412
 accounts:
369 413
     # is account authentication enabled, i.e., can users log into existing accounts?

+ 15
- 10
irc/client.go View File

@@ -20,6 +20,7 @@ import (
20 20
 	"github.com/ergochat/irc-go/ircfmt"
21 21
 	"github.com/ergochat/irc-go/ircmsg"
22 22
 	"github.com/ergochat/irc-go/ircreader"
23
+	"github.com/soroushj/menge"
23 24
 	"github.com/xdg-go/scram"
24 25
 
25 26
 	"github.com/ergochat/ergo/irc/caps"
@@ -174,6 +175,9 @@ type Session struct {
174 175
 	capState     caps.State
175 176
 	capVersion   caps.Version
176 177
 
178
+	stateMutex             sync.RWMutex // tier 1
179
+	subscribedMetadataKeys menge.StringSet
180
+
177 181
 	registrationMessages int
178 182
 
179 183
 	zncPlaybackTimes      *zncPlaybackTimes
@@ -349,16 +353,17 @@ func (server *Server) RunClient(conn IRCConn) {
349 353
 	}
350 354
 	client.history.Initialize(config.History.ClientLength, time.Duration(config.History.AutoresizeWindow))
351 355
 	session := &Session{
352
-		client:     client,
353
-		socket:     socket,
354
-		capVersion: caps.Cap301,
355
-		capState:   caps.NoneState,
356
-		ctime:      now,
357
-		lastActive: now,
358
-		realIP:     realIP,
359
-		proxiedIP:  proxiedIP,
360
-		isTor:      wConn.Config.Tor,
361
-		hideSTS:    wConn.Config.Tor || wConn.Config.HideSTS,
356
+		client:                 client,
357
+		socket:                 socket,
358
+		capVersion:             caps.Cap301,
359
+		capState:               caps.NoneState,
360
+		ctime:                  now,
361
+		lastActive:             now,
362
+		realIP:                 realIP,
363
+		proxiedIP:              proxiedIP,
364
+		isTor:                  wConn.Config.Tor,
365
+		hideSTS:                wConn.Config.Tor || wConn.Config.HideSTS,
366
+		subscribedMetadataKeys: menge.NewStringSet(),
362 367
 	}
363 368
 	client.sessions = []*Session{session}
364 369
 

+ 4
- 0
irc/commands.go View File

@@ -178,6 +178,10 @@ func init() {
178 178
 			handler:   lusersHandler,
179 179
 			minParams: 0,
180 180
 		},
181
+		"METADATA": {
182
+			handler:   metadataHandler,
183
+			minParams: 2,
184
+		},
181 185
 		"MODE": {
182 186
 			handler:   modeHandler,
183 187
 			minParams: 1,

+ 54
- 0
irc/config.go View File

@@ -300,6 +300,38 @@ func (t *ThrottleConfig) UnmarshalYAML(unmarshal func(interface{}) error) (err e
300 300
 	return
301 301
 }
302 302
 
303
+type MetadataKeyConfig struct {
304
+	AllowedKeys           []string `yaml:"allowed-keys"`
305
+	AllowedKeysMatcher    *regexp.Regexp
306
+	BlockedKeys           []string `yaml:"blocked-keys"`
307
+	BlockedKeysMatcher    *regexp.Regexp
308
+	RestrictedKeys        []string `yaml:"restricted-keys"`
309
+	RestrictedKeysMatcher *regexp.Regexp
310
+}
311
+
312
+func (mkc *MetadataKeyConfig) compileMatchers() (err error) {
313
+	mkc.AllowedKeysMatcher, err = utils.CompileMasks(mkc.AllowedKeys)
314
+	if err != nil {
315
+		err = errors.New("allowed-keys")
316
+	}
317
+	mkc.BlockedKeysMatcher, err = utils.CompileMasks(mkc.BlockedKeys)
318
+	if err != nil {
319
+		err = errors.New("blocked-keys")
320
+	}
321
+	mkc.RestrictedKeysMatcher, err = utils.CompileMasks(mkc.RestrictedKeys)
322
+	if err != nil {
323
+		err = errors.New("restricted-keys")
324
+	}
325
+	return err
326
+}
327
+
328
+type MetadataConfig struct {
329
+	MaxKeys  int `yaml:"max-keys"`
330
+	MaxSubs  int `yaml:"max-subs"`
331
+	Users    MetadataKeyConfig
332
+	Channels MetadataKeyConfig
333
+}
334
+
303 335
 type AccountConfig struct {
304 336
 	Registration          AccountRegistrationConfig
305 337
 	AuthenticationEnabled bool `yaml:"authentication-enabled"`
@@ -621,6 +653,8 @@ type Config struct {
621 653
 		MySQL       mysql.Config
622 654
 	}
623 655
 
656
+	Metadata MetadataConfig
657
+
624 658
 	Accounts AccountConfig
625 659
 
626 660
 	Channels struct {
@@ -1228,6 +1262,26 @@ func LoadConfig(filename string) (config *Config, err error) {
1228 1262
 		config.Server.capValues[caps.Multiline] = multilineCapValue
1229 1263
 	}
1230 1264
 
1265
+	// confirm that we don't have both allowed and blocked metadata keys set
1266
+	config.Server.capValues[caps.Metadata] = fmt.Sprintf("maxsub=%d,maxkey=%d", config.Metadata.MaxSubs, config.Metadata.MaxKeys)
1267
+
1268
+	if len(config.Metadata.Users.AllowedKeys) > 0 && len(config.Metadata.Users.BlockedKeys) > 0 {
1269
+		return nil, errors.New("You can only set either allowed-keys or blocked-keys in metadata.users, not both")
1270
+	}
1271
+	if len(config.Metadata.Channels.AllowedKeys) > 0 && len(config.Metadata.Channels.BlockedKeys) > 0 {
1272
+		return nil, errors.New("You can only set either allowed-keys or blocked-keys in metadata.channels, not both")
1273
+	}
1274
+
1275
+	err = config.Metadata.Users.compileMatchers()
1276
+	if err != nil {
1277
+		return nil, fmt.Errorf("Could not compile metadata.users.%s", err.Error())
1278
+	}
1279
+
1280
+	err = config.Metadata.Channels.compileMatchers()
1281
+	if err != nil {
1282
+		return nil, fmt.Errorf("Could not compile metadata.channels.%s", err.Error())
1283
+	}
1284
+
1231 1285
 	// handle legacy name 'bouncer' for 'multiclient' section:
1232 1286
 	if config.Accounts.Bouncer != nil {
1233 1287
 		config.Accounts.Multiclient = *config.Accounts.Bouncer

+ 90
- 0
irc/handlers.go View File

@@ -12,6 +12,7 @@ import (
12 12
 	"fmt"
13 13
 	"net"
14 14
 	"os"
15
+	"regexp"
15 16
 	"runtime"
16 17
 	"runtime/debug"
17 18
 	"runtime/pprof"
@@ -1709,6 +1710,95 @@ func lusersHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respo
1709 1710
 	return false
1710 1711
 }
1711 1712
 
1713
+// METADATA <Target> <Subcommand> [<Param 1> ... [<Param n>]]
1714
+func metadataHandler(server *Server, client *Client, msg ircmsg.Message, rb *ResponseBuffer) bool {
1715
+	// target := msg.Params[0]
1716
+	subCommand := strings.ToUpper(msg.Params[1])
1717
+
1718
+	config := server.Config().Metadata
1719
+
1720
+	//TODO: do this once when the server boots?
1721
+	keyNameMatcher, _ := regexp.Compile(`^[a-z0-9_.-][a-z0-9_.\-:]*$`)
1722
+
1723
+	switch subCommand {
1724
+	// these subcommands affect the client itself, or other targets
1725
+	//
1726
+
1727
+	// these subcommands affect the current session
1728
+	//
1729
+	case "SUB":
1730
+		var addedKeys []string
1731
+
1732
+		rb.session.stateMutex.Lock()
1733
+		defer rb.session.stateMutex.Unlock()
1734
+		for i, key := range msg.Params {
1735
+			if i < 2 {
1736
+				// skip target and subcommand
1737
+				continue
1738
+			}
1739
+
1740
+			if len(rb.session.subscribedMetadataKeys)+len(addedKeys) > config.MaxSubs {
1741
+				rb.Add(nil, server.name, ERR_METADATATOOMANYSUBS, client.nick, key)
1742
+				break
1743
+			}
1744
+
1745
+			if !keyNameMatcher.MatchString(key) {
1746
+				rb.Add(nil, server.name, ERR_KEYINVALID, client.nick, key)
1747
+				continue
1748
+			}
1749
+
1750
+			// see if key is restricted
1751
+			if client.Oper() == nil && (config.Users.RestrictedKeysMatcher.MatchString(key) || config.Channels.RestrictedKeysMatcher.MatchString(key)) {
1752
+				rb.Add(nil, server.name, ERR_KEYNOPERMISSION, client.nick, "*", key, "permission denied")
1753
+				// still let the user subscribe to the key, don't continue on to the next one
1754
+			}
1755
+
1756
+			addedKeys = append(addedKeys, key)
1757
+		}
1758
+		rb.session.subscribedMetadataKeys.Add(addedKeys...)
1759
+
1760
+		if len(addedKeys) > 0 {
1761
+			rb.Add(nil, server.name, RPL_METADATASUBOK, client.nick, strings.Join(addedKeys, " "))
1762
+		}
1763
+		rb.Add(nil, server.name, RPL_METADATAEND, client.nick, "end of metadata")
1764
+
1765
+	case "UNSUB":
1766
+		var removedKeys []string
1767
+
1768
+		rb.session.stateMutex.Lock()
1769
+		defer rb.session.stateMutex.Unlock()
1770
+		for i, key := range msg.Params {
1771
+			if i < 2 {
1772
+				// skip target and subcommand
1773
+				continue
1774
+			}
1775
+
1776
+			if !keyNameMatcher.MatchString(key) {
1777
+				rb.Add(nil, server.name, ERR_KEYINVALID, client.nick, key)
1778
+				continue
1779
+			}
1780
+
1781
+			removedKeys = append(removedKeys, key)
1782
+		}
1783
+		rb.session.subscribedMetadataKeys.Remove(removedKeys...)
1784
+
1785
+		if len(removedKeys) > 0 {
1786
+			rb.Add(nil, server.name, RPL_METADATAUNSUBOK, client.nick, strings.Join(removedKeys, " "))
1787
+		}
1788
+		rb.Add(nil, server.name, RPL_METADATAEND, client.nick, "end of metadata")
1789
+
1790
+	case "SUBS":
1791
+		rb.session.stateMutex.RLock()
1792
+		defer rb.session.stateMutex.RUnlock()
1793
+		if rb.session.subscribedMetadataKeys.Size() > 0 {
1794
+			//TODO: loop and return subscriptions with multiple numerics if we need to
1795
+			rb.Add(nil, server.name, RPL_METADATASUBS, client.nick, strings.Join(rb.session.subscribedMetadataKeys.AsSlice(), " "))
1796
+		}
1797
+		rb.Add(nil, server.name, RPL_METADATAEND, client.nick, "end of metadata")
1798
+	}
1799
+	return false
1800
+}
1801
+
1712 1802
 // MODE <target> [<modestring> [<mode arguments>...]]
1713 1803
 func modeHandler(server *Server, client *Client, msg ircmsg.Message, rb *ResponseBuffer) bool {
1714 1804
 	if 0 < len(msg.Params[0]) && msg.Params[0][0] == '#' {

+ 8
- 0
irc/help.go View File

@@ -347,6 +347,14 @@ Lists all the nicknames you are currently monitoring.
347 347
 
348 348
     MONITOR S
349 349
 Lists whether each nick in your MONITOR list is online or offline.`,
350
+	},
351
+	"metadata": {
352
+		text: `METADATA <target> <subcmd>
353
+
354
+Lets you add metadata to yourself and channels, as well as subscribe
355
+to metadata changes. The subcommands are:
356
+
357
+//TODO`,
350 358
 	},
351 359
 	"motd": {
352 360
 		text: `MOTD [server]

+ 4
- 0
irc/metadata.go View File

@@ -0,0 +1,4 @@
1
+// Copyright (c) 2021 Daniel Oaks <daniel@danieloaks.net>
2
+// released under the MIT license
3
+
4
+package irc

+ 44
- 0
traditional.yaml View File

@@ -337,6 +337,50 @@ server:
337 337
     # the default value of 512. DO NOT change this on a public server:
338 338
     # max-line-len: 512
339 339
 
340
+# metadata options
341
+metadata:
342
+    # maximum number of keys users can have set.
343
+    # this excludes admin-set keys which users cannot set or view
344
+    max-keys: 16
345
+
346
+    # maximum number of keys users can subscribe to at one time.
347
+    # opers can subscribe to more than this though
348
+    max-subs: 16
349
+
350
+    users:
351
+        # uncomment this if you ONLY want these keys to be settable
352
+        # allowed-keys:
353
+        #     - avatar
354
+        #     - color
355
+        #     - display-name
356
+        #     - homepage
357
+        #     - status
358
+
359
+        # uncomment this if you want all BUT these keys to be settable
360
+        # blocked-keys:
361
+        #     - example-bad-key
362
+        #     - another-bad-example
363
+
364
+        # these keys are restricted to opers
365
+        restricted-keys:
366
+            - admin:*
367
+            - server:*
368
+
369
+    channels:
370
+        # uncomment this if you ONLY want these keys to be settable
371
+        # allowed-keys:
372
+        #     - url
373
+
374
+        # uncomment this if you want all BUT these keys to be settable
375
+        # blocked-keys:
376
+        #     - example-bad-key
377
+        #     - another-bad-example
378
+
379
+        # these keys are restricted to opers
380
+        restricted-keys:
381
+            - admin:*
382
+            - server:*
383
+
340 384
 # account options
341 385
 accounts:
342 386
     # is account authentication enabled, i.e., can users log into existing accounts?

Loading…
Cancel
Save