You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
2.6 KiB
Go

package main
import (
"bytes"
"crypto/rsa"
"encoding/json"
"fmt"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/util"
"strings"
"sync"
)
var (
openDBs = make(map[string]userdb, 32)
dbopenlock sync.Mutex
)
type userdb struct {
*leveldb.DB
}
func (db *userdb) getPublicKey() (*rsa.PublicKey, error) {
val, err := db.Get([]byte("public_key"), nil)
if err != nil {
return nil, fmt.Errorf("unable to get public key: %v", err)
}
var key rsa.PublicKey
if err := json.Unmarshal(val, &key); err != nil {
return nil, fmt.Errorf("unable to get public key: %v", err)
}
return &key, nil
}
func (db *userdb) nextKey(prefix string) (string, error) {
r := util.BytesPrefix([]byte(prefix))
it := db.NewIterator(r, nil)
defer it.Release()
id := 0
if it.Last() {
key := it.Key()
id_s := strings.TrimPrefix(string(key), prefix)
lastId, err := decodeInt(id_s)
if err != nil {
return "0", fmt.Errorf("error getting note id: %v", err)
}
id = lastId + 1
}
return fmt.Sprintf("%s%s", prefix, encodeInt(id)), nil
}
// iterates through a range of values, starting with a prefix, parsing the
// lexnum part on each key, and calling the callback for each value with the
// value's associated number in its lexical series
func (db *userdb) collect(prefix []byte, n int, fn func(n int, v []byte) error) error {
r := util.BytesPrefix(prefix)
it := db.NewIterator(r, nil)
defer it.Release()
var step func() bool
if n < 0 {
if !it.Last() {
return fmt.Errorf("collect unable to advance iterator to last")
}
step = it.Prev
n = -n
} else {
step = it.Next
}
for i := 0; it.Valid() && i < n; i++ {
id_s := string(bytes.TrimPrefix(it.Key(), prefix))
id, err := decodeInt(id_s)
if err != nil {
return fmt.Errorf("unable to collect on prefix %s: %v", prefix, err)
}
if err := fn(id, it.Value()); err != nil {
return fmt.Errorf("callback error in collect: %v", err)
}
step()
}
return nil
}
func getUserDB(nick string, create bool) (*userdb, error) {
if db, ok := openDBs[nick]; ok {
return &db, nil
}
opts := &opt.Options{
ErrorIfMissing: !create,
}
path := fmt.Sprintf("./%s.db", nick)
conn, err := leveldb.OpenFile(path, opts)
if err != nil {
return nil, fmt.Errorf("unable to open db file at %s: %v", path, err)
}
info_log.Printf("opened database file: %s", path)
dbopenlock.Lock()
defer dbopenlock.Unlock()
db := userdb{conn}
openDBs[nick] = db
return &db, nil
}
func getUserKey(nick string) (*rsa.PublicKey, error) {
db, err := getUserDB(nick, false)
if err != nil {
return nil, err
}
return db.getPublicKey()
}