Changeset 478 in code for trunk


Ignore:
Timestamp:
Mar 24, 2021, 5:15:52 PM (4 years ago)
Author:
hubert
Message:

Implement casemapping

TL;DR: supports for casemapping, now logs are saved in
casemapped/canonical/tolower form
(eg. in the #channel directory instead of #Channel... or something)

What is casemapping?

see <https://modern.ircdocs.horse/#casemapping-parameter>

Casemapping and multi-upstream

Since each upstream does not necessarily use the same casemapping, and
since casemappings cannot coexist [0],

  1. soju must also update the database accordingly to upstreams' casemapping, otherwise it will end up inconsistent,
  2. soju must "normalize" entity names and expose only one casemapping that is a subset of all supported casemappings (here, ascii).

[0] On some upstreams, "emersion[m]" and "emersion{m}" refer to the same
user (upstreams that advertise rfc1459 for example), while on others
(upstreams that advertise ascii) they don't.

Once upstream's casemapping is known (default to rfc1459), entity names
in map keys are made into casemapped form, for upstreamConn,
upstreamChannel and network.

downstreamConn advertises "CASEMAPPING=ascii", and always casemap map
keys with ascii.

Some functions require the caller to casemap their argument (to avoid
needless calls to casemapping functions).

Message forwarding and casemapping

downstream message handling (joins and parts basically):
When relaying entity names from downstreams to upstreams, soju uses the
upstream casemapping, in order to not get in the way of the user. This
does not brings any issue, as long as soju replies with the ascii
casemapping in mind (solves point 1.).

marshalEntity/marshalUserPrefix:
When relaying entity names from upstreams with non-ascii casemappings,
soju *partially* casemap them: it only change the case of characters
which are not ascii letters. ASCII case is thus kept intact, while
special symbols like []{} are the same every time soju sends them to
downstreams (solves point 2.).

Casemapping changes

Casemapping changes are not fully supported by this patch and will
result in loss of history. This is a limitation of the protocol and
should be solved by the RENAME spec.

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/bridge.go

    r405 r478  
    5555
    5656        var buf strings.Builder
    57         for nick, memberships := range ch.Members {
     57        for _, entry := range ch.Members.innerMap {
     58                nick := entry.originalKey
     59                memberships := entry.value.(*memberships)
    5860                s := memberships.Format(dc) + dc.marshalEntity(ch.conn.network, nick)
    5961
  • trunk/downstream.go

    r464 r478  
    117117        user        *user
    118118        nick        string
     119        nickCM      string
    119120        rawUsername string
    120121        networkName string
     
    193194        // TODO: this doesn't account for nick changes
    194195        if net.conn != nil {
    195                 return nick == net.conn.nick
     196                return net.casemap(nick) == net.conn.nickCM
    196197        }
    197198        // We're not currently connected to the upstream connection, so we don't
     
    199200        // configured nickname and hope it was the one being used when we were
    200201        // connected.
    201         return nick == net.Nick
     202        return net.casemap(nick) == net.casemap(net.Nick)
    202203}
    203204
     
    211212                return dc.nick
    212213        }
     214        name = partialCasemap(net.casemap, name)
    213215        if dc.network != nil {
    214216                if dc.network != net {
     
    224226                return dc.prefix()
    225227        }
     228        prefix.Name = partialCasemap(net.casemap, prefix.Name)
    226229        if dc.network != nil {
    227230                if dc.network != net {
     
    359362        }
    360363
    361         delivered, ok := network.delivered[entity]
    362         if !ok {
     364        delivered := network.delivered.Value(entity)
     365        if delivered == nil {
    363366                return
    364367        }
     
    446449                        }}
    447450                }
    448                 if nick == serviceNick {
     451                nickCM := casemapASCII(nick)
     452                if nickCM == serviceNickCM {
    449453                        return ircError{&irc.Message{
    450454                                Command: irc.ERR_NICKNAMEINUSE,
     
    453457                }
    454458                dc.nick = nick
     459                dc.nickCM = nickCM
    455460        case "USER":
    456461                if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
     
    767772                })
    768773                dc.nick = uc.nick
     774                dc.nickCM = casemapASCII(dc.nick)
    769775        }
    770776}
     
    912918        isupport := []string{
    913919                fmt.Sprintf("CHATHISTORY=%v", dc.srv.HistoryLimit),
     920                "CASEMAPPING=ascii",
    914921        }
    915922
     
    961968
    962969        dc.forEachUpstream(func(uc *upstreamConn) {
    963                 for _, ch := range uc.channels {
     970                for _, entry := range uc.channels.innerMap {
     971                        ch := entry.value.(*upstreamChannel)
    964972                        if !ch.complete {
    965973                                continue
    966974                        }
    967                         if record, ok := uc.network.channels[ch.Name]; ok && record.Detached {
     975                        record := uc.network.channels.Value(ch.Name)
     976                        if record != nil && record.Detached {
    968977                                continue
    969978                        }
     
    988997
    989998                // Fast-forward history to last message
    990                 for target, delivered := range net.delivered {
    991                         if ch, ok := net.channels[target]; ok && ch.Detached {
     999                for target, entry := range net.delivered.innerMap {
     1000                        delivered := entry.value.(map[string]string)
     1001                        ch := net.channels.Value(target)
     1002                        if ch != nil && ch.Detached {
    9921003                                continue
    9931004                        }
     
    10201031
    10211032func (dc *downstreamConn) sendNetworkBacklog(net *network) {
    1022         for target := range net.delivered {
     1033        for _, entry := range net.delivered.innerMap {
     1034                target := entry.originalKey
    10231035                dc.sendTargetBacklog(net, target)
    10241036        }
     
    10291041                return
    10301042        }
    1031         if ch, ok := net.channels[target]; ok && ch.Detached {
     1043        if ch := net.channels.Value(target); ch != nil && ch.Detached {
    10321044                return
    10331045        }
    1034         delivered, ok := net.delivered[target]
    1035         if !ok {
     1046        delivered := net.delivered.Value(target)
     1047        if delivered == nil {
    10361048                return
    10371049        }
     
    11591171                        }}
    11601172                }
    1161                 if nick == serviceNick {
     1173                if casemapASCII(nick) == serviceNickCM {
    11621174                        return ircError{&irc.Message{
    11631175                                Command: irc.ERR_NICKNAMEINUSE,
     
    11951207                        })
    11961208                        dc.nick = nick
     1209                        dc.nickCM = casemapASCII(dc.nick)
    11971210                }
    11981211        case "JOIN":
     
    12271240                        })
    12281241
    1229                         var ch *Channel
    1230                         var ok bool
    1231                         if ch, ok = uc.network.channels[upstreamName]; ok {
     1242                        ch := uc.network.channels.Value(upstreamName)
     1243                        if ch != nil {
    12321244                                // Don't clear the channel key if there's one set
    12331245                                // TODO: add a way to unset the channel key
     
    12411253                                        Key:  key,
    12421254                                }
    1243                                 uc.network.channels[upstreamName] = ch
     1255                                uc.network.channels.SetValue(upstreamName, ch)
    12441256                        }
    12451257                        if err := dc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
     
    12651277
    12661278                        if strings.EqualFold(reason, "detach") {
    1267                                 var ch *Channel
    1268                                 var ok bool
    1269                                 if ch, ok = uc.network.channels[upstreamName]; ok {
     1279                                ch := uc.network.channels.Value(upstreamName)
     1280                                if ch != nil {
    12701281                                        uc.network.detach(ch)
    12711282                                } else {
     
    12741285                                                Detached: true,
    12751286                                        }
    1276                                         uc.network.channels[upstreamName] = ch
     1287                                        uc.network.channels.SetValue(upstreamName, ch)
    12771288                                }
    12781289                                if err := dc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
     
    13611372                }
    13621373
    1363                 if name == dc.nick {
     1374                if casemapASCII(name) == dc.nickCM {
    13641375                        if modeStr != "" {
    13651376                                dc.forEachUpstream(func(uc *upstreamConn) {
     
    13991410                        })
    14001411                } else {
    1401                         ch, ok := uc.channels[upstreamName]
    1402                         if !ok {
     1412                        ch := uc.channels.Value(upstreamName)
     1413                        if ch == nil {
    14031414                                return ircError{&irc.Message{
    14041415                                        Command: irc.ERR_NOSUCHCHANNEL,
     
    14361447                }
    14371448
    1438                 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
     1449                uc, upstreamName, err := dc.unmarshalEntity(channel)
    14391450                if err != nil {
    14401451                        return err
     
    14451456                        uc.SendMessageLabeled(dc.id, &irc.Message{
    14461457                                Command: "TOPIC",
    1447                                 Params:  []string{upstreamChannel, topic},
     1458                                Params:  []string{upstreamName, topic},
    14481459                        })
    14491460                } else { // getting topic
    1450                         ch, ok := uc.channels[upstreamChannel]
    1451                         if !ok {
     1461                        ch := uc.channels.Value(upstreamName)
     1462                        if ch == nil {
    14521463                                return ircError{&irc.Message{
    14531464                                        Command: irc.ERR_NOSUCHCHANNEL,
    1454                                         Params:  []string{dc.nick, upstreamChannel, "No such channel"},
     1465                                        Params:  []string{dc.nick, upstreamName, "No such channel"},
    14551466                                }}
    14561467                        }
     
    15141525                channels := strings.Split(msg.Params[0], ",")
    15151526                for _, channel := range channels {
    1516                         uc, upstreamChannel, err := dc.unmarshalEntity(channel)
     1527                        uc, upstreamName, err := dc.unmarshalEntity(channel)
    15171528                        if err != nil {
    15181529                                return err
    15191530                        }
    15201531
    1521                         ch, ok := uc.channels[upstreamChannel]
    1522                         if ok {
     1532                        ch := uc.channels.Value(upstreamName)
     1533                        if ch != nil {
    15231534                                sendNames(dc, ch)
    15241535                        } else {
     
    15261537                                uc.SendMessageLabeled(dc.id, &irc.Message{
    15271538                                        Command: "NAMES",
    1528                                         Params:  []string{upstreamChannel},
     1539                                        Params:  []string{upstreamName},
    15291540                                })
    15301541                        }
     
    15431554                // TODO: support WHO masks
    15441555                entity := msg.Params[0]
    1545 
    1546                 if entity == dc.nick {
     1556                entityCM := casemapASCII(entity)
     1557
     1558                if entityCM == dc.nickCM {
    15471559                        // TODO: support AWAY (H/G) in self WHO reply
    15481560                        dc.SendMessage(&irc.Message{
     
    15581570                        return nil
    15591571                }
    1560                 if entity == serviceNick {
     1572                if entityCM == serviceNickCM {
    15611573                        dc.SendMessage(&irc.Message{
    15621574                                Prefix:  dc.srv.prefix(),
     
    16091621                }
    16101622
    1611                 if mask == dc.nick {
     1623                if casemapASCII(mask) == dc.nickCM {
    16121624                        dc.SendMessage(&irc.Message{
    16131625                                Prefix:  dc.srv.prefix(),
  • trunk/irc.go

    r463 r478  
    122122                                }
    123123                                member := arguments[nextArgument]
    124                                 if _, ok := ch.Members[member]; ok {
     124                                m := ch.Members.Value(member)
     125                                if m != nil {
    125126                                        if plusMinus == '+' {
    126                                                 ch.Members[member].Add(ch.conn.availableMemberships, membership)
     127                                                m.Add(ch.conn.availableMemberships, membership)
    127128                                        } else {
    128129                                                // TODO: for upstreams without multi-prefix, query the user modes again
    129                                                 ch.Members[member].Remove(membership)
     130                                                m.Remove(membership)
    130131                                        }
    131132                                }
     
    419420        return cmd, params, true
    420421}
     422
     423type casemapping func(string) string
     424
     425func casemapNone(name string) string {
     426        return name
     427}
     428
     429// CasemapASCII of name is the canonical representation of name according to the
     430// ascii casemapping.
     431func casemapASCII(name string) string {
     432        var sb strings.Builder
     433        sb.Grow(len(name))
     434        for _, r := range name {
     435                if 'A' <= r && r <= 'Z' {
     436                        r += 'a' - 'A'
     437                }
     438                sb.WriteRune(r)
     439        }
     440        return sb.String()
     441}
     442
     443// casemapRFC1459 of name is the canonical representation of name according to the
     444// rfc1459 casemapping.
     445func casemapRFC1459(name string) string {
     446        var sb strings.Builder
     447        sb.Grow(len(name))
     448        for _, r := range name {
     449                if 'A' <= r && r <= 'Z' {
     450                        r += 'a' - 'A'
     451                } else if r == '{' {
     452                        r = '['
     453                } else if r == '}' {
     454                        r = ']'
     455                } else if r == '\\' {
     456                        r = '|'
     457                } else if r == '~' {
     458                        r = '^'
     459                }
     460                sb.WriteRune(r)
     461        }
     462        return sb.String()
     463}
     464
     465// casemapRFC1459Strict of name is the canonical representation of name
     466// according to the rfc1459-strict casemapping.
     467func casemapRFC1459Strict(name string) string {
     468        var sb strings.Builder
     469        sb.Grow(len(name))
     470        for _, r := range name {
     471                if 'A' <= r && r <= 'Z' {
     472                        r += 'a' - 'A'
     473                } else if r == '{' {
     474                        r = '['
     475                } else if r == '}' {
     476                        r = ']'
     477                } else if r == '\\' {
     478                        r = '|'
     479                }
     480                sb.WriteRune(r)
     481        }
     482        return sb.String()
     483}
     484
     485func parseCasemappingToken(tokenValue string) (casemap casemapping, ok bool) {
     486        switch tokenValue {
     487        case "ascii":
     488                casemap = casemapASCII
     489        case "rfc1459":
     490                casemap = casemapRFC1459
     491        case "rfc1459-strict":
     492                casemap = casemapRFC1459Strict
     493        default:
     494                return nil, false
     495        }
     496        return casemap, true
     497}
     498
     499func partialCasemap(higher casemapping, name string) string {
     500        nameFullyCM := higher(name)
     501        var sb strings.Builder
     502        sb.Grow(len(name))
     503        for i, r := range nameFullyCM {
     504                if 'a' <= r && r <= 'z' {
     505                        r = rune(name[i])
     506                }
     507                sb.WriteRune(r)
     508        }
     509        return sb.String()
     510}
     511
     512type casemapMap struct {
     513        innerMap map[string]casemapEntry
     514        casemap  casemapping
     515}
     516
     517type casemapEntry struct {
     518        originalKey string
     519        value       interface{}
     520}
     521
     522func newCasemapMap(size int) casemapMap {
     523        return casemapMap{
     524                innerMap: make(map[string]casemapEntry, size),
     525                casemap:  casemapNone,
     526        }
     527}
     528
     529func (cm *casemapMap) OriginalKey(name string) (key string, ok bool) {
     530        entry, ok := cm.innerMap[cm.casemap(name)]
     531        if !ok {
     532                return "", false
     533        }
     534        return entry.originalKey, true
     535}
     536
     537func (cm *casemapMap) Has(name string) bool {
     538        _, ok := cm.innerMap[cm.casemap(name)]
     539        return ok
     540}
     541
     542func (cm *casemapMap) Len() int {
     543        return len(cm.innerMap)
     544}
     545
     546func (cm *casemapMap) SetValue(name string, value interface{}) {
     547        nameCM := cm.casemap(name)
     548        entry, ok := cm.innerMap[nameCM]
     549        if !ok {
     550                cm.innerMap[nameCM] = casemapEntry{
     551                        originalKey: name,
     552                        value:       value,
     553                }
     554                return
     555        }
     556        entry.value = value
     557        cm.innerMap[nameCM] = entry
     558}
     559
     560func (cm *casemapMap) Delete(name string) {
     561        delete(cm.innerMap, cm.casemap(name))
     562}
     563
     564func (cm *casemapMap) SetCasemapping(newCasemap casemapping) {
     565        cm.casemap = newCasemap
     566        newInnerMap := make(map[string]casemapEntry, len(cm.innerMap))
     567        for _, entry := range cm.innerMap {
     568                newInnerMap[cm.casemap(entry.originalKey)] = entry
     569        }
     570        cm.innerMap = newInnerMap
     571}
     572
     573type upstreamChannelCasemapMap struct{ casemapMap }
     574
     575func (cm *upstreamChannelCasemapMap) Value(name string) *upstreamChannel {
     576        entry, ok := cm.innerMap[cm.casemap(name)]
     577        if !ok {
     578                return nil
     579        }
     580        return entry.value.(*upstreamChannel)
     581}
     582
     583type channelCasemapMap struct{ casemapMap }
     584
     585func (cm *channelCasemapMap) Value(name string) *Channel {
     586        entry, ok := cm.innerMap[cm.casemap(name)]
     587        if !ok {
     588                return nil
     589        }
     590        return entry.value.(*Channel)
     591}
     592
     593type membershipsCasemapMap struct{ casemapMap }
     594
     595func (cm *membershipsCasemapMap) Value(name string) *memberships {
     596        entry, ok := cm.innerMap[cm.casemap(name)]
     597        if !ok {
     598                return nil
     599        }
     600        return entry.value.(*memberships)
     601}
     602
     603type mapStringStringCasemapMap struct{ casemapMap }
     604
     605func (cm *mapStringStringCasemapMap) Value(name string) map[string]string {
     606        entry, ok := cm.innerMap[cm.casemap(name)]
     607        if !ok {
     608                return nil
     609        }
     610        return entry.value.(map[string]string)
     611}
  • trunk/service.go

    r436 r478  
    2828
    2929const serviceNick = "BouncerServ"
     30const serviceNickCM = "bouncerserv"
    3031const serviceRealname = "soju bouncer service"
    3132
     
    409410                                statuses = append(statuses, "connected")
    410411                        }
    411                         details = fmt.Sprintf("%v channels", len(uc.channels))
     412                        details = fmt.Sprintf("%v channels", uc.channels.Len())
    412413                } else {
    413414                        statuses = append(statuses, "disconnected")
     
    769770        }
    770771
    771         ch, ok := uc.network.channels[upstreamName]
    772         if !ok {
     772        ch := uc.network.channels.Value(upstreamName)
     773        if ch == nil {
    773774                return fmt.Errorf("unknown channel %q", name)
    774775        }
  • trunk/upstream.go

    r469 r478  
    4949        modes        channelModes
    5050        creationTime string
    51         Members      map[string]*memberships
     51        Members      membershipsCasemapMap
    5252        complete     bool
    5353        detachTimer  *time.Timer
     
    8787        registered    bool
    8888        nick          string
     89        nickCM        string
    8990        username      string
    9091        realname      string
    9192        modes         userModes
    92         channels      map[string]*upstreamChannel
     93        channels      upstreamChannelCasemapMap
    9394        supportedCaps map[string]string
    9495        caps          map[string]bool
     
    99100        saslClient  sasl.Client
    100101        saslStarted bool
     102
     103        casemapIsSet bool
    101104
    102105        // set of LIST commands in progress, per downstream
     
    187190                network:                  network,
    188191                user:                     network.user,
    189                 channels:                 make(map[string]*upstreamChannel),
     192                channels:                 upstreamChannelCasemapMap{newCasemapMap(0)},
    190193                supportedCaps:            make(map[string]string),
    191194                caps:                     make(map[string]bool),
     
    214217
    215218func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
    216         ch, ok := uc.channels[name]
    217         if !ok {
     219        ch := uc.channels.Value(name)
     220        if ch == nil {
    218221                return nil, fmt.Errorf("unknown channel %q", name)
    219222        }
     
    223226func (uc *upstreamConn) isChannel(entity string) bool {
    224227        return strings.ContainsRune(uc.availableChannelTypes, rune(entity[0]))
     228}
     229
     230func (uc *upstreamConn) isOurNick(nick string) bool {
     231        return uc.nickCM == uc.network.casemap(nick)
    225232}
    226233
     
    414421                } else { // regular user message
    415422                        target := entity
    416                         if target == uc.nick {
     423                        if uc.isOurNick(target) {
    417424                                target = msg.Prefix.Name
    418425                        }
    419426
    420                         if ch, ok := uc.network.channels[target]; ok {
     427                        ch := uc.network.channels.Value(target)
     428                        if ch != nil {
    421429                                if ch.Detached {
    422430                                        uc.handleDetachedMessage(msg.Prefix.Name, text, ch)
     
    591599                uc.logger.Printf("connection registered")
    592600
    593                 if len(uc.network.channels) > 0 {
     601                if uc.network.channels.Len() > 0 {
    594602                        var channels, keys []string
    595                         for _, ch := range uc.network.channels {
     603                        for _, entry := range uc.network.channels.innerMap {
     604                                ch := entry.value.(*Channel)
    596605                                channels = append(channels, ch.Name)
    597606                                keys = append(keys, ch.Key)
     
    635644                        var err error
    636645                        switch parameter {
     646                        case "CASEMAPPING":
     647                                casemap, ok := parseCasemappingToken(value)
     648                                if !ok {
     649                                        casemap = casemapRFC1459
     650                                }
     651                                uc.network.updateCasemapping(casemap)
     652                                uc.nickCM = uc.network.casemap(uc.nick)
     653                                uc.casemapIsSet = true
    637654                        case "CHANMODES":
    638655                                if !negate {
     
    672689                        }
    673690                })
     691        case irc.ERR_NOMOTD, irc.RPL_ENDOFMOTD:
     692                if !uc.casemapIsSet {
     693                        // upstream did not send any CASEMAPPING token, thus
     694                        // we assume it implements the old RFCs with rfc1459.
     695                        uc.casemapIsSet = true
     696                        uc.network.updateCasemapping(casemapRFC1459)
     697                        uc.nickCM = uc.network.casemap(uc.nick)
     698                }
    674699        case "BATCH":
    675700                var tag string
     
    717742
    718743                me := false
    719                 if msg.Prefix.Name == uc.nick {
     744                if uc.isOurNick(msg.Prefix.Name) {
    720745                        uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
    721746                        me = true
    722747                        uc.nick = newNick
    723                 }
    724 
    725                 for _, ch := range uc.channels {
    726                         if memberships, ok := ch.Members[msg.Prefix.Name]; ok {
    727                                 delete(ch.Members, msg.Prefix.Name)
    728                                 ch.Members[newNick] = memberships
     748                        uc.nickCM = uc.network.casemap(uc.nick)
     749                }
     750
     751                for _, entry := range uc.channels.innerMap {
     752                        ch := entry.value.(*upstreamChannel)
     753                        memberships := ch.Members.Value(msg.Prefix.Name)
     754                        if memberships != nil {
     755                                ch.Members.Delete(msg.Prefix.Name)
     756                                ch.Members.SetValue(newNick, memberships)
    729757                                uc.appendLog(ch.Name, msg)
    730758                        }
     
    751779
    752780                for _, ch := range strings.Split(channels, ",") {
    753                         if msg.Prefix.Name == uc.nick {
     781                        if uc.isOurNick(msg.Prefix.Name) {
    754782                                uc.logger.Printf("joined channel %q", ch)
    755                                 uc.channels[ch] = &upstreamChannel{
     783                                members := membershipsCasemapMap{newCasemapMap(0)}
     784                                members.casemap = uc.network.casemap
     785                                uc.channels.SetValue(ch, &upstreamChannel{
    756786                                        Name:    ch,
    757787                                        conn:    uc,
    758                                         Members: make(map[string]*memberships),
    759                                 }
     788                                        Members: members,
     789                                })
    760790                                uc.updateChannelAutoDetach(ch)
    761791
     
    769799                                        return err
    770800                                }
    771                                 ch.Members[msg.Prefix.Name] = &memberships{}
     801                                ch.Members.SetValue(msg.Prefix.Name, &memberships{})
    772802                        }
    773803
     
    787817
    788818                for _, ch := range strings.Split(channels, ",") {
    789                         if msg.Prefix.Name == uc.nick {
     819                        if uc.isOurNick(msg.Prefix.Name) {
    790820                                uc.logger.Printf("parted channel %q", ch)
    791                                 if uch, ok := uc.channels[ch]; ok {
    792                                         delete(uc.channels, ch)
     821                                uch := uc.channels.Value(ch)
     822                                if uch != nil {
     823                                        uc.channels.Delete(ch)
    793824                                        uch.updateAutoDetach(0)
    794825                                }
     
    798829                                        return err
    799830                                }
    800                                 delete(ch.Members, msg.Prefix.Name)
     831                                ch.Members.Delete(msg.Prefix.Name)
    801832                        }
    802833
     
    815846                }
    816847
    817                 if user == uc.nick {
     848                if uc.isOurNick(user) {
    818849                        uc.logger.Printf("kicked from channel %q by %s", channel, msg.Prefix.Name)
    819                         delete(uc.channels, channel)
     850                        uc.channels.Delete(channel)
    820851                } else {
    821852                        ch, err := uc.getChannel(channel)
     
    823854                                return err
    824855                        }
    825                         delete(ch.Members, user)
     856                        ch.Members.Delete(user)
    826857                }
    827858
     
    832863                }
    833864
    834                 if msg.Prefix.Name == uc.nick {
     865                if uc.isOurNick(msg.Prefix.Name) {
    835866                        uc.logger.Printf("quit")
    836867                }
    837868
    838                 for _, ch := range uc.channels {
    839                         if _, ok := ch.Members[msg.Prefix.Name]; ok {
    840                                 delete(ch.Members, msg.Prefix.Name)
     869                for _, entry := range uc.channels.innerMap {
     870                        ch := entry.value.(*upstreamChannel)
     871                        if ch.Members.Has(msg.Prefix.Name) {
     872                                ch.Members.Delete(msg.Prefix.Name)
    841873
    842874                                uc.appendLog(ch.Name, msg)
     
    909941                        uc.appendLog(ch.Name, msg)
    910942
    911                         if ch, ok := uc.network.channels[name]; !ok || !ch.Detached {
     943                        c := uc.network.channels.Value(name)
     944                        if c == nil || !c.Detached {
    912945                                uc.forEachDownstream(func(dc *downstreamConn) {
    913946                                        params := make([]string, len(msg.Params))
     
    965998                }
    966999                if firstMode {
    967                         if c, ok := uc.network.channels[channel]; !ok || !c.Detached {
     1000                        c := uc.network.channels.Value(channel)
     1001                        if c == nil || !c.Detached {
    9681002                                modeStr, modeParams := ch.modes.Format()
    9691003
     
    10621096                }
    10631097
    1064                 ch, ok := uc.channels[name]
    1065                 if !ok {
     1098                ch := uc.channels.Value(name)
     1099                if ch == nil {
    10661100                        // NAMES on a channel we have not joined, forward to downstream
    10671101                        uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
     
    10911125                for _, s := range splitSpace(members) {
    10921126                        memberships, nick := uc.parseMembershipPrefix(s)
    1093                         ch.Members[nick] = memberships
     1127                        ch.Members.SetValue(nick, memberships)
    10941128                }
    10951129        case irc.RPL_ENDOFNAMES:
     
    10991133                }
    11001134
    1101                 ch, ok := uc.channels[name]
    1102                 if !ok {
     1135                ch := uc.channels.Value(name)
     1136                if ch == nil {
    11031137                        // NAMES on a channel we have not joined, forward to downstream
    11041138                        uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
     
    11191153                ch.complete = true
    11201154
    1121                 if c, ok := uc.network.channels[name]; !ok || !c.Detached {
     1155                c := uc.network.channels.Value(name)
     1156                if c == nil || !c.Detached {
    11221157                        uc.forEachDownstream(func(dc *downstreamConn) {
    11231158                                forwardChannel(dc, ch)
     
    12731308                }
    12741309
    1275                 weAreInvited := nick == uc.nick
     1310                weAreInvited := uc.isOurNick(nick)
    12761311
    12771312                uc.forEachDownstream(func(dc *downstreamConn) {
     
    13961431        case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
    13971432                // Ignore
    1398         case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
     1433        case irc.RPL_MOTDSTART, irc.RPL_MOTD:
    13991434                // Ignore
    14001435        case irc.RPL_LISTSTART:
     
    16021637func (uc *upstreamConn) register() {
    16031638        uc.nick = uc.network.Nick
     1639        uc.nickCM = uc.network.casemap(uc.nick)
    16041640        uc.username = uc.network.GetUsername()
    16051641        uc.realname = uc.network.GetRealname()
     
    17061742
    17071743        detached := false
    1708         if ch, ok := uc.network.channels[entity]; ok {
     1744        if ch := uc.network.channels.Value(entity); ch != nil {
    17091745                detached = ch.Detached
    17101746        }
    17111747
    1712         delivered, ok := uc.network.delivered[entity]
    1713         if !ok {
    1714                 lastID, err := uc.user.msgStore.LastMsgID(uc.network, entity, time.Now())
     1748        delivered := uc.network.delivered.Value(entity)
     1749        entityCM := uc.network.casemap(entity)
     1750        if delivered == nil {
     1751                lastID, err := uc.user.msgStore.LastMsgID(uc.network, entityCM, time.Now())
    17151752                if err != nil {
    17161753                        uc.logger.Printf("failed to log message: failed to get last message ID: %v", err)
     
    17191756
    17201757                delivered = make(map[string]string)
    1721                 uc.network.delivered[entity] = delivered
     1758                uc.network.delivered.SetValue(entity, delivered)
    17221759
    17231760                for clientName, _ := range uc.network.offlineClients {
     
    17341771        }
    17351772
    1736         msgID, err := uc.user.msgStore.Append(uc.network, entity, msg)
     1773        msgID, err := uc.user.msgStore.Append(uc.network, entityCM, msg)
    17371774        if err != nil {
    17381775                uc.logger.Printf("failed to log message: %v", err)
     
    17551792
    17561793        // Don't forward messages if it's a detached channel
    1757         if ch, ok := uc.network.channels[target]; ok && ch.Detached {
     1794        ch := uc.network.channels.Value(target)
     1795        if ch != nil && ch.Detached {
    17581796                return
    17591797        }
     
    17901828
    17911829func (uc *upstreamConn) updateChannelAutoDetach(name string) {
    1792         if uch, ok := uc.channels[name]; ok {
    1793                 if ch, ok := uc.network.channels[name]; ok && !ch.Detached {
    1794                         uch.updateAutoDetach(ch.DetachAfter)
    1795                 }
    1796         }
    1797 }
     1830        uch := uc.channels.Value(name)
     1831        if uch == nil {
     1832                return
     1833        }
     1834        ch := uc.network.channels.Value(name)
     1835        if ch == nil || ch.Detached {
     1836                return
     1837        }
     1838        uch.updateAutoDetach(ch.DetachAfter)
     1839}
  • trunk/user.go

    r467 r478  
    6262
    6363        conn           *upstreamConn
    64         channels       map[string]*Channel
    65         delivered      map[string]map[string]string // entity -> client name -> msg ID
    66         offlineClients map[string]struct{}          // indexed by client name
     64        channels       channelCasemapMap
     65        delivered      mapStringStringCasemapMap // entity -> client name -> msg ID
     66        offlineClients map[string]struct{}       // indexed by client name
    6767        lastError      error
     68        casemap        casemapping
    6869}
    6970
    7071func newNetwork(user *user, record *Network, channels []Channel) *network {
    71         m := make(map[string]*Channel, len(channels))
     72        m := channelCasemapMap{newCasemapMap(0)}
    7273        for _, ch := range channels {
    7374                ch := ch
    74                 m[ch.Name] = &ch
     75                m.SetValue(ch.Name, &ch)
    7576        }
    7677
     
    8081                stopped:        make(chan struct{}),
    8182                channels:       m,
    82                 delivered:      make(map[string]map[string]string),
     83                delivered:      mapStringStringCasemapMap{newCasemapMap(0)},
    8384                offlineClients: make(map[string]struct{}),
     85                casemap:        casemapRFC1459,
    8486        }
    8587}
     
    186188
    187189        if net.conn != nil {
    188                 if uch, ok := net.conn.channels[ch.Name]; ok {
     190                uch := net.conn.channels.Value(ch.Name)
     191                if uch != nil {
    189192                        uch.updateAutoDetach(0)
    190193                }
     
    211214        var uch *upstreamChannel
    212215        if net.conn != nil {
    213                 uch = net.conn.channels[ch.Name]
     216                uch = net.conn.channels.Value(ch.Name)
    214217
    215218                net.conn.updateChannelAutoDetach(ch.Name)
     
    232235
    233236func (net *network) deleteChannel(name string) error {
    234         ch, ok := net.channels[name]
    235         if !ok {
     237        ch := net.channels.Value(name)
     238        if ch == nil {
    236239                return fmt.Errorf("unknown channel %q", name)
    237240        }
    238241        if net.conn != nil {
    239                 if uch, ok := net.conn.channels[ch.Name]; ok {
     242                uch := net.conn.channels.Value(ch.Name)
     243                if uch != nil {
    240244                        uch.updateAutoDetach(0)
    241245                }
     
    245249                return err
    246250        }
    247         delete(net.channels, name)
     251        net.channels.Delete(name)
    248252        return nil
     253}
     254
     255func (net *network) updateCasemapping(newCasemap casemapping) {
     256        net.casemap = newCasemap
     257        net.channels.SetCasemapping(newCasemap)
     258        net.delivered.SetCasemapping(newCasemap)
     259        if net.conn != nil {
     260                net.conn.channels.SetCasemapping(newCasemap)
     261                for _, entry := range net.conn.channels.innerMap {
     262                        uch := entry.value.(*upstreamChannel)
     263                        uch.Members.SetCasemapping(newCasemap)
     264                }
     265        }
    249266}
    250267
     
    411428                case eventChannelDetach:
    412429                        uc, name := e.uc, e.name
    413                         c, ok := uc.network.channels[name]
    414                         if !ok || c.Detached {
     430                        c := uc.network.channels.Value(name)
     431                        if c == nil || c.Detached {
    415432                                continue
    416433                        }
     
    500517        uc.endPendingLISTs(true)
    501518
    502         for _, uch := range uc.channels {
     519        for _, entry := range uc.channels.innerMap {
     520                uch := entry.value.(*upstreamChannel)
    503521                uch.updateAutoDetach(0)
    504522        }
     
    571589        // Most network changes require us to re-connect to the upstream server
    572590
    573         channels := make([]Channel, 0, len(network.channels))
    574         for _, ch := range network.channels {
     591        channels := make([]Channel, 0, network.channels.Len())
     592        for _, entry := range network.channels.innerMap {
     593                ch := entry.value.(*Channel)
    575594                channels = append(channels, *ch)
    576595        }
Note: See TracChangeset for help on using the changeset viewer.