diff --git a/database/database.go b/database/database.go index be60fd8..b4a6ac8 100644 --- a/database/database.go +++ b/database/database.go @@ -131,6 +131,7 @@ type Network struct { Pass string ConnectCommands []string SASL SASL + AutoAway bool Enabled bool } diff --git a/database/postgres.go b/database/postgres.go index 98030fe..9af5c4a 100644 --- a/database/postgres.go +++ b/database/postgres.go @@ -51,6 +51,7 @@ CREATE TABLE "Network" ( sasl_plain_password VARCHAR(255), sasl_external_cert BYTEA, sasl_external_key BYTEA, + auto_away BOOLEAN NOT NULL DEFAULT TRUE, enabled BOOLEAN NOT NULL DEFAULT TRUE, UNIQUE("user", addr, nick), UNIQUE("user", name) @@ -163,6 +164,7 @@ var postgresMigrations = []string{ ALTER COLUMN "user" SET NOT NULL; `, + `ALTER TABLE "Network" ADD COLUMN auto_away BOOLEAN NOT NULL DEFAULT TRUE`, } type PostgresDB struct { @@ -379,7 +381,7 @@ func (db *PostgresDB) ListNetworks(ctx context.Context, userID int64) ([]Network rows, err := db.db.QueryContext(ctx, ` 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" WHERE "user" = $1`, userID) if err != nil { @@ -394,7 +396,7 @@ func (db *PostgresDB) ListNetworks(ctx context.Context, userID int64) ([]Network var saslMechanism, saslPlainUsername, saslPlainPassword sql.NullString err := rows.Scan(&net.ID, &name, &net.Addr, &nick, &username, &realname, &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 { return nil, err } @@ -450,23 +452,23 @@ func (db *PostgresDB) StoreNetwork(ctx context.Context, userID int64, network *N err = db.db.QueryRowContext(ctx, ` INSERT INTO "Network" ("user", name, addr, nick, username, realname, pass, connect_commands, sasl_mechanism, sasl_plain_username, sasl_plain_password, sasl_external_cert, - sasl_external_key, enabled) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) + sasl_external_key, auto_away, enabled) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING id`, userID, netName, network.Addr, nick, netUsername, realname, pass, connectCommands, 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 { _, err = db.db.ExecContext(ctx, ` UPDATE "Network" SET name = $2, addr = $3, nick = $4, username = $5, realname = $6, pass = $7, connect_commands = $8, sasl_mechanism = $9, sasl_plain_username = $10, sasl_plain_password = $11, sasl_external_cert = $12, sasl_external_key = $13, - enabled = $14 + auto_away = $14, enabled = $15 WHERE id = $1`, network.ID, netName, network.Addr, nick, netUsername, realname, pass, connectCommands, saslMechanism, saslPlainUsername, saslPlainPassword, network.SASL.External.CertBlob, - network.SASL.External.PrivKeyBlob, network.Enabled) + network.SASL.External.PrivKeyBlob, network.AutoAway, network.Enabled) } return err } diff --git a/database/sqlite.go b/database/sqlite.go index b3e5242..90b05b1 100644 --- a/database/sqlite.go +++ b/database/sqlite.go @@ -49,6 +49,7 @@ CREATE TABLE Network ( sasl_plain_password TEXT, sasl_external_cert BLOB, sasl_external_key BLOB, + auto_away INTEGER NOT NULL DEFAULT 1, enabled INTEGER NOT NULL DEFAULT 1, FOREIGN KEY(user) REFERENCES User(id), 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); `, "ALTER TABLE User ADD COLUMN nick TEXT;", + "ALTER TABLE Network ADD COLUMN auto_away INTEGER NOT NULL DEFAULT 1;", } type SqliteDB struct { @@ -488,7 +490,7 @@ func (db *SqliteDB) ListNetworks(ctx context.Context, userID int64) ([]Network, rows, err := db.db.QueryContext(ctx, ` 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_external_cert, sasl_external_key, auto_away, enabled FROM Network WHERE user = ?`, userID) @@ -504,7 +506,7 @@ func (db *SqliteDB) ListNetworks(ctx context.Context, userID int64) ([]Network, var saslMechanism, saslPlainUsername, saslPlainPassword sql.NullString err := rows.Scan(&net.ID, &name, &net.Addr, &nick, &username, &realname, &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 { 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_external_cert", network.SASL.External.CertBlob), sql.Named("sasl_external_key", network.SASL.External.PrivKeyBlob), + sql.Named("auto_away", network.AutoAway), sql.Named("enabled", network.Enabled), 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, 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, - enabled = :enabled + auto_away = :auto_away, enabled = :enabled WHERE id = :id`, args...) } else { var res sql.Result res, err = db.db.ExecContext(ctx, ` INSERT INTO Network(user, 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_password, sasl_external_cert, sasl_external_key, auto_away, enabled) VALUES (:user, :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_password, :sasl_external_cert, :sasl_external_key, :auto_away, :enabled)`, args...) if err != nil { return err diff --git a/doc/soju.1.scd b/doc/soju.1.scd index 41cfc10..b5e398e 100644 --- a/doc/soju.1.scd +++ b/doc/soju.1.scd @@ -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. 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 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 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 Enable or disable the network. If the network is disabled, the bouncer won't connect to it. By default, the network is enabled. diff --git a/service.go b/service.go index 97de0f3..41e52a8 100644 --- a/service.go +++ b/service.go @@ -201,7 +201,7 @@ func init() { "network": { children: serviceCommandSet{ "create": { - usage: "-addr [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick] [-enabled enabled] [-connect-command command]...", + usage: "-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", handle: handleServiceNetworkCreate, }, @@ -210,7 +210,7 @@ func init() { handle: handleServiceNetworkStatus, }, "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", handle: handleServiceNetworkUpdate, }, @@ -431,7 +431,7 @@ func getNetworkFromArg(dc *downstreamConn, params []string) (*network, []string, type networkFlagSet struct { *flag.FlagSet Addr, Name, Nick, Username, Pass, Realname *string - Enabled *bool + AutoAway, Enabled *bool ConnectCommands []string } @@ -443,6 +443,7 @@ func newNetworkFlagSet() *networkFlagSet { fs.Var(stringPtrFlag{&fs.Username}, "username", "") fs.Var(stringPtrFlag{&fs.Pass}, "pass", "") fs.Var(stringPtrFlag{&fs.Realname}, "realname", "") + fs.Var(boolPtrFlag{&fs.AutoAway}, "auto-away", "") fs.Var(boolPtrFlag{&fs.Enabled}, "enabled", "") fs.Var((*stringSliceFlag)(&fs.ConnectCommands), "connect-command", "") return fs @@ -478,6 +479,9 @@ func (fs *networkFlagSet) update(network *database.Network) error { if fs.Realname != nil { network.Realname = *fs.Realname } + if fs.AutoAway != nil { + network.AutoAway = *fs.AutoAway + } if fs.Enabled != nil { network.Enabled = *fs.Enabled } diff --git a/upstream.go b/upstream.go index ac6cf47..e1030a2 100644 --- a/upstream.go +++ b/upstream.go @@ -1958,6 +1958,10 @@ func (uc *upstreamConn) produce(target string, msg *irc.Message, originID uint64 func (uc *upstreamConn) updateAway() { ctx := context.TODO() + if !uc.network.AutoAway { + return + } + away := true uc.forEachDownstream(func(dc *downstreamConn) { if dc.away == nil {