Conversation
| this.but_export = new MissionPlanner.Controls.MyButton(); | ||
| this.but_import = new MissionPlanner.Controls.MyButton(); |
There was a problem hiding this comment.
The button naming is inconsistent with the existing convention in this file. Existing buttons use uppercase prefix "BUT_" (BUT_enable, BUT_save), but the new buttons use lowercase "but_" (but_export, but_import). For consistency, these should be renamed to BUT_export and BUT_import to match the established naming pattern.
Joystick/JoystickSetup.cs Outdated
| MainV2.joystick.saveconfig(); | ||
| MainV2.joystick.ExportConfig(sfd.FileName); | ||
| } |
There was a problem hiding this comment.
The but_export_Click method does not check if MainV2.joystick is null before calling saveconfig() and ExportConfig(). If the joystick is not initialized, this will result in a NullReferenceException. Add a null check similar to the one in BUT_save_Click to ensure a joystick is configured before attempting to export.
| // compress all joystick config files from user data directory | ||
| string userDataDir = Settings.GetUserDataDirectory(); | ||
| | ||
| // find all joystick config files (buttons and axis) | ||
| var buttonFiles = Directory.GetFiles(userDataDir, "joystickbutton*.xml", SearchOption.TopDirectoryOnly); | ||
| var axisFiles = Directory.GetFiles(userDataDir, "joystickaxis*.xml", SearchOption.TopDirectoryOnly); | ||
| | ||
| var allFiles = buttonFiles.Concat(axisFiles).ToList(); | ||
| | ||
| if (allFiles.Count == 0) | ||
| { | ||
| throw new FileNotFoundException("No joystick configuration files found in " + userDataDir); | ||
| } | ||
| | ||
| if (File.Exists(fileName)) | ||
| File.Delete(fileName); | ||
| | ||
| // create archive with all config files | ||
| using (var archive = ZipFile.Open(fileName, ZipArchiveMode.Create)) | ||
| { | ||
| foreach (var file in allFiles) | ||
| { | ||
| archive.CreateEntryFromFile(file, Path.GetFileName(file)); | ||
| } | ||
| } | ||
| } | ||
| | ||
| public void ImportConfig(string fileName) | ||
| { | ||
| // decompress all joystick config files to user data directory | ||
| string userDataDir = Settings.GetUserDataDirectory(); | ||
| | ||
| if (!File.Exists(fileName)) | ||
| { | ||
| throw new FileNotFoundException("Archive file not found: " + fileName); | ||
| } | ||
| | ||
| // extract all files from archive to user data directory | ||
| using (var archive = ZipFile.OpenRead(fileName)) | ||
| { | ||
| foreach (var entry in archive.Entries) | ||
| { | ||
| // only extract joystick config files | ||
| if (entry.Name.StartsWith("joystickbutton") || entry.Name.StartsWith("joystickaxis")) | ||
| { | ||
| string outputPath = Path.Combine(userDataDir, entry.Name); | ||
| entry.ExtractToFile(outputPath, true); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
The ExportConfig and ImportConfig methods lack error handling for file I/O operations. If operations like File.Delete, ZipFile.Open, or ZipFile.OpenRead fail, the exceptions will propagate unhandled to the UI layer. Consider wrapping these operations in try-catch blocks and providing user-friendly error messages, or at minimum document that calling code should handle these exceptions.
| // compress all joystick config files from user data directory | |
| string userDataDir = Settings.GetUserDataDirectory(); | |
| // find all joystick config files (buttons and axis) | |
| var buttonFiles = Directory.GetFiles(userDataDir, "joystickbutton*.xml", SearchOption.TopDirectoryOnly); | |
| var axisFiles = Directory.GetFiles(userDataDir, "joystickaxis*.xml", SearchOption.TopDirectoryOnly); | |
| var allFiles = buttonFiles.Concat(axisFiles).ToList(); | |
| if (allFiles.Count == 0) | |
| { | |
| throw new FileNotFoundException("No joystick configuration files found in " + userDataDir); | |
| } | |
| if (File.Exists(fileName)) | |
| File.Delete(fileName); | |
| // create archive with all config files | |
| using (var archive = ZipFile.Open(fileName, ZipArchiveMode.Create)) | |
| { | |
| foreach (var file in allFiles) | |
| { | |
| archive.CreateEntryFromFile(file, Path.GetFileName(file)); | |
| } | |
| } | |
| } | |
| public void ImportConfig(string fileName) | |
| { | |
| // decompress all joystick config files to user data directory | |
| string userDataDir = Settings.GetUserDataDirectory(); | |
| if (!File.Exists(fileName)) | |
| { | |
| throw new FileNotFoundException("Archive file not found: " + fileName); | |
| } | |
| // extract all files from archive to user data directory | |
| using (var archive = ZipFile.OpenRead(fileName)) | |
| { | |
| foreach (var entry in archive.Entries) | |
| { | |
| // only extract joystick config files | |
| if (entry.Name.StartsWith("joystickbutton") || entry.Name.StartsWith("joystickaxis")) | |
| { | |
| string outputPath = Path.Combine(userDataDir, entry.Name); | |
| entry.ExtractToFile(outputPath, true); | |
| } | |
| } | |
| } | |
| try | |
| { | |
| // compress all joystick config files from user data directory | |
| string userDataDir = Settings.GetUserDataDirectory(); | |
| // find all joystick config files (buttons and axis) | |
| var buttonFiles = Directory.GetFiles(userDataDir, "joystickbutton*.xml", SearchOption.TopDirectoryOnly); | |
| var axisFiles = Directory.GetFiles(userDataDir, "joystickaxis*.xml", SearchOption.TopDirectoryOnly); | |
| var allFiles = buttonFiles.Concat(axisFiles).ToList(); | |
| if (allFiles.Count == 0) | |
| { | |
| throw new FileNotFoundException("No joystick configuration files found in " + userDataDir); | |
| } | |
| if (File.Exists(fileName)) | |
| File.Delete(fileName); | |
| // create archive with all config files | |
| using (var archive = ZipFile.Open(fileName, ZipArchiveMode.Create)) | |
| { | |
| foreach (var file in allFiles) | |
| { | |
| archive.CreateEntryFromFile(file, Path.GetFileName(file)); | |
| } | |
| } | |
| } | |
| catch (FileNotFoundException) | |
| { | |
| // Preserve explicit FileNotFoundException messages for callers | |
| throw; | |
| } | |
| catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is InvalidDataException) | |
| { | |
| throw new IOException($"Failed to export joystick configuration to '{fileName}': {ex.Message}", ex); | |
| } | |
| } | |
| public void ImportConfig(string fileName) | |
| { | |
| try | |
| { | |
| // decompress all joystick config files to user data directory | |
| string userDataDir = Settings.GetUserDataDirectory(); | |
| if (!File.Exists(fileName)) | |
| { | |
| throw new FileNotFoundException("Archive file not found: " + fileName); | |
| } | |
| // extract all files from archive to user data directory | |
| using (var archive = ZipFile.OpenRead(fileName)) | |
| { | |
| foreach (var entry in archive.Entries) | |
| { | |
| // only extract joystick config files | |
| if (entry.Name.StartsWith("joystickbutton") || entry.Name.StartsWith("joystickaxis")) | |
| { | |
| string outputPath = Path.Combine(userDataDir, entry.Name); | |
| entry.ExtractToFile(outputPath, true); | |
| } | |
| } | |
| } | |
| } | |
| catch (FileNotFoundException) | |
| { | |
| // Preserve explicit FileNotFoundException messages for callers | |
| throw; | |
| } | |
| catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is InvalidDataException) | |
| { | |
| throw new IOException($"Failed to import joystick configuration from '{fileName}': {ex.Message}", ex); | |
| } |
| MainV2.joystick.ImportConfig(ofd.FileName); | ||
| MainV2.joystick.loadconfig(); | ||
| CustomMessageBox.Show("Please reopen joystick for changes to take effect"); | ||
| this.BeginInvoke((Action)delegate () | ||
| { | ||
| this.Close(); | ||
| ((Form)this.Parent).Close(); | ||
| }); |
There was a problem hiding this comment.
The but_import_Click method lacks error handling for exceptions that might be thrown by ImportConfig() or loadconfig(). If these operations fail (e.g., corrupted archive, invalid file format, file access denied), the user will see an unhandled exception. Wrap the operations in a try-catch block and display user-friendly error messages using CustomMessageBox.Show.
| MainV2.joystick.ImportConfig(ofd.FileName); | |
| MainV2.joystick.loadconfig(); | |
| CustomMessageBox.Show("Please reopen joystick for changes to take effect"); | |
| this.BeginInvoke((Action)delegate () | |
| { | |
| this.Close(); | |
| ((Form)this.Parent).Close(); | |
| }); | |
| try | |
| { | |
| MainV2.joystick.ImportConfig(ofd.FileName); | |
| MainV2.joystick.loadconfig(); | |
| CustomMessageBox.Show("Please reopen joystick for changes to take effect"); | |
| this.BeginInvoke((Action)delegate () | |
| { | |
| this.Close(); | |
| ((Form)this.Parent).Close(); | |
| }); | |
| } | |
| catch (Exception ex) | |
| { | |
| CustomMessageBox.Show("Failed to import joystick configuration.\n\nError: " + ex.Message, "Import Error"); | |
| } |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 5 comments.
Files not reviewed (1)
- Joystick/JoystickSetup.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private void but_export_Click(object sender, EventArgs e) | ||
| { | ||
| SaveFileDialog sfd = new SaveFileDialog(); | ||
| sfd.Filter = "Joystick config files (*.joycfg)|*.joycfg|All files (*.*)|*.*"; | ||
| if (sfd.ShowDialog() == DialogResult.OK) | ||
| { | ||
| if (MainV2.joystick == null) | ||
| { | ||
| CustomMessageBox.Show("No joystick configuration is available to export.", "Export Joystick Config", MessageBoxButtons.OK); | ||
| return; | ||
| } | ||
| | ||
| try | ||
| { | ||
| MainV2.joystick.saveconfig(); | ||
| MainV2.joystick.ExportConfig(sfd.FileName); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| CustomMessageBox.Show("Failed to export joystick configuration:\n" + ex.Message, "Export Joystick Config", MessageBoxButtons.OK); | ||
| } | ||
| } |
There was a problem hiding this comment.
Consider providing a more informative success message to the user after a successful export, including the file path where the configuration was saved.
| | ||
| | ||
| namespace MissionPlanner.Joystick | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; |
There was a problem hiding this comment.
This import statement appears to be unused. The code doesn't use any types from Microsoft.CodeAnalysis.CSharp.Syntax namespace. Consider removing it to avoid unnecessary dependencies.
| using Microsoft.CodeAnalysis.CSharp.Syntax; |
| MainV2.joystick.saveconfig(); | ||
| MainV2.joystick.ExportConfig(sfd.FileName); |
There was a problem hiding this comment.
The MainV2.joystick object should be checked for null before calling saveconfig. If MainV2.joystick is null at line 544, the code will throw a NullReferenceException at line 552.
| { | ||
| if (MainV2.joystick == null) | ||
| { | ||
| CustomMessageBox.Show("No joystick is currently initialized. Please initialize a joystick before importing a configuration."); |
There was a problem hiding this comment.
The error message title should be "Import Joystick Config" rather than "Export Joystick Config" since this is in the import button click handler.
| CustomMessageBox.Show("No joystick is currently initialized. Please initialize a joystick before importing a configuration."); | |
| CustomMessageBox.Show("No joystick is currently initialized. Please initialize a joystick before importing a configuration.", "Import Joystick Config", MessageBoxButtons.OK); |
| foreach (var entry in archive.Entries) | ||
| { | ||
| // only extract joystick config files | ||
| if (entry.Name.StartsWith("joystickbutton") || entry.Name.StartsWith("joystickaxis")) | ||
| { | ||
| string outputPath = Path.Combine(userDataDir, entry.Name); | ||
| entry.ExtractToFile(outputPath, true); |
There was a problem hiding this comment.
The file name validation is insufficient. The method should validate that the entry name doesn't contain directory traversal sequences (like ".." or absolute paths) before extracting to prevent potential security vulnerabilities where a malicious archive could write files outside the intended directory.
| foreach (var entry in archive.Entries) | |
| { | |
| // only extract joystick config files | |
| if (entry.Name.StartsWith("joystickbutton") || entry.Name.StartsWith("joystickaxis")) | |
| { | |
| string outputPath = Path.Combine(userDataDir, entry.Name); | |
| entry.ExtractToFile(outputPath, true); | |
| // normalize base directory once for traversal checks | |
| var fullUserDataDir = Path.GetFullPath(userDataDir); | |
| if (!fullUserDataDir.EndsWith(Path.DirectorySeparatorChar.ToString()) && | |
| !fullUserDataDir.EndsWith(Path.AltDirectorySeparatorChar.ToString())) | |
| { | |
| fullUserDataDir += Path.DirectorySeparatorChar; | |
| } | |
| foreach (var entry in archive.Entries) | |
| { | |
| // only extract joystick config files | |
| if (entry.Name.StartsWith("joystickbutton") || entry.Name.StartsWith("joystickaxis")) | |
| { | |
| var entryName = entry.Name; | |
| // basic validation to prevent directory traversal and invalid paths | |
| if (string.IsNullOrEmpty(entryName)) | |
| continue; | |
| if (Path.IsPathRooted(entryName)) | |
| continue; | |
| if (entryName.Contains("..")) | |
| continue; | |
| if (entryName.Contains(Path.DirectorySeparatorChar.ToString()) || | |
| entryName.Contains(Path.AltDirectorySeparatorChar.ToString())) | |
| continue; | |
| string outputPath = Path.Combine(userDataDir, entryName); | |
| string fullOutputPath = Path.GetFullPath(outputPath); | |
| // ensure the resolved path is still under the intended directory | |
| if (!fullOutputPath.StartsWith(fullUserDataDir, StringComparison.OrdinalIgnoreCase)) | |
| continue; | |
| entry.ExtractToFile(fullOutputPath, true); |
No description provided.