Changeset 293 in code for trunk


Ignore:
Timestamp:
May 21, 2020, 8:36:54 PM (5 years ago)
Author:
delthas
Message:

Fix parsing MODE messages by updating channel memberships

Previously, we only considered channel modes in the modes of a MODE
messages, which means channel membership changes were ignored. This
resulted in bugs where users channel memberships would not be properly
updated and cached with wrong values. Further, mode arguments
representing entities were not properly marshaled.

This adds support for correctly parsing and updating channel memberships
when processing MODE messages. Mode arguments corresponding to channel
memberships updates are now also properly marshaled.

MODE messages can't be easily sent from history because marshaling these
messages require knowing about the upstream available channel types and
channel membership types, which is currently only possible when
connected. For now this is not an issue since we do not send MODE
messages in history.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/downstream.go

    r292 r293  
    269269// marshalMessage re-formats a message coming from an upstream connection so
    270270// that it's suitable for being sent on this downstream connection. Only
    271 // messages that may appear in logs are supported.
     271// messages that may appear in logs are supported, except MODE.
    272272func (dc *downstreamConn) marshalMessage(msg *irc.Message, net *network) *irc.Message {
    273273        msg = msg.Copy()
     
    286286                msg.Params[1] = dc.marshalEntity(net, msg.Params[1])
    287287        case "TOPIC":
    288                 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
    289         case "MODE":
    290288                msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
    291289        case "QUIT":
  • trunk/irc.go

    r292 r293  
    8585type channelModes map[byte]string
    8686
    87 func (cm channelModes) Apply(modeTypes map[byte]channelModeType, modeStr string, arguments ...string) error {
     87// applyChannelModes parses a mode string and mode arguments from a MODE message,
     88// and applies the corresponding channel mode and user membership changes on that channel.
     89//
     90// If ch.modes is nil, channel modes are not updated.
     91//
     92// needMarshaling is a list of indexes of mode arguments that represent entities
     93// that must be marshaled when sent downstream.
     94func applyChannelModes(ch *upstreamChannel, modeStr string, arguments []string) (needMarshaling map[int]struct{}, err error) {
     95        needMarshaling = make(map[int]struct{}, len(arguments))
    8896        nextArgument := 0
    8997        var plusMinus byte
     98outer:
    9099        for i := 0; i < len(modeStr); i++ {
    91100                mode := modeStr[i]
     
    95104                }
    96105                if plusMinus != '+' && plusMinus != '-' {
    97                         return fmt.Errorf("malformed modestring %q: missing plus/minus", modeStr)
    98                 }
    99 
    100                 mt, ok := modeTypes[mode]
     106                        return nil, fmt.Errorf("malformed modestring %q: missing plus/minus", modeStr)
     107                }
     108
     109                for _, membership := range ch.conn.availableMemberships {
     110                        if membership.Mode == mode {
     111                                if nextArgument >= len(arguments) {
     112                                        return nil, fmt.Errorf("malformed modestring %q: missing mode argument for %c%c", modeStr, plusMinus, mode)
     113                                }
     114                                member := arguments[nextArgument]
     115                                if _, ok := ch.Members[member]; ok {
     116                                        if plusMinus == '+' {
     117                                                ch.Members[member].Add(ch.conn.availableMemberships, membership)
     118                                        } else {
     119                                                // TODO: for upstreams without multi-prefix, query the user modes again
     120                                                ch.Members[member].Remove(membership)
     121                                        }
     122                                }
     123                                needMarshaling[nextArgument] = struct{}{}
     124                                nextArgument++
     125                                continue outer
     126                        }
     127                }
     128
     129                mt, ok := ch.conn.availableChannelModes[mode]
    101130                if !ok {
    102131                        continue
     
    110139                                        argument = arguments[nextArgument]
    111140                                }
    112                                 cm[mode] = argument
     141                                if ch.modes != nil {
     142                                        ch.modes[mode] = argument
     143                                }
    113144                        } else {
    114                                 delete(cm, mode)
     145                                delete(ch.modes, mode)
    115146                        }
    116147                        nextArgument++
    117148                } else if mt == modeTypeC || mt == modeTypeD {
    118149                        if plusMinus == '+' {
    119                                 cm[mode] = ""
     150                                if ch.modes != nil {
     151                                        ch.modes[mode] = ""
     152                                }
    120153                        } else {
    121                                 delete(cm, mode)
    122                         }
    123                 }
    124         }
    125         return nil
     154                                delete(ch.modes, mode)
     155                        }
     156                }
     157        }
     158        return needMarshaling, nil
    126159}
    127160
  • trunk/upstream.go

    r292 r293  
    818818                        }
    819819
    820                         if ch.modes != nil {
    821                                 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[2:]...); err != nil {
    822                                         return err
     820                        needMarshaling, err := applyChannelModes(ch, modeStr, msg.Params[2:])
     821                        if err != nil {
     822                                return err
     823                        }
     824
     825                        uc.appendLog(ch.Name, msg)
     826                        uc.forEachDownstream(func(dc *downstreamConn) {
     827                                params := make([]string, len(msg.Params))
     828                                params[0] = dc.marshalEntity(uc.network, name)
     829                                params[1] = modeStr
     830
     831                                copy(params[2:], msg.Params[2:])
     832                                for i, modeParam := range params[2:] {
     833                                        if _, ok := needMarshaling[i]; ok {
     834                                                params[2+i] = dc.marshalEntity(uc.network, modeParam)
     835                                        }
    823836                                }
    824                         }
    825 
    826                         uc.produce(ch.Name, msg, nil)
     837
     838                                dc.SendMessage(&irc.Message{
     839                                        Prefix:  dc.marshalUserPrefix(uc.network, msg.Prefix),
     840                                        Command: "MODE",
     841                                        Params:  params,
     842                                })
     843                        })
    827844                }
    828845        case irc.RPL_UMODEIS:
     
    857874                firstMode := ch.modes == nil
    858875                ch.modes = make(map[byte]string)
    859                 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[3:]...); err != nil {
     876                if _, err := applyChannelModes(ch, modeStr, msg.Params[3:]); err != nil {
    860877                        return err
    861878                }
Note: See TracChangeset for help on using the changeset viewer.