Skip to content

Commit 38cc33a

Browse files
committed
Move interactivity things to prompts package
1 parent 735888e commit 38cc33a

2 files changed

Lines changed: 239 additions & 231 deletions

File tree

cmd/root.go

Lines changed: 2 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,14 @@
33
package cmd
44

55
import (
6-
"errors"
76
"fmt"
8-
"io/ioutil"
97
stdlog "log"
108
"os"
11-
"regexp"
12-
"strconv"
13-
"strings"
149
"time"
15-
"unicode"
1610

17-
"github.com/AlecAivazis/survey/v2"
1811
"github.com/loophole/cli/config"
1912
"github.com/loophole/cli/internal/pkg/cache"
20-
"github.com/loophole/cli/internal/pkg/closehandler"
21-
"github.com/loophole/cli/internal/pkg/communication"
13+
"github.com/loophole/cli/internal/pkg/prompts"
2214
"github.com/mattn/go-colorable"
2315
"github.com/rs/zerolog"
2416
"github.com/rs/zerolog/log"
@@ -30,239 +22,18 @@ var signalChan chan os.Signal
3022

3123
var alreadyRunning bool
3224

33-
//Possible answers for prompts and error messages
34-
const (
35-
AnswerTunnelTypeHTTP string = "Expose an HTTP Port"
36-
AnswerTunnelTypePath string = "Expose a local path"
37-
AnswerTunnelTypeWebDAV string = "Expose a local path with WebDAV"
38-
AnswerYes string = "Yes"
39-
AnswerNo string = "No"
40-
PortRangeErrorMsg string = "port must be between 0-65535"
41-
PathValidityErrorMsg string = "enter an existing path without any quotation marks"
42-
)
43-
4425
var rootCmd = &cobra.Command{
4526
Use: "loophole",
4627
Short: "Loophole - End to end TLS encrypted TCP communication between you and your clients",
4728
Long: "Loophole - End to end TLS encrypted TCP communication between you and your clients",
4829
Run: func(cmd *cobra.Command, args []string) {
4930
if !alreadyRunning {
5031
alreadyRunning = true
51-
interactivePrompt()
32+
prompts.StartInteractivePrompt(httpCmd.Root(), signalChan)
5233
}
5334
},
5435
}
5536

56-
func getPortPrompt() []*survey.Question {
57-
return []*survey.Question{
58-
{
59-
Name: "port",
60-
Prompt: &survey.Input{Message: "Please enter the http port you want to expose: "},
61-
Validate: func(val interface{}) error {
62-
if port, ok := val.(string); !ok {
63-
return errors.New(PortRangeErrorMsg)
64-
} else { //else is necessary here to keep access to port
65-
n, err := strconv.Atoi(port)
66-
if err != nil {
67-
return errors.New(PortRangeErrorMsg)
68-
}
69-
if (n < 0) || (n > 65535) {
70-
return errors.New(PortRangeErrorMsg)
71-
}
72-
}
73-
74-
return nil
75-
},
76-
},
77-
}
78-
}
79-
80-
func getPathPrompt() []*survey.Question {
81-
return []*survey.Question{
82-
{
83-
Name: "path",
84-
Prompt: &survey.Input{Message: "Please enter the path you want to expose: "},
85-
Validate: func(val interface{}) error {
86-
if path, ok := val.(string); !ok {
87-
return errors.New(PathValidityErrorMsg)
88-
} else { //else is necessary here to keep access to path
89-
_, err := os.Stat(path)
90-
if err == nil {
91-
return nil
92-
}
93-
return errors.New(PathValidityErrorMsg)
94-
}
95-
},
96-
},
97-
}
98-
}
99-
100-
func getLastArgsPrompt(lastArgs string) *survey.Select {
101-
return &survey.Select{
102-
Message: fmt.Sprintf("Your last settings were: '%s', would you like to reuse them?", lastArgs),
103-
Options: []string{AnswerYes, AnswerNo},
104-
}
105-
}
106-
107-
func getInitialPrompt() *survey.Select {
108-
return &survey.Select{
109-
Message: "Welcome to loophole. What do you want to do?",
110-
Options: []string{AnswerTunnelTypeHTTP, AnswerTunnelTypePath, AnswerTunnelTypeWebDAV},
111-
}
112-
}
113-
114-
func askBasicAuth() string {
115-
res := ""
116-
prompt := &survey.Select{
117-
Message: "Do you want to secure your tunnel using a username and password?",
118-
Options: []string{AnswerNo, AnswerYes},
119-
}
120-
var usernamePrompt = []*survey.Question{
121-
{
122-
Name: "username",
123-
Prompt: &survey.Input{Message: "Please enter the username you want to use: "}, //not asking for a password since it's already implemented in virtual-serve
124-
},
125-
}
126-
err := survey.AskOne(prompt, &res)
127-
if err != nil {
128-
signalChan <- nil
129-
}
130-
if res == AnswerYes {
131-
err = survey.Ask(usernamePrompt, &res)
132-
if err != nil {
133-
os.Exit(0)
134-
return err.Error()
135-
}
136-
} else {
137-
return ""
138-
}
139-
return res
140-
}
141-
142-
func askHostname() string {
143-
res := ""
144-
prompt := &survey.Select{
145-
Message: "Do you want to use a custom hostname?",
146-
Options: []string{AnswerNo, AnswerYes},
147-
}
148-
var hostnamePrompt = []*survey.Question{
149-
{
150-
Name: "hostname",
151-
Prompt: &survey.Input{Message: "Please enter the hostname you want to use: "},
152-
Validate: func(val interface{}) error {
153-
var validChars = regexp.MustCompile(`^[a-z][a-z0-9]{0,30}$`).MatchString
154-
if hostname, ok := val.(string); !ok || len(hostname) > 31 || !validChars(hostname) || !unicode.IsLetter(rune(hostname[0])) {
155-
return errors.New("hostname must be up to 31 characters, may only contain lowercase letters and numbers and must start with a letter")
156-
}
157-
158-
return nil
159-
},
160-
},
161-
}
162-
err := survey.AskOne(prompt, &res)
163-
if err != nil {
164-
signalChan <- nil
165-
}
166-
if res == AnswerYes {
167-
err = survey.Ask(hostnamePrompt, &res)
168-
if err != nil {
169-
os.Exit(0)
170-
return err.Error()
171-
}
172-
} else {
173-
return ""
174-
}
175-
return res
176-
}
177-
178-
func interactivePrompt() {
179-
argPath := cache.GetLocalStorageFile("lastArgs", "logs")
180-
var lastArgs string = ""
181-
if _, err := os.Stat(argPath); err == nil {
182-
argBytes, err := ioutil.ReadFile(argPath)
183-
if err != nil {
184-
communication.Fatal("Error reading last used arguments:" + err.Error())
185-
}
186-
lastArgs = string(argBytes)
187-
}
188-
var lastArgsPrompt = getLastArgsPrompt(lastArgs)
189-
var initialPrompt = getInitialPrompt()
190-
var portPrompt = getPortPrompt()
191-
var pathPrompt = getPathPrompt()
192-
193-
var res string
194-
var exposePort int
195-
var exposePath string
196-
var arguments []string
197-
198-
cmd := httpCmd.Root() //TODO: find a better way to access rootCMD
199-
200-
if lastArgs != "" {
201-
err := survey.AskOne(lastArgsPrompt, &res)
202-
if err != nil {
203-
signalChan <- nil
204-
}
205-
if res == AnswerYes {
206-
cmd.SetArgs(strings.Split(lastArgs, " ")) //needs validation
207-
cmd.Execute()
208-
os.Exit(1)
209-
}
210-
}
211-
err := survey.AskOne(initialPrompt, &res)
212-
213-
if err != nil {
214-
signalChan <- nil
215-
}
216-
switch res {
217-
case AnswerTunnelTypeHTTP:
218-
err = survey.Ask(portPrompt, &exposePort)
219-
if err != nil {
220-
signalChan <- nil
221-
}
222-
arguments = []string{"http", strconv.Itoa(exposePort)}
223-
case AnswerTunnelTypePath:
224-
err = survey.Ask(pathPrompt, &exposePath)
225-
if err != nil {
226-
signalChan <- nil
227-
}
228-
arguments = []string{"path", exposePath}
229-
case AnswerTunnelTypeWebDAV:
230-
err = survey.Ask(pathPrompt, &exposePath)
231-
if err != nil {
232-
signalChan <- nil
233-
}
234-
arguments = []string{"webdav", exposePath}
235-
}
236-
237-
hostname := askHostname()
238-
if hostname != "" {
239-
arguments = append(arguments, "--hostname", hostname)
240-
}
241-
basicAuth := askBasicAuth()
242-
if basicAuth != "" {
243-
arguments = append(arguments, "--basic-auth-username", basicAuth)
244-
}
245-
cmd.SetArgs(arguments)
246-
247-
var argumentsWithQuotes []string
248-
//setting the path argument in code doesn't work when it contains quotation marks,
249-
//but they do need to be there when entered as a standalone command in a command line if the path contains spaces
250-
//so, we give a copy of the arguments to the closehandler, with the path in quotation marks, where necessary
251-
if strings.Contains(exposePath, " ") {
252-
for i := 0; i < len(arguments); i++ {
253-
if arguments[i] == exposePath {
254-
argumentsWithQuotes = append(argumentsWithQuotes, fmt.Sprintf("'%s'", exposePath))
255-
} else {
256-
argumentsWithQuotes = append(argumentsWithQuotes, arguments[i])
257-
}
258-
}
259-
closehandler.SaveArguments(argumentsWithQuotes)
260-
} else {
261-
closehandler.SaveArguments(arguments)
262-
}
263-
cmd.Execute()
264-
}
265-
26637
func init() {
26738
cobra.OnInitialize(initLogger)
26839

0 commit comments

Comments
 (0)