Changeset 139 in code
- Timestamp:
- Mar 25, 2020, 8:40:08 AM (5 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/bridge.go
r132 r139 30 30 // TODO: send multiple members in each message 31 31 for nick, membership := range ch.Members { 32 s := dc.marshalNick(ch.conn, nick) 33 if membership != 0 { 34 s = string(membership) + s 35 } 32 s := membership.String() + dc.marshalNick(ch.conn, nick) 36 33 37 34 dc.SendMessage(&irc.Message{ -
trunk/downstream.go
r137 r139 852 852 } 853 853 854 uc, upstreamName, err := dc.unmarshalEntity(name) 855 if err != nil { 856 return err 857 } 858 859 if uc.isChannel(upstreamName) { 860 // TODO: handle MODE channel mode arguments 861 if modeStr != "" { 862 uc.SendMessage(&irc.Message{ 863 Command: "MODE", 864 Params: []string{upstreamName, modeStr}, 865 }) 866 } else { 867 ch, ok := uc.channels[upstreamName] 868 if !ok { 869 return ircError{&irc.Message{ 870 Command: irc.ERR_NOSUCHCHANNEL, 871 Params: []string{dc.nick, name, "No such channel"}, 872 }} 873 } 874 875 dc.SendMessage(&irc.Message{ 876 Prefix: dc.srv.prefix(), 877 Command: irc.RPL_CHANNELMODEIS, 878 Params: []string{dc.nick, name, string(ch.modes)}, 879 }) 880 } 881 } else { 882 if name != dc.nick { 883 return ircError{&irc.Message{ 884 Command: irc.ERR_USERSDONTMATCH, 885 Params: []string{dc.nick, "Cannot change mode for other users"}, 886 }} 887 } 888 854 if name == dc.nick { 889 855 if modeStr != "" { 890 856 dc.forEachUpstream(func(uc *upstreamConn) { … … 901 867 }) 902 868 } 869 return nil 870 } 871 872 uc, upstreamName, err := dc.unmarshalEntity(name) 873 if err != nil { 874 return err 875 } 876 877 if !uc.isChannel(upstreamName) { 878 return ircError{&irc.Message{ 879 Command: irc.ERR_USERSDONTMATCH, 880 Params: []string{dc.nick, "Cannot change mode for other users"}, 881 }} 882 } 883 884 if modeStr != "" { 885 params := []string{upstreamName, modeStr} 886 params = append(params, msg.Params[2:]...) 887 uc.SendMessage(&irc.Message{ 888 Command: "MODE", 889 Params: params, 890 }) 891 } else { 892 ch, ok := uc.channels[upstreamName] 893 if !ok { 894 return ircError{&irc.Message{ 895 Command: irc.ERR_NOSUCHCHANNEL, 896 Params: []string{dc.nick, name, "No such channel"}, 897 }} 898 } 899 900 if ch.modes == nil { 901 // we haven't received the initial RPL_CHANNELMODEIS yet 902 // ignore the request, we will broadcast the modes later when we receive RPL_CHANNELMODEIS 903 return nil 904 } 905 906 modeStr, modeParams := ch.modes.Format() 907 params := []string{dc.nick, name, modeStr} 908 params = append(params, modeParams...) 909 910 dc.SendMessage(&irc.Message{ 911 Prefix: dc.srv.prefix(), 912 Command: irc.RPL_CHANNELMODEIS, 913 Params: params, 914 }) 903 915 } 904 916 case "WHO": -
trunk/irc.go
r128 r139 16 16 ) 17 17 18 type modeSetstring18 type userModes string 19 19 20 func (ms modeSet) Has(c byte) bool {20 func (ms userModes) Has(c byte) bool { 21 21 return strings.IndexByte(string(ms), c) >= 0 22 22 } 23 23 24 func (ms * modeSet) Add(c byte) {24 func (ms *userModes) Add(c byte) { 25 25 if !ms.Has(c) { 26 *ms += modeSet(c)26 *ms += userModes(c) 27 27 } 28 28 } 29 29 30 func (ms * modeSet) Del(c byte) {30 func (ms *userModes) Del(c byte) { 31 31 i := strings.IndexByte(string(*ms), c) 32 32 if i >= 0 { … … 35 35 } 36 36 37 func (ms * modeSet) Apply(s string) error {37 func (ms *userModes) Apply(s string) error { 38 38 var plusMinus byte 39 39 for i := 0; i < len(s); i++ { … … 54 54 return nil 55 55 } 56 57 type channelModeType byte 58 59 // standard channel mode types, as explained in https://modern.ircdocs.horse/#mode-message 60 const ( 61 // modes that add or remove an address to or from a list 62 modeTypeA channelModeType = iota 63 // modes that change a setting on a channel, and must always have a parameter 64 modeTypeB 65 // modes that change a setting on a channel, and must have a parameter when being set, and no parameter when being unset 66 modeTypeC 67 // modes that change a setting on a channel, and must not have a parameter 68 modeTypeD 69 ) 70 71 var stdChannelModes = map[byte]channelModeType{ 72 'b': modeTypeA, // ban list 73 'e': modeTypeA, // ban exception list 74 'I': modeTypeA, // invite exception list 75 'k': modeTypeB, // channel key 76 'l': modeTypeC, // channel user limit 77 'i': modeTypeD, // channel is invite-only 78 'm': modeTypeD, // channel is moderated 79 'n': modeTypeD, // channel has no external messages 80 's': modeTypeD, // channel is secret 81 't': modeTypeD, // channel has protected topic 82 } 83 84 type channelModes map[byte]string 85 86 func (cm channelModes) Apply(modeTypes map[byte]channelModeType, modeStr string, arguments ...string) error { 87 nextArgument := 0 88 var plusMinus byte 89 for i := 0; i < len(modeStr); i++ { 90 mode := modeStr[i] 91 if mode == '+' || mode == '-' { 92 plusMinus = mode 93 continue 94 } 95 if plusMinus != '+' && plusMinus != '-' { 96 return fmt.Errorf("malformed modestring %q: missing plus/minus", modeStr) 97 } 98 99 mt, ok := modeTypes[mode] 100 if !ok { 101 continue 102 } 103 if mt == modeTypeB || (mt == modeTypeC && plusMinus == '+') { 104 if plusMinus == '+' { 105 var argument string 106 // some sentitive arguments (such as channel keys) can be omitted for privacy 107 // (this will only happen for RPL_CHANNELMODEIS, never for MODE messages) 108 if nextArgument < len(arguments) { 109 argument = arguments[nextArgument] 110 } 111 cm[mode] = argument 112 } else { 113 delete(cm, mode) 114 } 115 nextArgument++ 116 } else if mt == modeTypeC || mt == modeTypeD { 117 if plusMinus == '+' { 118 cm[mode] = "" 119 } else { 120 delete(cm, mode) 121 } 122 } 123 } 124 return nil 125 } 126 127 func (cm channelModes) Format() (modeString string, parameters []string) { 128 var modesWithValues strings.Builder 129 var modesWithoutValues strings.Builder 130 parameters = make([]string, 0, 16) 131 for mode, value := range cm { 132 if value != "" { 133 modesWithValues.WriteString(string(mode)) 134 parameters = append(parameters, value) 135 } else { 136 modesWithoutValues.WriteString(string(mode)) 137 } 138 } 139 modeString = "+" + modesWithValues.String() + modesWithoutValues.String() 140 return 141 } 142 143 const stdChannelTypes = "#&+!" 56 144 57 145 type channelStatus byte … … 75 163 } 76 164 77 type membership byte 165 type membership struct { 166 Mode byte 167 Prefix byte 168 } 78 169 79 const ( 80 membershipFounder membership = '~'81 membershipProtected membership = '&'82 membershipOperator membership = '@'83 membershipHalfOp membership = '%'84 membershipVoice membership = '+'85 ) 170 var stdMemberships = []membership{ 171 {'q', '~'}, // founder 172 {'a', '&'}, // protected 173 {'o', '@'}, // operator 174 {'h', '%'}, // halfop 175 {'v', '+'}, // voice 176 } 86 177 87 const stdMembershipPrefixes = "~&@%+" 88 89 func (m membership) String() string { 90 if m == 0 { 178 func (m *membership) String() string { 179 if m == nil { 91 180 return "" 92 181 } 93 return string(m) 94 } 95 96 func parseMembershipPrefix(s string) (prefix membership, nick string) { 97 // TODO: any prefix from PREFIX RPL_ISUPPORT 98 if strings.IndexByte(stdMembershipPrefixes, s[0]) >= 0 { 99 return membership(s[0]), s[1:] 100 } else { 101 return 0, s 102 } 182 return string(m.Prefix) 103 183 } 104 184 -
trunk/upstream.go
r131 r139 22 22 TopicTime time.Time 23 23 Status channelStatus 24 modes modeSet25 Members map[string] membership24 modes channelModes 25 Members map[string]*membership 26 26 complete bool 27 27 } … … 39 39 serverName string 40 40 availableUserModes string 41 availableChannelModes string 42 channelModesWithParam string 41 availableChannelModes map[byte]channelModeType 42 availableChannelTypes string 43 availableMemberships []membership 43 44 44 45 registered bool … … 47 48 realname string 48 49 closed bool 49 modes modeSet50 modes userModes 50 51 channels map[string]*upstreamChannel 51 52 caps map[string]string … … 73 74 outgoing := make(chan *irc.Message, 64) 74 75 uc := &upstreamConn{ 75 network: network, 76 logger: logger, 77 net: netConn, 78 irc: irc.NewConn(netConn), 79 srv: network.user.srv, 80 user: network.user, 81 outgoing: outgoing, 82 ring: NewRing(network.user.srv.RingCap), 83 channels: make(map[string]*upstreamChannel), 84 caps: make(map[string]string), 76 network: network, 77 logger: logger, 78 net: netConn, 79 irc: irc.NewConn(netConn), 80 srv: network.user.srv, 81 user: network.user, 82 outgoing: outgoing, 83 ring: NewRing(network.user.srv.RingCap), 84 channels: make(map[string]*upstreamChannel), 85 caps: make(map[string]string), 86 availableChannelTypes: stdChannelTypes, 87 availableChannelModes: stdChannelModes, 88 availableMemberships: stdMemberships, 85 89 } 86 90 … … 131 135 132 136 func (uc *upstreamConn) isChannel(entity string) bool { 133 for _, r := range entity { 134 switch r { 135 // TODO: support upstream ISUPPORT channel prefixes 136 case '#', '&', '+', '!': 137 return true 138 } 139 break 137 if i := strings.IndexByte(uc.availableChannelTypes, entity[0]); i >= 0 { 138 return true 140 139 } 141 140 return false 141 } 142 143 func (uc *upstreamConn) parseMembershipPrefix(s string) (membership *membership, nick string) { 144 for _, m := range uc.availableMemberships { 145 if m.Prefix == s[0] { 146 return &m, s[1:] 147 } 148 } 149 return nil, s 142 150 } 143 151 … … 150 158 }) 151 159 return nil 152 case "MODE":153 var name, modeStr string154 if err := parseMessageParams(msg, &name, &modeStr); err != nil {155 return err156 }157 158 if !uc.isChannel(name) { // user mode change159 if name != uc.nick {160 return fmt.Errorf("received MODE message for unknown nick %q", name)161 }162 return uc.modes.Apply(modeStr)163 } else { // channel mode change164 // TODO: handle MODE channel mode arguments165 ch, err := uc.getChannel(name)166 if err != nil {167 return err168 }169 if err := ch.modes.Apply(modeStr); err != nil {170 return err171 }172 173 uc.forEachDownstream(func(dc *downstreamConn) {174 dc.SendMessage(&irc.Message{175 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),176 Command: "MODE",177 Params: []string{dc.marshalChannel(uc, name), modeStr},178 })179 })180 }181 160 case "NOTICE": 182 161 uc.logger.Print(msg) … … 347 326 } 348 327 case irc.RPL_MYINFO: 349 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, &uc.availableChannelModes); err != nil { 350 return err 351 } 352 if len(msg.Params) > 5 { 353 uc.channelModesWithParam = msg.Params[5] 328 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil { 329 return err 330 } 331 case irc.RPL_ISUPPORT: 332 if err := parseMessageParams(msg, nil, nil); err != nil { 333 return err 334 } 335 for _, token := range msg.Params[1 : len(msg.Params)-1] { 336 negate := false 337 parameter := token 338 value := "" 339 if strings.HasPrefix(token, "-") { 340 negate = true 341 token = token[1:] 342 } else { 343 if i := strings.IndexByte(token, '='); i >= 0 { 344 parameter = token[:i] 345 value = token[i+1:] 346 } 347 } 348 if !negate { 349 switch parameter { 350 case "CHANMODES": 351 parts := strings.SplitN(value, ",", 5) 352 if len(parts) < 4 { 353 return fmt.Errorf("malformed ISUPPORT CHANMODES value: %v", value) 354 } 355 modes := make(map[byte]channelModeType) 356 for i, mt := range []channelModeType{modeTypeA, modeTypeB, modeTypeC, modeTypeD} { 357 for j := 0; j < len(parts[i]); j++ { 358 mode := parts[i][j] 359 modes[mode] = mt 360 } 361 } 362 uc.availableChannelModes = modes 363 case "CHANTYPES": 364 uc.availableChannelTypes = value 365 case "PREFIX": 366 if value == "" { 367 uc.availableMemberships = nil 368 } else { 369 if value[0] != '(' { 370 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value) 371 } 372 sep := strings.IndexByte(value, ')') 373 if sep < 0 || len(value) != sep*2 { 374 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value) 375 } 376 memberships := make([]membership, len(value)/2-1) 377 for i := range memberships { 378 memberships[i] = membership{ 379 Mode: value[i+1], 380 Prefix: value[sep+i+1], 381 } 382 } 383 uc.availableMemberships = memberships 384 } 385 } 386 } else { 387 // TODO: handle ISUPPORT negations 388 } 354 389 } 355 390 case "NICK": … … 400 435 Name: ch, 401 436 conn: uc, 402 Members: make(map[string] membership),437 Members: make(map[string]*membership), 403 438 } 439 440 uc.SendMessage(&irc.Message{ 441 Command: "MODE", 442 Params: []string{ch}, 443 }) 404 444 } else { 405 445 ch, err := uc.getChannel(ch) … … 407 447 return err 408 448 } 409 ch.Members[msg.Prefix.Name] = 0449 ch.Members[msg.Prefix.Name] = nil 410 450 } 411 451 … … 509 549 }) 510 550 }) 551 case "MODE": 552 var name, modeStr string 553 if err := parseMessageParams(msg, &name, &modeStr); err != nil { 554 return err 555 } 556 557 if !uc.isChannel(name) { // user mode change 558 if name != uc.nick { 559 return fmt.Errorf("received MODE message for unknown nick %q", name) 560 } 561 return uc.modes.Apply(modeStr) 562 // TODO: notify downstreams about user mode change? 563 } else { // channel mode change 564 ch, err := uc.getChannel(name) 565 if err != nil { 566 return err 567 } 568 569 if ch.modes != nil { 570 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[2:]...); err != nil { 571 return err 572 } 573 } 574 575 uc.forEachDownstream(func(dc *downstreamConn) { 576 params := []string{dc.marshalChannel(uc, name), modeStr} 577 params = append(params, msg.Params[2:]...) 578 579 dc.SendMessage(&irc.Message{ 580 Prefix: dc.marshalUserPrefix(uc, msg.Prefix), 581 Command: "MODE", 582 Params: params, 583 }) 584 }) 585 } 586 case irc.RPL_UMODEIS: 587 if err := parseMessageParams(msg, nil); err != nil { 588 return err 589 } 590 modeStr := "" 591 if len(msg.Params) > 1 { 592 modeStr = msg.Params[1] 593 } 594 595 uc.modes = "" 596 if err := uc.modes.Apply(modeStr); err != nil { 597 return err 598 } 599 // TODO: send RPL_UMODEIS to downstream connections when applicable 600 case irc.RPL_CHANNELMODEIS: 601 var channel string 602 if err := parseMessageParams(msg, nil, &channel); err != nil { 603 return err 604 } 605 modeStr := "" 606 if len(msg.Params) > 2 { 607 modeStr = msg.Params[2] 608 } 609 610 ch, err := uc.getChannel(channel) 611 if err != nil { 612 return err 613 } 614 615 firstMode := ch.modes == nil 616 ch.modes = make(map[byte]string) 617 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[3:]...); err != nil { 618 return err 619 } 620 if firstMode { 621 modeStr, modeParams := ch.modes.Format() 622 623 uc.forEachDownstream(func(dc *downstreamConn) { 624 params := []string{dc.nick, dc.marshalChannel(uc, channel), modeStr} 625 params = append(params, modeParams...) 626 627 dc.SendMessage(&irc.Message{ 628 Prefix: dc.srv.prefix(), 629 Command: irc.RPL_CHANNELMODEIS, 630 Params: params, 631 }) 632 }) 633 } 511 634 case rpl_topicwhotime: 512 635 var name, who, timeStr string … … 541 664 542 665 for _, s := range strings.Split(members, " ") { 543 membership, nick := parseMembershipPrefix(s)666 membership, nick := uc.parseMembershipPrefix(s) 544 667 ch.Members[nick] = membership 545 668 } … … 680 803 channelList := make([]string, len(channels)) 681 804 for i, channel := range channels { 682 prefix, channel := parseMembershipPrefix(channel)805 prefix, channel := uc.parseMembershipPrefix(channel) 683 806 channel = dc.marshalChannel(uc, channel) 684 807 channelList[i] = prefix.String() + channel
Note:
See TracChangeset
for help on using the changeset viewer.