diff --git a/contrib/znc-import.go b/contrib/znc-import.go index 68e8608..fb8b16a 100644 --- a/contrib/znc-import.go +++ b/contrib/znc-import.go @@ -181,6 +181,7 @@ func main() { n.Username = netIdent n.Realname = netRealname n.Pass = pass + n.Enabled = section.Values.Get("IRCConnectEnabled") != "false" if err := db.StoreNetwork(userID, n); err != nil { logger.Fatalf("failed to store network: %v", err) diff --git a/db.go b/db.go index 3d0c756..36ea462 100644 --- a/db.go +++ b/db.go @@ -60,6 +60,7 @@ type Network struct { Pass string ConnectCommands []string SASL SASL + Enabled bool } func (net *Network) GetName() string { diff --git a/db_sqlite.go b/db_sqlite.go index 6f15a53..46c359a 100644 --- a/db_sqlite.go +++ b/db_sqlite.go @@ -34,6 +34,7 @@ CREATE TABLE Network ( sasl_plain_password VARCHAR(255), sasl_external_cert BLOB DEFAULT NULL, sasl_external_key BLOB DEFAULT NULL, + enabled INTEGER NOT NULL DEFAULT 1, FOREIGN KEY(user) REFERENCES User(id), UNIQUE(user, addr, nick), UNIQUE(user, name) @@ -131,6 +132,7 @@ var sqliteMigrations = []string{ ); `, "ALTER TABLE Channel ADD COLUMN detached_internal_msgid VARCHAR(255)", + "ALTER TABLE Network ADD COLUMN enabled INTEGER NOT NULL DEFAULT 1", } type SqliteDB struct { @@ -312,7 +314,7 @@ func (db *SqliteDB) ListNetworks(userID int64) ([]Network, error) { rows, err := db.db.Query(`SELECT id, name, addr, nick, username, realname, pass, connect_commands, sasl_mechanism, sasl_plain_username, sasl_plain_password, - sasl_external_cert, sasl_external_key + sasl_external_cert, sasl_external_key, enabled FROM Network WHERE user = ?`, userID) @@ -328,7 +330,7 @@ func (db *SqliteDB) ListNetworks(userID int64) ([]Network, error) { var saslMechanism, saslPlainUsername, saslPlainPassword sql.NullString err := rows.Scan(&net.ID, &name, &net.Addr, &net.Nick, &username, &realname, &pass, &connectCommands, &saslMechanism, &saslPlainUsername, &saslPlainPassword, - &net.SASL.External.CertBlob, &net.SASL.External.PrivKeyBlob) + &net.SASL.External.CertBlob, &net.SASL.External.PrivKeyBlob, &net.Enabled) if err != nil { return nil, err } @@ -382,21 +384,21 @@ func (db *SqliteDB) StoreNetwork(userID int64, network *Network) error { _, err = db.db.Exec(`UPDATE Network SET name = ?, addr = ?, nick = ?, username = ?, realname = ?, pass = ?, connect_commands = ?, sasl_mechanism = ?, sasl_plain_username = ?, sasl_plain_password = ?, - sasl_external_cert = ?, sasl_external_key = ? + sasl_external_cert = ?, sasl_external_key = ?, enabled = ? WHERE id = ?`, netName, network.Addr, network.Nick, netUsername, realname, pass, connectCommands, saslMechanism, saslPlainUsername, saslPlainPassword, - network.SASL.External.CertBlob, network.SASL.External.PrivKeyBlob, + network.SASL.External.CertBlob, network.SASL.External.PrivKeyBlob, network.Enabled, network.ID) } else { var res sql.Result res, err = db.db.Exec(`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) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + sasl_plain_password, sasl_external_cert, sasl_external_key, enabled) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, userID, netName, network.Addr, network.Nick, netUsername, realname, pass, connectCommands, saslMechanism, saslPlainUsername, saslPlainPassword, network.SASL.External.CertBlob, - network.SASL.External.PrivKeyBlob) + network.SASL.External.PrivKeyBlob, network.Enabled) if err != nil { return err } diff --git a/doc/soju.1.scd b/doc/soju.1.scd index b48c608..122dca8 100644 --- a/doc/soju.1.scd +++ b/doc/soju.1.scd @@ -158,6 +158,10 @@ 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. + *-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. + *-connect-command* Send the specified command as a raw IRC message right after connecting to the server. This can be used to identify to an account when the diff --git a/downstream.go b/downstream.go index 86a3e16..123fb2c 100644 --- a/downstream.go +++ b/downstream.go @@ -1001,8 +1001,9 @@ func (dc *downstreamConn) loadNetwork() error { dc.logger.Printf("auto-saving network %q", dc.networkName) var err error network, err = dc.user.createNetwork(&Network{ - Addr: dc.networkName, - Nick: nick, + Addr: dc.networkName, + Nick: nick, + Enabled: true, }) if err != nil { return err @@ -2174,6 +2175,7 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error { Username: username, Realname: realname, Pass: pass, + Enabled: true, } network, err := dc.user.createNetwork(record) if err != nil { diff --git a/service.go b/service.go index 295dbd3..3d51329 100644 --- a/service.go +++ b/service.go @@ -18,6 +18,7 @@ import ( "io/ioutil" "math/big" "sort" + "strconv" "strings" "time" @@ -149,7 +150,7 @@ func init() { "network": { children: serviceCommandSet{ "create": { - usage: "-addr [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick] [-connect-command command]...", + usage: "-addr [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick] [-enabled enabled] [-connect-command command]...", desc: "add a new network", handle: handleServiceNetworkCreate, }, @@ -158,7 +159,7 @@ func init() { handle: handleServiceNetworkStatus, }, "update": { - usage: " [-addr addr] [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick] [-connect-command command]...", + usage: " [-addr addr] [-name name] [-username username] [-pass pass] [-realname realname] [-nick nick] [-enabled enabled] [-connect-command command]...", desc: "update a network", handle: handleServiceNetworkUpdate, }, @@ -317,9 +318,30 @@ func (f stringPtrFlag) Set(s string) error { return nil } +type boolPtrFlag struct { + ptr **bool +} + +func (f boolPtrFlag) String() string { + if f.ptr == nil || *f.ptr == nil { + return "" + } + return strconv.FormatBool(**f.ptr) +} + +func (f boolPtrFlag) Set(s string) error { + v, err := strconv.ParseBool(s) + if err != nil { + return err + } + *f.ptr = &v + return nil +} + type networkFlagSet struct { *flag.FlagSet Addr, Name, Nick, Username, Pass, Realname *string + Enabled *bool ConnectCommands []string } @@ -331,6 +353,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.Enabled}, "enabled", "") fs.Var((*stringSliceFlag)(&fs.ConnectCommands), "connect-command", "") return fs } @@ -362,6 +385,9 @@ func (fs *networkFlagSet) update(network *Network) error { if fs.Realname != nil { network.Realname = *fs.Realname } + if fs.Enabled != nil { + network.Enabled = *fs.Enabled + } if fs.ConnectCommands != nil { if len(fs.ConnectCommands) == 1 && fs.ConnectCommands[0] == "" { network.ConnectCommands = nil @@ -388,8 +414,9 @@ func handleServiceNetworkCreate(dc *downstreamConn, params []string) error { } record := &Network{ - Addr: *fs.Addr, - Nick: dc.nick, + Addr: *fs.Addr, + Nick: dc.nick, + Enabled: true, } if err := fs.update(record); err != nil { return err @@ -415,6 +442,8 @@ func handleServiceNetworkStatus(dc *downstreamConn, params []string) error { statuses = append(statuses, "connected") } details = fmt.Sprintf("%v channels", uc.channels.Len()) + } else if !net.Enabled { + statuses = append(statuses, "disabled") } else { statuses = append(statuses, "disconnected") if net.lastError != nil { diff --git a/user.go b/user.go index 192b9ff..589326c 100644 --- a/user.go +++ b/user.go @@ -172,6 +172,10 @@ func userIdent(u *User) string { } func (net *network) run() { + if !net.Enabled { + return + } + var lastTry time.Time for { if net.isStopped() {