From 4d5b897d6638b8bcc9418672db145ed443b74e6c Mon Sep 17 00:00:00 2001 From: Mikkel Krautz Date: Sat, 27 Aug 2011 20:52:06 +0200 Subject: [PATCH] Re-add SQLite import support, but make it a build-time option. --- Makefile | 12 ++++- args.go | 73 +++++++++++++++++++++++++++ channel.go | 3 +- ctl.go | 2 +- freeze.go | 8 +-- gencert.go | 4 +- grumble.go | 107 ++++++++++++++++++--------------------- murmurdb.go | 118 ++++++++++++++++++++++++++++++++++++++++---- murmurdb_null.go | 13 +++++ pkg/sqlite/Makefile | 2 +- server.go | 2 +- 11 files changed, 265 insertions(+), 79 deletions(-) create mode 100644 args.go create mode 100644 murmurdb_null.go diff --git a/Makefile b/Makefile index aaf05a8..468aee1 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,17 @@ GOFILES = \ gencert.go \ register.go \ ctlrpc.go \ - ctl.go + ctl.go \ + args.go + +ifeq ($(SQLITE),1) + GOFILES += murmurdb.go + PACKAGES += pkg/sqlite + GCFLAGS += -Ipkg/sqlite/_obj + LDFLAGS += -Lpkg/sqlite/_obj +else + GOFILES += murmurdb_null.go +endif ifeq ($(GOOS),windows) GOFILES += signal_windows.go diff --git a/args.go b/args.go new file mode 100644 index 0000000..61862c2 --- /dev/null +++ b/args.go @@ -0,0 +1,73 @@ +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + "runtime" +) + +type args struct { + ShowHelp bool + DataDir string + BlobDir string + CtlNet string + CtlAddr string + GenerateCert bool + SQLiteDB string + CleanUp bool +} + +func defaultGrumbleDir() string { + dirname := ".grumble" + if runtime.GOOS == "windows" { + dirname = "grumble" + } + return filepath.Join(os.Getenv("HOME"), dirname) +} + +func defaultDataDir() string { + return filepath.Join(defaultGrumbleDir(), "data") +} + +func defaultBlobDir() string { + return filepath.Join(defaultGrumbleDir(), "blob") +} + +func defaultCtlNet() string { + if runtime.GOOS == "windows" { + return "tcp" + } + return "unix" +} + +func defaultCtlAddr() string { + if runtime.GOOS == "windows" { + return "localhost:5454" + } + return filepath.Join(defaultGrumbleDir(), ".ctl") +} + +func Usage() { + fmt.Fprintf(os.Stderr, "usage: grumble [options]\n") + fmt.Fprintf(os.Stderr, "remote control: grumble [options] ctl [ctlopts]\n") + flag.PrintDefaults() +} + +var Args args + +func init() { + flag.BoolVar(&Args.ShowHelp, "help", false, "Show this help") + flag.StringVar(&Args.DataDir, "datadir", defaultDataDir(), "Directory to use for server storage") + flag.StringVar(&Args.BlobDir, "blobdir", defaultBlobDir(), "Directory to use for blob storage") + flag.StringVar(&Args.CtlNet, "ctlnet", defaultCtlNet(), "Network to use for ctl socket") + flag.StringVar(&Args.CtlAddr, "ctladdr", defaultCtlAddr(), "Address to use for ctl socket") + flag.BoolVar(&Args.GenerateCert, "gencert", false, "Generate a self-signed certificate for use with Grumble") + + // SQLite related + if (SQLiteSupport) { + flag.StringVar(&Args.SQLiteDB, "murmurdb", "", "Path to a Murmur SQLite database to import from") + flag.BoolVar(&Args.CleanUp, "cleanup", false, "Clean up Grumble's data directory on launch") + } +} diff --git a/channel.go b/channel.go index fef2e7c..9ed5301 100644 --- a/channel.go +++ b/channel.go @@ -41,7 +41,8 @@ func NewChannel(id int, name string) (channel *Channel) { channel.clients = make(map[uint32]*Client) channel.children = make(map[int]*Channel) channel.ACL = []*ChannelACL{} - channel.Groups = map[string]*Group{} + channel.Groups = make(map[string]*Group) + channel.Links = make(map[int]*Channel) return } diff --git a/ctl.go b/ctl.go index d2e4df5..4690fc3 100644 --- a/ctl.go +++ b/ctl.go @@ -41,7 +41,7 @@ func GrumbleCtl(args []string) { sid, _ := strconv.Atoi64(args[1]) - client, err := rpc.Dial(*ctlnet, *ctladdr) + client, err := rpc.Dial(Args.CtlNet, Args.CtlAddr) if err != nil { log.Fatalf("Could not connect to control socket: %v", err) } diff --git a/freeze.go b/freeze.go index 996fef0..8dd9671 100644 --- a/freeze.go +++ b/freeze.go @@ -34,7 +34,7 @@ func (server *Server) FreezeToFile() (err os.Error) { if err != nil { return err } - f, err := ioutil.TempFile(filepath.Join(*datadir, strconv.Itoa64(server.Id)), ".main.fz_") + f, err := ioutil.TempFile(filepath.Join(Args.DataDir, strconv.Itoa64(server.Id)), ".main.fz_") if err != nil { return err } @@ -54,7 +54,7 @@ func (server *Server) FreezeToFile() (err os.Error) { if err != nil { return err } - err = os.Rename(f.Name(), filepath.Join(*datadir, strconv.Itoa64(server.Id), "main.fz")) + err = os.Rename(f.Name(), filepath.Join(Args.DataDir, strconv.Itoa64(server.Id), "main.fz")) if err != nil { return err } @@ -70,7 +70,7 @@ func (server *Server) FreezeToFile() (err os.Error) { // Open a new freeze log func (server *Server) openFreezeLog() (err os.Error) { - logfn := filepath.Join(*datadir, strconv.Itoa64(server.Id), "log.fz") + logfn := filepath.Join(Args.DataDir, strconv.Itoa64(server.Id), "log.fz") err = os.Remove(logfn) if pe, ok := err.(*os.PathError); ok && pe.Error == os.ENOENT { // OK. File does not exist... @@ -388,7 +388,7 @@ func NewServerFromFrozen(name string) (s *Server, err os.Error) { return nil, err } - path := filepath.Join(*datadir, name) + path := filepath.Join(Args.DataDir, name) mainFile := filepath.Join(path, "main.fz") logFile := filepath.Join(path, "log.fz") diff --git a/gencert.go b/gencert.go index 405c171..09254d7 100644 --- a/gencert.go +++ b/gencert.go @@ -55,7 +55,7 @@ func GenerateSelfSignedCert(certpath, keypath string) (err os.Error) { Bytes: keybuf, } - certfn := filepath.Join(*datadir, "cert") + certfn := filepath.Join(Args.DataDir, "cert") file, err := os.OpenFile(certfn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700) if err != nil { return err @@ -66,7 +66,7 @@ func GenerateSelfSignedCert(certpath, keypath string) (err os.Error) { return err } - keyfn := filepath.Join(*datadir, "key") + keyfn := filepath.Join(Args.DataDir, "key") file, err = os.OpenFile(keyfn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700) if err != nil { return err diff --git a/grumble.go b/grumble.go index b629953..aba8b40 100644 --- a/grumble.go +++ b/grumble.go @@ -14,59 +14,15 @@ import ( "path/filepath" "regexp" "rpc" - "runtime" ) -func defaultGrumbleDir() string { - dirname := ".grumble" - if runtime.GOOS == "windows" { - dirname = "grumble" - } - return filepath.Join(os.Getenv("HOME"), dirname) -} - -func defaultDataDir() string { - return filepath.Join(defaultGrumbleDir(), "data") -} - -func defaultBlobDir() string { - return filepath.Join(defaultGrumbleDir(), "blob") -} - -func defaultCtlNet() string { - if runtime.GOOS == "windows" { - return "tcp" - } - return "unix" -} - -func defaultCtlAddr() string { - if runtime.GOOS == "windows" { - return "localhost:5454" - } - return filepath.Join(defaultGrumbleDir(), ".ctl") -} - -var help *bool = flag.Bool("help", false, "Show this help") -var datadir *string = flag.String("datadir", defaultDataDir(), "Directory to use for server storage") -var blobdir *string = flag.String("blobdir", defaultBlobDir(), "Directory to use for blob storage") -var ctlnet *string = flag.String("ctlnet", defaultCtlNet(), "Network to use for ctl socket") -var ctladdr *string = flag.String("ctladdr", defaultCtlAddr(), "Address to use for ctl socket") -var gencert *bool = flag.Bool("gencert", false, "Generate a self-signed certificate for use with Grumble") - var servers map[int64]*Server -func Usage() { - fmt.Fprintf(os.Stderr, "usage: grumble [options]\n") - fmt.Fprintf(os.Stderr, "remote control: grumble [options] ctl [ctlopts]\n") - flag.PrintDefaults() -} - func main() { var err os.Error flag.Parse() - if *help == true { + if Args.ShowHelp == true { Usage() return } @@ -82,16 +38,15 @@ func main() { log.SetFlags(log.LstdFlags|log.Lmicroseconds) log.Printf("Grumble") - log.Printf("Using blob directory: %s", *blobdir) - err = blobstore.Open(*blobdir, true) + log.Printf("Using blob directory: %s", Args.BlobDir) + err = blobstore.Open(Args.BlobDir, true) if err != nil { log.Fatalf("Unable to initialize blobstore: %v", err.String()) } - // Generate a cert? - if *gencert { - certfn := filepath.Join(*datadir, "cert") - keyfn := filepath.Join(*datadir, "key") + if Args.GenerateCert { + certfn := filepath.Join(Args.DataDir, "cert") + keyfn := filepath.Join(Args.DataDir, "key") log.Printf("Generating 2048-bit RSA keypair for self-signed certificate...") err := GenerateSelfSignedCert(certfn, keyfn) @@ -108,15 +63,51 @@ func main() { return } - f, err := os.Open(*datadir) + // Should we import data from a Murmur SQLite file? + if SQLiteSupport && len(Args.SQLiteDB) > 0 { + f, err := os.Open(Args.DataDir) + if err != nil { + log.Fatalf("Murmur import failed: %s", err.String()) + } + defer f.Close() + + names, err := f.Readdirnames(-1) + if err != nil { + log.Fatalf("Murmur import failed: %s", err.String()) + } + + if !Args.CleanUp && len(names) > 0 { + log.Fatalf("Non-empty datadir. Refusing to import Murmur data.") + } + if Args.CleanUp { + log.Print("Cleaning up existing data directory") + for _, name := range names { + if err := os.RemoveAll(filepath.Join(Args.DataDir, name)); err != nil { + log.Fatalf("Unable to cleanup file: %s", name) + } + } + } + + log.Printf("Importing Murmur data from '%s'", Args.SQLiteDB) + if err = MurmurImport(Args.SQLiteDB); err != nil { + log.Fatalf("Murmur import failed: %s", err.String()) + } + + log.Printf("Import from Murmur SQLite database succeeded.") + log.Printf("Please restart Grumble to make use of the imported data.") + + return + } + + f, err := os.Open(Args.DataDir) if err != nil { - log.Fatalf("Murmur import failed: %s", err.String()) + log.Fatal(err) } defer f.Close() names, err := f.Readdirnames(-1) if err != nil { - log.Fatalf("Murmur import failed: %s", err.String()) + log.Fatal(err) } servers = make(map[int64]*Server) @@ -144,16 +135,16 @@ func main() { servers[s.Id] = s - os.Mkdir(filepath.Join(*datadir, fmt.Sprintf("%v", 1)), 0750) + os.Mkdir(filepath.Join(Args.DataDir, fmt.Sprintf("%v", 1)), 0750) s.FreezeToFile() go s.ListenAndMurmur() } - if *ctlnet == "unix" { - os.Remove(*ctladdr) + if Args.CtlNet == "unix" { + os.Remove(Args.CtlAddr) } - lis, err := net.Listen(*ctlnet, *ctladdr) + lis, err := net.Listen(Args.CtlNet, Args.CtlAddr) if err != nil { log.Panicf("Unable to listen on ctl socket: %v", err) } diff --git a/murmurdb.go b/murmurdb.go index fd4e342..8670f74 100644 --- a/murmurdb.go +++ b/murmurdb.go @@ -10,9 +10,13 @@ package main // SQLite datbase into a format that Grumble can understand. import ( + "grumble/ban" "grumble/blobstore" + "grumble/sqlite" + "log" + "net" "os" - "sqlite" + "path/filepath" "strconv" ) @@ -30,6 +34,51 @@ const ( UserInfoLastActive ) +const SQLiteSupport = true + +// Import the structure of an existing Murmur SQLite database. +func MurmurImport(filename string) (err os.Error) { + db, err := sqlite.Open(filename) + if err != nil { + panic(err.String()) + } + + stmt, err := db.Prepare("SELECT server_id FROM servers") + if err != nil { + panic(err.String()) + } + + var serverids []int64 + var sid int64 + for stmt.Next() { + stmt.Scan(&sid) + serverids = append(serverids, sid) + } + + log.Printf("Found servers: %v (%v servers)", serverids, len(serverids)) + + for _, sid := range serverids { + m, err := NewServerFromSQLite(sid, db) + if err != nil { + return err + } + + err = os.Mkdir(filepath.Join(Args.DataDir, strconv.Itoa64(sid)), 0750) + if err != nil { + return err + } + + err = m.FreezeToFile() + if err != nil { + return err + } + + log.Printf("Successfully imported server %v", sid) + } + + return +} + // Create a new Server from a Murmur SQLite database func NewServerFromSQLite(id int64, db *sqlite.Conn) (s *Server, err os.Error) { s, err = NewServer(id, "", int(DefaultPort+id-1)) @@ -67,6 +116,11 @@ func NewServerFromSQLite(id int64, db *sqlite.Conn) (s *Server, err os.Error) { return nil, err } + err = populateBans(s, db) + if err != nil { + return nil, err + } + return } @@ -88,11 +142,13 @@ func populateChannelInfoFromDatabase(server *Server, c *Channel, db *sqlite.Conn return err } - key, err := blobstore.Put([]byte(description)) - if err != nil { - return err + if len(description) > 0 { + key, err := blobstore.Put([]byte(description)) + if err != nil { + return err + } + c.DescriptionBlob = key } - c.DescriptionBlob = key } if err := stmt.Reset(); err != nil { @@ -357,7 +413,7 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) { } if UserId == 0 { - server.SuperUserPassword = "sha1$$" + SHA1Password + server.cfg.Set("SuperUserPassword", "sha1$$" + SHA1Password) } user, err := NewUser(uint32(UserId), UserName) @@ -365,11 +421,13 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) { return err } - key, err := blobstore.Put(Texture) - if err != nil { - return err + if len(Texture) > 0 { + key, err := blobstore.Put(Texture) + if err != nil { + return err + } + user.TextureBlob = key } - user.TextureBlob = key user.LastActive = uint64(LastActive) user.LastChannelId = LastChannel @@ -428,3 +486,43 @@ func populateUsers(server *Server, db *sqlite.Conn) (err os.Error) { return } + +// Populate bans +func populateBans(server *Server, db *sqlite.Conn) (err os.Error) { + stmt, err := db.Prepare("SELECT base, mask, name, hash, reason, start, duration FROM bans WHERE server_id=?") + if err != nil { + return + } + + err = stmt.Exec(server.Id) + if err != nil { + return err + } + + for stmt.Next() { + var ( + Ban ban.Ban + IP []byte + StartDate string + Duration int64 + ) + + err = stmt.Scan(&IP, &Ban.Mask, &Ban.Username, &Ban.CertHash, &Ban.Reason, &StartDate, &Duration) + if err != nil { + return err + } + + if len(IP) == 16 && IP[10] == 0xff && IP[11] == 0xff { + Ban.IP = net.IPv4(IP[12], IP[13], IP[14], IP[15]) + } else { + Ban.IP = IP + } + + Ban.SetISOStartDate(StartDate) + Ban.Duration = uint32(Duration) + + server.Bans = append(server.Bans, Ban) + } + + return +} diff --git a/murmurdb_null.go b/murmurdb_null.go new file mode 100644 index 0000000..3c0a534 --- /dev/null +++ b/murmurdb_null.go @@ -0,0 +1,13 @@ +// Copyright (c) 2011 The Grumble Authors +// The use of this source code is goverened by a BSD-style +// license that can be found in the LICENSE-file. + +package main + +import "os" + +const SQLiteSupport = false + +func MurmurImport(filename string) (err os.Error) { + return os.NewError("no sqlite support built in") +} \ No newline at end of file diff --git a/pkg/sqlite/Makefile b/pkg/sqlite/Makefile index 6a290bf..a0bed93 100644 --- a/pkg/sqlite/Makefile +++ b/pkg/sqlite/Makefile @@ -4,7 +4,7 @@ include $(GOROOT)/src/Make.inc -TARG=sqlite +TARG=grumble/sqlite CGOFILES=sqlite.go diff --git a/server.go b/server.go index 1ea431e..741ae2c 100644 --- a/server.go +++ b/server.go @@ -1184,7 +1184,7 @@ func (s *Server) ListenAndMurmur() { go s.SendUDP() // Create a new listening TLS socket. - cert, err := tls.LoadX509KeyPair(filepath.Join(*datadir, "cert"), filepath.Join(*datadir, "key")) + cert, err := tls.LoadX509KeyPair(filepath.Join(Args.DataDir, "cert"), filepath.Join(Args.DataDir, "key")) if err != nil { s.Printf("Unable to load x509 key pair: %v", err) return