mirror of
https://github.com/rwinkhart/MUTN.git
synced 2026-08-27 20:36:29 -04:00
Port to latest libmutton development version (use go-boilerplate)
This commit is contained in:
@@ -5,7 +5,8 @@ go 1.24.3
|
|||||||
require (
|
require (
|
||||||
github.com/Trojan2021/BEAN v0.0.0-20241210230804-8f294833b514
|
github.com/Trojan2021/BEAN v0.0.0-20241210230804-8f294833b514
|
||||||
github.com/charmbracelet/glamour v0.7.0
|
github.com/charmbracelet/glamour v0.7.0
|
||||||
github.com/rwinkhart/libmutton v0.3.2-0.20250508234242-fb83d29a23e0
|
github.com/rwinkhart/go-boilerplate v0.0.0-20250509154735-0846290a7620
|
||||||
|
github.com/rwinkhart/libmutton v0.3.2-0.20250509161203-03c040b373b6
|
||||||
golang.org/x/term v0.32.0
|
golang.org/x/term v0.32.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -59,10 +59,12 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rwinkhart/convertroman v0.2.0 h1:otUm939eXT77q/Lr31mJtMwVWEw0RjEUB71gO65h9OM=
|
github.com/rwinkhart/convertroman v0.2.0 h1:otUm939eXT77q/Lr31mJtMwVWEw0RjEUB71gO65h9OM=
|
||||||
github.com/rwinkhart/convertroman v0.2.0/go.mod h1:Af6HqvX0EIM4Y3HcnNXbbRey1dLasGB7WU0+3Cowgmw=
|
github.com/rwinkhart/convertroman v0.2.0/go.mod h1:Af6HqvX0EIM4Y3HcnNXbbRey1dLasGB7WU0+3Cowgmw=
|
||||||
|
github.com/rwinkhart/go-boilerplate v0.0.0-20250509154735-0846290a7620 h1:MjxVq+EqJgB/sxgSbPTTuzbOrU61kKa4oJvjqqdEoO4=
|
||||||
|
github.com/rwinkhart/go-boilerplate v0.0.0-20250509154735-0846290a7620/go.mod h1:cnzIF45I0FCOvE4YIB+26pLCUx2kWyY2llKYZruNaRY=
|
||||||
github.com/rwinkhart/go-winio-easy-pipe-handles v0.0.0-20250407031321-96994a0e8410 h1:NhHwFM3Pgm6zRUfFKvi0p5ndjfFbVWsRwmmhyFlG4PE=
|
github.com/rwinkhart/go-winio-easy-pipe-handles v0.0.0-20250407031321-96994a0e8410 h1:NhHwFM3Pgm6zRUfFKvi0p5ndjfFbVWsRwmmhyFlG4PE=
|
||||||
github.com/rwinkhart/go-winio-easy-pipe-handles v0.0.0-20250407031321-96994a0e8410/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/rwinkhart/go-winio-easy-pipe-handles v0.0.0-20250407031321-96994a0e8410/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/rwinkhart/libmutton v0.3.2-0.20250508234242-fb83d29a23e0 h1:XoJyvbNJBU3HbYHlyYBh3VKzVad10wOZ/feFkBTvxWU=
|
github.com/rwinkhart/libmutton v0.3.2-0.20250509161203-03c040b373b6 h1:sV+cBjbQHX1++LrmGpcJ3+QOF6SYJDfPNuIywzqHK10=
|
||||||
github.com/rwinkhart/libmutton v0.3.2-0.20250508234242-fb83d29a23e0/go.mod h1:IW7espY4xtdVTd0/6THtmRadR+utzH1dDXcgrP3VqlM=
|
github.com/rwinkhart/libmutton v0.3.2-0.20250509161203-03c040b373b6/go.mod h1:qUMh7UidlnhcAyLEygYV8z87le46MnL71r8b6MvJL4w=
|
||||||
github.com/rwinkhart/peercred-mini v0.0.0-20250407033241-c09add2eceea h1:VE2ti/AE4Y3kgnyK+J5c5hpddE+dKHpsFch3K2R6puQ=
|
github.com/rwinkhart/peercred-mini v0.0.0-20250407033241-c09add2eceea h1:VE2ti/AE4Y3kgnyK+J5c5hpddE+dKHpsFch3K2R6puQ=
|
||||||
github.com/rwinkhart/peercred-mini v0.0.0-20250407033241-c09add2eceea/go.mod h1:t+YkvAdnTKTrg4d469tw3K+GCUzX/Bja4h8yKjSIsGs=
|
github.com/rwinkhart/peercred-mini v0.0.0-20250407033241-c09add2eceea/go.mod h1:t+YkvAdnTKTrg4d469tw3K+GCUzX/Bja4h8yKjSIsGs=
|
||||||
github.com/rwinkhart/rcw v0.0.0-20250508234041-b49d9f1eea42 h1:/vsBCwHP22cmqrtjsRB1OcPcb+sckktdeFlHKmhzD54=
|
github.com/rwinkhart/rcw v0.0.0-20250508234041-b49d9f1eea42 h1:/vsBCwHP22cmqrtjsRB1OcPcb+sckktdeFlHKmhzD54=
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/rwinkhart/MUTN/src/cli"
|
"github.com/rwinkhart/MUTN/src/cli"
|
||||||
|
"github.com/rwinkhart/go-boilerplate/back"
|
||||||
"github.com/rwinkhart/libmutton/core"
|
"github.com/rwinkhart/libmutton/core"
|
||||||
"github.com/rwinkhart/libmutton/sync"
|
"github.com/rwinkhart/libmutton/sync"
|
||||||
)
|
)
|
||||||
@@ -96,7 +97,7 @@ func main() {
|
|||||||
case "note", "-n":
|
case "note", "-n":
|
||||||
field = 4
|
field = 4
|
||||||
case "rename", "-r":
|
case "rename", "-r":
|
||||||
core.TargetIsFile(targetLocation, true, 0) // ensure location exists before prompting for new location
|
back.TargetIsFile(targetLocation, true, 0) // ensure location exists before prompting for new location
|
||||||
cli.RenameCli(args[1]) // pass the incomplete path as the server and all clients (reading from the deletions directory) will have a different home directory
|
cli.RenameCli(args[1]) // pass the incomplete path as the server and all clients (reading from the deletions directory) will have a different home directory
|
||||||
default:
|
default:
|
||||||
cli.HelpEdit()
|
cli.HelpEdit()
|
||||||
@@ -161,11 +162,11 @@ func main() {
|
|||||||
case "startrcwd":
|
case "startrcwd":
|
||||||
core.RCWDArgument()
|
core.RCWDArgument()
|
||||||
case "sync":
|
case "sync":
|
||||||
cli.RunJobWrapper(true)
|
sync.RunJob(true, false)
|
||||||
case "init":
|
case "init":
|
||||||
cli.TempInitCli()
|
cli.TempInitCli()
|
||||||
case "tweak":
|
case "tweak":
|
||||||
core.PrintError("\"tweak\" is not yet implemented", 0, true)
|
back.PrintError("\"tweak\" is not yet implemented", 0, true)
|
||||||
case "copy":
|
case "copy":
|
||||||
cli.HelpCopy()
|
cli.HelpCopy()
|
||||||
case "edit":
|
case "edit":
|
||||||
|
|||||||
+2
-3
@@ -3,6 +3,7 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/rwinkhart/go-boilerplate/front"
|
||||||
"github.com/rwinkhart/libmutton/core"
|
"github.com/rwinkhart/libmutton/core"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
@@ -19,7 +20,5 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
core.GetPassphrase = func() []byte {
|
core.GetPassphrase = front.InputHidden
|
||||||
return inputHidden("RCW Passphrase:")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-5
@@ -1,6 +1,7 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/rwinkhart/go-boilerplate/front"
|
||||||
"github.com/rwinkhart/libmutton/core"
|
"github.com/rwinkhart/libmutton/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,19 +14,19 @@ func AddEntry(targetLocation string, hideSecrets bool, entryType uint8) {
|
|||||||
var unencryptedEntry []string
|
var unencryptedEntry []string
|
||||||
|
|
||||||
if entryType < 2 {
|
if entryType < 2 {
|
||||||
username := input("Username:")
|
username := front.Input("Username:")
|
||||||
|
|
||||||
// determine whether to generate the password
|
// determine whether to generate the password
|
||||||
var password string
|
var password string
|
||||||
if entryType == 0 {
|
if entryType == 0 {
|
||||||
password = string(inputHidden("Password:"))
|
password = string(front.InputHidden("Password:"))
|
||||||
} else {
|
} else {
|
||||||
password = inputPasswordGen()
|
password = inputPasswordGen()
|
||||||
}
|
}
|
||||||
|
|
||||||
totp := string(inputHidden("TOTP secret:"))
|
totp := string(front.InputHidden("TOTP secret:"))
|
||||||
url := input("URL:")
|
url := front.Input("URL:")
|
||||||
if inputBinary("Add a note to this entry?") {
|
if front.InputBinary("Add a note to this entry?") {
|
||||||
note, _ := editNote([]string{})
|
note, _ := editNote([]string{})
|
||||||
unencryptedEntry = append([]string{password, username, totp, url}, note...)
|
unencryptedEntry = append([]string{password, username, totp, url}, note...)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+12
-10
@@ -6,6 +6,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/rwinkhart/go-boilerplate/back"
|
||||||
|
"github.com/rwinkhart/go-boilerplate/front"
|
||||||
"github.com/rwinkhart/libmutton/core"
|
"github.com/rwinkhart/libmutton/core"
|
||||||
"github.com/rwinkhart/libmutton/sync"
|
"github.com/rwinkhart/libmutton/sync"
|
||||||
)
|
)
|
||||||
@@ -13,7 +15,7 @@ import (
|
|||||||
// RenameCli renames an entry at oldLocationIncomplete to a new location (user input) on both the client and the server.
|
// RenameCli renames an entry at oldLocationIncomplete to a new location (user input) on both the client and the server.
|
||||||
func RenameCli(oldLocationIncomplete string) {
|
func RenameCli(oldLocationIncomplete string) {
|
||||||
// prompt user for new location and rename
|
// prompt user for new location and rename
|
||||||
newLocationIncomplete := input("New location:")
|
newLocationIncomplete := front.Input("New location:")
|
||||||
sync.RenameRemoteFromClient(oldLocationIncomplete, newLocationIncomplete, false)
|
sync.RenameRemoteFromClient(oldLocationIncomplete, newLocationIncomplete, false)
|
||||||
|
|
||||||
// exit is done from sync.RenameRemoteFromClient
|
// exit is done from sync.RenameRemoteFromClient
|
||||||
@@ -27,13 +29,13 @@ func EditEntryField(targetLocation string, hideSecrets bool, field int) {
|
|||||||
// edit the field
|
// edit the field
|
||||||
switch field {
|
switch field {
|
||||||
case 0:
|
case 0:
|
||||||
unencryptedEntry[field] = string(inputHidden("Password:"))
|
unencryptedEntry[field] = string(front.InputHidden("Password:"))
|
||||||
case 1:
|
case 1:
|
||||||
unencryptedEntry[field] = input("Username:")
|
unencryptedEntry[field] = front.Input("Username:")
|
||||||
case 2:
|
case 2:
|
||||||
unencryptedEntry[field] = string(inputHidden("TOTP secret:"))
|
unencryptedEntry[field] = string(front.InputHidden("TOTP secret:"))
|
||||||
case 3:
|
case 3:
|
||||||
unencryptedEntry[field] = input("URL:")
|
unencryptedEntry[field] = front.Input("URL:")
|
||||||
case 4: // edit notes fields
|
case 4: // edit notes fields
|
||||||
// store note and non-note data separately
|
// store note and non-note data separately
|
||||||
nonNoteData := unencryptedEntry[:4]
|
nonNoteData := unencryptedEntry[:4]
|
||||||
@@ -42,7 +44,7 @@ func EditEntryField(targetLocation string, hideSecrets bool, field int) {
|
|||||||
// edit the note
|
// edit the note
|
||||||
editedNote, noteEdited := editNote(noteData)
|
editedNote, noteEdited := editNote(noteData)
|
||||||
if !noteEdited { // exit early if the note was not edited
|
if !noteEdited { // exit early if the note was not edited
|
||||||
core.PrintError("Entry is unchanged", 0, true)
|
back.PrintError("Entry is unchanged", 0, true)
|
||||||
}
|
}
|
||||||
unencryptedEntry = append(nonNoteData, editedNote...)
|
unencryptedEntry = append(nonNoteData, editedNote...)
|
||||||
}
|
}
|
||||||
@@ -66,7 +68,7 @@ func GenUpdate(targetLocation string, hideSecrets bool) {
|
|||||||
// editNote uses the user-specified text editor to edit an existing note (or create a new one if baseNote is empty).
|
// editNote uses the user-specified text editor to edit an existing note (or create a new one if baseNote is empty).
|
||||||
// Returns the edited note and a boolean indicating whether the note was edited.
|
// Returns the edited note and a boolean indicating whether the note was edited.
|
||||||
func editNote(baseNote []string) ([]string, bool) {
|
func editNote(baseNote []string) ([]string, bool) {
|
||||||
tempFile := core.CreateTempFile()
|
tempFile := back.CreateTempFile()
|
||||||
defer func(name string) {
|
defer func(name string) {
|
||||||
_ = os.Remove(name) // error ignored; if the file could be created, it can probably be removed
|
_ = os.Remove(name) // error ignored; if the file could be created, it can probably be removed
|
||||||
}(tempFile.Name())
|
}(tempFile.Name())
|
||||||
@@ -102,13 +104,13 @@ func editNote(baseNote []string) ([]string, bool) {
|
|||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(core.AnsiError + "Failed to write note with " + editor + core.AnsiReset) // panic is used to ensure the tempFile is removed, as per the defer statement
|
panic(back.AnsiError + "Failed to write note with " + editor + back.AnsiReset) // panic is used to ensure the tempFile is removed, as per the defer statement
|
||||||
}
|
}
|
||||||
|
|
||||||
// open the tempFile for reading
|
// open the tempFile for reading
|
||||||
tempFile, err = os.Open(tempFile.Name())
|
tempFile, err = os.Open(tempFile.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(core.AnsiError + "Failed to read note written with " + editor + core.AnsiReset) // panic is used to ensure the tempFile is removed, as per the defer statement
|
panic(back.AnsiError + "Failed to read note written with " + editor + back.AnsiReset) // panic is used to ensure the tempFile is removed, as per the defer statement
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the edited note from the tempFile
|
// read the edited note from the tempFile
|
||||||
@@ -122,7 +124,7 @@ func editNote(baseNote []string) ([]string, bool) {
|
|||||||
_ = tempFile.Close() // error ignored; if the file could be opened, it can probably be closed
|
_ = tempFile.Close() // error ignored; if the file could be opened, it can probably be closed
|
||||||
|
|
||||||
// remove trailing empty strings from the edited note
|
// remove trailing empty strings from the edited note
|
||||||
note = core.RemoveTrailingEmptyStrings(note)
|
note = back.RemoveTrailingEmptyStrings(note)
|
||||||
|
|
||||||
// clamp trailing whitespace in each note line
|
// clamp trailing whitespace in each note line
|
||||||
core.ClampTrailingWhitespace(note)
|
core.ClampTrailingWhitespace(note)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/rwinkhart/libmutton/core"
|
"github.com/rwinkhart/go-boilerplate/back"
|
||||||
"github.com/rwinkhart/libmutton/sync"
|
"github.com/rwinkhart/libmutton/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ func printFileEntry(entry string, lastSlash, charCounter, indent int, colorAlter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// print fileEntryName to screen
|
// print fileEntryName to screen
|
||||||
fmt.Printf("%s%s%s ", colorCode, fileEntryName, core.AnsiReset)
|
fmt.Printf("%s%s%s ", colorCode, fileEntryName, back.AnsiReset)
|
||||||
|
|
||||||
return charCounter, colorAlternator
|
return charCounter, colorAlternator
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ func EntryListGen() {
|
|||||||
fileList, dirList := sync.WalkEntryDir()
|
fileList, dirList := sync.WalkEntryDir()
|
||||||
|
|
||||||
// print header bar w/total entry count
|
// print header bar w/total entry count
|
||||||
fmt.Print("\n"+ansiBlackOnWhite, len(fileList), " libmutton entries:"+core.AnsiReset)
|
fmt.Print("\n"+ansiBlackOnWhite, len(fileList), " libmutton entries:"+back.AnsiReset)
|
||||||
|
|
||||||
// dirList iteration
|
// dirList iteration
|
||||||
dirListLength := len(dirList) // save length for multiple references below
|
dirListLength := len(dirList) // save length for multiple references below
|
||||||
@@ -128,7 +128,7 @@ func EntryListGen() {
|
|||||||
containsFiles = true
|
containsFiles = true
|
||||||
skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped
|
skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped
|
||||||
indent, vanityDirectory = determineIndentation(skippedDirList, dirList, i) // calculate the final indentation multiplier
|
indent, vanityDirectory = determineIndentation(skippedDirList, dirList, i) // calculate the final indentation multiplier
|
||||||
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+core.AnsiReset+"\n", vanityDirectory)
|
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+back.AnsiReset+"\n", vanityDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
charCounter, colorAlternator = printFileEntry(file, lastSlash, charCounter, indent, colorAlternator)
|
charCounter, colorAlternator = printFileEntry(file, lastSlash, charCounter, indent, colorAlternator)
|
||||||
@@ -140,8 +140,8 @@ func EntryListGen() {
|
|||||||
if dirListLength > 1 { // and directories besides the root-level exist... display directory header and empty directory warning
|
if dirListLength > 1 { // and directories besides the root-level exist... display directory header and empty directory warning
|
||||||
skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped
|
skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped
|
||||||
indent, vanityDirectory = determineIndentation(skippedDirList, dirList, i) // calculate the final indentation multiplier
|
indent, vanityDirectory = determineIndentation(skippedDirList, dirList, i) // calculate the final indentation multiplier
|
||||||
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+core.AnsiReset+"\n", vanityDirectory)
|
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+back.AnsiReset+"\n", vanityDirectory)
|
||||||
fmt.Print(strings.Repeat(" ", indent*2) + ansiEmptyDirectoryWarning + "-empty directory-" + core.AnsiReset)
|
fmt.Print(strings.Repeat(" ", indent*2) + ansiEmptyDirectoryWarning + "-empty directory-" + back.AnsiReset)
|
||||||
} else { // warn if the only thing that exists is the root-level directory
|
} else { // warn if the only thing that exists is the root-level directory
|
||||||
fmt.Print("\n\nNothing's here! For help creating your first entry, run \"mutn help\".")
|
fmt.Print("\n\nNothing's here! For help creating your first entry, run \"mutn help\".")
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-9
@@ -4,7 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/rwinkhart/go-boilerplate/back"
|
||||||
"github.com/rwinkhart/libmutton/core"
|
"github.com/rwinkhart/libmutton/core"
|
||||||
|
"github.com/rwinkhart/libmutton/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ansiShownPassword = "\033[38;5;10m"
|
const ansiShownPassword = "\033[38;5;10m"
|
||||||
@@ -19,33 +21,33 @@ func EntryReader(decryptedEntry []string, hideSecrets, syncEnabled bool) {
|
|||||||
// if the first field (password) is not empty, print it
|
// if the first field (password) is not empty, print it
|
||||||
if decryptedEntry[0] != "" {
|
if decryptedEntry[0] != "" {
|
||||||
if !hideSecrets {
|
if !hideSecrets {
|
||||||
fmt.Print(ansiDirectoryHeader + "Password:" + core.AnsiReset + "\n" + ansiShownPassword + decryptedEntry[0] + core.AnsiReset + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "Password:" + back.AnsiReset + "\n" + ansiShownPassword + decryptedEntry[0] + back.AnsiReset + "\n\n")
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(ansiDirectoryHeader + "Password:" + core.AnsiReset + "\n" + ansiEmptyDirectoryWarning + "End command in \"show\" or \"-s\" to view" + core.AnsiReset + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "Password:" + back.AnsiReset + "\n" + ansiEmptyDirectoryWarning + "End command in \"show\" or \"-s\" to view" + back.AnsiReset + "\n\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
// if the second field (username) is not empty, print it
|
// if the second field (username) is not empty, print it
|
||||||
if decryptedEntry[1] != "" {
|
if decryptedEntry[1] != "" {
|
||||||
fmt.Print(ansiDirectoryHeader + "Username:" + core.AnsiReset + "\n" + decryptedEntry[1] + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "Username:" + back.AnsiReset + "\n" + decryptedEntry[1] + "\n\n")
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
// if the third field (TOTP secret) is not empty, print it
|
// if the third field (TOTP secret) is not empty, print it
|
||||||
if decryptedEntry[2] != "" {
|
if decryptedEntry[2] != "" {
|
||||||
if !hideSecrets {
|
if !hideSecrets {
|
||||||
fmt.Print(ansiDirectoryHeader + "TOTP Secret:" + core.AnsiReset + "\n" + ansiShownPassword + decryptedEntry[2] + core.AnsiReset + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "TOTP Secret:" + back.AnsiReset + "\n" + ansiShownPassword + decryptedEntry[2] + back.AnsiReset + "\n\n")
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(ansiDirectoryHeader + "TOTP Secret:" + core.AnsiReset + "\n" + ansiEmptyDirectoryWarning + "End command in \"show\" or \"-s\" to view" + core.AnsiReset + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "TOTP Secret:" + back.AnsiReset + "\n" + ansiEmptyDirectoryWarning + "End command in \"show\" or \"-s\" to view" + back.AnsiReset + "\n\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
// if the fourth field (url) is not empty, print it
|
// if the fourth field (url) is not empty, print it
|
||||||
if decryptedEntry[3] != "" {
|
if decryptedEntry[3] != "" {
|
||||||
fmt.Print(ansiDirectoryHeader + "URL:" + core.AnsiReset + "\n" + decryptedEntry[3] + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "URL:" + back.AnsiReset + "\n" + decryptedEntry[3] + "\n\n")
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
// print the notes header
|
// print the notes header
|
||||||
fmt.Println(ansiDirectoryHeader + "Notes:" + core.AnsiReset)
|
fmt.Println(ansiDirectoryHeader + "Notes:" + back.AnsiReset)
|
||||||
|
|
||||||
// combine remaining fields into a single string (to support Markdown rendering)
|
// combine remaining fields into a single string (to support Markdown rendering)
|
||||||
var noteLines []string
|
var noteLines []string
|
||||||
@@ -62,7 +64,7 @@ func EntryReader(decryptedEntry []string, hideSecrets, syncEnabled bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if syncEnabled {
|
if syncEnabled {
|
||||||
RunJobWrapper(false)
|
sync.RunJob(false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
@@ -70,7 +72,7 @@ func EntryReader(decryptedEntry []string, hideSecrets, syncEnabled bool) {
|
|||||||
|
|
||||||
// EntryReaderDecrypt is a wrapper for EntryReader that first decrypts an RCW-wrapped file before sending it to EntryReader.
|
// EntryReaderDecrypt is a wrapper for EntryReader that first decrypts an RCW-wrapped file before sending it to EntryReader.
|
||||||
func EntryReaderDecrypt(targetLocation string, hideSecrets bool) {
|
func EntryReaderDecrypt(targetLocation string, hideSecrets bool) {
|
||||||
if isFile, _ := core.TargetIsFile(targetLocation, true, 2); isFile {
|
if isFile, _ := back.TargetIsFile(targetLocation, true, 2); isFile {
|
||||||
EntryReader(core.DecryptFileToSlice(targetLocation), hideSecrets, false) // never sync if decrypting straight to EntryReader, as this means the entry could not have been modified
|
EntryReader(core.DecryptFileToSlice(targetLocation), hideSecrets, false) // never sync if decrypting straight to EntryReader, as this means the entry could not have been modified
|
||||||
}
|
}
|
||||||
// do not exit, as this is the job of EntryReader
|
// do not exit, as this is the job of EntryReader
|
||||||
|
|||||||
+16
-14
@@ -7,6 +7,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/rwinkhart/go-boilerplate/back"
|
||||||
|
"github.com/rwinkhart/go-boilerplate/front"
|
||||||
"github.com/rwinkhart/libmutton/core"
|
"github.com/rwinkhart/libmutton/core"
|
||||||
"github.com/rwinkhart/libmutton/sync"
|
"github.com/rwinkhart/libmutton/sync"
|
||||||
)
|
)
|
||||||
@@ -14,30 +16,30 @@ import (
|
|||||||
// TempInitCli initializes the MUTN environment based on user input.
|
// TempInitCli initializes the MUTN environment based on user input.
|
||||||
func TempInitCli() {
|
func TempInitCli() {
|
||||||
// text editor
|
// text editor
|
||||||
textEditor := cmp.Or(input("Text editor (leave blank for $EDITOR, falls back to \""+fallbackEditor+"\"):"), os.Getenv("EDITOR"), fallbackEditor)
|
textEditor := cmp.Or(front.Input("Text editor (leave blank for $EDITOR, falls back to \""+fallbackEditor+"\"):"), os.Getenv("EDITOR"), fallbackEditor)
|
||||||
|
|
||||||
// SSH info
|
// SSH info
|
||||||
configSSH := inputBinary("Configure SSH settings (for synchronization)?")
|
configSSH := front.InputBinary("Configure SSH settings (for synchronization)?")
|
||||||
if configSSH {
|
if configSSH {
|
||||||
// necessary SSH info
|
// necessary SSH info
|
||||||
fmt.Println(AnsiBold + "\nNote:" + core.AnsiReset + " Only key-based authentication is supported (keys may optionally be passphrase-protected).\nThe remote server must already be in your ~" + core.PathSeparator + ".ssh" + core.PathSeparator + "known_hosts file.")
|
fmt.Println(AnsiBold + "\nNote:" + back.AnsiReset + " Only key-based authentication is supported (keys may optionally be passphrase-protected).\nThe remote server must already be in your ~" + core.PathSeparator + ".ssh" + core.PathSeparator + "known_hosts file.")
|
||||||
sshUser := input("Remote SSH username:")
|
sshUser := front.Input("Remote SSH username:")
|
||||||
sshPort := input("Remote SSH port:")
|
sshPort := front.Input("Remote SSH port:")
|
||||||
sshIP := input("Remote SSH IP/domain:")
|
sshIP := front.Input("Remote SSH IP/domain:")
|
||||||
|
|
||||||
// prompt for and ensure existence of SSH identity file
|
// prompt for and ensure existence of SSH identity file
|
||||||
var sshKey string
|
var sshKey string
|
||||||
var sshKeyIsFile bool
|
var sshKeyIsFile bool
|
||||||
for !sshKeyIsFile {
|
for !sshKeyIsFile {
|
||||||
fallbackSSHKey := core.Home + core.PathSeparator + ".ssh" + core.PathSeparator + "id_ed25519"
|
fallbackSSHKey := back.Home + core.PathSeparator + ".ssh" + core.PathSeparator + "id_ed25519"
|
||||||
sshKey = cmp.Or(core.ExpandPathWithHome(input("SSH private identity file path (falls back to \""+fallbackSSHKey+"\"):")), fallbackSSHKey)
|
sshKey = cmp.Or(back.ExpandPathWithHome(front.Input("SSH private identity file path (falls back to \""+fallbackSSHKey+"\"):")), fallbackSSHKey)
|
||||||
sshKeyIsFile, _ = core.TargetIsFile(sshKey, false, 0)
|
sshKeyIsFile, _ = back.TargetIsFile(sshKey, false, 0)
|
||||||
if !sshKeyIsFile {
|
if !sshKeyIsFile {
|
||||||
fmt.Println(core.AnsiError+"SSH identity file not found:", sshKey+core.AnsiReset) // do not exit after error (allow user to retry)
|
fmt.Println(back.AnsiError+"SSH identity file not found:", sshKey+back.AnsiReset) // do not exit after error (allow user to retry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sshKeyProtected := inputBinary("Is the identity file password-protected?")
|
sshKeyProtected := front.InputBinary("Is the identity file password-protected?")
|
||||||
|
|
||||||
// initialize libmutton directories
|
// initialize libmutton directories
|
||||||
oldDeviceID := core.DirInit(false)
|
oldDeviceID := core.DirInit(false)
|
||||||
@@ -60,10 +62,10 @@ func TempInitCli() {
|
|||||||
// RCW sanity check file
|
// RCW sanity check file
|
||||||
var passphraseInput1 []byte
|
var passphraseInput1 []byte
|
||||||
for {
|
for {
|
||||||
passphraseInput1 = inputHidden("New master (RCW) passphrase:")
|
passphraseInput1 = front.InputHidden("New master (RCW) passphrase:")
|
||||||
passphraseInput2 := inputHidden("Confirm RCW passphrase:")
|
passphraseInput2 := front.InputHidden("Confirm RCW passphrase:")
|
||||||
if !bytes.Equal(passphraseInput1, passphraseInput2) {
|
if !bytes.Equal(passphraseInput1, passphraseInput2) {
|
||||||
fmt.Println(core.AnsiError + "Passphrases do not match" + core.AnsiReset)
|
fmt.Println(back.AnsiError + "Passphrases do not match" + back.AnsiReset)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
core.RCWSanityCheckGen(passphraseInput1)
|
core.RCWSanityCheckGen(passphraseInput1)
|
||||||
|
|||||||
+32
-31
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/rwinkhart/go-boilerplate/back"
|
||||||
"github.com/rwinkhart/libmutton/core"
|
"github.com/rwinkhart/libmutton/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,13 +15,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func HelpMain() {
|
func HelpMain() {
|
||||||
fmt.Print(AnsiBold + "\nMUTN | Copyright (c) 2024-2025 Randall Winkhart\n" + core.AnsiReset + `
|
fmt.Print(AnsiBold + "\nMUTN | Copyright (c) 2024-2025 Randall Winkhart\n" + back.AnsiReset + `
|
||||||
This software exists under the MIT license; you may redistribute it under certain conditions.
|
This software exists under the MIT license; you may redistribute it under certain conditions.
|
||||||
This program comes with absolutely no warranty; type "mutn version" for details.
|
This program comes with absolutely no warranty; type "mutn version" for details.
|
||||||
|
|
||||||
` + AnsiBold + "Usage:" + core.AnsiReset + ` mutn [/<entry name> [argument] [option]] | [argument]
|
` + AnsiBold + "Usage:" + back.AnsiReset + ` mutn [/<entry name> [argument] [option]] | [argument]
|
||||||
|
|
||||||
` + AnsiBold + "Arguments:" + core.AnsiReset + `
|
` + AnsiBold + "Arguments:" + back.AnsiReset + `
|
||||||
help Bring up this menu
|
help Bring up this menu
|
||||||
version Display version and license information
|
version Display version and license information
|
||||||
init Set up MUTN (generates libmutton.ini)
|
init Set up MUTN (generates libmutton.ini)
|
||||||
@@ -31,7 +32,7 @@ This program comes with absolutely no warranty; type "mutn version" for details.
|
|||||||
shear Delete an existing entry
|
shear Delete an existing entry
|
||||||
sync Manually sync the entry directory
|
sync Manually sync the entry directory
|
||||||
|
|
||||||
` + AnsiBold + "Options:" + core.AnsiReset + `
|
` + AnsiBold + "Options:" + back.AnsiReset + `
|
||||||
copy:
|
copy:
|
||||||
password|-pw|<blank> Copy the password in an entry to your clipboard
|
password|-pw|<blank> Copy the password in an entry to your clipboard
|
||||||
username|-u Copy the username in an entry to your clipboard
|
username|-u Copy the username in an entry to your clipboard
|
||||||
@@ -52,17 +53,17 @@ This program comes with absolutely no warranty; type "mutn version" for details.
|
|||||||
note|-n Add a note entry
|
note|-n Add a note entry
|
||||||
folder|-f Add a new folder for entries
|
folder|-f Add a new folder for entries
|
||||||
|
|
||||||
` + AnsiBold + "Tip 1:" + core.AnsiReset + ` You can quickly read an entry with "mutn /<entry name>"
|
` + AnsiBold + "Tip 1:" + back.AnsiReset + ` You can quickly read an entry with "mutn /<entry name>"
|
||||||
` + AnsiBold + "Tip 2:" + core.AnsiReset + ` Type "mutn" (no arguments/options) to view a list of saved entries
|
` + AnsiBold + "Tip 2:" + back.AnsiReset + ` Type "mutn" (no arguments/options) to view a list of saved entries
|
||||||
` + AnsiBold + "Tip 3:" + core.AnsiReset + ` Provide "add", "edit", "copy", or "gen" as the only argument to receive more specific help
|
` + AnsiBold + "Tip 3:" + back.AnsiReset + ` Provide "add", "edit", "copy", or "gen" as the only argument to receive more specific help
|
||||||
` + AnsiBold + "Tip 4:" + core.AnsiReset + " Using \"add\", \"edit\", or \"copy\" without specifying an option (field) will default to \"password\"\n\n")
|
` + AnsiBold + "Tip 4:" + back.AnsiReset + " Using \"add\", \"edit\", or \"copy\" without specifying an option (field) will default to \"password\"\n\n")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HelpAdd() {
|
func HelpAdd() {
|
||||||
fmt.Print(AnsiBold + "\nUsage:" + core.AnsiReset + ` mutn /<entry name> add <option>
|
fmt.Print(AnsiBold + "\nUsage:" + back.AnsiReset + ` mutn /<entry name> add <option>
|
||||||
|
|
||||||
` + AnsiBold + "Options:" + core.AnsiReset + `
|
` + AnsiBold + "Options:" + back.AnsiReset + `
|
||||||
add:
|
add:
|
||||||
password|-pw|<blank> Add a password entry
|
password|-pw|<blank> Add a password entry
|
||||||
note|-n Add a note entry
|
note|-n Add a note entry
|
||||||
@@ -71,9 +72,9 @@ func HelpAdd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HelpEdit() {
|
func HelpEdit() {
|
||||||
fmt.Print(AnsiBold + "\nUsage:" + core.AnsiReset + ` mutn /<entry name> edit <option>
|
fmt.Print(AnsiBold + "\nUsage:" + back.AnsiReset + ` mutn /<entry name> edit <option>
|
||||||
|
|
||||||
` + AnsiBold + "Options:" + core.AnsiReset + `
|
` + AnsiBold + "Options:" + back.AnsiReset + `
|
||||||
edit:
|
edit:
|
||||||
password|-pw|<blank> Change the password in an entry
|
password|-pw|<blank> Change the password in an entry
|
||||||
username|-u Change the username in an entry
|
username|-u Change the username in an entry
|
||||||
@@ -85,9 +86,9 @@ func HelpEdit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HelpCopy() {
|
func HelpCopy() {
|
||||||
fmt.Print(AnsiBold + "\nUsage:" + core.AnsiReset + ` mutn /<entry name> copy <option>
|
fmt.Print(AnsiBold + "\nUsage:" + back.AnsiReset + ` mutn /<entry name> copy <option>
|
||||||
|
|
||||||
` + AnsiBold + "Options:" + core.AnsiReset + `
|
` + AnsiBold + "Options:" + back.AnsiReset + `
|
||||||
copy:
|
copy:
|
||||||
password|-pw|<blank> Copy the password in an entry to your clipboard
|
password|-pw|<blank> Copy the password in an entry to your clipboard
|
||||||
username|-u Copy the username in an entry to your clipboard
|
username|-u Copy the username in an entry to your clipboard
|
||||||
@@ -98,18 +99,18 @@ func HelpCopy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HelpGen() {
|
func HelpGen() {
|
||||||
fmt.Print(AnsiBold + "\nUsage:" + core.AnsiReset + ` mutn /<entry name> gen [option]
|
fmt.Print(AnsiBold + "\nUsage:" + back.AnsiReset + ` mutn /<entry name> gen [option]
|
||||||
|
|
||||||
` + AnsiBold + "Options:" + core.AnsiReset + `
|
` + AnsiBold + "Options:" + back.AnsiReset + `
|
||||||
gen:
|
gen:
|
||||||
update|-u Generate a password for an existing entry
|
update|-u Generate a password for an existing entry
|
||||||
|
|
||||||
` + AnsiBold + "Tip:" + core.AnsiReset + " If no options are provided, a new password entry is generated\n\n")
|
` + AnsiBold + "Tip:" + back.AnsiReset + " If no options are provided, a new password entry is generated\n\n")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MITLicense() {
|
func MITLicense() {
|
||||||
fmt.Print(AnsiBold + "\n MIT License" + core.AnsiReset + `
|
fmt.Print(AnsiBold + "\n MIT License" + back.AnsiReset + `
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
Permission is hereby granted, free of charge, to any
|
||||||
person obtaining a copy of this software and associated
|
person obtaining a copy of this software and associated
|
||||||
@@ -141,20 +142,20 @@ func Version() {
|
|||||||
MITLicense()
|
MITLicense()
|
||||||
fmt.Print("\n\n MUTN is a simple, self-hosted,\n SSH-synchronized password manager based on libmutton\n\n" +
|
fmt.Print("\n\n MUTN is a simple, self-hosted,\n SSH-synchronized password manager based on libmutton\n\n" +
|
||||||
" .. ..\n" +
|
" .. ..\n" +
|
||||||
" /()\\''.''. " + ansiVersionMeat + "♥♥♥♥" + core.AnsiReset + " .''.''/()\\ _)\n" +
|
" /()\\''.''. " + ansiVersionMeat + "♥♥♥♥" + back.AnsiReset + " .''.''/()\\ _)\n" +
|
||||||
" _. : * " + ansiVersionMeat + "♥♥♥♥♥♥ ♥♥♥♥♥♥♥♥" + core.AnsiReset + " * : <[◎]|_|=\n" +
|
" _. : * " + ansiVersionMeat + "♥♥♥♥♥♥ ♥♥♥♥♥♥♥♥" + back.AnsiReset + " * : <[◎]|_|=\n" +
|
||||||
" }-}-*] `..'..' " + ansiVersionMeat + "♥♥♥♥♥♥♥♥♥♥♥♥♥" + core.AnsiReset + " `..'..' |\n" +
|
" }-}-*] `..'..' " + ansiVersionMeat + "♥♥♥♥♥♥♥♥♥♥♥♥♥" + back.AnsiReset + " `..'..' |\n" +
|
||||||
" ◎-◎ // \\\\ " + ansiVersionMeat + "♥♥♥♥♥♥♥♥♥" + core.AnsiReset + " // \\\\ /|\\\n" +
|
" ◎-◎ // \\\\ " + ansiVersionMeat + "♥♥♥♥♥♥♥♥♥" + back.AnsiReset + " // \\\\ /|\\\n" +
|
||||||
ansiVersionOutline + "<><><><><><><><><><><><><><>-<><><><><><><><><><><><><><>\n" +
|
ansiVersionOutline + "<><><><><><><><><><><><><><>-<><><><><><><><><><><><><><>\n" +
|
||||||
"\\" + ansiBlackOnWhite + " " + core.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " " + back.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " MUTN v" + MUTNVersion + " " + core.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " MUTN v" + MUTNVersion + " " + back.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " The Tripe Transmission Update " + core.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " The Tripe Transmission Update " + back.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " " + core.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " " + back.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " Built with libmutton v" + core.LibmuttonVersion + " " + core.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " Built with libmutton v" + core.LibmuttonVersion + " " + back.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " " + core.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " " + back.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " Copyright (c) 2024-2025: Randall Winkhart " + core.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " Copyright (c) 2024-2025: Randall Winkhart " + back.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " " + core.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " " + back.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"<><><><><><><><><><><><><><>-<><><><><><><><><><><><><><>\n" + core.AnsiReset +
|
"<><><><><><><><><><><><><><>-<><><><><><><><><><><><><><>\n" + back.AnsiReset +
|
||||||
"\n For more information, see:\n\n" +
|
"\n For more information, see:\n\n" +
|
||||||
" https://github.com/rwinkhart/MUTN\n" +
|
" https://github.com/rwinkhart/MUTN\n" +
|
||||||
" https://github.com/rwinkhart/libmutton\n\n")
|
" https://github.com/rwinkhart/libmutton\n\n")
|
||||||
|
|||||||
@@ -1,74 +1,19 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rwinkhart/go-boilerplate/back"
|
||||||
|
"github.com/rwinkhart/go-boilerplate/front"
|
||||||
"github.com/rwinkhart/libmutton/core"
|
"github.com/rwinkhart/libmutton/core"
|
||||||
"github.com/rwinkhart/libmutton/sync"
|
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// input prompts the user for input and returns the input as a string.
|
|
||||||
func input(prompt string) string {
|
|
||||||
fmt.Print("\n" + prompt + " ")
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
userInput, _ := reader.ReadString('\n')
|
|
||||||
return strings.TrimRight(userInput, "\n\r ") // remove trailing newlines, carriage returns, and spaces
|
|
||||||
}
|
|
||||||
|
|
||||||
// inputHidden prompts the user for input and returns the input as a byte array, hiding the input from the terminal.
|
|
||||||
func inputHidden(prompt string) []byte {
|
|
||||||
fmt.Print("\n" + prompt + " ")
|
|
||||||
byteInput, _ := term.ReadPassword(int(os.Stdin.Fd()))
|
|
||||||
fmt.Println()
|
|
||||||
return byteInput
|
|
||||||
}
|
|
||||||
|
|
||||||
// inputInt prompts the user for input and returns the input as an integer.
|
|
||||||
// A maxValue of 0 will cause the function to return 0, an error - a negative maxValue will disable the maxValue check.
|
|
||||||
func inputInt(prompt string, maxValue int) int {
|
|
||||||
if maxValue == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// loop until a valid integer is entered
|
|
||||||
for {
|
|
||||||
fmt.Print("\n" + prompt + " ")
|
|
||||||
var userInput int
|
|
||||||
_, err := fmt.Scanln(&userInput)
|
|
||||||
if err == nil && userInput > 0 && (userInput <= maxValue || maxValue < 0) {
|
|
||||||
return userInput
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// inputBinary prompts the user with a yes/no question and returns the response as a boolean.
|
|
||||||
func inputBinary(prompt string) bool {
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
fmt.Print("\n" + prompt + " (y/N) ")
|
|
||||||
char, _, _ := reader.ReadRune()
|
|
||||||
if char == 'y' || char == 'Y' {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// inputMenuGen prompts the user with a menu and returns the user's choice as an integer.
|
|
||||||
func inputMenuGen(prompt string, options []string) int {
|
|
||||||
for i, option := range options {
|
|
||||||
fmt.Printf("%d. %s\n", i+1, option)
|
|
||||||
}
|
|
||||||
return inputInt(prompt, len(options))
|
|
||||||
}
|
|
||||||
|
|
||||||
// inputPasswordGen prompts the user for password generation parameters and returns a generated password as a string.
|
// inputPasswordGen prompts the user for password generation parameters and returns a generated password as a string.
|
||||||
func inputPasswordGen() string {
|
func inputPasswordGen() string {
|
||||||
passLength := inputInt("Password length:", -1)
|
passLength := front.InputPositiveInt("Password length:", 1, -1)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
passCharset := uint8(inputMenuGen("Password complexity:", []string{"Simple", "Complex", "Ultra Complex (not compatible with many services)"}))
|
passCharset := uint8(front.InputMenuGen("Password complexity:", []string{"Simple", "Complex", "Ultra Complex (not compatible with many services)"}))
|
||||||
var complexity float64
|
var complexity float64
|
||||||
switch passCharset {
|
switch passCharset {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -87,15 +32,9 @@ func writeEntryCLI(targetLocation string, unencryptedEntry []string, hideSecrets
|
|||||||
// write the entry to the target location
|
// write the entry to the target location
|
||||||
core.WriteEntry(targetLocation, []byte(strings.Join(unencryptedEntry, "\n")))
|
core.WriteEntry(targetLocation, []byte(strings.Join(unencryptedEntry, "\n")))
|
||||||
// preview the entry
|
// preview the entry
|
||||||
fmt.Println(AnsiBold + "\nEntry Preview:" + core.AnsiReset)
|
fmt.Println(AnsiBold + "\nEntry Preview:" + back.AnsiReset)
|
||||||
EntryReader(unencryptedEntry, hideSecrets, true)
|
EntryReader(unencryptedEntry, hideSecrets, true)
|
||||||
} else {
|
} else {
|
||||||
core.PrintError("No data supplied for entry", core.ErrorTargetNotFound, true)
|
back.PrintError("No data supplied for entry", back.ErrorTargetNotFound, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunJobWrapper is a wrapper for sync.RunJob that sets the passphrase input function to inputHidden.
|
|
||||||
func RunJobWrapper(manualSync bool) {
|
|
||||||
core.PassphraseInputFunction = inputHidden
|
|
||||||
sync.RunJob(manualSync, false)
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user