123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- package config
- import (
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "os/user"
- "path/filepath"
- "strconv"
- "github.com/asaskevich/govalidator"
- "github.com/claudiodangelis/qrcp/util"
- "github.com/manifoldco/promptui"
- )
- // Config of qrcp
- type Config struct {
- FQDN string `json:"fqdn"`
- Interface string `json:"interface"`
- Port int `json:"port"`
- KeepAlive bool `json:"keepAlive"`
- Path string `json:"path"`
- }
- func configFile() string {
- currentUser, err := user.Current()
- if err != nil {
- panic(err)
- }
- return filepath.Join(currentUser.HomeDir, ".qrcp.json")
- }
- type configOptions struct {
- interactive bool
- listAllInterfaces bool
- }
- func chooseInterface(opts configOptions) (string, error) {
- interfaces, err := util.Interfaces(opts.listAllInterfaces)
- if err != nil {
- return "", err
- }
- if len(interfaces) == 0 {
- return "", errors.New("no interfaces found")
- }
- if len(interfaces) == 1 && opts.interactive == false {
- for name := range interfaces {
- fmt.Printf("only one interface found: %s, using this one\n", name)
- return name, nil
- }
- }
- // Map for pretty printing
- m := make(map[string]string)
- items := []string{}
- for name, ip := range interfaces {
- label := fmt.Sprintf("%s (%s)", name, ip)
- m[label] = name
- items = append(items, label)
- }
- // Add the "any" interface
- anyIP := "0.0.0.0"
- anyName := "any"
- anyLabel := fmt.Sprintf("%s (%s)", anyName, anyIP)
- m[anyLabel] = anyName
- items = append(items, anyLabel)
- prompt := promptui.Select{
- Items: items,
- Label: "Choose interface",
- }
- _, result, err := prompt.Run()
- if err != nil {
- return "", err
- }
- return m[result], nil
- }
- // Load a new configuration
- func Load(opts configOptions) (Config, error) {
- var cfg Config
- // Read the configuration file, if it exists
- if file, err := ioutil.ReadFile(configFile()); err == nil {
- // Read the config
- if err := json.Unmarshal(file, &cfg); err != nil {
- return cfg, err
- }
- }
- // Prompt if needed
- if cfg.Interface == "" {
- iface, err := chooseInterface(opts)
- if err != nil {
- return cfg, err
- }
- cfg.Interface = iface
- // Write config
- if err := write(cfg); err != nil {
- return cfg, err
- }
- }
- return cfg, nil
- }
- // Wizard starts an interactive configuration managements
- func Wizard() error {
- var cfg Config
- if file, err := ioutil.ReadFile(configFile()); err == nil {
- // Read the config
- if err := json.Unmarshal(file, &cfg); err != nil {
- return err
- }
- }
- // Ask for interface
- opts := configOptions{
- interactive: true,
- }
- iface, err := chooseInterface(opts)
- if err != nil {
- return err
- }
- cfg.Interface = iface
- // Ask for fully qualified domain name
- validateFqdn := func(input string) error {
- if input != "" && govalidator.IsDNSName(input) == false {
- return errors.New("invalid domain")
- }
- return nil
- }
- promptFqdn := promptui.Prompt{
- Validate: validateFqdn,
- Label: "Choose fully-qualified domain name",
- Default: "",
- }
- if promptFqdnString, err := promptFqdn.Run(); err == nil {
- cfg.FQDN = promptFqdnString
- }
- // Ask for port
- validatePort := func(input string) error {
- _, err := strconv.ParseInt(input, 10, 16)
- if err != nil {
- return errors.New("Invalid number")
- }
- return nil
- }
- promptPort := promptui.Prompt{
- Validate: validatePort,
- Label: "Choose port, 0 means random port",
- Default: fmt.Sprintf("%d", cfg.Port),
- }
- if promptPortResultString, err := promptPort.Run(); err == nil {
- if port, err := strconv.ParseInt(promptPortResultString, 10, 16); err == nil {
- cfg.Port = int(port)
- }
- }
- // Ask for path
- promptPath := promptui.Prompt{
- Label: "Choose path, empty means random",
- Default: cfg.Path,
- }
- if promptPathResultString, err := promptPath.Run(); err == nil {
- if promptPathResultString != "" {
- cfg.Path = promptPathResultString
- }
- }
- // Ask for keep alive
- promptKeepAlive := promptui.Select{
- Items: []string{"No", "Yes"},
- Label: "Should the server keep alive after transferring?",
- }
- if _, promptKeepAliveResultString, err := promptKeepAlive.Run(); err == nil {
- if promptKeepAliveResultString == "Yes" {
- cfg.KeepAlive = true
- } else {
- cfg.KeepAlive = false
- }
- }
- // Write it down
- if err := write(cfg); err != nil {
- return err
- }
- b, err := json.MarshalIndent(cfg, "", " ")
- if err != nil {
- return err
- }
- fmt.Printf("Configuration updated:\n%s\n", string(b))
- return nil
- }
- // write the configuration file to disk
- func write(cfg Config) error {
- j, err := json.MarshalIndent(cfg, "", " ")
- if err != nil {
- return err
- }
- if err := ioutil.WriteFile(configFile(), j, 0644); err != nil {
- return err
- }
- return nil
- }
- // New returns a new configuration struct. It loads defaults, then overrides
- // values if any.
- func New(iface string, port int, path string, fqdn string, keepAlive bool, listAllInterfaces bool) (Config, error) {
- // Load saved file / defults
- cfg, err := Load(configOptions{listAllInterfaces: listAllInterfaces})
- if err != nil {
- return cfg, err
- }
- if iface != "" {
- cfg.Interface = iface
- }
- if fqdn != "" {
- if govalidator.IsDNSName(fqdn) == false {
- return cfg, errors.New("invalid value for fully-qualified domain name")
- }
- cfg.FQDN = fqdn
- }
- if port != 0 {
- if port > 65535 {
- return cfg, errors.New("invalid value for port")
- }
- cfg.Port = port
- }
- if keepAlive {
- cfg.KeepAlive = true
- }
- if path != "" {
- cfg.Path = path
- }
- return cfg, nil
- }
|