mirror of
https://github.com/rwinkhart/MUTN.git
synced 2026-09-02 15:17:24 -04:00
Refactor for better modularity (share more code among edit functions, rename "offline" package to "backend"
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
gofmt -l -w -s ./src/cli/*.go
|
gofmt -l -w -s ./src/cli/*.go
|
||||||
gofmt -l -w -s ./src/offline/*.go
|
gofmt -l -w -s ./src/backend/*.go
|
||||||
git add -f extra src .gitignore commit.sh go.mod go.sum LICENSE main.go README.md
|
git add -f extra src .gitignore commit.sh go.mod go.sum LICENSE main.go README.md
|
||||||
git commit -m "$1"
|
git commit -m "$1"
|
||||||
git push
|
git push
|
||||||
|
|||||||
+2
-2
@@ -1,14 +1,14 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
termy "golang.org/x/crypto/ssh/terminal"
|
termy "golang.org/x/crypto/ssh/terminal"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// global variables used across multiple files
|
// global variables used across multiple files
|
||||||
var (
|
var (
|
||||||
rootLength = len(offline.EntryRoot)
|
rootLength = len(backend.EntryRoot)
|
||||||
width, _, _ = termy.GetSize(int(os.Stdout.Fd()))
|
width, _, _ = termy.GetSize(int(os.Stdout.Fd()))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -2,16 +2,16 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddEntry creates a new entry at targetLocation by taking user input via CLI prompts
|
// AddEntry creates a new entry at targetLocation by taking user input via CLI prompts
|
||||||
// entryType: 0 = standard (password), 1 = auto-generated password, 2 = note
|
// entryType: 0 = standard (password), 1 = auto-generated password, 2 = note
|
||||||
func AddEntry(targetLocation string, hidePassword bool, entryType uint8) {
|
func AddEntry(targetLocation string, hidePassword bool, entryType uint8) {
|
||||||
_, isAccessible := offline.TargetIsFile(targetLocation, false, 0)
|
_, isAccessible := backend.TargetIsFile(targetLocation, false, 0)
|
||||||
if isAccessible {
|
if isAccessible {
|
||||||
fmt.Println(offline.AnsiError + "Target location already exists" + offline.AnsiReset)
|
fmt.Println(backend.AnsiError + "Target location already exists" + backend.AnsiReset)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ func AddEntry(targetLocation string, hidePassword bool, entryType uint8) {
|
|||||||
if entryType == 0 {
|
if entryType == 0 {
|
||||||
password = inputHidden("Password:")
|
password = inputHidden("Password:")
|
||||||
} else {
|
} else {
|
||||||
password = offline.StringGen(inputInt("Password length:", -1), inputBinary("Generate a complex (special characters) password?"), 0.2)
|
password = backend.StringGen(inputInt("Password length:", -1), inputBinary("Generate a complex (special characters) password?"), 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
url := input("URL:")
|
url := input("URL:")
|
||||||
@@ -41,5 +41,5 @@ func AddEntry(targetLocation string, hidePassword bool, entryType uint8) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write and preview the new entry
|
// write and preview the new entry
|
||||||
writeEntryShortcut(targetLocation, unencryptedEntry, hidePassword)
|
writeEntryCLI(targetLocation, unencryptedEntry, hidePassword)
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-39
@@ -3,7 +3,7 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -13,25 +13,19 @@ import (
|
|||||||
// RenameCli renames an entry at oldLocation to a new location (user input)
|
// RenameCli renames an entry at oldLocation to a new location (user input)
|
||||||
func RenameCli(oldLocation string) {
|
func RenameCli(oldLocation string) {
|
||||||
// ensure targetLocation exists
|
// ensure targetLocation exists
|
||||||
offline.TargetIsFile(oldLocation, true, 0)
|
backend.TargetIsFile(oldLocation, true, 0)
|
||||||
|
|
||||||
// prompt user for new location and rename
|
// prompt user for new location and rename
|
||||||
newLocation := offline.EntryRoot + offline.PathSeparator + input("New location:")
|
newLocation := backend.EntryRoot + backend.PathSeparator + input("New location:")
|
||||||
offline.Rename(oldLocation, newLocation)
|
backend.Rename(oldLocation, newLocation)
|
||||||
|
|
||||||
// exit is done from offline.Rename
|
// exit is done from backend.Rename
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditEntry edits a field of an entry at targetLocation (user input), does not allow for editing notes
|
// EditEntryField edits a field of an entry at targetLocation (user input), does not allow for editing notes
|
||||||
func EditEntry(targetLocation string, hidePassword bool, field int) {
|
func EditEntryField(targetLocation string, hidePassword bool, field int) {
|
||||||
// ensure targetLocation exists
|
// fetch old entry data (with all required lines present)
|
||||||
offline.TargetIsFile(targetLocation, true, 2)
|
unencryptedEntry := backend.GetOldEntryData(targetLocation, field)
|
||||||
|
|
||||||
// read old entry data
|
|
||||||
unencryptedEntry := offline.DecryptGPG(targetLocation)
|
|
||||||
|
|
||||||
// ensure slice is long enough for field
|
|
||||||
unencryptedEntry = offline.EnsureSliceLength(unencryptedEntry, field)
|
|
||||||
|
|
||||||
// edit the field
|
// edit the field
|
||||||
switch field {
|
switch field {
|
||||||
@@ -44,20 +38,13 @@ func EditEntry(targetLocation string, hidePassword bool, field int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write and preview the modified entry
|
// write and preview the modified entry
|
||||||
writeEntryShortcut(targetLocation, unencryptedEntry, hidePassword)
|
writeEntryCLI(targetLocation, unencryptedEntry, hidePassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditEntryNote edits the note of an entry at targetLocation (user input)
|
// EditEntryNote edits the note of an entry at targetLocation (user input)
|
||||||
func EditEntryNote(targetLocation string, hidePassword bool) {
|
func EditEntryNote(targetLocation string, hidePassword bool) {
|
||||||
// ensure targetLocation exists
|
// fetch old entry data (with all required lines present)
|
||||||
offline.TargetIsFile(targetLocation, true, 2)
|
unencryptedEntry := backend.GetOldEntryData(targetLocation, 2)
|
||||||
|
|
||||||
// read old entry data
|
|
||||||
unencryptedEntry := offline.DecryptGPG(targetLocation)
|
|
||||||
|
|
||||||
// ensure slice is long enough for note
|
|
||||||
// avoids errors when storing non-note data
|
|
||||||
unencryptedEntry = offline.EnsureSliceLength(unencryptedEntry, 2) // 2 is used because it is the index of URL, the last non-note field
|
|
||||||
|
|
||||||
// store non-note data separately
|
// store non-note data separately
|
||||||
nonNoteData := unencryptedEntry[:3]
|
nonNoteData := unencryptedEntry[:3]
|
||||||
@@ -68,36 +55,33 @@ func EditEntryNote(targetLocation string, hidePassword bool) {
|
|||||||
// 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
|
||||||
fmt.Println(offline.AnsiError + "Entry is unchanged" + offline.AnsiReset)
|
fmt.Println(backend.AnsiError + "Entry is unchanged" + backend.AnsiReset)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
unencryptedEntry = append(nonNoteData, editedNote...)
|
unencryptedEntry = append(nonNoteData, editedNote...)
|
||||||
|
|
||||||
// write and preview the modified entry
|
// write and preview the modified entry
|
||||||
writeEntryShortcut(targetLocation, unencryptedEntry, hidePassword)
|
writeEntryCLI(targetLocation, unencryptedEntry, hidePassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenUpdate generates a new password for an entry at targetLocation (user input)
|
// GenUpdate generates a new password for an entry at targetLocation (user input)
|
||||||
func GenUpdate(targetLocation string, hidePassword bool) {
|
func GenUpdate(targetLocation string, hidePassword bool) {
|
||||||
// ensure targetLocation exists
|
// fetch old entry data
|
||||||
offline.TargetIsFile(targetLocation, true, 2)
|
unencryptedEntry := backend.GetOldEntryData(targetLocation, 0)
|
||||||
|
|
||||||
// read old entry data
|
|
||||||
unencryptedEntry := offline.DecryptGPG(targetLocation)
|
|
||||||
|
|
||||||
// generate a new password
|
// generate a new password
|
||||||
unencryptedEntry[0] = offline.StringGen(inputInt("Password length:", -1), inputBinary("Generate a complex (special characters) password?"), 0.2)
|
unencryptedEntry[0] = backend.StringGen(inputInt("Password length:", -1), inputBinary("Generate a complex (special characters) password?"), 0.2)
|
||||||
|
|
||||||
// write and preview the modified entry
|
// write and preview the modified entry
|
||||||
writeEntryShortcut(targetLocation, unencryptedEntry, hidePassword)
|
writeEntryCLI(targetLocation, unencryptedEntry, hidePassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 := offline.CreateTempFile()
|
tempFile := backend.CreateTempFile()
|
||||||
defer os.Remove(tempFile.Name())
|
defer os.Remove(tempFile.Name())
|
||||||
editor := offline.ReadConfig([]string{"textEditor"})[0]
|
editor := backend.ReadConfig([]string{"textEditor"})[0]
|
||||||
|
|
||||||
// write baseNote to tempFile (if it is not empty)
|
// write baseNote to tempFile (if it is not empty)
|
||||||
if len(baseNote) > 0 {
|
if len(baseNote) > 0 {
|
||||||
@@ -116,13 +100,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(offline.AnsiError + "Failed to write note with " + editor + offline.AnsiReset)
|
panic(backend.AnsiError + "Failed to write note with " + editor + backend.AnsiReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(offline.AnsiError + "Failed to write note with " + editor + offline.AnsiReset)
|
panic(backend.AnsiError + "Failed to write note with " + editor + backend.AnsiReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the edited note from the tempFile
|
// read the edited note from the tempFile
|
||||||
@@ -136,7 +120,7 @@ func editNote(baseNote []string) ([]string, bool) {
|
|||||||
tempFile.Close()
|
tempFile.Close()
|
||||||
|
|
||||||
// remove trailing empty strings from the edited note
|
// remove trailing empty strings from the edited note
|
||||||
note = offline.RemoveTrailingEmptyStrings(note)
|
note = backend.RemoveTrailingEmptyStrings(note)
|
||||||
|
|
||||||
// trim trailing whitespace from each note line
|
// trim trailing whitespace from each note line
|
||||||
for i, line := range note {
|
for i, line := range note {
|
||||||
|
|||||||
+11
-11
@@ -2,7 +2,7 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -23,10 +23,10 @@ func determineIndentation(skippedDirList []bool, dirList []string, currentDirInd
|
|||||||
var trimmedDirectory = dirList[currentDirIndex]
|
var trimmedDirectory = dirList[currentDirIndex]
|
||||||
|
|
||||||
// determine initial indentation multiplier based on PathSeparator occurrences
|
// determine initial indentation multiplier based on PathSeparator occurrences
|
||||||
indent := strings.Count(trimmedDirectory, offline.PathSeparator) - 1 // subtract 1 to avoid indenting root-level directories
|
indent := strings.Count(trimmedDirectory, backend.PathSeparator) - 1 // subtract 1 to avoid indenting root-level directories
|
||||||
|
|
||||||
for i, skipped := range skippedDirList[:currentDirIndex] { // checks each skipped directory to determine if it is a parent to the current directory
|
for i, skipped := range skippedDirList[:currentDirIndex] { // checks each skipped directory to determine if it is a parent to the current directory
|
||||||
if strings.HasPrefix(trimmedDirectory, dirList[i]+offline.PathSeparator) { // if the current directory is the child of this iteration's directory...
|
if strings.HasPrefix(trimmedDirectory, dirList[i]+backend.PathSeparator) { // if the current directory is the child of this iteration's directory...
|
||||||
if skipped { // ...and this iteration's directory was skipped...
|
if skipped { // ...and this iteration's directory was skipped...
|
||||||
subtractor++ // increment the subtractor to indicate that the visual indentation should be reduced
|
subtractor++ // increment the subtractor to indicate that the visual indentation should be reduced
|
||||||
} else {
|
} else {
|
||||||
@@ -72,7 +72,7 @@ func printFileEntry(entry string, lastSlash int, charCounter int, colorAlternato
|
|||||||
}
|
}
|
||||||
|
|
||||||
// print fileEntryName to screen
|
// print fileEntryName to screen
|
||||||
fmt.Printf("%s%s%s ", colorCode, fileEntryName, offline.AnsiReset)
|
fmt.Printf("%s%s%s ", colorCode, fileEntryName, backend.AnsiReset)
|
||||||
|
|
||||||
return charCounter, colorAlternator
|
return charCounter, colorAlternator
|
||||||
}
|
}
|
||||||
@@ -84,16 +84,16 @@ func EntryListGen() {
|
|||||||
var dirList []string
|
var dirList []string
|
||||||
|
|
||||||
// walk entry directory
|
// walk entry directory
|
||||||
_ = filepath.WalkDir(offline.EntryRoot,
|
_ = filepath.WalkDir(backend.EntryRoot,
|
||||||
func(fullPath string, entry fs.DirEntry, err error) error {
|
func(fullPath string, entry fs.DirEntry, err error) error {
|
||||||
|
|
||||||
// check for errors encountered while walking directory
|
// check for errors encountered while walking directory
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
fmt.Println(offline.AnsiError + "\nThe entry directory does not exist - run \"mutn init\" to create it" + offline.AnsiReset)
|
fmt.Println(backend.AnsiError + "\nThe entry directory does not exist - run \"mutn init\" to create it" + backend.AnsiReset)
|
||||||
} else {
|
} else {
|
||||||
// otherwise, print the source of the error
|
// otherwise, print the source of the error
|
||||||
fmt.Println(offline.AnsiError + "\nAn unexpected error occurred while generating the entry list: " + err.Error() + offline.AnsiReset)
|
fmt.Println(backend.AnsiError + "\nAn unexpected error occurred while generating the entry list: " + err.Error() + backend.AnsiReset)
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ func EntryListGen() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// print header bar w/total entry count
|
// print header bar w/total entry count
|
||||||
fmt.Print("\n"+ansiBlackOnWhite, len(fileList), " libmutton entries:"+offline.AnsiReset)
|
fmt.Print("\n"+ansiBlackOnWhite, len(fileList), " libmutton entries:"+backend.AnsiReset)
|
||||||
|
|
||||||
// dirList iteration
|
// dirList iteration
|
||||||
dirListLength := len(dirList) // save length for multiple references below
|
dirListLength := len(dirList) // save length for multiple references below
|
||||||
@@ -137,7 +137,7 @@ func EntryListGen() {
|
|||||||
|
|
||||||
// check if next directory is within the current one
|
// check if next directory is within the current one
|
||||||
if dirListLength > i+1 {
|
if dirListLength > i+1 {
|
||||||
if nextDir := dirList[i+1]; directory == nextDir[:strings.LastIndex(nextDir, offline.PathSeparator)] {
|
if nextDir := dirList[i+1]; directory == nextDir[:strings.LastIndex(nextDir, backend.PathSeparator)] {
|
||||||
containsSubdirectory = true
|
containsSubdirectory = true
|
||||||
} else {
|
} else {
|
||||||
containsSubdirectory = false
|
containsSubdirectory = false
|
||||||
@@ -151,7 +151,7 @@ func EntryListGen() {
|
|||||||
for _, file := range fileList {
|
for _, file := range fileList {
|
||||||
|
|
||||||
// print the current file if it belongs in the current directory - otherwise, break the loop and move on to the next directory
|
// print the current file if it belongs in the current directory - otherwise, break the loop and move on to the next directory
|
||||||
if lastSlash := strings.LastIndex(file, offline.PathSeparator) + 1; file[:lastSlash-1] == directory {
|
if lastSlash := strings.LastIndex(file, backend.PathSeparator) + 1; file[:lastSlash-1] == directory {
|
||||||
|
|
||||||
// print directory header if this is the first run of the loop
|
// print directory header if this is the first run of the loop
|
||||||
if !containsFiles {
|
if !containsFiles {
|
||||||
@@ -171,7 +171,7 @@ func EntryListGen() {
|
|||||||
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
|
||||||
printDirectoryHeader(vanityDirectory, indent)
|
printDirectoryHeader(vanityDirectory, indent)
|
||||||
fmt.Print(strings.Repeat(" ", indent*2) + ansiEmptyDirectoryWarning + "-empty directory-" + offline.AnsiReset)
|
fmt.Print(strings.Repeat(" ", indent*2) + ansiEmptyDirectoryWarning + "-empty directory-" + backend.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\".")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printDirectoryHeader(vanityDirectory string, indent int) {
|
func printDirectoryHeader(vanityDirectory string, indent int) {
|
||||||
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+offline.AnsiReset+"\n", vanityDirectory)
|
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+backend.AnsiReset+"\n", vanityDirectory)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printDirectoryHeader(vanityDirectory string, indent int) {
|
func printDirectoryHeader(vanityDirectory string, indent int) {
|
||||||
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+offline.AnsiReset+"\n", strings.ReplaceAll(vanityDirectory, offline.PathSeparator, "/"))
|
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+backend.AnsiReset+"\n", strings.ReplaceAll(vanityDirectory, backend.PathSeparator, "/"))
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -5,7 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
|
|
||||||
"github.com/charmbracelet/glamour"
|
"github.com/charmbracelet/glamour"
|
||||||
)
|
)
|
||||||
@@ -22,24 +22,24 @@ func EntryReader(decryptedEntry []string, hidePassword bool, sync 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 !hidePassword {
|
if !hidePassword {
|
||||||
fmt.Print(ansiDirectoryHeader + "Password:" + offline.AnsiReset + "\n" + ansiShownPassword + decryptedEntry[0] + offline.AnsiReset + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "Password:" + backend.AnsiReset + "\n" + ansiShownPassword + decryptedEntry[0] + backend.AnsiReset + "\n\n")
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(ansiDirectoryHeader + "Password:" + offline.AnsiReset + "\n" + ansiEmptyDirectoryWarning + "End command in \"show\" or \"-s\" to view" + offline.AnsiReset + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "Password:" + backend.AnsiReset + "\n" + ansiEmptyDirectoryWarning + "End command in \"show\" or \"-s\" to view" + backend.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:" + offline.AnsiReset + "\n" + decryptedEntry[1] + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "Username:" + backend.AnsiReset + "\n" + decryptedEntry[1] + "\n\n")
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
// if the third field (url) is not empty, print it
|
// if the third field (url) is not empty, print it
|
||||||
if decryptedEntry[2] != "" {
|
if decryptedEntry[2] != "" {
|
||||||
fmt.Print(ansiDirectoryHeader + "URL:" + offline.AnsiReset + "\n" + decryptedEntry[2] + "\n\n")
|
fmt.Print(ansiDirectoryHeader + "URL:" + backend.AnsiReset + "\n" + decryptedEntry[2] + "\n\n")
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
// print the notes header
|
// print the notes header
|
||||||
fmt.Println(ansiDirectoryHeader + "Notes:" + offline.AnsiReset)
|
fmt.Println(ansiDirectoryHeader + "Notes:" + backend.AnsiReset)
|
||||||
|
|
||||||
// combine remaining fields into a single string (for markdown rendering)
|
// combine remaining fields into a single string (for markdown rendering)
|
||||||
var markdownNotes []string
|
var markdownNotes []string
|
||||||
@@ -57,17 +57,17 @@ func EntryReader(decryptedEntry []string, hidePassword bool, sync bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sync && !offline.Windows {
|
if sync && !backend.Windows {
|
||||||
SshypSync() // TODO Remove after native sync is implemented
|
SshypSync() // TODO Remove after native sync is implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EntryReaderShortcut is a shortcut for EntryReader that decrypts a GPG-encrypted file and prints the contents
|
// EntryReaderDecrypt is a wrapper for EntryReader that first decrypts a GPG-encrypted file before sending it to EntryReader
|
||||||
func EntryReaderShortcut(targetLocation string, hidePassword bool, sync bool) {
|
func EntryReaderDecrypt(targetLocation string, hidePassword bool, sync bool) {
|
||||||
if isFile, _ := offline.TargetIsFile(targetLocation, true, 2); isFile {
|
if isFile, _ := backend.TargetIsFile(targetLocation, true, 2); isFile {
|
||||||
EntryReader(offline.DecryptGPG(targetLocation), hidePassword, sync)
|
EntryReader(backend.DecryptGPG(targetLocation), hidePassword, sync)
|
||||||
}
|
}
|
||||||
// do not exit, as this is the job of EntryReader
|
// do not exit, as this is the job of EntryReader
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -2,7 +2,7 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,20 +11,20 @@ func TempInitCli() {
|
|||||||
// gpgID
|
// gpgID
|
||||||
var gpgID string
|
var gpgID string
|
||||||
if inputBinary("Auto-generate GPG key?") {
|
if inputBinary("Auto-generate GPG key?") {
|
||||||
gpgID = offline.GpgKeyGen()
|
gpgID = backend.GpgKeyGen()
|
||||||
} else {
|
} else {
|
||||||
// select GPG key from menu
|
// select GPG key from menu
|
||||||
uidSlice := offline.GpgUIDListGen()
|
uidSlice := backend.GpgUIDListGen()
|
||||||
gpgIDInt := inputMenuGen("Select GPG key:", uidSlice)
|
gpgIDInt := inputMenuGen("Select GPG key:", uidSlice)
|
||||||
if gpgIDInt == 0 {
|
if gpgIDInt == 0 {
|
||||||
fmt.Println(offline.AnsiError + "No GPG keys found - please generate one" + offline.AnsiReset)
|
fmt.Println(backend.AnsiError + "No GPG keys found - please generate one" + backend.AnsiReset)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
gpgID = uidSlice[gpgIDInt-1]
|
gpgID = uidSlice[gpgIDInt-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// textEditor
|
// textEditor
|
||||||
textEditor := input("Text editor (leave blank for $EDITOR, falls back to \"" + offline.FallbackEditor + "\"):")
|
textEditor := input("Text editor (leave blank for $EDITOR, falls back to \"" + backend.FallbackEditor + "\"):")
|
||||||
|
|
||||||
offline.TempInit(map[string]string{"textEditor": textEditor, "gpgID": gpgID})
|
backend.TempInit(map[string]string{"textEditor": textEditor, "gpgID": gpgID})
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-30
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
)
|
)
|
||||||
|
|
||||||
// global constants used only in this file
|
// global constants used only in this file
|
||||||
@@ -14,13 +14,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func HelpMain() {
|
func HelpMain() {
|
||||||
fmt.Print(ansiBold + "\nMUTN | Copyright (c) 2024 Randall Winkhart\n" + offline.AnsiReset + `
|
fmt.Print(ansiBold + "\nMUTN | Copyright (c) 2024 Randall Winkhart\n" + backend.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:" + offline.AnsiReset + ` mutn [/<entry name> [argument] [option]] | [argument]
|
` + ansiBold + "Usage:" + backend.AnsiReset + ` mutn [/<entry name> [argument] [option]] | [argument]
|
||||||
|
|
||||||
` + ansiBold + "Arguments:" + offline.AnsiReset + `
|
` + ansiBold + "Arguments:" + backend.AnsiReset + `
|
||||||
help|-h Bring up this menu
|
help|-h Bring up this menu
|
||||||
version|-v Display version and license information
|
version|-v Display version and license information
|
||||||
init Set up MUTN (generates libmutton.ini)
|
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
|
shear Delete an existing entry
|
||||||
sync Manually sync the entry directory
|
sync Manually sync the entry directory
|
||||||
|
|
||||||
` + ansiBold + "Options:" + offline.AnsiReset + `
|
` + ansiBold + "Options:" + backend.AnsiReset + `
|
||||||
copy:
|
copy:
|
||||||
password|-pw|<blank> Copy the password of an entry to your clipboard
|
password|-pw|<blank> Copy the password of an entry to your clipboard
|
||||||
username|-u Copy the username of an entry to your clipboard
|
username|-u Copy the username of an entry to your clipboard
|
||||||
@@ -52,17 +52,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:" + offline.AnsiReset + ` You can quickly read an entry with "mutn /<entry name>"
|
` + ansiBold + "Tip 1:" + backend.AnsiReset + ` You can quickly read an entry with "mutn /<entry name>"
|
||||||
` + ansiBold + "Tip 2:" + offline.AnsiReset + ` Type "mutn" (no arguments/options) to view a list of saved entries
|
` + ansiBold + "Tip 2:" + backend.AnsiReset + ` Type "mutn" (no arguments/options) to view a list of saved entries
|
||||||
` + ansiBold + "Tip 3:" + offline.AnsiReset + ` Provide "add", "edit", "copy", or "gen" as the only argument to receive more specific help
|
` + ansiBold + "Tip 3:" + backend.AnsiReset + ` Provide "add", "edit", "copy", or "gen" as the only argument to receive more specific help
|
||||||
` + ansiBold + "Tip 4:" + offline.AnsiReset + " Using \"add\", \"edit\", or \"copy\" without specifying an option (field) will default to \"password\"\n\n")
|
` + ansiBold + "Tip 4:" + backend.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:" + offline.AnsiReset + ` mutn /<entry name> add <option>
|
fmt.Print(ansiBold + "\nUsage:" + backend.AnsiReset + ` mutn /<entry name> add <option>
|
||||||
|
|
||||||
` + ansiBold + "Options:" + offline.AnsiReset + `
|
` + ansiBold + "Options:" + backend.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 +71,9 @@ func HelpAdd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HelpEdit() {
|
func HelpEdit() {
|
||||||
fmt.Print(ansiBold + "\nUsage:" + offline.AnsiReset + ` mutn /<entry name> edit <option>
|
fmt.Print(ansiBold + "\nUsage:" + backend.AnsiReset + ` mutn /<entry name> edit <option>
|
||||||
|
|
||||||
` + ansiBold + "Options:" + offline.AnsiReset + `
|
` + ansiBold + "Options:" + backend.AnsiReset + `
|
||||||
edit:
|
edit:
|
||||||
password|-pw|<blank> Change the password of an entry
|
password|-pw|<blank> Change the password of an entry
|
||||||
username|-u Change the username of an entry
|
username|-u Change the username of an entry
|
||||||
@@ -84,9 +84,9 @@ func HelpEdit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HelpCopy() {
|
func HelpCopy() {
|
||||||
fmt.Print(ansiBold + "\nUsage:" + offline.AnsiReset + ` mutn /<entry name> copy <option>
|
fmt.Print(ansiBold + "\nUsage:" + backend.AnsiReset + ` mutn /<entry name> copy <option>
|
||||||
|
|
||||||
` + ansiBold + "Options:" + offline.AnsiReset + `
|
` + ansiBold + "Options:" + backend.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
|
||||||
@@ -97,18 +97,18 @@ func HelpCopy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HelpGen() {
|
func HelpGen() {
|
||||||
fmt.Print(ansiBold + "\nUsage:" + offline.AnsiReset + ` mutn /<entry name> gen [option]
|
fmt.Print(ansiBold + "\nUsage:" + backend.AnsiReset + ` mutn /<entry name> gen [option]
|
||||||
|
|
||||||
` + ansiBold + "Options:" + offline.AnsiReset + `
|
` + ansiBold + "Options:" + backend.AnsiReset + `
|
||||||
gen:
|
gen:
|
||||||
update|-u Generate a password for an existing entry
|
update|-u Generate a password for an existing entry
|
||||||
|
|
||||||
` + ansiBold + "Tip:" + offline.AnsiReset + " If no options are provided, a new password entry is generated\n\n")
|
` + ansiBold + "Tip:" + backend.AnsiReset + " If no options are provided, a new password entry is generated\n\n")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Version() {
|
func Version() {
|
||||||
fmt.Print(ansiBold + "\n MIT License" + offline.AnsiReset + `
|
fmt.Print(ansiBold + "\n MIT License" + backend.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
|
||||||
@@ -136,18 +136,18 @@ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|||||||
"\n\n---------------------------------------------------------" +
|
"\n\n---------------------------------------------------------" +
|
||||||
"\n\n MUTN is a simple, self-hosted,\n SSH-synchronized password manager based on libmutton\n\n" +
|
"\n\n MUTN is a simple, self-hosted,\n SSH-synchronized password manager based on libmutton\n\n" +
|
||||||
" .. ..\n" +
|
" .. ..\n" +
|
||||||
" /()\\''.''. " + ansiVersionMeat + "♥♥♥♥" + offline.AnsiReset + " .''.''/()\\ _)\n" +
|
" /()\\''.''. " + ansiVersionMeat + "♥♥♥♥" + backend.AnsiReset + " .''.''/()\\ _)\n" +
|
||||||
" _. : * " + ansiVersionMeat + "♥♥♥♥♥♥ ♥♥♥♥♥♥♥♥" + offline.AnsiReset + " * : <[◎]|_|=\n" +
|
" _. : * " + ansiVersionMeat + "♥♥♥♥♥♥ ♥♥♥♥♥♥♥♥" + backend.AnsiReset + " * : <[◎]|_|=\n" +
|
||||||
" }-}-*] `..'..' " + ansiVersionMeat + "♥♥♥♥♥♥♥♥♥♥♥♥♥" + offline.AnsiReset + " `..'..' |\n" +
|
" }-}-*] `..'..' " + ansiVersionMeat + "♥♥♥♥♥♥♥♥♥♥♥♥♥" + backend.AnsiReset + " `..'..' |\n" +
|
||||||
" ◎-◎ // \\\\ " + ansiVersionMeat + "♥♥♥♥♥♥♥♥♥" + offline.AnsiReset + " // \\\\ /|\\\n" +
|
" ◎-◎ // \\\\ " + ansiVersionMeat + "♥♥♥♥♥♥♥♥♥" + backend.AnsiReset + " // \\\\ /|\\\n" +
|
||||||
ansiVersionOutline + "<><><><><><><><><><><><><><>-<><><><><><><><><><><><><><>\n" +
|
ansiVersionOutline + "<><><><><><><><><><><><><><>-<><><><><><><><><><><><><><>\n" +
|
||||||
"\\" + ansiBlackOnWhite + " " + offline.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " " + backend.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " MUTN Version 0.0.1 " + offline.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " MUTN Version 0.0.1 " + backend.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " The Butchered Update " + offline.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " The Butchered Update " + backend.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " " + offline.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " " + backend.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " Copyright (c) 2024 Randall Winkhart " + offline.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " Copyright (c) 2024 Randall Winkhart " + backend.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"\\" + ansiBlackOnWhite + " " + offline.AnsiReset + ansiVersionOutline + "/\n" +
|
"\\" + ansiBlackOnWhite + " " + backend.AnsiReset + ansiVersionOutline + "/\n" +
|
||||||
"<><><><><><><><><><><><><><>-<><><><><><><><><><><><><><>\n" + offline.AnsiReset +
|
"<><><><><><><><><><><><><><>-<><><><><><><><><><><><><><>\n" + backend.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")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/rwinkhart/MUTN/src/offline"
|
"github.com/rwinkhart/MUTN/src/backend"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -71,16 +71,16 @@ func inputMenuGen(prompt string, options []string) int {
|
|||||||
return inputInt(prompt, len(options))
|
return inputInt(prompt, len(options))
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeEntryShortcut writes an entry to targetLocation and previews it (errors if no data is supplied)
|
// writeEntryCLI writes an entry to targetLocation and previews it (errors if no data is supplied)
|
||||||
func writeEntryShortcut(targetLocation string, unencryptedEntry []string, hidePassword bool) {
|
func writeEntryCLI(targetLocation string, unencryptedEntry []string, hidePassword bool) {
|
||||||
if offline.EntryIsNotEmpty(unencryptedEntry) {
|
if backend.EntryIsNotEmpty(unencryptedEntry) {
|
||||||
// write the entry to the target location
|
// write the entry to the target location
|
||||||
offline.WriteEntry(targetLocation, unencryptedEntry)
|
backend.WriteEntry(targetLocation, unencryptedEntry)
|
||||||
// preview the entry
|
// preview the entry
|
||||||
fmt.Println(ansiBold + "\nEntry Preview:" + offline.AnsiReset)
|
fmt.Println(ansiBold + "\nEntry Preview:" + backend.AnsiReset)
|
||||||
EntryReader(unencryptedEntry, hidePassword, true)
|
EntryReader(unencryptedEntry, hidePassword, true)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(offline.AnsiError + "No data supplied for entry" + offline.AnsiReset)
|
fmt.Println(backend.AnsiError + "No data supplied for entry" + backend.AnsiReset)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user