Make the auto-away functionality configurable

This commit is contained in:
zsrv 2022-09-26 13:49:26 -04:00 committed by Simon Ser
parent d605d64d1d
commit 926dcb37ac
6 changed files with 35 additions and 16 deletions

View File

@ -131,6 +131,7 @@ type Network struct {
Pass string Pass string
ConnectCommands []string ConnectCommands []string
SASL SASL SASL SASL
AutoAway bool
Enabled bool Enabled bool
} }

View File

@ -51,6 +51,7 @@ CREATE TABLE "Network" (
sasl_plain_password VARCHAR(255), sasl_plain_password VARCHAR(255),
sasl_external_cert BYTEA, sasl_external_cert BYTEA,
sasl_external_key BYTEA, sasl_external_key BYTEA,
auto_away BOOLEAN NOT NULL DEFAULT TRUE,
enabled BOOLEAN NOT NULL DEFAULT TRUE, enabled BOOLEAN NOT NULL DEFAULT TRUE,
UNIQUE("user", addr, nick), UNIQUE("user", addr, nick),
UNIQUE("user", name) UNIQUE("user", name)
@ -163,6 +164,7 @@ var postgresMigrations = []string{
ALTER COLUMN "user" ALTER COLUMN "user"
SET NOT NULL; SET NOT NULL;
`, `,
`ALTER TABLE "Network" ADD COLUMN auto_away BOOLEAN NOT NULL DEFAULT TRUE`,
} }
type PostgresDB struct { type PostgresDB struct {
@ -379,7 +381,7 @@ func (db *PostgresDB) ListNetworks(ctx context.Context, userID int64) ([]Network
rows, err := db.db.QueryContext(ctx, ` rows, err := db.db.QueryContext(ctx, `
SELECT id, name, addr, nick, username, realname, pass, connect_commands, sasl_mechanism, SELECT id, name, addr, nick, username, realname, pass, connect_commands, sasl_mechanism,
sasl_plain_username, sasl_plain_password, sasl_external_cert, sasl_external_key, enabled sasl_plain_username, sasl_plain_password, sasl_external_cert, sasl_external_key, auto_away, enabled
FROM "Network" FROM "Network"
WHERE "user" = $1`, userID) WHERE "user" = $1`, userID)
if err != nil { if err != nil {
@ -394,7 +396,7 @@ func (db *PostgresDB) ListNetworks(ctx context.Context, userID int64) ([]Network
var saslMechanism, saslPlainUsername, saslPlainPassword sql.NullString var saslMechanism, saslPlainUsername, saslPlainPassword sql.NullString
err := rows.Scan(&net.ID, &name, &net.Addr, &nick, &username, &realname, err := rows.Scan(&net.ID, &name, &net.Addr, &nick, &username, &realname,
&pass, &connectCommands, &saslMechanism, &saslPlainUsername, &saslPlainPassword, &pass, &connectCommands, &saslMechanism, &saslPlainUsername, &saslPlainPassword,
&net.SASL.External.CertBlob, &net.SASL.External.PrivKeyBlob, &net.Enabled) &net.SASL.External.CertBlob, &net.SASL.External.PrivKeyBlob, &net.AutoAway, &net.Enabled)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -450,23 +452,23 @@ func (db *PostgresDB) StoreNetwork(ctx context.Context, userID int64, network *N
err = db.db.QueryRowContext(ctx, ` err = db.db.QueryRowContext(ctx, `
INSERT INTO "Network" ("user", name, addr, nick, username, realname, pass, connect_commands, INSERT INTO "Network" ("user", name, addr, nick, username, realname, pass, connect_commands,
sasl_mechanism, sasl_plain_username, sasl_plain_password, sasl_external_cert, sasl_mechanism, sasl_plain_username, sasl_plain_password, sasl_external_cert,
sasl_external_key, enabled) sasl_external_key, auto_away, enabled)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
RETURNING id`, RETURNING id`,
userID, netName, network.Addr, nick, netUsername, realname, pass, connectCommands, userID, netName, network.Addr, nick, netUsername, realname, pass, connectCommands,
saslMechanism, saslPlainUsername, saslPlainPassword, network.SASL.External.CertBlob, saslMechanism, saslPlainUsername, saslPlainPassword, network.SASL.External.CertBlob,
network.SASL.External.PrivKeyBlob, network.Enabled).Scan(&network.ID) network.SASL.External.PrivKeyBlob, network.AutoAway, network.Enabled).Scan(&network.ID)
} else { } else {
_, err = db.db.ExecContext(ctx, ` _, err = db.db.ExecContext(ctx, `
UPDATE "Network" UPDATE "Network"
SET name = $2, addr = $3, nick = $4, username = $5, realname = $6, pass = $7, SET name = $2, addr = $3, nick = $4, username = $5, realname = $6, pass = $7,
connect_commands = $8, sasl_mechanism = $9, sasl_plain_username = $10, connect_commands = $8, sasl_mechanism = $9, sasl_plain_username = $10,
sasl_plain_password = $11, sasl_external_cert = $12, sasl_external_key = $13, sasl_plain_password = $11, sasl_external_cert = $12, sasl_external_key = $13,
enabled = $14 auto_away = $14, enabled = $15
WHERE id = $1`, WHERE id = $1`,
network.ID, netName, network.Addr, nick, netUsername, realname, pass, connectCommands, network.ID, netName, network.Addr, nick, netUsername, realname, pass, connectCommands,
saslMechanism, saslPlainUsername, saslPlainPassword, network.SASL.External.CertBlob, saslMechanism, saslPlainUsername, saslPlainPassword, network.SASL.External.CertBlob,
network.SASL.External.PrivKeyBlob, network.Enabled) network.SASL.External.PrivKeyBlob, network.AutoAway, network.Enabled)
} }
return err return err
} }

View File

@ -49,6 +49,7 @@ CREATE TABLE Network (
sasl_plain_password TEXT, sasl_plain_password TEXT,
sasl_external_cert BLOB, sasl_external_cert BLOB,
sasl_external_key BLOB, sasl_external_key BLOB,
auto_away INTEGER NOT NULL DEFAULT 1,
enabled INTEGER NOT NULL DEFAULT 1, enabled INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY(user) REFERENCES User(id), FOREIGN KEY(user) REFERENCES User(id),
UNIQUE(user, addr, nick), UNIQUE(user, addr, nick),
@ -248,6 +249,7 @@ var sqliteMigrations = []string{
UPDATE WebPushSubscription AS wps SET user = (SELECT n.user FROM Network AS n WHERE n.id = wps.network); UPDATE WebPushSubscription AS wps SET user = (SELECT n.user FROM Network AS n WHERE n.id = wps.network);
`, `,
"ALTER TABLE User ADD COLUMN nick TEXT;", "ALTER TABLE User ADD COLUMN nick TEXT;",
"ALTER TABLE Network ADD COLUMN auto_away INTEGER NOT NULL DEFAULT 1;",
} }
type SqliteDB struct { type SqliteDB struct {
@ -488,7 +490,7 @@ func (db *SqliteDB) ListNetworks(ctx context.Context, userID int64) ([]Network,
rows, err := db.db.QueryContext(ctx, ` rows, err := db.db.QueryContext(ctx, `
SELECT id, name, addr, nick, username, realname, pass, SELECT id, name, addr, nick, username, realname, pass,
connect_commands, sasl_mechanism, sasl_plain_username, sasl_plain_password, connect_commands, sasl_mechanism, sasl_plain_username, sasl_plain_password,
sasl_external_cert, sasl_external_key, enabled sasl_external_cert, sasl_external_key, auto_away, enabled
FROM Network FROM Network
WHERE user = ?`, WHERE user = ?`,
userID) userID)
@ -504,7 +506,7 @@ func (db *SqliteDB) ListNetworks(ctx context.Context, userID int64) ([]Network,
var saslMechanism, saslPlainUsername, saslPlainPassword sql.NullString var saslMechanism, saslPlainUsername, saslPlainPassword sql.NullString
err := rows.Scan(&net.ID, &name, &net.Addr, &nick, &username, &realname, err := rows.Scan(&net.ID, &name, &net.Addr, &nick, &username, &realname,
&pass, &connectCommands, &saslMechanism, &saslPlainUsername, &saslPlainPassword, &pass, &connectCommands, &saslMechanism, &saslPlainUsername, &saslPlainPassword,
&net.SASL.External.CertBlob, &net.SASL.External.PrivKeyBlob, &net.Enabled) &net.SASL.External.CertBlob, &net.SASL.External.PrivKeyBlob, &net.AutoAway, &net.Enabled)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -561,6 +563,7 @@ func (db *SqliteDB) StoreNetwork(ctx context.Context, userID int64, network *Net
sql.Named("sasl_plain_password", saslPlainPassword), sql.Named("sasl_plain_password", saslPlainPassword),
sql.Named("sasl_external_cert", network.SASL.External.CertBlob), sql.Named("sasl_external_cert", network.SASL.External.CertBlob),
sql.Named("sasl_external_key", network.SASL.External.PrivKeyBlob), sql.Named("sasl_external_key", network.SASL.External.PrivKeyBlob),
sql.Named("auto_away", network.AutoAway),
sql.Named("enabled", network.Enabled), sql.Named("enabled", network.Enabled),
sql.Named("id", network.ID), // only for UPDATE sql.Named("id", network.ID), // only for UPDATE
@ -575,17 +578,17 @@ func (db *SqliteDB) StoreNetwork(ctx context.Context, userID int64, network *Net
realname = :realname, pass = :pass, connect_commands = :connect_commands, realname = :realname, pass = :pass, connect_commands = :connect_commands,
sasl_mechanism = :sasl_mechanism, sasl_plain_username = :sasl_plain_username, sasl_plain_password = :sasl_plain_password, sasl_mechanism = :sasl_mechanism, sasl_plain_username = :sasl_plain_username, sasl_plain_password = :sasl_plain_password,
sasl_external_cert = :sasl_external_cert, sasl_external_key = :sasl_external_key, sasl_external_cert = :sasl_external_cert, sasl_external_key = :sasl_external_key,
enabled = :enabled auto_away = :auto_away, enabled = :enabled
WHERE id = :id`, args...) WHERE id = :id`, args...)
} else { } else {
var res sql.Result var res sql.Result
res, err = db.db.ExecContext(ctx, ` res, err = db.db.ExecContext(ctx, `
INSERT INTO Network(user, name, addr, nick, username, realname, pass, INSERT INTO Network(user, name, addr, nick, username, realname, pass,
connect_commands, sasl_mechanism, sasl_plain_username, connect_commands, sasl_mechanism, sasl_plain_username,
sasl_plain_password, sasl_external_cert, sasl_external_key, enabled) sasl_plain_password, sasl_external_cert, sasl_external_key, auto_away, enabled)
VALUES (:user, :name, :addr, :nick, :username, :realname, :pass, VALUES (:user, :name, :addr, :nick, :username, :realname, :pass,
:connect_commands, :sasl_mechanism, :sasl_plain_username, :connect_commands, :sasl_mechanism, :sasl_plain_username,
:sasl_plain_password, :sasl_external_cert, :sasl_external_key, :enabled)`, :sasl_plain_password, :sasl_external_cert, :sasl_external_key, :auto_away, :enabled)`,
args...) args...)
if err != nil { if err != nil {
return err return err

View File

@ -36,7 +36,7 @@ If a network specified in the username doesn't exist, and the network name is a
valid hostname, the network will be automatically added. valid hostname, the network will be automatically added.
When all clients are disconnected from the bouncer, the user is automatically When all clients are disconnected from the bouncer, the user is automatically
marked as away. marked as away by default.
soju will reload the configuration file, the TLS certificate/key and the MOTD soju will reload the configuration file, the TLS certificate/key and the MOTD
file when it receives the HUP signal. The configuration options _listen_, _db_ file when it receives the HUP signal. The configuration options _listen_, _db_
@ -217,6 +217,11 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just
Connect with the specified nickname. By default, the account's username Connect with the specified nickname. By default, the account's username
is used. is used.
*-auto-away* true|false
Enable or disable the auto-away feature. If the feature is enabled, the
user will be marked as away when all clients are disconnected from the
bouncer. By default, auto-away is enabled.
*-enabled* true|false *-enabled* true|false
Enable or disable the network. If the network is disabled, the bouncer Enable or disable the network. If the network is disabled, the bouncer
won't connect to it. By default, the network is enabled. won't connect to it. By default, the network is enabled.

View File

@ -201,7 +201,7 @@ func init() {
"network": { "network": {
children: serviceCommandSet{ children: serviceCommandSet{
"create": { "create": {
usage: "-addr <addr> [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick] [-enabled enabled] [-connect-command command]...", usage: "-addr <addr> [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick] [-auto-away auto-away] [-enabled enabled] [-connect-command command]...",
desc: "add a new network", desc: "add a new network",
handle: handleServiceNetworkCreate, handle: handleServiceNetworkCreate,
}, },
@ -210,7 +210,7 @@ func init() {
handle: handleServiceNetworkStatus, handle: handleServiceNetworkStatus,
}, },
"update": { "update": {
usage: "[name] [-addr addr] [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick] [-enabled enabled] [-connect-command command]...", usage: "[name] [-addr addr] [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick] [-auto-away auto-away] [-enabled enabled] [-connect-command command]...",
desc: "update a network", desc: "update a network",
handle: handleServiceNetworkUpdate, handle: handleServiceNetworkUpdate,
}, },
@ -431,7 +431,7 @@ func getNetworkFromArg(dc *downstreamConn, params []string) (*network, []string,
type networkFlagSet struct { type networkFlagSet struct {
*flag.FlagSet *flag.FlagSet
Addr, Name, Nick, Username, Pass, Realname *string Addr, Name, Nick, Username, Pass, Realname *string
Enabled *bool AutoAway, Enabled *bool
ConnectCommands []string ConnectCommands []string
} }
@ -443,6 +443,7 @@ func newNetworkFlagSet() *networkFlagSet {
fs.Var(stringPtrFlag{&fs.Username}, "username", "") fs.Var(stringPtrFlag{&fs.Username}, "username", "")
fs.Var(stringPtrFlag{&fs.Pass}, "pass", "") fs.Var(stringPtrFlag{&fs.Pass}, "pass", "")
fs.Var(stringPtrFlag{&fs.Realname}, "realname", "") fs.Var(stringPtrFlag{&fs.Realname}, "realname", "")
fs.Var(boolPtrFlag{&fs.AutoAway}, "auto-away", "")
fs.Var(boolPtrFlag{&fs.Enabled}, "enabled", "") fs.Var(boolPtrFlag{&fs.Enabled}, "enabled", "")
fs.Var((*stringSliceFlag)(&fs.ConnectCommands), "connect-command", "") fs.Var((*stringSliceFlag)(&fs.ConnectCommands), "connect-command", "")
return fs return fs
@ -478,6 +479,9 @@ func (fs *networkFlagSet) update(network *database.Network) error {
if fs.Realname != nil { if fs.Realname != nil {
network.Realname = *fs.Realname network.Realname = *fs.Realname
} }
if fs.AutoAway != nil {
network.AutoAway = *fs.AutoAway
}
if fs.Enabled != nil { if fs.Enabled != nil {
network.Enabled = *fs.Enabled network.Enabled = *fs.Enabled
} }

View File

@ -1958,6 +1958,10 @@ func (uc *upstreamConn) produce(target string, msg *irc.Message, originID uint64
func (uc *upstreamConn) updateAway() { func (uc *upstreamConn) updateAway() {
ctx := context.TODO() ctx := context.TODO()
if !uc.network.AutoAway {
return
}
away := true away := true
uc.forEachDownstream(func(dc *downstreamConn) { uc.forEachDownstream(func(dc *downstreamConn) {
if dc.away == nil { if dc.away == nil {