diff --git a/.gitignore b/.gitignore index e43b0f9..04d12c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,20 @@ .DS_Store + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Log files +*.log + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/PMPC-NetworkTester b/PMPC-NetworkTester new file mode 100755 index 0000000..40b1666 Binary files /dev/null and b/PMPC-NetworkTester differ diff --git a/go.mod b/go.mod index e1e8885..9ef8704 100644 --- a/go.mod +++ b/go.mod @@ -2,3 +2,4 @@ module github.com/PatchMyPCTeam/PMPC-NetworkTester go 1.19 +require github.com/gorilla/websocket v1.5.3 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..25a9fc4 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/gui.go b/gui.go new file mode 100644 index 0000000..26f968a --- /dev/null +++ b/gui.go @@ -0,0 +1,523 @@ +package main + +import ( + "encoding/json" + "fmt" + "html/template" + "log" + "net" + "net/http" + "sync" + "time" + + "github.com/gorilla/websocket" + "github.com/PatchMyPCTeam/PMPC-NetworkTester/packages/downloadFile" + "github.com/PatchMyPCTeam/PMPC-NetworkTester/packages/goCMTrace" +) + +type WebGUI struct { + clients map[*websocket.Conn]bool + clientsMux sync.RWMutex + upgrader websocket.Upgrader + results []connectionResult + resultsMux sync.RWMutex +} + +type ProgressUpdate struct { + Type string `json:"type"` + Current int `json:"current"` + Total int `json:"total"` + Status string `json:"status"` + Progress float64 `json:"progress"` +} + +type TestResult struct { + Type string `json:"type"` + Result connectionResult `json:"result"` +} + +func NewWebGUI() *WebGUI { + return &WebGUI{ + clients: make(map[*websocket.Conn]bool), + upgrader: websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true // Allow connections from any origin + }, + }, + results: make([]connectionResult, 0), + } +} + +func (w *WebGUI) broadcast(message interface{}) { + w.clientsMux.RLock() + defer w.clientsMux.RUnlock() + + data, err := json.Marshal(message) + if err != nil { + return + } + + for client := range w.clients { + err := client.WriteMessage(websocket.TextMessage, data) + if err != nil { + delete(w.clients, client) + client.Close() + } + } +} + +func (w *WebGUI) handleWebSocket(rw http.ResponseWriter, r *http.Request) { + conn, err := w.upgrader.Upgrade(rw, r, nil) + if err != nil { + log.Printf("WebSocket upgrade error: %v", err) + return + } + defer conn.Close() + + w.clientsMux.Lock() + w.clients[conn] = true + w.clientsMux.Unlock() + + // Send current results to new client + w.resultsMux.RLock() + for _, result := range w.results { + w.broadcast(TestResult{Type: "result", Result: result}) + } + w.resultsMux.RUnlock() + + // Keep connection alive + for { + _, _, err := conn.ReadMessage() + if err != nil { + w.clientsMux.Lock() + delete(w.clients, conn) + w.clientsMux.Unlock() + break + } + } +} + +func (w *WebGUI) addResult(result connectionResult) { + w.resultsMux.Lock() + w.results = append(w.results, result) + w.resultsMux.Unlock() + + w.broadcast(TestResult{Type: "result", Result: result}) +} + +func (w *WebGUI) updateProgress(current, total int, status string) { + var progress float64 + if total > 0 { + progress = float64(current) / float64(total) * 100 + } + + update := ProgressUpdate{ + Type: "progress", + Current: current, + Total: total, + Status: status, + Progress: progress, + } + + w.broadcast(update) +} + +func (w *WebGUI) connectionTestWeb(connection connectionInfo, wg *sync.WaitGroup) connectionResult { + defer wg.Done() + + // Duplicate the connectionTest logic to avoid WaitGroup conflicts + result := connectionResult{} + timeout := time.Second * 5 + conn, err := net.DialTimeout("tcp", net.JoinHostPort(connection.domainName, connection.port), timeout) + logObj := new(goCMTrace.LogEntry) + logObj.File = "PMPC-NetworkTester.log" + if err != nil { + result = connectionResult{ + product: connection.product, + domainName: connection.domainName, + port: connection.port, + reason: connection.reason, + result: "Failed", + err: err, + } + } + if conn != nil { + defer conn.Close() + result = connectionResult{ + product: connection.product, + domainName: connection.domainName, + port: connection.port, + reason: connection.reason, + result: "Success", + } + logMessage := "Successfully tested for Product: " + result.product + " for the reason: " + result.reason + " connected to: " + net.JoinHostPort(result.domainName, result.port) + logObj.Message = logMessage + logObj.State = 1 + goCMTrace.LogData(*logObj) + } + if result.result == "Failed" { + logMessage := "Failed test for Product: " + result.product + " to connect to: " + result.domainName + " " + result.port + " due to " + result.err.Error() + logObj.Message = logMessage + logObj.State = 3 + goCMTrace.LogData(*logObj) + } + + // Add result to GUI + w.addResult(result) + + return result +} + +func (w *WebGUI) runNetworkTests() { + var wg sync.WaitGroup + + w.updateProgress(0, 1, "Starting network tests...") + + // Test initial connection to Patch My PC + wg.Add(1) + connectionObj := connectionInfo{ + product: "Patch My PC Network Tester", + domainName: "patchmypc.com", + port: "443", + reason: "Base Functionality", + } + + state := w.connectionTestWeb(connectionObj, &wg) + wg.Wait() + + if state.result == "Success" { + w.updateProgress(1, 1, "Downloading domain list...") + + fileName, err := downloadFile.DownloadFile("https://patchmypc.com/scupcatalog/downloads/PatchMyPC-DomainList.csv") + if err != nil { + w.updateProgress(1, 1, fmt.Sprintf("Error downloading domain list: %v", err)) + return + } + + records, err := readData(fileName) + if err != nil { + w.updateProgress(1, 1, fmt.Sprintf("Error reading domain list: %v", err)) + return + } + + // Count valid connections + totalConnections := 0 + for _, record := range records { + if len(record) > 5 && shouldConnect(record[2]) { + totalConnections++ + } + } + + w.updateProgress(0, totalConnections, fmt.Sprintf("Testing %d connections...", totalConnections)) + + current := 0 + for _, record := range records { + if len(record) > 5 { + connectionObj := connectionInfo{ + product: record[1], + domainName: record[2], + port: record[4], + reason: record[5], + } + if shouldConnect(connectionObj.domainName) { + wg.Add(1) + current++ + w.updateProgress(current, totalConnections, fmt.Sprintf("Testing %s (%d/%d)", connectionObj.domainName, current, totalConnections)) + go w.connectionTestWeb(connectionObj, &wg) + } + } + } + + go func() { + wg.Wait() + w.updateProgress(totalConnections, totalConnections, "Network tests completed!") + }() + + } else { + w.updateProgress(1, 1, "Failed to connect to Patch My PC - Cannot progress further.") + } +} + +func (w *WebGUI) handleStart(rw http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + go w.runNetworkTests() + rw.WriteHeader(http.StatusOK) + rw.Write([]byte("Tests started")) + } +} + +const htmlTemplate = ` + + +
+