diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index e6ae6d7cd..8c8cf5e0a 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -14,11 +14,34 @@ Improved compatability between environments when building mRemoteNG from source #493: Changed backup file name time stamp to use local system time rather than UTC -1.75.7009 (2017-07-XX): +1.75.7010 (2017-10-29): Fixes: ------ -#596: Exception when launching ExtTool without a connection selected +#756: CustomConsPath always null + + +1.75.7009 (2017-10-28): + +Fixes: +------ +#676: Portable version ignores /cons param on first run +#675: Attempting to add new connection/folder does not work in some situations +#665: Can not add new connection or new folder in some situations +#658: Keep Port Scan tab open after import +#646: Exception after click on import port scan +#635: Updated PuTTYNG to 0.70 +#610: mRemoteNG cannot start /crashes for some users on Windows server 2012 R2 server +#600: Missing horizontal scrollbar on Connections Panel +#596: Exception when launching external tool without a connection selected +#550: Sometimes double-clicking connection tree node began rename instead of connecting +#536: Prevented log file creation when writeLogFile option is not set +#529: Erratic Tree Selection when using SQL Database +#482: Default connection password not decrypted when loaded +#335: The Quick Connect Toolbar > Connection view does not show open connections with the play icon +#176: Unable to enter text in Quick Connect when SSH connection active +Minor error message correction +Minor code refactoring NO.RELEASE (2017-06-15): diff --git a/CREDITS.TXT b/CREDITS.TXT index 5b80d2a0e..5a4f7baa7 100644 --- a/CREDITS.TXT +++ b/CREDITS.TXT @@ -13,6 +13,10 @@ countchappy (github.com/countchappy) Tony Lambert Julien Roncaglia (github.com/vbfox) github.com/peterchenadded +Brandon Wulf (github.com/mrwulf) +Pedro Rodrigues (github.com/pedro2555) +github.com/dekelMP + Past Contributors diff --git a/Jenkinsfile_publish.groovy b/Jenkinsfile_publish.groovy index 12640d98e..2c30f8792 100644 --- a/Jenkinsfile_publish.groovy +++ b/Jenkinsfile_publish.groovy @@ -32,12 +32,12 @@ node('windows') { withCredentials([file(credentialsId: '9b674d57-6792-48e3-984a-4d1bab2abb64', variable: 'CODE_SIGNING_CERT')]) { withCredentials([usernamePassword(credentialsId: '05b7449b-05c0-490f-8661-236242526e62', passwordVariable: 'MRNG_CERT_PASSWORD', usernameVariable: 'NO_USERNAME')]) { stage ('Build mRemoteNG (Normal - MSI)') { - bat "\"${vsToolsDir}\\VsDevCmd.bat\" && msbuild.exe /nologo /p:Configuration=\"Release Installer\" /p:Platform=x86 /p:CertPath=\"${env.CODE_SIGNING_CERT}\" /p:CertPassword=${env.MRNG_CERT_PASSWORD} \"${jobDir}\\mRemoteV1.sln\"" + bat "\"${vsToolsDir}\\VsDevCmd.bat\" && msbuild.exe /nologo /t:Clean,Build /p:Configuration=\"Release Installer\" /p:Platform=x86 /p:CertPath=\"${env.CODE_SIGNING_CERT}\" /p:CertPassword=${env.MRNG_CERT_PASSWORD} \"${jobDir}\\mRemoteV1.sln\"" archiveArtifacts artifacts: "Release\\*.msi", caseSensitive: false, onlyIfSuccessful: true, fingerprint: true } stage ('Build mRemoteNG (Portable)') { - bat "\"${vsToolsDir}\\VsDevCmd.bat\" && msbuild.exe /nologo /p:Configuration=\"Release Portable\" /p:Platform=x86 /p:CertPath=\"${env.CODE_SIGNING_CERT}\" /p:CertPassword=${env.MRNG_CERT_PASSWORD} \"${jobDir}\\mRemoteV1.sln\"" + bat "\"${vsToolsDir}\\VsDevCmd.bat\" && msbuild.exe /nologo /t:Clean,Build /p:Configuration=\"Release Portable\" /p:Platform=x86 /p:CertPath=\"${env.CODE_SIGNING_CERT}\" /p:CertPassword=${env.MRNG_CERT_PASSWORD} \"${jobDir}\\mRemoteV1.sln\"" archiveArtifacts artifacts: "Release\\*.zip", caseSensitive: false, onlyIfSuccessful: true, fingerprint: true } } @@ -50,6 +50,11 @@ node('windows') { stage ('Run Unit Tests (Portable)') { bat "\"${vsToolsDir}\\VsDevCmd.bat\" && VSTest.Console.exe /logger:trx /TestAdapterPath:${nunitTestAdapterPath} \"${jobDir}\\mRemoteNGTests\\bin\\Release Portable\\mRemoteNGTests.dll\"" } + + stage ('Generate UpdateCheck Files') { + bat "powershell -ExecutionPolicy Bypass -File \"${jobDir}\\Tools\\create_upg_chk_files.ps1\" -TagName \"${env.TagName}\" -UpdateChannel \"${env.UpdateChannel}\"" + archiveArtifacts artifacts: "Release\\*.txt", caseSensitive: false, onlyIfSuccessful: true + } stage ('Publish to GitHub') { withCredentials([string(credentialsId: '5443a369-dbe8-42d3-b4e8-04d0b4e9039a', variable: 'GH_AUTH_TOKEN')]) { diff --git a/Tools/create_upg_chk_files.ps1 b/Tools/create_upg_chk_files.ps1 index 28d539549..acf2e88a9 100644 --- a/Tools/create_upg_chk_files.ps1 +++ b/Tools/create_upg_chk_files.ps1 @@ -1,42 +1,109 @@ #Requires -Version 4.0 +param ( + [string] + [Parameter(Mandatory=$true)] + $TagName, + + [string] + [Parameter(Mandatory=$true)] + [ValidateSet("Stable","Beta","Development")] + $UpdateChannel +) + + + +function New-MsiUpdateFileContent { + param ( + [System.IO.FileInfo] + [Parameter(Mandatory=$true)] + $MsiFile, + + [string] + [Parameter(Mandatory=$true)] + $TagName + ) + + $version = $MsiFile.BaseName -replace "[a-zA-Z-]*" + $certThumbprint = (Get-AuthenticodeSignature -FilePath $MsiFile).SignerCertificate.Thumbprint + $hash = Get-FileHash -Algorithm SHA512 $MsiFile | % { $_.Hash } + + $fileContents = ` +"Version: $version +dURL: https://github.com/mRemoteNG/mRemoteNG/releases/download/$TagName/$($MsiFile.Name) +clURL: https://raw.githubusercontent.com/mRemoteNG/mRemoteNG/$TagName/CHANGELOG.TXT +CertificateThumbprint: $certThumbprint +Checksum: $hash" + Write-Output $fileContents +} + + +function New-ZipUpdateFileContent { + param ( + [System.IO.FileInfo] + [Parameter(Mandatory=$true)] + $ZipFile, + + [string] + [Parameter(Mandatory=$true)] + $TagName + ) + + $version = $ZipFile.BaseName -replace "[a-zA-Z-]*" + $hash = Get-FileHash -Algorithm SHA512 $ZipFile | % { $_.Hash } + + $fileContents = ` +"Version: $version +dURL: https://github.com/mRemoteNG/mRemoteNG/releases/download/$TagName/$($ZipFile.Name) +clURL: https://raw.githubusercontent.com/mRemoteNG/mRemoteNG/$TagName/CHANGELOG.TXT +Checksum: $hash" + Write-Output $fileContents +} + + +function Resolve-UpdateCheckFileName { + param ( + [string] + [Parameter(Mandatory=$true)] + [ValidateSet("Stable","Beta","Development")] + $UpdateChannel, + + [string] + [Parameter(Mandatory=$true)] + [ValidateSet("Normal","Portable")] + $Type + ) + + $fileName = "" + + if ($UpdateChannel -eq "Beta") { $fileName += "beta-" } + elseif ($UpdateChannel -eq "Development") { $fileName += "dev-" } + + $fileName += "update" + + if ($Type -eq "Portable") { $fileName += "-portable" } + + $fileName += ".txt" + + Write-Output $fileName +} + + + + $releaseFolder = Join-Path -Path $PSScriptRoot -ChildPath "..\Release" -Resolve -$tag = Read-Host -Prompt 'Tag name' -Write-Host -Write-Host -Write-Host +# build msi update file +$msiFile = Get-ChildItem -Path "$releaseFolder\*.msi" | sort LastWriteTime | select -last 1 +$msiUpdateContents = New-MsiUpdateFileContent -MsiFile $msiFile -TagName $TagName +$msiUpdateFileName = Resolve-UpdateCheckFileName -UpdateChannel $UpdateChannel -Type Normal +Write-Output "`n`nMSI Update Check File Contents ($msiUpdateFileName)`n------------------------------" +Tee-Object -InputObject $msiUpdateContents -FilePath "$releaseFolder\$msiUpdateFileName" -Write-Host PORTABLE -Write-Host -------- -$file = Get-ChildItem -Path "$releaseFolder\*.zip" | sort LastWriteTime | select -last 1 | % { $_.FullName } -$filename = $file.Split("\") | select -last 1 -$version = $file.tostring().Split("-")[2].trim(".zip") -Write-Host Version: $version - -Write-Host dURL: https://github.com/mRemoteNG/mRemoteNG/releases/download/$tag/$filename -Write-Host clURL: https://raw.githubusercontent.com/mRemoteNG/mRemoteNG/$tag/CHANGELOG.TXT - -$hash = Get-FileHash -Algorithm SHA512 $file | % { $_.Hash } -Write-Host Checksum: $hash - - -Write-Host -Write-Host -Write-Host - -Write-Host MSI -Write-Host --- -$file = Get-ChildItem -Path "$releaseFolder\*.msi" | sort LastWriteTime | select -last 1 | % { $_.FullName } -$filename = $file.Split("\") | select -last 1 - -$version = $file.tostring().Split("-")[2].trim(".msi") -Write-Host Version: $version - -Write-Host dURL: https://github.com/mRemoteNG/mRemoteNG/releases/download/$tag/$filename -Write-Host clURL: https://raw.githubusercontent.com/mRemoteNG/mRemoteNG/$tag/CHANGELOG.TXT - -Write-Host CertificateThumbprint: 0CEA828E5C787EA8AA89268D83816C1EA03375BA -$hash = Get-FileHash -Algorithm SHA512 $file | % { $_.Hash } -Write-Host Checksum: $hash \ No newline at end of file +# build zip update file +$zipFile = Get-ChildItem -Path "$releaseFolder\*.zip" | sort LastWriteTime | select -last 1 +$zipUpdateContents = New-ZipUpdateFileContent -ZipFile $zipFile -TagName $TagName +$zipUpdateFileName = Resolve-UpdateCheckFileName -UpdateChannel $UpdateChannel -Type Portable +Write-Output "`n`nZip Update Check File Contents ($zipUpdateFileName)`n------------------------------" +Tee-Object -InputObject $zipUpdateContents -FilePath "$releaseFolder\$zipUpdateFileName" \ No newline at end of file diff --git a/Tools/github_functions.ps1 b/Tools/github_functions.ps1 new file mode 100644 index 000000000..8f38fbda4 --- /dev/null +++ b/Tools/github_functions.ps1 @@ -0,0 +1,220 @@ +$githubUrl = 'https://api.github.com' + + +function Publish-GitHubRelease { + param ( + [string] + [Parameter(Mandatory=$true)] + # + $Owner, + + [string] + [Parameter(Mandatory=$true)] + # + $Repository, + + [string] + [Parameter(Mandatory=$true)] + # + $ReleaseTitle, + + [string] + [Parameter(Mandatory=$true)] + # + $TagName, + + [string] + [Parameter(Mandatory=$true)] + # Either the SHA of the commit to target or the branch name. + $TargetCommitish, + + [string] + [Parameter(Mandatory=$true)] + # + $Description, + + [bool] + [Parameter(Mandatory=$true)] + # + $IsDraft, + + [bool] + [Parameter(Mandatory=$true)] + # + $IsPrerelease, + + [string] + [Parameter(Mandatory=$true)] + # The OAuth2 token to use for authentication. + $AuthToken + ) + + $body = New-GitHubReleaseRequestBody -TagName $TagName -TargetCommitish $TargetCommitish -ReleaseTitle $ReleaseTitle -Description $Description -IsDraft $IsDraft -IsPrerelease $IsPrerelease + $req_publishRelease = Invoke-WebRequest -Uri "$githubUrl/repos/$Owner/$Repository/releases" -Method Post -Headers @{"Authorization"="token $AuthToken"} -Body $body -ErrorAction Stop + $response_publishRelease = ConvertFrom-Json -InputObject $req_publishRelease.Content + + Write-Output $response_publishRelease +} + + +function Edit-GitHubRelease { + param ( + [string] + #[Parameter(Mandatory=$true)] + # + $Owner, + + [string] + #[Parameter(Mandatory=$true)] + # + $Repository, + + [string] + #[Parameter(Mandatory=$true)] + # + $ReleaseId, + + [string] + # + $ReleaseTitle, + + [string] + # + $TagName, + + [string] + # Either the SHA of the commit to target or the branch name. + $TargetCommitish, + + [string] + # + $Description, + + [bool] + # + $IsDraft, + + [bool] + # + $IsPrerelease, + + [string] + #[Parameter(Mandatory=$true)] + # The OAuth2 token to use for authentication. + $AuthToken + ) + + $body_params = @{ + "TagName" = $TagName + "TargetCommitish" = $TargetCommitish + "ReleaseTitle" = $ReleaseTitle + "Description" = $Description + } + if ($PSBoundParameters.ContainsKey("IsDraft")) { $body_params.Add("IsDraft", $IsDraft) } + if ($PSBoundParameters.ContainsKey("IsPrerelease")) { $body_params.Add("IsPrerelease", $IsPrerelease) } + + $body = New-GitHubReleaseRequestBody @body_params + $req_editRelease = Invoke-WebRequest -Uri "$githubUrl/repos/$Owner/$Repository/releases/$ReleaseId" -Method Post -Headers @{"Authorization"="token $AuthToken"} -Body $body -ErrorAction Stop + $response_editRelease = ConvertFrom-Json -InputObject $req_editRelease.Content + + Write-Output $response_editRelease +} + + +function Get-GitHubRelease { + param ( + [string] + [Parameter(Mandatory=$true)] + # + $Owner, + + [string] + [Parameter(Mandatory=$true)] + # + $Repository, + + [string] + [Parameter(Mandatory=$true)] + # + $ReleaseId, + + [string] + [Parameter(Mandatory=$true)] + # The OAuth2 token to use for authentication. + $AuthToken + ) + + $req_getRelease = Invoke-WebRequest -Uri "$githubUrl/repos/$Owner/$Repository/releases/$ReleaseId" -Method Get -Headers @{"Authorization"="token $AuthToken"} -ErrorAction Stop + $response_getRelease = ConvertFrom-Json -InputObject $req_getRelease.Content + + Write-Output $response_getRelease +} + + +function Upload-GitHubReleaseAsset { + param ( + [string] + [Parameter(Mandatory=$true)] + $UploadUri, + + [string] + [Parameter(Mandatory=$true)] + # Path to the file to upload with the release + $FilePath, + + [string] + [Parameter(Mandatory=$true)] + # Content type of the file + $ContentType, + + [string] + [Parameter(Mandatory=$true)] + # The OAuth2 token to use for authentication. + $AuthToken + ) + + $UploadUri = $UploadUri -replace "(\{[\w,\?]*\})$" + $file = Get-Item -Path $FilePath + + $req_uploadZipAsset = Invoke-WebRequest -Uri "$($UploadUri)?name=$($file.Name)" -Method Post -Headers @{"Authorization"="token $AuthToken"} -ContentType $ContentType -InFile $file.FullName -ErrorAction Stop +} + + +function New-GitHubReleaseRequestBody { + param ( + [string] + # + $TagName, + + [string] + # Either the SHA of the commit to target or the branch name. + $TargetCommitish, + + [string] + # Title of the release + $ReleaseTitle, + + [string] + # Description of the release + $Description, + + [bool] + # Is this a draft? + $IsDraft, + + [bool] + # Is this a pre-release? + $IsPrerelease + ) + + $body_params = [ordered]@{} + if ($TagName -ne "") { $body_params.Add("tag_name", $TagName) } + if ($TargetCommitish -ne "") { $body_params.Add("target_commitish", $TargetCommitish) } + if ($ReleaseTitle -ne "") { $body_params.Add("name", $ReleaseTitle) } + if ($Description -ne "") { $body_params.Add("body", $Description) } + if ($PSBoundParameters.ContainsKey("IsDraft")) { $body_params.Add("draft", $IsDraft) } + if ($PSBoundParameters.ContainsKey("IsPrerelease")) { $body_params.Add("prerelease", $IsPrerelease) } + + $json_body = ConvertTo-Json -InputObject $body_params -Compress + Write-Output $json_body +} \ No newline at end of file diff --git a/Tools/publish_draft_github_release.ps1 b/Tools/publish_draft_github_release.ps1 new file mode 100644 index 000000000..ab5f0ec87 --- /dev/null +++ b/Tools/publish_draft_github_release.ps1 @@ -0,0 +1,25 @@ +param ( + [string] + [Parameter(Mandatory=$true)] + # + $Owner, + + [string] + [Parameter(Mandatory=$true)] + # + $Repository, + + [string] + [Parameter(Mandatory=$true)] + # + $ReleaseId, + + [string] + [Parameter(Mandatory=$true)] + # The OAuth2 token to use for authentication. + $AuthToken +) + +. "$PSScriptRoot\github_functions.ps1" + +Edit-GitHubRelease -Owner $Owner -Repository $Repository -ReleaseId $ReleaseId -AuthToken $AuthToken -IsDraft $false \ No newline at end of file diff --git a/Tools/publish_to_github.ps1 b/Tools/publish_to_github.ps1 index 6a8344321..4f920a0d8 100644 --- a/Tools/publish_to_github.ps1 +++ b/Tools/publish_to_github.ps1 @@ -62,137 +62,15 @@ param ( ) -$githubUrl = 'https://api.github.com' if ($DescriptionIsBase64Encoded) { $Description = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Description))) } -function Publish-Release { - param ( - [string] - [Parameter(Mandatory=$true)] - # - $Owner, - - [string] - [Parameter(Mandatory=$true)] - # - $Repository, - - [string] - [Parameter(Mandatory=$true)] - # - $ReleaseTitle, - - [string] - [Parameter(Mandatory=$true)] - # - $TagName, - - [string] - [Parameter(Mandatory=$true)] - # Either the SHA of the commit to target or the branch name. - $TargetCommitish, - - [string] - [Parameter(Mandatory=$true)] - # - $Description, - - [bool] - [Parameter(Mandatory=$true)] - # - $IsDraft, - - [bool] - [Parameter(Mandatory=$true)] - # - $IsPrerelease, - - [string] - [Parameter(Mandatory=$true)] - # The OAuth2 token to use for authentication. - $AuthToken - ) - - $body_publishRelease = @{ - "tag_name" = $TagName - "target_commitish" = $TargetCommitish - "name" = $ReleaseTitle - "body" = $Description - "draft" = $IsDraft - "prerelease" = $IsPrerelease - } - - $req_publishRelease = Invoke-WebRequest -Uri "$githubUrl/repos/$Owner/$Repository/releases" -Method Post -Headers @{"Authorization"="token $AuthToken"} -Body (ConvertTo-Json -InputObject $body_publishRelease -Compress) -ErrorAction Stop - $response_publishRelease = ConvertFrom-Json -InputObject $req_publishRelease.Content - - Write-Output $response_publishRelease -} +. "$PSScriptRoot\github_functions.ps1" -function Get-GitHubRelease { - param ( - [string] - [Parameter(Mandatory=$true)] - # - $Owner, - - [string] - [Parameter(Mandatory=$true)] - # - $Repository, - - [string] - [Parameter(Mandatory=$true)] - # - $ReleaseId, - - [string] - [Parameter(Mandatory=$true)] - # The OAuth2 token to use for authentication. - $AuthToken - ) - - $req_getRelease = Invoke-WebRequest -Uri "$githubUrl/repos/$Owner/$Repository/releases/$ReleaseId" -Method Get -Headers @{"Authorization"="token $AuthToken"} -ErrorAction Stop - $response_getRelease = ConvertFrom-Json -InputObject $req_getRelease.Content - - Write-Output $response_getRelease -} - - -function Upload-ReleaseAsset { - param ( - [string] - [Parameter(Mandatory=$true)] - $UploadUri, - - [string] - [Parameter(Mandatory=$true)] - # Path to the file to upload with the release - $FilePath, - - [string] - [Parameter(Mandatory=$true)] - # Content type of the file - $ContentType, - - [string] - [Parameter(Mandatory=$true)] - # The OAuth2 token to use for authentication. - $AuthToken - ) - - $UploadUri = $UploadUri -replace "(\{[\w,\?]*\})$" - $file = Get-Item -Path $FilePath - - $req_uploadZipAsset = Invoke-WebRequest -Uri "$($UploadUri)?name=$($file.Name)" -Method Post -Headers @{"Authorization"="token $AuthToken"} -ContentType $ContentType -InFile $file.FullName -ErrorAction Stop -} - - - -$release = Publish-Release -Owner $Owner -Repository $Repository -ReleaseTitle $ReleaseTitle -TagName $TagName -TargetCommitish $TargetCommitish -Description $Description -IsDraft ([bool]::Parse($IsDraft)) -IsPrerelease ([bool]::Parse($IsPrerelease)) -AuthToken $AuthToken -$zipUpload = Upload-ReleaseAsset -UploadUri $release.upload_url -FilePath $ZipFilePath -ContentType "application/zip" -AuthToken $AuthToken -$msiUpload = Upload-ReleaseAsset -UploadUri $release.upload_url -FilePath $MsiFilePath -ContentType "application/octet-stream" -AuthToken $AuthToken +$release = Publish-GitHubRelease -Owner $Owner -Repository $Repository -ReleaseTitle $ReleaseTitle -TagName $TagName -TargetCommitish $TargetCommitish -Description $Description -IsDraft ([bool]::Parse($IsDraft)) -IsPrerelease ([bool]::Parse($IsPrerelease)) -AuthToken $AuthToken +$zipUpload = Upload-GitHubReleaseAsset -UploadUri $release.upload_url -FilePath $ZipFilePath -ContentType "application/zip" -AuthToken $AuthToken +$msiUpload = Upload-GitHubReleaseAsset -UploadUri $release.upload_url -FilePath $MsiFilePath -ContentType "application/octet-stream" -AuthToken $AuthToken Write-Output (Get-GitHubRelease -Owner $Owner -Repository $Repository -ReleaseId $release.id -AuthToken $AuthToken) \ No newline at end of file diff --git a/Tools/validate_microsoft_tool.ps1 b/Tools/validate_microsoft_tool.ps1 new file mode 100644 index 000000000..96329658a --- /dev/null +++ b/Tools/validate_microsoft_tool.ps1 @@ -0,0 +1,17 @@ +# $FullPath Full path to the Microsoft executable to validate +param ( + [string] + [Parameter(Mandatory=$true)] + $FullPath +) + +$validMSCertThumbprints = @("3BDA323E552DB1FDE5F4FBEE75D6D5B2B187EEDC", "108E2BA23632620C427C570B6D9DB51AC31387FE", "98ED99A67886D020C564923B7DF25E9AC019DF26", "5EAD300DC7E4D637948ECB0ED829A072BD152E17") +$exeSignature = Get-AuthenticodeSignature -FilePath $FullPath +$baseErrorMsg = "Could not validate the certificate of $FullPath. " + +if ($exeSignature.Status -ne "Valid") { + Write-Error -Message ($baseErrorMsg+"The signature was invalid.") -ErrorAction Stop +} +elseif ($validMSCertThumbprints -notcontains $exeSignature.SignerCertificate.Thumbprint) { + Write-Error -Message ($baseErrorMsg+"The certificate thumbprint ($($exeSignature.SignerCertificate.Thumbprint)) is not trusted.") -ErrorAction Stop +} \ No newline at end of file diff --git a/mRemoteNGTests/mRemoteNGTests.csproj b/mRemoteNGTests/mRemoteNGTests.csproj index 8e381f8b1..9f544709f 100644 --- a/mRemoteNGTests/mRemoteNGTests.csproj +++ b/mRemoteNGTests/mRemoteNGTests.csproj @@ -310,11 +310,11 @@ - \ No newline at end of file diff --git a/mRemoteV1/App/Logger.cs b/mRemoteV1/App/Logger.cs index 2f015f54a..4031e0587 100644 --- a/mRemoteV1/App/Logger.cs +++ b/mRemoteV1/App/Logger.cs @@ -1,14 +1,14 @@ -using System; +using System; +using System.IO; +using System.Windows.Forms; using log4net; using log4net.Appender; using log4net.Config; -using System.IO; -using System.Windows.Forms; // ReSharper disable ArrangeAccessorOwnerBody namespace mRemoteNG.App { - public class Logger + public class Logger { public static readonly Logger Instance = new Logger(); diff --git a/mRemoteV1/App/Runtime.cs b/mRemoteV1/App/Runtime.cs index 317c34249..5a650713c 100644 --- a/mRemoteV1/App/Runtime.cs +++ b/mRemoteV1/App/Runtime.cs @@ -1,30 +1,28 @@ -using mRemoteNG.App.Info; -using mRemoteNG.Config.Connections; -using mRemoteNG.Connection; -using mRemoteNG.Messages; -using mRemoteNG.Tools; -using mRemoteNG.Tree.Root; using System; using System.IO; using System.Security; using System.Threading; using System.Windows.Forms; +using mRemoteNG.App.Info; +using mRemoteNG.Config.Connections; using mRemoteNG.Config.Connections.Multiuser; using mRemoteNG.Config.DataProviders; +using mRemoteNG.Connection; using mRemoteNG.Credential; using mRemoteNG.Credential.Repositories; +using mRemoteNG.Messages; using mRemoteNG.Security; using mRemoteNG.Security.SymmetricEncryption; +using mRemoteNG.Tools; +using mRemoteNG.Tree.Root; using mRemoteNG.UI; using mRemoteNG.UI.Forms; using mRemoteNG.UI.TaskDialog; namespace mRemoteNG.App { - public static class Runtime + public static class Runtime { - #region Public Properties - public static bool IsPortableEdition { get @@ -42,12 +40,11 @@ namespace mRemoteNG.App public static NotificationAreaIcon NotificationAreaIcon { get; set; } public static RemoteConnectionsSyncronizer RemoteConnectionsSyncronizer { get; set; } // ReSharper disable once UnusedAutoPropertyAccessor.Local - private static DateTime LastSqlUpdate { get; set; } + public static DateTime LastSqlUpdate { get; set; } public static ExternalToolsService ExternalToolsService { get; } = new ExternalToolsService(); public static SecureString EncryptionKey { get; set; } = new RootNodeInfo(RootNodeType.Connection).PasswordString.ConvertToSecureString(); public static ICredentialRepositoryList CredentialProviderCatalog { get; } = new CredentialRepositoryList(); public static ConnectionsService ConnectionsService { get; } = new ConnectionsService(); - #endregion #region Connections Loading/Saving public static void LoadConnectionsAsync() diff --git a/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs b/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs index d88b88987..8cc7daf4b 100644 --- a/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs +++ b/mRemoteV1/Config/Connections/Multiuser/SqlConnectionsUpdateChecker.cs @@ -57,7 +57,15 @@ namespace mRemoteNG.Config.Connections private bool DatabaseIsMoreUpToDateThanUs() { - return GetLastUpdateTimeFromDbResponse() > _lastUpdateTime; + var lastUpdateInDb = GetLastUpdateTimeFromDbResponse(); + var IAmTheLastoneUpdated = CheckIfIAmTheLastOneUpdated(lastUpdateInDb); + return (lastUpdateInDb > _lastUpdateTime && !IAmTheLastoneUpdated); + } + + private bool CheckIfIAmTheLastOneUpdated(DateTime lastUpdateInDb) + { + DateTime LastSqlUpdateWithoutMilliseconds = new DateTime(Runtime.LastSqlUpdate.Ticks - (Runtime.LastSqlUpdate.Ticks % TimeSpan.TicksPerSecond), Runtime.LastSqlUpdate.Kind); + return lastUpdateInDb == LastSqlUpdateWithoutMilliseconds; } private DateTime GetLastUpdateTimeFromDbResponse() diff --git a/mRemoteV1/Connection/DefaultConnectionInheritance.cs b/mRemoteV1/Connection/DefaultConnectionInheritance.cs index 1dc37f64a..b55ca8634 100644 --- a/mRemoteV1/Connection/DefaultConnectionInheritance.cs +++ b/mRemoteV1/Connection/DefaultConnectionInheritance.cs @@ -1,4 +1,5 @@ using System; +using mRemoteNG.App; namespace mRemoteNG.Connection @@ -17,11 +18,17 @@ namespace mRemoteNG.Connection public void LoadFrom(TSource sourceInstance, Func propertyNameMutator = null) { - if (propertyNameMutator == null) propertyNameMutator = (a) => a; + if (propertyNameMutator == null) propertyNameMutator = a => a; var inheritanceProperties = GetProperties(); foreach (var property in inheritanceProperties) { var propertyFromSettings = typeof(TSource).GetProperty(propertyNameMutator(property.Name)); + if (propertyFromSettings == null) + { + Runtime.MessageCollector.AddMessage(Messages.MessageClass.ErrorMsg, + $"DefaultConInherit-LoadFrom: Could not load {property.Name}", true); + continue; + } var valueFromSettings = propertyFromSettings.GetValue(sourceInstance, null); property.SetValue(Instance, valueFromSettings, null); } @@ -29,12 +36,18 @@ namespace mRemoteNG.Connection public void SaveTo(TDestination destinationInstance, Func propertyNameMutator = null) { - if (propertyNameMutator == null) propertyNameMutator = (a) => a; + if (propertyNameMutator == null) propertyNameMutator = a => a; var inheritanceProperties = GetProperties(); foreach (var property in inheritanceProperties) { var propertyFromSettings = typeof(TDestination).GetProperty(propertyNameMutator(property.Name)); var localValue = property.GetValue(Instance, null); + if (propertyFromSettings == null) + { + Runtime.MessageCollector?.AddMessage(Messages.MessageClass.ErrorMsg, + $"DefaultConInherit-SaveTo: Could not load {property.Name}", true); + continue; + } propertyFromSettings.SetValue(destinationInstance, localValue, null); } } diff --git a/mRemoteV1/Connection/Protocol/IntegratedProgram.cs b/mRemoteV1/Connection/Protocol/IntegratedProgram.cs index 9c24c2d0e..e807ecd53 100644 --- a/mRemoteV1/Connection/Protocol/IntegratedProgram.cs +++ b/mRemoteV1/Connection/Protocol/IntegratedProgram.cs @@ -1,12 +1,11 @@ -using mRemoteNG.App; -using mRemoteNG.Tools; using System; using System.Diagnostics; using System.Drawing; using System.Threading; using System.Windows.Forms; +using mRemoteNG.App; using mRemoteNG.Messages; - +using mRemoteNG.Tools; namespace mRemoteNG.Connection.Protocol { @@ -63,9 +62,9 @@ namespace mRemoteNG.Connection.Protocol _process.Exited += ProcessExited; _process.Start(); - _process.WaitForInputIdle(Settings.Default.MaxPuttyWaitTime * 1000); - - var startTicks = Environment.TickCount; + _process.WaitForInputIdle(Settings.Default.MaxPuttyWaitTime * 1000); + + var startTicks = Environment.TickCount; while (_handle.ToInt32() == 0 & Environment.TickCount < startTicks + Settings.Default.MaxPuttyWaitTime * 1000) { _process.Refresh(); diff --git a/mRemoteV1/Resources/Language/Language.Designer.cs b/mRemoteV1/Resources/Language/Language.Designer.cs index 3055c2219..e4699c9ed 100644 --- a/mRemoteV1/Resources/Language/Language.Designer.cs +++ b/mRemoteV1/Resources/Language/Language.Designer.cs @@ -8,18 +8,18 @@ // //------------------------------------------------------------------------------ -namespace mRemoteNG { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] +namespace mRemoteNG +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Language { @@ -529,7 +529,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to You cannot import a normal connection file. + /// Looks up a localized string similar to You cannot import a normal connection file. ///Please use File - Load Connections for normal connection files!. /// internal static string strCannotImportNormalSessionFile { @@ -665,9 +665,9 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to The (RDP) Sessions feature requires that you have a copy of eolwtscom.dll registered on your system. - ///mRemoteNG ships with this component but it is not registered automatically if you do not use the mRemoteNG Installer. - ///To register it manually, run the following command from an elevated command prompt: regsvr32 "C:\Program Files\mRemoteNG\eolwtscom.dll" (where C:\Program Files\mRemoteNG\ is the path to your mRemoteNG installation). + /// Looks up a localized string similar to The (RDP) Sessions feature requires that you have a copy of eolwtscom.dll registered on your system. + ///mRemoteNG ships with this component but it is not registered automatically if you do not use the mRemoteNG Installer. + ///To register it manually, run the following command from an elevated command prompt: regsvr32 "C:\Program Files\mRemoteNG\eolwtscom.dll" (where C:\Program Files\mRemoteNG\ is the path to your mRemoteNG installation). ///If this check still fails or you are unable to use the (RDP) Sessions feature [rest of string was truncated]";. /// internal static string strCcEOLFailed { @@ -686,9 +686,9 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to To use the Gecko Rendering Engine you need to have XULrunner 1.8.1.x and the path to the installation set in your Options. - ///You can download XULrunner 1.8.1.3 here: ftp://ftp.mozilla.org/pub/xulrunner/releases/1.8.1.3/contrib/win32/ - ///When you are finished downloading extract the package to a path of your choice. Then in mRemoteNG go to Tools - Options - Advanced and enter the correct path in the XULrunner path field. + /// Looks up a localized string similar to To use the Gecko Rendering Engine you need to have XULrunner 1.8.1.x and the path to the installation set in your Options. + ///You can download XULrunner 1.8.1.3 here: ftp://ftp.mozilla.org/pub/xulrunner/releases/1.8.1.3/contrib/win32/ + ///When you are finished downloading extract the package to a path of your choice. Then in mRemoteNG go to Tools - Options - Advanced and enter the correct path in the XULrunner path field. ///If you are still not able to pass this check or use the Gecko Engine in mRemoteNG please cons [rest of string was truncated]";. /// internal static string strCcGeckoFailed { @@ -707,8 +707,8 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to ICA requires that the XenDesktop Online Plugin is installed and that the wfica.ocx library is registered. You can download the client here: http://www.citrix.com/download/ - ///If you have the XenDesktop Online Plugin installed and the check still fails, try to register wfica.ocx manually. + /// Looks up a localized string similar to ICA requires that the XenDesktop Online Plugin is installed and that the wfica.ocx library is registered. You can download the client here: http://www.citrix.com/download/ + ///If you have the XenDesktop Online Plugin installed and the check still fails, try to register wfica.ocx manually. ///To do this open up the run dialog (Start - Run) and enter the following: regsvr32 "c:\Program Files\Citrix\ICA Client\wfica.ocx" (Where c:\Program Files\Citrix\ICA Client\ is the path to your XenDesktop Online Plugin installat [rest of string was truncated]";. /// internal static string strCcICAFailed { @@ -718,7 +718,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to All ICA components were found and seem to be registered properly. + /// Looks up a localized string similar to All ICA components were found and seem to be registered properly. ///Citrix ICA Client Control Version {0}. /// internal static string strCcICAOK { @@ -737,7 +737,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to The SSH, Telnet, Rlogin and RAW protocols need PuTTY to work. PuTTY comes with every mRemoteNG package and is located in the installation path. + /// Looks up a localized string similar to The SSH, Telnet, Rlogin and RAW protocols need PuTTY to work. PuTTY comes with every mRemoteNG package and is located in the installation path. ///Please make sure that either you have the Putty.exe in your mRemoteNG directory (default: c:\Program Files\mRemoteNG\) or that you specified a valid path to your PuTTY executable in the Options (Tools - Options - Advanced - Custom PuTTY path). /// internal static string strCcPuttyFailed { @@ -756,7 +756,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to For RDP to work properly you need to have at least Remote Desktop Connection (Terminal Services) Client 8.0 installed. You can download it here: http://support.microsoft.com/kb/925876 + /// Looks up a localized string similar to For RDP to work properly you need to have at least Remote Desktop Connection (Terminal Services) Client 8.0 installed. You can download it here: http://support.microsoft.com/kb/925876 ///If this check still fails or you are unable to use RDP, please consult the mRemoteNG Forum at http://forum.mremoteng.org/.. /// internal static string strCcRDPFailed { @@ -766,7 +766,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to All RDP components were found and seem to be registered properly. + /// Looks up a localized string similar to All RDP components were found and seem to be registered properly. ///Remote Desktop Connection Control Version {0}. /// internal static string strCcRDPOK { @@ -776,8 +776,8 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to VNC requires VncSharp.dll to be located in your mRemoteNG application folder. - ///Please make sure that you have the VncSharp.dll file in your mRemoteNG application folder (usually C:\Program Files\mRemoteNG\). + /// Looks up a localized string similar to VNC requires VncSharp.dll to be located in your mRemoteNG application folder. + ///Please make sure that you have the VncSharp.dll file in your mRemoteNG application folder (usually C:\Program Files\mRemoteNG\). ///If you are still not able to pass this check or use VNC in mRemoteNG please consult the mRemoteNG Forum at http://forum.mremoteng.org/. /// internal static string strCcVNCFailed { @@ -787,7 +787,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to All VNC components were found and seem to be registered properly. + /// Looks up a localized string similar to All VNC components were found and seem to be registered properly. ///VncSharp Control Version {0}. /// internal static string strCcVNCOK { @@ -1166,7 +1166,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to Do you want to close the connection: + /// Looks up a localized string similar to Do you want to close the connection: ///"{0}"?. /// internal static string strConfirmCloseConnectionMainInstruction { @@ -1347,8 +1347,8 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to RDP error! - ///Error Code: {0} + /// Looks up a localized string similar to RDP error! + ///Error Code: {0} ///Error Description: {1}. /// internal static string strConnectionRdpErrorDetail { @@ -1403,7 +1403,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to Connections file "{0}" could not be loaded! + /// Looks up a localized string similar to Connections file "{0}" could not be loaded! ///Starting with new connections file.. /// internal static string strConnectionsFileCouldNotBeLoadedNew { @@ -1539,8 +1539,8 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to In v1.76 we have introduced a credential management system. This feature requires a significant change in how we store and interact with credentials within mRemoteNG. You will be required to perform a one-way upgrade of your mRemoteNG connections file. - /// + /// Looks up a localized string similar to In v1.76 we have introduced a credential management system. This feature requires a significant change in how we store and interact with credentials within mRemoteNG. You will be required to perform a one-way upgrade of your mRemoteNG connections file. + /// ///This page will walk you through the process of upgrading your connections file or give you a chance to open a different connections file if you do not want to perform the upgrade.. /// internal static string strCredentialManagerUpgradeDescription { @@ -1838,10 +1838,10 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to The Windows security setting, "System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing", is enabled. This setting is not compatible with {0}. - /// - ///See the Microsoft Support article at http://support.microsoft.com/kb/811833 for more information. - /// + /// Looks up a localized string similar to The Windows security setting, "System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing", is enabled. This setting is not compatible with {0}. + /// + ///See the Microsoft Support article at http://support.microsoft.com/kb/811833 for more information. + /// ///{0} will now close.. /// internal static string strErrorFipsPolicyIncompatible { @@ -3867,7 +3867,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to You are trying to load a connection file that was created using an very early version of mRemote, this could result in an runtime error. + /// Looks up a localized string similar to You are trying to load a connection file that was created using an very early version of mRemote, this could result in an runtime error. ///If you run into such an error, please create a new connection file!. /// internal static string strOldConffile { @@ -5236,8 +5236,8 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to Protocol Event Disconnected. - ///Message: + /// Looks up a localized string similar to Protocol Event Disconnected. + ///Message: ///{0}. /// internal static string strProtocolEventDisconnected { @@ -5247,7 +5247,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to Protocol Event Disconnected failed. + /// Looks up a localized string similar to Protocol Event Disconnected failed. ///{0}. /// internal static string strProtocolEventDisconnectFailed { @@ -7093,7 +7093,7 @@ namespace mRemoteNG { } /// - /// Looks up a localized string similar to Download complete! + /// Looks up a localized string similar to Download complete! ///mRemoteNG will now quit and begin with the installation.. /// internal static string strUpdateDownloadComplete { diff --git a/mRemoteV1/Resources/Language/Language.ja-JP.resx b/mRemoteV1/Resources/Language/Language.ja-JP.resx index 69086f585..dd31088d5 100644 --- a/mRemoteV1/Resources/Language/Language.ja-JP.resx +++ b/mRemoteV1/Resources/Language/Language.ja-JP.resx @@ -246,7 +246,7 @@ You cannot import a normal connection file. -Please use File - Load Connections for normal connection files! +Please use File - Open Connection File for normal connection files! IPアドレスのフォーマットが正しくないのでポートスキャンの開始に失敗しました diff --git a/mRemoteV1/Resources/Language/Language.resx b/mRemoteV1/Resources/Language/Language.resx index 06c729829..2f6014289 100644 --- a/mRemoteV1/Resources/Language/Language.resx +++ b/mRemoteV1/Resources/Language/Language.resx @@ -245,7 +245,7 @@ You cannot import a normal connection file. -Please use File - Load Connections for normal connection files! +Please use File - Open Connection File for normal connection files! Cannot start Port Scan, incorrect IP format! diff --git a/mRemoteV1/Resources/PuTTYNG.exe b/mRemoteV1/Resources/PuTTYNG.exe index 78dbd246e..429c3e636 100644 Binary files a/mRemoteV1/Resources/PuTTYNG.exe and b/mRemoteV1/Resources/PuTTYNG.exe differ diff --git a/mRemoteV1/Tools/ConnectionsTreeToMenuItemsConverter.cs b/mRemoteV1/Tools/ConnectionsTreeToMenuItemsConverter.cs index b36aff7a9..bf6cfa005 100644 --- a/mRemoteV1/Tools/ConnectionsTreeToMenuItemsConverter.cs +++ b/mRemoteV1/Tools/ConnectionsTreeToMenuItemsConverter.cs @@ -6,16 +6,15 @@ using mRemoteNG.App; using mRemoteNG.Connection; using mRemoteNG.Container; using mRemoteNG.Tree; -using mRemoteNG.UI.Controls; namespace mRemoteNG.Tools { - public class ConnectionsTreeToMenuItemsConverter + public class ConnectionsTreeToMenuItemsConverter { - private readonly StatusImageList _statusImageList = new StatusImageList(); public MouseEventHandler MouseUpEventHandler { get; set; } + public IEnumerable CreateToolStripDropDownItems(ConnectionTreeModel connectionTreeModel) { var rootNodes = connectionTreeModel.RootNodes; @@ -50,15 +49,26 @@ namespace mRemoteNG.Tools var menuItem = new ToolStripMenuItem { Text = node.Name, - Tag = node, - Image = _statusImageList.GetImage(node) + Tag = node }; var nodeAsContainer = node as ContainerInfo; if (nodeAsContainer != null) { + menuItem.Image = Resources.Folder; + menuItem.Tag = nodeAsContainer; AddSubMenuNodes(nodeAsContainer.Children, menuItem); } + else if (node.GetTreeNodeType() == TreeNodeType.PuttySession) + { + menuItem.Image = Resources.PuttySessions; + menuItem.Tag = node; + } + else if (node.GetTreeNodeType() == TreeNodeType.Connection) + { + menuItem.Image = node.OpenConnections.Count > 0 ? Resources.Play : Resources.Pause; + menuItem.Tag = node; + } menuItem.MouseUp += MouseUpEventHandler; return menuItem; diff --git a/mRemoteV1/Tools/ExternalTool.cs b/mRemoteV1/Tools/ExternalTool.cs index d38bf8095..31c384405 100644 --- a/mRemoteV1/Tools/ExternalTool.cs +++ b/mRemoteV1/Tools/ExternalTool.cs @@ -112,7 +112,7 @@ namespace mRemoteNG.Tools private void SetConnectionInfoFields(ConnectionInfo newConnectionInfo) { newConnectionInfo.Protocol = ProtocolType.IntApp; - newConnectionInfo.ExtApp = DisplayName; + newConnectionInfo.ExtApp = FileName; newConnectionInfo.Name = DisplayName; newConnectionInfo.Panel = Language.strMenuExternalTools; } diff --git a/mRemoteV1/Tools/ExternalToolArgumentParser.cs b/mRemoteV1/Tools/ExternalToolArgumentParser.cs index 6e697ea12..edbb1d2b3 100644 --- a/mRemoteV1/Tools/ExternalToolArgumentParser.cs +++ b/mRemoteV1/Tools/ExternalToolArgumentParser.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using mRemoteNG.App; using mRemoteNG.Connection; +using mRemoteNG.Security.SymmetricEncryption; using mRemoteNG.Tools.Cmdline; namespace mRemoteNG.Tools { - public class ExternalToolArgumentParser + public class ExternalToolArgumentParser { private readonly ConnectionInfo _connectionInfo; @@ -170,12 +172,26 @@ namespace mRemoteNG.Tools break; case "username": replacement = _connectionInfo.Username; + if (string.IsNullOrEmpty(replacement)) + if (Settings.Default.EmptyCredentials == "windows") + replacement = Environment.UserName; + else if (Settings.Default.EmptyCredentials == "custom") + replacement = Settings.Default.DefaultUsername; break; case "password": replacement = _connectionInfo.Password; + if (string.IsNullOrEmpty(replacement) && Settings.Default.EmptyCredentials == "custom") + replacement = new LegacyRijndaelCryptographyProvider() + .Decrypt(Convert.ToString(Settings.Default.DefaultPassword), + Runtime.EncryptionKey); break; case "domain": replacement = _connectionInfo.Domain; + if (string.IsNullOrEmpty(replacement)) + if (Settings.Default.EmptyCredentials == "windows") + replacement = Environment.UserDomainName; + else if (Settings.Default.EmptyCredentials == "custom") + replacement = Settings.Default.DefaultDomain; break; case "description": replacement = _connectionInfo.Description; diff --git a/mRemoteV1/Tools/IeBrowserEmulation.cs b/mRemoteV1/Tools/IeBrowserEmulation.cs index 11a0fe10a..b7e9fba87 100644 --- a/mRemoteV1/Tools/IeBrowserEmulation.cs +++ b/mRemoteV1/Tools/IeBrowserEmulation.cs @@ -7,7 +7,7 @@ using mRemoteNG.App; namespace mRemoteNG.Tools { - public class IeBrowserEmulation + public class IeBrowserEmulation { // found this here: // http://www.neowin.net/forum/topic/1077469-vbnet-webbrowser-control-does-not-load-javascript/#comment-596755046 @@ -205,7 +205,7 @@ namespace mRemoteNG.Tools } catch (Exception ex) { - Runtime.MessageCollector.AddExceptionMessage("IeBrowserEmulation.Register() failed.", ex); + Runtime.MessageCollector?.AddExceptionMessage("IeBrowserEmulation.Register() failed.", ex); } } @@ -219,7 +219,7 @@ namespace mRemoteNG.Tools } catch (Exception ex) { - Runtime.MessageCollector.AddExceptionMessage("IeBrowserEmulation.Unregister() failed.", ex); + Runtime.MessageCollector?.AddExceptionMessage("IeBrowserEmulation.Unregister() failed.", ex); } #endif } diff --git a/mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs b/mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs index 5c3a5cc70..0b3146e54 100644 --- a/mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs +++ b/mRemoteV1/UI/Controls/ConnectionTree/ConnectionTree.cs @@ -22,10 +22,10 @@ namespace mRemoteNG.UI.Controls private readonly PuttySessionsManager _puttySessionsManager = PuttySessionsManager.Instance; private readonly StatusImageList _statusImageList = new StatusImageList(); - public ConnectionInfo SelectedNode - { - get { return (ConnectionInfo) SelectedObject; } - } + private bool _allowEdit; + private bool _isUpdatingColumnWidth; + + public ConnectionInfo SelectedNode => (ConnectionInfo) SelectedObject; public NodeSearcher NodeSearcher { get; private set; } @@ -103,30 +103,49 @@ namespace mRemoteNG.UI.Controls Collapsed += (sender, args) => { var container = args.Model as ContainerInfo; - if (container != null) - container.IsExpanded = false; + if (container == null) return; + container.IsExpanded = false; + UpdateColumnWidth(); }; Expanded += (sender, args) => { var container = args.Model as ContainerInfo; - if (container != null) - container.IsExpanded = true; + if (container == null) return; + container.IsExpanded = true; + UpdateColumnWidth(); }; + SizeChanged += OnSizeChanged; SelectionChanged += tvConnections_AfterSelect; MouseDoubleClick += OnMouse_DoubleClick; MouseClick += OnMouse_SingleClick; CellToolTipShowing += tvConnections_CellToolTipShowing; ModelCanDrop += _dragAndDropHandler.HandleEvent_ModelCanDrop; ModelDropped += _dragAndDropHandler.HandleEvent_ModelDropped; + BeforeLabelEdit += HandleCheckForValidEdit; + } + + private void OnSizeChanged(object o, EventArgs eventArgs) + { + if (_isUpdatingColumnWidth) + return; + UpdateColumnWidth(); + } + + private void UpdateColumnWidth() + { + _isUpdatingColumnWidth = true; + AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); + Columns[0].Width += SmallImageSize.Width; + _isUpdatingColumnWidth = false; } private void PopulateTreeView() { UnregisterModelUpdateHandlers(); SetObjects(ConnectionTreeModel.RootNodes); - ResizeColumns(); RegisterModelUpdateHandlers(); NodeSearcher = new NodeSearcher(ConnectionTreeModel); + UpdateColumnWidth(); ExecutePostSetupActions(); } @@ -147,12 +166,12 @@ namespace mRemoteNG.UI.Controls private void OnPuttySessionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) { RefreshObjects(GetRootPuttyNodes().ToList()); - ResizeColumns(); } private void HandleCollectionPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs) { - //TODO for some reason property changed events are getting triggered twice for each changed property. should be just once. cant find source of duplication + // for some reason property changed events are getting triggered twice for each changed property. should be just once. cant find source of duplication + // Removed "TO DO" from above comment. Per #142 it apperas that this no longer occurs with ObjectListView 2.9.1 var property = propertyChangedEventArgs.PropertyName; if (property != nameof(ConnectionInfo.Name) && property != nameof(ConnectionInfo.OpenConnections) @@ -162,9 +181,11 @@ namespace mRemoteNG.UI.Controls } var senderAsConnectionInfo = sender as ConnectionInfo; - if (senderAsConnectionInfo != null) - RefreshObject(senderAsConnectionInfo); - ResizeColumns(); + if (senderAsConnectionInfo == null) + return; + + RefreshObject(senderAsConnectionInfo); + UpdateColumnWidth(); } private void ExecutePostSetupActions() @@ -223,11 +244,12 @@ namespace mRemoteNG.UI.Controls private void AddNode(ConnectionInfo newNode) { - if (SelectedNode == null) return; + // use root node if no node is selected + ConnectionInfo parentNode = SelectedNode ?? GetRootConnectionNode(); DefaultConnectionInfo.Instance.SaveTo(newNode); DefaultConnectionInheritance.Instance.SaveTo(newNode.Inheritance); - var selectedContainer = SelectedNode as ContainerInfo; - var parent = selectedContainer ?? SelectedNode?.Parent; + var selectedContainer = parentNode as ContainerInfo; + var parent = selectedContainer ?? parentNode?.Parent; newNode.SetParent(parent); Expand(parent); SelectObject(newNode, true); @@ -245,10 +267,24 @@ namespace mRemoteNG.UI.Controls public void RenameSelectedNode() { + _allowEdit = true; SelectedItem.BeginEdit(); Runtime.SaveConnectionsAsync(); } + public void HandleCheckForValidEdit(object sender, LabelEditEventArgs e) + { + if (!(sender is ConnectionTree)) return; + if (_allowEdit) + { + _allowEdit = false; + } + else + { + e.CancelEdit = true; + } + } + public void DeleteSelectedNode() { if (SelectedNode is RootNodeInfo || SelectedNode is PuttySessionInfo) return; @@ -274,7 +310,6 @@ namespace mRemoteNG.UI.Controls private void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) { RefreshObject(sender); - ResizeColumns(); } private void tvConnections_AfterSelect(object sender, EventArgs e) @@ -321,16 +356,6 @@ namespace mRemoteNG.UI.Controls Runtime.MessageCollector.AddExceptionStackTrace("tvConnections_MouseMove (UI.Window.ConnectionTreeWindow) failed", ex); } } - - private void ResizeColumns() - { - foreach (OLVColumn column in Columns) - { - column.FreeSpaceProportion = 95; - column.AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize); - } - - } #endregion } } \ No newline at end of file diff --git a/mRemoteV1/UI/Controls/ConnectionTree/NameColumn.cs b/mRemoteV1/UI/Controls/ConnectionTree/NameColumn.cs index 5d6c0d6a4..d6f8ec24a 100644 --- a/mRemoteV1/UI/Controls/ConnectionTree/NameColumn.cs +++ b/mRemoteV1/UI/Controls/ConnectionTree/NameColumn.cs @@ -3,11 +3,12 @@ using mRemoteNG.Connection; namespace mRemoteNG.UI.Controls { - public class NameColumn : OLVColumn + public class NameColumn : OLVColumn { public NameColumn(ImageGetterDelegate imageGetterDelegate) { AspectName = "Name"; + FillsFreeSpace = false; AspectGetter = item => ((ConnectionInfo) item).Name; ImageGetter = imageGetterDelegate; } diff --git a/mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs b/mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs index f0de81334..a54be6e3b 100644 --- a/mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs +++ b/mRemoteV1/UI/Window/ConnectionTreeWindow.Designer.cs @@ -1,5 +1,8 @@ +using mRemoteNG.Connection; +using mRemoteNG.Tree; + namespace mRemoteNG.UI.Window { public partial class ConnectionTreeWindow : BaseWindow @@ -19,6 +22,9 @@ namespace mRemoteNG.UI.Window private void InitializeComponent() { this.components = new System.ComponentModel.Container(); + mRemoteNG.Tree.TreeNodeCompositeClickHandler treeNodeCompositeClickHandler1 = new mRemoteNG.Tree.TreeNodeCompositeClickHandler(); + mRemoteNG.Tree.AlwaysConfirmYes alwaysConfirmYes1 = new mRemoteNG.Tree.AlwaysConfirmYes(); + mRemoteNG.Tree.TreeNodeCompositeClickHandler treeNodeCompositeClickHandler2 = new mRemoteNG.Tree.TreeNodeCompositeClickHandler(); this.olvConnections = new mRemoteNG.UI.Controls.ConnectionTree(); this.pnlConnections = new System.Windows.Forms.Panel(); this.PictureBox1 = new System.Windows.Forms.PictureBox(); @@ -48,22 +54,31 @@ namespace mRemoteNG.UI.Window | System.Windows.Forms.AnchorStyles.Right))); this.olvConnections.BorderStyle = System.Windows.Forms.BorderStyle.None; this.olvConnections.CellEditUseWholeCell = false; + this.olvConnections.ConnectionTreeModel = new ConnectionTreeModel(); this.olvConnections.Cursor = System.Windows.Forms.Cursors.Default; + treeNodeCompositeClickHandler1.ClickHandlers = new ITreeNodeClickHandler[0]; + this.olvConnections.DoubleClickHandler = treeNodeCompositeClickHandler1; this.olvConnections.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; this.olvConnections.HideSelection = false; this.olvConnections.IsSimpleDragSource = true; + this.olvConnections.IsSimpleDropSink = true; this.olvConnections.LabelEdit = true; this.olvConnections.Location = new System.Drawing.Point(0, 0); this.olvConnections.MultiSelect = false; this.olvConnections.Name = "olvConnections"; + this.olvConnections.NodeDeletionConfirmer = alwaysConfirmYes1; + this.olvConnections.PostSetupActions = new mRemoteNG.UI.Controls.IConnectionTreeDelegate[0]; this.olvConnections.SelectedBackColor = System.Drawing.SystemColors.Highlight; this.olvConnections.SelectedForeColor = System.Drawing.SystemColors.HighlightText; this.olvConnections.ShowGroups = false; + treeNodeCompositeClickHandler2.ClickHandlers = new mRemoteNG.Tree.ITreeNodeClickHandler[0]; + this.olvConnections.SingleClickHandler = treeNodeCompositeClickHandler2; this.olvConnections.Size = new System.Drawing.Size(192, 410); this.olvConnections.TabIndex = 20; this.olvConnections.UnfocusedSelectedBackColor = System.Drawing.SystemColors.Highlight; this.olvConnections.UnfocusedSelectedForeColor = System.Drawing.SystemColors.HighlightText; this.olvConnections.UseCompatibleStateImageBehavior = false; + this.olvConnections.UseOverlays = false; this.olvConnections.View = System.Windows.Forms.View.Details; this.olvConnections.VirtualMode = true; // diff --git a/mRemoteV1/UI/Window/PortScanWindow.cs b/mRemoteV1/UI/Window/PortScanWindow.cs index 8e9bb83ca..d8cdced92 100644 --- a/mRemoteV1/UI/Window/PortScanWindow.cs +++ b/mRemoteV1/UI/Window/PortScanWindow.cs @@ -1,12 +1,12 @@ -using System.Collections.Generic; using System; +using System.Collections.Generic; using System.Windows.Forms; -using WeifenLuo.WinFormsUI.Docking; using mRemoteNG.App; using mRemoteNG.Connection.Protocol; using mRemoteNG.Container; using mRemoteNG.Messages; using mRemoteNG.Tools; +using WeifenLuo.WinFormsUI.Docking; using static mRemoteNG.Tools.MiscTools; namespace mRemoteNG.UI.Window