Ver código fonte

upgrade message-tags to non-draft version

tags/v1.1.0-rc1
Shivaram Lingamneni 5 anos atrás
pai
commit
85493ef031
19 arquivos alterados com 195 adições e 308 exclusões
  1. 2
    2
      Gopkg.lock
  2. 5
    5
      gencapdefs.py
  3. 6
    6
      irc/caps/defs.go
  4. 37
    94
      irc/channel.go
  5. 55
    77
      irc/client.go
  6. 0
    4
      irc/config.go
  7. 15
    23
      irc/handlers.go
  8. 2
    2
      irc/history/history.go
  9. 10
    16
      irc/idletimer.go
  10. 3
    2
      irc/languages/languages.go
  11. 1
    0
      irc/numerics.go
  12. 26
    24
      irc/responsebuffer.go
  13. 3
    8
      irc/server.go
  14. 7
    5
      irc/socket.go
  15. 0
    3
      irc/types.go
  16. 0
    27
      irc/utils/message_tags.go
  17. 20
    5
      irc/utils/text.go
  18. 2
    4
      oragono.yaml
  19. 1
    1
      vendor

+ 2
- 2
Gopkg.lock Ver arquivo

@@ -27,7 +27,7 @@
27 27
 
28 28
 [[projects]]
29 29
   branch = "master"
30
-  digest = "1:6bcd7bcd5e14cc9552fbf83b2f77f24935c0c502d009c9825b6c212c3f8eb967"
30
+  digest = "1:e6ed6eaa63211bb90847d8c5f11d7412e56c96b5befb7402ee7a7a8ad02700ec"
31 31
   name = "github.com/goshuirc/irc-go"
32 32
   packages = [
33 33
     "ircfmt",
@@ -35,7 +35,7 @@
35 35
     "ircmsg",
36 36
   ]
37 37
   pruneopts = "UT"
38
-  revision = "cf199aea7186fd960d0ed5abbf579bb0f9d890d1"
38
+  revision = "ca74bf6a176d2d1dce6f28f99901a2d48d8da2bd"
39 39
 
40 40
 [[projects]]
41 41
   digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"

+ 5
- 5
gencapdefs.py Ver arquivo

@@ -83,15 +83,15 @@ CAPDEFS = [
83 83
     ),
84 84
     CapDef(
85 85
         identifier="MaxLine",
86
-        name="oragono.io/maxline",
87
-        url="https://oragono.io/maxline",
86
+        name="oragono.io/maxline-2",
87
+        url="https://oragono.io/maxline-2",
88 88
         standard="Oragono-specific",
89 89
     ),
90 90
     CapDef(
91 91
         identifier="MessageTags",
92
-        name="draft/message-tags-0.2",
93
-        url="https://ircv3.net/specs/core/message-tags-3.3.html",
94
-        standard="draft IRCv3",
92
+        name="message-tags",
93
+        url="https://ircv3.net/specs/extensions/message-tags.html",
94
+        standard="IRCv3",
95 95
     ),
96 96
     CapDef(
97 97
         identifier="MultiPrefix",

+ 6
- 6
irc/caps/defs.go Ver arquivo

@@ -57,12 +57,12 @@ const (
57 57
 	// https://gist.github.com/DanielOaks/8126122f74b26012a3de37db80e4e0c6
58 58
 	Languages Capability = iota
59 59
 
60
-	// MaxLine is the Oragono-specific capability named "oragono.io/maxline":
61
-	// https://oragono.io/maxline
60
+	// MaxLine is the Oragono-specific capability named "oragono.io/maxline-2":
61
+	// https://oragono.io/maxline-2
62 62
 	MaxLine Capability = iota
63 63
 
64
-	// MessageTags is the draft IRCv3 capability named "draft/message-tags-0.2":
65
-	// https://ircv3.net/specs/core/message-tags-3.3.html
64
+	// MessageTags is the IRCv3 capability named "message-tags":
65
+	// https://ircv3.net/specs/extensions/message-tags.html
66 66
 	MessageTags Capability = iota
67 67
 
68 68
 	// MultiPrefix is the IRCv3 capability named "multi-prefix":
@@ -112,8 +112,8 @@ var (
112 112
 		"invite-notify",
113 113
 		"draft/labeled-response",
114 114
 		"draft/languages",
115
-		"oragono.io/maxline",
116
-		"draft/message-tags-0.2",
115
+		"oragono.io/maxline-2",
116
+		"message-tags",
117 117
 		"multi-prefix",
118 118
 		"draft/rename",
119 119
 		"draft/resume-0.3",

+ 37
- 94
irc/channel.go Ver arquivo

@@ -14,7 +14,6 @@ import (
14 14
 
15 15
 	"sync"
16 16
 
17
-	"github.com/goshuirc/irc-go/ircmsg"
18 17
 	"github.com/oragono/oragono/irc/caps"
19 18
 	"github.com/oragono/oragono/irc/history"
20 19
 	"github.com/oragono/oragono/irc/modes"
@@ -425,11 +424,13 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
425 424
 
426 425
 		channel.regenerateMembersCache()
427 426
 
427
+		message := utils.SplitMessage{}
428
+		message.Msgid = details.realname
428 429
 		channel.history.Add(history.Item{
429 430
 			Type:        history.Join,
430 431
 			Nick:        details.nickMask,
431 432
 			AccountName: details.accountName,
432
-			Msgid:       details.realname,
433
+			Message:     message,
433 434
 		})
434 435
 
435 436
 		return
@@ -603,16 +604,17 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
603 604
 	serverTime := client.capabilities.Has(caps.ServerTime)
604 605
 
605 606
 	for _, item := range items {
606
-		var tags Tags
607
+		var tags map[string]string
607 608
 		if serverTime {
608
-			tags = ensureTag(tags, "time", item.Time.Format(IRCv3TimestampFormat))
609
+			tags = map[string]string{"time": item.Time.Format(IRCv3TimestampFormat)}
609 610
 		}
610 611
 
612
+		// TODO(#437) support history.Tagmsg
611 613
 		switch item.Type {
612 614
 		case history.Privmsg:
613
-			rb.AddSplitMessageFromClient(item.Msgid, item.Nick, item.AccountName, tags, "PRIVMSG", chname, item.Message)
615
+			rb.AddSplitMessageFromClient(item.Nick, item.AccountName, tags, "PRIVMSG", chname, item.Message)
614 616
 		case history.Notice:
615
-			rb.AddSplitMessageFromClient(item.Msgid, item.Nick, item.AccountName, tags, "NOTICE", chname, item.Message)
617
+			rb.AddSplitMessageFromClient(item.Nick, item.AccountName, tags, "NOTICE", chname, item.Message)
616 618
 		case history.Join:
617 619
 			nick := stripMaskFromNick(item.Nick)
618 620
 			var message string
@@ -624,16 +626,16 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
624 626
 			rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
625 627
 		case history.Part:
626 628
 			nick := stripMaskFromNick(item.Nick)
627
-			message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Original)
629
+			message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message)
628 630
 			rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
629 631
 		case history.Quit:
630 632
 			nick := stripMaskFromNick(item.Nick)
631
-			message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Original)
633
+			message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message)
632 634
 			rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
633 635
 		case history.Kick:
634 636
 			nick := stripMaskFromNick(item.Nick)
635 637
 			// XXX Msgid is the kick target
636
-			message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Msgid, item.Message.Original)
638
+			message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Message.Msgid, item.Message.Message)
637 639
 			rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
638 640
 		}
639 641
 	}
@@ -717,82 +719,20 @@ func (channel *Channel) CanSpeak(client *Client) bool {
717 719
 	return true
718 720
 }
719 721
 
720
-// TagMsg sends a tag message to everyone in this channel who can accept them.
721
-func (channel *Channel) TagMsg(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, rb *ResponseBuffer) {
722
-	channel.sendMessage(msgid, "TAGMSG", []caps.Capability{caps.MessageTags}, minPrefix, clientOnlyTags, client, nil, rb)
723
-}
724
-
725
-// sendMessage sends a given message to everyone on this channel.
726
-func (channel *Channel) sendMessage(msgid, cmd string, requiredCaps []caps.Capability, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message *string, rb *ResponseBuffer) {
727
-	if !channel.CanSpeak(client) {
728
-		rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
722
+func (channel *Channel) SendSplitMessage(command string, minPrefix *modes.Mode, clientOnlyTags map[string]string, client *Client, message utils.SplitMessage, rb *ResponseBuffer) {
723
+	var histType history.ItemType
724
+	switch command {
725
+	case "PRIVMSG":
726
+		histType = history.Privmsg
727
+	case "NOTICE":
728
+		histType = history.Notice
729
+	case "TAGMSG":
730
+		histType = history.Tagmsg
731
+	default:
732
+		channel.server.logger.Error("internal", "unrecognized Channel.SendSplitMessage command", command)
729 733
 		return
730 734
 	}
731 735
 
732
-	// for STATUSMSG
733
-	var minPrefixMode modes.Mode
734
-	if minPrefix != nil {
735
-		minPrefixMode = *minPrefix
736
-	}
737
-	// send echo-message
738
-	if client.capabilities.Has(caps.EchoMessage) {
739
-		var messageTagsToUse *map[string]ircmsg.TagValue
740
-		if client.capabilities.Has(caps.MessageTags) {
741
-			messageTagsToUse = clientOnlyTags
742
-		}
743
-
744
-		nickMaskString := client.NickMaskString()
745
-		accountName := client.AccountName()
746
-		if message == nil {
747
-			rb.AddFromClient(msgid, nickMaskString, accountName, messageTagsToUse, cmd, channel.name)
748
-		} else {
749
-			rb.AddFromClient(msgid, nickMaskString, accountName, messageTagsToUse, cmd, channel.name, *message)
750
-		}
751
-	}
752
-	for _, member := range channel.Members() {
753
-		if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
754
-			// STATUSMSG
755
-			continue
756
-		}
757
-		// echo-message is handled above, so skip sending the msg to the user themselves as well
758
-		if member == client {
759
-			continue
760
-		}
761
-
762
-		canReceive := true
763
-		for _, capName := range requiredCaps {
764
-			if !member.capabilities.Has(capName) {
765
-				canReceive = false
766
-			}
767
-		}
768
-		if !canReceive {
769
-			continue
770
-		}
771
-
772
-		var messageTagsToUse *map[string]ircmsg.TagValue
773
-		if member.capabilities.Has(caps.MessageTags) {
774
-			messageTagsToUse = clientOnlyTags
775
-		}
776
-
777
-		if message == nil {
778
-			member.SendFromClient(msgid, client, messageTagsToUse, cmd, channel.name)
779
-		} else {
780
-			member.SendFromClient(msgid, client, messageTagsToUse, cmd, channel.name, *message)
781
-		}
782
-	}
783
-}
784
-
785
-// SplitPrivMsg sends a private message to everyone in this channel.
786
-func (channel *Channel) SplitPrivMsg(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message utils.SplitMessage, rb *ResponseBuffer) {
787
-	channel.sendSplitMessage(msgid, "PRIVMSG", history.Privmsg, minPrefix, clientOnlyTags, client, &message, rb)
788
-}
789
-
790
-// SplitNotice sends a private message to everyone in this channel.
791
-func (channel *Channel) SplitNotice(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message utils.SplitMessage, rb *ResponseBuffer) {
792
-	channel.sendSplitMessage(msgid, "NOTICE", history.Notice, minPrefix, clientOnlyTags, client, &message, rb)
793
-}
794
-
795
-func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.ItemType, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message *utils.SplitMessage, rb *ResponseBuffer) {
796 736
 	if !channel.CanSpeak(client) {
797 737
 		rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
798 738
 		return
@@ -805,16 +745,16 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.Ite
805 745
 	}
806 746
 	// send echo-message
807 747
 	if client.capabilities.Has(caps.EchoMessage) {
808
-		var tagsToUse *map[string]ircmsg.TagValue
748
+		var tagsToUse map[string]string
809 749
 		if client.capabilities.Has(caps.MessageTags) {
810 750
 			tagsToUse = clientOnlyTags
811 751
 		}
812 752
 		nickMaskString := client.NickMaskString()
813 753
 		accountName := client.AccountName()
814
-		if message == nil {
815
-			rb.AddFromClient(msgid, nickMaskString, accountName, tagsToUse, cmd, channel.name)
754
+		if command == "TAGMSG" && client.capabilities.Has(caps.MessageTags) {
755
+			rb.AddFromClient(message.Msgid, nickMaskString, accountName, tagsToUse, command, channel.name)
816 756
 		} else {
817
-			rb.AddSplitMessageFromClient(msgid, nickMaskString, accountName, tagsToUse, cmd, channel.name, *message)
757
+			rb.AddSplitMessageFromClient(nickMaskString, accountName, tagsToUse, command, channel.name, message)
818 758
 		}
819 759
 	}
820 760
 
@@ -832,22 +772,23 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.Ite
832 772
 		if member == client {
833 773
 			continue
834 774
 		}
835
-		var tagsToUse *map[string]ircmsg.TagValue
775
+		var tagsToUse map[string]string
836 776
 		if member.capabilities.Has(caps.MessageTags) {
837 777
 			tagsToUse = clientOnlyTags
778
+		} else if command == "TAGMSG" {
779
+			continue
838 780
 		}
839 781
 
840
-		if message == nil {
841
-			member.sendFromClientInternal(false, now, msgid, nickmask, account, tagsToUse, cmd, channel.name)
782
+		if command == "TAGMSG" {
783
+			member.sendFromClientInternal(false, now, message.Msgid, nickmask, account, tagsToUse, command, channel.name)
842 784
 		} else {
843
-			member.sendSplitMsgFromClientInternal(false, now, msgid, nickmask, account, tagsToUse, cmd, channel.name, *message)
785
+			member.sendSplitMsgFromClientInternal(false, now, nickmask, account, tagsToUse, command, channel.name, message)
844 786
 		}
845 787
 	}
846 788
 
847 789
 	channel.history.Add(history.Item{
848 790
 		Type:        histType,
849
-		Msgid:       msgid,
850
-		Message:     *message,
791
+		Message:     message,
851 792
 		Nick:        nickmask,
852 793
 		AccountName: account,
853 794
 		Time:        now,
@@ -980,12 +921,14 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb
980 921
 		member.Send(nil, clientMask, "KICK", channel.name, targetNick, comment)
981 922
 	}
982 923
 
924
+	message := utils.SplitMessage{}
925
+	message.Message = comment
926
+	message.Msgid = targetNick // XXX abuse this field
983 927
 	channel.history.Add(history.Item{
984 928
 		Type:        history.Kick,
985 929
 		Nick:        clientMask,
986
-		Message:     utils.MakeSplitMessage(comment, true),
987 930
 		AccountName: target.AccountName(),
988
-		Msgid:       targetNick, // XXX abuse this field
931
+		Message:     message,
989 932
 	})
990 933
 
991 934
 	channel.Quit(target)

+ 55
- 77
irc/client.go Ver arquivo

@@ -62,14 +62,13 @@ type Client struct {
62 62
 	hasQuit            bool
63 63
 	hops               int
64 64
 	hostname           string
65
-	idletimer          *IdleTimer
65
+	idletimer          IdleTimer
66 66
 	invitedTo          map[string]bool
67 67
 	isDestroyed        bool
68 68
 	isTor              bool
69 69
 	isQuitting         bool
70 70
 	languages          []string
71 71
 	loginThrottle      connection_limits.GenericThrottle
72
-	maxlenTags         uint32
73 72
 	maxlenRest         uint32
74 73
 	nick               string
75 74
 	nickCasefolded     string
@@ -122,8 +121,9 @@ type ClientDetails struct {
122 121
 func RunNewClient(server *Server, conn clientConn) {
123 122
 	now := time.Now()
124 123
 	config := server.Config()
125
-	fullLineLenLimit := config.Limits.LineLen.Tags + config.Limits.LineLen.Rest
126
-	socket := NewSocket(conn.Conn, fullLineLenLimit*2, config.Server.MaxSendQBytes)
124
+	fullLineLenLimit := ircmsg.MaxlenTagsFromClient + config.Limits.LineLen.Rest
125
+	// give them 1k of grace over the limit:
126
+	socket := NewSocket(conn.Conn, fullLineLenLimit+1024, config.Server.MaxSendQBytes)
127 127
 	client := &Client{
128 128
 		atime:        now,
129 129
 		capabilities: caps.NewSet(),
@@ -260,30 +260,21 @@ func (client *Client) IPString() string {
260 260
 // command goroutine
261 261
 //
262 262
 
263
-func (client *Client) recomputeMaxlens() (int, int) {
264
-	maxlenTags := 512
263
+func (client *Client) recomputeMaxlens() int {
265 264
 	maxlenRest := 512
266
-	if client.capabilities.Has(caps.MessageTags) {
267
-		maxlenTags = 4096
268
-	}
269 265
 	if client.capabilities.Has(caps.MaxLine) {
270
-		limits := client.server.Limits()
271
-		if limits.LineLen.Tags > maxlenTags {
272
-			maxlenTags = limits.LineLen.Tags
273
-		}
274
-		maxlenRest = limits.LineLen.Rest
266
+		maxlenRest = client.server.Limits().LineLen.Rest
275 267
 	}
276 268
 
277
-	atomic.StoreUint32(&client.maxlenTags, uint32(maxlenTags))
278 269
 	atomic.StoreUint32(&client.maxlenRest, uint32(maxlenRest))
279 270
 
280
-	return maxlenTags, maxlenRest
271
+	return maxlenRest
281 272
 }
282 273
 
283 274
 // allow these negotiated length limits to be read without locks; this is a convenience
284 275
 // so that Client.Send doesn't have to acquire any Client locks
285
-func (client *Client) maxlens() (int, int) {
286
-	return int(atomic.LoadUint32(&client.maxlenTags)), int(atomic.LoadUint32(&client.maxlenRest))
276
+func (client *Client) MaxlenRest() int {
277
+	return int(atomic.LoadUint32(&client.maxlenRest))
287 278
 }
288 279
 
289 280
 func (client *Client) run() {
@@ -306,8 +297,7 @@ func (client *Client) run() {
306 297
 		client.destroy(false)
307 298
 	}()
308 299
 
309
-	client.idletimer = NewIdleTimer(client)
310
-	client.idletimer.Start()
300
+	client.idletimer.Initialize(client)
311 301
 
312 302
 	client.nickTimer = NewNickTimer(client)
313 303
 
@@ -316,7 +306,7 @@ func (client *Client) run() {
316 306
 	firstLine := true
317 307
 
318 308
 	for {
319
-		maxlenTags, maxlenRest := client.recomputeMaxlens()
309
+		maxlenRest := client.recomputeMaxlens()
320 310
 
321 311
 		line, err = client.socket.Read()
322 312
 		if err != nil {
@@ -345,9 +335,12 @@ func (client *Client) run() {
345 335
 			}
346 336
 		}
347 337
 
348
-		msg, err = ircmsg.ParseLineMaxLen(line, maxlenTags, maxlenRest)
338
+		msg, err = ircmsg.ParseLineStrict(line, true, maxlenRest)
349 339
 		if err == ircmsg.ErrorLineIsEmpty {
350 340
 			continue
341
+		} else if err == ircmsg.ErrorLineTooLong {
342
+			client.Send(nil, client.server.name, ERR_INPUTTOOLONG, client.nick, client.t("Input line too long"))
343
+			continue
351 344
 		} else if err != nil {
352 345
 			client.Quit(client.t("Received malformed line"))
353 346
 			break
@@ -553,11 +546,11 @@ func (client *Client) replayPrivmsgHistory(rb *ResponseBuffer, items []history.I
553 546
 		default:
554 547
 			continue
555 548
 		}
556
-		var tags Tags
549
+		var tags map[string]string
557 550
 		if serverTime {
558
-			tags = ensureTag(tags, "time", item.Time.Format(IRCv3TimestampFormat))
551
+			tags = map[string]string{"time": item.Time.Format(IRCv3TimestampFormat)}
559 552
 		}
560
-		rb.AddSplitMessageFromClient(item.Msgid, item.Nick, item.AccountName, tags, command, nick, item.Message)
553
+		rb.AddSplitMessageFromClient(item.Nick, item.AccountName, tags, command, nick, item.Message)
561 554
 	}
562 555
 	if !complete {
563 556
 		rb.Add(nil, "HistServ", "NOTICE", nick, client.t("Some additional message history may have been lost"))
@@ -855,17 +848,18 @@ func (client *Client) Quit(message string) {
855 848
 		return
856 849
 	}
857 850
 
858
-	var quitLine string
851
+	var finalData []byte
859 852
 	// #364: don't send QUIT lines to unregistered clients
860 853
 	if registered {
861 854
 		quitMsg := ircmsg.MakeMessage(nil, prefix, "QUIT", message)
862
-		quitLine, _ = quitMsg.Line()
855
+		finalData, _ = quitMsg.LineBytesStrict(false, 512)
863 856
 	}
864 857
 
865 858
 	errorMsg := ircmsg.MakeMessage(nil, "", "ERROR", message)
866
-	errorLine, _ := errorMsg.Line()
859
+	errorMsgBytes, _ := errorMsg.LineBytesStrict(false, 512)
860
+	finalData = append(finalData, errorMsgBytes...)
867 861
 
868
-	client.socket.SetFinalData(quitLine + errorLine)
862
+	client.socket.SetFinalData(finalData)
869 863
 }
870 864
 
871 865
 // destroy gets rid of a client, removes them from server lists etc.
@@ -976,50 +970,45 @@ func (client *Client) destroy(beingResumed bool) {
976 970
 
977 971
 // SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client.
978 972
 // Adds account-tag to the line as well.
979
-func (client *Client) SendSplitMsgFromClient(msgid string, from *Client, tags Tags, command, target string, message utils.SplitMessage) {
980
-	client.sendSplitMsgFromClientInternal(false, time.Time{}, msgid, from.NickMaskString(), from.AccountName(), tags, command, target, message)
973
+func (client *Client) SendSplitMsgFromClient(from *Client, tags map[string]string, command, target string, message utils.SplitMessage) {
974
+	client.sendSplitMsgFromClientInternal(false, time.Time{}, from.NickMaskString(), from.AccountName(), tags, command, target, message)
981 975
 }
982 976
 
983
-func (client *Client) sendSplitMsgFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags Tags, command, target string, message utils.SplitMessage) {
977
+func (client *Client) sendSplitMsgFromClientInternal(blocking bool, serverTime time.Time, nickmask, accountName string, tags map[string]string, command, target string, message utils.SplitMessage) {
984 978
 	if client.capabilities.Has(caps.MaxLine) || message.Wrapped == nil {
985
-		client.sendFromClientInternal(blocking, serverTime, msgid, nickmask, accountName, tags, command, target, message.Original)
979
+		client.sendFromClientInternal(blocking, serverTime, message.Msgid, nickmask, accountName, tags, command, target, message.Message)
986 980
 	} else {
987
-		for _, str := range message.Wrapped {
988
-			client.sendFromClientInternal(blocking, serverTime, msgid, nickmask, accountName, tags, command, target, str)
981
+		for _, messagePair := range message.Wrapped {
982
+			client.sendFromClientInternal(blocking, serverTime, messagePair.Msgid, nickmask, accountName, tags, command, target, messagePair.Message)
989 983
 		}
990 984
 	}
991 985
 }
992 986
 
993 987
 // SendFromClient sends an IRC line coming from a specific client.
994 988
 // Adds account-tag to the line as well.
995
-func (client *Client) SendFromClient(msgid string, from *Client, tags Tags, command string, params ...string) error {
989
+func (client *Client) SendFromClient(msgid string, from *Client, tags map[string]string, command string, params ...string) error {
996 990
 	return client.sendFromClientInternal(false, time.Time{}, msgid, from.NickMaskString(), from.AccountName(), tags, command, params...)
997 991
 }
998 992
 
999
-// helper to add a tag to `tags` (or create a new tag set if the current one is nil)
1000
-func ensureTag(tags Tags, tagName, tagValue string) (result Tags) {
1001
-	if tags == nil {
1002
-		result = ircmsg.MakeTags(tagName, tagValue)
1003
-	} else {
1004
-		result = tags
1005
-		(*tags)[tagName] = ircmsg.MakeTagValue(tagValue)
1006
-	}
1007
-	return
1008
-}
1009
-
1010
-// XXX this is a hack where we allow overriding the client's nickmask
1011
-// this is to support CHGHOST, which requires that we send the *original* nickmask with the response
1012
-func (client *Client) sendFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags Tags, command string, params ...string) error {
993
+// this is SendFromClient, but directly exposing nickmask and accountName,
994
+// for things like history replay and CHGHOST where they no longer (necessarily)
995
+// correspond to the current state of a client
996
+func (client *Client) sendFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags map[string]string, command string, params ...string) error {
997
+	msg := ircmsg.MakeMessage(tags, nickmask, command, params...)
1013 998
 	// attach account-tag
1014 999
 	if client.capabilities.Has(caps.AccountTag) && accountName != "*" {
1015
-		tags = ensureTag(tags, "account", accountName)
1000
+		msg.SetTag("account", accountName)
1016 1001
 	}
1017 1002
 	// attach message-id
1018
-	if len(msgid) > 0 && client.capabilities.Has(caps.MessageTags) {
1019
-		tags = ensureTag(tags, "draft/msgid", msgid)
1003
+	if msgid != "" && client.capabilities.Has(caps.MessageTags) {
1004
+		msg.SetTag("draft/msgid", msgid)
1005
+	}
1006
+	// attach server-time
1007
+	if client.capabilities.Has(caps.ServerTime) {
1008
+		msg.SetTag("time", time.Now().UTC().Format(IRCv3TimestampFormat))
1020 1009
 	}
1021 1010
 
1022
-	return client.sendInternal(blocking, serverTime, tags, nickmask, command, params...)
1011
+	return client.SendRawMessage(msg, blocking)
1023 1012
 }
1024 1013
 
1025 1014
 var (
@@ -1039,24 +1028,24 @@ var (
1039 1028
 func (client *Client) SendRawMessage(message ircmsg.IrcMessage, blocking bool) error {
1040 1029
 	// use dumb hack to force the last param to be a trailing param if required
1041 1030
 	var usedTrailingHack bool
1042
-	if commandsThatMustUseTrailing[strings.ToUpper(message.Command)] && len(message.Params) > 0 {
1031
+	if commandsThatMustUseTrailing[message.Command] && len(message.Params) > 0 {
1043 1032
 		lastParam := message.Params[len(message.Params)-1]
1044 1033
 		// to force trailing, we ensure the final param contains a space
1045
-		if !strings.Contains(lastParam, " ") {
1034
+		if strings.IndexByte(lastParam, ' ') == -1 {
1046 1035
 			message.Params[len(message.Params)-1] = lastParam + " "
1047 1036
 			usedTrailingHack = true
1048 1037
 		}
1049 1038
 	}
1050 1039
 
1051 1040
 	// assemble message
1052
-	maxlenTags, maxlenRest := client.maxlens()
1053
-	line, err := message.LineMaxLenBytes(maxlenTags, maxlenRest)
1041
+	maxlenRest := client.MaxlenRest()
1042
+	line, err := message.LineBytesStrict(false, maxlenRest)
1054 1043
 	if err != nil {
1055 1044
 		logline := fmt.Sprintf("Error assembling message for sending: %v\n%s", err, debug.Stack())
1056 1045
 		client.server.logger.Error("internal", logline)
1057 1046
 
1058 1047
 		message = ircmsg.MakeMessage(nil, client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending")
1059
-		line, _ := message.LineBytes()
1048
+		line, _ := message.LineBytesStrict(false, 0)
1060 1049
 
1061 1050
 		if blocking {
1062 1051
 			client.socket.BlockingWrite(line)
@@ -1068,7 +1057,7 @@ func (client *Client) SendRawMessage(message ircmsg.IrcMessage, blocking bool) e
1068 1057
 
1069 1058
 	// if we used the trailing hack, we need to strip the final space we appended earlier on
1070 1059
 	if usedTrailingHack {
1071
-		copy(line[len(line)-3:], []byte{'\r', '\n'})
1060
+		copy(line[len(line)-3:], "\r\n")
1072 1061
 		line = line[:len(line)-1]
1073 1062
 	}
1074 1063
 
@@ -1084,24 +1073,13 @@ func (client *Client) SendRawMessage(message ircmsg.IrcMessage, blocking bool) e
1084 1073
 	}
1085 1074
 }
1086 1075
 
1087
-func (client *Client) sendInternal(blocking bool, serverTime time.Time, tags Tags, prefix string, command string, params ...string) error {
1088
-	// attach server time
1089
-	if client.capabilities.Has(caps.ServerTime) {
1090
-		if serverTime.IsZero() {
1091
-			serverTime = time.Now()
1092
-		}
1093
-		tags = ensureTag(tags, "time", serverTime.UTC().Format(IRCv3TimestampFormat))
1094
-	}
1095
-
1096
-	// send out the message
1097
-	message := ircmsg.MakeMessage(tags, prefix, command, params...)
1098
-	client.SendRawMessage(message, blocking)
1099
-	return nil
1100
-}
1101
-
1102 1076
 // Send sends an IRC line to the client.
1103
-func (client *Client) Send(tags Tags, prefix string, command string, params ...string) error {
1104
-	return client.sendInternal(false, time.Time{}, tags, prefix, command, params...)
1077
+func (client *Client) Send(tags map[string]string, prefix string, command string, params ...string) error {
1078
+	msg := ircmsg.MakeMessage(tags, prefix, command, params...)
1079
+	if client.capabilities.Has(caps.ServerTime) && !msg.HasTag("time") {
1080
+		msg.SetTag("time", time.Now().UTC().Format(IRCv3TimestampFormat))
1081
+	}
1082
+	return client.SendRawMessage(msg, false)
1105 1083
 }
1106 1084
 
1107 1085
 // Notice sends the client a notice from the server.

+ 0
- 4
irc/config.go Ver arquivo

@@ -202,7 +202,6 @@ type OperConfig struct {
202 202
 
203 203
 // LineLenConfig controls line lengths.
204 204
 type LineLenLimits struct {
205
-	Tags int
206 205
 	Rest int
207 206
 }
208 207
 
@@ -553,9 +552,6 @@ func LoadConfig(filename string) (config *Config, err error) {
553 552
 	}
554 553
 	config.Server.WebIRC = newWebIRC
555 554
 	// process limits
556
-	if config.Limits.LineLen.Tags < 512 {
557
-		config.Limits.LineLen.Tags = 512
558
-	}
559 555
 	if config.Limits.LineLen.Rest < 512 {
560 556
 		config.Limits.LineLen.Rest = 512
561 557
 	}

+ 15
- 23
irc/handlers.go Ver arquivo

@@ -1874,7 +1874,7 @@ func nickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1874 1874
 
1875 1875
 // NOTICE <target>{,<target>} <message>
1876 1876
 func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
1877
-	clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
1877
+	clientOnlyTags := msg.ClientOnlyTags()
1878 1878
 	targets := strings.Split(msg.Params[0], ",")
1879 1879
 	message := msg.Params[1]
1880 1880
 
@@ -1883,7 +1883,6 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
1883 1883
 		return false
1884 1884
 	}
1885 1885
 
1886
-	// split privmsg
1887 1886
 	splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
1888 1887
 
1889 1888
 	for i, targetString := range targets {
@@ -1905,8 +1904,7 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
1905 1904
 				// errors silently ignored with NOTICE as per RFC
1906 1905
 				continue
1907 1906
 			}
1908
-			msgid := server.generateMessageID()
1909
-			channel.SplitNotice(msgid, lowestPrefix, clientOnlyTags, client, splitMsg, rb)
1907
+			channel.SendSplitMessage("NOTICE", lowestPrefix, clientOnlyTags, client, splitMsg, rb)
1910 1908
 		} else {
1911 1909
 			target, err := CasefoldName(targetString)
1912 1910
 			if err != nil {
@@ -1926,23 +1924,21 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
1926 1924
 			if !user.capabilities.Has(caps.MessageTags) {
1927 1925
 				clientOnlyTags = nil
1928 1926
 			}
1929
-			msgid := server.generateMessageID()
1930 1927
 			// restrict messages appropriately when +R is set
1931 1928
 			// intentionally make the sending user think the message went through fine
1932 1929
 			allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
1933 1930
 			allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
1934 1931
 			if allowedPlusR && allowedTor {
1935
-				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1932
+				user.SendSplitMsgFromClient(client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1936 1933
 			}
1937 1934
 			nickMaskString := client.NickMaskString()
1938 1935
 			accountName := client.AccountName()
1939 1936
 			if client.capabilities.Has(caps.EchoMessage) {
1940
-				rb.AddSplitMessageFromClient(msgid, nickMaskString, accountName, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1937
+				rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1941 1938
 			}
1942 1939
 
1943 1940
 			user.history.Add(history.Item{
1944 1941
 				Type:        history.Notice,
1945
-				Msgid:       msgid,
1946 1942
 				Message:     splitMsg,
1947 1943
 				Nick:        nickMaskString,
1948 1944
 				AccountName: accountName,
@@ -2096,7 +2092,7 @@ func isRestrictedCTCPMessage(message string) bool {
2096 2092
 
2097 2093
 // PRIVMSG <target>{,<target>} <message>
2098 2094
 func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2099
-	clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
2095
+	clientOnlyTags := msg.ClientOnlyTags()
2100 2096
 	targets := strings.Split(msg.Params[0], ",")
2101 2097
 	message := msg.Params[1]
2102 2098
 
@@ -2133,8 +2129,7 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
2133 2129
 				rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
2134 2130
 				continue
2135 2131
 			}
2136
-			msgid := server.generateMessageID()
2137
-			channel.SplitPrivMsg(msgid, lowestPrefix, clientOnlyTags, client, splitMsg, rb)
2132
+			channel.SendSplitMessage("PRIVMSG", lowestPrefix, clientOnlyTags, client, splitMsg, rb)
2138 2133
 		} else {
2139 2134
 			target, err = CasefoldName(targetString)
2140 2135
 			if service, isService := OragonoServices[target]; isService {
@@ -2151,18 +2146,17 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
2151 2146
 			if !user.capabilities.Has(caps.MessageTags) {
2152 2147
 				clientOnlyTags = nil
2153 2148
 			}
2154
-			msgid := server.generateMessageID()
2155 2149
 			// restrict messages appropriately when +R is set
2156 2150
 			// intentionally make the sending user think the message went through fine
2157 2151
 			allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
2158 2152
 			allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
2159 2153
 			if allowedPlusR && allowedTor {
2160
-				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2154
+				user.SendSplitMsgFromClient(client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2161 2155
 			}
2162 2156
 			nickMaskString := client.NickMaskString()
2163 2157
 			accountName := client.AccountName()
2164 2158
 			if client.capabilities.Has(caps.EchoMessage) {
2165
-				rb.AddSplitMessageFromClient(msgid, nickMaskString, accountName, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2159
+				rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
2166 2160
 			}
2167 2161
 			if user.HasMode(modes.Away) {
2168 2162
 				//TODO(dan): possibly implement cooldown of away notifications to users
@@ -2171,7 +2165,6 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
2171 2165
 
2172 2166
 			user.history.Add(history.Item{
2173 2167
 				Type:        history.Privmsg,
2174
-				Msgid:       msgid,
2175 2168
 				Message:     splitMsg,
2176 2169
 				Nick:        nickMaskString,
2177 2170
 				AccountName: accountName,
@@ -2357,7 +2350,7 @@ func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
2357 2350
 
2358 2351
 // TAGMSG <target>{,<target>}
2359 2352
 func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2360
-	clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
2353
+	clientOnlyTags := msg.ClientOnlyTags()
2361 2354
 	// no client-only tags, so we can drop it
2362 2355
 	if clientOnlyTags == nil {
2363 2356
 		return false
@@ -2366,6 +2359,7 @@ func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2366 2359
 	targets := strings.Split(msg.Params[0], ",")
2367 2360
 
2368 2361
 	cnick := client.Nick()
2362
+	message := utils.MakeSplitMessage("", true) // assign consistent message ID
2369 2363
 	for i, targetString := range targets {
2370 2364
 		// max of four targets per privmsg
2371 2365
 		if i > maxTargets-1 {
@@ -2390,9 +2384,7 @@ func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2390 2384
 				rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
2391 2385
 				continue
2392 2386
 			}
2393
-			msgid := server.generateMessageID()
2394
-
2395
-			channel.TagMsg(msgid, lowestPrefix, clientOnlyTags, client, rb)
2387
+			channel.SendSplitMessage("TAGMSG", lowestPrefix, clientOnlyTags, client, message, rb)
2396 2388
 		} else {
2397 2389
 			target, err = CasefoldName(targetString)
2398 2390
 			user := server.clients.Get(target)
@@ -2402,19 +2394,19 @@ func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2402 2394
 				}
2403 2395
 				continue
2404 2396
 			}
2405
-			msgid := server.generateMessageID()
2406 2397
 
2407 2398
 			// end user can't receive tagmsgs
2408 2399
 			if !user.capabilities.Has(caps.MessageTags) {
2409 2400
 				continue
2410 2401
 			}
2411
-			user.SendFromClient(msgid, client, clientOnlyTags, "TAGMSG", user.nick)
2402
+			unick := user.Nick()
2403
+			user.SendSplitMsgFromClient(client, clientOnlyTags, "TAGMSG", unick, message)
2412 2404
 			if client.capabilities.Has(caps.EchoMessage) {
2413
-				rb.AddFromClient(msgid, client.NickMaskString(), client.AccountName(), clientOnlyTags, "TAGMSG", user.nick)
2405
+				rb.AddSplitMessageFromClient(client.NickMaskString(), client.AccountName(), clientOnlyTags, "TAGMSG", unick, message)
2414 2406
 			}
2415 2407
 			if user.HasMode(modes.Away) {
2416 2408
 				//TODO(dan): possibly implement cooldown of away notifications to users
2417
-				rb.Add(nil, server.name, RPL_AWAY, cnick, user.Nick(), user.AwayMessage())
2409
+				rb.Add(nil, server.name, RPL_AWAY, cnick, unick, user.AwayMessage())
2418 2410
 			}
2419 2411
 		}
2420 2412
 	}

+ 2
- 2
irc/history/history.go Ver arquivo

@@ -21,6 +21,7 @@ const (
21 21
 	Kick
22 22
 	Quit
23 23
 	Mode
24
+	Tagmsg
24 25
 )
25 26
 
26 27
 // Item represents an event (e.g., a PRIVMSG or a JOIN) and its associated data
@@ -33,14 +34,13 @@ type Item struct {
33 34
 	AccountName string
34 35
 	Message     utils.SplitMessage
35 36
 	// for non-privmsg items, we may stuff some other data in here
36
-	Msgid string
37 37
 }
38 38
 
39 39
 // HasMsgid tests whether a message has the message id `msgid`.
40 40
 func (item *Item) HasMsgid(msgid string) bool {
41 41
 	// XXX we stuff other data in the Msgid field sometimes,
42 42
 	// don't match it by accident
43
-	return (item.Type == Privmsg || item.Type == Notice) && item.Msgid == msgid
43
+	return (item.Type == Privmsg || item.Type == Notice) && item.Message.Msgid == msgid
44 44
 }
45 45
 
46 46
 type Predicate func(item Item) (matches bool)

+ 10
- 16
irc/idletimer.go Ver arquivo

@@ -53,14 +53,17 @@ type IdleTimer struct {
53 53
 	timer       *time.Timer
54 54
 }
55 55
 
56
-// NewIdleTimer sets up a new IdleTimer using constant timeouts.
57
-func NewIdleTimer(client *Client) *IdleTimer {
58
-	it := IdleTimer{
59
-		registerTimeout: RegisterTimeout,
60
-		client:          client,
61
-	}
56
+// Initialize sets up an IdleTimer and starts counting idle time;
57
+// if there is no activity from the client, it will eventually be stopped.
58
+func (it *IdleTimer) Initialize(client *Client) {
59
+	it.client = client
60
+	it.registerTimeout = RegisterTimeout
62 61
 	it.idleTimeout, it.quitTimeout = it.recomputeDurations()
63
-	return &it
62
+
63
+	it.Lock()
64
+	defer it.Unlock()
65
+	it.state = TimerUnregistered
66
+	it.resetTimeout()
64 67
 }
65 68
 
66 69
 // recomputeDurations recomputes the idle and quit durations, given the client's caps.
@@ -81,15 +84,6 @@ func (it *IdleTimer) recomputeDurations() (idleTimeout, quitTimeout time.Duratio
81 84
 	return
82 85
 }
83 86
 
84
-// Start starts counting idle time; if there is no activity from the client,
85
-// it will eventually be stopped.
86
-func (it *IdleTimer) Start() {
87
-	it.Lock()
88
-	defer it.Unlock()
89
-	it.state = TimerUnregistered
90
-	it.resetTimeout()
91
-}
92
-
93 87
 func (it *IdleTimer) Touch() {
94 88
 	idleTimeout, quitTimeout := it.recomputeDurations()
95 89
 

+ 3
- 2
irc/languages/languages.go Ver arquivo

@@ -175,7 +175,7 @@ func (lm *Manager) Translators() []string {
175 175
 		tlist = append(tlist, fmt.Sprintf("%s (%s): %s", info.Name, info.Code, info.Contributors))
176 176
 	}
177 177
 
178
-	sort.Sort(tlist)
178
+	tlist.Sort()
179 179
 	return tlist
180 180
 }
181 181
 
@@ -228,7 +228,7 @@ func (lm *Manager) Translate(languages []string, originalString string) string {
228 228
 }
229 229
 
230 230
 func (lm *Manager) CapValue() string {
231
-	langCodes := make([]string, len(lm.Languages)+1)
231
+	langCodes := make(sort.StringSlice, len(lm.Languages)+1)
232 232
 	langCodes[0] = strconv.Itoa(len(lm.Languages))
233 233
 	i := 1
234 234
 	for _, info := range lm.Languages {
@@ -239,5 +239,6 @@ func (lm *Manager) CapValue() string {
239 239
 		langCodes[i] = codeToken
240 240
 		i += 1
241 241
 	}
242
+	langCodes.Sort()
242 243
 	return strings.Join(langCodes, ",")
243 244
 }

+ 1
- 0
irc/numerics.go Ver arquivo

@@ -119,6 +119,7 @@ const (
119 119
 	ERR_NOTOPLEVEL                  = "413"
120 120
 	ERR_WILDTOPLEVEL                = "414"
121 121
 	ERR_BADMASK                     = "415"
122
+	ERR_INPUTTOOLONG                = "417"
122 123
 	ERR_UNKNOWNCOMMAND              = "421"
123 124
 	ERR_NOMOTD                      = "422"
124 125
 	ERR_NOADMININFO                 = "423"

+ 26
- 24
irc/responsebuffer.go Ver arquivo

@@ -32,7 +32,8 @@ type ResponseBuffer struct {
32 32
 
33 33
 // GetLabel returns the label from the given message.
34 34
 func GetLabel(msg ircmsg.IrcMessage) string {
35
-	return msg.Tags[caps.LabelTagName].Value
35
+	_, value := msg.GetTag(caps.LabelTagName)
36
+	return value
36 37
 }
37 38
 
38 39
 // NewResponseBuffer returns a new ResponseBuffer.
@@ -42,8 +43,7 @@ func NewResponseBuffer(target *Client) *ResponseBuffer {
42 43
 	}
43 44
 }
44 45
 
45
-// Add adds a standard new message to our queue.
46
-func (rb *ResponseBuffer) Add(tags *map[string]ircmsg.TagValue, prefix string, command string, params ...string) {
46
+func (rb *ResponseBuffer) AddMessage(msg ircmsg.IrcMessage) {
47 47
 	if rb.finalized {
48 48
 		rb.target.server.logger.Error("internal", "message added to finalized ResponseBuffer, undefined behavior")
49 49
 		debug.PrintStack()
@@ -52,33 +52,38 @@ func (rb *ResponseBuffer) Add(tags *map[string]ircmsg.TagValue, prefix string, c
52 52
 		return
53 53
 	}
54 54
 
55
-	message := ircmsg.MakeMessage(tags, prefix, command, params...)
56
-	rb.messages = append(rb.messages, message)
55
+	rb.messages = append(rb.messages, msg)
56
+}
57
+
58
+// Add adds a standard new message to our queue.
59
+func (rb *ResponseBuffer) Add(tags map[string]string, prefix string, command string, params ...string) {
60
+	rb.AddMessage(ircmsg.MakeMessage(tags, prefix, command, params...))
57 61
 }
58 62
 
59 63
 // AddFromClient adds a new message from a specific client to our queue.
60
-func (rb *ResponseBuffer) AddFromClient(msgid string, fromNickMask string, fromAccount string, tags *map[string]ircmsg.TagValue, command string, params ...string) {
64
+func (rb *ResponseBuffer) AddFromClient(msgid string, fromNickMask string, fromAccount string, tags map[string]string, command string, params ...string) {
65
+	msg := ircmsg.MakeMessage(nil, fromNickMask, command, params...)
66
+	msg.UpdateTags(tags)
67
+
61 68
 	// attach account-tag
62
-	if rb.target.capabilities.Has(caps.AccountTag) {
63
-		if fromAccount != "*" {
64
-			tags = ensureTag(tags, "account", fromAccount)
65
-		}
69
+	if rb.target.capabilities.Has(caps.AccountTag) && fromAccount != "*" {
70
+		msg.SetTag("account", fromAccount)
66 71
 	}
67 72
 	// attach message-id
68 73
 	if len(msgid) > 0 && rb.target.capabilities.Has(caps.MessageTags) {
69
-		tags = ensureTag(tags, "draft/msgid", msgid)
74
+		msg.SetTag("draft/msgid", msgid)
70 75
 	}
71 76
 
72
-	rb.Add(tags, fromNickMask, command, params...)
77
+	rb.AddMessage(msg)
73 78
 }
74 79
 
75 80
 // AddSplitMessageFromClient adds a new split message from a specific client to our queue.
76
-func (rb *ResponseBuffer) AddSplitMessageFromClient(msgid string, fromNickMask string, fromAccount string, tags *map[string]ircmsg.TagValue, command string, target string, message utils.SplitMessage) {
81
+func (rb *ResponseBuffer) AddSplitMessageFromClient(fromNickMask string, fromAccount string, tags map[string]string, command string, target string, message utils.SplitMessage) {
77 82
 	if rb.target.capabilities.Has(caps.MaxLine) || message.Wrapped == nil {
78
-		rb.AddFromClient(msgid, fromNickMask, fromAccount, tags, command, target, message.Original)
83
+		rb.AddFromClient(message.Msgid, fromNickMask, fromAccount, tags, command, target, message.Message)
79 84
 	} else {
80
-		for _, str := range message.Wrapped {
81
-			rb.AddFromClient(msgid, fromNickMask, fromAccount, tags, command, target, str)
85
+		for _, messagePair := range message.Wrapped {
86
+			rb.AddFromClient(messagePair.Msgid, fromNickMask, fromAccount, tags, command, target, messagePair.Message)
82 87
 		}
83 88
 	}
84 89
 }
@@ -103,7 +108,7 @@ func (rb *ResponseBuffer) sendBatchStart(batchType string, blocking bool) {
103 108
 
104 109
 	message := ircmsg.MakeMessage(nil, rb.target.server.name, "BATCH", "+"+rb.batchID, batchType)
105 110
 	if rb.Label != "" {
106
-		message.Tags[caps.LabelTagName] = ircmsg.MakeTagValue(rb.Label)
111
+		message.SetTag(caps.LabelTagName, rb.Label)
107 112
 	}
108 113
 	rb.target.SendRawMessage(message, blocking)
109 114
 }
@@ -149,7 +154,7 @@ func (rb *ResponseBuffer) flushInternal(final bool, blocking bool) error {
149 154
 
150 155
 	// if label but no batch, add label to first message
151 156
 	if useLabel && !useBatch && len(rb.messages) == 1 && rb.batchID == "" {
152
-		rb.messages[0].Tags[caps.LabelTagName] = ircmsg.MakeTagValue(rb.Label)
157
+		rb.messages[0].SetTag(caps.LabelTagName, rb.Label)
153 158
 	} else if useBatch {
154 159
 		rb.sendBatchStart(defaultBatchType, blocking)
155 160
 	}
@@ -157,16 +162,13 @@ func (rb *ResponseBuffer) flushInternal(final bool, blocking bool) error {
157 162
 	// send each message out
158 163
 	for _, message := range rb.messages {
159 164
 		// attach server-time if needed
160
-		if rb.target.capabilities.Has(caps.ServerTime) {
161
-			if !message.Tags["time"].HasValue {
162
-				t := time.Now().UTC().Format(IRCv3TimestampFormat)
163
-				message.Tags["time"] = ircmsg.MakeTagValue(t)
164
-			}
165
+		if rb.target.capabilities.Has(caps.ServerTime) && !message.HasTag("time") {
166
+			message.SetTag("time", time.Now().UTC().Format(IRCv3TimestampFormat))
165 167
 		}
166 168
 
167 169
 		// attach batch ID
168 170
 		if rb.batchID != "" {
169
-			message.Tags["batch"] = ircmsg.MakeTagValue(rb.batchID)
171
+			message.SetTag("batch", rb.batchID)
170 172
 		}
171 173
 
172 174
 		// send message out

+ 3
- 8
irc/server.go Ver arquivo

@@ -387,11 +387,6 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, isTor b
387 387
 	return &wrapper, nil
388 388
 }
389 389
 
390
-// generateMessageID returns a network-unique message ID.
391
-func (server *Server) generateMessageID() string {
392
-	return utils.GenerateSecretToken()
393
-}
394
-
395 390
 //
396 391
 // server functionality
397 392
 //
@@ -623,7 +618,7 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
623 618
 	} else {
624 619
 		// enforce configs that can't be changed after launch:
625 620
 		currentLimits := server.Limits()
626
-		if currentLimits.LineLen.Tags != config.Limits.LineLen.Tags || currentLimits.LineLen.Rest != config.Limits.LineLen.Rest {
621
+		if currentLimits.LineLen.Rest != config.Limits.LineLen.Rest {
627 622
 			return fmt.Errorf("Maximum line length (linelen) cannot be changed after launching the server, rehash aborted")
628 623
 		} else if server.name != config.Server.Name {
629 624
 			return fmt.Errorf("Server name cannot be changed after launching the server, rehash aborted")
@@ -703,9 +698,9 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
703 698
 	}
704 699
 
705 700
 	// MaxLine
706
-	if config.Limits.LineLen.Tags != 512 || config.Limits.LineLen.Rest != 512 {
701
+	if config.Limits.LineLen.Rest != 512 {
707 702
 		SupportedCapabilities.Enable(caps.MaxLine)
708
-		value := fmt.Sprintf("%d,%d", config.Limits.LineLen.Tags, config.Limits.LineLen.Rest)
703
+		value := fmt.Sprintf("%d", config.Limits.LineLen.Rest)
709 704
 		CapValues.Set(caps.MaxLine, value)
710 705
 	}
711 706
 

+ 7
- 5
irc/socket.go Ver arquivo

@@ -20,6 +20,8 @@ import (
20 20
 var (
21 21
 	handshakeTimeout, _ = time.ParseDuration("5s")
22 22
 	errSendQExceeded    = errors.New("SendQ exceeded")
23
+
24
+	sendQExceededMessage = []byte("\r\nERROR :SendQ Exceeded\r\n")
23 25
 )
24 26
 
25 27
 // Socket represents an IRC socket.
@@ -38,7 +40,7 @@ type Socket struct {
38 40
 	totalLength   int
39 41
 	closed        bool
40 42
 	sendQExceeded bool
41
-	finalData     string // what to send when we die
43
+	finalData     []byte // what to send when we die
42 44
 	finalized     bool
43 45
 }
44 46
 
@@ -196,7 +198,7 @@ func (socket *Socket) wakeWriter() {
196 198
 }
197 199
 
198 200
 // SetFinalData sets the final data to send when the SocketWriter closes.
199
-func (socket *Socket) SetFinalData(data string) {
201
+func (socket *Socket) SetFinalData(data []byte) {
200 202
 	socket.Lock()
201 203
 	defer socket.Unlock()
202 204
 	socket.finalData = data
@@ -271,7 +273,7 @@ func (socket *Socket) finalize() {
271 273
 	socket.finalized = true
272 274
 	finalData := socket.finalData
273 275
 	if socket.sendQExceeded {
274
-		finalData = "\r\nERROR :SendQ Exceeded\r\n"
276
+		finalData = sendQExceededMessage
275 277
 	}
276 278
 	socket.Unlock()
277 279
 
@@ -279,8 +281,8 @@ func (socket *Socket) finalize() {
279 281
 		return
280 282
 	}
281 283
 
282
-	if finalData != "" {
283
-		socket.conn.Write([]byte(finalData))
284
+	if len(finalData) != 0 {
285
+		socket.conn.Write(finalData)
284 286
 	}
285 287
 
286 288
 	// close the connection

+ 0
- 3
irc/types.go Ver arquivo

@@ -6,7 +6,6 @@
6 6
 package irc
7 7
 
8 8
 import "github.com/oragono/oragono/irc/modes"
9
-import "github.com/goshuirc/irc-go/ircmsg"
10 9
 
11 10
 // ClientSet is a set of clients.
12 11
 type ClientSet map[*Client]bool
@@ -57,5 +56,3 @@ func (members MemberSet) AnyHasMode(mode modes.Mode) bool {
57 56
 
58 57
 // ChannelSet is a set of channels.
59 58
 type ChannelSet map[*Channel]bool
60
-
61
-type Tags *map[string]ircmsg.TagValue

+ 0
- 27
irc/utils/message_tags.go Ver arquivo

@@ -1,27 +0,0 @@
1
-// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
2
-// released under the MIT license
3
-
4
-package utils
5
-
6
-import "github.com/goshuirc/irc-go/ircmsg"
7
-
8
-// GetClientOnlyTags takes a tag map and returns a map containing just the client-only tags from it.
9
-func GetClientOnlyTags(tags map[string]ircmsg.TagValue) *map[string]ircmsg.TagValue {
10
-	if len(tags) < 1 {
11
-		return nil
12
-	}
13
-
14
-	clientOnlyTags := make(map[string]ircmsg.TagValue)
15
-
16
-	for name, value := range tags {
17
-		if len(name) > 1 && name[0] == '+' {
18
-			clientOnlyTags[name] = value
19
-		}
20
-	}
21
-
22
-	if len(clientOnlyTags) < 1 {
23
-		return nil
24
-	}
25
-
26
-	return &clientOnlyTags
27
-}

+ 20
- 5
irc/utils/text.go Ver arquivo

@@ -50,17 +50,32 @@ func WordWrap(text string, lineWidth int) []string {
50 50
 	return lines
51 51
 }
52 52
 
53
+type MessagePair struct {
54
+	Message string
55
+	Msgid   string
56
+}
57
+
53 58
 // SplitMessage represents a message that's been split for sending.
54 59
 type SplitMessage struct {
55
-	Original string
56
-	Wrapped  []string // if this is nil, Original didn't need wrapping and can be sent to anyone
60
+	MessagePair
61
+	Wrapped []MessagePair // if this is nil, `Message` didn't need wrapping and can be sent to anyone
57 62
 }
58 63
 
64
+const defaultLineWidth = 400
65
+
59 66
 func MakeSplitMessage(original string, origIs512 bool) (result SplitMessage) {
60
-	result.Original = original
67
+	result.Message = original
68
+	result.Msgid = GenerateSecretToken()
61 69
 
62
-	if !origIs512 {
63
-		result.Wrapped = WordWrap(original, 400)
70
+	if !origIs512 && defaultLineWidth < len(original) {
71
+		wrapped := WordWrap(original, defaultLineWidth)
72
+		result.Wrapped = make([]MessagePair, len(wrapped))
73
+		for i, wrappedMessage := range wrapped {
74
+			result.Wrapped[i] = MessagePair{
75
+				Message: wrappedMessage,
76
+				Msgid:   GenerateSecretToken(),
77
+			}
78
+		}
64 79
 	}
65 80
 
66 81
 	return

+ 2
- 4
oragono.yaml Ver arquivo

@@ -487,10 +487,8 @@ limits:
487 487
     # maximum length of IRC lines
488 488
     # this should generally be 1024-2048, and will only apply when negotiated by clients
489 489
     linelen:
490
-        # tags section
491
-        tags: 2048
492
-
493
-        # rest of the message
490
+        # ratified version of the message-tags cap fixes the max tag length at 8191 bytes
491
+        # configurable length for the rest of the message:
494 492
         rest: 2048
495 493
 
496 494
 # fakelag: prevents clients from spamming commands too rapidly

+ 1
- 1
vendor

@@ -1 +1 @@
1
-Subproject commit 72043bab39044196e57bff1fe5e59c9ec81e59f3
1
+Subproject commit 8ddbb531841add50f8b7aff8fe00bef311448aaa

Carregando…
Cancelar
Salvar