diff --git a/db.go b/db.go index 9cac960..9ba4d7b 100644 --- a/db.go +++ b/db.go @@ -215,6 +215,29 @@ func (db *DB) StoreNetwork(username string, network *Network) error { return err } +func (db *DB) DeleteNetwork(id int64) error { + db.lock.Lock() + defer db.lock.Unlock() + + tx, err := db.db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + + _, err = tx.Exec("DELETE FROM Network WHERE id = ?", id) + if err != nil { + return err + } + + _, err = tx.Exec("DELETE FROM Channel WHERE network = ?", id) + if err != nil { + return err + } + + return tx.Commit() +} + func (db *DB) ListChannels(networkID int64) ([]Channel, error) { db.lock.RLock() defer db.lock.RUnlock() diff --git a/service.go b/service.go index 268afb6..0756804 100644 --- a/service.go +++ b/service.go @@ -97,6 +97,11 @@ func init() { desc: "show a list of saved networks and their current status", handle: handleServiceNetworkStatus, }, + "delete": { + usage: "", + desc: "delete a network", + handle: handleServiceNetworkDelete, + }, }, }, } @@ -143,9 +148,14 @@ func handleServiceHelp(dc *downstreamConn, params []string) error { return nil } -func handleServiceCreateNetwork(dc *downstreamConn, params []string) error { +func newFlagSet() *flag.FlagSet { fs := flag.NewFlagSet("", flag.ContinueOnError) fs.SetOutput(ioutil.Discard) + return fs +} + +func handleServiceCreateNetwork(dc *downstreamConn, params []string) error { + fs := newFlagSet() addr := fs.String("addr", "", "") name := fs.String("name", "", "") username := fs.String("username", "", "") @@ -177,7 +187,7 @@ func handleServiceCreateNetwork(dc *downstreamConn, params []string) error { return fmt.Errorf("could not create network: %v", err) } - sendServicePRIVMSG(dc, fmt.Sprintf("created network %s successfully", network.GetName())) + sendServicePRIVMSG(dc, fmt.Sprintf("created network %q", network.GetName())) return nil } @@ -204,3 +214,21 @@ func handleServiceNetworkStatus(dc *downstreamConn, params []string) error { }) return nil } + +func handleServiceNetworkDelete(dc *downstreamConn, params []string) error { + if len(params) != 1 { + return fmt.Errorf("expected exactly one argument") + } + + net := dc.user.getNetwork(params[0]) + if net == nil { + return fmt.Errorf("unknown network %q", params[0]) + } + + if err := dc.user.deleteNetwork(net.ID); err != nil { + return err + } + + sendServicePRIVMSG(dc, fmt.Sprintf("deleted network %q", net.GetName())) + return nil +} diff --git a/user.go b/user.go index 09e0d6b..7e0a299 100644 --- a/user.go +++ b/user.go @@ -37,8 +37,9 @@ type eventDownstreamDisconnected struct { type network struct { Network - user *user - ring *Ring + user *user + ring *Ring + stopped chan struct{} lock sync.Mutex conn *upstreamConn @@ -50,6 +51,7 @@ func newNetwork(user *user, record *Network) *network { Network: *record, user: user, ring: NewRing(user.srv.RingCap), + stopped: make(chan struct{}), history: make(map[string]uint64), } } @@ -57,6 +59,13 @@ func newNetwork(user *user, record *Network) *network { func (net *network) run() { var lastTry time.Time for { + select { + case <-net.stopped: + return + default: + // This space is intentionally left blank + } + if dur := time.Now().Sub(lastTry); dur < retryConnectMinDelay { delay := retryConnectMinDelay - dur net.user.srv.Logger.Printf("waiting %v before trying to reconnect to %q", delay.Truncate(time.Second), net.Addr) @@ -92,6 +101,19 @@ func (net *network) upstream() *upstreamConn { return net.conn } +func (net *network) Stop() { + select { + case <-net.stopped: + return + default: + close(net.stopped) + } + + if uc := net.upstream(); uc != nil { + uc.Close() + } +} + type user struct { User srv *Server @@ -265,3 +287,27 @@ func (u *user) createNetwork(net *Network) (*network, error) { go network.run() return network, nil } + +func (u *user) deleteNetwork(id int64) error { + for i, net := range u.networks { + if net.ID != id { + continue + } + + if err := u.srv.db.DeleteNetwork(net.ID); err != nil { + return err + } + + u.forEachDownstream(func(dc *downstreamConn) { + if dc.network != nil && dc.network == net { + dc.Close() + } + }) + + net.Stop() + u.networks = append(u.networks[:i], u.networks[i+1:]...) + return nil + } + + panic("tried deleting a non-existing network") +}