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
6 changes: 6 additions & 0 deletions gencapdefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@
url="https://github.com/ircv3/ircv3-specifications/pull/489",
standard="draft IRCv3",
),
CapDef(
identifier="Persistence",
name="draft/persistence",
url="https://github.com/ircv3/ircv3-specifications/pull/503",
standard="proposed IRCv3",
),
]

def validate_defs():
Expand Down
7 changes: 6 additions & 1 deletion irc/caps/defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package caps

const (
// number of recognized capabilities:
numCapabs = 29
numCapabs = 30
// length of the uint64 array that represents the bitset:
bitsetLen = 1
)
Expand Down Expand Up @@ -61,6 +61,10 @@ const (
// https://github.com/ircv3/ircv3-specifications/pull/398
Multiline Capability = iota

// Persistence is the proposed IRCv3 capability named "draft/persistence":
// https://github.com/ircv3/ircv3-specifications/pull/503
Persistence Capability = iota

// ReadMarker is the draft IRCv3 capability named "draft/read-marker":
// https://github.com/ircv3/ircv3-specifications/pull/489
ReadMarker Capability = iota
Expand Down Expand Up @@ -145,6 +149,7 @@ var (
"draft/event-playback",
"draft/languages",
"draft/multiline",
"draft/persistence",
"draft/read-marker",
"draft/relaymsg",
"echo-message",
Expand Down
4 changes: 4 additions & 0 deletions irc/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ func init() {
usablePreReg: true,
minParams: 1,
},
"PERSISTENCE": {
handler: persistenceHandler,
minParams: 1,
},
"PING": {
handler: pingHandler,
usablePreReg: true,
Expand Down
85 changes: 85 additions & 0 deletions irc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2599,6 +2599,91 @@ func passHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons
return false
}

// PERSISTENCE <subcommand> [params...]
func persistenceHandler(server *Server, client *Client, msg ircmsg.Message, rb *ResponseBuffer) bool {
account := client.Account()
if account == "" {
rb.Add(nil, server.name, "FAIL", "PERSISTENCE", "ACCOUNT_REQUIRED", client.t("You're not logged into an account"))
return false
}

switch strings.ToUpper(msg.Params[0]) {
case "GET":
reportPersistenceStatus(client, rb, false)
case "SET":
if len(msg.Params) == 1 {
goto fail
}
var desiredSetting PersistentStatus
switch strings.ToUpper(msg.Params[1]) {
case "DEFAULT":
desiredSetting = PersistentUnspecified
case "OFF":
desiredSetting = PersistentDisabled
case "ON":
desiredSetting = PersistentMandatory
default:
goto fail
}

broadcast := false
_, err := server.accounts.ModifyAccountSettings(account,
func(input AccountSettings) (output AccountSettings, err error) {
output = input
output.AlwaysOn = desiredSetting
broadcast = output.AlwaysOn != input.AlwaysOn
return
})
if err != nil {
server.logger.Error("internal", "couldn't modify persistence setting", err.Error())
rb.Add(nil, server.name, "FAIL", "PERSISTENCE", "UNKNOWN_ERROR", client.t("An error occurred"))
return false
}

reportPersistenceStatus(client, rb, broadcast)

default:
goto fail
}

return false

fail:
rb.Add(nil, server.name, "FAIL", "PERSISTENCE", "INVALID_PARAMS", client.t("Invalid parameters"))
return false
}

func reportPersistenceStatus(client *Client, rb *ResponseBuffer, broadcast bool) {
settings := client.AccountSettings()
serverSetting := client.server.Config().Accounts.Multiclient.AlwaysOn
effectiveSetting := persistenceEnabled(serverSetting, settings.AlwaysOn)
toString := func(setting PersistentStatus) string {
switch setting {
case PersistentUnspecified:
return "DEFAULT"
case PersistentDisabled:
return "OFF"
case PersistentMandatory:
return "ON"
default:
return "*" // impossible
}
}
storedSettingStr := toString(settings.AlwaysOn)
effectiveSettingStr := "OFF"
if effectiveSetting {
effectiveSettingStr = "ON"
}
rb.Add(nil, client.server.name, "PERSISTENCE", "STATUS", storedSettingStr, effectiveSettingStr)
if broadcast {
for _, session := range client.Sessions() {
if session != rb.session && session.capabilities.Has(caps.Persistence) {
session.Send(nil, client.server.name, "PERSISTENCE", "STATUS", storedSettingStr, effectiveSettingStr)
}
}
}
}

// PING [params...]
func pingHandler(server *Server, client *Client, msg ircmsg.Message, rb *ResponseBuffer) bool {
rb.Add(nil, server.name, "PONG", server.name, msg.Params[0])
Expand Down
7 changes: 7 additions & 0 deletions irc/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,13 @@ Leaves the given channels and shows people the given reason.`,

When the server requires a connection password to join, used to send us the
password.`,
},
"persistence": {
text: `PERSISTENCE [params]

PERSISTENCE is a command associated with an IRC protocol extension for
persistent connections. End users should probably use /NS GET ALWAYS-ON
and /NS SET ALWAYS-ON instead.`,
},
"ping": {
text: `PING <args>...
Expand Down
3 changes: 3 additions & 0 deletions irc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,9 @@ func (server *Server) playRegistrationBurst(session *Session) {

rb := NewResponseBuffer(session)
server.RplISupport(c, rb)
if d.account != "" && session.capabilities.Has(caps.Persistence) {
reportPersistenceStatus(c, rb, false)
}
server.Lusers(c, rb)
server.MOTD(c, rb)
rb.Send(true)
Expand Down