Changeset 139 in code for trunk/upstream.go


Ignore:
Timestamp:
Mar 25, 2020, 8:40:08 AM (5 years ago)
Author:
delthas
Message:

Add MODE arguments support

  • Add RPL_ISUPPORT support with CHANMODES, CHANTYPES, PREFIX parsing
  • Add support for channel mode state with mode arguments
  • Add upstream support for RPL_UMODEIS, RPL_CHANNELMODEIS
  • Request channel MODE on upstream channel JOIN
  • Use sane default channel mode and channel mode types
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/upstream.go

    r131 r139  
    2222        TopicTime time.Time
    2323        Status    channelStatus
    24         modes     modeSet
    25         Members   map[string]membership
     24        modes     channelModes
     25        Members   map[string]*membership
    2626        complete  bool
    2727}
     
    3939        serverName            string
    4040        availableUserModes    string
    41         availableChannelModes string
    42         channelModesWithParam string
     41        availableChannelModes map[byte]channelModeType
     42        availableChannelTypes string
     43        availableMemberships  []membership
    4344
    4445        registered bool
     
    4748        realname   string
    4849        closed     bool
    49         modes      modeSet
     50        modes      userModes
    5051        channels   map[string]*upstreamChannel
    5152        caps       map[string]string
     
    7374        outgoing := make(chan *irc.Message, 64)
    7475        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,
    8589        }
    8690
     
    131135
    132136func (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
    140139        }
    141140        return false
     141}
     142
     143func (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
    142150}
    143151
     
    150158                })
    151159                return nil
    152         case "MODE":
    153                 var name, modeStr string
    154                 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
    155                         return err
    156                 }
    157 
    158                 if !uc.isChannel(name) { // user mode change
    159                         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 change
    164                         // TODO: handle MODE channel mode arguments
    165                         ch, err := uc.getChannel(name)
    166                         if err != nil {
    167                                 return err
    168                         }
    169                         if err := ch.modes.Apply(modeStr); err != nil {
    170                                 return err
    171                         }
    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                 }
    181160        case "NOTICE":
    182161                uc.logger.Print(msg)
     
    347326                }
    348327        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                        }
    354389                }
    355390        case "NICK":
     
    400435                                        Name:    ch,
    401436                                        conn:    uc,
    402                                         Members: make(map[string]membership),
     437                                        Members: make(map[string]*membership),
    403438                                }
     439
     440                                uc.SendMessage(&irc.Message{
     441                                        Command: "MODE",
     442                                        Params:  []string{ch},
     443                                })
    404444                        } else {
    405445                                ch, err := uc.getChannel(ch)
     
    407447                                        return err
    408448                                }
    409                                 ch.Members[msg.Prefix.Name] = 0
     449                                ch.Members[msg.Prefix.Name] = nil
    410450                        }
    411451
     
    509549                        })
    510550                })
     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                }
    511634        case rpl_topicwhotime:
    512635                var name, who, timeStr string
     
    541664
    542665                for _, s := range strings.Split(members, " ") {
    543                         membership, nick := parseMembershipPrefix(s)
     666                        membership, nick := uc.parseMembershipPrefix(s)
    544667                        ch.Members[nick] = membership
    545668                }
     
    680803                        channelList := make([]string, len(channels))
    681804                        for i, channel := range channels {
    682                                 prefix, channel := parseMembershipPrefix(channel)
     805                                prefix, channel := uc.parseMembershipPrefix(channel)
    683806                                channel = dc.marshalChannel(uc, channel)
    684807                                channelList[i] = prefix.String() + channel
Note: See TracChangeset for help on using the changeset viewer.