mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
174 lines
5.8 KiB
C++
174 lines
5.8 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
|
|
#include "SshHostGenerator.h"
|
|
#include "../../inc/DefaultSettings.h"
|
|
|
|
#include "DynamicProfileUtils.h"
|
|
#include <LibraryResources.h>
|
|
|
|
static constexpr std::wstring_view SshHostGeneratorNamespace{ L"Windows.Terminal.SSH" };
|
|
|
|
static constexpr std::wstring_view PROFILE_TITLE_PREFIX = L"SSH - ";
|
|
static constexpr std::wstring_view PROFILE_ICON_PATH = L"\uE977"; // PC1
|
|
static constexpr std::wstring_view GENERATOR_ICON_PATH = L"\uE969"; // StorageNetworkWireless
|
|
|
|
// OpenSSH is installed under System32 when installed via Optional Features
|
|
static constexpr std::wstring_view SSH_EXE_PATH1 = L"%SystemRoot%\\System32\\OpenSSH\\ssh.exe";
|
|
|
|
// OpenSSH (x86/x64) is installed under Program Files when installed via MSI
|
|
static constexpr std::wstring_view SSH_EXE_PATH2 = L"%ProgramFiles%\\OpenSSH\\ssh.exe";
|
|
|
|
// OpenSSH (x86) is installed under Program Files x86 when installed via MSI on x64 machine
|
|
static constexpr std::wstring_view SSH_EXE_PATH3 = L"%ProgramFiles(x86)%\\OpenSSH\\ssh.exe";
|
|
|
|
static constexpr std::wstring_view SSH_SYSTEM_CONFIG_PATH = L"%ProgramData%\\ssh\\ssh_config";
|
|
static constexpr std::wstring_view SSH_USER_CONFIG_PATH = L"%UserProfile%\\.ssh\\config";
|
|
|
|
static constexpr std::wstring_view SSH_CONFIG_HOST_KEY{ L"Host" };
|
|
static constexpr std::wstring_view SSH_CONFIG_HOSTNAME_KEY{ L"HostName" };
|
|
|
|
using namespace ::Microsoft::Terminal::Settings::Model;
|
|
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
|
|
|
/*static*/ const std::wregex SshHostGenerator::_configKeyValueRegex{ LR"(^\s*(\w+)\s+([^\s]+.*[^\s])\s*$)" };
|
|
|
|
/*static*/ std::wstring_view SshHostGenerator::_getProfileName(const std::wstring_view& hostName) noexcept
|
|
{
|
|
return std::wstring_view{ L"" + PROFILE_TITLE_PREFIX + hostName };
|
|
}
|
|
|
|
/*static*/ std::wstring_view SshHostGenerator::_getProfileIconPath() noexcept
|
|
{
|
|
return PROFILE_ICON_PATH;
|
|
}
|
|
|
|
/*static*/ std::wstring_view SshHostGenerator::_getProfileCommandLine(const std::wstring_view& sshExePath, const std::wstring_view& hostName) noexcept
|
|
{
|
|
return std::wstring_view{ L"\"" + sshExePath + L"\" " + hostName };
|
|
}
|
|
|
|
/*static*/ bool SshHostGenerator::_tryFindSshExePath(std::wstring& sshExePath) noexcept
|
|
{
|
|
try
|
|
{
|
|
for (const auto& path : { SSH_EXE_PATH1, SSH_EXE_PATH2, SSH_EXE_PATH3 })
|
|
{
|
|
if (std::filesystem::exists(wil::ExpandEnvironmentStringsW<std::wstring>(path.data())))
|
|
{
|
|
sshExePath = path;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
CATCH_LOG();
|
|
|
|
return false;
|
|
}
|
|
|
|
/*static*/ bool SshHostGenerator::_tryParseConfigKeyValue(const std::wstring_view& line, std::wstring& key, std::wstring& value) noexcept
|
|
{
|
|
try
|
|
{
|
|
if (!line.empty() && !line.starts_with(L"#"))
|
|
{
|
|
std::wstring input{ line };
|
|
std::wsmatch match;
|
|
if (std::regex_search(input, match, SshHostGenerator::_configKeyValueRegex))
|
|
{
|
|
key = match[1];
|
|
value = match[2];
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
CATCH_LOG();
|
|
|
|
return false;
|
|
}
|
|
|
|
/*static*/ void SshHostGenerator::_getHostNamesFromConfigFile(const std::wstring_view& configPath, std::vector<std::wstring>& hostNames) noexcept
|
|
{
|
|
try
|
|
{
|
|
const std::filesystem::path resolvedConfigPath{ wil::ExpandEnvironmentStringsW<std::wstring>(configPath.data()) };
|
|
if (std::filesystem::exists(resolvedConfigPath))
|
|
{
|
|
std::wifstream inputStream(resolvedConfigPath);
|
|
|
|
std::wstring line;
|
|
std::wstring key;
|
|
std::wstring value;
|
|
|
|
std::wstring lastHost;
|
|
|
|
while (std::getline(inputStream, line))
|
|
{
|
|
if (_tryParseConfigKeyValue(line, key, value))
|
|
{
|
|
if (til::equals_insensitive_ascii(key, SSH_CONFIG_HOST_KEY))
|
|
{
|
|
// Save potential Host value for later
|
|
lastHost = value;
|
|
}
|
|
else if (til::equals_insensitive_ascii(key, SSH_CONFIG_HOSTNAME_KEY))
|
|
{
|
|
// HostName was specified
|
|
if (!lastHost.empty())
|
|
{
|
|
hostNames.emplace_back(lastHost);
|
|
lastHost = L"";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CATCH_LOG();
|
|
}
|
|
|
|
std::wstring_view SshHostGenerator::GetNamespace() const noexcept
|
|
{
|
|
return SshHostGeneratorNamespace;
|
|
}
|
|
|
|
std::wstring_view SshHostGenerator::GetDisplayName() const noexcept
|
|
{
|
|
return RS_(L"SshHostGeneratorDisplayName");
|
|
}
|
|
|
|
std::wstring_view SshHostGenerator::GetIcon() const noexcept
|
|
{
|
|
return GENERATOR_ICON_PATH;
|
|
}
|
|
|
|
// Method Description:
|
|
// - Generate a list of profiles for each detected OpenSSH host.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <A list of SSH host profiles.>
|
|
void SshHostGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
|
|
{
|
|
std::wstring sshExePath;
|
|
if (_tryFindSshExePath(sshExePath))
|
|
{
|
|
std::vector<std::wstring> hostNames;
|
|
|
|
_getHostNamesFromConfigFile(SSH_SYSTEM_CONFIG_PATH, hostNames);
|
|
_getHostNamesFromConfigFile(SSH_USER_CONFIG_PATH, hostNames);
|
|
|
|
for (const auto& hostName : hostNames)
|
|
{
|
|
const auto profile{ CreateDynamicProfile(_getProfileName(hostName)) };
|
|
|
|
profile->Commandline(winrt::hstring{ _getProfileCommandLine(sshExePath, hostName) });
|
|
profile->Icon(winrt::hstring{ _getProfileIconPath() });
|
|
|
|
profiles.emplace_back(profile);
|
|
}
|
|
}
|
|
}
|