Get rid of embedded browser (#255)

This change makes it possible to use Box, DropBox and Imgur with the default browser, instead of the embedded which causes many issues. Other plugins need to follow.
This commit is contained in:
Robin Krom 2021-03-27 00:11:06 +01:00 committed by GitHub
parent ecb1b91ae7
commit 19fb98ae55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
71 changed files with 4037 additions and 3542 deletions

View file

@ -65,17 +65,17 @@
</ItemGroup>
<ItemGroup>
<Tokens Include="Box_ClientId">
<ReplacementValue>$(Box_ClientId)</ReplacementValue>
<Tokens Include="Box13_ClientId">
<ReplacementValue>$(Box13_ClientId)</ReplacementValue>
</Tokens>
<Tokens Include="Box_ClientSecret">
<ReplacementValue>$(Box_ClientSecret)</ReplacementValue>
<Tokens Include="Box13_ClientSecret">
<ReplacementValue>$(Box13_ClientSecret)</ReplacementValue>
</Tokens>
<Tokens Include="DropBox_ClientId">
<ReplacementValue>$(DropBox_ClientId)</ReplacementValue>
<Tokens Include="DropBox13_ClientId">
<ReplacementValue>$(DropBox13_ClientId)</ReplacementValue>
</Tokens>
<Tokens Include="DropBox_ClientSecret">
<ReplacementValue>$(DropBox_ClientSecret)</ReplacementValue>
<Tokens Include="DropBox13_ClientSecret">
<ReplacementValue>$(DropBox13_ClientSecret)</ReplacementValue>
</Tokens>
<Tokens Include="Flickr_ClientId">
<ReplacementValue>$(Flickr_ClientId)</ReplacementValue>
@ -83,11 +83,11 @@
<Tokens Include="Flickr_ClientSecret">
<ReplacementValue>$(Flickr_ClientSecret)</ReplacementValue>
</Tokens>
<Tokens Include="Imgur_ClientId">
<ReplacementValue>$(Imgur_ClientId)</ReplacementValue>
<Tokens Include="Imgur13_ClientId">
<ReplacementValue>$(Imgur13_ClientId)</ReplacementValue>
</Tokens>
<Tokens Include="Imgur_ClientSecret">
<ReplacementValue>$(Imgur_ClientSecret)</ReplacementValue>
<Tokens Include="Imgur13_ClientSecret">
<ReplacementValue>$(Imgur13_ClientSecret)</ReplacementValue>
</Tokens>
<Tokens Include="Photobucket_ClientId">
<ReplacementValue>$(Photobucket_ClientId)</ReplacementValue>

View file

@ -34,7 +34,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GreenshotDropboxPlugin", "G
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GreenshotFlickrPlugin", "GreenshotFlickrPlugin\GreenshotFlickrPlugin.csproj", "{7EC72A5A-D73A-4B4B-9CA1-2216C7D92D5E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GreenshotPicasaPlugin", "GreenshotPicasaPlugin\GreenshotPicasaPlugin.csproj", "{1893A2E4-A78A-4713-A8E7-E70058DABEE0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GreenshotGooglePhotosPlugin", "GreenshotGooglePhotosPlugin\GreenshotGooglePhotosPlugin.csproj", "{1893A2E4-A78A-4713-A8E7-E70058DABEE0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GreenshotOfficePlugin", "GreenshotOfficePlugin\GreenshotOfficePlugin.csproj", "{92599C09-FF29-4ABD-B6E6-C48ECD781BAB}"
EndProject

View file

@ -105,10 +105,6 @@ namespace Greenshot.Drawing {
cursor.DrawStretched(graphics, Bounds);
}
public override Size DefaultSize {
get {
return cursor.Size;
}
}
public override Size DefaultSize => cursor?.Size ?? new Size(16, 16);
}
}

View file

@ -548,23 +548,11 @@ namespace Greenshot.Drawing
return ScaleHelper.ShapeAngleRoundBehavior.Instance;
}
public virtual bool HasContextMenu {
get {
return true;
}
}
public virtual bool HasContextMenu => true;
public virtual bool HasDefaultSize {
get {
return false;
}
}
public virtual bool HasDefaultSize => false;
public virtual Size DefaultSize {
get {
throw new NotSupportedException("Object doesn't have a default size");
}
}
public virtual Size DefaultSize => throw new NotSupportedException("Object doesn't have a default size");
/// <summary>
/// Allows to override the initializing of the fields, so we can actually have our own defaults

View file

@ -62,7 +62,7 @@ namespace Greenshot.Drawing {
Width = value.Width;
Height = value.Height;
}
get { return icon; }
get => icon;
}
/**
@ -78,27 +78,32 @@ namespace Greenshot.Drawing {
base.Dispose(disposing);
}
public void Load(string filename) {
if (File.Exists(filename))
public void Load(string filename)
{
if (!File.Exists(filename))
{
return;
}
using Icon fileIcon = new Icon(filename);
Icon = fileIcon;
Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width);
}
}
public override void Draw(Graphics graphics, RenderMode rm) {
if (icon != null) {
public override void Draw(Graphics graphics, RenderMode rm)
{
if (icon == null)
{
return;
}
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
graphics.CompositingQuality = CompositingQuality.Default;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawIcon(icon, Bounds);
}
}
public override bool HasDefaultSize => true;
public override Size DefaultSize => icon.Size;
public override Size DefaultSize => icon?.Size ?? new Size(16,16);
}
}

View file

@ -77,13 +77,16 @@ namespace Greenshot.Drawing {
AddField(GetType(), FieldType.SHADOW, false);
}
protected void BitmapContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) {
if (sender.Equals(this)) {
protected void BitmapContainer_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
if (!sender.Equals(this))
{
return;
}
if (FieldType.SHADOW.Equals(e.Field.FieldType)) {
ChangeShadowField();
}
}
}
public void ChangeShadowField() {
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
@ -189,12 +192,14 @@ namespace Greenshot.Drawing {
/// This checks if a shadow is already generated
/// </summary>
/// <param name="shadow"></param>
private void CheckShadow(bool shadow) {
if (shadow && _shadowBitmap == null)
private void CheckShadow(bool shadow)
{
if (!shadow || _shadowBitmap != null)
{
return;
}
using var matrix = new Matrix();
_shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), matrix);
}
}
/// <summary>
@ -202,8 +207,12 @@ namespace Greenshot.Drawing {
/// </summary>
/// <param name="graphics"></param>
/// <param name="rm"></param>
public override void Draw(Graphics graphics, RenderMode rm) {
if (image != null) {
public override void Draw(Graphics graphics, RenderMode rm)
{
if (image == null)
{
return;
}
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
@ -217,10 +226,9 @@ namespace Greenshot.Drawing {
graphics.DrawImage(image, Bounds);
}
}
}
public override bool HasDefaultSize => true;
public override Size DefaultSize => image.Size;
public override Size DefaultSize => image?.Size ?? new Size(32, 32);
}
}

View file

@ -21,6 +21,7 @@
using System.Windows.Forms;
using Greenshot.Helpers;
using GreenshotPlugin.Core;
namespace Greenshot.Forms {
partial class AboutForm {

View file

@ -1,786 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 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 <http://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// Description of EnvironmentInfo.
/// </summary>
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<AssemblyFileVersionAttribute>();
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<AssemblyInformationalVersionAttribute>();
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 externalException)
{
// e.g. COMException
report.AppendLine().AppendLine("ErrorCode: 0x" + externalException.ErrorCode.ToString("X"));
}
report.AppendLine().AppendLine("Stack:").AppendLine(ex.StackTrace);
if (ex is ReflectionTypeLoadException reflectionTypeLoadException)
{
report.AppendLine().AppendLine("LoaderExceptions: ");
foreach (Exception cbE in 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();
}
}
/// <summary>
/// Provides detailed information about the host operating system.
/// Code is available at: http://www.csharp411.com/determine-windows-version-and-edition-with-c/
/// </summary>
public static class OsInfo
{
/// <summary>
/// Determines if the current application is 32 or 64-bit.
/// </summary>
public static int Bits => IntPtr.Size * 8;
private static string _sEdition;
/// <summary>
/// Gets the edition of the operating system running on this computer.
/// </summary>
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;
/// <summary>
/// Gets the name of the operating system running on this computer.
/// </summary>
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
{
/// <summary>
/// The size of this data structure, in bytes. Set this member to sizeof(OSVERSIONINFOEX).
/// </summary>
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;
/// <summary>
/// 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.
/// </summary>
public string ServicePackVersion
{
get
{
fixed (char* servicePackVersion = _szCSDVersion)
{
return new string(servicePackVersion);
}
}
}
/// <summary>
/// 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.
/// </summary>
public short ServicePackMajor => _wServicePackMajor;
/// <summary>
/// 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.
/// </summary>
public short ServicePackMinor => _wServicePackMinor;
/// <summary>
/// A bit mask that identifies the product suites available on the system. This member can be a combination of the
/// following values.
/// </summary>
public ushort SuiteMask => _wSuiteMask;
/// <summary>
/// Any additional information about the system.
/// </summary>
public byte ProductType => _wProductType;
/// <summary>
/// Factory for an empty OsVersionInfoEx
/// </summary>
/// <returns>OSVERSIONINFOEX</returns>
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;
/// <summary>
/// Gets the service pack information of the operating system running on this computer.
/// </summary>
public static string ServicePack
{
get
{
string servicePack = string.Empty;
OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create();
if (GetVersionEx(ref osVersionInfo))
{
servicePack = osVersionInfo.ServicePackVersion;
}
return servicePack;
}
}
/// <summary>
/// Gets the full version string of the operating system running on this computer.
/// </summary>
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}";
}
}
}
}

View file

@ -99,8 +99,8 @@ Source: {#BaseDir}\GreenshotFlickrPlugin\Languages\language_flickr*.xml; DestDir
Source: {#BaseDir}\GreenshotPhotobucketPlugin\{#BinDir}\GreenshotPhotobucketPlugin.dll; DestDir: {app}\Plugins\GreenshotPhotobucketPlugin; Components: plugins\photobucket; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\GreenshotPhotobucketPlugin\Languages\language_photo*.xml; DestDir: {app}\Languages\Plugins\GreenshotPhotobucketPlugin; Components: plugins\photobucket; Flags: overwritereadonly ignoreversion replacesameversion;
;Picasa Plugin
Source: {#BaseDir}\GreenshotPicasaPlugin\{#BinDir}\GreenshotPicasaPlugin.dll; DestDir: {app}\Plugins\GreenshotPicasaPlugin; Components: plugins\picasa; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\GreenshotPicasaPlugin\Languages\language_picasa*.xml; DestDir: {app}\Languages\Plugins\GreenshotPicasaPlugin; Components: plugins\picasa; Flags: overwritereadonly ignoreversion replacesameversion;
;Source: {#BaseDir}\GreenshotPicasaPlugin\{#BinDir}\GreenshotPicasaPlugin.dll; DestDir: {app}\Plugins\GreenshotPicasaPlugin; Components: plugins\picasa; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
;Source: {#BaseDir}\GreenshotPicasaPlugin\Languages\language_picasa*.xml; DestDir: {app}\Languages\Plugins\GreenshotPicasaPlugin; Components: plugins\picasa; Flags: overwritereadonly ignoreversion replacesameversion;
;Confluence Plugin
Source: {#BaseDir}\GreenshotConfluencePlugin\{#BinDir}\GreenshotConfluencePlugin.dll; DestDir: {app}\Plugins\GreenshotConfluencePlugin; Components: plugins\confluence; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion;
Source: {#BaseDir}\GreenshotConfluencePlugin\Languages\language_confluence*.xml; DestDir: {app}\Languages\Plugins\GreenshotConfluencePlugin; Components: plugins\confluence; Flags: overwritereadonly ignoreversion replacesameversion;
@ -491,7 +491,7 @@ Name: "plugins\imgur"; Description: {cm:imgur}; Types: default full custom; Flag
Name: "plugins\jira"; Description: {cm:jira}; Types: full custom; Flags: disablenouninstallwarning
Name: "plugins\office"; Description: {cm:office}; Types: default full custom; Flags: disablenouninstallwarning
Name: "plugins\photobucket"; Description: {cm:photobucket}; Types: full custom; Flags: disablenouninstallwarning
Name: "plugins\picasa"; Description: {cm:picasa}; Types: full custom; Flags: disablenouninstallwarning
;Name: "plugins\picasa"; Description: {cm:picasa}; Types: full custom; Flags: disablenouninstallwarning
Name: "plugins\win10"; Description: {cm:win10}; Types: default full custom; Flags: disablenouninstallwarning; Check: IsWindows10OrNewer()
Name: "languages"; Description: {cm:language}; Types: full custom; Flags: disablenouninstallwarning
Name: "languages\arSY"; Description: {cm:arSY}; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('d')

View file

@ -30,17 +30,9 @@ namespace GreenshotBoxPlugin {
_plugin = plugin;
}
public override string Designation {
get {
return "Box";
}
}
public override string Designation => "Box";
public override string Description {
get {
return Language.GetString("box", LangKey.upload_menu_item);
}
}
public override string Description => Language.GetString("box", LangKey.upload_menu_item);
public override Image DisplayIcon {
get {

View file

@ -21,10 +21,10 @@
using GreenshotPlugin.Core;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
using GreenshotPlugin.Core.OAuth;
using GreenshotPlugin.IniFile;
namespace GreenshotBoxPlugin {
@ -73,9 +73,8 @@ namespace GreenshotBoxPlugin {
CloudServiceName = "Box",
ClientId = BoxCredentials.ClientId,
ClientSecret = BoxCredentials.ClientSecret,
RedirectUrl = "https://www.box.com/home/",
BrowserSize = new Size(1060, 600),
AuthorizeMode = OAuth2AuthorizeMode.EmbeddedBrowser,
RedirectUrl = "https://getgreenshot.org/authorize/box",
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = Config.RefreshToken,
AccessToken = Config.AccessToken,
AccessTokenExpires = Config.AccessTokenExpires

View file

@ -25,7 +25,7 @@ namespace GreenshotBoxPlugin {
/// You can set your own values here
/// </summary>
public static class BoxCredentials {
public static string ClientId = "${Box_ClientId}";
public static string ClientSecret = "${Box_ClientSecret}";
public static string ClientId = "${Box13_ClientId}";
public static string ClientSecret = "${Box13_ClientSecret}";
}
}

View file

@ -21,7 +21,6 @@
using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
@ -45,14 +44,13 @@ namespace GreenshotDropboxPlugin {
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
if (disposing) {
if (_itemPlugInConfig != null) {
private void Dispose(bool disposing)
{
if (!disposing) return;
if (_itemPlugInConfig == null) return;
_itemPlugInConfig.Dispose();
_itemPlugInConfig = null;
}
}
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
@ -102,20 +100,16 @@ namespace GreenshotDropboxPlugin {
public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) {
uploadUrl = null;
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false);
try {
string dropboxUrl = null;
try
{
bool result = false;
new PleaseWaitForm().ShowAndWait("Dropbox", Language.GetString("dropbox", LangKey.communication_wait),
delegate
{
string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
dropboxUrl = DropboxUtils.UploadToDropbox(surfaceToUpload, outputSettings, filename);
result = DropboxUtils.UploadToDropbox(surfaceToUpload, outputSettings, captureDetails);
}
);
if (dropboxUrl == null) {
return false;
}
uploadUrl = dropboxUrl;
return true;
return result;
} catch (Exception e) {
Log.Error(e);
MessageBox.Show(Language.GetString("dropbox", LangKey.upload_failure) + " " + e.Message);

View file

@ -18,6 +18,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Windows.Forms;
using GreenshotDropboxPlugin.Forms;
using GreenshotPlugin.Core;
@ -39,10 +41,18 @@ namespace GreenshotDropboxPlugin {
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Dropbox link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("DropboxToken", Description = "The Dropbox token", Encrypted = true, ExcludeIfNull = true)]
public string DropboxToken { get; set; }
[IniProperty("DropboxTokenSecret", Description = "The Dropbox token secret", Encrypted = true, ExcludeIfNull = true)]
public string DropboxTokenSecret { get; set; }
[IniProperty("RefreshToken", Description = "Dropbox refresh Token", Encrypted = true, ExcludeIfNull = true)]
public string RefreshToken { get; set; }
/// <summary>
/// AccessToken, not stored
/// </summary>
public string AccessToken { get; set; }
/// <summary>
/// AccessTokenExpires, not stored
/// </summary>
public DateTimeOffset AccessTokenExpires { get; set; }
/// <summary>
/// A form for token

View file

@ -20,11 +20,13 @@
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using GreenshotPlugin.Core;
using GreenshotPlugin.Core.OAuth;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using Newtonsoft.Json;
namespace GreenshotDropboxPlugin {
/// <summary>
@ -37,49 +39,54 @@ namespace GreenshotDropboxPlugin {
private DropboxUtils() {
}
public static string UploadToDropbox(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string filename) {
var oAuth = new OAuthSession(DropBoxCredentials.CONSUMER_KEY, DropBoxCredentials.CONSUMER_SECRET)
public static bool UploadToDropbox(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, ICaptureDetails captureDetails)
{
BrowserSize = new Size(1080, 650),
CheckVerifier = false,
AccessTokenUrl = "https://api.dropbox.com/1/oauth/access_token",
AuthorizeUrl = "https://api.dropbox.com/1/oauth/authorize",
RequestTokenUrl = "https://api.dropbox.com/1/oauth/request_token",
LoginTitle = "Dropbox authorization",
Token = DropboxConfig.DropboxToken,
TokenSecret = DropboxConfig.DropboxTokenSecret
var oauth2Settings = new OAuth2Settings
{
AuthUrlPattern = "https://api.dropbox.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}&redirect_uri={RedirectUrl}",
TokenUrl = "https://api.dropbox.com/oauth2/token",
RedirectUrl = "https://getgreenshot.org/authorize/dropbox",
CloudServiceName = "Dropbox",
ClientId = DropBoxCredentials.CONSUMER_KEY,
ClientSecret = DropBoxCredentials.CONSUMER_SECRET,
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = DropboxConfig.RefreshToken,
AccessToken = DropboxConfig.AccessToken,
AccessTokenExpires = DropboxConfig.AccessTokenExpires
};
try
{
string filename = Path.GetFileName(FilenameHelper.GetFilename(DropboxConfig.UploadFormat, captureDetails));
SurfaceContainer image = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
try {
SurfaceContainer imageToUpload = new SurfaceContainer(surfaceToUpload, outputSettings, filename);
string uploadResponse = oAuth.MakeOAuthRequest(HTTPMethod.POST, "https://api-content.dropbox.com/1/files_put/sandbox/" + OAuthSession.UrlEncode3986(filename), null, null, imageToUpload);
Log.DebugFormat("Upload response: {0}", uploadResponse);
} catch (Exception ex) {
IDictionary<string, object> arguments = new Dictionary<string, object>
{
{ "autorename", true },
{ "mute", true },
{ "path", "/" + filename.Replace(Path.DirectorySeparatorChar, '\\')}
};
IDictionary<string, object> headers = new Dictionary<string, object>
{
{ "Dropbox-API-Arg", JsonConvert.SerializeObject(arguments)}
};
var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, "https://content.dropboxapi.com/2/files/upload", oauth2Settings);
NetworkHelper.Post(webRequest, headers, image);
var responseString = NetworkHelper.GetResponseAsString(webRequest);
Log.DebugFormat("Upload response: {0}", responseString);
var response = JsonConvert.DeserializeObject<IDictionary<string, string>>(responseString);
return response.ContainsKey("id");
}
catch (Exception ex) {
Log.Error("Upload error: ", ex);
throw;
} finally {
if (!string.IsNullOrEmpty(oAuth.Token)) {
DropboxConfig.DropboxToken = oAuth.Token;
}
if (!string.IsNullOrEmpty(oAuth.TokenSecret)) {
DropboxConfig.DropboxTokenSecret = oAuth.TokenSecret;
}
}
// Try to get a URL to the uploaded image
try {
string responseString = oAuth.MakeOAuthRequest(HTTPMethod.GET, "https://api.dropbox.com/1/shares/sandbox/" + OAuthSession.UrlEncode3986(filename), null, null, null);
if (responseString != null) {
Log.DebugFormat("Parsing output: {0}", responseString);
IDictionary<string, object> returnValues = JSONHelper.JsonDecode(responseString);
if (returnValues.ContainsKey("url")) {
return returnValues["url"] as string;
}
}
} catch (Exception ex) {
Log.Error("Can't parse response.", ex);
}
return null;
DropboxConfig.RefreshToken = oauth2Settings.RefreshToken;
DropboxConfig.AccessToken = oauth2Settings.AccessToken;
DropboxConfig.AccessTokenExpires = oauth2Settings.AccessTokenExpires;
DropboxConfig.IsDirty = true;
IniConfig.Save();
}
}
}
}

View file

@ -25,7 +25,7 @@ namespace GreenshotDropboxPlugin {
/// You can set your own values here
/// </summary>
public static class DropBoxCredentials {
public static string CONSUMER_KEY = "${DropBox_ClientId}";
public static string CONSUMER_SECRET = "${DropBox_ClientSecret}";
public static string CONSUMER_KEY = "${DropBox13_ClientId}";
public static string CONSUMER_SECRET = "${DropBox13_ClientSecret}";
}
}

View file

@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Xml;
using GreenshotPlugin.Core;
using GreenshotPlugin.Core.OAuth;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;

View file

@ -1,5 +1,5 @@
/*
* A Picasa Plugin for Greenshot
* A GooglePhotos Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
* For more information see: http://getgreenshot.org/
@ -20,7 +20,7 @@
using GreenshotPlugin.Controls;
namespace GreenshotPicasaPlugin.Forms {
public class PicasaForm : GreenshotForm {
namespace GreenshotGooglePhotosPlugin.Forms {
public class GooglePhotosForm : GreenshotForm {
}
}

View file

@ -1,5 +1,5 @@
/*
* A Picasa Plugin for Greenshot
* A GooglePhotos Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
* For more information see: http://getgreenshot.org/
@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GreenshotPicasaPlugin.Forms {
namespace GreenshotGooglePhotosPlugin.Forms {
partial class SettingsForm {
/// <summary>
/// Designer variable used to keep track of non-visual components.
@ -84,13 +84,13 @@ namespace GreenshotPicasaPlugin.Forms {
this.combobox_uploadimageformat.Location = new System.Drawing.Point(197, 12);
this.combobox_uploadimageformat.Name = "combobox_uploadimageformat";
this.combobox_uploadimageformat.PropertyName = "UploadFormat";
this.combobox_uploadimageformat.SectionName = "Picasa";
this.combobox_uploadimageformat.SectionName = "GooglePhotos";
this.combobox_uploadimageformat.Size = new System.Drawing.Size(225, 21);
this.combobox_uploadimageformat.TabIndex = 1;
//
// label_upload_format
//
this.label_upload_format.LanguageKey = "picasa.label_upload_format";
this.label_upload_format.LanguageKey = "googlephotos.label_upload_format";
this.label_upload_format.Location = new System.Drawing.Point(10, 18);
this.label_upload_format.Name = "label_upload_format";
this.label_upload_format.Size = new System.Drawing.Size(181, 33);
@ -99,7 +99,7 @@ namespace GreenshotPicasaPlugin.Forms {
// label_AfterUpload
//
this.label_AfterUpload.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.label_AfterUpload.LanguageKey = "picasa.label_AfterUpload";
this.label_AfterUpload.LanguageKey = "googlephotos.label_AfterUpload";
this.label_AfterUpload.Location = new System.Drawing.Point(10, 51);
this.label_AfterUpload.Name = "label_AfterUpload";
this.label_AfterUpload.Size = new System.Drawing.Size(181, 29);
@ -108,11 +108,11 @@ namespace GreenshotPicasaPlugin.Forms {
// checkboxAfterUploadLinkToClipBoard
//
this.checkboxAfterUploadLinkToClipBoard.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "picasa.label_AfterUploadLinkToClipBoard";
this.checkboxAfterUploadLinkToClipBoard.LanguageKey = "googlephotos.label_AfterUploadLinkToClipBoard";
this.checkboxAfterUploadLinkToClipBoard.Location = new System.Drawing.Point(197, 50);
this.checkboxAfterUploadLinkToClipBoard.Name = "checkboxAfterUploadLinkToClipBoard";
this.checkboxAfterUploadLinkToClipBoard.PropertyName = "AfterUploadLinkToClipBoard";
this.checkboxAfterUploadLinkToClipBoard.SectionName = "Picasa";
this.checkboxAfterUploadLinkToClipBoard.SectionName = "GooglePhotos";
this.checkboxAfterUploadLinkToClipBoard.Size = new System.Drawing.Size(104, 17);
this.checkboxAfterUploadLinkToClipBoard.TabIndex = 2;
this.checkboxAfterUploadLinkToClipBoard.UseVisualStyleBackColor = true;
@ -129,7 +129,7 @@ namespace GreenshotPicasaPlugin.Forms {
this.Controls.Add(this.buttonCancel);
this.Controls.Add(this.buttonOK);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.LanguageKey = "picasa.settings_title";
this.LanguageKey = "googlephotos.settings_title";
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "SettingsForm";

View file

@ -1,5 +1,5 @@
/*
* A Picasa Plugin for Greenshot
* A GooglePhotos Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
* For more information see: http://getgreenshot.org/
@ -18,11 +18,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GreenshotPicasaPlugin.Forms {
namespace GreenshotGooglePhotosPlugin.Forms {
/// <summary>
/// Description of PasswordRequestForm.
/// </summary>
public partial class SettingsForm : PicasaForm {
public partial class SettingsForm : GooglePhotosForm {
public SettingsForm()
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -1,5 +1,5 @@
/*
* A Picasa Plugin for Greenshot
* A GooglePhotos Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
* For more information see: http://getgreenshot.org/
@ -20,42 +20,42 @@
using System.Windows.Forms;
using GreenshotPlugin.Core;
using System;
using GreenshotPicasaPlugin.Forms;
using GreenshotGooglePhotosPlugin.Forms;
using GreenshotPlugin.IniFile;
namespace GreenshotPicasaPlugin {
namespace GreenshotGooglePhotosPlugin {
/// <summary>
/// Description of PicasaConfiguration.
/// Description of GooglePhotosConfiguration.
/// </summary>
[IniSection("Picasa", Description = "Greenshot Picasa Plugin configuration")]
public class PicasaConfiguration : IniSection {
[IniSection("GooglePhotos", Description = "Greenshot GooglePhotos Plugin configuration")]
public class GooglePhotosConfiguration : IniSection {
[IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")]
public OutputFormat UploadFormat { get; set; }
[IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")]
public int UploadJpegQuality { get; set; }
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Picasa link to clipboard.", DefaultValue = "true")]
[IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send GooglePhotos link to clipboard.", DefaultValue = "true")]
public bool AfterUploadLinkToClipBoard { get; set; }
[IniProperty("AddFilename", Description = "Is the filename passed on to Picasa", DefaultValue = "False")]
[IniProperty("AddFilename", Description = "Is the filename passed on to GooglePhotos", DefaultValue = "False")]
public bool AddFilename {
get;
set;
}
[IniProperty("UploadUser", Description = "The Picasa user to upload to", DefaultValue = "default")]
[IniProperty("UploadUser", Description = "The GooglePhotos user to upload to", DefaultValue = "default")]
public string UploadUser {
get;
set;
}
[IniProperty("UploadAlbum", Description = "The Picasa album to upload to", DefaultValue = "default")]
[IniProperty("UploadAlbum", Description = "The GooglePhotos album to upload to", DefaultValue = "default")]
public string UploadAlbum {
get;
set;
}
[IniProperty("RefreshToken", Description = "Picasa authorization refresh Token", Encrypted = true)]
[IniProperty("RefreshToken", Description = "GooglePhotos authorization refresh Token", Encrypted = true)]
public string RefreshToken {
get;
set;

View file

@ -1,5 +1,5 @@
/*
* A Picasa Plugin for Greenshot
* A GooglePhotos Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
* For more information see: http://getgreenshot.org/
@ -22,21 +22,21 @@ using System.Drawing;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace GreenshotPicasaPlugin {
public class PicasaDestination : AbstractDestination {
private readonly PicasaPlugin _plugin;
public PicasaDestination(PicasaPlugin plugin) {
namespace GreenshotGooglePhotosPlugin {
public class GooglePhotosDestination : AbstractDestination {
private readonly GooglePhotosPlugin _plugin;
public GooglePhotosDestination(GooglePhotosPlugin plugin) {
_plugin = plugin;
}
public override string Designation => "Picasa";
public override string Designation => "GooglePhotos";
public override string Description => Language.GetString("picasa", LangKey.upload_menu_item);
public override string Description => Language.GetString("googlephotos", LangKey.upload_menu_item);
public override Image DisplayIcon {
get {
ComponentResourceManager resources = new ComponentResourceManager(typeof(PicasaPlugin));
return (Image)resources.GetObject("Picasa");
ComponentResourceManager resources = new ComponentResourceManager(typeof(GooglePhotosPlugin));
return (Image)resources.GetObject("GooglePhotos");
}
}

View file

@ -1,5 +1,5 @@
/*
* A Picasa Plugin for Greenshot
* A GooglePhotos Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
* For more information see: http://getgreenshot.org/
@ -28,14 +28,14 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace GreenshotPicasaPlugin {
namespace GreenshotGooglePhotosPlugin {
/// <summary>
/// This is the Picasa base code
/// This is the GooglePhotos base code
/// </summary>
[Plugin("Picasa", true)]
public class PicasaPlugin : IGreenshotPlugin {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PicasaPlugin));
private static PicasaConfiguration _config;
[Plugin("GooglePhotos", true)]
public class GooglePhotosPlugin : IGreenshotPlugin {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosPlugin));
private static GooglePhotosConfiguration _config;
private ComponentResourceManager _resources;
private ToolStripMenuItem _itemPlugInRoot;
@ -44,29 +44,28 @@ namespace GreenshotPicasaPlugin {
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) {
if (disposing) {
if (_itemPlugInRoot != null) {
private void Dispose(bool disposing)
{
if (!disposing) return;
if (_itemPlugInRoot == null) return;
_itemPlugInRoot.Dispose();
_itemPlugInRoot = null;
}
}
}
/// <summary>
/// Implementation of the IGreenshotPlugin.Initialize
/// </summary>
public bool Initialize() {
SimpleServiceProvider.Current.AddService<IDestination>(new PicasaDestination(this));
SimpleServiceProvider.Current.AddService<IDestination>(new GooglePhotosDestination(this));
// Get configuration
_config = IniConfig.GetIniSection<PicasaConfiguration>();
_resources = new ComponentResourceManager(typeof(PicasaPlugin));
_config = IniConfig.GetIniSection<GooglePhotosConfiguration>();
_resources = new ComponentResourceManager(typeof(GooglePhotosPlugin));
_itemPlugInRoot = new ToolStripMenuItem
{
Text = Language.GetString("picasa", LangKey.Configure),
Image = (Image) _resources.GetObject("Picasa")
Text = Language.GetString("googlephotos", LangKey.Configure),
Image = (Image) _resources.GetObject("GooglePhotos")
};
_itemPlugInRoot.Click += ConfigMenuClick;
PluginUtils.AddToContextMenu(_itemPlugInRoot);
@ -76,12 +75,12 @@ namespace GreenshotPicasaPlugin {
public void OnLanguageChanged(object sender, EventArgs e) {
if (_itemPlugInRoot != null) {
_itemPlugInRoot.Text = Language.GetString("picasa", LangKey.Configure);
_itemPlugInRoot.Text = Language.GetString("googlephotos", LangKey.Configure);
}
}
public void Shutdown() {
Log.Debug("Picasa Plugin shutdown.");
Log.Debug("GooglePhotos Plugin shutdown.");
Language.LanguageChanged -= OnLanguageChanged;
//host.OnImageEditorOpen -= new OnImageEditorOpenHandler(ImageEditorOpened);
}
@ -101,11 +100,11 @@ namespace GreenshotPicasaPlugin {
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality);
try {
string url = null;
new PleaseWaitForm().ShowAndWait("Picasa", Language.GetString("picasa", LangKey.communication_wait),
new PleaseWaitForm().ShowAndWait("GooglePhotos", Language.GetString("googlephotos", LangKey.communication_wait),
delegate
{
string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails));
url = PicasaUtils.UploadToPicasa(surfaceToUpload, outputSettings, captureDetails.Title, filename);
url = GooglePhotosUtils.UploadToGooglePhotos(surfaceToUpload, outputSettings, captureDetails.Title, filename);
}
);
uploadUrl = url;
@ -116,7 +115,7 @@ namespace GreenshotPicasaPlugin {
return true;
} catch (Exception e) {
Log.Error("Error uploading.", e);
MessageBox.Show(Language.GetString("picasa", LangKey.upload_failure) + " " + e.Message);
MessageBox.Show(Language.GetString("googlephotos", LangKey.upload_failure) + " " + e.Message);
}
uploadUrl = null;
return false;

View file

@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Picasa" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>picasa.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="GooglePhotos" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>GooglePhotos.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View file

@ -1,5 +1,5 @@
/*
* A Picasa Plugin for Greenshot
* A GooglePhotos Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
* For more information see: http://getgreenshot.org/
@ -21,40 +21,41 @@
using GreenshotPlugin.Core;
using System;
using System.Xml;
using GreenshotPlugin.Core.OAuth;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace GreenshotPicasaPlugin {
namespace GreenshotGooglePhotosPlugin {
/// <summary>
/// Description of PicasaUtils.
/// Description of GooglePhotosUtils.
/// </summary>
public static class PicasaUtils {
private const string PicasaScope = "https://picasaweb.google.com/data/";
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PicasaUtils));
private static readonly PicasaConfiguration Config = IniConfig.GetIniSection<PicasaConfiguration>();
private const string AuthUrl = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={ClientId}&redirect_uri={RedirectUrl}&state={State}&scope=" + PicasaScope;
public static class GooglePhotosUtils {
private const string GooglePhotosScope = "https://picasaweb.google.com/data/";
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosUtils));
private static readonly GooglePhotosConfiguration Config = IniConfig.GetIniSection<GooglePhotosConfiguration>();
private const string AuthUrl = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={ClientId}&redirect_uri={RedirectUrl}&state={State}&scope=" + GooglePhotosScope;
private const string TokenUrl = "https://www.googleapis.com/oauth2/v3/token";
private const string UploadUrl = "https://picasaweb.google.com/data/feed/api/user/{0}/albumid/{1}";
/// <summary>
/// Do the actual upload to Picasa
/// Do the actual upload to GooglePhotos
/// </summary>
/// <param name="surfaceToUpload">Image to upload</param>
/// <param name="outputSettings"></param>
/// <param name="title"></param>
/// <param name="filename"></param>
/// <returns>PicasaResponse</returns>
public static string UploadToPicasa(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) {
/// <param name="outputSettings">SurfaceOutputSettings</param>
/// <param name="title">string</param>
/// <param name="filename">string</param>
/// <returns>GooglePhotosResponse</returns>
public static string UploadToGooglePhotos(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) {
// Fill the OAuth2Settings
var settings = new OAuth2Settings
{
AuthUrlPattern = AuthUrl,
TokenUrl = TokenUrl,
CloudServiceName = "Picasa",
ClientId = PicasaCredentials.ClientId,
ClientSecret = PicasaCredentials.ClientSecret,
AuthorizeMode = OAuth2AuthorizeMode.LocalServer,
CloudServiceName = "GooglePhotos",
ClientId = GooglePhotosCredentials.ClientId,
ClientSecret = GooglePhotosCredentials.ClientSecret,
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = Config.RefreshToken,
AccessToken = Config.AccessToken,
AccessTokenExpires = Config.AccessTokenExpires
@ -111,7 +112,7 @@ namespace GreenshotPicasaPlugin {
return url;
}
} catch(Exception e) {
Log.ErrorFormat("Could not parse Picasa response due to error {0}, response was: {1}", e.Message, response);
Log.ErrorFormat("Could not parse GooglePhotos response due to error {0}, response was: {1}", e.Message, response);
}
return null;
}

View file

@ -19,13 +19,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GreenshotPicasaPlugin {
namespace GreenshotGooglePhotosPlugin {
/// <summary>
/// This class is merely a placeholder for the file keeping the API key and secret for dropbox integration.
/// You can set your own values here
/// </summary>
public static class PicasaCredentials {
public static string ClientId = "${Picasa_ClientId}";
public static string ClientSecret = "${Picasa_ClientSecret}";
public static class GooglePhotosCredentials {
public static string ClientId = "${GooglePhotos_ClientId}";
public static string ClientSecret = "${GooglePhotos_ClientSecret}";
}
}

View file

@ -1,10 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<RootNamespace>GreenshotPicasaPlugin</RootNamespace>
<AssemblyName>GreenshotPicasaPlugin</AssemblyName>
</PropertyGroup>
<ItemGroup>
<None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -12,7 +6,7 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Picasa.png" />
<EmbeddedResource Include="GooglePhotos.png" />
</ItemGroup>
<ItemGroup>

View file

@ -1,5 +1,5 @@
/*
* A Picasa Plugin for Greenshot
* A GooglePhotos Plugin for Greenshot
* Copyright (C) 2011 Francis Noel
*
* For more information see: http://getgreenshot.org/
@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace GreenshotPicasaPlugin {
namespace GreenshotGooglePhotosPlugin {
public enum LangKey
{
upload_menu_item,

View file

@ -4,8 +4,8 @@
<resource name="CANCEL">Zrušit</resource>
<resource name="communication_wait">Probíhá komunikace s Picasem. Prosím počkejte ...</resource>
<resource name="Configure">Konfigurace</resource>
<resource name="delete_question">Opravdu chcete odstranit obrázek {0} z Picasa?</resource>
<resource name="delete_title">Odstranit Picasa {0}</resource>
<resource name="delete_question">Opravdu chcete odstranit obrázek {0} z GooglePhotos?</resource>
<resource name="delete_title">Odstranit GooglePhotos {0}</resource>
<resource name="History">Historie</resource>
<resource name="InvalidCredentials">Neplatná oprávnění. Otevřít nastavení pro provedené změn.</resource>
<resource name="label_AfterUpload">Po odeslání</resource>
@ -19,11 +19,11 @@
<resource name="PictureDisplaySize.OriginalUrl">Originál URL</resource>
<resource name="PictureDisplaySize.SquareThumbnailUrl">Čtvercové náhledy URL ???</resource>
<resource name="PictureDisplaySize.WebUrl">Webová adresa URL</resource>
<resource name="settings_title">Nastavení Picasa</resource>
<resource name="settings_title">Nastavení GooglePhotos</resource>
<resource name="Upload">Nahrát</resource>
<resource name="upload_failure">Nahrání obrázku do Picasa se nezdařilo:</resource>
<resource name="upload_menu_item">Nahrát do Picasa</resource>
<resource name="upload_success">Úspěšně odeslaný obrázek do Picasa!</resource>
<resource name="UsernameNotSet">Prosím ověřit aplikaci Picasa. Otevřít nastavení obrazovky. ???</resource>
<resource name="upload_failure">Nahrání obrázku do GooglePhotos se nezdařilo:</resource>
<resource name="upload_menu_item">Nahrát do GooglePhotos</resource>
<resource name="upload_success">Úspěšně odeslaný obrázek do GooglePhotos!</resource>
<resource name="UsernameNotSet">Prosím ověřit aplikaci GooglePhotos. Otevřít nastavení obrazovky. ???</resource>
</resources>
</language>

View file

@ -8,25 +8,25 @@
Anschliessend
</resource>
<resource name="Configure">
Picasa konfigurieren
GooglePhotos konfigurieren
</resource>
<resource name="upload_menu_item">
Hochladen zu Picasa
Hochladen zu GooglePhotos
</resource>
<resource name="settings_title">
Picasa Einstellungen
GooglePhotos Einstellungen
</resource>
<resource name="upload_success">
Hochladen zu Picasa war erfolgreich !
Hochladen zu GooglePhotos war erfolgreich !
</resource>
<resource name="upload_failure">
Fehler beim Hochladen zu Picasa:
Fehler beim Hochladen zu GooglePhotos:
</resource>
<resource name="label_upload_format">
Grafikformat
</resource>
<resource name="communication_wait">
Übermittle Daten zu Picasa. Bitte warten...
Übermittle Daten zu GooglePhotos. Bitte warten...
</resource>
</resources>
</language>

View file

@ -8,25 +8,25 @@
After upload
</resource>
<resource name="Configure">
Configure Picasa
Configure GooglePhotos
</resource>
<resource name="upload_menu_item">
Upload to Picasa
Upload to GooglePhotos
</resource>
<resource name="settings_title">
Picasa settings
GooglePhotos settings
</resource>
<resource name="upload_success">
Successfully uploaded image to Picasa!
Successfully uploaded image to GooglePhotos!
</resource>
<resource name="upload_failure">
An error occured while uploading to Picasa:
An error occured while uploading to GooglePhotos:
</resource>
<resource name="label_upload_format">
Image format
</resource>
<resource name="communication_wait">
Communicating with Picasa. Please wait...
Communicating with GooglePhotos. Please wait...
</resource>
</resources>
</language>

View file

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="Français" ietf="fr-FR" version="1.0.0" languagegroup="">
<resources>
<resource name="communication_wait">Communication en cours avec Picasa. Veuillez patientez...</resource>
<resource name="Configure">Configurer Picasa</resource>
<resource name="communication_wait">Communication en cours avec GooglePhotos. Veuillez patientez...</resource>
<resource name="Configure">Configurer GooglePhotos</resource>
<resource name="label_AfterUpload">Après téléversement</resource>
<resource name="label_AfterUploadLinkToClipBoard">Copier le lien dans le presse-papier</resource>
<resource name="label_upload_format">Format image</resource>
<resource name="settings_title">Paramètres Picasa</resource>
<resource name="upload_failure">Une erreur s'est produite lors du téléversement vers Picasa :</resource>
<resource name="upload_menu_item">Téléverser vers Picasa</resource>
<resource name="upload_success">Image téléversée avec succès vers Picasa !</resource>
<resource name="settings_title">Paramètres GooglePhotos</resource>
<resource name="upload_failure">Une erreur s'est produite lors du téléversement vers GooglePhotos :</resource>
<resource name="upload_menu_item">Téléverser vers GooglePhotos</resource>
<resource name="upload_success">Image téléversée avec succès vers GooglePhotos !</resource>
</resources>
</language>

View file

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="Bahasa Indonesia" ietf="id-ID" version="1.0.0.0" languagegroup="">
<resources>
<resource name="communication_wait">Menyambungkan ke Picasa. Tunggu sebentar...</resource>
<resource name="Configure">Konfigurasi Picasa</resource>
<resource name="communication_wait">Menyambungkan ke GooglePhotos. Tunggu sebentar...</resource>
<resource name="Configure">Konfigurasi GooglePhotos</resource>
<resource name="label_AfterUpload">Sesudah mengunggah</resource>
<resource name="label_AfterUploadLinkToClipBoard">Sambung ke papanklip</resource>
<resource name="label_upload_format">Format gambar</resource>
<resource name="settings_title">Pengaturan Picasa</resource>
<resource name="upload_failure">Kesalahan terjadi ketika mengunggah ke Picasa:</resource>
<resource name="upload_menu_item">Unggah ke Picasa</resource>
<resource name="upload_success">Berhasil mengunggah gambar ke Picasa!</resource>
<resource name="settings_title">Pengaturan GooglePhotos</resource>
<resource name="upload_failure">Kesalahan terjadi ketika mengunggah ke GooglePhotos:</resource>
<resource name="upload_menu_item">Unggah ke GooglePhotos</resource>
<resource name="upload_success">Berhasil mengunggah gambar ke GooglePhotos!</resource>
</resources>
</language>

View file

@ -8,25 +8,25 @@
Dopo il caricamento
</resource>
<resource name="Configure">
Impostazioni Picasa
Impostazioni GooglePhotos
</resource>
<resource name="upload_menu_item">
Carica su Picasa
Carica su GooglePhotos
</resource>
<resource name="settings_title">
Impostazioni Picasa
Impostazioni GooglePhotos
</resource>
<resource name="upload_success">
Caricamento immagine su Picasa completato!
Caricamento immagine su GooglePhotos completato!
</resource>
<resource name="upload_failure">
Si è verificato un errore durante il caricamento su Picasa:
Si è verificato un errore durante il caricamento su GooglePhotos:
</resource>
<resource name="label_upload_format">
Formato immagine
</resource>
<resource name="communication_wait">
Comunicazione con Picasa...
Comunicazione con GooglePhotos...
</resource>
</resources>
</language>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="日本語" ietf="ja-JP" version="1.0.0">
<resources>
<resource name="label_AfterUploadLinkToClipBoard">リンクをクリップボードへコピー</resource>
<resource name="label_AfterUpload">アップロード後</resource>
<resource name="Configure">GooglePhotos の設定</resource>
<resource name="upload_menu_item">GooglePhotos にアップロード</resource>
<resource name="settings_title">GooglePhotos の設定</resource>
<resource name="upload_success">GooglePhotos へのアップロードに成功しました!</resource>
<resource name="upload_failure">GooglePhotos へのアップロード中にエラーが発生しました:</resource>
<resource name="label_upload_format">画像フォーマット</resource>
<resource name="communication_wait">GooglePhotos に接続中です。しばらくお待ち下さい...</resource>
</resources>
</language>

View file

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="Taqbaylit" ietf="kab-DZ" version="1.0.0" languagegroup="">
<resources>
<resource name="communication_wait">S tidett tebɣiḍ ad tekkseḍ amazray adigan n Picasa ?...</resource>
<resource name="Configure">Swel Picasa</resource>
<resource name="communication_wait">S tidett tebɣiḍ ad tekkseḍ amazray adigan n GooglePhotos ?...</resource>
<resource name="Configure">Swel GooglePhotos</resource>
<resource name="label_AfterUpload">Ticki yemmed usali</resource>
<resource name="label_AfterUploadLinkToClipBoard">Nɣel aseɣwen ɣef afus</resource>
<resource name="label_upload_format">Amsal n tugna</resource>
<resource name="settings_title">Iɣewwaṛen Picasa</resource>
<resource name="upload_failure">Teḍra-d tuccḍa deg usali ɣer Picasa :</resource>
<resource name="upload_menu_item">Sali ɣer Picasa</resource>
<resource name="upload_success">Tugna tuli ɣer Picasa !</resource>
<resource name="settings_title">Iɣewwaṛen GooglePhotos</resource>
<resource name="upload_failure">Teḍra-d tuccḍa deg usali ɣer GooglePhotos :</resource>
<resource name="upload_menu_item">Sali ɣer GooglePhotos</resource>
<resource name="upload_success">Tugna tuli ɣer GooglePhotos !</resource>
</resources>
</language>

View file

@ -8,25 +8,25 @@
얼로드 후
</resource>
<resource name="Configure">
Picasa 설정
GooglePhotos 설정
</resource>
<resource name="upload_menu_item">
Picasa로 업로드
GooglePhotos로 업로드
</resource>
<resource name="settings_title">
Picasa 설정
GooglePhotos 설정
</resource>
<resource name="upload_success">
Picasa로 이미지 업로드 성공!
GooglePhotos로 이미지 업로드 성공!
</resource>
<resource name="upload_failure">
Picasa로 업로드시 오류 발생:
GooglePhotos로 업로드시 오류 발생:
</resource>
<resource name="label_upload_format">
이미지 형식
</resource>
<resource name="communication_wait">
Picasa와 연결 중 잠시 기다리세요...
GooglePhotos와 연결 중 잠시 기다리세요...
</resource>
</resources>
</language>

View file

@ -9,25 +9,25 @@
Pēc augšupielādes
</resource>
<resource name="Configure">
Picasa iestatījumi
GooglePhotos iestatījumi
</resource>
<resource name="upload_menu_item">
Augšupieladēt uz Picasa
Augšupieladēt uz GooglePhotos
</resource>
<resource name="settings_title">
Picasa iestatījumi
GooglePhotos iestatījumi
</resource>
<resource name="upload_success">
Attēls veiksmīgi augšupielādēts uz Picasa!
Attēls veiksmīgi augšupielādēts uz GooglePhotos!
</resource>
<resource name="upload_failure">
Kļūda augšuplādējot uz Picasa:
Kļūda augšuplādējot uz GooglePhotos:
</resource>
<resource name="label_upload_format">
Attēla formāts
</resource>
<resource name="communication_wait">
Savienojos ar Picasa. Lūdzu uzgaidiet...
Savienojos ar GooglePhotos. Lūdzu uzgaidiet...
</resource>
</resources>
</language>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="Polski" ietf="pl-PL" version="1.1.4" languagegroup="2">
<resources>
<resource name="communication_wait">Trwa komunikacja z GooglePhotos. Proszę czekać...</resource>
<resource name="Configure">Konfiguruj GooglePhotos</resource>
<resource name="label_AfterUpload">Po wysłaniu</resource>
<resource name="label_AfterUploadLinkToClipBoard">Link do schowka</resource>
<resource name="label_upload_format">Format obrazów</resource>
<resource name="settings_title">Ustawienia GooglePhotos</resource>
<resource name="upload_failure">Wystąpił błąd przy wysyłaniu do GooglePhotos:</resource>
<resource name="upload_menu_item">Wyślij do GooglePhotos</resource>
<resource name="upload_success">Wysyłanie obrazu do GooglePhotos powiodło się!</resource>
</resources>
</language>

View file

@ -8,25 +8,25 @@
Após enviar
</resource>
<resource name="Configure">
Configurar o Picasa
Configurar o GooglePhotos
</resource>
<resource name="upload_menu_item">
enviar para o Picasa
enviar para o GooglePhotos
</resource>
<resource name="settings_title">
Definições Picasa
Definições GooglePhotos
</resource>
<resource name="upload_success">
Imagem enviada com êxito para o Picasa!
Imagem enviada com êxito para o GooglePhotos!
</resource>
<resource name="upload_failure">
Ocorreu um erro ao enviar para o Picasa:
Ocorreu um erro ao enviar para o GooglePhotos:
</resource>
<resource name="label_upload_format">
Formato da imagem
</resource>
<resource name="communication_wait">
A comunicar com o Picasa. Por favor aguarde...
A comunicar com o GooglePhotos. Por favor aguarde...
</resource>
</resources>
</language>

View file

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="Русский" ietf="ru-RU" version="1.1.0.2515" languagegroup="5">
<resources>
<resource name="communication_wait">Обмен данными с Picasa. Подождите...</resource>
<resource name="Configure">Настройка Picasa</resource>
<resource name="communication_wait">Обмен данными с GooglePhotos. Подождите...</resource>
<resource name="Configure">Настройка GooglePhotos</resource>
<resource name="label_AfterUpload">После загрузки</resource>
<resource name="label_AfterUploadLinkToClipBoard">Ссылки в буфер обмена</resource>
<resource name="label_upload_format">Формат изображения</resource>
<resource name="settings_title">Настройки Picasa</resource>
<resource name="upload_failure">Произошла ошибка при загрузке на Picasa:</resource>
<resource name="upload_menu_item">Загрузить на Picasa</resource>
<resource name="upload_success">Изображение успешно загружено на Picasa!</resource>
<resource name="settings_title">Настройки GooglePhotos</resource>
<resource name="upload_failure">Произошла ошибка при загрузке на GooglePhotos:</resource>
<resource name="upload_menu_item">Загрузить на GooglePhotos</resource>
<resource name="upload_success">Изображение успешно загружено на GooglePhotos!</resource>
</resources>
</language>

View file

@ -8,25 +8,25 @@
Vid uppladdning
</resource>
<resource name="Configure">
Konfigurera Picasa
Konfigurera GooglePhotos
</resource>
<resource name="upload_menu_item">
Ladda upp till Picasa
Ladda upp till GooglePhotos
</resource>
<resource name="settings_title">
Picasa-inställningar
GooglePhotos-inställningar
</resource>
<resource name="upload_success">
Skärmdumpen laddades upp till Picasa!
Skärmdumpen laddades upp till GooglePhotos!
</resource>
<resource name="upload_failure">
Ett fel uppstod vid uppladdning till Picasa:
Ett fel uppstod vid uppladdning till GooglePhotos:
</resource>
<resource name="label_upload_format">
Bildformat
</resource>
<resource name="communication_wait">
Kommunicerar med Picasa. Vänta...
Kommunicerar med GooglePhotos. Vänta...
</resource>
</resources>
</language>

View file

@ -3,12 +3,12 @@
<resources>
<resource name="label_AfterUploadLinkToClipBoard">Посилання в буфер обміну</resource>
<resource name="label_AfterUpload">Після вивантаження</resource>
<resource name="Configure">Налаштувати Picasa</resource>
<resource name="upload_menu_item">Вивантажити на Picasa</resource>
<resource name="settings_title">Параметри Picasa</resource>
<resource name="upload_success">Зображення вдало вивантажено на Picasa!</resource>
<resource name="upload_failure">Відбулась помилка під час вивантаження на Picasa:</resource>
<resource name="Configure">Налаштувати GooglePhotos</resource>
<resource name="upload_menu_item">Вивантажити на GooglePhotos</resource>
<resource name="settings_title">Параметри GooglePhotos</resource>
<resource name="upload_success">Зображення вдало вивантажено на GooglePhotos!</resource>
<resource name="upload_failure">Відбулась помилка під час вивантаження на GooglePhotos:</resource>
<resource name="label_upload_format">Формат зображення</resource>
<resource name="communication_wait">З’єднання з Picasa. Будь ласка, зачекайте...</resource>
<resource name="communication_wait">З’єднання з GooglePhotos. Будь ласка, зачекайте...</resource>
</resources>
</language>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="简体中文" ietf="zh-CN" version="1.0.3" languagegroup="a">
<resources>
<resource name="communication_wait">正在连接到GooglePhotos。请稍后...</resource>
<resource name="Configure">配置 GooglePhotos</resource>
<resource name="label_AfterUpload">上传之后</resource>
<resource name="label_AfterUploadLinkToClipBoard">复制链接到剪贴板</resource>
<resource name="label_upload_format">图片格式</resource>
<resource name="settings_title">GooglePhotos设置</resource>
<resource name="upload_failure">上传到GooglePhotos时发生错误</resource>
<resource name="upload_menu_item">上传到GooglePhotos</resource>
<resource name="upload_success">图片已成功上传到了GooglePhotos</resource>
</resources>
</language>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="正體中文" ietf="zh-TW" version="1.0.0" languagegroup="9">
<resources>
<resource name="communication_wait">正在與 GooglePhotos 通訊,請稍候...</resource>
<resource name="Configure">組態 GooglePhotos</resource>
<resource name="label_AfterUpload">上傳後</resource>
<resource name="label_AfterUploadLinkToClipBoard">連結到剪貼簿</resource>
<resource name="label_upload_format">圖片格式</resource>
<resource name="settings_title">GooglePhotos 設定</resource>
<resource name="upload_failure">上傳到 GooglePhotos 時發生錯誤:</resource>
<resource name="upload_menu_item">上傳到 GooglePhotos</resource>
<resource name="upload_success">上傳圖片到 GooglePhotos 成功!</resource>
</resources>
</language>

View file

@ -25,7 +25,7 @@ using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyDescription("A plugin to upload images to Picasa")]
[assembly: AssemblyDescription("A plugin to upload images to GooglePhotos")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.

View file

@ -25,7 +25,7 @@ namespace GreenshotImgurPlugin {
/// You can set your own values here
/// </summary>
public static class ImgurCredentials {
public static string CONSUMER_KEY = "${Imgur_ClientId}";
public static string CONSUMER_SECRET = "${Imgur_ClientSecret}";
public static string CONSUMER_KEY = "${Imgur13_ClientId}";
public static string CONSUMER_SECRET = "${Imgur13_ClientSecret}";
}
}

View file

@ -20,11 +20,11 @@
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using GreenshotPlugin.Core;
using GreenshotPlugin.Core.OAuth;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
@ -37,8 +37,6 @@ namespace GreenshotImgurPlugin {
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurUtils));
private const string SmallUrlPattern = "http://i.imgur.com/{0}s.jpg";
private static readonly ImgurConfiguration Config = IniConfig.GetIniSection<ImgurConfiguration>();
private const string AuthUrlPattern = "https://api.imgur.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}";
private const string TokenUrl = "https://api.imgur.com/oauth2/token";
/// <summary>
/// Check if we need to load the history
@ -162,20 +160,20 @@ namespace GreenshotImgurPlugin {
responseString = reader.ReadToEnd();
}
} catch (Exception ex) {
Log.Error("Upload to imgur gave an exeption: ", ex);
Log.Error("Upload to imgur gave an exception: ", ex);
throw;
}
} else {
var oauth2Settings = new OAuth2Settings
{
AuthUrlPattern = AuthUrlPattern,
TokenUrl = TokenUrl,
RedirectUrl = "https://getgreenshot.org/oauth/imgur",
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/authorize/imgur",
CloudServiceName = "Imgur",
ClientId = ImgurCredentials.CONSUMER_KEY,
ClientSecret = ImgurCredentials.CONSUMER_SECRET,
AuthorizeMode = OAuth2AuthorizeMode.OutOfBoundAuto,
AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver,
RefreshToken = Config.RefreshToken,
AccessToken = Config.AccessToken,
AccessTokenExpires = Config.AccessTokenExpires
@ -221,7 +219,7 @@ namespace GreenshotImgurPlugin {
Log.InfoFormat("Retrieving Imgur image for {0} with url {1}", imgurInfo.Hash, imgurInfo.SmallSquare);
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(string.Format(SmallUrlPattern, imgurInfo.Hash), HTTPMethod.GET);
webRequest.ServicePoint.Expect100Continue = false;
// Not for getting the thumbnail, in anonymous modus
// Not for getting the thumbnail, in anonymous mode
//SetClientId(webRequest);
using WebResponse response = webRequest.GetResponse();
LogRateLimitInfo(response);
@ -304,7 +302,7 @@ namespace GreenshotImgurPlugin {
}
}
}
// Make sure we remove it from the history, if no error occured
// Make sure we remove it from the history, if no error occurred
Config.runtimeImgurHistory.Remove(imgurInfo.Hash);
Config.ImgurUploadHistory.Remove(imgurInfo.Hash);
imgurInfo.Image = null;

View file

@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Xml;
using GreenshotPlugin.Core;
using GreenshotPlugin.Core.OAuth;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="日本語" ietf="ja-JP" version="1.0.0">
<resources>
<resource name="label_AfterUploadLinkToClipBoard">リンクをクリップボードへコピー</resource>
<resource name="label_AfterUpload">アップロード後</resource>
<resource name="Configure">Picasa の設定</resource>
<resource name="upload_menu_item">Picasa にアップロード</resource>
<resource name="settings_title">Picasa の設定</resource>
<resource name="upload_success">Picasa へのアップロードに成功しました!</resource>
<resource name="upload_failure">Picasa へのアップロード中にエラーが発生しました:</resource>
<resource name="label_upload_format">画像フォーマット</resource>
<resource name="communication_wait">Picasa に接続中です。しばらくお待ち下さい...</resource>
</resources>
</language>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="Polski" ietf="pl-PL" version="1.1.4" languagegroup="2">
<resources>
<resource name="communication_wait">Trwa komunikacja z Picasa. Proszę czekać...</resource>
<resource name="Configure">Konfiguruj Picasa</resource>
<resource name="label_AfterUpload">Po wysłaniu</resource>
<resource name="label_AfterUploadLinkToClipBoard">Link do schowka</resource>
<resource name="label_upload_format">Format obrazów</resource>
<resource name="settings_title">Ustawienia Picasa</resource>
<resource name="upload_failure">Wystąpił błąd przy wysyłaniu do Picasa:</resource>
<resource name="upload_menu_item">Wyślij do Picasa</resource>
<resource name="upload_success">Wysyłanie obrazu do Picasa powiodło się!</resource>
</resources>
</language>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="简体中文" ietf="zh-CN" version="1.0.3" languagegroup="a">
<resources>
<resource name="communication_wait">正在连接到Picasa。请稍后...</resource>
<resource name="Configure">配置 Picasa</resource>
<resource name="label_AfterUpload">上传之后</resource>
<resource name="label_AfterUploadLinkToClipBoard">复制链接到剪贴板</resource>
<resource name="label_upload_format">图片格式</resource>
<resource name="settings_title">Picasa设置</resource>
<resource name="upload_failure">上传到Picasa时发生错误</resource>
<resource name="upload_menu_item">上传到Picasa</resource>
<resource name="upload_success">图片已成功上传到了Picasa</resource>
</resources>
</language>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<language description="正體中文" ietf="zh-TW" version="1.0.0" languagegroup="9">
<resources>
<resource name="communication_wait">正在與 Picasa 通訊,請稍候...</resource>
<resource name="Configure">組態 Picasa</resource>
<resource name="label_AfterUpload">上傳後</resource>
<resource name="label_AfterUploadLinkToClipBoard">連結到剪貼簿</resource>
<resource name="label_upload_format">圖片格式</resource>
<resource name="settings_title">Picasa 設定</resource>
<resource name="upload_failure">上傳到 Picasa 時發生錯誤:</resource>
<resource name="upload_menu_item">上傳到 Picasa</resource>
<resource name="upload_success">上傳圖片到 Picasa 成功!</resource>
</resources>
</language>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

View file

@ -32,9 +32,8 @@ namespace GreenshotPlugin.Controls {
public sealed partial class OAuthLoginForm : Form {
private static readonly ILog LOG = LogManager.GetLogger(typeof(OAuthLoginForm));
private readonly string _callbackUrl;
private IDictionary<string, string> _callbackParameters;
public IDictionary<string, string> CallbackParameters => _callbackParameters;
public IDictionary<string, string> CallbackParameters { get; private set; }
public bool IsOk => DialogResult == DialogResult.OK;
@ -94,7 +93,7 @@ namespace GreenshotPlugin.Controls {
if (queryParams.Length > 0) {
queryParams = NetworkHelper.UrlDecode(queryParams);
//Store the Token and Token Secret
_callbackParameters = NetworkHelper.ParseQueryString(queryParams);
CallbackParameters = NetworkHelper.ParseQueryString(queryParams);
}
DialogResult = DialogResult.OK;
}

View file

@ -0,0 +1,793 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2021 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.UnmanagedHelpers;
using Microsoft.Win32;
namespace GreenshotPlugin.Core
{
/// <summary>
/// Description of EnvironmentInfo.
/// </summary>
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<AssemblyFileVersionAttribute>();
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<AssemblyInformationalVersionAttribute>();
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 externalException)
{
// e.g. COMException
report.AppendLine().AppendLine("ErrorCode: 0x" + externalException.ErrorCode.ToString("X"));
}
report.AppendLine().AppendLine("Stack:").AppendLine(ex.StackTrace);
if (ex is ReflectionTypeLoadException reflectionTypeLoadException)
{
report.AppendLine().AppendLine("LoaderExceptions: ");
foreach (Exception cbE in 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();
}
}
/// <summary>
/// Provides detailed information about the host operating system.
/// Code is available at: http://www.csharp411.com/determine-windows-version-and-edition-with-c/
/// </summary>
public static class OsInfo
{
/// <summary>
/// Determines if the current application is 32 or 64-bit.
/// </summary>
public static int Bits => IntPtr.Size * 8;
private static string _sEdition;
/// <summary>
/// Gets the edition of the operating system running on this computer.
/// </summary>
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;
/// <summary>
/// Gets the name of the operating system running on this computer.
/// </summary>
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
{
/// <summary>
/// The size of this data structure, in bytes. Set this member to sizeof(OSVERSIONINFOEX).
/// </summary>
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;
/// 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.
/// </summary>
public string ServicePackVersion
{
get
{
fixed (char* servicePackVersion = _szCSDVersion)
{
return new string(servicePackVersion);
}
}
}
/// <summary>
/// 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.
/// </summary>
public short ServicePackMajor => _wServicePackMajor;
/// <summary>
/// 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.
/// </summary>
public short ServicePackMinor => _wServicePackMinor;
/// <summary>
/// A bit mask that identifies the product suites available on the system. This member can be a combination of the
/// following values.
/// </summary>
public ushort SuiteMask => _wSuiteMask;
/// <summary>
/// Any additional information about the system.
/// </summary>
public byte ProductType => _wProductType;
/// <summary>
/// Factory for an empty OsVersionInfoEx
/// </summary>
/// <returns>OSVERSIONINFOEX</returns>
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;
/// <summary>
/// Gets the service pack information of the operating system running on this computer.
/// </summary>
public static string ServicePack
{
get
{
string servicePack = string.Empty;
OSVERSIONINFOEX osVersionInfo = OSVERSIONINFOEX.Create();
if (GetVersionEx(ref osVersionInfo))
{
servicePack = osVersionInfo.ServicePackVersion;
}
return servicePack;
}
}
/// Gets the full version string of the operating system running on this computer.
/// </summary>
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}";
}
}
}
}

View file

@ -32,11 +32,13 @@ using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace GreenshotPlugin.Core {
namespace GreenshotPlugin.Core
{
/// <summary>
/// HTTP Method to make sure we have the correct method
/// </summary>
public enum HTTPMethod {
public enum HTTPMethod
{
GET,
POST,
PUT,
@ -46,17 +48,17 @@ namespace GreenshotPlugin.Core {
/// <summary>
/// Description of NetworkHelper.
/// </summary>
public static class NetworkHelper {
public static class NetworkHelper
{
private static readonly ILog Log = LogManager.GetLogger(typeof(NetworkHelper));
private static readonly CoreConfiguration Config = IniConfig.GetIniSection<CoreConfiguration>();
static NetworkHelper() {
static NetworkHelper()
{
try
{
// Disable certificate checking
ServicePointManager.ServerCertificateValidationCallback += delegate {
return true;
};
ServicePointManager.ServerCertificateValidationCallback += delegate { return true; };
}
catch (Exception ex)
{
@ -64,21 +66,23 @@ namespace GreenshotPlugin.Core {
}
}
/// <summary>
/// Download the uri into a memory stream, without catching exceptions
/// </summary>
/// <param name="url">Of an image</param>
/// <returns>MemoryStream which is already seek-ed to 0</returns>
public static MemoryStream GetAsMemoryStream(string url) {
public static MemoryStream GetAsMemoryStream(string url)
{
var request = CreateWebRequest(url);
using var response = (HttpWebResponse)request.GetResponse();
var memoryStream = new MemoryStream();
using (var responseStream = response.GetResponseStream()) {
using (var responseStream = response.GetResponseStream())
{
responseStream?.CopyTo(memoryStream);
// Make sure it can be used directly
memoryStream.Seek(0, SeekOrigin.Begin);
}
return memoryStream;
}
@ -96,8 +100,10 @@ namespace GreenshotPlugin.Core {
{
continue;
}
extensions.AppendFormat(@"\.{0}|", extension);
}
extensions.Length--;
var imageUrlRegex = new Regex($@"(http|https)://.*(?<extension>{extensions})");
@ -117,10 +123,12 @@ namespace GreenshotPlugin.Core {
{
content = streamReader.ReadLine();
}
if (string.IsNullOrEmpty(content))
{
throw;
}
match = imageUrlRegex.Match(content);
if (!match.Success)
{
@ -135,6 +143,7 @@ namespace GreenshotPlugin.Core {
{
Log.Error("Problem downloading the image from: " + url, e);
}
return null;
}
@ -143,7 +152,8 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="uri">string with uri to connect to</param>
/// <returns>WebRequest</returns>
public static HttpWebRequest CreateWebRequest(string uri) {
public static HttpWebRequest CreateWebRequest(string uri)
{
return CreateWebRequest(new Uri(uri));
}
@ -153,7 +163,8 @@ namespace GreenshotPlugin.Core {
/// <param name="uri">string with uri to connect to</param>
/// /// <param name="method">Method to use</param>
/// <returns>WebRequest</returns>
public static HttpWebRequest CreateWebRequest(string uri, HTTPMethod method) {
public static HttpWebRequest CreateWebRequest(string uri, HTTPMethod method)
{
return CreateWebRequest(new Uri(uri), method);
}
@ -163,7 +174,8 @@ namespace GreenshotPlugin.Core {
/// <param name="uri">Uri with uri to connect to</param>
/// <param name="method">Method to use</param>
/// <returns>WebRequest</returns>
public static HttpWebRequest CreateWebRequest(Uri uri, HTTPMethod method) {
public static HttpWebRequest CreateWebRequest(Uri uri, HTTPMethod method)
{
var webRequest = CreateWebRequest(uri);
webRequest.Method = method.ToString();
return webRequest;
@ -174,7 +186,8 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="uri">Uri with uri to connect to</param>
/// <returns>WebRequest</returns>
public static HttpWebRequest CreateWebRequest(Uri uri) {
public static HttpWebRequest CreateWebRequest(Uri uri)
{
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
webRequest.Proxy = Config.UseProxy ? CreateProxy(uri) : null;
// Make sure the default credentials are available
@ -195,33 +208,46 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="uri"></param>
/// <returns>IWebProxy filled with all the proxy details or null if none is set/wanted</returns>
public static IWebProxy CreateProxy(Uri uri) {
public static IWebProxy CreateProxy(Uri uri)
{
IWebProxy proxyToUse = null;
if (!Config.UseProxy)
{
return proxyToUse;
}
proxyToUse = WebRequest.DefaultWebProxy;
if (proxyToUse != null) {
if (proxyToUse != null)
{
proxyToUse.Credentials = CredentialCache.DefaultCredentials;
if (!Log.IsDebugEnabled)
{
return proxyToUse;
}
// check the proxy for the Uri
if (!proxyToUse.IsBypassed(uri)) {
if (!proxyToUse.IsBypassed(uri))
{
var proxyUri = proxyToUse.GetProxy(uri);
if (proxyUri != null) {
if (proxyUri != null)
{
Log.Debug("Using proxy: " + proxyUri + " for " + uri);
} else {
}
else
{
Log.Debug("No proxy found!");
}
} else {
}
else
{
Log.Debug("Proxy bypass for: " + uri);
}
} else {
}
else
{
Log.Debug("No proxy found!");
}
return proxyToUse;
}
@ -231,11 +257,14 @@ namespace GreenshotPlugin.Core {
/// <param name="text"></param>
/// <returns></returns>
// [Obsolete("Use System.Uri.EscapeDataString instead")]
public static string UrlEncode(string text) {
if (!string.IsNullOrEmpty(text)) {
public static string UrlEncode(string text)
{
if (!string.IsNullOrEmpty(text))
{
// System.Uri provides reliable parsing, but doesn't encode spaces.
return Uri.EscapeDataString(text).Replace("%20", "+");
}
return null;
}
@ -245,17 +274,22 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="text"></param>
/// <returns>escaped data string</returns>
public static string EscapeDataString(string text) {
if (!string.IsNullOrEmpty(text)) {
public static string EscapeDataString(string text)
{
if (!string.IsNullOrEmpty(text))
{
var result = new StringBuilder();
int currentLocation = 0;
while (currentLocation < text.Length) {
while (currentLocation < text.Length)
{
string process = text.Substring(currentLocation, Math.Min(16384, text.Length - currentLocation));
result.Append(Uri.EscapeDataString(process));
currentLocation += 16384;
}
return result.ToString();
}
return null;
}
@ -264,7 +298,8 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="text">String to decode.</param>
/// <returns>decoded string</returns>
public static string UrlDecode(string text) {
public static string UrlDecode(string text)
{
// pre-process for + sign space formatting since System.Uri doesn't handle it
// plus literals are encoded as %2b normally so this should be safe
text = text.Replace("+", " ");
@ -276,22 +311,31 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="queryString"></param>
/// <returns>IDictionary string, string</returns>
public static IDictionary<string, string> ParseQueryString(string queryString) {
public static IDictionary<string, string> ParseQueryString(string queryString)
{
IDictionary<string, string> parameters = new SortedDictionary<string, string>();
// remove anything other than query string from uri
if (queryString.Contains("?")) {
if (queryString.Contains("?"))
{
queryString = queryString.Substring(queryString.IndexOf('?') + 1);
}
foreach (string vp in Regex.Split(queryString, "&")) {
if (string.IsNullOrEmpty(vp)) {
foreach (string vp in Regex.Split(queryString, "&"))
{
if (string.IsNullOrEmpty(vp))
{
continue;
}
string[] singlePair = Regex.Split(vp, "=");
if (parameters.ContainsKey(singlePair[0])) {
if (parameters.ContainsKey(singlePair[0]))
{
parameters.Remove(singlePair[0]);
}
parameters.Add(singlePair[0], singlePair.Length == 2 ? singlePair[1] : string.Empty);
}
return parameters;
}
@ -300,17 +344,21 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="queryParameters">the list of query parameters</param>
/// <returns>a string with the query parameters</returns>
public static string GenerateQueryParameters(IDictionary<string, object> queryParameters) {
if (queryParameters == null || queryParameters.Count == 0) {
public static string GenerateQueryParameters(IDictionary<string, object> queryParameters)
{
if (queryParameters == null || queryParameters.Count == 0)
{
return string.Empty;
}
queryParameters = new SortedDictionary<string, object>(queryParameters);
var sb = new StringBuilder();
foreach(string key in queryParameters.Keys) {
foreach (string key in queryParameters.Keys)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}={1}&", key, UrlEncode($"{queryParameters[key]}"));
}
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
@ -321,34 +369,40 @@ namespace GreenshotPlugin.Core {
/// </summary>
/// <param name="webRequest">HttpWebRequest to write the multipart form data to</param>
/// <param name="postParameters">Parameters to include in the multipart form data</param>
public static void WriteMultipartFormData(HttpWebRequest webRequest, IDictionary<string, object> postParameters) {
public static void WriteMultipartFormData(HttpWebRequest webRequest, IDictionary<string, object> postParameters)
{
string boundary = $"----------{Guid.NewGuid():N}";
webRequest.ContentType = "multipart/form-data; boundary=" + boundary;
using Stream formDataStream = webRequest.GetRequestStream();
WriteMultipartFormData(formDataStream, boundary, postParameters);
}
/// <summary>
/// Write Multipart Form Data to a Stream, content-type should be set before this!
/// </summary>
/// <param name="formDataStream">Stream to write the multipart form data to</param>
/// <param name="boundary">String boundary for the multipart/form-data</param>
/// <param name="postParameters">Parameters to include in the multipart form data</param>
public static void WriteMultipartFormData(Stream formDataStream, string boundary, IDictionary<string, object> postParameters) {
public static void WriteMultipartFormData(Stream formDataStream, string boundary, IDictionary<string, object> postParameters)
{
bool needsClrf = false;
foreach (var param in postParameters) {
foreach (var param in postParameters)
{
// Add a CRLF to allow multiple parameters to be added.
// Skip it on the first parameter, add it to subsequent parameters.
if (needsClrf) {
if (needsClrf)
{
formDataStream.Write(Encoding.UTF8.GetBytes("\r\n"), 0, Encoding.UTF8.GetByteCount("\r\n"));
}
needsClrf = true;
if (param.Value is IBinaryContainer binaryContainer) {
if (param.Value is IBinaryContainer binaryContainer)
{
binaryContainer.WriteFormDataToStream(boundary, param.Key, formDataStream);
} else {
}
else
{
string postData = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{param.Key}\"\r\n\r\n{param.Value}";
formDataStream.Write(Encoding.UTF8.GetBytes(postData), 0, Encoding.UTF8.GetByteCount(postData));
}
@ -359,12 +413,91 @@ namespace GreenshotPlugin.Core {
formDataStream.Write(Encoding.UTF8.GetBytes(footer), 0, Encoding.UTF8.GetByteCount(footer));
}
/// <summary>
/// Post content HttpWebRequest
/// </summary>
/// <param name="webRequest">HttpWebRequest to write the multipart form data to</param>
/// <param name="headers">IDictionary with the headers</param>
/// <param name="binaryContainer">IBinaryContainer</param>
public static void Post(HttpWebRequest webRequest, IDictionary<string, object> headers, IBinaryContainer binaryContainer = null)
{
foreach (var header in headers)
{
switch (header.Key)
{
case "Content-Type":
webRequest.ContentType = header.Value as string;
break;
case "Accept":
webRequest.Accept = header.Value as string;
break;
default:
webRequest.Headers.Add(header.Key, Convert.ToString(header.Value));
break;
}
}
if (!headers.ContainsKey("Content-Type"))
{
webRequest.ContentType = "application/octet-stream";
}
if (binaryContainer != null)
{
using var requestStream = webRequest.GetRequestStream();
binaryContainer.WriteToStream(requestStream);
}
}
/// <summary>
/// Post content HttpWebRequest
/// </summary>
/// <param name="webRequest">HttpWebRequest to write the multipart form data to</param>
/// <param name="headers">IDictionary with the headers</param>
/// <param name="jsonString">string</param>
public static void Post(HttpWebRequest webRequest, IDictionary<string, object> headers, string jsonString)
{
if (headers != null)
{
foreach (var header in headers)
{
switch (header.Key)
{
case "Content-Type":
webRequest.ContentType = header.Value as string;
break;
case "Accept":
webRequest.Accept = header.Value as string;
break;
default:
webRequest.Headers.Add(header.Key, Convert.ToString(header.Value));
break;
}
}
if (!headers.ContainsKey("Content-Type"))
{
webRequest.ContentType = "application/json";
}
}
else
{
webRequest.ContentType = "application/json";
}
if (jsonString != null)
{
using var requestStream = webRequest.GetRequestStream();
using var streamWriter = new StreamWriter(requestStream);
streamWriter.Write(jsonString);
}
}
/// <summary>
/// Post the parameters "x-www-form-urlencoded"
/// </summary>
/// <param name="webRequest"></param>
/// <param name="parameters"></param>
public static void UploadFormUrlEncoded(HttpWebRequest webRequest, IDictionary<string, object> parameters) {
public static void UploadFormUrlEncoded(HttpWebRequest webRequest, IDictionary<string, object> parameters)
{
webRequest.ContentType = "application/x-www-form-urlencoded";
string urlEncoded = GenerateQueryParameters(parameters);
@ -377,12 +510,16 @@ namespace GreenshotPlugin.Core {
/// Log the headers of the WebResponse, if IsDebugEnabled
/// </summary>
/// <param name="response">WebResponse</param>
private static void DebugHeaders(WebResponse response) {
if (!Log.IsDebugEnabled) {
private static void DebugHeaders(WebResponse response)
{
if (!Log.IsDebugEnabled)
{
return;
}
Log.DebugFormat("Debug information on the response from {0} :", response.ResponseUri);
foreach (string key in response.Headers.AllKeys) {
foreach (string key in response.Headers.AllKeys)
{
Log.DebugFormat("Reponse-header: {0}={1}", key, response.Headers[key]);
}
}
@ -393,7 +530,8 @@ namespace GreenshotPlugin.Core {
/// <param name="webRequest">The request object.</param>
/// <returns>The response data.</returns>
/// TODO: This method should handle the StatusCode better!
public static string GetResponseAsString(HttpWebRequest webRequest) {
public static string GetResponseAsString(HttpWebRequest webRequest)
{
return GetResponseAsString(webRequest, false);
}
@ -409,6 +547,7 @@ namespace GreenshotPlugin.Core {
{
return null;
}
using (response)
{
Stream responseStream = response.GetResponseStream();
@ -418,6 +557,7 @@ namespace GreenshotPlugin.Core {
responseData = reader.ReadToEnd();
}
}
return responseData;
}
@ -427,11 +567,13 @@ namespace GreenshotPlugin.Core {
/// <param name="webRequest"></param>
/// <param name="alsoReturnContentOnError"></param>
/// <returns></returns>
public static string GetResponseAsString(HttpWebRequest webRequest, bool alsoReturnContentOnError) {
public static string GetResponseAsString(HttpWebRequest webRequest, bool alsoReturnContentOnError)
{
string responseData = null;
HttpWebResponse response = null;
bool isHttpError = false;
try {
try
{
response = (HttpWebResponse)webRequest.GetResponse();
Log.InfoFormat("Response status: {0}", response.StatusCode);
isHttpError = (int)response.StatusCode >= 300;
@ -439,6 +581,7 @@ namespace GreenshotPlugin.Core {
{
Log.ErrorFormat("HTTP error {0}", response.StatusCode);
}
DebugHeaders(response);
responseData = GetResponseAsString(response);
if (isHttpError)
@ -446,10 +589,12 @@ namespace GreenshotPlugin.Core {
Log.ErrorFormat("HTTP response {0}", responseData);
}
}
catch (WebException e) {
catch (WebException e)
{
response = (HttpWebResponse)e.Response;
HttpStatusCode statusCode = HttpStatusCode.Unused;
if (response != null) {
if (response != null)
{
statusCode = response.StatusCode;
Log.ErrorFormat("HTTP error {0}", statusCode);
string errorContent = GetResponseAsString(response);
@ -457,13 +602,16 @@ namespace GreenshotPlugin.Core {
{
return errorContent;
}
Log.ErrorFormat("Content: {0}", errorContent);
}
Log.Error("WebException: ", e);
if (statusCode == HttpStatusCode.Unauthorized)
{
throw new UnauthorizedAccessException(e.Message);
}
throw;
}
finally
@ -474,17 +622,20 @@ namespace GreenshotPlugin.Core {
{
Log.ErrorFormat("HTTP error {0} with content: {1}", response.StatusCode, responseData);
}
response.Close();
}
}
return responseData;
}
}
}
/// <summary>
/// This interface can be used to pass binary information around, like byte[] or Image
/// </summary>
public interface IBinaryContainer {
public interface IBinaryContainer
{
void WriteFormDataToStream(string boundary, string name, Stream formDataStream);
void WriteToStream(Stream formDataStream);
string ToBase64String(Base64FormattingOptions formattingOptions);
@ -495,14 +646,15 @@ namespace GreenshotPlugin.Core {
string Filename { get; set; }
}
/// <summary>
/// A container to supply surfaces to a Multi-part form data upload
/// </summary>
public class SurfaceContainer : IBinaryContainer {
public class SurfaceContainer : IBinaryContainer
{
private readonly ISurface _surface;
private readonly SurfaceOutputSettings _outputSettings;
public SurfaceContainer(ISurface surface, SurfaceOutputSettings outputSettings, string filename) {
public SurfaceContainer(ISurface surface, SurfaceOutputSettings outputSettings, string filename)
{
_surface = surface;
_outputSettings = outputSettings;
Filename = filename;
@ -538,7 +690,8 @@ namespace GreenshotPlugin.Core {
/// <param name="boundary">Multipart separator</param>
/// <param name="name">Name of the thing</param>
/// <param name="formDataStream">Stream to write to</param>
public void WriteFormDataToStream(string boundary, string name, Stream formDataStream) {
public void WriteFormDataToStream(string boundary, string name, Stream formDataStream)
{
// Add just the first part of this param, since we will write the file data directly to the Stream
string header = $"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{Filename ?? name}\";\r\nContent-Type: {ContentType}\r\n\r\n";
@ -550,7 +703,8 @@ namespace GreenshotPlugin.Core {
/// A plain "write data to stream"
/// </summary>
/// <param name="dataStream"></param>
public void WriteToStream(Stream dataStream) {
public void WriteToStream(Stream dataStream)
{
// Write the file data directly to the Stream, rather than serializing it to a string.
ImageOutput.SaveToStream(_surface, dataStream, _outputSettings);
}
@ -559,7 +713,8 @@ namespace GreenshotPlugin.Core {
/// Upload the Surface as image to the webrequest
/// </summary>
/// <param name="webRequest"></param>
public void Upload(HttpWebRequest webRequest) {
public void Upload(HttpWebRequest webRequest)
{
webRequest.ContentType = ContentType;
using var requestStream = webRequest.GetRequestStream();
WriteToStream(requestStream);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// 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.
/// </summary>
public class LocalJsonReceiver
{
private static readonly ILog Log = LogManager.GetLogger(typeof(LocalJsonReceiver));
private readonly ManualResetEvent _ready = new ManualResetEvent(true);
private IDictionary<string, string> _returnValues;
/// <summary>
/// The url format for the website to post to. Expects one port parameter.
/// Default: http://localhost:{0}/authorize/
/// </summary>
public string ListeningUrlFormat { get; set; } = "http://localhost:{0}/authorize/";
private string _listeningUri;
/// <summary>
/// The URL where the server is listening
/// </summary>
public string ListeningUri {
get {
if (string.IsNullOrEmpty(_listeningUri))
{
_listeningUri = string.Format(ListeningUrlFormat, GetRandomUnusedPort());
}
return _listeningUri;
}
set => _listeningUri = value;
}
/// <summary>
/// This action is called when the URI must be opened, default is just to run Process.Start
/// </summary>
public Action<string> OpenUriAction
{
set;
get;
} = authorizationUrl =>
{
Log.DebugFormat("Open a browser with: {0}", authorizationUrl);
using var process = Process.Start(authorizationUrl);
};
/// <summary>
/// Timeout for waiting for the website to respond
/// </summary>
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(4);
/// <summary>
/// The OAuth code receiver
/// </summary>
/// <param name="oauth2Settings">OAuth2Settings</param>
/// <returns>Dictionary with values</returns>
public IDictionary<string, string> 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;
}
/// <summary>
/// Handle a connection async, this allows us to break the waiting
/// </summary>
/// <param name="result">IAsyncResult</param>
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<Dictionary<string, string>>(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);
}
}
/// <summary>
/// Returns a random, unused port.
/// </summary>
/// <returns>port to use</returns>
private static int GetRandomUnusedPort() {
var listener = new TcpListener(IPAddress.Loopback, 0);
try {
listener.Start();
return ((IPEndPoint)listener.LocalEndpoint).Port;
} finally {
listener.Stop();
}
}
}
}

View file

@ -0,0 +1,183 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using log4net;
namespace GreenshotPlugin.Core.OAuth
{
/// <summary>
/// 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.
/// </summary>
public class LocalServerCodeReceiver {
private static readonly ILog Log = LogManager.GetLogger(typeof(LocalServerCodeReceiver));
private readonly ManualResetEvent _ready = new ManualResetEvent(true);
/// <summary>
/// The call back format. Expects one port parameter.
/// Default: http://localhost:{0}/authorize/
/// </summary>
public string LoopbackCallbackUrl { get; set; } = "http://localhost:{0}/authorize/";
/// <summary>
/// HTML code to to return the _browser, default it will try to close the _browser / tab, this won't always work.
/// You can use CloudServiceName where you want to show the CloudServiceName from your OAuth2 settings
/// </summary>
public string ClosePageResponse { get; set; } = @"<html>
<head><title>OAuth 2.0 Authentication CloudServiceName</title></head>
<body>
Greenshot received information from CloudServiceName. You can close this browser / tab if it is not closed itself...
<script type='text/javascript'>
window.setTimeout(function() {
window.open('', '_self', '');
window.close();
}, 1000);
if (window.opener) {
window.opener.checkToken();
}
</script>
</body>
</html>";
private string _redirectUri;
/// <summary>
/// The URL to redirect to
/// </summary>
protected string RedirectUri {
get {
if (!string.IsNullOrEmpty(_redirectUri)) {
return _redirectUri;
}
return _redirectUri = string.Format(LoopbackCallbackUrl, GetRandomUnusedPort());
}
}
private string _cloudServiceName;
private readonly IDictionary<string, string> _returnValues = new Dictionary<string, string>();
/// <summary>
/// The OAuth code receiver
/// </summary>
/// <param name="oauth2Settings"></param>
/// <returns>Dictionary with values</returns>
public IDictionary<string, string> ReceiveCode(OAuth2Settings oauth2Settings) {
// Set the redirect URL on the settings
oauth2Settings.RedirectUrl = RedirectUri;
_cloudServiceName = oauth2Settings.CloudServiceName;
using (var listener = new HttpListener()) {
listener.Prefixes.Add(oauth2Settings.RedirectUrl);
try {
listener.Start();
// Get the formatted FormattedAuthUrl
string authorizationUrl = oauth2Settings.FormattedAuthUrl;
Log.DebugFormat("Open a browser with: {0}", authorizationUrl);
Process.Start(authorizationUrl);
// Wait to get the authorization code response.
var context = listener.BeginGetContext(ListenerCallback, listener);
_ready.Reset();
while (!context.AsyncWaitHandle.WaitOne(1000, true)) {
Log.Debug("Waiting for response");
}
} catch (Exception) {
// Make sure we can clean up, also if the thead is aborted
_ready.Set();
throw;
} finally {
_ready.WaitOne();
listener.Close();
}
}
return _returnValues;
}
/// <summary>
/// Handle a connection async, this allows us to break the waiting
/// </summary>
/// <param name="result">IAsyncResult</param>
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;
try {
NameValueCollection nameValueCollection = request.QueryString;
// Get response object.
using (HttpListenerResponse response = context.Response) {
// Write a "close" response.
byte[] buffer = Encoding.UTF8.GetBytes(ClosePageResponse.Replace("CloudServiceName", _cloudServiceName));
// Write to response stream.
response.ContentLength64 = buffer.Length;
using var stream = response.OutputStream;
stream.Write(buffer, 0, buffer.Length);
}
// Create a new response URL with a dictionary that contains all the response query parameters.
foreach (var name in nameValueCollection.AllKeys) {
if (!_returnValues.ContainsKey(name)) {
_returnValues.Add(name, nameValueCollection[name]);
}
}
} catch (Exception) {
context.Response.OutputStream.Close();
throw;
}
_ready.Set();
}
/// <summary>
/// Returns a random, unused port.
/// </summary>
/// <returns>port to use</returns>
private static int GetRandomUnusedPort() {
var listener = new TcpListener(IPAddress.Loopback, 0);
try {
listener.Start();
return ((IPEndPoint)listener.LocalEndpoint).Port;
} finally {
listener.Stop();
}
}
}
}

View file

@ -0,0 +1,33 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace GreenshotPlugin.Core.OAuth
{
/// <summary>
/// Specify the authorize mode that is used to get the token from the cloud service.
/// </summary>
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
JsonReceiver, // Will start a local HttpListener and wait for a Json post
EmbeddedBrowser // Will open into an embedded _browser (OAuthLoginForm), and catch the redirect
}
}

View file

@ -0,0 +1,372 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Net;
using GreenshotPlugin.Controls;
namespace GreenshotPlugin.Core.OAuth {
/// <summary>
/// Code to simplify OAuth 2
/// </summary>
public static class OAuth2Helper {
private const string RefreshToken = "refresh_token";
private const string AccessToken = "access_token";
private const string Code = "code";
private const string Error = "error";
private const string ClientId = "client_id";
private const string ClientSecret = "client_secret";
private const string GrantType = "grant_type";
private const string AuthorizationCode = "authorization_code";
private const string RedirectUri = "redirect_uri";
private const string ExpiresIn = "expires_in";
/// <summary>
/// Generate an OAuth 2 Token by using the supplied code
/// </summary>
/// <param name="settings">OAuth2Settings to update with the information that was retrieved</param>
public static void GenerateRefreshToken(OAuth2Settings settings) {
IDictionary<string, object> data = new Dictionary<string, object>
{
// Use the returned code to get a refresh code
{ Code, settings.Code },
{ ClientId, settings.ClientId },
{ ClientSecret, settings.ClientSecret },
{ GrantType, AuthorizationCode }
};
foreach (string key in settings.AdditionalAttributes.Keys) {
data.Add(key, settings.AdditionalAttributes[key]);
}
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(settings.TokenUrl, HTTPMethod.POST);
NetworkHelper.UploadFormUrlEncoded(webRequest, data);
string accessTokenJsonResult = NetworkHelper.GetResponseAsString(webRequest, true);
IDictionary<string, object> refreshTokenResult = JSONHelper.JsonDecode(accessTokenJsonResult);
if (refreshTokenResult.ContainsKey("error"))
{
if (refreshTokenResult.ContainsKey("error_description")) {
throw new Exception($"{refreshTokenResult["error"]} - {refreshTokenResult["error_description"]}");
}
throw new Exception((string)refreshTokenResult["error"]);
}
// gives as described here: https://developers.google.com/identity/protocols/OAuth2InstalledApp
// "access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
// "expires_in":3920,
// "token_type":"Bearer",
// "refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
if (refreshTokenResult.ContainsKey(AccessToken))
{
settings.AccessToken = (string)refreshTokenResult[AccessToken];
}
if (refreshTokenResult.ContainsKey(RefreshToken))
{
settings.RefreshToken = (string)refreshTokenResult[RefreshToken];
}
if (refreshTokenResult.ContainsKey(ExpiresIn))
{
object seconds = refreshTokenResult[ExpiresIn];
if (seconds != null)
{
settings.AccessTokenExpires = DateTimeOffset.Now.AddSeconds((double)seconds);
}
}
settings.Code = null;
}
/// <summary>
/// Used to update the settings with the callback information
/// </summary>
/// <param name="settings">OAuth2Settings</param>
/// <param name="callbackParameters">IDictionary</param>
/// <returns>true if the access token is already in the callback</returns>
private static bool UpdateFromCallback(OAuth2Settings settings, IDictionary<string, string> callbackParameters)
{
if (!callbackParameters.ContainsKey(AccessToken))
{
return false;
}
if (callbackParameters.ContainsKey(RefreshToken))
{
// Refresh the refresh token :)
settings.RefreshToken = callbackParameters[RefreshToken];
}
if (callbackParameters.ContainsKey(ExpiresIn))
{
var expiresIn = callbackParameters[ExpiresIn];
settings.AccessTokenExpires = DateTimeOffset.MaxValue;
if (expiresIn != null)
{
if (double.TryParse(expiresIn, out var seconds))
{
settings.AccessTokenExpires = DateTimeOffset.Now.AddSeconds(seconds);
}
}
}
settings.AccessToken = callbackParameters[AccessToken];
return true;
}
/// <summary>
/// Go out and retrieve a new access token via refresh-token with the TokenUrl in the settings
/// Will update the access token, refresh token, expire date
/// </summary>
/// <param name="settings"></param>
public static void GenerateAccessToken(OAuth2Settings settings) {
IDictionary<string, object> data = new Dictionary<string, object>
{
{ RefreshToken, settings.RefreshToken },
{ ClientId, settings.ClientId },
{ ClientSecret, settings.ClientSecret },
{ GrantType, RefreshToken }
};
foreach (string key in settings.AdditionalAttributes.Keys) {
data.Add(key, settings.AdditionalAttributes[key]);
}
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(settings.TokenUrl, HTTPMethod.POST);
NetworkHelper.UploadFormUrlEncoded(webRequest, data);
string accessTokenJsonResult = NetworkHelper.GetResponseAsString(webRequest, true);
// gives as described here: https://developers.google.com/identity/protocols/OAuth2InstalledApp
// "access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
// "expires_in":3920,
// "token_type":"Bearer",
IDictionary<string, object> accessTokenResult = JSONHelper.JsonDecode(accessTokenJsonResult);
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;
}
if (accessTokenResult.ContainsKey("error_description")) {
throw new Exception($"{accessTokenResult["error"]} - {accessTokenResult["error_description"]}");
}
throw new Exception((string)accessTokenResult["error"]);
}
if (accessTokenResult.ContainsKey(AccessToken))
{
settings.AccessToken = (string) accessTokenResult[AccessToken];
settings.AccessTokenExpires = DateTimeOffset.MaxValue;
}
if (accessTokenResult.ContainsKey(RefreshToken)) {
// Refresh the refresh token :)
settings.RefreshToken = (string)accessTokenResult[RefreshToken];
}
if (accessTokenResult.ContainsKey(ExpiresIn))
{
object seconds = accessTokenResult[ExpiresIn];
if (seconds != null)
{
settings.AccessTokenExpires = DateTimeOffset.Now.AddSeconds((double) seconds);
}
}
}
/// <summary>
/// Authorize by using the mode specified in the settings
/// </summary>
/// <param name="settings">OAuth2Settings</param>
/// <returns>false if it was canceled, true if it worked, exception if not</returns>
public static bool Authorize(OAuth2Settings settings) {
var completed = settings.AuthorizeMode switch
{
OAuth2AuthorizeMode.LocalServer => AuthorizeViaLocalServer(settings),
OAuth2AuthorizeMode.EmbeddedBrowser => AuthorizeViaEmbeddedBrowser(settings),
OAuth2AuthorizeMode.JsonReceiver => AuthorizeViaDefaultBrowser(settings),
_ => throw new NotImplementedException($"Authorize mode '{settings.AuthorizeMode}' is not 'yet' implemented."),
};
return completed;
}
/// <summary>
/// Authorize via the default browser, via the Greenshot website.
/// It will wait for a Json post.
/// If this works, return the code
/// </summary>
/// <param name="settings">OAuth2Settings with the Auth / Token url etc</param>
/// <returns>true if completed, false if canceled</returns>
private static bool AuthorizeViaDefaultBrowser(OAuth2Settings settings)
{
var codeReceiver = new LocalJsonReceiver();
IDictionary<string, string> result = codeReceiver.ReceiveCode(settings);
if (result == null || result.Count == 0)
{
return false;
}
foreach (var key in result.Keys)
{
switch (key)
{
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;
}
}
if (result.TryGetValue("error", out var error))
{
if (result.TryGetValue("error_description", out var errorDescription))
{
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);
return !string.IsNullOrEmpty(settings.AccessToken);
}
return true;
}
/// <summary>
/// Authorize via an embedded browser
/// If this works, return the code
/// </summary>
/// <param name="settings">OAuth2Settings with the Auth / Token url etc</param>
/// <returns>true if completed, false if canceled</returns>
private static bool AuthorizeViaEmbeddedBrowser(OAuth2Settings settings) {
if (string.IsNullOrEmpty(settings.CloudServiceName)) {
throw new ArgumentNullException(nameof(settings.CloudServiceName));
}
if (settings.BrowserSize == Size.Empty) {
throw new ArgumentNullException(nameof(settings.BrowserSize));
}
OAuthLoginForm loginForm = new OAuthLoginForm($"Authorize {settings.CloudServiceName}", settings.BrowserSize, settings.FormattedAuthUrl, settings.RedirectUrl);
loginForm.ShowDialog();
if (!loginForm.IsOk) return false;
if (loginForm.CallbackParameters.TryGetValue(Code, out var code) && !string.IsNullOrEmpty(code)) {
settings.Code = code;
GenerateRefreshToken(settings);
return true;
}
return UpdateFromCallback(settings, loginForm.CallbackParameters);
}
/// <summary>
/// Authorize via a local server by using the LocalServerCodeReceiver
/// If this works, return the code
/// </summary>
/// <param name="settings">OAuth2Settings with the Auth / Token url etc</param>
/// <returns>true if completed</returns>
private static bool AuthorizeViaLocalServer(OAuth2Settings settings) {
var codeReceiver = new LocalServerCodeReceiver();
IDictionary<string, string> result = codeReceiver.ReceiveCode(settings);
if (result.TryGetValue(Code, out var code) && !string.IsNullOrEmpty(code)) {
settings.Code = code;
GenerateRefreshToken(settings);
return true;
}
if (result.TryGetValue("error", out var error)) {
if (result.TryGetValue("error_description", out var errorDescription)) {
throw new Exception(errorDescription);
}
if ("access_denied" == error) {
throw new UnauthorizedAccessException("Access denied");
}
throw new Exception(error);
}
return false;
}
/// <summary>
/// Simple helper to add the Authorization Bearer header
/// </summary>
/// <param name="webRequest">WebRequest</param>
/// <param name="settings">OAuth2Settings</param>
public static void AddOAuth2Credentials(HttpWebRequest webRequest, OAuth2Settings settings) {
if (!string.IsNullOrEmpty(settings.AccessToken)) {
webRequest.Headers.Add("Authorization", "Bearer " + settings.AccessToken);
}
}
/// <summary>
/// Check and authenticate or refresh tokens
/// </summary>
/// <param name="settings">OAuth2Settings</param>
public static void CheckAndAuthenticateOrRefresh(OAuth2Settings settings) {
// Get Refresh / Access token
if (string.IsNullOrEmpty(settings.RefreshToken)) {
if (!Authorize(settings)) {
throw new Exception("Authentication cancelled");
}
}
if (settings.IsAccessTokenExpired) {
GenerateAccessToken(settings);
// Get Refresh / Access token
if (string.IsNullOrEmpty(settings.RefreshToken)) {
if (!Authorize(settings)) {
throw new Exception("Authentication cancelled");
}
GenerateAccessToken(settings);
}
}
if (settings.IsAccessTokenExpired) {
throw new Exception("Authentication failed");
}
}
/// <summary>
/// CreateWebRequest ready for OAuth 2 access
/// </summary>
/// <param name="method">HTTPMethod</param>
/// <param name="url"></param>
/// <param name="settings">OAuth2Settings</param>
/// <returns>HttpWebRequest</returns>
public static HttpWebRequest CreateOAuth2WebRequest(HTTPMethod method, string url, OAuth2Settings settings) {
CheckAndAuthenticateOrRefresh(settings);
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, method);
AddOAuth2Credentials(webRequest, settings);
return webRequest;
}
}
}

View file

@ -0,0 +1,181 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
namespace GreenshotPlugin.Core.OAuth
{
/// <summary>
/// Settings for the OAuth 2 protocol
/// </summary>
public class OAuth2Settings {
public OAuth2Settings() {
AdditionalAttributes = new Dictionary<string, string>();
// Create a default state
var state = Guid.NewGuid().ToString();
// Only store a small part of the GUID
State = state.Substring(0, state.IndexOf('-')-1);
AuthorizeMode = OAuth2AuthorizeMode.Unknown;
}
public OAuth2AuthorizeMode AuthorizeMode {
get;
set;
}
/// <summary>
/// Specify the name of the cloud service, so it can be used in window titles, logs etc
/// </summary>
public string CloudServiceName {
get;
set;
}
/// <summary>
/// Specify the size of the embedded Browser, if using this
/// </summary>
public Size BrowserSize {
get;
set;
}
/// <summary>
/// The OAuth 2 client id
/// </summary>
public string ClientId {
get;
set;
}
/// <summary>
/// The OAuth 2 client secret
/// </summary>
public string ClientSecret {
get;
set;
}
/// <summary>
/// The OAuth 2 state, this is something that is passed to the server, is not processed but returned back to the client.
/// e.g. a correlation ID
/// Default this is filled with a new Guid
/// </summary>
public string State {
get;
set;
}
/// <summary>
/// The authorization URL where the values of this class can be "injected"
/// </summary>
public string AuthUrlPattern {
get;
set;
}
/// <summary>
/// Get formatted Auth url (this will call a FormatWith(this) on the AuthUrlPattern
/// </summary>
public string FormattedAuthUrl => AuthUrlPattern.FormatWith(this);
/// <summary>
/// The URL to get a Token
/// </summary>
public string TokenUrl {
get;
set;
}
/// <summary>
/// This is the redirect URL, in some implementations this is automatically set (LocalServerCodeReceiver)
/// In some implementations this could be e.g. urn:ietf:wg:oauth:2.0:oob or urn:ietf:wg:oauth:2.0:oob:auto
/// </summary>
public string RedirectUrl {
get;
set;
}
/// <summary>
/// Bearer token for accessing OAuth 2 services
/// </summary>
public string AccessToken {
get;
set;
}
/// <summary>
/// Expire time for the AccessToken, this this time (-60 seconds) is passed a new AccessToken needs to be generated with the RefreshToken
/// </summary>
public DateTimeOffset AccessTokenExpires {
get;
set;
}
/// <summary>
/// Return true if the access token is expired.
/// Important "side-effect": if true is returned the AccessToken will be set to null!
/// </summary>
public bool IsAccessTokenExpired {
get {
if (AccessTokenExpires == default)
{
return false;
}
bool expired = true;
if (!string.IsNullOrEmpty(AccessToken)) {
expired = DateTimeOffset.Now.AddSeconds(60) > AccessTokenExpires;
}
// Make sure the token is not usable
if (expired) {
AccessToken = null;
}
return expired;
}
}
/// <summary>
/// Token used to get a new Access Token
/// </summary>
public string RefreshToken {
get;
set;
}
/// <summary>
/// Put anything in here which is needed for the OAuth 2 implementation of this specific service but isn't generic, e.g. for Google there is a "scope"
/// </summary>
public IDictionary<string, string> AdditionalAttributes {
get;
set;
}
/// <summary>
/// This contains the code returned from the authorization, but only shortly after it was received.
/// It will be cleared as soon as it was used.
/// </summary>
public string Code {
get;
set;
}
}
}

View file

@ -0,0 +1,629 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using GreenshotPlugin.Controls;
using log4net;
namespace GreenshotPlugin.Core.OAuth
{
/// <summary>
/// An OAuth 1 session object
/// </summary>
public class OAuthSession {
private static readonly ILog Log = LogManager.GetLogger(typeof(OAuthSession));
protected const string OAUTH_VERSION = "1.0";
protected const string OAUTH_PARAMETER_PREFIX = "oauth_";
//
// List of know and used oauth parameters' names
//
protected const string OAUTH_CONSUMER_KEY_KEY = "oauth_consumer_key";
protected const string OAUTH_CALLBACK_KEY = "oauth_callback";
protected const string OAUTH_VERSION_KEY = "oauth_version";
protected const string OAUTH_SIGNATURE_METHOD_KEY = "oauth_signature_method";
protected const string OAUTH_TIMESTAMP_KEY = "oauth_timestamp";
protected const string OAUTH_NONCE_KEY = "oauth_nonce";
protected const string OAUTH_TOKEN_KEY = "oauth_token";
protected const string OAUTH_VERIFIER_KEY = "oauth_verifier";
protected const string OAUTH_TOKEN_SECRET_KEY = "oauth_token_secret";
protected const string OAUTH_SIGNATURE_KEY = "oauth_signature";
protected const string HMACSHA1SignatureType = "HMAC-SHA1";
protected const string PlainTextSignatureType = "PLAINTEXT";
protected static Random random = new Random();
protected const string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
private string _userAgent = "Greenshot";
private IDictionary<string, string> _requestTokenResponseParameters;
public IDictionary<string, object> RequestTokenParameters { get; } = new Dictionary<string, object>();
/// <summary>
/// Parameters of the last called getAccessToken
/// </summary>
public IDictionary<string, string> AccessTokenResponseParameters { get; private set; }
/// <summary>
/// Parameters of the last called getRequestToken
/// </summary>
public IDictionary<string, string> RequestTokenResponseParameters => _requestTokenResponseParameters;
private readonly string _consumerKey;
private readonly string _consumerSecret;
// default _browser size
public HTTPMethod RequestTokenMethod {
get;
set;
}
public HTTPMethod AccessTokenMethod {
get;
set;
}
public string RequestTokenUrl {
get;
set;
}
public string AuthorizeUrl {
get;
set;
}
public string AccessTokenUrl {
get;
set;
}
public string Token {
get;
set;
}
public string TokenSecret {
get;
set;
}
public string Verifier {
get;
set;
}
public OAuthSignatureTypes SignatureType {
get;
set;
}
public bool UseMultipartFormData { get; set; }
public string UserAgent {
get {
return _userAgent;
}
set {
_userAgent = value;
}
}
public string CallbackUrl { get; set; } = "http://getgreenshot.org";
public bool CheckVerifier { get; set; } = true;
public Size BrowserSize { get; set; } = new Size(864, 587);
public string LoginTitle { get; set; } = "Authorize Greenshot access";
public bool UseHttpHeadersForAuthorization { get; set; } = true;
public bool AutoLogin {
get;
set;
}
/// <summary>
/// Create an OAuthSession with the consumerKey / consumerSecret
/// </summary>
/// <param name="consumerKey">"Public" key for the encoding. When using RSASHA1 this is the path to the private key file</param>
/// <param name="consumerSecret">"Private" key for the encoding. when usin RSASHA1 this is the password for the private key file</param>
public OAuthSession(string consumerKey, string consumerSecret) {
_consumerKey = consumerKey;
_consumerSecret = consumerSecret;
UseMultipartFormData = true;
RequestTokenMethod = HTTPMethod.GET;
AccessTokenMethod = HTTPMethod.GET;
SignatureType = OAuthSignatureTypes.HMACSHA1;
AutoLogin = true;
}
/// <summary>
/// Helper function to compute a hash value
/// </summary>
/// <param name="hashAlgorithm">The hashing algorithm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function</param>
/// <param name="data">The data to hash</param>
/// <returns>a Base64 string of the hash value</returns>
private static string ComputeHash(HashAlgorithm hashAlgorithm, string data) {
if (hashAlgorithm == null) {
throw new ArgumentNullException(nameof(hashAlgorithm));
}
if (string.IsNullOrEmpty(data)) {
throw new ArgumentNullException(nameof(data));
}
byte[] dataBuffer = Encoding.UTF8.GetBytes(data);
byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer);
return Convert.ToBase64String(hashBytes);
}
/// <summary>
/// Generate the normalized paramter string
/// </summary>
/// <param name="queryParameters">the list of query parameters</param>
/// <returns>a string with the normalized query parameters</returns>
private static string GenerateNormalizedParametersString(IDictionary<string, object> queryParameters) {
if (queryParameters == null || queryParameters.Count == 0) {
return string.Empty;
}
queryParameters = new SortedDictionary<string, object>(queryParameters);
StringBuilder sb = new StringBuilder();
foreach (string key in queryParameters.Keys) {
if (queryParameters[key] is string) {
sb.AppendFormat(CultureInfo.InvariantCulture, "{0}={1}&", key, UrlEncode3986($"{queryParameters[key]}"));
}
}
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
/// <summary>
/// This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case.
/// While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth
/// The resulting string is for UTF-8 encoding!
/// </summary>
/// <param name="value">The value to Url encode</param>
/// <returns>Returns a Url encoded string (unicode) with UTF-8 encoded % values</returns>
public static string UrlEncode3986(string value) {
StringBuilder result = new StringBuilder();
foreach (char symbol in value) {
if (UnreservedChars.IndexOf(symbol) != -1) {
result.Append(symbol);
} else {
byte[] utf8Bytes = Encoding.UTF8.GetBytes(symbol.ToString());
foreach(byte utf8Byte in utf8Bytes) {
result.AppendFormat("%{0:X2}", utf8Byte);
}
}
}
return result.ToString();
}
/// <summary>
/// Generate the timestamp for the signature
/// </summary>
/// <returns></returns>
public static string GenerateTimeStamp() {
// Default implementation of UNIX time of the current UTC time
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
/// <summary>
/// Generate a nonce
/// </summary>
/// <returns></returns>
public static string GenerateNonce() {
// Just a simple implementation of a random number between 123400 and 9999999
return random.Next(123400, 9999999).ToString();
}
/// <summary>
/// Get the request token using the consumer key and secret. Also initializes tokensecret
/// </summary>
/// <returns>response, this doesn't need to be used!!</returns>
private string GetRequestToken() {
IDictionary<string, object> parameters = new Dictionary<string, object>();
foreach(var value in RequestTokenParameters) {
parameters.Add(value);
}
Sign(RequestTokenMethod, RequestTokenUrl, parameters);
string response = MakeRequest(RequestTokenMethod, RequestTokenUrl, null, parameters, null);
if (!string.IsNullOrEmpty(response)) {
response = NetworkHelper.UrlDecode(response);
Log.DebugFormat("Request token response: {0}", response);
_requestTokenResponseParameters = NetworkHelper.ParseQueryString(response);
if (_requestTokenResponseParameters.TryGetValue(OAUTH_TOKEN_KEY, out var value)) {
Token = value;
TokenSecret = _requestTokenResponseParameters[OAUTH_TOKEN_SECRET_KEY];
}
}
return response;
}
/// <summary>
/// Authorize the token by showing the dialog
/// </summary>
/// <param name="requestTokenResponse">Pass the response from the server's request token, so if there is something wrong we can show it.</param>
/// <returns>The request token.</returns>
private string GetAuthorizeToken(string requestTokenResponse) {
if (string.IsNullOrEmpty(Token)) {
Exception e = new Exception("The request token is not set, service responded with: " + requestTokenResponse);
throw e;
}
Log.DebugFormat("Opening AuthorizationLink: {0}", AuthorizationLink);
OAuthLoginForm oAuthLoginForm = new OAuthLoginForm(LoginTitle, BrowserSize, AuthorizationLink, CallbackUrl);
oAuthLoginForm.ShowDialog();
if (oAuthLoginForm.IsOk) {
if (oAuthLoginForm.CallbackParameters != null) {
if (oAuthLoginForm.CallbackParameters.TryGetValue(OAUTH_TOKEN_KEY, out var tokenValue)) {
Token = tokenValue;
}
if (oAuthLoginForm.CallbackParameters.TryGetValue(OAUTH_VERIFIER_KEY, out var verifierValue)) {
Verifier = verifierValue;
}
}
}
if (CheckVerifier) {
if (!string.IsNullOrEmpty(Verifier)) {
return Token;
}
return null;
}
return Token;
}
/// <summary>
/// Get the access token
/// </summary>
/// <returns>The access token.</returns>
private string GetAccessToken() {
if (string.IsNullOrEmpty(Token) || (CheckVerifier && string.IsNullOrEmpty(Verifier))) {
Exception e = new Exception("The request token and verifier were not set");
throw e;
}
IDictionary<string, object> parameters = new Dictionary<string, object>();
Sign(AccessTokenMethod, AccessTokenUrl, parameters);
string response = MakeRequest(AccessTokenMethod, AccessTokenUrl, null, parameters, null);
if (!string.IsNullOrEmpty(response)) {
response = NetworkHelper.UrlDecode(response);
Log.DebugFormat("Access token response: {0}", response);
AccessTokenResponseParameters = NetworkHelper.ParseQueryString(response);
if (AccessTokenResponseParameters.TryGetValue(OAUTH_TOKEN_KEY, out var tokenValue) && tokenValue != null) {
Token = tokenValue;
}
if (AccessTokenResponseParameters.TryGetValue(OAUTH_TOKEN_SECRET_KEY, out var secretValue) && secretValue != null) {
TokenSecret = secretValue;
}
}
return Token;
}
/// <summary>
/// This method goes through the whole authorize process, including a Authorization window.
/// </summary>
/// <returns>true if the process is completed</returns>
public bool Authorize() {
Token = null;
TokenSecret = null;
Verifier = null;
Log.Debug("Creating Token");
string requestTokenResponse;
try {
requestTokenResponse = GetRequestToken();
} catch (Exception ex) {
Log.Error(ex);
throw new NotSupportedException("Service is not available: " + ex.Message);
}
if (string.IsNullOrEmpty(GetAuthorizeToken(requestTokenResponse))) {
Log.Debug("User didn't authenticate!");
return false;
}
try {
Thread.Sleep(1000);
return GetAccessToken() != null;
} catch (Exception ex) {
Log.Error(ex);
throw;
}
}
/// <summary>
/// Get the link to the authorization page for this application.
/// </summary>
/// <returns>The url with a valid request token, or a null string.</returns>
private string AuthorizationLink => AuthorizeUrl + "?" + OAUTH_TOKEN_KEY + "=" + Token + "&" + OAUTH_CALLBACK_KEY + "=" + UrlEncode3986(CallbackUrl);
/// <summary>
/// Submit a web request using oAuth.
/// </summary>
/// <param name="method">GET or POST</param>
/// <param name="requestUrl">The full url, including the querystring for the signing/request</param>
/// <param name="parametersToSign">Parameters for the request, which need to be signed</param>
/// <param name="additionalParameters">Parameters for the request, which do not need to be signed</param>
/// <param name="postData">Data to post (MemoryStream)</param>
/// <returns>The web server response.</returns>
public string MakeOAuthRequest(HTTPMethod method, string requestUrl, IDictionary<string, object> parametersToSign, IDictionary<string, object> additionalParameters, IBinaryContainer postData) {
return MakeOAuthRequest(method, requestUrl, requestUrl, null, parametersToSign, additionalParameters, postData);
}
/// <summary>
/// Submit a web request using oAuth.
/// </summary>
/// <param name="method">GET or POST</param>
/// <param name="requestUrl">The full url, including the querystring for the signing/request</param>
/// <param name="headers">Header values</param>
/// <param name="parametersToSign">Parameters for the request, which need to be signed</param>
/// <param name="additionalParameters">Parameters for the request, which do not need to be signed</param>
/// <param name="postData">Data to post (MemoryStream)</param>
/// <returns>The web server response.</returns>
public string MakeOAuthRequest(HTTPMethod method, string requestUrl, IDictionary<string, string> headers, IDictionary<string, object> parametersToSign, IDictionary<string, object> additionalParameters, IBinaryContainer postData) {
return MakeOAuthRequest(method, requestUrl, requestUrl, headers, parametersToSign, additionalParameters, postData);
}
/// <summary>
/// Submit a web request using oAuth.
/// </summary>
/// <param name="method">GET or POST</param>
/// <param name="signUrl">The full url, including the querystring for the signing</param>
/// <param name="requestUrl">The full url, including the querystring for the request</param>
/// <param name="parametersToSign">Parameters for the request, which need to be signed</param>
/// <param name="additionalParameters">Parameters for the request, which do not need to be signed</param>
/// <param name="postData">Data to post (MemoryStream)</param>
/// <returns>The web server response.</returns>
public string MakeOAuthRequest(HTTPMethod method, string signUrl, string requestUrl, IDictionary<string, object> parametersToSign, IDictionary<string, object> additionalParameters, IBinaryContainer postData) {
return MakeOAuthRequest(method, signUrl, requestUrl, null, parametersToSign, additionalParameters, postData);
}
/// <summary>
/// Submit a web request using oAuth.
/// </summary>
/// <param name="method">GET or POST</param>
/// <param name="signUrl">The full url, including the querystring for the signing</param>
/// <param name="requestUrl">The full url, including the querystring for the request</param>
/// <param name="headers">Headers for the request</param>
/// <param name="parametersToSign">Parameters for the request, which need to be signed</param>
/// <param name="additionalParameters">Parameters for the request, which do not need to be signed</param>
/// <param name="postData">Data to post (MemoryStream)</param>
/// <returns>The web server response.</returns>
public string MakeOAuthRequest(HTTPMethod method, string signUrl, string requestUrl, IDictionary<string, string> headers, IDictionary<string, object> parametersToSign, IDictionary<string, object> additionalParameters, IBinaryContainer postData) {
if (parametersToSign == null) {
parametersToSign = new Dictionary<string, object>();
}
int retries = 2;
Exception lastException = null;
while (retries-- > 0) {
// If we are not trying to get a Authorization or Accestoken, and we don't have a token, create one
if (string.IsNullOrEmpty(Token)) {
if (!AutoLogin || !Authorize()) {
throw new Exception("Not authorized");
}
}
try {
Sign(method, signUrl, parametersToSign);
// Join all parameters
IDictionary<string, object> newParameters = new Dictionary<string, object>();
foreach (var parameter in parametersToSign) {
newParameters.Add(parameter);
}
if (additionalParameters != null) {
foreach (var parameter in additionalParameters) {
newParameters.Add(parameter);
}
}
return MakeRequest(method, requestUrl, headers, newParameters, postData);
} catch (UnauthorizedAccessException uaEx) {
lastException = uaEx;
Token = null;
TokenSecret = null;
// Remove oauth keys, so they aren't added double
List<string> keysToDelete = new List<string>();
foreach (string parameterKey in parametersToSign.Keys)
{
if (parameterKey.StartsWith(OAUTH_PARAMETER_PREFIX))
{
keysToDelete.Add(parameterKey);
}
}
foreach (string keyToDelete in keysToDelete)
{
parametersToSign.Remove(keyToDelete);
}
}
}
if (lastException != null) {
throw lastException;
}
throw new Exception("Not authorized");
}
/// <summary>
/// OAuth sign the parameters, meaning all oauth parameters are added to the supplied dictionary.
/// And additionally a signature is added.
/// </summary>
/// <param name="method">Method (POST,PUT,GET)</param>
/// <param name="requestUrl">Url to call</param>
/// <param name="parameters">IDictionary of string and string</param>
private void Sign(HTTPMethod method, string requestUrl, IDictionary<string, object> parameters) {
if (parameters == null) {
throw new ArgumentNullException(nameof(parameters));
}
// Build the signature base
StringBuilder signatureBase = new StringBuilder();
// Add Method to signature base
signatureBase.Append(method).Append("&");
// Add normalized URL
Uri url = new Uri(requestUrl);
string normalizedUrl = string.Format(CultureInfo.InvariantCulture, "{0}://{1}", url.Scheme, url.Host);
if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443))) {
normalizedUrl += ":" + url.Port;
}
normalizedUrl += url.AbsolutePath;
signatureBase.Append(UrlEncode3986(normalizedUrl)).Append("&");
// Add normalized parameters
parameters.Add(OAUTH_VERSION_KEY, OAUTH_VERSION);
parameters.Add(OAUTH_NONCE_KEY, GenerateNonce());
parameters.Add(OAUTH_TIMESTAMP_KEY, GenerateTimeStamp());
switch(SignatureType) {
case OAuthSignatureTypes.PLAINTEXT:
parameters.Add(OAUTH_SIGNATURE_METHOD_KEY, PlainTextSignatureType);
break;
default:
parameters.Add(OAUTH_SIGNATURE_METHOD_KEY, HMACSHA1SignatureType);
break;
}
parameters.Add(OAUTH_CONSUMER_KEY_KEY, _consumerKey);
if (CallbackUrl != null && RequestTokenUrl != null && requestUrl.StartsWith(RequestTokenUrl)) {
parameters.Add(OAUTH_CALLBACK_KEY, CallbackUrl);
}
if (!string.IsNullOrEmpty(Verifier)) {
parameters.Add(OAUTH_VERIFIER_KEY, Verifier);
}
if (!string.IsNullOrEmpty(Token)) {
parameters.Add(OAUTH_TOKEN_KEY, Token);
}
signatureBase.Append(UrlEncode3986(GenerateNormalizedParametersString(parameters)));
Log.DebugFormat("Signature base: {0}", signatureBase);
string key = string.Format(CultureInfo.InvariantCulture, "{0}&{1}", UrlEncode3986(_consumerSecret), string.IsNullOrEmpty(TokenSecret) ? string.Empty : UrlEncode3986(TokenSecret));
switch (SignatureType) {
case OAuthSignatureTypes.PLAINTEXT:
parameters.Add(OAUTH_SIGNATURE_KEY, key);
break;
default:
// Generate Signature and add it to the parameters
HMACSHA1 hmacsha1 = new HMACSHA1 {Key = Encoding.UTF8.GetBytes(key)};
string signature = ComputeHash(hmacsha1, signatureBase.ToString());
parameters.Add(OAUTH_SIGNATURE_KEY, signature);
break;
}
}
/// <summary>
/// Make the actual OAuth request, all oauth parameters are passed as header (default) and the others are placed in the url or post data.
/// Any additional parameters added after the Sign call are not in the signature, this could be by design!
/// </summary>
/// <param name="method"></param>
/// <param name="requestUrl"></param>
/// <param name="headers"></param>
/// <param name="parameters"></param>
/// <param name="postData">IBinaryParameter</param>
/// <returns>Response from server</returns>
private string MakeRequest(HTTPMethod method, string requestUrl, IDictionary<string, string> headers, IDictionary<string, object> parameters, IBinaryContainer postData) {
if (parameters == null) {
throw new ArgumentNullException(nameof(parameters));
}
IDictionary<string, object> requestParameters;
// Add oAuth values as HTTP headers, if this is allowed
StringBuilder authHeader = null;
if (UseHttpHeadersForAuthorization) {
authHeader = new StringBuilder();
requestParameters = new Dictionary<string, object>();
foreach (string parameterKey in parameters.Keys) {
if (parameterKey.StartsWith(OAUTH_PARAMETER_PREFIX)) {
authHeader.AppendFormat(CultureInfo.InvariantCulture, "{0}=\"{1}\", ", parameterKey, UrlEncode3986($"{parameters[parameterKey]}"));
} else if (!requestParameters.ContainsKey(parameterKey)) {
requestParameters.Add(parameterKey, parameters[parameterKey]);
}
}
// Remove trailing comma and space and add it to the headers
if (authHeader.Length > 0) {
authHeader.Remove(authHeader.Length - 2, 2);
}
} else {
requestParameters = parameters;
}
if (HTTPMethod.GET == method || postData != null) {
if (requestParameters.Count > 0) {
// Add the parameters to the request
requestUrl += "?" + NetworkHelper.GenerateQueryParameters(requestParameters);
}
}
// Create webrequest
HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(requestUrl, method);
webRequest.ServicePoint.Expect100Continue = false;
webRequest.UserAgent = _userAgent;
if (UseHttpHeadersForAuthorization && authHeader != null) {
Log.DebugFormat("Authorization: OAuth {0}", authHeader);
webRequest.Headers.Add("Authorization: OAuth " + authHeader);
}
if (headers != null) {
foreach(string key in headers.Keys) {
webRequest.Headers.Add(key, headers[key]);
}
}
if ((HTTPMethod.POST == method || HTTPMethod.PUT == method) && postData == null && requestParameters.Count > 0) {
if (UseMultipartFormData) {
NetworkHelper.WriteMultipartFormData(webRequest, requestParameters);
} else {
StringBuilder form = new StringBuilder();
foreach (string parameterKey in requestParameters.Keys)
{
var binaryParameter = parameters[parameterKey] as IBinaryContainer;
form.AppendFormat(CultureInfo.InvariantCulture, "{0}={1}&", UrlEncode3986(parameterKey), binaryParameter != null ? UrlEncode3986(binaryParameter.ToBase64String(Base64FormattingOptions.None)) : UrlEncode3986($"{parameters[parameterKey]}"));
}
// Remove trailing &
if (form.Length > 0) {
form.Remove(form.Length - 1, 1);
}
webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] data = Encoding.UTF8.GetBytes(form.ToString());
using var requestStream = webRequest.GetRequestStream();
requestStream.Write(data, 0, data.Length);
}
} else if (postData != null) {
postData.Upload(webRequest);
} else {
webRequest.ContentLength = 0;
}
string responseData;
try {
responseData = NetworkHelper.GetResponseAsString(webRequest);
Log.DebugFormat("Response: {0}", responseData);
} catch (Exception ex) {
Log.Error("Couldn't retrieve response: ", ex);
throw;
}
return responseData;
}
}
}

View file

@ -0,0 +1,31 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace GreenshotPlugin.Core.OAuth
{
/// <summary>
/// Provides a predefined set of algorithms that are supported officially by the OAuth 1.x protocol
/// </summary>
public enum OAuthSignatureTypes {
HMACSHA1,
PLAINTEXT,
}
}

File diff suppressed because it is too large Load diff

View file

@ -266,33 +266,6 @@ namespace GreenshotPlugin.Core
}
}
/// <summary>
/// Retrieve all windows with a certain title or classname
/// </summary>
/// <param name="windows">IEnumerable</param>
/// <param name="titlePattern">The regexp to look for in the title</param>
/// <param name="classnamePattern">The regexp to look for in the classname</param>
/// <returns>IEnumerable WindowDetails with all the found windows</returns>
private static IEnumerable<WindowDetails> FindWindow(IEnumerable<WindowDetails> windows, string titlePattern, string classnamePattern) {
Regex titleRegexp = null;
Regex classnameRegexp = null;
if (titlePattern != null && titlePattern.Trim().Length > 0) {
titleRegexp = new Regex(titlePattern);
}
if (classnamePattern != null && classnamePattern.Trim().Length > 0) {
classnameRegexp = new Regex(classnamePattern);
}
foreach(WindowDetails window in windows) {
if (titleRegexp != null && titleRegexp.IsMatch(window.Text)) {
yield return window;
} else if (classnameRegexp != null && classnameRegexp.IsMatch(window.ClassName)) {
yield return window;
}
}
}
/// <summary>
/// Retrieve the child with matching classname
/// </summary>

View file

@ -43,14 +43,14 @@ stages:
platform: $(buildPlatform)
configuration: $(buildConfiguration)
env:
Box_ClientId: $(Box_ClientId)
Box_ClientSecret: $(Box_ClientSecret)
DropBox_ClientId: $(DropBox_ClientId)
DropBox_ClientSecret: $(DropBox_ClientSecret)
Box13_ClientId: $(Box13_ClientId)
Box13_ClientSecret: $(Box13_ClientSecret)
DropBox13_ClientId: $(DropBox13_ClientId)
DropBox13_ClientSecret: $(DropBox13_ClientSecret)
Flickr_ClientId: $(Flickr_ClientId)
Flickr_ClientSecret: $(Flickr_ClientSecret)
Imgur_ClientId: $(Imgur_ClientId)
Imgur_ClientSecret: $(Imgur_ClientSecret)
Imgur13_ClientId: $(Imgur13_ClientId)
Imgur13_ClientSecret: $(Imgur13_ClientSecret)
Photobucket_ClientId: $(Photobucket_ClientId)
Photobucket_ClientSecret: $(Photobucket_ClientSecret)
Picasa_ClientId: $(Picasa_ClientId)