feat: new interface
This commit is contained in:
parent
c47c6719d2
commit
6c30928065
3 changed files with 141 additions and 350 deletions
|
@ -7,6 +7,10 @@ Which is based on [Keep A Changelog](http://keepachangelog.com/)
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- New interface
|
||||||
|
|
||||||
## v1.2.1 - 2019-10-19
|
## v1.2.1 - 2019-10-19
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
416
gpm/cli.go
416
gpm/cli.go
|
@ -1,332 +1,190 @@
|
||||||
// Copyright 2019 Adrien Waksberg
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package gpm
|
package gpm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/atotto/clipboard"
|
"log"
|
||||||
"github.com/olekukonko/tablewriter"
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
ui "github.com/gizak/termui/v3"
|
||||||
"io/ioutil"
|
"github.com/gizak/termui/v3/widgets"
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cli contain config and wallet to use
|
|
||||||
type Cli struct {
|
type Cli struct {
|
||||||
Config Config
|
Config Config
|
||||||
Wallet Wallet
|
Wallet Wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
// printEntries show entries with tables
|
func (c *Cli) ErrorBox(msg string) {
|
||||||
func (c *Cli) printEntries(entries []Entry) {
|
p := widgets.NewParagraph()
|
||||||
var otp string
|
p.Title = "Notification"
|
||||||
var tables map[string]*tablewriter.Table
|
p.SetRect(10, 0, 70, 5)
|
||||||
|
p.Text = fmt.Sprintf("[ERROR: %s](fg:red) ", msg)
|
||||||
|
|
||||||
tables = make(map[string]*tablewriter.Table)
|
ui.Render(p)
|
||||||
|
|
||||||
for i, entry := range entries {
|
|
||||||
if entry.OTP == "" {
|
|
||||||
otp = ""
|
|
||||||
} else {
|
|
||||||
otp = "X"
|
|
||||||
}
|
|
||||||
if _, present := tables[entry.Group]; present == false {
|
|
||||||
tables[entry.Group] = tablewriter.NewWriter(os.Stdout)
|
|
||||||
tables[entry.Group].SetHeader([]string{"", "Name", "URI", "User", "OTP", "Comment"})
|
|
||||||
tables[entry.Group].SetBorder(false)
|
|
||||||
tables[entry.Group].SetColumnColor(
|
|
||||||
tablewriter.Colors{tablewriter.Normal, tablewriter.FgYellowColor},
|
|
||||||
tablewriter.Colors{tablewriter.Normal, tablewriter.FgWhiteColor},
|
|
||||||
tablewriter.Colors{tablewriter.Normal, tablewriter.FgCyanColor},
|
|
||||||
tablewriter.Colors{tablewriter.Normal, tablewriter.FgGreenColor},
|
|
||||||
tablewriter.Colors{tablewriter.Normal, tablewriter.FgWhiteColor},
|
|
||||||
tablewriter.Colors{tablewriter.Normal, tablewriter.FgMagentaColor})
|
|
||||||
}
|
|
||||||
|
|
||||||
tables[entry.Group].Append([]string{strconv.Itoa(i), entry.Name, entry.URI, entry.User, otp, entry.Comment})
|
|
||||||
}
|
|
||||||
|
|
||||||
for group, table := range tables {
|
|
||||||
fmt.Printf("\n%s\n\n", group)
|
|
||||||
table.Render()
|
|
||||||
fmt.Println("")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// error print a message and exit)
|
func (c *Cli) InputBox(msg string, hidden bool) string {
|
||||||
func (c *Cli) error(msg string) {
|
var input, secret string
|
||||||
fmt.Printf("ERROR: %s\n", msg)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// input from the console
|
p := widgets.NewParagraph()
|
||||||
func (c *Cli) input(text string, defaultValue string, show bool) string {
|
p.SetRect(10, 10, 70, 5)
|
||||||
fmt.Print(text)
|
p.Text = fmt.Sprintf("%s ", msg)
|
||||||
|
|
||||||
if show == false {
|
ui.Render(p)
|
||||||
data, _ := terminal.ReadPassword(int(syscall.Stdin))
|
|
||||||
text := string(data)
|
|
||||||
fmt.Printf("\n")
|
|
||||||
|
|
||||||
if text == "" {
|
uiEvents := ui.PollEvents()
|
||||||
return defaultValue
|
for {
|
||||||
}
|
e := <-uiEvents
|
||||||
return text
|
switch e.ID {
|
||||||
}
|
case "q", "<C-c>":
|
||||||
|
return ""
|
||||||
input := bufio.NewScanner(os.Stdin)
|
case "<Backspace>":
|
||||||
input.Scan()
|
if len(input) >= 1 {
|
||||||
if input.Text() == "" {
|
input = input[:len(input)-1]
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return input.Text()
|
|
||||||
}
|
|
||||||
|
|
||||||
// selectEntry with a form
|
|
||||||
func (c *Cli) selectEntry() Entry {
|
|
||||||
var index int
|
|
||||||
|
|
||||||
entries := c.Wallet.SearchEntry(*PATTERN, *GROUP)
|
|
||||||
if len(entries) == 0 {
|
|
||||||
fmt.Println("no entry found")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.printEntries(entries)
|
|
||||||
if len(entries) == 1 {
|
|
||||||
return entries[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
c1 := make(chan int, 1)
|
|
||||||
go func(max int) {
|
|
||||||
for true {
|
|
||||||
index, err := strconv.Atoi(c.input("Select the entry: ", "", true))
|
|
||||||
if err == nil && index >= 0 && index+1 <= max {
|
|
||||||
c1 <- index
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
fmt.Println("your choice is not an integer or is out of range")
|
case "<Enter>":
|
||||||
|
return input
|
||||||
|
case "<Space>":
|
||||||
|
input = input + " "
|
||||||
|
default:
|
||||||
|
input = input + e.ID
|
||||||
}
|
}
|
||||||
}(len(entries))
|
|
||||||
|
|
||||||
select {
|
if hidden {
|
||||||
case res := <-c1:
|
secret = ""
|
||||||
index = res
|
for i := 1; i <= int(float64(len(input)) * 1.75); i++ {
|
||||||
case <-time.After(30 * time.Second):
|
secret = secret + "*"
|
||||||
os.Exit(1)
|
}
|
||||||
|
p.Text = fmt.Sprintf("%s %s", msg, secret)
|
||||||
|
} else {
|
||||||
|
p.Text = fmt.Sprintf("%s %s", msg, input)
|
||||||
|
}
|
||||||
|
ui.Render(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries[index]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadWallet get and unlock the wallet
|
func (c *Cli) EntryBox(entry Entry) {
|
||||||
func (c *Cli) loadWallet() {
|
p := widgets.NewParagraph()
|
||||||
|
p.Title = "Entry"
|
||||||
|
p.SetRect(25, 0, 80, 23)
|
||||||
|
p.Text = fmt.Sprintf("%s[Name:](fg:yellow) %s\n", p.Text, entry.Name)
|
||||||
|
p.Text = fmt.Sprintf("%s[URI:](fg:yellow) %s\n", p.Text, entry.URI)
|
||||||
|
p.Text = fmt.Sprintf("%s[User:](fg:yellow) %s\n", p.Text, entry.User)
|
||||||
|
if entry.OTP == "" {
|
||||||
|
p.Text = fmt.Sprintf("%s[OTP:](fg:yellow) [no](fg:red)\n", p.Text)
|
||||||
|
} else {
|
||||||
|
p.Text = fmt.Sprintf("%s[OTP:](fg:yellow) [yes](fg:green)\n", p.Text)
|
||||||
|
}
|
||||||
|
p.Text = fmt.Sprintf("%s[Comment:](fg:yellow) %v\n", p.Text, entry.Comment)
|
||||||
|
|
||||||
|
ui.Render(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cli) UnlockWallet(wallet string) error {
|
||||||
var walletName string
|
var walletName string
|
||||||
|
var err error
|
||||||
|
|
||||||
passphrase := c.input("Enter the passphrase to unlock the wallet: ", "", false)
|
ui.Clear()
|
||||||
|
if wallet == "" {
|
||||||
if *WALLET == "" {
|
|
||||||
walletName = c.Config.WalletDefault
|
walletName = c.Config.WalletDefault
|
||||||
} else {
|
} else {
|
||||||
walletName = *WALLET
|
walletName = wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Wallet = Wallet{
|
c.Wallet = Wallet{
|
||||||
Name: walletName,
|
Name: walletName,
|
||||||
Path: fmt.Sprintf("%s/%s.gpm", c.Config.WalletDir, walletName),
|
Path: fmt.Sprintf("%s/%s.gpm", c.Config.WalletDir, walletName),
|
||||||
Passphrase: passphrase,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.Wallet.Load()
|
for i := 0; i < 3; i++ {
|
||||||
if err != nil {
|
c.Wallet.Passphrase = c.InputBox("Enter the passphrase to unlock the wallet:\n", true)
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
|
err = c.Wallet.Load()
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c.ErrorBox(fmt.Sprintf("%s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// List the entry of a wallet
|
func (c *Cli) GroupsBox() string {
|
||||||
func (c *Cli) listEntry() {
|
return ""
|
||||||
c.loadWallet()
|
|
||||||
entries := c.Wallet.SearchEntry(*PATTERN, *GROUP)
|
|
||||||
if len(entries) == 0 {
|
|
||||||
fmt.Println("no entry found")
|
|
||||||
os.Exit(1)
|
|
||||||
} else {
|
|
||||||
c.printEntries(entries)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete an entry of a wallet
|
func (c *Cli) ListEntries() {
|
||||||
func (c *Cli) deleteEntry() {
|
var pattern, group string
|
||||||
var entry Entry
|
var entries []Entry
|
||||||
|
|
||||||
c.loadWallet()
|
refresh := true
|
||||||
entry = c.selectEntry()
|
index := -1
|
||||||
confirm := c.input("are you sure you want to remove this entry [y/N] ?", "N", true)
|
|
||||||
|
|
||||||
if confirm == "y" {
|
l := widgets.NewList()
|
||||||
err := c.Wallet.DeleteEntry(entry.ID)
|
l.Title = "Entries"
|
||||||
if err != nil {
|
l.TextStyle = ui.NewStyle(ui.ColorYellow)
|
||||||
c.error(fmt.Sprintf("%s", err))
|
l.SelectedRowStyle = ui.NewStyle(ui.ColorGreen, ui.ColorClear, ui.ModifierBold)
|
||||||
|
l.WrapText = false
|
||||||
|
l.SetRect(0, 0, 25, 23)
|
||||||
|
|
||||||
|
ui.Clear()
|
||||||
|
uiEvents := ui.PollEvents()
|
||||||
|
for {
|
||||||
|
if refresh {
|
||||||
|
refresh = false
|
||||||
|
index = -1
|
||||||
|
entries = c.Wallet.SearchEntry(pattern, group)
|
||||||
|
l.Rows = []string{}
|
||||||
|
for _, entry := range entries {
|
||||||
|
l.Rows = append(l.Rows, entry.Name)
|
||||||
|
}
|
||||||
|
ui.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.Wallet.Save()
|
if index >= 0 {
|
||||||
if err != nil {
|
c.EntryBox(entries[index])
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("the entry has been deleted")
|
ui.Render(l)
|
||||||
}
|
e := <-uiEvents
|
||||||
}
|
switch e.ID {
|
||||||
|
case "q", "<C-c>":
|
||||||
// Add a new entry in wallet
|
return
|
||||||
func (c *Cli) addEntry() {
|
case "<Enter>":
|
||||||
c.loadWallet()
|
index = l.SelectedRow
|
||||||
|
case "<Escape>":
|
||||||
entry := Entry{}
|
pattern = ""
|
||||||
entry.GenerateID()
|
refresh = true
|
||||||
entry.Name = c.input("Enter the name: ", "", true)
|
case "/":
|
||||||
entry.Group = c.input("Enter the group: ", "", true)
|
pattern = c.InputBox("Search", false)
|
||||||
entry.URI = c.input("Enter the URI: ", "", true)
|
refresh = true
|
||||||
entry.User = c.input("Enter the username: ", "", true)
|
case "g":
|
||||||
if *RANDOM {
|
group = c.GroupsBox()
|
||||||
entry.Password = RandomString(c.Config.PasswordLength,
|
refresh = true
|
||||||
c.Config.PasswordLetter, c.Config.PasswordDigit, c.Config.PasswordSpecial)
|
case "j", "<Down>":
|
||||||
} else {
|
if len(entries) > 0 {
|
||||||
entry.Password = c.input("Enter the new password: ", entry.Password, false)
|
l.ScrollDown()
|
||||||
}
|
}
|
||||||
entry.OTP = c.input("Enter the OTP key: ", "", false)
|
case "k", "<Up>":
|
||||||
entry.Comment = c.input("Enter a comment: ", "", true)
|
if len(entries) > 0 {
|
||||||
|
l.ScrollUp()
|
||||||
err := c.Wallet.AddEntry(entry)
|
|
||||||
if err != nil {
|
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.Wallet.Save()
|
|
||||||
if err != nil {
|
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("the entry has been added")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update an entry in wallet
|
|
||||||
func (c *Cli) updateEntry() {
|
|
||||||
c.loadWallet()
|
|
||||||
|
|
||||||
entry := c.selectEntry()
|
|
||||||
entry.Name = c.input("Enter the new name: ", entry.Name, true)
|
|
||||||
entry.Group = c.input("Enter the new group: ", entry.Group, true)
|
|
||||||
entry.URI = c.input("Enter the new URI: ", entry.URI, true)
|
|
||||||
entry.User = c.input("Enter the new username: ", entry.User, true)
|
|
||||||
if *RANDOM {
|
|
||||||
entry.Password = RandomString(c.Config.PasswordLength,
|
|
||||||
c.Config.PasswordLetter, c.Config.PasswordDigit, c.Config.PasswordSpecial)
|
|
||||||
} else {
|
|
||||||
entry.Password = c.input("Enter the new password: ", entry.Password, false)
|
|
||||||
}
|
|
||||||
entry.OTP = c.input("Enter the new OTP key: ", entry.OTP, false)
|
|
||||||
entry.Comment = c.input("Enter a new comment: ", entry.Comment, true)
|
|
||||||
|
|
||||||
err := c.Wallet.UpdateEntry(entry)
|
|
||||||
if err != nil {
|
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
}
|
|
||||||
c.Wallet.Save()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy login and password from an entry
|
|
||||||
func (c *Cli) copyEntry() {
|
|
||||||
c.loadWallet()
|
|
||||||
entry := c.selectEntry()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for true {
|
|
||||||
choice := c.input("select one action: ", "", true)
|
|
||||||
switch choice {
|
|
||||||
case "l":
|
|
||||||
clipboard.WriteAll(entry.User)
|
|
||||||
case "p":
|
|
||||||
clipboard.WriteAll(entry.Password)
|
|
||||||
case "o":
|
|
||||||
code, time, _ := entry.OTPCode()
|
|
||||||
fmt.Printf("this OTP code is available for %d seconds\n", time)
|
|
||||||
clipboard.WriteAll(code)
|
|
||||||
case "q":
|
|
||||||
clipboard.WriteAll("")
|
|
||||||
os.Exit(0)
|
|
||||||
default:
|
|
||||||
fmt.Println("l -> copy login")
|
|
||||||
fmt.Println("p -> copy password")
|
|
||||||
fmt.Println("o -> copy OTP code")
|
|
||||||
fmt.Println("q -> quit")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(90 * time.Second):
|
|
||||||
clipboard.WriteAll("")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import entries from json file
|
func Run() {
|
||||||
func (c *Cli) ImportWallet() {
|
var c Cli
|
||||||
c.loadWallet()
|
c.Config.Load("")
|
||||||
|
|
||||||
_, err := os.Stat(*IMPORT)
|
if err := ui.Init(); err != nil {
|
||||||
|
log.Fatalf("failed to initialize termui: %v", err)
|
||||||
|
}
|
||||||
|
defer ui.Close()
|
||||||
|
|
||||||
|
err := c.UnlockWallet("test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.error(fmt.Sprintf("%s", err))
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(*IMPORT)
|
c.ListEntries()
|
||||||
if err != nil {
|
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.Wallet.Import(data)
|
|
||||||
if err != nil {
|
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.Wallet.Save()
|
|
||||||
if err != nil {
|
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("the import was successful")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export a wallet in json format
|
|
||||||
func (c *Cli) ExportWallet() {
|
|
||||||
c.loadWallet()
|
|
||||||
|
|
||||||
data, err := c.Wallet.Export()
|
|
||||||
if err != nil {
|
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ioutil.WriteFile(*EXPORT, data, 0600)
|
|
||||||
if err != nil {
|
|
||||||
c.error(fmt.Sprintf("%s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("the export was successful")
|
|
||||||
}
|
}
|
||||||
|
|
71
gpm/main.go
71
gpm/main.go
|
@ -1,71 +0,0 @@
|
||||||
// Copyright 2019 Adrien Waksberg
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package gpm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Options
|
|
||||||
var (
|
|
||||||
ADD = flag.Bool("add", false, "add a new entry in the wallet")
|
|
||||||
UPDATE = flag.Bool("update", false, "update an entry")
|
|
||||||
DELETE = flag.Bool("delete", false, "delete an entry")
|
|
||||||
LIST = flag.Bool("list", false, "list the entries in a wallet")
|
|
||||||
LENGTH = flag.Int("length", 16, "specify the password length")
|
|
||||||
COPY = flag.Bool("copy", false, "enter an copy mode for an entry")
|
|
||||||
CONFIG = flag.String("config", "", "specify the config file")
|
|
||||||
GROUP = flag.String("group", "", "search the entries in this group ")
|
|
||||||
WALLET = flag.String("wallet", "", "specify the wallet")
|
|
||||||
PATTERN = flag.String("pattern", "", "search the entries with this pattern")
|
|
||||||
RANDOM = flag.Bool("random", false, "generate a random password for a new entry or an update")
|
|
||||||
PASSWD = flag.Bool("password", false, "generate and print a random password")
|
|
||||||
DIGIT = flag.Bool("digit", false, "use digit to generate a random password")
|
|
||||||
LETTER = flag.Bool("letter", false, "use letter to generate a random password")
|
|
||||||
SPECIAL = flag.Bool("special", false, "use special chars to generate a random password")
|
|
||||||
EXPORT = flag.String("export", "", "json file path to export a wallet")
|
|
||||||
IMPORT = flag.String("import", "", "json file path to import entries")
|
|
||||||
HELP = flag.Bool("help", false, "print this help message")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Run the cli interface
|
|
||||||
func Run() {
|
|
||||||
var cli Cli
|
|
||||||
cli.Config.Load(*CONFIG)
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
if *HELP {
|
|
||||||
flag.PrintDefaults()
|
|
||||||
os.Exit(1)
|
|
||||||
} else if *PASSWD {
|
|
||||||
fmt.Println(RandomString(*LENGTH, *LETTER, *DIGIT, *SPECIAL))
|
|
||||||
} else if *LIST {
|
|
||||||
cli.listEntry()
|
|
||||||
} else if *COPY {
|
|
||||||
cli.copyEntry()
|
|
||||||
} else if *ADD {
|
|
||||||
cli.addEntry()
|
|
||||||
} else if *UPDATE {
|
|
||||||
cli.updateEntry()
|
|
||||||
} else if *DELETE {
|
|
||||||
cli.deleteEntry()
|
|
||||||
} else if *IMPORT != "" {
|
|
||||||
cli.ImportWallet()
|
|
||||||
} else if *EXPORT != "" {
|
|
||||||
cli.ExportWallet()
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue