Changeset 478 in code for trunk/user.go


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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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.