diff --git a/Greenshot/Forms/AboutForm.Designer.cs b/Greenshot/Forms/AboutForm.Designer.cs index 1b35f8699..a2ff0904b 100644 --- a/Greenshot/Forms/AboutForm.Designer.cs +++ b/Greenshot/Forms/AboutForm.Designer.cs @@ -21,6 +21,7 @@ using System.Windows.Forms; using Greenshot.Helpers; +using GreenshotPlugin.Core; namespace Greenshot.Forms { partial class AboutForm { diff --git a/GreenshotImgurPlugin/ImgurUtils.cs b/GreenshotImgurPlugin/ImgurUtils.cs index 9b1e73e68..580b89c66 100644 --- a/GreenshotImgurPlugin/ImgurUtils.cs +++ b/GreenshotImgurPlugin/ImgurUtils.cs @@ -169,11 +169,11 @@ namespace GreenshotImgurPlugin { { AuthUrlPattern = "https://api.imgur.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}", TokenUrl = "https://api.imgur.com/oauth2/token", - RedirectUrl = "https://getgreenshot.org/oauth/imgur", + RedirectUrl = "https://getgreenshot.org/authorize/imgur", CloudServiceName = "Imgur", ClientId = ImgurCredentials.CONSUMER_KEY, ClientSecret = ImgurCredentials.CONSUMER_SECRET, - AuthorizeMode = OAuth2AuthorizeMode.MonitorTitle, + AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver, RefreshToken = Config.RefreshToken, AccessToken = Config.AccessToken, AccessTokenExpires = Config.AccessTokenExpires diff --git a/Greenshot/Helpers/EnvironmentInfo.cs b/GreenshotPlugin/Core/EnvironmentInfo.cs similarity index 96% rename from Greenshot/Helpers/EnvironmentInfo.cs rename to GreenshotPlugin/Core/EnvironmentInfo.cs index 59caf857e..ba0c061d2 100644 --- a/Greenshot/Helpers/EnvironmentInfo.cs +++ b/GreenshotPlugin/Core/EnvironmentInfo.cs @@ -1,855 +1,854 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: http://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using GreenshotPlugin.Core; -using GreenshotPlugin.IniFile; -using GreenshotPlugin.UnmanagedHelpers; -using Microsoft.Win32; - -namespace Greenshot.Helpers -{ - /// - /// Description of EnvironmentInfo. - /// - public static class EnvironmentInfo - { - private static bool? _isWindows; - - public static bool IsWindows - { - get - { - if (_isWindows.HasValue) - { - return _isWindows.Value; - } - _isWindows = Environment.OSVersion.Platform.ToString().StartsWith("Win"); - return _isWindows.Value; - } - } - - public static bool IsNet45OrNewer() - { - // Class "ReflectionContext" exists from .NET 4.5 onwards. - return Type.GetType("System.Reflection.ReflectionContext", false) != null; - } - - public static string GetGreenshotVersion(bool shortVersion = false) - { - var executingAssembly = Assembly.GetExecutingAssembly(); - - // Use assembly version - string greenshotVersion = executingAssembly.GetName().Version.ToString(); - - // Use AssemblyFileVersion if available - var assemblyFileVersionAttribute = executingAssembly.GetCustomAttribute(); - if (!string.IsNullOrEmpty(assemblyFileVersionAttribute?.Version)) - { - var assemblyFileVersion = new Version(assemblyFileVersionAttribute.Version); - greenshotVersion = assemblyFileVersion.ToString(3); - } - - if (!shortVersion) - { - // Use AssemblyInformationalVersion if available - var informationalVersionAttribute = executingAssembly.GetCustomAttribute(); - if (!string.IsNullOrEmpty(informationalVersionAttribute?.InformationalVersion)) - { - greenshotVersion = informationalVersionAttribute.InformationalVersion; - } - } - - return greenshotVersion.Replace("+", " - "); - } - - public static string EnvironmentToString(bool newline) - { - StringBuilder environment = new StringBuilder(); - environment.Append("Software version: " + GetGreenshotVersion()); - if (IniConfig.IsPortable) { - environment.Append(" Portable"); - } - environment.Append(" (" + OsInfo.Bits + " bit)"); - - if (newline) - { - environment.AppendLine(); - } - else - { - environment.Append(", "); - } - environment.Append(".NET runtime version: " + Environment.Version); - if (IsNet45OrNewer()) - { - environment.Append("+"); - - } - if (newline) - { - environment.AppendLine(); - } - else - { - environment.Append(", "); - } - environment.Append("Time: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss zzz")); - - if (IsWindows) - { - if (newline) - { - environment.AppendLine(); - } - else - { - environment.Append(", "); - } - - environment.Append($"OS: {OsInfo.Name}"); - if (!string.IsNullOrEmpty(OsInfo.Edition)) - { - environment.Append($" {OsInfo.Edition}"); - - } - if (!string.IsNullOrEmpty(OsInfo.ServicePack)) - { - environment.Append($" {OsInfo.ServicePack}"); - - } - environment.Append($" x{OsInfo.Bits}"); - environment.Append($" {OsInfo.VersionString}"); - if (newline) - { - environment.AppendLine(); - } - else - { - environment.Append(", "); - } - // Get some important information for fixing GDI related Problems - environment.AppendFormat("GDI object count: {0}", User32.GetGuiResourcesGDICount()); - if (newline) - { - environment.AppendLine(); - } - else - { - environment.Append(", "); - } - environment.AppendFormat("User object count: {0}", User32.GetGuiResourcesUserCount()); - } - else - { - if (newline) - { - environment.AppendLine(); - } - else - { - environment.Append(", "); - } - environment.AppendFormat("OS: {0}", Environment.OSVersion.Platform); - } - if (newline) - { - environment.AppendLine(); - } - else - { - environment.Append(", "); - } - // TODO: Is this needed? - // environment.AppendFormat("Surface count: {0}", Surface.Count); - - return environment.ToString(); - } - - public static string ExceptionToString(Exception ex) - { - if (ex == null) - return "null\r\n"; - - StringBuilder report = new StringBuilder(); - - report.AppendLine("Exception: " + ex.GetType()); - report.AppendLine("Message: " + ex.Message); - if (ex.Data.Count > 0) - { - report.AppendLine(); - report.AppendLine("Additional Information:"); - foreach (object key in ex.Data.Keys) - { - object data = ex.Data[key]; - if (data != null) - { - report.AppendLine(key + " : " + data); - } - } - } - if (ex is ExternalException) - { - // e.g. COMException - report.AppendLine().AppendLine("ErrorCode: 0x" + (ex as ExternalException).ErrorCode.ToString("X")); - } - - report.AppendLine().AppendLine("Stack:").AppendLine(ex.StackTrace); - - if (ex is ReflectionTypeLoadException) - { - report.AppendLine().AppendLine("LoaderExceptions: "); - foreach (Exception cbE in (ex as ReflectionTypeLoadException).LoaderExceptions) - { - report.AppendLine(cbE.Message); - } - } - - if (ex.InnerException != null) - { - report.AppendLine("--- InnerException: ---"); - report.AppendLine(ExceptionToString(ex.InnerException)); - } - return report.ToString(); - } - - public static string BuildReport(Exception exception) - { - StringBuilder exceptionText = new StringBuilder(); - exceptionText.AppendLine(EnvironmentToString(true)); - exceptionText.AppendLine(ExceptionToString(exception)); - exceptionText.AppendLine("Configuration dump:"); - - return exceptionText.ToString(); - } - } - - /// - /// Provides detailed information about the host operating system. - /// Code is available at: http://www.csharp411.com/determine-windows-version-and-edition-with-c/ - /// - public static class OsInfo - { - /// - /// Determines if the current application is 32 or 64-bit. - /// - public static int Bits => IntPtr.Size * 8; - - private static string _sEdition; - - /// - /// Gets the edition of the operating system running on this computer. - /// - public static string Edition - { - get - { - if (_sEdition != null) - { - return _sEdition; //***** RETURN *****// - } - - string edition = string.Empty; - - OperatingSystem osVersion = Environment.OSVersion; - OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); - - if (GetVersionEx(ref osVersionInfo)) - { - int majorVersion = osVersion.Version.Major; - int minorVersion = osVersion.Version.Minor; - byte productType = osVersionInfo.ProductType; - ushort suiteMask = osVersionInfo.SuiteMask; - - if (majorVersion == 4) - { - if (productType == VER_NT_WORKSTATION) - { - // Windows NT 4.0 Workstation - edition = "Workstation"; - } - else if (productType == VER_NT_SERVER) - { - edition = (suiteMask & VER_SUITE_ENTERPRISE) != 0 ? "Enterprise Server" : "Standard Server"; - } - } - - else if (majorVersion == 5) - { - if (productType == VER_NT_WORKSTATION) - { - if ((suiteMask & VER_SUITE_PERSONAL) != 0) - { - // Windows XP Home Edition - edition = "Home"; - } - else - { - // Windows XP / Windows 2000 Professional - edition = "Professional"; - } - } - else if (productType == VER_NT_SERVER) - { - if (minorVersion == 0) - { - if ((suiteMask & VER_SUITE_DATACENTER) != 0) - { - // Windows 2000 Datacenter Server - edition = "Datacenter Server"; - } - else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) - { - // Windows 2000 Advanced Server - edition = "Advanced Server"; - } - else - { - // Windows 2000 Server - edition = "Server"; - } - } - else - { - if ((suiteMask & VER_SUITE_DATACENTER) != 0) - { - // Windows Server 2003 Datacenter Edition - edition = "Datacenter"; - } - else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) - { - // Windows Server 2003 Enterprise Edition - edition = "Enterprise"; - } - else if ((suiteMask & VER_SUITE_BLADE) != 0) - { - // Windows Server 2003 Web Edition - edition = "Web Edition"; - } - else - { - // Windows Server 2003 Standard Edition - edition = "Standard"; - } - } - } - } - - else if (majorVersion == 6) - { - if (GetProductInfo(majorVersion, minorVersion, osVersionInfo.ServicePackMajor, osVersionInfo.ServicePackMinor, out var ed)) - { - switch (ed) - { - case PRODUCT_BUSINESS: - edition = "Business"; - break; - case PRODUCT_BUSINESS_N: - edition = "Business N"; - break; - case PRODUCT_CLUSTER_SERVER: - edition = "HPC Edition"; - break; - case PRODUCT_DATACENTER_SERVER: - edition = "Datacenter Server"; - break; - case PRODUCT_DATACENTER_SERVER_CORE: - edition = "Datacenter Server (core installation)"; - break; - case PRODUCT_ENTERPRISE: - edition = "Enterprise"; - break; - case PRODUCT_ENTERPRISE_N: - edition = "Enterprise N"; - break; - case PRODUCT_ENTERPRISE_SERVER: - edition = "Enterprise Server"; - break; - case PRODUCT_ENTERPRISE_SERVER_CORE: - edition = "Enterprise Server (core installation)"; - break; - case PRODUCT_ENTERPRISE_SERVER_CORE_V: - edition = "Enterprise Server without Hyper-V (core installation)"; - break; - case PRODUCT_ENTERPRISE_SERVER_IA64: - edition = "Enterprise Server for Itanium-based Systems"; - break; - case PRODUCT_ENTERPRISE_SERVER_V: - edition = "Enterprise Server without Hyper-V"; - break; - case PRODUCT_HOME_BASIC: - edition = "Home Basic"; - break; - case PRODUCT_HOME_BASIC_N: - edition = "Home Basic N"; - break; - case PRODUCT_HOME_PREMIUM: - edition = "Home Premium"; - break; - case PRODUCT_HOME_PREMIUM_N: - edition = "Home Premium N"; - break; - case PRODUCT_HYPERV: - edition = "Microsoft Hyper-V Server"; - break; - case PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT: - edition = "Windows Essential Business Management Server"; - break; - case PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING: - edition = "Windows Essential Business Messaging Server"; - break; - case PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY: - edition = "Windows Essential Business Security Server"; - break; - case PRODUCT_SERVER_FOR_SMALLBUSINESS: - edition = "Windows Essential Server Solutions"; - break; - case PRODUCT_SERVER_FOR_SMALLBUSINESS_V: - edition = "Windows Essential Server Solutions without Hyper-V"; - break; - case PRODUCT_SMALLBUSINESS_SERVER: - edition = "Windows Small Business Server"; - break; - case PRODUCT_STANDARD_SERVER: - edition = "Standard Server"; - break; - case PRODUCT_STANDARD_SERVER_CORE: - edition = "Standard Server (core installation)"; - break; - case PRODUCT_STANDARD_SERVER_CORE_V: - edition = "Standard Server without Hyper-V (core installation)"; - break; - case PRODUCT_STANDARD_SERVER_V: - edition = "Standard Server without Hyper-V"; - break; - case PRODUCT_STARTER: - edition = "Starter"; - break; - case PRODUCT_STORAGE_ENTERPRISE_SERVER: - edition = "Enterprise Storage Server"; - break; - case PRODUCT_STORAGE_EXPRESS_SERVER: - edition = "Express Storage Server"; - break; - case PRODUCT_STORAGE_STANDARD_SERVER: - edition = "Standard Storage Server"; - break; - case PRODUCT_STORAGE_WORKGROUP_SERVER: - edition = "Workgroup Storage Server"; - break; - case PRODUCT_UNDEFINED: - edition = "Unknown product"; - break; - case PRODUCT_ULTIMATE: - edition = "Ultimate"; - break; - case PRODUCT_ULTIMATE_N: - edition = "Ultimate N"; - break; - case PRODUCT_WEB_SERVER: - edition = "Web Server"; - break; - case PRODUCT_WEB_SERVER_CORE: - edition = "Web Server (core installation)"; - break; - } - } - } - } - - _sEdition = edition; - return edition; - } - } - - private static string _name; - /// - /// Gets the name of the operating system running on this computer. - /// - public static string Name - { - get - { - if (_name != null) - { - return _name; //***** RETURN *****// - } - - string name = "unknown"; - - OperatingSystem osVersion = Environment.OSVersion; - OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); - if (GetVersionEx(ref osVersionInfo)) - { - int majorVersion = osVersion.Version.Major; - int minorVersion = osVersion.Version.Minor; - byte productType = osVersionInfo.ProductType; - ushort suiteMask = osVersionInfo.SuiteMask; - switch (osVersion.Platform) - { - case PlatformID.Win32Windows: - if (majorVersion == 4) - { - string csdVersion = osVersionInfo.ServicePackVersion; - switch (minorVersion) - { - case 0: - if (csdVersion == "B" || csdVersion == "C") - { - name = "Windows 95 OSR2"; - } - else - { - name = "Windows 95"; - } - break; - case 10: - name = csdVersion == "A" ? "Windows 98 Second Edition" : "Windows 98"; - break; - case 90: - name = "Windows Me"; - break; - } - } - break; - case PlatformID.Win32NT: - switch (majorVersion) - { - case 3: - name = "Windows NT 3.51"; - break; - case 4: - switch (productType) - { - case 1: - name = "Windows NT 4.0"; - break; - case 3: - name = "Windows NT 4.0 Server"; - break; - } - break; - case 5: - switch (minorVersion) - { - case 0: - name = "Windows 2000"; - break; - case 1: - name = suiteMask switch - { - 0x0200 => "Windows XP Professional", - _ => "Windows XP" - }; - break; - case 2: - name = suiteMask switch - { - 0x0200 => "Windows XP Professional x64", - 0x0002 => "Windows Server 2003 Enterprise", - 0x0080 => "Windows Server 2003 Data Center", - 0x0400 => "Windows Server 2003 Web Edition", - 0x8000 => "Windows Home Server", - _ => "Windows Server 2003" - }; - break; - } - break; - case 6: - switch (minorVersion) - { - case 0: - name = productType switch - { - 3 => "Windows Server 2008", - _ => "Windows Vista" - }; - break; - case 1: - name = productType switch - { - 3 => "Windows Server 2008 R2", - _ => "Windows 7" - }; - break; - case 2: - name = "Windows 8"; - break; - case 3: - name = "Windows 8.1"; - break; - } - break; - case 10: - string releaseId = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ReleaseId", "").ToString(); - name = $"Windows 10 {releaseId}"; - break; - } - break; - } - } - - _name = name; - return name; - } - } - - [DllImport("Kernel32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool GetProductInfo( - int osMajorVersion, - int osMinorVersion, - int spMajorVersion, - int spMinorVersion, - out int edition); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool GetVersionEx(ref OSVERSIONINFOEX osVersionInfo); - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - private unsafe struct OSVERSIONINFOEX - { - /// - /// The size of this data structure, in bytes. Set this member to sizeof(OSVERSIONINFOEX). - /// - private int _dwOSVersionInfoSize; - - private readonly int _dwMajorVersion; - private readonly int _dwMinorVersion; - private readonly int _dwBuildNumber; - private readonly int _dwPlatformId; - private fixed char _szCSDVersion[128]; - private readonly short _wServicePackMajor; - private readonly short _wServicePackMinor; - private readonly ushort _wSuiteMask; - private readonly byte _wProductType; - private readonly byte _wReserved; - - /// - /// The major version number of the operating system. - /// - public int MajorVersion => _dwMajorVersion; - - /// - /// The minor version number of the operating system. - /// - public int MinorVersion => _dwMinorVersion; - - /// - /// The build number of the operating system. - /// - public int BuildNumber => _dwBuildNumber; - - /// - /// The operating system platform. This member can be VER_PLATFORM_WIN32_NT (2). - /// - public int PlatformId => _dwPlatformId; - - /// - /// A null-terminated string, such as "Service Pack 3", that indicates the latest Service Pack installed on the system. - /// If no Service Pack has been installed, the string is empty. - /// - public string ServicePackVersion - { - get - { - fixed (char* servicePackVersion = _szCSDVersion) - { - return new string(servicePackVersion); - } - - } - } - - /// - /// The major version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the - /// major version number is 3. - /// If no Service Pack has been installed, the value is zero. - /// - public short ServicePackMajor => _wServicePackMajor; - - /// - /// The minor version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the - /// minor version number is 0. - /// - public short ServicePackMinor => _wServicePackMinor; - - /// - /// A bit mask that identifies the product suites available on the system. This member can be a combination of the - /// following values. - /// - public ushort SuiteMask => _wSuiteMask; - - /// - /// Any additional information about the system. - /// - public byte ProductType => _wProductType; - - /// - /// Factory for an empty OsVersionInfoEx - /// - /// OSVERSIONINFOEX - public static OSVERSIONINFOEX Create() - { - return new OSVERSIONINFOEX - { - _dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)) - }; - } - } - - private const int PRODUCT_UNDEFINED = 0x00000000; - private const int PRODUCT_ULTIMATE = 0x00000001; - private const int PRODUCT_HOME_BASIC = 0x00000002; - private const int PRODUCT_HOME_PREMIUM = 0x00000003; - private const int PRODUCT_ENTERPRISE = 0x00000004; - private const int PRODUCT_HOME_BASIC_N = 0x00000005; - private const int PRODUCT_BUSINESS = 0x00000006; - private const int PRODUCT_STANDARD_SERVER = 0x00000007; - private const int PRODUCT_DATACENTER_SERVER = 0x00000008; - private const int PRODUCT_SMALLBUSINESS_SERVER = 0x00000009; - private const int PRODUCT_ENTERPRISE_SERVER = 0x0000000A; - private const int PRODUCT_STARTER = 0x0000000B; - private const int PRODUCT_DATACENTER_SERVER_CORE = 0x0000000C; - private const int PRODUCT_STANDARD_SERVER_CORE = 0x0000000D; - private const int PRODUCT_ENTERPRISE_SERVER_CORE = 0x0000000E; - private const int PRODUCT_ENTERPRISE_SERVER_IA64 = 0x0000000F; - private const int PRODUCT_BUSINESS_N = 0x00000010; - private const int PRODUCT_WEB_SERVER = 0x00000011; - private const int PRODUCT_CLUSTER_SERVER = 0x00000012; - private const int PRODUCT_STORAGE_EXPRESS_SERVER = 0x00000014; - private const int PRODUCT_STORAGE_STANDARD_SERVER = 0x00000015; - private const int PRODUCT_STORAGE_WORKGROUP_SERVER = 0x00000016; - private const int PRODUCT_STORAGE_ENTERPRISE_SERVER = 0x00000017; - private const int PRODUCT_SERVER_FOR_SMALLBUSINESS = 0x00000018; - private const int PRODUCT_HOME_PREMIUM_N = 0x0000001A; - private const int PRODUCT_ENTERPRISE_N = 0x0000001B; - private const int PRODUCT_ULTIMATE_N = 0x0000001C; - private const int PRODUCT_WEB_SERVER_CORE = 0x0000001D; - private const int PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E; - private const int PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F; - private const int PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020; - private const int PRODUCT_SERVER_FOR_SMALLBUSINESS_V = 0x00000023; - private const int PRODUCT_STANDARD_SERVER_V = 0x00000024; - private const int PRODUCT_ENTERPRISE_SERVER_V = 0x00000026; - private const int PRODUCT_STANDARD_SERVER_CORE_V = 0x00000028; - private const int PRODUCT_ENTERPRISE_SERVER_CORE_V = 0x00000029; - private const int PRODUCT_HYPERV = 0x0000002A; - - private const int VER_NT_WORKSTATION = 1; - private const int VER_NT_SERVER = 3; - private const int VER_SUITE_ENTERPRISE = 2; - private const int VER_SUITE_DATACENTER = 128; - private const int VER_SUITE_PERSONAL = 512; - private const int VER_SUITE_BLADE = 1024; - - /// - /// Gets the service pack information of the operating system running on this computer. - /// - public static string ServicePack - { - get - { - string servicePack = string.Empty; - OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); - - if (GetVersionEx(ref osVersionInfo)) - { - servicePack = osVersionInfo.ServicePackVersion; - } - - return servicePack; - } - } - - /// - /// Gets the build version number of the operating system running on this computer. - /// - public static int BuildVersion => Environment.OSVersion.Version.Build; - - /// - /// Gets the full version string of the operating system running on this computer. - /// - public static string VersionString - { - get - { - if (WindowsVersion.IsWindows10OrLater) - { - return $"build {Environment.OSVersion.Version.Build}"; - } - if (Environment.OSVersion.Version.Revision != 0) - { - return $"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor} build {Environment.OSVersion.Version.Build} revision {Environment.OSVersion.Version.Revision:X}"; - } - return $"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor} build {Environment.OSVersion.Version.Build}"; - } - } - - /// - /// Gets the full version of the operating system running on this computer. - /// - public static Version Version - { - get - { - return Environment.OSVersion.Version; - } - } - - /// - /// Gets the major version number of the operating system running on this computer. - /// - public static int MajorVersion - { - get - { - return Environment.OSVersion.Version.Major; - } - } - - /// - /// Gets the minor version number of the operating system running on this computer. - /// - public static int MinorVersion - { - get - { - return Environment.OSVersion.Version.Minor; - } - } - - /// - /// Gets the revision version number of the operating system running on this computer. - /// - public static int RevisionVersion - { - get - { - return Environment.OSVersion.Version.Revision; - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using GreenshotPlugin.IniFile; +using GreenshotPlugin.UnmanagedHelpers; +using Microsoft.Win32; + +namespace GreenshotPlugin.Core +{ + /// + /// Description of EnvironmentInfo. + /// + public static class EnvironmentInfo + { + private static bool? _isWindows; + + public static bool IsWindows + { + get + { + if (_isWindows.HasValue) + { + return _isWindows.Value; + } + _isWindows = Environment.OSVersion.Platform.ToString().StartsWith("Win"); + return _isWindows.Value; + } + } + + public static bool IsNet45OrNewer() + { + // Class "ReflectionContext" exists from .NET 4.5 onwards. + return Type.GetType("System.Reflection.ReflectionContext", false) != null; + } + + public static string GetGreenshotVersion(bool shortVersion = false) + { + var executingAssembly = Assembly.GetExecutingAssembly(); + + // Use assembly version + string greenshotVersion = executingAssembly.GetName().Version.ToString(); + + // Use AssemblyFileVersion if available + var assemblyFileVersionAttribute = executingAssembly.GetCustomAttribute(); + if (!string.IsNullOrEmpty(assemblyFileVersionAttribute?.Version)) + { + var assemblyFileVersion = new Version(assemblyFileVersionAttribute.Version); + greenshotVersion = assemblyFileVersion.ToString(3); + } + + if (!shortVersion) + { + // Use AssemblyInformationalVersion if available + var informationalVersionAttribute = executingAssembly.GetCustomAttribute(); + if (!string.IsNullOrEmpty(informationalVersionAttribute?.InformationalVersion)) + { + greenshotVersion = informationalVersionAttribute.InformationalVersion; + } + } + + return greenshotVersion.Replace("+", " - "); + } + + public static string EnvironmentToString(bool newline) + { + StringBuilder environment = new StringBuilder(); + environment.Append("Software version: " + GetGreenshotVersion()); + if (IniConfig.IsPortable) { + environment.Append(" Portable"); + } + environment.Append(" (" + OsInfo.Bits + " bit)"); + + if (newline) + { + environment.AppendLine(); + } + else + { + environment.Append(", "); + } + environment.Append(".NET runtime version: " + Environment.Version); + if (IsNet45OrNewer()) + { + environment.Append("+"); + + } + if (newline) + { + environment.AppendLine(); + } + else + { + environment.Append(", "); + } + environment.Append("Time: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss zzz")); + + if (IsWindows) + { + if (newline) + { + environment.AppendLine(); + } + else + { + environment.Append(", "); + } + + environment.Append($"OS: {OsInfo.Name}"); + if (!string.IsNullOrEmpty(OsInfo.Edition)) + { + environment.Append($" {OsInfo.Edition}"); + + } + if (!string.IsNullOrEmpty(OsInfo.ServicePack)) + { + environment.Append($" {OsInfo.ServicePack}"); + + } + environment.Append($" x{OsInfo.Bits}"); + environment.Append($" {OsInfo.VersionString}"); + if (newline) + { + environment.AppendLine(); + } + else + { + environment.Append(", "); + } + // Get some important information for fixing GDI related Problems + environment.AppendFormat("GDI object count: {0}", User32.GetGuiResourcesGDICount()); + if (newline) + { + environment.AppendLine(); + } + else + { + environment.Append(", "); + } + environment.AppendFormat("User object count: {0}", User32.GetGuiResourcesUserCount()); + } + else + { + if (newline) + { + environment.AppendLine(); + } + else + { + environment.Append(", "); + } + environment.AppendFormat("OS: {0}", Environment.OSVersion.Platform); + } + if (newline) + { + environment.AppendLine(); + } + else + { + environment.Append(", "); + } + // TODO: Is this needed? + // environment.AppendFormat("Surface count: {0}", Surface.Count); + + return environment.ToString(); + } + + public static string ExceptionToString(Exception ex) + { + if (ex == null) + return "null\r\n"; + + StringBuilder report = new StringBuilder(); + + report.AppendLine("Exception: " + ex.GetType()); + report.AppendLine("Message: " + ex.Message); + if (ex.Data.Count > 0) + { + report.AppendLine(); + report.AppendLine("Additional Information:"); + foreach (object key in ex.Data.Keys) + { + object data = ex.Data[key]; + if (data != null) + { + report.AppendLine(key + " : " + data); + } + } + } + if (ex is ExternalException) + { + // e.g. COMException + report.AppendLine().AppendLine("ErrorCode: 0x" + (ex as ExternalException).ErrorCode.ToString("X")); + } + + report.AppendLine().AppendLine("Stack:").AppendLine(ex.StackTrace); + + if (ex is ReflectionTypeLoadException) + { + report.AppendLine().AppendLine("LoaderExceptions: "); + foreach (Exception cbE in (ex as ReflectionTypeLoadException).LoaderExceptions) + { + report.AppendLine(cbE.Message); + } + } + + if (ex.InnerException != null) + { + report.AppendLine("--- InnerException: ---"); + report.AppendLine(ExceptionToString(ex.InnerException)); + } + return report.ToString(); + } + + public static string BuildReport(Exception exception) + { + StringBuilder exceptionText = new StringBuilder(); + exceptionText.AppendLine(EnvironmentToString(true)); + exceptionText.AppendLine(ExceptionToString(exception)); + exceptionText.AppendLine("Configuration dump:"); + + return exceptionText.ToString(); + } + } + + /// + /// Provides detailed information about the host operating system. + /// Code is available at: http://www.csharp411.com/determine-windows-version-and-edition-with-c/ + /// + public static class OsInfo + { + /// + /// Determines if the current application is 32 or 64-bit. + /// + public static int Bits => IntPtr.Size * 8; + + private static string _sEdition; + + /// + /// Gets the edition of the operating system running on this computer. + /// + public static string Edition + { + get + { + if (_sEdition != null) + { + return _sEdition; //***** RETURN *****// + } + + string edition = string.Empty; + + OperatingSystem osVersion = Environment.OSVersion; + OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); + + if (GetVersionEx(ref osVersionInfo)) + { + int majorVersion = osVersion.Version.Major; + int minorVersion = osVersion.Version.Minor; + byte productType = osVersionInfo.ProductType; + ushort suiteMask = osVersionInfo.SuiteMask; + + if (majorVersion == 4) + { + if (productType == VER_NT_WORKSTATION) + { + // Windows NT 4.0 Workstation + edition = "Workstation"; + } + else if (productType == VER_NT_SERVER) + { + edition = (suiteMask & VER_SUITE_ENTERPRISE) != 0 ? "Enterprise Server" : "Standard Server"; + } + } + + else if (majorVersion == 5) + { + if (productType == VER_NT_WORKSTATION) + { + if ((suiteMask & VER_SUITE_PERSONAL) != 0) + { + // Windows XP Home Edition + edition = "Home"; + } + else + { + // Windows XP / Windows 2000 Professional + edition = "Professional"; + } + } + else if (productType == VER_NT_SERVER) + { + if (minorVersion == 0) + { + if ((suiteMask & VER_SUITE_DATACENTER) != 0) + { + // Windows 2000 Datacenter Server + edition = "Datacenter Server"; + } + else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) + { + // Windows 2000 Advanced Server + edition = "Advanced Server"; + } + else + { + // Windows 2000 Server + edition = "Server"; + } + } + else + { + if ((suiteMask & VER_SUITE_DATACENTER) != 0) + { + // Windows Server 2003 Datacenter Edition + edition = "Datacenter"; + } + else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) + { + // Windows Server 2003 Enterprise Edition + edition = "Enterprise"; + } + else if ((suiteMask & VER_SUITE_BLADE) != 0) + { + // Windows Server 2003 Web Edition + edition = "Web Edition"; + } + else + { + // Windows Server 2003 Standard Edition + edition = "Standard"; + } + } + } + } + + else if (majorVersion == 6) + { + if (GetProductInfo(majorVersion, minorVersion, osVersionInfo.ServicePackMajor, osVersionInfo.ServicePackMinor, out var ed)) + { + switch (ed) + { + case PRODUCT_BUSINESS: + edition = "Business"; + break; + case PRODUCT_BUSINESS_N: + edition = "Business N"; + break; + case PRODUCT_CLUSTER_SERVER: + edition = "HPC Edition"; + break; + case PRODUCT_DATACENTER_SERVER: + edition = "Datacenter Server"; + break; + case PRODUCT_DATACENTER_SERVER_CORE: + edition = "Datacenter Server (core installation)"; + break; + case PRODUCT_ENTERPRISE: + edition = "Enterprise"; + break; + case PRODUCT_ENTERPRISE_N: + edition = "Enterprise N"; + break; + case PRODUCT_ENTERPRISE_SERVER: + edition = "Enterprise Server"; + break; + case PRODUCT_ENTERPRISE_SERVER_CORE: + edition = "Enterprise Server (core installation)"; + break; + case PRODUCT_ENTERPRISE_SERVER_CORE_V: + edition = "Enterprise Server without Hyper-V (core installation)"; + break; + case PRODUCT_ENTERPRISE_SERVER_IA64: + edition = "Enterprise Server for Itanium-based Systems"; + break; + case PRODUCT_ENTERPRISE_SERVER_V: + edition = "Enterprise Server without Hyper-V"; + break; + case PRODUCT_HOME_BASIC: + edition = "Home Basic"; + break; + case PRODUCT_HOME_BASIC_N: + edition = "Home Basic N"; + break; + case PRODUCT_HOME_PREMIUM: + edition = "Home Premium"; + break; + case PRODUCT_HOME_PREMIUM_N: + edition = "Home Premium N"; + break; + case PRODUCT_HYPERV: + edition = "Microsoft Hyper-V Server"; + break; + case PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT: + edition = "Windows Essential Business Management Server"; + break; + case PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING: + edition = "Windows Essential Business Messaging Server"; + break; + case PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY: + edition = "Windows Essential Business Security Server"; + break; + case PRODUCT_SERVER_FOR_SMALLBUSINESS: + edition = "Windows Essential Server Solutions"; + break; + case PRODUCT_SERVER_FOR_SMALLBUSINESS_V: + edition = "Windows Essential Server Solutions without Hyper-V"; + break; + case PRODUCT_SMALLBUSINESS_SERVER: + edition = "Windows Small Business Server"; + break; + case PRODUCT_STANDARD_SERVER: + edition = "Standard Server"; + break; + case PRODUCT_STANDARD_SERVER_CORE: + edition = "Standard Server (core installation)"; + break; + case PRODUCT_STANDARD_SERVER_CORE_V: + edition = "Standard Server without Hyper-V (core installation)"; + break; + case PRODUCT_STANDARD_SERVER_V: + edition = "Standard Server without Hyper-V"; + break; + case PRODUCT_STARTER: + edition = "Starter"; + break; + case PRODUCT_STORAGE_ENTERPRISE_SERVER: + edition = "Enterprise Storage Server"; + break; + case PRODUCT_STORAGE_EXPRESS_SERVER: + edition = "Express Storage Server"; + break; + case PRODUCT_STORAGE_STANDARD_SERVER: + edition = "Standard Storage Server"; + break; + case PRODUCT_STORAGE_WORKGROUP_SERVER: + edition = "Workgroup Storage Server"; + break; + case PRODUCT_UNDEFINED: + edition = "Unknown product"; + break; + case PRODUCT_ULTIMATE: + edition = "Ultimate"; + break; + case PRODUCT_ULTIMATE_N: + edition = "Ultimate N"; + break; + case PRODUCT_WEB_SERVER: + edition = "Web Server"; + break; + case PRODUCT_WEB_SERVER_CORE: + edition = "Web Server (core installation)"; + break; + } + } + } + } + + _sEdition = edition; + return edition; + } + } + + private static string _name; + /// + /// Gets the name of the operating system running on this computer. + /// + public static string Name + { + get + { + if (_name != null) + { + return _name; //***** RETURN *****// + } + + string name = "unknown"; + + OperatingSystem osVersion = Environment.OSVersion; + OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); + if (GetVersionEx(ref osVersionInfo)) + { + int majorVersion = osVersion.Version.Major; + int minorVersion = osVersion.Version.Minor; + byte productType = osVersionInfo.ProductType; + ushort suiteMask = osVersionInfo.SuiteMask; + switch (osVersion.Platform) + { + case PlatformID.Win32Windows: + if (majorVersion == 4) + { + string csdVersion = osVersionInfo.ServicePackVersion; + switch (minorVersion) + { + case 0: + if (csdVersion == "B" || csdVersion == "C") + { + name = "Windows 95 OSR2"; + } + else + { + name = "Windows 95"; + } + break; + case 10: + name = csdVersion == "A" ? "Windows 98 Second Edition" : "Windows 98"; + break; + case 90: + name = "Windows Me"; + break; + } + } + break; + case PlatformID.Win32NT: + switch (majorVersion) + { + case 3: + name = "Windows NT 3.51"; + break; + case 4: + switch (productType) + { + case 1: + name = "Windows NT 4.0"; + break; + case 3: + name = "Windows NT 4.0 Server"; + break; + } + break; + case 5: + switch (minorVersion) + { + case 0: + name = "Windows 2000"; + break; + case 1: + name = suiteMask switch + { + 0x0200 => "Windows XP Professional", + _ => "Windows XP" + }; + break; + case 2: + name = suiteMask switch + { + 0x0200 => "Windows XP Professional x64", + 0x0002 => "Windows Server 2003 Enterprise", + 0x0080 => "Windows Server 2003 Data Center", + 0x0400 => "Windows Server 2003 Web Edition", + 0x8000 => "Windows Home Server", + _ => "Windows Server 2003" + }; + break; + } + break; + case 6: + switch (minorVersion) + { + case 0: + name = productType switch + { + 3 => "Windows Server 2008", + _ => "Windows Vista" + }; + break; + case 1: + name = productType switch + { + 3 => "Windows Server 2008 R2", + _ => "Windows 7" + }; + break; + case 2: + name = "Windows 8"; + break; + case 3: + name = "Windows 8.1"; + break; + } + break; + case 10: + string releaseId = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ReleaseId", "").ToString(); + name = $"Windows 10 {releaseId}"; + break; + } + break; + } + } + + _name = name; + return name; + } + } + + [DllImport("Kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GetProductInfo( + int osMajorVersion, + int osMinorVersion, + int spMajorVersion, + int spMinorVersion, + out int edition); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetVersionEx(ref OSVERSIONINFOEX osVersionInfo); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private unsafe struct OSVERSIONINFOEX + { + /// + /// The size of this data structure, in bytes. Set this member to sizeof(OSVERSIONINFOEX). + /// + private int _dwOSVersionInfoSize; + + private readonly int _dwMajorVersion; + private readonly int _dwMinorVersion; + private readonly int _dwBuildNumber; + private readonly int _dwPlatformId; + private fixed char _szCSDVersion[128]; + private readonly short _wServicePackMajor; + private readonly short _wServicePackMinor; + private readonly ushort _wSuiteMask; + private readonly byte _wProductType; + private readonly byte _wReserved; + + /// + /// The major version number of the operating system. + /// + public int MajorVersion => _dwMajorVersion; + + /// + /// The minor version number of the operating system. + /// + public int MinorVersion => _dwMinorVersion; + + /// + /// The build number of the operating system. + /// + public int BuildNumber => _dwBuildNumber; + + /// + /// The operating system platform. This member can be VER_PLATFORM_WIN32_NT (2). + /// + public int PlatformId => _dwPlatformId; + + /// + /// A null-terminated string, such as "Service Pack 3", that indicates the latest Service Pack installed on the system. + /// If no Service Pack has been installed, the string is empty. + /// + public string ServicePackVersion + { + get + { + fixed (char* servicePackVersion = _szCSDVersion) + { + return new string(servicePackVersion); + } + + } + } + + /// + /// The major version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the + /// major version number is 3. + /// If no Service Pack has been installed, the value is zero. + /// + public short ServicePackMajor => _wServicePackMajor; + + /// + /// The minor version number of the latest Service Pack installed on the system. For example, for Service Pack 3, the + /// minor version number is 0. + /// + public short ServicePackMinor => _wServicePackMinor; + + /// + /// A bit mask that identifies the product suites available on the system. This member can be a combination of the + /// following values. + /// + public ushort SuiteMask => _wSuiteMask; + + /// + /// Any additional information about the system. + /// + public byte ProductType => _wProductType; + + /// + /// Factory for an empty OsVersionInfoEx + /// + /// OSVERSIONINFOEX + public static OSVERSIONINFOEX Create() + { + return new OSVERSIONINFOEX + { + _dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)) + }; + } + } + + private const int PRODUCT_UNDEFINED = 0x00000000; + private const int PRODUCT_ULTIMATE = 0x00000001; + private const int PRODUCT_HOME_BASIC = 0x00000002; + private const int PRODUCT_HOME_PREMIUM = 0x00000003; + private const int PRODUCT_ENTERPRISE = 0x00000004; + private const int PRODUCT_HOME_BASIC_N = 0x00000005; + private const int PRODUCT_BUSINESS = 0x00000006; + private const int PRODUCT_STANDARD_SERVER = 0x00000007; + private const int PRODUCT_DATACENTER_SERVER = 0x00000008; + private const int PRODUCT_SMALLBUSINESS_SERVER = 0x00000009; + private const int PRODUCT_ENTERPRISE_SERVER = 0x0000000A; + private const int PRODUCT_STARTER = 0x0000000B; + private const int PRODUCT_DATACENTER_SERVER_CORE = 0x0000000C; + private const int PRODUCT_STANDARD_SERVER_CORE = 0x0000000D; + private const int PRODUCT_ENTERPRISE_SERVER_CORE = 0x0000000E; + private const int PRODUCT_ENTERPRISE_SERVER_IA64 = 0x0000000F; + private const int PRODUCT_BUSINESS_N = 0x00000010; + private const int PRODUCT_WEB_SERVER = 0x00000011; + private const int PRODUCT_CLUSTER_SERVER = 0x00000012; + private const int PRODUCT_STORAGE_EXPRESS_SERVER = 0x00000014; + private const int PRODUCT_STORAGE_STANDARD_SERVER = 0x00000015; + private const int PRODUCT_STORAGE_WORKGROUP_SERVER = 0x00000016; + private const int PRODUCT_STORAGE_ENTERPRISE_SERVER = 0x00000017; + private const int PRODUCT_SERVER_FOR_SMALLBUSINESS = 0x00000018; + private const int PRODUCT_HOME_PREMIUM_N = 0x0000001A; + private const int PRODUCT_ENTERPRISE_N = 0x0000001B; + private const int PRODUCT_ULTIMATE_N = 0x0000001C; + private const int PRODUCT_WEB_SERVER_CORE = 0x0000001D; + private const int PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E; + private const int PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F; + private const int PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020; + private const int PRODUCT_SERVER_FOR_SMALLBUSINESS_V = 0x00000023; + private const int PRODUCT_STANDARD_SERVER_V = 0x00000024; + private const int PRODUCT_ENTERPRISE_SERVER_V = 0x00000026; + private const int PRODUCT_STANDARD_SERVER_CORE_V = 0x00000028; + private const int PRODUCT_ENTERPRISE_SERVER_CORE_V = 0x00000029; + private const int PRODUCT_HYPERV = 0x0000002A; + + private const int VER_NT_WORKSTATION = 1; + private const int VER_NT_SERVER = 3; + private const int VER_SUITE_ENTERPRISE = 2; + private const int VER_SUITE_DATACENTER = 128; + private const int VER_SUITE_PERSONAL = 512; + private const int VER_SUITE_BLADE = 1024; + + /// + /// Gets the service pack information of the operating system running on this computer. + /// + public static string ServicePack + { + get + { + string servicePack = string.Empty; + OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create(); + + if (GetVersionEx(ref osVersionInfo)) + { + servicePack = osVersionInfo.ServicePackVersion; + } + + return servicePack; + } + } + + /// + /// Gets the build version number of the operating system running on this computer. + /// + public static int BuildVersion => Environment.OSVersion.Version.Build; + + /// + /// Gets the full version string of the operating system running on this computer. + /// + public static string VersionString + { + get + { + if (WindowsVersion.IsWindows10OrLater) + { + return $"build {Environment.OSVersion.Version.Build}"; + } + if (Environment.OSVersion.Version.Revision != 0) + { + return $"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor} build {Environment.OSVersion.Version.Build} revision {Environment.OSVersion.Version.Revision:X}"; + } + return $"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor} build {Environment.OSVersion.Version.Build}"; + } + } + + /// + /// Gets the full version of the operating system running on this computer. + /// + public static Version Version + { + get + { + return Environment.OSVersion.Version; + } + } + + /// + /// Gets the major version number of the operating system running on this computer. + /// + public static int MajorVersion + { + get + { + return Environment.OSVersion.Version.Major; + } + } + + /// + /// Gets the minor version number of the operating system running on this computer. + /// + public static int MinorVersion + { + get + { + return Environment.OSVersion.Version.Minor; + } + } + + /// + /// Gets the revision version number of the operating system running on this computer. + /// + public static int RevisionVersion + { + get + { + return Environment.OSVersion.Version.Revision; + } + } + } } \ No newline at end of file diff --git a/GreenshotPlugin/Core/OAuth/LocalJsonReceiver.cs b/GreenshotPlugin/Core/OAuth/LocalJsonReceiver.cs new file mode 100644 index 000000000..7b363b2fa --- /dev/null +++ b/GreenshotPlugin/Core/OAuth/LocalJsonReceiver.cs @@ -0,0 +1,191 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using log4net; +using Newtonsoft.Json; + +namespace GreenshotPlugin.Core.OAuth +{ + /// + /// OAuth 2.0 verification code receiver that runs a local server on a free port + /// and waits for a call with the authorization verification code. + /// + public class LocalJsonReceiver + { + private static readonly ILog Log = LogManager.GetLogger(typeof(LocalJsonReceiver)); + private readonly ManualResetEvent _ready = new ManualResetEvent(true); + private IDictionary _returnValues; + + /// + /// The url format for the website to post to. Expects one port parameter. + /// Default: http://localhost:{0}/authorize/ + /// + public string ListeningUrlFormat { get; set; } = "http://localhost:{0}/authorize/"; + + private string _listeningUri; + /// + /// The URL where the server is listening + /// + public string ListeningUri { + get { + if (string.IsNullOrEmpty(_listeningUri)) + { + _listeningUri = string.Format(ListeningUrlFormat, GetRandomUnusedPort()); + } + return _listeningUri; + } + set => _listeningUri = value; + } + + /// + /// This action is called when the URI must be opened, default is just to run Process.Start + /// + public Action OpenUriAction + { + set; + get; + } = authorizationUrl => + { + Log.DebugFormat("Open a browser with: {0}", authorizationUrl); + using var process = Process.Start(authorizationUrl); + }; + + /// + /// Timeout for waiting for the website to respond + /// + public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(4); + + /// + /// The OAuth code receiver + /// + /// OAuth2Settings + /// Dictionary with values + public IDictionary ReceiveCode(OAuth2Settings oauth2Settings) { + using var listener = new HttpListener(); + // Make sure the port is stored in the state, so the website can process this. + oauth2Settings.State = new Uri(ListeningUri).Port.ToString(); + listener.Prefixes.Add(ListeningUri); + try { + listener.Start(); + _ready.Reset(); + + listener.BeginGetContext(ListenerCallback, listener); + OpenUriAction(oauth2Settings.FormattedAuthUrl); + _ready.WaitOne(Timeout, true); + } catch (Exception) { + // Make sure we can clean up, also if the thead is aborted + _ready.Set(); + throw; + } finally { + listener.Close(); + } + + return _returnValues; + } + + /// + /// Handle a connection async, this allows us to break the waiting + /// + /// IAsyncResult + private void ListenerCallback(IAsyncResult result) { + HttpListener listener = (HttpListener)result.AsyncState; + + //If not listening return immediately as this method is called one last time after Close() + if (!listener.IsListening) { + return; + } + + // Use EndGetContext to complete the asynchronous operation. + HttpListenerContext context = listener.EndGetContext(result); + + // Handle request + HttpListenerRequest request = context.Request; + + if (request.HasEntityBody) + { + // Process the body + using var body = request.InputStream; + using var reader = new StreamReader(body, request.ContentEncoding); + using var jsonTextReader = new JsonTextReader(reader); + var serializer = new JsonSerializer(); + _returnValues = serializer.Deserialize>(jsonTextReader); + } + + // Create the response. + using (HttpListenerResponse response = context.Response) + { + if (request.HttpMethod == "OPTIONS") + { + response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); + response.AddHeader("Access-Control-Allow-Methods", "POST"); + response.AddHeader("Access-Control-Max-Age", "1728000"); + } + + response.AppendHeader("Access-Control-Allow-Origin", "*"); + if (request.HasEntityBody) + { + response.ContentType = "application/json"; + // currently only return the version, more can be added later + string jsonContent = "{\"version\": \"" + EnvironmentInfo.GetGreenshotVersion(true) + "\"}"; + + // Write a "close" response. + byte[] buffer = Encoding.UTF8.GetBytes(jsonContent); + // Write to response stream. + response.ContentLength64 = buffer.Length; + using var stream = response.OutputStream; + stream.Write(buffer, 0, buffer.Length); + } + } + + if (_returnValues != null) + { + _ready.Set(); + } + else + { + // Make sure the next request is processed + listener.BeginGetContext(ListenerCallback, listener); + } + } + + /// + /// Returns a random, unused port. + /// + /// port to use + private static int GetRandomUnusedPort() { + var listener = new TcpListener(IPAddress.Loopback, 0); + try { + listener.Start(); + return ((IPEndPoint)listener.LocalEndpoint).Port; + } finally { + listener.Stop(); + } + } + } +} \ No newline at end of file diff --git a/GreenshotPlugin/Core/OAuth/OAuth2AuthorizeMode.cs b/GreenshotPlugin/Core/OAuth/OAuth2AuthorizeMode.cs index b5eee19b3..98d4515f9 100644 --- a/GreenshotPlugin/Core/OAuth/OAuth2AuthorizeMode.cs +++ b/GreenshotPlugin/Core/OAuth/OAuth2AuthorizeMode.cs @@ -27,8 +27,7 @@ namespace GreenshotPlugin.Core.OAuth public enum OAuth2AuthorizeMode { Unknown, // Will give an exception, caller needs to specify another value LocalServer, // Will specify a redirect URL to http://localhost:port/authorize, while having a HttpListener - MonitorTitle, // Will monitor for title changes, the title needs the status and query params - Pin, // Not implemented yet: Will ask the user to enter the shown PIN + JsonReceiver, // Will start a local HttpListener and wait for a Json post EmbeddedBrowser // Will open into an embedded _browser (OAuthLoginForm), and catch the redirect } } \ No newline at end of file diff --git a/GreenshotPlugin/Core/OAuth/OAuth2Helper.cs b/GreenshotPlugin/Core/OAuth/OAuth2Helper.cs index 629a764f8..2189f09a1 100644 --- a/GreenshotPlugin/Core/OAuth/OAuth2Helper.cs +++ b/GreenshotPlugin/Core/OAuth/OAuth2Helper.cs @@ -21,12 +21,9 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Net; -using System.Windows.Forms; using GreenshotPlugin.Controls; -using GreenshotPlugin.Hooking; namespace GreenshotPlugin.Core.OAuth { /// @@ -134,7 +131,7 @@ namespace GreenshotPlugin.Core.OAuth { /// /// Go out and retrieve a new access token via refresh-token with the TokenUrl in the settings - /// Will upate the access token, refresh token, expire date + /// Will update the access token, refresh token, expire date /// /// public static void GenerateAccessToken(OAuth2Settings settings) { @@ -159,22 +156,23 @@ namespace GreenshotPlugin.Core.OAuth { // "token_type":"Bearer", IDictionary accessTokenResult = JSONHelper.JsonDecode(accessTokenJsonResult); - if (accessTokenResult.ContainsKey("error")) { - if ("invalid_grant" == (string)accessTokenResult["error"]) { + if (accessTokenResult.ContainsKey("error")) + { + if ("invalid_grant" == (string)accessTokenResult["error"]) { // Refresh token has also expired, we need a new one! settings.RefreshToken = null; settings.AccessToken = null; settings.AccessTokenExpires = DateTimeOffset.MinValue; settings.Code = null; return; - } else { - if (accessTokenResult.ContainsKey("error_description")) { - throw new Exception($"{accessTokenResult["error"]} - {accessTokenResult["error_description"]}"); - } else { - throw new Exception((string)accessTokenResult["error"]); - } } - } + + if (accessTokenResult.ContainsKey("error_description")) { + throw new Exception($"{accessTokenResult["error"]} - {accessTokenResult["error_description"]}"); + } + + throw new Exception((string)accessTokenResult["error"]); + } if (accessTokenResult.ContainsKey(AccessToken)) { @@ -205,76 +203,62 @@ namespace GreenshotPlugin.Core.OAuth { { OAuth2AuthorizeMode.LocalServer => AuthenticateViaLocalServer(settings), OAuth2AuthorizeMode.EmbeddedBrowser => AuthenticateViaEmbeddedBrowser(settings), - OAuth2AuthorizeMode.MonitorTitle => AuthenticateViaDefaultBrowser(settings), + OAuth2AuthorizeMode.JsonReceiver => AuthenticateViaDefaultBrowser(settings), _ => throw new NotImplementedException($"Authorize mode '{settings.AuthorizeMode}' is not 'yet' implemented."), }; return completed; } /// - /// Authenticate via the default browser, using the browser title. + /// Authenticate via the default browser, via the Greenshot website. + /// It will wait for a Json post. /// If this works, return the code /// /// OAuth2Settings with the Auth / Token url etc /// true if completed, false if canceled private static bool AuthenticateViaDefaultBrowser(OAuth2Settings settings) { - var monitor = new WindowsTitleMonitor(); + var codeReceiver = new LocalJsonReceiver(); + IDictionary result = codeReceiver.ReceiveCode(settings); - string error = null; - var fields = new HashSet(); - int nrOfFields = 100; - - monitor.TitleChangeEvent += args => + foreach (var key in result.Keys) { - if (!args.Title.Contains(settings.State)) + switch (key) { - return; + case AccessToken: + settings.AccessToken = result[key]; + break; + case ExpiresIn: + if (int.TryParse(result[key], out var seconds)) + { + settings.AccessTokenExpires = DateTimeOffset.Now.AddSeconds(seconds); + } + break; + case RefreshToken: + settings.RefreshToken = result[key]; + break; } + } - var title = args.Title; - title = title.Substring(0,title.IndexOf(' ')); - - var parameters = NetworkHelper.ParseQueryString(title); - - if (parameters.ContainsKey("nr")) + if (result.TryGetValue("error", out var error)) + { + if (result.TryGetValue("error_description", out var errorDescription)) { - nrOfFields = int.Parse(parameters["nr"]); + throw new Exception(errorDescription); } + if ("access_denied" == error) + { + throw new UnauthorizedAccessException("Access denied"); + } + throw new Exception(error); + } + if (result.TryGetValue(Code, out var code) && !string.IsNullOrEmpty(code)) + { + settings.Code = code; + GenerateRefreshToken(settings); + } - foreach (var key in parameters.Keys) - { - fields.Add(key); - switch (key) - { - case AccessToken: - settings.AccessToken = parameters[key]; - break; - case ExpiresIn: - if (int.TryParse(parameters[key], out var seconds)) - { - settings.AccessTokenExpires = DateTimeOffset.Now.AddSeconds(seconds); - } - break; - case RefreshToken: - settings.RefreshToken = parameters[key]; - break; - case Error: - error = parameters[key]; - break; - } - } - }; - using (var process = Process.Start(settings.FormattedAuthUrl)) - { - while (nrOfFields > fields.Count) - { - // Have the thread process Forms events - Application.DoEvents(); - } - }; - monitor.Dispose(); - return error == null; + return true; } ///