mirror of
https://codeberg.org/scip/anydb.git
synced 2026-10-08 03:04:20 +02:00
satisfy linter
This commit is contained in:
16
app/attr.go
16
app/attr.go
@@ -1,3 +1,5 @@
|
||||
package app
|
||||
|
||||
/*
|
||||
Copyright © 2024-2025 Thomas von Dein
|
||||
|
||||
@@ -14,7 +16,6 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -125,17 +126,24 @@ func (attr *DbAttr) GetFileValue() error {
|
||||
}
|
||||
} else {
|
||||
// read from console stdin
|
||||
var input string
|
||||
var data string
|
||||
var (
|
||||
input string
|
||||
data string
|
||||
buf strings.Builder
|
||||
)
|
||||
|
||||
for {
|
||||
_, err := fmt.Scanln(&input)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
data += input + "\n"
|
||||
|
||||
buf.WriteString(input)
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
data += buf.String()
|
||||
|
||||
attr.Val = []byte(data)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
package app
|
||||
|
||||
/*
|
||||
Copyright © 2024 Thomas von Dein
|
||||
|
||||
@@ -14,7 +16,6 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@@ -35,7 +36,7 @@ const (
|
||||
ArgonParallel uint8 = 2
|
||||
ArgonSaltLen int = 16
|
||||
ArgonKeyLen uint32 = 32
|
||||
B64SaltLen int = 16 //22
|
||||
B64SaltLen int = 16 // 22
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
@@ -47,7 +48,8 @@ type Key struct {
|
||||
// password
|
||||
func AskForPassword() ([]byte, error) {
|
||||
fmt.Fprint(os.Stderr, "Password: ")
|
||||
pass, err := term.ReadPassword(int(syscall.Stdin))
|
||||
|
||||
pass, err := term.ReadPassword(syscall.Stdin)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read password: %w", err)
|
||||
}
|
||||
@@ -57,6 +59,8 @@ func AskForPassword() ([]byte, error) {
|
||||
return pass, nil
|
||||
}
|
||||
|
||||
// DeriveKey derives a key from password.
|
||||
//
|
||||
// We're using the Argon2id key derivation algorithm to derive a
|
||||
// secure key from the given password. This is important, because
|
||||
// users might use unsecure passwords. The resulting encrypted data
|
||||
@@ -78,7 +82,7 @@ func DeriveKey(password []byte, salt []byte) (*Key, error) {
|
||||
}
|
||||
|
||||
hash := argon2.IDKey(
|
||||
[]byte(password), salt,
|
||||
password, salt,
|
||||
ArgonIter,
|
||||
ArgonMem,
|
||||
ArgonParallel,
|
||||
@@ -95,6 +99,7 @@ func DeriveKey(password []byte, salt []byte) (*Key, error) {
|
||||
// Retrieve a random chunk of given size
|
||||
func GetRandom(size int, capacity int) ([]byte, error) {
|
||||
buf := make([]byte, size, capacity)
|
||||
|
||||
_, err := rand.Read(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve random bytes: %w", err)
|
||||
@@ -103,7 +108,7 @@ func GetRandom(size int, capacity int) ([]byte, error) {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Encrypt clear text given in attr using ChaCha20 and auhtenticate
|
||||
// Encrypt clear text given in attr using ChaCha20 and authenticate
|
||||
// using the mac Poly1305. The cipher text will be put into attr, thus
|
||||
// modifying it.
|
||||
//
|
||||
@@ -143,7 +148,7 @@ func Encrypt(pass []byte, attr *DbAttr) error {
|
||||
// Do the reverse
|
||||
func Decrypt(pass []byte, cipherb []byte) ([]byte, error) {
|
||||
if len(cipherb) < B64SaltLen {
|
||||
return nil, fmt.Errorf("encrypted cipher block too small")
|
||||
return nil, errors.New("encrypted cipher block too small")
|
||||
}
|
||||
|
||||
key, err := DeriveKey(pass, cipherb[0:B64SaltLen])
|
||||
|
||||
182
app/db.go
182
app/db.go
@@ -25,6 +25,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -64,7 +65,7 @@ type DbTag struct {
|
||||
}
|
||||
|
||||
func (entry *DbEntry) Taglist() string {
|
||||
return strings.Join(entry.Tags, ",")
|
||||
return strings.Join(entry.GetTags(), ",")
|
||||
}
|
||||
|
||||
const BucketData string = "data"
|
||||
@@ -113,6 +114,7 @@ func (db *DB) Open() error {
|
||||
}
|
||||
|
||||
db.DB = b
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -128,8 +130,10 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
var entries DbEntries
|
||||
var filter *regexp.Regexp
|
||||
var (
|
||||
entries DbEntries
|
||||
filter *regexp.Regexp
|
||||
)
|
||||
|
||||
if len(attr.Args) > 0 {
|
||||
// via cli
|
||||
@@ -158,7 +162,7 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
|
||||
|
||||
databucket := root.Bucket([]byte("data"))
|
||||
if databucket == nil {
|
||||
return fmt.Errorf("failed to retrieve data sub bucket")
|
||||
return errors.New("failed to retrieve data sub bucket")
|
||||
}
|
||||
|
||||
err := bucket.ForEach(func(key, pbentry []byte) error {
|
||||
@@ -169,7 +173,7 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
|
||||
|
||||
if fulltext {
|
||||
// avoid crash due to access fault
|
||||
value := databucket.Get([]byte(entry.Key)) // empty is ok
|
||||
value := databucket.Get([]byte(entry.GetKey())) // empty is ok
|
||||
vc := make([]byte, len(value))
|
||||
copy(vc, value)
|
||||
entry.Value = string(vc)
|
||||
@@ -179,26 +183,21 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
|
||||
|
||||
switch {
|
||||
case filter != nil:
|
||||
if filter.MatchString(entry.Key) ||
|
||||
filter.MatchString(strings.Join(entry.Tags, " ")) {
|
||||
if filter.MatchString(entry.GetKey()) ||
|
||||
filter.MatchString(strings.Join(entry.GetTags(), " ")) {
|
||||
include = true
|
||||
}
|
||||
|
||||
if !entry.Binary && !include && fulltext {
|
||||
if filter.MatchString(string(entry.Value)) {
|
||||
if !entry.GetBinary() && !include && fulltext {
|
||||
if filter.MatchString(entry.GetValue()) {
|
||||
include = true
|
||||
}
|
||||
}
|
||||
case len(attr.Tags) > 0:
|
||||
for _, search := range attr.Tags {
|
||||
for _, tag := range entry.Tags {
|
||||
if tag == search {
|
||||
include = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if slices.Contains(entry.GetTags(), search) {
|
||||
include = true
|
||||
|
||||
if include {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -239,6 +238,7 @@ func (db *DB) Set(attr *DbAttr) error {
|
||||
// any tags. if so, we initialize our update struct with these
|
||||
// tags unless it has new tags configured.
|
||||
slog.Debug("+++ GET")
|
||||
|
||||
oldentry, err := db.txGet(attr)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "no such key") {
|
||||
@@ -247,9 +247,9 @@ func (db *DB) Set(attr *DbAttr) error {
|
||||
}
|
||||
|
||||
if oldentry != nil {
|
||||
if len(oldentry.Tags) > 0 && len(entry.Tags) == 0 {
|
||||
if len(oldentry.GetTags()) > 0 && len(entry.GetTags()) == 0 {
|
||||
// initialize update entry with tags from old entry
|
||||
entry.Tags = oldentry.Tags
|
||||
entry.Tags = oldentry.GetTags()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +261,7 @@ func (db *DB) Set(attr *DbAttr) error {
|
||||
}
|
||||
|
||||
slog.Debug("+++ UPDATE")
|
||||
|
||||
err = db.DB.Update(func(tx *bolt.Tx) error {
|
||||
// create root bucket
|
||||
root, err := tx.CreateBucketIfNotExists([]byte(db.Bucket))
|
||||
@@ -277,7 +278,7 @@ func (db *DB) Set(attr *DbAttr) error {
|
||||
slog.Debug("opened/created buckets", "root", root, "data", bucket)
|
||||
|
||||
// write meta data
|
||||
err = bucket.Put([]byte(entry.Key), []byte(pbentry))
|
||||
err = bucket.Put([]byte(entry.GetKey()), pbentry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert data: %w", err)
|
||||
}
|
||||
@@ -289,14 +290,13 @@ func (db *DB) Set(attr *DbAttr) error {
|
||||
}
|
||||
|
||||
// write value
|
||||
err = databucket.Put([]byte(entry.Key), attr.Val)
|
||||
err = databucket.Put([]byte(entry.GetKey()), attr.Val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert data: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -304,67 +304,6 @@ func (db *DB) Set(attr *DbAttr) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// internal DB getter, assumes db.DB has already been
|
||||
// opened successfully. Do NOT call this w/o valid
|
||||
// DB handle!
|
||||
func (db *DB) txGet(attr *DbAttr) (*DbEntry, error) {
|
||||
entry := DbEntry{}
|
||||
|
||||
err := db.DB.View(func(tx *bolt.Tx) error {
|
||||
// root bucket
|
||||
root := tx.Bucket([]byte(db.Bucket))
|
||||
if root == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get meta sub bucket
|
||||
bucket := root.Bucket([]byte("meta"))
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
slog.Debug("opened buckets", "root", root, "data", bucket)
|
||||
|
||||
// retrieve meta data
|
||||
pbentry := bucket.Get([]byte(attr.Key))
|
||||
if pbentry == nil {
|
||||
return fmt.Errorf("no such key: %s", attr.Key)
|
||||
}
|
||||
|
||||
// put into struct
|
||||
if err := proto.Unmarshal(pbentry, &entry); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal from protobuf: %w", err)
|
||||
}
|
||||
|
||||
// get data sub bucket
|
||||
databucket := root.Bucket([]byte("data"))
|
||||
if databucket == nil {
|
||||
return fmt.Errorf("failed to retrieve data sub bucket")
|
||||
}
|
||||
|
||||
// retrieve actual data value
|
||||
value := databucket.Get([]byte(attr.Key))
|
||||
if len(value) == 0 {
|
||||
return fmt.Errorf("no such key: %s", attr.Key)
|
||||
}
|
||||
|
||||
// we need to make a copy of it, otherwise we'll get an
|
||||
// "unexpected fault address" error
|
||||
vc := make([]byte, len(value))
|
||||
copy(vc, value)
|
||||
|
||||
entry.Value = string(vc)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entry, nil
|
||||
}
|
||||
|
||||
func (db *DB) Get(attr *DbAttr) (*DbEntry, error) {
|
||||
if err := db.Open(); err != nil {
|
||||
return nil, err
|
||||
@@ -372,7 +311,6 @@ func (db *DB) Get(attr *DbAttr) (*DbEntry, error) {
|
||||
defer db.Close()
|
||||
|
||||
entry, err := db.txGet(attr)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read from DB: %w", err)
|
||||
}
|
||||
@@ -418,10 +356,11 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
|
||||
}
|
||||
|
||||
var entries DbEntries
|
||||
|
||||
now := time.Now()
|
||||
newfile := db.Dbfile + now.Format("-02.01.2006T03:04.05")
|
||||
|
||||
if err := json.Unmarshal([]byte(attr.Val), &entries); err != nil {
|
||||
if err := json.Unmarshal(attr.Val, &entries); err != nil {
|
||||
return "", cleanError(newfile, fmt.Errorf("failed to unmarshal json: %w", err))
|
||||
}
|
||||
|
||||
@@ -431,7 +370,6 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to rename file %s to %s: %w", db.Dbfile, newfile, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// should now be a new db file
|
||||
@@ -462,7 +400,7 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
|
||||
}
|
||||
|
||||
// write meta data
|
||||
err = bucket.Put([]byte(entry.Key), []byte(pbentry))
|
||||
err = bucket.Put([]byte(entry.GetKey()), pbentry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert data into DB: %w", err)
|
||||
}
|
||||
@@ -474,7 +412,7 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
|
||||
}
|
||||
|
||||
// write value
|
||||
err = databucket.Put([]byte(entry.Key), []byte(entry.Value))
|
||||
err = databucket.Put([]byte(entry.GetKey()), []byte(entry.GetValue()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert data: %w", err)
|
||||
}
|
||||
@@ -482,7 +420,6 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", cleanError(newfile, err)
|
||||
}
|
||||
@@ -520,16 +457,14 @@ func (db *DB) Info() (*DbInfo, error) {
|
||||
}
|
||||
|
||||
info.Buckets = append(info.Buckets, binfo)
|
||||
|
||||
return nil
|
||||
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read from DB: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
})
|
||||
|
||||
return info, err
|
||||
@@ -559,7 +494,7 @@ func (db *DB) Getall(attr *DbAttr) (DbEntries, error) {
|
||||
// get data sub bucket
|
||||
databucket := root.Bucket([]byte("data"))
|
||||
if databucket == nil {
|
||||
return fmt.Errorf("failed to retrieve data sub bucket")
|
||||
return errors.New("failed to retrieve data sub bucket")
|
||||
}
|
||||
|
||||
slog.Debug("opened buckets", "root", root, "data", bucket)
|
||||
@@ -572,7 +507,7 @@ func (db *DB) Getall(attr *DbAttr) (DbEntries, error) {
|
||||
}
|
||||
|
||||
// retrieve the value from the data sub bucket
|
||||
value := databucket.Get([]byte(entry.Key))
|
||||
value := databucket.Get([]byte(entry.GetKey()))
|
||||
|
||||
// we need to make a copy of it, otherwise we'll get an
|
||||
// "unexpected fault address" error
|
||||
@@ -587,5 +522,66 @@ func (db *DB) Getall(attr *DbAttr) (DbEntries, error) {
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return entries, err
|
||||
}
|
||||
|
||||
// internal DB getter, assumes db.DB has already been
|
||||
// opened successfully. Do NOT call this w/o valid
|
||||
// DB handle!
|
||||
func (db *DB) txGet(attr *DbAttr) (*DbEntry, error) {
|
||||
entry := DbEntry{}
|
||||
|
||||
err := db.DB.View(func(tx *bolt.Tx) error {
|
||||
// root bucket
|
||||
root := tx.Bucket([]byte(db.Bucket))
|
||||
if root == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get meta sub bucket
|
||||
bucket := root.Bucket([]byte("meta"))
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
slog.Debug("opened buckets", "root", root, "data", bucket)
|
||||
|
||||
// retrieve meta data
|
||||
pbentry := bucket.Get([]byte(attr.Key))
|
||||
if pbentry == nil {
|
||||
return fmt.Errorf("no such key: %s", attr.Key)
|
||||
}
|
||||
|
||||
// put into struct
|
||||
if err := proto.Unmarshal(pbentry, &entry); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal from protobuf: %w", err)
|
||||
}
|
||||
|
||||
// get data sub bucket
|
||||
databucket := root.Bucket([]byte("data"))
|
||||
if databucket == nil {
|
||||
return errors.New("failed to retrieve data sub bucket")
|
||||
}
|
||||
|
||||
// retrieve actual data value
|
||||
value := databucket.Get([]byte(attr.Key))
|
||||
if len(value) == 0 {
|
||||
return fmt.Errorf("no such key: %s", attr.Key)
|
||||
}
|
||||
|
||||
// we need to make a copy of it, otherwise we'll get an
|
||||
// "unexpected fault address" error
|
||||
vc := make([]byte, len(value))
|
||||
copy(vc, value)
|
||||
|
||||
entry.Value = string(vc)
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entry, nil
|
||||
}
|
||||
|
||||
16
app/io.go
16
app/io.go
@@ -1,3 +1,5 @@
|
||||
package app
|
||||
|
||||
/*
|
||||
Copyright © 2024 Thomas von Dein
|
||||
|
||||
@@ -14,18 +16,24 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package app
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
func cleanError(file string, err error) error {
|
||||
// remove given [backup] file and forward the given error
|
||||
return os.Remove(file)
|
||||
oserr := os.Remove(file)
|
||||
if oserr != nil {
|
||||
return errors.Join(err, oserr)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func fileExists(filename string) bool {
|
||||
info, err := os.Stat(filename)
|
||||
|
||||
if err != nil {
|
||||
// return false on any error
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user