diff --git a/go.mod b/go.mod index cf96ddf..1a10514 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.24.3 require ( github.com/Trojan2021/BEAN v0.0.0-20241210230804-8f294833b514 github.com/charmbracelet/glamour v0.7.0 - github.com/rwinkhart/go-boilerplate v0.0.0-20250509173525-20670ec7bb9c - github.com/rwinkhart/libmutton v0.3.2-0.20250521151921-02c663447a55 + github.com/rwinkhart/go-boilerplate v0.0.0-20250529185306-e2e64d7fa43b + github.com/rwinkhart/libmutton v0.3.2-0.20250529233550-74b8261bc88f golang.org/x/term v0.32.0 ) diff --git a/go.sum b/go.sum index 7576954..699ba3d 100644 --- a/go.sum +++ b/go.sum @@ -57,12 +57,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/rwinkhart/convertroman v0.2.0 h1:otUm939eXT77q/Lr31mJtMwVWEw0RjEUB71gO65h9OM= github.com/rwinkhart/convertroman v0.2.0/go.mod h1:Af6HqvX0EIM4Y3HcnNXbbRey1dLasGB7WU0+3Cowgmw= -github.com/rwinkhart/go-boilerplate v0.0.0-20250509173525-20670ec7bb9c h1:RIMnYf1MwsvmAr9E0/cpn7rTh8BWdfsQ2r31ITKJp2A= -github.com/rwinkhart/go-boilerplate v0.0.0-20250509173525-20670ec7bb9c/go.mod h1:cnzIF45I0FCOvE4YIB+26pLCUx2kWyY2llKYZruNaRY= +github.com/rwinkhart/go-boilerplate v0.0.0-20250529185306-e2e64d7fa43b h1:ENgsUlCmYktd1eauEkjW6Fu8rgZzyGOS/m/6jc968xI= +github.com/rwinkhart/go-boilerplate v0.0.0-20250529185306-e2e64d7fa43b/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/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/rwinkhart/libmutton v0.3.2-0.20250521151921-02c663447a55 h1:eYUJ/h+lN6O/ovrrSM9vLidWAmRiViLyeMRl5P0DD4Q= -github.com/rwinkhart/libmutton v0.3.2-0.20250521151921-02c663447a55/go.mod h1:noIP2Ov56fNq2McVhkOj5+VVLOV4E2eie2HbuOhXhXA= +github.com/rwinkhart/libmutton v0.3.2-0.20250529233550-74b8261bc88f h1:HL3z/oTAqssmILfA3Z0IAscK1sbxFp8/g9GnhHSNLSY= +github.com/rwinkhart/libmutton v0.3.2-0.20250529233550-74b8261bc88f/go.mod h1:uIz4AJcXW7ZHvvoPlLNWQofTBIbFa1UJ2JI6O+FSQ60= github.com/rwinkhart/peercred-mini v0.1.0 h1:TiS6u8cEWzW55S9X4iVpU72Iuy/NG6rMUJMtUEVEFLw= github.com/rwinkhart/peercred-mini v0.1.0/go.mod h1:+x4Mxc2veE+YePSOpcUasjimh7n799c1KraLuQwHR20= github.com/rwinkhart/rcw v0.2.0 h1:/rFgJ+20OSThipKAX5KPwW2xbPpgh1zl49THmEkkPis= diff --git a/mutn.go b/mutn.go index 960f1a2..dbf71ef 100644 --- a/mutn.go +++ b/mutn.go @@ -1,11 +1,15 @@ package main import ( + "bytes" + "cmp" + "fmt" "os" "strings" "github.com/rwinkhart/MUTN/src/cli" "github.com/rwinkhart/go-boilerplate/back" + "github.com/rwinkhart/go-boilerplate/front" "github.com/rwinkhart/libmutton/core" "github.com/rwinkhart/libmutton/crypt" "github.com/rwinkhart/libmutton/global" @@ -166,7 +170,22 @@ func main() { case "sync": syncclient.RunJob(true, false) case "init": - cli.TempInitCli() + const fallbackEditor = "vi" // TODO "edit" for Windows + var rcwPass []byte + for { // get master passphrase + rcwPass = front.InputHidden("Master passphrase:") + if !bytes.Equal(rcwPass, front.InputHidden("Confirm master passphrase:")) || len(rcwPass) == 0 { + fmt.Println(back.AnsiError + "Passphrases do not match" + back.AnsiReset) + continue + } + break + } + err := core.LibmuttonInit(front.Input, + [][3]string{{"MUTN", "textEditor", cmp.Or(front.Input("Text editor (leave blank for $EDITOR, falls back to \""+fallbackEditor+"\"):"), os.Getenv("EDITOR"), fallbackEditor)}}, + rcwPass, false) + if err != nil { + back.PrintError("Initialization failed: "+err.Error(), 0, true) + } case "tweak": back.PrintError("\"tweak\" is not yet implemented", 0, true) case "copy": diff --git a/src/cli/1globals.go b/src/cli/1globals.go index 288351d..fc2d2ba 100644 --- a/src/cli/1globals.go +++ b/src/cli/1globals.go @@ -15,7 +15,6 @@ var ( const ( MUTNVersion = "0.D.0" // untagged releases feature a letter suffix corresponding to the eventual release version, e.g "0.2.A" -> "0.2.0", "0.2.B" -> "0.2.1" - AnsiBold = "\033[1m" ansiBlackOnWhite = "\033[38;5;0;48;5;15m" ) diff --git a/src/cli/edit.go b/src/cli/edit.go index 4eb0d75..9e0db88 100644 --- a/src/cli/edit.go +++ b/src/cli/edit.go @@ -8,6 +8,7 @@ import ( "github.com/rwinkhart/go-boilerplate/back" "github.com/rwinkhart/go-boilerplate/front" + "github.com/rwinkhart/libmutton/cfg" "github.com/rwinkhart/libmutton/core" "github.com/rwinkhart/libmutton/syncclient" ) @@ -74,7 +75,7 @@ func editNote(baseNote []string) ([]string, bool) { }(tempFile.Name()) // fetch the user's text editor - editorCfg, _ := core.ParseConfig([][2]string{{"MUTN", "textEditor"}}, "") + editorCfg, _ := cfg.ParseConfig([][2]string{{"MUTN", "textEditor"}}, "") editor := editorCfg[0] // write baseNote to tempFile (if it is not empty) diff --git a/src/cli/init.go b/src/cli/init.go deleted file mode 100644 index 61605ca..0000000 --- a/src/cli/init.go +++ /dev/null @@ -1,74 +0,0 @@ -package cli - -import ( - "bytes" - "cmp" - "fmt" - "os" - "strconv" - - "github.com/rwinkhart/go-boilerplate/back" - "github.com/rwinkhart/go-boilerplate/front" - "github.com/rwinkhart/libmutton/core" - "github.com/rwinkhart/libmutton/global" - "github.com/rwinkhart/libmutton/syncclient" -) - -// TempInitCli initializes the MUTN environment based on user input. -func TempInitCli() { - // text editor - textEditor := cmp.Or(front.Input("Text editor (leave blank for $EDITOR, falls back to \""+fallbackEditor+"\"):"), os.Getenv("EDITOR"), fallbackEditor) - - // SSH info - configSSH := front.InputBinary("Configure SSH settings (for synchronization)?") - if configSSH { - // necessary SSH info - 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 ~" + global.PathSeparator + ".ssh" + global.PathSeparator + "known_hosts file.") - sshUser := front.Input("Remote SSH username:") - sshPort := front.Input("Remote SSH port:") - sshIP := front.Input("Remote SSH IP/domain:") - - // prompt for and ensure existence of SSH identity file - var sshKey string - var sshKeyIsFile bool - for !sshKeyIsFile { - fallbackSSHKey := back.Home + global.PathSeparator + ".ssh" + global.PathSeparator + "id_ed25519" - sshKey = cmp.Or(back.ExpandPathWithHome(front.Input("SSH private identity file path (falls back to \""+fallbackSSHKey+"\"):")), fallbackSSHKey) - sshKeyIsFile, _ = back.TargetIsFile(sshKey, false, 0) - if !sshKeyIsFile { - fmt.Println(back.AnsiError+"SSH identity file not found:", sshKey+back.AnsiReset) // do not exit after error (allow user to retry) - } - } - - sshKeyProtected := front.InputBinary("Is the identity file password-protected?") - - // initialize libmutton directories - oldDeviceID := global.DirInit(false) - - // write config file (temporarily assigns sshEntryRoot and sshIsWindows to null to pass initial device ID registration) - core.WriteConfig([][3]string{{"MUTN", "textEditor", textEditor}, {"LIBMUTTON", "sshUser", sshUser}, {"LIBMUTTON", "sshIP", sshIP}, {"LIBMUTTON", "sshPort", sshPort}, {"LIBMUTTON", "sshKey", sshKey}, {"LIBMUTTON", "sshKeyProtected", strconv.FormatBool(sshKeyProtected)}, {"LIBMUTTON", "sshEntryRoot", "null"}, {"LIBMUTTON", "sshIsWindows", "false"}}, nil, false) - - // generate and register device ID - sshEntryRoot, sshIsWindows := syncclient.DeviceIDGen(oldDeviceID) - - // update config file with sshEntryRoot and sshIsWindows - core.WriteConfig([][3]string{{"LIBMUTTON", "sshEntryRoot", sshEntryRoot}, {"LIBMUTTON", "sshIsWindows", sshIsWindows}}, nil, true) - } else { - // initialize libmutton directories - global.DirInit(false) - - // write config file - core.WriteConfig([][3]string{{"MUTN", "textEditor", textEditor}}, nil, false) - } - // RCW sanity check file - var passphrase []byte - for { - passphrase = front.InputHidden("Master passphrase:") - if !bytes.Equal(passphrase, front.InputHidden("Confirm master passphrase:")) { - fmt.Println(back.AnsiError + "Passphrases do not match" + back.AnsiReset) - continue - } - core.RCWSanityCheckGen(passphrase) - break - } -} diff --git a/src/cli/printInfo.go b/src/cli/printInfo.go index 47bba2c..22d3c65 100644 --- a/src/cli/printInfo.go +++ b/src/cli/printInfo.go @@ -15,13 +15,13 @@ const ( ) func HelpMain() { - fmt.Print(AnsiBold + "\nMUTN | Copyright (c) 2024-2025 Randall Winkhart\n" + back.AnsiReset + ` + fmt.Print(back.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 program comes with absolutely no warranty; type "mutn version" for details. -` + AnsiBold + "Usage:" + back.AnsiReset + ` mutn [/ [argument] [option]] | [argument] +` + back.AnsiBold + "Usage:" + back.AnsiReset + ` mutn [/ [argument] [option]] | [argument] -` + AnsiBold + "Arguments:" + back.AnsiReset + ` +` + back.AnsiBold + "Arguments:" + back.AnsiReset + ` help Bring up this menu version Display version and license information init Set up MUTN (generates libmutton.ini) @@ -32,7 +32,7 @@ This program comes with absolutely no warranty; type "mutn version" for details. shear Delete an existing entry sync Manually sync the entry directory -` + AnsiBold + "Options:" + back.AnsiReset + ` +` + back.AnsiBold + "Options:" + back.AnsiReset + ` copy: password|-pw| Copy the password in an entry to your clipboard username|-u Copy the username in an entry to your clipboard @@ -53,17 +53,17 @@ This program comes with absolutely no warranty; type "mutn version" for details. note|-n Add a note entry folder|-f Add a new folder for entries -` + AnsiBold + "Tip 1:" + back.AnsiReset + ` You can quickly read an entry with "mutn /" -` + AnsiBold + "Tip 2:" + back.AnsiReset + ` Type "mutn" (no arguments/options) to view a list of saved entries -` + AnsiBold + "Tip 3:" + back.AnsiReset + ` Provide "add", "edit", "copy", or "gen" as the only argument to receive more specific help -` + AnsiBold + "Tip 4:" + back.AnsiReset + " Using \"add\", \"edit\", or \"copy\" without specifying an option (field) will default to \"password\"\n\n") +` + back.AnsiBold + "Tip 1:" + back.AnsiReset + ` You can quickly read an entry with "mutn /" +` + back.AnsiBold + "Tip 2:" + back.AnsiReset + ` Type "mutn" (no arguments/options) to view a list of saved entries +` + back.AnsiBold + "Tip 3:" + back.AnsiReset + ` Provide "add", "edit", "copy", or "gen" as the only argument to receive more specific help +` + back.AnsiBold + "Tip 4:" + back.AnsiReset + " Using \"add\", \"edit\", or \"copy\" without specifying an option (field) will default to \"password\"\n\n") os.Exit(0) } func HelpAdd() { - fmt.Print(AnsiBold + "\nUsage:" + back.AnsiReset + ` mutn / add