diff --git a/.gitignore b/.gitignore index e43b0f9..fd371d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ .DS_Store +PMPC-NetworkTester +PMPC-NetworkTester.exe +*.log +*.csv diff --git a/go.mod b/go.mod index e1e8885..103f96e 100644 --- a/go.mod +++ b/go.mod @@ -2,3 +2,39 @@ module github.com/PatchMyPCTeam/PMPC-NetworkTester go 1.19 +require fyne.io/fyne/v2 v2.6.3 + +require ( + fyne.io/systray v1.11.0 // indirect + github.com/BurntSushi/toml v1.4.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fredbi/uri v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fyne-io/gl-js v0.2.0 // indirect + github.com/fyne-io/glfw-js v0.3.0 // indirect + github.com/fyne-io/image v0.1.1 // indirect + github.com/fyne-io/oksvg v0.1.0 // indirect + github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect + github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect + github.com/go-text/render v0.2.0 // indirect + github.com/go-text/typesetting v0.2.1 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/hack-pad/go-indexeddb v0.3.2 // indirect + github.com/hack-pad/safejs v0.1.0 // indirect + github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect + github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rymdport/portal v0.4.1 // indirect + github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect + github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect + github.com/stretchr/testify v1.10.0 // indirect + github.com/yuin/goldmark v1.7.8 // indirect + golang.org/x/image v0.24.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2a6450e --- /dev/null +++ b/go.sum @@ -0,0 +1,74 @@ +fyne.io/fyne/v2 v2.6.3 h1:cvtM2KHeRuH+WhtHiA63z5wJVBkQ9+Ay0UMl9PxFHyA= +fyne.io/fyne/v2 v2.6.3/go.mod h1:NGSurpRElVoI1G3h+ab2df3O5KLGh1CGbsMMcX0bPIs= +fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= +fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8= +github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs= +github.com/fyne-io/gl-js v0.2.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI= +github.com/fyne-io/glfw-js v0.3.0 h1:d8k2+Y7l+zy2pc7wlGRyPfTgZoqDf3AI4G+2zOWhWUk= +github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk= +github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA= +github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM= +github.com/fyne-io/oksvg v0.1.0 h1:7EUKk3HV3Y2E+qypp3nWqMXD7mum0hCw2KEGhI1fnBw= +github.com/fyne-io/oksvg v0.1.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI= +github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA= +github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc= +github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU= +github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8= +github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M= +github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A= +github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0= +github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8= +github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio= +github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE= +github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= +github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M= +github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk= +github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA= +github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= +github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE= +github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= +github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= +github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= +github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= +golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index e36df23..01a838f 100644 --- a/main.go +++ b/main.go @@ -6,14 +6,20 @@ import ( "log" "net" "os" + "strings" "sync" "time" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" "github.com/PatchMyPCTeam/PMPC-NetworkTester/packages/downloadFile" "github.com/PatchMyPCTeam/PMPC-NetworkTester/packages/goCMTrace" ) -func connectionTest(connection connectionInfo, wg *sync.WaitGroup) connectionResult { +func connectionTest(connection connectionInfo, wg *sync.WaitGroup, gui *guiState) connectionResult { result := connectionResult{} timeout := time.Second * 5 conn, err := net.DialTimeout("tcp", net.JoinHostPort(connection.domainName, connection.port), timeout) @@ -49,6 +55,12 @@ func connectionTest(connection connectionInfo, wg *sync.WaitGroup) connectionRes logObj.State = 3 goCMTrace.LogData(*logObj) } + + // Add result to GUI if available + if gui != nil { + gui.addResult(result) + } + wg.Done() return result } @@ -94,7 +106,216 @@ type connectionResult struct { err error } +// GUI state management +type guiState struct { + results *widget.List + statusText *widget.Label + progress *widget.ProgressBar + darkMode bool + resultData []connectionResult + mutex sync.Mutex +} + +func (g *guiState) addResult(result connectionResult) { + g.mutex.Lock() + defer g.mutex.Unlock() + g.resultData = append(g.resultData, result) + g.results.Refresh() +} + +func (g *guiState) updateStatus(status string) { + g.statusText.SetText(status) +} + +func (g *guiState) setProgress(value float64) { + g.progress.SetValue(value) +} + +func createGUI() (*guiState, func()) { + myApp := app.New() + myApp.SetIcon(nil) // Use default icon + myWindow := myApp.NewWindow("PMPC Network Tester") + myWindow.Resize(fyne.NewSize(800, 600)) + + gui := &guiState{ + darkMode: false, + resultData: make([]connectionResult, 0), + } + + // Create widgets + gui.statusText = widget.NewLabel("Ready to test network connections...") + gui.progress = widget.NewProgressBar() + + // Dark mode toggle + darkModeCheck := widget.NewCheck("Dark Mode", func(checked bool) { + gui.darkMode = checked + if checked { + myApp.Settings().SetTheme(theme.DarkTheme()) + } else { + myApp.Settings().SetTheme(theme.LightTheme()) + } + }) + + // Start test button + var startButton *widget.Button + startButton = widget.NewButton("Start Network Test", func() { + startButton.Disable() + go func() { + defer startButton.Enable() + runNetworkTests(gui) + }() + }) + + // Results list + gui.results = widget.NewList( + func() int { + gui.mutex.Lock() + defer gui.mutex.Unlock() + return len(gui.resultData) + }, + func() fyne.CanvasObject { + return widget.NewLabel("Template item") + }, + func(id widget.ListItemID, item fyne.CanvasObject) { + gui.mutex.Lock() + defer gui.mutex.Unlock() + if id < len(gui.resultData) { + result := gui.resultData[id] + label := item.(*widget.Label) + status := result.result + if result.result == "Failed" && result.err != nil { + status = fmt.Sprintf("Failed: %s", result.err.Error()) + } + label.SetText(fmt.Sprintf("%s | %s:%s | %s | %s", + result.product, result.domainName, result.port, status, result.reason)) + + // Color code the status + if result.result == "Success" { + label.Importance = widget.SuccessImportance + } else { + label.Importance = widget.WarningImportance + } + } + }, + ) + + // Layout + content := container.NewBorder( + container.NewVBox( + container.NewHBox(darkModeCheck, startButton), + gui.statusText, + gui.progress, + ), + nil, + nil, + nil, + gui.results, + ) + + myWindow.SetContent(content) + + return gui, func() { + myWindow.ShowAndRun() + } +} + +func runNetworkTests(gui *guiState) { + var wg sync.WaitGroup + wg.Add(1) + + gui.updateStatus("Testing connection to Patch My PC...") + gui.setProgress(0.1) + + connectionObj := connectionInfo{ + product: "Patch My PC Network Tester", + domainName: "patchmypc.com", + port: "443", + reason: "Base Functionality", + } + state := connectionTest(connectionObj, &wg, gui) + + if state.result == "Success" { + gui.updateStatus("Downloading domain list...") + gui.setProgress(0.3) + + fileName, err := downloadFile.DownloadFile("https://patchmypc.com/scupcatalog/downloads/PatchMyPC-DomainList.csv") + if err != nil { + gui.updateStatus(fmt.Sprintf("Failed to download domain list: %v", err)) + return + } + + gui.updateStatus("Reading domain list...") + gui.setProgress(0.4) + + records, err := readData(fileName) + if err != nil { + gui.updateStatus(fmt.Sprintf("Failed to read domain list: %v", err)) + return + } + + gui.updateStatus("Testing connections...") + totalTests := 0 + for _, record := range records { + connectionObj := connectionInfo{ + product: record[1], + domainName: record[2], + port: record[4], + reason: record[5], + } + if shouldConnect(connectionObj.domainName) { + totalTests++ + } + } + + currentTest := 0 + for _, record := range records { + connectionObj := connectionInfo{ + product: record[1], + domainName: record[2], + port: record[4], + reason: record[5], + } + if shouldConnect(connectionObj.domainName) { + wg.Add(1) + go connectionTest(connectionObj, &wg, gui) + currentTest++ + progress := 0.4 + (0.5 * float64(currentTest) / float64(totalTests)) + gui.setProgress(progress) + } + } + } else { + gui.updateStatus("Failed to connect to Patch My PC - Cannot progress farther.") + gui.setProgress(1.0) + return + } + + wg.Wait() + gui.updateStatus("Network testing completed!") + gui.setProgress(1.0) +} + func main() { + // Check command line arguments for /nogui flag + useGUI := true + for _, arg := range os.Args[1:] { + if strings.ToLower(arg) == "/nogui" { + useGUI = false + break + } + } + + if useGUI { + // Run GUI mode + gui, showFunc := createGUI() + _ = gui // Keep reference to prevent GC + showFunc() + } else { + // Run CLI mode (original behavior) + runCLIMode() + } +} + +func runCLIMode() { var wg sync.WaitGroup wg.Add(1) connectionObj := connectionInfo{ @@ -103,7 +324,7 @@ func main() { port: "443", reason: "Base Functionality", } - state := connectionTest(connectionObj, &wg) + state := connectionTest(connectionObj, &wg, nil) if state.result == "Success" { fileName, err := downloadFile.DownloadFile("https://patchmypc.com/scupcatalog/downloads/PatchMyPC-DomainList.csv") if err != nil { @@ -123,7 +344,7 @@ func main() { } if shouldConnect(connectionObj.domainName) { wg.Add(1) - go connectionTest(connectionObj, &wg) + go connectionTest(connectionObj, &wg, nil) } } } else { diff --git a/readme.md b/readme.md index 7cffd1e..5e297fc 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,36 @@ # Patch My PC Network Tester +A network connectivity testing tool for Patch My PC services with both GUI and CLI interfaces. + +## Features + +- **Graphical User Interface (GUI)**: Default mode with an intuitive interface +- **Dark Mode Support**: Toggle between light and dark themes +- **Command Line Interface (CLI)**: Available via `/nogui` flag +- **Real-time Testing**: Live progress updates and results display +- **Network Connectivity Testing**: Tests connections to Patch My PC services and domains + +## Usage + +### GUI Mode (Default) +Run the application without any arguments to launch the graphical interface: +``` +PMPC-NetworkTester +``` + +The GUI includes: +- Dark/Light mode toggle +- Start Network Test button +- Progress bar with status updates +- Real-time results list +- All testing runs within the application window + +### CLI Mode +Run with the `/nogui` flag for command-line operation: +``` +PMPC-NetworkTester /nogui +``` + ## DISCLAIMER The information contained in this online site is presented for general educational and information purposes only. The information contained in this site should not be considered exhaustive and the user should seek the advice of appropriate professionals. @@ -10,6 +41,18 @@ Patch My PC provides scripts, macro, and other code examples for illustration on ## Compile Instructions Windows +For GUI version: +```go +go build -o PMPC-NetworkTester.exe +``` + +For Windows GUI without console window: ```go go build -o PMPC-NetworkTester.exe -ldflags -H=windowsgui ``` + +## Dependencies + +- Go 1.19+ +- Fyne v2 (for GUI functionality) +- Platform-specific GUI libraries (automatically handled by Fyne)