Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ We're pleased to be publishing the release candidate for v2.19.0 (the official r

This release includes changes to the config file format, one of which is not backwards-compatible (see below to determine whether you are affected). It includes no changes to the database file format.

Many thanks to [@andymandias](https://github.com/andymandias), [@emersion](https://github.com/emersion), [@englut](https://github.com/englut), [@Jokler](https://github.com/Jokler), [@jwheare](https://github.com/jwheare), [@KlaasT](https://github.com/KlaasT), [@SAY-5](https://github.com/SAY-5), [@skizzerz](https://github.com/skizzerz), [@ValwareIRC](https://github.com/ValwareIRC), and [@whitequark](https://github.com/whitequark) for helpful discussions, contributing patches, reporting issues, and helping test.
Many thanks to [@andymandias](https://github.com/andymandias), [@emersion](https://github.com/emersion), [@englut](https://github.com/englut), [@Jokler](https://github.com/Jokler), [@jwheare](https://github.com/jwheare), [@KlaasT](https://github.com/KlaasT), [@luxaritas](https://github.com/luxaritas), [@SAY-5](https://github.com/SAY-5), [@skizzerz](https://github.com/skizzerz), [@ValwareIRC](https://github.com/ValwareIRC), and [@whitequark](https://github.com/whitequark) for helpful discussions, contributing patches, reporting issues, and helping test.

### Compatibility breaks
* The `extjwt` configuration block now takes `algorithm` (`hmac`, `rsa`, or `eddsa`) and either `key` or `key-file` to configure the signing key (see `default.yaml` for examples). If you are using `extjwt`, the suggested upgrade path is to add the new keys before upgrading (duplicating the legacy keys `secret` and/or `rsa-private-key-file`), upgrade Ergo, then once the new version is confirmed stable, delete the legacy keys. (#2385)
Expand All @@ -29,6 +29,7 @@ Many thanks to [@andymandias](https://github.com/andymandias), [@emersion](https
* Added support for [draft/whoami](https://github.com/ircv3/ircv3-specifications/pull/603), a proposed IRCv3 extension allowing clients to track their message source ("NUH") (#2417)
* HTTP cookies are harvested from the initial websocket handshake; if a websocket client sends `SASL EXTERNAL`, they can be passed to an `auth-script` for validation (future versions of Ergo may implement some form of native HTTP cookie authentication) (#2185, thanks [@emersion](https://github.com/emersion)!)
* Added new API endpoints: `/v1/whois` (analogue of the `WHOIS` command to get information about an active nickname), `/v1/ns/saget` (retrieves user account settings), and `/v1/ns/saset` (modifies user account settings) (#2387, #2421, thanks [@KlaasT](https://github.com/KlaasT)!)
* Added a new ChanServ setting `store-events` to optionally suppress history storage of events like `JOIN` and `QUIT` (#2424, thanks [@luxaritas](https://github.com/luxaritas)!)

### Fixed
* Fixed a race condition where an always-on client's channel join might not be persisted (#2398)
Expand Down
27 changes: 22 additions & 5 deletions irc/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
type ChannelSettings struct {
History HistoryStatus
QueryCutoff HistoryCutoff
StoreEvents StoreEvents
}

// Channel represents a channel that clients can join.
Expand Down Expand Up @@ -101,7 +102,7 @@ func (channel *Channel) initializeLists() {
}

func (channel *Channel) resizeHistory(config *Config) {
status, _, _ := channel.historyStatus(config)
status, _, _, _ := channel.historyStatus(config)
if status == HistoryEphemeral {
channel.history.Resize(config.History.ChannelLength, time.Duration(config.History.AutoresizeWindow))
} else {
Expand Down Expand Up @@ -674,9 +675,9 @@ func (channel *Channel) IsEmpty() bool {

// figure out where history is being stored: persistent, ephemeral, or neither
// target is only needed if we're doing persistent history
func (channel *Channel) historyStatus(config *Config) (status HistoryStatus, target string, restrictions HistoryCutoff) {
func (channel *Channel) historyStatus(config *Config) (status HistoryStatus, storeEvents StoreEvents, target string, restrictions HistoryCutoff) {
if !config.History.Enabled {
return HistoryDisabled, "", HistoryCutoffNone
return HistoryDisabled, StoreEventsAll, "", HistoryCutoffNone
}

channel.stateMutex.RLock()
Expand All @@ -690,7 +691,7 @@ func (channel *Channel) historyStatus(config *Config) (status HistoryStatus, tar
restrictions = config.History.Restrictions.queryCutoff
}

return channelHistoryStatus(config, registered, settings.History), target, restrictions
return channelHistoryStatus(config, registered, settings.History), settings.StoreEvents, target, restrictions
}

func (channel *Channel) joinTimeCutoff(client *Client) (present bool, cutoff time.Time) {
Expand Down Expand Up @@ -726,12 +727,28 @@ func channelHistoryStatus(config *Config, registered bool, storedStatus HistoryS
}
}

func channelEventIsStorable(itemType history.ItemType, storeEvents StoreEvents) bool {
switch storeEvents {
case StoreEventsAll:
return true
case StoreEventsNoJoins:
return !(itemType == history.Join || itemType == history.Part || itemType == history.Quit)
case StoreEventsNone:
return itemType == history.Privmsg || itemType == history.Notice || itemType == history.Tagmsg
default:
return true
}
}

func (channel *Channel) AddHistoryItem(item history.Item, account string) (err error) {
if !itemIsStorable(&item, channel.server.Config()) {
return
}

status, target, _ := channel.historyStatus(channel.server.Config())
status, storeEvents, target, _ := channel.historyStatus(channel.server.Config())
if !channelEventIsStorable(item.Type, storeEvents) {
return
}
if status == HistoryPersistent {
err = channel.server.historyDB.AddChannelItem(target, item, account)
if err != nil {
Expand Down
15 changes: 15 additions & 0 deletions irc/chanserv.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ by unprivileged users. Your options are:
channel; note that history will be effectively
unavailable to clients that are not always-on]
4. 'default' [use the server default]`,
`$bSTORE-EVENTS$b
'store-events' lets you control whether non-message events (like JOIN or
QUIT) are stored in your channel's history. Your options are:
1. 'all' [store all events, the default]
2. 'no-joins' [don't store JOIN/PART/QUIT]
3. 'none' [don't store any events, including TOPIC/KICK]`,
},
enabled: chanregEnabled,
minParams: 3,
Expand Down Expand Up @@ -813,6 +819,8 @@ func displayChannelSetting(service *ircService, settingName string, settings Cha
}
service.Notice(rb, fmt.Sprintf(client.t("The stored channel history query cutoff setting is: %s"), historyCutoffToString(settings.QueryCutoff)))
service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, the channel history query cutoff setting is: %s"), historyCutoffToString(effectiveValue)))
case "store-events":
service.Notice(rb, fmt.Sprintf(client.t("The stored channel setting for store-events is: %s"), storeEventsToString(settings.StoreEvents)))
default:
service.Notice(rb, client.t("Invalid params"))
}
Expand Down Expand Up @@ -863,6 +871,13 @@ func csSetHandler(service *ircService, server *Server, client *Client, command s
break
}
channel.SetSettings(settings)
case "store-events":
settings.StoreEvents, err = storeEventsFromString(value)
if err != nil {
err = errInvalidParams
break
}
channel.SetSettings(settings)
}

switch err {
Expand Down
34 changes: 34 additions & 0 deletions irc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,40 @@ func historyStatusToString(status HistoryStatus) string {
}
}

type StoreEvents uint

const (
StoreEventsAll StoreEvents = iota // default, store all events
StoreEventsNoJoins // don't store JOIN/QUIT/PART, do store TOPIC and NICK
StoreEventsNone // don't store any events, only HISTORY/PRIVMSG/TAGMSG
)

func storeEventsFromString(str string) (status StoreEvents, err error) {
switch strings.ToLower(str) {
case "all", "default", "on", "true", "yes":
return StoreEventsAll, nil
case "no-joins":
return StoreEventsNoJoins, nil
case "none":
return StoreEventsNone, nil
default:
return StoreEventsAll, errInvalidParams
}
}

func storeEventsToString(storeEvents StoreEvents) string {
switch storeEvents {
case StoreEventsAll:
return "all"
case StoreEventsNoJoins:
return "no-joins"
case StoreEventsNone:
return "none"
default:
return "unknown"
}
}

// XXX you must have already checked History.Enabled before calling this
func historyEnabled(serverSetting PersistentStatus, localSetting HistoryStatus) (result HistoryStatus) {
switch serverSetting {
Expand Down
2 changes: 1 addition & 1 deletion irc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3721,7 +3721,7 @@ func renameHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respo
}

config := server.Config()
status, _, _ := channel.historyStatus(config)
status, _, _, _ := channel.historyStatus(config)
if status == HistoryPersistent {
rb.Add(nil, server.name, "FAIL", "RENAME", "CANNOT_RENAME", oldName, utils.SafeErrorParam(newName), client.t("Channels with persistent history cannot be renamed"))
return false
Expand Down
4 changes: 2 additions & 2 deletions irc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1177,7 +1177,7 @@ func (server *Server) GetHistorySequence(providedChannel *Channel, client *Clien
err = errInsufficientPrivs
return
}
status, target, restriction = channel.historyStatus(config)
status, _, target, restriction = channel.historyStatus(config)
switch status {
case HistoryEphemeral:
hist = &channel.history
Expand Down Expand Up @@ -1279,7 +1279,7 @@ func (server *Server) DeleteMessage(target, msgid, accountName string) (err erro
if target[0] == '#' {
channel := server.channels.Get(target)
if channel != nil {
if status, _, _ := channel.historyStatus(config); status == HistoryEphemeral {
if status, _, _, _ := channel.historyStatus(config); status == HistoryEphemeral {
hist = &channel.history
}
}
Expand Down