Explorar el Código

db: Remove SQLite db, hopefully looking up clients still works.Channel persistence is broken by this, will fix it later.

tags/v0.1.0
Daniel Oaks hace 7 años
padre
commit
ae69ef5cd6
Se han modificado 7 ficheros con 98 adiciones y 197 borrados
  1. 2
    0
      CHANGELOG.md
  2. 24
    19
      irc/channel.go
  3. 23
    94
      irc/client_lookup_set.go
  4. 1
    5
      irc/config.go
  5. 13
    40
      irc/database.go
  6. 31
    35
      irc/server.go
  7. 4
    4
      oragono.go

+ 2
- 0
CHANGELOG.md Ver fichero

14
 
14
 
15
 ### Added
15
 ### Added
16
 * Added YAML config file format.
16
 * Added YAML config file format.
17
+* Added buntdb key-value store for persistent data.
17
 * Added native SSL/TLS support (thanks to @edmand).
18
 * Added native SSL/TLS support (thanks to @edmand).
18
 * Added ability to generate testing certificates from the command line.
19
 * Added ability to generate testing certificates from the command line.
19
 * Added support for looking up usernames with [ident](https://tools.ietf.org/html/rfc1413) on client connection.
20
 * Added support for looking up usernames with [ident](https://tools.ietf.org/html/rfc1413) on client connection.
35
 
36
 
36
 ### Removed
37
 ### Removed
37
 * Removed gitconfig configuration format [replaced with YAML].
38
 * Removed gitconfig configuration format [replaced with YAML].
39
+* Removed sqlite database [replaced with buntdb key-value store].
38
 * Removed `THEATER` command (it broke and I'm not that interested in putting the work in to get it working again with the aim of this project. PRs accepted).
40
 * Removed `THEATER` command (it broke and I'm not that interested in putting the work in to get it working again with the aim of this project. PRs accepted).
39
 
41
 
40
 ### Fixed
42
 ### Fixed

+ 24
- 19
irc/channel.go Ver fichero

424
 }
424
 }
425
 
425
 
426
 func (channel *Channel) Persist() (err error) {
426
 func (channel *Channel) Persist() (err error) {
427
-	if channel.flags[Persistent] {
428
-		//TODO(dan): Save topicSetBy/topicSetTime and createdTime
429
-		_, err = channel.server.db.Exec(`
430
-            INSERT OR REPLACE INTO channel
431
-              (name, flags, key, topic, user_limit, ban_list, except_list,
432
-               invite_list)
433
-              VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
434
-			channel.name.String(), channel.flags.String(), channel.key,
435
-			channel.topic, channel.userLimit, channel.lists[BanMask].String(),
436
-			channel.lists[ExceptMask].String(), channel.lists[InviteMask].String())
437
-	} else {
438
-		_, err = channel.server.db.Exec(`
439
-            DELETE FROM channel WHERE name = ?`, channel.name.String())
440
-	}
441
-
442
-	if err != nil {
443
-		Log.error.Println("Channel.Persist:", channel, err)
444
-	}
445
-
446
 	return
427
 	return
428
+
429
+	//TODO(dan): Fix persistence
430
+	/*
431
+			if channel.flags[Persistent] {
432
+				//TODO(dan): Save topicSetBy/topicSetTime and createdTime
433
+				_, err = channel.server.db.Exec(`
434
+		            INSERT OR REPLACE INTO channel
435
+		              (name, flags, key, topic, user_limit, ban_list, except_list,
436
+		               invite_list)
437
+		              VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
438
+					channel.name.String(), channel.flags.String(), channel.key,
439
+					channel.topic, channel.userLimit, channel.lists[BanMask].String(),
440
+					channel.lists[ExceptMask].String(), channel.lists[InviteMask].String())
441
+			} else {
442
+				_, err = channel.server.db.Exec(`
443
+		            DELETE FROM channel WHERE name = ?`, channel.name.String())
444
+			}
445
+
446
+			if err != nil {
447
+				Log.error.Println("Channel.Persist:", channel, err)
448
+			}
449
+
450
+			return
451
+	*/
447
 }
452
 }
448
 
453
 
449
 func (channel *Channel) Notice(client *Client, message string) {
454
 func (channel *Channel) Notice(client *Client, message string) {

+ 23
- 94
irc/client_lookup_set.go Ver fichero

4
 package irc
4
 package irc
5
 
5
 
6
 import (
6
 import (
7
-	"database/sql"
8
 	"errors"
7
 	"errors"
9
-	"log"
10
 	"regexp"
8
 	"regexp"
11
 	"strings"
9
 	"strings"
10
+
11
+	"github.com/DanielOaks/girc-go/ircmatch"
12
 )
12
 )
13
 
13
 
14
 var (
14
 var (
15
 	ErrNickMissing      = errors.New("nick missing")
15
 	ErrNickMissing      = errors.New("nick missing")
16
 	ErrNicknameInUse    = errors.New("nickname in use")
16
 	ErrNicknameInUse    = errors.New("nickname in use")
17
 	ErrNicknameMismatch = errors.New("nickname mismatch")
17
 	ErrNicknameMismatch = errors.New("nickname mismatch")
18
-	wildMaskExpr        = regexp.MustCompile(`\*|\?`)
19
-	likeQuoter          = strings.NewReplacer(
20
-		`\`, `\\`,
21
-		`%`, `\%`,
22
-		`_`, `\_`,
23
-		`*`, `%`,
24
-		`?`, `_`)
25
 )
18
 )
26
 
19
 
27
-func HasWildcards(mask string) bool {
28
-	return wildMaskExpr.MatchString(mask)
29
-}
30
-
31
 func ExpandUserHost(userhost Name) (expanded Name) {
20
 func ExpandUserHost(userhost Name) (expanded Name) {
32
 	expanded = userhost
21
 	expanded = userhost
33
 	// fill in missing wildcards for nicks
22
 	// fill in missing wildcards for nicks
23
+	//TODO(dan): this would fail with dan@lol, do we want to accommodate that?
34
 	if !strings.Contains(expanded.String(), "!") {
24
 	if !strings.Contains(expanded.String(), "!") {
35
 		expanded += "!*"
25
 		expanded += "!*"
36
 	}
26
 	}
40
 	return
30
 	return
41
 }
31
 }
42
 
32
 
43
-func QuoteLike(userhost Name) string {
44
-	return likeQuoter.Replace(userhost.String())
45
-}
46
-
47
 type ClientLookupSet struct {
33
 type ClientLookupSet struct {
48
 	byNick map[Name]*Client
34
 	byNick map[Name]*Client
49
-	db     *ClientDB
50
 }
35
 }
51
 
36
 
52
 func NewClientLookupSet() *ClientLookupSet {
37
 func NewClientLookupSet() *ClientLookupSet {
53
 	return &ClientLookupSet{
38
 	return &ClientLookupSet{
54
 		byNick: make(map[Name]*Client),
39
 		byNick: make(map[Name]*Client),
55
-		db:     NewClientDB(),
56
 	}
40
 	}
57
 }
41
 }
58
 
42
 
68
 		return ErrNicknameInUse
52
 		return ErrNicknameInUse
69
 	}
53
 	}
70
 	clients.byNick[client.Nick().ToLower()] = client
54
 	clients.byNick[client.Nick().ToLower()] = client
71
-	clients.db.Add(client)
72
 	return nil
55
 	return nil
73
 }
56
 }
74
 
57
 
80
 		return ErrNicknameMismatch
63
 		return ErrNicknameMismatch
81
 	}
64
 	}
82
 	delete(clients.byNick, client.nick.ToLower())
65
 	delete(clients.byNick, client.nick.ToLower())
83
-	clients.db.Remove(client)
84
 	return nil
66
 	return nil
85
 }
67
 }
86
 
68
 
87
 func (clients *ClientLookupSet) FindAll(userhost Name) (set ClientSet) {
69
 func (clients *ClientLookupSet) FindAll(userhost Name) (set ClientSet) {
88
-	userhost = ExpandUserHost(userhost)
89
 	set = make(ClientSet)
70
 	set = make(ClientSet)
90
-	rows, err := clients.db.db.Query(
91
-		`SELECT nickname FROM client WHERE userhost LIKE ? ESCAPE '\'`,
92
-		QuoteLike(userhost))
93
-	if err != nil {
94
-		Log.error.Println("ClientLookupSet.FindAll.Query:", err)
95
-		return
96
-	}
97
-	for rows.Next() {
98
-		var sqlNickname string
99
-		err := rows.Scan(&sqlNickname)
100
-		if err != nil {
101
-			Log.error.Println("ClientLookupSet.FindAll.Scan:", err)
102
-			return
103
-		}
104
-		nickname := Name(sqlNickname)
105
-		client := clients.Get(nickname)
106
-		if client == nil {
107
-			Log.error.Println("ClientLookupSet.FindAll: missing client:", nickname)
108
-			continue
71
+
72
+	userhost = ExpandUserHost(userhost)
73
+	matcher := ircmatch.MakeMatch(userhost.String())
74
+
75
+	var casemappedNickMask string
76
+	for _, client := range clients.byNick {
77
+		casemappedNickMask = NewName(client.nickMaskString).String()
78
+		if matcher.Match(casemappedNickMask) {
79
+			set.Add(client)
109
 		}
80
 		}
110
-		set.Add(client)
111
 	}
81
 	}
112
-	return
82
+
83
+	return set
113
 }
84
 }
114
 
85
 
115
 func (clients *ClientLookupSet) Find(userhost Name) *Client {
86
 func (clients *ClientLookupSet) Find(userhost Name) *Client {
116
 	userhost = ExpandUserHost(userhost)
87
 	userhost = ExpandUserHost(userhost)
117
-	row := clients.db.db.QueryRow(
118
-		`SELECT nickname FROM client WHERE userhost LIKE ? ESCAPE '\' LIMIT 1`,
119
-		QuoteLike(userhost))
120
-	var nickname Name
121
-	err := row.Scan(&nickname)
122
-	if err != nil {
123
-		Log.error.Println("ClientLookupSet.Find:", err)
124
-		return nil
125
-	}
126
-	return clients.Get(nickname)
127
-}
128
-
129
-//
130
-// client db
131
-//
88
+	matcher := ircmatch.MakeMatch(userhost.String())
132
 
89
 
133
-type ClientDB struct {
134
-	db *sql.DB
135
-}
136
-
137
-func NewClientDB() *ClientDB {
138
-	db := &ClientDB{
139
-		db: OpenDB(":memory:"),
140
-	}
141
-	stmts := []string{
142
-		`CREATE TABLE client (
143
-          nickname TEXT NOT NULL COLLATE NOCASE UNIQUE,
144
-          userhost TEXT NOT NULL COLLATE NOCASE,
145
-          UNIQUE (nickname, userhost) ON CONFLICT REPLACE)`,
146
-		`CREATE UNIQUE INDEX idx_nick ON client (nickname COLLATE NOCASE)`,
147
-		`CREATE UNIQUE INDEX idx_uh ON client (userhost COLLATE NOCASE)`,
148
-	}
149
-	for _, stmt := range stmts {
150
-		_, err := db.db.Exec(stmt)
151
-		if err != nil {
152
-			log.Fatal("NewClientDB: ", stmt, err)
90
+	var casemappedNickMask string
91
+	for _, client := range clients.byNick {
92
+		casemappedNickMask = NewName(client.nickMaskString).String()
93
+		if matcher.Match(casemappedNickMask) {
94
+			return client
153
 		}
95
 		}
154
 	}
96
 	}
155
-	return db
156
-}
157
 
97
 
158
-func (db *ClientDB) Add(client *Client) {
159
-	_, err := db.db.Exec(`INSERT INTO client (nickname, userhost) VALUES (?, ?)`,
160
-		client.Nick().String(), client.UserHost().String())
161
-	if err != nil {
162
-		Log.error.Println("ClientDB.Add:", err)
163
-	}
164
-}
165
-
166
-func (db *ClientDB) Remove(client *Client) {
167
-	_, err := db.db.Exec(`DELETE FROM client WHERE nickname = ?`,
168
-		client.Nick().String())
169
-	if err != nil {
170
-		Log.error.Println("ClientDB.Remove:", err)
171
-	}
98
+	return nil
172
 }
99
 }
173
 
100
 
174
 //
101
 //
175
 // usermask to regexp
102
 // usermask to regexp
176
 //
103
 //
177
 
104
 
105
+//TODO(dan): move this over to generally using glob syntax instead?
106
+// kinda more expected in normal ban/etc masks, though regex is useful (probably as an extban?)
178
 type UserMaskSet struct {
107
 type UserMaskSet struct {
179
 	masks  map[Name]bool
108
 	masks  map[Name]bool
180
 	regexp *regexp.Regexp
109
 	regexp *regexp.Regexp

+ 1
- 5
irc/config.go Ver fichero

84
 	}
84
 	}
85
 
85
 
86
 	Datastore struct {
86
 	Datastore struct {
87
-		Path       string
88
-		SQLitePath string `yaml:"sqlite-path"`
87
+		Path string
89
 	}
88
 	}
90
 
89
 
91
 	Registration struct {
90
 	Registration struct {
152
 	if config.Datastore.Path == "" {
151
 	if config.Datastore.Path == "" {
153
 		return nil, errors.New("Datastore path missing")
152
 		return nil, errors.New("Datastore path missing")
154
 	}
153
 	}
155
-	if config.Datastore.SQLitePath == "" {
156
-		return nil, errors.New("SQLite database path missing")
157
-	}
158
 	if len(config.Server.Listen) == 0 {
154
 	if len(config.Server.Listen) == 0 {
159
 		return nil, errors.New("Server listening addresses missing")
155
 		return nil, errors.New("Server listening addresses missing")
160
 	}
156
 	}

+ 13
- 40
irc/database.go Ver fichero

4
 package irc
4
 package irc
5
 
5
 
6
 import (
6
 import (
7
-	"database/sql"
8
 	"encoding/base64"
7
 	"encoding/base64"
9
 	"fmt"
8
 	"fmt"
10
 	"log"
9
 	"log"
11
 	"os"
10
 	"os"
12
 
11
 
13
-	_ "github.com/mattn/go-sqlite3"
14
 	"github.com/tidwall/buntdb"
12
 	"github.com/tidwall/buntdb"
15
 )
13
 )
16
 
14
 
17
 const (
15
 const (
16
+	// 'version' of the database schema
17
+	keySchemaVersion = "db.version"
18
 	// key for the primary salt used by the ircd
18
 	// key for the primary salt used by the ircd
19
 	keySalt = "crypto.salt"
19
 	keySalt = "crypto.salt"
20
 )
20
 )
21
 
21
 
22
-func InitDB(buntpath string, path string) {
22
+// InitDB creates the database.
23
+func InitDB(path string) {
23
 	// prepare kvstore db
24
 	// prepare kvstore db
24
-	os.Remove(buntpath)
25
-	store, err := buntdb.Open(buntpath)
25
+	//TODO(dan): fail if already exists instead? don't want to overwrite good data
26
+	os.Remove(path)
27
+	store, err := buntdb.Open(path)
26
 	if err != nil {
28
 	if err != nil {
27
 		log.Fatal(fmt.Sprintf("Failed to open datastore: %s", err.Error()))
29
 		log.Fatal(fmt.Sprintf("Failed to open datastore: %s", err.Error()))
28
 	}
30
 	}
29
 	defer store.Close()
31
 	defer store.Close()
30
 
32
 
31
 	err = store.Update(func(tx *buntdb.Tx) error {
33
 	err = store.Update(func(tx *buntdb.Tx) error {
34
+		// set base db salt
32
 		salt, err := NewSalt()
35
 		salt, err := NewSalt()
33
 		encodedSalt := base64.StdEncoding.EncodeToString(salt)
36
 		encodedSalt := base64.StdEncoding.EncodeToString(salt)
34
 		if err != nil {
37
 		if err != nil {
35
 			log.Fatal("Could not generate cryptographically-secure salt for the user:", err.Error())
38
 			log.Fatal("Could not generate cryptographically-secure salt for the user:", err.Error())
36
 		}
39
 		}
37
 		tx.Set(keySalt, encodedSalt, nil)
40
 		tx.Set(keySalt, encodedSalt, nil)
41
+
42
+		// set schema version
43
+		tx.Set(keySchemaVersion, "1", nil)
38
 		return nil
44
 		return nil
39
 	})
45
 	})
40
 
46
 
41
 	if err != nil {
47
 	if err != nil {
42
 		log.Fatal("Could not save bunt store:", err.Error())
48
 		log.Fatal("Could not save bunt store:", err.Error())
43
 	}
49
 	}
44
-
45
-	// prepare SQLite db
46
-	os.Remove(path)
47
-	db := OpenDB(path)
48
-	defer db.Close()
49
-	_, err = db.Exec(`
50
-        CREATE TABLE channel (
51
-          name TEXT NOT NULL UNIQUE,
52
-          flags TEXT DEFAULT '',
53
-          key TEXT DEFAULT '',
54
-          topic TEXT DEFAULT '',
55
-          user_limit INTEGER DEFAULT 0,
56
-          ban_list TEXT DEFAULT '',
57
-          except_list TEXT DEFAULT '',
58
-          invite_list TEXT DEFAULT '')`)
59
-	if err != nil {
60
-		log.Fatal("initdb error: ", err)
61
-	}
62
 }
50
 }
63
 
51
 
52
+// UpgradeDB upgrades the datastore to the latest schema.
64
 func UpgradeDB(path string) {
53
 func UpgradeDB(path string) {
65
-	db := OpenDB(path)
66
-	alter := `ALTER TABLE channel ADD COLUMN %s TEXT DEFAULT ''`
67
-	cols := []string{"ban_list", "except_list", "invite_list"}
68
-	for _, col := range cols {
69
-		_, err := db.Exec(fmt.Sprintf(alter, col))
70
-		if err != nil {
71
-			log.Fatal("updatedb error: ", err)
72
-		}
73
-	}
74
-}
75
-
76
-func OpenDB(path string) *sql.DB {
77
-	db, err := sql.Open("sqlite3", path)
78
-	if err != nil {
79
-		log.Fatal("open db error: ", err)
80
-	}
81
-	return db
54
+	return
82
 }
55
 }

+ 31
- 35
irc/server.go Ver fichero

8
 import (
8
 import (
9
 	"bufio"
9
 	"bufio"
10
 	"crypto/tls"
10
 	"crypto/tls"
11
-	"database/sql"
12
 	"encoding/base64"
11
 	"encoding/base64"
13
 	"fmt"
12
 	"fmt"
14
 	"log"
13
 	"log"
39
 	clients             *ClientLookupSet
38
 	clients             *ClientLookupSet
40
 	commands            chan Command
39
 	commands            chan Command
41
 	ctime               time.Time
40
 	ctime               time.Time
42
-	db                  *sql.DB
43
 	store               buntdb.DB
41
 	store               buntdb.DB
44
 	idle                chan *Client
42
 	idle                chan *Client
45
 	limits              Limits
43
 	limits              Limits
79
 		clients:  NewClientLookupSet(),
77
 		clients:  NewClientLookupSet(),
80
 		commands: make(chan Command),
78
 		commands: make(chan Command),
81
 		ctime:    time.Now(),
79
 		ctime:    time.Now(),
82
-		db:       OpenDB(config.Datastore.SQLitePath),
83
 		idle:     make(chan *Client),
80
 		idle:     make(chan *Client),
84
 		limits: Limits{
81
 		limits: Limits{
85
 			AwayLen:  config.Limits.AwayLen,
82
 			AwayLen:  config.Limits.AwayLen,
216
 }
213
 }
217
 
214
 
218
 func (server *Server) loadChannels() {
215
 func (server *Server) loadChannels() {
219
-	rows, err := server.db.Query(`
220
-        SELECT name, flags, key, topic, user_limit, ban_list, except_list,
221
-               invite_list
222
-          FROM channel`)
223
-	if err != nil {
224
-		log.Fatal("error loading channels: ", err)
225
-	}
226
-	for rows.Next() {
227
-		var name, flags, key, topic string
228
-		var userLimit uint64
229
-		var banList, exceptList, inviteList string
230
-		err = rows.Scan(&name, &flags, &key, &topic, &userLimit, &banList,
231
-			&exceptList, &inviteList)
232
-		if err != nil {
233
-			log.Println("Server.loadChannels:", err)
234
-			continue
235
-		}
216
+	//TODO(dan): Fix channel persistence
217
+	/*
218
+			rows, err := server.db.Query(`
219
+		        SELECT name, flags, key, topic, user_limit, ban_list, except_list,
220
+		               invite_list
221
+		          FROM channel`)
222
+			if err != nil {
223
+				log.Fatal("error loading channels: ", err)
224
+			}
225
+			for rows.Next() {
226
+				var name, flags, key, topic string
227
+				var userLimit uint64
228
+				var banList, exceptList, inviteList string
229
+				err = rows.Scan(&name, &flags, &key, &topic, &userLimit, &banList,
230
+					&exceptList, &inviteList)
231
+				if err != nil {
232
+					log.Println("Server.loadChannels:", err)
233
+					continue
234
+				}
236
 
235
 
237
-		channel := NewChannel(server, NewName(name), false)
238
-		for _, flag := range flags {
239
-			channel.flags[ChannelMode(flag)] = true
240
-		}
241
-		channel.key = key
242
-		channel.topic = topic
243
-		channel.userLimit = userLimit
244
-		loadChannelList(channel, banList, BanMask)
245
-		loadChannelList(channel, exceptList, ExceptMask)
246
-		loadChannelList(channel, inviteList, InviteMask)
247
-	}
236
+				channel := NewChannel(server, NewName(name), false)
237
+				for _, flag := range flags {
238
+					channel.flags[ChannelMode(flag)] = true
239
+				}
240
+				channel.key = key
241
+				channel.topic = topic
242
+				channel.userLimit = userLimit
243
+				loadChannelList(channel, banList, BanMask)
244
+				loadChannelList(channel, exceptList, ExceptMask)
245
+				loadChannelList(channel, inviteList, InviteMask)
246
+			}
247
+	*/
248
 }
248
 }
249
 
249
 
250
 func (server *Server) Shutdown() {
250
 func (server *Server) Shutdown() {
253
 		client.Notice("Server is shutting down")
253
 		client.Notice("Server is shutting down")
254
 	}
254
 	}
255
 
255
 
256
-	if err := server.db.Close(); err != nil {
257
-		Log.error.Println("Server.Shutdown db.Close: error:", err)
258
-	}
259
 	if err := server.store.Close(); err != nil {
256
 	if err := server.store.Close(); err != nil {
260
 		Log.error.Println("Server.Shutdown store.Close: error:", err)
257
 		Log.error.Println("Server.Shutdown store.Close: error:", err)
261
 	}
258
 	}
263
 
260
 
264
 func (server *Server) Run() {
261
 func (server *Server) Run() {
265
 	// defer closing db/store
262
 	// defer closing db/store
266
-	defer server.db.Close()
267
 	defer server.store.Close()
263
 	defer server.store.Close()
268
 
264
 
269
 	done := false
265
 	done := false

+ 4
- 4
oragono.go Ver fichero

54
 		fmt.Print("\n")
54
 		fmt.Print("\n")
55
 		fmt.Println(encoded)
55
 		fmt.Println(encoded)
56
 	} else if arguments["initdb"].(bool) {
56
 	} else if arguments["initdb"].(bool) {
57
-		irc.InitDB(config.Datastore.Path, config.Datastore.SQLitePath)
58
-		log.Println("databases initialized: ", config.Datastore.Path, config.Datastore.SQLitePath)
57
+		irc.InitDB(config.Datastore.Path)
58
+		log.Println("database initialized: ", config.Datastore.Path)
59
 	} else if arguments["upgradedb"].(bool) {
59
 	} else if arguments["upgradedb"].(bool) {
60
-		irc.UpgradeDB(config.Datastore.SQLitePath)
61
-		log.Println("database upgraded: ", config.Datastore.SQLitePath)
60
+		irc.UpgradeDB(config.Datastore.Path)
61
+		log.Println("database upgraded: ", config.Datastore.Path)
62
 	} else if arguments["mkcerts"].(bool) {
62
 	} else if arguments["mkcerts"].(bool) {
63
 		log.Println("making self-signed certificates")
63
 		log.Println("making self-signed certificates")
64
 
64
 

Loading…
Cancelar
Guardar