github.com/sqlitebrowser/dio@v0.0.0-20240125125356-b587368e5c6b/cmd/root.go (about) 1 package cmd 2 3 import ( 4 "crypto/tls" 5 "crypto/x509" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "log" 10 "os" 11 "path/filepath" 12 13 "github.com/mitchellh/go-homedir" 14 "github.com/spf13/cobra" 15 "github.com/spf13/viper" 16 "golang.org/x/text/message" 17 ) 18 19 const ( 20 DIO_VERSION = "0.3.1" 21 ) 22 23 var ( 24 certUser string 25 cfgFile, cloud string 26 fOut = io.Writer(os.Stdout) 27 numFormat *message.Printer 28 TLSConfig tls.Config 29 ) 30 31 // RootCmd represents the base command when called without any subcommands 32 var RootCmd = &cobra.Command{ 33 Use: "dio", 34 Short: "Command line interface to DBHub.io", 35 Long: `dio is a command line interface (CLI) for DBHub.io. 36 37 With dio you can send and receive database files to a DBHub.io cloud, 38 and manipulate its tags and branches.`, 39 SilenceErrors: true, 40 SilenceUsage: true, 41 } 42 43 // Execute adds all child commands to the root command & sets flags appropriately. 44 // This is called by main.main(). It only needs to happen once to the rootCmd. 45 func Execute() { 46 if err := RootCmd.Execute(); err != nil { 47 fmt.Println(err) 48 os.Exit(1) 49 } 50 } 51 52 func init() { 53 // Add support for pretty printing numbers 54 numFormat = message.NewPrinter(message.MatchLanguage("en")) 55 56 // When run from go test we skip this, as we generate a temporary config file in the test suite setup 57 if os.Getenv("IS_TESTING") == "yes" { 58 return 59 } 60 61 // Add the global environment variables 62 RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", 63 fmt.Sprintf("config file (default is %s)", filepath.Join("$HOME", ".dio", "config.toml"))) 64 RootCmd.PersistentFlags().StringVar(&cloud, "cloud", "https://db4s.dbhub.io", 65 "Address of the DBHub.io cloud") 66 67 // Read all of our configuration data now 68 if cfgFile != "" { 69 // Use config file from the flag 70 viper.SetConfigFile(cfgFile) 71 } else { 72 // Find home directory 73 home, err := homedir.Dir() 74 if err != nil { 75 fmt.Println(err) 76 os.Exit(1) 77 } 78 79 // Search for config in ".dio" subdirectory under the users home directory 80 p := filepath.Join(home, ".dio") 81 viper.AddConfigPath(p) 82 viper.SetConfigName("config") 83 cfgFile = filepath.Join(p, "config.toml") 84 } 85 86 // If a config file is found, read it in. 87 if err := viper.ReadInConfig(); err != nil { 88 // No configuration file was found, so generate a default one and let the user know they need to supply the 89 // missing info 90 errInner := generateConfig(cfgFile) 91 if errInner != nil { 92 log.Fatalln(errInner) 93 return 94 } 95 log.Fatalf("No usable configuration file was found, so a default one has been generated in: %s\n"+ 96 "Please update it with your name, and the path to your DBHub.io user certificate file.\n", cfgFile) 97 return 98 } 99 100 // Make sure the paths to our CA Chain and user certificate have been set 101 if found := viper.IsSet("certs.cachain"); found == false { 102 log.Fatal("Path to Certificate Authority chain file not set in the config file") 103 return 104 } 105 if found := viper.IsSet("certs.cert"); found == false { 106 log.Fatal("Path to user certificate file not set in the config file") 107 return 108 } 109 110 // If an alternative DBHub.io cloud address is set in the config file, use that 111 if found := viper.IsSet("general.cloud"); found == true { 112 // If the user provided an override on the command line, that will override this anyway 113 cloud = viper.GetString("general.cloud") 114 } 115 116 // Read our certificate info, if present 117 ourCAPool := x509.NewCertPool() 118 chainFile, err := ioutil.ReadFile(viper.GetString("certs.cachain")) 119 if err != nil { 120 log.Fatal(err) 121 } 122 ok := ourCAPool.AppendCertsFromPEM(chainFile) 123 if !ok { 124 log.Fatal("Error when loading certificate chain file") 125 } 126 127 // TODO: Check if the client certificate file is present 128 certFile := viper.GetString("certs.cert") 129 if _, err = os.Stat(certFile); err != nil { 130 log.Fatalf("Please download your client certificate from DBHub.io, then update the configuration "+ 131 "file '%s' with its path", cfgFile) 132 } 133 134 // Load a client certificate file 135 cert, err := tls.LoadX509KeyPair(certFile, certFile) 136 if err != nil { 137 log.Fatal(err) 138 } 139 140 // Load our self signed CA Cert chain, and set TLS1.2 as minimum 141 TLSConfig = tls.Config{ 142 Certificates: []tls.Certificate{cert}, 143 ClientCAs: ourCAPool, 144 InsecureSkipVerify: true, 145 MinVersion: tls.VersionTLS12, 146 PreferServerCipherSuites: true, 147 RootCAs: ourCAPool, 148 } 149 150 // Extract the username and email from the TLS certificate 151 var email string 152 certUser, email, _, err = getUserAndServer() 153 if err != nil { 154 log.Fatal(err) 155 } 156 viper.Set("user.email", email) 157 }