Add support for importing a server from a Murmur SQLite database. Add support for seralizing a server to disk 'freezing' (currently zlib-JSON based).

This commit is contained in:
Mikkel Krautz 2011-04-08 13:21:47 +02:00
parent 2eb5516d31
commit 3ae9881d91
11 changed files with 130730 additions and 55 deletions

View file

@ -9,10 +9,20 @@ TARG = grumble
PACKAGES = \ PACKAGES = \
pkg/packetdatastream \ pkg/packetdatastream \
pkg/cryptstate \ pkg/cryptstate \
pkg/mumbleproto pkg/mumbleproto \
pkg/sqlite
GCFLAGS = -Ipkg/cryptstate/_obj -Ipkg/packetdatastream/_obj -Ipkg/mumbleproto/_obj GCFLAGS = \
LDFLAGS = -Lpkg/cryptstate/_obj -Lpkg/packetdatastream/_obj -Lpkg/mumbleproto/_obj -Ipkg/cryptstate/_obj \
-Ipkg/packetdatastream/_obj \
-Ipkg/mumbleproto/_obj \
-Ipkg/sqlite/_obj
LDFLAGS = \
-Lpkg/cryptstate/_obj \
-Lpkg/packetdatastream/_obj \
-Lpkg/mumbleproto/_obj \
-Lpkg/sqlite/_obj
GOFILES = \ GOFILES = \
grumble.go \ grumble.go \
@ -22,7 +32,9 @@ GOFILES = \
client.go \ client.go \
channel.go \ channel.go \
acl.go \ acl.go \
group.go group.go \
murmurdb.go \
json.go
.PHONY: grumble .PHONY: grumble
grumble: pkg grumble: pkg

View file

@ -51,12 +51,30 @@ func (group *Group) AddContains(id int) (ok bool) {
return return
} }
// Get the list of user ids in the Add set.
func (group *Group) AddUsers() []int {
users := []int{}
for uid, _ := range group.Add {
users = append(users, uid)
}
return users
}
// Check whether the Remove set contains id. // Check whether the Remove set contains id.
func (group *Group) RemoveContains(id int) (ok bool) { func (group *Group) RemoveContains(id int) (ok bool) {
_, ok = group.Remove[id] _, ok = group.Remove[id]
return return
} }
// Get the list of user ids in the Remove set.
func (group *Group) RemoveUsers() []int {
users := []int{}
for uid, _ := range group.Remove {
users = append(users, uid)
}
return users
}
// Check whether the Temporary set contains id. // Check whether the Temporary set contains id.
func (group *Group) TemporaryContains(id int) (ok bool) { func (group *Group) TemporaryContains(id int) (ok bool) {
_, ok = group.Temporary[id] _, ok = group.Temporary[id]

View file

@ -9,53 +9,160 @@ import (
"fmt" "fmt"
"os" "os"
"log" "log"
"mumbleproto" "json"
"goprotobuf.googlecode.com/hg/proto" "sqlite"
"compress/zlib"
"path/filepath"
) )
var help *bool = flag.Bool("help", false, "Show this help") var help *bool = flag.Bool("help", false, "Show this help")
var port *int = flag.Int("port", 64738, "Default port to listen on") var port *int = flag.Int("port", 64738, "Default port to listen on")
var host *string = flag.String("host", "0.0.0.0", "Default host to listen on") var host *string = flag.String("host", "0.0.0.0", "Default host to listen on")
var datadir *string = flag.String("datadir", "", "Directory to use for server storage")
var blobdir *string = flag.String("blobdir", "", "Directory to use for blob storage")
var sqlitedb *string = flag.String("murmurdb", "", "Path to murmur.sqlite to import server structure from")
var cleanup *bool = flag.Bool("clean", false, "Clean up existing data dir content before importing Murmur data")
func usage() { func Usage() {
fmt.Fprintf(os.Stderr, "usage: grumble [options]\n") fmt.Fprintf(os.Stderr, "usage: grumble [options]\n")
flag.PrintDefaults() flag.PrintDefaults()
} }
// Check that we're using a version of goprotobuf that is able to func MurmurImport(filename string) (err os.Error) {
// correctly encode empty byte slices. db, err := sqlite.Open(filename)
func checkProtoLib() { if err != nil {
us := &mumbleproto.UserState{} panic(err.String())
us.Texture = []byte{}
d, _ := proto.Marshal(us)
nus := &mumbleproto.UserState{}
proto.Unmarshal(d, nus)
if nus.Texture == nil {
log.Fatal("Unpatched version of goprotobuf. Grumble is refusing to run.")
} }
stmt, err := db.Prepare("SELECT server_id FROM servers")
if err != nil {
panic(err.String())
}
var servers []int64
var sid int64
for stmt.Next() {
stmt.Scan(&sid)
servers = append(servers, sid)
}
log.Printf("Found servers: %v (%v servers)", servers, len(servers))
for _, sid := range servers {
m, err := NewServerFromSQLite(sid, db)
if err != nil {
log.Printf("Unable to create server: %s", err.String())
return
}
f, err := os.Create(filepath.Join(*datadir, fmt.Sprintf("%v", sid)))
if err != nil {
log.Printf("%s", err.String())
return
}
zf, err := zlib.NewWriterLevel(f, zlib.BestCompression)
enc := json.NewEncoder(zf)
err = enc.Encode(m)
if err != nil {
log.Printf("%s", err.String())
return
}
zf.Close()
f.Close()
log.Printf("Successfully imported server %v", sid)
}
return
} }
func main() { func main() {
flag.Parse() flag.Parse()
if *help == true { if *help == true {
usage() Usage()
return return
} }
checkProtoLib() log.Printf("Grumble - Mumble server written in Go")
// Create our default server if len(*datadir) == 0 {
m, err := NewServer(*host, *port) *datadir = filepath.Join(os.Getenv("HOME"), ".grumble", "data")
}
log.Printf("Using data directory: %s", *datadir)
if len(*blobdir) == 0 {
*blobdir = filepath.Join(os.Getenv("HOME"), ".grumble", "blob")
}
log.Printf("Using blob directory: %s", *blobdir)
// Should we import data from a Murmur SQLite file?
if len(*sqlitedb) > 0 {
f, err := os.Open(*datadir)
if err != nil { 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 !*cleanup && len(names) > 0 {
log.Fatalf("Non-empty datadir. Refusing to import Murmur data.")
}
if *cleanup {
log.Printf("Cleaning up existing data directory")
for _, name := range names {
if err := os.Remove(filepath.Join(*datadir, name)); err != nil {
log.Fatalf("Unable to cleanup file: %s", name)
}
}
}
log.Printf("Importing Murmur data from '%s'", *sqlitedb)
if err = MurmurImport(*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 return
} }
// And launch it. f, err := os.Open(*datadir)
go m.ListenAndMurmur() if err != nil {
log.Fatalf("Murmur import failed: %s", err.String())
}
defer f.Close()
// Listen forever names, err := f.Readdirnames(-1)
if err != nil {
log.Fatalf("Murmur import failed: %s", err.String())
}
servers := make(map[int64]*Server)
for _, name := range names {
log.Printf("Loading server %v", name)
s, err := NewServerFromGrumbleDesc(filepath.Join(*datadir, name))
if err != nil {
log.Fatalf("Unable to load server: %s", err.String())
}
servers[s.Id] = s
go s.ListenAndMurmur()
}
if len(servers) > 0 {
// Sleep.
sleeper := make(chan int) sleeper := make(chan int)
zzz := <-sleeper zzz := <-sleeper
if zzz > 0 { if zzz > 0 {
} }
} }
}

190
json.go Normal file
View file

@ -0,0 +1,190 @@
package main
import (
"json"
"os"
"compress/zlib"
)
type jsonServer struct {
Id int "id"
MaxUsers int "max_user"
Channels []jsonChannel "channels"
}
type jsonChannel struct {
Id int "id"
Name string "name"
ParentId int "parent_id"
Position int64 "position"
InheritACL bool "inherit_acl"
ACL []jsonACL "acl"
Groups []jsonGroup "groups"
Description string "description"
DescriptionHash []byte "description_hash"
}
type jsonACL struct {
UserId int "user_id"
Group string "group"
ApplyHere bool "apply_here"
ApplySubs bool "apply_subs"
Allow uint32 "allow"
Deny uint32 "deny"
}
type jsonGroup struct {
Name string "name"
Inherit bool "inherit"
Inheritable bool "inheritable"
Add []int "add"
Remove []int "remove"
}
// Marshal a server into a JSON object
func (server *Server) MarshalJSON() (buf []byte, err os.Error) {
obj := make(map[string]interface{})
obj["id"] = server.Id
obj["max_user"] = server.MaxUsers
channels := []interface{}{}
for _, c := range server.Channels {
channels = append(channels, c)
}
obj["channels"] = channels
return json.Marshal(obj)
}
// Marshal a Channel into a JSON object
func (channel *Channel) MarshalJSON() (buf []byte, err os.Error) {
obj := make(map[string]interface{})
obj["id"] = channel.Id
obj["name"] = channel.Name
if channel.parent != nil {
obj["parent_id"] = channel.parent.Id
} else {
obj["parent_id"] = -1
}
obj["position"] = channel.Position
obj["inherit_acl"] = channel.InheritACL
obj["description"] = channel.Description
obj["description_hash"] = channel.DescriptionHash
obj["acl"] = channel.ACL
groups := []*Group{}
for _, grp := range channel.Groups {
groups = append(groups, grp)
}
obj["groups"] = groups
links := []int{}
for cid, _ := range channel.Links {
links = append(links, cid)
}
return json.Marshal(obj)
}
func (acl *ChannelACL) MarshalJSON() (buf []byte, err os.Error) {
obj := make(map[string]interface{})
obj["user_id"] = acl.UserId
obj["group"] = acl.Group
obj["apply_here"] = acl.ApplyHere
obj["apply_subs"] = acl.ApplySubs
obj["allow"] = acl.Allow
obj["deny"] = acl.Deny
return json.Marshal(obj)
}
func (group *Group) MarshalJSON() (buf []byte, err os.Error) {
obj := make(map[string]interface{})
obj["name"] = group.Name
obj["inherit"] = group.Inherit
obj["inheritable"] = group.Inheritable
obj["add"] = group.AddUsers()
obj["remove"] = group.RemoveUsers()
return json.Marshal(obj)
}
// Create a new Server from a Grumble zlib-compressed JSON description
func NewServerFromGrumbleDesc(filename string) (s *Server, err os.Error) {
descFile, err := os.Open(filename)
if err != nil {
return nil, err
}
defer descFile.Close()
zr, err := zlib.NewReader(descFile)
if err != nil {
return nil, err
}
srv := new(jsonServer)
decoder := json.NewDecoder(zr)
decoder.Decode(&srv)
s, err = NewServer(int64(srv.Id), "", int(DefaultPort+srv.Id-1))
if err != nil {
return nil, err
}
// Add all channels, but don't hook up parent/child relationships
// until all of them are loaded.
for _, jc := range srv.Channels {
c := NewChannel(jc.Id, jc.Name)
c.Position = int(jc.Position)
c.InheritACL = jc.InheritACL
c.Description = jc.Description
c.DescriptionHash = jc.DescriptionHash
for _, jacl := range jc.ACL {
acl := NewChannelACL(c)
acl.ApplyHere = jacl.ApplyHere
acl.ApplySubs = jacl.ApplySubs
acl.UserId = jacl.UserId
acl.Group = jacl.Group
acl.Deny = Permission(jacl.Deny)
acl.Allow = Permission(jacl.Allow)
c.ACL = append(c.ACL, acl)
}
for _, jgrp := range jc.Groups {
g := NewGroup(c, jgrp.Name)
g.Inherit = jgrp.Inherit
g.Inheritable = jgrp.Inheritable
for _, uid := range jgrp.Add {
g.Add[uid] = true
}
for _, uid := range jgrp.Remove {
g.Remove[uid] = true
}
c.Groups[g.Name] = g
}
s.Channels[c.Id] = c
}
// Hook up children with their parents.
for _, jc := range srv.Channels {
if jc.Id == 0 {
continue
}
childChan, exists := s.Channels[jc.Id]
if !exists {
return nil, os.NewError("Non-existant child channel")
}
parentChan, exists := s.Channels[jc.ParentId]
if !exists {
return nil, os.NewError("Non-existant parent channel")
}
parentChan.AddChild(childChan)
}
s.root = s.Channels[0]
return s, nil
}

View file

@ -146,7 +146,7 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
// Lookup channel for channel ID // Lookup channel for channel ID
if chanstate.ChannelId != nil { if chanstate.ChannelId != nil {
channel, ok = server.channels[int(*chanstate.ChannelId)] channel, ok = server.Channels[int(*chanstate.ChannelId)]
if !ok { if !ok {
client.Panic("Invalid channel specified in ChannelState message") client.Panic("Invalid channel specified in ChannelState message")
return return
@ -155,7 +155,7 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
// Lookup parent // Lookup parent
if chanstate.Parent != nil { if chanstate.Parent != nil {
parent, ok = server.channels[int(*chanstate.Parent)] parent, ok = server.Channels[int(*chanstate.Parent)]
if !ok { if !ok {
client.Panic("Invalid parent channel specified in ChannelState message") client.Panic("Invalid parent channel specified in ChannelState message")
return return
@ -235,7 +235,7 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
} }
// Add the new channel // Add the new channel
channel = server.NewChannel(name) channel = server.AddChannel(name)
channel.Description = description channel.Description = description
channel.Temporary = *chanstate.Temporary channel.Temporary = *chanstate.Temporary
channel.Position = int(*chanstate.Position) channel.Position = int(*chanstate.Position)
@ -384,13 +384,13 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
} }
// Add any valid channels to linkremove slice // Add any valid channels to linkremove slice
for _, cid := range chanstate.LinksRemove { for _, cid := range chanstate.LinksRemove {
if iter, ok := server.channels[int(cid)]; ok { if iter, ok := server.Channels[int(cid)]; ok {
linkremove = append(linkremove, iter) linkremove = append(linkremove, iter)
} }
} }
// Add any valid channels to linkadd slice // Add any valid channels to linkadd slice
for _, cid := range chanstate.LinksAdd { for _, cid := range chanstate.LinksAdd {
if iter, ok := server.channels[int(cid)]; ok { if iter, ok := server.Channels[int(cid)]; ok {
if !server.HasPermission(client, iter, LinkChannelPermission) { if !server.HasPermission(client, iter, LinkChannelPermission) {
client.sendPermissionDenied(client, iter, LinkChannelPermission) client.sendPermissionDenied(client, iter, LinkChannelPermission)
return return
@ -528,7 +528,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
// Does it have a channel ID? // Does it have a channel ID?
if userstate.ChannelId != nil { if userstate.ChannelId != nil {
// Destination channel // Destination channel
dstChan, ok := server.channels[int(*userstate.ChannelId)] dstChan, ok := server.Channels[int(*userstate.ChannelId)]
if !ok { if !ok {
return return
} }
@ -741,7 +741,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
} }
if userstate.ChannelId != nil { if userstate.ChannelId != nil {
channel, ok := server.channels[int(*userstate.ChannelId)] channel, ok := server.Channels[int(*userstate.ChannelId)]
if ok { if ok {
server.userEnterChannel(user, channel, userstate) server.userEnterChannel(user, channel, userstate)
broadcast = true broadcast = true
@ -820,7 +820,7 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) {
// Tree // Tree
for _, chanid := range txtmsg.TreeId { for _, chanid := range txtmsg.TreeId {
if channel, ok := server.channels[int(chanid)]; ok { if channel, ok := server.Channels[int(chanid)]; ok {
if !server.HasPermission(client, channel, TextMessagePermission) { if !server.HasPermission(client, channel, TextMessagePermission) {
client.sendPermissionDenied(client, channel, TextMessagePermission) client.sendPermissionDenied(client, channel, TextMessagePermission)
} }
@ -832,7 +832,7 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) {
// Direct-to-channel // Direct-to-channel
for _, chanid := range txtmsg.ChannelId { for _, chanid := range txtmsg.ChannelId {
if channel, ok := server.channels[int(chanid)]; ok { if channel, ok := server.Channels[int(chanid)]; ok {
if !server.HasPermission(client, channel, TextMessagePermission) { if !server.HasPermission(client, channel, TextMessagePermission) {
client.sendPermissionDenied(client, channel, TextMessagePermission) client.sendPermissionDenied(client, channel, TextMessagePermission)
return return
@ -874,7 +874,7 @@ func (server *Server) handleAclMessage(client *Client, msg *Message) {
} }
// Look up the channel this ACL message operates on. // Look up the channel this ACL message operates on.
channel, ok := server.channels[int(*acl.ChannelId)] channel, ok := server.Channels[int(*acl.ChannelId)]
if !ok { if !ok {
return return
} }
@ -1103,7 +1103,7 @@ func (server *Server) handlePermissionQuery(client *Client, msg *Message) {
return return
} }
channel := server.channels[int(*query.ChannelId)] channel := server.Channels[int(*query.ChannelId)]
server.sendClientPermissions(client, channel) server.sendClientPermissions(client, channel)
} }
@ -1157,7 +1157,7 @@ func (server *Server) handleRequestBlob(client *Client, msg *Message) {
// Request for channel descriptions // Request for channel descriptions
if len(blobreq.ChannelDescription) > 0 { if len(blobreq.ChannelDescription) > 0 {
for _, cid := range blobreq.ChannelDescription { for _, cid := range blobreq.ChannelDescription {
if channel, ok := server.channels[int(cid)]; ok { if channel, ok := server.Channels[int(cid)]; ok {
if len(channel.Description) > 0 { if len(channel.Description) > 0 {
chanstate.Reset() chanstate.Reset()
chanstate.ChannelId = proto.Uint32(uint32(channel.Id)) chanstate.ChannelId = proto.Uint32(uint32(channel.Id))

270
murmurdb.go Normal file
View file

@ -0,0 +1,270 @@
package main
// This file implements a Server that can be created from a Murmur SQLite file.
// This is read-only, so it's not generally useful. It's meant as a convenient
// way to import a Murmur server into Grumble, to be able to dump the structure of the
// SQLite datbase into a format that Grumble can understand.
import (
"os"
"sqlite"
"strconv"
)
const (
ChannelInfoDescription int = iota
ChannelInfoPosition
)
const (
UserInfoName int = iota
UserInfoEmail
UserInfoComment
UserInfoHash
UserInfoPassword
UserInfoLastActive
)
// 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))
if err != nil {
return nil, err
}
err = populateChannelsFromDatabase(s, db, 0)
if err != nil {
return nil, err
}
err = populateChannelLinkInfo(s, db)
if err != nil {
return nil, err
}
return
}
// Populate the Server with Channels from the database.
func populateChannelsFromDatabase(server *Server, db *sqlite.Conn, parentId int) os.Error {
parent, exists := server.Channels[parentId]
if !exists {
return os.NewError("Non-existant parent")
}
stmt, err := db.Prepare("SELECT channel_id, name, inheritacl FROM channels WHERE server_id=? AND parent_id=?")
if err != nil {
return err
}
err = stmt.Exec(server.Id, parentId)
if err != nil {
return err
}
for stmt.Next() {
var (
name string
chanid int
inherit bool
)
err = stmt.Scan(&chanid, &name, &inherit)
if err != nil {
return err
}
c := server.NewChannel(chanid, name)
c.InheritACL = inherit
parent.AddChild(c)
}
// Add channel_info
for _, c := range parent.children {
stmt, err = db.Prepare("SELECT value FROM channel_info WHERE server_id=? AND channel_id=? AND key=?")
if err != nil {
return err
}
// Fetch description
if err := stmt.Exec(server.Id, c.Id, ChannelInfoDescription); err != nil {
return err
}
for stmt.Next() {
var description string
err = stmt.Scan(&description)
if err != nil {
return err
}
c.Description = description
}
if err := stmt.Reset(); err != nil {
return err
}
// Fetch position
if err := stmt.Exec(server.Id, c.Id, ChannelInfoPosition); err != nil {
return err
}
for stmt.Next() {
var pos int
if err := stmt.Scan(&pos); err != nil {
return err
}
c.Position = pos
}
}
// Add ACLs
for _, c := range parent.children {
stmt, err = db.Prepare("SELECT user_id, group_name, apply_here, apply_sub, grantpriv, revokepriv FROM acl WHERE server_id=? AND channel_id=? ORDER BY priority")
if err != nil {
return err
}
if err := stmt.Exec(server.Id, c.Id); err != nil {
return err
}
for stmt.Next() {
var (
UserId string
Group string
ApplyHere bool
ApplySub bool
Allow int64
Deny int64
)
if err := stmt.Scan(&UserId, &Group, &ApplyHere, &ApplySub, &Allow, &Deny); err != nil {
return err
}
acl := NewChannelACL(c)
acl.ApplyHere = ApplyHere
acl.ApplySubs = ApplySub
if len(UserId) > 0 {
acl.UserId, err = strconv.Atoi(UserId)
if err != nil {
return err
}
} else if len(Group) > 0 {
acl.Group = Group
} else {
return os.NewError("Invalid ACL: Neither Group or UserId specified")
}
acl.Deny = Permission(Deny)
acl.Allow = Permission(Allow)
c.ACL = append(c.ACL, acl)
}
}
// Add groups
groups := make(map[int64]*Group)
for _, c := range parent.children {
stmt, err = db.Prepare("SELECT group_id, name, inherit, inheritable FROM groups WHERE server_id=? AND channel_id=?")
if err != nil {
return err
}
if err := stmt.Exec(server.Id, c.Id); err != nil {
return err
}
for stmt.Next() {
var (
GroupId int64
Name string
Inherit bool
Inheritable bool
)
if err := stmt.Scan(&GroupId, &Name, &Inherit, &Inheritable); err != nil {
return err
}
g := NewGroup(c, Name)
g.Inherit = Inherit
g.Inheritable = Inheritable
c.Groups[g.Name] = g
groups[GroupId] = g
}
}
// Add group members
for gid, grp := range groups {
stmt, err = db.Prepare("SELECT user_id, addit FROM group_members WHERE server_id=? AND group_id=?")
if err != nil {
return err
}
if err := stmt.Exec(server.Id, gid); err != nil {
return err
}
for stmt.Next() {
var (
UserId int64
Add bool
)
if err := stmt.Scan(&UserId, &Add); err != nil {
return err
}
if Add {
grp.Add[int(UserId)] = true
} else {
grp.Remove[int(UserId)] = true
}
}
}
// Add subchannels
for id, _ := range parent.children {
err = populateChannelsFromDatabase(server, db, id)
if err != nil {
return err
}
}
return nil
}
// Link a Server's channels together
func populateChannelLinkInfo(server *Server, db *sqlite.Conn) (err os.Error) {
stmt, err := db.Prepare("SELECT channel_id, link_id FROM channel_links WHERE server_id=?")
if err != nil {
return err
}
if err := stmt.Exec(server.Id); err != nil {
return err
}
for stmt.Next() {
var (
ChannelId int
LinkId int
)
if err := stmt.Scan(&ChannelId, &LinkId); err != nil {
return err
}
channel, exists := server.Channels[ChannelId]
if !exists {
return os.NewError("Attempt to perform link operation on non-existant channel.")
}
other, exists := server.Channels[LinkId]
if !exists {
return os.NewError("Attempt to perform link operation on non-existant channel.")
}
server.LinkChannels(channel, other)
}
return nil
}

16
pkg/sqlite/Makefile Normal file
View file

@ -0,0 +1,16 @@
# Copyright 2010 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.inc
TARG=sqlite
CGOFILES=sqlite.go
CGO_OFILES=sqlite3.o
ifeq ($(GOOS),linux)
CGO_LDFLAGS=-lpthread -ldl
endif
include $(GOROOT)/src/Make.pkg

402
pkg/sqlite/sqlite.go Normal file
View file

@ -0,0 +1,402 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sqlite provides access to the SQLite library, version 3.
package sqlite
/*
#include <sqlite3.h>
#include <stdlib.h>
// These wrappers are necessary because SQLITE_TRANSIENT
// is a pointer constant, and cgo doesn't translate them correctly.
// The definition in sqlite3.h is:
//
// typedef void (*sqlite3_destructor_type)(void*);
// #define SQLITE_STATIC ((sqlite3_destructor_type)0)
// #define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
}
static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
}
*/
import "C"
import (
"fmt"
"os"
"reflect"
"strconv"
"unsafe"
"time"
)
type Errno int
func (e Errno) String() string {
s := errText[e]
if s == "" {
return fmt.Sprintf("errno %d", int(e))
}
return s
}
var (
ErrError os.Error = Errno(1) // /* SQL error or missing database */
ErrInternal os.Error = Errno(2) // /* Internal logic error in SQLite */
ErrPerm os.Error = Errno(3) // /* Access permission denied */
ErrAbort os.Error = Errno(4) // /* Callback routine requested an abort */
ErrBusy os.Error = Errno(5) // /* The database file is locked */
ErrLocked os.Error = Errno(6) // /* A table in the database is locked */
ErrNoMem os.Error = Errno(7) // /* A malloc() failed */
ErrReadOnly os.Error = Errno(8) // /* Attempt to write a readonly database */
ErrInterrupt os.Error = Errno(9) // /* Operation terminated by sqlite3_interrupt()*/
ErrIOErr os.Error = Errno(10) // /* Some kind of disk I/O error occurred */
ErrCorrupt os.Error = Errno(11) // /* The database disk image is malformed */
ErrFull os.Error = Errno(13) // /* Insertion failed because database is full */
ErrCantOpen os.Error = Errno(14) // /* Unable to open the database file */
ErrEmpty os.Error = Errno(16) // /* Database is empty */
ErrSchema os.Error = Errno(17) // /* The database schema changed */
ErrTooBig os.Error = Errno(18) // /* String or BLOB exceeds size limit */
ErrConstraint os.Error = Errno(19) // /* Abort due to constraint violation */
ErrMismatch os.Error = Errno(20) // /* Data type mismatch */
ErrMisuse os.Error = Errno(21) // /* Library used incorrectly */
ErrNolfs os.Error = Errno(22) // /* Uses OS features not supported on host */
ErrAuth os.Error = Errno(23) // /* Authorization denied */
ErrFormat os.Error = Errno(24) // /* Auxiliary database format error */
ErrRange os.Error = Errno(25) // /* 2nd parameter to sqlite3_bind out of range */
ErrNotDB os.Error = Errno(26) // /* File opened that is not a database file */
Row = Errno(100) // /* sqlite3_step() has another row ready */
Done = Errno(101) // /* sqlite3_step() has finished executing */
)
var errText = map[Errno]string {
1: "SQL error or missing database",
2: "Internal logic error in SQLite",
3: "Access permission denied",
4: "Callback routine requested an abort",
5: "The database file is locked",
6: "A table in the database is locked",
7: "A malloc() failed",
8: "Attempt to write a readonly database",
9: "Operation terminated by sqlite3_interrupt()*/",
10: "Some kind of disk I/O error occurred",
11: "The database disk image is malformed",
12: "NOT USED. Table or record not found",
13: "Insertion failed because database is full",
14: "Unable to open the database file",
15: "NOT USED. Database lock protocol error",
16: "Database is empty",
17: "The database schema changed",
18: "String or BLOB exceeds size limit",
19: "Abort due to constraint violation",
20: "Data type mismatch",
21: "Library used incorrectly",
22: "Uses OS features not supported on host",
23: "Authorization denied",
24: "Auxiliary database format error",
25: "2nd parameter to sqlite3_bind out of range",
26: "File opened that is not a database file",
100: "sqlite3_step() has another row ready",
101: "sqlite3_step() has finished executing",
}
func (c *Conn) error(rv C.int) os.Error {
if c == nil || c.db == nil {
return os.NewError("nil sqlite database")
}
if rv == 0 {
return nil
}
if rv == 21 { // misuse
return Errno(rv)
}
return os.NewError(Errno(rv).String() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
}
type Conn struct {
db *C.sqlite3
}
func Version() string {
p := C.sqlite3_libversion();
return C.GoString(p);
}
func Open(filename string) (*Conn, os.Error) {
if C.sqlite3_threadsafe() == 0 {
return nil, os.NewError("sqlite library was not compiled for thread-safe operation")
}
var db *C.sqlite3
name := C.CString(filename)
defer C.free(unsafe.Pointer(name))
rv := C.sqlite3_open_v2(name, &db,
C.SQLITE_OPEN_FULLMUTEX |
C.SQLITE_OPEN_READWRITE |
C.SQLITE_OPEN_CREATE,
nil)
if rv != 0 {
return nil, Errno(rv)
}
if db == nil {
return nil, os.NewError("sqlite succeeded without returning a database")
}
return &Conn{db}, nil
}
func NewBackup(dst *Conn, dstTable string, src *Conn, srcTable string) (*Backup, os.Error) {
dname := C.CString(dstTable)
sname := C.CString(srcTable)
defer C.free(unsafe.Pointer(dname))
defer C.free(unsafe.Pointer(sname))
sb := C.sqlite3_backup_init(dst.db, dname, src.db, sname)
if sb == nil {
return nil, dst.error(C.sqlite3_errcode(dst.db))
}
return &Backup{sb, dst, src}, nil
}
type Backup struct {
sb *C.sqlite3_backup
dst, src *Conn
}
func (b *Backup) Step(npage int) os.Error {
rv := C.sqlite3_backup_step(b.sb, C.int(npage))
if rv == 0 || Errno(rv) == ErrBusy || Errno(rv) == ErrLocked {
return nil
}
return Errno(rv)
}
type BackupStatus struct {
Remaining int
PageCount int
}
func (b *Backup) Status() BackupStatus {
return BackupStatus{int(C.sqlite3_backup_remaining(b.sb)), int(C.sqlite3_backup_pagecount(b.sb))}
}
func (b *Backup) Run(npage int, sleepNs int64, c chan<- BackupStatus) os.Error {
var err os.Error
for {
err = b.Step(npage)
if err != nil {
break
}
if c != nil {
c <- b.Status()
}
time.Sleep(sleepNs)
}
return b.dst.error(C.sqlite3_errcode(b.dst.db))
}
func (b *Backup) Close() os.Error {
if b.sb == nil {
return os.EINVAL
}
C.sqlite3_backup_finish(b.sb)
b.sb = nil
return nil
}
func (c *Conn) BusyTimeout(ms int) os.Error {
rv := C.sqlite3_busy_timeout(c.db, C.int(ms))
if rv == 0 {
return nil
}
return Errno(rv)
}
func (c *Conn) Exec(cmd string, args ...interface{}) os.Error {
s, err := c.Prepare(cmd)
if err != nil {
return err
}
defer s.Finalize()
err = s.Exec(args...)
if err != nil {
return err
}
rv := C.sqlite3_step(s.stmt)
if Errno(rv) != Done {
return c.error(rv)
}
return nil
}
type Stmt struct {
c *Conn
stmt *C.sqlite3_stmt
err os.Error
t0 int64
sql string
args string
}
func (c *Conn) Prepare(cmd string) (*Stmt, os.Error) {
if c == nil || c.db == nil {
return nil, os.NewError("nil sqlite database")
}
cmdstr := C.CString(cmd)
defer C.free(unsafe.Pointer(cmdstr))
var stmt *C.sqlite3_stmt
var tail *C.char
rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &stmt, &tail)
if rv != 0 {
return nil, c.error(rv)
}
return &Stmt{c: c, stmt: stmt, sql: cmd, t0: time.Nanoseconds()}, nil
}
func (s *Stmt) Exec(args ...interface{}) os.Error {
s.args = fmt.Sprintf(" %v", []interface{}(args))
rv := C.sqlite3_reset(s.stmt)
if rv != 0 {
return s.c.error(rv)
}
n := int(C.sqlite3_bind_parameter_count(s.stmt))
if n != len(args) {
return os.NewError(fmt.Sprintf("incorrect argument count for Stmt.Exec: have %d want %d", len(args), n))
}
for i, v := range args {
var str string
switch v := v.(type) {
case []byte:
var p *byte
if len(v) > 0 {
p = &v[0]
}
if rv := C.my_bind_blob(s.stmt, C.int(i+1), unsafe.Pointer(p), C.int(len(v))); rv != 0 {
return s.c.error(rv)
}
continue
case bool:
if v {
str = "1"
} else {
str = "0"
}
default:
str = fmt.Sprint(v)
}
cstr := C.CString(str)
rv := C.my_bind_text(s.stmt, C.int(i+1), cstr, C.int(len(str)))
C.free(unsafe.Pointer(cstr))
if rv != 0 {
return s.c.error(rv)
}
}
return nil
}
func (s *Stmt) Error() os.Error {
return s.err
}
func (s *Stmt) Next() bool {
rv := C.sqlite3_step(s.stmt)
err := Errno(rv)
if err == Row {
return true
}
if err != Done {
s.err = s.c.error(rv)
}
return false
}
func (s *Stmt) Reset() os.Error {
C.sqlite3_reset(s.stmt)
return nil
}
func (s *Stmt) Scan(args ...interface{}) os.Error {
n := int(C.sqlite3_column_count(s.stmt))
if n != len(args) {
return os.NewError(fmt.Sprintf("incorrect argument count for Stmt.Scan: have %d want %d", len(args), n))
}
for i, v := range args {
n := C.sqlite3_column_bytes(s.stmt, C.int(i))
p := C.sqlite3_column_blob(s.stmt, C.int(i))
if p == nil && n > 0 {
return os.NewError("got nil blob")
}
var data []byte
if n > 0 {
data = (*[1<<30]byte)(unsafe.Pointer(p))[0:n]
}
switch v := v.(type) {
case *[]byte:
*v = data
case *string:
*v = string(data)
case *bool:
*v = string(data) == "1"
case *int:
x, err := strconv.Atoi(string(data))
if err != nil {
return os.NewError("arg " + strconv.Itoa(i) + " as int: " + err.String())
}
*v = x
case *int64:
x, err := strconv.Atoi64(string(data))
if err != nil {
return os.NewError("arg " + strconv.Itoa(i) + " as int64: " + err.String())
}
*v = x
case *float64:
x, err := strconv.Atof64(string(data))
if err != nil {
return os.NewError("arg " + strconv.Itoa(i) + " as float64: " + err.String())
}
*v = x
default:
return os.NewError("unsupported type in Scan: " + reflect.Typeof(v).String())
}
}
return nil
}
func (s *Stmt) SQL() string {
return s.sql + s.args
}
func (s *Stmt) Nanoseconds() int64 {
return time.Nanoseconds() - s.t0
}
func (s *Stmt) Finalize() os.Error {
rv := C.sqlite3_finalize(s.stmt)
if rv != 0 {
return s.c.error(rv)
}
return nil
}
func (c *Conn) Close() os.Error {
if c == nil || c.db == nil {
return os.NewError("nil sqlite database")
}
rv := C.sqlite3_close(c.db)
if rv != 0 {
return c.error(rv)
}
c.db = nil
return nil
}

123309
pkg/sqlite/sqlite3.c Normal file

File diff suppressed because it is too large Load diff

6329
pkg/sqlite/sqlite3.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -25,8 +25,6 @@ const DefaultPort = 64738
const UDPPacketSize = 1024 const UDPPacketSize = 1024
const CeltCompatBitstream = -2147483637 const CeltCompatBitstream = -2147483637
// Client connection states
const ( const (
StateClientConnected = iota StateClientConnected = iota
StateServerSentVersion StateServerSentVersion
@ -37,6 +35,7 @@ const (
// A Murmur server instance // A Murmur server instance
type Server struct { type Server struct {
Id int64
listener tls.Listener listener tls.Listener
address string address string
port int port int
@ -67,16 +66,17 @@ type Server struct {
// Channels // Channels
chanid int chanid int
root *Channel root *Channel
channels map[int]*Channel Channels map[int]*Channel
// ACL cache // ACL cache
aclcache ACLCache aclcache ACLCache
} }
// Allocate a new Murmur instance // Allocate a new Murmur instance
func NewServer(addr string, port int) (s *Server, err os.Error) { func NewServer(id int64, addr string, port int) (s *Server, err os.Error) {
s = new(Server) s = new(Server)
s.Id = id
s.address = addr s.address = addr
s.port = port s.port = port
@ -92,16 +92,19 @@ func NewServer(addr string, port int) (s *Server, err os.Error) {
s.MaxBandwidth = 300000 s.MaxBandwidth = 300000
s.MaxUsers = 10 s.MaxUsers = 10
s.channels = make(map[int]*Channel) s.Channels = make(map[int]*Channel)
s.root = s.NewChannel("Root") s.root = s.NewChannel(0, "Root")
subChan := s.NewChannel("SubChannel")
s.root.AddChild(subChan) /*
err = s.addChannelsFromDB(0)
if err != nil {
return nil, err
}
*/
s.aclcache = NewACLCache() s.aclcache = NewACLCache()
go s.handler()
return return
} }
@ -172,11 +175,28 @@ func (server *Server) RemoveClient(client *Client, kicked bool) {
} }
} }
// Add a new channel to the server. // Add an existing channel to the Server. (Do not arbitrarily pick an ID)
func (server *Server) NewChannel(name string) (channel *Channel) { func (server *Server) NewChannel(id int, name string) (channel *Channel) {
_, exists := server.Channels[id]
if exists {
// fime(mkrautz): Handle duplicates
return nil
}
channel = NewChannel(id, name)
server.Channels[id] = channel
if id > server.chanid {
server.chanid = id + 1
}
return
}
// Add a new channel to the server. Automatically assign it a channel ID.
func (server *Server) AddChannel(name string) (channel *Channel) {
channel = NewChannel(server.chanid, name) channel = NewChannel(server.chanid, name)
server.channels[channel.Id] = channel server.Channels[channel.Id] = channel
server.chanid += 1
return return
} }
@ -186,7 +206,7 @@ func (server *Server) RemoveChanel(channel *Channel) {
log.Printf("Attempted to remove root channel.") log.Printf("Attempted to remove root channel.")
return return
} }
server.channels[channel.Id] = nil, false server.Channels[channel.Id] = nil, false
} }
// Link two channels // Link two channels
@ -687,6 +707,8 @@ func (server *Server) userEnterChannel(client *Client, channel *Channel, usersta
// The accept loop of the server. // The accept loop of the server.
func (s *Server) ListenAndMurmur() { func (s *Server) ListenAndMurmur() {
// Launch the event handler goroutine
go s.handler()
// Setup our UDP listener and spawn our reader and writer goroutines // Setup our UDP listener and spawn our reader and writer goroutines
s.SetupUDP() s.SetupUDP()