mirror of
https://github.com/rwinkhart/libmutton.git
synced 2026-08-27 20:36:29 -04:00
Ensure entries are optimized as part of core.WriteEntry; fix massive inefficiency in core.EntryRefresh
This commit is contained in:
+42
-59
@@ -2,6 +2,7 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -17,11 +18,11 @@ import (
|
|||||||
"github.com/rwinkhart/rcw/wrappers"
|
"github.com/rwinkhart/rcw/wrappers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WriteEntry writes entryData to an encrypted file at realPath.
|
// WriteEntry writes decSlice to an encrypted file at realPath.
|
||||||
// If the entry contains an updated password, an age file is also created.
|
// If the entry contains an updated password, an age file is also created.
|
||||||
// Leave rcwPassword nil to use RCW demonization.
|
// Leave rcwPassword nil to use RCW demonization.
|
||||||
func WriteEntry(realPath string, decSlice []string, passwordIsNew bool, rcwPassword []byte) error {
|
func WriteEntry(realPath string, decSlice []string, passwordIsNew bool, rcwPassword []byte) error {
|
||||||
err := os.WriteFile(realPath, crypt.EncryptBytes([]byte(strings.Join(decSlice, "\n")), rcwPassword), 0600)
|
err := os.WriteFile(realPath, crypt.EncryptBytes([]byte(strings.Join(clampTrailingWhitespace(decSlice), "\n")), rcwPassword), 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("unable to write to file: " + err.Error())
|
return errors.New("unable to write to file: " + err.Error())
|
||||||
}
|
}
|
||||||
@@ -45,11 +46,6 @@ func WriteEntry(realPath string, decSlice []string, passwordIsNew bool, rcwPassw
|
|||||||
|
|
||||||
// EntryRefresh re-encrypts all libmutton entries with a new password
|
// EntryRefresh re-encrypts all libmutton entries with a new password
|
||||||
// and optimizes each entry to ensure they are as slim as possible.
|
// and optimizes each entry to ensure they are as slim as possible.
|
||||||
// This includes stripping trailing whitespace/newlines/carriage returns
|
|
||||||
// from each field and running each note through ClampTrailingWhitespace
|
|
||||||
// to ensure each note line is optimized as possible without breaking
|
|
||||||
// Markdown formatting.
|
|
||||||
// Be sure to verify passwords before using as input for this function!!
|
|
||||||
func EntryRefresh(oldRCWPassword, newRCWPassword []byte, removeOldDir bool) error {
|
func EntryRefresh(oldRCWPassword, newRCWPassword []byte, removeOldDir bool) error {
|
||||||
// ensure global.EntryRoot+"-new" and global.EntryRoot-"old" do not exist
|
// ensure global.EntryRoot+"-new" and global.EntryRoot-"old" do not exist
|
||||||
dirEnds := []string{"-new", "-old"}
|
dirEnds := []string{"-new", "-old"}
|
||||||
@@ -78,51 +74,27 @@ func EntryRefresh(oldRCWPassword, newRCWPassword []byte, removeOldDir bool) erro
|
|||||||
|
|
||||||
// decrypt, optimize, and re-encrypt each entry
|
// decrypt, optimize, and re-encrypt each entry
|
||||||
for _, vanityPath := range entries {
|
for _, vanityPath := range entries {
|
||||||
|
fmt.Println(vanityPath) // useful for CLI clients to track what entry caused a panic if one is corrupt
|
||||||
realPath := global.GetRealPath(vanityPath)
|
realPath := global.GetRealPath(vanityPath)
|
||||||
encBytes, err := os.ReadFile(realPath)
|
encBytes, err := os.ReadFile(realPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("unable to open \"" + realPath + "\" for decryption: " + err.Error())
|
return errors.New("unable to open \"" + realPath + "\" for decryption: " + err.Error())
|
||||||
}
|
}
|
||||||
decBytes, err := wrappers.Decrypt(encBytes, oldRCWPassword)
|
decBytes, err := wrappers.Decrypt(encBytes, oldRCWPassword)
|
||||||
decryptedEntry := strings.Split(string(decBytes), "\n")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// strip trailing whitespace...
|
|
||||||
fieldsLength := len(decryptedEntry)
|
|
||||||
if fieldsLength < 4 {
|
|
||||||
fieldsMain := back.RemoveTrailingEmptyStrings(decryptedEntry)
|
|
||||||
// ...from each non-note field
|
|
||||||
for i, line := range fieldsMain {
|
|
||||||
fieldsMain[i] = strings.TrimRight(line, " \t\r\n")
|
|
||||||
}
|
|
||||||
decryptedEntry = fieldsMain
|
|
||||||
} else {
|
|
||||||
fieldsMain := decryptedEntry[:4]
|
|
||||||
fieldsNote := back.RemoveTrailingEmptyStrings(decryptedEntry[4:])
|
|
||||||
// ...from each non-note field
|
|
||||||
for i, line := range fieldsMain {
|
|
||||||
fieldsMain[i] = strings.TrimRight(line, " \t\r\n")
|
|
||||||
}
|
|
||||||
// ...and from each note line (preserve Markdown formatting)
|
|
||||||
ClampTrailingWhitespace(fieldsNote)
|
|
||||||
|
|
||||||
// re-combine fields
|
// split & optimize entry
|
||||||
decryptedEntry = append(fieldsMain, fieldsNote...)
|
decSlice := clampTrailingWhitespace(strings.Split(string(decBytes), "\n"))
|
||||||
}
|
|
||||||
|
|
||||||
// re-encrypt the entry with the new password
|
// re-encrypt the entry with the new password
|
||||||
encBytes = wrappers.Encrypt([]byte(strings.Join(decryptedEntry, "\n")), newRCWPassword)
|
encBytes = wrappers.Encrypt([]byte(strings.Join(decSlice, "\n")), newRCWPassword)
|
||||||
|
|
||||||
// write the entry to the new directory
|
// write the entry to the new directory
|
||||||
if err = os.WriteFile(global.EntryRoot+"-new"+strings.ReplaceAll(vanityPath, "/", global.PathSeparator), encBytes, 0600); err != nil {
|
if err = os.WriteFile(global.EntryRoot+"-new"+strings.ReplaceAll(vanityPath, "/", global.PathSeparator), encBytes, 0600); err != nil {
|
||||||
return errors.New("unable to write to file: " + err.Error())
|
return errors.New("unable to write to file: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate new sanity check file
|
|
||||||
if err = RCWSanityCheckGen(newRCWPassword); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// swap the new directory with the old one
|
// swap the new directory with the old one
|
||||||
@@ -133,38 +105,49 @@ func EntryRefresh(oldRCWPassword, newRCWPassword []byte, removeOldDir bool) erro
|
|||||||
return errors.New("unable to rename new directory: " + err.Error())
|
return errors.New("unable to rename new directory: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate new sanity check file
|
||||||
|
if err = RCWSanityCheckGen(newRCWPassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClampTrailingWhitespace strips trailing newlines, carriage returns, and tabs from each line in a note.
|
// clampTrailingWhitespace ensures the provided decSlice contains no trailing blank lines.
|
||||||
// Additionally, it removes single trailing spaces and truncates multiple trailing spaces to two (for Markdown formatting).
|
// If decSlice contains a note, it strips trailing newlines, carriage returns, and tabs from
|
||||||
func ClampTrailingWhitespace(note []string) {
|
// each line in the note. Additionally, it removes single trailing spaces and truncates
|
||||||
for i, line := range note {
|
// multiple trailing spaces to two (for Markdown formatting).
|
||||||
// remove trailing tabs, carriage returns, and newlines
|
func clampTrailingWhitespace(decSlice []string) []string {
|
||||||
line = strings.TrimRight(line, "\t\r\n")
|
decSlice = back.RemoveTrailingEmptyStrings(decSlice)
|
||||||
|
if len(decSlice) >= 4 {
|
||||||
|
for i, noteLine := range decSlice[4:] {
|
||||||
|
// remove trailing tabs, carriage returns, and newlines
|
||||||
|
noteLine = strings.TrimRight(noteLine, "\t\r\n")
|
||||||
|
|
||||||
// determine the number of trailing spaces in the trimmed line
|
// determine the number of trailing spaces in the trimmed line
|
||||||
var endSpacesCount int
|
var endSpacesCount int
|
||||||
for j := len(line) - 1; j >= 0; j-- {
|
for j := len(noteLine) - 1; j >= 0; j-- {
|
||||||
if line[j] != ' ' {
|
if noteLine[j] != ' ' {
|
||||||
break
|
break
|
||||||
|
}
|
||||||
|
endSpacesCount++
|
||||||
}
|
}
|
||||||
endSpacesCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove single spaces, truncate multiple spaces (leave two for Markdown formatting)
|
// remove single spaces, truncate multiple spaces (leave two for Markdown formatting)
|
||||||
switch endSpacesCount {
|
switch endSpacesCount {
|
||||||
case 0:
|
case 0:
|
||||||
// no trailing spaces
|
// no trailing spaces
|
||||||
note[i] = line
|
decSlice[i] = noteLine
|
||||||
case 1:
|
case 1:
|
||||||
// remove the single trailing space
|
// remove the single trailing space
|
||||||
note[i] = strings.TrimRight(line, " ")
|
decSlice[i] = strings.TrimRight(noteLine, " ")
|
||||||
default:
|
default:
|
||||||
// truncate the trailing spaces to two
|
// truncate the trailing spaces to two
|
||||||
note[i] = line[:len(line)-endSpacesCount+2]
|
decSlice[i] = noteLine[:len(noteLine)-endSpacesCount+2]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return decSlice
|
||||||
}
|
}
|
||||||
|
|
||||||
// EntryAddPrecheck ensures the directory meant to contain a new
|
// EntryAddPrecheck ensures the directory meant to contain a new
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ go 1.25.6
|
|||||||
require (
|
require (
|
||||||
github.com/pkg/sftp v1.13.10
|
github.com/pkg/sftp v1.13.10
|
||||||
github.com/pquerna/otp v1.5.0
|
github.com/pquerna/otp v1.5.0
|
||||||
github.com/rwinkhart/go-boilerplate v0.1.1
|
github.com/rwinkhart/go-boilerplate v0.2.2
|
||||||
github.com/rwinkhart/rcw v0.2.4
|
github.com/rwinkhart/rcw v0.2.4
|
||||||
golang.org/x/crypto v0.46.0
|
golang.org/x/crypto v0.47.0
|
||||||
golang.org/x/sys v0.40.0
|
golang.org/x/sys v0.40.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
github.com/rwinkhart/go-boilerplate v0.1.1 h1:C+yyscGWeqvNpR91iw0i91zqX0LcxnCr1VOyT7iM4JY=
|
github.com/rwinkhart/go-boilerplate v0.2.2 h1:SVHTAQU+HWFivtUnDBcfrgClJV5ZmHyFS7/uERh7NKU=
|
||||||
github.com/rwinkhart/go-boilerplate v0.1.1/go.mod h1:/NVRKGslU20E5xU5YOgXzWxA6aa94BMtv5MtHRTb5Ek=
|
github.com/rwinkhart/go-boilerplate v0.2.2/go.mod h1:/NVRKGslU20E5xU5YOgXzWxA6aa94BMtv5MtHRTb5Ek=
|
||||||
github.com/rwinkhart/go-winio v0.1.0 h1:b72agLW+dETGmhR3VbcbwnStfgKfc5AfgJOXBJDkaHg=
|
github.com/rwinkhart/go-winio v0.1.0 h1:b72agLW+dETGmhR3VbcbwnStfgKfc5AfgJOXBJDkaHg=
|
||||||
github.com/rwinkhart/go-winio v0.1.0/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
|
github.com/rwinkhart/go-winio v0.1.0/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
|
||||||
github.com/rwinkhart/peercred-mini v0.1.2 h1:4cGWDbv0whvLeVvbUdx84V/9p+2fS+DEXgrA1KxlRFo=
|
github.com/rwinkhart/peercred-mini v0.1.2 h1:4cGWDbv0whvLeVvbUdx84V/9p+2fS+DEXgrA1KxlRFo=
|
||||||
@@ -26,9 +26,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
Reference in New Issue
Block a user