package main import "log" import "fmt" import "os" import "time" import "os/user" import "runtime" import "runtime/debug" // this is the king of dns libraries import "github.com/miekg/dns" import "git.wit.com/wit/gui" import pb "git.wit.com/wit/witProtobuf" import "git.wit.com/jcarr/dnssecsocket" import "github.com/gobuffalo/packr" import "github.com/davecgh/go-spew/spew" // will try to get this hosts FQDN import "github.com/Showmax/go-fqdn" var GITCOMMIT string // this is passed in as an ldflag var GOVERSION string // this is passed in as an ldflag var BUILDTIME string // this is passed in as an ldflag // reminder to use this for JSON // https://github.com/tidwall/gjson // value := gjson.Get(json, "name.last") // println(value.String()) // value := gjson.Get(json, friends.#[last=="Murphy"].first) // use mergo to merge structs // import "github.com/imdario/mergo" // mergo.Merge(&dest, src) // always sorted slice (new project) // https://github.com/yaa110/sslice // several smart slice functions (new project. April 2019) // https://github.com/elliotchance/pie func onExit(err error) { log.Println("Sleep for 1 second") time.Sleep(1 * 1000 * 1000 * 1000) // save the protobuf.Config as a JSON config file saveConfig() if (err != nil) { panic(err) } os.Exit(0) } var packrBox packr.Box func lookupAAAA(hostname string) string { // lookup the IP address from DNS dnsRR := dnssecsocket.Dnstrace(hostname, "AAAA") spew.Dump(dnsRR) if (dnsRR == nil) { return "BROKEN" } ipaddr := dns.Field(dnsRR, 1) log.Println("ipaddr", ipaddr) return ipaddr } func main() { // This puts all the files in that directory in the binary // This directory includes the default config file if there is not already one packrBox = packr.NewBox("./resources") // This will parse the command line and config file // This uses a protobuf definition and then Marshal's // and unmarshal's that into a JSON config file // that is human readable and editable. This is the easy way // to pass around a configuration structure throughout a // golang application IMHO parseConfig() for key, foo := range config.Accounts { log.Println("FOUND ACCOUNT = ", key, foo) } // pass a pointer to the config so the gui can access it gui.Data.Config = config initChannel() go processEvents() go gorillaDial("v000185.testing.com.customers.wprod.wit.com:9000") go watchGUI() user, err := user.Current() if err != nil { onExit(err) } gui.Data.Width = int(config.Width) gui.Data.Height = int(config.Height) // use this to discover what the OS thinks it's hostname is // seems to be cross platform (?) hostname := fqdn.Get() log.Println("fqdn.Get() = ", hostname) gui.Data.Hostname = hostname // this is a recursive dig for the AAAA record // TODO: check for dns hijacking ipAAAA := lookupAAAA(hostname) gui.Data.IPv6 = ipAAAA gui.Data.Version = "v0.7" gui.Data.GitCommit = GITCOMMIT gui.Data.GoVersion = GOVERSION gui.Data.Buildtime = BUILDTIME gui.Data.MouseClick = mainMouseClick gui.Data.HomeDir = user.HomeDir // Set output debugging level gui.Data.Debug = config.Debugging gui.Data.DebugTable = config.Debugtable log.Println("gui.Data.Debug = ", gui.Data.Debug) log.Println("gui.Data.DebugTable = ", gui.Data.DebugTable) // Current User log.Println("Hi " + user.Name + " (id: " + user.Uid + ")") log.Println("Username: " + user.Username) log.Println("Home Dir: " + user.HomeDir) // Get "Real" User under sudo. // More Info: https://stackoverflow.com/q/29733575/402585 log.Println("Real User: " + os.Getenv("SUDO_USER")) // make this the main loop in an attempt to figure out the crashes // do not change this until the GUI is stable gui.GoMainWindow() } // This is the handler for all mosue clicks (buttons, areas, etc)) func mainMouseClick(b *gui.ButtonMap) { log.Println("mainMouseClick() b =", b) if (b == nil) { log.Println("main() BACK IN CONTROL PANEL CODE (button is nil) WHY DID THIS HAPPEN?") log.Println("main() BACK IN CONTROL PANEL CODE (button is nil) WHY DID THIS HAPPEN?") log.Println("main() BACK IN CONTROL PANEL CODE (button is nil) WHY DID THIS HAPPEN?") } else { gui.Data.Current = b.Account log.Println("mainMouseClick() setting current account = ", b.Account) log.Println("main() BACK IN CONTROL PANEL CODE (button =", b, ")") if (b.Action == "BACK") { gui.Data.State = "splash" } else if (b.Action == "QUIT") { onExit(nil) } else if (b.Action == "CREATE") { log.Println("TRY TO ADD A NEW VIRTUAL MACHINE") log.Println("\tTRIGGER CREATE VM") gui.Data.State = "CREATE" } else if (b.Action == "CONFIG") { loadDefaultConfig() gui.Data.State = "done" } else if (b.Action == "DEBUG") { log.Println("debug.PrintStack() (SHOULD BE JUST THIS goroutine)") debug.PrintStack() } else if (b.Action == "DEBUG FULL") { log.Println("ATTEMPT FULL STACK DUMP") buf := make([]byte, 1<<16) runtime.Stack(buf, true) log.Printf("%s", buf) log.Println("FINISHED FULL STACK DUMP") } else if (b.Action == "ADD") { log.Println("\tSHOULD ADD ACCOUNT HERE") if (gui.Data.CurrentTab != nil) { if (gui.Data.CurrentTab.EntryNick != nil) { nick := gui.Data.CurrentTab.EntryNick.Text() username := gui.Data.CurrentTab.EntryUser.Text() password := gui.Data.CurrentTab.EntryPass.Text() log.Println("\tEntryNick =", nick) log.Println("\tEntryName =", username) log.Println("\tEntryPass =", password) acc := new(pb.Account) acc.Nick = nick acc.Username = username acc.Password = password config.Accounts = append(config.Accounts, acc) } } } else if (b.Action == "LOGIN") { log.Println("\tTRIGGER LOGIN ACCOUNT") gui.Data.State = "SEND LOGIN" count := 0 for { log.Println("Sleep() in buttonClick() gui.Data.State =", gui.Data.State) time.Sleep(200 * time.Millisecond) if (gui.Data.State == "NEW PROTOBUF") { if (currentMessage == nil) { gui.SocketError() gui.Data.State = "done" } else { log.Println("LOGIN currentMessage =", currentMessage) if (currentMessage.Type == pb.Event_OK) { log.Println("LOGIN WAS OK!") log.Println("LOGIN WAS OK!") log.Println("LOGIN WAS OK!") msg := "On account " + b.Account.Nick + "\n" gui.MessageWindow("Login OK", msg) } if (currentMessage.Type == pb.Event_FAIL) { log.Println("LOGIN FAILED") log.Println("LOGIN FAILED") log.Println("LOGIN FAILED") msg := "On account " + b.Account.Nick + "\n" msg += "pb.Comment = " + currentMessage.Comment + "\n" msg += "pb.Id = " + fmt.Sprintf("%d", currentMessage.Id) + "\n" msg += "pb.Email = " + currentMessage.Email + "\n" msg += "pb.Username = " + currentMessage.Username + "\n" gui.ErrorWindow("Login Failed", msg) } currentMessage = nil gui.Data.State = "done" } return } count += 1 if (count > 10) { log.Println("ERROR: waited too long for a resposne") currentMessage = nil gui.Data.State = "done" return } } } else if (b.Action == "SHOW") { log.Println("\tTRIGGER DISPLAY ACCOUNT") gui.Data.State = "SEND WEBSOCKET" count := 0 for { log.Println("Sleep() in buttonClick() gui.Data.State =", gui.Data.State) time.Sleep(200 * time.Millisecond) if (gui.Data.State == "NEW PROTOBUF") { if (currentMessage == nil) { gui.SocketError() gui.Data.State = "done" } else { count := countVMS(currentMessage) log.Println("SHOW VMS currentMessage =", currentMessage) log.Println("SHOW VMS count =", count) if (count != 0) { name := "Virtual Machines (" + b.Account.Nick + ")" mh := gui.AddVmsTab(name, count, b.Account) ReadReceivedData(currentMessage, mh) } currentMessage = nil gui.Data.State = "done" } return } count += 1 if (count > 10) { log.Println("ERROR: waited too long for a resposne") currentMessage = nil gui.Data.State = "done" return } } } } if (gui.Data.State == "splash") { gui.ShowAccountQuestionTab() gui.Data.State = "account1" return } else if (gui.Data.State == "account1") { gui.ShowAccountTab() gui.Data.State = "main" } else if (gui.Data.State == "main") { gui.ShowMainTab() gui.Data.State = "done" } else if (gui.Data.State == "QUIT") { onExit(nil) } } // this watches the GUI primarily to process protobuf's func watchGUI() { count := 0 for { if (count > 10) { log.Println("Sleep() in watchGUI() gui.Data.State =", gui.Data.State) count = 0 } count += 1 time.Sleep(200 * time.Millisecond) if (gui.Data.State == "SEND WEBSOCKET") { event := pb.MakeGetEvent() if (gui.Data.Current == nil) { event.Token = gui.Data.Current.Token event.Username = gui.Data.Current.Username } log.Println("\tTRIGGERING WEBSOCKET pb.MakeGetEvent() event.Token =", event.Token) gorillaSendProtobuf(event) gui.Data.State = "READ PROTOBUF" } if (gui.Data.State == "SEND LOGIN") { event := pb.MakeLoginEvent() if (gui.Data.Current == nil) { event.Token = gui.Data.Current.Token event.Username = gui.Data.Current.Username event.Password = gui.Data.Current.Password } log.Println("\tTRIGGERING LOGIN HERE pb.MakeLoginEvent() event.Token =", event.Token) gorillaSendProtobuf(event) gui.Data.State = "READ PROTOBUF" } if (gui.Data.State == "CREATE") { log.Println("\tTRIGGERING CREATE HERE") event := pb.MakeAddVmEvent() if (gui.Data.Current == nil) { event.Token = gui.Data.Current.Token event.Username = gui.Data.Current.Username } log.Println("\tTRIGGERING CREATE HERE with event.Token =", event.Token) spew.Dump(event) gorillaSendProtobuf(event) log.Println("\tEVENT SENT TO GORILLA WEBSOCKET") gui.Data.State = "READ PROTOBUF" } if (gui.Data.State == "kill") { log.Println("gui.State = kill") log.Println("gui.State = kill") log.Println("gui.State = kill") os.Exit(0) } if (gui.Data.State == "splash") { for key, _ := range config.Accounts { log.Println("gui.State = splash BUT THERE IS AN ACCOUNT Nick = ", config.Accounts[key].Nick) log.Println("gui.State = splash BUT THERE IS AN ACCOUNT Username = ", config.Accounts[key].Username) log.Println("SETTING gui.State = main") gui.Data.State = "main"; } } } }